Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial:
  sungem: fix compile failure caused by trivial #include consolidation
diff --git a/Documentation/DMA-API-HOWTO.txt b/Documentation/DMA-API-HOWTO.txt
index d568bc2..a0b6250 100644
--- a/Documentation/DMA-API-HOWTO.txt
+++ b/Documentation/DMA-API-HOWTO.txt
@@ -613,13 +613,13 @@
 				pass_to_upper_layers(cp->rx_buf);
 				make_and_setup_new_rx_buf(cp);
 			} else {
-				/* Just sync the buffer and give it back
-				 * to the card.
+				/* CPU should not write to
+				 * DMA_FROM_DEVICE-mapped area,
+				 * so dma_sync_single_for_device() is
+				 * not needed here. It would be required
+				 * for DMA_BIDIRECTIONAL mapping if
+				 * the memory was modified.
 				 */
-				dma_sync_single_for_device(&cp->dev,
-							   cp->rx_dma,
-							   cp->rx_len,
-							   DMA_FROM_DEVICE);
 				give_rx_buf_to_card(cp);
 			}
 		}
diff --git a/Documentation/DocBook/.gitignore b/Documentation/DocBook/.gitignore
index 679034c..720f245 100644
--- a/Documentation/DocBook/.gitignore
+++ b/Documentation/DocBook/.gitignore
@@ -8,4 +8,7 @@
 *.dvi
 *.log
 *.out
-media/
+*.png
+*.gif
+media-indices.tmpl
+media-entities.tmpl
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 3cebfa0..66725a3 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -14,7 +14,9 @@
 	    genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
 	    80211.xml debugobjects.xml sh.xml regulator.xml \
 	    alsa-driver-api.xml writing-an-alsa-driver.xml \
-	    tracepoint.xml media.xml drm.xml
+	    tracepoint.xml drm.xml media_api.xml
+
+include $(srctree)/Documentation/DocBook/media/Makefile
 
 ###
 # The build process is as follows (targets):
@@ -32,7 +34,7 @@
 
 ###
 # The targets that may be used.
-PHONY += xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs cleandocs xmldoclinks
+PHONY += xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs cleandocs
 
 BOOKS := $(addprefix $(obj)/,$(DOCBOOKS))
 xmldocs: $(BOOKS)
@@ -45,27 +47,14 @@
 pdfdocs: $(PDF)
 
 HTML := $(sort $(patsubst %.xml, %.html, $(BOOKS)))
-htmldocs: $(HTML) xmldoclinks
+htmldocs: $(HTML)
 	$(call build_main_index)
 	$(call build_images)
+	$(call install_media_images)
 
 MAN := $(patsubst %.xml, %.9, $(BOOKS))
 mandocs: $(MAN)
 
-build_images = mkdir -p $(objtree)/Documentation/DocBook/media/ && \
-	       cp $(srctree)/Documentation/DocBook/dvb/*.png \
-	          $(srctree)/Documentation/DocBook/v4l/*.gif \
-		  $(objtree)/Documentation/DocBook/media/
-
-xmldoclinks:
-ifneq ($(objtree),$(srctree))
-	for dep in dvb media-entities.tmpl media-indices.tmpl v4l; do \
-		rm -f $(objtree)/Documentation/DocBook/$$dep \
-		&& ln -s $(srctree)/Documentation/DocBook/$$dep $(objtree)/Documentation/DocBook/ \
-		|| exit; \
-	done
-endif
-
 installmandocs: mandocs
 	mkdir -p /usr/local/man/man9/
 	install Documentation/DocBook/man/*.9.gz /usr/local/man/man9/
@@ -97,11 +86,11 @@
         ) > $(dir $@).$(notdir $@).cmd
 endef
 
-%.xml: %.tmpl xmldoclinks FORCE
+%.xml: %.tmpl FORCE
 	$(call if_changed_rule,docproc)
 
 ###
-#Read in all saved dependency files 
+#Read in all saved dependency files
 cmd_files := $(wildcard $(foreach f,$(BOOKS),$(dir $(f)).$(notdir $(f)).cmd))
 
 ifneq ($(cmd_files),)
@@ -150,7 +139,7 @@
 
 index = index.html
 main_idx = Documentation/DocBook/$(index)
-build_main_index = rm -rf $(main_idx) && \
+build_main_index = rm -rf $(main_idx); \
 		   echo '<h1>Linux Kernel HTML Documentation</h1>' >> $(main_idx) && \
 		   echo '<h2>Kernel Version: $(KERNELVERSION)</h2>' >> $(main_idx) && \
 		   cat $(HTML) >> $(main_idx)
@@ -242,7 +231,7 @@
 
 clean-dirs := $(patsubst %.xml,%,$(DOCBOOKS)) man
 
-cleandocs:
+cleandocs: cleanmediadocs
 	$(Q)rm -f $(call objectify, $(clean-files))
 	$(Q)rm -rf $(call objectify, $(clean-dirs))
 
diff --git a/Documentation/DocBook/dvb/dvbproperty.xml b/Documentation/DocBook/dvb/dvbproperty.xml
deleted file mode 100644
index b5365f6..0000000
--- a/Documentation/DocBook/dvb/dvbproperty.xml
+++ /dev/null
@@ -1,590 +0,0 @@
-<section id="FE_GET_SET_PROPERTY">
-<title>FE_GET_PROPERTY/FE_SET_PROPERTY</title>
-
-<programlisting>
-/* Reserved fields should be set to 0 */
-struct dtv_property {
-	__u32 cmd;
-	union {
-		__u32 data;
-		struct {
-			__u8 data[32];
-			__u32 len;
-			__u32 reserved1[3];
-			void *reserved2;
-		} buffer;
-	} u;
-	int result;
-} __attribute__ ((packed));
-
-/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
-#define DTV_IOCTL_MAX_MSGS 64
-
-struct dtv_properties {
-	__u32 num;
-	struct dtv_property *props;
-};
-</programlisting>
-
-<section id="FE_GET_PROPERTY">
-<title>FE_GET_PROPERTY</title>
-<para>DESCRIPTION
-</para>
-<informaltable><tgroup cols="1"><tbody><row><entry
- align="char">
-<para>This ioctl call returns one or more frontend properties. This call only
- requires read-only access to the device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>SYNOPSIS
-</para>
-<informaltable><tgroup cols="1"><tbody><row><entry
- align="char">
-<para>int ioctl(int fd, int request = <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>,
- dtv_properties &#x22C6;props);</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>PARAMETERS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry align="char">
-<para>int fd</para>
-</entry><entry
- align="char">
-<para>File descriptor returned by a previous call to open().</para>
-</entry>
- </row><row><entry
- align="char">
-<para>int num</para>
-</entry><entry
- align="char">
-<para>Equals <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link> for this command.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>struct dtv_property *props</para>
-</entry><entry
- align="char">
-<para>Points to the location where the front-end property commands are stored.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>ERRORS</para>
-<informaltable><tgroup cols="2"><tbody><row>
-  <entry align="char"><para>EINVAL</para></entry>
-  <entry align="char"><para>Invalid parameter(s) received or number of parameters out of the range.</para></entry>
- </row><row>
-  <entry align="char"><para>ENOMEM</para></entry>
-  <entry align="char"><para>Out of memory.</para></entry>
- </row><row>
-  <entry align="char"><para>EFAULT</para></entry>
-  <entry align="char"><para>Failure while copying data from/to userspace.</para></entry>
- </row><row>
-  <entry align="char"><para>EOPNOTSUPP</para></entry>
-  <entry align="char"><para>Property type not supported.</para></entry>
- </row></tbody></tgroup></informaltable>
-</section>
-
-<section id="FE_SET_PROPERTY">
-<title>FE_SET_PROPERTY</title>
-<para>DESCRIPTION
-</para>
-<informaltable><tgroup cols="1"><tbody><row><entry
- align="char">
-<para>This ioctl call sets one or more frontend properties. This call only
- requires read-only access to the device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>SYNOPSIS
-</para>
-<informaltable><tgroup cols="1"><tbody><row><entry
- align="char">
-<para>int ioctl(int fd, int request = <link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link>,
- dtv_properties &#x22C6;props);</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>PARAMETERS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry align="char">
-<para>int fd</para>
-</entry><entry
- align="char">
-<para>File descriptor returned by a previous call to open().</para>
-</entry>
- </row><row><entry
- align="char">
-<para>int num</para>
-</entry><entry
- align="char">
-<para>Equals <link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link> for this command.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>struct dtv_property *props</para>
-</entry><entry
- align="char">
-<para>Points to the location where the front-end property commands are stored.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row>
-  <entry align="char"><para>EINVAL</para></entry>
-  <entry align="char"><para>Invalid parameter(s) received or number of parameters out of the range.</para></entry>
- </row><row>
-  <entry align="char"><para>ENOMEM</para></entry>
-  <entry align="char"><para>Out of memory.</para></entry>
- </row><row>
-  <entry align="char"><para>EFAULT</para></entry>
-  <entry align="char"><para>Failure while copying data from/to userspace.</para></entry>
- </row><row>
-  <entry align="char"><para>EOPNOTSUPP</para></entry>
-  <entry align="char"><para>Property type not supported.</para></entry>
- </row></tbody></tgroup></informaltable>
-</section>
-
-<section>
-	<title>Property types</title>
-<para>
-On <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>/<link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link>,
-the actual action is determined by the dtv_property cmd/data pairs. With one single ioctl, is possible to
-get/set up to 64 properties. The actual meaning of each property is described on the next sections.
-</para>
-
-<para>The available frontend property types are:</para>
-<programlisting>
-#define DTV_UNDEFINED		0
-#define DTV_TUNE		1
-#define DTV_CLEAR		2
-#define DTV_FREQUENCY		3
-#define DTV_MODULATION		4
-#define DTV_BANDWIDTH_HZ	5
-#define DTV_INVERSION		6
-#define DTV_DISEQC_MASTER	7
-#define DTV_SYMBOL_RATE		8
-#define DTV_INNER_FEC		9
-#define DTV_VOLTAGE		10
-#define DTV_TONE		11
-#define DTV_PILOT		12
-#define DTV_ROLLOFF		13
-#define DTV_DISEQC_SLAVE_REPLY	14
-#define DTV_FE_CAPABILITY_COUNT	15
-#define DTV_FE_CAPABILITY	16
-#define DTV_DELIVERY_SYSTEM	17
-#define DTV_ISDBT_PARTIAL_RECEPTION	18
-#define DTV_ISDBT_SOUND_BROADCASTING	19
-#define DTV_ISDBT_SB_SUBCHANNEL_ID	20
-#define DTV_ISDBT_SB_SEGMENT_IDX	21
-#define DTV_ISDBT_SB_SEGMENT_COUNT	22
-#define DTV_ISDBT_LAYERA_FEC			23
-#define DTV_ISDBT_LAYERA_MODULATION		24
-#define DTV_ISDBT_LAYERA_SEGMENT_COUNT		25
-#define DTV_ISDBT_LAYERA_TIME_INTERLEAVING	26
-#define DTV_ISDBT_LAYERB_FEC			27
-#define DTV_ISDBT_LAYERB_MODULATION		28
-#define DTV_ISDBT_LAYERB_SEGMENT_COUNT		29
-#define DTV_ISDBT_LAYERB_TIME_INTERLEAVING	30
-#define DTV_ISDBT_LAYERC_FEC			31
-#define DTV_ISDBT_LAYERC_MODULATION		32
-#define DTV_ISDBT_LAYERC_SEGMENT_COUNT		33
-#define DTV_ISDBT_LAYERC_TIME_INTERLEAVING	34
-#define DTV_API_VERSION		35
-#define DTV_CODE_RATE_HP	36
-#define DTV_CODE_RATE_LP	37
-#define DTV_GUARD_INTERVAL	38
-#define DTV_TRANSMISSION_MODE	39
-#define DTV_HIERARCHY		40
-#define DTV_ISDBT_LAYER_ENABLED	41
-#define DTV_ISDBS_TS_ID		42
-</programlisting>
-</section>
-
-<section id="fe_property_common">
-	<title>Parameters that are common to all Digital TV standards</title>
-	<section id="DTV_FREQUENCY">
-		<title><constant>DTV_FREQUENCY</constant></title>
-
-		<para>Central frequency of the channel, in HZ.</para>
-
-		<para>Notes:</para>
-		<para>1)For ISDB-T, the channels are usually transmitted with an offset of 143kHz.
-			E.g. a valid frequncy could be 474143 kHz. The stepping is bound to the bandwidth of
-			the channel which is 6MHz.</para>
-
-		<para>2)As in ISDB-Tsb the channel consists of only one or three segments the
-			frequency step is 429kHz, 3*429 respectively. As for ISDB-T the
-			central frequency of the channel is expected.</para>
-	</section>
-
-	<section id="DTV_BANDWIDTH_HZ">
-		<title><constant>DTV_BANDWIDTH_HZ</constant></title>
-
-		<para>Bandwidth for the channel, in HZ.</para>
-
-		<para>Possible values:
-			<constant>1712000</constant>,
-			<constant>5000000</constant>,
-			<constant>6000000</constant>,
-			<constant>7000000</constant>,
-			<constant>8000000</constant>,
-			<constant>10000000</constant>.
-		</para>
-
-		<para>Notes:</para>
-
-		<para>1) For ISDB-T it should be always 6000000Hz (6MHz)</para>
-		<para>2) For ISDB-Tsb it can vary depending on the number of connected segments</para>
-		<para>3) Bandwidth doesn't apply for DVB-C transmissions, as the bandwidth
-			 for DVB-C depends on the symbol rate</para>
-		<para>4) Bandwidth in ISDB-T is fixed (6MHz) or can be easily derived from
-			other parameters (DTV_ISDBT_SB_SEGMENT_IDX,
-			DTV_ISDBT_SB_SEGMENT_COUNT).</para>
-		<para>5) DVB-T supports 6, 7 and 8MHz.</para>
-		<para>6) In addition, DVB-T2 supports 1.172, 5 and 10MHz.</para>
-	</section>
-
-	<section id="DTV_DELIVERY_SYSTEM">
-		<title><constant>DTV_DELIVERY_SYSTEM</constant></title>
-
-		<para>Specifies the type of Delivery system</para>
-
-		<para>Possible values: </para>
-<programlisting>
-typedef enum fe_delivery_system {
-	SYS_UNDEFINED,
-	SYS_DVBC_ANNEX_AC,
-	SYS_DVBC_ANNEX_B,
-	SYS_DVBT,
-	SYS_DSS,
-	SYS_DVBS,
-	SYS_DVBS2,
-	SYS_DVBH,
-	SYS_ISDBT,
-	SYS_ISDBS,
-	SYS_ISDBC,
-	SYS_ATSC,
-	SYS_ATSCMH,
-	SYS_DMBTH,
-	SYS_CMMB,
-	SYS_DAB,
-	SYS_DVBT2,
-} fe_delivery_system_t;
-</programlisting>
-
-	</section>
-
-	<section id="DTV_TRANSMISSION_MODE">
-		<title><constant>DTV_TRANSMISSION_MODE</constant></title>
-
-		<para>Specifies the number of carriers used by the standard</para>
-
-		<para>Possible values are:</para>
-<programlisting>
-typedef enum fe_transmit_mode {
-	TRANSMISSION_MODE_2K,
-	TRANSMISSION_MODE_8K,
-	TRANSMISSION_MODE_AUTO,
-	TRANSMISSION_MODE_4K,
-	TRANSMISSION_MODE_1K,
-	TRANSMISSION_MODE_16K,
-	TRANSMISSION_MODE_32K,
-} fe_transmit_mode_t;
-</programlisting>
-
-		<para>Notes:</para>
-		<para>1) ISDB-T supports three carrier/symbol-size: 8K, 4K, 2K. It is called
-			'mode' in the standard: Mode 1 is 2K, mode 2 is 4K, mode 3 is 8K</para>
-
-		<para>2) If <constant>DTV_TRANSMISSION_MODE</constant> is set the <constant>TRANSMISSION_MODE_AUTO</constant> the
-			hardware will try to find the correct FFT-size (if capable) and will
-			use TMCC to fill in the missing parameters.</para>
-		<para>3) DVB-T specifies 2K and 8K as valid sizes.</para>
-		<para>4) DVB-T2 specifies 1K, 2K, 4K, 8K, 16K and 32K.</para>
-	</section>
-
-	<section id="DTV_GUARD_INTERVAL">
-		<title><constant>DTV_GUARD_INTERVAL</constant></title>
-
-		<para>Possible values are:</para>
-<programlisting>
-typedef enum fe_guard_interval {
-	GUARD_INTERVAL_1_32,
-	GUARD_INTERVAL_1_16,
-	GUARD_INTERVAL_1_8,
-	GUARD_INTERVAL_1_4,
-	GUARD_INTERVAL_AUTO,
-	GUARD_INTERVAL_1_128,
-	GUARD_INTERVAL_19_128,
-	GUARD_INTERVAL_19_256,
-} fe_guard_interval_t;
-</programlisting>
-
-		<para>Notes:</para>
-		<para>1) If <constant>DTV_GUARD_INTERVAL</constant> is set the <constant>GUARD_INTERVAL_AUTO</constant> the hardware will
-			try to find the correct guard interval (if capable) and will use TMCC to fill
-			in the missing parameters.</para>
-		<para>2) Intervals 1/128, 19/128 and 19/256 are used only for DVB-T2 at present</para>
-	</section>
-</section>
-
-<section id="isdbt">
-	<title>ISDB-T frontend</title>
-	<para>This section describes shortly what are the possible parameters in the Linux
-		DVB-API called "S2API" and now DVB API 5 in order to tune an ISDB-T/ISDB-Tsb
-		demodulator:</para>
-
-	<para>This ISDB-T/ISDB-Tsb API extension should reflect all information
-		needed to tune any ISDB-T/ISDB-Tsb hardware. Of course it is possible
-		that some very sophisticated devices won't need certain parameters to
-		tune.</para>
-
-	<para>The information given here should help application writers to know how
-		to handle ISDB-T and ISDB-Tsb hardware using the Linux DVB-API.</para>
-
-	<para>The details given here about ISDB-T and ISDB-Tsb are just enough to
-		basically show the dependencies between the needed parameter values,
-		but surely some information is left out. For more detailed information
-		see the following documents:</para>
-
-	<para>ARIB STD-B31 - "Transmission System for Digital Terrestrial
-		Television Broadcasting" and</para>
-	<para>ARIB TR-B14 - "Operational Guidelines for Digital Terrestrial
-		Television Broadcasting".</para>
-
-	<para>In order to read this document one has to have some knowledge the
-		channel structure in ISDB-T and ISDB-Tsb. I.e. it has to be known to
-		the reader that an ISDB-T channel consists of 13 segments, that it can
-		have up to 3 layer sharing those segments, and things like that.</para>
-
-	<para>Parameters used by ISDB-T and ISDB-Tsb.</para>
-
-	<section id="isdbt-new-parms">
-		<title>ISDB-T only parameters</title>
-
-		<section id="isdbt-part-rec">
-			<title><constant>DTV_ISDBT_PARTIAL_RECEPTION</constant></title>
-
-			<para><constant>If DTV_ISDBT_SOUND_BROADCASTING</constant> is '0' this bit-field represents whether
-				the channel is in partial reception mode or not.</para>
-
-			<para>If '1' <constant>DTV_ISDBT_LAYERA_*</constant> values are assigned to the center segment and
-				<constant>DTV_ISDBT_LAYERA_SEGMENT_COUNT</constant> has to be '1'.</para>
-
-			<para>If in addition <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'
-				<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> represents whether this ISDB-Tsb channel
-				is consisting of one segment and layer or three segments and two layers.</para>
-
-			<para>Possible values: 0, 1, -1 (AUTO)</para>
-		</section>
-
-		<section id="isdbt-sound-bcast">
-			<title><constant>DTV_ISDBT_SOUND_BROADCASTING</constant></title>
-
-			<para>This field represents whether the other DTV_ISDBT_*-parameters are
-				referring to an ISDB-T and an ISDB-Tsb channel. (See also
-				<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>).</para>
-
-			<para>Possible values: 0, 1, -1 (AUTO)</para>
-		</section>
-
-		<section id="isdbt-sb-ch-id">
-			<title><constant>DTV_ISDBT_SB_SUBCHANNEL_ID</constant></title>
-
-			<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
-
-			<para>(Note of the author: This might not be the correct description of the
-				<constant>SUBCHANNEL-ID</constant> in all details, but it is my understanding of the technical
-				background needed to program a device)</para>
-
-			<para>An ISDB-Tsb channel (1 or 3 segments) can be broadcasted alone or in a
-				set of connected ISDB-Tsb channels. In this set of channels every
-				channel can be received independently. The number of connected
-				ISDB-Tsb segment can vary, e.g. depending on the frequency spectrum
-				bandwidth available.</para>
-
-			<para>Example: Assume 8 ISDB-Tsb connected segments are broadcasted. The
-				broadcaster has several possibilities to put those channels in the
-				air: Assuming a normal 13-segment ISDB-T spectrum he can align the 8
-				segments from position 1-8 to 5-13 or anything in between.</para>
-
-			<para>The underlying layer of segments are subchannels: each segment is
-				consisting of several subchannels with a predefined IDs. A sub-channel
-				is used to help the demodulator to synchronize on the channel.</para>
-
-			<para>An ISDB-T channel is always centered over all sub-channels. As for
-				the example above, in ISDB-Tsb it is no longer as simple as that.</para>
-
-			<para><constant>The DTV_ISDBT_SB_SUBCHANNEL_ID</constant> parameter is used to give the
-				sub-channel ID of the segment to be demodulated.</para>
-
-			<para>Possible values: 0 .. 41, -1 (AUTO)</para>
-		</section>
-
-		<section id="isdbt-sb-seg-idx">
-
-			<title><constant>DTV_ISDBT_SB_SEGMENT_IDX</constant></title>
-
-			<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
-
-			<para><constant>DTV_ISDBT_SB_SEGMENT_IDX</constant> gives the index of the segment to be
-				demodulated for an ISDB-Tsb channel where several of them are
-				transmitted in the connected manner.</para>
-
-			<para>Possible values: 0 .. <constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant> - 1</para>
-
-			<para>Note: This value cannot be determined by an automatic channel search.</para>
-		</section>
-
-		<section id="isdbt-sb-seg-cnt">
-			<title><constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant></title>
-
-			<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
-
-			<para><constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant> gives the total count of connected ISDB-Tsb
-				channels.</para>
-
-			<para>Possible values: 1 .. 13</para>
-
-			<para>Note: This value cannot be determined by an automatic channel search.</para>
-		</section>
-
-		<section id="isdb-hierq-layers">
-			<title>Hierarchical layers</title>
-
-			<para>ISDB-T channels can be coded hierarchically. As opposed to DVB-T in
-				ISDB-T hierarchical layers can be decoded simultaneously. For that
-				reason a ISDB-T demodulator has 3 viterbi and 3 reed-solomon-decoders.</para>
-
-			<para>ISDB-T has 3 hierarchical layers which each can use a part of the
-				available segments. The total number of segments over all layers has
-				to 13 in ISDB-T.</para>
-
-			<section id="isdbt-layer-ena">
-				<title><constant>DTV_ISDBT_LAYER_ENABLED</constant></title>
-
-				<para>Hierarchical reception in ISDB-T is achieved by enabling or disabling
-					layers in the decoding process. Setting all bits of
-					<constant>DTV_ISDBT_LAYER_ENABLED</constant> to '1' forces all layers (if applicable) to be
-					demodulated. This is the default.</para>
-
-				<para>If the channel is in the partial reception mode
-					(<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> = 1) the central segment can be decoded
-					independently of the other 12 segments. In that mode layer A has to
-					have a <constant>SEGMENT_COUNT</constant> of 1.</para>
-
-				<para>In ISDB-Tsb only layer A is used, it can be 1 or 3 in ISDB-Tsb
-					according to <constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>. <constant>SEGMENT_COUNT</constant> must be filled
-					accordingly.</para>
-
-				<para>Possible values: 0x1, 0x2, 0x4 (|-able)</para>
-
-				<para><constant>DTV_ISDBT_LAYER_ENABLED[0:0]</constant> - layer A</para>
-				<para><constant>DTV_ISDBT_LAYER_ENABLED[1:1]</constant> - layer B</para>
-				<para><constant>DTV_ISDBT_LAYER_ENABLED[2:2]</constant> - layer C</para>
-				<para><constant>DTV_ISDBT_LAYER_ENABLED[31:3]</constant> unused</para>
-			</section>
-
-			<section id="isdbt-layer-fec">
-				<title><constant>DTV_ISDBT_LAYER*_FEC</constant></title>
-
-				<para>Possible values: <constant>FEC_AUTO</constant>, <constant>FEC_1_2</constant>, <constant>FEC_2_3</constant>, <constant>FEC_3_4</constant>, <constant>FEC_5_6</constant>, <constant>FEC_7_8</constant></para>
-			</section>
-
-			<section id="isdbt-layer-mod">
-				<title><constant>DTV_ISDBT_LAYER*_MODULATION</constant></title>
-
-				<para>Possible values: <constant>QAM_AUTO</constant>, QP<constant>SK, QAM_16</constant>, <constant>QAM_64</constant>, <constant>DQPSK</constant></para>
-
-				<para>Note: If layer C is <constant>DQPSK</constant> layer B has to be <constant>DQPSK</constant>. If layer B is <constant>DQPSK</constant>
-					and <constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>=0 layer has to be <constant>DQPSK</constant>.</para>
-			</section>
-
-			<section id="isdbt-layer-seg-cnt">
-				<title><constant>DTV_ISDBT_LAYER*_SEGMENT_COUNT</constant></title>
-
-				<para>Possible values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1 (AUTO)</para>
-
-				<para>Note: Truth table for <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> and
-					<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> and <constant>LAYER</constant>*_SEGMENT_COUNT</para>
-
-				<informaltable id="isdbt-layer_seg-cnt-table">
-					<tgroup cols="6">
-
-						<tbody>
-							<row>
-								<entry>PR</entry>
-								<entry>SB</entry>
-								<entry>Layer A width</entry>
-								<entry>Layer B width</entry>
-								<entry>Layer C width</entry>
-								<entry>total width</entry>
-							</row>
-
-							<row>
-								<entry>0</entry>
-								<entry>0</entry>
-								<entry>1 .. 13</entry>
-								<entry>1 .. 13</entry>
-								<entry>1 .. 13</entry>
-								<entry>13</entry>
-							</row>
-
-							<row>
-								<entry>1</entry>
-								<entry>0</entry>
-								<entry>1</entry>
-								<entry>1 .. 13</entry>
-								<entry>1 .. 13</entry>
-								<entry>13</entry>
-							</row>
-
-							<row>
-								<entry>0</entry>
-								<entry>1</entry>
-								<entry>1</entry>
-								<entry>0</entry>
-								<entry>0</entry>
-								<entry>1</entry>
-							</row>
-
-							<row>
-								<entry>1</entry>
-								<entry>1</entry>
-								<entry>1</entry>
-								<entry>2</entry>
-								<entry>0</entry>
-								<entry>13</entry>
-							</row>
-						</tbody>
-
-					</tgroup>
-				</informaltable>
-
-			</section>
-
-			<section id="isdbt_layer_t_interl">
-				<title><constant>DTV_ISDBT_LAYER*_TIME_INTERLEAVING</constant></title>
-
-				<para>Possible values: 0, 1, 2, 3, -1 (AUTO)</para>
-
-				<para>Note: The real inter-leaver depth-names depend on the mode (fft-size); the values
-					here are referring to what can be found in the TMCC-structure -
-					independent of the mode.</para>
-			</section>
-		</section>
-	</section>
-	<section id="dvbt2-params">
-		<title>DVB-T2 parameters</title>
-		
-		<para>This section covers parameters that apply only to the DVB-T2 delivery method. DVB-T2
-			support is currently in the early stages development so expect this section to grow
-			and become more detailed with time.</para>
-
-		<section id="dvbt2-plp-id">
-			<title><constant>DTV_DVBT2_PLP_ID</constant></title>
-
-			<para>DVB-T2 supports Physical Layer Pipes (PLP) to allow transmission of
-				many data types via a single multiplex. The API will soon support this
-				at which point this section will be expanded.</para>
-		</section>
-	</section>
-</section>
-</section>
diff --git a/Documentation/DocBook/dvb/dvbstb.png b/Documentation/DocBook/dvb/dvbstb.png
deleted file mode 100644
index 9b8f372..0000000
--- a/Documentation/DocBook/dvb/dvbstb.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/dvb/frontend.h.xml b/Documentation/DocBook/dvb/frontend.h.xml
deleted file mode 100644
index d792f78..0000000
--- a/Documentation/DocBook/dvb/frontend.h.xml
+++ /dev/null
@@ -1,428 +0,0 @@
-<programlisting>
-/*
- * frontend.h
- *
- * Copyright (C) 2000 Marcus Metzler &lt;marcus@convergence.de&gt;
- *                  Ralph  Metzler &lt;ralph@convergence.de&gt;
- *                  Holger Waechtler &lt;holger@convergence.de&gt;
- *                  Andre Draszik &lt;ad@convergence.de&gt;
- *                  for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * 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 Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
- */
-
-#ifndef _DVBFRONTEND_H_
-#define _DVBFRONTEND_H_
-
-#include &lt;linux/types.h&gt;
-
-typedef enum fe_type {
-        FE_QPSK,
-        FE_QAM,
-        FE_OFDM,
-        FE_ATSC
-} fe_type_t;
-
-
-typedef enum fe_caps {
-        FE_IS_STUPID                    = 0,
-        FE_CAN_INVERSION_AUTO           = 0x1,
-        FE_CAN_FEC_1_2                  = 0x2,
-        FE_CAN_FEC_2_3                  = 0x4,
-        FE_CAN_FEC_3_4                  = 0x8,
-        FE_CAN_FEC_4_5                  = 0x10,
-        FE_CAN_FEC_5_6                  = 0x20,
-        FE_CAN_FEC_6_7                  = 0x40,
-        FE_CAN_FEC_7_8                  = 0x80,
-        FE_CAN_FEC_8_9                  = 0x100,
-        FE_CAN_FEC_AUTO                 = 0x200,
-        FE_CAN_QPSK                     = 0x400,
-        FE_CAN_QAM_16                   = 0x800,
-        FE_CAN_QAM_32                   = 0x1000,
-        FE_CAN_QAM_64                   = 0x2000,
-        FE_CAN_QAM_128                  = 0x4000,
-        FE_CAN_QAM_256                  = 0x8000,
-        FE_CAN_QAM_AUTO                 = 0x10000,
-        FE_CAN_TRANSMISSION_MODE_AUTO   = 0x20000,
-        FE_CAN_BANDWIDTH_AUTO           = 0x40000,
-        FE_CAN_GUARD_INTERVAL_AUTO      = 0x80000,
-        FE_CAN_HIERARCHY_AUTO           = 0x100000,
-        FE_CAN_8VSB                     = 0x200000,
-        FE_CAN_16VSB                    = 0x400000,
-        FE_HAS_EXTENDED_CAPS            = 0x800000,   /* We need more bitspace for newer APIs, indicate this. */
-        FE_CAN_TURBO_FEC                = 0x8000000,  /* frontend supports "turbo fec modulation" */
-        FE_CAN_2G_MODULATION            = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */
-        FE_NEEDS_BENDING                = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */
-        FE_CAN_RECOVER                  = 0x40000000, /* frontend can recover from a cable unplug automatically */
-        FE_CAN_MUTE_TS                  = 0x80000000  /* frontend can stop spurious TS data output */
-} fe_caps_t;
-
-
-struct dvb_frontend_info {
-        char       name[128];
-        fe_type_t  type;
-        __u32      frequency_min;
-        __u32      frequency_max;
-        __u32      frequency_stepsize;
-        __u32      frequency_tolerance;
-        __u32      symbol_rate_min;
-        __u32      symbol_rate_max;
-        __u32      symbol_rate_tolerance;       /* ppm */
-        __u32      notifier_delay;              /* DEPRECATED */
-        fe_caps_t  caps;
-};
-
-
-/**
- *  Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for
- *  the meaning of this struct...
- */
-struct dvb_diseqc_master_cmd {
-        __u8 msg [6];   /*  { framing, address, command, data [3] } */
-        __u8 msg_len;   /*  valid values are 3...6  */
-};
-
-
-struct dvb_diseqc_slave_reply {
-        __u8 msg [4];   /*  { framing, data [3] } */
-        __u8 msg_len;   /*  valid values are 0...4, 0 means no msg  */
-        int  timeout;   /*  return from ioctl after timeout ms with */
-};                      /*  errorcode when no message was received  */
-
-
-typedef enum fe_sec_voltage {
-        SEC_VOLTAGE_13,
-        SEC_VOLTAGE_18,
-        SEC_VOLTAGE_OFF
-} fe_sec_voltage_t;
-
-
-typedef enum fe_sec_tone_mode {
-        SEC_TONE_ON,
-        SEC_TONE_OFF
-} fe_sec_tone_mode_t;
-
-
-typedef enum fe_sec_mini_cmd {
-        SEC_MINI_A,
-        SEC_MINI_B
-} fe_sec_mini_cmd_t;
-
-
-typedef enum fe_status {
-        FE_HAS_SIGNAL   = 0x01,   /* found something above the noise level */
-        FE_HAS_CARRIER  = 0x02,   /* found a DVB signal  */
-        FE_HAS_VITERBI  = 0x04,   /* FEC is stable  */
-        FE_HAS_SYNC     = 0x08,   /* found sync bytes  */
-        FE_HAS_LOCK     = 0x10,   /* everything's working... */
-        FE_TIMEDOUT     = 0x20,   /* no lock within the last ~2 seconds */
-        FE_REINIT       = 0x40    /* frontend was reinitialized,  */
-} fe_status_t;                    /* application is recommended to reset */
-                                  /* DiSEqC, tone and parameters */
-
-typedef enum fe_spectral_inversion {
-        INVERSION_OFF,
-        INVERSION_ON,
-        INVERSION_AUTO
-} fe_spectral_inversion_t;
-
-
-typedef enum fe_code_rate {
-        FEC_NONE = 0,
-        FEC_1_2,
-        FEC_2_3,
-        FEC_3_4,
-        FEC_4_5,
-        FEC_5_6,
-        FEC_6_7,
-        FEC_7_8,
-        FEC_8_9,
-        FEC_AUTO,
-        FEC_3_5,
-        FEC_9_10,
-} fe_code_rate_t;
-
-
-typedef enum fe_modulation {
-        QPSK,
-        QAM_16,
-        QAM_32,
-        QAM_64,
-        QAM_128,
-        QAM_256,
-        QAM_AUTO,
-        VSB_8,
-        VSB_16,
-        PSK_8,
-        APSK_16,
-        APSK_32,
-        DQPSK,
-} fe_modulation_t;
-
-typedef enum fe_transmit_mode {
-        TRANSMISSION_MODE_2K,
-        TRANSMISSION_MODE_8K,
-        TRANSMISSION_MODE_AUTO,
-        TRANSMISSION_MODE_4K,
-        TRANSMISSION_MODE_1K,
-        TRANSMISSION_MODE_16K,
-        TRANSMISSION_MODE_32K,
-} fe_transmit_mode_t;
-
-typedef enum fe_bandwidth {
-        BANDWIDTH_8_MHZ,
-        BANDWIDTH_7_MHZ,
-        BANDWIDTH_6_MHZ,
-        BANDWIDTH_AUTO,
-        BANDWIDTH_5_MHZ,
-        BANDWIDTH_10_MHZ,
-        BANDWIDTH_1_712_MHZ,
-} fe_bandwidth_t;
-
-
-typedef enum fe_guard_interval {
-        GUARD_INTERVAL_1_32,
-        GUARD_INTERVAL_1_16,
-        GUARD_INTERVAL_1_8,
-        GUARD_INTERVAL_1_4,
-        GUARD_INTERVAL_AUTO,
-        GUARD_INTERVAL_1_128,
-        GUARD_INTERVAL_19_128,
-        GUARD_INTERVAL_19_256,
-} fe_guard_interval_t;
-
-
-typedef enum fe_hierarchy {
-        HIERARCHY_NONE,
-        HIERARCHY_1,
-        HIERARCHY_2,
-        HIERARCHY_4,
-        HIERARCHY_AUTO
-} fe_hierarchy_t;
-
-
-struct dvb_qpsk_parameters {
-        __u32           symbol_rate;  /* symbol rate in Symbols per second */
-        fe_code_rate_t  fec_inner;    /* forward error correction (see above) */
-};
-
-struct dvb_qam_parameters {
-        __u32           symbol_rate; /* symbol rate in Symbols per second */
-        fe_code_rate_t  fec_inner;   /* forward error correction (see above) */
-        fe_modulation_t modulation;  /* modulation type (see above) */
-};
-
-struct dvb_vsb_parameters {
-        fe_modulation_t modulation;  /* modulation type (see above) */
-};
-
-struct dvb_ofdm_parameters {
-        fe_bandwidth_t      bandwidth;
-        fe_code_rate_t      code_rate_HP;  /* high priority stream code rate */
-        fe_code_rate_t      code_rate_LP;  /* low priority stream code rate */
-        fe_modulation_t     constellation; /* modulation type (see above) */
-        fe_transmit_mode_t  transmission_mode;
-        fe_guard_interval_t guard_interval;
-        fe_hierarchy_t      hierarchy_information;
-};
-
-
-struct dvb_frontend_parameters {
-        __u32 frequency;     /* (absolute) frequency in Hz for QAM/OFDM/ATSC */
-                             /* intermediate frequency in kHz for QPSK */
-        fe_spectral_inversion_t inversion;
-        union {
-                struct dvb_qpsk_parameters qpsk;
-                struct dvb_qam_parameters  qam;
-                struct dvb_ofdm_parameters ofdm;
-                struct dvb_vsb_parameters vsb;
-        } u;
-};
-
-
-struct dvb_frontend_event {
-        fe_status_t status;
-        struct dvb_frontend_parameters parameters;
-};
-
-/* S2API Commands */
-#define DTV_UNDEFINED           0
-#define DTV_TUNE                1
-#define DTV_CLEAR               2
-#define DTV_FREQUENCY           3
-#define DTV_MODULATION          4
-#define DTV_BANDWIDTH_HZ        5
-#define DTV_INVERSION           6
-#define DTV_DISEQC_MASTER       7
-#define DTV_SYMBOL_RATE         8
-#define DTV_INNER_FEC           9
-#define DTV_VOLTAGE             10
-#define DTV_TONE                11
-#define DTV_PILOT               12
-#define DTV_ROLLOFF             13
-#define DTV_DISEQC_SLAVE_REPLY  14
-
-/* Basic enumeration set for querying unlimited capabilities */
-#define DTV_FE_CAPABILITY_COUNT 15
-#define DTV_FE_CAPABILITY       16
-#define DTV_DELIVERY_SYSTEM     17
-
-/* ISDB-T and ISDB-Tsb */
-#define DTV_ISDBT_PARTIAL_RECEPTION     18
-#define DTV_ISDBT_SOUND_BROADCASTING    19
-
-#define DTV_ISDBT_SB_SUBCHANNEL_ID      20
-#define DTV_ISDBT_SB_SEGMENT_IDX        21
-#define DTV_ISDBT_SB_SEGMENT_COUNT      22
-
-#define DTV_ISDBT_LAYERA_FEC                    23
-#define DTV_ISDBT_LAYERA_MODULATION             24
-#define DTV_ISDBT_LAYERA_SEGMENT_COUNT          25
-#define DTV_ISDBT_LAYERA_TIME_INTERLEAVING      26
-
-#define DTV_ISDBT_LAYERB_FEC                    27
-#define DTV_ISDBT_LAYERB_MODULATION             28
-#define DTV_ISDBT_LAYERB_SEGMENT_COUNT          29
-#define DTV_ISDBT_LAYERB_TIME_INTERLEAVING      30
-
-#define DTV_ISDBT_LAYERC_FEC                    31
-#define DTV_ISDBT_LAYERC_MODULATION             32
-#define DTV_ISDBT_LAYERC_SEGMENT_COUNT          33
-#define DTV_ISDBT_LAYERC_TIME_INTERLEAVING      34
-
-#define DTV_API_VERSION         35
-
-#define DTV_CODE_RATE_HP        36
-#define DTV_CODE_RATE_LP        37
-#define DTV_GUARD_INTERVAL      38
-#define DTV_TRANSMISSION_MODE   39
-#define DTV_HIERARCHY           40
-
-#define DTV_ISDBT_LAYER_ENABLED 41
-
-#define DTV_ISDBS_TS_ID         42
-
-#define DTV_DVBT2_PLP_ID	43
-
-#define DTV_MAX_COMMAND                         DTV_DVBT2_PLP_ID
-
-typedef enum fe_pilot {
-        PILOT_ON,
-        PILOT_OFF,
-        PILOT_AUTO,
-} fe_pilot_t;
-
-typedef enum fe_rolloff {
-        ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
-        ROLLOFF_20,
-        ROLLOFF_25,
-        ROLLOFF_AUTO,
-} fe_rolloff_t;
-
-typedef enum fe_delivery_system {
-        SYS_UNDEFINED,
-        SYS_DVBC_ANNEX_AC,
-        SYS_DVBC_ANNEX_B,
-        SYS_DVBT,
-        SYS_DSS,
-        SYS_DVBS,
-        SYS_DVBS2,
-        SYS_DVBH,
-        SYS_ISDBT,
-        SYS_ISDBS,
-        SYS_ISDBC,
-        SYS_ATSC,
-        SYS_ATSCMH,
-        SYS_DMBTH,
-        SYS_CMMB,
-        SYS_DAB,
-        SYS_DVBT2,
-} fe_delivery_system_t;
-
-struct dtv_cmds_h {
-        char    *name;          /* A display name for debugging purposes */
-
-        __u32   cmd;            /* A unique ID */
-
-        /* Flags */
-        __u32   set:1;          /* Either a set or get property */
-        __u32   buffer:1;       /* Does this property use the buffer? */
-        __u32   reserved:30;    /* Align */
-};
-
-struct dtv_property {
-        __u32 cmd;
-        __u32 reserved[3];
-        union {
-                __u32 data;
-                struct {
-                        __u8 data[32];
-                        __u32 len;
-                        __u32 reserved1[3];
-                        void *reserved2;
-                } buffer;
-        } u;
-        int result;
-} __attribute__ ((packed));
-
-/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
-#define DTV_IOCTL_MAX_MSGS 64
-
-struct dtv_properties {
-        __u32 num;
-        struct dtv_property *props;
-};
-
-#define <link linkend="FE_GET_PROPERTY">FE_SET_PROPERTY</link>            _IOW('o', 82, struct dtv_properties)
-#define <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>            _IOR('o', 83, struct dtv_properties)
-
-
-/**
- * When set, this flag will disable any zigzagging or other "normal" tuning
- * behaviour. Additionally, there will be no automatic monitoring of the lock
- * status, and hence no frontend events will be generated. If a frontend device
- * is closed, this flag will be automatically turned off when the device is
- * reopened read-write.
- */
-#define FE_TUNE_MODE_ONESHOT 0x01
-
-
-#define <link linkend="FE_GET_INFO">FE_GET_INFO</link>                _IOR('o', 61, struct dvb_frontend_info)
-
-#define <link linkend="FE_DISEQC_RESET_OVERLOAD">FE_DISEQC_RESET_OVERLOAD</link>   _IO('o', 62)
-#define <link linkend="FE_DISEQC_SEND_MASTER_CMD">FE_DISEQC_SEND_MASTER_CMD</link>  _IOW('o', 63, struct dvb_diseqc_master_cmd)
-#define <link linkend="FE_DISEQC_RECV_SLAVE_REPLY">FE_DISEQC_RECV_SLAVE_REPLY</link> _IOR('o', 64, struct dvb_diseqc_slave_reply)
-#define <link linkend="FE_DISEQC_SEND_BURST">FE_DISEQC_SEND_BURST</link>       _IO('o', 65)  /* fe_sec_mini_cmd_t */
-
-#define <link linkend="FE_SET_TONE">FE_SET_TONE</link>                _IO('o', 66)  /* fe_sec_tone_mode_t */
-#define <link linkend="FE_SET_VOLTAGE">FE_SET_VOLTAGE</link>             _IO('o', 67)  /* fe_sec_voltage_t */
-#define <link linkend="FE_ENABLE_HIGH_LNB_VOLTAGE">FE_ENABLE_HIGH_LNB_VOLTAGE</link> _IO('o', 68)  /* int */
-
-#define <link linkend="FE_READ_STATUS">FE_READ_STATUS</link>             _IOR('o', 69, fe_status_t)
-#define <link linkend="FE_READ_BER">FE_READ_BER</link>                _IOR('o', 70, __u32)
-#define <link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link>    _IOR('o', 71, __u16)
-#define <link linkend="FE_READ_SNR">FE_READ_SNR</link>                _IOR('o', 72, __u16)
-#define <link linkend="FE_READ_UNCORRECTED_BLOCKS">FE_READ_UNCORRECTED_BLOCKS</link> _IOR('o', 73, __u32)
-
-#define <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link>            _IOW('o', 76, struct dvb_frontend_parameters)
-#define <link linkend="FE_GET_FRONTEND">FE_GET_FRONTEND</link>            _IOR('o', 77, struct dvb_frontend_parameters)
-#define <link linkend="FE_SET_FRONTEND_TUNE_MODE">FE_SET_FRONTEND_TUNE_MODE</link>  _IO('o', 81) /* unsigned int */
-#define <link linkend="FE_GET_EVENT">FE_GET_EVENT</link>               _IOR('o', 78, struct dvb_frontend_event)
-
-#define <link linkend="FE_DISHNETWORK_SEND_LEGACY_CMD">FE_DISHNETWORK_SEND_LEGACY_CMD</link> _IO('o', 80) /* unsigned int */
-
-#endif /*_DVBFRONTEND_H_*/
-</programlisting>
diff --git a/Documentation/DocBook/dvb/net.xml b/Documentation/DocBook/dvb/net.xml
deleted file mode 100644
index 94e388d..0000000
--- a/Documentation/DocBook/dvb/net.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<title>DVB Network API</title>
-<para>The DVB net device enables feeding of MPE (multi protocol encapsulation) packets
-received via DVB into the Linux network protocol stack, e.g. for internet via satellite
-applications. It can be accessed through <emphasis role="tt">/dev/dvb/adapter0/net0</emphasis>. Data types and
-and ioctl definitions can be accessed by including <emphasis role="tt">linux/dvb/net.h</emphasis> in your
-application.
-</para>
-<section id="dvb_net_types">
-<title>DVB Net Data Types</title>
-<para>To be written&#x2026;
-</para>
-</section>
diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl
deleted file mode 100644
index e5fe094..0000000
--- a/Documentation/DocBook/media-entities.tmpl
+++ /dev/null
@@ -1,464 +0,0 @@
-<!-- Generated file! Do not edit. -->
-
-<!-- Functions -->
-<!ENTITY func-close "<link linkend='func-close'><function>close()</function></link>">
-<!ENTITY func-ioctl "<link linkend='func-ioctl'><function>ioctl()</function></link>">
-<!ENTITY func-mmap "<link linkend='func-mmap'><function>mmap()</function></link>">
-<!ENTITY func-munmap "<link linkend='func-munmap'><function>munmap()</function></link>">
-<!ENTITY func-open "<link linkend='func-open'><function>open()</function></link>">
-<!ENTITY func-poll "<link linkend='func-poll'><function>poll()</function></link>">
-<!ENTITY func-read "<link linkend='func-read'><function>read()</function></link>">
-<!ENTITY func-select "<link linkend='func-select'><function>select()</function></link>">
-<!ENTITY func-write "<link linkend='func-write'><function>write()</function></link>">
-
-<!ENTITY media-func-close "<link linkend='media-func-close'><function>close()</function></link>">
-<!ENTITY media-func-ioctl "<link linkend='media-func-ioctl'><function>ioctl()</function></link>">
-<!ENTITY media-func-open "<link linkend='media-func-open'><function>open()</function></link>">
-
-<!-- Ioctls -->
-<!ENTITY VIDIOC-CROPCAP "<link linkend='vidioc-cropcap'><constant>VIDIOC_CROPCAP</constant></link>">
-<!ENTITY VIDIOC-DBG-G-CHIP-IDENT "<link linkend='vidioc-dbg-g-chip-ident'><constant>VIDIOC_DBG_G_CHIP_IDENT</constant></link>">
-<!ENTITY VIDIOC-DBG-G-REGISTER "<link linkend='vidioc-dbg-g-register'><constant>VIDIOC_DBG_G_REGISTER</constant></link>">
-<!ENTITY VIDIOC-DBG-S-REGISTER "<link linkend='vidioc-dbg-g-register'><constant>VIDIOC_DBG_S_REGISTER</constant></link>">
-<!ENTITY VIDIOC-DQBUF "<link linkend='vidioc-qbuf'><constant>VIDIOC_DQBUF</constant></link>">
-<!ENTITY VIDIOC-DQEVENT "<link linkend='vidioc-dqevent'><constant>VIDIOC_DQEVENT</constant></link>">
-<!ENTITY VIDIOC-ENCODER-CMD "<link linkend='vidioc-encoder-cmd'><constant>VIDIOC_ENCODER_CMD</constant></link>">
-<!ENTITY VIDIOC-ENUMAUDIO "<link linkend='vidioc-enumaudio'><constant>VIDIOC_ENUMAUDIO</constant></link>">
-<!ENTITY VIDIOC-ENUMAUDOUT "<link linkend='vidioc-enumaudioout'><constant>VIDIOC_ENUMAUDOUT</constant></link>">
-<!ENTITY VIDIOC-ENUMINPUT "<link linkend='vidioc-enuminput'><constant>VIDIOC_ENUMINPUT</constant></link>">
-<!ENTITY VIDIOC-ENUMOUTPUT "<link linkend='vidioc-enumoutput'><constant>VIDIOC_ENUMOUTPUT</constant></link>">
-<!ENTITY VIDIOC-ENUMSTD "<link linkend='vidioc-enumstd'><constant>VIDIOC_ENUMSTD</constant></link>">
-<!ENTITY VIDIOC-ENUM-DV-PRESETS "<link linkend='vidioc-enum-dv-presets'><constant>VIDIOC_ENUM_DV_PRESETS</constant></link>">
-<!ENTITY VIDIOC-ENUM-FMT "<link linkend='vidioc-enum-fmt'><constant>VIDIOC_ENUM_FMT</constant></link>">
-<!ENTITY VIDIOC-ENUM-FRAMEINTERVALS "<link linkend='vidioc-enum-frameintervals'><constant>VIDIOC_ENUM_FRAMEINTERVALS</constant></link>">
-<!ENTITY VIDIOC-ENUM-FRAMESIZES "<link linkend='vidioc-enum-framesizes'><constant>VIDIOC_ENUM_FRAMESIZES</constant></link>">
-<!ENTITY VIDIOC-G-AUDIO "<link linkend='vidioc-g-audio'><constant>VIDIOC_G_AUDIO</constant></link>">
-<!ENTITY VIDIOC-G-AUDOUT "<link linkend='vidioc-g-audioout'><constant>VIDIOC_G_AUDOUT</constant></link>">
-<!ENTITY VIDIOC-G-CROP "<link linkend='vidioc-g-crop'><constant>VIDIOC_G_CROP</constant></link>">
-<!ENTITY VIDIOC-G-CTRL "<link linkend='vidioc-g-ctrl'><constant>VIDIOC_G_CTRL</constant></link>">
-<!ENTITY VIDIOC-G-DV-PRESET "<link linkend='vidioc-g-dv-preset'><constant>VIDIOC_G_DV_PRESET</constant></link>">
-<!ENTITY VIDIOC-G-DV-TIMINGS "<link linkend='vidioc-g-dv-timings'><constant>VIDIOC_G_DV_TIMINGS</constant></link>">
-<!ENTITY VIDIOC-G-ENC-INDEX "<link linkend='vidioc-g-enc-index'><constant>VIDIOC_G_ENC_INDEX</constant></link>">
-<!ENTITY VIDIOC-G-EXT-CTRLS "<link linkend='vidioc-g-ext-ctrls'><constant>VIDIOC_G_EXT_CTRLS</constant></link>">
-<!ENTITY VIDIOC-G-FBUF "<link linkend='vidioc-g-fbuf'><constant>VIDIOC_G_FBUF</constant></link>">
-<!ENTITY VIDIOC-G-FMT "<link linkend='vidioc-g-fmt'><constant>VIDIOC_G_FMT</constant></link>">
-<!ENTITY VIDIOC-G-FREQUENCY "<link linkend='vidioc-g-frequency'><constant>VIDIOC_G_FREQUENCY</constant></link>">
-<!ENTITY VIDIOC-G-INPUT "<link linkend='vidioc-g-input'><constant>VIDIOC_G_INPUT</constant></link>">
-<!ENTITY VIDIOC-G-JPEGCOMP "<link linkend='vidioc-g-jpegcomp'><constant>VIDIOC_G_JPEGCOMP</constant></link>">
-<!ENTITY VIDIOC-G-MPEGCOMP "<link linkend=''><constant>VIDIOC_G_MPEGCOMP</constant></link>">
-<!ENTITY VIDIOC-G-MODULATOR "<link linkend='vidioc-g-modulator'><constant>VIDIOC_G_MODULATOR</constant></link>">
-<!ENTITY VIDIOC-G-OUTPUT "<link linkend='vidioc-g-output'><constant>VIDIOC_G_OUTPUT</constant></link>">
-<!ENTITY VIDIOC-G-PARM "<link linkend='vidioc-g-parm'><constant>VIDIOC_G_PARM</constant></link>">
-<!ENTITY VIDIOC-G-PRIORITY "<link linkend='vidioc-g-priority'><constant>VIDIOC_G_PRIORITY</constant></link>">
-<!ENTITY VIDIOC-G-SLICED-VBI-CAP "<link linkend='vidioc-g-sliced-vbi-cap'><constant>VIDIOC_G_SLICED_VBI_CAP</constant></link>">
-<!ENTITY VIDIOC-G-STD "<link linkend='vidioc-g-std'><constant>VIDIOC_G_STD</constant></link>">
-<!ENTITY VIDIOC-G-TUNER "<link linkend='vidioc-g-tuner'><constant>VIDIOC_G_TUNER</constant></link>">
-<!ENTITY VIDIOC-LOG-STATUS "<link linkend='vidioc-log-status'><constant>VIDIOC_LOG_STATUS</constant></link>">
-<!ENTITY VIDIOC-OVERLAY "<link linkend='vidioc-overlay'><constant>VIDIOC_OVERLAY</constant></link>">
-<!ENTITY VIDIOC-QBUF "<link linkend='vidioc-qbuf'><constant>VIDIOC_QBUF</constant></link>">
-<!ENTITY VIDIOC-QUERYBUF "<link linkend='vidioc-querybuf'><constant>VIDIOC_QUERYBUF</constant></link>">
-<!ENTITY VIDIOC-QUERYCAP "<link linkend='vidioc-querycap'><constant>VIDIOC_QUERYCAP</constant></link>">
-<!ENTITY VIDIOC-QUERYCTRL "<link linkend='vidioc-queryctrl'><constant>VIDIOC_QUERYCTRL</constant></link>">
-<!ENTITY VIDIOC-QUERYMENU "<link linkend='vidioc-queryctrl'><constant>VIDIOC_QUERYMENU</constant></link>">
-<!ENTITY VIDIOC-QUERYSTD "<link linkend='vidioc-querystd'><constant>VIDIOC_QUERYSTD</constant></link>">
-<!ENTITY VIDIOC-QUERY-DV-PRESET "<link linkend='vidioc-query-dv-preset'><constant>VIDIOC_QUERY_DV_PRESET</constant></link>">
-<!ENTITY VIDIOC-REQBUFS "<link linkend='vidioc-reqbufs'><constant>VIDIOC_REQBUFS</constant></link>">
-<!ENTITY VIDIOC-STREAMOFF "<link linkend='vidioc-streamon'><constant>VIDIOC_STREAMOFF</constant></link>">
-<!ENTITY VIDIOC-STREAMON "<link linkend='vidioc-streamon'><constant>VIDIOC_STREAMON</constant></link>">
-<!ENTITY VIDIOC-SUBSCRIBE-EVENT "<link linkend='vidioc-subscribe-event'><constant>VIDIOC_SUBSCRIBE_EVENT</constant></link>">
-<!ENTITY VIDIOC-S-AUDIO "<link linkend='vidioc-g-audio'><constant>VIDIOC_S_AUDIO</constant></link>">
-<!ENTITY VIDIOC-S-AUDOUT "<link linkend='vidioc-g-audioout'><constant>VIDIOC_S_AUDOUT</constant></link>">
-<!ENTITY VIDIOC-S-CROP "<link linkend='vidioc-g-crop'><constant>VIDIOC_S_CROP</constant></link>">
-<!ENTITY VIDIOC-S-CTRL "<link linkend='vidioc-g-ctrl'><constant>VIDIOC_S_CTRL</constant></link>">
-<!ENTITY VIDIOC-S-DV-PRESET "<link linkend='vidioc-g-dv-preset'><constant>VIDIOC_S_DV_PRESET</constant></link>">
-<!ENTITY VIDIOC-S-DV-TIMINGS "<link linkend='vidioc-g-dv-timings'><constant>VIDIOC_S_DV_TIMINGS</constant></link>">
-<!ENTITY VIDIOC-S-EXT-CTRLS "<link linkend='vidioc-g-ext-ctrls'><constant>VIDIOC_S_EXT_CTRLS</constant></link>">
-<!ENTITY VIDIOC-S-FBUF "<link linkend='vidioc-g-fbuf'><constant>VIDIOC_S_FBUF</constant></link>">
-<!ENTITY VIDIOC-S-FMT "<link linkend='vidioc-g-fmt'><constant>VIDIOC_S_FMT</constant></link>">
-<!ENTITY VIDIOC-S-FREQUENCY "<link linkend='vidioc-g-frequency'><constant>VIDIOC_S_FREQUENCY</constant></link>">
-<!ENTITY VIDIOC-S-HW-FREQ-SEEK "<link linkend='vidioc-s-hw-freq-seek'><constant>VIDIOC_S_HW_FREQ_SEEK</constant></link>">
-<!ENTITY VIDIOC-S-INPUT "<link linkend='vidioc-g-input'><constant>VIDIOC_S_INPUT</constant></link>">
-<!ENTITY VIDIOC-S-JPEGCOMP "<link linkend='vidioc-g-jpegcomp'><constant>VIDIOC_S_JPEGCOMP</constant></link>">
-<!ENTITY VIDIOC-S-MPEGCOMP "<link linkend=''><constant>VIDIOC_S_MPEGCOMP</constant></link>">
-<!ENTITY VIDIOC-S-MODULATOR "<link linkend='vidioc-g-modulator'><constant>VIDIOC_S_MODULATOR</constant></link>">
-<!ENTITY VIDIOC-S-OUTPUT "<link linkend='vidioc-g-output'><constant>VIDIOC_S_OUTPUT</constant></link>">
-<!ENTITY VIDIOC-S-PARM "<link linkend='vidioc-g-parm'><constant>VIDIOC_S_PARM</constant></link>">
-<!ENTITY VIDIOC-S-PRIORITY "<link linkend='vidioc-g-priority'><constant>VIDIOC_S_PRIORITY</constant></link>">
-<!ENTITY VIDIOC-S-STD "<link linkend='vidioc-g-std'><constant>VIDIOC_S_STD</constant></link>">
-<!ENTITY VIDIOC-S-TUNER "<link linkend='vidioc-g-tuner'><constant>VIDIOC_S_TUNER</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-ENUM-FRAME-SIZE "<link linkend='vidioc-subdev-enum-frame-size'><constant>VIDIOC_SUBDEV_ENUM_FRAME_SIZE</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-ENUM-MBUS-CODE "<link linkend='vidioc-subdev-enum-mbus-code'><constant>VIDIOC_SUBDEV_ENUM_MBUS_CODE</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-G-CROP "<link linkend='vidioc-subdev-g-crop'><constant>VIDIOC_SUBDEV_G_CROP</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-G-FMT "<link linkend='vidioc-subdev-g-fmt'><constant>VIDIOC_SUBDEV_G_FMT</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-G-FRAME-INTERVAL "<link linkend='vidioc-subdev-g-frame-interval'><constant>VIDIOC_SUBDEV_G_FRAME_INTERVAL</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-S-CROP "<link linkend='vidioc-subdev-g-crop'><constant>VIDIOC_SUBDEV_S_CROP</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-S-FMT "<link linkend='vidioc-subdev-g-fmt'><constant>VIDIOC_SUBDEV_S_FMT</constant></link>">
-<!ENTITY VIDIOC-SUBDEV-S-FRAME-INTERVAL "<link linkend='vidioc-subdev-g-frame-interval'><constant>VIDIOC_SUBDEV_S_FRAME_INTERVAL</constant></link>">
-<!ENTITY VIDIOC-TRY-ENCODER-CMD "<link linkend='vidioc-encoder-cmd'><constant>VIDIOC_TRY_ENCODER_CMD</constant></link>">
-<!ENTITY VIDIOC-TRY-EXT-CTRLS "<link linkend='vidioc-g-ext-ctrls'><constant>VIDIOC_TRY_EXT_CTRLS</constant></link>">
-<!ENTITY VIDIOC-TRY-FMT "<link linkend='vidioc-g-fmt'><constant>VIDIOC_TRY_FMT</constant></link>">
-<!ENTITY VIDIOC-UNSUBSCRIBE-EVENT "<link linkend='vidioc-subscribe-event'><constant>VIDIOC_UNSUBSCRIBE_EVENT</constant></link>">
-
-<!ENTITY MEDIA-IOC-DEVICE-INFO "<link linkend='media-ioc-device-info'><constant>MEDIA_IOC_DEVICE_INFO</constant></link>">
-<!ENTITY MEDIA-IOC-ENUM-ENTITIES "<link linkend='media-ioc-enum-entities'><constant>MEDIA_IOC_ENUM_ENTITIES</constant></link>">
-<!ENTITY MEDIA-IOC-ENUM-LINKS "<link linkend='media-ioc-enum-links'><constant>MEDIA_IOC_ENUM_LINKS</constant></link>">
-<!ENTITY MEDIA-IOC-SETUP-LINK "<link linkend='media-ioc-setup-link'><constant>MEDIA_IOC_SETUP_LINK</constant></link>">
-
-<!-- Types -->
-<!ENTITY v4l2-std-id "<link linkend='v4l2-std-id'>v4l2_std_id</link>">
-
-<!-- Enums -->
-<!ENTITY v4l2-buf-type "enum&nbsp;<link linkend='v4l2-buf-type'>v4l2_buf_type</link>">
-<!ENTITY v4l2-colorspace "enum&nbsp;<link linkend='v4l2-colorspace'>v4l2_colorspace</link>">
-<!ENTITY v4l2-ctrl-type "enum&nbsp;<link linkend='v4l2-ctrl-type'>v4l2_ctrl_type</link>">
-<!ENTITY v4l2-exposure-auto-type "enum&nbsp;<link linkend='v4l2-exposure-auto-type'>v4l2_exposure_auto_type</link>">
-<!ENTITY v4l2-field "enum&nbsp;<link linkend='v4l2-field'>v4l2_field</link>">
-<!ENTITY v4l2-frmivaltypes "enum&nbsp;<link linkend='v4l2-frmivaltypes'>v4l2_frmivaltypes</link>">
-<!ENTITY v4l2-frmsizetypes "enum&nbsp;<link linkend='v4l2-frmsizetypes'>v4l2_frmsizetypes</link>">
-<!ENTITY v4l2-mbus-pixelcode "enum&nbsp;<link linkend='v4l2-mbus-pixelcode'>v4l2_mbus_pixelcode</link>">
-<!ENTITY v4l2-memory "enum&nbsp;<link linkend='v4l2-memory'>v4l2_memory</link>">
-<!ENTITY v4l2-mpeg-audio-ac3-bitrate "enum&nbsp;<link linkend='v4l2-mpeg-audio-ac3-bitrate'>v4l2_mpeg_audio_ac3_bitrate</link>">
-<!ENTITY v4l2-mpeg-audio-crc "enum&nbsp;<link linkend='v4l2-mpeg-audio-crc'>v4l2_mpeg_audio_crc</link>">
-<!ENTITY v4l2-mpeg-audio-emphasis "enum&nbsp;<link linkend='v4l2-mpeg-audio-emphasis'>v4l2_mpeg_audio_emphasis</link>">
-<!ENTITY v4l2-mpeg-audio-encoding "enum&nbsp;<link linkend='v4l2-mpeg-audio-encoding'>v4l2_mpeg_audio_encoding</link>">
-<!ENTITY v4l2-mpeg-audio-l1-bitrate "enum&nbsp;<link linkend='v4l2-mpeg-audio-l1-bitrate'>v4l2_mpeg_audio_l1_bitrate</link>">
-<!ENTITY v4l2-mpeg-audio-l2-bitrate "enum&nbsp;<link linkend='v4l2-mpeg-audio-l2-bitrate'>v4l2_mpeg_audio_l2_bitrate</link>">
-<!ENTITY v4l2-mpeg-audio-l3-bitrate "enum&nbsp;<link linkend='v4l2-mpeg-audio-l3-bitrate'>v4l2_mpeg_audio_l3_bitrate</link>">
-<!ENTITY v4l2-mpeg-audio-mode "enum&nbsp;<link linkend='v4l2-mpeg-audio-mode'>v4l2_mpeg_audio_mode</link>">
-<!ENTITY v4l2-mpeg-audio-mode-extension "enum&nbsp;<link linkend='v4l2-mpeg-audio-mode-extension'>v4l2_mpeg_audio_mode_extension</link>">
-<!ENTITY v4l2-mpeg-audio-sampling-freq "enum&nbsp;<link linkend='v4l2-mpeg-audio-sampling-freq'>v4l2_mpeg_audio_sampling_freq</link>">
-<!ENTITY chroma-spatial-filter-type "enum&nbsp;<link linkend='chroma-spatial-filter-type'>v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type</link>">
-<!ENTITY luma-spatial-filter-type "enum&nbsp;<link linkend='luma-spatial-filter-type'>v4l2_mpeg_cx2341x_video_luma_spatial_filter_type</link>">
-<!ENTITY v4l2-mpeg-cx2341x-video-median-filter-type "enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-median-filter-type'>v4l2_mpeg_cx2341x_video_median_filter_type</link>">
-<!ENTITY v4l2-mpeg-cx2341x-video-spatial-filter-mode "enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-spatial-filter-mode'>v4l2_mpeg_cx2341x_video_spatial_filter_mode</link>">
-<!ENTITY v4l2-mpeg-cx2341x-video-temporal-filter-mode "enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-temporal-filter-mode'>v4l2_mpeg_cx2341x_video_temporal_filter_mode</link>">
-<!ENTITY v4l2-mpeg-stream-type "enum&nbsp;<link linkend='v4l2-mpeg-stream-type'>v4l2_mpeg_stream_type</link>">
-<!ENTITY v4l2-mpeg-stream-vbi-fmt "enum&nbsp;<link linkend='v4l2-mpeg-stream-vbi-fmt'>v4l2_mpeg_stream_vbi_fmt</link>">
-<!ENTITY v4l2-mpeg-video-aspect "enum&nbsp;<link linkend='v4l2-mpeg-video-aspect'>v4l2_mpeg_video_aspect</link>">
-<!ENTITY v4l2-mpeg-video-bitrate-mode "enum&nbsp;<link linkend='v4l2-mpeg-video-bitrate-mode'>v4l2_mpeg_video_bitrate_mode</link>">
-<!ENTITY v4l2-mpeg-video-encoding "enum&nbsp;<link linkend='v4l2-mpeg-video-encoding'>v4l2_mpeg_video_encoding</link>">
-<!ENTITY v4l2-power-line-frequency "enum&nbsp;<link linkend='v4l2-power-line-frequency'>v4l2_power_line_frequency</link>">
-<!ENTITY v4l2-priority "enum&nbsp;<link linkend='v4l2-priority'>v4l2_priority</link>">
-<!ENTITY v4l2-subdev-format-whence "enum&nbsp;<link linkend='v4l2-subdev-format-whence'>v4l2_subdev_format_whence</link>">
-<!ENTITY v4l2-tuner-type "enum&nbsp;<link linkend='v4l2-tuner-type'>v4l2_tuner_type</link>">
-<!ENTITY v4l2-preemphasis "enum&nbsp;<link linkend='v4l2-preemphasis'>v4l2_preemphasis</link>">
-
-<!-- Structures -->
-<!ENTITY v4l2-audio "struct&nbsp;<link linkend='v4l2-audio'>v4l2_audio</link>">
-<!ENTITY v4l2-audioout "struct&nbsp;<link linkend='v4l2-audioout'>v4l2_audioout</link>">
-<!ENTITY v4l2-bt-timings "struct&nbsp;<link linkend='v4l2-bt-timings'>v4l2_bt_timings</link>">
-<!ENTITY v4l2-buffer "struct&nbsp;<link linkend='v4l2-buffer'>v4l2_buffer</link>">
-<!ENTITY v4l2-plane "struct&nbsp;<link linkend='v4l2-plane'>v4l2_plane</link>">
-<!ENTITY v4l2-capability "struct&nbsp;<link linkend='v4l2-capability'>v4l2_capability</link>">
-<!ENTITY v4l2-captureparm "struct&nbsp;<link linkend='v4l2-captureparm'>v4l2_captureparm</link>">
-<!ENTITY v4l2-clip "struct&nbsp;<link linkend='v4l2-clip'>v4l2_clip</link>">
-<!ENTITY v4l2-control "struct&nbsp;<link linkend='v4l2-control'>v4l2_control</link>">
-<!ENTITY v4l2-crop "struct&nbsp;<link linkend='v4l2-crop'>v4l2_crop</link>">
-<!ENTITY v4l2-cropcap "struct&nbsp;<link linkend='v4l2-cropcap'>v4l2_cropcap</link>">
-<!ENTITY v4l2-dbg-chip-ident "struct&nbsp;<link linkend='v4l2-dbg-chip-ident'>v4l2_dbg_chip_ident</link>">
-<!ENTITY v4l2-dbg-match "struct&nbsp;<link linkend='v4l2-dbg-match'>v4l2_dbg_match</link>">
-<!ENTITY v4l2-dbg-register "struct&nbsp;<link linkend='v4l2-dbg-register'>v4l2_dbg_register</link>">
-<!ENTITY v4l2-dv-enum-preset "struct&nbsp;<link linkend='v4l2-dv-enum-preset'>v4l2_dv_enum_preset</link>">
-<!ENTITY v4l2-dv-preset "struct&nbsp;<link linkend='v4l2-dv-preset'>v4l2_dv_preset</link>">
-<!ENTITY v4l2-dv-timings "struct&nbsp;<link linkend='v4l2-dv-timings'>v4l2_dv_timings</link>">
-<!ENTITY v4l2-enc-idx "struct&nbsp;<link linkend='v4l2-enc-idx'>v4l2_enc_idx</link>">
-<!ENTITY v4l2-enc-idx-entry "struct&nbsp;<link linkend='v4l2-enc-idx-entry'>v4l2_enc_idx_entry</link>">
-<!ENTITY v4l2-encoder-cmd "struct&nbsp;<link linkend='v4l2-encoder-cmd'>v4l2_encoder_cmd</link>">
-<!ENTITY v4l2-event "struct&nbsp;<link linkend='v4l2-event'>v4l2_event</link>">
-<!ENTITY v4l2-event-subscription "struct&nbsp;<link linkend='v4l2-event-subscription'>v4l2_event_subscription</link>">
-<!ENTITY v4l2-event-vsync "struct&nbsp;<link linkend='v4l2-event-vsync'>v4l2_event_vsync</link>">
-<!ENTITY v4l2-ext-control "struct&nbsp;<link linkend='v4l2-ext-control'>v4l2_ext_control</link>">
-<!ENTITY v4l2-ext-controls "struct&nbsp;<link linkend='v4l2-ext-controls'>v4l2_ext_controls</link>">
-<!ENTITY v4l2-fmtdesc "struct&nbsp;<link linkend='v4l2-fmtdesc'>v4l2_fmtdesc</link>">
-<!ENTITY v4l2-format "struct&nbsp;<link linkend='v4l2-format'>v4l2_format</link>">
-<!ENTITY v4l2-fract "struct&nbsp;<link linkend='v4l2-fract'>v4l2_fract</link>">
-<!ENTITY v4l2-framebuffer "struct&nbsp;<link linkend='v4l2-framebuffer'>v4l2_framebuffer</link>">
-<!ENTITY v4l2-frequency "struct&nbsp;<link linkend='v4l2-frequency'>v4l2_frequency</link>">
-<!ENTITY v4l2-frmival-stepwise "struct&nbsp;<link linkend='v4l2-frmival-stepwise'>v4l2_frmival_stepwise</link>">
-<!ENTITY v4l2-frmivalenum "struct&nbsp;<link linkend='v4l2-frmivalenum'>v4l2_frmivalenum</link>">
-<!ENTITY v4l2-frmsize-discrete "struct&nbsp;<link linkend='v4l2-frmsize-discrete'>v4l2_frmsize_discrete</link>">
-<!ENTITY v4l2-frmsize-stepwise "struct&nbsp;<link linkend='v4l2-frmsize-stepwise'>v4l2_frmsize_stepwise</link>">
-<!ENTITY v4l2-frmsizeenum "struct&nbsp;<link linkend='v4l2-frmsizeenum'>v4l2_frmsizeenum</link>">
-<!ENTITY v4l2-hw-freq-seek "struct&nbsp;<link linkend='v4l2-hw-freq-seek'>v4l2_hw_freq_seek</link>">
-<!ENTITY v4l2-input "struct&nbsp;<link linkend='v4l2-input'>v4l2_input</link>">
-<!ENTITY v4l2-jpegcompression "struct&nbsp;<link linkend='v4l2-jpegcompression'>v4l2_jpegcompression</link>">
-<!ENTITY v4l2-mbus-framefmt "struct&nbsp;<link linkend='v4l2-mbus-framefmt'>v4l2_mbus_framefmt</link>">
-<!ENTITY v4l2-modulator "struct&nbsp;<link linkend='v4l2-modulator'>v4l2_modulator</link>">
-<!ENTITY v4l2-mpeg-vbi-fmt-ivtv "struct&nbsp;<link linkend='v4l2-mpeg-vbi-fmt-ivtv'>v4l2_mpeg_vbi_fmt_ivtv</link>">
-<!ENTITY v4l2-output "struct&nbsp;<link linkend='v4l2-output'>v4l2_output</link>">
-<!ENTITY v4l2-outputparm "struct&nbsp;<link linkend='v4l2-outputparm'>v4l2_outputparm</link>">
-<!ENTITY v4l2-pix-format "struct&nbsp;<link linkend='v4l2-pix-format'>v4l2_pix_format</link>">
-<!ENTITY v4l2-pix-format-mplane "struct&nbsp;<link linkend='v4l2-pix-format-mplane'>v4l2_pix_format_mplane</link>">
-<!ENTITY v4l2-plane-pix-format "struct&nbsp;<link linkend='v4l2-plane-pix-format'>v4l2_plane_pix_format</link>">
-<!ENTITY v4l2-queryctrl "struct&nbsp;<link linkend='v4l2-queryctrl'>v4l2_queryctrl</link>">
-<!ENTITY v4l2-querymenu "struct&nbsp;<link linkend='v4l2-querymenu'>v4l2_querymenu</link>">
-<!ENTITY v4l2-rect "struct&nbsp;<link linkend='v4l2-rect'>v4l2_rect</link>">
-<!ENTITY v4l2-requestbuffers "struct&nbsp;<link linkend='v4l2-requestbuffers'>v4l2_requestbuffers</link>">
-<!ENTITY v4l2-sliced-vbi-cap "struct&nbsp;<link linkend='v4l2-sliced-vbi-cap'>v4l2_sliced_vbi_cap</link>">
-<!ENTITY v4l2-sliced-vbi-data "struct&nbsp;<link linkend='v4l2-sliced-vbi-data'>v4l2_sliced_vbi_data</link>">
-<!ENTITY v4l2-sliced-vbi-format "struct&nbsp;<link linkend='v4l2-sliced-vbi-format'>v4l2_sliced_vbi_format</link>">
-<!ENTITY v4l2-subdev-frame-interval "struct&nbsp;<link linkend='v4l2-subdev-frame-interval'>v4l2_subdev_frame_interval</link>">
-<!ENTITY v4l2-subdev-frame-interval-enum "struct&nbsp;<link linkend='v4l2-subdev-frame-interval-enum'>v4l2_subdev_frame_interval_enum</link>">
-<!ENTITY v4l2-subdev-frame-size-enum "struct&nbsp;<link linkend='v4l2-subdev-frame-size-enum'>v4l2_subdev_frame_size_enum</link>">
-<!ENTITY v4l2-subdev-crop "struct&nbsp;<link linkend='v4l2-subdev-crop'>v4l2_subdev_crop</link>">
-<!ENTITY v4l2-subdev-format "struct&nbsp;<link linkend='v4l2-subdev-format'>v4l2_subdev_format</link>">
-<!ENTITY v4l2-subdev-mbus-code-enum "struct&nbsp;<link linkend='v4l2-subdev-mbus-code-enum'>v4l2_subdev_mbus_code_enum</link>">
-<!ENTITY v4l2-standard "struct&nbsp;<link linkend='v4l2-standard'>v4l2_standard</link>">
-<!ENTITY v4l2-streamparm "struct&nbsp;<link linkend='v4l2-streamparm'>v4l2_streamparm</link>">
-<!ENTITY v4l2-timecode "struct&nbsp;<link linkend='v4l2-timecode'>v4l2_timecode</link>">
-<!ENTITY v4l2-tuner "struct&nbsp;<link linkend='v4l2-tuner'>v4l2_tuner</link>">
-<!ENTITY v4l2-vbi-format "struct&nbsp;<link linkend='v4l2-vbi-format'>v4l2_vbi_format</link>">
-<!ENTITY v4l2-window "struct&nbsp;<link linkend='v4l2-window'>v4l2_window</link>">
-
-<!ENTITY media-device-info "struct&nbsp;<link linkend='media-device-info'>media_device_info</link>">
-<!ENTITY media-entity-desc "struct&nbsp;<link linkend='media-entity-desc'>media_entity_desc</link>">
-<!ENTITY media-links-enum "struct&nbsp;<link linkend='media-links-enum'>media_links_enum</link>">
-<!ENTITY media-pad-desc "struct&nbsp;<link linkend='media-pad-desc'>media_pad_desc</link>">
-<!ENTITY media-link-desc "struct&nbsp;<link linkend='media-link-desc'>media_link_desc</link>">
-
-<!-- Error Codes -->
-<!ENTITY EACCES "<errorcode>EACCES</errorcode> error code">
-<!ENTITY EAGAIN "<errorcode>EAGAIN</errorcode> error code">
-<!ENTITY EBADF "<errorcode>EBADF</errorcode> error code">
-<!ENTITY EBUSY "<errorcode>EBUSY</errorcode> error code">
-<!ENTITY EFAULT "<errorcode>EFAULT</errorcode> error code">
-<!ENTITY EIO "<errorcode>EIO</errorcode> error code">
-<!ENTITY EINTR "<errorcode>EINTR</errorcode> error code">
-<!ENTITY EINVAL "<errorcode>EINVAL</errorcode> error code">
-<!ENTITY ENFILE "<errorcode>ENFILE</errorcode> error code">
-<!ENTITY ENOMEM "<errorcode>ENOMEM</errorcode> error code">
-<!ENTITY ENOSPC "<errorcode>ENOSPC</errorcode> error code">
-<!ENTITY ENOTTY "<errorcode>ENOTTY</errorcode> error code">
-<!ENTITY ENXIO "<errorcode>ENXIO</errorcode> error code">
-<!ENTITY EMFILE "<errorcode>EMFILE</errorcode> error code">
-<!ENTITY EPERM "<errorcode>EPERM</errorcode> error code">
-<!ENTITY EPIPE "<errorcode>EPIPE</errorcode> error code">
-<!ENTITY ERANGE "<errorcode>ERANGE</errorcode> error code">
-
-<!-- Subsections -->
-<!ENTITY sub-biblio SYSTEM "v4l/biblio.xml">
-<!ENTITY sub-common SYSTEM "v4l/common.xml">
-<!ENTITY sub-planar-apis SYSTEM "v4l/planar-apis.xml">
-<!ENTITY sub-compat SYSTEM "v4l/compat.xml">
-<!ENTITY sub-controls SYSTEM "v4l/controls.xml">
-<!ENTITY sub-dev-capture SYSTEM "v4l/dev-capture.xml">
-<!ENTITY sub-dev-codec SYSTEM "v4l/dev-codec.xml">
-<!ENTITY sub-dev-event SYSTEM "v4l/dev-event.xml">
-<!ENTITY sub-dev-effect SYSTEM "v4l/dev-effect.xml">
-<!ENTITY sub-dev-osd SYSTEM "v4l/dev-osd.xml">
-<!ENTITY sub-dev-output SYSTEM "v4l/dev-output.xml">
-<!ENTITY sub-dev-overlay SYSTEM "v4l/dev-overlay.xml">
-<!ENTITY sub-dev-radio SYSTEM "v4l/dev-radio.xml">
-<!ENTITY sub-dev-raw-vbi SYSTEM "v4l/dev-raw-vbi.xml">
-<!ENTITY sub-dev-rds SYSTEM "v4l/dev-rds.xml">
-<!ENTITY sub-dev-sliced-vbi SYSTEM "v4l/dev-sliced-vbi.xml">
-<!ENTITY sub-dev-subdev SYSTEM "v4l/dev-subdev.xml">
-<!ENTITY sub-dev-teletext SYSTEM "v4l/dev-teletext.xml">
-<!ENTITY sub-driver SYSTEM "v4l/driver.xml">
-<!ENTITY sub-libv4l SYSTEM "v4l/libv4l.xml">
-<!ENTITY sub-lirc_device_interface SYSTEM "v4l/lirc_device_interface.xml">
-<!ENTITY sub-remote_controllers SYSTEM "v4l/remote_controllers.xml">
-<!ENTITY sub-fdl-appendix SYSTEM "v4l/fdl-appendix.xml">
-<!ENTITY sub-close SYSTEM "v4l/func-close.xml">
-<!ENTITY sub-ioctl SYSTEM "v4l/func-ioctl.xml">
-<!ENTITY sub-mmap SYSTEM "v4l/func-mmap.xml">
-<!ENTITY sub-munmap SYSTEM "v4l/func-munmap.xml">
-<!ENTITY sub-open SYSTEM "v4l/func-open.xml">
-<!ENTITY sub-poll SYSTEM "v4l/func-poll.xml">
-<!ENTITY sub-read SYSTEM "v4l/func-read.xml">
-<!ENTITY sub-select SYSTEM "v4l/func-select.xml">
-<!ENTITY sub-write SYSTEM "v4l/func-write.xml">
-<!ENTITY sub-io SYSTEM "v4l/io.xml">
-<!ENTITY sub-grey SYSTEM "v4l/pixfmt-grey.xml">
-<!ENTITY sub-m420 SYSTEM "v4l/pixfmt-m420.xml">
-<!ENTITY sub-nv12 SYSTEM "v4l/pixfmt-nv12.xml">
-<!ENTITY sub-nv12m SYSTEM "v4l/pixfmt-nv12m.xml">
-<!ENTITY sub-nv12mt SYSTEM "v4l/pixfmt-nv12mt.xml">
-<!ENTITY sub-nv16 SYSTEM "v4l/pixfmt-nv16.xml">
-<!ENTITY sub-packed-rgb SYSTEM "v4l/pixfmt-packed-rgb.xml">
-<!ENTITY sub-packed-yuv SYSTEM "v4l/pixfmt-packed-yuv.xml">
-<!ENTITY sub-sbggr16 SYSTEM "v4l/pixfmt-sbggr16.xml">
-<!ENTITY sub-sbggr8 SYSTEM "v4l/pixfmt-sbggr8.xml">
-<!ENTITY sub-sgbrg8 SYSTEM "v4l/pixfmt-sgbrg8.xml">
-<!ENTITY sub-sgrbg8 SYSTEM "v4l/pixfmt-sgrbg8.xml">
-<!ENTITY sub-uyvy SYSTEM "v4l/pixfmt-uyvy.xml">
-<!ENTITY sub-vyuy SYSTEM "v4l/pixfmt-vyuy.xml">
-<!ENTITY sub-y16 SYSTEM "v4l/pixfmt-y16.xml">
-<!ENTITY sub-y41p SYSTEM "v4l/pixfmt-y41p.xml">
-<!ENTITY sub-yuv410 SYSTEM "v4l/pixfmt-yuv410.xml">
-<!ENTITY sub-yuv411p SYSTEM "v4l/pixfmt-yuv411p.xml">
-<!ENTITY sub-yuv420 SYSTEM "v4l/pixfmt-yuv420.xml">
-<!ENTITY sub-yuv420m SYSTEM "v4l/pixfmt-yuv420m.xml">
-<!ENTITY sub-yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
-<!ENTITY sub-yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
-<!ENTITY sub-yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
-<!ENTITY sub-srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
-<!ENTITY sub-srggb12 SYSTEM "v4l/pixfmt-srggb12.xml">
-<!ENTITY sub-srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
-<!ENTITY sub-y10 SYSTEM "v4l/pixfmt-y10.xml">
-<!ENTITY sub-y12 SYSTEM "v4l/pixfmt-y12.xml">
-<!ENTITY sub-y10b SYSTEM "v4l/pixfmt-y10b.xml">
-<!ENTITY sub-pixfmt SYSTEM "v4l/pixfmt.xml">
-<!ENTITY sub-cropcap SYSTEM "v4l/vidioc-cropcap.xml">
-<!ENTITY sub-dbg-g-register SYSTEM "v4l/vidioc-dbg-g-register.xml">
-<!ENTITY sub-encoder-cmd SYSTEM "v4l/vidioc-encoder-cmd.xml">
-<!ENTITY sub-enum-fmt SYSTEM "v4l/vidioc-enum-fmt.xml">
-<!ENTITY sub-enum-frameintervals SYSTEM "v4l/vidioc-enum-frameintervals.xml">
-<!ENTITY sub-enum-framesizes SYSTEM "v4l/vidioc-enum-framesizes.xml">
-<!ENTITY sub-enumaudio SYSTEM "v4l/vidioc-enumaudio.xml">
-<!ENTITY sub-enumaudioout SYSTEM "v4l/vidioc-enumaudioout.xml">
-<!ENTITY sub-enuminput SYSTEM "v4l/vidioc-enuminput.xml">
-<!ENTITY sub-enumoutput SYSTEM "v4l/vidioc-enumoutput.xml">
-<!ENTITY sub-enum-dv-presets SYSTEM "v4l/vidioc-enum-dv-presets.xml">
-<!ENTITY sub-g-dv-preset SYSTEM "v4l/vidioc-g-dv-preset.xml">
-<!ENTITY sub-query-dv-preset SYSTEM "v4l/vidioc-query-dv-preset.xml">
-<!ENTITY sub-g-dv-timings SYSTEM "v4l/vidioc-g-dv-timings.xml">
-<!ENTITY sub-enumstd SYSTEM "v4l/vidioc-enumstd.xml">
-<!ENTITY sub-g-audio SYSTEM "v4l/vidioc-g-audio.xml">
-<!ENTITY sub-g-audioout SYSTEM "v4l/vidioc-g-audioout.xml">
-<!ENTITY sub-dbg-g-chip-ident SYSTEM "v4l/vidioc-dbg-g-chip-ident.xml">
-<!ENTITY sub-g-crop SYSTEM "v4l/vidioc-g-crop.xml">
-<!ENTITY sub-g-ctrl SYSTEM "v4l/vidioc-g-ctrl.xml">
-<!ENTITY sub-g-enc-index SYSTEM "v4l/vidioc-g-enc-index.xml">
-<!ENTITY sub-g-ext-ctrls SYSTEM "v4l/vidioc-g-ext-ctrls.xml">
-<!ENTITY sub-g-fbuf SYSTEM "v4l/vidioc-g-fbuf.xml">
-<!ENTITY sub-g-fmt SYSTEM "v4l/vidioc-g-fmt.xml">
-<!ENTITY sub-g-frequency SYSTEM "v4l/vidioc-g-frequency.xml">
-<!ENTITY sub-g-input SYSTEM "v4l/vidioc-g-input.xml">
-<!ENTITY sub-g-jpegcomp SYSTEM "v4l/vidioc-g-jpegcomp.xml">
-<!ENTITY sub-g-modulator SYSTEM "v4l/vidioc-g-modulator.xml">
-<!ENTITY sub-g-output SYSTEM "v4l/vidioc-g-output.xml">
-<!ENTITY sub-g-parm SYSTEM "v4l/vidioc-g-parm.xml">
-<!ENTITY sub-g-priority SYSTEM "v4l/vidioc-g-priority.xml">
-<!ENTITY sub-g-sliced-vbi-cap SYSTEM "v4l/vidioc-g-sliced-vbi-cap.xml">
-<!ENTITY sub-g-std SYSTEM "v4l/vidioc-g-std.xml">
-<!ENTITY sub-g-tuner SYSTEM "v4l/vidioc-g-tuner.xml">
-<!ENTITY sub-log-status SYSTEM "v4l/vidioc-log-status.xml">
-<!ENTITY sub-overlay SYSTEM "v4l/vidioc-overlay.xml">
-<!ENTITY sub-qbuf SYSTEM "v4l/vidioc-qbuf.xml">
-<!ENTITY sub-querybuf SYSTEM "v4l/vidioc-querybuf.xml">
-<!ENTITY sub-querycap SYSTEM "v4l/vidioc-querycap.xml">
-<!ENTITY sub-queryctrl SYSTEM "v4l/vidioc-queryctrl.xml">
-<!ENTITY sub-querystd SYSTEM "v4l/vidioc-querystd.xml">
-<!ENTITY sub-reqbufs SYSTEM "v4l/vidioc-reqbufs.xml">
-<!ENTITY sub-s-hw-freq-seek SYSTEM "v4l/vidioc-s-hw-freq-seek.xml">
-<!ENTITY sub-streamon SYSTEM "v4l/vidioc-streamon.xml">
-<!ENTITY sub-subdev-enum-frame-interval SYSTEM "v4l/vidioc-subdev-enum-frame-interval.xml">
-<!ENTITY sub-subdev-enum-frame-size SYSTEM "v4l/vidioc-subdev-enum-frame-size.xml">
-<!ENTITY sub-subdev-enum-mbus-code SYSTEM "v4l/vidioc-subdev-enum-mbus-code.xml">
-<!ENTITY sub-subdev-formats SYSTEM "v4l/subdev-formats.xml">
-<!ENTITY sub-subdev-g-crop SYSTEM "v4l/vidioc-subdev-g-crop.xml">
-<!ENTITY sub-subdev-g-fmt SYSTEM "v4l/vidioc-subdev-g-fmt.xml">
-<!ENTITY sub-subdev-g-frame-interval SYSTEM "v4l/vidioc-subdev-g-frame-interval.xml">
-<!ENTITY sub-capture-c SYSTEM "v4l/capture.c.xml">
-<!ENTITY sub-keytable-c SYSTEM "v4l/keytable.c.xml">
-<!ENTITY sub-v4l2grab-c SYSTEM "v4l/v4l2grab.c.xml">
-<!ENTITY sub-videodev2-h SYSTEM "v4l/videodev2.h.xml">
-<!ENTITY sub-v4l2 SYSTEM "v4l/v4l2.xml">
-<!ENTITY sub-dqevent SYSTEM "v4l/vidioc-dqevent.xml">
-<!ENTITY sub-subscribe-event SYSTEM "v4l/vidioc-subscribe-event.xml">
-<!ENTITY sub-intro SYSTEM "dvb/intro.xml">
-<!ENTITY sub-frontend SYSTEM "dvb/frontend.xml">
-<!ENTITY sub-dvbproperty SYSTEM "dvb/dvbproperty.xml">
-<!ENTITY sub-demux SYSTEM "dvb/demux.xml">
-<!ENTITY sub-video SYSTEM "dvb/video.xml">
-<!ENTITY sub-audio SYSTEM "dvb/audio.xml">
-<!ENTITY sub-ca SYSTEM "dvb/ca.xml">
-<!ENTITY sub-net SYSTEM "dvb/net.xml">
-<!ENTITY sub-kdapi SYSTEM "dvb/kdapi.xml">
-<!ENTITY sub-examples SYSTEM "dvb/examples.xml">
-<!ENTITY sub-frontend-h SYSTEM "dvb/frontend.h.xml">
-<!ENTITY sub-dvbapi SYSTEM "dvb/dvbapi.xml">
-<!ENTITY sub-media SYSTEM "media.xml">
-<!ENTITY sub-media-entities SYSTEM "media-entities.tmpl">
-<!ENTITY sub-media-indices SYSTEM "media-indices.tmpl">
-
-<!ENTITY sub-media-controller SYSTEM "v4l/media-controller.xml">
-<!ENTITY sub-media-func-open SYSTEM "v4l/media-func-open.xml">
-<!ENTITY sub-media-func-close SYSTEM "v4l/media-func-close.xml">
-<!ENTITY sub-media-func-ioctl SYSTEM "v4l/media-func-ioctl.xml">
-<!ENTITY sub-media-ioc-device-info SYSTEM "v4l/media-ioc-device-info.xml">
-<!ENTITY sub-media-ioc-enum-entities SYSTEM "v4l/media-ioc-enum-entities.xml">
-<!ENTITY sub-media-ioc-enum-links SYSTEM "v4l/media-ioc-enum-links.xml">
-<!ENTITY sub-media-ioc-setup-link SYSTEM "v4l/media-ioc-setup-link.xml">
-
-<!-- Function Reference -->
-<!ENTITY close SYSTEM "v4l/func-close.xml">
-<!ENTITY ioctl SYSTEM "v4l/func-ioctl.xml">
-<!ENTITY mmap SYSTEM "v4l/func-mmap.xml">
-<!ENTITY munmap SYSTEM "v4l/func-munmap.xml">
-<!ENTITY open SYSTEM "v4l/func-open.xml">
-<!ENTITY poll SYSTEM "v4l/func-poll.xml">
-<!ENTITY read SYSTEM "v4l/func-read.xml">
-<!ENTITY select SYSTEM "v4l/func-select.xml">
-<!ENTITY write SYSTEM "v4l/func-write.xml">
-<!ENTITY grey SYSTEM "v4l/pixfmt-grey.xml">
-<!ENTITY nv12 SYSTEM "v4l/pixfmt-nv12.xml">
-<!ENTITY nv12m SYSTEM "v4l/pixfmt-nv12m.xml">
-<!ENTITY nv16 SYSTEM "v4l/pixfmt-nv16.xml">
-<!ENTITY packed-rgb SYSTEM "v4l/pixfmt-packed-rgb.xml">
-<!ENTITY packed-yuv SYSTEM "v4l/pixfmt-packed-yuv.xml">
-<!ENTITY sbggr16 SYSTEM "v4l/pixfmt-sbggr16.xml">
-<!ENTITY sbggr8 SYSTEM "v4l/pixfmt-sbggr8.xml">
-<!ENTITY sgbrg8 SYSTEM "v4l/pixfmt-sgbrg8.xml">
-<!ENTITY sgrbg8 SYSTEM "v4l/pixfmt-sgrbg8.xml">
-<!ENTITY uyvy SYSTEM "v4l/pixfmt-uyvy.xml">
-<!ENTITY vyuy SYSTEM "v4l/pixfmt-vyuy.xml">
-<!ENTITY y16 SYSTEM "v4l/pixfmt-y16.xml">
-<!ENTITY y41p SYSTEM "v4l/pixfmt-y41p.xml">
-<!ENTITY yuv410 SYSTEM "v4l/pixfmt-yuv410.xml">
-<!ENTITY yuv411p SYSTEM "v4l/pixfmt-yuv411p.xml">
-<!ENTITY yuv420 SYSTEM "v4l/pixfmt-yuv420.xml">
-<!ENTITY yuv420m SYSTEM "v4l/pixfmt-yuv420m.xml">
-<!ENTITY yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
-<!ENTITY yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
-<!ENTITY yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
-<!ENTITY srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
-<!ENTITY srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
-<!ENTITY y10 SYSTEM "v4l/pixfmt-y10.xml">
-<!ENTITY cropcap SYSTEM "v4l/vidioc-cropcap.xml">
-<!ENTITY dbg-g-register SYSTEM "v4l/vidioc-dbg-g-register.xml">
-<!ENTITY encoder-cmd SYSTEM "v4l/vidioc-encoder-cmd.xml">
-<!ENTITY enum-fmt SYSTEM "v4l/vidioc-enum-fmt.xml">
-<!ENTITY enum-frameintervals SYSTEM "v4l/vidioc-enum-frameintervals.xml">
-<!ENTITY enum-framesizes SYSTEM "v4l/vidioc-enum-framesizes.xml">
-<!ENTITY enumaudio SYSTEM "v4l/vidioc-enumaudio.xml">
-<!ENTITY enumaudioout SYSTEM "v4l/vidioc-enumaudioout.xml">
-<!ENTITY enuminput SYSTEM "v4l/vidioc-enuminput.xml">
-<!ENTITY enumoutput SYSTEM "v4l/vidioc-enumoutput.xml">
-<!ENTITY enum-dv-presets SYSTEM "v4l/vidioc-enum-dv-presets.xml">
-<!ENTITY g-dv-preset SYSTEM "v4l/vidioc-g-dv-preset.xml">
-<!ENTITY query-dv-preset SYSTEM "v4l/vidioc-query-dv-preset.xml">
-<!ENTITY g-dv-timings SYSTEM "v4l/vidioc-g-dv-timings.xml">
-<!ENTITY enumstd SYSTEM "v4l/vidioc-enumstd.xml">
-<!ENTITY g-audio SYSTEM "v4l/vidioc-g-audio.xml">
-<!ENTITY g-audioout SYSTEM "v4l/vidioc-g-audioout.xml">
-<!ENTITY dbg-g-chip-ident SYSTEM "v4l/vidioc-dbg-g-chip-ident.xml">
-<!ENTITY g-crop SYSTEM "v4l/vidioc-g-crop.xml">
-<!ENTITY g-ctrl SYSTEM "v4l/vidioc-g-ctrl.xml">
-<!ENTITY g-enc-index SYSTEM "v4l/vidioc-g-enc-index.xml">
-<!ENTITY g-ext-ctrls SYSTEM "v4l/vidioc-g-ext-ctrls.xml">
-<!ENTITY g-fbuf SYSTEM "v4l/vidioc-g-fbuf.xml">
-<!ENTITY g-fmt SYSTEM "v4l/vidioc-g-fmt.xml">
-<!ENTITY g-frequency SYSTEM "v4l/vidioc-g-frequency.xml">
-<!ENTITY g-input SYSTEM "v4l/vidioc-g-input.xml">
-<!ENTITY g-jpegcomp SYSTEM "v4l/vidioc-g-jpegcomp.xml">
-<!ENTITY g-modulator SYSTEM "v4l/vidioc-g-modulator.xml">
-<!ENTITY g-output SYSTEM "v4l/vidioc-g-output.xml">
-<!ENTITY g-parm SYSTEM "v4l/vidioc-g-parm.xml">
-<!ENTITY g-priority SYSTEM "v4l/vidioc-g-priority.xml">
-<!ENTITY g-sliced-vbi-cap SYSTEM "v4l/vidioc-g-sliced-vbi-cap.xml">
-<!ENTITY g-std SYSTEM "v4l/vidioc-g-std.xml">
-<!ENTITY g-tuner SYSTEM "v4l/vidioc-g-tuner.xml">
-<!ENTITY log-status SYSTEM "v4l/vidioc-log-status.xml">
-<!ENTITY overlay SYSTEM "v4l/vidioc-overlay.xml">
-<!ENTITY qbuf SYSTEM "v4l/vidioc-qbuf.xml">
-<!ENTITY querybuf SYSTEM "v4l/vidioc-querybuf.xml">
-<!ENTITY querycap SYSTEM "v4l/vidioc-querycap.xml">
-<!ENTITY queryctrl SYSTEM "v4l/vidioc-queryctrl.xml">
-<!ENTITY querystd SYSTEM "v4l/vidioc-querystd.xml">
-<!ENTITY reqbufs SYSTEM "v4l/vidioc-reqbufs.xml">
-<!ENTITY s-hw-freq-seek SYSTEM "v4l/vidioc-s-hw-freq-seek.xml">
-<!ENTITY streamon SYSTEM "v4l/vidioc-streamon.xml">
-<!ENTITY dqevent SYSTEM "v4l/vidioc-dqevent.xml">
-<!ENTITY subscribe_event SYSTEM "v4l/vidioc-subscribe-event.xml">
diff --git a/Documentation/DocBook/media-indices.tmpl b/Documentation/DocBook/media-indices.tmpl
deleted file mode 100644
index 78d6031..0000000
--- a/Documentation/DocBook/media-indices.tmpl
+++ /dev/null
@@ -1,89 +0,0 @@
-<!-- Generated file! Do not edit. -->
-
-<index><title>List of Types</title>
-<indexentry><primaryie><link linkend='v4l2-std-id'>v4l2_std_id</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-buf-type'>v4l2_buf_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-colorspace'>v4l2_colorspace</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-ctrl-type'>v4l2_ctrl_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-exposure-auto-type'>v4l2_exposure_auto_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-field'>v4l2_field</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-frmivaltypes'>v4l2_frmivaltypes</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-frmsizetypes'>v4l2_frmsizetypes</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-memory'>v4l2_memory</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-ac3-bitrate'>v4l2_mpeg_audio_ac3_bitrate</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-crc'>v4l2_mpeg_audio_crc</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-emphasis'>v4l2_mpeg_audio_emphasis</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-encoding'>v4l2_mpeg_audio_encoding</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-l1-bitrate'>v4l2_mpeg_audio_l1_bitrate</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-l2-bitrate'>v4l2_mpeg_audio_l2_bitrate</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-l3-bitrate'>v4l2_mpeg_audio_l3_bitrate</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-mode'>v4l2_mpeg_audio_mode</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-mode-extension'>v4l2_mpeg_audio_mode_extension</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-audio-sampling-freq'>v4l2_mpeg_audio_sampling_freq</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='chroma-spatial-filter-type'>v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='luma-spatial-filter-type'>v4l2_mpeg_cx2341x_video_luma_spatial_filter_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-median-filter-type'>v4l2_mpeg_cx2341x_video_median_filter_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-spatial-filter-mode'>v4l2_mpeg_cx2341x_video_spatial_filter_mode</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-cx2341x-video-temporal-filter-mode'>v4l2_mpeg_cx2341x_video_temporal_filter_mode</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-stream-type'>v4l2_mpeg_stream_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-stream-vbi-fmt'>v4l2_mpeg_stream_vbi_fmt</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-video-aspect'>v4l2_mpeg_video_aspect</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-video-bitrate-mode'>v4l2_mpeg_video_bitrate_mode</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-mpeg-video-encoding'>v4l2_mpeg_video_encoding</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-power-line-frequency'>v4l2_power_line_frequency</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-priority'>v4l2_priority</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-tuner-type'>v4l2_tuner_type</link></primaryie></indexentry>
-<indexentry><primaryie>enum&nbsp;<link linkend='v4l2-preemphasis'>v4l2_preemphasis</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-audio'>v4l2_audio</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-audioout'>v4l2_audioout</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-bt-timings'>v4l2_bt_timings</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-buffer'>v4l2_buffer</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-capability'>v4l2_capability</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-captureparm'>v4l2_captureparm</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-clip'>v4l2_clip</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-control'>v4l2_control</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-crop'>v4l2_crop</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-cropcap'>v4l2_cropcap</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dbg-chip-ident'>v4l2_dbg_chip_ident</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dbg-match'>v4l2_dbg_match</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dbg-register'>v4l2_dbg_register</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dv-enum-preset'>v4l2_dv_enum_preset</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dv-preset'>v4l2_dv_preset</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-dv-timings'>v4l2_dv_timings</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-enc-idx'>v4l2_enc_idx</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-enc-idx-entry'>v4l2_enc_idx_entry</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-encoder-cmd'>v4l2_encoder_cmd</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-ext-control'>v4l2_ext_control</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-ext-controls'>v4l2_ext_controls</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-fmtdesc'>v4l2_fmtdesc</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-format'>v4l2_format</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-fract'>v4l2_fract</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-framebuffer'>v4l2_framebuffer</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frequency'>v4l2_frequency</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frmival-stepwise'>v4l2_frmival_stepwise</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frmivalenum'>v4l2_frmivalenum</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frmsize-discrete'>v4l2_frmsize_discrete</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frmsize-stepwise'>v4l2_frmsize_stepwise</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-frmsizeenum'>v4l2_frmsizeenum</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-hw-freq-seek'>v4l2_hw_freq_seek</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-input'>v4l2_input</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-jpegcompression'>v4l2_jpegcompression</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-modulator'>v4l2_modulator</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-mpeg-vbi-fmt-ivtv'>v4l2_mpeg_vbi_fmt_ivtv</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-output'>v4l2_output</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-outputparm'>v4l2_outputparm</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-pix-format'>v4l2_pix_format</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-queryctrl'>v4l2_queryctrl</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-querymenu'>v4l2_querymenu</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-rect'>v4l2_rect</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-requestbuffers'>v4l2_requestbuffers</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-sliced-vbi-cap'>v4l2_sliced_vbi_cap</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-sliced-vbi-data'>v4l2_sliced_vbi_data</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-sliced-vbi-format'>v4l2_sliced_vbi_format</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-standard'>v4l2_standard</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-streamparm'>v4l2_streamparm</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-timecode'>v4l2_timecode</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-tuner'>v4l2_tuner</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-vbi-format'>v4l2_vbi_format</link></primaryie></indexentry>
-<indexentry><primaryie>struct&nbsp;<link linkend='v4l2-window'>v4l2_window</link></primaryie></indexentry>
-</index>
diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
new file mode 100644
index 0000000..6628b4b
--- /dev/null
+++ b/Documentation/DocBook/media/Makefile
@@ -0,0 +1,386 @@
+###
+# Media build rules - Auto-generates media contents/indexes and *.h xml's
+#
+
+SHELL=/bin/bash
+
+MEDIA_OBJ_DIR=$(objtree)/Documentation/DocBook/
+MEDIA_SRC_DIR=$(srctree)/Documentation/DocBook/media
+
+MEDIA_TEMP =  media-entities.tmpl \
+	      media-indices.tmpl \
+	      videodev2.h.xml \
+	      v4l2.xml \
+	      audio.h.xml \
+	      ca.h.xml \
+	      dmx.h.xml \
+	      frontend.h.xml \
+	      net.h.xml \
+	      video.h.xml \
+
+IMGFILES := $(patsubst %.b64,%, $(notdir $(shell ls $(MEDIA_SRC_DIR)/*.b64)))
+OBJIMGFILES := $(addprefix $(MEDIA_OBJ_DIR)/, $(IMGFILES))
+GENFILES := $(addprefix $(MEDIA_OBJ_DIR)/, $(MEDIA_TEMP))
+
+PHONY += cleanmediadocs
+
+cleanmediadocs:
+	-@rm `find $(MEDIA_OBJ_DIR) -type l` $(GENFILES) $(OBJIMGFILES) 2>/dev/null
+
+$(obj)/media_api.xml: $(GENFILES) FORCE
+
+#$(MEDIA_OBJ_DIR)/media_api.html: $(MEDIA_OBJ_DIR)/media_api.xml
+#$(MEDIA_OBJ_DIR)/media_api.pdf: $(MEDIA_OBJ_DIR)/media_api.xml
+#$(MEDIA_OBJ_DIR)/media_api.ps: $(MEDIA_OBJ_DIR)/media_api.xml
+
+V4L_SGMLS = \
+	$(shell ls $(MEDIA_SRC_DIR)/v4l/*.xml|perl -ne 'print "$$1 " if (m,.*/(.*)\n,)') \
+	capture.c.xml \
+	keytable.c.xml \
+	v4l2grab.c.xml
+
+DVB_SGMLS = \
+	$(shell ls $(MEDIA_SRC_DIR)/dvb/*.xml|perl -ne 'print "$$1 " if (m,.*/(.*)\n,)')
+
+MEDIA_SGMLS =  $(addprefix ./,$(V4L_SGMLS)) $(addprefix ./,$(DVB_SGMLS)) $(addprefix ./,$(MEDIA_TEMP))
+
+FUNCS = \
+	close \
+	ioctl \
+	mmap \
+	munmap \
+	open \
+	poll \
+	read \
+	select \
+	write \
+
+IOCTLS = \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/videodev2.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/audio.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/ca.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/dmx.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/frontend.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/net.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/dvb/video.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/media.h) \
+	$(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/v4l2-subdev.h) \
+	VIDIOC_SUBDEV_G_FRAME_INTERVAL \
+	VIDIOC_SUBDEV_S_FRAME_INTERVAL \
+	VIDIOC_SUBDEV_ENUM_MBUS_CODE \
+	VIDIOC_SUBDEV_ENUM_FRAME_SIZE \
+	VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \
+
+TYPES = \
+	$(shell perl -ne 'print "$$1 " if /^typedef\s+[^\s]+\s+([^\s]+)\;/' $(srctree)/include/linux/videodev2.h) \
+	$(shell perl -ne 'print "$$1 " if /^}\s+([a-z0-9_]+_t)/' $(srctree)/include/linux/dvb/frontend.h)
+
+ENUMS = \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/videodev2.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/audio.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/ca.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/dmx.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/frontend.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/net.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/dvb/video.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/media.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-mediabus.h) \
+	$(shell perl -ne 'print "$$1 " if /^enum\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-subdev.h)
+
+STRUCTS = \
+	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/videodev2.h) \
+	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s\{]+)\s*/)' $(srctree)/include/linux/dvb/audio.h) \
+	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/ca.h) \
+	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/dmx.h) \
+	$(shell perl -ne 'print "$$1 " if (!/dtv\_cmds\_h/ && /^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/frontend.h) \
+	$(shell perl -ne 'print "$$1 " if (/^struct\s+([A-Z][^\s]+)\s+/)' $(srctree)/include/linux/dvb/net.h) \
+	$(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/linux/dvb/video.h) \
+	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/media.h) \
+	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-subdev.h) \
+	$(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/v4l2-mediabus.h)
+
+ERRORS = \
+	E2BIG \
+	EACCES \
+	EAGAIN \
+	EBADF \
+	EBADFD \
+	EBADR \
+	EBADRQC \
+	EBUSY \
+	ECHILD \
+	ECONNRESET \
+	EDEADLK \
+	EDOM \
+	EEXIST \
+	EFAULT \
+	EFBIG \
+	EILSEQ \
+	EINIT \
+	EINPROGRESS \
+	EINTR \
+	EINVAL \
+	EIO \
+	EMFILE \
+	ENFILE \
+	ENOBUFS \
+	ENODATA \
+	ENODEV \
+	ENOENT \
+	ENOIOCTLCMD \
+	ENOMEM \
+	ENOSPC \
+	ENOSR \
+	ENOSYS \
+	ENOTSUP \
+	ENOTSUPP \
+	ENOTTY \
+	ENXIO \
+	EOPNOTSUPP \
+	EOVERFLOW \
+	EPERM \
+	EPIPE \
+	EPROTO \
+	ERANGE \
+	EREMOTE \
+	EREMOTEIO \
+	ERESTART \
+	ERESTARTSYS \
+	ESHUTDOWN \
+	ESPIPE \
+	ETIME \
+	ETIMEDOUT \
+	EUSERS \
+	EWOULDBLOCK \
+	EXDEV \
+
+ESCAPE = \
+	-e "s/&/\\&amp;/g" \
+	-e "s/</\\&lt;/g" \
+	-e "s/>/\\&gt;/g"
+
+FILENAME = \
+	-e s,"^[^\/]*/",, \
+	-e s/"\\.xml"// \
+	-e s/"\\.tmpl"// \
+	-e s/\\\./-/g \
+	-e s/"^func-"// \
+	-e s/"^pixfmt-"// \
+	-e s/"^vidioc-"//
+
+# Generate references to these structs in videodev2.h.xml.
+DOCUMENTED = \
+	-e "s/\(enum *\)v4l2_mpeg_cx2341x_video_\([a-z]*_spatial_filter_type\)/\1<link linkend=\"\2\">v4l2_mpeg_cx2341x_video_\2<\/link>/g" \
+	-e "s/\(\(enum\|struct\) *\)\(v4l2_[a-zA-Z0-9_]*\)/\1<link linkend=\"\3\">\3<\/link>/g" \
+	-e "s/\(V4L2_PIX_FMT_[A-Z0-9_]\+\) /<link linkend=\"\1\">\1<\/link> /g" \
+	-e ":a;s/\(linkend=\".*\)_\(.*\">\)/\1-\2/;ta" \
+	-e "s/v4l2\-mpeg\-vbi\-ITV0/v4l2-mpeg-vbi-itv0-1/g"
+
+DVB_DOCUMENTED = \
+	-e "s/\(linkend\=\"\)FE_SET_PROPERTY/\1FE_GET_PROPERTY/g" \
+	-e "s,\(struct\s\+\)\([a-z0-9_]\+\)\(\s\+{\),\1\<link linkend=\"\2\">\2\<\/link\>\3,g" \
+	-e "s,\(}\s\+\)\([a-z0-9_]\+_t\+\),\1\<link linkend=\"\2\">\2\<\/link\>,g" \
+	-e "s,\(define\s\+\)\(DTV_[A-Z0-9_]\+\)\(\s\+[0-9]\+\),\1\<link linkend=\"\2\">\2\<\/link\>\3,g" \
+	-e "s,<link\s\+linkend=\".*\">\(DTV_IOCTL_MAX_MSGS\|dtv_cmds_h\|__.*_old\)<\/link>,\1,g" \
+	-e ":a;s/\(linkend=\".*\)_\(.*\">\)/\1-\2/;ta" \
+	-e "s,\(audio-mixer\|audio-karaoke\|audio-status\|ca-slot-info\|ca-descr-info\|ca-caps\|ca-msg\|ca-descr\|ca-pid\|dmx-filter\|dmx-caps\|video-system\|video-highlight\|video-spu\|video-spu-palette\|video-navi-pack\)-t,\1,g" \
+	-e "s,DTV-ISDBT-LAYER[A-C],DTV-ISDBT-LAYER,g" \
+	-e "s,\(define\s\+\)\([A-Z0-9_]\+\)\(\s\+_IO\),\1\<link linkend=\"\2\">\2\<\/link\>\3,g" \
+	-e "s,<link\s\+linkend=\".*\">\(__.*_OLD\)<\/link>,\1,g" \
+
+#
+# Media targets and dependencies
+#
+
+install_media_images = \
+	$(Q)cp $(OBJIMGFILES) $(MEDIA_OBJ_DIR)/media_api
+
+$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64
+	$(Q)base64 -d $< >$@
+
+$(MEDIA_OBJ_DIR)/v4l2.xml: $(OBJIMGFILES)
+	@$($(quiet)gen_xml)
+	@(ln -sf $(MEDIA_SRC_DIR)/v4l/*xml $(MEDIA_OBJ_DIR)/)
+	@(ln -sf $(MEDIA_SRC_DIR)/dvb/*xml $(MEDIA_OBJ_DIR)/)
+
+$(MEDIA_OBJ_DIR)/videodev2.h.xml: $(srctree)/include/linux/videodev2.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DOCUMENTED) |		\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/audio.h.xml: $(srctree)/include/linux/dvb/audio.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/ca.h.xml: $(srctree)/include/linux/dvb/ca.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/dmx.h.xml: $(srctree)/include/linux/dvb/dmx.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/frontend.h.xml: $(srctree)/include/linux/dvb/frontend.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/net.h.xml: $(srctree)/include/linux/dvb/net.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/video.h.xml: $(srctree)/include/linux/dvb/video.h $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(					\
+	echo "<programlisting>") > $@
+	@(					\
+	expand --tabs=8 < $< |			\
+	  sed $(ESCAPE) $(DVB_DOCUMENTED) |	\
+	  sed 's/i\.e\./&ie;/') >> $@
+	@(					\
+	echo "</programlisting>") >> $@
+
+$(MEDIA_OBJ_DIR)/media-entities.tmpl: $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(								\
+	echo "<!-- Generated file! Do not edit. -->") >$@
+	@(								\
+	echo -e "\n<!-- Functions -->") >>$@
+	@(								\
+	for ident in $(FUNCS) ; do					\
+	  entity=`echo $$ident | tr _ -` ;				\
+	  echo "<!ENTITY func-$$entity \"<link"				\
+	    "linkend='func-$$entity'><function>$$ident()</function></link>\">" \
+	  >>$@ ;							\
+	done)
+	@(								\
+	echo -e "\n<!-- Ioctls -->") >>$@
+	@(								\
+	for ident in $(IOCTLS) ; do					\
+	  entity=`echo $$ident | tr _ -` ;				\
+	  id=`grep "<refname>$$ident" $(MEDIA_OBJ_DIR)/vidioc-*.xml | sed -r s,"^.*/(.*).xml.*","\1",` ; \
+	  echo "<!ENTITY $$entity \"<link"				\
+	    "linkend='$$id'><constant>$$ident</constant></link>\">"	\
+	  >>$@ ;							\
+	done)
+	@(								\
+	echo -e "\n<!-- Types -->") >>$@
+	@(								\
+	for ident in $(TYPES) ; do					\
+	  entity=`echo $$ident | tr _ -` ;				\
+	  echo "<!ENTITY $$entity \"<link"				\
+	    "linkend='$$entity'>$$ident</link>\">" >>$@ ;		\
+	done)
+	@(								\
+	echo -e "\n<!-- Enums -->") >>$@
+	@(								\
+	for ident in $(ENUMS) ; do					\
+	  entity=`echo $$ident | sed -e "s/v4l2_mpeg_cx2341x_video_\([a-z]*_spatial_filter_type\)/\1/" | tr _ -` ; \
+	  echo "<!ENTITY $$entity \"enum&nbsp;<link"			\
+	    "linkend='$$entity'>$$ident</link>\">" >>$@ ;		\
+	done)
+	@(								\
+	echo -e "\n<!-- Structures -->") >>$@
+	@(								\
+	for ident in $(STRUCTS) ; do					\
+	  entity=`echo $$ident | tr _ - | sed s/v4l2-mpeg-vbi-ITV0/v4l2-mpeg-vbi-itv0-1/g` ; \
+	  echo "<!ENTITY $$entity \"struct&nbsp;<link"			\
+	    "linkend='$$entity'>$$ident</link>\">" >>$@ ;		\
+	done)
+	@(								\
+	echo -e "\n<!-- Error Codes -->") >>$@
+	@(								\
+	for ident in $(ERRORS) ; do					\
+	  echo "<!ENTITY $$ident \"<errorcode>$$ident</errorcode>"	\
+	    "error code\">" >>$@ ;					\
+	done)
+	@(								\
+	echo -e "\n<!-- Subsections -->") >>$@
+	@(								\
+	for file in $(MEDIA_SGMLS) ; do					\
+	  entity=`echo "$$file" | sed $(FILENAME) -e s/"^([^-]*)"/sub\1/` ; \
+	  if ! echo "$$file" |						\
+	    grep -q -E -e '^(func|vidioc|pixfmt)-' ; then		\
+	    echo "<!ENTITY sub-$$entity SYSTEM \"$$file\">" >>$@ ;	\
+	  fi ;								\
+	done)
+	@(								\
+	echo -e "\n<!-- Function Reference -->") >>$@
+	@(								\
+	for file in $(MEDIA_SGMLS) ; do					\
+	  if echo "$$file" |						\
+	    grep -q -E -e '(func|vidioc|pixfmt)-' ; then		\
+	    entity=`echo "$$file" |sed $(FILENAME)` ;			\
+	    echo "<!ENTITY $$entity SYSTEM \"$$file\">" >>$@ ;	\
+	  fi ;								\
+	done)
+
+# Jade can auto-generate a list-of-tables, which includes all structs,
+# but we only want data types, all types, and sorted please.
+$(MEDIA_OBJ_DIR)/media-indices.tmpl: $(MEDIA_OBJ_DIR)/v4l2.xml
+	@$($(quiet)gen_xml)
+	@(								\
+	echo "<!-- Generated file! Do not edit. -->") >$@
+	@(								\
+	echo -e "\n<index><title>List of Types</title>") >>$@
+	@(								\
+	for ident in $(TYPES) ; do					\
+	  id=`echo $$ident | tr _ -` ;					\
+	  echo "<indexentry><primaryie><link"				\
+	    "linkend='$$id'>$$ident</link></primaryie></indexentry>" >>$@ ; \
+	done)
+	@(								\
+	for ident in $(ENUMS) ; do					\
+	  id=`echo $$ident | sed -e "s/v4l2_mpeg_cx2341x_video_\([a-z]*_spatial_filter_type\)/\1/" | tr _ -`; \
+	  echo "<indexentry><primaryie>enum&nbsp;<link"			\
+	    "linkend='$$id'>$$ident</link></primaryie></indexentry>" >>$@ ; \
+	done)
+	@(								\
+	for ident in $(STRUCTS) ; do					\
+	  id=`echo $$ident | tr _ - | sed s/v4l2-mpeg-vbi-ITV0/v4l2-mpeg-vbi-itv0-1/g` ; \
+	  echo "<indexentry><primaryie>struct&nbsp;<link"		\
+	    "linkend='$$id'>$$ident</link></primaryie></indexentry>" >>$@ ; \
+	done)
+	@(								\
+	echo "</index>") >>$@
+
diff --git a/Documentation/DocBook/media/bayer.png.b64 b/Documentation/DocBook/media/bayer.png.b64
new file mode 100644
index 0000000..ccdf2bc
--- /dev/null
+++ b/Documentation/DocBook/media/bayer.png.b64
@@ -0,0 +1,171 @@
+iVBORw0KGgoAAAANSUhEUgAAAlgAAACqCAMAAABGfcHVAAAAAXNSR0IArs4c6QAAAwBQTFRFAAIA
+CAICAAQVEQEBAgsAJgECAAogAwsTAQopHQYBNAEAAAxNARQAERIQAhoDABwAABZEHRQKGRYKQw0F
+ACMBACUAERwpHR4cVRAFBR5rZhADACR2JiIhBDAGAiWGgQ4AcxQABDYACSeQMSYlJykmESxYlQ4A
+PSYZIS05OSsJHS5JOC8kAEMDUC8SADXLNDUzADbEAEsAADX/2RABCFIAAD/qxB0AAD//BFgAK0Vp
+WT4r3hwA3RsTRERAAEf/5CIA2iYCCUv+WUgz7iIAOk5g3CgVSU5SiD8uB2sABm8AE1X/U1RQOFyL
+4jkfIlz/RV98M1j+G2H/fVk23jtD4T0pXl9ieFtGcV894UIiYWJfAIwA50gOV2p+4kssO2j+dGZx
+bG1qVmj/OHH/aHJzfnBX5lQ7B50AZnahdXd0AKUG5V1ARnz/6mErCqgAAKsAent46GBIW4GhAK0A
+AK8B42FtALIOin9/ALUAiIOBALkAVIf/6WxWg4eBi4SKJrEAmoVtdY2geoP/rYVXhoyOqYVuJbUh
+IrgWX5D/jo6J7nszP7gAsI9S63xnN70zZqO/fZzCOb4+cZr+64dy8otYnJ6b7ImDRcM56IqcWMEo
+oJb/N8ZoTMRL7Y9/QchcsaOTo6eohaj/7ZqKXspXj6v9xal+oK+7d7vTUM+Afco5r7CumLTVStKV
+bs9ukbb/9qx/9q9l8queoLv/e9R66beG7rDImNRhi9aDwsPAs8bWzcK2cd67jtqP5MWUodyB8b+1
+tMr/z8L/j9+kbOXWnN2ZstD7yc7Rzs7Ly9xb183UwdD/+si/qeOmvuKIx9fj4tPCtuWiqOrL+tS2
+y9v++NPK2dvZt+m0ueq80+Wo3OeSwuy/yezG+d7f/eS/z/DS3uf/6Ono4PC71O39xPb02vPZ/+nR
++Ori6e399+vt+PGz+ur65fL55/Xb4vbh7ffX/PPY8vP9+vLy6Pf36fjr/PfM8vjr//f+/vn48P36
+9vv+/vzf+fv4/fvu//z7+v7//P/7/v/8//QpxAAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAFY8AABWW
+AQ2TT8cAAAAHdElNRQfaCRQXGSltwbPRAAAgAElEQVR42u2dDXwU1bXAZwEJtEaNH1nbh68fpoWK
+iE1ao2Bgo9RqIrEg+BIFmqLYLOlMcHHlU6DiQmrJM2jKo0QIBHgUjD5ETcQIlKq0gKDmA+UjiRAT
+BCOBkGzC5re/++6987Ezszszdzc7s9jfPa2wO+zMPefc/5575t67Z5hB/0Ek/W668xckcmVmQZ5S
+CvLmgshl4QCiZu+8ntCOgWlzVfrl5ZZFrl6T/VYSv9x5K3Pj9wnkh9fFFxQE6VcVqXY+8PjgH5K0
++/0bBxDaYcsN0i+vLlTbzH9kjEknkEF3zptjLPPmXL2VwGC/nxysm+YRyc+/S2bHNYUgmtJkf5RI
+vScH3HEvifz05mhqB8G68d6xJO3ecSWhHXYfYdvM99LHGEv6mEF3zmFJ5Gr49e9qVUh7O/wP/w/9
+gf4EXnKwbpjNGQs779bvktlxzULg7TCQzvDAItBvzqMD7hjrMJaxPx0Cv3OdBvqFBRZJs46xCCwi
+O+xNwNfSclom6F2L4j1A/UsG1hgI1jyWUzLEKf/gX0CwevIzsvSlJoyh8IY5LmPhEFhEhsCI9b7L
+oy/uI2GBRaDfPATWaGO596dDADhioJ+7PKyI5SBoF4NFZAcEa6ZjvL7MOg9MAWtPxv4aHdlfM315
+TMHy7Gg4pifN5cUxBMsPisub9dRrqHc1xBCsC7vHH6jVlQOO3eGBhccc9B+rGIWkP/ALBNYEA3uX
+xxasooMGbVaWxhSs0kr9Njs8zbEE60C2UbOTTAOrR6/ZHjB/ZWzBet+gzR0xBmuHfpttsQbLIEP2
+ZpsGVrsBWMspWBQsGrEoWFEAK1UUDbBkQEkJu+Ko+WDxDRmApWmH+WCF0u/bCFYIMyIHK30CL1kZ
+Y1J17wo51snhW1/4d9BdoZlgcZx7mcezzM1yemBp22E2WBzL66fsExVYjmxBxsNed1gHVra8XX2w
+WBc2A/4dDbCSp4v/2PrGb1L1hkKnZ8sRNFH39cel6K1lQyFbvLcZXf3YrmWsNlg6dpgMFltc3dAN
+j3+zazWrCVbKBun8ltcfS3FYBpb0D721L+uCxXoqxO5VfEMiBmsa6BL/+UxWqhZYMFytPSVd5yMU
+qKxJ3jlub7f4D5f+xmqDpW2HuWCxr0r69b7N6oAV6JsTj6VYBpaciP9L0QaLVXQv13ewUqeBdjyS
+ZM0/Cf6uBRbkak03uLSraBHnWfsJAJ/LEi2TIxZs7bPyZS6XZwu0XEaWCiwdO0wFi3sXgC/K4QDi
+qfhEoV8QWNtT8FLK+L90gddHWwjWjNGw1dG/mgW7/jFNsFjYvd/sKnK73Kh7P4oSWHw3JOcDkJGq
+BVbxBfD5IidKqpzOV/3gb05rwGJfRXEAfYM41nMKfMXpgaVhh5lgsVsAeJvj9YOMXVrE6YAlvHwa
+XJSFLJPBOg8m8W2lpLwFQ5YjNFgc6t45OFCx0OVgNRu1iIVEByznu+ArIUixnPMfKGRZARaCSRpf
+ENx/4wwiVgg7TASLc52CA4f4BiobCFmaYDlSusBUC8GaGgC6VgssFnavS3QtC7uXiyJYMP09o5m8
+O2GfOsW8il1TudoisF4FX8hGvy3lc1yGYAXZYSZYa+RBitvy9hyXIVij744RWP+jDRb8ygaCFLdm
+x7KoJO/tyWj2Jz3/JPhjssY8lnNL91cvsNL8KOtk1fNY5iTv3D/AP2UJMaubvGvZYSJY8Jv+T04+
+8eAyBCsFdvBXVg6F2UK7k85oDoUs7N5FsiwjSsk7v5cKkqHsD3nEcm4BnznxHINTENaaCVJpcBGn
+zXQilpYdZoL1iThSB+kXBNbu8VOhzFhwAICXrUzeF2RPnTpp6qy/nAG9YzWSd5gpfqZhRl/AkpjY
+P0HrrtBZDQ468ZuKHVgqXdYk793Ag4zkllXyDZfq5FhadpgJVjMoxZ3g3sHrV84ZzmMB8LpjdCym
+G3r/oDXdwFaD97EZHG9FxQ53VHKsadOh5K8/q51jYbDwC/FSiywFixX7/Sirk2Np2GEmWA2gHOvn
+Efe3aCfvXiTA27J9lpVLOl7cLvyH2g2PaU6QSmCx4mXcXDTASkaSmpxxEvw1VXsofBLPt79/9AgU
+2DJr5VDIFh2rh9IM6vXA0rDDgqGQW4b1awAN+neFvzoDvpTPjlqVvD8Nw+ToFG2wxKGQO3gUmnEk
+GmAlS/M/Y5KXg5pkLbD45F3IsdhgsExO3vHS5JMV2mDp2GFJ8o71KzYCK+VhSJYjxXKw4A0DeF0P
+LDF5xxOVXLQiltg384PAktaanxSmG+D9AkrtEFhWLEKzr4Jv+FsUNOizO/QjloYd5k439C6SVIID
+doPRPNbTXeA96yPW6JS3AFCkWMrpBg/qXmmYcEcbLO2IxTrfBRdfcAqYOZ1WDYVozvGf0s2vkxAs
+6yIWGqs/l9ZsnWtBsxFYKHa8bOEitDiPBQfhc49prhWyqHuliWhX1HIsvI1JL8eCMJ0CF1ezeBxk
+iz+xLMdCSyYfzRZugbd0gCO6OVZoO0xd0lnTDT57QdiktqYDtBnOvMPYcc7CRWhpghSmWW9qgoVW
+EC6u5uMGh7s3KmBNQzJ9+UnQpTnzzjmLTwHwRUVxcemWBnjnusuqRWi0ctX5cXlR8dq9HQB8s1pv
+SUfDDlMjFkxPQO/H5auKy/e2of0XhmuFKQ93gTctHwpHO1ColA+GqkVovntXFQndG5WZd0m6fqe9
+bYZzej6RPvjZ6qAJUtP2vLNrpP0c53bNYXVm3rXsMHnbzFrJL727XtCbIA0srYA/pVg33SAu6dx9
+BpyQ3Teot80oujc6E6TtWBpr1mfobPRDUrzlSEfH10d3FcEbBws3+rnX7m3o6Pjm43K9jX46dpi8
+0Y9zFe891tHZ/HHFMo5zEawV/uo4+HKsVWCdli1C+2F2p7nRj+OK+O7dUeRio7vnPdVoazIr3/Ru
+4dZkce2bI9vznmr51mRh2wd72e95T9HdmhzKDEt+paP4MQX9+Rf9lU60wKI//6JgUbAoWJczWMRF
+QehQSMEyJWJNM7B3eYwj1re8KEhnjMGaZNSsaUVB0tcrZaPqbVaMyxiVVlcqRP22KLZljEqLlApV
+q97uiG0ZowOOVzboyitmlTECK6fly2V6fr7qfXtMwTpaVKyUUtX74uYYggVAs1o9lX5F1SCGYDWB
+l2bMVMos5dsZL4HwwTIQFwYrmmICWNEUM8CKnpgDFpmEAVZQM263+shsl1ZxWz/6H/oD/ukPC6x5
+s42L6s4mrEFqClgkRX8hWPeONRYzwBpN0i4Ci8iOkGB5Q7xjbP2CZGDwoX62K29Qy/U33RB8bEDS
+SLUkpfUlYjE3EMmVIewIJTZ7sH4FfQHrqhuuV8tNQUduuJrpTyQ228hg/UoiByuXsN3+A64OtiPE
+kauYEP0bslw4c9MD9xPIA9d/5wc/JJH+uWUlaunL6Di3P1GzPxhMaMfV920N0q8qcvVO27/34/80
+lh9/b8D9D5DIz+3B7ivZFzlYv73+AaKG7x9AaEd8YbB+IUdH5hdkddR/9H2iOuX3XrE1ujnW3O+Q
+tXsdqR3PRnko/GUGQXX5jNsYjki9B5JIWvWSg3UrmVtY5jYSO9J/SV7n/efzOJKsDYI1mkSugOGp
+7ai+HAsLrLEE2afj3uvI7JhzEwTrgJGEA9ZtRPXlbx/wJMlNCA/WfgNpB/4wwCJyy5PM7UQ56u0w
+x2o7YtC/bSaB1eZx6xcqd9XHFKyXpLpnGuLYQBwTog+WF7wmlo3TkIzp7SB2YJ027F63p80csOoX
+dXR3aksHKC2PKVjZG8BpPQEvzYgpWPkrhd1koaWnJqMmhmCdqXd3dOpJd4e73hywjngM7C2viClY
+M7YbtPnKrFiDpSutWY0xBcuoe4HHNLC6KVgmgtUYa7AM8ncfBYuCRcGiYH3rwRJ+UKYLVookVoLl
+0Gw3FFgh7TAZrNRkQVKNwVKXCLIIrNBuUYKlZUZfwOJYd3FpeemqZawOWI4VCwSZ6bAyYk0V2501
+VVnzIBgsDTvMBSt1+vL5WPKVtZNCgMW6iqB6pcs41lKwtNyiACt1gmjGNHWZ/IjBYj17+T0jX+9a
+xGqCNT5wlZbXrQMrJUBEb+0f5D9NDwJLyw5zwUreLx4/80Z6qg5YrGvLMeykznplPXiTwZLc8o3K
+LQqwkqX9cl5VdbGIwWLXXIDGNjc0dwBwSfFLRWXEAoB/NN3xLgBetw6sDeC00C5UT/5LXjVYmnaY
+DNYe0IoeydgIe75GBywO/SC0t62hARXpV5S7NhcsdouWW9RgdfFm+EGXskx+hGCxW/yoTjnHch6o
+wsUXdMDi053Rk94CQFFewmSwtgsp1oIz4M2xmmBp22E6WCtxapK+shv8MVUTLM8p0LurCFVRXauq
+B28qWKj2hcwti3TAqklORRlWvrpMfoRgeU6Cz4VfvqLyDB+x2mA5UCV62OV3v6V8xoHpYOHC+6ic
+9CUZ0CqwtO0wHaz1yWPSUfb7GngjWQss9l0UL4QSVKgevEVgofrtvFtw9Y1drA5YqenIjuT5UqGx
+voCFCnzPFltzvgo+l1XADwZL6Oy/SHUIrAFLfH0azNACS8cOK8DCr1aCPVpgscXdgSjFek71yoqH
+mAkWrt+u4ZbgiKWuYNcXsIrlNe9dntJlLpcxWG8pC+JYBdbDivroSrB07LAALFw9acwH2kMh7ODP
+ZflN6arZ1kQsPbeEAAvbsTIaQyG79pQ8HXEpCnyHzrFSUHGJP8Ugx4Ij8InHNHIsPTtMB2vjmIyM
+jKzpe5QdohwK/6GsB29R8q7rliCwxmRBM6at7z7zm2iABb7RLPCtBmsFlld2A/CplXeFtQtwuxvO
+AHmxFDVY2naYDpbU+2O0wTolPPmBcwbVgzcVLB23aEw3gK7fJfd9uoEtB8f4Osw7ULnc+vpjHlYL
+rIDjP1UW/jUZrIC8PFoTLB07LAML7E/XBMsnlBUv4tU7uoO1BKwK0S2VQrsezhAs0Pi71KiB5XaK
+v6srZnXnsbygd/tMVWFnk8FqOYAnsb58KVt75l3PDvNzrFS0E3nCym7FWKgEqxsUadSrNxUsyS1t
+wW4JcVcIBT2VrysKEWut/yIfossr0SMJOsEqVjfHelo9O2pRjvUW+FJZ9Fc9FGrbYdFdYWry/G4g
+G0XUQyFOojkPUq/iiKxIr7lDodotRazBXWFqctZJ8NfkKCTvwnQsXw65Qw8sNI/FFwxPsRYs9BzH
+46D3MZ2IpWOHVdMNY1JrwHwNsHTq1ZsJFgfd8oLCLYZgwZfrFfNxkc5jfSKfS2QNwBIKhv/J4oiF
+XkxCFTS1F6F17LAMrGRtsFhUDz6g7A6LwFK5hbMQLG4NWl/gxJKMHXo5Ft+vdx9XFQy3BCx+ENZe
+hNaxwyqwUtNPakcszyk0A87x6jmrZWXFzQULAh1wC8z0VhmClZr6RjTAQlN34O1l+HET7jUNQIa0
+BlgpDwNFOWmrJkhhqOzVWYTWtsOatcLk5DGvgTOy/Q2qJZ21F8AXq92ouoq7aK8ffMxatFao7ZZg
+sPj9WMv9QHFbGCFYnAs23ftxZcWOgx3oOezGM+9BT8+waOYdDoafai9Ca9thOlh7lq+Esr4GKJJe
+1SI03nzxBVSvGpW9/uwFa5Z0VG659LbOPFYjNmPlHgD+nhyV/VicVEi996NlrM5+LLG3YQ9flG+6
+Mxms3YFnGsufIBm0H0vLDqv2YwGwUXc/VvFe8XNflLo4y/ZjabpFcx5rf3qUdpCyruLqg0cOVpe7
+We2Nfo7aA9Ja4YLa2plWgbXi+EvSIvT22t1jdXaQathhMljra/BPlfe8sVK5jSloBynLeir2HqlH
+5eBZ6/ZjSW6pVLtFCVa+YMaejdNTo73nnTXY8x76tfl73h2ybfcke97Z2Ox5Tybd887FZs87S7bn
+PWjTu9m/0nE4ZC8dlu15d2i1e9n8SkeonfFt/5VOuoYd9Odf9OdffQKL/q6QgkXBomBRsPoKlo+C
+9e8MllGzZoFV7+4EPm3pBqWxBSt7A/DqyWVQxqirpwv+H/6BRfybF9AY4zJGHt3u9YFOs8BqVlfi
+KlIXXjsYU7BWOCYpC61NUr6f5NhArJ4ZYK1Pn6astKZ6mzWtNYZgnf7aYyjNPFizSeowQ7DGkgiq
+QdpWf0QhR5Vv64+CcMAiaheBRWZHqFKRu1UCog7WbQOcROWucanID5RSs3+PUlrDKhVJ5BYnQ2iH
+vQl8repetdTj/ZXMreyTBML+6EbHHSRyRYmv6fQZlYDI5ZnvELU7+joyO5w3PXO+6YJKuiNXr8l+
++5hfGkv67cyjThI3329vamrqVYu61TCK2/6IzC2PwohFYAeMWB8Gd29IdZgBVwbJVVcFHxtgG0wk
+tiH2IBnZB7BKCNu9NpQdwYeuZOKD1IvP7QNYSf0GBsmg4EP9mBC6XB3iWLB69viIn3ngA8+GajeU
+MKR2BOtnD13nPbNuH4HUjcwl+ty+pMLgz9X1BayRZPpl9sGOPujXNKSs7kNjqSuzV5HoV1eYFOJo
+U+Rg5RK6pcreBztCTgIwhF/XtKVkn0siqfPeRe6bQsLHWuROJrRjIYimNNnJqKyznyf63NakaGrn
+Azk5ZJ/sIraDpM67VwCrcf1GXVnfDjtkLgANldX6gsAqA2C//vXWv0acJPBgvW/QbmUbADkQrI0b
+CewoAJ1GZlSHAxYcB+r1L1gJ7773oWfQbDd4HNsBASwD7SobwgLLf3yDgWzn7TDqXtGOAxsM7fBi
+sHqmTcifriP5WfkYrA6P+nlsKnFVYLBqMqZN15X0jWFFrGqXfrPFniIfAmtlhq4Zgh3PglJ3qbEd
+YYBVb6Sfqx53yAbHzBm64qiFYPlAhUtfvyJPWzhgeWdkz9JtdqZjA7TjX4bdy9txoXb8jBmGdmCw
+2rMMJtzemIDBanYbGFRZjMHak2VgbziP7oVgVRg98PSYuwOBZTRjzdvxLPAYPmG1OCywqosMPlRU
+jcFascDgc9m7MVhGj+7tcDeEA9bp8bUGH1uwAoH1tbuDxI4LB7KBsR08WBP2AP6Rb/5QAjtkGg+W
+0SNj0bOUk/hnQoe8EN9GTwRg6Q/sxzydAlh+YzuejfIzoavA+0ZgreLBemmBfgrgJQQrnGdCQ7DO
+Zx8wSIh4sNoMA+EqASyyZ0IjsPYbfNPDBSuKT7EnilgSWAR2ULAoWBQsChYFi4JFwaJgfVvBajcA
+azkFi4JFIxYFi4JFwaJgUbAoWBQsChZN3ilYNGJRsChYFCwKVphg5RCCVUAG1pCS6A6Fc0eSgNUB
+cu4jBKsgumDFE4IVTwZWmT3KYGWC00RgxROCFU8MViEZWLklZGDlVEU3YpXlkkWswgIysKAdUQUr
+s44IrLpMMrCqMkFUwVo4lzBiZf7raxKw6jK7ScECRGChaxGBBSWqYEEhAgsKCVjQDm80wUJCAhYS
+ErCQRA8sLERg4e5tI7Jjd1TBQjuiiMDygegOhfCCZGD1kEUsnzeqEQvZSwSWjxAsX5TB8hGC5SME
+yxdtsC77iOUnAwsKjViXU8QKC6xoRiwKFgWLRiwKFgWLgkXBomBRsChYFCwKFgWL3hVSsChYNGJR
+sChYFCwKFgWLgkXBomBhsGoIwTIoR1IpgmWg4PIwk/dygw80IMUgWOsJwTKsNhNlsIolsPRlkgBW
+pf7HOsIFy6jazIoVRN0r2LHbsNrMJBGsjNcaa3SkcT1fl6jBVd/coCNtFXz5nz0ZNcrrqa7emB8m
+WMVtDbrtHnR1oC9e/nxdMyQ7PJUG16soDku/ao+uWxqaPTwpK2Ycr9WV8TxYxeW6+jUfcTWEo97p
+8dv12z0+cwXfvUeI7Ng9vraWwA4IFliZkaWQaRMUbydk8KHAV+7WL+8t1G9vn66+nvJtFnEBfGGk
+W2RQVhwXSvNDoLN0RbSj0uUhsoNUDOvaCxGmdrxKpirfOma04M/VG+jnLveFpd8Kh7Kd7Gy1IgeI
+ulewo2WG6nrjs0PZwaCa4Y2tja2tjY3wL/g3fo3+j9/gF9LorpQO1Xt+jPaDdnxuo3AJ8bKyNkjr
+lIuxv81AhM81tirsaNWyo43wepHqp37fKeQyLcdb9OT4eSEHazO4XpjqAf1moVZ8uz4jt3TyZpw3
+uh62gyFSzQ8uf/H/m9jxbyIMdQEVChYVChYVChYVKhQsKhQsKhQsKlQoWFQoWFQoWCD0g0V8fvUL
+2SdDbKDwmqqu1xtQQd1SqCNBp/WYrKDkpR5/kEt9BKf5zFscUDTfE/zSq+llXwTdq4hYWwvmIlla
+8o786M6SwmeXbj6ruOjhrYVzl5YdEo41FSycK5z5odnfhJadJagZ6XG7hULLSBm0ZFNXoDgiSi86
+benmdtO/qYGGsGuqeJfOXbi0rJVfUtonOHnp5h6VlxeqvRxlrsokXTa3KjpzHWq6Sd408vKzopel
+M5eWHIpsKExjBIkfd1LEc93wBHxo2JRuiebD9wyxoWOJ4w7hz9QxktinmNp3LYtvxi3HD1si+EFs
+2JY4hf9yyHXhu9ILehcPF0/zmxey4IV7BXfFDVvSjRvKlanzIj5SKB0Y9g7Q8rIpYKUxoZremZYg
+eOuk6JqAlzerzhTACBesTGZkDpTJsKlbeANbHoH43Dc5J3M4w9wiXvP5BCYOHUuzMQO3ocel1jHx
+OVgyYf89ZOJ37vBQ6JP7YDsJjO0hXsN4JlNs+SkcIhi7qAsDj3Sh074cJZ3G3GVaz6H9C7Ahu+Cu
+u86iYwVMkqBOAtNvG3JNCX9kMjww6JD8tEzey2dN857QvTk5sH8HviN00xM2Ji5tMvZfIj7mlbyc
+hrzs589ME8Eg9Z8KrBL+xbqh/V7EcX0iE7fkEPrWt6yTrvmcjZnyYQ+Ol6OYQTU4YsVLEaV/3Aem
+9dyXI5jEzdjxdYttzO9xOLIzTfwQ9Hx/rEsVIz7bvOURW2KNeNqSJv60BHiaecPgKNjQId4PCcyD
+PFjis5EPj7Jh1kqYTOHACPgRv+RlIHj5Qb95YJWIugy1Pci/eo6JEzpz3XAhdEB3DdvcJHr5KcWZ
+6wbzYIQNlvjA9CeY3yKbN6GQJMh7gxkcGE8k2J4Sj50bxUzhwRLzu97/Mq/n4LWlqAnxjsOv7cw+
+4V+HM1sxWElisnnpZ7YXkRXPMbdIucEm4bToC24oUcpON/W3HVKABb66FkeoEiYNKojo2cQMA7yX
+A6dBL79jHliFUjPMNThlec8WJ4FyYiizJNjLiScVSCIwIgfLDxZiPHqHMjI+JyIdusAjzDi/6Enw
+pu2hs3Kw/HJPRrvjDg+OCzj93IjEDySwvH6o2HDbZgVYXYI9h69gtgXgHJG4zaxtWS3XMbJrTxy4
+TeUO/jvAg4XzdeYWrNJ1zIuB9GFi3IsWgNXL2M6jUW8UzFykf3+OeQpqj73slcYI6OUAWH6QRxo3
+QkescyP6ob54b8A1rYF/PXcIJiz+iz/jUwW+oUsfnvdaFrH+zPwk0AG9hw95gWwohN91/EWTR6x7
+bEuE0wLSiawwR95kftKtcpccrBPX9jskHwrBRD4rhNHjrPo08yPWoPNYpUEBiADsTOyuu4CkQm8n
+9LIsYn05ot8HEYGVu68KyrpRcLSDt3+/tj0l3HBCEW6Ot18x7JR0DM+6+GU5Vi/MsfaY1XG/Zv4X
+KNThc6wSrPPihMRtQJFj9T5iG/gBPu2/g08zI3VXNCT0TagcKw0rvPUe20N4UgJ62a/2sgU5Fmpz
+Q/9xFwKdqeVldGYeD8bQuCWE92YqsEQZ181rsjTohHXMfUGXhmAVYMk19a5wKBNiGiU+XtSZn26o
+YobMRark5eK7Qi867R1ggfjguLI56GgBM5JXJwfeSgt3haLGiYcwj6G8bBJYabwu8IZ0IJ4oeoZ5
+POj7EexlnwqMSMCyJyEZYkP5G5q+4BH3jeQlTYjkXlxCPAkfS6rDYEkTHQ+1muYaO1OHo03VEL7l
+PB6sJEFnfGsMwZJ0ieMjwRCmisdfUDgH+MzpuJH47gGAJqGhTHisIDB3lMinEBAsXuEEG8zZ/TIv
+JwW8bBJY4pyfeLeQJ8bTNEFlH/DFq7xcgM+UgxF5jtWybiiDponE75JPpAb75T4erCTh4D4MVi6U
+oTbhZtskuVn4LolzoJkibcJEBx6Jqhg7VCUnIT5xyVnxtHeEWMZLmllgpQkRS5wvTsJgjczLzc0c
+HD9MnPUXcyzv4XuYRMXXV+ZlsyIWdE1mf2bcZiHuSBFLRA7mqwlBXg4Moi3rbmYe8kcOFkzuEq75
+AA23S7AGvq1QqgqZIfD17sHj+ByrCh3cahfAQkc+HYxaNW+x60Zh9G/C6uSIYO0Tb/ZtP+mRcqxN
+trglqtSMPy3XLLACORbvLogUBiuXny5KFO9MA3eFJ0b0ezHotELIo6k5Vu9E5hYxY39ezLFwZ5bF
+I7DUXs5RpP0QjJo+gNUDhjNl6DZHfldYh8E68bNB2xTD077AXeGmBHT3ahpZf2YelC2eFirAQvdT
+TL/zgbvC51CGhXVR3hVuNQss3JDM9io5WLA3+21TgyVMCilvJveZCVYh8si5UWjePYBJICXGEes5
+6OUumZdz5Gm/X5gtjBSsLhif4fmXZPNYfvAhAgvNYz0kW65UgAWet9leNAsrPzjcP64m8G6pGqxe
+xtYjm26YaBO6Ep4mZu9ePygzD6xzV0jzWLChrXKwwLkRaNxTgCVOcp2TzbMhL5sLFpozGMrccoEP
+sqOYpwJN92CwdvZPVHhZAVaXlEhGOBQKcUk+lQzvB6/FlPfv91RgkeVaOVj+XvhlOGkWWTiIB67+
+hHoofNP23XYZWOew+/zq0xabB5Ziih+6Sw4WzhOE5F1U4NJE2+9BsJeHmD6Ptckm8iRfWgGH8awp
+dNddSi+LYHl5MPZHApYAZssjDB58YSOJwlrhzidsaAUCyNYKfXWLb7bZ6gJgecGJwba7zNs+EFjF
+atl5j42ZrJggPTwUeyswQWjMGXEAAAJqSURBVPpef366gV8rPCuddp9pYPGLkry7HrEx4+RgoTUo
+YbohU5zzs/FBLMjLfpPBgtFcXISeKK4Vnt+3OMEWh159qvTy44oJ0ntIJxxUYKUVoNu7nOEMjs5e
+YXcDOmJjmHHC1/F5G9rdkItWv6EKfsXM+3MMs80srsR1d3hnAxWMm9LKg5WTh3TOTGDUM++PMGgM
+9PrA4VH8adiKKa3m6Ye2UUjuwhMvAbBganNLK45YSdjJuXg/hh97+Z4QXjYTLDgY3iXQ/QQT6Mxx
+7wS8PFn08ln+fhI7OedmJo5wUjD0fixmmLRss244nhey2ccJW3jwfqwEfr/OyCU9wv21CNalEcwg
+8wZDaacQY59yiE/NmcBWITznt5Wxi2DBACJsQhH3Y/GnmSi968SGkLu8aD9WjrSM0h9veAjsx7Lz
+82z8Nq74wGlmgZXGzBW/AZsYKbkS92PF4xiBs4qWxUPkXvaFBCMcsEpy87Aod1ruLJlbwG/HlO0w
+hMcKln4oZDdNuXmBT+dONm8XKcqYdhbCljdLq2sFvMoFS/mOBHU5c6UAsi53ssiR+jTzBDaUt7Ss
+SbwJzSmTdH8+93GYX1TlCE4uUygDvZyn9nKUwSrJqZLePZO7tNsrtHUYdTDuTG9IL/tkYBAvORnu
+eff6Zb0qSo/OcADM3Pfu1VHWq3fAr2djlNlXudQXdCTYjV4L6uCodfEG97RwSL7nXa2zPwKwqFCJ
+mlCwqFCwqFCwqFCwqFChYFGhYFGhYFGhQsGiQsGiQsGiQoWCRYWCRYWCRYUKBYsKBYsKBYsKFQoW
+FQoWFQoWFSoULCqXq/w/gbudjI6bMwYAAAAASUVORK5CYII=
diff --git a/Documentation/DocBook/media/crop.gif.b64 b/Documentation/DocBook/media/crop.gif.b64
new file mode 100644
index 0000000..11d936a
--- /dev/null
+++ b/Documentation/DocBook/media/crop.gif.b64
@@ -0,0 +1,105 @@
+R0lGODlhuQJGAeMAAAAAAH9/fwCvAP8AANEA0dEAAK8Ar////wCOAAAA0QAA////////////////
+/////ywAAAAAuQJGAQAE/vDISau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqP
+yKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaH
+iImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6gQC9vr/A
+wcLDxMXGx8jJysvMzc7P0NHS09TV1tfYxbth2d3e3+DRAePk5ebn6Onl4ezt7u3q8fLqANtg7/j5
++s/z/f4B+wIKHAjsn8F09ex5IciwobuDEM1Bi0ixosWLGDNqrJhQIZdk/htDihxJsiTJiSZTqlzJ
+MmNHj1q+tRznsKbNmzhzDoz3EiYWmTN7+vQJgOfQmN5mAjzKtCg9pj+TBoU61ClCqlaAthSKVZdV
+dFy7NtHKMqxYW1/PmT2bhOzKtWxlpZUYF4pblXDrvpq7Tq+Tu+UGCB5MuLDhw4gTK17MuLHjx5Aj
+S55MubLly5gza95MmVxev0EAkxsg8jNoVXNJ0zy9RPQ41RtNsz6V2vPstlLTwdYo+zap2qt9G3Ed
+YLdL4bGAL0VOhLhxjL2Zf1IeXboM56Wtt6KuPXRudM8vVu+eiTt5H9hDjj9vyfyIXrTW80gfO4OC
++/jz69/Pv7///wAG/ijggAQWaOCBCCao4IIMNujggRe4J4IwBxBg4YUYZqjhhhx26OGHIIYo4ogk
+loihMBbi1k084VlklgLsWQKjBRJqgIwEBJRyY4UqZsNidhjMGOMkQlLgnjERwkdBjuVpk2QFTB5B
+H2/2DUlJkRNYhWQKUTKyJQpdFjHlcUFaSaQxo9nGQph/fCkDm0OMCV2VZh7iZpbnwCYfBnDKcecO
+fXq3ojotckRnnXr8SQGWEtQIphuKEhEoEHKKdygHCUiQ6QEJdDrEphWA2oGo3UXaAaMHOHrCpFmY
+2gSr6H2XJ5AXoHqBp5xyuimpPfCa6we+6uWqCaiqagKsTAxrBbLz/slqTqEUvWgBqLviSqqvnXpq
+rbbZTpDtt9ziSsG3unKraabkltutWMq+UOyswa3A7A/tfjGvDpW6eKm3v+a667i38vvvuQLzW7Cm
+AJ878L/W9ouuR/Xi8O6zasorRMRo3JtDvoaWOe2v4IIc7LUIE4zwtd1Sm7C6KZ8MLsmzYBzExIFV
+rILGJsgcB843cBztvgqHWnKwup5s8rroVivwwEc3DHLR/jKcis5K0JxmvDezQLUePNvgc0TSBix0
+1OuG6nS56nob7ssqp132wuIi7cnWU1j9ms1chkD3IF3X8DVEYe9AtNi37M2F3cXh/WgFhjPSNw1/
+HxS4CS97MPjH/ts5uQfieqbQuCWPzxC5QZPncPnYoXz+BueKY+Bm6J3AHsPo/5TOmup5sB5vxLJv
+0vsLtPtjO1W4D0Kz6r9nknwLwfczvFeam6IAmndjnfcsy2vtbM3qAT2KkhkULwj4SRITIbzLWYx9
+j9j82L3HvyljivzeG1tC9qCzf4379cEPigACCAYAB0jAAhrwgAhMoAIXyMAGOvCBEIygAVMVDBLo
+Ln1ZWx8SmjeP521CAEYiXypAGML1XHBPF8BfJVToue1drX+1GgUJZTHDFJywBSycRA5PwEF5eFAT
+NYRFEE9wwzXRYoc5c2H1YGgBW32QFkMk1vkoZr3FyQKJJeih/lH894kotsKLFpwi9zB4vSvqzxr8
+oxIXPQHGVbRRBEVUnxk3qMTEvS+GonjjBBCwxwMg4I+d0CMI4pjBOUqpjtACm/c4IUhASuCPfPQj
+I1lAyDLGAosk0OJT1hhIC0RSkpDsoyg9GUpAhtKPp6QAJD9pB0F+oJJWvOQZq5FGMuExFFHkYyR1
+OUpWqrKPvHykJIXZyzy40gOwXNURZ0mNWs6Jk5P0JChXKUxHXsCXwQTlKIe5h2OeSowvRKEFMOkI
+ck4IkbRqogyvaU1uZpOd1URlNXepSnriwZscSOaxlknHQekmnRVwIhAxgM09rtKXBrXnKalJzFTe
+AZ8b0Of9/vh5SH+CB6CLWicPEAoIiGpAoiQwp+OYOQ1nWgqaT0TBQTl6TUN4tH7oEyeUKDocdN5R
+nXnsAUv98FJO2i+kNBWTTZkYUI3SkJLgXKJMlxTU5gxVjbf8HxSRSqOY4rCpcXqqLXGKy6muAKQj
+EOkixPoBTV4FpQOdRU+jiicqkjGWsCCrB8wKlkWm9KhfTaod36pMDVbUR4TC6AQEmom1spGqjLOq
+Ef1aU4uiD6pclapaEWskxcpRlv0E7D9vWtScTjavVXXrUicgV0SUlgN0VYtd04pXFYBVBKc1RGxt
+pNVnsvWwn3WtXju3WEM2VrMX5WxGPdtaG+62dftkrFAd/utWyHa2q7k1bmjHOFocYfVitT3pbTsZ
+XRS8NgSzJUR4XZddfaG1sF7V7XTDeVXlOpW5Y3TucKFbXO8et4p99e1ygfvYrT5XsvUl4n35mlz9
+vpe/zfXvfAEcC8P+t63Uba+BswrfF8p3sEZtMGUhzN7eYvav7QuscDFMXA2DNrGilfCHfxvizRJ1
+wV1Mr3RRHGEPx5Wk0jCpebcbzQBLcb1KVfGNM9vi4L6YxPQ1sXpp3OHLDhnE+xPxkSVAWEw4uMcz
+rmyKbfyK8ZYPwfFVMJIZLMQNN8qyhVzxfovcX9tGNsbdFTCQ91pdHrmXwmC2sJipnOEyn1jLNXZy
+l3Es/g4pX5jPJfbzkgHd5DQ/mcVRdvGhD1DlS1z5rlnmcJC57Aov06i8HeMxphWd6TNvWdCdJjRK
+JL1nSvf5FZdGNJM3jepWeJpxoP7Zea0sY/vOmbe1ZsWtS5jnJU660paINXr/rGk6C3nQRI60kY/9
+alco+7sgGLYftN2oXCty15butZxn7WxO21rV/DB0q5FdCWXzmtmmDrSjoQ1lNKrbzQ/GrY9LgO0P
+cJsP/04tXcCdbHH/mNzAnneqo21vVuMbxvpWcqlThWZLPnrN0m6zdt8ccVL7GuHIneidsVthY6+7
+2l80M8VPrfBzM5yW9954vrm77zD+OuRAHbmgir1X/monGtYq/2lYr7tzNif44WOGc81H0G8P/HsP
+Afc24Fa77KXDccB1fjrX0O0MHYea4zSX+McZTeuWC5vrzfC6rkXNWrGPm+zlDvYqol7ynp/859YO
+esXhSm9IN3zad0+yx9/e7IRbvO8Y/7vGdwx2LA/+4HA3PN8XXm+YO1zmEA/74/mN9WdT3u+WBzzS
+ZU1moMN75fI+/OcTH/rFf33mjjf9oguP86HrnFJSlxzV3231EDS9A1rPA915nsipE7zdBuf8zfFb
+YDUf2OhhHr2r8Z7y0wsdtkTHfd2Lr/vjU8Ld4bb+3vPrfDxDX8/SZ/f3k29zkDNf5BMmOfEFS3ql
+/rsd8rR/f87jX/SMHx3zSddxsjdx1wde2UcvuUc6uxd+vTdInWduZ/dyzRRzjAd7ozaAY5d/BAZ/
+5Sd/52dy6YdyrHBtDyh3qjB8H2h3IUh9I6h3LKd6Lld5E3h5FZh5sZd34veCkxeDoDeDogeA9SeA
+ODh78dZoMBiBMlhSFPh6NniBQ0iA49d8F/d8/hd9QDh9goeBhFeEZXeEc4d2zKB238Z2VXd/yud+
+G7h/Hdh/ivd/NRiAmqeF+MeFcWd2XyiBSkiDTAiHN1h9RIh6RriDSNiDefiDbxiEcfiEGUiHkkd+
+U2h+VYh+V6h+kyBBlniJmJiJmriJluiCqSeI/neYhDm2hGvXeJzgCzEjQkxXgnZ4gmC4DGJofGS4
+CcAAC7XYfpFXe9h3ewi4ffSHhfGjiqvwC2eYi/pne/ynffM3YoiYOqhoi894dcuXhsi4hsqYgtyn
+gN5XHt1mi93oe6zoha6Ih6Ooh6VogTpSZ+3RG7/HAcGHBygYiSA4idCYisgUjqA4jqJYaOY4hqY4
+NepYCcI4cwWYbQcYK77IjMBYj3KxFu24Ae94B/HYhlZ4iAuZHAFJCQP5kBoQkXYwka3nhnvYjAyJ
+kWBXkP52kD0gcH2xjQBpj3CIkk6nks2SkFN2kWiRkZOgJByZAR5ZByDpg653jk1YCwM5jADQ/pN8
+QpM7wJIhR4l7oZOSoIoyCXxMiS8JWDsLaJRS+QgwQj5V6Y5XuTFZKTxbmZOvICRKEpYQOZY44JTv
+B5Xx0ZWKgCW+EIUc+IgeKI8qSI9YcZSiUCxp0YhSiHhUSJGSaJFyWTh0WQjv0guB6IiGCYmIOY+K
+GReA2QnHUxSEmZeTuZeV2ZeXWReZqQl2A5nHuIvJ2IvLeJOLuQ2leQmcA5lZ55Y9U5bOc5ZHEZtX
+cl+8mZK8iJCt6XO305h6cEK/KZbBuZK42UG6STzGeQdFlJw+aZte05w+9Jx/GZ10QEjUOU7W6TfY
+uUWzKCzcGQew9J2kFZ6QM56bVJ5+oZ6E/qBP6vmTdBCUhTiU/oiOtyGfgQBS1Gmfc4Cf5WiII4mT
+0uGffvBavymgckCg/GigRMmH1qGgm4OP5GWVy1mTwxl4Q2KheNB0memgO/OKyhCL3QefzAGiddCO
+R0micAChqyah+1mU58GicsCRwgijbyCj6daPsviPMYKjq4OhZdUTPOoGPtp1pFijFGomRMoGSvmN
+draawomNv/iaCXqeh2Ok51Sl1siaWKqQWlqhXJoFU4pr7Ck67nlWKgqlZ2oFaQolUZo/5Bih+hmk
+/IkoIfQHc8pUFKSXbBiSFXmgZcoedQoGf7qeWRKngrCkadekemqjfPokx+mlakilqQCp/mEoqSkq
+pJWaoSGKqdXYp5tqoiDhqdr4pqFqqi1KqqppqabAqbCoqlrpkq3aqK86jbW5AYlqWqiKDCi6qqCa
+q7Q1B4tqXR3wq4VAqydqq2aJq8bqqm6QrGCKWo4KlMF6DMN6q6w6rbIqpbBqgHqTrQ+6rdQDpJ+6
+p+C6rObqA9baNcy6behaDN0ard/arqKaBvGaRJzgrKmqrsTKrvo6V++aA/3KQwebBgArrNCam9Ja
+sPtKBgmLAvMqkfVKDPcKsfkqscdKseNqkCtwsfeZscOwsc4ZsR4bPgsbAxU7si0bBg3LrQ+bsh27
+sr4asy3wstojCTObrjQ6qU+Ks/7q/gU8yzw6uwU/a681m50qS7Q52wVH6wIkuwZLq7FNS57FCrVZ
+lLTFqIG92p4jdaczmqfrSqlcq7BoGrLAeZ2KcLUnm7XvubVpW7RVMLVsCqxk+6NBe7ZDW7cqULUu
+y7Yz2ZRe+wRwKwwo67Q3C7jlWjeEq6HNIl4mq7hy66Z067gWe7gwpYOSiZWPWrnBsLhaS7CaG7ic
+e5J4manMCQiJO7qXW1dPe7pfygR4O7l98LoFEbuqNbu0W7tJcLutC3Wiu7sC662Z+7swG7yRq5w1
+tXV7y6THi6/Jq7zLawTCi3vwWLy/QLpza7rWS7U6m73e8ZHce5e8O3CNG76bOwTk/otdJRu9kTq9
+HFu97Iu0M9O8bfkEgvsq54ua9Guz9nu/+Auv+tuRUtC/SqC73Zu+Lbm+BIy6PfC+h6Sk/8sXiWmo
+EQy/OkDBdMQGDIy+Acy4A7zBwHOeHowbahDCANy3A4u2JnybN5DCSqDAQcDCGGyZGhzDTlUDNNwa
+qQuB18iX2Yi84MvDbisDP5wsQSyOV0rEWYrEFQwDSzwWTVyYlEmoGTyhJCnFCOguB1yd3HDFpXqY
+WqzDXIygXly+nhiZWNwFNmwDOOy9mHvEa0yWOfiJn/sFcTwDc+zAT3nHQAyFnvvG90DGzkuIBWq2
+L/y3gqy9cwiIXZiPfIzIYryP/mUrkml8qI8snl/LiLpIrmrQx0IsplBMpp38F+NRxUhBBX88wqUL
+w6nMxq8Uxkv5BqS8x4MqlJrspF08y897j7zqeWuQy6ybxbxcqJsMzFEQm6xcyU7wyi5sxLLMzFkV
+UbacQnZgzG2ryHjay0L7y9b8wT61umXsJ6krzYxMzY48zlNMkOYcq9t8uOoMzn4rzu6swpnHlgi8
+B9x8y5jMt+tMvXaczz2MiPx8yf4cs/WszL6sxgatyjiZ0ADdB//MqAEtvdNM0NUc0eSMhmHbJu/a
+0Fv80Jzs0T8wPT1B0do8CNxM0mhs0ih9BTMCPiwNnoWQyzAtmjs801RQJPBx/tMzZSdcutNFzNHt
+7NNHwCgtPMm6zAvcadRRrNSQ+2lf0ZnHnNPGKdWoTNVSMDGoidXnTNQ0wNWu6dVfDU69INbyvAgX
+a9bEidZOgDioGdKOwKxw7aFybbvHFY2tmAiJmtcruNdNgJyl7NbVJdh+Sdh8Pcw4yiwGMAGRbQCU
+jQGRnQKXvQWPfcF0LLsQzNg+XIIgiiyVLQGUfdmZTQGpbQKr3cpPbcqhedT1W9CgjbDhqKBsktmT
+XdoHkNqtXdqnLdm7fdqVTdy7PcaGPMSxPdW1zbzD/GnHPNmm3duSXd0XIN3TTd3ajdoVwN1iIJ+K
+PZrNjQQS9Z1wIt3GPd28/m0B2L3d2e3dxJ3dY2DenA3IcTnezg3SUdvNwu3b1d3aqt3b8P3e6m3d
+AH7IIpvR87vRs93R+D3D48qbfbLaup3e7G3avD3g1G3c7W0GEl7fsPy9Dv7gof3c7prIolCa4d3T
+JO6+kQuYssPhgL0WK77MLU4ED7mWQ40KOg6oCt6pIV7HI37jg2vi50Q+SVoGxIjR3pzJDh3OEE3k
+tm3kR94RSa7k0VjjMi3l+Uvl5fqMV04GFaTlUH7SXL6FklyH/hrmZ+ALZH7PUX7mJa7fa2Iidn7n
+eJ7ner7nJgLiDC7AtC3neezG9wuXG2jmgr6KXh7Bhv5DiT4D1qqvja6d/o/+h0K9spPuu5UujXTO
+w5n+2ZsOjotOwJ9ewqGOi2ArxaUe6Keu6J0ew6s+5K3u6sZIjdYb60k962h+6R6L6/is64uY5myt
+vL4e58Ae7LwuscWO6Me+XclesMve7EqczUQb7dJOxdSOs9Z+7S4Q6e267dzexk5N6m3q2aYe7uVc
+yIVe7r0L6ugek/FM7OyuvudurAUgAfd+AAWw7z+Q7yfg79806utuk3F9uvyu7/qe7wCvAwtPAg3/
+UdmO6fP+wPVuJf5+7/uu8BXw8BmP8QrP7x0/AR0/8gl/8CKf8fhu8hpf8h4P8iHfuXpM7gAw8wBQ
+8zZ/8zif8zrf2e1e/vEWj/AIv/L4fgEXD/QXX/RFL/JAv/RLr/JDb/Qpr/QmD/ECz746f/VYj/U8
+T++sjigYz/Jfn/AYsPBC7/Rkj/JJ//Ri//Qr//FKz/JU/+omnPV0X/dbT/FdXyco//ZCbwEHH/Z/
+//drb/Z9H/htz/Ypr/Fp7+zx/rt1//hXf/eB7LhkP/Qk7/eCn/hwr/kjf/lBv/d7v/mKj/ahn+4x
+P/CQn/o5zNM2jtIPnwGvvwPeDq6qX/uSf99I3PkeEPtE7+JVH761r/q3f+g+zft+7/tyv8HBn/rD
+7+jvLurJz+jL//jNT+nPb/qEbvXTb/f2fegP8v3gH/7iP/7kX/7m/n/+6D/707r93K8bnPH+8B//
+8j//9F//9n//+E//oez47J/1SmHJEHDkpNVenPXm3X8wFEeyNM8RCFa2BVA4lme6tm8g13e+9/lW
+UDgkFgOvW1K5ZDadT6hSVURGrVdsdvnjdntGcHhY1ZbNZ3Ra3ZkSyWt4XF7z1rtivNi+5/f9f8BA
+wUHCQsNDxETFHaO3uUfISDa7vErLS8xMzU3OTr1Az1DRUdJS0yBHSdXVyL3TV9hY2dmjRdtb3NxB
+2iNW3985XeFh4mLjY+Rk5WUeYOdn6Gjpaepq62vsbO1t7m7vb/Bw8XHycvNz9HT1dfZ293f4ePl5
++nr7e/x8/X3+G37/f4ABBQ4kWNDgQYQJFS5k2NDhQ4gRJdKLAAA7
diff --git a/Documentation/DocBook/dvb/.gitignore b/Documentation/DocBook/media/dvb/.gitignore
similarity index 100%
rename from Documentation/DocBook/dvb/.gitignore
rename to Documentation/DocBook/media/dvb/.gitignore
diff --git a/Documentation/DocBook/dvb/audio.xml b/Documentation/DocBook/media/dvb/audio.xml
similarity index 79%
rename from Documentation/DocBook/dvb/audio.xml
rename to Documentation/DocBook/media/dvb/audio.xml
index eeb96b8..d643862 100644
--- a/Documentation/DocBook/dvb/audio.xml
+++ b/Documentation/DocBook/media/dvb/audio.xml
@@ -14,17 +14,17 @@
 audio device.
 </para>
 
-<section id="audio_stream_source_t">
+<section id="audio-stream-source-t">
 <title>audio_stream_source_t</title>
 <para>The audio stream source is set through the AUDIO_SELECT_SOURCE call and can take
 the following values, depending on whether we are replaying from an internal (demux) or
 external (user write) source.
 </para>
 <programlisting>
- typedef enum {
-	 AUDIO_SOURCE_DEMUX,
-	 AUDIO_SOURCE_MEMORY
- } audio_stream_source_t;
+typedef enum {
+	AUDIO_SOURCE_DEMUX,
+	AUDIO_SOURCE_MEMORY
+} audio_stream_source_t;
 </programlisting>
 <para>AUDIO_SOURCE_DEMUX selects the demultiplexer (fed either by the frontend or the
 DVR device) as the source of the video stream. If AUDIO_SOURCE_MEMORY
@@ -33,61 +33,64 @@
 </para>
 
 </section>
-<section id="audio_play_state_t">
+<section id="audio-play-state-t">
 <title>audio_play_state_t</title>
 <para>The following values can be returned by the AUDIO_GET_STATUS call representing the
 state of audio playback.
 </para>
 <programlisting>
- typedef enum {
-	 AUDIO_STOPPED,
-	 AUDIO_PLAYING,
-	 AUDIO_PAUSED
- } audio_play_state_t;
+typedef enum {
+	AUDIO_STOPPED,
+	AUDIO_PLAYING,
+	AUDIO_PAUSED
+} audio_play_state_t;
 </programlisting>
 
 </section>
-<section id="audio_channel_select_t">
+<section id="audio-channel-select-t">
 <title>audio_channel_select_t</title>
 <para>The audio channel selected via AUDIO_CHANNEL_SELECT is determined by the
 following values.
 </para>
 <programlisting>
- typedef enum {
-	 AUDIO_STEREO,
-	 AUDIO_MONO_LEFT,
-	 AUDIO_MONO_RIGHT,
- } audio_channel_select_t;
+typedef enum {
+	AUDIO_STEREO,
+	AUDIO_MONO_LEFT,
+	AUDIO_MONO_RIGHT,
+	AUDIO_MONO,
+	AUDIO_STEREO_SWAPPED
+} audio_channel_select_t;
 </programlisting>
 
 </section>
-<section id="struct_audio_status">
+<section id="audio-status">
 <title>struct audio_status</title>
 <para>The AUDIO_GET_STATUS call returns the following structure informing about various
 states of the playback operation.
 </para>
 <programlisting>
- typedef struct audio_status {
-	 boolean AV_sync_state;
-	 boolean mute_state;
-	 audio_play_state_t play_state;
-	 audio_stream_source_t stream_source;
-	 audio_channel_select_t channel_select;
-	 boolean bypass_mode;
- } audio_status_t;
+typedef struct audio_status {
+	boolean AV_sync_state;
+	boolean mute_state;
+	audio_play_state_t play_state;
+	audio_stream_source_t stream_source;
+	audio_channel_select_t channel_select;
+	boolean bypass_mode;
+	audio_mixer_t mixer_state;
+} audio_status_t;
 </programlisting>
 
 </section>
-<section id="struct_audio_mixer">
+<section id="audio-mixer">
 <title>struct audio_mixer</title>
 <para>The following structure is used by the AUDIO_SET_MIXER call to set the audio
 volume.
 </para>
 <programlisting>
- typedef struct audio_mixer {
-	 unsigned int volume_left;
-	 unsigned int volume_right;
- } audio_mixer_t;
+typedef struct audio_mixer {
+	unsigned int volume_left;
+	unsigned int volume_right;
+} audio_mixer_t;
 </programlisting>
 
 </section>
@@ -109,17 +112,17 @@
 </programlisting>
 
 </section>
-<section id="struct_audio_karaoke">
+<section id="audio-karaoke">
 <title>struct audio_karaoke</title>
 <para>The ioctl AUDIO_SET_KARAOKE uses the following format:
 </para>
 <programlisting>
- typedef
- struct audio_karaoke{
-	 int vocal1;
-	 int vocal2;
-	 int melody;
- } audio_karaoke_t;
+typedef
+struct audio_karaoke {
+	int vocal1;
+	int vocal2;
+	int melody;
+} audio_karaoke_t;
 </programlisting>
 <para>If Vocal1 or Vocal2 are non-zero, they get mixed into left and right t at 70% each. If both,
 Vocal1 and Vocal2 are non-zero, Vocal1 gets mixed into the left channel and Vocal2 into the
@@ -128,7 +131,7 @@
 </para>
 
 </section>
-<section id="audio_attributes">
+<section id="audio-attributes-t">
 <title>audio attributes</title>
 <para>The following attributes can be set by a call to AUDIO_SET_ATTRIBUTES:
 </para>
@@ -217,8 +220,7 @@
 <para>(blocking mode is the default)</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>ENODEV</para>
@@ -228,13 +230,6 @@
 </entry>
  </row><row><entry
  align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EBUSY</para>
 </entry><entry
  align="char">
@@ -276,8 +271,7 @@
 <para>File descriptor returned by a previous call to open().</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
@@ -332,8 +326,7 @@
 <para>Size of buf.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EPERM</para>
@@ -358,7 +351,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="AUDIO_STOP"
 role="subsection"><title>AUDIO_STOP</title>
 <para>DESCRIPTION
 </para>
@@ -391,25 +384,9 @@
 <para>Equals AUDIO_STOP for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_PLAY"
 role="subsection"><title>AUDIO_PLAY</title>
 <para>DESCRIPTION
 </para>
@@ -443,25 +420,9 @@
 <para>Equals AUDIO_PLAY for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_PAUSE"
 role="subsection"><title>AUDIO_PAUSE</title>
 <para>DESCRIPTION
 </para>
@@ -503,25 +464,49 @@
 <para>Equals AUDIO_PAUSE for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
+&return-value-dvb;
+
+</section><section id="AUDIO_CONTINUE"
+role="subsection"><title>AUDIO_CONTINUE</title>
+<para>DESCRIPTION
 </para>
-<informaltable><tgroup cols="2"><tbody><row><entry
+<informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
+<para>This ioctl restarts the decoding and playing process previously paused
+with AUDIO_PAUSE command.</para>
 </entry>
  </row><row><entry
  align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
+<para>It only works if the stream were previously stopped with AUDIO_PAUSE</para>
 </entry>
  </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request = AUDIO_CONTINUE);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals AUDIO_CONTINUE for this command.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SELECT_SOURCE"
 role="subsection"><title>AUDIO_SELECT_SOURCE</title>
 <para>DESCRIPTION
 </para>
@@ -567,32 +552,9 @@
  stream.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_MUTE"
 role="subsection"><title>AUDIO_SET_MUTE</title>
 <para>DESCRIPTION
 </para>
@@ -646,32 +608,9 @@
 <para>FALSE Audio Un-mute</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_AV_SYNC"
 role="subsection"><title>AUDIO_SET_AV_SYNC</title>
 <para>DESCRIPTION
 </para>
@@ -725,32 +664,9 @@
 <para>FALSE AV-sync OFF</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_BYPASS_MODE"
 role="subsection"><title>AUDIO_SET_BYPASS_MODE</title>
 <para>DESCRIPTION
 </para>
@@ -808,32 +724,9 @@
 <para>FALSE Bypass is enabled</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_CHANNEL_SELECT"
 role="subsection"><title>AUDIO_CHANNEL_SELECT</title>
 <para>DESCRIPTION
 </para>
@@ -877,32 +770,9 @@
  stereo).</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter ch.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_GET_STATUS"
 role="subsection"><title>AUDIO_GET_STATUS</title>
 <para>DESCRIPTION
 </para>
@@ -945,32 +815,9 @@
 <para>Returns the current state of Audio Device.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>status points to invalid address.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_GET_CAPABILITIES"
 role="subsection"><title>AUDIO_GET_CAPABILITIES</title>
 <para>DESCRIPTION
 </para>
@@ -1013,32 +860,9 @@
 <para>Returns a bit array of supported sound formats.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>cap points to an invalid address.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_CLEAR_BUFFER"
 role="subsection"><title>AUDIO_CLEAR_BUFFER</title>
 <para>DESCRIPTION
 </para>
@@ -1072,25 +896,9 @@
 <para>Equals AUDIO_CLEAR_BUFFER for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_ID"
 role="subsection"><title>AUDIO_SET_ID</title>
 <para>DESCRIPTION
 </para>
@@ -1136,32 +944,9 @@
 <para>audio sub-stream id</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid sub-stream id.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_MIXER"
 role="subsection"><title>AUDIO_SET_MIXER</title>
 <para>DESCRIPTION
 </para>
@@ -1202,32 +987,9 @@
 <para>mixer settings.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>mix points to an invalid address.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="AUDIO_SET_STREAMTYPE"
 role="subsection"><title>AUDIO_SET_STREAMTYPE</title>
 <para>DESCRIPTION
 </para>
@@ -1270,17 +1032,9 @@
 <para>stream type</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1288,7 +1042,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="AUDIO_SET_EXT_ID"
 role="subsection"><title>AUDIO_SET_EXT_ID</title>
 <para>DESCRIPTION
 </para>
@@ -1330,17 +1084,9 @@
 <para>audio sub_stream_id</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1348,7 +1094,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="AUDIO_SET_ATTRIBUTES"
 role="subsection"><title>AUDIO_SET_ATTRIBUTES</title>
 <para>DESCRIPTION
 </para>
@@ -1391,17 +1137,9 @@
 <para>audio attributes according to section ??</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1409,7 +1147,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="AUDIO_SET_KARAOKE"
 role="subsection"><title>AUDIO_SET_KARAOKE</title>
 <para>DESCRIPTION
 </para>
@@ -1422,7 +1160,7 @@
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(fd, int request = AUDIO_SET_STREAMTYPE,
+<para>int ioctl(fd, int request = AUDIO_SET_KARAOKE,
  audio_karaoke_t &#x22C6;karaoke);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1440,7 +1178,7 @@
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals AUDIO_SET_STREAMTYPE for this
+<para>Equals AUDIO_SET_KARAOKE for this
  command.</para>
 </entry>
  </row><row><entry
@@ -1452,17 +1190,9 @@
 <para>karaoke settings according to section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
diff --git a/Documentation/DocBook/dvb/ca.xml b/Documentation/DocBook/media/dvb/ca.xml
similarity index 67%
rename from Documentation/DocBook/dvb/ca.xml
rename to Documentation/DocBook/media/dvb/ca.xml
index b1f1d2f..5c4adb4 100644
--- a/Documentation/DocBook/dvb/ca.xml
+++ b/Documentation/DocBook/media/dvb/ca.xml
@@ -8,75 +8,85 @@
 <title>CA Data Types</title>
 
 
-<section id="ca_slot_info_t">
+<section id="ca-slot-info">
 <title>ca_slot_info_t</title>
  <programlisting>
- /&#x22C6; slot interface types and info &#x22C6;/
+typedef struct ca_slot_info {
+	int num;               /&#x22C6; slot number &#x22C6;/
 
- typedef struct ca_slot_info_s {
-	 int num;               /&#x22C6; slot number &#x22C6;/
+	int type;              /&#x22C6; CA interface this slot supports &#x22C6;/
+#define CA_CI            1     /&#x22C6; CI high level interface &#x22C6;/
+#define CA_CI_LINK       2     /&#x22C6; CI link layer level interface &#x22C6;/
+#define CA_CI_PHYS       4     /&#x22C6; CI physical layer level interface &#x22C6;/
+#define CA_DESCR         8     /&#x22C6; built-in descrambler &#x22C6;/
+#define CA_SC          128     /&#x22C6; simple smart card interface &#x22C6;/
 
-	 int type;           /&#x22C6; CA interface this slot supports &#x22C6;/
- #define CA_CI            1  /&#x22C6; CI high level interface &#x22C6;/
- #define CA_CI_LINK       2  /&#x22C6; CI link layer level interface &#x22C6;/
- #define CA_CI_PHYS       4  /&#x22C6; CI physical layer level interface &#x22C6;/
- #define CA_SC          128  /&#x22C6; simple smart card interface &#x22C6;/
-
-	 unsigned int flags;
- #define CA_CI_MODULE_PRESENT 1 /&#x22C6; module (or card) inserted &#x22C6;/
- #define CA_CI_MODULE_READY   2
- } ca_slot_info_t;
+	unsigned int flags;
+#define CA_CI_MODULE_PRESENT 1 /&#x22C6; module (or card) inserted &#x22C6;/
+#define CA_CI_MODULE_READY   2
+} ca_slot_info_t;
 </programlisting>
 
 </section>
-<section id="ca_descr_info_t">
+<section id="ca-descr-info">
 <title>ca_descr_info_t</title>
- <programlisting>
- typedef struct ca_descr_info_s {
-	 unsigned int num;  /&#x22C6; number of available descramblers (keys) &#x22C6;/
-	 unsigned int type; /&#x22C6; type of supported scrambling system &#x22C6;/
- #define CA_ECD           1
- #define CA_NDS           2
- #define CA_DSS           4
- } ca_descr_info_t;
+<programlisting>
+typedef struct ca_descr_info {
+	unsigned int num;  /&#x22C6; number of available descramblers (keys) &#x22C6;/
+	unsigned int type; /&#x22C6; type of supported scrambling system &#x22C6;/
+#define CA_ECD           1
+#define CA_NDS           2
+#define CA_DSS           4
+} ca_descr_info_t;
 </programlisting>
 
 </section>
-<section id="ca_cap_t">
-<title>ca_cap_t</title>
- <programlisting>
- typedef struct ca_cap_s {
-	 unsigned int slot_num;  /&#x22C6; total number of CA card and module slots &#x22C6;/
-	 unsigned int slot_type; /&#x22C6; OR of all supported types &#x22C6;/
-	 unsigned int descr_num; /&#x22C6; total number of descrambler slots (keys) &#x22C6;/
-	 unsigned int descr_type;/&#x22C6; OR of all supported types &#x22C6;/
+<section id="ca-caps">
+<title>ca_caps_t</title>
+<programlisting>
+typedef struct ca_caps {
+	unsigned int slot_num;  /&#x22C6; total number of CA card and module slots &#x22C6;/
+	unsigned int slot_type; /&#x22C6; OR of all supported types &#x22C6;/
+	unsigned int descr_num; /&#x22C6; total number of descrambler slots (keys) &#x22C6;/
+	unsigned int descr_type;/&#x22C6; OR of all supported types &#x22C6;/
  } ca_cap_t;
 </programlisting>
 
 </section>
-<section id="ca_msg_t">
+<section id="ca-msg">
 <title>ca_msg_t</title>
- <programlisting>
- /&#x22C6; a message to/from a CI-CAM &#x22C6;/
- typedef struct ca_msg_s {
-	 unsigned int index;
-	 unsigned int type;
-	 unsigned int length;
-	 unsigned char msg[256];
- } ca_msg_t;
+<programlisting>
+/&#x22C6; a message to/from a CI-CAM &#x22C6;/
+typedef struct ca_msg {
+	unsigned int index;
+	unsigned int type;
+	unsigned int length;
+	unsigned char msg[256];
+} ca_msg_t;
 </programlisting>
 
 </section>
-<section id="ca_descr_t">
+<section id="ca-descr">
 <title>ca_descr_t</title>
- <programlisting>
- typedef struct ca_descr_s {
-	 unsigned int index;
-	 unsigned int parity;
-	 unsigned char cw[8];
- } ca_descr_t;
+<programlisting>
+typedef struct ca_descr {
+	unsigned int index;
+	unsigned int parity;
+	unsigned char cw[8];
+} ca_descr_t;
 </programlisting>
- </section></section>
+</section>
+
+<section id="ca-pid">
+<title>ca-pid</title>
+<programlisting>
+typedef struct ca_pid {
+	unsigned int pid;
+	int index;		/&#x22C6; -1 == disable&#x22C6;/
+} ca_pid_t;
+</programlisting>
+</section></section>
+
 <section id="ca_function_calls">
 <title>CA Function Calls</title>
 
@@ -148,8 +158,7 @@
 <para>(blocking mode is the default)</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>ENODEV</para>
@@ -207,8 +216,7 @@
 <para>File descriptor returned by a previous call to open().</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
diff --git a/Documentation/DocBook/dvb/demux.xml b/Documentation/DocBook/media/dvb/demux.xml
similarity index 84%
rename from Documentation/DocBook/dvb/demux.xml
rename to Documentation/DocBook/media/dvb/demux.xml
index 1b8c4e9..37c1790 100644
--- a/Documentation/DocBook/dvb/demux.xml
+++ b/Documentation/DocBook/media/dvb/demux.xml
@@ -7,15 +7,19 @@
 <section id="dmx_types">
 <title>Demux Data Types</title>
 
-<section id="dmx_output_t">
+<section id="dmx-output-t">
 <title>dmx_output_t</title>
- <programlisting>
- typedef enum
- {
-	 DMX_OUT_DECODER,
-	 DMX_OUT_TAP,
-	 DMX_OUT_TS_TAP
- } dmx_output_t;
+<programlisting>
+typedef enum
+{
+	DMX_OUT_DECODER, /&#x22C6; Streaming directly to decoder. &#x22C6;/
+	DMX_OUT_TAP,     /&#x22C6; Output going to a memory buffer &#x22C6;/
+			 /&#x22C6; (to be retrieved via the read command).&#x22C6;/
+	DMX_OUT_TS_TAP,  /&#x22C6; Output multiplexed into a new TS  &#x22C6;/
+			 /&#x22C6; (to be retrieved by reading from the &#x22C6;/
+			 /&#x22C6; logical DVR device).                 &#x22C6;/
+	DMX_OUT_TSDEMUX_TAP /&#x22C6; Like TS_TAP but retrieved from the DMX device &#x22C6;/
+} dmx_output_t;
 </programlisting>
 <para><emphasis role="tt">DMX_OUT_TAP</emphasis> delivers the stream output to the demux device on which the ioctl is
 called.
@@ -26,96 +30,95 @@
 </para>
 </section>
 
-<section id="dmx_input_t">
+<section id="dmx-input-t">
 <title>dmx_input_t</title>
- <programlisting>
- typedef enum
- {
-	 DMX_IN_FRONTEND,
-	 DMX_IN_DVR
- } dmx_input_t;
+<programlisting>
+typedef enum
+{
+	DMX_IN_FRONTEND, /&#x22C6; Input from a front-end device.  &#x22C6;/
+	DMX_IN_DVR       /&#x22C6; Input from the logical DVR device.  &#x22C6;/
+} dmx_input_t;
 </programlisting>
 </section>
 
-<section id="dmx_pes_type_t">
+<section id="dmx-pes-type-t">
 <title>dmx_pes_type_t</title>
- <programlisting>
- typedef enum
- {
-	 DMX_PES_AUDIO,
-	 DMX_PES_VIDEO,
-	 DMX_PES_TELETEXT,
-	 DMX_PES_SUBTITLE,
-	 DMX_PES_PCR,
-	 DMX_PES_OTHER
- } dmx_pes_type_t;
+<programlisting>
+typedef enum
+{
+	DMX_PES_AUDIO0,
+	DMX_PES_VIDEO0,
+	DMX_PES_TELETEXT0,
+	DMX_PES_SUBTITLE0,
+	DMX_PES_PCR0,
+
+	DMX_PES_AUDIO1,
+	DMX_PES_VIDEO1,
+	DMX_PES_TELETEXT1,
+	DMX_PES_SUBTITLE1,
+	DMX_PES_PCR1,
+
+	DMX_PES_AUDIO2,
+	DMX_PES_VIDEO2,
+	DMX_PES_TELETEXT2,
+	DMX_PES_SUBTITLE2,
+	DMX_PES_PCR2,
+
+	DMX_PES_AUDIO3,
+	DMX_PES_VIDEO3,
+	DMX_PES_TELETEXT3,
+	DMX_PES_SUBTITLE3,
+	DMX_PES_PCR3,
+
+	DMX_PES_OTHER
+} dmx_pes_type_t;
 </programlisting>
 </section>
 
-<section id="dmx_event_t">
-<title>dmx_event_t</title>
- <programlisting>
- typedef enum
- {
-	 DMX_SCRAMBLING_EV,
-	 DMX_FRONTEND_EV
- } dmx_event_t;
-</programlisting>
-</section>
-
-<section id="dmx_scrambling_status_t">
-<title>dmx_scrambling_status_t</title>
- <programlisting>
- typedef enum
- {
-	 DMX_SCRAMBLING_OFF,
-	 DMX_SCRAMBLING_ON
- } dmx_scrambling_status_t;
-</programlisting>
-</section>
-
-<section id="dmx_filter">
+<section id="dmx-filter">
 <title>struct dmx_filter</title>
  <programlisting>
  typedef struct dmx_filter
- {
-	 uint8_t         filter[DMX_FILTER_SIZE];
-	 uint8_t         mask[DMX_FILTER_SIZE];
- } dmx_filter_t;
+{
+	__u8  filter[DMX_FILTER_SIZE];
+	__u8  mask[DMX_FILTER_SIZE];
+	__u8  mode[DMX_FILTER_SIZE];
+} dmx_filter_t;
 </programlisting>
 </section>
 
-<section id="dmx_sct_filter_params">
+<section id="dmx-sct-filter-params">
 <title>struct dmx_sct_filter_params</title>
- <programlisting>
- struct dmx_sct_filter_params
- {
-	 uint16_t            pid;
-	 dmx_filter_t        filter;
-	 uint32_t            timeout;
-	 uint32_t            flags;
- #define DMX_CHECK_CRC       1
- #define DMX_ONESHOT         2
- #define DMX_IMMEDIATE_START 4
- };
+<programlisting>
+struct dmx_sct_filter_params
+{
+	__u16          pid;
+	dmx_filter_t   filter;
+	__u32          timeout;
+	__u32          flags;
+#define DMX_CHECK_CRC       1
+#define DMX_ONESHOT         2
+#define DMX_IMMEDIATE_START 4
+#define DMX_KERNEL_CLIENT   0x8000
+};
 </programlisting>
 </section>
 
-<section id="dmx_pes_filter_params">
+<section id="dmx-pes-filter-params">
 <title>struct dmx_pes_filter_params</title>
- <programlisting>
- struct dmx_pes_filter_params
- {
-	 uint16_t            pid;
-	 dmx_input_t         input;
-	 dmx_output_t        output;
-	 dmx_pes_type_t      pes_type;
-	 uint32_t            flags;
- };
+<programlisting>
+struct dmx_pes_filter_params
+{
+	__u16          pid;
+	dmx_input_t    input;
+	dmx_output_t   output;
+	dmx_pes_type_t pes_type;
+	__u32          flags;
+};
 </programlisting>
 </section>
 
-<section id="dmx_event">
+<section id="dmx-event">
 <title>struct dmx_event</title>
  <programlisting>
  struct dmx_event
@@ -130,19 +133,44 @@
 </programlisting>
 </section>
 
-<section id="dmx_stc">
+<section id="dmx-stc">
 <title>struct dmx_stc</title>
- <programlisting>
- struct dmx_stc {
-	 unsigned int num;       /&#x22C6; input : which STC? 0..N &#x22C6;/
-	 unsigned int base;      /&#x22C6; output: divisor for stc to get 90 kHz clock &#x22C6;/
-	 uint64_t stc;           /&#x22C6; output: stc in 'base'&#x22C6;90 kHz units &#x22C6;/
- };
+<programlisting>
+struct dmx_stc {
+	unsigned int num;	/&#x22C6; input : which STC? 0..N &#x22C6;/
+	unsigned int base;	/&#x22C6; output: divisor for stc to get 90 kHz clock &#x22C6;/
+	__u64 stc;		/&#x22C6; output: stc in 'base'&#x22C6;90 kHz units &#x22C6;/
+};
 </programlisting>
- </section>
-
 </section>
 
+<section id="dmx-caps">
+<title>struct dmx_caps</title>
+<programlisting>
+ typedef struct dmx_caps {
+	__u32 caps;
+	int num_decoders;
+} dmx_caps_t;
+</programlisting>
+</section>
+
+<section id="dmx-source-t">
+<title>enum dmx_source_t</title>
+<programlisting>
+typedef enum {
+	DMX_SOURCE_FRONT0 = 0,
+	DMX_SOURCE_FRONT1,
+	DMX_SOURCE_FRONT2,
+	DMX_SOURCE_FRONT3,
+	DMX_SOURCE_DVR0   = 16,
+	DMX_SOURCE_DVR1,
+	DMX_SOURCE_DVR2,
+	DMX_SOURCE_DVR3
+} dmx_source_t;
+</programlisting>
+</section>
+
+</section>
 <section id="dmx_fcalls">
 <title>Demux Function Calls</title>
 
@@ -211,8 +239,7 @@
 <para>(blocking mode is the default)</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>ENODEV</para>
@@ -271,8 +298,7 @@
 <para>File descriptor returned by a previous call to open().</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
@@ -353,8 +379,7 @@
 <para>Size of buf.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EWOULDBLOCK</para>
@@ -457,8 +482,7 @@
 <para>Size of buf.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EWOULDBLOCK</para>
@@ -491,7 +515,7 @@
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="dmx_start">
+<section id="DMX_START">
 <title>DMX_START</title>
 <para>DESCRIPTION
 </para>
@@ -525,17 +549,9 @@
 <para>Equals DMX_START for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -556,7 +572,7 @@
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="dmx_stop">
+<section id="DMX_STOP">
 <title>DMX_STOP</title>
 <para>DESCRIPTION
 </para>
@@ -591,19 +607,10 @@
 <para>Equals DMX_STOP for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
-<section id="dmx_set_filter">
+<section id="DMX_SET_FILTER">
 <title>DMX_SET_FILTER</title>
 <para>DESCRIPTION
 </para>
@@ -654,26 +661,10 @@
 <para>Pointer to structure containing filter parameters.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid argument.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
-<section id="dmx_set_pes_filter">
+<section id="DMX_SET_PES_FILTER">
 <title>DMX_SET_PES_FILTER</title>
 <para>DESCRIPTION
 </para>
@@ -727,24 +718,9 @@
 <para>Pointer to structure containing filter parameters.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid argument.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EBUSY</para>
 </entry><entry
  align="char">
@@ -756,7 +732,7 @@
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="dms_set_buffer_size">
+<section id="DMX_SET_BUFFER_SIZE">
 <title>DMX_SET_BUFFER_SIZE</title>
 <para>DESCRIPTION
 </para>
@@ -799,27 +775,10 @@
 <para>Size of circular buffer.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOMEM</para>
-</entry><entry
- align="char">
-<para>The driver was not able to allocate a buffer of the
- requested size.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
-<section id="dmx_get_event">
+<section id="DMX_GET_EVENT">
 <title>DMX_GET_EVENT</title>
 <para>DESCRIPTION
 </para>
@@ -872,24 +831,9 @@
 <para>Pointer to the location where the event is to be stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>ev points to an invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EWOULDBLOCK</para>
 </entry><entry
  align="char">
@@ -899,7 +843,7 @@
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="dmx_get_stc">
+<section id="DMX_GET_STC">
 <title>DMX_GET_STC</title>
 <para>DESCRIPTION
 </para>
@@ -946,24 +890,9 @@
 <para>Pointer to the location where the stc is to be stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>stc points to an invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
diff --git a/Documentation/DocBook/dvb/dvbapi.xml b/Documentation/DocBook/media/dvb/dvbapi.xml
similarity index 85%
rename from Documentation/DocBook/dvb/dvbapi.xml
rename to Documentation/DocBook/media/dvb/dvbapi.xml
index 9fad86c..2ab6ddc 100644
--- a/Documentation/DocBook/dvb/dvbapi.xml
+++ b/Documentation/DocBook/media/dvb/dvbapi.xml
@@ -114,8 +114,28 @@
     &sub-examples;
   </chapter>
 <!-- END OF CHAPTERS -->
+  <appendix id="audio_h">
+    <title>DVB Audio Header File</title>
+    &sub-audio-h;
+  </appendix>
+  <appendix id="ca_h">
+    <title>DVB Conditional Access Header File</title>
+    &sub-ca-h;
+  </appendix>
+  <appendix id="dmx_h">
+    <title>DVB Demux Header File</title>
+    &sub-dmx-h;
+  </appendix>
   <appendix id="frontend_h">
     <title>DVB Frontend Header File</title>
     &sub-frontend-h;
   </appendix>
+  <appendix id="net_h">
+    <title>DVB Network Header File</title>
+    &sub-net-h;
+  </appendix>
+  <appendix id="video_h">
+    <title>DVB Video Header File</title>
+    &sub-video-h;
+  </appendix>
 
diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml
new file mode 100644
index 0000000..207e1a5
--- /dev/null
+++ b/Documentation/DocBook/media/dvb/dvbproperty.xml
@@ -0,0 +1,859 @@
+<section id="FE_GET_SET_PROPERTY">
+<title><constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant></title>
+<para>This section describes the DVB version 5 extention of the DVB-API, also
+called "S2API", as this API were added to provide support for DVB-S2. It was
+designed to be able to replace the old frontend API. Yet, the DISEQC and
+the capability ioctls weren't implemented yet via the new way.</para>
+<para>The typical usage for the <constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant>
+API is to replace the ioctl's were the <link linkend="dvb-frontend-parameters">
+struct <constant>dvb_frontend_parameters</constant></link> were used.</para>
+<section id="dtv-property">
+<title>DTV property type</title>
+<programlisting>
+/* Reserved fields should be set to 0 */
+struct dtv_property {
+	__u32 cmd;
+	union {
+		__u32 data;
+		struct {
+			__u8 data[32];
+			__u32 len;
+			__u32 reserved1[3];
+			void *reserved2;
+		} buffer;
+	} u;
+	int result;
+} __attribute__ ((packed));
+
+/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
+#define DTV_IOCTL_MAX_MSGS 64
+</programlisting>
+</section>
+<section id="dtv-properties">
+<title>DTV properties type</title>
+<programlisting>
+struct dtv_properties {
+	__u32 num;
+	struct dtv_property *props;
+};
+</programlisting>
+</section>
+
+<section id="FE_GET_PROPERTY">
+<title>FE_GET_PROPERTY</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call returns one or more frontend properties. This call only
+ requires read-only access to the device.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request = <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>,
+ dtv_properties &#x22C6;props);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int num</para>
+</entry><entry
+ align="char">
+<para>Equals <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link> for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct dtv_property *props</para>
+</entry><entry
+ align="char">
+<para>Points to the location where the front-end property commands are stored.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+<informaltable><tgroup cols="2"><tbody><row>
+  <entry align="char"><para>EOPNOTSUPP</para></entry>
+  <entry align="char"><para>Property type not supported.</para></entry>
+ </row></tbody></tgroup></informaltable>
+</section>
+
+<section id="FE_SET_PROPERTY">
+<title>FE_SET_PROPERTY</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call sets one or more frontend properties. This call only
+ requires read-only access to the device.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request = <link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link>,
+ dtv_properties &#x22C6;props);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int num</para>
+</entry><entry
+ align="char">
+<para>Equals <link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link> for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct dtv_property *props</para>
+</entry><entry
+ align="char">
+<para>Points to the location where the front-end property commands are stored.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+<informaltable><tgroup cols="2"><tbody><row>
+  <entry align="char"><para>EOPNOTSUPP</para></entry>
+  <entry align="char"><para>Property type not supported.</para></entry>
+ </row></tbody></tgroup></informaltable>
+</section>
+
+<section>
+	<title>Property types</title>
+<para>
+On <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>/<link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link>,
+the actual action is determined by the dtv_property cmd/data pairs. With one single ioctl, is possible to
+get/set up to 64 properties. The actual meaning of each property is described on the next sections.
+</para>
+
+<para>The available frontend property types are shown on the next section.</para>
+</section>
+
+<section id="fe_property_parameters">
+	<title>Digital TV property parameters</title>
+	<section id="DTV-UNDEFINED">
+	<title><constant>DTV_UNDEFINED</constant></title>
+	<para>Used internally. A GET/SET operation for it won't change or return anything.</para>
+	</section>
+	<section id="DTV-TUNE">
+	<title><constant>DTV_TUNE</constant></title>
+	<para>Interpret the cache of data, build either a traditional frontend tunerequest so we can pass validation in the <constant>FE_SET_FRONTEND</constant> ioctl.</para>
+	</section>
+	<section id="DTV-CLEAR">
+	<title><constant>DTV_CLEAR</constant></title>
+	<para>Reset a cache of data specific to the frontend here. This does not effect hardware.</para>
+	</section>
+	<section id="DTV-FREQUENCY">
+		<title><constant>DTV_FREQUENCY</constant></title>
+
+		<para>Central frequency of the channel, in HZ.</para>
+
+		<para>Notes:</para>
+		<para>1)For ISDB-T, the channels are usually transmitted with an offset of 143kHz.
+			E.g. a valid frequncy could be 474143 kHz. The stepping is bound to the bandwidth of
+			the channel which is 6MHz.</para>
+
+		<para>2)As in ISDB-Tsb the channel consists of only one or three segments the
+			frequency step is 429kHz, 3*429 respectively. As for ISDB-T the
+			central frequency of the channel is expected.</para>
+	</section>
+	<section id="DTV-MODULATION">
+	<title><constant>DTV_MODULATION</constant></title>
+<para>Specifies the frontend modulation type for cable and satellite types. The modulation can be one of the types bellow:</para>
+<programlisting>
+ typedef enum fe_modulation {
+	QPSK,
+	QAM_16,
+	QAM_32,
+	QAM_64,
+	QAM_128,
+	QAM_256,
+	QAM_AUTO,
+	VSB_8,
+	VSB_16,
+	PSK_8,
+	APSK_16,
+	APSK_32,
+	DQPSK,
+ } fe_modulation_t;
+</programlisting>
+	</section>
+	<section id="DTV-BANDWIDTH-HZ">
+		<title><constant>DTV_BANDWIDTH_HZ</constant></title>
+
+		<para>Bandwidth for the channel, in HZ.</para>
+
+		<para>Possible values:
+			<constant>1712000</constant>,
+			<constant>5000000</constant>,
+			<constant>6000000</constant>,
+			<constant>7000000</constant>,
+			<constant>8000000</constant>,
+			<constant>10000000</constant>.
+		</para>
+
+		<para>Notes:</para>
+
+		<para>1) For ISDB-T it should be always 6000000Hz (6MHz)</para>
+		<para>2) For ISDB-Tsb it can vary depending on the number of connected segments</para>
+		<para>3) Bandwidth doesn't apply for DVB-C transmissions, as the bandwidth
+			 for DVB-C depends on the symbol rate</para>
+		<para>4) Bandwidth in ISDB-T is fixed (6MHz) or can be easily derived from
+			other parameters (DTV_ISDBT_SB_SEGMENT_IDX,
+			DTV_ISDBT_SB_SEGMENT_COUNT).</para>
+		<para>5) DVB-T supports 6, 7 and 8MHz.</para>
+		<para>6) In addition, DVB-T2 supports 1.172, 5 and 10MHz.</para>
+	</section>
+	<section id="DTV-INVERSION">
+	<title><constant>DTV_INVERSION</constant></title>
+	<para>The Inversion field can take one of these values:
+	</para>
+	<programlisting>
+	typedef enum fe_spectral_inversion {
+		INVERSION_OFF,
+		INVERSION_ON,
+		INVERSION_AUTO
+	} fe_spectral_inversion_t;
+	</programlisting>
+	<para>It indicates if spectral inversion should be presumed or not. In the automatic setting
+	(<constant>INVERSION_AUTO</constant>) the hardware will try to figure out the correct setting by
+	itself.
+	</para>
+	</section>
+	<section id="DTV-DISEQC-MASTER">
+	<title><constant>DTV_DISEQC_MASTER</constant></title>
+	<para>Currently not implemented.</para>
+	</section>
+	<section id="DTV-SYMBOL-RATE">
+	<title><constant>DTV_SYMBOL_RATE</constant></title>
+	<para>Digital TV symbol rate, in bauds (symbols/second). Used on cable standards.</para>
+	</section>
+	<section id="DTV-INNER-FEC">
+	<title><constant>DTV_INNER_FEC</constant></title>
+	<para>Used cable/satellite transmissions. The acceptable values are:
+	</para>
+	<programlisting>
+typedef enum fe_code_rate {
+	FEC_NONE = 0,
+	FEC_1_2,
+	FEC_2_3,
+	FEC_3_4,
+	FEC_4_5,
+	FEC_5_6,
+	FEC_6_7,
+	FEC_7_8,
+	FEC_8_9,
+	FEC_AUTO,
+	FEC_3_5,
+	FEC_9_10,
+} fe_code_rate_t;
+	</programlisting>
+	<para>which correspond to error correction rates of 1/2, 2/3, etc.,
+	no error correction or auto detection.</para>
+	</section>
+	<section id="DTV-VOLTAGE">
+	<title><constant>DTV_VOLTAGE</constant></title>
+	<para>The voltage is usually used with non-DiSEqC capable LNBs to switch
+	the polarzation (horizontal/vertical). When using DiSEqC epuipment this
+	voltage has to be switched consistently to the DiSEqC commands as
+	described in the DiSEqC spec.</para>
+	<programlisting>
+		typedef enum fe_sec_voltage {
+		SEC_VOLTAGE_13,
+		SEC_VOLTAGE_18
+		} fe_sec_voltage_t;
+	</programlisting>
+	</section>
+	<section id="DTV-TONE">
+	<title><constant>DTV_TONE</constant></title>
+	<para>Currently not used.</para>
+	</section>
+	<section id="DTV-PILOT">
+	<title><constant>DTV_PILOT</constant></title>
+	<para>Sets DVB-S2 pilot</para>
+	<section id="fe-pilot-t">
+		<title>fe_pilot type</title>
+		<programlisting>
+typedef enum fe_pilot {
+	PILOT_ON,
+	PILOT_OFF,
+	PILOT_AUTO,
+} fe_pilot_t;
+		</programlisting>
+		</section>
+	</section>
+	<section id="DTV-ROLLOFF">
+	<title><constant>DTV_ROLLOFF</constant></title>
+		<para>Sets DVB-S2 rolloff</para>
+
+	<section id="fe-rolloff-t">
+		<title>fe_rolloff type</title>
+		<programlisting>
+typedef enum fe_rolloff {
+	ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
+	ROLLOFF_20,
+	ROLLOFF_25,
+	ROLLOFF_AUTO,
+} fe_rolloff_t;
+		</programlisting>
+		</section>
+	</section>
+	<section id="DTV-DISEQC-SLAVE-REPLY">
+	<title><constant>DTV_DISEQC_SLAVE_REPLY</constant></title>
+	<para>Currently not implemented.</para>
+	</section>
+	<section id="DTV-FE-CAPABILITY-COUNT">
+	<title><constant>DTV_FE_CAPABILITY_COUNT</constant></title>
+	<para>Currently not implemented.</para>
+	</section>
+	<section id="DTV-FE-CAPABILITY">
+	<title><constant>DTV_FE_CAPABILITY</constant></title>
+	<para>Currently not implemented.</para>
+	</section>
+	<section id="DTV-DELIVERY-SYSTEM">
+		<title><constant>DTV_DELIVERY_SYSTEM</constant></title>
+		<para>Specifies the type of Delivery system</para>
+		<section id="fe-delivery-system-t">
+		<title>fe_delivery_system type</title>
+		<para>Possible values: </para>
+<programlisting>
+typedef enum fe_delivery_system {
+	SYS_UNDEFINED,
+	SYS_DVBC_ANNEX_AC,
+	SYS_DVBC_ANNEX_B,
+	SYS_DVBT,
+	SYS_DSS,
+	SYS_DVBS,
+	SYS_DVBS2,
+	SYS_DVBH,
+	SYS_ISDBT,
+	SYS_ISDBS,
+	SYS_ISDBC,
+	SYS_ATSC,
+	SYS_ATSCMH,
+	SYS_DMBTH,
+	SYS_CMMB,
+	SYS_DAB,
+	SYS_DVBT2,
+} fe_delivery_system_t;
+</programlisting>
+		</section>
+	</section>
+	<section id="DTV-ISDBT-PARTIAL-RECEPTION">
+		<title><constant>DTV_ISDBT_PARTIAL_RECEPTION</constant></title>
+
+		<para>If <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '0' this bit-field represents whether
+			the channel is in partial reception mode or not.</para>
+
+		<para>If '1' <constant>DTV_ISDBT_LAYERA_*</constant> values are assigned to the center segment and
+			<constant>DTV_ISDBT_LAYERA_SEGMENT_COUNT</constant> has to be '1'.</para>
+
+		<para>If in addition <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'
+			<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> represents whether this ISDB-Tsb channel
+			is consisting of one segment and layer or three segments and two layers.</para>
+
+		<para>Possible values: 0, 1, -1 (AUTO)</para>
+	</section>
+	<section id="DTV-ISDBT-SOUND-BROADCASTING">
+		<title><constant>DTV_ISDBT_SOUND_BROADCASTING</constant></title>
+
+		<para>This field represents whether the other DTV_ISDBT_*-parameters are
+			referring to an ISDB-T and an ISDB-Tsb channel. (See also
+			<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>).</para>
+
+		<para>Possible values: 0, 1, -1 (AUTO)</para>
+	</section>
+	<section id="DTV-ISDBT-SB-SUBCHANNEL-ID">
+		<title><constant>DTV_ISDBT_SB_SUBCHANNEL_ID</constant></title>
+
+		<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
+
+		<para>(Note of the author: This might not be the correct description of the
+			<constant>SUBCHANNEL-ID</constant> in all details, but it is my understanding of the technical
+			background needed to program a device)</para>
+
+		<para>An ISDB-Tsb channel (1 or 3 segments) can be broadcasted alone or in a
+			set of connected ISDB-Tsb channels. In this set of channels every
+			channel can be received independently. The number of connected
+			ISDB-Tsb segment can vary, e.g. depending on the frequency spectrum
+			bandwidth available.</para>
+
+		<para>Example: Assume 8 ISDB-Tsb connected segments are broadcasted. The
+			broadcaster has several possibilities to put those channels in the
+			air: Assuming a normal 13-segment ISDB-T spectrum he can align the 8
+			segments from position 1-8 to 5-13 or anything in between.</para>
+
+		<para>The underlying layer of segments are subchannels: each segment is
+			consisting of several subchannels with a predefined IDs. A sub-channel
+			is used to help the demodulator to synchronize on the channel.</para>
+
+		<para>An ISDB-T channel is always centered over all sub-channels. As for
+			the example above, in ISDB-Tsb it is no longer as simple as that.</para>
+
+		<para><constant>The DTV_ISDBT_SB_SUBCHANNEL_ID</constant> parameter is used to give the
+			sub-channel ID of the segment to be demodulated.</para>
+
+		<para>Possible values: 0 .. 41, -1 (AUTO)</para>
+	</section>
+	<section id="DTV-ISDBT-SB-SEGMENT-IDX">
+		<title><constant>DTV_ISDBT_SB_SEGMENT_IDX</constant></title>
+		<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
+		<para><constant>DTV_ISDBT_SB_SEGMENT_IDX</constant> gives the index of the segment to be
+			demodulated for an ISDB-Tsb channel where several of them are
+			transmitted in the connected manner.</para>
+		<para>Possible values: 0 .. <constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant> - 1</para>
+		<para>Note: This value cannot be determined by an automatic channel search.</para>
+	</section>
+	<section id="DTV-ISDBT-SB-SEGMENT-COUNT">
+		<title><constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant></title>
+		<para>This field only applies if <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> is '1'.</para>
+		<para><constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant> gives the total count of connected ISDB-Tsb
+			channels.</para>
+		<para>Possible values: 1 .. 13</para>
+		<para>Note: This value cannot be determined by an automatic channel search.</para>
+	</section>
+	<section id="isdb-hierq-layers">
+		<title><constant>DTV-ISDBT-LAYER*</constant> parameters</title>
+		<para>ISDB-T channels can be coded hierarchically. As opposed to DVB-T in
+			ISDB-T hierarchical layers can be decoded simultaneously. For that
+			reason a ISDB-T demodulator has 3 viterbi and 3 reed-solomon-decoders.</para>
+		<para>ISDB-T has 3 hierarchical layers which each can use a part of the
+			available segments. The total number of segments over all layers has
+			to 13 in ISDB-T.</para>
+		<para>There are 3 parameter sets, for Layers A, B and C.</para>
+		<section id="DTV-ISDBT-LAYER-ENABLED">
+			<title><constant>DTV_ISDBT_LAYER_ENABLED</constant></title>
+			<para>Hierarchical reception in ISDB-T is achieved by enabling or disabling
+				layers in the decoding process. Setting all bits of
+				<constant>DTV_ISDBT_LAYER_ENABLED</constant> to '1' forces all layers (if applicable) to be
+				demodulated. This is the default.</para>
+			<para>If the channel is in the partial reception mode
+				(<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> = 1) the central segment can be decoded
+				independently of the other 12 segments. In that mode layer A has to
+				have a <constant>SEGMENT_COUNT</constant> of 1.</para>
+			<para>In ISDB-Tsb only layer A is used, it can be 1 or 3 in ISDB-Tsb
+				according to <constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>. <constant>SEGMENT_COUNT</constant> must be filled
+				accordingly.</para>
+			<para>Possible values: 0x1, 0x2, 0x4 (|-able)</para>
+			<para><constant>DTV_ISDBT_LAYER_ENABLED[0:0]</constant> - layer A</para>
+			<para><constant>DTV_ISDBT_LAYER_ENABLED[1:1]</constant> - layer B</para>
+			<para><constant>DTV_ISDBT_LAYER_ENABLED[2:2]</constant> - layer C</para>
+			<para><constant>DTV_ISDBT_LAYER_ENABLED[31:3]</constant> unused</para>
+		</section>
+		<section id="DTV-ISDBT-LAYER-FEC">
+			<title><constant>DTV_ISDBT_LAYER*_FEC</constant></title>
+			<para>Possible values: <constant>FEC_AUTO</constant>, <constant>FEC_1_2</constant>, <constant>FEC_2_3</constant>, <constant>FEC_3_4</constant>, <constant>FEC_5_6</constant>, <constant>FEC_7_8</constant></para>
+		</section>
+		<section id="DTV-ISDBT-LAYER-MODULATION">
+			<title><constant>DTV_ISDBT_LAYER*_MODULATION</constant></title>
+			<para>Possible values: <constant>QAM_AUTO</constant>, QP<constant>SK, QAM_16</constant>, <constant>QAM_64</constant>, <constant>DQPSK</constant></para>
+			<para>Note: If layer C is <constant>DQPSK</constant> layer B has to be <constant>DQPSK</constant>. If layer B is <constant>DQPSK</constant>
+				and <constant>DTV_ISDBT_PARTIAL_RECEPTION</constant>=0 layer has to be <constant>DQPSK</constant>.</para>
+		</section>
+		<section id="DTV-ISDBT-LAYER-SEGMENT-COUNT">
+			<title><constant>DTV_ISDBT_LAYER*_SEGMENT_COUNT</constant></title>
+			<para>Possible values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1 (AUTO)</para>
+			<para>Note: Truth table for <constant>DTV_ISDBT_SOUND_BROADCASTING</constant> and
+				<constant>DTV_ISDBT_PARTIAL_RECEPTION</constant> and <constant>LAYER</constant>*_SEGMENT_COUNT</para>
+			<informaltable id="isdbt-layer_seg-cnt-table">
+				<tgroup cols="6">
+					<tbody>
+						<row>
+							<entry>PR</entry>
+							<entry>SB</entry>
+							<entry>Layer A width</entry>
+							<entry>Layer B width</entry>
+							<entry>Layer C width</entry>
+							<entry>total width</entry>
+						</row>
+						<row>
+							<entry>0</entry>
+							<entry>0</entry>
+							<entry>1 .. 13</entry>
+							<entry>1 .. 13</entry>
+							<entry>1 .. 13</entry>
+							<entry>13</entry>
+						</row>
+						<row>
+							<entry>1</entry>
+							<entry>0</entry>
+							<entry>1</entry>
+							<entry>1 .. 13</entry>
+							<entry>1 .. 13</entry>
+							<entry>13</entry>
+						</row>
+						<row>
+							<entry>0</entry>
+							<entry>1</entry>
+							<entry>1</entry>
+							<entry>0</entry>
+							<entry>0</entry>
+							<entry>1</entry>
+						</row>
+						<row>
+							<entry>1</entry>
+							<entry>1</entry>
+							<entry>1</entry>
+							<entry>2</entry>
+							<entry>0</entry>
+							<entry>13</entry>
+						</row>
+					</tbody>
+				</tgroup>
+			</informaltable>
+		</section>
+		<section id="DTV-ISDBT-LAYER-TIME-INTERLEAVING">
+			<title><constant>DTV_ISDBT_LAYER*_TIME_INTERLEAVING</constant></title>
+			<para>Possible values: 0, 1, 2, 3, -1 (AUTO)</para>
+			<para>Note: The real inter-leaver depth-names depend on the mode (fft-size); the values
+				here are referring to what can be found in the TMCC-structure -
+				independent of the mode.</para>
+		</section>
+	</section>
+	<section id="DTV-API-VERSION">
+	<title><constant>DTV_API_VERSION</constant></title>
+	<para>Returns the major/minor version of the DVB API</para>
+	</section>
+	<section id="DTV-CODE-RATE-HP">
+	<title><constant>DTV_CODE_RATE_HP</constant></title>
+	<para>Used on terrestrial transmissions. The acceptable values are:
+	</para>
+	<programlisting>
+typedef enum fe_code_rate {
+	FEC_NONE = 0,
+	FEC_1_2,
+	FEC_2_3,
+	FEC_3_4,
+	FEC_4_5,
+	FEC_5_6,
+	FEC_6_7,
+	FEC_7_8,
+	FEC_8_9,
+	FEC_AUTO,
+	FEC_3_5,
+	FEC_9_10,
+} fe_code_rate_t;
+	</programlisting>
+	</section>
+	<section id="DTV-CODE-RATE-LP">
+	<title><constant>DTV_CODE_RATE_LP</constant></title>
+	<para>Used on terrestrial transmissions. The acceptable values are:
+	</para>
+	<programlisting>
+typedef enum fe_code_rate {
+	FEC_NONE = 0,
+	FEC_1_2,
+	FEC_2_3,
+	FEC_3_4,
+	FEC_4_5,
+	FEC_5_6,
+	FEC_6_7,
+	FEC_7_8,
+	FEC_8_9,
+	FEC_AUTO,
+	FEC_3_5,
+	FEC_9_10,
+} fe_code_rate_t;
+	</programlisting>
+	</section>
+	<section id="DTV-GUARD-INTERVAL">
+		<title><constant>DTV_GUARD_INTERVAL</constant></title>
+
+		<para>Possible values are:</para>
+<programlisting>
+typedef enum fe_guard_interval {
+	GUARD_INTERVAL_1_32,
+	GUARD_INTERVAL_1_16,
+	GUARD_INTERVAL_1_8,
+	GUARD_INTERVAL_1_4,
+	GUARD_INTERVAL_AUTO,
+	GUARD_INTERVAL_1_128,
+	GUARD_INTERVAL_19_128,
+	GUARD_INTERVAL_19_256,
+} fe_guard_interval_t;
+</programlisting>
+
+		<para>Notes:</para>
+		<para>1) If <constant>DTV_GUARD_INTERVAL</constant> is set the <constant>GUARD_INTERVAL_AUTO</constant> the hardware will
+			try to find the correct guard interval (if capable) and will use TMCC to fill
+			in the missing parameters.</para>
+		<para>2) Intervals 1/128, 19/128 and 19/256 are used only for DVB-T2 at present</para>
+	</section>
+	<section id="DTV-TRANSMISSION-MODE">
+		<title><constant>DTV_TRANSMISSION_MODE</constant></title>
+
+		<para>Specifies the number of carriers used by the standard</para>
+
+		<para>Possible values are:</para>
+<programlisting>
+typedef enum fe_transmit_mode {
+	TRANSMISSION_MODE_2K,
+	TRANSMISSION_MODE_8K,
+	TRANSMISSION_MODE_AUTO,
+	TRANSMISSION_MODE_4K,
+	TRANSMISSION_MODE_1K,
+	TRANSMISSION_MODE_16K,
+	TRANSMISSION_MODE_32K,
+} fe_transmit_mode_t;
+</programlisting>
+		<para>Notes:</para>
+		<para>1) ISDB-T supports three carrier/symbol-size: 8K, 4K, 2K. It is called
+			'mode' in the standard: Mode 1 is 2K, mode 2 is 4K, mode 3 is 8K</para>
+
+		<para>2) If <constant>DTV_TRANSMISSION_MODE</constant> is set the <constant>TRANSMISSION_MODE_AUTO</constant> the
+			hardware will try to find the correct FFT-size (if capable) and will
+			use TMCC to fill in the missing parameters.</para>
+		<para>3) DVB-T specifies 2K and 8K as valid sizes.</para>
+		<para>4) DVB-T2 specifies 1K, 2K, 4K, 8K, 16K and 32K.</para>
+	</section>
+	<section id="DTV-HIERARCHY">
+	<title><constant>DTV_HIERARCHY</constant></title>
+	<para>Frontend hierarchy</para>
+	<programlisting>
+typedef enum fe_hierarchy {
+	 HIERARCHY_NONE,
+	 HIERARCHY_1,
+	 HIERARCHY_2,
+	 HIERARCHY_4,
+	 HIERARCHY_AUTO
+ } fe_hierarchy_t;
+	</programlisting>
+	</section>
+	<section id="DTV-ISDBS-TS-ID">
+	<title><constant>DTV_ISDBS_TS_ID</constant></title>
+	<para>Currently unused.</para>
+	</section>
+	<section id="DTV-DVBT2-PLP-ID">
+		<title><constant>DTV_DVBT2_PLP_ID</constant></title>
+		<para>DVB-T2 supports Physical Layer Pipes (PLP) to allow transmission of
+			many data types via a single multiplex. The API will soon support this
+			at which point this section will be expanded.</para>
+	</section>
+</section>
+	<section id="frontend-property-terrestrial-systems">
+	<title>Properties used on terrestrial delivery systems</title>
+		<section id="dvbt-params">
+			<title>DVB-T delivery system</title>
+			<para>The following parameters are valid for DVB-T:</para>
+			<itemizedlist mark='opencircle'>
+				<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-BANDWIDTH-HZ"><constant>DTV_BANDWIDTH_HZ</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-CODE-RATE-HP"><constant>DTV_CODE_RATE_HP</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-CODE-RATE-LP"><constant>DTV_CODE_RATE_LP</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-HIERARCHY"><constant>DTV_HIERARCHY</constant></link></para></listitem>
+			</itemizedlist>
+		</section>
+		<section id="dvbt2-params">
+			<title>DVB-T2 delivery system</title>
+			<para>DVB-T2 support is currently in the early stages
+			of development, so expect that this section maygrow and become
+			more detailed with time.</para>
+		<para>The following parameters are valid for DVB-T2:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-BANDWIDTH-HZ"><constant>DTV_BANDWIDTH_HZ</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CODE-RATE-HP"><constant>DTV_CODE_RATE_HP</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CODE-RATE-LP"><constant>DTV_CODE_RATE_LP</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-HIERARCHY"><constant>DTV_HIERARCHY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DVBT2-PLP-ID"><constant>DTV_DVBT2_PLP_ID</constant></link></para></listitem>
+		</itemizedlist>
+		</section>
+		<section id="isdbt">
+		<title>ISDB-T delivery system</title>
+		<para>This ISDB-T/ISDB-Tsb API extension should reflect all information
+			needed to tune any ISDB-T/ISDB-Tsb hardware. Of course it is possible
+			that some very sophisticated devices won't need certain parameters to
+			tune.</para>
+		<para>The information given here should help application writers to know how
+			to handle ISDB-T and ISDB-Tsb hardware using the Linux DVB-API.</para>
+		<para>The details given here about ISDB-T and ISDB-Tsb are just enough to
+			basically show the dependencies between the needed parameter values,
+			but surely some information is left out. For more detailed information
+			see the following documents:</para>
+		<para>ARIB STD-B31 - "Transmission System for Digital Terrestrial
+			Television Broadcasting" and</para>
+		<para>ARIB TR-B14 - "Operational Guidelines for Digital Terrestrial
+			Television Broadcasting".</para>
+		<para>In order to understand the ISDB specific parameters,
+			one has to have some knowledge the channel structure in
+			ISDB-T and ISDB-Tsb. I.e. it has to be known to
+			the reader that an ISDB-T channel consists of 13 segments,
+			that it can have up to 3 layer sharing those segments,
+			and things like that.</para>
+		<para>The following parameters are valid for ISDB-T:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-BANDWIDTH-HZ"><constant>DTV_BANDWIDTH_HZ</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CODE-RATE-HP"><constant>DTV_CODE_RATE_HP</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CODE-RATE-LP"><constant>DTV_CODE_RATE_LP</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-HIERARCHY"><constant>DTV_HIERARCHY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-ENABLED"><constant>DTV_ISDBT_LAYER_ENABLED</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-PARTIAL-RECEPTION"><constant>DTV_ISDBT_PARTIAL_RECEPTION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-SOUND-BROADCASTING"><constant>DTV_ISDBT_SOUND_BROADCASTING</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-SB-SUBCHANNEL-ID"><constant>DTV_ISDBT_SB_SUBCHANNEL_ID</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-SB-SEGMENT-IDX"><constant>DTV_ISDBT_SB_SEGMENT_IDX</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-SB-SEGMENT-COUNT"><constant>DTV_ISDBT_SB_SEGMENT_COUNT</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-FEC"><constant>DTV_ISDBT_LAYERA_FEC</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-MODULATION"><constant>DTV_ISDBT_LAYERA_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-SEGMENT-COUNT"><constant>DTV_ISDBT_LAYERA_SEGMENT_COUNT</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-TIME-INTERLEAVING"><constant>DTV_ISDBT_LAYERA_TIME_INTERLEAVING</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-FEC"><constant>DTV_ISDBT_LAYERB_FEC</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-MODULATION"><constant>DTV_ISDBT_LAYERB_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-SEGMENT-COUNT"><constant>DTV_ISDBT_LAYERB_SEGMENT_COUNT</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-TIME-INTERLEAVING"><constant>DTV_ISDBT_LAYERB_TIME_INTERLEAVING</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-FEC"><constant>DTV_ISDBT_LAYERC_FEC</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-MODULATION"><constant>DTV_ISDBT_LAYERC_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-SEGMENT-COUNT"><constant>DTV_ISDBT_LAYERC_SEGMENT_COUNT</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBT-LAYER-TIME-INTERLEAVING"><constant>DTV_ISDBT_LAYERC_TIME_INTERLEAVING</constant></link></para></listitem>
+		</itemizedlist>
+		</section>
+		<section id="atsc-params">
+			<title>ATSC delivery system</title>
+			<para>The following parameters are valid for ATSC:</para>
+			<itemizedlist mark='opencircle'>
+				<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+				<listitem><para><link linkend="DTV-BANDWIDTH-HZ"><constant>DTV_BANDWIDTH_HZ</constant></link></para></listitem>
+			</itemizedlist>
+		</section>
+	</section>
+	<section id="frontend-property-cable-systems">
+	<title>Properties used on cable delivery systems</title>
+	<section id="dvbc-params">
+		<title>DVB-C delivery system</title>
+		<para>The DVB-C Annex-A/C is the widely used cable standard. Transmission uses QAM modulation.</para>
+		<para>The following parameters are valid for DVB-C Annex A/C:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+		</itemizedlist>
+	</section>
+	<section id="dvbc-annex-b-params">
+		<title>DVB-C Annex B delivery system</title>
+		<para>The DVB-C Annex-B is only used on a few Countries like the United States.</para>
+		<para>The following parameters are valid for DVB-C Annex B:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+		</itemizedlist>
+	</section>
+	</section>
+	<section id="frontend-property-satellital-systems">
+	<title>Properties used on satellital delivery systems</title>
+	<section id="dvbs-params">
+		<title>DVB-S delivery system</title>
+		<para>The following parameters are valid for DVB-S:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+		</itemizedlist>
+		<para>Future implementations might add those two missing parameters:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-DISEQC-MASTER"><constant>DTV_DISEQC_MASTER</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DISEQC-SLAVE-REPLY"><constant>DTV_DISEQC_SLAVE_REPLY</constant></link></para></listitem>
+		</itemizedlist>
+	</section>
+	<section id="dvbs2-params">
+		<title>DVB-S2 delivery system</title>
+		<para>The following parameters are valid for DVB-S2:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-VOLTAGE"><constant>DTV_VOLTAGE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TONE"><constant>DTV_TONE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-PILOT"><constant>DTV_PILOT</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ROLLOFF"><constant>DTV_ROLLOFF</constant></link></para></listitem>
+		</itemizedlist>
+		<para>Future implementations might add those two missing parameters:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-DISEQC-MASTER"><constant>DTV_DISEQC_MASTER</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DISEQC-SLAVE-REPLY"><constant>DTV_DISEQC_SLAVE_REPLY</constant></link></para></listitem>
+		</itemizedlist>
+	</section>
+	<section id="isdbs-params">
+		<title>ISDB-S delivery system</title>
+		<para>The following parameters are valid for ISDB-S:</para>
+		<itemizedlist mark='opencircle'>
+			<listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-VOLTAGE"><constant>DTV_VOLTAGE</constant></link></para></listitem>
+			<listitem><para><link linkend="DTV-ISDBS-TS-ID"><constant>DTV_ISDBS_TS_ID</constant></link></para></listitem>
+		</itemizedlist>
+	</section>
+	</section>
+</section>
diff --git a/Documentation/DocBook/dvb/dvbstb.pdf b/Documentation/DocBook/media/dvb/dvbstb.pdf
similarity index 100%
rename from Documentation/DocBook/dvb/dvbstb.pdf
rename to Documentation/DocBook/media/dvb/dvbstb.pdf
Binary files differ
diff --git a/Documentation/DocBook/dvb/examples.xml b/Documentation/DocBook/media/dvb/examples.xml
similarity index 100%
rename from Documentation/DocBook/dvb/examples.xml
rename to Documentation/DocBook/media/dvb/examples.xml
diff --git a/Documentation/DocBook/dvb/frontend.xml b/Documentation/DocBook/media/dvb/frontend.xml
similarity index 75%
rename from Documentation/DocBook/dvb/frontend.xml
rename to Documentation/DocBook/media/dvb/frontend.xml
index 60c6976..61407ea 100644
--- a/Documentation/DocBook/dvb/frontend.xml
+++ b/Documentation/DocBook/media/dvb/frontend.xml
@@ -20,22 +20,52 @@
 <section id="frontend_types">
 <title>Frontend Data Types</title>
 
-<section id="frontend_type">
-<title>frontend type</title>
+<section id="fe-type-t">
+<title>Frontend type</title>
 
-<para>For historical reasons frontend types are named after the type of modulation used in
-transmission.</para>
-<programlisting>
-	typedef enum fe_type {
-	FE_QPSK,   /&#x22C6; DVB-S &#x22C6;/
-	FE_QAM,    /&#x22C6; DVB-C &#x22C6;/
-	FE_OFDM    /&#x22C6; DVB-T &#x22C6;/
-	} fe_type_t;
-</programlisting>
+<para>For historical reasons, frontend types are named by the type of modulation used in
+transmission. The fontend types are given by fe_type_t type, defined as:</para>
 
+<table pgwide="1" frame="none" id="fe-type">
+<title>Frontend types</title>
+<tgroup cols="3">
+   &cs-def;
+   <thead>
+     <row>
+       <entry>fe_type</entry>
+       <entry>Description</entry>
+       <entry><link linkend="DTV-DELIVERY-SYSTEM">DTV_DELIVERY_SYSTEM</link> equivalent type</entry>
+     </row>
+  </thead>
+  <tbody valign="top">
+  <row>
+     <entry id="FE_QPSK"><constant>FE_QPSK</constant></entry>
+     <entry>For DVB-S standard</entry>
+     <entry><constant>SYS_DVBS</constant></entry>
+  </row>
+  <row>
+     <entry id="FE_QAM"><constant>FE_QAM</constant></entry>
+     <entry>For DVB-C annex A/C standard</entry>
+     <entry><constant>SYS_DVBC_ANNEX_AC</constant></entry>
+  </row>
+  <row>
+     <entry id="FE_OFDM"><constant>FE_OFDM</constant></entry>
+     <entry>For DVB-T standard</entry>
+     <entry><constant>SYS_DVBT</constant></entry>
+  </row>
+  <row>
+     <entry id="FE_ATSC"><constant>FE_ATSC</constant></entry>
+     <entry>For ATSC standard (terrestrial) or for DVB-C Annex B (cable) used in US.</entry>
+     <entry><constant>SYS_ATSC</constant> (terrestrial) or <constant>SYS_DVBC_ANNEX_B</constant> (cable)</entry>
+  </row>
+</tbody></tgroup></table>
+
+<para>Newer formats like DVB-S2, ISDB-T, ISDB-S and DVB-T2 are not described at the above, as they're
+supported via the new <link linkend="FE_GET_SET_PROPERTY">FE_GET_PROPERTY/FE_GET_SET_PROPERTY</link> ioctl's, using the <link linkend="DTV-DELIVERY-SYSTEM">DTV_DELIVERY_SYSTEM</link> parameter.
+</para>
 </section>
 
-<section id="frontend_caps">
+<section id="fe-caps-t">
 <title>frontend capabilities</title>
 
 <para>Capabilities describe what a frontend can do. Some capabilities can only be supported for
@@ -76,7 +106,7 @@
 </programlisting>
 </section>
 
-<section id="frontend_info">
+<section id="dvb-frontend-info">
 <title>frontend information</title>
 
 <para>Information about the frontend ca be queried with
@@ -99,7 +129,7 @@
 </programlisting>
 </section>
 
-<section id="frontend_diseqc">
+<section id="dvb-diseqc-master-cmd">
 <title>diseqc master command</title>
 
 <para>A message sent from the frontend to DiSEqC capable equipment.</para>
@@ -110,7 +140,7 @@
 	};
 </programlisting>
 </section>
-<section role="subsection">
+<section role="subsection" id="dvb-diseqc-slave-reply">
 <title>diseqc slave reply</title>
 
 <para>A reply to the frontend from DiSEqC 2.0 capable equipment.</para>
@@ -123,7 +153,7 @@
 </programlisting>
 </section>
 
-<section id="frontend_diseqc_slave_reply">
+<section id="fe-sec-voltage-t">
 <title>diseqc slave reply</title>
 <para>The voltage is usually used with non-DiSEqC capable LNBs to switch the polarzation
 (horizontal/vertical). When using DiSEqC epuipment this voltage has to be switched
@@ -136,7 +166,7 @@
 </programlisting>
 </section>
 
-<section id="frontend_sec_tone">
+<section id="fe-sec-tone-mode-t">
 <title>SEC continuous tone</title>
 
 <para>The continuous 22KHz tone is usually used with non-DiSEqC capable LNBs to switch the
@@ -151,7 +181,7 @@
 </programlisting>
 </section>
 
-<section id="frontend_sec_burst">
+<section id="fe-sec-mini-cmd-t">
 <title>SEC tone burst</title>
 
 <para>The 22KHz tone burst is usually used with non-DiSEqC capable switches to select
@@ -168,7 +198,7 @@
 <para></para>
 </section>
 
-<section id="frontend_status">
+<section id="fe-status-t">
 <title>frontend status</title>
 <para>Several functions of the frontend device use the fe_status data type defined
 by</para>
@@ -188,31 +218,54 @@
 
 </section>
 
-<section id="frontend_params">
+<section id="dvb-frontend-parameters">
 <title>frontend parameters</title>
 <para>The kind of parameters passed to the frontend device for tuning depend on
-the kind of hardware you are using. All kinds of parameters are combined as an
-union in the FrontendParameters structure:</para>
+the kind of hardware you are using.</para>
+<para>The struct <constant>dvb_frontend_parameters</constant> uses an
+union with specific per-system parameters. However, as newer delivery systems
+required more data, the structure size weren't enough to fit, and just
+extending its size would break the existing applications. So, those parameters
+were replaced by the usage of <link linkend="FE_GET_SET_PROPERTY">
+<constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant></link> ioctl's. The
+new API is flexible enough to add new parameters to existing delivery systems,
+and to add newer delivery systems.</para>
+<para>So, newer applications should use <link linkend="FE_GET_SET_PROPERTY">
+<constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant></link> instead, in
+order to be able to support the newer System Delivery like  DVB-S2, DVB-T2,
+DVB-C2, ISDB, etc.</para>
+<para>All kinds of parameters are combined as an union in the FrontendParameters structure:</para>
 <programlisting>
- struct dvb_frontend_parameters {
-	 uint32_t frequency;       /&#x22C6; (absolute) frequency in Hz for QAM/OFDM &#x22C6;/
-				   /&#x22C6; intermediate frequency in kHz for QPSK &#x22C6;/
-	 fe_spectral_inversion_t inversion;
-	 union {
-		 struct dvb_qpsk_parameters qpsk;
-		 struct dvb_qam_parameters  qam;
-		 struct dvb_ofdm_parameters ofdm;
-	 } u;
- };
+struct dvb_frontend_parameters {
+	uint32_t frequency;     /&#x22C6; (absolute) frequency in Hz for QAM/OFDM &#x22C6;/
+				/&#x22C6; intermediate frequency in kHz for QPSK &#x22C6;/
+	fe_spectral_inversion_t inversion;
+	union {
+		struct dvb_qpsk_parameters qpsk;
+		struct dvb_qam_parameters  qam;
+		struct dvb_ofdm_parameters ofdm;
+		struct dvb_vsb_parameters  vsb;
+	} u;
+};
 </programlisting>
-<para>For satellite QPSK frontends you have to use the <constant>QPSKParameters</constant> member defined by</para>
+<para>In the case of QPSK frontends the <constant>frequency</constant> field specifies the intermediate
+frequency, i.e. the offset which is effectively added to the local oscillator frequency (LOF) of
+the LNB. The intermediate frequency has to be specified in units of kHz. For QAM and
+OFDM frontends the <constant>frequency</constant> specifies the absolute frequency and is given in Hz.
+</para>
+<section id="dvb-qpsk-parameters">
+<title>QPSK parameters</title>
+<para>For satellite QPSK frontends you have to use the <constant>dvb_qpsk_parameters</constant> structure:</para>
 <programlisting>
  struct dvb_qpsk_parameters {
 	 uint32_t        symbol_rate;  /&#x22C6; symbol rate in Symbols per second &#x22C6;/
 	 fe_code_rate_t  fec_inner;    /&#x22C6; forward error correction (see above) &#x22C6;/
  };
 </programlisting>
-<para>for cable QAM frontend you use the <constant>QAMParameters</constant> structure</para>
+</section>
+<section id="dvb-qam-parameters">
+<title>QAM parameters</title>
+<para>for cable QAM frontend you use the <constant>dvb_qam_parameters</constant> structure:</para>
 <programlisting>
  struct dvb_qam_parameters {
 	 uint32_t         symbol_rate; /&#x22C6; symbol rate in Symbols per second &#x22C6;/
@@ -220,8 +273,19 @@
 	 fe_modulation_t  modulation;  /&#x22C6; modulation type (see above) &#x22C6;/
  };
 </programlisting>
-<para>DVB-T frontends are supported by the <constant>OFDMParamters</constant> structure
-</para>
+</section>
+<section id="dvb-vsb-parameters">
+<title>VSB parameters</title>
+<para>ATSC frontends are supported by the <constant>dvb_vsb_parameters</constant> structure:</para>
+<programlisting>
+struct dvb_vsb_parameters {
+	fe_modulation_t modulation;	/&#x22C6; modulation type (see above) &#x22C6;/
+};
+</programlisting>
+</section>
+<section id="dvb-ofdm-parameters">
+<title>OFDM parameters</title>
+<para>DVB-T frontends are supported by the <constant>dvb_ofdm_parameters</constant> structure:</para>
 <programlisting>
  struct dvb_ofdm_parameters {
 	 fe_bandwidth_t      bandwidth;
@@ -233,86 +297,124 @@
 	 fe_hierarchy_t      hierarchy_information;
  };
 </programlisting>
-<para>In the case of QPSK frontends the <constant>Frequency</constant> field specifies the intermediate
-frequency, i.e. the offset which is effectively added to the local oscillator frequency (LOF) of
-the LNB. The intermediate frequency has to be specified in units of kHz. For QAM and
-OFDM frontends the Frequency specifies the absolute frequency and is given in
-Hz.
-</para>
+</section>
+<section id="fe-spectral-inversion-t">
+<title>frontend spectral inversion</title>
 <para>The Inversion field can take one of these values:
 </para>
 <programlisting>
- typedef enum fe_spectral_inversion {
-	 INVERSION_OFF,
-	 INVERSION_ON,
-	 INVERSION_AUTO
- } fe_spectral_inversion_t;
+typedef enum fe_spectral_inversion {
+	INVERSION_OFF,
+	INVERSION_ON,
+	INVERSION_AUTO
+} fe_spectral_inversion_t;
 </programlisting>
 <para>It indicates if spectral inversion should be presumed or not. In the automatic setting
 (<constant>INVERSION_AUTO</constant>) the hardware will try to figure out the correct setting by
 itself.
 </para>
-<para>The possible values for the <constant>FEC_inner</constant> field are
+</section>
+<section id="fe-code-rate-t">
+<title>frontend code rate</title>
+<para>The possible values for the <constant>fec_inner</constant> field used on
+<link refend="dvb-qpsk-parameters"><constant>struct dvb_qpsk_parameters</constant></link> and
+<link refend="dvb-qam-parameters"><constant>struct dvb_qam_parameters</constant></link> are:
 </para>
 <programlisting>
- typedef enum fe_code_rate {
-	 FEC_NONE = 0,
-	 FEC_1_2,
-	 FEC_2_3,
-	 FEC_3_4,
-	 FEC_4_5,
-	 FEC_5_6,
-	 FEC_6_7,
-	 FEC_7_8,
-	 FEC_8_9,
-	 FEC_AUTO
- } fe_code_rate_t;
+typedef enum fe_code_rate {
+	FEC_NONE = 0,
+	FEC_1_2,
+	FEC_2_3,
+	FEC_3_4,
+	FEC_4_5,
+	FEC_5_6,
+	FEC_6_7,
+	FEC_7_8,
+	FEC_8_9,
+	FEC_AUTO,
+	FEC_3_5,
+	FEC_9_10,
+} fe_code_rate_t;
 </programlisting>
 <para>which correspond to error correction rates of 1/2, 2/3, etc., no error correction or auto
 detection.
 </para>
-<para>For cable and terrestrial frontends (QAM and OFDM) one also has to specify the quadrature
-modulation mode which can be one of the following:
+</section>
+<section id="fe-modulation-t">
+<title>frontend modulation type for QAM, OFDM and VSB</title>
+<para>For cable and terrestrial frontends, e. g. for
+<link refend="dvb-qam-parameters"><constant>struct dvb_qpsk_parameters</constant></link>,
+<link refend="dvb-ofdm-parameters"><constant>struct dvb_qam_parameters</constant></link> and
+<link refend="dvb-vsb-parameters"><constant>struct dvb_qam_parameters</constant></link>,
+it needs to specify the quadrature modulation mode which can be one of the following:
 </para>
 <programlisting>
  typedef enum fe_modulation {
- QPSK,
-	 QAM_16,
-	 QAM_32,
-	 QAM_64,
-	 QAM_128,
-	 QAM_256,
-	 QAM_AUTO
+	QPSK,
+	QAM_16,
+	QAM_32,
+	QAM_64,
+	QAM_128,
+	QAM_256,
+	QAM_AUTO,
+	VSB_8,
+	VSB_16,
+	PSK_8,
+	APSK_16,
+	APSK_32,
+	DQPSK,
  } fe_modulation_t;
 </programlisting>
+</section>
 <para>Finally, there are several more parameters for OFDM:
 </para>
+<section id="fe-transmit-mode-t">
+<title>Number of carriers per channel</title>
 <programlisting>
- typedef enum fe_transmit_mode {
-	 TRANSMISSION_MODE_2K,
-	 TRANSMISSION_MODE_8K,
-	 TRANSMISSION_MODE_AUTO
+typedef enum fe_transmit_mode {
+	TRANSMISSION_MODE_2K,
+	TRANSMISSION_MODE_8K,
+	TRANSMISSION_MODE_AUTO,
+	TRANSMISSION_MODE_4K,
+	TRANSMISSION_MODE_1K,
+	TRANSMISSION_MODE_16K,
+	TRANSMISSION_MODE_32K,
  } fe_transmit_mode_t;
 </programlisting>
- <programlisting>
- typedef enum fe_bandwidth {
-	 BANDWIDTH_8_MHZ,
-	 BANDWIDTH_7_MHZ,
-	 BANDWIDTH_6_MHZ,
-	 BANDWIDTH_AUTO
- } fe_bandwidth_t;
+</section>
+<section id="fe-bandwidth-t">
+<title>frontend bandwidth</title>
+<programlisting>
+typedef enum fe_bandwidth {
+	BANDWIDTH_8_MHZ,
+	BANDWIDTH_7_MHZ,
+	BANDWIDTH_6_MHZ,
+	BANDWIDTH_AUTO,
+	BANDWIDTH_5_MHZ,
+	BANDWIDTH_10_MHZ,
+	BANDWIDTH_1_712_MHZ,
+} fe_bandwidth_t;
 </programlisting>
- <programlisting>
- typedef enum fe_guard_interval {
-	 GUARD_INTERVAL_1_32,
-	 GUARD_INTERVAL_1_16,
-	 GUARD_INTERVAL_1_8,
-	 GUARD_INTERVAL_1_4,
-	 GUARD_INTERVAL_AUTO
- } fe_guard_interval_t;
+</section>
+<section id="fe-guard-interval-t">
+<title>frontend guard inverval</title>
+<programlisting>
+typedef enum fe_guard_interval {
+	GUARD_INTERVAL_1_32,
+	GUARD_INTERVAL_1_16,
+	GUARD_INTERVAL_1_8,
+	GUARD_INTERVAL_1_4,
+	GUARD_INTERVAL_AUTO,
+	GUARD_INTERVAL_1_128,
+	GUARD_INTERVAL_19_128,
+	GUARD_INTERVAL_19_256,
+} fe_guard_interval_t;
 </programlisting>
- <programlisting>
- typedef enum fe_hierarchy {
+</section>
+<section id="fe-hierarchy-t">
+<title>frontend hierarchy</title>
+<programlisting>
+typedef enum fe_hierarchy {
 	 HIERARCHY_NONE,
 	 HIERARCHY_1,
 	 HIERARCHY_2,
@@ -320,10 +422,11 @@
 	 HIERARCHY_AUTO
  } fe_hierarchy_t;
 </programlisting>
+</section>
 
 </section>
 
-<section id="frontend_events">
+<section id="dvb-frontend-event">
 <title>frontend events</title>
  <programlisting>
  struct dvb_frontend_event {
@@ -412,8 +515,7 @@
 <para>(blocking mode is the default)</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>ENODEV</para>
@@ -473,8 +575,7 @@
 <para>File descriptor returned by a previous call to open().</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
@@ -530,8 +631,7 @@
  to be stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
@@ -592,38 +692,8 @@
 <para>The bit error rate is stored into *ber.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>ber points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSIGNAL</para>
-</entry><entry
- align="char">
-<para>There is no signal, thus no meaningful bit error rate. Also
- returned if the front-end is not turned on.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSYS</para>
-</entry><entry
- align="char">
-<para>Function not available for this device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+
+&return-value-dvb;
 </section>
 
 <section id="FE_READ_SNR">
@@ -670,38 +740,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>snr points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSIGNAL</para>
-</entry><entry
- align="char">
-<para>There is no signal, thus no meaningful signal strength
- value. Also returned if front-end is not turned on.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSYS</para>
-</entry><entry
- align="char">
-<para>Function not available for this device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_READ_SIGNAL_STRENGTH">
@@ -748,38 +787,8 @@
 <para>The signal strength value is stored into *strength.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>status points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSIGNAL</para>
-</entry><entry
- align="char">
-<para>There is no signal, thus no meaningful signal strength
- value. Also returned if front-end is not turned on.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSYS</para>
-</entry><entry
- align="char">
-<para>Function not available for this device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+
+&return-value-dvb;
 </section>
 
 <section id="FE_READ_UNCORRECTED_BLOCKS">
@@ -833,30 +842,8 @@
  so far.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>ublocks points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>ENOSYS</para>
-</entry><entry
- align="char">
-<para>Function not available for this device.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+
+&return-value-dvb;
 </section>
 
 <section id="FE_SET_FRONTEND">
@@ -909,24 +896,10 @@
 <para>Points to parameters for tuning operation.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>p points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -982,25 +955,9 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>p points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1084,24 +1041,9 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>ev points to invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EWOULDBLOCK</para>
 </entry><entry
  align="char">
@@ -1113,11 +1055,6 @@
 <para>EOVERFLOW</para>
 </entry><entry
  align="char">
-</entry>
- </row><row><entry
- align="char">
-</entry><entry
- align="char">
 <para>Overflow in event queue - one or more events were lost.</para>
 </entry>
 </row></tbody></tgroup></informaltable>
@@ -1170,23 +1107,7 @@
  to be stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>info points to invalid address.</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_DISEQC_RESET_OVERLOAD">
@@ -1229,30 +1150,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>Permission denied (needs read/write access).</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_DISEQC_SEND_MASTER_CMD">
@@ -1302,45 +1200,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>Seq points to an invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>The data structure referred to by seq is invalid in some
- way.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>Permission denied (needs read/write access).</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_DISEQC_RECV_SLAVE_REPLY">
@@ -1390,45 +1250,7 @@
 <para>Pointer to the command to be received.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>Seq points to an invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>The data structure referred to by seq is invalid in some
- way.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>Permission denied (needs read/write access).</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_DISEQC_SEND_BURST">
@@ -1476,45 +1298,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>Seq points to an invalid address.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>The data structure referred to by seq is invalid in some
- way.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>Permission denied (needs read/write access).</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_SET_TONE">
@@ -1560,44 +1344,7 @@
 <para>The requested tone generation mode (on/off).</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>ENODEV</para>
-</entry><entry
- align="char">
-<para>Device driver not loaded/available.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EBUSY</para>
-</entry><entry
- align="char">
-<para>Device or resource busy.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid argument.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>File not opened with read permissions.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_SET_VOLTAGE">
@@ -1645,44 +1392,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>ENODEV</para>
-</entry><entry
- align="char">
-<para>Device driver not loaded/available.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EBUSY</para>
-</entry><entry
- align="char">
-<para>Device or resource busy.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid argument.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>File not opened with read permissions.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_ENABLE_HIGH_LNB_VOLTAGE">
@@ -1731,44 +1441,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>ENODEV</para>
-</entry><entry
- align="char">
-<para>Device driver not loaded/available.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EBUSY</para>
-</entry><entry
- align="char">
-<para>Device or resource busy.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Invalid argument.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EPERM</para>
-</entry><entry
- align="char">
-<para>File not opened with read permissions.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error in the device driver.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_SET_FRONTEND_TUNE_MODE">
@@ -1800,11 +1473,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS</para>
-<informaltable><tgroup cols="2"><tbody><row>
-<entry align="char"><para>EINVAL</para></entry>
-<entry align="char"><para>Invalid argument.</para></entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 <section id="FE_DISHNETWORK_SEND_LEGACY_CMD">
@@ -1838,12 +1507,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-<para>ERRORS</para>
-<informaltable><tgroup cols="1"><tbody><row>
-<entry align="char">
-	<para>There are no errors in use for this call</para>
-</entry>
-</row></tbody></tgroup></informaltable>
+&return-value-dvb;
 </section>
 
 </section>
diff --git a/Documentation/DocBook/dvb/intro.xml b/Documentation/DocBook/media/dvb/intro.xml
similarity index 92%
rename from Documentation/DocBook/dvb/intro.xml
rename to Documentation/DocBook/media/dvb/intro.xml
index 0dc83f6..c75dc7c 100644
--- a/Documentation/DocBook/dvb/intro.xml
+++ b/Documentation/DocBook/media/dvb/intro.xml
@@ -154,6 +154,10 @@
 </listitem>
  <listitem>
 
+<para><emphasis role="tt">/dev/dvb/adapterN/dvrM</emphasis>,</para>
+</listitem>
+ <listitem>
+
 <para><emphasis role="tt">/dev/dvb/adapterN/caM</emphasis>,</para></listitem></itemizedlist>
 
 <para>where N enumerates the DVB PCI cards in a system starting
@@ -175,10 +179,27 @@
 The DVB API include files should be included in application sources with
 a partial path like:</para>
 
-
+<programlisting>
+	#include &#x003C;linux/dvb/audio.h&#x003E;
+</programlisting>
+<programlisting>
+	#include &#x003C;linux/dvb/ca.h&#x003E;
+</programlisting>
+<programlisting>
+	#include &#x003C;linux/dvb/dmx.h&#x003E;
+</programlisting>
 <programlisting>
 	#include &#x003C;linux/dvb/frontend.h&#x003E;
 </programlisting>
+<programlisting>
+	#include &#x003C;linux/dvb/net.h&#x003E;
+</programlisting>
+<programlisting>
+	#include &#x003C;linux/dvb/osd.h&#x003E;
+</programlisting>
+<programlisting>
+	#include &#x003C;linux/dvb/video.h&#x003E;
+</programlisting>
 
 <para>To enable applications to support different API version, an
 additional include file <emphasis
diff --git a/Documentation/DocBook/dvb/kdapi.xml b/Documentation/DocBook/media/dvb/kdapi.xml
similarity index 100%
rename from Documentation/DocBook/dvb/kdapi.xml
rename to Documentation/DocBook/media/dvb/kdapi.xml
diff --git a/Documentation/DocBook/media/dvb/net.xml b/Documentation/DocBook/media/dvb/net.xml
new file mode 100644
index 0000000..67d37e5
--- /dev/null
+++ b/Documentation/DocBook/media/dvb/net.xml
@@ -0,0 +1,29 @@
+<title>DVB Network API</title>
+<para>The DVB net device enables feeding of MPE (multi protocol encapsulation) packets
+received via DVB into the Linux network protocol stack, e.g. for internet via satellite
+applications. It can be accessed through <emphasis role="tt">/dev/dvb/adapter0/net0</emphasis>. Data types and
+and ioctl definitions can be accessed by including <emphasis role="tt">linux/dvb/net.h</emphasis> in your
+application.
+</para>
+<section id="dvb_net_types">
+<title>DVB Net Data Types</title>
+
+<section id="dvb-net-if">
+<title>struct dvb_net_if</title>
+<programlisting>
+struct dvb_net_if {
+	__u16 pid;
+	__u16 if_num;
+	__u8  feedtype;
+#define DVB_NET_FEEDTYPE_MPE 0	/&#x22C6; multi protocol encapsulation &#x22C6;/
+#define DVB_NET_FEEDTYPE_ULE 1	/&#x22C6; ultra lightweight encapsulation &#x22C6;/
+};
+</programlisting>
+</section>
+
+</section>
+<section id="net_fcalls">
+<title>DVB net Function Calls</title>
+<para>To be written&#x2026;
+</para>
+</section>
diff --git a/Documentation/DocBook/dvb/video.xml b/Documentation/DocBook/media/dvb/video.xml
similarity index 80%
rename from Documentation/DocBook/dvb/video.xml
rename to Documentation/DocBook/media/dvb/video.xml
index 7bb287e..25fb823 100644
--- a/Documentation/DocBook/dvb/video.xml
+++ b/Documentation/DocBook/media/dvb/video.xml
@@ -18,15 +18,16 @@
 <section id="video_types">
 <title>Video Data Types</title>
 
-<section id="video_format_t">
+<section id="video-format-t">
 <title>video_format_t</title>
 <para>The <emphasis role="tt">video_format_t</emphasis> data type defined by
 </para>
 <programlisting>
- typedef enum {
-	 VIDEO_FORMAT_4_3,
-	 VIDEO_FORMAT_16_9
- } video_format_t;
+typedef enum {
+	VIDEO_FORMAT_4_3,     /&#x22C6; Select 4:3 format &#x22C6;/
+	VIDEO_FORMAT_16_9,    /&#x22C6; Select 16:9 format. &#x22C6;/
+	VIDEO_FORMAT_221_1    /&#x22C6; 2.21:1 &#x22C6;/
+} video_format_t;
 </programlisting>
 <para>is used in the VIDEO_SET_FORMAT function (??) to tell the driver which aspect ratio
 the output hardware (e.g. TV) has. It is also used in the data structures video_status
@@ -36,34 +37,36 @@
 </para>
 </section>
 
-<section id="video_display_format_t">
-<title>video_display_format_t</title>
+<section id="video-displayformat-t">
+<title>video_displayformat_t</title>
 <para>In case the display format of the video stream and of the display hardware differ the
 application has to specify how to handle the cropping of the picture. This can be done using
 the VIDEO_SET_DISPLAY_FORMAT call (??) which accepts
 </para>
 <programlisting>
- typedef enum {
-	 VIDEO_PAN_SCAN,
-	 VIDEO_LETTER_BOX,
-	 VIDEO_CENTER_CUT_OUT
- } video_display_format_t;
+typedef enum {
+	VIDEO_PAN_SCAN,       /&#x22C6; use pan and scan format &#x22C6;/
+	VIDEO_LETTER_BOX,     /&#x22C6; use letterbox format &#x22C6;/
+	VIDEO_CENTER_CUT_OUT  /&#x22C6; use center cut out format &#x22C6;/
+} video_displayformat_t;
 </programlisting>
 <para>as argument.
 </para>
 </section>
 
-<section id="video_stream_source">
+<section id="video-stream-source-t">
 <title>video stream source</title>
 <para>The video stream source is set through the VIDEO_SELECT_SOURCE call and can take
 the following values, depending on whether we are replaying from an internal (demuxer) or
 external (user write) source.
 </para>
 <programlisting>
- typedef enum {
-	 VIDEO_SOURCE_DEMUX,
-	 VIDEO_SOURCE_MEMORY
- } video_stream_source_t;
+typedef enum {
+	VIDEO_SOURCE_DEMUX, /&#x22C6; Select the demux as the main source &#x22C6;/
+	VIDEO_SOURCE_MEMORY /&#x22C6; If this source is selected, the stream
+			       comes from the user through the write
+			       system call &#x22C6;/
+} video_stream_source_t;
 </programlisting>
 <para>VIDEO_SOURCE_DEMUX selects the demultiplexer (fed either by the frontend or the
 DVR device) as the source of the video stream. If VIDEO_SOURCE_MEMORY
@@ -72,49 +75,98 @@
 </para>
 </section>
 
-<section id="video_play_state">
+<section id="video-play-state-t">
 <title>video play state</title>
 <para>The following values can be returned by the VIDEO_GET_STATUS call representing the
 state of video playback.
 </para>
 <programlisting>
- typedef enum {
-	 VIDEO_STOPPED,
-	 VIDEO_PLAYING,
-	 VIDEO_FREEZED
- } video_play_state_t;
+typedef enum {
+	VIDEO_STOPPED, /&#x22C6; Video is stopped &#x22C6;/
+	VIDEO_PLAYING, /&#x22C6; Video is currently playing &#x22C6;/
+	VIDEO_FREEZED  /&#x22C6; Video is freezed &#x22C6;/
+} video_play_state_t;
 </programlisting>
 </section>
 
-<section id="video_event">
+<section id="video-command">
+<para>The structure must be zeroed before use by the application
+This ensures it can be extended safely in the future.</para>
+<title>struct video-command</title>
+<programlisting>
+struct video_command {
+	__u32 cmd;
+	__u32 flags;
+	union {
+		struct {
+			__u64 pts;
+		} stop;
+
+		struct {
+			/&#x22C6; 0 or 1000 specifies normal speed,
+			   1 specifies forward single stepping,
+			   -1 specifies backward single stepping,
+			   &gt;>1: playback at speed/1000 of the normal speed,
+			   &lt;-1: reverse playback at (-speed/1000) of the normal speed. &#x22C6;/
+			__s32 speed;
+			__u32 format;
+		} play;
+
+		struct {
+			__u32 data[16];
+		} raw;
+	};
+};
+</programlisting>
+</section>
+
+<section id="video-size-t">
+<title>struct video_size-t</title>
+<programlisting>
+typedef struct {
+	int w;
+	int h;
+	video_format_t aspect_ratio;
+} video_size_t;
+</programlisting>
+</section>
+
+
+<section id="video-event">
 <title>struct video_event</title>
 <para>The following is the structure of a video event as it is returned by the VIDEO_GET_EVENT
 call.
 </para>
 <programlisting>
- struct video_event {
-	 int32_t type;
-	 time_t timestamp;
-	 union {
-		 video_format_t video_format;
-	 } u;
- };
+struct video_event {
+	__s32 type;
+#define VIDEO_EVENT_SIZE_CHANGED	1
+#define VIDEO_EVENT_FRAME_RATE_CHANGED	2
+#define VIDEO_EVENT_DECODER_STOPPED 	3
+#define VIDEO_EVENT_VSYNC 		4
+	__kernel_time_t timestamp;
+	union {
+		video_size_t size;
+		unsigned int frame_rate;	/&#x22C6; in frames per 1000sec &#x22C6;/
+		unsigned char vsync_field;	/&#x22C6; unknown/odd/even/progressive &#x22C6;/
+	} u;
+};
 </programlisting>
 </section>
 
-<section id="video_status">
+<section id="video-status">
 <title>struct video_status</title>
 <para>The VIDEO_GET_STATUS call returns the following structure informing about various
 states of the playback operation.
 </para>
 <programlisting>
- struct video_status {
-	 boolean video_blank;
-	 video_play_state_t play_state;
-	 video_stream_source_t stream_source;
-	 video_format_t video_format;
-	 video_displayformat_t display_format;
- };
+struct video_status {
+	int                   video_blank;   /&#x22C6; blank video on freeze? &#x22C6;/
+	video_play_state_t    play_state;    /&#x22C6; current state of playback &#x22C6;/
+	video_stream_source_t stream_source; /&#x22C6; current source (demux/memory) &#x22C6;/
+	video_format_t        video_format;  /&#x22C6; current aspect ratio of stream &#x22C6;/
+	video_displayformat_t display_format;/&#x22C6; selected cropping mode &#x22C6;/
+};
 </programlisting>
 <para>If video_blank is set video will be blanked out if the channel is changed or if playback is
 stopped. Otherwise, the last picture will be displayed. play_state indicates if the video is
@@ -127,17 +179,17 @@
 </para>
 </section>
 
-<section id="video_still_picture">
+<section id="video-still-picture">
 <title>struct video_still_picture</title>
 <para>An I-frame displayed via the VIDEO_STILLPICTURE call is passed on within the
 following structure.
 </para>
 <programlisting>
- /&#x22C6; pointer to and size of a single iframe in memory &#x22C6;/
- struct video_still_picture {
-	 char &#x22C6;iFrame;
-	 int32_t size;
- };
+/&#x22C6; pointer to and size of a single iframe in memory &#x22C6;/
+struct video_still_picture {
+	char &#x22C6;iFrame;        /&#x22C6; pointer to a single iframe in memory &#x22C6;/
+	int32_t size;
+};
 </programlisting>
 </section>
 
@@ -164,26 +216,26 @@
 </programlisting>
 </section>
 
-<section id="video_system">
+<section id="video-system">
 <title>video system</title>
 <para>A call to VIDEO_SET_SYSTEM sets the desired video system for TV output. The
 following system types can be set:
 </para>
 <programlisting>
- typedef enum {
-	  VIDEO_SYSTEM_PAL,
-	  VIDEO_SYSTEM_NTSC,
-	  VIDEO_SYSTEM_PALN,
-	  VIDEO_SYSTEM_PALNc,
-	  VIDEO_SYSTEM_PALM,
-	  VIDEO_SYSTEM_NTSC60,
-	  VIDEO_SYSTEM_PAL60,
-	  VIDEO_SYSTEM_PALM60
- } video_system_t;
+typedef enum {
+	 VIDEO_SYSTEM_PAL,
+	 VIDEO_SYSTEM_NTSC,
+	 VIDEO_SYSTEM_PALN,
+	 VIDEO_SYSTEM_PALNc,
+	 VIDEO_SYSTEM_PALM,
+	 VIDEO_SYSTEM_NTSC60,
+	 VIDEO_SYSTEM_PAL60,
+	 VIDEO_SYSTEM_PALM60
+} video_system_t;
 </programlisting>
 </section>
 
-<section id="video_highlight">
+<section id="video-highlight">
 <title>struct video_highlight</title>
 <para>Calling the ioctl VIDEO_SET_HIGHLIGHTS posts the SPU highlight information. The
 call expects the following format for that information:
@@ -210,7 +262,7 @@
 </programlisting>
 
 </section>
-<section id="video_spu">
+<section id="video-spu">
 <title>video SPU</title>
 <para>Calling VIDEO_SET_SPU deactivates or activates SPU decoding, according to the
 following format:
@@ -224,7 +276,7 @@
 </programlisting>
 
 </section>
-<section id="video_spu_palette">
+<section id="video-spu-palette">
 <title>video SPU palette</title>
 <para>The following structure is used to set the SPU palette by calling VIDEO_SPU_PALETTE:
 </para>
@@ -237,7 +289,7 @@
 </programlisting>
 
 </section>
-<section id="video_navi_pack">
+<section id="video-navi-pack">
 <title>video NAVI pack</title>
 <para>In order to get the navigational data the following structure has to be passed to the ioctl
 VIDEO_GET_NAVI:
@@ -252,7 +304,7 @@
 </section>
 
 
-<section id="video_attributes">
+<section id="video-attributes-t">
 <title>video attributes</title>
 <para>The following attributes can be set by a call to VIDEO_SET_ATTRIBUTES:
 </para>
@@ -347,8 +399,7 @@
 <para>(blocking mode is the default)</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>ENODEV</para>
@@ -406,8 +457,7 @@
 <para>File descriptor returned by a previous call to open().</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EBADF</para>
@@ -462,8 +512,7 @@
 <para>Size of buf.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+<para>RETURN VALUE</para>
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
 <para>EPERM</para>
@@ -488,7 +537,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_STOP"
 role="subsection"><title>VIDEO_STOP</title>
 <para>DESCRIPTION
 </para>
@@ -543,26 +592,9 @@
 <para>FALSE: Show last decoded frame.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_PLAY"
 role="subsection"><title>VIDEO_PLAY</title>
 <para>DESCRIPTION
 </para>
@@ -596,26 +628,9 @@
 <para>Equals VIDEO_PLAY for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_FREEZE"
 role="subsection"><title>VIDEO_FREEZE</title>
 <para>DESCRIPTION
 </para>
@@ -653,26 +668,9 @@
 <para>Equals VIDEO_FREEZE for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_CONTINUE"
 role="subsection"><title>VIDEO_CONTINUE</title>
 <para>DESCRIPTION
 </para>
@@ -706,26 +704,9 @@
 <para>Equals VIDEO_CONTINUE for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SELECT_SOURCE"
 role="subsection"><title>VIDEO_SELECT_SOURCE</title>
 <para>DESCRIPTION
 </para>
@@ -769,26 +750,9 @@
 <para>Indicates which source shall be used for the Video stream.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SET_BLANK"
 role="subsection"><title>VIDEO_SET_BLANK</title>
 <para>DESCRIPTION
 </para>
@@ -835,33 +799,9 @@
 <para>FALSE: Show last decoded frame.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal input parameter</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_GET_STATUS"
 role="subsection"><title>VIDEO_GET_STATUS</title>
 <para>DESCRIPTION
 </para>
@@ -903,33 +843,9 @@
 <para>Returns the current status of the Video Device.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error, possibly in the communication with the
- DVB subsystem.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>status points to invalid address</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_GET_EVENT"
 role="subsection"><title>VIDEO_GET_EVENT</title>
 <para>DESCRIPTION
 </para>
@@ -980,24 +896,9 @@
  stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>ev points to invalid address</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EWOULDBLOCK</para>
 </entry><entry
  align="char">
@@ -1009,16 +910,11 @@
 <para>EOVERFLOW</para>
 </entry><entry
  align="char">
-</entry>
- </row><row><entry
- align="char">
-</entry><entry
- align="char">
 <para>Overflow in event queue - one or more events were lost.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SET_DISPLAY_FORMAT"
 role="subsection"><title>VIDEO_SET_DISPLAY_FORMAT</title>
 <para>DESCRIPTION
 </para>
@@ -1063,32 +959,9 @@
 <para>Selects the video format to be used.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal parameter format.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_STILLPICTURE"
 role="subsection"><title>VIDEO_STILLPICTURE</title>
 <para>DESCRIPTION
 </para>
@@ -1133,32 +1006,9 @@
 <para>Pointer to a location where an I-frame and size is stored.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>sp points to an invalid iframe.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_FAST_FORWARD"
 role="subsection"><title>VIDEO_FAST_FORWARD</title>
 <para>DESCRIPTION
 </para>
@@ -1200,39 +1050,17 @@
 <para>The number of frames to skip.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EPERM</para>
 </entry><entry
  align="char">
 <para>Mode VIDEO_SOURCE_MEMORY not selected.</para>
 </entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal parameter format.</para>
-</entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SLOWMOTION"
 role="subsection"><title>VIDEO_SLOWMOTION</title>
 <para>DESCRIPTION
 </para>
@@ -1274,39 +1102,17 @@
 <para>The number of times to repeat each frame.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EPERM</para>
 </entry><entry
  align="char">
 <para>Mode VIDEO_SOURCE_MEMORY not selected.</para>
 </entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>Illegal parameter format.</para>
-</entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_GET_CAPABILITIES"
 role="subsection"><title>VIDEO_GET_CAPABILITIES</title>
 <para>DESCRIPTION
 </para>
@@ -1350,25 +1156,9 @@
  information.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EFAULT</para>
-</entry><entry
- align="char">
-<para>cap points to an invalid iframe.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SET_ID"
 role="subsection"><title>VIDEO_SET_ID</title>
 <para>DESCRIPTION
 </para>
@@ -1410,24 +1200,9 @@
 <para>video sub-stream id</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINTERNAL</para>
-</entry><entry
- align="char">
-<para>Internal error.</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1435,7 +1210,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_CLEAR_BUFFER"
 role="subsection"><title>VIDEO_CLEAR_BUFFER</title>
 <para>DESCRIPTION
 </para>
@@ -1468,18 +1243,9 @@
 <para>Equals VIDEO_CLEAR_BUFFER for this command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SET_STREAMTYPE"
 role="subsection"><title>VIDEO_SET_STREAMTYPE</title>
 <para>DESCRIPTION
 </para>
@@ -1522,25 +1288,9 @@
 <para>stream type</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>type is not a valid or supported stream type.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SET_FORMAT"
 role="subsection"><title>VIDEO_SET_FORMAT</title>
 <para>DESCRIPTION
 </para>
@@ -1583,17 +1333,9 @@
 <para>video format of TV as defined in section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1601,7 +1343,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SET_SYSTEM"
 role="subsection"><title>VIDEO_SET_SYSTEM</title>
 <para>DESCRIPTION
 </para>
@@ -1645,17 +1387,9 @@
 <para>video system of TV output.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1663,7 +1397,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SET_HIGHLIGHT"
 role="subsection"><title>VIDEO_SET_HIGHLIGHT</title>
 <para>DESCRIPTION
 </para>
@@ -1705,25 +1439,9 @@
 <para>SPU Highlight information according to section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
-<informaltable><tgroup cols="2"><tbody><row><entry
- align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor.</para>
-</entry>
- </row><row><entry
- align="char">
-<para>EINVAL</para>
-</entry><entry
- align="char">
-<para>input is not a valid highlight setting.</para>
-</entry>
- </row></tbody></tgroup></informaltable>
+&return-value-dvb;
 
-</section><section
+</section><section id="VIDEO_SET_SPU"
 role="subsection"><title>VIDEO_SET_SPU</title>
 <para>DESCRIPTION
 </para>
@@ -1766,17 +1484,9 @@
  to section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1785,7 +1495,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SET_SPU_PALETTE"
 role="subsection"><title>VIDEO_SET_SPU_PALETTE</title>
 <para>DESCRIPTION
 </para>
@@ -1827,17 +1537,9 @@
 <para>SPU palette according to section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
@@ -1845,7 +1547,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_GET_NAVI"
 role="subsection"><title>VIDEO_GET_NAVI</title>
 <para>DESCRIPTION
 </para>
@@ -1889,17 +1591,9 @@
  ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EFAULT</para>
 </entry><entry
  align="char">
@@ -1907,7 +1601,7 @@
 </entry>
  </row></tbody></tgroup></informaltable>
 
-</section><section
+</section><section id="VIDEO_SET_ATTRIBUTES"
 role="subsection"><title>VIDEO_SET_ATTRIBUTES</title>
 <para>DESCRIPTION
 </para>
@@ -1951,17 +1645,9 @@
 <para>video attributes according to section ??.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
-<para>ERRORS
-</para>
+&return-value-dvb;
 <informaltable><tgroup cols="2"><tbody><row><entry
  align="char">
-<para>EBADF</para>
-</entry><entry
- align="char">
-<para>fd is not a valid open file descriptor</para>
-</entry>
- </row><row><entry
- align="char">
 <para>EINVAL</para>
 </entry><entry
  align="char">
diff --git a/Documentation/DocBook/media/dvbstb.png.b64 b/Documentation/DocBook/media/dvbstb.png.b64
new file mode 100644
index 0000000..e8b52fd
--- /dev/null
+++ b/Documentation/DocBook/media/dvbstb.png.b64
@@ -0,0 +1,398 @@
+iVBORw0KGgoAAAANSUhEUgAAAzMAAAGaCAYAAAA7Jx25AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
+WXMAAA3XAAANiQFmEOuiAAAgAElEQVR42uzdd1RU18I28GdgKFZUBE0saFA0KoqFFkEhKhbAQmxJ
+bIkNNEpMEUwsMZarJMZrw4KxRExQczUqil0jRBA1GAjGQqLYC4TemdnfH76cj3HodYDntxaLmTll
+zuw57Zmz9z4yIYQAkYZzcnJCSkoKGjZsyMIgIiIiquPS09PRoEEDyBhmqCaQyWRo06YN3nvvPRYG
+ERERUR137Ngx/Pnnn5CzKKgmMDAwwKpVqxhmiIiIiAj29vZ4//33ocWiICIiIiKimohhhoiIiIiI
+GGaIiIiIiIgYZoiIiIiIiBhmiIiIiIiIYYaIiIiIiIhhhoiIiIiIiGGGiIiIiIgYZoiIiIiIiBhm
+iIiIiIiIGGaIiIiIiIgYZoiIiIiIiGGGiIiIiIiIYYaIiIiIiIhhhoiIiIiIGGaIiIiIiIgYZoiI
+iIiIiBhmiIiIiIiIYYaIiIiIiIhhhoiIiIiIqFLIWQRElSMsLAy2trZo1KgR5HJualTxEhIS8P33
+3+PDDz+sM5+5bdu2ePDgAZo2bcoVgCplm3J0dMS5c+fqzGf++uuvsWTJEm5TVClSU1ORk5ODBw8e
+oHXr1gwzRDVJbm4uAGDRokUwMDBggVCFmzlzJrKysurUZ3727BksLCzg4eHBFYAq3IIFC5CQkFCn
+PnNGRgYAYNWqVVwBqMJFRUVh48aNUCqVlfYeDDNElWzGjBkMM1QpNm7cWOc+c8uWLTFjxgzMmDGD
+KwBVuLt37yIkJKTOfW5nZ2duU1SpYaYysc0MERERERHVSAwzRERERETEMENERERERMQwQ0RERERE
+xDBDREREREQMM0RERERERAwzREREREREDDNERERERMQwQ0RERERExDBDRERERETEMENERERERMQw
+Q0REREREDDNEREREREQMM0RERERERAwzRERERETEMENERERERMQwQ0RERERExDBDREREREQMM0RE
+RERERAwzREREREREDDNEREREREQMM0RERERExDBDRERERETEMENERERERMQwQ0REREREDDNERERE
+REQMM0RERERERAwzRERERETEMENERERERMQwQ0REREREVGnkLAKimunBgwdISkoq8/SGhoZ47bXX
+WJCV6NmzZwgMDMS5c+ewd+9eFgiVSVZWFkJCQnD16lU8evQICoUChoaG6NChA2xsbNCxY0fIZDI8
+efIEp06dwuTJk0s876CgIJiYmKBLly4saKq2Y5Wuri6aNm0KQ0NDaGnxd3ZimCGqE/78808EBgbi
+p59+QkJCgsowLS0tyGQy6blSqYQQQmWcjz/+GGvXrmVBVoKtW7di+/btuHbtGoQQMDQ0ZKFQqf37
+77/w8fHBtm3bkJCQgCZNmsDS0hLGxsZ48OABtm/fjidPnsDU1BR2dnYICwtDz549SxxmlEol5s6d
+CxsbG+zZs4cFTpV2rDpx4gQOHDiAJ0+eqAzT09ODUqlETk4OAEBfXx/dunWDvb093Nzc0LdvX5Vj
+GVFBGH+JaqihQ4di06ZNOHr0qMrrly5dgkKhQG5urvSnVCqRlZWF27dvY8mSJQCA7OxsFmIlmTFj
+Bs6ePctfu6nMTp48iTfffBOrV6+Gnp4e9uzZg+fPn+PUqVPw9/fHkSNH8PDhQxw9ehRCCOzevRu3
+bt1CWlpaqd4jJiYG+/btw+PHj1noVGnHqnXr1uHcuXMqr+/fvx8ZGRnIzs5GSkoKIiIi8M0330BH
+Rwdr166Fvb09evXqhdOnT7MQiWGGqDazsrJSeV5Y1TFdXV107NgRX331FSZPniz9ElbTnDp1SuOX
+USaToXHjxujevTtXUCq1H3/8EcOGDcPz58/RtWtXREREYMKECdDR0VE9gGtpwcXFBdeuXYONjQ0A
+ID09vcTvs2HDBgBATk4OfH19WfBUqTp16gS5/P9XCDI3N5euujRs2BAWFhb46KOP8Ntvv+HIkSNo
+3rw5rl+/DicnJ3z66adQKpUsRGKYIaqNdHR0Sl3HeNy4cTXyysyBAwdq1EkX635TaV29ehVTpkyB
+UqlEw4YNcfToUbRs2bLIaZo0aYIjR47AyMioxFdm7ty5g6CgIGhrawMAtmzZgoyMDH4BVGlkMhl0
+dXVLNJ6rqyvCwsLQqlUrAMB3332Hjz/+mIVIDDNEtfkgURqOjo5YunRpjfqMd+7cwfTp0/llU62l
+VCoxY8YM6arp/Pnz0b59+xJNa2RkBC8vrxJfmfH19YWVlRUmTJgAAIiPj2cnFaRRxypTU1McOnRI
+CtwbNmzA4cOHWYjEMENUl+Xm5iIhIQH6+vowMTEpcJz8HQUIIdQ6DijoBKy0CppnUfN59uwZnJ2d
+S9V7mxCiVMtW2mWqiPckyu/EiROIiIgAAGhra8Pd3b1U00+aNAlZWVnFjpeamoqdO3di9uzZmD17
+tvT6f//732K3d6KqZGlpiRkzZkjPvby8it3HlmY/XNh+v6jtoCTHRU1RlmNSac8BGGaIqEpduXIF
+CxYsUHs9MTERfn5+sLa2xrVr15CSkoJJkyZBX18fbdq0QWRkpMrOLTAwEMOHD4epqSnat2+Pxo0b
+o3///vDz8yu0LU5ubi7Onz8PDw8PmJubS+87d+5cGBoaQi6Xw8LCQq2x5+XLl2Fra4s7d+4AAEJD
+Q+Hi4gIXFxfMnz9fZdzs7Gz4+vrC2toa+vr60NHRQdeuXeHj41PgSV5Zl+lVx44dw8CBA/Haa6+h
+Q4cO6NmzJw4cOFBn17OgoCC1XouoeD/++KP02NbWFkZGRqWa3sjICDt37ix2PH9/f8jlcowdOxaW
+lpawtrYGAERHR+Ps2bP8IjRQaGgooqKi6mTYnDNnjvT41q1buHTpkto4pdn3CyFw7do1eHt7o127
+dkhMTIQQAv7+/rCwsIBcLkfTpk3x8ccfS9Wxc3NzsXnzZvTu3Ru6urqoX78+3n33XbWeRPfv34/x
+48dLx6jFixdLw5KSkjB37lwMHz5cGp6/hkRsbCzmz58vDcv7++KLL5Cbm4vDhw9j7Nix0utz587F
+s2fPylUWZTkH0NTURqTxDAwMxN69e2vUMgcHBwsAIjExsdLfS1tbWwAQAMTdu3cLHW/hwoVi5syZ
+0vMrV66IESNGCF1dXWn63377TfTv31/o6+tLry1YsEAIIUR6eroYPXq00NPTE7t37xY5OTlCCCFu
+374t+vbtKwCIHj16iNjYWJX3PXXqlHBycpLm16JFCxEdHS06duwoHB0dhYuLi6hfv74AIHR0dMQf
+f/whTfvXX3+J06dPC2NjYwFA2NraitOnT4vTp0+L8PBwabynT5+KPn36iOnTp4vIyEjx6NEjcejQ
+IdGiRQsBQPTt21ekpaVVyDLlUSgUYvbs2UIul4stW7aI7OxsIYQQ0dHRwsLCQjRq1EgAEIaGhpXy
+vZubmwtfX1+NW/fzyrRdu3Zi5syZIiAgQDx58qRC5t22bVuN/MwVoVWrVlLZeXp6Vsp7KJVK0bVr
+V+Hl5SW95u/vL72vs7NznT7WeHt7Czs7O41brmnTpgkAwsDAQIwYMUKsX79eREZGCqVSWSGfuaq+
+9wYNGkjr2l9//VXi6dq3by9Nt3jxYpVhpdn3X7p0SYwePVrI5XKV5Rg8eLCwsrIS7u7u4u2335aG
+ff755+LJkyfirbfeEo6OjmLWrFli1KhRQktLSwAQrq6uast67949af6DBw9WGx4dHS0ds18td6VS
+KZYtWya9f+/evVWGr1y5Uujq6oqAgIACv/vSHgdLew5QFpGRkQKA2nlBRQgMDBQGBgaCYYYYZmpZ
+mDl48KAIDQ2V/i5duiTOnj0rvv76a6Grq6sSZtLS0kR2drZ0oAQgnJycxKFDh0RqaqqYOHGiaNKk
+iTh9+rRQKpVi7NixAkCBJ5MpKSmic+fOAoDo1KmTSElJURtn6NChAoDQ19cXlpaWIiIiQhr2xx9/
+SJ9jypQpatOamJgIAGLEiBFqw7Kzs0WfPn3EqFGj1Hbw+/fvlz6bt7d3hS7TokWLBACxZs0atWGP
+Hz+WDtx1Lcw0a9ZMKnMdHR3pwF4R4aa2hpnk5GSpzApbpyrC2bNnhUwmU/nRIzMzU/qxAIC4efMm
+w4yG8fDwkE6gtbS0hJ6eXoWFm5oQZkaOHClNN3r06HLv+xcsWCANs7GxUflhTKlUSu/XoEEDYWlp
+KS5cuKAy/erVq6XpY2Ji1JbX1NS00DCT/3hWULkrlUoxZMgQ6bvOK6f09HTRsWNH8d133xU4z7KU
+RWnOARhmiBhmqizMFPeXP8zk+eGHH6ThX331VYHvcezYMQFANG3aVGRlZRU4zpEjR6T5fPHFF2rD
+P/roI2n4s2fP1Ib369dPCkOlCTNbt24VAMS5c+fUhmVmZkq/MDVt2lS6mlTeZbpx44bQ1tYWhoaG
+hZbH8OHD62SYMTIyKnT9K2+4qa1h5p9//lEpp61bt1bK+4waNarAX5PzgjkAMWvWLIYZDQwz+a8m
+5P8rb7ipCWFmxowZ0nSOjo7l3vdv375dml9YWJjatPv27ZOGb9q0SW34zZs3peG7du1SG96pU6ci
+w0xe2Cms3O/fvy9d2XdychJKpVJMmzZNODg4CIVCUeA05TkOluQcQJPDjJw1UYlql5s3b6o07hdC
+IC0tDZcuXcKHH35Y4DT5718xZMiQAsfJ6xLZysqq0O41hw0bBmNjYzx//hxbt27F0qVLVe4rkNcr
+DQAYGxurTZ/XDWd8fHypPrOfnx8AIDw8HNHR0WrDmzVrhsePHyMhIQE3btxQuf9LWZdp3bp1UCgU
+GDhwYKHl0ahRI66Qr8jfpurevXvYsWMHvv/+e+Tm5qJdu3YYPHgwHB0d0b9//2K7JK7NFApFhc8z
+NjYWhw8fxvHjx9WGzZw5EytXroRCocCuXbuwfPlyNG3alCtsDZB3U+S8dhlHjx7FiRMnkJWVBQMD
+Azg4OGDAgAFwcHBAt27dSt37pSbIv8z5j1dl3ffn3+83aNCg0P1+3jxe1aJFC+nxw4cPK/zztmnT
+Bt988w3c3d1x6tQpTJo0CYcPH0ZUVFShXf6X5zhYknMATcYwQ1TL6OnpQV9fX+W1evXqYfjw4Viw
+YIHUkL4w+Xfy+Q+WFy5cAAA0b968yGn79++PAwcOID4+HtHR0ejRo0eJlz1vJy1K0cg1OTkZ165d
+g7a2dqGNzseMGaP2HuVZJiGE1EVo586dq/Uk5v79+7h27ZpGrYO5ubllDjfff/89tm/fDoVCIYWb
+3r17w9XVtVaHm1eDQ1xcXIW/x5YtW/DGG29g0KBBBZ68ubm54cCBA0hPT8f27dvx+eef18l9aFpa
+msZtUy9evChzuDly5AiCgoKQnZ2Nxo0bw9HREf369YO9vT369OlTI76Tf//9V3r8+uuvV/q+v6Dj
+oMrJc74f6Srr/kzTp0/Hvn37cP78efj7+2Pjxo2F9kJakWVR3GdnmCGqQ4QG9jrz9ttv4+7du6We
+Lj4+XroZX3Enqp06dZIeP3z4sFRhpizu3r0rdT+5Zs2aKtkRv3jxAk+fPgVQvVdfsrOzsWrVKqxa
+tarWbDf516979+5h69atUkifOnVqpVyx0ARNmjSRrmoCQExMTIXOPyMjA35+flAoFCq/yL66nefZ
+sGED5s2bp3LSVlfcuHGjxpzkl/RYlNcrV3JyMg4fPiz9GOPo6CiFA01269Yt6XHv3r2rbd9flbS0
+tODn5wdzc3NkZGTg1KlTmDVrVoFX1mp7WTDMEFWDFy9eYPny5Rq3XD179sTGjRtLPV3+E8i8k/jC
+GBoaSo+rYoeaF7KEELh//36JbzJYHvl/Nc/MzKy271NPTw/ffvttodUHq4uZmVmZryzo6ekhKysL
+enp6sLS0hJOTE/r37w8bGxvo6uoiMDCw1u437OzscPDgQQAvu+KtSAEBAUhNTYWPj0+Rv8quWLEC
+T58+xYMHD3Do0CGVX3Prip49exZYFa86ffbZZ/jhhx9KddUzj46ODpRKJZRKJTp06IChQ4fCwcEB
+ffv2hbGxMRYsWIDExESNPp5GRUVJz11cXKpt31/V0tLSpOPvkSNHsG/fPowfP14jjoMMM0S12Llz
+5zBgwABMnTq11nym5s2bQ0dHBzk5OYiOjoYQotB61/lv0PXGG29U+rLVr19fehwSElIlO3E9PT3p
+8d9//11t34tMJkP9+vU1rm1Daerk6+vrIzMzUyW8ODo6Ftk2q7YaO3asFGbu3LmDqKgo6f5H5SGE
+wIYNGzBmzBjMnTu3yHHj4+Px1VdfAXh5E826GGby7jOiSfLvc4qjq6sLhUIBpVKJjh07YsiQIXBw
+cIC9vX2R1YQ11c6dO6WaDi4uLmjXrl217furUlZWFiZMmIClS5di48aNePToEebMmYMBAwao3YOq
+tpdFcXjTTKIKolQqsXz5cgwYMAAAMHz48Fp1cM+7sV5cXBxu3LhR6Lh59XVbtWqFjh07VvqymZqa
+SifPfn5+RVbvS01NxcyZM8v9nq1bt5YaTF64cIF3TS+FvPZcenp6sLOzwxdffIHg4GAkJycjODgY
+ixYtgp2dXZ0LMgDg5uamchLy3XffVch8L168iIiICEyfPr3YcWfOnCmt25cuXUJ4eDhXWg2nq6sL
+bW1tyGQymJmZwd3dHQcOHMCLFy9w69YtrFu3DqNGjaqRQebx48dSNVpdXV2sXr26Wvf9FaUkx4yF
+CxeicePGmD9/PjZv3iwdfz09PTXiOMgwQ1TLvHjxAoMGDcKSJUsAvLxjcUE9oFTWTjH/1ZDKOrH+
+4IMPpMcBAQGFjpd38uPu7l7qXnOKWva8eeXV/c7TqFEjKWgFBwdjz549BU6fm5uLKVOmwMnJqdzL
+pKenh/79+wN4WVc5KCioyGnrWtjJq/Lwanixt7dneCmCjo4ONm3aJD3ftWsXTp8+XeLpExMT4erq
+qnZX8NWrV8PMzAz29vbFzqNly5YYPXq09Hzt2rXcwWsApVIpVTcqaXjJX+W3Jp3E50lISMDIkSOR
+kJAAANi0aRO6dOlSZfv+8sirYp2enl5gGaSkpBQ5/a+//oqNGzfCz88PWlpacHV1xbvvvgsA+Omn
+n3D06NEqPQ4yzBDVcufPn0fXrl1x8eJFKJVKaGlp4bPPPquy909PT1c5QJSlZ5X8YaiwOtmTJk2C
+paUlAGDz5s0F1rG+ffs2goODYWZmhnnz5qkNL67xdl7PVgUd8PKqfdy+fVsanp2djcePH6v8UjVt
+2jSsX79e6s0HeFllx8XFBdnZ2XBzc6uQZcr/+Tw8PNS650xMTERISAiAl41uU1NT68w2kXcAfzW8
+XLx4keGlGEOHDsWaNWuk525ubiVqJxQaGgpLS0v069dPpdvYK1euICgoCGPGjCnxjwvvvfee9PjA
+gQPVWpWS/v8JsBCixoWXV/e1+dsYFhZshBA4deoU+vTpgytXrkBXVxfbtm3DtGnT1MYt674//zGv
+LMfE4n5AzLvCevnyZdy+fVulDFasWCHtIwu6DUFycjImT56MBQsW4M0335ReX7dunfQdu7u7qx2D
+y3McLMk5AMMMUS2kVCrx9ddfY+DAgYiPj0dubi7kcjnGjx+Ptm3bVtlynDx5UuV5YVcJipK/u+bf
+f/+9wHHkcjkOHTqETp06IT4+HhMmTFD5BT4hIQETJ05EmzZtEBgYWGDf/flPivJ6bcp/QPjzzz8B
+vOxONDk5WWW4ra2tNI/58+fj4MGDeOedd/Dvv/9i3LhxGDdunBQ+PD090bx5c/To0QOmpqYwMzND
+UlIS/P391U7oyrpMw4YNg4eHBwDg/v376NWrF1asWIHAwEBs27YNjo6OMDAwkA4O3bt3r3WX9gtz
+7do1ZGVlMbyU0SeffIJ9+/ahWbNmSE1NhaurK9zc3HDy5EmVX3pTU1MRFBSEd955By4uLli2bJlK
+d8qZmZmYNWuWtP2WVF6bhLyTr3nz5hV78keVa968eYiLi6tR4eVV58+fV1mP9u3bh5iYGPz999/4
+/fffcfjwYSxatAg9evTA4MGDce/ePbi5ueH69euFVpEs674/f2+BBQWK2NhY6XFB1aofPHggPX78
++LHa8LyaDNnZ2bCzs4OXlxe8vb1hbm4OIQQcHBwAAGFhYXj//fdx7Ngx6bxi2rRpUCqV8PLyUpmn
+kZGRVPvj8ePHmDJlikrwKM9xsCTnAJqe9ok0noGBgdi7d6/GLM+TJ0+Eg4OD2h2ZZTKZiIyMFEII
+ERwcLACIxMTESlkGPz8/MWjQoALvCm1nZyc+/fTTYudx5swZ4erqKrS0tKRp9fT0xKRJk8T27dsL
+nCYpKUnMmzdPNGrUSLz++utixowZ4sMPPxQmJibC3d1dxMXFqU0TGhoqxo8fr7KMXbp0EV9++aUQ
+QoiTJ08KJycnleE9e/YU27Ztk+YRGxsrWrduLQ1//fXXxfnz56XhOTk5YsmSJdJdk/P+DAwMxMKF
+C0VGRkaFL5NCoRDffPONaNq0qcp4JiYm4ty5c+L9998XhoaGwsPDQwQHBxd65+ayMjc3F76+vnVq
+X9C2bds685nj4uLEkiVLRNu2bVX2MYaGhqJZs2YCgGjTpo1YtGiR2na3f/9+0blzZ2k6uVwuRo0a
+JY4fP17o+/31119i8uTJol27dmr7lN69e4tffvml1pe5t7e3sLOzq1PblLe3d6F3oq+oY5WTk5PQ
+1dVVW6/y/zVu3Fh06dJFjB07VmzatEk8fPiwRPMvzb7/119/FbNmzRJ6enoq+2tvb28RGxsr/v77
+bzFv3jzRvHlzabiurq7w8PAQFy5cEBkZGeLzzz8X7du3V9m23N3dRWBgoPQ+SqVSLF26VOX43KxZ
+M+n44ezsLNq3by+8vb1FaGioyM3NFcHBweLtt98WAISRkZHw8fFR+Zznzp0TDg4OKp/R1tZWZZsu
+7XGwLOcApRUZGSkAiNjY2ApftwIDA4WBgYGQCbZcpRqgSZMm8PX1Van+UJ2/Lo0ZMwYpKSkq7Tfk
+cjkcHR1x6tQpAC97FLG3t0diYqL0C31tkpWVhT/++ANxcXFo2rQpevToodKjSmVIS0tDWFiY1PNV
+QT38ZGRk4Pr160hISICRkRG6d+9eqp6AyloW169fR1xcHIyNjdGzZ0/I5XLExMTAxMRE5e7KFal7
+9+7w8PCQrhDVBSYmJvD29q5Tn1kIgdu3b+PPP/9EXFwclEoljI2N0bVrV3Tq1KlG3tFdUy1YsAAh
+ISEIDg6uU585Kiqqxnd7Xh37/uK8ePEC169fR7169dCnTx+pDeE///yDdu3alfomzjWxLKKiotC9
+e3fExsZWeK2VY8eO4f3332fXzEQlpVAosGzZMixbtky6HPzq8C+++KLOlIeenh6srKyq9D0bNGgg
+9RZXmHr16klV0qqyLPIaX+bXoUMHbjhUbjKZDJ06dVK5IS0RVf++vzhGRkYYNGiQ2uuVfdsCTSyL
+ysQwQ1QCT58+xZgxYxAWFlZg3XEtLS1YWFhI9WCJiIiIqPKxAwCiYpw5cwbdunVDeHh4kb18LFy4
+kIVFRERExDBDVP0UCgUWL14MJycnJCQkqN3fJI9MJoOJiQlGjBjBQiMiIiKqQqxmRlSAR48eYfz4
+8QgLC5P69y+MtrY2vvjii0pryEdEREREDDNEJXLq1CkMHjwYurq6Jbp5lIGBASZNmsSCIyIiIqpi
+/CmZKB8hBPbv3w8AhVYry09XVxdeXl68ISARERERwwxR9ZLJZNi+fTvWr18PLS2tYquO6ejoYMaM
+GSw4IiIiIoYZIs0wZ84cnDlzBo0aNSr0hoe6urr46KOPauUNMYmIiIgYZohqMEdHR4SGhkJbW7vA
+O2wrlUp4enqyoIiIiIgYZog0z6pVq9CsWTO4uLhAW1tbel1XVxeTJ0/Ga6+9xkIiIiIiYpgh0izr
+1q1DQEAA/ve//+Hw4cNYvnw5tLS0IJPJkJ2dDS8vLxYSEREREcMMkWa5cOECPvvsM/j6+sLGxgYy
+mQze3t4IDAyEEAI2Njbo2LEjC4qIiIioGvE+M0SvuH//PsaOHYtp06Zh6tSpKsOGDh2KW7duISsr
+iwVFRERExDBDpDnS09Ph5uYGMzMzrFu3rsBxzMzMWFBEREREDDNEmsXDwwNPnz7F1atXeSNMIiIi
+IoYZopohr8H/r7/+ipYtW7JAiIiIiBhmiDRfXoP/LVu2wMbGhgVCREREVAOwNzOq84pq8E9ERERE
+DDNEGqkkDf6JiIiISDOxmhnVaWzwT0RERFQLwsz333+P77//Hg0aNGCpUIVTKBTIycnB//73Pxgb
+G2vEMrHBPxEREVEtCTMxMTEIDQ2Fl5cXS4UqXFRUFM6fP4/MzEyNWB42+CciIiKqRWEGAJydnbFq
+1SqWClVKmDl+/LhGLAsb/BMRERHVDuwAgOoUNvgnIiIiqj3YAQDVKWzwT0RERMQwQ1TjsME/ERER
+EcMMUY3DBv9EREREtQ/bzFCtxwb/RERERAwzRDUOG/wTERER1V6sZka1Ghv8ExERETHMENU4bPBP
+RERExDBDVOOwwT8RERFR7cc2M1TrsME/EREREcMMUY3DBv9EREREdQermVGtwgb/RERERAwzRDWO
+pjb4X716NQwMDPgFUYWLioqqc5/54cOHWL16NZKTk7kCUKXsr83Nzevc5z527BhWr17NFaAYycnJ
+uH//Ptq1a4eGDRuyQDTkOMUwQ7WCJjb4b9iwIUxMTHDixAloabFGJ1W8Nm3awMjIqE59ZkdHR9y/
+fx8HDhzgCkAVrl27dujZs2ed+sytWrVCmzZtuE29QqlUIi0tTeUvJycHMpkM+vr66NKlCwupBLKz
+s2FqalqptWUYZqjG09QG/xYWFrh37x6/IKIKdObMGRYCUQX66KOP8NFHH9XpMlAoFIiOjkZ4eDgu
+X76My5cv48aNGxBCoHPnznBycoKVlRVsbGwQFRWFhQsX4urVq1x5NATDDNVobPBPREREpfHo0SMp
+tFy+fBnXrhOJlUQAACAASURBVF1DamoqWrZsCWtra4wfPx42Njbo06cPGjdurDLtjRs3WIAMM0QV
+hw3+iYiIqDCpqam4evUqwsPDERYWhvDwcDx69Aj169dH7969YWVlhdmzZ8Pa2hpt27ZlgTHMEFUd
+TW3wT0RERFWvuOpi1tbWWLhwIWxsbNCtWzfI5TwNZpghqiaa2OCfiIiIqk55qosRwwxRtdHUBv9E
+RERUOVhdjBhmqFZgg38iIqLajdXFiGGGai02+CciIqpdWF2MGGaoTjhx4gT27dvHBv9EREQ1FKuL
+EcMM1Um5ubn48ccfsXXrVjb4JyIiqgFYXYwYZojwssF/eno63n77bTb4JyIi0lCsLkYMM0SvyGvw
+r6WlhUmTJrFAiIiINACrixHDDFEJeHh44NmzZ2jQoAEvPxMREVUDVhcjhhmiMli3bh0CAgLw66+/
+YsiQISwQIiKiKlCS6mLW1tawtLRkdTFimCEqyIULF/DZZ59hy5YtbPBPRERUSVhdjBhmiCrY/fv3
+MXbsWEybNo0N/omIiCoIq4sRw0wVCAoKgomJCbp06cJvpw7Ka/BvZmaGdevWsUCIiIjKiNXFiGGm
+iimVSsydOxc2NjbYs2cPv506KK/B/5UrV6Crq8sCISIiKgFWFyOGGQ1w8uRJxMTEIDY2FqtXr8br
+r7/Ob6gOyd/gv2XLliwQIiKiAigUCty4cUPlqgurixHDjAbYsGEDACAnJwe+vr5Yvnx5lb7/qVOn
+4OTkxLWiGrDBPxERUcFYXYyoBoSZO3fuICgoCNra2lAoFNiyZQu+/PJL1KtXr0re/8CBA9i7dy/D
+TDVgg38iIqKXWF2MqIaGGV9fX1hZWeHNN9/E7t27ER8fj71792LatGlVEqSmT58OBwcHrhFVjA3+
+iYiormJ1Mc329OlThISEYPTo0UWOFxMTg3/++Yc/iNflMJOamoqdO3di/fr1UpgBgP/+97+YOnUq
+ZDJZieclhFAbX6lUQktLq8Dxnz17BmdnZyQlJZVqmYUQEEIUOt/yLFNFTfvqfACUqiyrAhv8ExFR
+XcHqYjXLzZs3MW7cOMTFxaFp06aFjrdnzx4cPXqUYaaaaGnCQvj7+0Mul2Ps2LGwtLSEtbU1ACA6
+Ohpnz54tdvrc3FycP38eHh4eMDc3BwAkJiZi7ty5MDQ0hFwuh4WFBU6fPq0y3eXLl2Fra4s7d+4A
+AEJDQ+Hi4gIXFxfMnz9f7X2ys7Ph6+sLa2tr6OvrQ0dHB127doWPjw+ysrIqZJnKO21+V69excSJ
+E2Fvb4/Bgwejbdu26N27N3bs2CGFm+qU1+D/wIEDbPBPRES1SmpqKi5cuAAfHx+4ubmhdevWaN26
+NSZOnIjQ0FD06dMHO3bsQGxsLJ48eYJffvkFX3zxBQYMGMAgoyFsbGygq6uLkJCQIse7cOECHB0d
+WWDVRfwfb29v4ezsLKqaUqkUXbt2FV5eXtJr/v7+AoAAUOwynTp1Sjg5OUnjt2jRQkRHR4uOHTsK
+R0dH4eLiIurXry8ACB0dHfHHH39I0/7111/i9OnTwtjYWAAQtra24vTp0+L06dMiPDxc5X2ePn0q
++vTpI6ZPny4iIyPFo0ePxKFDh0SLFi0EANG3b1+RlpZW7mUqz7T5bdq0SchkMuHp6SkUCoUQQoi0
+tDRhZ2cnAIgVK1ZU6fccGRkpAIjY2FghhBDnz58XcrlcbN++vUTTGxgYiL179woiIiJNk5ubKyIj
+I4Wfn5+YNm2aMDc3F9ra2kJLS0t06dJFfPDBB2Lz5s0iIiJC5OTksMBqkH79+olPPvlEer53717R
+tm1b6Xl6errQ09MTR44cYWFVscDAQGFgYCCqPcycPXtWyGQycffuXem1zMxMKWAAEDdv3ix2PkOH
+DhUAhL6+vrC0tBQRERHSsD/++ENoa2sLAGLKlClq05qYmAgAYsSIEQXOOzs7W/Tp00eMGjVKKJVK
+lWH79++XltPb27vClqk80z548EAafurUKZVhAQEBAoBo1KiRyMrKqpYwExsbK4yMjIS7u3uJp2eY
+ISIiTfHw4UPxv//9T8yfP1/0799fNGzYUAAQLVu2FCNGjBArVqwQZ86cEUlJSSysGm7x4sWiZ8+e
+hYaZ8+fPC21tbZGQkMDCqqYwU+3VzDZu3AgXFxe0a9dOek1PTw8zZ86Unq9fv77Y+ZiamgIAMjMz
+ERgYCAsLC2lY9+7d0bdvX6kqWWnt3LkTV69exZw5c9TanAwfPhz6+voAgK1btyI3N7dClqk8096+
+fRsKhQIAEBcXpzLM2NgYAJCSkoK7d+9W+fedkZHBBv9ERMTqYlQjODo64o8//kBCQkKBw8+fP4/u
+3bujSZMmLKxqUq0dAMTGxuLw4cM4fvy42rCZM2di5cqVUCgU2LVrF5YvX15k4yttbW21E/b8WrVq
+BQCIj48v9XL6+fkBAMLDwxEdHa02vFmzZnj8+DESEhJw48YNdO/evdzLVJ5p+/Xrh08//RRZWVkY
+OXKkyrD8Yay0nR5UhC+//JIN/omISCOxdzF6lY2NDXR0dBASEgJXV1e14WwvU8fDzJYtW/DGG29g
+0KBBBZ6su7m54cCBA0hPT8f27dvx+eefl/m98nr/EqVs+J6cnIxr165BW1sbT548KXCcMWPGqL1P
+ZS5TcdPK5XJ8++23Kq+lp6cjICAAO3bskF5TKpVV/p0fOXIEFy9eZIN/IiKqduxdjIqjr68Pa2tr
+XLhwQS3MZGRk4PLly/jss89YUHUxzGRkZMDPzw8KhUK6kvGq/FcdNmzYgHnz5lX5ryB3796FEAJK
+pRJr1qxRuWJSE9y7dw/r16/HrVu3MHXqVCxZsqRauw5csWIFbGxsuOUREVGV4s0oqawcHBxw9OhR
+tdcvX76M3Nxc2Nvbs5DqYpgJCAhAamoqfHx8iryasWLFCjx9+hQPHjzAoUOHVK6CVIW0tDQAL6+A
+3L9/H+3bt68RX2xaWhoWLFgAf39/+Pr6Ys2aNZDJZLhw4UK1Ltft27dx//59HiiIiKjSsLoYVSRH
+R0csX74ciYmJKq+zvUwdDjNCCGzYsAFjxozB3Llzixw3Pj4eX331FYCXN9Gs6jBTv3596XFISEiN
+CDNJSUlwdHREREQEgoKCMGTIEI1Ztl69esHExASTJk2Cn58f280QEVG5sboYVaa8djPBwcEqr7O9
+jGaolt7MLl68iIiICEyfPr3YcWfOnAkdHR0AwKVLlxAeHl6ly2pqaio1mvfz8yuyfUtqaqpKL2zV
+ZeXKlYiIiICJiYlGBRkAcHZ2BgD88MMPePPNN3HixAluhUREVGLsXYyqWv52M3ny2ss4ODiwgOpi
+mFm9ejXMzMxKVMewZcuWGD16tPR87dq1ZXrPokJIXljJzs5WG9aoUSNYW1sDAIKDg7Fnz54C55Gb
+m4spU6aUqj1KWRr+l2TavN7h9PT01Ibl5ORU+0oXFRUF4GV7JGdnZ4wcORL//PMPt0YiIlKhUCgQ
+FRWF7du3Y/r06VKVngEDBmD37t1o0qQJFi5ciIiICCQlJeHixYv49ttvMWbMGFZnpgrl4OCA8+fP
+S8/DwsLYXqauhpkrV64gKCgIY8aMUbtnS2Hee+896fGBAwfw999/F7jDK0reSXxBISCvy+fbt29L
+w7Ozs/H48WMAgKenpzTutGnTsH79emRlZUmv3blzBy4uLsjOzoabm1uFLFN5ps3rpezOnTv4888/
+pdczMzOxe/dulV8VyhuqyqJbt2745JNPYGBgAF1dXcTExKBbt2746quvpGUiIiLNdP36ddy5c6dS
+5v3o0SMcPHgQXl5ecHBwQJMmTdC9e3csWrQIL168wPjx43Hy5EkkJCQgOjoaO3bsgLu7OywsLNju
+hSpV3v1m0tPTAbysYtajRw+2l6lrYSYzMxOzZs0CgFLtdPLfUFOhUGDevHlq3QrnDzjPnz9XGSaE
+kE7qk5KSkJycrDLc1tZWmsf8+fNx8OBBvPPOO/j3338BAOPGjcO4ceOkEOHp6YnmzZujR48eMDU1
+hZmZGZKSkuDv768S0MqzTOWZNu/qkBACgwYNwsKFCzF79mz06NEDXbt2lcZbtWoVlixZgh9++KHK
+V7wlS5ZAX18fPXr0wN27dzFr1ixs3rwZXbp0waFDh7hlEhFpmN9//x0jR45Er169EBAQUO75sboY
+1SR57WZu3rwphRlWMdMQ4v94e3sLZ2dnUVn2798vOnfuLAAIAEIul4tRo0aJ48ePFzrNX3/9JSZP
+nizatWsnTZf317t3b/HLL7+I0NBQMX78eJVhXbp0EV9++aUQQoiTJ08KJycnleE9e/YU27Ztk94n
+NjZWtG7dWhr++uuvi/Pnz6ssS05OjliyZIlo1KiRyrwMDAzEwoULRUZGhjRueZapIj5PUlKScHBw
+UBnH2dlZxMTECIVCIczNzaXXR40aJdLT00Vli4yMFABEbGys9NquXbuEvr6+mDx5sqhfv744dOiQ
+mDt3rpDL5WLIkCHi1q1b0rgGBgZi7969goiIqtaVK1eEq6urkMlkwsnJSYSEhJR6Hrm5uSIyMlL4
++fmJadOmCXNzc6GtrS20tLREly5dxAcffCA2b94sIiIiRE5ODgudNFK/fv3EsGHDRJs2bYSenp44
+cuQIC6UaBQYGCgMDAyET/1fHaMGCBYiKikJgYGCdDHVpaWkICwuDnp4eLC0tC2xvArysmnX9+nUk
+JCTAyMgI3bt3L3Tcag6piIyMxJMnT/Dmm2/CxMREGpaSkoLQ0FA0b94cPXv2LHF1v/KIiopC9+7d
+ERsbK9VjFkLA3t4eTZo0QYcOHeDn54fDhw+jRYsWmDNnDkJDQ/HJJ5/gyy+/ROvWreHr66tS5ZCI
+iCpPeHg4vv76axw7dgxDhgzB4sWLpZoMxSmudzErKyv2LkY1zpIlS+Dv74/U1FTEx8cjLi6O1cyq
+0bFjx/D++++DFUz/T4MGDTBgwIBix6tXr16Jd+bVSSaToUePHujRo4fasEaNGlXrjTPzL+PGjRvR
+p08f/PLLLwCAESNG4PDhwzh//jwCAgLw6aefwt/fXyM6LiAiqgvCwsLw9ddfIygoCMOGDUNYWJjU
+EU5BeDNKqivy7jfToEEDtpfRIAwzVK0sLCwwY8YMzJs3D5GRkSqB5t1334Wrqyu+/vprfPPNN1i+
+fLlaux8iIqoYv/32G77++mucPn0azs7OCA8Ph6Wlpco4vBkl1WXW1taQy+VISUlhexmGGaL/b9my
+Zfj555/x7bffSl1v5wWagQMHwsfHB1u2bIFcLoeFhQXmzJmDxYsX8xcRIqIKEBwcjKVLl+LcuXNw
+dXXFlStX0Lt3bwBFVxezsrLizSipTqlXrx7Mzc1x7do1hhmGGaL/z9DQECtXroSnpycmT55cYKDR
+0tKCt7c39PX1MW/ePAQEBGD9+vUq9yAiIqKS+/XXX7F06VJcuHABI0eOREhICLKzs3H27FmsWLGC
+1cWICtC8eXMA4P1lGGaIVH344YfYunUrPv30Uxw4cEAt0ORxc3PD0KFDsXLlSrXuuYmIqHjnz5/H
+0qVLERwcjF69emHUqFG4c+cO+vXrx+piRMXw9vbGixcvWDuEYYZIlZaWFjZs2IC+ffvi1KlTcHJy
+Ugk0+Xtcq1evHpYtW8ZCIyIqhb1792LChAkAAG1tbSiVSsTGxsLIyAgjR47E2rVrWV2MqBjW1tYY
+PHgwC4JhhkidjY0NPvzwQ3h6eiIyMhI6OjpSoFm/fr10o9Ca4s8//8TEiRPRrFkzaGlp8QumCvf8
++XMsX74crq6udeYzv/POO3j48CFPuEshOTkZMTEx0o2ggZcN+QHgxYsXCAoKQlBQEJYtWwaZTCZd
+hcn7r6Ojo/JcLpdDJpNBW1sbLVq0gKGhYa0pq3///RdvvfUWNmzYUGfWj++//x7r16+HsbExN5YS
+ysrKwqBBg1gQJZCdnY309HQcO3as0tYxhhnSKCtXroSZmRnWrVuHzz77DDKZDGvXrsWWLVuwZs0a
+vP322xg4cGCN+CyJiYm4fv063N3dYWBgwC+XKtzq1avx8OHDOvWZDx48iMaNG8PDw4MrQCk4OjpK
+ISYnJ0f6r1QqkZ2drfZfCIGsrCzpf94JHABkZmZK/9u3b4+OHTvWqm0qOzu7Tq0bMTExiIyMhJeX
+FzcUqnBRUVG4ePGitN9gmKFaz8jICEuXLsWXX36Jd999F61atYJMJoO+vj5sbW1VOgWoKVatWsUw
+Q5Xi+PHjde4zt23bFt7e3gwzVClkMhlCQkLq3Od2dnbGqlWruAJQpYSZyj5Wse4LaZzZs2ejQ4cO
+mD9/vsrrEyZMwPTp0zFixAicOXOGBUVERERUxzHMkMbR1tbG+vXrERAQgODgYOn1vCpnDDRERERE
+BLCaGWkoe3t7zJw5E8+ePVN5PS/QAKiRVc6IiIiIiGGG6gBfX98CX2egISIiIiKGGaqxGGiIiIiI
+iGGGGGiIiIiIiGGGiIGGiIiIiBhmiBhoiIiIiIhhhhhoiIiIiIhhhoiBhoiIiIgYZogYaIiIiIiI
+YYaIgYaIiIiIYYaIgYaIiIiIGGaIGGiIiIiIiGGGiIGGiIiIiBhmiIGGiIiIiBhmiBhoiIiIiIhh
+hoiBhoiIiIgYZogYaIiIiIgYZogYaIiIiIiIYYaIgYaIiIiIGGaIGGiIiIiIGGaIGGiIiIiIiGGG
+iIGGiIiIiBhmiBhoiIiIiIhhhoiBhoiIiIhhhoiBhoiIiIiqNcxcuHABZ86cYalQhYuKimKgISIi
+IqLKCzNpaWkYNGgQS4XqPAYaIiIiohoUZv7zn//gP//5D0uEiIGGiIiIqGaFGSJioCmMEALh4eE4
+ceIEnj17BmNjY1haWuLtt99GvXr1kJiYiJ9//hnTpk2Tpnnw4AGSkpLK/J6GhoZ47bXXih0vKysL
+ISEhuHr1Kh49egSFQgFDQ0N06NABNjY26NixI2QyGZ48eYJTp05h8uTJXLGpWgUFBcHExARdunTR
+mGV69uwZAgMDce7cOezdu1dl2L179+Dh4QEDAwNs3boVBgYG/BKpytbLFy9eFDq8adOmaNWqVYHH
+rOjo6AKnad++PRo0aFBh63ZR2w4xzBAx0GiAFy9eYNKkSThx4gSaN28OW1tb3L9/H76+vsjKyoKL
+iwsePnwIuVyuEmb+/PNPBAYG4qeffkJCQoLKPLW0tCCTyaTnSqUSQgiVcT7++GOp3Avy77//wsfH
+B9u2bUNCQgKaNGkCS0tLGBsb48GDB9i+fTuePHkCU1NT2NnZISwsDD179mSYoWqlVCoxd+5c2NjY
+YM+ePdW+PFu3bsX27dtx7do1CCFgaGioNs6aNWtw4sQJAICtrS08PT35RVKV+Oeff+Dn54ddu3ap
+HCM6deqE8ePHo1+/foWGmaCgIPz22284fPgwAMDAwACzZs3CRx99JIWZ8qzbJdl2qIoIohrAwMBA
+7N27t1qXQalUCk9PT1G/fn1x+vTpYscPDg4WAERiYmKNLfeMjAxhYWEhAIjJkyeLjIwMaVh2drbY
+vHmzqFevngAgOnfuXOA8QkJCBADpLyQkpMDxsrKyxO3bt8WSJUsEADFr1qxCl+vEiRPC2NhYABAt
+W7YUe/bsEdnZ2SrjKBQKcfToUfHGG29I7+3q6lqrtgtzc3Ph6+tbp/YFbdu2rdGf+fjx4wKA0NHR
+EY8ePar25VEqlSIpKUl07dpVABCGhoZq42zbtk0AEDKZTJw9e7ZWr1/e3t7Czs6uTm1T3t7ewtnZ
+WaOX0cfHR+U48tNPP5V42i5duggA4sSJExW6bpdk2yEhIiMjBQARGxtb4fMODAwUBgYGQotxjqh0
+V2imT5+OESNG1Ime/7Zt24br16+jSZMm2Lx5M/T19aVhOjo6cHd3x5kzZ1CvXj08evSowHlYWVmp
+PC/oVzQA0NXVRceOHfHVV19h8uTJyMnJKXC8H3/8EcOGDcPz58/RtWtXREREYMKECdDR0VG7+uPi
+4oJr167BxsYGAJCens4VmarVhg0bAAA5OTnw9fXViP1a48aN0b1790LHmT59Oi5fvoyoqCi8/fbb
+/BKpys2bNw9vvvmm9DwkJKSkP9gjLi4OdnZ2GDx4cIWu2yXZdqhqMMwQMdAU6siRIwAAU1NT1KtX
+r8Bx3nrrLXzzzTdISUlBSkqK2nAdHR1oaZVuVzNu3DhkZ2ervX716lVMmTIFSqUSDRs2xNGjR9Gy
+Zcsi59WkSRMcOXIERkZGSEtL40pM1ebOnTsICgqCtrY2AGDLli3IyMjQjJOBYrZRKysrdO3alV8i
+VQu5XI7FixdLz/39/Uu0Pw8PD8fz58/x0UcfVdq6XdrjGzHMEDHQVKHHjx9LJ2FFXdWYPn06WrRo
+IY1fUJmVhqOjI5YuXarymlKpxIwZM6QrNvPnz0f79u1LND8jIyN4eXnxygxVK19fX1hZWWHChAkA
+gPj4eDYYJiqh0aNHw8TEBACQlJSEHTt2FDvN999/j+bNm2PkyJEsQIYZIqqLgaZJkyYAgOTkZCxY
+sKDQ8XR1dTFp0iT8+++/5X7PFy9eQF9fXzpo5Tlx4gQiIiIAANra2nB3dy/VfCdNmoSsrCyuvFQt
+UlNTsXPnTsyePRuzZ8+WXv/vf/+r1vlFVRBCQKlUlmm6kirL/IkKI5fL8fHHH0vP165dC4VCUej4
+KSkp+PHHHzF58mTo6elV2Lpd1m2nPNNyW2KYIWKgKaP8N9Fdv349Pv744wKrfwGAj48PbG1ty/V+
+SqWy0J7ifvzxR+mxra0tjIyMSjVvIyMj7Ny5kysuVQt/f3/I5XKMHTsWlpaWsLa2BgBER0fj7Nmz
+RU67f/9+jB8/Hi4uLnBxcVGpbpOUlIS5c+di+PDh0vBXr2rmd+zYMQwcOBCvvfYaOnTogJ49e+LA
+gQNFvn9KSgp++OEHDB48GOvWrSvyRC0wMBDDhw+Hqakp2rdvj8aNG6N///7w8/MrtB0cUUlNnTpV
+6j757t27Uk9lhR0z0tLSMH369HKv22XddgAgOzsbvr6+sLa2hr6+PnR0dNC1a1f4+PgU+gMbt6XS
+p0Qi9mZWCb2c1YbezOLi4sRrr72m0ouMhYWFCA8PL9V8tLW1penv3r1b6HghISHCxMSkwGGtWrWS
+5uHp6cmNgr2Z1RhKpVJ07dpVeHl5Sa/5+/tL63NJepK6d++ekMvlAoAYPHiw2vDo6GhpOytofgqF
+QsyePVvI5XKxZcsWqfe/6OhoYWFhIRo1aqTWI1N0dLQYO3as0NfXl5b1m2++KXD50tPTxejRo4We
+np7YvXu3yMnJEUIIcfv2bdG3b18BQPTo0aNSejRib2a1vzez/D7//HNpfezbt2+h21zPnj1Fv379
+ChxemnW7LNtOnqdPn4o+ffqI6dOni8jISPHo0SNx6NAh0aJFC2n509LSauW2VJW9mTHMEMNMJQWa
+2hBmhBDi999/F82bN1cJNADExIkTxcOHD0sdZg4ePChCQ0NV/n799VexZcsW0a5duwLDTHJyssp7
+r1mzhhsFw0yNcfbsWSGTyVSCfGZmptS9OABx8+bNYudjampaaJgRQggTE5NCw8yiRYsK3XYeP34s
+GjRooHZClpqaKjIzM8Xu3buLPOFTKpVi7NixAkCB301KSoro3LmzACA6deokUlJSGGYYZsrswYMH
+UrAHIMLCwtTGuXLligAg/P39C5xHSdftsm47Qry8fUGfPn3EqFGjhFKpVBm2f/9+6X29vb1r5bZU
+lWGG1cyIWOWsSD179sS1a9fUuq3cs2cPOnfujG3btpWqHr2bmxtsbW1V/vr37w93d3fcu3evwGni
+4uJUnjds2JArHdUYGzduhIuLC9q1aye9pqenh5kzZ6pU4yyOXC4v0/C//voLK1euhKGhYYG9Or32
+2msYMGCA2usNGjSAnp4e7O3ti3zfoKAg7N+/H02bNsXUqVPVhjds2BA+Pj4AgFu3buE///kPVwoq
+s9atW2PcuHHS8++++05tnK1bt6Jp06Z45513CpxHSdftsm47ALBz505cvXoVc+bMUesEZ/jw4dKt
+DrZu3Yrc3FxuS+XAMENUSYHm6tWrteaztW3bFmfOnMG+ffvwxhtvSK+npqZi5syZRd4X5lU3b95E
+RkaG9Jeeno6kpCRcvXoVffv2LdE8imr0SaRJYmNjcfjwYZVG/3lmzpwpddO8a9cuJCQkVMoyrFu3
+DgqFAgMHDoSurm6B4zRq1KjQ6V+9h9Or8u6XY2VlVej8hw0bBmNjY7WTN6Ky+OSTT6THP//8M2Jj
+Y6XnycnJ+OmnnzBx4kSVe6OVZd0uz7bj5+cH4GX30Bs3blT58/PzQ7NmzQAACQkJuHHjBrclhhki
+zQs0RfX+VVM/29ixY3Hjxg0sXbpU5VfgPXv2YPLkySW6QqOnpwd9fX3pr169emjcuDF69+6NzZs3
+FzhN06ZNVZ6/eqWGSFNt2bIFb7zxhkpnGnlatWoFNzc3AC9v6Lp9+/YKf38hhNRIunPnzhU+f6VS
+iQsXLgAAmjdvXuh42tra6N+/P4CXXVJHR0dz5aAy69WrFxwcHKR1MP+VzZI0/K/sbSc5ORnXrl2D
+trY2njx5gpiYGLW/MWPGwNPTE56entDS0uK2VA5ybhJElRNoHj9+XKKeTmoaPT09LF68GEOGDMGI
+ESPw9OlTAMBPP/2EUaNGYcyYMWWed7du3dCqVSu115s0aQJjY2M8f/4cABATE8MVjTReRkYG/Pz8
+oFAoCr1LeHx8vPR4w4YNmDdvXrHVyUrjxYsX0jZa1NWXsoqPj5duXljcL8SdOnWSHj98+BA9evTg
+SkJl9umnn0on/35+fliyZAkaNWqErVu3wtbWFt26dau2befu3btSN8xr1qyRrsAW937clhhmiDQq
+0MyZM6dGh5nU1FQ0aNCg0BteWllZITg4GLa2ttKVEl9f33KFGZlMht9++63AYXZ2djh48CAAIDQ0
+lCsZDE9QJwAAGAdJREFUabyAgACkpqbCx8enyLuEr1ixAk+fPsWDBw9w6NChcm1Dr8p/FTMzM7PC
+P2P+Kp95J36FMTQ0lB6X5OSOqCjDhg1Dp06dcOvWLaSkpGD79u2wt7fH9evXK6Qb/vJsO3mhRAiB
++/fvl+gGz9yWGGaINDLQ1GTu7u6YNWsW3nrrrULH6dChA3x8fPDhhx8CAP74448KXYaHDx/CyMgI
+enp6GDt2rBRm7ty5g6ioKJibm3NFI40khMCGDRswZswYzJ07t8hx4+Pj8dVXXwF4eRPNigwz+W8W
++Pfff1f452zevDl0dHSQk5OD6OhoCCEK3fflv/Ff/rZ3RGWhpaWFTz75ROpIY926dYiMjETjxo0r
+ZBsqz7ZTv3596XFISEiJwgy3pXKsC9wciKiwoLJ8+fJix8t/o8yS3GW5pJKSkmBpaSldbndzc1M5
+IBTUgw2Rprh48SIiIiJKVG9/5syZUkPkS5cuITw8vMwB6lWtW7eW5n3hwoVS9TxYEnK5XLoBaFxc
+nNSQuSBPnjwB8LKtUMeOHbmSULlNnDhRal9y//597N69GxMmTECDBg3KPe/ybDumpqZSEPHz8yty
+2ryOdLgtMcwQUSWEmaCgoCLvsAy8bBeQx8bGpsQnWcVZtGgROnfuLB2UdHR0sGnTJmn4rl27cPr0
+6RLPLzExEa6urnj27Bm/XKp0q1evhpmZWbFdvwJAy5YtMXr0aOn52rVrCxwvrzpJenp6gdtYSkqK
+2ut6enpSY+G7d+8iKCioyG20oG21uO33gw8+kB4HBAQUOl5eSHN3d6/xV65JM9SrVw+zZs1Sea00
+Df+LWrfLs+00atRICibBwcHYs2dPgdPm5uZiypQpcHJy4rbEMENElRFm8nauRf1ClL9dUP7uMvNk
+ZmaqXBIv7sQor3rOhg0b1HqAGjp0KNasWSM9d3NzQ2BgYLGfJTQ0FJaWlujXrx9atGjBL5cq1ZUr
+VxAUFIQxY8aU+ETjvffeU9mmCqrWkndl8vLly7h9+7b0ukKhwIoVK6SQk79TAQCYN2+e9NjDwwMP
+Hz5UC/ohISEAXvbClJqaqjI8f7frBTVMnjRpEiwtLQEAmzdvRmJioto4t2/fRnBwMMzMzFSWh6i8
+Zs2aJdUK6NOnDywsLEo8bXHrdnm2HU9PT+nxtGnTsH79emRlZUmv3blzBy4uLsjOzpZ6NeS2xDBD
+RJUQZhISEmBnZ4effvpJpYGiUqnEjh07pBt4LV++vMBfoV8NGzt27EBYWBhiYmJw79493L17Fzdv
+3sTFixexadMm2NvbS20MBg4cqDa/Tz75BPv27UOzZs2QmpoKV1dXuLm54eTJkyq/WKempiIoKAjv
+vPMOXFxcsGzZMnz++ef8YqlSZWZmSr8Ul6ZXsvw31FQoFJg3b57KjwB5PywAQHZ2Nuzs7ODl5QVv
+b2+Ym5tDCCF1VRsWFob3338fx44dA/CyobSHhweAl1VxevXqhRUrViAwMBDbtm2Do6MjDAwMpBO6
+7t27q9zQM/+PGbdu3VJbdrlcjkOHDqFTp06Ij4/HhAkTpAbQefuQiRMnok2bNggMDKyQKkBEeVq0
+aIGJEycCAGbMmFGqaYtbt8uz7YwbN066uWdOTg48PT3RvHlz9OjRA6ampjAzM0NSUhL8/f2lHz24
+LZWRIKoBDAwMxN69e2vUMgcHBwsAIjExsUaWuVKpFAYGBmLJkiXCy8tLmJmZiRYtWoghQ4YIV1dX
+0bZtWwFAmJqaip9//lltej8/PzFgwAChra0tAJT6z8DAQOTm5ha6fHFxcWLJkiXScgAQMplMGBoa
+imbNmgkAok2bNmLRokUiLi6uVm4X5ubmwtfXt07tC9q2bauxn3n//v2ic+fO0vool8vFqFGjxPHj
+xwud5q+//hKTJ08W7dq1U9sGevfuLX755ReVbXLp0qVCLpdL4zRr1kxs27ZNCCGEs7OzaN++vfD2
+9hahoaEq249CoRDffPONaNq0qcp7mJiYiHPnzon33/9/7d1/dNV1/cDxF9vdxgZzTn4VET/q2DIC
+A9EgEaxOBxI9kOmRUA6dOqEQET+UMDrQOXKC6mQHS4qAlHM8mgdPHTMMzBN4GHFU1CUJyVkonFJ+
+jDMYsrGN7dMfftnXxa8pG9y7PR5/7W73fnbv+3Mv7LnPfX12e9KtW7dk2rRpyebNm5OGhoZk06ZN
+yYwZM5Lu3bs3e41NnDgxeeyxx055LEeOHElmz56dFBYWJr17906mTp2afOMb30j69euX3HXXXRnx
+Opw/f34ycuTIDvWamj9/fjJu3LiMfgw7duxICgsLk6qqqhZd//08tz/Ia+ek+vr6ZNGiRUlhYeEp
+/7/94Ac/SGpqak57/9rDa+mkV199NYmIZM+ePa2+7T/96U9JUVFR0ilp7WlAaAOXXnppLF++vNlb
+MdJdaWlpXHfddXH48OGm39xkmqeffjpuuOGGk7/4iF27dkVZWVkcOnQo8vPzY9CgQTF06NCznnb2
+AvxCJnbt2hX/+Mc/oqKiIhobG6Nnz54xcODAKCkpadfvJx48eHBMmzat6TeHHUG/fv1i/vz5Heox
+/6+DBw9GWVlZ5Ofnx7Bhw5r+yvnu3bujf//+Z3091tbWRllZWVRUVETPnj1jyJAhkUqlory8PPr1
+63fOv4jeErW1tfH3v/89Kioqori4OK688spmZ3dKZ/fee2+UlpbG5s2bO8zz6d57743t27e36C27
+6ezll1+OoUOHttn2z+e1U1NTE2VlZVFZWRk9evSIwYMHt+iEOZn8Wjpp+/btMXjw4NizZ0/07du3
+Vbe9bt26uP32252aGTizkyET8e6ppktKSpr9sa50kK73C9pKjx49Tpkni2jZKVrz8vKaBpPf6+Tb
+SltDXl5eXHPNNXYUF1Rbhsz5vnby8/ObnfnTa6l1mZkBAADEDAAAgJgBAAAQMwAAgJgBAAAQMwAA
+AGIGAAAQMwAAAGIGAABAzAAAAIgZAABAzAAAAIgZAAAAMQMAAIgZAAAAMQMAACBmAAAAMQMAACBm
+AAAAxAwAAICYAQAAxAwAAICYAQAAEDMAAICYAQAAEDMAAABiBgAAEDMAAABiBgAAQMwAAACIGQAA
+QMwAAACIGQAAADEDAACIGQAAgDSTsgTQttauXRtFRUUWgla3ffv2DveYKyoqYu3atdG9e/cO85gb
+Ghri+PHj0aVLF096/163iXXr1sXatWs9AVqguro68vPzo1OnThYjTf6fEjPQVi+u1Lsvr9mzZ0dO
+To4FoU3k5eV1qMdbVFQUGzdujLKysg7zmOvq6qK6ujry8vKic+fOfohqQ5WVlTF69OgO9Zjz8/Mj
+IuLOO+/0BDiH2traqKmpiYKCgsjNzbUgLXDkyJGIiMjKars3g4kZaCPDhw+PJEksBLSit956q8M9
+5rq6uli1alUsXbo0qqqqYubMmTFr1qwoLi72hOC8LVy4MBYuXGghzmL9+vUxd+7c2Lt3b9x3330x
+Z86cpgjk4jMzAwBpLDc3N6ZPnx7l5eXxox/9KB566KEYMGBALFq0KCorKy0QtJHy8vIYP358jBs3
+LoYPHx67du2KBQsWCBkxAwCIGkhPhw8fjrvvvjsGDhwYVVVVsW3btli9enV8+MMftjhiBgAQNZB+
+Tpw4EcuXL4/LL7881q5dG2vWrImNGzfGkCFDLI6YAQBEDaSn9evXx5VXXhnf+973YtasWfHPf/4z
+Jk6caGHEDAAgaiA9mYsRMwCAqIGMYi5GzAAAogYyirkYMQMAiBrIOOZixAwAIGogo5iLETMAQDuN
+moULF4oa2iVzMWIGAGjnUfPwww+LGtoVczFiBgAQNZBxzMWIGQBA1IgaMkp5eXlMmDDBXIyYAQBE
+jaghM7x3LubIkSPmYsQMACBqRA3pzVwMYgYAEDVkHHMxiBkAQNSQUczFIGYAAFFDRjEXg5gBAFo9
+apYsWSJqaDPmYhAzAECbRc20adNEDW3CXAwt1SlJksQykPZP1E6dYvDgwTFp0iSLAZCGGhoa4sUX
+X4yNGzdGbW1tXHvttTFy5EjzDLwvhw4dinXr1sXOnTvjqquuijFjxkRhYaGF4RTr1q2LzZs3ixky
+wzXXXBNVVVXRtWtXiwGQxpIkiYqKiti3b180NDREz549o1evXpGdnW1xOGsMv/3223HgwIHo2rVr
+9OnTJwoKCiwMZ1RdXR1du3YVMwBA66urq4vVq1fHkiVLoqqqKmbOnBmzZ8+O4uJii0OTEydOxG9+
+85tYtGhRFBQUxI9//GNvJ+N9ETMAgKjhglu/fn3MnTs39u7dG/Pnz485c+Z4WyJiBgAQNaSv8vLy
+uPvuu+Opp56Kr3/967F48WKnWeYDczYzAKDNOfsZ/l4MbcGRGQDggnOkpuMwF4OYAQBEDRnHXAxi
+BgAQNWQUczFcKGZmAICLzkxN+2AuhgvNkRkAIO04UpNZzMUgZgAARE3GMReDmAEAEDUZxVwM6cDM
+DACQ9szUpA9zMaQTR2YAgIzjSM2FZy4GMQMAIGoyjrkYxAwAgKjJKOZiSHdmZgCAjGempnWZiyFT
+ODIDALQ7jtR8MP87F7N06dL42te+ZmEQMwAAoiZ9mYtBzAAAiJqMYi6GTGZmBgBo98zUnMpcDO2B
+IzMAQIfTHo/UvPnmm/Hmm2/G9ddff9brmYtBzAAAdPCoOXbsWDz77LMxfvz4i/44du3aFSUlJU2P
+KScn57TXMxdDe+NtZgBAh3U+bz/75S9/GRMmTIilS5de1Mewc+fOuPbaayM7OzuysrLigQceOOU6
+5eXlMWHChBg3blwMHz48du3aFQsWLBAyZDxHZgAA/k9Lj9QcO3YsPvrRj0ZlZWVkZ2fHlClTYsWK
+FZFKpS7o/S0rK4svfOELcfTo0Thx4kRERHTp0iXeeOON6NGjRxw+fDgWL14cv/jFL+Jzn/tc3H//
+/TFkyBA7GjEDANBRo+anP/1pLFiwIOrr6yMiIicnJ0aOHBl/+MMfoqio6ILcx5deeik+//nPR3V1
+dTQ0NDR9Pjc3NyZNmhRXX321uRjEDACAqPn/qLnzzjtj0KBBp7wNLTc3NwYMGBDPPPNM9O3bt03v
+15YtW2LMmDFx/PjxZiHzXgUFBfH973/fXAxiBgBA1LwbNQcPHoz6+vrTRkROTk5ccsklsWHDhrjq
+qqva5L5s2rQpvvzlL0ddXV00Njae9jpZWVkxZMiQ2LZtm52HmAEAIKKysjL69OkT1dXVZ7xOdnZ2
+pFKpePzxx1v9TGcbNmyI8ePHR319/RlD5r1B87vf/S5uvfVWO452y9nMAABaaNWqVU1zMmfS0NAQ
+dXV1cfPNN8eyZcta7Xs/9dRTceONN571iMx7JUkS3/3ud+P48eN2HGIGAKAjO3bsWCxZsuScMXMy
+JBobG2POnDnx7W9/+4xzLS31xBNPxM033xwnTpyIlr6pJkmSePvtty/6qaNBzAAAXGTLli2Lo0eP
+vq/bNDY2xsqVK+Omm26Kd9555wN930ceeSRuu+229xVEeXl50alTp4iIeO2118JUAe2VmRkAgHOo
+qamJgoKCiHj3rGV1dXXv6/a5ubnxiU98IjZs2BC9e/du8e1Wr14dU6dObdF8TCqVirq6uhgwYEBM
+mDAhxo4dG6NGjYrOnTvbgYgZAICObN++fbF169bYunVrPPfcc1FWVhZ1dXWRl5fXooH8nJycKC4u
+jmeffTYGDRp0zu/34IMPxne+850zHlXJy8uLurq6yM3NjS9+8Ytx0003xdixY6N///52FmIGAIAz
+q6+vj5dffjmef/75+Nvf/habNm2K/fv3RyqViuzs7KitrT3lNllZWZGXlxe///3vY+zYsWfc9s9+
+9rOYN29es0By9AXEDABAm/nPf/4TL7zwQmzZsiU2bdoUr776atTX10fnzp2jtra22VGWX/3qV3HX
+XXedso377rsvFi5cGBHvnua5sbHR0RcQMwAAF1ZdXV288sorsXXr1ti8eXOUlpbGgQMHmr7+pS99
+KdavXx9ZWe+ek2natGnx61//OiIi+vfvH1/5ylccfQExAwBcKPv27Ys//vGPFuIMDh8+HP/617/i
+6aefjoaGhujbt2/ccccdsX79+vj3v/8dH/rQh2L06NHRrVs3i3UWI0eOjE996lMWQsyIGQCg9ZSW
+lsZ1110XvXr1ii5duliQczj5N2mysrKaTqfM2e3evTuWL18e06ZNsxgdXMoSAABt4fXXX4+ioiIL
+QasbPHiwRSAi/NFMAABAzAAAAIgZAAAAMQMAAIgZAAAAMQMAACBmAAAAMQMAACBmAAAAxAwAAICY
+AQAAxAwAAICYAQAAEDMAAICYAQAAEDMAAABiBgAAEDMAAABiBgAAQMwAAACIGQAAQMwAAACIGQAA
+ADEDAACIGQAAADEDAAAgZgAAADEDAAAgZgAAAMQMAACAmAEAANqBlCUAADh/VVVVsXfv3vP7wSyV
+ik9+8pOxf//+OHjw4BmvV1xcHB/5yEdO+XySJPHaa6+d9jYDBgyILl262FGIGQAAmvvLX/4St9xy
+y3lto2fPnrF///7YvXt3rFy5Mh5++OFIkqTp6yUlJTFx4sQYNWrUGWPmz3/+c2zZsiWefPLJiIgo
+KiqK6dOnx4wZM8QMYgYAgFPV1NRERETv3r1jwYIF8dnPfjYuu+yySKVSUVpaGpMmTYqIiI997GPx
+3HPPRZIkcfz48XjrrbfiySefjGXLljVtY8SIETFixIi44oorYt68eU3f44c//GFMnDjxjPchKysr
+7rnnnrjnnnti4MCBsWPHjnj88cdjzJgxdhBiBgCA06uuro6cnJz461//GiUlJc2+1qNHj6aPc3Jy
+ok+fPk2XL7/88hg9enRceumlsXjx4ma3mz17djz00EOxc+fOiIgoLS09a8yclCRJVFRUxMiRI4UM
+7ZoTAAAAtIKampoYP378KSHTUjNmzIjGxsZoaGho+lwqlYqFCxc2XX7kkUfi2LFj59zWCy+8EAcO
+HIgZM2bYMYgZAADOHTPjxo37wLe/7LLLYtiwYXH8+PFmn7/llluiX79+ERFx5MiR+O1vf3vOba1e
+vTq6d+8eEyZMsGMQMwAAnN28efNiypQp57WNLVu2REFBQbPPpVKpmDVrVtPln//8582O3vyvo0eP
+xqOPPhpTpkyJvLw8OwYxAwDAOX6oysqKTp06ndc2srOzT7uNb37zm1FUVBQREW+88UbTmcpO59FH
+H41jx47Ft771LTsFMQMAwMVVWFgYU6dObbp8//33n/Z6SZLEihUrYtSoUR94dgfEDAAArWrmzJmR
+Sr17ItotW7bE888/f8p1XnrppXjllVeahQ+IGQAALqo+ffrEbbfd1nT5dEdnVqxYEcXFxfHVr37V
+giFmAABIH3PmzGn6+Iknnog9e/Y0Xa6qqorHHnssJk+eHJ07d7ZYiBkAANLH0KFD4/rrr4+IiMbG
+xnjggQeavmbwHzEDAEBamzt3btPHK1eujKqqqqbB/xEjRsSnP/1pi4SYAQAg/dxwww1NZyo7evRo
+rFq1KrZt2xZlZWUG/xEzAACk8Q9vWVnNZmeWLVsWDz74YFxyySVx6623WiDEDAAA6Wvy5MnRvXv3
+iIjYu3dvrFmzJu64447o0qWLxUHMAADQehobG1t1e/n5+TF9+vRmnzP4j5gBAKDVvfPOO00f19TU
+tMo2p0+fHnl5eRERMWzYsPjMZz5joREzAAC0rmeeeabp471798aOHTvOe5u9evWKyZMnR0QY/EfM
+AADQeg4dOhRTpkyJIUOGxIoVK5p97eqrr44bb7wxfvKTn5zX95gzZ04UFhbGxIkTLTgdUsoSAAC0
+vm7dusWaNWva9HtcccUVsWnTpigsLLTgdEiOzAAAZLChQ4daBMQMAACAmAEAABAzAAAAYgYAABAz
+AAAAYgYAAEDMAAAAYgYAAEDMAAAAiBkAAEDMAAAAiBkAAAAxAwAAIGYAAAAxAwAAIGYAAADEDAAA
+IGYAAADEDAAAgJgBAADEjCUAAADEDAAAgJgBAAAQMwAAgJgBAAAQMwAAAGIGAAAQMwAAAGIGAABA
+zAAAAIgZAABAzAAAAIgZAAAAMQMAAIgZAACA9JCyBABAW+jVq1cUFBRYCFpdZWWlRSAiIjolSZJY
+BgCgtVRUVMTGjRstBG1q6NCh8fGPf9xCiBkxAwAAZB4zMwAAQEZKvb79xaYLJYOutiIAAEBGcGQG
+AADISP8FpxZnWS0U37cAAAAASUVORK5CYII=
diff --git a/Documentation/DocBook/media/fieldseq_bt.gif.b64 b/Documentation/DocBook/media/fieldseq_bt.gif.b64
new file mode 100644
index 0000000..b5b557b
--- /dev/null
+++ b/Documentation/DocBook/media/fieldseq_bt.gif.b64
@@ -0,0 +1,447 @@
+R0lGODlhcwKfAucAAAAAAElJDK+vr0gSElYMDC8kDV5bEBcHOwYGSEQODmEaGgoKOBkTVC0tVyAg
+aDcJC6Ojoys8DAAYGqSkxV9fFFtdEJmZmUA4EF0wMAAAcAoTHTZHJ0gYGAcMTwcSO29ISFUHB2AV
+FXd3YAcHMRUVQiIAGg4HT3t7eywOJ3d3dwcHSEEgABMuDnd3OGpkSQAAYlZGBzEEBGJlDCstCxwc
+WQcHSzkRGWBtYC0AACA3ABAKNhAQTTMwDA0VQD4AAEYVFVVVVSQMJQULOB8fQScnYBgYRD5VPmZm
+DEZRB2ZiDAoKSgAAVAwQOH5+lBwcS+7u7hoaST4+X3d3WACPADMzMyBRIDgAAGBgc0JCEHEAAEwN
+DRkwDAoKOR8kPZR7eyA1IABpABgNQBA9EABVAAsLRww/DAwMPgBNAENDCgc9B8zMzAUFQQBDAD4M
+DAwOKgAAcQA5AEtLFYqKAA0NTC8HBxEREQgfCAArAAApACIqMkkGBhoqKnwAAAsGQ6qqqkoKCg4O
+MlkcHAoZJCcrW6SkpFQAAAAAOBAOSwAVGh0ROgMPHWZmB00QEGUAAFQaGjEyC2w4OLe3n4qKioiI
+iBAVMC4uXhkZUGIAAHJYWHd3AAAAPhAQUQUGL0BAIGggIBgAGkIVFV9fEAwcJR8KJA8MU9EAAAcH
+VRoaYWhoaDcAALu7AGZmZnAAAGRkZGQVFVhqWD4KCgwOUzMzDAAAmgklBzEHBzExClhYWBMTPAYJ
+Qy8fCFpaB///////ACISRExUDUQrDAwMVhISSEYYGHd3IDhcOERERElJAAkPNTsHF1hYckgGBj05
+CFYAADg4OCAVO0hCDDAwMLu7ilpaDR8qCDg+EBxGHN3d3REGNjo9CDQ8DBwYRGZmHFMAABQ+FBE+
+ESIiIhs+BxU0FWVeBw04DYqKsxAsEB8hQAwuDAc2BwwqDAoqCgcIL1dMDQAA0Q0iDQwiDAckBxAQ
+EDwAAAAAU0JCDAkJPru7u5oAADg4bAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAALwALAAAAABzAp8C
+AAj+AHkJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
+MGPKnEmzps2bOHPq3Mkz5z0AQIMCSNHyZ0WjE5GqNAZAqcGmT+8VZMoL6k6rEp0KfEJl489VBcEB
+GGjBwk8LBJsyFQqU15MUdQDUWfVk4JNVccER5ZXCT8+/gAN/xFp0LEWtDxEfRKo44s8n1/YeJCyQ
+8GO+1xhSbvwxRWaklBEiXoVW4886BNW0FQjkyem6laUKdLqKSuZrQIxtpbLqc51JbsHBFkw8pYUT
+w4uHDK2SM0PnCqHPNiz9uWGFoS1fb7h5u0nQshf+ar2G2isAKn4FpqByHQivn8YkY3WK9RoANXx1
+kwUncBVw5QCSdAsA8jiDXIAeEQYXAMbgp0YKKQAFYVxEPbjgXm/FBURmD1pQxz1qsDfUdAVlCMCG
+vFg4lhpMgbOKYX6IBY5fHX642FBx0cULbnKlUFdQkgS1IxA91mWMBWJRYQF7dZQ2HVBIsdhjbG4R
+WUeE4f3UFlQN6hUiUK1puV1Q93Sp24LglNYlAGmmKGJrvBxJ4YWxiUkmLzGymZ6ULnqXAlgC5Tmj
+QRE2qd+NkwDKCziTwAjcT6rhV1WW28013D11UfHfVrItieCnHw0YVIEHgnoRVutllgJ/X+F54hP+
+fjT1FWR68WXbXV3dU4cxmBoDnGpSaZUqru/NJRUVrV3DXrHBAnCNrrwmN9Cs17jIC2+QUUEUY4Zh
+qyxR52X2IlFwFWSUUU/8tmObXBrzxBNEhveeYVCd5wd5RD1Ra3eVzajGPeCoSq8x4qJ2ZXDghvlq
+rFJBBR6z82aGbLbe+TqbjT9lNtCq4npHUMblqQEOUr3Nmx+VJJIVl7aSToqQan/ZydbMNNds8804
+56zzzjzXbGpFWA0q0IdKWSWrswIhyUuTAtn3L9IE2ddsQUzveF/GKUJtwVirSAZECliLpnUdqmns
+ockml500agCkx625YxnlqUCT6NaU2lbL+zD+AKXNpTHK09Lr5MaCk+h3WrIZ3fDULnc90Nd4b12Q
+VY6zJtmiTnoc+LV+QYiUfuiyS6lB96wHAKDMAa5TZBC27vrrsMcu++y012777bjDHg3N4Dgj7c8P
+YTXzPUUnTvx1Rgl/PFlUgBMv2gMp/zaJawUFtuabT6fUudTFjfxYVk2/uVERCmX38tHrTe/iTa8C
+Th1A4MevyykCsStV9Bt1jfvwy288lQ5bX5zYcr3spU8g1ZMQ4gbCH7HxIlGLetp7/sOYOjxhPthz
+FX40VRDPqA54IISIqAhkoN+F0CFBkxf0FBe2s1XNaVG6D5W08sKrGcY+ZuPa5aB3I7OBI3L+qHkb
+EPPXPbiZzAKHSh8Om6YdAKowQ/TLnlUOhrbwbQeKinNiFaVSuYEskReSI4iNeNFFg1StKgnRH9lY
+NZYUSEopX8NgWpIDlVURJFawSd0J92iQW5DKhHzkTnhS9UALFq9Op6MVuW5VG7RF6oFt46GtrkEs
+pRgjWcvSVbCIhrwdUqtW3tLWvOqClFCCSzbiIxG61PUtlxnDXfBqosusAgQ42TGSHwPAj2TDSqbs
+kkS1rMst/zdLxjkMYza6JMWmshdNQqx0fAkYGanjyW6J0iiHUkq65DiQV2aGWMFB0T1EmbVAmjMh
+lDwnRRS0MvwcclI/WZCOTAQnpSwoPgD+mIRW6EnK6zyBKYZKmozQYk/vqIiKPEKoWNRAHtQkdC9W
+TNn4DPMlKkIllldyYy61mKK1UAE/AI3aQq2CJDbBhW2oXJFH/YeyekmlodeSYUnb5BaAIrEgk3CP
+QNlUmgi55UVNMoxPNwpJd95HMk5p1Ojsghe5lGwrTa1V0nSqzqpadSXVmckOr7rHdAEyNbDRVU+A
+MDiumvWsHclqTO6xKbSeMAVlPcgqWgMvReWkWm7Nq14tota9+vUjb2FILF/FE7P89bCITaxiF8vY
+xjr2sZCNrGQnS9nKWvaymM2sZjfL2c569rOgDa1oR1uSfxHvtKhNrWpXy9rWuva1sI3+7WkBaVrZ
+2va2uM0tbNOo29769re7FQ1wh0vc3/K2uMhNbnAN4hrlOve5p21ZQvIxi+pa97rYza52t8vd7nr3
+u+CtbgZUOBBP4OO86E2vetfL3va6973wja98z1uIhHBDFfjNr373y9/++ve/AA6wgAeMX24kxBpg
+SLCCF8zgBjv4wRCOsIQnTOEEWyMhSwivhjfM4Q6DlwaiYcV8R0ziEptYvp5gSD7cweIWu/jFMI6x
+jGdM4xrb+MYsngV5BeKJUvj4x0AOspCHTOQiG/nISE6yjy+REGL04slQjrKUp0zlKlv5yljOspaf
+TIyEVGEKYA6zmMdM5jKb+cxoTrP+mtcM5iok5AU4jrOc50znGztANPhQsp73zOc+JznFC1lxnQdN
+6ELTWMcI6bGfF83oRhuZyQhx8pYnTelKWzrLXUbIl9nM6U57+tNqdjNC4GzoUpt60HdeTJ4dzepW
+LxrQChH0qWdN60PvmBeKdrWud/3oJl/618AONpYzfZBNg/rYyE72mUV9EFLX+tnQdkeqSbdqXlv7
+2qWAdULOYYZue/vb4A63uMdN7nKb+9zo7jYXIIAQDhDg3fCOt7znTe962/ve+M63vt9di4TI4ggA
+D7jAB07wghv84AhPuMIXDnBZJOQd6Ii4xCdO8Ypb/OIYz7jGN87xiL8jIexIt8j+R07ykqPbDQiB
+wB/2zfKWu/zl+uaAiqNNc1oj+iC5xrbOWw3pg0ha2EAP+qWJbRBjK/voSP80sw3i7Jo7ndDTNle1
+d051RmsbIbJ+utbnfHOD5LzqYN9zzw3yc6Gb/exWJnpBjJ70trvdzEsvSNO3TncbR/1jUw+73pF8
+9YNkve6Al3HXC/L1vRt+yGMvSNnRznjGq50gbH+75N8ed4LMPfCYb/Hdp5X3w3v+x303yN8zn/nB
+E6Twn/d84gmy+Ma7PuiPH0jkJ0/7o1d+IJcnfeA3P5vOp/7woS/IEOZA/OIb//jIT77yl8/85jv/
++cUnBEJ+oIXqW//62M++9rf+z/3ue//74K8+LBKChWmY//zoT7/618/+9rv//fCPv/mxkBBzkOP+
++M+//vfP//77//8AGIACeH/mkBD2AH0ImIAKuIDPxwQIQQjhF4ESOIEUGH4/MHO6p3umV16/14Gr
+NxCt93oi+GuxJxCzV3soCGq3JxC5l4F0x3vv4XsdqHfBRxCj54J0t4E8NoOp94ECEYIjGISTVoK8
+cIIpeIRstoK80II4+HQweA8yyINVV4MDcYNN+HQ6iGtSqHq+JoReWGlEaIRIOIbL9mZXuHt4toXA
+h4FnmIO3hnpquHM+yAtA+IV2OGVhSIZ6mIRm2IYvmIZxSIMMAQUvUIiGeIj+iJiIiriIjNiIjviI
+kFiIS8BuB5EA83CJmJiJmriJnNiJnviJoBiKoniJOJAQ2ZAJqJiKqriKrNiKrviKsBiLsjiLqJgN
+CREPbJCLuriLvNiLvviLwBiMwjiMxJiL8ZAQhhCJyriMzNiMkDgCKZcKoziN1FiN1iiKCcCGfoiF
+bxiIejeHdXiH4tgLebiH5liGo7aNW/eEUeiNvEaFAmGF6lhrWQiH7shr4DiO+oiHXnaO/khmSsiE
+8zhr7HiPU6iNAwlt9WiQVJeP+/iQ5NiP/ziRUxCQCVlzBcmQOgePvEAEHvaRIBmS3pUBAoAQrsAH
+KJmSKrmSLNmSLvmSMBn+kzI5kyjZDAkRCgSWkzq5kzw5YKGQEGJQYUI5lERZlBQmBhgmkkq5lCC5
+CQghAI1Ak1I5lVRZlTPpCgzRAG+wlVzZlV75lWAZlmI5lmRZlma5lfRQkgehACfWlm75lvBVXwgR
+B3JQl3Z5l3iZl3q5l3zZl375l4BZl3GQEOJwBoZ5mIiZmIq5mIzZmI75mJAZmYYpDgmhCWd5mZiZ
+mZppliTwlCIGl6AZmm2pAAh5kTbXjRqJbQ4JkfpYjhTpjxZpmtCWkalpbRwpj7JZaAtZm9a2mqwp
+jq75muYYm7lJa7TJm7p2m8VJj6iJnLrmm79ph8EpnHpInMtpasfpnKz+xpFOUAPe+Z3gGZ7iOZ7k
+WZ7meZ7omZ7eqQKUaBADAALwGZ/yOZ/0WZ/2eZ/4mZ/6uZ/wGQMJgQa7EKACOqAEWqAGeqAImqAK
+uqAMGqBokBDrkA4SOqEUWqEWeqEYmqEauqEc2qESug4JsQbqOaIkWqImmp4LkHJ6wJ8s2qIu+qL7
+OQCleZ2EtpvayXNdGJ13OJ3UOYbWSaOFlp03anUzCqR0ZqND2mjQqaNCyKM9eoQ/aqR1JqRJ2mfK
+KaW62ZxVumhLyqQj6KRPioJRiqVyRqVbqmdXSqZ1hqRnymdd6qWvB6ZhSntjqqZ2BohtaqUM0Z0n
+2qd++qfmyZ4I8Z7+MFqohnqo+OmfCAGgDdqojvqokLqgD4oQEeqhlnqpmJqpHAqiCCGigPqpoNqn
+KXoQELCiiHqqqFqoMhpodrqmWpqnevamcNp4cjqnklenrUpjZgqrRpamuYpjbMqrSCars4p2tWqr
+boervxpjuyqsQ+ary1pjweqsRUasxWp2x4qsSaes0epizUqtQMaRWrmZ5Fqu5jqWaYkQbCma7Nqu
+cZkQdBmY8jqv9FqvfzmYCFGYkrmv/Nqv/gqZlIkQlnmuBFuw5NqZByEAn+muDNuw+ECaC+GRTDmx
+FDuSamkQJ2mVGruxHAuTNokQONmTIjuyJAtgP4kQQWmUKruyLBv+YUiJEBlWsTI7s9XllAkblR2b
+szqrsVjJqt1qY9MKrkJmrdcKexKprWLahz+rq3gqtEUGrUsLY0HrtD9GtEUrbNmKtMrGrUv7rU4L
+tVHrYlNLtaVgtVcLbFmrtcjGtT/rtULLkVHgAHI7t3Rbt3Z7t3ibt3q7t3zbt3JLA7eGAZ4wuIRb
+uIZ7uIibuIq7uIzbuI47uKCQEJ1ADJRbuZZ7uZibuZq7uZzbuZ77uZTbCQnxBVVQuqZ7uqibuqq7
+uqzbuq77urBbul+QELjgt7Z7u7ibu317DqIRCI/7u8AbvMLruBhAWsZ7vMibvMq7vMzbvM77vNAb
+vdI7vdRbvdb+e73Ym73au73c273e+73gG77iO77kW77me77om77qy1VnBEblsRXNlEGawRa6ARr0
+K0nzEhRCcxDlwxZScRdxYSnRIxRwsr4GfMAPkQJUxQtAYFdDhRhqQCSEhRCh8TBGdMHumx5ptB3k
+gSK4UQcaYxXKssAIXMImPBCqARsXpMF+EBcSxUB0wRVJNDnkZcFEdcPRJB7bcUkFUUuAEysnHMRB
+TFMt7EVpUkRTEVYZVMEChMEGZDV/cyN2IUOpoUtRBMRCnMUHrMD9IRm+kkqE0kCTUcNNjMMvHEVS
+fMYcNcJa3Mbqm8JLIylcDMZ2ARe3VhVskTIzsy0eoxV6BD3+ilEv5vNVblzI3qskMXIx/aTGhUQw
+2EHGH4S/TvFFDrQVVIzCVvzHhrzJ3ptTldO/ZIIYq3LHB3TBTEw622FH0bHDJOzDaMzJsAy+9vFD
+qHzGFyRdCXHKryzJ1+EhGlzJTQM/t2E/IUzKsXzM19s8aSwzvDIzuQzJeJzHzJy/QLG/G1wiTXU4
+kYzM3NzN3vzN4BzO4jzOZpUCr3TOr4TLH4FE6PxKcUXO8BzP8jzP9FzP9nzP+JzP+rzP/NzP/vzP
+AP1YuTPQBF3QBn3QUexXrHPQDN3QDk3QCb1XpfPQFF3RFg0hjtUzGr3RHN3RPGPMZiUzHj3SJF3S
+NwPSXAX+yia90ixN0hm9VXr1Eyh9VTKdWDWNWEOF0/K7VyOCWDd9WD/9V0HtVzl9WEWtWD0N1Joc
+0kvNVUO9V0dN1DutV0ntBZhw1Vid1Vq91Vzd1V791WAd1mKN1YEz01b10zfwCmq91mzd1m791nAd
+13I913Rd12p9A2WdWDl9DWPd137914A91l5AOC/NgWRLZD331DGNFWKotlub1zrNeYdNZLAW1YiV
+1PZItond1DTN2I5Np5Bt1MjTjlRb2VOdV5g92YgX2kLt2Z99q6wt1ZKt2kFm2oW9g7QNZJtt1lX1
+04392ioY21A92rkdroTdWEnNCKm63Mytn8sg3IsdHur+oKnUXd3WvaHqAN15ldOE0Nze/d3yyQjH
+zVipXdxLpt1u5dvATXnojVY5DYXm7WO2jdySkdlUu9s27drrva3tfVbvTdpfO96LVd7mjd8+rd/7
+bXv9Xc7EHd/zTd71Hd9lu+BOjeAJnmzMptjb3eDm/eADLhmECt4inqqKOi+8rU4/XanXveIsrqmc
+auJ6fR2lOuI0fqqryhen7VYEXtwGrtTh8dsXvmYZztlW9d8S7uFIHeHx3eOt/eNBruDaE+Oz3eEC
+nuSGXeAU3tlO/uQYnuVFzuHFjeSJldTr6rBmLppyCeMHHh76+q9u/uZw3pgBq+aiPRAKe+Z4DpoQ
+i+P+t80LjpAFgB7ogj7ohF7ohn7oiJ7oir7ogO4DXt7bWOENLTvplL6y3vDo6pTTAtANjN7pnv7p
+oL7ojlDlY67kWB7lay57XN7lqF7nvXfkpH7Zps7jmH5O6r3qxzbkJ35ORu7gsX5YO57bTO5Xt47r
+Slfr5tTrVK4eOY5WST0MbRDt0j7t1F7t1n7t2J7t2r7t3B7tdIDsgfTT8FAG5F7u5n7u6J7u6r7u
+7N7u7v7u5A4P4M5HOQ0BD9Dt+J7v+r7v3D4Mv/5XwU7bwy7RFm7sfNjqf6XsYf7vfhXwqj3w0a3q
+Bu9pui7lr+7rzN7n9u20EJ9XxT7xB0/nCQ/muS3+5rJ+5bSO8MRe8CCPZhUf2Re/7HxO3wNBfRV4
+8zif8903fipP8OFhfwMY9EI/9EQfgAXY83rF3Tq/9Eyf8xeY8TSP26cu8iu/5S0v5PO+Rwpf8gzP
+07Mu7Fl/Qh9/9S4f9iG09bRt8sD+9QJv9iA09mQPd24PPGiv2moP8Gz/8HP/M3Af9wC596ZS95N9
+9w0vGe4Gc4if+Ip/b/2G9B6PFRDXcZI/+ZRf+Rv3cY7vVvW+covf+Z6P+DIH9RCO8mCf+WjV934v
+Zi/v6jEI66L/4aTf9qZ/Vqif+m0G+KAi+IdN+F4f+3o/+0xt9bb/98Cf0iSf9l1P1ZIRAnne/G3+
+meYant5Y8Q1jUP3Wf/3Yn/3av/3c3/3e//3gX/3fgPufoukL6/zoL18hkPyoLRl/HurwH//yj+iO
+XvxaPhCSXun6v/8Sdun2/+UAwUuggG5ZDB5EmFDhQoYNHT6EGNGgI4G8UgComFHjRo4dPX4EGVLk
+yIwAUlT0VErlSpYtXb6EGVPmTJo1VV6qeA/APZI9ff4E2lMnT4FVphxFmlTpUqZNnT6FGlXq0So5
+dwbFmlUryYs58dkEG1bs2JqeKnbdmlbt2oomUZKFG1fuTJwCh7LFm3fk3aJT/f4FHFhqVbtX9R5G
+nBEtr3tf5z6GDNeswMWJLSd2KzBlZM6d6Vr+JXpZtFq+vIwKRp1a9VPCjA2Php11cWPPtW2vnGwR
+Y2zeWzPzwuBJ+HDixY0fR55c+XLmzYWDAt1butDXX6pcx55d+3bu3b1/Bx9e/PUv0aefBzk7kHP2
+7d2/b47h7G709UH+tp/fdWj99kv3r+8/AM+rbEAC6TMwP/wS7E1ABmNz8MHRIpTwsgIrHO1CDGNb
+cEPLKPRQLxBDxGtEEtfS8MS8UlTxMABWuSdGGWeksUYbb8QxRx135DHHSV5rUUQAJumxSCOPRDLJ
+GH/kL0i8LlIySimnNHIVBJ1EDAAtt+SySy+/BDNMMccks0wzm8SSNDPXZLNNN9/0Es00t7r+CE47
+78TTzTkTo7JPP/+U8Yk92XoCUEMPRVLQQdW6BlFHH8VxUUknpbRSSy/FNFNNN+W0U08/BTVUUUcl
+tVRTT0U1VVVXZbVVV1+FNVZZZ6W1VltvxTVXXXfltdfD6rAgIwvqyOiJk1zDyktjkOVFWWYz0mlL
+cPz4qEsqQrtmlToAACcFQaPt8pYvj7WACi2pCFYgbjWyUk5f34V31xSAyAiIVRTbTaeN1ABCSyAU
+5QjIwtRFU1+NDOZlWGo7MuwJbQW9po5VruFFDSqo0EjgZvlbhYqF/aDi3mYBUCOjbd2NN2WVY1UD
+AEWfAGBhXvzYdmCNwFnliSeoWJbhgnf+0xhhqxQDx6PXYObJGHoreqLbktB8zQJwAOblCWCbpeLY
+mc1FeWWvv0YVnHRprugasfO9khdjFBU6458J3qjtZ3m5BgCKAw7Nap1J1mgSjNuCOjQqJtkoBYyH
+LFqgVZgEu3HHUZ1Xca2NIRLtj1JIHO+4gX774Cs1hrvqVaiVW2iNXwsao53AKZmXOuru+nHZZ6+0
+ZUHraD1yg+UWyNg6UPbSZi9PKv1zd50t/umNUH97pxROAnlj2qen/lJ0/Uj8njq+tXyjSeow5m6f
+Nw8d2rSFrlt8tyv6UQ2YW2f/7/IBr2hw7w+/R42iF5e+ev//T9Mk7HWsOnEpRmnDXOz++me++dmM
+gWfJ3PL4cxWlFctp9FufQIZFNasF6yqse90CAThCEmKobqwj39yqxreQpK6BKnyWwowWmu8JJGIT
+q9jFMqi8inTsYyGDm+H+BroSFtGI9qFCBIfmGi4ZA1xbmmEKv8TELS3ridOqFpeoAL9sbatbVBNh
+A8t1LsLBrWVlJOIR1bhGNrbRjW+EYxzlOMdQpcAYd8SjMeCHGAvkEY/pomMgBTlIQhbSkIdEZCIV
+uUhGNtKRj4RkJCU5SUpW0pKXxGR98rRJTnZyS1pD1RM9OUpSnmlVoixlKlXJJVgBwBjPg2UsZTlL
+WtbSlrfEZS51mcsOlUonq9hlMIX+OUxiFvN57Trli4y5TGY2M5jGSJuqelmqaY7KRKC65qey6SkW
+naqao/pmqLbZqXFyqpyb6qapwhmqdWozjaI6p6bimal0UlNryshHPvW5T37205//BGhABTpQguoT
+fu30VGlc0AKGNtShD4VoRCU6UYpW1KIXZagLzJOq0lSioB8FaUhFStAozKeVWsuHO1S6Upa21KUv
+hWlMZTpTmtZUpbMIDULJ+Rpi9MKnPwVqUIU6VKIW1ahHRWpSfUqMjYbyNS+waVSlOlWq1tQBJn3V
+b1JaVa521asyxWlbQHmq0vRUqWdFa1rVilSmFkaB7gwNVL86V7py9aqUiWaqtFr+V772laZhVddY
+TVXWtRbWsIc9alv3k8y4+tWxj13pXXVz0opsFbKXrStgmyVYX/IUsZ8FrWEVO09MlUaumEWtVyVb
+T1L9BgovgG1sZTtb2tbWtrfFbW51u1vYLgECYmVsRWxxDOIW17jHRW5ylbtc5jbXuc8lri2aStbX
+GIK318VudrW72xFg1VV7TW14q6pZnZrTs6FFb3oTO93BPlW875XqavOKKvDC175gzSlnSUVY9fbX
+v0tlb2cbe18Cv1S+lBWIZQu8YHeQV7/WPO9/JRza0b5TnO5lMIMPnFWtEWEWHwZxiEU8YhKX2MQn
+RnGKVfzhDAgAuKoqTShUMWP+GtfYxjfGcY51vGMe99jHMw5FgPf7miWs2MhHRnKSVbwJ77bqNw14
+Q5SlPGUqV9nKV8ZylrW8ZS5HmR4uDmxwBRIHOZTZzGdGc5rVvGY2t9nNb4ZzmeMgZAiHRhNdxnOe
+9bxnLpOgyayqb4YJ7GAx88KsE0b0YSv81oRiWNAE3vB3UfroAhMaxhFOdKbTuuhCn5bS8I20kyf9
+aftamqOY1nSq2UpneDqa1OINNaC15oQa1NrWt8Z1rnW9a1732te/BnatVfDbMF86NGjYRbKVvWxm
+N9vZz4Z2tKU9bWonGw2svnBo1hBsbnfb298G9gL+vKpAvzq1pnZqaA6tanb+F5XTxq6Ip82N2liT
+e9TzPnd+C73udvcbqO8+9YDxTe9xS/PeA78suqmrbn83/N/YxqarEf7Yehu8shPHrMLby3CHOxzg
+6Y43xi9bcb3OGtwnR3nKfT3sFwe8IsiudsxlPnOaT/vabi30tlW+c56fXNx4RTAvFCzyvmpcwBXh
+d8fZ/fGFh5zojiU5fQ/+9MzqG94CSbrSU830jTud6nyNujen/vWvGn3IHNd6u7l+dIHIm+yqLXjJ
+KwJlPtfd7nfP8pdbDvIxx9nvfwd84OE8Z5xfnRd3xnviFV93PwOdwxXxsJIlP3nKn7jFe2+6QGT8
+Y8533vOf73GQC+9ygRT+ufKnR73kmex4SV/87XQ1e52RnvZ+r/3sXn893Fkvatfn3quxbzXaab91
+iMMV976vatjVOXbkSxX42Z798FVte9m3vfldVb49KzKEYHTf+98Hf/jFP37yl9/850d/95VA7M0W
+WhZHgH/85T9/+tff/vfHf/71v3/4y6L4jQ6NEUi/ASTAAjRA9HODuJO63ru+qXq+iBM+6Us06gu+
+42tAm8q+1mK+C5ypBzQ+rJNA4hs9vuMFt+PAmcpAcNrAE4QpDwTA6AvBCfy/nRI4FqSpFBSVcrPB
+mHJBGoTBGJwwCoQ+69vBG1RAsdu+OVDCJWTCJnTCJ4TCKJTCKaTCKlz+QkLAvK4TCCyYhi70wi8E
+wzAUwzEkwzI0wzNEwy7Eghk0r9CwByuEwziUwzmsQiY4wuVjwCJsQasjPUMDQhkcwcwrQT1EwTvU
+vgQjRPzKQrbzwz8MQjbcFNNKxJjCQXZawUTswTb8QUfsLyGEQAucRJWqRFDRwVBsMD4kwazjRAqD
+RHmSOFN0h1H8lN9oAmWwxVvExVzUxV3kxV70xV8ExmC8RfEpr0h8DRGQgmRUxmVkxmZ0xmeExmiU
+xmmkxmQUgVbMlNIQxm3kxm70RmG8AkPUQFkrNBI0x1NhLRUkR8M7Ry1MlXTMwQdTR3YUxHYsFXhk
+J3Dwo33kx370x3/+BMiAFMiB5MdidEWeIciEVMiFZEiC5JpkQsiGlMiJpEiBBIf5OpWK1MiN5Mg/
+WhU16MiQFMmF3KNTAcmRRMmU7MdMYsmWdMmXhMmYlMmZpMmatMmbxMmc1Mmd5Mme9MmfBMqgFMqh
+JMrzuJqKGJZiORbeGQnkAZousaLz4RIsAomWWZr5QSWTQCXisZaFcaUHEoh+OShncZaiNEv0iJyK
+sBd8caCK4Bd/ASMeeiAX8hykrAOZ6Yg6ARgieg3eEZoU2J6RKaNngZkOARILO8vEFA3b6Z2YqQia
+6Z6MwBmd4ZkoqsswgqG/VKKNAJajxMzy8UvjaRZjCMxnmQRwOE3+CZJLxWTN3hAbgSAbGzqbthSI
+taHN1RSezgFLurEbj/ADl1mFq+TL0AjNjKGYnaCCpUGYrKmbkjxMRmvN6ESRpQGmiqCc3cFIzLHM
+udTNJcIgjgjOmXGZFwLNaPpLNMofxzSY5uSFrNmhz5TO+MwLxsQdyqAX7OwI3wGeLhGeLuHKFPpO
+zlyYOkDP8SmMLuEJUTrKqwDMQtkNvxGIGlrN4ZHPCsWL68mewMRP7wEf9VHNy6TL3UyfjvhNLSLP
+BSrOHkocwwgZgzEXLsHL57TQGc0LAapO3UBQuUmgFuqcEPVOi9jMHhIZ8TzO4yFO80QQgzGMlumK
+ERUIIApQxKT+0SkdiRMqSWZpm/cRCbr0UQeSIY6wGrwkUPjsSyTlzvnACMPJCAmdHyml0jf9iCTi
+COxsIlTaziWaIlGKSmnBS41IyozomJFhpe9EpajMiLqhFiAxF15AzUN1zDYNHjiV1Eml1Eq11Ev1
+HzvaxyvNiz7aR0DC1FAV1VEl1VI11VNF1VRV1VVl1VZ1VQNxpliV1Vl9Hme4BVrF1VzdJVRwBlTQ
+1V8FVlniVV8N1mLV1WE11mSl1VtwBmV11llVl1WS1mml1mq11mvF1mzV1m3l1m711m8Nk2KTJmdY
+FUkAAEko13NNV3RVFWfASFFBC4PUFAAgV1UxV3ZNlXtd11X+cVdWiVd5zMF6zVd1tVeCHVh8RZV+
+XZV/dUtIcdg+2UuBVYOHpdgoqQh9tYuK1dgjuViD3diP7ZGOxVeQJdkcqQiFrZqSVVkagR+GTbDU
+g9mYTbEMyCmB9QR8wNmc1dmd5dme9dmfBdqgFdqhxdlCENmK4AbQU9qlZVoe44ajFQhrAIOppdqq
+tdqrxdqs1dqt5dqu9dqptQao5QXTk9myNdtZoIGTpY97YAWiddu3hdu4HdrccFmhg8WWIi+bvY29
+7Yy64AWMbcRV7ESxPY3VMNzDFYzWAFwTDEXJQlna4NvInQu6BZpLJMS8fQvJ1Vyy8FvAVUXBRSzF
+AtzCRdz+0jVdp1Bcg2XcSXTctXWMzYVdm6BccR06WMRczYjd3KWJzjXYzwVd0SLc0xXe4UWK1MXX
+1U3E1vUK3WVemJjd9hOIczCD6aXe6rXe68Xe7NXe7eXe7vXe6eUC9qPXiuAAAjDf80Xf9FXf9WXf
+9nXf94Xf+DXfWhDb9+O/+8Xf/NVf/fM/gQDcd0CHABbgASbgAjbgA0bgBFbgBWbgAH4HsWWH75Xg
+CabgCvbeBBQIlIWAP5DfDvbgDwbh+OUArCrFULxdXtiM5lXhmxBb3/3dtRJdgyVd4qVhwzVeUGxc
+tV3eFV7h5y3hSTzhFOZh5uVdfHXhF9604K3hJV6NGyb+wrsVRR22i9cdYt31YcvVwyCu4uYt4k1E
+YkVTYiYW48Bw4kGE4ijOYNfdYua94jy03ZrN3DWO3S4GwS8GrRjG1xke4z2GijJGXkJU3imW49xt
+Y4EYAjpE5ERWZCrEQnUR2B/QgkiW5Emm5Eq25EvG5EzW5E3m5EiGBbHlwjQU5VEm5VI+wzX0X4M1
+B3Jg5VZ25VeG5ViW5Vmm5Vq25VtmZXMQ2zdc5F72ZUS2wzSuCELo5GI25mNG5k7+ARLG4iLU4kGG
+XToOXDsG41TOYz7G5qjw4zNGY154XCqG5sgtZLvl5mcOZ8mV5iOm5lWz5orQ42yGZ6oQ2z/Ww0Bm
+DHD+PufbGOfaNUVzzue9Ted1Dt0wjueCnoJt5mZ7htx/3tt95uZTbAu9ZWiAbmGBrua/lWGD1miE
+PmOFxueJ5oxx5r4DJOmSNunyW7+IrohhaIOWdumXhumYlumZpumatumbxumWpoP63d+e9umfzr/+
+xWh8hYcyMOqjRuqkVuqlZuqmduqnhuqoNmp4EFsBPOmrxmqSxmBvpg8IeICcBuuwFuuxxulhYGY3
+7mc4xl2Qto2AtujCwmN31miD5mgo9mi2tg2HLme1RmG8rg23fmu1iuu+mOt4ruu7vWu/7gy9PmN/
+VuzHAOzARqvBNo3CNux5fujEfmzIGOfIO9vPTr3+y3PkinAFPjDt00bt1Fbt1Wbt1nbt14bt2Dbt
+ZhDbzWva28Zt0BO9oa4IMfja3wbu4BZurxUDsSVb0EbuyVs9rq4IAWgE2Ybu6Jbu6Y5tVzhrgaC7
+xdPu7c47MGsWgVUAuRXv8SbvoDXadu47wVPv9WbvNiM83hYIcTiD+abv+rbv+8bv/Nbv/ebv/vbv
++RYHsUU87ibwAn+DxmPugWjb8mbwBhdvBbhucm5svhbizZaLyJZspaLsd7ZsJj5sWNRsC5cLxoZi
+xxZxzq3oDJ9sgu7wMf5wUwzxEycLEr9bE5fxsMBwFV8v9K7sFufjF89hYRbkG5eMCKe1nkPyJF/+
+OfEV2AEAgSeH8iiX8imn8iq38ivH8izX8iePAbGFuZoD8zAXc2i7OfjmhXVIhzRX8zVn8zZ38zeH
+8ziX8zmn8zRfB7HVOSXX8z2vgZ9LcF6AAD3Y8kEn9EI3dC0fgAjnZxOmcCKHixzXcXdjcR9fYiBn
+XSm+Z0efcUV/aBvX9M/gcXWO9F7YcErfY0tPXkxf6E+XXU7fa5Vea1a3CUgfdaEqdVMXY1QHZFX/
+aFl3XlefcFjva1+vCVqv9YfjcQ7H9dPV9XrmdWIviwgvAj6ndiVv5O+uCEY49G3n9m7H8mUQ23oY
+83En9zCvB7FVhzpX93Vn93anc3XA82qX953+83OUJQRvx/d873ZGAPYSb3RoB3UzF/VIv/Vlr+Fm
+L8IYB3iXoPE3FvYKX/iWMPZjB7BkN/hKx+yEfvaI/3XH+2FM/HeOd4mJp/iCv/jhRfgdVHiRL4Vx
+zm4Dh3nF07vRFojwdvCbx3nzFlsya++e93n1fm/Ale//JvqiN/qj7+8A5/EBj/mmtzsER1kBWPCc
+p/qqxwcI9/gOS+6tlzzRxnaBKG3qFvuxJ3vXpm0et+3cVvu117HdBlzfHu64l/u539ri5vHj5vq8
+T7HljvrnLvu/B3yxt+6sR2tGf3iWH/kUp3iiMvmTZ/aM7+iNR3zc6PcaD/nJLwWSP/bGd/z+0k15
+G1x5kW/4tD58zGfhUF98Sbf4zhfez2fB0Of4ca6E7aL92rf93FoCvg6ES+D93vf93wf+4Bf+4Sf+
+4jf+4+d9KwDlTGD+5nf+54f+6Jf+6af+6rf+62d+VDZzFmCD7vf+7wf/8Bf/8Sf/8jf/80f/7mcB
+sbWu23f/96d9XFD1QkD++rf/+8f/4w+ECNcrgUUVwAUIXgIHEixo8CDChAoXMmx4UBIASQ4nUqxo
+8aJAiBIxcuzo0aEzAB9HkiyZQiQvAClKsmxpEYAzlzJnItRI8+ZNmzh3ttTJ8+fHkECHejwpUCXR
+pBdhKm3q0KfTqA8jSq1qEKrVqkKzcjX+mnLVvbBix5Ita/Ys2rRq17JVCyBa27hy59KtG7YVgFZ2
+9/LtGxevXr+CBwsGTPgw4rnRACRu7BjtKpQAJlOubPky5syaN3Pu7Pkz6NCiR5Mubfo06tSqV7Nu
+7fo17NiyZ78W+Pg27rCSIOTufViAJAG+h/sFLpw48rrGkzOXC0FS8+hxuVKvbv069uzat3Pv7v07
++PDix5Mvb/48+vTq17Nv7/49/Pjy59Ovb/8+/vz69/Pv7/8/gAEKOCCBBRp4IIIJKrgggw06+CCE
+EUo4IYUVWnghhhlq2N0TFvBizEIgJmSMMSlQoUaIaohI0IofbvgijDGilwKIIF5DhTH+JxbUokE1
+NsTjQC0CKSORRRqp1CQe+rHSkj5a4Acv1/BYIonXuPjhNeCkoOU1JuqYQgoe8rJll7xYQAUVHoLo
+B47gCDTkkXHKOadH1wDByyrXPOFji0vueA8v96xyJZ+8qHHnjR+iGKSLVDzBCzh78uLoE3XQeSmm
+mVrkIxBW+mgoECvtyKiIhVpQolFTYqnllvd8Cqemscp66SSrqEliHacGusqjPQIq6KSGFqrGSh1e
+SeqVkzT6RKWzOvssndcA0OubLq6CY4kFgUnio9eC6aKIXkJZarUgnknFSmvieOex0Lr7Lrzxyjsv
+vQx1CSaYVta7L7/9+vsvwAELPDD+wQUbfDDCCSu8MMMNO/wwxBFLPHF1VOhrgbK8GNuuQiSSqOaH
+HqfLIonoHlRHyYt6uZKKxqBM5ctK4kjFuIMKtAqsCIUpkJ0DiZkzySSOzMvLxqhYUNHUBukyiSiu
+/GbJYlL0hJs3L2os0AQVjWKNIrfrsckGvazjmGiudCrT28ZcdtjG2Ixn1tqK2bNAPzvkcbZde/x1
+yaIS1PLLvLCZI5Qh57jo1FXjuWjhcQu0tYhFgzxQ0ifnKOrgNJuJK5VMG822qG4PhHNFFtedsZnV
+/ugxyHh/CvXlBqGNMpMzFz424hdNknGnAtGoepQz5w58uaMyerPUx0u6s5l+Czn+0D1APPoEEK4q
+Do7jAx0qUJK2odSil8kTTyiLxgN70IrMWzB0sBYBsSgVA/3e4o2H9ziqqsfjKf6Vy4u5vv6Opb50
+XS97AtkeL7o3JnINBAho4p+q8gc8WyFERNGbXvWuRLeKvE8g8TPU9whyD+Hd73gSvNL5jBe/C2os
+g8RL3/8IOBDsVWR3AundAoFXP7KVr3wnXBEF0Wcb6bUQUCLa4EUSRTWBNAl4T4oSkCJoPB2yq4dP
+YN+xnnczfUUJZ6uAkh9IV5FITcpK1GPgzTxYQhP2UH9wWtHzVvTBigAwjEycH0GeKKU1Fq+Nx0Ki
+6q74tQDC0YdfFJwYKUJG0xH+C43EEojiAgiuKf6xisbL00CuMagjWnIidbSZGMGHIkAOkmNa5Jjq
+QIRJnr3tlHE8ZBgNGDyNVa2RToTSHik5ST+uiJSqW2UXNdhJi1jMT5JC40D8tKO9gYt1pRwf0XBl
+xDa6cplqGBRYZDkmCzzhgyk4GioltcymXaloppSkG30YtYssMQVQOiYqBec3ygmtXM48JSpfVodp
+prOf5bomnlxlkTB184biBBIp8cY1w+WNlxUcn97AMTyHtBNKk/ADMpM5z8fVM3IeW6g/tZYjcOQy
+pIUkFUCzScxrKFN6GZVnj5ipt4YS8qHnxJ1HdrcKFPUOmYfaKETPqb+ECqT+oNXUHzA1CaL4xU+b
+h8JYoIbWoiumQGlBfSbw3lhTbRrkfR/sKcd+KkQfUrKXwxSRUampVmQxNZ4LeaqyUsDPqa6CV3zc
+ZU15NsxfclGTbqWIV1tIPhGuAqil/CE6r3SopLZyrcVrqzZ16rS5/i1UY0WWQ/VqU8b+1SJ2+qDH
+cnUsQVlVkq974eggyKgB5jVQRKSeQFNQ2M4mhAq9m91pO3jZVPoRhW+bImu5WhAL1BV2uWoRaW2K
+2dYGUa2sRa3vYugi2WJxIra1Eo1cxr49cVGXp31mc8332gwKN4/FBZzRWgRA5Q42rym0YvXGCyjo
+brNuI6MubQ/yWd/hin3+yd0tXukb3h6yELb5BexGPUqia9XTuwxNF+vAZljJ+c5smWWi7VwkLU/p
+bo6qK5oAzOngaDYNbyup3G6fdmCG1KFwpBIZg7PlYBOTOL19s9zHKhw2+oKuWhtecUEm4eFyeuwE
+2CPRXWc60+zGTrmZa9xIWtxDEx9Zxpml8NZQjLSPCi7Dre2xhgHAYfclmKMLxhZQI4g3hqKtyZd9
+MjQpJuc5y+he+OouR/AFpvmqZ3344t9F/AwmQGfFzvn6iKFTgOfz6FmuIxE0865zj0aPJNGLNk+j
++dyRTNO5057+NKhDLepRk7rUpj41qlOt6lWzutWufjWsY91q0frMUgP+EWSgULIUy4DoHpLhda4N
+4mvKgMPFB/F1rpPHmGFbZiWX6bVlNJcSTWvN1rdeRR0AUIeqHuUy3aYMDvkIhN9KK3d+YwxGmE2Z
+YKfE29MuiLqLrRBkpwS5IsEMiDDz7cnIm91dBcDwXDUZ3ap7Mr+qTL8REm1AneQy1JZ1VFJgyXET
+xCv0ruzASzsQdMNbMg+/OPR0bQEpIwTZvq4DtTh+FE2rPOQVR/m7DeKHybi4m71Tg21XfhCOPwHb
+pX1kMq2dwEh6JebXzjYPha1rlxudIC3398iN3fFuow7kLX+6zn0Hc5DfejLzVAMAlHXFrS+d6WYi
++c7n23OYew/i1gH+e6+eAAAX+yHbbS8IOHjVTVhhnd59L7u/xxTJqfvayldn+cfLjm6s82LcFPdd
+JMFuJca33MoDmQS10b7je9g966Mznc9LDni/P7zpgd9SQvzusl5ZHfFpd/rBDzIJcMyeRfNMU+DN
+LviEqFzufOa6260CDjHVPZPDRwnwjcF6wG888Z7X/d15JmbR5xrsizo87JVekOljXe5+GPlAqIA6
+2zyK8pq2wOBRWVyNAbyotNf10x39fBGO3uOvpz9Bfnxsj+c88Ng3yNWhCPBNiqK1H/vljhpYyQCC
+nP4BIJ9VCrUMYPBFHLvM1ptgHvIxn/ykX/Npn+nlXu4xXrAh2+/+NN3/4Z/8ZAzWgZ/3zV8HOiAK
+ZtJvMZGtnYn8yBX8lZ73XBoIkt79QZ8Ikh7YQUnrZR8M+o7NAF+5EWD0TZ0HvuAR9pyxSeAENgXc
+EY3K3Am9SeDY6WC7VcbdXcZKAB/XBaFIIBvVeMgJfiFljKFl0BrWWVbjsYsIPtvzcd1FnQyUAEHG
+jJC/ieDIjV/HVUbsseFkiMjTlaEO+mAKoFwRbhywGSJSgKCQcY+tSWDBZeD26aAdQl8VSkWa+EHV
+cN6jbCHzTQLK8KAL+qATxmCUTN/+sRspruHfEcQqVM3TyZ1lPIr4DRegmF8eDd5ejQmvTF+lFOIH
+3gM4gIMX9qD+/R2h7jWgE3IcFYDF0q0hFFrA9AEfFVgGLhmgbXjIAi6dNBphAoEjFX5iUkzCuIlK
+wxEi8LUKQ9RiMtZfxXFg211cqNCi8zHd06Ff13kI6nUdlABjkMwT9SCEGuTKB2Wis1Eb5wliLE5j
+6SXi0g3kRHIc2BWdCVak63FdA1bjm8yTZY3jPfKe6zWhOkaFtEiUB3Kd3E3UD/pjP8Zg1KXeGepa
+pfDjE7Lb01keENyJzVnJjXyQQa6d0lgAoUEK7T3h0/UiQ5DhM25iK94k9Rldw5njB3Zk7pkIQaCi
+90zCo6CiAjLfxVnlD4KlJ65kU1ABB5piZRhDJqKkB+pbwUH+G7FJHeEt3STQorcx4NxJIgSAo5lM
+i8Zgm7bZlSQuXmWEm0ElxEks2sV5m8NdJQpixi9aRq7l5bxN5aRcI8tVZstpyWa2YVNmUmAGSjcC
+AMGJYWkCQMLtXGUkXTqypW3eJm7mpm7uZp65DjnhRJvhzVJSSHA6E060jOsYFnsUZ47xpnM+J3RG
+p3ROJ3VWp3VeJ3Zmp3Zu54D4pnd+J3iGp3iOJ3mWp3nKpH8gp3muJ3u2p3uyJ3r2B3O+J33Wp32S
+p4LA5n3uJ3/yZzc24374Grb0J4EWqHn+Z4GchIEuKIOCJzhooIBM4oD4GoDqB4UWyIUSCEcKyIYW
+iIQKSIb+TqgI9keIciiE9keHEsiHBkiJsuiI8keLAkiK/seMRqjfeAEm5KiO7iiP9qiP/iiQBqmQ
+DimR6mjIVWh+hOgNvAKTNqmTPimURqmUTimVVqmVXimT3sCRJqiuXUORfimYhqmYFqkXyM+JAoiE
+ekIprCmbtqmbvimcxqmczimd1qmdruklbCmBhGgVTIGf/imgBqqgDiqhFqqhHiqiJqqfVoGeDkjR
+3QM+3KmkTiqlVqqdeoKZ5qffqKmldqqnfuqc5qn3ICl+8KminiqqpqqqJiqjjiqXQk+kgqqszmqn
+YqrvnOl/pCmt7iqv1qmo5hqp3oepriqxFquxGmqrAuv+q9pGrPaqsz5rKdjqmOCqf+gqtF7rrv5q
+jP7HsB6rt37rqibrtvrHozYrtp6rp0prjQaIhNpAMrwrvMarvM4rvdarvd4rvuarvr7rAzQqiKrc
+FoSDwA4swRaswR4swiaswi4swzaswG6BvwZI0RHCvlasxV4sxu6rDWRqgkioA7gDyIasyI4syZas
+yZ4syqasyq4syL5AxAJIiBJDL8wszdaszd4szuaszu4sz/asz84sMbwsjeraPcwCyx4t0iat0q6s
+A3Asgnjs0kat1E5tyrqsq+6pysnsz24t13at1/Zs0F6toxKt0VKt2Z5t1DbtrWrqQHws2r4t3Fat
+0Pr+R8x+rd3eLd7ybNgqq4aSbdz+LeCGrNpOK9sKhNsGLuKirdXyrYjymdbmLeRG7tfu7biiqN8m
+LuZS7eCuK5r6DQqUAOiGruiOLumWrumeLuqmruquLuh+wtySqMrRAhvMLu3Wru3eLu7mru7uLu/2
+ru/OLi28Ln8UHQSwrvEeL/ImL+uigNMeiLWiK/RWqra+aICqXJ+CK/Zm76GKK/XqR7lGL/hOqrpS
+a388b/ier5xOb7DaR7dqr/u+L/eub318L/rWL5yOb+HyAqfaL//iqfBWL59d7/sOcPbG77IGirn2
+b/3ib8d6rvI+MARHcOq6rtj+K59RQw5ksAZvMAf+d7AHfzAIh7AIjzAJZzA1/K/36lrxSjALtzAE
+M+/aNnDbZi4NR+3iVi6MZq3k7jAPgy0K58ejlm0NDzHLbi758gfUErESy20Fu6jj9jAURzHNUm73
+AvHlLjEWk6wR5+/hZrEXu8MNV3Gp6rAUl/EOU7H80kcQfzEbb7EMGy4bf3EYp/F81K0Z33HeovEB
+F20ce7EbP63fhIEJDDIhF7IhHzIiJ7IiLzIjN7IjD3If/PAY81nAOqwlXzImZzLDQmwTy6iuEcIj
+h7IojzIpP3IYNK+BmK8Co6/6Yqj1EjAsg6sB9y2srjL/MjAgD8T+2vL5tjLWBnAsB7OxzvLY1jL+
+L6MvLjvvph4zK0uysL6yMEdzqhKziRozM4NvMqfyMl8z+Ppy4w6EAEuzOG+vM9sH/XIz9Gazh/rN
+NpCCO78zPMezPM8zPdezPd8zPuezO7NDOddHiD6CDAS0QA80QRe0QR80Qie0Qi80Qwf0I/SzGqsw
+GegzRVe0RV90Pm8DKq/zDPcxFs+xKz8xHo/05EL0fKyxRy/xHytzR6c0EYP0Lw/E45I0TfusHtOy
+bQixS9fwSmtzS+80DcP0NwvETNe0UefsTRdzTgP1EPc0RwvEKcyCVE81VVe1VV81Vme1Vm81V3e1
+VC+BSctHiFKAKpS1WZ81Wqe1Wq81W7e1W7/+NVyXNQWEdXwUnQBkgFfntV7vNV939SlstIr6jSNk
+AWEXtmEfNmIntmIvNmM3tmM/NmH7AF3DR4iKwxlcNmZntmZvNmd3tmd/NmiHtmhftjhM9nvYdTdA
+tmqvNmu39mM7AmAPiCqj87V6swWD8zjnNrKatnucM21jqzoHti7/NrrathPjtm4nd6BSs8QSbQIT
+t7MGt2xvM3RDq3HDLDQrt3Yztydbc3VHd2zb6ECgQBCUt3mfN3qnt3qvN3u3t3u/N3yXNwUz7m0L
+xAxEAH7nt37vN3/3t3//N4AHuIAPOH7PAG+3B/GOQnwvOIM3uIPDNwwT7hvzQhczdeIKdX3+80JR
+HzWHT/GBswdKWzjmOrVww7GIYy6GHzdRdziL12xSV/NSn3jikvh0/7SM/22KY7dItziLv3hzQ49O
+33jc0rh4m7iQ4/iHr4cd83iH+3h3x/iRD3l4s6vfPEMYXDmWZ7mWbzmXd7mXfzmYh7mYX7kOJLl6
+hGg1/IKarzmbt7mbvzmcx7mczzmd17maV4OZp8fEjjmf97mf//mYP8OUd+5wf/ezXje3Zrd2Jzd3
+D613G/quSneR6y+kOyui062iL3puNzq5Onel96qkU3mhfzqtXjrsArOmM3qez4inkzqthjqhC8Qu
+u/qnmnoOo3qqb/qqM1qr0zqownqusjP+Rg87sRf7PfNzJyc6nwF0Qze7sz87tC/0Qyd7pw8EBEy0
+sWe7tg+7RsdwLht5lL9tjiu7TDN5j+86pl1xuKMtkYs6uK+72Y47pu+4uRu1kzs6lMO72bZ7rFO4
+visuupfHktd7Td97tef7v0stvwf7QER1Xz88xEe8VoM1tZ/6QFRAXGe8xm88x8N1BQQ8edg1Xks8
+yZf8w/+1t7O0QAy2a7e8y788Y0t2xd/6QFj2aN88zue8zod2ac/8fqA2zAe90Ls8bKe8T8u6r8uq
+rQMwcuf6OHO65T560lcqsFcrdU+9pS69hWa60wsz1A9vr2M91Q86wyO92Gc9yI9H+3b+vdenvXj4
+9tlLatWXr9/4wgHcPd7nvd7vPd/3vd//PeAHvuDfPTa4fXiEaDYggeIvPuM3vuM/PuRHvuRPPuVX
+vuJng+GDB/EOPud3vud//uD7AtlbvY0n/NLKu8WvOMHbe+Z/R4ibftqOPt2XPuwjLerTvOqvPk0b
+fNQjfO0j7cKT/rv//tHePtPnvu6PNO+DPZAT/9IG/+wPv/OrrPFvPb0n/x0v/8+r+/Qzrewjsd8k
+AuiPP/mXP+AXvs9b/0Bog+W3v/u/P/xXvja0vndMrPnfP/6XfyJ8/37Mdtz7KkDwEngPwD2BBxEm
+VLiQYUOHDyFGfEjQoMAqUzBm1Lj+kWNHjx9BhhQ5EmOVgxQlplS5kmVLgSkAnMRXimZNmzdx5tS5
+k2dPnz9pejoI02VRo0ddAkhx0BNQp0+hRv156WRBpFexYkVpkWRXr1/BjjQ50GpWs2dVEh04U2pb
+t295Cn0ZE21duxCVMoW7l69bqmQr3hVsdyuvi2ERJ1YMciyvwoMhZ1XrmG1fy5fjDqUbmfPZvAId
+ZRE9mnRp06dRp1a9mnVr0T6qBu48u2VhcWdw59a9m3dv37+BBxc+HLe42LSRs5wsoJtr58+hR2/t
+SHNy6y0/8zo1i3t379/Bhxc/nnx58+e5Lzl+nX3DwhVUxZc/n359+/fx59e/n3/+/Arr2wsQoeUy
+QM/AAxFM8LxTqhPQQYWyc8CdCSms0MILMcxQww057NDDCV8A8MH2CiOmlxNRTFHFFVls0cUXYYxR
+xhOJEXHE6ya7Z5YPeezRxx89dKDBGx+MEMgjkUySwxABI5LEsngxccYpqazSyhhrbNJJHDfTUckv
+wURSyLm2dNDIMNFMc0kby+ysxCvhjFNOGLN0DMo2Z8txRzX57HPCMXmZDE/rsjvkhUMRTVTRRRlt
+1NFHIY1U0kMNYXPQwQrLJpNNOe3U009BDVXUUUkt1dRNs7H00rsmg2CJSWGNVdZZJT1kyFVpy64p
+zHjtlaa/7JQNV8EKO2yxY5H+Dauxx4ZltcvKfI2WL7kC3axZznSVVtu9gGX2WrSKTVbccUVa9s5v
+0coR2m3ZfYpaQdEVLNt26XWq23PjzSpccvnttyRV8z1K3XoJ9uldawOua96CGcbpXmETRmpffylO
+1lyIIzZq4IY5rungjO3K7pkwSC7Z5JNRTlnllVlu2eWXSdYBYJBXKqyaX3DOWeedee7Z55+BDlro
+oXGuZmaaU5qMEJiZbtrpp2F+5lakrzrTz6vDZDJYqo96c86vwYazTm+5TqvLPbFOO0lA4S27KKvV
+jttHrcl2OyKvw85b7xfHxtfuiPSUW/Ae2Ub4b5bgHlzxDOn2+3CH8N5b8sn++8b48YYCX1xzDAu/
+PKmlBJJw89EpbNxyzxWKfPLVw64cdcDPJl32zl9XKbsmlMld9915793334EPXvjhidf96NoLE0GK
+5Zlv3vnnoY9e+umpr9765UU4/vXJrine++/BD7/4Jqau/aHszF+o7vS3Zl99x9lv230y53cI/frb
+x19L/fPXX/75/6e/+9VvfekroPkOWLsAxs9w/OMFAMBhDAlOkIIVtOAFMZhBDW6Qgx3UIBXgZ0AA
+UMGDJTThCVGYQgmC8HTpg4kKYRhDGZoQHA3k3wxxmEMdTlANDuSFGnYYRCGisIcOtMAQkZjEDPqQ
+iU104hOhGEUpTpGKVbT+4hWxmEUtbpGLXfTiF8EYRjGOkYxlNOMZ0ZhGNa6RjW104xvhGEc5zpGO
+dbTjHfGYRz3ukY999OMfARlIQQ6SkIU05CERyQsgrAIh1wBAEV9ykBBCDgCVtKSdBGJJTWZSWASx
+JDj8QMlMGgMhBHmgJitJSlRWMpOaBCVZGAKERybkHsaoJBCK6ElNGkSXEAwlQ2Cyynv08pUKUQMj
+z9fChFhlkispyBOM8YRETvMufqgDQiYBjgFt5lxPWEUdRgjJ1BnOlA/E2LnKyQsL1OGX72vlJKqC
+kDv5DUopqIM004mQJ1QSdAJRAwDg+QR74rOB6VxnOxmSz3TCRJoJMYb+OIF5jZRYJQUSzYpVLIBM
+am7ULOw8CBX6eQ9wSlJYq6CCRL/Z0HGu1JwLQae1UqDNhNIFAMa4JyxJKs9zysYq+TxINrOJEGP0
+kxdUsAAmWfoSmbrHWvk8lxqWelFlJsUgT6iDRTma1aOsApn7LOITwDEJbgorBYFxnE+RSs9OWsuR
+WKUlTe9BBSDglJM6dSlPe4jWolZ0lrzwKkLUIFG95rOtooynJN0aKNAZwwI1NCoI63BUq6gBsqCj
+LD/rapUnyDKypFRDCuz5GW9WchXSHG0dYFKRVcBTq611iTUFYgEqDKWsY2VqYg9bSrje9a3LtFw5
+C/LPUDqVpzsdCjL+0erIHoKUrr1Nal2ZqlvaKsQYrB0hSvNiz5ailhfXqINBqvtDZhrEKsYAwhOe
+AELH1IGRfpilMUq7WUbG9BqjrcgkSOla/a7Eo0BgbVyR2tKFrJO179tlWlGZ35f6dqZ1HShx5Zng
+VmoSdGidxGx5MYlrBvitqGyugBtcFVQe1a4P/GU5TWkV9uK2u+M1Z2ExSZCGFsSRDf0nLyIrEEdW
+RK/79fFCUlBaAEjUqry0LS3BAY4WohW4xpXuQWDM0rJQYRUQzimEAmOBIXNYICDUZCiVW8qjDpat
+W45ubr254SuXBcUxsco1VgGOOuBSnVQAhyzJm+d0OjLABeklK6H+xGYb/pjQ/owshv+cF3TWocBn
+frJanavUM5fln4Kap5Pr6tMoU1kgQ0UIEJZC5gFF1Z255XJZBA3LOwm0Dv8soouDa2ZTppPGhgMH
+iXc8EDUXmtcISXKj6XouKgA7xI/GNF0POunABLPE0GVwTn2aAgwLRMNkmYQ0NSzYgm4m2Yalqzfn
+KlTr8pguKTYIdwMFDldnGAChdLF50ateWoM3vqsgJX3t+5L89prf1cItrT0szGIfdpXM9DAxESpl
+2aj3yhM+8J3AEWpUxrTRjjyxl+lsp4lrvJLF9DbHb6nSlyAz1Zi0ih8w3sPQDhWgLoYmBFcRk3n7
+VZYAOO9clJL+giL6t98997lDrkHqutyjoSJFilVF/nOl/7zKg4k4zTVaFNAuneorOaIFSSwYIFqQ
+qFy7egWz3pJrdB0tQCRt0lsS9aqvne1td/vb4R53uc+d7nW3+93xnne9F1KJffd72PG3db8PXogQ
+rZ/gCZ94HEIRgop3PAwHOD+CkPDxlS8hC304ectvnoM1ZDzZ6xd59yXwdaRHnek9t8DQg35+omcf
+6i8H+8fJ/nCqbz3r3ed6EU619M3sPe9RZ/vc91MSrzD+8ZGffOUvn/nNd/7zoR/942NV9wiEEiaw
+n33tb5/73ff+98EffvGPX/vaOz2UpJ9+9a+f/dK/QfkcmJ3+YlSM/scCg1lxv3u9dKxj1KL93wqD
+DepvABGjMYSPfeSPABXQK+5PkvLP+gJjV/ivYfzP984vMARwATUwJAxw0PAnATcwBDuiATPpAZEH
+SiRwAgumAoHPcwJQBGFQIzrw8w5i/mIwBknwgUzw9/ZPBVfQ/FwQSjLwBkVwBp8oO7qgB5RwCZmw
+CZ3wCaEwCqVwCqmwCpeQEBww86DkAtqhC73wC8EwDMVwDMmwDM3wDNGwCy8ACGMPSpDBCuEwDuVw
+DquQEuCPf7IjH2SHdGYB/7QwMKSEdQTxa1zHgQrjBfZwdGjHifIwETenD7PQEKEkEAexEq+kEPnn
+EB1Rcxb+sYkacRMVBxJL8A8PghIt8RRnBBP1RxNBcXA6kYk+sRXlRhR1kBQFwhRRMRddRBXxhxVl
+MW5e0YeyIw9EoRiN8RiRMRmVcRmZsRmd8RmhsRgFAQIiMROhpACAIRu1cRu5sRu98RvBMRzFcRzJ
+MRsLgA1nD0o0IBrZsR3d8R2hUQPuUID6yQaJMARzsPpOMAJ9kGFY0BZ5YQjvUQONkBHrcSDx0Q8l
+kR/7kWD+cSEPQiARkgAL0hMPciIXMB938AJ7sCHb5SGtEQMxcgErEhb7aQOIIyVVciVZcjjgQACq
+cRWhBBSkoyZt8iZXAxTQ8XAK4w5a8ieBMiiDwzjoxyD+D6ISFCQplXIpyyMDYHIUIVIgKKA/qLIq
+rfIq+YMCdhIAoWQJmPIrwVIpGWAeP7Cf9PAX44YW9ZEHb1EX3ZJvttJufBEtsSYY488s6VJt1HIj
+gxAQ3/IvV4QXCQhKEDEv65IsV+8gztIw/WQvARIXAdMtBVPyCJMxr8Yu8bCfuqADOLMzPfMzQTM0
+RXM0SbM0TfM0OTMXqBEqQ/IgeGAcYDM2ZXM2abM2bfM2cTM3dXM3YZMH4tJtCoMTUHM4ibM4jfM0
+3QAxb68GR1IBNRIgU9AjtwUkZVIkm3MAS1IYL/I66e85o5IXolM6pYU6e1EIubP+svMumfM8K8Y7
+W1P+IMJTPH2FPAfTOtnTX9IzM9fzPvvFPauzI+UzWuiTMu2TP8klP+nxIIbgOBm0QR3UNLGQNf9T
+IKCBNy30QjE0Q3cTGn6zbAojFx40REW0QZlAOYdPMS2zMRXyPaMkMl10MkevMlOUTzAzQQViMWcU
+TRzzOyHTRVERRl9PRnMUTWq0LFF0SHV0RSe0RX30L4FU/wSiMJEUTIo0MW90SsNkR1m0R5u0Ep8U
+Ag9CSrF0bUwUAftpDyQgTdV0Tdm0Td30TeE0TuV0Tuk0TRFhNWvxO3VhBfi0T/30TwE1UAV1UAm1
+UA31UPlUFzqUawpDEer0USE1UiWVTuWxKC1yPw3+dFz8szwZMkDHc1Gp5gUz9UDLNH1AcFTFZVPr
+E0A9lVcGNEYLFFWPBUGNVCDsUVbtT0k5lVVb9TJeNUhjFVcTg1atlBfQdFKRNVmVNU7vNCZ3VSD2
+FFGldVqptVoNVVH3Z0kddVm5tVuRtVKrhQavdEyVREuXlEu7VBC/dB/DlFyVpEqXc1zd9UjM9VmZ
+NF1zcV3ZkhfEdF59BF5PVF791UfqdVXbEl/zFVSRZi4HlkcA1kyPtGF7pGAJtBQRNmGz1V77VWI7
+5GFNtZ8WdERFdmQh1FkNlhcqVENVdmVZFjc5NGNPFkRJdmZpljNL1FJNElOFNTFUtWLhs1cFVGH+
+aUZUd1YxiDVeeeFWizYsehZWeRVop0VoQYZol1ZZStV8TrVqwaJpgfVpoRYufhVKA1JrC/Bqaydr
+ybYruFZs4/NrwVZqM4Zq05YkjjZgeeEcYiFv9XZv+bZv/fZvATdwBXdwCTdvyQBP15IjBYIHkqBx
+HfdxITdyJXdyKbdyLfdyMbdxfRNmfZYX2KFwQTd0RXd0CTc5cVY7I5ZjPYRinfZgL9YS9VVx+VV1
+HdZsXycWaZdDWLdrXfd1BzF2+7Jdc7djbRd1cHd4M2R3xRZdfTdvgLcNA2NjkddCPBZr+wkpwzJ7
+tbcpnzJPWXQqsTJ8xXd880MrObd1ecErt3f+fdmXO8bydNVTIFBSKOm3fn/yJU22c2kSJ/m3f2tS
+J8+Xd3nBJ+23gA14KIvXc9B2bkVibcH0Z93WV+E2YuSWgTkwgS9ngS34IxyYXSE4gvsibB94bDdY
+LDD4cTS4hDmig/e1bUFYKkTYg0lYhRnjhA9nGOExh3V4h51xGvMXfbGxHIV4iIm4iMfxHANYbNeR
+h5m4iXMYXA/wY1N3ei9EeUeYeZsXbJ43HaOXijWkes8WL70YQ6xYhrE4i+dki3lSSMe4QsD4dsW4
+jSukjPf1jNE4TtSYK7tYjt3Yhv/meOWYjmXXju/4Eic4YRhWjt/YeDWTDh35kSGZCiPUe5f+lAvT
+8JIxOZM1+QzXMIlH+A0jOZRF2ZHtEH7101ZpOCRYWHZd+IWhIob3VSJTeSPqFmJReZY9YpWD94Nd
++S1gWXZlGZf/xZRtNGmFeQR19WRbuZeB4pd3eYaPOSNqWYpvOZozQpeh12uZuZkPOWAq+Jin2XoP
+Am9Jt5zN+ZwD93B/WIAZN3Pd+Z3hOZ4vd3P7x14/F53xOZ/L2XTD9QjjmI/dQZCfmZALuUryWC7Z
+mI8XWYH/mY8FOpt7t6Cdt5vzJZHbeKEzuKEDOZk7l6AlOhUpOl4seowxGoX7aRDaN6W11ynXWWwN
+gHxhOqbF1wBCGl0KQ31VOqeXcgf82G7+smN+DziohfoM8FdC7XV//TeplVo1ALieT5aAhzqq7Zco
++9koq9mapwCbuVibt9lgavpbvlmYwzmMdTaatXqNO7Wro8KZIRqarXms4bisj/ms9Zir1Xon2Hqr
+BSKYxbqn3QYJPSCwBXuwCbuwDfuwETuxFXuxGVuwEZcv25oZYGCyKbuyLfuyMTuzNXuzObuzPXuy
+meGrr6UwhKCxTfu0UTu1GRsQ/LpsALmNH1qv7/WjW0e0m2Wkvbikb1ijYZuj0dejaZtObHtYcJuK
+dfuPeXuMYxutLTa4a9uTZVh6Sbq1uea1ldu3BRi4nbtFDho4E1qRqZtqskMZ8qG8zfv+vNE7vdV7
+vdm7vd37veHbvCEpcZ/ZBVrgvvE7v/V7v/m7v/37vwE8wAX8vl1guHGlMCohvhV8wRm8weE7CsIb
+aeiboQFySS0cf6JYnFH3Oy/8ZP3HA4v1lFm0wzv3wxkvgjgvxTFowtOR8lT8xVfIAoPQxWFcxT3v
+iWo8xyUI8A5Px3Pc8OYH8Xw8xfeuyI38yJE8yZV8yZm8yZ38yaE8yqV8yqm8yq38yrE8y7V8y7m8
+y738y8E8zMV8zMm8zM38zNEcInLsINZJnyoMxO1HwoBLzsnJlRIuIVgNANhrxlYJyr4JglIA7UTM
+koDgGhKNxdP8y1Mg3ARikbbpw37+qOZujiHwpcmeC9k8iiFMSqKuAQgUDGO8axUkirKmLamugYSa
+LdFVHbAAoKH2qZ2sqdxsLb5QndIhxtIjzdRiqiGgJMz8xrz0KawGzr1SfdWN/dYEArZ07NZknbqK
+btAqfbdyvbmiLCH2/CSkCV/+CptK/cmSfd9AzNiNfdEFYhX6qbrazCF23dYV7tJPzXLWaYR0ruFM
+DdLpauxETsbFHcz/SZpa7SXmKt0XgtVOp88RjMK47N0hR9oAgOTo3N3JYsSKfd8T3aj8QKZEiqDs
+XcOMgcXoHaei/bmqvSHcq4e0va9+Sui+7ZsmnuLRfBIWCXSCaZfQKqamKuTDHdL+82ndXSrf82wh
+gL3T7uHpSq259N3lu9yR1I2lfGrbk6ndpz3Aug3oO96vTMrZGmnF2O2mih7cWh7pzdzOSu3PjCHR
+eP3WaarPyR7kPG7g/1zrTwmVKgLOwKmmkN3ACN3nwX7v9UcNiI3vAT/wBf+OUuCCgPwsvo6CeLwo
+MGjwHf/xIT/yJX/yKb/yLf/yMT/zNZ8zQKvzPf/zQT/0RX/0Sb/0Tf/0Ub+ifGjsUr/1Xf/1YR/2
+Pf71Yr/2bf/2YZ/xCm73eb/3ff/3gT/4hV/utXD4jf/4kT/5g78FL2fmlf/5oT/6f19cR9xeSdx9
+Mrz54dxur1+Au9+Ftn97wp/+mqvfw60fw8c/9dKfrAViAsrh/eE//uV//um//u3//vE///Uf/g18
+VQojEgBCmsCBBAsaPIgwocKFDBsKjMQr4j0A9yJavIgxo8aNHDt6/AjSYwoAFp+UO4kypcqVLFu6
+fAkzpkyUEyyODIkzp86dPDUCSGHRwayhRIsaPYo0qdKlTJs6HbrE4sSKPatavRpyqkVuqrp6/Qo2
+rNixZMuaPYu2KzepFLG6ffv2psQMT+vavYvXqQObJOH6/Vv1Z1B3hAsbPow4seLFjBs7fkz4BVuq
+gCtbzqg1IrFenDt7/gw6tOjRpEubPs2Z2OTLrFnL5XVvFuTZtGvbfrw34uv+1rwtC47o4Lbw4cQb
+S5bYtrdyt5l5bUYNPbr06aZVI6e8PDvP17GLe/8+PDev3drL8/zNKzj49eyNrzYPH2Tz59Tr278/
+2jrs5PH7b+QuW3sCDkiYeOT5h2BG6O1gSoMOPghhhBJOSGGFFl6IYYMIvJdgh83Vs0uIIo5IYokm
+nohiiiquyGKI9XDYIYKvEZJhjTbeiGOGO/AVY48WoacegUKCd9x+2PkI33z4Lcmkffo1hyR8AA5J
+pXcG9hVlgkBWyaVwRUKZpXZKNklmmaU9yV+Y2k3ZZZuzXakmglu6Sad718VZ3phm7slnL2geiSdv
+bNZJKGJwBgrfnIUuGhn+jIi2pmefkjL556PKDcpooYdamh16wuQIaqiiWsiJo5xW1pwsi6zKaquu
+vgprrLLOSmuttq4qi6mn/vUaBAiMCmywoQrD467KKZopoV+maexfkU4KbX2VNlsZpsnSuSm1lyF7
+rZvLAqotVs9GSy5004YLl7Xddpktun9xuy6X37oL17jl3kvaufRepW68VLa7r1vonZJXwQYfzFRU
+dwZ8VXNxyAFxxBJPTHHFFl+MccYabwxxHLoynNNrAtCFcMkmF3xKsSALDFRElrwBc8wyz0xzzTbf
+jHPOOu8MsyYfrywffxSkRXTRRh+NFgU/Ay0SlgLQw3PUUk9N9c6WqMz+dGAtp+evt0tnvZG9+I79
+mb5gg9Rv1wQCfDZO8KpN4LxtZ8UffWTfXfbXc6vcHdxVsr23R2/73Z7cgXckNt5jm314RmkTvh7g
+jfu0dRFmXI555ppvznnnnn8OeuiiX86O3oE390gAqq/Oeuuuvw577LLPTnvtqj9i+t69cjF6777/
+DrzoRWA9eUeDQ05k7nMnrvi9jBevG5Z9Iz+g5ND/uHWQ1BeufNvMN0/u89A/vj1x1l/Py/HlD2c4
++gs7B37z4hdP/vq3nX+9+vbb1r7738c/qflNrn77ow3+oIceEsxhgQxsoAMfCMEISnCCFKygBRdo
+j+6drTlYmIYHPwj+whCKcIQkLKEJT4jCFHoQCxoE24wuCMMYynCGFyQB8dx3Ef0VcDb9Q9//ANgn
+ATaOgDvEzQ1xGBEdFtExPbzeD4G4JyEejohLZMwBi6fEKi6midB7IhTLJMXAUVGLibni5LJIRsRw
+sXhe/GKTwqg76QUojbUxY+PQA4Vg6HGPfOyjH/8IyEAKcpCELKQeR9DCrKXqCIxspCMfCclISnKS
+lKykJS/JyFy9D4e9UoIhPwnKUIqykFA4IhLRSMfCrHFybXTjkuA4tzGmskCmxCEqZ7nKxrXSlfeB
+ZdtkOUs7Hu6Wqczl4XbJS2klkmnATKUwAzewk0lzmk1RmJGQ+L7+h3Fsm9zspjc15rFNuk9kJKOm
+Oc85i5RFD5sK2trLqgbPeMoTZz4Tpw+FhrR86nOfZlGaPa8nMqjNc6AEhefV1snOHGZvlrQx5unq
+lszwLRNozaTjM/dGTDo6dG/IjKh0fHm2iqbxonPLaBo3ujyIejSAE12ZSMlI0rahJxEmqKlNb4rT
+nOp0pzztqU9/CtSaluqfXeQPC8KB1KQqdalMbapTnwrVqEp1qkhlQUtB1qs+BHWrXO2qV4GaiFq6
+Dz2eKIVZz4rWtKp1rWxtq1vfCte4mvUSV2VYc6owhbzqda987atf/wrYwAp2sITNaxXqGjDu4EOu
+jG2sYx8bV0/+iBV9ZIWsZS+L2bfSlahs5A9eCwva0Ip2tIM9LGcHKL3FZna1rLWsZBGa0CRurayt
+ra1tNYvYfd2VtLztrW8Fa9prYlOxty2ucc/62vFgKbaVPa5za7tZ4SJxt7+trnV5G1wwoY+4z+1u
+ZpN7IGyihxEgKK95z4ve9Kp3vextr3vfC9/yLiO39GqOOtKB3/zqd7/87a9//wvgAAt4wPhVB33d
+NaP4KnjBDG5wfBkx2fzN1rsUtmx0tetEz153wxwu7YHRxd0Ki1iu4F1uQps74hS79cLMuidlPtvh
+GMt4CtltMUBTq+Ics7XEscWeRWir4yDP9cPhou6Mj3zdGoP+a3w4FrKQedzj9E3YyUFm8ZI7+2Ik
+a7m6SmZniKmsYij3GD0JmIeZz4zmNKt5zWxus5vfDOc4mxkHRNZWc+yAhzzrec987rOf/wzoQAt6
+0ITOsx3qTK1epULOjG60ox8d5wREGIFTBrOKrcxOI29506Lt8nCbbOkUi5m5lQ61iDGNTU1zetUe
+Pu0QQW3qCo/6xKWOtXdRPV0Ns3rXwEV0s75s6+7Omp1khrSxj43sN9PZ1cfkTzzYAO1oS3va1K62
+ta+N7Wxre9vQjoevjaXoZIt73MeWNGxJ/eNgVxjXOFQ1r9+tV08jEdjqPu6wxVvrehuX3f7TNbz/
+bdhv74r+3vq+7b1Pme+C25bfLrYIjAEOb3lzEtYKN/iksZjwirOW4RnOMsT/LfFxUlzjrT24LbdG
+XgerfOUsb+98mf1QyqAjDTSvuc1vjvOc63znPO+5z39Oc3QI/FQJbrnRj75yCJ+b1ukm+W05XlSP
+f/zdId/uyJ3+3YufMeNYt/DQOeXuqXO66jeWimq7zlqTj5XraHcs1LHscLFT/euWInjbH6t2yrL9
+7nJ9Oyv9LfdVk53JZud71pdO7K1xgACMb7zjHw/5yEt+8pSvvOUvz/ha0P1RzXkHOj4P+tCLfvSk
+L73pT4/61Kv+8+/YPKJ69QfMy372tK/95Tmg9Tvu3fD+uIU5RwEf+E0Pnn5X5z2Jcz/M3RufrX7X
+JfCDr+Xho7bwy8c78qGp/OqntfnNljr0tyz9V1Nf+4zNu4QtogB8qH/97G+/+98P//jLf/70r7/6
+C+H6QDXHG2Dov///D4ABKIADSIAFaIAHiID95w35hyciwwr2B4ERKIETWH8KcH0YtTWOkAUbyIEd
+6IEfCIIhKIIjSIIlaIIb6AMMGCfNIQ5n4IIvCIMxKIMzSIM1aIM3iIM56ILioIJqIjLdcIJBKIRD
+SIQm6AgXWFLZR35D5nsp5X3fh2ThN0XFt4RrZX6U1nRVCFfcF3NxB4Xg14NhYndamFZXiHFZSIZt
+xYX+v/eEXyhjUihGVJiGZmWGW2cRw9AGeaiHe8iHfeiHfwiIgSiIg0iIeUgHYZglzQEPZcCIjeiI
+jwiJkSiJk0iJlWiJl8iI8ICIUdIrD1CInwiKoSiKhDgMSChTSriEa+iEXuiGRwaHcTR+c2iFpng2
+KCaLaqWK3vN8rchhrxhLcjiHdah7aHiLaJWLG7SLvJhkm4gkYyiLwph8xFiMTChd7ZaMyshlzOgj
+zhiMtAg26PEBkCCO40iO5WiO54iO6aiO68iO7SiOGKCNPdIcRlAM9WiP94iP+aiP+8iP/eiP/wiQ
+9WgE8Rgjr6EG7oiQCamQC+mOH+CNWYMe1nhlfzeTkc5XkVOYUOGFcJlmY1HHkRcZhxlpYon3kSWZ
+ah1JfCIZZeljDCngki8JkzEpkzNJkzVpkzeJkzlpk6uAkn+3CjoJlEEplENJlC7JkyAZR0WplEvJ
+lEFpDCMpXgAglVNJlVVplVeJlVmplVvJlV3ZlUiZUl4plmNJlmVpllUJlr90lmvJlm1plisJl3Ep
+l3O5NwEBADs=
diff --git a/Documentation/DocBook/media/fieldseq_tb.gif.b64 b/Documentation/DocBook/media/fieldseq_tb.gif.b64
new file mode 100644
index 0000000..7b4c176
--- /dev/null
+++ b/Documentation/DocBook/media/fieldseq_tb.gif.b64
@@ -0,0 +1,445 @@
+R0lGODlhdQKaAucAAAAAAElJDK+vr1YMDBUVZC8kDQAAVkYQEBcHOwYGSCEJHSAgaKOjoys8DDMz
+CgAYGp+fn19fFJmZmQoKO10wMA0VIAAAcDsICCsMDAcMT1MMD2ZmAAcSO29ISFUHByIAGoiIAA4H
+T0pKDJaFhXd3d0EgABoaVGYyAC4AKXd3ODs7BwAAN1MAKQAAYlZGB2JlDBwcWWBtYCA3ABAQTQAA
+ZQ0VQD4AAFVVVUhjSCQMJQAAfBMHMkQgIEtLSzAyDD5VPmZmDEZRB2FhEWZiDFo2ETkdCwAAVEUt
+Gu7u7js7Ozc3N3d3WACPADU1NTMzMyBRIDgAAEJCEHEAAEwNDZeXAABpAEQFBSMjIxgNQDooCBA9
+EEhIbwBVAAw/DAwMPgBNAENDCgc9B8zMzABDAD4MDAwOKjwKCkQWKUscHAAAcUtLFRMTEwohCoqK
+AA0NTBEREQgfCBUqIgApADIAAA4ULzg+DEEfH3wAAAcHSaqqqlkcHDgMDKSkpFQAABUVRjEwCGZm
+B00QEDAwXSUMJGUAAJaWlhQUUnx8jVQaGgcGLggSGy8GBmw4OGNAL4qKioiIiGIAAEsHB3JYWHd3
+AAAAPlctLYQyAGggIBgAGkIVFQwcJRgYSA8MU9EAAAcHVQAALRoaYbu7AEY1H2ZmZlxdEHAAAD82
+DlhqWExGHgwOUzMzDAAAmgA5KTEHB2ZmPlpaB///////ACISRExUDTJPJUQrDAwMVhISSEhISHd3
+IC4xCjhcOA4ORERERBkVXElJAG5gYFhYcnt1ZkgGBlYAAAUFMTg4ODo3BTJrAFESEmZmMF5jBwoG
+Q1paDUkKChxGHN3d3RwYRGZmHCgoKFMAACYmJi4YLhQ+FCIiIhU0FT0AKR4eHmVeBw04DRAsEAwu
+DAc2BwoqCgAAPFdMDQAA0WAqKgwiDEgZGRkQRAckBxsTPDEwDBAQEDwAAEJGDAAAU0FBQEJCDLu7
+u2IYGJoAABgYRjg4bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAALAALAAAAAB1ApoC
+AAj+AGEJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
+MGOelAegpk0AJFrSrLhTpYQ3AHoeDFpQqMCfQQHIXEh0olGBYkZtpGkTW56B0EYBfTMKCUEJEqja
+7DpQDIAbBJsOJHF1qdu3cOOqVKtTKcWnEOnmlQALWk6Eep8C4Ou3YWC7JUlAg9VUL0K8vcRMRUwC
+gFdoXBdD6+WE4A0kQqE5kSqwsuWBepFg8yq3tevXsPPKg4n3YW2HjnHPZrp7oODehoHDui2ysfDH
+iKFi42iU6A20A5G84SsQrdE8iKdPR3181KPY4MP+NySBC4L4lHRJAI0MSwwJ0++B5nSvHqdAJPVv
+LHb/U54YJzX99RR+QOnX3ntKidELANiMYlce2DB4FX9vHMdYTfIQeNZ+8dlHkFg9QSihQEQpyKCD
+H9q0E4X+AfhGTir6ZhMskWGTU33Y8EWffFC5OB+CONX3V1BixVgfe7DgWFlB7621nmRMAjBdLwk1
+Bw0SAEBZ1mKw5PHddQ9aNgp0jB0nQWfnpanmDTVNU56aI6lFghOLkbAcTVJh9xl28uCJBDQ2Jkkn
+EqN0Js8bvWTYy3dmzfbUnFcWOhxXsznxmWhKHeooANAcmihrBhEFKaGGAtAVoH9xN1ymFa66GFH+
+lv4JYEFE7eRphi/21ephJDqRR6fY1MlqnlnCkitmfZra5VlI8Fnms89CmuRy6jkKVEGLlkbntEwG
+CwuKyK2VE3HfLhaapQKNuSxrjpkF50Sg9SnvvPTWa++9+Oar77789lsvPDcBsE0Tqb67kVpWDVSh
+UMbRxKUEy23XV5YOFwRNrckZS93FYlRs1sNKjZLqDSRUnBBREnNsMsS0bvrtyDD6x+lRGWPsqkCZ
+pSVcT4MZtCRN7M6Ws0AM70bU0UbPljDO8uQ4kAQ177a0scA5TWKVAQ8mVJECUbnqTZ/h3NYb30Hb
+ssEQFZn12my37fbbcMct99xxo62RWlknq2r+mVsrhbfeRzmBDZsu68xdT33TGHDJGYc629+JG77T
+gjfFHLmqtiKWFTZv3CAZr9CKcQOiC66q6uadd4yYcc+ynrdav5EYMHAIYx2dVljunGnZtWF3E5q5
+5Wb3QfH6a/zxyCev/LwAB3xFEoUMnxHCuu9N5Myw/KSxQCrbZRaUNhOUMsV2XQzyy0U1fvb4qj/8
+xtk7iey4+TTDn2njBJpNtF1N5Wp60QQhEABb97ikwQIbwrFa9qImEAQmRGKMsZ2MaESmdCmFBFCq
+zShIs6xXWUh40gthStgEAHZYSksitIictvWIN4BmdUnzE6oEFalSQaVYjwDAVR41KEkJpRf+l5qV
+piYFuOEUjESzGZUPTfWnQEmOVdYqIAVlxcDMKewvdtKf6aBFtr4s6IWq8p+dBog0xrRFVCx0YbWG
+EyFslU1aLfRKyaa1GHDNMUW3QwtmDOSXa3ltiwF8Q1tw9p12qS+FiCxJoTyXyOkBpz5OkAwZk1Uf
+smgobEI5UmUeMaD8eEUoSFjQlI4SIadlsnEAEsMlP4kToJDliaRkEHWIEkopSeCPSPwfYvIAoLNI
+JpUEwcz7mgIxBqmnQjDsUi89N8lniRIWwIQklKSJyyRxUJokygmhbMm/I6otbH3RijFp5J4JFkR7
+BJFU3rJXwUa6UySgemddxHPEhZBLnij+AVRDVEm097XmBtTBp0AHOrx7ukUeZXOIQQk6klFYqCBj
+QgISbsBBuOiToRjNaHgWCieOarQjhVnIRGsCzriE5aMoTalKV8rSlrr0pTCNqUxnStOa2vSmOM2p
+TnfK05769KdADapQh0rUohr1qEhNqlKXipARSOKpUI2qVKdK1apa9apYzapWocolgxwiGGANq1jH
+StaymvWsaE2rWtca1oTE4BRwjatc50rXutr1rnjNq173CtcYJEQYSwisYAdL2MIa9rCITaxiF8vY
+wAojIVuNrGQnS9mtAgMh0GCrZjfL2c6y9RAOYUQnRkva0pr2tKhNrWpXy9rWuna09Hj+6ALIQdva
+2va2uM2tbnfL29769re0bUFCnsCE4hr3uMhNrnKXy9zmOve50C3uExKSi1hY97rYza52t8vd7nr3
+u+ANr3VzkRBIvPa86E2vel17icesArjwja985/vbBYR2vfjNr35ZG1uEzJa+AA6wgHkrXIQQN7oI
+TrCCF/zc6SKkuuKNsIQnTGHwkhch5t2vhjeM3/YeRB7vHbCIRwxg+zZEtBxOsYr5K1sSu/jFvi3w
+QQ7M4Brb+MbMdfBBIFzhHvv4x9298EEyvOIiG7kTHjYIiGHM5CbX1sQMQfGRp8zh/h7kv07OMoll
+bBAa4/jLYF6wjg3CYyCb+cwUFrL+QYhM5TbnN8lFCbGW5yxgKC/EDlPIs573zOc++/nPgA60oAdN
+aD3zASF+cIOiF83oRjv60ZCOtKQnTelKK9oLCfFGNzbN6U57+tOgDrWoR03qUpt6095ISBSawepW
+u/rVsI61rGdN61rb+tasjkJCzFDoXvv618Am9B4QwgdLG/vYyE62pf1wXzc7e71WNgiW6Uxt+XK5
+IF4Os7a3vdwxF6TMaA63uLmr5oKw+dnobi2cPyTnaru7vs1Ot7xXG+2CTPvd+NbttQmSbW77m9ve
+Jgi4x03wcZebIOeet8JJu27ftDvfELetnRUi5YVbvBP1Jsi9Ix7xfQ+k3/8O+Zf+Az6QgRf85GY+
++EASfnF5N5xoD+c4xCeekIq3fN4ZH8jGZY5vjwsE5CIPOoNJLhCTo/zoFVa5QFh+82e/fDgx5/m7
+aY4QHgzg6ljPuta3zvWue/3rYA+72K/uDAYgpB5eSLva1872trv97XCPu9znTve0JyIh5uiC3vfO
+9777/e+AD7zgB0/4wuvdHAlRBRAWz/jGO/7xkI+85CdP+cpbfvGqSEgrxs75znv+82LHAEIYMIG6
+m/70qE893esR76bLO+cC2bnUq+1zWABd6LiPLtFhYXSk+168SocF013f5qcvefYzbz3xnw17WMge
++XOu/e1zT/0cU/f32E9zeZf+73L3Qj/fVD+Izbnf5uY///tOln7116/762f//eEN/vDJX2TjRx39
+WQ6/QcpBj/77//8AGIACOIAEWIAGeIAI2H+lIAAIYQJp8IAQGIESOIEUWIEWeIEYmIEa+ICUkBDZ
+8AUgGIIiOIIkWIImeIIomIIquIIgmA0JoQZtEIMyOIM0WIM2eIM4mIM6uIM8GINqkBB9kIBCOIRE
+WIQImAwIIQDvsIFM2IRO+IQaaAIOUQlSUIVWeIVYmIVauIVc2IVe+IVgWIXUwIAHAQqrcIZomIZq
+uIZs2IZu+IZwGIdyeIZGkBDXUAV4mId6uId82Id++IeAGIiCOIh4eA0JEQH+oZCIiriIjNiIjviI
+kBiJkjiJlJiIEZAQNhCGmriJnNiJYJgJSWgBcziKpFiKpiiHoKB89Ddl5od/7qZ+7BeL1vdg8FeL
+3iV/q+hs9ueK1aZ/BTF+uVhkrciLdAaLsniMx7V7vWeL8IeLwUhlu0iMc+aLBAGMz5hiwyiNWWaM
+yIiMysiM4IhdzniNRhaN2uhk1DgQaBAJ7NiO7viO8BiP8jiP9FiP9niP7DgMZncQt+AJ/viPABmQ
+AjmQBFmQBnmQCJmQ/lgMCREO4PCQEBmREjmRFFmRFnmRGJmRGvmQ4ZAQYPAKIBmSIjmSJFmSJnmS
+KJmSKrmSIAkGCWEF+Bj+kzI5kzR5j8N2EAyQAAq5kzzZkz6ZkLegiuRYZS12juk3XN2YlN8YjuE4
+jkOpYuZolDCWjgJhjU+pX9kolS/GjUkZi0vJlMzolFe5YVGplSRGlbBglWMJbUVpli7GlV25fl8J
+lrUolmupX2XplgOGlmp5l+iVlXo5YHAZl9Q3l3T5fnbpl+uVl4FZYg6RCTUZmZI5mfZ4aAcxAz+Z
+mZq5mQeZAAnxDWEQmqI5mqRZmqZ5mqiZmqq5mqwZmt+QECIACLI5m7RZm7Z5m7iZm7q5m7zZm7Ip
+AglxAZQ5nMQpmTdpEHzAmcq5nJs5A0KpmOkFmI0JYINJmLhnmIeJfYn+CZ3oxZjTKV98yZ1Y2Zbf
+SZ1IaZ3sh53Z6XvbKZ7s5X3lGWDh6Z5s6V/xGWDViZ4ip57reXTtSZ+r5Z336VvzCaB/SZ4DCl/5
+qZ//xp/9eXL/aaCoJaAJultoeQ6QkKEauqEc2qEe+qEgGqIiOqIkmqF9sI8GwQvisKIs2qIu+qIw
+GqMyOqM0WqM2uqKfkBDpMAY82qM++qNAGqRCOqREWqRGeqQ8mg4JoQKT0KRO+qRQGqVSOqVUWqVW
+eqVY2qQqkBBQUKJe+qVgGqYk2gqjtwI3eqZomqZqaqO88JwSmlrSWaG9taAMCnDu96C/F6FvWloU
+Kqe4VaB7ymL26af+wEWndaptDoqnBrd9gapu8EmoBOqmjUpacQqpuGWohwpmiaqo4aanjdqnlkoO
+FyqmpFqqpiqiJ4oQKrqmrNqqrjqjOYoQcCAHtFqrtnqruJqrurqrvNqrvvqrtAoHCfEHv1Csxnqs
+yJqsyrqszNqszvqs0Fqsf8Clp1qt1lqqZIqTZvqq3NqtrNqmJzapgnploTqn55mpQbepnHpmnhqo
+oGqpgCqup1Wp5Rpc54quIaeu6wpk7bqn7wqp8SqvpUWv9Yqp+Gpj+rqvPtavb/qvhBqwAgtbCFqv
+tmWwBzt0d6qwBMewEuqwfoqWHXAJIjuyJFuyJnuyKJuyKruyLNv+siOLQgRhDwswszRbszZ7szib
+szq7szzbsz47szCQELTwBERbtEZ7tEibtEq7tEzbtE77tERLCwmhDLlQtVZ7tVibtVq7tVzbtV77
+tWBbtcqQEHrgsmZ7tmibti1LAQghBj/7tnAbt3L7s/bAVHZ7t3ibt3q7t3zbt377t4AbuII7uIRb
+uIZ7uIibuIq7uIzbuI77uJAbuZI7uZRbuZb7UhCUPf50HwJySIYRMFTCM6ALSDJSFYPkM2szG4Sy
+Fa90ITZRUpcbu7KLESRQQRRFEEtCulBBQrB7NumTS78bvJp7uh+WHHukGaPDJU0hGu00u877vPt0
+GrCAJYOUB9f+orsH1BVI4ATVZDjB6xh4YRRZpBBGAUQF8RzAuyzQu77sqxBWY71YkSN2ARmsQRzg
+yz/HEb7Giz3hch/FQhBm4RVqgR3tW8AGnCTQIT9dw0nz67ncAhj5i79K1jixI0HYS0Dcgy4HvMHP
+G8DGMk3W0cAIIUAnEzD7IzuVg70V3L8XfDQ30QvxxMEybLlOIAEQ0k+sdMIG0UK90FVDEcHpa054
+hBX8W7zR8b9lcRogNMNMLLmPQFFYlDd4MUYMcb9BrMNDXBrLQb7JYb6eAR1L3MRi3LgXgw0wu0UD
+gsQKYcVapMKI8RPEO8HB1DnI+wbK+1BjnMeMKzhG/DU2kSj+WXMyQOy6N+HHNRG6NzE1Fnwf4jQ0
+bazHkBzJkjzJlFzJlnzJNEUCvbDJnIwkInFLnbzJAYXJpFzKpnzKqJzKqrzKrNzKrvzKsBzLsjzL
+tFzLtnzLGhHKurzLvNzLvvzLwBzMwuzLo8xSCjLMyJzMyrzMy3zGKnXMzBzN0jzNyXxTDELN2JzN
+2uwhLkUT3KvN4BzOyAwgePxR3izO6JzOvNxGNsXNLuXOLEUT5axR8gxT9fxSuVtT8MxS+6xS99zN
+YZxR/9xS+UxT/axSB41SAx3PAY1RC71SBT1T3CwMjFDRFn3RGJ3RGr3RHN3RHv3RIG3RXZXQ5qwW
+OLALKJ3+0iq90izd0i790jAd0zI90yiNAzIyzwKtFiG90zzd0z4d0h2wFg7cUtzcl5MabSRNz2ox
+fRd7Yzr20P6sFvMnrkkW0TJV1BGLWkhdT1HdG0zd1DX21A3NUP881ZNa1UPNz6li1I261fa81GDt
+b2KN0w4t1Vl9WmhtzWt916bl1i/1z18d1wo212/dG2b9qUKt1+pYnIzd2Paoj6jB1Sn1z2zQmpZ9
+2Zid2azJBjdd2AMBk44d2qLNjsdp1TGF1XxNqb2R1Dnt1YK9bYT913ad2qOV1+2817SNcast2QoN
+168dZrEN0Iad20iW2Lc9EGwdqH4t3B/328Dd2bI93Ln+bdv6nCpU6InYnd3a3YVjGNmeLRAfyILi
+Pd7kXd4q6IL7Q9dkrRaZuN3u/d7YDYqlkdYrxc38Z4T4nd/6XYAL6N3RPRBaQIgCPuAEXuCDqAXQ
+zdwCEYT73eAOjt9ION+KXZXEvdwtBdjOrakJfuGzTdvUbdC4TdsWztCuneE4FtwcLt0ebtzVjdwV
+vtvfbXsmfuIbTuIrR9wfLtGpgqHX2uM+jqooytp13RuzCqxGfuRInuS+KqzpHeNd+uNQHuUZmq1J
+Qt8IHeKpPeIrheEzjrA1vuUdnto5ftVYztda3tXN3eVh/eVovnQ4zuIg7uK5feaT7dtqLmZsXucq
+Lub+cK7jci7iMP7fP3fnGNvkgi58by7hxy0QeBZsjv7okC5olskYvF3SvaFpp5bpmr7pnF5qqWbo
+Cg4LvBbppF7qjl7aVp5SqA3o/h3qgU3oyIXiNu7m093nZP7nWR7org7rg53nvb3nfD3mp13md03n
+v57mvN5+oJ7iN17rit7iFD7nus7sg57syr4q6k1QZZ3oVT7hsMADZBDu4j7u5F7u5n7u6J7u6r7u
+7B7uZhDkla7UvZF3hlfv9n7v+E54iLfssw4Li9DuAB/wAj/w7C56zx7n0c7q2RTjr27tsg7mwH7X
+wg5Tq57rrU7tMm7t0PXwbY7ozt7ti56WL37x/d7+8MnO8Xre7Ct+8H4uENcN3zAf89xNhpQe4+Ft
+3jif8zp/guiN7THe3jIf9EIvBfIN8tAOC/f94Eq/9PxN80K+3r1xhwY+9VRf9YBoiPwO8QPB4Ezf
+9V5PDxFu9Agv8tJO8lpf7RrvXCh/7LS+8mLf8mSv8DV/6CbP62tv6SrP5yx/6wlv8QtP92nfYL6O
+922v92/P998Oeoq/+IwPdmVn9h1vDt8w+ZRf+ZZ/+Zif+Zq/+Zzf+Z4/+fvu84e+eY1f+qav+AZ/
++MOO62Y+7SUf+Go/+PKe98Fu66vf960P+SmP9rCvXHc/+4Vf+3t/+3Hv93O/673fbbLf2rQv8bb+
+T/HEntXGTvgZn/zJ9fvMH/zOP/zQPxCS8NPgH/7i/9Fa8vTarhY/QNPqv/7s3/4z/QPLP+QDQQHj
+X//2L/7P/1LmT1D7L1BQvfsAAUvgQIIFDR5EmFDhQoYNEcoDIM/hRIoVLV4USAIARo4dPXYEQOLj
+SJIlQ5ZEmbIiRIkqXb48yBLmzJkyad5MqRHnTpgnef4EKRLo0JURiR51aBPp0odGmT4tqBPqVIQA
+epHAmlXrVq5dvX4FG1bs2LA+qT6FOIrsWrZt3b7FOsrpWaZp4d7Fm3dtr410/QIAHFjwYMKFDR9G
+nFjxYsZC/SKFyFjyZMqVLQ9u+fho5MudPX/+pqxZ9GjSpU2fRp1a9WrWrV2/hh1b9mzatW3fxp1b
+927evX3/Bh5c+HDixY0fR55c+XLmzZ0/hx5d+nTq1a1fx55d+3buFN9IICjhDUEkQiFyJNwL1nlY
+6df3JcgZALY8Cgc7yQxt1Jv5JJC8J+yGwoSSwAnAnABPoPkKkiuz7h6EMEKKSLiBoBtGIUgq9ggS
+Q0AAbvivKgcFYm+ugTY8ET7x6hNRICT2+w+aN0aBBhYxnHCiIBMVdHAUJ1jMwwkM2wNADIL4G1FC
+JZdUUgwAQkQCABZhyYM/EuEbCJtRkEDCCfVaLKjEJFG8MkNsEporSol6qXAgJLBxrL0k55L+AJsQ
+XfyuPSccC3JHJv8EVDtsEqxyIGgG7YtMWHoJUdGB/BTTIEVRhAaAGg9K8w0uiyzokRwfnTMzJx4x
+iIQcAXjkTIFGecTPQF+FFToKV3Wsl0fYc3QgElTFdMy+IMWyTIJcldPFUeqbFEs/5wJWTmyMhOWN
+SpOMtVprj3PyvzegnRXXYN0k4Q1qFRxMWMJESlbHcd1LF1SDmPVVTqyoPHXca+/FtzcE81BVHk3f
+E7bTN3q5FMz4fo03TCwrLVjdgVoVI0poH/6Ux3dFJbXTesU4k9Vi8wU55N0euVAojTBTdFd73VXY
+4pZfzojXi4dds00X4aR5ZoHEuxMWJPL+NOpZaT8WuWijY6v0WUkTDVbihpolOsWlB1oRzcweGQ8W
+GWm0EUeHv171R4GCHNIoUz8l9mi11x7NCZmlBjCwXuQDzOqpiSyXbvXko88+wZyYWD/+4Ow5apep
+NhCAUd11MuO02YY8csknp7xyyy/HPPNYSeilc897mRgmCT73PEHNT0c9ddVXZ71111+HPXbZZ6e9
+dttvxz133XeHXB7ffwc+eOGHJ754449HPnnlC08OCeWfhz566acXnnnkoKE+e+23n7460L4HP/zA
+ViaObvHPR38x8oc7OX333y/M+1G4p7/++ltdXziIbrW/f/+hx59zNPI/AhaweHLxXpz+lKOU5TBw
+gY8jjlSaI0HpmKWBEByOA5OjQeRQcDkehI4FH5i/4HDwOCY0DgiTo0LnmEUQC4BhDGU4QxrW0IY3
+xGEOdbhDGNYgRSQEjlJQkQsiFtGIR0RiEpW4RCY20YlPJCIqfihA+MgDBjzEYha1uMUdCkJX33qO
+WRZADjKW0YxnRGMa1bhGNrbRjW8kYwum2Byl5CIWd8RjHvW4Rz720Y9/BGQgBXnHXMyRORKUxyrg
+uEhGNtKRb1zAFxM4kDE+0pKXxCQb5XglIP6mjoMEZShFOUpBFpKTVDyRIjO5SlZaMpIZAWMLHVPJ
+VtbSlpo05AUzY0dS9tKXv/yjKd/+00nfIFKVt0RmMsnxSliwsDlmMUEapDlNalbTmtfEZja1uU1u
+dlOalMjlCAeihjaU05znRGc61blOdrbTne+EZznVEM4VwkcA7/BmPvW5T3520wSSpI5ZQLEKghbU
+oAdFaEIVulCGNtShDyWoEeiJHKVEIBQXxWhGNbpRjnbUox8FaUhFetEITPQ4EhSABSC6Upa21KUP
+BQVApyNGZdbUlpscpnM+CUye9rSXwkRhcYxpU6KukpnOZA5Ni7rUR+I0qOWbCy99OlWqBtOkKazi
+MZm6VUjKtIKz5GpY3ehUDOovqlVFa1rxCNSyBmeoYoVrGo8ay2c65haewGte9br+V7721a9/BWxg
+BTtYvBbjqsVRChhesVjGNtaxj4VsZCU7WcpW1rKLBcNhIwgfBiSAsJ8FbWhFO9hbeDU6So1rauOo
+2QyeVa2vnSpbidmbt6o2tXOdpEBoaVu4knW2vNkpbIX7S9mikkRa5W1YcRtQsCa3t6w16y6HO92f
+QtetWXUuXJc70+Zml6u+1alrqTveQRZ3gtj1Lle3+9WBzGC074VvfAObAOsGcS4iAER+9btf/vbX
+v/8FcIAFPGAC51cE9f2NBPkgXwY3OL4zMG0Iu5vepYKXjuIlb4b9aN5DopfCS13vaSf8YZtamDnB
+1XCK9cjhD3qYxDYNsYQp+eL+oppYlwORqop1HAsWK6e2NFZmjMM4YiDf0sbiFEiOd5ziHtczlUWu
+qZBlORBeiMPKV8ZylrW8ZS532ctfBnOYrfwJBPtGKSqYRJrVvGY2t9nNb4ZznOU8ZzqnWQVlpi1n
+VyBmPvfZz38OMy8iPOQZQzmZR94ghpes4SZ30MWGrqWU61poSN8Uz8BV9KLJ2+iTPrrSRh30lHX7
+aUuf8sLS1TSTL72bH5M6k5JOqmOqDGha19rWXiazqU88lz/8wte/BnawhT1sYhfb2MdGdrJ9/YdV
+60aCDNjzraU9bVoLGpa5hcVuXY1JRFM006meLqex+uRtg/razKV0uS/Z7RP+fhvcwhW3UD2t7kbC
+ejmopXdTm50bFL873PvGTavzzUh7KwffA2cku43Tb3/DG+C3ETjCu3pu7rbXwRfHuGDpq+sbCyQe
+lwV5yEU+csvG4+G2UXDGVb5yvUKY4uwdtcQbqXDEurvhaY33Zskt80UWPDkH53kbaQ5VVN98uDln
+37yDrkafIwfoS1fj0FtbdKPDFunCiTjU0dj04zxd62eUenRxXPWjn7w2Wf96GbluHLNswhZvh3vc
+5T53utfd7nfHe971/nbDchzJsFAFEAQ/eMIX3vCHR3ziFb94xjde8KowO22ejYe9V97yl8e83jcR
+6knHPO1Rj/xsGE72ql7+/bo7//zWOR/rdKfejGEvoc1J31PTAwftn197cbyeetjbl+qzL33oZXP7
+tOeeOGJ8afKVv/yGStTvic6MNEY6fepX3/oilYbwY4NIlTLf+99PvvGHYxYxGND89tM+bJRyfvZz
+L/2vkaDz2j9/6U0MqfdWIPTD+9vdPDXpz7k/g8s/b+M/3fA/sTsvAKQr1ju1/XPABDSumYKfCZzA
+AuQ3CsRA97HAgMvADhSf6tCLEBTBEcyKhlEOaCDBFFRBtzDBDVrBF4TBr+CdGaTBGrTBG8TBHNTB
+HeTBHvTBHwTCIBTCISTCIjTCI0TCJFTCJeSOPKGarHER81jAhWCXXxn+jL35Fr6ZkoRwEpuJGvMJ
+CfNBl/tgEas4GAvhFHK5QryRGyZ0Q9OYlYG4kAxhGoPoEMAAEbt5Gag5w515gy08iJO5E2KZC0dB
+kXD5D8DIGIBxEcDIPxNpqzeUxKfIlkackiqpw4LQEi7xEj3sQz6Em5hZiO9wQsNhGUaEGYuxin9B
+xVRJFZ05nEmURc0YlLGBwkORgEwkCEYJGFiEG1DsRYZJiDx4klGwGULMDEMMFkuREydokw3Zk0oJ
+naiJxFm0xp+IQ7UYCFvxFoXYFU/8xYTpw1M0CGOkkieJxZzpxVBsJseRBydBlr6QRljYE7AxxWvE
+R6SoxG3JiArpxoP+KA9xQZNyYcRzQUV2vEeB+EOFdEeDMR+JoBsnNBtNYQ9PEQissUeDzMeNHIp9
+6Zd/+UeBIRj7SBhgPEhhPAhi/Jt0PEVlJIhRUBWnEBL2SJzA2EJI3ECO1EmOIBltbCbC8J1vURmG
+gBqTPMS3eckhOccaQUaE7MVIsZGQ2AiUpEelpMac3MmspIikmUZGJBOnIcqSFMdQrBqAXMiBeANS
+acp1fEqE+aKNMBWCwEhyrEattEuOcJuYYBrBmBvCAEdz8Uu9iZv5AMTwgMKwYcO6ackqJIhKqQ8T
+MRBYwIZF1BopoUu/vMvM1MzN5MzO9Ewe5BzSAZ2bGB3RNJ3PRM3+1FTN1WTN1nTN14TN2JTN2aTN
+2jwd0cTN3NTN3eTN3vTN3/zNNZgG4CTO4jTO4yROJVgDJUDO5nTO5wRO5WRO6KTO6qRO6bTO7NTO
+4pyGNdjO7wRP3hwWbAjP8jRPAGAH81TP7awGAKiG9YTP6mzP94zP+mzO+bTP/DROdrAK/fTP38QG
+ZRlApzMG53AEAHAEA0VQBU3Q5jCGKdS5RxnQrivQ5jjQBmWOC2VQ53hQBRyWCWW7Cs3QBbVQEh1R
+DF2ODo3A9nCMEZCEF4XRGJXRGaXRGrXRG8XRHNVRGC0YABDRQwiGIBXSISXSIjXSI0XSJFXSJWVS
+IR0IDRWIGDj+hSml0iq10ivF0izV0i3l0i710imNgSc1UWFYgjI10zNF0zRV0zVl0zZ10zeF0zIV
+BjHF0B210zvF0zzdUWAYCBXVmiYF1EAV1EFt0kPwKrNghE5Q1EVl1EZ11EeF1EiV1Eml1EpVVHrI
+DB9tPddbLYGAUlh4AiYQ1VEl1VI11VNF1VRV1VVl1VYV1Seg07EDPpyLVYGABEvF1VzV1V2t1Evo
+U6VLvfVCVF4l1mI11knF1EcRUW3jVHLAqU8NVVeV1mml1mplVVj1VBNVsln1KWH61Fs91nAVV2L1
+VYHw00Rq1jMSVsdI1HF113dF1kxd1nR9vVoFVWvF13zV11X+xVZY+NRt5Vae8lYTBVd4NdiD7YRy
+hYVzRa5mXdeBaFeEldhxTVYFmVd67VR/NdFo3deO9Vhr7dd/Ddjgy1YMLdiJRdliVViGxVgyeliB
+sIMpkNmZpdmatdmbxdmc1dmd5dmenVk+UNaB8AM3INqiNdqjRdqkVdqlZdqmddqnJVovsFdv6Iaq
+tdqrxdqs1dqt5dqu9dqvBduq9QZ7jYJmMNuzRdu0Vdu1Zdu2ddu3hdu4NdsosFcz8Nm7xdu81due
+3YNfHQg+gNrAFdzBJVyo9YNDZdeUVVxirdj2uFiMfdaN/djJpdxrtVeAHVlfGliTXdzOzdWVBVbc
+Q1yI9dz+0qXUxtVUz6PXyMVQjq3c14XdkNXWzI0tez1Z08VdRgVd1EvXl4WFiM3d4O0E1H3c1bVX
+14Xd5P1Y2cVQzKXdUdrcgbhd4TXd3T2ull2m0RUI4KVe0yXeTeVU1h0I5FXe8s1X5pXV5wWm6LXV
+7g1e612PhuVU3+WBAbDf+8Xf/NXf/eXf/vXf/wXgALZfZ2CAoBWIevCCBFbgBWbgBnbgB4bgCJbg
+CabgBE4EezWHLtDgDebgDvbgDwbhEBbhESbhEtZgc7DXwHO8FWbhFnZhxoO8kh2IVhDgGrbhG8bh
+AMYAvxUIBpiACgbiIBbiIabgetDe33Xf3P1e1U1X8RX+CPI13yieVvRNMvUVWNtNYtyFX3RtWd/l
+3ixW3CXONux14nuV4jOmViqGBee14vLCYjD23C2WX9fzYjj2XDFm1vA9XjTmY1dVYzZu40BiX1iY
+XjuWWDnGXt8tB3pg5EZ25EeG5EiW5Emm5Eq25Etm5FIQAAOGhWjqp08G5VDWJnCSYYHIhi9A5VRW
+5VVm5VZ25VeG5ViW5VlG5WywV3KKp1zW5V3m5Xeap1KGhT7A5GEm5mI25ktOBh6GhXsS5WZ2ZlD+
+p5czi0qQgmq25mvG5mzW5m3m5m725m8G52qmhk222IEYKPBD53RuPnu9hipw53eG53iW53mm53q2
+53v+xud8dudrsFeLur5/BuiA/qiSAmYbCOeDRuiEVmhwzgRlTil1huiILqiYkubENeQwllfwdb0y
+huI+9uhR/eNA1tw3vmiUReQuPuIvLmmDxWMy3uOPhulSDWmRJqVBLuSVfteTxtg6xmmJbemW5eiY
+FupXvVyarmmS7mmD1Wl6pV8ycOqnhuqoluqppuqqtuqrxuqsdmozKOByFgi3y7ywFuuxvru+01gM
+NYdvUOu1Zuu2duu3huu4luu5puu6VmsUBuZ1CIC95uu+9uu/BuzAFuzBJuzCNuy9Xgd7XQStZuzG
+duzHzuodNlfOojyytuzLDuvNq2jSTeqD/WnIfen+oYbpmTbqULLpzj7Ype7dlEZtls5oJm7WoBbt
+0S7q0jZtpG5tcVVth2Xt3HbXzzZeYO7o2Y5i0rbtUsJt3zbW3Z7f3lbucAXuJg5t4uZj4z5uQDrt
+5w5X5qbjI86ESADv8Bbv8Sbv8jbv80bv9Fbv9Q5voPVqWHAvlpPvi9u4sx6IbwiD/Nbv/ebv/vbv
+/wbwABfwASfw/P4Ge8WvAlPwBWfwBh+wAwPmC2DvCafwCrfw9e7byf7b+ebwBnO5n/xQztZuY43u
+2J5u6j5j677uDUvuEddV7g5W53ZxXi1xPRZuFO9jFV9xPsruGedVGBfdzd5eH2fc1x5joD5xHDf+
+Xx3f8RVrcSKnVCAvPhmHckut8Y1OciVXXiZv8rV68iqPVCn/Ot89B0gw8zNH8zRX8zVn8zZ38zeH
+8zg38z7oaselMmrD8zwHs1yzb4FIhzEA9EAX9EEn9EI39ENH9ERX9EUH9HSwVzSrs0iX9Emn9Dm7
+M2CGAjnX9E3n9E6P81ZQZmjT81En9SuzNhCXUBEH80q9ct7Lci2P3dru8j7q8VWPcmXm4p2mcluP
+1Fb/PNmG9SWX9Vnfo1rn9TDH9TmOcSFH4mOPV07OYyy/8WAXdmAG5CY3dmd3VDHXOkU+5m8H93Cv
+ZE3mZALQgXNH93RX93Vn93Z393eH93iX93P+NwB71QIuwPd81/d95/d+9/d/B/iAF/iBx3ctsFch
+oIKEV/iFZ/iGd/iHh/iIl/iJp/iEFwJ7FWZx1/iN//Zk1nCBEAAamPeRJ/mSN3l5J4AjpuaFZvmW
+d/luHmdOPmeJpnnwc74+h4V21ued5/me93l85mdg9meBJvqivz6CxnmDfvmlZ3qWb+iPX+buq/mp
+Xz6KRnUFsWhth1RfTztgp/bk5fJZz3atX1RuhzqeJntH5fqv8/qvf92w7/KxT3uzXzq0T3tGXXut
+a3u3p1y4x/Yv13q6Dzrf7YBLMPzDR/zEV/zFZ/zGd/zHh/zIP/yJSV1YsAcuwvzM1/wcggH+e6WF
+JwD90Bf90Sf90jf900f91Ff91Qd9WrBXZYCi2Jf92af9J1IGe9UDydf93ef93o98ClBmMdj84Sd+
+zbeHI04qEV2OT11+E21+FFUOP+2wEG8h5VcO5r9+589+6E8O6W8x6n8m608O7B9/7S9/7kcO7/cx
+ZbmKGHT/F+yP95f/EewBAOiB+cd/vaj/+8///n+L/QcIEgIHEixo8CDChAoXMmyoEBsAhxInUqxo
+cWAvALA2wgLg8SPIkCJHkixp8iTKlCpXsmzp8iXMmDJn0qxp8ybOnDp38uzpUyfHoEKHEi1q9CjS
+pEqXMm3q9CnUqFKnUq1q9SrWrFq3cu3+6vUr2LBix5Ita/Ys2rRq17Jt6/Yt3Lhy59Kta/cu3rx6
+9/Lt6/cv4MCCBxMubPgw4sSKFzNu7Pgx5MiSJ1OubPky5syaN3Pu7Pkz6NCiR5Mubfo06tSqV7Nu
+7fo17NiyyyKRAKsXUtxGe/Ui4URMbjG6gw6/Pfs48uSbSeDGDc1Jr99Ciw9trpQ6x+LYlXPv7t3v
+I9t5SMAab11CHljQqPfmDc34bWjYSMyH5lu6QNuw6NuHJcGJE7bhlgd02Gy03XcJKrigWtDcAMso
+0CBhXXHjTScPLPKMAh+FsIjx4HO3AZedcU4gAQs2E8JiIhJvMPgijDGWZd0N71nn4Q3+5E1Hom4d
+StAbCRqxF9989MlzI4IyKrkkk009MoqAvL3xY4ajnFgdhhqu6GGHYpBXG3w8wvdIiUi02CSaaapZ
+FDQAXHmgcaNA15tQAvF2opwCGafbfen1CCdu/zlB3oDQPRjmmokquiijjTr6aFn2EfQepJVaeimm
+mWq6KaedevopqKGKOiqppZp6KqqpqspWHhtu5ARH+iU51Bu83Vgrb8AVh2svb2bXC67A3aejrQFC
+5QSlEpAJC5izTmergLfZSihxvA1KFK7S7QcgecIByxtzuIoHnRN+ugqhs0ORoJ+DscJ53bTw8Spc
+tdHpGJS3uG407IHWjtgUEgZuNMr+iOkhitS8Pca7q62+clTrtfty65+UQH7bi2383nbuKOnWye6h
+/r2blK0Y71kyosXey1G+vJVHrsHF/stUqxzBulHMTPEqL7SI8urwRhDrSGB06f34LXMXCzvxxhx1
+TJXAYrgqj0aI3qffjiQerN1GWhI13LobSUDtq1A9smyN+9640XPRzTwyoFmPDGV1G6kYtn/3ct31
+DSciccORAqPoMb6Hhtd11cXdACDWWmu999yNE8cR3mPD1+5TN4x4sxiJByUPuW+nDPfoVX5d9n76
+WQ432KoTKjg2hLNsuH7Mwb24sXLHPbnjdJ+eYd/MAn55yE1F7WrncIPudt2P667+m9dZwypP8H9j
+yDrlrt8Gu+xFjZJe2H//6TTqz5MOOYLDcT3czU6FGDDOtheHnnrYDTmk45jzjgTZI0MeIUeg0bHv
+ledpTknRit4jvpF5aSOCc9y7IBcm/cltfWwrHlMsVzN0wY0EwKEg9rbGu4NFMISlG10vCNiq7jnw
+RMjykO3C1EAUNU9MI5wgBrUGQLZtSDcgXAoB8WbA4nhQPTncG/789zvc7FA950Lf5FKYnhVSpWYm
+2g+9SKgiWvEmWrDY2QnTB8HmWEtyTEGWhVS0tqBY6FkuO1kXT0hCXL0BQ1B0HHt6ITUIHekp60LC
+zTw4vqD8sGS6Cpqt5ChG7Nn+ChuiUwr8SJCeR+RhkGxcGSLppLA43lEoEMOGjUbYyT3tcRR9dMof
+b9Y3S16wOomEoyZvuMj78caRULHiiSjJyvhx0WS6mVcYsRUdUCZxlM0p5SmnAsoHyYNsxeEfCYBW
+uiSGaZEorMrZCAaLtK0RR5g8nwjzd0S7wcqYTqOUE3EDq3I+5UPKypAzg4KEUViphiUcYwBzqEQb
+TkVzsPobh4SioW9O03yke9eHgonHKG6JnU5xJ5lIYEe4zbOeutuTQY1YFN00UYAkfMoyhRfQz42C
+oEjM6HZ086GOPlGUDF3nR51CT9scrVaI0txueEdN6J1LbpXrn1QcdDNbTan+OBqSpgh36jQzqi91
+YtPbCKnnN8DhhgQljSlSnJC2mq5tQui8qCX/Z8aR/RSrGaQnl76Vxafm1Hmy7FpPtQbQspqwrFW9
+KgtflbakvaF/Xt3ojpQ6sLHCR6rCmyhVZprWWq01b229Jz5N1zzrVW94ddXeXYEalc75apO9eAQY
+DQrMnf0MsJRjWl6JkqPATktO4LInLAkFLZUJM47bipgJX1Y047QplE95RPvexSv6vPKGPMtVyZJm
+r8fellhWeYPBIjit2L3WoMn9oq3EUNpe6lFiuJWjxpwDAN86BbhZ45UEqGs/1iYyubQ1rW7LddB2
+ukmn0FLvN/eGXuy6bLv+nsxufKMLXtSqZ7xmXRWCE5ymsQ2EsE5pJkHsIqmBfDUqExZIhQnD4PxY
+ZcN4o8uFSZDhp4R4xIIpyPWogmK7QHggVmmxnhQs4xnTuMY2vjGOc6zjHfO4xz7+MZCDLOQhE7nI
+Rj4yp6YUFAm4iCP8Q5xURIIbqm1Eyhmq2udAgg0BD4XKVGscAORBNZGQx8pj/oh8O5JiTzbZyaN4
+AwD6eqWRVBkkafvdRm7Q0zbN7F5hjsqZP3LlOoekymu+8ke2fBQqd8SoGhkJbujcES0bjNFDuQEA
+3nYkj+AU0SDJEqWNEiSRiDnUSMYLCYqn56AECcpC+RCnkarmLlftzwL+xTJHLM1kLmf5ym94k60N
+HZRg5xrXJPj1rIeSB49EF5BpE4NWhT0UW88T2a9eWR7aDItHPLDV0nYznLVFFEsX+9vDPrSuoWsU
+RntkWYPmCLGJDe8UH/tE5HayR1bWOTLxD9n3drV/1L1oXFs6SLI+tVs4azcARDfbVfs3NqwEyCTJ
++90V/ze56bNujVCNTuaO96HfPe9kC0XPq97XAzv3noqT/DbffMShBb6ie8kDziMPipze82akYrzW
+IW+5yPfzwFtXGVhXIjfIiZL0f28bG9wmzsqMxfSMD53Wvb45wuuCDf1kO4BbfziuD3T0sJ/b6kAP
+OsALbGIvZzrTH1/+88XDbuCKI4HhTLaZu7t2IpYTO72whZCr6j6igD0Cy/KWKNaJrni+oxvXvS0K
+u0F3KKTD/efxBg7TB8Xnhc9MDO+ZuuMNvPGrV9nEWXdLqgemN5iD/SgaL0rczZ322bN80FSOYdIT
+n3YSLKvid6+7wWrPeNLzUNlN/g/lJGr4nyNu7WSPvNIbL5TaR75z6aF82adN76mRffMRYzraac/8
+tBecoKd/i8LfMKLUMxr8/R6/SFxN5vDfm/oc10jAbJN7Qn+EPIGOs35U3Gpt06EIX0joBrHdGyVh
+S3rcwLKAjsjVHpPl3a19msUdoOzVH/NBX71hH7xh4KSBhI78m3n+bVuTgd//tV72QR7BiYSDnR9b
+BEgeCEzN2ZsKCgVo9YLpnR30KR7xPd643V/X/Nr+8SCujYLAyFvdhYQLUaAEYMjwxcrQ6dN+WImB
+tQio6V6GYAM2jB/9+Vz0+aDasaC0OYEp4VoRYp0EGNi/OUFIpMfm5ZptgB4hiR4ZlpvdvBkM0sUj
+6JmOjNqn/ZuRJEXsGaHZodzoWVqOFGEhyt+h+d3CZcwDAd/ZtZzH2Q0VahfyeVr/AV3NUWAQml0U
+4iEi3qGtdY63vV0Yjty9AeGKuMolbpP/kV3Qvd7okWL47WFbtIktKd69CZ5SFGIjkuKuJSKWtQgj
+hhy59WB2rMz+DTyIs73Hc9xMFFabr0iAg3EhKFqavDkBKIbi4lme9ImNzIkivXnEClbi0oWdbwQF
+aCHOI+TSG3weLaYbr4mhpc0TFeriWjhB1b3b//VCChqFMMZfQHKiohmjOzKiQYZeeogEA7hdrNRX
+tcWZRY0EFNpZhQVPUQRJhnFjoZHaHV4dRoagoAVaQprimrlhOpYksc0HJ0aE09VhpbkhAHRaCs5i
+ot0jPoYER/IjUAalUA4lURYlYCiXIYXF0ZSMyXTHUpbMC16FtzCl+SHGU/aMUWalVm4lV3alV34l
+WIalWI4lWZalWZ4lWqalcvwEW/qEF2JGCralXNLEW14GIM7+JV7ORGoAAJBchF/+JWA2xCjUnmZQ
+TUkFJmImJmIOZl1aRpAoJmRGZkVkxF5WZWZQTWNWBmaKxmaGRiqCxmeWRkRwJmFeZmnC5WnaJS1y
+RmiSxmiGRmeCRmx+xmx6Rmt2xm2KxmvCQgcwgm/+JnAGp3AOJ3EWp3EeJ3Imp29SQLFlJmXE5g/s
+gnROJ3VWp3VeJ3Zmp3ZuJ3d2p3T+QHOKRiqKgXKWp3meJ3oqZwdQzmqGxm5eQifEp3zOJ33Wp33e
+J37mp37uJ3/GJySEJ2wG2xMwAYEWqIEeKIImqIIuKIM2qIM+KIE+AYCCJpbJAz30J4ZmqIZuKH9e
+AntWJkf+wCeHjiiJlmh+/ifiOOdkxOaAQqiLviiMxqiDSmiKimeFXqiJ5qiOjqiH7kt7gsZ77qiQ
+Dul+ouiVqahksKiMLimTNmmD0uiR2miu4SiRVqmVdkKP7sePfkaQXqmXCqmR1mZnKKmTlqmZLimU
+iilr3uiXtmmOZmluuue9WMMg1Kmd3ime5qme7imf9qmf/img1qkCTChtBpsPvACiJqqiLiqjNqqj
+PiqkRqqkTiqi+gCh2iaWMYACBCqndqqnfiqgWsOHosZuLgA5nCqqpqqqriqrtqqrviqsxqqsnmoL
+XOqYBlsuxIKu7iqv9qqv/iqwBquwDiuxFquu5oKtrmn+rq3CrDarsz4rtMrqAozqaZRqtF4rtmYr
+rNZqjQZoiuWqsYaruI4ruRIrsnYrhS6rtq4ru2LrtPooiG6EqbYrvdbrtibrZsQmuJYrv/arvwbr
+uUapZ1Yos9qrwR7sqb6rlsYrLPjCKjwsxEasxE4sxVasxV4sxmasxj6sEeBrYQYbKYSCyI4syZas
+yZ4syqasyq4sy7asyJKCx2ZGKgqABWyszd4szuasxvoCtZrGbiKCFASt0A4t0Rat0R4t0iat0i4t
+0watDcQsaqZYNnwB1Vat1V4t1mat1m4t13at134t1WYD1KomRwgANTQt2qat2q4t0yJCz4rmvYio
+m87+7YaGaWpaBpmeqd7uLYOm6d1WRipaKN0ObobC6ZZ6RpcSruKe6NjirYDyLeRGboH6LZJGRuBS
+6eJmLn0aLsPKreZ+bifYbeVCRt5KrumaKeVKaddgLuhmLueS6r3IAgLMLu3Wru3eLu7mru7uLu/2
+ru/O7gQ0rmYGWx0EgfEeL/Imr/IuL/M2r/M+L/RGr/HWgfBSRioywO9mr/ZuL/f+riy8rWvey7wi
+LPm2K7cKrGzi6r+uL/v2a8Cq6XIQbPnO77oqbJwCqfjSr/5e6/nC78d+a/sGsACba/VORuAW7P4m
+8Kza7+F2hrUqMATfK7oWKgAPsAVf8LEWsGQccAT+d3CrMjDDjq8HjzA59O/fPqf6YrAKB/D7nrAB
+yy8JkzAIwy5HyG733jAO5/DuBu8Ee0ZsFq/0BrEQDzERQy/19jBuZqoOLzET5/D3wisNb4Tntu7i
+ii5ppliLnq4WN2nqDuyUUvHnvm61xi0Ya64VeytHZPEWrzGMdnG6rm4Zuy74jkbixvHgnnH6YjEb
+7/GLuvFnXK4dK64Y++y9AC3bHjIiJ3LSPi0Sc0ZsTi3YRrIkTzIle63YNnL8lu3ZKjInd/Ihuy0U
+jzFHOKzOlrIpn/LFdiwm/y9HhKzLvjIsx7IssyzMrrLMYhnNorIu77Ip82woEzJHiHAMR7AJj+7+
+Y+jrCifz+rawMTsGBw+zB8+wKMsrNHtwMV8xR+yrMm/zuDKz6mYIAlezAkszMFOzOEPwNaPxRmgz
+N7czAdsyZjzzOScwOcMtR0QDJ+SzPu8zP/ezP/8zQAe0QA80QedzImhwZMTmoVIqQze0Qz+0pFoq
+PJPtRjAALxQ0Rme0Rm80QUfDHOsmGQfyHSM06T4uH5/0k5L0YwCySNPtINuzFLc03eIxBacxSt+0
+gvoxpn6xTLvpS4dviPa0m9K0D5s0Th91hKq0M7OpUH/pT9NxSDf1lRL1reoxUiO1TicxT0u1lT41
+SHOEOmCBWI81WZe1WZ81Wqe1Wq81W7e1WO/+gFI3xg/PAl3XtV3fNV7ntV7vNV/3tV//NV0fMfr+
+MZbxgVsfNmIntmK7tTp8tJwG8zwrcDrncTa7s2W/82DvdNeEc2TPbz0DtTl3Nv1Odk2v82WfNrB6
+sxdvtmjr72dDNWS39vySdlFXMGrfdgZPtGPCsGyT72t/dWj39sHSdlVXNm7jtmq/MTgLt287Nv5y
+hDXkgHRPN3VXt3VfN3Znt3ZvN3d3t3RjQlwzRmzqQgOUt3mfN3qnt3qvN3u3t3u/N3yXty6E92Jc
+r3ffN37nt357t6j+MkzDwhRzNZjSt2KU7lWjdFYrKxwLeJV69WPHNIMTKVU7slEfOIITeGL+sHSE
+C6mDPzeEb/iOTni+VriF83GCZ/KCg7iOdjiXFrInvziMLy0jZ3ZxbwQkVzKO57iOb+0l07iCw4LZ
+xriQD7nQgvLCRjEsgAIvLzmTZ6wq+/iIp1gEzDKVV7mVr2wEYDhizGzNNrmXf/nDgoJzt3hsM7fB
+EjeF2/Zxn3ZyE7a6mrnB/vaDw4Iwwzm7onmUG/eas7mWH4Y823m7yrmH0zmg1yues7Jp7zmf6zbg
+8nah1++YI+694DNHV7qlX3pAHzSjo3CKLTREfzqoh/qjSjSUa8b1XjSmp7qqV7pH+zdoA7iKh3if
+G4aBlzgbn7ipM3WsmyiLS3pQ77qJijj+osOCGtv6rc96YWg4sPNopDtwVC87hwq7aVq1se8xrt/y
+VkP7hva6s/+6ttctshNGrVf76V57POv6txdus3PGbqJDCLw7vMe7vM87vde7vd87vue7vr87M4T7
+YMSmOyyDwA88wRe8wR88wie8wi88wze8wLuDv59YYe87xVe8xV/8vqPDum/GAz/6uh76tOu5ortz
+m2v2cns8pLs6bAc3yvNvxAcGMo+8ZZe8VrN2y2eroJM5y9/8s4J81Iq8zG8zzf+4PHA2zztrzvv6
+zh/9rPr8ZcR80HPz0KP4yTP9syZ9t2/EIHwA13e913892Ie92I892Ze92Z8914P3pq/+aLDJgNu/
+PdzHvdzPPd3Xvd3fPd7nPdy//FFmKtr/PeAHvuCj/SBsvGbUcbr3p7T//EYUO7mXO9//hbInfoca
+fmYgPuXr5+I/PYk/vumaO0VnCOtmfn5yO7s/O+kz7tonaed7fuSC/m5ne+rjp+lz/L2cAQvkvu7v
+Pu/3vu//PvAHv/APP/HnPgpEvl8APDIsP/M3v/M/P/RHv/RPP/VXv/UvP8SvvuXisjYUv/d/P/iH
+P/GfgeVjxm4qOZin/5I/uf+G/EZM+ZXHv/xTeZZrP2Rwufrnvy6LucoDN6FbPUCQEziQYEGBLWAl
+lAdAXkKHDyFGlDiRYkWLFzFaXNj+MGGuWB9BhhQ5kmRJkydRplT5MZfDjRlhxpQ5k2ZCEgBcrjK4
+k2dPnz+BBhU6cIHDmzWRJlWaFAAJhwuGRpU6lSpPhAoZLtW6devLjivBhhU7VmVLrBy5plUL86hC
+nVXhxpVrsKhNnGvx5s3Y9Olcv3+nXoXlVW/hvIQ9klW8mPFJs4OzGpastu3gt4AxZ95ZF1blyZ/X
+8k2ILkRp06dRp1a9mnVr169hl2bmMjJo2zUJu1u2m3dv37+BBxc+nHhx47vd0UZ7mznbu7D4xJY+
+nXr12OiMPm++faZoWJc6hRc/nnx58+fRp1e/nn14SMq5x69I+AkT+/fx59e/n3/+f///AQzQvifg
+k89AiCqTh572GGzQwQfZuyS7AymkyDvwIMxQww3Te++sCimkT8ARSSzRxAAJ/BBEAxNckMMXYcxQ
+QrtWrBGWC2PMUcf1PIRsORuZE/HEIYks0r8UfQSSuxZ3bNJJ8WbsTDsl5cPxyStz7JEwKm0T0sgv
+wTwRyS25/IxJLNHkMErPytzOO3Wsi1POOV1LpMA2PyNMl+P47NPPP43T5U48JauMAWboTFRROdWZ
+kFA3nUoIKs0opVQwMh89rLbEGuvUU7IewzTTtRK8rNJT/eKMzVEn825SVGGN69LaWMULsU9xzTWl
+UGmtlbLn5DE11mGjUnVKXwv+c5XYZaOa9Udku9pU12mpBYnXZ6FdqlRmuQXK2Gwlc3WVcckt19xz
+0U1X3XXZbdfdcY0YFNylCJMmlHvxzVffffnt199/AQ5Y4HulkXfepBK04N2FGW7YYXe/PTgv78SQ
+x+KLMc5Y44057tjjj0EO+WKDJaaJMJFRTlnllVEmuWSZKkOC5ZlprhlkMRx9OS3vdDa5155jEhVo
+jIQe2qJVjcYI6aRl4pnpi4p+OqKopX6I6qppxPqipbWuyOmuIboaa7GrJltqrsGWMm2YAGjb7bfh
+jlvuuemu2+678c4b27UXytvvvwEPXHC49077psERT1zxwNfOiITHIY9c8sn+Ka/c8ssxz1zzzaFp
+PCJoNg9d9NFJL33yzj232vTVWW+99NRhj1322Wmv3fbbcc9d9915793334EPXvjhiS/e+OORT175
+5Zlv3vnnoY9e+umpr97667HvXQwAUE9olBseygP1hZCK26ms+n7bKfIhgvuG7iVCHwAJHmLocMLT
+d7uh/AHoBecbsbU98D0ECaN4AwCwEakbwa0XCXEf/ML2QP61TYFGgeDUjkUR8rEvWrB4X/ZAmDQn
+POIhb6BfQrbHEQ46JA9OaNsokGChZ8lPIit0IEcK+IYYTkR+OnRIbVZoQ6w4BAk3aCAAJXK4HcIC
+CU4YRefk8QYSIjEikcn+4RIj6BBoOOGIN5zIFi8COpiQT4xc2WAXQ5jGkj3CCSwEwBJ7ERkbQkOK
+sNgiGts3w/1lUIi06kUF8wiZP/4QLUHko3bY9zNYmNCERkGjBLDhxSouZ5A11E4etKPIzgCSXhns
+ZEKw8T81jhJc0OCeTQbYmUfIcUpi6KIQCTlJyFhSlg6BpAwhs73/ARGRh7RaJKkYvjd+zyEjJCBH
+FEmrW9KShWjUJDYaIobHUZAEB1zfXaqJwP9lExv02+BdHoENBOahgf4TZzcTkodz5gGFccTGKO4y
+Ck6Skp6jMiYssMFOWIghkqysCAmAGT89zhKDtRwiD/fYmSPy0mq+TEj+AZH5LGJicoeaTOZyYElQ
+O5IAi4pEwikXMgpYYPIGSMCkxXCSQlg8ooHywEbnjvLNlb50pW27US86Z8DBvGGPnXNCSbcITjzW
+k6iEYuM+31DMhvhTIjkUZRXfttA9Ek6jsaQNLsmHBHQylDZww8rbQinJEuqzjsFsH9wiRauMTvCE
+Ym3oQanIEPJt7xFYnJBM0ZkQF97ohOSTp0NuQILtoU4Cd8loURELpI+CTqSdGSBTEfSGJ3ptoLDs
+43KWKVCCRvGjhewlM5l4g8b2CpNvayMs7mlBs1o1IZnN4kN1ytq3ajQycr0LOQHgBHaK4QZv6EUc
+Z0m+2sCTiuSLo/r+OMi+wyaWuSC6QTj1edy3VTUhN3DCUxEqS8sey4/z9CIHA8tVuM7WJlKFyCga
+O9JTAlSYMbzoQypZ0KvKdr4crC1KIYJJMbwhUjIlX15hAVx//vUhpiSsYT3ZXAUfCJJJraUN8+DD
+i/yMhvKtHw4NaNcLaxQJB/QseUFMAvMS8Q36TEgdtfpBeTghrdiyYoYnwsECpnK1P8SZfSNKvnBC
+kXt1hEYcZYYT8gG0c6vEiT+juD+eBhioe13pUBccZfl8NL0bhoz67uc2XAZygi1F6wLd9kGsameV
+H+6q+qoaxc7FTQIOdsgoTptDBCpQbg58m5gteeeO7k23tM0xTgr+2DZ0QnLOb+CpkLEpThYfWYV3
+IXRekRBHEx5xFFOU8qUxbbRHeJcrYuhem5fyhgtmmtSlRlYTDUNkO7JYKRKosqlhjaffznrWkxEx
+rX+L3ZfhutZJecSouYKEbPJXwzArdqyRnWxlL5vZzXb2s6EdbWlPm9rVtva1sZ1tbfOa29329rfB
+HW5xj5vcbYWdK8mdbnWvm93s1nXj0N1uec+b3urWHQLrnW997/trjVsIF/cdcIGn24WFA9u/B55w
+hXdbnPfmdNr6zTdNps1sT6s409AGu4ivbeMHn7jHDd61iyct46nrONhOrrWRG23lQ2s50EruOe+M
+QBI1t/nNcZ7+c53vnOc99/nPgW7z7qV8bLWJwSmQnnSlL53pTXf606EedalPHekxcBnFaxN0rW+d
+610POjBydjvvMCJNZc8QPdBC9LLVpj5hcvvb/zOmj4u8NpAw+90btKYEx27sePf7etD+w4fTHS1t
+h/vhEc8EuYdc5XX/++PPo3eHO4TskLe8eALvwME3vvCJ9/zbFz87wtj98peXfO76XnrLZ/5Gmy96
+5z8feyOFXnajV73lT48774jCBb33/e+BH3zhD5/4xTf+8ZHvez4IXvS1oQMHoB996U+f+tW3/vWx
+n33tbx/6dLg6yB0ChuSPn/zlNz/yTRF223knBdVyf65CkXb+168dLa/q1v0LwpmX94wwk3j//zvl
+MWKucdgPAA1QMeKP+WqvNuwP/xxQ/+aO8xzC/w6wAsFCAPdO4xSo/SywA1EiATWv+erPAUlQICCQ
+8V5vAj1wBUsCAycvITiQBWXwI0Cw9USwL0rwAb+P8FRwBmfQBVFPgUShFoiwCI3wCJEwCZVwCZmw
+CZ3wCYuQARQwdgijAjThCrEwC7VwC7mwC73wC8EwDMXwCitgByUwIbIACtVwDdmwDZ+wANSvdlLv
+9v6O9dROarxE9vSwRGiPCh2PDv8u98ROgSoPEPHODucPD9luDxmRRPoQdmzPEPFOENePECXxEOXv
+BhPC8Br+sRP74xFTJxIvsewoUQ4VqBKkIBVVcRVZsRVd8RVhMRZlcRZpMRWpQQCmEBJrIxu+oBd9
+8ReBMRiFcRiJsRiN8RiRsRezwQxTMCFsoBahMRqlcRppMRPikHa8QxkGZhu5sRu9UWBAABdDcAHR
+whAe5hzRMR3ZxRCYkf4cYgO+MR7lcR4BJgKucXYK0AdlsAbv0OIYMAd1UEX8EC0oUB9XEAh1bwMN
+kgX5MRH9cQQB8v5OUBNhoSAX0gIRchAdIgYvsgIbkiIbMCKJZSLJsQc7sgIzshIdAhWGoCVd8iVh
+MiZlciZpsiZt8iZxsiVfQQrHcSAdogxSISiFciiJsij+jfIokTIplXIpmTIoy6AdFREtjiEnqbIq
+rfIqcXId7lF25nAU0QQRKZITPXEs8QMUPUcUvRJLShEbLTEtvzITS3ITyXIuyxIqH9IhSM8tr2Qt
+8bEt9fJJwDIuYUEs6dITzdLf/vAvnYQvudIvFXNHAtMn5bIw5/IwJQ4t8vIxdYQx+U4Ix+EzQTM0
+RXM0SbM0TfM0UTM1VRM0l68ndREt6KAGZHM2abM2bfM2cTM3dXM3ebM3ZdP7BPI1w281ibM4jfM4
+VTP9siYhN/IkLfAjBTMkRTJWSFIyK9I5UXIrO7M5sdMAodM6pXM6UaU6hTMhLLI73S8lTZE70fP9
+vrP+PGEhPMWzUsgzFGvjPNtzWtSTLdkzP6nlPe0TIudzWOrzLO/TP9NTOzXQIY6gBBz0QSE0QiV0
+Qim0Qi30QjE0Qx+UJ21QMBXhAUA0REV0REm0RE30RFE0RVV0RUFUEeySaQijCDR0Rmm0Rm00Q+Fw
+OTUyIQpRM3MkMuGTMCmTES0T6zDTR3eEMxeUR5FUR4A0QB1CSIdUD4sU/BIiM5tUTRTU5BwzSzfk
+SQ0U9qa0E6uUB6/US2FESbnUIRrBEtz0TeE0TuV0Tum0Tu30TvE0T930BMSxQ60zDlghUAV1UAm1
+UA31UBE1URV1URk1UOPgRZOGME5ATym1Ui31UvP+lAi2VOYUSBvp8VNBNR7DMRehNCHMUR1RNVXP
+kR2Ds1RhAR5DNVZltR43lQAVEkH/Ey7Bc0BHElJZ7kBxVT9rleNuNVjhT1fhUz55FTMKFDEJ0lh1
+ZT/7sj+htVMANExxcFnH01ddDlir1VOktTFXkhvItVzN9VzRNV3VdV3ZtV3d9V3JdRw4tB9htDbK
+IAPwNV/1dV/5tV/99V8BNmAFdmDx9SlbFVsT4hjgdWEZtmEd9l3/YFghrkvRFELA1FmjdEwNk1uB
+Bi0rFkLUlFMp72O/FFldVUo11vPK9AxhAUtJNu8kFuUo9mXb42IvM2NTlkg5lv8Sk2ZhVkdVkkn+
+fdZBbNZIcTZnqXRndcZjh3Y9QtZWx/VhpXZqqZZd5ZVUERYW7pVgubZrvfZrBdZgk0QwFbZqzfZs
+pTZigXY9YfBbP+VaMVZStBVWmvVmzdNtwTVmuyYf8ZYx4NZu43Nut/Vg4/Y6+5YxwnU72/ZwF+Nv
+jVZuBZc+lfZl+o9xFyNxlxQWPHVWObdzQ2FUXdNVT1VVSbd014VVx9Y6YdVzWTdU7XFt+TMh2hRT
+abd2bddO+RRrCxdQG7V3ffd3gXdRH5VwAXdSb/d4kZd2NRV2p1Vom7ZmTTZrURZp4W5lm7Fln5dB
+npZYRzZ72aNorXQwqTdpifdxsdd7nVZvtab+K9H3PMDXTMV3fGPPet3xTNs3PbZ3Yh1iCN2wf/33
+f5twXh2yXtHCCsfwgBE4gRU4DMuwfMM3DQE4giW4f3NUbYKQWi0XLBw3fJU1cuOibs0XPzO4LNQX
+a/h2hDU4egu3gz24KkA4fEUYhR2jhKvmhGU4JTYYflm4hafiheE3hm+YJDB3TRc3iHFYhQF3h3m4
+WCa3ZCrXiHeFhqVm95Cziq34ilOzNf0UPmPTN734i8E4jHkTOFMXPsEAi9E4ja1YOS2YOZ33fs3j
+fVl2euUXTOg3KvESjtEjf2W2e/W4POT4eum4jmeviSWGaf+4E/h4b2f2jwO5fuOXkBHvju/+0n4T
+eTwWeX0bWY8fGY8nU5IPj5IJOI8vGZOl+Gm8gyWxcpVZuZVrcid1F3CBsilpuZZt+ZaXUmz3b2lr
+Yypd+ZeBeZW1knnFtYih2CRymGWVeImFwodZFoiPmSVOmWlsOJpFIpmvd5mZ2VsM+WCe2JqFeJqT
+pprBmQaR2Hy1eZt9wpmvF5qjeYhFNiE2t3XpWR5Bd4tF13T1eZ/JBXV3mXJrY3XreaC78XXbeEdh
+ARWpcaEZuqFj8RZj2Xx5MRkpuqIt+qKPcRkdGH6f0aE9+qMX2hqJWXFhoUdLuZMrOZJBGfS6eV4Q
++Y8z2YQ3GY5RepQ/eaXdTpQjtWdLOab+a3im77emd1pMcTpMdPpXj7SUoUScjWb3JvipofoJBZgi
+DXiBrfqqsdoLG7iMXRWCo/qrwboWKngAudeYyxkksBmS01mdeYKdIdmdjxmeodaszzoW0tqTA5et
+5cKt8RquoViuyxoWOLKu7fqcOViv97qlweWbCTsWAFt/6fqs7zql1xqxCYKvU9qvjfix+zghliAF
+QDu0RXu0Sbu0Tfu0UTu1VXu1Q1uU6HWoHcIeZHu2abu2bfu2cTu3dXu3ebu3aVuxs4UwlIG1ibu4
+jfu4V9sVmHpoXnucBxi2BRM+YYesIbuYrVO6XXW6M5CIrRu7s9a7G4e6Ue6PXKe8zdv+vJu7W+Xp
+vNm7vUUHnlBw7dbbvem7vicHuFBvcfR7v/mbgpqvvwE8wAEnvhVRwA38wOdG2xR8wRm8wR38wSE8
+wiV8wim8wi38wjE8wzV8wzm8wz38w0E8xEV8xEm8xE38xFE8xVV8xVm8xV38xWE8sRrJltyMifpr
+uy2EgQgqbrwMg9wmnypC2A5IsioqbrTIgObs2MAsqvgHz2L8yZmDBGhMtB6iLYSIt9qmpLZstn5G
+iDiozUwsIuCsc6DBiNxKiySrc8TACU6LsuZr1aAMyuW8MLZnhz7KxCIMwSLinZCgieK8xhLpWbxc
+O9gruxLClHBGkXqBxrTK0gy9qjD+ac4l/TPyKsK0qJv0HCJ6YYcOq8vvwtMtzI5OSSKI3CXc61k+
+CruOys3Hi5wm/dULQ8q9R4F64RGUa+8KXbNeC9Rfi75oPLcEy9cH3SLEC3SUHNaRHSnqfJG2CXxu
+fSKEvHCMfMe/bNitLMZIwIVGS8etfcvZKtnBnSucQALUSSF86Nkj4hF8C9jOfMcF/VhWyMAwQr9q
+LNUhIpyI3cyYKLbCvd+R4hFEK1KyrG3wC0GgacLevd27vTMCqor2rMaYzCF6waUGT7yoy98xPiNM
+Kaxey4bsHeHli9fJC8wp4rc6p4BOS5HoaLJWScK8/TlmLONlPiacoOHri4EmaMv+56vO+MfLfjzM
+I0LO0tzOqMqOkLx/AOzRm/zYZ77pdUcMHN3ppX7qqR7Cbw3X3k0tJIDbzE0puq3qwT7sxX7syb7s
+zf7s0T7t1X7t2f7B7fvt4R5y2B1sQCfu7Z6+517k7n7v2fveEPzv/57A/RHwCV/ABR/jCj/x+/sF
+rzu7Hd9zxBvxGR+8AZfyDQfHsSbyNZkiLT98tZt2NF+mHaJibKb0TZ9mgBtaTub0Wb/1Qyb1kSVm
+XH/2aR9jRCn0f9ohLoEeeL/3ff/3gT/4hX/4ib/4jf/4eb8PYN9XCOMZquD5oT/6pX/6qb/6rf/6
+sT/7tf/5n2H5ayVBSgH5xX/+/Mm//I/fp6dYgTBEqTtBSyJQkIuapTdaa86E/dEfldWf/d3D+1kl
+D+O/kAECFix5AOQJPIgwocKFDBs6fAgx4kMSAA7Ko9cpo8aNHDt6/AgypMiRJDNeOkhRosqVLFu6
+bAiAxMFLJWvavImTJCSLBV/6/AlUJUGDAp8wOYo0qdKlTJs6fQo1qtSjT3gSDYo1a9CUAi/m/Ao2
+rMiTArlqPYv2Z8yDBzy4fQs3rty5dOvavYs3r9tFVtP6/ctw6MFw4AobPow4seLFjBs7fgy5cLi+
+gCsDNstgmN7NnDt7znsAZUXLpEmvFUhTrOrVX3d27Vk6NlbBRafavo07t9T+qq+vyv7t0qxX1sSL
+jxUNPDnW07BSG38OvZPrgbCVW4dIG5ZR3dy7e3/Km7rv6+QVCscYPT1xsrDMln/vkLlz9fTBTs8O
+H3727d/7+9cdHn75lXdefQZ+xZ57Ay4Ii3wHPmjTfdUxeN1+/12IYVQBTkihcgVCCOJxZY3W4YDM
+BfJZiiqueNcwlJWoXHbfhEFjjTbeiGOOOu7IY48+/kjjNy/CCJxZfLCIZJIrBoIckfA5GGKUHkk4
+npOlWZhhllpSNaSVpX0oZZgmNenldVCKGSaVZcqG5ZZu/rdhlWv+BSaaUSZI4pzAnWlniGrqaVmb
+bw7aXZyAWlZnnxDieej+njIJNA89kk5KaaWWXopppppuymmnkvbRZaNnZZfNF6aeimqqqq7Kaquu
+vgprrKZmE6qoWZklQCme7sprr752Og+ZtlrG3Dx3HItsssouy2yzzj4LbbTSHktMrcMClZ0WVWzL
+bbfefgtuuOKOS2655m6rhbXX+oQrIdO+C2+88kob7IjrEvtoc4pG+ee9s1XHH6EC42aov1gluq+B
+jBrsF58JG9gvwz4JOnDFGqorsUQIP6zewhlr5TDH6kX8MUsUW4wyUwWX3NLGIkPnMctAMXdAOzbf
+jHPOOu/Mc88+/wx00DbPgbHMgVUHhxxKL810004/DXXUUk9NddVKw1H+tNHmkcgAFEJ/DXbYYgcd
+mr1aq5XvfC+nR/LZDp2cctxc9ua2xiQOt3bHwta9Ush5G9c23wrBLXfKKwvOkMt/sxYz4hH5vThr
+gTtOd22FX87E4ZQjpHjkYjW+OUxpew7d5JQTjvnAmofe3t3okV4c6KwrdKKStt+Ol4uVz747YZH9
+Dnzwwj822e68G4l78srDxaTZvDMEOez2Zc036qkTunronUuPk+zPCxQ99ziZ7rj117+Z/ebbi1+T
+99+Hz35N5CNu/vlbpk/5+vGP5P7z8O8/kvkJrn72yxL+HKc/AIKkf7xjzjkGAMEISnCCFKygBS+I
+wQxqcIMQbAX16pb+HXN0YYQkLKEJT4jCFKpwhSxsoQtHaI4Pug0zzuCgDW+Iwxxu8Bx7+95B/qdA
+kAiwegArIMoOiLgEBrEjDJwdEJfYkSGCsIhGrBgSBadEKGqkiax7ohY1IkW3EbCKcJLh2bL4RS6G
+rli/aqMb37gpUBlvdqSSlR3viMc8xopWc2QdrnQFx0AKso31ap0PF8IcREhhkYxspCMfCclISnKS
+lKykJRdpAzNqLTvXOJcnPwnKUJrrGpo0Gq6occlUqnKVrLQkInp4SC9+UTqllNkYydifK/INjVpU
+4+Zk+cUwnu2WuPSOLuvGSyj6knLA1KIwN0nFYqKvlixL5hKX6Tj+B2pgm9zspje/Cc5winOc5Cyn
+ObfpwT6GLju6aIA73wnPeMpznvSspz3vic98ulMX1CwZDc8J0IAKdKDm5KHzDvnDfC2AHAxtqEMf
+CtGISnSiFK2oRS/K0Bb082PZyUUsPgrSkIp0pCQtqUlPitKUqvSjudhoxoSzCozKdKY0relFFwBL
+HzJnoTbtqU9/SlGNqnNzHV2pUY+K1KSqtKVDzd/dYgrUqEq1pzg9KEIbpNCpanWrFhWqeHxYVKWK
+daxkPSlTv/o9mHJ1rWxtaFUNedWEHoSnba3rVL0qINaFtax87atSz5pX7T3VroSN6lsVhFDm+MEN
+jG2sYx8L2cj+SnaylK2sZS/LWC+4VGLZiUIzPgva0Ip2tKQtrWlPi9rUqvazUdgsw4yE2djKdra0
+xawfcvq+rBZ2tzbFK4f0Wh2P+nW4xEUpYH8rWItAlbfMxehh83TVnTZ3ul11rcH2Wtzsave4clLf
+YKkL3og+N65yFQhdw4tecvi2u6cLrnbfm13u+lCt6U3veMmL1bnWN73rBat74Qvgvso3rd/dL3jv
+S17m8EIcDG6wgx8M4QhLeMIUrrCFL8zgT1jXX9lRwSQ+DOIQi3jEJC6xiU+M4hSr+MMq2PC9MLMC
+DMt4xjSu8YV5gVv/6dbA1O3v97Ab4CAndcDPoy+Pp4vguEr+98jT9fHzgCzkKC/VxesyMpN5m+To
+7vjKu3Uy76As5TCXlMjHKzCXC5vlxOaLADpos5vfDOc4y3nOdK6zne+M5zYbgMrXyo4QqADoQAt6
+0IQutKEPjehEK3rRgBYCn4eFKxrkedKUrrSl8UyAHDcwX6BYhac/DepQi3rUpC61qU+N6lR72giP
+tlV2IhCKWMt61rSuta1vjetc63rXvI51BFotKlxZQNXELraxj51qUGjaiVs+s129TMf/innaYwZ2
+o6zs7LqmOZbNzjZboQ3cqwiX2uQOKZlnh21vr3XbOu22urcK7nVKu9zlPrcfzfxurrI7twe5hSf+
+DfCAC3z+4AQvuMEPjvCEK/zfxbD2obIDhldIfOIUr7jFL47xjGt84xzvuMTB4HBAYSYBCy+5yU+O
+coXfYtlddHe+pRpvos6b3tS2d3K7styXb3XfOtavzrka8/aKm+b0trl3lftzfbN8jS5Pek+DXr6Z
+Ez3MRncq0p0+VZ5v2udYjyrU6Sf1qUe56gjEd9epuvRf5svfKW+7299+8IY3NepXcQAg7o73vOt9
+73zvu9//DvjAC/7uDgi5nkYO98Qr3u0rt6qauX52n359gGEXe5DJnkSzR56mWmc25Ddf08kTceiW
+FzPmsah50Ds37cxsuuorKvopkr70Uj79LlP/+op2vuX+B+k0sn8P/OCbmtVzB/tV1NCG5Ct/+cxv
+vvOfD/3oS3/61E++Ggw/J2ELf/vcB76yHc/tg7D50uQvv/nrvOfiU/4qQui1+98P//jz2tHqv/1B
+BCDp8+t//+TPNPjb/Xm5d1GxJ0aVR3vvZXvIhHsCKFG7x3QByIBBhX1rAmYHiIATWCbpFoET5YBq
+B4EbGFEEOEwGaIHFlYAztIAg6Fasl035Ug9eAIMxKIMzSIM1aIM3iIM5qIM7CIOJgIFekh3rEABD
+SIRFaIRHiIRJqIRLyIRN6IRDuA4/aCWYMQE8aIVXiIVZuIP1wIKIs2QqKIH1J3sHMW4lCGAneEYp
+CIb+Hdh6HwiGDSWC0DR7ZghfaKg1GviGK/h//GZeeThRcWg0FUiHw2WHpqSGKsiGLeiGeQiItkSC
+g8hXhSgzeOiHieiF+bIF9qCJm8iJneiJnwiKoSiKo0iKpaiJgiCFTpIdrpACreiKrwiLsSiLs0iL
+tWiLt4iLregKqUgkZiEGpgiMwSiMw2iKW9CFgsMcX4ZcMsdedDdf0IVu0Bh+P7aMQudfzZh5h4RY
+0/hk1eiM1IiNqKeN0qhTvUAC54iO6aiO68iO7eiO7wiP8SiP7zgK3gh2ozCP+aiP+8iP/XiO9RiO
+t+ePA0mQBamPvUCO7wMAC8mQDemQDwmRESmRE0kokRVpkRYZkFN0kRvJkR3pkR/pkBmJgiBJkiVp
+kh6JXympkivJkm4TEAA7
diff --git a/Documentation/DocBook/media/nv12mt.gif.b64 b/Documentation/DocBook/media/nv12mt.gif.b64
new file mode 100644
index 0000000..083a7c85
--- /dev/null
+++ b/Documentation/DocBook/media/nv12mt.gif.b64
@@ -0,0 +1,37 @@
+R0lGODlhFgFnAMZnAAAAAAYCAgAASAwFBQAAdEgAACQODkgASCoQEEgAdHQAADATEjUVFHQASDsX
+F3QAdE0eHVMhIABISABInEhIAIM0Mok2NI84Nk9PT5o9O5xIAHRInFlZWaxEQbhJRgB0v75LSLhQ
+TbRTUcBRTrBXVatcWsJWVKdfXW9vb6VhX0h0v8RcWZhpaJJubpBwb8ZiX8ZiYI5zc4t1dYd4eMhn
+Zb90AIN8fH9/f8pracpta8ttasxzcM51dM52dM53dc94dkic39F+fNOEgpmZmdWJh9ePjdiTkt+c
+SNuamd2gnt6lo3S//5y/nOCqqeKwr+S1tOa7uv+/dOjBwOrGxuzKye3KyuzMy5zf/7/fnO/S0fHX
+1//fnPPd3fTe3vXj4vfo6Pnu7r////v09N////35+f//v///3///////////////////////////
+/////////////////////////////////////////////////////////////////////////yH+
+FE5WMTJNVCBtZW1vcnkgbGF5b3V0ACwAAAAAFgFnAAAH/oBngoOEhYaHiImKi4yNjo+QkZKTlJWW
+l5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGmkM3ysvMzc7P
+0NHS0CjT1tfY19XZ3N3Y297h4sxDlQDj6OLn6ezZ6+3w0u/x9M0A5r/3vvq9/Lz+kQDqEpiLIC6D
+txAyUliLIS2HsyDKkniIIiyLrzC60tiKoyCPq0CqEpmKJCqQJk+lNLWyVEtSKPPJ3Ddz0stRN0Xl
+DLUTVEyaQPvVlNTzU1FPRzsl5fRTaNB/QwNGHTi1IL6nu5Zu0qqpKVSsVME+4pqJLCazl9Ba8oqp
+jAIA/gTCIOXkVsAVo52OAABgV6kmMxr2Cgbil9LLGh/OIJ6r6QgBLAfuMm48YYzPT1siF7a5qUyD
+u1HibtaUWfLoS55NT+a0+DSklqXPxK5EJkuhm7NdV8pMYW9i3YJsT8q99Wqm2MQn/cihZRBuzasv
+RenrdgnwM8uFQzpSOfrrTcihW5KyogiYM89VF9cUWq7i3+sRTVlB5Lyj6ngNd/58pn0mMUmY4ER6
++R2nGWCEMbUIGQE2QUYj3Fnm3VibIPgeJ178kEFz4Imn4F8aEJbcWY18EcQLViziVoITOvLSFgXA
+5R4iVvxg44045oijByAU0QMIQAYppJAwPHhIFILx/qUeieDFCIB1igjBg45U4nhBlVSaAEIQYiTi
+2IzXLbTKF1mUaeaZaJr5RAc5aKeIFStw0RErVehARZp4mrlAnmlCscILU8yp3y5iILECBI7AKaeg
+q3DxQ5cuMgLgCg5uZBwuTpiAhBgQKZqRK45CKqYiftZ30aW1WEFDEF58xIinn4L6aCMIabHDDhye
+OqgtYgSRonOLwBqrrKImglAQUjyEalg0xjlRLKEuotayFIoliLC6whKtsVVFai222Wo7KyLT7kpU
+VeCGK26xhJTLmblZGZKuuutW1C2tUc1Lb7233TuqU9c6m9At2wJrLb68JEEGP/rG4u4iBaPnr7S8
+/ohhgsRnNOxwLgU/LJVh9YR8Qwsl3HCOCyHIIDI986yMjgwkzKBMyy63g1LN8JzAgskoq4wzOzT/
+3E0MIIiQggM2CG0ztbW88MUZDAgc7y5aFlHBCDsk4ebA8NryxQsZd7DoV7oIQcQKBpyhRRM/jOCE
+VV3X8kQRcKYtCBnsNsQLFHSLPQjecL+bi9lxApCFEjuM0ASzuXwNtdSMf5yLlkQEAcIOSmzN9S5O
+A6Dxs3HPogUIOSBhRQATy1LEE/d8vq+3t5BhhH0YA6zLFELo47qlocNChhC/unowLRYzDLnevbvy
+e/DC265LEqgPsntITCsPvCEef4fV9CVVz8ry/vYOjzwh3KvkvSrgh+985AGPDbrgvl9PbuoRNev+
+6wjHzzz29L8v7/G8g18r0sct8SkrEeXTyfkigaS9sIgRBCwge/bSlzBNAjAV/BACAXgIGAmmOxo8
+lybK8AC5TGdJh4igBAuhOUaUYQOWqQEIMVGb2jVGAh6iRA0N9iYOFmJE8RFhJ/CzIPn9qxDZoYR/
+NLEcBNClAUzIISWWw6FO+XAQQOzKAocjxUKoUBEImY+pIAEY+GhCCh0wT2OAkEVJkMc8EtGYB/cC
+pRBKjjVmTKER85fCBhlpEQ2c4SYGEKC3XWILCQhDGyUBIBNE4BG7O0IGtZg8SkRIEV+k2CJM/oSi
+R0gShWs5Q4aYY4kaiKiLlvDCBUjZCNcRMYh3zMSXFuEDHGAJS1e65Y20xKVGvPJNurTSjUbQox8N
+6ZhAosEf3ZIkADxQXsHU0QWI6SNkIrNIKlJAHSkpwEvIUEKK6AKfxrmncZbJT4BSxBZUIIgTlsic
+ZyrnE1bQJkws8gxkgieaIEDPFkaiBlCapR21dxwn7UWQTErEpCqlCMBQEJRrKdQKnkAaVFKiUB2g
+aEEPCk5YEpRshygV7VwRAU3lbRaZQsIA+qc+kBLCVriKhaos0CpcqIpVNpxaNwNXCGTJoldWyJ5C
+fcVDl35Up0blKVJjyT6lNvUgW/TfUp+6jzkhJhWqBqxfJQ+4Pqd6FXZXrepUv8rHsWK1q2e1qlnF
+SlVbsKWt4wurW6O6saxKFa5gZCn+5mrXiijNZn8FWmDTEbTBuqMSHDDsODCgWHEwtrHeeCxkucGB
+Y1j2spjNrGY3y9nOevazoA2taEdL2tKa9rSoTa1qV8va1rr2tbCNrWxnS9va2va2uM3tLQIBADs=
diff --git a/Documentation/DocBook/media/nv12mt_example.gif.b64 b/Documentation/DocBook/media/nv12mt_example.gif.b64
new file mode 100644
index 0000000..a512078
--- /dev/null
+++ b/Documentation/DocBook/media/nv12mt_example.gif.b64
@@ -0,0 +1,121 @@
+R0lGODlhoAHkAOe1AAAAAAAASAAAdEgAAEgASEgAdBgYGHQAABoaGnQASHQAdC0eHigoKEIlJEYm
+JS4uLlssKzY2NgBISFIyMQBInEBAQEhBQUhIAFBBQVhCQkhISF5DQ2NDQmdDQ3NEQ05OToNGRHhJ
+SJxIAItHRY9HRXRInJdIRlpaWppLSaJJRqpJR7BIRa5KR2NfX7JKR2BgYGxdXWleXmZfX31ZWXJc
+XHpaWQB0v29dXLZKSHhbWolXVpVUU5JVU55SUJhUUqdQTrpLSK9OTKRRT6FSULRNS7hMSrVNSr5L
+SL9MSr1OS7xQTsBRTrtTUL5WU7lYVsJWVEh0v7deW4pqab5dW8RcWbdgXrZjYcZgXnd3d8ZiX8Zi
+YL9lY7RoZshnZb90ALJubLFwb8prabBzccpta79wbsttaqx6ecxzcKt8e4aGhr93dc10cs51dM52
+dImJib97ec53dc94dqiEhKeHhkic39F+fKeKiaWPj9OEgqSSkb6PjtWJh5qamqGamqCcnNePjZ+f
+n9iTkr6amtiUktmVk9+cSL6enduamb+jo92gnr+pqd6lo7Ozs7+wsHS//7W1tZy/nOCqqbm5ub+4
+uOKwr+S1tL+/v9+/dOa7uv+/dOjBwOrGxuzKye3KyuzMy5zf/7/fnO/S0d/fnPHX1+Dg4P/fnPPd
+3fTe3vXj4vfo6L//v+/v7/nu7r///9//v/v09N//39////35+f//v///3///////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////yH+EUNyZWF0ZWQgd2l0aCBH
+SU1QACwAAAAAoAHkAAAI/gBrCRxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJ
+sqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iTKl3KtKnTp1CjSp1KtapV
+h6QkWdrKtavXr2DDih0rlpFWsmjTqkVrdq3bt27bwp1L16vcunjn3s3LNy2jVRU/MKhAuLDhw4gT
+K17MeDGCwY0jS54c+THly5gvW87MufPhzZ5DcwYturRkBGkqvgAEdPVP169Z+4Q9W3ZP2hJx89St
+k3dv27uBB4+tWvhO3ziRJzeeU/lN5zWhK5ROk7pM69eZL2+t/Xl3hthj/oZ/OZ789+jn0ROnWN5l
+e5bv4aefGX9l/ZTx7+Ofn537+tr/5cZffwAGeNyA4iFoXnH+FejgcAb+xmCEzSm44IMQ3mbhQPqh
+1KFJH4K4oXwNajghhhJSuJ2K3p1oIoopvgjjihOFWJKNI+GY44j28dgji+D5qJKOIRFZpJAeIpkk
+kAvZOMsBAAjQykdOHhDAJ1QqSVAhAABwpUcf0iJCl2TS0ZGNmZDpSJYcpekllmfyaKMXNtRCZ0av
+jEJQiIUIAAoBcMbJUSEUxHJkQa+wglEpgGKUJ4da1vLkmn1OiZEsoUC6USkDrJmJlIKyF+ksCWD5
+qaUWydJFHagIhCOj/oFyFCKpsYIp3CZPRCKLRXdeumqrs5Zay6kaxTGGniESS2uoNUYKay3PZhQJ
+FYnI8mqjtm5KwAVd1pltQangkYUnFEUrLbUtRGqnAKocsOZGmlDxRwyRZvLlpMwKqC2W5mLEyh5U
+zKAutNjme5G9WOJr8ECerJqKRIQautG/RsjRkRcAFNrRK4kUYQZHGHt5wbsb5edso/1mFEoQVrS6
+L5saEWunt7J+N221DyncUQ5OsJrRk2Z6AWpHN1RxbEc6a2TyRsvKjJAncUQt9dRUT/3DEUgcofXW
+XHc9CEMpK30ylmKaaRAebFSt9to/VLG21F0cQYUmDkXMENRvs13F/hFLXNH131wvgpCy7t6dt9pt
+L3HEH20ADrgWuyrktNguXlT2zAulEsrmnHfueec6KNEFuQt5QoUpYBdc80aXh11LJ2Vw8vnss+dg
+B+2cH3IEHg8zBHRDmuNOew5mPDE6RggTXOtBwQvveQ5oUDFGpnefvtCTNJcsp7qcRomqRq8Q4nFD
+pqMuOZlvLmxR9wCQbJApcbzSbEKoGEt9Q5V+9IoPSlCyEZddch/4dsCESjikfAl5UrdgNj+eUOIJ
+h5BBehB4oZTAT34R8c0rDvEEA8bkgTvog09AKELyWa8lS9NJw3wGHQpW0ILxy6BxHngIDL5khagw
+EkdwSB0XkkhU/jpBxRhIVwvn+NA9A4PIBSGiG0z4LCZCJJ0OwVcHKU7whEis3IEScsQstmSJDpmi
++ipUOix6sYEz6uIZWQLGIDFJPVw04xplWCKCqHGOK2njdJJ4KBbd8UdAjNAfUchHiegRIWJcnYoG
+OaTt1bEWjPwhTA5pkERq75ECieR+tBgjSMqRPoWcCCX3FMpvoUiTmwykg1AJSJmM0lWlHCONMvnJ
+BHHyJtWCDStb6coYHsSSlPsJD/xgx1raUpU6ecUTisiaXfKylzYk5Rtn8ooi2MaZS0JmTjaBB2Zi
+s5E3McUaeifNGd1kE0OQzTdPEp8TYKEv8ByLGvRgCQ1IoQmK/oinPr+igXfuE56IOMIU3iCISWyl
+n/9M6EH9qdB9qiEE7zQEPhsKTw3woSIagIxpNqoYFmCgAg5YQQY4StLEGECjJTUNDo5AAhMAIQUg
+aABKU1rSk9L0pixYAAM44IKR3pSjBkiNNm+SiixA0gh3mCZL8AAw1I0iEj9Igv9ktJOivmAOxoQj
+GnNSiT+YjgaykUU0W4QTTHj1hC/ww1jJ2JOu1oAJ5quFWNm6VZww9XQvsMMizrCESGwRJ0X1JOqA
+mRHCXoSpSK1FKPbaV7rqaydPOMIe6nCEKCzifp20SRYeVj7DXsSzFYlsD6xwhDNc9q91tckojjCG
+Q3giXUpl/skfPPjVWCpyJ6sdww7mELkMPRYnsgiEomAZ25Vwk2GJNedMgqso0N6yIc51iCzwQERm
+FlclyiTIW+OKWuBSl7jKbZJtKTLd6lo3vDDBmauwyl3H2qS8mkJvQqK7EPgWhL6/1dA6s3kT+4KX
+qnSsiX/LCeAt7ldE3jUvfgOc2pcMmMC+hdCBSbJghDz4vAV+SIUJcuH4ZpiuExbJhgvS4RFD15Ey
+6bCHI9zdEIPExAJRMYzF+9yUqHjF3Y2wi00ZkxvPeI81PsmNcZzZHAvWJiYe8o/ni+KYlekhQyay
+QBIlETd9CXzIGpiYrkyRR1WSOc5kX8YkdkmMPInLChxa/n2/C2SEYPZ6VgrUmZcX5Db/TAFTSh5D
+oizlWuBKVzkrgaG8oLGMqKoONxhYISSgOokc2mV93qXrgmmRPv0pUHfqlUL4rBz7McTS2AI1nYeq
+YT4mzcJsPnFCwjWuKqtZWkYQQ2/NnABINHoiN+utclg56cJqKVpNe7VB+Izhg2yCCnsYrkL61ev8
+MpgjzZZrqlWtkBWSkyFiyp5GYsAFKmxCI4WgQ7Qd8i9v//cgqBSzAH2tkWg9q9fELvZBZJGIXM3a
+IMy+dZ2Z/BFNo9q8bmRIrhfipkLXLBS/ukgpCtCKcT8E4axqYVYRUgguU1rh2Hq3vqUNcH4zJBV1
+aDVC/vI96gaHMYl2UwgcwnA4xLmt5XGI29zwZ/GnwVxqbYsa1hwHOMEZxAtmmjTebj41vhGB53/z
+uUNOjZEPuRtlG1850eOQ85tHtg5rVR5BHH7yfUMkfws5hfOGd7uxh0J3vGsI0w3SPLPbLhQPPB5F
+FIg+s4HL7J+Lew1MXbjbYhxOwf4eQcSO98293eyYoEIWvn0QkpfZ5BQhNJl5jJD6TW8hpYCCQPRc
+sj4Q4glTzQjXGRI+0MubV+8C++P/LpDL+fuz6eEYFQA9ctWNPuCQl4iYxyzLWmywgw0RU5dqnpHQ
+1RDaG48IDTEYIvYZfPUUcdPw+TUA70HfIIlPdkKk/v8m7hM/9zRm0fJh0jAh5AEnOISwkYs8kFGc
+4Qx6Oib4tVoQJ0LaJVFcchS/fN0XFqQOdANKXkcgHzZL6+de7Nd1/UdImISACUhW81cd43VxLOaA
+FgiBznaAGFiBBqiBSNZkBfiBDdiBDyiCpHaBJihfBBiC9PdsHLiBHtiCMSiBAyh/LwiDKJiCNxh+
+KmiDO0iDCyhJGViCMkiEQNiDMJFCSOh/MyiAQfhMTDSBFGiER/iDVdiECOFOFPVPCLWF8dSFXlhR
+DBWGeQGGZFiGY3iGdGFRGDVTP7VRNvWGJBWHcghUbliHnkGHeFgaeriHeShUEeiELFiEJJiDVxiF
+/iOIg4YoiIPIiC6IhT4IiUkohey2hOoniUxIhStohZuIiE8ITp+YSpbIgCdYiIpoijrIiZFYaomY
+ipiIR5q4igo4ikKoipPYioRIbbQIhbF4i6HIX0O4iJ3Yi5kojLKoi404jMboi7sIioGojKcYjbko
+jYfIir/ITpTYdNloEUvmccn4S9vIjeFYitToiK9Iis94jK5IjLC4jMWIjLb4juUIjfOojjxoEXM2
+ENJnd9r4M3Gmj9M3hRORj5unJtc3kP9YkOlTiQfzZJICJcImjhmxjwJBkAwZjF/nJ9gyC3g2LN+X
+jhSnkYAnaHbyfDUYkpdWkYWjerCHEaImEJzi/ikRCZIHwZF59iWZpm0SeRE26ZGf8JICOYusVxBr
+R5O1tzyTc5IJAWzCkpRKeZSbByrL0o9M4y6BF5Rz13e3d4+PWC63tpXeOJQDkW1YGRHmIjTtsm5P
+2XjYkjxF2ZUVASsaV3Jw6ZVwApZhWZdmeWuvBxFedm7rUzAFN3k7uSiqgzEmWZhTpmwQcZbDNzJd
+Fn+nxyt1MpcVgSl9Fnk0g5eIBIKBWSspNxGPBphxeWsVR5fWaJiAdwBBM5N19Wf3ljrL85b1lXB8
+ojGWWRHGkmX/Y3CcCY5riW+qw5IUkWtOd2u0mZpDSThqmVqs1nFLeWtOCRE3A1sZAXZXaRHx/jIv
+WsKSv8l/5NiYBSN5GlFuAtNuGad5PnmRn7l5X/Kdl5hJDuMQKYM9/rIHFZMR5Nl6IhA0OjkRHDM+
+GLGfAwGf8SmU0Yc+V7J7iSkRK9MyDUkmVyJ8C0mVFuF9WAJA7XOQAzFw26egn0B3/0kRPPNE5VJ9
+ZFIo3eOaE1E0RxOXKNolFHAJIMqenqgSQzd1V5M1SMc1X4OOJoE2U0c1Vfc2MheAG5GjN9c2fOM3
+Pbo1SocRStpyibM4jfOkRwA5vIigJ9F2hRc6cldtE0dh2Qg7slN4nXN4uIN215YRXup2xROmKPGm
+zgM90vNmTzOmfWSUKxE+AlpG7eWMJ/FK/lxJEJaHp33KP6G3E69AQB5UPYGKjcGJEjQkQZDKjChB
+qHk5ZRz0qC9BQiP0BCF0QHoqYp5pE+kncZEqqJnqS8opEONHfr/SjXY0q/OxY8FJq7Wwf6RZTKvK
+qq2adXlpfzLBq7rqe1XUqwWBq7k6jqXIrC35Ra6Ke/Eoj0eGqXq5jtdKjyWhqQdaj+oIrc3agOIa
+rS7hrcoKrtjqq+b4qkZWruZ6rtPamdcoqehWqvbKp7UIr/Eqr8KamdpKgvwannZGVQOrmC+hqcf6
+rTB4sPq6sATmsJPqEYQKsem6gRKLkdSqE7mkTvhai5M0rwA7jTYxTOyqrgxLTcvkGhk7/rEfMUoW
+O5k4UU3X9LHA+hBaqIZ0IQhkUE9YIFH5pLNwYYZCKxaKsAWNwE9pWLRrQbRMqxaCgAL+BLRPmxZs
+SBEZ5YehQQIeUAEGMAE9pbV5eIdiqxgbcAQqMAIdYAGE0Ydlexlu+7aTQQIQMBg85VNyKxlB5bIl
+sVm1sF3NOBKR9Qd4sARnkAg54KwaWxNZAAOA0LLjWlVG5QnJ9Y0qcVdOBVVSFbhdmgVXZbNAmq0x
+0VW1FWP/yq0uYVYUlFanW4034VZwNRBzhYqFahOYm1eM5Vfs2LlHFrMWi1hJtVh8pbsoO7IxEVmT
+VVmnVa0o4bedpbiiKxOiRVqmhaiu/guPqsVarmWdlqsSs0VLYFWvI5FbuxWbtFuw7yVcxtuuM3Fc
+mVS5zGtj6uu7p7pc00a/ybRMAwG43asS9oW/BMsSDwbAOaFeRcRe/YsS/kXAiyvA03axJIsekBvA
+DlxdDBy9NvbAEHy9LTLBDVzB9wW9N/rBCqzBG8y+K+LBI2y/AHfBK+xgJnzCqHuEKsylMFFiIuyu
+OgzCm+qOL1TDG3vDMezCO+y/Mby+M+yIQIy+MAydMlu8MjwSnJbDNoyAS0yvKXbETxywQYyQFrdl
+qElsyEFlS5eQYtagD/EoHWKRtQBAH5kQfxmfrJSPFOqQ/ZqgXWI2FLlnWvzE1nsQ/ntsZahZxF0c
+EUApEIuWfBxXxbD5aSIJk4oMEYeWaC75yIiMxnuWcAeqSYdcoJFcxRDRkwgjym8cY338xJ52PR05
+yiRJoAj7wnY5EKRiayUXb0/8nA3xdINMnbFmvrq3kcJiER76xOnGlyNKwmXsPrQpxt9xbNqndn0X
+lYJHwcApegUTbpNmy1sMSfO5bBkXo80pEdxmbkPJKNwCAMfMEOXGeEZUqiljoFGcy7aXfPEGHfRm
+b7I5liKQzrAMynt5lwznOtq8zQIxzMK5PKfpaxB3f78MJ24ZzROx0Kqaz1uCyRjsEK/Xl7J7yusL
+ciKXEL0ymGVZuxMRLUCndQYh/nVDWqSHc6RQSZQQbUc6+nI7h6VHEKWQ7NBDo2lTenNGZ9M3jXmq
+k5z9PBGh2caYrNJL+nIwd3XCetRtXMqETNIN/ZDoAwD8KBCEh6ahoKbOw6YvTRBFSae483Zx58QU
+/SyXIxBk7Tx692vDadEXvRAsSZwFsdV459XCk3iLF5LTTNTYO9UP8c6fLNiH6s1w4gWpx6JM5Hmm
+15610DqFjRCl5z/HKWes6XcV4cqubKMGIXu093O+qZ6c98qCzRAY6sm77M+/56kGgaHOR5gVYXyt
+S3A1yj7hzBCxqh+pbdembZYxmjGiENxy7c+1kH2MWRAMCgtjUqF3fNooHKsu/lF+53cT6YfE9piL
+7gd/SVzNM0KsMJF/VFwQvJqyHBywAHjeTLy7IMveW3q+KFzI8N3d2R3fXBzBPezDoXuO7a3f/R3Y
+/v3eUCzg963e+T3f9W3fBq7gDI7FnJuvCfzfA36zxo3f9L2uAU7gC+7dEa7hG47hGS6KyJzgJF7i
+1mrhJm7eH37iK96OCA7iAP7iLN7gKe7iBX7gEy7i8evhNA7j8p3jwLjjFC7jNr4QOVu1buG0SD4W
+Sr7kYtHkTq60Ua4WUD7lXHG1E5G1eZsZcbvljNHlXr4YYB7miDHmZH4YZn7mbQuII+7jKF7jEn7j
+F67iPT7jc77f7j3kPy7n/nAe5y1e5H8ez24e6H5e53Y+6DjO54hu6Hge4kFe1EAO4UKu4/zN4xw+
+6Y+e55Su6Zle4YSu54p+6Ive5zFL0Ize6JEu6e5d6kTs6KrO6a/u6adu6X0O6m8u6nR+54U+67b+
+6Zsu67pO66M+7MQO6Il+68bO679O5Ki+58je7KGe7MHe6w5+E3F8rF62ZHGM3dglmab+Etk+3s5+
+7A2xx5EtAlK9aZrcIRR5xrINZbY6kXbsxqstu5rM7R+ax3Wcx6K57koSyAaZKvGOPA4pyCO93hFB
+ynCSyPWOEMaJJAoPzwVNBSbLk6v8JVAdEcOsHwq/dZN9EA9vZhcfoivJ/tgOMS0Vj48jH5PDYvLA
+Dp4agS+z/PEJYZ7qMikSLxCs0ANMwHj+6AhTaRHrLOhw5j4aDRE2zxGTkp0XsfM9X5WOoCzB/Nwv
+n8/YTPMK8aAM/ZnqpjR2cO9cvy0LZBESzT0FA89a/zJoGdPc+PUm+pkPndvQfekXcScL13D61tNU
+WtNYitM/p20JbRB6/zY516RA7fcCcSdxzzBDKjU/bdOI//cVLduDnzdX0zeHrxCahpjvnkkr7TaG
+D/kI0SshEwCQaaFtrhB2c9Ku09bCA6Zo/RBQvXauXzt2cNYXYTcyo2m1Tztv3ZtkRvtcvTmwf50a
+8ztCM81szdW2g/uV/maSgD3u1V5pUgkldeco4vMx1xmR0b8QMvAFj039lqLW/VkRlf3tjvw9GQ+g
+2Q9uQ8OcGvH94V8Rdj2dqY/vo5+YBlqpSkKgio3IACGgVS2CBQ0eRFiQUhEwrxI+hOiFQqyCtETQ
+qVWKwCeIHWtRenLIYa0XgDyeJCiRYsFZBzCi9AjykAyTMDuqLJgpAEeNHG1CXNjw50OcB1vaGAqx
+ZNKCS5kaLDUAwFQAEwn2fErQU5c6qEjWzJpRKlUKosZaDbu1zg2wWaNSrRrrLQBHTNV6bdr26dyp
+EwsJDFvr7tfAYuH6pVo3sFq2hflWdXVgKtLCBJ0yvVxZs0FUYzzl/t0cmrNnwqIrd/58MLPpwKhB
+sw7tejVszbNt2qZdGHfurLt5P/X9e2hw4T+JF0d5/KFy5B6ZN1+uF3rS59NVS7d+G3v2k9UNeude
+Orz28cO3l08Inrt66+zZtz+P/rp8mO+h278fn75l/R3xN/+vuAAF7I++AYU7kDf3CjSQQfQSVNDB
+8iDMjULYFtyvOwnHs/DCDcPrkLUQRcMwQ/8+XA/FFE1USsXsRgytRBbTc3E6GGOsMb8Zo9sRIRl7
+fA1I/oT8LkcAjTySyCGBQxK5G2trkkAlxSPyycp+FNJK3aJEkMsIp6QSSCzF9LLCMs2cUsst03RR
+zcDc7O1M2uBksRJMOjFz8QQsLOGzTz//BDRQQQcldFAN9iw0UUUXTfRQRh+F9FFHI6W00j8ntTRT
+SjHVtFNFNeAjKzciqKBUU09FNVVVV2W1VVYfINVVWWelVVZYa8U1V1xv1bVXX1Hl9Vdhew12WGNn
+feARMJdltllnn4U2WmmnpbZaa6/FNlttt+W2W2+/BTdcccclt1xzz0U3XXXXZbddd9+FN15556W3
+XnvvxTdfffflt19//+03IAA7
diff --git a/Documentation/DocBook/media/pipeline.png.b64 b/Documentation/DocBook/media/pipeline.png.b64
new file mode 100644
index 0000000..97d9ac0
--- /dev/null
+++ b/Documentation/DocBook/media/pipeline.png.b64
@@ -0,0 +1,213 @@
+iVBORw0KGgoAAAANSUhEUgAAAlgAAAEcCAMAAAAsmToJAAAAAXNSR0IArs4c6QAAAwBQTFRFAAEA
+EAEBAwUBCgMBCAUKAgkMHQIDCwYXFQYDBgkVDwgFCAsHChASJwkDFA4NEg4TDhANHgwHDg8aExAG
+DBEjBBUZERMQCBNRDxU0ExcZFRgVFRcgCRkzHBcUDRdCNRELIxYTAx4mLBUMEBwcEhsiDxwhExsu
+Dh8XJRkQByAtDCMUHx4bACk0DSsiGScsFCRcJSUiGSZCDiwqGCg1LyQbIycpVhsPDy0wNyQVECxA
+PSIfCS84ADJCEStWRiIULSwpADdHCDkgEy1/BjorEjhADDhXCzZvITNPUysQDzs8MjIvCj04BT1O
+PDEoQjEhMDU3LzY9KTdFTTMYNzk2GD6PAEpfEUVjBExFCEpPB1ArK0FjKEVZYTscdDYXG0d5SkA5
+FEiJQUNAOURPAFhCA1RpP0ZJVEMvYUAyBFtRWkYnDlR+QEleAF1jkTojHlV1AGB4PlN5YU87cUws
+SVRXAGaBWVJKUlRRRVZlAG1PWFNSBGt5AmqVAG+NAHNpK17BWl9ifFs9KWmnSGZ0YGFfp1IuCnWj
+AHuKbWBXoVNAdGBPDniWAHujTWmLk14rAH+ch19XX2aRX2t8AIeLiWgvAIeeAIaqa21rOnehdW1m
+AImspWJYj2tLW3C0AIyvWXaNAo6xb3Z5dXZzent4WIKiQojAen+CkX1pcoSWcYDOoH9fp4E+Y4ur
+hoWCXIvGb4mut3xpqYNYnYdUO5rDyXxdjY6LYJqk0IRGb5azXZnMjJGUgJWqpJB8l5aUyJN4kKK1
+rZ6IYqzge6Xho6GdnaWpgqy6yZ9zvaR0hqzMk6rN5Jxxw6mF26lbq7C2pLTGwrCbs7Owvb+8zcGc
+8rp42L2xsMbY3L+jtMfsz8W2nM/yx8jH0MfA7MWU78h/3crIyNTo4NLD6dK1z9bc1dbT3NXOv9vr
+/OKSwur8++Gw4uTh0ef78uPU+OPL3Ojz++bGyfH36evo/+3b6vT88vPw/feu1/v+//bb//nK/PnW
+5f/+//ro+fv49/7///37//71//3//v/8sZeTnQAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAFY4AABWO
+AUTUBDsAAAAHdElNRQfaCRQPAiJBEFMLAAAgAElEQVR42u2dD3wU5bX300nWws50shA4lyTGikRE
+Qy0aUChKvJZqTQGBFNurVo0FWq2VqhAq0hc0t1rAxtLLpu61cUl6gWtNe3vb7Z9IKm+Xqn2tkFih
+GmwFA9Xwpw1s/UOyCc97zvPM7L/sbjabXTLZPL/PhzA7O7M7s893zjnPmec5k8WkpNKgLPkTSEmw
+pCRYVlSjp5n/3+3xGGsONHt2vx1t04PNTbtP8aUujxAuHvN6XjHe721pbmqLsp/H021+crP5wce8
+Ta8Evi+wKMHKHKkwjf/vA+D/77SrAKDeeTRiO3/vcnzDoTzXgy/+CEKMbVdw5dh9tMGubAfuuMjf
+E7HnfgAf/X9yJn6Aupqv206fNfYdXHr/Aloc944EK7PBWg9QurVxlQ3s70Vs+FmAdQ0LAP6By1cA
+KCT2AsBqXKme7mEfajBr25MaTIjkSjPA0mHsVnz/G8jgzyBna4MGH/UzdgHYt9ZrMNovwcpksN4E
+eJpeHbLB5PDtelT4L/wP4Fr+95NirQa34d85cB1jX3eAsGVhiPRuR644WPjOafxgHXw9uD8axL/k
+3+gj3P4vLgJ0SrAyGaxzoFCsfpObJh8aJVMtzUSMA3JZTw/Ab8RKYcD+Rvu2ehsJJIBu9llFwdW3
+0l8N1B0CrDlgpz1K4OfIkRqgT4cOjNkkWBkLFiM4ejT4nXjVrcH/wb9G4BUUwOcZ+xCgtkh74Ci9
+9Iu1Jig/BjhDhmzCGUTzccaKNx9lAqwSUAVY17Kvw7U7S2wLn6fXRXCzn90P2dIVZhxYIlxSiCE0
+W+8F1l+GYDU0hG99P9/gVxiEKxiGvYPMoCMjT2a8fxyRQkQw9HpJg3EBGAmsr4PDz8Eaxz4F+UBf
++DjtMZ0WJ/9ZBu8ZDlZwfX7fjTG0x9ibPTF93D52UEdELgD9LXYw19zteA7onMwLAHuI74SB9QGG
+b372DKAvLQF4xN/7FPlR/17gjD3H/BKszHWFYRbr6siEA7sL4Jbga4zG2Yd2sCuqaoC1H63YS3wJ
+nSW3RyFgsQ0coQLsNn4KdD/3gtcauG0HOCItVgYH78EYq4fHWOFaDLA6tMNHe3St0m0L3+Ixln+/
+BqP3ib0xGue2LRQstmu+vXjz13mMZYRbubhoFw7yXyRYmdwrHMN7hR/fgVGSdrovVzvEUpuXYPhn
+wG9+wJcOaTDZ3GcOpU/fiACLmb3CP5o0ncemCcamQY4EK+PzWD3zYayNYvcwYUj0UiCldTf+9yXQ
+2RNlKl+6Aw2YLRCtU/D+uBZgxQDr9iIkrauIeo067OOu8Cfs38Fxgi9+XoKVyWCx+wBKG5t1tDfU
+9ME81hlybtki3U59w9XNuOVL/r+IJbu/58yXwOgGdPd0aXAuwXU384eAdT7YtzXqPPO+HuybPRdA
+9hnW5YDR2zz41ntnJFiZDBbbbnfQvUI7OBaF5bF+ZdwehALCj+4n2n9qLDkK/8zxMdSNjlA/3UP4
+vRFqsf4+xhG4V8h3G0sB+/4ptJjzB5luyDDV1otUVXd9vbGmpXFr09td96mXha5kT9Yb4psfbKxv
+EgMdDjZuE2MTzPfru/9eX/8HsabWeMcY3eBtaHrL+LiDjVt3B75v224/k2CNFL3+D/kbSLCkJFhS
+EiwpKQmWlARLSoIlJSXBkpJgSUmwpKQkWFISLCkJlpSUBEtKgiUlwZKSkmBJSbCkJFhSUhIsKQmW
+leRP28f50/H5EizLSlHEDC9jCuGxJ6crtoU7TkVjxJxkuGu+XVvNp9t0bS9SFu4Ra1+/PTt/c+R+
+uFLbfMJYtNvMbRk7nq34JFiZrPB5hYdsoKqqA9Sjfbm61ZhkOAdwG1D3IVd2WhJFGp4CB+6Xw8Iq
+kD6j0QbZR3D3F/he8CPjHT1s3r0EK8PB6rHBLARmby7kRm538i4QYP2Jag4dmwkOKkU69gjbqcEb
+rOddgD1sL8DDofu8D/C4/9gCyPHTrNVZp3oXixpZjCCVYI0gsHy8Tihjf1Rt+Le7pSWw2U4bjBZg
+faUEkRJ1QETZte8ShefDT3CxtukV1tXSQm6yBXf+d7Nc0T9o2/d4SUjCyf8iTJZgjTCwng2+FVKG
+janqMn9o3UgDLOKmy1g83NrKQ6zzwc493+/YFFFKpoAqIuk03/5dmoCP1g/00xKsjAerpIWLYzMK
+oDTw9ACfqgY2w7A8rCDpHEETzZb+kPgCeIpqAz7HOD0PI2yXMfYpUcS2gGosLwf7bq8G1zNuwn7D
+JFgZD1ZAFBWdTxVBcjZHezJFKFhPAfyQwLqDu0LOGCysmi8qHS0G+Dg6wR4qJHKCVy4ajWtn0lec
+SzvfDzczCVbmg1VQxWVgs2E6oaY/HgcsP3FVKvB6pGUDCLCu53aMB+eE0E9E3y9ntxet4Gh6a/QD
+q3Qi62UYyyRYIyzdwPt/O2/Xg+X4+oJF1bNhEV/k1YgWCVdIXvGfIupCS6Xz9/8+xQGQOwddoXh8
+ABqv/8JNf9/a2goQ0jWQYGU2WH7W0cbJ6dL6lLYNsVi3AnzNWHfwyXV7WDCOZyI4p7ptRvW/XdVb
+T8/B4P1TZtnRccFCWqoEa6RYrCvUfHPt5JhgfTeQ5uxobz1DVd7HElH0rIq/cb5wAx3yCLTDbf4z
+It1gxPElMAE7mCQqFS/BGilgIRiXdeDyTkoWsO7W1ihgvYkheCuJPz7nBDumUTbrV5D3NvOdT2VI
+j2O8hRudxz/8YT97EXL9fnSFz/nZq4FnpbA+z7yQYGVyjLXcAXb+MIFFEXmsIFi5gU6kv0tFs6OK
+eGsBf1iF9g9yhLnMz0vh+u/npd3VN/j7/EEBpWcCYMngPaOlqCWhSatd84tUteDGHZF5LAKLv+xR
+TeGLQ/N1dcZm8faGEjV/4VF0hCoHaaaqnOIrtYXivuP2MlBnBOvEq6oESyq+QobC9ER7vydyq5Eq
+CZaUBEtKgiUlwZJKnyqmlXRLsKRSrhJlBI5KlmClX+USLKl0qFKCJZUO1UqwpNKhZkVtl2BJpVzt
+qtIswZJKvRSlUoIllQ6wFAmWVOqlKkq3BEsq5apWyzokWFIpV4eiVEiwpNISZHVLsKRSrlpFaZBg
+SaVeoCidEiyplKthZGYcJFhpV4FSPgIHZUmw0q8KtFkdEiypVKsbwVIafBIsqVSrhNCq7ZBgSaU8
+gie2ytq6JVhSqVWVSlZLbeiUYEmlVp4ChexWebsESyq16qzgZmtahwRLKg1mSylvlGBJpVotQLGW
+T4IllXKrpYyAjKkEaygEmU+WBGtINE3J9AmHEqwhIwskWFKpV4mi1EuwpFIvJbMHAEqwhkrNme0M
++4Dl6+w4++rs7EnT+Q3R6SRysxm7hu3D5HQGC1Z3x7Kc8RNnX2XIds1VadXU8ebS7Ik2W+pHlfja
+Z+RMnBg4CVt6z+YqW+CHmzrRNr6x39PpUJQBPQjF10anY37HNek+nYkXm42Dp6N5O5MHy9eQs8Lt
+CqpufOirNGjtNaGv3POyWlOJVWeVbW3YCUysS+/pTHWGnc5VOYf7OcKCgSSzOm8cXxN6Au7x6T0b
+15IVoSy4LtYOJwlWs80Z/slnGSxaYUuZ1equ7XP0Zxcs1NJ+TqdNUaoSPZ2qyKM/u2DxCz/flwRY
+3fkrIz/57IPlqrumKjVcdUReJUMBlqtuam3co0y4sENn39M562DRV3oHDFZ7Tl+IhgAsl6smPxWD
+LFuiQXT2wXK5NtrinU5VGbQlcjreiX0/eQjAQhtcNUCwWi+OwtCQgOVy2gbPlWdetK8bCrBc7nh3
+broVpTyB09m6xGURsFxrywcE1oGov/nQgOVyOgadIVrisgxYSFb3IH1h41KXZcBybawcAFi+0VEJ
+GiKwXM6iwXHVNs9lIbCQrNiHWpFA9r09+q80RGC5VtYmDlZBdICGCizX2nWDSjPE+sWHCCyXszrm
+sbYqiqe/89FclgLLNa8lUbDqV7qsBZZr9mACeJvbYmC55nbEuWFY0s/pFNVYDCy3LUGwfBe7rAaW
+syR5rg6sdFkNLFdsZ+io6KesQ3uM5h06sFw1tYmB9aDTcmC5ViQ9Rao7x2U9sNYeiHW40/ob76e7
+LAeW62JfQmDFMlhDCZZzWrJgHV5pQbBim6zGfu7qdC+1IFg1iUwyyvKstCBYrquSBSvfZUWwlsS6
+xtv7id5vrLEgWHUTEwGr3G1FsNY2JTmgYbwlwaqrjToEprMdu4UFFQWxx89oLguC5Zp3IAGwprqs
+CFZNeXJgdaywJFiurOiDkwOKZdGWWBKstZ4EwFpiSbBcSSZJm93WBCvaLcP2ELBi5bnWWhIs94PD
+F6z85MCqqrMmWBdGM0mOAFexplU0bLQmWAn4k6yl1gRrYnJgLbMoWHN90buEhmL1DGstCtalIw6s
+iuEEFus3xFpnUbAulGBZGqwyg6uY4zkkWBKsZMDqNMDySLAkWKkEixcHiTdxVYIlwUoKrGYBVrcE
+S4KVUrBE+B77xqgES4KVHFj00ArFK8GSYKUYLF/cEEuCJcFKEiwevndLsCRYqQbLqyhx5r1IsCRY
+SYKF4XurBEuClXqwKuONTZZgSbCSBYvFe/yqBEuClTRYtRIsCVY6wGISLAmWBEuCJcGSYEmwMhGs
+JRIslFM3phzrOsQ54keHCViz9VFiI11fGe901kiLlW6wwOBJh/zYW10EwwUs0A2wIA5Yn4AVEixL
+gAUZBRa+KcE662A5UeZ7xrITN3I6hydYEafD/yewnBKsswqW8xOA0jfRms/RItxM9oo0HMF6lB95
+qVO4c+PM5vKVKyRYaQeLWyYOVo0uIIK7Xa5vGosPDTew6GwEWF8Vp6MjTuKK4WRJsM4SWKYQrEkA
+ZKwuIYwESxcu2TS8YqyAVrrcAJNx3RaACUZc9f1S8oEyxjrbYNUA3GEE658nyAqXOodd8B4C1hf0
+vG/Tyq/i/25yiStl8D40MVbIi0K60Emzhm+vcK7xoo5e3CA84b0SrKEBy2m8GId/vxgIroY3WFt4
+wPXo5fxs7pVgnX2wTFdYQ66Qa+MlwxgsdIWbTFco3l5TALl11gJr49xJ+Zfem+nphgIRvF9EXUWA
+q3HxWwZYzmEIVmjwjl1BKrl1OYyzFliXGB3Xh1IOVs0NULppoGD54oK1ZlL+TcmBZYby+sO8a8g1
+ziUyWYmCNQlqBgpWd1ywHr08/xpnUnksI91AOQZ3gVii1RTI5z2UIFg3wNI0gvUFgM9sqrkHD6wu
+xWBR5kindhwQWNOUKl9MsP7VyB0klXkX6Z6QBOksc22CYH0ZBg4WKNXdMcH6ajBlO/AEKcdpFh3f
+lot0I8Ry8UB+ZWJg3aOnE6y6POCPqXlUh9tSDNZFUOz8NBS6BwoWKoytIFiI6t14oNclBlYqFAoW
+IZgEWKgwtoJgoXn5TE0BfDIxsFKhULC+gDSmEyxdXLimq9ENr1in67xTXhxYC6V1Bueom/iuOl5y
+t8UC69FR8DDyqn87CbDC2AqC9W90OP8Gk4YELPScyYIVxlYQrG9SBP5lXXcOAVho9wrTChZ6F714
+ZRhXSNbdBJzQVdyaBVzHp43VpS6xSaQDDYK1ln6xLaH2ZUBgBdkKgnURXEgxd27dkIB1t2sQYAXZ
+CoJ1Azm37+uwaUjA+ow7ra6Qmp74mOcMmq8vQK4bF9H5110CY92uFaDjXl+dh32PFWi9hdm6jbbW
+H6qJ6QrX8r1CDeLEhgQU0hIGW0GwdAKrJhTmszuCNBys2fUJnI4adjrEVhCsKwks/BU3DokrdKUZ
+LIxkddMerRFm+fuj4A4836uIIKIDadJu4ZcVgja5Tti5c4m90jgxlgnW7CAFSjKq6gMWWAOsS5M6
+neogWJcSWO7MBQvbiiei5xFBhm5Caua6XIaxKuC92eJNLmMtIiiM2uwEwJo1OLCmtVvVYiUFVmXn
+yLFYwiNehE5vBeUHuOaGgeW6h/dt9W8PEKw+MVZ3vwqNsYgq68RYfcCa60vgdCCcKtbHFQ5RjJV2
+sJaCkWnCBfeaYA8lHCye7dRhXogrnFwXoCxGr1AfVK/QoCpar7DYZRGwBhS8V3b27RXqQ9Yr7B+s
+jsGB9X262y9uDlxWh/ZlFrbUo6VLnSFg1WFHG/faMgrj9jV83GUgeI8Hlisv+TxWkKrwPBbEymPp
+ISOv+tOWgn43ASMZmTKwKjti5rH08/rJY9WJzO7Fse8uzU00uz0QsMpaB515N8YifttMUgHkhoIV
+SEKQC7oE9JB0Q1ywRJbiNtfAwZrW3h098/7pWJl33XTi/YPljH/bmSs/pWBVdsTIvFOO0ribHBus
+uov4uYWl6Psm5VMPVr3SOLgYy8CmeFPwRbEz3BWKtYWbAglS/SZX/2Ald69wWjhVCd4r1EOzpmkD
+S9cHDJajsiPuvUK4qr97hV8QRIVb6tSApccDq7Vv+biBBu/OkPkewQkf5n9ha8OWnVHmtwx6dEN3
+UqMbQsCqyZ90zyTRFf3ipPzZnGy0clvmTip9iC+izSO79uiVBcaYjnxw3TPpwltMdC9cEgusCAd0
+dkY3mPcIL9dzuSOnkxK3mddcWZA/9aEgWFvmFly4xEDm0UmznP3dhA7/7gv7lo9THN0jfTxWKFim
+uzScfDF/W7y42ozGzFvSfMyMGZ/R3gVmZGAZsG7g8z8CSUdxeDRmRozug3NNsIzzXcvBmgR5mwYJ
+VjkFh+0SrFCw9E/y4VaFaylDV8xpKtxIMR+a2Ro+k/DLGDg715iDSOGmGgzfruW4Xc9XWwesOqLl
+QmO83Fd1KN1Y8zli7Vu6fq9z4yQaJcPBwrdmbdp4ER9IQxHZeXWDBKuWdzuaRzpYgU4hgkPjBWqM
+7u73aaSMDvS83G9yjJwGTNfxZbrhLpb58CyA8wSb1gHLmEEIGh6p8xJ+fM4r521y1XzxXpcxNpnA
+cn6Cj17EUP9eAuu8/mOs/sBqNe58jHSwzE5hjRhz5TSGXrnoRxYjsraEgeUU/bxP0p9NZorB2MlS
+YGEwdZHRwXZHDrZaczkYYLmN+YRz4bw6BOvewYNlPiGvRLpC0xWGdf4IKmMudBhYhgpNyAywnBYE
+ixz4F3XI+3ZY3Qa6HUJpCAOsQCpvVF3MAcoDA8t8Ql7gaQgSrOgWKxwspyFzPoVVLdaauWvNVPZa
+d8g0588BxlhiXrRpscwzShFYtYG7Bi0SrABYGGNNDomx+oJ1XUjeMwSsqy0GlrsAxvKFbyFYGGNN
+EIHUBMMrfisQY10iDt1IfaYCrNbgXc5aCZYJFvX7Jm+iXmGhKxKsux+l0fPXO7d8EfKvDgPrX83O
+oqXSDYUr+QCUUS7R9auhwArByuWnhwwFe4Wue4KT7gcNFgu5f14uwTLBMge7FrrCweKBvlGhxZyo
+E7RSYvVtVoqxPgGBCTmBPNb15tLkAozTRR7LeCt3U8rAKguC1T5SwQreJKwxK0TiVT5J55lpfJvW
+GbUjt1yuz+aZd9BmG+8Gt3GtmaSXbnLpVgret+B55M9+SBzKlhsgV9xOoCNd6/qEPoGqSNaJt/RL
+HxKZ95SAVRvuCeVM6NRpZBe3NYMsn5xiL8FKJVgs1A9KsCRYKQMLg6xpXkWpl2BJsFIKVi09H09R
+CiRYEqyUgtVBw2YqFaVj2IJVKcFyWfXJFM2K4hmuYHWoEizLguUz0qPDEawyRYJlWbBoGkL38ASr
+Q1HaJFiWBavBvAs97MAqD/ZoJVjWA6tNUaqHJVgdNONLgmXdx8opijoswaJR+yDBsi5YZsJhmIHV
+wW8bSLCsC5bHmFMxzMCqCBv+KsGyHljtxpSK4QWWMFhh04wkWNYCywyyhhdYFUbZDAmWdcEygqxh
+BVZnn0lGEizLgYVBlne4gWUYLEWVYFkXrHaRyRpOYHUGRlVLsKwLFgZZZRYGa3x0722oNdZJLbMo
+WLMzC6wZ/VQtI7CWWBOs/HgGyxyw31cPuq0J1vjkwGq0KFjl8Q66mo9PzppnTbCmxzNYsW/qtFnU
+YuUkN26xc601waqPd9Beng/Kml1nRbBqlvWtsuapClTYj3lTp3OpNcGakRxY7BpLgrXCy/qN3rNi
+O4+hBGtljKExtXjQVY7Y0Xu3zZpgtSQJlm5JsK7qZPGj9woEy7vUimCNj8GN6BF2x45YKtxWBGui
+L0mwqjZaECx3fvyDBsoHZbHxddYDa2NF9ENuDYx8jTkOcK4FwaorYMlqqgXBWtEc/5gr6erPYo0r
+rAfW1JhGVunv2tfc1gNrbmfSYE2rsRxYbls/x1xL3cIsxmLxM3RgbVwW02CV9NuRutJyYLnLWfKa
+aDmwlnr7OeRmuqmDYLXMsxpYWuwIq/9rv95pNbBs3YMAq3ajxcByl/V3yHj9NxBYrHqltcC6qj2G
+7y5XKhJoCsVtLbDmtrPBSK+xFFju/i8Tnm/I4jOqaqwE1opt0Y+3Je6g5JC5bTluK4G1opENTjZL
+gTW1td8D7qZ8AweL5TutA9bKZTGvg3gTv0J7hqPd1gFrRfUguWL+8RYC65rdifgMDIUFWKxko1XA
+WrouNleJtlFnVG84JGDNa2CDls9mFbDcF7+SyAFTIivLzMQtsQRYdeO9MQNCpXIATbHWEmC5x7ey
+FMinbbQEWHVKYuFiuaIEwGItORYAa60tRpqqakBcoZr6HvzZB2tFTgdLjWonuoccLPe8GxPs3lYq
+SndW8LJYN945pGDVrc2KYa7aoCDOUJkY7vDGqe4hBcu9IquNpUydkyJGC5xtsNxLshLu3aIZ6MwK
+Pfgm29SN7qDG/9ydVm28JuTFivHj26JfEB0l2B1UBu5TOmpzZteEfMPE9J6Ne6or5MXS8eMPs5Sq
+Y5ntGudZPJ0lK4LLdUtslw4gaVKtKB1ZEUd/eOvCS01pl6ZZ+ebC3GVN7TFSn94CGidTltxd3I7D
+62688mydTsjnL/O2d7LUq+PAA5eetdaZdKG5dOMDLe0DyvHSPZ0sZmV5y8IrpkoNC9VbGqz2anSB
+NLSvoE021fBSg6K0WROs1uoCY6xoWUOHbCgJVirUVlUgLBWG7M2SquEoC8ZYLWXGuPYSpT4t8a/U
+WVCUXuHQqkpV+FOrobHdJ5tnWIPVaSGwqvjseaVaQjXchS3pswxYbdwH1sugKgNUEXqvcIjlAap7
+JW1VRqjEOmB5MFwvl1hliGiqujXAokExHtkgmQNWpTXA6lQKlGbZHpmijsCYdwsEe9WyPTJGLYrS
+aAmwOhKcJSE1PNRIjz2xAlhV5YpXNkfmqIrK21oALF/ch5hIDTuBqN1ghS6hjLAySD5RxmjoD6Q2
+8LhXqUyQl2J3K4BVEO/hOFLDTtV8YvHQg9WtqOWyNTJIojje0IPV2W8xNanhpLYy/pSmoQfLfFyU
+VMZ4whargFUrmyPTPKEFwGqjMl1SGdQnrLYGWL7WVpltyByVG7NAs+RPIZVa/yNuo0iwpFKpShG6
+JwCWP4FPC27TM9AD6Ym+m182UUp0tn9HNFhGTftIsDyqkQZXVPEknu3zdVWbsbnvIR+7vUjVFu4j
+Jj7MUkm054YiNX/ZUbFnma6WrO6740Gb+IpD+MkzdoiVG0rU/DtPibWAn3B6xCNRFmwIUbniNfy9
+HTOWnYqGz1Pmxo9NdzhmPE1LO8t0rWQ1geXfH/JDB/puqiGyLxum68Ze2BB2beGJnqSP2jRYfcEC
+83yA+mpd04FmOQAUHo3Y8KAOqmID/Q90zhqQVNY7Exy4tUYb36/hnhpcH/ndvVOAPyH1VTtk4/tf
+o1XT+WcVcupwUaPFkR4Emw3hAA7WBgf+MPjL5L7Ul6tDYGy8gFoL4BvM/4wDf13gv/5+vuj4Qdg+
+KhhCDhabezF2K7afTbRfMmpWppkDVfoBaw7kPO9jPXtzYHLEht+FcSeYbw5chsu7wNbWimIvQu4R
+5ltM53PcBt/zs72a43fh++2fAhysro/Aom6210bv/xhmdLL9NvWHjH0JinFRgx9KsMLAegbgEew8
+Hz4f7H3syU4wwPp/GjzP2FOQd5RdADf72c+0vLcYmwn3+tkL2qi3Qvdp5VoOE/zsTQ2ew720vD/j
+Vnl7mG8B3JZkBz+kMFB8sF7TdHGBYFP/lEKzqsDQ9NunP0BZC8jhkF0mVl4BtPLvNvt77ECFfhoN
+cRasY69WVf2ZsWMPVG1lbL4KYzlYf9X0dxgxtYx15ehv0Lm+3clYNizjX75MghUG1vlGY6O9vxr/
+q68KFs6cDo5csfH2+dl+P+tVoY3RP8b//tNGdCFev2QfPFBFrbihqlLEXm/qo470sGa+F277CrsL
+rsPVv9Wy30s21RAYABUfrP+A85iJzE18ZUSS/H9hHP49Bx7Y+WATIyyqxb7/bYTk7+fA91jvR8je
+fRZGY3ygaM+3cbBahUNshQlo3EazXdVbT9DrL0EpWjObtFjhYP0JHCeE2/sOjMKlSgiOCFFK9wVf
+4m/+Tw3eRouFru9Nsli/EJgsh2sZ+wrkveV/hlsoxmF7OriXDfagg/olo0U0XkmoUVEcLDZYjuoq
+VLWDwPo4PBIgaAKdQ8SQvJOj4BbGPrBxb134FvsYt1hdWfC4scHPwP4Gt3dPvyhsXpvfQOqvoNP5
+vgpj0XgVLnAAOOgsj+fAwqrpUCxjLHBU8YYAAus7kG108Dg2vC6xqbfDOUN6sk+xF2z6sgd53PSE
+2HUVNWDXGDj3Q53bPGGZToXu9Q4bA1TGvZeM18DVGlYhry9YATWQBdpqrBc+L0IY2tuPoMO0QfHu
+XWSW/gPGYn/uZQ0M/l6wwSLGY8J8HS41D4CDhTHYN/hFo7EDALk7WpaDg8h7ir46SoQ68sAKCFur
+IkiOg8CKzB6FgLUe4EcijgcYi+A8KN6rogb0v4DRP2QbUdocvqHQUxpZL7sgKimwOsLnhvYFS21t
+QbUqkWCN7vNRJ6eA/izFbIep8uwhjNNP5kBh1e06YGBF9vUZJI5fEyfRpuW+EwYWGjO0TWXAwdJ/
+YnrB+0Hb0XKfpv9UgmU0BJbt7hUAAAvPSURBVAfr9gTB6mH3YVfQTxfs2N0tMyHnRBhYgrdfi23f
+tI06Yu613gGz/ATW7mTBosA9dJRK/BjrioAr/HEg2gro2BSw/zokjZAFm9mhMQ5wzLoCAys8WrwK
+ik+ZfUhhukLAIuwge7M2Dl2h6udeMZcds+m/YRTbTZZghcZYQVf4N97TiwFWD4ZScIuf+of2oxyj
+O4KukDfgnzQ0WGKn++BfTK7uEnsZrrCLwq2BclUWMayuv+D9XAqH9jGMnh6J5KoIcvaF5dyA0qgH
+WtrYKP13wrpeb/wax5AhnvEKBYsda209cQBmoVfkYB1AsA6I9w6AJsEKBetvGFmh/3qeneERVEyL
+tRx0Ho+vMs2UzQze74LPi3hdJKy4J/ylsTty9XjIqn/mDDh471SVaeGPkowP1n4bueHP2m/ZzoPw
+sPhqCow10J9TcgsPmvTfdXqf9NNSDv7FaP0b5hUxEwp1yPf7Q8HyNnq4Kfsa682B33CjeC6F9PQ9
+f8SQXoIVmm6YQhmdX6jFe3X4AYsJ1nrIEx2+JwR+y9FMYdeKTNwF8HsRwY4FnduDDwJ9P1z5U9OI
+kVn7Hy17gLeC2qgGY/cAwMIj1Vef6L0PIz5+U6eq2hsSIy6sRlVRj1HfwY4tQPd1yIbo49LX0Bzl
+QC69X+2hcEr/DfYMbw6zWN+F/CNsly33HT++n7+H4U/2DdaVQ51LXFwowQoD600NFu5hL4MOY6nR
+G6qrooCFG43lv3n7u7nwtVNsu07x1AIoPYHNRR7wXR1uRotAeR/yliLqfTPX2KsNwy7HDnZwSqBT
+n3DCXVGmRVSC7wcsdp8GqupA+5m/2R+ax3rfZnRZcPNetK+qCqOPUOxNS6V+sj+GqsgRLqLrwnCG
+Aiw/kqerDpVHaYtpEQr93EbS92knJFjht3RetuMPw2/E3HkkIr8QeLnA/M3bKIClrSkWOa7TIv+h
+MZo/RVc/XeL/CdmnTUcYuLuznpobxp0a0KFWRnvQUVbfJJcBliJuQr/+WJGiPdAyBfLeCM1jeRVT
+9GpXmZK/mTqxPTunKzOa6Jo6x3y/qmuMMprWzFFs7xn5DhFi3We33WkEorumK8U7uP099mQRLp4a
+6VyxacGGEMmhY9vLsm0Ln7/LQb6wPHzKXIV4+RHzN8de+sFVevaM53lO9dgG3XYnub/1ikJ5nKcU
+Bd3ig+ZHBPYib7R3fnb+5u6B3IRudSjRxgAnPGxm552Jf5c/ua3kWJlE9PpMS111vHJslEc8yIF+
+UoNQgwLoBqOVYpRgSSWtRipIDC1R35NgSSWn7lpR5zpG5VgJllQyaq0UPbOYTw+RYEkNWG314sE0
+1XHm7UmwpAYkX3MlUkUpBk/c8ukSLKmE1d5YaT7ur7q/h0hKsKT6V2erp7qiIJATr27t/0m+Eiyp
+aGrxNjd7GmurqyrLSxziOX9Clc2JPUdZgiUVTaBECFeU17Yk/nBuCZZUNBWEMFVQUe1paRvgY0kl
+WFJRXSEfFt3W3pHsY24lWFJpkQRLSoIlJcGSkmBJSUmwpCRYUhIsKak0g9Vab0zwqq3nQ057vQ1b
+m96OuuvL9XySoZ+1NG7b7RcTO7z1TebErdcbt/WtAHAwuPJg49am4LQA39Z62RgRqq8X2UmP0SYH
+Guu3RS+q8O5WsQW2RQNuwtuCWiV2U9TWG2o3WlDs5e/11jeeSAdYEfMKt9tpTqFaGuW7juXwSYb+
+Y9NVAIe2B49rfx5ubeNzvHuX22i/8FqiYmXhW+Yi2AKVMReb8+6lAnKAGEgnSs3sn0KzCtXRf4iy
+5QLRav69OpWDKj7Rw96fSa0yms95vs/GV4btEVIq0r+3iO91BFvwNWpB5fG0g/WyBouamh+zw8V9
+djw5EzhYvRdA/jbPdBj9Hk1MLW3aYHM820OgFDY9aaPqWSFaD7lbPTMh30+Tox2rmxeICfWMV0KU
+YMUF630dxm1tbiwC+9tnIjdcr4lWw20ubmoEGOdH1OybPUU0PxV/dftWXDw3bHaduAuoQd4+KpqF
+exXBuNO0iC2oOX6fbrCuAF71+GUtL6KSKNuZBwKs12xU1eGkDb7HvstrxPyWivy9puHJsf+dsSh0
+n+M2eBZJ/Bh87wz7GE2O7rIZ5R0O2SRY/YD1HbATGb1TqDJfmF6badYg/Y5qP2HU8RtFk+vf1ahg
+5Bh4iTO3L5qlQ+P0Pzaaa/+uLW8PewJoavEGXmQvrWBlGzVmmprbOOWBQpFesD+icrA6W71i82qE
+ZR1dNlRR9Me8tB93f3OU0afRrimj/X8FlVzji8TUOXyDLF40mfk+kvusBCs+WBVGISNvM9XkDJ0J
+rUDpjaLV2luahZtrYzq0MlGN9C/aqKN+ciE/YF15Ctqt9/lf0lNU25YdNveiGqQPMdbzF9uod9IM
+1q2Qu/lEyBkEAuzdq98KK0iKJucniOE6sdkP0dT98LEi28I96PePU6mAn4H+UmjZUVxRfITt0kQN
+m8Xw+AEJVhSwOoNg/QrgzmAEHlq7YeHz4aUcXgC0WAvgslOiEsh/CiKpGhYV9HuWikKKNv1wlB60
+Yi9Q1eSP8zJGH+bk/SENYBlDvHipyIPZ6KJmrDNOyNPcGuamg2D1ziS/fgUPqdArbkbzS1Xewf4c
+vy7053gditc0HlL9GHLxstgAdkXTnhMBVilrk2BFActsCsJmOjaFtszoSHubPaFbhoJ1vIjs0MkF
+oCowDsF5DLJp9SpeknEBZG/XzKJF60OK6Z2cQnvpyVf0GxhY7OAC6rw5tOei7BoEqxej8F9TKObY
+wU4uB/SK2aA/z3z3UUjPus7BjyhGJ9h7Dsw4wfbmIEPYl8TvUeFOvx+tXf5pCVa/YPVu4D05ZXWU
+EhchYCFXo45QTw93duAl7g+UiqSm/ruOH2E4wt4xwcJ9yNWofURUiwBrd7rTDeiBPbdnO0B9Lg5Y
+vpmgPm3UKcSzKcLTGWUaLyqrvV8Dx0uUJTmETClU8IhgW3SKvf4R3OzkmFw8pXYJVnxXSL+z97E8
+ozRRTLAO6ZCNsfoHOmz2U52xl9BiKUGLRUXW7EdEzvG3WraZDTpWJKp+ptVihYPFj2Hv+dH6CSZY
+vinC5VFfcboyY88V8N/oFY2C7xR1vaoB/Jp/0LFVDu2BV7QJ9PiAf/A4fhzaKsgm4wVKhWQpTh5L
+6PBiyDsRGyzkitfvNAKr5RhYhcRYIq+jG2DdBXeYNOZBDjdeIsb6IMk674mDdWDVFNE92AW2mGCh
+FdVCe7JdOXazV9ibRVVuu8ag+R0d7Ge8CnMpjqezfR00AstQpWQpNliPlf1cOLB4VZOP6/QgGhao
+wb0Kctifgr1CwxUaddXGmHYJvee4I0aamnqFf7LZ09wrxL7encYXxrZYiyHXeKbPX8vz3qFIazLF
+6WOPUoVu9Q1ebnKHqPf+lRL6Owc7kGixqMb4rTCZdXhI9eBo9kqWYoM1nUprM+pOx7FYRfBR4Sd/
+oelHeMD/eQLo14E8lgjeeWb9bwG7NF2Un2QskMc6L915rPsBCmsbn5wOKsVIEU8F52Cd2asZ4X41
+FRDN3/aY3f4Sz7xrW58cBbPQQFEW9Ble1f5F0Fc3zOePnVhMi7drjh8ZaWQZvPcD1ssOyFm3rWG5
+g9d5jKjoJ8A6Qw9fEBX9eqfg1g0XYBx/hjLv6xpE5v1n1CP8iqhua1RTZmeeMffyknOZsW2VI/2Z
+994N2fxeofZ8RB4raLG+ZDoyDKr2Z9P9Px5v9S7HqEmddYr6HsXcNo0+KlbaZ5GB66EbWJD9tPlh
+Eqz+YqxdOvUKHaJXGL0G6cxgDdJj/F5hDm+39XRfgyruoyO8mSfvP3oq8CgU8iDB24YH6bah8jRL
+PVhtnkax0OjhSavelmZPc1vYKlONnhb+1xC96PV6dpsjFl5vbqL9Xvd46Kazr8lDHY4DYiXpIG4b
+NOroECVJkRe5R/QKvUabHPB6ml45FbbKkPHSbApPB/+pPa8YHu5gcxMfvOD1eGj31zweDNNagi1t
+iM9GbfE0p2V0g5QUk2BJSbCkJFhSUoPW/wfr5tj8wgE+HwAAAABJRU5ErkJggg==
diff --git a/Documentation/DocBook/v4l/.gitignore b/Documentation/DocBook/media/v4l/.gitignore
similarity index 100%
rename from Documentation/DocBook/v4l/.gitignore
rename to Documentation/DocBook/media/v4l/.gitignore
diff --git a/Documentation/DocBook/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
similarity index 100%
rename from Documentation/DocBook/v4l/biblio.xml
rename to Documentation/DocBook/media/v4l/biblio.xml
diff --git a/Documentation/DocBook/v4l/capture.c.xml b/Documentation/DocBook/media/v4l/capture.c.xml
similarity index 100%
rename from Documentation/DocBook/v4l/capture.c.xml
rename to Documentation/DocBook/media/v4l/capture.c.xml
diff --git a/Documentation/DocBook/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml
similarity index 98%
rename from Documentation/DocBook/v4l/common.xml
rename to Documentation/DocBook/media/v4l/common.xml
index 9028721..a86f7a0 100644
--- a/Documentation/DocBook/v4l/common.xml
+++ b/Documentation/DocBook/media/v4l/common.xml
@@ -236,7 +236,15 @@
     <para>The &VIDIOC-QUERYCAP; ioctl is available to check if the kernel
 device is compatible with this specification, and to query the <link
 linkend="devices">functions</link> and <link linkend="io">I/O
-methods</link> supported by the device. Other features can be queried
+methods</link> supported by the device.</para>
+
+    <para>Starting with kernel version 3.1, VIDIOC-QUERYCAP will return the
+V4L2 API version used by the driver, with generally matches the Kernel version.
+There's no need of using &VIDIOC-QUERYCAP; to check if an specific ioctl is
+supported, the V4L2 core now returns ENOIOCTLCMD if a driver doesn't provide
+support for an ioctl.</para>
+
+    <para>Other features can be queried
 by calling the respective ioctl, for example &VIDIOC-ENUMINPUT;
 to learn about the number, types and names of video connectors on the
 device. Although abstraction is a major objective of this API, the
diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
similarity index 98%
rename from Documentation/DocBook/v4l/compat.xml
rename to Documentation/DocBook/media/v4l/compat.xml
index 9f7cd4f..ce1004a 100644
--- a/Documentation/DocBook/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -10,12 +10,10 @@
     <para>The Video For Linux API was first introduced in Linux 2.1 to
 unify and replace various TV and radio device related interfaces,
 developed independently by driver writers in prior years. Starting
-with Linux 2.5 the much improved V4L2 API replaces the V4L API,
-although existing drivers will continue to support V4L applications in
-the future, either directly or through the V4L2 compatibility layer in
-the <filename>videodev</filename> kernel module translating ioctls on
-the fly. For a transition period not all drivers will support the V4L2
-API.</para>
+with Linux 2.5 the much improved V4L2 API replaces the V4L API.
+The support for the old V4L calls were removed from Kernel, but the
+library <xref linkend="libv4l" /> supports the conversion of a V4L
+API system call into a V4L2 one.</para>
 
     <section>
       <title>Opening and Closing Devices</title>
@@ -84,12 +82,7 @@
 device file. V4L2 drivers <emphasis>may</emphasis> support multiple
 opens, see <xref linkend="open" /> for details and consequences.</para>
 
-      <para>V4L drivers respond to V4L2 ioctls with an &EINVAL;. The
-compatibility layer in the V4L2 <filename>videodev</filename> module
-can translate V4L ioctl requests to their V4L2 counterpart, however a
-V4L2 driver usually needs more preparation to become fully V4L
-compatible. This is covered in more detail in <xref
-	  linkend="driver" />.</para>
+      <para>V4L drivers respond to V4L2 ioctls with an &EINVAL;.</para>
     </section>
 
     <section>
@@ -2367,6 +2360,16 @@
         </listitem>
       </orderedlist>
     </section>
+    <section>
+      <title>V4L2 in Linux 3.1</title>
+      <orderedlist>
+        <listitem>
+	  <para>VIDIOC_QUERYCAP now returns a per-subsystem version instead of a per-driver one.</para>
+	  <para>Standardize an error code for invalid ioctl.</para>
+          <para>Added V4L2_CTRL_TYPE_BITMASK.</para>
+        </listitem>
+      </orderedlist>
+    </section>
 
     <section id="other">
       <title>Relation of V4L2 to other Linux multimedia APIs</title>
@@ -2472,6 +2475,9 @@
         <listitem>
 	  <para>&VIDIOC-DBG-G-CHIP-IDENT; ioctl.</para>
         </listitem>
+        <listitem>
+	  <para>Flash API. <xref linkend="flash-controls" /></para>
+        </listitem>
       </itemizedlist>
     </section>
 
diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
similarity index 62%
rename from Documentation/DocBook/v4l/controls.xml
rename to Documentation/DocBook/media/v4l/controls.xml
index a920ee8..8516401 100644
--- a/Documentation/DocBook/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -51,6 +51,10 @@
 called.</para>
       </footnote></para>
 
+    <para>
+      All controls use machine endianness.
+    </para>
+
     <table pgwide="1" frame="none" id="control-id">
       <title>Control IDs</title>
       <tgroup cols="3">
@@ -325,6 +329,22 @@
 <constant>V4L2_CID_ILLUMINATORS_2</constant> + 1).</entry>
 	  </row>
 	  <row>
+	    <entry><constant>V4L2_CID_MIN_BUFFERS_FOR_CAPTURE</constant></entry>
+	    <entry>integer</entry>
+	    <entry>This is a read-only control that can be read by the application
+and used as a hint to determine the number of CAPTURE buffers to pass to REQBUFS.
+The value is the minimum number of CAPTURE buffers that is necessary for hardware
+to work.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_CID_MIN_BUFFERS_FOR_OUTPUT</constant></entry>
+	    <entry>integer</entry>
+	    <entry>This is a read-only control that can be read by the application
+and used as a hint to determine the number of OUTPUT buffers to pass to REQBUFS.
+The value is the minimum number of OUTPUT buffers that is necessary for hardware
+to work.</entry>
+	  </row>
+	  <row>
 	    <entry><constant>V4L2_CID_PRIVATE_BASE</constant></entry>
 	    <entry></entry>
 	    <entry>ID of the first custom (driver specific) control.
@@ -545,6 +565,10 @@
 return an error). A good example is the list of supported MPEG audio
 bitrates. Some drivers only support one or two bitrates, others
 support a wider range.</para>
+
+      <para>
+	All controls use machine endianness.
+      </para>
     </section>
 
     <section>
@@ -670,7 +694,8 @@
 	      </row><row><entry spanname="descr">The MPEG-1, -2 or -4
 output stream type. One cannot assume anything here. Each hardware
 MPEG encoder tends to support different subsets of the available MPEG
-stream types. The currently defined stream types are:</entry>
+stream types. This control is specific to multiplexed MPEG streams.
+The currently defined stream types are:</entry>
 	      </row>
 	      <row>
 		<entrytbl spanname="descr" cols="2">
@@ -800,6 +825,7 @@
 		<entry spanname="id"><constant>V4L2_CID_MPEG_AUDIO_ENCODING</constant>&nbsp;</entry>
 		<entry>enum&nbsp;v4l2_mpeg_audio_encoding</entry>
 	      </row><row><entry spanname="descr">MPEG Audio encoding.
+This control is specific to multiplexed MPEG streams.
 Possible values are:</entry>
 	      </row>
 	      <row>
@@ -1250,7 +1276,8 @@
 		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_ENCODING</constant>&nbsp;</entry>
 		<entry>enum&nbsp;v4l2_mpeg_video_encoding</entry>
 	      </row><row><entry spanname="descr">MPEG Video encoding
-method. Possible values are:</entry>
+method. This control is specific to multiplexed MPEG streams.
+Possible values are:</entry>
 	      </row>
 	      <row>
 		<entrytbl spanname="descr" cols="2">
@@ -1406,6 +1433,959 @@
 		  </tbody>
 		</entrytbl>
 	      </row>
+
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">If enabled the decoder expects to receive a single slice per buffer, otherwise
+the decoder expects a single frame in per buffer. Applicable to the decoder, all codecs.
+</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">Enable writing sample aspect ratio in the Video Usability Information.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_video_h264_vui_sar_idc</entry>
+	      </row>
+	      <row><entry spanname="descr">VUI sample aspect ratio indicator for H.264 encoding. The value
+is defined in the table E-1 in the standard. Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED</constant>&nbsp;</entry>
+			  <entry>Unspecified</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1</constant>&nbsp;</entry>
+			  <entry>1x1</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11</constant>&nbsp;</entry>
+			  <entry>12x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11</constant>&nbsp;</entry>
+			  <entry>10x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11</constant>&nbsp;</entry>
+			  <entry>16x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33</constant>&nbsp;</entry>
+			  <entry>40x33</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11</constant>&nbsp;</entry>
+			  <entry>24x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11</constant>&nbsp;</entry>
+			  <entry>20x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11</constant>&nbsp;</entry>
+			  <entry>32x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33</constant>&nbsp;</entry>
+			  <entry>80x33</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11</constant>&nbsp;</entry>
+			  <entry>18x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11</constant>&nbsp;</entry>
+			  <entry>15x11</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33</constant>&nbsp;</entry>
+			  <entry>64x33</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99</constant>&nbsp;</entry>
+			  <entry>160x99</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3</constant>&nbsp;</entry>
+			  <entry>4x3</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2</constant>&nbsp;</entry>
+			  <entry>3x2</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1</constant>&nbsp;</entry>
+			  <entry>2x1</entry>
+			</row>
+			<row>
+			  <entry><constant>V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED</constant>&nbsp;</entry>
+			  <entry>Extended SAR</entry>
+			</row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Extended sample aspect ratio width for H.264 VUI encoding.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Extended sample aspect ratio height for H.264 VUI encoding.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_LEVEL</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_video_h264_level</entry>
+	      </row>
+	      <row><entry spanname="descr">The level information for the H264 video elementary stream.
+Applicable to the H264 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_1_0</constant>&nbsp;</entry>
+		      <entry>Level 1.0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_1B</constant>&nbsp;</entry>
+		      <entry>Level 1B</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_1_1</constant>&nbsp;</entry>
+		      <entry>Level 1.1</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_1_2</constant>&nbsp;</entry>
+		      <entry>Level 1.2</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_1_3</constant>&nbsp;</entry>
+		      <entry>Level 1.3</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_2_0</constant>&nbsp;</entry>
+		      <entry>Level 2.0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_2_1</constant>&nbsp;</entry>
+		      <entry>Level 2.1</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_2_2</constant>&nbsp;</entry>
+		      <entry>Level 2.2</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_3_0</constant>&nbsp;</entry>
+		      <entry>Level 3.0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_3_1</constant>&nbsp;</entry>
+		      <entry>Level 3.1</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_3_2</constant>&nbsp;</entry>
+		      <entry>Level 3.2</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_4_0</constant>&nbsp;</entry>
+		      <entry>Level 4.0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_4_1</constant>&nbsp;</entry>
+		      <entry>Level 4.1</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_4_2</constant>&nbsp;</entry>
+		      <entry>Level 4.2</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_5_0</constant>&nbsp;</entry>
+		      <entry>Level 5.0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LEVEL_5_1</constant>&nbsp;</entry>
+		      <entry>Level 5.1</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_video_mpeg4_level</entry>
+	      </row>
+	      <row><entry spanname="descr">The level information for the MPEG4 elementary stream.
+Applicable to the MPEG4 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_0</constant>&nbsp;</entry>
+		      <entry>Level 0</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_0B</constant>&nbsp;</entry>
+		      <entry>Level 0b</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_1</constant>&nbsp;</entry>
+		      <entry>Level 1</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_2</constant>&nbsp;</entry>
+		      <entry>Level 2</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_3</constant>&nbsp;</entry>
+		      <entry>Level 3</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_3B</constant>&nbsp;</entry>
+		      <entry>Level 3b</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_4</constant>&nbsp;</entry>
+		      <entry>Level 4</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_LEVEL_5</constant>&nbsp;</entry>
+		      <entry>Level 5</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_PROFILE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_h264_profile</entry>
+	      </row>
+	      <row><entry spanname="descr">The profile information for H264.
+Applicable to the H264 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE</constant>&nbsp;</entry>
+		      <entry>Baseline profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE</constant>&nbsp;</entry>
+		      <entry>Constrained Baseline profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_MAIN</constant>&nbsp;</entry>
+		      <entry>Main profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED</constant>&nbsp;</entry>
+		      <entry>Extended profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH</constant>&nbsp;</entry>
+		      <entry>High profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10</constant>&nbsp;</entry>
+		      <entry>High 10 profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422</constant>&nbsp;</entry>
+		      <entry>High 422 profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE</constant>&nbsp;</entry>
+		      <entry>High 444 Predictive profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA</constant>&nbsp;</entry>
+		      <entry>High 10 Intra profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA</constant>&nbsp;</entry>
+		      <entry>High 422 Intra profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA</constant>&nbsp;</entry>
+		      <entry>High 444 Intra profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA</constant>&nbsp;</entry>
+		      <entry>CAVLC 444 Intra profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE</constant>&nbsp;</entry>
+		      <entry>Scalable Baseline profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH</constant>&nbsp;</entry>
+		      <entry>Scalable High profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA</constant>&nbsp;</entry>
+		      <entry>Scalable High Intra profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH</constant>&nbsp;</entry>
+		      <entry>Stereo High profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH</constant>&nbsp;</entry>
+		      <entry>Multiview High profile</entry>
+		    </row>
+
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_mpeg4_profile</entry>
+	      </row>
+	      <row><entry spanname="descr">The profile information for MPEG4.
+Applicable to the MPEG4 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_PROFILE_SIMPLE</constant>&nbsp;</entry>
+		      <entry>Simple profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_PROFILE_ADVANCED_SIMPLE</constant>&nbsp;</entry>
+		      <entry>Advanced Simple profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_PROFILE_CORE</constant>&nbsp;</entry>
+		      <entry>Core profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_PROFILE_SIMPLE_SCALABLE</constant>&nbsp;</entry>
+		      <entry>Simple Scalable profile</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_PROFILE_ADVANCED_CODING_EFFICIENCY</constant>&nbsp;</entry>
+		      <entry></entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MAX_REF_PIC</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">The maximum number of reference pictures used for encoding.
+Applicable to the encoder.
+</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_multi_slice_mode</entry>
+	      </row>
+	      <row><entry spanname="descr">Determines how the encoder should handle division of frame into slices.
+Applicable to the encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE</constant>&nbsp;</entry>
+		      <entry>Single slice per frame.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB</constant>&nbsp;</entry>
+		      <entry>Multiple slices with set maximum number of macroblocks per slice.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES</constant>&nbsp;</entry>
+		      <entry>Multiple slice with set maximum size in bytes per slice.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">The maximum number of macroblocks in a slice. Used when
+<constant>V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE</constant> is set to <constant>V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB</constant>.
+Applicable to the encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">The maximum size of a slice in bytes. Used when
+<constant>V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE</constant> is set to <constant>V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES</constant>.
+Applicable to the encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_h264_loop_filter_mode</entry>
+	      </row>
+	      <row><entry spanname="descr">Loop filter mode for H264 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED</constant>&nbsp;</entry>
+		      <entry>Loop filter is enabled.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED</constant>&nbsp;</entry>
+		      <entry>Loop filter is disabled.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY</constant>&nbsp;</entry>
+		      <entry>Loop filter is disabled at the slice boundary.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Loop filter alpha coefficient, defined in the H264 standard.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Loop filter beta coefficient, defined in the H264 standard.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_h264_symbol_mode</entry>
+	      </row>
+	      <row><entry spanname="descr">Entropy coding mode for H264 - CABAC/CAVALC.
+Applicable to the H264 encoder.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC</constant>&nbsp;</entry>
+		      <entry>Use CAVLC entropy coding.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC</constant>&nbsp;</entry>
+		      <entry>Use CABAC entropy coding.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">Enable 8X8 transform for H264. Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Cyclic intra macroblock refresh. This is the number of continuous macroblocks
+refreshed every frame. Each frame a succesive set of macroblocks is refreshed until the cycle completes and starts from the
+top of the frame. Applicable to H264, H263 and MPEG4 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">Frame level rate control enable.
+If this control is disabled then the quantization parameter for each frame type is constant and set with appropriate controls
+(e.g. <constant>V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP</constant>).
+If frame rate control is enabled then quantization parameter is adjusted to meet the chosen bitrate. Minimum and maximum value
+for the quantization parameter can be set with appropriate controls (e.g. <constant>V4L2_CID_MPEG_VIDEO_H263_MIN_QP</constant>).
+Applicable to encoders.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">Macroblock level rate control enable.
+Applicable to the MPEG4 and H264 encoders.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_QPEL</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row>
+	      <row><entry spanname="descr">Quarter pixel motion estimation for MPEG4. Applicable to the MPEG4 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an I frame for H263. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H263_MIN_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Minimum quantization parameter for H263. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H263_MAX_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Maximum quantization parameter for H263. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an P frame for H263. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an B frame for H263. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an I frame for H264. Valid range: from 0 to 51.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_MIN_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Minimum quantization parameter for H264. Valid range: from 0 to 51.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_MAX_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Maximum quantization parameter for H264. Valid range: from 0 to 51.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an P frame for H264. Valid range: from 0 to 51.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an B frame for H264. Valid range: from 0 to 51.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an I frame for MPEG4. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Minimum quantization parameter for MPEG4. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Maximum quantization parameter for MPEG4. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an P frame for MPEG4. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Quantization parameter for an B frame for MPEG4. Valid range: from 1 to 31.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VBV_SIZE</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">The Video Buffer Verifier size in kilobytes, it is used as a limitation of frame skip.
+The VBV is defined in the standard as a mean to verify that the produced stream will be succesfully decoded.
+The standard describes it as "Part of a hypothetical decoder that is conceptually connected to the
+output of the encoder. Its purpose is to provide a constraint on the variability of the data rate that an
+encoder or editing process may produce.".
+Applicable to the MPEG1, MPEG2, MPEG4 encoders.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">The Coded Picture Buffer size in kilobytes, it is used as a limitation of frame skip.
+The CPB is defined in the H264 standard as a mean to verify that the produced stream will be succesfully decoded.
+Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_I_PERIOD</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row>
+	      <row><entry spanname="descr">Period between I-frames in the open GOP for H264. In case of an open GOP
+this is the period between two I-frames. The period between IDR (Instantaneous Decoding Refresh) frames is taken from the GOP_SIZE control.
+An IDR frame, which stands for Instantaneous Decoding Refresh is an I-frame after which no prior frames are
+referenced. This means that a stream can be restarted from an IDR frame without the need to store or decode any
+previous frames. Applicable to the H264 encoder.</entry>
+	      </row>
+
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_HEADER_MODE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_header_mode</entry>
+	      </row>
+	      <row><entry spanname="descr">Determines whether the header is returned as the first buffer or is
+it returned together with the first frame. Applicable to encoders.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE</constant>&nbsp;</entry>
+		      <entry>The stream header is returned separately in the first buffer.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME</constant>&nbsp;</entry>
+		      <entry>The stream header is returned together with the first encoded frame.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Enabled the deblocking post processing filter for MPEG4 decoder.
+Applicable to the MPEG4 decoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_RES</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">vop_time_increment_resolution value for MPEG4. Applicable to the MPEG4 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_INC</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">vop_time_increment value for MPEG4. Applicable to the MPEG4 encoder.</entry>
+	      </row>
+
+	    </tbody>
+	  </tgroup>
+	</table>
+      </section>
+
+      <section>
+	<title>MFC 5.1 MPEG Controls</title>
+
+	<para>The following MPEG class controls deal with MPEG
+decoding and encoding settings that are specific to the Multi Format Codec 5.1 device present
+in the S5P family of SoCs by Samsung.
+</para>
+
+	<table pgwide="1" frame="none" id="mfc51-control-id">
+	  <title>MFC 5.1 Control IDs</title>
+	  <tgroup cols="4">
+	    <colspec colname="c1" colwidth="1*" />
+	    <colspec colname="c2" colwidth="6*" />
+	    <colspec colname="c3" colwidth="2*" />
+	    <colspec colname="c4" colwidth="6*" />
+	    <spanspec namest="c1" nameend="c2" spanname="id" />
+	    <spanspec namest="c2" nameend="c4" spanname="descr" />
+	    <thead>
+	      <row>
+		<entry spanname="id" align="left">ID</entry>
+		<entry align="left">Type</entry>
+	      </row><row><entry spanname="descr" align="left">Description</entry>
+	      </row>
+	    </thead>
+	    <tbody valign="top">
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">If the display delay is enabled then the decoder has to return a
+CAPTURE buffer after processing a certain number of OUTPUT buffers. If this number is low, then it may result in
+buffers not being dequeued in display order. In addition hardware may still use those buffers as reference, thus
+application should not write to those buffers. This feature can be used for example for generating thumbnails of videos.
+Applicable to the H264 decoder.
+	      </entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">Display delay value for H264 decoder.
+The decoder is forced to return a decoded frame after the set 'display delay' number of frames. If this number is
+low it may result in frames returned out of dispaly order, in addition the hardware may still be using the returned buffer
+as a reference picture for subsequent frames.
+</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">The number of reference pictures used for encoding a P picture.
+Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_PADDING</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Padding enable in the encoder - use a color instead of repeating border pixels.
+Applicable to encoders.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">Padding color in the encoder. Applicable to encoders. The supplied 32-bit integer is interpreted as follows (bit
+0 = least significant bit):</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry>Bit 0:7</entry>
+		      <entry>V chrominance information</entry>
+		    </row>
+		    <row>
+		      <entry>Bit 8:15</entry>
+		      <entry>U chrominance information</entry>
+		    </row>
+		    <row>
+		      <entry>Bit 16:23</entry>
+		      <entry>Y luminance information</entry>
+		    </row>
+		    <row>
+		      <entry>Bit 24:31</entry>
+		      <entry>Must be zero.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">Reaction coefficient for MFC rate control. Applicable to encoders.
+<para>Note 1: Valid only when the frame level RC is enabled.</para>
+<para>Note 2: For tight CBR, this field must be small (ex. 2 ~ 10).
+For VBR, this field must be large (ex. 100 ~ 1000).</para>
+<para>Note 3: It is not recommended to use the greater number than FRAME_RATE * (10^9 / BIT_RATE).</para>
+</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Adaptive rate control for dark region.
+Valid only when H.264 and macroblock level RC is enabled (<constant>V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE</constant>).
+Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Adaptive rate control for smooth region.
+Valid only when H.264 and macroblock level RC is enabled (<constant>V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE</constant>).
+Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Adaptive rate control for static region.
+Valid only when H.264 and macroblock level RC is enabled (<constant>V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE</constant>).
+Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY</constant>&nbsp;</entry>
+		<entry>boolean</entry>
+	      </row><row><entry spanname="descr">Adaptive rate control for activity region.
+Valid only when H.264 and macroblock level RC is enabled (<constant>V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE</constant>).
+Applicable to the H264 encoder.</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_mfc51_frame_skip_mode</entry>
+	      </row>
+	      <row><entry spanname="descr">
+Indicates in what conditions the encoder should skip frames. If encoding a frame would cause the encoded stream to be larger then
+a chosen data limit then the frame will be skipped.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED</constant>&nbsp;</entry>
+		      <entry>Frame skip mode is disabled.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT</constant>&nbsp;</entry>
+		      <entry>Frame skip mode enabled and buffer limit is set by the chosen level and is defined by the standard.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT</constant>&nbsp;</entry>
+		      <entry>Frame skip mode enabled and buffer limit is set by the VBV (MPEG1/2/4) or CPB (H264) buffer size control.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT</constant>&nbsp;</entry>
+		<entry>integer</entry>
+	      </row><row><entry spanname="descr">Enable rate-control with fixed target bit.
+If this setting is enabled, then the rate control logic of the encoder will calculate the average bitrate
+for a GOP and keep it below or equal the set bitrate target. Otherwise the rate control logic calculates the
+overall average bitrate for the stream and keeps it below or equal to the set bitrate. In the first case
+the average bitrate for the whole stream will be smaller then the set bitrate. This is caused because the
+average is calculated for smaller number of frames, on the other hand enabling this setting will ensure that
+the stream will meet tight bandwidth contraints. Applicable to encoders.
+</entry>
+	      </row>
+	      <row><entry></entry></row>
+	      <row>
+		<entry spanname="id"><constant>V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE</constant>&nbsp;</entry>
+		<entry>enum&nbsp;v4l2_mpeg_mfc51_force_frame_type</entry>
+	      </row>
+	      <row><entry spanname="descr">Force a frame type for the next queued buffer. Applicable to encoders.
+Possible values are:</entry>
+	      </row>
+	      <row>
+		<entrytbl spanname="descr" cols="2">
+		  <tbody valign="top">
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED</constant>&nbsp;</entry>
+		      <entry>Forcing a specific frame type disabled.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME</constant>&nbsp;</entry>
+		      <entry>Force an I-frame.</entry>
+		    </row>
+		    <row>
+		      <entry><constant>V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED</constant>&nbsp;</entry>
+		      <entry>Force a non-coded frame.</entry>
+		    </row>
+		  </tbody>
+		</entrytbl>
+	      </row>
 	    </tbody>
 	  </tgroup>
 	</table>
@@ -2092,6 +3072,289 @@
 <para>For more details about RDS specification, refer to
 <xref linkend="en50067" /> document, from CENELEC.</para>
     </section>
+
+    <section id="flash-controls">
+      <title>Flash Control Reference</title>
+
+      <note>
+	<title>Experimental</title>
+
+	<para>This is an <link linkend="experimental">experimental</link>
+interface and may change in the future.</para>
+      </note>
+
+      <para>
+	The V4L2 flash controls are intended to provide generic access
+	to flash controller devices. Flash controller devices are
+	typically used in digital cameras.
+      </para>
+
+      <para>
+	The interface can support both LED and xenon flash devices. As
+	of writing this, there is no xenon flash driver using this
+	interface.
+      </para>
+
+      <section id="flash-controls-use-cases">
+	<title>Supported use cases</title>
+
+	<section>
+	  <title>Unsynchronised LED flash (software strobe)</title>
+
+	  <para>
+	    Unsynchronised LED flash is controlled directly by the
+	    host as the sensor. The flash must be enabled by the host
+	    before the exposure of the image starts and disabled once
+	    it ends. The host is fully responsible for the timing of
+	    the flash.
+	  </para>
+
+	  <para>Example of such device: Nokia N900.</para>
+	</section>
+
+	<section>
+	  <title>Synchronised LED flash (hardware strobe)</title>
+
+	  <para>
+	    The synchronised LED flash is pre-programmed by the host
+	    (power and timeout) but controlled by the sensor through a
+	    strobe signal from the sensor to the flash.
+	  </para>
+
+	  <para>
+	    The sensor controls the flash duration and timing. This
+	    information typically must be made available to the
+	    sensor.
+	  </para>
+
+	</section>
+
+	<section>
+	  <title>LED flash as torch</title>
+
+	  <para>
+	    LED flash may be used as torch in conjunction with another
+	    use case involving camera or individually.
+	  </para>
+
+	</section>
+
+      </section>
+
+      <table pgwide="1" frame="none" id="flash-control-id">
+      <title>Flash Control IDs</title>
+
+      <tgroup cols="4">
+	<colspec colname="c1" colwidth="1*" />
+	<colspec colname="c2" colwidth="6*" />
+	<colspec colname="c3" colwidth="2*" />
+	<colspec colname="c4" colwidth="6*" />
+	<spanspec namest="c1" nameend="c2" spanname="id" />
+	<spanspec namest="c2" nameend="c4" spanname="descr" />
+	<thead>
+	  <row>
+	    <entry spanname="id" align="left">ID</entry>
+	    <entry align="left">Type</entry>
+	  </row><row rowsep="1"><entry spanname="descr" align="left">Description</entry>
+	  </row>
+	</thead>
+	<tbody valign="top">
+	  <row><entry></entry></row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_CLASS</constant></entry>
+	    <entry>class</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">The FLASH class descriptor.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_LED_MODE</constant></entry>
+	    <entry>menu</entry>
+	  </row>
+	  <row id="v4l2-flash-led-mode">
+	    <entry spanname="descr">Defines the mode of the flash LED,
+	    the high-power white LED attached to the flash controller.
+	    Setting this control may not be possible in presence of
+	    some faults. See V4L2_CID_FLASH_FAULT.</entry>
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_NONE</constant></entry>
+		  <entry>Off.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_FLASH</constant></entry>
+		  <entry>Flash mode.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_LED_MODE_TORCH</constant></entry>
+		  <entry>Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_SOURCE</constant></entry>
+	    <entry>menu</entry>
+	  </row>
+	  <row id="v4l2-flash-strobe-source"><entry
+	  spanname="descr">Defines the source of the flash LED
+	  strobe.</entry>
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_STROBE_SOURCE_SOFTWARE</constant></entry>
+		  <entry>The flash strobe is triggered by using
+		  the V4L2_CID_FLASH_STROBE control.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_STROBE_SOURCE_EXTERNAL</constant></entry>
+		  <entry>The flash strobe is triggered by an
+		  external source. Typically this is a sensor,
+		  which makes it possible to synchronises the
+		  flash strobe start to exposure start.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE</constant></entry>
+	    <entry>button</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Strobe flash. Valid when
+	    V4L2_CID_FLASH_LED_MODE is set to
+	    V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE
+	    is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this
+	    control may not be possible in presence of some faults.
+	    See V4L2_CID_FLASH_FAULT.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STOP</constant></entry>
+	    <entry>button</entry>
+	  </row>
+	  <row><entry spanname="descr">Stop flash strobe immediately.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STATUS</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Strobe status: whether the flash
+	    is strobing at the moment or not. This is a read-only
+	    control.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Hardware timeout for flash. The
+	    flash strobe is stopped after this period of time has
+	    passed from the start of the strobe.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Intensity of the flash strobe when
+	    the flash LED is in flash mode
+	    (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps
+	    (mA) if possible.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_TORCH_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Intensity of the flash LED in
+	    torch mode (V4L2_FLASH_LED_MODE_TORCH). The unit should be
+	    milliamps (mA) if possible. Setting this control may not
+	    be possible in presence of some faults. See
+	    V4L2_CID_FLASH_FAULT.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_INDICATOR_INTENSITY</constant></entry>
+	    <entry>integer</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Intensity of the indicator LED.
+	    The indicator LED may be fully independent of the flash
+	    LED. The unit should be microamps (uA) if possible.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_FAULT</constant></entry>
+	    <entry>bitmask</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Faults related to the flash. The
+	    faults tell about specific problems in the flash chip
+	    itself or the LEDs attached to it. Faults may prevent
+	    further use of some of the flash controls. In particular,
+	    V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE
+	    if the fault affects the flash LED. Exactly which faults
+	    have such an effect is chip dependent. Reading the faults
+	    resets the control and returns the chip to a usable state
+	    if possible.</entry>
+	  </row>
+	  <row>
+	    <entrytbl spanname="descr" cols="2">
+	      <tbody valign="top">
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_OVER_VOLTAGE</constant></entry>
+		  <entry>Flash controller voltage to the flash LED
+		  has exceeded the limit specific to the flash
+		  controller.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_TIMEOUT</constant></entry>
+		  <entry>The flash strobe was still on when
+		  the timeout set by the user ---
+		  V4L2_CID_FLASH_TIMEOUT control --- has expired.
+		  Not all flash controllers may set this in all
+		  such conditions.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_OVER_TEMPERATURE</constant></entry>
+		  <entry>The flash controller has overheated.</entry>
+		</row>
+		<row>
+		  <entry><constant>V4L2_FLASH_FAULT_SHORT_CIRCUIT</constant></entry>
+		  <entry>The short circuit protection of the flash
+		  controller has been triggered.</entry>
+		</row>
+	      </tbody>
+	    </entrytbl>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_CHARGE</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+	  <row><entry spanname="descr">Enable or disable charging of the xenon
+	  flash capacitor.</entry>
+	  </row>
+	  <row>
+	    <entry spanname="id"><constant>V4L2_CID_FLASH_READY</constant></entry>
+	    <entry>boolean</entry>
+	  </row>
+	  <row>
+	    <entry spanname="descr">Is the flash ready to strobe?
+	    Xenon flashes require their capacitors charged before
+	    strobing. LED flashes often require a cooldown period
+	    after strobe during which another strobe will not be
+	    possible. This is a read-only control.</entry>
+	  </row>
+	  <row><entry></entry></row>
+	</tbody>
+      </tgroup>
+      </table>
+
+    </section>
 </section>
 
   <!--
diff --git a/Documentation/DocBook/v4l/crop.pdf b/Documentation/DocBook/media/v4l/crop.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/crop.pdf
rename to Documentation/DocBook/media/v4l/crop.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/dev-capture.xml b/Documentation/DocBook/media/v4l/dev-capture.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-capture.xml
rename to Documentation/DocBook/media/v4l/dev-capture.xml
diff --git a/Documentation/DocBook/v4l/dev-codec.xml b/Documentation/DocBook/media/v4l/dev-codec.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-codec.xml
rename to Documentation/DocBook/media/v4l/dev-codec.xml
diff --git a/Documentation/DocBook/v4l/dev-effect.xml b/Documentation/DocBook/media/v4l/dev-effect.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-effect.xml
rename to Documentation/DocBook/media/v4l/dev-effect.xml
diff --git a/Documentation/DocBook/media/v4l/dev-event.xml b/Documentation/DocBook/media/v4l/dev-event.xml
new file mode 100644
index 0000000..f14ae3f
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/dev-event.xml
@@ -0,0 +1,51 @@
+  <title>Event Interface</title>
+
+  <para>The V4L2 event interface provides a means for a user to get
+  immediately notified on certain conditions taking place on a device.
+  This might include start of frame or loss of signal events, for
+  example. Changes in the value or state of a V4L2 control can also be
+  reported through events.
+  </para>
+
+  <para>To receive events, the events the user is interested in first must
+  be subscribed using the &VIDIOC-SUBSCRIBE-EVENT; ioctl. Once an event is
+  subscribed, the events of subscribed types are dequeueable using the
+  &VIDIOC-DQEVENT; ioctl. Events may be unsubscribed using
+  VIDIOC_UNSUBSCRIBE_EVENT ioctl. The special event type V4L2_EVENT_ALL may
+  be used to unsubscribe all the events the driver supports.</para>
+
+  <para>The event subscriptions and event queues are specific to file
+  handles. Subscribing an event on one file handle does not affect
+  other file handles.</para>
+
+  <para>The information on dequeueable events is obtained by using select or
+  poll system calls on video devices. The V4L2 events use POLLPRI events on
+  poll system call and exceptions on select system call.</para>
+
+  <para>Starting with kernel 3.1 certain guarantees can be given with
+  regards to events:<orderedlist>
+	<listitem>
+	  <para>Each subscribed event has its own internal dedicated event queue.
+This means that flooding of one event type will not interfere with other
+event types.</para>
+	</listitem>
+	<listitem>
+	  <para>If the internal event queue for a particular subscribed event
+becomes full, then the oldest event in that queue will be dropped.</para>
+	</listitem>
+	<listitem>
+	  <para>Where applicable, certain event types can ensure that the payload
+of the oldest event that is about to be dropped will be merged with the payload
+of the next oldest event. Thus ensuring that no information is lost, but only an
+intermediate step leading up to that information. See the documentation for the
+event you want to subscribe to whether this is applicable for that event or not.</para>
+	</listitem>
+      </orderedlist></para>
+
+  <!--
+Local Variables:
+mode: sgml
+sgml-parent-document: "v4l2.sgml"
+indent-tabs-mode: nil
+End:
+  -->
diff --git a/Documentation/DocBook/v4l/dev-osd.xml b/Documentation/DocBook/media/v4l/dev-osd.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-osd.xml
rename to Documentation/DocBook/media/v4l/dev-osd.xml
diff --git a/Documentation/DocBook/v4l/dev-output.xml b/Documentation/DocBook/media/v4l/dev-output.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-output.xml
rename to Documentation/DocBook/media/v4l/dev-output.xml
diff --git a/Documentation/DocBook/v4l/dev-overlay.xml b/Documentation/DocBook/media/v4l/dev-overlay.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-overlay.xml
rename to Documentation/DocBook/media/v4l/dev-overlay.xml
diff --git a/Documentation/DocBook/v4l/dev-radio.xml b/Documentation/DocBook/media/v4l/dev-radio.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-radio.xml
rename to Documentation/DocBook/media/v4l/dev-radio.xml
diff --git a/Documentation/DocBook/v4l/dev-raw-vbi.xml b/Documentation/DocBook/media/v4l/dev-raw-vbi.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-raw-vbi.xml
rename to Documentation/DocBook/media/v4l/dev-raw-vbi.xml
diff --git a/Documentation/DocBook/v4l/dev-rds.xml b/Documentation/DocBook/media/v4l/dev-rds.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-rds.xml
rename to Documentation/DocBook/media/v4l/dev-rds.xml
diff --git a/Documentation/DocBook/v4l/dev-sliced-vbi.xml b/Documentation/DocBook/media/v4l/dev-sliced-vbi.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-sliced-vbi.xml
rename to Documentation/DocBook/media/v4l/dev-sliced-vbi.xml
diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-subdev.xml
rename to Documentation/DocBook/media/v4l/dev-subdev.xml
diff --git a/Documentation/DocBook/v4l/dev-teletext.xml b/Documentation/DocBook/media/v4l/dev-teletext.xml
similarity index 100%
rename from Documentation/DocBook/v4l/dev-teletext.xml
rename to Documentation/DocBook/media/v4l/dev-teletext.xml
diff --git a/Documentation/DocBook/v4l/driver.xml b/Documentation/DocBook/media/v4l/driver.xml
similarity index 100%
rename from Documentation/DocBook/v4l/driver.xml
rename to Documentation/DocBook/media/v4l/driver.xml
diff --git a/Documentation/DocBook/v4l/fdl-appendix.xml b/Documentation/DocBook/media/v4l/fdl-appendix.xml
similarity index 100%
rename from Documentation/DocBook/v4l/fdl-appendix.xml
rename to Documentation/DocBook/media/v4l/fdl-appendix.xml
diff --git a/Documentation/DocBook/v4l/fieldseq_bt.pdf b/Documentation/DocBook/media/v4l/fieldseq_bt.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/fieldseq_bt.pdf
rename to Documentation/DocBook/media/v4l/fieldseq_bt.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/fieldseq_tb.pdf b/Documentation/DocBook/media/v4l/fieldseq_tb.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/fieldseq_tb.pdf
rename to Documentation/DocBook/media/v4l/fieldseq_tb.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/func-close.xml b/Documentation/DocBook/media/v4l/func-close.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-close.xml
rename to Documentation/DocBook/media/v4l/func-close.xml
diff --git a/Documentation/DocBook/media/v4l/func-ioctl.xml b/Documentation/DocBook/media/v4l/func-ioctl.xml
new file mode 100644
index 0000000..2de64be
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/func-ioctl.xml
@@ -0,0 +1,79 @@
+<refentry id="func-ioctl">
+  <refmeta>
+    <refentrytitle>V4L2 ioctl()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>v4l2-ioctl</refname>
+    <refpurpose>Program a V4L2 device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>void *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>V4L2 ioctl request code as defined in the <filename>videodev2.h</filename> header file, for example
+VIDIOC_QUERYCAP.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para>Pointer to a function parameter, usually a structure.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>ioctl()</function> function is used to program
+V4L2 devices. The argument <parameter>fd</parameter> must be an open
+file descriptor. An ioctl <parameter>request</parameter> has encoded
+in it whether the argument is an input, output or read/write
+parameter, and the size of the argument <parameter>argp</parameter> in
+bytes. Macros and defines specifying V4L2 ioctl requests are located
+in the <filename>videodev2.h</filename> header file.
+Applications should use their own copy, not include the version in the
+kernel sources on the system they compile on. All V4L2 ioctl requests,
+their respective function and parameters are specified in <xref
+	linkend="user-func" />.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+    <para>When an ioctl that takes an output or read/write parameter fails,
+    the parameter remains unmodified.</para>
+  </refsect1>
+</refentry>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-parent-document: "v4l2.sgml"
+indent-tabs-mode: nil
+End:
+-->
diff --git a/Documentation/DocBook/v4l/func-mmap.xml b/Documentation/DocBook/media/v4l/func-mmap.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-mmap.xml
rename to Documentation/DocBook/media/v4l/func-mmap.xml
diff --git a/Documentation/DocBook/v4l/func-munmap.xml b/Documentation/DocBook/media/v4l/func-munmap.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-munmap.xml
rename to Documentation/DocBook/media/v4l/func-munmap.xml
diff --git a/Documentation/DocBook/v4l/func-open.xml b/Documentation/DocBook/media/v4l/func-open.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-open.xml
rename to Documentation/DocBook/media/v4l/func-open.xml
diff --git a/Documentation/DocBook/v4l/func-poll.xml b/Documentation/DocBook/media/v4l/func-poll.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-poll.xml
rename to Documentation/DocBook/media/v4l/func-poll.xml
diff --git a/Documentation/DocBook/v4l/func-read.xml b/Documentation/DocBook/media/v4l/func-read.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-read.xml
rename to Documentation/DocBook/media/v4l/func-read.xml
diff --git a/Documentation/DocBook/v4l/func-select.xml b/Documentation/DocBook/media/v4l/func-select.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-select.xml
rename to Documentation/DocBook/media/v4l/func-select.xml
diff --git a/Documentation/DocBook/v4l/func-write.xml b/Documentation/DocBook/media/v4l/func-write.xml
similarity index 100%
rename from Documentation/DocBook/v4l/func-write.xml
rename to Documentation/DocBook/media/v4l/func-write.xml
diff --git a/Documentation/DocBook/media/v4l/gen-errors.xml b/Documentation/DocBook/media/v4l/gen-errors.xml
new file mode 100644
index 0000000..5bbf3ce
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/gen-errors.xml
@@ -0,0 +1,78 @@
+<title>Generic Error Codes</title>
+
+<table frame="none" pgwide="1" id="gen-errors">
+  <title>Generic error codes</title>
+  <tgroup cols="2">
+    &cs-str;
+    <tbody valign="top">
+	<!-- Keep it ordered alphabetically -->
+      <row>
+	<entry>EBADF</entry>
+	<entry>The file descriptor is not a valid.</entry>
+      </row>
+      <row>
+	<entry>EBUSY</entry>
+	<entry>The ioctl can't be handled because the device is busy. This is
+	       typically return while device is streaming, and an ioctl tried to
+	       change something that would affect the stream, or would require the
+	       usage of a hardware resource that was already allocated. The ioctl
+	       must not be retried without performing another action to fix the
+	       problem first (typically: stop the stream before retrying).</entry>
+      </row>
+      <row>
+	<entry>EFAULT</entry>
+	<entry>There was a failure while copying data from/to userspace,
+	       probably caused by an invalid pointer reference.</entry>
+      </row>
+      <row>
+	<entry>EINVAL</entry>
+	<entry>One or more of the ioctl parameters are invalid or out of the
+	       allowed range. This is a widely used error code. See the individual
+	       ioctl requests for specific causes.</entry>
+      </row>
+      <row>
+        <entry>ENODEV</entry>
+	<entry>Device not found or was removed.</entry>
+      </row>
+      <row>
+	<entry>ENOMEM</entry>
+	<entry>There's not enough memory to handle the desired operation.</entry>
+      </row>
+      <row>
+	<entry>ENOTTY</entry>
+	<entry>The ioctl is not supported by the driver, actually meaning that
+	       the required functionality is not available, or the file
+	       descriptor is not for a media device.</entry>
+      </row>
+      <row>
+	<entry>ENOSPC</entry>
+	<entry>On USB devices, the stream ioctl's can return this error, meaning
+	       that this request would overcommit the usb bandwidth reserved
+	       for periodic transfers (up to 80% of the USB bandwidth).</entry>
+      </row>
+      <row>
+	<entry>ENOSYS or EOPNOTSUPP</entry>
+	<entry>Function not available for this device (dvb API only. Will likely
+	       be replaced anytime soon by ENOTTY).</entry>
+      </row>
+      <row>
+	<entry>EPERM</entry>
+	<entry>Permission denied. Can be returned if the device needs write
+		permission, or some special capabilities is needed
+		(e. g. root)</entry>
+      </row>
+      <row>
+	<entry>EWOULDBLOCK</entry>
+	<entry>Operation would block. Used when the ioctl would need to wait
+	       for an event, but the device was opened in non-blocking mode.</entry>
+      </row>
+    </tbody>
+  </tgroup>
+</table>
+
+<para>Note 1: ioctls may return other error codes. Since errors may have side
+effects such as a driver reset, applications should abort on unexpected errors.
+</para>
+
+<para>Note 2: Request-specific error codes are listed in the individual
+requests descriptions.</para>
diff --git a/Documentation/DocBook/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml
similarity index 100%
rename from Documentation/DocBook/v4l/io.xml
rename to Documentation/DocBook/media/v4l/io.xml
diff --git a/Documentation/DocBook/v4l/keytable.c.xml b/Documentation/DocBook/media/v4l/keytable.c.xml
similarity index 100%
rename from Documentation/DocBook/v4l/keytable.c.xml
rename to Documentation/DocBook/media/v4l/keytable.c.xml
diff --git a/Documentation/DocBook/v4l/libv4l.xml b/Documentation/DocBook/media/v4l/libv4l.xml
similarity index 100%
rename from Documentation/DocBook/v4l/libv4l.xml
rename to Documentation/DocBook/media/v4l/libv4l.xml
diff --git a/Documentation/DocBook/v4l/lirc_device_interface.xml b/Documentation/DocBook/media/v4l/lirc_device_interface.xml
similarity index 99%
rename from Documentation/DocBook/v4l/lirc_device_interface.xml
rename to Documentation/DocBook/media/v4l/lirc_device_interface.xml
index 0e0453f..8d7eb6b 100644
--- a/Documentation/DocBook/v4l/lirc_device_interface.xml
+++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml
@@ -246,6 +246,8 @@
     </listitem>
   </varlistentry>
 </variablelist>
-
+<section id="lirc_dev_errors">
+  &return-value;
+</section>
 </section>
 </section>
diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/media/v4l/media-controller.xml
similarity index 100%
rename from Documentation/DocBook/v4l/media-controller.xml
rename to Documentation/DocBook/media/v4l/media-controller.xml
diff --git a/Documentation/DocBook/v4l/media-func-close.xml b/Documentation/DocBook/media/v4l/media-func-close.xml
similarity index 100%
rename from Documentation/DocBook/v4l/media-func-close.xml
rename to Documentation/DocBook/media/v4l/media-func-close.xml
diff --git a/Documentation/DocBook/media/v4l/media-func-ioctl.xml b/Documentation/DocBook/media/v4l/media-func-ioctl.xml
new file mode 100644
index 0000000..39478d0
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/media-func-ioctl.xml
@@ -0,0 +1,73 @@
+<refentry id="media-func-ioctl">
+  <refmeta>
+    <refentrytitle>media ioctl()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>media-ioctl</refname>
+    <refpurpose>Control a media device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>void *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>Media ioctl request code as defined in the media.h header file,
+	  for example MEDIA_IOC_SETUP_LINK.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para>Pointer to a request-specific structure.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+    <para>The <function>ioctl()</function> function manipulates media device
+    parameters. The argument <parameter>fd</parameter> must be an open file
+    descriptor.</para>
+    <para>The ioctl <parameter>request</parameter> code specifies the media
+    function to be called. It has encoded in it whether the argument is an
+    input, output or read/write parameter, and the size of the argument
+    <parameter>argp</parameter> in bytes.</para>
+    <para>Macros and structures definitions specifying media ioctl requests and
+    their parameters are located in the media.h header file. All media ioctl
+    requests, their respective function and parameters are specified in
+    <xref linkend="media-user-func" />.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <para>Request-specific error codes are listed in the
+    individual requests descriptions.</para>
+    <para>When an ioctl that takes an output or read/write parameter fails,
+    the parameter remains unmodified.</para>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/v4l/media-func-open.xml b/Documentation/DocBook/media/v4l/media-func-open.xml
similarity index 100%
rename from Documentation/DocBook/v4l/media-func-open.xml
rename to Documentation/DocBook/media/v4l/media-func-open.xml
diff --git a/Documentation/DocBook/v4l/media-ioc-device-info.xml b/Documentation/DocBook/media/v4l/media-ioc-device-info.xml
similarity index 97%
rename from Documentation/DocBook/v4l/media-ioc-device-info.xml
rename to Documentation/DocBook/media/v4l/media-ioc-device-info.xml
index 1f32373..2ce5214 100644
--- a/Documentation/DocBook/v4l/media-ioc-device-info.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-device-info.xml
@@ -127,7 +127,6 @@
   </refsect1>
 
   <refsect1>
-    <title>Return value</title>
-    <para>This function doesn't return specific error codes.</para>
+    &return-value;
   </refsect1>
 </refentry>
diff --git a/Documentation/DocBook/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml
similarity index 100%
rename from Documentation/DocBook/v4l/media-ioc-enum-entities.xml
rename to Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml
diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
similarity index 98%
rename from Documentation/DocBook/v4l/media-ioc-enum-links.xml
rename to Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
index d2fc73e..355df43 100644
--- a/Documentation/DocBook/v4l/media-ioc-enum-links.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
@@ -139,7 +139,7 @@
     </table>
 
     <table pgwide="1" frame="none" id="media-link-desc">
-      <title>struct <structname>media_links_desc</structname></title>
+      <title>struct <structname>media_link_desc</structname></title>
       <tgroup cols="3">
         &cs-str;
 	<tbody valign="top">
diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/media/v4l/media-ioc-setup-link.xml
similarity index 87%
rename from Documentation/DocBook/v4l/media-ioc-setup-link.xml
rename to Documentation/DocBook/media/v4l/media-ioc-setup-link.xml
index cec97af..fc2e522 100644
--- a/Documentation/DocBook/v4l/media-ioc-setup-link.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-setup-link.xml
@@ -72,15 +72,6 @@
 
     <variablelist>
       <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The link properties can't be changed because the link is
-	  currently busy. This can be caused, for instance, by an active media
-	  stream (audio or video) on the link. The ioctl shouldn't be retried if
-	  no other action is performed before to fix the problem.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The &media-link-desc; references a non-existing link, or the
diff --git a/Documentation/DocBook/v4l/pipeline.pdf b/Documentation/DocBook/media/v4l/pipeline.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/pipeline.pdf
rename to Documentation/DocBook/media/v4l/pipeline.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/pixfmt-grey.xml b/Documentation/DocBook/media/v4l/pixfmt-grey.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-grey.xml
rename to Documentation/DocBook/media/v4l/pixfmt-grey.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-m420.xml b/Documentation/DocBook/media/v4l/pixfmt-m420.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-m420.xml
rename to Documentation/DocBook/media/v4l/pixfmt-m420.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-nv12.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-nv12.xml
rename to Documentation/DocBook/media/v4l/pixfmt-nv12.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-nv12m.xml
rename to Documentation/DocBook/media/v4l/pixfmt-nv12m.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-nv12mt.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12mt.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-nv12mt.xml
rename to Documentation/DocBook/media/v4l/pixfmt-nv12mt.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-nv16.xml b/Documentation/DocBook/media/v4l/pixfmt-nv16.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-nv16.xml
rename to Documentation/DocBook/media/v4l/pixfmt-nv16.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
rename to Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-packed-yuv.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-yuv.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-packed-yuv.xml
rename to Documentation/DocBook/media/v4l/pixfmt-packed-yuv.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-sbggr16.xml b/Documentation/DocBook/media/v4l/pixfmt-sbggr16.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-sbggr16.xml
rename to Documentation/DocBook/media/v4l/pixfmt-sbggr16.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-sbggr8.xml b/Documentation/DocBook/media/v4l/pixfmt-sbggr8.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-sbggr8.xml
rename to Documentation/DocBook/media/v4l/pixfmt-sbggr8.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-sgbrg8.xml b/Documentation/DocBook/media/v4l/pixfmt-sgbrg8.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-sgbrg8.xml
rename to Documentation/DocBook/media/v4l/pixfmt-sgbrg8.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-sgrbg8.xml b/Documentation/DocBook/media/v4l/pixfmt-sgrbg8.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-sgrbg8.xml
rename to Documentation/DocBook/media/v4l/pixfmt-sgrbg8.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-srggb10.xml
rename to Documentation/DocBook/media/v4l/pixfmt-srggb10.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-srggb12.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb12.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-srggb12.xml
rename to Documentation/DocBook/media/v4l/pixfmt-srggb12.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-srggb8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb8.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-srggb8.xml
rename to Documentation/DocBook/media/v4l/pixfmt-srggb8.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-uyvy.xml b/Documentation/DocBook/media/v4l/pixfmt-uyvy.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-uyvy.xml
rename to Documentation/DocBook/media/v4l/pixfmt-uyvy.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-vyuy.xml b/Documentation/DocBook/media/v4l/pixfmt-vyuy.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-vyuy.xml
rename to Documentation/DocBook/media/v4l/pixfmt-vyuy.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-y10.xml b/Documentation/DocBook/media/v4l/pixfmt-y10.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-y10.xml
rename to Documentation/DocBook/media/v4l/pixfmt-y10.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-y10b.xml b/Documentation/DocBook/media/v4l/pixfmt-y10b.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-y10b.xml
rename to Documentation/DocBook/media/v4l/pixfmt-y10b.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-y12.xml b/Documentation/DocBook/media/v4l/pixfmt-y12.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-y12.xml
rename to Documentation/DocBook/media/v4l/pixfmt-y12.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-y16.xml b/Documentation/DocBook/media/v4l/pixfmt-y16.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-y16.xml
rename to Documentation/DocBook/media/v4l/pixfmt-y16.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-y41p.xml b/Documentation/DocBook/media/v4l/pixfmt-y41p.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-y41p.xml
rename to Documentation/DocBook/media/v4l/pixfmt-y41p.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv410.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv410.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuv410.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuv410.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv411p.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv411p.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuv411p.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuv411p.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv420.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv420.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuv420.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuv420.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv420m.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuv420m.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuv420m.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuv422p.xml b/Documentation/DocBook/media/v4l/pixfmt-yuv422p.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuv422p.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuv422p.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yuyv.xml b/Documentation/DocBook/media/v4l/pixfmt-yuyv.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yuyv.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yuyv.xml
diff --git a/Documentation/DocBook/v4l/pixfmt-yvyu.xml b/Documentation/DocBook/media/v4l/pixfmt-yvyu.xml
similarity index 100%
rename from Documentation/DocBook/v4l/pixfmt-yvyu.xml
rename to Documentation/DocBook/media/v4l/pixfmt-yvyu.xml
diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml
similarity index 94%
rename from Documentation/DocBook/v4l/pixfmt.xml
rename to Documentation/DocBook/media/v4l/pixfmt.xml
index deb6602..2ff6b77 100644
--- a/Documentation/DocBook/v4l/pixfmt.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt.xml
@@ -121,7 +121,7 @@
     an array of <structname>v4l2_plane_pix_format</structname> structures,
     describing all planes of that format.</para>
   <table pgwide="1" frame="none" id="v4l2-plane-pix-format">
-    <title>struct <structname>vl42_plane_pix_format</structname></title>
+    <title>struct <structname>v4l2_plane_pix_format</structname></title>
     <tgroup cols="3">
       &cs-str;
       <tbody valign="top">
@@ -741,10 +741,55 @@
 	  <row id="V4L2-PIX-FMT-MPEG">
 	    <entry><constant>V4L2_PIX_FMT_MPEG</constant></entry>
 	    <entry>'MPEG'</entry>
-	    <entry>MPEG stream. The actual format is determined by
+	    <entry>MPEG multiplexed stream. The actual format is determined by
 extended control <constant>V4L2_CID_MPEG_STREAM_TYPE</constant>, see
 <xref linkend="mpeg-control-id" />.</entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-H264">
+		<entry><constant>V4L2_PIX_FMT_H264</constant></entry>
+		<entry>'H264'</entry>
+		<entry>H264 video elementary stream with start codes.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-H264-NO-SC">
+		<entry><constant>V4L2_PIX_FMT_H264_NO_SC</constant></entry>
+		<entry>'AVC1'</entry>
+		<entry>H264 video elementary stream without start codes.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-H263">
+		<entry><constant>V4L2_PIX_FMT_H263</constant></entry>
+		<entry>'H263'</entry>
+		<entry>H263 video elementary stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-MPEG1">
+		<entry><constant>V4L2_PIX_FMT_MPEG1</constant></entry>
+		<entry>'MPG1'</entry>
+		<entry>MPEG1 video elementary stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-MPEG2">
+		<entry><constant>V4L2_PIX_FMT_MPEG2</constant></entry>
+		<entry>'MPG2'</entry>
+		<entry>MPEG2 video elementary stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-MPEG4">
+		<entry><constant>V4L2_PIX_FMT_MPEG4</constant></entry>
+		<entry>'MPG4'</entry>
+		<entry>MPEG4 video elementary stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-XVID">
+		<entry><constant>V4L2_PIX_FMT_XVID</constant></entry>
+		<entry>'XVID'</entry>
+		<entry>Xvid video elementary stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-VC1-ANNEX-G">
+		<entry><constant>V4L2_PIX_FMT_VC1_ANNEX_G</constant></entry>
+		<entry>'VC1G'</entry>
+		<entry>VC1, SMPTE 421M Annex G compliant stream.</entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-VC1-ANNEX-L">
+		<entry><constant>V4L2_PIX_FMT_VC1_ANNEX_L</constant></entry>
+		<entry>'VC1L'</entry>
+		<entry>VC1, SMPTE 421M Annex L compliant stream.</entry>
+	  </row>
 	</tbody>
       </tgroup>
     </table>
@@ -804,6 +849,12 @@
 	    <entry>'CPIA'</entry>
 	    <entry>YUV format used by the gspca cpia1 driver.</entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-JPGL">
+	    <entry><constant>V4L2_PIX_FMT_JPGL</constant></entry>
+	    <entry>'JPGL'</entry>
+	    <entry>JPEG-Light format (Pegasus Lossless JPEG)
+			used in Divio webcams NW 80x.</entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-SPCA501">
 	    <entry><constant>V4L2_PIX_FMT_SPCA501</constant></entry>
 	    <entry>'S501'</entry>
@@ -854,6 +905,11 @@
 	    <entry>'PJPG'</entry>
 	    <entry>Pixart 73xx JPEG format used by the gspca driver.</entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-SE401">
+	    <entry><constant>V4L2_PIX_FMT_SE401</constant></entry>
+	    <entry>'S401'</entry>
+	    <entry>Compressed RGB format used by the gspca se401 driver</entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-SQ905C">
 	    <entry><constant>V4L2_PIX_FMT_SQ905C</constant></entry>
 	    <entry>'905C'</entry>
diff --git a/Documentation/DocBook/v4l/planar-apis.xml b/Documentation/DocBook/media/v4l/planar-apis.xml
similarity index 100%
rename from Documentation/DocBook/v4l/planar-apis.xml
rename to Documentation/DocBook/media/v4l/planar-apis.xml
diff --git a/Documentation/DocBook/v4l/remote_controllers.xml b/Documentation/DocBook/media/v4l/remote_controllers.xml
similarity index 100%
rename from Documentation/DocBook/v4l/remote_controllers.xml
rename to Documentation/DocBook/media/v4l/remote_controllers.xml
diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml
similarity index 99%
rename from Documentation/DocBook/v4l/subdev-formats.xml
rename to Documentation/DocBook/media/v4l/subdev-formats.xml
index 8d3409d..49c532e 100644
--- a/Documentation/DocBook/v4l/subdev-formats.xml
+++ b/Documentation/DocBook/media/v4l/subdev-formats.xml
@@ -380,9 +380,6 @@
 	<title>Bayer Patterns</title>
 	<mediaobject>
 	  <imageobject>
-	    <imagedata fileref="bayer.pdf" format="PS" />
-	  </imageobject>
-	  <imageobject>
 	    <imagedata fileref="bayer.png" format="PNG" />
 	  </imageobject>
 	  <textobject>
@@ -2528,7 +2525,7 @@
 
       <para>Those data formats consist of an ordered sequence of 8-bit bytes
 	obtained from JPEG compression process. Additionally to the
-	<constant>_JPEG</constant> prefix the format code is made of
+	<constant>_JPEG</constant> postfix the format code is made of
 	the following information.
 	<itemizedlist>
 	  <listitem><para>The number of bus samples per entropy encoded byte.</para></listitem>
diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
similarity index 97%
rename from Documentation/DocBook/v4l/v4l2.xml
rename to Documentation/DocBook/media/v4l/v4l2.xml
index a7fd76d..0d05e87 100644
--- a/Documentation/DocBook/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -87,7 +87,7 @@
       </author>
 
       <author>
-      	<firstname>Pawel</firstname>
+	<firstname>Pawel</firstname>
 	<surname>Osciak</surname>
 	<contrib>Designed and documented the multi-planar API.</contrib>
 	<affiliation>
@@ -128,6 +128,15 @@
 applications. -->
 
       <revision>
+	<revnumber>3.1</revnumber>
+	<date>2011-06-27</date>
+	<authorinitials>mcc, po, hv</authorinitials>
+	<revremark>Documented that VIDIOC_QUERYCAP now returns a per-subsystem version instead of a per-driver one.
+		   Standardize an error code for invalid ioctl.
+		   Added V4L2_CTRL_TYPE_BITMASK.</revremark>
+      </revision>
+
+      <revision>
 	<revnumber>2.6.39</revnumber>
 	<date>2011-03-01</date>
 	<authorinitials>mcc, po</authorinitials>
@@ -401,7 +410,7 @@
 </partinfo>
 
 <title>Video for Linux Two API Specification</title>
- <subtitle>Revision 2.6.39</subtitle>
+ <subtitle>Revision 3.1</subtitle>
 
   <chapter id="common">
     &sub-common;
diff --git a/Documentation/DocBook/v4l/v4l2grab.c.xml b/Documentation/DocBook/media/v4l/v4l2grab.c.xml
similarity index 100%
rename from Documentation/DocBook/v4l/v4l2grab.c.xml
rename to Documentation/DocBook/media/v4l/v4l2grab.c.xml
diff --git a/Documentation/DocBook/v4l/vbi_525.pdf b/Documentation/DocBook/media/v4l/vbi_525.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/vbi_525.pdf
rename to Documentation/DocBook/media/v4l/vbi_525.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/vbi_625.pdf b/Documentation/DocBook/media/v4l/vbi_625.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/vbi_625.pdf
rename to Documentation/DocBook/media/v4l/vbi_625.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/vbi_hsync.pdf b/Documentation/DocBook/media/v4l/vbi_hsync.pdf
similarity index 100%
rename from Documentation/DocBook/v4l/vbi_hsync.pdf
rename to Documentation/DocBook/media/v4l/vbi_hsync.pdf
Binary files differ
diff --git a/Documentation/DocBook/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
similarity index 94%
rename from Documentation/DocBook/v4l/vidioc-cropcap.xml
rename to Documentation/DocBook/media/v4l/vidioc-cropcap.xml
index 816e90e..b4f2f25 100644
--- a/Documentation/DocBook/v4l/vidioc-cropcap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
@@ -156,19 +156,10 @@
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The &v4l2-cropcap; <structfield>type</structfield> is
-invalid or the ioctl is not supported. This is not permitted for
-video capture, output and overlay devices, which must support
-<constant>VIDIOC_CROPCAP</constant>.</para>
+invalid. This is not permitted for video capture, output and overlay devices,
+which must support <constant>VIDIOC_CROPCAP</constant>.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-dbg-g-chip-ident.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-dbg-g-chip-ident.xml
rename to Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml
index 4a09e20..4ecd966 100644
--- a/Documentation/DocBook/v4l/vidioc-dbg-g-chip-ident.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml
@@ -258,18 +258,9 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The driver does not support this ioctl, or the
-<structfield>match_type</structfield> is invalid.</para>
+	  <para>The <structfield>match_type</structfield> is invalid.</para>
 	</listitem>
       </varlistentry>
      </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-dbg-g-register.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
similarity index 94%
rename from Documentation/DocBook/v4l/vidioc-dbg-g-register.xml
rename to Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
index 980c7f3..a44aebc 100644
--- a/Documentation/DocBook/v4l/vidioc-dbg-g-register.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
@@ -247,15 +247,6 @@
 
     <variablelist>
       <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The driver does not support this ioctl, or the kernel
-was not compiled with the <constant>CONFIG_VIDEO_ADV_DEBUG</constant>
-option, or the <structfield>match_type</structfield> is invalid, or the
-selected chip or register does not exist.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EPERM</errorcode></term>
 	<listitem>
 	  <para>Insufficient permissions. Root privileges are required
@@ -265,11 +256,3 @@
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
similarity index 84%
rename from Documentation/DocBook/v4l/vidioc-dqevent.xml
rename to Documentation/DocBook/media/v4l/vidioc-dqevent.xml
index 4e0a7cc..7769642 100644
--- a/Documentation/DocBook/v4l/vidioc-dqevent.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
@@ -81,6 +81,13 @@
 	  </row>
 	  <row>
 	    <entry></entry>
+	    <entry>&v4l2-event-ctrl;</entry>
+            <entry><structfield>ctrl</structfield></entry>
+	    <entry>Event data for event V4L2_EVENT_CTRL.
+            </entry>
+	  </row>
+	  <row>
+	    <entry></entry>
 	    <entry>__u8</entry>
             <entry><structfield>data</structfield>[64]</entry>
 	    <entry>Event data. Defined by the event type. The union
@@ -110,8 +117,16 @@
 	    <entry>Event timestamp.</entry>
 	  </row>
 	  <row>
+	    <entry>u32</entry>
+	    <entry><structfield>id</structfield></entry>
+            <entry></entry>
+	    <entry>The ID associated with the event source. If the event does not
+		have an associated ID (this depends on the event type), then this
+		is 0.</entry>
+	  </row>
+	  <row>
 	    <entry>__u32</entry>
-	    <entry><structfield>reserved</structfield>[9]</entry>
+	    <entry><structfield>reserved</structfield>[8]</entry>
             <entry></entry>
 	    <entry>Reserved for future extensions. Drivers must set
 	    the array to zero.</entry>
@@ -121,11 +136,7 @@
     </table>
 
   </refsect1>
+  <refsect1>
+    &return-value;
+  </refsect1>
 </refentry>
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-encoder-cmd.xml b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
similarity index 96%
rename from Documentation/DocBook/v4l/vidioc-encoder-cmd.xml
rename to Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
index b0dde94..af7f3f2 100644
--- a/Documentation/DocBook/v4l/vidioc-encoder-cmd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
@@ -180,8 +180,7 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The driver does not support this ioctl, or the
-<structfield>cmd</structfield> field is invalid.</para>
+	  <para>The <structfield>cmd</structfield> field is invalid.</para>
 	</listitem>
       </varlistentry>
       <varlistentry>
@@ -194,11 +193,3 @@
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-enum-dv-presets.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-enum-dv-presets.xml
rename to Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml
diff --git a/Documentation/DocBook/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-enum-fmt.xml
rename to Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml
diff --git a/Documentation/DocBook/v4l/vidioc-enum-frameintervals.xml b/Documentation/DocBook/media/v4l/vidioc-enum-frameintervals.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-enum-frameintervals.xml
rename to Documentation/DocBook/media/v4l/vidioc-enum-frameintervals.xml
index 3c216e1..5fd72c4 100644
--- a/Documentation/DocBook/v4l/vidioc-enum-frameintervals.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-frameintervals.xml
@@ -254,17 +254,6 @@
 
   <refsect1>
     &return-value;
-
-    <para>See the description section above for a list of return
-values that <varname>errno</varname> can have.</para>
   </refsect1>
 
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-enum-framesizes.xml b/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-enum-framesizes.xml
rename to Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
index 6afa454..f77a13f 100644
--- a/Documentation/DocBook/v4l/vidioc-enum-framesizes.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
@@ -267,16 +267,5 @@
 
   <refsect1>
     &return-value;
-
-    <para>See the description section above for a list of return
-values that <varname>errno</varname> can have.</para>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-enumaudio.xml b/Documentation/DocBook/media/v4l/vidioc-enumaudio.xml
similarity index 89%
rename from Documentation/DocBook/v4l/vidioc-enumaudio.xml
rename to Documentation/DocBook/media/v4l/vidioc-enumaudio.xml
index 9ae8f2d..ea816ab 100644
--- a/Documentation/DocBook/v4l/vidioc-enumaudio.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enumaudio.xml
@@ -68,19 +68,9 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The number of the audio input is out of bounds, or
-there are no audio inputs at all and this ioctl is not
-supported.</para>
+	  <para>The number of the audio input is out of bounds.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-enumaudioout.xml b/Documentation/DocBook/media/v4l/vidioc-enumaudioout.xml
similarity index 90%
rename from Documentation/DocBook/v4l/vidioc-enumaudioout.xml
rename to Documentation/DocBook/media/v4l/vidioc-enumaudioout.xml
index d3d7c0a..2e87ced 100644
--- a/Documentation/DocBook/v4l/vidioc-enumaudioout.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enumaudioout.xml
@@ -71,19 +71,9 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The number of the audio output is out of bounds, or
-there are no audio outputs at all and this ioctl is not
-supported.</para>
+	  <para>The number of the audio output is out of bounds.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-enuminput.xml b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-enuminput.xml
rename to Documentation/DocBook/media/v4l/vidioc-enuminput.xml
diff --git a/Documentation/DocBook/v4l/vidioc-enumoutput.xml b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-enumoutput.xml
rename to Documentation/DocBook/media/v4l/vidioc-enumoutput.xml
diff --git a/Documentation/DocBook/v4l/vidioc-enumstd.xml b/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-enumstd.xml
rename to Documentation/DocBook/media/v4l/vidioc-enumstd.xml
diff --git a/Documentation/DocBook/v4l/vidioc-g-audio.xml b/Documentation/DocBook/media/v4l/vidioc-g-audio.xml
similarity index 93%
rename from Documentation/DocBook/v4l/vidioc-g-audio.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-audio.xml
index 65361a8..d7bb9b3 100644
--- a/Documentation/DocBook/v4l/vidioc-g-audio.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-audio.xml
@@ -164,25 +164,9 @@
 	<listitem>
 	  <para>No audio inputs combine with the current video input,
 or the number of the selected audio input is out of bounds or it does
-not combine, or there are no audio inputs at all and the ioctl is not
-supported.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>I/O is in progress, the input cannot be
-switched.</para>
+not combine.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-audioout.xml b/Documentation/DocBook/media/v4l/vidioc-g-audioout.xml
similarity index 91%
rename from Documentation/DocBook/v4l/vidioc-g-audioout.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-audioout.xml
index 3632730..200a270 100644
--- a/Documentation/DocBook/v4l/vidioc-g-audioout.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-audioout.xml
@@ -130,25 +130,9 @@
 	<listitem>
 	  <para>No audio outputs combine with the current video
 output, or the number of the selected audio output is out of bounds or
-it does not combine, or there are no audio outputs at all and the
-ioctl is not supported.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>I/O is in progress, the output cannot be
-switched.</para>
+it does not combine.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml
similarity index 93%
rename from Documentation/DocBook/v4l/vidioc-g-crop.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-crop.xml
index d235b1d..01a5064 100644
--- a/Documentation/DocBook/v4l/vidioc-g-crop.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml
@@ -122,22 +122,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>Cropping is not supported.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-ctrl.xml b/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml
similarity index 94%
rename from Documentation/DocBook/v4l/vidioc-g-ctrl.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml
index 8b5e6ff..5146d00 100644
--- a/Documentation/DocBook/v4l/vidioc-g-ctrl.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml
@@ -117,6 +117,13 @@
 this control belongs to.</para>
 	</listitem>
       </varlistentry>
+      <varlistentry>
+	<term><errorcode>EACCES</errorcode></term>
+	<listitem>
+	  <para>Attempt to set a read-only control or to get a
+	  write-only control.</para>
+	</listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
similarity index 96%
rename from Documentation/DocBook/v4l/vidioc-g-dv-preset.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
index d733721..7940c11 100644
--- a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
@@ -97,14 +97,8 @@
 	</tbody>
       </tgroup>
     </table>
-
+  </refsect1>
+  <refsect1>
+    &return-value;
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
similarity index 98%
rename from Documentation/DocBook/v4l/vidioc-g-dv-timings.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
index d5ec6ab..4a8648a 100644
--- a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
@@ -212,12 +212,7 @@
       </tgroup>
     </table>
   </refsect1>
+  <refsect1>
+    &return-value;
+  </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-enc-index.xml b/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
similarity index 95%
rename from Documentation/DocBook/v4l/vidioc-g-enc-index.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
index 9f242e4..2aef02c 100644
--- a/Documentation/DocBook/v4l/vidioc-g-enc-index.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
@@ -192,22 +192,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The driver does not support this ioctl.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
similarity index 95%
rename from Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
index 3aa7f8f..5122ce8 100644
--- a/Documentation/DocBook/v4l/vidioc-g-ext-ctrls.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
@@ -250,6 +250,13 @@
 These controls are described in <xref
 		linkend="fm-tx-controls" />.</entry>
 	  </row>
+	  <row>
+	    <entry><constant>V4L2_CTRL_CLASS_FLASH</constant></entry>
+	    <entry>0x9c0000</entry>
+	    <entry>The class containing flash device controls.
+These controls are described in <xref
+		linkend="flash-controls" />.</entry>
+	  </row>
 	</tbody>
       </tgroup>
     </table>
@@ -294,6 +301,13 @@
 to store the payload and this error code is returned.</para>
 	</listitem>
       </varlistentry>
+      <varlistentry>
+	<term><errorcode>EACCES</errorcode></term>
+	<listitem>
+	  <para>Attempt to try or set a read-only control or to get a
+	  write-only control.</para>
+	</listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
diff --git a/Documentation/DocBook/v4l/vidioc-g-fbuf.xml b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml
similarity index 96%
rename from Documentation/DocBook/v4l/vidioc-g-fbuf.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml
index e7dda48..0557182 100644
--- a/Documentation/DocBook/v4l/vidioc-g-fbuf.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml
@@ -446,28 +446,11 @@
 	</listitem>
       </varlistentry>
       <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The framebuffer parameters cannot be changed at this
-time because overlay is already enabled, or capturing is enabled
-and the hardware cannot capture and overlay simultaneously.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The ioctl is not supported or the
-<constant>VIDIOC_S_FBUF</constant> parameters are unsuitable.</para>
+	  <para>The <constant>VIDIOC_S_FBUF</constant> parameters are unsuitable.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
similarity index 93%
rename from Documentation/DocBook/v4l/vidioc-g-fmt.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
index a4ae59b..17fbda1 100644
--- a/Documentation/DocBook/v4l/vidioc-g-fmt.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
@@ -184,29 +184,13 @@
 
     <variablelist>
       <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The data format cannot be changed at this
-time, for example because I/O is already in progress.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The &v4l2-format; <structfield>type</structfield>
-field is invalid, the requested buffer type not supported, or
-<constant>VIDIOC_TRY_FMT</constant> was called and is not
-supported with this buffer type.</para>
+field is invalid, the requested buffer type not supported, or the
+format is not supported with this buffer type.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-frequency.xml b/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-g-frequency.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
diff --git a/Documentation/DocBook/v4l/vidioc-g-input.xml b/Documentation/DocBook/media/v4l/vidioc-g-input.xml
similarity index 84%
rename from Documentation/DocBook/v4l/vidioc-g-input.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-input.xml
index ed076e9..08ae82f 100644
--- a/Documentation/DocBook/v4l/vidioc-g-input.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-input.xml
@@ -75,26 +75,9 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The number of the video input is out of bounds, or
-there are no video inputs at all and this ioctl is not
-supported.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>I/O is in progress, the input cannot be
-switched.</para>
+	  <para>The number of the video input is out of bounds.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-jpegcomp.xml b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
similarity index 93%
rename from Documentation/DocBook/v4l/vidioc-g-jpegcomp.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
index 77394b2..01ea24b 100644
--- a/Documentation/DocBook/v4l/vidioc-g-jpegcomp.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
@@ -159,22 +159,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>This ioctl is not supported.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-modulator.xml b/Documentation/DocBook/media/v4l/vidioc-g-modulator.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-g-modulator.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-modulator.xml
diff --git a/Documentation/DocBook/v4l/vidioc-g-output.xml b/Documentation/DocBook/media/v4l/vidioc-g-output.xml
similarity index 86%
rename from Documentation/DocBook/v4l/vidioc-g-output.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-output.xml
index 3ea8c0e..fd45f1c 100644
--- a/Documentation/DocBook/v4l/vidioc-g-output.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-output.xml
@@ -76,25 +76,9 @@
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The number of the video output is out of bounds, or
-there are no video outputs at all and this ioctl is not
-supported.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>I/O is in progress, the output cannot be
-switched.</para>
+there are no video outputs at all.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-parm.xml b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-g-parm.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-parm.xml
index 392aa9e..19b1d85 100644
--- a/Documentation/DocBook/v4l/vidioc-g-parm.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml
@@ -311,22 +311,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>This ioctl is not supported.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-priority.xml b/Documentation/DocBook/media/v4l/vidioc-g-priority.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-g-priority.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-priority.xml
index 5fb0019..8f5e3da 100644
--- a/Documentation/DocBook/v4l/vidioc-g-priority.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-priority.xml
@@ -120,8 +120,7 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The requested priority value is invalid, or the
-driver does not support access priorities.</para>
+	  <para>The requested priority value is invalid.</para>
 	</listitem>
       </varlistentry>
       <varlistentry>
diff --git a/Documentation/DocBook/v4l/vidioc-g-sliced-vbi-cap.xml b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml
similarity index 96%
rename from Documentation/DocBook/v4l/vidioc-g-sliced-vbi-cap.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml
index 10e721b..71741da 100644
--- a/Documentation/DocBook/v4l/vidioc-g-sliced-vbi-cap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml
@@ -246,19 +246,10 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>The device does not support sliced VBI capturing or
-output, or the value in the <structfield>type</structfield> field is
+	  <para>The value in the <structfield>type</structfield> field is
 wrong.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-g-std.xml b/Documentation/DocBook/media/v4l/vidioc-g-std.xml
similarity index 90%
rename from Documentation/DocBook/v4l/vidioc-g-std.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-std.xml
index 912f851..37996f2 100644
--- a/Documentation/DocBook/v4l/vidioc-g-std.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-std.xml
@@ -82,14 +82,7 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>This ioctl is not supported, or the
-<constant>VIDIOC_S_STD</constant> parameter was unsuitable.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The device is busy and therefore can not change the standard</para>
+	  <para>The <constant>VIDIOC_S_STD</constant> parameter was unsuitable.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
diff --git a/Documentation/DocBook/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-g-tuner.xml
rename to Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
diff --git a/Documentation/DocBook/v4l/vidioc-log-status.xml b/Documentation/DocBook/media/v4l/vidioc-log-status.xml
similarity index 80%
rename from Documentation/DocBook/v4l/vidioc-log-status.xml
rename to Documentation/DocBook/media/v4l/vidioc-log-status.xml
index 2634b7c..5ded7d3 100644
--- a/Documentation/DocBook/v4l/vidioc-log-status.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-log-status.xml
@@ -37,22 +37,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The driver does not support this ioctl.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-overlay.xml b/Documentation/DocBook/media/v4l/vidioc-overlay.xml
similarity index 90%
rename from Documentation/DocBook/v4l/vidioc-overlay.xml
rename to Documentation/DocBook/media/v4l/vidioc-overlay.xml
index 1036c58..250a7de 100644
--- a/Documentation/DocBook/v4l/vidioc-overlay.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-overlay.xml
@@ -65,19 +65,10 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>Video overlay is not supported, or the
-parameters have not been set up. See <xref
+	  <para>The overlay parameters have not been set up. See <xref
 linkend="overlay" /> for the necessary steps.</para>
 	</listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml
similarity index 95%
rename from Documentation/DocBook/v4l/vidioc-qbuf.xml
rename to Documentation/DocBook/media/v4l/vidioc-qbuf.xml
index f2b11f8..9caa49a 100644
--- a/Documentation/DocBook/v4l/vidioc-qbuf.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml
@@ -158,15 +158,6 @@
 <structfield>userptr</structfield> or
 <structfield>length</structfield> are invalid.</para>
 	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ENOMEM</errorcode></term>
-	<listitem>
-	  <para>Not enough physical or virtual memory was available to
-enqueue a user pointer buffer.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EIO</errorcode></term>
 	<listitem>
 	  <para><constant>VIDIOC_DQBUF</constant> failed due to an
@@ -184,11 +175,3 @@
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
similarity index 78%
rename from Documentation/DocBook/v4l/vidioc-query-dv-preset.xml
rename to Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
index d272f7a..23b17f6 100644
--- a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
@@ -61,27 +61,5 @@
 
   <refsect1>
     &return-value;
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>This ioctl is not supported.</para>
-	</listitem>
-    </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The device is busy and therefore can not sense the preset</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-querybuf.xml b/Documentation/DocBook/media/v4l/vidioc-querybuf.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-querybuf.xml
rename to Documentation/DocBook/media/v4l/vidioc-querybuf.xml
diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
similarity index 93%
rename from Documentation/DocBook/v4l/vidioc-querycap.xml
rename to Documentation/DocBook/media/v4l/vidioc-querycap.xml
index f29f1b8..e3664d6 100644
--- a/Documentation/DocBook/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
@@ -67,9 +67,8 @@
 	    <entry><para>Name of the driver, a unique NUL-terminated
 ASCII string. For example: "bttv". Driver specific applications can
 use this information to verify the driver identity. It is also useful
-to work around known bugs, or to identify drivers in error reports.
-The driver version is stored in the <structfield>version</structfield>
-field.</para><para>Storing strings in fixed sized arrays is bad
+to work around known bugs, or to identify drivers in error reports.</para>
+<para>Storing strings in fixed sized arrays is bad
 practice but unavoidable here. Drivers and applications should take
 precautions to never read or write beyond the end of the array and to
 make sure the strings are properly NUL-terminated.</para></entry>
@@ -100,9 +99,13 @@
 	  <row>
 	    <entry>__u32</entry>
 	    <entry><structfield>version</structfield></entry>
-	    <entry><para>Version number of the driver. Together with
-the <structfield>driver</structfield> field this identifies a
-particular driver. The version number is formatted using the
+	    <entry><para>Version number of the driver.</para>
+<para>Starting on kernel 3.1, the version reported is provided per
+V4L2 subsystem, following the same Kernel numberation scheme. However, it
+should not always return the same version as the kernel, if, for example,
+an stable or distribution-modified kernel uses the V4L2 stack from a
+newer kernel.</para>
+<para>The version number is formatted using the
 <constant>KERNEL_VERSION()</constant> macro:</para></entry>
 	  </row>
 	  <row>
@@ -280,24 +283,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The device is not compatible with this
-specification.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
-
diff --git a/Documentation/DocBook/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml
similarity index 97%
rename from Documentation/DocBook/v4l/vidioc-queryctrl.xml
rename to Documentation/DocBook/media/v4l/vidioc-queryctrl.xml
index 0d5e828..677ea64 100644
--- a/Documentation/DocBook/v4l/vidioc-queryctrl.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml
@@ -156,7 +156,8 @@
 	    <entry>Maximum value, inclusive. This field gives an upper
 bound for <constant>V4L2_CTRL_TYPE_INTEGER</constant> controls and the
 highest valid index for <constant>V4L2_CTRL_TYPE_MENU</constant>
-controls.
+controls. For <constant>V4L2_CTRL_TYPE_BITMASK</constant> controls it is the
+set of usable bits.
 For <constant>V4L2_CTRL_TYPE_STRING</constant> controls the maximum value
 gives the maximum length of the string. This length <emphasis>does not include the terminating
 zero</emphasis>. It may not be valid for any other type of control, including
@@ -291,6 +292,15 @@
 <constant>VIDIOC_QUERYMENU</constant> ioctl.</entry>
 	  </row>
 	  <row>
+	    <entry><constant>V4L2_CTRL_TYPE_BITMASK</constant></entry>
+	    <entry>0</entry>
+	    <entry>n/a</entry>
+	    <entry>any</entry>
+	    <entry>A bitmask field. The maximum value is the set of bits that can
+be used, all other bits are to be 0. The maximum value is interpreted as a __u32,
+allowing the use of bit 31 in the bitmask.</entry>
+	  </row>
+	  <row>
 	    <entry><constant>V4L2_CTRL_TYPE_BUTTON</constant></entry>
 	    <entry>0</entry>
 	    <entry>0</entry>
diff --git a/Documentation/DocBook/v4l/vidioc-querystd.xml b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
similarity index 78%
rename from Documentation/DocBook/v4l/vidioc-querystd.xml
rename to Documentation/DocBook/media/v4l/vidioc-querystd.xml
index 1a9e603..4b79c7c 100644
--- a/Documentation/DocBook/v4l/vidioc-querystd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
@@ -62,28 +62,5 @@
 
   <refsect1>
     &return-value;
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>This ioctl is not supported.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The device is busy and therefore can not detect the standard</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
similarity index 92%
rename from Documentation/DocBook/v4l/vidioc-reqbufs.xml
rename to Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
index 69800ae..7be4b1d 100644
--- a/Documentation/DocBook/v4l/vidioc-reqbufs.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
@@ -122,14 +122,6 @@
 
     <variablelist>
       <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The driver supports multiple opens and I/O is already
-in progress, or reallocation of buffers was attempted although one or
-more are still mapped.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The buffer type (<structfield>type</structfield> field) or the
@@ -140,11 +132,3 @@
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml
rename to Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
diff --git a/Documentation/DocBook/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
similarity index 91%
rename from Documentation/DocBook/v4l/vidioc-streamon.xml
rename to Documentation/DocBook/media/v4l/vidioc-streamon.xml
index 75ed39b..81cca45 100644
--- a/Documentation/DocBook/v4l/vidioc-streamon.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
@@ -88,9 +88,9 @@
       <varlistentry>
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
-	  <para>Streaming I/O is not supported, the buffer
-<structfield>type</structfield> is not supported, or no buffers have
-been allocated (memory mapping) or enqueued (output) yet.</para>
+	  <para>The buffer<structfield>type</structfield> is not supported,
+	  or no buffers have been allocated (memory mapping) or enqueued
+	  (output) yet.</para>
 	</listitem>
       </varlistentry>
       <varlistentry>
@@ -105,11 +105,3 @@
     </variablelist>
   </refsect1>
 </refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-interval.xml
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-enum-frame-size.xml
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-enum-mbus-code.xml
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml
similarity index 98%
rename from Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml
index f367c57..a67cde6 100644
--- a/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-fmt.xml
@@ -177,4 +177,7 @@
       </varlistentry>
     </variablelist>
   </refsect1>
+  <refsect1>
+    &return-value;
+  </refsect1>
 </refentry>
diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml
similarity index 100%
rename from Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml
rename to Documentation/DocBook/media/v4l/vidioc-subdev-g-frame-interval.xml
diff --git a/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml
new file mode 100644
index 0000000..69c0d8a
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml
@@ -0,0 +1,297 @@
+<refentry id="vidioc-subscribe-event">
+  <refmeta>
+    <refentrytitle>ioctl VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</refname>
+    <refpurpose>Subscribe or unsubscribe event</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct v4l2_event_subscription
+*<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Subscribe or unsubscribe V4L2 event. Subscribed events are
+    dequeued by using the &VIDIOC-DQEVENT; ioctl.</para>
+
+    <table frame="none" pgwide="1" id="v4l2-event-subscription">
+      <title>struct <structname>v4l2_event_subscription</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>type</structfield></entry>
+	    <entry>Type of the event.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>id</structfield></entry>
+	    <entry>ID of the event source. If there is no ID associated with
+		the event source, then set this to 0. Whether or not an event
+		needs an ID depends on the event type.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>flags</structfield></entry>
+	    <entry>Event flags, see <xref linkend="event-flags" />.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>reserved</structfield>[5]</entry>
+	    <entry>Reserved for future extensions. Drivers and applications
+	    must set the array to zero.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table frame="none" pgwide="1" id="event-type">
+      <title>Event Types</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>V4L2_EVENT_ALL</constant></entry>
+	    <entry>0</entry>
+	    <entry>All events. V4L2_EVENT_ALL is valid only for
+	    VIDIOC_UNSUBSCRIBE_EVENT for unsubscribing all events at once.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_VSYNC</constant></entry>
+	    <entry>1</entry>
+	    <entry>This event is triggered on the vertical sync.
+	    This event has a &v4l2-event-vsync; associated with it.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_EOS</constant></entry>
+	    <entry>2</entry>
+	    <entry>This event is triggered when the end of a stream is reached.
+	    This is typically used with MPEG decoders to report to the application
+	    when the last of the MPEG stream has been decoded.
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_CTRL</constant></entry>
+	    <entry>3</entry>
+	    <entry><para>This event requires that the <structfield>id</structfield>
+		matches the control ID from which you want to receive events.
+		This event is triggered if the control's value changes, if a
+		button control is pressed or if the control's flags change.
+	    	This event has a &v4l2-event-ctrl; associated with it. This struct
+		contains much of the same information as &v4l2-queryctrl; and
+		&v4l2-control;.</para>
+
+		<para>If the event is generated due to a call to &VIDIOC-S-CTRL; or
+		&VIDIOC-S-EXT-CTRLS;, then the event will <emphasis>not</emphasis> be sent to
+		the file handle that called the ioctl function. This prevents
+		nasty feedback loops. If you <emphasis>do</emphasis> want to get the
+		event, then set the <constant>V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK</constant>
+		flag.
+		</para>
+
+		<para>This event type will ensure that no information is lost when
+		more events are raised than there is room internally. In that
+		case the &v4l2-event-ctrl; of the second-oldest event is kept,
+		but the <structfield>changes</structfield> field of the
+		second-oldest event is ORed with the <structfield>changes</structfield>
+		field of the oldest event.</para>
+	    </entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_PRIVATE_START</constant></entry>
+	    <entry>0x08000000</entry>
+	    <entry>Base event number for driver-private events.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="event-flags">
+      <title>Event Flags</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>V4L2_EVENT_SUB_FL_SEND_INITIAL</constant></entry>
+	    <entry>0x0001</entry>
+	    <entry>When this event is subscribed an initial event will be sent
+		containing the current status. This only makes sense for events
+		that are triggered by a status change such as <constant>V4L2_EVENT_CTRL</constant>.
+		Other events will ignore this flag.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK</constant></entry>
+	    <entry>0x0002</entry>
+	    <entry><para>If set, then events directly caused by an ioctl will also be sent to
+		the filehandle that called that ioctl. For example, changing a control using
+		&VIDIOC-S-CTRL; will cause a V4L2_EVENT_CTRL to be sent back to that same
+		filehandle. Normally such events are suppressed to prevent feedback loops
+		where an application changes a control to a one value and then another, and
+		then receives an event telling it that that control has changed to the first
+		value.</para>
+
+		<para>Since it can't tell whether that event was caused by another application
+		or by the &VIDIOC-S-CTRL; call it is hard to decide whether to set the
+		control to the value in the event, or ignore it.</para>
+
+		<para>Think carefully when you set this flag so you won't get into situations
+		like that.</para>
+	    </entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table frame="none" pgwide="1" id="v4l2-event-vsync">
+      <title>struct <structname>v4l2_event_vsync</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u8</entry>
+	    <entry><structfield>field</structfield></entry>
+	    <entry>The upcoming field. See &v4l2-field;.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table frame="none" pgwide="1" id="v4l2-event-ctrl">
+      <title>struct <structname>v4l2_event_ctrl</structname></title>
+      <tgroup cols="4">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>changes</structfield></entry>
+	    <entry></entry>
+	    <entry>A bitmask that tells what has changed. See <xref linkend="changes-flags" />.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>type</structfield></entry>
+	    <entry></entry>
+	    <entry>The type of the control. See &v4l2-ctrl-type;.</entry>
+	  </row>
+	  <row>
+	    <entry>union (anonymous)</entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	  </row>
+	  <row>
+	    <entry></entry>
+	    <entry>__s32</entry>
+	    <entry><structfield>value</structfield></entry>
+	    <entry>The 32-bit value of the control for 32-bit control types.
+		This is 0 for string controls since the value of a string
+		cannot be passed using &VIDIOC-DQEVENT;.</entry>
+	  </row>
+	  <row>
+	    <entry></entry>
+	    <entry>__s64</entry>
+	    <entry><structfield>value64</structfield></entry>
+	    <entry>The 64-bit value of the control for 64-bit control types.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>flags</structfield></entry>
+	    <entry></entry>
+	    <entry>The control flags. See <xref linkend="control-flags" />.</entry>
+	  </row>
+	  <row>
+	    <entry>__s32</entry>
+	    <entry><structfield>minimum</structfield></entry>
+	    <entry></entry>
+	    <entry>The minimum value of the control. See &v4l2-queryctrl;.</entry>
+	  </row>
+	  <row>
+	    <entry>__s32</entry>
+	    <entry><structfield>maximum</structfield></entry>
+	    <entry></entry>
+	    <entry>The maximum value of the control. See &v4l2-queryctrl;.</entry>
+	  </row>
+	  <row>
+	    <entry>__s32</entry>
+	    <entry><structfield>step</structfield></entry>
+	    <entry></entry>
+	    <entry>The step value of the control. See &v4l2-queryctrl;.</entry>
+	  </row>
+	  <row>
+	    <entry>__s32</entry>
+	    <entry><structfield>default_value</structfield></entry>
+	    <entry></entry>
+	    <entry>The default value value of the control. See &v4l2-queryctrl;.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="changes-flags">
+      <title>Changes</title>
+      <tgroup cols="3">
+	&cs-def;
+	<tbody valign="top">
+	  <row>
+	    <entry><constant>V4L2_EVENT_CTRL_CH_VALUE</constant></entry>
+	    <entry>0x0001</entry>
+	    <entry>This control event was triggered because the value of the control
+		changed. Special case: if a button control is pressed, then this
+		event is sent as well, even though there is not explicit value
+		associated with a button control.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_CTRL_CH_FLAGS</constant></entry>
+	    <entry>0x0002</entry>
+	    <entry>This control event was triggered because the control flags
+		changed.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/vbi_525.gif.b64 b/Documentation/DocBook/media/vbi_525.gif.b64
new file mode 100644
index 0000000..d5dcf06
--- /dev/null
+++ b/Documentation/DocBook/media/vbi_525.gif.b64
@@ -0,0 +1,84 @@
+R0lGODlhKgPIAIAAAAAAAP///yH5BAEAAAEALAAAAAAqA8gAAAL+jI+py+0Po5y02ouz3rz7D4bi
+SJbmiabqWgJs475LLCt0fdy4oeN9/QPuEEFZkXVcJZXDXNP5pC0TgGrMSrRMidhA1/uNbB9j2CZ8
+Kc+qHDXDTT2jK3BuPau13vFpdmc/p6Uh5SeYoXMHyFNomEeYiNEVKCFFx8Wz2Eh56YWp2bfnGXk1
+OEhaKnem2rYa6vp3KIqaBhULmsk4Ufc1KTbq4rfbhxkcOQx22limZ4P8STYH3PsGu8pqe439aw36
+eji9qT1rGCpraf5MkQynyJeuG0c73imvLYzuUAwF/P6WTK8vHDdj2Qia8hYL4bF2o/CpmydOXa6I
+uqQNPFepny/+d+cM0qsH8qNGCI8M3gvG7KG8iSJJVoNIp1w5h/C+gSPjgWE9hR0Lqmzp0RFPjLV+
+hoRki2XNPJyCVmy2U6KnHm6WnboRcOPFkS59xqQpEKZRpkDHfi1rdqlXgTMVKVVL7h/cnmi1rtxq
+t27Yn1n5xrySUi81iYAlvR2MN23Fm/nkyHzp9G9iSof3Ps1pE3PmyV2dhaSL1Jiee3/ZjI5Mkhlj
+xDPXGnkClgns1pxV0K6d4rbYF7pRv44CW7Dtojt6f/YxO7hxrrmVJ3/eZDnd4tCjVw+OPbv27dy7
+e/8OPrz48eTLmz+PPr369ezbu38PP778+fTr27+PP7/+/fz++/v/D2CAAg5IYIEGHohgggouSNFv
+1l2HHIRCACehgw9eOIR0001I4YVq8MJIVZItUpJiG564GG75VJaXb5aVthtljwnV1mauyXijVqtB
+FVRoK7Foxi0kNphaYdhYNRUxQMZDWZKd9IXTQTmmFluUDQln5TcqBrnlYEOhaGJXNZrUpR24sLPN
+kC6uaBGWMywERpWISeUZacIE5iZH8OApJ3FrtvhnY5AdR1iZVOw4p1BTZhljlGNG1aijfgIKl4+f
+kNZjoIL2ySOacX4kYlyyfDgooWBSWmikOH15mU5ksfqiqUVqNsySXN7FqZ5jWdoTr7sSqaOtTH6Y
+EajMNZX+kbC53qopDDMuymhprgLbGaTUbgrtm8smCqOqQRYbZrV58vijtzZgNW2TTHZEag7rHFuU
+Pp4aSq6sc9EJa7jinpVuq/Ruy+xSj9KibL0YyRXrXr7WlC+242qrDMJsEYYSVvAiUzGJwg7c7BqI
+GjyiuQ5f7PG/7j57VqkpqryyyJ0WDDBxC29ymr3+YFEzyRpLE5qG91qYYYVAR4hh0B0WTbTRR1Mn
+NBKTDs0h0lErTTXTSyddNdZabw311ET7nLDTTct2tddmn82bc2V3zbbYazMId9xyz0133XbfjXfe
+eu/Nd99+/w144IIPTnjhhh+OeOKKczcR2CYvDnnkkgf+XoTF2eUCs9uTb85554MrVUjmJGDuuMue
+n4566gKyxM+T2L37cNqqz0577QG2/ikpVxEie7LflW578MIPL1vroVdifOy3outkscD/THz00k+v
+ne46ApQT70o2ZWz1RT5Pffji2w4YWcqLkrzvMhNT/Wjuvy/6+PLPL/w/854vr+t58gP+vufySb8A
+CnB8phEBmo7nhDHwz3vQGKADH0jAT4UgVGZQILjeBsEManB6GqKgP+h0vtFtcIQk5KAJpqAa/znL
+Xc4CXv9KCMP2fMyA8fvDDCdYwzbg7IQbwZ0IqeHCGArRbj4UwgvxgDJSHXEfIUQVEpuIqiLycIhU
+jJv+FNO2RCeJQ4kPuuIHUMi+Kb4piFUso4K8yIQsYm8cIlKj9VrQQyiqUH9mrOPm0DgcN8YsXoLQ
+Ix1HAMY/ArKCdiyk5PDYHD+6qo1dlOPItIXIG0XSkJT02yR5qEg2EqyRHYyjzyrnyEqK8oyhTEgj
+7bFJo13SI2EwzCdDhDP4yXKWtKxYLWWJsVu+L5e6rFkv4bezX9pSmDd0XzdgZkwa7SJnFDMNMX35
+TFdGM5jE5GU1o4kn1WDzmXbg2TaFaSZrgvNks+ymOL9Jy3DesGUiSd5wmEhGt5SiHUipp+naCZL7
+6ZOV+WyixMJhT1MKlJ+CFCP2nmexf9plCZZbJWT+3Cm7MJIxSfGcp0WTglGC9CtL+9RERz3aT3pm
+FFeiuShBHcqNN75ToqjkaBhXqr8XJnSPIC0oHP2JU5FqdKQ2g5jyLNerfgo1qDolKTlMmsqTlrJa
+Km1OAmOGCKa+1KkstRBEUdDQpUpqoEk1KlF2ei2fftQoYyVrSFERUK9aQp4tRakmbXrTqtbUpXD9
+oVw1d9UTZLWiXO0jWnn61Y7xca5mJWxhifpXsKr1IWxV6kQPitc1GnZOTcVqFhRq0Lxmdqp6palb
+L5vYxQL0nkA9rGnVgql9FvWoiu2qX9uqVWxVtrNP/em6lsdZ2t6VbE9ap1B9y9qS9jWwwS2uzvD+
+OdmFDjWoIF0tcZ+7VqTWFLjMpS5Ri6krsaoJpt6M2hFLK7bGuha6DAPsqSi7XNSmV73NDa1xVSLe
+1xLUqlaLbViWCF7vJu27ns2pe8k72rCSq6z3XW+B22ve8rZWvuM9LW/xm13LPo2q9mUufScU3+gm
+OMCiDRtukytVEIcYsRuO44I1LNz5RrTCytXvfo/G3wnTNsOM/S98S+zED1vYwS0WsWxxGkLMbjXF
+DWbvhV185CS/GMm9ky6KOywmHM/xxz7WMY97bFbn3vjENR7ulSVM05QumcljXnGMabwnGysYylO2
+spG/TOUqo1fLa35vl4ksZ7uyeMRmrq8akav+5OI5+c5sFlRaezpgA/P5zXDGLZ05bOc0e5nRD/Zz
+mfscHWYiQdNKAK6n4wfAxSTi09wk5zipqctunvqct1T1L8P5i1GLLtTsdMRBrBvrHNoE18fEL6dH
+CexgC3vYxC62sY+N7GQre9nMbraznw3taEt72tSutrWvje1sa3vb3O62t78N7nCLe9zkLre5z43u
+dKt73exut7vfDe94y3ve9K63ve9t7SBkNdH47re/9Qq6CAP63wQvuGZ2mYneFoPWBm+4w8VUWiMB
+5IIPr7jFX2a/YCZ8zxfvuLnf1VB5QcnjJDd4YTKucN3xuuQsb7nLXw7zmMt85jSvuc1vjvP+nOt8
+5zzvuc9/DvSgC33oRC+60Y+O9KQrfelMb7rTnw71qEt96lSvutWvjvWsa33rXO+6178O9rCLfexk
+L7vZz472scG0vllD24rZzrW28bbtcl873N2uObUfqkQzJFaJPAO9Fm53W34/mcbO+7/t9j1ksfzY
+MiUO+DaXDPCLT9VpKr8yZnpQDM50JcmkyTOdNT5Enx8mxhAPaxApq/CULxjFV9S8kT9yhWts0zL4
+JVnX44uigl1481Cf8KsI3Kf+Er6biMXS18/+gy2JJfBzFw/Mc35U0NcXJxAh+4A1ENC69xdoER38
+34Mf+sZvF/5OP3yQ+QKAt8+14Z9/2dH+H3dnh4d/Als5f1MzMcdsCoj5SfwwqXVb/Mca6qd9WBaA
+R/J+1qddDHeAUZZy85c+mOcp/ndc5QMqGyMawrd5ACVx/8dYKrcsFQg7DAhEu6NAG7g9q3cU3RN4
+zBJV9jdwsXM/GQiCRuZWNWh7Msh3QmaAhoYSIyhja1ALbQJ/obM+L0iExvJry8d8LpiAuPdSN7h9
+3VOD3kdHW1AVsOOAxEclTySEIIQOHViF7XSFZQgUVFiGj8CCYpiGR+g8Axgt24c8Q9gpvTJbHjZg
+IjguFJQVZChbH2h/2rODJjgqxieDGTiFevgyFKWGAYOBj8gtVPF564IpLRKJgziAgAj+ieFniNxX
+fUo4LPcXhn2YEqMnif+TMYNHgKoWeTTYTGoifZzXeAsoivpXJ2f4PaHHik7oMZ1ni4yIi8fDib+I
+gen3g6pohE34gMa4cbO4ixJkh8m4d0HYi5Lniq1XjMqojcqgd2AmNXVnd3g3juRIYXT3dnGXjuZ4
+jl/zjboVjuvIjvB4d/NoUOiYd+qYj/Z4j+6IQXNXj/IojuAYkAK5j/yoZwV5kAa5kA2Zdg8JkREp
+kRNJkRVpkT73ZxwnjASpjwCJkIP0jv3Yke34kSAZjww5kPQ4kiSZkipZkhOkNifpkOWIkjQ5kzZJ
+NqyXi9uYeIrXho8TZtTlCjnEMfn+Z07jN3n3hIuC1ZNKeY2JiD6Zs0gC5iWzliav+Inv51vKx3wo
+WIrTV3uh2IqC9zjZN5ZL2DBgSZW+iI2GBpTT2IwmtpajqJSGIY232JRbuQ1myZZoKZZZmTt8ySV3
+ggapWEHRAJjU2JaL6YVMKYepMpe/GJlH6ZTI2Jdu6ZRcuZGQBJePqTCTmYRG2XyO6Q52Ui5QuJn7
+sA4amC2XOYeJCWukWVugeX2y+ZeiGZSO0ZrncpdGWYKwOZq2mV94SXwzEyymCULIo4u0h5rt95ZD
+uZuuyS2xSJuNeZZ3WJlhBmRQBAhCGVrLmRfGCXF1yTyg2ThkQlZ5eJ3lWYipOZ3+UKmd/uSDrwmf
+ciSY76kuacmY+Hk9lWmEwumJ8BmDSBl9/zKgpEmI6CkjGcOM/MmN3QicnRmX0OBpuvmW3GlD4jkr
+QEmUFuqfHXokUjkPGtoYDSqd+meiE+qMehmf0ZmQComTHtmScSWTMWqjHPmSMPmPMhpRGemjMYmP
+N4mjM0qjMHqjLkmkL5qjIPCjLXqhLqqkSWqSQXqkSFqTLHmlVpqlIrmkF+mlXwqmYSqmY7puiEim
+Zzogj4GEaMqmAIIQmtmmcTofbyqhcqp0GSlD1gCndvpvuqYldSU3dOqkfJpun/VFt1md5sFQjOKn
+hFpu+dObKVMXUnSMx5AfDBX+agfqqH0qQQtkCrMZf81gqBvnmemBTZtacuCyp98yFbyAD/NJSLiD
+p4dKoSuHqu62qJHqlpTYJ5AgcvKBqbfqclroUOUZBynoFP/pHrMqrI8KL2CErB1YQPHBrM06bjwJ
+lxsDJCkkqgD3WNZ6Ro16lT5gq0JCnBPGrfs5SerJcaOKm+BaH+4KC5kkZoR2nTTBrixToKCESTwK
+r2mqkatySi1lr/uJr7nFpJ6kooMWpf8KsHpErwQraed6sIAKLez6SQHrsHAjr6wQsSpGMzzIqp0U
+ZfwKR9W6sfzRsarwsXnWrYDJryurohjbWSibsvohs5MmaBI7se45qQhLq5L+YrIiZLM3ix85i2e/
+oRMHJLJesmfoArVPyWqldnivNrW1hGqvhk5Xi7VcW05ei0u9JrbKNLbS8nioyE1bC7bAtLYIt7Xo
+BLfmBLdWW7Vz20vq9E2mFrZ1u2qihrcdRHq19Vj5CoaFVqIMC2kAdq/U57KWqGh0hWBJu2WG67Q6
+y11AO6WEq6O71WjIhbRSBaubG1OVZrH7R7lAhLhyGWmLO4MHtmOUhoDqhWaJO7mru34YorlBC1mV
+Frr8RmWf61K9q7uaRaO5K1m26xKzq7qKa7CM+7qu27nadVaWC4GnCxXKS2HG+1CYm7nHG717FVnC
+Syuje7mlq0XIK7DUO2T+6Luwvhu97gu7iya7qVu97Fu5khtZ5ju+2ru94uu8v6ux1Oe/BUG8ema8
+A+y9T8Zg9suZCGqZjtu4pfm4wUu/68u8FqzAFwa8H7bBjgZVyAi+vDuo8xvAIVy/F5y++Eu7dZaI
+wym/sQvDL6xc2IvBLFy7C6zBJfxECPV9BIZe+ru/CZy96DfEWHm/DDxGFYyZ1luqcfa+EPy8MQy6
+SsyqXLbCPeti5fq74gq62JWtSMTFwavFUgyPFShlKVxkV7y8ienCkPvEEhzBEkzDS4zEBaq+ZXxp
++RtopEs1MQYwCIzAQJzEZ1zFBPq/8evGiOzEWUbFR4zChZzG5bvHkoz+aWRmyZRsw5mMxRl8w51M
+sYcMvYrsZqFMwiq8xpp8yptcyavMynw8yXrcyqksy7d7x5D8yA46ymScyzKcyKUcySfsyWpMy5Z2
+yZjsyrGMzOBoxlYcsrXsyMHMum28yKSsyz8cub9cw8Kczc1MzK+szHl8zMX8zXVcuNh8uIT8zJ/c
+utUsvVHMyxTszA3MxOWMw8mMx+BcxOIczsY8y9s8zOZsy9DcvOv8zrvcy+zcgI0sz+RsugBdvPic
+z/Z8zxmSoqNT0aq4a1JiI92Q0bm2aqeqt3cb0qk20q1W0iYttbR4ax3N0RsNBBdNQ114QjCNQzLd
+AjRttDmt0zvN0z1u7dM/DdRBLdRDTdRFbdRHjdRJrdRLzdRN7dRPDdVRLdVTTdVVbdVXjdVfVBkx
++APSnNU5bZaaCsVfPdQnR8TkJwlnTdZAnSwXJIidutZBHbhrqpqnuKpx/a9c3RdvndZ43dO+pCSY
+E9gqF8bNWgAAOw==
diff --git a/Documentation/DocBook/media/vbi_625.gif.b64 b/Documentation/DocBook/media/vbi_625.gif.b64
new file mode 100644
index 0000000..831f49a
--- /dev/null
+++ b/Documentation/DocBook/media/vbi_625.gif.b64
@@ -0,0 +1,90 @@
+R0lGODlhKgPIAIAAAAAAAP///yH5BAEAAAEALAAAAAAqA8gAAAL+jI+py+0Po5y02ouz3rz7D4bi
+SJbmiabqWgJs475LLCt0fdy4oeN9/QPuEEFZkXVcJZXDXNP5pC0TgGrOCqVMidhAVdqVbLmx73Wc
+FXfNabGFzfbG3Rz0bDO/2G1hzJ7o8ceT56dB+Gb4JciD16fnh3VI97bmOCE4tyhVUSbHKOlg1xnp
+6aWFKDfaecrqQlrK2vqK2bjImPFaiLuKuxvY+2HLq1tniHcLzFmWy6mnitxMeWs5iaZo0xZhTahj
+rdzXHa3m6Eod+h1+LW7MXpx83P7962y+ju4O//5oGr8PHUvs36VjoCBsujTsxp5t0MIB1MZLYb07
+CBt+QlWRHz/+Zto62NLYD+Ouj7Q+ZlMj0J80kCr1iaSHT6WmeAXPAXOVzNs0hw8fHAwzkeLATz9E
+xVo2qCa2o7AA9Wz5cmXIgFAhKu2Yb2q1rFSrDmUZFeUgrQaLdhWriFZKGKt6LNTSlopXthevrIUB
+d9rSp6FGcbnLwCRYe2ELo+VK+CxEwF9XkoypeCtZn05dTiqlNupMxnyWxXkL17OVtHz7loMTdO+4
+pGsMsz0dKbVcyK7LXsWbyKSweTA95qatDHho4T7TqqsdWN1toaFbExNMHMkTzimgR2cSZfpgI9qt
+T8aePbz4IQebeLcsZDz56ecjv2g/9z37+fTNd6+vPb/+/fz++/v/D2CAAg5IYIEGHohgggouyGCD
+Dj4IYYQSTkhhhRZeiGGGGm7IYYcefghiiCKOSGKJJp6IYooqrsjidyrAh9yL+K2nng/31WgjjtzN
+mKOO8lFHxhlJxRjkkEY2tloWy51k2mxAVoaQQkImRiRuIyEmD5ZIomeVYMLIZhMkS6rWm4vJecZl
+cWBsRomUz+Vlymg4bWflYnGWo5FOGZ02FphPYmbkmHQmRxRSgzJXpntl/UlmcIca5ItvilJJx2OS
+TkrZo5k6CgemfBDFKJPF7ZRTIZsMgxUip4qKKFN5UropSKD54xasW9p6a65VBiYmb/dc2qZuwMaH
+laXvZEb+FbKPCKpkm68KutBoTshZWpN6MRqtm6H+8ZmTulabqplhXikuNtBhgqqnM6SLa7jE2nZd
+rGzK5CeUqMxJq6l2YavvTn6yGVG7zGn77aZgvOvuruvGexnCndXLq5YCC2Vsmg2LUzGcTSm8r7fg
+0pUKxMgwdOdY/O4JaMkFf/pqyiv/Jau9CY/asqatOlwnzuM6JvHMOsPsZaQZ/3zzV0NfdnS4HL3c
+KsBZpnIk01NCHbXP1o4MsSjgyAzp0xsddzHRHqOz2289d83wmb46e/aibauZNhXGMWuz3KjNG6Vz
++fooHY/p8Q0ejYDL6PeO9hX+4+DVsRr4DjByPMLjE5v+ILnUJ1Qe9t+Cb855j4d/jrnVfSuOQuii
+N+5555qrbjjrrTt+Y4uyz0577bbfjnvuuu/Oe+++/w588MIPT3zxxh+PfPLKL8+87rWGYLqI0TdP
+ffWwM249oXKDgC/y02cPfvgkkPJ97t137075HKovfvvuQ1KXh9zKJ6V37A7P/vv6739Oa0BFnoRK
+QG9+2PlJMLDnu/zxb4EMxJPJ/DLA/sXvF0EogsgG5hQDkupeCOydAhkIwvcdAYJeqYdfymOMCvLK
+Swe7yKqgkLU4dZB3AaRbCG8YwhrOEGazUaHJNuKboqjQaRBMSDrqBkOu4W9uTAQbDp8IRSV2jFtm
+2Y7+thwIDyzi64VIBKIMvQip+/Gwit5Tkw2jiMbsGcVRPfyhBTdGq7gY6ovoG1UL6ximJSwtVLjT
+YRr/mMZZFctJRZSgLswiR73gMWcsqw0Jx0a8DwJyksAj4CCjRr7T2aSCiQTiIiMGsvg8UorBkyQl
+T7k7S3aNXQJEm2lWxcl9bRGFnWFM2TAIyuOZEpUpOqNHLhgMX9ahXqq02xZTQrCdRQyWdpolq+Yk
+uTdqMoG8BOEnZSSsHYLRRmukFAnFGKOA2ayVsBjhNkUgTVcab5fVNNE1F5fNk33wnY2y2iOBWbQ2
+8rFj9axLNBmZy3W2c4H0vFwXcTmUeXaxmBmUlf3+LkmSdJprn5kb50AvWruCUu6g3gKNQrtZmns+
+dJUU/WE/6bjRgAIUoyx1J0e599I0eNQ+INXVPaEH0ZTeAZzE2QI7WwrU7Hw0KzNdT00rOkqckjSm
+9jynUvMJyaBKVX5MDSJN9jHUj+UzqTCdGtWcOECJyAmf8CqSbWDTxLSiVa1MZA1b5+bWt5ImZHI1
+Dj2YZddgiSyvel1rXc3w17bSNbCiIWxhDUsGwyoWbNdYrGITO1jCJjatRXIsYs/gV7betbJkhZtM
+ndqChkaPJ6fYTdk2g9pyQUmVrJVJQDS6Qnak9pBX1RxXxyfa2o4LmoG7LW6nVdJjgfa3imzc/Ez+
+K9ubKNdiuWytSJz7XKbCliKzxapuE+fJ3k5wHVOoX3AB4tvIAYKnxEUp4Yp7Xj5Od6LLtS5tmYtQ
+8Lo2uq5Fbns5+N7Xei68T82ufl3J2/Tyt78Bxm6BS5fb9HJ0vXI57X2jcUv50pe7842uffOLX/f+
+t3UDPmAS59Xd8X63MR32sD9tO1zxfti4y0phcjEMYdV+dsISpnB9XfzgVuS4xgberk79S+Pdphid
+CRbwkEML3KpKmMH6OC6OYaxjKGtVNdDlMYn1e2ENZ3jLQdbuFxe34grL68hdRa+RyaviQo02g51F
+kpN74WApV0rGFumy0sQs3yxzOcpatjOY/eX+Zbols06wCXSbrwzWPyt5w9hdsHQfHVM0L5POMfPz
+mC09Zj3HWM6XZPToFo3nT7Nv0F7e3KhJ+WNHa5rPe04opUkN4FDf+cZwfnGfWY3pH59am2UGda51
+PZ5dj7glb+4Xp5d66yl3VNax/nVzHx3nZM9ZuCiutrV7vN9gZ3t1xW7xjqct7YoK2dlUZnasV+3q
+Y2cqwsL2tY2vLerrDfu68ea2t40dbmS32nIzfreVkYblJ+d73d8GOLxLzeFtHzzhC1e0qgW+705H
+fJrlJveyLb5sdIN74gSnNsM/DvJ6N1zk2H5dt1Vla45v8tWofjbG+01hjUt80wO/dMgRXvL+nOsc
+CHM1Qs/fw9fhkEtMmrBhovMW2Mn+Vel1Zbpcnf50r7KN6CMpOj6DjoSfZ/3o1dG6Erz+da5Pdexk
+L7vZz472tKt97Wxvu9vfDve4y33udK+73e+O97zrfe9877vf/w74wAt+8IQvvOEPj/jEK37xjG+8
+4x8P+chLfvKUr7zlL4/5zGt+85zvvOfx7sNrXfzzpC89gyQB6zqbfvWsL9Bh7xgyNbd+9rT3zxwr
+3aly1n73vAcdMw7rxt4Lf/iE4+LX2rJH4it/+bLNvSI7JXbmS3/61K++9a+P/exrf/vc7773vw/+
+8It//OQvv/nPj/70q3/97G+/+98P//j+y3/+9K+//e+P//zrf//877///w+AASiAA0iABWiAB4iA
+CaiAC8iADeiADwiBtoc4n+Y6FChvFYg6qaOBG/g6HNiBq3OBE7gua1I1FCd1JKhsXkVa4jaCPRRD
+XoOCKUg1MMeCtVQZ0RdVZQVD/+I1dzImWsMT0AKDUmeCR3I3HHOELXdSahMoP/g0n/GCUdKETvgn
+5MMnJ3MYX4VFRQgoUChIboMmybdSIHOFYqhSfQFoJlWDQGOEYjMLs2A5b7iC6kQzaCJ6ayhLX6VN
+JONAgHVUdSiHu2KFPoaHD5QykrZDsYEq3VQSUzQ5qzUyMniDOTiGNoeFGPE8/DZjQjj+XzhIiXfm
+ibymegeFLBqkiZFYM4XoMXqjiqNHiskSikqIKIX2iDA3K9mSJ9QiiZmAiq3YhrIIjCoYjOrFilQo
+dGamibzoMlxoViozBrhIg8yojDOYjM6hi9XoXZcohf/whVaBWYi4LZXQh7WYhNsiil9Gi6eIe4lY
+KsP4Um6yV+04jKVIV7U4ilVIVKkYKzXGUAZHS3QoGbEniRv0j/tYWpmojqT1h+5yTANZaY5Whc8g
+Q8QEJ/AIjlrTi+aIMkn0M7lgKAupPQTTjWiIexfpDBZhhp+4PQ/Zj2TYUNpYh81CkRsJezKYSUt4
+hi6piDBJkuOYkji5ks5nSUA4JZz+uI1KMpPHyBIjeTVqBpKvcYNRmCTRCJBNmYtPaZV22Ip5cHv8
+xpVEWJVQiZRMKZakYZRS+HNkyYRaqJYtaIRS6Y0zGI/zRmlEJoIKFoIeaIF6mYEg6Jcf+JeNlpd/
+Y0qFGTsY2JeCGZiKCZiNuZeO+ZiMCZnnZZikg2CWaVCYiWSaWV6I6XB8mZiRKZmiGYGlaZqniZqp
+qZqryZqt2WuDOZl4uZikKZux+ZmzGZq5WZu2mZmc2ZueeZm+aZfC2V+wyZupZpy0eZu4uZzHuZlE
+OYUK85UlaJA6uJTSuTXU6IvTeJbwpUw9CDluKTZAWZ3N8TZiWZdulZ7UaY9s6Z3+NqidDjmNmFiR
+ntAtKRiI9qknh+GFgoh842iTqvCR7QmWDmmI79mT6hJKCgpVBkpm5RmewQWODRqSP5mTMWmhFLow
+XyOPzdBCC/VfBVmJBqOS5BlfIPomJeqOGvqd40mX71gL53km8RQscdOi6siRCHqiOMqNDGouwCSi
+TUKCSXmUYLSfRzmHYYmeD3mK98meI+qLKgqhUbqWBEqIDpqhUOqS63mOXfqkPJp6SgpgF+RgTnNv
+6Uil8MiOKcpr9AhHzNgsUjpiSZMRXGqidzqCV7c2ERqkVLqicroXdEozb5qQZNSeikimiSiROEGk
+YMhm+FifPTo5v7dPGNkyWTr+pzJ6oQ6ahy76p16KqSy6oYLqp6DqpTB6qqU4oeeIkBjzhDv5iNMZ
+n1NapUlKq/DplOT4P1+6qTwqXbEoqp7lqakao5qKqz66klwqTFQkWJAzV0Z3V31KosT5msmpm7up
+nMH5OcCprdaKrdn6m9yqU5W5reK6meUKms05mteqruwart7aru46rncZr99qr/farelar/mqr+/K
+nPvqr//qmgNLsAVrsAeLsAl7O8ansNP3U9ZjKaHasID0sNxTsc3Dbi86sfxzaPzRsZOUse62sR9y
+Ho8BI+RUp1KhhlMVshc7sgMSG8N0pUGZi8HET2KRYUxGSS37sh60jMuCZgD+Sqgn6U+xtLLTJqIS
+5bInEkD7+LE9qyASQShBCBX3g0j66KHFZbRDS3CkhkfQtLQu9UqGKrJQmyD+s1O1MpciRrYn9opm
+xkrPMkO0VEVqe7QNdFlm2yIFpoxusap1ezO8lTWdFJVu25U3qjKpeDBhWyI1BKx6CyJJJWltyahW
+dCrRgowf9kKH26s3qXrSAkV+BLm086EvKaYNirIZpyqlK2Lsxbmiij5xG7qjKzwh9oxA8k8eCmtf
+m10+pTFXyrgkEry0GyDd5Q2ykbtmtE1DtFN2YUGY2ranyjzDq3ePi05PO3U+IEzF6rsV8byg25mT
+BpJS+0aryqnTe33mC1P+WUVv+iYE6otUMzss4utNpuu6yGlN6auxWWtUMbFGWZW8S6Gza1hiXHJg
+w4lD1Jt38EtBNOW/NMdN+ysqBYwwFDwXB1ycxCsgDGxV/du+7ssdHAyhFtwuJFy/Ioy4GuyxEjwQ
+7OtpMxfCLEwnJvwyNGxTD6qjKkwjLvy++QjBPVy2UmTD0zTETYXCWqrD9MHDMexxuMbEAdxGAZwJ
+sNoCQOGH2MtZjhVZSWdZr7d0W9x00cqseAV2Z7VXz2pZYNx0XRxXSafGXRzGUwjHbwVZcxzHscfG
+39hEWWzHalXH2/saYsWrxYqSMnxxA6xyhoRviTxpyMqkV/Zy9+iPEMf+v+q2cqaGw8BSxEsGaZyR
+jWsWZmdmyM92xLOGyD9cyfdWc7iBN5Dsb678b6ZMyaWVcqjcY6XcbKfMySAGiqO8iUFMaJncaxh8
+rpucboucyoxMXTksybP2ygZnYzIXRrXsxLfsy3Wmy5A8wGH6Wbh8admMaNesusCMS+AMw7RcawUH
+wgm5otzscs8sy+mMzNW8cSjmzeNmzrkcaUr4yYFGzhh0z738z4c80PaLcvK8yo08nu68o84cy/qM
+0Adtyay8rcRcXsY8yW56buKsptPTzwkX0C6Xzx03zy1MzcccngxdcfDcbNE8yyatziSdbSFdzgX9
+yxqdaRxdZIpm0b/+iaY+PcgeJs2UEW3KjKeQGMmPDM2cHNHJbMv1DNKAbMpYLNKJ2kH1I9W5TNWk
+nNWwTHJ9M9SKnNDL7Mgq7YpevdTa/NJuUNRPjXNvbWQKt3NwPdc8nSNhjRdtTc9wqtQOjdZ+PclN
+jc4TrdBy/dV0bdcjp62SZNYEdtdr3RF6jdKH2s6VLYqN/cuCDdOETdYX2G6f7dmGfdg3F9c7gtex
+FdOXvNCWrV6sDZF3KNGqbNT6FNqKDWyiXdqkXdeL/diazdYnDdXsfNmuXWVq7duRDdznPNqJrdvM
+vdu8XdG4DWan3bypTdFlTdzmNm4ufdzTbN2FbdvFLN3OvdzkHd7RF93bJf3b393ZKZ3dSY3Z2AzZ
+3s3ZAhzd551mNv3Ozw3U5lHGpfPfl3NGA351Rmfgj6XHd7xYUKdZCR51rGE2vVJ1E04eAU45Fl7F
+1htMGv5LHN7hXZ3EIS7iI07iJW7iJ47iKa7iK87iLe7iLw7jMS7jM07jNW7jN47jOa7jO87jPe7j
+Pw7kQV68E+EQhqrAQs6aZmirzYzkQC4aAmmIygHlTS7kP0G3gRJ8VB7kAGCRbQB8uqflTu6Ci4jl
+ehjmPs7laf58XB7Fau6DR56aBQAAOw==
diff --git a/Documentation/DocBook/media/vbi_hsync.gif.b64 b/Documentation/DocBook/media/vbi_hsync.gif.b64
new file mode 100644
index 0000000..cdafabe
--- /dev/null
+++ b/Documentation/DocBook/media/vbi_hsync.gif.b64
@@ -0,0 +1,43 @@
+R0lGODlhBwHJAOcAAAAAAAEBAQICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0NDQ4O
+Dg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEh
+ISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0
+NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdH
+R0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpa
+WltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1t
+bW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CA
+gIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOT
+k5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaam
+pqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5
+ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zM
+zM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f
+3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy
+8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///ywAAAAABwHJAAAI/gD/CRxI
+sKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bN
+mzhz6tzJs6fPn0CDCh1KtKjRo0iTKl3KtKnTp1CjSp1KtarVqyQBYN1aVSvXr1C9gh2rVOxCsV4B
+mE2b0GxDt2TjtnWo9l9du2rrar2bl+BavQL3ApZLeC5du3j77g2MF/FAtIv1AoZb+Gfey5gza97M
+ua/ByJ4XI8b8+PHl0ZkrE6XsuCDr1xD5ip7d2m9pv6IZqxYK+zPC3g/T0mabGLdk4YEH7wYK3PZB
+yqyXSw/++3l139OzS4R+Hbtr7eCp/nv/bp18+PMKuZcfj7792fXm47ufz/52fd308zu/X3u/fv3N
++Sfgf/MFaJ98BLpnIH4IJojegv0d6GB7EEI4oXYVdnfhgxoOyOCG4WXIH4jTidggiSV2KOGHKGa3
+oIUtqvaiijEuNyN8NUp344g5EqYef9H1KNePJwYpJFlEehjhkT7iuCKLTMZl4olRgjWlklV+deWT
+WWpJ45JgdrnVllCKOeaXMJrZFZpfqmkVmWG6SRWcRsoZFZl12hkWmzxemCdXeAr555lOgjnof4de
+tSOVG0KWaFl3GVponH52ZumlmGaq6aaY0pjmhJppmRqQbTaKm6gewgnio2uSOumq/jpO+qmDrE5F
+p6AtSZZeSrf2WOtEoZEmm2C/Astnn6CapKtjbClWZki95lhsbLcRtxmlHkVb47TBWcuYcGvxeiyj
+fp7kGbOJEZscStrGyK1T7bb4blPxojgvU4Hiulu+vto4Lpck3rvUoljCuq+npZp6cKGz0uovwwmX
+u3CRESc7sZINJyhwWbJW7PDFXGZM4MZI1WsvyCF7rDHKZYqMKMuSvmqwS5yOypHJAcP0K8k4z5xr
+RTz/C7DPLO2crdDPEr2S0R31rDDNQB/dMbISQ01R0FOT+/TPV0vtqtZVc21s0wjLLONFJG8XNdkQ
+y5z2UNy+TW3XbN8Ho9xBxa3z/to3lz0i3nljBPhbfG+UZMoqG5db2+KJ9O7gDDHd99dUstpscsgR
+x6CzqC0O0uN70z05xVlHdNpwgvUHGWrFef5RppGHPjawNddue3nB5nYufsKmu/vrhL/3kuRqq1Tr
+6pd/G+6HymGLdvC7Dl+46cYD7aywoSleXGOtj5RnnZALP3vx7Bb2J/iyk6++subTZanz2ZJ2te2R
+st8+9NaFHx/x1Jff5GFz0Z9/+Dc3c9EnSK4ryfLG1z89GaY6AjwQARvnQLfBr24XpFrizGSk+tlv
+aOJbXwULxj3/gTB6DBwhCD2oQLBtkIR66mAEVTe9AqqQhCzMigvNhsIbrnCG/m6ZIAB9+MPqwfCB
+IryhDI14QiQ2kIiUyqH3dqhBHtoJfSZs4gu16CYsGpCKYDyinLz4QS5W8YwcjF0WkxbCJxKRjC0M
+oxnlmCU46tA19BPiCO04xZjM8IBq/GL63hjIMloNitiS4uv+aMUxRk5/ihQXIhMJSUaiUUzgq6RM
+LEmhR5qLk2LsoieVBco5YnKUCiwlG2OIyqyoMoNpPIsm/TjJRMKya698JYZiB7kELq2W6OvlLT8H
+TF62MJfM+R3+lnnIAB5zk8zBHOZks7/BqEuXwXwmLS1DzestDnmNud5MsqlDZPKGWMkzT+9CBc33
+5PGd8IynPOfJwkilLp37/gniN8dZyDgOcienCadudnc6anavnT30p/SKokvH9fOO/+RmqxIK0YUi
+EosBNVz2tnnRR9KzUxyFYjAzqpHehZSQbdxYEBEqUhcVM0WTbGhNZBor+7xNj8SMaT7TJc1Tgcug
+Bf2LNZnlKODp1KYCbR64ujcZ0OBxe5FR3jAfqsSdNiujucMnPnl3uaxiraNI3ep3hro8161uNLbB
+G00fNk3abG+aAiXqcKqlGG8Oy6hgLang+HnUjERyiBFV4VpZitKa5rWEgKJjldgpKs5d9KOQjeym
+XkrSMdnzpYatpWY3y1l6NXGB3RlsZ9eDzp7ydKmnW1dAlTnaQ94zruEkS2tUnfra1iIUdRvlHueu
+iS7N2daic1VncEEz3N/6MbVyNU1TV0tUdL3VuF6aKnQhJdrpWve62M2udrfL3e5697vgDa94x0ve
+8lIkIAA7
diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media_api.tmpl
similarity index 89%
rename from Documentation/DocBook/media.tmpl
rename to Documentation/DocBook/media_api.tmpl
index 88f2cc6..4e8e898 100644
--- a/Documentation/DocBook/media.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -8,7 +8,8 @@
 <!ENTITY ie                     "i.&nbsp;e.">
 <!ENTITY fd                     "File descriptor returned by <link linkend='func-open'><function>open()</function></link>.">
 <!ENTITY i2c                    "I<superscript>2</superscript>C">
-<!ENTITY return-value		"<title>Return Value</title><para>On success <returnvalue>0</returnvalue> is returned, on error <returnvalue>-1</returnvalue> and the <varname>errno</varname> variable is set appropriately:</para>">
+<!ENTITY return-value		"<title>Return Value</title><para>On success <returnvalue>0</returnvalue> is returned, on error <returnvalue>-1</returnvalue> and the <varname>errno</varname> variable is set appropriately. The generic error codes are described at the <link linkend='gen-errors'>Generic Error Codes</link> chapter.</para>">
+<!ENTITY return-value-dvb	"<para>RETURN VALUE</para><para>On success <returnvalue>0</returnvalue> is returned, on error <returnvalue>-1</returnvalue> and the <varname>errno</varname> variable is set appropriately. The generic error codes are described at the <link linkend='gen-errors'>Generic Error Codes</link> chapter.</para>">
 <!ENTITY manvol                 "<manvolnum>2</manvolnum>">
 
 <!-- Table templates: structs, structs w/union, defines. -->
@@ -110,6 +111,11 @@
 &sub-media-controller;
 </part>
 
+<chapter id="gen_errors">
+&sub-gen-errors;
+</chapter>
+
+
 &sub-fdl-appendix;
 
 </book>
diff --git a/Documentation/DocBook/v4l/bayer.pdf b/Documentation/DocBook/v4l/bayer.pdf
deleted file mode 100644
index 905e60e..0000000
--- a/Documentation/DocBook/v4l/bayer.pdf
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/bayer.png b/Documentation/DocBook/v4l/bayer.png
deleted file mode 100644
index 9b15fb2..0000000
--- a/Documentation/DocBook/v4l/bayer.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/crop.gif b/Documentation/DocBook/v4l/crop.gif
deleted file mode 100644
index 3b9e7d8..0000000
--- a/Documentation/DocBook/v4l/crop.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/dev-event.xml b/Documentation/DocBook/v4l/dev-event.xml
deleted file mode 100644
index be5a98f..0000000
--- a/Documentation/DocBook/v4l/dev-event.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-  <title>Event Interface</title>
-
-  <para>The V4L2 event interface provides means for user to get
-  immediately notified on certain conditions taking place on a device.
-  This might include start of frame or loss of signal events, for
-  example.
-  </para>
-
-  <para>To receive events, the events the user is interested in first must
-  be subscribed using the &VIDIOC-SUBSCRIBE-EVENT; ioctl. Once an event is
-  subscribed, the events of subscribed types are dequeueable using the
-  &VIDIOC-DQEVENT; ioctl. Events may be unsubscribed using
-  VIDIOC_UNSUBSCRIBE_EVENT ioctl. The special event type V4L2_EVENT_ALL may
-  be used to unsubscribe all the events the driver supports.</para>
-
-  <para>The event subscriptions and event queues are specific to file
-  handles. Subscribing an event on one file handle does not affect
-  other file handles.
-  </para>
-
-  <para>The information on dequeueable events is obtained by using select or
-  poll system calls on video devices. The V4L2 events use POLLPRI events on
-  poll system call and exceptions on select system call.  </para>
-
-  <!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
-  -->
diff --git a/Documentation/DocBook/v4l/fieldseq_bt.gif b/Documentation/DocBook/v4l/fieldseq_bt.gif
deleted file mode 100644
index 60e8569..0000000
--- a/Documentation/DocBook/v4l/fieldseq_bt.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/fieldseq_tb.gif b/Documentation/DocBook/v4l/fieldseq_tb.gif
deleted file mode 100644
index 718492f..0000000
--- a/Documentation/DocBook/v4l/fieldseq_tb.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/func-ioctl.xml b/Documentation/DocBook/v4l/func-ioctl.xml
deleted file mode 100644
index b60fd37..0000000
--- a/Documentation/DocBook/v4l/func-ioctl.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-<refentry id="func-ioctl">
-  <refmeta>
-    <refentrytitle>V4L2 ioctl()</refentrytitle>
-    &manvol;
-  </refmeta>
-
-  <refnamediv>
-    <refname>v4l2-ioctl</refname>
-    <refpurpose>Program a V4L2 device</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
-      <funcprototype>
-	<funcdef>int <function>ioctl</function></funcdef>
-	<paramdef>int <parameter>fd</parameter></paramdef>
-	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>void *<parameter>argp</parameter></paramdef>
-      </funcprototype>
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Arguments</title>
-
-    <variablelist>
-      <varlistentry>
-	<term><parameter>fd</parameter></term>
-	<listitem>
-	  <para>&fd;</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>request</parameter></term>
-	<listitem>
-	  <para>V4L2 ioctl request code as defined in the <filename>videodev2.h</filename> header file, for example
-VIDIOC_QUERYCAP.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>argp</parameter></term>
-	<listitem>
-	  <para>Pointer to a function parameter, usually a structure.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>Description</title>
-
-    <para>The <function>ioctl()</function> function is used to program
-V4L2 devices. The argument <parameter>fd</parameter> must be an open
-file descriptor. An ioctl <parameter>request</parameter> has encoded
-in it whether the argument is an input, output or read/write
-parameter, and the size of the argument <parameter>argp</parameter> in
-bytes. Macros and defines specifying V4L2 ioctl requests are located
-in the <filename>videodev2.h</filename> header file.
-Applications should use their own copy, not include the version in the
-kernel sources on the system they compile on. All V4L2 ioctl requests,
-their respective function and parameters are specified in <xref
-	linkend="user-func" />.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>Return Value</title>
-
-    <para>On success the <function>ioctl()</function> function returns
-<returnvalue>0</returnvalue> and does not reset the
-<varname>errno</varname> variable. On failure
-<returnvalue>-1</returnvalue> is returned, when the ioctl takes an
-output or read/write parameter it remains unmodified, and the
-<varname>errno</varname> variable is set appropriately. See below for
-possible error codes. Generic errors like <errorcode>EBADF</errorcode>
-or <errorcode>EFAULT</errorcode> are not listed in the sections
-discussing individual ioctl requests.</para>
-    <para>Note ioctls may return undefined error codes. Since errors
-may have side effects such as a driver reset applications should
-abort on unexpected errors.</para>
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EBADF</errorcode></term>
-	<listitem>
-	  <para><parameter>fd</parameter> is not a valid open file
-descriptor.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EBUSY</errorcode></term>
-	<listitem>
-	  <para>The property cannot be changed right now. Typically
-this error code is returned when I/O is in progress or the driver
-supports multiple opens and another process locked the property.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EFAULT</errorcode></term>
-	<listitem>
-	  <para><parameter>argp</parameter> references an inaccessible
-memory area.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ENOTTY</errorcode></term>
-	<listitem>
-	  <para><parameter>fd</parameter> is  not  associated  with  a
-character special device.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The <parameter>request</parameter> or the data pointed
-to by <parameter>argp</parameter> is not valid. This is a very common
-error code, see the individual ioctl requests listed in <xref
-	      linkend="user-func" /> for actual causes.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ENOMEM</errorcode></term>
-	<listitem>
-	  <para>Not enough physical or virtual memory was available to
-complete the request.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ERANGE</errorcode></term>
-	<listitem>
-	  <para>The application attempted to set a control with the
-&VIDIOC-S-CTRL; ioctl to a value which is out of bounds.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-</refentry>
-
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/DocBook/v4l/media-func-ioctl.xml b/Documentation/DocBook/v4l/media-func-ioctl.xml
deleted file mode 100644
index bda8604..0000000
--- a/Documentation/DocBook/v4l/media-func-ioctl.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-<refentry id="media-func-ioctl">
-  <refmeta>
-    <refentrytitle>media ioctl()</refentrytitle>
-    &manvol;
-  </refmeta>
-
-  <refnamediv>
-    <refname>media-ioctl</refname>
-    <refpurpose>Control a media device</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
-      <funcprototype>
-	<funcdef>int <function>ioctl</function></funcdef>
-	<paramdef>int <parameter>fd</parameter></paramdef>
-	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>void *<parameter>argp</parameter></paramdef>
-      </funcprototype>
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Arguments</title>
-
-    <variablelist>
-      <varlistentry>
-	<term><parameter>fd</parameter></term>
-	<listitem>
-	  <para>&fd;</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>request</parameter></term>
-	<listitem>
-	  <para>Media ioctl request code as defined in the media.h header file,
-	  for example MEDIA_IOC_SETUP_LINK.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>argp</parameter></term>
-	<listitem>
-	  <para>Pointer to a request-specific structure.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>Description</title>
-    <para>The <function>ioctl()</function> function manipulates media device
-    parameters. The argument <parameter>fd</parameter> must be an open file
-    descriptor.</para>
-    <para>The ioctl <parameter>request</parameter> code specifies the media
-    function to be called. It has encoded in it whether the argument is an
-    input, output or read/write parameter, and the size of the argument
-    <parameter>argp</parameter> in bytes.</para>
-    <para>Macros and structures definitions specifying media ioctl requests and
-    their parameters are located in the media.h header file. All media ioctl
-    requests, their respective function and parameters are specified in
-    <xref linkend="media-user-func" />.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>Return Value</title>
-
-    <para><function>ioctl()</function> returns <returnvalue>0</returnvalue> on
-    success. On failure, <returnvalue>-1</returnvalue> is returned, and the
-    <varname>errno</varname> variable is set appropriately. Generic error codes
-    are listed below, and request-specific error codes are listed in the
-    individual requests descriptions.</para>
-    <para>When an ioctl that takes an output or read/write parameter fails,
-    the parameter remains unmodified.</para>
-
-    <variablelist>
-      <varlistentry>
-	<term><errorcode>EBADF</errorcode></term>
-	<listitem>
-	  <para><parameter>fd</parameter> is not a valid open file descriptor.
-	  </para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EFAULT</errorcode></term>
-	<listitem>
-	  <para><parameter>argp</parameter> references an inaccessible memory
-	  area.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>EINVAL</errorcode></term>
-	<listitem>
-	  <para>The <parameter>request</parameter> or the data pointed to by
-	  <parameter>argp</parameter> is not valid. This is a very common error
-	  code, see the individual ioctl requests listed in
-	  <xref linkend="media-user-func" /> for actual causes.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ENOMEM</errorcode></term>
-	<listitem>
-	  <para>Insufficient kernel memory was available to complete the
-	  request.</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><errorcode>ENOTTY</errorcode></term>
-	<listitem>
-	  <para><parameter>fd</parameter> is  not  associated  with  a character
-	  special device.</para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-</refentry>
diff --git a/Documentation/DocBook/v4l/nv12mt.gif b/Documentation/DocBook/v4l/nv12mt.gif
deleted file mode 100644
index ef2d4cf..0000000
--- a/Documentation/DocBook/v4l/nv12mt.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/nv12mt_example.gif b/Documentation/DocBook/v4l/nv12mt_example.gif
deleted file mode 100644
index df81d68..0000000
--- a/Documentation/DocBook/v4l/nv12mt_example.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/pipeline.png b/Documentation/DocBook/v4l/pipeline.png
deleted file mode 100644
index f19b86c..0000000
--- a/Documentation/DocBook/v4l/pipeline.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/vbi_525.gif b/Documentation/DocBook/v4l/vbi_525.gif
deleted file mode 100644
index 5580b69..0000000
--- a/Documentation/DocBook/v4l/vbi_525.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/vbi_625.gif b/Documentation/DocBook/v4l/vbi_625.gif
deleted file mode 100644
index 34e3251..0000000
--- a/Documentation/DocBook/v4l/vbi_625.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/vbi_hsync.gif b/Documentation/DocBook/v4l/vbi_hsync.gif
deleted file mode 100644
index b02434d..0000000
--- a/Documentation/DocBook/v4l/vbi_hsync.gif
+++ /dev/null
Binary files differ
diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml
deleted file mode 100644
index c50536a..0000000
--- a/Documentation/DocBook/v4l/videodev2.h.xml
+++ /dev/null
@@ -1,1946 +0,0 @@
-<programlisting>
-/*
- *  Video for Linux Two header file
- *
- *  Copyright (C) 1999-2007 the contributors
- *
- *  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.
- *
- *  Alternatively you can redistribute this file under the terms of the
- *  BSD license as stated below:
- *
- *  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 names of its contributors may not be used to endorse or promote
- *     products derived from this software without specific prior written
- *     permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *  "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 COPYRIGHT
- *  OWNER OR CONTRIBUTORS 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.
- *
- *      Header file for v4l or V4L2 drivers and applications
- * with public API.
- * All kernel-specific stuff were moved to media/v4l2-dev.h, so
- * no #if __KERNEL tests are allowed here
- *
- *      See http://linuxtv.org for more info
- *
- *      Author: Bill Dirks &lt;bill@thedirks.org&gt;
- *              Justin Schoeman
- *              Hans Verkuil &lt;hverkuil@xs4all.nl&gt;
- *              et al.
- */
-#ifndef __LINUX_VIDEODEV2_H
-#define __LINUX_VIDEODEV2_H
-
-#ifdef __KERNEL__
-#include &lt;linux/time.h&gt;     /* need struct timeval */
-#else
-#include &lt;sys/time.h&gt;
-#endif
-#include &lt;linux/compiler.h&gt;
-#include &lt;linux/ioctl.h&gt;
-#include &lt;linux/types.h&gt;
-
-/*
- * Common stuff for both V4L1 and V4L2
- * Moved from videodev.h
- */
-#define VIDEO_MAX_FRAME               32
-#define VIDEO_MAX_PLANES               8
-
-#ifndef __KERNEL__
-
-/* These defines are V4L1 specific and should not be used with the V4L2 API!
-   They will be removed from this header in the future. */
-
-#define VID_TYPE_CAPTURE        1       /* Can capture */
-#define VID_TYPE_TUNER          2       /* Can tune */
-#define VID_TYPE_TELETEXT       4       /* Does teletext */
-#define VID_TYPE_OVERLAY        8       /* Overlay onto frame buffer */
-#define VID_TYPE_CHROMAKEY      16      /* Overlay by chromakey */
-#define VID_TYPE_CLIPPING       32      /* Can clip */
-#define VID_TYPE_FRAMERAM       64      /* Uses the frame buffer memory */
-#define VID_TYPE_SCALES         128     /* Scalable */
-#define VID_TYPE_MONOCHROME     256     /* Monochrome only */
-#define VID_TYPE_SUBCAPTURE     512     /* Can capture subareas of the image */
-#define VID_TYPE_MPEG_DECODER   1024    /* Can decode MPEG streams */
-#define VID_TYPE_MPEG_ENCODER   2048    /* Can encode MPEG streams */
-#define VID_TYPE_MJPEG_DECODER  4096    /* Can decode MJPEG streams */
-#define VID_TYPE_MJPEG_ENCODER  8192    /* Can encode MJPEG streams */
-#endif
-
-/*
- *      M I S C E L L A N E O U S
- */
-
-/*  Four-character-code (FOURCC) */
-#define v4l2_fourcc(a, b, c, d)\
-        ((__u32)(a) | ((__u32)(b) &lt;&lt; 8) | ((__u32)(c) &lt;&lt; 16) | ((__u32)(d) &lt;&lt; 24))
-
-/*
- *      E N U M S
- */
-enum <link linkend="v4l2-field">v4l2_field</link> {
-        V4L2_FIELD_ANY           = 0, /* driver can choose from none,
-                                         top, bottom, interlaced
-                                         depending on whatever it thinks
-                                         is approximate ... */
-        V4L2_FIELD_NONE          = 1, /* this device has no fields ... */
-        V4L2_FIELD_TOP           = 2, /* top field only */
-        V4L2_FIELD_BOTTOM        = 3, /* bottom field only */
-        V4L2_FIELD_INTERLACED    = 4, /* both fields interlaced */
-        V4L2_FIELD_SEQ_TB        = 5, /* both fields sequential into one
-                                         buffer, top-bottom order */
-        V4L2_FIELD_SEQ_BT        = 6, /* same as above + bottom-top order */
-        V4L2_FIELD_ALTERNATE     = 7, /* both fields alternating into
-                                         separate buffers */
-        V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field
-                                         first and the top field is
-                                         transmitted first */
-        V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field
-                                         first and the bottom field is
-                                         transmitted first */
-};
-#define V4L2_FIELD_HAS_TOP(field)       \
-        ((field) == V4L2_FIELD_TOP      ||\
-         (field) == V4L2_FIELD_INTERLACED ||\
-         (field) == V4L2_FIELD_INTERLACED_TB ||\
-         (field) == V4L2_FIELD_INTERLACED_BT ||\
-         (field) == V4L2_FIELD_SEQ_TB   ||\
-         (field) == V4L2_FIELD_SEQ_BT)
-#define V4L2_FIELD_HAS_BOTTOM(field)    \
-        ((field) == V4L2_FIELD_BOTTOM   ||\
-         (field) == V4L2_FIELD_INTERLACED ||\
-         (field) == V4L2_FIELD_INTERLACED_TB ||\
-         (field) == V4L2_FIELD_INTERLACED_BT ||\
-         (field) == V4L2_FIELD_SEQ_TB   ||\
-         (field) == V4L2_FIELD_SEQ_BT)
-#define V4L2_FIELD_HAS_BOTH(field)      \
-        ((field) == V4L2_FIELD_INTERLACED ||\
-         (field) == V4L2_FIELD_INTERLACED_TB ||\
-         (field) == V4L2_FIELD_INTERLACED_BT ||\
-         (field) == V4L2_FIELD_SEQ_TB ||\
-         (field) == V4L2_FIELD_SEQ_BT)
-
-enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> {
-        V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
-        V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
-        V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
-        V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
-        V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
-        V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
-        V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
-#if 1
-        /* Experimental */
-        V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
-#endif
-        V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
-        V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE  = 10,
-        V4L2_BUF_TYPE_PRIVATE              = 0x80,
-};
-
-#define V4L2_TYPE_IS_MULTIPLANAR(type)                  \
-        ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE   \
-         || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
-
-#define V4L2_TYPE_IS_OUTPUT(type)                               \
-        ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT                   \
-         || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE         \
-         || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY               \
-         || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY        \
-         || (type) == V4L2_BUF_TYPE_VBI_OUTPUT                  \
-         || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
-
-enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link> {
-        V4L2_TUNER_RADIO             = 1,
-        V4L2_TUNER_ANALOG_TV         = 2,
-        V4L2_TUNER_DIGITAL_TV        = 3,
-};
-
-enum <link linkend="v4l2-memory">v4l2_memory</link> {
-        V4L2_MEMORY_MMAP             = 1,
-        V4L2_MEMORY_USERPTR          = 2,
-        V4L2_MEMORY_OVERLAY          = 3,
-};
-
-/* see also http://vektor.theorem.ca/graphics/ycbcr/ */
-enum <link linkend="v4l2-colorspace">v4l2_colorspace</link> {
-        /* ITU-R 601 -- broadcast NTSC/PAL */
-        V4L2_COLORSPACE_SMPTE170M     = 1,
-
-        /* 1125-Line (US) HDTV */
-        V4L2_COLORSPACE_SMPTE240M     = 2,
-
-        /* HD and modern captures. */
-        V4L2_COLORSPACE_REC709        = 3,
-
-        /* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
-        V4L2_COLORSPACE_BT878         = 4,
-
-        /* These should be useful.  Assume 601 extents. */
-        V4L2_COLORSPACE_470_SYSTEM_M  = 5,
-        V4L2_COLORSPACE_470_SYSTEM_BG = 6,
-
-        /* I know there will be cameras that send this.  So, this is
-         * unspecified chromaticities and full 0-255 on each of the
-         * Y'CbCr components
-         */
-        V4L2_COLORSPACE_JPEG          = 7,
-
-        /* For RGB colourspaces, this is probably a good start. */
-        V4L2_COLORSPACE_SRGB          = 8,
-};
-
-enum <link linkend="v4l2-priority">v4l2_priority</link> {
-        V4L2_PRIORITY_UNSET       = 0,  /* not initialized */
-        V4L2_PRIORITY_BACKGROUND  = 1,
-        V4L2_PRIORITY_INTERACTIVE = 2,
-        V4L2_PRIORITY_RECORD      = 3,
-        V4L2_PRIORITY_DEFAULT     = V4L2_PRIORITY_INTERACTIVE,
-};
-
-struct <link linkend="v4l2-rect">v4l2_rect</link> {
-        __s32   left;
-        __s32   top;
-        __s32   width;
-        __s32   height;
-};
-
-struct <link linkend="v4l2-fract">v4l2_fract</link> {
-        __u32   numerator;
-        __u32   denominator;
-};
-
-/*
- *      D R I V E R   C A P A B I L I T I E S
- */
-struct <link linkend="v4l2-capability">v4l2_capability</link> {
-        __u8    driver[16];     /* i.e.ie; "bttv" */
-        __u8    card[32];       /* i.e.ie; "Hauppauge WinTV" */
-        __u8    bus_info[32];   /* "PCI:" + pci_name(pci_dev) */
-        __u32   version;        /* should use KERNEL_VERSION() */
-        __u32   capabilities;   /* Device capabilities */
-        __u32   reserved[4];
-};
-
-/* Values for 'capabilities' field */
-#define V4L2_CAP_VIDEO_CAPTURE          0x00000001  /* Is a video capture device */
-#define V4L2_CAP_VIDEO_OUTPUT           0x00000002  /* Is a video output device */
-#define V4L2_CAP_VIDEO_OVERLAY          0x00000004  /* Can do video overlay */
-#define V4L2_CAP_VBI_CAPTURE            0x00000010  /* Is a raw VBI capture device */
-#define V4L2_CAP_VBI_OUTPUT             0x00000020  /* Is a raw VBI output device */
-#define V4L2_CAP_SLICED_VBI_CAPTURE     0x00000040  /* Is a sliced VBI capture device */
-#define V4L2_CAP_SLICED_VBI_OUTPUT      0x00000080  /* Is a sliced VBI output device */
-#define V4L2_CAP_RDS_CAPTURE            0x00000100  /* RDS data capture */
-#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY   0x00000200  /* Can do video output overlay */
-#define V4L2_CAP_HW_FREQ_SEEK           0x00000400  /* Can do hardware frequency seek  */
-#define V4L2_CAP_RDS_OUTPUT             0x00000800  /* Is an RDS encoder */
-
-/* Is a video capture device that supports multiplanar formats */
-#define V4L2_CAP_VIDEO_CAPTURE_MPLANE   0x00001000
-/* Is a video output device that supports multiplanar formats */
-#define V4L2_CAP_VIDEO_OUTPUT_MPLANE    0x00002000
-
-#define V4L2_CAP_TUNER                  0x00010000  /* has a tuner */
-#define V4L2_CAP_AUDIO                  0x00020000  /* has audio support */
-#define V4L2_CAP_RADIO                  0x00040000  /* is a radio device */
-#define V4L2_CAP_MODULATOR              0x00080000  /* has a modulator */
-
-#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
-#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
-#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
-
-/*
- *      V I D E O   I M A G E   F O R M A T
- */
-struct <link linkend="v4l2-pix-format">v4l2_pix_format</link> {
-        __u32                   width;
-        __u32                   height;
-        __u32                   pixelformat;
-        enum <link linkend="v4l2-field">v4l2_field</link>         field;
-        __u32                   bytesperline;   /* for padding, zero if unused */
-        __u32                   sizeimage;
-        enum <link linkend="v4l2-colorspace">v4l2_colorspace</link>    colorspace;
-        __u32                   priv;           /* private data, depends on pixelformat */
-};
-
-/*      Pixel format         FOURCC                          depth  Description  */
-
-/* RGB formats */
-#define <link linkend="V4L2-PIX-FMT-RGB332">V4L2_PIX_FMT_RGB332</link>  v4l2_fourcc('R', 'G', 'B', '1') /*  8  RGB-3-3-2     */
-#define <link linkend="V4L2-PIX-FMT-RGB444">V4L2_PIX_FMT_RGB444</link>  v4l2_fourcc('R', '4', '4', '4') /* 16  xxxxrrrr ggggbbbb */
-#define <link linkend="V4L2-PIX-FMT-RGB555">V4L2_PIX_FMT_RGB555</link>  v4l2_fourcc('R', 'G', 'B', 'O') /* 16  RGB-5-5-5     */
-#define <link linkend="V4L2-PIX-FMT-RGB565">V4L2_PIX_FMT_RGB565</link>  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
-#define <link linkend="V4L2-PIX-FMT-RGB555X">V4L2_PIX_FMT_RGB555X</link> v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
-#define <link linkend="V4L2-PIX-FMT-RGB565X">V4L2_PIX_FMT_RGB565X</link> v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
-#define <link linkend="V4L2-PIX-FMT-BGR666">V4L2_PIX_FMT_BGR666</link>  v4l2_fourcc('B', 'G', 'R', 'H') /* 18  BGR-6-6-6     */
-#define <link linkend="V4L2-PIX-FMT-BGR24">V4L2_PIX_FMT_BGR24</link>   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
-#define <link linkend="V4L2-PIX-FMT-RGB24">V4L2_PIX_FMT_RGB24</link>   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
-#define <link linkend="V4L2-PIX-FMT-BGR32">V4L2_PIX_FMT_BGR32</link>   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
-#define <link linkend="V4L2-PIX-FMT-RGB32">V4L2_PIX_FMT_RGB32</link>   v4l2_fourcc('R', 'G', 'B', '4') /* 32  RGB-8-8-8-8   */
-
-/* Grey formats */
-#define <link linkend="V4L2-PIX-FMT-GREY">V4L2_PIX_FMT_GREY</link>    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
-#define <link linkend="V4L2-PIX-FMT-Y4">V4L2_PIX_FMT_Y4</link>      v4l2_fourcc('Y', '0', '4', ' ') /*  4  Greyscale     */
-#define <link linkend="V4L2-PIX-FMT-Y6">V4L2_PIX_FMT_Y6</link>      v4l2_fourcc('Y', '0', '6', ' ') /*  6  Greyscale     */
-#define <link linkend="V4L2-PIX-FMT-Y10">V4L2_PIX_FMT_Y10</link>     v4l2_fourcc('Y', '1', '0', ' ') /* 10  Greyscale     */
-#define <link linkend="V4L2-PIX-FMT-Y16">V4L2_PIX_FMT_Y16</link>     v4l2_fourcc('Y', '1', '6', ' ') /* 16  Greyscale     */
-
-/* Grey bit-packed formats */
-#define <link linkend="V4L2-PIX-FMT-Y10BPACK">V4L2_PIX_FMT_Y10BPACK</link>    v4l2_fourcc('Y', '1', '0', 'B') /* 10  Greyscale bit-packed */
-
-/* Palette formats */
-#define <link linkend="V4L2-PIX-FMT-PAL8">V4L2_PIX_FMT_PAL8</link>    v4l2_fourcc('P', 'A', 'L', '8') /*  8  8-bit palette */
-
-/* Luminance+Chrominance formats */
-#define <link linkend="V4L2-PIX-FMT-YVU410">V4L2_PIX_FMT_YVU410</link>  v4l2_fourcc('Y', 'V', 'U', '9') /*  9  YVU 4:1:0     */
-#define <link linkend="V4L2-PIX-FMT-YVU420">V4L2_PIX_FMT_YVU420</link>  v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
-#define <link linkend="V4L2-PIX-FMT-YUYV">V4L2_PIX_FMT_YUYV</link>    v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
-#define <link linkend="V4L2-PIX-FMT-YYUV">V4L2_PIX_FMT_YYUV</link>    v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16  YUV 4:2:2     */
-#define <link linkend="V4L2-PIX-FMT-YVYU">V4L2_PIX_FMT_YVYU</link>    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
-#define <link linkend="V4L2-PIX-FMT-UYVY">V4L2_PIX_FMT_UYVY</link>    v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16  YUV 4:2:2     */
-#define <link linkend="V4L2-PIX-FMT-VYUY">V4L2_PIX_FMT_VYUY</link>    v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16  YUV 4:2:2     */
-#define <link linkend="V4L2-PIX-FMT-YUV422P">V4L2_PIX_FMT_YUV422P</link> v4l2_fourcc('4', '2', '2', 'P') /* 16  YVU422 planar */
-#define <link linkend="V4L2-PIX-FMT-YUV411P">V4L2_PIX_FMT_YUV411P</link> v4l2_fourcc('4', '1', '1', 'P') /* 16  YVU411 planar */
-#define <link linkend="V4L2-PIX-FMT-Y41P">V4L2_PIX_FMT_Y41P</link>    v4l2_fourcc('Y', '4', '1', 'P') /* 12  YUV 4:1:1     */
-#define <link linkend="V4L2-PIX-FMT-YUV444">V4L2_PIX_FMT_YUV444</link>  v4l2_fourcc('Y', '4', '4', '4') /* 16  xxxxyyyy uuuuvvvv */
-#define <link linkend="V4L2-PIX-FMT-YUV555">V4L2_PIX_FMT_YUV555</link>  v4l2_fourcc('Y', 'U', 'V', 'O') /* 16  YUV-5-5-5     */
-#define <link linkend="V4L2-PIX-FMT-YUV565">V4L2_PIX_FMT_YUV565</link>  v4l2_fourcc('Y', 'U', 'V', 'P') /* 16  YUV-5-6-5     */
-#define <link linkend="V4L2-PIX-FMT-YUV32">V4L2_PIX_FMT_YUV32</link>   v4l2_fourcc('Y', 'U', 'V', '4') /* 32  YUV-8-8-8-8   */
-#define <link linkend="V4L2-PIX-FMT-YUV410">V4L2_PIX_FMT_YUV410</link>  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
-#define <link linkend="V4L2-PIX-FMT-YUV420">V4L2_PIX_FMT_YUV420</link>  v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
-#define <link linkend="V4L2-PIX-FMT-HI240">V4L2_PIX_FMT_HI240</link>   v4l2_fourcc('H', 'I', '2', '4') /*  8  8-bit color   */
-#define <link linkend="V4L2-PIX-FMT-HM12">V4L2_PIX_FMT_HM12</link>    v4l2_fourcc('H', 'M', '1', '2') /*  8  YUV 4:2:0 16x16 macroblocks */
-#define <link linkend="V4L2-PIX-FMT-M420">V4L2_PIX_FMT_M420</link>    v4l2_fourcc('M', '4', '2', '0') /* 12  YUV 4:2:0 2 lines y, 1 line uv interleaved */
-
-/* two planes -- one Y, one Cr + Cb interleaved  */
-#define <link linkend="V4L2-PIX-FMT-NV12">V4L2_PIX_FMT_NV12</link>    v4l2_fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
-#define <link linkend="V4L2-PIX-FMT-NV21">V4L2_PIX_FMT_NV21</link>    v4l2_fourcc('N', 'V', '2', '1') /* 12  Y/CrCb 4:2:0  */
-#define <link linkend="V4L2-PIX-FMT-NV16">V4L2_PIX_FMT_NV16</link>    v4l2_fourcc('N', 'V', '1', '6') /* 16  Y/CbCr 4:2:2  */
-#define <link linkend="V4L2-PIX-FMT-NV61">V4L2_PIX_FMT_NV61</link>    v4l2_fourcc('N', 'V', '6', '1') /* 16  Y/CrCb 4:2:2  */
-
-/* two non contiguous planes - one Y, one Cr + Cb interleaved  */
-#define <link linkend="V4L2-PIX-FMT-NV12M">V4L2_PIX_FMT_NV12M</link>   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
-#define <link linkend="V4L2-PIX-FMT-NV12MT">V4L2_PIX_FMT_NV12MT</link>  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */
-
-/* three non contiguous planes - Y, Cb, Cr */
-#define <link linkend="V4L2-PIX-FMT-YUV420M">V4L2_PIX_FMT_YUV420M</link> v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
-
-/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
-#define <link linkend="V4L2-PIX-FMT-SBGGR8">V4L2_PIX_FMT_SBGGR8</link>  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
-#define <link linkend="V4L2-PIX-FMT-SGBRG8">V4L2_PIX_FMT_SGBRG8</link>  v4l2_fourcc('G', 'B', 'R', 'G') /*  8  GBGB.. RGRG.. */
-#define <link linkend="V4L2-PIX-FMT-SGRBG8">V4L2_PIX_FMT_SGRBG8</link>  v4l2_fourcc('G', 'R', 'B', 'G') /*  8  GRGR.. BGBG.. */
-#define <link linkend="V4L2-PIX-FMT-SRGGB8">V4L2_PIX_FMT_SRGGB8</link>  v4l2_fourcc('R', 'G', 'G', 'B') /*  8  RGRG.. GBGB.. */
-#define <link linkend="V4L2-PIX-FMT-SBGGR10">V4L2_PIX_FMT_SBGGR10</link> v4l2_fourcc('B', 'G', '1', '0') /* 10  BGBG.. GRGR.. */
-#define <link linkend="V4L2-PIX-FMT-SGBRG10">V4L2_PIX_FMT_SGBRG10</link> v4l2_fourcc('G', 'B', '1', '0') /* 10  GBGB.. RGRG.. */
-#define <link linkend="V4L2-PIX-FMT-SGRBG10">V4L2_PIX_FMT_SGRBG10</link> v4l2_fourcc('B', 'A', '1', '0') /* 10  GRGR.. BGBG.. */
-#define <link linkend="V4L2-PIX-FMT-SRGGB10">V4L2_PIX_FMT_SRGGB10</link> v4l2_fourcc('R', 'G', '1', '0') /* 10  RGRG.. GBGB.. */
-        /* 10bit raw bayer DPCM compressed to 8 bits */
-#define <link linkend="V4L2-PIX-FMT-SGRBG10DPCM8">V4L2_PIX_FMT_SGRBG10DPCM8</link> v4l2_fourcc('B', 'D', '1', '0')
-        /*
-         * 10bit raw bayer, expanded to 16 bits
-         * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
-         */
-#define <link linkend="V4L2-PIX-FMT-SBGGR16">V4L2_PIX_FMT_SBGGR16</link> v4l2_fourcc('B', 'Y', 'R', '2') /* 16  BGBG.. GRGR.. */
-
-/* compressed formats */
-#define <link linkend="V4L2-PIX-FMT-MJPEG">V4L2_PIX_FMT_MJPEG</link>    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */
-#define <link linkend="V4L2-PIX-FMT-JPEG">V4L2_PIX_FMT_JPEG</link>     v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG     */
-#define <link linkend="V4L2-PIX-FMT-DV">V4L2_PIX_FMT_DV</link>       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
-#define <link linkend="V4L2-PIX-FMT-MPEG">V4L2_PIX_FMT_MPEG</link>     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */
-
-/*  Vendor-specific formats   */
-#define <link linkend="V4L2-PIX-FMT-CPIA1">V4L2_PIX_FMT_CPIA1</link>    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
-#define <link linkend="V4L2-PIX-FMT-WNVA">V4L2_PIX_FMT_WNVA</link>     v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
-#define <link linkend="V4L2-PIX-FMT-SN9C10X">V4L2_PIX_FMT_SN9C10X</link>  v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
-#define <link linkend="V4L2-PIX-FMT-SN9C20X-I420">V4L2_PIX_FMT_SN9C20X_I420</link> v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */
-#define <link linkend="V4L2-PIX-FMT-PWC1">V4L2_PIX_FMT_PWC1</link>     v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */
-#define <link linkend="V4L2-PIX-FMT-PWC2">V4L2_PIX_FMT_PWC2</link>     v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */
-#define <link linkend="V4L2-PIX-FMT-ET61X251">V4L2_PIX_FMT_ET61X251</link> v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */
-#define <link linkend="V4L2-PIX-FMT-SPCA501">V4L2_PIX_FMT_SPCA501</link>  v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */
-#define <link linkend="V4L2-PIX-FMT-SPCA505">V4L2_PIX_FMT_SPCA505</link>  v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */
-#define <link linkend="V4L2-PIX-FMT-SPCA508">V4L2_PIX_FMT_SPCA508</link>  v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
-#define <link linkend="V4L2-PIX-FMT-SPCA561">V4L2_PIX_FMT_SPCA561</link>  v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
-#define <link linkend="V4L2-PIX-FMT-PAC207">V4L2_PIX_FMT_PAC207</link>   v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
-#define <link linkend="V4L2-PIX-FMT-MR97310A">V4L2_PIX_FMT_MR97310A</link> v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
-#define <link linkend="V4L2-PIX-FMT-SN9C2028">V4L2_PIX_FMT_SN9C2028</link> v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */
-#define <link linkend="V4L2-PIX-FMT-SQ905C">V4L2_PIX_FMT_SQ905C</link>   v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
-#define <link linkend="V4L2-PIX-FMT-PJPG">V4L2_PIX_FMT_PJPG</link>     v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
-#define <link linkend="V4L2-PIX-FMT-OV511">V4L2_PIX_FMT_OV511</link>    v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
-#define <link linkend="V4L2-PIX-FMT-OV518">V4L2_PIX_FMT_OV518</link>    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
-#define <link linkend="V4L2-PIX-FMT-STV0680">V4L2_PIX_FMT_STV0680</link>  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
-#define <link linkend="V4L2-PIX-FMT-TM6000">V4L2_PIX_FMT_TM6000</link>   v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
-#define <link linkend="V4L2-PIX-FMT-CIT-YYVYUY">V4L2_PIX_FMT_CIT_YYVYUY</link> v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
-#define <link linkend="V4L2-PIX-FMT-KONICA420">V4L2_PIX_FMT_KONICA420</link>  v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
-
-/*
- *      F O R M A T   E N U M E R A T I O N
- */
-struct <link linkend="v4l2-fmtdesc">v4l2_fmtdesc</link> {
-        __u32               index;             /* Format number      */
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link>  type;              /* buffer type        */
-        __u32               flags;
-        __u8                description[32];   /* Description string */
-        __u32               pixelformat;       /* Format fourcc      */
-        __u32               reserved[4];
-};
-
-#define V4L2_FMT_FLAG_COMPRESSED 0x0001
-#define V4L2_FMT_FLAG_EMULATED   0x0002
-
-#if 1
-        /* Experimental Frame Size and frame rate enumeration */
-/*
- *      F R A M E   S I Z E   E N U M E R A T I O N
- */
-enum <link linkend="v4l2-frmsizetypes">v4l2_frmsizetypes</link> {
-        V4L2_FRMSIZE_TYPE_DISCRETE      = 1,
-        V4L2_FRMSIZE_TYPE_CONTINUOUS    = 2,
-        V4L2_FRMSIZE_TYPE_STEPWISE      = 3,
-};
-
-struct <link linkend="v4l2-frmsize-discrete">v4l2_frmsize_discrete</link> {
-        __u32                   width;          /* Frame width [pixel] */
-        __u32                   height;         /* Frame height [pixel] */
-};
-
-struct <link linkend="v4l2-frmsize-stepwise">v4l2_frmsize_stepwise</link> {
-        __u32                   min_width;      /* Minimum frame width [pixel] */
-        __u32                   max_width;      /* Maximum frame width [pixel] */
-        __u32                   step_width;     /* Frame width step size [pixel] */
-        __u32                   min_height;     /* Minimum frame height [pixel] */
-        __u32                   max_height;     /* Maximum frame height [pixel] */
-        __u32                   step_height;    /* Frame height step size [pixel] */
-};
-
-struct <link linkend="v4l2-frmsizeenum">v4l2_frmsizeenum</link> {
-        __u32                   index;          /* Frame size number */
-        __u32                   pixel_format;   /* Pixel format */
-        __u32                   type;           /* Frame size type the device supports. */
-
-        union {                                 /* Frame size */
-                struct <link linkend="v4l2-frmsize-discrete">v4l2_frmsize_discrete</link>    discrete;
-                struct <link linkend="v4l2-frmsize-stepwise">v4l2_frmsize_stepwise</link>    stepwise;
-        };
-
-        __u32   reserved[2];                    /* Reserved space for future use */
-};
-
-/*
- *      F R A M E   R A T E   E N U M E R A T I O N
- */
-enum <link linkend="v4l2-frmivaltypes">v4l2_frmivaltypes</link> {
-        V4L2_FRMIVAL_TYPE_DISCRETE      = 1,
-        V4L2_FRMIVAL_TYPE_CONTINUOUS    = 2,
-        V4L2_FRMIVAL_TYPE_STEPWISE      = 3,
-};
-
-struct <link linkend="v4l2-frmival-stepwise">v4l2_frmival_stepwise</link> {
-        struct <link linkend="v4l2-fract">v4l2_fract</link>       min;            /* Minimum frame interval [s] */
-        struct <link linkend="v4l2-fract">v4l2_fract</link>       max;            /* Maximum frame interval [s] */
-        struct <link linkend="v4l2-fract">v4l2_fract</link>       step;           /* Frame interval step size [s] */
-};
-
-struct <link linkend="v4l2-frmivalenum">v4l2_frmivalenum</link> {
-        __u32                   index;          /* Frame format index */
-        __u32                   pixel_format;   /* Pixel format */
-        __u32                   width;          /* Frame width */
-        __u32                   height;         /* Frame height */
-        __u32                   type;           /* Frame interval type the device supports. */
-
-        union {                                 /* Frame interval */
-                struct <link linkend="v4l2-fract">v4l2_fract</link>               discrete;
-                struct <link linkend="v4l2-frmival-stepwise">v4l2_frmival_stepwise</link>    stepwise;
-        };
-
-        __u32   reserved[2];                    /* Reserved space for future use */
-};
-#endif
-
-/*
- *      T I M E C O D E
- */
-struct <link linkend="v4l2-timecode">v4l2_timecode</link> {
-        __u32   type;
-        __u32   flags;
-        __u8    frames;
-        __u8    seconds;
-        __u8    minutes;
-        __u8    hours;
-        __u8    userbits[4];
-};
-
-/*  Type  */
-#define V4L2_TC_TYPE_24FPS              1
-#define V4L2_TC_TYPE_25FPS              2
-#define V4L2_TC_TYPE_30FPS              3
-#define V4L2_TC_TYPE_50FPS              4
-#define V4L2_TC_TYPE_60FPS              5
-
-/*  Flags  */
-#define V4L2_TC_FLAG_DROPFRAME          0x0001 /* "drop-frame" mode */
-#define V4L2_TC_FLAG_COLORFRAME         0x0002
-#define V4L2_TC_USERBITS_field          0x000C
-#define V4L2_TC_USERBITS_USERDEFINED    0x0000
-#define V4L2_TC_USERBITS_8BITCHARS      0x0008
-/* The above is based on SMPTE timecodes */
-
-struct <link linkend="v4l2-jpegcompression">v4l2_jpegcompression</link> {
-        int quality;
-
-        int  APPn;              /* Number of APP segment to be written,
-                                 * must be 0..15 */
-        int  APP_len;           /* Length of data in JPEG APPn segment */
-        char APP_data[60];      /* Data in the JPEG APPn segment. */
-
-        int  COM_len;           /* Length of data in JPEG COM segment */
-        char COM_data[60];      /* Data in JPEG COM segment */
-
-        __u32 jpeg_markers;     /* Which markers should go into the JPEG
-                                 * output. Unless you exactly know what
-                                 * you do, leave them untouched.
-                                 * Inluding less markers will make the
-                                 * resulting code smaller, but there will
-                                 * be fewer applications which can read it.
-                                 * The presence of the APP and COM marker
-                                 * is influenced by APP_len and COM_len
-                                 * ONLY, not by this property! */
-
-#define V4L2_JPEG_MARKER_DHT (1&lt;&lt;3)    /* Define Huffman Tables */
-#define V4L2_JPEG_MARKER_DQT (1&lt;&lt;4)    /* Define Quantization Tables */
-#define V4L2_JPEG_MARKER_DRI (1&lt;&lt;5)    /* Define Restart Interval */
-#define V4L2_JPEG_MARKER_COM (1&lt;&lt;6)    /* Comment segment */
-#define V4L2_JPEG_MARKER_APP (1&lt;&lt;7)    /* App segment, driver will
-                                        * allways use APP0 */
-};
-
-/*
- *      M E M O R Y - M A P P I N G   B U F F E R S
- */
-struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> {
-        __u32                   count;
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link>      type;
-        enum <link linkend="v4l2-memory">v4l2_memory</link>        memory;
-        __u32                   reserved[2];
-};
-
-/**
- * struct <link linkend="v4l2-plane">v4l2_plane</link> - plane info for multi-planar buffers
- * @bytesused:          number of bytes occupied by data in the plane (payload)
- * @length:             size of this plane (NOT the payload) in bytes
- * @mem_offset:         when memory in the associated struct <link linkend="v4l2-buffer">v4l2_buffer</link> is
- *                      V4L2_MEMORY_MMAP, equals the offset from the start of
- *                      the device memory for this plane (or is a "cookie" that
- *                      should be passed to mmap() called on the video node)
- * @userptr:            when memory is V4L2_MEMORY_USERPTR, a userspace pointer
- *                      pointing to this plane
- * @data_offset:        offset in the plane to the start of data; usually 0,
- *                      unless there is a header in front of the data
- *
- * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
- * with two planes can have one plane for Y, and another for interleaved CbCr
- * components. Each plane can reside in a separate memory buffer, or even in
- * a completely separate memory node (e.g. in embedded devices).
- */
-struct <link linkend="v4l2-plane">v4l2_plane</link> {
-        __u32                   bytesused;
-        __u32                   length;
-        union {
-                __u32           mem_offset;
-                unsigned long   userptr;
-        } m;
-        __u32                   data_offset;
-        __u32                   reserved[11];
-};
-
-/**
- * struct <link linkend="v4l2-buffer">v4l2_buffer</link> - video buffer info
- * @index:      id number of the buffer
- * @type:       buffer type (type == *_MPLANE for multiplanar buffers)
- * @bytesused:  number of bytes occupied by data in the buffer (payload);
- *              unused (set to 0) for multiplanar buffers
- * @flags:      buffer informational flags
- * @field:      field order of the image in the buffer
- * @timestamp:  frame timestamp
- * @timecode:   frame timecode
- * @sequence:   sequence count of this frame
- * @memory:     the method, in which the actual video data is passed
- * @offset:     for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
- *              offset from the start of the device memory for this plane,
- *              (or a "cookie" that should be passed to mmap() as offset)
- * @userptr:    for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
- *              a userspace pointer pointing to this buffer
- * @planes:     for multiplanar buffers; userspace pointer to the array of plane
- *              info structs for this buffer
- * @length:     size in bytes of the buffer (NOT its payload) for single-plane
- *              buffers (when type != *_MPLANE); number of elements in the
- *              planes array for multi-plane buffers
- * @input:      input number from which the video data has has been captured
- *
- * Contains data exchanged by application and driver using one of the Streaming
- * I/O methods.
- */
-struct <link linkend="v4l2-buffer">v4l2_buffer</link> {
-        __u32                   index;
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link>      type;
-        __u32                   bytesused;
-        __u32                   flags;
-        enum <link linkend="v4l2-field">v4l2_field</link>         field;
-        struct timeval          timestamp;
-        struct <link linkend="v4l2-timecode">v4l2_timecode</link>    timecode;
-        __u32                   sequence;
-
-        /* memory location */
-        enum <link linkend="v4l2-memory">v4l2_memory</link>        memory;
-        union {
-                __u32           offset;
-                unsigned long   userptr;
-                struct <link linkend="v4l2-plane">v4l2_plane</link> *planes;
-        } m;
-        __u32                   length;
-        __u32                   input;
-        __u32                   reserved;
-};
-
-/*  Flags for 'flags' field */
-#define V4L2_BUF_FLAG_MAPPED    0x0001  /* Buffer is mapped (flag) */
-#define V4L2_BUF_FLAG_QUEUED    0x0002  /* Buffer is queued for processing */
-#define V4L2_BUF_FLAG_DONE      0x0004  /* Buffer is ready */
-#define V4L2_BUF_FLAG_KEYFRAME  0x0008  /* Image is a keyframe (I-frame) */
-#define V4L2_BUF_FLAG_PFRAME    0x0010  /* Image is a P-frame */
-#define V4L2_BUF_FLAG_BFRAME    0x0020  /* Image is a B-frame */
-/* Buffer is ready, but the data contained within is corrupted. */
-#define V4L2_BUF_FLAG_ERROR     0x0040
-#define V4L2_BUF_FLAG_TIMECODE  0x0100  /* timecode field is valid */
-#define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
-
-/*
- *      O V E R L A Y   P R E V I E W
- */
-struct <link linkend="v4l2-framebuffer">v4l2_framebuffer</link> {
-        __u32                   capability;
-        __u32                   flags;
-/* FIXME: in theory we should pass something like PCI device + memory
- * region + offset instead of some physical address */
-        void                    *base;
-        struct <link linkend="v4l2-pix-format">v4l2_pix_format</link>  fmt;
-};
-/*  Flags for the 'capability' field. Read only */
-#define V4L2_FBUF_CAP_EXTERNOVERLAY     0x0001
-#define V4L2_FBUF_CAP_CHROMAKEY         0x0002
-#define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
-#define V4L2_FBUF_CAP_BITMAP_CLIPPING   0x0008
-#define V4L2_FBUF_CAP_LOCAL_ALPHA       0x0010
-#define V4L2_FBUF_CAP_GLOBAL_ALPHA      0x0020
-#define V4L2_FBUF_CAP_LOCAL_INV_ALPHA   0x0040
-#define V4L2_FBUF_CAP_SRC_CHROMAKEY     0x0080
-/*  Flags for the 'flags' field. */
-#define V4L2_FBUF_FLAG_PRIMARY          0x0001
-#define V4L2_FBUF_FLAG_OVERLAY          0x0002
-#define V4L2_FBUF_FLAG_CHROMAKEY        0x0004
-#define V4L2_FBUF_FLAG_LOCAL_ALPHA      0x0008
-#define V4L2_FBUF_FLAG_GLOBAL_ALPHA     0x0010
-#define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA  0x0020
-#define V4L2_FBUF_FLAG_SRC_CHROMAKEY    0x0040
-
-struct <link linkend="v4l2-clip">v4l2_clip</link> {
-        struct <link linkend="v4l2-rect">v4l2_rect</link>        c;
-        struct <link linkend="v4l2-clip">v4l2_clip</link>        __user *next;
-};
-
-struct <link linkend="v4l2-window">v4l2_window</link> {
-        struct <link linkend="v4l2-rect">v4l2_rect</link>        w;
-        enum <link linkend="v4l2-field">v4l2_field</link>         field;
-        __u32                   chromakey;
-        struct <link linkend="v4l2-clip">v4l2_clip</link>        __user *clips;
-        __u32                   clipcount;
-        void                    __user *bitmap;
-        __u8                    global_alpha;
-};
-
-/*
- *      C A P T U R E   P A R A M E T E R S
- */
-struct <link linkend="v4l2-captureparm">v4l2_captureparm</link> {
-        __u32              capability;    /*  Supported modes */
-        __u32              capturemode;   /*  Current mode */
-        struct <link linkend="v4l2-fract">v4l2_fract</link>  timeperframe;  /*  Time per frame in .1us units */
-        __u32              extendedmode;  /*  Driver-specific extensions */
-        __u32              readbuffers;   /*  # of buffers for read */
-        __u32              reserved[4];
-};
-
-/*  Flags for 'capability' and 'capturemode' fields */
-#define V4L2_MODE_HIGHQUALITY   0x0001  /*  High quality imaging mode */
-#define V4L2_CAP_TIMEPERFRAME   0x1000  /*  timeperframe field is supported */
-
-struct <link linkend="v4l2-outputparm">v4l2_outputparm</link> {
-        __u32              capability;   /*  Supported modes */
-        __u32              outputmode;   /*  Current mode */
-        struct <link linkend="v4l2-fract">v4l2_fract</link>  timeperframe; /*  Time per frame in seconds */
-        __u32              extendedmode; /*  Driver-specific extensions */
-        __u32              writebuffers; /*  # of buffers for write */
-        __u32              reserved[4];
-};
-
-/*
- *      I N P U T   I M A G E   C R O P P I N G
- */
-struct <link linkend="v4l2-cropcap">v4l2_cropcap</link> {
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link>      type;
-        struct <link linkend="v4l2-rect">v4l2_rect</link>        bounds;
-        struct <link linkend="v4l2-rect">v4l2_rect</link>        defrect;
-        struct <link linkend="v4l2-fract">v4l2_fract</link>       pixelaspect;
-};
-
-struct <link linkend="v4l2-crop">v4l2_crop</link> {
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link>      type;
-        struct <link linkend="v4l2-rect">v4l2_rect</link>        c;
-};
-
-/*
- *      A N A L O G   V I D E O   S T A N D A R D
- */
-
-typedef __u64 v4l2_std_id;
-
-/* one bit for each */
-#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)
-#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)
-#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)
-#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)
-#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)
-#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)
-#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)
-#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)
-
-#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)
-#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)
-#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)
-#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)
-
-#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)
-#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000)
-#define V4L2_STD_NTSC_443       ((v4l2_std_id)0x00004000)
-#define V4L2_STD_NTSC_M_KR      ((v4l2_std_id)0x00008000)
-
-#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)
-#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)
-#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)
-#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)
-#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)
-#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)
-#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)
-#define V4L2_STD_SECAM_LC       ((v4l2_std_id)0x00800000)
-
-/* ATSC/HDTV */
-#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)
-#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)
-
-/* FIXME:
-   Although std_id is 64 bits, there is an issue on PPC32 architecture that
-   makes switch(__u64) to break. So, there's a hack on v4l2-common.c rounding
-   this value to 32 bits.
-   As, currently, the max value is for V4L2_STD_ATSC_16_VSB (30 bits wide),
-   it should work fine. However, if needed to add more than two standards,
-   v4l2-common.c should be fixed.
- */
-
-/* some merged standards */
-#define V4L2_STD_MN     (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)
-#define V4L2_STD_B      (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)
-#define V4L2_STD_GH     (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)
-#define V4L2_STD_DK     (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)
-
-/* some common needed stuff */
-#define V4L2_STD_PAL_BG         (V4L2_STD_PAL_B         |\
-                                 V4L2_STD_PAL_B1        |\
-                                 V4L2_STD_PAL_G)
-#define V4L2_STD_PAL_DK         (V4L2_STD_PAL_D         |\
-                                 V4L2_STD_PAL_D1        |\
-                                 V4L2_STD_PAL_K)
-#define V4L2_STD_PAL            (V4L2_STD_PAL_BG        |\
-                                 V4L2_STD_PAL_DK        |\
-                                 V4L2_STD_PAL_H         |\
-                                 V4L2_STD_PAL_I)
-#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M        |\
-                                 V4L2_STD_NTSC_M_JP     |\
-                                 V4L2_STD_NTSC_M_KR)
-#define V4L2_STD_SECAM_DK       (V4L2_STD_SECAM_D       |\
-                                 V4L2_STD_SECAM_K       |\
-                                 V4L2_STD_SECAM_K1)
-#define V4L2_STD_SECAM          (V4L2_STD_SECAM_B       |\
-                                 V4L2_STD_SECAM_G       |\
-                                 V4L2_STD_SECAM_H       |\
-                                 V4L2_STD_SECAM_DK      |\
-                                 V4L2_STD_SECAM_L       |\
-                                 V4L2_STD_SECAM_LC)
-
-#define V4L2_STD_525_60         (V4L2_STD_PAL_M         |\
-                                 V4L2_STD_PAL_60        |\
-                                 V4L2_STD_NTSC          |\
-                                 V4L2_STD_NTSC_443)
-#define V4L2_STD_625_50         (V4L2_STD_PAL           |\
-                                 V4L2_STD_PAL_N         |\
-                                 V4L2_STD_PAL_Nc        |\
-                                 V4L2_STD_SECAM)
-#define V4L2_STD_ATSC           (V4L2_STD_ATSC_8_VSB    |\
-                                 V4L2_STD_ATSC_16_VSB)
-
-#define V4L2_STD_UNKNOWN        0
-#define V4L2_STD_ALL            (V4L2_STD_525_60        |\
-                                 V4L2_STD_625_50)
-
-struct <link linkend="v4l2-standard">v4l2_standard</link> {
-        __u32                index;
-        v4l2_std_id          id;
-        __u8                 name[24];
-        struct <link linkend="v4l2-fract">v4l2_fract</link>    frameperiod; /* Frames, not fields */
-        __u32                framelines;
-        __u32                reserved[4];
-};
-
-/*
- *      V I D E O       T I M I N G S   D V     P R E S E T
- */
-struct <link linkend="v4l2-dv-preset">v4l2_dv_preset</link> {
-        __u32   preset;
-        __u32   reserved[4];
-};
-
-/*
- *      D V     P R E S E T S   E N U M E R A T I O N
- */
-struct <link linkend="v4l2-dv-enum-preset">v4l2_dv_enum_preset</link> {
-        __u32   index;
-        __u32   preset;
-        __u8    name[32]; /* Name of the preset timing */
-        __u32   width;
-        __u32   height;
-        __u32   reserved[4];
-};
-
-/*
- *      D V     P R E S E T     V A L U E S
- */
-#define         V4L2_DV_INVALID         0
-#define         V4L2_DV_480P59_94       1 /* BT.1362 */
-#define         V4L2_DV_576P50          2 /* BT.1362 */
-#define         V4L2_DV_720P24          3 /* SMPTE 296M */
-#define         V4L2_DV_720P25          4 /* SMPTE 296M */
-#define         V4L2_DV_720P30          5 /* SMPTE 296M */
-#define         V4L2_DV_720P50          6 /* SMPTE 296M */
-#define         V4L2_DV_720P59_94       7 /* SMPTE 274M */
-#define         V4L2_DV_720P60          8 /* SMPTE 274M/296M */
-#define         V4L2_DV_1080I29_97      9 /* BT.1120/ SMPTE 274M */
-#define         V4L2_DV_1080I30         10 /* BT.1120/ SMPTE 274M */
-#define         V4L2_DV_1080I25         11 /* BT.1120 */
-#define         V4L2_DV_1080I50         12 /* SMPTE 296M */
-#define         V4L2_DV_1080I60         13 /* SMPTE 296M */
-#define         V4L2_DV_1080P24         14 /* SMPTE 296M */
-#define         V4L2_DV_1080P25         15 /* SMPTE 296M */
-#define         V4L2_DV_1080P30         16 /* SMPTE 296M */
-#define         V4L2_DV_1080P50         17 /* BT.1120 */
-#define         V4L2_DV_1080P60         18 /* BT.1120 */
-
-/*
- *      D V     B T     T I M I N G S
- */
-
-/* BT.656/BT.1120 timing data */
-struct <link linkend="v4l2-bt-timings">v4l2_bt_timings</link> {
-        __u32   width;          /* width in pixels */
-        __u32   height;         /* height in lines */
-        __u32   interlaced;     /* Interlaced or progressive */
-        __u32   polarities;     /* Positive or negative polarity */
-        __u64   pixelclock;     /* Pixel clock in HZ. Ex. 74.25MHz-&gt;74250000 */
-        __u32   hfrontporch;    /* Horizpontal front porch in pixels */
-        __u32   hsync;          /* Horizontal Sync length in pixels */
-        __u32   hbackporch;     /* Horizontal back porch in pixels */
-        __u32   vfrontporch;    /* Vertical front porch in pixels */
-        __u32   vsync;          /* Vertical Sync length in lines */
-        __u32   vbackporch;     /* Vertical back porch in lines */
-        __u32   il_vfrontporch; /* Vertical front porch for bottom field of
-                                 * interlaced field formats
-                                 */
-        __u32   il_vsync;       /* Vertical sync length for bottom field of
-                                 * interlaced field formats
-                                 */
-        __u32   il_vbackporch;  /* Vertical back porch for bottom field of
-                                 * interlaced field formats
-                                 */
-        __u32   reserved[16];
-} __attribute__ ((packed));
-
-/* Interlaced or progressive format */
-#define V4L2_DV_PROGRESSIVE     0
-#define V4L2_DV_INTERLACED      1
-
-/* Polarities. If bit is not set, it is assumed to be negative polarity */
-#define V4L2_DV_VSYNC_POS_POL   0x00000001
-#define V4L2_DV_HSYNC_POS_POL   0x00000002
-
-
-/* DV timings */
-struct <link linkend="v4l2-dv-timings">v4l2_dv_timings</link> {
-        __u32 type;
-        union {
-                struct <link linkend="v4l2-bt-timings">v4l2_bt_timings</link>  bt;
-                __u32   reserved[32];
-        };
-} __attribute__ ((packed));
-
-/* Values for the type field */
-#define V4L2_DV_BT_656_1120     0       /* BT.656/1120 timing type */
-
-/*
- *      V I D E O   I N P U T S
- */
-struct <link linkend="v4l2-input">v4l2_input</link> {
-        __u32        index;             /*  Which input */
-        __u8         name[32];          /*  Label */
-        __u32        type;              /*  Type of input */
-        __u32        audioset;          /*  Associated audios (bitfield) */
-        __u32        tuner;             /*  Associated tuner */
-        v4l2_std_id  std;
-        __u32        status;
-        __u32        capabilities;
-        __u32        reserved[3];
-};
-
-/*  Values for the 'type' field */
-#define V4L2_INPUT_TYPE_TUNER           1
-#define V4L2_INPUT_TYPE_CAMERA          2
-
-/* field 'status' - general */
-#define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
-#define V4L2_IN_ST_NO_SIGNAL   0x00000002
-#define V4L2_IN_ST_NO_COLOR    0x00000004
-
-/* field 'status' - sensor orientation */
-/* If sensor is mounted upside down set both bits */
-#define V4L2_IN_ST_HFLIP       0x00000010 /* Frames are flipped horizontally */
-#define V4L2_IN_ST_VFLIP       0x00000020 /* Frames are flipped vertically */
-
-/* field 'status' - analog */
-#define V4L2_IN_ST_NO_H_LOCK   0x00000100  /* No horizontal sync lock */
-#define V4L2_IN_ST_COLOR_KILL  0x00000200  /* Color killer is active */
-
-/* field 'status' - digital */
-#define V4L2_IN_ST_NO_SYNC     0x00010000  /* No synchronization lock */
-#define V4L2_IN_ST_NO_EQU      0x00020000  /* No equalizer lock */
-#define V4L2_IN_ST_NO_CARRIER  0x00040000  /* Carrier recovery failed */
-
-/* field 'status' - VCR and set-top box */
-#define V4L2_IN_ST_MACROVISION 0x01000000  /* Macrovision detected */
-#define V4L2_IN_ST_NO_ACCESS   0x02000000  /* Conditional access denied */
-#define V4L2_IN_ST_VTR         0x04000000  /* VTR time constant */
-
-/* capabilities flags */
-#define V4L2_IN_CAP_PRESETS             0x00000001 /* Supports S_DV_PRESET */
-#define V4L2_IN_CAP_CUSTOM_TIMINGS      0x00000002 /* Supports S_DV_TIMINGS */
-#define V4L2_IN_CAP_STD                 0x00000004 /* Supports S_STD */
-
-/*
- *      V I D E O   O U T P U T S
- */
-struct <link linkend="v4l2-output">v4l2_output</link> {
-        __u32        index;             /*  Which output */
-        __u8         name[32];          /*  Label */
-        __u32        type;              /*  Type of output */
-        __u32        audioset;          /*  Associated audios (bitfield) */
-        __u32        modulator;         /*  Associated modulator */
-        v4l2_std_id  std;
-        __u32        capabilities;
-        __u32        reserved[3];
-};
-/*  Values for the 'type' field */
-#define V4L2_OUTPUT_TYPE_MODULATOR              1
-#define V4L2_OUTPUT_TYPE_ANALOG                 2
-#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY       3
-
-/* capabilities flags */
-#define V4L2_OUT_CAP_PRESETS            0x00000001 /* Supports S_DV_PRESET */
-#define V4L2_OUT_CAP_CUSTOM_TIMINGS     0x00000002 /* Supports S_DV_TIMINGS */
-#define V4L2_OUT_CAP_STD                0x00000004 /* Supports S_STD */
-
-/*
- *      C O N T R O L S
- */
-struct <link linkend="v4l2-control">v4l2_control</link> {
-        __u32                id;
-        __s32                value;
-};
-
-struct <link linkend="v4l2-ext-control">v4l2_ext_control</link> {
-        __u32 id;
-        __u32 size;
-        __u32 reserved2[1];
-        union {
-                __s32 value;
-                __s64 value64;
-                char *string;
-        };
-} __attribute__ ((packed));
-
-struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link> {
-        __u32 ctrl_class;
-        __u32 count;
-        __u32 error_idx;
-        __u32 reserved[2];
-        struct <link linkend="v4l2-ext-control">v4l2_ext_control</link> *controls;
-};
-
-/*  Values for ctrl_class field */
-#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
-#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */
-#define V4L2_CTRL_CLASS_CAMERA 0x009a0000       /* Camera class controls */
-#define V4L2_CTRL_CLASS_FM_TX 0x009b0000        /* FM Modulator control class */
-
-#define V4L2_CTRL_ID_MASK         (0x0fffffff)
-#define V4L2_CTRL_ID2CLASS(id)    ((id) &amp; 0x0fff0000UL)
-#define V4L2_CTRL_DRIVER_PRIV(id) (((id) &amp; 0xffff) &gt;= 0x1000)
-
-enum <link linkend="v4l2-ctrl-type">v4l2_ctrl_type</link> {
-        V4L2_CTRL_TYPE_INTEGER       = 1,
-        V4L2_CTRL_TYPE_BOOLEAN       = 2,
-        V4L2_CTRL_TYPE_MENU          = 3,
-        V4L2_CTRL_TYPE_BUTTON        = 4,
-        V4L2_CTRL_TYPE_INTEGER64     = 5,
-        V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
-        V4L2_CTRL_TYPE_STRING        = 7,
-};
-
-/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
-struct <link linkend="v4l2-queryctrl">v4l2_queryctrl</link> {
-        __u32                id;
-        enum <link linkend="v4l2-ctrl-type">v4l2_ctrl_type</link>  type;
-        __u8                 name[32];  /* Whatever */
-        __s32                minimum;   /* Note signedness */
-        __s32                maximum;
-        __s32                step;
-        __s32                default_value;
-        __u32                flags;
-        __u32                reserved[2];
-};
-
-/*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
-struct <link linkend="v4l2-querymenu">v4l2_querymenu</link> {
-        __u32           id;
-        __u32           index;
-        __u8            name[32];       /* Whatever */
-        __u32           reserved;
-};
-
-/*  Control flags  */
-#define V4L2_CTRL_FLAG_DISABLED         0x0001
-#define V4L2_CTRL_FLAG_GRABBED          0x0002
-#define V4L2_CTRL_FLAG_READ_ONLY        0x0004
-#define V4L2_CTRL_FLAG_UPDATE           0x0008
-#define V4L2_CTRL_FLAG_INACTIVE         0x0010
-#define V4L2_CTRL_FLAG_SLIDER           0x0020
-#define V4L2_CTRL_FLAG_WRITE_ONLY       0x0040
-
-/*  Query flag, to be ORed with the control ID */
-#define V4L2_CTRL_FLAG_NEXT_CTRL        0x80000000
-
-/*  User-class control IDs defined by V4L2 */
-#define V4L2_CID_BASE                   (V4L2_CTRL_CLASS_USER | 0x900)
-#define V4L2_CID_USER_BASE              V4L2_CID_BASE
-/*  IDs reserved for driver specific controls */
-#define V4L2_CID_PRIVATE_BASE           0x08000000
-
-#define V4L2_CID_USER_CLASS             (V4L2_CTRL_CLASS_USER | 1)
-#define V4L2_CID_BRIGHTNESS             (V4L2_CID_BASE+0)
-#define V4L2_CID_CONTRAST               (V4L2_CID_BASE+1)
-#define V4L2_CID_SATURATION             (V4L2_CID_BASE+2)
-#define V4L2_CID_HUE                    (V4L2_CID_BASE+3)
-#define V4L2_CID_AUDIO_VOLUME           (V4L2_CID_BASE+5)
-#define V4L2_CID_AUDIO_BALANCE          (V4L2_CID_BASE+6)
-#define V4L2_CID_AUDIO_BASS             (V4L2_CID_BASE+7)
-#define V4L2_CID_AUDIO_TREBLE           (V4L2_CID_BASE+8)
-#define V4L2_CID_AUDIO_MUTE             (V4L2_CID_BASE+9)
-#define V4L2_CID_AUDIO_LOUDNESS         (V4L2_CID_BASE+10)
-#define V4L2_CID_BLACK_LEVEL            (V4L2_CID_BASE+11) /* Deprecated */
-#define V4L2_CID_AUTO_WHITE_BALANCE     (V4L2_CID_BASE+12)
-#define V4L2_CID_DO_WHITE_BALANCE       (V4L2_CID_BASE+13)
-#define V4L2_CID_RED_BALANCE            (V4L2_CID_BASE+14)
-#define V4L2_CID_BLUE_BALANCE           (V4L2_CID_BASE+15)
-#define V4L2_CID_GAMMA                  (V4L2_CID_BASE+16)
-#define V4L2_CID_WHITENESS              (V4L2_CID_GAMMA) /* Deprecated */
-#define V4L2_CID_EXPOSURE               (V4L2_CID_BASE+17)
-#define V4L2_CID_AUTOGAIN               (V4L2_CID_BASE+18)
-#define V4L2_CID_GAIN                   (V4L2_CID_BASE+19)
-#define V4L2_CID_HFLIP                  (V4L2_CID_BASE+20)
-#define V4L2_CID_VFLIP                  (V4L2_CID_BASE+21)
-
-/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */
-#define V4L2_CID_HCENTER                (V4L2_CID_BASE+22)
-#define V4L2_CID_VCENTER                (V4L2_CID_BASE+23)
-
-#define V4L2_CID_POWER_LINE_FREQUENCY   (V4L2_CID_BASE+24)
-enum <link linkend="v4l2-power-line-frequency">v4l2_power_line_frequency</link> {
-        V4L2_CID_POWER_LINE_FREQUENCY_DISABLED  = 0,
-        V4L2_CID_POWER_LINE_FREQUENCY_50HZ      = 1,
-        V4L2_CID_POWER_LINE_FREQUENCY_60HZ      = 2,
-};
-#define V4L2_CID_HUE_AUTO                       (V4L2_CID_BASE+25)
-#define V4L2_CID_WHITE_BALANCE_TEMPERATURE      (V4L2_CID_BASE+26)
-#define V4L2_CID_SHARPNESS                      (V4L2_CID_BASE+27)
-#define V4L2_CID_BACKLIGHT_COMPENSATION         (V4L2_CID_BASE+28)
-#define V4L2_CID_CHROMA_AGC                     (V4L2_CID_BASE+29)
-#define V4L2_CID_COLOR_KILLER                   (V4L2_CID_BASE+30)
-#define V4L2_CID_COLORFX                        (V4L2_CID_BASE+31)
-enum <link linkend="v4l2-colorfx">v4l2_colorfx</link> {
-        V4L2_COLORFX_NONE       = 0,
-        V4L2_COLORFX_BW         = 1,
-        V4L2_COLORFX_SEPIA      = 2,
-        V4L2_COLORFX_NEGATIVE = 3,
-        V4L2_COLORFX_EMBOSS = 4,
-        V4L2_COLORFX_SKETCH = 5,
-        V4L2_COLORFX_SKY_BLUE = 6,
-        V4L2_COLORFX_GRASS_GREEN = 7,
-        V4L2_COLORFX_SKIN_WHITEN = 8,
-        V4L2_COLORFX_VIVID = 9,
-};
-#define V4L2_CID_AUTOBRIGHTNESS                 (V4L2_CID_BASE+32)
-#define V4L2_CID_BAND_STOP_FILTER               (V4L2_CID_BASE+33)
-
-#define V4L2_CID_ROTATE                         (V4L2_CID_BASE+34)
-#define V4L2_CID_BG_COLOR                       (V4L2_CID_BASE+35)
-
-#define V4L2_CID_CHROMA_GAIN                    (V4L2_CID_BASE+36)
-
-#define V4L2_CID_ILLUMINATORS_1                 (V4L2_CID_BASE+37)
-#define V4L2_CID_ILLUMINATORS_2                 (V4L2_CID_BASE+38)
-
-/* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+39)
-
-/*  MPEG-class control IDs defined by V4L2 */
-#define V4L2_CID_MPEG_BASE                      (V4L2_CTRL_CLASS_MPEG | 0x900)
-#define V4L2_CID_MPEG_CLASS                     (V4L2_CTRL_CLASS_MPEG | 1)
-
-/*  MPEG streams */
-#define V4L2_CID_MPEG_STREAM_TYPE               (V4L2_CID_MPEG_BASE+0)
-enum <link linkend="v4l2-mpeg-stream-type">v4l2_mpeg_stream_type</link> {
-        V4L2_MPEG_STREAM_TYPE_MPEG2_PS   = 0, /* MPEG-2 program stream */
-        V4L2_MPEG_STREAM_TYPE_MPEG2_TS   = 1, /* MPEG-2 transport stream */
-        V4L2_MPEG_STREAM_TYPE_MPEG1_SS   = 2, /* MPEG-1 system stream */
-        V4L2_MPEG_STREAM_TYPE_MPEG2_DVD  = 3, /* MPEG-2 DVD-compatible stream */
-        V4L2_MPEG_STREAM_TYPE_MPEG1_VCD  = 4, /* MPEG-1 VCD-compatible stream */
-        V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */
-};
-#define V4L2_CID_MPEG_STREAM_PID_PMT            (V4L2_CID_MPEG_BASE+1)
-#define V4L2_CID_MPEG_STREAM_PID_AUDIO          (V4L2_CID_MPEG_BASE+2)
-#define V4L2_CID_MPEG_STREAM_PID_VIDEO          (V4L2_CID_MPEG_BASE+3)
-#define V4L2_CID_MPEG_STREAM_PID_PCR            (V4L2_CID_MPEG_BASE+4)
-#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO       (V4L2_CID_MPEG_BASE+5)
-#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO       (V4L2_CID_MPEG_BASE+6)
-#define V4L2_CID_MPEG_STREAM_VBI_FMT            (V4L2_CID_MPEG_BASE+7)
-enum <link linkend="v4l2-mpeg-stream-vbi-fmt">v4l2_mpeg_stream_vbi_fmt</link> {
-        V4L2_MPEG_STREAM_VBI_FMT_NONE = 0,  /* No VBI in the MPEG stream */
-        V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1,  /* VBI in private packets, IVTV format */
-};
-
-/*  MPEG audio */
-#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ       (V4L2_CID_MPEG_BASE+100)
-enum <link linkend="v4l2-mpeg-audio-sampling-freq">v4l2_mpeg_audio_sampling_freq</link> {
-        V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
-        V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1,
-        V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2,
-};
-#define V4L2_CID_MPEG_AUDIO_ENCODING            (V4L2_CID_MPEG_BASE+101)
-enum <link linkend="v4l2-mpeg-audio-encoding">v4l2_mpeg_audio_encoding</link> {
-        V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
-        V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
-        V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
-        V4L2_MPEG_AUDIO_ENCODING_AAC     = 3,
-        V4L2_MPEG_AUDIO_ENCODING_AC3     = 4,
-};
-#define V4L2_CID_MPEG_AUDIO_L1_BITRATE          (V4L2_CID_MPEG_BASE+102)
-enum <link linkend="v4l2-mpeg-audio-l1-bitrate">v4l2_mpeg_audio_l1_bitrate</link> {
-        V4L2_MPEG_AUDIO_L1_BITRATE_32K  = 0,
-        V4L2_MPEG_AUDIO_L1_BITRATE_64K  = 1,
-        V4L2_MPEG_AUDIO_L1_BITRATE_96K  = 2,
-        V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3,
-        V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4,
-        V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5,
-        V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6,
-        V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7,
-        V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8,
-        V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9,
-        V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10,
-        V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11,
-        V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12,
-        V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_L2_BITRATE          (V4L2_CID_MPEG_BASE+103)
-enum <link linkend="v4l2-mpeg-audio-l2-bitrate">v4l2_mpeg_audio_l2_bitrate</link> {
-        V4L2_MPEG_AUDIO_L2_BITRATE_32K  = 0,
-        V4L2_MPEG_AUDIO_L2_BITRATE_48K  = 1,
-        V4L2_MPEG_AUDIO_L2_BITRATE_56K  = 2,
-        V4L2_MPEG_AUDIO_L2_BITRATE_64K  = 3,
-        V4L2_MPEG_AUDIO_L2_BITRATE_80K  = 4,
-        V4L2_MPEG_AUDIO_L2_BITRATE_96K  = 5,
-        V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6,
-        V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7,
-        V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8,
-        V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9,
-        V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10,
-        V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11,
-        V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12,
-        V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_L3_BITRATE          (V4L2_CID_MPEG_BASE+104)
-enum <link linkend="v4l2-mpeg-audio-l3-bitrate">v4l2_mpeg_audio_l3_bitrate</link> {
-        V4L2_MPEG_AUDIO_L3_BITRATE_32K  = 0,
-        V4L2_MPEG_AUDIO_L3_BITRATE_40K  = 1,
-        V4L2_MPEG_AUDIO_L3_BITRATE_48K  = 2,
-        V4L2_MPEG_AUDIO_L3_BITRATE_56K  = 3,
-        V4L2_MPEG_AUDIO_L3_BITRATE_64K  = 4,
-        V4L2_MPEG_AUDIO_L3_BITRATE_80K  = 5,
-        V4L2_MPEG_AUDIO_L3_BITRATE_96K  = 6,
-        V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7,
-        V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8,
-        V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9,
-        V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10,
-        V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11,
-        V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12,
-        V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_MODE                (V4L2_CID_MPEG_BASE+105)
-enum <link linkend="v4l2-mpeg-audio-mode">v4l2_mpeg_audio_mode</link> {
-        V4L2_MPEG_AUDIO_MODE_STEREO       = 0,
-        V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1,
-        V4L2_MPEG_AUDIO_MODE_DUAL         = 2,
-        V4L2_MPEG_AUDIO_MODE_MONO         = 3,
-};
-#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION      (V4L2_CID_MPEG_BASE+106)
-enum <link linkend="v4l2-mpeg-audio-mode-extension">v4l2_mpeg_audio_mode_extension</link> {
-        V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4  = 0,
-        V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8  = 1,
-        V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2,
-        V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3,
-};
-#define V4L2_CID_MPEG_AUDIO_EMPHASIS            (V4L2_CID_MPEG_BASE+107)
-enum <link linkend="v4l2-mpeg-audio-emphasis">v4l2_mpeg_audio_emphasis</link> {
-        V4L2_MPEG_AUDIO_EMPHASIS_NONE         = 0,
-        V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1,
-        V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17    = 2,
-};
-#define V4L2_CID_MPEG_AUDIO_CRC                 (V4L2_CID_MPEG_BASE+108)
-enum <link linkend="v4l2-mpeg-audio-crc">v4l2_mpeg_audio_crc</link> {
-        V4L2_MPEG_AUDIO_CRC_NONE  = 0,
-        V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
-};
-#define V4L2_CID_MPEG_AUDIO_MUTE                (V4L2_CID_MPEG_BASE+109)
-#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE         (V4L2_CID_MPEG_BASE+110)
-#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE         (V4L2_CID_MPEG_BASE+111)
-enum <link linkend="v4l2-mpeg-audio-ac3-bitrate">v4l2_mpeg_audio_ac3_bitrate</link> {
-        V4L2_MPEG_AUDIO_AC3_BITRATE_32K  = 0,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_40K  = 1,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_48K  = 2,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_56K  = 3,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_64K  = 4,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_80K  = 5,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_96K  = 6,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
-        V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
-};
-
-/*  MPEG video */
-#define V4L2_CID_MPEG_VIDEO_ENCODING            (V4L2_CID_MPEG_BASE+200)
-enum <link linkend="v4l2-mpeg-video-encoding">v4l2_mpeg_video_encoding</link> {
-        V4L2_MPEG_VIDEO_ENCODING_MPEG_1     = 0,
-        V4L2_MPEG_VIDEO_ENCODING_MPEG_2     = 1,
-        V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
-};
-#define V4L2_CID_MPEG_VIDEO_ASPECT              (V4L2_CID_MPEG_BASE+201)
-enum <link linkend="v4l2-mpeg-video-aspect">v4l2_mpeg_video_aspect</link> {
-        V4L2_MPEG_VIDEO_ASPECT_1x1     = 0,
-        V4L2_MPEG_VIDEO_ASPECT_4x3     = 1,
-        V4L2_MPEG_VIDEO_ASPECT_16x9    = 2,
-        V4L2_MPEG_VIDEO_ASPECT_221x100 = 3,
-};
-#define V4L2_CID_MPEG_VIDEO_B_FRAMES            (V4L2_CID_MPEG_BASE+202)
-#define V4L2_CID_MPEG_VIDEO_GOP_SIZE            (V4L2_CID_MPEG_BASE+203)
-#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE         (V4L2_CID_MPEG_BASE+204)
-#define V4L2_CID_MPEG_VIDEO_PULLDOWN            (V4L2_CID_MPEG_BASE+205)
-#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE        (V4L2_CID_MPEG_BASE+206)
-enum <link linkend="v4l2-mpeg-video-bitrate-mode">v4l2_mpeg_video_bitrate_mode</link> {
-        V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0,
-        V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1,
-};
-#define V4L2_CID_MPEG_VIDEO_BITRATE             (V4L2_CID_MPEG_BASE+207)
-#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK        (V4L2_CID_MPEG_BASE+208)
-#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
-#define V4L2_CID_MPEG_VIDEO_MUTE                (V4L2_CID_MPEG_BASE+210)
-#define V4L2_CID_MPEG_VIDEO_MUTE_YUV            (V4L2_CID_MPEG_BASE+211)
-
-/*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
-#define V4L2_CID_MPEG_CX2341X_BASE                              (V4L2_CTRL_CLASS_MPEG | 0x1000)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE         (V4L2_CID_MPEG_CX2341X_BASE+0)
-enum <link linkend="v4l2-mpeg-cx2341x-video-spatial-filter-mode">v4l2_mpeg_cx2341x_video_spatial_filter_mode</link> {
-        V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0,
-        V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO   = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER              (V4L2_CID_MPEG_CX2341X_BASE+1)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE    (V4L2_CID_MPEG_CX2341X_BASE+2)
-enum <link linkend="luma-spatial-filter-type">v4l2_mpeg_cx2341x_video_luma_spatial_filter_type</link> {
-        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF                  = 0,
-        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR               = 1,
-        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT              = 2,
-        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE      = 3,
-        V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE  (V4L2_CID_MPEG_CX2341X_BASE+3)
-enum <link linkend="chroma-spatial-filter-type">v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type</link> {
-        V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF    = 0,
-        V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE        (V4L2_CID_MPEG_CX2341X_BASE+4)
-enum <link linkend="v4l2-mpeg-cx2341x-video-temporal-filter-mode">v4l2_mpeg_cx2341x_video_temporal_filter_mode</link> {
-        V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0,
-        V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO   = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER             (V4L2_CID_MPEG_CX2341X_BASE+5)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE          (V4L2_CID_MPEG_CX2341X_BASE+6)
-enum <link linkend="v4l2-mpeg-cx2341x-video-median-filter-type">v4l2_mpeg_cx2341x_video_median_filter_type</link> {
-        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF      = 0,
-        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR      = 1,
-        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT     = 2,
-        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3,
-        V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG     = 4,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM   (V4L2_CID_MPEG_CX2341X_BASE+7)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP      (V4L2_CID_MPEG_CX2341X_BASE+8)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP    (V4L2_CID_MPEG_CX2341X_BASE+10)
-#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS         (V4L2_CID_MPEG_CX2341X_BASE+11)
-
-/*  Camera class control IDs */
-#define V4L2_CID_CAMERA_CLASS_BASE      (V4L2_CTRL_CLASS_CAMERA | 0x900)
-#define V4L2_CID_CAMERA_CLASS           (V4L2_CTRL_CLASS_CAMERA | 1)
-
-#define V4L2_CID_EXPOSURE_AUTO                  (V4L2_CID_CAMERA_CLASS_BASE+1)
-enum  <link linkend="v4l2-exposure-auto-type">v4l2_exposure_auto_type</link> {
-        V4L2_EXPOSURE_AUTO = 0,
-        V4L2_EXPOSURE_MANUAL = 1,
-        V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
-        V4L2_EXPOSURE_APERTURE_PRIORITY = 3
-};
-#define V4L2_CID_EXPOSURE_ABSOLUTE              (V4L2_CID_CAMERA_CLASS_BASE+2)
-#define V4L2_CID_EXPOSURE_AUTO_PRIORITY         (V4L2_CID_CAMERA_CLASS_BASE+3)
-
-#define V4L2_CID_PAN_RELATIVE                   (V4L2_CID_CAMERA_CLASS_BASE+4)
-#define V4L2_CID_TILT_RELATIVE                  (V4L2_CID_CAMERA_CLASS_BASE+5)
-#define V4L2_CID_PAN_RESET                      (V4L2_CID_CAMERA_CLASS_BASE+6)
-#define V4L2_CID_TILT_RESET                     (V4L2_CID_CAMERA_CLASS_BASE+7)
-
-#define V4L2_CID_PAN_ABSOLUTE                   (V4L2_CID_CAMERA_CLASS_BASE+8)
-#define V4L2_CID_TILT_ABSOLUTE                  (V4L2_CID_CAMERA_CLASS_BASE+9)
-
-#define V4L2_CID_FOCUS_ABSOLUTE                 (V4L2_CID_CAMERA_CLASS_BASE+10)
-#define V4L2_CID_FOCUS_RELATIVE                 (V4L2_CID_CAMERA_CLASS_BASE+11)
-#define V4L2_CID_FOCUS_AUTO                     (V4L2_CID_CAMERA_CLASS_BASE+12)
-
-#define V4L2_CID_ZOOM_ABSOLUTE                  (V4L2_CID_CAMERA_CLASS_BASE+13)
-#define V4L2_CID_ZOOM_RELATIVE                  (V4L2_CID_CAMERA_CLASS_BASE+14)
-#define V4L2_CID_ZOOM_CONTINUOUS                (V4L2_CID_CAMERA_CLASS_BASE+15)
-
-#define V4L2_CID_PRIVACY                        (V4L2_CID_CAMERA_CLASS_BASE+16)
-
-#define V4L2_CID_IRIS_ABSOLUTE                  (V4L2_CID_CAMERA_CLASS_BASE+17)
-#define V4L2_CID_IRIS_RELATIVE                  (V4L2_CID_CAMERA_CLASS_BASE+18)
-
-/* FM Modulator class control IDs */
-#define V4L2_CID_FM_TX_CLASS_BASE               (V4L2_CTRL_CLASS_FM_TX | 0x900)
-#define V4L2_CID_FM_TX_CLASS                    (V4L2_CTRL_CLASS_FM_TX | 1)
-
-#define V4L2_CID_RDS_TX_DEVIATION               (V4L2_CID_FM_TX_CLASS_BASE + 1)
-#define V4L2_CID_RDS_TX_PI                      (V4L2_CID_FM_TX_CLASS_BASE + 2)
-#define V4L2_CID_RDS_TX_PTY                     (V4L2_CID_FM_TX_CLASS_BASE + 3)
-#define V4L2_CID_RDS_TX_PS_NAME                 (V4L2_CID_FM_TX_CLASS_BASE + 5)
-#define V4L2_CID_RDS_TX_RADIO_TEXT              (V4L2_CID_FM_TX_CLASS_BASE + 6)
-
-#define V4L2_CID_AUDIO_LIMITER_ENABLED          (V4L2_CID_FM_TX_CLASS_BASE + 64)
-#define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME     (V4L2_CID_FM_TX_CLASS_BASE + 65)
-#define V4L2_CID_AUDIO_LIMITER_DEVIATION        (V4L2_CID_FM_TX_CLASS_BASE + 66)
-
-#define V4L2_CID_AUDIO_COMPRESSION_ENABLED      (V4L2_CID_FM_TX_CLASS_BASE + 80)
-#define V4L2_CID_AUDIO_COMPRESSION_GAIN         (V4L2_CID_FM_TX_CLASS_BASE + 81)
-#define V4L2_CID_AUDIO_COMPRESSION_THRESHOLD    (V4L2_CID_FM_TX_CLASS_BASE + 82)
-#define V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME  (V4L2_CID_FM_TX_CLASS_BASE + 83)
-#define V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 84)
-
-#define V4L2_CID_PILOT_TONE_ENABLED             (V4L2_CID_FM_TX_CLASS_BASE + 96)
-#define V4L2_CID_PILOT_TONE_DEVIATION           (V4L2_CID_FM_TX_CLASS_BASE + 97)
-#define V4L2_CID_PILOT_TONE_FREQUENCY           (V4L2_CID_FM_TX_CLASS_BASE + 98)
-
-#define V4L2_CID_TUNE_PREEMPHASIS               (V4L2_CID_FM_TX_CLASS_BASE + 112)
-enum <link linkend="v4l2-preemphasis">v4l2_preemphasis</link> {
-        V4L2_PREEMPHASIS_DISABLED       = 0,
-        V4L2_PREEMPHASIS_50_uS          = 1,
-        V4L2_PREEMPHASIS_75_uS          = 2,
-};
-#define V4L2_CID_TUNE_POWER_LEVEL               (V4L2_CID_FM_TX_CLASS_BASE + 113)
-#define V4L2_CID_TUNE_ANTENNA_CAPACITOR         (V4L2_CID_FM_TX_CLASS_BASE + 114)
-
-/*
- *      T U N I N G
- */
-struct <link linkend="v4l2-tuner">v4l2_tuner</link> {
-        __u32                   index;
-        __u8                    name[32];
-        enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link>    type;
-        __u32                   capability;
-        __u32                   rangelow;
-        __u32                   rangehigh;
-        __u32                   rxsubchans;
-        __u32                   audmode;
-        __s32                   signal;
-        __s32                   afc;
-        __u32                   reserved[4];
-};
-
-struct <link linkend="v4l2-modulator">v4l2_modulator</link> {
-        __u32                   index;
-        __u8                    name[32];
-        __u32                   capability;
-        __u32                   rangelow;
-        __u32                   rangehigh;
-        __u32                   txsubchans;
-        __u32                   reserved[4];
-};
-
-/*  Flags for the 'capability' field */
-#define V4L2_TUNER_CAP_LOW              0x0001
-#define V4L2_TUNER_CAP_NORM             0x0002
-#define V4L2_TUNER_CAP_STEREO           0x0010
-#define V4L2_TUNER_CAP_LANG2            0x0020
-#define V4L2_TUNER_CAP_SAP              0x0020
-#define V4L2_TUNER_CAP_LANG1            0x0040
-#define V4L2_TUNER_CAP_RDS              0x0080
-#define V4L2_TUNER_CAP_RDS_BLOCK_IO     0x0100
-#define V4L2_TUNER_CAP_RDS_CONTROLS     0x0200
-
-/*  Flags for the 'rxsubchans' field */
-#define V4L2_TUNER_SUB_MONO             0x0001
-#define V4L2_TUNER_SUB_STEREO           0x0002
-#define V4L2_TUNER_SUB_LANG2            0x0004
-#define V4L2_TUNER_SUB_SAP              0x0004
-#define V4L2_TUNER_SUB_LANG1            0x0008
-#define V4L2_TUNER_SUB_RDS              0x0010
-
-/*  Values for the 'audmode' field */
-#define V4L2_TUNER_MODE_MONO            0x0000
-#define V4L2_TUNER_MODE_STEREO          0x0001
-#define V4L2_TUNER_MODE_LANG2           0x0002
-#define V4L2_TUNER_MODE_SAP             0x0002
-#define V4L2_TUNER_MODE_LANG1           0x0003
-#define V4L2_TUNER_MODE_LANG1_LANG2     0x0004
-
-struct <link linkend="v4l2-frequency">v4l2_frequency</link> {
-        __u32                 tuner;
-        enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link>  type;
-        __u32                 frequency;
-        __u32                 reserved[8];
-};
-
-struct <link linkend="v4l2-hw-freq-seek">v4l2_hw_freq_seek</link> {
-        __u32                 tuner;
-        enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link>  type;
-        __u32                 seek_upward;
-        __u32                 wrap_around;
-        __u32                 spacing;
-        __u32                 reserved[7];
-};
-
-/*
- *      R D S
- */
-
-struct <link linkend="v4l2-rds-data">v4l2_rds_data</link> {
-        __u8    lsb;
-        __u8    msb;
-        __u8    block;
-} __attribute__ ((packed));
-
-#define V4L2_RDS_BLOCK_MSK       0x7
-#define V4L2_RDS_BLOCK_A         0
-#define V4L2_RDS_BLOCK_B         1
-#define V4L2_RDS_BLOCK_C         2
-#define V4L2_RDS_BLOCK_D         3
-#define V4L2_RDS_BLOCK_C_ALT     4
-#define V4L2_RDS_BLOCK_INVALID   7
-
-#define V4L2_RDS_BLOCK_CORRECTED 0x40
-#define V4L2_RDS_BLOCK_ERROR     0x80
-
-/*
- *      A U D I O
- */
-struct <link linkend="v4l2-audio">v4l2_audio</link> {
-        __u32   index;
-        __u8    name[32];
-        __u32   capability;
-        __u32   mode;
-        __u32   reserved[2];
-};
-
-/*  Flags for the 'capability' field */
-#define V4L2_AUDCAP_STEREO              0x00001
-#define V4L2_AUDCAP_AVL                 0x00002
-
-/*  Flags for the 'mode' field */
-#define V4L2_AUDMODE_AVL                0x00001
-
-struct <link linkend="v4l2-audioout">v4l2_audioout</link> {
-        __u32   index;
-        __u8    name[32];
-        __u32   capability;
-        __u32   mode;
-        __u32   reserved[2];
-};
-
-/*
- *      M P E G   S E R V I C E S
- *
- *      NOTE: EXPERIMENTAL API
- */
-#if 1
-#define V4L2_ENC_IDX_FRAME_I    (0)
-#define V4L2_ENC_IDX_FRAME_P    (1)
-#define V4L2_ENC_IDX_FRAME_B    (2)
-#define V4L2_ENC_IDX_FRAME_MASK (0xf)
-
-struct <link linkend="v4l2-enc-idx-entry">v4l2_enc_idx_entry</link> {
-        __u64 offset;
-        __u64 pts;
-        __u32 length;
-        __u32 flags;
-        __u32 reserved[2];
-};
-
-#define V4L2_ENC_IDX_ENTRIES (64)
-struct <link linkend="v4l2-enc-idx">v4l2_enc_idx</link> {
-        __u32 entries;
-        __u32 entries_cap;
-        __u32 reserved[4];
-        struct <link linkend="v4l2-enc-idx-entry">v4l2_enc_idx_entry</link> entry[V4L2_ENC_IDX_ENTRIES];
-};
-
-
-#define V4L2_ENC_CMD_START      (0)
-#define V4L2_ENC_CMD_STOP       (1)
-#define V4L2_ENC_CMD_PAUSE      (2)
-#define V4L2_ENC_CMD_RESUME     (3)
-
-/* Flags for V4L2_ENC_CMD_STOP */
-#define V4L2_ENC_CMD_STOP_AT_GOP_END    (1 &lt;&lt; 0)
-
-struct <link linkend="v4l2-encoder-cmd">v4l2_encoder_cmd</link> {
-        __u32 cmd;
-        __u32 flags;
-        union {
-                struct {
-                        __u32 data[8];
-                } raw;
-        };
-};
-
-#endif
-
-
-/*
- *      D A T A   S E R V I C E S   ( V B I )
- *
- *      Data services API by Michael Schimek
- */
-
-/* Raw VBI */
-struct <link linkend="v4l2-vbi-format">v4l2_vbi_format</link> {
-        __u32   sampling_rate;          /* in 1 Hz */
-        __u32   offset;
-        __u32   samples_per_line;
-        __u32   sample_format;          /* V4L2_PIX_FMT_* */
-        __s32   start[2];
-        __u32   count[2];
-        __u32   flags;                  /* V4L2_VBI_* */
-        __u32   reserved[2];            /* must be zero */
-};
-
-/*  VBI flags  */
-#define V4L2_VBI_UNSYNC         (1 &lt;&lt; 0)
-#define V4L2_VBI_INTERLACED     (1 &lt;&lt; 1)
-
-/* Sliced VBI
- *
- *    This implements is a proposal V4L2 API to allow SLICED VBI
- * required for some hardware encoders. It should change without
- * notice in the definitive implementation.
- */
-
-struct <link linkend="v4l2-sliced-vbi-format">v4l2_sliced_vbi_format</link> {
-        __u16   service_set;
-        /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
-           service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
-                                 (equals frame lines 313-336 for 625 line video
-                                  standards, 263-286 for 525 line standards) */
-        __u16   service_lines[2][24];
-        __u32   io_size;
-        __u32   reserved[2];            /* must be zero */
-};
-
-/* Teletext World System Teletext
-   (WST), defined on ITU-R BT.653-2 */
-#define V4L2_SLICED_TELETEXT_B          (0x0001)
-/* Video Program System, defined on ETS 300 231*/
-#define V4L2_SLICED_VPS                 (0x0400)
-/* Closed Caption, defined on EIA-608 */
-#define V4L2_SLICED_CAPTION_525         (0x1000)
-/* Wide Screen System, defined on ITU-R BT1119.1 */
-#define V4L2_SLICED_WSS_625             (0x4000)
-
-#define V4L2_SLICED_VBI_525             (V4L2_SLICED_CAPTION_525)
-#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)
-
-struct <link linkend="v4l2-sliced-vbi-cap">v4l2_sliced_vbi_cap</link> {
-        __u16   service_set;
-        /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
-           service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
-                                 (equals frame lines 313-336 for 625 line video
-                                  standards, 263-286 for 525 line standards) */
-        __u16   service_lines[2][24];
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
-        __u32   reserved[3];    /* must be 0 */
-};
-
-struct <link linkend="v4l2-sliced-vbi-data">v4l2_sliced_vbi_data</link> {
-        __u32   id;
-        __u32   field;          /* 0: first field, 1: second field */
-        __u32   line;           /* 1-23 */
-        __u32   reserved;       /* must be 0 */
-        __u8    data[48];
-};
-
-/*
- * Sliced VBI data inserted into MPEG Streams
- */
-
-/*
- * V4L2_MPEG_STREAM_VBI_FMT_IVTV:
- *
- * Structure of payload contained in an MPEG 2 Private Stream 1 PES Packet in an
- * MPEG-2 Program Pack that contains V4L2_MPEG_STREAM_VBI_FMT_IVTV Sliced VBI
- * data
- *
- * Note, the MPEG-2 Program Pack and Private Stream 1 PES packet header
- * definitions are not included here.  See the MPEG-2 specifications for details
- * on these headers.
- */
-
-/* Line type IDs */
-#define V4L2_MPEG_VBI_IVTV_TELETEXT_B     (1)
-#define V4L2_MPEG_VBI_IVTV_CAPTION_525    (4)
-#define V4L2_MPEG_VBI_IVTV_WSS_625        (5)
-#define V4L2_MPEG_VBI_IVTV_VPS            (7)
-
-struct <link linkend="v4l2-mpeg-vbi-itv0-line">v4l2_mpeg_vbi_itv0_line</link> {
-        __u8 id;        /* One of V4L2_MPEG_VBI_IVTV_* above */
-        __u8 data[42];  /* Sliced VBI data for the line */
-} __attribute__ ((packed));
-
-struct <link linkend="v4l2-mpeg-vbi-itv0">v4l2_mpeg_vbi_itv0</link> {
-        __le32 linemask[2]; /* Bitmasks of VBI service lines present */
-        struct <link linkend="v4l2-mpeg-vbi-itv0-line">v4l2_mpeg_vbi_itv0_line</link> line[35];
-} __attribute__ ((packed));
-
-struct <link linkend="v4l2-mpeg-vbi-itv0-1">v4l2_mpeg_vbi_ITV0</link> {
-        struct <link linkend="v4l2-mpeg-vbi-itv0-line">v4l2_mpeg_vbi_itv0_line</link> line[36];
-} __attribute__ ((packed));
-
-#define V4L2_MPEG_VBI_IVTV_MAGIC0       "itv0"
-#define V4L2_MPEG_VBI_IVTV_MAGIC1       "ITV0"
-
-struct <link linkend="v4l2-mpeg-vbi-fmt-ivtv">v4l2_mpeg_vbi_fmt_ivtv</link> {
-        __u8 magic[4];
-        union {
-                struct <link linkend="v4l2-mpeg-vbi-itv0">v4l2_mpeg_vbi_itv0</link> itv0;
-                struct <link linkend="v4l2-mpeg-vbi-itv0-1">v4l2_mpeg_vbi_ITV0</link> ITV0;
-        };
-} __attribute__ ((packed));
-
-/*
- *      A G G R E G A T E   S T R U C T U R E S
- */
-
-/**
- * struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link> - additional, per-plane format definition
- * @sizeimage:          maximum size in bytes required for data, for which
- *                      this plane will be used
- * @bytesperline:       distance in bytes between the leftmost pixels in two
- *                      adjacent lines
- */
-struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link> {
-        __u32           sizeimage;
-        __u16           bytesperline;
-        __u16           reserved[7];
-} __attribute__ ((packed));
-
-/**
- * struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link> - multiplanar format definition
- * @width:              image width in pixels
- * @height:             image height in pixels
- * @pixelformat:        little endian four character code (fourcc)
- * @field:              field order (for interlaced video)
- * @colorspace:         supplemental to pixelformat
- * @plane_fmt:          per-plane information
- * @num_planes:         number of planes for this format
- */
-struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link> {
-        __u32                           width;
-        __u32                           height;
-        __u32                           pixelformat;
-        enum <link linkend="v4l2-field">v4l2_field</link>                 field;
-        enum <link linkend="v4l2-colorspace">v4l2_colorspace</link>            colorspace;
-
-        struct <link linkend="v4l2-plane-pix-format">v4l2_plane_pix_format</link>    plane_fmt[VIDEO_MAX_PLANES];
-        __u8                            num_planes;
-        __u8                            reserved[11];
-} __attribute__ ((packed));
-
-/**
- * struct <link linkend="v4l2-format">v4l2_format</link> - stream data format
- * @type:       type of the data stream
- * @pix:        definition of an image format
- * @pix_mp:     definition of a multiplanar image format
- * @win:        definition of an overlaid image
- * @vbi:        raw VBI capture or output parameters
- * @sliced:     sliced VBI capture or output parameters
- * @raw_data:   placeholder for future extensions and custom formats
- */
-struct <link linkend="v4l2-format">v4l2_format</link> {
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
-        union {
-                struct <link linkend="v4l2-pix-format">v4l2_pix_format</link>          pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
-                struct <link linkend="v4l2-pix-format-mplane">v4l2_pix_format_mplane</link>   pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
-                struct <link linkend="v4l2-window">v4l2_window</link>              win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
-                struct <link linkend="v4l2-vbi-format">v4l2_vbi_format</link>          vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
-                struct <link linkend="v4l2-sliced-vbi-format">v4l2_sliced_vbi_format</link>   sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
-                __u8    raw_data[200];                   /* user-defined */
-        } fmt;
-};
-
-/*      Stream type-dependent parameters
- */
-struct <link linkend="v4l2-streamparm">v4l2_streamparm</link> {
-        enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
-        union {
-                struct <link linkend="v4l2-captureparm">v4l2_captureparm</link> capture;
-                struct <link linkend="v4l2-outputparm">v4l2_outputparm</link>  output;
-                __u8    raw_data[200];  /* user-defined */
-        } parm;
-};
-
-/*
- *      E V E N T S
- */
-
-#define V4L2_EVENT_ALL                          0
-#define V4L2_EVENT_VSYNC                        1
-#define V4L2_EVENT_EOS                          2
-#define V4L2_EVENT_PRIVATE_START                0x08000000
-
-/* Payload for V4L2_EVENT_VSYNC */
-struct <link linkend="v4l2-event-vsync">v4l2_event_vsync</link> {
-        /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
-        __u8 field;
-} __attribute__ ((packed));
-
-struct <link linkend="v4l2-event">v4l2_event</link> {
-        __u32                           type;
-        union {
-                struct <link linkend="v4l2-event-vsync">v4l2_event_vsync</link> vsync;
-                __u8                    data[64];
-        } u;
-        __u32                           pending;
-        __u32                           sequence;
-        struct timespec                 timestamp;
-        __u32                           reserved[9];
-};
-
-struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link> {
-        __u32                           type;
-        __u32                           reserved[7];
-};
-
-/*
- *      A D V A N C E D   D E B U G G I N G
- *
- *      NOTE: EXPERIMENTAL API, NEVER RELY ON THIS IN APPLICATIONS!
- *      FOR DEBUGGING, TESTING AND INTERNAL USE ONLY!
- */
-
-/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */
-
-#define V4L2_CHIP_MATCH_HOST       0  /* Match against chip ID on host (0 for the host) */
-#define V4L2_CHIP_MATCH_I2C_DRIVER 1  /* Match against I2C driver name */
-#define V4L2_CHIP_MATCH_I2C_ADDR   2  /* Match against I2C 7-bit address */
-#define V4L2_CHIP_MATCH_AC97       3  /* Match against anciliary AC97 chip */
-
-struct <link linkend="v4l2-dbg-match">v4l2_dbg_match</link> {
-        __u32 type; /* Match type */
-        union {     /* Match this chip, meaning determined by type */
-                __u32 addr;
-                char name[32];
-        };
-} __attribute__ ((packed));
-
-struct <link linkend="v4l2-dbg-register">v4l2_dbg_register</link> {
-        struct <link linkend="v4l2-dbg-match">v4l2_dbg_match</link> match;
-        __u32 size;     /* register size in bytes */
-        __u64 reg;
-        __u64 val;
-} __attribute__ ((packed));
-
-/* VIDIOC_DBG_G_CHIP_IDENT */
-struct <link linkend="v4l2-dbg-chip-ident">v4l2_dbg_chip_ident</link> {
-        struct <link linkend="v4l2-dbg-match">v4l2_dbg_match</link> match;
-        __u32 ident;       /* chip identifier as specified in &lt;media/v4l2-chip-ident.h&gt; */
-        __u32 revision;    /* chip revision, chip specific */
-} __attribute__ ((packed));
-
-/*
- *      I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
- *
- */
-#define VIDIOC_QUERYCAP          _IOR('V',  0, struct <link linkend="v4l2-capability">v4l2_capability</link>)
-#define VIDIOC_RESERVED           _IO('V',  1)
-#define VIDIOC_ENUM_FMT         _IOWR('V',  2, struct <link linkend="v4l2-fmtdesc">v4l2_fmtdesc</link>)
-#define VIDIOC_G_FMT            _IOWR('V',  4, struct <link linkend="v4l2-format">v4l2_format</link>)
-#define VIDIOC_S_FMT            _IOWR('V',  5, struct <link linkend="v4l2-format">v4l2_format</link>)
-#define VIDIOC_REQBUFS          _IOWR('V',  8, struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link>)
-#define VIDIOC_QUERYBUF         _IOWR('V',  9, struct <link linkend="v4l2-buffer">v4l2_buffer</link>)
-#define VIDIOC_G_FBUF            _IOR('V', 10, struct <link linkend="v4l2-framebuffer">v4l2_framebuffer</link>)
-#define VIDIOC_S_FBUF            _IOW('V', 11, struct <link linkend="v4l2-framebuffer">v4l2_framebuffer</link>)
-#define VIDIOC_OVERLAY           _IOW('V', 14, int)
-#define VIDIOC_QBUF             _IOWR('V', 15, struct <link linkend="v4l2-buffer">v4l2_buffer</link>)
-#define VIDIOC_DQBUF            _IOWR('V', 17, struct <link linkend="v4l2-buffer">v4l2_buffer</link>)
-#define VIDIOC_STREAMON          _IOW('V', 18, int)
-#define VIDIOC_STREAMOFF         _IOW('V', 19, int)
-#define VIDIOC_G_PARM           _IOWR('V', 21, struct <link linkend="v4l2-streamparm">v4l2_streamparm</link>)
-#define VIDIOC_S_PARM           _IOWR('V', 22, struct <link linkend="v4l2-streamparm">v4l2_streamparm</link>)
-#define VIDIOC_G_STD             _IOR('V', 23, v4l2_std_id)
-#define VIDIOC_S_STD             _IOW('V', 24, v4l2_std_id)
-#define VIDIOC_ENUMSTD          _IOWR('V', 25, struct <link linkend="v4l2-standard">v4l2_standard</link>)
-#define VIDIOC_ENUMINPUT        _IOWR('V', 26, struct <link linkend="v4l2-input">v4l2_input</link>)
-#define VIDIOC_G_CTRL           _IOWR('V', 27, struct <link linkend="v4l2-control">v4l2_control</link>)
-#define VIDIOC_S_CTRL           _IOWR('V', 28, struct <link linkend="v4l2-control">v4l2_control</link>)
-#define VIDIOC_G_TUNER          _IOWR('V', 29, struct <link linkend="v4l2-tuner">v4l2_tuner</link>)
-#define VIDIOC_S_TUNER           _IOW('V', 30, struct <link linkend="v4l2-tuner">v4l2_tuner</link>)
-#define VIDIOC_G_AUDIO           _IOR('V', 33, struct <link linkend="v4l2-audio">v4l2_audio</link>)
-#define VIDIOC_S_AUDIO           _IOW('V', 34, struct <link linkend="v4l2-audio">v4l2_audio</link>)
-#define VIDIOC_QUERYCTRL        _IOWR('V', 36, struct <link linkend="v4l2-queryctrl">v4l2_queryctrl</link>)
-#define VIDIOC_QUERYMENU        _IOWR('V', 37, struct <link linkend="v4l2-querymenu">v4l2_querymenu</link>)
-#define VIDIOC_G_INPUT           _IOR('V', 38, int)
-#define VIDIOC_S_INPUT          _IOWR('V', 39, int)
-#define VIDIOC_G_OUTPUT          _IOR('V', 46, int)
-#define VIDIOC_S_OUTPUT         _IOWR('V', 47, int)
-#define VIDIOC_ENUMOUTPUT       _IOWR('V', 48, struct <link linkend="v4l2-output">v4l2_output</link>)
-#define VIDIOC_G_AUDOUT          _IOR('V', 49, struct <link linkend="v4l2-audioout">v4l2_audioout</link>)
-#define VIDIOC_S_AUDOUT          _IOW('V', 50, struct <link linkend="v4l2-audioout">v4l2_audioout</link>)
-#define VIDIOC_G_MODULATOR      _IOWR('V', 54, struct <link linkend="v4l2-modulator">v4l2_modulator</link>)
-#define VIDIOC_S_MODULATOR       _IOW('V', 55, struct <link linkend="v4l2-modulator">v4l2_modulator</link>)
-#define VIDIOC_G_FREQUENCY      _IOWR('V', 56, struct <link linkend="v4l2-frequency">v4l2_frequency</link>)
-#define VIDIOC_S_FREQUENCY       _IOW('V', 57, struct <link linkend="v4l2-frequency">v4l2_frequency</link>)
-#define VIDIOC_CROPCAP          _IOWR('V', 58, struct <link linkend="v4l2-cropcap">v4l2_cropcap</link>)
-#define VIDIOC_G_CROP           _IOWR('V', 59, struct <link linkend="v4l2-crop">v4l2_crop</link>)
-#define VIDIOC_S_CROP            _IOW('V', 60, struct <link linkend="v4l2-crop">v4l2_crop</link>)
-#define VIDIOC_G_JPEGCOMP        _IOR('V', 61, struct <link linkend="v4l2-jpegcompression">v4l2_jpegcompression</link>)
-#define VIDIOC_S_JPEGCOMP        _IOW('V', 62, struct <link linkend="v4l2-jpegcompression">v4l2_jpegcompression</link>)
-#define VIDIOC_QUERYSTD          _IOR('V', 63, v4l2_std_id)
-#define VIDIOC_TRY_FMT          _IOWR('V', 64, struct <link linkend="v4l2-format">v4l2_format</link>)
-#define VIDIOC_ENUMAUDIO        _IOWR('V', 65, struct <link linkend="v4l2-audio">v4l2_audio</link>)
-#define VIDIOC_ENUMAUDOUT       _IOWR('V', 66, struct <link linkend="v4l2-audioout">v4l2_audioout</link>)
-#define VIDIOC_G_PRIORITY        _IOR('V', 67, enum <link linkend="v4l2-priority">v4l2_priority</link>)
-#define VIDIOC_S_PRIORITY        _IOW('V', 68, enum <link linkend="v4l2-priority">v4l2_priority</link>)
-#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct <link linkend="v4l2-sliced-vbi-cap">v4l2_sliced_vbi_cap</link>)
-#define VIDIOC_LOG_STATUS         _IO('V', 70)
-#define VIDIOC_G_EXT_CTRLS      _IOWR('V', 71, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
-#define VIDIOC_S_EXT_CTRLS      _IOWR('V', 72, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
-#define VIDIOC_TRY_EXT_CTRLS    _IOWR('V', 73, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
-#if 1
-#define VIDIOC_ENUM_FRAMESIZES  _IOWR('V', 74, struct <link linkend="v4l2-frmsizeenum">v4l2_frmsizeenum</link>)
-#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct <link linkend="v4l2-frmivalenum">v4l2_frmivalenum</link>)
-#define VIDIOC_G_ENC_INDEX       _IOR('V', 76, struct <link linkend="v4l2-enc-idx">v4l2_enc_idx</link>)
-#define VIDIOC_ENCODER_CMD      _IOWR('V', 77, struct <link linkend="v4l2-encoder-cmd">v4l2_encoder_cmd</link>)
-#define VIDIOC_TRY_ENCODER_CMD  _IOWR('V', 78, struct <link linkend="v4l2-encoder-cmd">v4l2_encoder_cmd</link>)
-#endif
-
-#if 1
-/* Experimental, meant for debugging, testing and internal use.
-   Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined.
-   You must be root to use these ioctls. Never use these in applications! */
-#define VIDIOC_DBG_S_REGISTER    _IOW('V', 79, struct <link linkend="v4l2-dbg-register">v4l2_dbg_register</link>)
-#define VIDIOC_DBG_G_REGISTER   _IOWR('V', 80, struct <link linkend="v4l2-dbg-register">v4l2_dbg_register</link>)
-
-/* Experimental, meant for debugging, testing and internal use.
-   Never use this ioctl in applications! */
-#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct <link linkend="v4l2-dbg-chip-ident">v4l2_dbg_chip_ident</link>)
-#endif
-
-#define VIDIOC_S_HW_FREQ_SEEK    _IOW('V', 82, struct <link linkend="v4l2-hw-freq-seek">v4l2_hw_freq_seek</link>)
-#define VIDIOC_ENUM_DV_PRESETS  _IOWR('V', 83, struct <link linkend="v4l2-dv-enum-preset">v4l2_dv_enum_preset</link>)
-#define VIDIOC_S_DV_PRESET      _IOWR('V', 84, struct <link linkend="v4l2-dv-preset">v4l2_dv_preset</link>)
-#define VIDIOC_G_DV_PRESET      _IOWR('V', 85, struct <link linkend="v4l2-dv-preset">v4l2_dv_preset</link>)
-#define VIDIOC_QUERY_DV_PRESET  _IOR('V',  86, struct <link linkend="v4l2-dv-preset">v4l2_dv_preset</link>)
-#define VIDIOC_S_DV_TIMINGS     _IOWR('V', 87, struct <link linkend="v4l2-dv-timings">v4l2_dv_timings</link>)
-#define VIDIOC_G_DV_TIMINGS     _IOWR('V', 88, struct <link linkend="v4l2-dv-timings">v4l2_dv_timings</link>)
-#define VIDIOC_DQEVENT           _IOR('V', 89, struct <link linkend="v4l2-event">v4l2_event</link>)
-#define VIDIOC_SUBSCRIBE_EVENT   _IOW('V', 90, struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link>)
-#define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link>)
-
-/* Reminder: when adding new ioctls please add support for them to
-   drivers/media/video/v4l2-compat-ioctl32.c as well! */
-
-#define BASE_VIDIOC_PRIVATE     192             /* 192-255 are private */
-
-#endif /* __LINUX_VIDEODEV2_H */
-</programlisting>
diff --git a/Documentation/DocBook/v4l/vidioc-subscribe-event.xml b/Documentation/DocBook/v4l/vidioc-subscribe-event.xml
deleted file mode 100644
index 8b50179..0000000
--- a/Documentation/DocBook/v4l/vidioc-subscribe-event.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<refentry id="vidioc-subscribe-event">
-  <refmeta>
-    <refentrytitle>ioctl VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</refentrytitle>
-    &manvol;
-  </refmeta>
-
-  <refnamediv>
-    <refname>VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</refname>
-    <refpurpose>Subscribe or unsubscribe event</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-      <funcprototype>
-	<funcdef>int <function>ioctl</function></funcdef>
-	<paramdef>int <parameter>fd</parameter></paramdef>
-	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>struct v4l2_event_subscription
-*<parameter>argp</parameter></paramdef>
-      </funcprototype>
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Arguments</title>
-
-    <variablelist>
-      <varlistentry>
-	<term><parameter>fd</parameter></term>
-	<listitem>
-	  <para>&fd;</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>request</parameter></term>
-	<listitem>
-	  <para>VIDIOC_SUBSCRIBE_EVENT, VIDIOC_UNSUBSCRIBE_EVENT</para>
-	</listitem>
-      </varlistentry>
-      <varlistentry>
-	<term><parameter>argp</parameter></term>
-	<listitem>
-	  <para></para>
-	</listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>Description</title>
-
-    <para>Subscribe or unsubscribe V4L2 event. Subscribed events are
-    dequeued by using the &VIDIOC-DQEVENT; ioctl.</para>
-
-    <table frame="none" pgwide="1" id="v4l2-event-subscription">
-      <title>struct <structname>v4l2_event_subscription</structname></title>
-      <tgroup cols="3">
-	&cs-str;
-	<tbody valign="top">
-	  <row>
-	    <entry>__u32</entry>
-	    <entry><structfield>type</structfield></entry>
-	    <entry>Type of the event.</entry>
-	  </row>
-	  <row>
-	    <entry>__u32</entry>
-	    <entry><structfield>reserved</structfield>[7]</entry>
-	    <entry>Reserved for future extensions. Drivers and applications
-	    must set the array to zero.</entry>
-	  </row>
-	</tbody>
-      </tgroup>
-    </table>
-
-    <table frame="none" pgwide="1" id="event-type">
-      <title>Event Types</title>
-      <tgroup cols="3">
-	&cs-def;
-	<tbody valign="top">
-	  <row>
-	    <entry><constant>V4L2_EVENT_ALL</constant></entry>
-	    <entry>0</entry>
-	    <entry>All events. V4L2_EVENT_ALL is valid only for
-	    VIDIOC_UNSUBSCRIBE_EVENT for unsubscribing all events at once.
-	    </entry>
-	  </row>
-	  <row>
-	    <entry><constant>V4L2_EVENT_VSYNC</constant></entry>
-	    <entry>1</entry>
-	    <entry>This event is triggered on the vertical sync.
-	    This event has &v4l2-event-vsync; associated with it.
-	    </entry>
-	  </row>
-	  <row>
-	    <entry><constant>V4L2_EVENT_EOS</constant></entry>
-	    <entry>2</entry>
-	    <entry>This event is triggered when the end of a stream is reached.
-	    This is typically used with MPEG decoders to report to the application
-	    when the last of the MPEG stream has been decoded.
-	    </entry>
-	  </row>
-	  <row>
-	    <entry><constant>V4L2_EVENT_PRIVATE_START</constant></entry>
-	    <entry>0x08000000</entry>
-	    <entry>Base event number for driver-private events.</entry>
-	  </row>
-	</tbody>
-      </tgroup>
-    </table>
-
-    <table frame="none" pgwide="1" id="v4l2-event-vsync">
-      <title>struct <structname>v4l2_event_vsync</structname></title>
-      <tgroup cols="3">
-	&cs-str;
-	<tbody valign="top">
-	  <row>
-	    <entry>__u8</entry>
-	    <entry><structfield>field</structfield></entry>
-	    <entry>The upcoming field. See &v4l2-field;.</entry>
-	  </row>
-	</tbody>
-      </tgroup>
-    </table>
-
-  </refsect1>
-</refentry>
-<!--
-Local Variables:
-mode: sgml
-sgml-parent-document: "v4l2.sgml"
-indent-tabs-mode: nil
-End:
--->
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
index 06eb6d9..6f3c598 100644
--- a/Documentation/cgroups/memory.txt
+++ b/Documentation/cgroups/memory.txt
@@ -380,7 +380,7 @@
 
 5.2 stat file
 
-memory.stat file includes following statistics
+5.2.1 memory.stat file includes following statistics
 
 # per-memory cgroup local status
 cache		- # of bytes of page cache memory.
@@ -438,6 +438,89 @@
 	 file_mapped is accounted only when the memory cgroup is owner of page
 	 cache.)
 
+5.2.2 memory.vmscan_stat
+
+memory.vmscan_stat includes statistics information for memory scanning and
+freeing, reclaiming. The statistics shows memory scanning information since
+memory cgroup creation and can be reset to 0 by writing 0 as
+
+ #echo 0 > ../memory.vmscan_stat
+
+This file contains following statistics.
+
+[param]_[file_or_anon]_pages_by_[reason]_[under_heararchy]
+[param]_elapsed_ns_by_[reason]_[under_hierarchy]
+
+For example,
+
+  scanned_file_pages_by_limit indicates the number of scanned
+  file pages at vmscan.
+
+Now, 3 parameters are supported
+
+  scanned - the number of pages scanned by vmscan
+  rotated - the number of pages activated at vmscan
+  freed   - the number of pages freed by vmscan
+
+If "rotated" is high against scanned/freed, the memcg seems busy.
+
+Now, 2 reason are supported
+
+  limit - the memory cgroup's limit
+  system - global memory pressure + softlimit
+           (global memory pressure not under softlimit is not handled now)
+
+When under_hierarchy is added in the tail, the number indicates the
+total memcg scan of its children and itself.
+
+elapsed_ns is a elapsed time in nanosecond. This may include sleep time
+and not indicates CPU usage. So, please take this as just showing
+latency.
+
+Here is an example.
+
+# cat /cgroup/memory/A/memory.vmscan_stat
+scanned_pages_by_limit 9471864
+scanned_anon_pages_by_limit 6640629
+scanned_file_pages_by_limit 2831235
+rotated_pages_by_limit 4243974
+rotated_anon_pages_by_limit 3971968
+rotated_file_pages_by_limit 272006
+freed_pages_by_limit 2318492
+freed_anon_pages_by_limit 962052
+freed_file_pages_by_limit 1356440
+elapsed_ns_by_limit 351386416101
+scanned_pages_by_system 0
+scanned_anon_pages_by_system 0
+scanned_file_pages_by_system 0
+rotated_pages_by_system 0
+rotated_anon_pages_by_system 0
+rotated_file_pages_by_system 0
+freed_pages_by_system 0
+freed_anon_pages_by_system 0
+freed_file_pages_by_system 0
+elapsed_ns_by_system 0
+scanned_pages_by_limit_under_hierarchy 9471864
+scanned_anon_pages_by_limit_under_hierarchy 6640629
+scanned_file_pages_by_limit_under_hierarchy 2831235
+rotated_pages_by_limit_under_hierarchy 4243974
+rotated_anon_pages_by_limit_under_hierarchy 3971968
+rotated_file_pages_by_limit_under_hierarchy 272006
+freed_pages_by_limit_under_hierarchy 2318492
+freed_anon_pages_by_limit_under_hierarchy 962052
+freed_file_pages_by_limit_under_hierarchy 1356440
+elapsed_ns_by_limit_under_hierarchy 351386416101
+scanned_pages_by_system_under_hierarchy 0
+scanned_anon_pages_by_system_under_hierarchy 0
+scanned_file_pages_by_system_under_hierarchy 0
+rotated_pages_by_system_under_hierarchy 0
+rotated_anon_pages_by_system_under_hierarchy 0
+rotated_file_pages_by_system_under_hierarchy 0
+freed_pages_by_system_under_hierarchy 0
+freed_anon_pages_by_system_under_hierarchy 0
+freed_file_pages_by_system_under_hierarchy 0
+elapsed_ns_by_system_under_hierarchy 0
+
 5.3 swappiness
 
 Similar to /proc/sys/vm/swappiness, but affecting a hierarchy of groups only.
diff --git a/Documentation/devicetree/bindings/arm/arm-boards b/Documentation/devicetree/bindings/arm/arm-boards
new file mode 100644
index 0000000..91f2614
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/arm-boards
@@ -0,0 +1,20 @@
+ARM Versatile Application and Platform Baseboards
+-------------------------------------------------
+ARM's development hardware platform with connectors for customizable
+core tiles.  The hardware configuration of the Versatile boards is
+highly customizable.
+
+Required properties (in root node):
+	compatible = "arm,versatile-ab";  /* Application baseboard */
+	compatible = "arm,versatile-pb";  /* Platform baseboard */
+
+Interrupt controllers:
+- VIC required properties:
+	compatible = "arm,versatile-vic";
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+- SIC required properties:
+	compatible = "arm,versatile-sic";
+	interrupt-controller;
+	#interrupt-cells = <1>;
diff --git a/Documentation/devicetree/bindings/arm/sirf.txt b/Documentation/devicetree/bindings/arm/sirf.txt
new file mode 100644
index 0000000..6b07f65
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sirf.txt
@@ -0,0 +1,3 @@
+prima2 "cb" evalutation board
+Required root node properties:
+    - compatible = "sirf,prima2-cb", "sirf,prima2";
diff --git a/Documentation/devicetree/bindings/arm/xilinx.txt b/Documentation/devicetree/bindings/arm/xilinx.txt
new file mode 100644
index 0000000..6f1ed830
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/xilinx.txt
@@ -0,0 +1,7 @@
+Xilinx Zynq EP107 Emulation Platform board
+
+This board is an emulation platform for the Zynq product which is
+based on an ARM Cortex A9 processor.
+
+Required root node properties:
+    - compatible = "xlnx,zynq-ep107";
diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt
new file mode 100644
index 0000000..d1e3f44
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt
@@ -0,0 +1,17 @@
+* Freescale Smart Direct Memory Access (SDMA) Controller for i.MX
+
+Required properties:
+- compatible : Should be "fsl,<chip>-sdma"
+- reg : Should contain SDMA registers location and length
+- interrupts : Should contain SDMA interrupt
+- fsl,sdma-ram-script-name : Should contain the full path of SDMA RAM
+  scripts firmware
+
+Examples:
+
+sdma@83fb0000 {
+	compatible = "fsl,imx51-sdma", "fsl,imx35-sdma";
+	reg = <0x83fb0000 0x4000>;
+	interrupts = <6>;
+	fsl,sdma-ram-script-name = "sdma-imx51.bin";
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
new file mode 100644
index 0000000..7190c99
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -0,0 +1,36 @@
+Device-Tree bindings for input/gpio_keys.c keyboard driver
+
+Required properties:
+	- compatible = "gpio-keys";
+
+Optional properties:
+	- autorepeat: Boolean, Enable auto repeat feature of Linux input
+	  subsystem.
+
+Each button (key) is represented as a sub-node of "gpio-keys":
+Subnode properties:
+
+	- gpios: OF devcie-tree gpio specificatin.
+	- label: Descriptive name of the key.
+	- linux,code: Keycode to emit.
+
+Optional subnode-properties:
+	- linux,input-type: Specify event type this button/key generates.
+	  If not specified defaults to <1> == EV_KEY.
+	- debounce-interval: Debouncing interval time in milliseconds.
+	  If not specified defaults to 5.
+	- gpio-key,wakeup: Boolean, button can wake-up the system.
+
+Example nodes:
+
+	gpio_keys {
+			compatible = "gpio-keys";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			autorepeat;
+			button@21 {
+				label = "GPIO Key UP";
+				linux,code = <103>;
+				gpios = <&gpio1 0 1>;
+			};
+			...
diff --git a/Documentation/devicetree/bindings/i2c/arm-versatile.txt b/Documentation/devicetree/bindings/i2c/arm-versatile.txt
new file mode 100644
index 0000000..361d31c
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/arm-versatile.txt
@@ -0,0 +1,10 @@
+i2c Controller on ARM Versatile platform:
+
+Required properties:
+- compatible : Must be "arm,versatile-i2c";
+- reg
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- Child nodes conforming to i2c bus binding
diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
new file mode 100644
index 0000000..ab22fe6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
@@ -0,0 +1,34 @@
+* Freescale Enhanced Secure Digital Host Controller (eSDHC) for i.MX
+
+The Enhanced Secure Digital Host Controller on Freescale i.MX family
+provides an interface for MMC, SD, and SDIO types of memory cards.
+
+Required properties:
+- compatible : Should be "fsl,<chip>-esdhc"
+- reg : Should contain eSDHC registers location and length
+- interrupts : Should contain eSDHC interrupt
+
+Optional properties:
+- fsl,card-wired : Indicate the card is wired to host permanently
+- fsl,cd-internal : Indicate to use controller internal card detection
+- fsl,wp-internal : Indicate to use controller internal write protection
+- cd-gpios : Specify GPIOs for card detection
+- wp-gpios : Specify GPIOs for write protection
+
+Examples:
+
+esdhc@70004000 {
+	compatible = "fsl,imx51-esdhc";
+	reg = <0x70004000 0x4000>;
+	interrupts = <1>;
+	fsl,cd-internal;
+	fsl,wp-internal;
+};
+
+esdhc@70008000 {
+	compatible = "fsl,imx51-esdhc";
+	reg = <0x70008000 0x4000>;
+	interrupts = <2>;
+	cd-gpios = <&gpio0 6 0>; /* GPIO1_6 */
+	wp-gpios = <&gpio0 5 0>; /* GPIO1_5 */
+};
diff --git a/Documentation/devicetree/bindings/mtd/arm-versatile.txt b/Documentation/devicetree/bindings/mtd/arm-versatile.txt
new file mode 100644
index 0000000..476845d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/arm-versatile.txt
@@ -0,0 +1,8 @@
+Flash device on ARM Versatile board
+
+Required properties:
+- compatible : must be "arm,versatile-flash";
+- bank-width : width in bytes of flash interface.
+
+Optional properties:
+- Subnode partition map from mtd flash binding
diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
new file mode 100644
index 0000000..de43951
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -0,0 +1,24 @@
+* Freescale Fast Ethernet Controller (FEC)
+
+Required properties:
+- compatible : Should be "fsl,<soc>-fec"
+- reg : Address and length of the register set for the device
+- interrupts : Should contain fec interrupt
+- phy-mode : String, operation mode of the PHY interface.
+  Supported values are: "mii", "gmii", "sgmii", "tbi", "rmii",
+  "rgmii", "rgmii-id", "rgmii-rxid", "rgmii-txid", "rtbi", "smii".
+- phy-reset-gpios : Should specify the gpio for phy reset
+
+Optional properties:
+- local-mac-address : 6 bytes, mac address
+
+Example:
+
+fec@83fec000 {
+	compatible = "fsl,imx51-fec", "fsl,imx27-fec";
+	reg = <0x83fec000 0x4000>;
+	interrupts = <87>;
+	phy-mode = "mii";
+	phy-reset-gpios = <&gpio1 14 0>; /* GPIO2_14 */
+	local-mac-address = [00 04 9F 01 1B B9];
+};
diff --git a/Documentation/devicetree/bindings/net/smsc-lan91c111.txt b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt
new file mode 100644
index 0000000..953049b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt
@@ -0,0 +1,10 @@
+SMSC LAN91c111 Ethernet mac
+
+Required properties:
+- compatible = "smsc,lan91c111";
+- reg : physical address and size of registers
+- interrupts : interrupt connection
+
+Optional properties:
+- phy-device : phandle to Ethernet phy
+- local-mac-address : Ethernet mac address to use
diff --git a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
new file mode 100644
index 0000000..a2891ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt
@@ -0,0 +1,5 @@
+OLPC XO-1 RTC
+~~~~~~~~~~~~~
+
+Required properties:
+ - compatible : "olpc,xo1-rtc"
diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
new file mode 100644
index 0000000..a9c0406
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
@@ -0,0 +1,19 @@
+* Freescale i.MX Universal Asynchronous Receiver/Transmitter (UART)
+
+Required properties:
+- compatible : Should be "fsl,<soc>-uart"
+- reg : Address and length of the register set for the device
+- interrupts : Should contain uart interrupt
+
+Optional properties:
+- fsl,uart-has-rtscts : Indicate the uart has rts and cts
+- fsl,irda-mode : Indicate the uart supports irda mode
+
+Example:
+
+uart@73fbc000 {
+	compatible = "fsl,imx51-uart", "fsl,imx21-uart";
+	reg = <0x73fbc000 0x4000>;
+	interrupts = <31>;
+	fsl,uart-has-rtscts;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
new file mode 100644
index 0000000..2144af1
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
@@ -0,0 +1,14 @@
+* Freescale i.MX Watchdog Timer (WDT) Controller
+
+Required properties:
+- compatible : Should be "fsl,<soc>-wdt"
+- reg : Should contain WDT registers location and length
+- interrupts : Should contain WDT interrupt
+
+Examples:
+
+wdt@73f98000 {
+	compatible = "fsl,imx51-wdt", "fsl,imx21-wdt";
+	reg = <0x73f98000 0x4000>;
+	interrupts = <58>;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
new file mode 100644
index 0000000..79ead82
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
@@ -0,0 +1,11 @@
+* Samsung's Watchdog Timer Controller
+
+The Samsung's Watchdog controller is used for resuming system operation
+after a preset amount of time during which the WDT reset event has not
+occured.
+
+Required properties:
+- compatible : should be "samsung,s3c2410-wdt"
+- reg : base physical address of the controller and length of memory mapped
+	region.
+- interrupts : interrupt number to the cpu.
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
old mode 100644
new mode 100755
index 3348d31..c466f58
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -27,7 +27,7 @@
 		"or51211", "or51132_qam", "or51132_vsb", "bluebird",
 		"opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718",
 		"af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395",
-		"lme2510c_s7395_old");
+		"lme2510c_s7395_old", "drxk", "drxk_terratec_h5");
 
 # Check args
 syntax() if (scalar(@ARGV) != 1);
@@ -634,6 +634,37 @@
     $outfile;
 }
 
+sub drxk {
+    my $url = "http://l4m-daten.de/files/";
+    my $zipfile = "DDTuner.zip";
+    my $hash = "f5a37b9a20a3534997997c0b1382a3e5";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+    my $drvfile = "DDTuner.sys";
+    my $fwfile = "drxk_a3.mc";
+
+    checkstandard();
+
+    wgetfile($zipfile, $url . $zipfile);
+    verify($zipfile, $hash);
+    unzip($zipfile, $tmpdir);
+    extract("$tmpdir/$drvfile", 0x14dd8, 15634, "$fwfile");
+
+    "$fwfile"
+}
+
+sub drxk_terratec_h5 {
+    my $url = "http://www.linuxtv.org/downloads/firmware/";
+    my $hash = "19000dada8e2741162ccc50cc91fa7f1";
+    my $fwfile = "dvb-usb-terratec-h5-drxk.fw";
+
+    checkstandard();
+
+    wgetfile($fwfile, $url . $fwfile);
+    verify($fwfile, $hash);
+
+    "$fwfile"
+}
+
 # ---------------------------------------------------------------
 # Utilities
 
diff --git a/Documentation/fb/modedb.txt b/Documentation/fb/modedb.txt
index ec4dee7..16aa084 100644
--- a/Documentation/fb/modedb.txt
+++ b/Documentation/fb/modedb.txt
@@ -20,7 +20,7 @@
 
 Valid mode specifiers (mode_option argument):
 
-    <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m]
+    <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
     <name>[-<bpp>][@<refresh>]
 
 with <xres>, <yres>, <bpp> and <refresh> decimal numbers and <name> a string.
@@ -36,6 +36,21 @@
 
        Sample usage: 1024x768M@60m - CVT timing with margins
 
+DRM drivers also add options to enable or disable outputs:
+
+'e' will force the display to be enabled, i.e. it will override the detection
+if a display is connected. 'D' will force the display to be enabled and use
+digital output. This is useful for outputs that have both analog and digital
+signals (e.g. HDMI and DVI-I). For other outputs it behaves like 'e'. If 'd'
+is specified the output is disabled.
+
+You can additionally specify which output the options matches to.
+To force the VGA output to be enabled and drive a specific mode say:
+    video=VGA-1:1280x1024@60me
+
+Specifying the option multiple times for different ports is possible, e.g.:
+    video=LVDS-1:d video=HDMI-1:D
+
 ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo *****
 
 What is the VESA(TM) Coordinated Video Timings (CVT)?
@@ -132,5 +147,5 @@
     tridentfb	- Trident (Cyber)blade chipset frame buffer
     vt8623fb	- VIA 8623 frame buffer
 
-BTW, only a few drivers use this at the moment. Others are to follow
-(feel free to send patches).
+BTW, only a few fb drivers use this at the moment. Others are to follow
+(feel free to send patches). The DRM drivers also support this.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index f10014f..ea0bace 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -460,13 +460,6 @@
 
 ----------------------------
 
-What: 	DMA_xxBIT_MASK macros
-When:	Jun 2011
-Why:	DMA_xxBIT_MASK macros were replaced with DMA_BIT_MASK() macros.
-Who:	FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-
-----------------------------
-
 What:	iwlwifi disable_hw_scan module parameters
 When:	3.0
 Why:	Hareware scan is the prefer method for iwlwifi devices for
@@ -534,6 +527,41 @@
 
 ----------------------------
 
+What:	Support for driver specific ioctls in the pwc driver (everything
+	defined in media/pwc-ioctl.h)
+When:	3.3
+Why:	This stems from the v4l1 era, with v4l2 everything can be done with
+	standardized v4l2 API calls
+Who:	Hans de Goede <hdegoede@redhat.com>
+
+----------------------------
+
+What:	Driver specific sysfs API in the pwc driver
+When:	3.3
+Why:	Setting pan/tilt should be done with v4l2 controls, like with other
+	cams. The button is available as a standard input device
+Who:	Hans de Goede <hdegoede@redhat.com>
+
+----------------------------
+
+What:	Driver specific use of pixfmt.priv in the pwc driver
+When:	3.3
+Why:	The .priv field never was intended for this, setting a framerate is
+	support using the standardized S_PARM ioctl
+Who:	Hans de Goede <hdegoede@redhat.com>
+
+----------------------------
+
+What:	Software emulation of arbritary resolutions in the pwc driver
+When:	3.3
+Why:	The pwc driver claims to support any resolution between 160x120
+	and 640x480, but emulates this by simply drawing a black border
+	around the image. Userspace can draw its own black border if it
+	really wants one.
+Who:	Hans de Goede <hdegoede@redhat.com>
+
+----------------------------
+
 What:	For VIDIOC_S_FREQUENCY the type field must match the device node's type.
 	If not, return -EINVAL.
 When:	3.2
diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt
index 272f80d..22f3a0e 100644
--- a/Documentation/filesystems/ext3.txt
+++ b/Documentation/filesystems/ext3.txt
@@ -147,15 +147,6 @@
 			package for more details
 			(http://sourceforge.net/projects/linuxquota).
 
-bh		(*)	ext3 associates buffer heads to data pages to
-nobh			(a) cache disk block mapping information
-			(b) link pages into transaction to provide
-			    ordering guarantees.
-			"bh" option forces use of buffer heads.
-			"nobh" option tries to avoid associating buffer
-			heads (supported only for "writeback" mode).
-
-
 Specification
 =============
 Ext3 shares all disk implementation with the ext2 filesystem, and adds
@@ -227,5 +218,5 @@
 programs: 	http://e2fsprogs.sourceforge.net/
 		http://ext2resize.sourceforge.net
 
-useful links:	http://www.ibm.com/developerworks/library/l-fs7.html
-		http://www.ibm.com/developerworks/library/l-fs8.html
+useful links:	http://www.ibm.com/developerworks/library/l-fs7/index.html
+        http://www.ibm.com/developerworks/library/l-fs8/index.html
diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index 3ae9bc9..232a575 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -68,12 +68,12 @@
     '-o barriers=[0|1]' mount option for both ext3 and ext4 filesystems
     for a fair comparison.  When tuning ext3 for best benchmark numbers,
     it is often worthwhile to try changing the data journaling mode; '-o
-    data=writeback,nobh' can be faster for some workloads.  (Note
-    however that running mounted with data=writeback can potentially
-    leave stale data exposed in recently written files in case of an
-    unclean shutdown, which could be a security exposure in some
-    situations.)  Configuring the filesystem with a large journal can
-    also be helpful for metadata-intensive workloads.
+    data=writeback' can be faster for some workloads.  (Note however that
+    running mounted with data=writeback can potentially leave stale data
+    exposed in recently written files in case of an unclean shutdown,
+    which could be a security exposure in some situations.)  Configuring
+    the filesystem with a large journal can also be helpful for
+    metadata-intensive workloads.
 
 2. Features
 ===========
@@ -272,14 +272,6 @@
 			package for more details
 			(http://sourceforge.net/projects/linuxquota).
 
-bh		(*)	ext4 associates buffer heads to data pages to
-nobh			(a) cache disk block mapping information
-			(b) link pages into transaction to provide
-			    ordering guarantees.
-			"bh" option forces use of buffer heads.
-			"nobh" option tries to avoid associating buffer
-			heads (supported only for "writeback" mode).
-
 stripe=n		Number of filesystem blocks that mballoc will try
 			to use for allocation size and alignment. For RAID5/6
 			systems this should be the number of data
@@ -393,8 +385,7 @@
 			write and convert the extent to initialized after IO
 			completes. This approach allows ext4 code to avoid
 			using inode mutex, which improves scalability on high
-			speed storages. However this does not work with nobh
-			option and the mount will fail. Nor does it work with
+			speed storages. However this does not work with
 			data journaling and dioread_nolock option will be
 			ignored with kernel warning. Note that dioread_nolock
 			code path is only used for extent-based files.
diff --git a/Documentation/filesystems/nfs/Exporting b/Documentation/filesystems/nfs/Exporting
index 87019d2..09994c2 100644
--- a/Documentation/filesystems/nfs/Exporting
+++ b/Documentation/filesystems/nfs/Exporting
@@ -92,7 +92,14 @@
    1/ provide the filehandle fragment routines described below.
    2/ make sure that d_splice_alias is used rather than d_add
       when ->lookup finds an inode for a given parent and name.
-      Typically the ->lookup routine will end with a:
+
+      If inode is NULL, d_splice_alias(inode, dentry) is eqivalent to
+
+		d_add(dentry, inode), NULL
+
+      Similarly, d_splice_alias(ERR_PTR(err), dentry) = ERR_PTR(err)
+
+      Typically the ->lookup routine will simply end with a:
 
 		return d_splice_alias(inode, dentry);
 	}
diff --git a/Documentation/hwmon/adm1275 b/Documentation/hwmon/adm1275
index 6a3a647..097b3cc 100644
--- a/Documentation/hwmon/adm1275
+++ b/Documentation/hwmon/adm1275
@@ -43,8 +43,8 @@
 Sysfs entries
 -------------
 
-The following attributes are supported. Limits are read-write; all other
-attributes are read-only.
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
 
 in1_label		"vin1" or "vout1" depending on chip variant and
 			configuration.
@@ -53,8 +53,12 @@
 in1_max			Maximum voltage. From VOUT_OV_WARN_LIMIT register.
 in1_min_alarm		Voltage low alarm. From VOLTAGE_UV_WARNING status.
 in1_max_alarm		Voltage high alarm. From VOLTAGE_OV_WARNING status.
+in1_highest		Historical maximum voltage.
+in1_reset_history	Write any value to reset history.
 
 curr1_label		"iout1"
 curr1_input		Measured current. From READ_IOUT register.
 curr1_max		Maximum current. From IOUT_OC_WARN_LIMIT register.
 curr1_max_alarm		Current high alarm. From IOUT_OC_WARN_LIMIT register.
+curr1_highest		Historical maximum current.
+curr1_reset_history	Write any value to reset history.
diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp
index f85e913..fa8776a 100644
--- a/Documentation/hwmon/coretemp
+++ b/Documentation/hwmon/coretemp
@@ -35,6 +35,13 @@
 All Sysfs entries are named with their core_id (represented here by 'X').
 tempX_input	 - Core temperature (in millidegrees Celsius).
 tempX_max	 - All cooling devices should be turned on (on Core2).
+		   Initialized with IA32_THERM_INTERRUPT. When the CPU
+		   temperature reaches this temperature, an interrupt is
+		   generated and tempX_max_alarm is set.
+tempX_max_hyst   - If the CPU temperature falls below than temperature,
+		   an interrupt is generated and tempX_max_alarm is reset.
+tempX_max_alarm  - Set if the temperature reaches or exceeds tempX_max.
+		   Reset if the temperature drops to or below tempX_max_hyst.
 tempX_crit	 - Maximum junction temperature (in millidegrees Celsius).
 tempX_crit_alarm - Set when Out-of-spec bit is set, never clears.
 		   Correct CPU operation is no longer guaranteed.
diff --git a/Documentation/hwmon/lm25066 b/Documentation/hwmon/lm25066
new file mode 100644
index 0000000..a21db81
--- /dev/null
+++ b/Documentation/hwmon/lm25066
@@ -0,0 +1,90 @@
+Kernel driver max8688
+=====================
+
+Supported chips:
+  * National Semiconductor LM25066
+    Prefix: 'lm25066'
+    Addresses scanned: -
+    Datasheets:
+	http://www.national.com/pf/LM/LM25066.html
+	http://www.national.com/pf/LM/LM25066A.html
+  * National Semiconductor LM5064
+    Prefix: 'lm5064'
+    Addresses scanned: -
+    Datasheet:
+	http://www.national.com/pf/LM/LM5064.html
+  * National Semiconductor LM5066
+    Prefix: 'lm5066'
+    Addresses scanned: -
+    Datasheet:
+	http://www.national.com/pf/LM/LM5066.html
+
+Author: Guenter Roeck <guenter.roeck@ericsson.com>
+
+
+Description
+-----------
+
+This driver supports hardware montoring for National Semiconductor LM25066,
+LM5064, and LM5064 Power Management, Monitoring, Control, and Protection ICs.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+in1_label		"vin"
+in1_input		Measured input voltage.
+in1_average		Average measured input voltage.
+in1_min			Minimum input voltage.
+in1_max			Maximum input voltage.
+in1_min_alarm		Input voltage low alarm.
+in1_max_alarm		Input voltage high alarm.
+
+in2_label		"vout1"
+in2_input		Measured output voltage.
+in2_average		Average measured output voltage.
+in2_min			Minimum output voltage.
+in2_min_alarm		Output voltage low alarm.
+
+in3_label		"vout2"
+in3_input		Measured voltage on vaux pin
+
+curr1_label		"iin"
+curr1_input		Measured input current.
+curr1_average		Average measured input current.
+curr1_max		Maximum input current.
+curr1_max_alarm		Input current high alarm.
+
+power1_label		"pin"
+power1_input		Measured input power.
+power1_average		Average measured input power.
+power1_max		Maximum input power limit.
+power1_alarm		Input power alarm
+power1_input_highest	Historical maximum power.
+power1_reset_history	Write any value to reset maximum power history.
+
+temp1_input		Measured temperature.
+temp1_max		Maximum temperature.
+temp1_crit		Critical high temperature.
+temp1_max_alarm		Chip temperature high alarm.
+temp1_crit_alarm	Chip temperature critical high alarm.
diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90
index f3efd18..9cd14cfe 100644
--- a/Documentation/hwmon/lm90
+++ b/Documentation/hwmon/lm90
@@ -113,7 +113,11 @@
     Prefix: 'w83l771'
     Addresses scanned: I2C 0x4c
     Datasheet: Not publicly available, can be requested from Nuvoton
-
+  * Philips/NXP SA56004X
+    Prefix: 'sa56004'
+    Addresses scanned: I2C 0x48 through 0x4F
+    Datasheet: Publicly available at NXP website
+               http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf
 
 Author: Jean Delvare <khali@linux-fr.org>
 
@@ -193,6 +197,9 @@
   * The AWG and ASG variants only differ in package format.
   * Diode ideality factor configuration (remote sensor) at 0xE3
 
+SA56004X:
+  * Better local resolution
+
 All temperature values are given in degrees Celsius. Resolution
 is 1.0 degree for the local temperature, 0.125 degree for the remote
 temperature, except for the MAX6657, MAX6658 and MAX6659 which have a
diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245
new file mode 100644
index 0000000..cbd8aea
--- /dev/null
+++ b/Documentation/hwmon/lm95245
@@ -0,0 +1,33 @@
+Kernel driver lm95245
+==================
+
+Supported chips:
+  * National Semiconductor LM95245
+    Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d
+    Datasheet: Publicly available at the National Semiconductor website
+               http://www.national.com/mpf/LM/LM95245.html
+
+
+Author: Alexander Stein <alexander.stein@systec-electronic.com>
+
+Description
+-----------
+
+The LM95245 is an 11-bit digital temperature sensor with a 2-wire System
+Management Bus (SMBus) interface and TruTherm technology that can monitor
+the temperature of a remote diode as well as its own temperature.
+The LM95245 can be used to very accurately monitor the temperature of
+external devices such as microprocessors.
+
+All temperature values are given in millidegrees Celsius. Local temperature
+is given within a range of -127 to +127.875 degrees. Remote temperatures are
+given within a range of -127 to +255 degrees. Resolution depends on
+temperature input and range.
+
+Each sensor has its own critical limit, but the hysteresis is common to all
+two channels.
+
+The lm95245 driver can change its update interval to a fixed set of values.
+It will round up to the next selectable interval. See the datasheet for exact
+values. Reading sensor values more often will do no harm, but will return
+'old' values.
diff --git a/Documentation/hwmon/max16064 b/Documentation/hwmon/max16064
index 4172899..f6e8bcb 100644
--- a/Documentation/hwmon/max16064
+++ b/Documentation/hwmon/max16064
@@ -50,6 +50,8 @@
 in[1-4]_max_alarm	Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in[1-4]_lcrit_alarm	Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in[1-4]_crit_alarm	Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in[1-4]_highest		Historical maximum voltage.
+in[1-4]_reset_history	Write any value to reset history.
 
 temp1_input		Measured temperature. From READ_TEMPERATURE_1 register.
 temp1_max		Maximum temperature. From OT_WARN_LIMIT register.
@@ -60,3 +62,5 @@
 temp1_crit_alarm	Chip temperature critical high alarm. Set by comparing
 			READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
 			status is set.
+temp1_highest		Historical maximum temperature.
+temp1_reset_history	Write any value to reset history.
diff --git a/Documentation/hwmon/max1668 b/Documentation/hwmon/max1668
new file mode 100644
index 0000000..0616ed9
--- /dev/null
+++ b/Documentation/hwmon/max1668
@@ -0,0 +1,60 @@
+Kernel driver max1668
+=====================
+
+Supported chips:
+  * Maxim MAX1668, MAX1805 and MAX1989
+    Prefix: 'max1668'
+    Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
+    Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX1668-MAX1989.pdf
+
+Author:
+    David George <david.george@ska.ac.za>
+
+Description
+-----------
+
+This driver implements support for the Maxim MAX1668, MAX1805 and MAX1989
+chips.
+
+The three devices are very similar, but the MAX1805 has a reduced feature
+set; only two remote temperature inputs vs the four avaible on the other
+two ICs.
+
+The driver is able to distinguish between the devices and creates sysfs
+entries as follows:
+
+MAX1805, MAX1668 and MAX1989:
+
+temp1_input     ro local (ambient) temperature
+temp1_max       rw local temperature maximum threshold for alarm
+temp1_max_alarm ro local temperature maximum threshold alarm
+temp1_min       rw local temperature minimum threshold for alarm
+temp1_min_alarm ro local temperature minimum threshold alarm
+temp2_input     ro remote temperature 1
+temp2_max       rw remote temperature 1 maximum threshold for alarm
+temp2_max_alarm ro remote temperature 1 maximum threshold alarm
+temp2_min       rw remote temperature 1 minimum threshold for alarm
+temp2_min_alarm ro remote temperature 1 minimum threshold alarm
+temp3_input     ro remote temperature 2
+temp3_max       rw remote temperature 2 maximum threshold for alarm
+temp3_max_alarm ro remote temperature 2 maximum threshold alarm
+temp3_min       rw remote temperature 2 minimum threshold for alarm
+temp3_min_alarm ro remote temperature 2 minimum threshold alarm
+
+MAX1668 and MAX1989 only:
+temp4_input     ro remote temperature 3
+temp4_max       rw remote temperature 3 maximum threshold for alarm
+temp4_max_alarm ro remote temperature 3 maximum threshold alarm
+temp4_min       rw remote temperature 3 minimum threshold for alarm
+temp4_min_alarm ro remote temperature 3 minimum threshold alarm
+temp5_input     ro remote temperature 4
+temp5_max       rw remote temperature 4 maximum threshold for alarm
+temp5_max_alarm ro remote temperature 4 maximum threshold alarm
+temp5_min       rw remote temperature 4 minimum threshold for alarm
+temp5_min_alarm ro remote temperature 4 minimum threshold alarm
+
+Module Parameters
+-----------------
+
+* read_only: int
+  Set to non-zero if you wish to prevent write access to alarm thresholds.
diff --git a/Documentation/hwmon/max34440 b/Documentation/hwmon/max34440
index 6c525dd..8ab5153 100644
--- a/Documentation/hwmon/max34440
+++ b/Documentation/hwmon/max34440
@@ -56,6 +56,8 @@
 in[1-6]_max_alarm	Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in[1-6]_lcrit_alarm	Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in[1-6]_crit_alarm	Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in[1-6]_highest		Historical maximum voltage.
+in[1-6]_reset_history	Write any value to reset history.
 
 curr[1-6]_label		"iout[1-6]".
 curr[1-6]_input		Measured current. From READ_IOUT register.
@@ -63,6 +65,8 @@
 curr[1-6]_crit		Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
 curr[1-6]_max_alarm	Current high alarm. From IOUT_OC_WARNING status.
 curr[1-6]_crit_alarm	Current critical high alarm. From IOUT_OC_FAULT status.
+curr[1-6]_highest	Historical maximum current.
+curr[1-6]_reset_history	Write any value to reset history.
 
 			in6 and curr6 attributes only exist for MAX34440.
 
@@ -75,5 +79,7 @@
 temp[1-8]_crit		Critical high temperature. From OT_FAULT_LIMIT register.
 temp[1-8]_max_alarm	Temperature high alarm.
 temp[1-8]_crit_alarm	Temperature critical high alarm.
+temp[1-8]_highest	Historical maximum temperature.
+temp[1-8]_reset_history	Write any value to reset history.
 
 			temp7 and temp8 attributes only exist for MAX34440.
diff --git a/Documentation/hwmon/max8688 b/Documentation/hwmon/max8688
index 0ddd3a4..71ed10a 100644
--- a/Documentation/hwmon/max8688
+++ b/Documentation/hwmon/max8688
@@ -50,6 +50,8 @@
 in1_max_alarm		Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in1_lcrit_alarm		Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in1_crit_alarm		Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in1_highest		Historical maximum voltage.
+in1_reset_history	Write any value to reset history.
 
 curr1_label		"iout1"
 curr1_input		Measured current. From READ_IOUT register.
@@ -57,6 +59,8 @@
 curr1_crit		Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
 curr1_max_alarm		Current high alarm. From IOUT_OC_WARN_LIMIT register.
 curr1_crit_alarm	Current critical high alarm. From IOUT_OC_FAULT status.
+curr1_highest		Historical maximum current.
+curr1_reset_history	Write any value to reset history.
 
 temp1_input		Measured temperature. From READ_TEMPERATURE_1 register.
 temp1_max		Maximum temperature. From OT_WARN_LIMIT register.
@@ -67,3 +71,5 @@
 temp1_crit_alarm	Chip temperature critical high alarm. Set by comparing
 			READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
 			status is set.
+temp1_highest		Historical maximum temperature.
+temp1_reset_history	Write any value to reset history.
diff --git a/Documentation/hwmon/ntc_thermistor b/Documentation/hwmon/ntc_thermistor
new file mode 100644
index 0000000..3bfda94
--- /dev/null
+++ b/Documentation/hwmon/ntc_thermistor
@@ -0,0 +1,93 @@
+Kernel driver ntc_thermistor
+=================
+
+Supported thermistors:
+* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333
+  Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473', 'ncp15wl333'
+  Datasheet: Publicly available at Murata
+
+Other NTC thermistors can be supported simply by adding compensation
+tables; e.g., NCP15WL333 support is added by the table ncpXXwl333.
+
+Authors:
+	MyungJoo Ham <myungjoo.ham@samsung.com>
+
+Description
+-----------
+
+The NTC thermistor is a simple thermistor that requires users to provide the
+resistance and lookup the corresponding compensation table to get the
+temperature input.
+
+The NTC driver provides lookup tables with a linear approximation function
+and four circuit models with an option not to use any of the four models.
+
+The four circuit models provided are:
+
+	$: resister, [TH]: the thermistor
+
+ 1. connect = NTC_CONNECTED_POSITIVE, pullup_ohm > 0
+
+   [pullup_uV]
+       |    |
+      [TH]  $ (pullup_ohm)
+       |    |
+       +----+-----------------------[read_uV]
+       |
+       $ (pulldown_ohm)
+       |
+      --- (ground)
+
+ 2. connect = NTC_CONNECTED_POSITIVE, pullup_ohm = 0 (not-connected)
+
+   [pullup_uV]
+       |
+      [TH]
+       |
+       +----------------------------[read_uV]
+       |
+       $ (pulldown_ohm)
+       |
+      --- (ground)
+
+ 3. connect = NTC_CONNECTED_GROUND, pulldown_ohm > 0
+
+   [pullup_uV]
+       |
+       $ (pullup_ohm)
+       |
+       +----+-----------------------[read_uV]
+       |    |
+      [TH]  $ (pulldown_ohm)
+       |    |
+      -------- (ground)
+
+ 4. connect = NTC_CONNECTED_GROUND, pulldown_ohm = 0 (not-connected)
+
+   [pullup_uV]
+       |
+       $ (pullup_ohm)
+       |
+       +----------------------------[read_uV]
+       |
+      [TH]
+       |
+      --- (ground)
+
+When one of the four circuit models is used, read_uV, pullup_uV, pullup_ohm,
+pulldown_ohm, and connect should be provided. When none of the four models
+are suitable or the user can get the resistance directly, the user should
+provide read_ohm and _not_ provide the others.
+
+Sysfs Interface
+---------------
+name		the mandatory global attribute, the thermistor name.
+
+temp1_type	always 4 (thermistor)
+		RO
+
+temp1_input	measure the temperature and provide the measured value.
+		(reading this file initiates the reading procedure.)
+		RO
+
+Note that each NTC thermistor has only _one_ thermistor; thus, only temp1 exists.
diff --git a/Documentation/hwmon/pmbus b/Documentation/hwmon/pmbus
index 5e462fc..c36c1c1 100644
--- a/Documentation/hwmon/pmbus
+++ b/Documentation/hwmon/pmbus
@@ -13,6 +13,13 @@
     Prefix: 'ltc2978'
     Addresses scanned: -
     Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf
+  * ON Semiconductor ADP4000, NCP4200, NCP4208
+    Prefixes: 'adp4000', 'ncp4200', 'ncp4208'
+    Addresses scanned: -
+    Datasheets:
+	http://www.onsemi.com/pub_link/Collateral/ADP4000-D.PDF
+	http://www.onsemi.com/pub_link/Collateral/NCP4200-D.PDF
+	http://www.onsemi.com/pub_link/Collateral/JUNE%202009-%20REV.%200.PDF
   * Generic PMBus devices
     Prefix: 'pmbus'
     Addresses scanned: -
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index 8f63c24..a4aa8f6 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -139,6 +139,29 @@
 		thumb: drivers should report the voltage values at the
 		"pins" of the chip.
 
+in[0-*]_average
+		Average voltage
+		Unit: millivolt
+		RO
+
+in[0-*]_lowest
+		Historical minimum voltage
+		Unit: millivolt
+		RO
+
+in[0-*]_highest
+		Historical maximum voltage
+		Unit: millivolt
+		RO
+
+in[0-*]_reset_history
+		Reset inX_lowest and inX_highest
+		WO
+
+in_reset_history
+		Reset inX_lowest and inX_highest for all sensors
+		WO
+
 in[0-*]_label	Suggested voltage channel label.
 		Text string
 		Should only be created if the driver has hints about what
@@ -407,6 +430,29 @@
 		Unit: milliampere
 		RO
 
+curr[1-*]_average
+		Average current use
+		Unit: milliampere
+		RO
+
+curr[1-*]_lowest
+		Historical minimum current
+		Unit: milliampere
+		RO
+
+curr[1-*]_highest
+		Historical maximum current
+		Unit: milliampere
+		RO
+
+curr[1-*]_reset_history
+		Reset currX_lowest and currX_highest
+		WO
+
+curr_reset_history
+		Reset currX_lowest and currX_highest for all sensors
+		WO
+
 Also see the Alarms section for status flags associated with currents.
 
 *********
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index a70e43e..4ca9389 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1846,7 +1846,9 @@
 			See Documentation/sound/oss/oss-parameters.txt
 
 	panic=		[KNL] Kernel behaviour on panic: delay <timeout>
-			seconds before rebooting
+			timeout > 0: seconds before rebooting
+			timeout = 0: wait forever
+			timeout < 0: reboot immediately
 			Format: <timeout>
 
 	parkbd.port=	[HW] Parallel port number the keyboard adapter is
diff --git a/Documentation/md.txt b/Documentation/md.txt
index f0eee83..fc94770 100644
--- a/Documentation/md.txt
+++ b/Documentation/md.txt
@@ -360,18 +360,20 @@
         A file recording the current state of the device in the array
 	which can be a comma separated list of
 	      faulty   - device has been kicked from active use due to
-                         a detected fault
+                         a detected fault or it has unacknowledged bad
+                         blocks
 	      in_sync  - device is a fully in-sync member of the array
 	      writemostly - device will only be subject to read
 		         requests if there are no other options.
 			 This applies only to raid1 arrays.
-	      blocked  - device has failed, metadata is "external",
-	                 and the failure hasn't been acknowledged yet.
+	      blocked  - device has failed, and the failure hasn't been
+			 acknowledged yet by the metadata handler.
 			 Writes that would write to this device if
 			 it were not faulty are blocked.
 	      spare    - device is working, but not a full member.
 			 This includes spares that are in the process
 			 of being recovered to
+	      write_error - device has ever seen a write error.
 	This list may grow in future.
 	This can be written to.
 	Writing "faulty"  simulates a failure on the device.
@@ -379,9 +381,11 @@
 	Writing "writemostly" sets the writemostly flag.
 	Writing "-writemostly" clears the writemostly flag.
 	Writing "blocked" sets the "blocked" flag.
-	Writing "-blocked" clears the "blocked" flag and allows writes
-		to complete.
+	Writing "-blocked" clears the "blocked" flags and allows writes
+		to complete and possibly simulates an error.
 	Writing "in_sync" sets the in_sync flag.
+	Writing "write_error" sets writeerrorseen flag.
+	Writing "-write_error" clears writeerrorseen flag.
 
 	This file responds to select/poll. Any change to 'faulty'
 	or 'blocked' causes an event.
@@ -419,7 +423,6 @@
         written, it will be rejected.
 
       recovery_start
-
         When the device is not 'in_sync', this records the number of
 	sectors from the start of the device which are known to be
 	correct.  This is normally zero, but during a recovery
@@ -435,6 +438,20 @@
 	Setting this to 'none' is equivalent to setting 'in_sync'.
 	Setting to any other value also clears the 'in_sync' flag.
 	
+      bad_blocks
+	This gives the list of all known bad blocks in the form of
+	start address and length (in sectors respectively). If output
+	is too big to fit in a page, it will be truncated. Writing
+	"sector length" to this file adds new acknowledged (i.e.
+	recorded to disk safely) bad blocks.
+
+      unacknowledged_bad_blocks
+	This gives the list of known-but-not-yet-saved-to-disk bad
+	blocks in the same form of 'bad_blocks'. If output is too big
+	to fit in a page, it will be truncated. Writing to this file
+	adds bad blocks without acknowledging them. This is largely
+	for testing.
+
 
 
 An active md device will also contain and entry for each active device
diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt
index 76a2087..669b5fb 100644
--- a/Documentation/media-framework.txt
+++ b/Documentation/media-framework.txt
@@ -310,7 +310,7 @@
 the configuration information to be applied later.
 
 Link configuration must not have any side effect on other links. If an enabled
-link at a sink pad prevents another link at the same pad from being disabled,
+link at a sink pad prevents another link at the same pad from being enabled,
 the link_setup operation must return -EBUSY and can't implicitly disable the
 first enabled link.
 
diff --git a/Documentation/scsi/ChangeLog.megaraid_sas b/Documentation/scsi/ChangeLog.megaraid_sas
index 9ed1d9d..1b6e27d 100644
--- a/Documentation/scsi/ChangeLog.megaraid_sas
+++ b/Documentation/scsi/ChangeLog.megaraid_sas
@@ -1,3 +1,11 @@
+Release Date    : Tue. Jul 26, 2011 17:00:00 PST 2010 -
+			(emaild-id:megaraidlinux@lsi.com)
+			Adam Radford
+Current Version : 00.00.05.40-rc1
+Old Version     : 00.00.05.38-rc1
+    1. Fix FastPath I/O to work with degraded RAID 1.
+    2. Add .change_queue_depth support.
+-------------------------------------------------------------------------------
 Release Date    : Wed. May 11, 2011 17:00:00 PST 2010 -
 			(emaild-id:megaraidlinux@lsi.com)
 			Adam Radford
diff --git a/Documentation/security/keys-ecryptfs.txt b/Documentation/security/keys-ecryptfs.txt
new file mode 100644
index 0000000..c3bbeba
--- /dev/null
+++ b/Documentation/security/keys-ecryptfs.txt
@@ -0,0 +1,68 @@
+		Encrypted keys for the eCryptfs filesystem
+
+ECryptfs is a stacked filesystem which transparently encrypts and decrypts each
+file using a randomly generated File Encryption Key (FEK).
+
+Each FEK is in turn encrypted with a File Encryption Key Encryption Key (FEFEK)
+either in kernel space or in user space with a daemon called 'ecryptfsd'.  In
+the former case the operation is performed directly by the kernel CryptoAPI
+using a key, the FEFEK, derived from a user prompted passphrase;  in the latter
+the FEK is encrypted by 'ecryptfsd' with the help of external libraries in order
+to support other mechanisms like public key cryptography, PKCS#11 and TPM based
+operations.
+
+The data structure defined by eCryptfs to contain information required for the
+FEK decryption is called authentication token and, currently, can be stored in a
+kernel key of the 'user' type, inserted in the user's session specific keyring
+by the userspace utility 'mount.ecryptfs' shipped with the package
+'ecryptfs-utils'.
+
+The 'encrypted' key type has been extended with the introduction of the new
+format 'ecryptfs' in order to be used in conjunction with the eCryptfs
+filesystem.  Encrypted keys of the newly introduced format store an
+authentication token in its payload with a FEFEK randomly generated by the
+kernel and protected by the parent master key.
+
+In order to avoid known-plaintext attacks, the datablob obtained through
+commands 'keyctl print' or 'keyctl pipe' does not contain the overall
+authentication token, which content is well known, but only the FEFEK in
+encrypted form.
+
+The eCryptfs filesystem may really benefit from using encrypted keys in that the
+required key can be securely generated by an Administrator and provided at boot
+time after the unsealing of a 'trusted' key in order to perform the mount in a
+controlled environment.  Another advantage is that the key is not exposed to
+threats of malicious software, because it is available in clear form only at
+kernel level.
+
+Usage:
+   keyctl add encrypted name "new ecryptfs key-type:master-key-name keylen" ring
+   keyctl add encrypted name "load hex_blob" ring
+   keyctl update keyid "update key-type:master-key-name"
+
+name:= '<16 hexadecimal characters>'
+key-type:= 'trusted' | 'user'
+keylen:= 64
+
+
+Example of encrypted key usage with the eCryptfs filesystem:
+
+Create an encrypted key "1000100010001000" of length 64 bytes with format
+'ecryptfs' and save it using a previously loaded user key "test":
+
+    $ keyctl add encrypted 1000100010001000 "new ecryptfs user:test 64" @u
+    19184530
+
+    $ keyctl print 19184530
+    ecryptfs user:test 64 490045d4bfe48c99f0d465fbbbb79e7500da954178e2de0697
+    dd85091f5450a0511219e9f7cd70dcd498038181466f78ac8d4c19504fcc72402bfc41c2
+    f253a41b7507ccaa4b2b03fff19a69d1cc0b16e71746473f023a95488b6edfd86f7fdd40
+    9d292e4bacded1258880122dd553a661
+
+    $ keyctl pipe 19184530 > ecryptfs.blob
+
+Mount an eCryptfs filesystem using the created encrypted key "1000100010001000"
+into the '/secret' directory:
+
+    $ mount -i -t ecryptfs -oecryptfs_sig=1000100010001000,\
+      ecryptfs_cipher=aes,ecryptfs_key_bytes=32 /secret /secret
diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt
index 8fb79bc..5f50cca 100644
--- a/Documentation/security/keys-trusted-encrypted.txt
+++ b/Documentation/security/keys-trusted-encrypted.txt
@@ -53,12 +53,19 @@
 should therefore be loaded in as secure a way as possible, preferably early in
 boot.
 
-Usage:
-  keyctl add encrypted name "new key-type:master-key-name keylen" ring
-  keyctl add encrypted name "load hex_blob" ring
-  keyctl update keyid "update key-type:master-key-name"
+The decrypted portion of encrypted keys can contain either a simple symmetric
+key or a more complex structure. The format of the more complex structure is
+application specific, which is identified by 'format'.
 
-where 'key-type' is either 'trusted' or 'user'.
+Usage:
+    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
+        ring
+    keyctl add encrypted name "load hex_blob" ring
+    keyctl update keyid "update key-type:master-key-name"
+
+format:= 'default | ecryptfs'
+key-type:= 'trusted' | 'user'
+
 
 Examples of trusted and encrypted key usage:
 
@@ -114,15 +121,25 @@
     7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
     df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8
 
-Create and save an encrypted key "evm" using the above trusted key "kmk":
+The initial consumer of trusted keys is EVM, which at boot time needs a high
+quality symmetric key for HMAC protection of file metadata.  The use of a
+trusted key provides strong guarantees that the EVM key has not been
+compromised by a user level problem, and when sealed to specific boot PCR
+values, protects against boot and offline attacks.  Create and save an
+encrypted key "evm" using the above trusted key "kmk":
 
+option 1: omitting 'format'
     $ keyctl add encrypted evm "new trusted:kmk 32" @u
     159771175
 
+option 2: explicitly defining 'format' as 'default'
+    $ keyctl add encrypted evm "new default trusted:kmk 32" @u
+    159771175
+
     $ keyctl print 159771175
-    trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
-    be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
-    5972dcb82ab2dde83376d82b2e3c09ffc
+    default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+    82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+    24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
 
     $ keyctl pipe 159771175 > evm.blob
 
@@ -132,14 +149,11 @@
     831684262
 
     $ keyctl print 831684262
-    trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
-    be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
-    5972dcb82ab2dde83376d82b2e3c09ffc
+    default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+    82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+    24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
 
-
-The initial consumer of trusted keys is EVM, which at boot time needs a high
-quality symmetric key for HMAC protection of file metadata.  The use of a
-trusted key provides strong guarantees that the EVM key has not been
-compromised by a user level problem, and when sealed to specific boot PCR
-values, protects against boot and offline attacks.  Other uses for trusted and
-encrypted keys, such as for disk and file encryption are anticipated.
+Other uses for trusted and encrypted keys, such as for disk and file encryption
+are anticipated.  In particular the new format 'ecryptfs' has been defined in
+in order to use encrypted keys to mount an eCryptfs filesystem.  More details
+about the usage can be found in the file 'Documentation/keys-ecryptfs.txt'.
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 1c7fb0a..704e474 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -61,6 +61,7 @@
 - rtsig-nr
 - sem
 - sg-big-buff                 [ generic SCSI device (sg) ]
+- shm_rmid_forced
 - shmall
 - shmmax                      [ sysv ipc ]
 - shmmni
@@ -518,6 +519,27 @@
 
 ==============================================================
 
+shm_rmid_forced:
+
+Linux lets you set resource limits, including how much memory one
+process can consume, via setrlimit(2).  Unfortunately, shared memory
+segments are allowed to exist without association with any process, and
+thus might not be counted against any resource limits.  If enabled,
+shared memory segments are automatically destroyed when their attach
+count becomes zero after a detach or a process termination.  It will
+also destroy segments that were created, but never attached to, on exit
+from the process.  The only use left for IPC_RMID is to immediately
+destroy an unattached segment.  Of course, this breaks the way things are
+defined, so some applications might stop working.  Note that this
+feature will do you no good unless you also configure your resource
+limits (in particular, RLIMIT_AS and RLIMIT_NPROC).  Most systems don't
+need this.
+
+Note that if you change this from 0 to 1, already created segments
+without users and with a dead originative process will be destroyed.
+
+==============================================================
+
 softlockup_thresh:
 
 This value can be used to lower the softlockup tolerance threshold.  The
diff --git a/Documentation/video4linux/API.html b/Documentation/video4linux/API.html
index d72fd2a..256f8ef 100644
--- a/Documentation/video4linux/API.html
+++ b/Documentation/video4linux/API.html
@@ -9,7 +9,7 @@
   <table border="0">
    <tr>
     <td>
-     <a href="http://www.linuxtv.org/downloads/video4linux/API/V4L1_API.html">V4L original API</a>
+     <a href="http://linuxtv.org/downloads/legacy/video4linux/API/V4L1_API.html">V4L original API</a>
     </td>
     <td>
      Obsoleted by V4L2 API
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 87c4634..8910449 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -27,3 +27,5 @@
  26 -> Hauppauge WinTV-HVR1290                             [0070:8551]
  27 -> Mygica X8558 PRO DMB-TH                             [14f1:8578]
  28 -> LEADTEK WinFast PxTV1200                            [107d:6f22]
+ 29 -> GoTView X5 3D Hybrid                                [5654:2390]
+ 30 -> NetUP Dual DVB-T/C-CI RF                            [1b55:e2e4]
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index 42517d9..d9c0f11 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -84,3 +84,4 @@
  83 -> Prof 7301 DVB-S/S2                                  [b034:3034]
  84 -> Samsung SMT 7020 DVB-S                              [18ac:dc00,18ac:dccd]
  85 -> Twinhan VP-1027 DVB-S                               [1822:0023]
+ 86 -> TeVii S464 DVB-S/S2                                 [d464:9022]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 9aae449..4a7b3df 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -74,3 +74,5 @@
  74 -> Actionmaster/LinXcel/Digitus VC211A      (em2800)
  75 -> Dikom DK300                              (em2882)
  76 -> KWorld PlusTV 340U or UB435-Q (ATSC)     (em2870)        [1b80:a340]
+ 77 -> EM2874 Leadership ISDBT                  (em2874)
+ 78 -> PCTV nanoStick T2 290e                   (em28174)
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 6b4c72d..7efae9b 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -182,3 +182,7 @@
 181 -> TechoTrend TT-budget T-3000              [13c2:2804]
 182 -> Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid  [17de:b136]
 183 -> Compro VideoMate Vista M1F               [185b:c900]
+184 -> Encore ENLTV-FM 3                        [1a7f:2108]
+185 -> MagicPro ProHDTV Pro2 DMB-TH/Hybrid      [17de:d136]
+186 -> Beholder BeholdTV 501                    [5ace:5010]
+187 -> Beholder BeholdTV 503 FM                 [5ace:5030]
diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner
index e67c1db..6323b7a 100644
--- a/Documentation/video4linux/CARDLIST.tuner
+++ b/Documentation/video4linux/CARDLIST.tuner
@@ -78,8 +78,10 @@
 tuner=78 - Philips FMD1216MEX MK3 Hybrid Tuner
 tuner=79 - Philips PAL/SECAM multi (FM1216 MK5)
 tuner=80 - Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough
+tuner=81 - Xceive 4000 tuner
 tuner=81 - Partsnic (Daewoo) PTI-5NF05
 tuner=82 - Philips CU1216L
 tuner=83 - NXP TDA18271
 tuner=84 - Sony BTF-Pxn01Z
 tuner=85 - Philips FQ1236 MK5
+tuner=86 - Tena TNF5337 MFD
diff --git a/Documentation/video4linux/CARDLIST.usbvision b/Documentation/video4linux/CARDLIST.usbvision
index 0b72d3f..6fd1af3 100644
--- a/Documentation/video4linux/CARDLIST.usbvision
+++ b/Documentation/video4linux/CARDLIST.usbvision
@@ -63,3 +63,5 @@
  62 -> Pinnacle PCTV Bungee USB (PAL) FM                        [2304:0419]
  63 -> Hauppauge WinTv-USB                                      [2400:4200]
  64 -> Pinnacle Studio PCTV USB (NTSC) FM V3                    [2304:0113]
+ 65 -> Nogatech USB MicroCam NTSC (NV3000N)                     [0573:3000]
+ 66 -> Nogatech USB MicroCam PAL (NV3001P)                      [0573:3001]
diff --git a/Documentation/video4linux/README.davinci-vpbe b/Documentation/video4linux/README.davinci-vpbe
new file mode 100644
index 0000000..7a460b0
--- /dev/null
+++ b/Documentation/video4linux/README.davinci-vpbe
@@ -0,0 +1,93 @@
+
+                VPBE V4L2 driver design
+ ======================================================================
+
+ File partitioning
+ -----------------
+ V4L2 display device driver
+         drivers/media/video/davinci/vpbe_display.c
+         drivers/media/video/davinci/vpbe_display.h
+
+ VPBE display controller
+         drivers/media/video/davinci/vpbe.c
+         drivers/media/video/davinci/vpbe.h
+
+ VPBE venc sub device driver
+         drivers/media/video/davinci/vpbe_venc.c
+         drivers/media/video/davinci/vpbe_venc.h
+         drivers/media/video/davinci/vpbe_venc_regs.h
+
+ VPBE osd driver
+         drivers/media/video/davinci/vpbe_osd.c
+         drivers/media/video/davinci/vpbe_osd.h
+         drivers/media/video/davinci/vpbe_osd_regs.h
+
+ Functional partitioning
+ -----------------------
+
+ Consists of the following (in the same order as the list under file
+ partitioning):-
+
+ 1. V4L2 display driver
+    Implements creation of video2 and video3 device nodes and
+    provides v4l2 device interface to manage VID0 and VID1 layers.
+
+ 2. Display controller
+    Loads up VENC, OSD and external encoders such as ths8200. It provides
+    a set of API calls to V4L2 drivers to set the output/standards
+    in the VENC or external sub devices. It also provides
+    a device object to access the services from OSD subdevice
+    using sub device ops. The connection of external encoders to VENC LCD
+    controller port is done at init time based on default output and standard
+    selection or at run time when application change the output through
+    V4L2 IOCTLs.
+
+    When connected to an external encoder, vpbe controller is also responsible
+    for setting up the interface between VENC and external encoders based on
+    board specific settings (specified in board-xxx-evm.c). This allows
+    interfacing external encoders such as ths8200. The setup_if_config()
+    is implemented for this as well as configure_venc() (part of the next patch)
+    API to set timings in VENC for a specific display resolution. As of this
+    patch series, the interconnection and enabling and setting of the external
+    encoders is not present, and would be a part of the next patch series.
+
+ 3. VENC subdevice module
+    Responsible for setting outputs provided through internal DACs and also
+    setting timings at LCD controller port when external encoders are connected
+    at the port or LCD panel timings required. When external encoder/LCD panel
+    is connected, the timings for a specific standard/preset is retrieved from
+    the board specific table and the values are used to set the timings in
+    venc using non-standard timing mode.
+
+    Support LCD Panel displays using the VENC. For example to support a Logic
+    PD display, it requires setting up the LCD controller port with a set of
+    timings for the resolution supported and setting the dot clock. So we could
+    add the available outputs as a board specific entry (i.e add the "LogicPD"
+    output name to board-xxx-evm.c). A table of timings for various LCDs
+    supported can be maintained in the board specific setup file to support
+    various LCD displays.As of this patch a basic driver is present, and this
+    support for external encoders and displays forms a part of the next
+    patch series.
+
+ 4. OSD module
+    OSD module implements all OSD layer management and hardware specific
+    features. The VPBE module interacts with the OSD for enabling and
+    disabling appropriate features of the OSD.
+
+ Current status:-
+
+ A fully functional working version of the V4L2 driver is available. This
+ driver has been tested with NTSC and PAL standards and buffer streaming.
+
+ Following are TBDs.
+
+ vpbe display controller
+    - Add support for external encoders.
+    - add support for selecting external encoder as default at probe time.
+
+ vpbe venc sub device
+    - add timings for supporting ths8200
+    - add support for LogicPD LCD.
+
+ FB drivers
+    - Add support for fbdev drivers.- Ready and part of subsequent patches.
diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt
index 881e7f4..9346fc8 100644
--- a/Documentation/video4linux/v4l2-controls.txt
+++ b/Documentation/video4linux/v4l2-controls.txt
@@ -277,16 +277,13 @@
 	{
 		switch (ctrl->id) {
 		case V4L2_CID_BRIGHTNESS:
-			ctrl->cur.val = read_reg(0x123);
+			ctrl->val = read_reg(0x123);
 			break;
 		}
 	}
 
-The 'new value' union is not used in g_volatile_ctrl. In general controls
-that need to implement g_volatile_ctrl are read-only controls.
-
-Note that if one or more controls in a control cluster are marked as volatile,
-then all the controls in the cluster are seen as volatile.
+Note that you use the 'new value' union as well in g_volatile_ctrl. In general
+controls that need to implement g_volatile_ctrl are read-only controls.
 
 To mark a control as volatile you have to set the is_volatile flag:
 
@@ -453,6 +450,25 @@
 	ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
 	ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
 
+In practice using cluster arrays like this becomes very tiresome. So instead
+the following equivalent method is used:
+
+	struct {
+		/* audio cluster */
+		struct v4l2_ctrl *volume;
+		struct v4l2_ctrl *mute;
+	};
+
+The anonymous struct is used to clearly 'cluster' these two control pointers,
+but it serves no other purpose. The effect is the same as creating an
+array with two control pointers. So you can just do:
+
+	state->volume = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+	state->mute = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+	v4l2_ctrl_cluster(2, &state->volume);
+
+And in foo_s_ctrl you can use these pointers directly: state->mute->val.
+
 Note that controls in a cluster may be NULL. For example, if for some
 reason mute was never added (because the hardware doesn't support that
 particular feature), then mute will be NULL. So in that case we have a
@@ -475,6 +491,43 @@
 The 'is_new' flag is always 1 when called from v4l2_ctrl_handler_setup().
 
 
+Handling autogain/gain-type Controls with Auto Clusters
+=======================================================
+
+A common type of control cluster is one that handles 'auto-foo/foo'-type
+controls. Typical examples are autogain/gain, autoexposure/exposure,
+autowhitebalance/red balance/blue balance. In all cases you have one controls
+that determines whether another control is handled automatically by the hardware,
+or whether it is under manual control from the user.
+
+If the cluster is in automatic mode, then the manual controls should be
+marked inactive. When the volatile controls are read the g_volatile_ctrl
+operation should return the value that the hardware's automatic mode set up
+automatically.
+
+If the cluster is put in manual mode, then the manual controls should become
+active again and the is_volatile flag should be ignored (so g_volatile_ctrl is
+no longer called while in manual mode).
+
+Finally the V4L2_CTRL_FLAG_UPDATE should be set for the auto control since
+changing that control affects the control flags of the manual controls.
+
+In order to simplify this a special variation of v4l2_ctrl_cluster was
+introduced:
+
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+			u8 manual_val, bool set_volatile);
+
+The first two arguments are identical to v4l2_ctrl_cluster. The third argument
+tells the framework which value switches the cluster into manual mode. The
+last argument will optionally set the is_volatile flag for the non-auto controls.
+
+The first control of the cluster is assumed to be the 'auto' control.
+
+Using this function will ensure that you don't need to handle all the complex
+flag and volatile handling.
+
+
 VIDIOC_LOG_STATUS Support
 =========================
 
@@ -636,9 +689,7 @@
 -EINVAL as the spec says.
 
 5) The spec does not mention what should happen when you try to set/get a
-control class controls. ivtv currently returns -EINVAL (indicating that the
-control ID does not exist) while the framework will return -EACCES, which
-makes more sense.
+control class controls. The framework will return -EACCES.
 
 
 Proposals for Extensions
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index cf21f7a..f8dcabf 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -817,11 +817,7 @@
 
 	...
 
-	ret = v4l2_fh_init(&my_fh->fh, vfd);
-	if (ret) {
-		kfree(my_fh);
-		return ret;
-	}
+	v4l2_fh_init(&my_fh->fh, vfd);
 
 	...
 
@@ -844,7 +840,7 @@
 
 Below is a short description of the v4l2_fh functions used:
 
-int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
 
   Initialise the file handle. This *MUST* be performed in the driver's
   v4l2_file_operations->open() handler.
@@ -901,15 +897,39 @@
 The V4L2 events provide a generic way to pass events to user space.
 The driver must use v4l2_fh to be able to support V4L2 events.
 
+Events are defined by a type and an optional ID. The ID may refer to a V4L2
+object such as a control ID. If unused, then the ID is 0.
+
+When the user subscribes to an event the driver will allocate a number of
+kevent structs for that event. So every (type, ID) event tuple will have
+its own set of kevent structs. This guarantees that if a driver is generating
+lots of events of one type in a short time, then that will not overwrite
+events of another type.
+
+But if you get more events of one type than the number of kevents that were
+reserved, then the oldest event will be dropped and the new one added.
+
+Furthermore, the internal struct v4l2_subscribed_event has merge() and
+replace() callbacks which drivers can set. These callbacks are called when
+a new event is raised and there is no more room. The replace() callback
+allows you to replace the payload of the old event with that of the new event,
+merging any relevant data from the old payload into the new payload that
+replaces it. It is called when this event type has only one kevent struct
+allocated. The merge() callback allows you to merge the oldest event payload
+into that of the second-oldest event payload. It is called when there are two
+or more kevent structs allocated.
+
+This way no status information is lost, just the intermediate steps leading
+up to that state.
+
+A good example of these replace/merge callbacks is in v4l2-event.c:
+ctrls_replace() and ctrls_merge() callbacks for the control event.
+
+Note: these callbacks can be called from interrupt context, so they must be
+fast.
+
 Useful functions:
 
-- v4l2_event_alloc()
-
-  To use events, the driver must allocate events for the file handle. By
-  calling the function more than once, the driver may assure that at least n
-  events in total have been allocated. The function may not be called in
-  atomic context.
-
 - v4l2_event_queue()
 
   Queue events to video device. The driver's only responsibility is to fill
@@ -920,7 +940,9 @@
 
   The video_device->ioctl_ops->vidioc_subscribe_event must check the driver
   is able to produce events with specified event id. Then it calls
-  v4l2_event_subscribe() to subscribe the event.
+  v4l2_event_subscribe() to subscribe the event. The last argument is the
+  size of the event queue for this event. If it is 0, then the framework
+  will fill in a default value (this depends on the event type).
 
 - v4l2_event_unsubscribe()
 
@@ -935,14 +957,8 @@
 
   Returns the number of pending events. Useful when implementing poll.
 
-Drivers do not initialise events directly. The events are initialised
-through v4l2_fh_init() if video_device->ioctl_ops->vidioc_subscribe_event is
-non-NULL. This *MUST* be performed in the driver's
-v4l2_file_operations->open() handler.
-
 Events are delivered to user space through the poll system call. The driver
-can use v4l2_fh->events->wait wait_queue_head_t as the argument for
-poll_wait().
+can use v4l2_fh->wait (a wait_queue_head_t) as the argument for poll_wait().
 
 There are standard and private events. New standard events must use the
 smallest available event type. The drivers must allocate their events from
@@ -952,5 +968,4 @@
 available event type is 'class base + 1'.
 
 An example on how the V4L2 events may be used can be found in the OMAP
-3 ISP driver available at <URL:http://gitorious.org/omap3camera> as of
-writing this.
+3 ISP driver (drivers/media/video/omap3isp).
diff --git a/Documentation/watchdog/00-INDEX b/Documentation/watchdog/00-INDEX
index ee99451..fc51128 100644
--- a/Documentation/watchdog/00-INDEX
+++ b/Documentation/watchdog/00-INDEX
@@ -8,6 +8,8 @@
 	- directory holding watchdog related example programs.
 watchdog-api.txt
 	- description of the Linux Watchdog driver API.
+watchdog-kernel-api.txt
+	- description of the Linux WatchDog Timer Driver Core kernel API.
 watchdog-parameters.txt
 	- information on driver parameters (for drivers other than
 	  the ones that have driver-specific files here)
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
new file mode 100644
index 0000000..4f7c894
--- /dev/null
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -0,0 +1,162 @@
+The Linux WatchDog Timer Driver Core kernel API.
+===============================================
+Last reviewed: 22-Jul-2011
+
+Wim Van Sebroeck <wim@iguana.be>
+
+Introduction
+------------
+This document does not describe what a WatchDog Timer (WDT) Driver or Device is.
+It also does not describe the API which can be used by user space to communicate
+with a WatchDog Timer. If you want to know this then please read the following
+file: Documentation/watchdog/watchdog-api.txt .
+
+So what does this document describe? It describes the API that can be used by
+WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core
+Framework. This framework provides all interfacing towards user space so that
+the same code does not have to be reproduced each time. This also means that
+a watchdog timer driver then only needs to provide the different routines
+(operations) that control the watchdog timer (WDT).
+
+The API
+-------
+Each watchdog timer driver that wants to use the WatchDog Timer Driver Core
+must #include <linux/watchdog.h> (you would have to do this anyway when
+writing a watchdog device driver). This include file contains following
+register/unregister routines:
+
+extern int watchdog_register_device(struct watchdog_device *);
+extern void watchdog_unregister_device(struct watchdog_device *);
+
+The watchdog_register_device routine registers a watchdog timer device.
+The parameter of this routine is a pointer to a watchdog_device structure.
+This routine returns zero on success and a negative errno code for failure.
+
+The watchdog_unregister_device routine deregisters a registered watchdog timer
+device. The parameter of this routine is the pointer to the registered
+watchdog_device structure.
+
+The watchdog device structure looks like this:
+
+struct watchdog_device {
+	const struct watchdog_info *info;
+	const struct watchdog_ops *ops;
+	unsigned int bootstatus;
+	unsigned int timeout;
+	unsigned int min_timeout;
+	unsigned int max_timeout;
+	void *driver_data;
+	unsigned long status;
+};
+
+It contains following fields:
+* info: a pointer to a watchdog_info structure. This structure gives some
+  additional information about the watchdog timer itself. (Like it's unique name)
+* ops: a pointer to the list of watchdog operations that the watchdog supports.
+* timeout: the watchdog timer's timeout value (in seconds).
+* min_timeout: the watchdog timer's minimum timeout value (in seconds).
+* max_timeout: the watchdog timer's maximum timeout value (in seconds).
+* bootstatus: status of the device after booting (reported with watchdog
+  WDIOF_* status bits).
+* driver_data: a pointer to the drivers private data of a watchdog device.
+  This data should only be accessed via the watchdog_set_drvadata and
+  watchdog_get_drvdata routines.
+* status: this field contains a number of status bits that give extra
+  information about the status of the device (Like: is the watchdog timer
+  running/active, is the nowayout bit set, is the device opened via
+  the /dev/watchdog interface or not, ...).
+
+The list of watchdog operations is defined as:
+
+struct watchdog_ops {
+	struct module *owner;
+	/* mandatory operations */
+	int (*start)(struct watchdog_device *);
+	int (*stop)(struct watchdog_device *);
+	/* optional operations */
+	int (*ping)(struct watchdog_device *);
+	unsigned int (*status)(struct watchdog_device *);
+	int (*set_timeout)(struct watchdog_device *, unsigned int);
+	long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
+};
+
+It is important that you first define the module owner of the watchdog timer
+driver's operations. This module owner will be used to lock the module when
+the watchdog is active. (This to avoid a system crash when you unload the
+module and /dev/watchdog is still open).
+Some operations are mandatory and some are optional. The mandatory operations
+are:
+* start: this is a pointer to the routine that starts the watchdog timer
+  device.
+  The routine needs a pointer to the watchdog timer device structure as a
+  parameter. It returns zero on success or a negative errno code for failure.
+* stop: with this routine the watchdog timer device is being stopped.
+  The routine needs a pointer to the watchdog timer device structure as a
+  parameter. It returns zero on success or a negative errno code for failure.
+  Some watchdog timer hardware can only be started and not be stopped. The
+  driver supporting this hardware needs to make sure that a start and stop
+  routine is being provided. This can be done by using a timer in the driver
+  that regularly sends a keepalive ping to the watchdog timer hardware.
+
+Not all watchdog timer hardware supports the same functionality. That's why
+all other routines/operations are optional. They only need to be provided if
+they are supported. These optional routines/operations are:
+* ping: this is the routine that sends a keepalive ping to the watchdog timer
+  hardware.
+  The routine needs a pointer to the watchdog timer device structure as a
+  parameter. It returns zero on success or a negative errno code for failure.
+  Most hardware that does not support this as a separate function uses the
+  start function to restart the watchdog timer hardware. And that's also what
+  the watchdog timer driver core does: to send a keepalive ping to the watchdog
+  timer hardware it will either use the ping operation (when available) or the
+  start operation (when the ping operation is not available).
+  (Note: the WDIOC_KEEPALIVE ioctl call will only be active when the
+  WDIOF_KEEPALIVEPING bit has been set in the option field on the watchdog's
+  info structure).
+* status: this routine checks the status of the watchdog timer device. The
+  status of the device is reported with watchdog WDIOF_* status flags/bits.
+* set_timeout: this routine checks and changes the timeout of the watchdog
+  timer device. It returns 0 on success, -EINVAL for "parameter out of range"
+  and -EIO for "could not write value to the watchdog". On success the timeout
+  value of the watchdog_device will be changed to the value that was just used
+  to re-program the watchdog timer device.
+  (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
+  watchdog's info structure).
+* ioctl: if this routine is present then it will be called first before we do
+  our own internal ioctl call handling. This routine should return -ENOIOCTLCMD
+  if a command is not supported. The parameters that are passed to the ioctl
+  call are: watchdog_device, cmd and arg.
+
+The status bits should (preferably) be set with the set_bit and clear_bit alike
+bit-operations. The status bits that are defined are:
+* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
+  is active or not. When the watchdog is active after booting, then you should
+  set this status bit (Note: when you register the watchdog timer device with
+  this bit set, then opening /dev/watchdog will skip the start operation)
+* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device
+  was opened via /dev/watchdog.
+  (This bit should only be used by the WatchDog Timer Driver Core).
+* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character
+  has been sent (so that we can support the magic close feature).
+  (This bit should only be used by the WatchDog Timer Driver Core).
+* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
+  If this bit is set then the watchdog timer will not be able to stop.
+
+Note: The WatchDog Timer Driver Core supports the magic close feature and
+the nowayout feature. To use the magic close feature you must set the
+WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure.
+The nowayout feature will overrule the magic close feature.
+
+To get or set driver specific data the following two helper functions should be
+used:
+
+static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
+static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
+
+The watchdog_set_drvdata function allows you to add driver specific data. The
+arguments of this function are the watchdog device where you want to add the
+driver specific data to and a pointer to the data itself.
+
+The watchdog_get_drvdata function allows you to retrieve driver specific data.
+The argument of this function is the watchdog device where you want to retrieve
+data from. The function retruns the pointer to the driver specific data.
diff --git a/MAINTAINERS b/MAINTAINERS
index 1288294..cf5e8a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -734,6 +734,12 @@
 S:	Maintained
 F:	arch/arm/mach-gemini/
 
+ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
+M:	Barry Song <baohua.song@csr.com>
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	arch/arm/mach-prima2/
+
 ARM/EBSA110 MACHINE SUPPORT
 M:	Russell King <linux@arm.linux.org.uk>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -4144,6 +4150,13 @@
 F:	drivers/video/matrox/matroxfb_*
 F:	include/linux/matroxfb.h
 
+MAX1668 TEMPERATURE SENSOR DRIVER
+M:	"David George" <david.george@ska.ac.za>
+L:	lm-sensors@lm-sensors.org
+S:	Maintained
+F:	Documentation/hwmon/max1668
+F:	drivers/hwmon/max1668.c
+
 MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
 M:	"Hans J. Koch" <hjk@hansjkoch.de>
 L:	lm-sensors@lm-sensors.org
@@ -4217,9 +4230,10 @@
 
 MIPS
 M:	Ralf Baechle <ralf@linux-mips.org>
-W:	http://www.linux-mips.org/
 L:	linux-mips@linux-mips.org
+W:	http://www.linux-mips.org/
 T:	git git://git.linux-mips.org/pub/scm/linux.git
+Q:	http://patchwork.linux-mips.org/project/linux-mips/list/
 S:	Supported
 F:	Documentation/mips/
 F:	arch/mips/
@@ -4992,6 +5006,17 @@
 F:	drivers/video/fb-puv3.c
 F:	drivers/rtc/rtc-puv3.c
 
+PMBUS HARDWARE MONITORING DRIVERS
+M:	Guenter Roeck <guenter.roeck@ericsson.com>
+L:	lm-sensors@lm-sensors.org
+W:	http://www.lm-sensors.org/
+W:	http://www.roeck-us.net/linux/drivers/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
+S:	Maintained
+F:	Documentation/hwmon/pmbus
+F:	drivers/hwmon/pmbus/
+F:	include/linux/i2c/pmbus.h
+
 PMC SIERRA MaxRAID DRIVER
 M:	Anil Ravindranath <anil_ravindranath@pmc-sierra.com>
 L:	linux-scsi@vger.kernel.org
@@ -6401,7 +6426,7 @@
 L:	tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
 L:	tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
 W:	http://tomoyo.sourceforge.jp/
-T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.3.x/tomoyo-lsm/patches/
+T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/
 S:	Maintained
 F:	security/tomoyo/
 
diff --git a/Makefile b/Makefile
index d018956..f676d15 100644
--- a/Makefile
+++ b/Makefile
@@ -360,7 +360,7 @@
 LINUXINCLUDE    := -I$(srctree)/arch/$(hdr-arch)/include \
                    -Iarch/$(hdr-arch)/include/generated -Iinclude \
                    $(if $(KBUILD_SRC), -I$(srctree)/include) \
-                   -include include/generated/autoconf.h
+                   -include $(srctree)/include/linux/kconfig.h
 
 KBUILD_CPPFLAGS := -D__KERNEL__
 
diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h
index e756d04..640f909 100644
--- a/arch/alpha/include/asm/atomic.h
+++ b/arch/alpha/include/asm/atomic.h
@@ -176,15 +176,15 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -196,10 +196,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /**
  * atomic64_add_unless - add unless the number is a given value
@@ -208,7 +207,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 {
@@ -256,5 +255,4 @@
 #define smp_mb__before_atomic_inc()	smp_mb()
 #define smp_mb__after_atomic_inc()	smp_mb()
 
-#include <asm-generic/atomic-long.h>
 #endif /* _ALPHA_ATOMIC_H */
diff --git a/arch/alpha/include/asm/bitops.h b/arch/alpha/include/asm/bitops.h
index 85b8152..a19ba5e 100644
--- a/arch/alpha/include/asm/bitops.h
+++ b/arch/alpha/include/asm/bitops.h
@@ -456,8 +456,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(l,n,a)   test_and_set_bit(n,a)
-#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* __KERNEL__ */
 
diff --git a/arch/alpha/include/asm/local.h b/arch/alpha/include/asm/local.h
index b9e3e33..9c94b84 100644
--- a/arch/alpha/include/asm/local.h
+++ b/arch/alpha/include/asm/local.h
@@ -2,7 +2,7 @@
 #define _ALPHA_LOCAL_H
 
 #include <linux/percpu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 typedef struct
 {
diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index 65cf3e2..fd698a1 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -72,7 +72,6 @@
 #define user_mode(regs) (((regs)->ps & 8) != 0)
 #define instruction_pointer(regs) ((regs)->pc)
 #define profile_pc(regs) instruction_pointer(regs)
-extern void show_regs(struct pt_regs *);
 
 #define task_pt_regs(task) \
   ((struct pt_regs *) (task_stack_page(task) + 2*PAGE_SIZE) - 1)
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index 8e47709..8143cd7 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -17,7 +17,7 @@
 #include <linux/init.h>
 
 #include <asm/hwrpb.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/irq_regs.h>
 #include <asm/pal.h>
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index d739703..4087a56 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -31,7 +31,7 @@
 
 #include <asm/hwrpb.h>
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
diff --git a/arch/alpha/kernel/sys_alcor.c b/arch/alpha/kernel/sys_alcor.c
index 0e14399..8606d77 100644
--- a/arch/alpha/kernel/sys_alcor.c
+++ b/arch/alpha/kernel/sys_alcor.c
@@ -183,7 +183,7 @@
  */
 
 static int __init
-alcor_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+alcor_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[7][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c
index c8c112d..1029619 100644
--- a/arch/alpha/kernel/sys_cabriolet.c
+++ b/arch/alpha/kernel/sys_cabriolet.c
@@ -175,7 +175,7 @@
  */
 
 static inline int __init
-eb66p_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+eb66p_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[5][5] __initdata = {
 		/*INT  INTA  INTB  INTC   INTD */
@@ -205,7 +205,7 @@
  */
 
 static inline int __init
-cabriolet_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+cabriolet_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[5][5] __initdata = {
 		/*INT   INTA  INTB  INTC   INTD */
@@ -289,7 +289,7 @@
  */
 
 static inline int __init
-alphapc164_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+alphapc164_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[7][5] __initdata = {
 		/*INT   INTA  INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index f885682..bb7f0c7 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -382,7 +382,7 @@
 }
 
 static int __init
-dp264_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+dp264_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[6][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
@@ -404,7 +404,7 @@
 }
 
 static int __init
-monet_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+monet_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[13][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
@@ -466,7 +466,7 @@
 }
 
 static int __init
-webbrick_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+webbrick_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[13][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
@@ -488,7 +488,7 @@
 }
 
 static int __init
-clipper_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+clipper_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[7][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_eb64p.c b/arch/alpha/kernel/sys_eb64p.c
index a7a23b4..3c6c13c 100644
--- a/arch/alpha/kernel/sys_eb64p.c
+++ b/arch/alpha/kernel/sys_eb64p.c
@@ -169,7 +169,7 @@
  */
 
 static int __init
-eb64p_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+eb64p_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[5][5] __initdata = {
 		/*INT  INTA  INTB  INTC   INTD */
diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c
index a60cd5b..35f480d 100644
--- a/arch/alpha/kernel/sys_eiger.c
+++ b/arch/alpha/kernel/sys_eiger.c
@@ -144,7 +144,7 @@
 }
 
 static int __init
-eiger_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+eiger_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	u8 irq_orig;
 
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c
index 388b99d..95cfc83 100644
--- a/arch/alpha/kernel/sys_marvel.c
+++ b/arch/alpha/kernel/sys_marvel.c
@@ -318,7 +318,7 @@
 }
 
 static int 
-marvel_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+marvel_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct pci_controller *hose = dev->sysdata;
 	struct io7_port *io7_port = hose->sysdata;
diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c
index 61ccd95..258da68 100644
--- a/arch/alpha/kernel/sys_miata.c
+++ b/arch/alpha/kernel/sys_miata.c
@@ -151,7 +151,7 @@
  */
 
 static int __init
-miata_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+miata_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
         static char irq_tab[18][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c
index 0e6e469..c0fd728 100644
--- a/arch/alpha/kernel/sys_mikasa.c
+++ b/arch/alpha/kernel/sys_mikasa.c
@@ -146,7 +146,7 @@
  */
 
 static int __init
-mikasa_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+mikasa_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[8][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c
index 99c0f46..4112200 100644
--- a/arch/alpha/kernel/sys_nautilus.c
+++ b/arch/alpha/kernel/sys_nautilus.c
@@ -65,7 +65,7 @@
 }
 
 static int __init
-nautilus_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+nautilus_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	/* Preserve the IRQ set up by the console.  */
 
diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c
index a00ac70..2172528 100644
--- a/arch/alpha/kernel/sys_noritake.c
+++ b/arch/alpha/kernel/sys_noritake.c
@@ -194,7 +194,7 @@
  */
 
 static int __init
-noritake_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+noritake_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[15][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c
index 7f52161..a125d6b 100644
--- a/arch/alpha/kernel/sys_rawhide.c
+++ b/arch/alpha/kernel/sys_rawhide.c
@@ -223,7 +223,7 @@
  */
 
 static int __init
-rawhide_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+rawhide_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[5][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c
index f33648e..2581cbe 100644
--- a/arch/alpha/kernel/sys_ruffian.c
+++ b/arch/alpha/kernel/sys_ruffian.c
@@ -119,7 +119,7 @@
  */
 
 static int __init
-ruffian_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+ruffian_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
         static char irq_tab[11][5] __initdata = {
 	      /*INT  INTA INTB INTC INTD */
diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c
index 216d94d..b172b27 100644
--- a/arch/alpha/kernel/sys_rx164.c
+++ b/arch/alpha/kernel/sys_rx164.c
@@ -144,7 +144,7 @@
  */
 
 static int __init
-rx164_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+rx164_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 #if 0
 	static char irq_tab_pass1[6][5] __initdata = {
diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c
index da714e4..98d1dbf 100644
--- a/arch/alpha/kernel/sys_sable.c
+++ b/arch/alpha/kernel/sys_sable.c
@@ -194,7 +194,7 @@
  */
 
 static int __init
-sable_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+sable_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[9][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
@@ -376,7 +376,7 @@
  */
 
 static int __init
-lynx_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+lynx_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[19][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_sio.c b/arch/alpha/kernel/sys_sio.c
index 85b4aea..47bec1e 100644
--- a/arch/alpha/kernel/sys_sio.c
+++ b/arch/alpha/kernel/sys_sio.c
@@ -146,7 +146,7 @@
 }
 
 static inline int __init
-noname_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+noname_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	/*
 	 * The Noname board has 5 PCI slots with each of the 4
@@ -185,7 +185,7 @@
 }
 
 static inline int __init
-p2k_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+p2k_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[][5] __initdata = {
 		/*INT A   B   C   D */
diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c
index 41d4ad4..73e1c31 100644
--- a/arch/alpha/kernel/sys_sx164.c
+++ b/arch/alpha/kernel/sys_sx164.c
@@ -95,7 +95,7 @@
  */
 
 static int __init
-sx164_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+sx164_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[5][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c
index a31f8cd..2ae99ad 100644
--- a/arch/alpha/kernel/sys_takara.c
+++ b/arch/alpha/kernel/sys_takara.c
@@ -157,7 +157,7 @@
  */
 
 static int __init
-takara_map_irq_srm(struct pci_dev *dev, u8 slot, u8 pin)
+takara_map_irq_srm(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[15][5] __initdata = {
 		{ 16+3, 16+3, 16+3, 16+3, 16+3},   /* slot  6 == device 3 */
@@ -188,7 +188,7 @@
 }
 
 static int __init
-takara_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+takara_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[15][5] __initdata = {
 		{ 16+3, 16+3, 16+3, 16+3, 16+3},   /* slot  6 == device 3 */
diff --git a/arch/alpha/kernel/sys_titan.c b/arch/alpha/kernel/sys_titan.c
index 6994407..f47b30a 100644
--- a/arch/alpha/kernel/sys_titan.c
+++ b/arch/alpha/kernel/sys_titan.c
@@ -305,7 +305,7 @@
 }
 
 static int __devinit
-titan_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+titan_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	u8 intline;
 	int irq;
diff --git a/arch/alpha/kernel/sys_wildfire.c b/arch/alpha/kernel/sys_wildfire.c
index d92cdc7..17c85a6 100644
--- a/arch/alpha/kernel/sys_wildfire.c
+++ b/arch/alpha/kernel/sys_wildfire.c
@@ -290,7 +290,7 @@
  */
 
 static int __init
-wildfire_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+wildfire_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static char irq_tab[8][5] __initdata = {
 		/*INT    INTA   INTB   INTC   INTD */
diff --git a/arch/alpha/lib/dec_and_lock.c b/arch/alpha/lib/dec_and_lock.c
index 0f5520d..f9f5fe8 100644
--- a/arch/alpha/lib/dec_and_lock.c
+++ b/arch/alpha/lib/dec_and_lock.c
@@ -6,7 +6,7 @@
  */
 
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
   asm (".text					\n\
 	.global _atomic_dec_and_lock		\n\
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9cb1f4b..2c71a8f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -242,6 +242,7 @@
 	select ARM_AMBA
 	select ARCH_HAS_CPUFREQ
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select ICST
 	select GENERIC_CLOCKEVENTS
 	select PLAT_VERSATILE
@@ -253,6 +254,7 @@
 	bool "ARM Ltd. RealView family"
 	select ARM_AMBA
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select ICST
 	select GENERIC_CLOCKEVENTS
 	select ARCH_WANT_OPTIONAL_GPIOLIB
@@ -268,6 +270,7 @@
 	select ARM_AMBA
 	select ARM_VIC
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select ICST
 	select GENERIC_CLOCKEVENTS
 	select ARCH_WANT_OPTIONAL_GPIOLIB
@@ -284,6 +287,7 @@
 	select ARM_AMBA
 	select ARM_TIMER_SP804
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select GENERIC_CLOCKEVENTS
 	select HAVE_CLK
 	select HAVE_PATA_PLATFORM
@@ -324,7 +328,7 @@
 
 config ARCH_CNS3XXX
 	bool "Cavium Networks CNS3XXX family"
-	select CPU_V6
+	select CPU_V6K
 	select GENERIC_CLOCKEVENTS
 	select ARM_GIC
 	select MIGHT_HAVE_PCI
@@ -340,6 +344,19 @@
 	help
 	  Support for the Cortina Systems Gemini family SoCs
 
+config ARCH_PRIMA2
+	bool "CSR SiRFSoC PRIMA2 ARM Cortex A9 Platform"
+	select CPU_V7
+	select GENERIC_TIME
+	select NO_IOPORT
+	select GENERIC_CLOCKEVENTS
+	select CLKDEV_LOOKUP
+	select GENERIC_IRQ_CHIP
+	select USE_OF
+	select ZONE_DMA
+	help
+          Support for CSR SiRFSoC ARM Cortex A9 Platform
+
 config ARCH_EBSA110
 	bool "EBSA-110"
 	select CPU_SA110
@@ -379,6 +396,7 @@
 	select ARCH_REQUIRE_GPIOLIB
 	select CLKDEV_LOOKUP
 	select CLKSRC_MMIO
+	select GENERIC_IRQ_CHIP
 	select HAVE_SCHED_CLOCK
 	help
 	  Support for Freescale MXC/iMX-based family of processors
@@ -586,7 +604,6 @@
 	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_SCHED_CLOCK
-	select ARCH_HAS_BARRIERS if CACHE_L2X0
 	select ARCH_HAS_CPUFREQ
 	help
 	  This enables support for NVIDIA Tegra based systems (Tegra APX,
@@ -613,6 +630,8 @@
 	select TICK_ONESHOT
 	select PLAT_PXA
 	select SPARSE_IRQ
+	select AUTO_ZRELADDR
+	select MULTI_IRQ_HANDLER
 	help
 	  Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
 
@@ -633,6 +652,7 @@
 	bool "Renesas SH-Mobile / R-Mobile"
 	select HAVE_CLK
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select GENERIC_CLOCKEVENTS
 	select NO_IOPORT
 	select SPARSE_IRQ
@@ -750,6 +770,7 @@
 	bool "Samsung S5PV210/S5PC110"
 	select CPU_V7
 	select ARCH_SPARSEMEM_ENABLE
+	select ARCH_HAS_HOLES_MEMORYMODEL
 	select GENERIC_GPIO
 	select HAVE_CLK
 	select CLKDEV_LOOKUP
@@ -768,6 +789,7 @@
 	bool "Samsung EXYNOS4"
 	select CPU_V7
 	select ARCH_SPARSEMEM_ENABLE
+	select ARCH_HAS_HOLES_MEMORYMODEL
 	select GENERIC_GPIO
 	select HAVE_CLK
 	select CLKDEV_LOOKUP
@@ -812,6 +834,7 @@
 	select ARM_VIC
 	select GENERIC_CLOCKEVENTS
 	select CLKDEV_LOOKUP
+	select HAVE_MACH_CLKDEV
 	select GENERIC_GPIO
 	help
 	  Support for ST-Ericsson U300 series mobile platforms.
@@ -884,6 +907,19 @@
 	select HAVE_PWM
 	help
 	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
+
+config ARCH_ZYNQ
+	bool "Xilinx Zynq ARM Cortex A9 Platform"
+	select CPU_V7
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select CLKDEV_LOOKUP
+	select ARM_GIC
+	select ARM_AMBA
+	select ICST
+	select USE_OF
+	help
+	  Support for Xilinx Zynq ARM Cortex A9 Platform
 endchoice
 
 #
@@ -1680,6 +1716,7 @@
 	bool "Flattened Device Tree support"
 	select OF
 	select OF_EARLY_FLATTREE
+	select IRQ_DOMAIN
 	help
 	  Include support for flattened device tree machine descriptions.
 
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 206c34e..70c424e 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -168,6 +168,7 @@
 machine-$(CONFIG_ARCH_OMAP4)		:= omap2
 machine-$(CONFIG_ARCH_ORION5X)		:= orion5x
 machine-$(CONFIG_ARCH_PNX4008)		:= pnx4008
+machine-$(CONFIG_ARCH_PRIMA2)		:= prima2
 machine-$(CONFIG_ARCH_PXA)		:= pxa
 machine-$(CONFIG_ARCH_REALVIEW)		:= realview
 machine-$(CONFIG_ARCH_RPC)		:= rpc
@@ -194,6 +195,7 @@
 machine-$(CONFIG_MACH_SPEAR310)		:= spear3xx
 machine-$(CONFIG_MACH_SPEAR320)		:= spear3xx
 machine-$(CONFIG_MACH_SPEAR600)		:= spear6xx
+machine-$(CONFIG_ARCH_ZYNQ)		:= zynq
 
 # Platform directory name.  This list is sorted alphanumerically
 # by CONFIG_* macro name.
@@ -201,6 +203,7 @@
 plat-$(CONFIG_ARCH_OMAP)	:= omap
 plat-$(CONFIG_ARCH_S3C64XX)	:= samsung
 plat-$(CONFIG_ARCH_TCC_926)	:= tcc
+plat-$(CONFIG_ARCH_ZYNQ)	:= versatile
 plat-$(CONFIG_PLAT_IOP)		:= iop
 plat-$(CONFIG_PLAT_NOMADIK)	:= nomadik
 plat-$(CONFIG_PLAT_ORION)	:= orion
@@ -279,6 +282,12 @@
 zinstall uinstall install: vmlinux
 	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
 
+%.dtb:
+	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+
+dtbs:
+	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+
 # We use MRPROPER_FILES and CLEAN_FILES now
 archclean:
 	$(Q)$(MAKE) $(clean)=$(boot)
@@ -295,6 +304,7 @@
   echo  '  uImage        - U-Boot wrapped zImage'
   echo  '  bootpImage    - Combined zImage and initial RAM disk' 
   echo  '                  (supply initrd image via make variable INITRD=<path>)'
+  echo  '  dtbs          - Build device tree blobs for enabled boards'
   echo  '  install       - Install uncompressed kernel'
   echo  '  zinstall      - Install compressed kernel'
   echo  '  uinstall      - Install U-Boot wrapped compressed kernel'
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index 9128fdd..a1edfd5 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -59,6 +59,12 @@
 
 endif
 
+# Rule to build device tree blobs
+$(obj)/%.dtb: $(src)/dts/%.dts
+	$(call cmd,dtc)
+
+$(obj)/dtbs: $(addprefix $(obj)/, $(dtb-y))
+
 quiet_cmd_uimage = UIMAGE  $@
       cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
 		   -C none -a $(LOADADDR) -e $(STARTADDR) \
diff --git a/arch/arm/boot/dts/prima2-cb.dts b/arch/arm/boot/dts/prima2-cb.dts
new file mode 100644
index 0000000..6fecc88
--- /dev/null
+++ b/arch/arm/boot/dts/prima2-cb.dts
@@ -0,0 +1,416 @@
+/dts-v1/;
+/ {
+	model = "SiRF Prima2 eVB";
+	compatible = "sirf,prima2-cb", "sirf,prima2";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&intc>;
+
+	memory {
+		reg = <0x00000000 0x20000000>;
+	};
+
+	chosen {
+		bootargs = "mem=512M real_root=/dev/mmcblk0p2 console=ttyS0 panel=1 bootsplash=true bpp=16 androidboot.console=ttyS1";
+		linux,stdout-path = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			reg = <0x0>;
+			d-cache-line-size = <32>;
+			i-cache-line-size = <32>;
+			d-cache-size = <32768>;
+			i-cache-size = <32768>;
+			/* from bootloader */
+			timebase-frequency = <0>;
+			bus-frequency = <0>;
+			clock-frequency = <0>;
+		};
+	};
+
+	axi {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x40000000 0x40000000 0x80000000>;
+
+		l2-cache-controller@80040000 {
+			compatible = "arm,pl310-cache";
+			reg = <0x80040000 0x1000>;
+			interrupts = <59>;
+		};
+
+		intc: interrupt-controller@80020000 {
+			#interrupt-cells = <1>;
+			interrupt-controller;
+			compatible = "sirf,prima2-intc";
+			reg = <0x80020000 0x1000>;
+		};
+
+		sys-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x88000000 0x88000000 0x40000>;
+
+			clock-controller@88000000 {
+				compatible = "sirf,prima2-clkc";
+				reg = <0x88000000 0x1000>;
+				interrupts = <3>;
+			};
+
+			reset-controller@88010000 {
+				compatible = "sirf,prima2-rstc";
+				reg = <0x88010000 0x1000>;
+			};
+		};
+
+		mem-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x90000000 0x90000000 0x10000>;
+
+			memory-controller@90000000 {
+				compatible = "sirf,prima2-memc";
+				reg = <0x90000000 0x10000>;
+				interrupts = <27>;
+			};
+		};
+
+		disp-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x90010000 0x90010000 0x30000>;
+
+			display@90010000 {
+				compatible = "sirf,prima2-lcd";
+				reg = <0x90010000 0x20000>;
+				interrupts = <30>;
+			};
+
+			vpp@90020000 {
+				compatible = "sirf,prima2-vpp";
+				reg = <0x90020000 0x10000>;
+				interrupts = <31>;
+			};
+		};
+
+		graphics-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x98000000 0x98000000 0x8000000>;
+
+			graphics@98000000 {
+				compatible = "powervr,sgx531";
+				reg = <0x98000000 0x8000000>;
+				interrupts = <6>;
+			};
+		};
+
+		multimedia-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xa0000000 0xa0000000 0x8000000>;
+
+			multimedia@a0000000 {
+				compatible = "sirf,prima2-video-codec";
+				reg = <0xa0000000 0x8000000>;
+				interrupts = <5>;
+			};
+		};
+
+		dsp-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xa8000000 0xa8000000 0x2000000>;
+
+			dspif@a8000000 {
+				compatible = "sirf,prima2-dspif";
+				reg = <0xa8000000 0x10000>;
+				interrupts = <9>;
+			};
+
+			gps@a8010000 {
+				compatible = "sirf,prima2-gps";
+				reg = <0xa8010000 0x10000>;
+				interrupts = <7>;
+			};
+
+			dsp@a9000000 {
+				compatible = "sirf,prima2-dsp";
+				reg = <0xa9000000 0x1000000>;
+				interrupts = <8>;
+			};
+		};
+
+		peri-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xb0000000 0xb0000000 0x180000>;
+
+			timer@b0020000 {
+				compatible = "sirf,prima2-tick";
+				reg = <0xb0020000 0x1000>;
+				interrupts = <0>;
+			};
+
+			nand@b0030000 {
+				compatible = "sirf,prima2-nand";
+				reg = <0xb0030000 0x10000>;
+				interrupts = <41>;
+			};
+
+			audio@b0040000 {
+				compatible = "sirf,prima2-audio";
+				reg = <0xb0040000 0x10000>;
+				interrupts = <35>;
+			};
+
+			uart0: uart@b0050000 {
+				cell-index = <0>;
+				compatible = "sirf,prima2-uart";
+				reg = <0xb0050000 0x10000>;
+				interrupts = <17>;
+			};
+
+			uart1: uart@b0060000 {
+				cell-index = <1>;
+				compatible = "sirf,prima2-uart";
+				reg = <0xb0060000 0x10000>;
+				interrupts = <18>;
+			};
+
+			uart2: uart@b0070000 {
+				cell-index = <2>;
+				compatible = "sirf,prima2-uart";
+				reg = <0xb0070000 0x10000>;
+				interrupts = <19>;
+			};
+
+			usp0: usp@b0080000 {
+				cell-index = <0>;
+				compatible = "sirf,prima2-usp";
+				reg = <0xb0080000 0x10000>;
+				interrupts = <20>;
+			};
+
+			usp1: usp@b0090000 {
+				cell-index = <1>;
+				compatible = "sirf,prima2-usp";
+				reg = <0xb0090000 0x10000>;
+				interrupts = <21>;
+			};
+
+			usp2: usp@b00a0000 {
+				cell-index = <2>;
+				compatible = "sirf,prima2-usp";
+				reg = <0xb00a0000 0x10000>;
+				interrupts = <22>;
+			};
+
+			dmac0: dma-controller@b00b0000 {
+				cell-index = <0>;
+				compatible = "sirf,prima2-dmac";
+				reg = <0xb00b0000 0x10000>;
+				interrupts = <12>;
+			};
+
+			dmac1: dma-controller@b0160000 {
+				cell-index = <1>;
+				compatible = "sirf,prima2-dmac";
+				reg = <0xb0160000 0x10000>;
+				interrupts = <13>;
+			};
+
+			vip@b00C0000 {
+				compatible = "sirf,prima2-vip";
+				reg = <0xb00C0000 0x10000>;
+			};
+
+			spi0: spi@b00d0000 {
+				cell-index = <0>;
+				compatible = "sirf,prima2-spi";
+				reg = <0xb00d0000 0x10000>;
+				interrupts = <15>;
+			};
+
+			spi1: spi@b0170000 {
+				cell-index = <1>;
+				compatible = "sirf,prima2-spi";
+				reg = <0xb0170000 0x10000>;
+				interrupts = <16>;
+			};
+
+			i2c0: i2c@b00e0000 {
+				cell-index = <0>;
+				compatible = "sirf,prima2-i2c";
+				reg = <0xb00e0000 0x10000>;
+				interrupts = <24>;
+			};
+
+			i2c1: i2c@b00f0000 {
+				cell-index = <1>;
+				compatible = "sirf,prima2-i2c";
+				reg = <0xb00f0000 0x10000>;
+				interrupts = <25>;
+			};
+
+			tsc@b0110000 {
+				compatible = "sirf,prima2-tsc";
+				reg = <0xb0110000 0x10000>;
+				interrupts = <33>;
+			};
+
+			gpio: gpio-controller@b0120000 {
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				compatible = "sirf,prima2-gpio";
+				reg = <0xb0120000 0x10000>;
+				gpio-controller;
+				interrupt-controller;
+			};
+
+			pwm@b0130000 {
+				compatible = "sirf,prima2-pwm";
+				reg = <0xb0130000 0x10000>;
+			};
+
+			efusesys@b0140000 {
+				compatible = "sirf,prima2-efuse";
+				reg = <0xb0140000 0x10000>;
+			};
+
+			pulsec@b0150000 {
+				compatible = "sirf,prima2-pulsec";
+				reg = <0xb0150000 0x10000>;
+				interrupts = <48>;
+			};
+
+			pci-iobg {
+				compatible = "sirf,prima2-pciiobg", "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x56000000 0x56000000 0x1b00000>;
+
+				sd0: sdhci@56000000 {
+					cell-index = <0>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56000000 0x100000>;
+					interrupts = <38>;
+				};
+
+				sd1: sdhci@56100000 {
+					cell-index = <1>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56100000 0x100000>;
+					interrupts = <38>;
+				};
+
+				sd2: sdhci@56200000 {
+					cell-index = <2>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56200000 0x100000>;
+					interrupts = <23>;
+				};
+
+				sd3: sdhci@56300000 {
+					cell-index = <3>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56300000 0x100000>;
+					interrupts = <23>;
+				};
+
+				sd4: sdhci@56400000 {
+					cell-index = <4>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56400000 0x100000>;
+					interrupts = <39>;
+				};
+
+				sd5: sdhci@56500000 {
+					cell-index = <5>;
+					compatible = "sirf,prima2-sdhc";
+					reg = <0x56500000 0x100000>;
+					interrupts = <39>;
+				};
+
+				pci-copy@57900000 {
+					compatible = "sirf,prima2-pcicp";
+					reg = <0x57900000 0x100000>;
+					interrupts = <40>;
+				};
+
+				rom-interface@57a00000 {
+					compatible = "sirf,prima2-romif";
+					reg = <0x57a00000 0x100000>;
+				};
+			};
+		};
+
+		rtc-iobg {
+			compatible = "sirf,prima2-rtciobg", "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			reg = <0x80030000 0x10000>;
+
+			gpsrtc@1000 {
+				compatible = "sirf,prima2-gpsrtc";
+				reg = <0x1000 0x1000>;
+				interrupts = <55 56 57>;
+			};
+
+			sysrtc@2000 {
+				compatible = "sirf,prima2-sysrtc";
+				reg = <0x2000 0x1000>;
+				interrupts = <52 53 54>;
+			};
+
+			pwrc@3000 {
+				compatible = "sirf,prima2-pwrc";
+				reg = <0x3000 0x1000>;
+				interrupts = <32>;
+			};
+		};
+
+		uus-iobg {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xb8000000 0xb8000000 0x40000>;
+
+			usb0: usb@b00e0000 {
+				compatible = "chipidea,ci13611a-prima2";
+				reg = <0xb8000000 0x10000>;
+				interrupts = <10>;
+			};
+
+			usb1: usb@b00f0000 {
+				compatible = "chipidea,ci13611a-prima2";
+				reg = <0xb8010000 0x10000>;
+				interrupts = <11>;
+			};
+
+			sata@b00f0000 {
+				compatible = "synopsys,dwc-ahsata";
+				reg = <0xb8020000 0x10000>;
+				interrupts = <37>;
+			};
+
+			security@b00f0000 {
+				compatible = "sirf,prima2-security";
+				reg = <0xb8030000 0x10000>;
+				interrupts = <42>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/skeleton.dtsi b/arch/arm/boot/dts/skeleton.dtsi
new file mode 100644
index 0000000..b41d241
--- /dev/null
+++ b/arch/arm/boot/dts/skeleton.dtsi
@@ -0,0 +1,13 @@
+/*
+ * Skeleton device tree; the bare minimum needed to boot; just include and
+ * add a compatible value.  The bootloader will typically populate the memory
+ * node.
+ */
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	chosen { };
+	aliases { };
+	memory { device_type = "memory"; reg = <0 0>; };
+};
diff --git a/arch/arm/boot/dts/tegra-harmony.dts b/arch/arm/boot/dts/tegra-harmony.dts
new file mode 100644
index 0000000..4c05334
--- /dev/null
+++ b/arch/arm/boot/dts/tegra-harmony.dts
@@ -0,0 +1,70 @@
+/dts-v1/;
+
+/memreserve/ 0x1c000000 0x04000000;
+/include/ "tegra20.dtsi"
+
+/ {
+	model = "NVIDIA Tegra2 Harmony evaluation board";
+	compatible = "nvidia,harmony", "nvidia,tegra20";
+
+	chosen {
+		bootargs = "vmalloc=192M video=tegrafb console=ttyS0,115200n8 root=/dev/mmcblk0p2 rw rootwait";
+	};
+
+	memory@0 {
+		reg = < 0x00000000 0x40000000 >;
+	};
+
+	i2c@7000c000 {
+		clock-frequency = <400000>;
+
+		codec: wm8903@1a {
+			compatible = "wlf,wm8903";
+			reg = <0x1a>;
+			interrupts = < 347 >;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			/* 0x8000 = Not configured */
+			gpio-cfg = < 0x8000 0x8000 0 0x8000 0x8000 >;
+		};
+	};
+
+	i2c@7000c400 {
+		clock-frequency = <400000>;
+	};
+
+	i2c@7000c500 {
+		clock-frequency = <400000>;
+	};
+
+	i2c@7000d000 {
+		clock-frequency = <400000>;
+	};
+
+	sound {
+		compatible = "nvidia,harmony-sound", "nvidia,tegra-wm8903";
+
+		spkr-en-gpios = <&codec 2 0>;
+		hp-det-gpios = <&gpio 178 0>;
+		int-mic-en-gpios = <&gpio 184 0>;
+		ext-mic-en-gpios = <&gpio 185 0>;
+	};
+
+	serial@70006300 {
+		clock-frequency = < 216000000 >;
+	};
+
+	sdhci@c8000200 {
+		gpios = <&gpio 69 0>, /* cd, gpio PI5 */
+			<&gpio 57 0>, /* wp, gpio PH1 */
+			<&gpio 155 0>; /* power, gpio PT3 */
+	};
+
+	sdhci@c8000600 {
+		gpios = <&gpio 58 0>, /* cd, gpio PH2 */
+			<&gpio 59 0>, /* wp, gpio PH3 */
+			<&gpio 70 0>; /* power, gpio PI6 */
+	};
+};
diff --git a/arch/arm/boot/dts/tegra-seaboard.dts b/arch/arm/boot/dts/tegra-seaboard.dts
new file mode 100644
index 0000000..1940cae
--- /dev/null
+++ b/arch/arm/boot/dts/tegra-seaboard.dts
@@ -0,0 +1,28 @@
+/dts-v1/;
+
+/memreserve/ 0x1c000000 0x04000000;
+/include/ "tegra20.dtsi"
+
+/ {
+	model = "NVIDIA Seaboard";
+	compatible = "nvidia,seaboard", "nvidia,tegra20";
+
+	chosen {
+		bootargs = "vmalloc=192M video=tegrafb console=ttyS0,115200n8 root=/dev/mmcblk1p3 rw rootwait";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = < 0x00000000 0x40000000 >;
+	};
+
+	serial@70006300 {
+		clock-frequency = < 216000000 >;
+	};
+
+	sdhci@c8000400 {
+		gpios = <&gpio 69 0>, /* cd, gpio PI5 */
+			<&gpio 57 0>, /* wp, gpio PH1 */
+			<&gpio 70 0>; /* power, gpio PI6 */
+	};
+};
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
new file mode 100644
index 0000000..5727595
--- /dev/null
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -0,0 +1,139 @@
+/include/ "skeleton.dtsi"
+
+/ {
+	compatible = "nvidia,tegra20";
+	interrupt-parent = <&intc>;
+
+	intc: interrupt-controller@50041000 {
+		compatible = "nvidia,tegra20-gic";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		reg = < 0x50041000 0x1000 >,
+		      < 0x50040100 0x0100 >;
+	};
+
+	i2c@7000c000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2c";
+		reg = <0x7000C000 0x100>;
+		interrupts = < 70 >;
+	};
+
+	i2c@7000c400 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2c";
+		reg = <0x7000C400 0x100>;
+		interrupts = < 116 >;
+	};
+
+	i2c@7000c500 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2c";
+		reg = <0x7000C500 0x100>;
+		interrupts = < 124 >;
+	};
+
+	i2c@7000d000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2c";
+		reg = <0x7000D000 0x200>;
+		interrupts = < 85 >;
+	};
+
+	i2s@70002800 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2s";
+		reg = <0x70002800 0x200>;
+		interrupts = < 45 >;
+		dma-channel = < 2 >;
+	};
+
+	i2s@70002a00 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-i2s";
+		reg = <0x70002a00 0x200>;
+		interrupts = < 35 >;
+		dma-channel = < 1 >;
+	};
+
+	das@70000c00 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "nvidia,tegra20-das";
+		reg = <0x70000c00 0x80>;
+	};
+
+	gpio: gpio@6000d000 {
+		compatible = "nvidia,tegra20-gpio";
+		reg = < 0x6000d000 0x1000 >;
+		interrupts = < 64 65 66 67 87 119 121 >;
+		#gpio-cells = <2>;
+		gpio-controller;
+	};
+
+	serial@70006000 {
+		compatible = "nvidia,tegra20-uart";
+		reg = <0x70006000 0x40>;
+		reg-shift = <2>;
+		interrupts = < 68 >;
+	};
+
+	serial@70006040 {
+		compatible = "nvidia,tegra20-uart";
+		reg = <0x70006040 0x40>;
+		reg-shift = <2>;
+		interrupts = < 69 >;
+	};
+
+	serial@70006200 {
+		compatible = "nvidia,tegra20-uart";
+		reg = <0x70006200 0x100>;
+		reg-shift = <2>;
+		interrupts = < 78 >;
+	};
+
+	serial@70006300 {
+		compatible = "nvidia,tegra20-uart";
+		reg = <0x70006300 0x100>;
+		reg-shift = <2>;
+		interrupts = < 122 >;
+	};
+
+	serial@70006400 {
+		compatible = "nvidia,tegra20-uart";
+		reg = <0x70006400 0x100>;
+		reg-shift = <2>;
+		interrupts = < 123 >;
+	};
+
+	sdhci@c8000000 {
+		compatible = "nvidia,tegra20-sdhci";
+		reg = <0xc8000000 0x200>;
+		interrupts = < 46 >;
+	};
+
+	sdhci@c8000200 {
+		compatible = "nvidia,tegra20-sdhci";
+		reg = <0xc8000200 0x200>;
+		interrupts = < 47 >;
+	};
+
+	sdhci@c8000400 {
+		compatible = "nvidia,tegra20-sdhci";
+		reg = <0xc8000400 0x200>;
+		interrupts = < 51 >;
+	};
+
+	sdhci@c8000600 {
+		compatible = "nvidia,tegra20-sdhci";
+		reg = <0xc8000600 0x200>;
+		interrupts = < 63 >;
+	};
+};
+
diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
new file mode 100644
index 0000000..0b32925
--- /dev/null
+++ b/arch/arm/boot/dts/versatile-ab.dts
@@ -0,0 +1,192 @@
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+	model = "ARM Versatile AB";
+	compatible = "arm,versatile-ab";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&vic>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		i2c0 = &i2c0;
+	};
+
+	memory {
+		reg = <0x0 0x08000000>;
+	};
+
+	flash@34000000 {
+		compatible = "arm,versatile-flash";
+		reg = <0x34000000 0x4000000>;
+		bank-width = <4>;
+	};
+
+	i2c0: i2c@10002000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "arm,versatile-i2c";
+		reg = <0x10002000 0x1000>;
+
+		rtc@68 {
+			compatible = "dallas,ds1338";
+			reg = <0x68>;
+		};
+	};
+
+	net@10010000 {
+		compatible = "smsc,lan91c111";
+		reg = <0x10010000 0x10000>;
+		interrupts = <25>;
+	};
+
+	lcd@10008000 {
+		compatible = "arm,versatile-lcd";
+		reg = <0x10008000 0x1000>;
+	};
+
+	amba {
+		compatible = "arm,amba-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		vic: intc@10140000 {
+			compatible = "arm,versatile-vic";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x10140000 0x1000>;
+		};
+
+		sic: intc@10003000 {
+			compatible = "arm,versatile-sic";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x10003000 0x1000>;
+			interrupt-parent = <&vic>;
+			interrupts = <31>; /* Cascaded to vic */
+		};
+
+		dma@10130000 {
+			compatible = "arm,pl081", "arm,primecell";
+			reg = <0x10130000 0x1000>;
+			interrupts = <17>;
+		};
+
+		uart0: uart@101f1000 {
+			compatible = "arm,pl011", "arm,primecell";
+			reg = <0x101f1000 0x1000>;
+			interrupts = <12>;
+		};
+
+		uart1: uart@101f2000 {
+			compatible = "arm,pl011", "arm,primecell";
+			reg = <0x101f2000 0x1000>;
+			interrupts = <13>;
+		};
+
+		uart2: uart@101f3000 {
+			compatible = "arm,pl011", "arm,primecell";
+			reg = <0x101f3000 0x1000>;
+			interrupts = <14>;
+		};
+
+		smc@10100000 {
+			compatible = "arm,primecell";
+			reg = <0x10100000 0x1000>;
+		};
+
+		mpmc@10110000 {
+			compatible = "arm,primecell";
+			reg = <0x10110000 0x1000>;
+		};
+
+		display@10120000 {
+			compatible = "arm,pl110", "arm,primecell";
+			reg = <0x10120000 0x1000>;
+			interrupts = <16>;
+		};
+
+		sctl@101e0000 {
+			compatible = "arm,primecell";
+			reg = <0x101e0000 0x1000>;
+		};
+
+		watchdog@101e1000 {
+			compatible = "arm,primecell";
+			reg = <0x101e1000 0x1000>;
+			interrupts = <0>;
+		};
+
+		gpio0: gpio@101e4000 {
+			compatible = "arm,pl061", "arm,primecell";
+			reg = <0x101e4000 0x1000>;
+			gpio-controller;
+			interrupts = <6>;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio1: gpio@101e5000 {
+			compatible = "arm,pl061", "arm,primecell";
+			reg = <0x101e5000 0x1000>;
+			interrupts = <7>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		rtc@101e8000 {
+			compatible = "arm,pl030", "arm,primecell";
+			reg = <0x101e8000 0x1000>;
+			interrupts = <10>;
+		};
+
+		sci@101f0000 {
+			compatible = "arm,primecell";
+			reg = <0x101f0000 0x1000>;
+			interrupts = <15>;
+		};
+
+		ssp@101f4000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x101f4000 0x1000>;
+			interrupts = <11>;
+		};
+
+		fpga {
+			compatible = "arm,versatile-fpga", "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0x10000000 0x10000>;
+
+			aaci@4000 {
+				compatible = "arm,primecell";
+				reg = <0x4000 0x1000>;
+				interrupts = <24>;
+			};
+			mmc@5000 {
+				compatible = "arm,primecell";
+				reg = < 0x5000 0x1000>;
+				interrupts = <22>;
+			};
+			kmi@6000 {
+				compatible = "arm,pl050", "arm,primecell";
+				reg = <0x6000 0x1000>;
+				interrupt-parent = <&sic>;
+				interrupts = <3>;
+			};
+			kmi@7000 {
+				compatible = "arm,pl050", "arm,primecell";
+				reg = <0x7000 0x1000>;
+				interrupt-parent = <&sic>;
+				interrupts = <4>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts
new file mode 100644
index 0000000..8a614e3
--- /dev/null
+++ b/arch/arm/boot/dts/versatile-pb.dts
@@ -0,0 +1,48 @@
+/include/ "versatile-ab.dts"
+
+/ {
+	model = "ARM Versatile PB";
+	compatible = "arm,versatile-pb";
+
+	amba {
+		gpio2: gpio@101e6000 {
+			compatible = "arm,pl061", "arm,primecell";
+			reg = <0x101e6000 0x1000>;
+			interrupts = <8>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio3: gpio@101e7000 {
+			compatible = "arm,pl061", "arm,primecell";
+			reg = <0x101e7000 0x1000>;
+			interrupts = <9>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		fpga {
+			uart@9000 {
+				compatible = "arm,pl011", "arm,primecell";
+				reg = <0x9000 0x1000>;
+				interrupt-parent = <&sic>;
+				interrupts = <6>;
+			};
+			sci@a000 {
+				compatible = "arm,primecell";
+				reg = <0xa000 0x1000>;
+				interrupt-parent = <&sic>;
+				interrupts = <5>;
+			};
+			mmc@b000 {
+				compatible = "arm,primecell";
+				reg = <0xb000 0x1000>;
+				interrupts = <23>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/zynq-ep107.dts b/arch/arm/boot/dts/zynq-ep107.dts
new file mode 100644
index 0000000..37ca192
--- /dev/null
+++ b/arch/arm/boot/dts/zynq-ep107.dts
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/dts-v1/;
+/ {
+	model = "Xilinx Zynq EP107";
+	compatible = "xlnx,zynq-ep107";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&intc>;
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x10000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyPS0,9600 root=/dev/ram rw initrd=0x800000,8M earlyprintk";
+		linux,stdout-path = &uart0;
+	};
+
+	amba {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		intc: interrupt-controller@f8f01000 {
+			interrupt-controller;
+			compatible = "arm,gic";
+			reg = <0xF8F01000 0x1000>;
+			#interrupt-cells = <2>;
+		};
+
+		uart0: uart@e0000000 {
+			compatible = "xlnx,xuartps";
+			reg = <0xE0000000 0x1000>;
+			interrupts = <59 0>;
+			clock = <50000000>;
+		};
+	};
+};
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 7bdd917..3227ca9 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -38,12 +38,6 @@
 /* Address of GIC 0 CPU interface */
 void __iomem *gic_cpu_base_addr __read_mostly;
 
-struct gic_chip_data {
-	unsigned int irq_offset;
-	void __iomem *dist_base;
-	void __iomem *cpu_base;
-};
-
 /*
  * Supported arch specific GIC irq extension.
  * Default make them NULL.
diff --git a/arch/arm/common/it8152.c b/arch/arm/common/it8152.c
index 14ad62e..a7934ba 100644
--- a/arch/arm/common/it8152.c
+++ b/arch/arm/common/it8152.c
@@ -144,7 +144,7 @@
 }
 
 /* mapping for on-chip devices */
-int __init it8152_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int __init it8152_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if ((dev->vendor == PCI_VENDOR_ID_ITE) &&
 	    (dev->device == PCI_DEVICE_ID_ITE_8152)) {
diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig
index 5a6ff7c..db2cb7d 100644
--- a/arch/arm/configs/mxs_defconfig
+++ b/arch/arm/configs/mxs_defconfig
@@ -22,6 +22,8 @@
 # CONFIG_IOSCHED_DEADLINE is not set
 # CONFIG_IOSCHED_CFQ is not set
 CONFIG_ARCH_MXS=y
+CONFIG_MACH_MX23EVK=y
+CONFIG_MACH_MX28EVK=y
 CONFIG_MACH_STMP378X_DEVB=y
 CONFIG_MACH_TX28=y
 # CONFIG_ARM_THUMB is not set
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index a5cce24..97d31a4 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -11,12 +11,12 @@
 CONFIG_UX500_SOC_DB5500=y
 CONFIG_UX500_SOC_DB8500=y
 CONFIG_MACH_U8500=y
+CONFIG_MACH_SNOWBALL=y
 CONFIG_MACH_U5500=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=2
-CONFIG_HOTPLUG_CPU=y
 CONFIG_PREEMPT=y
 CONFIG_AEABI=y
 CONFIG_CMDLINE="root=/dev/ram0 console=ttyAMA2,115200n8"
@@ -25,8 +25,13 @@
 CONFIG_VFP=y
 CONFIG_NEON=y
 CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_NETFILTER=y
 CONFIG_PHONET=y
-CONFIG_PHONET_PIPECTRLR=y
 # CONFIG_WIRELESS is not set
 CONFIG_CAIF=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
@@ -35,6 +40,13 @@
 CONFIG_MISC_DEVICES=y
 CONFIG_AB8500_PWM=y
 CONFIG_SENSORS_BH1780=y
+CONFIG_NETDEVICES=y
+CONFIG_SMSC_PHY=y
+CONFIG_NET_ETHERNET=y
+CONFIG_SMSC911X=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_WLAN is not set
 # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
 CONFIG_INPUT_EVDEV=y
 # CONFIG_KEYBOARD_ATKBD is not set
@@ -49,9 +61,9 @@
 CONFIG_INPUT_AB8500_PONKEY=y
 # CONFIG_SERIO is not set
 CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_AMBA_PL011=y
 CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
-# CONFIG_LEGACY_PTYS is not set
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_NOMADIK=y
 CONFIG_I2C=y
@@ -64,14 +76,19 @@
 CONFIG_MFD_STMPE=y
 CONFIG_MFD_TC3589X=y
 CONFIG_AB8500_CORE=y
-CONFIG_REGULATOR=y
 CONFIG_REGULATOR_AB8500=y
 # CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_MUSB_PIO_ONLY=y
+CONFIG_USB_GADGET=y
+CONFIG_AB8500_USB=y
 CONFIG_MMC=y
+CONFIG_MMC_CLKGATE=y
 CONFIG_MMC_ARMMMCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_LM3530=y
 CONFIG_LEDS_LP5521=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_AB8500=y
@@ -79,7 +96,6 @@
 CONFIG_DMADEVICES=y
 CONFIG_STE_DMA40=y
 CONFIG_STAGING=y
-# CONFIG_STAGING_EXCLUDE_BUILD is not set
 CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT2_FS_XATTR=y
@@ -91,6 +107,8 @@
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_CONFIGFS_FS=m
 # CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_MAGIC_SYSRQ=y
@@ -99,7 +117,5 @@
 # CONFIG_SCHED_DEBUG is not set
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_INFO=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
 # CONFIG_FTRACE is not set
 CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
index 7e79503..86976d0 100644
--- a/arch/arm/include/asm/atomic.h
+++ b/arch/arm/include/asm/atomic.h
@@ -208,16 +208,15 @@
 
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 
 	c = atomic_read(v);
 	while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
 		c = old;
-	return c != u;
+	return c;
 }
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #define atomic_inc(v)		atomic_add(1, v)
 #define atomic_dec(v)		atomic_sub(1, v)
@@ -460,9 +459,6 @@
 #define atomic64_dec_and_test(v)	(atomic64_dec_return((v)) == 0)
 #define atomic64_inc_not_zero(v)	atomic64_add_unless((v), 1LL, 0LL)
 
-#else /* !CONFIG_GENERIC_ATOMIC64 */
-#include <asm-generic/atomic64.h>
-#endif
-#include <asm-generic/atomic-long.h>
+#endif /* !CONFIG_GENERIC_ATOMIC64 */
 #endif
 #endif
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
index f428059..f7419ef 100644
--- a/arch/arm/include/asm/bitops.h
+++ b/arch/arm/include/asm/bitops.h
@@ -310,10 +310,7 @@
 /*
  * Ext2 is defined to use little-endian byte ordering.
  */
-#define ext2_set_bit_atomic(lock, nr, p)	\
-		test_and_set_bit_le(nr, p)
-#define ext2_clear_bit_atomic(lock, nr, p)	\
-		test_and_clear_bit_le(nr, p)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* __KERNEL__ */
 
diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h
index 765d332..80751c1 100644
--- a/arch/arm/include/asm/clkdev.h
+++ b/arch/arm/include/asm/clkdev.h
@@ -14,7 +14,12 @@
 
 #include <linux/slab.h>
 
+#ifdef CONFIG_HAVE_MACH_CLKDEV
 #include <mach/clkdev.h>
+#else
+#define __clk_get(clk)	({ 1; })
+#define __clk_put(clk)	do { } while (0)
+#endif
 
 static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
 {
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 0691f9d..435d3f8 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -41,6 +41,12 @@
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
 void gic_enable_ppi(unsigned int);
+
+struct gic_chip_data {
+	unsigned int irq_offset;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+};
 #endif
 
 #endif
diff --git a/arch/arm/include/asm/hardware/it8152.h b/arch/arm/include/asm/hardware/it8152.h
index b2f95c7..b3fea38 100644
--- a/arch/arm/include/asm/hardware/it8152.h
+++ b/arch/arm/include/asm/hardware/it8152.h
@@ -105,7 +105,7 @@
 
 extern void it8152_irq_demux(unsigned int irq, struct irq_desc *desc);
 extern void it8152_init_irq(void);
-extern int it8152_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin);
+extern int it8152_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
 extern int it8152_pci_setup(int nr, struct pci_sys_data *sys);
 extern struct pci_bus *it8152_pci_scan_bus(int nr, struct pci_sys_data *sys);
 
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 2721a58..5a526af 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -23,6 +23,7 @@
 extern void migrate_irqs(void);
 
 extern void asm_do_IRQ(unsigned int, struct pt_regs *);
+void handle_IRQ(unsigned int, struct pt_regs *);
 void init_IRQ(void);
 
 #endif
diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 3281fb4..217aa19 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -74,4 +74,11 @@
 #define MACHINE_END				\
 };
 
+#define DT_MACHINE_START(_name, _namestr)		\
+static const struct machine_desc __mach_desc_##_name	\
+ __used							\
+ __attribute__((__section__(".arch.info.init"))) = {	\
+	.nr		= ~0,				\
+	.name		= _namestr,
+
 #endif
diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h
index 16330bd..186efd4 100644
--- a/arch/arm/include/asm/mach/pci.h
+++ b/arch/arm/include/asm/mach/pci.h
@@ -25,7 +25,7 @@
 	void		(*preinit)(void);
 	void		(*postinit)(void);
 	u8		(*swizzle)(struct pci_dev *dev, u8 *pin);
-	int		(*map_irq)(struct pci_dev *dev, u8 slot, u8 pin);
+	int		(*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
 };
 
 /*
@@ -44,7 +44,7 @@
 					/* Bridge swizzling			*/
 	u8		(*swizzle)(struct pci_dev *, u8 *);
 					/* IRQ mapping				*/
-	int		(*map_irq)(struct pci_dev *, u8, u8);
+	int		(*map_irq)(const struct pci_dev *, u8, u8);
 	struct hw_pci	*hw;
 	void		*private_data;	/* platform controller private data	*/
 };
diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h
index 92e2a83..2b1f245 100644
--- a/arch/arm/include/asm/pci.h
+++ b/arch/arm/include/asm/pci.h
@@ -3,9 +3,19 @@
 
 #ifdef __KERNEL__
 #include <asm-generic/pci-dma-compat.h>
+#include <asm-generic/pci-bridge.h>
 
 #include <asm/mach/pci.h> /* for pci_sys_data */
-#include <mach/hardware.h> /* for PCIBIOS_MIN_* */
+
+extern unsigned long pcibios_min_io;
+#define PCIBIOS_MIN_IO pcibios_min_io
+extern unsigned long pcibios_min_mem;
+#define PCIBIOS_MIN_MEM pcibios_min_mem
+
+static inline int pcibios_assign_all_busses(void)
+{
+	return pci_has_flag(PCI_REASSIGN_ALL_RSRC);
+}
 
 #ifdef CONFIG_PCI_DOMAINS
 static inline int pci_domain_nr(struct pci_bus *bus)
diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h
index 11b8708..6f65ca8 100644
--- a/arch/arm/include/asm/prom.h
+++ b/arch/arm/include/asm/prom.h
@@ -16,11 +16,6 @@
 #include <asm/setup.h>
 #include <asm/irq.h>
 
-static inline void irq_dispose_mapping(unsigned int virq)
-{
-	return;
-}
-
 extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
 extern void arm_dt_memblock_reserve(void);
 
diff --git a/arch/arm/include/asm/vga.h b/arch/arm/include/asm/vga.h
index 250a4dd..91f4021 100644
--- a/arch/arm/include/asm/vga.h
+++ b/arch/arm/include/asm/vga.h
@@ -2,9 +2,10 @@
 #define ASMARM_VGA_H
 
 #include <linux/io.h>
-#include <mach/hardware.h>
 
-#define VGA_MAP_MEM(x,s)	(PCIMEM_BASE + (x))
+extern unsigned long vga_base;
+
+#define VGA_MAP_MEM(x,s)	(vga_base + (x))
 
 #define vga_readb(x)	(*((volatile unsigned char *)x))
 #define vga_writeb(x,y)	(*((volatile unsigned char *)y) = (x))
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index e4ee050..d6df359 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -476,7 +476,7 @@
 /*
  * Map a slot/pin to an IRQ.
  */
-static int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct pci_sys_data *sys = dev->sysdata;
 	int irq = -1;
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index 0cdd7b4..1a33e9d 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -132,17 +132,3 @@
 
 	return mdesc_best;
 }
-
-/**
- * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
- *
- * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
- * mapped 1:1 onto Linux irq numbers.  Cascaded irq controllers are not
- * supported.
- */
-unsigned int irq_create_of_mapping(struct device_node *controller,
-				   const u32 *intspec, unsigned int intsize)
-{
-	return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 0f928a1..de3dcab 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -67,12 +67,12 @@
 }
 
 /*
- * do_IRQ handles all hardware IRQ's.  Decoded IRQs should not
- * come via this function.  Instead, they should provide their
- * own 'handler'
+ * handle_IRQ handles all hardware IRQ's.  Decoded IRQs should
+ * not come via this function.  Instead, they should provide their
+ * own 'handler'.  Used by platform code implementing C-based 1st
+ * level decoding.
  */
-asmlinkage void __exception_irq_entry
-asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
+void handle_IRQ(unsigned int irq, struct pt_regs *regs)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
@@ -97,6 +97,15 @@
 	set_irq_regs(old_regs);
 }
 
+/*
+ * asm_do_IRQ is the interface to be used from assembly code.
+ */
+asmlinkage void __exception_irq_entry
+asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
+{
+	handle_IRQ(irq, regs);
+}
+
 void set_irq_flags(unsigned int irq, unsigned int iflags)
 {
 	unsigned long clr = 0, set = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 167e3cb..d88ff02 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -27,7 +27,7 @@
 #include <linux/clockchips.h>
 #include <linux/completion.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/cpu.h>
 #include <asm/cputype.h>
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 2d3436e..bc9f9da 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -25,7 +25,7 @@
 #include <linux/init.h>
 #include <linux/sched.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/system.h>
 #include <asm/unistd.h>
diff --git a/arch/arm/lib/ecard.S b/arch/arm/lib/ecard.S
index 8678eb2..e6057fa 100644
--- a/arch/arm/lib/ecard.S
+++ b/arch/arm/lib/ecard.S
@@ -12,7 +12,6 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
-#include <mach/hardware.h>
 
 #define CPSR2SPSR(rt) \
 		mrs	rt, cpsr; \
diff --git a/arch/arm/lib/io-readsw-armv3.S b/arch/arm/lib/io-readsw-armv3.S
index 9aaf7c7..88487c8 100644
--- a/arch/arm/lib/io-readsw-armv3.S
+++ b/arch/arm/lib/io-readsw-armv3.S
@@ -9,7 +9,6 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
-#include <mach/hardware.h>
 
 .Linsw_bad_alignment:
 		adr	r0, .Linsw_bad_align_msg
diff --git a/arch/arm/lib/io-writesw-armv3.S b/arch/arm/lib/io-writesw-armv3.S
index cd34503..49b8004 100644
--- a/arch/arm/lib/io-writesw-armv3.S
+++ b/arch/arm/lib/io-writesw-armv3.S
@@ -9,7 +9,6 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
-#include <mach/hardware.h>
 
 .Loutsw_bad_alignment:
 		adr	r0, .Loutsw_bad_align_msg
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index 9696623..bf57e8b 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the linux kernel.
 #
 
-obj-y		:= irq.o gpio.o
+obj-y		:= irq.o gpio.o setup.o
 obj-m		:=
 obj-n		:=
 obj-		:=
diff --git a/arch/arm/mach-at91/at91cap9.c b/arch/arm/mach-at91/at91cap9.c
index f1013d0..bfc6844 100644
--- a/arch/arm/mach-at91/at91cap9.c
+++ b/arch/arm/mach-at91/at91cap9.c
@@ -25,23 +25,10 @@
 #include <mach/at91_rstc.h>
 #include <mach/at91_shdwc.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91cap9_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91CAP9_SRAM_SIZE,
-		.pfn		= __phys_to_pfn(AT91CAP9_SRAM_BASE),
-		.length		= AT91CAP9_SRAM_SIZE,
-		.type		= MT_DEVICE,
-	},
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -339,24 +326,17 @@
  *  AT91CAP9 processor initialization
  * -------------------------------------------------------------------- */
 
-void __init at91cap9_map_io(void)
+static void __init at91cap9_map_io(void)
 {
-	/* Map peripherals */
-	iotable_init(at91cap9_io_desc, ARRAY_SIZE(at91cap9_io_desc));
+	at91_init_sram(0, AT91CAP9_SRAM_BASE, AT91CAP9_SRAM_SIZE);
 }
 
-void __init at91cap9_initialize(unsigned long main_clock)
+static void __init at91cap9_initialize(void)
 {
 	at91_arch_reset = at91cap9_reset;
 	pm_power_off = at91cap9_poweroff;
 	at91_extern_irq = (1 << AT91CAP9_ID_IRQ0) | (1 << AT91CAP9_ID_IRQ1);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91cap9_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91cap9_gpio, 4);
 
@@ -409,14 +389,9 @@
 	0,	/* Advanced Interrupt Controller (IRQ1) */
 };
 
-void __init at91cap9_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91cap9_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91cap9_soc = {
+	.map_io = at91cap9_map_io,
+	.default_irq_priority = at91cap9_default_irq_priority,
+	.register_clocks = at91cap9_register_clocks,
+	.init = at91cap9_initialize,
+};
diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
index 83a1a3f..f73302d 100644
--- a/arch/arm/mach-at91/at91rm9200.c
+++ b/arch/arm/mach-at91/at91rm9200.c
@@ -20,25 +20,16 @@
 #include <mach/at91_st.h>
 #include <mach/cpu.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
 static struct map_desc at91rm9200_io_desc[] __initdata = {
 	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_4K,
-		.type		= MT_DEVICE,
-	}, {
 		.virtual	= AT91_VA_BASE_EMAC,
 		.pfn		= __phys_to_pfn(AT91RM9200_BASE_EMAC),
 		.length		= SZ_16K,
 		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91RM9200_SRAM_SIZE,
-		.pfn		= __phys_to_pfn(AT91RM9200_SRAM_BASE),
-		.length		= AT91RM9200_SRAM_SIZE,
-		.type		= MT_DEVICE,
 	},
 };
 
@@ -304,24 +295,17 @@
 	at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
 }
 
-int rm9200_type;
-EXPORT_SYMBOL(rm9200_type);
-
-void __init at91rm9200_set_type(int type)
-{
-	rm9200_type = type;
-}
-
 /* --------------------------------------------------------------------
  *  AT91RM9200 processor initialization
  * -------------------------------------------------------------------- */
-void __init at91rm9200_map_io(void)
+static void __init at91rm9200_map_io(void)
 {
 	/* Map peripherals */
+	at91_init_sram(0, AT91RM9200_SRAM_BASE, AT91RM9200_SRAM_SIZE);
 	iotable_init(at91rm9200_io_desc, ARRAY_SIZE(at91rm9200_io_desc));
 }
 
-void __init at91rm9200_initialize(unsigned long main_clock)
+static void __init at91rm9200_initialize(void)
 {
 	at91_arch_reset = at91rm9200_reset;
 	at91_extern_irq = (1 << AT91RM9200_ID_IRQ0) | (1 << AT91RM9200_ID_IRQ1)
@@ -329,12 +313,6 @@
 			| (1 << AT91RM9200_ID_IRQ4) | (1 << AT91RM9200_ID_IRQ5)
 			| (1 << AT91RM9200_ID_IRQ6);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91rm9200_register_clocks();
-
 	/* Initialize GPIO subsystem */
 	at91_gpio_init(at91rm9200_gpio,
 		cpu_is_at91rm9200_bga() ? AT91RM9200_BGA : AT91RM9200_PQFP);
@@ -383,14 +361,9 @@
 	0	/* Advanced Interrupt Controller (IRQ6) */
 };
 
-void __init at91rm9200_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91rm9200_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91rm9200_soc = {
+	.map_io = at91rm9200_map_io,
+	.default_irq_priority = at91rm9200_default_irq_priority,
+	.register_clocks = at91rm9200_register_clocks,
+	.init = at91rm9200_initialize,
+};
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index 7d606b0..cb397be 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -17,58 +17,16 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/cpu.h>
+#include <mach/at91_dbgu.h>
 #include <mach/at91sam9260.h>
 #include <mach/at91_pmc.h>
 #include <mach/at91_rstc.h>
 #include <mach/at91_shdwc.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91sam9260_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	}
-};
-
-static struct map_desc at91sam9260_sram_desc[] __initdata = {
-	{
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9260_SRAM0_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9260_SRAM0_BASE),
-		.length		= AT91SAM9260_SRAM0_SIZE,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9260_SRAM0_SIZE - AT91SAM9260_SRAM1_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9260_SRAM1_BASE),
-		.length		= AT91SAM9260_SRAM1_SIZE,
-		.type		= MT_DEVICE,
-	}
-};
-
-static struct map_desc at91sam9g20_sram_desc[] __initdata = {
-	{
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9G20_SRAM0_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9G20_SRAM0_BASE),
-		.length		= AT91SAM9G20_SRAM0_SIZE,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9G20_SRAM0_SIZE - AT91SAM9G20_SRAM1_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9G20_SRAM1_BASE),
-		.length		= AT91SAM9G20_SRAM1_SIZE,
-		.type		= MT_DEVICE,
-	}
-};
-
-static struct map_desc at91sam9xe_sram_desc[] __initdata = {
-	{
-		.pfn		= __phys_to_pfn(AT91SAM9XE_SRAM_BASE),
-		.type		= MT_DEVICE,
-	}
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -330,11 +288,9 @@
 
 static void __init at91sam9xe_map_io(void)
 {
-	unsigned long cidr, sram_size;
+	unsigned long sram_size;
 
-	cidr = at91_sys_read(AT91_DBGU_CIDR);
-
-	switch (cidr & AT91_CIDR_SRAMSIZ) {
+	switch (at91_soc_initdata.cidr & AT91_CIDR_SRAMSIZ) {
 		case AT91_CIDR_SRAMSIZ_32K:
 			sram_size = 2 * SZ_16K;
 			break;
@@ -343,38 +299,29 @@
 			sram_size = SZ_16K;
 	}
 
-	at91sam9xe_sram_desc->virtual = AT91_IO_VIRT_BASE - sram_size;
-	at91sam9xe_sram_desc->length = sram_size;
-
-	iotable_init(at91sam9xe_sram_desc, ARRAY_SIZE(at91sam9xe_sram_desc));
+	at91_init_sram(0, AT91SAM9XE_SRAM_BASE, sram_size);
 }
 
-void __init at91sam9260_map_io(void)
+static void __init at91sam9260_map_io(void)
 {
-	/* Map peripherals */
-	iotable_init(at91sam9260_io_desc, ARRAY_SIZE(at91sam9260_io_desc));
-
-	if (cpu_is_at91sam9xe())
+	if (cpu_is_at91sam9xe()) {
 		at91sam9xe_map_io();
-	else if (cpu_is_at91sam9g20())
-		iotable_init(at91sam9g20_sram_desc, ARRAY_SIZE(at91sam9g20_sram_desc));
-	else
-		iotable_init(at91sam9260_sram_desc, ARRAY_SIZE(at91sam9260_sram_desc));
+	} else if (cpu_is_at91sam9g20()) {
+		at91_init_sram(0, AT91SAM9G20_SRAM0_BASE, AT91SAM9G20_SRAM0_SIZE);
+		at91_init_sram(1, AT91SAM9G20_SRAM1_BASE, AT91SAM9G20_SRAM1_SIZE);
+	} else {
+		at91_init_sram(0, AT91SAM9260_SRAM0_BASE, AT91SAM9260_SRAM0_SIZE);
+		at91_init_sram(1, AT91SAM9260_SRAM1_BASE, AT91SAM9260_SRAM1_SIZE);
+	}
 }
 
-void __init at91sam9260_initialize(unsigned long main_clock)
+static void __init at91sam9260_initialize(void)
 {
 	at91_arch_reset = at91sam9_alt_reset;
 	pm_power_off = at91sam9260_poweroff;
 	at91_extern_irq = (1 << AT91SAM9260_ID_IRQ0) | (1 << AT91SAM9260_ID_IRQ1)
 			| (1 << AT91SAM9260_ID_IRQ2);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91sam9260_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91sam9260_gpio, 3);
 }
@@ -421,14 +368,9 @@
 	0,	/* Advanced Interrupt Controller */
 };
 
-void __init at91sam9260_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91sam9260_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91sam9260_soc = {
+	.map_io = at91sam9260_map_io,
+	.default_irq_priority = at91sam9260_default_irq_priority,
+	.register_clocks = at91sam9260_register_clocks,
+	.init = at91sam9260_initialize,
+};
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index c148316..d522b47 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -22,36 +22,10 @@
 #include <mach/at91_rstc.h>
 #include <mach/at91_shdwc.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91sam9261_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	},
-};
-
-static struct map_desc at91sam9261_sram_desc[] __initdata = {
-	{
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9261_SRAM_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9261_SRAM_BASE),
-		.length		= AT91SAM9261_SRAM_SIZE,
-		.type		= MT_DEVICE,
-	},
-};
-
-static struct map_desc at91sam9g10_sram_desc[] __initdata = {
-	{
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9G10_SRAM_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9G10_SRAM_BASE),
-		.length		= AT91SAM9G10_SRAM_SIZE,
-		.type		= MT_DEVICE,
-	},
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -302,30 +276,21 @@
  *  AT91SAM9261 processor initialization
  * -------------------------------------------------------------------- */
 
-void __init at91sam9261_map_io(void)
+static void __init at91sam9261_map_io(void)
 {
-	/* Map peripherals */
-	iotable_init(at91sam9261_io_desc, ARRAY_SIZE(at91sam9261_io_desc));
-
 	if (cpu_is_at91sam9g10())
-		iotable_init(at91sam9g10_sram_desc, ARRAY_SIZE(at91sam9g10_sram_desc));
+		at91_init_sram(0, AT91SAM9G10_SRAM_BASE, AT91SAM9G10_SRAM_SIZE);
 	else
-		iotable_init(at91sam9261_sram_desc, ARRAY_SIZE(at91sam9261_sram_desc));
+		at91_init_sram(0, AT91SAM9261_SRAM_BASE, AT91SAM9261_SRAM_SIZE);
 }
 
-void __init at91sam9261_initialize(unsigned long main_clock)
+static void __init at91sam9261_initialize(void)
 {
 	at91_arch_reset = at91sam9_alt_reset;
 	pm_power_off = at91sam9261_poweroff;
 	at91_extern_irq = (1 << AT91SAM9261_ID_IRQ0) | (1 << AT91SAM9261_ID_IRQ1)
 			| (1 << AT91SAM9261_ID_IRQ2);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91sam9261_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91sam9261_gpio, 3);
 }
@@ -372,14 +337,9 @@
 	0,	/* Advanced Interrupt Controller */
 };
 
-void __init at91sam9261_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91sam9261_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91sam9261_soc = {
+	.map_io = at91sam9261_map_io,
+	.default_irq_priority = at91sam9261_default_irq_priority,
+	.register_clocks = at91sam9261_register_clocks,
+	.init = at91sam9261_initialize,
+};
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index dc28477..044f3c9 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -21,28 +21,10 @@
 #include <mach/at91_rstc.h>
 #include <mach/at91_shdwc.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91sam9263_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9263_SRAM0_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9263_SRAM0_BASE),
-		.length		= AT91SAM9263_SRAM0_SIZE,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9263_SRAM0_SIZE - AT91SAM9263_SRAM1_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9263_SRAM1_BASE),
-		.length		= AT91SAM9263_SRAM1_SIZE,
-		.type		= MT_DEVICE,
-	},
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -313,24 +295,18 @@
  *  AT91SAM9263 processor initialization
  * -------------------------------------------------------------------- */
 
-void __init at91sam9263_map_io(void)
+static void __init at91sam9263_map_io(void)
 {
-	/* Map peripherals */
-	iotable_init(at91sam9263_io_desc, ARRAY_SIZE(at91sam9263_io_desc));
+	at91_init_sram(0, AT91SAM9263_SRAM0_BASE, AT91SAM9263_SRAM0_SIZE);
+	at91_init_sram(1, AT91SAM9263_SRAM1_BASE, AT91SAM9263_SRAM1_SIZE);
 }
 
-void __init at91sam9263_initialize(unsigned long main_clock)
+static void __init at91sam9263_initialize(void)
 {
 	at91_arch_reset = at91sam9_alt_reset;
 	pm_power_off = at91sam9263_poweroff;
 	at91_extern_irq = (1 << AT91SAM9263_ID_IRQ0) | (1 << AT91SAM9263_ID_IRQ1);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91sam9263_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91sam9263_gpio, 5);
 }
@@ -377,14 +353,9 @@
 	0,	/* Advanced Interrupt Controller (IRQ1) */
 };
 
-void __init at91sam9263_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91sam9263_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91sam9263_soc = {
+	.map_io = at91sam9263_map_io,
+	.default_irq_priority = at91sam9263_default_irq_priority,
+	.register_clocks = at91sam9263_register_clocks,
+	.init = at91sam9263_initialize,
+};
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index 11e2141..e04c5fb 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -22,23 +22,10 @@
 #include <mach/at91_shdwc.h>
 #include <mach/cpu.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91sam9g45_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	}, {
-		.virtual	= AT91_IO_VIRT_BASE - AT91SAM9G45_SRAM_SIZE,
-		.pfn		= __phys_to_pfn(AT91SAM9G45_SRAM_BASE),
-		.length		= AT91SAM9G45_SRAM_SIZE,
-		.type		= MT_DEVICE,
-	}
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -329,24 +316,17 @@
  *  AT91SAM9G45 processor initialization
  * -------------------------------------------------------------------- */
 
-void __init at91sam9g45_map_io(void)
+static void __init at91sam9g45_map_io(void)
 {
-	/* Map peripherals */
-	iotable_init(at91sam9g45_io_desc, ARRAY_SIZE(at91sam9g45_io_desc));
+	at91_init_sram(0, AT91SAM9G45_SRAM_BASE, AT91SAM9G45_SRAM_SIZE);
 }
 
-void __init at91sam9g45_initialize(unsigned long main_clock)
+static void __init at91sam9g45_initialize(void)
 {
 	at91_arch_reset = at91sam9g45_reset;
 	pm_power_off = at91sam9g45_poweroff;
 	at91_extern_irq = (1 << AT91SAM9G45_ID_IRQ0);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91sam9g45_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91sam9g45_gpio, 5);
 }
@@ -393,14 +373,9 @@
 	0,	/* Advanced Interrupt Controller (IRQ0) */
 };
 
-void __init at91sam9g45_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91sam9g45_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91sam9g45_soc = {
+	.map_io = at91sam9g45_map_io,
+	.default_irq_priority = at91sam9g45_default_irq_priority,
+	.register_clocks = at91sam9g45_register_clocks,
+	.init = at91sam9g45_initialize,
+};
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index 29dff18..a238105 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -16,30 +16,16 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/cpu.h>
+#include <mach/at91_dbgu.h>
 #include <mach/at91sam9rl.h>
 #include <mach/at91_pmc.h>
 #include <mach/at91_rstc.h>
 #include <mach/at91_shdwc.h>
 
+#include "soc.h"
 #include "generic.h"
 #include "clock.h"
 
-static struct map_desc at91sam9rl_io_desc[] __initdata = {
-	{
-		.virtual	= AT91_VA_BASE_SYS,
-		.pfn		= __phys_to_pfn(AT91_BASE_SYS),
-		.length		= SZ_16K,
-		.type		= MT_DEVICE,
-	},
-};
-
-static struct map_desc at91sam9rl_sram_desc[] __initdata = {
-	{
-		.pfn		= __phys_to_pfn(AT91SAM9RL_SRAM_BASE),
-		.type		= MT_DEVICE,
-	}
-};
-
 /* --------------------------------------------------------------------
  *  Clocks
  * -------------------------------------------------------------------- */
@@ -287,16 +273,11 @@
  *  AT91SAM9RL processor initialization
  * -------------------------------------------------------------------- */
 
-void __init at91sam9rl_map_io(void)
+static void __init at91sam9rl_map_io(void)
 {
-	unsigned long cidr, sram_size;
+	unsigned long sram_size;
 
-	/* Map peripherals */
-	iotable_init(at91sam9rl_io_desc, ARRAY_SIZE(at91sam9rl_io_desc));
-
-	cidr = at91_sys_read(AT91_DBGU_CIDR);
-
-	switch (cidr & AT91_CIDR_SRAMSIZ) {
+	switch (at91_soc_initdata.cidr & AT91_CIDR_SRAMSIZ) {
 		case AT91_CIDR_SRAMSIZ_32K:
 			sram_size = 2 * SZ_16K;
 			break;
@@ -305,25 +286,16 @@
 			sram_size = SZ_16K;
 	}
 
-	at91sam9rl_sram_desc->virtual = AT91_IO_VIRT_BASE - sram_size;
-	at91sam9rl_sram_desc->length = sram_size;
-
 	/* Map SRAM */
-	iotable_init(at91sam9rl_sram_desc, ARRAY_SIZE(at91sam9rl_sram_desc));
+	at91_init_sram(0, AT91SAM9RL_SRAM_BASE, sram_size);
 }
 
-void __init at91sam9rl_initialize(unsigned long main_clock)
+static void __init at91sam9rl_initialize(void)
 {
 	at91_arch_reset = at91sam9_alt_reset;
 	pm_power_off = at91sam9rl_poweroff;
 	at91_extern_irq = (1 << AT91SAM9RL_ID_IRQ0);
 
-	/* Init clock subsystem */
-	at91_clock_init(main_clock);
-
-	/* Register the processor-specific clocks */
-	at91sam9rl_register_clocks();
-
 	/* Register GPIO subsystem */
 	at91_gpio_init(at91sam9rl_gpio, 4);
 }
@@ -370,14 +342,9 @@
 	0,	/* Advanced Interrupt Controller */
 };
 
-void __init at91sam9rl_init_interrupts(unsigned int priority[NR_AIC_IRQS])
-{
-	if (!priority)
-		priority = at91sam9rl_default_irq_priority;
-
-	/* Initialize the AIC interrupt controller */
-	at91_aic_init(priority);
-
-	/* Enable GPIO interrupts */
-	at91_gpio_irq_setup();
-}
+struct at91_init_soc __initdata at91sam9rl_soc = {
+	.map_io = at91sam9rl_map_io,
+	.default_irq_priority = at91sam9rl_default_irq_priority,
+	.register_clocks = at91sam9rl_register_clocks,
+	.init = at91sam9rl_initialize,
+};
diff --git a/arch/arm/mach-at91/board-1arm.c b/arch/arm/mach-at91/board-1arm.c
index ab1d463..5aa5885 100644
--- a/arch/arm/mach-at91/board-1arm.c
+++ b/arch/arm/mach-at91/board-1arm.c
@@ -46,7 +46,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -63,11 +63,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init onearm_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata onearm_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -97,8 +92,8 @@
 MACHINE_START(ONEARM, "Ajeco 1ARM single board computer")
 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= onearm_init_early,
-	.init_irq	= onearm_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= onearm_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-afeb-9260v1.c b/arch/arm/mach-at91/board-afeb-9260v1.c
index a4924de..b0c796d 100644
--- a/arch/arm/mach-at91/board-afeb-9260v1.c
+++ b/arch/arm/mach-at91/board-afeb-9260v1.c
@@ -51,7 +51,7 @@
 static void __init afeb9260_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -70,12 +70,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init afeb9260_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -219,9 +213,9 @@
 MACHINE_START(AFEB9260, "Custom afeb9260 board")
 	/* Maintainer: Sergey Lapin <slapin@ossfans.org> */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= afeb9260_init_early,
-	.init_irq	= afeb9260_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= afeb9260_board_init,
 MACHINE_END
 
diff --git a/arch/arm/mach-at91/board-cam60.c b/arch/arm/mach-at91/board-cam60.c
index 148fccb..d1abd58 100644
--- a/arch/arm/mach-at91/board-cam60.c
+++ b/arch/arm/mach-at91/board-cam60.c
@@ -48,7 +48,7 @@
 static void __init cam60_init_early(void)
 {
 	/* Initialize processor: 10 MHz crystal */
-	at91sam9260_initialize(10000000);
+	at91_initialize(10000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -57,12 +57,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init cam60_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host
  */
@@ -199,8 +193,8 @@
 MACHINE_START(CAM60, "KwikByte CAM60")
 	/* Maintainer: KwikByte */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= cam60_init_early,
-	.init_irq	= cam60_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= cam60_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-cap9adk.c b/arch/arm/mach-at91/board-cap9adk.c
index cdb65d4..679b0b7 100644
--- a/arch/arm/mach-at91/board-cap9adk.c
+++ b/arch/arm/mach-at91/board-cap9adk.c
@@ -53,7 +53,7 @@
 static void __init cap9adk_init_early(void)
 {
 	/* Initialize processor: 12 MHz crystal */
-	at91cap9_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* Setup the LEDs: USER1 and USER2 LED for cpu/timer... */
 	at91_init_leds(AT91_PIN_PA10, AT91_PIN_PA11);
@@ -65,12 +65,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init cap9adk_init_irq(void)
-{
-	at91cap9_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -397,8 +391,8 @@
 MACHINE_START(AT91CAP9ADK, "Atmel AT91CAP9A-DK")
 	/* Maintainer: Stelian Pop <stelian.pop@leadtechdesign.com> */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91cap9_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= cap9adk_init_early,
-	.init_irq	= cap9adk_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= cap9adk_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-carmeva.c b/arch/arm/mach-at91/board-carmeva.c
index f36b186..c578c5d 100644
--- a/arch/arm/mach-at91/board-carmeva.c
+++ b/arch/arm/mach-at91/board-carmeva.c
@@ -43,7 +43,7 @@
 static void __init carmeva_init_early(void)
 {
 	/* Initialize processor: 20.000 MHz crystal */
-	at91rm9200_initialize(20000000);
+	at91_initialize(20000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -57,11 +57,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init carmeva_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata carmeva_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -163,8 +158,8 @@
 MACHINE_START(CARMEVA, "Carmeva")
 	/* Maintainer: Conitec Datasystems */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= carmeva_init_early,
-	.init_irq	= carmeva_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= carmeva_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-cpu9krea.c b/arch/arm/mach-at91/board-cpu9krea.c
index 9805110..f4da8a1 100644
--- a/arch/arm/mach-at91/board-cpu9krea.c
+++ b/arch/arm/mach-at91/board-cpu9krea.c
@@ -50,7 +50,7 @@
 static void __init cpu9krea_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DGBU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -81,11 +81,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init cpu9krea_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
 /*
  * USB Host port
  */
@@ -376,8 +371,8 @@
 #endif
 	/* Maintainer: Eric Benard - EUKREA Electromatique */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= cpu9krea_init_early,
-	.init_irq	= cpu9krea_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= cpu9krea_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-cpuat91.c b/arch/arm/mach-at91/board-cpuat91.c
index 6daabe3..2d919f5 100644
--- a/arch/arm/mach-at91/board-cpuat91.c
+++ b/arch/arm/mach-at91/board-cpuat91.c
@@ -57,7 +57,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -82,11 +82,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init cpuat91_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata cpuat91_eth_data = {
 	.is_rmii	= 1,
 };
@@ -180,8 +175,8 @@
 MACHINE_START(CPUAT91, "Eukrea")
 	/* Maintainer: Eric Benard - EUKREA Electromatique */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= cpuat91_init_early,
-	.init_irq	= cpuat91_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= cpuat91_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-csb337.c b/arch/arm/mach-at91/board-csb337.c
index d98bcec..17654d5 100644
--- a/arch/arm/mach-at91/board-csb337.c
+++ b/arch/arm/mach-at91/board-csb337.c
@@ -46,7 +46,7 @@
 static void __init csb337_init_early(void)
 {
 	/* Initialize processor: 3.6864 MHz crystal */
-	at91rm9200_initialize(3686400);
+	at91_initialize(3686400);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PB0, AT91_PIN_PB1);
@@ -58,11 +58,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init csb337_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata csb337_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC2,
 	.is_rmii	= 0,
@@ -258,8 +253,8 @@
 MACHINE_START(CSB337, "Cogent CSB337")
 	/* Maintainer: Bill Gatliff */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= csb337_init_early,
-	.init_irq	= csb337_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= csb337_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-csb637.c b/arch/arm/mach-at91/board-csb637.c
index 019aab4..72b5567 100644
--- a/arch/arm/mach-at91/board-csb637.c
+++ b/arch/arm/mach-at91/board-csb637.c
@@ -43,7 +43,7 @@
 static void __init csb637_init_early(void)
 {
 	/* Initialize processor: 3.6864 MHz crystal */
-	at91rm9200_initialize(3686400);
+	at91_initialize(3686400);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -52,11 +52,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init csb637_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata csb637_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC0,
 	.is_rmii	= 0,
@@ -139,8 +134,8 @@
 MACHINE_START(CSB637, "Cogent CSB637")
 	/* Maintainer: Bill Gatliff */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= csb637_init_early,
-	.init_irq	= csb637_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= csb637_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-eb9200.c b/arch/arm/mach-at91/board-eb9200.c
index e948453..01170a2 100644
--- a/arch/arm/mach-at91/board-eb9200.c
+++ b/arch/arm/mach-at91/board-eb9200.c
@@ -43,7 +43,7 @@
 static void __init eb9200_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -60,11 +60,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init eb9200_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata eb9200_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -121,8 +116,8 @@
 
 MACHINE_START(ATEB9200, "Embest ATEB9200")
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= eb9200_init_early,
-	.init_irq	= eb9200_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= eb9200_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-ecbat91.c b/arch/arm/mach-at91/board-ecbat91.c
index a6f57fa..7c0313c 100644
--- a/arch/arm/mach-at91/board-ecbat91.c
+++ b/arch/arm/mach-at91/board-ecbat91.c
@@ -49,7 +49,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PC7, AT91_PIN_PC7);
@@ -64,11 +64,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ecb_at91init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata ecb_at91eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 0,
@@ -173,8 +168,8 @@
 MACHINE_START(ECBAT91, "emQbit's ECB_AT91")
 	/* Maintainer: emQbit.com */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ecb_at91init_early,
-	.init_irq	= ecb_at91init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ecb_at91board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-eco920.c b/arch/arm/mach-at91/board-eco920.c
index bfc0062..8252c72 100644
--- a/arch/arm/mach-at91/board-eco920.c
+++ b/arch/arm/mach-at91/board-eco920.c
@@ -35,7 +35,7 @@
 	/* Set cpu type: PQFP */
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PB0, AT91_PIN_PB1);
@@ -47,11 +47,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init eco920_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata eco920_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC2,
 	.is_rmii	= 1,
@@ -135,8 +130,8 @@
 MACHINE_START(ECO920, "eco920")
 	/* Maintainer: Sascha Hauer */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= eco920_init_early,
-	.init_irq	= eco920_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= eco920_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-flexibity.c b/arch/arm/mach-at91/board-flexibity.c
index 466c063..4c3f65d 100644
--- a/arch/arm/mach-at91/board-flexibity.c
+++ b/arch/arm/mach-at91/board-flexibity.c
@@ -40,7 +40,7 @@
 static void __init flexibity_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -49,11 +49,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init flexibity_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
 /* USB Host port */
 static struct at91_usbh_data __initdata flexibity_usbh_data = {
 	.ports		= 2,
@@ -155,8 +150,8 @@
 MACHINE_START(FLEXIBITY, "Flexibity Connect")
 	/* Maintainer: Maxim Osipov */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= flexibity_init_early,
-	.init_irq	= flexibity_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= flexibity_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-foxg20.c b/arch/arm/mach-at91/board-foxg20.c
index e2d1dc9..f27d1a7 100644
--- a/arch/arm/mach-at91/board-foxg20.c
+++ b/arch/arm/mach-at91/board-foxg20.c
@@ -60,7 +60,7 @@
 static void __init foxg20_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -101,12 +101,6 @@
 
 }
 
-static void __init foxg20_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -267,8 +261,8 @@
 MACHINE_START(ACMENETUSFOXG20, "Acme Systems srl FOX Board G20")
 	/* Maintainer: Sergio Tanzilli */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= foxg20_init_early,
-	.init_irq	= foxg20_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= foxg20_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-gsia18s.c b/arch/arm/mach-at91/board-gsia18s.c
index 1d4f36b..2e95949 100644
--- a/arch/arm/mach-at91/board-gsia18s.c
+++ b/arch/arm/mach-at91/board-gsia18s.c
@@ -75,11 +75,6 @@
 	at91_register_uart(AT91SAM9260_ID_US4, 5, 0);
 }
 
-static void __init init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
 /*
  * Two USB Host ports
  */
@@ -577,8 +572,8 @@
 
 MACHINE_START(GSIA18S, "GS_IA18_S")
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= gsia18s_init_early,
-	.init_irq	= init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= gsia18s_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-kafa.c b/arch/arm/mach-at91/board-kafa.c
index 9b003ff..4a17089 100644
--- a/arch/arm/mach-at91/board-kafa.c
+++ b/arch/arm/mach-at91/board-kafa.c
@@ -46,7 +46,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Set up the LEDs */
 	at91_init_leds(AT91_PIN_PB4, AT91_PIN_PB4);
@@ -61,11 +61,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init kafa_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata kafa_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 0,
@@ -99,8 +94,8 @@
 MACHINE_START(KAFA, "Sperry-Sun KAFA")
 	/* Maintainer: Sergei Sharonov */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= kafa_init_early,
-	.init_irq	= kafa_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= kafa_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-kb9202.c b/arch/arm/mach-at91/board-kb9202.c
index a813a74..9dc8d49 100644
--- a/arch/arm/mach-at91/board-kb9202.c
+++ b/arch/arm/mach-at91/board-kb9202.c
@@ -48,7 +48,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 10 MHz crystal */
-	at91rm9200_initialize(10000000);
+	at91_initialize(10000000);
 
 	/* Set up the LEDs */
 	at91_init_leds(AT91_PIN_PC19, AT91_PIN_PC18);
@@ -69,11 +69,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init kb9202_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata kb9202_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PB29,
 	.is_rmii	= 0,
@@ -140,8 +135,8 @@
 MACHINE_START(KB9200, "KB920x")
 	/* Maintainer: KwikByte, Inc. */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= kb9202_init_early,
-	.init_irq	= kb9202_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= kb9202_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-neocore926.c b/arch/arm/mach-at91/board-neocore926.c
index 961e805..9bc6ab3 100644
--- a/arch/arm/mach-at91/board-neocore926.c
+++ b/arch/arm/mach-at91/board-neocore926.c
@@ -54,7 +54,7 @@
 static void __init neocore926_init_early(void)
 {
 	/* Initialize processor: 20 MHz crystal */
-	at91sam9263_initialize(20000000);
+	at91_initialize(20000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -66,12 +66,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init neocore926_init_irq(void)
-{
-	at91sam9263_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -388,8 +382,8 @@
 MACHINE_START(NEOCORE926, "ADENEO NEOCORE 926")
 	/* Maintainer: ADENEO */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9263_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= neocore926_init_early,
-	.init_irq	= neocore926_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= neocore926_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-pcontrol-g20.c b/arch/arm/mach-at91/board-pcontrol-g20.c
index 21a21af..49e3f69 100644
--- a/arch/arm/mach-at91/board-pcontrol-g20.c
+++ b/arch/arm/mach-at91/board-pcontrol-g20.c
@@ -53,13 +53,6 @@
 	at91_register_uart(AT91SAM9260_ID_US4, 3, 0);
 }
 
-
-static void __init init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 static struct sam9_smc_config __initdata pcontrol_smc_config[2] = { {
 	.ncs_read_setup		= 16,
 	.nrd_setup		= 18,
@@ -223,8 +216,8 @@
 MACHINE_START(PCONTROL_G20, "PControl G20")
 	/* Maintainer: pgsellmann@portner-elektronik.at */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= pcontrol_g20_init_early,
-	.init_irq	= init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= pcontrol_g20_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-picotux200.c b/arch/arm/mach-at91/board-picotux200.c
index 756cc2a..b7b8390 100644
--- a/arch/arm/mach-at91/board-picotux200.c
+++ b/arch/arm/mach-at91/board-picotux200.c
@@ -46,7 +46,7 @@
 static void __init picotux200_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -60,11 +60,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init picotux200_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata picotux200_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -124,8 +119,8 @@
 MACHINE_START(PICOTUX2XX, "picotux 200")
 	/* Maintainer: Kleinhenz Elektronik GmbH */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= picotux200_init_early,
-	.init_irq	= picotux200_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= picotux200_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-qil-a9260.c b/arch/arm/mach-at91/board-qil-a9260.c
index d1a6001..81f9110 100644
--- a/arch/arm/mach-at91/board-qil-a9260.c
+++ b/arch/arm/mach-at91/board-qil-a9260.c
@@ -51,7 +51,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 12.000 MHz crystal */
-	at91sam9260_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -72,12 +72,6 @@
 
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -269,8 +263,8 @@
 MACHINE_START(QIL_A9260, "CALAO QIL_A9260")
 	/* Maintainer: calao-systems */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-rm9200dk.c b/arch/arm/mach-at91/board-rm9200dk.c
index aef9627..6f08faa 100644
--- a/arch/arm/mach-at91/board-rm9200dk.c
+++ b/arch/arm/mach-at91/board-rm9200dk.c
@@ -48,7 +48,7 @@
 static void __init dk_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PB2, AT91_PIN_PB2);
@@ -65,11 +65,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init dk_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata dk_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -228,8 +223,8 @@
 MACHINE_START(AT91RM9200DK, "Atmel AT91RM9200-DK")
 	/* Maintainer: SAN People/Atmel */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= dk_init_early,
-	.init_irq	= dk_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= dk_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-rm9200ek.c b/arch/arm/mach-at91/board-rm9200ek.c
index 015a021..85bcccd 100644
--- a/arch/arm/mach-at91/board-rm9200ek.c
+++ b/arch/arm/mach-at91/board-rm9200ek.c
@@ -48,7 +48,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PB1, AT91_PIN_PB2);
@@ -65,11 +65,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
 static struct at91_eth_data __initdata ek_eth_data = {
 	.phy_irq_pin	= AT91_PIN_PC4,
 	.is_rmii	= 1,
@@ -194,8 +189,8 @@
 MACHINE_START(AT91RM9200EK, "Atmel AT91RM9200-EK")
 	/* Maintainer: SAN People/Atmel */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9-l9260.c b/arch/arm/mach-at91/board-sam9-l9260.c
index aaf1bf0..4d3a02f 100644
--- a/arch/arm/mach-at91/board-sam9-l9260.c
+++ b/arch/arm/mach-at91/board-sam9-l9260.c
@@ -47,7 +47,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PA9, AT91_PIN_PA6);
@@ -67,12 +67,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -213,8 +207,8 @@
 MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260")
 	/* Maintainer: Olimex */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c
index 5c24074..8a50c3e 100644
--- a/arch/arm/mach-at91/board-sam9260ek.c
+++ b/arch/arm/mach-at91/board-sam9260ek.c
@@ -53,7 +53,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -70,12 +70,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -354,8 +348,8 @@
 MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c
index b60c22b..5096a0e 100644
--- a/arch/arm/mach-at91/board-sam9261ek.c
+++ b/arch/arm/mach-at91/board-sam9261ek.c
@@ -57,7 +57,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9261_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs */
 	at91_init_leds(AT91_PIN_PA13, AT91_PIN_PA14);
@@ -69,12 +69,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9261_init_interrupts(NULL);
-}
-
-
 /*
  * DM9000 ethernet device
  */
@@ -621,8 +615,8 @@
 #endif
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9261_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c
index 9bbdc92..ea8f185 100644
--- a/arch/arm/mach-at91/board-sam9263ek.c
+++ b/arch/arm/mach-at91/board-sam9263ek.c
@@ -56,7 +56,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 16.367 MHz crystal */
-	at91sam9263_initialize(16367660);
+	at91_initialize(16367660);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -68,12 +68,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9263_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -452,8 +446,8 @@
 MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9263_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 1325a50..817f59d 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -64,7 +64,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -81,12 +81,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -404,17 +398,17 @@
 MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
 
 MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
index 33eaa13..ad234cc 100644
--- a/arch/arm/mach-at91/board-sam9m10g45ek.c
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -50,7 +50,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 12.000 MHz crystal */
-	at91sam9g45_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* DGBU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -63,12 +63,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9g45_init_interrupts(NULL);
-}
-
-
 /*
  * USB HS Host port (common to OHCI & EHCI)
  */
@@ -422,8 +416,8 @@
 MACHINE_START(AT91SAM9M10G45EK, "Atmel AT91SAM9M10G45-EK")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9g45_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c
index effb399..4f14b54 100644
--- a/arch/arm/mach-at91/board-sam9rlek.c
+++ b/arch/arm/mach-at91/board-sam9rlek.c
@@ -41,7 +41,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 12.000 MHz crystal */
-	at91sam9rl_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -53,12 +53,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9rl_init_interrupts(NULL);
-}
-
-
 /*
  * USB HS Device port
  */
@@ -330,8 +324,8 @@
 MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK")
 	/* Maintainer: Atmel */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9rl_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-snapper9260.c b/arch/arm/mach-at91/board-snapper9260.c
index 6010ce1..c73d25e 100644
--- a/arch/arm/mach-at91/board-snapper9260.c
+++ b/arch/arm/mach-at91/board-snapper9260.c
@@ -42,7 +42,7 @@
 
 static void __init snapper9260_init_early(void)
 {
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Debug on ttyS0 */
 	at91_register_uart(0, 0, 0);
@@ -55,11 +55,6 @@
 	at91_register_uart(AT91SAM9260_ID_US2, 3, 0);
 }
 
-static void __init snapper9260_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
 static struct at91_usbh_data __initdata snapper9260_usbh_data = {
 	.ports		= 2,
 };
@@ -179,9 +174,9 @@
 
 MACHINE_START(SNAPPER_9260, "Bluewater Systems Snapper 9260/9G20 module")
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= snapper9260_init_early,
-	.init_irq	= snapper9260_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= snapper9260_board_init,
 MACHINE_END
 
diff --git a/arch/arm/mach-at91/board-stamp9g20.c b/arch/arm/mach-at91/board-stamp9g20.c
index 5e5c856..936e5fd 100644
--- a/arch/arm/mach-at91/board-stamp9g20.c
+++ b/arch/arm/mach-at91/board-stamp9g20.c
@@ -35,7 +35,7 @@
 void __init stamp9g20_init_early(void)
 {
 	/* Initialize processor: 18.432 MHz crystal */
-	at91sam9260_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* DGBU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -76,12 +76,6 @@
 	at91_register_uart(AT91SAM9260_ID_US5, 6, 0);
 }
 
-static void __init init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * NAND flash
  */
@@ -299,17 +293,17 @@
 MACHINE_START(PORTUXG20, "taskit PortuxG20")
 	/* Maintainer: taskit GmbH */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= portuxg20_init_early,
-	.init_irq	= init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= portuxg20_board_init,
 MACHINE_END
 
 MACHINE_START(STAMP9G20, "taskit Stamp9G20")
 	/* Maintainer: taskit GmbH */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= stamp9g20evb_init_early,
-	.init_irq	= init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= stamp9g20evb_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-usb-a9260.c b/arch/arm/mach-at91/board-usb-a9260.c
index 0e784e6..8c4c1a0 100644
--- a/arch/arm/mach-at91/board-usb-a9260.c
+++ b/arch/arm/mach-at91/board-usb-a9260.c
@@ -51,7 +51,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 12.000 MHz crystal */
-	at91sam9260_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -60,12 +60,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9260_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -229,8 +223,8 @@
 MACHINE_START(USB_A9260, "CALAO USB_A9260")
 	/* Maintainer: calao-systems */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9260_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-usb-a9263.c b/arch/arm/mach-at91/board-usb-a9263.c
index cf626dd..25e7937 100644
--- a/arch/arm/mach-at91/board-usb-a9263.c
+++ b/arch/arm/mach-at91/board-usb-a9263.c
@@ -50,7 +50,7 @@
 static void __init ek_init_early(void)
 {
 	/* Initialize processor: 12.00 MHz crystal */
-	at91sam9263_initialize(12000000);
+	at91_initialize(12000000);
 
 	/* DBGU on ttyS0. (Rx & Tx only) */
 	at91_register_uart(0, 0, 0);
@@ -59,12 +59,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init ek_init_irq(void)
-{
-	at91sam9263_init_interrupts(NULL);
-}
-
-
 /*
  * USB Host port
  */
@@ -245,8 +239,8 @@
 MACHINE_START(USB_A9263, "CALAO USB_A9263")
 	/* Maintainer: calao-systems */
 	.timer		= &at91sam926x_timer,
-	.map_io		= at91sam9263_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= ek_init_early,
-	.init_irq	= ek_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= ek_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/board-yl-9200.c b/arch/arm/mach-at91/board-yl-9200.c
index c208cc3..95edcbd 100644
--- a/arch/arm/mach-at91/board-yl-9200.c
+++ b/arch/arm/mach-at91/board-yl-9200.c
@@ -56,7 +56,7 @@
 	at91rm9200_set_type(ARCH_REVISON_9200_PQFP);
 
 	/* Initialize processor: 18.432 MHz crystal */
-	at91rm9200_initialize(18432000);
+	at91_initialize(18432000);
 
 	/* Setup the LEDs D2=PB17 (timer), D3=PB16 (cpu) */
 	at91_init_leds(AT91_PIN_PB16, AT91_PIN_PB17);
@@ -79,12 +79,6 @@
 	at91_set_serial_console(0);
 }
 
-static void __init yl9200_init_irq(void)
-{
-	at91rm9200_init_interrupts(NULL);
-}
-
-
 /*
  * LEDs
  */
@@ -599,8 +593,8 @@
 MACHINE_START(YL9200, "uCdragon YL-9200")
 	/* Maintainer: S.Birtles */
 	.timer		= &at91rm9200_timer,
-	.map_io		= at91rm9200_map_io,
+	.map_io		= at91_map_io,
 	.init_early	= yl9200_init_early,
-	.init_irq	= yl9200_init_irq,
+	.init_irq	= at91_init_irq_default,
 	.init_machine	= yl9200_board_init,
 MACHINE_END
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index 8ff3418..938b34f 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -11,35 +11,19 @@
 #include <linux/clkdev.h>
 
  /* Map io */
-extern void __init at91rm9200_map_io(void);
-extern void __init at91sam9260_map_io(void);
-extern void __init at91sam9261_map_io(void);
-extern void __init at91sam9263_map_io(void);
-extern void __init at91sam9rl_map_io(void);
-extern void __init at91sam9g45_map_io(void);
-extern void __init at91x40_map_io(void);
-extern void __init at91cap9_map_io(void);
+extern void __init at91_map_io(void);
+extern void __init at91_init_sram(int bank, unsigned long base,
+				  unsigned int length);
 
  /* Processors */
 extern void __init at91rm9200_set_type(int type);
-extern void __init at91rm9200_initialize(unsigned long main_clock);
-extern void __init at91sam9260_initialize(unsigned long main_clock);
-extern void __init at91sam9261_initialize(unsigned long main_clock);
-extern void __init at91sam9263_initialize(unsigned long main_clock);
-extern void __init at91sam9rl_initialize(unsigned long main_clock);
-extern void __init at91sam9g45_initialize(unsigned long main_clock);
+extern void __init at91_initialize(unsigned long main_clock);
 extern void __init at91x40_initialize(unsigned long main_clock);
-extern void __init at91cap9_initialize(unsigned long main_clock);
 
  /* Interrupts */
-extern void __init at91rm9200_init_interrupts(unsigned int priority[]);
-extern void __init at91sam9260_init_interrupts(unsigned int priority[]);
-extern void __init at91sam9261_init_interrupts(unsigned int priority[]);
-extern void __init at91sam9263_init_interrupts(unsigned int priority[]);
-extern void __init at91sam9rl_init_interrupts(unsigned int priority[]);
-extern void __init at91sam9g45_init_interrupts(unsigned int priority[]);
+extern void __init at91_init_irq_default(void);
+extern void __init at91_init_interrupts(unsigned int priority[]);
 extern void __init at91x40_init_interrupts(unsigned int priority[]);
-extern void __init at91cap9_init_interrupts(unsigned int priority[]);
 extern void __init at91_aic_init(unsigned int priority[]);
 
  /* Timer */
@@ -49,7 +33,6 @@
 extern struct sys_timer at91x40_timer;
 
  /* Clocks */
-extern int __init at91_clock_init(unsigned long main_clock);
 /*
  * function to specify the clock of the default console. As we do not
  * use the device/driver bus, the dev_name is not intialize. So we need
@@ -62,6 +45,11 @@
 extern void __init at91sam9rl_set_console_clock(int id);
 extern void __init at91sam9g45_set_console_clock(int id);
 extern void __init at91cap9_set_console_clock(int id);
+#ifdef CONFIG_AT91_PMC_UNIT
+extern int __init at91_clock_init(unsigned long main_clock);
+#else
+static int inline at91_clock_init(unsigned long main_clock) { return 0; }
+#endif
 struct device;
 
  /* Power Management */
diff --git a/arch/arm/mach-at91/include/mach/at91_dbgu.h b/arch/arm/mach-at91/include/mach/at91_dbgu.h
index 6dcaa77..dbfe455a 100644
--- a/arch/arm/mach-at91/include/mach/at91_dbgu.h
+++ b/arch/arm/mach-at91/include/mach/at91_dbgu.h
@@ -16,22 +16,25 @@
 #ifndef AT91_DBGU_H
 #define AT91_DBGU_H
 
+#define dbgu_readl(dbgu, field) \
+	__raw_readl(AT91_VA_BASE_SYS + dbgu + AT91_DBGU_ ## field)
+
 #ifdef AT91_DBGU
-#define AT91_DBGU_CR		(AT91_DBGU + 0x00)	/* Control Register */
-#define AT91_DBGU_MR		(AT91_DBGU + 0x04)	/* Mode Register */
-#define AT91_DBGU_IER		(AT91_DBGU + 0x08)	/* Interrupt Enable Register */
+#define AT91_DBGU_CR		(0x00)	/* Control Register */
+#define AT91_DBGU_MR		(0x04)	/* Mode Register */
+#define AT91_DBGU_IER		(0x08)	/* Interrupt Enable Register */
 #define		AT91_DBGU_TXRDY		(1 << 1)		/* Transmitter Ready */
 #define		AT91_DBGU_TXEMPTY	(1 << 9)		/* Transmitter Empty */
-#define AT91_DBGU_IDR		(AT91_DBGU + 0x0c)	/* Interrupt Disable Register */
-#define AT91_DBGU_IMR		(AT91_DBGU + 0x10)	/* Interrupt Mask Register */
-#define AT91_DBGU_SR		(AT91_DBGU + 0x14)	/* Status Register */
-#define AT91_DBGU_RHR		(AT91_DBGU + 0x18)	/* Receiver Holding Register */
-#define AT91_DBGU_THR		(AT91_DBGU + 0x1c)	/* Transmitter Holding Register */
-#define AT91_DBGU_BRGR		(AT91_DBGU + 0x20)	/* Baud Rate Generator Register */
+#define AT91_DBGU_IDR		(0x0c)	/* Interrupt Disable Register */
+#define AT91_DBGU_IMR		(0x10)	/* Interrupt Mask Register */
+#define AT91_DBGU_SR		(0x14)	/* Status Register */
+#define AT91_DBGU_RHR		(0x18)	/* Receiver Holding Register */
+#define AT91_DBGU_THR		(0x1c)	/* Transmitter Holding Register */
+#define AT91_DBGU_BRGR		(0x20)	/* Baud Rate Generator Register */
 
-#define AT91_DBGU_CIDR		(AT91_DBGU + 0x40)	/* Chip ID Register */
-#define AT91_DBGU_EXID		(AT91_DBGU + 0x44)	/* Chip ID Extension Register */
-#define AT91_DBGU_FNR		(AT91_DBGU + 0x48)	/* Force NTRST Register [SAM9 only] */
+#define AT91_DBGU_CIDR		(0x40)	/* Chip ID Register */
+#define AT91_DBGU_EXID		(0x44)	/* Chip ID Extension Register */
+#define AT91_DBGU_FNR		(0x48)	/* Force NTRST Register [SAM9 only] */
 #define		AT91_DBGU_FNTRST	(1 << 0)		/* Force NTRST */
 
 #endif /* AT91_DBGU */
diff --git a/arch/arm/mach-at91/include/mach/at91cap9.h b/arch/arm/mach-at91/include/mach/at91cap9.h
index 6659938..c5df1e8 100644
--- a/arch/arm/mach-at91/include/mach/at91cap9.h
+++ b/arch/arm/mach-at91/include/mach/at91cap9.h
@@ -75,7 +75,6 @@
 #define AT91CAP9_BASE_EMAC		0xfffbc000
 #define AT91CAP9_BASE_ADC		0xfffc0000
 #define AT91CAP9_BASE_ISI		0xfffc4000
-#define AT91_BASE_SYS			0xffffe200
 
 /*
  * System Peripherals (offset from AT91_BASE_SYS)
diff --git a/arch/arm/mach-at91/include/mach/at91rm9200.h b/arch/arm/mach-at91/include/mach/at91rm9200.h
index 99e0f8d..e4037b5 100644
--- a/arch/arm/mach-at91/include/mach/at91rm9200.h
+++ b/arch/arm/mach-at91/include/mach/at91rm9200.h
@@ -74,7 +74,6 @@
 #define AT91RM9200_BASE_SSC1	0xfffd4000
 #define AT91RM9200_BASE_SSC2	0xfffd8000
 #define AT91RM9200_BASE_SPI	0xfffe0000
-#define AT91_BASE_SYS		0xfffff000
 
 
 /*
diff --git a/arch/arm/mach-at91/include/mach/at91sam9260.h b/arch/arm/mach-at91/include/mach/at91sam9260.h
index 8b6bf83..9a79116 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9260.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9260.h
@@ -76,7 +76,6 @@
 #define AT91SAM9260_BASE_TC4		0xfffdc040
 #define AT91SAM9260_BASE_TC5		0xfffdc080
 #define AT91SAM9260_BASE_ADC		0xfffe0000
-#define AT91_BASE_SYS			0xffffe800
 
 /*
  * System Peripherals (offset from AT91_BASE_SYS)
diff --git a/arch/arm/mach-at91/include/mach/at91sam9261.h b/arch/arm/mach-at91/include/mach/at91sam9261.h
index eafbdda..ce59620 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9261.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9261.h
@@ -60,7 +60,6 @@
 #define AT91SAM9261_BASE_SSC2		0xfffc4000
 #define AT91SAM9261_BASE_SPI0		0xfffc8000
 #define AT91SAM9261_BASE_SPI1		0xfffcc000
-#define AT91_BASE_SYS			0xffffea00
 
 
 /*
diff --git a/arch/arm/mach-at91/include/mach/at91sam9263.h b/arch/arm/mach-at91/include/mach/at91sam9263.h
index e2d3482..f1b9296 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9263.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9263.h
@@ -70,7 +70,6 @@
 #define AT91SAM9263_BASE_EMAC		0xfffbc000
 #define AT91SAM9263_BASE_ISI		0xfffc4000
 #define AT91SAM9263_BASE_2DGE		0xfffc8000
-#define AT91_BASE_SYS			0xffffe000
 
 /*
  * System Peripherals (offset from AT91_BASE_SYS)
diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45.h b/arch/arm/mach-at91/include/mach/at91sam9g45.h
index 659304a..2c611b9 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9g45.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9g45.h
@@ -82,7 +82,6 @@
 #define AT91SAM9G45_BASE_TC3		0xfffd4000
 #define AT91SAM9G45_BASE_TC4		0xfffd4040
 #define AT91SAM9G45_BASE_TC5		0xfffd4080
-#define AT91_BASE_SYS			0xffffe200
 
 /*
  * System Peripherals (offset from AT91_BASE_SYS)
diff --git a/arch/arm/mach-at91/include/mach/at91sam9rl.h b/arch/arm/mach-at91/include/mach/at91sam9rl.h
index 41dbbe6..1aabacd 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9rl.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9rl.h
@@ -64,7 +64,6 @@
 #define AT91SAM9RL_BASE_TSC	0xfffd0000
 #define AT91SAM9RL_BASE_UDPHS	0xfffd4000
 #define AT91SAM9RL_BASE_AC97C	0xfffd8000
-#define AT91_BASE_SYS		0xffffc000
 
 
 /*
diff --git a/arch/arm/mach-at91/include/mach/clkdev.h b/arch/arm/mach-at91/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-at91/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-at91/include/mach/cpu.h b/arch/arm/mach-at91/include/mach/cpu.h
index df966c2..f6ce936 100644
--- a/arch/arm/mach-at91/include/mach/cpu.h
+++ b/arch/arm/mach-at91/include/mach/cpu.h
@@ -1,7 +1,8 @@
 /*
  * arch/arm/mach-at91/include/mach/cpu.h
  *
- *  Copyright (C) 2006 SAN People
+ * Copyright (C) 2006 SAN People
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.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
@@ -10,12 +11,8 @@
  *
  */
 
-#ifndef __ASM_ARCH_CPU_H
-#define __ASM_ARCH_CPU_H
-
-#include <mach/hardware.h>
-#include <mach/at91_dbgu.h>
-
+#ifndef __MACH_CPU_H__
+#define __MACH_CPU_H__
 
 #define ARCH_ID_AT91RM9200	0x09290780
 #define ARCH_ID_AT91SAM9260	0x019803a0
@@ -39,16 +36,6 @@
 #define ARCH_ID_AT91M40807	0x14080745
 #define ARCH_ID_AT91R40008	0x44000840
 
-static inline unsigned long at91_cpu_identify(void)
-{
-	return (at91_sys_read(AT91_DBGU_CIDR) & ~AT91_CIDR_VERSION);
-}
-
-static inline unsigned long at91_cpu_fully_identify(void)
-{
-	return at91_sys_read(AT91_DBGU_CIDR);
-}
-
 #define ARCH_EXID_AT91SAM9M11	0x00000001
 #define ARCH_EXID_AT91SAM9M10	0x00000002
 #define ARCH_EXID_AT91SAM9G46	0x00000003
@@ -60,40 +47,80 @@
 #define ARCH_EXID_AT91SAM9G25	0x00000003
 #define ARCH_EXID_AT91SAM9X25	0x00000004
 
-static inline unsigned long at91_exid_identify(void)
-{
-	return at91_sys_read(AT91_DBGU_EXID);
-}
-
-
 #define ARCH_FAMILY_AT91X92	0x09200000
 #define ARCH_FAMILY_AT91SAM9	0x01900000
 #define ARCH_FAMILY_AT91SAM9XE	0x02900000
 
-static inline unsigned long at91_arch_identify(void)
-{
-	return (at91_sys_read(AT91_DBGU_CIDR) & AT91_CIDR_ARCH);
-}
-
-#ifdef CONFIG_ARCH_AT91CAP9
-#include <mach/at91_pmc.h>
-
+/* PMC revision */
 #define ARCH_REVISION_CAP9_B	0x399
 #define ARCH_REVISION_CAP9_C	0x601
 
-static inline unsigned long at91cap9_rev_identify(void)
-{
-	return (at91_sys_read(AT91_PMC_VER));
-}
-#endif
-
-#ifdef CONFIG_ARCH_AT91RM9200
-extern int rm9200_type;
+/* RM9200 type */
 #define ARCH_REVISON_9200_BGA	(0 << 0)
 #define ARCH_REVISON_9200_PQFP	(1 << 0)
-#define cpu_is_at91rm9200()	(at91_cpu_identify() == ARCH_ID_AT91RM9200)
-#define cpu_is_at91rm9200_bga()	(!cpu_is_at91rm9200_pqfp())
-#define cpu_is_at91rm9200_pqfp() (cpu_is_at91rm9200() && rm9200_type & ARCH_REVISON_9200_PQFP)
+
+enum at91_soc_type {
+	/* 920T */
+	AT91_SOC_RM9200,
+
+	/* CAP */
+	AT91_SOC_CAP9,
+
+	/* SAM92xx */
+	AT91_SOC_SAM9260, AT91_SOC_SAM9261, AT91_SOC_SAM9263,
+
+	/* SAM9Gxx */
+	AT91_SOC_SAM9G10, AT91_SOC_SAM9G20, AT91_SOC_SAM9G45,
+
+	/* SAM9RL */
+	AT91_SOC_SAM9RL,
+
+	/* SAM9X5 */
+	AT91_SOC_SAM9X5,
+
+	/* Unknown type */
+	AT91_SOC_NONE
+};
+
+enum at91_soc_subtype {
+	/* RM9200 */
+	AT91_SOC_RM9200_BGA, AT91_SOC_RM9200_PQFP,
+
+	/* CAP9 */
+	AT91_SOC_CAP9_REV_B, AT91_SOC_CAP9_REV_C,
+
+	/* SAM9260 */
+	AT91_SOC_SAM9XE,
+
+	/* SAM9G45 */
+	AT91_SOC_SAM9G45ES, AT91_SOC_SAM9M10, AT91_SOC_SAM9G46, AT91_SOC_SAM9M11,
+
+	/* SAM9X5 */
+	AT91_SOC_SAM9G15, AT91_SOC_SAM9G35, AT91_SOC_SAM9X35,
+	AT91_SOC_SAM9G25, AT91_SOC_SAM9X25,
+
+	/* Unknown subtype */
+	AT91_SOC_SUBTYPE_NONE
+};
+
+struct at91_socinfo {
+	unsigned int type, subtype;
+	unsigned int cidr, exid;
+};
+
+extern struct at91_socinfo at91_soc_initdata;
+const char *at91_get_soc_type(struct at91_socinfo *c);
+const char *at91_get_soc_subtype(struct at91_socinfo *c);
+
+static inline int at91_soc_is_detected(void)
+{
+	return at91_soc_initdata.type != AT91_SOC_NONE;
+}
+
+#ifdef CONFIG_ARCH_AT91RM9200
+#define cpu_is_at91rm9200()	(at91_soc_initdata.type == AT91_SOC_RM9200)
+#define cpu_is_at91rm9200_bga()	(at91_soc_initdata.subtype == AT91_SOC_RM9200_BGA)
+#define cpu_is_at91rm9200_pqfp() (at91_soc_initdata.subtype == AT91_SOC_RM9200_PQFP)
 #else
 #define cpu_is_at91rm9200()	(0)
 #define cpu_is_at91rm9200_bga()	(0)
@@ -101,52 +128,49 @@
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9260
-#define cpu_is_at91sam9xe()	(at91_arch_identify() == ARCH_FAMILY_AT91SAM9XE)
-#define cpu_is_at91sam9260()	((at91_cpu_identify() == ARCH_ID_AT91SAM9260) || cpu_is_at91sam9xe())
+#define cpu_is_at91sam9xe()	(at91_soc_initdata.subtype == AT91_SOC_SAM9XE)
+#define cpu_is_at91sam9260()	(at91_soc_initdata.type == AT91_SOC_SAM9260)
 #else
 #define cpu_is_at91sam9xe()	(0)
 #define cpu_is_at91sam9260()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9G20
-#define cpu_is_at91sam9g20()	(at91_cpu_identify() == ARCH_ID_AT91SAM9G20)
+#define cpu_is_at91sam9g20()	(at91_soc_initdata.type == AT91_SOC_SAM9G20)
 #else
 #define cpu_is_at91sam9g20()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9261
-#define cpu_is_at91sam9261()	(at91_cpu_identify() == ARCH_ID_AT91SAM9261)
+#define cpu_is_at91sam9261()	(at91_soc_initdata.type == AT91_SOC_SAM9261)
 #else
 #define cpu_is_at91sam9261()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9G10
-#define cpu_is_at91sam9g10()	((at91_cpu_identify() & ~AT91_CIDR_EXT)	== ARCH_ID_AT91SAM9G10)
+#define cpu_is_at91sam9g10()	(at91_soc_initdata.type == AT91_SOC_SAM9G10)
 #else
 #define cpu_is_at91sam9g10()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9263
-#define cpu_is_at91sam9263()	(at91_cpu_identify() == ARCH_ID_AT91SAM9263)
+#define cpu_is_at91sam9263()	(at91_soc_initdata.type == AT91_SOC_SAM9263)
 #else
 #define cpu_is_at91sam9263()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9RL
-#define cpu_is_at91sam9rl()	(at91_cpu_identify() == ARCH_ID_AT91SAM9RL64)
+#define cpu_is_at91sam9rl()	(at91_soc_initdata.type == AT91_SOC_SAM9RL)
 #else
 #define cpu_is_at91sam9rl()	(0)
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9G45
-#define cpu_is_at91sam9g45()	(at91_cpu_identify() == ARCH_ID_AT91SAM9G45)
-#define cpu_is_at91sam9g45es()	(at91_cpu_fully_identify() == ARCH_ID_AT91SAM9G45ES)
-#define cpu_is_at91sam9m10()    (cpu_is_at91sam9g45() && \
-                                (at91_exid_identify() == ARCH_EXID_AT91SAM9M10))
-#define cpu_is_at91sam9m46()    (cpu_is_at91sam9g45() && \
-                                (at91_exid_identify() == ARCH_EXID_AT91SAM9G46))
-#define cpu_is_at91sam9m11()    (cpu_is_at91sam9g45() && \
-                                (at91_exid_identify() == ARCH_EXID_AT91SAM9M11))
+#define cpu_is_at91sam9g45()	(at91_soc_initdata.type == AT91_SOC_SAM9G45)
+#define cpu_is_at91sam9g45es()	(at91_soc_initdata.subtype == AT91_SOC_SAM9G45ES)
+#define cpu_is_at91sam9m10()	(at91_soc_initdata.subtype == AT91_SOC_SAM9M10)
+#define cpu_is_at91sam9g46()	(at91_soc_initdata.subtype == AT91_SOC_SAM9G46)
+#define cpu_is_at91sam9m11()	(at91_soc_initdata.subtype == AT91_SOC_SAM9M11)
 #else
 #define cpu_is_at91sam9g45()	(0)
 #define cpu_is_at91sam9g45es()	(0)
@@ -156,17 +180,12 @@
 #endif
 
 #ifdef CONFIG_ARCH_AT91SAM9X5
-#define cpu_is_at91sam9x5()	(at91_cpu_identify() == ARCH_ID_AT91SAM9X5)
-#define cpu_is_at91sam9g15()	(cpu_is_at91sam9x5() && \
-				(at91_exid_identify() == ARCH_EXID_AT91SAM9G15))
-#define cpu_is_at91sam9g35()	(cpu_is_at91sam9x5() && \
-				(at91_exid_identify() == ARCH_EXID_AT91SAM9G35))
-#define cpu_is_at91sam9x35()	(cpu_is_at91sam9x5() && \
-				(at91_exid_identify() == ARCH_EXID_AT91SAM9X35))
-#define cpu_is_at91sam9g25()	(cpu_is_at91sam9x5() && \
-				(at91_exid_identify() == ARCH_EXID_AT91SAM9G25))
-#define cpu_is_at91sam9x25()	(cpu_is_at91sam9x5() && \
-				(at91_exid_identify() == ARCH_EXID_AT91SAM9X25))
+#define cpu_is_at91sam9x5()	(at91_soc_initdata.type == AT91_SOC_SAM9X5)
+#define cpu_is_at91sam9g15()	(at91_soc_initdata.subtype == AT91_SOC_SAM9G15)
+#define cpu_is_at91sam9g35()	(at91_soc_initdata.subtype == AT91_SOC_SAM9G35)
+#define cpu_is_at91sam9x35()	(at91_soc_initdata.subtype == AT91_SOC_SAM9X35)
+#define cpu_is_at91sam9g25()	(at91_soc_initdata.subtype == AT91_SOC_SAM9G25)
+#define cpu_is_at91sam9x25()	(at91_soc_initdata.subtype == AT91_SOC_SAM9X25)
 #else
 #define cpu_is_at91sam9x5()	(0)
 #define cpu_is_at91sam9g15()	(0)
@@ -177,9 +196,9 @@
 #endif
 
 #ifdef CONFIG_ARCH_AT91CAP9
-#define cpu_is_at91cap9()	(at91_cpu_identify() == ARCH_ID_AT91CAP9)
-#define cpu_is_at91cap9_revB()	(at91cap9_rev_identify() == ARCH_REVISION_CAP9_B)
-#define cpu_is_at91cap9_revC()	(at91cap9_rev_identify() == ARCH_REVISION_CAP9_C)
+#define cpu_is_at91cap9()	(at91_soc_initdata.type == AT91_SOC_CAP9)
+#define cpu_is_at91cap9_revB()	(at91_soc_initdata.subtype == AT91_SOC_CAP9_REV_B)
+#define cpu_is_at91cap9_revC()	(at91_soc_initdata.subtype == AT91_SOC_CAP9_REV_C)
 #else
 #define cpu_is_at91cap9()	(0)
 #define cpu_is_at91cap9_revB()	(0)
@@ -192,4 +211,4 @@
  */
 #define cpu_is_at32ap7000()	(0)
 
-#endif
+#endif /* __MACH_CPU_H__ */
diff --git a/arch/arm/mach-at91/include/mach/debug-macro.S b/arch/arm/mach-at91/include/mach/debug-macro.S
index 0f959fa..bc1e0b2 100644
--- a/arch/arm/mach-at91/include/mach/debug-macro.S
+++ b/arch/arm/mach-at91/include/mach/debug-macro.S
@@ -15,23 +15,23 @@
 #include <mach/at91_dbgu.h>
 
 	.macro	addruart, rp, rv
-	ldr	\rp, =(AT91_BASE_SYS + AT91_DBGU)		@ System peripherals (phys address)
-	ldr	\rv, =(AT91_VA_BASE_SYS	+ AT91_DBGU)		@ System peripherals (virt address)
+	ldr	\rp, =(AT91_BASE_SYS + AT91_DBGU)	@ System peripherals (phys address)
+	ldr	\rv, =(AT91_VA_BASE_SYS	+ AT91_DBGU)	@ System peripherals (virt address)
 	.endm
 
 	.macro	senduart,rd,rx
-	strb	\rd, [\rx, #(AT91_DBGU_THR - AT91_DBGU)]	@ Write to Transmitter Holding Register
+	strb	\rd, [\rx, #(AT91_DBGU_THR)]		@ Write to Transmitter Holding Register
 	.endm
 
 	.macro	waituart,rd,rx
-1001:	ldr	\rd, [\rx, #(AT91_DBGU_SR - AT91_DBGU)]		@ Read Status Register
-	tst	\rd, #AT91_DBGU_TXRDY				@ DBGU_TXRDY = 1 when ready to transmit
+1001:	ldr	\rd, [\rx, #(AT91_DBGU_SR)]		@ Read Status Register
+	tst	\rd, #AT91_DBGU_TXRDY			@ DBGU_TXRDY = 1 when ready to transmit
 	beq	1001b
 	.endm
 
 	.macro	busyuart,rd,rx
-1001:	ldr	\rd, [\rx, #(AT91_DBGU_SR - AT91_DBGU)]		@ Read Status Register
-	tst	\rd, #AT91_DBGU_TXEMPTY				@ DBGU_TXEMPTY = 1 when transmission complete
+1001:	ldr	\rd, [\rx, #(AT91_DBGU_SR)]		@ Read Status Register
+	tst	\rd, #AT91_DBGU_TXEMPTY			@ DBGU_TXEMPTY = 1 when transmission complete
 	beq	1001b
 	.endm
 
diff --git a/arch/arm/mach-at91/include/mach/hardware.h b/arch/arm/mach-at91/include/mach/hardware.h
index 1008b9f..483478d 100644
--- a/arch/arm/mach-at91/include/mach/hardware.h
+++ b/arch/arm/mach-at91/include/mach/hardware.h
@@ -36,6 +36,20 @@
 #error "Unsupported AT91 processor"
 #endif
 
+#if !defined(CONFIG_ARCH_AT91X40)
+/*
+ * On all at91 except rm9200 and x40 have the System Controller starts
+ * at address 0xffffc000 and has a size of 16KiB.
+ *
+ * On rm9200 it's start at 0xfffe4000 of 111KiB with non reserved data starting
+ * at 0xfffff000
+ *
+ * Removes the individual definitions of AT91_BASE_SYS and
+ * replaces them with a common version at base 0xfffffc000 and size 16KiB
+ * and map the same memory space
+ */
+#define AT91_BASE_SYS	0xffffc000
+#endif
 
 /*
  * Peripheral identifiers/interrupts.
diff --git a/arch/arm/mach-at91/include/mach/io.h b/arch/arm/mach-at91/include/mach/io.h
index 0b0cccc..4298e78 100644
--- a/arch/arm/mach-at91/include/mach/io.h
+++ b/arch/arm/mach-at91/include/mach/io.h
@@ -21,14 +21,23 @@
 #ifndef __ASM_ARCH_IO_H
 #define __ASM_ARCH_IO_H
 
+#include <mach/hardware.h>
+
 #define IO_SPACE_LIMIT		0xFFFFFFFF
 
 #define __io(a)		__typesafe_io(a)
 #define __mem_pci(a)	(a)
 
-
 #ifndef __ASSEMBLY__
 
+#ifndef CONFIG_ARCH_AT91X40
+#define __arch_ioremap	at91_ioremap
+#define __arch_iounmap	at91_iounmap
+#endif
+
+void __iomem *at91_ioremap(unsigned long phys, size_t size, unsigned int type);
+void at91_iounmap(volatile void __iomem *addr);
+
 static inline unsigned int at91_sys_read(unsigned int reg_offset)
 {
 	void __iomem *addr = (void __iomem *)AT91_VA_BASE_SYS;
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index ea53f4d..4159eca 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -20,7 +20,7 @@
 #include <linux/io.h>
 
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
 
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
new file mode 100644
index 0000000..aa64294
--- /dev/null
+++ b/arch/arm/mach-at91/setup.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * Under GPLv2
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+#include <mach/cpu.h>
+#include <mach/at91_dbgu.h>
+#include <mach/at91_pmc.h>
+
+#include "soc.h"
+#include "generic.h"
+
+struct at91_init_soc __initdata at91_boot_soc;
+
+struct at91_socinfo at91_soc_initdata;
+EXPORT_SYMBOL(at91_soc_initdata);
+
+void __init at91rm9200_set_type(int type)
+{
+	if (type == ARCH_REVISON_9200_PQFP)
+		at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA;
+	else
+		at91_soc_initdata.subtype = AT91_SOC_RM9200_PQFP;
+}
+
+void __init at91_init_irq_default(void)
+{
+	at91_init_interrupts(at91_boot_soc.default_irq_priority);
+}
+
+void __init at91_init_interrupts(unsigned int *priority)
+{
+	/* Initialize the AIC interrupt controller */
+	at91_aic_init(priority);
+
+	/* Enable GPIO interrupts */
+	at91_gpio_irq_setup();
+}
+
+static struct map_desc sram_desc[2] __initdata;
+
+void __init at91_init_sram(int bank, unsigned long base, unsigned int length)
+{
+	struct map_desc *desc = &sram_desc[bank];
+
+	desc->virtual = AT91_IO_VIRT_BASE - length;
+	if (bank > 0)
+		desc->virtual -= sram_desc[bank - 1].length;
+
+	desc->pfn = __phys_to_pfn(base);
+	desc->length = length;
+	desc->type = MT_DEVICE;
+
+	pr_info("AT91: sram at 0x%lx of 0x%x mapped at 0x%lx\n",
+		base, length, desc->virtual);
+
+	iotable_init(desc, 1);
+}
+
+static struct map_desc at91_io_desc __initdata = {
+	.virtual	= AT91_VA_BASE_SYS,
+	.pfn		= __phys_to_pfn(AT91_BASE_SYS),
+	.length		= SZ_16K,
+	.type		= MT_DEVICE,
+};
+
+void __iomem *at91_ioremap(unsigned long p, size_t size, unsigned int type)
+{
+	if (p >= AT91_BASE_SYS && p <= (AT91_BASE_SYS + SZ_16K - 1))
+		return (void __iomem *)AT91_IO_P2V(p);
+
+	return __arm_ioremap_caller(p, size, type, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(at91_ioremap);
+
+void at91_iounmap(volatile void __iomem *addr)
+{
+	unsigned long virt = (unsigned long)addr;
+
+	if (virt >= VMALLOC_START && virt < VMALLOC_END)
+		__iounmap(addr);
+}
+EXPORT_SYMBOL(at91_iounmap);
+
+#define AT91_DBGU0	0xfffff200
+#define AT91_DBGU1	0xffffee00
+
+static void __init soc_detect(u32 dbgu_base)
+{
+	u32 cidr, socid;
+
+	cidr = __raw_readl(AT91_IO_P2V(dbgu_base) + AT91_DBGU_CIDR);
+	socid = cidr & ~AT91_CIDR_VERSION;
+
+	switch (socid) {
+	case ARCH_ID_AT91CAP9: {
+#ifdef CONFIG_AT91_PMC_UNIT
+		u32 pmc_ver = at91_sys_read(AT91_PMC_VER);
+
+		if (pmc_ver == ARCH_REVISION_CAP9_B)
+			at91_soc_initdata.subtype = AT91_SOC_CAP9_REV_B;
+		else if (pmc_ver == ARCH_REVISION_CAP9_C)
+			at91_soc_initdata.subtype = AT91_SOC_CAP9_REV_C;
+#endif
+		at91_soc_initdata.type = AT91_SOC_CAP9;
+		at91_boot_soc = at91cap9_soc;
+		break;
+	}
+
+	case ARCH_ID_AT91RM9200:
+		at91_soc_initdata.type = AT91_SOC_RM9200;
+		at91_boot_soc = at91rm9200_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9260:
+		at91_soc_initdata.type = AT91_SOC_SAM9260;
+		at91_boot_soc = at91sam9260_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9261:
+		at91_soc_initdata.type = AT91_SOC_SAM9261;
+		at91_boot_soc = at91sam9261_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9263:
+		at91_soc_initdata.type = AT91_SOC_SAM9263;
+		at91_boot_soc = at91sam9263_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9G20:
+		at91_soc_initdata.type = AT91_SOC_SAM9G20;
+		at91_boot_soc = at91sam9260_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9G45:
+		at91_soc_initdata.type = AT91_SOC_SAM9G45;
+		if (cidr == ARCH_ID_AT91SAM9G45ES)
+			at91_soc_initdata.subtype = AT91_SOC_SAM9G45ES;
+		at91_boot_soc = at91sam9g45_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9RL64:
+		at91_soc_initdata.type = AT91_SOC_SAM9RL;
+		at91_boot_soc = at91sam9rl_soc;
+		break;
+
+	case ARCH_ID_AT91SAM9X5:
+		at91_soc_initdata.type = AT91_SOC_SAM9X5;
+		at91_boot_soc = at91sam9x5_soc;
+		break;
+	}
+
+	/* at91sam9g10 */
+	if ((cidr & ~AT91_CIDR_EXT) == ARCH_ID_AT91SAM9G10) {
+		at91_soc_initdata.type = AT91_SOC_SAM9G10;
+		at91_boot_soc = at91sam9261_soc;
+	}
+	/* at91sam9xe */
+	else if ((cidr & AT91_CIDR_ARCH) == ARCH_FAMILY_AT91SAM9XE) {
+		at91_soc_initdata.type = AT91_SOC_SAM9260;
+		at91_soc_initdata.subtype = AT91_SOC_SAM9XE;
+		at91_boot_soc = at91sam9260_soc;
+	}
+
+	if (!at91_soc_is_detected())
+		return;
+
+	at91_soc_initdata.cidr = cidr;
+
+	/* sub version of soc */
+	at91_soc_initdata.exid = __raw_readl(AT91_IO_P2V(dbgu_base) + AT91_DBGU_EXID);
+
+	if (at91_soc_initdata.type == AT91_SOC_SAM9G45) {
+		switch (at91_soc_initdata.exid) {
+		case ARCH_EXID_AT91SAM9M10:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9M10;
+			break;
+		case ARCH_EXID_AT91SAM9G46:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9G46;
+			break;
+		case ARCH_EXID_AT91SAM9M11:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9M11;
+			break;
+		}
+	}
+
+	if (at91_soc_initdata.type == AT91_SOC_SAM9X5) {
+		switch (at91_soc_initdata.exid) {
+		case ARCH_EXID_AT91SAM9G15:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9G15;
+			break;
+		case ARCH_EXID_AT91SAM9G35:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9G35;
+			break;
+		case ARCH_EXID_AT91SAM9X35:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9X35;
+			break;
+		case ARCH_EXID_AT91SAM9G25:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9G25;
+			break;
+		case ARCH_EXID_AT91SAM9X25:
+			at91_soc_initdata.subtype = AT91_SOC_SAM9X25;
+			break;
+		}
+	}
+}
+
+static const char *soc_name[] = {
+	[AT91_SOC_RM9200]	= "at91rm9200",
+	[AT91_SOC_CAP9]		= "at91cap9",
+	[AT91_SOC_SAM9260]	= "at91sam9260",
+	[AT91_SOC_SAM9261]	= "at91sam9261",
+	[AT91_SOC_SAM9263]	= "at91sam9263",
+	[AT91_SOC_SAM9G10]	= "at91sam9g10",
+	[AT91_SOC_SAM9G20]	= "at91sam9g20",
+	[AT91_SOC_SAM9G45]	= "at91sam9g45",
+	[AT91_SOC_SAM9RL]	= "at91sam9rl",
+	[AT91_SOC_SAM9X5]	= "at91sam9x5",
+	[AT91_SOC_NONE]		= "Unknown"
+};
+
+const char *at91_get_soc_type(struct at91_socinfo *c)
+{
+	return soc_name[c->type];
+}
+EXPORT_SYMBOL(at91_get_soc_type);
+
+static const char *soc_subtype_name[] = {
+	[AT91_SOC_RM9200_BGA]	= "at91rm9200 BGA",
+	[AT91_SOC_RM9200_PQFP]	= "at91rm9200 PQFP",
+	[AT91_SOC_CAP9_REV_B]	= "at91cap9 revB",
+	[AT91_SOC_CAP9_REV_C]	= "at91cap9 revC",
+	[AT91_SOC_SAM9XE]	= "at91sam9xe",
+	[AT91_SOC_SAM9G45ES]	= "at91sam9g45es",
+	[AT91_SOC_SAM9M10]	= "at91sam9m10",
+	[AT91_SOC_SAM9G46]	= "at91sam9g46",
+	[AT91_SOC_SAM9M11]	= "at91sam9m11",
+	[AT91_SOC_SAM9G15]	= "at91sam9g15",
+	[AT91_SOC_SAM9G35]	= "at91sam9g35",
+	[AT91_SOC_SAM9X35]	= "at91sam9x35",
+	[AT91_SOC_SAM9G25]	= "at91sam9g25",
+	[AT91_SOC_SAM9X25]	= "at91sam9x25",
+	[AT91_SOC_SUBTYPE_NONE]	= "Unknown"
+};
+
+const char *at91_get_soc_subtype(struct at91_socinfo *c)
+{
+	return soc_subtype_name[c->subtype];
+}
+EXPORT_SYMBOL(at91_get_soc_subtype);
+
+void __init at91_map_io(void)
+{
+	/* Map peripherals */
+	iotable_init(&at91_io_desc, 1);
+
+	at91_soc_initdata.type = AT91_SOC_NONE;
+	at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
+
+	soc_detect(AT91_DBGU0);
+	if (!at91_soc_is_detected())
+		soc_detect(AT91_DBGU1);
+
+	if (!at91_soc_is_detected())
+		panic("AT91: Impossible to detect the SOC type");
+
+	pr_info("AT91: Detected soc type: %s\n",
+		at91_get_soc_type(&at91_soc_initdata));
+	pr_info("AT91: Detected soc subtype: %s\n",
+		at91_get_soc_subtype(&at91_soc_initdata));
+
+	if (!at91_soc_is_enabled())
+		panic("AT91: Soc not enabled");
+
+	if (at91_boot_soc.map_io)
+		at91_boot_soc.map_io();
+}
+
+void __init at91_initialize(unsigned long main_clock)
+{
+	/* Init clock subsystem */
+	at91_clock_init(main_clock);
+
+	/* Register the processor-specific clocks */
+	at91_boot_soc.register_clocks();
+
+	at91_boot_soc.init();
+}
diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h
new file mode 100644
index 0000000..21ed881
--- /dev/null
+++ b/arch/arm/mach-at91/soc.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * Under GPLv2
+ */
+
+struct at91_init_soc {
+	unsigned int *default_irq_priority;
+	void (*map_io)(void);
+	void (*register_clocks)(void);
+	void (*init)(void);
+};
+
+extern struct at91_init_soc at91_boot_soc;
+extern struct at91_init_soc at91cap9_soc;
+extern struct at91_init_soc at91rm9200_soc;
+extern struct at91_init_soc at91sam9260_soc;
+extern struct at91_init_soc at91sam9261_soc;
+extern struct at91_init_soc at91sam9263_soc;
+extern struct at91_init_soc at91sam9g45_soc;
+extern struct at91_init_soc at91sam9rl_soc;
+extern struct at91_init_soc at91sam9x5_soc;
+
+static inline int at91_soc_is_enabled(void)
+{
+	return at91_boot_soc.init != NULL;
+}
+
+#if !defined(CONFIG_ARCH_AT91CAP9)
+#define at91cap9_soc	at91_boot_soc
+#endif
+
+#if !defined(CONFIG_ARCH_AT91RM9200)
+#define at91rm9200_soc	at91_boot_soc
+#endif
+
+#if !(defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20))
+#define at91sam9260_soc	at91_boot_soc
+#endif
+
+#if !(defined(CONFIG_ARCH_AT91SAM9261) || defined(CONFIG_ARCH_AT91SAM9G10))
+#define at91sam9261_soc	at91_boot_soc
+#endif
+
+#if !defined(CONFIG_ARCH_AT91SAM9263)
+#define at91sam9263_soc	at91_boot_soc
+#endif
+
+#if !defined(CONFIG_ARCH_AT91SAM9G45)
+#define at91sam9g45_soc	at91_boot_soc
+#endif
+
+#if !defined(CONFIG_ARCH_AT91SAM9RL)
+#define at91sam9rl_soc	at91_boot_soc
+#endif
+
+#if !defined(CONFIG_ARCH_AT91SAM9X5)
+#define at91sam9x5_soc	at91_boot_soc
+#endif
diff --git a/arch/arm/mach-bcmring/dma.c b/arch/arm/mach-bcmring/dma.c
index 9f2a948..0ca0005 100644
--- a/arch/arm/mach-bcmring/dma.c
+++ b/arch/arm/mach-bcmring/dma.c
@@ -34,7 +34,7 @@
 
 #include <linux/mm.h>
 #include <linux/pfn.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <mach/dma.h>
 
 /* I don't quite understand why dc4 fails when this is set to 1 and DMA is enabled */
diff --git a/arch/arm/mach-bcmring/include/mach/clkdev.h b/arch/arm/mach-bcmring/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-bcmring/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-bcmring/include/mach/hardware.h b/arch/arm/mach-bcmring/include/mach/hardware.h
index 8bf3564..ed78aab 100644
--- a/arch/arm/mach-bcmring/include/mach/hardware.h
+++ b/arch/arm/mach-bcmring/include/mach/hardware.h
@@ -36,8 +36,6 @@
 #define RAM_SIZE                (CFG_GLOBAL_RAM_SIZE-CFG_GLOBAL_RAM_SIZE_RESERVED)
 #define RAM_BASE                PAGE_OFFSET
 
-#define pcibios_assign_all_busses()	1
-
 /* Macros to make managing spinlocks a bit more controlled in terms of naming. */
 /* See reg_gpio.h, reg_irq.h, arch.c, gpio.c for example usage. */
 #if defined(__KERNEL__)
diff --git a/arch/arm/mach-cns3xxx/cns3420vb.c b/arch/arm/mach-cns3xxx/cns3420vb.c
index 08e5c87..3e7d149 100644
--- a/arch/arm/mach-cns3xxx/cns3420vb.c
+++ b/arch/arm/mach-cns3xxx/cns3420vb.c
@@ -29,7 +29,6 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
-#include <mach/hardware.h>
 #include <mach/cns3xxx.h>
 #include <mach/irqs.h>
 #include "core.h"
@@ -170,6 +169,8 @@
 
 static void __init cns3420_init(void)
 {
+	cns3xxx_l2x0_init();
+
 	platform_add_devices(cns3420_pdevs, ARRAY_SIZE(cns3420_pdevs));
 
 	cns3xxx_ahci_init();
diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c
index da30078..941a308 100644
--- a/arch/arm/mach-cns3xxx/core.c
+++ b/arch/arm/mach-cns3xxx/core.c
@@ -16,6 +16,7 @@
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
+#include <asm/hardware/cache-l2x0.h>
 #include <mach/cns3xxx.h>
 #include "core.h"
 
@@ -244,3 +245,45 @@
 struct sys_timer cns3xxx_timer = {
 	.init = cns3xxx_timer_init,
 };
+
+#ifdef CONFIG_CACHE_L2X0
+
+void __init cns3xxx_l2x0_init(void)
+{
+	void __iomem *base = ioremap(CNS3XXX_L2C_BASE, SZ_4K);
+	u32 val;
+
+	if (WARN_ON(!base))
+		return;
+
+	/*
+	 * Tag RAM Control register
+	 *
+	 * bit[10:8]	- 1 cycle of write accesses latency
+	 * bit[6:4]	- 1 cycle of read accesses latency
+	 * bit[3:0]	- 1 cycle of setup latency
+	 *
+	 * 1 cycle of latency for setup, read and write accesses
+	 */
+	val = readl(base + L2X0_TAG_LATENCY_CTRL);
+	val &= 0xfffff888;
+	writel(val, base + L2X0_TAG_LATENCY_CTRL);
+
+	/*
+	 * Data RAM Control register
+	 *
+	 * bit[10:8]	- 1 cycles of write accesses latency
+	 * bit[6:4]	- 1 cycles of read accesses latency
+	 * bit[3:0]	- 1 cycle of setup latency
+	 *
+	 * 1 cycle of latency for setup, read and write accesses
+	 */
+	val = readl(base + L2X0_DATA_LATENCY_CTRL);
+	val &= 0xfffff888;
+	writel(val, base + L2X0_DATA_LATENCY_CTRL);
+
+	/* 32 KiB, 8-way, parity disable */
+	l2x0_init(base, 0x00540000, 0xfe000fff);
+}
+
+#endif /* CONFIG_CACHE_L2X0 */
diff --git a/arch/arm/mach-cns3xxx/core.h b/arch/arm/mach-cns3xxx/core.h
index ffeb3a8..fcd2253 100644
--- a/arch/arm/mach-cns3xxx/core.h
+++ b/arch/arm/mach-cns3xxx/core.h
@@ -13,6 +13,12 @@
 
 extern struct sys_timer cns3xxx_timer;
 
+#ifdef CONFIG_CACHE_L2X0
+void __init cns3xxx_l2x0_init(void);
+#else
+static inline void cns3xxx_l2x0_init(void) {}
+#endif /* CONFIG_CACHE_L2X0 */
+
 void __init cns3xxx_map_io(void);
 void __init cns3xxx_init_irq(void);
 void cns3xxx_power_off(void);
diff --git a/arch/arm/mach-cns3xxx/include/mach/hardware.h b/arch/arm/mach-cns3xxx/include/mach/hardware.h
deleted file mode 100644
index 57e0983..0000000
--- a/arch/arm/mach-cns3xxx/include/mach/hardware.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * This file contains the hardware definitions of the Cavium Networks boards.
- *
- * Copyright 2003 ARM Limited.
- * Copyright 2008 Cavium Networks
- *
- * This file 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 __MACH_HARDWARE_H
-#define __MACH_HARDWARE_H
-
-#include <asm/sizes.h>
-
-/* macro to get at IO space when running virtually */
-#define PCIBIOS_MIN_IO		0x00000000
-#define PCIBIOS_MIN_MEM		0x00000000
-#define pcibios_assign_all_busses()	1
-
-#endif
diff --git a/arch/arm/mach-cns3xxx/include/mach/pm.h b/arch/arm/mach-cns3xxx/include/mach/pm.h
index 6eae7f7..c2588cc 100644
--- a/arch/arm/mach-cns3xxx/include/mach/pm.h
+++ b/arch/arm/mach-cns3xxx/include/mach/pm.h
@@ -11,7 +11,7 @@
 #ifndef __CNS3XXX_PM_H
 #define __CNS3XXX_PM_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 void cns3xxx_pwr_clk_en(unsigned int block);
 void cns3xxx_pwr_clk_dis(unsigned int block);
diff --git a/arch/arm/mach-cns3xxx/pcie.c b/arch/arm/mach-cns3xxx/pcie.c
index 78defd7..06fd25d 100644
--- a/arch/arm/mach-cns3xxx/pcie.c
+++ b/arch/arm/mach-cns3xxx/pcie.c
@@ -172,7 +172,7 @@
 	return pci_scan_bus(sys->busnr, &cns3xxx_pcie_ops, sys);
 }
 
-static int cns3xxx_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct cns3xxx_pcie *cnspci = pdev_to_cnspci(dev);
 	int irq = cnspci->irqs[slot];
@@ -369,6 +369,9 @@
 {
 	int i;
 
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 	hook_fault_code(16 + 6, cns3xxx_pcie_abort_handler, SIGBUS, 0,
 			"imprecise external abort");
 
diff --git a/arch/arm/mach-cns3xxx/pm.c b/arch/arm/mach-cns3xxx/pm.c
index 5e57955..0c04678 100644
--- a/arch/arm/mach-cns3xxx/pm.c
+++ b/arch/arm/mach-cns3xxx/pm.c
@@ -10,7 +10,7 @@
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <mach/system.h>
 #include <mach/cns3xxx.h>
 #include <mach/pm.h>
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index 29671ef..bd53945 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -1117,6 +1117,8 @@
 static __init int da850_evm_init_cpufreq(void) { return 0; }
 #endif
 
+#define DA850EVM_SATA_REFCLKPN_RATE	(100 * 1000 * 1000)
+
 static __init void da850_evm_init(void)
 {
 	int ret;
@@ -1237,6 +1239,11 @@
 	if (ret)
 		pr_warning("da850_evm_init: spi 1 registration failed: %d\n",
 				ret);
+
+	ret = da850_register_sata(DA850EVM_SATA_REFCLKPN_RATE);
+	if (ret)
+		pr_warning("da850_evm_init: sata registration failed: %d\n",
+				ret);
 }
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c
index ae65319..0086113 100644
--- a/arch/arm/mach-davinci/clock.c
+++ b/arch/arm/mach-davinci/clock.c
@@ -44,7 +44,7 @@
 		__clk_enable(clk->parent);
 	if (clk->usecount++ == 0 && (clk->flags & CLK_PSC))
 		davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc,
-				PSC_STATE_ENABLE);
+				true, clk->flags);
 }
 
 static void __clk_disable(struct clk *clk)
@@ -54,8 +54,7 @@
 	if (--clk->usecount == 0 && !(clk->flags & CLK_PLL) &&
 	    (clk->flags & CLK_PSC))
 		davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc,
-				(clk->flags & PSC_SWRSTDISABLE) ?
-				PSC_STATE_SWRSTDISABLE : PSC_STATE_DISABLE);
+				false, clk->flags);
 	if (clk->parent)
 		__clk_disable(clk->parent);
 }
@@ -239,8 +238,7 @@
 		pr_debug("Clocks: disable unused %s\n", ck->name);
 
 		davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc,
-				(ck->flags & PSC_SWRSTDISABLE) ?
-				PSC_STATE_SWRSTDISABLE : PSC_STATE_DISABLE);
+				false, ck->flags);
 	}
 	spin_unlock_irq(&clockfw_lock);
 
diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h
index 50b2482..a705f36 100644
--- a/arch/arm/mach-davinci/clock.h
+++ b/arch/arm/mach-davinci/clock.h
@@ -111,6 +111,7 @@
 #define CLK_PLL			BIT(4) /* PLL-derived clock */
 #define PRE_PLL			BIT(5) /* source is before PLL mult/div */
 #define PSC_SWRSTDISABLE	BIT(6) /* Disable state is SwRstDisable */
+#define PSC_FORCE		BIT(7) /* Force module state transtition */
 
 #define CLK(dev, con, ck) 	\
 	{			\
diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
index 133aac4..935dbed 100644
--- a/arch/arm/mach-davinci/da850.c
+++ b/arch/arm/mach-davinci/da850.c
@@ -58,6 +58,7 @@
 static struct clk ref_clk = {
 	.name		= "ref_clk",
 	.rate		= DA850_REF_FREQ,
+	.set_rate	= davinci_simple_set_rate,
 };
 
 static struct clk pll0_clk = {
@@ -373,6 +374,14 @@
 	.flags		= DA850_CLK_ASYNC3,
 };
 
+static struct clk sata_clk = {
+	.name		= "sata",
+	.parent		= &pll0_sysclk2,
+	.lpsc		= DA850_LPSC1_SATA,
+	.gpsc		= 1,
+	.flags		= PSC_FORCE,
+};
+
 static struct clk_lookup da850_clks[] = {
 	CLK(NULL,		"ref",		&ref_clk),
 	CLK(NULL,		"pll0",		&pll0_clk),
@@ -419,6 +428,7 @@
 	CLK(NULL,		"usb20",	&usb20_clk),
 	CLK("spi_davinci.0",	NULL,		&spi0_clk),
 	CLK("spi_davinci.1",	NULL,		&spi1_clk),
+	CLK("ahci",		NULL,		&sata_clk),
 	CLK(NULL,		NULL,		NULL),
 };
 
diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index fc4e98e..2f7e719 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -14,6 +14,8 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/serial_8250.h>
+#include <linux/ahci_platform.h>
+#include <linux/clk.h>
 
 #include <mach/cputype.h>
 #include <mach/common.h>
@@ -33,6 +35,7 @@
 #define DA8XX_SPI0_BASE			0x01c41000
 #define DA830_SPI1_BASE			0x01e12000
 #define DA8XX_LCD_CNTRL_BASE		0x01e13000
+#define DA850_SATA_BASE			0x01e18000
 #define DA850_MMCSD1_BASE		0x01e1b000
 #define DA8XX_EMAC_CPPI_PORT_BASE	0x01e20000
 #define DA8XX_EMAC_CPGMACSS_BASE	0x01e22000
@@ -842,3 +845,126 @@
 
 	return platform_device_register(&da8xx_spi_device[instance]);
 }
+
+#ifdef CONFIG_ARCH_DAVINCI_DA850
+
+static struct resource da850_sata_resources[] = {
+	{
+		.start	= DA850_SATA_BASE,
+		.end	= DA850_SATA_BASE + 0x1fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_DA850_SATAINT,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+/* SATA PHY Control Register offset from AHCI base */
+#define SATA_P0PHYCR_REG	0x178
+
+#define SATA_PHY_MPY(x)		((x) << 0)
+#define SATA_PHY_LOS(x)		((x) << 6)
+#define SATA_PHY_RXCDR(x)	((x) << 10)
+#define SATA_PHY_RXEQ(x)	((x) << 13)
+#define SATA_PHY_TXSWING(x)	((x) << 19)
+#define SATA_PHY_ENPLL(x)	((x) << 31)
+
+static struct clk *da850_sata_clk;
+static unsigned long da850_sata_refclkpn;
+
+/* Supported DA850 SATA crystal frequencies */
+#define KHZ_TO_HZ(freq) ((freq) * 1000)
+static unsigned long da850_sata_xtal[] = {
+	KHZ_TO_HZ(300000),
+	KHZ_TO_HZ(250000),
+	0,			/* Reserved */
+	KHZ_TO_HZ(187500),
+	KHZ_TO_HZ(150000),
+	KHZ_TO_HZ(125000),
+	KHZ_TO_HZ(120000),
+	KHZ_TO_HZ(100000),
+	KHZ_TO_HZ(75000),
+	KHZ_TO_HZ(60000),
+};
+
+static int da850_sata_init(struct device *dev, void __iomem *addr)
+{
+	int i, ret;
+	unsigned int val;
+
+	da850_sata_clk = clk_get(dev, NULL);
+	if (IS_ERR(da850_sata_clk))
+		return PTR_ERR(da850_sata_clk);
+
+	ret = clk_enable(da850_sata_clk);
+	if (ret)
+		goto err0;
+
+	/* Enable SATA clock receiver */
+	val = __raw_readl(DA8XX_SYSCFG1_VIRT(DA8XX_PWRDN_REG));
+	val &= ~BIT(0);
+	__raw_writel(val, DA8XX_SYSCFG1_VIRT(DA8XX_PWRDN_REG));
+
+	/* Get the multiplier needed for 1.5GHz PLL output */
+	for (i = 0; i < ARRAY_SIZE(da850_sata_xtal); i++)
+		if (da850_sata_xtal[i] == da850_sata_refclkpn)
+			break;
+
+	if (i == ARRAY_SIZE(da850_sata_xtal)) {
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	val = SATA_PHY_MPY(i + 1) |
+		SATA_PHY_LOS(1) |
+		SATA_PHY_RXCDR(4) |
+		SATA_PHY_RXEQ(1) |
+		SATA_PHY_TXSWING(3) |
+		SATA_PHY_ENPLL(1);
+
+	__raw_writel(val, addr + SATA_P0PHYCR_REG);
+
+	return 0;
+
+err1:
+	clk_disable(da850_sata_clk);
+err0:
+	clk_put(da850_sata_clk);
+	return ret;
+}
+
+static void da850_sata_exit(struct device *dev)
+{
+	clk_disable(da850_sata_clk);
+	clk_put(da850_sata_clk);
+}
+
+static struct ahci_platform_data da850_sata_pdata = {
+	.init	= da850_sata_init,
+	.exit	= da850_sata_exit,
+};
+
+static u64 da850_sata_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device da850_sata_device = {
+	.name	= "ahci",
+	.id	= -1,
+	.dev	= {
+		.platform_data		= &da850_sata_pdata,
+		.dma_mask		= &da850_sata_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(da850_sata_resources),
+	.resource	= da850_sata_resources,
+};
+
+int __init da850_register_sata(unsigned long refclkpn)
+{
+	da850_sata_refclkpn = refclkpn;
+	if (!da850_sata_refclkpn)
+		return -EINVAL;
+
+	return platform_device_register(&da850_sata_device);
+}
+#endif
diff --git a/arch/arm/mach-davinci/include/mach/clkdev.h b/arch/arm/mach-davinci/include/mach/clkdev.h
deleted file mode 100644
index 14a5048..0000000
--- a/arch/arm/mach-davinci/include/mach/clkdev.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
-
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
-
-#endif
diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h
index ad64da7..eaca7d8 100644
--- a/arch/arm/mach-davinci/include/mach/da8xx.h
+++ b/arch/arm/mach-davinci/include/mach/da8xx.h
@@ -57,6 +57,7 @@
 #define DA8XX_SYSCFG1_BASE	(IO_PHYS + 0x22C000)
 #define DA8XX_SYSCFG1_VIRT(x)	(da8xx_syscfg1_base + (x))
 #define DA8XX_DEEPSLEEP_REG	0x8
+#define DA8XX_PWRDN_REG		0x18
 
 #define DA8XX_PSC0_BASE		0x01c10000
 #define DA8XX_PLL0_BASE		0x01c11000
@@ -89,6 +90,7 @@
 int da8xx_register_cpuidle(void);
 void __iomem * __init da8xx_get_mem_ctlr(void);
 int da850_register_pm(struct platform_device *pdev);
+int __init da850_register_sata(unsigned long refclkpn);
 
 extern struct platform_device da8xx_serial_device;
 extern struct emac_platform_data da8xx_emac_pdata;
diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h
index 1110fdd..47fd0bc 100644
--- a/arch/arm/mach-davinci/include/mach/psc.h
+++ b/arch/arm/mach-davinci/include/mach/psc.h
@@ -244,12 +244,13 @@
 #define PSC_STATE_ENABLE	3
 
 #define MDSTAT_STATE_MASK	0x1f
+#define MDCTL_FORCE		BIT(31)
 
 #ifndef __ASSEMBLER__
 
 extern int davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id);
 extern void davinci_psc_config(unsigned int domain, unsigned int ctlr,
-		unsigned int id, u32 next_state);
+		unsigned int id, bool enable, u32 flags);
 
 #endif
 
diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c
index a415804..1fb6bdf 100644
--- a/arch/arm/mach-davinci/psc.c
+++ b/arch/arm/mach-davinci/psc.c
@@ -25,6 +25,8 @@
 #include <mach/cputype.h>
 #include <mach/psc.h>
 
+#include "clock.h"
+
 /* Return nonzero iff the domain's clock is active */
 int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id)
 {
@@ -48,11 +50,12 @@
 
 /* Enable or disable a PSC domain */
 void davinci_psc_config(unsigned int domain, unsigned int ctlr,
-		unsigned int id, u32 next_state)
+		unsigned int id, bool enable, u32 flags)
 {
 	u32 epcpr, ptcmd, ptstat, pdstat, pdctl1, mdstat, mdctl;
 	void __iomem *psc_base;
 	struct davinci_soc_info *soc_info = &davinci_soc_info;
+	u32 next_state = PSC_STATE_ENABLE;
 
 	if (!soc_info->psc_bases || (ctlr >= soc_info->psc_bases_num)) {
 		pr_warning("PSC: Bad psc data: 0x%x[%d]\n",
@@ -62,9 +65,18 @@
 
 	psc_base = ioremap(soc_info->psc_bases[ctlr], SZ_4K);
 
+	if (!enable) {
+		if (flags & PSC_SWRSTDISABLE)
+			next_state = PSC_STATE_SWRSTDISABLE;
+		else
+			next_state = PSC_STATE_DISABLE;
+	}
+
 	mdctl = __raw_readl(psc_base + MDCTL + 4 * id);
 	mdctl &= ~MDSTAT_STATE_MASK;
 	mdctl |= next_state;
+	if (flags & PSC_FORCE)
+		mdctl |= MDCTL_FORCE;
 	__raw_writel(mdctl, psc_base + MDCTL + 4 * id);
 
 	pdstat = __raw_readl(psc_base + PDSTAT);
diff --git a/arch/arm/mach-dove/include/mach/hardware.h b/arch/arm/mach-dove/include/mach/hardware.h
index 32b0826..f1368b9 100644
--- a/arch/arm/mach-dove/include/mach/hardware.h
+++ b/arch/arm/mach-dove/include/mach/hardware.h
@@ -11,13 +11,6 @@
 
 #include "dove.h"
 
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO			0x1000
-#define PCIBIOS_MIN_MEM			0x01000000
-#define PCIMEM_BASE			DOVE_PCIE0_MEM_PHYS_BASE
-
-
 /* Macros below are required for compatibility with PXA AC'97 driver.	*/
 #define __REG(x)	(*((volatile u32 *)((x) - DOVE_SB_REGS_PHYS_BASE + \
 				DOVE_SB_REGS_VIRT_BASE)))
diff --git a/arch/arm/mach-dove/pcie.c b/arch/arm/mach-dove/pcie.c
index 502d1ca..aa2b3a0 100644
--- a/arch/arm/mach-dove/pcie.c
+++ b/arch/arm/mach-dove/pcie.c
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/mbus.h>
+#include <video/vga.h>
 #include <asm/mach/pci.h>
 #include <asm/mach/arch.h>
 #include <asm/setup.h>
@@ -192,7 +193,7 @@
 	return bus;
 }
 
-static int __init dove_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init dove_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct pcie_port *pp = bus_to_port(dev->bus->number);
 
@@ -228,6 +229,8 @@
 
 void __init dove_pcie_init(int init_port0, int init_port1)
 {
+	vga_base = DOVE_PCIE0_MEM_PHYS_BASE;
+
 	if (init_port0)
 		add_pcie_port(0, DOVE_PCIE0_VIRT_BASE);
 
diff --git a/arch/arm/mach-ep93xx/include/mach/clkdev.h b/arch/arm/mach-ep93xx/include/mach/clkdev.h
deleted file mode 100644
index 50cb991..0000000
--- a/arch/arm/mach-ep93xx/include/mach/clkdev.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * arch/arm/mach-ep93xx/include/mach/clkdev.h
- */
-
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-ep93xx/include/mach/hardware.h b/arch/arm/mach-ep93xx/include/mach/hardware.h
index 5a3ce02..4df8428 100644
--- a/arch/arm/mach-ep93xx/include/mach/hardware.h
+++ b/arch/arm/mach-ep93xx/include/mach/hardware.h
@@ -8,8 +8,6 @@
 #include <mach/ep93xx-regs.h>
 #include <mach/platform.h>
 
-#define pcibios_assign_all_busses()	0
-
 /*
  * The EP93xx has two external crystal oscillators.  To generate the
  * required high-frequency clocks, the processor uses two phase-locked-
diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
index ae433a0..0c77ab9 100644
--- a/arch/arm/mach-exynos4/Kconfig
+++ b/arch/arm/mach-exynos4/Kconfig
@@ -16,7 +16,8 @@
 	  Enable EXYNOS4210 CPU support
 
 config EXYNOS4_MCT
-	bool "Kernel timer support by MCT"
+	bool
+	default y
 	help
 	  Use MCT (Multi Core Timer) as kernel timers
 
@@ -25,6 +26,11 @@
 	help
 	  Compile in platform device definitions for AHCI
 
+config EXYNOS4_SETUP_FIMD0
+	bool
+	help
+	  Common setup code for FIMD0.
+
 config EXYNOS4_DEV_PD
 	bool
 	help
@@ -35,6 +41,11 @@
 	help
 	  Common setup code for SYSTEM MMU in EXYNOS4
 
+config EXYNOS4_DEV_DWMCI
+	bool
+	help
+	  Compile in platform device definitions for DWMCI
+
 config EXYNOS4_SETUP_I2C1
 	bool
 	help
@@ -103,6 +114,7 @@
 config MACH_SMDKC210
 	bool "SMDKC210"
 	select CPU_EXYNOS4210
+	select S5P_DEV_FIMD0
 	select S3C_DEV_RTC
 	select S3C_DEV_WDT
 	select S3C_DEV_I2C1
@@ -114,6 +126,7 @@
 	select SAMSUNG_DEV_BACKLIGHT
 	select EXYNOS4_DEV_PD
 	select EXYNOS4_DEV_SYSMMU
+	select EXYNOS4_SETUP_FIMD0
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_SDHCI
 	help
@@ -122,6 +135,7 @@
 config MACH_SMDKV310
 	bool "SMDKV310"
 	select CPU_EXYNOS4210
+	select S5P_DEV_FIMD0
 	select S3C_DEV_RTC
 	select S3C_DEV_WDT
 	select S3C_DEV_I2C1
@@ -130,10 +144,12 @@
 	select S3C_DEV_HSMMC2
 	select S3C_DEV_HSMMC3
 	select SAMSUNG_DEV_BACKLIGHT
+	select EXYNOS4_DEV_AHCI
 	select SAMSUNG_DEV_KEYPAD
 	select EXYNOS4_DEV_PD
 	select SAMSUNG_DEV_PWM
 	select EXYNOS4_DEV_SYSMMU
+	select EXYNOS4_SETUP_FIMD0
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_KEYPAD
 	select EXYNOS4_SETUP_SDHCI
@@ -157,13 +173,22 @@
 config MACH_UNIVERSAL_C210
 	bool "Mobile UNIVERSAL_C210 Board"
 	select CPU_EXYNOS4210
+	select S5P_GPIO_INT
+	select S5P_DEV_FIMC0
+	select S5P_DEV_FIMC1
+	select S5P_DEV_FIMC2
+	select S5P_DEV_FIMC3
 	select S3C_DEV_HSMMC
 	select S3C_DEV_HSMMC2
 	select S3C_DEV_HSMMC3
 	select S3C_DEV_I2C1
+	select S3C_DEV_I2C3
 	select S3C_DEV_I2C5
+	select S5P_DEV_MFC
 	select S5P_DEV_ONENAND
+	select EXYNOS4_DEV_PD
 	select EXYNOS4_SETUP_I2C1
+	select EXYNOS4_SETUP_I2C3
 	select EXYNOS4_SETUP_I2C5
 	select EXYNOS4_SETUP_SDHCI
 	help
@@ -180,13 +205,16 @@
 	select S3C_DEV_I2C1
 	select S3C_DEV_I2C3
 	select S3C_DEV_I2C5
+	select S5P_DEV_MFC
 	select S5P_DEV_USB_EHCI
+	select EXYNOS4_DEV_PD
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_I2C3
 	select EXYNOS4_SETUP_I2C5
 	select EXYNOS4_SETUP_SDHCI
 	select EXYNOS4_SETUP_USB_PHY
 	select SAMSUNG_DEV_PWM
+	select SAMSUNG_DEV_ADC
 	help
 	  Machine support for Samsung Mobile NURI Board.
 
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
index 1366995..b7fe1d7 100644
--- a/arch/arm/mach-exynos4/Makefile
+++ b/arch/arm/mach-exynos4/Makefile
@@ -13,18 +13,13 @@
 # Core support for EXYNOS4 system
 
 obj-$(CONFIG_CPU_EXYNOS4210)	+= cpu.o init.o clock.o irq-combiner.o
-obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o irq-eint.o dma.o
+obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o irq-eint.o dma.o pmu.o
 obj-$(CONFIG_PM)		+= pm.o sleep.o
 obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 
-ifeq ($(CONFIG_EXYNOS4_MCT),y)
-obj-y				+= mct.o
-else
-obj-y				+= time.o
-obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
-endif
+obj-$(CONFIG_EXYNOS4_MCT)	+= mct.o
 
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
@@ -42,8 +37,10 @@
 obj-$(CONFIG_EXYNOS4_DEV_AHCI)		+= dev-ahci.o
 obj-$(CONFIG_EXYNOS4_DEV_PD)		+= dev-pd.o
 obj-$(CONFIG_EXYNOS4_DEV_SYSMMU)	+= dev-sysmmu.o
+obj-$(CONFIG_EXYNOS4_DEV_DWMCI)	+= dev-dwmci.o
 
 obj-$(CONFIG_EXYNOS4_SETUP_FIMC)	+= setup-fimc.o
+obj-$(CONFIG_EXYNOS4_SETUP_FIMD0)	+= setup-fimd0.o
 obj-$(CONFIG_EXYNOS4_SETUP_I2C1)	+= setup-i2c1.o
 obj-$(CONFIG_EXYNOS4_SETUP_I2C2)	+= setup-i2c2.o
 obj-$(CONFIG_EXYNOS4_SETUP_I2C3)	+= setup-i2c3.o
diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 66494f2..851dea0 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -528,6 +528,11 @@
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 0),
 	}, {
+		.name		= "mfc",
+		.devname	= "s5p-mfc",
+		.enable		= exynos4_clk_ip_mfc_ctrl,
+		.ctrlbit	= (1 << 0),
+	}, {
 		.name		= "i2c",
 		.devname	= "s3c2440-i2c.0",
 		.parent		= &clk_aclk_100.clk,
@@ -731,6 +736,52 @@
 	.nr_sources	= ARRAY_SIZE(clkset_mout_g2d_list),
 };
 
+static struct clk *clkset_mout_mfc0_list[] = {
+	[0] = &clk_mout_mpll.clk,
+	[1] = &clk_sclk_apll.clk,
+};
+
+static struct clksrc_sources clkset_mout_mfc0 = {
+	.sources	= clkset_mout_mfc0_list,
+	.nr_sources	= ARRAY_SIZE(clkset_mout_mfc0_list),
+};
+
+static struct clksrc_clk clk_mout_mfc0 = {
+	.clk	= {
+		.name		= "mout_mfc0",
+	},
+	.sources	= &clkset_mout_mfc0,
+	.reg_src	= { .reg = S5P_CLKSRC_MFC, .shift = 0, .size = 1 },
+};
+
+static struct clk *clkset_mout_mfc1_list[] = {
+	[0] = &clk_mout_epll.clk,
+	[1] = &clk_sclk_vpll.clk,
+};
+
+static struct clksrc_sources clkset_mout_mfc1 = {
+	.sources	= clkset_mout_mfc1_list,
+	.nr_sources	= ARRAY_SIZE(clkset_mout_mfc1_list),
+};
+
+static struct clksrc_clk clk_mout_mfc1 = {
+	.clk	= {
+		.name		= "mout_mfc1",
+	},
+	.sources	= &clkset_mout_mfc1,
+	.reg_src	= { .reg = S5P_CLKSRC_MFC, .shift = 4, .size = 1 },
+};
+
+static struct clk *clkset_mout_mfc_list[] = {
+	[0] = &clk_mout_mfc0.clk,
+	[1] = &clk_mout_mfc1.clk,
+};
+
+static struct clksrc_sources clkset_mout_mfc = {
+	.sources	= clkset_mout_mfc_list,
+	.nr_sources	= ARRAY_SIZE(clkset_mout_mfc_list),
+};
+
 static struct clksrc_clk clk_dout_mmc0 = {
 	.clk		= {
 		.name		= "dout_mmc0",
@@ -974,6 +1025,14 @@
 		.reg_div = { .reg = S5P_CLKDIV_IMAGE, .shift = 0, .size = 4 },
 	}, {
 		.clk		= {
+			.name		= "sclk_mfc",
+			.devname	= "s5p-mfc",
+		},
+		.sources = &clkset_mout_mfc,
+		.reg_src = { .reg = S5P_CLKSRC_MFC, .shift = 8, .size = 1 },
+		.reg_div = { .reg = S5P_CLKDIV_MFC, .shift = 0, .size = 4 },
+	}, {
+		.clk		= {
 			.name		= "sclk_mmc",
 			.devname	= "s3c-sdhci.0",
 			.parent		= &clk_dout_mmc0.clk,
@@ -1049,6 +1108,8 @@
 	&clk_dout_mmc2,
 	&clk_dout_mmc3,
 	&clk_dout_mmc4,
+	&clk_mout_mfc0,
+	&clk_mout_mfc1,
 };
 
 static int xtal_rate;
diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c
index bfd6214..2d8a40c 100644
--- a/arch/arm/mach-exynos4/cpu.c
+++ b/arch/arm/mach-exynos4/cpu.c
@@ -16,12 +16,16 @@
 
 #include <asm/proc-fns.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
 
 #include <plat/cpu.h>
 #include <plat/clock.h>
+#include <plat/devs.h>
 #include <plat/exynos4.h>
+#include <plat/adc-core.h>
 #include <plat/sdhci.h>
 #include <plat/devs.h>
+#include <plat/fb-core.h>
 #include <plat/fimc-core.h>
 #include <plat/iic-core.h>
 
@@ -103,7 +107,17 @@
 		.pfn		= __phys_to_pfn(EXYNOS4_PA_HSPHY),
 		.length		= SZ_4K,
 		.type		= MT_DEVICE,
-	}
+	}, {
+		.virtual	= (unsigned long)S5P_VA_GIC_CPU,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_GIC_CPU),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= (unsigned long)S5P_VA_GIC_DIST,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_GIC_DIST),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE,
+	},
 };
 
 static void exynos4_idle(void)
@@ -129,6 +143,8 @@
 	exynos4_default_sdhci2();
 	exynos4_default_sdhci3();
 
+	s3c_adc_setname("samsung-adc-v3");
+
 	s3c_fimc_setname(0, "exynos4-fimc");
 	s3c_fimc_setname(1, "exynos4-fimc");
 	s3c_fimc_setname(2, "exynos4-fimc");
@@ -138,6 +154,8 @@
 	s3c_i2c0_setname("s3c2440-i2c");
 	s3c_i2c1_setname("s3c2440-i2c");
 	s3c_i2c2_setname("s3c2440-i2c");
+
+	s5p_fb_setname(0, "exynos4-fb");
 }
 
 void __init exynos4_init_clocks(int xtal)
@@ -150,22 +168,23 @@
 	exynos4_setup_clocks();
 }
 
+static void exynos4_gic_irq_eoi(struct irq_data *d)
+{
+	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
+
+	gic_data->cpu_base = S5P_VA_GIC_CPU +
+			    (EXYNOS4_GIC_BANK_OFFSET * smp_processor_id());
+}
+
 void __init exynos4_init_irq(void)
 {
 	int irq;
 
-	gic_init(0, IRQ_LOCALTIMER, S5P_VA_GIC_DIST, S5P_VA_GIC_CPU);
+	gic_init(0, IRQ_SPI(0), S5P_VA_GIC_DIST, S5P_VA_GIC_CPU);
+	gic_arch_extn.irq_eoi = exynos4_gic_irq_eoi;
 
 	for (irq = 0; irq < MAX_COMBINER_NR; irq++) {
 
-		/*
-		 * From SPI(0) to SPI(39) and SPI(51), SPI(53) are
-		 * connected to the interrupt combiner. These irqs
-		 * should be initialized to support cascade interrupt.
-		 */
-		if ((irq >= 40) && !(irq == 51) && !(irq == 53))
-			continue;
-
 		combiner_init(irq, (void __iomem *)S5P_VA_COMBINER(irq),
 				COMBINER_IRQ(irq, 0));
 		combiner_cascade_irq(irq, IRQ_SPI(irq));
diff --git a/arch/arm/mach-exynos4/dev-audio.c b/arch/arm/mach-exynos4/dev-audio.c
index 983069a..5a9f9c2e 100644
--- a/arch/arm/mach-exynos4/dev-audio.c
+++ b/arch/arm/mach-exynos4/dev-audio.c
@@ -21,6 +21,7 @@
 #include <mach/map.h>
 #include <mach/dma.h>
 #include <mach/irqs.h>
+#include <mach/regs-audss.h>
 
 static const char *rclksrc[] = {
 	[0] = "busclk",
@@ -55,6 +56,7 @@
 			.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI
 					 | QUIRK_NEED_RSTCLR,
 			.src_clk = rclksrc,
+			.idma_addr = EXYNOS4_AUDSS_INT_MEM,
 		},
 	},
 };
diff --git a/arch/arm/mach-exynos4/dev-dwmci.c b/arch/arm/mach-exynos4/dev-dwmci.c
new file mode 100644
index 0000000..b025db4
--- /dev/null
+++ b/arch/arm/mach-exynos4/dev-dwmci.c
@@ -0,0 +1,82 @@
+/*
+ * linux/arch/arm/mach-exynos4/dev-dwmci.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Platform device for Synopsys DesignWare Mobile Storage IP
+ *
+ * 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/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mmc/dw_mmc.h>
+
+#include <plat/devs.h>
+
+#include <mach/map.h>
+
+static int exynos4_dwmci_get_bus_wd(u32 slot_id)
+{
+	return 4;
+}
+
+static int exynos4_dwmci_init(u32 slot_id, irq_handler_t handler, void *data)
+{
+	return 0;
+}
+
+static struct resource exynos4_dwmci_resource[] = {
+	[0] = {
+		.start	= EXYNOS4_PA_DWMCI,
+		.end	= EXYNOS4_PA_DWMCI + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_DWMCI,
+		.end	= IRQ_DWMCI,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct dw_mci_board exynos4_dwci_pdata = {
+	.num_slots			= 1,
+	.quirks				= DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
+	.bus_hz				= 80 * 1000 * 1000,
+	.detect_delay_ms	= 200,
+	.init				= exynos4_dwmci_init,
+	.get_bus_wd			= exynos4_dwmci_get_bus_wd,
+};
+
+static u64 exynos4_dwmci_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device exynos4_device_dwmci = {
+	.name		= "dw_mmc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(exynos4_dwmci_resource),
+	.resource	= exynos4_dwmci_resource,
+	.dev		= {
+		.dma_mask		= &exynos4_dwmci_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+		.platform_data	= &exynos4_dwci_pdata,
+	},
+};
+
+void __init exynos4_dwmci_set_platdata(struct dw_mci_board *pd)
+{
+	struct dw_mci_board *npd;
+
+	npd = s3c_set_platdata(pd, sizeof(struct dw_mci_board),
+			&exynos4_device_dwmci);
+
+	if (!npd->init)
+		npd->init = exynos4_dwmci_init;
+	if (!npd->get_bus_wd)
+		npd->get_bus_wd = exynos4_dwmci_get_bus_wd;
+}
diff --git a/arch/arm/mach-exynos4/hotplug.c b/arch/arm/mach-exynos4/hotplug.c
index 2b5909e..7490789 100644
--- a/arch/arm/mach-exynos4/hotplug.c
+++ b/arch/arm/mach-exynos4/hotplug.c
@@ -13,9 +13,12 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/smp.h>
+#include <linux/io.h>
 
 #include <asm/cacheflush.h>
 
+#include <mach/regs-pmu.h>
+
 extern volatile int pen_release;
 
 static inline void cpu_enter_lowpower(void)
@@ -58,12 +61,12 @@
 
 static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
 {
-	/*
-	 * there is no power-control hardware on this platform, so all
-	 * we can do is put the core into WFI; this is safe as the calling
-	 * code will have already disabled interrupts
-	 */
 	for (;;) {
+
+		/* make cpu1 to be turned off at next WFI command */
+		if (cpu == 1)
+			__raw_writel(0, S5P_ARM_CORE1_CONFIGURATION);
+
 		/*
 		 * here's the WFI
 		 */
diff --git a/arch/arm/mach-exynos4/include/mach/dwmci.h b/arch/arm/mach-exynos4/include/mach/dwmci.h
new file mode 100644
index 0000000..7ce6574
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/dwmci.h
@@ -0,0 +1,20 @@
+/* linux/arch/arm/mach-exynos4/include/mach/dwmci.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * Synopsys DesignWare Mobile Storage for EXYNOS4210
+ *
+ * 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_ARM_ARCH_DWMCI_H
+#define __ASM_ARM_ARCH_DWMCI_H __FILE__
+
+#include <linux/mmc/dw_mmc.h>
+
+extern void exynos4_dwmci_set_platdata(struct dw_mci_board *pd);
+
+#endif /* __ASM_ARM_ARCH_DWMCI_H */
diff --git a/arch/arm/mach-exynos4/include/mach/entry-macro.S b/arch/arm/mach-exynos4/include/mach/entry-macro.S
index d8f38c2..d7a1e28 100644
--- a/arch/arm/mach-exynos4/include/mach/entry-macro.S
+++ b/arch/arm/mach-exynos4/include/mach/entry-macro.S
@@ -10,6 +10,7 @@
 */
 
 #include <mach/hardware.h>
+#include <mach/map.h>
 #include <asm/hardware/gic.h>
 
 		.macro	disable_fiq
@@ -18,6 +19,10 @@
 		.macro  get_irqnr_preamble, base, tmp
 		ldr	\base, =gic_cpu_base_addr
 		ldr	\base, [\base]
+		mrc     p15, 0, \tmp, c0, c0, 5
+		and     \tmp, \tmp, #3
+		cmp     \tmp, #1
+		addeq   \base, \base, #EXYNOS4_GIC_BANK_OFFSET
 		.endm
 
 		.macro  arch_ret_to_user, tmp1, tmp2
@@ -75,10 +80,4 @@
 		/* As above, this assumes that irqstat and base are preserved.. */
 
 		.macro test_for_ltirq, irqnr, irqstat, base, tmp
-		bic	\irqnr, \irqstat, #0x1c00
-		mov	\tmp, #0
-		cmp	\irqnr, #29
-		moveq	\tmp, #1
-		streq	\irqstat, [\base, #GIC_CPU_EOI]
-		cmp	\tmp, #0
 		.endm
diff --git a/arch/arm/mach-exynos4/include/mach/irqs.h b/arch/arm/mach-exynos4/include/mach/irqs.h
index 5d03730..934d2a4 100644
--- a/arch/arm/mach-exynos4/include/mach/irqs.h
+++ b/arch/arm/mach-exynos4/include/mach/irqs.h
@@ -19,40 +19,105 @@
 
 #define IRQ_PPI(x)		S5P_IRQ(x+16)
 
-#define IRQ_LOCALTIMER		IRQ_PPI(13)
-
 /* SPI: Shared Peripheral Interrupt */
 
 #define IRQ_SPI(x)		S5P_IRQ(x+32)
 
-#define IRQ_MCT1		IRQ_SPI(35)
+#define IRQ_EINT0		IRQ_SPI(16)
+#define IRQ_EINT1		IRQ_SPI(17)
+#define IRQ_EINT2		IRQ_SPI(18)
+#define IRQ_EINT3		IRQ_SPI(19)
+#define IRQ_EINT4		IRQ_SPI(20)
+#define IRQ_EINT5		IRQ_SPI(21)
+#define IRQ_EINT6		IRQ_SPI(22)
+#define IRQ_EINT7		IRQ_SPI(23)
+#define IRQ_EINT8		IRQ_SPI(24)
+#define IRQ_EINT9		IRQ_SPI(25)
+#define IRQ_EINT10		IRQ_SPI(26)
+#define IRQ_EINT11		IRQ_SPI(27)
+#define IRQ_EINT12		IRQ_SPI(28)
+#define IRQ_EINT13		IRQ_SPI(29)
+#define IRQ_EINT14		IRQ_SPI(30)
+#define IRQ_EINT15		IRQ_SPI(31)
+#define IRQ_EINT16_31		IRQ_SPI(32)
 
-#define IRQ_EINT0		IRQ_SPI(40)
-#define IRQ_EINT1		IRQ_SPI(41)
-#define IRQ_EINT2		IRQ_SPI(42)
-#define IRQ_EINT3		IRQ_SPI(43)
-#define IRQ_USB_HSOTG		IRQ_SPI(44)
-#define IRQ_USB_HOST		IRQ_SPI(45)
-#define IRQ_MODEM_IF		IRQ_SPI(46)
-#define IRQ_ROTATOR		IRQ_SPI(47)
-#define IRQ_JPEG		IRQ_SPI(48)
-#define IRQ_2D			IRQ_SPI(49)
-#define IRQ_PCIE		IRQ_SPI(50)
-#define IRQ_MCT0		IRQ_SPI(51)
-#define IRQ_MFC			IRQ_SPI(52)
-#define IRQ_AUDIO_SS		IRQ_SPI(54)
-#define IRQ_AC97		IRQ_SPI(55)
-#define IRQ_SPDIF		IRQ_SPI(56)
-#define IRQ_KEYPAD		IRQ_SPI(57)
-#define IRQ_INTFEEDCTRL_SSS	IRQ_SPI(58)
-#define IRQ_SLIMBUS		IRQ_SPI(59)
-#define IRQ_PMU			IRQ_SPI(60)
-#define IRQ_TSI			IRQ_SPI(61)
-#define IRQ_SATA		IRQ_SPI(62)
-#define IRQ_GPS			IRQ_SPI(63)
+#define IRQ_PDMA0		IRQ_SPI(35)
+#define IRQ_PDMA1		IRQ_SPI(36)
+#define IRQ_TIMER0_VIC		IRQ_SPI(37)
+#define IRQ_TIMER1_VIC		IRQ_SPI(38)
+#define IRQ_TIMER2_VIC		IRQ_SPI(39)
+#define IRQ_TIMER3_VIC		IRQ_SPI(40)
+#define IRQ_TIMER4_VIC		IRQ_SPI(41)
+#define IRQ_MCT_L0		IRQ_SPI(42)
+#define IRQ_WDT			IRQ_SPI(43)
+#define IRQ_RTC_ALARM		IRQ_SPI(44)
+#define IRQ_RTC_TIC		IRQ_SPI(45)
+#define IRQ_GPIO_XB		IRQ_SPI(46)
+#define IRQ_GPIO_XA		IRQ_SPI(47)
+#define IRQ_MCT_L1		IRQ_SPI(48)
+
+#define IRQ_UART0		IRQ_SPI(52)
+#define IRQ_UART1		IRQ_SPI(53)
+#define IRQ_UART2		IRQ_SPI(54)
+#define IRQ_UART3		IRQ_SPI(55)
+#define IRQ_UART4		IRQ_SPI(56)
+#define IRQ_MCT_G0		IRQ_SPI(57)
+#define IRQ_IIC			IRQ_SPI(58)
+#define IRQ_IIC1		IRQ_SPI(59)
+#define IRQ_IIC2		IRQ_SPI(60)
+#define IRQ_IIC3		IRQ_SPI(61)
+#define IRQ_IIC4		IRQ_SPI(62)
+#define IRQ_IIC5		IRQ_SPI(63)
+#define IRQ_IIC6		IRQ_SPI(64)
+#define IRQ_IIC7		IRQ_SPI(65)
+
+#define IRQ_USB_HOST		IRQ_SPI(70)
+#define IRQ_USB_HSOTG		IRQ_SPI(71)
+#define IRQ_MODEM_IF		IRQ_SPI(72)
+#define IRQ_HSMMC0		IRQ_SPI(73)
+#define IRQ_HSMMC1		IRQ_SPI(74)
+#define IRQ_HSMMC2		IRQ_SPI(75)
+#define IRQ_HSMMC3		IRQ_SPI(76)
+#define IRQ_DWMCI		IRQ_SPI(77)
+
+#define IRQ_MIPICSI0		IRQ_SPI(78)
+
+#define IRQ_MIPICSI1		IRQ_SPI(80)
+
+#define IRQ_ONENAND_AUDI	IRQ_SPI(82)
+#define IRQ_ROTATOR		IRQ_SPI(83)
+#define IRQ_FIMC0		IRQ_SPI(84)
+#define IRQ_FIMC1		IRQ_SPI(85)
+#define IRQ_FIMC2		IRQ_SPI(86)
+#define IRQ_FIMC3		IRQ_SPI(87)
+#define IRQ_JPEG		IRQ_SPI(88)
+#define IRQ_2D			IRQ_SPI(89)
+#define IRQ_PCIE		IRQ_SPI(90)
+
+#define IRQ_MFC			IRQ_SPI(94)
+
+#define IRQ_AUDIO_SS		IRQ_SPI(96)
+#define IRQ_I2S0		IRQ_SPI(97)
+#define IRQ_I2S1		IRQ_SPI(98)
+#define IRQ_I2S2		IRQ_SPI(99)
+#define IRQ_AC97		IRQ_SPI(100)
+
+#define IRQ_SPDIF		IRQ_SPI(104)
+#define IRQ_ADC0		IRQ_SPI(105)
+#define IRQ_PEN0		IRQ_SPI(106)
+#define IRQ_ADC1		IRQ_SPI(107)
+#define IRQ_PEN1		IRQ_SPI(108)
+#define IRQ_KEYPAD		IRQ_SPI(109)
+#define IRQ_PMU			IRQ_SPI(110)
+#define IRQ_GPS			IRQ_SPI(111)
+#define IRQ_INTFEEDCTRL_SSS	IRQ_SPI(112)
+#define IRQ_SLIMBUS		IRQ_SPI(113)
+
+#define IRQ_TSI			IRQ_SPI(115)
+#define IRQ_SATA		IRQ_SPI(116)
 
 #define MAX_IRQ_IN_COMBINER	8
-#define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(64))
+#define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128))
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 
 #define IRQ_SYSMMU_MDMA0_0	COMBINER_IRQ(4, 0)
@@ -73,75 +138,14 @@
 #define IRQ_SYSMMU_MFC_M1_0	COMBINER_IRQ(5, 6)
 #define IRQ_SYSMMU_PCIE_0	COMBINER_IRQ(5, 7)
 
-#define IRQ_PDMA0		COMBINER_IRQ(21, 0)
-#define IRQ_PDMA1		COMBINER_IRQ(21, 1)
+#define IRQ_FIMD0_FIFO		COMBINER_IRQ(11, 0)
+#define IRQ_FIMD0_VSYNC		COMBINER_IRQ(11, 1)
+#define IRQ_FIMD0_SYSTEM	COMBINER_IRQ(11, 2)
 
-#define IRQ_TIMER0_VIC		COMBINER_IRQ(22, 0)
-#define IRQ_TIMER1_VIC		COMBINER_IRQ(22, 1)
-#define IRQ_TIMER2_VIC		COMBINER_IRQ(22, 2)
-#define IRQ_TIMER3_VIC		COMBINER_IRQ(22, 3)
-#define IRQ_TIMER4_VIC		COMBINER_IRQ(22, 4)
+#define MAX_COMBINER_NR		16
 
-#define IRQ_RTC_ALARM		COMBINER_IRQ(23, 0)
-#define IRQ_RTC_TIC		COMBINER_IRQ(23, 1)
-
-#define IRQ_GPIO_XB		COMBINER_IRQ(24, 0)
-#define IRQ_GPIO_XA		COMBINER_IRQ(24, 1)
-
-#define IRQ_UART0		COMBINER_IRQ(26, 0)
-#define IRQ_UART1		COMBINER_IRQ(26, 1)
-#define IRQ_UART2		COMBINER_IRQ(26, 2)
-#define IRQ_UART3		COMBINER_IRQ(26, 3)
-#define IRQ_UART4		COMBINER_IRQ(26, 4)
-
-#define IRQ_IIC			COMBINER_IRQ(27, 0)
-#define IRQ_IIC1		COMBINER_IRQ(27, 1)
-#define IRQ_IIC2		COMBINER_IRQ(27, 2)
-#define IRQ_IIC3		COMBINER_IRQ(27, 3)
-#define IRQ_IIC4		COMBINER_IRQ(27, 4)
-#define IRQ_IIC5		COMBINER_IRQ(27, 5)
-#define IRQ_IIC6		COMBINER_IRQ(27, 6)
-#define IRQ_IIC7		COMBINER_IRQ(27, 7)
-
-#define IRQ_HSMMC0		COMBINER_IRQ(29, 0)
-#define IRQ_HSMMC1		COMBINER_IRQ(29, 1)
-#define IRQ_HSMMC2		COMBINER_IRQ(29, 2)
-#define IRQ_HSMMC3		COMBINER_IRQ(29, 3)
-
-#define IRQ_MIPI_CSIS0		COMBINER_IRQ(30, 0)
-#define IRQ_MIPI_CSIS1		COMBINER_IRQ(30, 1)
-
-#define IRQ_FIMC0		COMBINER_IRQ(32, 0)
-#define IRQ_FIMC1		COMBINER_IRQ(32, 1)
-#define IRQ_FIMC2		COMBINER_IRQ(33, 0)
-#define IRQ_FIMC3		COMBINER_IRQ(33, 1)
-
-#define IRQ_ONENAND_AUDI	COMBINER_IRQ(34, 0)
-
-#define IRQ_MCT_L1		COMBINER_IRQ(35, 3)
-
-#define IRQ_EINT4		COMBINER_IRQ(37, 0)
-#define IRQ_EINT5		COMBINER_IRQ(37, 1)
-#define IRQ_EINT6		COMBINER_IRQ(37, 2)
-#define IRQ_EINT7		COMBINER_IRQ(37, 3)
-#define IRQ_EINT8		COMBINER_IRQ(38, 0)
-
-#define IRQ_EINT9		COMBINER_IRQ(38, 1)
-#define IRQ_EINT10		COMBINER_IRQ(38, 2)
-#define IRQ_EINT11		COMBINER_IRQ(38, 3)
-#define IRQ_EINT12		COMBINER_IRQ(38, 4)
-#define IRQ_EINT13		COMBINER_IRQ(38, 5)
-#define IRQ_EINT14		COMBINER_IRQ(38, 6)
-#define IRQ_EINT15		COMBINER_IRQ(38, 7)
-
-#define IRQ_EINT16_31		COMBINER_IRQ(39, 0)
-
-#define IRQ_MCT_L0		COMBINER_IRQ(51, 0)
-
-#define IRQ_WDT			COMBINER_IRQ(53, 0)
-#define IRQ_MCT_G0		COMBINER_IRQ(53, 4)
-
-#define MAX_COMBINER_NR		54
+#define IRQ_ADC			IRQ_ADC0
+#define IRQ_TC			IRQ_PEN0
 
 #define S5P_IRQ_EINT_BASE	COMBINER_IRQ(MAX_COMBINER_NR, 0)
 
@@ -155,6 +159,6 @@
 #define IRQ_GPIO_END		(S5P_GPIOINT_BASE + S5P_GPIOINT_COUNT)
 
 /* Set the default NR_IRQS */
-#define NR_IRQS			(IRQ_GPIO_END)
+#define NR_IRQS			(IRQ_GPIO_END + 64)
 
 #endif /* __ASM_ARCH_IRQS_H */
diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h
index 0009e77..d32296d 100644
--- a/arch/arm/mach-exynos4/include/mach/map.h
+++ b/arch/arm/mach-exynos4/include/mach/map.h
@@ -57,12 +57,14 @@
 
 #define EXYNOS4_PA_DMC0			0x10400000
 
-#define EXYNOS4_PA_COMBINER		0x10448000
+#define EXYNOS4_PA_COMBINER		0x10440000
+
+#define EXYNOS4_PA_GIC_CPU		0x10480000
+#define EXYNOS4_PA_GIC_DIST		0x10490000
+#define EXYNOS4_GIC_BANK_OFFSET		0x8000
 
 #define EXYNOS4_PA_COREPERI		0x10500000
-#define EXYNOS4_PA_GIC_CPU		0x10500100
 #define EXYNOS4_PA_TWD			0x10500600
-#define EXYNOS4_PA_GIC_DIST		0x10501000
 #define EXYNOS4_PA_L2CC			0x10502000
 
 #define EXYNOS4_PA_MDMA			0x10810000
@@ -93,7 +95,10 @@
 #define EXYNOS4_PA_MIPI_CSIS0		0x11880000
 #define EXYNOS4_PA_MIPI_CSIS1		0x11890000
 
+#define EXYNOS4_PA_FIMD0		0x11C00000
+
 #define EXYNOS4_PA_HSMMC(x)		(0x12510000 + ((x) * 0x10000))
+#define EXYNOS4_PA_DWMCI		0x12550000
 
 #define EXYNOS4_PA_SATA			0x12560000
 #define EXYNOS4_PA_SATAPHY		0x125D0000
@@ -103,11 +108,15 @@
 
 #define EXYNOS4_PA_EHCI			0x12580000
 #define EXYNOS4_PA_HSPHY		0x125B0000
+#define EXYNOS4_PA_MFC			0x13400000
 
 #define EXYNOS4_PA_UART			0x13800000
 
 #define EXYNOS4_PA_IIC(x)		(0x13860000 + ((x) * 0x10000))
 
+#define EXYNOS4_PA_ADC			0x13910000
+#define EXYNOS4_PA_ADC1			0x13911000
+
 #define EXYNOS4_PA_AC97			0x139A0000
 
 #define EXYNOS4_PA_SPDIF		0x139B0000
@@ -130,6 +139,8 @@
 #define S3C_PA_IIC5			EXYNOS4_PA_IIC(5)
 #define S3C_PA_IIC6			EXYNOS4_PA_IIC(6)
 #define S3C_PA_IIC7			EXYNOS4_PA_IIC(7)
+#define SAMSUNG_PA_ADC			EXYNOS4_PA_ADC
+#define SAMSUNG_PA_ADC1			EXYNOS4_PA_ADC1
 #define S3C_PA_RTC			EXYNOS4_PA_RTC
 #define S3C_PA_WDT			EXYNOS4_PA_WATCHDOG
 
@@ -140,10 +151,12 @@
 #define S5P_PA_FIMC3			EXYNOS4_PA_FIMC3
 #define S5P_PA_MIPI_CSIS0		EXYNOS4_PA_MIPI_CSIS0
 #define S5P_PA_MIPI_CSIS1		EXYNOS4_PA_MIPI_CSIS1
+#define S5P_PA_FIMD0			EXYNOS4_PA_FIMD0
 #define S5P_PA_ONENAND			EXYNOS4_PA_ONENAND
 #define S5P_PA_ONENAND_DMA		EXYNOS4_PA_ONENAND_DMA
 #define S5P_PA_SDRAM			EXYNOS4_PA_SDRAM
 #define S5P_PA_SROMC			EXYNOS4_PA_SROMC
+#define S5P_PA_MFC			EXYNOS4_PA_MFC
 #define S5P_PA_SYSCON			EXYNOS4_PA_SYSCON
 #define S5P_PA_TIMER			EXYNOS4_PA_TIMER
 #define S5P_PA_EHCI			EXYNOS4_PA_EHCI
diff --git a/arch/arm/mach-exynos4/include/mach/pm-core.h b/arch/arm/mach-exynos4/include/mach/pm-core.h
index f26e46b..1df3b81 100644
--- a/arch/arm/mach-exynos4/include/mach/pm-core.h
+++ b/arch/arm/mach-exynos4/include/mach/pm-core.h
@@ -47,3 +47,13 @@
 {
 	/* nothing here yet */
 }
+
+static inline void s3c_pm_restored_gpios(void)
+{
+	/* nothing here yet */
+}
+
+static inline void s3c_pm_saved_gpios(void)
+{
+	/* nothing here yet */
+}
diff --git a/arch/arm/mach-exynos4/include/mach/pmu.h b/arch/arm/mach-exynos4/include/mach/pmu.h
new file mode 100644
index 0000000..a952904
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/pmu.h
@@ -0,0 +1,25 @@
+/* linux/arch/arm/mach-exynos4/include/mach/pmu.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * EXYNOS4210 - PMU(Power Management Unit) support
+ *
+ * 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_ARCH_PMU_H
+#define __ASM_ARCH_PMU_H __FILE__
+
+enum sys_powerdown {
+	SYS_AFTR,
+	SYS_LPA,
+	SYS_SLEEP,
+	NUM_SYS_POWERDOWN,
+};
+
+extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode);
+
+#endif /* __ASM_ARCH_PMU_H */
diff --git a/arch/arm/mach-exynos4/include/mach/regs-audss.h b/arch/arm/mach-exynos4/include/mach/regs-audss.h
new file mode 100644
index 0000000..ca5a8b6
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/regs-audss.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-exynos4/include/mach/regs-audss.h
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ *		http://www.samsung.com
+ *
+ * Exynos4 Audio SubSystem clock register definitions
+ *
+ * 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 __PLAT_REGS_AUDSS_H
+#define __PLAT_REGS_AUDSS_H __FILE__
+
+#define EXYNOS4_AUDSS_INT_MEM	(0x03000000)
+
+#endif /* _PLAT_REGS_AUDSS_H */
diff --git a/arch/arm/mach-exynos4/include/mach/regs-clock.h b/arch/arm/mach-exynos4/include/mach/regs-clock.h
index 6e311c1..d493fdb 100644
--- a/arch/arm/mach-exynos4/include/mach/regs-clock.h
+++ b/arch/arm/mach-exynos4/include/mach/regs-clock.h
@@ -25,6 +25,9 @@
 #define S5P_CLKDIV_STAT_RIGHTBUS	S5P_CLKREG(0x08600)
 #define S5P_CLKGATE_IP_RIGHTBUS		S5P_CLKREG(0x08800)
 
+#define S5P_EPLL_LOCK			S5P_CLKREG(0x0C010)
+#define S5P_VPLL_LOCK			S5P_CLKREG(0x0C020)
+
 #define S5P_EPLL_CON0			S5P_CLKREG(0x0C110)
 #define S5P_EPLL_CON1			S5P_CLKREG(0x0C114)
 #define S5P_VPLL_CON0			S5P_CLKREG(0x0C120)
@@ -33,7 +36,9 @@
 #define S5P_CLKSRC_TOP0			S5P_CLKREG(0x0C210)
 #define S5P_CLKSRC_TOP1			S5P_CLKREG(0x0C214)
 #define S5P_CLKSRC_CAM			S5P_CLKREG(0x0C220)
+#define S5P_CLKSRC_TV			S5P_CLKREG(0x0C224)
 #define S5P_CLKSRC_MFC			S5P_CLKREG(0x0C228)
+#define S5P_CLKSRC_G3D			S5P_CLKREG(0x0C22C)
 #define S5P_CLKSRC_IMAGE		S5P_CLKREG(0x0C230)
 #define S5P_CLKSRC_LCD0			S5P_CLKREG(0x0C234)
 #define S5P_CLKSRC_LCD1			S5P_CLKREG(0x0C238)
@@ -61,6 +66,7 @@
 #define S5P_CLKDIV_PERIL3		S5P_CLKREG(0x0C55C)
 #define S5P_CLKDIV_PERIL4		S5P_CLKREG(0x0C560)
 #define S5P_CLKDIV_PERIL5		S5P_CLKREG(0x0C564)
+#define S5P_CLKDIV2_RATIO		S5P_CLKREG(0x0C580)
 
 #define S5P_CLKSRC_MASK_TOP		S5P_CLKREG(0x0C310)
 #define S5P_CLKSRC_MASK_CAM		S5P_CLKREG(0x0C320)
@@ -120,6 +126,12 @@
 #define S5P_APLL_VAL_1000		((250 << 16) | (6 << 8) | 1)
 #define S5P_APLL_VAL_800		((200 << 16) | (6 << 8) | 1)
 
+#define S5P_EPLLCON0_ENABLE_SHIFT	(31)
+#define S5P_EPLLCON0_LOCKED_SHIFT	(29)
+
+#define S5P_VPLLCON0_ENABLE_SHIFT	(31)
+#define S5P_VPLLCON0_LOCKED_SHIFT	(29)
+
 #define S5P_CLKSRC_CPU_MUXCORE_SHIFT	(16)
 #define S5P_CLKMUX_STATCPU_MUXCORE_MASK	(0x7 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)
 
diff --git a/arch/arm/mach-exynos4/include/mach/regs-pmu.h b/arch/arm/mach-exynos4/include/mach/regs-pmu.h
index a964337..fa49bbb 100644
--- a/arch/arm/mach-exynos4/include/mach/regs-pmu.h
+++ b/arch/arm/mach-exynos4/include/mach/regs-pmu.h
@@ -158,6 +158,7 @@
 #define S5P_PMU_GPS_CONF			S5P_PMUREG(0x3CE0)
 
 #define S5P_PMU_SATA_PHY_CONTROL_EN		0x1
+#define S5P_CORE_LOCAL_PWR_EN			0x3
 #define S5P_INT_LOCAL_PWR_EN			0x7
 
 #define S5P_CHECK_SLEEP				0x00000BAD
diff --git a/arch/arm/mach-exynos4/localtimer.c b/arch/arm/mach-exynos4/localtimer.c
deleted file mode 100644
index 6bf3d0a..0000000
--- a/arch/arm/mach-exynos4/localtimer.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/* linux/arch/arm/mach-exynos4/localtimer.c
- *
- * Cloned from linux/arch/arm/mach-realview/localtimer.c
- *
- *  Copyright (C) 2002 ARM Ltd.
- *  All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/clockchips.h>
-
-#include <asm/irq.h>
-#include <asm/localtimer.h>
-
-/*
- * Setup the local clock events for a CPU.
- */
-int __cpuinit local_timer_setup(struct clock_event_device *evt)
-{
-	evt->irq = IRQ_LOCALTIMER;
-	twd_timer_setup(evt);
-	return 0;
-}
diff --git a/arch/arm/mach-exynos4/mach-nuri.c b/arch/arm/mach-exynos4/mach-nuri.c
index 642702b..43be71b 100644
--- a/arch/arm/mach-exynos4/mach-nuri.c
+++ b/arch/arm/mach-exynos4/mach-nuri.c
@@ -13,10 +13,15 @@
 #include <linux/input.h>
 #include <linux/i2c.h>
 #include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/i2c-gpio.h>
 #include <linux/gpio_keys.h>
 #include <linux/gpio.h>
+#include <linux/power/max8903_charger.h>
+#include <linux/power/max17042_battery.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/fixed.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
 #include <linux/mmc/host.h>
 #include <linux/fb.h>
 #include <linux/pwm_backlight.h>
@@ -26,6 +31,7 @@
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
+#include <plat/adc.h>
 #include <plat/regs-serial.h>
 #include <plat/exynos4.h>
 #include <plat/cpu.h>
@@ -35,6 +41,8 @@
 #include <plat/clock.h>
 #include <plat/gpio-cfg.h>
 #include <plat/iic.h>
+#include <plat/mfc.h>
+#include <plat/pd.h>
 
 #include <mach/map.h>
 
@@ -54,6 +62,7 @@
 
 enum fixed_regulator_id {
 	FIXED_REG_ID_MMC = 0,
+	FIXED_REG_ID_MAX8903,
 };
 
 static struct s3c2410_uartcfg nuri_uartcfgs[] __initdata = {
@@ -344,10 +353,730 @@
 	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_UP);
 }
 
-/* GPIO I2C 5 (PMIC) */
-static struct i2c_board_info i2c5_devs[] __initdata = {
-	/* max8997, To be updated */
+static struct regulator_consumer_supply __initdata max8997_ldo1_[] = {
+	REGULATOR_SUPPLY("vdd", "s5p-adc"), /* Used by CPU's ADC drv */
 };
+static struct regulator_consumer_supply __initdata max8997_ldo3_[] = {
+	REGULATOR_SUPPLY("vdd11", "s5p-mipi-csis.0"), /* MIPI */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo4_[] = {
+	REGULATOR_SUPPLY("vdd18", "s5p-mipi-csis.0"), /* MIPI */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo5_[] = {
+	REGULATOR_SUPPLY("vhsic", "modemctl"), /* MODEM */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo7_[] = {
+	REGULATOR_SUPPLY("dig_18", "0-001f"), /* HCD803 */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo8_[] = {
+	REGULATOR_SUPPLY("vusb_d", NULL), /* Used by CPU */
+	REGULATOR_SUPPLY("vdac", NULL), /* Used by CPU */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo11_[] = {
+	REGULATOR_SUPPLY("vcc", "platform-lcd"), /* U804 LVDS */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo12_[] = {
+	REGULATOR_SUPPLY("vddio", "6-003c"), /* HDC802 */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo13_[] = {
+	REGULATOR_SUPPLY("vmmc", "s3c-sdhci.2"), /* TFLASH */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo14_[] = {
+	REGULATOR_SUPPLY("inmotor", "max8997-haptic"),
+};
+static struct regulator_consumer_supply __initdata max8997_ldo15_[] = {
+	REGULATOR_SUPPLY("avdd", "3-004a"), /* Touch Screen */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo16_[] = {
+	REGULATOR_SUPPLY("d_sensor", "0-001f"), /* HDC803 */
+};
+static struct regulator_consumer_supply __initdata max8997_ldo18_[] = {
+	REGULATOR_SUPPLY("vdd", "3-004a"), /* Touch Screen */
+};
+static struct regulator_consumer_supply __initdata max8997_buck1_[] = {
+	REGULATOR_SUPPLY("vdd_arm", NULL), /* CPUFREQ */
+};
+static struct regulator_consumer_supply __initdata max8997_buck2_[] = {
+	REGULATOR_SUPPLY("vdd_int", NULL), /* CPUFREQ */
+};
+static struct regulator_consumer_supply __initdata max8997_buck3_[] = {
+	REGULATOR_SUPPLY("vdd", "mali_dev.0"), /* G3D of Exynos 4 */
+};
+static struct regulator_consumer_supply __initdata max8997_buck4_[] = {
+	REGULATOR_SUPPLY("core", "0-001f"), /* HDC803 */
+};
+static struct regulator_consumer_supply __initdata max8997_buck6_[] = {
+	REGULATOR_SUPPLY("dig_28", "0-001f"), /* pin "7" of HDC803 */
+};
+static struct regulator_consumer_supply __initdata max8997_esafeout1_[] = {
+	REGULATOR_SUPPLY("usb_vbus", NULL), /* CPU's USB OTG */
+};
+static struct regulator_consumer_supply __initdata max8997_esafeout2_[] = {
+	REGULATOR_SUPPLY("usb_vbus", "modemctl"), /* VBUS of Modem */
+};
+
+static struct regulator_consumer_supply __initdata max8997_charger_[] = {
+	REGULATOR_SUPPLY("vinchg1", "charger-manager.0"),
+};
+static struct regulator_consumer_supply __initdata max8997_chg_toff_[] = {
+	REGULATOR_SUPPLY("vinchg_stop", NULL), /* for jack interrupt handlers */
+};
+
+static struct regulator_consumer_supply __initdata max8997_32khz_ap_[] = {
+	REGULATOR_SUPPLY("gps_clk", "bcm4751"),
+	REGULATOR_SUPPLY("bt_clk", "bcm4330-b1"),
+	REGULATOR_SUPPLY("wifi_clk", "bcm433-b1"),
+};
+
+static struct regulator_init_data __initdata max8997_ldo1_data = {
+	.constraints	= {
+		.name		= "VADC_3.3V_C210",
+		.min_uV		= 3300000,
+		.max_uV		= 3300000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo1_),
+	.consumer_supplies	= max8997_ldo1_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo2_data = {
+	.constraints	= {
+		.name		= "VALIVE_1.1V_C210",
+		.min_uV		= 1100000,
+		.max_uV		= 1100000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_ldo3_data = {
+	.constraints	= {
+		.name		= "VUSB_1.1V_C210",
+		.min_uV		= 1100000,
+		.max_uV		= 1100000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo3_),
+	.consumer_supplies	= max8997_ldo3_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo4_data = {
+	.constraints	= {
+		.name		= "VMIPI_1.8V",
+		.min_uV		= 1800000,
+		.max_uV		= 1800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo4_),
+	.consumer_supplies	= max8997_ldo4_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo5_data = {
+	.constraints	= {
+		.name		= "VHSIC_1.2V_C210",
+		.min_uV		= 1200000,
+		.max_uV		= 1200000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo5_),
+	.consumer_supplies	= max8997_ldo5_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo6_data = {
+	.constraints	= {
+		.name		= "VCC_1.8V_PDA",
+		.min_uV		= 1800000,
+		.max_uV		= 1800000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_ldo7_data = {
+	.constraints	= {
+		.name		= "CAM_ISP_1.8V",
+		.min_uV		= 1800000,
+		.max_uV		= 1800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo7_),
+	.consumer_supplies	= max8997_ldo7_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo8_data = {
+	.constraints	= {
+		.name		= "VUSB/VDAC_3.3V_C210",
+		.min_uV		= 3300000,
+		.max_uV		= 3300000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo8_),
+	.consumer_supplies	= max8997_ldo8_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo9_data = {
+	.constraints	= {
+		.name		= "VCC_2.8V_PDA",
+		.min_uV		= 2800000,
+		.max_uV		= 2800000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_ldo10_data = {
+	.constraints	= {
+		.name		= "VPLL_1.1V_C210",
+		.min_uV		= 1100000,
+		.max_uV		= 1100000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_ldo11_data = {
+	.constraints	= {
+		.name		= "LVDS_VDD3.3V",
+		.min_uV		= 3300000,
+		.max_uV		= 3300000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.boot_on	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo11_),
+	.consumer_supplies	= max8997_ldo11_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo12_data = {
+	.constraints	= {
+		.name		= "VT_CAM_1.8V",
+		.min_uV		= 1800000,
+		.max_uV		= 1800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo12_),
+	.consumer_supplies	= max8997_ldo12_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo13_data = {
+	.constraints	= {
+		.name		= "VTF_2.8V",
+		.min_uV		= 2800000,
+		.max_uV		= 2800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo13_),
+	.consumer_supplies	= max8997_ldo13_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo14_data = {
+	.constraints	= {
+		.name		= "VCC_3.0V_MOTOR",
+		.min_uV		= 3000000,
+		.max_uV		= 3000000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo14_),
+	.consumer_supplies	= max8997_ldo14_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo15_data = {
+	.constraints	= {
+		.name		= "VTOUCH_ADVV2.8V",
+		.min_uV		= 2800000,
+		.max_uV		= 2800000,
+		.apply_uV	= 1,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo15_),
+	.consumer_supplies	= max8997_ldo15_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo16_data = {
+	.constraints	= {
+		.name		= "CAM_SENSOR_IO_1.8V",
+		.min_uV		= 1800000,
+		.max_uV		= 1800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo16_),
+	.consumer_supplies	= max8997_ldo16_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo18_data = {
+	.constraints	= {
+		.name		= "VTOUCH_VDD2.8V",
+		.min_uV		= 2800000,
+		.max_uV		= 2800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.apply_uV	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_ldo18_),
+	.consumer_supplies	= max8997_ldo18_,
+};
+
+static struct regulator_init_data __initdata max8997_ldo21_data = {
+	.constraints	= {
+		.name		= "VDDQ_M1M2_1.2V",
+		.min_uV		= 1200000,
+		.max_uV		= 1200000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_buck1_data = {
+	.constraints	= {
+		.name		= "VARM_1.2V_C210",
+		.min_uV		= 900000,
+		.max_uV		= 1350000,
+		.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE,
+		.always_on	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_buck1_),
+	.consumer_supplies = max8997_buck1_,
+};
+
+static struct regulator_init_data __initdata max8997_buck2_data = {
+	.constraints	= {
+		.name		= "VINT_1.1V_C210",
+		.min_uV		= 900000,
+		.max_uV		= 1100000,
+		.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE,
+		.always_on	= 1,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_buck2_),
+	.consumer_supplies = max8997_buck2_,
+};
+
+static struct regulator_init_data __initdata max8997_buck3_data = {
+	.constraints	= {
+		.name		= "VG3D_1.1V_C210",
+		.min_uV		= 900000,
+		.max_uV		= 1100000,
+		.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE |
+				  REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_buck3_),
+	.consumer_supplies = max8997_buck3_,
+};
+
+static struct regulator_init_data __initdata max8997_buck4_data = {
+	.constraints	= {
+		.name		= "CAM_ISP_CORE_1.2V",
+		.min_uV		= 1200000,
+		.max_uV		= 1200000,
+		.apply_uV	= 1,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_buck4_),
+	.consumer_supplies = max8997_buck4_,
+};
+
+static struct regulator_init_data __initdata max8997_buck5_data = {
+	.constraints	= {
+		.name		= "VMEM_1.2V_C210",
+		.min_uV		= 1200000,
+		.max_uV		= 1200000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_buck6_data = {
+	.constraints	= {
+		.name		= "CAM_AF_2.8V",
+		.min_uV		= 2800000,
+		.max_uV		= 2800000,
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_buck6_),
+	.consumer_supplies = max8997_buck6_,
+};
+
+static struct regulator_init_data __initdata max8997_buck7_data = {
+	.constraints	= {
+		.name		= "VCC_SUB_2.0V",
+		.min_uV		= 2000000,
+		.max_uV		= 2000000,
+		.apply_uV	= 1,
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_32khz_ap_data = {
+	.constraints	= {
+		.name		= "32KHz AP",
+		.always_on	= 1,
+		.state_mem	= {
+			.enabled	= 1,
+		},
+	},
+	.num_consumer_supplies = ARRAY_SIZE(max8997_32khz_ap_),
+	.consumer_supplies = max8997_32khz_ap_,
+};
+
+static struct regulator_init_data __initdata max8997_32khz_cp_data = {
+	.constraints	= {
+		.name		= "32KHz CP",
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_vichg_data = {
+	.constraints	= {
+		.name		= "VICHG",
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+};
+
+static struct regulator_init_data __initdata max8997_esafeout1_data = {
+	.constraints	= {
+		.name		= "SAFEOUT1",
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_esafeout1_),
+	.consumer_supplies	= max8997_esafeout1_,
+};
+
+static struct regulator_init_data __initdata max8997_esafeout2_data = {
+	.constraints	= {
+		.name		= "SAFEOUT2",
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+		.state_mem	= {
+			.disabled	= 1,
+		},
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_esafeout2_),
+	.consumer_supplies	= max8997_esafeout2_,
+};
+
+static struct regulator_init_data __initdata max8997_charger_cv_data = {
+	.constraints	= {
+		.name		= "CHARGER_CV",
+		.min_uV		= 4200000,
+		.max_uV		= 4200000,
+		.apply_uV	= 1,
+	},
+};
+
+static struct regulator_init_data __initdata max8997_charger_data = {
+	.constraints	= {
+		.name		= "CHARGER",
+		.min_uA		= 200000,
+		.max_uA		= 950000,
+		.boot_on	= 1,
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_CURRENT,
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_charger_),
+	.consumer_supplies	= max8997_charger_,
+};
+
+static struct regulator_init_data __initdata max8997_charger_topoff_data = {
+	.constraints	= {
+		.name		= "CHARGER TOPOFF",
+		.min_uA		= 50000,
+		.max_uA		= 200000,
+		.valid_ops_mask = REGULATOR_CHANGE_CURRENT,
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(max8997_chg_toff_),
+	.consumer_supplies	= max8997_chg_toff_,
+};
+
+static struct max8997_regulator_data __initdata nuri_max8997_regulators[] = {
+	{ MAX8997_LDO1, &max8997_ldo1_data },
+	{ MAX8997_LDO2, &max8997_ldo2_data },
+	{ MAX8997_LDO3, &max8997_ldo3_data },
+	{ MAX8997_LDO4, &max8997_ldo4_data },
+	{ MAX8997_LDO5, &max8997_ldo5_data },
+	{ MAX8997_LDO6, &max8997_ldo6_data },
+	{ MAX8997_LDO7, &max8997_ldo7_data },
+	{ MAX8997_LDO8, &max8997_ldo8_data },
+	{ MAX8997_LDO9, &max8997_ldo9_data },
+	{ MAX8997_LDO10, &max8997_ldo10_data },
+	{ MAX8997_LDO11, &max8997_ldo11_data },
+	{ MAX8997_LDO12, &max8997_ldo12_data },
+	{ MAX8997_LDO13, &max8997_ldo13_data },
+	{ MAX8997_LDO14, &max8997_ldo14_data },
+	{ MAX8997_LDO15, &max8997_ldo15_data },
+	{ MAX8997_LDO16, &max8997_ldo16_data },
+
+	{ MAX8997_LDO18, &max8997_ldo18_data },
+	{ MAX8997_LDO21, &max8997_ldo21_data },
+
+	{ MAX8997_BUCK1, &max8997_buck1_data },
+	{ MAX8997_BUCK2, &max8997_buck2_data },
+	{ MAX8997_BUCK3, &max8997_buck3_data },
+	{ MAX8997_BUCK4, &max8997_buck4_data },
+	{ MAX8997_BUCK5, &max8997_buck5_data },
+	{ MAX8997_BUCK6, &max8997_buck6_data },
+	{ MAX8997_BUCK7, &max8997_buck7_data },
+
+	{ MAX8997_EN32KHZ_AP, &max8997_32khz_ap_data },
+	{ MAX8997_EN32KHZ_CP, &max8997_32khz_cp_data },
+
+	{ MAX8997_ENVICHG, &max8997_vichg_data },
+	{ MAX8997_ESAFEOUT1, &max8997_esafeout1_data },
+	{ MAX8997_ESAFEOUT2, &max8997_esafeout2_data },
+	{ MAX8997_CHARGER_CV, &max8997_charger_cv_data },
+	{ MAX8997_CHARGER, &max8997_charger_data },
+	{ MAX8997_CHARGER_TOPOFF, &max8997_charger_topoff_data },
+};
+
+static struct max8997_platform_data __initdata nuri_max8997_pdata = {
+	.wakeup			= 1,
+
+	.num_regulators		= ARRAY_SIZE(nuri_max8997_regulators),
+	.regulators		= nuri_max8997_regulators,
+
+	.buck125_gpios = { EXYNOS4_GPX0(5), EXYNOS4_GPX0(6), EXYNOS4_GPL0(0) },
+	.buck2_gpiodvs = true,
+
+	.buck1_voltage[0] = 1350000, /* 1.35V */
+	.buck1_voltage[1] = 1300000, /* 1.3V */
+	.buck1_voltage[2] = 1250000, /* 1.25V */
+	.buck1_voltage[3] = 1200000, /* 1.2V */
+	.buck1_voltage[4] = 1150000, /* 1.15V */
+	.buck1_voltage[5] = 1100000, /* 1.1V */
+	.buck1_voltage[6] = 1000000, /* 1.0V */
+	.buck1_voltage[7] = 950000, /* 0.95V */
+
+	.buck2_voltage[0] = 1100000, /* 1.1V */
+	.buck2_voltage[1] = 1000000, /* 1.0V */
+	.buck2_voltage[2] = 950000, /* 0.95V */
+	.buck2_voltage[3] = 900000, /* 0.9V */
+	.buck2_voltage[4] = 1100000, /* 1.1V */
+	.buck2_voltage[5] = 1000000, /* 1.0V */
+	.buck2_voltage[6] = 950000, /* 0.95V */
+	.buck2_voltage[7] = 900000, /* 0.9V */
+
+	.buck5_voltage[0] = 1200000, /* 1.2V */
+	.buck5_voltage[1] = 1200000, /* 1.2V */
+	.buck5_voltage[2] = 1200000, /* 1.2V */
+	.buck5_voltage[3] = 1200000, /* 1.2V */
+	.buck5_voltage[4] = 1200000, /* 1.2V */
+	.buck5_voltage[5] = 1200000, /* 1.2V */
+	.buck5_voltage[6] = 1200000, /* 1.2V */
+	.buck5_voltage[7] = 1200000, /* 1.2V */
+};
+
+/* GPIO I2C 5 (PMIC) */
+enum { I2C5_MAX8997 };
+static struct i2c_board_info i2c5_devs[] __initdata = {
+	[I2C5_MAX8997] = {
+		I2C_BOARD_INFO("max8997", 0xCC >> 1),
+		.platform_data	= &nuri_max8997_pdata,
+	},
+};
+
+static struct max17042_platform_data nuri_battery_platform_data = {
+};
+
+/* GPIO I2C 9 (Fuel Gauge) */
+static struct i2c_gpio_platform_data i2c9_gpio_data = {
+	.sda_pin		= EXYNOS4_GPY4(0),      /* XM0ADDR_8 */
+	.scl_pin		= EXYNOS4_GPY4(1),      /* XM0ADDR_9 */
+};
+static struct platform_device i2c9_gpio = {
+	.name			= "i2c-gpio",
+	.id			= 9,
+	.dev			= {
+		.platform_data	= &i2c9_gpio_data,
+	},
+};
+enum { I2C9_MAX17042};
+static struct i2c_board_info i2c9_devs[] __initdata = {
+	[I2C9_MAX17042] = {
+		I2C_BOARD_INFO("max17042", 0x36),
+		.platform_data = &nuri_battery_platform_data,
+	},
+};
+
+/* MAX8903 Secondary Charger */
+static struct regulator_consumer_supply supplies_max8903[] = {
+	REGULATOR_SUPPLY("vinchg2", "charger-manager.0"),
+};
+
+static struct regulator_init_data max8903_charger_en_data = {
+	.constraints = {
+		.name		= "VOUT_CHARGER",
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		.boot_on	= 1,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(supplies_max8903),
+	.consumer_supplies = supplies_max8903,
+};
+
+static struct fixed_voltage_config max8903_charger_en = {
+	.supply_name = "VOUT_CHARGER",
+	.microvolts = 5000000, /* Assume 5VDC */
+	.gpio = EXYNOS4_GPY4(5), /* TA_EN negaged */
+	.enable_high = 0, /* Enable = Low */
+	.enabled_at_boot = 1,
+	.init_data = &max8903_charger_en_data,
+};
+
+static struct platform_device max8903_fixed_reg_dev = {
+	.name = "reg-fixed-voltage",
+	.id = FIXED_REG_ID_MAX8903,
+	.dev = { .platform_data	= &max8903_charger_en },
+};
+
+static struct max8903_pdata nuri_max8903 = {
+	/*
+	 * cen: don't control with the driver, let it be
+	 * controlled by regulator above
+	 */
+	.dok = EXYNOS4_GPX1(4), /* TA_nCONNECTED */
+	/* uok, usus: not connected */
+	.chg = EXYNOS4_GPE2(0), /* TA_nCHG */
+	/* flt: vcc_1.8V_pda */
+	.dcm = EXYNOS4_GPL0(1), /* CURR_ADJ */
+
+	.dc_valid = true,
+	.usb_valid = false, /* USB is not wired to MAX8903 */
+};
+
+static struct platform_device nuri_max8903_device = {
+	.name			= "max8903-charger",
+	.dev			= {
+		.platform_data	= &nuri_max8903,
+	},
+};
+
+static struct device *nuri_cm_devices[] = {
+	&s3c_device_i2c5.dev,
+	&s3c_device_adc.dev,
+	NULL, /* Reserved for UART */
+	NULL,
+};
+
+static void __init nuri_power_init(void)
+{
+	int gpio;
+	int irq_base = IRQ_GPIO_END + 1;
+	int ta_en = 0;
+
+	nuri_max8997_pdata.irq_base = irq_base;
+	irq_base += MAX8997_IRQ_NR;
+
+	gpio = EXYNOS4_GPX0(7);
+	gpio_request(gpio, "AP_PMIC_IRQ");
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+
+	gpio = EXYNOS4_GPX2(3);
+	gpio_request(gpio, "FUEL_ALERT");
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+
+	gpio = nuri_max8903.dok;
+	gpio_request(gpio, "TA_nCONNECTED");
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+	ta_en = gpio_get_value(gpio) ? 0 : 1;
+
+	gpio = nuri_max8903.chg;
+	gpio_request(gpio, "TA_nCHG");
+	gpio_direction_input(gpio);
+
+	gpio = nuri_max8903.dcm;
+	gpio_request(gpio, "CURR_ADJ");
+	gpio_direction_output(gpio, ta_en);
+}
 
 /* USB EHCI */
 static struct s5p_ehci_platdata nuri_ehci_pdata;
@@ -361,6 +1090,7 @@
 
 static struct platform_device *nuri_devices[] __initdata = {
 	/* Samsung Platform Devices */
+	&s3c_device_i2c5, /* PMIC should initialize first */
 	&emmc_fixed_voltage,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc2,
@@ -369,11 +1099,20 @@
 	&s3c_device_timer[0],
 	&s5p_device_ehci,
 	&s3c_device_i2c3,
+	&i2c9_gpio,
+	&s3c_device_adc,
+	&s3c_device_rtc,
+	&s5p_device_mfc,
+	&s5p_device_mfc_l,
+	&s5p_device_mfc_r,
+	&exynos4_device_pd[PD_MFC],
 
 	/* NURI Devices */
 	&nuri_gpio_keys,
 	&nuri_lcd_device,
 	&nuri_backlight_device,
+	&max8903_fixed_reg_dev,
+	&nuri_max8903_device,
 };
 
 static void __init nuri_map_io(void)
@@ -383,21 +1122,32 @@
 	s3c24xx_init_uarts(nuri_uartcfgs, ARRAY_SIZE(nuri_uartcfgs));
 }
 
+static void __init nuri_reserve(void)
+{
+	s5p_mfc_reserve_mem(0x43000000, 8 << 20, 0x51000000, 8 << 20);
+}
+
 static void __init nuri_machine_init(void)
 {
 	nuri_sdhci_init();
 	nuri_tsp_init();
+	nuri_power_init();
 
 	i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));
 	s3c_i2c3_set_platdata(&i2c3_data);
 	i2c_register_board_info(3, i2c3_devs, ARRAY_SIZE(i2c3_devs));
+	s3c_i2c5_set_platdata(NULL);
+	i2c5_devs[I2C5_MAX8997].irq = gpio_to_irq(EXYNOS4_GPX0(7));
 	i2c_register_board_info(5, i2c5_devs, ARRAY_SIZE(i2c5_devs));
+	i2c9_devs[I2C9_MAX17042].irq = gpio_to_irq(EXYNOS4_GPX2(3));
+	i2c_register_board_info(9, i2c9_devs, ARRAY_SIZE(i2c9_devs));
 
 	nuri_ehci_init();
 	clk_xusbxti.rate = 24000000;
 
 	/* Last */
 	platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices));
+	s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
 }
 
 MACHINE_START(NURI, "NURI")
@@ -407,4 +1157,5 @@
 	.map_io		= nuri_map_io,
 	.init_machine	= nuri_machine_init,
 	.timer		= &exynos4_timer,
+	.reserve        = &nuri_reserve,
 MACHINE_END
diff --git a/arch/arm/mach-exynos4/mach-smdkc210.c b/arch/arm/mach-exynos4/mach-smdkc210.c
index f606ea7..a7c65e0 100644
--- a/arch/arm/mach-exynos4/mach-smdkc210.c
+++ b/arch/arm/mach-exynos4/mach-smdkc210.c
@@ -9,7 +9,9 @@
 */
 
 #include <linux/serial_core.h>
+#include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/lcd.h>
 #include <linux/mmc/host.h>
 #include <linux/platform_device.h>
 #include <linux/smsc911x.h>
@@ -20,11 +22,15 @@
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
+#include <video/platform_lcd.h>
+
 #include <plat/regs-serial.h>
 #include <plat/regs-srom.h>
+#include <plat/regs-fb-v4.h>
 #include <plat/exynos4.h>
 #include <plat/cpu.h>
 #include <plat/devs.h>
+#include <plat/fb.h>
 #include <plat/sdhci.h>
 #include <plat/iic.h>
 #include <plat/pd.h>
@@ -114,6 +120,67 @@
 	.clk_type		= S3C_SDHCI_CLK_DIV_EXTERNAL,
 };
 
+static void lcd_lte480wv_set_power(struct plat_lcd_data *pd,
+				   unsigned int power)
+{
+	if (power) {
+#if !defined(CONFIG_BACKLIGHT_PWM)
+		gpio_request_one(EXYNOS4_GPD0(1), GPIOF_OUT_INIT_HIGH, "GPD0");
+		gpio_free(EXYNOS4_GPD0(1));
+#endif
+		/* fire nRESET on power up */
+		gpio_request(EXYNOS4_GPX0(6), "GPX0");
+
+		gpio_direction_output(EXYNOS4_GPX0(6), 1);
+		mdelay(100);
+
+		gpio_set_value(EXYNOS4_GPX0(6), 0);
+		mdelay(10);
+
+		gpio_set_value(EXYNOS4_GPX0(6), 1);
+		mdelay(10);
+
+		gpio_free(EXYNOS4_GPX0(6));
+	} else {
+#if !defined(CONFIG_BACKLIGHT_PWM)
+		gpio_request_one(EXYNOS4_GPD0(1), GPIOF_OUT_INIT_LOW, "GPD0");
+		gpio_free(EXYNOS4_GPD0(1));
+#endif
+	}
+}
+
+static struct plat_lcd_data smdkc210_lcd_lte480wv_data = {
+	.set_power		= lcd_lte480wv_set_power,
+};
+
+static struct platform_device smdkc210_lcd_lte480wv = {
+	.name			= "platform-lcd",
+	.dev.parent		= &s5p_device_fimd0.dev,
+	.dev.platform_data	= &smdkc210_lcd_lte480wv_data,
+};
+
+static struct s3c_fb_pd_win smdkc210_fb_win0 = {
+	.win_mode = {
+		.left_margin	= 13,
+		.right_margin	= 8,
+		.upper_margin	= 7,
+		.lower_margin	= 5,
+		.hsync_len	= 3,
+		.vsync_len	= 1,
+		.xres		= 800,
+		.yres		= 480,
+	},
+	.max_bpp		= 32,
+	.default_bpp		= 24,
+};
+
+static struct s3c_fb_platdata smdkc210_lcd0_pdata __initdata = {
+	.win[0]		= &smdkc210_fb_win0,
+	.vidcon0	= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
+	.vidcon1	= VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
+	.setup_gpio	= exynos4_fimd0_gpio_setup_24bpp,
+};
+
 static struct resource smdkc210_smsc911x_resources[] = {
 	[0] = {
 		.start	= EXYNOS4_PA_SROM_BANK(1),
@@ -168,6 +235,8 @@
 	&exynos4_device_pd[PD_GPS],
 	&exynos4_device_sysmmu,
 	&samsung_asoc_dma,
+	&s5p_device_fimd0,
+	&smdkc210_lcd_lte480wv,
 	&smdkc210_smsc911x,
 };
 
@@ -225,6 +294,7 @@
 	s3c_sdhci3_set_platdata(&smdkc210_hsmmc3_pdata);
 
 	samsung_bl_set(&smdkc210_bl_gpio_info, &smdkc210_bl_data);
+	s5p_fimd0_set_platdata(&smdkc210_lcd0_pdata);
 
 	platform_add_devices(smdkc210_devices, ARRAY_SIZE(smdkc210_devices));
 }
diff --git a/arch/arm/mach-exynos4/mach-smdkv310.c b/arch/arm/mach-exynos4/mach-smdkv310.c
index df11078..ea41495 100644
--- a/arch/arm/mach-exynos4/mach-smdkv310.c
+++ b/arch/arm/mach-exynos4/mach-smdkv310.c
@@ -184,9 +184,12 @@
 	&exynos4_device_pd[PD_CAM],
 	&exynos4_device_pd[PD_TV],
 	&exynos4_device_pd[PD_GPS],
+	&exynos4_device_spdif,
 	&exynos4_device_sysmmu,
 	&samsung_asoc_dma,
+	&samsung_asoc_idma,
 	&smdkv310_smsc911x,
+	&exynos4_device_ahci,
 };
 
 static void __init smdkv310_smsc911x_init(void)
diff --git a/arch/arm/mach-exynos4/mach-universal_c210.c b/arch/arm/mach-exynos4/mach-universal_c210.c
index 97d329f..0e280d1 100644
--- a/arch/arm/mach-exynos4/mach-universal_c210.c
+++ b/arch/arm/mach-exynos4/mach-universal_c210.c
@@ -18,6 +18,9 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/max8952.h>
 #include <linux/mmc/host.h>
+#include <linux/i2c-gpio.h>
+#include <linux/i2c/mcs.h>
+#include <linux/i2c/atmel_mxt_ts.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
@@ -27,7 +30,10 @@
 #include <plat/cpu.h>
 #include <plat/devs.h>
 #include <plat/iic.h>
+#include <plat/gpio-cfg.h>
+#include <plat/mfc.h>
 #include <plat/sdhci.h>
+#include <plat/pd.h>
 
 #include <mach/map.h>
 
@@ -477,6 +483,96 @@
 	},
 };
 
+/* I2C3 (TSP) */
+static struct mxt_platform_data qt602240_platform_data = {
+	.x_line		= 19,
+	.y_line		= 11,
+	.x_size		= 800,
+	.y_size		= 480,
+	.blen		= 0x11,
+	.threshold	= 0x28,
+	.voltage	= 2800000,		/* 2.8V */
+	.orient		= MXT_DIAGONAL,
+};
+
+static struct i2c_board_info i2c3_devs[] __initdata = {
+	{
+		I2C_BOARD_INFO("qt602240_ts", 0x4a),
+		.platform_data = &qt602240_platform_data,
+	},
+};
+
+static void __init universal_tsp_init(void)
+{
+	int gpio;
+
+	/* TSP_LDO_ON: XMDMADDR_11 */
+	gpio = EXYNOS4_GPE2(3);
+	gpio_request(gpio, "TSP_LDO_ON");
+	gpio_direction_output(gpio, 1);
+	gpio_export(gpio, 0);
+
+	/* TSP_INT: XMDMADDR_7 */
+	gpio = EXYNOS4_GPE1(7);
+	gpio_request(gpio, "TSP_INT");
+
+	s5p_register_gpio_interrupt(gpio);
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
+	s3c_gpio_setpull(gpio, S3C_GPIO_PULL_UP);
+	i2c3_devs[0].irq = gpio_to_irq(gpio);
+}
+
+
+/* GPIO I2C 12 (3 Touchkey) */
+static uint32_t touchkey_keymap[] = {
+	/* MCS_KEY_MAP(value, keycode) */
+	MCS_KEY_MAP(0, KEY_MENU),		/* KEY_SEND */
+	MCS_KEY_MAP(1, KEY_BACK),		/* KEY_END */
+};
+
+static struct mcs_platform_data touchkey_data = {
+	.keymap		= touchkey_keymap,
+	.keymap_size	= ARRAY_SIZE(touchkey_keymap),
+	.key_maxval	= 2,
+};
+
+/* GPIO I2C 3_TOUCH 2.8V */
+#define I2C_GPIO_BUS_12		12
+static struct i2c_gpio_platform_data i2c_gpio12_data = {
+	.sda_pin	= EXYNOS4_GPE4(0),	/* XMDMDATA_8 */
+	.scl_pin	= EXYNOS4_GPE4(1),	/* XMDMDATA_9 */
+};
+
+static struct platform_device i2c_gpio12 = {
+	.name		= "i2c-gpio",
+	.id		= I2C_GPIO_BUS_12,
+	.dev		= {
+		.platform_data	= &i2c_gpio12_data,
+	},
+};
+
+static struct i2c_board_info i2c_gpio12_devs[] __initdata = {
+	{
+		I2C_BOARD_INFO("mcs5080_touchkey", 0x20),
+		.platform_data = &touchkey_data,
+	},
+};
+
+static void __init universal_touchkey_init(void)
+{
+	int gpio;
+
+	gpio = EXYNOS4_GPE3(7);			/* XMDMDATA_7 */
+	gpio_request(gpio, "3_TOUCH_INT");
+	s5p_register_gpio_interrupt(gpio);
+	s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
+	i2c_gpio12_devs[0].irq = gpio_to_irq(gpio);
+
+	gpio = EXYNOS4_GPE3(3);			/* XMDMDATA_3 */
+	gpio_request(gpio, "3_TOUCH_EN");
+	gpio_direction_output(gpio, 1);
+}
+
 /* GPIO KEYS */
 static struct gpio_keys_button universal_gpio_keys_tables[] = {
 	{
@@ -608,15 +704,25 @@
 
 static struct platform_device *universal_devices[] __initdata = {
 	/* Samsung Platform Devices */
+	&s5p_device_fimc0,
+	&s5p_device_fimc1,
+	&s5p_device_fimc2,
+	&s5p_device_fimc3,
 	&mmc0_fixed_voltage,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc2,
 	&s3c_device_hsmmc3,
+	&s3c_device_i2c3,
 	&s3c_device_i2c5,
 
 	/* Universal Devices */
+	&i2c_gpio12,
 	&universal_gpio_keys,
 	&s5p_device_onenand,
+	&s5p_device_mfc,
+	&s5p_device_mfc_l,
+	&s5p_device_mfc_r,
+	&exynos4_device_pd[PD_MFC],
 };
 
 static void __init universal_map_io(void)
@@ -626,6 +732,11 @@
 	s3c24xx_init_uarts(universal_uartcfgs, ARRAY_SIZE(universal_uartcfgs));
 }
 
+static void __init universal_reserve(void)
+{
+	s5p_mfc_reserve_mem(0x43000000, 8 << 20, 0x51000000, 8 << 20);
+}
+
 static void __init universal_machine_init(void)
 {
 	universal_sdhci_init();
@@ -633,11 +744,20 @@
 	i2c_register_board_info(0, i2c0_devs, ARRAY_SIZE(i2c0_devs));
 	i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));
 
+	universal_tsp_init();
+	s3c_i2c3_set_platdata(NULL);
+	i2c_register_board_info(3, i2c3_devs, ARRAY_SIZE(i2c3_devs));
+
 	s3c_i2c5_set_platdata(NULL);
 	i2c_register_board_info(5, i2c5_devs, ARRAY_SIZE(i2c5_devs));
 
+	universal_touchkey_init();
+	i2c_register_board_info(I2C_GPIO_BUS_12, i2c_gpio12_devs,
+			ARRAY_SIZE(i2c_gpio12_devs));
+
 	/* Last */
 	platform_add_devices(universal_devices, ARRAY_SIZE(universal_devices));
+	s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
 }
 
 MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210")
@@ -647,4 +767,5 @@
 	.map_io		= universal_map_io,
 	.init_machine	= universal_machine_init,
 	.timer		= &exynos4_timer,
+	.reserve        = &universal_reserve,
 MACHINE_END
diff --git a/arch/arm/mach-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
index 14ac10b..1ae059b 100644
--- a/arch/arm/mach-exynos4/mct.c
+++ b/arch/arm/mach-exynos4/mct.c
@@ -383,8 +383,8 @@
 		setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
 	} else {
 		mct_tick1_event_irq.dev_id = &mct_tick[cpu];
-		irq_set_affinity(IRQ_MCT1, cpumask_of(1));
 		setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
+		irq_set_affinity(IRQ_MCT_L1, cpumask_of(1));
 	}
 }
 
diff --git a/arch/arm/mach-exynos4/platsmp.c b/arch/arm/mach-exynos4/platsmp.c
index b68d5bd..7c2282c 100644
--- a/arch/arm/mach-exynos4/platsmp.c
+++ b/arch/arm/mach-exynos4/platsmp.c
@@ -28,9 +28,12 @@
 
 #include <mach/hardware.h>
 #include <mach/regs-clock.h>
+#include <mach/regs-pmu.h>
 
 extern void exynos4_secondary_startup(void);
 
+#define CPU1_BOOT_REG S5P_VA_SYSRAM
+
 /*
  * control for which core is the next to come out of the secondary
  * boot "holding pen"
@@ -58,6 +61,31 @@
 
 static DEFINE_SPINLOCK(boot_lock);
 
+static void __cpuinit exynos4_gic_secondary_init(void)
+{
+	void __iomem *dist_base = S5P_VA_GIC_DIST +
+				 (EXYNOS4_GIC_BANK_OFFSET * smp_processor_id());
+	void __iomem *cpu_base = S5P_VA_GIC_CPU +
+				(EXYNOS4_GIC_BANK_OFFSET * smp_processor_id());
+	int i;
+
+	/*
+	 * Deal with the banked PPI and SGI interrupts - disable all
+	 * PPI interrupts, ensure all SGI interrupts are enabled.
+	 */
+	__raw_writel(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
+	__raw_writel(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
+
+	/*
+	 * Set priority on PPI and SGI interrupts
+	 */
+	for (i = 0; i < 32; i += 4)
+		__raw_writel(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
+
+	__raw_writel(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	__raw_writel(1, cpu_base + GIC_CPU_CTRL);
+}
+
 void __cpuinit platform_secondary_init(unsigned int cpu)
 {
 	/*
@@ -65,7 +93,7 @@
 	 * core (e.g. timer irq), then they will not have been enabled
 	 * for us: do so
 	 */
-	gic_secondary_init(0);
+	exynos4_gic_secondary_init();
 
 	/*
 	 * let the primary processor know we're out of the
@@ -100,16 +128,41 @@
 	 */
 	write_pen_release(cpu);
 
+	if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
+		__raw_writel(S5P_CORE_LOCAL_PWR_EN,
+			     S5P_ARM_CORE1_CONFIGURATION);
+
+		timeout = 10;
+
+		/* wait max 10 ms until cpu1 is on */
+		while ((__raw_readl(S5P_ARM_CORE1_STATUS)
+			& S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
+			if (timeout-- == 0)
+				break;
+
+			mdelay(1);
+		}
+
+		if (timeout == 0) {
+			printk(KERN_ERR "cpu1 power enable failed");
+			spin_unlock(&boot_lock);
+			return -ETIMEDOUT;
+		}
+	}
 	/*
 	 * Send the secondary CPU a soft interrupt, thereby causing
 	 * the boot monitor to read the system wide flags register,
 	 * and branch to the address found there.
 	 */
-	gic_raise_softirq(cpumask_of(cpu), 1);
 
 	timeout = jiffies + (1 * HZ);
 	while (time_before(jiffies, timeout)) {
 		smp_rmb();
+
+		__raw_writel(BSYM(virt_to_phys(exynos4_secondary_startup)),
+			CPU1_BOOT_REG);
+		gic_raise_softirq(cpumask_of(cpu), 1);
+
 		if (pen_release == -1)
 			break;
 
diff --git a/arch/arm/mach-exynos4/pm.c b/arch/arm/mach-exynos4/pm.c
index 533c28f..bc6ca94 100644
--- a/arch/arm/mach-exynos4/pm.c
+++ b/arch/arm/mach-exynos4/pm.c
@@ -18,92 +18,23 @@
 #include <linux/suspend.h>
 #include <linux/syscore_ops.h>
 #include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
 
 #include <asm/cacheflush.h>
 #include <asm/hardware/cache-l2x0.h>
 
 #include <plat/cpu.h>
 #include <plat/pm.h>
+#include <plat/pll.h>
+#include <plat/regs-srom.h>
 
 #include <mach/regs-irq.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-clock.h>
 #include <mach/regs-pmu.h>
 #include <mach/pm-core.h>
-
-static struct sleep_save exynos4_sleep[] = {
-	{ .reg = S5P_ARM_CORE0_LOWPWR			, .val = 0x2, },
-	{ .reg = S5P_DIS_IRQ_CORE0			, .val = 0x0, },
-	{ .reg = S5P_DIS_IRQ_CENTRAL0			, .val = 0x0, },
-	{ .reg = S5P_ARM_CORE1_LOWPWR			, .val = 0x2, },
-	{ .reg = S5P_DIS_IRQ_CORE1			, .val = 0x0, },
-	{ .reg = S5P_DIS_IRQ_CENTRAL1			, .val = 0x0, },
-	{ .reg = S5P_ARM_COMMON_LOWPWR			, .val = 0x2, },
-	{ .reg = S5P_L2_0_LOWPWR			, .val = 0x3, },
-	{ .reg = S5P_L2_1_LOWPWR			, .val = 0x3, },
-	{ .reg = S5P_CMU_ACLKSTOP_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_SCLKSTOP_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_APLL_SYSCLK_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_MPLL_SYSCLK_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_VPLL_SYSCLK_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_EPLL_SYSCLK_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR	, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_GPSALIVE_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_CAM_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_TV_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_MFC_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_G3D_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_LCD0_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_LCD1_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_MAUDIO_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_CLKSTOP_GPS_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_CAM_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_TV_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_MFC_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_G3D_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_LCD0_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_LCD1_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_MAUDIO_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CMU_RESET_GPS_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_TOP_BUS_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_TOP_RETENTION_LOWPWR		, .val = 0x1, },
-	{ .reg = S5P_TOP_PWR_LOWPWR			, .val = 0x3, },
-	{ .reg = S5P_LOGIC_RESET_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_ONENAND_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_MODIMIF_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_G2D_ACP_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_USBOTG_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_HSMMC_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_CSSYS_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_SECSS_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_PCIE_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_SATA_MEM_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_DRAM_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_MAUDIO_LOWPWR	, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_GPIO_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_UART_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_MMCA_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_MMCB_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_EBIA_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_EBIB_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_ISOLATION_LOWPWR	, .val = 0x0, },
-	{ .reg = S5P_PAD_RETENTION_ALV_SEL_LOWPWR	, .val = 0x0, },
-	{ .reg = S5P_XUSBXTI_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_XXTI_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_EXT_REGULATOR_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_GPIO_MODE_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_GPIO_MODE_MAUDIO_LOWPWR		, .val = 0x0, },
-	{ .reg = S5P_CAM_LOWPWR				, .val = 0x0, },
-	{ .reg = S5P_TV_LOWPWR				, .val = 0x0, },
-	{ .reg = S5P_MFC_LOWPWR				, .val = 0x0, },
-	{ .reg = S5P_G3D_LOWPWR				, .val = 0x0, },
-	{ .reg = S5P_LCD0_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_LCD1_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_MAUDIO_LOWPWR			, .val = 0x0, },
-	{ .reg = S5P_GPS_LOWPWR				, .val = 0x0, },
-	{ .reg = S5P_GPS_ALIVE_LOWPWR			, .val = 0x0, },
-};
+#include <mach/pmu.h>
 
 static struct sleep_save exynos4_set_clksrc[] = {
 	{ .reg = S5P_CLKSRC_MASK_TOP			, .val = 0x00000001, },
@@ -118,20 +49,28 @@
 	{ .reg = S5P_CLKSRC_MASK_DMC			, .val = 0x00010000, },
 };
 
+static struct sleep_save exynos4_epll_save[] = {
+	SAVE_ITEM(S5P_EPLL_CON0),
+	SAVE_ITEM(S5P_EPLL_CON1),
+};
+
+static struct sleep_save exynos4_vpll_save[] = {
+	SAVE_ITEM(S5P_VPLL_CON0),
+	SAVE_ITEM(S5P_VPLL_CON1),
+};
+
 static struct sleep_save exynos4_core_save[] = {
 	/* CMU side */
 	SAVE_ITEM(S5P_CLKDIV_LEFTBUS),
 	SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS),
 	SAVE_ITEM(S5P_CLKDIV_RIGHTBUS),
 	SAVE_ITEM(S5P_CLKGATE_IP_RIGHTBUS),
-	SAVE_ITEM(S5P_EPLL_CON0),
-	SAVE_ITEM(S5P_EPLL_CON1),
-	SAVE_ITEM(S5P_VPLL_CON0),
-	SAVE_ITEM(S5P_VPLL_CON1),
 	SAVE_ITEM(S5P_CLKSRC_TOP0),
 	SAVE_ITEM(S5P_CLKSRC_TOP1),
 	SAVE_ITEM(S5P_CLKSRC_CAM),
+	SAVE_ITEM(S5P_CLKSRC_TV),
 	SAVE_ITEM(S5P_CLKSRC_MFC),
+	SAVE_ITEM(S5P_CLKSRC_G3D),
 	SAVE_ITEM(S5P_CLKSRC_IMAGE),
 	SAVE_ITEM(S5P_CLKSRC_LCD0),
 	SAVE_ITEM(S5P_CLKSRC_LCD1),
@@ -158,6 +97,7 @@
 	SAVE_ITEM(S5P_CLKDIV_PERIL4),
 	SAVE_ITEM(S5P_CLKDIV_PERIL5),
 	SAVE_ITEM(S5P_CLKDIV_TOP),
+	SAVE_ITEM(S5P_CLKSRC_MASK_TOP),
 	SAVE_ITEM(S5P_CLKSRC_MASK_CAM),
 	SAVE_ITEM(S5P_CLKSRC_MASK_TV),
 	SAVE_ITEM(S5P_CLKSRC_MASK_LCD0),
@@ -166,6 +106,7 @@
 	SAVE_ITEM(S5P_CLKSRC_MASK_FSYS),
 	SAVE_ITEM(S5P_CLKSRC_MASK_PERIL0),
 	SAVE_ITEM(S5P_CLKSRC_MASK_PERIL1),
+	SAVE_ITEM(S5P_CLKDIV2_RATIO),
 	SAVE_ITEM(S5P_CLKGATE_SCLKCAM),
 	SAVE_ITEM(S5P_CLKGATE_IP_CAM),
 	SAVE_ITEM(S5P_CLKGATE_IP_TV),
@@ -186,8 +127,10 @@
 	SAVE_ITEM(S5P_CLKGATE_IP_DMC),
 	SAVE_ITEM(S5P_CLKSRC_CPU),
 	SAVE_ITEM(S5P_CLKDIV_CPU),
+	SAVE_ITEM(S5P_CLKDIV_CPU + 0x4),
 	SAVE_ITEM(S5P_CLKGATE_SCLKCPU),
 	SAVE_ITEM(S5P_CLKGATE_IP_CPU),
+
 	/* GIC side */
 	SAVE_ITEM(S5P_VA_GIC_CPU + 0x000),
 	SAVE_ITEM(S5P_VA_GIC_CPU + 0x004),
@@ -270,6 +213,13 @@
 	SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070),
 	SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080),
 	SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090),
+
+	/* SROM side */
+	SAVE_ITEM(S5P_SROM_BW),
+	SAVE_ITEM(S5P_SROM_BC0),
+	SAVE_ITEM(S5P_SROM_BC1),
+	SAVE_ITEM(S5P_SROM_BC2),
+	SAVE_ITEM(S5P_SROM_BC3),
 };
 
 static struct sleep_save exynos4_l2cc_save[] = {
@@ -280,37 +230,11 @@
 	SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL),
 };
 
+/* For Cortex-A9 Diagnostic and Power control register */
+static unsigned int save_arm_register[2];
+
 static int exynos4_cpu_suspend(unsigned long arg)
 {
-	unsigned long tmp;
-	unsigned long mask = 0xFFFFFFFF;
-
-	/* Setting Central Sequence Register for power down mode */
-
-	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
-	tmp &= ~(S5P_CENTRAL_LOWPWR_CFG);
-	__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
-
-	/* Setting Central Sequence option Register */
-
-	tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION);
-	tmp &= ~(S5P_USE_MASK);
-	tmp |= S5P_USE_STANDBY_WFI0;
-	__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
-
-	/* Clear all interrupt pending to avoid early wakeup */
-
-	__raw_writel(mask, (S5P_VA_GIC_DIST + 0x280));
-	__raw_writel(mask, (S5P_VA_GIC_DIST + 0x284));
-	__raw_writel(mask, (S5P_VA_GIC_DIST + 0x288));
-
-	/* Disable all interrupt */
-
-	__raw_writel(0x0, (S5P_VA_GIC_CPU + 0x000));
-	__raw_writel(0x0, (S5P_VA_GIC_DIST + 0x000));
-	__raw_writel(mask, (S5P_VA_GIC_DIST + 0x184));
-	__raw_writel(mask, (S5P_VA_GIC_DIST + 0x188));
-
 	outer_flush_all();
 
 	/* issue the standby signal into the pm unit. */
@@ -326,12 +250,14 @@
 
 	s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
 	s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save));
+	s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
+	s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
 
 	tmp = __raw_readl(S5P_INFORM1);
 
 	/* Set value of power down register for sleep mode */
 
-	s3c_pm_do_restore_core(exynos4_sleep, ARRAY_SIZE(exynos4_sleep));
+	exynos4_sys_powerdown_conf(SYS_SLEEP);
 	__raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
 
 	/* ensure at least INFORM0 has the resume address */
@@ -373,12 +299,80 @@
 	flush_cache_all();
 }
 
+static unsigned long pll_base_rate;
+
+static void exynos4_restore_pll(void)
+{
+	unsigned long pll_con, locktime, lockcnt;
+	unsigned long pll_in_rate;
+	unsigned int p_div, epll_wait = 0, vpll_wait = 0;
+
+	if (pll_base_rate == 0)
+		return;
+
+	pll_in_rate = pll_base_rate;
+
+	/* EPLL */
+	pll_con = exynos4_epll_save[0].val;
+
+	if (pll_con & (1 << 31)) {
+		pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT);
+		p_div = (pll_con >> PLL46XX_PDIV_SHIFT);
+
+		pll_in_rate /= 1000000;
+
+		locktime = (3000 / pll_in_rate) * p_div;
+		lockcnt = locktime * 10000 / (10000 / pll_in_rate);
+
+		__raw_writel(lockcnt, S5P_EPLL_LOCK);
+
+		s3c_pm_do_restore_core(exynos4_epll_save,
+					ARRAY_SIZE(exynos4_epll_save));
+		epll_wait = 1;
+	}
+
+	pll_in_rate = pll_base_rate;
+
+	/* VPLL */
+	pll_con = exynos4_vpll_save[0].val;
+
+	if (pll_con & (1 << 31)) {
+		pll_in_rate /= 1000000;
+		/* 750us */
+		locktime = 750;
+		lockcnt = locktime * 10000 / (10000 / pll_in_rate);
+
+		__raw_writel(lockcnt, S5P_VPLL_LOCK);
+
+		s3c_pm_do_restore_core(exynos4_vpll_save,
+					ARRAY_SIZE(exynos4_vpll_save));
+		vpll_wait = 1;
+	}
+
+	/* Wait PLL locking */
+
+	do {
+		if (epll_wait) {
+			pll_con = __raw_readl(S5P_EPLL_CON0);
+			if (pll_con & (1 << S5P_EPLLCON0_LOCKED_SHIFT))
+				epll_wait = 0;
+		}
+
+		if (vpll_wait) {
+			pll_con = __raw_readl(S5P_VPLL_CON0);
+			if (pll_con & (1 << S5P_VPLLCON0_LOCKED_SHIFT))
+				vpll_wait = 0;
+		}
+	} while (epll_wait || vpll_wait);
+}
+
 static struct sysdev_driver exynos4_pm_driver = {
 	.add		= exynos4_pm_add,
 };
 
 static __init int exynos4_pm_drvinit(void)
 {
+	struct clk *pll_base;
 	unsigned int tmp;
 
 	s3c_pm_init();
@@ -389,12 +383,69 @@
 	tmp |= ((0xFF << 8) | (0x1F << 1));
 	__raw_writel(tmp, S5P_WAKEUP_MASK);
 
+	pll_base = clk_get(NULL, "xtal");
+
+	if (!IS_ERR(pll_base)) {
+		pll_base_rate = clk_get_rate(pll_base);
+		clk_put(pll_base);
+	}
+
 	return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver);
 }
 arch_initcall(exynos4_pm_drvinit);
 
+static int exynos4_pm_suspend(void)
+{
+	unsigned long tmp;
+
+	/* Setting Central Sequence Register for power down mode */
+
+	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
+	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
+	__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
+
+	/* Save Power control register */
+	asm ("mrc p15, 0, %0, c15, c0, 0"
+	     : "=r" (tmp) : : "cc");
+	save_arm_register[0] = tmp;
+
+	/* Save Diagnostic register */
+	asm ("mrc p15, 0, %0, c15, c0, 1"
+	     : "=r" (tmp) : : "cc");
+	save_arm_register[1] = tmp;
+
+	return 0;
+}
+
 static void exynos4_pm_resume(void)
 {
+	unsigned long tmp;
+
+	/*
+	 * If PMU failed while entering sleep mode, WFI will be
+	 * ignored by PMU and then exiting cpu_do_idle().
+	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
+	 * in this situation.
+	 */
+	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
+	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
+		tmp |= S5P_CENTRAL_LOWPWR_CFG;
+		__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
+		/* No need to perform below restore code */
+		goto early_wakeup;
+	}
+	/* Restore Power control register */
+	tmp = save_arm_register[0];
+	asm volatile ("mcr p15, 0, %0, c15, c0, 0"
+		      : : "r" (tmp)
+		      : "cc");
+
+	/* Restore Diagnostic register */
+	tmp = save_arm_register[1];
+	asm volatile ("mcr p15, 0, %0, c15, c0, 1"
+		      : : "r" (tmp)
+		      : "cc");
+
 	/* For release retention */
 
 	__raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
@@ -407,6 +458,8 @@
 
 	s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
 
+	exynos4_restore_pll();
+
 	exynos4_scu_enable(S5P_VA_SCU);
 
 #ifdef CONFIG_CACHE_L2X0
@@ -415,9 +468,13 @@
 	/* enable L2X0*/
 	writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL);
 #endif
+
+early_wakeup:
+	return;
 }
 
 static struct syscore_ops exynos4_pm_syscore_ops = {
+	.suspend	= exynos4_pm_suspend,
 	.resume		= exynos4_pm_resume,
 };
 
diff --git a/arch/arm/mach-exynos4/pmu.c b/arch/arm/mach-exynos4/pmu.c
new file mode 100644
index 0000000..7ea9eb2
--- /dev/null
+++ b/arch/arm/mach-exynos4/pmu.c
@@ -0,0 +1,175 @@
+/* linux/arch/arm/mach-exynos4/pmu.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * EXYNOS4210 - CPU PMU(Power Management Unit) support
+ *
+ * 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/kernel.h>
+
+#include <mach/regs-clock.h>
+#include <mach/pmu.h>
+
+static void __iomem *sys_powerdown_reg[] = {
+	S5P_ARM_CORE0_LOWPWR,
+	S5P_DIS_IRQ_CORE0,
+	S5P_DIS_IRQ_CENTRAL0,
+	S5P_ARM_CORE1_LOWPWR,
+	S5P_DIS_IRQ_CORE1,
+	S5P_DIS_IRQ_CENTRAL1,
+	S5P_ARM_COMMON_LOWPWR,
+	S5P_L2_0_LOWPWR,
+	S5P_L2_1_LOWPWR,
+	S5P_CMU_ACLKSTOP_LOWPWR,
+	S5P_CMU_SCLKSTOP_LOWPWR,
+	S5P_CMU_RESET_LOWPWR,
+	S5P_APLL_SYSCLK_LOWPWR,
+	S5P_MPLL_SYSCLK_LOWPWR,
+	S5P_VPLL_SYSCLK_LOWPWR,
+	S5P_EPLL_SYSCLK_LOWPWR,
+	S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR,
+	S5P_CMU_RESET_GPSALIVE_LOWPWR,
+	S5P_CMU_CLKSTOP_CAM_LOWPWR,
+	S5P_CMU_CLKSTOP_TV_LOWPWR,
+	S5P_CMU_CLKSTOP_MFC_LOWPWR,
+	S5P_CMU_CLKSTOP_G3D_LOWPWR,
+	S5P_CMU_CLKSTOP_LCD0_LOWPWR,
+	S5P_CMU_CLKSTOP_LCD1_LOWPWR,
+	S5P_CMU_CLKSTOP_MAUDIO_LOWPWR,
+	S5P_CMU_CLKSTOP_GPS_LOWPWR,
+	S5P_CMU_RESET_CAM_LOWPWR,
+	S5P_CMU_RESET_TV_LOWPWR,
+	S5P_CMU_RESET_MFC_LOWPWR,
+	S5P_CMU_RESET_G3D_LOWPWR,
+	S5P_CMU_RESET_LCD0_LOWPWR,
+	S5P_CMU_RESET_LCD1_LOWPWR,
+	S5P_CMU_RESET_MAUDIO_LOWPWR,
+	S5P_CMU_RESET_GPS_LOWPWR,
+	S5P_TOP_BUS_LOWPWR,
+	S5P_TOP_RETENTION_LOWPWR,
+	S5P_TOP_PWR_LOWPWR,
+	S5P_LOGIC_RESET_LOWPWR,
+	S5P_ONENAND_MEM_LOWPWR,
+	S5P_MODIMIF_MEM_LOWPWR,
+	S5P_G2D_ACP_MEM_LOWPWR,
+	S5P_USBOTG_MEM_LOWPWR,
+	S5P_HSMMC_MEM_LOWPWR,
+	S5P_CSSYS_MEM_LOWPWR,
+	S5P_SECSS_MEM_LOWPWR,
+	S5P_PCIE_MEM_LOWPWR,
+	S5P_SATA_MEM_LOWPWR,
+	S5P_PAD_RETENTION_DRAM_LOWPWR,
+	S5P_PAD_RETENTION_MAUDIO_LOWPWR,
+	S5P_PAD_RETENTION_GPIO_LOWPWR,
+	S5P_PAD_RETENTION_UART_LOWPWR,
+	S5P_PAD_RETENTION_MMCA_LOWPWR,
+	S5P_PAD_RETENTION_MMCB_LOWPWR,
+	S5P_PAD_RETENTION_EBIA_LOWPWR,
+	S5P_PAD_RETENTION_EBIB_LOWPWR,
+	S5P_PAD_RETENTION_ISOLATION_LOWPWR,
+	S5P_PAD_RETENTION_ALV_SEL_LOWPWR,
+	S5P_XUSBXTI_LOWPWR,
+	S5P_XXTI_LOWPWR,
+	S5P_EXT_REGULATOR_LOWPWR,
+	S5P_GPIO_MODE_LOWPWR,
+	S5P_GPIO_MODE_MAUDIO_LOWPWR,
+	S5P_CAM_LOWPWR,
+	S5P_TV_LOWPWR,
+	S5P_MFC_LOWPWR,
+	S5P_G3D_LOWPWR,
+	S5P_LCD0_LOWPWR,
+	S5P_LCD1_LOWPWR,
+	S5P_MAUDIO_LOWPWR,
+	S5P_GPS_LOWPWR,
+	S5P_GPS_ALIVE_LOWPWR,
+};
+
+static const unsigned int sys_powerdown_val[][NUM_SYS_POWERDOWN] = {
+	/* { AFTR, LPA, SLEEP }*/
+	{ 0, 0, 2 },	/* ARM_CORE0 */
+	{ 0, 0, 0 },	/* ARM_DIS_IRQ_CORE0 */
+	{ 0, 0, 0 },	/* ARM_DIS_IRQ_CENTRAL0 */
+	{ 0, 0, 2 },	/* ARM_CORE1 */
+	{ 0, 0, 0 },	/* ARM_DIS_IRQ_CORE1 */
+	{ 0, 0, 0 },	/* ARM_DIS_IRQ_CENTRAL1 */
+	{ 0, 0, 2 },	/* ARM_COMMON */
+	{ 2, 2, 3 },	/* ARM_CPU_L2_0 */
+	{ 2, 2, 3 },	/* ARM_CPU_L2_1 */
+	{ 1, 0, 0 },	/* CMU_ACLKSTOP */
+	{ 1, 0, 0 },	/* CMU_SCLKSTOP */
+	{ 1, 1, 0 },	/* CMU_RESET */
+	{ 1, 0, 0 },	/* APLL_SYSCLK */
+	{ 1, 0, 0 },	/* MPLL_SYSCLK */
+	{ 1, 0, 0 },	/* VPLL_SYSCLK */
+	{ 1, 1, 0 },	/* EPLL_SYSCLK */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_GPS_ALIVE */
+	{ 1, 1, 0 },	/* CMU_RESET_GPS_ALIVE */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_CAM */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_TV */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_MFC */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_G3D */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_LCD0 */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_LCD1 */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_MAUDIO */
+	{ 1, 1, 0 },	/* CMU_CLKSTOP_GPS */
+	{ 1, 1, 0 },	/* CMU_RESET_CAM */
+	{ 1, 1, 0 },	/* CMU_RESET_TV */
+	{ 1, 1, 0 },	/* CMU_RESET_MFC */
+	{ 1, 1, 0 },	/* CMU_RESET_G3D */
+	{ 1, 1, 0 },	/* CMU_RESET_LCD0 */
+	{ 1, 1, 0 },	/* CMU_RESET_LCD1 */
+	{ 1, 1, 0 },	/* CMU_RESET_MAUDIO */
+	{ 1, 1, 0 },	/* CMU_RESET_GPS */
+	{ 3, 0, 0 },	/* TOP_BUS */
+	{ 1, 0, 1 },	/* TOP_RETENTION */
+	{ 3, 0, 3 },	/* TOP_PWR */
+	{ 1, 1, 0 },	/* LOGIC_RESET */
+	{ 3, 0, 0 },	/* ONENAND_MEM */
+	{ 3, 0, 0 },	/* MODIMIF_MEM */
+	{ 3, 0, 0 },	/* G2D_ACP_MEM */
+	{ 3, 0, 0 },	/* USBOTG_MEM */
+	{ 3, 0, 0 },	/* HSMMC_MEM */
+	{ 3, 0, 0 },	/* CSSYS_MEM */
+	{ 3, 0, 0 },	/* SECSS_MEM */
+	{ 3, 0, 0 },	/* PCIE_MEM */
+	{ 3, 0, 0 },	/* SATA_MEM */
+	{ 1, 0, 0 },	/* PAD_RETENTION_DRAM */
+	{ 1, 1, 0 },	/* PAD_RETENTION_MAUDIO */
+	{ 1, 0, 0 },	/* PAD_RETENTION_GPIO */
+	{ 1, 0, 0 },	/* PAD_RETENTION_UART */
+	{ 1, 0, 0 },	/* PAD_RETENTION_MMCA */
+	{ 1, 0, 0 },	/* PAD_RETENTION_MMCB */
+	{ 1, 0, 0 },	/* PAD_RETENTION_EBIA */
+	{ 1, 0, 0 },	/* PAD_RETENTION_EBIB */
+	{ 1, 0, 0 },	/* PAD_RETENTION_ISOLATION */
+	{ 1, 0, 0 },	/* PAD_RETENTION_ALV_SEL */
+	{ 1, 1, 0 },	/* XUSBXTI */
+	{ 1, 1, 0 },	/* XXTI */
+	{ 1, 1, 0 },	/* EXT_REGULATOR */
+	{ 1, 0, 0 },	/* GPIO_MODE */
+	{ 1, 1, 0 },	/* GPIO_MODE_MAUDIO */
+	{ 7, 0, 0 },	/* CAM */
+	{ 7, 0, 0 },	/* TV */
+	{ 7, 0, 0 },	/* MFC */
+	{ 7, 0, 0 },	/* G3D */
+	{ 7, 0, 0 },	/* LCD0 */
+	{ 7, 0, 0 },	/* LCD1 */
+	{ 7, 7, 0 },	/* MAUDIO */
+	{ 7, 0, 0 },	/* GPS */
+	{ 7, 0, 0 },	/* GPS_ALIVE */
+};
+
+void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
+{
+	unsigned int count = ARRAY_SIZE(sys_powerdown_reg);
+
+	for (; count > 0; count--)
+		__raw_writel(sys_powerdown_val[count - 1][mode],
+				sys_powerdown_reg[count - 1]);
+}
diff --git a/arch/arm/mach-exynos4/setup-fimd0.c b/arch/arm/mach-exynos4/setup-fimd0.c
new file mode 100644
index 0000000..07a6dbe
--- /dev/null
+++ b/arch/arm/mach-exynos4/setup-fimd0.c
@@ -0,0 +1,43 @@
+/* linux/arch/arm/mach-exynos4/setup-fimd0.c
+ *
+ * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Base Exynos4 FIMD 0 configuration
+ *
+ * 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/fb.h>
+#include <linux/gpio.h>
+
+#include <plat/gpio-cfg.h>
+#include <plat/regs-fb-v4.h>
+
+#include <mach/map.h>
+
+void exynos4_fimd0_gpio_setup_24bpp(void)
+{
+	unsigned int reg;
+
+	s3c_gpio_cfgrange_nopull(EXYNOS4_GPF0(0), 8, S3C_GPIO_SFN(2));
+	s3c_gpio_cfgrange_nopull(EXYNOS4_GPF1(0), 8, S3C_GPIO_SFN(2));
+	s3c_gpio_cfgrange_nopull(EXYNOS4_GPF2(0), 8, S3C_GPIO_SFN(2));
+	s3c_gpio_cfgrange_nopull(EXYNOS4_GPF3(0), 4, S3C_GPIO_SFN(2));
+
+	/*
+	 * Set DISPLAY_CONTROL register for Display path selection.
+	 *
+	 * DISPLAY_CONTROL[1:0]
+	 * ---------------------
+	 *  00 | MIE
+	 *  01 | MDINE
+	 *  10 | FIMD : selected
+	 *  11 | FIMD
+	 */
+	reg = __raw_readl(S3C_VA_SYS + 0x0210);
+	reg |= (1 << 1);
+	__raw_writel(reg, S3C_VA_SYS + 0x0210);
+}
diff --git a/arch/arm/mach-exynos4/time.c b/arch/arm/mach-exynos4/time.c
deleted file mode 100644
index ebb8f38..0000000
--- a/arch/arm/mach-exynos4/time.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/* linux/arch/arm/mach-exynos4/time.c
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS4 (and compatible) HRT support
- * PWM 2/4 is used for this feature
- *
- * 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/sched.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/clockchips.h>
-#include <linux/platform_device.h>
-
-#include <asm/smp_twd.h>
-
-#include <mach/map.h>
-#include <plat/regs-timer.h>
-#include <asm/mach/time.h>
-
-static unsigned long clock_count_per_tick;
-
-static struct clk *tin2;
-static struct clk *tin4;
-static struct clk *tdiv2;
-static struct clk *tdiv4;
-static struct clk *timerclk;
-
-static void exynos4_pwm_stop(unsigned int pwm_id)
-{
-	unsigned long tcon;
-
-	tcon = __raw_readl(S3C2410_TCON);
-
-	switch (pwm_id) {
-	case 2:
-		tcon &= ~S3C2410_TCON_T2START;
-		break;
-	case 4:
-		tcon &= ~S3C2410_TCON_T4START;
-		break;
-	default:
-		break;
-	}
-	__raw_writel(tcon, S3C2410_TCON);
-}
-
-static void exynos4_pwm_init(unsigned int pwm_id, unsigned long tcnt)
-{
-	unsigned long tcon;
-
-	tcon = __raw_readl(S3C2410_TCON);
-
-	/* timers reload after counting zero, so reduce the count by 1 */
-	tcnt--;
-
-	/* ensure timer is stopped... */
-	switch (pwm_id) {
-	case 2:
-		tcon &= ~(0xf<<12);
-		tcon |= S3C2410_TCON_T2MANUALUPD;
-
-		__raw_writel(tcnt, S3C2410_TCNTB(2));
-		__raw_writel(tcnt, S3C2410_TCMPB(2));
-		__raw_writel(tcon, S3C2410_TCON);
-
-		break;
-	case 4:
-		tcon &= ~(7<<20);
-		tcon |= S3C2410_TCON_T4MANUALUPD;
-
-		__raw_writel(tcnt, S3C2410_TCNTB(4));
-		__raw_writel(tcnt, S3C2410_TCMPB(4));
-		__raw_writel(tcon, S3C2410_TCON);
-
-		break;
-	default:
-		break;
-	}
-}
-
-static inline void exynos4_pwm_start(unsigned int pwm_id, bool periodic)
-{
-	unsigned long tcon;
-
-	tcon  = __raw_readl(S3C2410_TCON);
-
-	switch (pwm_id) {
-	case 2:
-		tcon |= S3C2410_TCON_T2START;
-		tcon &= ~S3C2410_TCON_T2MANUALUPD;
-
-		if (periodic)
-			tcon |= S3C2410_TCON_T2RELOAD;
-		else
-			tcon &= ~S3C2410_TCON_T2RELOAD;
-		break;
-	case 4:
-		tcon |= S3C2410_TCON_T4START;
-		tcon &= ~S3C2410_TCON_T4MANUALUPD;
-
-		if (periodic)
-			tcon |= S3C2410_TCON_T4RELOAD;
-		else
-			tcon &= ~S3C2410_TCON_T4RELOAD;
-		break;
-	default:
-		break;
-	}
-	__raw_writel(tcon, S3C2410_TCON);
-}
-
-static int exynos4_pwm_set_next_event(unsigned long cycles,
-					struct clock_event_device *evt)
-{
-	exynos4_pwm_init(2, cycles);
-	exynos4_pwm_start(2, 0);
-	return 0;
-}
-
-static void exynos4_pwm_set_mode(enum clock_event_mode mode,
-				struct clock_event_device *evt)
-{
-	exynos4_pwm_stop(2);
-
-	switch (mode) {
-	case CLOCK_EVT_MODE_PERIODIC:
-		exynos4_pwm_init(2, clock_count_per_tick);
-		exynos4_pwm_start(2, 1);
-		break;
-	case CLOCK_EVT_MODE_ONESHOT:
-		break;
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-	case CLOCK_EVT_MODE_RESUME:
-		break;
-	}
-}
-
-static struct clock_event_device pwm_event_device = {
-	.name		= "pwm_timer2",
-	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
-	.rating		= 200,
-	.shift		= 32,
-	.set_next_event	= exynos4_pwm_set_next_event,
-	.set_mode	= exynos4_pwm_set_mode,
-};
-
-irqreturn_t exynos4_clock_event_isr(int irq, void *dev_id)
-{
-	struct clock_event_device *evt = &pwm_event_device;
-
-	evt->event_handler(evt);
-
-	return IRQ_HANDLED;
-}
-
-static struct irqaction exynos4_clock_event_irq = {
-	.name		= "pwm_timer2_irq",
-	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
-	.handler	= exynos4_clock_event_isr,
-};
-
-static void __init exynos4_clockevent_init(void)
-{
-	unsigned long pclk;
-	unsigned long clock_rate;
-	struct clk *tscaler;
-
-	pclk = clk_get_rate(timerclk);
-
-	/* configure clock tick */
-
-	tscaler = clk_get_parent(tdiv2);
-
-	clk_set_rate(tscaler, pclk / 2);
-	clk_set_rate(tdiv2, pclk / 2);
-	clk_set_parent(tin2, tdiv2);
-
-	clock_rate = clk_get_rate(tin2);
-
-	clock_count_per_tick = clock_rate / HZ;
-
-	pwm_event_device.mult =
-		div_sc(clock_rate, NSEC_PER_SEC, pwm_event_device.shift);
-	pwm_event_device.max_delta_ns =
-		clockevent_delta2ns(-1, &pwm_event_device);
-	pwm_event_device.min_delta_ns =
-		clockevent_delta2ns(1, &pwm_event_device);
-
-	pwm_event_device.cpumask = cpumask_of(0);
-	clockevents_register_device(&pwm_event_device);
-
-	setup_irq(IRQ_TIMER2, &exynos4_clock_event_irq);
-}
-
-static cycle_t exynos4_pwm4_read(struct clocksource *cs)
-{
-	return (cycle_t) ~__raw_readl(S3C_TIMERREG(0x40));
-}
-
-#ifdef CONFIG_PM
-static void exynos4_pwm4_resume(struct clocksource *cs)
-{
-	unsigned long pclk;
-
-	pclk = clk_get_rate(timerclk);
-
-	clk_set_rate(tdiv4, pclk / 2);
-	clk_set_parent(tin4, tdiv4);
-
-	exynos4_pwm_init(4, ~0);
-	exynos4_pwm_start(4, 1);
-}
-#endif
-
-struct clocksource pwm_clocksource = {
-	.name		= "pwm_timer4",
-	.rating		= 250,
-	.read		= exynos4_pwm4_read,
-	.mask		= CLOCKSOURCE_MASK(32),
-	.flags		= CLOCK_SOURCE_IS_CONTINUOUS ,
-#ifdef CONFIG_PM
-	.resume		= exynos4_pwm4_resume,
-#endif
-};
-
-static void __init exynos4_clocksource_init(void)
-{
-	unsigned long pclk;
-	unsigned long clock_rate;
-
-	pclk = clk_get_rate(timerclk);
-
-	clk_set_rate(tdiv4, pclk / 2);
-	clk_set_parent(tin4, tdiv4);
-
-	clock_rate = clk_get_rate(tin4);
-
-	exynos4_pwm_init(4, ~0);
-	exynos4_pwm_start(4, 1);
-
-	if (clocksource_register_hz(&pwm_clocksource, clock_rate))
-		panic("%s: can't register clocksource\n", pwm_clocksource.name);
-}
-
-static void __init exynos4_timer_resources(void)
-{
-	struct platform_device tmpdev;
-
-	tmpdev.dev.bus = &platform_bus_type;
-
-	timerclk = clk_get(NULL, "timers");
-	if (IS_ERR(timerclk))
-		panic("failed to get timers clock for system timer");
-
-	clk_enable(timerclk);
-
-	tmpdev.id = 2;
-	tin2 = clk_get(&tmpdev.dev, "pwm-tin");
-	if (IS_ERR(tin2))
-		panic("failed to get pwm-tin2 clock for system timer");
-
-	tdiv2 = clk_get(&tmpdev.dev, "pwm-tdiv");
-	if (IS_ERR(tdiv2))
-		panic("failed to get pwm-tdiv2 clock for system timer");
-	clk_enable(tin2);
-
-	tmpdev.id = 4;
-	tin4 = clk_get(&tmpdev.dev, "pwm-tin");
-	if (IS_ERR(tin4))
-		panic("failed to get pwm-tin4 clock for system timer");
-
-	tdiv4 = clk_get(&tmpdev.dev, "pwm-tdiv");
-	if (IS_ERR(tdiv4))
-		panic("failed to get pwm-tdiv4 clock for system timer");
-
-	clk_enable(tin4);
-}
-
-static void __init exynos4_timer_init(void)
-{
-#ifdef CONFIG_LOCAL_TIMERS
-	twd_base = S5P_VA_TWD;
-#endif
-
-	exynos4_timer_resources();
-	exynos4_clockevent_init();
-	exynos4_clocksource_init();
-}
-
-struct sys_timer exynos4_timer = {
-	.init		= exynos4_timer_init,
-};
diff --git a/arch/arm/mach-footbridge/cats-pci.c b/arch/arm/mach-footbridge/cats-pci.c
index ae3e1c8..32321f6 100644
--- a/arch/arm/mach-footbridge/cats-pci.c
+++ b/arch/arm/mach-footbridge/cats-pci.c
@@ -16,7 +16,7 @@
 /* cats host-specific stuff */
 static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
 
-static int __init cats_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init cats_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (dev->irq >= 255)
 		return -1;	/* not a valid interrupt. */
diff --git a/arch/arm/mach-footbridge/dc21285.c b/arch/arm/mach-footbridge/dc21285.c
index 3ffa548..1331fff 100644
--- a/arch/arm/mach-footbridge/dc21285.c
+++ b/arch/arm/mach-footbridge/dc21285.c
@@ -295,6 +295,9 @@
 	unsigned int mem_size, mem_mask;
 	int cfn_mode;
 
+	pcibios_min_mem = 0x81000000;
+	vga_base = PCIMEM_BASE;
+
 	mem_size = (unsigned int)high_memory - PAGE_OFFSET;
 	for (mem_mask = 0x00100000; mem_mask < 0x10000000; mem_mask <<= 1)
 		if (mem_mask >= mem_size)
diff --git a/arch/arm/mach-footbridge/ebsa285-pci.c b/arch/arm/mach-footbridge/ebsa285-pci.c
index e5ab5bd..511c673 100644
--- a/arch/arm/mach-footbridge/ebsa285-pci.c
+++ b/arch/arm/mach-footbridge/ebsa285-pci.c
@@ -15,7 +15,7 @@
 
 static int irqmap_ebsa285[] __initdata = { IRQ_IN3, IRQ_IN1, IRQ_IN0, IRQ_PCI };
 
-static int __init ebsa285_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ebsa285_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
 	    dev->device == PCI_DEVICE_ID_CONTAQ_82C693)
diff --git a/arch/arm/mach-footbridge/include/mach/hardware.h b/arch/arm/mach-footbridge/include/mach/hardware.h
index b6fdf23..15d5498 100644
--- a/arch/arm/mach-footbridge/include/mach/hardware.h
+++ b/arch/arm/mach-footbridge/include/mach/hardware.h
@@ -100,9 +100,4 @@
 extern void nw_cpld_modify(unsigned int mask, unsigned int set);
 #endif
 
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO		0x1000
-#define PCIBIOS_MIN_MEM 	0x81000000
-
 #endif
diff --git a/arch/arm/mach-footbridge/netwinder-pci.c b/arch/arm/mach-footbridge/netwinder-pci.c
index e263d6d..6218761 100644
--- a/arch/arm/mach-footbridge/netwinder-pci.c
+++ b/arch/arm/mach-footbridge/netwinder-pci.c
@@ -17,7 +17,7 @@
  * We now use the slot ID instead of the device identifiers to select
  * which interrupt is routed where.
  */
-static int __init netwinder_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init netwinder_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	switch (slot) {
 	case 0:  /* host bridge */
diff --git a/arch/arm/mach-footbridge/personal-pci.c b/arch/arm/mach-footbridge/personal-pci.c
index d5fca95..aeb651d 100644
--- a/arch/arm/mach-footbridge/personal-pci.c
+++ b/arch/arm/mach-footbridge/personal-pci.c
@@ -18,7 +18,8 @@
 	IRQ_DOORBELLHOST, IRQ_DMA1, IRQ_DMA2, IRQ_PCI
 };
 
-static int __init personal_server_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init personal_server_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	unsigned char line;
 
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index e8dd22f..0519dd7 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -278,6 +278,7 @@
 	select SOC_IMX27
 	select IMX_HAVE_PLATFORM_FSL_USB2_UDC
 	select IMX_HAVE_PLATFORM_IMX2_WDT
+	select IMX_HAVE_PLATFORM_IMX_FB
 	select IMX_HAVE_PLATFORM_IMX_I2C
 	select IMX_HAVE_PLATFORM_IMX_KEYPAD
 	select IMX_HAVE_PLATFORM_IMX_UART
diff --git a/arch/arm/mach-imx/clock-imx1.c b/arch/arm/mach-imx/clock-imx1.c
index dcc4172..4aabeb2 100644
--- a/arch/arm/mach-imx/clock-imx1.c
+++ b/arch/arm/mach-imx/clock-imx1.c
@@ -587,9 +587,9 @@
 	_REGISTER_CLOCK(NULL, "mma", mma_clk)
 	_REGISTER_CLOCK("imx_udc.0", NULL, usbd_clk)
 	_REGISTER_CLOCK(NULL, "gpt", gpt_clk)
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart_clk)
+	_REGISTER_CLOCK("imx1-uart.0", NULL, uart_clk)
+	_REGISTER_CLOCK("imx1-uart.1", NULL, uart_clk)
+	_REGISTER_CLOCK("imx1-uart.2", NULL, uart_clk)
 	_REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
 	_REGISTER_CLOCK("imx1-cspi.0", NULL, spi_clk)
 	_REGISTER_CLOCK("imx1-cspi.1", NULL, spi_clk)
diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
index bf30a8c..ee15d8c 100644
--- a/arch/arm/mach-imx/clock-imx21.c
+++ b/arch/arm/mach-imx/clock-imx21.c
@@ -1162,10 +1162,10 @@
 	_REGISTER_CLOCK(NULL, "perclk3", per_clk[2])
 	_REGISTER_CLOCK(NULL, "perclk4", per_clk[3])
 	_REGISTER_CLOCK(NULL, "clko", clko_clk)
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart_clk[0])
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart_clk[1])
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart_clk[2])
-	_REGISTER_CLOCK("imx-uart.3", NULL, uart_clk[3])
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart_clk[0])
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart_clk[1])
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart_clk[2])
+	_REGISTER_CLOCK("imx21-uart.3", NULL, uart_clk[3])
 	_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[0])
 	_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
 	_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
diff --git a/arch/arm/mach-imx/clock-imx25.c b/arch/arm/mach-imx/clock-imx25.c
index af1c580..0fc7ba5 100644
--- a/arch/arm/mach-imx/clock-imx25.c
+++ b/arch/arm/mach-imx/clock-imx25.c
@@ -272,11 +272,12 @@
 	},
 
 static struct clk_lookup lookups[] = {
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
-	_REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk)
-	_REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk)
+	/* i.mx25 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
+	_REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk)
+	_REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk)
 	_REGISTER_CLOCK("mxc-ehci.0", "usb", usbotg_clk)
 	_REGISTER_CLOCK("mxc-ehci.1", "usb", usbotg_clk)
 	_REGISTER_CLOCK("mxc-ehci.2", "usb", usbotg_clk)
@@ -295,19 +296,20 @@
 	_REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
 	_REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk)
 	_REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk)
-	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	_REGISTER_CLOCK("imx25-fec.0", NULL, fec_clk)
 	_REGISTER_CLOCK("imxdi_rtc.0", NULL, dryice_clk)
 	_REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
 	_REGISTER_CLOCK("imx2-wdt.0", NULL, wdt_clk)
 	_REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk)
 	_REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx25.0", NULL, esdhc1_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx25.1", NULL, esdhc2_clk)
 	_REGISTER_CLOCK("mx2-camera.0", NULL, csi_clk)
 	_REGISTER_CLOCK(NULL, "audmux", audmux_clk)
 	_REGISTER_CLOCK("flexcan.0", NULL, can1_clk)
 	_REGISTER_CLOCK("flexcan.1", NULL, can2_clk)
-	_REGISTER_CLOCK("imx-sdma", NULL, sdma_clk)
+	/* i.mx25 has the i.mx35 type sdma */
+	_REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk)
 };
 
 int __init mx25_clocks_init(void)
diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c
index 583f251..6912b82 100644
--- a/arch/arm/mach-imx/clock-imx27.c
+++ b/arch/arm/mach-imx/clock-imx27.c
@@ -624,12 +624,13 @@
 	},
 
 static struct clk_lookup lookups[] = {
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
-	_REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk)
-	_REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk)
-	_REGISTER_CLOCK("imx-uart.5", NULL, uart6_clk)
+	/* i.mx27 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
+	_REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk)
+	_REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk)
+	_REGISTER_CLOCK("imx21-uart.5", NULL, uart6_clk)
 	_REGISTER_CLOCK(NULL, "gpt1", gpt1_clk)
 	_REGISTER_CLOCK(NULL, "gpt2", gpt2_clk)
 	_REGISTER_CLOCK(NULL, "gpt3", gpt3_clk)
@@ -662,7 +663,7 @@
 	_REGISTER_CLOCK(NULL, "brom", brom_clk)
 	_REGISTER_CLOCK(NULL, "emma", emma_clk)
 	_REGISTER_CLOCK(NULL, "slcdc", slcdc_clk)
-	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	_REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk)
 	_REGISTER_CLOCK(NULL, "emi", emi_clk)
 	_REGISTER_CLOCK(NULL, "sahara2", sahara2_clk)
 	_REGISTER_CLOCK(NULL, "ata", ata_clk)
diff --git a/arch/arm/mach-imx/clock-imx31.c b/arch/arm/mach-imx/clock-imx31.c
index 25f343f..d973770 100644
--- a/arch/arm/mach-imx/clock-imx31.c
+++ b/arch/arm/mach-imx/clock-imx31.c
@@ -547,11 +547,12 @@
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk1)
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk2)
 	_REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk)
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
-	_REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk)
-	_REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk)
+	/* i.mx31 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
+	_REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk)
+	_REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk)
 	_REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk)
 	_REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk)
 	_REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_clk)
@@ -564,7 +565,7 @@
 	_REGISTER_CLOCK(NULL, "ata", ata_clk)
 	_REGISTER_CLOCK(NULL, "rtic", rtic_clk)
 	_REGISTER_CLOCK(NULL, "rng", rng_clk)
-	_REGISTER_CLOCK("imx-sdma", NULL, sdma_clk1)
+	_REGISTER_CLOCK("imx31-sdma", NULL, sdma_clk1)
 	_REGISTER_CLOCK(NULL, "sdma_ipg", sdma_clk2)
 	_REGISTER_CLOCK(NULL, "mstick", mstick1_clk)
 	_REGISTER_CLOCK(NULL, "mstick", mstick2_clk)
diff --git a/arch/arm/mach-imx/clock-imx35.c b/arch/arm/mach-imx/clock-imx35.c
index 5a4cc1e..88b62a0 100644
--- a/arch/arm/mach-imx/clock-imx35.c
+++ b/arch/arm/mach-imx/clock-imx35.c
@@ -458,10 +458,11 @@
 	_REGISTER_CLOCK("imx-epit.0", NULL, epit1_clk)
 	_REGISTER_CLOCK("imx-epit.1", NULL, epit2_clk)
 	_REGISTER_CLOCK(NULL, "esai", esai_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.2", NULL, esdhc3_clk)
-	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx35.0", NULL, esdhc1_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx35.1", NULL, esdhc2_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx35.2", NULL, esdhc3_clk)
+	/* i.mx35 has the i.mx27 type fec */
+	_REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk)
 	_REGISTER_CLOCK(NULL, "gpio", gpio1_clk)
 	_REGISTER_CLOCK(NULL, "gpio", gpio2_clk)
 	_REGISTER_CLOCK(NULL, "gpio", gpio3_clk)
@@ -481,14 +482,15 @@
 	_REGISTER_CLOCK(NULL, "rtc", rtc_clk)
 	_REGISTER_CLOCK(NULL, "rtic", rtic_clk)
 	_REGISTER_CLOCK(NULL, "scc", scc_clk)
-	_REGISTER_CLOCK("imx-sdma", NULL, sdma_clk)
+	_REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk)
 	_REGISTER_CLOCK(NULL, "spba", spba_clk)
 	_REGISTER_CLOCK(NULL, "spdif", spdif_clk)
 	_REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk)
 	_REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk)
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
+	/* i.mx35 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
 	_REGISTER_CLOCK("mxc-ehci.0", "usb", usbotg_clk)
 	_REGISTER_CLOCK("mxc-ehci.1", "usb", usbotg_clk)
 	_REGISTER_CLOCK("mxc-ehci.2", "usb", usbotg_clk)
diff --git a/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c
index 01ebcb3..66e8726 100644
--- a/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c
+++ b/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c
@@ -225,7 +225,8 @@
 
 static struct esdhc_platform_data sd1_pdata = {
 	.cd_gpio = GPIO_SD1CD,
-	.wp_gpio = -EINVAL,
+	.cd_type = ESDHC_CD_GPIO,
+	.wp_type = ESDHC_WP_NONE,
 };
 
 /*
diff --git a/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c
index 558eb52..0f0af02 100644
--- a/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c
+++ b/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c
@@ -236,7 +236,8 @@
 
 static struct esdhc_platform_data sd1_pdata = {
 	.cd_gpio = GPIO_SD1CD,
-	.wp_gpio = -EINVAL,
+	.cd_type = ESDHC_CD_GPIO,
+	.wp_type = ESDHC_WP_NONE,
 };
 
 /*
diff --git a/arch/arm/mach-imx/mach-mx25_3ds.c b/arch/arm/mach-imx/mach-mx25_3ds.c
index 01534bb..7f66a91 100644
--- a/arch/arm/mach-imx/mach-mx25_3ds.c
+++ b/arch/arm/mach-imx/mach-mx25_3ds.c
@@ -215,6 +215,8 @@
 static const struct esdhc_platform_data mx25pdk_esdhc_pdata __initconst = {
 	.wp_gpio = SD1_GPIO_WP,
 	.cd_gpio = SD1_GPIO_CD,
+	.wp_type = ESDHC_WP_GPIO,
+	.cd_type = ESDHC_CD_GPIO,
 };
 
 static void __init mx25pdk_init(void)
diff --git a/arch/arm/mach-imx/mach-mx27_3ds.c b/arch/arm/mach-imx/mach-mx27_3ds.c
index b31d412..6fa6934 100644
--- a/arch/arm/mach-imx/mach-mx27_3ds.c
+++ b/arch/arm/mach-imx/mach-mx27_3ds.c
@@ -29,6 +29,7 @@
 #include <linux/mfd/mc13783.h>
 #include <linux/spi/spi.h>
 #include <linux/regulator/machine.h>
+#include <linux/spi/l4f00242t03.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -47,7 +48,10 @@
 #define SPI2_SS0		IMX_GPIO_NR(4, 21)
 #define EXPIO_PARENT_INT	gpio_to_irq(IMX_GPIO_NR(3, 28))
 #define PMIC_INT		IMX_GPIO_NR(3, 14)
+#define SPI1_SS0		IMX_GPIO_NR(4, 28)
 #define SD1_CD			IMX_GPIO_NR(2, 26)
+#define LCD_RESET		IMX_GPIO_NR(1, 3)
+#define LCD_ENABLE		IMX_GPIO_NR(1, 31)
 
 static const int mx27pdk_pins[] __initconst = {
 	/* UART1 */
@@ -96,6 +100,12 @@
 	PE2_PF_USBOTG_DIR,
 	PE24_PF_USBOTG_CLK,
 	PE25_PF_USBOTG_DATA7,
+	/* CSPI1 */
+	PD31_PF_CSPI1_MOSI,
+	PD30_PF_CSPI1_MISO,
+	PD29_PF_CSPI1_SCLK,
+	PD25_PF_CSPI1_RDY,
+	SPI1_SS0 | GPIO_GPIO | GPIO_OUT,
 	/* CSPI2 */
 	PD22_PF_CSPI2_SCLK,
 	PD23_PF_CSPI2_MISO,
@@ -106,6 +116,31 @@
 	PD18_PF_I2C_CLK,
 	/* PMIC INT */
 	PMIC_INT | GPIO_GPIO | GPIO_IN,
+	/* LCD */
+	PA5_PF_LSCLK,
+	PA6_PF_LD0,
+	PA7_PF_LD1,
+	PA8_PF_LD2,
+	PA9_PF_LD3,
+	PA10_PF_LD4,
+	PA11_PF_LD5,
+	PA12_PF_LD6,
+	PA13_PF_LD7,
+	PA14_PF_LD8,
+	PA15_PF_LD9,
+	PA16_PF_LD10,
+	PA17_PF_LD11,
+	PA18_PF_LD12,
+	PA19_PF_LD13,
+	PA20_PF_LD14,
+	PA21_PF_LD15,
+	PA22_PF_LD16,
+	PA23_PF_LD17,
+	PA28_PF_HSYNC,
+	PA29_PF_VSYNC,
+	PA30_PF_CONTRAST,
+	LCD_ENABLE | GPIO_GPIO | GPIO_OUT,
+	LCD_RESET | GPIO_GPIO | GPIO_OUT,
 };
 
 static const struct imxuart_platform_data uart_pdata __initconst = {
@@ -258,10 +293,18 @@
 		.num_regulators = ARRAY_SIZE(mx27_3ds_regulators),
 
 	},
-	.flags  = MC13783_USE_REGULATOR,
+	.flags  = MC13783_USE_REGULATOR | MC13783_USE_TOUCHSCREEN |
+	MC13783_USE_RTC,
 };
 
 /* SPI */
+static int spi1_chipselect[] = {SPI1_SS0};
+
+static const struct spi_imx_master spi1_pdata __initconst = {
+	.chipselect	= spi1_chipselect,
+	.num_chipselect	= ARRAY_SIZE(spi1_chipselect),
+};
+
 static int spi2_chipselect[] = {SPI2_SS0};
 
 static const struct spi_imx_master spi2_pdata __initconst = {
@@ -269,6 +312,46 @@
 	.num_chipselect	= ARRAY_SIZE(spi2_chipselect),
 };
 
+static struct imx_fb_videomode mx27_3ds_modes[] = {
+	{	/* 480x640 @ 60 Hz */
+		.mode = {
+			.name		= "Epson-VGA",
+			.refresh	= 60,
+			.xres		= 480,
+			.yres		= 640,
+			.pixclock	= 41701,
+			.left_margin	= 20,
+			.right_margin	= 41,
+			.upper_margin	= 10,
+			.lower_margin	= 5,
+			.hsync_len	= 20,
+			.vsync_len	= 10,
+			.sync		= FB_SYNC_OE_ACT_HIGH |
+						FB_SYNC_CLK_INVERT,
+			.vmode		= FB_VMODE_NONINTERLACED,
+			.flag		= 0,
+		},
+		.bpp		= 16,
+		.pcr		= 0xFAC08B82,
+	},
+};
+
+static const struct imx_fb_platform_data mx27_3ds_fb_data __initconst = {
+	.mode = mx27_3ds_modes,
+	.num_modes = ARRAY_SIZE(mx27_3ds_modes),
+	.pwmr		= 0x00A903FF,
+	.lscr1		= 0x00120300,
+	.dmacr		= 0x00020010,
+};
+
+/* LCD */
+static struct l4f00242t03_pdata mx27_3ds_lcd_pdata = {
+	.reset_gpio		= LCD_RESET,
+	.data_enable_gpio	= LCD_ENABLE,
+	.core_supply		= "lcd_2v8",
+	.io_supply		= "vdd_lcdio",
+};
+
 static struct spi_board_info mx27_3ds_spi_devs[] __initdata = {
 	{
 		.modalias	= "mc13783",
@@ -278,6 +361,12 @@
 		.platform_data	= &mc13783_pdata,
 		.irq = gpio_to_irq(PMIC_INT),
 		.mode = SPI_CS_HIGH,
+	}, {
+		.modalias	= "l4f00242t03",
+		.max_speed_hz	= 5000000,
+		.bus_num	= 0,
+		.chip_select	= 0, /* SS0 */
+		.platform_data	= &mx27_3ds_lcd_pdata,
 	},
 };
 
@@ -311,12 +400,14 @@
 		imx27_add_fsl_usb2_udc(&otg_device_pdata);
 
 	imx27_add_spi_imx1(&spi2_pdata);
+	imx27_add_spi_imx0(&spi1_pdata);
 	spi_register_board_info(mx27_3ds_spi_devs,
 						ARRAY_SIZE(mx27_3ds_spi_devs));
 
 	if (mxc_expio_init(MX27_CS5_BASE_ADDR, EXPIO_PARENT_INT))
 		pr_warn("Init of the debugboard failed, all devices on the debugboard are unusable.\n");
 	imx27_add_imx_i2c(0, &mx27_3ds_i2c0_data);
+	imx27_add_imx_fb(&mx27_3ds_fb_data);
 }
 
 static void __init mx27pdk_timer_init(void)
diff --git a/arch/arm/mach-imx/mach-pcm043.c b/arch/arm/mach-imx/mach-pcm043.c
index 163cc31..660ec3e 100644
--- a/arch/arm/mach-imx/mach-pcm043.c
+++ b/arch/arm/mach-imx/mach-pcm043.c
@@ -349,6 +349,8 @@
 static struct esdhc_platform_data sd1_pdata = {
 	.wp_gpio = SD1_GPIO_WP,
 	.cd_gpio = SD1_GPIO_CD,
+	.wp_type = ESDHC_WP_GPIO,
+	.cd_type = ESDHC_CD_GPIO,
 };
 
 /*
diff --git a/arch/arm/mach-imx/mm-imx21.c b/arch/arm/mach-imx/mm-imx21.c
index 6d7d518..3f05dfe 100644
--- a/arch/arm/mach-imx/mm-imx21.c
+++ b/arch/arm/mach-imx/mm-imx21.c
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <mach/hardware.h>
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <asm/pgtable.h>
 #include <asm/mach/map.h>
 #include <mach/irqs.h>
@@ -82,4 +83,6 @@
 	mxc_register_gpio("imx21-gpio", 3, MX21_GPIO4_BASE_ADDR, SZ_256, MX21_INT_GPIO, 0);
 	mxc_register_gpio("imx21-gpio", 4, MX21_GPIO5_BASE_ADDR, SZ_256, MX21_INT_GPIO, 0);
 	mxc_register_gpio("imx21-gpio", 5, MX21_GPIO6_BASE_ADDR, SZ_256, MX21_INT_GPIO, 0);
+
+	imx_add_imx_dma();
 }
diff --git a/arch/arm/mach-imx/mm-imx25.c b/arch/arm/mach-imx/mm-imx25.c
index 9a1591c..cc4d152 100644
--- a/arch/arm/mach-imx/mm-imx25.c
+++ b/arch/arm/mach-imx/mm-imx25.c
@@ -24,6 +24,7 @@
 #include <asm/mach/map.h>
 
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <mach/hardware.h>
 #include <mach/mx25.h>
 #include <mach/iomux-v3.h>
@@ -61,6 +62,27 @@
 	mxc_init_irq(MX25_IO_ADDRESS(MX25_AVIC_BASE_ADDR));
 }
 
+static struct sdma_script_start_addrs imx25_sdma_script __initdata = {
+	.ap_2_ap_addr = 729,
+	.uart_2_mcu_addr = 904,
+	.per_2_app_addr = 1255,
+	.mcu_2_app_addr = 834,
+	.uartsh_2_mcu_addr = 1120,
+	.per_2_shp_addr = 1329,
+	.mcu_2_shp_addr = 1048,
+	.ata_2_mcu_addr = 1560,
+	.mcu_2_ata_addr = 1479,
+	.app_2_per_addr = 1189,
+	.app_2_mcu_addr = 770,
+	.shp_2_per_addr = 1407,
+	.shp_2_mcu_addr = 979,
+};
+
+static struct sdma_platform_data imx25_sdma_pdata __initdata = {
+	.fw_name = "sdma-imx25.bin",
+	.script_addrs = &imx25_sdma_script,
+};
+
 void __init imx25_soc_init(void)
 {
 	/* i.mx25 has the i.mx31 type gpio */
@@ -68,4 +90,7 @@
 	mxc_register_gpio("imx31-gpio", 1, MX25_GPIO2_BASE_ADDR, SZ_16K, MX25_INT_GPIO2, 0);
 	mxc_register_gpio("imx31-gpio", 2, MX25_GPIO3_BASE_ADDR, SZ_16K, MX25_INT_GPIO3, 0);
 	mxc_register_gpio("imx31-gpio", 3, MX25_GPIO4_BASE_ADDR, SZ_16K, MX25_INT_GPIO4, 0);
+
+	/* i.mx25 has the i.mx35 type sdma */
+	imx_add_imx_sdma("imx35-sdma", MX25_SDMA_BASE_ADDR, MX25_INT_SDMA, &imx25_sdma_pdata);
 }
diff --git a/arch/arm/mach-imx/mm-imx27.c b/arch/arm/mach-imx/mm-imx27.c
index 133b300..96dd1f5 100644
--- a/arch/arm/mach-imx/mm-imx27.c
+++ b/arch/arm/mach-imx/mm-imx27.c
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <mach/hardware.h>
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <asm/pgtable.h>
 #include <asm/mach/map.h>
 #include <mach/irqs.h>
@@ -83,4 +84,6 @@
 	mxc_register_gpio("imx21-gpio", 3, MX27_GPIO4_BASE_ADDR, SZ_256, MX27_INT_GPIO, 0);
 	mxc_register_gpio("imx21-gpio", 4, MX27_GPIO5_BASE_ADDR, SZ_256, MX27_INT_GPIO, 0);
 	mxc_register_gpio("imx21-gpio", 5, MX27_GPIO6_BASE_ADDR, SZ_256, MX27_INT_GPIO, 0);
+
+	imx_add_imx_dma();
 }
diff --git a/arch/arm/mach-imx/mm-imx31.c b/arch/arm/mach-imx/mm-imx31.c
index 6d103c0..b7c55e7 100644
--- a/arch/arm/mach-imx/mm-imx31.c
+++ b/arch/arm/mach-imx/mm-imx31.c
@@ -24,6 +24,7 @@
 #include <asm/mach/map.h>
 
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <mach/hardware.h>
 #include <mach/iomux-v3.h>
 #include <mach/irqs.h>
@@ -57,9 +58,34 @@
 	mxc_init_irq(MX31_IO_ADDRESS(MX31_AVIC_BASE_ADDR));
 }
 
+static struct sdma_script_start_addrs imx31_to1_sdma_script __initdata = {
+	.per_2_per_addr = 1677,
+};
+
+static struct sdma_script_start_addrs imx31_to2_sdma_script __initdata = {
+	.ap_2_ap_addr = 423,
+	.ap_2_bp_addr = 829,
+	.bp_2_ap_addr = 1029,
+};
+
+static struct sdma_platform_data imx31_sdma_pdata __initdata = {
+	.fw_name = "sdma-imx31-to2.bin",
+	.script_addrs = &imx31_to2_sdma_script,
+};
+
 void __init imx31_soc_init(void)
 {
+	int to_version = mx31_revision() >> 4;
+
 	mxc_register_gpio("imx31-gpio", 0, MX31_GPIO1_BASE_ADDR, SZ_16K, MX31_INT_GPIO1, 0);
 	mxc_register_gpio("imx31-gpio", 1, MX31_GPIO2_BASE_ADDR, SZ_16K, MX31_INT_GPIO2, 0);
 	mxc_register_gpio("imx31-gpio", 2, MX31_GPIO3_BASE_ADDR, SZ_16K, MX31_INT_GPIO3, 0);
+
+	if (to_version == 1) {
+		strncpy(imx31_sdma_pdata.fw_name, "sdma-imx31-to1.bin",
+			strlen(imx31_sdma_pdata.fw_name));
+		imx31_sdma_pdata.script_addrs = &imx31_to1_sdma_script;
+	}
+
+	imx_add_imx_sdma("imx31-sdma", MX31_SDMA_BASE_ADDR, MX31_INT_SDMA, &imx31_sdma_pdata);
 }
diff --git a/arch/arm/mach-imx/mm-imx35.c b/arch/arm/mach-imx/mm-imx35.c
index bb068bc..f49bac7 100644
--- a/arch/arm/mach-imx/mm-imx35.c
+++ b/arch/arm/mach-imx/mm-imx35.c
@@ -25,6 +25,7 @@
 #include <asm/hardware/cache-l2x0.h>
 
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <mach/hardware.h>
 #include <mach/iomux-v3.h>
 #include <mach/irqs.h>
@@ -54,10 +55,55 @@
 	mxc_init_irq(MX35_IO_ADDRESS(MX35_AVIC_BASE_ADDR));
 }
 
+static struct sdma_script_start_addrs imx35_to1_sdma_script __initdata = {
+	.ap_2_ap_addr = 642,
+	.uart_2_mcu_addr = 817,
+	.mcu_2_app_addr = 747,
+	.uartsh_2_mcu_addr = 1183,
+	.per_2_shp_addr = 1033,
+	.mcu_2_shp_addr = 961,
+	.ata_2_mcu_addr = 1333,
+	.mcu_2_ata_addr = 1252,
+	.app_2_mcu_addr = 683,
+	.shp_2_per_addr = 1111,
+	.shp_2_mcu_addr = 892,
+};
+
+static struct sdma_script_start_addrs imx35_to2_sdma_script __initdata = {
+	.ap_2_ap_addr = 729,
+	.uart_2_mcu_addr = 904,
+	.per_2_app_addr = 1597,
+	.mcu_2_app_addr = 834,
+	.uartsh_2_mcu_addr = 1270,
+	.per_2_shp_addr = 1120,
+	.mcu_2_shp_addr = 1048,
+	.ata_2_mcu_addr = 1429,
+	.mcu_2_ata_addr = 1339,
+	.app_2_per_addr = 1531,
+	.app_2_mcu_addr = 770,
+	.shp_2_per_addr = 1198,
+	.shp_2_mcu_addr = 979,
+};
+
+static struct sdma_platform_data imx35_sdma_pdata __initdata = {
+	.fw_name = "sdma-imx35-to2.bin",
+	.script_addrs = &imx35_to2_sdma_script,
+};
+
 void __init imx35_soc_init(void)
 {
+	int to_version = mx35_revision() >> 4;
+
 	/* i.mx35 has the i.mx31 type gpio */
 	mxc_register_gpio("imx31-gpio", 0, MX35_GPIO1_BASE_ADDR, SZ_16K, MX35_INT_GPIO1, 0);
 	mxc_register_gpio("imx31-gpio", 1, MX35_GPIO2_BASE_ADDR, SZ_16K, MX35_INT_GPIO2, 0);
 	mxc_register_gpio("imx31-gpio", 2, MX35_GPIO3_BASE_ADDR, SZ_16K, MX35_INT_GPIO3, 0);
+
+	if (to_version == 1) {
+		strncpy(imx35_sdma_pdata.fw_name, "sdma-imx35-to1.bin",
+			strlen(imx35_sdma_pdata.fw_name));
+		imx35_sdma_pdata.script_addrs = &imx35_to1_sdma_script;
+	}
+
+	imx_add_imx_sdma("imx35-sdma", MX35_SDMA_BASE_ADDR, MX35_INT_SDMA, &imx35_sdma_pdata);
 }
diff --git a/arch/arm/mach-integrator/include/mach/hardware.h b/arch/arm/mach-integrator/include/mach/hardware.h
index 57f51ba..65fed7c 100644
--- a/arch/arm/mach-integrator/include/mach/hardware.h
+++ b/arch/arm/mach-integrator/include/mach/hardware.h
@@ -32,13 +32,6 @@
 #define IO_SIZE			0x0B000000                 // How much?
 #define IO_START		INTEGRATOR_HDR_BASE        // PA of IO
 
-#define PCIMEM_BASE		PCI_MEMORY_VADDR
-
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO		0x6000
-#define PCIBIOS_MIN_MEM 	0x00100000
-
 /* macro to get at IO space when running virtually */
 #ifdef CONFIG_MMU
 #define IO_ADDRESS(x)	(((x) & 0x000fffff) | (((x) >> 4) & 0x0ff00000) | IO_BASE)
diff --git a/arch/arm/mach-integrator/pci.c b/arch/arm/mach-integrator/pci.c
index 2fdb954..520b6bf 100644
--- a/arch/arm/mach-integrator/pci.c
+++ b/arch/arm/mach-integrator/pci.c
@@ -95,7 +95,7 @@
  * map the specified device/slot/pin to an IRQ.  This works out such
  * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1.
  */
-static int __init integrator_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init integrator_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int intnr = ((slot - 9) + (pin - 1)) & 3;
 
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
index 6467d99..dd56bfb 100644
--- a/arch/arm/mach-integrator/pci_v3.c
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -27,6 +27,7 @@
 #include <linux/spinlock.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <video/vga.h>
 
 #include <mach/hardware.h>
 #include <mach/platform.h>
@@ -502,6 +503,10 @@
 	unsigned int temp;
 	int ret;
 
+	pcibios_min_io = 0x6000;
+	pcibios_min_mem = 0x00100000;
+	vga_base = PCI_MEMORY_VADDR;
+
 	/*
 	 * Hook in our fault handler for PCI errors
 	 */
diff --git a/arch/arm/mach-iop13xx/include/mach/hardware.h b/arch/arm/mach-iop13xx/include/mach/hardware.h
index 8e1d562..786fa26 100644
--- a/arch/arm/mach-iop13xx/include/mach/hardware.h
+++ b/arch/arm/mach-iop13xx/include/mach/hardware.h
@@ -2,18 +2,11 @@
 #define __ASM_ARCH_HARDWARE_H
 #include <asm/types.h>
 
-#define pcibios_assign_all_busses() 1
-
 #ifndef __ASSEMBLY__
-extern unsigned long iop13xx_pcibios_min_io;
-extern unsigned long iop13xx_pcibios_min_mem;
 extern u16 iop13xx_dev_id(void);
 extern void iop13xx_set_atu_mmr_bases(void);
 #endif
 
-#define PCIBIOS_MIN_IO      (iop13xx_pcibios_min_io)
-#define PCIBIOS_MIN_MEM     (iop13xx_pcibios_min_mem)
-
 /*
  * Generic chipset bits
  *
diff --git a/arch/arm/mach-iop13xx/iq81340mc.c b/arch/arm/mach-iop13xx/iq81340mc.c
index 9b5a63f..23dfaff 100644
--- a/arch/arm/mach-iop13xx/iq81340mc.c
+++ b/arch/arm/mach-iop13xx/iq81340mc.c
@@ -30,7 +30,7 @@
 extern int init_atu; /* Flag to select which ATU(s) to initialize / disable */
 
 static int __init
-iq81340mc_pcix_map_irq(struct pci_dev *dev, u8 idsel, u8 pin)
+iq81340mc_pcix_map_irq(const struct pci_dev *dev, u8 idsel, u8 pin)
 {
 	switch (idsel) {
 	case 1:
diff --git a/arch/arm/mach-iop13xx/pci.c b/arch/arm/mach-iop13xx/pci.c
index ba3dae3..251c408 100644
--- a/arch/arm/mach-iop13xx/pci.c
+++ b/arch/arm/mach-iop13xx/pci.c
@@ -39,8 +39,6 @@
 u32 iop13xx_atux_mem_base;
 size_t iop13xx_atue_mem_size;
 size_t iop13xx_atux_mem_size;
-unsigned long iop13xx_pcibios_min_io = 0;
-unsigned long iop13xx_pcibios_min_mem = 0;
 
 EXPORT_SYMBOL(iop13xx_atue_mem_base);
 EXPORT_SYMBOL(iop13xx_atux_mem_base);
@@ -390,7 +388,7 @@
 }
 
 static int
-iop13xx_pcie_map_irq(struct pci_dev *dev, u8 idsel, u8 pin)
+iop13xx_pcie_map_irq(const struct pci_dev *dev, u8 idsel, u8 pin)
 {
 	WARN_ON(idsel != 0);
 
@@ -971,7 +969,8 @@
 	__raw_writel(__raw_readl(IOP13XX_XBG_BECSR) & 3, IOP13XX_XBG_BECSR);
 
 	/* Setup the Min Address for PCI memory... */
-	iop13xx_pcibios_min_mem = IOP13XX_PCIX_LOWER_MEM_BA;
+	pcibios_min_io = 0;
+	pcibios_min_mem = IOP13XX_PCIX_LOWER_MEM_BA;
 
 	/* if Linux is given control of an ATU
 	 * clear out its prior configuration,
diff --git a/arch/arm/mach-iop32x/em7210.c b/arch/arm/mach-iop32x/em7210.c
index 779f924..6cbffbf 100644
--- a/arch/arm/mach-iop32x/em7210.c
+++ b/arch/arm/mach-iop32x/em7210.c
@@ -81,7 +81,7 @@
 #define INTD	IRQ_IOP32X_XINT3
 
 static int __init
-em7210_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+em7210_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[][4] = {
 		/*
diff --git a/arch/arm/mach-iop32x/glantank.c b/arch/arm/mach-iop32x/glantank.c
index c6b6f9c..ceef5d4 100644
--- a/arch/arm/mach-iop32x/glantank.c
+++ b/arch/arm/mach-iop32x/glantank.c
@@ -77,7 +77,7 @@
 #define INTD	IRQ_IOP32X_XINT3
 
 static int __init
-glantank_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+glantank_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[][4] = {
 		/*
diff --git a/arch/arm/mach-iop32x/include/mach/hardware.h b/arch/arm/mach-iop32x/include/mach/hardware.h
index d559c4e..48cb1b2 100644
--- a/arch/arm/mach-iop32x/include/mach/hardware.h
+++ b/arch/arm/mach-iop32x/include/mach/hardware.h
@@ -18,9 +18,6 @@
  * but when we read them, we convert them to virtual addresses. See
  * arch/arm/plat-iop/pci.c.
  */
-#define pcibios_assign_all_busses() 1
-#define PCIBIOS_MIN_IO		0x00000000
-#define PCIBIOS_MIN_MEM		0x00000000
 
 #ifndef __ASSEMBLY__
 void iop32x_init_irq(void);
diff --git a/arch/arm/mach-iop32x/iq31244.c b/arch/arm/mach-iop32x/iq31244.c
index fde962c..3a62514 100644
--- a/arch/arm/mach-iop32x/iq31244.c
+++ b/arch/arm/mach-iop32x/iq31244.c
@@ -103,7 +103,7 @@
  * EP80219/IQ31244 PCI.
  */
 static int __init
-ep80219_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+ep80219_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
@@ -139,7 +139,7 @@
 };
 
 static int __init
-iq31244_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+iq31244_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-iop32x/iq80321.c b/arch/arm/mach-iop32x/iq80321.c
index 3a95950..35b7e69 100644
--- a/arch/arm/mach-iop32x/iq80321.c
+++ b/arch/arm/mach-iop32x/iq80321.c
@@ -71,7 +71,7 @@
  * IQ80321 PCI.
  */
 static int __init
-iq80321_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+iq80321_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c
index 626aa37..1a374ea 100644
--- a/arch/arm/mach-iop32x/n2100.c
+++ b/arch/arm/mach-iop32x/n2100.c
@@ -78,7 +78,7 @@
  * N2100 PCI.
  */
 static int __init
-n2100_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+n2100_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-iop33x/include/mach/hardware.h b/arch/arm/mach-iop33x/include/mach/hardware.h
index 8c10e43..8392853 100644
--- a/arch/arm/mach-iop33x/include/mach/hardware.h
+++ b/arch/arm/mach-iop33x/include/mach/hardware.h
@@ -18,9 +18,6 @@
  * but when we read them, we convert them to virtual addresses.  See
  * arch/arm/mach-iop3xx/iop3xx-pci.c
  */
-#define pcibios_assign_all_busses()	1
-#define PCIBIOS_MIN_IO		0x00000000
-#define PCIBIOS_MIN_MEM		0x00000000
 
 #ifndef __ASSEMBLY__
 void iop33x_init_irq(void);
diff --git a/arch/arm/mach-iop33x/iq80331.c b/arch/arm/mach-iop33x/iq80331.c
index c565f8d..637c027 100644
--- a/arch/arm/mach-iop33x/iq80331.c
+++ b/arch/arm/mach-iop33x/iq80331.c
@@ -54,7 +54,7 @@
  * IQ80331 PCI.
  */
 static int __init
-iq80331_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+iq80331_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-iop33x/iq80332.c b/arch/arm/mach-iop33x/iq80332.c
index 36a9efb..90a0436 100644
--- a/arch/arm/mach-iop33x/iq80332.c
+++ b/arch/arm/mach-iop33x/iq80332.c
@@ -54,7 +54,7 @@
  * IQ80332 PCI.
  */
 static int __init
-iq80332_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+iq80332_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-ixp2000/enp2611.c b/arch/arm/mach-ixp2000/enp2611.c
index 88663ab..62c60ad 100644
--- a/arch/arm/mach-ixp2000/enp2611.c
+++ b/arch/arm/mach-ixp2000/enp2611.c
@@ -148,7 +148,8 @@
 	return pci_scan_bus(sys->busnr, &enp2611_pci_ops, sys);
 }
 
-static int __init enp2611_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init enp2611_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-ixp2000/include/mach/hardware.h b/arch/arm/mach-ixp2000/include/mach/hardware.h
index f033de4..cdaf1db 100644
--- a/arch/arm/mach-ixp2000/include/mach/hardware.h
+++ b/arch/arm/mach-ixp2000/include/mach/hardware.h
@@ -19,16 +19,8 @@
 #ifndef __ASM_ARCH_HARDWARE_H__
 #define __ASM_ARCH_HARDWARE_H__
 
-/*
- * This needs to be platform-specific?
- */
-#define PCIBIOS_MIN_IO          0x00000000
-#define PCIBIOS_MIN_MEM         0x00000000
-
 #include "ixp2000-regs.h"	/* Chipset Registers */
 
-#define pcibios_assign_all_busses() 0
-
 /*
  * Platform helper functions
  */
diff --git a/arch/arm/mach-ixp2000/ixdp2400.c b/arch/arm/mach-ixp2000/ixdp2400.c
index dfffc1e..5bad1a8 100644
--- a/arch/arm/mach-ixp2000/ixdp2400.c
+++ b/arch/arm/mach-ixp2000/ixdp2400.c
@@ -78,7 +78,8 @@
 	return 1;
 }
 
-static int __init ixdp2400_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdp2400_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	if (ixdp2x00_master_npu()) {
 
diff --git a/arch/arm/mach-ixp2000/ixdp2800.c b/arch/arm/mach-ixp2000/ixdp2800.c
index cd4c9bc..3d3cef8 100644
--- a/arch/arm/mach-ixp2000/ixdp2800.c
+++ b/arch/arm/mach-ixp2000/ixdp2800.c
@@ -161,7 +161,8 @@
 	return 1;
 }
 
-static int __init ixdp2800_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdp2800_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	if (ixdp2x00_master_npu()) {
 
diff --git a/arch/arm/mach-ixp2000/ixdp2x01.c b/arch/arm/mach-ixp2000/ixdp2x01.c
index 84835b2..be2a254 100644
--- a/arch/arm/mach-ixp2000/ixdp2x01.c
+++ b/arch/arm/mach-ixp2000/ixdp2x01.c
@@ -252,7 +252,8 @@
 
 #define DEVPIN(dev, pin) ((pin) | ((dev) << 3))
 
-static int __init ixdp2x01_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdp2x01_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	u8 bus = dev->bus->number;
 	u32 devpin = DEVPIN(PCI_SLOT(dev->devfn), pin);
diff --git a/arch/arm/mach-ixp2000/pci.c b/arch/arm/mach-ixp2000/pci.c
index f797c5f..f5098b3 100644
--- a/arch/arm/mach-ixp2000/pci.c
+++ b/arch/arm/mach-ixp2000/pci.c
@@ -196,6 +196,11 @@
 void __init
 ixp2000_pci_preinit(void)
 {
+	pci_set_flags(0);
+
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 #ifndef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO
 	/*
 	 * Configure the PCI unit to properly byteswap I/O transactions,
diff --git a/arch/arm/mach-ixp23xx/include/mach/hardware.h b/arch/arm/mach-ixp23xx/include/mach/hardware.h
index 57b508b..60e55fa 100644
--- a/arch/arm/mach-ixp23xx/include/mach/hardware.h
+++ b/arch/arm/mach-ixp23xx/include/mach/hardware.h
@@ -15,13 +15,9 @@
 #define __ASM_ARCH_HARDWARE_H
 
 /* PCI IO info */
-#define PCIBIOS_MIN_IO		0x00000000
-#define PCIBIOS_MIN_MEM		0xe0000000
 
 #include "ixp23xx.h"
 
-#define pcibios_assign_all_busses()	0
-
 /*
  * Platform helper functions
  */
diff --git a/arch/arm/mach-ixp23xx/ixdp2351.c b/arch/arm/mach-ixp23xx/ixdp2351.c
index 8dcba17..ec028e3 100644
--- a/arch/arm/mach-ixp23xx/ixdp2351.c
+++ b/arch/arm/mach-ixp23xx/ixdp2351.c
@@ -168,7 +168,7 @@
  */
 #define DEVPIN(dev, pin) ((pin) | ((dev) << 3))
 
-static int __init ixdp2351_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdp2351_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	u8 bus = dev->bus->number;
 	u32 devpin = DEVPIN(PCI_SLOT(dev->devfn), pin);
diff --git a/arch/arm/mach-ixp23xx/pci.c b/arch/arm/mach-ixp23xx/pci.c
index 563819a..e6be571 100644
--- a/arch/arm/mach-ixp23xx/pci.c
+++ b/arch/arm/mach-ixp23xx/pci.c
@@ -227,6 +227,11 @@
 
 void __init ixp23xx_pci_preinit(void)
 {
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0xe0000000;
+
+	pci_set_flags(0);
+
 	ixp23xx_pci_common_init();
 
 	hook_fault_code(16+6, ixp23xx_pci_abort_handler, SIGBUS, 0,
diff --git a/arch/arm/mach-ixp23xx/roadrunner.c b/arch/arm/mach-ixp23xx/roadrunner.c
index 8fe0c62..844551d 100644
--- a/arch/arm/mach-ixp23xx/roadrunner.c
+++ b/arch/arm/mach-ixp23xx/roadrunner.c
@@ -56,7 +56,8 @@
 #define INTC_PIN	IXP23XX_GPIO_PIN_11
 #define INTD_PIN	IXP23XX_GPIO_PIN_12
 
-static int __init roadrunner_map_irq(struct pci_dev *dev, u8 idsel, u8 pin)
+static int __init roadrunner_map_irq(const struct pci_dev *dev, u8 idsel,
+	u8 pin)
 {
 	static int pci_card_slot_irq[] = {INTB, INTC, INTD, INTA};
 	static int pmc_card_slot_irq[] = {INTA, INTB, INTC, INTD};
diff --git a/arch/arm/mach-ixp4xx/avila-pci.c b/arch/arm/mach-ixp4xx/avila-pci.c
index 162043f..8fea0a3 100644
--- a/arch/arm/mach-ixp4xx/avila-pci.c
+++ b/arch/arm/mach-ixp4xx/avila-pci.c
@@ -46,7 +46,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init avila_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init avila_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[IRQ_LINES] = {
 		IXP4XX_GPIO_IRQ(INTA),
diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c
index e2e98bb..2131832 100644
--- a/arch/arm/mach-ixp4xx/common-pci.c
+++ b/arch/arm/mach-ixp4xx/common-pci.c
@@ -346,6 +346,11 @@
 {
 	unsigned long cpuid = read_cpuid_id();
 
+#ifdef CONFIG_IXP4XX_INDIRECT_PCI
+	pcibios_min_mem = 0x10000000; /* 1 GB of indirect PCI MMIO space */
+#else
+	pcibios_min_mem = 0x48000000; /* 64 MB of PCI MMIO space */
+#endif
 	/*
 	 * Determine which PCI read method to use.
 	 * Rev 0 IXP425 requires workaround.
diff --git a/arch/arm/mach-ixp4xx/coyote-pci.c b/arch/arm/mach-ixp4xx/coyote-pci.c
index 37fda7d..71f5c9c 100644
--- a/arch/arm/mach-ixp4xx/coyote-pci.c
+++ b/arch/arm/mach-ixp4xx/coyote-pci.c
@@ -37,7 +37,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init coyote_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init coyote_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (slot == SLOT0_DEVID)
 		return IXP4XX_GPIO_IRQ(SLOT0_INTA);
diff --git a/arch/arm/mach-ixp4xx/dsmg600-pci.c b/arch/arm/mach-ixp4xx/dsmg600-pci.c
index c761201..0532510 100644
--- a/arch/arm/mach-ixp4xx/dsmg600-pci.c
+++ b/arch/arm/mach-ixp4xx/dsmg600-pci.c
@@ -44,7 +44,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init dsmg600_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init dsmg600_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[MAX_DEV][IRQ_LINES] = {
 		{ IXP4XX_GPIO_IRQ(INTE), -1, -1 },
diff --git a/arch/arm/mach-ixp4xx/fsg-pci.c b/arch/arm/mach-ixp4xx/fsg-pci.c
index 44ccde9..d2ac803 100644
--- a/arch/arm/mach-ixp4xx/fsg-pci.c
+++ b/arch/arm/mach-ixp4xx/fsg-pci.c
@@ -38,7 +38,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init fsg_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init fsg_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[IRQ_LINES] = {
 		IXP4XX_GPIO_IRQ(INTC),
diff --git a/arch/arm/mach-ixp4xx/gateway7001-pci.c b/arch/arm/mach-ixp4xx/gateway7001-pci.c
index fc11241..76581fb 100644
--- a/arch/arm/mach-ixp4xx/gateway7001-pci.c
+++ b/arch/arm/mach-ixp4xx/gateway7001-pci.c
@@ -35,7 +35,8 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init gateway7001_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init gateway7001_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	if (slot == 1)
 		return IRQ_IXP4XX_GPIO11;
diff --git a/arch/arm/mach-ixp4xx/goramo_mlr.c b/arch/arm/mach-ixp4xx/goramo_mlr.c
index 5f00ad2..7548d9a 100644
--- a/arch/arm/mach-ixp4xx/goramo_mlr.c
+++ b/arch/arm/mach-ixp4xx/goramo_mlr.c
@@ -462,7 +462,7 @@
 	}
 }
 
-static int __init gmlr_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init gmlr_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	switch(slot) {
 	case SLOT_ETHA:	return IXP4XX_GPIO_IRQ(GPIO_IRQ_ETHA);
diff --git a/arch/arm/mach-ixp4xx/gtwx5715-pci.c b/arch/arm/mach-ixp4xx/gtwx5715-pci.c
index 38cc072..d68fc06 100644
--- a/arch/arm/mach-ixp4xx/gtwx5715-pci.c
+++ b/arch/arm/mach-ixp4xx/gtwx5715-pci.c
@@ -49,7 +49,7 @@
 }
 
 
-static int __init gtwx5715_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init gtwx5715_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int rc = -1;
 
diff --git a/arch/arm/mach-ixp4xx/include/mach/hardware.h b/arch/arm/mach-ixp4xx/include/mach/hardware.h
index 8138371..c30e7e9 100644
--- a/arch/arm/mach-ixp4xx/include/mach/hardware.h
+++ b/arch/arm/mach-ixp4xx/include/mach/hardware.h
@@ -17,19 +17,14 @@
 #ifndef __ASM_ARCH_HARDWARE_H__
 #define __ASM_ARCH_HARDWARE_H__
 
-#define PCIBIOS_MIN_IO		0x00001000
 #ifdef CONFIG_IXP4XX_INDIRECT_PCI
-#define PCIBIOS_MIN_MEM		0x10000000 /* 1 GB of indirect PCI MMIO space */
 #define PCIBIOS_MAX_MEM		0x4FFFFFFF
 #else
-#define PCIBIOS_MIN_MEM		0x48000000 /* 64 MB of PCI MMIO space */
 #define PCIBIOS_MAX_MEM		0x4BFFFFFF
 #endif
 
 #define ARCH_HAS_DMA_SET_COHERENT_MASK
 
-#define pcibios_assign_all_busses()	1
-
 /* Register locations and bits */
 #include "ixp4xx-regs.h"
 
diff --git a/arch/arm/mach-ixp4xx/ixdp425-pci.c b/arch/arm/mach-ixp4xx/ixdp425-pci.c
index 58f4004..fffd8c5 100644
--- a/arch/arm/mach-ixp4xx/ixdp425-pci.c
+++ b/arch/arm/mach-ixp4xx/ixdp425-pci.c
@@ -43,7 +43,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init ixdp425_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdp425_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[IRQ_LINES] = {
 		IXP4XX_GPIO_IRQ(INTA),
diff --git a/arch/arm/mach-ixp4xx/ixdpg425-pci.c b/arch/arm/mach-ixp4xx/ixdpg425-pci.c
index e64f6d0..34efe75 100644
--- a/arch/arm/mach-ixp4xx/ixdpg425-pci.c
+++ b/arch/arm/mach-ixp4xx/ixdpg425-pci.c
@@ -31,7 +31,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init ixdpg425_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init ixdpg425_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (slot == 12 || slot == 13)
 		return IRQ_IXP4XX_GPIO7;
diff --git a/arch/arm/mach-ixp4xx/nas100d-pci.c b/arch/arm/mach-ixp4xx/nas100d-pci.c
index 428d120..5434ccf 100644
--- a/arch/arm/mach-ixp4xx/nas100d-pci.c
+++ b/arch/arm/mach-ixp4xx/nas100d-pci.c
@@ -41,7 +41,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init nas100d_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init nas100d_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[MAX_DEV][IRQ_LINES] = {
 		{ IXP4XX_GPIO_IRQ(INTA), -1, -1 },
diff --git a/arch/arm/mach-ixp4xx/nslu2-pci.c b/arch/arm/mach-ixp4xx/nslu2-pci.c
index 2e85f76..b571605 100644
--- a/arch/arm/mach-ixp4xx/nslu2-pci.c
+++ b/arch/arm/mach-ixp4xx/nslu2-pci.c
@@ -38,7 +38,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init nslu2_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init nslu2_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	static int pci_irq_table[IRQ_LINES] = {
 		IXP4XX_GPIO_IRQ(INTA),
diff --git a/arch/arm/mach-ixp4xx/vulcan-pci.c b/arch/arm/mach-ixp4xx/vulcan-pci.c
index 03bdec5..0bc3f34 100644
--- a/arch/arm/mach-ixp4xx/vulcan-pci.c
+++ b/arch/arm/mach-ixp4xx/vulcan-pci.c
@@ -43,7 +43,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init vulcan_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init vulcan_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (slot == 1)
 		return IXP4XX_GPIO_IRQ(INTA);
diff --git a/arch/arm/mach-ixp4xx/wg302v2-pci.c b/arch/arm/mach-ixp4xx/wg302v2-pci.c
index 17f3cf5..f27dfcf 100644
--- a/arch/arm/mach-ixp4xx/wg302v2-pci.c
+++ b/arch/arm/mach-ixp4xx/wg302v2-pci.c
@@ -35,7 +35,7 @@
 	ixp4xx_pci_preinit();
 }
 
-static int __init wg302v2_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init wg302v2_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (slot == 1)
 		return IRQ_IXP4XX_GPIO8;
diff --git a/arch/arm/mach-kirkwood/include/mach/hardware.h b/arch/arm/mach-kirkwood/include/mach/hardware.h
index cde8528..742b74f 100644
--- a/arch/arm/mach-kirkwood/include/mach/hardware.h
+++ b/arch/arm/mach-kirkwood/include/mach/hardware.h
@@ -11,11 +11,4 @@
 
 #include "kirkwood.h"
 
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO			0x00001000
-#define PCIBIOS_MIN_MEM			0x01000000
-#define PCIMEM_BASE			KIRKWOOD_PCIE_MEM_PHYS_BASE /* mem base for VGA */
-
-
 #endif
diff --git a/arch/arm/mach-kirkwood/pcie.c b/arch/arm/mach-kirkwood/pcie.c
index ca294ff..74b992d 100644
--- a/arch/arm/mach-kirkwood/pcie.c
+++ b/arch/arm/mach-kirkwood/pcie.c
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/mbus.h>
+#include <video/vga.h>
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
 #include <plat/pcie.h>
@@ -244,7 +245,8 @@
 	return bus;
 }
 
-static int __init kirkwood_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init kirkwood_pcie_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	struct pcie_port *pp = bus_to_port(dev->bus);
 
@@ -271,6 +273,8 @@
 
 void __init kirkwood_pcie_init(unsigned int portmask)
 {
+	vga_base = KIRKWOOD_PCIE_MEM_PHYS_BASE;
+
 	if (portmask & KW_PCIE0)
 		add_pcie_port(0, PCIE_VIRT_BASE);
 
diff --git a/arch/arm/mach-ks8695/board-dsm320.c b/arch/arm/mach-ks8695/board-dsm320.c
index ada92b6..1338cb3 100644
--- a/arch/arm/mach-ks8695/board-dsm320.c
+++ b/arch/arm/mach-ks8695/board-dsm320.c
@@ -34,7 +34,7 @@
 #include "generic.h"
 
 #ifdef CONFIG_PCI
-static int dsm320_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int dsm320_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	switch (slot) {
 	case 0:
diff --git a/arch/arm/mach-ks8695/board-micrel.c b/arch/arm/mach-ks8695/board-micrel.c
index c7ad09b..e2e3cba 100644
--- a/arch/arm/mach-ks8695/board-micrel.c
+++ b/arch/arm/mach-ks8695/board-micrel.c
@@ -24,7 +24,7 @@
 #include "generic.h"
 
 #ifdef CONFIG_PCI
-static int micrel_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int micrel_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	return KS8695_IRQ_EXTERN0;
 }
diff --git a/arch/arm/mach-ks8695/include/mach/devices.h b/arch/arm/mach-ks8695/include/mach/devices.h
index 2744fec..85a3c9a 100644
--- a/arch/arm/mach-ks8695/include/mach/devices.h
+++ b/arch/arm/mach-ks8695/include/mach/devices.h
@@ -30,7 +30,7 @@
 
 struct ks8695_pci_cfg {
 	short mode;
-	int (*map_irq)(struct pci_dev *, u8, u8);
+	int (*map_irq)(const struct pci_dev *, u8, u8);
 };
 extern __init void ks8695_init_pci(struct ks8695_pci_cfg *);
 
diff --git a/arch/arm/mach-ks8695/include/mach/hardware.h b/arch/arm/mach-ks8695/include/mach/hardware.h
index e0f911d..5e0c388 100644
--- a/arch/arm/mach-ks8695/include/mach/hardware.h
+++ b/arch/arm/mach-ks8695/include/mach/hardware.h
@@ -42,13 +42,4 @@
 #define KS8695_PCIIO_PA		0x80000000
 #define KS8695_PCIIO_SIZE	SZ_64K
 
-
-/*
- * PCI support
- */
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO		0
-#define PCIBIOS_MIN_MEM		0
-
 #endif
diff --git a/arch/arm/mach-ks8695/pci.c b/arch/arm/mach-ks8695/pci.c
index 5fcd082..c7c9a18 100644
--- a/arch/arm/mach-ks8695/pci.c
+++ b/arch/arm/mach-ks8695/pci.c
@@ -317,6 +317,9 @@
 		return;
 	}
 
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 	printk(KERN_INFO "PCI: Initialising\n");
 	ks8695_show_pciregs();
 
diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c
index da0e649..1e02751 100644
--- a/arch/arm/mach-lpc32xx/clock.c
+++ b/arch/arm/mach-lpc32xx/clock.c
@@ -1077,7 +1077,7 @@
 	_REGISTER_CLOCK("lpc32xx-nand.0", "nand_ck", clk_nand)
 	_REGISTER_CLOCK("tbd", "i2s0_ck", clk_i2s0)
 	_REGISTER_CLOCK("tbd", "i2s1_ck", clk_i2s1)
-	_REGISTER_CLOCK("lpc32xx-ts", NULL, clk_tsc)
+	_REGISTER_CLOCK("ts-lpc32xx", NULL, clk_tsc)
 	_REGISTER_CLOCK("dev:mmc0", "MCLK", clk_mmc)
 	_REGISTER_CLOCK("lpc-net.0", NULL, clk_net)
 	_REGISTER_CLOCK("dev:clcd", NULL, clk_lcd)
diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c
index ee24dc2..205b2db 100644
--- a/arch/arm/mach-lpc32xx/common.c
+++ b/arch/arm/mach-lpc32xx/common.c
@@ -95,6 +95,48 @@
 	},
 };
 
+/* TSC (Touch Screen Controller) */
+
+static struct resource lpc32xx_tsc_resources[] = {
+	{
+		.start = LPC32XX_ADC_BASE,
+		.end = LPC32XX_ADC_BASE + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = IRQ_LPC32XX_TS_IRQ,
+		.end = IRQ_LPC32XX_TS_IRQ,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device lpc32xx_tsc_device = {
+	.name =  "ts-lpc32xx",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(lpc32xx_tsc_resources),
+	.resource = lpc32xx_tsc_resources,
+};
+
+/* RTC */
+
+static struct resource lpc32xx_rtc_resources[] = {
+	{
+		.start = LPC32XX_RTC_BASE,
+		.end = LPC32XX_RTC_BASE + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	},{
+		.start = IRQ_LPC32XX_RTC,
+		.end = IRQ_LPC32XX_RTC,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device lpc32xx_rtc_device = {
+	.name =  "rtc-lpc32xx",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(lpc32xx_rtc_resources),
+	.resource = lpc32xx_rtc_resources,
+};
+
 /*
  * Returns the unique ID for the device
  */
diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h
index f82211f..5583f52 100644
--- a/arch/arm/mach-lpc32xx/common.h
+++ b/arch/arm/mach-lpc32xx/common.h
@@ -28,6 +28,8 @@
 extern struct platform_device lpc32xx_i2c0_device;
 extern struct platform_device lpc32xx_i2c1_device;
 extern struct platform_device lpc32xx_i2c2_device;
+extern struct platform_device lpc32xx_tsc_device;
+extern struct platform_device lpc32xx_rtc_device;
 
 /*
  * Other arch specific structures and functions
diff --git a/arch/arm/mach-lpc32xx/include/mach/clkdev.h b/arch/arm/mach-lpc32xx/include/mach/clkdev.h
deleted file mode 100644
index 9bf0637..0000000
--- a/arch/arm/mach-lpc32xx/include/mach/clkdev.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * arch/arm/mach-lpc32xx/include/mach/clkdev.h
- *
- * Author: Kevin Wells <kevin.wells@nxp.com>
- *
- * Copyright (C) 2010 NXP Semiconductors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ASM_ARCH_CLKDEV_H
-#define __ASM_ARCH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig
index 67793a6..56ef5f6 100644
--- a/arch/arm/mach-mmp/Kconfig
+++ b/arch/arm/mach-mmp/Kconfig
@@ -77,6 +77,13 @@
 	  Say 'Y' here if you want to support the Marvell PXA168-based
 	  Teton BGA Development Board.
 
+config MACH_SHEEVAD
+	bool "Marvell's PXA168 GuruPlug Display (gplugD) Board"
+	select CPU_PXA168
+	help
+	  Say 'Y' here if you want to support the Marvell PXA168-based
+	  GuruPlug Display (gplugD) Board
+
 endmenu
 
 config CPU_PXA168
diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index 5c68382..b0ac942 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -19,3 +19,4 @@
 obj-$(CONFIG_MACH_FLINT)	+= flint.o
 obj-$(CONFIG_MACH_MARVELL_JASPER) += jasper.o
 obj-$(CONFIG_MACH_TETON_BGA)	+= teton_bga.o
+obj-$(CONFIG_MACH_SHEEVAD)	+= gplugd.o
diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c
index 886e056..7c6f95f 100644
--- a/arch/arm/mach-mmp/clock.c
+++ b/arch/arm/mach-mmp/clock.c
@@ -88,3 +88,18 @@
 	return rate;
 }
 EXPORT_SYMBOL(clk_get_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	if (clk->ops->setrate) {
+		spin_lock_irqsave(&clocks_lock, flags);
+		ret = clk->ops->setrate(clk, rate);
+		spin_unlock_irqrestore(&clocks_lock, flags);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
diff --git a/arch/arm/mach-mmp/clock.h b/arch/arm/mach-mmp/clock.h
index 9b027d7..3143e99 100644
--- a/arch/arm/mach-mmp/clock.h
+++ b/arch/arm/mach-mmp/clock.h
@@ -12,6 +12,7 @@
 	void			(*enable)(struct clk *);
 	void			(*disable)(struct clk *);
 	unsigned long		(*getrate)(struct clk *);
+	int			(*setrate)(struct clk *, unsigned long);
 };
 
 struct clk {
diff --git a/arch/arm/mach-mmp/gplugd.c b/arch/arm/mach-mmp/gplugd.c
new file mode 100644
index 0000000..c070c24
--- /dev/null
+++ b/arch/arm/mach-mmp/gplugd.c
@@ -0,0 +1,189 @@
+/*
+ *  linux/arch/arm/mach-mmp/gplugd.c
+ *
+ *  Support for the Marvell PXA168-based GuruPlug Display (gplugD) Platform.
+ *
+ *  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
+ *  publishhed by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <mach/gpio.h>
+#include <mach/pxa168.h>
+#include <mach/mfp-pxa168.h>
+#include <mach/mfp-gplugd.h>
+
+#include "common.h"
+
+static unsigned long gplugd_pin_config[] __initdata = {
+	/* UART3 */
+	GPIO8_UART3_SOUT,
+	GPIO9_UART3_SIN,
+	GPI1O_UART3_CTS,
+	GPI11_UART3_RTS,
+
+	/* MMC2 */
+	GPIO28_MMC2_CMD,
+	GPIO29_MMC2_CLK,
+	GPIO30_MMC2_DAT0,
+	GPIO31_MMC2_DAT1,
+	GPIO32_MMC2_DAT2,
+	GPIO33_MMC2_DAT3,
+
+	/* LCD & HDMI clock selection GPIO: 0: 74.176MHz, 1: 74.25 MHz */
+	GPIO35_GPIO,
+	GPIO36_GPIO, /* CEC Interrupt */
+
+	/* MMC1 */
+	GPIO43_MMC1_CLK,
+	GPIO49_MMC1_CMD,
+	GPIO41_MMC1_DAT0,
+	GPIO40_MMC1_DAT1,
+	GPIO52_MMC1_DAT2,
+	GPIO51_MMC1_DAT3,
+	GPIO53_MMC1_CD,
+
+	/* LCD */
+	GPIO56_LCD_FCLK_RD,
+	GPIO57_LCD_LCLK_A0,
+	GPIO58_LCD_PCLK_WR,
+	GPIO59_LCD_DENA_BIAS,
+	GPIO60_LCD_DD0,
+	GPIO61_LCD_DD1,
+	GPIO62_LCD_DD2,
+	GPIO63_LCD_DD3,
+	GPIO64_LCD_DD4,
+	GPIO65_LCD_DD5,
+	GPIO66_LCD_DD6,
+	GPIO67_LCD_DD7,
+	GPIO68_LCD_DD8,
+	GPIO69_LCD_DD9,
+	GPIO70_LCD_DD10,
+	GPIO71_LCD_DD11,
+	GPIO72_LCD_DD12,
+	GPIO73_LCD_DD13,
+	GPIO74_LCD_DD14,
+	GPIO75_LCD_DD15,
+	GPIO76_LCD_DD16,
+	GPIO77_LCD_DD17,
+	GPIO78_LCD_DD18,
+	GPIO79_LCD_DD19,
+	GPIO80_LCD_DD20,
+	GPIO81_LCD_DD21,
+	GPIO82_LCD_DD22,
+	GPIO83_LCD_DD23,
+
+	/* GPIO */
+	GPIO84_GPIO,
+	GPIO85_GPIO,
+
+	/* Fast-Ethernet*/
+	GPIO86_TX_CLK,
+	GPIO87_TX_EN,
+	GPIO88_TX_DQ3,
+	GPIO89_TX_DQ2,
+	GPIO90_TX_DQ1,
+	GPIO91_TX_DQ0,
+	GPIO92_MII_CRS,
+	GPIO93_MII_COL,
+	GPIO94_RX_CLK,
+	GPIO95_RX_ER,
+	GPIO96_RX_DQ3,
+	GPIO97_RX_DQ2,
+	GPIO98_RX_DQ1,
+	GPIO99_RX_DQ0,
+	GPIO100_MII_MDC,
+	GPIO101_MII_MDIO,
+	GPIO103_RX_DV,
+	GPIO104_GPIO,     /* Reset PHY */
+
+	/* RTC interrupt */
+	GPIO102_GPIO,
+
+	/* I2C */
+	GPIO105_CI2C_SDA,
+	GPIO106_CI2C_SCL,
+
+	/* Select JTAG */
+	GPIO109_GPIO,
+
+	/* I2S */
+	GPIO114_I2S_FRM,
+	GPIO115_I2S_BCLK,
+	GPIO116_I2S_TXD
+};
+
+static struct i2c_board_info gplugd_i2c_board_info[] = {
+	{
+		.type = "isl1208",
+		.addr = 0x6F,
+	}
+};
+
+/* Bring PHY out of reset by setting GPIO 104 */
+static int gplugd_eth_init(void)
+{
+	if (unlikely(gpio_request(104, "ETH_RESET_N"))) {
+		printk(KERN_ERR "Can't get hold of GPIO 104 to bring Ethernet "
+				"PHY out of reset\n");
+		return -EIO;
+	}
+
+	gpio_direction_output(104, 1);
+	gpio_free(104);
+	return 0;
+}
+
+struct pxa168_eth_platform_data gplugd_eth_platform_data = {
+	.port_number = 0,
+	.phy_addr    = 0,
+	.speed       = 0, /* Autonagotiation */
+	.init        = gplugd_eth_init,
+};
+
+static void __init select_disp_freq(void)
+{
+	/* set GPIO 35 & clear GPIO 85 to set LCD External Clock to 74.25 MHz */
+	if (unlikely(gpio_request(35, "DISP_FREQ_SEL"))) {
+		printk(KERN_ERR "Can't get hold of GPIO 35 to select display "
+				"frequency\n");
+	} else {
+		gpio_direction_output(35, 1);
+		gpio_free(104);
+	}
+
+	if (unlikely(gpio_request(85, "DISP_FREQ_SEL_2"))) {
+		printk(KERN_ERR "Can't get hold of GPIO 85 to select display "
+				"frequency\n");
+	} else {
+		gpio_direction_output(85, 0);
+		gpio_free(104);
+	}
+}
+
+static void __init gplugd_init(void)
+{
+	mfp_config(ARRAY_AND_SIZE(gplugd_pin_config));
+
+	select_disp_freq();
+
+	/* on-chip devices */
+	pxa168_add_uart(3);
+	pxa168_add_ssp(0);
+	pxa168_add_twsi(0, NULL, ARRAY_AND_SIZE(gplugd_i2c_board_info));
+
+	pxa168_add_eth(&gplugd_eth_platform_data);
+}
+
+MACHINE_START(SHEEVAD, "PXA168-based GuruPlug Display (gplugD) Platform")
+	.map_io		= mmp_map_io,
+	.nr_irqs	= IRQ_BOARD_START,
+	.init_irq       = pxa168_init_irq,
+	.timer          = &pxa168_timer,
+	.init_machine   = gplugd_init,
+MACHINE_END
diff --git a/arch/arm/mach-mmp/include/mach/clkdev.h b/arch/arm/mach-mmp/include/mach/clkdev.h
deleted file mode 100644
index 2fb354e..0000000
--- a/arch/arm/mach-mmp/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif /* __ASM_MACH_CLKDEV_H */
diff --git a/arch/arm/mach-mmp/include/mach/mfp-gplugd.h b/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
new file mode 100644
index 0000000..b8cf38d
--- /dev/null
+++ b/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
@@ -0,0 +1,52 @@
+/*
+ * linux/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
+ *
+ *   MFP definitions used in gplugD
+ *
+ * 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 __MACH_MFP_GPLUGD_H
+#define __MACH_MFP_GPLUGD_H
+
+#include <plat/mfp.h>
+#include <mach/mfp.h>
+
+/* UART3 */
+#define GPIO8_UART3_SOUT       MFP_CFG(GPIO8, AF2)
+#define GPIO9_UART3_SIN        MFP_CFG(GPIO9, AF2)
+#define GPI1O_UART3_CTS        MFP_CFG(GPIO10, AF2)
+#define GPI11_UART3_RTS        MFP_CFG(GPIO11, AF2)
+
+/* MMC2 */
+#define	GPIO28_MMC2_CMD		MFP_CFG_DRV(GPIO28, AF6, FAST)
+#define	GPIO29_MMC2_CLK		MFP_CFG_DRV(GPIO29, AF6, FAST)
+#define	GPIO30_MMC2_DAT0	MFP_CFG_DRV(GPIO30, AF6, FAST)
+#define	GPIO31_MMC2_DAT1	MFP_CFG_DRV(GPIO31, AF6, FAST)
+#define	GPIO32_MMC2_DAT2	MFP_CFG_DRV(GPIO32, AF6, FAST)
+#define	GPIO33_MMC2_DAT3	MFP_CFG_DRV(GPIO33, AF6, FAST)
+
+/* I2S */
+#undef GPIO114_I2S_FRM
+#undef GPIO115_I2S_BCLK
+
+#define GPIO114_I2S_FRM	        MFP_CFG_DRV(GPIO114, AF1, FAST)
+#define GPIO115_I2S_BCLK        MFP_CFG_DRV(GPIO115, AF1, FAST)
+#define GPIO116_I2S_TXD         MFP_CFG_DRV(GPIO116, AF1, FAST)
+
+/* MMC4 */
+#define GPIO125_MMC4_DAT3       MFP_CFG_DRV(GPIO125, AF7, FAST)
+#define GPIO126_MMC4_DAT2       MFP_CFG_DRV(GPIO126, AF7, FAST)
+#define GPIO127_MMC4_DAT1       MFP_CFG_DRV(GPIO127, AF7, FAST)
+#define GPIO0_2_MMC4_DAT0       MFP_CFG_DRV(GPIO0_2, AF7, FAST)
+#define GPIO1_2_MMC4_CMD        MFP_CFG_DRV(GPIO1_2, AF7, FAST)
+#define GPIO2_2_MMC4_CLK        MFP_CFG_DRV(GPIO2_2, AF7, FAST)
+
+/* OTG GPIO */
+#define GPIO_USB_OTG_PEN        18
+#define GPIO_USB_OIDIR          20
+
+/* Other GPIOs are 35, 84, 85 */
+#endif /* __MACH_MFP_GPLUGD_H */
diff --git a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
index 713be15..8c78232 100644
--- a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
+++ b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
@@ -305,4 +305,23 @@
 #define GPIO112_KP_MKOUT6       MFP_CFG(GPIO112, AF7)
 #define GPIO121_KP_MKIN4        MFP_CFG(GPIO121, AF7)
 
+/* Fast Ethernet */
+#define GPIO86_TX_CLK		MFP_CFG(GPIO86, AF5)
+#define GPIO87_TX_EN		MFP_CFG(GPIO87, AF5)
+#define GPIO88_TX_DQ3		MFP_CFG(GPIO88, AF5)
+#define GPIO89_TX_DQ2		MFP_CFG(GPIO89, AF5)
+#define GPIO90_TX_DQ1		MFP_CFG(GPIO90, AF5)
+#define GPIO91_TX_DQ0		MFP_CFG(GPIO91, AF5)
+#define GPIO92_MII_CRS		MFP_CFG(GPIO92, AF5)
+#define GPIO93_MII_COL		MFP_CFG(GPIO93, AF5)
+#define GPIO94_RX_CLK		MFP_CFG(GPIO94, AF5)
+#define GPIO95_RX_ER		MFP_CFG(GPIO95, AF5)
+#define GPIO96_RX_DQ3		MFP_CFG(GPIO96, AF5)
+#define GPIO97_RX_DQ2		MFP_CFG(GPIO97, AF5)
+#define GPIO98_RX_DQ1		MFP_CFG(GPIO98, AF5)
+#define GPIO99_RX_DQ0		MFP_CFG(GPIO99, AF5)
+#define GPIO100_MII_MDC		MFP_CFG(GPIO100, AF5)
+#define GPIO101_MII_MDIO	MFP_CFG(GPIO101, AF5)
+#define GPIO103_RX_DV		MFP_CFG(GPIO103, AF5)
+
 #endif /* __ASM_MACH_MFP_PXA168_H */
diff --git a/arch/arm/mach-mmp/include/mach/pxa168.h b/arch/arm/mach-mmp/include/mach/pxa168.h
index a52b3d2..7f00584 100644
--- a/arch/arm/mach-mmp/include/mach/pxa168.h
+++ b/arch/arm/mach-mmp/include/mach/pxa168.h
@@ -14,9 +14,11 @@
 #include <video/pxa168fb.h>
 #include <plat/pxa27x_keypad.h>
 #include <mach/cputype.h>
+#include <linux/pxa168_eth.h>
 
 extern struct pxa_device_desc pxa168_device_uart1;
 extern struct pxa_device_desc pxa168_device_uart2;
+extern struct pxa_device_desc pxa168_device_uart3;
 extern struct pxa_device_desc pxa168_device_twsi0;
 extern struct pxa_device_desc pxa168_device_twsi1;
 extern struct pxa_device_desc pxa168_device_pwm1;
@@ -31,6 +33,7 @@
 extern struct pxa_device_desc pxa168_device_nand;
 extern struct pxa_device_desc pxa168_device_fb;
 extern struct pxa_device_desc pxa168_device_keypad;
+extern struct pxa_device_desc pxa168_device_eth;
 
 static inline int pxa168_add_uart(int id)
 {
@@ -39,6 +42,7 @@
 	switch (id) {
 	case 1: d = &pxa168_device_uart1; break;
 	case 2: d = &pxa168_device_uart2; break;
+	case 3: d = &pxa168_device_uart3; break;
 	}
 
 	if (d == NULL)
@@ -117,4 +121,8 @@
 	return pxa_register_device(&pxa168_device_keypad, data, sizeof(*data));
 }
 
+static inline int pxa168_add_eth(struct pxa168_eth_platform_data *data)
+{
+	return pxa_register_device(&pxa168_device_eth, data, sizeof(*data));
+}
 #endif /* __ASM_MACH_PXA168_H */
diff --git a/arch/arm/mach-mmp/include/mach/regs-apmu.h b/arch/arm/mach-mmp/include/mach/regs-apmu.h
index f7011ef..8447ac6 100644
--- a/arch/arm/mach-mmp/include/mach/regs-apmu.h
+++ b/arch/arm/mach-mmp/include/mach/regs-apmu.h
@@ -29,6 +29,7 @@
 #define APMU_BUS	APMU_REG(0x06c)
 #define APMU_SDH2	APMU_REG(0x0e8)
 #define APMU_SDH3	APMU_REG(0x0ec)
+#define APMU_ETH	APMU_REG(0x0fc)
 
 #define APMU_FNCLK_EN	(1 << 4)
 #define APMU_AXICLK_EN	(1 << 3)
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index ab9f999..0156f53 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -66,6 +66,7 @@
 /* APB peripheral clocks */
 static APBC_CLK(uart1, PXA168_UART1, 1, 14745600);
 static APBC_CLK(uart2, PXA168_UART2, 1, 14745600);
+static APBC_CLK(uart3, PXA168_UART3, 1, 14745600);
 static APBC_CLK(twsi0, PXA168_TWSI0, 1, 33000000);
 static APBC_CLK(twsi1, PXA168_TWSI1, 1, 33000000);
 static APBC_CLK(pwm1, PXA168_PWM1, 1, 13000000);
@@ -81,11 +82,13 @@
 
 static APMU_CLK(nand, NAND, 0x19b, 156000000);
 static APMU_CLK(lcd, LCD, 0x7f, 312000000);
+static APMU_CLK(eth, ETH, 0x09, 0);
 
 /* device and clock bindings */
 static struct clk_lookup pxa168_clkregs[] = {
 	INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
 	INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL),
+	INIT_CLKREG(&clk_uart3, "pxa2xx-uart.2", NULL),
 	INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL),
 	INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL),
 	INIT_CLKREG(&clk_pwm1, "pxa168-pwm.0", NULL),
@@ -100,6 +103,7 @@
 	INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
 	INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL),
 	INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL),
+	INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"),
 };
 
 static int __init pxa168_init(void)
@@ -149,6 +153,7 @@
 /* on-chip devices */
 PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 21, 22);
 PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 23, 24);
+PXA168_DEVICE(uart3, "pxa2xx-uart", 2, UART3, 0xd4026000, 0x30, 23, 24);
 PXA168_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28);
 PXA168_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28);
 PXA168_DEVICE(pwm1, "pxa168-pwm", 0, NONE, 0xd401a000, 0x10);
@@ -163,3 +168,4 @@
 PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
 PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8);
 PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c);
+PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c
index e411039..6bd37a2 100644
--- a/arch/arm/mach-mmp/ttc_dkb.c
+++ b/arch/arm/mach-mmp/ttc_dkb.c
@@ -15,6 +15,8 @@
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/onenand.h>
 #include <linux/interrupt.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/gpio.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -25,7 +27,17 @@
 
 #include "common.h"
 
-#define TTCDKB_NR_IRQS		(IRQ_BOARD_START + 24)
+#define TTCDKB_GPIO_EXT0(x)	(NR_BUILTIN_GPIO + ((x < 0) ? 0 :	\
+				((x < 16) ? x : 15)))
+#define TTCDKB_GPIO_EXT1(x)	(NR_BUILTIN_GPIO + 16 + ((x < 0) ? 0 :	\
+				((x < 16) ? x : 15)))
+
+/*
+ * 16 board interrupts -- MAX7312 GPIO expander
+ * 16 board interrupts -- PCA9575 GPIO expander
+ * 24 board interrupts -- 88PM860x PMIC
+ */
+#define TTCDKB_NR_IRQS		(IRQ_BOARD_START + 16 + 16 + 24)
 
 static unsigned long ttc_dkb_pin_config[] __initdata = {
 	/* UART2 */
@@ -113,6 +125,22 @@
 	&ttc_dkb_device_onenand,
 };
 
+static struct pca953x_platform_data max7312_data[] = {
+	{
+		.gpio_base	= TTCDKB_GPIO_EXT0(0),
+		.irq_base	= IRQ_BOARD_START,
+	},
+};
+
+static struct i2c_board_info ttc_dkb_i2c_info[] = {
+	{
+		.type		= "max7312",
+		.addr		= 0x23,
+		.irq		= IRQ_GPIO(80),
+		.platform_data	= &max7312_data,
+	},
+};
+
 static void __init ttc_dkb_init(void)
 {
 	mfp_config(ARRAY_AND_SIZE(ttc_dkb_pin_config));
@@ -121,6 +149,7 @@
 	pxa910_add_uart(1);
 
 	/* off-chip devices */
+	pxa910_add_twsi(0, NULL, ARRAY_AND_SIZE(ttc_dkb_i2c_info));
 	platform_add_devices(ARRAY_AND_SIZE(ttc_dkb_devices));
 }
 
diff --git a/arch/arm/mach-msm/include/mach/clkdev.h b/arch/arm/mach-msm/include/mach/clkdev.h
deleted file mode 100644
index f87a57b..0000000
--- a/arch/arm/mach-msm/include/mach/clkdev.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- */
-#ifndef __ASM_ARCH_MSM_CLKDEV_H
-#define __ASM_ARCH_MSM_CLKDEV_H
-
-struct clk;
-
-static inline int __clk_get(struct clk *clk) { return 1; }
-static inline void __clk_put(struct clk *clk) { }
-#endif
diff --git a/arch/arm/mach-mv78xx0/include/mach/hardware.h b/arch/arm/mach-mv78xx0/include/mach/hardware.h
index 5d88755..67cab0a 100644
--- a/arch/arm/mach-mv78xx0/include/mach/hardware.h
+++ b/arch/arm/mach-mv78xx0/include/mach/hardware.h
@@ -11,11 +11,4 @@
 
 #include "mv78xx0.h"
 
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO			0x00001000
-#define PCIBIOS_MIN_MEM			0x01000000
-#define PCIMEM_BASE			MV78XX0_PCIE_MEM_PHYS_BASE /* mem base for VGA */
-
-
 #endif
diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c
index f27c7d2..c51af1c 100644
--- a/arch/arm/mach-mv78xx0/pcie.c
+++ b/arch/arm/mach-mv78xx0/pcie.c
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/mbus.h>
+#include <video/vga.h>
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
 #include <plat/pcie.h>
@@ -259,7 +260,8 @@
 	return bus;
 }
 
-static int __init mv78xx0_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init mv78xx0_pcie_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	struct pcie_port *pp = bus_to_port(dev->bus->number);
 
@@ -297,6 +299,8 @@
 
 void __init mv78xx0_pcie_init(int init_port0, int init_port1)
 {
+	vga_base = MV78XX0_PCIE_MEM_PHYS_BASE;
+
 	if (init_port0) {
 		add_pcie_port(0, 0, PCIE00_VIRT_BASE);
 		if (!orion_pcie_x4_mode((void __iomem *)PCIE00_VIRT_BASE)) {
diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig
index f25e9d7..b4e7c58 100644
--- a/arch/arm/mach-mx5/Kconfig
+++ b/arch/arm/mach-mx5/Kconfig
@@ -180,6 +180,7 @@
 	select IMX_HAVE_PLATFORM_IMX_I2C
 	select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
 	select IMX_HAVE_PLATFORM_SPI_IMX
+	select LEDS_GPIO_REGISTER
 	help
 	  Include support for MX53 EVK platform. This includes specific
 	  configurations for the board and its peripherals.
@@ -203,10 +204,23 @@
 	select IMX_HAVE_PLATFORM_IMX_UART
 	select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
 	select IMX_HAVE_PLATFORM_GPIO_KEYS
+	select LEDS_GPIO_REGISTER
 	help
 	  Include support for MX53 LOCO platform. This includes specific
 	  configurations for the board and its peripherals.
 
+config MACH_MX53_ARD
+	bool "Support MX53 ARD platforms"
+	select SOC_IMX53
+	select IMX_HAVE_PLATFORM_IMX2_WDT
+	select IMX_HAVE_PLATFORM_IMX_I2C
+	select IMX_HAVE_PLATFORM_IMX_UART
+	select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
+	select IMX_HAVE_PLATFORM_GPIO_KEYS
+	help
+	  Include support for MX53 ARD platform. This includes specific
+	  configurations for the board and its peripherals.
+
 endif # ARCH_MX53_SUPPORTED
 
 endif
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile
index 0b9338c..383e7cd 100644
--- a/arch/arm/mach-mx5/Makefile
+++ b/arch/arm/mach-mx5/Makefile
@@ -6,12 +6,14 @@
 obj-y   := cpu.o mm.o clock-mx51-mx53.o devices.o ehci.o system.o
 obj-$(CONFIG_SOC_IMX50) += mm-mx50.o
 
+obj-$(CONFIG_PM) += pm-imx5.o
 obj-$(CONFIG_CPU_FREQ_IMX)    += cpu_op-mx51.o
 obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o
 obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o
 obj-$(CONFIG_MACH_MX53_EVK) += board-mx53_evk.o
 obj-$(CONFIG_MACH_MX53_SMD) += board-mx53_smd.o
 obj-$(CONFIG_MACH_MX53_LOCO) += board-mx53_loco.o
+obj-$(CONFIG_MACH_MX53_ARD) += board-mx53_ard.o
 obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o
 obj-$(CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD) += eukrea_mbimx51-baseboard.o
 obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += board-cpuimx51sd.o
diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c
index e54e4bf..e400b09 100644
--- a/arch/arm/mach-mx5/board-mx51_babbage.c
+++ b/arch/arm/mach-mx5/board-mx51_babbage.c
@@ -41,6 +41,8 @@
 #define BABBAGE_POWER_KEY	IMX_GPIO_NR(2, 21)
 #define BABBAGE_ECSPI1_CS0	IMX_GPIO_NR(4, 24)
 #define BABBAGE_ECSPI1_CS1	IMX_GPIO_NR(4, 25)
+#define BABBAGE_SD2_CD		IMX_GPIO_NR(1, 6)
+#define BABBAGE_SD2_WP		IMX_GPIO_NR(1, 5)
 
 /* USB_CTRL_1 */
 #define MX51_USB_CTRL_1_OFFSET			0x10
@@ -142,6 +144,9 @@
 	MX51_PAD_SD1_DATA1__SD1_DATA1,
 	MX51_PAD_SD1_DATA2__SD1_DATA2,
 	MX51_PAD_SD1_DATA3__SD1_DATA3,
+	/* CD/WP from controller */
+	MX51_PAD_GPIO1_0__SD1_CD,
+	MX51_PAD_GPIO1_1__SD1_WP,
 
 	/* SD 2 */
 	MX51_PAD_SD2_CMD__SD2_CMD,
@@ -150,6 +155,9 @@
 	MX51_PAD_SD2_DATA1__SD2_DATA1,
 	MX51_PAD_SD2_DATA2__SD2_DATA2,
 	MX51_PAD_SD2_DATA3__SD2_DATA3,
+	/* CD/WP gpio */
+	MX51_PAD_GPIO1_6__GPIO1_6,
+	MX51_PAD_GPIO1_5__GPIO1_5,
 
 	/* eCSPI1 */
 	MX51_PAD_CSPI1_MISO__ECSPI1_MISO,
@@ -331,6 +339,18 @@
 	.num_chipselect = ARRAY_SIZE(mx51_babbage_spi_cs),
 };
 
+static const struct esdhc_platform_data mx51_babbage_sd1_data __initconst = {
+	.cd_type = ESDHC_CD_CONTROLLER,
+	.wp_type = ESDHC_WP_CONTROLLER,
+};
+
+static const struct esdhc_platform_data mx51_babbage_sd2_data __initconst = {
+	.cd_gpio = BABBAGE_SD2_CD,
+	.wp_gpio = BABBAGE_SD2_WP,
+	.cd_type = ESDHC_CD_GPIO,
+	.wp_type = ESDHC_WP_GPIO,
+};
+
 /*
  * Board specific initialization.
  */
@@ -376,8 +396,8 @@
 	mxc_iomux_v3_setup_pad(usbh1stp);
 	babbage_usbhub_reset();
 
-	imx51_add_sdhci_esdhc_imx(0, NULL);
-	imx51_add_sdhci_esdhc_imx(1, NULL);
+	imx51_add_sdhci_esdhc_imx(0, &mx51_babbage_sd1_data);
+	imx51_add_sdhci_esdhc_imx(1, &mx51_babbage_sd2_data);
 
 	spi_register_board_info(mx51_babbage_spi_board_info,
 		ARRAY_SIZE(mx51_babbage_spi_board_info));
diff --git a/arch/arm/mach-mx5/board-mx53_ard.c b/arch/arm/mach-mx5/board-mx53_ard.c
new file mode 100644
index 0000000..76a67c4
--- /dev/null
+++ b/arch/arm/mach-mx5/board-mx53_ard.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/smsc911x.h>
+
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/iomux-mx53.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+#include "crm_regs.h"
+#include "devices-imx53.h"
+
+#define ARD_ETHERNET_INT_B	IMX_GPIO_NR(2, 31)
+#define ARD_SD1_CD		IMX_GPIO_NR(1, 1)
+#define ARD_SD1_WP		IMX_GPIO_NR(1, 9)
+#define ARD_I2CPORTEXP_B	IMX_GPIO_NR(2, 3)
+#define ARD_VOLUMEDOWN		IMX_GPIO_NR(4, 0)
+#define ARD_HOME			IMX_GPIO_NR(5, 10)
+#define ARD_BACK			IMX_GPIO_NR(5, 11)
+#define ARD_PROG			IMX_GPIO_NR(5, 12)
+#define ARD_VOLUMEUP		IMX_GPIO_NR(5, 13)
+
+static iomux_v3_cfg_t mx53_ard_pads[] = {
+	/* UART1 */
+	MX53_PAD_PATA_DIOW__UART1_TXD_MUX,
+	MX53_PAD_PATA_DMACK__UART1_RXD_MUX,
+	/* WEIM for CS1 */
+	MX53_PAD_EIM_EB3__GPIO2_31, /* ETHERNET_INT_B */
+	MX53_PAD_EIM_D16__EMI_WEIM_D_16,
+	MX53_PAD_EIM_D17__EMI_WEIM_D_17,
+	MX53_PAD_EIM_D18__EMI_WEIM_D_18,
+	MX53_PAD_EIM_D19__EMI_WEIM_D_19,
+	MX53_PAD_EIM_D20__EMI_WEIM_D_20,
+	MX53_PAD_EIM_D21__EMI_WEIM_D_21,
+	MX53_PAD_EIM_D22__EMI_WEIM_D_22,
+	MX53_PAD_EIM_D23__EMI_WEIM_D_23,
+	MX53_PAD_EIM_D24__EMI_WEIM_D_24,
+	MX53_PAD_EIM_D25__EMI_WEIM_D_25,
+	MX53_PAD_EIM_D26__EMI_WEIM_D_26,
+	MX53_PAD_EIM_D27__EMI_WEIM_D_27,
+	MX53_PAD_EIM_D28__EMI_WEIM_D_28,
+	MX53_PAD_EIM_D29__EMI_WEIM_D_29,
+	MX53_PAD_EIM_D30__EMI_WEIM_D_30,
+	MX53_PAD_EIM_D31__EMI_WEIM_D_31,
+	MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0,
+	MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1,
+	MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2,
+	MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3,
+	MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4,
+	MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5,
+	MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6,
+	MX53_PAD_EIM_OE__EMI_WEIM_OE,
+	MX53_PAD_EIM_RW__EMI_WEIM_RW,
+	MX53_PAD_EIM_CS1__EMI_WEIM_CS_1,
+	/* SDHC1 */
+	MX53_PAD_SD1_CMD__ESDHC1_CMD,
+	MX53_PAD_SD1_CLK__ESDHC1_CLK,
+	MX53_PAD_SD1_DATA0__ESDHC1_DAT0,
+	MX53_PAD_SD1_DATA1__ESDHC1_DAT1,
+	MX53_PAD_SD1_DATA2__ESDHC1_DAT2,
+	MX53_PAD_SD1_DATA3__ESDHC1_DAT3,
+	MX53_PAD_PATA_DATA8__ESDHC1_DAT4,
+	MX53_PAD_PATA_DATA9__ESDHC1_DAT5,
+	MX53_PAD_PATA_DATA10__ESDHC1_DAT6,
+	MX53_PAD_PATA_DATA11__ESDHC1_DAT7,
+	MX53_PAD_GPIO_1__GPIO1_1,
+	MX53_PAD_GPIO_9__GPIO1_9,
+	/* I2C2 */
+	MX53_PAD_EIM_EB2__I2C2_SCL,
+	MX53_PAD_KEY_ROW3__I2C2_SDA,
+	/* I2C3 */
+	MX53_PAD_GPIO_3__I2C3_SCL,
+	MX53_PAD_GPIO_16__I2C3_SDA,
+	/* GPIO */
+	MX53_PAD_DISP0_DAT16__GPIO5_10,	/* home */
+	MX53_PAD_DISP0_DAT17__GPIO5_11,	/* back */
+	MX53_PAD_DISP0_DAT18__GPIO5_12,	/* prog */
+	MX53_PAD_DISP0_DAT19__GPIO5_13,	/* vol up */
+	MX53_PAD_GPIO_10__GPIO4_0,		/* vol down */
+};
+
+#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake)	\
+{							\
+	.gpio		= gpio_num,				\
+	.type		= EV_KEY,				\
+	.code		= ev_code,				\
+	.active_low	= act_low,				\
+	.desc		= "btn " descr,			\
+	.wakeup		= wake,					\
+}
+
+static struct gpio_keys_button ard_buttons[] = {
+	GPIO_BUTTON(ARD_HOME, KEY_HOME, 1, "home", 0),
+	GPIO_BUTTON(ARD_BACK, KEY_BACK, 1, "back", 0),
+	GPIO_BUTTON(ARD_PROG, KEY_PROGRAM, 1, "program", 0),
+	GPIO_BUTTON(ARD_VOLUMEUP, KEY_VOLUMEUP, 1, "volume-up", 0),
+	GPIO_BUTTON(ARD_VOLUMEDOWN, KEY_VOLUMEDOWN, 1, "volume-down", 0),
+};
+
+static const struct gpio_keys_platform_data ard_button_data __initconst = {
+	.buttons        = ard_buttons,
+	.nbuttons       = ARRAY_SIZE(ard_buttons),
+};
+
+static struct resource ard_smsc911x_resources[] = {
+	{
+		.start = MX53_CS1_64MB_BASE_ADDR,
+		.end = MX53_CS1_64MB_BASE_ADDR + SZ_32M - 1,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start =  gpio_to_irq(ARD_ETHERNET_INT_B),
+		.end =  gpio_to_irq(ARD_ETHERNET_INT_B),
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+struct smsc911x_platform_config ard_smsc911x_config = {
+	.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+	.irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
+	.flags = SMSC911X_USE_32BIT,
+};
+
+static struct platform_device ard_smsc_lan9220_device = {
+	.name = "smsc911x",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(ard_smsc911x_resources),
+	.resource = ard_smsc911x_resources,
+	.dev = {
+		.platform_data = &ard_smsc911x_config,
+	},
+};
+
+static const struct esdhc_platform_data mx53_ard_sd1_data __initconst = {
+	.cd_gpio = ARD_SD1_CD,
+	.wp_gpio = ARD_SD1_WP,
+};
+
+static struct imxi2c_platform_data mx53_ard_i2c2_data = {
+	.bitrate = 50000,
+};
+
+static struct imxi2c_platform_data mx53_ard_i2c3_data = {
+	.bitrate = 400000,
+};
+
+static void __init mx53_ard_io_init(void)
+{
+	mxc_iomux_v3_setup_multiple_pads(mx53_ard_pads,
+				ARRAY_SIZE(mx53_ard_pads));
+
+	gpio_request(ARD_ETHERNET_INT_B, "eth-int-b");
+	gpio_direction_input(ARD_ETHERNET_INT_B);
+
+	gpio_request(ARD_I2CPORTEXP_B, "i2cptexp-rst");
+	gpio_direction_output(ARD_I2CPORTEXP_B, 1);
+}
+
+/* Config CS1 settings for ethernet controller */
+static int weim_cs_config(void)
+{
+	u32 reg;
+	void __iomem *weim_base, *iomuxc_base;
+
+	weim_base = ioremap(MX53_WEIM_BASE_ADDR, SZ_4K);
+	if (!weim_base)
+		return -ENOMEM;
+
+	iomuxc_base = ioremap(MX53_IOMUXC_BASE_ADDR, SZ_4K);
+	if (!iomuxc_base)
+		return -ENOMEM;
+
+	/* CS1 timings for LAN9220 */
+	writel(0x20001, (weim_base + 0x18));
+	writel(0x0, (weim_base + 0x1C));
+	writel(0x16000202, (weim_base + 0x20));
+	writel(0x00000002, (weim_base + 0x24));
+	writel(0x16002082, (weim_base + 0x28));
+	writel(0x00000000, (weim_base + 0x2C));
+	writel(0x00000000, (weim_base + 0x90));
+
+	/* specify 64 MB on CS1 and CS0 on GPR1 */
+	reg = readl(iomuxc_base + 0x4);
+	reg &= ~0x3F;
+	reg |= 0x1B;
+	writel(reg, (iomuxc_base + 0x4));
+
+	iounmap(iomuxc_base);
+	iounmap(weim_base);
+
+	return 0;
+}
+
+static struct platform_device *devices[] __initdata = {
+	&ard_smsc_lan9220_device,
+};
+
+static void __init mx53_ard_board_init(void)
+{
+	imx53_soc_init();
+	imx53_add_imx_uart(0, NULL);
+
+	mx53_ard_io_init();
+	weim_cs_config();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+
+	imx53_add_sdhci_esdhc_imx(0, &mx53_ard_sd1_data);
+	imx53_add_imx2_wdt(0, NULL);
+	imx53_add_imx_i2c(1, &mx53_ard_i2c2_data);
+	imx53_add_imx_i2c(2, &mx53_ard_i2c3_data);
+	imx_add_gpio_keys(&ard_button_data);
+}
+
+static void __init mx53_ard_timer_init(void)
+{
+	mx53_clocks_init(32768, 24000000, 22579200, 0);
+}
+
+static struct sys_timer mx53_ard_timer = {
+	.init	= mx53_ard_timer_init,
+};
+
+MACHINE_START(MX53_ARD, "Freescale MX53 ARD Board")
+	.map_io = mx53_map_io,
+	.init_early = imx53_init_early,
+	.init_irq = mx53_init_irq,
+	.timer = &mx53_ard_timer,
+	.init_machine = mx53_ard_board_init,
+MACHINE_END
diff --git a/arch/arm/mach-mx5/board-mx53_evk.c b/arch/arm/mach-mx5/board-mx53_evk.c
index 0d9218a..1b417b0 100644
--- a/arch/arm/mach-mx5/board-mx53_evk.c
+++ b/arch/arm/mach-mx5/board-mx53_evk.c
@@ -35,6 +35,7 @@
 #define MX53_EVK_FEC_PHY_RST	IMX_GPIO_NR(7, 6)
 #define EVK_ECSPI1_CS0		IMX_GPIO_NR(2, 30)
 #define EVK_ECSPI1_CS1		IMX_GPIO_NR(3, 19)
+#define MX53EVK_LED		IMX_GPIO_NR(7, 7)
 
 #include "crm_regs.h"
 #include "devices-imx53.h"
@@ -58,12 +59,27 @@
 	/* ecspi chip select lines */
 	MX53_PAD_EIM_EB2__GPIO2_30,
 	MX53_PAD_EIM_D19__GPIO3_19,
+	/* LED */
+	MX53_PAD_PATA_DA_1__GPIO7_7,
 };
 
 static const struct imxuart_platform_data mx53_evk_uart_pdata __initconst = {
 	.flags = IMXUART_HAVE_RTSCTS,
 };
 
+static const struct gpio_led mx53evk_leds[] __initconst = {
+	{
+		.name			= "green",
+		.default_trigger	= "heartbeat",
+		.gpio			= MX53EVK_LED,
+	},
+};
+
+static const struct gpio_led_platform_data mx53evk_leds_data __initconst = {
+	.leds		= mx53evk_leds,
+	.num_leds	= ARRAY_SIZE(mx53evk_leds),
+};
+
 static inline void mx53_evk_init_uart(void)
 {
 	imx53_add_imx_uart(0, NULL);
@@ -135,6 +151,7 @@
 		ARRAY_SIZE(mx53_evk_spi_board_info));
 	imx53_add_ecspi(0, &mx53_evk_spi_data);
 	imx53_add_imx2_wdt(0, NULL);
+	gpio_led_register_device(-1, &mx53evk_leds_data);
 }
 
 static void __init mx53_evk_timer_init(void)
diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c
index 359c3e2..4e1d51d 100644
--- a/arch/arm/mach-mx5/board-mx53_loco.c
+++ b/arch/arm/mach-mx5/board-mx53_loco.c
@@ -38,6 +38,10 @@
 #define MX53_LOCO_UI1			IMX_GPIO_NR(2, 14)
 #define MX53_LOCO_UI2			IMX_GPIO_NR(2, 15)
 #define LOCO_FEC_PHY_RST		IMX_GPIO_NR(7, 6)
+#define LOCO_LED			IMX_GPIO_NR(7, 7)
+#define LOCO_SD3_CD			IMX_GPIO_NR(3, 11)
+#define LOCO_SD3_WP			IMX_GPIO_NR(3, 12)
+#define LOCO_SD1_CD			IMX_GPIO_NR(3, 13)
 
 static iomux_v3_cfg_t mx53_loco_pads[] = {
 	/* FEC */
@@ -70,6 +74,8 @@
 	MX53_PAD_SD1_DATA1__ESDHC1_DAT1,
 	MX53_PAD_SD1_DATA2__ESDHC1_DAT2,
 	MX53_PAD_SD1_DATA3__ESDHC1_DAT3,
+	/* SD1_CD */
+	MX53_PAD_EIM_DA13__GPIO3_13,
 	/* SD3 */
 	MX53_PAD_PATA_DATA8__ESDHC3_DAT0,
 	MX53_PAD_PATA_DATA9__ESDHC3_DAT1,
@@ -163,7 +169,7 @@
 	MX53_PAD_GPIO_7__SPDIF_PLOCK,
 	MX53_PAD_GPIO_17__SPDIF_OUT1,
 	/* GPIO */
-	MX53_PAD_PATA_DA_1__GPIO7_7,
+	MX53_PAD_PATA_DA_1__GPIO7_7,		/* LED */
 	MX53_PAD_PATA_DA_2__GPIO7_8,
 	MX53_PAD_PATA_DATA5__GPIO2_5,
 	MX53_PAD_PATA_DATA6__GPIO2_6,
@@ -202,6 +208,19 @@
 	.nbuttons       = ARRAY_SIZE(loco_buttons),
 };
 
+static const struct esdhc_platform_data mx53_loco_sd1_data __initconst = {
+	.cd_gpio = LOCO_SD1_CD,
+	.cd_type = ESDHC_CD_GPIO,
+	.wp_type = ESDHC_WP_NONE,
+};
+
+static const struct esdhc_platform_data mx53_loco_sd3_data __initconst = {
+	.cd_gpio = LOCO_SD3_CD,
+	.wp_gpio = LOCO_SD3_WP,
+	.cd_type = ESDHC_CD_GPIO,
+	.wp_type = ESDHC_WP_GPIO,
+};
+
 static inline void mx53_loco_fec_reset(void)
 {
 	int ret;
@@ -225,6 +244,19 @@
 	.bitrate = 100000,
 };
 
+static const struct gpio_led mx53loco_leds[] __initconst = {
+	{
+		.name			= "green",
+		.default_trigger	= "heartbeat",
+		.gpio			= LOCO_LED,
+	},
+};
+
+static const struct gpio_led_platform_data mx53loco_leds_data __initconst = {
+	.leds		= mx53loco_leds,
+	.num_leds	= ARRAY_SIZE(mx53loco_leds),
+};
+
 static void __init mx53_loco_board_init(void)
 {
 	imx53_soc_init();
@@ -237,9 +269,10 @@
 	imx53_add_imx2_wdt(0, NULL);
 	imx53_add_imx_i2c(0, &mx53_loco_i2c_data);
 	imx53_add_imx_i2c(1, &mx53_loco_i2c_data);
-	imx53_add_sdhci_esdhc_imx(0, NULL);
-	imx53_add_sdhci_esdhc_imx(2, NULL);
+	imx53_add_sdhci_esdhc_imx(0, &mx53_loco_sd1_data);
+	imx53_add_sdhci_esdhc_imx(2, &mx53_loco_sd3_data);
 	imx_add_gpio_keys(&loco_button_data);
+	gpio_led_register_device(-1, &mx53loco_leds_data);
 }
 
 static void __init mx53_loco_timer_init(void)
diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
index 0adeea1..7f20308 100644
--- a/arch/arm/mach-mx5/clock-mx51-mx53.c
+++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
@@ -1254,12 +1254,20 @@
 	NULL,  NULL, &ipg_clk, &aips_tz1_clk);
 DEFINE_CLOCK(uart3_ipg_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG7_OFFSET,
 	NULL,  NULL, &ipg_clk, &spba_clk);
+DEFINE_CLOCK(uart4_ipg_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG4_OFFSET,
+	NULL,  NULL, &ipg_clk, &spba_clk);
+DEFINE_CLOCK(uart5_ipg_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG6_OFFSET,
+	NULL,  NULL, &ipg_clk, &spba_clk);
 DEFINE_CLOCK(uart1_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG4_OFFSET,
 	NULL,  NULL, &uart_root_clk, &uart1_ipg_clk);
 DEFINE_CLOCK(uart2_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG6_OFFSET,
 	NULL,  NULL, &uart_root_clk, &uart2_ipg_clk);
 DEFINE_CLOCK(uart3_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG8_OFFSET,
 	NULL,  NULL, &uart_root_clk, &uart3_ipg_clk);
+DEFINE_CLOCK(uart4_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG5_OFFSET,
+	NULL,  NULL, &uart_root_clk, &uart4_ipg_clk);
+DEFINE_CLOCK(uart5_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG7_OFFSET,
+	NULL,  NULL, &uart_root_clk, &uart5_ipg_clk);
 
 /* GPT */
 DEFINE_CLOCK(gpt_ipg_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG10_OFFSET,
@@ -1279,6 +1287,8 @@
 	NULL, NULL, &ipg_perclk, NULL);
 DEFINE_CLOCK(hsi2c_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET,
 	NULL, NULL, &ipg_clk, NULL);
+DEFINE_CLOCK(i2c3_mx53_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET,
+	NULL, NULL, &ipg_perclk, NULL);
 
 /* FEC */
 DEFINE_CLOCK(fec_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG12_OFFSET,
@@ -1412,11 +1422,13 @@
        },
 
 static struct clk_lookup mx51_lookups[] = {
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
+	/* i.mx51 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
 	_REGISTER_CLOCK(NULL, "gpt", gpt_clk)
-	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	/* i.mx51 has the i.mx27 type fec */
+	_REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk)
 	_REGISTER_CLOCK("mxc_pwm.0", "pwm", pwm1_clk)
 	_REGISTER_CLOCK("mxc_pwm.1", "pwm", pwm2_clk)
 	_REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk)
@@ -1436,7 +1448,8 @@
 	_REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk)
 	_REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk)
 	_REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk)
-	_REGISTER_CLOCK("imx-sdma", NULL, sdma_clk)
+	/* i.mx51 has the i.mx35 type sdma */
+	_REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk)
 	_REGISTER_CLOCK(NULL, "ckih", ckih_clk)
 	_REGISTER_CLOCK(NULL, "ckih2", ckih2_clk)
 	_REGISTER_CLOCK(NULL, "gpt_32k", gpt_32k_clk)
@@ -1444,10 +1457,10 @@
 	_REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk)
 	/* i.mx51 has the i.mx35 type cspi */
 	_REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.2", NULL, esdhc3_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.3", NULL, esdhc4_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx51.0", NULL, esdhc1_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx51.1", NULL, esdhc2_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx51.2", NULL, esdhc3_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx51.3", NULL, esdhc4_clk)
 	_REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk)
 	_REGISTER_CLOCK(NULL, "iim_clk", iim_clk)
 	_REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk)
@@ -1460,25 +1473,36 @@
 };
 
 static struct clk_lookup mx53_lookups[] = {
-	_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
-	_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
-	_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
+	/* i.mx53 has the i.mx21 type uart */
+	_REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk)
+	_REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk)
+	_REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk)
+	_REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk)
+	_REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk)
 	_REGISTER_CLOCK(NULL, "gpt", gpt_clk)
-	_REGISTER_CLOCK("fec.0", NULL, fec_clk)
+	/* i.mx53 has the i.mx25 type fec */
+	_REGISTER_CLOCK("imx25-fec.0", NULL, fec_clk)
 	_REGISTER_CLOCK(NULL, "iim_clk", iim_clk)
 	_REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk)
 	_REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_mx53_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.2", NULL, esdhc3_mx53_clk)
-	_REGISTER_CLOCK("sdhci-esdhc-imx.3", NULL, esdhc4_mx53_clk)
+	_REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_mx53_clk)
 	/* i.mx53 has the i.mx51 type ecspi */
 	_REGISTER_CLOCK("imx51-ecspi.0", NULL, ecspi1_clk)
 	_REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk)
 	/* i.mx53 has the i.mx25 type cspi */
 	_REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx53.0", NULL, esdhc1_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx53.1", NULL, esdhc2_mx53_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx53.2", NULL, esdhc3_mx53_clk)
+	_REGISTER_CLOCK("sdhci-esdhc-imx53.3", NULL, esdhc4_mx53_clk)
 	_REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk)
 	_REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk)
+	/* i.mx53 has the i.mx35 type sdma */
+	_REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk)
+	_REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk)
+	_REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk)
+	_REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk)
+	_REGISTER_CLOCK("imx-keypad", NULL, dummy_clk)
 };
 
 static void clk_tree_init(void)
diff --git a/arch/arm/mach-mx5/crm_regs.h b/arch/arm/mach-mx5/crm_regs.h
index 87c0c58..5e11ba7 100644
--- a/arch/arm/mach-mx5/crm_regs.h
+++ b/arch/arm/mach-mx5/crm_regs.h
@@ -114,6 +114,8 @@
 #define MXC_CCM_CCGR4		(MX51_CCM_BASE + 0x78)
 #define MXC_CCM_CCGR5		(MX51_CCM_BASE + 0x7C)
 #define MXC_CCM_CCGR6		(MX51_CCM_BASE + 0x80)
+#define MXC_CCM_CCGR7		(MX51_CCM_BASE + 0x84)
+
 #define MXC_CCM_CMEOR		(MX51_CCM_BASE + 0x84)
 
 /* Define the bits in register CCR */
diff --git a/arch/arm/mach-mx5/devices-imx53.h b/arch/arm/mach-mx5/devices-imx53.h
index 48f4c8c..c27fe8b 100644
--- a/arch/arm/mach-mx5/devices-imx53.h
+++ b/arch/arm/mach-mx5/devices-imx53.h
@@ -32,3 +32,11 @@
 extern const struct imx_imx2_wdt_data imx53_imx2_wdt_data[];
 #define imx53_add_imx2_wdt(id, pdata)	\
 	imx_add_imx2_wdt(&imx53_imx2_wdt_data[id])
+
+extern const struct imx_imx_ssi_data imx53_imx_ssi_data[];
+#define imx53_add_imx_ssi(id, pdata)	\
+	imx_add_imx_ssi(&imx53_imx_ssi_data[id], pdata)
+
+extern const struct imx_imx_keypad_data imx53_imx_keypad_data;
+#define imx53_add_imx_keypad(pdata)	\
+	imx_add_imx_keypad(&imx53_imx_keypad_data, pdata)
diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c
index 665843d..baea6e5 100644
--- a/arch/arm/mach-mx5/mm.c
+++ b/arch/arm/mach-mx5/mm.c
@@ -18,6 +18,7 @@
 
 #include <mach/hardware.h>
 #include <mach/common.h>
+#include <mach/devices-common.h>
 #include <mach/iomux-v3.h>
 
 /*
@@ -100,6 +101,43 @@
 	tzic_init_irq(tzic_virt);
 }
 
+static struct sdma_script_start_addrs imx51_sdma_script __initdata = {
+	.ap_2_ap_addr = 642,
+	.uart_2_mcu_addr = 817,
+	.mcu_2_app_addr = 747,
+	.mcu_2_shp_addr = 961,
+	.ata_2_mcu_addr = 1473,
+	.mcu_2_ata_addr = 1392,
+	.app_2_per_addr = 1033,
+	.app_2_mcu_addr = 683,
+	.shp_2_per_addr = 1251,
+	.shp_2_mcu_addr = 892,
+};
+
+static struct sdma_platform_data imx51_sdma_pdata __initdata = {
+	.fw_name = "sdma-imx51.bin",
+	.script_addrs = &imx51_sdma_script,
+};
+
+static struct sdma_script_start_addrs imx53_sdma_script __initdata = {
+	.ap_2_ap_addr = 642,
+	.app_2_mcu_addr = 683,
+	.mcu_2_app_addr = 747,
+	.uart_2_mcu_addr = 817,
+	.shp_2_mcu_addr = 891,
+	.mcu_2_shp_addr = 960,
+	.uartsh_2_mcu_addr = 1032,
+	.spdif_2_mcu_addr = 1100,
+	.mcu_2_spdif_addr = 1134,
+	.firi_2_mcu_addr = 1193,
+	.mcu_2_firi_addr = 1290,
+};
+
+static struct sdma_platform_data imx53_sdma_pdata __initdata = {
+	.fw_name = "sdma-imx53.bin",
+	.script_addrs = &imx53_sdma_script,
+};
+
 void __init imx51_soc_init(void)
 {
 	/* i.mx51 has the i.mx31 type gpio */
@@ -107,6 +145,9 @@
 	mxc_register_gpio("imx31-gpio", 1, MX51_GPIO2_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO2_LOW, MX51_MXC_INT_GPIO2_HIGH);
 	mxc_register_gpio("imx31-gpio", 2, MX51_GPIO3_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO3_LOW, MX51_MXC_INT_GPIO3_HIGH);
 	mxc_register_gpio("imx31-gpio", 3, MX51_GPIO4_BASE_ADDR, SZ_16K, MX51_MXC_INT_GPIO4_LOW, MX51_MXC_INT_GPIO4_HIGH);
+
+	/* i.mx51 has the i.mx35 type sdma */
+	imx_add_imx_sdma("imx35-sdma", MX51_SDMA_BASE_ADDR, MX51_INT_SDMA, &imx51_sdma_pdata);
 }
 
 void __init imx53_soc_init(void)
@@ -119,4 +160,7 @@
 	mxc_register_gpio("imx31-gpio", 4, MX53_GPIO5_BASE_ADDR, SZ_16K, MX53_INT_GPIO5_LOW, MX53_INT_GPIO5_HIGH);
 	mxc_register_gpio("imx31-gpio", 5, MX53_GPIO6_BASE_ADDR, SZ_16K, MX53_INT_GPIO6_LOW, MX53_INT_GPIO6_HIGH);
 	mxc_register_gpio("imx31-gpio", 6, MX53_GPIO7_BASE_ADDR, SZ_16K, MX53_INT_GPIO7_LOW, MX53_INT_GPIO7_HIGH);
+
+	/* i.mx53 has the i.mx35 type sdma */
+	imx_add_imx_sdma("imx35-sdma", MX53_SDMA_BASE_ADDR, MX53_INT_SDMA, &imx53_sdma_pdata);
 }
diff --git a/arch/arm/mach-mx5/mx51_efika.c b/arch/arm/mach-mx5/mx51_efika.c
index 56739c2..4435e03 100644
--- a/arch/arm/mach-mx5/mx51_efika.c
+++ b/arch/arm/mach-mx5/mx51_efika.c
@@ -260,8 +260,8 @@
 };
 
 static struct regulator_consumer_supply vsd_consumers[] = {
-	REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx.0"),
-	REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx.1"),
+	REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.0"),
+	REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.1"),
 };
 
 static struct regulator_consumer_supply pwgt1_consumer[] = {
diff --git a/arch/arm/mach-mx5/pm-imx5.c b/arch/arm/mach-mx5/pm-imx5.c
new file mode 100644
index 0000000..e4529af
--- /dev/null
+++ b/arch/arm/mach-mx5/pm-imx5.c
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/suspend.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <mach/system.h>
+#include "crm_regs.h"
+
+static struct clk *gpc_dvfs_clk;
+
+static int mx5_suspend_enter(suspend_state_t state)
+{
+	clk_enable(gpc_dvfs_clk);
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		mx5_cpu_lp_set(STOP_POWER_OFF);
+		break;
+	case PM_SUSPEND_STANDBY:
+		mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (state == PM_SUSPEND_MEM) {
+		local_flush_tlb_all();
+		flush_cache_all();
+
+		/*clear the EMPGC0/1 bits */
+		__raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR);
+		__raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR);
+	}
+	cpu_do_idle();
+	clk_disable(gpc_dvfs_clk);
+
+	return 0;
+}
+
+static int mx5_pm_valid(suspend_state_t state)
+{
+	return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+static const struct platform_suspend_ops mx5_suspend_ops = {
+	.valid = mx5_pm_valid,
+	.enter = mx5_suspend_enter,
+};
+
+static int __init mx5_pm_init(void)
+{
+	if (gpc_dvfs_clk == NULL)
+		gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
+
+	if (!IS_ERR(gpc_dvfs_clk)) {
+		if (cpu_is_mx51())
+			suspend_set_ops(&mx5_suspend_ops);
+	} else
+		return -EPERM;
+
+	return 0;
+}
+device_initcall(mx5_pm_init);
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index 162b0b0..4cd0231 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -41,6 +41,7 @@
 config MACH_MX28EVK
 	bool "Support MX28EVK Platform"
 	select SOC_IMX28
+	select LEDS_GPIO_REGISTER
 	select MXS_HAVE_AMBA_DUART
 	select MXS_HAVE_PLATFORM_AUART
 	select MXS_HAVE_PLATFORM_FEC
@@ -60,6 +61,7 @@
 	select MXS_HAVE_PLATFORM_AUART
 	select MXS_HAVE_PLATFORM_FEC
 	select MXS_HAVE_PLATFORM_MXS_I2C
+	select MXS_HAVE_PLATFORM_MXS_MMC
 	select MXS_HAVE_PLATFORM_MXS_PWM
 
 config MACH_TX28
diff --git a/arch/arm/mach-mxs/include/mach/clkdev.h b/arch/arm/mach-mxs/include/mach/clkdev.h
deleted file mode 100644
index 3a8f2e3..0000000
--- a/arch/arm/mach-mxs/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __MACH_MXS_CLKDEV_H__
-#define __MACH_MXS_CLKDEV_H__
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c
index 56767a5..eaaf6ff 100644
--- a/arch/arm/mach-mxs/mach-mx28evk.c
+++ b/arch/arm/mach-mxs/mach-mx28evk.c
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
+#include <linux/leds.h>
 #include <linux/irq.h>
 #include <linux/clk.h>
 
@@ -29,6 +30,7 @@
 
 #define MX28EVK_FLEXCAN_SWITCH	MXS_GPIO_NR(2, 13)
 #define MX28EVK_FEC_PHY_POWER	MXS_GPIO_NR(2, 15)
+#define MX28EVK_GPIO_LED	MXS_GPIO_NR(3, 5)
 #define MX28EVK_BL_ENABLE	MXS_GPIO_NR(3, 18)
 #define MX28EVK_LCD_ENABLE	MXS_GPIO_NR(3, 30)
 #define MX28EVK_FEC_PHY_RESET	MXS_GPIO_NR(4, 13)
@@ -178,6 +180,23 @@
 	/* slot power enable */
 	MX28_PAD_PWM4__GPIO_3_29 |
 		(MXS_PAD_4MA | MXS_PAD_3V3 | MXS_PAD_NOPULL),
+
+	/* led */
+	MX28_PAD_AUART1_TX__GPIO_3_5 | MXS_PAD_CTRL,
+};
+
+/* led */
+static const struct gpio_led mx28evk_leds[] __initconst = {
+	{
+		.name = "GPIO-LED",
+		.default_trigger = "heartbeat",
+		.gpio = MX28EVK_GPIO_LED,
+	},
+};
+
+static const struct gpio_led_platform_data mx28evk_led_data __initconst = {
+	.leds = mx28evk_leds,
+	.num_leds = ARRAY_SIZE(mx28evk_leds),
 };
 
 /* fec */
@@ -385,6 +404,8 @@
 	if (ret)
 		pr_warn("failed to request gpio mmc1-slot-power: %d\n", ret);
 	mx28_add_mxs_mmc(1, &mx28evk_mmc_pdata[1]);
+
+	gpio_led_register_device(0, &mx28evk_led_data);
 }
 
 static void __init mx28evk_timer_init(void)
diff --git a/arch/arm/mach-mxs/mach-tx28.c b/arch/arm/mach-mxs/mach-tx28.c
index 6766a12..515a423 100644
--- a/arch/arm/mach-mxs/mach-tx28.c
+++ b/arch/arm/mach-mxs/mach-tx28.c
@@ -139,6 +139,11 @@
 	},
 };
 
+static struct mxs_mmc_platform_data tx28_mmc0_pdata __initdata = {
+       .wp_gpio = -EINVAL,
+       .flags = SLOTF_4_BIT_CAPABLE,
+};
+
 static void __init tx28_stk5v3_init(void)
 {
 	mxs_iomux_setup_multiple_pads(tx28_stk5v3_pads,
@@ -155,6 +160,7 @@
 	mx28_add_mxs_i2c(0);
 	i2c_register_board_info(0, tx28_stk5v3_i2c_boardinfo,
 			ARRAY_SIZE(tx28_stk5v3_i2c_boardinfo));
+	mx28_add_mxs_mmc(0, &tx28_mmc0_pdata);
 }
 
 static void __init tx28_timer_init(void)
diff --git a/arch/arm/mach-nomadik/include/mach/clkdev.h b/arch/arm/mach-nomadik/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-nomadik/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-nuc93x/include/mach/clkdev.h b/arch/arm/mach-nuc93x/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-nuc93x/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-omap1/include/mach/clkdev.h b/arch/arm/mach-omap1/include/mach/clkdev.h
deleted file mode 100644
index ea8640e..0000000
--- a/arch/arm/mach-omap1/include/mach/clkdev.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * arch/arm/mach-omap1/include/mach/clkdev.h
- */
-
-#include <plat/clkdev.h>
diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c
index 98ba978..495b398 100644
--- a/arch/arm/mach-omap1/pm.c
+++ b/arch/arm/mach-omap1/pm.c
@@ -44,7 +44,7 @@
 #include <linux/io.h>
 
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
 
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 19d5891..4ae6257 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -266,9 +266,10 @@
 	select REGULATOR_FIXED_VOLTAGE
 
 config MACH_CM_T35
-	bool "CompuLab CM-T35 module"
+	bool "CompuLab CM-T35/CM-T3730 modules"
 	depends on ARCH_OMAP3
 	default y
+	select MACH_CM_T3730
 	select OMAP_PACKAGE_CUS
 
 config MACH_CM_T3517
@@ -277,6 +278,9 @@
 	default y
 	select OMAP_PACKAGE_CBB
 
+config MACH_CM_T3730
+       bool
+
 config MACH_IGEP0020
 	bool "IGEP v2 board"
 	depends on ARCH_OMAP3
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 933b25b..c7cef44 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -22,6 +22,7 @@
 #include <linux/i2c/twl.h>
 #include <linux/gpio_keys.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
 #include <linux/leds.h>
 #include <linux/leds_pwm.h>
 
@@ -37,6 +38,7 @@
 #include <plat/mmc.h>
 #include <plat/omap4-keypad.h>
 #include <video/omapdss.h>
+#include <linux/wl12xx.h>
 
 #include "mux.h"
 #include "hsmmc.h"
@@ -51,6 +53,9 @@
 #define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */
 #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */
 
+#define GPIO_WIFI_PMENA		54
+#define GPIO_WIFI_IRQ		53
+
 static const int sdp4430_keymap[] = {
 	KEY(0, 0, KEY_E),
 	KEY(0, 1, KEY_R),
@@ -124,6 +129,64 @@
 	KEY(7, 6, KEY_OK),
 	KEY(7, 7, KEY_DOWN),
 };
+static struct omap_device_pad keypad_pads[] __initdata = {
+	{	.name   = "kpd_col1.kpd_col1",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_col1.kpd_col1",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_col2.kpd_col2",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_col3.kpd_col3",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_col4.kpd_col4",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_col5.kpd_col5",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "gpmc_a23.kpd_col7",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "gpmc_a22.kpd_col6",
+		.enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
+	},
+	{	.name   = "kpd_row0.kpd_row0",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "kpd_row1.kpd_row1",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "kpd_row2.kpd_row2",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "kpd_row3.kpd_row3",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "kpd_row4.kpd_row4",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "kpd_row5.kpd_row5",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "gpmc_a18.kpd_row6",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+	{	.name   = "gpmc_a19.kpd_row7",
+		.enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
+			OMAP_MUX_MODE1 | OMAP_INPUT_EN,
+	},
+};
 
 static struct matrix_keymap_data sdp4430_keymap_data = {
 	.keymap			= sdp4430_keymap,
@@ -135,6 +198,13 @@
 	.rows			= 8,
 	.cols			= 8,
 };
+
+static struct omap_board_data keypad_data = {
+	.id	    		= 1,
+	.pads	 		= keypad_pads,
+	.pads_cnt       	= ARRAY_SIZE(keypad_pads),
+};
+
 static struct gpio_led sdp4430_gpio_leds[] = {
 	{
 		.name	= "omap4:green:debug0",
@@ -275,11 +345,40 @@
 	.id		= -1,
 };
 
+static struct regulator_consumer_supply sdp4430_vbat_supply[] = {
+	REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"),
+	REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"),
+};
+
+static struct regulator_init_data sdp4430_vbat_data = {
+	.constraints = {
+		.always_on	= 1,
+	},
+	.num_consumer_supplies	= ARRAY_SIZE(sdp4430_vbat_supply),
+	.consumer_supplies	= sdp4430_vbat_supply,
+};
+
+static struct fixed_voltage_config sdp4430_vbat_pdata = {
+	.supply_name	= "VBAT",
+	.microvolts	= 3750000,
+	.init_data	= &sdp4430_vbat_data,
+	.gpio		= -EINVAL,
+};
+
+static struct platform_device sdp4430_vbat = {
+	.name		= "reg-fixed-voltage",
+	.id		= -1,
+	.dev = {
+		.platform_data = &sdp4430_vbat_pdata,
+	},
+};
+
 static struct platform_device *sdp4430_devices[] __initdata = {
 	&sdp4430_lcd_device,
 	&sdp4430_gpio_keys_device,
 	&sdp4430_leds_gpio,
 	&sdp4430_leds_pwm,
+	&sdp4430_vbat,
 };
 
 static struct omap_lcd_config sdp4430_lcd_config __initdata = {
@@ -315,8 +414,17 @@
 	{
 		.mmc		= 1,
 		.caps		= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
+		.gpio_cd	= -EINVAL,
 		.gpio_wp	= -EINVAL,
 	},
+	{
+		.mmc		= 5,
+		.caps		= MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
+		.gpio_cd	= -EINVAL,
+		.gpio_wp	= -EINVAL,
+		.ocr_mask	= MMC_VDD_165_195,
+		.nonremovable	= true,
+	},
 	{}	/* Terminator */
 };
 
@@ -324,6 +432,37 @@
 	REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"),
 };
 
+static struct regulator_consumer_supply omap4_sdp4430_vmmc5_supply = {
+	.supply = "vmmc",
+	.dev_name = "omap_hsmmc.4",
+};
+
+static struct regulator_init_data sdp4430_vmmc5 = {
+	.constraints = {
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+	.num_consumer_supplies = 1,
+	.consumer_supplies = &omap4_sdp4430_vmmc5_supply,
+};
+
+static struct fixed_voltage_config sdp4430_vwlan = {
+	.supply_name		= "vwl1271",
+	.microvolts		= 1800000, /* 1.8V */
+	.gpio			= GPIO_WIFI_PMENA,
+	.startup_delay		= 70000, /* 70msec */
+	.enable_high		= 1,
+	.enabled_at_boot	= 0,
+	.init_data		= &sdp4430_vmmc5,
+};
+
+static struct platform_device omap_vwlan_device = {
+	.name		= "reg-fixed-voltage",
+	.id		= 1,
+	.dev = {
+		.platform_data = &sdp4430_vwlan,
+	},
+};
+
 static int omap4_twl6030_hsmmc_late_init(struct device *dev)
 {
 	int ret = 0;
@@ -395,7 +534,33 @@
 	},
 };
 
+static struct twl4030_codec_data twl6040_codec = {
+	/* single-step ramp for headset and handsfree */
+	.hs_left_step	= 0x0f,
+	.hs_right_step	= 0x0f,
+	.hf_left_step	= 0x1d,
+	.hf_right_step	= 0x1d,
+};
+
+static struct twl4030_vibra_data twl6040_vibra = {
+	.vibldrv_res = 8,
+	.vibrdrv_res = 3,
+	.viblmotor_res = 10,
+	.vibrmotor_res = 10,
+	.vddvibl_uV = 0,	/* fixed volt supply - VBAT */
+	.vddvibr_uV = 0,	/* fixed volt supply - VBAT */
+};
+
+static struct twl4030_audio_data twl6040_audio = {
+	.codec		= &twl6040_codec,
+	.vibra		= &twl6040_vibra,
+	.audpwron_gpio	= 127,
+	.naudint_irq	= OMAP44XX_IRQ_SYS_2N,
+	.irq_base	= TWL6040_CODEC_IRQ_BASE,
+};
+
 static struct twl4030_platform_data sdp4430_twldata = {
+	.audio		= &twl6040_audio,
 	/* Regulators */
 	.vusim		= &sdp4430_vusim,
 	.vaux1		= &sdp4430_vaux1,
@@ -593,6 +758,41 @@
 }
  #endif
 
+static void omap4_sdp4430_wifi_mux_init(void)
+{
+	omap_mux_init_gpio(GPIO_WIFI_IRQ, OMAP_PIN_INPUT |
+				OMAP_PIN_OFF_WAKEUPENABLE);
+	omap_mux_init_gpio(GPIO_WIFI_PMENA, OMAP_PIN_OUTPUT);
+
+	omap_mux_init_signal("sdmmc5_cmd.sdmmc5_cmd",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+	omap_mux_init_signal("sdmmc5_clk.sdmmc5_clk",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+	omap_mux_init_signal("sdmmc5_dat0.sdmmc5_dat0",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+	omap_mux_init_signal("sdmmc5_dat1.sdmmc5_dat1",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+	omap_mux_init_signal("sdmmc5_dat2.sdmmc5_dat2",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+	omap_mux_init_signal("sdmmc5_dat3.sdmmc5_dat3",
+				OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
+
+}
+
+static struct wl12xx_platform_data omap4_sdp4430_wlan_data __initdata = {
+	.irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ),
+	.board_ref_clock = WL12XX_REFCLOCK_26,
+	.board_tcxo_clock = WL12XX_TCXOCLOCK_26,
+};
+
+static void omap4_sdp4430_wifi_init(void)
+{
+	omap4_sdp4430_wifi_mux_init();
+	if (wl12xx_set_platform_data(&omap4_sdp4430_wlan_data))
+		pr_err("Error setting wl12xx data\n");
+	platform_device_register(&omap_vwlan_device);
+}
+
 static void __init omap_4430sdp_init(void)
 {
 	int status;
@@ -609,6 +809,7 @@
 	omap_sfh7741prox_init();
 	platform_add_devices(sdp4430_devices, ARRAY_SIZE(sdp4430_devices));
 	board_serial_init();
+	omap4_sdp4430_wifi_init();
 	omap4_twl6030_hsmmc_init(mmc);
 
 	usb_musb_init(&musb_board_data);
@@ -622,7 +823,7 @@
 				ARRAY_SIZE(sdp4430_spi_board_info));
 	}
 
-	status = omap4_keyboard_init(&sdp4430_keypad_data);
+	status = omap4_keyboard_init(&sdp4430_keypad_data, &keypad_data);
 	if (status)
 		pr_err("Keypad initialization failed: %d\n", status);
 
diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c
index 35891d4..3af8aab 100644
--- a/arch/arm/mach-omap2/board-cm-t35.c
+++ b/arch/arm/mach-omap2/board-cm-t35.c
@@ -1,8 +1,9 @@
 /*
- * board-cm-t35.c (CompuLab CM-T35 module)
+ * CompuLab CM-T35/CM-T3730 modules support
  *
- * Copyright (C) 2009 CompuLab, Ltd.
- * Author: Mike Rapoport <mike@compulab.co.il>
+ * Copyright (C) 2009-2011 CompuLab, Ltd.
+ * Authors: Mike Rapoport <mike@compulab.co.il>
+ *	    Igor Grinberg <grinberg@compulab.co.il>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -13,11 +14,6 @@
  * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #include <linux/kernel.h>
@@ -149,12 +145,12 @@
 	},
 	{
 		.name           = "linux",
-		.offset         = MTDPART_OFS_APPEND,	/* Offset = 0x280000 */
+		.offset         = MTDPART_OFS_APPEND,	/* Offset = 0x2A0000 */
 		.size           = 32 * NAND_BLOCK_SIZE,
 	},
 	{
 		.name           = "rootfs",
-		.offset         = MTDPART_OFS_APPEND,	/* Offset = 0x680000 */
+		.offset         = MTDPART_OFS_APPEND,	/* Offset = 0x6A0000 */
 		.size           = MTDPART_SIZ_FULL,
 	},
 };
@@ -433,9 +429,9 @@
 	if (gpio_request_one(wlan_rst, GPIOF_OUT_INIT_HIGH, "WLAN RST") == 0) {
 		gpio_export(wlan_rst, 0);
 		udelay(10);
-		gpio_set_value(wlan_rst, 0);
+		gpio_set_value_cansleep(wlan_rst, 0);
 		udelay(10);
-		gpio_set_value(wlan_rst, 1);
+		gpio_set_value_cansleep(wlan_rst, 1);
 	} else {
 		pr_err("CM-T35: could not obtain gpio for WiFi reset\n");
 	}
@@ -539,17 +535,11 @@
 	OMAP3_MUX(UART1_TX, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(UART1_RX, OMAP_MUX_MODE0 | OMAP_PIN_INPUT),
 
-	/* DSS */
+	/* common DSS */
 	OMAP3_MUX(DSS_PCLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_HSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_VSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_ACBIAS, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA0, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA2, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA3, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA4, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA5, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_DATA6, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_DATA7, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_DATA8, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
@@ -562,12 +552,6 @@
 	OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_DATA16, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 	OMAP3_MUX(DSS_DATA17, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA18, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA19, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA20, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA21, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA22, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
-	OMAP3_MUX(DSS_DATA23, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
 
 	/* display controls */
 	OMAP3_MUX(MCBSP1_FSR, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
@@ -580,19 +564,53 @@
 
 	{ .reg_offset = OMAP_MUX_TERMINATOR },
 };
+
+static void __init cm_t3x_common_dss_mux_init(int mux_mode)
+{
+	omap_mux_init_signal("dss_data18", mux_mode);
+	omap_mux_init_signal("dss_data19", mux_mode);
+	omap_mux_init_signal("dss_data20", mux_mode);
+	omap_mux_init_signal("dss_data21", mux_mode);
+	omap_mux_init_signal("dss_data22", mux_mode);
+	omap_mux_init_signal("dss_data23", mux_mode);
+}
+
+static void __init cm_t35_init_mux(void)
+{
+	omap_mux_init_signal("gpio_70", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("gpio_71", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("gpio_72", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("gpio_73", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("gpio_74", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("gpio_75", OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+	cm_t3x_common_dss_mux_init(OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT);
+}
+
+static void __init cm_t3730_init_mux(void)
+{
+	omap_mux_init_signal("sys_boot0", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("sys_boot1", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("sys_boot3", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("sys_boot4", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("sys_boot5", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	omap_mux_init_signal("sys_boot6", OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+	cm_t3x_common_dss_mux_init(OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT);
+}
+#else
+static inline void cm_t35_init_mux(void) {}
+static inline void cm_t3730_init_mux(void) {}
 #endif
 
 static struct omap_board_config_kernel cm_t35_config[] __initdata = {
 };
 
-static void __init cm_t35_init(void)
+static void __init cm_t3x_common_init(void)
 {
 	omap_board_config = cm_t35_config;
 	omap_board_config_size = ARRAY_SIZE(cm_t35_config);
 	omap3_mux_init(board_mux, OMAP_PACKAGE_CUS);
 	omap_serial_init();
 	cm_t35_init_i2c();
-	cm_t35_init_nand();
 	omap_ads7846_init(1, CM_T35_GPIO_PENDOWN, 0, NULL);
 	cm_t35_init_ethernet();
 	cm_t35_init_led();
@@ -602,6 +620,19 @@
 	usbhs_init(&usbhs_bdata);
 }
 
+static void __init cm_t35_init(void)
+{
+	cm_t3x_common_init();
+	cm_t35_init_mux();
+	cm_t35_init_nand();
+}
+
+static void __init cm_t3730_init(void)
+{
+	cm_t3x_common_init();
+	cm_t3730_init_mux();
+}
+
 MACHINE_START(CM_T35, "Compulab CM-T35")
 	.boot_params	= 0x80000100,
 	.reserve	= omap_reserve,
@@ -611,3 +642,13 @@
 	.init_machine	= cm_t35_init,
 	.timer		= &omap3_timer,
 MACHINE_END
+
+MACHINE_START(CM_T3730, "Compulab CM-T3730")
+	.boot_params    = 0x80000100,
+	.reserve        = omap_reserve,
+	.map_io         = omap3_map_io,
+	.init_early     = cm_t35_init_early,
+	.init_irq       = omap3_init_irq,
+	.init_machine   = cm_t3730_init,
+	.timer          = &omap3_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 34f8411..32f5f89 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -60,7 +60,8 @@
  *	AXBX	= GPIO173, GPIO172, GPIO171: 1 1 1
  *	C1_3	= GPIO173, GPIO172, GPIO171: 1 1 0
  *	C4	= GPIO173, GPIO172, GPIO171: 1 0 1
- *	XM	= GPIO173, GPIO172, GPIO171: 0 0 0
+ *	XMA/XMB = GPIO173, GPIO172, GPIO171: 0 0 0
+ *	XMC = GPIO173, GPIO172, GPIO171: 0 1 0
  */
 enum {
 	OMAP3BEAGLE_BOARD_UNKN = 0,
@@ -68,14 +69,26 @@
 	OMAP3BEAGLE_BOARD_C1_3,
 	OMAP3BEAGLE_BOARD_C4,
 	OMAP3BEAGLE_BOARD_XM,
+	OMAP3BEAGLE_BOARD_XMC,
 };
 
 static u8 omap3_beagle_version;
 
-static u8 omap3_beagle_get_rev(void)
-{
-	return omap3_beagle_version;
-}
+/*
+ * Board-specific configuration
+ * Defaults to BeagleBoard-xMC
+ */
+static struct {
+	int mmc1_gpio_wp;
+	int usb_pwr_level;
+	int reset_gpio;
+	int usr_button_gpio;
+} beagle_config = {
+	.mmc1_gpio_wp = -EINVAL,
+	.usb_pwr_level = GPIOF_OUT_INIT_LOW,
+	.reset_gpio = 129,
+	.usr_button_gpio = 4,
+};
 
 static struct gpio omap3_beagle_rev_gpios[] __initdata = {
 	{ 171, GPIOF_IN, "rev_id_0"    },
@@ -110,18 +123,32 @@
 	case 7:
 		printk(KERN_INFO "OMAP3 Beagle Rev: Ax/Bx\n");
 		omap3_beagle_version = OMAP3BEAGLE_BOARD_AXBX;
+		beagle_config.mmc1_gpio_wp = 29;
+		beagle_config.reset_gpio = 170;
+		beagle_config.usr_button_gpio = 7;
 		break;
 	case 6:
 		printk(KERN_INFO "OMAP3 Beagle Rev: C1/C2/C3\n");
 		omap3_beagle_version = OMAP3BEAGLE_BOARD_C1_3;
+		beagle_config.mmc1_gpio_wp = 23;
+		beagle_config.reset_gpio = 170;
+		beagle_config.usr_button_gpio = 7;
 		break;
 	case 5:
 		printk(KERN_INFO "OMAP3 Beagle Rev: C4\n");
 		omap3_beagle_version = OMAP3BEAGLE_BOARD_C4;
+		beagle_config.mmc1_gpio_wp = 23;
+		beagle_config.reset_gpio = 170;
+		beagle_config.usr_button_gpio = 7;
 		break;
 	case 0:
-		printk(KERN_INFO "OMAP3 Beagle Rev: xM\n");
+		printk(KERN_INFO "OMAP3 Beagle Rev: xM Ax/Bx\n");
 		omap3_beagle_version = OMAP3BEAGLE_BOARD_XM;
+		beagle_config.usb_pwr_level = GPIOF_OUT_INIT_HIGH;
+		break;
+	case 2:
+		printk(KERN_INFO "OMAP3 Beagle Rev: xM C\n");
+		omap3_beagle_version = OMAP3BEAGLE_BOARD_XMC;
 		break;
 	default:
 		printk(KERN_INFO "OMAP3 Beagle Rev: unknown %hd\n", beagle_rev);
@@ -225,7 +252,7 @@
 	{
 		.mmc		= 1,
 		.caps		= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
-		.gpio_wp	= 29,
+		.gpio_wp	= -EINVAL,
 	},
 	{}	/* Terminator */
 };
@@ -243,17 +270,11 @@
 static int beagle_twl_gpio_setup(struct device *dev,
 		unsigned gpio, unsigned ngpio)
 {
-	int r, usb_pwr_level;
+	int r;
 
-	if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
-		mmc[0].gpio_wp = -EINVAL;
-	} else if ((omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C1_3) ||
-		(omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C4)) {
-		omap_mux_init_gpio(23, OMAP_PIN_INPUT);
-		mmc[0].gpio_wp = 23;
-	} else {
-		omap_mux_init_gpio(29, OMAP_PIN_INPUT);
-	}
+	if (beagle_config.mmc1_gpio_wp != -EINVAL)
+		omap_mux_init_gpio(beagle_config.mmc1_gpio_wp, OMAP_PIN_INPUT);
+	mmc[0].gpio_wp = beagle_config.mmc1_gpio_wp;
 	/* gpio + 0 is "mmc0_cd" (input/IRQ) */
 	mmc[0].gpio_cd = gpio + 0;
 	omap2_hsmmc_init(mmc);
@@ -263,9 +284,8 @@
 	 * high / others active low)
 	 * DVI reset GPIO is different between beagle revisions
 	 */
-	if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
-		usb_pwr_level = GPIOF_OUT_INIT_HIGH;
-		beagle_dvi_device.reset_gpio = 129;
+	/* Valid for all -xM revisions */
+	if (cpu_is_omap3630()) {
 		/*
 		 * gpio + 1 on Xm controls the TFP410's enable line (active low)
 		 * gpio + 2 control varies depending on the board rev as below:
@@ -283,8 +303,6 @@
 			pr_err("%s: unable to configure DVI_LDO_EN\n",
 				__func__);
 	} else {
-		usb_pwr_level = GPIOF_OUT_INIT_LOW;
-		beagle_dvi_device.reset_gpio = 170;
 		/*
 		 * REVISIT: need ehci-omap hooks for external VBUS
 		 * power switch and overcurrent detect
@@ -292,8 +310,10 @@
 		if (gpio_request_one(gpio + 1, GPIOF_IN, "EHCI_nOC"))
 			pr_err("%s: unable to configure EHCI_nOC\n", __func__);
 	}
+	beagle_dvi_device.reset_gpio = beagle_config.reset_gpio;
 
-	gpio_request_one(gpio + TWL4030_GPIO_MAX, usb_pwr_level, "nEN_USB_PWR");
+	gpio_request_one(gpio + TWL4030_GPIO_MAX, beagle_config.usb_pwr_level,
+			"nEN_USB_PWR");
 
 	/* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
 	gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
@@ -404,7 +424,8 @@
 static struct gpio_keys_button gpio_buttons[] = {
 	{
 		.code			= BTN_EXTRA,
-		.gpio			= 7,
+		/* Dynamically assigned depending on board */
+		.gpio			= -EINVAL,
 		.desc			= "user",
 		.wakeup			= 1,
 	},
@@ -468,8 +489,8 @@
 		return;
 	}
 
-	/* Custom OPP enabled for XM */
-	if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
+	/* Custom OPP enabled for all xM versions */
+	if (cpu_is_omap3630()) {
 		struct omap_hwmod *mh = omap_hwmod_lookup("mpu");
 		struct omap_hwmod *dh = omap_hwmod_lookup("iva");
 		struct device *dev;
@@ -509,6 +530,9 @@
 	omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
 	omap3_beagle_init_rev();
 	omap3_beagle_i2c_init();
+
+	gpio_buttons[0].gpio = beagle_config.usr_button_gpio;
+
 	platform_add_devices(omap3_beagle_devices,
 			ARRAY_SIZE(omap3_beagle_devices));
 	omap_display_init(&beagle_dss_data);
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index f1f18d0..f949a99 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -519,7 +519,6 @@
 	usb_musb_init(NULL);
 	usbhs_init(&usbhs_bdata);
 	overo_spi_init();
-	overo_ads7846_init();
 	overo_init_smsc911x();
 	overo_display_init();
 	overo_init_led();
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index bdb24db..5a886cd 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -39,6 +39,7 @@
 #include <sound/tpa6130a2-plat.h>
 #include <media/radio-si4713.h>
 #include <media/si4713.h>
+#include <linux/leds-lp5523.h>
 
 #include <../drivers/staging/iio/light/tsl2563.h>
 
@@ -53,6 +54,7 @@
 #define RX51_WL1251_IRQ_GPIO		42
 #define RX51_FMTX_RESET_GPIO		163
 #define RX51_FMTX_IRQ			53
+#define RX51_LP5523_CHIP_EN_GPIO	41
 
 #define RX51_USB_TRANSCEIVER_RST_GPIO	67
 
@@ -71,6 +73,64 @@
 };
 #endif
 
+#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
+static struct lp5523_led_config rx51_lp5523_led_config[] = {
+	{
+		.chan_nr	= 0,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 1,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 2,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 3,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 4,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 5,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 6,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 7,
+		.led_current	= 50,
+	}, {
+		.chan_nr	= 8,
+		.led_current	= 50,
+	}
+};
+
+static int rx51_lp5523_setup(void)
+{
+	return gpio_request_one(RX51_LP5523_CHIP_EN_GPIO, GPIOF_DIR_OUT,
+			"lp5523_enable");
+}
+
+static void rx51_lp5523_release(void)
+{
+	gpio_free(RX51_LP5523_CHIP_EN_GPIO);
+}
+
+static void rx51_lp5523_enable(bool state)
+{
+	gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state);
+}
+
+static struct lp5523_platform_data rx51_lp5523_platform_data = {
+	.led_config		= rx51_lp5523_led_config,
+	.num_channels		= ARRAY_SIZE(rx51_lp5523_led_config),
+	.clock_mode		= LP5523_CLOCK_AUTO,
+	.setup_resources	= rx51_lp5523_setup,
+	.release_resources	= rx51_lp5523_release,
+	.enable			= rx51_lp5523_enable,
+};
+#endif
+
 static struct omap2_mcspi_device_config wl1251_mcspi_config = {
 	.turbo_mode	= 0,
 	.single_channel	= 1,
@@ -358,6 +418,10 @@
 	REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"),
 };
 
+static struct regulator_consumer_supply rx51_vaux2_supply[] = {
+	REGULATOR_SUPPLY("vdds_csib", "omap3isp"),
+};
+
 static struct regulator_consumer_supply rx51_vaux3_supply[] = {
 	REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"),
 };
@@ -419,6 +483,8 @@
 		.valid_ops_mask		= REGULATOR_CHANGE_MODE
 					| REGULATOR_CHANGE_STATUS,
 	},
+	.num_consumer_supplies	= ARRAY_SIZE(rx51_vaux2_supply),
+	.consumer_supplies	= rx51_vaux2_supply,
 };
 
 /* VAUX3 - adds more power to VIO_18 rail */
@@ -495,6 +561,32 @@
 	.consumer_supplies	= rx51_vmmc2_supplies,
 };
 
+static struct regulator_init_data rx51_vpll1 = {
+	.constraints = {
+		.name			= "VPLL",
+		.min_uV			= 1800000,
+		.max_uV			= 1800000,
+		.apply_uV		= true,
+		.always_on		= true,
+		.valid_modes_mask	= REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+		.valid_ops_mask		= REGULATOR_CHANGE_MODE,
+	},
+};
+
+static struct regulator_init_data rx51_vpll2 = {
+	.constraints = {
+		.name			= "VSDI_CSI",
+		.min_uV			= 1800000,
+		.max_uV			= 1800000,
+		.apply_uV		= true,
+		.always_on		= true,
+		.valid_modes_mask	= REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+		.valid_ops_mask		= REGULATOR_CHANGE_MODE,
+	},
+};
+
 static struct regulator_init_data rx51_vsim = {
 	.constraints = {
 		.name			= "VMMC2_IO_18",
@@ -524,6 +616,43 @@
 	.consumer_supplies	= rx51_vio_supplies,
 };
 
+static struct regulator_init_data rx51_vintana1 = {
+	.constraints = {
+		.name			= "VINTANA1",
+		.min_uV			= 1500000,
+		.max_uV			= 1500000,
+		.always_on		= true,
+		.valid_modes_mask	= REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+		.valid_ops_mask		= REGULATOR_CHANGE_MODE,
+	},
+};
+
+static struct regulator_init_data rx51_vintana2 = {
+	.constraints = {
+		.name			= "VINTANA2",
+		.min_uV			= 2750000,
+		.max_uV			= 2750000,
+		.apply_uV		= true,
+		.always_on		= true,
+		.valid_modes_mask	= REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+		.valid_ops_mask		= REGULATOR_CHANGE_MODE,
+	},
+};
+
+static struct regulator_init_data rx51_vintdig = {
+	.constraints = {
+		.name			= "VINTDIG",
+		.min_uV			= 1500000,
+		.max_uV			= 1500000,
+		.always_on		= true,
+		.valid_modes_mask	= REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+		.valid_ops_mask		= REGULATOR_CHANGE_MODE,
+	},
+};
+
 static struct si4713_platform_data rx51_si4713_i2c_data __initdata_or_module = {
 	.gpio_reset	= RX51_FMTX_RESET_GPIO,
 };
@@ -741,11 +870,11 @@
 	.resource_config = twl4030_rconfig,
 };
 
-struct twl4030_codec_vibra_data rx51_vibra_data __initdata = {
+struct twl4030_vibra_data rx51_vibra_data __initdata = {
 	.coexist	= 0,
 };
 
-struct twl4030_codec_data rx51_codec_data __initdata = {
+struct twl4030_audio_data rx51_audio_data __initdata = {
 	.audio_mclk	= 26000000,
 	.vibra		= &rx51_vibra_data,
 };
@@ -755,13 +884,18 @@
 	.gpio			= &rx51_gpio_data,
 	.keypad			= &rx51_kp_data,
 	.power			= &rx51_t2scripts_data,
-	.codec			= &rx51_codec_data,
+	.audio			= &rx51_audio_data,
 
 	.vaux1			= &rx51_vaux1,
 	.vaux2			= &rx51_vaux2,
 	.vaux4			= &rx51_vaux4,
 	.vmmc1			= &rx51_vmmc1,
+	.vpll1			= &rx51_vpll1,
+	.vpll2			= &rx51_vpll2,
 	.vsim			= &rx51_vsim,
+	.vintana1		= &rx51_vintana1,
+	.vintana2		= &rx51_vintana2,
+	.vintdig		= &rx51_vintdig,
 	.vio			= &rx51_vio,
 };
 
@@ -800,6 +934,12 @@
 		.platform_data = &rx51_tsl2563_platform_data,
 	},
 #endif
+#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
+	{
+		I2C_BOARD_INFO("lp5523", 0x32),
+		.platform_data  = &rx51_lp5523_platform_data,
+	},
+#endif
 	{
 		I2C_BOARD_INFO("tpa6130a2", 0x60),
 		.platform_data = &rx51_tpa6130a2_data,
@@ -950,6 +1090,7 @@
 void __init rx51_peripherals_init(void)
 {
 	rx51_i2c_init();
+	regulator_has_full_constraints();
 	gpmc_onenand_init(board_onenand_data);
 	board_smc91x_init();
 	rx51_add_gpio_keys();
diff --git a/arch/arm/mach-omap2/board-zoom-debugboard.c b/arch/arm/mach-omap2/board-zoom-debugboard.c
index 6402e78..369c2eb 100644
--- a/arch/arm/mach-omap2/board-zoom-debugboard.c
+++ b/arch/arm/mach-omap2/board-zoom-debugboard.c
@@ -23,6 +23,7 @@
 #define ZOOM_SMSC911X_GPIO	158
 #define ZOOM_QUADUART_CS	3
 #define ZOOM_QUADUART_GPIO	102
+#define ZOOM_QUADUART_RST_GPIO	152
 #define QUART_CLK		1843200
 #define DEBUG_BASE		0x08000000
 #define ZOOM_ETHR_START	DEBUG_BASE
@@ -67,6 +68,14 @@
 	unsigned long cs_mem_base;
 	int quart_gpio = 0;
 
+	if (gpio_request_one(ZOOM_QUADUART_RST_GPIO,
+				GPIOF_OUT_INIT_LOW,
+				"TL16CP754C GPIO") < 0) {
+		pr_err("Failed to request GPIO%d for TL16CP754C\n",
+			ZOOM_QUADUART_RST_GPIO);
+		return;
+	}
+
 	quart_cs = ZOOM_QUADUART_CS;
 
 	if (gpmc_cs_request(quart_cs, SZ_1M, &cs_mem_base) < 0) {
diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c
index 13a6442..6d0aa4f 100644
--- a/arch/arm/mach-omap2/board-zoom-peripherals.c
+++ b/arch/arm/mach-omap2/board-zoom-peripherals.c
@@ -274,12 +274,12 @@
 			TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2);
 
 	if (machine_is_omap_zoom2()) {
-		struct twl4030_codec_audio_data *audio_data;
-		audio_data = zoom_twldata.codec->audio;
+		struct twl4030_codec_data *codec_data;
+		codec_data = zoom_twldata.audio->codec;
 
-		audio_data->ramp_delay_value = 3;	/* 161 ms */
-		audio_data->hs_extmute = 1;
-		audio_data->set_hs_extmute = zoom2_set_hs_extmute;
+		codec_data->ramp_delay_value = 3;	/* 161 ms */
+		codec_data->hs_extmute = 1;
+		codec_data->set_hs_extmute = zoom2_set_hs_extmute;
 	}
 	omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata);
 	omap_register_i2c_bus(2, 400, NULL, 0);
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 180299e..1f3481f 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -38,6 +38,14 @@
 u8 cpu_mask;
 
 /*
+ * clkdm_control: if true, then when a clock is enabled in the
+ * hardware, its clockdomain will first be enabled; and when a clock
+ * is disabled in the hardware, its clockdomain will be disabled
+ * afterwards.
+ */
+static bool clkdm_control = true;
+
+/*
  * OMAP2+ specific clock functions
  */
 
@@ -100,6 +108,19 @@
 }
 
 /**
+ * omap2_clk_disable_clkdm_control - disable clkdm control on clk enable/disable
+ *
+ * Prevent the OMAP clock code from calling into the clockdomain code
+ * when a hardware clock in that clockdomain is enabled or disabled.
+ * Intended to be called at init time from omap*_clk_init().  No
+ * return value.
+ */
+void __init omap2_clk_disable_clkdm_control(void)
+{
+	clkdm_control = false;
+}
+
+/**
  * omap2_clk_dflt_find_companion - find companion clock to @clk
  * @clk: struct clk * to find the companion clock of
  * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
@@ -268,7 +289,7 @@
 		clk->ops->disable(clk);
 	}
 
-	if (clk->clkdm)
+	if (clkdm_control && clk->clkdm)
 		clkdm_clk_disable(clk->clkdm, clk);
 
 	if (clk->parent)
@@ -308,7 +329,7 @@
 		}
 	}
 
-	if (clk->clkdm) {
+	if (clkdm_control && clk->clkdm) {
 		ret = clkdm_clk_enable(clk->clkdm, clk);
 		if (ret) {
 			WARN(1, "clock: %s: could not enable clockdomain %s: "
@@ -330,7 +351,7 @@
 	return 0;
 
 oce_err3:
-	if (clk->clkdm)
+	if (clkdm_control && clk->clkdm)
 		clkdm_clk_disable(clk->clkdm, clk);
 oce_err2:
 	if (clk->parent)
@@ -453,6 +474,7 @@
 	if (IS_ERR_VALUE(r)) {
 		WARN(1, "clock: %s: unable to set MPU rate to %d: %d\n",
 		     mpurate_ck->name, mpurate, r);
+		clk_put(mpurate_ck);
 		return -EINVAL;
 	}
 
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index e10ff2b..48ac568 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -16,6 +16,8 @@
 #ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H
 #define __ARCH_ARM_MACH_OMAP2_CLOCK_H
 
+#include <linux/kernel.h>
+
 #include <plat/clock.h>
 
 /* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
@@ -72,6 +74,7 @@
 #endif
 
 void omap2_init_clk_clkdm(struct clk *clk);
+void __init omap2_clk_disable_clkdm_control(void);
 
 /* clkt_clksel.c public functions */
 u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
diff --git a/arch/arm/mach-omap2/clock2420_data.c b/arch/arm/mach-omap2/clock2420_data.c
index 2926d02..debc040 100644
--- a/arch/arm/mach-omap2/clock2420_data.c
+++ b/arch/arm/mach-omap2/clock2420_data.c
@@ -1805,9 +1805,9 @@
 	CLK(NULL,	"gfx_ick",	&gfx_ick,	CK_242X),
 	/* DSS domain clocks */
 	CLK("omapdss_dss",	"ick",		&dss_ick,	CK_242X),
-	CLK("omapdss_dss",	"fck",		&dss1_fck,	CK_242X),
-	CLK("omapdss_dss",	"sys_clk",	&dss2_fck,	CK_242X),
-	CLK("omapdss_dss",	"tv_clk",	&dss_54m_fck,	CK_242X),
+	CLK(NULL,	"dss1_fck",		&dss1_fck,	CK_242X),
+	CLK(NULL,	"dss2_fck",	&dss2_fck,	CK_242X),
+	CLK(NULL,	"dss_54m_fck",	&dss_54m_fck,	CK_242X),
 	/* L3 domain clocks */
 	CLK(NULL,	"core_l3_ck",	&core_l3_ck,	CK_242X),
 	CLK(NULL,	"ssi_fck",	&ssi_ssr_sst_fck, CK_242X),
@@ -1844,13 +1844,13 @@
 	CLK(NULL,	"gpt12_ick",	&gpt12_ick,	CK_242X),
 	CLK(NULL,	"gpt12_fck",	&gpt12_fck,	CK_242X),
 	CLK("omap-mcbsp.1", "ick",	&mcbsp1_ick,	CK_242X),
-	CLK("omap-mcbsp.1", "fck",	&mcbsp1_fck,	CK_242X),
+	CLK(NULL,	"mcbsp1_fck",	&mcbsp1_fck,	CK_242X),
 	CLK("omap-mcbsp.2", "ick",	&mcbsp2_ick,	CK_242X),
-	CLK("omap-mcbsp.2", "fck",	&mcbsp2_fck,	CK_242X),
+	CLK(NULL,	"mcbsp2_fck",	&mcbsp2_fck,	CK_242X),
 	CLK("omap2_mcspi.1", "ick",	&mcspi1_ick,	CK_242X),
-	CLK("omap2_mcspi.1", "fck",	&mcspi1_fck,	CK_242X),
+	CLK(NULL,	"mcspi1_fck",	&mcspi1_fck,	CK_242X),
 	CLK("omap2_mcspi.2", "ick",	&mcspi2_ick,	CK_242X),
-	CLK("omap2_mcspi.2", "fck",	&mcspi2_fck,	CK_242X),
+	CLK(NULL,	"mcspi2_fck",	&mcspi2_fck,	CK_242X),
 	CLK(NULL,	"uart1_ick",	&uart1_ick,	CK_242X),
 	CLK(NULL,	"uart1_fck",	&uart1_fck,	CK_242X),
 	CLK(NULL,	"uart2_ick",	&uart2_ick,	CK_242X),
@@ -1860,7 +1860,7 @@
 	CLK(NULL,	"gpios_ick",	&gpios_ick,	CK_242X),
 	CLK(NULL,	"gpios_fck",	&gpios_fck,	CK_242X),
 	CLK("omap_wdt",	"ick",		&mpu_wdt_ick,	CK_242X),
-	CLK("omap_wdt",	"fck",		&mpu_wdt_fck,	CK_242X),
+	CLK(NULL,	"mpu_wdt_fck",	&mpu_wdt_fck,	CK_242X),
 	CLK(NULL,	"sync_32k_ick",	&sync_32k_ick,	CK_242X),
 	CLK(NULL,	"wdt1_ick",	&wdt1_ick,	CK_242X),
 	CLK(NULL,	"omapctrl_ick",	&omapctrl_ick,	CK_242X),
@@ -1880,11 +1880,11 @@
 	CLK(NULL,	"eac_ick",	&eac_ick,	CK_242X),
 	CLK(NULL,	"eac_fck",	&eac_fck,	CK_242X),
 	CLK("omap_hdq.0", "ick",	&hdq_ick,	CK_242X),
-	CLK("omap_hdq.1", "fck",	&hdq_fck,	CK_242X),
+	CLK("omap_hdq.0", "fck",	&hdq_fck,	CK_242X),
 	CLK("omap_i2c.1", "ick",	&i2c1_ick,	CK_242X),
-	CLK("omap_i2c.1", "fck",	&i2c1_fck,	CK_242X),
+	CLK(NULL,	"i2c1_fck",	&i2c1_fck,	CK_242X),
 	CLK("omap_i2c.2", "ick",	&i2c2_ick,	CK_242X),
-	CLK("omap_i2c.2", "fck",	&i2c2_fck,	CK_242X),
+	CLK(NULL,	"i2c2_fck",	&i2c2_fck,	CK_242X),
 	CLK(NULL,	"gpmc_fck",	&gpmc_fck,	CK_242X),
 	CLK(NULL,	"sdma_fck",	&sdma_fck,	CK_242X),
 	CLK(NULL,	"sdma_ick",	&sdma_ick,	CK_242X),
diff --git a/arch/arm/mach-omap2/clock2430_data.c b/arch/arm/mach-omap2/clock2430_data.c
index 0c79d39..96a942e 100644
--- a/arch/arm/mach-omap2/clock2430_data.c
+++ b/arch/arm/mach-omap2/clock2430_data.c
@@ -1895,9 +1895,9 @@
 	CLK(NULL,	"mdm_osc_ck",	&mdm_osc_ck,	CK_243X),
 	/* DSS domain clocks */
 	CLK("omapdss_dss",	"ick",		&dss_ick,	CK_243X),
-	CLK("omapdss_dss",	"fck",		&dss1_fck,	CK_243X),
-	CLK("omapdss_dss",	"sys_clk",	&dss2_fck,	CK_243X),
-	CLK("omapdss_dss",	"tv_clk",	&dss_54m_fck,	CK_243X),
+	CLK(NULL,	"dss1_fck",		&dss1_fck,	CK_243X),
+	CLK(NULL,	"dss2_fck",	&dss2_fck,	CK_243X),
+	CLK(NULL,	"dss_54m_fck",	&dss_54m_fck,	CK_243X),
 	/* L3 domain clocks */
 	CLK(NULL,	"core_l3_ck",	&core_l3_ck,	CK_243X),
 	CLK(NULL,	"ssi_fck",	&ssi_ssr_sst_fck, CK_243X),
@@ -1934,21 +1934,21 @@
 	CLK(NULL,	"gpt12_ick",	&gpt12_ick,	CK_243X),
 	CLK(NULL,	"gpt12_fck",	&gpt12_fck,	CK_243X),
 	CLK("omap-mcbsp.1", "ick",	&mcbsp1_ick,	CK_243X),
-	CLK("omap-mcbsp.1", "fck",	&mcbsp1_fck,	CK_243X),
+	CLK(NULL,	"mcbsp1_fck",	&mcbsp1_fck,	CK_243X),
 	CLK("omap-mcbsp.2", "ick",	&mcbsp2_ick,	CK_243X),
-	CLK("omap-mcbsp.2", "fck",	&mcbsp2_fck,	CK_243X),
+	CLK(NULL,	"mcbsp2_fck",	&mcbsp2_fck,	CK_243X),
 	CLK("omap-mcbsp.3", "ick",	&mcbsp3_ick,	CK_243X),
-	CLK("omap-mcbsp.3", "fck",	&mcbsp3_fck,	CK_243X),
+	CLK(NULL,	"mcbsp3_fck",	&mcbsp3_fck,	CK_243X),
 	CLK("omap-mcbsp.4", "ick",	&mcbsp4_ick,	CK_243X),
-	CLK("omap-mcbsp.4", "fck",	&mcbsp4_fck,	CK_243X),
+	CLK(NULL,	"mcbsp4_fck",	&mcbsp4_fck,	CK_243X),
 	CLK("omap-mcbsp.5", "ick",	&mcbsp5_ick,	CK_243X),
-	CLK("omap-mcbsp.5", "fck",	&mcbsp5_fck,	CK_243X),
+	CLK(NULL,	"mcbsp5_fck",	&mcbsp5_fck,	CK_243X),
 	CLK("omap2_mcspi.1", "ick",	&mcspi1_ick,	CK_243X),
-	CLK("omap2_mcspi.1", "fck",	&mcspi1_fck,	CK_243X),
+	CLK(NULL,	"mcspi1_fck",	&mcspi1_fck,	CK_243X),
 	CLK("omap2_mcspi.2", "ick",	&mcspi2_ick,	CK_243X),
-	CLK("omap2_mcspi.2", "fck",	&mcspi2_fck,	CK_243X),
+	CLK(NULL,	"mcspi2_fck",	&mcspi2_fck,	CK_243X),
 	CLK("omap2_mcspi.3", "ick",	&mcspi3_ick,	CK_243X),
-	CLK("omap2_mcspi.3", "fck",	&mcspi3_fck,	CK_243X),
+	CLK(NULL,	"mcspi3_fck",	&mcspi3_fck,	CK_243X),
 	CLK(NULL,	"uart1_ick",	&uart1_ick,	CK_243X),
 	CLK(NULL,	"uart1_fck",	&uart1_fck,	CK_243X),
 	CLK(NULL,	"uart2_ick",	&uart2_ick,	CK_243X),
@@ -1958,7 +1958,7 @@
 	CLK(NULL,	"gpios_ick",	&gpios_ick,	CK_243X),
 	CLK(NULL,	"gpios_fck",	&gpios_fck,	CK_243X),
 	CLK("omap_wdt",	"ick",		&mpu_wdt_ick,	CK_243X),
-	CLK("omap_wdt",	"fck",		&mpu_wdt_fck,	CK_243X),
+	CLK(NULL,	"mpu_wdt_fck",	&mpu_wdt_fck,	CK_243X),
 	CLK(NULL,	"sync_32k_ick",	&sync_32k_ick,	CK_243X),
 	CLK(NULL,	"wdt1_ick",	&wdt1_ick,	CK_243X),
 	CLK(NULL,	"omapctrl_ick",	&omapctrl_ick,	CK_243X),
@@ -1975,9 +1975,9 @@
 	CLK("omap_hdq.0", "ick",	&hdq_ick,	CK_243X),
 	CLK("omap_hdq.1", "fck",	&hdq_fck,	CK_243X),
 	CLK("omap_i2c.1", "ick",	&i2c1_ick,	CK_243X),
-	CLK("omap_i2c.1", "fck",	&i2chs1_fck,	CK_243X),
+	CLK(NULL,	"i2chs1_fck",	&i2chs1_fck,	CK_243X),
 	CLK("omap_i2c.2", "ick",	&i2c2_ick,	CK_243X),
-	CLK("omap_i2c.2", "fck",	&i2chs2_fck,	CK_243X),
+	CLK(NULL,	"i2chs2_fck",	&i2chs2_fck,	CK_243X),
 	CLK(NULL,	"gpmc_fck",	&gpmc_fck,	CK_243X),
 	CLK(NULL,	"sdma_fck",	&sdma_fck,	CK_243X),
 	CLK(NULL,	"sdma_ick",	&sdma_ick,	CK_243X),
@@ -1990,9 +1990,9 @@
 	CLK(NULL,	"usb_fck",	&usb_fck,	CK_243X),
 	CLK("musb-omap2430",	"ick",	&usbhs_ick,	CK_243X),
 	CLK("omap_hsmmc.0", "ick",	&mmchs1_ick,	CK_243X),
-	CLK("omap_hsmmc.0", "fck",	&mmchs1_fck,	CK_243X),
+	CLK(NULL,	"mmchs1_fck",	&mmchs1_fck,	CK_243X),
 	CLK("omap_hsmmc.1", "ick",	&mmchs2_ick,	CK_243X),
-	CLK("omap_hsmmc.1", "fck",	&mmchs2_fck,	CK_243X),
+	CLK(NULL,	"mmchs2_fck",	&mmchs2_fck,	CK_243X),
 	CLK(NULL,	"gpio5_ick",	&gpio5_ick,	CK_243X),
 	CLK(NULL,	"gpio5_fck",	&gpio5_fck,	CK_243X),
 	CLK(NULL,	"mdm_intc_ick",	&mdm_intc_ick,	CK_243X),
diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c
index 75b119b..ffd55b1 100644
--- a/arch/arm/mach-omap2/clock3xxx_data.c
+++ b/arch/arm/mach-omap2/clock3xxx_data.c
@@ -3289,25 +3289,25 @@
 	CLK("omap-mcbsp.1",	"prcm_fck",	&core_96m_fck,	CK_3XXX),
 	CLK("omap-mcbsp.5",	"prcm_fck",	&core_96m_fck,	CK_3XXX),
 	CLK(NULL,	"core_96m_fck",	&core_96m_fck,	CK_3XXX),
-	CLK("omap_hsmmc.2",	"fck",	&mmchs3_fck,	CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
-	CLK("omap_hsmmc.1",	"fck",	&mmchs2_fck,	CK_3XXX),
+	CLK(NULL,	"mmchs3_fck",	&mmchs3_fck,	CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+	CLK(NULL,	"mmchs2_fck",	&mmchs2_fck,	CK_3XXX),
 	CLK(NULL,	"mspro_fck",	&mspro_fck,	CK_34XX | CK_36XX),
-	CLK("omap_hsmmc.0",	"fck",	&mmchs1_fck,	CK_3XXX),
-	CLK("omap_i2c.3", "fck",	&i2c3_fck,	CK_3XXX),
-	CLK("omap_i2c.2", "fck",	&i2c2_fck,	CK_3XXX),
-	CLK("omap_i2c.1", "fck",	&i2c1_fck,	CK_3XXX),
-	CLK("omap-mcbsp.5", "fck",	&mcbsp5_fck,	CK_3XXX),
-	CLK("omap-mcbsp.1", "fck",	&mcbsp1_fck,	CK_3XXX),
+	CLK(NULL,	"mmchs1_fck",	&mmchs1_fck,	CK_3XXX),
+	CLK(NULL,	"i2c3_fck",	&i2c3_fck,	CK_3XXX),
+	CLK(NULL,	"i2c2_fck",	&i2c2_fck,	CK_3XXX),
+	CLK(NULL,	"i2c1_fck",	&i2c1_fck,	CK_3XXX),
+	CLK(NULL,	"mcbsp5_fck",	&mcbsp5_fck,	CK_3XXX),
+	CLK(NULL,	"mcbsp1_fck",	&mcbsp1_fck,	CK_3XXX),
 	CLK(NULL,	"core_48m_fck",	&core_48m_fck,	CK_3XXX),
-	CLK("omap2_mcspi.4", "fck",	&mcspi4_fck,	CK_3XXX),
-	CLK("omap2_mcspi.3", "fck",	&mcspi3_fck,	CK_3XXX),
-	CLK("omap2_mcspi.2", "fck",	&mcspi2_fck,	CK_3XXX),
-	CLK("omap2_mcspi.1", "fck",	&mcspi1_fck,	CK_3XXX),
+	CLK(NULL,	"mcspi4_fck",	&mcspi4_fck,	CK_3XXX),
+	CLK(NULL,	"mcspi3_fck",	&mcspi3_fck,	CK_3XXX),
+	CLK(NULL,	"mcspi2_fck",	&mcspi2_fck,	CK_3XXX),
+	CLK(NULL,	"mcspi1_fck",	&mcspi1_fck,	CK_3XXX),
 	CLK(NULL,	"uart2_fck",	&uart2_fck,	CK_3XXX),
 	CLK(NULL,	"uart1_fck",	&uart1_fck,	CK_3XXX),
 	CLK(NULL,	"fshostusb_fck", &fshostusb_fck, CK_3430ES1),
 	CLK(NULL,	"core_12m_fck",	&core_12m_fck,	CK_3XXX),
-	CLK("omap_hdq.0", "fck",	&hdq_fck,	CK_3XXX),
+	CLK("omap_hdq.0",	"fck",	&hdq_fck,	CK_3XXX),
 	CLK(NULL,	"ssi_ssr_fck",	&ssi_ssr_fck_3430es1,	CK_3430ES1),
 	CLK(NULL,	"ssi_ssr_fck",	&ssi_ssr_fck_3430es2,	CK_3430ES2PLUS | CK_36XX),
 	CLK(NULL,	"ssi_sst_fck",	&ssi_sst_fck_3430es1,	CK_3430ES1),
@@ -3356,11 +3356,11 @@
 	CLK("omap_rng",	"ick",		&rng_ick,	CK_34XX | CK_36XX),
 	CLK(NULL,	"sha11_ick",	&sha11_ick,	CK_34XX | CK_36XX),
 	CLK(NULL,	"des1_ick",	&des1_ick,	CK_34XX | CK_36XX),
-	CLK("omapdss_dss",	"fck",		&dss1_alwon_fck_3430es1, CK_3430ES1),
-	CLK("omapdss_dss",	"fck",		&dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
-	CLK("omapdss_dss",	"tv_clk",	&dss_tv_fck,	CK_3XXX),
-	CLK("omapdss_dss",	"video_clk",	&dss_96m_fck,	CK_3XXX),
-	CLK("omapdss_dss",	"sys_clk",	&dss2_alwon_fck, CK_3XXX),
+	CLK(NULL,	"dss1_alwon_fck",		&dss1_alwon_fck_3430es1, CK_3430ES1),
+	CLK(NULL,	"dss1_alwon_fck",		&dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
+	CLK(NULL,	"dss_tv_fck",	&dss_tv_fck,	CK_3XXX),
+	CLK(NULL,	"dss_96m_fck",	&dss_96m_fck,	CK_3XXX),
+	CLK(NULL,	"dss2_alwon_fck",	&dss2_alwon_fck, CK_3XXX),
 	CLK("omapdss_dss",	"ick",		&dss_ick_3430es1,	CK_3430ES1),
 	CLK("omapdss_dss",	"ick",		&dss_ick_3430es2,	CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
 	CLK(NULL,	"cam_mclk",	&cam_mclk,	CK_34XX | CK_36XX),
@@ -3385,7 +3385,7 @@
 	CLK(NULL,	"gpt1_fck",	&gpt1_fck,	CK_3XXX),
 	CLK(NULL,	"wkup_32k_fck",	&wkup_32k_fck,	CK_3XXX),
 	CLK(NULL,	"gpio1_dbck",	&gpio1_dbck,	CK_3XXX),
-	CLK("omap_wdt",	"fck",		&wdt2_fck,	CK_3XXX),
+	CLK(NULL,	"wdt2_fck",		&wdt2_fck,	CK_3XXX),
 	CLK(NULL,	"wkup_l4_ick",	&wkup_l4_ick,	CK_34XX | CK_36XX),
 	CLK(NULL,	"usim_ick",	&usim_ick,	CK_3430ES2PLUS | CK_36XX),
 	CLK("omap_wdt",	"ick",		&wdt2_ick,	CK_3XXX),
@@ -3436,9 +3436,9 @@
 	CLK("omap-mcbsp.2", "ick",	&mcbsp2_ick,	CK_3XXX),
 	CLK("omap-mcbsp.3", "ick",	&mcbsp3_ick,	CK_3XXX),
 	CLK("omap-mcbsp.4", "ick",	&mcbsp4_ick,	CK_3XXX),
-	CLK("omap-mcbsp.2", "fck",	&mcbsp2_fck,	CK_3XXX),
-	CLK("omap-mcbsp.3", "fck",	&mcbsp3_fck,	CK_3XXX),
-	CLK("omap-mcbsp.4", "fck",	&mcbsp4_fck,	CK_3XXX),
+	CLK(NULL,	"mcbsp2_fck",	&mcbsp2_fck,	CK_3XXX),
+	CLK(NULL,	"mcbsp3_fck",	&mcbsp3_fck,	CK_3XXX),
+	CLK(NULL,	"mcbsp4_fck",	&mcbsp4_fck,	CK_3XXX),
 	CLK("etb",	"emu_src_ck",	&emu_src_ck,	CK_3XXX),
 	CLK(NULL,	"pclk_fck",	&pclk_fck,	CK_3XXX),
 	CLK(NULL,	"pclkx2_fck",	&pclkx2_fck,	CK_3XXX),
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index 044df38..2af0e3f 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -1397,6 +1397,40 @@
 	.recalc		= &followparent_recalc,
 };
 
+static const struct clksel_rate div3_8to32_rates[] = {
+	{ .div = 8, .val = 0, .flags = RATE_IN_44XX },
+	{ .div = 16, .val = 1, .flags = RATE_IN_44XX },
+	{ .div = 32, .val = 2, .flags = RATE_IN_44XX },
+	{ .div = 0 },
+};
+
+static const struct clksel div_ts_div[] = {
+	{ .parent = &l4_wkup_clk_mux_ck, .rates = div3_8to32_rates },
+	{ .parent = NULL },
+};
+
+static struct clk div_ts_ck = {
+	.name		= "div_ts_ck",
+	.parent		= &l4_wkup_clk_mux_ck,
+	.clksel		= div_ts_div,
+	.clksel_reg	= OMAP4430_CM_WKUP_BANDGAP_CLKCTRL,
+	.clksel_mask	= OMAP4430_CLKSEL_24_25_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk bandgap_ts_fclk = {
+	.name		= "bandgap_ts_fclk",
+	.ops		= &clkops_omap2_dflt,
+	.enable_reg	= OMAP4430_CM_WKUP_BANDGAP_CLKCTRL,
+	.enable_bit	= OMAP4460_OPTFCLKEN_TS_FCLK_SHIFT,
+	.clkdm_name	= "l4_wkup_clkdm",
+	.parent		= &div_ts_ck,
+	.recalc		= &followparent_recalc,
+};
+
 static struct clk dss_48mhz_clk = {
 	.name		= "dss_48mhz_clk",
 	.ops		= &clkops_omap2_dflt,
@@ -1605,6 +1639,7 @@
 	.ops		= &clkops_omap2_dflt,
 	.enable_reg	= OMAP4430_CM_L3_2_GPMC_CLKCTRL,
 	.enable_bit	= OMAP4430_MODULEMODE_HWCTRL,
+	.flags		= ENABLE_ON_INIT,
 	.clkdm_name	= "l3_2_clkdm",
 	.parent		= &l3_div_ck,
 	.recalc		= &followparent_recalc,
@@ -2773,19 +2808,39 @@
 
 /* SCRM aux clk nodes */
 
-static const struct clksel auxclk_sel[] = {
+static const struct clksel auxclk_src_sel[] = {
 	{ .parent = &sys_clkin_ck, .rates = div_1_0_rates },
 	{ .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
 	{ .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
 	{ .parent = NULL },
 };
 
-static struct clk auxclk0_ck = {
-	.name		= "auxclk0_ck",
+static const struct clksel_rate div16_1to16_rates[] = {
+	{ .div = 1, .val = 0, .flags = RATE_IN_4430 },
+	{ .div = 2, .val = 1, .flags = RATE_IN_4430 },
+	{ .div = 3, .val = 2, .flags = RATE_IN_4430 },
+	{ .div = 4, .val = 3, .flags = RATE_IN_4430 },
+	{ .div = 5, .val = 4, .flags = RATE_IN_4430 },
+	{ .div = 6, .val = 5, .flags = RATE_IN_4430 },
+	{ .div = 7, .val = 6, .flags = RATE_IN_4430 },
+	{ .div = 8, .val = 7, .flags = RATE_IN_4430 },
+	{ .div = 9, .val = 8, .flags = RATE_IN_4430 },
+	{ .div = 10, .val = 9, .flags = RATE_IN_4430 },
+	{ .div = 11, .val = 10, .flags = RATE_IN_4430 },
+	{ .div = 12, .val = 11, .flags = RATE_IN_4430 },
+	{ .div = 13, .val = 12, .flags = RATE_IN_4430 },
+	{ .div = 14, .val = 13, .flags = RATE_IN_4430 },
+	{ .div = 15, .val = 14, .flags = RATE_IN_4430 },
+	{ .div = 16, .val = 15, .flags = RATE_IN_4430 },
+	{ .div = 0 },
+};
+
+static struct clk auxclk0_src_ck = {
+	.name		= "auxclk0_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK0,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2793,12 +2848,29 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
-static struct clk auxclk1_ck = {
-	.name		= "auxclk1_ck",
+static const struct clksel auxclk0_sel[] = {
+	{ .parent = &auxclk0_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk0_ck = {
+	.name		= "auxclk0_ck",
+	.parent		= &auxclk0_src_ck,
+	.clksel		= auxclk0_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK0,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk auxclk1_src_ck = {
+	.name		= "auxclk1_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK1,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2806,12 +2878,29 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
-static struct clk auxclk2_ck = {
-	.name		= "auxclk2_ck",
+static const struct clksel auxclk1_sel[] = {
+	{ .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk1_ck = {
+	.name		= "auxclk1_ck",
+	.parent		= &auxclk1_src_ck,
+	.clksel		= auxclk1_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK1,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk auxclk2_src_ck = {
+	.name		= "auxclk2_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK2,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2819,12 +2908,29 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
-static struct clk auxclk3_ck = {
-	.name		= "auxclk3_ck",
+static const struct clksel auxclk2_sel[] = {
+	{ .parent = &auxclk2_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk2_ck = {
+	.name		= "auxclk2_ck",
+	.parent		= &auxclk2_src_ck,
+	.clksel		= auxclk2_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK2,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk auxclk3_src_ck = {
+	.name		= "auxclk3_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK3,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2832,12 +2938,29 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
-static struct clk auxclk4_ck = {
-	.name		= "auxclk4_ck",
+static const struct clksel auxclk3_sel[] = {
+	{ .parent = &auxclk3_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk3_ck = {
+	.name		= "auxclk3_ck",
+	.parent		= &auxclk3_src_ck,
+	.clksel		= auxclk3_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK3,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk auxclk4_src_ck = {
+	.name		= "auxclk4_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK4,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2845,12 +2968,29 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
-static struct clk auxclk5_ck = {
-	.name		= "auxclk5_ck",
+static const struct clksel auxclk4_sel[] = {
+	{ .parent = &auxclk4_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk4_ck = {
+	.name		= "auxclk4_ck",
+	.parent		= &auxclk4_src_ck,
+	.clksel		= auxclk4_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK4,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
+static struct clk auxclk5_src_ck = {
+	.name		= "auxclk5_src_ck",
 	.parent		= &sys_clkin_ck,
 	.init		= &omap2_init_clksel_parent,
 	.ops		= &clkops_omap2_dflt,
-	.clksel		= auxclk_sel,
+	.clksel		= auxclk_src_sel,
 	.clksel_reg	= OMAP4_SCRM_AUXCLK5,
 	.clksel_mask	= OMAP4_SRCSELECT_MASK,
 	.recalc		= &omap2_clksel_recalc,
@@ -2858,6 +2998,23 @@
 	.enable_bit	= OMAP4_ENABLE_SHIFT,
 };
 
+static const struct clksel auxclk5_sel[] = {
+	{ .parent = &auxclk5_src_ck, .rates = div16_1to16_rates },
+	{ .parent = NULL },
+};
+
+static struct clk auxclk5_ck = {
+	.name		= "auxclk5_ck",
+	.parent		= &auxclk5_src_ck,
+	.clksel		= auxclk5_sel,
+	.clksel_reg	= OMAP4_SCRM_AUXCLK5,
+	.clksel_mask	= OMAP4_CLKDIV_MASK,
+	.ops		= &clkops_null,
+	.recalc		= &omap2_clksel_recalc,
+	.round_rate	= &omap2_clksel_round_rate,
+	.set_rate	= &omap2_clksel_set_rate,
+};
+
 static const struct clksel auxclkreq_sel[] = {
 	{ .parent = &auxclk0_ck, .rates = div_1_0_rates },
 	{ .parent = &auxclk1_ck, .rates = div_1_1_rates },
@@ -3028,14 +3185,16 @@
 	CLK(NULL,	"aes2_fck",			&aes2_fck,	CK_443X),
 	CLK(NULL,	"aess_fck",			&aess_fck,	CK_443X),
 	CLK(NULL,	"bandgap_fclk",			&bandgap_fclk,	CK_443X),
+	CLK(NULL,	"bandgap_ts_fclk",		&bandgap_ts_fclk,	CK_446X),
 	CLK(NULL,	"des3des_fck",			&des3des_fck,	CK_443X),
+	CLK(NULL,	"div_ts_ck",			&div_ts_ck,	CK_446X),
 	CLK(NULL,	"dmic_sync_mux_ck",		&dmic_sync_mux_ck,	CK_443X),
 	CLK(NULL,	"dmic_fck",			&dmic_fck,	CK_443X),
 	CLK(NULL,	"dsp_fck",			&dsp_fck,	CK_443X),
-	CLK("omapdss_dss",	"sys_clk",			&dss_sys_clk,	CK_443X),
-	CLK("omapdss_dss",	"tv_clk",			&dss_tv_clk,	CK_443X),
-	CLK("omapdss_dss",	"video_clk",			&dss_48mhz_clk,	CK_443X),
-	CLK("omapdss_dss",	"fck",				&dss_dss_clk,	CK_443X),
+	CLK(NULL,	"dss_sys_clk",			&dss_sys_clk,	CK_443X),
+	CLK(NULL,	"dss_tv_clk",			&dss_tv_clk,	CK_443X),
+	CLK(NULL,	"dss_48mhz_clk",		&dss_48mhz_clk,	CK_443X),
+	CLK(NULL,	"dss_dss_clk",			&dss_dss_clk,	CK_443X),
 	CLK("omapdss_dss",	"ick",				&dss_fck,	CK_443X),
 	CLK(NULL,	"efuse_ctrl_cust_fck",		&efuse_ctrl_cust_fck,	CK_443X),
 	CLK(NULL,	"emif1_fck",			&emif1_fck,	CK_443X),
@@ -3056,12 +3215,12 @@
 	CLK(NULL,	"gpio6_ick",			&gpio6_ick,	CK_443X),
 	CLK(NULL,	"gpmc_ick",			&gpmc_ick,	CK_443X),
 	CLK(NULL,	"gpu_fck",			&gpu_fck,	CK_443X),
-	CLK("omap2_hdq.0",	"fck",				&hdq1w_fck,	CK_443X),
+	CLK(NULL,	"hdq1w_fck",			&hdq1w_fck,	CK_443X),
 	CLK(NULL,	"hsi_fck",			&hsi_fck,	CK_443X),
-	CLK("omap_i2c.1",	"fck",				&i2c1_fck,	CK_443X),
-	CLK("omap_i2c.2",	"fck",				&i2c2_fck,	CK_443X),
-	CLK("omap_i2c.3",	"fck",				&i2c3_fck,	CK_443X),
-	CLK("omap_i2c.4",	"fck",				&i2c4_fck,	CK_443X),
+	CLK(NULL,	"i2c1_fck",			&i2c1_fck,	CK_443X),
+	CLK(NULL,	"i2c2_fck",			&i2c2_fck,	CK_443X),
+	CLK(NULL,	"i2c3_fck",			&i2c3_fck,	CK_443X),
+	CLK(NULL,	"i2c4_fck",			&i2c4_fck,	CK_443X),
 	CLK(NULL,	"ipu_fck",			&ipu_fck,	CK_443X),
 	CLK(NULL,	"iss_ctrlclk",			&iss_ctrlclk,	CK_443X),
 	CLK(NULL,	"iss_fck",			&iss_fck,	CK_443X),
@@ -3072,23 +3231,23 @@
 	CLK(NULL,	"mcasp_sync_mux_ck",		&mcasp_sync_mux_ck,	CK_443X),
 	CLK(NULL,	"mcasp_fck",			&mcasp_fck,	CK_443X),
 	CLK(NULL,	"mcbsp1_sync_mux_ck",		&mcbsp1_sync_mux_ck,	CK_443X),
-	CLK("omap-mcbsp.1",	"fck",				&mcbsp1_fck,	CK_443X),
+	CLK(NULL,	"mcbsp1_fck",			&mcbsp1_fck,	CK_443X),
 	CLK(NULL,	"mcbsp2_sync_mux_ck",		&mcbsp2_sync_mux_ck,	CK_443X),
-	CLK("omap-mcbsp.2",	"fck",				&mcbsp2_fck,	CK_443X),
+	CLK(NULL,	"mcbsp2_fck",			&mcbsp2_fck,	CK_443X),
 	CLK(NULL,	"mcbsp3_sync_mux_ck",		&mcbsp3_sync_mux_ck,	CK_443X),
-	CLK("omap-mcbsp.3",	"fck",				&mcbsp3_fck,	CK_443X),
+	CLK(NULL,	"mcbsp3_fck",			&mcbsp3_fck,	CK_443X),
 	CLK(NULL,	"mcbsp4_sync_mux_ck",		&mcbsp4_sync_mux_ck,	CK_443X),
-	CLK("omap-mcbsp.4",	"fck",				&mcbsp4_fck,	CK_443X),
+	CLK(NULL,	"mcbsp4_fck",			&mcbsp4_fck,	CK_443X),
 	CLK(NULL,	"mcpdm_fck",			&mcpdm_fck,	CK_443X),
-	CLK("omap2_mcspi.1",	"fck",				&mcspi1_fck,	CK_443X),
-	CLK("omap2_mcspi.2",	"fck",				&mcspi2_fck,	CK_443X),
-	CLK("omap2_mcspi.3",	"fck",				&mcspi3_fck,	CK_443X),
-	CLK("omap2_mcspi.4",	"fck",				&mcspi4_fck,	CK_443X),
-	CLK("omap_hsmmc.0",	"fck",				&mmc1_fck,	CK_443X),
-	CLK("omap_hsmmc.1",	"fck",				&mmc2_fck,	CK_443X),
-	CLK("omap_hsmmc.2",	"fck",				&mmc3_fck,	CK_443X),
-	CLK("omap_hsmmc.3",	"fck",				&mmc4_fck,	CK_443X),
-	CLK("omap_hsmmc.4",	"fck",				&mmc5_fck,	CK_443X),
+	CLK(NULL,	"mcspi1_fck",			&mcspi1_fck,	CK_443X),
+	CLK(NULL,	"mcspi2_fck",			&mcspi2_fck,	CK_443X),
+	CLK(NULL,	"mcspi3_fck",			&mcspi3_fck,	CK_443X),
+	CLK(NULL,	"mcspi4_fck",			&mcspi4_fck,	CK_443X),
+	CLK(NULL,	"mmc1_fck",			&mmc1_fck,	CK_443X),
+	CLK(NULL,	"mmc2_fck",			&mmc2_fck,	CK_443X),
+	CLK(NULL,	"mmc3_fck",			&mmc3_fck,	CK_443X),
+	CLK(NULL,	"mmc4_fck",			&mmc4_fck,	CK_443X),
+	CLK(NULL,	"mmc5_fck",			&mmc5_fck,	CK_443X),
 	CLK(NULL,	"ocp2scp_usb_phy_phy_48m",	&ocp2scp_usb_phy_phy_48m,	CK_443X),
 	CLK(NULL,	"ocp2scp_usb_phy_ick",		&ocp2scp_usb_phy_ick,	CK_443X),
 	CLK(NULL,	"ocp_wp_noc_ick",		&ocp_wp_noc_ick,	CK_443X),
@@ -3145,21 +3304,27 @@
 	CLK(NULL,	"usim_ck",			&usim_ck,	CK_443X),
 	CLK(NULL,	"usim_fclk",			&usim_fclk,	CK_443X),
 	CLK(NULL,	"usim_fck",			&usim_fck,	CK_443X),
-	CLK("omap_wdt",	"fck",				&wd_timer2_fck,	CK_443X),
+	CLK(NULL,	"wd_timer2_fck",		&wd_timer2_fck,	CK_443X),
 	CLK(NULL,	"wd_timer3_fck",		&wd_timer3_fck,	CK_443X),
 	CLK(NULL,	"stm_clk_div_ck",		&stm_clk_div_ck,	CK_443X),
 	CLK(NULL,	"trace_clk_div_ck",		&trace_clk_div_ck,	CK_443X),
+	CLK(NULL,	"auxclk0_src_ck",		&auxclk0_src_ck,	CK_443X),
 	CLK(NULL,	"auxclk0_ck",			&auxclk0_ck,	CK_443X),
-	CLK(NULL,	"auxclk1_ck",			&auxclk1_ck,	CK_443X),
-	CLK(NULL,	"auxclk2_ck",			&auxclk2_ck,	CK_443X),
-	CLK(NULL,	"auxclk3_ck",			&auxclk3_ck,	CK_443X),
-	CLK(NULL,	"auxclk4_ck",			&auxclk4_ck,	CK_443X),
-	CLK(NULL,	"auxclk5_ck",			&auxclk5_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq0_ck",		&auxclkreq0_ck,	CK_443X),
+	CLK(NULL,	"auxclk1_src_ck",		&auxclk1_src_ck,	CK_443X),
+	CLK(NULL,	"auxclk1_ck",			&auxclk1_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq1_ck",		&auxclkreq1_ck,	CK_443X),
+	CLK(NULL,	"auxclk2_src_ck",		&auxclk2_src_ck,	CK_443X),
+	CLK(NULL,	"auxclk2_ck",			&auxclk2_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq2_ck",		&auxclkreq2_ck,	CK_443X),
+	CLK(NULL,	"auxclk3_src_ck",		&auxclk3_src_ck,	CK_443X),
+	CLK(NULL,	"auxclk3_ck",			&auxclk3_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq3_ck",		&auxclkreq3_ck,	CK_443X),
+	CLK(NULL,	"auxclk4_src_ck",		&auxclk4_src_ck,	CK_443X),
+	CLK(NULL,	"auxclk4_ck",			&auxclk4_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq4_ck",		&auxclkreq4_ck,	CK_443X),
+	CLK(NULL,	"auxclk5_src_ck",		&auxclk5_src_ck,	CK_443X),
+	CLK(NULL,	"auxclk5_ck",			&auxclk5_ck,	CK_443X),
 	CLK(NULL,	"auxclkreq5_ck",		&auxclkreq5_ck,	CK_443X),
 	CLK(NULL,	"gpmc_ck",			&dummy_ck,	CK_443X),
 	CLK(NULL,	"gpt1_ick",			&dummy_ck,	CK_443X),
@@ -3208,9 +3373,13 @@
 	if (cpu_is_omap44xx()) {
 		cpu_mask = RATE_IN_4430;
 		cpu_clkflg = CK_443X;
+	} else if (cpu_is_omap446x()) {
+		cpu_mask = RATE_IN_4460;
+		cpu_clkflg = CK_446X;
 	}
 
 	clk_init(&omap2_clk_functions);
+	omap2_clk_disable_clkdm_control();
 
 	for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
 									  c++)
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c
index 6cb6c03..ab7db08 100644
--- a/arch/arm/mach-omap2/clockdomain.c
+++ b/arch/arm/mach-omap2/clockdomain.c
@@ -1,8 +1,8 @@
 /*
  * OMAP2/3/4 clockdomain framework functions
  *
- * Copyright (C) 2008-2010 Texas Instruments, Inc.
- * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2008-2011 Texas Instruments, Inc.
+ * Copyright (C) 2008-2011 Nokia Corporation
  *
  * Written by Paul Walmsley and Jouni Högander
  * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
@@ -92,6 +92,8 @@
 
 	pwrdm_add_clkdm(pwrdm, clkdm);
 
+	spin_lock_init(&clkdm->lock);
+
 	pr_debug("clockdomain: registered %s\n", clkdm->name);
 
 	return 0;
@@ -690,6 +692,9 @@
  */
 int clkdm_sleep(struct clockdomain *clkdm)
 {
+	int ret;
+	unsigned long flags;
+
 	if (!clkdm)
 		return -EINVAL;
 
@@ -704,7 +709,11 @@
 
 	pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
 
-	return arch_clkdm->clkdm_sleep(clkdm);
+	spin_lock_irqsave(&clkdm->lock, flags);
+	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
+	ret = arch_clkdm->clkdm_sleep(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
+	return ret;
 }
 
 /**
@@ -718,6 +727,9 @@
  */
 int clkdm_wakeup(struct clockdomain *clkdm)
 {
+	int ret;
+	unsigned long flags;
+
 	if (!clkdm)
 		return -EINVAL;
 
@@ -732,7 +744,11 @@
 
 	pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
 
-	return arch_clkdm->clkdm_wakeup(clkdm);
+	spin_lock_irqsave(&clkdm->lock, flags);
+	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
+	ret = arch_clkdm->clkdm_wakeup(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
+	return ret;
 }
 
 /**
@@ -747,6 +763,8 @@
  */
 void clkdm_allow_idle(struct clockdomain *clkdm)
 {
+	unsigned long flags;
+
 	if (!clkdm)
 		return;
 
@@ -762,8 +780,11 @@
 	pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
 		 clkdm->name);
 
+	spin_lock_irqsave(&clkdm->lock, flags);
+	clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
 	arch_clkdm->clkdm_allow_idle(clkdm);
 	pwrdm_clkdm_state_switch(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
 }
 
 /**
@@ -777,6 +798,8 @@
  */
 void clkdm_deny_idle(struct clockdomain *clkdm)
 {
+	unsigned long flags;
+
 	if (!clkdm)
 		return;
 
@@ -792,11 +815,90 @@
 	pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
 		 clkdm->name);
 
+	spin_lock_irqsave(&clkdm->lock, flags);
+	clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
 	arch_clkdm->clkdm_deny_idle(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
 }
 
+/**
+ * clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled?
+ * @clkdm: struct clockdomain *
+ *
+ * Returns true if clockdomain @clkdm currently has
+ * hardware-supervised idle enabled, or false if it does not or if
+ * @clkdm is NULL.  It is only valid to call this function after
+ * clkdm_init() has been called.  This function does not actually read
+ * bits from the hardware; it instead tests an in-memory flag that is
+ * changed whenever the clockdomain code changes the auto-idle mode.
+ */
+bool clkdm_in_hwsup(struct clockdomain *clkdm)
+{
+	bool ret;
+	unsigned long flags;
 
-/* Clockdomain-to-clock framework interface code */
+	if (!clkdm)
+		return false;
+
+	spin_lock_irqsave(&clkdm->lock, flags);
+	ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;
+	spin_unlock_irqrestore(&clkdm->lock, flags);
+
+	return ret;
+}
+
+/* Clockdomain-to-clock/hwmod framework interface code */
+
+static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
+{
+	unsigned long flags;
+
+	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
+		return -EINVAL;
+
+	/*
+	 * For arch's with no autodeps, clkcm_clk_enable
+	 * should be called for every clock instance or hwmod that is
+	 * enabled, so the clkdm can be force woken up.
+	 */
+	if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps)
+		return 0;
+
+	spin_lock_irqsave(&clkdm->lock, flags);
+	arch_clkdm->clkdm_clk_enable(clkdm);
+	pwrdm_wait_transition(clkdm->pwrdm.ptr);
+	pwrdm_clkdm_state_switch(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
+
+	pr_debug("clockdomain: clkdm %s: enabled\n", clkdm->name);
+
+	return 0;
+}
+
+static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)
+{
+	unsigned long flags;
+
+	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
+		return -EINVAL;
+
+	if (atomic_read(&clkdm->usecount) == 0) {
+		WARN_ON(1); /* underflow */
+		return -ERANGE;
+	}
+
+	if (atomic_dec_return(&clkdm->usecount) > 0)
+		return 0;
+
+	spin_lock_irqsave(&clkdm->lock, flags);
+	arch_clkdm->clkdm_clk_disable(clkdm);
+	pwrdm_clkdm_state_switch(clkdm);
+	spin_unlock_irqrestore(&clkdm->lock, flags);
+
+	pr_debug("clockdomain: clkdm %s: disabled\n", clkdm->name);
+
+	return 0;
+}
 
 /**
  * clkdm_clk_enable - add an enabled downstream clock to this clkdm
@@ -819,25 +921,10 @@
 	 * downstream clocks for debugging purposes?
 	 */
 
-	if (!clkdm || !clk)
+	if (!clk)
 		return -EINVAL;
 
-	if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable)
-		return -EINVAL;
-
-	if (atomic_inc_return(&clkdm->usecount) > 1)
-		return 0;
-
-	/* Clockdomain now has one enabled downstream clock */
-
-	pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
-		 clk->name);
-
-	arch_clkdm->clkdm_clk_enable(clkdm);
-	pwrdm_wait_transition(clkdm->pwrdm.ptr);
-	pwrdm_clkdm_state_switch(clkdm);
-
-	return 0;
+	return _clkdm_clk_hwmod_enable(clkdm);
 }
 
 /**
@@ -850,9 +937,8 @@
  * clockdomain usecount goes to 0, put the clockdomain to sleep
  * (software-supervised mode) or remove the clkdm autodependencies
  * (hardware-supervised mode).  Returns -EINVAL if passed null
- * pointers; -ERANGE if the @clkdm usecount underflows and debugging
- * is enabled; or returns 0 upon success or if the clockdomain is in
- * hwsup idle mode.
+ * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
+ * upon success or if the clockdomain is in hwsup idle mode.
  */
 int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
 {
@@ -861,30 +947,72 @@
 	 * downstream clocks for debugging purposes?
 	 */
 
-	if (!clkdm || !clk)
+	if (!clk)
 		return -EINVAL;
 
-	if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable)
-		return -EINVAL;
+	return _clkdm_clk_hwmod_disable(clkdm);
+}
 
-#ifdef DEBUG
-	if (atomic_read(&clkdm->usecount) == 0) {
-		WARN_ON(1); /* underflow */
-		return -ERANGE;
-	}
-#endif
-
-	if (atomic_dec_return(&clkdm->usecount) > 0)
+/**
+ * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
+ * @clkdm: struct clockdomain *
+ * @oh: struct omap_hwmod * of the enabled downstream hwmod
+ *
+ * Increment the usecount of the clockdomain @clkdm and ensure that it
+ * is awake before @oh is enabled. Intended to be called by
+ * module_enable() code.
+ * If the clockdomain is in software-supervised idle mode, force the
+ * clockdomain to wake.  If the clockdomain is in hardware-supervised idle
+ * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
+ * clockdomain can be read from/written to by on-chip processors.
+ * Returns -EINVAL if passed null pointers;
+ * returns 0 upon success or if the clockdomain is in hwsup idle mode.
+ */
+int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
+{
+	/* The clkdm attribute does not exist yet prior OMAP4 */
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
 		return 0;
 
-	/* All downstream clocks of this clockdomain are now disabled */
+	/*
+	 * XXX Rewrite this code to maintain a list of enabled
+	 * downstream hwmods for debugging purposes?
+	 */
 
-	pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
-		 clk->name);
+	if (!oh)
+		return -EINVAL;
 
-	arch_clkdm->clkdm_clk_disable(clkdm);
-	pwrdm_clkdm_state_switch(clkdm);
+	return _clkdm_clk_hwmod_enable(clkdm);
+}
 
-	return 0;
+/**
+ * clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
+ * @clkdm: struct clockdomain *
+ * @oh: struct omap_hwmod * of the disabled downstream hwmod
+ *
+ * Decrement the usecount of this clockdomain @clkdm when @oh is
+ * disabled. Intended to be called by module_disable() code.
+ * If the clockdomain usecount goes to 0, put the clockdomain to sleep
+ * (software-supervised mode) or remove the clkdm autodependencies
+ * (hardware-supervised mode).
+ * Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
+ * underflows; or returns 0 upon success or if the clockdomain is in hwsup
+ * idle mode.
+ */
+int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
+{
+	/* The clkdm attribute does not exist yet prior OMAP4 */
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+		return 0;
+
+	/*
+	 * XXX Rewrite this code to maintain a list of enabled
+	 * downstream hwmods for debugging purposes?
+	 */
+
+	if (!oh)
+		return -EINVAL;
+
+	return _clkdm_clk_hwmod_disable(clkdm);
 }
 
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h
index 5823584..1e50c88 100644
--- a/arch/arm/mach-omap2/clockdomain.h
+++ b/arch/arm/mach-omap2/clockdomain.h
@@ -17,9 +17,11 @@
 #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
 
 #include <linux/init.h>
+#include <linux/spinlock.h>
 
 #include "powerdomain.h"
 #include <plat/clock.h>
+#include <plat/omap_hwmod.h>
 #include <plat/cpu.h>
 
 /*
@@ -82,6 +84,9 @@
 	const struct omap_chip_id omap_chip;
 };
 
+/* Possible flags for struct clockdomain._flags */
+#define _CLKDM_FLAG_HWSUP_ENABLED		BIT(0)
+
 /**
  * struct clockdomain - OMAP clockdomain
  * @name: clockdomain name
@@ -89,6 +94,7 @@
  * @clktrctrl_reg: CLKSTCTRL reg for the given clock domain
  * @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg
  * @flags: Clockdomain capability flags
+ * @_flags: Flags for use only by internal clockdomain code
  * @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit
  * @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers
  * @cm_inst: (OMAP4 only) CM instance register offset
@@ -113,6 +119,7 @@
 	} pwrdm;
 	const u16 clktrctrl_mask;
 	const u8 flags;
+	u8 _flags;
 	const u8 dep_bit;
 	const u8 prcm_partition;
 	const s16 cm_inst;
@@ -122,6 +129,7 @@
 	const struct omap_chip_id omap_chip;
 	atomic_t usecount;
 	struct list_head node;
+	spinlock_t lock;
 };
 
 /**
@@ -177,12 +185,15 @@
 
 void clkdm_allow_idle(struct clockdomain *clkdm);
 void clkdm_deny_idle(struct clockdomain *clkdm);
+bool clkdm_in_hwsup(struct clockdomain *clkdm);
 
 int clkdm_wakeup(struct clockdomain *clkdm);
 int clkdm_sleep(struct clockdomain *clkdm);
 
 int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
 int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
+int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh);
+int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh);
 
 extern void __init omap2xxx_clockdomains_init(void);
 extern void __init omap3xxx_clockdomains_init(void);
diff --git a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
index 48d0db7..f740edb 100644
--- a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
+++ b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
@@ -183,7 +183,8 @@
 		_clkdm_add_autodeps(clkdm);
 		_enable_hwsup(clkdm);
 	} else {
-		clkdm_wakeup(clkdm);
+		if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+			omap2_clkdm_wakeup(clkdm);
 	}
 
 	return 0;
@@ -205,7 +206,8 @@
 		_clkdm_del_autodeps(clkdm);
 		_enable_hwsup(clkdm);
 	} else {
-		clkdm_sleep(clkdm);
+		if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+			omap2_clkdm_sleep(clkdm);
 	}
 
 	return 0;
diff --git a/arch/arm/mach-omap2/clockdomain44xx.c b/arch/arm/mach-omap2/clockdomain44xx.c
index a1a4ecd..b43706a 100644
--- a/arch/arm/mach-omap2/clockdomain44xx.c
+++ b/arch/arm/mach-omap2/clockdomain44xx.c
@@ -95,13 +95,8 @@
 
 static int omap4_clkdm_clk_enable(struct clockdomain *clkdm)
 {
-	bool hwsup = false;
-
-	hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
-					clkdm->cm_inst, clkdm->clkdm_offs);
-
-	if (!hwsup)
-		clkdm_wakeup(clkdm);
+	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+		return omap4_clkdm_wakeup(clkdm);
 
 	return 0;
 }
@@ -113,8 +108,8 @@
 	hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
 					clkdm->cm_inst, clkdm->clkdm_offs);
 
-	if (!hwsup)
-		clkdm_sleep(clkdm);
+	if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
+		omap4_clkdm_sleep(clkdm);
 
 	return 0;
 }
diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c
index 66090f2..dccc651 100644
--- a/arch/arm/mach-omap2/clockdomains44xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains44xx_data.c
@@ -565,7 +565,7 @@
 };
 
 static struct clockdomain mpu_44xx_clkdm = {
-	.name		  = "mpu_clkdm",
+	.name		  = "mpuss_clkdm",
 	.pwrdm		  = { .name = "mpu_pwrdm" },
 	.prcm_partition	  = OMAP4430_CM1_PARTITION,
 	.cm_inst	  = OMAP4430_CM1_MPU_INST,
diff --git a/arch/arm/mach-omap2/cm-regbits-44xx.h b/arch/arm/mach-omap2/cm-regbits-44xx.h
index 0e77945..65597a7 100644
--- a/arch/arm/mach-omap2/cm-regbits-44xx.h
+++ b/arch/arm/mach-omap2/cm-regbits-44xx.h
@@ -101,6 +101,10 @@
 #define OMAP4430_CLKACTIVITY_CORE_DPLL_EMU_CLK_SHIFT		9
 #define OMAP4430_CLKACTIVITY_CORE_DPLL_EMU_CLK_MASK		(1 << 9)
 
+/* Used by CM_L4CFG_CLKSTCTRL */
+#define OMAP4460_CLKACTIVITY_CORE_TS_GFCLK_SHIFT		9
+#define OMAP4460_CLKACTIVITY_CORE_TS_GFCLK_MASK			(1 << 9)
+
 /* Used by CM_CEFUSE_CLKSTCTRL */
 #define OMAP4430_CLKACTIVITY_CUST_EFUSE_SYS_CLK_SHIFT		9
 #define OMAP4430_CLKACTIVITY_CUST_EFUSE_SYS_CLK_MASK		(1 << 9)
@@ -413,6 +417,10 @@
 #define OMAP4430_CLKACTIVITY_WKUP_32K_GFCLK_SHIFT		11
 #define OMAP4430_CLKACTIVITY_WKUP_32K_GFCLK_MASK		(1 << 11)
 
+/* Used by CM_WKUP_CLKSTCTRL */
+#define OMAP4460_CLKACTIVITY_WKUP_TS_GFCLK_SHIFT		13
+#define OMAP4460_CLKACTIVITY_WKUP_TS_GFCLK_MASK			(1 << 13)
+
 /*
  * Used by CM1_ABE_TIMER5_CLKCTRL, CM1_ABE_TIMER6_CLKCTRL,
  * CM1_ABE_TIMER7_CLKCTRL, CM1_ABE_TIMER8_CLKCTRL, CM_L3INIT_MMC1_CLKCTRL,
@@ -444,6 +452,10 @@
 #define OMAP4430_CLKSEL_60M_SHIFT				24
 #define OMAP4430_CLKSEL_60M_MASK				(1 << 24)
 
+/* Used by CM_MPU_MPU_CLKCTRL */
+#define OMAP4460_CLKSEL_ABE_DIV_MODE_SHIFT			25
+#define OMAP4460_CLKSEL_ABE_DIV_MODE_MASK			(1 << 25)
+
 /* Used by CM1_ABE_AESS_CLKCTRL */
 #define OMAP4430_CLKSEL_AESS_FCLK_SHIFT				24
 #define OMAP4430_CLKSEL_AESS_FCLK_MASK				(1 << 24)
@@ -460,6 +472,10 @@
 #define OMAP4430_CLKSEL_DIV_SHIFT				24
 #define OMAP4430_CLKSEL_DIV_MASK				(1 << 24)
 
+/* Used by CM_MPU_MPU_CLKCTRL */
+#define OMAP4460_CLKSEL_EMIF_DIV_MODE_SHIFT			24
+#define OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK			(1 << 24)
+
 /* Used by CM_CAM_FDIF_CLKCTRL */
 #define OMAP4430_CLKSEL_FCLK_SHIFT				24
 #define OMAP4430_CLKSEL_FCLK_MASK				(0x3 << 24)
@@ -555,6 +571,14 @@
 #define OMAP4430_D2D_STATDEP_SHIFT				18
 #define OMAP4430_D2D_STATDEP_MASK				(1 << 18)
 
+/* Used by CM_CLKSEL_DPLL_MPU */
+#define OMAP4460_DCC_COUNT_MAX_SHIFT				24
+#define OMAP4460_DCC_COUNT_MAX_MASK				(0xff << 24)
+
+/* Used by CM_CLKSEL_DPLL_MPU */
+#define OMAP4460_DCC_EN_SHIFT					22
+#define OMAP4460_DCC_EN_MASK					(1 << 22)
+
 /*
  * Used by CM_SSC_DELTAMSTEP_DPLL_ABE, CM_SSC_DELTAMSTEP_DPLL_CORE,
  * CM_SSC_DELTAMSTEP_DPLL_DDRPHY, CM_SSC_DELTAMSTEP_DPLL_IVA,
@@ -564,6 +588,10 @@
 #define OMAP4430_DELTAMSTEP_SHIFT				0
 #define OMAP4430_DELTAMSTEP_MASK				(0xfffff << 0)
 
+/* Renamed from DELTAMSTEP Used by CM_SSC_DELTAMSTEP_DPLL_USB */
+#define OMAP4460_DELTAMSTEP_0_20_SHIFT				0
+#define OMAP4460_DELTAMSTEP_0_20_MASK				(0x1fffff << 0)
+
 /* Used by CM_DLL_CTRL */
 #define OMAP4430_DLL_OVERRIDE_SHIFT				0
 #define OMAP4430_DLL_OVERRIDE_MASK				(1 << 0)
@@ -1106,6 +1134,10 @@
 #define OMAP4430_MODULEMODE_SHIFT				0
 #define OMAP4430_MODULEMODE_MASK				(0x3 << 0)
 
+/* Used by CM_L4CFG_DYNAMICDEP */
+#define OMAP4460_MPU_DYNDEP_SHIFT				19
+#define OMAP4460_MPU_DYNDEP_MASK				(1 << 19)
+
 /* Used by CM_DSS_DSS_CLKCTRL */
 #define OMAP4430_OPTFCLKEN_48MHZ_CLK_SHIFT			9
 #define OMAP4430_OPTFCLKEN_48MHZ_CLK_MASK			(1 << 9)
@@ -1198,6 +1230,10 @@
 #define OMAP4430_OPTFCLKEN_SYS_CLK_SHIFT			10
 #define OMAP4430_OPTFCLKEN_SYS_CLK_MASK				(1 << 10)
 
+/* Used by CM_WKUP_BANDGAP_CLKCTRL */
+#define OMAP4460_OPTFCLKEN_TS_FCLK_SHIFT			8
+#define OMAP4460_OPTFCLKEN_TS_FCLK_MASK				(1 << 8)
+
 /* Used by CM_DSS_DSS_CLKCTRL */
 #define OMAP4430_OPTFCLKEN_TV_CLK_SHIFT				11
 #define OMAP4430_OPTFCLKEN_TV_CLK_MASK				(1 << 11)
diff --git a/arch/arm/mach-omap2/cm44xx.h b/arch/arm/mach-omap2/cm44xx.h
index 0b87ec8..3380bee 100644
--- a/arch/arm/mach-omap2/cm44xx.h
+++ b/arch/arm/mach-omap2/cm44xx.h
@@ -1,7 +1,7 @@
 /*
  * OMAP4 Clock Management (CM) definitions
  *
- * Copyright (C) 2007-2009 Texas Instruments, Inc.
+ * Copyright (C) 2007-2011 Texas Instruments, Inc.
  * Copyright (C) 2007-2009 Nokia Corporation
  *
  * Written by Paul Walmsley
@@ -23,10 +23,4 @@
 #define OMAP4_CM_CLKSTCTRL				0x0000
 #define OMAP4_CM_STATICDEP				0x0004
 
-/* Function prototypes */
-# ifndef __ASSEMBLER__
-
-extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
-
-# endif
 #endif
diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c
index a482bfa..eb2a472 100644
--- a/arch/arm/mach-omap2/cminst44xx.c
+++ b/arch/arm/mach-omap2/cminst44xx.c
@@ -2,6 +2,7 @@
  * OMAP4 CM instance functions
  *
  * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments, Inc.
  * Paul Walmsley
  *
  * This program is free software; you can redistribute it and/or modify
@@ -32,6 +33,22 @@
 #include "prm44xx.h"
 #include "prcm_mpu44xx.h"
 
+/*
+ * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
+ *
+ *   0x0 func:     Module is fully functional, including OCP
+ *   0x1 trans:    Module is performing transition: wakeup, or sleep, or sleep
+ *                 abortion
+ *   0x2 idle:     Module is in Idle mode (only OCP part). It is functional if
+ *                 using separate functional clock
+ *   0x3 disabled: Module is disabled and cannot be accessed
+ *
+ */
+#define CLKCTRL_IDLEST_FUNCTIONAL		0x0
+#define CLKCTRL_IDLEST_INTRANSITION		0x1
+#define CLKCTRL_IDLEST_INTERFACE_IDLE		0x2
+#define CLKCTRL_IDLEST_DISABLED			0x3
+
 static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
 	[OMAP4430_INVALID_PRCM_PARTITION]	= 0,
 	[OMAP4430_PRM_PARTITION]		= OMAP4430_PRM_BASE,
@@ -41,6 +58,48 @@
 	[OMAP4430_PRCM_MPU_PARTITION]		= OMAP4430_PRCM_MPU_BASE,
 };
 
+/* Private functions */
+
+/**
+ * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
+ * bit 0.
+ */
+static u32 _clkctrl_idlest(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
+{
+	u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
+	v &= OMAP4430_IDLEST_MASK;
+	v >>= OMAP4430_IDLEST_SHIFT;
+	return v;
+}
+
+/**
+ * _is_module_ready - can module registers be accessed without causing an abort?
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
+ * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
+ */
+static bool _is_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
+{
+	u32 v;
+
+	v = _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs);
+
+	return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
+		v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
+}
+
+/* Public functions */
+
 /* Read a register in a CM instance */
 u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx)
 {
@@ -200,36 +259,93 @@
  */
 
 /**
- * omap4_cm_wait_module_ready - wait for a module to be in 'func' state
- * @clkctrl_reg: CLKCTRL module address
+ * omap4_cminst_wait_module_ready - wait for a module to be in 'func' state
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
  *
  * Wait for the module IDLEST to be functional. If the idle state is in any
  * the non functional state (trans, idle or disabled), module and thus the
  * sysconfig cannot be accessed and will probably lead to an "imprecise
  * external abort"
- *
- * Module idle state:
- *   0x0 func:     Module is fully functional, including OCP
- *   0x1 trans:    Module is performing transition: wakeup, or sleep, or sleep
- *                 abortion
- *   0x2 idle:     Module is in Idle mode (only OCP part). It is functional if
- *                 using separate functional clock
- *   0x3 disabled: Module is disabled and cannot be accessed
- *
  */
-int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg)
+int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs,
+				   u16 clkctrl_offs)
 {
 	int i = 0;
 
-	if (!clkctrl_reg)
+	if (!clkctrl_offs)
 		return 0;
 
-	omap_test_timeout((
-		((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) == 0) ||
-		 (((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) >>
-		  OMAP4430_IDLEST_SHIFT) == 0x2)),
-		MAX_MODULE_READY_TIME, i);
+	omap_test_timeout(_is_module_ready(part, inst, cdoffs, clkctrl_offs),
+			  MAX_MODULE_READY_TIME, i);
 
 	return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
 }
 
+/**
+ * omap4_cminst_wait_module_idle - wait for a module to be in 'disabled'
+ * state
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * Wait for the module IDLEST to be disabled. Some PRCM transition,
+ * like reset assertion or parent clock de-activation must wait the
+ * module to be fully disabled.
+ */
+int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
+{
+	int i = 0;
+
+	if (!clkctrl_offs)
+		return 0;
+
+	omap_test_timeout((_clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) ==
+			   CLKCTRL_IDLEST_DISABLED),
+			  MAX_MODULE_READY_TIME, i);
+
+	return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
+}
+
+/**
+ * omap4_cminst_module_enable - Enable the modulemode inside CLKCTRL
+ * @mode: Module mode (SW or HW)
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * No return value.
+ */
+void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
+			    u16 clkctrl_offs)
+{
+	u32 v;
+
+	v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
+	v &= ~OMAP4430_MODULEMODE_MASK;
+	v |= mode << OMAP4430_MODULEMODE_SHIFT;
+	omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
+}
+
+/**
+ * omap4_cminst_module_disable - Disable the module inside CLKCTRL
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * No return value.
+ */
+void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
+			     u16 clkctrl_offs)
+{
+	u32 v;
+
+	v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
+	v &= ~OMAP4430_MODULEMODE_MASK;
+	omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
+}
diff --git a/arch/arm/mach-omap2/cminst44xx.h b/arch/arm/mach-omap2/cminst44xx.h
index 2b32c18..f2ea645 100644
--- a/arch/arm/mach-omap2/cminst44xx.h
+++ b/arch/arm/mach-omap2/cminst44xx.h
@@ -17,6 +17,14 @@
 extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs);
 extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
 
+extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
+extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
+
+extern void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
+				       u16 clkctrl_offs);
+extern void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
+					u16 clkctrl_offs);
+
 /*
  * In an ideal world, we would not export these low-level functions,
  * but this will probably take some time to fix properly
@@ -32,6 +40,4 @@
 extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx,
 					   u32 mask);
 
-extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
-
 #endif
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 5b8ca68..1077ad6 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -230,7 +230,7 @@
 };
 
 int __init omap4_keyboard_init(struct omap4_keypad_platform_data
-						*sdp4430_keypad_data)
+			*sdp4430_keypad_data, struct omap_board_data *bdata)
 {
 	struct omap_device *od;
 	struct omap_hwmod *oh;
@@ -257,6 +257,7 @@
 						name, oh->name);
 		return PTR_ERR(od);
 	}
+	oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
 
 	return 0;
 }
diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index 66868c5..a9b45c7 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <mach/hardware.h>
 #include <plat/mmc.h>
 #include <plat/omap-pm.h>
@@ -213,12 +214,10 @@
 static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
 			int controller_nr)
 {
-	if ((mmc_controller->slots[0].switch_pin > 0) && \
-		(mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
+	if (gpio_is_valid(mmc_controller->slots[0].switch_pin))
 		omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
 					OMAP_PIN_INPUT_PULLUP);
-	if ((mmc_controller->slots[0].gpio_wp > 0) && \
-		(mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
+	if (gpio_is_valid(mmc_controller->slots[0].gpio_wp))
 		omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
 					OMAP_PIN_INPUT_PULLUP);
 	if (cpu_is_omap34xx()) {
diff --git a/arch/arm/mach-omap2/i2c.c b/arch/arm/mach-omap2/i2c.c
index 79c478c..ace9994 100644
--- a/arch/arm/mach-omap2/i2c.c
+++ b/arch/arm/mach-omap2/i2c.c
@@ -21,9 +21,19 @@
 
 #include <plat/cpu.h>
 #include <plat/i2c.h>
+#include <plat/common.h>
+#include <plat/omap_hwmod.h>
 
 #include "mux.h"
 
+/* In register I2C_CON, Bit 15 is the I2C enable bit */
+#define I2C_EN					BIT(15)
+#define OMAP2_I2C_CON_OFFSET			0x24
+#define OMAP4_I2C_CON_OFFSET			0xA4
+
+/* Maximum microseconds to wait for OMAP module to softreset */
+#define MAX_MODULE_SOFTRESET_WAIT	10000
+
 void __init omap2_i2c_mux_pins(int bus_id)
 {
 	char mux_name[sizeof("i2c2_scl.i2c2_scl")];
@@ -37,3 +47,61 @@
 	sprintf(mux_name, "i2c%i_sda.i2c%i_sda", bus_id, bus_id);
 	omap_mux_init_signal(mux_name, OMAP_PIN_INPUT);
 }
+
+/**
+ * omap_i2c_reset - reset the omap i2c module.
+ * @oh: struct omap_hwmod *
+ *
+ * The i2c moudle in omap2, omap3 had a special sequence to reset. The
+ * sequence is:
+ * - Disable the I2C.
+ * - Write to SOFTRESET bit.
+ * - Enable the I2C.
+ * - Poll on the RESETDONE bit.
+ * The sequence is implemented in below function. This is called for 2420,
+ * 2430 and omap3.
+ */
+int omap_i2c_reset(struct omap_hwmod *oh)
+{
+	u32 v;
+	u16 i2c_con;
+	int c = 0;
+
+	if (oh->class->rev == OMAP_I2C_IP_VERSION_2) {
+		i2c_con = OMAP4_I2C_CON_OFFSET;
+	} else if (oh->class->rev == OMAP_I2C_IP_VERSION_1) {
+		i2c_con = OMAP2_I2C_CON_OFFSET;
+	} else {
+		WARN(1, "Cannot reset I2C block %s: unsupported revision\n",
+		     oh->name);
+		return -EINVAL;
+	}
+
+	/* Disable I2C */
+	v = omap_hwmod_read(oh, i2c_con);
+	v &= ~I2C_EN;
+	omap_hwmod_write(v, oh, i2c_con);
+
+	/* Write to the SOFTRESET bit */
+	omap_hwmod_softreset(oh);
+
+	/* Enable I2C */
+	v = omap_hwmod_read(oh, i2c_con);
+	v |= I2C_EN;
+	omap_hwmod_write(v, oh, i2c_con);
+
+	/* Poll on RESETDONE bit */
+	omap_test_timeout((omap_hwmod_read(oh,
+				oh->class->sysc->syss_offs)
+				& SYSS_RESETDONE_MASK),
+				MAX_MODULE_SOFTRESET_WAIT, c);
+
+	if (c == MAX_MODULE_SOFTRESET_WAIT)
+		pr_warning("%s: %s: softreset failed (waited %d usec)\n",
+			__func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
+	else
+		pr_debug("%s: %s: softreset in %d usec\n", __func__,
+			oh->name, c);
+
+	return 0;
+}
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index 2537090..37efb86 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -31,7 +31,7 @@
 static struct omap_chip_id omap_chip;
 static unsigned int omap_revision;
 
-u32 omap3_features;
+u32 omap_features;
 
 unsigned int omap_rev(void)
 {
@@ -183,14 +183,14 @@
 #define OMAP3_CHECK_FEATURE(status,feat)				\
 	if (((status & OMAP3_ ##feat## _MASK) 				\
 		>> OMAP3_ ##feat## _SHIFT) != FEAT_ ##feat## _NONE) { 	\
-		omap3_features |= OMAP3_HAS_ ##feat;			\
+		omap_features |= OMAP3_HAS_ ##feat;			\
 	}
 
 static void __init omap3_check_features(void)
 {
 	u32 status;
 
-	omap3_features = 0;
+	omap_features = 0;
 
 	status = omap_ctrl_readl(OMAP3_CONTROL_OMAP_STATUS);
 
@@ -200,11 +200,11 @@
 	OMAP3_CHECK_FEATURE(status, NEON);
 	OMAP3_CHECK_FEATURE(status, ISP);
 	if (cpu_is_omap3630())
-		omap3_features |= OMAP3_HAS_192MHZ_CLK;
+		omap_features |= OMAP3_HAS_192MHZ_CLK;
 	if (!cpu_is_omap3505() && !cpu_is_omap3517())
-		omap3_features |= OMAP3_HAS_IO_WAKEUP;
+		omap_features |= OMAP3_HAS_IO_WAKEUP;
 
-	omap3_features |= OMAP3_HAS_SDRC;
+	omap_features |= OMAP3_HAS_SDRC;
 
 	/*
 	 * TODO: Get additional info (where applicable)
@@ -212,9 +212,34 @@
 	 */
 }
 
+static void __init omap4_check_features(void)
+{
+	u32 si_type;
+
+	if (cpu_is_omap443x())
+		omap_features |= OMAP4_HAS_MPU_1GHZ;
+
+
+	if (cpu_is_omap446x()) {
+		si_type =
+			read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1);
+		switch ((si_type & (3 << 16)) >> 16) {
+		case 2:
+			/* High performance device */
+			omap_features |= OMAP4_HAS_MPU_1_5GHZ;
+			break;
+		case 1:
+		default:
+			/* Standard device */
+			omap_features |= OMAP4_HAS_MPU_1_2GHZ;
+			break;
+		}
+	}
+}
+
 static void __init ti816x_check_features(void)
 {
-	omap3_features = OMAP3_HAS_NEON;
+	omap_features = OMAP3_HAS_NEON;
 }
 
 static void __init omap3_check_revision(void)
@@ -344,10 +369,10 @@
 	rev = (idcode >> 28) & 0xf;
 
 	/*
-	 * Few initial ES2.0 samples IDCODE is same as ES1.0
+	 * Few initial 4430 ES2.0 samples IDCODE is same as ES1.0
 	 * Use ARM register to detect the correct ES version
 	 */
-	if (!rev) {
+	if (!rev && (hawkeye != 0xb94e)) {
 		idcode = read_cpuid(CPUID_ID);
 		rev = (idcode & 0xf) - 1;
 	}
@@ -377,6 +402,15 @@
 			omap_chip.oc |= CHIP_IS_OMAP4430ES2_2;
 		}
 		break;
+	case 0xb94e:
+		switch (rev) {
+		case 0:
+		default:
+			omap_revision = OMAP4460_REV_ES1_0;
+			omap_chip.oc |= CHIP_IS_OMAP4460ES1_0;
+			break;
+		}
+		break;
 	default:
 		/* Unknown default to latest silicon rev as default */
 		omap_revision = OMAP4430_REV_ES2_2;
@@ -518,6 +552,7 @@
 		return;
 	} else if (cpu_is_omap44xx()) {
 		omap4_check_revision();
+		omap4_check_features();
 		return;
 	} else {
 		pr_err("OMAP revision unknown, please fix!\n");
diff --git a/arch/arm/mach-omap2/include/mach/clkdev.h b/arch/arm/mach-omap2/include/mach/clkdev.h
deleted file mode 100644
index 53b0274..0000000
--- a/arch/arm/mach-omap2/include/mach/clkdev.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * arch/arm/mach-omap2/include/mach/clkdev.h
- */
-
-#include <plat/clkdev.h>
diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c
index adb083e..f286012 100644
--- a/arch/arm/mach-omap2/iommu2.c
+++ b/arch/arm/mach-omap2/iommu2.c
@@ -225,8 +225,8 @@
 	attr = e->mixed << 5;
 	attr |= e->endian;
 	attr |= e->elsz >> 3;
-	attr <<= ((e->pgsz & MMU_CAM_PGSZ_4K) ? 0 : 6);
-
+	attr <<= (((e->pgsz == MMU_CAM_PGSZ_4K) ||
+			(e->pgsz == MMU_CAM_PGSZ_64K)) ? 0 : 6);
 	return attr;
 }
 
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
index 3fc5dc7..e61fead 100644
--- a/arch/arm/mach-omap2/omap-iommu.c
+++ b/arch/arm/mach-omap2/omap-iommu.c
@@ -67,7 +67,7 @@
 		.pdata = {
 			.name = "ducati",
 			.nr_tlb_entries = 32,
-			.clk_name = "ducati_ick",
+			.clk_name = "ipu_fck",
 			.da_start = 0x0,
 			.da_end = 0xFFFFF000,
 		},
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 7d242c9..84cc0bd 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -146,9 +146,10 @@
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"
-#include "cm44xx.h"
+#include "cminst44xx.h"
 #include "prm2xxx_3xxx.h"
 #include "prm44xx.h"
+#include "prminst44xx.h"
 #include "mux.h"
 
 /* Maximum microseconds to wait for OMAP module to softreset */
@@ -679,6 +680,56 @@
 }
 
 /**
+ * _enable_module - enable CLKCTRL modulemode on OMAP4
+ * @oh: struct omap_hwmod *
+ *
+ * Enables the PRCM module mode related to the hwmod @oh.
+ * No return value.
+ */
+static void _enable_module(struct omap_hwmod *oh)
+{
+	/* The module mode does not exist prior OMAP4 */
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+		return;
+
+	if (!oh->clkdm || !oh->prcm.omap4.modulemode)
+		return;
+
+	pr_debug("omap_hwmod: %s: _enable_module: %d\n",
+		 oh->name, oh->prcm.omap4.modulemode);
+
+	omap4_cminst_module_enable(oh->prcm.omap4.modulemode,
+				   oh->clkdm->prcm_partition,
+				   oh->clkdm->cm_inst,
+				   oh->clkdm->clkdm_offs,
+				   oh->prcm.omap4.clkctrl_offs);
+}
+
+/**
+ * _disable_module - enable CLKCTRL modulemode on OMAP4
+ * @oh: struct omap_hwmod *
+ *
+ * Disable the PRCM module mode related to the hwmod @oh.
+ * No return value.
+ */
+static void _disable_module(struct omap_hwmod *oh)
+{
+	/* The module mode does not exist prior OMAP4 */
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+		return;
+
+	if (!oh->clkdm || !oh->prcm.omap4.modulemode)
+		return;
+
+	pr_debug("omap_hwmod: %s: _disable_module\n", oh->name);
+
+	omap4_cminst_module_disable(oh->clkdm->prcm_partition,
+				    oh->clkdm->cm_inst,
+				    oh->clkdm->clkdm_offs,
+				    oh->prcm.omap4.clkctrl_offs);
+}
+
+/**
  * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh
  * @oh: struct omap_hwmod *oh
  *
@@ -990,9 +1041,40 @@
 
 	return oh;
 }
+/**
+ * _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod
+ * @oh: struct omap_hwmod *
+ *
+ * Convert a clockdomain name stored in a struct omap_hwmod into a
+ * clockdomain pointer, and save it into the struct omap_hwmod.
+ * return -EINVAL if clkdm_name does not exist or if the lookup failed.
+ */
+static int _init_clkdm(struct omap_hwmod *oh)
+{
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+		return 0;
+
+	if (!oh->clkdm_name) {
+		pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name);
+		return -EINVAL;
+	}
+
+	oh->clkdm = clkdm_lookup(oh->clkdm_name);
+	if (!oh->clkdm) {
+		pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n",
+			oh->name, oh->clkdm_name);
+		return -EINVAL;
+	}
+
+	pr_debug("omap_hwmod: %s: associated to clkdm %s\n",
+		oh->name, oh->clkdm_name);
+
+	return 0;
+}
 
 /**
- * _init_clocks - clk_get() all clocks associated with this hwmod
+ * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as
+ * well the clockdomain.
  * @oh: struct omap_hwmod *
  * @data: not used; pass NULL
  *
@@ -1012,6 +1094,7 @@
 	ret |= _init_main_clk(oh);
 	ret |= _init_interface_clks(oh);
 	ret |= _init_opt_clks(oh);
+	ret |= _init_clkdm(oh);
 
 	if (!ret)
 		oh->_state = _HWMOD_STATE_CLKS_INITED;
@@ -1028,7 +1111,7 @@
  * Wait for a module @oh to leave slave idle.  Returns 0 if the module
  * does not have an IDLEST bit or if the module successfully leaves
  * slave idle; otherwise, pass along the return value of the
- * appropriate *_cm_wait_module_ready() function.
+ * appropriate *_cm*_wait_module_ready() function.
  */
 static int _wait_target_ready(struct omap_hwmod *oh)
 {
@@ -1055,7 +1138,13 @@
 						 oh->prcm.omap2.idlest_reg_id,
 						 oh->prcm.omap2.idlest_idle_bit);
 	} else if (cpu_is_omap44xx()) {
-		ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg);
+		if (!oh->clkdm)
+			return -EINVAL;
+
+		ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition,
+						     oh->clkdm->cm_inst,
+						     oh->clkdm->clkdm_offs,
+						     oh->prcm.omap4.clkctrl_offs);
 	} else {
 		BUG();
 	};
@@ -1064,6 +1153,36 @@
 }
 
 /**
+ * _wait_target_disable - wait for a module to be disabled
+ * @oh: struct omap_hwmod *
+ *
+ * Wait for a module @oh to enter slave idle.  Returns 0 if the module
+ * does not have an IDLEST bit or if the module successfully enters
+ * slave idle; otherwise, pass along the return value of the
+ * appropriate *_cm*_wait_module_idle() function.
+ */
+static int _wait_target_disable(struct omap_hwmod *oh)
+{
+	/* TODO: For now just handle OMAP4+ */
+	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+		return 0;
+
+	if (!oh)
+		return -EINVAL;
+
+	if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
+		return 0;
+
+	if (oh->flags & HWMOD_NO_IDLEST)
+		return 0;
+
+	return omap4_cminst_wait_module_idle(oh->clkdm->prcm_partition,
+					     oh->clkdm->cm_inst,
+					     oh->clkdm->clkdm_offs,
+					     oh->prcm.omap4.clkctrl_offs);
+}
+
+/**
  * _lookup_hardreset - fill register bit info for this hwmod/reset line
  * @oh: struct omap_hwmod *
  * @name: name of the reset line in the context of this hwmod
@@ -1119,8 +1238,10 @@
 		return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs,
 						  ohri.rst_shift);
 	else if (cpu_is_omap44xx())
-		return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg,
-						  ohri.rst_shift);
+		return omap4_prminst_assert_hardreset(ohri.rst_shift,
+				  oh->clkdm->pwrdm.ptr->prcm_partition,
+				  oh->clkdm->pwrdm.ptr->prcm_offs,
+				  oh->prcm.omap4.rstctrl_offs);
 	else
 		return -EINVAL;
 }
@@ -1155,8 +1276,10 @@
 		if (ohri.st_shift)
 			pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
 			       oh->name, name);
-		ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg,
-						   ohri.rst_shift);
+		ret = omap4_prminst_deassert_hardreset(ohri.rst_shift,
+				  oh->clkdm->pwrdm.ptr->prcm_partition,
+				  oh->clkdm->pwrdm.ptr->prcm_offs,
+				  oh->prcm.omap4.rstctrl_offs);
 	} else {
 		return -EINVAL;
 	}
@@ -1191,8 +1314,10 @@
 		return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
 						       ohri.st_shift);
 	} else if (cpu_is_omap44xx()) {
-		return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg,
-						       ohri.rst_shift);
+		return omap4_prminst_is_hardreset_asserted(ohri.rst_shift,
+				  oh->clkdm->pwrdm.ptr->prcm_partition,
+				  oh->clkdm->pwrdm.ptr->prcm_offs,
+				  oh->prcm.omap4.rstctrl_offs);
 	} else {
 		return -EINVAL;
 	}
@@ -1312,6 +1437,7 @@
 static int _enable(struct omap_hwmod *oh)
 {
 	int r;
+	int hwsup = 0;
 
 	pr_debug("omap_hwmod: %s: enabling\n", oh->name);
 
@@ -1323,14 +1449,6 @@
 		return -EINVAL;
 	}
 
-	/* Mux pins for device runtime if populated */
-	if (oh->mux && (!oh->mux->enabled ||
-			((oh->_state == _HWMOD_STATE_IDLE) &&
-			 oh->mux->pads_dynamic)))
-		omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
-
-	_add_initiator_dep(oh, mpu_oh);
-	_enable_clocks(oh);
 
 	/*
 	 * If an IP contains only one HW reset line, then de-assert it in order
@@ -1341,22 +1459,56 @@
 	     oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
 		_deassert_hardreset(oh, oh->rst_lines[0].name);
 
-	r = _wait_target_ready(oh);
-	if (r) {
-		pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
-			 oh->name, r);
-		_disable_clocks(oh);
+	/* Mux pins for device runtime if populated */
+	if (oh->mux && (!oh->mux->enabled ||
+			((oh->_state == _HWMOD_STATE_IDLE) &&
+			 oh->mux->pads_dynamic)))
+		omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
 
-		return r;
+	_add_initiator_dep(oh, mpu_oh);
+
+	if (oh->clkdm) {
+		/*
+		 * A clockdomain must be in SW_SUP before enabling
+		 * completely the module. The clockdomain can be set
+		 * in HW_AUTO only when the module become ready.
+		 */
+		hwsup = clkdm_in_hwsup(oh->clkdm);
+		r = clkdm_hwmod_enable(oh->clkdm, oh);
+		if (r) {
+			WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
+			     oh->name, oh->clkdm->name, r);
+			return r;
+		}
 	}
 
-	oh->_state = _HWMOD_STATE_ENABLED;
+	_enable_clocks(oh);
+	_enable_module(oh);
 
-	/* Access the sysconfig only if the target is ready */
-	if (oh->class->sysc) {
-		if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
-			_update_sysc_cache(oh);
-		_enable_sysc(oh);
+	r = _wait_target_ready(oh);
+	if (!r) {
+		/*
+		 * Set the clockdomain to HW_AUTO only if the target is ready,
+		 * assuming that the previous state was HW_AUTO
+		 */
+		if (oh->clkdm && hwsup)
+			clkdm_allow_idle(oh->clkdm);
+
+		oh->_state = _HWMOD_STATE_ENABLED;
+
+		/* Access the sysconfig only if the target is ready */
+		if (oh->class->sysc) {
+			if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
+				_update_sysc_cache(oh);
+			_enable_sysc(oh);
+		}
+	} else {
+		_disable_clocks(oh);
+		pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
+			 oh->name, r);
+
+		if (oh->clkdm)
+			clkdm_hwmod_disable(oh->clkdm, oh);
 	}
 
 	return r;
@@ -1372,6 +1524,8 @@
  */
 static int _idle(struct omap_hwmod *oh)
 {
+	int ret;
+
 	pr_debug("omap_hwmod: %s: idling\n", oh->name);
 
 	if (oh->_state != _HWMOD_STATE_ENABLED) {
@@ -1383,7 +1537,20 @@
 	if (oh->class->sysc)
 		_idle_sysc(oh);
 	_del_initiator_dep(oh, mpu_oh);
+	_disable_module(oh);
+	ret = _wait_target_disable(oh);
+	if (ret)
+		pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
+			oh->name);
+	/*
+	 * The module must be in idle mode before disabling any parents
+	 * clocks. Otherwise, the parent clock might be disabled before
+	 * the module transition is done, and thus will prevent the
+	 * transition to complete properly.
+	 */
 	_disable_clocks(oh);
+	if (oh->clkdm)
+		clkdm_hwmod_disable(oh->clkdm, oh);
 
 	/* Mux pins for device idle if populated */
 	if (oh->mux && oh->mux->pads_dynamic)
@@ -1475,7 +1642,14 @@
 	if (oh->_state == _HWMOD_STATE_ENABLED) {
 		_del_initiator_dep(oh, mpu_oh);
 		/* XXX what about the other system initiators here? dma, dsp */
+		_disable_module(oh);
+		ret = _wait_target_disable(oh);
+		if (ret)
+			pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
+				oh->name);
 		_disable_clocks(oh);
+		if (oh->clkdm)
+			clkdm_hwmod_disable(oh->clkdm, oh);
 	}
 	/* XXX Should this code also force-disable the optional clocks? */
 
@@ -1656,6 +1830,33 @@
 }
 
 /**
+ * omap_hwmod_softreset - reset a module via SYSCONFIG.SOFTRESET bit
+ * @oh: struct omap_hwmod *
+ *
+ * This is a public function exposed to drivers. Some drivers may need to do
+ * some settings before and after resetting the device.  Those drivers after
+ * doing the necessary settings could use this function to start a reset by
+ * setting the SYSCONFIG.SOFTRESET bit.
+ */
+int omap_hwmod_softreset(struct omap_hwmod *oh)
+{
+	u32 v;
+	int ret;
+
+	if (!oh || !(oh->_sysc_cache))
+		return -EINVAL;
+
+	v = oh->_sysc_cache;
+	ret = _set_softreset(oh, &v);
+	if (ret)
+		goto error;
+	_write_sysconfig(v, oh);
+
+error:
+	return ret;
+}
+
+/**
  * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode
  * @oh: struct omap_hwmod *
  * @idlemode: SIDLEMODE field bits (shifted to bit 0)
diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
index f3901ab..a015c69 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
@@ -1029,9 +1029,16 @@
 static struct omap_hwmod_class i2c_class = {
 	.name		= "i2c",
 	.sysc		= &i2c_sysc,
+	.rev		= OMAP_I2C_IP_VERSION_1,
+	.reset		= &omap_i2c_reset,
 };
 
-static struct omap_i2c_dev_attr i2c_dev_attr;
+static struct omap_i2c_dev_attr i2c_dev_attr = {
+	.flags		= OMAP_I2C_FLAG_NO_FIFO |
+			  OMAP_I2C_FLAG_SIMPLE_CLOCK |
+			  OMAP_I2C_FLAG_16BIT_DATA_REG |
+			  OMAP_I2C_FLAG_BUS_SHIFT_2,
+};
 
 /* I2C1 */
 
diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
index 2a52f02..16743c7 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
@@ -1078,10 +1078,15 @@
 static struct omap_hwmod_class i2c_class = {
 	.name		= "i2c",
 	.sysc		= &i2c_sysc,
+	.rev		= OMAP_I2C_IP_VERSION_1,
+	.reset		= &omap_i2c_reset,
 };
 
 static struct omap_i2c_dev_attr i2c_dev_attr = {
 	.fifo_depth	= 8, /* bytes */
+	.flags		= OMAP_I2C_FLAG_APPLY_ERRATA_I207 |
+			  OMAP_I2C_FLAG_BUS_SHIFT_2 |
+			  OMAP_I2C_FLAG_FORCE_19200_INT_CLK,
 };
 
 /* I2C1 */
@@ -1092,6 +1097,7 @@
 
 static struct omap_hwmod omap2430_i2c1_hwmod = {
 	.name		= "i2c1",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap2_i2c1_mpu_irqs,
 	.sdma_reqs	= omap2_i2c1_sdma_reqs,
 	.main_clk	= "i2chs1_fck",
@@ -1127,6 +1133,7 @@
 
 static struct omap_hwmod omap2430_i2c2_hwmod = {
 	.name		= "i2c2",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap2_i2c2_mpu_irqs,
 	.sdma_reqs	= omap2_i2c2_sdma_reqs,
 	.main_clk	= "i2chs2_fck",
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 1a52716..25bf43b 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -1306,8 +1306,10 @@
 };
 
 static struct omap_hwmod_class i2c_class = {
-	.name = "i2c",
-	.sysc = &i2c_sysc,
+	.name	= "i2c",
+	.sysc	= &i2c_sysc,
+	.rev	= OMAP_I2C_IP_VERSION_1,
+	.reset	= &omap_i2c_reset,
 };
 
 static struct omap_hwmod_dma_info omap3xxx_dss_sdma_chs[] = {
@@ -1607,6 +1609,9 @@
 
 static struct omap_i2c_dev_attr i2c1_dev_attr = {
 	.fifo_depth	= 8, /* bytes */
+	.flags		= OMAP_I2C_FLAG_APPLY_ERRATA_I207 |
+			  OMAP_I2C_FLAG_RESET_REGS_POSTIDLE |
+			  OMAP_I2C_FLAG_BUS_SHIFT_2,
 };
 
 static struct omap_hwmod_ocp_if *omap3xxx_i2c1_slaves[] = {
@@ -1615,6 +1620,7 @@
 
 static struct omap_hwmod omap3xxx_i2c1_hwmod = {
 	.name		= "i2c1",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap2_i2c1_mpu_irqs,
 	.sdma_reqs	= omap2_i2c1_sdma_reqs,
 	.main_clk	= "i2c1_fck",
@@ -1638,6 +1644,9 @@
 
 static struct omap_i2c_dev_attr i2c2_dev_attr = {
 	.fifo_depth	= 8, /* bytes */
+	.flags = OMAP_I2C_FLAG_APPLY_ERRATA_I207 |
+		 OMAP_I2C_FLAG_RESET_REGS_POSTIDLE |
+		 OMAP_I2C_FLAG_BUS_SHIFT_2,
 };
 
 static struct omap_hwmod_ocp_if *omap3xxx_i2c2_slaves[] = {
@@ -1646,6 +1655,7 @@
 
 static struct omap_hwmod omap3xxx_i2c2_hwmod = {
 	.name		= "i2c2",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap2_i2c2_mpu_irqs,
 	.sdma_reqs	= omap2_i2c2_sdma_reqs,
 	.main_clk	= "i2c2_fck",
@@ -1669,6 +1679,9 @@
 
 static struct omap_i2c_dev_attr i2c3_dev_attr = {
 	.fifo_depth	= 64, /* bytes */
+	.flags = OMAP_I2C_FLAG_APPLY_ERRATA_I207 |
+		 OMAP_I2C_FLAG_RESET_REGS_POSTIDLE |
+		 OMAP_I2C_FLAG_BUS_SHIFT_2,
 };
 
 static struct omap_hwmod_irq_info i2c3_mpu_irqs[] = {
@@ -1688,6 +1701,7 @@
 
 static struct omap_hwmod omap3xxx_i2c3_hwmod = {
 	.name		= "i2c3",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= i2c3_mpu_irqs,
 	.sdma_reqs	= i2c3_sdma_reqs,
 	.main_clk	= "i2c3_fck",
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index e011437..6201422 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -22,11 +22,13 @@
 
 #include <plat/omap_hwmod.h>
 #include <plat/cpu.h>
+#include <plat/i2c.h>
 #include <plat/gpio.h>
 #include <plat/dma.h>
 #include <plat/mcspi.h>
 #include <plat/mcbsp.h>
 #include <plat/mmc.h>
+#include <plat/i2c.h>
 
 #include "omap_hwmod_common_data.h"
 
@@ -121,9 +123,16 @@
 static struct omap_hwmod omap44xx_dmm_hwmod = {
 	.name		= "dmm",
 	.class		= &omap44xx_dmm_hwmod_class,
-	.mpu_irqs	= omap44xx_dmm_irqs,
+	.clkdm_name	= "l3_emif_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_MEMIF_DMM_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_MEMIF_DMM_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_dmm_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dmm_slaves),
+	.mpu_irqs	= omap44xx_dmm_irqs,
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 };
 
@@ -171,6 +180,13 @@
 static struct omap_hwmod omap44xx_emif_fw_hwmod = {
 	.name		= "emif_fw",
 	.class		= &omap44xx_emif_fw_hwmod_class,
+	.clkdm_name	= "l3_emif_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_MEMIF_EMIF_FW_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_MEMIF_EMIF_FW_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_emif_fw_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_emif_fw_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -210,6 +226,14 @@
 static struct omap_hwmod omap44xx_l3_instr_hwmod = {
 	.name		= "l3_instr",
 	.class		= &omap44xx_l3_hwmod_class,
+	.clkdm_name	= "l3_instr_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L3INSTR_L3_INSTR_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INSTR_L3_INSTR_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
 	.slaves		= omap44xx_l3_instr_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l3_instr_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -302,7 +326,14 @@
 static struct omap_hwmod omap44xx_l3_main_1_hwmod = {
 	.name		= "l3_main_1",
 	.class		= &omap44xx_l3_hwmod_class,
+	.clkdm_name	= "l3_1_clkdm",
 	.mpu_irqs	= omap44xx_l3_main_1_irqs,
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L3_1_L3_1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3_1_L3_1_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l3_main_1_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l3_main_1_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -398,6 +429,13 @@
 static struct omap_hwmod omap44xx_l3_main_2_hwmod = {
 	.name		= "l3_main_2",
 	.class		= &omap44xx_l3_hwmod_class,
+	.clkdm_name	= "l3_2_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L3_2_L3_2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3_2_L3_2_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l3_main_2_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l3_main_2_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -448,6 +486,14 @@
 static struct omap_hwmod omap44xx_l3_main_3_hwmod = {
 	.name		= "l3_main_3",
 	.class		= &omap44xx_l3_hwmod_class,
+	.clkdm_name	= "l3_instr_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L3INSTR_L3_3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INSTR_L3_3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
 	.slaves		= omap44xx_l3_main_3_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l3_main_3_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -505,6 +551,12 @@
 static struct omap_hwmod omap44xx_l4_abe_hwmod = {
 	.name		= "l4_abe",
 	.class		= &omap44xx_l4_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM1_ABE_L4ABE_CLKCTRL_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l4_abe_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l4_abe_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -527,6 +579,13 @@
 static struct omap_hwmod omap44xx_l4_cfg_hwmod = {
 	.name		= "l4_cfg",
 	.class		= &omap44xx_l4_hwmod_class,
+	.clkdm_name	= "l4_cfg_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L4CFG_L4_CFG_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4CFG_L4_CFG_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l4_cfg_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l4_cfg_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -549,6 +608,13 @@
 static struct omap_hwmod omap44xx_l4_per_hwmod = {
 	.name		= "l4_per",
 	.class		= &omap44xx_l4_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_L4PER_L4PER_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_L4_PER_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l4_per_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l4_per_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -571,6 +637,13 @@
 static struct omap_hwmod omap44xx_l4_wkup_hwmod = {
 	.name		= "l4_wkup",
 	.class		= &omap44xx_l4_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_WKUP_L4WKUP_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_L4WKUP_CONTEXT_OFFSET,
+		},
+	},
 	.slaves		= omap44xx_l4_wkup_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_l4_wkup_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -601,6 +674,7 @@
 static struct omap_hwmod omap44xx_mpu_private_hwmod = {
 	.name		= "mpu_private",
 	.class		= &omap44xx_mpu_bus_hwmod_class,
+	.clkdm_name	= "mpuss_clkdm",
 	.slaves		= omap44xx_mpu_private_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_mpu_private_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -739,12 +813,15 @@
 static struct omap_hwmod omap44xx_aess_hwmod = {
 	.name		= "aess",
 	.class		= &omap44xx_aess_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_aess_irqs,
 	.sdma_reqs	= omap44xx_aess_sdma_reqs,
 	.main_clk	= "aess_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_AESS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_AESS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_AESS_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_aess_slaves,
@@ -771,9 +848,10 @@
 static struct omap_hwmod omap44xx_bandgap_hwmod = {
 	.name		= "bandgap",
 	.class		= &omap44xx_bandgap_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_BANDGAP_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_BANDGAP_CLKCTRL_OFFSET,
 		},
 	},
 	.opt_clks	= bandgap_opt_clks,
@@ -828,11 +906,13 @@
 static struct omap_hwmod omap44xx_counter_32k_hwmod = {
 	.name		= "counter_32k",
 	.class		= &omap44xx_counter_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.flags		= HWMOD_SWSUP_SIDLE,
 	.main_clk	= "sys_32k_ck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_SYNCTIMER_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_SYNCTIMER_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_SYNCTIMER_CONTEXT_OFFSET,
 		},
 	},
 	.slaves		= omap44xx_counter_32k_slaves,
@@ -911,11 +991,13 @@
 static struct omap_hwmod omap44xx_dma_system_hwmod = {
 	.name		= "dma_system",
 	.class		= &omap44xx_dma_hwmod_class,
+	.clkdm_name	= "l3_dma_clkdm",
 	.mpu_irqs	= omap44xx_dma_system_irqs,
 	.main_clk	= "l3_div_ck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_SDMA_SDMA_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_SDMA_SDMA_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_SDMA_SDMA_CONTEXT_OFFSET,
 		},
 	},
 	.dev_attr	= &dma_dev_attr,
@@ -1003,12 +1085,15 @@
 static struct omap_hwmod omap44xx_dmic_hwmod = {
 	.name		= "dmic",
 	.class		= &omap44xx_dmic_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_dmic_irqs,
 	.sdma_reqs	= omap44xx_dmic_sdma_reqs,
 	.main_clk	= "dmic_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_DMIC_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_DMIC_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_DMIC_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_dmic_slaves,
@@ -1070,12 +1155,13 @@
 static struct omap_hwmod omap44xx_dsp_c0_hwmod = {
 	.name		= "dsp_c0",
 	.class		= &omap44xx_dsp_hwmod_class,
+	.clkdm_name	= "tesla_clkdm",
 	.flags		= HWMOD_INIT_NO_RESET,
 	.rst_lines	= omap44xx_dsp_c0_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_dsp_c0_resets),
 	.prcm = {
 		.omap4 = {
-			.rstctrl_reg = OMAP4430_RM_TESLA_RSTCTRL,
+			.rstctrl_offs = OMAP4_RM_TESLA_RSTCTRL_OFFSET,
 		},
 	},
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1084,14 +1170,17 @@
 static struct omap_hwmod omap44xx_dsp_hwmod = {
 	.name		= "dsp",
 	.class		= &omap44xx_dsp_hwmod_class,
+	.clkdm_name	= "tesla_clkdm",
 	.mpu_irqs	= omap44xx_dsp_irqs,
 	.rst_lines	= omap44xx_dsp_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_dsp_resets),
 	.main_clk	= "dsp_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_TESLA_TESLA_CLKCTRL,
-			.rstctrl_reg = OMAP4430_RM_TESLA_RSTCTRL,
+			.clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET,
+			.rstctrl_offs = OMAP4_RM_TESLA_RSTCTRL_OFFSET,
+			.context_offs = OMAP4_RM_TESLA_TESLA_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.slaves		= omap44xx_dsp_slaves,
@@ -1136,7 +1225,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1175,10 +1264,12 @@
 static struct omap_hwmod omap44xx_dss_hwmod = {
 	.name		= "dss_core",
 	.class		= &omap44xx_dss_hwmod_class,
-	.main_clk	= "dss_fck",
+	.clkdm_name	= "l3_dss_clkdm",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
 	.opt_clks	= dss_opt_clks,
@@ -1238,7 +1329,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dispc = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_dispc_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_dispc_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1267,17 +1358,27 @@
 	&omap44xx_l4_per__dss_dispc,
 };
 
+static struct omap_hwmod_opt_clk dss_dispc_opt_clks[] = {
+	{ .role = "sys_clk", .clk = "dss_sys_clk" },
+	{ .role = "tv_clk", .clk = "dss_tv_clk" },
+	{ .role = "hdmi_clk", .clk = "dss_48mhz_clk" },
+};
+
 static struct omap_hwmod omap44xx_dss_dispc_hwmod = {
 	.name		= "dss_dispc",
 	.class		= &omap44xx_dispc_hwmod_class,
+	.clkdm_name	= "l3_dss_clkdm",
 	.mpu_irqs	= omap44xx_dss_dispc_irqs,
 	.sdma_reqs	= omap44xx_dss_dispc_sdma_reqs,
-	.main_clk	= "dss_fck",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
+	.opt_clks	= dss_dispc_opt_clks,
+	.opt_clks_cnt	= ARRAY_SIZE(dss_dispc_opt_clks),
 	.slaves		= omap44xx_dss_dispc_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_dispc_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1329,7 +1430,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dsi1 = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_dsi1_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_dsi1_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1358,17 +1459,25 @@
 	&omap44xx_l4_per__dss_dsi1,
 };
 
+static struct omap_hwmod_opt_clk dss_dsi1_opt_clks[] = {
+	{ .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
 static struct omap_hwmod omap44xx_dss_dsi1_hwmod = {
 	.name		= "dss_dsi1",
 	.class		= &omap44xx_dsi_hwmod_class,
+	.clkdm_name	= "l3_dss_clkdm",
 	.mpu_irqs	= omap44xx_dss_dsi1_irqs,
 	.sdma_reqs	= omap44xx_dss_dsi1_sdma_reqs,
-	.main_clk	= "dss_fck",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
+	.opt_clks	= dss_dsi1_opt_clks,
+	.opt_clks_cnt	= ARRAY_SIZE(dss_dsi1_opt_clks),
 	.slaves		= omap44xx_dss_dsi1_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_dsi1_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1399,7 +1508,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dsi2 = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_dsi2_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_dsi2_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1428,17 +1537,25 @@
 	&omap44xx_l4_per__dss_dsi2,
 };
 
+static struct omap_hwmod_opt_clk dss_dsi2_opt_clks[] = {
+	{ .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
 static struct omap_hwmod omap44xx_dss_dsi2_hwmod = {
 	.name		= "dss_dsi2",
 	.class		= &omap44xx_dsi_hwmod_class,
+	.clkdm_name	= "l3_dss_clkdm",
 	.mpu_irqs	= omap44xx_dss_dsi2_irqs,
 	.sdma_reqs	= omap44xx_dss_dsi2_sdma_reqs,
-	.main_clk	= "dss_fck",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
+	.opt_clks	= dss_dsi2_opt_clks,
+	.opt_clks_cnt	= ARRAY_SIZE(dss_dsi2_opt_clks),
 	.slaves		= omap44xx_dss_dsi2_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_dsi2_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1489,7 +1606,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_hdmi = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_hdmi_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_hdmi_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1518,17 +1635,25 @@
 	&omap44xx_l4_per__dss_hdmi,
 };
 
+static struct omap_hwmod_opt_clk dss_hdmi_opt_clks[] = {
+	{ .role = "sys_clk", .clk = "dss_sys_clk" },
+};
+
 static struct omap_hwmod omap44xx_dss_hdmi_hwmod = {
 	.name		= "dss_hdmi",
 	.class		= &omap44xx_hdmi_hwmod_class,
+	.clkdm_name	= "l3_dss_clkdm",
 	.mpu_irqs	= omap44xx_dss_hdmi_irqs,
 	.sdma_reqs	= omap44xx_dss_hdmi_sdma_reqs,
-	.main_clk	= "dss_fck",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
+	.opt_clks	= dss_hdmi_opt_clks,
+	.opt_clks_cnt	= ARRAY_SIZE(dss_hdmi_opt_clks),
 	.slaves		= omap44xx_dss_hdmi_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_hdmi_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1574,7 +1699,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_rfbi = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_rfbi_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_rfbi_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1603,16 +1728,24 @@
 	&omap44xx_l4_per__dss_rfbi,
 };
 
+static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = {
+	{ .role = "ick", .clk = "dss_fck" },
+};
+
 static struct omap_hwmod omap44xx_dss_rfbi_hwmod = {
 	.name		= "dss_rfbi",
 	.class		= &omap44xx_rfbi_hwmod_class,
+	.clkdm_name	= "l3_dss_clkdm",
 	.sdma_reqs	= omap44xx_dss_rfbi_sdma_reqs,
-	.main_clk	= "dss_fck",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
+	.opt_clks	= dss_rfbi_opt_clks,
+	.opt_clks_cnt	= ARRAY_SIZE(dss_rfbi_opt_clks),
 	.slaves		= omap44xx_dss_rfbi_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_rfbi_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -1642,7 +1775,7 @@
 static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_venc = {
 	.master		= &omap44xx_l3_main_2_hwmod,
 	.slave		= &omap44xx_dss_venc_hwmod,
-	.clk		= "l3_div_ck",
+	.clk		= "dss_fck",
 	.addr		= omap44xx_dss_venc_dma_addrs,
 	.user		= OCP_USER_SDMA,
 };
@@ -1674,10 +1807,12 @@
 static struct omap_hwmod omap44xx_dss_venc_hwmod = {
 	.name		= "dss_venc",
 	.class		= &omap44xx_venc_hwmod_class,
-	.main_clk	= "dss_fck",
+	.clkdm_name	= "l3_dss_clkdm",
+	.main_clk	= "dss_dss_clk",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
 		},
 	},
 	.slaves		= omap44xx_dss_venc_slaves,
@@ -1751,11 +1886,14 @@
 static struct omap_hwmod omap44xx_gpio1_hwmod = {
 	.name		= "gpio1",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.mpu_irqs	= omap44xx_gpio1_irqs,
 	.main_clk	= "gpio1_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_GPIO1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_GPIO1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_GPIO1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio1_opt_clks,
@@ -1803,12 +1941,15 @@
 static struct omap_hwmod omap44xx_gpio2_hwmod = {
 	.name		= "gpio2",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.mpu_irqs	= omap44xx_gpio2_irqs,
 	.main_clk	= "gpio2_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_GPIO2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_GPIO2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_GPIO2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio2_opt_clks,
@@ -1856,12 +1997,15 @@
 static struct omap_hwmod omap44xx_gpio3_hwmod = {
 	.name		= "gpio3",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.mpu_irqs	= omap44xx_gpio3_irqs,
 	.main_clk	= "gpio3_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_GPIO3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_GPIO3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_GPIO3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio3_opt_clks,
@@ -1909,12 +2053,15 @@
 static struct omap_hwmod omap44xx_gpio4_hwmod = {
 	.name		= "gpio4",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.mpu_irqs	= omap44xx_gpio4_irqs,
 	.main_clk	= "gpio4_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_GPIO4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_GPIO4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_GPIO4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio4_opt_clks,
@@ -1962,12 +2109,15 @@
 static struct omap_hwmod omap44xx_gpio5_hwmod = {
 	.name		= "gpio5",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.mpu_irqs	= omap44xx_gpio5_irqs,
 	.main_clk	= "gpio5_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_GPIO5_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_GPIO5_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_GPIO5_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio5_opt_clks,
@@ -2015,12 +2165,15 @@
 static struct omap_hwmod omap44xx_gpio6_hwmod = {
 	.name		= "gpio6",
 	.class		= &omap44xx_gpio_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.mpu_irqs	= omap44xx_gpio6_irqs,
 	.main_clk	= "gpio6_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_GPIO6_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_GPIO6_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_GPIO6_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= gpio6_opt_clks,
@@ -2094,11 +2247,14 @@
 static struct omap_hwmod omap44xx_hsi_hwmod = {
 	.name		= "hsi",
 	.class		= &omap44xx_hsi_hwmod_class,
+	.clkdm_name	= "l3_init_clkdm",
 	.mpu_irqs	= omap44xx_hsi_irqs,
 	.main_clk	= "hsi_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L3INIT_HSI_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L3INIT_HSI_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INIT_HSI_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.slaves		= omap44xx_hsi_slaves,
@@ -2127,6 +2283,12 @@
 static struct omap_hwmod_class omap44xx_i2c_hwmod_class = {
 	.name	= "i2c",
 	.sysc	= &omap44xx_i2c_sysc,
+	.rev	= OMAP_I2C_IP_VERSION_2,
+	.reset	= &omap_i2c_reset,
+};
+
+static struct omap_i2c_dev_attr i2c_dev_attr = {
+	.flags	= OMAP_I2C_FLAG_BUS_SHIFT_NONE,
 };
 
 /* i2c1 */
@@ -2168,17 +2330,21 @@
 static struct omap_hwmod omap44xx_i2c1_hwmod = {
 	.name		= "i2c1",
 	.class		= &omap44xx_i2c_hwmod_class,
-	.flags		= HWMOD_INIT_NO_RESET,
+	.clkdm_name	= "l4_per_clkdm",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap44xx_i2c1_irqs,
 	.sdma_reqs	= omap44xx_i2c1_sdma_reqs,
 	.main_clk	= "i2c1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_I2C1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_I2C1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_I2C1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_i2c1_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_i2c1_slaves),
+	.dev_attr	= &i2c_dev_attr,
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 };
 
@@ -2221,17 +2387,21 @@
 static struct omap_hwmod omap44xx_i2c2_hwmod = {
 	.name		= "i2c2",
 	.class		= &omap44xx_i2c_hwmod_class,
-	.flags		= HWMOD_INIT_NO_RESET,
+	.clkdm_name	= "l4_per_clkdm",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap44xx_i2c2_irqs,
 	.sdma_reqs	= omap44xx_i2c2_sdma_reqs,
 	.main_clk	= "i2c2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_I2C2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_I2C2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_I2C2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_i2c2_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_i2c2_slaves),
+	.dev_attr	= &i2c_dev_attr,
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 };
 
@@ -2274,17 +2444,21 @@
 static struct omap_hwmod omap44xx_i2c3_hwmod = {
 	.name		= "i2c3",
 	.class		= &omap44xx_i2c_hwmod_class,
-	.flags		= HWMOD_INIT_NO_RESET,
+	.clkdm_name	= "l4_per_clkdm",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap44xx_i2c3_irqs,
 	.sdma_reqs	= omap44xx_i2c3_sdma_reqs,
 	.main_clk	= "i2c3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_I2C3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_I2C3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_I2C3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_i2c3_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_i2c3_slaves),
+	.dev_attr	= &i2c_dev_attr,
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 };
 
@@ -2327,17 +2501,21 @@
 static struct omap_hwmod omap44xx_i2c4_hwmod = {
 	.name		= "i2c4",
 	.class		= &omap44xx_i2c_hwmod_class,
-	.flags		= HWMOD_INIT_NO_RESET,
+	.clkdm_name	= "l4_per_clkdm",
+	.flags		= HWMOD_16BIT_REG,
 	.mpu_irqs	= omap44xx_i2c4_irqs,
 	.sdma_reqs	= omap44xx_i2c4_sdma_reqs,
 	.main_clk	= "i2c4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_I2C4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_I2C4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_I2C4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_i2c4_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_i2c4_slaves),
+	.dev_attr	= &i2c_dev_attr,
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 };
 
@@ -2390,12 +2568,13 @@
 static struct omap_hwmod omap44xx_ipu_c0_hwmod = {
 	.name		= "ipu_c0",
 	.class		= &omap44xx_ipu_hwmod_class,
+	.clkdm_name	= "ducati_clkdm",
 	.flags		= HWMOD_INIT_NO_RESET,
 	.rst_lines	= omap44xx_ipu_c0_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_ipu_c0_resets),
 	.prcm = {
 		.omap4 = {
-			.rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL,
+			.rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET,
 		},
 	},
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -2405,12 +2584,13 @@
 static struct omap_hwmod omap44xx_ipu_c1_hwmod = {
 	.name		= "ipu_c1",
 	.class		= &omap44xx_ipu_hwmod_class,
+	.clkdm_name	= "ducati_clkdm",
 	.flags		= HWMOD_INIT_NO_RESET,
 	.rst_lines	= omap44xx_ipu_c1_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_ipu_c1_resets),
 	.prcm = {
 		.omap4 = {
-			.rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL,
+			.rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET,
 		},
 	},
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -2419,14 +2599,17 @@
 static struct omap_hwmod omap44xx_ipu_hwmod = {
 	.name		= "ipu",
 	.class		= &omap44xx_ipu_hwmod_class,
+	.clkdm_name	= "ducati_clkdm",
 	.mpu_irqs	= omap44xx_ipu_irqs,
 	.rst_lines	= omap44xx_ipu_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_ipu_resets),
 	.main_clk	= "ipu_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_DUCATI_DUCATI_CLKCTRL,
-			.rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL,
+			.clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET,
+			.rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET,
+			.context_offs = OMAP4_RM_DUCATI_DUCATI_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.slaves		= omap44xx_ipu_slaves,
@@ -2506,12 +2689,15 @@
 static struct omap_hwmod omap44xx_iss_hwmod = {
 	.name		= "iss",
 	.class		= &omap44xx_iss_hwmod_class,
+	.clkdm_name	= "iss_clkdm",
 	.mpu_irqs	= omap44xx_iss_irqs,
 	.sdma_reqs	= omap44xx_iss_sdma_reqs,
 	.main_clk	= "iss_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_CAM_ISS_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_CAM_ISS_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_CAM_ISS_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.opt_clks	= iss_opt_clks,
@@ -2586,12 +2772,13 @@
 static struct omap_hwmod omap44xx_iva_seq0_hwmod = {
 	.name		= "iva_seq0",
 	.class		= &omap44xx_iva_hwmod_class,
+	.clkdm_name	= "ivahd_clkdm",
 	.flags		= HWMOD_INIT_NO_RESET,
 	.rst_lines	= omap44xx_iva_seq0_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_iva_seq0_resets),
 	.prcm = {
 		.omap4 = {
-			.rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL,
+			.rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET,
 		},
 	},
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -2601,12 +2788,13 @@
 static struct omap_hwmod omap44xx_iva_seq1_hwmod = {
 	.name		= "iva_seq1",
 	.class		= &omap44xx_iva_hwmod_class,
+	.clkdm_name	= "ivahd_clkdm",
 	.flags		= HWMOD_INIT_NO_RESET,
 	.rst_lines	= omap44xx_iva_seq1_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_iva_seq1_resets),
 	.prcm = {
 		.omap4 = {
-			.rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL,
+			.rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET,
 		},
 	},
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
@@ -2615,14 +2803,17 @@
 static struct omap_hwmod omap44xx_iva_hwmod = {
 	.name		= "iva",
 	.class		= &omap44xx_iva_hwmod_class,
+	.clkdm_name	= "ivahd_clkdm",
 	.mpu_irqs	= omap44xx_iva_irqs,
 	.rst_lines	= omap44xx_iva_resets,
 	.rst_lines_cnt	= ARRAY_SIZE(omap44xx_iva_resets),
 	.main_clk	= "iva_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_IVAHD_IVAHD_CLKCTRL,
-			.rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL,
+			.clkctrl_offs = OMAP4_CM_IVAHD_IVAHD_CLKCTRL_OFFSET,
+			.rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET,
+			.context_offs = OMAP4_RM_IVAHD_IVAHD_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.slaves		= omap44xx_iva_slaves,
@@ -2687,11 +2878,14 @@
 static struct omap_hwmod omap44xx_kbd_hwmod = {
 	.name		= "kbd",
 	.class		= &omap44xx_kbd_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.mpu_irqs	= omap44xx_kbd_irqs,
 	.main_clk	= "kbd_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_KEYBOARD_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_KEYBOARD_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_KEYBOARD_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_kbd_slaves,
@@ -2752,10 +2946,12 @@
 static struct omap_hwmod omap44xx_mailbox_hwmod = {
 	.name		= "mailbox",
 	.class		= &omap44xx_mailbox_hwmod_class,
+	.clkdm_name	= "l4_cfg_clkdm",
 	.mpu_irqs	= omap44xx_mailbox_irqs,
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4CFG_MAILBOX_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4CFG_MAILBOX_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4CFG_MAILBOX_CONTEXT_OFFSET,
 		},
 	},
 	.slaves		= omap44xx_mailbox_slaves,
@@ -2842,12 +3038,15 @@
 static struct omap_hwmod omap44xx_mcbsp1_hwmod = {
 	.name		= "mcbsp1",
 	.class		= &omap44xx_mcbsp_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_mcbsp1_irqs,
 	.sdma_reqs	= omap44xx_mcbsp1_sdma_reqs,
 	.main_clk	= "mcbsp1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_MCBSP1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_MCBSP1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_MCBSP1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mcbsp1_slaves,
@@ -2915,12 +3114,15 @@
 static struct omap_hwmod omap44xx_mcbsp2_hwmod = {
 	.name		= "mcbsp2",
 	.class		= &omap44xx_mcbsp_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_mcbsp2_irqs,
 	.sdma_reqs	= omap44xx_mcbsp2_sdma_reqs,
 	.main_clk	= "mcbsp2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_MCBSP2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_MCBSP2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_MCBSP2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mcbsp2_slaves,
@@ -2988,12 +3190,15 @@
 static struct omap_hwmod omap44xx_mcbsp3_hwmod = {
 	.name		= "mcbsp3",
 	.class		= &omap44xx_mcbsp_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_mcbsp3_irqs,
 	.sdma_reqs	= omap44xx_mcbsp3_sdma_reqs,
 	.main_clk	= "mcbsp3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_MCBSP3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_MCBSP3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_MCBSP3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mcbsp3_slaves,
@@ -3040,12 +3245,15 @@
 static struct omap_hwmod omap44xx_mcbsp4_hwmod = {
 	.name		= "mcbsp4",
 	.class		= &omap44xx_mcbsp_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mcbsp4_irqs,
 	.sdma_reqs	= omap44xx_mcbsp4_sdma_reqs,
 	.main_clk	= "mcbsp4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MCBSP4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MCBSP4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MCBSP4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mcbsp4_slaves,
@@ -3132,12 +3340,15 @@
 static struct omap_hwmod omap44xx_mcpdm_hwmod = {
 	.name		= "mcpdm",
 	.class		= &omap44xx_mcpdm_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_mcpdm_irqs,
 	.sdma_reqs	= omap44xx_mcpdm_sdma_reqs,
 	.main_clk	= "mcpdm_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_PDM_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_PDM_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_PDM_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mcpdm_slaves,
@@ -3217,12 +3428,15 @@
 static struct omap_hwmod omap44xx_mcspi1_hwmod = {
 	.name		= "mcspi1",
 	.class		= &omap44xx_mcspi_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mcspi1_irqs,
 	.sdma_reqs	= omap44xx_mcspi1_sdma_reqs,
 	.main_clk	= "mcspi1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MCSPI1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MCSPI1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MCSPI1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.dev_attr	= &mcspi1_dev_attr,
@@ -3277,12 +3491,15 @@
 static struct omap_hwmod omap44xx_mcspi2_hwmod = {
 	.name		= "mcspi2",
 	.class		= &omap44xx_mcspi_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mcspi2_irqs,
 	.sdma_reqs	= omap44xx_mcspi2_sdma_reqs,
 	.main_clk	= "mcspi2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MCSPI2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MCSPI2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MCSPI2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.dev_attr	= &mcspi2_dev_attr,
@@ -3337,12 +3554,15 @@
 static struct omap_hwmod omap44xx_mcspi3_hwmod = {
 	.name		= "mcspi3",
 	.class		= &omap44xx_mcspi_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mcspi3_irqs,
 	.sdma_reqs	= omap44xx_mcspi3_sdma_reqs,
 	.main_clk	= "mcspi3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MCSPI3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MCSPI3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MCSPI3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.dev_attr	= &mcspi3_dev_attr,
@@ -3395,12 +3615,15 @@
 static struct omap_hwmod omap44xx_mcspi4_hwmod = {
 	.name		= "mcspi4",
 	.class		= &omap44xx_mcspi_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mcspi4_irqs,
 	.sdma_reqs	= omap44xx_mcspi4_sdma_reqs,
 	.main_clk	= "mcspi4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MCSPI4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MCSPI4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MCSPI4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.dev_attr	= &mcspi4_dev_attr,
@@ -3479,12 +3702,15 @@
 static struct omap_hwmod omap44xx_mmc1_hwmod = {
 	.name		= "mmc1",
 	.class		= &omap44xx_mmc_hwmod_class,
+	.clkdm_name	= "l3_init_clkdm",
 	.mpu_irqs	= omap44xx_mmc1_irqs,
 	.sdma_reqs	= omap44xx_mmc1_sdma_reqs,
 	.main_clk	= "mmc1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L3INIT_MMC1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L3INIT_MMC1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INIT_MMC1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.dev_attr	= &mmc1_dev_attr,
@@ -3538,12 +3764,15 @@
 static struct omap_hwmod omap44xx_mmc2_hwmod = {
 	.name		= "mmc2",
 	.class		= &omap44xx_mmc_hwmod_class,
+	.clkdm_name	= "l3_init_clkdm",
 	.mpu_irqs	= omap44xx_mmc2_irqs,
 	.sdma_reqs	= omap44xx_mmc2_sdma_reqs,
 	.main_clk	= "mmc2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L3INIT_MMC2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L3INIT_MMC2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INIT_MMC2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mmc2_slaves,
@@ -3592,12 +3821,15 @@
 static struct omap_hwmod omap44xx_mmc3_hwmod = {
 	.name		= "mmc3",
 	.class		= &omap44xx_mmc_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mmc3_irqs,
 	.sdma_reqs	= omap44xx_mmc3_sdma_reqs,
 	.main_clk	= "mmc3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MMCSD3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MMCSD3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MMCSD3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mmc3_slaves,
@@ -3644,13 +3876,16 @@
 static struct omap_hwmod omap44xx_mmc4_hwmod = {
 	.name		= "mmc4",
 	.class		= &omap44xx_mmc_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mmc4_irqs,
 
 	.sdma_reqs	= omap44xx_mmc4_sdma_reqs,
 	.main_clk	= "mmc4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MMCSD4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MMCSD4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MMCSD4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mmc4_slaves,
@@ -3697,12 +3932,15 @@
 static struct omap_hwmod omap44xx_mmc5_hwmod = {
 	.name		= "mmc5",
 	.class		= &omap44xx_mmc_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_mmc5_irqs,
 	.sdma_reqs	= omap44xx_mmc5_sdma_reqs,
 	.main_clk	= "mmc5_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_MMCSD5_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_MMCSD5_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_MMCSD5_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_mmc5_slaves,
@@ -3737,12 +3975,14 @@
 static struct omap_hwmod omap44xx_mpu_hwmod = {
 	.name		= "mpu",
 	.class		= &omap44xx_mpu_hwmod_class,
+	.clkdm_name	= "mpuss_clkdm",
 	.flags		= HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
 	.mpu_irqs	= omap44xx_mpu_irqs,
 	.main_clk	= "dpll_mpu_m2_ck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_MPU_MPU_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_MPU_MPU_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_MPU_MPU_CONTEXT_OFFSET,
 		},
 	},
 	.masters	= omap44xx_mpu_masters,
@@ -3809,13 +4049,16 @@
 static struct omap_hwmod omap44xx_smartreflex_core_hwmod = {
 	.name		= "smartreflex_core",
 	.class		= &omap44xx_smartreflex_hwmod_class,
+	.clkdm_name	= "l4_ao_clkdm",
 	.mpu_irqs	= omap44xx_smartreflex_core_irqs,
 
 	.main_clk	= "smartreflex_core_fck",
 	.vdd_name	= "core",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_ALWON_SR_CORE_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_ALWON_SR_CORE_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ALWON_SR_CORE_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_smartreflex_core_slaves,
@@ -3856,12 +4099,15 @@
 static struct omap_hwmod omap44xx_smartreflex_iva_hwmod = {
 	.name		= "smartreflex_iva",
 	.class		= &omap44xx_smartreflex_hwmod_class,
+	.clkdm_name	= "l4_ao_clkdm",
 	.mpu_irqs	= omap44xx_smartreflex_iva_irqs,
 	.main_clk	= "smartreflex_iva_fck",
 	.vdd_name	= "iva",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_ALWON_SR_IVA_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_ALWON_SR_IVA_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ALWON_SR_IVA_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_smartreflex_iva_slaves,
@@ -3902,12 +4148,15 @@
 static struct omap_hwmod omap44xx_smartreflex_mpu_hwmod = {
 	.name		= "smartreflex_mpu",
 	.class		= &omap44xx_smartreflex_hwmod_class,
+	.clkdm_name	= "l4_ao_clkdm",
 	.mpu_irqs	= omap44xx_smartreflex_mpu_irqs,
 	.main_clk	= "smartreflex_mpu_fck",
 	.vdd_name	= "mpu",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_ALWON_SR_MPU_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_ALWON_SR_MPU_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ALWON_SR_MPU_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_smartreflex_mpu_slaves,
@@ -3966,9 +4215,11 @@
 static struct omap_hwmod omap44xx_spinlock_hwmod = {
 	.name		= "spinlock",
 	.class		= &omap44xx_spinlock_hwmod_class,
+	.clkdm_name	= "l4_cfg_clkdm",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4CFG_HW_SEM_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4CFG_HW_SEM_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4CFG_HW_SEM_CONTEXT_OFFSET,
 		},
 	},
 	.slaves		= omap44xx_spinlock_slaves,
@@ -4047,11 +4298,14 @@
 static struct omap_hwmod omap44xx_timer1_hwmod = {
 	.name		= "timer1",
 	.class		= &omap44xx_timer_1ms_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.mpu_irqs	= omap44xx_timer1_irqs,
 	.main_clk	= "timer1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_TIMER1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_TIMER1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_TIMER1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer1_slaves,
@@ -4092,11 +4346,14 @@
 static struct omap_hwmod omap44xx_timer2_hwmod = {
 	.name		= "timer2",
 	.class		= &omap44xx_timer_1ms_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer2_irqs,
 	.main_clk	= "timer2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer2_slaves,
@@ -4137,11 +4394,14 @@
 static struct omap_hwmod omap44xx_timer3_hwmod = {
 	.name		= "timer3",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer3_irqs,
 	.main_clk	= "timer3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer3_slaves,
@@ -4182,11 +4442,14 @@
 static struct omap_hwmod omap44xx_timer4_hwmod = {
 	.name		= "timer4",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer4_irqs,
 	.main_clk	= "timer4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer4_slaves,
@@ -4246,11 +4509,14 @@
 static struct omap_hwmod omap44xx_timer5_hwmod = {
 	.name		= "timer5",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_timer5_irqs,
 	.main_clk	= "timer5_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_TIMER5_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_TIMER5_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_TIMER5_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer5_slaves,
@@ -4310,12 +4576,15 @@
 static struct omap_hwmod omap44xx_timer6_hwmod = {
 	.name		= "timer6",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_timer6_irqs,
 
 	.main_clk	= "timer6_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_TIMER6_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_TIMER6_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_TIMER6_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer6_slaves,
@@ -4375,11 +4644,14 @@
 static struct omap_hwmod omap44xx_timer7_hwmod = {
 	.name		= "timer7",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_timer7_irqs,
 	.main_clk	= "timer7_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_TIMER7_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_TIMER7_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_TIMER7_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer7_slaves,
@@ -4439,11 +4711,14 @@
 static struct omap_hwmod omap44xx_timer8_hwmod = {
 	.name		= "timer8",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_timer8_irqs,
 	.main_clk	= "timer8_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_TIMER8_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_TIMER8_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_TIMER8_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer8_slaves,
@@ -4484,11 +4759,14 @@
 static struct omap_hwmod omap44xx_timer9_hwmod = {
 	.name		= "timer9",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer9_irqs,
 	.main_clk	= "timer9_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER9_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER9_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER9_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer9_slaves,
@@ -4529,11 +4807,14 @@
 static struct omap_hwmod omap44xx_timer10_hwmod = {
 	.name		= "timer10",
 	.class		= &omap44xx_timer_1ms_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer10_irqs,
 	.main_clk	= "timer10_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER10_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER10_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER10_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer10_slaves,
@@ -4574,11 +4855,14 @@
 static struct omap_hwmod omap44xx_timer11_hwmod = {
 	.name		= "timer11",
 	.class		= &omap44xx_timer_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_timer11_irqs,
 	.main_clk	= "timer11_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER11_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_DMTIMER11_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_DMTIMER11_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_timer11_slaves,
@@ -4647,12 +4931,15 @@
 static struct omap_hwmod omap44xx_uart1_hwmod = {
 	.name		= "uart1",
 	.class		= &omap44xx_uart_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_uart1_irqs,
 	.sdma_reqs	= omap44xx_uart1_sdma_reqs,
 	.main_clk	= "uart1_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_UART1_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_UART1_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_UART1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_uart1_slaves,
@@ -4699,12 +4986,15 @@
 static struct omap_hwmod omap44xx_uart2_hwmod = {
 	.name		= "uart2",
 	.class		= &omap44xx_uart_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_uart2_irqs,
 	.sdma_reqs	= omap44xx_uart2_sdma_reqs,
 	.main_clk	= "uart2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_UART2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_UART2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_UART2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_uart2_slaves,
@@ -4751,13 +5041,16 @@
 static struct omap_hwmod omap44xx_uart3_hwmod = {
 	.name		= "uart3",
 	.class		= &omap44xx_uart_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.flags		= HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET,
 	.mpu_irqs	= omap44xx_uart3_irqs,
 	.sdma_reqs	= omap44xx_uart3_sdma_reqs,
 	.main_clk	= "uart3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_UART3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_UART3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_UART3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_uart3_slaves,
@@ -4804,12 +5097,15 @@
 static struct omap_hwmod omap44xx_uart4_hwmod = {
 	.name		= "uart4",
 	.class		= &omap44xx_uart_hwmod_class,
+	.clkdm_name	= "l4_per_clkdm",
 	.mpu_irqs	= omap44xx_uart4_irqs,
 	.sdma_reqs	= omap44xx_uart4_sdma_reqs,
 	.main_clk	= "uart4_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L4PER_UART4_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L4PER_UART4_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L4PER_UART4_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_uart4_slaves,
@@ -4882,12 +5178,15 @@
 static struct omap_hwmod omap44xx_usb_otg_hs_hwmod = {
 	.name		= "usb_otg_hs",
 	.class		= &omap44xx_usb_otg_hs_hwmod_class,
+	.clkdm_name	= "l3_init_clkdm",
 	.flags		= HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
 	.mpu_irqs	= omap44xx_usb_otg_hs_irqs,
 	.main_clk	= "usb_otg_hs_ick",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_L3INIT_USB_OTG_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_L3INIT_USB_OTG_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_L3INIT_USB_OTG_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 	.opt_clks	= usb_otg_hs_opt_clks,
@@ -4955,11 +5254,14 @@
 static struct omap_hwmod omap44xx_wd_timer2_hwmod = {
 	.name		= "wd_timer2",
 	.class		= &omap44xx_wd_timer_hwmod_class,
+	.clkdm_name	= "l4_wkup_clkdm",
 	.mpu_irqs	= omap44xx_wd_timer2_irqs,
 	.main_clk	= "wd_timer2_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM_WKUP_WDT2_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM_WKUP_WDT2_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_WKUP_WDT2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_wd_timer2_slaves,
@@ -5019,11 +5321,14 @@
 static struct omap_hwmod omap44xx_wd_timer3_hwmod = {
 	.name		= "wd_timer3",
 	.class		= &omap44xx_wd_timer_hwmod_class,
+	.clkdm_name	= "abe_clkdm",
 	.mpu_irqs	= omap44xx_wd_timer3_irqs,
 	.main_clk	= "wd_timer3_fck",
 	.prcm = {
 		.omap4 = {
-			.clkctrl_reg = OMAP4430_CM1_ABE_WDT3_CLKCTRL,
+			.clkctrl_offs = OMAP4_CM1_ABE_WDT3_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_ABE_WDT3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
 		},
 	},
 	.slaves		= omap44xx_wd_timer3_slaves,
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 49486f5..3feb359 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -106,8 +106,9 @@
 int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
 {
 	u32 cur_state;
-	int sleep_switch = 0;
+	int sleep_switch = -1;
 	int ret = 0;
+	int hwsup = 0;
 
 	if (pwrdm == NULL || IS_ERR(pwrdm))
 		return -EINVAL;
@@ -127,6 +128,7 @@
 			(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
 			sleep_switch = LOWPOWERSTATE_SWITCH;
 		} else {
+			hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
 			clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
 			pwrdm_wait_transition(pwrdm);
 			sleep_switch = FORCEWAKEUP_SWITCH;
@@ -142,7 +144,7 @@
 
 	switch (sleep_switch) {
 	case FORCEWAKEUP_SWITCH:
-		if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO)
+		if (hwsup)
 			clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
 		else
 			clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
diff --git a/arch/arm/mach-omap2/powerdomains44xx_data.c b/arch/arm/mach-omap2/powerdomains44xx_data.c
index 3a7e678..247e794 100644
--- a/arch/arm/mach-omap2/powerdomains44xx_data.c
+++ b/arch/arm/mach-omap2/powerdomains44xx_data.c
@@ -1,7 +1,7 @@
 /*
  * OMAP4 Power domains framework
  *
- * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ * Copyright (C) 2009-2011 Texas Instruments, Inc.
  * Copyright (C) 2009-2011 Nokia Corporation
  *
  * Abhijit Pagare (abhijitpagare@ti.com)
@@ -41,14 +41,14 @@
 	.banks		  = 5,
 	.pwrsts_mem_ret	= {
 		[0] = PWRSTS_OFF,	/* core_nret_bank */
-		[1] = PWRSTS_OFF_RET,	/* core_ocmram */
+		[1] = PWRSTS_RET,	/* core_ocmram */
 		[2] = PWRSTS_RET,	/* core_other_bank */
 		[3] = PWRSTS_OFF_RET,	/* ducati_l2ram */
 		[4] = PWRSTS_OFF_RET,	/* ducati_unicache */
 	},
 	.pwrsts_mem_on	= {
 		[0] = PWRSTS_ON,	/* core_nret_bank */
-		[1] = PWRSTS_OFF_RET,	/* core_ocmram */
+		[1] = PWRSTS_ON,	/* core_ocmram */
 		[2] = PWRSTS_ON,	/* core_other_bank */
 		[3] = PWRSTS_ON,	/* ducati_l2ram */
 		[4] = PWRSTS_ON,	/* ducati_unicache */
@@ -205,7 +205,7 @@
 	.prcm_offs	  = OMAP4430_PRM_MPU_INST,
 	.prcm_partition	  = OMAP4430_PRM_PARTITION,
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
-	.pwrsts		  = PWRSTS_OFF_RET_ON,
+	.pwrsts		  = PWRSTS_RET_ON,
 	.pwrsts_logic_ret = PWRSTS_OFF_RET,
 	.banks		  = 3,
 	.pwrsts_mem_ret	= {
@@ -318,6 +318,7 @@
 	.prcm_partition	  = OMAP4430_PRM_PARTITION,
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
 	.pwrsts		  = PWRSTS_OFF_ON,
+	.flags		  = PWRDM_HAS_LOWPOWERSTATECHANGE,
 };
 
 /*
diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
index 6be1438..2e40a5c 100644
--- a/arch/arm/mach-omap2/prcm.c
+++ b/arch/arm/mach-omap2/prcm.c
@@ -70,7 +70,7 @@
 		prcm_offs = OMAP3430_GR_MOD;
 		omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
 	} else if (cpu_is_omap44xx()) {
-		omap4_prm_global_warm_sw_reset(); /* never returns */
+		omap4_prminst_global_warm_sw_reset(); /* never returns */
 	} else {
 		WARN_ON(1);
 	}
diff --git a/arch/arm/mach-omap2/prm-regbits-44xx.h b/arch/arm/mach-omap2/prm-regbits-44xx.h
index 6d2776f..3cb247b 100644
--- a/arch/arm/mach-omap2/prm-regbits-44xx.h
+++ b/arch/arm/mach-omap2/prm-regbits-44xx.h
@@ -283,6 +283,14 @@
 #define OMAP4430_DUCATI_UNICACHE_STATEST_SHIFT				10
 #define OMAP4430_DUCATI_UNICACHE_STATEST_MASK				(0x3 << 10)
 
+/* Used by PRM_DEVICE_OFF_CTRL */
+#define OMAP4460_EMIF1_OFFWKUP_DISABLE_SHIFT				8
+#define OMAP4460_EMIF1_OFFWKUP_DISABLE_MASK				(1 << 8)
+
+/* Used by PRM_DEVICE_OFF_CTRL */
+#define OMAP4460_EMIF2_OFFWKUP_DISABLE_SHIFT				9
+#define OMAP4460_EMIF2_OFFWKUP_DISABLE_MASK				(1 << 9)
+
 /* Used by RM_MPU_RSTST */
 #define OMAP4430_EMULATION_RST_SHIFT					0
 #define OMAP4430_EMULATION_RST_MASK					(1 << 0)
diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c
index a2a04bf..0016555 100644
--- a/arch/arm/mach-omap2/prm44xx.c
+++ b/arch/arm/mach-omap2/prm44xx.c
@@ -1,7 +1,7 @@
 /*
  * OMAP4 PRM module functions
  *
- * Copyright (C) 2010 Texas Instruments, Inc.
+ * Copyright (C) 2011 Texas Instruments, Inc.
  * Copyright (C) 2010 Nokia Corporation
  * Benoît Cousson
  * Paul Walmsley
@@ -24,12 +24,6 @@
 #include "prm44xx.h"
 #include "prm-regbits-44xx.h"
 
-/*
- * Address offset (in bytes) between the reset control and the reset
- * status registers: 4 bytes on OMAP4
- */
-#define OMAP4_RST_CTRL_ST_OFFSET		4
-
 /* PRM low-level functions */
 
 /* Read a register in a CM/PRM instance in the PRM module */
@@ -56,140 +50,3 @@
 
 	return v;
 }
-
-/* Read a PRM register, AND it, and shift the result down to bit 0 */
-/* XXX deprecated */
-u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask)
-{
-	u32 v;
-
-	v = __raw_readl(reg);
-	v &= mask;
-	v >>= __ffs(mask);
-
-	return v;
-}
-
-/* Read-modify-write a register in a PRM module. Caller must lock */
-/* XXX deprecated */
-u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg)
-{
-	u32 v;
-
-	v = __raw_readl(reg);
-	v &= ~mask;
-	v |= bits;
-	__raw_writel(v, reg);
-
-	return v;
-}
-
-u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 reg)
-{
-	return omap4_prm_rmw_inst_reg_bits(bits, bits, inst, reg);
-}
-
-u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 reg)
-{
-	return omap4_prm_rmw_inst_reg_bits(bits, 0x0, inst, reg);
-}
-
-/**
- * omap4_prm_is_hardreset_asserted - read the HW reset line state of
- * submodules contained in the hwmod module
- * @rstctrl_reg: RM_RSTCTRL register address for this module
- * @shift: register bit shift corresponding to the reset line to check
- *
- * Returns 1 if the (sub)module hardreset line is currently asserted,
- * 0 if the (sub)module hardreset line is not currently asserted, or
- * -EINVAL upon parameter error.
- */
-int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift)
-{
-	if (!cpu_is_omap44xx() || !rstctrl_reg)
-		return -EINVAL;
-
-	return omap4_prm_read_bits_shift(rstctrl_reg, (1 << shift));
-}
-
-/**
- * omap4_prm_assert_hardreset - assert the HW reset line of a submodule
- * @rstctrl_reg: RM_RSTCTRL register address for this module
- * @shift: register bit shift corresponding to the reset line to assert
- *
- * Some IPs like dsp, ipu or iva contain processors that require an HW
- * reset line to be asserted / deasserted in order to fully enable the
- * IP.  These modules may have multiple hard-reset lines that reset
- * different 'submodules' inside the IP block.  This function will
- * place the submodule into reset.  Returns 0 upon success or -EINVAL
- * upon an argument error.
- */
-int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift)
-{
-	u32 mask;
-
-	if (!cpu_is_omap44xx() || !rstctrl_reg)
-		return -EINVAL;
-
-	mask = 1 << shift;
-	omap4_prm_rmw_reg_bits(mask, mask, rstctrl_reg);
-
-	return 0;
-}
-
-/**
- * omap4_prm_deassert_hardreset - deassert a submodule hardreset line and wait
- * @rstctrl_reg: RM_RSTCTRL register address for this module
- * @shift: register bit shift corresponding to the reset line to deassert
- *
- * Some IPs like dsp, ipu or iva contain processors that require an HW
- * reset line to be asserted / deasserted in order to fully enable the
- * IP.  These modules may have multiple hard-reset lines that reset
- * different 'submodules' inside the IP block.  This function will
- * take the submodule out of reset and wait until the PRCM indicates
- * that the reset has completed before returning.  Returns 0 upon success or
- * -EINVAL upon an argument error, -EEXIST if the submodule was already out
- * of reset, or -EBUSY if the submodule did not exit reset promptly.
- */
-int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift)
-{
-	u32 mask;
-	void __iomem *rstst_reg;
-	int c;
-
-	if (!cpu_is_omap44xx() || !rstctrl_reg)
-		return -EINVAL;
-
-	rstst_reg = rstctrl_reg + OMAP4_RST_CTRL_ST_OFFSET;
-
-	mask = 1 << shift;
-
-	/* Check the current status to avoid de-asserting the line twice */
-	if (omap4_prm_read_bits_shift(rstctrl_reg, mask) == 0)
-		return -EEXIST;
-
-	/* Clear the reset status by writing 1 to the status bit */
-	omap4_prm_rmw_reg_bits(0xffffffff, mask, rstst_reg);
-	/* de-assert the reset control line */
-	omap4_prm_rmw_reg_bits(mask, 0, rstctrl_reg);
-	/* wait the status to be set */
-	omap_test_timeout(omap4_prm_read_bits_shift(rstst_reg, mask),
-			  MAX_MODULE_HARDRESET_WAIT, c);
-
-	return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
-}
-
-void omap4_prm_global_warm_sw_reset(void)
-{
-	u32 v;
-
-	v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
-				    OMAP4_RM_RSTCTRL);
-	v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
-	omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST,
-				 OMAP4_RM_RSTCTRL);
-
-	/* OCP barrier */
-	v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
-				    OMAP4_RM_RSTCTRL);
-}
diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h
index 6e53120..7dfa379 100644
--- a/arch/arm/mach-omap2/prm44xx.h
+++ b/arch/arm/mach-omap2/prm44xx.h
@@ -750,16 +750,6 @@
 extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx);
 extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx);
 extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
-extern u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg);
-extern u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 idx);
-extern u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 idx);
-extern u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask);
-
-extern int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift);
-extern int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift);
-extern int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift);
-
-extern void omap4_prm_global_warm_sw_reset(void);
 
 # endif
 
diff --git a/arch/arm/mach-omap2/prminst44xx.c b/arch/arm/mach-omap2/prminst44xx.c
index a303242..3a7bab1 100644
--- a/arch/arm/mach-omap2/prminst44xx.c
+++ b/arch/arm/mach-omap2/prminst44xx.c
@@ -2,6 +2,7 @@
  * OMAP4 PRM instance functions
  *
  * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments, Inc.
  * Paul Walmsley
  *
  * This program is free software; you can redistribute it and/or modify
@@ -53,7 +54,7 @@
 
 /* Read-modify-write a register in PRM. Caller must lock */
 u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
-				   s16 idx)
+				    u16 idx)
 {
 	u32 v;
 
@@ -64,3 +65,112 @@
 
 	return v;
 }
+
+/*
+ * Address offset (in bytes) between the reset control and the reset
+ * status registers: 4 bytes on OMAP4
+ */
+#define OMAP4_RST_CTRL_ST_OFFSET		4
+
+/**
+ * omap4_prminst_is_hardreset_asserted - read the HW reset line state of
+ * submodules contained in the hwmod module
+ * @rstctrl_reg: RM_RSTCTRL register address for this module
+ * @shift: register bit shift corresponding to the reset line to check
+ *
+ * Returns 1 if the (sub)module hardreset line is currently asserted,
+ * 0 if the (sub)module hardreset line is not currently asserted, or
+ * -EINVAL upon parameter error.
+ */
+int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
+					u16 rstctrl_offs)
+{
+	u32 v;
+
+	v = omap4_prminst_read_inst_reg(part, inst, rstctrl_offs);
+	v &= 1 << shift;
+	v >>= shift;
+
+	return v;
+}
+
+/**
+ * omap4_prminst_assert_hardreset - assert the HW reset line of a submodule
+ * @rstctrl_reg: RM_RSTCTRL register address for this module
+ * @shift: register bit shift corresponding to the reset line to assert
+ *
+ * Some IPs like dsp, ipu or iva contain processors that require an HW
+ * reset line to be asserted / deasserted in order to fully enable the
+ * IP.  These modules may have multiple hard-reset lines that reset
+ * different 'submodules' inside the IP block.  This function will
+ * place the submodule into reset.  Returns 0 upon success or -EINVAL
+ * upon an argument error.
+ */
+int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
+				   u16 rstctrl_offs)
+{
+	u32 mask = 1 << shift;
+
+	omap4_prminst_rmw_inst_reg_bits(mask, mask, part, inst, rstctrl_offs);
+
+	return 0;
+}
+
+/**
+ * omap4_prminst_deassert_hardreset - deassert a submodule hardreset line and
+ * wait
+ * @rstctrl_reg: RM_RSTCTRL register address for this module
+ * @shift: register bit shift corresponding to the reset line to deassert
+ *
+ * Some IPs like dsp, ipu or iva contain processors that require an HW
+ * reset line to be asserted / deasserted in order to fully enable the
+ * IP.  These modules may have multiple hard-reset lines that reset
+ * different 'submodules' inside the IP block.  This function will
+ * take the submodule out of reset and wait until the PRCM indicates
+ * that the reset has completed before returning.  Returns 0 upon success or
+ * -EINVAL upon an argument error, -EEXIST if the submodule was already out
+ * of reset, or -EBUSY if the submodule did not exit reset promptly.
+ */
+int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
+				     u16 rstctrl_offs)
+{
+	int c;
+	u32 mask = 1 << shift;
+	u16 rstst_offs = rstctrl_offs + OMAP4_RST_CTRL_ST_OFFSET;
+
+	/* Check the current status to avoid de-asserting the line twice */
+	if (omap4_prminst_is_hardreset_asserted(shift, part, inst,
+						rstctrl_offs) == 0)
+		return -EEXIST;
+
+	/* Clear the reset status by writing 1 to the status bit */
+	omap4_prminst_rmw_inst_reg_bits(0xffffffff, mask, part, inst,
+					rstst_offs);
+	/* de-assert the reset control line */
+	omap4_prminst_rmw_inst_reg_bits(mask, 0, part, inst, rstctrl_offs);
+	/* wait the status to be set */
+	omap_test_timeout(omap4_prminst_is_hardreset_asserted(shift, part, inst,
+							      rstst_offs),
+			  MAX_MODULE_HARDRESET_WAIT, c);
+
+	return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
+}
+
+
+void omap4_prminst_global_warm_sw_reset(void)
+{
+	u32 v;
+
+	v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
+				    OMAP4430_PRM_DEVICE_INST,
+				    OMAP4_PRM_RSTCTRL_OFFSET);
+	v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
+	omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION,
+				 OMAP4430_PRM_DEVICE_INST,
+				 OMAP4_PRM_RSTCTRL_OFFSET);
+
+	/* OCP barrier */
+	v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
+				    OMAP4430_PRM_DEVICE_INST,
+				    OMAP4_PRM_RSTCTRL_OFFSET);
+}
diff --git a/arch/arm/mach-omap2/prminst44xx.h b/arch/arm/mach-omap2/prminst44xx.h
index 02dd66d..46f2efb 100644
--- a/arch/arm/mach-omap2/prminst44xx.h
+++ b/arch/arm/mach-omap2/prminst44xx.h
@@ -2,6 +2,7 @@
  * OMAP4 Power/Reset Management (PRM) function prototypes
  *
  * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments, Inc.
  * Paul Walmsley
  *
  * This program is free software; you can redistribute it and/or modify
@@ -18,8 +19,15 @@
 extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx);
 extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx);
 extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part,
-					   s16 inst, s16 idx);
+					   s16 inst, u16 idx);
 
-extern void omap4_prm_global_warm_sw_reset(void);
+extern void omap4_prminst_global_warm_sw_reset(void);
+
+extern int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
+					       u16 rstctrl_offs);
+extern int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
+					  u16 rstctrl_offs);
+extern int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
+					    u16 rstctrl_offs);
 
 #endif
diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c
index 3aaa46f..2543342 100644
--- a/arch/arm/mach-omap2/twl-common.c
+++ b/arch/arm/mach-omap2/twl-common.c
@@ -80,11 +80,11 @@
 	.irq_line	= 1,
 };
 
-static struct twl4030_codec_audio_data omap3_audio;
+static struct twl4030_codec_data omap3_codec;
 
-static struct twl4030_codec_data omap3_codec_pdata = {
+static struct twl4030_audio_data omap3_audio_pdata = {
 	.audio_mclk = 26000000,
-	.audio = &omap3_audio,
+	.codec = &omap3_codec,
 };
 
 static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = {
@@ -292,8 +292,8 @@
 	if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
 		pmic_data->madc = &omap3_madc_pdata;
 
-	if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec)
-		pmic_data->codec = &omap3_codec_pdata;
+	if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
+		pmic_data->audio = &omap3_audio_pdata;
 
 	/* Common regulator configurations */
 	if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)
diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h
index f2b2b35..3e5499d 100644
--- a/arch/arm/mach-orion5x/common.h
+++ b/arch/arm/mach-orion5x/common.h
@@ -51,7 +51,7 @@
 void orion5x_pci_set_cardbus_mode(void);
 int orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys);
 struct pci_bus *orion5x_pci_sys_scan_bus(int nr, struct pci_sys_data *sys);
-int orion5x_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin);
+int orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
 
 struct machine_desc;
 struct meminfo;
diff --git a/arch/arm/mach-orion5x/db88f5281-setup.c b/arch/arm/mach-orion5x/db88f5281-setup.c
index f95d3cb..a3e3e9e 100644
--- a/arch/arm/mach-orion5x/db88f5281-setup.c
+++ b/arch/arm/mach-orion5x/db88f5281-setup.c
@@ -237,7 +237,8 @@
 	}
 }
 
-static int __init db88f5281_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init db88f5281_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c
index 855e0e7..a6eddae 100644
--- a/arch/arm/mach-orion5x/dns323-setup.c
+++ b/arch/arm/mach-orion5x/dns323-setup.c
@@ -70,14 +70,14 @@
  * PCI setup
  */
 
-static int __init dns323_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init dns323_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
 	/*
 	 * Check for devices with hard-wired IRQs.
 	 */
-	irq = orion5x_pci_map_irq(dev, slot, pin);
+	irq = orion5x_pci_map_irq(const dev, slot, pin);
 	if (irq != -1)
 		return irq;
 
diff --git a/arch/arm/mach-orion5x/include/mach/hardware.h b/arch/arm/mach-orion5x/include/mach/hardware.h
index e51aaf4..3957354 100644
--- a/arch/arm/mach-orion5x/include/mach/hardware.h
+++ b/arch/arm/mach-orion5x/include/mach/hardware.h
@@ -11,11 +11,4 @@
 
 #include "orion5x.h"
 
-#define pcibios_assign_all_busses()	1
-
-#define PCIBIOS_MIN_IO		0x00001000
-#define PCIBIOS_MIN_MEM		0x01000000
-#define PCIMEM_BASE		ORION5X_PCIE_MEM_PHYS_BASE
-
-
 #endif
diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c
index c0eb646..0038124 100644
--- a/arch/arm/mach-orion5x/kurobox_pro-setup.c
+++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c
@@ -119,7 +119,8 @@
  * PCI
  ****************************************************************************/
 
-static int __init kurobox_pro_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init kurobox_pro_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/mss2-setup.c b/arch/arm/mach-orion5x/mss2-setup.c
index 59263b7..ef3bb8e 100644
--- a/arch/arm/mach-orion5x/mss2-setup.c
+++ b/arch/arm/mach-orion5x/mss2-setup.c
@@ -73,7 +73,7 @@
 /****************************************************************************
  * PCI setup
  ****************************************************************************/
-static int __init mss2_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init mss2_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c
index e8706f1..28b8760 100644
--- a/arch/arm/mach-orion5x/pci.c
+++ b/arch/arm/mach-orion5x/pci.c
@@ -560,6 +560,8 @@
 {
 	int ret = 0;
 
+	vga_base = ORION5X_PCIE_MEM_PHYS_BASE;
+
 	if (nr == 0) {
 		orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr);
 		ret = pcie_setup(sys);
@@ -587,7 +589,7 @@
 	return bus;
 }
 
-int __init orion5x_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int bus = dev->bus->number;
 
diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
index 9eec7c2..291d22b 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
@@ -131,7 +131,7 @@
 }
 
 static int __init
-rd88f5181l_fxo_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+rd88f5181l_fxo_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
index 0cc90bbf..3f02362 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
@@ -140,7 +140,7 @@
 }
 
 static int __init
-rd88f5181l_ge_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+rd88f5181l_ge_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/rd88f5182-setup.c b/arch/arm/mach-orion5x/rd88f5182-setup.c
index 48da39b..27fd38e 100644
--- a/arch/arm/mach-orion5x/rd88f5182-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5182-setup.c
@@ -172,7 +172,8 @@
 	}
 }
 
-static int __init rd88f5182_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init rd88f5182_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/terastation_pro2-setup.c b/arch/arm/mach-orion5x/terastation_pro2-setup.c
index 29ce826..a34e4fa 100644
--- a/arch/arm/mach-orion5x/terastation_pro2-setup.c
+++ b/arch/arm/mach-orion5x/terastation_pro2-setup.c
@@ -100,7 +100,7 @@
 	}
 }
 
-static int __init tsp2_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init tsp2_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/ts209-setup.c b/arch/arm/mach-orion5x/ts209-setup.c
index 47162fd..c983161 100644
--- a/arch/arm/mach-orion5x/ts209-setup.c
+++ b/arch/arm/mach-orion5x/ts209-setup.c
@@ -143,7 +143,8 @@
 	}
 }
 
-static int __init qnap_ts209_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init qnap_ts209_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/ts409-setup.c b/arch/arm/mach-orion5x/ts409-setup.c
index 5aacc7a..cc33b22 100644
--- a/arch/arm/mach-orion5x/ts409-setup.c
+++ b/arch/arm/mach-orion5x/ts409-setup.c
@@ -121,7 +121,8 @@
  * PCI
  ****************************************************************************/
 
-static int __init qnap_ts409_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init qnap_ts409_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/wnr854t-setup.c b/arch/arm/mach-orion5x/wnr854t-setup.c
index 444a1c7..2653595 100644
--- a/arch/arm/mach-orion5x/wnr854t-setup.c
+++ b/arch/arm/mach-orion5x/wnr854t-setup.c
@@ -133,7 +133,8 @@
 	platform_device_register(&wnr854t_nor_flash);
 }
 
-static int __init wnr854t_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init wnr854t_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-orion5x/wrt350n-v2-setup.c b/arch/arm/mach-orion5x/wrt350n-v2-setup.c
index d1952be..251ef15 100644
--- a/arch/arm/mach-orion5x/wrt350n-v2-setup.c
+++ b/arch/arm/mach-orion5x/wrt350n-v2-setup.c
@@ -221,7 +221,8 @@
 	platform_device_register(&wrt350n_v2_button_device);
 }
 
-static int __init wrt350n_v2_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init wrt350n_v2_pci_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	int irq;
 
diff --git a/arch/arm/mach-pnx4008/include/mach/clkdev.h b/arch/arm/mach-pnx4008/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-pnx4008/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile
new file mode 100644
index 0000000..7af7fc0
--- /dev/null
+++ b/arch/arm/mach-prima2/Makefile
@@ -0,0 +1,7 @@
+obj-y := timer.o
+obj-y += irq.o
+obj-y += clock.o
+obj-y += rstc.o
+obj-y += prima2.o
+obj-$(CONFIG_DEBUG_LL) += lluart.o
+obj-$(CONFIG_CACHE_L2X0) += l2x0.o
diff --git a/arch/arm/mach-prima2/Makefile.boot b/arch/arm/mach-prima2/Makefile.boot
new file mode 100644
index 0000000..d023db3
--- /dev/null
+++ b/arch/arm/mach-prima2/Makefile.boot
@@ -0,0 +1,3 @@
+zreladdr-y		:= 0x00008000
+params_phys-y		:= 0x00000100
+initrd_phys-y		:= 0x00800000
diff --git a/arch/arm/mach-prima2/clock.c b/arch/arm/mach-prima2/clock.c
new file mode 100644
index 0000000..f9a2aaf
--- /dev/null
+++ b/arch/arm/mach-prima2/clock.c
@@ -0,0 +1,509 @@
+/*
+ * Clock tree for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/clkdev.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/mach/map.h>
+#include <mach/map.h>
+
+#define SIRFSOC_CLKC_CLK_EN0    0x0000
+#define SIRFSOC_CLKC_CLK_EN1    0x0004
+#define SIRFSOC_CLKC_REF_CFG    0x0014
+#define SIRFSOC_CLKC_CPU_CFG    0x0018
+#define SIRFSOC_CLKC_MEM_CFG    0x001c
+#define SIRFSOC_CLKC_SYS_CFG    0x0020
+#define SIRFSOC_CLKC_IO_CFG     0x0024
+#define SIRFSOC_CLKC_DSP_CFG    0x0028
+#define SIRFSOC_CLKC_GFX_CFG    0x002c
+#define SIRFSOC_CLKC_MM_CFG     0x0030
+#define SIRFSOC_LKC_LCD_CFG     0x0034
+#define SIRFSOC_CLKC_MMC_CFG    0x0038
+#define SIRFSOC_CLKC_PLL1_CFG0  0x0040
+#define SIRFSOC_CLKC_PLL2_CFG0  0x0044
+#define SIRFSOC_CLKC_PLL3_CFG0  0x0048
+#define SIRFSOC_CLKC_PLL1_CFG1  0x004c
+#define SIRFSOC_CLKC_PLL2_CFG1  0x0050
+#define SIRFSOC_CLKC_PLL3_CFG1  0x0054
+#define SIRFSOC_CLKC_PLL1_CFG2  0x0058
+#define SIRFSOC_CLKC_PLL2_CFG2  0x005c
+#define SIRFSOC_CLKC_PLL3_CFG2  0x0060
+
+#define SIRFSOC_CLOCK_VA_BASE		SIRFSOC_VA(0x005000)
+
+#define KHZ     1000
+#define MHZ     (KHZ * KHZ)
+
+struct clk_ops {
+	unsigned long (*get_rate)(struct clk *clk);
+	long (*round_rate)(struct clk *clk, unsigned long rate);
+	int (*set_rate)(struct clk *clk, unsigned long rate);
+	int (*enable)(struct clk *clk);
+	int (*disable)(struct clk *clk);
+	struct clk *(*get_parent)(struct clk *clk);
+	int (*set_parent)(struct clk *clk, struct clk *parent);
+};
+
+struct clk {
+	struct clk *parent;     /* parent clk */
+	unsigned long rate;     /* clock rate in Hz */
+	signed char usage;      /* clock enable count */
+	signed char enable_bit; /* enable bit: 0 ~ 63 */
+	unsigned short regofs;  /* register offset */
+	struct clk_ops *ops;    /* clock operation */
+};
+
+static DEFINE_SPINLOCK(clocks_lock);
+
+static inline unsigned long clkc_readl(unsigned reg)
+{
+	return readl(SIRFSOC_CLOCK_VA_BASE + reg);
+}
+
+static inline void clkc_writel(u32 val, unsigned reg)
+{
+	writel(val, SIRFSOC_CLOCK_VA_BASE + reg);
+}
+
+/*
+ * osc_rtc - real time oscillator - 32.768KHz
+ * osc_sys - high speed oscillator - 26MHz
+ */
+
+static struct clk clk_rtc = {
+	.rate = 32768,
+};
+
+static struct clk clk_osc = {
+	.rate = 26 * MHZ,
+};
+
+/*
+ * std pll
+ */
+static unsigned long std_pll_get_rate(struct clk *clk)
+{
+	unsigned long fin = clk_get_rate(clk->parent);
+	u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
+		SIRFSOC_CLKC_PLL1_CFG0;
+
+	if (clkc_readl(regcfg2) & BIT(2)) {
+		/* pll bypass mode */
+		clk->rate = fin;
+	} else {
+		/* fout = fin * nf / nr / od */
+		u32 cfg0 = clkc_readl(clk->regofs);
+		u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
+		u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
+		u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
+		WARN_ON(fin % MHZ);
+		clk->rate = fin / MHZ * nf / nr / od * MHZ;
+	}
+
+	return clk->rate;
+}
+
+static int std_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long fin, nf, nr, od, reg;
+
+	/*
+	 * fout = fin * nf / (nr * od);
+	 * set od = 1, nr = fin/MHz, so fout = nf * MHz
+	 */
+
+	nf = rate / MHZ;
+	if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1))
+		return -EINVAL;
+
+	fin = clk_get_rate(clk->parent);
+	BUG_ON(fin < MHZ);
+
+	nr = fin / MHZ;
+	BUG_ON((fin % MHZ) || nr > BIT(6));
+
+	od = 1;
+
+	reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19);
+	clkc_writel(reg, clk->regofs);
+
+	reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0;
+	clkc_writel((nf >> 1) - 1, reg);
+
+	reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0;
+	while (!(clkc_readl(reg) & BIT(6)))
+		cpu_relax();
+
+	clk->rate = 0; /* set to zero will force recalculation */
+	return 0;
+}
+
+static struct clk_ops std_pll_ops = {
+	.get_rate = std_pll_get_rate,
+	.set_rate = std_pll_set_rate,
+};
+
+static struct clk clk_pll1 = {
+	.parent = &clk_osc,
+	.regofs = SIRFSOC_CLKC_PLL1_CFG0,
+	.ops = &std_pll_ops,
+};
+
+static struct clk clk_pll2 = {
+	.parent = &clk_osc,
+	.regofs = SIRFSOC_CLKC_PLL2_CFG0,
+	.ops = &std_pll_ops,
+};
+
+static struct clk clk_pll3 = {
+	.parent = &clk_osc,
+	.regofs = SIRFSOC_CLKC_PLL3_CFG0,
+	.ops = &std_pll_ops,
+};
+
+/*
+ * clock domains - cpu, mem, sys/io
+ */
+
+static struct clk clk_mem;
+
+static struct clk *dmn_get_parent(struct clk *clk)
+{
+	struct clk *clks[] = {
+		&clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3
+	};
+	u32 cfg = clkc_readl(clk->regofs);
+	WARN_ON((cfg & (BIT(3) - 1)) > 4);
+	return clks[cfg & (BIT(3) - 1)];
+}
+
+static int dmn_set_parent(struct clk *clk, struct clk *parent)
+{
+	const struct clk *clks[] = {
+		&clk_osc, &clk_rtc, &clk_pll1, &clk_pll2, &clk_pll3
+	};
+	u32 cfg = clkc_readl(clk->regofs);
+	int i;
+	for (i = 0; i < ARRAY_SIZE(clks); i++) {
+		if (clks[i] == parent) {
+			cfg &= ~(BIT(3) - 1);
+			clkc_writel(cfg | i, clk->regofs);
+			/* BIT(3) - switching status: 1 - busy, 0 - done */
+			while (clkc_readl(clk->regofs) & BIT(3))
+				cpu_relax();
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static unsigned long dmn_get_rate(struct clk *clk)
+{
+	unsigned long fin = clk_get_rate(clk->parent);
+	u32 cfg = clkc_readl(clk->regofs);
+	if (cfg & BIT(24)) {
+		/* fcd bypass mode */
+		clk->rate = fin;
+	} else {
+		/*
+		 * wait count: bit[19:16], hold count: bit[23:20]
+		 */
+		u32 wait = (cfg >> 16) & (BIT(4) - 1);
+		u32 hold = (cfg >> 20) & (BIT(4) - 1);
+
+		clk->rate = fin / (wait + hold + 2);
+	}
+
+	return clk->rate;
+}
+
+static int dmn_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long fin;
+	unsigned ratio, wait, hold, reg;
+	unsigned bits = (clk == &clk_mem) ? 3 : 4;
+
+	fin = clk_get_rate(clk->parent);
+	ratio = fin / rate;
+
+	if (unlikely(ratio < 2 || ratio > BIT(bits + 1)))
+		return -EINVAL;
+
+	WARN_ON(fin % rate);
+
+	wait = (ratio >> 1) - 1;
+	hold = ratio - wait - 2;
+
+	reg = clkc_readl(clk->regofs);
+	reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20));
+	reg |= (wait << 16) | (hold << 20) | BIT(25);
+	clkc_writel(reg, clk->regofs);
+
+	/* waiting FCD been effective */
+	while (clkc_readl(clk->regofs) & BIT(25))
+		cpu_relax();
+
+	clk->rate = 0; /* set to zero will force recalculation */
+
+	return 0;
+}
+
+/*
+ * cpu clock has no FCD register in Prima2, can only change pll
+ */
+static int cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret1, ret2;
+	struct clk *cur_parent, *tmp_parent;
+
+	cur_parent = dmn_get_parent(clk);
+	BUG_ON(cur_parent == NULL || cur_parent->usage > 1);
+
+	/* switch to tmp pll before setting parent clock's rate */
+	tmp_parent = cur_parent == &clk_pll1 ? &clk_pll2 : &clk_pll1;
+	ret1 = dmn_set_parent(clk, tmp_parent);
+	BUG_ON(ret1);
+
+	ret2 = clk_set_rate(cur_parent, rate);
+
+	ret1 = dmn_set_parent(clk, cur_parent);
+
+	clk->rate = 0; /* set to zero will force recalculation */
+
+	return ret2 ? ret2 : ret1;
+}
+
+static struct clk_ops cpu_ops = {
+	.get_parent = dmn_get_parent,
+	.set_parent = dmn_set_parent,
+	.set_rate = cpu_set_rate,
+};
+
+static struct clk clk_cpu = {
+	.parent = &clk_pll1,
+	.regofs = SIRFSOC_CLKC_CPU_CFG,
+	.ops = &cpu_ops,
+};
+
+
+static struct clk_ops msi_ops = {
+	.set_rate = dmn_set_rate,
+	.get_rate = dmn_get_rate,
+	.set_parent = dmn_set_parent,
+	.get_parent = dmn_get_parent,
+};
+
+static struct clk clk_mem = {
+	.parent = &clk_pll2,
+	.regofs = SIRFSOC_CLKC_MEM_CFG,
+	.ops = &msi_ops,
+};
+
+static struct clk clk_sys = {
+	.parent = &clk_pll3,
+	.regofs = SIRFSOC_CLKC_SYS_CFG,
+	.ops = &msi_ops,
+};
+
+static struct clk clk_io = {
+	.parent = &clk_pll3,
+	.regofs = SIRFSOC_CLKC_IO_CFG,
+	.ops = &msi_ops,
+};
+
+/*
+ * on-chip clock sets
+ */
+static struct clk_lookup onchip_clks[] = {
+	{
+		.dev_id = "rtc",
+		.clk = &clk_rtc,
+	}, {
+		.dev_id = "osc",
+		.clk = &clk_osc,
+	}, {
+		.dev_id = "pll1",
+		.clk = &clk_pll1,
+	}, {
+		.dev_id = "pll2",
+		.clk = &clk_pll2,
+	}, {
+		.dev_id = "pll3",
+		.clk = &clk_pll3,
+	}, {
+		.dev_id = "cpu",
+		.clk = &clk_cpu,
+	}, {
+		.dev_id = "mem",
+		.clk = &clk_mem,
+	}, {
+		.dev_id = "sys",
+			.clk = &clk_sys,
+	}, {
+		.dev_id = "io",
+			.clk = &clk_io,
+	},
+};
+
+int clk_enable(struct clk *clk)
+{
+	unsigned long flags;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return -EINVAL;
+
+	if (clk->parent)
+		clk_enable(clk->parent);
+
+	spin_lock_irqsave(&clocks_lock, flags);
+	if (!clk->usage++ && clk->ops && clk->ops->enable)
+		clk->ops->enable(clk);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+	unsigned long flags;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return;
+
+	WARN_ON(!clk->usage);
+
+	spin_lock_irqsave(&clocks_lock, flags);
+	if (--clk->usage == 0 && clk->ops && clk->ops->disable)
+		clk->ops->disable(clk);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+
+	if (clk->parent)
+		clk_disable(clk->parent);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0;
+
+	if (clk->rate)
+		return clk->rate;
+
+	if (clk->ops && clk->ops->get_rate)
+		return clk->ops->get_rate(clk);
+
+	return clk_get_rate(clk->parent);
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0;
+
+	if (clk->ops && clk->ops->round_rate)
+		return clk->ops->round_rate(clk, rate);
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return -EINVAL;
+
+	if (!clk->ops || !clk->ops->set_rate)
+		return -EINVAL;
+
+	return clk->ops->set_rate(clk, rate);
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	int ret;
+	unsigned long flags;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return -EINVAL;
+
+	if (!clk->ops || !clk->ops->set_parent)
+		return -EINVAL;
+
+	spin_lock_irqsave(&clocks_lock, flags);
+	ret = clk->ops->set_parent(clk, parent);
+	if (!ret) {
+		parent->usage += clk->usage;
+		clk->parent->usage -= clk->usage;
+		BUG_ON(clk->parent->usage < 0);
+		clk->parent = parent;
+	}
+	spin_unlock_irqrestore(&clocks_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	unsigned long flags;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return NULL;
+
+	if (!clk->ops || !clk->ops->get_parent)
+		return clk->parent;
+
+	spin_lock_irqsave(&clocks_lock, flags);
+	clk->parent = clk->ops->get_parent(clk);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+	return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+static void __init sirfsoc_clk_init(void)
+{
+	clkdev_add_table(onchip_clks, ARRAY_SIZE(onchip_clks));
+}
+
+static struct of_device_id clkc_ids[] = {
+	{ .compatible = "sirf,prima2-clkc" },
+};
+
+void __init sirfsoc_of_clk_init(void)
+{
+	struct device_node *np;
+	struct resource res;
+	struct map_desc sirfsoc_clkc_iodesc = {
+		.virtual = SIRFSOC_CLOCK_VA_BASE,
+		.type    = MT_DEVICE,
+	};
+
+	np = of_find_matching_node(NULL, clkc_ids);
+	if (!np)
+		panic("unable to find compatible clkc node in dtb\n");
+
+	if (of_address_to_resource(np, 0, &res))
+		panic("unable to find clkc range in dtb");
+	of_node_put(np);
+
+	sirfsoc_clkc_iodesc.pfn = __phys_to_pfn(res.start);
+	sirfsoc_clkc_iodesc.length = 1 + res.end - res.start;
+
+	iotable_init(&sirfsoc_clkc_iodesc, 1);
+
+	sirfsoc_clk_init();
+}
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
new file mode 100644
index 0000000..83e5d21
--- /dev/null
+++ b/arch/arm/mach-prima2/common.h
@@ -0,0 +1,26 @@
+/*
+ * This file contains common function prototypes to avoid externs in the c files.
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_PRIMA2_COMMON_H__
+#define __MACH_PRIMA2_COMMON_H__
+
+#include <linux/init.h>
+#include <asm/mach/time.h>
+
+extern struct sys_timer sirfsoc_timer;
+
+extern void __init sirfsoc_of_irq_init(void);
+extern void __init sirfsoc_of_clk_init(void);
+
+#ifndef CONFIG_DEBUG_LL
+static inline void sirfsoc_map_lluart(void)  {}
+#else
+extern void __init sirfsoc_map_lluart(void);
+#endif
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/clkdev.h b/arch/arm/mach-prima2/include/mach/clkdev.h
new file mode 100644
index 0000000..6693251
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/clkdev.h
@@ -0,0 +1,15 @@
+/*
+ * arch/arm/mach-prima2/include/mach/clkdev.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_CLKDEV_H
+#define __MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/debug-macro.S b/arch/arm/mach-prima2/include/mach/debug-macro.S
new file mode 100644
index 0000000..bf75106
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/debug-macro.S
@@ -0,0 +1,29 @@
+/*
+ * arch/arm/mach-prima2/include/mach/debug-macro.S
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <mach/hardware.h>
+#include <mach/uart.h>
+
+	.macro	addruart, rp, rv
+	ldr	\rp, =SIRFSOC_UART1_PA_BASE		@ physical
+	ldr	\rv, =SIRFSOC_UART1_VA_BASE		@ virtual
+	.endm
+
+	.macro	senduart,rd,rx
+	str	\rd, [\rx, #SIRFSOC_UART_TXFIFO_DATA]
+	.endm
+
+	.macro	busyuart,rd,rx
+	.endm
+
+	.macro	waituart,rd,rx
+1001:	ldr	\rd, [\rx, #SIRFSOC_UART_TXFIFO_STATUS]
+	tst	\rd, #SIRFSOC_UART1_TXFIFO_EMPTY
+	beq	1001b
+	.endm
+
diff --git a/arch/arm/mach-prima2/include/mach/entry-macro.S b/arch/arm/mach-prima2/include/mach/entry-macro.S
new file mode 100644
index 0000000..1c8a50f
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/entry-macro.S
@@ -0,0 +1,29 @@
+/*
+ * arch/arm/mach-prima2/include/mach/entry-macro.S
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <mach/hardware.h>
+
+#define SIRFSOC_INT_ID 0x38
+
+	.macro  get_irqnr_preamble, base, tmp
+	ldr     \base, =sirfsoc_intc_base
+	ldr     \base, [\base]
+	.endm
+
+	.macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr \irqnr, [\base, #SIRFSOC_INT_ID]	@ Get the highest priority irq
+	cmp \irqnr, #0x40			@ the irq num can't be larger than 0x3f
+	movges \irqnr, #0
+	.endm
+
+	.macro  disable_fiq
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
diff --git a/arch/arm/mach-prima2/include/mach/hardware.h b/arch/arm/mach-prima2/include/mach/hardware.h
new file mode 100644
index 0000000..105b969
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/hardware.h
@@ -0,0 +1,15 @@
+/*
+ * arch/arm/mach-prima2/include/mach/hardware.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_HARDWARE_H__
+#define __MACH_HARDWARE_H__
+
+#include <asm/sizes.h>
+#include <mach/map.h>
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/io.h b/arch/arm/mach-prima2/include/mach/io.h
new file mode 100644
index 0000000..6c31e9e
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/io.h
@@ -0,0 +1,16 @@
+/*
+ * arch/arm/mach-prima2/include/mach/io.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_PRIMA2_IO_H
+#define __MACH_PRIMA2_IO_H
+
+#define IO_SPACE_LIMIT ((resource_size_t)0)
+
+#define __mem_pci(a)            (a)
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/irqs.h b/arch/arm/mach-prima2/include/mach/irqs.h
new file mode 100644
index 0000000..bb354f9
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/irqs.h
@@ -0,0 +1,17 @@
+/*
+ * arch/arm/mach-prima2/include/mach/irqs.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H
+
+#define SIRFSOC_INTENAL_IRQ_START  0
+#define SIRFSOC_INTENAL_IRQ_END    59
+
+#define NR_IRQS	220
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/map.h b/arch/arm/mach-prima2/include/mach/map.h
new file mode 100644
index 0000000..66b1ae2
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/map.h
@@ -0,0 +1,16 @@
+/*
+ * memory & I/O static mapping definitions for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_PRIMA2_MAP_H__
+#define __MACH_PRIMA2_MAP_H__
+
+#include <mach/vmalloc.h>
+
+#define SIRFSOC_VA(x)			(VMALLOC_END + ((x) & 0x00FFF000))
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/memory.h b/arch/arm/mach-prima2/include/mach/memory.h
new file mode 100644
index 0000000..368cd5a
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/memory.h
@@ -0,0 +1,21 @@
+/*
+ * arch/arm/mach-prima2/include/mach/memory.h
+ *
+ * Copyright (c) 2010 – 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#define PLAT_PHYS_OFFSET        UL(0x00000000)
+
+/*
+ * Restrict DMA-able region to workaround silicon limitation.
+ * The limitation restricts buffers available for DMA to SD/MMC
+ * hardware to be below 256MB
+ */
+#define ARM_DMA_ZONE_SIZE	(SZ_256M)
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/system.h b/arch/arm/mach-prima2/include/mach/system.h
new file mode 100644
index 0000000..0dbd257
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/system.h
@@ -0,0 +1,29 @@
+/*
+ * arch/arm/mach-prima2/include/mach/system.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_SYSTEM_H__
+#define __MACH_SYSTEM_H__
+
+#include <linux/bitops.h>
+#include <mach/hardware.h>
+
+#define SIRFSOC_SYS_RST_BIT  BIT(31)
+
+extern void __iomem *sirfsoc_rstc_base;
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(SIRFSOC_SYS_RST_BIT, sirfsoc_rstc_base);
+}
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/timex.h b/arch/arm/mach-prima2/include/mach/timex.h
new file mode 100644
index 0000000..d6f98a7
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/timex.h
@@ -0,0 +1,14 @@
+/*
+ * arch/arm/mach-prima2/include/mach/timex.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_TIMEX_H__
+#define __MACH_TIMEX_H__
+
+#define CLOCK_TICK_RATE  1000000
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/uart.h b/arch/arm/mach-prima2/include/mach/uart.h
new file mode 100644
index 0000000..c98b4d5
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/uart.h
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-prima2/include/mach/uart.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_PRIMA2_SIRFSOC_UART_H
+#define __MACH_PRIMA2_SIRFSOC_UART_H
+
+/* UART-1: used as serial debug port */
+#define SIRFSOC_UART1_PA_BASE          0xb0060000
+#define SIRFSOC_UART1_VA_BASE          SIRFSOC_VA(0x060000)
+#define SIRFSOC_UART1_SIZE		SZ_4K
+
+#define SIRFSOC_UART_TXFIFO_STATUS	0x0114
+#define SIRFSOC_UART_TXFIFO_DATA	0x0118
+
+#define SIRFSOC_UART1_TXFIFO_FULL                       (1 << 5)
+#define SIRFSOC_UART1_TXFIFO_EMPTY			(1 << 6)
+
+#endif
diff --git a/arch/arm/mach-prima2/include/mach/uncompress.h b/arch/arm/mach-prima2/include/mach/uncompress.h
new file mode 100644
index 0000000..83125c6
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/uncompress.h
@@ -0,0 +1,40 @@
+/*
+ * arch/arm/mach-prima2/include/mach/uncompress.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/uart.h>
+
+void arch_decomp_setup(void)
+{
+}
+
+#define arch_decomp_wdog()
+
+static __inline__ void putc(char c)
+{
+	/*
+	 * during kernel decompression, all mappings are flat:
+	 *  virt_addr == phys_addr
+	 */
+	while (__raw_readl(SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS)
+		& SIRFSOC_UART1_TXFIFO_FULL)
+		barrier();
+
+	__raw_writel(c, SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_DATA);
+}
+
+static inline void flush(void)
+{
+}
+
+#endif
+
diff --git a/arch/arm/mach-prima2/include/mach/vmalloc.h b/arch/arm/mach-prima2/include/mach/vmalloc.h
new file mode 100644
index 0000000..c9f90fe
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/vmalloc.h
@@ -0,0 +1,16 @@
+/*
+ * arch/arm/ach-prima2/include/mach/vmalloc.h
+ *
+ * Copyright (c) 2010 – 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_VMALLOC_H
+#define __MACH_VMALLOC_H
+
+#include <linux/const.h>
+
+#define VMALLOC_END    _AC(0xFEC00000, UL)
+
+#endif
diff --git a/arch/arm/mach-prima2/irq.c b/arch/arm/mach-prima2/irq.c
new file mode 100644
index 0000000..c3404cb
--- /dev/null
+++ b/arch/arm/mach-prima2/irq.c
@@ -0,0 +1,71 @@
+/*
+ * interrupt controller support for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <mach/hardware.h>
+#include <asm/mach/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define SIRFSOC_INT_RISC_MASK0          0x0018
+#define SIRFSOC_INT_RISC_MASK1          0x001C
+#define SIRFSOC_INT_RISC_LEVEL0         0x0020
+#define SIRFSOC_INT_RISC_LEVEL1         0x0024
+
+void __iomem *sirfsoc_intc_base;
+
+static __init void
+sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+
+	gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq);
+	ct = gc->chip_types;
+
+	ct->chip.irq_mask = irq_gc_mask_clr_bit;
+	ct->chip.irq_unmask = irq_gc_mask_set_bit;
+	ct->regs.mask = SIRFSOC_INT_RISC_MASK0;
+
+	irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0);
+}
+
+static __init void sirfsoc_irq_init(void)
+{
+	sirfsoc_alloc_gc(sirfsoc_intc_base, 0, 32);
+	sirfsoc_alloc_gc(sirfsoc_intc_base + 4, 32, SIRFSOC_INTENAL_IRQ_END - 32);
+
+	writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL0);
+	writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL1);
+
+	writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK0);
+	writel_relaxed(0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1);
+}
+
+static struct of_device_id intc_ids[]  = {
+	{ .compatible = "sirf,prima2-intc" },
+};
+
+void __init sirfsoc_of_irq_init(void)
+{
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, intc_ids);
+	if (!np)
+		panic("unable to find compatible intc node in dtb\n");
+
+	sirfsoc_intc_base = of_iomap(np, 0);
+	if (!sirfsoc_intc_base)
+		panic("unable to map intc cpu registers\n");
+
+	of_node_put(np);
+
+	sirfsoc_irq_init();
+}
diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c
new file mode 100644
index 0000000..9cda205
--- /dev/null
+++ b/arch/arm/mach-prima2/l2x0.c
@@ -0,0 +1,59 @@
+/*
+ * l2 cache initialization for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <mach/memory.h>
+
+#define L2X0_ADDR_FILTERING_START       0xC00
+#define L2X0_ADDR_FILTERING_END         0xC04
+
+static struct of_device_id l2x_ids[]  = {
+	{ .compatible = "arm,pl310-cache" },
+};
+
+static int __init sirfsoc_of_l2x_init(void)
+{
+	struct device_node *np;
+	void __iomem *sirfsoc_l2x_base;
+
+	np = of_find_matching_node(NULL, l2x_ids);
+	if (!np)
+		panic("unable to find compatible l2x node in dtb\n");
+
+	sirfsoc_l2x_base = of_iomap(np, 0);
+	if (!sirfsoc_l2x_base)
+		panic("unable to map l2x cpu registers\n");
+
+	of_node_put(np);
+
+	if (!(readl_relaxed(sirfsoc_l2x_base + L2X0_CTRL) & 1)) {
+		/*
+		 * set the physical memory windows L2 cache will cover
+		 */
+		writel_relaxed(PLAT_PHYS_OFFSET + 1024 * 1024 * 1024,
+			sirfsoc_l2x_base + L2X0_ADDR_FILTERING_END);
+		writel_relaxed(PLAT_PHYS_OFFSET | 0x1,
+			sirfsoc_l2x_base + L2X0_ADDR_FILTERING_START);
+
+		writel_relaxed(0,
+			sirfsoc_l2x_base + L2X0_TAG_LATENCY_CTRL);
+		writel_relaxed(0,
+			sirfsoc_l2x_base + L2X0_DATA_LATENCY_CTRL);
+	}
+	l2x0_init((void __iomem *)sirfsoc_l2x_base, 0x00040000,
+		0x00000000);
+
+	return 0;
+}
+early_initcall(sirfsoc_of_l2x_init);
diff --git a/arch/arm/mach-prima2/lluart.c b/arch/arm/mach-prima2/lluart.c
new file mode 100644
index 0000000..a89f9b3
--- /dev/null
+++ b/arch/arm/mach-prima2/lluart.c
@@ -0,0 +1,25 @@
+/*
+ * Static memory mapping for DEBUG_LL
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <asm/page.h>
+#include <asm/mach/map.h>
+#include <mach/map.h>
+#include <mach/uart.h>
+
+void __init sirfsoc_map_lluart(void)
+{
+	struct map_desc sirfsoc_lluart_map = {
+		.virtual        = SIRFSOC_UART1_VA_BASE,
+		.pfn            = __phys_to_pfn(SIRFSOC_UART1_PA_BASE),
+		.length         = SIRFSOC_UART1_SIZE,
+		.type           = MT_DEVICE,
+	};
+
+	iotable_init(&sirfsoc_lluart_map, 1);
+}
diff --git a/arch/arm/mach-prima2/prima2.c b/arch/arm/mach-prima2/prima2.c
new file mode 100644
index 0000000..f57124b
--- /dev/null
+++ b/arch/arm/mach-prima2/prima2.c
@@ -0,0 +1,41 @@
+/*
+ * Defines machines for CSR SiRFprimaII 
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include "common.h"
+
+static struct of_device_id sirfsoc_of_bus_ids[] __initdata = {
+	{ .compatible = "simple-bus", },
+	{},
+};
+
+void __init sirfsoc_mach_init(void)
+{
+	of_platform_bus_probe(NULL, sirfsoc_of_bus_ids, NULL);
+}
+
+static const char *prima2cb_dt_match[] __initdata = {
+       "sirf,prima2-cb",
+       NULL
+};
+
+MACHINE_START(PRIMA2_EVB, "prima2cb")
+	/* Maintainer: Barry Song <baohua.song@csr.com> */
+	.boot_params	= 0x00000100,
+	.init_early     = sirfsoc_of_clk_init,
+	.map_io         = sirfsoc_map_lluart,
+	.init_irq	= sirfsoc_of_irq_init,
+	.timer		= &sirfsoc_timer,
+	.init_machine	= sirfsoc_mach_init,
+	.dt_compat      = prima2cb_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c
new file mode 100644
index 0000000..d074786
--- /dev/null
+++ b/arch/arm/mach-prima2/rstc.c
@@ -0,0 +1,69 @@
+/*
+ * reset controller for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+void __iomem *sirfsoc_rstc_base;
+static DEFINE_MUTEX(rstc_lock);
+
+static struct of_device_id rstc_ids[]  = {
+	{ .compatible = "sirf,prima2-rstc" },
+};
+
+static int __init sirfsoc_of_rstc_init(void)
+{
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, rstc_ids);
+	if (!np)
+		panic("unable to find compatible rstc node in dtb\n");
+
+	sirfsoc_rstc_base = of_iomap(np, 0);
+	if (!sirfsoc_rstc_base)
+		panic("unable to map rstc cpu registers\n");
+
+	of_node_put(np);
+
+	return 0;
+}
+early_initcall(sirfsoc_of_rstc_init);
+
+int sirfsoc_reset_device(struct device *dev)
+{
+	const unsigned int *prop = of_get_property(dev->of_node, "reset-bit", NULL);
+	unsigned int reset_bit;
+
+	if (!prop)
+		return -ENODEV;
+
+	reset_bit = be32_to_cpup(prop);
+
+	mutex_lock(&rstc_lock);
+
+	/*
+	 * Writing 1 to this bit resets corresponding block. Writing 0 to this
+	 * bit de-asserts reset signal of the corresponding block.
+	 * datasheet doesn't require explicit delay between the set and clear
+	 * of reset bit. it could be shorter if tests pass.
+	 */
+	writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | reset_bit,
+		sirfsoc_rstc_base + (reset_bit / 32) * 4);
+	msleep(10);
+	writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
+		sirfsoc_rstc_base + (reset_bit / 32) * 4);
+
+	mutex_unlock(&rstc_lock);
+
+	return 0;
+}
diff --git a/arch/arm/mach-prima2/timer.c b/arch/arm/mach-prima2/timer.c
new file mode 100644
index 0000000..44027f3
--- /dev/null
+++ b/arch/arm/mach-prima2/timer.c
@@ -0,0 +1,217 @@
+/*
+ * System timer for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mach/map.h>
+#include <asm/mach/time.h>
+
+#define SIRFSOC_TIMER_COUNTER_LO	0x0000
+#define SIRFSOC_TIMER_COUNTER_HI	0x0004
+#define SIRFSOC_TIMER_MATCH_0		0x0008
+#define SIRFSOC_TIMER_MATCH_1		0x000C
+#define SIRFSOC_TIMER_MATCH_2		0x0010
+#define SIRFSOC_TIMER_MATCH_3		0x0014
+#define SIRFSOC_TIMER_MATCH_4		0x0018
+#define SIRFSOC_TIMER_MATCH_5		0x001C
+#define SIRFSOC_TIMER_STATUS		0x0020
+#define SIRFSOC_TIMER_INT_EN		0x0024
+#define SIRFSOC_TIMER_WATCHDOG_EN	0x0028
+#define SIRFSOC_TIMER_DIV		0x002C
+#define SIRFSOC_TIMER_LATCH		0x0030
+#define SIRFSOC_TIMER_LATCHED_LO	0x0034
+#define SIRFSOC_TIMER_LATCHED_HI	0x0038
+
+#define SIRFSOC_TIMER_WDT_INDEX		5
+
+#define SIRFSOC_TIMER_LATCH_BIT	 BIT(0)
+
+static void __iomem *sirfsoc_timer_base;
+static void __init sirfsoc_of_timer_map(void);
+
+/* timer0 interrupt handler */
+static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *ce = dev_id;
+
+	WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
+
+	/* clear timer0 interrupt */
+	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
+
+	ce->event_handler(ce);
+
+	return IRQ_HANDLED;
+}
+
+/* read 64-bit timer counter */
+static cycle_t sirfsoc_timer_read(struct clocksource *cs)
+{
+	u64 cycles;
+
+	/* latch the 64-bit timer counter */
+	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
+	cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
+
+	return cycles;
+}
+
+static int sirfsoc_timer_set_next_event(unsigned long delta,
+	struct clock_event_device *ce)
+{
+	unsigned long now, next;
+
+	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
+	next = now + delta;
+	writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
+	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
+
+	return next - now > delta ? -ETIME : 0;
+}
+
+static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
+	struct clock_event_device *ce)
+{
+	u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		WARN_ON(1);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+		writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+		break;
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+		break;
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static struct clock_event_device sirfsoc_clockevent = {
+	.name = "sirfsoc_clockevent",
+	.rating = 200,
+	.features = CLOCK_EVT_FEAT_ONESHOT,
+	.set_mode = sirfsoc_timer_set_mode,
+	.set_next_event = sirfsoc_timer_set_next_event,
+};
+
+static struct clocksource sirfsoc_clocksource = {
+	.name = "sirfsoc_clocksource",
+	.rating = 200,
+	.mask = CLOCKSOURCE_MASK(64),
+	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+	.read = sirfsoc_timer_read,
+};
+
+static struct irqaction sirfsoc_timer_irq = {
+	.name = "sirfsoc_timer0",
+	.flags = IRQF_TIMER,
+	.irq = 0,
+	.handler = sirfsoc_timer_interrupt,
+	.dev_id = &sirfsoc_clockevent,
+};
+
+/* Overwrite weak default sched_clock with more precise one */
+unsigned long long notrace sched_clock(void)
+{
+	static int is_mapped = 0;
+
+	/*
+	 * sched_clock is called earlier than .init of sys_timer
+	 * if we map timer memory in .init of sys_timer, system
+	 * will panic due to illegal memory access
+	 */
+	if(!is_mapped) {
+		sirfsoc_of_timer_map();
+		is_mapped = 1;
+	}
+
+	return sirfsoc_timer_read(NULL) * (NSEC_PER_SEC / CLOCK_TICK_RATE);
+}
+
+static void __init sirfsoc_clockevent_init(void)
+{
+	clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
+
+	sirfsoc_clockevent.max_delta_ns =
+		clockevent_delta2ns(-2, &sirfsoc_clockevent);
+	sirfsoc_clockevent.min_delta_ns =
+		clockevent_delta2ns(2, &sirfsoc_clockevent);
+
+	sirfsoc_clockevent.cpumask = cpumask_of(0);
+	clockevents_register_device(&sirfsoc_clockevent);
+}
+
+/* initialize the kernel jiffy timer source */
+static void __init sirfsoc_timer_init(void)
+{
+	unsigned long rate;
+
+	/* timer's input clock is io clock */
+	struct clk *clk = clk_get_sys("io", NULL);
+
+	BUG_ON(IS_ERR(clk));
+
+	rate = clk_get_rate(clk);
+
+	BUG_ON(rate < CLOCK_TICK_RATE);
+	BUG_ON(rate % CLOCK_TICK_RATE);
+
+	writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
+	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
+	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
+
+	BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+
+	BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
+
+	sirfsoc_clockevent_init();
+}
+
+static struct of_device_id timer_ids[] = {
+	{ .compatible = "sirf,prima2-tick" },
+};
+
+static void __init sirfsoc_of_timer_map(void)
+{
+	struct device_node *np;
+	const unsigned int *intspec;
+
+	np = of_find_matching_node(NULL, timer_ids);
+	if (!np)
+		panic("unable to find compatible timer node in dtb\n");
+	sirfsoc_timer_base = of_iomap(np, 0);
+	if (!sirfsoc_timer_base)
+		panic("unable to map timer cpu registers\n");
+
+	/* Get the interrupts property */
+	intspec = of_get_property(np, "interrupts", NULL);
+	BUG_ON(!intspec);
+	sirfsoc_timer_irq.irq = be32_to_cpup(intspec);
+
+	of_node_put(np);
+}
+
+struct sys_timer sirfsoc_timer = {
+	.init = sirfsoc_timer_init,
+};
diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c
index 810a982..ef3e8b1 100644
--- a/arch/arm/mach-pxa/balloon3.c
+++ b/arch/arm/mach-pxa/balloon3.c
@@ -825,6 +825,7 @@
 	.map_io		= balloon3_map_io,
 	.nr_irqs	= BALLOON3_NR_IRQS,
 	.init_irq	= balloon3_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= balloon3_init,
 	.boot_params	= PLAT_PHYS_OFFSET + 0x100,
diff --git a/arch/arm/mach-pxa/capc7117.c b/arch/arm/mach-pxa/capc7117.c
index 4284513..648b0ab 100644
--- a/arch/arm/mach-pxa/capc7117.c
+++ b/arch/arm/mach-pxa/capc7117.c
@@ -151,6 +151,7 @@
 	.boot_params = 0xa0000100,
 	.map_io = pxa3xx_map_io,
 	.init_irq = pxa3xx_init_irq,
+	.handle_irq = pxa3xx_handle_irq,
 	.timer = &pxa_timer,
 	.init_machine = capc7117_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c
index d515222..4d46610 100644
--- a/arch/arm/mach-pxa/clock.c
+++ b/arch/arm/mach-pxa/clock.c
@@ -53,6 +53,21 @@
 }
 EXPORT_SYMBOL(clk_get_rate);
 
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	if (clk->ops->setrate) {
+		spin_lock_irqsave(&clocks_lock, flags);
+		ret = clk->ops->setrate(clk, rate);
+		spin_unlock_irqrestore(&clocks_lock, flags);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
 void clk_dummy_enable(struct clk *clk)
 {
 }
diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
index 1f2fb9c..3a258b1 100644
--- a/arch/arm/mach-pxa/clock.h
+++ b/arch/arm/mach-pxa/clock.h
@@ -5,6 +5,7 @@
 	void			(*enable)(struct clk *);
 	void			(*disable)(struct clk *);
 	unsigned long		(*getrate)(struct clk *);
+	int			(*setrate)(struct clk *, unsigned long);
 };
 
 struct clk {
diff --git a/arch/arm/mach-pxa/cm-x2xx-pci.c b/arch/arm/mach-pxa/cm-x2xx-pci.c
index 1afc0fb..6bf479d 100644
--- a/arch/arm/mach-pxa/cm-x2xx-pci.c
+++ b/arch/arm/mach-pxa/cm-x2xx-pci.c
@@ -77,7 +77,7 @@
 #endif
 
 /* PCI IRQ mapping*/
-static int __init cmx2xx_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init cmx2xx_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
@@ -125,6 +125,9 @@
 {
 	pr_info("Initializing CM-X2XX PCI subsystem\n");
 
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 	__raw_writel(0x800, IT8152_PCI_CFG_ADDR);
 	if (__raw_readl(IT8152_PCI_CFG_DATA) == 0x81521283) {
 		pr_info("PCI Bridge found.\n");
diff --git a/arch/arm/mach-pxa/cm-x2xx.c b/arch/arm/mach-pxa/cm-x2xx.c
index bc55d07..13cf518 100644
--- a/arch/arm/mach-pxa/cm-x2xx.c
+++ b/arch/arm/mach-pxa/cm-x2xx.c
@@ -21,7 +21,8 @@
 #include <asm/mach-types.h>
 #include <asm/mach/map.h>
 
-#include <mach/pxa2xx-regs.h>
+#include <mach/pxa25x.h>
+#include <mach/pxa27x.h>
 #include <mach/audio.h>
 #include <mach/pxafb.h>
 #include <mach/smemc.h>
@@ -516,6 +517,8 @@
 	.map_io		= cmx2xx_map_io,
 	.nr_irqs	= CMX2XX_NR_IRQS,
 	.init_irq	= cmx2xx_init_irq,
+	/* NOTE: pxa25x_handle_irq() works on PXA27x w/o camera support */
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= cmx2xx_init,
 #ifdef CONFIG_PCI
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b199596..b6a5134 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -855,6 +855,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= cm_x300_init,
 	.fixup		= cm_x300_fixup,
diff --git a/arch/arm/mach-pxa/colibri-pxa270.c b/arch/arm/mach-pxa/colibri-pxa270.c
index 7545a48..8709209 100644
--- a/arch/arm/mach-pxa/colibri-pxa270.c
+++ b/arch/arm/mach-pxa/colibri-pxa270.c
@@ -310,6 +310,7 @@
 	.init_machine	= colibri_pxa270_init,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
@@ -318,6 +319,7 @@
 	.init_machine	= colibri_pxa270_income_init,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
diff --git a/arch/arm/mach-pxa/colibri-pxa300.c b/arch/arm/mach-pxa/colibri-pxa300.c
index 66dd81c..60a6781 100644
--- a/arch/arm/mach-pxa/colibri-pxa300.c
+++ b/arch/arm/mach-pxa/colibri-pxa300.c
@@ -187,6 +187,7 @@
 	.init_machine	= colibri_pxa300_init,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
diff --git a/arch/arm/mach-pxa/colibri-pxa320.c b/arch/arm/mach-pxa/colibri-pxa320.c
index ff9ff5f..d2c6631 100644
--- a/arch/arm/mach-pxa/colibri-pxa320.c
+++ b/arch/arm/mach-pxa/colibri-pxa320.c
@@ -23,8 +23,7 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/irq.h>
 
-#include <mach/pxa3xx-regs.h>
-#include <mach/mfp-pxa320.h>
+#include <mach/pxa320.h>
 #include <mach/colibri.h>
 #include <mach/pxafb.h>
 #include <mach/ohci.h>
@@ -258,6 +257,7 @@
 	.init_machine	= colibri_pxa320_init,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c
index 3a5507e..185a37c 100644
--- a/arch/arm/mach-pxa/corgi.c
+++ b/arch/arm/mach-pxa/corgi.c
@@ -722,6 +722,7 @@
 	.fixup		= fixup_corgi,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.init_machine	= corgi_init,
 	.timer		= &pxa_timer,
 MACHINE_END
@@ -732,6 +733,7 @@
 	.fixup		= fixup_corgi,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.init_machine	= corgi_init,
 	.timer		= &pxa_timer,
 MACHINE_END
@@ -742,6 +744,7 @@
 	.fixup		= fixup_corgi,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.init_machine	= corgi_init,
 	.timer		= &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/csb726.c b/arch/arm/mach-pxa/csb726.c
index 0481c29..fe812ea 100644
--- a/arch/arm/mach-pxa/csb726.c
+++ b/arch/arm/mach-pxa/csb726.c
@@ -22,10 +22,9 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/csb726.h>
-#include <mach/mfp-pxa27x.h>
+#include <mach/pxa27x.h>
 #include <mach/mmc.h>
 #include <mach/ohci.h>
-#include <mach/pxa2xx-regs.h>
 #include <mach/audio.h>
 #include <mach/smemc.h>
 
@@ -276,6 +275,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io         = pxa27x_map_io,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.init_machine   = csb726_init,
 	.timer          = &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c
index f8a6e9d..2e37ea5 100644
--- a/arch/arm/mach-pxa/em-x270.c
+++ b/arch/arm/mach-pxa/em-x270.c
@@ -1302,6 +1302,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= em_x270_init,
 MACHINE_END
@@ -1310,6 +1311,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= em_x270_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c
index 2e3970f..b4599ec 100644
--- a/arch/arm/mach-pxa/eseries.c
+++ b/arch/arm/mach-pxa/eseries.c
@@ -193,6 +193,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e330_init,
 	.timer		= &pxa_timer,
@@ -242,6 +243,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e350_init,
 	.timer		= &pxa_timer,
@@ -364,6 +366,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e400_init,
 	.timer		= &pxa_timer,
@@ -552,6 +555,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e740_init,
 	.timer		= &pxa_timer,
@@ -743,6 +747,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e750_init,
 	.timer		= &pxa_timer,
@@ -947,6 +952,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= ESERIES_NR_IRQS,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.fixup		= eseries_fixup,
 	.init_machine	= e800_init,
 	.timer		= &pxa_timer,
diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c
index d88aed8..b73eadb 100644
--- a/arch/arm/mach-pxa/ezx.c
+++ b/arch/arm/mach-pxa/ezx.c
@@ -801,6 +801,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = a780_init,
 MACHINE_END
@@ -866,6 +867,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = e680_init,
 MACHINE_END
@@ -931,6 +933,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = a1200_init,
 MACHINE_END
@@ -1121,6 +1124,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = a910_init,
 MACHINE_END
@@ -1186,6 +1190,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = e6_init,
 MACHINE_END
@@ -1225,6 +1230,7 @@
 	.map_io         = pxa27x_map_io,
 	.nr_irqs	= EZX_NR_IRQS,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = e2_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/generic.h b/arch/arm/mach-pxa/generic.h
index e6c9344..92a2e85 100644
--- a/arch/arm/mach-pxa/generic.h
+++ b/arch/arm/mach-pxa/generic.h
@@ -13,21 +13,8 @@
 struct sys_timer;
 
 extern struct sys_timer pxa_timer;
-extern void __init pxa_init_irq(int irq_nr,
-				int (*set_wake)(struct irq_data *,
-						unsigned int));
-extern void __init pxa25x_init_irq(void);
-#ifdef CONFIG_CPU_PXA26x
-extern void __init pxa26x_init_irq(void);
-#endif
-extern void __init pxa27x_init_irq(void);
-extern void __init pxa3xx_init_irq(void);
-extern void __init pxa95x_init_irq(void);
 
 extern void __init pxa_map_io(void);
-extern void __init pxa25x_map_io(void);
-extern void __init pxa27x_map_io(void);
-extern void __init pxa3xx_map_io(void);
 
 extern unsigned int get_clk_frequency_khz(int info);
 
diff --git a/arch/arm/mach-pxa/gumstix.c b/arch/arm/mach-pxa/gumstix.c
index d65e4bd..deaa111 100644
--- a/arch/arm/mach-pxa/gumstix.c
+++ b/arch/arm/mach-pxa/gumstix.c
@@ -236,6 +236,7 @@
 	.boot_params	= 0xa0000100, /* match u-boot bi_boot_params */
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= gumstix_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/h5000.c b/arch/arm/mach-pxa/h5000.c
index 657db46..0a23512 100644
--- a/arch/arm/mach-pxa/h5000.c
+++ b/arch/arm/mach-pxa/h5000.c
@@ -28,6 +28,7 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
+#include <asm/irq.h>
 
 #include <mach/pxa25x.h>
 #include <mach/h5000.h>
@@ -205,6 +206,7 @@
 	.boot_params = 0xa0000100,
 	.map_io = pxa25x_map_io,
 	.init_irq = pxa25x_init_irq,
+	.handle_irq = pxa25x_handle_irq,
 	.timer = &pxa_timer,
 	.init_machine = h5000_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/himalaya.c b/arch/arm/mach-pxa/himalaya.c
index e8603eb..a997d0ab 100644
--- a/arch/arm/mach-pxa/himalaya.c
+++ b/arch/arm/mach-pxa/himalaya.c
@@ -24,8 +24,7 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
-#include <mach/mfp-pxa25x.h>
-#include <mach/hardware.h>
+#include <mach/pxa25x.h>
 
 #include "generic.h"
 
@@ -162,6 +161,7 @@
 	.boot_params = 0xa0000100,
 	.map_io = pxa25x_map_io,
 	.init_irq = pxa25x_init_irq,
+	.handle_irq = pxa25x_handle_irq,
 	.init_machine = himalaya_init,
 	.timer = &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/hx4700.c b/arch/arm/mach-pxa/hx4700.c
index 99960a1..c748a47 100644
--- a/arch/arm/mach-pxa/hx4700.c
+++ b/arch/arm/mach-pxa/hx4700.c
@@ -842,6 +842,7 @@
 	.map_io       = pxa27x_map_io,
 	.nr_irqs      = HX4700_NR_IRQS,
 	.init_irq     = pxa27x_init_irq,
+	.handle_irq     = pxa27x_handle_irq,
 	.init_machine = hx4700_init,
 	.timer        = &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/icontrol.c b/arch/arm/mach-pxa/icontrol.c
index 6cedc81..d427429 100644
--- a/arch/arm/mach-pxa/icontrol.c
+++ b/arch/arm/mach-pxa/icontrol.c
@@ -194,6 +194,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= icontrol_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
index f7fb64f..ddf20e5 100644
--- a/arch/arm/mach-pxa/idp.c
+++ b/arch/arm/mach-pxa/idp.c
@@ -196,6 +196,7 @@
 	/* Maintainer: Vibren Technologies */
 	.map_io		= idp_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= idp_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/include/mach/clkdev.h b/arch/arm/mach-pxa/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-pxa/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-pxa/include/mach/hardware.h b/arch/arm/mach-pxa/include/mach/hardware.h
index 6957ba5..de63ca3 100644
--- a/arch/arm/mach-pxa/include/mach/hardware.h
+++ b/arch/arm/mach-pxa/include/mach/hardware.h
@@ -337,9 +337,6 @@
 #endif
 
 #if defined(CONFIG_MACH_ARMCORE) && defined(CONFIG_PCI)
-#define PCIBIOS_MIN_IO		0
-#define PCIBIOS_MIN_MEM		0
-#define pcibios_assign_all_busses()	1
 #define ARCH_HAS_DMA_SET_COHERENT_MASK
 #endif
 
diff --git a/arch/arm/mach-pxa/include/mach/irqs.h b/arch/arm/mach-pxa/include/mach/irqs.h
index 0384024..7cc5a78 100644
--- a/arch/arm/mach-pxa/include/mach/irqs.h
+++ b/arch/arm/mach-pxa/include/mach/irqs.h
@@ -104,4 +104,16 @@
 
 #define NR_IRQS			(IRQ_BOARD_START)
 
+#ifndef __ASSEMBLY__
+struct irq_data;
+struct pt_regs;
+
+void pxa_mask_irq(struct irq_data *);
+void pxa_unmask_irq(struct irq_data *);
+void icip_handle_irq(struct pt_regs *);
+void ichp_handle_irq(struct pt_regs *);
+
+void pxa_init_irq(int irq_nr, int (*set_wake)(struct irq_data *, unsigned int));
+#endif
+
 #endif /* __ASM_MACH_IRQS_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa25x.h b/arch/arm/mach-pxa/include/mach/pxa25x.h
index 508c3ba..3ac0baa 100644
--- a/arch/arm/mach-pxa/include/mach/pxa25x.h
+++ b/arch/arm/mach-pxa/include/mach/pxa25x.h
@@ -4,5 +4,14 @@
 #include <mach/hardware.h>
 #include <mach/pxa2xx-regs.h>
 #include <mach/mfp-pxa25x.h>
+#include <mach/irqs.h>
+
+extern void __init pxa25x_map_io(void);
+extern void __init pxa25x_init_irq(void);
+#ifdef CONFIG_CPU_PXA26x
+extern void __init pxa26x_init_irq(void);
+#endif
+
+#define pxa25x_handle_irq	icip_handle_irq
 
 #endif /* __MACH_PXA25x_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa27x.h b/arch/arm/mach-pxa/include/mach/pxa27x.h
index 0b70269..b9b1bdc 100644
--- a/arch/arm/mach-pxa/include/mach/pxa27x.h
+++ b/arch/arm/mach-pxa/include/mach/pxa27x.h
@@ -4,6 +4,7 @@
 #include <mach/hardware.h>
 #include <mach/pxa2xx-regs.h>
 #include <mach/mfp-pxa27x.h>
+#include <mach/irqs.h>
 
 #define ARB_CNTRL	__REG(0x48000048)  /* Arbiter Control Register */
 
@@ -17,6 +18,10 @@
 #define ARB_CORE_PARK		(1<<24)	   /* Be parked with core when idle */
 #define ARB_LOCK_FLAG		(1<<23)	   /* Only Locking masters gain access to the bus */
 
+extern void __init pxa27x_map_io(void);
+extern void __init pxa27x_init_irq(void);
 extern int __init pxa27x_set_pwrmode(unsigned int mode);
 
+#define pxa27x_handle_irq	ichp_handle_irq
+
 #endif /* __MACH_PXA27x_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa300.h b/arch/arm/mach-pxa/include/mach/pxa300.h
index 2f33076..733b641 100644
--- a/arch/arm/mach-pxa/include/mach/pxa300.h
+++ b/arch/arm/mach-pxa/include/mach/pxa300.h
@@ -1,8 +1,7 @@
 #ifndef __MACH_PXA300_H
 #define __MACH_PXA300_H
 
-#include <mach/hardware.h>
-#include <mach/pxa3xx-regs.h>
+#include <mach/pxa3xx.h>
 #include <mach/mfp-pxa300.h>
 
 #endif /* __MACH_PXA300_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa320.h b/arch/arm/mach-pxa/include/mach/pxa320.h
index cab78e9..b6204e4 100644
--- a/arch/arm/mach-pxa/include/mach/pxa320.h
+++ b/arch/arm/mach-pxa/include/mach/pxa320.h
@@ -1,8 +1,7 @@
 #ifndef __MACH_PXA320_H
 #define __MACH_PXA320_H
 
-#include <mach/hardware.h>
-#include <mach/pxa3xx-regs.h>
+#include <mach/pxa3xx.h>
 #include <mach/mfp-pxa320.h>
 
 #endif /* __MACH_PXA320_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa3xx.h b/arch/arm/mach-pxa/include/mach/pxa3xx.h
new file mode 100644
index 0000000..cd3e57f
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/pxa3xx.h
@@ -0,0 +1,14 @@
+#ifndef __MACH_PXA3XX_H	
+#define __MACH_PXA3XX_H
+
+#include <mach/hardware.h>
+#include <mach/pxa3xx-regs.h>
+#include <mach/irqs.h>
+
+extern void __init pxa3xx_map_io(void);
+extern void __init pxa3xx_init_irq(void);
+extern void __init pxa95x_init_irq(void);
+
+#define pxa3xx_handle_irq	ichp_handle_irq
+
+#endif /* __MACH_PXA3XX_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa930.h b/arch/arm/mach-pxa/include/mach/pxa930.h
index d45f76a..190363b 100644
--- a/arch/arm/mach-pxa/include/mach/pxa930.h
+++ b/arch/arm/mach-pxa/include/mach/pxa930.h
@@ -1,8 +1,7 @@
 #ifndef __MACH_PXA930_H
 #define __MACH_PXA930_H
 
-#include <mach/hardware.h>
-#include <mach/pxa3xx-regs.h>
+#include <mach/pxa3xx.h>
 #include <mach/mfp-pxa930.h>
 
 #endif /* __MACH_PXA930_H */
diff --git a/arch/arm/mach-pxa/include/mach/regs-intc.h b/arch/arm/mach-pxa/include/mach/regs-intc.h
deleted file mode 100644
index 662288e..0000000
--- a/arch/arm/mach-pxa/include/mach/regs-intc.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef __ASM_MACH_REGS_INTC_H
-#define __ASM_MACH_REGS_INTC_H
-
-#include <mach/hardware.h>
-
-/*
- * Interrupt Controller
- */
-
-#define ICIP		__REG(0x40D00000)  /* Interrupt Controller IRQ Pending Register */
-#define ICMR		__REG(0x40D00004)  /* Interrupt Controller Mask Register */
-#define ICLR		__REG(0x40D00008)  /* Interrupt Controller Level Register */
-#define ICFP		__REG(0x40D0000C)  /* Interrupt Controller FIQ Pending Register */
-#define ICPR		__REG(0x40D00010)  /* Interrupt Controller Pending Register */
-#define ICCR		__REG(0x40D00014)  /* Interrupt Controller Control Register */
-#define ICHP		__REG(0x40D00018)  /* Interrupt Controller Highest Priority Register */
-
-#define ICIP2		__REG(0x40D0009C)  /* Interrupt Controller IRQ Pending Register 2 */
-#define ICMR2		__REG(0x40D000A0)  /* Interrupt Controller Mask Register 2 */
-#define ICLR2		__REG(0x40D000A4)  /* Interrupt Controller Level Register 2 */
-#define ICFP2		__REG(0x40D000A8)  /* Interrupt Controller FIQ Pending Register 2 */
-#define ICPR2		__REG(0x40D000AC)  /* Interrupt Controller Pending Register 2 */
-
-#define ICIP3		__REG(0x40D00130)  /* Interrupt Controller IRQ Pending Register 3 */
-#define ICMR3		__REG(0x40D00134)  /* Interrupt Controller Mask Register 3 */
-#define ICLR3		__REG(0x40D00138)  /* Interrupt Controller Level Register 3 */
-#define ICFP3		__REG(0x40D0013C)  /* Interrupt Controller FIQ Pending Register 3 */
-#define ICPR3		__REG(0x40D00140)  /* Interrupt Controller Pending Register 3 */
-
-#endif /* __ASM_MACH_REGS_INTC_H */
diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c
index 32ed551..b09e848 100644
--- a/arch/arm/mach-pxa/irq.c
+++ b/arch/arm/mach-pxa/irq.c
@@ -37,6 +37,8 @@
 #define IPR(i)			(((i) < 32) ? (0x01c + ((i) << 2)) :		\
 				((i) < 64) ? (0x0b0 + (((i) - 32) << 2)) :	\
 				      (0x144 + (((i) - 64) << 2)))
+#define ICHP_VAL_IRQ		(1 << 31)
+#define ICHP_IRQ(i)		(((i) >> 16) & 0x7fff)
 #define IPR_VALID		(1 << 31)
 #define IRQ_BIT(n)		(((n) - PXA_IRQ(0)) & 0x1f)
 
@@ -64,7 +66,7 @@
 	return (void __iomem *)io_p2v(phys_base[i]);
 }
 
-static void pxa_mask_irq(struct irq_data *d)
+void pxa_mask_irq(struct irq_data *d)
 {
 	void __iomem *base = irq_data_get_irq_chip_data(d);
 	uint32_t icmr = __raw_readl(base + ICMR);
@@ -73,7 +75,7 @@
 	__raw_writel(icmr, base + ICMR);
 }
 
-static void pxa_unmask_irq(struct irq_data *d)
+void pxa_unmask_irq(struct irq_data *d)
 {
 	void __iomem *base = irq_data_get_irq_chip_data(d);
 	uint32_t icmr = __raw_readl(base + ICMR);
@@ -127,6 +129,36 @@
 	.irq_set_type	= pxa_set_low_gpio_type,
 };
 
+asmlinkage void __exception_irq_entry icip_handle_irq(struct pt_regs *regs)
+{
+	uint32_t icip, icmr, mask;
+
+	do {
+		icip = __raw_readl(IRQ_BASE + ICIP);
+		icmr = __raw_readl(IRQ_BASE + ICMR);
+		mask = icip & icmr;
+
+		if (mask == 0)
+			break;
+
+		handle_IRQ(PXA_IRQ(fls(mask) - 1), regs);
+	} while (1);
+}
+
+asmlinkage void __exception_irq_entry ichp_handle_irq(struct pt_regs *regs)
+{
+	uint32_t ichp;
+
+	do {
+		__asm__ __volatile__("mrc p6, 0, %0, c5, c0, 0\n": "=r"(ichp));
+
+		if ((ichp & ICHP_VAL_IRQ) == 0)
+			break;
+
+		handle_IRQ(PXA_IRQ(ICHP_IRQ(ichp)), regs);
+	} while (1);
+}
+
 static void __init pxa_init_low_gpio_irq(set_wake_t fn)
 {
 	int irq;
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..8f97e15 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -441,6 +441,7 @@
 	.map_io		= pxa3xx_map_io,
 	.nr_irqs	= LITTLETON_NR_IRQS,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= littleton_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/lpd270.c b/arch/arm/mach-pxa/lpd270.c
index 6cf8180..c171d6e 100644
--- a/arch/arm/mach-pxa/lpd270.c
+++ b/arch/arm/mach-pxa/lpd270.c
@@ -503,6 +503,7 @@
 	.map_io		= lpd270_map_io,
 	.nr_irqs	= LPD270_NR_IRQS,
 	.init_irq	= lpd270_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= lpd270_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c
index e10ddb8..a8c696b 100644
--- a/arch/arm/mach-pxa/lubbock.c
+++ b/arch/arm/mach-pxa/lubbock.c
@@ -553,6 +553,7 @@
 	.map_io		= lubbock_map_io,
 	.nr_irqs	= LUBBOCK_NR_IRQS,
 	.init_irq	= lubbock_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= lubbock_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c
index 0e42798..5fe5bcd 100644
--- a/arch/arm/mach-pxa/magician.c
+++ b/arch/arm/mach-pxa/magician.c
@@ -757,6 +757,7 @@
 	.map_io = pxa27x_map_io,
 	.nr_irqs = MAGICIAN_NR_IRQS,
 	.init_irq = pxa27x_init_irq,
+	.handle_irq = pxa27x_handle_irq,
 	.init_machine = magician_init,
 	.timer = &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c
index 3479e2b..4622eb7 100644
--- a/arch/arm/mach-pxa/mainstone.c
+++ b/arch/arm/mach-pxa/mainstone.c
@@ -620,6 +620,7 @@
 	.map_io		= mainstone_map_io,
 	.nr_irqs	= MAINSTONE_NR_IRQS,
 	.init_irq	= mainstone_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= mainstone_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c
index aa67637..64810f9 100644
--- a/arch/arm/mach-pxa/mioa701.c
+++ b/arch/arm/mach-pxa/mioa701.c
@@ -754,6 +754,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= &pxa27x_map_io,
 	.init_irq	= &pxa27x_init_irq,
+	.handle_irq	= &pxa27x_handle_irq,
 	.init_machine	= mioa701_machine_init,
 	.timer		= &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/mp900.c b/arch/arm/mach-pxa/mp900.c
index 59cce78..fb408861 100644
--- a/arch/arm/mach-pxa/mp900.c
+++ b/arch/arm/mach-pxa/mp900.c
@@ -96,6 +96,7 @@
 	.timer		= &pxa_timer,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.init_machine	= mp900c_init,
 MACHINE_END
 
diff --git a/arch/arm/mach-pxa/palmld.c b/arch/arm/mach-pxa/palmld.c
index 4061ecd..6b77365 100644
--- a/arch/arm/mach-pxa/palmld.c
+++ b/arch/arm/mach-pxa/palmld.c
@@ -345,6 +345,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= palmld_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmld_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmt5.c b/arch/arm/mach-pxa/palmt5.c
index df4d7d0..9bd3e47 100644
--- a/arch/arm/mach-pxa/palmt5.c
+++ b/arch/arm/mach-pxa/palmt5.c
@@ -206,6 +206,7 @@
 	.map_io		= pxa27x_map_io,
 	.reserve	= palmt5_reserve,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmt5_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmtc.c b/arch/arm/mach-pxa/palmtc.c
index fb06bd0..6ad4a6c 100644
--- a/arch/arm/mach-pxa/palmtc.c
+++ b/arch/arm/mach-pxa/palmtc.c
@@ -31,14 +31,13 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
+#include <mach/pxa25x.h>
 #include <mach/audio.h>
 #include <mach/palmtc.h>
 #include <mach/mmc.h>
 #include <mach/pxafb.h>
-#include <mach/mfp-pxa25x.h>
 #include <mach/irda.h>
 #include <mach/udc.h>
-#include <mach/pxa2xx-regs.h>
 
 #include "generic.h"
 #include "devices.h"
@@ -541,6 +540,7 @@
 	.boot_params 	= 0xa0000100,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmtc_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmte2.c b/arch/arm/mach-pxa/palmte2.c
index 726f5b9..664232f 100644
--- a/arch/arm/mach-pxa/palmte2.c
+++ b/arch/arm/mach-pxa/palmte2.c
@@ -31,11 +31,11 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
+#include <mach/pxa25x.h>
 #include <mach/audio.h>
 #include <mach/palmte2.h>
 #include <mach/mmc.h>
 #include <mach/pxafb.h>
-#include <mach/mfp-pxa25x.h>
 #include <mach/irda.h>
 #include <mach/udc.h>
 #include <mach/palmasoc.h>
@@ -359,6 +359,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmte2_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmtreo.c b/arch/arm/mach-pxa/palmtreo.c
index 20d1b18..bb27d4b 100644
--- a/arch/arm/mach-pxa/palmtreo.c
+++ b/arch/arm/mach-pxa/palmtreo.c
@@ -444,6 +444,7 @@
 	.map_io         = pxa27x_map_io,
 	.reserve	= treo_reserve,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = treo680_init,
 MACHINE_END
@@ -453,6 +454,7 @@
 	.map_io         = pxa27x_map_io,
 	.reserve	= treo_reserve,
 	.init_irq       = pxa27x_init_irq,
+	.handle_irq       = pxa27x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine	= centro_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c
index 595f002..fc42855 100644
--- a/arch/arm/mach-pxa/palmtx.c
+++ b/arch/arm/mach-pxa/palmtx.c
@@ -367,6 +367,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= palmtx_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmtx_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c
index 5a5329b..e61c1cc 100644
--- a/arch/arm/mach-pxa/palmz72.c
+++ b/arch/arm/mach-pxa/palmz72.c
@@ -402,6 +402,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= palmz72_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/pcm027.c b/arch/arm/mach-pxa/pcm027.c
index 1fc8a66..ffa65df 100644
--- a/arch/arm/mach-pxa/pcm027.c
+++ b/arch/arm/mach-pxa/pcm027.c
@@ -262,6 +262,7 @@
 	.map_io		= pcm027_map_io,
 	.nr_irqs	= PCM027_NR_IRQS,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= pcm027_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c
index 16d14fd..a113ea9 100644
--- a/arch/arm/mach-pxa/poodle.c
+++ b/arch/arm/mach-pxa/poodle.c
@@ -468,6 +468,7 @@
 	.map_io		= pxa25x_map_io,
 	.nr_irqs	= POODLE_NR_IRQS,	/* 4 for LoCoMo */
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= poodle_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index ef1c56a..b5cd9e5 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -32,7 +32,6 @@
 #include <mach/ohci.h>
 #include <mach/pm.h>
 #include <mach/dma.h>
-#include <mach/regs-intc.h>
 #include <mach/smemc.h>
 
 #include "generic.h"
@@ -338,13 +337,13 @@
 
 static void pxa_mask_ext_wakeup(struct irq_data *d)
 {
-	ICMR2 &= ~(1 << ((d->irq - PXA_IRQ(0)) & 0x1f));
+	pxa_mask_irq(d);
 	PECR &= ~PECR_IE(d->irq - IRQ_WAKEUP0);
 }
 
 static void pxa_unmask_ext_wakeup(struct irq_data *d)
 {
-	ICMR2 |= 1 << ((d->irq - PXA_IRQ(0)) & 0x1f);
+	pxa_unmask_irq(d);
 	PECR |= PECR_IE(d->irq - IRQ_WAKEUP0);
 }
 
diff --git a/arch/arm/mach-pxa/pxa95x.c b/arch/arm/mach-pxa/pxa95x.c
index ecc82a3..0ee166b 100644
--- a/arch/arm/mach-pxa/pxa95x.c
+++ b/arch/arm/mach-pxa/pxa95x.c
@@ -27,7 +27,6 @@
 #include <mach/reset.h>
 #include <mach/pm.h>
 #include <mach/dma.h>
-#include <mach/regs-intc.h>
 
 #include "generic.h"
 #include "devices.h"
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index 2f37d43..bbcd905 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -46,10 +46,7 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
-#include <mach/hardware.h>
-#include <mach/pxa3xx-regs.h>
-#include <mach/mfp-pxa3xx.h>
-#include <mach/mfp-pxa300.h>
+#include <mach/pxa300.h>
 #include <mach/ohci.h>
 #include <mach/pxafb.h>
 #include <mach/mmc.h>
@@ -1093,6 +1090,7 @@
 	.init_machine	= raumfeld_controller_init,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 #endif
@@ -1103,6 +1101,7 @@
 	.init_machine	= raumfeld_connector_init,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 #endif
@@ -1113,6 +1112,7 @@
 	.init_machine	= raumfeld_speaker_init,
 	.map_io		= pxa3xx_map_io,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 #endif
diff --git a/arch/arm/mach-pxa/saar.c b/arch/arm/mach-pxa/saar.c
index fee97a9..df4356e 100644
--- a/arch/arm/mach-pxa/saar.c
+++ b/arch/arm/mach-pxa/saar.c
@@ -599,6 +599,7 @@
 	.boot_params    = 0xa0000100,
 	.map_io         = pxa3xx_map_io,
 	.init_irq       = pxa3xx_init_irq,
+	.handle_irq       = pxa3xx_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = saar_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/saarb.c b/arch/arm/mach-pxa/saarb.c
index e53a333..ebd6379 100644
--- a/arch/arm/mach-pxa/saarb.c
+++ b/arch/arm/mach-pxa/saarb.c
@@ -107,6 +107,7 @@
 	.map_io         = pxa3xx_map_io,
 	.nr_irqs	= SAARB_NR_IRQS,
 	.init_irq       = pxa95x_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = saarb_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c
index 01c5769..438c7b5 100644
--- a/arch/arm/mach-pxa/spitz.c
+++ b/arch/arm/mach-pxa/spitz.c
@@ -984,6 +984,7 @@
 	.fixup		= spitz_fixup,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.init_machine	= spitz_init,
 	.timer		= &pxa_timer,
 MACHINE_END
@@ -994,6 +995,7 @@
 	.fixup		= spitz_fixup,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.init_machine	= spitz_init,
 	.timer		= &pxa_timer,
 MACHINE_END
@@ -1004,6 +1006,7 @@
 	.fixup		= spitz_fixup,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.init_machine	= spitz_init,
 	.timer		= &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c
index cb5611d..3f8d0af 100644
--- a/arch/arm/mach-pxa/stargate2.c
+++ b/arch/arm/mach-pxa/stargate2.c
@@ -1001,6 +1001,7 @@
 MACHINE_START(INTELMOTE2, "IMOTE 2")
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= imote2_init,
 	.boot_params	= 0xA0000100,
@@ -1012,6 +1013,7 @@
 	.map_io = pxa27x_map_io,
 	.nr_irqs = STARGATE_NR_IRQS,
 	.init_irq = pxa27x_init_irq,
+	.handle_irq = pxa27x_handle_irq,
 	.timer = &pxa_timer,
 	.init_machine = stargate2_init,
 	.boot_params = 0xA0000100,
diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c
index 53d4a47..32fb58e 100644
--- a/arch/arm/mach-pxa/tavorevb.c
+++ b/arch/arm/mach-pxa/tavorevb.c
@@ -492,6 +492,7 @@
 	.boot_params    = 0xa0000100,
 	.map_io         = pxa3xx_map_io,
 	.init_irq       = pxa3xx_init_irq,
+	.handle_irq       = pxa3xx_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = tavorevb_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/tavorevb3.c b/arch/arm/mach-pxa/tavorevb3.c
index 79f4422..fd5a8ea 100644
--- a/arch/arm/mach-pxa/tavorevb3.c
+++ b/arch/arm/mach-pxa/tavorevb3.c
@@ -129,6 +129,7 @@
 	.map_io         = pxa3xx_map_io,
 	.nr_irqs	= TAVOREVB3_NR_IRQS,
 	.init_irq       = pxa3xx_init_irq,
+	.handle_irq       = pxa3xx_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine   = evb3_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
index 5fa1457..9f69a26 100644
--- a/arch/arm/mach-pxa/tosa.c
+++ b/arch/arm/mach-pxa/tosa.c
@@ -974,6 +974,7 @@
 	.map_io         = pxa25x_map_io,
 	.nr_irqs	= TOSA_NR_IRQS,
 	.init_irq       = pxa25x_init_irq,
+	.handle_irq       = pxa25x_handle_irq,
 	.init_machine   = tosa_init,
 	.timer          = &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/trizeps4.c b/arch/arm/mach-pxa/trizeps4.c
index 687417a..c041750 100644
--- a/arch/arm/mach-pxa/trizeps4.c
+++ b/arch/arm/mach-pxa/trizeps4.c
@@ -558,6 +558,7 @@
 	.init_machine	= trizeps4_init,
 	.map_io		= trizeps4_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
@@ -567,5 +568,6 @@
 	.init_machine	= trizeps4_init,
 	.map_io		= trizeps4_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/viper.c b/arch/arm/mach-pxa/viper.c
index 903218e..d4a3dc7 100644
--- a/arch/arm/mach-pxa/viper.c
+++ b/arch/arm/mach-pxa/viper.c
@@ -995,6 +995,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= viper_map_io,
 	.init_irq	= viper_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer          = &pxa_timer,
 	.init_machine	= viper_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/vpac270.c b/arch/arm/mach-pxa/vpac270.c
index 67bd414..5f8490a 100644
--- a/arch/arm/mach-pxa/vpac270.c
+++ b/arch/arm/mach-pxa/vpac270.c
@@ -719,6 +719,7 @@
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= vpac270_init
 MACHINE_END
diff --git a/arch/arm/mach-pxa/xcep.c b/arch/arm/mach-pxa/xcep.c
index f55f8f2..acc600f 100644
--- a/arch/arm/mach-pxa/xcep.c
+++ b/arch/arm/mach-pxa/xcep.c
@@ -28,8 +28,7 @@
 #include <asm/mach/map.h>
 
 #include <mach/hardware.h>
-#include <mach/pxa2xx-regs.h>
-#include <mach/mfp-pxa25x.h>
+#include <mach/pxa25x.h>
 #include <mach/smemc.h>
 
 #include "generic.h"
@@ -185,6 +184,7 @@
 	.init_machine	= xcep_init,
 	.map_io		= pxa25x_map_io,
 	.init_irq	= pxa25x_init_irq,
+	.handle_irq	= pxa25x_handle_irq,
 	.timer		= &pxa_timer,
 MACHINE_END
 
diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c
index fbe9e02..6c9275a 100644
--- a/arch/arm/mach-pxa/z2.c
+++ b/arch/arm/mach-pxa/z2.c
@@ -40,6 +40,7 @@
 #include <mach/pxafb.h>
 #include <mach/mmc.h>
 #include <plat/pxa27x_keypad.h>
+#include <mach/pm.h>
 
 #include "generic.h"
 #include "devices.h"
@@ -677,6 +678,20 @@
 static inline void z2_pmic_init(void) {}
 #endif
 
+#ifdef CONFIG_PM
+static void z2_power_off(void)
+{
+	/* We're using deep sleep as poweroff, so clear PSPR to ensure that
+	 * bootloader will jump to its entry point in resume handler
+	 */
+	PSPR = 0x0;
+	local_irq_disable();
+	pxa27x_cpu_suspend(PWRMODE_DEEPSLEEP, PLAT_PHYS_OFFSET - PAGE_OFFSET);
+}
+#else
+#define z2_power_off   NULL
+#endif
+
 /******************************************************************************
  * Machine init
  ******************************************************************************/
@@ -698,12 +713,15 @@
 	z2_leds_init();
 	z2_keys_init();
 	z2_pmic_init();
+
+	pm_power_off = z2_power_off;
 }
 
 MACHINE_START(ZIPIT2, "Zipit Z2")
 	.boot_params	= 0xa0000100,
 	.map_io		= pxa27x_map_io,
 	.init_irq	= pxa27x_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= z2_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c
index 9b99cc1..99c49bc 100644
--- a/arch/arm/mach-pxa/zeus.c
+++ b/arch/arm/mach-pxa/zeus.c
@@ -35,14 +35,13 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
-#include <mach/pxa2xx-regs.h>
+#include <mach/pxa27x.h>
 #include <mach/regs-uart.h>
 #include <mach/ohci.h>
 #include <mach/mmc.h>
 #include <mach/pxa27x-udc.h>
 #include <mach/udc.h>
 #include <mach/pxafb.h>
-#include <mach/mfp-pxa27x.h>
 #include <mach/pm.h>
 #include <mach/audio.h>
 #include <mach/arcom-pcmcia.h>
@@ -909,6 +908,7 @@
 	.map_io		= zeus_map_io,
 	.nr_irqs	= ZEUS_NR_IRQS,
 	.init_irq	= zeus_init_irq,
+	.handle_irq	= pxa27x_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= zeus_init,
 MACHINE_END
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..15ec66b 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -24,7 +24,7 @@
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
-#include <mach/hardware.h>
+#include <mach/pxa3xx.h>
 #include <mach/audio.h>
 #include <mach/pxafb.h>
 #include <mach/zylonite.h>
@@ -426,6 +426,7 @@
 	.map_io		= pxa3xx_map_io,
 	.nr_irqs	= ZYLONITE_NR_IRQS,
 	.init_irq	= pxa3xx_init_irq,
+	.handle_irq	= pxa3xx_handle_irq,
 	.timer		= &pxa_timer,
 	.init_machine	= zylonite_init,
 MACHINE_END
diff --git a/arch/arm/mach-s3c2410/include/mach/pm-core.h b/arch/arm/mach-s3c2410/include/mach/pm-core.h
index 70a83b2..45eea52 100644
--- a/arch/arm/mach-s3c2410/include/mach/pm-core.h
+++ b/arch/arm/mach-s3c2410/include/mach/pm-core.h
@@ -62,3 +62,6 @@
 					   struct pm_uart_save *save)
 {
 }
+
+static inline void s3c_pm_restored_gpios(void) { }
+static inline void s3c_pm_saved_gpios(void) { }
diff --git a/arch/arm/mach-s3c2440/clock.c b/arch/arm/mach-s3c2440/clock.c
index 554e0d3..f9e6bda 100644
--- a/arch/arm/mach-s3c2440/clock.c
+++ b/arch/arm/mach-s3c2440/clock.c
@@ -36,7 +36,7 @@
 #include <linux/io.h>
 
 #include <mach/hardware.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 
 #include <mach/regs-clock.h>
diff --git a/arch/arm/mach-s3c2440/s3c2442.c b/arch/arm/mach-s3c2440/s3c2442.c
index 6224bad..9ad99f8 100644
--- a/arch/arm/mach-s3c2440/s3c2442.c
+++ b/arch/arm/mach-s3c2440/s3c2442.c
@@ -38,7 +38,7 @@
 #include <linux/io.h>
 
 #include <mach/hardware.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 
 #include <mach/regs-clock.h>
diff --git a/arch/arm/mach-s3c2440/s3c244x-clock.c b/arch/arm/mach-s3c2440/s3c244x-clock.c
index f8d9613..7f5ea0a 100644
--- a/arch/arm/mach-s3c2440/s3c244x-clock.c
+++ b/arch/arm/mach-s3c2440/s3c244x-clock.c
@@ -35,7 +35,7 @@
 #include <linux/io.h>
 
 #include <mach/hardware.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 
 #include <mach/regs-clock.h>
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index fdc89fc..f057b6a 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -267,3 +267,26 @@
 	select MACH_SMARTQ
 	help
 	    Machine support for the SmartQ 7
+
+config MACH_WLF_CRAGG_6410
+	bool "Wolfson Cragganmore 6410"
+	select CPU_S3C6410
+	select S3C64XX_SETUP_SDHCI
+	select S3C64XX_SETUP_I2C1
+	select S3C64XX_SETUP_IDE
+	select S3C64XX_SETUP_FB_24BPP
+	select S3C64XX_SETUP_KEYPAD
+	select SAMSUNG_DEV_ADC
+	select SAMSUNG_DEV_KEYPAD
+	select S3C_DEV_USB_HOST
+	select S3C_DEV_USB_HSOTG
+	select S3C_DEV_HSMMC
+	select S3C_DEV_HSMMC1
+	select S3C_DEV_HSMMC2
+	select S3C_DEV_I2C1
+	select S3C_DEV_WDT
+	select S3C_DEV_RTC
+	select S3C64XX_DEV_SPI
+	select S3C24XX_GPIO_EXTRA128
+	help
+	  Machine support for the Wolfson Cragganmore S3C6410 variant.
diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile
index f5a7144..61b4034 100644
--- a/arch/arm/mach-s3c64xx/Makefile
+++ b/arch/arm/mach-s3c64xx/Makefile
@@ -55,6 +55,7 @@
 obj-$(CONFIG_MACH_SMARTQ)	+= mach-smartq.o
 obj-$(CONFIG_MACH_SMARTQ5)	+= mach-smartq5.o
 obj-$(CONFIG_MACH_SMARTQ7)	+= mach-smartq7.o
+obj-$(CONFIG_MACH_WLF_CRAGG_6410) += mach-crag6410.o
 
 # device support
 
diff --git a/arch/arm/mach-s3c64xx/include/mach/irqs.h b/arch/arm/mach-s3c64xx/include/mach/irqs.h
index 8e2df26..c026f67 100644
--- a/arch/arm/mach-s3c64xx/include/mach/irqs.h
+++ b/arch/arm/mach-s3c64xx/include/mach/irqs.h
@@ -198,7 +198,9 @@
  * interrupt controllers). */
 #define IRQ_BOARD_START (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1)
 
-#ifdef CONFIG_SMDK6410_WM1190_EV1
+#ifdef CONFIG_MACH_WLF_CRAGG_6410
+#define IRQ_BOARD_NR 128
+#elif defined(CONFIG_SMDK6410_WM1190_EV1)
 #define IRQ_BOARD_NR 64
 #elif defined(CONFIG_SMDK6410_WM1192_EV1)
 #define IRQ_BOARD_NR 64
@@ -215,6 +217,7 @@
 /* Compatibility */
 
 #define IRQ_ONENAND	IRQ_ONENAND0
+#define IRQ_I2S0	IRQ_S3C6410_IIS
 
 #endif /* __ASM_MACH_S3C64XX_IRQS_H */
 
diff --git a/arch/arm/mach-s3c64xx/include/mach/pm-core.h b/arch/arm/mach-s3c64xx/include/mach/pm-core.h
index 1e9f20f..38659be 100644
--- a/arch/arm/mach-s3c64xx/include/mach/pm-core.h
+++ b/arch/arm/mach-s3c64xx/include/mach/pm-core.h
@@ -53,7 +53,7 @@
  * the IRQ wake controls depending on the CPU we are running on */
 
 #define s3c_irqwake_eintallow	((1 << 28) - 1)
-#define s3c_irqwake_intallow	(0)
+#define s3c_irqwake_intallow	(~0)
 
 static inline void s3c_pm_arch_update_uart(void __iomem *regs,
 					   struct pm_uart_save *save)
@@ -96,3 +96,20 @@
 		save->ucon = new_ucon;
 	}
 }
+
+static inline void s3c_pm_restored_gpios(void)
+{
+	/* ensure sleep mode has been cleared from the system */
+
+	__raw_writel(0, S3C64XX_SLPEN);
+}
+
+static inline void s3c_pm_saved_gpios(void)
+{
+	/* turn on the sleep mode and keep it there, as it seems that during
+	 * suspend the xCON registers get re-set and thus you can end up with
+	 * problems between going to sleep and resuming.
+	 */
+
+	__raw_writel(S3C64XX_SLPEN_USE_xSLP, S3C64XX_SLPEN);
+}
diff --git a/arch/arm/mach-s3c64xx/irq.c b/arch/arm/mach-s3c64xx/irq.c
index 97660c8..75d9a0e 100644
--- a/arch/arm/mach-s3c64xx/irq.c
+++ b/arch/arm/mach-s3c64xx/irq.c
@@ -48,14 +48,22 @@
 	},
 };
 
+/* setup the sources the vic should advertise resume for, even though it
+ * is not doing the wake (set_irq_wake needs to be valid) */
+#define IRQ_VIC0_RESUME (1 << (IRQ_RTC_TIC - IRQ_VIC0_BASE))
+#define IRQ_VIC1_RESUME (1 << (IRQ_RTC_ALARM - IRQ_VIC1_BASE) |	\
+			 1 << (IRQ_PENDN - IRQ_VIC1_BASE) |	\
+			 1 << (IRQ_HSMMC0 - IRQ_VIC1_BASE) |	\
+			 1 << (IRQ_HSMMC1 - IRQ_VIC1_BASE) |	\
+			 1 << (IRQ_HSMMC2 - IRQ_VIC1_BASE))
 
 void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
 {
 	printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);
 
 	/* initialise the pair of VICs */
-	vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0);
-	vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0);
+	vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, IRQ_VIC0_RESUME);
+	vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, IRQ_VIC1_RESUME);
 
 	/* add the timer sub-irqs */
 	s3c_init_vic_timer_irq(5, IRQ_TIMER0);
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c
new file mode 100644
index 0000000..9026249
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/mach-crag6410.c
@@ -0,0 +1,774 @@
+/* linux/arch/arm/mach-s3c64xx/mach-crag6410.c
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *	Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Copyright 2011 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/pwm_backlight.h>
+#include <linux/dm9000.h>
+#include <linux/gpio_keys.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/spi/spi.h>
+
+#include <linux/i2c/pca953x.h>
+
+#include <video/platform_lcd.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/gpio.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <mach/s3c6410.h>
+#include <mach/regs-sys.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-modem.h>
+
+#include <mach/regs-gpio-memport.h>
+
+#include <plat/regs-serial.h>
+#include <plat/regs-fb-v4.h>
+#include <plat/fb.h>
+#include <plat/sdhci.h>
+#include <plat/gpio-cfg.h>
+#include <plat/s3c64xx-spi.h>
+
+#include <plat/keypad.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+#include <plat/adc.h>
+#include <plat/iic.h>
+#include <plat/pm.h>
+
+#include <sound/wm8915.h>
+#include <sound/wm8962.h>
+#include <sound/wm9081.h>
+
+#define BANFF_PMIC_IRQ_BASE		IRQ_BOARD_START
+#define GLENFARCLAS_PMIC_IRQ_BASE	(IRQ_BOARD_START + 64)
+
+#define PCA935X_GPIO_BASE		GPIO_BOARD_START
+#define CODEC_GPIO_BASE		(GPIO_BOARD_START + 8)
+#define GLENFARCLAS_PMIC_GPIO_BASE	(GPIO_BOARD_START + 16)
+
+/* serial port setup */
+
+#define UCON (S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK)
+#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
+#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
+
+static struct s3c2410_uartcfg crag6410_uartcfgs[] __initdata = {
+	[0] = {
+		.hwport		= 0,
+		.flags		= 0,
+		.ucon		= UCON,
+		.ulcon		= ULCON,
+		.ufcon		= UFCON,
+	},
+	[1] = {
+		.hwport		= 1,
+		.flags		= 0,
+		.ucon		= UCON,
+		.ulcon		= ULCON,
+		.ufcon		= UFCON,
+	},
+	[2] = {
+		.hwport		= 2,
+		.flags		= 0,
+		.ucon		= UCON,
+		.ulcon		= ULCON,
+		.ufcon		= UFCON,
+	},
+	[3] = {
+		.hwport		= 3,
+		.flags		= 0,
+		.ucon		= UCON,
+		.ulcon		= ULCON,
+		.ufcon		= UFCON,
+	},
+};
+
+static struct platform_pwm_backlight_data crag6410_backlight_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 1000,
+	.dft_brightness	= 600,
+	.pwm_period_ns	= 100000,	/* about 1kHz */
+};
+
+static struct platform_device crag6410_backlight_device = {
+	.name		= "pwm-backlight",
+	.id		= -1,
+	.dev		= {
+		.parent	= &s3c_device_timer[0].dev,
+		.platform_data = &crag6410_backlight_data,
+	},
+};
+
+static void crag6410_lcd_power_set(struct plat_lcd_data *pd, unsigned int power)
+{
+	pr_debug("%s: setting power %d\n", __func__, power);
+
+	if (power) {
+		gpio_set_value(S3C64XX_GPB(0), 1);
+		msleep(1);
+		s3c_gpio_cfgpin(S3C64XX_GPF(14), S3C_GPIO_SFN(2));
+	} else {
+		gpio_direction_output(S3C64XX_GPF(14), 0);
+		gpio_set_value(S3C64XX_GPB(0), 0);
+	}
+}
+
+static struct platform_device crag6410_lcd_powerdev = {
+	.name			= "platform-lcd",
+	.id			= -1,
+	.dev.parent		= &s3c_device_fb.dev,
+	.dev.platform_data	= &(struct plat_lcd_data) {
+		.set_power	= crag6410_lcd_power_set,
+	},
+};
+
+/* 640x480 URT */
+static struct s3c_fb_pd_win crag6410_fb_win0 = {
+	/* this is to ensure we use win0 */
+	.win_mode	= {
+		.left_margin	= 150,
+		.right_margin	= 80,
+		.upper_margin	= 40,
+		.lower_margin	= 5,
+		.hsync_len	= 40,
+		.vsync_len	= 5,
+		.xres		= 640,
+		.yres		= 480,
+	},
+	.max_bpp	= 32,
+	.default_bpp	= 16,
+	.virtual_y	= 480 * 2,
+	.virtual_x	= 640,
+};
+
+/* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
+static struct s3c_fb_platdata crag6410_lcd_pdata __initdata = {
+	.setup_gpio	= s3c64xx_fb_gpio_setup_24bpp,
+	.win[0]		= &crag6410_fb_win0,
+	.vidcon0	= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
+	.vidcon1	= VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
+};
+
+/* 2x6 keypad */
+
+static uint32_t crag6410_keymap[] __initdata = {
+	/* KEY(row, col, keycode) */
+	KEY(0, 0, KEY_VOLUMEUP),
+	KEY(0, 1, KEY_HOME),
+	KEY(0, 2, KEY_VOLUMEDOWN),
+	KEY(0, 3, KEY_HELP),
+	KEY(0, 4, KEY_MENU),
+	KEY(0, 5, KEY_MEDIA),
+	KEY(1, 0, 232),
+	KEY(1, 1, KEY_DOWN),
+	KEY(1, 2, KEY_LEFT),
+	KEY(1, 3, KEY_UP),
+	KEY(1, 4, KEY_RIGHT),
+	KEY(1, 5, KEY_CAMERA),
+};
+
+static struct matrix_keymap_data crag6410_keymap_data __initdata = {
+	.keymap		= crag6410_keymap,
+	.keymap_size	= ARRAY_SIZE(crag6410_keymap),
+};
+
+static struct samsung_keypad_platdata crag6410_keypad_data __initdata = {
+	.keymap_data	= &crag6410_keymap_data,
+	.rows		= 2,
+	.cols		= 6,
+};
+
+static struct gpio_keys_button crag6410_gpio_keys[] = {
+	[0] = {
+		.code	= KEY_SUSPEND,
+		.gpio	= S3C64XX_GPL(10),	/* EINT 18 */
+		.type	= EV_KEY,
+		.wakeup	= 1,
+		.active_low = 1,
+	},
+	[1] = {
+		.code	= SW_FRONT_PROXIMITY,
+		.gpio	= S3C64XX_GPN(11),	/* EINT 11 */
+		.type	= EV_SW,
+	},
+};
+
+static struct gpio_keys_platform_data crag6410_gpio_keydata = {
+	.buttons	= crag6410_gpio_keys,
+	.nbuttons	= ARRAY_SIZE(crag6410_gpio_keys),
+};
+
+static struct platform_device crag6410_gpio_keydev = {
+	.name		= "gpio-keys",
+	.id		= 0,
+	.dev.platform_data = &crag6410_gpio_keydata,
+};
+
+static struct resource crag6410_dm9k_resource[] = {
+	[0] = {
+		.start	= S3C64XX_PA_XM0CSN5,
+		.end	= S3C64XX_PA_XM0CSN5 + 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= S3C64XX_PA_XM0CSN5 + (1 << 8),
+		.end	= S3C64XX_PA_XM0CSN5 + (1 << 8) + 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= S3C_EINT(17),
+		.end	= S3C_EINT(17),
+		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+	},
+};
+
+static struct dm9000_plat_data mini6410_dm9k_pdata = {
+	.flags	= DM9000_PLATF_16BITONLY,
+};
+
+static struct platform_device crag6410_dm9k_device = {
+	.name		= "dm9000",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(crag6410_dm9k_resource),
+	.resource	= crag6410_dm9k_resource,
+	.dev.platform_data = &mini6410_dm9k_pdata,
+};
+
+static struct resource crag6410_mmgpio_resource[] = {
+	[0] = {
+		.start	= S3C64XX_PA_XM0CSN4 + 1,
+		.end	= S3C64XX_PA_XM0CSN4 + 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device crag6410_mmgpio = {
+	.name		= "basic-mmio-gpio",
+	.id		= -1,
+	.resource	= crag6410_mmgpio_resource,
+	.num_resources	= ARRAY_SIZE(crag6410_mmgpio_resource),
+	.dev.platform_data = &(struct bgpio_pdata) {
+		.base	= -1,
+	},
+};
+
+static struct platform_device speyside_device = {
+	.name		= "speyside",
+	.id		= -1,
+};
+
+static struct platform_device speyside_wm8962_device = {
+	.name		= "speyside-wm8962",
+	.id		= -1,
+};
+
+static struct regulator_consumer_supply wallvdd_consumers[] = {
+	REGULATOR_SUPPLY("SPKVDD1", "1-001a"),
+	REGULATOR_SUPPLY("SPKVDD2", "1-001a"),
+};
+
+static struct regulator_init_data wallvdd_data = {
+	.constraints = {
+		.always_on = 1,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(wallvdd_consumers),
+	.consumer_supplies = wallvdd_consumers,
+};
+
+static struct fixed_voltage_config wallvdd_pdata = {
+	.supply_name = "WALLVDD",
+	.microvolts = 5000000,
+	.init_data = &wallvdd_data,
+	.gpio = -EINVAL,
+};
+
+static struct platform_device wallvdd_device = {
+	.name		= "reg-fixed-voltage",
+	.id		= -1,
+	.dev = {
+		.platform_data = &wallvdd_pdata,
+	},
+};
+
+static struct platform_device *crag6410_devices[] __initdata = {
+	&s3c_device_hsmmc0,
+	&s3c_device_hsmmc1,
+	&s3c_device_hsmmc2,
+	&s3c_device_i2c0,
+	&s3c_device_i2c1,
+	&s3c_device_fb,
+	&s3c_device_ohci,
+	&s3c_device_usb_hsotg,
+	&s3c_device_adc,
+	&s3c_device_rtc,
+	&s3c_device_ts,
+	&s3c_device_timer[0],
+	&s3c64xx_device_iis0,
+	&s3c64xx_device_iis1,
+	&samsung_asoc_dma,
+	&samsung_device_keypad,
+	&crag6410_gpio_keydev,
+	&crag6410_dm9k_device,
+	&s3c64xx_device_spi0,
+	&crag6410_mmgpio,
+	&crag6410_lcd_powerdev,
+	&crag6410_backlight_device,
+	&speyside_device,
+	&speyside_wm8962_device,
+	&wallvdd_device,
+};
+
+static struct pca953x_platform_data crag6410_pca_data = {
+	.gpio_base	= PCA935X_GPIO_BASE,
+	.irq_base	= 0,
+};
+
+static struct regulator_consumer_supply vddarm_consumers[] __initdata = {
+	REGULATOR_SUPPLY("vddarm", NULL),
+};
+
+static struct regulator_init_data vddarm __initdata = {
+	.constraints = {
+		.name = "VDDARM",
+		.min_uV = 1000000,
+		.max_uV = 1300000,
+		.always_on = 1,
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(vddarm_consumers),
+	.consumer_supplies = vddarm_consumers,
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddint __initdata = {
+	.constraints = {
+		.name = "VDDINT",
+		.min_uV = 1000000,
+		.max_uV = 1200000,
+		.always_on = 1,
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+
+static struct regulator_init_data vddmem __initdata = {
+	.constraints = {
+		.name = "VDDMEM",
+		.always_on = 1,
+	},
+};
+
+static struct regulator_init_data vddsys __initdata = {
+	.constraints = {
+		.name = "VDDSYS,VDDEXT,VDDPCM,VDDSS",
+		.always_on = 1,
+	},
+};
+
+static struct regulator_consumer_supply vddmmc_consumers[] __initdata = {
+	REGULATOR_SUPPLY("vmmc", "s3c-sdhci.0"),
+	REGULATOR_SUPPLY("vmmc", "s3c-sdhci.1"),
+	REGULATOR_SUPPLY("vmmc", "s3c-sdhci.2"),
+};
+
+static struct regulator_init_data vddmmc __initdata = {
+	.constraints = {
+		.name = "VDDMMC,UH",
+		.always_on = 1,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(vddmmc_consumers),
+	.consumer_supplies = vddmmc_consumers,
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddotgi __initdata = {
+	.constraints = {
+		.name = "VDDOTGi",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddotg __initdata = {
+	.constraints = {
+		.name = "VDDOTG",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddhi __initdata = {
+	.constraints = {
+		.name = "VDDHI",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddadc __initdata = {
+	.constraints = {
+		.name = "VDDADC,VDDDAC",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddmem0 __initdata = {
+	.constraints = {
+		.name = "VDDMEM0",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddpll __initdata = {
+	.constraints = {
+		.name = "VDDPLL",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddlcd __initdata = {
+	.constraints = {
+		.name = "VDDLCD",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct regulator_init_data vddalive __initdata = {
+	.constraints = {
+		.name = "VDDALIVE",
+		.always_on = 1,
+	},
+	.supply_regulator = "WALLVDD",
+};
+
+static struct wm831x_backup_pdata banff_backup_pdata __initdata = {
+	.charger_enable = 1,
+	.vlim = 2500,  /* mV */
+	.ilim = 200,   /* uA */
+};
+
+static struct wm831x_status_pdata banff_red_led __initdata = {
+	.name = "banff:red:",
+	.default_src = WM831X_STATUS_MANUAL,
+};
+
+static struct wm831x_status_pdata banff_green_led __initdata = {
+	.name = "banff:green:",
+	.default_src = WM831X_STATUS_MANUAL,
+};
+
+static struct wm831x_touch_pdata touch_pdata __initdata = {
+	.data_irq = S3C_EINT(26),
+	.pd_irq = S3C_EINT(27),
+};
+
+static struct wm831x_pdata crag_pmic_pdata __initdata = {
+	.wm831x_num = 1,
+	.irq_base = BANFF_PMIC_IRQ_BASE,
+	.gpio_base = GPIO_BOARD_START + 8,
+
+	.backup = &banff_backup_pdata,
+
+	.gpio_defaults = {
+		/* GPIO11: Touchscreen data - CMOS, DBVDD, active high*/
+		[10] = WM831X_GPN_POL | WM831X_GPN_ENA | 0x6,
+		/* GPIO12: Touchscreen pen down - CMOS, DBVDD, active high*/
+		[11] = WM831X_GPN_POL | WM831X_GPN_ENA | 0x7,
+	},
+
+	.dcdc = {
+		&vddarm,  /* DCDC1 */
+		&vddint,  /* DCDC2 */
+		&vddmem,  /* DCDC3 */
+	},
+
+	.ldo = {
+		&vddsys,   /* LDO1 */
+		&vddmmc,   /* LDO2 */
+		NULL,      /* LDO3 */
+		&vddotgi,  /* LDO4 */
+		&vddotg,   /* LDO5 */
+		&vddhi,    /* LDO6 */
+		&vddadc,   /* LDO7 */
+		&vddmem0,  /* LDO8 */
+		&vddpll,   /* LDO9 */
+		&vddlcd,   /* LDO10 */
+		&vddalive, /* LDO11 */
+	},
+
+	.status = {
+		&banff_green_led,
+		&banff_red_led,
+	},
+
+	.touch = &touch_pdata,
+};
+
+static struct i2c_board_info i2c_devs0[] __initdata = {
+	{ I2C_BOARD_INFO("24c08", 0x50), },
+	{ I2C_BOARD_INFO("tca6408", 0x20),
+	  .platform_data = &crag6410_pca_data,
+	},
+	{ I2C_BOARD_INFO("wm8312", 0x34),
+	  .platform_data = &crag_pmic_pdata,
+	  .irq = S3C_EINT(23),
+	},
+};
+
+static struct s3c2410_platform_i2c i2c0_pdata = {
+	.frequency = 400000,
+};
+
+static struct regulator_init_data pvdd_1v2 __initdata = {
+	.constraints = {
+		.name = "PVDD_1V2",
+		.always_on = 1,
+	},
+};
+
+static struct regulator_consumer_supply pvdd_1v8_consumers[] __initdata = {
+	REGULATOR_SUPPLY("PLLVDD", "1-001a"),
+	REGULATOR_SUPPLY("DBVDD", "1-001a"),
+	REGULATOR_SUPPLY("CPVDD", "1-001a"),
+	REGULATOR_SUPPLY("AVDD2", "1-001a"),
+	REGULATOR_SUPPLY("DCVDD", "1-001a"),
+	REGULATOR_SUPPLY("AVDD", "1-001a"),
+};
+
+static struct regulator_init_data pvdd_1v8 __initdata = {
+	.constraints = {
+		.name = "PVDD_1V8",
+		.always_on = 1,
+	},
+
+	.consumer_supplies = pvdd_1v8_consumers,
+	.num_consumer_supplies = ARRAY_SIZE(pvdd_1v8_consumers),
+};
+
+static struct regulator_consumer_supply pvdd_3v3_consumers[] __initdata = {
+	REGULATOR_SUPPLY("MICVDD", "1-001a"),
+	REGULATOR_SUPPLY("AVDD1", "1-001a"),
+};
+
+static struct regulator_init_data pvdd_3v3 __initdata = {
+	.constraints = {
+		.name = "PVDD_3V3",
+		.always_on = 1,
+	},
+
+	.consumer_supplies = pvdd_3v3_consumers,
+	.num_consumer_supplies = ARRAY_SIZE(pvdd_3v3_consumers),
+};
+
+static struct wm831x_pdata glenfarclas_pmic_pdata __initdata = {
+	.wm831x_num = 2,
+	.irq_base = GLENFARCLAS_PMIC_IRQ_BASE,
+	.gpio_base = GLENFARCLAS_PMIC_GPIO_BASE,
+
+	.gpio_defaults = {
+		/* GPIO1-3: IRQ inputs, rising edge triggered, CMOS */
+		[0] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA,
+		[1] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA,
+		[2] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA,
+	},
+
+	.dcdc = {
+		&pvdd_1v2,  /* DCDC1 */
+		&pvdd_1v8,  /* DCDC2 */
+		&pvdd_3v3,  /* DCDC3 */
+	},
+
+	.disable_touch = true,
+};
+
+static struct wm8915_retune_mobile_config wm8915_retune[] = {
+	{
+		.name = "Sub LPF",
+		.rate = 48000,
+		.regs = {
+			0x6318, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000,
+			0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000,
+			0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000
+		},
+	},
+	{
+		.name = "Sub HPF",
+		.rate = 48000,
+		.regs = {
+			0x000A, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000,
+			0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000,
+			0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000
+		},
+	},
+};
+
+static struct wm8915_pdata wm8915_pdata __initdata = {
+	.ldo_ena = S3C64XX_GPN(7),
+	.gpio_base = CODEC_GPIO_BASE,
+	.micdet_def = 1,
+	.inl_mode = WM8915_DIFFERRENTIAL_1,
+	.inr_mode = WM8915_DIFFERRENTIAL_1,
+
+	.irq_flags = IRQF_TRIGGER_RISING,
+
+	.gpio_default = {
+		0x8001, /* GPIO1 == ADCLRCLK1 */
+		0x8001, /* GPIO2 == ADCLRCLK2, input due to CPU */
+		0x0141, /* GPIO3 == HP_SEL */
+		0x0002, /* GPIO4 == IRQ */
+		0x020e, /* GPIO5 == CLKOUT */
+	},
+
+	.retune_mobile_cfgs = wm8915_retune,
+	.num_retune_mobile_cfgs = ARRAY_SIZE(wm8915_retune),
+};
+
+static struct wm8962_pdata wm8962_pdata __initdata = {
+	.gpio_init = {
+		0,
+		WM8962_GPIO_FN_OPCLK,
+		WM8962_GPIO_FN_DMICCLK,
+		0,
+		0x8000 | WM8962_GPIO_FN_DMICDAT,
+		WM8962_GPIO_FN_IRQ,    /* Open drain mode */
+	},
+	.irq_active_low = true,
+};
+
+static struct wm9081_pdata wm9081_pdata __initdata = {
+	.irq_high = false,
+	.irq_cmos = false,
+};
+
+static struct i2c_board_info i2c_devs1[] __initdata = {
+	{ I2C_BOARD_INFO("wm8311", 0x34),
+	  .irq = S3C_EINT(0),
+	  .platform_data = &glenfarclas_pmic_pdata },
+
+	{ I2C_BOARD_INFO("wm1250-ev1", 0x27) },
+	{ I2C_BOARD_INFO("wm8915", 0x1a),
+	  .platform_data = &wm8915_pdata,
+	  .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2,
+	},
+	{ I2C_BOARD_INFO("wm9081", 0x6c),
+	  .platform_data = &wm9081_pdata, },
+	{ I2C_BOARD_INFO("wm8962", 0x1a),
+	  .platform_data = &wm8962_pdata,
+	  .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2,
+	},
+};
+
+static void __init crag6410_map_io(void)
+{
+	s3c64xx_init_io(NULL, 0);
+	s3c24xx_init_clocks(12000000);
+	s3c24xx_init_uarts(crag6410_uartcfgs, ARRAY_SIZE(crag6410_uartcfgs));
+
+	/* LCD type and Bypass set by bootloader */
+}
+
+static struct s3c_sdhci_platdata crag6410_hsmmc2_pdata = {
+	.max_width		= 4,
+	.cd_type		= S3C_SDHCI_CD_PERMANENT,
+};
+
+static struct s3c_sdhci_platdata crag6410_hsmmc1_pdata = {
+	.max_width		= 4,
+	.cd_type		= S3C_SDHCI_CD_GPIO,
+	.ext_cd_gpio		= S3C64XX_GPF(11),
+};
+
+static void crag6410_cfg_sdhci0(struct platform_device *dev, int width)
+{
+	/* Set all the necessary GPG pins to special-function 2 */
+	s3c_gpio_cfgrange_nopull(S3C64XX_GPG(0), 2 + width, S3C_GPIO_SFN(2));
+
+	/* force card-detected for prototype 0 */
+	s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_DOWN);
+}
+
+static struct s3c_sdhci_platdata crag6410_hsmmc0_pdata = {
+	.max_width		= 4,
+	.cd_type		= S3C_SDHCI_CD_INTERNAL,
+	.cfg_gpio		= crag6410_cfg_sdhci0,
+};
+
+static void __init crag6410_machine_init(void)
+{
+	/* Open drain IRQs need pullups */
+	s3c_gpio_setpull(S3C64XX_GPM(0), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C64XX_GPN(0), S3C_GPIO_PULL_UP);
+
+	gpio_request(S3C64XX_GPB(0), "LCD power");
+	gpio_direction_output(S3C64XX_GPB(0), 0);
+
+	gpio_request(S3C64XX_GPF(14), "LCD PWM");
+	gpio_direction_output(S3C64XX_GPF(14), 0);  /* turn off */
+
+	gpio_request(S3C64XX_GPB(1), "SD power");
+	gpio_direction_output(S3C64XX_GPB(1), 0);
+
+	gpio_request(S3C64XX_GPF(10), "nRESETSEL");
+	gpio_direction_output(S3C64XX_GPF(10), 1);
+
+	s3c_sdhci0_set_platdata(&crag6410_hsmmc0_pdata);
+	s3c_sdhci1_set_platdata(&crag6410_hsmmc1_pdata);
+	s3c_sdhci2_set_platdata(&crag6410_hsmmc2_pdata);
+
+	s3c_i2c0_set_platdata(&i2c0_pdata);
+	s3c_i2c1_set_platdata(NULL);
+	s3c_fb_set_platdata(&crag6410_lcd_pdata);
+
+	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
+	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
+
+	samsung_keypad_set_platdata(&crag6410_keypad_data);
+
+	platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices));
+
+	regulator_has_full_constraints();
+
+	s3c_pm_init();
+}
+
+MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410")
+	/* Maintainer: Mark Brown <broonie@opensource.wolfsonmicro.com> */
+	.boot_params	= S3C64XX_PA_SDRAM + 0x100,
+	.init_irq	= s3c6410_init_irq,
+	.map_io		= crag6410_map_io,
+	.init_machine	= crag6410_machine_init,
+	.timer		= &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s5p64x0/Makefile b/arch/arm/mach-s5p64x0/Makefile
index ae6bf6f..5f6afdf 100644
--- a/arch/arm/mach-s5p64x0/Makefile
+++ b/arch/arm/mach-s5p64x0/Makefile
@@ -13,7 +13,7 @@
 # Core support for S5P64X0 system
 
 obj-$(CONFIG_ARCH_S5P64X0)	+= cpu.o init.o clock.o dma.o gpiolib.o
-obj-$(CONFIG_ARCH_S5P64X0)	+= setup-i2c0.o
+obj-$(CONFIG_ARCH_S5P64X0)	+= setup-i2c0.o irq-eint.o
 obj-$(CONFIG_CPU_S5P6440)	+= clock-s5p6440.o
 obj-$(CONFIG_CPU_S5P6450)	+= clock-s5p6450.o
 
diff --git a/arch/arm/mach-s5p64x0/include/mach/irqs.h b/arch/arm/mach-s5p64x0/include/mach/irqs.h
index 513abff..5837a36 100644
--- a/arch/arm/mach-s5p64x0/include/mach/irqs.h
+++ b/arch/arm/mach-s5p64x0/include/mach/irqs.h
@@ -85,6 +85,8 @@
 #define IRQ_S3CUART_RX4		IRQ_S5P_UART_RX4
 #define IRQ_S3CUART_RX5		IRQ_S5P_UART_RX5
 
+#define IRQ_I2S0		IRQ_I2SV40
+
 /* S5P6450 EINT feature will be added */
 
 /*
diff --git a/arch/arm/mach-s5p64x0/include/mach/regs-gpio.h b/arch/arm/mach-s5p64x0/include/mach/regs-gpio.h
index 0953ef6..6ce2547 100644
--- a/arch/arm/mach-s5p64x0/include/mach/regs-gpio.h
+++ b/arch/arm/mach-s5p64x0/include/mach/regs-gpio.h
@@ -34,4 +34,14 @@
 #define S5P6450_GPQ_BASE		(S5P_VA_GPIO + 0x0180)
 #define S5P6450_GPS_BASE		(S5P_VA_GPIO + 0x0300)
 
+/* External interrupt control registers for group0 */
+
+#define EINT0CON0_OFFSET		(0x900)
+#define EINT0MASK_OFFSET		(0x920)
+#define EINT0PEND_OFFSET		(0x924)
+
+#define S5P64X0_EINT0CON0		(S5P_VA_GPIO + EINT0CON0_OFFSET)
+#define S5P64X0_EINT0MASK		(S5P_VA_GPIO + EINT0MASK_OFFSET)
+#define S5P64X0_EINT0PEND		(S5P_VA_GPIO + EINT0PEND_OFFSET)
+
 #endif /* __ASM_ARCH_REGS_GPIO_H */
diff --git a/arch/arm/mach-s5p64x0/irq-eint.c b/arch/arm/mach-s5p64x0/irq-eint.c
new file mode 100644
index 0000000..69ed454
--- /dev/null
+++ b/arch/arm/mach-s5p64x0/irq-eint.c
@@ -0,0 +1,152 @@
+/* arch/arm/mach-s5p64x0/irq-eint.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *		http://www.samsung.com/
+ *
+ * Based on linux/arch/arm/mach-s3c64xx/irq-eint.c
+ *
+ * S5P64X0 - Interrupt handling for External Interrupts.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <plat/regs-irqtype.h>
+#include <plat/gpio-cfg.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/regs-clock.h>
+
+#define eint_offset(irq)	((irq) - IRQ_EINT(0))
+
+static int s5p64x0_irq_eint_set_type(struct irq_data *data, unsigned int type)
+{
+	int offs = eint_offset(data->irq);
+	int shift;
+	u32 ctrl, mask;
+	u32 newvalue = 0;
+
+	if (offs > 15)
+		return -EINVAL;
+
+	switch (type) {
+	case IRQ_TYPE_NONE:
+		printk(KERN_WARNING "No edge setting!\n");
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		newvalue = S3C2410_EXTINT_RISEEDGE;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		newvalue = S3C2410_EXTINT_FALLEDGE;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		newvalue = S3C2410_EXTINT_BOTHEDGE;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		newvalue = S3C2410_EXTINT_LOWLEV;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		newvalue = S3C2410_EXTINT_HILEV;
+		break;
+	default:
+		printk(KERN_ERR "No such irq type %d", type);
+		return -EINVAL;
+	}
+
+	shift = (offs / 2) * 4;
+	mask = 0x7 << shift;
+
+	ctrl = __raw_readl(S5P64X0_EINT0CON0) & ~mask;
+	ctrl |= newvalue << shift;
+	__raw_writel(ctrl, S5P64X0_EINT0CON0);
+
+	/* Configure the GPIO pin for 6450 or 6440 based on CPU ID */
+	if (0x50000 == (__raw_readl(S5P64X0_SYS_ID) & 0xFF000))
+		s3c_gpio_cfgpin(S5P6450_GPN(offs), S3C_GPIO_SFN(2));
+	else
+		s3c_gpio_cfgpin(S5P6440_GPN(offs), S3C_GPIO_SFN(2));
+
+	return 0;
+}
+
+/*
+ * s5p64x0_irq_demux_eint
+ *
+ * This function demuxes the IRQ from the group0 external interrupts,
+ * from IRQ_EINT(0) to IRQ_EINT(15). It is designed to be inlined into
+ * the specific handlers s5p64x0_irq_demux_eintX_Y.
+ */
+static inline void s5p64x0_irq_demux_eint(unsigned int start, unsigned int end)
+{
+	u32 status = __raw_readl(S5P64X0_EINT0PEND);
+	u32 mask = __raw_readl(S5P64X0_EINT0MASK);
+	unsigned int irq;
+
+	status &= ~mask;
+	status >>= start;
+	status &= (1 << (end - start + 1)) - 1;
+
+	for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) {
+		if (status & 1)
+			generic_handle_irq(irq);
+		status >>= 1;
+	}
+}
+
+static void s5p64x0_irq_demux_eint0_3(unsigned int irq, struct irq_desc *desc)
+{
+	s5p64x0_irq_demux_eint(0, 3);
+}
+
+static void s5p64x0_irq_demux_eint4_11(unsigned int irq, struct irq_desc *desc)
+{
+	s5p64x0_irq_demux_eint(4, 11);
+}
+
+static void s5p64x0_irq_demux_eint12_15(unsigned int irq,
+					struct irq_desc *desc)
+{
+	s5p64x0_irq_demux_eint(12, 15);
+}
+
+static int s5p64x0_alloc_gc(void)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+
+	gc = irq_alloc_generic_chip("s5p64x0-eint", 1, S5P_IRQ_EINT_BASE,
+				    S5P_VA_GPIO, handle_level_irq);
+	if (!gc) {
+		printk(KERN_ERR "%s: irq_alloc_generic_chip for group 0"
+			"external interrupts failed\n", __func__);
+		return -EINVAL;
+	}
+
+	ct = gc->chip_types;
+	ct->chip.irq_ack = irq_gc_ack;
+	ct->chip.irq_mask = irq_gc_mask_set_bit;
+	ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+	ct->chip.irq_set_type = s5p64x0_irq_eint_set_type;
+	ct->regs.ack = EINT0PEND_OFFSET;
+	ct->regs.mask = EINT0MASK_OFFSET;
+	irq_setup_generic_chip(gc, IRQ_MSK(16), IRQ_GC_INIT_MASK_CACHE,
+			       IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+	return 0;
+}
+
+static int __init s5p64x0_init_irq_eint(void)
+{
+	int ret = s5p64x0_alloc_gc();
+	irq_set_chained_handler(IRQ_EINT0_3, s5p64x0_irq_demux_eint0_3);
+	irq_set_chained_handler(IRQ_EINT4_11, s5p64x0_irq_demux_eint4_11);
+	irq_set_chained_handler(IRQ_EINT12_15, s5p64x0_irq_demux_eint12_15);
+
+	return ret;
+}
+arch_initcall(s5p64x0_init_irq_eint);
diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig
index 79bb3a0..69dd87c 100644
--- a/arch/arm/mach-s5pv210/Kconfig
+++ b/arch/arm/mach-s5pv210/Kconfig
@@ -90,6 +90,7 @@
 	select S3C_DEV_HSMMC2
 	select S3C_DEV_I2C1
 	select S3C_DEV_I2C2
+	select S5P_DEV_MFC
 	select S3C_DEV_USB_HSOTG
 	select S5P_DEV_ONENAND
 	select SAMSUNG_DEV_KEYPAD
diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c
index ae72f87..52a8e60 100644
--- a/arch/arm/mach-s5pv210/clock.c
+++ b/arch/arm/mach-s5pv210/clock.c
@@ -324,6 +324,12 @@
 		.enable		= s5pv210_clk_ip0_ctrl,
 		.ctrlbit	= (1 << 26),
 	}, {
+		.name		= "mfc",
+		.devname	= "s5p-mfc",
+		.parent		= &clk_pclk_psys.clk,
+		.enable		= s5pv210_clk_ip0_ctrl,
+		.ctrlbit	= (1 << 16),
+	}, {
 		.name		= "otg",
 		.parent		= &clk_hclk_psys.clk,
 		.enable		= s5pv210_clk_ip1_ctrl,
@@ -879,6 +885,7 @@
 	}, {
 		.clk		= {
 			.name		= "sclk_mfc",
+			.devname	= "s5p-mfc",
 			.enable		= s5pv210_clk_ip0_ctrl,
 			.ctrlbit	= (1 << 16),
 		},
diff --git a/arch/arm/mach-s5pv210/cpu.c b/arch/arm/mach-s5pv210/cpu.c
index 61e6c24..79907ec 100644
--- a/arch/arm/mach-s5pv210/cpu.c
+++ b/arch/arm/mach-s5pv210/cpu.c
@@ -126,7 +126,7 @@
 	s5pv210_default_sdhci2();
 	s5pv210_default_sdhci3();
 
-	s3c_adc_setname("s3c64xx-adc");
+	s3c_adc_setname("samsung-adc-v3");
 
 	s3c_cfcon_setname("s5pv210-pata");
 
diff --git a/arch/arm/mach-s5pv210/dev-audio.c b/arch/arm/mach-s5pv210/dev-audio.c
index 8d58f19..63f5d82 100644
--- a/arch/arm/mach-s5pv210/dev-audio.c
+++ b/arch/arm/mach-s5pv210/dev-audio.c
@@ -18,6 +18,7 @@
 #include <mach/map.h>
 #include <mach/dma.h>
 #include <mach/irqs.h>
+#include <mach/regs-audss.h>
 
 static const char *rclksrc[] = {
 	[0] = "busclk",
@@ -52,6 +53,7 @@
 			.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI
 					 | QUIRK_NEED_RSTCLR,
 			.src_clk = rclksrc,
+			.idma_addr = S5PV210_AUDSS_INT_MEM,
 		},
 	},
 };
diff --git a/arch/arm/mach-s5pv210/include/mach/map.h b/arch/arm/mach-s5pv210/include/mach/map.h
index 1dd5883..aac343c 100644
--- a/arch/arm/mach-s5pv210/include/mach/map.h
+++ b/arch/arm/mach-s5pv210/include/mach/map.h
@@ -59,6 +59,8 @@
 
 #define S5PV210_PA_CFCON		0xE8200000
 
+#define S5PV210_PA_MFC			0xF1700000
+
 #define S5PV210_PA_HSMMC(x)		(0xEB000000 + ((x) * 0x100000))
 
 #define S5PV210_PA_HSOTG		0xEC000000
@@ -107,6 +109,7 @@
 #define S5P_PA_FIMC1			S5PV210_PA_FIMC1
 #define S5P_PA_FIMC2			S5PV210_PA_FIMC2
 #define S5P_PA_MIPI_CSIS0		S5PV210_PA_MIPI_CSIS
+#define S5P_PA_MFC			S5PV210_PA_MFC
 #define S5P_PA_ONENAND			S5PC110_PA_ONENAND
 #define S5P_PA_ONENAND_DMA		S5PC110_PA_ONENAND_DMA
 #define S5P_PA_SDRAM			S5PV210_PA_SDRAM
diff --git a/arch/arm/mach-s5pv210/include/mach/pm-core.h b/arch/arm/mach-s5pv210/include/mach/pm-core.h
index e8d394f..3e22109 100644
--- a/arch/arm/mach-s5pv210/include/mach/pm-core.h
+++ b/arch/arm/mach-s5pv210/include/mach/pm-core.h
@@ -41,3 +41,6 @@
 {
 	/* nothing here yet */
 }
+
+static inline void s3c_pm_restored_gpios(void) { }
+static inline void s3c_pm_saved_gpios(void) { }
diff --git a/arch/arm/mach-s5pv210/include/mach/regs-audss.h b/arch/arm/mach-s5pv210/include/mach/regs-audss.h
new file mode 100644
index 0000000..eacc1f7
--- /dev/null
+++ b/arch/arm/mach-s5pv210/include/mach/regs-audss.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-s5pv210/include/mach/regs-audss.h
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ *		http://www.samsung.com
+ *
+ * S5PV210 Audio SubSystem clock register definitions
+ *
+ * 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 __PLAT_REGS_AUDSS_H
+#define __PLAT_REGS_AUDSS_H __FILE__
+
+#define S5PV210_AUDSS_INT_MEM	(0xC0000000)
+
+#endif /* _PLAT_REGS_AUDSS_H */
diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index e0c4d06..85c2d51 100644
--- a/arch/arm/mach-s5pv210/mach-goni.c
+++ b/arch/arm/mach-s5pv210/mach-goni.c
@@ -46,6 +46,7 @@
 #include <plat/sdhci.h>
 #include <plat/clock.h>
 #include <plat/s5p-time.h>
+#include <plat/mfc.h>
 #include <plat/regs-fb-v4.h>
 
 /* Following are default values for UCON, ULCON and UFCON UART registers */
@@ -808,6 +809,9 @@
 	&goni_i2c_gpio5,
 	&mmc2_fixed_voltage,
 	&goni_device_gpiokeys,
+	&s5p_device_mfc,
+	&s5p_device_mfc_l,
+	&s5p_device_mfc_r,
 	&s3c_device_i2c0,
 	&s5p_device_fimc0,
 	&s5p_device_fimc1,
@@ -841,6 +845,11 @@
 	s5p_set_timer_source(S5P_PWM3, S5P_PWM4);
 }
 
+static void __init goni_reserve(void)
+{
+	s5p_mfc_reserve_mem(0x43000000, 8 << 20, 0x51000000, 8 << 20);
+}
+
 static void __init goni_machine_init(void)
 {
 	/* Radio: call before I2C 1 registeration */
@@ -893,4 +902,5 @@
 	.map_io		= goni_map_io,
 	.init_machine	= goni_machine_init,
 	.timer		= &s5p_timer,
+	.reserve	= &goni_reserve,
 MACHINE_END
diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c b/arch/arm/mach-s5pv210/mach-smdkv210.c
index ef20f92..5e011fc 100644
--- a/arch/arm/mach-s5pv210/mach-smdkv210.c
+++ b/arch/arm/mach-s5pv210/mach-smdkv210.c
@@ -229,6 +229,7 @@
 	&s5pv210_device_iis0,
 	&s5pv210_device_spdif,
 	&samsung_asoc_dma,
+	&samsung_asoc_idma,
 	&samsung_device_keypad,
 	&smdkv210_dm9000,
 	&smdkv210_lcd_lte480wv,
diff --git a/arch/arm/mach-sa1100/include/mach/hardware.h b/arch/arm/mach-sa1100/include/mach/hardware.h
index 967ae76..99f5856 100644
--- a/arch/arm/mach-sa1100/include/mach/hardware.h
+++ b/arch/arm/mach-sa1100/include/mach/hardware.h
@@ -76,12 +76,4 @@
 #include "SA-1101.h"
 #endif
 
-#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_PCI)
-#define PCIBIOS_MIN_IO		0
-#define PCIBIOS_MIN_MEM		0
-#define pcibios_assign_all_busses()	1
-#define HAVE_ARCH_PCI_SET_DMA_MASK	1
-#endif
-
-
 #endif  /* _ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-sa1100/pci-nanoengine.c b/arch/arm/mach-sa1100/pci-nanoengine.c
index fba7a91..964c6c3 100644
--- a/arch/arm/mach-sa1100/pci-nanoengine.c
+++ b/arch/arm/mach-sa1100/pci-nanoengine.c
@@ -122,7 +122,8 @@
 	.write	= nanoengine_write_config,
 };
 
-static int __init pci_nanoengine_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init pci_nanoengine_map_irq(const struct pci_dev *dev, u8 slot,
+	u8 pin)
 {
 	return NANOENGINE_IRQ_GPIO_PCI;
 }
@@ -252,6 +253,9 @@
 {
 	int ret = 0;
 
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 	if (nr == 0) {
 		sys->mem_offset = NANO_PCI_MEM_RW_PHYS;
 		sys->io_offset = 0x400;
diff --git a/arch/arm/mach-shark/include/mach/hardware.h b/arch/arm/mach-shark/include/mach/hardware.h
index 94d84b2..663f952 100644
--- a/arch/arm/mach-shark/include/mach/hardware.h
+++ b/arch/arm/mach-shark/include/mach/hardware.h
@@ -12,11 +12,5 @@
 
 #define UNCACHEABLE_ADDR        0xdf010000
 
-#define pcibios_assign_all_busses()     1
-
-#define PCIBIOS_MIN_IO          0x6000
-#define PCIBIOS_MIN_MEM         0x50000000
-#define PCIMEM_BASE		0xe8000000
-
 #endif
 
diff --git a/arch/arm/mach-shark/pci.c b/arch/arm/mach-shark/pci.c
index 89d175c..7cb79a0 100644
--- a/arch/arm/mach-shark/pci.c
+++ b/arch/arm/mach-shark/pci.c
@@ -8,12 +8,13 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <video/vga.h>
 
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
 #include <asm/mach-types.h>
 
-static int __init shark_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init shark_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (dev->bus->number == 0)
 		if (dev->devfn == 0)
@@ -37,8 +38,15 @@
 
 static int __init shark_pci_init(void)
 {
-	if (machine_is_shark())
-		pci_common_init(&shark_pci);
+	if (!machine_is_shark())
+		return;
+
+	pcibios_min_io = 0x6000;
+	pcibios_min_mem = 0x50000000;
+	vga_base = 0xe8000000;
+
+	pci_common_init(&shark_pci);
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 837138e..9e0856b 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -957,19 +957,16 @@
 	},
 };
 
-static struct platform_device csi2_device = {
-	.name   = "sh-mobile-csi2",
-	.id     = 0,
+static struct sh_mobile_ceu_companion csi2 = {
+	.id		= 0,
 	.num_resources	= ARRAY_SIZE(csi2_resources),
 	.resource	= csi2_resources,
-	.dev    = {
-		.platform_data = &csi2_info,
-	},
+	.platform_data	= &csi2_info,
 };
 
 static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
 	.flags = SH_CEU_FLAG_USE_8BIT_BUS,
-	.csi2_dev = &csi2_device.dev,
+	.csi2 = &csi2,
 };
 
 static struct resource ceu_resources[] = {
@@ -1013,7 +1010,6 @@
 	&lcdc1_device,
 	&lcdc_device,
 	&hdmi_device,
-	&csi2_device,
 	&ceu_device,
 	&ap4evb_camera,
 	&meram_device,
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 5b36b6c..d41c01f 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -1192,8 +1192,8 @@
 };
 
 
-static int mackerel_camera_add(struct soc_camera_link *icl, struct device *dev);
-static void mackerel_camera_del(struct soc_camera_link *icl);
+static int mackerel_camera_add(struct soc_camera_device *icd);
+static void mackerel_camera_del(struct soc_camera_device *icd);
 
 static int camera_set_capture(struct soc_camera_platform_info *info,
 			      int enable)
@@ -1232,16 +1232,15 @@
 	soc_camera_platform_release(&camera_device);
 }
 
-static int mackerel_camera_add(struct soc_camera_link *icl,
-			       struct device *dev)
+static int mackerel_camera_add(struct soc_camera_device *icd)
 {
-	return soc_camera_platform_add(icl, dev, &camera_device, &camera_link,
+	return soc_camera_platform_add(icd, &camera_device, &camera_link,
 				       mackerel_camera_release, 0);
 }
 
-static void mackerel_camera_del(struct soc_camera_link *icl)
+static void mackerel_camera_del(struct soc_camera_device *icd)
 {
-	soc_camera_platform_del(icl, camera_device, &camera_link);
+	soc_camera_platform_del(icd, camera_device, &camera_link);
 }
 
 static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
diff --git a/arch/arm/mach-spear3xx/include/mach/clkdev.h b/arch/arm/mach-spear3xx/include/mach/clkdev.h
deleted file mode 100644
index a3d0733..0000000
--- a/arch/arm/mach-spear3xx/include/mach/clkdev.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-spear3xx/include/mach/clkdev.h
- *
- * Clock Dev framework definitions for SPEAr3xx machine family
- *
- * Copyright (C) 2009 ST Microelectronics
- * Viresh Kumar<viresh.kumar@st.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
-
-#include <plat/clkdev.h>
-
-#endif /* __MACH_CLKDEV_H */
diff --git a/arch/arm/mach-spear6xx/include/mach/clkdev.h b/arch/arm/mach-spear6xx/include/mach/clkdev.h
deleted file mode 100644
index 05676bf..0000000
--- a/arch/arm/mach-spear6xx/include/mach/clkdev.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-spear6xx/include/mach/clkdev.h
- *
- * Clock Dev framework definitions for SPEAr6xx machine family
- *
- * Copyright (C) 2009 ST Microelectronics
- * Viresh Kumar<viresh.kumar@st.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
-
-#include <plat/clkdev.h>
-
-#endif /* __MACH_CLKDEV_H */
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 5ec1846..d82ebab 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -27,14 +27,14 @@
 
 config MACH_HARMONY
        bool "Harmony board"
-       select MACH_HAS_SND_SOC_TEGRA_WM8903
+       select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
        help
          Support for nVidia Harmony development platform
 
 config MACH_KAEN
        bool "Kaen board"
        select MACH_SEABOARD
-       select MACH_HAS_SND_SOC_TEGRA_WM8903
+       select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
        help
          Support for the Kaen version of Seaboard
 
@@ -45,12 +45,18 @@
 
 config MACH_SEABOARD
        bool "Seaboard board"
-       select MACH_HAS_SND_SOC_TEGRA_WM8903
+       select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC
        help
          Support for nVidia Seaboard development platform. It will
 	 also be included for some of the derivative boards that
 	 have large similarities with the seaboard design.
 
+config MACH_TEGRA_DT
+	bool "Generic Tegra board (FDT support)"
+	select USE_OF
+	help
+	  Support for generic nVidia Tegra boards using Flattened Device Tree
+
 config MACH_TRIMSLICE
        bool "TrimSlice board"
        select TEGRA_PCI
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index ed58ef9..f11b910 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -29,5 +29,8 @@
 obj-${CONFIG_MACH_SEABOARD}             += board-seaboard.o
 obj-${CONFIG_MACH_SEABOARD}             += board-seaboard-pinmux.o
 
+obj-${CONFIG_MACH_TEGRA_DT}             += board-dt.o
+obj-${CONFIG_MACH_TEGRA_DT}             += board-harmony-pinmux.o
+
 obj-${CONFIG_MACH_TRIMSLICE}            += board-trimslice.o
 obj-${CONFIG_MACH_TRIMSLICE}            += board-trimslice-pinmux.o
diff --git a/arch/arm/mach-tegra/Makefile.boot b/arch/arm/mach-tegra/Makefile.boot
index db52d61..428ad12 100644
--- a/arch/arm/mach-tegra/Makefile.boot
+++ b/arch/arm/mach-tegra/Makefile.boot
@@ -1,3 +1,6 @@
 zreladdr-$(CONFIG_ARCH_TEGRA_2x_SOC)	:= 0x00008000
 params_phys-$(CONFIG_ARCH_TEGRA_2x_SOC)	:= 0x00000100
 initrd_phys-$(CONFIG_ARCH_TEGRA_2x_SOC)	:= 0x00800000
+
+dtb-$(CONFIG_MACH_HARMONY) += tegra-harmony.dtb
+dtb-$(CONFIG_MACH_SEABOARD) += tegra-seaboard.dtb
diff --git a/arch/arm/mach-tegra/board-dt.c b/arch/arm/mach-tegra/board-dt.c
new file mode 100644
index 0000000..9f47e04
--- /dev/null
+++ b/arch/arm/mach-tegra/board-dt.c
@@ -0,0 +1,119 @@
+/*
+ * nVidia Tegra device tree board support
+ *
+ * Copyright (C) 2010 Secret Lab Technologies, Ltd.
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pda_power.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-tegra.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/setup.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "board.h"
+#include "board-harmony.h"
+#include "clock.h"
+#include "devices.h"
+
+void harmony_pinmux_init(void);
+void seaboard_pinmux_init(void);
+
+
+struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC1_BASE, "sdhci-tegra.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC2_BASE, "sdhci-tegra.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC3_BASE, "sdhci-tegra.2", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC4_BASE, "sdhci-tegra.3", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C_BASE, "tegra-i2c.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C2_BASE, "tegra-i2c.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C3_BASE, "tegra-i2c.2", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_DVC_BASE, "tegra-i2c.3", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra-i2s.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra-i2s.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra-das", NULL),
+	{}
+};
+
+static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
+	/* name		parent		rate		enabled */
+	{ "uartd",	"pll_p",	216000000,	true },
+	{ NULL,		NULL,		0,		0},
+};
+
+static struct of_device_id tegra_dt_match_table[] __initdata = {
+	{ .compatible = "simple-bus", },
+	{}
+};
+
+static struct of_device_id tegra_dt_gic_match[] __initdata = {
+	{ .compatible = "nvidia,tegra20-gic", },
+	{}
+};
+
+static void __init tegra_dt_init(void)
+{
+	struct device_node *node;
+
+	node = of_find_matching_node_by_address(NULL, tegra_dt_gic_match,
+						TEGRA_ARM_INT_DIST_BASE);
+	if (node)
+		irq_domain_add_simple(node, INT_GIC_BASE);
+
+	tegra_clk_init_from_table(tegra_dt_clk_init_table);
+
+	if (of_machine_is_compatible("nvidia,harmony"))
+		harmony_pinmux_init();
+	else if (of_machine_is_compatible("nvidia,seaboard"))
+		seaboard_pinmux_init();
+
+	/*
+	 * Finished with the static registrations now; fill in the missing
+	 * devices
+	 */
+	of_platform_populate(NULL, tegra_dt_match_table, tegra20_auxdata_lookup, NULL);
+}
+
+static const char * tegra_dt_board_compat[] = {
+	"nvidia,harmony",
+	"nvidia,seaboard",
+	NULL
+};
+
+DT_MACHINE_START(TEGRA_DT, "nVidia Tegra (Flattened Device Tree)")
+	.map_io		= tegra_map_common_io,
+	.init_early	= tegra_init_early,
+	.init_irq	= tegra_init_irq,
+	.timer		= &tegra_timer,
+	.init_machine	= tegra_dt_init,
+	.dt_compat	= tegra_dt_board_compat,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c
index 30e18bc..846cd7d 100644
--- a/arch/arm/mach-tegra/board-harmony.c
+++ b/arch/arm/mach-tegra/board-harmony.c
@@ -25,7 +25,6 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/i2c.h>
-#include <linux/i2c-tegra.h>
 
 #include <sound/wm8903.h>
 
@@ -83,22 +82,6 @@
 	},
 };
 
-static struct tegra_i2c_platform_data harmony_i2c1_platform_data = {
-	.bus_clk_rate   = 400000,
-};
-
-static struct tegra_i2c_platform_data harmony_i2c2_platform_data = {
-	.bus_clk_rate   = 400000,
-};
-
-static struct tegra_i2c_platform_data harmony_i2c3_platform_data = {
-	.bus_clk_rate   = 400000,
-};
-
-static struct tegra_i2c_platform_data harmony_dvc_platform_data = {
-	.bus_clk_rate   = 400000,
-};
-
 static struct wm8903_platform_data harmony_wm8903_pdata = {
 	.irq_active_low = 0,
 	.micdet_cfg = 0,
@@ -121,11 +104,6 @@
 
 static void __init harmony_i2c_init(void)
 {
-	tegra_i2c_device1.dev.platform_data = &harmony_i2c1_platform_data;
-	tegra_i2c_device2.dev.platform_data = &harmony_i2c2_platform_data;
-	tegra_i2c_device3.dev.platform_data = &harmony_i2c3_platform_data;
-	tegra_i2c_device4.dev.platform_data = &harmony_dvc_platform_data;
-
 	platform_device_register(&tegra_i2c_device1);
 	platform_device_register(&tegra_i2c_device2);
 	platform_device_register(&tegra_i2c_device3);
diff --git a/arch/arm/mach-tegra/board-paz00-pinmux.c b/arch/arm/mach-tegra/board-paz00-pinmux.c
index 2643d1b..bdd2627 100644
--- a/arch/arm/mach-tegra/board-paz00-pinmux.c
+++ b/arch/arm/mach-tegra/board-paz00-pinmux.c
@@ -141,12 +141,10 @@
 };
 
 static struct tegra_gpio_table gpio_table[] = {
-	{ .gpio = TEGRA_GPIO_SD1_CD,	.enable = true	},
-	{ .gpio = TEGRA_GPIO_SD1_WP,	.enable = true	},
-	{ .gpio = TEGRA_GPIO_SD1_POWER,	.enable = true	},
-	{ .gpio = TEGRA_GPIO_SD4_CD,	.enable = true	},
-	{ .gpio = TEGRA_GPIO_SD4_WP,	.enable = true	},
-	{ .gpio = TEGRA_GPIO_SD4_POWER,	.enable = true	},
+	{ .gpio = TEGRA_GPIO_SD1_CD,	.enable = true },
+	{ .gpio = TEGRA_GPIO_SD1_WP,	.enable = true },
+	{ .gpio = TEGRA_GPIO_SD1_POWER,	.enable = true },
+	{ .gpio = TEGRA_ULPI_RST,	.enable = true },
 };
 
 void paz00_pinmux_init(void)
diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c
index 57e50a8..ea2f79c 100644
--- a/arch/arm/mach-tegra/board-paz00.c
+++ b/arch/arm/mach-tegra/board-paz00.c
@@ -25,6 +25,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pda_power.h>
 #include <linux/io.h>
+#include <linux/i2c.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -34,6 +35,7 @@
 #include <mach/iomap.h>
 #include <mach/irqs.h>
 #include <mach/sdhci.h>
+#include <mach/gpio.h>
 
 #include "board.h"
 #include "board-paz00.h"
@@ -66,10 +68,22 @@
 static struct platform_device *paz00_devices[] __initdata = {
 	&debug_uart,
 	&tegra_sdhci_device1,
-	&tegra_sdhci_device2,
 	&tegra_sdhci_device4,
 };
 
+static void paz00_i2c_init(void)
+{
+	platform_device_register(&tegra_i2c_device1);
+	platform_device_register(&tegra_i2c_device2);
+	platform_device_register(&tegra_i2c_device4);
+}
+
+static void paz00_usb_init(void)
+{
+	platform_device_register(&tegra_ehci2_device);
+	platform_device_register(&tegra_ehci3_device);
+}
+
 static void __init tegra_paz00_fixup(struct machine_desc *desc,
 	struct tag *tags, char **cmdline, struct meminfo *mi)
 {
@@ -84,23 +98,16 @@
 	{ NULL,		NULL,		0,		0},
 };
 
-
 static struct tegra_sdhci_platform_data sdhci_pdata1 = {
 	.cd_gpio	= TEGRA_GPIO_SD1_CD,
 	.wp_gpio	= TEGRA_GPIO_SD1_WP,
 	.power_gpio	= TEGRA_GPIO_SD1_POWER,
 };
 
-static struct tegra_sdhci_platform_data sdhci_pdata2 = {
+static struct tegra_sdhci_platform_data sdhci_pdata4 = {
 	.cd_gpio	= -1,
 	.wp_gpio	= -1,
 	.power_gpio	= -1,
-};
-
-static struct tegra_sdhci_platform_data sdhci_pdata4 = {
-	.cd_gpio	= TEGRA_GPIO_SD4_CD,
-	.wp_gpio	= TEGRA_GPIO_SD4_WP,
-	.power_gpio	= TEGRA_GPIO_SD4_POWER,
 	.is_8bit	= 1,
 };
 
@@ -111,13 +118,15 @@
 	paz00_pinmux_init();
 
 	tegra_sdhci_device1.dev.platform_data = &sdhci_pdata1;
-	tegra_sdhci_device2.dev.platform_data = &sdhci_pdata2;
 	tegra_sdhci_device4.dev.platform_data = &sdhci_pdata4;
 
 	platform_add_devices(paz00_devices, ARRAY_SIZE(paz00_devices));
+
+	paz00_i2c_init();
+	paz00_usb_init();
 }
 
-MACHINE_START(PAZ00, "paz00")
+MACHINE_START(PAZ00, "Toshiba AC100 / Dynabook AZ")
 	.boot_params	= 0x00000100,
 	.fixup		= tegra_paz00_fixup,
 	.map_io         = tegra_map_common_io,
diff --git a/arch/arm/mach-tegra/board-paz00.h b/arch/arm/mach-tegra/board-paz00.h
index da193ca7..d4ff39d 100644
--- a/arch/arm/mach-tegra/board-paz00.h
+++ b/arch/arm/mach-tegra/board-paz00.h
@@ -17,12 +17,10 @@
 #ifndef _MACH_TEGRA_BOARD_PAZ00_H
 #define _MACH_TEGRA_BOARD_PAZ00_H
 
-#define TEGRA_GPIO_SD1_CD               TEGRA_GPIO_PV5
-#define TEGRA_GPIO_SD1_WP               TEGRA_GPIO_PH1
-#define TEGRA_GPIO_SD1_POWER            TEGRA_GPIO_PT3
-#define TEGRA_GPIO_SD4_CD               TEGRA_GPIO_PH2
-#define TEGRA_GPIO_SD4_WP               TEGRA_GPIO_PH3
-#define TEGRA_GPIO_SD4_POWER            TEGRA_GPIO_PI6
+#define TEGRA_GPIO_SD1_CD		TEGRA_GPIO_PV5
+#define TEGRA_GPIO_SD1_WP		TEGRA_GPIO_PH1
+#define TEGRA_GPIO_SD1_POWER		TEGRA_GPIO_PT3
+#define TEGRA_ULPI_RST			TEGRA_GPIO_PV0
 
 void paz00_pinmux_init(void);
 
diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c
index 10fbbdc..56cbabf 100644
--- a/arch/arm/mach-tegra/board-seaboard.c
+++ b/arch/arm/mach-tegra/board-seaboard.c
@@ -19,7 +19,6 @@
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 #include <linux/i2c.h>
-#include <linux/i2c-tegra.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/io.h>
@@ -66,22 +65,6 @@
 	{ NULL,		NULL,		0,		0},
 };
 
-static struct tegra_i2c_platform_data seaboard_i2c1_platform_data = {
-	.bus_clk_rate	= 400000.
-};
-
-static struct tegra_i2c_platform_data seaboard_i2c2_platform_data = {
-	.bus_clk_rate	= 400000,
-};
-
-static struct tegra_i2c_platform_data seaboard_i2c3_platform_data = {
-	.bus_clk_rate	= 400000,
-};
-
-static struct tegra_i2c_platform_data seaboard_dvc_platform_data = {
-	.bus_clk_rate	= 400000,
-};
-
 static struct gpio_keys_button seaboard_gpio_keys_buttons[] = {
 	{
 		.code		= SW_LID,
@@ -137,9 +120,9 @@
 static struct platform_device *seaboard_devices[] __initdata = {
 	&debug_uart,
 	&tegra_pmu_device,
-	&tegra_sdhci_device1,
-	&tegra_sdhci_device3,
 	&tegra_sdhci_device4,
+	&tegra_sdhci_device3,
+	&tegra_sdhci_device1,
 	&seaboard_gpio_keys_device,
 };
 
@@ -161,11 +144,6 @@
 
 	i2c_register_board_info(3, &adt7461_device, 1);
 
-	tegra_i2c_device1.dev.platform_data = &seaboard_i2c1_platform_data;
-	tegra_i2c_device2.dev.platform_data = &seaboard_i2c2_platform_data;
-	tegra_i2c_device3.dev.platform_data = &seaboard_i2c3_platform_data;
-	tegra_i2c_device4.dev.platform_data = &seaboard_dvc_platform_data;
-
 	platform_device_register(&tegra_i2c_device1);
 	platform_device_register(&tegra_i2c_device2);
 	platform_device_register(&tegra_i2c_device3);
diff --git a/arch/arm/mach-tegra/board-trimslice-pinmux.c b/arch/arm/mach-tegra/board-trimslice-pinmux.c
index d9dc5d2..47c596c 100644
--- a/arch/arm/mach-tegra/board-trimslice-pinmux.c
+++ b/arch/arm/mach-tegra/board-trimslice-pinmux.c
@@ -29,7 +29,7 @@
 	{TEGRA_PINGROUP_ATC,   TEGRA_MUX_NAND,          TEGRA_PUPD_NORMAL,	TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_ATD,   TEGRA_MUX_GMI,           TEGRA_PUPD_NORMAL,	TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_ATE,   TEGRA_MUX_GMI,           TEGRA_PUPD_NORMAL,	TEGRA_TRI_TRISTATE},
-	{TEGRA_PINGROUP_CDEV1, TEGRA_MUX_OSC,           TEGRA_PUPD_NORMAL,      TEGRA_TRI_NORMAL},
+	{TEGRA_PINGROUP_CDEV1, TEGRA_MUX_PLLA_OUT,      TEGRA_PUPD_NORMAL,      TEGRA_TRI_NORMAL},
 	{TEGRA_PINGROUP_CDEV2, TEGRA_MUX_PLLP_OUT4,     TEGRA_PUPD_PULL_DOWN,   TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_CRTP,  TEGRA_MUX_CRT,           TEGRA_PUPD_NORMAL,      TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_CSUS,  TEGRA_MUX_VI_SENSOR_CLK, TEGRA_PUPD_PULL_DOWN,   TEGRA_TRI_TRISTATE},
@@ -126,7 +126,7 @@
 	{TEGRA_PINGROUP_SPIH,  TEGRA_MUX_SPI2_ALT,      TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_UAA,   TEGRA_MUX_ULPI,          TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_UAB,   TEGRA_MUX_ULPI,          TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
-	{TEGRA_PINGROUP_UAC,   TEGRA_MUX_RSVD2,         TEGRA_PUPD_NORMAL,      TEGRA_TRI_TRISTATE},
+	{TEGRA_PINGROUP_UAC,   TEGRA_MUX_RSVD2,         TEGRA_PUPD_NORMAL,      TEGRA_TRI_NORMAL},
 	{TEGRA_PINGROUP_UAD,   TEGRA_MUX_IRDA,          TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_UCA,   TEGRA_MUX_UARTC,         TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
 	{TEGRA_PINGROUP_UCB,   TEGRA_MUX_UARTC,         TEGRA_PUPD_PULL_UP,     TEGRA_TRI_TRISTATE},
@@ -145,6 +145,9 @@
 static struct tegra_gpio_table gpio_table[] = {
 	{ .gpio = TRIMSLICE_GPIO_SD4_CD, .enable = true	}, /* mmc4 cd */
 	{ .gpio = TRIMSLICE_GPIO_SD4_WP, .enable = true	}, /* mmc4 wp */
+
+	{ .gpio = TRIMSLICE_GPIO_USB1_MODE, .enable = true }, /* USB1 mode */
+	{ .gpio = TRIMSLICE_GPIO_USB2_RST,  .enable = true }, /* USB2 PHY rst */
 };
 
 void __init trimslice_pinmux_init(void)
diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c
index cda4cfd..89a6d2a 100644
--- a/arch/arm/mach-tegra/board-trimslice.c
+++ b/arch/arm/mach-tegra/board-trimslice.c
@@ -23,6 +23,8 @@
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 #include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -30,6 +32,7 @@
 
 #include <mach/iomap.h>
 #include <mach/sdhci.h>
+#include <mach/gpio.h>
 
 #include "board.h"
 #include "clock.h"
@@ -71,12 +74,58 @@
 	.power_gpio	= -1,
 };
 
+static struct platform_device trimslice_audio_device = {
+	.name	= "tegra-snd-trimslice",
+	.id	= 0,
+};
+
 static struct platform_device *trimslice_devices[] __initdata = {
 	&debug_uart,
 	&tegra_sdhci_device1,
 	&tegra_sdhci_device4,
+	&tegra_i2s_device1,
+	&tegra_das_device,
+	&tegra_pcm_device,
+	&trimslice_audio_device,
 };
 
+static struct i2c_board_info trimslice_i2c3_board_info[] = {
+	{
+		I2C_BOARD_INFO("tlv320aic23", 0x1a),
+	},
+	{
+		I2C_BOARD_INFO("em3027", 0x56),
+	},
+};
+
+static void trimslice_i2c_init(void)
+{
+	platform_device_register(&tegra_i2c_device1);
+	platform_device_register(&tegra_i2c_device2);
+	platform_device_register(&tegra_i2c_device3);
+
+	i2c_register_board_info(2, trimslice_i2c3_board_info,
+				ARRAY_SIZE(trimslice_i2c3_board_info));
+}
+
+static void trimslice_usb_init(void)
+{
+	int err;
+
+	platform_device_register(&tegra_ehci3_device);
+
+	platform_device_register(&tegra_ehci2_device);
+
+	err = gpio_request_one(TRIMSLICE_GPIO_USB1_MODE, GPIOF_OUT_INIT_HIGH,
+			       "usb1mode");
+	if (err) {
+		pr_err("TrimSlice: failed to obtain USB1 mode gpio: %d\n", err);
+		return;
+	}
+
+	platform_device_register(&tegra_ehci1_device);
+}
+
 static void __init tegra_trimslice_fixup(struct machine_desc *desc,
 	struct tag *tags, char **cmdline, struct meminfo *mi)
 {
@@ -90,6 +139,10 @@
 static __initdata struct tegra_clk_init_table trimslice_clk_init_table[] = {
 	/* name		parent		rate		enabled */
 	{ "uarta",	"pll_p",	216000000,	true },
+	{ "pll_a",	"pll_p_out1",	56448000,	true },
+	{ "pll_a_out0",	"pll_a",	11289600,	true },
+	{ "cdev1",	NULL,		0,		true },
+	{ "i2s1",	"pll_a_out0",	11289600,	false},
 	{ NULL,		NULL,		0,		0},
 };
 
@@ -112,6 +165,9 @@
 	tegra_sdhci_device4.dev.platform_data = &sdhci_pdata4;
 
 	platform_add_devices(trimslice_devices, ARRAY_SIZE(trimslice_devices));
+
+	trimslice_i2c_init();
+	trimslice_usb_init();
 }
 
 MACHINE_START(TRIMSLICE, "trimslice")
diff --git a/arch/arm/mach-tegra/board-trimslice.h b/arch/arm/mach-tegra/board-trimslice.h
index e8ef629..7a7dee8 100644
--- a/arch/arm/mach-tegra/board-trimslice.h
+++ b/arch/arm/mach-tegra/board-trimslice.h
@@ -20,6 +20,9 @@
 #define TRIMSLICE_GPIO_SD4_CD	TEGRA_GPIO_PP1	/* mmc4 cd */
 #define TRIMSLICE_GPIO_SD4_WP	TEGRA_GPIO_PP2	/* mmc4 wp */
 
+#define TRIMSLICE_GPIO_USB1_MODE	TEGRA_GPIO_PV2 /* USB1 mode */
+#define TRIMSLICE_GPIO_USB2_RST		TEGRA_GPIO_PV0 /* USB2 PHY reset */
+
 void trimslice_pinmux_init(void);
 
 #endif
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index 1528f9d..57e35d2 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -22,10 +22,14 @@
 #include <linux/dma-mapping.h>
 #include <linux/fsl_devices.h>
 #include <linux/serial_8250.h>
+#include <linux/i2c-tegra.h>
+#include <linux/platform_data/tegra_usb.h>
 #include <asm/pmu.h>
 #include <mach/irqs.h>
 #include <mach/iomap.h>
 #include <mach/dma.h>
+#include <mach/usb_phy.h>
+#include "gpio-names.h"
 
 static struct resource i2c_resource1[] = {
 	[0] = {
@@ -79,13 +83,29 @@
 	},
 };
 
+static struct tegra_i2c_platform_data tegra_i2c1_platform_data = {
+	.bus_clk_rate   = 400000,
+};
+
+static struct tegra_i2c_platform_data tegra_i2c2_platform_data = {
+	.bus_clk_rate   = 400000,
+};
+
+static struct tegra_i2c_platform_data tegra_i2c3_platform_data = {
+	.bus_clk_rate   = 400000,
+};
+
+static struct tegra_i2c_platform_data tegra_dvc_platform_data = {
+	.bus_clk_rate   = 400000,
+};
+
 struct platform_device tegra_i2c_device1 = {
 	.name		= "tegra-i2c",
 	.id		= 0,
 	.resource	= i2c_resource1,
 	.num_resources	= ARRAY_SIZE(i2c_resource1),
 	.dev = {
-		.platform_data = 0,
+		.platform_data = &tegra_i2c1_platform_data,
 	},
 };
 
@@ -95,7 +115,7 @@
 	.resource	= i2c_resource2,
 	.num_resources	= ARRAY_SIZE(i2c_resource2),
 	.dev = {
-		.platform_data = 0,
+		.platform_data = &tegra_i2c2_platform_data,
 	},
 };
 
@@ -105,7 +125,7 @@
 	.resource	= i2c_resource3,
 	.num_resources	= ARRAY_SIZE(i2c_resource3),
 	.dev = {
-		.platform_data = 0,
+		.platform_data = &tegra_i2c3_platform_data,
 	},
 };
 
@@ -115,7 +135,7 @@
 	.resource	= i2c_resource4,
 	.num_resources	= ARRAY_SIZE(i2c_resource4),
 	.dev = {
-		.platform_data = 0,
+		.platform_data = &tegra_dvc_platform_data,
 	},
 };
 
@@ -334,6 +354,28 @@
 	},
 };
 
+static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
+	/* All existing boards use GPIO PV0 for phy reset */
+	.reset_gpio = TEGRA_GPIO_PV0,
+	.clk = "cdev2",
+};
+
+static struct tegra_ehci_platform_data tegra_ehci1_pdata = {
+	.operating_mode = TEGRA_USB_OTG,
+	.power_down_on_bus_suspend = 1,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci2_pdata = {
+	.phy_config = &tegra_ehci2_ulpi_phy_config,
+	.operating_mode = TEGRA_USB_HOST,
+	.power_down_on_bus_suspend = 1,
+};
+
+static struct tegra_ehci_platform_data tegra_ehci3_pdata = {
+	.operating_mode = TEGRA_USB_HOST,
+	.power_down_on_bus_suspend = 1,
+};
+
 static u64 tegra_ehci_dmamask = DMA_BIT_MASK(32);
 
 struct platform_device tegra_ehci1_device = {
@@ -342,6 +384,7 @@
 	.dev	= {
 		.dma_mask	= &tegra_ehci_dmamask,
 		.coherent_dma_mask = DMA_BIT_MASK(32),
+		.platform_data = &tegra_ehci1_pdata,
 	},
 	.resource = tegra_usb1_resources,
 	.num_resources = ARRAY_SIZE(tegra_usb1_resources),
@@ -353,6 +396,7 @@
 	.dev	= {
 		.dma_mask	= &tegra_ehci_dmamask,
 		.coherent_dma_mask = DMA_BIT_MASK(32),
+		.platform_data = &tegra_ehci2_pdata,
 	},
 	.resource = tegra_usb2_resources,
 	.num_resources = ARRAY_SIZE(tegra_usb2_resources),
@@ -364,6 +408,7 @@
 	.dev	= {
 		.dma_mask	= &tegra_ehci_dmamask,
 		.coherent_dma_mask = DMA_BIT_MASK(32),
+		.platform_data = &tegra_ehci3_pdata,
 	},
 	.resource = tegra_usb3_resources,
 	.num_resources = ARRAY_SIZE(tegra_usb3_resources),
diff --git a/arch/arm/mach-tegra/include/mach/barriers.h b/arch/arm/mach-tegra/include/mach/barriers.h
deleted file mode 100644
index 425b42e..0000000
--- a/arch/arm/mach-tegra/include/mach/barriers.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * arch/arm/mach-realview/include/mach/barriers.h
- *
- * Copyright (C) 2010 ARM Ltd.
- * Written by Catalin Marinas <catalin.marinas@arm.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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __MACH_BARRIERS_H
-#define __MACH_BARRIERS_H
-
-#include <asm/outercache.h>
-
-#define rmb()		dsb()
-#define wmb()		do { dsb(); outer_sync(); } while (0)
-#define mb()		wmb()
-
-#endif	/* __MACH_BARRIERS_H */
diff --git a/arch/arm/mach-tegra/include/mach/hardware.h b/arch/arm/mach-tegra/include/mach/hardware.h
deleted file mode 100644
index 56e43b3..0000000
--- a/arch/arm/mach-tegra/include/mach/hardware.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * arch/arm/mach-tegra/include/mach/hardware.h
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
- *	Erik Gilling <konkers@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __MACH_TEGRA_HARDWARE_H
-#define __MACH_TEGRA_HARDWARE_H
-
-#define PCIBIOS_MIN_IO			0x1000
-#define PCIBIOS_MIN_MEM			0
-#define pcibios_assign_all_busses()	1
-
-#endif
diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
index d0183d8..027c421 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -21,7 +21,6 @@
 #ifndef __MACH_TEGRA_SYSTEM_H
 #define __MACH_TEGRA_SYSTEM_H
 
-#include <mach/hardware.h>
 #include <mach/iomap.h>
 
 extern void (*arch_reset)(char mode, const char *cmd);
diff --git a/arch/arm/mach-tegra/io.c b/arch/arm/mach-tegra/io.c
index 31848a9..ea50fe2 100644
--- a/arch/arm/mach-tegra/io.c
+++ b/arch/arm/mach-tegra/io.c
@@ -24,7 +24,6 @@
 #include <linux/mm.h>
 #include <linux/io.h>
 
-#include <mach/hardware.h>
 #include <asm/page.h>
 #include <asm/mach/map.h>
 
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index 2941212..f1f699d 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -449,7 +449,7 @@
 	return 1;
 }
 
-static int tegra_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int tegra_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	return INT_PCIE_INTR;
 }
@@ -912,6 +912,8 @@
 	if (!(init_port0 || init_port1))
 		return -ENODEV;
 
+	pcibios_min_mem = 0;
+
 	err = tegra_pcie_get_resources();
 	if (err)
 		return err;
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index 1a594dc..0886cbc 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -21,7 +21,6 @@
 
 #include <asm/cacheflush.h>
 #include <asm/hardware/gic.h>
-#include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/smp_scu.h>
 
@@ -122,7 +121,7 @@
 	}
 
 	for (i = 0; i < ncores; i++)
-		cpu_set(i, cpu_possible_map);
+		set_cpu_possible(i, true);
 
 	set_smp_cross_call(gic_raise_softirq);
 }
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index bb61807..0fe9b3e 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2182,8 +2182,8 @@
 	PERIPH_CLK("tvo",	"tvo",			NULL,	49,	0x188,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
 	PERIPH_CLK("tvdac",	"tvdac",		NULL,	53,	0x194,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
-	PERIPH_CLK("disp1",	"tegradc.0",		NULL,	27,	0x138,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
-	PERIPH_CLK("disp2",	"tegradc.1",		NULL,	26,	0x13c,	600000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp1",	"tegradc.0",		NULL,	27,	0x138,	600000000, mux_pllp_plld_pllc_clkm,	MUX), /* scales with voltage and process_id */
+	PERIPH_CLK("disp2",	"tegradc.1",		NULL,	26,	0x13c,	600000000, mux_pllp_plld_pllc_clkm,	MUX), /* scales with voltage and process_id */
 	PERIPH_CLK("usbd",	"fsl-tegra-udc",	NULL,	22,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb2",	"tegra-ehci.1",		NULL,	58,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
 	PERIPH_CLK("usb3",	"tegra-ehci.2",		NULL,	59,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
diff --git a/arch/arm/mach-u300/spi.c b/arch/arm/mach-u300/spi.c
index 5767208..7b597e2 100644
--- a/arch/arm/mach-u300/spi.c
+++ b/arch/arm/mach-u300/spi.c
@@ -40,8 +40,8 @@
 	.hierarchy = SSP_MASTER,
 	/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
 	.slave_tx_disable = 0,
-	.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
-	.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
+	.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
+	.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
 	.ctrl_len = SSP_BITS_12,
 	.wait_state = SSP_MWIRE_WAIT_ZERO,
 	.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
diff --git a/arch/arm/mach-u300/timer.c b/arch/arm/mach-u300/timer.c
index 18d7fa0..5f51bde 100644
--- a/arch/arm/mach-u300/timer.c
+++ b/arch/arm/mach-u300/timer.c
@@ -27,9 +27,6 @@
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
 
-/* Be able to sleep for atleast 4 seconds (usually more) */
-#define APPTIMER_MIN_RANGE 4
-
 /*
  * APP side special timer registers
  * This timer contains four timers which can fire an interrupt each.
@@ -309,11 +306,11 @@
 
 /* Use general purpose timer 1 as clock event */
 static struct clock_event_device clockevent_u300_1mhz = {
-	.name           = "GPT1",
-	.rating         = 300, /* Reasonably fast and accurate clock event */
-	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
-	.set_next_event = u300_set_next_event,
-	.set_mode       = u300_set_mode,
+	.name		= "GPT1",
+	.rating		= 300, /* Reasonably fast and accurate clock event */
+	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.set_next_event	= u300_set_next_event,
+	.set_mode	= u300_set_mode,
 };
 
 /* Clock event timer interrupt handler */
@@ -328,9 +325,9 @@
 }
 
 static struct irqaction u300_timer_irq = {
-	.name           = "U300 Timer Tick",
-	.flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
-	.handler        = u300_timer_interrupt,
+	.name		= "U300 Timer Tick",
+	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= u300_timer_interrupt,
 };
 
 /*
@@ -413,16 +410,10 @@
 			"GPT2", rate, 300, 32, clocksource_mmio_readl_up))
 		pr_err("timer: failed to initialize U300 clock source\n");
 
-	clockevents_calc_mult_shift(&clockevent_u300_1mhz,
-				    rate, APPTIMER_MIN_RANGE);
-	/* 32bit counter, so 32bits delta is max */
-	clockevent_u300_1mhz.max_delta_ns =
-		clockevent_delta2ns(0xffffffff, &clockevent_u300_1mhz);
-	/* This timer is slow enough to set for 1 cycle == 1 MHz */
-	clockevent_u300_1mhz.min_delta_ns =
-		clockevent_delta2ns(1, &clockevent_u300_1mhz);
-	clockevent_u300_1mhz.cpumask = cpumask_of(0);
-	clockevents_register_device(&clockevent_u300_1mhz);
+	/* Configure and register the clockevent */
+	clockevents_config_and_register(&clockevent_u300_1mhz, rate,
+					1, 0xffffffff);
+
 	/*
 	 * TODO: init and register the rest of the timers too, they can be
 	 * used by hrtimers!
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index f8b9392..4210cb4 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -20,7 +20,7 @@
 
 endmenu
 
-menu "Ux500 target platform"
+menu "Ux500 target platform (boards)"
 
 config MACH_U8500
 	bool "U8500 Development platform"
@@ -29,6 +29,19 @@
 	help
 	  Include support for the mop500 development platform.
 
+config MACH_HREFV60
+       bool "U85000 Development platform, HREFv60 version"
+       depends on UX500_SOC_DB8500
+       help
+         Include support for the HREFv60 new development platform.
+
+config MACH_SNOWBALL
+	bool "U8500 Snowball platform"
+	depends on UX500_SOC_DB8500
+	select MACH_U8500
+	help
+	  Include support for the snowball development platform.
+
 config MACH_U5500
 	bool "U5500 Development platform"
 	depends on UX500_SOC_DB5500
diff --git a/arch/arm/mach-ux500/board-mop500-pins.c b/arch/arm/mach-ux500/board-mop500-pins.c
index 70cdbd6..f26fd76 100644
--- a/arch/arm/mach-ux500/board-mop500-pins.c
+++ b/arch/arm/mach-ux500/board-mop500-pins.c
@@ -236,6 +236,46 @@
 
 };
 
+static pin_cfg_t snowball_pins[] = {
+	/* SSP0, to AB8500 */
+	GPIO143_SSP0_CLK,
+	GPIO144_SSP0_FRM,
+	GPIO145_SSP0_RXD	| PIN_PULL_DOWN,
+	GPIO146_SSP0_TXD,
+
+	/* MMC0: MicroSD card */
+	GPIO21_MC0_DAT31DIR     | PIN_OUTPUT_HIGH,
+
+	/* MMC2: LAN */
+	GPIO86_SM_ADQ0,
+	GPIO87_SM_ADQ1,
+	GPIO88_SM_ADQ2,
+	GPIO89_SM_ADQ3,
+	GPIO90_SM_ADQ4,
+	GPIO91_SM_ADQ5,
+	GPIO92_SM_ADQ6,
+	GPIO93_SM_ADQ7,
+
+	GPIO94_SM_ADVn,
+	GPIO95_SM_CS0n,
+	GPIO96_SM_OEn,
+	GPIO97_SM_WEn,
+
+	GPIO128_SM_CKO,
+	GPIO130_SM_FBCLK,
+	GPIO131_SM_ADQ8,
+	GPIO132_SM_ADQ9,
+	GPIO133_SM_ADQ10,
+	GPIO134_SM_ADQ11,
+	GPIO135_SM_ADQ12,
+	GPIO136_SM_ADQ13,
+	GPIO137_SM_ADQ14,
+	GPIO138_SM_ADQ15,
+
+	/* RSTn_LAN */
+	GPIO141_GPIO		| PIN_OUTPUT_HIGH,
+};
+
 void __init mop500_pins_init(void)
 {
 	nmk_config_pins(mop500_pins_common,
@@ -243,6 +283,9 @@
 	if (machine_is_hrefv60())
 		nmk_config_pins(mop500_pins_hrefv60,
 				ARRAY_SIZE(mop500_pins_hrefv60));
+	else if (machine_is_snowball())
+		nmk_config_pins(snowball_pins,
+				ARRAY_SIZE(snowball_pins));
 	else
 		nmk_config_pins(mop500_pins_default,
 				ARRAY_SIZE(mop500_pins_default));
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 0f2e522..2735d03 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -272,7 +272,14 @@
 			.max_uV = 2900000,
 			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
 					  REGULATOR_CHANGE_STATUS,
-			.boot_on = 1, /* must be on for display */
+			.boot_on = 1, /* display is on at boot */
+			/*
+			 * This voltage cannot be disabled right now because
+			 * it is somehow affecting the external MMC
+			 * functionality, though that typically will use
+			 * AUX3.
+			 */
+			.always_on = 1,
 		},
 		.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers),
 		.consumer_supplies = ab8500_vaux1_consumers,
diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c
index 7c6cb4fa..d0cb9e5 100644
--- a/arch/arm/mach-ux500/board-mop500-sdi.c
+++ b/arch/arm/mach-ux500/board-mop500-sdi.c
@@ -32,13 +32,32 @@
 #define MCI_DATA31DIREN		(1 << 5)
 #define MCI_FBCLKEN		(1 << 7)
 
+/* GPIO pins used by the sdi0 level shifter */
+static int sdi0_en = -1;
+static int sdi0_vsel = -1;
+
 static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd,
 				   unsigned char power_mode)
 {
-	if (power_mode == MMC_POWER_UP)
-		gpio_set_value_cansleep(GPIO_SDMMC_EN, 1);
-	else if (power_mode == MMC_POWER_OFF)
-		gpio_set_value_cansleep(GPIO_SDMMC_EN, 0);
+	switch (power_mode) {
+	case MMC_POWER_UP:
+	case MMC_POWER_ON:
+		/*
+		 * Level shifter voltage should depend on vdd to when deciding
+		 * on either 1.8V or 2.9V. Once the decision has been made the
+		 * level shifter must be disabled and re-enabled with a changed
+		 * select signal in order to switch the voltage. Since there is
+		 * no framework support yet for indicating 1.8V in vdd, use the
+		 * default 2.9V.
+		 */
+		gpio_direction_output(sdi0_vsel, 0);
+		gpio_direction_output(sdi0_en, 1);
+		break;
+	case MMC_POWER_OFF:
+		gpio_direction_output(sdi0_vsel, 0);
+		gpio_direction_output(sdi0_en, 0);
+		break;
+	}
 
 	return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN |
 	       MCI_DATA2DIREN | MCI_DATA31DIREN;
@@ -67,8 +86,10 @@
 static struct mmci_platform_data mop500_sdi0_data = {
 	.vdd_handler	= mop500_sdi0_vdd_handler,
 	.ocr_mask	= MMC_VDD_29_30,
-	.f_max		= 100000000,
-	.capabilities	= MMC_CAP_4_BIT_DATA,
+	.f_max		= 50000000,
+	.capabilities	= MMC_CAP_4_BIT_DATA |
+				MMC_CAP_SD_HIGHSPEED |
+				MMC_CAP_MMC_HIGHSPEED,
 	.gpio_wp	= -1,
 #ifdef CONFIG_STE_DMA40
 	.dma_filter	= stedma40_filter,
@@ -77,10 +98,6 @@
 #endif
 };
 
-/* GPIO pins used by the sdi0 level shifter */
-static int sdi0_en = -1;
-static int sdi0_vsel = -1;
-
 static void sdi0_configure(void)
 {
 	int ret;
@@ -140,7 +157,7 @@
 
 static struct mmci_platform_data mop500_sdi2_data = {
 	.ocr_mask	= MMC_VDD_165_195,
-	.f_max		= 100000000,
+	.f_max		= 50000000,
 	.capabilities	= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
 	.gpio_cd	= -1,
 	.gpio_wp	= -1,
@@ -177,7 +194,7 @@
 
 static struct mmci_platform_data mop500_sdi4_data = {
 	.ocr_mask	= MMC_VDD_29_30,
-	.f_max		= 100000000,
+	.f_max		= 50000000,
 	.capabilities	= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
 			  MMC_CAP_MMC_HIGHSPEED,
 	.gpio_cd	= -1,
@@ -199,17 +216,27 @@
 	/* PoP:ed eMMC on top of DB8500 v1.0 has problems with high speed */
 	if (!cpu_is_u8500v10())
 		mop500_sdi2_data.capabilities |= MMC_CAP_MMC_HIGHSPEED;
-	db8500_add_sdi2(&mop500_sdi2_data, periphid);
+	/* sdi2 on snowball is in ATL_B mode for FSMC (LAN) */
+	if (!machine_is_snowball())
+		db8500_add_sdi2(&mop500_sdi2_data, periphid);
 
 	/* On-board eMMC */
 	db8500_add_sdi4(&mop500_sdi4_data, periphid);
 
-	if (machine_is_hrefv60()) {
-		mop500_sdi0_data.gpio_cd = HREFV60_SDMMC_CD_GPIO;
-		sdi0_en = HREFV60_SDMMC_EN_GPIO;
-		sdi0_vsel = HREFV60_SDMMC_1V8_3V_GPIO;
+	if (machine_is_hrefv60() || machine_is_snowball()) {
+		if (machine_is_hrefv60()) {
+			mop500_sdi0_data.gpio_cd = HREFV60_SDMMC_CD_GPIO;
+			sdi0_en = HREFV60_SDMMC_EN_GPIO;
+			sdi0_vsel = HREFV60_SDMMC_1V8_3V_GPIO;
+		} else if (machine_is_snowball()) {
+			mop500_sdi0_data.gpio_cd = SNOWBALL_SDMMC_CD_GPIO;
+			mop500_sdi0_data.cd_invert = true;
+			sdi0_en = SNOWBALL_SDMMC_EN_GPIO;
+			sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO;
+		}
 		sdi0_configure();
 	}
+
 	/*
 	 * On boards with the TC35892 GPIO expander, sdi0 will finally
 	 * be added when the TC35892 initializes and calls
diff --git a/arch/arm/mach-ux500/board-mop500-uib.c b/arch/arm/mach-ux500/board-mop500-uib.c
index 69cce41..5af36aa 100644
--- a/arch/arm/mach-ux500/board-mop500-uib.c
+++ b/arch/arm/mach-ux500/board-mop500-uib.c
@@ -25,7 +25,7 @@
 	void (*init)(void);
 };
 
-static struct __initdata uib mop500_uibs[] = {
+static struct uib __initdata mop500_uibs[] = {
 	[STUIB] = {
 		.name	= "ST-UIB",
 		.option	= "stuib",
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 2a08c07..cd54aba 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -26,9 +26,11 @@
 #include <linux/mfd/ab8500/gpio.h>
 #include <linux/leds-lp5521.h>
 #include <linux/input.h>
+#include <linux/smsc911x.h>
 #include <linux/gpio_keys.h>
 #include <linux/delay.h>
 
+#include <linux/leds.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
@@ -47,6 +49,26 @@
 #include "board-mop500.h"
 #include "board-mop500-regulators.h"
 
+static struct gpio_led snowball_led_array[] = {
+	{
+		.name = "user_led",
+		.default_trigger = "none",
+		.gpio = 142,
+	},
+};
+
+static struct gpio_led_platform_data snowball_led_data = {
+	.leds = snowball_led_array,
+	.num_leds = ARRAY_SIZE(snowball_led_array),
+};
+
+static struct platform_device snowball_led_dev = {
+	.name = "leds-gpio",
+	.dev = {
+		.platform_data = &snowball_led_data,
+	},
+};
+
 static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
 	.gpio_base		= MOP500_AB8500_GPIO(0),
 	.irq_base		= MOP500_AB8500_VIR_GPIO_IRQ_BASE,
@@ -69,6 +91,97 @@
 					0x7A, 0x00, 0x00},
 };
 
+static struct gpio_keys_button snowball_key_array[] = {
+	{
+		.gpio           = 32,
+		.type           = EV_KEY,
+		.code           = KEY_1,
+		.desc           = "userpb",
+		.active_low     = 1,
+		.debounce_interval = 50,
+		.wakeup         = 1,
+	},
+	{
+		.gpio           = 151,
+		.type           = EV_KEY,
+		.code           = KEY_2,
+		.desc           = "extkb1",
+		.active_low     = 1,
+		.debounce_interval = 50,
+		.wakeup         = 1,
+	},
+	{
+		.gpio           = 152,
+		.type           = EV_KEY,
+		.code           = KEY_3,
+		.desc           = "extkb2",
+		.active_low     = 1,
+		.debounce_interval = 50,
+		.wakeup         = 1,
+	},
+	{
+		.gpio           = 161,
+		.type           = EV_KEY,
+		.code           = KEY_4,
+		.desc           = "extkb3",
+		.active_low     = 1,
+		.debounce_interval = 50,
+		.wakeup         = 1,
+	},
+	{
+		.gpio           = 162,
+		.type           = EV_KEY,
+		.code           = KEY_5,
+		.desc           = "extkb4",
+		.active_low     = 1,
+		.debounce_interval = 50,
+		.wakeup         = 1,
+	},
+};
+
+static struct gpio_keys_platform_data snowball_key_data = {
+	.buttons        = snowball_key_array,
+	.nbuttons       = ARRAY_SIZE(snowball_key_array),
+};
+
+static struct platform_device snowball_key_dev = {
+	.name           = "gpio-keys",
+	.id             = -1,
+	.dev            = {
+		.platform_data  = &snowball_key_data,
+	}
+};
+
+static struct smsc911x_platform_config snowball_sbnet_cfg = {
+	.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH,
+	.irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
+	.flags = SMSC911X_USE_16BIT | SMSC911X_FORCE_INTERNAL_PHY,
+	.shift = 1,
+};
+
+static struct resource sbnet_res[] = {
+	{
+		.name = "smsc911x-memory",
+		.start = (0x5000 << 16),
+		.end  =  (0x5000 << 16) + 0xffff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = NOMADIK_GPIO_TO_IRQ(140),
+		.end = NOMADIK_GPIO_TO_IRQ(140),
+		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+	},
+};
+
+static struct platform_device snowball_sbnet_dev = {
+	.name           = "smsc911x",
+	.num_resources  = ARRAY_SIZE(sbnet_res),
+	.resource       = sbnet_res,
+	.dev            = {
+		.platform_data = &snowball_sbnet_cfg,
+	},
+};
+
 static struct ab8500_platform_data ab8500_platdata = {
 	.irq_base	= MOP500_AB8500_IRQ_BASE,
 	.regulator_reg_init = ab8500_regulator_reg_init,
@@ -295,8 +408,9 @@
 }
 
 /* add any platform devices here - TODO */
-static struct platform_device *platform_devs[] __initdata = {
+static struct platform_device *mop500_platform_devs[] __initdata = {
 	&mop500_gpio_keys_device,
+	&ab8500_device,
 };
 
 #ifdef CONFIG_STE_DMA40
@@ -478,6 +592,13 @@
 	db8500_add_uart2(&uart2_plat);
 }
 
+static struct platform_device *snowball_platform_devs[] __initdata = {
+	&snowball_led_dev,
+	&snowball_key_dev,
+	&snowball_sbnet_dev,
+	&ab8500_device,
+};
+
 static void __init mop500_init_machine(void)
 {
 	int i2c0_devs;
@@ -487,24 +608,29 @@
 	 * all these GPIO pins to the internal GPIO controller
 	 * instead.
 	 */
-	if (machine_is_hrefv60())
-		mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
-	else
-		mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
+	if (!machine_is_snowball()) {
+		if (machine_is_hrefv60())
+			mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
+		else
+			mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
+	}
 
 	u8500_init_devices();
 
 	mop500_pins_init();
 
-	platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
+	if (machine_is_snowball())
+		platform_add_devices(snowball_platform_devs,
+					ARRAY_SIZE(snowball_platform_devs));
+	else
+		platform_add_devices(mop500_platform_devs,
+					ARRAY_SIZE(mop500_platform_devs));
 
 	mop500_i2c_init();
 	mop500_sdi_init();
 	mop500_spi_init();
 	mop500_uart_init();
 
-	platform_device_register(&ab8500_device);
-
 	i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
 	if (machine_is_hrefv60())
 		i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
@@ -512,6 +638,9 @@
 	i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
 	i2c_register_board_info(2, mop500_i2c2_devices,
 				ARRAY_SIZE(mop500_i2c2_devices));
+
+	/* This board has full regulator constraints */
+	regulator_has_full_constraints();
 }
 
 MACHINE_START(U8500, "ST-Ericsson MOP500 platform")
@@ -531,3 +660,12 @@
 	.timer		= &ux500_timer,
 	.init_machine	= mop500_init_machine,
 MACHINE_END
+
+MACHINE_START(SNOWBALL, "Calao Systems Snowball platform")
+	.boot_params	= 0x100,
+	.map_io		= u8500_map_io,
+	.init_irq	= ux500_init_irq,
+	/* we re-use nomadik timer here */
+	.timer		= &ux500_timer,
+	.init_machine	= mop500_init_machine,
+MACHINE_END
diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h
index 03a31cc..ee77a89 100644
--- a/arch/arm/mach-ux500/board-mop500.h
+++ b/arch/arm/mach-ux500/board-mop500.h
@@ -7,6 +7,11 @@
 #ifndef __BOARD_MOP500_H
 #define __BOARD_MOP500_H
 
+/* snowball GPIO for MMC card */
+#define SNOWBALL_SDMMC_EN_GPIO 217
+#define SNOWBALL_SDMMC_1V8_3V_GPIO 228
+#define SNOWBALL_SDMMC_CD_GPIO 218
+
 /* HREFv60-specific GPIO assignments, this board has no GPIO expander */
 #define HREFV60_TOUCH_RST_GPIO		143
 #define HREFV60_PROX_SENSE_GPIO		217
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index 7d107be..e832664 100644
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/clkdev.h>
+#include <linux/cpufreq.h>
 
 #include <plat/mtu.h>
 #include <mach/hardware.h>
@@ -742,6 +743,51 @@
 late_initcall(clk_debugfs_init);
 #endif /* defined(CONFIG_DEBUG_FS) */
 
+unsigned long clk_smp_twd_rate = 400000000;
+
+unsigned long clk_smp_twd_get_rate(struct clk *clk)
+{
+	return clk_smp_twd_rate;
+}
+
+static struct clk clk_smp_twd = {
+	.get_rate = clk_smp_twd_get_rate,
+	.name =  "smp_twd",
+};
+
+static struct clk_lookup clk_smp_twd_lookup = {
+	.dev_id = "smp_twd",
+	.clk = &clk_smp_twd,
+};
+
+#ifdef CONFIG_CPU_FREQ
+
+static int clk_twd_cpufreq_transition(struct notifier_block *nb,
+				      unsigned long state, void *data)
+{
+	struct cpufreq_freqs *f = data;
+
+	if (state == CPUFREQ_PRECHANGE) {
+		/* Save frequency in simple Hz */
+		clk_smp_twd_rate = f->new * 1000;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block clk_twd_cpufreq_nb = {
+	.notifier_call = clk_twd_cpufreq_transition,
+};
+
+static int clk_init_smp_twd_cpufreq(void)
+{
+	return cpufreq_register_notifier(&clk_twd_cpufreq_nb,
+				  CPUFREQ_TRANSITION_NOTIFIER);
+}
+late_initcall(clk_init_smp_twd_cpufreq);
+
+#endif
+
 int __init clk_init(void)
 {
 	if (cpu_is_u8500ed()) {
@@ -762,6 +808,8 @@
 	else
 		clkdev_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks));
 
+	clkdev_add(&clk_smp_twd_lookup);
+
 #ifdef CONFIG_DEBUG_FS
 	clk_debugfs_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks));
 	if (cpu_is_u8500ed())
diff --git a/arch/arm/mach-ux500/cpu-db5500.c b/arch/arm/mach-ux500/cpu-db5500.c
index c01bc19..22705d2 100644
--- a/arch/arm/mach-ux500/cpu-db5500.c
+++ b/arch/arm/mach-ux500/cpu-db5500.c
@@ -44,6 +44,7 @@
 	__IO_DEV_DESC(U5500_GPIO3_BASE, SZ_4K),
 	__IO_DEV_DESC(U5500_GPIO4_BASE, SZ_4K),
 	__IO_DEV_DESC(U5500_PRCMU_BASE, SZ_4K),
+	__IO_DEV_DESC(U5500_PRCMU_TCDM_BASE, SZ_4K),
 };
 
 static struct resource db5500_pmu_resources[] = {
diff --git a/arch/arm/mach-ux500/include/mach/clkdev.h b/arch/arm/mach-ux500/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-ux500/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-ux500/include/mach/uncompress.h b/arch/arm/mach-ux500/include/mach/uncompress.h
index 088b550..7dd0807 100644
--- a/arch/arm/mach-ux500/include/mach/uncompress.h
+++ b/arch/arm/mach-ux500/include/mach/uncompress.h
@@ -54,7 +54,8 @@
 	if (machine_is_u8500() ||
 	    machine_is_svp8500v1() ||
 	    machine_is_svp8500v2() ||
-	    machine_is_hrefv60())
+	    machine_is_hrefv60()   ||
+	    machine_is_snowball())
 		ux500_uart_base = U8500_UART2_BASE;
 	else if (machine_is_u5500())
 		ux500_uart_base = U5500_UART0_BASE;
diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c
index 82e5359..0a01cbd 100644
--- a/arch/arm/mach-ux500/usb.c
+++ b/arch/arm/mach-ux500/usb.c
@@ -6,6 +6,7 @@
  */
 #include <linux/platform_device.h>
 #include <linux/usb/musb.h>
+#include <linux/dma-mapping.h>
 #include <plat/ste_dma40.h>
 #include <mach/hardware.h>
 #include <mach/usb.h>
diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
index 9cdec5a..c1f38f6 100644
--- a/arch/arm/mach-versatile/Kconfig
+++ b/arch/arm/mach-versatile/Kconfig
@@ -17,4 +17,12 @@
 	  Include support for the ARM(R) Versatile Application Baseboard
 	  for the ARM926EJ-S.
 
+config MACH_VERSATILE_DT
+	bool "Support Versatile platform from device tree"
+	select USE_OF
+	select CPU_ARM926T
+	help
+	  Include support for the ARM(R) Versatile/PB platform,
+	  using the device tree for discovery
+
 endmenu
diff --git a/arch/arm/mach-versatile/Makefile b/arch/arm/mach-versatile/Makefile
index 97cf4d8..81fa3fe 100644
--- a/arch/arm/mach-versatile/Makefile
+++ b/arch/arm/mach-versatile/Makefile
@@ -5,4 +5,5 @@
 obj-y					:= core.o
 obj-$(CONFIG_ARCH_VERSATILE_PB)		+= versatile_pb.o
 obj-$(CONFIG_MACH_VERSATILE_AB)		+= versatile_ab.o
+obj-$(CONFIG_MACH_VERSATILE_DT)		+= versatile_dt.o
 obj-$(CONFIG_PCI)			+= pci.o
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index 0c99cf0..e340a54 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -24,6 +24,9 @@
 #include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
 #include <linux/amba/bus.h>
 #include <linux/amba/clcd.h>
 #include <linux/amba/pl061.h>
@@ -83,13 +86,26 @@
 #define PIC_MASK	0
 #endif
 
+/* Lookup table for finding a DT node that represents the vic instance */
+static const struct of_device_id vic_of_match[] __initconst = {
+	{ .compatible = "arm,versatile-vic", },
+	{}
+};
+
+static const struct of_device_id sic_of_match[] __initconst = {
+	{ .compatible = "arm,versatile-sic", },
+	{}
+};
+
 void __init versatile_init_irq(void)
 {
 	vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0);
+	irq_domain_generate_simple(vic_of_match, VERSATILE_VIC_BASE, IRQ_VIC_START);
 
 	writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
 
 	fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq);
+	irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START);
 
 	/*
 	 * Interrupts on secondary controller from 0 to 8 are routed to
@@ -646,6 +662,52 @@
 	&kmi1_device,
 };
 
+#ifdef CONFIG_OF
+/*
+ * Lookup table for attaching a specific name and platform_data pointer to
+ * devices as they get created by of_platform_populate().  Ideally this table
+ * would not exist, but the current clock implementation depends on some devices
+ * having a specific name.
+ */
+struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI0_BASE, "fpga:06", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI1_BASE, "fpga:07", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART3_BASE, "fpga:09", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", NULL),
+
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_CLCD_BASE, "dev:20", &clcd_plat_data),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART0_BASE, "dev:f1", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART1_BASE, "dev:f2", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART2_BASE, "dev:f3", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_SSP_BASE, "dev:f4", NULL),
+
+#if 0
+	/*
+	 * These entries are unnecessary because no clocks referencing
+	 * them.  I've left them in for now as place holders in case
+	 * any of them need to be added back, but they should be
+	 * removed before actually committing this patch.  --gcl
+	 */
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_AACI_BASE, "fpga:04", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI1_BASE, "fpga:0a", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_SMC_BASE, "dev:00", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_MPMC_BASE, "dev:10", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_DMAC_BASE, "dev:30", NULL),
+
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCTL_BASE, "dev:e0", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_WATCHDOG_BASE, "dev:e1", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO0_BASE, "dev:e4", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO1_BASE, "dev:e5", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO2_BASE, "dev:e6", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO3_BASE, "dev:e7", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_RTC_BASE, "dev:e8", NULL),
+	OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI_BASE, "dev:f0", NULL),
+#endif
+	{}
+};
+#endif
+
 #ifdef CONFIG_LEDS
 #define VA_LEDS_BASE (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_LED_OFFSET)
 
diff --git a/arch/arm/mach-versatile/core.h b/arch/arm/mach-versatile/core.h
index fd6404e..e014227 100644
--- a/arch/arm/mach-versatile/core.h
+++ b/arch/arm/mach-versatile/core.h
@@ -23,6 +23,7 @@
 #define __ASM_ARCH_VERSATILE_H
 
 #include <linux/amba/bus.h>
+#include <linux/of_platform.h>
 
 extern void __init versatile_init(void);
 extern void __init versatile_init_early(void);
@@ -30,6 +31,9 @@
 extern void __init versatile_map_io(void);
 extern struct sys_timer versatile_timer;
 extern unsigned int mmc_status(struct device *dev);
+#ifdef CONFIG_OF
+extern struct of_dev_auxdata versatile_auxdata_lookup[];
+#endif
 
 #define AMBA_DEVICE(name,busid,base,plat)			\
 static struct amba_device name##_device = {			\
diff --git a/arch/arm/mach-versatile/include/mach/hardware.h b/arch/arm/mach-versatile/include/mach/hardware.h
index 6911e1f..4d4973d 100644
--- a/arch/arm/mach-versatile/include/mach/hardware.h
+++ b/arch/arm/mach-versatile/include/mach/hardware.h
@@ -30,12 +30,6 @@
 #define VERSATILE_PCI_VIRT_BASE		(void __iomem *)0xe8000000ul
 #define VERSATILE_PCI_CFG_VIRT_BASE	(void __iomem *)0xe9000000ul
 
-/* CIK guesswork */
-#define PCIBIOS_MIN_IO			0x44000000
-#define PCIBIOS_MIN_MEM			0x50000000
-
-#define pcibios_assign_all_busses()     1
-
 /* macro to get at IO space when running virtually */
 #define IO_ADDRESS(x)		(((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000)
 
diff --git a/arch/arm/mach-versatile/pci.c b/arch/arm/mach-versatile/pci.c
index 13c7e5f..c898deb 100644
--- a/arch/arm/mach-versatile/pci.c
+++ b/arch/arm/mach-versatile/pci.c
@@ -311,6 +311,9 @@
 
 void __init pci_versatile_preinit(void)
 {
+	pcibios_min_io = 0x44000000;
+	pcibios_min_mem = 0x50000000;
+
 	__raw_writel(VERSATILE_PCI_MEM_BASE0 >> 28, PCI_IMAP0);
 	__raw_writel(VERSATILE_PCI_MEM_BASE1 >> 28, PCI_IMAP1);
 	__raw_writel(VERSATILE_PCI_MEM_BASE2 >> 28, PCI_IMAP2);
@@ -325,7 +328,7 @@
 /*
  * map the specified device/slot/pin to an IRQ.   Different backplanes may need to modify this.
  */
-static int __init versatile_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init versatile_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 	int devslot = PCI_SLOT(dev->devfn);
diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c
new file mode 100644
index 0000000..54e037c
--- /dev/null
+++ b/arch/arm/mach-versatile/versatile_dt.c
@@ -0,0 +1,51 @@
+/*
+ * Versatile board support using the device tree
+ *
+ *  Copyright (C) 2010 Secret Lab Technologies Ltd.
+ *  Copyright (C) 2009 Jeremy Kerr <jeremy.kerr@canonical.com>
+ *  Copyright (C) 2004 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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/of_irq.h>
+#include <linux/of_platform.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "core.h"
+
+static void __init versatile_dt_init(void)
+{
+	of_platform_populate(NULL, of_default_bus_match_table,
+			     versatile_auxdata_lookup, NULL);
+}
+
+static const char *versatile_dt_match[] __initconst = {
+	"arm,versatile-ab",
+	"arm,versatile-pb",
+	NULL,
+};
+
+DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
+	.map_io		= versatile_map_io,
+	.init_early	= versatile_init_early,
+	.init_irq	= versatile_init_irq,
+	.timer		= &versatile_timer,
+	.init_machine	= versatile_dt_init,
+	.dt_compat	= versatile_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-w90x900/include/mach/clkdev.h b/arch/arm/mach-w90x900/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/mach-w90x900/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
new file mode 100644
index 0000000..397268c
--- /dev/null
+++ b/arch/arm/mach-zynq/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Common support
+obj-y				:= common.o timer.o
diff --git a/arch/arm/mach-zynq/Makefile.boot b/arch/arm/mach-zynq/Makefile.boot
new file mode 100644
index 0000000..67039c3
--- /dev/null
+++ b/arch/arm/mach-zynq/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x00800000
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
new file mode 100644
index 0000000..73e9368
--- /dev/null
+++ b/arch/arm/mach-zynq/common.c
@@ -0,0 +1,118 @@
+/*
+ * This file contains common code that is intended to be used across
+ * boards so that it's not replicated.
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/cpumask.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <asm/page.h>
+#include <asm/hardware/gic.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/zynq_soc.h>
+#include <mach/clkdev.h>
+#include "common.h"
+
+static struct of_device_id zynq_of_bus_ids[] __initdata = {
+	{ .compatible = "simple-bus", },
+	{}
+};
+
+/**
+ * xilinx_init_machine() - System specific initialization, intended to be
+ *			   called from board specific initialization.
+ */
+static void __init xilinx_init_machine(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+	/*
+	 * 64KB way size, 8-way associativity, parity disabled
+	 */
+	l2x0_init(PL310_L2CC_BASE, 0x02060000, 0xF0F0FFFF);
+#endif
+
+	of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
+}
+
+/**
+ * xilinx_irq_init() - Interrupt controller initialization for the GIC.
+ */
+static void __init xilinx_irq_init(void)
+{
+	gic_init(0, 29, SCU_GIC_DIST_BASE, SCU_GIC_CPU_BASE);
+}
+
+/* The minimum devices needed to be mapped before the VM system is up and
+ * running include the GIC, UART and Timer Counter.
+ */
+
+static struct map_desc io_desc[] __initdata = {
+	{
+		.virtual	= TTC0_VIRT,
+		.pfn		= __phys_to_pfn(TTC0_PHYS),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= SCU_PERIPH_VIRT,
+		.pfn		= __phys_to_pfn(SCU_PERIPH_PHYS),
+		.length		= SZ_8K,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= PL310_L2CC_VIRT,
+		.pfn		= __phys_to_pfn(PL310_L2CC_PHYS),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE,
+	},
+
+#ifdef CONFIG_DEBUG_LL
+	{
+		.virtual	= UART0_VIRT,
+		.pfn		= __phys_to_pfn(UART0_PHYS),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE,
+	},
+#endif
+
+};
+
+/**
+ * xilinx_map_io() - Create memory mappings needed for early I/O.
+ */
+static void __init xilinx_map_io(void)
+{
+	iotable_init(io_desc, ARRAY_SIZE(io_desc));
+}
+
+static const char *xilinx_dt_match[] = {
+	"xlnx,zynq-ep107",
+	NULL
+};
+
+MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
+	.map_io		= xilinx_map_io,
+	.init_irq	= xilinx_irq_init,
+	.init_machine	= xilinx_init_machine,
+	.timer		= &xttcpss_sys_timer,
+	.dt_compat	= xilinx_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/common.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/common.h
index 66cd3f4..a009644 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/common.h
@@ -1,10 +1,8 @@
 /*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+ * This file contains common function prototypes to avoid externs
+ * in the c files.
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +12,13 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_ZYNQ_COMMON_H__
+#define __MACH_ZYNQ_COMMON_H__
 
-struct clk;
+#include <asm/mach/time.h>
 
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+extern struct sys_timer xttcpss_sys_timer;
 
 #endif
diff --git a/arch/arm/mach-zynq/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/clkdev.h
new file mode 100644
index 0000000..c6e73d8
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/clkdev.h
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-zynq/include/mach/clkdev.h
+ *
+ *  Copyright (C) 2011 Xilinx, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_CLKDEV_H__
+#define __MACH_CLKDEV_H__
+
+#include <plat/clock.h>
+
+struct clk {
+	unsigned long		rate;
+	const struct clk_ops	*ops;
+	const struct icst_params *params;
+	void __iomem		*vcoreg;
+};
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-zynq/include/mach/debug-macro.S b/arch/arm/mach-zynq/include/mach/debug-macro.S
new file mode 100644
index 0000000..9f664d5
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/debug-macro.S
@@ -0,0 +1,36 @@
+/* arch/arm/mach-zynq/include/mach/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <mach/zynq_soc.h>
+#include <mach/uart.h>
+
+		.macro	addruart, rp, rv
+		ldr	\rp, =LL_UART_PADDR	@ physical
+		ldr	\rv, =LL_UART_VADDR	@ virtual
+		.endm
+
+		.macro	senduart,rd,rx
+		str	\rd, [\rx, #UART_FIFO_OFFSET]	@ TXDATA
+		.endm
+
+		.macro	waituart,rd,rx
+		.endm
+
+		.macro	busyuart,rd,rx
+1002:		ldr	\rd, [\rx, #UART_SR_OFFSET]	@ get status register
+		tst	\rd, #UART_SR_TXFULL		@
+		bne	1002b			@ wait if FIFO is full
+		.endm
diff --git a/arch/arm/mach-zynq/include/mach/entry-macro.S b/arch/arm/mach-zynq/include/mach/entry-macro.S
new file mode 100644
index 0000000..3cfc01b
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/entry-macro.S
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/mach-zynq/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * based on arch/plat-mxc/include/mach/entry-macro.S
+ *
+ *  Copyright (C) 2007 Lennert Buytenhek <buytenh@wantstofly.org>
+ *  Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <mach/hardware.h>
+#include <asm/hardware/entry-macro-gic.S>
+
+		.macro  disable_fiq
+		.endm
+
+		.macro  arch_ret_to_user, tmp1, tmp2
+		.endm
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/hardware.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/include/mach/hardware.h
index 66cd3f4..d558d8a 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/hardware.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/hardware.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,9 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
-
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+#ifndef __MACH_HARDWARE_H__
+#define __MACH_HARDWARE_H__
 
 #endif
diff --git a/arch/arm/mach-zynq/include/mach/io.h b/arch/arm/mach-zynq/include/mach/io.h
new file mode 100644
index 0000000..39d9885
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/io.h
@@ -0,0 +1,33 @@
+/* arch/arm/mach-zynq/include/mach/io.h
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_IO_H__
+#define __MACH_IO_H__
+
+/* Allow IO space to be anywhere in the memory */
+
+#define IO_SPACE_LIMIT 0xffff
+
+/* IO address mapping macros, nothing special at this time but required */
+
+#ifdef __ASSEMBLER__
+#define IOMEM(x)		(x)
+#else
+#define IOMEM(x)		((void __force __iomem *)(x))
+#endif
+
+#define __io(a)			__typesafe_io(a)
+#define __mem_pci(a)		(a)
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/irqs.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/include/mach/irqs.h
index 66cd3f4..5fb04fd 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/irqs.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/irqs.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,12 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_IRQS_H
+#define __MACH_IRQS_H
 
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+#define ARCH_NR_GPIOS	118
+#define NR_IRQS		(128 + ARCH_NR_GPIOS)
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/memory.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/include/mach/memory.h
index 66cd3f4..35a9263 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/memory.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/memory.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,13 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_MEMORY_H__
+#define __MACH_MEMORY_H__
 
-struct clk;
+#include <asm/sizes.h>
 
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+#define PLAT_PHYS_OFFSET	UL(0x0)
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/system.h
similarity index 61%
rename from arch/arm/mach-tegra/include/mach/clkdev.h
rename to arch/arm/mach-zynq/include/mach/system.h
index 66cd3f4..1b84d70 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/system.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/system.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,19 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_SYSTEM_H__
+#define __MACH_SYSTEM_H__
 
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
+static inline void arch_idle(void)
 {
-	return 1;
+	cpu_do_idle();
 }
 
-static inline void __clk_put(struct clk *clk)
+static inline void arch_reset(char mode, const char *cmd)
 {
+	/* Add architecture specific reset processing here */
 }
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/timex.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/include/mach/timex.h
index 66cd3f4..6c0245e 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/timex.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/timex.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,14 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_TIMEX_H__
+#define __MACH_TIMEX_H__
 
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+/* the following is needed for the system to build but will be removed
+   in the future, the value is not important but won't hurt
+*/
+#define CLOCK_TICK_RATE	(100 * HZ)
 
 #endif
diff --git a/arch/arm/mach-zynq/include/mach/uart.h b/arch/arm/mach-zynq/include/mach/uart.h
new file mode 100644
index 0000000..5c47c97
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/uart.h
@@ -0,0 +1,25 @@
+/* arch/arm/mach-zynq/include/mach/uart.h
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_UART_H__
+#define __MACH_UART_H__
+
+#define UART_CR_OFFSET		0x00  /* Control Register [8:0] */
+#define UART_SR_OFFSET		0x2C  /* Channel Status [11:0] */
+#define UART_FIFO_OFFSET	0x30  /* FIFO [15:0] or [7:0] */
+
+#define UART_SR_TXFULL		0x00000010	/* TX FIFO full */
+#define UART_SR_TXEMPTY		0x00000008	/* TX FIFO empty */
+
+#endif
diff --git a/arch/arm/mach-zynq/include/mach/uncompress.h b/arch/arm/mach-zynq/include/mach/uncompress.h
new file mode 100644
index 0000000..af4e844
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/uncompress.h
@@ -0,0 +1,51 @@
+/* arch/arm/mach-zynq/include/mach/uncompress.h
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_UNCOMPRESS_H__
+#define __MACH_UNCOMPRESS_H__
+
+#include <linux/io.h>
+#include <asm/processor.h>
+#include <mach/zynq_soc.h>
+#include <mach/uart.h>
+
+void arch_decomp_setup(void)
+{
+}
+
+static inline void flush(void)
+{
+	/*
+	 * Wait while the FIFO is not empty
+	 */
+	while (!(__raw_readl(IOMEM(LL_UART_PADDR + UART_SR_OFFSET)) &
+		UART_SR_TXEMPTY))
+		cpu_relax();
+}
+
+#define arch_decomp_wdog()
+
+static void putc(char ch)
+{
+	/*
+	 * Wait for room in the FIFO, then write the char into the FIFO
+	 */
+	while (__raw_readl(IOMEM(LL_UART_PADDR + UART_SR_OFFSET)) &
+		UART_SR_TXFULL)
+		cpu_relax();
+
+	__raw_writel(ch, IOMEM(LL_UART_PADDR + UART_FIFO_OFFSET));
+}
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-zynq/include/mach/vmalloc.h
similarity index 60%
copy from arch/arm/mach-tegra/include/mach/clkdev.h
copy to arch/arm/mach-zynq/include/mach/vmalloc.h
index 66cd3f4..2398eff 100644
--- a/arch/arm/mach-tegra/include/mach/clkdev.h
+++ b/arch/arm/mach-zynq/include/mach/vmalloc.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/clkdev.h
+/* arch/arm/mach-zynq/include/mach/vmalloc.h
  *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *	Colin Cross <ccross@google.com>
+ *  Copyright (C) 2011 Xilinx
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,21 +10,11 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
  */
 
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
+#ifndef __MACH_VMALLOC_H__
+#define __MACH_VMALLOC_H__
 
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
+#define VMALLOC_END       0xE0000000UL
 
 #endif
diff --git a/arch/arm/mach-zynq/include/mach/zynq_soc.h b/arch/arm/mach-zynq/include/mach/zynq_soc.h
new file mode 100644
index 0000000..d0d3f8f
--- /dev/null
+++ b/arch/arm/mach-zynq/include/mach/zynq_soc.h
@@ -0,0 +1,48 @@
+/* arch/arm/mach-zynq/include/mach/zynq_soc.h
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MACH_XILINX_SOC_H__
+#define __MACH_XILINX_SOC_H__
+
+#define PERIPHERAL_CLOCK_RATE		2500000
+
+/* For now, all mappings are flat (physical = virtual)
+ */
+#define UART0_PHYS			0xE0000000
+#define UART0_VIRT			UART0_PHYS
+
+#define TTC0_PHYS			0xF8001000
+#define TTC0_VIRT			TTC0_PHYS
+
+#define PL310_L2CC_PHYS			0xF8F02000
+#define PL310_L2CC_VIRT			PL310_L2CC_PHYS
+
+#define SCU_PERIPH_PHYS			0xF8F00000
+#define SCU_PERIPH_VIRT			SCU_PERIPH_PHYS
+
+/* The following are intended for the devices that are mapped early */
+
+#define TTC0_BASE			IOMEM(TTC0_VIRT)
+#define SCU_PERIPH_BASE			IOMEM(SCU_PERIPH_VIRT)
+#define SCU_GIC_CPU_BASE		(SCU_PERIPH_BASE + 0x100)
+#define SCU_GIC_DIST_BASE		(SCU_PERIPH_BASE + 0x1000)
+#define PL310_L2CC_BASE			IOMEM(PL310_L2CC_VIRT)
+
+/*
+ * Mandatory for CONFIG_LL_DEBUG, UART is mapped virtual = physical
+ */
+#define LL_UART_PADDR	UART0_PHYS
+#define LL_UART_VADDR	UART0_VIRT
+
+#endif
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c
new file mode 100644
index 0000000..c2c96cc
--- /dev/null
+++ b/arch/arm/mach-zynq/timer.c
@@ -0,0 +1,298 @@
+/*
+ * This file contains driver for the Xilinx PS Timer Counter IP.
+ *
+ *  Copyright (C) 2011 Xilinx
+ *
+ * based on arch/mips/kernel/time.c timer driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/io.h>
+
+#include <asm/mach/time.h>
+#include <mach/zynq_soc.h>
+#include "common.h"
+
+#define IRQ_TIMERCOUNTER0	42
+
+/*
+ * This driver configures the 2 16-bit count-up timers as follows:
+ *
+ * T1: Timer 1, clocksource for generic timekeeping
+ * T2: Timer 2, clockevent source for hrtimers
+ * T3: Timer 3, <unused>
+ *
+ * The input frequency to the timer module for emulation is 2.5MHz which is
+ * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
+ * the timers are clocked at 78.125KHz (12.8 us resolution).
+ *
+ * The input frequency to the timer module in silicon will be 200MHz. With the
+ * pre-scaler of 32, the timers are clocked at 6.25MHz (160ns resolution).
+ */
+#define XTTCPSS_CLOCKSOURCE	0	/* Timer 1 as a generic timekeeping */
+#define XTTCPSS_CLOCKEVENT	1	/* Timer 2 as a clock event */
+
+#define XTTCPSS_TIMER_BASE		TTC0_BASE
+#define XTTCPCC_EVENT_TIMER_IRQ		(IRQ_TIMERCOUNTER0 + 1)
+/*
+ * Timer Register Offset Definitions of Timer 1, Increment base address by 4
+ * and use same offsets for Timer 2
+ */
+#define XTTCPSS_CLK_CNTRL_OFFSET	0x00 /* Clock Control Reg, RW */
+#define XTTCPSS_CNT_CNTRL_OFFSET	0x0C /* Counter Control Reg, RW */
+#define XTTCPSS_COUNT_VAL_OFFSET	0x18 /* Counter Value Reg, RO */
+#define XTTCPSS_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
+#define XTTCPSS_MATCH_1_OFFSET		0x30 /* Match 1 Value Reg, RW */
+#define XTTCPSS_MATCH_2_OFFSET		0x3C /* Match 2 Value Reg, RW */
+#define XTTCPSS_MATCH_3_OFFSET		0x48 /* Match 3 Value Reg, RW */
+#define XTTCPSS_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
+#define XTTCPSS_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
+
+#define XTTCPSS_CNT_CNTRL_DISABLE_MASK	0x1
+
+/* Setup the timers to use pre-scaling */
+
+#define TIMER_RATE (PERIPHERAL_CLOCK_RATE / 32)
+
+/**
+ * struct xttcpss_timer - This definition defines local timer structure
+ *
+ * @base_addr:	Base address of timer
+ **/
+struct xttcpss_timer {
+	void __iomem *base_addr;
+};
+
+static struct xttcpss_timer timers[2];
+static struct clock_event_device xttcpss_clockevent;
+
+/**
+ * xttcpss_set_interval - Set the timer interval value
+ *
+ * @timer:	Pointer to the timer instance
+ * @cycles:	Timer interval ticks
+ **/
+static void xttcpss_set_interval(struct xttcpss_timer *timer,
+					unsigned long cycles)
+{
+	u32 ctrl_reg;
+
+	/* Disable the counter, set the counter value  and re-enable counter */
+	ctrl_reg = __raw_readl(timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
+	ctrl_reg |= XTTCPSS_CNT_CNTRL_DISABLE_MASK;
+	__raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
+
+	__raw_writel(cycles, timer->base_addr + XTTCPSS_INTR_VAL_OFFSET);
+
+	/* Reset the counter (0x10) so that it starts from 0, one-shot
+	   mode makes this needed for timing to be right. */
+	ctrl_reg |= 0x10;
+	ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK;
+	__raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
+}
+
+/**
+ * xttcpss_clock_event_interrupt - Clock event timer interrupt handler
+ *
+ * @irq:	IRQ number of the Timer
+ * @dev_id:	void pointer to the xttcpss_timer instance
+ *
+ * returns: Always IRQ_HANDLED - success
+ **/
+static irqreturn_t xttcpss_clock_event_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = &xttcpss_clockevent;
+	struct xttcpss_timer *timer = dev_id;
+
+	/* Acknowledge the interrupt and call event handler */
+	__raw_writel(__raw_readl(timer->base_addr + XTTCPSS_ISR_OFFSET),
+			timer->base_addr + XTTCPSS_ISR_OFFSET);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction event_timer_irq = {
+	.name	= "xttcpss clockevent",
+	.flags	= IRQF_DISABLED | IRQF_TIMER,
+	.handler = xttcpss_clock_event_interrupt,
+};
+
+/**
+ * xttcpss_timer_hardware_init - Initialize the timer hardware
+ *
+ * Initialize the hardware to start the clock source, get the clock
+ * event timer ready to use, and hook up the interrupt.
+ **/
+static void __init xttcpss_timer_hardware_init(void)
+{
+	/* Setup the clock source counter to be an incrementing counter
+	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
+	   it by 32 also. Let it start running now.
+	 */
+	timers[XTTCPSS_CLOCKSOURCE].base_addr = XTTCPSS_TIMER_BASE;
+
+	__raw_writel(0x0, timers[XTTCPSS_CLOCKSOURCE].base_addr +
+				XTTCPSS_IER_OFFSET);
+	__raw_writel(0x9, timers[XTTCPSS_CLOCKSOURCE].base_addr +
+				XTTCPSS_CLK_CNTRL_OFFSET);
+	__raw_writel(0x10, timers[XTTCPSS_CLOCKSOURCE].base_addr +
+				XTTCPSS_CNT_CNTRL_OFFSET);
+
+	/* Setup the clock event timer to be an interval timer which
+	 * is prescaled by 32 using the interval interrupt. Leave it
+	 * disabled for now.
+	 */
+
+	timers[XTTCPSS_CLOCKEVENT].base_addr = XTTCPSS_TIMER_BASE + 4;
+
+	__raw_writel(0x23, timers[XTTCPSS_CLOCKEVENT].base_addr +
+			XTTCPSS_CNT_CNTRL_OFFSET);
+	__raw_writel(0x9, timers[XTTCPSS_CLOCKEVENT].base_addr +
+			XTTCPSS_CLK_CNTRL_OFFSET);
+	__raw_writel(0x1, timers[XTTCPSS_CLOCKEVENT].base_addr +
+			XTTCPSS_IER_OFFSET);
+
+	/* Setup IRQ the clock event timer */
+	event_timer_irq.dev_id = &timers[XTTCPSS_CLOCKEVENT];
+	setup_irq(XTTCPCC_EVENT_TIMER_IRQ, &event_timer_irq);
+}
+
+/**
+ * __raw_readl_cycles - Reads the timer counter register
+ *
+ * returns: Current timer counter register value
+ **/
+static cycle_t __raw_readl_cycles(struct clocksource *cs)
+{
+	struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKSOURCE];
+
+	return (cycle_t)__raw_readl(timer->base_addr +
+				XTTCPSS_COUNT_VAL_OFFSET);
+}
+
+
+/*
+ * Instantiate and initialize the clock source structure
+ */
+static struct clocksource clocksource_xttcpss = {
+	.name		= "xttcpss_timer1",
+	.rating		= 200,			/* Reasonable clock source */
+	.read		= __raw_readl_cycles,
+	.mask		= CLOCKSOURCE_MASK(16),
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+
+/**
+ * xttcpss_set_next_event - Sets the time interval for next event
+ *
+ * @cycles:	Timer interval ticks
+ * @evt:	Address of clock event instance
+ *
+ * returns: Always 0 - success
+ **/
+static int xttcpss_set_next_event(unsigned long cycles,
+					struct clock_event_device *evt)
+{
+	struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT];
+
+	xttcpss_set_interval(timer, cycles);
+	return 0;
+}
+
+/**
+ * xttcpss_set_mode - Sets the mode of timer
+ *
+ * @mode:	Mode to be set
+ * @evt:	Address of clock event instance
+ **/
+static void xttcpss_set_mode(enum clock_event_mode mode,
+					struct clock_event_device *evt)
+{
+	struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT];
+	u32 ctrl_reg;
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		xttcpss_set_interval(timer, TIMER_RATE / HZ);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		ctrl_reg = __raw_readl(timer->base_addr +
+					XTTCPSS_CNT_CNTRL_OFFSET);
+		ctrl_reg |= XTTCPSS_CNT_CNTRL_DISABLE_MASK;
+		__raw_writel(ctrl_reg,
+				timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		ctrl_reg = __raw_readl(timer->base_addr +
+					XTTCPSS_CNT_CNTRL_OFFSET);
+		ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK;
+		__raw_writel(ctrl_reg,
+				timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET);
+		break;
+	}
+}
+
+/*
+ * Instantiate and initialize the clock event structure
+ */
+static struct clock_event_device xttcpss_clockevent = {
+	.name		= "xttcpss_timer2",
+	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.set_next_event	= xttcpss_set_next_event,
+	.set_mode	= xttcpss_set_mode,
+	.rating		= 200,
+};
+
+/**
+ * xttcpss_timer_init - Initialize the timer
+ *
+ * Initializes the timer hardware and register the clock source and clock event
+ * timers with Linux kernal timer framework
+ **/
+static void __init xttcpss_timer_init(void)
+{
+	xttcpss_timer_hardware_init();
+	clocksource_register_hz(&clocksource_xttcpss, TIMER_RATE);
+
+	/* Calculate the parameters to allow the clockevent to operate using
+	   integer math
+	*/
+	clockevents_calc_mult_shift(&xttcpss_clockevent, TIMER_RATE, 4);
+
+	xttcpss_clockevent.max_delta_ns =
+		clockevent_delta2ns(0xfffe, &xttcpss_clockevent);
+	xttcpss_clockevent.min_delta_ns =
+		clockevent_delta2ns(1, &xttcpss_clockevent);
+
+	/* Indicate that clock event is on 1st CPU as SMP boot needs it */
+
+	xttcpss_clockevent.cpumask = cpumask_of(0);
+	clockevents_register_device(&xttcpss_clockevent);
+}
+
+/*
+ * Instantiate and initialize the system timer structure
+ */
+struct sys_timer xttcpss_sys_timer = {
+	.init		= xttcpss_timer_init,
+};
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 0074b8d..88633fe 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -821,7 +821,8 @@
 	depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || \
 		   REALVIEW_EB_A9MP || SOC_IMX35 || SOC_IMX31 || MACH_REALVIEW_PBX || \
 		   ARCH_NOMADIK || ARCH_OMAP4 || ARCH_EXYNOS4 || ARCH_TEGRA || \
-		   ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE
+		   ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \
+		   ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX
 	default y
 	select OUTER_CACHE
 	select OUTER_CACHE_SYNC
diff --git a/arch/arm/mm/iomap.c b/arch/arm/mm/iomap.c
index ffad039..430df1a 100644
--- a/arch/arm/mm/iomap.c
+++ b/arch/arm/mm/iomap.c
@@ -9,6 +9,9 @@
 #include <linux/ioport.h>
 #include <linux/io.h>
 
+unsigned long vga_base;
+EXPORT_SYMBOL(vga_base);
+
 #ifdef __io
 void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
@@ -23,6 +26,15 @@
 #endif
 
 #ifdef CONFIG_PCI
+unsigned long pcibios_min_io = 0x1000;
+EXPORT_SYMBOL(pcibios_min_io);
+
+unsigned long pcibios_min_mem = 0x01000000;
+EXPORT_SYMBOL(pcibios_min_mem);
+
+unsigned int pci_flags = PCI_REASSIGN_ALL_RSRC;
+EXPORT_SYMBOL(pci_flags);
+
 void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
 {
 	resource_size_t start = pci_resource_start(dev, bar);
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index 64f1fc7..28c72a2 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -28,7 +28,6 @@
 #include <linux/init.h>
 #include <asm/assembler.h>
 #include <asm/hwcap.h>
-#include <mach/hardware.h>
 #include <asm/pgtable.h>
 #include <asm/pgtable-hwdef.h>
 #include <asm/page.h>
diff --git a/arch/arm/plat-iop/pci.c b/arch/arm/plat-iop/pci.c
index 43f2b15..845549c 100644
--- a/arch/arm/plat-iop/pci.c
+++ b/arch/arm/plat-iop/pci.c
@@ -374,6 +374,9 @@
 
 void __init iop3xx_pci_preinit(void)
 {
+	pcibios_min_io = 0;
+	pcibios_min_mem = 0;
+
 	iop3xx_atu_disable();
 	iop3xx_atu_setup();
 	iop3xx_atu_debug();
diff --git a/arch/arm/plat-mxc/devices.c b/arch/arm/plat-mxc/devices.c
index fb166b2..0d6ed31 100644
--- a/arch/arm/plat-mxc/devices.c
+++ b/arch/arm/plat-mxc/devices.c
@@ -95,8 +95,22 @@
 	.parent		= &platform_bus,
 };
 
+struct device mxc_ahb_bus = {
+	.init_name	= "mxc_ahb",
+	.parent		= &platform_bus,
+};
+
 static int __init mxc_device_init(void)
 {
-	return device_register(&mxc_aips_bus);
+	int ret;
+
+	ret = device_register(&mxc_aips_bus);
+	if (IS_ERR_VALUE(ret))
+		goto done;
+
+	ret = device_register(&mxc_ahb_bus);
+
+done:
+	return ret;
 }
 core_initcall(mxc_device_init);
diff --git a/arch/arm/plat-mxc/devices/platform-fec.c b/arch/arm/plat-mxc/devices/platform-fec.c
index 4fc6ffc..0bae44e 100644
--- a/arch/arm/plat-mxc/devices/platform-fec.c
+++ b/arch/arm/plat-mxc/devices/platform-fec.c
@@ -11,40 +11,45 @@
 #include <mach/hardware.h>
 #include <mach/devices-common.h>
 
-#define imx_fec_data_entry_single(soc)					\
+#define imx_fec_data_entry_single(soc, _devid)				\
 	{								\
+		.devid = _devid,					\
 		.iobase = soc ## _FEC_BASE_ADDR,			\
 		.irq = soc ## _INT_FEC,					\
 	}
 
 #ifdef CONFIG_SOC_IMX25
 const struct imx_fec_data imx25_fec_data __initconst =
-	imx_fec_data_entry_single(MX25);
+	imx_fec_data_entry_single(MX25, "imx25-fec");
 #endif /* ifdef CONFIG_SOC_IMX25 */
 
 #ifdef CONFIG_SOC_IMX27
 const struct imx_fec_data imx27_fec_data __initconst =
-	imx_fec_data_entry_single(MX27);
+	imx_fec_data_entry_single(MX27, "imx27-fec");
 #endif /* ifdef CONFIG_SOC_IMX27 */
 
 #ifdef CONFIG_SOC_IMX35
+/* i.mx35 has the i.mx27 type fec */
 const struct imx_fec_data imx35_fec_data __initconst =
-	imx_fec_data_entry_single(MX35);
+	imx_fec_data_entry_single(MX35, "imx27-fec");
 #endif
 
 #ifdef CONFIG_SOC_IMX50
+/* i.mx50 has the i.mx25 type fec */
 const struct imx_fec_data imx50_fec_data __initconst =
-	imx_fec_data_entry_single(MX50);
+	imx_fec_data_entry_single(MX50, "imx25-fec");
 #endif
 
 #ifdef CONFIG_SOC_IMX51
+/* i.mx51 has the i.mx27 type fec */
 const struct imx_fec_data imx51_fec_data __initconst =
-	imx_fec_data_entry_single(MX51);
+	imx_fec_data_entry_single(MX51, "imx27-fec");
 #endif
 
 #ifdef CONFIG_SOC_IMX53
+/* i.mx53 has the i.mx25 type fec */
 const struct imx_fec_data imx53_fec_data __initconst =
-	imx_fec_data_entry_single(MX53);
+	imx_fec_data_entry_single(MX53, "imx25-fec");
 #endif
 
 struct platform_device *__init imx_add_fec(
@@ -63,7 +68,7 @@
 		},
 	};
 
-	return imx_add_platform_device_dmamask("fec", 0,
+	return imx_add_platform_device_dmamask(data->devid, 0,
 			res, ARRAY_SIZE(res),
 			pdata, sizeof(*pdata), DMA_BIT_MASK(32));
 }
diff --git a/arch/arm/plat-mxc/devices/platform-imx-dma.c b/arch/arm/plat-mxc/devices/platform-imx-dma.c
index c64f015..7fa7e9c 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-dma.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-dma.c
@@ -6,207 +6,29 @@
  * the terms of the GNU General Public License version 2 as published by the
  * Free Software Foundation.
  */
-#include <linux/compiler.h>
-#include <linux/err.h>
-#include <linux/init.h>
-
-#include <mach/hardware.h>
 #include <mach/devices-common.h>
-#include <mach/sdma.h>
 
-struct imx_imx_sdma_data {
-	resource_size_t iobase;
-	resource_size_t irq;
-	struct sdma_platform_data pdata;
-};
+struct platform_device __init __maybe_unused *imx_add_imx_dma(void)
+{
+	return platform_device_register_resndata(&mxc_ahb_bus,
+			"imx-dma", -1, NULL, 0, NULL, 0);
+}
 
-#define imx_imx_sdma_data_entry_single(soc, _sdma_version, _cpu_name, _to_version)\
-	{								\
-		.iobase = soc ## _SDMA ## _BASE_ADDR,			\
-		.irq = soc ## _INT_SDMA,				\
-		.pdata = {						\
-			.sdma_version = _sdma_version,			\
-			.cpu_name = _cpu_name,				\
-			.to_version = _to_version,			\
-		},							\
-	}
-
-#ifdef CONFIG_SOC_IMX25
-struct imx_imx_sdma_data imx25_imx_sdma_data __initconst =
-	imx_imx_sdma_data_entry_single(MX25, 2, "imx25", 1);
-#endif /* ifdef CONFIG_SOC_IMX25 */
-
-#ifdef CONFIG_SOC_IMX31
-struct imx_imx_sdma_data imx31_imx_sdma_data __initdata =
-	imx_imx_sdma_data_entry_single(MX31, 1, "imx31", 1);
-#endif /* ifdef CONFIG_SOC_IMX31 */
-
-#ifdef CONFIG_SOC_IMX35
-struct imx_imx_sdma_data imx35_imx_sdma_data __initdata =
-	imx_imx_sdma_data_entry_single(MX35, 2, "imx35", 1);
-#endif /* ifdef CONFIG_SOC_IMX35 */
-
-#ifdef CONFIG_SOC_IMX51
-struct imx_imx_sdma_data imx51_imx_sdma_data __initconst =
-	imx_imx_sdma_data_entry_single(MX51, 2, "imx51", 1);
-#endif /* ifdef CONFIG_SOC_IMX51 */
-
-static struct platform_device __init __maybe_unused *imx_add_imx_sdma(
-		const struct imx_imx_sdma_data *data)
+struct platform_device __init __maybe_unused *imx_add_imx_sdma(char *name,
+	resource_size_t iobase, int irq, struct sdma_platform_data *pdata)
 {
 	struct resource res[] = {
 		{
-			.start = data->iobase,
-			.end = data->iobase + SZ_16K - 1,
+			.start = iobase,
+			.end = iobase + SZ_16K - 1,
 			.flags = IORESOURCE_MEM,
 		}, {
-			.start = data->irq,
-			.end = data->irq,
+			.start = irq,
+			.end = irq,
 			.flags = IORESOURCE_IRQ,
 		},
 	};
 
-	return imx_add_platform_device("imx-sdma", -1,
-			res, ARRAY_SIZE(res),
-			&data->pdata, sizeof(data->pdata));
+	return platform_device_register_resndata(&mxc_ahb_bus, name,
+			-1, res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
 }
-
-static struct platform_device __init __maybe_unused *imx_add_imx_dma(void)
-{
-	return imx_add_platform_device("imx-dma", -1, NULL, 0, NULL, 0);
-}
-
-#ifdef CONFIG_ARCH_MX25
-static struct sdma_script_start_addrs addr_imx25 = {
-	.ap_2_ap_addr = 729,
-	.uart_2_mcu_addr = 904,
-	.per_2_app_addr = 1255,
-	.mcu_2_app_addr = 834,
-	.uartsh_2_mcu_addr = 1120,
-	.per_2_shp_addr = 1329,
-	.mcu_2_shp_addr = 1048,
-	.ata_2_mcu_addr = 1560,
-	.mcu_2_ata_addr = 1479,
-	.app_2_per_addr = 1189,
-	.app_2_mcu_addr = 770,
-	.shp_2_per_addr = 1407,
-	.shp_2_mcu_addr = 979,
-};
-#endif
-
-#ifdef CONFIG_SOC_IMX31
-static struct sdma_script_start_addrs addr_imx31_to1 = {
-	.per_2_per_addr = 1677,
-};
-
-static struct sdma_script_start_addrs addr_imx31_to2 = {
-	.ap_2_ap_addr = 423,
-	.ap_2_bp_addr = 829,
-	.bp_2_ap_addr = 1029,
-};
-#endif
-
-#ifdef CONFIG_SOC_IMX35
-static struct sdma_script_start_addrs addr_imx35_to1 = {
-	.ap_2_ap_addr = 642,
-	.uart_2_mcu_addr = 817,
-	.mcu_2_app_addr = 747,
-	.uartsh_2_mcu_addr = 1183,
-	.per_2_shp_addr = 1033,
-	.mcu_2_shp_addr = 961,
-	.ata_2_mcu_addr = 1333,
-	.mcu_2_ata_addr = 1252,
-	.app_2_mcu_addr = 683,
-	.shp_2_per_addr = 1111,
-	.shp_2_mcu_addr = 892,
-};
-
-static struct sdma_script_start_addrs addr_imx35_to2 = {
-	.ap_2_ap_addr = 729,
-	.uart_2_mcu_addr = 904,
-	.per_2_app_addr = 1597,
-	.mcu_2_app_addr = 834,
-	.uartsh_2_mcu_addr = 1270,
-	.per_2_shp_addr = 1120,
-	.mcu_2_shp_addr = 1048,
-	.ata_2_mcu_addr = 1429,
-	.mcu_2_ata_addr = 1339,
-	.app_2_per_addr = 1531,
-	.app_2_mcu_addr = 770,
-	.shp_2_per_addr = 1198,
-	.shp_2_mcu_addr = 979,
-};
-#endif
-
-#ifdef CONFIG_SOC_IMX51
-static struct sdma_script_start_addrs addr_imx51 = {
-	.ap_2_ap_addr = 642,
-	.uart_2_mcu_addr = 817,
-	.mcu_2_app_addr = 747,
-	.mcu_2_shp_addr = 961,
-	.ata_2_mcu_addr = 1473,
-	.mcu_2_ata_addr = 1392,
-	.app_2_per_addr = 1033,
-	.app_2_mcu_addr = 683,
-	.shp_2_per_addr = 1251,
-	.shp_2_mcu_addr = 892,
-};
-#endif
-
-static int __init imxXX_add_imx_dma(void)
-{
-	struct platform_device *ret;
-
-#if defined(CONFIG_SOC_IMX21) || defined(CONFIG_SOC_IMX27)
-	if (cpu_is_mx21() || cpu_is_mx27())
-		ret = imx_add_imx_dma();
-	else
-#endif
-
-#if defined(CONFIG_SOC_IMX25)
-	if (cpu_is_mx25()) {
-		imx25_imx_sdma_data.pdata.script_addrs = &addr_imx25;
-		ret = imx_add_imx_sdma(&imx25_imx_sdma_data);
-	} else
-#endif
-
-#if defined(CONFIG_SOC_IMX31)
-	if (cpu_is_mx31()) {
-		int to_version = mx31_revision() >> 4;
-		imx31_imx_sdma_data.pdata.to_version = to_version;
-		if (to_version == 1)
-			imx31_imx_sdma_data.pdata.script_addrs = &addr_imx31_to1;
-		else
-			imx31_imx_sdma_data.pdata.script_addrs = &addr_imx31_to2;
-		ret = imx_add_imx_sdma(&imx31_imx_sdma_data);
-	} else
-#endif
-
-#if defined(CONFIG_SOC_IMX35)
-	if (cpu_is_mx35()) {
-		int to_version = mx35_revision() >> 4;
-		imx35_imx_sdma_data.pdata.to_version = to_version;
-		if (to_version == 1)
-			imx35_imx_sdma_data.pdata.script_addrs = &addr_imx35_to1;
-		else
-			imx35_imx_sdma_data.pdata.script_addrs = &addr_imx35_to2;
-		ret = imx_add_imx_sdma(&imx35_imx_sdma_data);
-	} else
-#endif
-
-#if defined(CONFIG_SOC_IMX51)
-	if (cpu_is_mx51()) {
-		int to_version = mx51_revision() >> 4;
-		imx51_imx_sdma_data.pdata.to_version = to_version;
-		imx51_imx_sdma_data.pdata.script_addrs = &addr_imx51;
-		ret = imx_add_imx_sdma(&imx51_imx_sdma_data);
-	} else
-#endif
-		ret = ERR_PTR(-ENODEV);
-
-	if (IS_ERR(ret))
-		return PTR_ERR(ret);
-
-	return 0;
-}
-arch_initcall(imxXX_add_imx_dma);
diff --git a/arch/arm/plat-mxc/devices/platform-imx-i2c.c b/arch/arm/plat-mxc/devices/platform-imx-i2c.c
index 2ab74f0..afe60f7 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-i2c.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-i2c.c
@@ -94,8 +94,9 @@
 	imx_imx_i2c_data_entry(MX53, _id, _hwid, SZ_4K)
 	imx53_imx_i2c_data_entry(0, 1),
 	imx53_imx_i2c_data_entry(1, 2),
+	imx53_imx_i2c_data_entry(2, 3),
 };
-#endif /* ifdef CONFIG_SOC_IMX51 */
+#endif /* ifdef CONFIG_SOC_IMX53 */
 
 struct platform_device *__init imx_add_imx_i2c(
 		const struct imx_imx_i2c_data *data,
diff --git a/arch/arm/plat-mxc/devices/platform-imx-keypad.c b/arch/arm/plat-mxc/devices/platform-imx-keypad.c
index 2636611..479c3e9 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-keypad.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-keypad.c
@@ -46,6 +46,11 @@
 	imx_imx_keypad_data_entry_single(MX51, SZ_16);
 #endif /* ifdef CONFIG_SOC_IMX51 */
 
+#ifdef CONFIG_SOC_IMX53
+const struct imx_imx_keypad_data imx53_imx_keypad_data __initconst =
+	imx_imx_keypad_data_entry_single(MX53, SZ_16);
+#endif /* ifdef CONFIG_SOC_IMX53 */
+
 struct platform_device *__init imx_add_imx_keypad(
 		const struct imx_imx_keypad_data *data,
 		const struct matrix_keymap_data *pdata)
diff --git a/arch/arm/plat-mxc/devices/platform-imx-ssi.c b/arch/arm/plat-mxc/devices/platform-imx-ssi.c
index 66b8593..21c6f30 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-ssi.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-ssi.c
@@ -76,6 +76,16 @@
 };
 #endif /* ifdef CONFIG_SOC_IMX51 */
 
+#ifdef CONFIG_SOC_IMX53
+const struct imx_imx_ssi_data imx53_imx_ssi_data[] __initconst = {
+#define imx53_imx_ssi_data_entry(_id, _hwid)				\
+	imx_imx_ssi_data_entry(MX53, _id, _hwid, SZ_16K)
+	imx53_imx_ssi_data_entry(0, 1),
+	imx53_imx_ssi_data_entry(1, 2),
+	imx53_imx_ssi_data_entry(2, 3),
+};
+#endif /* ifdef CONFIG_SOC_IMX53 */
+
 struct platform_device *__init imx_add_imx_ssi(
 		const struct imx_imx_ssi_data *data,
 		const struct imx_ssi_platform_data *pdata)
diff --git a/arch/arm/plat-mxc/devices/platform-imx-uart.c b/arch/arm/plat-mxc/devices/platform-imx-uart.c
index 3c854c2..2020d84 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-uart.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-uart.c
@@ -123,6 +123,8 @@
 	imx53_imx_uart_data_entry(0, 1),
 	imx53_imx_uart_data_entry(1, 2),
 	imx53_imx_uart_data_entry(2, 3),
+	imx53_imx_uart_data_entry(3, 4),
+	imx53_imx_uart_data_entry(4, 5),
 };
 #endif /* ifdef CONFIG_SOC_IMX53 */
 
@@ -150,7 +152,7 @@
 		},
 	};
 
-	return imx_add_platform_device("imx-uart", data->id, res,
+	return imx_add_platform_device("imx1-uart", data->id, res,
 			ARRAY_SIZE(res), pdata, sizeof(*pdata));
 }
 
@@ -170,6 +172,7 @@
 		},
 	};
 
-	return imx_add_platform_device("imx-uart", data->id, res, ARRAY_SIZE(res),
-			pdata, sizeof(*pdata));
+	/* i.mx21 type uart runs on all i.mx except i.mx1 */
+	return imx_add_platform_device("imx21-uart", data->id,
+			res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
 }
diff --git a/arch/arm/plat-mxc/devices/platform-sdhci-esdhc-imx.c b/arch/arm/plat-mxc/devices/platform-sdhci-esdhc-imx.c
index 6b2940b..5955f5d 100644
--- a/arch/arm/plat-mxc/devices/platform-sdhci-esdhc-imx.c
+++ b/arch/arm/plat-mxc/devices/platform-sdhci-esdhc-imx.c
@@ -10,21 +10,22 @@
 #include <mach/devices-common.h>
 #include <mach/esdhc.h>
 
-#define imx_sdhci_esdhc_imx_data_entry_single(soc, _id, hwid) \
+#define imx_sdhci_esdhc_imx_data_entry_single(soc, _devid, _id, hwid) \
 	{								\
+		.devid = _devid,					\
 		.id = _id,						\
 		.iobase = soc ## _ESDHC ## hwid ## _BASE_ADDR,	\
 		.irq = soc ## _INT_ESDHC ## hwid,			\
 	}
 
-#define imx_sdhci_esdhc_imx_data_entry(soc, id, hwid)	\
-	[id] = imx_sdhci_esdhc_imx_data_entry_single(soc, id, hwid)
+#define imx_sdhci_esdhc_imx_data_entry(soc, devid, id, hwid)	\
+	[id] = imx_sdhci_esdhc_imx_data_entry_single(soc, devid, id, hwid)
 
 #ifdef CONFIG_SOC_IMX25
 const struct imx_sdhci_esdhc_imx_data
 imx25_sdhci_esdhc_imx_data[] __initconst = {
 #define imx25_sdhci_esdhc_imx_data_entry(_id, _hwid)			\
-	imx_sdhci_esdhc_imx_data_entry(MX25, _id, _hwid)
+	imx_sdhci_esdhc_imx_data_entry(MX25, "sdhci-esdhc-imx25", _id, _hwid)
 	imx25_sdhci_esdhc_imx_data_entry(0, 1),
 	imx25_sdhci_esdhc_imx_data_entry(1, 2),
 };
@@ -34,7 +35,7 @@
 const struct imx_sdhci_esdhc_imx_data
 imx35_sdhci_esdhc_imx_data[] __initconst = {
 #define imx35_sdhci_esdhc_imx_data_entry(_id, _hwid)			\
-	imx_sdhci_esdhc_imx_data_entry(MX35, _id, _hwid)
+	imx_sdhci_esdhc_imx_data_entry(MX35, "sdhci-esdhc-imx35", _id, _hwid)
 	imx35_sdhci_esdhc_imx_data_entry(0, 1),
 	imx35_sdhci_esdhc_imx_data_entry(1, 2),
 	imx35_sdhci_esdhc_imx_data_entry(2, 3),
@@ -45,7 +46,7 @@
 const struct imx_sdhci_esdhc_imx_data
 imx51_sdhci_esdhc_imx_data[] __initconst = {
 #define imx51_sdhci_esdhc_imx_data_entry(_id, _hwid)			\
-	imx_sdhci_esdhc_imx_data_entry(MX51, _id, _hwid)
+	imx_sdhci_esdhc_imx_data_entry(MX51, "sdhci-esdhc-imx51", _id, _hwid)
 	imx51_sdhci_esdhc_imx_data_entry(0, 1),
 	imx51_sdhci_esdhc_imx_data_entry(1, 2),
 	imx51_sdhci_esdhc_imx_data_entry(2, 3),
@@ -57,7 +58,7 @@
 const struct imx_sdhci_esdhc_imx_data
 imx53_sdhci_esdhc_imx_data[] __initconst = {
 #define imx53_sdhci_esdhc_imx_data_entry(_id, _hwid)			\
-	imx_sdhci_esdhc_imx_data_entry(MX53, _id, _hwid)
+	imx_sdhci_esdhc_imx_data_entry(MX53, "sdhci-esdhc-imx53", _id, _hwid)
 	imx53_sdhci_esdhc_imx_data_entry(0, 1),
 	imx53_sdhci_esdhc_imx_data_entry(1, 2),
 	imx53_sdhci_esdhc_imx_data_entry(2, 3),
@@ -65,6 +66,11 @@
 };
 #endif /* ifdef CONFIG_SOC_IMX53 */
 
+static const struct esdhc_platform_data default_esdhc_pdata __initconst = {
+	.wp_type = ESDHC_WP_NONE,
+	.cd_type = ESDHC_CD_NONE,
+};
+
 struct platform_device *__init imx_add_sdhci_esdhc_imx(
 		const struct imx_sdhci_esdhc_imx_data *data,
 		const struct esdhc_platform_data *pdata)
@@ -81,6 +87,13 @@
 		},
 	};
 
-	return imx_add_platform_device("sdhci-esdhc-imx", data->id, res,
+	/*
+	 * If machine does not provide pdata, use the default one
+	 * which means no WP/CD support
+	 */
+	if (!pdata)
+		pdata = &default_esdhc_pdata;
+
+	return imx_add_platform_device(data->devid, data->id, res,
 			ARRAY_SIZE(res), pdata, sizeof(*pdata));
 }
diff --git a/arch/arm/plat-mxc/include/mach/clkdev.h b/arch/arm/plat-mxc/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/plat-mxc/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index 03f6266..524538a 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -9,8 +9,10 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/init.h>
+#include <mach/sdma.h>
 
 extern struct device mxc_aips_bus;
+extern struct device mxc_ahb_bus;
 
 struct platform_device *imx_add_platform_device_dmamask(
 		const char *name, int id,
@@ -28,6 +30,7 @@
 
 #include <linux/fec.h>
 struct imx_fec_data {
+	const char *devid;
 	resource_size_t iobase;
 	resource_size_t irq;
 };
@@ -274,6 +277,7 @@
 
 #include <mach/esdhc.h>
 struct imx_sdhci_esdhc_imx_data {
+	const char *devid;
 	int id;
 	resource_size_t iobase;
 	resource_size_t irq;
@@ -293,3 +297,7 @@
 struct platform_device *__init imx_add_spi_imx(
 		const struct imx_spi_imx_data *data,
 		const struct spi_imx_master *pdata);
+
+struct platform_device *imx_add_imx_dma(void);
+struct platform_device *imx_add_imx_sdma(char *name,
+	resource_size_t iobase, int irq, struct sdma_platform_data *pdata);
diff --git a/arch/arm/plat-mxc/include/mach/dma.h b/arch/arm/plat-mxc/include/mach/dma.h
index ef77515..233d0a5 100644
--- a/arch/arm/plat-mxc/include/mach/dma.h
+++ b/arch/arm/plat-mxc/include/mach/dma.h
@@ -60,7 +60,8 @@
 
 static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
 {
-	return !strcmp(dev_name(chan->device->dev), "imx-sdma") ||
+	return !strcmp(dev_name(chan->device->dev), "imx31-sdma") ||
+		!strcmp(dev_name(chan->device->dev), "imx35-sdma") ||
 		!strcmp(dev_name(chan->device->dev), "imx-dma");
 }
 
diff --git a/arch/arm/plat-mxc/include/mach/esdhc.h b/arch/arm/plat-mxc/include/mach/esdhc.h
index 86003f4..aaf9748 100644
--- a/arch/arm/plat-mxc/include/mach/esdhc.h
+++ b/arch/arm/plat-mxc/include/mach/esdhc.h
@@ -10,17 +10,34 @@
 #ifndef __ASM_ARCH_IMX_ESDHC_H
 #define __ASM_ARCH_IMX_ESDHC_H
 
+enum wp_types {
+	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
+	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
+	ESDHC_WP_GPIO,		/* external gpio pin for WP */
+};
+
+enum cd_types {
+	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
+	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
+	ESDHC_CD_GPIO,		/* external gpio pin for CD */
+	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
+};
+
 /**
- * struct esdhc_platform_data - optional platform data for esdhc on i.MX
+ * struct esdhc_platform_data - platform data for esdhc on i.MX
  *
- * strongly recommended for i.MX25/35, not needed for other variants
+ * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
  *
- * @wp_gpio:	gpio for write_protect (-EINVAL if unused)
- * @cd_gpio:	gpio for card_detect interrupt (-EINVAL if unused)
+ * @wp_gpio:	gpio for write_protect
+ * @cd_gpio:	gpio for card_detect interrupt
+ * @wp_type:	type of write_protect method (see wp_types enum above)
+ * @cd_type:	type of card_detect method (see cd_types enum above)
  */
 
 struct esdhc_platform_data {
 	unsigned int wp_gpio;
 	unsigned int cd_gpio;
+	enum wp_types wp_type;
+	enum cd_types cd_type;
 };
 #endif /* __ASM_ARCH_IMX_ESDHC_H */
diff --git a/arch/arm/plat-mxc/include/mach/mx53.h b/arch/arm/plat-mxc/include/mach/mx53.h
index 74cd093..5e3c323 100644
--- a/arch/arm/plat-mxc/include/mach/mx53.h
+++ b/arch/arm/plat-mxc/include/mach/mx53.h
@@ -176,10 +176,10 @@
 /*
  * DMA request assignments
  */
-#define MX53_DMA_REQ_SSI3_TX1		47
-#define MX53_DMA_REQ_SSI3_RX1		46
-#define MX53_DMA_REQ_SSI3_TX2		45
-#define MX53_DMA_REQ_SSI3_RX2		44
+#define MX53_DMA_REQ_SSI3_TX0		47
+#define MX53_DMA_REQ_SSI3_RX0		46
+#define MX53_DMA_REQ_SSI3_TX1		45
+#define MX53_DMA_REQ_SSI3_RX1		44
 #define MX53_DMA_REQ_UART3_TX	43
 #define MX53_DMA_REQ_UART3_RX	42
 #define MX53_DMA_REQ_ESAI_TX		41
@@ -194,14 +194,14 @@
 #define MX53_DMA_REQ_ASRC_DMA1	32
 #define MX53_DMA_REQ_EMI_WR		31
 #define MX53_DMA_REQ_EMI_RD		30
-#define MX53_DMA_REQ_SSI1_TX1		29
-#define MX53_DMA_REQ_SSI1_RX1		28
-#define MX53_DMA_REQ_SSI1_TX2		27
-#define MX53_DMA_REQ_SSI1_RX2		26
-#define MX53_DMA_REQ_SSI2_TX1		25
-#define MX53_DMA_REQ_SSI2_RX1		24
-#define MX53_DMA_REQ_SSI2_TX2		23
-#define MX53_DMA_REQ_SSI2_RX2		22
+#define MX53_DMA_REQ_SSI1_TX0		29
+#define MX53_DMA_REQ_SSI1_RX0		28
+#define MX53_DMA_REQ_SSI1_TX1		27
+#define MX53_DMA_REQ_SSI1_RX1		26
+#define MX53_DMA_REQ_SSI2_TX0		25
+#define MX53_DMA_REQ_SSI2_RX0		24
+#define MX53_DMA_REQ_SSI2_TX1		23
+#define MX53_DMA_REQ_SSI2_RX1		22
 #define MX53_DMA_REQ_I2C2_SDHC2	21
 #define MX53_DMA_REQ_I2C1_SDHC1	20
 #define MX53_DMA_REQ_UART1_TX	19
@@ -241,7 +241,7 @@
 #define MX53_INT_IPU_ERR	10
 #define MX53_INT_IPU_SYN	11
 #define MX53_INT_GPU	12
-#define MX53_INT_RESV13	13
+#define MX53_INT_UART4	13
 #define MX53_INT_USB_H1	14
 #define MX53_INT_EMI	15
 #define MX53_INT_USB_H2	16
@@ -314,7 +314,7 @@
 #define MX53_INT_CAN2	83
 #define MX53_INT_GPU2_IRQ	84
 #define MX53_INT_GPU2_BUSY	85
-#define MX53_INT_RESV86	86
+#define MX53_INT_UART5	86
 #define MX53_INT_FEC	87
 #define MX53_INT_OWIRE	88
 #define MX53_INT_CTI1_TG2	89
diff --git a/arch/arm/plat-mxc/include/mach/sdma.h b/arch/arm/plat-mxc/include/mach/sdma.h
index 913e043..3a39428 100644
--- a/arch/arm/plat-mxc/include/mach/sdma.h
+++ b/arch/arm/plat-mxc/include/mach/sdma.h
@@ -48,15 +48,11 @@
 /**
  * struct sdma_platform_data - platform specific data for SDMA engine
  *
- * @sdma_version	The version of this SDMA engine
- * @cpu_name		used to generate the firmware name
- * @to_version		CPU Tape out version
+ * @fw_name		The firmware name
  * @script_addrs	SDMA scripts addresses in SDMA ROM
  */
 struct sdma_platform_data {
-	int sdma_version;
-	char *cpu_name;
-	int to_version;
+	char *fw_name;
 	struct sdma_script_start_addrs *script_addrs;
 };
 
diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h
index d85e2d1..88fd404 100644
--- a/arch/arm/plat-mxc/include/mach/uncompress.h
+++ b/arch/arm/plat-mxc/include/mach/uncompress.h
@@ -117,6 +117,7 @@
 	case MACH_TYPE_MX53_EVK:
 	case MACH_TYPE_MX53_LOCO:
 	case MACH_TYPE_MX53_SMD:
+	case MACH_TYPE_MX53_ARD:
 		uart_base = MX53_UART1_BASE_ADDR;
 		break;
 	default:
diff --git a/arch/arm/plat-mxc/irq-common.c b/arch/arm/plat-mxc/irq-common.c
index e1c6eff..96953e2 100644
--- a/arch/arm/plat-mxc/irq-common.c
+++ b/arch/arm/plat-mxc/irq-common.c
@@ -42,17 +42,16 @@
 
 int mxc_set_irq_fiq(unsigned int irq, unsigned int type)
 {
-	struct mxc_irq_chip *chip;
-	struct irq_chip *base;
+	struct irq_chip_generic *gc;
+	int (*set_irq_fiq)(unsigned int, unsigned int);
 	int ret;
 
 	ret = -ENOSYS;
 
-	base = irq_get_chip(irq);
-	if (base) {
-		chip = container_of(base, struct mxc_irq_chip, base);
-		if (chip->set_irq_fiq)
-			ret = chip->set_irq_fiq(irq, type);
+	gc = irq_get_chip_data(irq);
+	if (gc && gc->private) {
+		set_irq_fiq = gc->private;
+		ret = set_irq_fiq(irq, type);
 	}
 
 	return ret;
diff --git a/arch/arm/plat-mxc/tzic.c b/arch/arm/plat-mxc/tzic.c
index 710f2e7..f257fcc 100644
--- a/arch/arm/plat-mxc/tzic.c
+++ b/arch/arm/plat-mxc/tzic.c
@@ -68,78 +68,34 @@
 
 	return 0;
 }
+#else
+#define tzic_set_irq_fiq NULL
 #endif
 
-/**
- * tzic_mask_irq() - Disable interrupt source "d" in the TZIC
- *
- * @param  d            interrupt source
- */
-static void tzic_mask_irq(struct irq_data *d)
+static unsigned int *wakeup_intr[4];
+
+static __init void tzic_init_gc(unsigned int irq_start)
 {
-	int index, off;
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	int idx = irq_start >> 5;
 
-	index = d->irq >> 5;
-	off = d->irq & 0x1F;
-	__raw_writel(1 << off, tzic_base + TZIC_ENCLEAR0(index));
+	gc = irq_alloc_generic_chip("tzic", 1, irq_start, tzic_base,
+				    handle_level_irq);
+	gc->private = tzic_set_irq_fiq;
+	gc->wake_enabled = IRQ_MSK(32);
+	wakeup_intr[idx] = &gc->wake_active;
+
+	ct = gc->chip_types;
+	ct->chip.irq_mask = irq_gc_mask_disable_reg;
+	ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+	ct->chip.irq_set_wake = irq_gc_set_wake;
+	ct->regs.disable = TZIC_ENCLEAR0(idx);
+	ct->regs.enable = TZIC_ENSET0(idx);
+
+	irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0);
 }
 
-/**
- * tzic_unmask_irq() - Enable interrupt source "d" in the TZIC
- *
- * @param  d            interrupt source
- */
-static void tzic_unmask_irq(struct irq_data *d)
-{
-	int index, off;
-
-	index = d->irq >> 5;
-	off = d->irq & 0x1F;
-	__raw_writel(1 << off, tzic_base + TZIC_ENSET0(index));
-}
-
-static unsigned int wakeup_intr[4];
-
-/**
- * tzic_set_wake_irq() - Set interrupt source "d" in the TZIC as a wake-up source.
- *
- * @param  d            interrupt source
- * @param  enable       enable as wake-up if equal to non-zero
- * 			disble as wake-up if equal to zero
- *
- * @return       This function returns 0 on success.
- */
-static int tzic_set_wake_irq(struct irq_data *d, unsigned int enable)
-{
-	unsigned int index, off;
-
-	index = d->irq >> 5;
-	off = d->irq & 0x1F;
-
-	if (index > 3)
-		return -EINVAL;
-
-	if (enable)
-		wakeup_intr[index] |= (1 << off);
-	else
-		wakeup_intr[index] &= ~(1 << off);
-
-	return 0;
-}
-
-static struct mxc_irq_chip mxc_tzic_chip = {
-	.base = {
-		.name = "MXC_TZIC",
-		.irq_ack = tzic_mask_irq,
-		.irq_mask = tzic_mask_irq,
-		.irq_unmask = tzic_unmask_irq,
-		.irq_set_wake = tzic_set_wake_irq,
-	},
-#ifdef CONFIG_FIQ
-	.set_irq_fiq = tzic_set_irq_fiq,
-#endif
-};
-
 /*
  * This function initializes the TZIC hardware and disables all the
  * interrupts. It registers the interrupt enable and disable functions
@@ -168,11 +124,8 @@
 
 	/* all IRQ no FIQ Warning :: No selection */
 
-	for (i = 0; i < TZIC_NUM_IRQS; i++) {
-		irq_set_chip_and_handler(i, &mxc_tzic_chip.base,
-					 handle_level_irq);
-		set_irq_flags(i, IRQF_VALID);
-	}
+	for (i = 0; i < TZIC_NUM_IRQS; i += 32)
+		tzic_init_gc(i);
 
 #ifdef CONFIG_FIQ
 	/* Initialize FIQ */
@@ -199,7 +152,7 @@
 
 	for (i = 0; i < 4; i++) {
 		v = is_idle ? __raw_readl(tzic_base + TZIC_ENSET0(i)) :
-			wakeup_intr[i];
+			*wakeup_intr[i];
 		__raw_writel(v, tzic_base + TZIC_WAKEUP0(i));
 	}
 
diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c
index 964704f..3ba4d11 100644
--- a/arch/arm/plat-omap/clock.c
+++ b/arch/arm/plat-omap/clock.c
@@ -475,8 +475,41 @@
 /*
  *	debugfs support to trace clock tree hierarchy and attributes
  */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
 static struct dentry *clk_debugfs_root;
 
+static int clk_dbg_show_summary(struct seq_file *s, void *unused)
+{
+	struct clk *c;
+	struct clk *pa;
+
+	seq_printf(s, "%-30s %-30s %-10s %s\n",
+		"clock-name", "parent-name", "rate", "use-count");
+
+	list_for_each_entry(c, &clocks, node) {
+		pa = c->parent;
+		seq_printf(s, "%-30s %-30s %-10lu %d\n",
+			c->name, pa ? pa->name : "none", c->rate, c->usecount);
+	}
+
+	return 0;
+}
+
+static int clk_dbg_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, clk_dbg_show_summary, inode->i_private);
+}
+
+static const struct file_operations debug_clock_fops = {
+	.open           = clk_dbg_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
 static int clk_debugfs_register_one(struct clk *c)
 {
 	int err;
@@ -545,6 +578,12 @@
 		if (err)
 			goto err_out;
 	}
+
+	d = debugfs_create_file("summary", S_IRUGO,
+		d, NULL, &debug_clock_fops);
+	if (!d)
+		return -ENOMEM;
+
 	return 0;
 err_out:
 	debugfs_remove_recursive(clk_debugfs_root);
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 8dfb818..75a847d 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -209,8 +209,8 @@
 	}
 	omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
 
-	/* Enable autoidle on OMAP2 / OMAP3 */
-	if (cpu_is_omap24xx() || cpu_is_omap34xx())
+	/* Enable autoidle on OMAP2+ */
+	if (cpu_class_is_omap2())
 		autoidle = 1;
 
 	/*
diff --git a/arch/arm/plat-omap/include/plat/clkdev.h b/arch/arm/plat-omap/include/plat/clkdev.h
deleted file mode 100644
index 730c49d..0000000
--- a/arch/arm/plat-omap/include/plat/clkdev.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __MACH_CLKDEV_H
-#define __MACH_CLKDEV_H
-
-static inline int __clk_get(struct clk *clk)
-{
-	return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
-
-#endif
diff --git a/arch/arm/plat-omap/include/plat/clkdev_omap.h b/arch/arm/plat-omap/include/plat/clkdev_omap.h
index f1899a3..387a963 100644
--- a/arch/arm/plat-omap/include/plat/clkdev_omap.h
+++ b/arch/arm/plat-omap/include/plat/clkdev_omap.h
@@ -39,6 +39,7 @@
 #define CK_36XX		(1 << 10)	/* 36xx/37xx-specific clocks */
 #define CK_443X		(1 << 11)
 #define CK_TI816X	(1 << 12)
+#define CK_446X		(1 << 13)
 
 
 #define CK_34XX		(CK_3430ES1 | CK_3430ES2PLUS)
diff --git a/arch/arm/plat-omap/include/plat/clock.h b/arch/arm/plat-omap/include/plat/clock.h
index f57e064..df4b968 100644
--- a/arch/arm/plat-omap/include/plat/clock.h
+++ b/arch/arm/plat-omap/include/plat/clock.h
@@ -58,10 +58,12 @@
 #define RATE_IN_36XX		(1 << 4)
 #define RATE_IN_4430		(1 << 5)
 #define RATE_IN_TI816X		(1 << 6)
+#define RATE_IN_4460		(1 << 7)
 
 #define RATE_IN_24XX		(RATE_IN_242X | RATE_IN_243X)
 #define RATE_IN_34XX		(RATE_IN_3430ES1 | RATE_IN_3430ES2PLUS)
 #define RATE_IN_3XXX		(RATE_IN_34XX | RATE_IN_36XX)
+#define RATE_IN_44XX		(RATE_IN_4430 | RATE_IN_4460)
 
 /* RATE_IN_3430ES2PLUS_36XX includes 34xx/35xx with ES >=2, and all 36xx/37xx */
 #define RATE_IN_3430ES2PLUS_36XX	(RATE_IN_3430ES2PLUS | RATE_IN_36XX)
diff --git a/arch/arm/plat-omap/include/plat/cpu.h b/arch/arm/plat-omap/include/plat/cpu.h
index 8198bb6..67b3d75 100644
--- a/arch/arm/plat-omap/include/plat/cpu.h
+++ b/arch/arm/plat-omap/include/plat/cpu.h
@@ -88,6 +88,7 @@
  * cpu_is_omap243x():	True for OMAP2430
  * cpu_is_omap343x():	True for OMAP3430
  * cpu_is_omap443x():	True for OMAP4430
+ * cpu_is_omap446x():	True for OMAP4460
  */
 #define GET_OMAP_CLASS	(omap_rev() & 0xff)
 
@@ -123,6 +124,7 @@
 IS_OMAP_SUBCLASS(343x, 0x343)
 IS_OMAP_SUBCLASS(363x, 0x363)
 IS_OMAP_SUBCLASS(443x, 0x443)
+IS_OMAP_SUBCLASS(446x, 0x446)
 
 IS_TI_SUBCLASS(816x, 0x816)
 
@@ -137,6 +139,7 @@
 #define cpu_is_ti816x()			0
 #define cpu_is_omap44xx()		0
 #define cpu_is_omap443x()		0
+#define cpu_is_omap446x()		0
 
 #if defined(MULTI_OMAP1)
 # if defined(CONFIG_ARCH_OMAP730)
@@ -361,8 +364,10 @@
 # if defined(CONFIG_ARCH_OMAP4)
 # undef cpu_is_omap44xx
 # undef cpu_is_omap443x
+# undef cpu_is_omap446x
 # define cpu_is_omap44xx()		is_omap44xx()
 # define cpu_is_omap443x()		is_omap443x()
+# define cpu_is_omap446x()		is_omap446x()
 # endif
 
 /* Macros to detect if we have OMAP1 or OMAP2 */
@@ -410,6 +415,9 @@
 #define OMAP4430_REV_ES2_1	(OMAP443X_CLASS | (0x21 << 8))
 #define OMAP4430_REV_ES2_2	(OMAP443X_CLASS | (0x22 << 8))
 
+#define OMAP446X_CLASS		0x44600044
+#define OMAP4460_REV_ES1_0	(OMAP446X_CLASS | (0x10 << 8))
+
 /*
  * omap_chip bits
  *
@@ -439,13 +447,15 @@
 #define CHIP_IS_OMAP4430ES2_1		(1 << 12)
 #define CHIP_IS_OMAP4430ES2_2		(1 << 13)
 #define CHIP_IS_TI816X			(1 << 14)
+#define CHIP_IS_OMAP4460ES1_0		(1 << 15)
 
 #define CHIP_IS_OMAP24XX		(CHIP_IS_OMAP2420 | CHIP_IS_OMAP2430)
 
 #define CHIP_IS_OMAP4430		(CHIP_IS_OMAP4430ES1 |		\
 					 CHIP_IS_OMAP4430ES2 |		\
 					 CHIP_IS_OMAP4430ES2_1 |	\
-					 CHIP_IS_OMAP4430ES2_2)
+					 CHIP_IS_OMAP4430ES2_2 |	\
+					 CHIP_IS_OMAP4460ES1_0)
 
 /*
  * "GE" here represents "greater than or equal to" in terms of ES
@@ -468,7 +478,7 @@
 /*
  * Runtime detection of OMAP3 features
  */
-extern u32 omap3_features;
+extern u32 omap_features;
 
 #define OMAP3_HAS_L2CACHE		BIT(0)
 #define OMAP3_HAS_IVA			BIT(1)
@@ -478,11 +488,15 @@
 #define OMAP3_HAS_192MHZ_CLK		BIT(5)
 #define OMAP3_HAS_IO_WAKEUP		BIT(6)
 #define OMAP3_HAS_SDRC			BIT(7)
+#define OMAP4_HAS_MPU_1GHZ		BIT(8)
+#define OMAP4_HAS_MPU_1_2GHZ		BIT(9)
+#define OMAP4_HAS_MPU_1_5GHZ		BIT(10)
+
 
 #define OMAP3_HAS_FEATURE(feat,flag)			\
 static inline unsigned int omap3_has_ ##feat(void)	\
 {							\
-	return (omap3_features & OMAP3_HAS_ ##flag);	\
+	return omap_features & OMAP3_HAS_ ##flag;	\
 }							\
 
 OMAP3_HAS_FEATURE(l2cache, L2CACHE)
@@ -494,4 +508,19 @@
 OMAP3_HAS_FEATURE(io_wakeup, IO_WAKEUP)
 OMAP3_HAS_FEATURE(sdrc, SDRC)
 
+/*
+ * Runtime detection of OMAP4 features
+ */
+extern u32 omap_features;
+
+#define OMAP4_HAS_FEATURE(feat, flag)			\
+static inline unsigned int omap4_has_ ##feat(void)	\
+{							\
+	return omap_features & OMAP4_HAS_ ##flag;	\
+}							\
+
+OMAP4_HAS_FEATURE(mpu_1ghz, MPU_1GHZ)
+OMAP4_HAS_FEATURE(mpu_1_2ghz, MPU_1_2GHZ)
+OMAP4_HAS_FEATURE(mpu_1_5ghz, MPU_1_5GHZ)
+
 #endif
diff --git a/arch/arm/plat-omap/include/plat/i2c.h b/arch/arm/plat-omap/include/plat/i2c.h
index 878d632..7c22b9e 100644
--- a/arch/arm/plat-omap/include/plat/i2c.h
+++ b/arch/arm/plat-omap/include/plat/i2c.h
@@ -22,6 +22,7 @@
 #define __ASM__ARCH_OMAP_I2C_H
 
 #include <linux/i2c.h>
+#include <linux/i2c-omap.h>
 
 #if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE)
 extern int omap_register_i2c_bus(int bus_id, u32 clkrate,
@@ -46,10 +47,13 @@
  */
 struct omap_i2c_dev_attr {
 	u8	fifo_depth;
-	u8	flags;
+	u32	flags;
 };
 
 void __init omap1_i2c_mux_pins(int bus_id);
 void __init omap2_i2c_mux_pins(int bus_id);
 
+struct omap_hwmod;
+int omap_i2c_reset(struct omap_hwmod *oh);
+
 #endif /* __ASM__ARCH_OMAP_I2C_H */
diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h
index c884320..926d25c 100644
--- a/arch/arm/plat-omap/include/plat/irqs.h
+++ b/arch/arm/plat-omap/include/plat/irqs.h
@@ -407,11 +407,19 @@
 #endif
 #define TWL6030_IRQ_END		(TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)
 
+#define TWL6040_CODEC_IRQ_BASE	TWL6030_IRQ_END
+#ifdef CONFIG_TWL6040_CODEC
+#define TWL6040_CODEC_NR_IRQS	6
+#else
+#define TWL6040_CODEC_NR_IRQS	0
+#endif
+#define TWL6040_CODEC_IRQ_END	(TWL6040_CODEC_IRQ_BASE + TWL6040_CODEC_NR_IRQS)
+
 /* Total number of interrupts depends on the enabled blocks above */
-#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END)
+#if (TWL4030_GPIO_IRQ_END > TWL6040_CODEC_IRQ_END)
 #define TWL_IRQ_END 		TWL4030_GPIO_IRQ_END
 #else
-#define TWL_IRQ_END		TWL6030_IRQ_END
+#define TWL_IRQ_END		TWL6040_CODEC_IRQ_END
 #endif
 
 /* GPMC related */
diff --git a/arch/arm/plat-omap/include/plat/omap4-keypad.h b/arch/arm/plat-omap/include/plat/omap4-keypad.h
index 2b1d9bc..9fe6c87 100644
--- a/arch/arm/plat-omap/include/plat/omap4-keypad.h
+++ b/arch/arm/plat-omap/include/plat/omap4-keypad.h
@@ -10,5 +10,6 @@
 	u8 cols;
 };
 
-extern int omap4_keyboard_init(struct omap4_keypad_platform_data *);
+extern int omap4_keyboard_init(struct omap4_keypad_platform_data *,
+				struct omap_board_data *);
 #endif
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index ce06ac6..0e329ca 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -2,6 +2,7 @@
  * omap_hwmod macros, structures
  *
  * Copyright (C) 2009-2011 Nokia Corporation
+ * Copyright (C) 2011 Texas Instruments, Inc.
  * Paul Walmsley
  *
  * Created in collaboration with (alphabetical order): Benoît Cousson,
@@ -79,6 +80,11 @@
 #define HWMOD_IDLEMODE_SMART		(1 << 2)
 #define HWMOD_IDLEMODE_SMART_WKUP	(1 << 3)
 
+/* modulemode control type (SW or HW) */
+#define MODULEMODE_HWCTRL		1
+#define MODULEMODE_SWCTRL		2
+
+
 /**
  * struct omap_hwmod_mux_info - hwmod specific mux configuration
  * @pads:              array of omap_device_pad entries
@@ -360,9 +366,11 @@
  * @submodule_wkdep_bit: bit shift of the WKDEP range
  */
 struct omap_hwmod_omap4_prcm {
-	void __iomem	*clkctrl_reg;
-	void __iomem	*rstctrl_reg;
+	u16		clkctrl_offs;
+	u16		rstctrl_offs;
+	u16		context_offs;
 	u8		submodule_wkdep_bit;
+	u8		modulemode;
 };
 
 
@@ -515,6 +523,8 @@
 	const char			*main_clk;
 	struct clk			*_clk;
 	struct omap_hwmod_opt_clk	*opt_clks;
+	char				*clkdm_name;
+	struct clockdomain		*clkdm;
 	char				*vdd_name;
 	struct voltagedomain		*voltdm;
 	struct omap_hwmod_ocp_if	**masters; /* connect to *_IA */
@@ -566,6 +576,7 @@
 
 void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs);
 u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs);
+int omap_hwmod_softreset(struct omap_hwmod *oh);
 
 int omap_hwmod_count_resources(struct omap_hwmod *oh);
 int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res);
diff --git a/arch/arm/plat-omap/include/plat/uncompress.h b/arch/arm/plat-omap/include/plat/uncompress.h
index ac4b60d..a067484 100644
--- a/arch/arm/plat-omap/include/plat/uncompress.h
+++ b/arch/arm/plat-omap/include/plat/uncompress.h
@@ -148,6 +148,7 @@
 		/* omap3 based boards using UART3 */
 		DEBUG_LL_OMAP3(3, cm_t35);
 		DEBUG_LL_OMAP3(3, cm_t3517);
+		DEBUG_LL_OMAP3(3, cm_t3730);
 		DEBUG_LL_OMAP3(3, craneboard);
 		DEBUG_LL_OMAP3(3, devkit8000);
 		DEBUG_LL_OMAP3(3, igep0020);
diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c
index 83a37c5..c60737c 100644
--- a/arch/arm/plat-omap/iovmm.c
+++ b/arch/arm/plat-omap/iovmm.c
@@ -72,7 +72,7 @@
 	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
 		size_t bytes;
 
-		bytes = sg_dma_len(sg);
+		bytes = sg->length;
 
 		if (!iopgsz_ok(bytes)) {
 			pr_err("%s: sg[%d] not iommu pagesize(%x)\n",
@@ -198,7 +198,7 @@
 		int err;
 
 		pa = sg_phys(sg);
-		bytes = sg_dma_len(sg);
+		bytes = sg->length;
 
 		BUG_ON(bytes != PAGE_SIZE);
 
@@ -476,7 +476,7 @@
 		struct iotlb_entry e;
 
 		pa = sg_phys(sg);
-		bytes = sg_dma_len(sg);
+		bytes = sg->length;
 
 		flags &= ~IOVMF_PGSZ_MASK;
 		pgsz = bytes_to_iopgsz(bytes);
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
index 3c1fbdc..6c62af1 100644
--- a/arch/arm/plat-omap/mcbsp.c
+++ b/arch/arm/plat-omap/mcbsp.c
@@ -966,6 +966,33 @@
 }
 EXPORT_SYMBOL(omap_mcbsp_stop);
 
+/*
+ * The following functions are only required on an OMAP1-only build.
+ * mach-omap2/mcbsp.c contains the real functions
+ */
+#ifndef CONFIG_ARCH_OMAP2PLUS
+int omap2_mcbsp_set_clks_src(u8 id, u8 fck_src_id)
+{
+	WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
+	     __func__);
+	return -EINVAL;
+}
+
+void omap2_mcbsp1_mux_clkr_src(u8 mux)
+{
+	WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
+	     __func__);
+	return;
+}
+
+void omap2_mcbsp1_mux_fsr_src(u8 mux)
+{
+	WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
+	     __func__);
+	return;
+}
+#endif
+
 #ifdef CONFIG_ARCH_OMAP3
 #define max_thres(m)			(mcbsp->pdata->buffer_size)
 #define valid_threshold(m, val)		((val) <= max_thres(m))
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index 3471c65..b6b4097 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -236,56 +236,71 @@
 	return 0;
 }
 
+static void _add_clkdev(struct omap_device *od, const char *clk_alias,
+		       const char *clk_name)
+{
+	struct clk *r;
+	struct clk_lookup *l;
+
+	if (!clk_alias || !clk_name)
+		return;
+
+	pr_debug("omap_device: %s: Creating %s -> %s\n",
+		 dev_name(&od->pdev.dev), clk_alias, clk_name);
+
+	r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias);
+	if (!IS_ERR(r)) {
+		pr_warning("omap_device: %s: alias %s already exists\n",
+			   dev_name(&od->pdev.dev), clk_alias);
+		clk_put(r);
+		return;
+	}
+
+	r = omap_clk_get_by_name(clk_name);
+	if (IS_ERR(r)) {
+		pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
+		       dev_name(&od->pdev.dev), clk_name);
+		return;
+	}
+
+	l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev));
+	if (!l) {
+		pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
+		       dev_name(&od->pdev.dev), clk_alias);
+		return;
+	}
+
+	clkdev_add(l);
+}
+
 /**
- * _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks
+ * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
+ * and main clock
  * @od: struct omap_device *od
+ * @oh: struct omap_hwmod *oh
  *
- * For every optional clock present per hwmod per omap_device, this function
- * adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role>
- * if it does not exist already.
+ * For the main clock and every optional clock present per hwmod per
+ * omap_device, this function adds an entry in the clkdev table of the
+ * form <dev-id=dev_name, con-id=role> if it does not exist already.
  *
  * The function is called from inside omap_device_build_ss(), after
  * omap_device_register.
  *
  * This allows drivers to get a pointer to its optional clocks based on its role
  * by calling clk_get(<dev*>, <role>).
+ * In the case of the main clock, a "fck" alias is used.
  *
  * No return value.
  */
-static void _add_optional_clock_clkdev(struct omap_device *od,
-				      struct omap_hwmod *oh)
+static void _add_hwmod_clocks_clkdev(struct omap_device *od,
+				     struct omap_hwmod *oh)
 {
 	int i;
 
-	for (i = 0; i < oh->opt_clks_cnt; i++) {
-		struct omap_hwmod_opt_clk *oc;
-		struct clk *r;
-		struct clk_lookup *l;
+	_add_clkdev(od, "fck", oh->main_clk);
 
-		oc = &oh->opt_clks[i];
-
-		if (!oc->_clk)
-			continue;
-
-		r = clk_get_sys(dev_name(&od->pdev.dev), oc->role);
-		if (!IS_ERR(r))
-			continue; /* clkdev entry exists */
-
-		r = omap_clk_get_by_name((char *)oc->clk);
-		if (IS_ERR(r)) {
-			pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
-			       dev_name(&od->pdev.dev), oc->clk);
-			continue;
-		}
-
-		l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
-		if (!l) {
-			pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
-			       dev_name(&od->pdev.dev), oc->role);
-			return;
-		}
-		clkdev_add(l);
-	}
+	for (i = 0; i < oh->opt_clks_cnt; i++)
+		_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
 }
 
 
@@ -492,7 +507,7 @@
 
 	for (i = 0; i < oh_cnt; i++) {
 		hwmods[i]->od = od;
-		_add_optional_clock_clkdev(od, hwmods[i]);
+		_add_hwmod_clocks_clkdev(od, hwmods[i]);
 	}
 
 	if (ret)
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index e98f5c5..9843c95 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/arch/arm/plat-s5p/Kconfig
@@ -39,6 +39,7 @@
 
 config S5P_HRT
 	bool
+	select SAMSUNG_DEV_PWM
 	help
 	  Use the High Resolution timer support
 
@@ -70,6 +71,16 @@
 	help
 	  Compile in platform device definitions for FIMC controller 3
 
+config S5P_DEV_FIMD0
+	bool
+	help
+	  Compile in platform device definitions for FIMD controller 0
+
+config S5P_DEV_MFC
+	bool
+	help
+	  Compile in platform device definitions for MFC
+
 config S5P_DEV_ONENAND
 	bool
 	help
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index e234cc4..4b53e04 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/arch/arm/plat-s5p/Makefile
@@ -25,11 +25,12 @@
 obj-$(CONFIG_S5P_HRT) 		+= s5p-time.o
 
 # devices
-
+obj-$(CONFIG_S5P_DEV_MFC)	+= dev-mfc.o
 obj-$(CONFIG_S5P_DEV_FIMC0)	+= dev-fimc0.o
 obj-$(CONFIG_S5P_DEV_FIMC1)	+= dev-fimc1.o
 obj-$(CONFIG_S5P_DEV_FIMC2)	+= dev-fimc2.o
 obj-$(CONFIG_S5P_DEV_FIMC3)	+= dev-fimc3.o
+obj-$(CONFIG_S5P_DEV_FIMD0)	+= dev-fimd0.o
 obj-$(CONFIG_S5P_DEV_ONENAND)	+= dev-onenand.o
 obj-$(CONFIG_S5P_DEV_CSIS0)	+= dev-csis0.o
 obj-$(CONFIG_S5P_DEV_CSIS1)	+= dev-csis1.o
diff --git a/arch/arm/plat-s5p/dev-fimd0.c b/arch/arm/plat-s5p/dev-fimd0.c
new file mode 100644
index 0000000..f728bb5
--- /dev/null
+++ b/arch/arm/plat-s5p/dev-fimd0.c
@@ -0,0 +1,67 @@
+/* linux/arch/arm/plat-s5p/dev-fimd0.c
+ *
+ * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Core file for Samsung Display Controller (FIMD) 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/gfp.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/irqs.h>
+#include <mach/map.h>
+
+#include <plat/fb.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+static struct resource s5p_fimd0_resource[] = {
+	[0] = {
+		.start	= S5P_PA_FIMD0,
+		.end	= S5P_PA_FIMD0 + SZ_32K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_FIMD0_VSYNC,
+		.end	= IRQ_FIMD0_VSYNC,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.start	= IRQ_FIMD0_FIFO,
+		.end	= IRQ_FIMD0_FIFO,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= IRQ_FIMD0_SYSTEM,
+		.end	= IRQ_FIMD0_SYSTEM,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fimd0_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s5p_device_fimd0 = {
+	.name		= "s5p-fb",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(s5p_fimd0_resource),
+	.resource	= s5p_fimd0_resource,
+	.dev		= {
+		.dma_mask		= &fimd0_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s5p_fimd0_set_platdata(struct s3c_fb_platdata *pd)
+{
+	s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
+			&s5p_device_fimd0);
+}
diff --git a/arch/arm/plat-s5p/dev-mfc.c b/arch/arm/plat-s5p/dev-mfc.c
new file mode 100644
index 0000000..94226a0
--- /dev/null
+++ b/arch/arm/plat-s5p/dev-mfc.c
@@ -0,0 +1,123 @@
+/* linux/arch/arm/plat-s5p/dev-mfc.c
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
+ *
+ * Base S5P MFC resource and device definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/memblock.h>
+#include <linux/ioport.h>
+
+#include <mach/map.h>
+#include <plat/devs.h>
+#include <plat/irqs.h>
+#include <plat/mfc.h>
+
+static struct resource s5p_mfc_resource[] = {
+	[0] = {
+		.start	= S5P_PA_MFC,
+		.end	= S5P_PA_MFC + SZ_64K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_MFC,
+		.end	= IRQ_MFC,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+struct platform_device s5p_device_mfc = {
+	.name		= "s5p-mfc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s5p_mfc_resource),
+	.resource	= s5p_mfc_resource,
+};
+
+/*
+ * MFC hardware has 2 memory interfaces which are modelled as two separate
+ * platform devices to let dma-mapping distinguish between them.
+ *
+ * MFC parent device (s5p_device_mfc) must be registered before memory
+ * interface specific devices (s5p_device_mfc_l and s5p_device_mfc_r).
+ */
+
+static u64 s5p_mfc_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device s5p_device_mfc_l = {
+	.name		= "s5p-mfc-l",
+	.id		= -1,
+	.dev		= {
+		.parent			= &s5p_device_mfc.dev,
+		.dma_mask		= &s5p_mfc_dma_mask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device s5p_device_mfc_r = {
+	.name		= "s5p-mfc-r",
+	.id		= -1,
+	.dev		= {
+		.parent			= &s5p_device_mfc.dev,
+		.dma_mask		= &s5p_mfc_dma_mask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+struct s5p_mfc_reserved_mem {
+	phys_addr_t	base;
+	unsigned long	size;
+	struct device	*dev;
+};
+
+static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata;
+
+void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize,
+				phys_addr_t lbase, unsigned int lsize)
+{
+	int i;
+
+	s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev;
+	s5p_mfc_mem[0].base = rbase;
+	s5p_mfc_mem[0].size = rsize;
+
+	s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev;
+	s5p_mfc_mem[1].base = lbase;
+	s5p_mfc_mem[1].size = lsize;
+
+	for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) {
+		struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i];
+		if (memblock_remove(area->base, area->size)) {
+			printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n",
+			       area->size, (unsigned long) area->base);
+			area->base = 0;
+		}
+	}
+}
+
+static int __init s5p_mfc_memory_init(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) {
+		struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i];
+		if (!area->base)
+			continue;
+
+		if (dma_declare_coherent_memory(area->dev, area->base,
+				area->base, area->size,
+				DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0)
+			printk(KERN_ERR "Failed to declare coherent memory for MFC device (%ld bytes at 0x%08lx)\n",
+			       area->size, (unsigned long) area->base);
+	}
+	return 0;
+}
+device_initcall(s5p_mfc_memory_init);
diff --git a/arch/arm/plat-s5p/include/plat/map-s5p.h b/arch/arm/plat-s5p/include/plat/map-s5p.h
index d973d39..36d3551 100644
--- a/arch/arm/plat-s5p/include/plat/map-s5p.h
+++ b/arch/arm/plat-s5p/include/plat/map-s5p.h
@@ -35,9 +35,10 @@
 #define S5P_VA_COREPERI_BASE	S3C_ADDR(0x02800000)
 #define S5P_VA_COREPERI(x)	(S5P_VA_COREPERI_BASE + (x))
 #define S5P_VA_SCU		S5P_VA_COREPERI(0x0)
-#define S5P_VA_GIC_CPU		S5P_VA_COREPERI(0x100)
 #define S5P_VA_TWD		S5P_VA_COREPERI(0x600)
-#define S5P_VA_GIC_DIST		S5P_VA_COREPERI(0x1000)
+
+#define S5P_VA_GIC_CPU		S3C_ADDR(0x02810000)
+#define S5P_VA_GIC_DIST		S3C_ADDR(0x02820000)
 
 #define S3C_VA_USB_HSPHY	S3C_ADDR(0x02900000)
 
diff --git a/arch/arm/plat-s5p/include/plat/mfc.h b/arch/arm/plat-s5p/include/plat/mfc.h
new file mode 100644
index 0000000..6697f8c
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/mfc.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ *
+ * 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 __PLAT_S5P_MFC_H
+#define __PLAT_S5P_MFC_H
+
+/**
+ * s5p_mfc_reserve_mem - function to early reserve memory for MFC driver
+ * @rbase:	base address for MFC 'right' memory interface
+ * @rsize:	size of the memory reserved for MFC 'right' interface
+ * @lbase:	base address for MFC 'left' memory interface
+ * @lsize:	size of the memory reserved for MFC 'left' interface
+ *
+ * This function reserves system memory for both MFC device memory
+ * interfaces and registers it to respective struct device entries as
+ * coherent memory.
+ */
+void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize,
+				phys_addr_t lbase, unsigned int lsize);
+
+#endif /* __PLAT_S5P_MFC_H */
diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c
index e8f2be2..ee8deef 100644
--- a/arch/arm/plat-samsung/adc.c
+++ b/arch/arm/plat-samsung/adc.c
@@ -21,6 +21,7 @@
 #include <linux/clk.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/regulator/consumer.h>
 
 #include <plat/regs-adc.h>
 #include <plat/adc.h>
@@ -39,8 +40,9 @@
  */
 
 enum s3c_cpu_type {
-	TYPE_S3C24XX,
-	TYPE_S3C64XX
+	TYPE_ADCV1, /* S3C24XX */
+	TYPE_ADCV2, /* S3C64XX, S5P64X0, S5PC100 */
+	TYPE_ADCV3, /* S5PV210, S5PC110, EXYNOS4210 */
 };
 
 struct s3c_adc_client {
@@ -71,6 +73,7 @@
 	unsigned int		 prescale;
 
 	int			 irq;
+	struct regulator	*vdd;
 };
 
 static struct adc_device *adc_dev;
@@ -91,6 +94,7 @@
 				  struct s3c_adc_client *client)
 {
 	unsigned con = readl(adc->regs + S3C2410_ADCCON);
+	enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
 
 	client->select_cb(client, 1);
 
@@ -98,8 +102,12 @@
 	con &= ~S3C2410_ADCCON_STDBM;
 	con &= ~S3C2410_ADCCON_STARTMASK;
 
-	if (!client->is_ts)
-		con |= S3C2410_ADCCON_SELMUX(client->channel);
+	if (!client->is_ts) {
+		if (cpu == TYPE_ADCV3)
+			writel(client->channel & 0xf, adc->regs + S5P_ADCMUX);
+		else
+			con |= S3C2410_ADCCON_SELMUX(client->channel);
+	}
 
 	writel(con, adc->regs + S3C2410_ADCCON);
 }
@@ -285,8 +293,8 @@
 
 	client->nr_samples--;
 
-	if (cpu == TYPE_S3C64XX) {
-		/* S3C64XX ADC resolution is 12-bit */
+	if (cpu != TYPE_ADCV1) {
+		/* S3C64XX/S5P ADC resolution is 12-bit */
 		data0 &= 0xfff;
 		data1 &= 0xfff;
 	} else {
@@ -312,7 +320,7 @@
 	}
 
 exit:
-	if (cpu == TYPE_S3C64XX) {
+	if (cpu != TYPE_ADCV1) {
 		/* Clear ADC interrupt */
 		writel(0, adc->regs + S3C64XX_ADCCLRINT);
 	}
@@ -338,17 +346,24 @@
 	adc->pdev = pdev;
 	adc->prescale = S3C2410_ADCCON_PRSCVL(49);
 
+	adc->vdd = regulator_get(dev, "vdd");
+	if (IS_ERR(adc->vdd)) {
+		dev_err(dev, "operating without regulator \"vdd\" .\n");
+		ret = PTR_ERR(adc->vdd);
+		goto err_alloc;
+	}
+
 	adc->irq = platform_get_irq(pdev, 1);
 	if (adc->irq <= 0) {
 		dev_err(dev, "failed to get adc irq\n");
 		ret = -ENOENT;
-		goto err_alloc;
+		goto err_reg;
 	}
 
 	ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc);
 	if (ret < 0) {
 		dev_err(dev, "failed to attach adc irq\n");
-		goto err_alloc;
+		goto err_reg;
 	}
 
 	adc->clk = clk_get(dev, "adc");
@@ -372,10 +387,14 @@
 		goto err_clk;
 	}
 
+	ret = regulator_enable(adc->vdd);
+	if (ret)
+		goto err_ioremap;
+
 	clk_enable(adc->clk);
 
 	tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
-	if (platform_get_device_id(pdev)->driver_data == TYPE_S3C64XX) {
+	if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1) {
 		/* Enable 12-bit ADC resolution */
 		tmp |= S3C64XX_ADCCON_RESSEL;
 	}
@@ -388,12 +407,15 @@
 
 	return 0;
 
+ err_ioremap:
+	iounmap(adc->regs);
  err_clk:
 	clk_put(adc->clk);
 
  err_irq:
 	free_irq(adc->irq, adc);
-
+ err_reg:
+	regulator_put(adc->vdd);
  err_alloc:
 	kfree(adc);
 	return ret;
@@ -406,6 +428,8 @@
 	iounmap(adc->regs);
 	free_irq(adc->irq, adc);
 	clk_disable(adc->clk);
+	regulator_disable(adc->vdd);
+	regulator_put(adc->vdd);
 	clk_put(adc->clk);
 	kfree(adc);
 
@@ -413,8 +437,10 @@
 }
 
 #ifdef CONFIG_PM
-static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
+static int s3c_adc_suspend(struct device *dev)
 {
+	struct platform_device *pdev = container_of(dev,
+			struct platform_device, dev);
 	struct adc_device *adc = platform_get_drvdata(pdev);
 	unsigned long flags;
 	u32 con;
@@ -428,19 +454,30 @@
 	disable_irq(adc->irq);
 	spin_unlock_irqrestore(&adc->lock, flags);
 	clk_disable(adc->clk);
+	regulator_disable(adc->vdd);
 
 	return 0;
 }
 
-static int s3c_adc_resume(struct platform_device *pdev)
+static int s3c_adc_resume(struct device *dev)
 {
+	struct platform_device *pdev = container_of(dev,
+			struct platform_device, dev);
 	struct adc_device *adc = platform_get_drvdata(pdev);
+	int ret;
+	unsigned long tmp;
 
+	ret = regulator_enable(adc->vdd);
+	if (ret)
+		return ret;
 	clk_enable(adc->clk);
 	enable_irq(adc->irq);
 
-	writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
-	       adc->regs + S3C2410_ADCCON);
+	tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
+	/* Enable 12-bit ADC resolution */
+	if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1)
+		tmp |= S3C64XX_ADCCON_RESSEL;
+	writel(tmp, adc->regs + S3C2410_ADCCON);
 
 	return 0;
 }
@@ -453,25 +490,32 @@
 static struct platform_device_id s3c_adc_driver_ids[] = {
 	{
 		.name           = "s3c24xx-adc",
-		.driver_data    = TYPE_S3C24XX,
+		.driver_data    = TYPE_ADCV1,
 	}, {
 		.name           = "s3c64xx-adc",
-		.driver_data    = TYPE_S3C64XX,
+		.driver_data    = TYPE_ADCV2,
+	}, {
+		.name		= "samsung-adc-v3",
+		.driver_data	= TYPE_ADCV3,
 	},
 	{ }
 };
 MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);
 
+static const struct dev_pm_ops adc_pm_ops = {
+	.suspend	= s3c_adc_suspend,
+	.resume		= s3c_adc_resume,
+};
+
 static struct platform_driver s3c_adc_driver = {
 	.id_table	= s3c_adc_driver_ids,
 	.driver		= {
 		.name	= "s3c-adc",
 		.owner	= THIS_MODULE,
+		.pm	= &adc_pm_ops,
 	},
 	.probe		= s3c_adc_probe,
 	.remove		= __devexit_p(s3c_adc_remove),
-	.suspend	= s3c_adc_suspend,
-	.resume		= s3c_adc_resume,
 };
 
 static int __init adc_init(void)
@@ -485,4 +529,4 @@
 	return ret;
 }
 
-arch_initcall(adc_init);
+module_init(adc_init);
diff --git a/arch/arm/plat-samsung/dev-asocdma.c b/arch/arm/plat-samsung/dev-asocdma.c
index a068c4f..97e35d3 100644
--- a/arch/arm/plat-samsung/dev-asocdma.c
+++ b/arch/arm/plat-samsung/dev-asocdma.c
@@ -23,3 +23,13 @@
 	}
 };
 EXPORT_SYMBOL(samsung_asoc_dma);
+
+struct platform_device samsung_asoc_idma = {
+	.name		= "samsung-idma",
+	.id		= -1,
+	.dev		= {
+		.dma_mask		= &audio_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	}
+};
+EXPORT_SYMBOL(samsung_asoc_idma);
diff --git a/arch/arm/plat-samsung/include/plat/audio.h b/arch/arm/plat-samsung/include/plat/audio.h
index a0826ed..aa9875f 100644
--- a/arch/arm/plat-samsung/include/plat/audio.h
+++ b/arch/arm/plat-samsung/include/plat/audio.h
@@ -44,6 +44,7 @@
 	 * Also corresponds to clocks of I2SMOD[10]
 	 */
 	const char **src_clk;
+	dma_addr_t idma_addr;
 };
 
 /**
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index e3b31c2..24ebb1e 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -40,6 +40,7 @@
 extern struct platform_device s3c64xx_device_spi1;
 
 extern struct platform_device samsung_asoc_dma;
+extern struct platform_device samsung_asoc_idma;
 
 extern struct platform_device s3c64xx_device_pcm0;
 extern struct platform_device s3c64xx_device_pcm1;
@@ -49,6 +50,7 @@
 extern struct platform_device s3c_device_ts;
 
 extern struct platform_device s3c_device_fb;
+extern struct platform_device s5p_device_fimd0;
 extern struct platform_device s3c_device_ohci;
 extern struct platform_device s3c_device_lcd;
 extern struct platform_device s3c_device_wdt;
@@ -112,6 +114,7 @@
 extern struct platform_device exynos4_device_spdif;
 extern struct platform_device exynos4_device_pd[];
 extern struct platform_device exynos4_device_ahci;
+extern struct platform_device exynos4_device_dwmci;
 
 extern struct platform_device s5p6440_device_pcm;
 extern struct platform_device s5p6440_device_iis;
@@ -136,6 +139,9 @@
 extern struct platform_device s5p_device_fimc2;
 extern struct platform_device s5p_device_fimc3;
 
+extern struct platform_device s5p_device_mfc;
+extern struct platform_device s5p_device_mfc_l;
+extern struct platform_device s5p_device_mfc_r;
 extern struct platform_device s5p_device_mipi_csis0;
 extern struct platform_device s5p_device_mipi_csis1;
 
diff --git a/arch/arm/plat-samsung/include/plat/fb-core.h b/arch/arm/plat-samsung/include/plat/fb-core.h
index bca383e..6abcbf1 100644
--- a/arch/arm/plat-samsung/include/plat/fb-core.h
+++ b/arch/arm/plat-samsung/include/plat/fb-core.h
@@ -26,4 +26,19 @@
 #endif
 }
 
+/* Re-define device name depending on support. */
+static inline void s5p_fb_setname(int id, char *name)
+{
+	switch (id) {
+#ifdef CONFIG_S5P_DEV_FIMD0
+	case 0:
+		s5p_device_fimd0.name = name;
+	break;
+#endif
+	default:
+		printk(KERN_ERR "%s: invalid device id(%d)\n", __func__, id);
+	break;
+	}
+}
+
 #endif /* __ASM_PLAT_FB_CORE_H */
diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h
index cb3ca3a..01f10e4 100644
--- a/arch/arm/plat-samsung/include/plat/fb.h
+++ b/arch/arm/plat-samsung/include/plat/fb.h
@@ -74,6 +74,14 @@
 extern void s3c_fb_set_platdata(struct s3c_fb_platdata *pd);
 
 /**
+ * s5p_fimd0_set_platdata() - Setup the FB device with platform data.
+ * @pd: The platform data to set. The data is copied from the passed structure
+ *      so the machine data can mark the data __initdata so that any unused
+ *      machines will end up dumping their data at runtime.
+ */
+extern void s5p_fimd0_set_platdata(struct s3c_fb_platdata *pd);
+
+/**
  * s3c64xx_fb_gpio_setup_24bpp() - S3C64XX setup function for 24bpp LCD
  *
  * Initialise the GPIO for an 24bpp LCD display on the RGB interface.
@@ -94,4 +102,11 @@
  */
 extern void s5pv210_fb_gpio_setup_24bpp(void);
 
+/**
+ * exynos4_fimd0_gpio_setup_24bpp() - Exynos4 setup function for 24bpp LCD0
+ *
+ * Initialise the GPIO for an 24bpp LCD display on the RGB interface 0.
+ */
+extern void exynos4_fimd0_gpio_setup_24bpp(void);
+
 #endif /* __PLAT_S3C_FB_H */
diff --git a/arch/arm/plat-samsung/include/plat/regs-adc.h b/arch/arm/plat-samsung/include/plat/regs-adc.h
index 7554c4f..035e8c3 100644
--- a/arch/arm/plat-samsung/include/plat/regs-adc.h
+++ b/arch/arm/plat-samsung/include/plat/regs-adc.h
@@ -21,6 +21,7 @@
 #define S3C2410_ADCDAT1	   S3C2410_ADCREG(0x10)
 #define S3C64XX_ADCUPDN		S3C2410_ADCREG(0x14)
 #define S3C64XX_ADCCLRINT	S3C2410_ADCREG(0x18)
+#define S5P_ADCMUX		S3C2410_ADCREG(0x1C)
 #define S3C64XX_ADCCLRINTPNDNUP	S3C2410_ADCREG(0x20)
 
 
diff --git a/arch/arm/plat-samsung/irq-uart.c b/arch/arm/plat-samsung/irq-uart.c
index 657405c..3014c72 100644
--- a/arch/arm/plat-samsung/irq-uart.c
+++ b/arch/arm/plat-samsung/irq-uart.c
@@ -19,6 +19,8 @@
 #include <linux/irq.h>
 #include <linux/io.h>
 
+#include <asm/mach/irq.h>
+
 #include <mach/map.h>
 #include <plat/irq-uart.h>
 #include <plat/regs-serial.h>
@@ -30,9 +32,12 @@
 static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc)
 {
 	struct s3c_uart_irq *uirq = desc->irq_data.handler_data;
+	struct irq_chip *chip = irq_get_chip(irq);
 	u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP);
 	int base = uirq->base_irq;
 
+	chained_irq_enter(chip, desc);
+
 	if (pend & (1 << 0))
 		generic_handle_irq(base);
 	if (pend & (1 << 1))
@@ -41,6 +46,8 @@
 		generic_handle_irq(base + 2);
 	if (pend & (1 << 3))
 		generic_handle_irq(base + 3);
+
+	chained_irq_exit(chip, desc);
 }
 
 static void __init s3c_init_uart_irq(struct s3c_uart_irq *uirq)
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index 5fa1742..ae6f998 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -269,6 +269,7 @@
 	/* save all necessary core registers not covered by the drivers */
 
 	s3c_pm_save_gpios();
+	s3c_pm_saved_gpios();
 	s3c_pm_save_uarts();
 	s3c_pm_save_core();
 
@@ -306,6 +307,7 @@
 	s3c_pm_restore_core();
 	s3c_pm_restore_uarts();
 	s3c_pm_restore_gpios();
+	s3c_pm_restored_gpios();
 
 	s3c_pm_debug_init();
 
diff --git a/arch/arm/plat-spear/include/plat/clkdev.h b/arch/arm/plat-spear/include/plat/clkdev.h
deleted file mode 100644
index a2d0112..0000000
--- a/arch/arm/plat-spear/include/plat/clkdev.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/plat-spear/include/plat/clkdev.h
- *
- * Clock Dev framework definitions for SPEAr platform
- *
- * Copyright (C) 2009 ST Microelectronics
- * Viresh Kumar<viresh.kumar@st.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __PLAT_CLKDEV_H
-#define __PLAT_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif /* __PLAT_CLKDEV_H */
diff --git a/arch/arm/plat-tcc/include/mach/clkdev.h b/arch/arm/plat-tcc/include/mach/clkdev.h
deleted file mode 100644
index 04b37a8..0000000
--- a/arch/arm/plat-tcc/include/mach/clkdev.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_MACH_CLKDEV_H
-#define __ASM_MACH_CLKDEV_H
-
-#define __clk_get(clk) ({ 1; })
-#define __clk_put(clk) do { } while (0)
-
-#endif
diff --git a/arch/avr32/include/asm/atomic.h b/arch/avr32/include/asm/atomic.h
index bbce6a1..e0ac263 100644
--- a/arch/avr32/include/asm/atomic.h
+++ b/arch/avr32/include/asm/atomic.h
@@ -78,70 +78,63 @@
 /*
  * atomic_sub_unless - sub unless the number is a given value
  * @v: pointer of type atomic_t
- * @a: the amount to add to v...
+ * @a: the amount to subtract from v...
  * @u: ...unless v is equal to u.
  *
- * If the atomic value v is not equal to u, this function subtracts a
- * from v, and returns non zero. If v is equal to u then it returns
- * zero. This is done as an atomic operation.
+ * Atomically subtract @a from @v, so long as it was not @u.
+ * Returns the old value of @v.
 */
-static inline int atomic_sub_unless(atomic_t *v, int a, int u)
+static inline void atomic_sub_unless(atomic_t *v, int a, int u)
 {
-	int tmp, result = 0;
+	int tmp;
 
 	asm volatile(
 		"/* atomic_sub_unless */\n"
 		"1:	ssrf	5\n"
-		"	ld.w	%0, %3\n"
-		"	cp.w	%0, %5\n"
+		"	ld.w	%0, %2\n"
+		"	cp.w	%0, %4\n"
 		"	breq	1f\n"
-		"	sub	%0, %4\n"
-		"	stcond	%2, %0\n"
+		"	sub	%0, %3\n"
+		"	stcond	%1, %0\n"
 		"	brne	1b\n"
-		"	mov	%1, 1\n"
 		"1:"
-		: "=&r"(tmp), "=&r"(result), "=o"(v->counter)
-		: "m"(v->counter), "rKs21"(a), "rKs21"(u), "1"(result)
+		: "=&r"(tmp), "=o"(v->counter)
+		: "m"(v->counter), "rKs21"(a), "rKs21"(u)
 		: "cc", "memory");
-
-	return result;
 }
 
 /*
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
- * If the atomic value v is not equal to u, this function adds a to v,
- * and returns non zero. If v is equal to u then it returns zero. This
- * is done as an atomic operation.
+ * Atomically adds @a to @v, so long as it was not @u.
+ * Returns the old value of @v.
 */
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
-	int tmp, result;
+	int tmp, old = atomic_read(v);
 
 	if (__builtin_constant_p(a) && (a >= -1048575) && (a <= 1048576))
-		result = atomic_sub_unless(v, -a, u);
+		atomic_sub_unless(v, -a, u);
 	else {
-		result = 0;
 		asm volatile(
-			"/* atomic_add_unless */\n"
+			"/* __atomic_add_unless */\n"
 			"1:	ssrf	5\n"
-			"	ld.w	%0, %3\n"
-			"	cp.w	%0, %5\n"
+			"	ld.w	%0, %2\n"
+			"	cp.w	%0, %4\n"
 			"	breq	1f\n"
-			"	add	%0, %4\n"
-			"	stcond	%2, %0\n"
+			"	add	%0, %3\n"
+			"	stcond	%1, %0\n"
 			"	brne	1b\n"
-			"	mov	%1, 1\n"
 			"1:"
-			: "=&r"(tmp), "=&r"(result), "=o"(v->counter)
-			: "m"(v->counter), "r"(a), "ir"(u), "1"(result)
+			: "=&r"(tmp), "=o"(v->counter)
+			: "m"(v->counter), "r"(a), "ir"(u)
 			: "cc", "memory");
 	}
 
-	return result;
+	return old;
 }
 
 /*
@@ -188,7 +181,6 @@
 #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
 #define atomic_add_negative(i, v) (atomic_add_return(i, v) < 0)
 
-#define atomic_inc_not_zero(v)	atomic_add_unless(v, 1, 0)
 #define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v)
 
 #define smp_mb__before_atomic_dec()	barrier()
@@ -196,6 +188,4 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
-
 #endif /*  __ASM_AVR32_ATOMIC_H */
diff --git a/arch/avr32/include/asm/ptrace.h b/arch/avr32/include/asm/ptrace.h
index e53dd0d..c67a007 100644
--- a/arch/avr32/include/asm/ptrace.h
+++ b/arch/avr32/include/asm/ptrace.h
@@ -132,8 +132,6 @@
 #define instruction_pointer(regs)       ((regs)->pc)
 #define profile_pc(regs)                instruction_pointer(regs)
 
-extern void show_regs (struct pt_regs *);
-
 static __inline__ int valid_user_regs(struct pt_regs *regs)
 {
 	/*
diff --git a/arch/blackfin/include/asm/atomic.h b/arch/blackfin/include/asm/atomic.h
index 4c707db..1352256 100644
--- a/arch/blackfin/include/asm/atomic.h
+++ b/arch/blackfin/include/asm/atomic.h
@@ -89,15 +89,14 @@
 #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
-#define atomic_add_unless(v, a, u)				\
+#define __atomic_add_unless(v, a, u)				\
 ({								\
 	int c, old;						\
 	c = atomic_read(v);					\
 	while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
 		c = old;					\
-	c != (u);						\
+	c;							\
 })
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /*
  * atomic_inc_and_test - increment and test
@@ -112,10 +111,7 @@
 #define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
 #define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
 
-#include <asm-generic/atomic-long.h>
 
 #endif
 
-#include <asm-generic/atomic64.h>
-
 #endif
diff --git a/arch/blackfin/include/asm/dma.h b/arch/blackfin/include/asm/dma.h
index d9dbc1a..dac0c97 100644
--- a/arch/blackfin/include/asm/dma.h
+++ b/arch/blackfin/include/asm/dma.h
@@ -10,7 +10,7 @@
 
 #include <linux/interrupt.h>
 #include <mach/dma.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/blackfin.h>
 #include <asm/page.h>
 #include <asm-generic/dma.h>
diff --git a/arch/blackfin/include/asm/ipipe.h b/arch/blackfin/include/asm/ipipe.h
index 9e0cc0e..17b5e92 100644
--- a/arch/blackfin/include/asm/ipipe.h
+++ b/arch/blackfin/include/asm/ipipe.h
@@ -32,7 +32,7 @@
 #include <asm/ptrace.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/traps.h>
 #include <asm/bitsperlong.h>
 
diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h
index 7854d43..10d8641 100644
--- a/arch/blackfin/include/asm/ptrace.h
+++ b/arch/blackfin/include/asm/ptrace.h
@@ -102,7 +102,6 @@
 /* user_mode returns true if only one bit is set in IPEND, other than the
    master interrupt enable.  */
 #define user_mode(regs) (!(((regs)->ipend & ~0x10) & (((regs)->ipend & ~0x10) - 1)))
-extern void show_regs(struct pt_regs *);
 
 #define arch_has_single_step()	(1)
 /* common code demands this function */
diff --git a/arch/blackfin/include/asm/spinlock.h b/arch/blackfin/include/asm/spinlock.h
index 2336093..490c7ca 100644
--- a/arch/blackfin/include/asm/spinlock.h
+++ b/arch/blackfin/include/asm/spinlock.h
@@ -11,7 +11,7 @@
 # include <asm-generic/spinlock.h>
 #else
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 asmlinkage int __raw_spin_is_locked_asm(volatile int *ptr);
 asmlinkage void __raw_spin_lock_asm(volatile int *ptr);
diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c
index 48808a1..9277905 100644
--- a/arch/blackfin/kernel/ftrace.c
+++ b/arch/blackfin/kernel/ftrace.c
@@ -9,7 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 
 #ifdef CONFIG_DYNAMIC_FTRACE
diff --git a/arch/blackfin/kernel/ipipe.c b/arch/blackfin/kernel/ipipe.c
index 486426f..dbe1122 100644
--- a/arch/blackfin/kernel/ipipe.c
+++ b/arch/blackfin/kernel/ipipe.c
@@ -32,7 +32,7 @@
 #include <linux/unistd.h>
 #include <linux/io.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq_handler.h>
 
 DEFINE_PER_CPU(struct pt_regs, __ipipe_tick_regs);
diff --git a/arch/blackfin/kernel/nmi.c b/arch/blackfin/kernel/nmi.c
index 679d0db..9919d29 100644
--- a/arch/blackfin/kernel/nmi.c
+++ b/arch/blackfin/kernel/nmi.c
@@ -18,7 +18,7 @@
 #include <linux/smp.h>
 #include <linux/timer.h>
 #include <asm/blackfin.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/bfin_watchdog.h>
 
diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c
index 1c143a4..107622a 100644
--- a/arch/blackfin/mach-common/smp.c
+++ b/arch/blackfin/mach-common/smp.c
@@ -23,7 +23,7 @@
 #include <linux/seq_file.h>
 #include <linux/irq.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/irq_handler.h>
 #include <asm/mmu_context.h>
diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c
index c03bc3b..642c6fe 100644
--- a/arch/cris/arch-v32/drivers/cryptocop.c
+++ b/arch/cris/arch-v32/drivers/cryptocop.c
@@ -16,7 +16,7 @@
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/list.h>
 #include <linux/interrupt.h>
diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c
index a0843a7..0b99df7 100644
--- a/arch/cris/arch-v32/kernel/smp.c
+++ b/arch/cris/arch-v32/kernel/smp.c
@@ -7,7 +7,7 @@
 #include <asm/mmu_context.h>
 #include <hwregs/asm/mmu_defs_asm.h>
 #include <hwregs/supp_reg.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/err.h>
 #include <linux/init.h>
diff --git a/arch/cris/include/arch-v10/arch/ptrace.h b/arch/cris/include/arch-v10/arch/ptrace.h
index 2f464ea..1a23273 100644
--- a/arch/cris/include/arch-v10/arch/ptrace.h
+++ b/arch/cris/include/arch-v10/arch/ptrace.h
@@ -112,7 +112,6 @@
 #define user_mode(regs) (((regs)->dccr & 0x100) != 0)
 #define instruction_pointer(regs) ((regs)->irp)
 #define profile_pc(regs) instruction_pointer(regs)
-extern void show_regs(struct pt_regs *);
 
 #endif  /*  __KERNEL__  */
 
diff --git a/arch/cris/include/arch-v32/arch/ptrace.h b/arch/cris/include/arch-v32/arch/ptrace.h
index ffca8d0..19773d3 100644
--- a/arch/cris/include/arch-v32/arch/ptrace.h
+++ b/arch/cris/include/arch-v32/arch/ptrace.h
@@ -111,7 +111,6 @@
 #define arch_has_single_step() (1)
 #define user_mode(regs) (((regs)->ccs & (1 << (U_CCS_BITNR + CCS_SHIFT))) != 0)
 #define instruction_pointer(regs) ((regs)->erp)
-extern void show_regs(struct pt_regs *);
 #define profile_pc(regs) instruction_pointer(regs)
 
 #endif  /*  __KERNEL__  */
diff --git a/arch/cris/include/asm/atomic.h b/arch/cris/include/asm/atomic.h
index 88dc9b9..bbf0938 100644
--- a/arch/cris/include/asm/atomic.h
+++ b/arch/cris/include/asm/atomic.h
@@ -138,7 +138,7 @@
 
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int ret;
 	unsigned long flags;
@@ -148,9 +148,8 @@
 	if (ret != u)
 		v->counter += a;
 	cris_atomic_restore(v, flags);
-	return ret != u;
+	return ret;
 }
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /* Atomic operations are already serializing */
 #define smp_mb__before_atomic_dec()    barrier()
@@ -158,5 +157,4 @@
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif
diff --git a/arch/cris/include/asm/bitops.h b/arch/cris/include/asm/bitops.h
index 310e0de..a78a2d7 100644
--- a/arch/cris/include/asm/bitops.h
+++ b/arch/cris/include/asm/bitops.h
@@ -20,7 +20,7 @@
 
 #include <arch/bitops.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/compiler.h>
 
 /*
@@ -156,8 +156,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(l,n,a)   test_and_set_bit(n,a)
-#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #include <asm-generic/bitops/sched.h>
 
diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c
index c99aeab..aa585e4 100644
--- a/arch/cris/kernel/process.c
+++ b/arch/cris/kernel/process.c
@@ -12,7 +12,7 @@
  * This file handles the architecture-dependent parts of process handling..
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
diff --git a/arch/frv/include/asm/atomic.h b/arch/frv/include/asm/atomic.h
index fae32c7..0d8a7d6 100644
--- a/arch/frv/include/asm/atomic.h
+++ b/arch/frv/include/asm/atomic.h
@@ -241,7 +241,7 @@
 #define atomic64_cmpxchg(v, old, new)	(__cmpxchg_64(old, new, &(v)->counter))
 #define atomic64_xchg(v, new)		(__xchg_64(new, &(v)->counter))
 
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -253,10 +253,8 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
-#include <asm-generic/atomic-long.h>
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/frv/include/asm/bitops.h b/arch/frv/include/asm/bitops.h
index a1d00b0..57bf85d 100644
--- a/arch/frv/include/asm/bitops.h
+++ b/arch/frv/include/asm/bitops.h
@@ -403,8 +403,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(lock,nr,addr)	test_and_set_bit  ((nr) ^ 0x18, (addr))
-#define ext2_clear_bit_atomic(lock,nr,addr)	test_and_clear_bit((nr) ^ 0x18, (addr))
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* __KERNEL__ */
 
diff --git a/arch/frv/include/asm/hardirq.h b/arch/frv/include/asm/hardirq.h
index 5fc8b6f..c62833d 100644
--- a/arch/frv/include/asm/hardirq.h
+++ b/arch/frv/include/asm/hardirq.h
@@ -12,7 +12,7 @@
 #ifndef __ASM_HARDIRQ_H
 #define __ASM_HARDIRQ_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern atomic_t irq_err_count;
 static inline void ack_bad_irq(int irq)
diff --git a/arch/frv/include/asm/processor.h b/arch/frv/include/asm/processor.h
index 4b789ab..81c2e27 100644
--- a/arch/frv/include/asm/processor.h
+++ b/arch/frv/include/asm/processor.h
@@ -97,7 +97,6 @@
  */
 #define start_thread(_regs, _pc, _usp)			\
 do {							\
-	set_fs(USER_DS); /* reads from user space */	\
 	__frame = __kernel_frame0_ptr;			\
 	__frame->pc	= (_pc);			\
 	__frame->psr	&= ~PSR_S;			\
diff --git a/arch/frv/include/asm/ptrace.h b/arch/frv/include/asm/ptrace.h
index 6bfad4c..ef6635c 100644
--- a/arch/frv/include/asm/ptrace.h
+++ b/arch/frv/include/asm/ptrace.h
@@ -78,7 +78,6 @@
 #define user_stack_pointer(regs)	((regs)->sp)
 
 extern unsigned long user_stack(const struct pt_regs *);
-extern void show_regs(struct pt_regs *);
 #define profile_pc(regs) ((regs)->pc)
 
 #define task_pt_regs(task) ((task)->thread.frame0)
diff --git a/arch/frv/kernel/irq.c b/arch/frv/kernel/irq.c
index a5f624a..3facbc2 100644
--- a/arch/frv/kernel/irq.c
+++ b/arch/frv/kernel/irq.c
@@ -25,7 +25,7 @@
 #include <linux/module.h>
 #include <linux/bitops.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/system.h>
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
index 9d35975..3901df1 100644
--- a/arch/frv/kernel/process.c
+++ b/arch/frv/kernel/process.c
@@ -143,10 +143,7 @@
 
 void flush_thread(void)
 {
-#if 0 //ndef NO_FPU
-	unsigned long zero = 0;
-#endif
-	set_fs(USER_DS);
+	/* nothing */
 }
 
 inline unsigned long user_stack(const struct pt_regs *regs)
diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c
index c42c83d..4fb63a3 100644
--- a/arch/frv/mm/pgalloc.c
+++ b/arch/frv/mm/pgalloc.c
@@ -133,13 +133,7 @@
 
 pgd_t *pgd_alloc(struct mm_struct *mm)
 {
-	pgd_t *pgd;
-
-	pgd = quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
-	if (!pgd)
-		return pgd;
-
-	return pgd;
+	return quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
 }
 
 void pgd_free(struct mm_struct *mm, pgd_t *pgd)
diff --git a/arch/h8300/include/asm/atomic.h b/arch/h8300/include/asm/atomic.h
index 984221a..f5a38c1 100644
--- a/arch/h8300/include/asm/atomic.h
+++ b/arch/h8300/include/asm/atomic.h
@@ -104,7 +104,7 @@
 
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int ret;
 	unsigned long flags;
@@ -114,9 +114,8 @@
 	if (ret != u)
 		v->counter += a;
 	local_irq_restore(flags);
-	return ret != u;
+	return ret;
 }
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v)
 {
@@ -146,5 +145,4 @@
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc() barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif /* __ARCH_H8300_ATOMIC __ */
diff --git a/arch/h8300/include/asm/posix_types.h b/arch/h8300/include/asm/posix_types.h
index 5c55392..6f833a1 100644
--- a/arch/h8300/include/asm/posix_types.h
+++ b/arch/h8300/include/asm/posix_types.h
@@ -50,7 +50,7 @@
 #define	__FD_CLR(d, set)	((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d))
 
 #undef	__FD_ISSET
-#define	__FD_ISSET(d, set)	((set)->fds_bits[__FDELT(d)] & __FDMASK(d))
+#define	__FD_ISSET(d, set)	(!!((set)->fds_bits[__FDELT(d)] & __FDMASK(d)))
 
 #undef	__FD_ZERO
 #define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof(*(fd_set *)fdsetp)))
diff --git a/arch/h8300/include/asm/ptrace.h b/arch/h8300/include/asm/ptrace.h
index d866c0e..d09c440 100644
--- a/arch/h8300/include/asm/ptrace.h
+++ b/arch/h8300/include/asm/ptrace.h
@@ -60,7 +60,6 @@
 #define user_mode(regs) (!((regs)->ccr & PS_S))
 #define instruction_pointer(regs) ((regs)->pc)
 #define profile_pc(regs) instruction_pointer(regs)
-extern void show_regs(struct pt_regs *);
 #endif /* __KERNEL__ */
 #endif /* __ASSEMBLY__ */
 #endif /* _H8300_PTRACE_H */
diff --git a/arch/ia64/include/asm/atomic.h b/arch/ia64/include/asm/atomic.h
index 4468814..3fad89e 100644
--- a/arch/ia64/include/asm/atomic.h
+++ b/arch/ia64/include/asm/atomic.h
@@ -90,7 +90,7 @@
 	(cmpxchg(&((v)->counter), old, new))
 #define atomic64_xchg(v, new) (xchg(&((v)->counter), new))
 
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -102,10 +102,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 static __inline__ long atomic64_add_unless(atomic64_t *v, long a, long u)
 {
@@ -216,5 +215,4 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif /* _ASM_IA64_ATOMIC_H */
diff --git a/arch/ia64/include/asm/bitops.h b/arch/ia64/include/asm/bitops.h
index b76f7e0..8e20bff 100644
--- a/arch/ia64/include/asm/bitops.h
+++ b/arch/ia64/include/asm/bitops.h
@@ -458,8 +458,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(l,n,a)	test_and_set_bit(n,a)
-#define ext2_clear_bit_atomic(l,n,a)	test_and_clear_bit(n,a)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #include <asm-generic/bitops/sched.h>
 
diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h
index 03afe79..d9f397f 100644
--- a/arch/ia64/include/asm/processor.h
+++ b/arch/ia64/include/asm/processor.h
@@ -75,7 +75,7 @@
 #include <asm/percpu.h>
 #include <asm/rse.h>
 #include <asm/unwind.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #ifdef CONFIG_NUMA
 #include <asm/nodedata.h>
 #endif
diff --git a/arch/ia64/include/asm/ptrace.h b/arch/ia64/include/asm/ptrace.h
index 7ae9c3f..f5cb276 100644
--- a/arch/ia64/include/asm/ptrace.h
+++ b/arch/ia64/include/asm/ptrace.h
@@ -286,7 +286,6 @@
   struct task_struct;			/* forward decl */
   struct unw_frame_info;		/* forward decl */
 
-  extern void show_regs (struct pt_regs *);
   extern void ia64_do_show_stack (struct unw_frame_info *, void *);
   extern unsigned long ia64_get_user_rbs_end (struct task_struct *, struct pt_regs *,
 					      unsigned long *);
diff --git a/arch/ia64/include/asm/spinlock.h b/arch/ia64/include/asm/spinlock.h
index 1a91c91..b77768d 100644
--- a/arch/ia64/include/asm/spinlock.h
+++ b/arch/ia64/include/asm/spinlock.h
@@ -13,7 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/bitops.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/intrinsics.h>
 #include <asm/system.h>
 
diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c
index be450a3..0bd537b 100644
--- a/arch/ia64/kernel/smp.c
+++ b/arch/ia64/kernel/smp.c
@@ -32,7 +32,7 @@
 #include <linux/bitops.h>
 #include <linux/kexec.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/current.h>
 #include <asm/delay.h>
 #include <asm/machvec.h>
diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c
index 14ec641..5590979 100644
--- a/arch/ia64/kernel/smpboot.c
+++ b/arch/ia64/kernel/smpboot.c
@@ -40,7 +40,7 @@
 #include <linux/percpu.h>
 #include <linux/bitops.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cache.h>
 #include <asm/current.h>
 #include <asm/delay.h>
diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c
index c4696d2..6a867dc 100644
--- a/arch/ia64/kernel/uncached.c
+++ b/arch/ia64/kernel/uncached.c
@@ -25,7 +25,7 @@
 #include <asm/pal.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/tlbflush.h>
 #include <asm/sn/arch.h>
 
diff --git a/arch/m32r/include/asm/atomic.h b/arch/m32r/include/asm/atomic.h
index d44a51e..1e7f29f 100644
--- a/arch/m32r/include/asm/atomic.h
+++ b/arch/m32r/include/asm/atomic.h
@@ -239,15 +239,15 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -259,10 +259,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 static __inline__ void atomic_clear_mask(unsigned long  mask, atomic_t *addr)
 {
@@ -314,5 +313,4 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif	/* _ASM_M32R_ATOMIC_H */
diff --git a/arch/m32r/include/asm/mmu_context.h b/arch/m32r/include/asm/mmu_context.h
index a70a3df..a979a41 100644
--- a/arch/m32r/include/asm/mmu_context.h
+++ b/arch/m32r/include/asm/mmu_context.h
@@ -11,7 +11,7 @@
 
 #ifndef __ASSEMBLY__
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgalloc.h>
 #include <asm/mmu.h>
 #include <asm/tlbflush.h>
diff --git a/arch/m32r/include/asm/ptrace.h b/arch/m32r/include/asm/ptrace.h
index 840a123..5275275 100644
--- a/arch/m32r/include/asm/ptrace.h
+++ b/arch/m32r/include/asm/ptrace.h
@@ -138,8 +138,6 @@
 #define instruction_pointer(regs) ((regs)->bpc)
 #define profile_pc(regs) instruction_pointer(regs)
 
-extern void show_regs(struct pt_regs *);
-
 extern void withdraw_debug_trap(struct pt_regs *regs);
 
 #define task_pt_regs(task) \
diff --git a/arch/m32r/include/asm/spinlock.h b/arch/m32r/include/asm/spinlock.h
index 179a064..b0ea2f2 100644
--- a/arch/m32r/include/asm/spinlock.h
+++ b/arch/m32r/include/asm/spinlock.h
@@ -10,7 +10,7 @@
  */
 
 #include <linux/compiler.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 
 /*
diff --git a/arch/m32r/kernel/smp.c b/arch/m32r/kernel/smp.c
index 092d40a..ce7aea3 100644
--- a/arch/m32r/kernel/smp.c
+++ b/arch/m32r/kernel/smp.c
@@ -26,7 +26,7 @@
 
 #include <asm/cacheflush.h>
 #include <asm/pgalloc.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/mmu_context.h>
 #include <asm/m32r.h>
diff --git a/arch/m32r/kernel/traps.c b/arch/m32r/kernel/traps.c
index fbd1090..ee6a919 100644
--- a/arch/m32r/kernel/traps.c
+++ b/arch/m32r/kernel/traps.c
@@ -21,7 +21,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <asm/smp.h>
 
diff --git a/arch/m68k/include/asm/atomic.h b/arch/m68k/include/asm/atomic.h
index 307a573..65c6be6 100644
--- a/arch/m68k/include/asm/atomic.h
+++ b/arch/m68k/include/asm/atomic.h
@@ -183,7 +183,7 @@
 	__asm__ __volatile__("orl %1,%0" : "+m" (*v) : ASM_DI (mask));
 }
 
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -195,10 +195,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /* Atomic operations are already serializing */
 #define smp_mb__before_atomic_dec()	barrier()
@@ -206,6 +205,4 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
-#include <asm-generic/atomic64.h>
 #endif /* __ARCH_M68K_ATOMIC __ */
diff --git a/arch/m68k/include/asm/posix_types.h b/arch/m68k/include/asm/posix_types.h
index 63cdcc1..98d0970 100644
--- a/arch/m68k/include/asm/posix_types.h
+++ b/arch/m68k/include/asm/posix_types.h
@@ -51,7 +51,7 @@
 #define	__FD_CLR(d, set)	((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d))
 
 #undef	__FD_ISSET
-#define	__FD_ISSET(d, set)	((set)->fds_bits[__FDELT(d)] & __FDMASK(d))
+#define	__FD_ISSET(d, set)	(!!((set)->fds_bits[__FDELT(d)] & __FDMASK(d)))
 
 #undef	__FD_ZERO
 #define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof(*(fd_set *)fdsetp)))
diff --git a/arch/m68k/include/asm/ptrace.h b/arch/m68k/include/asm/ptrace.h
index 6e6e3ac..65322b1 100644
--- a/arch/m68k/include/asm/ptrace.h
+++ b/arch/m68k/include/asm/ptrace.h
@@ -85,7 +85,6 @@
 #define user_mode(regs) (!((regs)->sr & PS_S))
 #define instruction_pointer(regs) ((regs)->pc)
 #define profile_pc(regs) instruction_pointer(regs)
-extern void show_regs(struct pt_regs *);
 
 #define arch_has_single_step()	(1)
 
diff --git a/arch/microblaze/include/asm/cpuinfo.h b/arch/microblaze/include/asm/cpuinfo.h
index d8f0133..7d6831a 100644
--- a/arch/microblaze/include/asm/cpuinfo.h
+++ b/arch/microblaze/include/asm/cpuinfo.h
@@ -38,6 +38,7 @@
 	u32 use_exc;
 	u32 ver_code;
 	u32 mmu;
+	u32 mmu_privins;
 	u32 endian;
 
 	/* CPU caches */
diff --git a/arch/microblaze/include/asm/irqflags.h b/arch/microblaze/include/asm/irqflags.h
index c4532f0..c9a6262 100644
--- a/arch/microblaze/include/asm/irqflags.h
+++ b/arch/microblaze/include/asm/irqflags.h
@@ -14,7 +14,7 @@
 
 #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
 
-static inline unsigned long arch_local_irq_save(void)
+static inline notrace unsigned long arch_local_irq_save(void)
 {
 	unsigned long flags;
 	asm volatile("	msrclr %0, %1	\n"
@@ -25,7 +25,7 @@
 	return flags;
 }
 
-static inline void arch_local_irq_disable(void)
+static inline notrace void arch_local_irq_disable(void)
 {
 	/* this uses r0 without declaring it - is that correct? */
 	asm volatile("	msrclr r0, %0	\n"
@@ -35,7 +35,7 @@
 		     : "memory");
 }
 
-static inline void arch_local_irq_enable(void)
+static inline notrace void arch_local_irq_enable(void)
 {
 	/* this uses r0 without declaring it - is that correct? */
 	asm volatile("	msrset	r0, %0	\n"
@@ -47,7 +47,7 @@
 
 #else /* !CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR */
 
-static inline unsigned long arch_local_irq_save(void)
+static inline notrace unsigned long arch_local_irq_save(void)
 {
 	unsigned long flags, tmp;
 	asm volatile ("	mfs	%0, rmsr	\n"
@@ -61,7 +61,7 @@
 	return flags;
 }
 
-static inline void arch_local_irq_disable(void)
+static inline notrace void arch_local_irq_disable(void)
 {
 	unsigned long tmp;
 	asm volatile("	mfs	%0, rmsr	\n"
@@ -74,7 +74,7 @@
 		     : "memory");
 }
 
-static inline void arch_local_irq_enable(void)
+static inline notrace void arch_local_irq_enable(void)
 {
 	unsigned long tmp;
 	asm volatile("	mfs	%0, rmsr	\n"
@@ -89,7 +89,7 @@
 
 #endif /* CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR */
 
-static inline unsigned long arch_local_save_flags(void)
+static inline notrace unsigned long arch_local_save_flags(void)
 {
 	unsigned long flags;
 	asm volatile("	mfs	%0, rmsr	\n"
@@ -100,7 +100,7 @@
 	return flags;
 }
 
-static inline void arch_local_irq_restore(unsigned long flags)
+static inline notrace void arch_local_irq_restore(unsigned long flags)
 {
 	asm volatile("	mts	rmsr, %0	\n"
 		     "	nop			\n"
@@ -109,12 +109,12 @@
 		     : "memory");
 }
 
-static inline bool arch_irqs_disabled_flags(unsigned long flags)
+static inline notrace bool arch_irqs_disabled_flags(unsigned long flags)
 {
 	return (flags & MSR_IE) == 0;
 }
 
-static inline bool arch_irqs_disabled(void)
+static inline notrace bool arch_irqs_disabled(void)
 {
 	return arch_irqs_disabled_flags(arch_local_save_flags());
 }
diff --git a/arch/microblaze/include/asm/mmu_context_mm.h b/arch/microblaze/include/asm/mmu_context_mm.h
index 3e5c254..d686477 100644
--- a/arch/microblaze/include/asm/mmu_context_mm.h
+++ b/arch/microblaze/include/asm/mmu_context_mm.h
@@ -11,7 +11,7 @@
 #ifndef _ASM_MICROBLAZE_MMU_CONTEXT_H
 #define _ASM_MICROBLAZE_MMU_CONTEXT_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/bitops.h>
 #include <asm/mmu.h>
 #include <asm-generic/mm_hooks.h>
diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h
index 242be57..32764cd 100644
--- a/arch/microblaze/include/asm/pci-bridge.h
+++ b/arch/microblaze/include/asm/pci-bridge.h
@@ -10,28 +10,19 @@
 #include <linux/pci.h>
 #include <linux/list.h>
 #include <linux/ioport.h>
+#include <asm-generic/pci-bridge.h>
 
 struct device_node;
 
-enum {
-	/* Force re-assigning all resources (ignore firmware
-	 * setup completely)
-	 */
-	PCI_REASSIGN_ALL_RSRC	= 0x00000001,
-
-	/* Do not try to assign, just use existing setup */
-	PCI_PROBE_ONLY		= 0x00000004,
-
-	/* Don't bother with ISA alignment unless the bridge has
-	 * ISA forwarding enabled
-	 */
-	PCI_CAN_SKIP_ISA_ALIGN	= 0x00000008,
-
-	/* Enable domain numbers in /proc */
-	PCI_ENABLE_PROC_DOMAINS	= 0x00000010,
-	/* ... except for domain 0 */
-	PCI_COMPAT_DOMAIN_0		= 0x00000020,
-};
+#ifdef CONFIG_PCI
+extern struct list_head hose_list;
+extern int pcibios_vaddr_is_ioport(void __iomem *address);
+#else
+static inline int pcibios_vaddr_is_ioport(void __iomem *address)
+{
+	return 0;
+}
+#endif
 
 /*
  * Structure of a PCI controller (host bridge)
@@ -151,40 +142,5 @@
 extern void pcibios_free_controller(struct pci_controller *phb);
 extern void pcibios_setup_phb_resources(struct pci_controller *hose);
 
-#ifdef CONFIG_PCI
-extern unsigned int pci_flags;
-
-static inline void pci_set_flags(int flags)
-{
-	pci_flags = flags;
-}
-
-static inline void pci_add_flags(int flags)
-{
-	pci_flags |= flags;
-}
-
-static inline int pci_has_flag(int flag)
-{
-	return pci_flags & flag;
-}
-
-extern struct list_head hose_list;
-
-extern int pcibios_vaddr_is_ioport(void __iomem *address);
-#else
-static inline int pcibios_vaddr_is_ioport(void __iomem *address)
-{
-	return 0;
-}
-
-static inline void pci_set_flags(int flags) { }
-static inline void pci_add_flags(int flags) { }
-static inline int pci_has_flag(int flag)
-{
-	return 0;
-}
-#endif	/* CONFIG_PCI */
-
 #endif	/* __KERNEL__ */
 #endif	/* _ASM_MICROBLAZE_PCI_BRIDGE_H */
diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h
index aed2a6b..7283bfb 100644
--- a/arch/microblaze/include/asm/processor.h
+++ b/arch/microblaze/include/asm/processor.h
@@ -125,9 +125,6 @@
 	.pgdir = swapper_pg_dir, \
 }
 
-/* Do necessary setup to start up a newly executed thread.  */
-void start_thread(struct pt_regs *regs,
-		unsigned long pc, unsigned long usp);
 
 /* Free all resources held by a thread. */
 extern inline void release_thread(struct task_struct *dead_task)
diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h
index 9bd01ec..20c5e8e 100644
--- a/arch/microblaze/include/asm/prom.h
+++ b/arch/microblaze/include/asm/prom.h
@@ -21,13 +21,17 @@
 
 #include <linux/types.h>
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define HAVE_ARCH_DEVTREE_FIXUPS
 
 /* Other Prototypes */
-extern int early_uartlite_console(void);
-extern int early_uart16550_console(void);
+enum early_consoles {
+	UARTLITE = 1,
+	UART16550 = 2,
+};
+
+extern int of_early_console(void *version);
 
 /*
  * OF address retreival & translation
diff --git a/arch/microblaze/include/asm/ptrace.h b/arch/microblaze/include/asm/ptrace.h
index d9b6630..816bee6 100644
--- a/arch/microblaze/include/asm/ptrace.h
+++ b/arch/microblaze/include/asm/ptrace.h
@@ -61,8 +61,6 @@
 #define instruction_pointer(regs)	((regs)->pc)
 #define profile_pc(regs)		instruction_pointer(regs)
 
-void show_regs(struct pt_regs *);
-
 #else /* __KERNEL__ */
 
 /* pt_regs offsets used by gdbserver etc in ptrace syscalls */
diff --git a/arch/microblaze/include/asm/pvr.h b/arch/microblaze/include/asm/pvr.h
index a10bec6..4bbdb4c 100644
--- a/arch/microblaze/include/asm/pvr.h
+++ b/arch/microblaze/include/asm/pvr.h
@@ -111,16 +111,16 @@
 /* Target family PVR mask */
 #define PVR10_TARGET_FAMILY_MASK	0xFF000000
 
-/* MMU descrtiption */
+/* MMU description */
 #define PVR11_USE_MMU			0xC0000000
 #define PVR11_MMU_ITLB_SIZE		0x38000000
 #define PVR11_MMU_DTLB_SIZE		0x07000000
 #define PVR11_MMU_TLB_ACCESS		0x00C00000
 #define PVR11_MMU_ZONES			0x003C0000
+#define PVR11_MMU_PRIVINS		0x00010000
 /* MSR Reset value PVR mask */
 #define PVR11_MSR_RESET_VALUE_MASK	0x000007FF
 
-
 /* PVR access macros */
 #define PVR_IS_FULL(_pvr)	(_pvr.pvr[0] & PVR0_PVR_FULL_MASK)
 #define PVR_USE_BARREL(_pvr)	(_pvr.pvr[0] & PVR0_USE_BARREL_MASK)
@@ -216,6 +216,7 @@
 #define PVR_MMU_DTLB_SIZE(_pvr)		(_pvr.pvr[11] & PVR11_MMU_DTLB_SIZE)
 #define PVR_MMU_TLB_ACCESS(_pvr)	(_pvr.pvr[11] & PVR11_MMU_TLB_ACCESS)
 #define PVR_MMU_ZONES(_pvr)		(_pvr.pvr[11] & PVR11_MMU_ZONES)
+#define PVR_MMU_PRIVINS(pvr)		(pvr.pvr[11] & PVR11_MMU_PRIVINS)
 
 /* endian */
 #define PVR_ENDIAN(_pvr)	(_pvr.pvr[0] & PVR0_ENDI)
diff --git a/arch/microblaze/include/asm/setup.h b/arch/microblaze/include/asm/setup.h
index 8f39689..904e5ef 100644
--- a/arch/microblaze/include/asm/setup.h
+++ b/arch/microblaze/include/asm/setup.h
@@ -23,6 +23,7 @@
 void early_printk(const char *fmt, ...);
 
 int setup_early_printk(char *opt);
+void remap_early_printk(void);
 void disable_early_printk(void);
 
 #if defined(CONFIG_EARLY_PRINTK)
diff --git a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
index f70a604..916aaed 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
@@ -72,6 +72,7 @@
 	CI(pvr_user2, USER2);
 
 	CI(mmu, USE_MMU);
+	CI(mmu_privins, MMU_PRIVINS);
 	CI(endian, ENDIAN);
 
 	CI(use_icache, USE_ICACHE);
diff --git a/arch/microblaze/kernel/cpu/cpuinfo-static.c b/arch/microblaze/kernel/cpu/cpuinfo-static.c
index b16b994..592bb2e 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo-static.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo-static.c
@@ -119,6 +119,7 @@
 	ci->pvr_user2 = fcpu(cpu, "xlnx,pvr-user2");
 
 	ci->mmu = fcpu(cpu, "xlnx,use-mmu");
+	ci->mmu_privins = fcpu(cpu, "xlnx,mmu-privileged-instr");
 	ci->endian = fcpu(cpu, "xlnx,endianness");
 
 	ci->ver_code = 0;
diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c
index c1640c5..44394d8 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo.c
@@ -88,4 +88,8 @@
 		printk(KERN_WARNING "%s: Unsupported PVR setting\n", __func__);
 		set_cpuinfo_static(&cpuinfo, cpu);
 	}
+
+	if (cpuinfo.mmu_privins)
+		printk(KERN_WARNING "%s: Stream instructions enabled"
+			" - USERSPACE CAN LOCK THIS KERNEL!\n", __func__);
 }
diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c
index b4048af..7b5dca7 100644
--- a/arch/microblaze/kernel/cpu/mb.c
+++ b/arch/microblaze/kernel/cpu/mb.c
@@ -97,6 +97,10 @@
 		(cpuinfo.use_exc & PVR2_FPU_EXC_MASK) ? "fpu " : "",
 		(cpuinfo.use_exc & PVR2_USE_FSL_EXC) ? "fsl " : "");
 
+	count += seq_printf(m,
+			"Stream-insns:\t%sprivileged\n",
+			cpuinfo.mmu_privins ? "un" : "");
+
 	if (cpuinfo.use_icache)
 		count += seq_printf(m,
 				"Icache:\t\t%ukB\tline length:\t%dB\n",
@@ -110,10 +114,11 @@
 				"Dcache:\t\t%ukB\tline length:\t%dB\n",
 				cpuinfo.dcache_size >> 10,
 				cpuinfo.dcache_line_length);
+		seq_printf(m, "Dcache-Policy:\t");
 		if (cpuinfo.dcache_wb)
-			count += seq_printf(m, "\t\twrite-back\n");
+			count += seq_printf(m, "write-back\n");
 		else
-			count += seq_printf(m, "\t\twrite-through\n");
+			count += seq_printf(m, "write-through\n");
 	} else
 		count += seq_printf(m, "Dcache:\t\tno\n");
 
diff --git a/arch/microblaze/kernel/early_printk.c b/arch/microblaze/kernel/early_printk.c
index c3616a0..d26d92d 100644
--- a/arch/microblaze/kernel/early_printk.c
+++ b/arch/microblaze/kernel/early_printk.c
@@ -35,7 +35,7 @@
 	 * we'll never timeout on a working UART.
 	 */
 
-	unsigned retries = 10000;
+	unsigned retries = 1000000;
 	/* read status bit - 0x8 offset */
 	while (--retries && (in_be32(base_addr + 8) & (1 << 3)))
 		;
@@ -60,7 +60,7 @@
 static struct console early_serial_uartlite_console = {
 	.name = "earlyser",
 	.write = early_printk_uartlite_write,
-	.flags = CON_PRINTBUFFER,
+	.flags = CON_PRINTBUFFER | CON_BOOT,
 	.index = -1,
 };
 #endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
@@ -104,7 +104,7 @@
 static struct console early_serial_uart16550_console = {
 	.name = "earlyser",
 	.write = early_printk_uart16550_write,
-	.flags = CON_PRINTBUFFER,
+	.flags = CON_PRINTBUFFER | CON_BOOT,
 	.index = -1,
 };
 #endif /* CONFIG_SERIAL_8250_CONSOLE */
@@ -127,48 +127,56 @@
 
 int __init setup_early_printk(char *opt)
 {
+	int version = 0;
+
 	if (early_console_initialized)
 		return 1;
 
+	base_addr = of_early_console(&version);
+	if (base_addr) {
+#ifdef CONFIG_MMU
+		early_console_reg_tlb_alloc(base_addr);
+#endif
+		switch (version) {
 #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
-	base_addr = early_uartlite_console();
-	if (base_addr) {
-		early_console_initialized = 1;
-#ifdef CONFIG_MMU
-		early_console_reg_tlb_alloc(base_addr);
+		case UARTLITE:
+			printk(KERN_INFO "Early console on uartlite "
+						"at 0x%08x\n", base_addr);
+			early_console = &early_serial_uartlite_console;
+			break;
 #endif
-		early_console = &early_serial_uartlite_console;
-		early_printk("early_printk_console is enabled at 0x%08x\n",
-							base_addr);
-
-		/* register_console(early_console); */
-
-		return 0;
-	}
-#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
-
 #ifdef CONFIG_SERIAL_8250_CONSOLE
-	base_addr = early_uart16550_console();
-	base_addr &= ~3; /* clear register offset */
-	if (base_addr) {
-		early_console_initialized = 1;
-#ifdef CONFIG_MMU
-		early_console_reg_tlb_alloc(base_addr);
+		case UART16550:
+			printk(KERN_INFO "Early console on uart16650 "
+						"at 0x%08x\n", base_addr);
+			early_console = &early_serial_uart16550_console;
+			break;
 #endif
-		early_console = &early_serial_uart16550_console;
+		default:
+			printk(KERN_INFO  "Unsupported early console %d\n",
+								version);
+			return 1;
+		}
 
-		early_printk("early_printk_console is enabled at 0x%08x\n",
-							base_addr);
-
-		/* register_console(early_console); */
-
+		register_console(early_console);
+		early_console_initialized = 1;
 		return 0;
 	}
-#endif /* CONFIG_SERIAL_8250_CONSOLE */
-
 	return 1;
 }
 
+/* Remap early console to virtual address and do not allocate one TLB
+ * only for early console because of performance degression */
+void __init remap_early_printk(void)
+{
+	if (!early_console_initialized || !early_console)
+		return;
+	printk(KERN_INFO "early_printk_console remaping from 0x%x to ",
+								base_addr);
+	base_addr = (u32) ioremap(base_addr, PAGE_SIZE);
+	printk(KERN_CONT "0x%x\n", base_addr);
+}
+
 void __init disable_early_printk(void)
 {
 	if (!early_console_initialized || !early_console)
diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S
index 56572e9..e62be83 100644
--- a/arch/microblaze/kernel/hw_exception_handler.S
+++ b/arch/microblaze/kernel/hw_exception_handler.S
@@ -1113,23 +1113,23 @@
 lw_r11_vm:	R3_TO_LWREG_VM_V	(11);
 lw_r12_vm:	R3_TO_LWREG_VM_V	(12);
 lw_r13_vm:	R3_TO_LWREG_VM_V	(13);
-lw_r14_vm:	R3_TO_LWREG_VM		(14);
+lw_r14_vm:	R3_TO_LWREG_VM_V	(14);
 lw_r15_vm:	R3_TO_LWREG_VM_V	(15);
-lw_r16_vm:	R3_TO_LWREG_VM		(16);
+lw_r16_vm:	R3_TO_LWREG_VM_V	(16);
 lw_r17_vm:	R3_TO_LWREG_VM_V	(17);
 lw_r18_vm:	R3_TO_LWREG_VM_V	(18);
-lw_r19_vm:	R3_TO_LWREG_VM		(19);
-lw_r20_vm:	R3_TO_LWREG_VM		(20);
-lw_r21_vm:	R3_TO_LWREG_VM		(21);
-lw_r22_vm:	R3_TO_LWREG_VM		(22);
-lw_r23_vm:	R3_TO_LWREG_VM		(23);
-lw_r24_vm:	R3_TO_LWREG_VM		(24);
-lw_r25_vm:	R3_TO_LWREG_VM		(25);
-lw_r26_vm:	R3_TO_LWREG_VM		(26);
-lw_r27_vm:	R3_TO_LWREG_VM		(27);
-lw_r28_vm:	R3_TO_LWREG_VM		(28);
-lw_r29_vm:	R3_TO_LWREG_VM		(29);
-lw_r30_vm:	R3_TO_LWREG_VM		(30);
+lw_r19_vm:	R3_TO_LWREG_VM_V	(19);
+lw_r20_vm:	R3_TO_LWREG_VM_V	(20);
+lw_r21_vm:	R3_TO_LWREG_VM_V	(21);
+lw_r22_vm:	R3_TO_LWREG_VM_V	(22);
+lw_r23_vm:	R3_TO_LWREG_VM_V	(23);
+lw_r24_vm:	R3_TO_LWREG_VM_V	(24);
+lw_r25_vm:	R3_TO_LWREG_VM_V	(25);
+lw_r26_vm:	R3_TO_LWREG_VM_V	(26);
+lw_r27_vm:	R3_TO_LWREG_VM_V	(27);
+lw_r28_vm:	R3_TO_LWREG_VM_V	(28);
+lw_r29_vm:	R3_TO_LWREG_VM_V	(29);
+lw_r30_vm:	R3_TO_LWREG_VM_V	(30);
 lw_r31_vm:	R3_TO_LWREG_VM_V	(31);
 
 sw_table_vm:
@@ -1147,23 +1147,23 @@
 sw_r11_vm:	SWREG_TO_R3_VM_V	(11);
 sw_r12_vm:	SWREG_TO_R3_VM_V	(12);
 sw_r13_vm:	SWREG_TO_R3_VM_V	(13);
-sw_r14_vm:	SWREG_TO_R3_VM		(14);
+sw_r14_vm:	SWREG_TO_R3_VM_V	(14);
 sw_r15_vm:	SWREG_TO_R3_VM_V	(15);
-sw_r16_vm:	SWREG_TO_R3_VM		(16);
+sw_r16_vm:	SWREG_TO_R3_VM_V	(16);
 sw_r17_vm:	SWREG_TO_R3_VM_V	(17);
 sw_r18_vm:	SWREG_TO_R3_VM_V	(18);
-sw_r19_vm:	SWREG_TO_R3_VM		(19);
-sw_r20_vm:	SWREG_TO_R3_VM		(20);
-sw_r21_vm:	SWREG_TO_R3_VM		(21);
-sw_r22_vm:	SWREG_TO_R3_VM		(22);
-sw_r23_vm:	SWREG_TO_R3_VM		(23);
-sw_r24_vm:	SWREG_TO_R3_VM		(24);
-sw_r25_vm:	SWREG_TO_R3_VM		(25);
-sw_r26_vm:	SWREG_TO_R3_VM		(26);
-sw_r27_vm:	SWREG_TO_R3_VM		(27);
-sw_r28_vm:	SWREG_TO_R3_VM		(28);
-sw_r29_vm:	SWREG_TO_R3_VM		(29);
-sw_r30_vm:	SWREG_TO_R3_VM		(30);
+sw_r19_vm:	SWREG_TO_R3_VM_V	(19);
+sw_r20_vm:	SWREG_TO_R3_VM_V	(20);
+sw_r21_vm:	SWREG_TO_R3_VM_V	(21);
+sw_r22_vm:	SWREG_TO_R3_VM_V	(22);
+sw_r23_vm:	SWREG_TO_R3_VM_V	(23);
+sw_r24_vm:	SWREG_TO_R3_VM_V	(24);
+sw_r25_vm:	SWREG_TO_R3_VM_V	(25);
+sw_r26_vm:	SWREG_TO_R3_VM_V	(26);
+sw_r27_vm:	SWREG_TO_R3_VM_V	(27);
+sw_r28_vm:	SWREG_TO_R3_VM_V	(28);
+sw_r29_vm:	SWREG_TO_R3_VM_V	(29);
+sw_r30_vm:	SWREG_TO_R3_VM_V	(30);
 sw_r31_vm:	SWREG_TO_R3_VM_V	(31);
 #endif /* CONFIG_MMU */
 
diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c
index c88f066..eb41441 100644
--- a/arch/microblaze/kernel/intc.c
+++ b/arch/microblaze/kernel/intc.c
@@ -134,7 +134,7 @@
 	intr_type =
 		be32_to_cpup(of_get_property(intc,
 						"xlnx,kind-of-intr", NULL));
-	if (intr_type >= (1 << (nr_irq + 1)))
+	if (intr_type > (u32)((1ULL << nr_irq) - 1))
 		printk(KERN_INFO " ERROR: Mismatch in kind-of-intr param\n");
 
 #ifdef CONFIG_SELFMOD_INTC
diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c
index 968648a..dbb8124 100644
--- a/arch/microblaze/kernel/process.c
+++ b/arch/microblaze/kernel/process.c
@@ -237,7 +237,6 @@
 /* Set up a thread for executing a new program */
 void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
 {
-	set_fs(USER_DS);
 	regs->pc = pc;
 	regs->r1 = usp;
 	regs->pt_mode = 0;
diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c
index b15cc21..977484a 100644
--- a/arch/microblaze/kernel/prom.c
+++ b/arch/microblaze/kernel/prom.c
@@ -53,69 +53,58 @@
 }
 
 #ifdef CONFIG_EARLY_PRINTK
-/* MS this is Microblaze specifig function */
-static int __init early_init_dt_scan_serial(unsigned long node,
+char *stdout;
+
+int __init early_init_dt_scan_chosen_serial(unsigned long node,
 				const char *uname, int depth, void *data)
 {
 	unsigned long l;
 	char *p;
-	const __be32 *addr;
 
-	pr_debug("search \"serial\", depth: %d, uname: %s\n", depth, uname);
+	pr_debug("%s: depth: %d, uname: %s\n", __func__, depth, uname);
 
-/* find all serial nodes */
-	if (strncmp(uname, "serial", 6) != 0)
-		return 0;
+	if (depth == 1 && (strcmp(uname, "chosen") == 0 ||
+				strcmp(uname, "chosen@0") == 0)) {
+		p = of_get_flat_dt_prop(node, "linux,stdout-path", &l);
+		if (p != NULL && l > 0)
+			stdout = p; /* store pointer to stdout-path */
+	}
 
-/* find compatible node with uartlite */
-	p = of_get_flat_dt_prop(node, "compatible", &l);
-	if ((strncmp(p, "xlnx,xps-uartlite", 17) != 0) &&
-			(strncmp(p, "xlnx,opb-uartlite", 17) != 0) &&
-			(strncmp(p, "xlnx,axi-uartlite", 17) != 0))
-		return 0;
+	if (stdout && strstr(stdout, uname)) {
+		p = of_get_flat_dt_prop(node, "compatible", &l);
+		pr_debug("Compatible string: %s\n", p);
 
-	addr = of_get_flat_dt_prop(node, "reg", &l);
-	return be32_to_cpup(addr); /* return address */
+		if ((strncmp(p, "xlnx,xps-uart16550", 18) == 0) ||
+			(strncmp(p, "xlnx,axi-uart16550", 18) == 0)) {
+			unsigned int addr;
+
+			*(u32 *)data = UART16550;
+
+			addr = *(u32 *)of_get_flat_dt_prop(node, "reg", &l);
+			addr += *(u32 *)of_get_flat_dt_prop(node,
+							"reg-offset", &l);
+			/* clear register offset */
+			return be32_to_cpu(addr) & ~3;
+		}
+		if ((strncmp(p, "xlnx,xps-uartlite", 17) == 0) ||
+				(strncmp(p, "xlnx,opb-uartlite", 17) == 0) ||
+				(strncmp(p, "xlnx,axi-uartlite", 17) == 0) ||
+				(strncmp(p, "xlnx,mdm", 8) == 0)) {
+			unsigned int *addrp;
+
+			*(u32 *)data = UARTLITE;
+
+			addrp = of_get_flat_dt_prop(node, "reg", &l);
+			return be32_to_cpup(addrp); /* return address */
+		}
+	}
+	return 0;
 }
 
-/* this function is looking for early uartlite console - Microblaze specific */
-int __init early_uartlite_console(void)
+/* this function is looking for early console - Microblaze specific */
+int __init of_early_console(void *version)
 {
-	return of_scan_flat_dt(early_init_dt_scan_serial, NULL);
-}
-
-/* MS this is Microblaze specifig function */
-static int __init early_init_dt_scan_serial_full(unsigned long node,
-				const char *uname, int depth, void *data)
-{
-	unsigned long l;
-	char *p;
-	unsigned int addr;
-
-	pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
-
-/* find all serial nodes */
-	if (strncmp(uname, "serial", 6) != 0)
-		return 0;
-
-	early_init_dt_check_for_initrd(node);
-
-/* find compatible node with uartlite */
-	p = of_get_flat_dt_prop(node, "compatible", &l);
-
-	if ((strncmp(p, "xlnx,xps-uart16550", 18) != 0) &&
-		(strncmp(p, "xlnx,axi-uart16550", 18) != 0))
-		return 0;
-
-	addr = *(u32 *)of_get_flat_dt_prop(node, "reg", &l);
-	addr += *(u32 *)of_get_flat_dt_prop(node, "reg-offset", &l);
-	return be32_to_cpu(addr); /* return address */
-}
-
-/* this function is looking for early uartlite console - Microblaze specific */
-int __init early_uart16550_console(void)
-{
-	return of_scan_flat_dt(early_init_dt_scan_serial_full, NULL);
+	return of_scan_flat_dt(early_init_dt_scan_chosen_serial, version);
 }
 #endif
 
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c
index 8e2c09b..0e654a1 100644
--- a/arch/microblaze/kernel/setup.c
+++ b/arch/microblaze/kernel/setup.c
@@ -59,6 +59,11 @@
 
 	setup_memory();
 
+#ifdef CONFIG_EARLY_PRINTK
+	/* remap early console to virtual address */
+	remap_early_printk();
+#endif
+
 	xilinx_pci_init();
 
 #if defined(CONFIG_SELFMOD_INTC) || defined(CONFIG_SELFMOD_TIMER)
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index aef6c91..5ce8029 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -16,6 +16,7 @@
 platforms += loongson
 platforms += mipssim
 platforms += mti-malta
+platforms += netlogic
 platforms += pmc-sierra
 platforms += pnx833x
 platforms += pnx8550
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 884819c..53e3514 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -191,18 +191,6 @@
 #
 include $(srctree)/arch/mips/Kbuild.platforms
 
-#
-# NETLOGIC SOC Common (common)
-#
-cflags-$(CONFIG_NLM_COMMON)		+= -I$(srctree)/arch/mips/include/asm/mach-netlogic
-cflags-$(CONFIG_NLM_COMMON)		+= -I$(srctree)/arch/mips/include/asm/netlogic
-
-#
-# NETLOGIC XLR/XLS SoC, Simulator and boards
-#
-core-$(CONFIG_NLM_XLR)      		+= arch/mips/netlogic/xlr/
-load-$(CONFIG_NLM_XLR_BOARD)		+= 0xffffffff84000000
-
 cflags-y			+= -I$(srctree)/arch/mips/include/asm/mach-generic
 drivers-$(CONFIG_PCI)		+= arch/mips/pci/
 
diff --git a/arch/mips/ar7/clock.c b/arch/mips/ar7/clock.c
index 2ca4ada..2460f9d 100644
--- a/arch/mips/ar7/clock.c
+++ b/arch/mips/ar7/clock.c
@@ -443,7 +443,7 @@
 		return &vbus_clk;
 	if (!strcmp(id, "cpu"))
 		return &cpu_clk;
-	if (!strcmp(id, "dsp"));
+	if (!strcmp(id, "dsp"))
 		return &dsp_clk;
 	if (!strcmp(id, "vbus"))
 		return &vbus_clk;
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c
index 7d2fab3..33ffecf 100644
--- a/arch/mips/ar7/platform.c
+++ b/arch/mips/ar7/platform.c
@@ -229,7 +229,7 @@
 		.name	= "irq",
 		.flags	= IORESOURCE_IRQ,
 		.start	= 27,
-		.end 	= 27,
+		.end	= 27,
 	},
 };
 
diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c
index 23818d2..8088c6f 100644
--- a/arch/mips/ar7/prom.c
+++ b/arch/mips/ar7/prom.c
@@ -77,7 +77,7 @@
 	u16	csum;
 	u8	len;
 	char	data[11];
-} __attribute__ ((packed));
+} __packed;
 
 struct psp_var_map_entry {
 	u8	num;
diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
index 4a02fe8..1d93f81 100644
--- a/arch/mips/include/asm/atomic.h
+++ b/arch/mips/include/asm/atomic.h
@@ -303,15 +303,15 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), (new)))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -323,9 +323,8 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #define atomic_dec_return(v) atomic_sub_return(1, (v))
 #define atomic_inc_return(v) atomic_add_return(1, (v))
@@ -680,7 +679,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 {
@@ -766,10 +765,6 @@
  */
 #define atomic64_add_negative(i, v) (atomic64_add_return(i, (v)) < 0)
 
-#else /* !CONFIG_64BIT */
-
-#include <asm-generic/atomic64.h>
-
 #endif /* CONFIG_64BIT */
 
 /*
@@ -781,6 +776,4 @@
 #define smp_mb__before_atomic_inc()	smp_mb__before_llsc()
 #define smp_mb__after_atomic_inc()	smp_llsc_mb()
 
-#include <asm-generic/atomic-long.h>
-
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/mips/include/asm/fixmap.h b/arch/mips/include/asm/fixmap.h
index 0b89b83..98bcc98 100644
--- a/arch/mips/include/asm/fixmap.h
+++ b/arch/mips/include/asm/fixmap.h
@@ -14,6 +14,7 @@
 #define _ASM_FIXMAP_H
 
 #include <asm/page.h>
+#include <spaces.h>
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
 #include <asm/kmap_types.h>
@@ -67,15 +68,6 @@
  * the start of the fixmap, and leave one page empty
  * at the top of mem..
  */
-#ifdef CONFIG_BCM63XX
-#define FIXADDR_TOP     ((unsigned long)(long)(int)0xff000000)
-#else
-#if defined(CONFIG_CPU_TX39XX) || defined(CONFIG_CPU_TX49XX)
-#define FIXADDR_TOP	((unsigned long)(long)(int)(0xff000000 - 0x20000))
-#else
-#define FIXADDR_TOP	((unsigned long)(long)(int)0xfffe0000)
-#endif
-#endif
 #define FIXADDR_SIZE	(__end_of_fixed_addresses << PAGE_SHIFT)
 #define FIXADDR_START	(FIXADDR_TOP - FIXADDR_SIZE)
 
diff --git a/arch/mips/include/asm/gt64120.h b/arch/mips/include/asm/gt64120.h
index e64b410..0aa44ab 100644
--- a/arch/mips/include/asm/gt64120.h
+++ b/arch/mips/include/asm/gt64120.h
@@ -21,8 +21,6 @@
 #ifndef _ASM_GT64120_H
 #define _ASM_GT64120_H
 
-#include <linux/clocksource.h>
-
 #include <asm/addrspace.h>
 #include <asm/byteorder.h>
 
diff --git a/arch/mips/include/asm/hw_irq.h b/arch/mips/include/asm/hw_irq.h
index 77adda2..9e8ef59 100644
--- a/arch/mips/include/asm/hw_irq.h
+++ b/arch/mips/include/asm/hw_irq.h
@@ -8,7 +8,7 @@
 #ifndef __ASM_HW_IRQ_H
 #define __ASM_HW_IRQ_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern atomic_t irq_err_count;
 
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 0ec0129..2354c87 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -18,7 +18,6 @@
 
 static inline void irq_dispose_mapping(unsigned int virq)
 {
-	return;
 }
 
 #ifdef CONFIG_I8259
diff --git a/arch/mips/include/asm/local.h b/arch/mips/include/asm/local.h
index fffc830..94fde8d 100644
--- a/arch/mips/include/asm/local.h
+++ b/arch/mips/include/asm/local.h
@@ -3,7 +3,7 @@
 
 #include <linux/percpu.h>
 #include <linux/bitops.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cmpxchg.h>
 #include <asm/war.h>
 
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 85fd275..0ed5230 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -89,7 +89,6 @@
 
 /* Interrupt Mask register */
 #define PERF_IRQMASK_REG		0xc
-#define PERF_IRQSTAT_REG		0x10
 
 /* Interrupt Status register */
 #define PERF_IRQSTAT_REG		0x10
diff --git a/arch/mips/include/asm/mach-bcm63xx/spaces.h b/arch/mips/include/asm/mach-bcm63xx/spaces.h
new file mode 100644
index 0000000..61e750f
--- /dev/null
+++ b/arch/mips/include/asm/mach-bcm63xx/spaces.h
@@ -0,0 +1,17 @@
+/*
+ * 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 (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_BCM63XX_SPACES_H
+#define _ASM_BCM63XX_SPACES_H
+
+#define FIXADDR_TOP		((unsigned long)(long)(int)0xff000000)
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_BCM63XX_SPACES_H */
diff --git a/arch/mips/include/asm/mach-generic/dma-coherence.h b/arch/mips/include/asm/mach-generic/dma-coherence.h
index 8da9807..9c95177 100644
--- a/arch/mips/include/asm/mach-generic/dma-coherence.h
+++ b/arch/mips/include/asm/mach-generic/dma-coherence.h
@@ -49,7 +49,6 @@
 
 static inline void plat_extra_sync_for_device(struct device *dev)
 {
-	return;
 }
 
 static inline int plat_dma_mapping_error(struct device *dev,
diff --git a/arch/mips/include/asm/mach-generic/spaces.h b/arch/mips/include/asm/mach-generic/spaces.h
index c9fa4b1..d7a9efd 100644
--- a/arch/mips/include/asm/mach-generic/spaces.h
+++ b/arch/mips/include/asm/mach-generic/spaces.h
@@ -82,4 +82,8 @@
 #define PAGE_OFFSET		(CAC_BASE + PHYS_OFFSET)
 #endif
 
+#ifndef FIXADDR_TOP
+#define FIXADDR_TOP		((unsigned long)(long)(int)0xfffe0000)
+#endif
+
 #endif /* __ASM_MACH_GENERIC_SPACES_H */
diff --git a/arch/mips/include/asm/mach-ip27/dma-coherence.h b/arch/mips/include/asm/mach-ip27/dma-coherence.h
index 016d098..06c4419 100644
--- a/arch/mips/include/asm/mach-ip27/dma-coherence.h
+++ b/arch/mips/include/asm/mach-ip27/dma-coherence.h
@@ -60,7 +60,6 @@
 
 static inline void plat_extra_sync_for_device(struct device *dev)
 {
-	return;
 }
 
 static inline int plat_dma_mapping_error(struct device *dev,
diff --git a/arch/mips/include/asm/mach-jazz/dma-coherence.h b/arch/mips/include/asm/mach-jazz/dma-coherence.h
index 302101b..9fc1e9a 100644
--- a/arch/mips/include/asm/mach-jazz/dma-coherence.h
+++ b/arch/mips/include/asm/mach-jazz/dma-coherence.h
@@ -50,7 +50,6 @@
 
 static inline void plat_extra_sync_for_device(struct device *dev)
 {
-	return;
 }
 
 static inline int plat_dma_mapping_error(struct device *dev,
diff --git a/arch/mips/include/asm/mach-loongson/dma-coherence.h b/arch/mips/include/asm/mach-loongson/dma-coherence.h
index 981c75f..e143305 100644
--- a/arch/mips/include/asm/mach-loongson/dma-coherence.h
+++ b/arch/mips/include/asm/mach-loongson/dma-coherence.h
@@ -55,7 +55,6 @@
 
 static inline void plat_extra_sync_for_device(struct device *dev)
 {
-	return;
 }
 
 static inline int plat_dma_mapping_error(struct device *dev,
diff --git a/arch/mips/include/asm/mach-malta/cpu-feature-overrides.h b/arch/mips/include/asm/mach-malta/cpu-feature-overrides.h
index 2848cea..37e3583 100644
--- a/arch/mips/include/asm/mach-malta/cpu-feature-overrides.h
+++ b/arch/mips/include/asm/mach-malta/cpu-feature-overrides.h
@@ -32,6 +32,7 @@
 /* #define cpu_has_vtag_icache	? */
 /* #define cpu_has_dc_aliases	? */
 /* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_clo_clz		1
 #define cpu_has_nofpuex		0
 /* #define cpu_has_64bits	? */
 /* #define cpu_has_64bit_zero_reg ? */
@@ -58,6 +59,7 @@
 /* #define cpu_has_vtag_icache	? */
 /* #define cpu_has_dc_aliases	? */
 /* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_clo_clz		1
 #define cpu_has_nofpuex		0
 /* #define cpu_has_64bits	? */
 /* #define cpu_has_64bit_zero_reg ? */
diff --git a/arch/mips/include/asm/mach-mipssim/cpu-feature-overrides.h b/arch/mips/include/asm/mach-mipssim/cpu-feature-overrides.h
index 779b022..27aaaa5 100644
--- a/arch/mips/include/asm/mach-mipssim/cpu-feature-overrides.h
+++ b/arch/mips/include/asm/mach-mipssim/cpu-feature-overrides.h
@@ -31,6 +31,7 @@
 /* #define cpu_has_vtag_icache	? */
 /* #define cpu_has_dc_aliases	? */
 /* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_clo_clz		1
 #define cpu_has_nofpuex		0
 /* #define cpu_has_64bits	? */
 /* #define cpu_has_64bit_zero_reg ? */
@@ -56,6 +57,7 @@
 /* #define cpu_has_vtag_icache	? */
 /* #define cpu_has_dc_aliases	? */
 /* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_clo_clz		1
 #define cpu_has_nofpuex		0
 /* #define cpu_has_64bits	? */
 /* #define cpu_has_64bit_zero_reg ? */
diff --git a/arch/mips/include/asm/mach-powertv/cpu-feature-overrides.h b/arch/mips/include/asm/mach-powertv/cpu-feature-overrides.h
new file mode 100644
index 0000000..f751e3e
--- /dev/null
+++ b/arch/mips/include/asm/mach-powertv/cpu-feature-overrides.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010  Cisco Systems, Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _ASM_MACH_POWERTV_CPU_FEATURE_OVERRIDES_H_
+#define _ASM_MACH_POWERTV_CPU_FEATURE_OVERRIDES_H_
+#define cpu_has_tlb			1
+#define cpu_has_4kex			1
+#define cpu_has_3k_cache		0
+#define cpu_has_4k_cache		1
+#define cpu_has_tx39_cache		0
+#define cpu_has_fpu			0
+#define cpu_has_counter			1
+#define cpu_has_watch			1
+#define cpu_has_divec			1
+#define cpu_has_vce			0
+#define cpu_has_cache_cdex_p		0
+#define cpu_has_cache_cdex_s		0
+#define cpu_has_mcheck			1
+#define cpu_has_ejtag			1
+#define cpu_has_llsc			1
+#define cpu_has_mips16			0
+#define cpu_has_mdmx			0
+#define cpu_has_mips3d			0
+#define cpu_has_smartmips		0
+#define cpu_has_vtag_icache		0
+#define cpu_has_dc_aliases		0
+#define cpu_has_ic_fills_f_dc		0
+#define cpu_has_mips32r1		0
+#define cpu_has_mips32r2		1
+#define cpu_has_mips64r1		0
+#define cpu_has_mips64r2		0
+#define cpu_has_dsp			0
+#define cpu_has_mipsmt			0
+#define cpu_has_userlocal		0
+#define cpu_has_nofpuex			0
+#define cpu_has_64bits			0
+#define cpu_has_64bit_zero_reg		0
+#define cpu_has_vint			1
+#define cpu_has_veic			1
+#define cpu_has_inclusive_pcaches	0
+
+#define cpu_dcache_line_size()		32
+#define cpu_icache_line_size()		32
+#endif
diff --git a/arch/mips/include/asm/mach-powertv/dma-coherence.h b/arch/mips/include/asm/mach-powertv/dma-coherence.h
index a8e72cf..62c0940 100644
--- a/arch/mips/include/asm/mach-powertv/dma-coherence.h
+++ b/arch/mips/include/asm/mach-powertv/dma-coherence.h
@@ -102,7 +102,6 @@
 
 static inline void plat_extra_sync_for_device(struct device *dev)
 {
-	return;
 }
 
 static inline int plat_dma_mapping_error(struct device *dev,
diff --git a/arch/mips/include/asm/mach-tx39xx/spaces.h b/arch/mips/include/asm/mach-tx39xx/spaces.h
new file mode 100644
index 0000000..151fe7a
--- /dev/null
+++ b/arch/mips/include/asm/mach-tx39xx/spaces.h
@@ -0,0 +1,17 @@
+/*
+ * 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 (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_TX39XX_SPACES_H
+#define _ASM_TX39XX_SPACES_H
+
+#define FIXADDR_TOP		((unsigned long)(long)(int)0xfefe0000)
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_TX39XX_SPACES_H */
diff --git a/arch/mips/include/asm/mach-tx49xx/spaces.h b/arch/mips/include/asm/mach-tx49xx/spaces.h
new file mode 100644
index 0000000..0cb10a6
--- /dev/null
+++ b/arch/mips/include/asm/mach-tx49xx/spaces.h
@@ -0,0 +1,17 @@
+/*
+ * 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 (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_TX49XX_SPACES_H
+#define _ASM_TX49XX_SPACES_H
+
+#define FIXADDR_TOP		((unsigned long)(long)(int)0xfefe0000)
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_TX49XX_SPACES_H */
diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
index 7e40f37..b2202a6 100644
--- a/arch/mips/include/asm/pgtable.h
+++ b/arch/mips/include/asm/pgtable.h
@@ -414,6 +414,7 @@
  * constraints placed on us by the cache architecture.
  */
 #define HAVE_ARCH_UNMAPPED_AREA
+#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
 
 /*
  * No page table caches to initialise
diff --git a/arch/mips/include/asm/smp-ops.h b/arch/mips/include/asm/smp-ops.h
index 9e09af3..ef2a804 100644
--- a/arch/mips/include/asm/smp-ops.h
+++ b/arch/mips/include/asm/smp-ops.h
@@ -11,6 +11,8 @@
 #ifndef __ASM_SMP_OPS_H
 #define __ASM_SMP_OPS_H
 
+#include <linux/errno.h>
+
 #ifdef CONFIG_SMP
 
 #include <linux/cpumask.h>
@@ -56,8 +58,43 @@
 
 #endif /* !CONFIG_SMP */
 
-extern struct plat_smp_ops up_smp_ops;
-extern struct plat_smp_ops cmp_smp_ops;
-extern struct plat_smp_ops vsmp_smp_ops;
+static inline int register_up_smp_ops(void)
+{
+#ifdef CONFIG_SMP_UP
+	extern struct plat_smp_ops up_smp_ops;
+
+	register_smp_ops(&up_smp_ops);
+
+	return 0;
+#else
+	return -ENODEV;
+#endif
+}
+
+static inline int register_cmp_smp_ops(void)
+{
+#ifdef CONFIG_MIPS_CMP
+	extern struct plat_smp_ops cmp_smp_ops;
+
+	register_smp_ops(&cmp_smp_ops);
+
+	return 0;
+#else
+	return -ENODEV;
+#endif
+}
+
+static inline int register_vsmp_smp_ops(void)
+{
+#ifdef CONFIG_MIPS_MT_SMP
+	extern struct plat_smp_ops vsmp_smp_ops;
+
+	register_smp_ops(&vsmp_smp_ops);
+
+	return 0;
+#else
+	return -ENODEV;
+#endif
+}
 
 #endif /* __ASM_SMP_OPS_H */
diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h
index af42385..d4fb4d8 100644
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -17,7 +17,7 @@
 #include <linux/threads.h>
 #include <linux/cpumask.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp-ops.h>
 
 extern int smp_num_siblings;
diff --git a/arch/mips/include/asm/smtc.h b/arch/mips/include/asm/smtc.h
index ea60bf0..c9736fc 100644
--- a/arch/mips/include/asm/smtc.h
+++ b/arch/mips/include/asm/smtc.h
@@ -46,6 +46,7 @@
 extern void smtc_smp_finish(void);
 extern void smtc_boot_secondary(int cpu, struct task_struct *t);
 extern void smtc_cpus_done(void);
+extern void smtc_init_secondary(void);
 
 
 /*
diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h
index dcbd4bb..504d40a 100644
--- a/arch/mips/include/asm/uasm.h
+++ b/arch/mips/include/asm/uasm.h
@@ -150,6 +150,7 @@
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_dsll(buf, rs, rt, sh)
 # define UASM_i_SRA(buf, rs, rt, sh) uasm_i_dsra(buf, rs, rt, sh)
 # define UASM_i_SRL(buf, rs, rt, sh) uasm_i_dsrl(buf, rs, rt, sh)
+# define UASM_i_SRL_SAFE(buf, rs, rt, sh) uasm_i_dsrl_safe(buf, rs, rt, sh)
 # define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_drotr(buf, rs, rt, sh)
 # define UASM_i_MFC0(buf, rt, rd...) uasm_i_dmfc0(buf, rt, rd)
 # define UASM_i_MTC0(buf, rt, rd...) uasm_i_dmtc0(buf, rt, rd)
@@ -165,6 +166,7 @@
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_sll(buf, rs, rt, sh)
 # define UASM_i_SRA(buf, rs, rt, sh) uasm_i_sra(buf, rs, rt, sh)
 # define UASM_i_SRL(buf, rs, rt, sh) uasm_i_srl(buf, rs, rt, sh)
+# define UASM_i_SRL_SAFE(buf, rs, rt, sh) uasm_i_srl(buf, rs, rt, sh)
 # define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_rotr(buf, rs, rt, sh)
 # define UASM_i_MFC0(buf, rt, rd...) uasm_i_mfc0(buf, rt, rd)
 # define UASM_i_MTC0(buf, rt, rd...) uasm_i_mtc0(buf, rt, rd)
diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h
index 6fcfc48..ecea787 100644
--- a/arch/mips/include/asm/unistd.h
+++ b/arch/mips/include/asm/unistd.h
@@ -363,17 +363,18 @@
 #define __NR_open_by_handle_at		(__NR_Linux + 340)
 #define __NR_clock_adjtime		(__NR_Linux + 341)
 #define __NR_syncfs			(__NR_Linux + 342)
-#define __NR_setns			(__NR_Linux + 343)
+#define __NR_sendmmsg			(__NR_Linux + 343)
+#define __NR_setns			(__NR_Linux + 344)
 
 /*
  * Offset of the last Linux o32 flavoured syscall
  */
-#define __NR_Linux_syscalls		343
+#define __NR_Linux_syscalls		344
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
 
 #define __NR_O32_Linux			4000
-#define __NR_O32_Linux_syscalls		343
+#define __NR_O32_Linux_syscalls		344
 
 #if _MIPS_SIM == _MIPS_SIM_ABI64
 
@@ -683,17 +684,18 @@
 #define __NR_open_by_handle_at		(__NR_Linux + 299)
 #define __NR_clock_adjtime		(__NR_Linux + 300)
 #define __NR_syncfs			(__NR_Linux + 301)
-#define __NR_setns			(__NR_Linux + 302)
+#define __NR_sendmmsg			(__NR_Linux + 302)
+#define __NR_setns			(__NR_Linux + 303)
 
 /*
  * Offset of the last Linux 64-bit flavoured syscall
  */
-#define __NR_Linux_syscalls		302
+#define __NR_Linux_syscalls		303
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
 
 #define __NR_64_Linux			5000
-#define __NR_64_Linux_syscalls		302
+#define __NR_64_Linux_syscalls		303
 
 #if _MIPS_SIM == _MIPS_SIM_NABI32
 
@@ -1008,17 +1010,18 @@
 #define __NR_open_by_handle_at		(__NR_Linux + 304)
 #define __NR_clock_adjtime		(__NR_Linux + 305)
 #define __NR_syncfs			(__NR_Linux + 306)
-#define __NR_setns			(__NR_Linux + 307)
+#define __NR_sendmmsg			(__NR_Linux + 307)
+#define __NR_setns			(__NR_Linux + 308)
 
 /*
  * Offset of the last N32 flavoured syscall
  */
-#define __NR_Linux_syscalls		307
+#define __NR_Linux_syscalls		308
 
 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
 
 #define __NR_N32_Linux			6000
-#define __NR_N32_Linux_syscalls		307
+#define __NR_N32_Linux_syscalls		308
 
 #ifdef __KERNEL__
 
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index bb133d1..ebc0cd2 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -71,7 +71,6 @@
 	local_irq_enable();
 	__asm__(" 	.globl __pastwait	\n"
 		"__pastwait:			\n");
-	return;
 }
 
 /*
diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c
index 9b734d7..b53970d 100644
--- a/arch/mips/kernel/irq.c
+++ b/arch/mips/kernel/irq.c
@@ -23,7 +23,7 @@
 #include <linux/kgdb.h>
 #include <linux/ftrace.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
diff --git a/arch/mips/kernel/irq_cpu.c b/arch/mips/kernel/irq_cpu.c
index 6e71b28..191eb52 100644
--- a/arch/mips/kernel/irq_cpu.c
+++ b/arch/mips/kernel/irq_cpu.c
@@ -103,14 +103,12 @@
 	clear_c0_status(ST0_IM);
 	clear_c0_cause(CAUSEF_IP);
 
-	/*
-	 * Only MT is using the software interrupts currently, so we just
-	 * leave them uninitialized for other processors.
-	 */
-	if (cpu_has_mipsmt)
-		for (i = irq_base; i < irq_base + 2; i++)
-			irq_set_chip_and_handler(i, &mips_mt_cpu_irq_controller,
-						 handle_percpu_irq);
+	/* Software interrupts are used for MT/CMT IPI */
+	for (i = irq_base; i < irq_base + 2; i++)
+		irq_set_chip_and_handler(i, cpu_has_mipsmt ?
+					 &mips_mt_cpu_irq_controller :
+					 &mips_cpu_irq_controller,
+					 handle_percpu_irq);
 
 	for (i = irq_base + 2; i < irq_base + 8; i++)
 		irq_set_chip_and_handler(i, &mips_cpu_irq_controller,
diff --git a/arch/mips/kernel/mips-mt.c b/arch/mips/kernel/mips-mt.c
index b2259e7..594ca69 100644
--- a/arch/mips/kernel/mips-mt.c
+++ b/arch/mips/kernel/mips-mt.c
@@ -12,7 +12,7 @@
 
 #include <asm/cpu.h>
 #include <asm/processor.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/hardirq.h>
 #include <asm/mmu_context.h>
diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c
index d0deaab..0aee944 100644
--- a/arch/mips/kernel/perf_event.c
+++ b/arch/mips/kernel/perf_event.c
@@ -192,8 +192,6 @@
 
 	local64_add(delta, &event->count);
 	local64_sub(delta, &hwc->period_left);
-
-	return;
 }
 
 static void mipspmu_start(struct perf_event *event, int flags)
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index c28fbe6..b30cb25 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -103,7 +103,6 @@
 		__init_dsp();
 	regs->cp0_epc = pc;
 	regs->regs[29] = sp;
-	current_thread_info()->addr_limit = USER_DS;
 }
 
 void exit_thread(void)
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index 557ef72..7a80b7c 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -36,7 +36,7 @@
 #include <asm/mipsmtregs.h>
 #include <asm/mips_mt.h>
 #include <asm/cacheflush.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
 #include <asm/system.h>
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 99e656e..e521420 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -589,6 +589,7 @@
 	sys	sys_open_by_handle_at	3	/* 4340 */
 	sys	sys_clock_adjtime	2
 	sys	sys_syncfs		1
+	sys	sys_sendmmsg		4
 	sys	sys_setns		2
 	.endm
 
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index fb0575f..85874d6 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -428,5 +428,6 @@
 	PTR	sys_open_by_handle_at
 	PTR	sys_clock_adjtime		/* 5300 */
 	PTR	sys_syncfs
+	PTR	sys_sendmmsg
 	PTR	sys_setns
 	.size	sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 4de0c55..b85842f 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -428,5 +428,6 @@
 	PTR	sys_open_by_handle_at
 	PTR	compat_sys_clock_adjtime	/* 6305 */
 	PTR	sys_syncfs
+	PTR	compat_sys_sendmmsg
 	PTR	sys_setns
 	.size	sysn32_call_table,.-sysn32_call_table
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 4a387de..46c4763 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -546,5 +546,6 @@
 	PTR	compat_sys_open_by_handle_at	/* 4340 */
 	PTR	compat_sys_clock_adjtime
 	PTR	sys_syncfs
+	PTR	compat_sys_sendmmsg
 	PTR	sys_setns
 	.size	sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/smp-cmp.c b/arch/mips/kernel/smp-cmp.c
index cc81771..fe30951 100644
--- a/arch/mips/kernel/smp-cmp.c
+++ b/arch/mips/kernel/smp-cmp.c
@@ -25,7 +25,7 @@
 #include <linux/interrupt.h>
 #include <linux/compiler.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c
index 1ec56e6..ce9e286 100644
--- a/arch/mips/kernel/smp-mt.c
+++ b/arch/mips/kernel/smp-mt.c
@@ -24,7 +24,7 @@
 #include <linux/compiler.h>
 #include <linux/smp.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cacheflush.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 32a2561..32c1e95 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -34,7 +34,7 @@
 #include <linux/err.h>
 #include <linux/ftrace.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
 #include <asm/r4k-timer.h>
diff --git a/arch/mips/kernel/smtc-proc.c b/arch/mips/kernel/smtc-proc.c
index fe25655..928a5a6 100644
--- a/arch/mips/kernel/smtc-proc.c
+++ b/arch/mips/kernel/smtc-proc.c
@@ -10,7 +10,7 @@
 
 #include <asm/cpu.h>
 #include <asm/processor.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/hardirq.h>
 #include <asm/mmu_context.h>
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c
index cedac46..f0895e7 100644
--- a/arch/mips/kernel/smtc.c
+++ b/arch/mips/kernel/smtc.c
@@ -30,7 +30,7 @@
 
 #include <asm/cpu.h>
 #include <asm/processor.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/hardirq.h>
 #include <asm/hazards.h>
diff --git a/arch/mips/kernel/sync-r4k.c b/arch/mips/kernel/sync-r4k.c
index 05dd170..99f913c 100644
--- a/arch/mips/kernel/sync-r4k.c
+++ b/arch/mips/kernel/sync-r4k.c
@@ -16,7 +16,7 @@
 #include <linux/cpumask.h>
 
 #include <asm/r4k-timer.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/barrier.h>
 #include <asm/mipsregs.h>
 
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c
index dbb6b40..2cd50ad 100644
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -46,7 +46,7 @@
 #include <asm/mipsregs.h>
 #include <asm/mipsmtregs.h>
 #include <asm/cacheflush.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cpu.h>
 #include <asm/mips_mt.h>
 #include <asm/processor.h>
diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c
index 9456089..7e9c0ff 100644
--- a/arch/mips/lantiq/clk.c
+++ b/arch/mips/lantiq/clk.c
@@ -100,6 +100,19 @@
 }
 EXPORT_SYMBOL(clk_put);
 
+int clk_enable(struct clk *clk)
+{
+	/* not used */
+	return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+	/* not used */
+}
+EXPORT_SYMBOL(clk_disable);
+
 static inline u32 ltq_get_counter_resolution(void)
 {
 	u32 res;
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.c b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
index 6405724..2b666d3 100644
--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.c
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
@@ -45,8 +45,6 @@
 	/*  flush the write action */
 	inb(EC_IO_PORT_DATA);
 	spin_unlock_irqrestore(&index_access_lock, flags);
-
-	return;
 }
 EXPORT_SYMBOL_GPL(ec_write);
 
diff --git a/arch/mips/mipssim/sim_setup.c b/arch/mips/mipssim/sim_setup.c
index 55f22a3..256e0cd 100644
--- a/arch/mips/mipssim/sim_setup.c
+++ b/arch/mips/mipssim/sim_setup.c
@@ -34,6 +34,7 @@
 #include <asm/time.h>
 #include <asm/mips-boards/sim.h>
 #include <asm/mips-boards/simint.h>
+#include <asm/smp-ops.h>
 
 
 static void __init serial_init(void);
@@ -59,18 +60,17 @@
 
 	prom_meminit();
 
-#ifdef CONFIG_MIPS_MT_SMP
-	if (cpu_has_mipsmt)
-		register_smp_ops(&vsmp_smp_ops);
-	else
-		register_smp_ops(&up_smp_ops);
-#endif
+	if (cpu_has_mipsmt) {
+		if (!register_vsmp_smp_ops())
+			return;
+
 #ifdef CONFIG_MIPS_MT_SMTC
-	if (cpu_has_mipsmt)
 		register_smp_ops(&ssmtc_smp_ops);
-	else
-		register_smp_ops(&up_smp_ops);
+			return;
 #endif
+	}
+
+	register_up_smp_ops();
 }
 
 static void __init serial_init(void)
diff --git a/arch/mips/mipssim/sim_smtc.c b/arch/mips/mipssim/sim_smtc.c
index 30df472..9150639 100644
--- a/arch/mips/mipssim/sim_smtc.c
+++ b/arch/mips/mipssim/sim_smtc.c
@@ -24,7 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/smp.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cpu.h>
 #include <asm/processor.h>
 #include <asm/smtc.h>
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index eeb642e..b9aabb9 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -604,6 +604,7 @@
 			r4k_blast_scache();
 		else
 			blast_scache_range(addr, addr + size);
+		__sync();
 		return;
 	}
 
@@ -620,6 +621,7 @@
 	}
 
 	bc_wback_inv(addr, size);
+	__sync();
 }
 
 static void r4k_dma_cache_inv(unsigned long addr, unsigned long size)
@@ -647,6 +649,7 @@
 				 (addr + size - 1) & almask);
 			blast_inv_scache_range(addr, addr + size);
 		}
+		__sync();
 		return;
 	}
 
@@ -663,6 +666,7 @@
 	}
 
 	bc_inv(addr, size);
+	__sync();
 }
 #endif /* CONFIG_DMA_NONCOHERENT */
 
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index 21ea14e..4608491 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -15,18 +15,18 @@
 #include <linux/scatterlist.h>
 #include <linux/string.h>
 #include <linux/gfp.h>
+#include <linux/highmem.h>
 
 #include <asm/cache.h>
 #include <asm/io.h>
 
 #include <dma-coherence.h>
 
-static inline unsigned long dma_addr_to_virt(struct device *dev,
+static inline struct page *dma_addr_to_page(struct device *dev,
 	dma_addr_t dma_addr)
 {
-	unsigned long addr = plat_dma_addr_to_phys(dev, dma_addr);
-
-	return (unsigned long)phys_to_virt(addr);
+	return pfn_to_page(
+		plat_dma_addr_to_phys(dev, dma_addr) >> PAGE_SHIFT);
 }
 
 /*
@@ -148,20 +148,20 @@
 	free_pages(addr, get_order(size));
 }
 
-static inline void __dma_sync(unsigned long addr, size_t size,
+static inline void __dma_sync_virtual(void *addr, size_t size,
 	enum dma_data_direction direction)
 {
 	switch (direction) {
 	case DMA_TO_DEVICE:
-		dma_cache_wback(addr, size);
+		dma_cache_wback((unsigned long)addr, size);
 		break;
 
 	case DMA_FROM_DEVICE:
-		dma_cache_inv(addr, size);
+		dma_cache_inv((unsigned long)addr, size);
 		break;
 
 	case DMA_BIDIRECTIONAL:
-		dma_cache_wback_inv(addr, size);
+		dma_cache_wback_inv((unsigned long)addr, size);
 		break;
 
 	default:
@@ -169,12 +169,49 @@
 	}
 }
 
+/*
+ * A single sg entry may refer to multiple physically contiguous
+ * pages. But we still need to process highmem pages individually.
+ * If highmem is not configured then the bulk of this loop gets
+ * optimized out.
+ */
+static inline void __dma_sync(struct page *page,
+	unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+	size_t left = size;
+
+	do {
+		size_t len = left;
+
+		if (PageHighMem(page)) {
+			void *addr;
+
+			if (offset + len > PAGE_SIZE) {
+				if (offset >= PAGE_SIZE) {
+					page += offset >> PAGE_SHIFT;
+					offset &= ~PAGE_MASK;
+				}
+				len = PAGE_SIZE - offset;
+			}
+
+			addr = kmap_atomic(page);
+			__dma_sync_virtual(addr + offset, len, direction);
+			kunmap_atomic(addr);
+		} else
+			__dma_sync_virtual(page_address(page) + offset,
+					   size, direction);
+		offset = 0;
+		page++;
+		left -= len;
+	} while (left);
+}
+
 static void mips_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 	size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)
 {
 	if (cpu_is_noncoherent_r10000(dev))
-		__dma_sync(dma_addr_to_virt(dev, dma_addr), size,
-		           direction);
+		__dma_sync(dma_addr_to_page(dev, dma_addr),
+			   dma_addr & ~PAGE_MASK, size, direction);
 
 	plat_unmap_dma_mem(dev, dma_addr, size, direction);
 }
@@ -185,13 +222,11 @@
 	int i;
 
 	for (i = 0; i < nents; i++, sg++) {
-		unsigned long addr;
-
-		addr = (unsigned long) sg_virt(sg);
-		if (!plat_device_is_coherent(dev) && addr)
-			__dma_sync(addr, sg->length, direction);
-		sg->dma_address = plat_map_dma_mem(dev,
-				                   (void *)addr, sg->length);
+		if (!plat_device_is_coherent(dev))
+			__dma_sync(sg_page(sg), sg->offset, sg->length,
+				   direction);
+		sg->dma_address = plat_map_dma_mem_page(dev, sg_page(sg)) +
+				  sg->offset;
 	}
 
 	return nents;
@@ -201,30 +236,23 @@
 	unsigned long offset, size_t size, enum dma_data_direction direction,
 	struct dma_attrs *attrs)
 {
-	unsigned long addr;
-
-	addr = (unsigned long) page_address(page) + offset;
-
 	if (!plat_device_is_coherent(dev))
-		__dma_sync(addr, size, direction);
+		__dma_sync(page, offset, size, direction);
 
-	return plat_map_dma_mem(dev, (void *)addr, size);
+	return plat_map_dma_mem_page(dev, page) + offset;
 }
 
 static void mips_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
 	int nhwentries, enum dma_data_direction direction,
 	struct dma_attrs *attrs)
 {
-	unsigned long addr;
 	int i;
 
 	for (i = 0; i < nhwentries; i++, sg++) {
 		if (!plat_device_is_coherent(dev) &&
-		    direction != DMA_TO_DEVICE) {
-			addr = (unsigned long) sg_virt(sg);
-			if (addr)
-				__dma_sync(addr, sg->length, direction);
-		}
+		    direction != DMA_TO_DEVICE)
+			__dma_sync(sg_page(sg), sg->offset, sg->length,
+				   direction);
 		plat_unmap_dma_mem(dev, sg->dma_address, sg->length, direction);
 	}
 }
@@ -232,24 +260,18 @@
 static void mips_dma_sync_single_for_cpu(struct device *dev,
 	dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
 {
-	if (cpu_is_noncoherent_r10000(dev)) {
-		unsigned long addr;
-
-		addr = dma_addr_to_virt(dev, dma_handle);
-		__dma_sync(addr, size, direction);
-	}
+	if (cpu_is_noncoherent_r10000(dev))
+		__dma_sync(dma_addr_to_page(dev, dma_handle),
+			   dma_handle & ~PAGE_MASK, size, direction);
 }
 
 static void mips_dma_sync_single_for_device(struct device *dev,
 	dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
 {
 	plat_extra_sync_for_device(dev);
-	if (!plat_device_is_coherent(dev)) {
-		unsigned long addr;
-
-		addr = dma_addr_to_virt(dev, dma_handle);
-		__dma_sync(addr, size, direction);
-	}
+	if (!plat_device_is_coherent(dev))
+		__dma_sync(dma_addr_to_page(dev, dma_handle),
+			   dma_handle & ~PAGE_MASK, size, direction);
 }
 
 static void mips_dma_sync_sg_for_cpu(struct device *dev,
@@ -260,8 +282,8 @@
 	/* Make sure that gcc doesn't leave the empty loop body.  */
 	for (i = 0; i < nelems; i++, sg++) {
 		if (cpu_is_noncoherent_r10000(dev))
-			__dma_sync((unsigned long)page_address(sg_page(sg)),
-			           sg->length, direction);
+			__dma_sync(sg_page(sg), sg->offset, sg->length,
+				   direction);
 	}
 }
 
@@ -273,8 +295,8 @@
 	/* Make sure that gcc doesn't leave the empty loop body.  */
 	for (i = 0; i < nelems; i++, sg++) {
 		if (!plat_device_is_coherent(dev))
-			__dma_sync((unsigned long)page_address(sg_page(sg)),
-			           sg->length, direction);
+			__dma_sync(sg_page(sg), sg->offset, sg->length,
+				   direction);
 	}
 }
 
@@ -295,7 +317,7 @@
 
 	plat_extra_sync_for_device(dev);
 	if (!plat_device_is_coherent(dev))
-		__dma_sync((unsigned long)vaddr, size, direction);
+		__dma_sync_virtual(vaddr, size, direction);
 }
 
 EXPORT_SYMBOL(dma_cache_sync);
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 1aadeb4..b7ebc4f 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -277,11 +277,11 @@
 	k = __pmd_offset(vaddr);
 	pgd = pgd_base + i;
 
-	for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) {
+	for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
 		pud = (pud_t *)pgd;
-		for ( ; (j < PTRS_PER_PUD) && (vaddr != end); pud++, j++) {
+		for ( ; (j < PTRS_PER_PUD) && (vaddr < end); pud++, j++) {
 			pmd = (pmd_t *)pud;
-			for (; (k < PTRS_PER_PMD) && (vaddr != end); pmd++, k++) {
+			for (; (k < PTRS_PER_PMD) && (vaddr < end); pmd++, k++) {
 				if (pmd_none(*pmd)) {
 					pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
 					set_pmd(pmd, __pmd((unsigned long)pte));
@@ -368,7 +368,7 @@
 #ifdef CONFIG_DISCONTIGMEM
 #error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet"
 #endif
-	max_mapnr = highend_pfn;
+	max_mapnr = highend_pfn ? highend_pfn : max_low_pfn;
 #else
 	max_mapnr = max_low_pfn;
 #endif
diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c
index ae3c20a..9ff5d0f 100644
--- a/arch/mips/mm/mmap.c
+++ b/arch/mips/mm/mmap.c
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/module.h>
+#include <linux/personality.h>
 #include <linux/random.h>
 #include <linux/sched.h>
 
@@ -17,21 +18,65 @@
 
 EXPORT_SYMBOL(shm_align_mask);
 
+/* gap between mmap and stack */
+#define MIN_GAP (128*1024*1024UL)
+#define MAX_GAP        ((TASK_SIZE)/6*5)
+
+static int mmap_is_legacy(void)
+{
+	if (current->personality & ADDR_COMPAT_LAYOUT)
+		return 1;
+
+	if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
+		return 1;
+
+	return sysctl_legacy_va_layout;
+}
+
+static unsigned long mmap_base(unsigned long rnd)
+{
+	unsigned long gap = rlimit(RLIMIT_STACK);
+
+	if (gap < MIN_GAP)
+		gap = MIN_GAP;
+	else if (gap > MAX_GAP)
+		gap = MAX_GAP;
+
+	return PAGE_ALIGN(TASK_SIZE - gap - rnd);
+}
+
+static inline unsigned long COLOUR_ALIGN_DOWN(unsigned long addr,
+					      unsigned long pgoff)
+{
+	unsigned long base = addr & ~shm_align_mask;
+	unsigned long off = (pgoff << PAGE_SHIFT) & shm_align_mask;
+
+	if (base + off <= addr)
+		return base + off;
+
+	return base - off;
+}
+
 #define COLOUR_ALIGN(addr,pgoff)				\
 	((((addr) + shm_align_mask) & ~shm_align_mask) +	\
 	 (((pgoff) << PAGE_SHIFT) & shm_align_mask))
 
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
-	unsigned long len, unsigned long pgoff, unsigned long flags)
+enum mmap_allocation_direction {UP, DOWN};
+
+static unsigned long arch_get_unmapped_area_foo(struct file *filp,
+	unsigned long addr0, unsigned long len, unsigned long pgoff,
+	unsigned long flags, enum mmap_allocation_direction dir)
 {
-	struct vm_area_struct * vmm;
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	unsigned long addr = addr0;
 	int do_color_align;
 
-	if (len > TASK_SIZE)
+	if (unlikely(len > TASK_SIZE))
 		return -ENOMEM;
 
 	if (flags & MAP_FIXED) {
-		/* Even MAP_FIXED mappings must reside within TASK_SIZE.  */
+		/* Even MAP_FIXED mappings must reside within TASK_SIZE */
 		if (TASK_SIZE - len < addr)
 			return -EINVAL;
 
@@ -48,32 +93,128 @@
 	do_color_align = 0;
 	if (filp || (flags & MAP_SHARED))
 		do_color_align = 1;
+
+	/* requesting a specific address */
 	if (addr) {
 		if (do_color_align)
 			addr = COLOUR_ALIGN(addr, pgoff);
 		else
 			addr = PAGE_ALIGN(addr);
-		vmm = find_vma(current->mm, addr);
-		if (TASK_SIZE - len >= addr &&
-		    (!vmm || addr + len <= vmm->vm_start))
-			return addr;
-	}
-	addr = current->mm->mmap_base;
-	if (do_color_align)
-		addr = COLOUR_ALIGN(addr, pgoff);
-	else
-		addr = PAGE_ALIGN(addr);
 
-	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
-		/* At this point:  (!vmm || addr < vmm->vm_end). */
-		if (TASK_SIZE - len < addr)
-			return -ENOMEM;
-		if (!vmm || addr + len <= vmm->vm_start)
+		vma = find_vma(mm, addr);
+		if (TASK_SIZE - len >= addr &&
+		   (!vma || addr + len <= vma->vm_start))
 			return addr;
-		addr = vmm->vm_end;
-		if (do_color_align)
-			addr = COLOUR_ALIGN(addr, pgoff);
 	}
+
+	if (dir == UP) {
+		addr = mm->mmap_base;
+			if (do_color_align)
+				addr = COLOUR_ALIGN(addr, pgoff);
+			else
+				addr = PAGE_ALIGN(addr);
+
+		for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
+			/* At this point:  (!vma || addr < vma->vm_end). */
+			if (TASK_SIZE - len < addr)
+				return -ENOMEM;
+			if (!vma || addr + len <= vma->vm_start)
+				return addr;
+			addr = vma->vm_end;
+			if (do_color_align)
+				addr = COLOUR_ALIGN(addr, pgoff);
+		 }
+	 } else {
+		/* check if free_area_cache is useful for us */
+		if (len <= mm->cached_hole_size) {
+			mm->cached_hole_size = 0;
+			mm->free_area_cache = mm->mmap_base;
+		}
+
+		/* either no address requested or can't fit in requested address hole */
+		addr = mm->free_area_cache;
+			if (do_color_align) {
+				unsigned long base =
+					COLOUR_ALIGN_DOWN(addr - len, pgoff);
+
+			addr = base + len;
+		 }
+
+		/* make sure it can fit in the remaining address space */
+		if (likely(addr > len)) {
+			vma = find_vma(mm, addr - len);
+			if (!vma || addr <= vma->vm_start) {
+				/* remember the address as a hint for next time */
+				return mm->free_area_cache = addr-len;
+			}
+		}
+
+		if (unlikely(mm->mmap_base < len))
+			goto bottomup;
+
+		addr = mm->mmap_base-len;
+		if (do_color_align)
+			addr = COLOUR_ALIGN_DOWN(addr, pgoff);
+
+		do {
+			/*
+			 * Lookup failure means no vma is above this address,
+			 * else if new region fits below vma->vm_start,
+			 * return with success:
+			 */
+			vma = find_vma(mm, addr);
+			if (likely(!vma || addr+len <= vma->vm_start)) {
+				/* remember the address as a hint for next time */
+				return mm->free_area_cache = addr;
+			}
+
+			/* remember the largest hole we saw so far */
+			if (addr + mm->cached_hole_size < vma->vm_start)
+				mm->cached_hole_size = vma->vm_start - addr;
+
+			/* try just below the current vma->vm_start */
+			addr = vma->vm_start-len;
+			if (do_color_align)
+				addr = COLOUR_ALIGN_DOWN(addr, pgoff);
+		} while (likely(len < vma->vm_start));
+
+bottomup:
+		/*
+		 * A failed mmap() very likely causes application failure,
+		 * so fall back to the bottom-up function here. This scenario
+		 * can happen with large stack limits and large mmap()
+		 * allocations.
+		 */
+		mm->cached_hole_size = ~0UL;
+		mm->free_area_cache = TASK_UNMAPPED_BASE;
+		addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+		/*
+		 * Restore the topdown base:
+		 */
+		mm->free_area_cache = mm->mmap_base;
+		mm->cached_hole_size = ~0UL;
+
+		return addr;
+	}
+}
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
+	unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	return arch_get_unmapped_area_foo(filp,
+			addr0, len, pgoff, flags, UP);
+}
+
+/*
+ * There is no need to export this but sched.h declares the function as
+ * extern so making it static here results in an error.
+ */
+unsigned long arch_get_unmapped_area_topdown(struct file *filp,
+	unsigned long addr0, unsigned long len, unsigned long pgoff,
+	unsigned long flags)
+{
+	return arch_get_unmapped_area_foo(filp,
+			addr0, len, pgoff, flags, DOWN);
 }
 
 void arch_pick_mmap_layout(struct mm_struct *mm)
@@ -89,9 +230,15 @@
 			random_factor &= 0xffffffful;
 	}
 
-	mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
-	mm->get_unmapped_area = arch_get_unmapped_area;
-	mm->unmap_area = arch_unmap_area;
+	if (mmap_is_legacy()) {
+		mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
+		mm->get_unmapped_area = arch_get_unmapped_area;
+		mm->unmap_area = arch_unmap_area;
+	} else {
+		mm->mmap_base = mmap_base(random_factor);
+		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		mm->unmap_area = arch_unmap_area_topdown;
+	}
 }
 
 static inline unsigned long brk_rnd(void)
diff --git a/arch/mips/mm/pgtable-32.c b/arch/mips/mm/pgtable-32.c
index 575e401..adc6911 100644
--- a/arch/mips/mm/pgtable-32.c
+++ b/arch/mips/mm/pgtable-32.c
@@ -52,7 +52,7 @@
 	 * Fixed mappings:
 	 */
 	vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
-	fixrange_init(vaddr, 0, pgd_base);
+	fixrange_init(vaddr, vaddr + FIXADDR_SIZE, pgd_base);
 
 #ifdef CONFIG_HIGHMEM
 	/*
diff --git a/arch/mips/mm/pgtable-64.c b/arch/mips/mm/pgtable-64.c
index 78eaa4f..cda4e30 100644
--- a/arch/mips/mm/pgtable-64.c
+++ b/arch/mips/mm/pgtable-64.c
@@ -76,5 +76,5 @@
 	 * Fixed mappings:
 	 */
 	vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
-	fixrange_init(vaddr, 0, pgd_base);
+	fixrange_init(vaddr, vaddr + FIXADDR_SIZE, pgd_base);
 }
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 424ed4b..b6e1cff 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -42,6 +42,18 @@
 extern void tlb_do_page_fault_0(void);
 extern void tlb_do_page_fault_1(void);
 
+struct work_registers {
+	int r1;
+	int r2;
+	int r3;
+};
+
+struct tlb_reg_save {
+	unsigned long a;
+	unsigned long b;
+} ____cacheline_aligned_in_smp;
+
+static struct tlb_reg_save handler_reg_save[NR_CPUS];
 
 static inline int r45k_bvahwbug(void)
 {
@@ -248,6 +260,73 @@
 static int pgd_reg __cpuinitdata;
 enum vmalloc64_mode {not_refill, refill_scratch, refill_noscratch};
 
+static struct work_registers __cpuinit build_get_work_registers(u32 **p)
+{
+	struct work_registers r;
+
+	int smp_processor_id_reg;
+	int smp_processor_id_sel;
+	int smp_processor_id_shift;
+
+	if (scratch_reg > 0) {
+		/* Save in CPU local C0_KScratch? */
+		UASM_i_MTC0(p, 1, 31, scratch_reg);
+		r.r1 = K0;
+		r.r2 = K1;
+		r.r3 = 1;
+		return r;
+	}
+
+	if (num_possible_cpus() > 1) {
+#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
+		smp_processor_id_shift = 51;
+		smp_processor_id_reg = 20; /* XContext */
+		smp_processor_id_sel = 0;
+#else
+# ifdef CONFIG_32BIT
+		smp_processor_id_shift = 25;
+		smp_processor_id_reg = 4; /* Context */
+		smp_processor_id_sel = 0;
+# endif
+# ifdef CONFIG_64BIT
+		smp_processor_id_shift = 26;
+		smp_processor_id_reg = 4; /* Context */
+		smp_processor_id_sel = 0;
+# endif
+#endif
+		/* Get smp_processor_id */
+		UASM_i_MFC0(p, K0, smp_processor_id_reg, smp_processor_id_sel);
+		UASM_i_SRL_SAFE(p, K0, K0, smp_processor_id_shift);
+
+		/* handler_reg_save index in K0 */
+		UASM_i_SLL(p, K0, K0, ilog2(sizeof(struct tlb_reg_save)));
+
+		UASM_i_LA(p, K1, (long)&handler_reg_save);
+		UASM_i_ADDU(p, K0, K0, K1);
+	} else {
+		UASM_i_LA(p, K0, (long)&handler_reg_save);
+	}
+	/* K0 now points to save area, save $1 and $2  */
+	UASM_i_SW(p, 1, offsetof(struct tlb_reg_save, a), K0);
+	UASM_i_SW(p, 2, offsetof(struct tlb_reg_save, b), K0);
+
+	r.r1 = K1;
+	r.r2 = 1;
+	r.r3 = 2;
+	return r;
+}
+
+static void __cpuinit build_restore_work_registers(u32 **p)
+{
+	if (scratch_reg > 0) {
+		UASM_i_MFC0(p, 1, 31, scratch_reg);
+		return;
+	}
+	/* K0 already points to save area, restore $1 and $2  */
+	UASM_i_LW(p, 1, offsetof(struct tlb_reg_save, a), K0);
+	UASM_i_LW(p, 2, offsetof(struct tlb_reg_save, b), K0);
+}
+
 #ifndef CONFIG_MIPS_PGD_C0_CONTEXT
 
 /*
@@ -1160,9 +1239,6 @@
 	memset(relocs, 0, sizeof(relocs));
 	memset(final_handler, 0, sizeof(final_handler));
 
-	if (scratch_reg == 0)
-		scratch_reg = allocate_kscratch();
-
 	if ((scratch_reg > 0 || scratchpad_available()) && use_bbit_insns()) {
 		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
 							  scratch_reg);
@@ -1462,22 +1538,28 @@
  */
 static void __cpuinit
 build_pte_present(u32 **p, struct uasm_reloc **r,
-		  unsigned int pte, unsigned int ptr, enum label_id lid)
+		  int pte, int ptr, int scratch, enum label_id lid)
 {
+	int t = scratch >= 0 ? scratch : pte;
+
 	if (kernel_uses_smartmips_rixi) {
 		if (use_bbit_insns()) {
 			uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid);
 			uasm_i_nop(p);
 		} else {
-			uasm_i_andi(p, pte, pte, _PAGE_PRESENT);
-			uasm_il_beqz(p, r, pte, lid);
-			iPTE_LW(p, pte, ptr);
+			uasm_i_andi(p, t, pte, _PAGE_PRESENT);
+			uasm_il_beqz(p, r, t, lid);
+			if (pte == t)
+				/* You lose the SMP race :-(*/
+				iPTE_LW(p, pte, ptr);
 		}
 	} else {
-		uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
-		uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
-		uasm_il_bnez(p, r, pte, lid);
-		iPTE_LW(p, pte, ptr);
+		uasm_i_andi(p, t, pte, _PAGE_PRESENT | _PAGE_READ);
+		uasm_i_xori(p, t, t, _PAGE_PRESENT | _PAGE_READ);
+		uasm_il_bnez(p, r, t, lid);
+		if (pte == t)
+			/* You lose the SMP race :-(*/
+			iPTE_LW(p, pte, ptr);
 	}
 }
 
@@ -1497,19 +1579,19 @@
  */
 static void __cpuinit
 build_pte_writable(u32 **p, struct uasm_reloc **r,
-		   unsigned int pte, unsigned int ptr, enum label_id lid)
+		   unsigned int pte, unsigned int ptr, int scratch,
+		   enum label_id lid)
 {
-	if (use_bbit_insns()) {
-		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid);
-		uasm_i_nop(p);
-		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid);
-		uasm_i_nop(p);
-	} else {
-		uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
-		uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
-		uasm_il_bnez(p, r, pte, lid);
+	int t = scratch >= 0 ? scratch : pte;
+
+	uasm_i_andi(p, t, pte, _PAGE_PRESENT | _PAGE_WRITE);
+	uasm_i_xori(p, t, t, _PAGE_PRESENT | _PAGE_WRITE);
+	uasm_il_bnez(p, r, t, lid);
+	if (pte == t)
+		/* You lose the SMP race :-(*/
 		iPTE_LW(p, pte, ptr);
-	}
+	else
+		uasm_i_nop(p);
 }
 
 /* Make PTE writable, update software status bits as well, then store
@@ -1531,15 +1613,19 @@
  */
 static void __cpuinit
 build_pte_modifiable(u32 **p, struct uasm_reloc **r,
-		     unsigned int pte, unsigned int ptr, enum label_id lid)
+		     unsigned int pte, unsigned int ptr, int scratch,
+		     enum label_id lid)
 {
 	if (use_bbit_insns()) {
 		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid);
 		uasm_i_nop(p);
 	} else {
-		uasm_i_andi(p, pte, pte, _PAGE_WRITE);
-		uasm_il_beqz(p, r, pte, lid);
-		iPTE_LW(p, pte, ptr);
+		int t = scratch >= 0 ? scratch : pte;
+		uasm_i_andi(p, t, pte, _PAGE_WRITE);
+		uasm_il_beqz(p, r, t, lid);
+		if (pte == t)
+			/* You lose the SMP race :-(*/
+			iPTE_LW(p, pte, ptr);
 	}
 }
 
@@ -1619,7 +1705,7 @@
 	memset(relocs, 0, sizeof(relocs));
 
 	build_r3000_tlbchange_handler_head(&p, K0, K1);
-	build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
+	build_pte_present(&p, &r, K0, K1, -1, label_nopage_tlbl);
 	uasm_i_nop(&p); /* load delay */
 	build_make_valid(&p, &r, K0, K1);
 	build_r3000_tlb_reload_write(&p, &l, &r, K0, K1);
@@ -1649,7 +1735,7 @@
 	memset(relocs, 0, sizeof(relocs));
 
 	build_r3000_tlbchange_handler_head(&p, K0, K1);
-	build_pte_writable(&p, &r, K0, K1, label_nopage_tlbs);
+	build_pte_writable(&p, &r, K0, K1, -1, label_nopage_tlbs);
 	uasm_i_nop(&p); /* load delay */
 	build_make_write(&p, &r, K0, K1);
 	build_r3000_tlb_reload_write(&p, &l, &r, K0, K1);
@@ -1673,13 +1759,14 @@
 	u32 *p = handle_tlbm;
 	struct uasm_label *l = labels;
 	struct uasm_reloc *r = relocs;
+	struct work_registers wr;
 
 	memset(handle_tlbm, 0, sizeof(handle_tlbm));
 	memset(labels, 0, sizeof(labels));
 	memset(relocs, 0, sizeof(relocs));
 
 	build_r3000_tlbchange_handler_head(&p, K0, K1);
-	build_pte_modifiable(&p, &r, K0, K1, label_nopage_tlbm);
+	build_pte_modifiable(&p, &r, wr.r1, wr.r2,  wr.r3, label_nopage_tlbm);
 	uasm_i_nop(&p); /* load delay */
 	build_make_write(&p, &r, K0, K1);
 	build_r3000_pte_reload_tlbwi(&p, K0, K1);
@@ -1702,15 +1789,16 @@
 /*
  * R4000 style TLB load/store/modify handlers.
  */
-static void __cpuinit
+static struct work_registers __cpuinit
 build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l,
-				   struct uasm_reloc **r, unsigned int pte,
-				   unsigned int ptr)
+				   struct uasm_reloc **r)
 {
+	struct work_registers wr = build_get_work_registers(p);
+
 #ifdef CONFIG_64BIT
-	build_get_pmde64(p, l, r, pte, ptr); /* get pmd in ptr */
+	build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */
 #else
-	build_get_pgde32(p, pte, ptr); /* get pgd in ptr */
+	build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */
 #endif
 
 #ifdef CONFIG_HUGETLB_PAGE
@@ -1719,21 +1807,22 @@
 	 * instead contains the tlb pte. Check the PAGE_HUGE bit and
 	 * see if we need to jump to huge tlb processing.
 	 */
-	build_is_huge_pte(p, r, pte, ptr, label_tlb_huge_update);
+	build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update);
 #endif
 
-	UASM_i_MFC0(p, pte, C0_BADVADDR);
-	UASM_i_LW(p, ptr, 0, ptr);
-	UASM_i_SRL(p, pte, pte, PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2);
-	uasm_i_andi(p, pte, pte, (PTRS_PER_PTE - 1) << PTE_T_LOG2);
-	UASM_i_ADDU(p, ptr, ptr, pte);
+	UASM_i_MFC0(p, wr.r1, C0_BADVADDR);
+	UASM_i_LW(p, wr.r2, 0, wr.r2);
+	UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2);
+	uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2);
+	UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1);
 
 #ifdef CONFIG_SMP
 	uasm_l_smp_pgtable_change(l, *p);
 #endif
-	iPTE_LW(p, pte, ptr); /* get even pte */
+	iPTE_LW(p, wr.r1, wr.r2); /* get even pte */
 	if (!m4kc_tlbp_war())
 		build_tlb_probe_entry(p);
+	return wr;
 }
 
 static void __cpuinit
@@ -1746,6 +1835,7 @@
 	build_update_entries(p, tmp, ptr);
 	build_tlb_write_entry(p, l, r, tlb_indexed);
 	uasm_l_leave(l, *p);
+	build_restore_work_registers(p);
 	uasm_i_eret(p); /* return from trap */
 
 #ifdef CONFIG_64BIT
@@ -1758,6 +1848,7 @@
 	u32 *p = handle_tlbl;
 	struct uasm_label *l = labels;
 	struct uasm_reloc *r = relocs;
+	struct work_registers wr;
 
 	memset(handle_tlbl, 0, sizeof(handle_tlbl));
 	memset(labels, 0, sizeof(labels));
@@ -1777,8 +1868,8 @@
 		/* No need for uasm_i_nop */
 	}
 
-	build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1);
-	build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
+	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
+	build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl);
 	if (m4kc_tlbp_war())
 		build_tlb_probe_entry(&p);
 
@@ -1788,44 +1879,43 @@
 		 * have triggered it.  Skip the expensive test..
 		 */
 		if (use_bbit_insns()) {
-			uasm_il_bbit0(&p, &r, K0, ilog2(_PAGE_VALID),
+			uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID),
 				      label_tlbl_goaround1);
 		} else {
-			uasm_i_andi(&p, K0, K0, _PAGE_VALID);
-			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround1);
+			uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID);
+			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround1);
 		}
 		uasm_i_nop(&p);
 
 		uasm_i_tlbr(&p);
 		/* Examine  entrylo 0 or 1 based on ptr. */
 		if (use_bbit_insns()) {
-			uasm_i_bbit0(&p, K1, ilog2(sizeof(pte_t)), 8);
+			uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
 		} else {
-			uasm_i_andi(&p, K0, K1, sizeof(pte_t));
-			uasm_i_beqz(&p, K0, 8);
+			uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t));
+			uasm_i_beqz(&p, wr.r3, 8);
 		}
-
-		UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
-		UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
+		/* load it in the delay slot*/
+		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0);
+		/* load it if ptr is odd */
+		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1);
 		/*
-		 * If the entryLo (now in K0) is valid (bit 1), RI or
+		 * If the entryLo (now in wr.r3) is valid (bit 1), RI or
 		 * XI must have triggered it.
 		 */
 		if (use_bbit_insns()) {
-			uasm_il_bbit1(&p, &r, K0, 1, label_nopage_tlbl);
-			/* Reload the PTE value */
-			iPTE_LW(&p, K0, K1);
+			uasm_il_bbit1(&p, &r, wr.r3, 1, label_nopage_tlbl);
+			uasm_i_nop(&p);
 			uasm_l_tlbl_goaround1(&l, p);
 		} else {
-			uasm_i_andi(&p, K0, K0, 2);
-			uasm_il_bnez(&p, &r, K0, label_nopage_tlbl);
-			uasm_l_tlbl_goaround1(&l, p);
-			/* Reload the PTE value */
-			iPTE_LW(&p, K0, K1);
+			uasm_i_andi(&p, wr.r3, wr.r3, 2);
+			uasm_il_bnez(&p, &r, wr.r3, label_nopage_tlbl);
+			uasm_i_nop(&p);
 		}
+		uasm_l_tlbl_goaround1(&l, p);
 	}
-	build_make_valid(&p, &r, K0, K1);
-	build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1);
+	build_make_valid(&p, &r, wr.r1, wr.r2);
+	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
 
 #ifdef CONFIG_HUGETLB_PAGE
 	/*
@@ -1833,8 +1923,8 @@
 	 * spots a huge page.
 	 */
 	uasm_l_tlb_huge_update(&l, p);
-	iPTE_LW(&p, K0, K1);
-	build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
+	iPTE_LW(&p, wr.r1, wr.r2);
+	build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl);
 	build_tlb_probe_entry(&p);
 
 	if (kernel_uses_smartmips_rixi) {
@@ -1843,50 +1933,51 @@
 		 * have triggered it.  Skip the expensive test..
 		 */
 		if (use_bbit_insns()) {
-			uasm_il_bbit0(&p, &r, K0, ilog2(_PAGE_VALID),
+			uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID),
 				      label_tlbl_goaround2);
 		} else {
-			uasm_i_andi(&p, K0, K0, _PAGE_VALID);
-			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+			uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID);
+			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2);
 		}
 		uasm_i_nop(&p);
 
 		uasm_i_tlbr(&p);
 		/* Examine  entrylo 0 or 1 based on ptr. */
 		if (use_bbit_insns()) {
-			uasm_i_bbit0(&p, K1, ilog2(sizeof(pte_t)), 8);
+			uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
 		} else {
-			uasm_i_andi(&p, K0, K1, sizeof(pte_t));
-			uasm_i_beqz(&p, K0, 8);
+			uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t));
+			uasm_i_beqz(&p, wr.r3, 8);
 		}
-		UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
-		UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
+		/* load it in the delay slot*/
+		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0);
+		/* load it if ptr is odd */
+		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1);
 		/*
-		 * If the entryLo (now in K0) is valid (bit 1), RI or
+		 * If the entryLo (now in wr.r3) is valid (bit 1), RI or
 		 * XI must have triggered it.
 		 */
 		if (use_bbit_insns()) {
-			uasm_il_bbit0(&p, &r, K0, 1, label_tlbl_goaround2);
+			uasm_il_bbit0(&p, &r, wr.r3, 1, label_tlbl_goaround2);
 		} else {
-			uasm_i_andi(&p, K0, K0, 2);
-			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+			uasm_i_andi(&p, wr.r3, wr.r3, 2);
+			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2);
 		}
-		/* Reload the PTE value */
-		iPTE_LW(&p, K0, K1);
 
 		/*
 		 * We clobbered C0_PAGEMASK, restore it.  On the other branch
 		 * it is restored in build_huge_tlb_write_entry.
 		 */
-		build_restore_pagemask(&p, &r, K0, label_nopage_tlbl, 0);
+		build_restore_pagemask(&p, &r, wr.r3, label_nopage_tlbl, 0);
 
 		uasm_l_tlbl_goaround2(&l, p);
 	}
-	uasm_i_ori(&p, K0, K0, (_PAGE_ACCESSED | _PAGE_VALID));
-	build_huge_handler_tail(&p, &r, &l, K0, K1);
+	uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID));
+	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
 #endif
 
 	uasm_l_nopage_tlbl(&l, p);
+	build_restore_work_registers(&p);
 	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
 	uasm_i_nop(&p);
 
@@ -1905,17 +1996,18 @@
 	u32 *p = handle_tlbs;
 	struct uasm_label *l = labels;
 	struct uasm_reloc *r = relocs;
+	struct work_registers wr;
 
 	memset(handle_tlbs, 0, sizeof(handle_tlbs));
 	memset(labels, 0, sizeof(labels));
 	memset(relocs, 0, sizeof(relocs));
 
-	build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1);
-	build_pte_writable(&p, &r, K0, K1, label_nopage_tlbs);
+	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
+	build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs);
 	if (m4kc_tlbp_war())
 		build_tlb_probe_entry(&p);
-	build_make_write(&p, &r, K0, K1);
-	build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1);
+	build_make_write(&p, &r, wr.r1, wr.r2);
+	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
 
 #ifdef CONFIG_HUGETLB_PAGE
 	/*
@@ -1923,15 +2015,16 @@
 	 * build_r4000_tlbchange_handler_head spots a huge page.
 	 */
 	uasm_l_tlb_huge_update(&l, p);
-	iPTE_LW(&p, K0, K1);
-	build_pte_writable(&p, &r, K0, K1, label_nopage_tlbs);
+	iPTE_LW(&p, wr.r1, wr.r2);
+	build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs);
 	build_tlb_probe_entry(&p);
-	uasm_i_ori(&p, K0, K0,
+	uasm_i_ori(&p, wr.r1, wr.r1,
 		   _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
-	build_huge_handler_tail(&p, &r, &l, K0, K1);
+	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
 #endif
 
 	uasm_l_nopage_tlbs(&l, p);
+	build_restore_work_registers(&p);
 	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
 	uasm_i_nop(&p);
 
@@ -1950,18 +2043,19 @@
 	u32 *p = handle_tlbm;
 	struct uasm_label *l = labels;
 	struct uasm_reloc *r = relocs;
+	struct work_registers wr;
 
 	memset(handle_tlbm, 0, sizeof(handle_tlbm));
 	memset(labels, 0, sizeof(labels));
 	memset(relocs, 0, sizeof(relocs));
 
-	build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1);
-	build_pte_modifiable(&p, &r, K0, K1, label_nopage_tlbm);
+	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
+	build_pte_modifiable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbm);
 	if (m4kc_tlbp_war())
 		build_tlb_probe_entry(&p);
 	/* Present and writable bits set, set accessed and dirty bits. */
-	build_make_write(&p, &r, K0, K1);
-	build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1);
+	build_make_write(&p, &r, wr.r1, wr.r2);
+	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
 
 #ifdef CONFIG_HUGETLB_PAGE
 	/*
@@ -1969,15 +2063,16 @@
 	 * build_r4000_tlbchange_handler_head spots a huge page.
 	 */
 	uasm_l_tlb_huge_update(&l, p);
-	iPTE_LW(&p, K0, K1);
-	build_pte_modifiable(&p, &r, K0, K1, label_nopage_tlbm);
+	iPTE_LW(&p, wr.r1, wr.r2);
+	build_pte_modifiable(&p, &r, wr.r1, wr.r2,  wr.r3, label_nopage_tlbm);
 	build_tlb_probe_entry(&p);
-	uasm_i_ori(&p, K0, K0,
+	uasm_i_ori(&p, wr.r1, wr.r1,
 		   _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
-	build_huge_handler_tail(&p, &r, &l, K0, K1);
+	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
 #endif
 
 	uasm_l_nopage_tlbm(&l, p);
+	build_restore_work_registers(&p);
 	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
 	uasm_i_nop(&p);
 
@@ -2036,6 +2131,7 @@
 
 	default:
 		if (!run_once) {
+			scratch_reg = allocate_kscratch();
 #ifdef CONFIG_MIPS_PGD_C0_CONTEXT
 			build_r4000_setup_pgd();
 #endif
diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c
index 31180c3..4b988b9 100644
--- a/arch/mips/mti-malta/malta-init.c
+++ b/arch/mips/mti-malta/malta-init.c
@@ -28,6 +28,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/cacheflush.h>
+#include <asm/smp-ops.h>
 #include <asm/traps.h>
 
 #include <asm/gcmpregs.h>
@@ -358,15 +359,14 @@
 #ifdef CONFIG_SERIAL_8250_CONSOLE
 	console_config();
 #endif
-#ifdef CONFIG_MIPS_CMP
 	/* Early detection of CMP support */
 	if (gcmp_probe(GCMP_BASE_ADDR, GCMP_ADDRSPACE_SZ))
-		register_smp_ops(&cmp_smp_ops);
-	else
-#endif
-#ifdef CONFIG_MIPS_MT_SMP
-		register_smp_ops(&vsmp_smp_ops);
-#endif
+		if (!register_cmp_smp_ops())
+			return;
+
+	if (!register_vsmp_smp_ops())
+		return;
+
 #ifdef CONFIG_MIPS_MT_SMTC
 	register_smp_ops(&msmtc_smp_ops);
 #endif
diff --git a/arch/mips/mti-malta/malta-smtc.c b/arch/mips/mti-malta/malta-smtc.c
index 49a38b0..1efc8c3 100644
--- a/arch/mips/mti-malta/malta-smtc.c
+++ b/arch/mips/mti-malta/malta-smtc.c
@@ -152,7 +152,7 @@
 		 * runtime code can anyway deal with the null set
 		 */
 		printk(KERN_WARNING
-			"IRQ affinity leaves no legal CPU for IRQ %d\n", irq);
+		       "IRQ affinity leaves no legal CPU for IRQ %d\n", d->irq);
 
 	/* Do any generic SMTC IRQ affinity setup */
 	smtc_set_irq_affinity(d->irq, tmask);
diff --git a/arch/mips/netlogic/Platform b/arch/mips/netlogic/Platform
new file mode 100644
index 0000000..f87c164
--- /dev/null
+++ b/arch/mips/netlogic/Platform
@@ -0,0 +1,11 @@
+#
+# NETLOGIC includes
+#
+cflags-$(CONFIG_NLM_COMMON)  += -I$(srctree)/arch/mips/include/asm/mach-netlogic
+cflags-$(CONFIG_NLM_COMMON)  += -I$(srctree)/arch/mips/include/asm/netlogic
+
+#
+# NETLOGIC XLR/XLS SoC, Simulator and boards
+#
+core-$(CONFIG_NLM_XLR)	      += arch/mips/netlogic/xlr/
+load-$(CONFIG_NLM_XLR_BOARD)  += 0xffffffff84000000
diff --git a/arch/mips/netlogic/xlr/irq.c b/arch/mips/netlogic/xlr/irq.c
index 1446d58..521bb73 100644
--- a/arch/mips/netlogic/xlr/irq.c
+++ b/arch/mips/netlogic/xlr/irq.c
@@ -209,7 +209,7 @@
 			irq_set_chip_and_handler(i, &xlr_pic, handle_level_irq);
 		else
 			irq_set_chip_and_handler(i, &nlm_cpu_intr,
-						handle_level_irq);
+						handle_percpu_irq);
 	}
 #ifdef CONFIG_SMP
 	irq_set_chip_and_handler(IRQ_IPI_SMP_FUNCTION, &nlm_cpu_intr,
diff --git a/arch/mips/netlogic/xlr/smp.c b/arch/mips/netlogic/xlr/smp.c
index b495a7f..d842bce 100644
--- a/arch/mips/netlogic/xlr/smp.c
+++ b/arch/mips/netlogic/xlr/smp.c
@@ -87,17 +87,7 @@
 /* IRQ_IPI_SMP_RESCHEDULE  handler */
 void nlm_smp_resched_ipi_handler(unsigned int irq, struct irq_desc *desc)
 {
-	set_need_resched();
-}
-
-void nlm_common_ipi_handler(int irq, struct pt_regs *regs)
-{
-	if (irq == IRQ_IPI_SMP_FUNCTION) {
-		smp_call_function_interrupt();
-	} else {
-		/* Announce that we are for reschduling */
-		set_need_resched();
-	}
+	scheduler_ipi();
 }
 
 /*
@@ -122,6 +112,7 @@
 #ifdef notyet
 	nlm_common_msgring_cpu_init();
 #endif
+	local_irq_enable();
 }
 
 void nlm_cpus_done(void)
diff --git a/arch/mips/nxp/pnx8550/common/setup.c b/arch/mips/nxp/pnx8550/common/setup.c
index 64246c9..71adac3 100644
--- a/arch/mips/nxp/pnx8550/common/setup.c
+++ b/arch/mips/nxp/pnx8550/common/setup.c
@@ -140,6 +140,4 @@
 			PNX8XXX_UART_LCR_8BIT;
 		ip3106_baud(UART_BASE, pnx8550_console_port) = 5;
 	}
-
-	return;
 }
diff --git a/arch/mips/pci/ops-nile4.c b/arch/mips/pci/ops-nile4.c
index b7f0fb0..99929cf 100644
--- a/arch/mips/pci/ops-nile4.c
+++ b/arch/mips/pci/ops-nile4.c
@@ -4,7 +4,6 @@
 #include <asm/bootinfo.h>
 
 #include <asm/lasat/lasat.h>
-#include <asm/gt64120.h>
 #include <asm/nile4.h>
 
 #define PCI_ACCESS_READ  0
diff --git a/arch/mips/pmc-sierra/msp71xx/msp_setup.c b/arch/mips/pmc-sierra/msp71xx/msp_setup.c
index 2413ea6..0abfbe0 100644
--- a/arch/mips/pmc-sierra/msp71xx/msp_setup.c
+++ b/arch/mips/pmc-sierra/msp71xx/msp_setup.c
@@ -228,13 +228,11 @@
 	 */
 	msp_serial_setup();
 
-#ifdef CONFIG_MIPS_MT_SMP
-	register_smp_ops(&vsmp_smp_ops);
-#endif
-
+	if (register_vsmp_smp_ops()) {
 #ifdef CONFIG_MIPS_MT_SMTC
-	register_smp_ops(&msp_smtc_smp_ops);
+		register_smp_ops(&msp_smtc_smp_ops);
 #endif
+	}
 
 #ifdef CONFIG_PMCTWILED
 	/*
diff --git a/arch/mips/pnx8550/common/setup.c b/arch/mips/pnx8550/common/setup.c
index 43cb394..fccd6b0 100644
--- a/arch/mips/pnx8550/common/setup.c
+++ b/arch/mips/pnx8550/common/setup.c
@@ -139,6 +139,4 @@
 			PNX8XXX_UART_LCR_8BIT;
 		ip3106_baud(UART_BASE, pnx8550_console_port) = 5;
 	}
-
-	return;
 }
diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c
index 041fc1a..a969eb8 100644
--- a/arch/mips/rb532/devices.c
+++ b/arch/mips/rb532/devices.c
@@ -251,28 +251,22 @@
 
 static void __init parse_mac_addr(char *macstr)
 {
-	int i, j;
-	unsigned char result, value;
+	int i, h, l;
 
 	for (i = 0; i < 6; i++) {
-		result = 0;
-
 		if (i != 5 && *(macstr + 2) != ':')
 			return;
 
-		for (j = 0; j < 2; j++) {
-			if (isxdigit(*macstr)
-			    && (value =
-				isdigit(*macstr) ? *macstr -
-				'0' : toupper(*macstr) - 'A' + 10) < 16) {
-				result = result * 16 + value;
-				macstr++;
-			} else
-				return;
-		}
+		h = hex_to_bin(*macstr++);
+		if (h == -1)
+			return;
+
+		l = hex_to_bin(*macstr++);
+		if (l == -1)
+			return;
 
 		macstr++;
-		korina_dev0_data.mac[i] = result;
+		korina_dev0_data.mac[i] = (h << 4) + l;
 	}
 }
 
diff --git a/arch/mips/sgi-ip27/ip27-nmi.c b/arch/mips/sgi-ip27/ip27-nmi.c
index bc4fa8d..005c29e 100644
--- a/arch/mips/sgi-ip27/ip27-nmi.c
+++ b/arch/mips/sgi-ip27/ip27-nmi.c
@@ -3,7 +3,7 @@
 #include <linux/nodemask.h>
 #include <linux/spinlock.h>
 #include <linux/smp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/sn/types.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/nmi.h>
diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c
index be4460a..76ee045 100644
--- a/arch/mips/sibyte/sb1250/irq.c
+++ b/arch/mips/sibyte/sb1250/irq.c
@@ -123,6 +123,13 @@
 }
 #endif
 
+static void disable_sb1250_irq(struct irq_data *d)
+{
+	unsigned int irq = d->irq;
+
+	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
+}
+
 static void enable_sb1250_irq(struct irq_data *d)
 {
 	unsigned int irq = d->irq;
@@ -180,6 +187,7 @@
 	.name = "SB1250-IMR",
 	.irq_mask_ack = ack_sb1250_irq,
 	.irq_unmask = enable_sb1250_irq,
+	.irq_mask = disable_sb1250_irq,
 #ifdef CONFIG_SMP
 	.irq_set_affinity = sb1250_set_affinity
 #endif
diff --git a/arch/mn10300/include/asm/atomic.h b/arch/mn10300/include/asm/atomic.h
index 9d773a6..b9a8f84 100644
--- a/arch/mn10300/include/asm/atomic.h
+++ b/arch/mn10300/include/asm/atomic.h
@@ -260,16 +260,15 @@
 #define atomic_dec_and_test(v)		(atomic_sub_return(1, (v)) == 0)
 #define atomic_inc_and_test(v)		(atomic_add_return(1, (v)) == 0)
 
-#define atomic_add_unless(v, a, u)				\
+#define __atomic_add_unless(v, a, u)				\
 ({								\
 	int c, old;						\
 	c = atomic_read(v);					\
 	while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
 		c = old;					\
-	c != (u);						\
+	c;							\
 })
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /**
  * atomic_clear_mask - Atomically clear bits in memory
@@ -344,8 +343,6 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
-
 #endif /* __KERNEL__ */
 #endif /* CONFIG_SMP */
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/mn10300/include/asm/bitops.h b/arch/mn10300/include/asm/bitops.h
index 0939462..596bb27 100644
--- a/arch/mn10300/include/asm/bitops.h
+++ b/arch/mn10300/include/asm/bitops.h
@@ -227,12 +227,7 @@
 #include <asm-generic/bitops/find.h>
 #include <asm-generic/bitops/sched.h>
 #include <asm-generic/bitops/hweight.h>
-
-#define ext2_set_bit_atomic(lock, nr, addr) \
-	test_and_set_bit((nr), (addr))
-#define ext2_clear_bit_atomic(lock, nr, addr) \
-	test_and_clear_bit((nr), (addr))
-
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 #include <asm-generic/bitops/le.h>
 
 #endif /* __KERNEL__ */
diff --git a/arch/mn10300/include/asm/mmu_context.h b/arch/mn10300/include/asm/mmu_context.h
index c8f6c82..c67c2b5 100644
--- a/arch/mn10300/include/asm/mmu_context.h
+++ b/arch/mn10300/include/asm/mmu_context.h
@@ -22,7 +22,7 @@
 #ifndef _ASM_MMU_CONTEXT_H
 #define _ASM_MMU_CONTEXT_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
 #include <asm-generic/mm_hooks.h>
diff --git a/arch/mn10300/include/asm/processor.h b/arch/mn10300/include/asm/processor.h
index 4c1b5cc..f7b3c9a 100644
--- a/arch/mn10300/include/asm/processor.h
+++ b/arch/mn10300/include/asm/processor.h
@@ -127,7 +127,6 @@
 {
 	struct thread_info *ti = current_thread_info();
 	struct pt_regs *frame0;
-	set_fs(USER_DS);
 
 	frame0 = thread_info_to_uregs(ti);
 	frame0->epsw = EPSW_nSL | EPSW_IE | EPSW_IM;
diff --git a/arch/mn10300/include/asm/ptrace.h b/arch/mn10300/include/asm/ptrace.h
index b696181..55b79ef 100644
--- a/arch/mn10300/include/asm/ptrace.h
+++ b/arch/mn10300/include/asm/ptrace.h
@@ -89,7 +89,6 @@
 #define user_mode(regs)			(((regs)->epsw & EPSW_nSL) == EPSW_nSL)
 #define instruction_pointer(regs)	((regs)->pc)
 #define user_stack_pointer(regs)	((regs)->sp)
-extern void show_regs(struct pt_regs *);
 
 #define arch_has_single_step()	(1)
 
diff --git a/arch/mn10300/include/asm/spinlock.h b/arch/mn10300/include/asm/spinlock.h
index 9342915..1ae580f 100644
--- a/arch/mn10300/include/asm/spinlock.h
+++ b/arch/mn10300/include/asm/spinlock.h
@@ -11,7 +11,7 @@
 #ifndef _ASM_SPINLOCK_H
 #define _ASM_SPINLOCK_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/rwlock.h>
 #include <asm/page.h>
 
diff --git a/arch/mn10300/include/asm/system.h b/arch/mn10300/include/asm/system.h
index 8ff3e5a..94b4c5e 100644
--- a/arch/mn10300/include/asm/system.h
+++ b/arch/mn10300/include/asm/system.h
@@ -19,7 +19,7 @@
 
 #include <linux/kernel.h>
 #include <linux/irqflags.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #if !defined(CONFIG_LAZY_SAVE_FPU)
 struct fpu_state_struct;
diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c
index c5e12bf..a45f0c7 100644
--- a/arch/mn10300/kernel/mn10300-watchdog.c
+++ b/arch/mn10300/kernel/mn10300-watchdog.c
@@ -19,7 +19,7 @@
 #include <linux/nmi.h>
 #include <asm/processor.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/intctl-regs.h>
 #include <asm/rtc-regs.h>
 #include <asm/div64.h>
diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c
index bd3e5e7..9220a75 100644
--- a/arch/mn10300/kernel/traps.c
+++ b/arch/mn10300/kernel/traps.c
@@ -30,7 +30,7 @@
 #include <asm/system.h>
 #include <linux/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 #include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c
index eef989c..f9bb8cb 100644
--- a/arch/mn10300/mm/misalignment.c
+++ b/arch/mn10300/mm/misalignment.c
@@ -26,7 +26,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 #include <asm/pgalloc.h>
 #include <asm/cpu-regs.h>
diff --git a/arch/mn10300/proc-mn2ws0050/proc-init.c b/arch/mn10300/proc-mn2ws0050/proc-init.c
index c58249b..fe6e249 100644
--- a/arch/mn10300/proc-mn2ws0050/proc-init.c
+++ b/arch/mn10300/proc-mn2ws0050/proc-init.c
@@ -18,7 +18,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 #include <asm/pgalloc.h>
 #include <asm/busctl-regs.h>
diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
index f819559..b1dc71f 100644
--- a/arch/parisc/include/asm/atomic.h
+++ b/arch/parisc/include/asm/atomic.h
@@ -197,15 +197,15 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -217,10 +217,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #define atomic_add(i,v)	((void)(__atomic_add_return( (i),(v))))
 #define atomic_sub(i,v)	((void)(__atomic_add_return(-(i),(v))))
@@ -317,7 +316,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 {
@@ -336,12 +335,7 @@
 
 #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
 
-#else /* CONFIG_64BIT */
-
-#include <asm-generic/atomic64.h>
-
 #endif /* !CONFIG_64BIT */
 
-#include <asm-generic/atomic-long.h>
 
 #endif /* _ASM_PARISC_ATOMIC_H_ */
diff --git a/arch/parisc/include/asm/bitops.h b/arch/parisc/include/asm/bitops.h
index 43c516f..8c9b631 100644
--- a/arch/parisc/include/asm/bitops.h
+++ b/arch/parisc/include/asm/bitops.h
@@ -8,7 +8,7 @@
 #include <linux/compiler.h>
 #include <asm/types.h>		/* for BITS_PER_LONG/SHIFT_PER_LONG */
 #include <asm/byteorder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * HP-PARISC specific bit operations
@@ -223,14 +223,7 @@
 #ifdef __KERNEL__
 
 #include <asm-generic/bitops/le.h>
-
-/* '3' is bits per byte */
-#define LE_BYTE_ADDR ((sizeof(unsigned long) - 1) << 3)
-
-#define ext2_set_bit_atomic(l,nr,addr) \
-		test_and_set_bit((nr)   ^ LE_BYTE_ADDR, (unsigned long *)addr)
-#define ext2_clear_bit_atomic(l,nr,addr) \
-		test_and_clear_bit( (nr) ^ LE_BYTE_ADDR, (unsigned long *)addr)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif	/* __KERNEL__ */
 
diff --git a/arch/parisc/include/asm/mmu_context.h b/arch/parisc/include/asm/mmu_context.h
index 354b2ac..59be257 100644
--- a/arch/parisc/include/asm/mmu_context.h
+++ b/arch/parisc/include/asm/mmu_context.h
@@ -3,7 +3,7 @@
 
 #include <linux/mm.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
 #include <asm-generic/mm_hooks.h>
diff --git a/arch/parisc/include/asm/ptrace.h b/arch/parisc/include/asm/ptrace.h
index 7f09533..250ae35 100644
--- a/arch/parisc/include/asm/ptrace.h
+++ b/arch/parisc/include/asm/ptrace.h
@@ -56,7 +56,6 @@
 #define instruction_pointer(regs)	((regs)->iaoq[0] & ~3)
 #define user_stack_pointer(regs)	((regs)->gr[30])
 unsigned long profile_pc(struct pt_regs *);
-extern void show_regs(struct pt_regs *);
 
 
 #endif /* __KERNEL__ */
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index df65366..a7bb757 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -31,7 +31,7 @@
 #include <linux/string.h>
 EXPORT_SYMBOL(memset);
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 EXPORT_SYMBOL(__xchg8);
 EXPORT_SYMBOL(__xchg32);
 EXPORT_SYMBOL(__cmpxchg_u32);
diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c
index 828305f..32d5884 100644
--- a/arch/parisc/kernel/smp.c
+++ b/arch/parisc/kernel/smp.c
@@ -33,7 +33,7 @@
 #include <linux/ftrace.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/current.h>
 #include <asm/delay.h>
 #include <asm/tlbflush.h>
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 8b58bf0..f19e660 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -33,7 +33,7 @@
 #include <asm/irq.h>
 #include <asm/traps.h>
 #include <asm/unaligned.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 #include <asm/pdc.h>
 #include <asm/pdc_chassis.h>
diff --git a/arch/parisc/lib/bitops.c b/arch/parisc/lib/bitops.c
index 353963d..a8bffd8 100644
--- a/arch/parisc/lib/bitops.c
+++ b/arch/parisc/lib/bitops.c
@@ -9,7 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_SMP
 arch_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned = {
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
index b8f152e..e2a4c26 100644
--- a/arch/powerpc/include/asm/atomic.h
+++ b/arch/powerpc/include/asm/atomic.h
@@ -181,21 +181,21 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int t;
 
 	__asm__ __volatile__ (
 	PPC_RELEASE_BARRIER
-"1:	lwarx	%0,0,%1		# atomic_add_unless\n\
+"1:	lwarx	%0,0,%1		# __atomic_add_unless\n\
 	cmpw	0,%0,%3 \n\
 	beq-	2f \n\
 	add	%0,%2,%0 \n"
@@ -209,10 +209,9 @@
 	: "r" (&v->counter), "r" (a), "r" (u)
 	: "cc", "memory");
 
-	return t != u;
+	return t;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #define atomic_sub_and_test(a, v)	(atomic_sub_return((a), (v)) == 0)
 #define atomic_dec_and_test(v)		(atomic_dec_return((v)) == 0)
@@ -444,7 +443,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 {
@@ -452,7 +451,7 @@
 
 	__asm__ __volatile__ (
 	PPC_RELEASE_BARRIER
-"1:	ldarx	%0,0,%1		# atomic_add_unless\n\
+"1:	ldarx	%0,0,%1		# __atomic_add_unless\n\
 	cmpd	0,%0,%3 \n\
 	beq-	2f \n\
 	add	%0,%2,%0 \n"
@@ -470,11 +469,7 @@
 
 #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
 
-#else  /* __powerpc64__ */
-#include <asm-generic/atomic64.h>
-
 #endif /* __powerpc64__ */
 
-#include <asm-generic/atomic-long.h>
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_ATOMIC_H_ */
diff --git a/arch/powerpc/include/asm/bitops.h b/arch/powerpc/include/asm/bitops.h
index f18c6d9..e137afc 100644
--- a/arch/powerpc/include/asm/bitops.h
+++ b/arch/powerpc/include/asm/bitops.h
@@ -327,10 +327,7 @@
 				    unsigned long size, unsigned long offset);
 /* Bitmap functions for the ext2 filesystem */
 
-#define ext2_set_bit_atomic(lock, nr, addr) \
-	test_and_set_bit_le((nr), (unsigned long*)addr)
-#define ext2_clear_bit_atomic(lock, nr, addr) \
-	test_and_clear_bit_le((nr), (unsigned long*)addr)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #include <asm-generic/bitops/sched.h>
 
diff --git a/arch/powerpc/include/asm/emulated_ops.h b/arch/powerpc/include/asm/emulated_ops.h
index 2cc41c7..63f2a22 100644
--- a/arch/powerpc/include/asm/emulated_ops.h
+++ b/arch/powerpc/include/asm/emulated_ops.h
@@ -18,7 +18,7 @@
 #ifndef _ASM_POWERPC_EMULATED_OPS_H
 #define _ASM_POWERPC_EMULATED_OPS_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/perf_event.h>
 
 
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index c57a28e..c0e1bc3 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -14,7 +14,7 @@
 #include <linux/radix-tree.h>
 
 #include <asm/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 
 /* Define a way to iterate across irqs. */
diff --git a/arch/powerpc/include/asm/local.h b/arch/powerpc/include/asm/local.h
index c2410af..b8da913 100644
--- a/arch/powerpc/include/asm/local.h
+++ b/arch/powerpc/include/asm/local.h
@@ -2,7 +2,7 @@
 #define _ARCH_POWERPC_LOCAL_H
 
 #include <linux/percpu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 typedef struct
 {
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 90bd3ed..56b879a 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -10,58 +10,10 @@
 #include <linux/pci.h>
 #include <linux/list.h>
 #include <linux/ioport.h>
+#include <asm-generic/pci-bridge.h>
 
 struct device_node;
 
-enum {
-	/* Force re-assigning all resources (ignore firmware
-	 * setup completely)
-	 */
-	PPC_PCI_REASSIGN_ALL_RSRC	= 0x00000001,
-
-	/* Re-assign all bus numbers */
-	PPC_PCI_REASSIGN_ALL_BUS	= 0x00000002,
-
-	/* Do not try to assign, just use existing setup */
-	PPC_PCI_PROBE_ONLY		= 0x00000004,
-
-	/* Don't bother with ISA alignment unless the bridge has
-	 * ISA forwarding enabled
-	 */
-	PPC_PCI_CAN_SKIP_ISA_ALIGN	= 0x00000008,
-
-	/* Enable domain numbers in /proc */
-	PPC_PCI_ENABLE_PROC_DOMAINS	= 0x00000010,
-	/* ... except for domain 0 */
-	PPC_PCI_COMPAT_DOMAIN_0		= 0x00000020,
-};
-#ifdef CONFIG_PCI
-extern unsigned int ppc_pci_flags;
-
-static inline void ppc_pci_set_flags(int flags)
-{
-	ppc_pci_flags = flags;
-}
-
-static inline void ppc_pci_add_flags(int flags)
-{
-	ppc_pci_flags |= flags;
-}
-
-static inline int ppc_pci_has_flag(int flag)
-{
-	return (ppc_pci_flags & flag);
-}
-#else
-static inline void ppc_pci_set_flags(int flags) { }
-static inline void ppc_pci_add_flags(int flags) { }
-static inline int ppc_pci_has_flag(int flag)
-{
-	return 0;
-}
-#endif
-
-
 /*
  * Structure of a PCI controller (host bridge)
  */
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h
index 1f52268..49c3de5 100644
--- a/arch/powerpc/include/asm/pci.h
+++ b/arch/powerpc/include/asm/pci.h
@@ -44,7 +44,7 @@
  * bus numbers (don't do that on ppc64 yet !)
  */
 #define pcibios_assign_all_busses() \
-	(ppc_pci_has_flag(PPC_PCI_REASSIGN_ALL_BUS))
+	(pci_has_flag(PCI_REASSIGN_ALL_BUS))
 
 static inline void pcibios_set_master(struct pci_dev *dev)
 {
diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h
index b823536..b5c9190 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -18,7 +18,7 @@
  */
 #include <linux/types.h>
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define HAVE_ARCH_DEVTREE_FIXUPS
 
diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h
index 2dc595d..e30a13d 100644
--- a/arch/powerpc/include/asm/system.h
+++ b/arch/powerpc/include/asm/system.h
@@ -120,7 +120,6 @@
 		    unsigned long error_code);
 #endif
 extern void print_backtrace(unsigned long *);
-extern void show_regs(struct pt_regs * regs);
 extern void flush_instruction_cache(void);
 extern void hard_reset_now(void);
 extern void poweroff_now(void);
diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
index 2458218..59dbf6a 100644
--- a/arch/powerpc/kernel/of_platform.c
+++ b/arch/powerpc/kernel/of_platform.c
@@ -26,7 +26,7 @@
 #include <asm/topology.h>
 #include <asm/pci-bridge.h>
 #include <asm/ppc-pci.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_PPC_OF_PLATFORM_PCI
 
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 0187829..32656f1 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -50,7 +50,7 @@
 resource_size_t isa_mem_base;
 
 /* Default PCI flags is 0 on ppc32, modified at boot on ppc64 */
-unsigned int ppc_pci_flags = 0;
+unsigned int pci_flags = 0;
 
 
 static struct dma_map_ops *pci_dma_ops = &dma_direct_ops;
@@ -842,9 +842,9 @@
 {
 	struct pci_controller *hose = pci_bus_to_host(bus);
 
-	if (!(ppc_pci_flags & PPC_PCI_ENABLE_PROC_DOMAINS))
+	if (!pci_has_flag(PCI_ENABLE_PROC_DOMAINS))
 		return 0;
-	if (ppc_pci_flags & PPC_PCI_COMPAT_DOMAIN_0)
+	if (pci_has_flag(PCI_COMPAT_DOMAIN_0))
 		return hose->global_number != 0;
 	return 1;
 }
@@ -920,13 +920,13 @@
 		struct resource *res = dev->resource + i;
 		if (!res->flags)
 			continue;
-		/* On platforms that have PPC_PCI_PROBE_ONLY set, we don't
+		/* On platforms that have PCI_PROBE_ONLY set, we don't
 		 * consider 0 as an unassigned BAR value. It's technically
 		 * a valid value, but linux doesn't like it... so when we can
 		 * re-assign things, we do so, but if we can't, we keep it
 		 * around and hope for the best...
 		 */
-		if (res->start == 0 && !(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) {
+		if (res->start == 0 && !pci_has_flag(PCI_PROBE_ONLY)) {
 			pr_debug("PCI:%s Resource %d %016llx-%016llx [%x] is unassigned\n",
 				 pci_name(dev), i,
 				 (unsigned long long)res->start,
@@ -973,7 +973,7 @@
 	int i;
 
 	/* We don't do anything if PCI_PROBE_ONLY is set */
-	if (ppc_pci_flags & PPC_PCI_PROBE_ONLY)
+	if (pci_has_flag(PCI_PROBE_ONLY))
 		return 0;
 
 	/* Job is a bit different between memory and IO */
@@ -1143,7 +1143,7 @@
 
 static int skip_isa_ioresource_align(struct pci_dev *dev)
 {
-	if ((ppc_pci_flags & PPC_PCI_CAN_SKIP_ISA_ALIGN) &&
+	if (pci_has_flag(PCI_CAN_SKIP_ISA_ALIGN) &&
 	    !(dev->bus->bridge_ctl & PCI_BRIDGE_CTL_ISA))
 		return 1;
 	return 0;
@@ -1271,7 +1271,7 @@
 			 * and as such ensure proper re-allocation
 			 * later.
 			 */
-			if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)
+			if (pci_has_flag(PCI_REASSIGN_ALL_RSRC))
 				goto clear_resource;
 			pr = pci_find_parent_resource(bus->self, res);
 			if (pr == res) {
@@ -1456,7 +1456,7 @@
 	list_for_each_entry(b, &pci_root_buses, node)
 		pcibios_allocate_bus_resources(b);
 
-	if (!(ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)) {
+	if (!pci_has_flag(PCI_REASSIGN_ALL_RSRC)) {
 		pcibios_allocate_resources(0);
 		pcibios_allocate_resources(1);
 	}
@@ -1465,7 +1465,7 @@
 	 * the low IO area and the VGA memory area if they intersect the
 	 * bus available resources to avoid allocating things on top of them
 	 */
-	if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) {
+	if (!pci_has_flag(PCI_PROBE_ONLY)) {
 		list_for_each_entry(b, &pci_root_buses, node)
 			pcibios_reserve_legacy_regions(b);
 	}
@@ -1473,7 +1473,7 @@
 	/* Now, if the platform didn't decide to blindly trust the firmware,
 	 * we proceed to assigning things that were left unassigned
 	 */
-	if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) {
+	if (!pci_has_flag(PCI_PROBE_ONLY)) {
 		pr_debug("PCI: Assigning unassigned resources...\n");
 		pci_assign_unassigned_resources();
 	}
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index e2f24bad..bb15451 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -230,7 +230,7 @@
 
 	printk(KERN_INFO "PCI: Probing PCI hardware\n");
 
-	if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_BUS)
+	if (pci_has_flag(PCI_REASSIGN_ALL_BUS))
 		pci_assign_all_buses = 1;
 
 	/* Scan all of the recorded PCI controllers.  */
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index fc6452b..ab34046 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -55,12 +55,12 @@
 	ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot;
 
 	if (pci_probe_only)
-		ppc_pci_flags |= PPC_PCI_PROBE_ONLY;
+		pci_add_flags(PCI_PROBE_ONLY);
 
 	/* On ppc64, we always enable PCI domains and we keep domain 0
 	 * backward compatible in /proc for video cards
 	 */
-	ppc_pci_flags |= PPC_PCI_ENABLE_PROC_DOMAINS | PPC_PCI_COMPAT_DOMAIN_0;
+	pci_add_flags(PCI_ENABLE_PROC_DOMAINS | PCI_COMPAT_DOMAIN_0);
 
 	/* Scan all of the recorded PCI controllers.  */
 	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
index 7d28f54..f5ae872 100644
--- a/arch/powerpc/kernel/ppc_ksyms.c
+++ b/arch/powerpc/kernel/ppc_ksyms.c
@@ -18,7 +18,7 @@
 #include <asm/cacheflush.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/checksum.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 0e0ea94..d5ca823 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -39,7 +39,7 @@
 #include <asm/udbg.h>
 #include <asm/syscalls.h>
 #include <asm/smp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/mmu.h>
 #include <asm/topology.h>
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index 54e66da..6cd8f01 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -291,7 +291,7 @@
 		prop = of_get_property(of_chosen,
 				"linux,pci-assign-all-buses", NULL);
 		if (prop && *prop)
-			ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+			pci_add_flags(PCI_REASSIGN_ALL_BUS);
 #endif /* CONFIG_PPC32 */
 	}
 }
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index 67f6c3b..481ef06 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -27,7 +27,7 @@
 #include <asm/rtas.h>
 #include <asm/prom.h>
 #include <asm/nvram.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/machdep.h>
 
 
diff --git a/arch/powerpc/kernel/smp-tbsync.c b/arch/powerpc/kernel/smp-tbsync.c
index 03e45c4..640de83 100644
--- a/arch/powerpc/kernel/smp-tbsync.c
+++ b/arch/powerpc/kernel/smp-tbsync.c
@@ -11,7 +11,7 @@
 #include <linux/unistd.h>
 #include <linux/init.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 #include <asm/time.h>
 
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index f932f8a..7bf2187 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -33,7 +33,7 @@
 #include <linux/topology.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff --git a/arch/powerpc/platforms/40x/ep405.c b/arch/powerpc/platforms/40x/ep405.c
index 4058fd1..b0389bb 100644
--- a/arch/powerpc/platforms/40x/ep405.c
+++ b/arch/powerpc/platforms/40x/ep405.c
@@ -100,7 +100,7 @@
 	/* Find & init the BCSR CPLD */
 	ep405_init_bcsr();
 
-	ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+	pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 }
 
 static int __init ep405_probe(void)
diff --git a/arch/powerpc/platforms/40x/ppc40x_simple.c b/arch/powerpc/platforms/40x/ppc40x_simple.c
index 2521d93..e8dd5c5 100644
--- a/arch/powerpc/platforms/40x/ppc40x_simple.c
+++ b/arch/powerpc/platforms/40x/ppc40x_simple.c
@@ -61,7 +61,7 @@
 static int __init ppc40x_probe(void)
 {
 	if (of_flat_dt_match(of_get_flat_dt_root(), board)) {
-		ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+		pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 		return 1;
 	}
 
diff --git a/arch/powerpc/platforms/40x/walnut.c b/arch/powerpc/platforms/40x/walnut.c
index 335df91..8b691df 100644
--- a/arch/powerpc/platforms/40x/walnut.c
+++ b/arch/powerpc/platforms/40x/walnut.c
@@ -51,7 +51,7 @@
 	if (!of_flat_dt_is_compatible(root, "ibm,walnut"))
 		return 0;
 
-	ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+	pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/44x/canyonlands.c b/arch/powerpc/platforms/44x/canyonlands.c
index afc5e8e..e300dd4 100644
--- a/arch/powerpc/platforms/44x/canyonlands.c
+++ b/arch/powerpc/platforms/44x/canyonlands.c
@@ -55,7 +55,7 @@
 {
 	unsigned long root = of_get_flat_dt_root();
 	if (of_flat_dt_is_compatible(root, "amcc,canyonlands")) {
-		ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+		pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 		return 1;
 		}
 	return 0;
diff --git a/arch/powerpc/platforms/44x/ebony.c b/arch/powerpc/platforms/44x/ebony.c
index 88b9117..6a4232b 100644
--- a/arch/powerpc/platforms/44x/ebony.c
+++ b/arch/powerpc/platforms/44x/ebony.c
@@ -54,7 +54,7 @@
 	if (!of_flat_dt_is_compatible(root, "ibm,ebony"))
 		return 0;
 
-	ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+	pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c
index c81c19c..8d22027 100644
--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
@@ -72,7 +72,7 @@
 
 	for (i = 0; i < ARRAY_SIZE(board); i++) {
 		if (of_flat_dt_is_compatible(root, board[i])) {
-			ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+			pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 			return 1;
 		}
 	}
diff --git a/arch/powerpc/platforms/44x/sam440ep.c b/arch/powerpc/platforms/44x/sam440ep.c
index a78e8eb..9e09b83 100644
--- a/arch/powerpc/platforms/44x/sam440ep.c
+++ b/arch/powerpc/platforms/44x/sam440ep.c
@@ -51,7 +51,7 @@
 	if (!of_flat_dt_is_compatible(root, "acube,sam440ep"))
 		return 0;
 
-	ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
+	pci_set_flags(PCI_REASSIGN_ALL_RSRC);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
index 5f5e693..bfb11e0 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
@@ -371,7 +371,7 @@
 
 	pr_debug("Adding MPC52xx PCI host bridge %s\n", node->full_name);
 
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 
 	if (of_address_to_resource(node, 0, &rsrc) != 0) {
 		printk(KERN_ERR "Can't get %s resources\n", node->full_name);
diff --git a/arch/powerpc/platforms/82xx/pq2.c b/arch/powerpc/platforms/82xx/pq2.c
index 9761a59..d111b02 100644
--- a/arch/powerpc/platforms/82xx/pq2.c
+++ b/arch/powerpc/platforms/82xx/pq2.c
@@ -53,7 +53,7 @@
 	if (of_address_to_resource(np, 0, &r) || r.end - r.start < 0x10b)
 		goto err;
 
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 
 	hose = pcibios_alloc_controller(np);
 	if (!hose)
diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c
index f8fa2fc..c55129f 100644
--- a/arch/powerpc/platforms/83xx/km83xx.c
+++ b/arch/powerpc/platforms/83xx/km83xx.c
@@ -28,7 +28,7 @@
 #include <linux/of_device.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c
index 93e60f1..32a5289 100644
--- a/arch/powerpc/platforms/83xx/mpc832x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c
@@ -27,7 +27,7 @@
 #include <linux/of_device.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c
index 81e44fa..6b45969 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_itx.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c
@@ -26,7 +26,7 @@
 #include <linux/of_platform.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c
index c1b1dc5..041c517 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c
@@ -26,7 +26,7 @@
 #include <linux/of_platform.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c
index 81c052b..934cc8c 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c
@@ -34,7 +34,7 @@
 #include <linux/of_device.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/83xx/sbc834x.c b/arch/powerpc/platforms/83xx/sbc834x.c
index 49023db..af41d8c 100644
--- a/arch/powerpc/platforms/83xx/sbc834x.c
+++ b/arch/powerpc/platforms/83xx/sbc834x.c
@@ -28,7 +28,7 @@
 #include <linux/of_platform.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c
index 6299a2a..2bf9978 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c
@@ -31,7 +31,7 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
index 747d1ee..973b3f4 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
@@ -36,7 +36,7 @@
 #include <linux/memblock.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/85xx/sbc8548.c b/arch/powerpc/platforms/85xx/sbc8548.c
index ecdd8c0..d07dcb7 100644
--- a/arch/powerpc/platforms/85xx/sbc8548.c
+++ b/arch/powerpc/platforms/85xx/sbc8548.c
@@ -34,7 +34,7 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c
index d809836..7f92096 100644
--- a/arch/powerpc/platforms/cell/cpufreq_spudemand.c
+++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c
@@ -24,7 +24,7 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/machdep.h>
 #include <asm/spu.h>
 
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c
index dbb641e..f2e1dfe 100644
--- a/arch/powerpc/platforms/cell/smp.c
+++ b/arch/powerpc/platforms/cell/smp.c
@@ -28,7 +28,7 @@
 #include <linux/cpu.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 0c87bcd..bf4d41d 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -24,7 +24,7 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/spu.h>
 #include <asm/spu_csa.h>
 #include "spufs.h"
diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c
index 3f65443..83285c5 100644
--- a/arch/powerpc/platforms/chrp/pci.c
+++ b/arch/powerpc/platforms/chrp/pci.c
@@ -199,7 +199,7 @@
 		printk ("RTAS supporting Pegasos OF not found, please upgrade"
 			" your firmware\n");
 	}
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 	/* keep the reference to the root node */
 }
 
diff --git a/arch/powerpc/platforms/chrp/smp.c b/arch/powerpc/platforms/chrp/smp.c
index a800122..feab30b 100644
--- a/arch/powerpc/platforms/chrp/smp.c
+++ b/arch/powerpc/platforms/chrp/smp.c
@@ -18,7 +18,7 @@
 #include <linux/spinlock.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c
index 2df48c2..8bda9be 100644
--- a/arch/powerpc/platforms/iseries/smp.c
+++ b/arch/powerpc/platforms/iseries/smp.c
@@ -29,7 +29,7 @@
 #include <linux/cpu.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c
index d679964..c2f3e86 100644
--- a/arch/powerpc/platforms/powermac/backlight.c
+++ b/arch/powerpc/platforms/powermac/backlight.c
@@ -12,7 +12,7 @@
 #include <linux/backlight.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/prom.h>
 #include <asm/backlight.h>
 
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index 41a80a4..5cc8385 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -732,7 +732,7 @@
 static int __init setup_uninorth(struct pci_controller *hose,
 				 struct resource *addr)
 {
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 	has_uninorth = 1;
 	hose->ops = &macrisc_pci_ops;
 	hose->cfg_addr = ioremap(addr->start + 0x800000, 0x1000);
@@ -998,7 +998,7 @@
 	struct device_node *np, *root;
 	struct device_node *ht = NULL;
 
-	ppc_pci_set_flags(PPC_PCI_CAN_SKIP_ISA_ALIGN);
+	pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN);
 
 	root = of_find_node_by_path("/");
 	if (root == NULL) {
@@ -1057,7 +1057,7 @@
 	 * some offset between bus number and domains for now when we
 	 * assign all busses should help for now
 	 */
-	if (ppc_pci_has_flag(PPC_PCI_REASSIGN_ALL_BUS))
+	if (pci_has_flag(PCI_REASSIGN_ALL_BUS))
 		pcibios_assign_bus_offset = 0x10;
 #endif
 }
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index d15fca3..9a521dc 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -35,7 +35,7 @@
 #include <linux/compiler.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/code-patching.h>
 #include <asm/irq.h>
 #include <asm/page.h>
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 46b55cf..ada6e07 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -31,7 +31,7 @@
 #include <linux/spinlock.h>
 #include <linux/of.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/eeh.h>
 #include <asm/eeh_event.h>
 #include <asm/io.h>
diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
index 8ed0d2d..fc5ae76 100644
--- a/arch/powerpc/platforms/pseries/eeh_cache.c
+++ b/arch/powerpc/platforms/pseries/eeh_cache.c
@@ -25,7 +25,7 @@
 #include <linux/rbtree.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pci-bridge.h>
 #include <asm/ppc-pci.h>
 
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 1672db2..4e44c4d 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -27,7 +27,7 @@
 #include <linux/cpu.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 3bba8bd..4ce547e 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -351,7 +351,7 @@
 		printk(KERN_WARNING "Can't get bus-range for %s, assume"
 			" bus 0\n", dev->full_name);
 
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 	hose = pcibios_alloc_controller(dev);
 	if (!hose)
 		return -ENOMEM;
@@ -640,7 +640,7 @@
 		       " bus 0\n", dev->full_name);
 	}
 
-	ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+	pci_add_flags(PCI_REASSIGN_ALL_BUS);
 	hose = pcibios_alloc_controller(dev);
 	if (!hose)
 		return -ENOMEM;
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 265313e..2d66275 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -32,7 +32,7 @@
 #include <linux/fs_uart_pd.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/time.h>
diff --git a/arch/powerpc/sysdev/grackle.c b/arch/powerpc/sysdev/grackle.c
index cf27df6..08abe91 100644
--- a/arch/powerpc/sysdev/grackle.c
+++ b/arch/powerpc/sysdev/grackle.c
@@ -57,7 +57,7 @@
 {
 	setup_indirect_pci(hose, 0xfec00000, 0xfee00000, 0);
 	if (of_machine_is_compatible("PowerMac1,1"))
-		ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
+		pci_add_flags(PCI_REASSIGN_ALL_BUS);
 	if (of_machine_is_compatible("AAPL,PowerBook1998"))
 		grackle_set_loop_snoop(hose, 1);
 #if 0	/* Disabled for now, HW problems ??? */
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 2ec4f3b..a59ba96 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -1977,7 +1977,7 @@
 {
 	struct device_node *np;
 
-	ppc_pci_flags |= PPC_PCI_ENABLE_PROC_DOMAINS | PPC_PCI_COMPAT_DOMAIN_0;
+	pci_add_flags(PCI_ENABLE_PROC_DOMAINS | PCI_COMPAT_DOMAIN_0);
 
 #ifdef CONFIG_PPC4xx_PCI_EXPRESS
 	for_each_compatible_node(np, NULL, "ibm,plb-pciex")
diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c
index ee05680..9f51f97 100644
--- a/arch/powerpc/sysdev/tsi108_dev.c
+++ b/arch/powerpc/sysdev/tsi108_dev.c
@@ -23,7 +23,7 @@
 #include <asm/tsi108.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/prom.h>
diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h
index d9db138..8517d2a 100644
--- a/arch/s390/include/asm/atomic.h
+++ b/arch/s390/include/asm/atomic.h
@@ -93,7 +93,7 @@
 	return old;
 }
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -105,10 +105,9 @@
 			break;
 		c = old;
 	}
-	return c != u;
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #undef __CS_LOOP
 
@@ -332,6 +331,4 @@
 #define smp_mb__before_atomic_inc()	smp_mb()
 #define smp_mb__after_atomic_inc()	smp_mb()
 
-#include <asm-generic/atomic-long.h>
-
 #endif /* __ARCH_S390_ATOMIC__  */
diff --git a/arch/s390/include/asm/bitops.h b/arch/s390/include/asm/bitops.h
index 667c6e9..e5beb49 100644
--- a/arch/s390/include/asm/bitops.h
+++ b/arch/s390/include/asm/bitops.h
@@ -832,10 +832,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(lock, nr, addr)	\
-	test_and_set_bit_le(nr, addr)
-#define ext2_clear_bit_atomic(lock, nr, addr)	\
-	test_and_clear_bit_le(nr, addr)
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 
 #endif /* __KERNEL__ */
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index 9ad628a..62fd80c 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -539,7 +539,6 @@
  * These are defined as per linux/ptrace.h, which see.
  */
 #define arch_has_single_step()	(1)
-extern void show_regs(struct pt_regs * regs);
 
 #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
 #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN)
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index 1ca3d1d..45df6d4 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -27,7 +27,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mathemu.h>
 #include <asm/cpcmd.h>
 #include <asm/lowcore.h>
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index e9372c7..ffabcd9 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -36,7 +36,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mathemu.h>
 #include <asm/cpcmd.h>
 #include <asm/lowcore.h>
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index 1dc924b..d362657 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -332,8 +332,8 @@
 	return ret;
 }
 
-static int ap325rxa_camera_add(struct soc_camera_link *icl, struct device *dev);
-static void ap325rxa_camera_del(struct soc_camera_link *icl);
+static int ap325rxa_camera_add(struct soc_camera_device *icd);
+static void ap325rxa_camera_del(struct soc_camera_device *icd);
 
 static struct soc_camera_platform_info camera_info = {
 	.format_name = "UYVY",
@@ -366,24 +366,23 @@
 	soc_camera_platform_release(&camera_device);
 }
 
-static int ap325rxa_camera_add(struct soc_camera_link *icl,
-			       struct device *dev)
+static int ap325rxa_camera_add(struct soc_camera_device *icd)
 {
-	int ret = soc_camera_platform_add(icl, dev, &camera_device, &camera_link,
+	int ret = soc_camera_platform_add(icd, &camera_device, &camera_link,
 					  ap325rxa_camera_release, 0);
 	if (ret < 0)
 		return ret;
 
 	ret = camera_probe();
 	if (ret < 0)
-		soc_camera_platform_del(icl, camera_device, &camera_link);
+		soc_camera_platform_del(icd, camera_device, &camera_link);
 
 	return ret;
 }
 
-static void ap325rxa_camera_del(struct soc_camera_link *icl)
+static void ap325rxa_camera_del(struct soc_camera_device *icd)
 {
-	soc_camera_platform_del(icl, camera_device, &camera_link);
+	soc_camera_platform_del(icd, camera_device, &camera_link);
 }
 #endif /* CONFIG_I2C */
 
diff --git a/arch/sh/drivers/pci/fixups-cayman.c b/arch/sh/drivers/pci/fixups-cayman.c
index b68b61d..edc2fb7 100644
--- a/arch/sh/drivers/pci/fixups-cayman.c
+++ b/arch/sh/drivers/pci/fixups-cayman.c
@@ -5,7 +5,7 @@
 #include <cpu/irq.h>
 #include "pci-sh5.h"
 
-int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int result = -1;
 
diff --git a/arch/sh/drivers/pci/fixups-dreamcast.c b/arch/sh/drivers/pci/fixups-dreamcast.c
index 942ef4f..edeea89 100644
--- a/arch/sh/drivers/pci/fixups-dreamcast.c
+++ b/arch/sh/drivers/pci/fixups-dreamcast.c
@@ -64,7 +64,7 @@
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, gapspci_fixup_resources);
 
-int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	/*
 	 * The interrupt routing semantics here are quite trivial.
diff --git a/arch/sh/drivers/pci/fixups-landisk.c b/arch/sh/drivers/pci/fixups-landisk.c
index 95c6e2d..ecb1d10 100644
--- a/arch/sh/drivers/pci/fixups-landisk.c
+++ b/arch/sh/drivers/pci/fixups-landisk.c
@@ -19,7 +19,7 @@
 #define PCIMCR_MRSET_OFF	0xBFFFFFFF
 #define PCIMCR_RFSH_OFF		0xFFFFFFFB
 
-int pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
 	/*
 	 * slot0: pin1-4 = irq5,6,7,8
diff --git a/arch/sh/drivers/pci/fixups-r7780rp.c b/arch/sh/drivers/pci/fixups-r7780rp.c
index 08b2d86..f9370dc 100644
--- a/arch/sh/drivers/pci/fixups-r7780rp.c
+++ b/arch/sh/drivers/pci/fixups-r7780rp.c
@@ -18,7 +18,7 @@
 	65, 66, 67, 68,
 };
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
 	return irq_tab[slot];
 }
diff --git a/arch/sh/drivers/pci/fixups-rts7751r2d.c b/arch/sh/drivers/pci/fixups-rts7751r2d.c
index e248516..eaddb56 100644
--- a/arch/sh/drivers/pci/fixups-rts7751r2d.c
+++ b/arch/sh/drivers/pci/fixups-rts7751r2d.c
@@ -31,7 +31,7 @@
 	IRQ_ETH0, IRQ_ETH1, IRQ_INTA, IRQ_INTD,
 };
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
 	if (mach_is_lboxre2())
 		return lboxre2_irq_tab[slot];
diff --git a/arch/sh/drivers/pci/fixups-sdk7780.c b/arch/sh/drivers/pci/fixups-sdk7780.c
index 0930f98..0b84725 100644
--- a/arch/sh/drivers/pci/fixups-sdk7780.c
+++ b/arch/sh/drivers/pci/fixups-sdk7780.c
@@ -27,7 +27,7 @@
 	{ 68, 67, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
 };
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
        return sdk7780_irq_tab[pin-1][slot];
 }
diff --git a/arch/sh/drivers/pci/fixups-se7751.c b/arch/sh/drivers/pci/fixups-se7751.c
index fd3e6b0..2ec146c 100644
--- a/arch/sh/drivers/pci/fixups-se7751.c
+++ b/arch/sh/drivers/pci/fixups-se7751.c
@@ -6,7 +6,7 @@
 #include <linux/io.h>
 #include "pci-sh4.h"
 
-int __init pcibios_map_platform_irq(struct pci_dev *, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *, u8 slot, u8 pin)
 {
         switch (slot) {
         case 0: return 13;
diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c
index 2e8a18b..1615e59 100644
--- a/arch/sh/drivers/pci/fixups-sh03.c
+++ b/arch/sh/drivers/pci/fixups-sh03.c
@@ -3,7 +3,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 
-int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq;
 
diff --git a/arch/sh/drivers/pci/fixups-snapgear.c b/arch/sh/drivers/pci/fixups-snapgear.c
index 5a39ecc..4a093c6 100644
--- a/arch/sh/drivers/pci/fixups-snapgear.c
+++ b/arch/sh/drivers/pci/fixups-snapgear.c
@@ -18,7 +18,7 @@
 #include <linux/pci.h>
 #include "pci-sh4.h"
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
 	int irq = -1;
 
diff --git a/arch/sh/drivers/pci/fixups-titan.c b/arch/sh/drivers/pci/fixups-titan.c
index 3a79fa8..bd1addb 100644
--- a/arch/sh/drivers/pci/fixups-titan.c
+++ b/arch/sh/drivers/pci/fixups-titan.c
@@ -27,7 +27,7 @@
 	TITAN_IRQ_USB,
 };
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
 	int irq = titan_irq_tab[slot];
 
diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c
index 4418f90..4df27c4f 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.c
+++ b/arch/sh/drivers/pci/pcie-sh7786.c
@@ -466,7 +466,7 @@
 	return 0;
 }
 
-int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
+int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
         return 71;
 }
diff --git a/arch/sh/include/asm/atomic.h b/arch/sh/include/asm/atomic.h
index c798312..63a27db 100644
--- a/arch/sh/include/asm/atomic.h
+++ b/arch/sh/include/asm/atomic.h
@@ -30,7 +30,6 @@
 #define atomic_inc_and_test(v)		(atomic_inc_return(v) == 0)
 #define atomic_sub_and_test(i,v)	(atomic_sub_return((i), (v)) == 0)
 #define atomic_dec_and_test(v)		(atomic_sub_return(1, (v)) == 0)
-#define atomic_inc_not_zero(v)		atomic_add_unless((v), 1, 0)
 
 #define atomic_inc(v)			atomic_add(1, (v))
 #define atomic_dec(v)			atomic_sub(1, (v))
@@ -39,15 +38,15 @@
 #define atomic_cmpxchg(v, o, n)		(cmpxchg(&((v)->counter), (o), (n)))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -60,7 +59,7 @@
 		c = old;
 	}
 
-	return c != (u);
+	return c;
 }
 
 #define smp_mb__before_atomic_dec()	smp_mb()
@@ -68,7 +67,4 @@
 #define smp_mb__before_atomic_inc()	smp_mb()
 #define smp_mb__after_atomic_inc()	smp_mb()
 
-#include <asm-generic/atomic-long.h>
-#include <asm-generic/atomic64.h>
-
 #endif /* __ASM_SH_ATOMIC_H */
diff --git a/arch/sh/include/asm/hw_irq.h b/arch/sh/include/asm/hw_irq.h
index 603cdde..693d441 100644
--- a/arch/sh/include/asm/hw_irq.h
+++ b/arch/sh/include/asm/hw_irq.h
@@ -3,7 +3,7 @@
 
 #include <linux/init.h>
 #include <linux/sh_intc.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern atomic_t irq_err_count;
 
diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h
index f0efe97..cb21e23 100644
--- a/arch/sh/include/asm/pci.h
+++ b/arch/sh/include/asm/pci.h
@@ -112,7 +112,7 @@
 #endif
 
 /* Board-specific fixup routines. */
-int pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin);
+int pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin);
 
 extern void pcibios_resource_to_bus(struct pci_dev *dev,
 	struct pci_bus_region *region, struct resource *res);
diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h
index 88bd6be..b97baf8 100644
--- a/arch/sh/include/asm/ptrace.h
+++ b/arch/sh/include/asm/ptrace.h
@@ -45,8 +45,6 @@
 #define GET_FP(regs)	((regs)->regs[14])
 #define GET_USP(regs)	((regs)->regs[15])
 
-extern void show_regs(struct pt_regs *);
-
 #define arch_has_single_step()	(1)
 
 /*
diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h
index 9070d94..78b0d0f4 100644
--- a/arch/sh/include/asm/smp.h
+++ b/arch/sh/include/asm/smp.h
@@ -8,7 +8,7 @@
 #ifdef CONFIG_SMP
 
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/current.h>
 #include <asm/percpu.h>
 
diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c
index 425d604..84db0d6 100644
--- a/arch/sh/kernel/idle.c
+++ b/arch/sh/kernel/idle.c
@@ -18,7 +18,7 @@
 #include <linux/smp.h>
 #include <asm/pgalloc.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/smp.h>
 
 void (*pm_idle)(void) = NULL;
diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c
index 6207561..3147a9a 100644
--- a/arch/sh/kernel/smp.c
+++ b/arch/sh/kernel/smp.c
@@ -21,7 +21,7 @@
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/mmu_context.h>
diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c
index 67110be..cd3a404 100644
--- a/arch/sh/kernel/traps_64.c
+++ b/arch/sh/kernel/traps_64.c
@@ -28,7 +28,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/processor.h>
 #include <asm/pgtable.h>
 #include <asm/fpu.h>
diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c
index 468889d..521b543 100644
--- a/arch/sh/kernel/unwinder.c
+++ b/arch/sh/kernel/unwinder.c
@@ -13,7 +13,7 @@
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <asm/unwinder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * This is the most basic stack unwinder an architecture can
diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h
index 7ae128b..5c3c8b6 100644
--- a/arch/sparc/include/asm/atomic_32.h
+++ b/arch/sparc/include/asm/atomic_32.h
@@ -22,7 +22,7 @@
 extern int __atomic_add_return(int, atomic_t *);
 extern int atomic_cmpxchg(atomic_t *, int, int);
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
-extern int atomic_add_unless(atomic_t *, int, int);
+extern int __atomic_add_unless(atomic_t *, int, int);
 extern void atomic_set(atomic_t *, int);
 
 #define atomic_read(v)          (*(volatile int *)&(v)->counter)
@@ -52,7 +52,6 @@
 #define atomic_dec_and_test(v) (atomic_dec_return(v) == 0)
 #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /* This is the old 24-bit implementation.  It's still used internally
  * by some sparc-specific code, notably the semaphore implementation.
@@ -161,5 +160,4 @@
 
 #endif /* !(__KERNEL__) */
 
-#include <asm-generic/atomic-long.h>
 #endif /* !(__ARCH_SPARC_ATOMIC__) */
diff --git a/arch/sparc/include/asm/atomic_64.h b/arch/sparc/include/asm/atomic_64.h
index bdb2ff8..9f421df 100644
--- a/arch/sparc/include/asm/atomic_64.h
+++ b/arch/sparc/include/asm/atomic_64.h
@@ -70,7 +70,7 @@
 #define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n)))
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -82,10 +82,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 #define atomic64_cmpxchg(v, o, n) \
 	((__typeof__((v)->counter))cmpxchg(&((v)->counter), (o), (n)))
@@ -114,5 +113,4 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif /* !(__ARCH_SPARC64_ATOMIC__) */
diff --git a/arch/sparc/include/asm/bitops_64.h b/arch/sparc/include/asm/bitops_64.h
index 38e9aa1..325e295 100644
--- a/arch/sparc/include/asm/bitops_64.h
+++ b/arch/sparc/include/asm/bitops_64.h
@@ -91,10 +91,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(lock,nr,addr) \
-	test_and_set_bit((nr) ^ 0x38,(unsigned long *)(addr))
-#define ext2_clear_bit_atomic(lock,nr,addr) \
-	test_and_clear_bit((nr) ^ 0x38,(unsigned long *)(addr))
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* __KERNEL__ */
 
diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h
index cfa9cd2..64f7a00 100644
--- a/arch/sparc/include/asm/elf_64.h
+++ b/arch/sparc/include/asm/elf_64.h
@@ -177,9 +177,11 @@
 		cap |= HWCAP_SPARC_ULTRA3;
 	else if (tlb_type == hypervisor) {
 		if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 ||
-		    sun4v_chip_type == SUN4V_CHIP_NIAGARA2)
+		    sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+		    sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
 			cap |= HWCAP_SPARC_BLKINIT;
-		if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2)
+		if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+		    sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
 			cap |= HWCAP_SPARC_N2;
 	}
 
diff --git a/arch/sparc/include/asm/hypervisor.h b/arch/sparc/include/asm/hypervisor.h
index 7568640..7a5f80d 100644
--- a/arch/sparc/include/asm/hypervisor.h
+++ b/arch/sparc/include/asm/hypervisor.h
@@ -2950,6 +2950,7 @@
 #define HV_GRP_N2_CPU			0x0202
 #define HV_GRP_NIU			0x0204
 #define HV_GRP_VF_CPU			0x0205
+#define HV_GRP_KT_CPU			0x0209
 #define HV_GRP_DIAG			0x0300
 
 #ifndef __ASSEMBLY__
diff --git a/arch/sparc/include/asm/leon_pci.h b/arch/sparc/include/asm/leon_pci.h
index 42b4b31..f48527e 100644
--- a/arch/sparc/include/asm/leon_pci.h
+++ b/arch/sparc/include/asm/leon_pci.h
@@ -12,7 +12,7 @@
 	struct pci_ops *ops;
 	struct resource	io_space;
 	struct resource	mem_space;
-	int (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin);
+	int (*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
 };
 
 extern void leon_pci_init(struct platform_device *ofdev,
diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h
index 56bbaad..edd3d3cde 100644
--- a/arch/sparc/include/asm/prom.h
+++ b/arch/sparc/include/asm/prom.h
@@ -21,7 +21,7 @@
 #include <linux/of_pdt.h>
 #include <linux/proc_fs.h>
 #include <linux/mutex.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define OF_ROOT_NODE_ADDR_CELLS_DEFAULT	2
 #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT	1
diff --git a/arch/sparc/include/asm/ptrace.h b/arch/sparc/include/asm/ptrace.h
index b928b31..a0e1bcf 100644
--- a/arch/sparc/include/asm/ptrace.h
+++ b/arch/sparc/include/asm/ptrace.h
@@ -213,7 +213,6 @@
 #else
 #define profile_pc(regs) instruction_pointer(regs)
 #endif
-extern void show_regs(struct pt_regs *);
 #endif /* (__KERNEL__) */
 
 #else /* __ASSEMBLY__ */
@@ -257,7 +256,6 @@
 #define instruction_pointer(regs) ((regs)->pc)
 #define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP])
 unsigned long profile_pc(struct pt_regs *);
-extern void show_regs(struct pt_regs *);
 #endif /* (__KERNEL__) */
 
 #else /* (!__ASSEMBLY__) */
diff --git a/arch/sparc/include/asm/smp_32.h b/arch/sparc/include/asm/smp_32.h
index 093f108..01c51c7 100644
--- a/arch/sparc/include/asm/smp_32.h
+++ b/arch/sparc/include/asm/smp_32.h
@@ -22,7 +22,7 @@
 
 #include <asm/ptrace.h>
 #include <asm/asi.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  *	Private routines/data
diff --git a/arch/sparc/include/asm/smp_64.h b/arch/sparc/include/asm/smp_64.h
index 20bca89..29862a9 100644
--- a/arch/sparc/include/asm/smp_64.h
+++ b/arch/sparc/include/asm/smp_64.h
@@ -27,7 +27,7 @@
  */
 
 #include <linux/bitops.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/percpu.h>
 
 DECLARE_PER_CPU(cpumask_t, cpu_sibling_map);
diff --git a/arch/sparc/include/asm/spitfire.h b/arch/sparc/include/asm/spitfire.h
index f0d0c40..55a17c6 100644
--- a/arch/sparc/include/asm/spitfire.h
+++ b/arch/sparc/include/asm/spitfire.h
@@ -42,6 +42,7 @@
 #define SUN4V_CHIP_INVALID	0x00
 #define SUN4V_CHIP_NIAGARA1	0x01
 #define SUN4V_CHIP_NIAGARA2	0x02
+#define SUN4V_CHIP_NIAGARA3	0x03
 #define SUN4V_CHIP_UNKNOWN	0xff
 
 #ifndef __ASSEMBLY__
diff --git a/arch/sparc/include/asm/xor_64.h b/arch/sparc/include/asm/xor_64.h
index bee4bf4..9ed6ff6 100644
--- a/arch/sparc/include/asm/xor_64.h
+++ b/arch/sparc/include/asm/xor_64.h
@@ -65,6 +65,7 @@
 #define XOR_SELECT_TEMPLATE(FASTEST) \
 	((tlb_type == hypervisor && \
 	  (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || \
-	   sun4v_chip_type == SUN4V_CHIP_NIAGARA2)) ? \
+	   sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || \
+	   sun4v_chip_type == SUN4V_CHIP_NIAGARA3)) ? \
 	 &xor_block_niagara : \
 	 &xor_block_VIS)
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index 138dbbc..17cf290 100644
--- a/arch/sparc/kernel/cpu.c
+++ b/arch/sparc/kernel/cpu.c
@@ -474,11 +474,18 @@
 		sparc_pmu_type = "niagara2";
 		break;
 
+	case SUN4V_CHIP_NIAGARA3:
+		sparc_cpu_type = "UltraSparc T3 (Niagara3)";
+		sparc_fpu_type = "UltraSparc T3 integrated FPU";
+		sparc_pmu_type = "niagara3";
+		break;
+
 	default:
 		printk(KERN_WARNING "CPU: Unknown sun4v cpu type [%s]\n",
 		       prom_cpu_compatible);
 		sparc_cpu_type = "Unknown SUN4V CPU";
 		sparc_fpu_type = "Unknown SUN4V FPU";
+		sparc_pmu_type = "Unknown SUN4V PMU";
 		break;
 	}
 }
diff --git a/arch/sparc/kernel/cpumap.c b/arch/sparc/kernel/cpumap.c
index d91fd78..4197e8d 100644
--- a/arch/sparc/kernel/cpumap.c
+++ b/arch/sparc/kernel/cpumap.c
@@ -324,6 +324,7 @@
 	switch (sun4v_chip_type) {
 	case SUN4V_CHIP_NIAGARA1:
 	case SUN4V_CHIP_NIAGARA2:
+	case SUN4V_CHIP_NIAGARA3:
 		rover_inc_table = niagara_iterate_method;
 		break;
 	default:
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index aa594c7..c752603 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -132,6 +132,8 @@
 	.asciz	"sun4v"
 prom_niagara_prefix:
 	.asciz	"SUNW,UltraSPARC-T"
+prom_sparc_prefix:
+	.asciz	"SPARC-T"
 	.align	4
 prom_root_compatible:
 	.skip	64
@@ -382,6 +384,22 @@
 90:	ldub	[%g7], %g2
 	ldub	[%g1], %g4
 	cmp	%g2, %g4
+	bne,pn	%icc, 89f
+	 add	%g7, 1, %g7
+	subcc	%g3, 1, %g3
+	bne,pt	%xcc, 90b
+	 add	%g1, 1, %g1
+	ba,pt	%xcc, 91f
+	 nop
+
+89:	sethi	%hi(prom_cpu_compatible), %g1
+	or	%g1, %lo(prom_cpu_compatible), %g1
+	sethi	%hi(prom_sparc_prefix), %g7
+	or	%g7, %lo(prom_sparc_prefix), %g7
+	mov	7, %g3
+90:	ldub	[%g7], %g2
+	ldub	[%g1], %g4
+	cmp	%g2, %g4
 	bne,pn	%icc, 4f
 	 add	%g7, 1, %g7
 	subcc	%g3, 1, %g3
@@ -390,6 +408,15 @@
 
 	sethi	%hi(prom_cpu_compatible), %g1
 	or	%g1, %lo(prom_cpu_compatible), %g1
+	ldub	[%g1 + 7], %g2
+	cmp	%g2, '3'
+	be,pt	%xcc, 5f
+	 mov	SUN4V_CHIP_NIAGARA3, %g4
+	ba,pt	%xcc, 4f
+	 nop
+
+91:	sethi	%hi(prom_cpu_compatible), %g1
+	or	%g1, %lo(prom_cpu_compatible), %g1
 	ldub	[%g1 + 17], %g2
 	cmp	%g2, '1'
 	be,pt	%xcc, 5f
@@ -397,6 +424,7 @@
 	cmp	%g2, '2'
 	be,pt	%xcc, 5f
 	 mov	SUN4V_CHIP_NIAGARA2, %g4
+	
 4:
 	mov	SUN4V_CHIP_UNKNOWN, %g4
 5:	sethi	%hi(sun4v_chip_type), %g2
@@ -514,6 +542,9 @@
 	 cmp	%g1, SUN4V_CHIP_NIAGARA2
 	be,pt	%xcc, niagara2_patch
 	 nop
+	cmp	%g1, SUN4V_CHIP_NIAGARA3
+	be,pt	%xcc, niagara2_patch
+	 nop
 
 	call	generic_patch_copyops
 	 nop
diff --git a/arch/sparc/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c
index 7c60afb..d306e64 100644
--- a/arch/sparc/kernel/hvapi.c
+++ b/arch/sparc/kernel/hvapi.c
@@ -38,6 +38,7 @@
 	{ .group = HV_GRP_N2_CPU,				},
 	{ .group = HV_GRP_NIU,					},
 	{ .group = HV_GRP_VF_CPU,				},
+	{ .group = HV_GRP_KT_CPU,				},
 	{ .group = HV_GRP_DIAG,		.flags = FLAG_PRE_API	},
 };
 
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index 4e78862..0dd8422 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -26,7 +26,7 @@
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/io.h>
diff --git a/arch/sparc/kernel/leon_pci_grpci2.c b/arch/sparc/kernel/leon_pci_grpci2.c
index 44dc093..fad1bd0 100644
--- a/arch/sparc/kernel/leon_pci_grpci2.c
+++ b/arch/sparc/kernel/leon_pci_grpci2.c
@@ -215,7 +215,7 @@
 DEFINE_SPINLOCK(grpci2_dev_lock);
 struct grpci2_priv *grpci2priv;
 
-int grpci2_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+int grpci2_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct grpci2_priv *priv = dev->bus->sysdata;
 	int irq_group;
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c
index fe8fb44..1210fde 100644
--- a/arch/sparc/kernel/leon_smp.c
+++ b/arch/sparc/kernel/leon_smp.c
@@ -28,7 +28,7 @@
 #include <asm/tlbflush.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq_regs.h>
 #include <asm/traps.h>
 
diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c
index 8ac23e6..343b0f9 100644
--- a/arch/sparc/kernel/pcr.c
+++ b/arch/sparc/kernel/pcr.c
@@ -80,8 +80,11 @@
 {
 	unsigned long ret;
 
-	ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
-	if (ret != HV_EOK)
+	if (val & PCR_N2_HTRACE) {
+		ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
+		if (ret != HV_EOK)
+			write_pcr(val);
+	} else
 		write_pcr(val);
 }
 
@@ -106,6 +109,10 @@
 			perf_hsvc_group = HV_GRP_N2_CPU;
 			break;
 
+		case SUN4V_CHIP_NIAGARA3:
+			perf_hsvc_group = HV_GRP_KT_CPU;
+			break;
+
 		default:
 			return -ENODEV;
 		}
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c
index 62a0343..614da62 100644
--- a/arch/sparc/kernel/perf_event.c
+++ b/arch/sparc/kernel/perf_event.c
@@ -22,7 +22,7 @@
 #include <asm/stacktrace.h>
 #include <asm/cpudata.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/nmi.h>
 #include <asm/pcr.h>
 
@@ -1343,7 +1343,8 @@
 		sparc_pmu = &niagara1_pmu;
 		return true;
 	}
-	if (!strcmp(sparc_pmu_type, "niagara2")) {
+	if (!strcmp(sparc_pmu_type, "niagara2") ||
+	    !strcmp(sparc_pmu_type, "niagara3")) {
 		sparc_pmu = &niagara2_pmu;
 		return true;
 	}
diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c
index 21b1253..f671e7f 100644
--- a/arch/sparc/kernel/smp_32.c
+++ b/arch/sparc/kernel/smp_32.c
@@ -22,7 +22,7 @@
 #include <linux/delay.h>
 
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <asm/irq.h>
 #include <asm/page.h>
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 99cb172..4a442c3 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -28,7 +28,7 @@
 
 #include <asm/head.h>
 #include <asm/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/tlbflush.h>
 #include <asm/mmu_context.h>
 #include <asm/cpudata.h>
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
index d3c7a12..8600eb2 100644
--- a/arch/sparc/lib/atomic32.c
+++ b/arch/sparc/lib/atomic32.c
@@ -7,7 +7,7 @@
  * Based on asm-parisc/atomic.h Copyright (C) 2000 Philipp Rumpf
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/spinlock.h>
 #include <linux/module.h>
 
@@ -55,7 +55,7 @@
 }
 EXPORT_SYMBOL(atomic_cmpxchg);
 
-int atomic_add_unless(atomic_t *v, int a, int u)
+int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int ret;
 	unsigned long flags;
@@ -67,7 +67,7 @@
 	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
 	return ret != u;
 }
-EXPORT_SYMBOL(atomic_add_unless);
+EXPORT_SYMBOL(__atomic_add_unless);
 
 /* Atomic operations are already serializing */
 void atomic_set(atomic_t *v, int i)
diff --git a/arch/tile/include/asm/atomic.h b/arch/tile/include/asm/atomic.h
index 739cfe0..921dbeb 100644
--- a/arch/tile/include/asm/atomic.h
+++ b/arch/tile/include/asm/atomic.h
@@ -121,15 +121,6 @@
  */
 #define atomic_add_negative(i, v)	(atomic_add_return((i), (v)) < 0)
 
-/**
- * atomic_inc_not_zero - increment unless the number is zero
- * @v: pointer of type atomic_t
- *
- * Atomically increments @v by 1, so long as @v is non-zero.
- * Returns non-zero if @v was non-zero, and zero otherwise.
- */
-#define atomic_inc_not_zero(v)		atomic_add_unless((v), 1, 0)
-
 /* Nonexistent functions intended to cause link errors. */
 extern unsigned long __xchg_called_with_bad_pointer(void);
 extern unsigned long __cmpxchg_called_with_bad_pointer(void);
@@ -186,9 +177,4 @@
 #include <asm/atomic_64.h>
 #endif
 
-/* Provide the appropriate atomic_long_t definitions. */
-#ifndef __ASSEMBLY__
-#include <asm-generic/atomic-long.h>
-#endif
-
 #endif /* _ASM_TILE_ATOMIC_H */
diff --git a/arch/tile/include/asm/atomic_32.h b/arch/tile/include/asm/atomic_32.h
index 92a8bee..c03349e 100644
--- a/arch/tile/include/asm/atomic_32.h
+++ b/arch/tile/include/asm/atomic_32.h
@@ -11,7 +11,7 @@
  *   NON INFRINGEMENT.  See the GNU General Public License for
  *   more details.
  *
- * Do not include directly; use <asm/atomic.h>.
+ * Do not include directly; use <linux/atomic.h>.
  */
 
 #ifndef _ASM_TILE_ATOMIC_32_H
@@ -21,7 +21,7 @@
 
 #ifndef __ASSEMBLY__
 
-/* Tile-specific routines to support <asm/atomic.h>. */
+/* Tile-specific routines to support <linux/atomic.h>. */
 int _atomic_xchg(atomic_t *v, int n);
 int _atomic_xchg_add(atomic_t *v, int i);
 int _atomic_xchg_add_unless(atomic_t *v, int a, int u);
@@ -81,18 +81,18 @@
 }
 
 /**
- * atomic_add_unless - add unless the number is already a given value
+ * __atomic_add_unless - add unless the number is already a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as @v was not already @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	smp_mb();  /* barrier for proper semantics */
-	return _atomic_xchg_add_unless(v, a, u) != u;
+	return _atomic_xchg_add_unless(v, a, u);
 }
 
 /**
@@ -199,7 +199,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as @v was not already @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static inline u64 atomic64_add_unless(atomic64_t *v, u64 a, u64 u)
 {
diff --git a/arch/tile/include/asm/atomic_64.h b/arch/tile/include/asm/atomic_64.h
index 1c1e60d..27fe667 100644
--- a/arch/tile/include/asm/atomic_64.h
+++ b/arch/tile/include/asm/atomic_64.h
@@ -11,7 +11,7 @@
  *   NON INFRINGEMENT.  See the GNU General Public License for
  *   more details.
  *
- * Do not include directly; use <asm/atomic.h>.
+ * Do not include directly; use <linux/atomic.h>.
  */
 
 #ifndef _ASM_TILE_ATOMIC_64_H
@@ -64,7 +64,7 @@
 	return val;
 }
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int guess, oldval = v->counter;
 	do {
@@ -73,7 +73,7 @@
 		guess = oldval;
 		oldval = atomic_cmpxchg(v, guess, guess + a);
 	} while (guess != oldval);
-	return oldval != u;
+	return oldval;
 }
 
 /* Now the true 64-bit operations. */
diff --git a/arch/tile/include/asm/bitops_32.h b/arch/tile/include/asm/bitops_32.h
index d31ab90..571b118 100644
--- a/arch/tile/include/asm/bitops_32.h
+++ b/arch/tile/include/asm/bitops_32.h
@@ -16,7 +16,7 @@
 #define _ASM_TILE_BITOPS_32_H
 
 #include <linux/compiler.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 /* Tile-specific routines to support <asm/bitops.h>. */
diff --git a/arch/tile/include/asm/bitops_64.h b/arch/tile/include/asm/bitops_64.h
index 99615e8..e9c8e38 100644
--- a/arch/tile/include/asm/bitops_64.h
+++ b/arch/tile/include/asm/bitops_64.h
@@ -16,7 +16,7 @@
 #define _ASM_TILE_BITOPS_64_H
 
 #include <linux/compiler.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 /* See <asm/bitops.h> for API comments. */
@@ -97,9 +97,6 @@
 	return (oldval & mask) != 0;
 }
 
-#define ext2_set_bit_atomic(lock, nr, addr)			\
-	test_and_set_bit((nr), (unsigned long *)(addr))
-#define ext2_clear_bit_atomic(lock, nr, addr)			\
-	test_and_clear_bit((nr), (unsigned long *)(addr))
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* _ASM_TILE_BITOPS_64_H */
diff --git a/arch/tile/include/asm/ptrace.h b/arch/tile/include/asm/ptrace.h
index 6be2246..c6cddd7 100644
--- a/arch/tile/include/asm/ptrace.h
+++ b/arch/tile/include/asm/ptrace.h
@@ -112,8 +112,6 @@
 /* Trace the current syscall. */
 extern void do_syscall_trace(void);
 
-extern void show_regs(struct pt_regs *);
-
 #define arch_has_single_step()	(1)
 
 /*
diff --git a/arch/tile/include/asm/spinlock_32.h b/arch/tile/include/asm/spinlock_32.h
index a8f2c6e..a5e4208 100644
--- a/arch/tile/include/asm/spinlock_32.h
+++ b/arch/tile/include/asm/spinlock_32.h
@@ -17,7 +17,7 @@
 #ifndef _ASM_TILE_SPINLOCK_32_H
 #define _ASM_TILE_SPINLOCK_32_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 #include <asm/system.h>
 #include <linux/compiler.h>
diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S
index 72ade79..fc94607 100644
--- a/arch/tile/kernel/intvec_32.S
+++ b/arch/tile/kernel/intvec_32.S
@@ -21,7 +21,7 @@
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
 #include <asm/irqflags.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/asm-offsets.h>
 #include <hv/hypervisor.h>
 #include <arch/abi.h>
diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c
index 6d4cb5d..2a8014c 100644
--- a/arch/tile/kernel/pci.c
+++ b/arch/tile/kernel/pci.c
@@ -228,7 +228,7 @@
  * (pin - 1) converts from the PCI standard's [1:4] convention to
  * a normal [0:3] range.
  */
-static int tile_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int tile_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	struct pci_controller *controller =
 		(struct pci_controller *)dev->sysdata;
diff --git a/arch/tile/lib/atomic_32.c b/arch/tile/lib/atomic_32.c
index 4657021..771b251 100644
--- a/arch/tile/lib/atomic_32.c
+++ b/arch/tile/lib/atomic_32.c
@@ -17,7 +17,7 @@
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/futex.h>
 #include <arch/chip.h>
 
diff --git a/arch/tile/lib/atomic_asm_32.S b/arch/tile/lib/atomic_asm_32.S
index 2444873..1f75a2a 100644
--- a/arch/tile/lib/atomic_asm_32.S
+++ b/arch/tile/lib/atomic_asm_32.S
@@ -70,7 +70,7 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 #include <asm/processor.h>
 
diff --git a/arch/um/include/asm/ptrace-generic.h b/arch/um/include/asm/ptrace-generic.h
index b7c5bab..ae084ad 100644
--- a/arch/um/include/asm/ptrace-generic.h
+++ b/arch/um/include/asm/ptrace-generic.h
@@ -47,8 +47,6 @@
 extern int set_fpregs(struct user_i387_struct __user *buf,
 		      struct task_struct *child);
 
-extern void show_regs(struct pt_regs *regs);
-
 extern int arch_copy_tls(struct task_struct *new);
 extern void clear_flushed_tls(struct task_struct *task);
 
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
index 100eab8..4892fbb 100644
--- a/arch/unicore32/kernel/pci.c
+++ b/arch/unicore32/kernel/pci.c
@@ -102,7 +102,7 @@
 	writel(readl(PCIBRI_CMD) | PCIBRI_CMD_IO | PCIBRI_CMD_MEM, PCIBRI_CMD);
 }
 
-static int __init pci_puv3_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init pci_puv3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	if (dev->bus->number == 0) {
 #ifdef CONFIG_ARCH_FPGA /* 4 pci slots */
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 78fe57d..7cf916f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1905,7 +1905,7 @@
 # x86-64 doesn't support PCI BIOS access from long mode so always go direct.
 config PCI_DIRECT
 	def_bool y
-	depends on PCI && (X86_64 || (PCI_GODIRECT || PCI_GOANY || PCI_GOOLPC))
+	depends on PCI && (X86_64 || (PCI_GODIRECT || PCI_GOANY || PCI_GOOLPC || PCI_GOMMCONFIG))
 
 config PCI_MMCONFIG
 	def_bool y
@@ -2024,11 +2024,44 @@
 	  Add support for detecting the unique features of the OLPC
 	  XO hardware.
 
-config OLPC_XO1
-	tristate "OLPC XO-1 support"
-	depends on OLPC && MFD_CS5535
+config OLPC_XO1_PM
+	bool "OLPC XO-1 Power Management"
+	depends on OLPC && MFD_CS5535 && PM_SLEEP
+	select MFD_CORE
 	---help---
-	  Add support for non-essential features of the OLPC XO-1 laptop.
+	  Add support for poweroff and suspend of the OLPC XO-1 laptop.
+
+config OLPC_XO1_RTC
+	bool "OLPC XO-1 Real Time Clock"
+	depends on OLPC_XO1_PM && RTC_DRV_CMOS
+	---help---
+	  Add support for the XO-1 real time clock, which can be used as a
+	  programmable wakeup source.
+
+config OLPC_XO1_SCI
+	bool "OLPC XO-1 SCI extras"
+	depends on OLPC && OLPC_XO1_PM
+	select POWER_SUPPLY
+	select GPIO_CS5535
+	select MFD_CORE
+	---help---
+	  Add support for SCI-based features of the OLPC XO-1 laptop:
+	   - EC-driven system wakeups
+	   - Power button
+	   - Ebook switch
+	   - Lid switch
+	   - AC adapter status updates
+	   - Battery status updates
+
+config OLPC_XO15_SCI
+	bool "OLPC XO-1.5 SCI extras"
+	depends on OLPC && ACPI
+	select POWER_SUPPLY
+	---help---
+	  Add support for SCI-based features of the OLPC XO-1.5 laptop:
+	   - EC-driven system wakeups
+	   - AC adapter status updates
+	   - Battery status updates
 
 endif # X86_32
 
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index 5852519..f6f5c53 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -43,7 +43,7 @@
 #include <asm/mman.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/vgtod.h>
 #include <asm/sys_ia32.h>
 
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 4a0b7c7..7b3ca83 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -8,7 +8,7 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/apicdef.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/fixmap.h>
 #include <asm/mpspec.h>
 #include <asm/system.h>
diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
index 952a826..10572e3 100644
--- a/arch/x86/include/asm/atomic.h
+++ b/arch/x86/include/asm/atomic.h
@@ -221,15 +221,15 @@
 }
 
 /**
- * atomic_add_unless - add unless the number is already a given value
+ * __atomic_add_unless - add unless the number is already a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as @v was not already @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -241,10 +241,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 /*
  * atomic_dec_if_positive - decrement by 1 if old value positive
@@ -319,5 +318,4 @@
 # include "atomic64_64.h"
 #endif
 
-#include <asm-generic/atomic-long.h>
 #endif /* _ASM_X86_ATOMIC_H */
diff --git a/arch/x86/include/asm/atomic64_32.h b/arch/x86/include/asm/atomic64_32.h
index 2a934aa..24098aa 100644
--- a/arch/x86/include/asm/atomic64_32.h
+++ b/arch/x86/include/asm/atomic64_32.h
@@ -263,7 +263,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
 {
diff --git a/arch/x86/include/asm/atomic64_64.h b/arch/x86/include/asm/atomic64_64.h
index 49fd1ea..017594d 100644
--- a/arch/x86/include/asm/atomic64_64.h
+++ b/arch/x86/include/asm/atomic64_64.h
@@ -202,7 +202,7 @@
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
 static inline int atomic64_add_unless(atomic64_t *v, long a, long u)
 {
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
index 69d5813..1775d6e 100644
--- a/arch/x86/include/asm/bitops.h
+++ b/arch/x86/include/asm/bitops.h
@@ -458,10 +458,7 @@
 
 #include <asm-generic/bitops/le.h>
 
-#define ext2_set_bit_atomic(lock, nr, addr)			\
-	test_and_set_bit((nr), (unsigned long *)(addr))
-#define ext2_clear_bit_atomic(lock, nr, addr)			\
-	test_and_clear_bit((nr), (unsigned long *)(addr))
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_X86_BITOPS_H */
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h
index 13f5504..0919905 100644
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -21,7 +21,7 @@
 #include <linux/profile.h>
 #include <linux/smp.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/irq.h>
 #include <asm/sections.h>
 
diff --git a/arch/x86/include/asm/kdebug.h b/arch/x86/include/asm/kdebug.h
index fe2cc6e..d73f157 100644
--- a/arch/x86/include/asm/kdebug.h
+++ b/arch/x86/include/asm/kdebug.h
@@ -28,7 +28,6 @@
 extern void show_trace(struct task_struct *t, struct pt_regs *regs,
 		       unsigned long *sp, unsigned long bp);
 extern void __show_regs(struct pt_regs *regs, int all);
-extern void show_regs(struct pt_regs *regs);
 extern unsigned long oops_begin(void);
 extern void oops_end(unsigned long, struct pt_regs *, int signr);
 #ifdef CONFIG_KEXEC
diff --git a/arch/x86/include/asm/local.h b/arch/x86/include/asm/local.h
index 2e99724..9cdae5d 100644
--- a/arch/x86/include/asm/local.h
+++ b/arch/x86/include/asm/local.h
@@ -4,7 +4,7 @@
 #include <linux/percpu.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/asm.h>
 
 typedef struct {
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 716b48a..c9321f3 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -124,7 +124,7 @@
 
 #include <linux/percpu.h>
 #include <linux/init.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern int mce_disabled;
 extern int mce_p5_enabled;
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index 8b5393e..6902152 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -2,7 +2,7 @@
 #define _ASM_X86_MMU_CONTEXT_H
 
 #include <asm/desc.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
 #include <asm/paravirt.h>
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 5ca6801..87bdbca 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -13,6 +13,7 @@
 
 #define OLPC_F_PRESENT		0x01
 #define OLPC_F_DCON		0x02
+#define OLPC_F_EC_WIDE_SCI	0x04
 
 #ifdef CONFIG_OLPC
 
@@ -62,6 +63,13 @@
 	return olpc_platform_info.boardrev >= rev;
 }
 
+extern void olpc_ec_wakeup_set(u16 value);
+extern void olpc_ec_wakeup_clear(u16 value);
+extern bool olpc_ec_wakeup_available(void);
+
+extern int olpc_ec_mask_write(u16 bits);
+extern int olpc_ec_sci_query(u16 *sci_value);
+
 #else
 
 static inline int machine_is_olpc(void)
@@ -74,6 +82,20 @@
 	return 0;
 }
 
+static inline void olpc_ec_wakeup_set(u16 value) { }
+static inline void olpc_ec_wakeup_clear(u16 value) { }
+
+static inline bool olpc_ec_wakeup_available(void)
+{
+	return false;
+}
+
+#endif
+
+#ifdef CONFIG_OLPC_XO1_PM
+extern void do_olpc_suspend_lowlevel(void);
+extern void olpc_xo1_pm_wakeup_set(u16 value);
+extern void olpc_xo1_pm_wakeup_clear(u16 value);
 #endif
 
 extern int pci_olpc_init(void);
@@ -83,14 +105,19 @@
 extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
 		unsigned char *outbuf, size_t outlen);
 
-extern int olpc_ec_mask_set(uint8_t bits);
-extern int olpc_ec_mask_unset(uint8_t bits);
-
 /* EC commands */
 
-#define EC_FIRMWARE_REV		0x08
-#define EC_WLAN_ENTER_RESET	0x35
-#define EC_WLAN_LEAVE_RESET	0x25
+#define EC_FIRMWARE_REV			0x08
+#define EC_WRITE_SCI_MASK		0x1b
+#define EC_WAKE_UP_WLAN			0x24
+#define EC_WLAN_LEAVE_RESET		0x25
+#define EC_READ_EB_MODE			0x2a
+#define EC_SET_SCI_INHIBIT		0x32
+#define EC_SET_SCI_INHIBIT_RELEASE	0x34
+#define EC_WLAN_ENTER_RESET		0x35
+#define EC_WRITE_EXT_SCI_MASK		0x38
+#define EC_SCI_QUERY			0x84
+#define EC_EXT_SCI_QUERY		0x85
 
 /* SCI source values */
 
@@ -99,10 +126,12 @@
 #define EC_SCI_SRC_BATTERY	0x02
 #define EC_SCI_SRC_BATSOC	0x04
 #define EC_SCI_SRC_BATERR	0x08
-#define EC_SCI_SRC_EBOOK	0x10
-#define EC_SCI_SRC_WLAN		0x20
+#define EC_SCI_SRC_EBOOK	0x10	/* XO-1 only */
+#define EC_SCI_SRC_WLAN		0x20	/* XO-1 only */
 #define EC_SCI_SRC_ACPWR	0x40
-#define EC_SCI_SRC_ALL		0x7F
+#define EC_SCI_SRC_BATCRIT	0x80
+#define EC_SCI_SRC_GPWAKE	0x100	/* XO-1.5 only */
+#define EC_SCI_SRC_ALL		0x1FF
 
 /* GPIO assignments */
 
@@ -116,7 +145,7 @@
 #define OLPC_GPIO_SMB_CLK      14
 #define OLPC_GPIO_SMB_DATA     15
 #define OLPC_GPIO_WORKAUX	geode_gpio(24)
-#define OLPC_GPIO_LID		geode_gpio(26)
-#define OLPC_GPIO_ECSCI		geode_gpio(27)
+#define OLPC_GPIO_LID		26
+#define OLPC_GPIO_ECSCI		27
 
 #endif /* _ASM_X86_OLPC_H */
diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
index df12870..644dd885 100644
--- a/arch/x86/include/asm/prom.h
+++ b/arch/x86/include/asm/prom.h
@@ -19,7 +19,7 @@
 #include <linux/pci.h>
 
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/setup.h>
 #include <asm/irq_controller.h>
 
diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h
index e9e51f7..ee67edf 100644
--- a/arch/x86/include/asm/spinlock.h
+++ b/arch/x86/include/asm/spinlock.h
@@ -1,7 +1,7 @@
 #ifndef _ASM_X86_SPINLOCK_H
 #define _ASM_X86_SPINLOCK_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 #include <asm/processor.h>
 #include <linux/compiler.h>
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index 1f2e61e..a1fe5c1 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -21,7 +21,7 @@
 struct exec_domain;
 #include <asm/processor.h>
 #include <asm/ftrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct thread_info {
 	struct task_struct	*task;		/* main task structure */
diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c
index b117efd..8a439d3 100644
--- a/arch/x86/kernel/amd_gart_64.c
+++ b/arch/x86/kernel/amd_gart_64.c
@@ -30,7 +30,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/io.h>
 #include <linux/gfp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mtrr.h>
 #include <asm/pgtable.h>
 #include <asm/proto.h>
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index b24be38..52fa563 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -38,7 +38,7 @@
 #include <asm/perf_event.h>
 #include <asm/x86_init.h>
 #include <asm/pgalloc.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/mpspec.h>
 #include <asm/i8259.h>
 #include <asm/proto.h>
diff --git a/arch/x86/kernel/apic/es7000_32.c b/arch/x86/kernel/apic/es7000_32.c
index 9536b3f..5d513bc 100644
--- a/arch/x86/kernel/apic/es7000_32.c
+++ b/arch/x86/kernel/apic/es7000_32.c
@@ -48,7 +48,7 @@
 #include <linux/io.h>
 
 #include <asm/apicdef.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/fixmap.h>
 #include <asm/mpspec.h>
 #include <asm/setup.h>
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 22a073d..6218439 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -21,7 +21,7 @@
 #include <linux/topology.h>
 #include <linux/cpumask.h>
 #include <asm/pgtable.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/proto.h>
 #include <asm/setup.h>
 #include <asm/apic.h>
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index 65b8f5c..6104852 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -14,7 +14,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/timer.h>
 #include <asm/hw_irq.h>
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index f09d4bb..b3300e6 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -15,7 +15,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/timer.h>
 #include <asm/hw_irq.h>
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index fbc097a..9682ec5 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -49,7 +49,7 @@
 #include <asm/stacktrace.h>
 #include <asm/processor.h>
 #include <asm/debugreg.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/traps.h>
 #include <asm/desc.h>
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 2b2255b..57dcbd4 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -33,7 +33,7 @@
 #include <asm/page.h>
 #include <asm/current.h>
 #include <asm/apicdef.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "kvm_cache_regs.h"
 #include "irq.h"
 #include "trace.h"
diff --git a/arch/x86/kvm/timer.c b/arch/x86/kvm/timer.c
index abd86e86..ae432ea 100644
--- a/arch/x86/kvm/timer.c
+++ b/arch/x86/kvm/timer.c
@@ -15,7 +15,7 @@
 #include <linux/kvm_host.h>
 #include <linux/kvm.h>
 #include <linux/hrtimer.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "kvm_timer.h"
 
 static int __kvm_timer_fn(struct kvm_vcpu *vcpu, struct kvm_timer *ktimer)
diff --git a/arch/x86/lib/atomic64_32.c b/arch/x86/lib/atomic64_32.c
index 540179e..042f682 100644
--- a/arch/x86/lib/atomic64_32.c
+++ b/arch/x86/lib/atomic64_32.c
@@ -4,7 +4,7 @@
 
 #include <asm/processor.h>
 #include <asm/cmpxchg.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 long long atomic64_read_cx8(long long, const atomic64_t *v);
 EXPORT_SYMBOL(atomic64_read_cx8);
diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c
index 3adff7d..67421f3 100644
--- a/arch/x86/mm/mmio-mod.c
+++ b/arch/x86/mm/mmio-mod.c
@@ -34,7 +34,7 @@
 #include <asm/pgtable.h>
 #include <linux/mmiotrace.h>
 #include <asm/e820.h> /* for ISA_START_ADDRESS */
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/percpu.h>
 #include <linux/cpu.h>
 
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 68c3c13..ae3cb23 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -246,10 +246,9 @@
 
 		conflict = insert_resource_conflict(root, res);
 		if (conflict)
-			dev_err(&info->bridge->dev,
-				"address space collision: host bridge window %pR "
-				"conflicts with %s %pR\n",
-				res, conflict->name, conflict);
+			dev_info(&info->bridge->dev,
+				 "ignoring host bridge window %pR (conflicts with %s %pR)\n",
+				 res, conflict->name, conflict);
 		else
 			pci_bus_add_resource(info->bus, res, 0);
 	}
diff --git a/arch/x86/pci/ce4100.c b/arch/x86/pci/ce4100.c
index 67858be..9917609 100644
--- a/arch/x86/pci/ce4100.c
+++ b/arch/x86/pci/ce4100.c
@@ -257,6 +257,7 @@
 {
 	int i;
 
+	WARN_ON(seg);
 	if (bus == 1) {
 		for (i = 0; i < ARRAY_SIZE(bus1_fixups); i++) {
 			if (bus1_fixups[i].dev_func == devfn &&
@@ -282,6 +283,7 @@
 {
 	int i;
 
+	WARN_ON(seg);
 	if (bus == 1) {
 		for (i = 0; i < ARRAY_SIZE(bus1_fixups); i++) {
 			if (bus1_fixups[i].dev_func == devfn &&
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index 5fe7502..92df322 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -247,13 +247,6 @@
 	},
 #endif		/* __i386__ */
 	{
-		.callback = find_sort_method,
-		.ident = "Dell System",
-		.matches = {
-			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
-		},
-	},
-	{
 		.callback = set_bf_sort,
 		.ident = "Dell PowerEdge 1950",
 		.matches = {
@@ -294,6 +287,13 @@
 		},
 	},
 	{
+		.callback = find_sort_method,
+		.ident = "Dell System",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+		},
+	},
+	{
 		.callback = set_bf_sort,
 		.ident = "HP ProLiant BL20p G3",
 		.matches = {
diff --git a/arch/x86/pci/direct.c b/arch/x86/pci/direct.c
index e6fd847..4f2c704 100644
--- a/arch/x86/pci/direct.c
+++ b/arch/x86/pci/direct.c
@@ -22,7 +22,7 @@
 {
 	unsigned long flags;
 
-	if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
+	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095)) {
 		*value = -1;
 		return -EINVAL;
 	}
@@ -53,7 +53,7 @@
 {
 	unsigned long flags;
 
-	if ((bus > 255) || (devfn > 255) || (reg > 4095))
+	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095))
 		return -EINVAL;
 
 	raw_spin_lock_irqsave(&pci_config_lock, flags);
@@ -97,6 +97,7 @@
 	unsigned long flags;
 	int dev, fn;
 
+	WARN_ON(seg);
 	if ((bus > 255) || (devfn > 255) || (reg > 255)) {
 		*value = -1;
 		return -EINVAL;
@@ -138,6 +139,7 @@
 	unsigned long flags;
 	int dev, fn;
 
+	WARN_ON(seg);
 	if ((bus > 255) || (devfn > 255) || (reg > 255)) 
 		return -EINVAL;
 
diff --git a/arch/x86/pci/numaq_32.c b/arch/x86/pci/numaq_32.c
index 5c9e245..512a88c 100644
--- a/arch/x86/pci/numaq_32.c
+++ b/arch/x86/pci/numaq_32.c
@@ -34,6 +34,7 @@
 	unsigned long flags;
 	void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus));
 
+	WARN_ON(seg);
 	if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
 		return -EINVAL;
 
@@ -73,6 +74,7 @@
 	unsigned long flags;
 	void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus));
 
+	WARN_ON(seg);
 	if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) 
 		return -EINVAL;
 
diff --git a/arch/x86/pci/olpc.c b/arch/x86/pci/olpc.c
index 13700ec..5262603 100644
--- a/arch/x86/pci/olpc.c
+++ b/arch/x86/pci/olpc.c
@@ -206,6 +206,8 @@
 {
 	uint32_t *addr;
 
+	WARN_ON(seg);
+
 	/* Use the hardware mechanism for non-simulated devices */
 	if (!is_simulated(bus, devfn))
 		return pci_direct_conf1.read(seg, bus, devfn, reg, len, value);
@@ -264,6 +266,8 @@
 static int pci_olpc_write(unsigned int seg, unsigned int bus,
 		unsigned int devfn, int reg, int len, uint32_t value)
 {
+	WARN_ON(seg);
+
 	/* Use the hardware mechanism for non-simulated devices */
 	if (!is_simulated(bus, devfn))
 		return pci_direct_conf1.write(seg, bus, devfn, reg, len, value);
diff --git a/arch/x86/pci/pcbios.c b/arch/x86/pci/pcbios.c
index a5f7d0d..f685535 100644
--- a/arch/x86/pci/pcbios.c
+++ b/arch/x86/pci/pcbios.c
@@ -181,6 +181,7 @@
 	unsigned long flags;
 	unsigned long bx = (bus << 8) | devfn;
 
+	WARN_ON(seg);
 	if (!value || (bus > 255) || (devfn > 255) || (reg > 255))
 		return -EINVAL;
 
@@ -247,6 +248,7 @@
 	unsigned long flags;
 	unsigned long bx = (bus << 8) | devfn;
 
+	WARN_ON(seg);
 	if ((bus > 255) || (devfn > 255) || (reg > 255)) 
 		return -EINVAL;
 
diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c
index 03008f7..6f2f8ee 100644
--- a/arch/x86/pci/visws.c
+++ b/arch/x86/pci/visws.c
@@ -24,7 +24,7 @@
 
 unsigned int pci_bus0, pci_bus1;
 
-static int __init visws_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+static int __init visws_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
 	int irq, bus = dev->bus->number;
 
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index 81c5e21..fd332c5 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,2 +1,5 @@
 obj-$(CONFIG_OLPC)		+= olpc.o olpc_ofw.o olpc_dt.o
-obj-$(CONFIG_OLPC_XO1)		+= olpc-xo1.o
+obj-$(CONFIG_OLPC_XO1_PM)		+= olpc-xo1-pm.o xo1-wakeup.o
+obj-$(CONFIG_OLPC_XO1_RTC)	+= olpc-xo1-rtc.o
+obj-$(CONFIG_OLPC_XO1_SCI)	+= olpc-xo1-sci.o
+obj-$(CONFIG_OLPC_XO15_SCI)	+= olpc-xo15-sci.o
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c
new file mode 100644
index 0000000..6f3855a
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -0,0 +1,215 @@
+/*
+ * Support for power management features of the OLPC XO-1 laptop
+ *
+ * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
+ * Copyright (C) 2010 One Laptop per Child
+ * Copyright (C) 2006 Red Hat, Inc.
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * 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/cs5535.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/mfd/core.h>
+#include <linux/suspend.h>
+
+#include <asm/io.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "olpc-xo1-pm"
+
+static unsigned long acpi_base;
+static unsigned long pms_base;
+
+static u16 wakeup_mask = CS5536_PM_PWRBTN;
+
+static struct {
+	unsigned long address;
+	unsigned short segment;
+} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
+
+/* Set bits in the wakeup mask */
+void olpc_xo1_pm_wakeup_set(u16 value)
+{
+	wakeup_mask |= value;
+}
+EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
+
+/* Clear bits in the wakeup mask */
+void olpc_xo1_pm_wakeup_clear(u16 value)
+{
+	wakeup_mask &= ~value;
+}
+EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
+
+static int xo1_power_state_enter(suspend_state_t pm_state)
+{
+	unsigned long saved_sci_mask;
+	int r;
+
+	/* Only STR is supported */
+	if (pm_state != PM_SUSPEND_MEM)
+		return -EINVAL;
+
+	r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
+	if (r)
+		return r;
+
+	/*
+	 * Save SCI mask (this gets lost since PM1_EN is used as a mask for
+	 * wakeup events, which is not necessarily the same event set)
+	 */
+	saved_sci_mask = inl(acpi_base + CS5536_PM1_STS);
+	saved_sci_mask &= 0xffff0000;
+
+	/* Save CPU state */
+	do_olpc_suspend_lowlevel();
+
+	/* Resume path starts here */
+
+	/* Restore SCI mask (using dword access to CS5536_PM1_EN) */
+	outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
+
+	/* Tell the EC to stop inhibiting SCIs */
+	olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
+
+	/*
+	 * Tell the wireless module to restart USB communication.
+	 * Must be done twice.
+	 */
+	olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+	olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+
+	return 0;
+}
+
+asmlinkage int xo1_do_sleep(u8 sleep_state)
+{
+	void *pgd_addr = __va(read_cr3());
+
+	/* Program wakeup mask (using dword access to CS5536_PM1_EN) */
+	outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS);
+
+	__asm__("movl %0,%%eax" : : "r" (pgd_addr));
+	__asm__("call *(%%edi); cld"
+		: : "D" (&ofw_bios_entry));
+	__asm__("movb $0x34, %al\n\t"
+		"outb %al, $0x70\n\t"
+		"movb $0x30, %al\n\t"
+		"outb %al, $0x71\n\t");
+	return 0;
+}
+
+static void xo1_power_off(void)
+{
+	printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
+
+	/* Enable all of these controls with 0 delay */
+	outl(0x40000000, pms_base + CS5536_PM_SCLK);
+	outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
+	outl(0x40000000, pms_base + CS5536_PM_WKXD);
+	outl(0x40000000, pms_base + CS5536_PM_WKD);
+
+	/* Clear status bits (possibly unnecessary) */
+	outl(0x0002ffff, pms_base  + CS5536_PM_SSC);
+	outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+
+	/* Write SLP_EN bit to start the machinery */
+	outl(0x00002000, acpi_base + CS5536_PM1_CNT);
+}
+
+static int xo1_power_state_valid(suspend_state_t pm_state)
+{
+	/* suspend-to-RAM only */
+	return pm_state == PM_SUSPEND_MEM;
+}
+
+static const struct platform_suspend_ops xo1_suspend_ops = {
+	.valid = xo1_power_state_valid,
+	.enter = xo1_power_state_enter,
+};
+
+static int __devinit xo1_pm_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int err;
+
+	/* don't run on non-XOs */
+	if (!machine_is_olpc())
+		return -ENODEV;
+
+	err = mfd_cell_enable(pdev);
+	if (err)
+		return err;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't fetch device resource info\n");
+		return -EIO;
+	}
+	if (strcmp(pdev->name, "cs5535-pms") == 0)
+		pms_base = res->start;
+	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
+		acpi_base = res->start;
+
+	/* If we have both addresses, we can override the poweroff hook */
+	if (pms_base && acpi_base) {
+		suspend_set_ops(&xo1_suspend_ops);
+		pm_power_off = xo1_power_off;
+		printk(KERN_INFO "OLPC XO-1 support registered\n");
+	}
+
+	return 0;
+}
+
+static int __devexit xo1_pm_remove(struct platform_device *pdev)
+{
+	mfd_cell_disable(pdev);
+
+	if (strcmp(pdev->name, "cs5535-pms") == 0)
+		pms_base = 0;
+	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
+		acpi_base = 0;
+
+	pm_power_off = NULL;
+	return 0;
+}
+
+static struct platform_driver cs5535_pms_driver = {
+	.driver = {
+		.name = "cs5535-pms",
+		.owner = THIS_MODULE,
+	},
+	.probe = xo1_pm_probe,
+	.remove = __devexit_p(xo1_pm_remove),
+};
+
+static struct platform_driver cs5535_acpi_driver = {
+	.driver = {
+		.name = "olpc-xo1-pm-acpi",
+		.owner = THIS_MODULE,
+	},
+	.probe = xo1_pm_probe,
+	.remove = __devexit_p(xo1_pm_remove),
+};
+
+static int __init xo1_pm_init(void)
+{
+	int r;
+
+	r = platform_driver_register(&cs5535_pms_driver);
+	if (r)
+		return r;
+
+	r = platform_driver_register(&cs5535_acpi_driver);
+	if (r)
+		platform_driver_unregister(&cs5535_pms_driver);
+
+	return r;
+}
+arch_initcall(xo1_pm_init);
diff --git a/arch/x86/platform/olpc/olpc-xo1-rtc.c b/arch/x86/platform/olpc/olpc-xo1-rtc.c
new file mode 100644
index 0000000..a2b4efd
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-rtc.c
@@ -0,0 +1,81 @@
+/*
+ * Support for OLPC XO-1 Real Time Clock (RTC)
+ *
+ * Copyright (C) 2011 One Laptop per Child
+ *
+ * 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/mc146818rtc.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+
+#include <asm/msr.h>
+#include <asm/olpc.h>
+
+static void rtc_wake_on(struct device *dev)
+{
+	olpc_xo1_pm_wakeup_set(CS5536_PM_RTC);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+	olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC);
+}
+
+static struct resource rtc_platform_resource[] = {
+	[0] = {
+		.start	= RTC_PORT(0),
+		.end	= RTC_PORT(1),
+		.flags	= IORESOURCE_IO,
+	},
+	[1] = {
+		.start	= RTC_IRQ,
+		.end	= RTC_IRQ,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct cmos_rtc_board_info rtc_info = {
+	.rtc_day_alarm = 0,
+	.rtc_mon_alarm = 0,
+	.rtc_century = 0,
+	.wake_on = rtc_wake_on,
+	.wake_off = rtc_wake_off,
+};
+
+static struct platform_device xo1_rtc_device = {
+	.name = "rtc_cmos",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(rtc_platform_resource),
+	.dev.platform_data = &rtc_info,
+	.resource = rtc_platform_resource,
+};
+
+static int __init xo1_rtc_init(void)
+{
+	int r;
+	struct device_node *node;
+
+	node = of_find_compatible_node(NULL, NULL, "olpc,xo1-rtc");
+	if (!node)
+		return 0;
+	of_node_put(node);
+
+	pr_info("olpc-xo1-rtc: Initializing OLPC XO-1 RTC\n");
+	rdmsrl(MSR_RTC_DOMA_OFFSET, rtc_info.rtc_day_alarm);
+	rdmsrl(MSR_RTC_MONA_OFFSET, rtc_info.rtc_mon_alarm);
+	rdmsrl(MSR_RTC_CEN_OFFSET, rtc_info.rtc_century);
+
+	r = platform_device_register(&xo1_rtc_device);
+	if (r)
+		return r;
+
+	device_init_wakeup(&xo1_rtc_device.dev, 1);
+	return 0;
+}
+arch_initcall(xo1_rtc_init);
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
new file mode 100644
index 0000000..1d4c783
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -0,0 +1,614 @@
+/*
+ * Support for OLPC XO-1 System Control Interrupts (SCI)
+ *
+ * Copyright (C) 2010 One Laptop per Child
+ * Copyright (C) 2006 Red Hat, Inc.
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * 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/cs5535.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/mfd/core.h>
+#include <linux/power_supply.h>
+#include <linux/suspend.h>
+#include <linux/workqueue.h>
+
+#include <asm/io.h>
+#include <asm/msr.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME	"olpc-xo1-sci"
+#define PFX		DRV_NAME ": "
+
+static unsigned long acpi_base;
+static struct input_dev *power_button_idev;
+static struct input_dev *ebook_switch_idev;
+static struct input_dev *lid_switch_idev;
+
+static int sci_irq;
+
+static bool lid_open;
+static bool lid_inverted;
+static int lid_wake_mode;
+
+enum lid_wake_modes {
+	LID_WAKE_ALWAYS,
+	LID_WAKE_OPEN,
+	LID_WAKE_CLOSE,
+};
+
+static const char * const lid_wake_mode_names[] = {
+	[LID_WAKE_ALWAYS] = "always",
+	[LID_WAKE_OPEN] = "open",
+	[LID_WAKE_CLOSE] = "close",
+};
+
+static void battery_status_changed(void)
+{
+	struct power_supply *psy = power_supply_get_by_name("olpc-battery");
+
+	if (psy) {
+		power_supply_changed(psy);
+		put_device(psy->dev);
+	}
+}
+
+static void ac_status_changed(void)
+{
+	struct power_supply *psy = power_supply_get_by_name("olpc-ac");
+
+	if (psy) {
+		power_supply_changed(psy);
+		put_device(psy->dev);
+	}
+}
+
+/* Report current ebook switch state through input layer */
+static void send_ebook_state(void)
+{
+	unsigned char state;
+
+	if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) {
+		pr_err(PFX "failed to get ebook state\n");
+		return;
+	}
+
+	input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state);
+	input_sync(ebook_switch_idev);
+}
+
+static void flip_lid_inverter(void)
+{
+	/* gpio is high; invert so we'll get l->h event interrupt */
+	if (lid_inverted)
+		cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+	else
+		cs5535_gpio_set(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+	lid_inverted = !lid_inverted;
+}
+
+static void detect_lid_state(void)
+{
+	/*
+	 * the edge detector hookup on the gpio inputs on the geode is
+	 * odd, to say the least.  See http://dev.laptop.org/ticket/5703
+	 * for details, but in a nutshell:  we don't use the edge
+	 * detectors.  instead, we make use of an anomoly:  with the both
+	 * edge detectors turned off, we still get an edge event on a
+	 * positive edge transition.  to take advantage of this, we use the
+	 * front-end inverter to ensure that that's the edge we're always
+	 * going to see next.
+	 */
+
+	int state;
+
+	state = cs5535_gpio_isset(OLPC_GPIO_LID, GPIO_READ_BACK);
+	lid_open = !state ^ !lid_inverted; /* x ^^ y */
+	if (!state)
+		return;
+
+	flip_lid_inverter();
+}
+
+/* Report current lid switch state through input layer */
+static void send_lid_state(void)
+{
+	input_report_switch(lid_switch_idev, SW_LID, !lid_open);
+	input_sync(lid_switch_idev);
+}
+
+static ssize_t lid_wake_mode_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	const char *mode = lid_wake_mode_names[lid_wake_mode];
+	return sprintf(buf, "%s\n", mode);
+}
+static ssize_t lid_wake_mode_set(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(lid_wake_mode_names); i++) {
+		const char *mode = lid_wake_mode_names[i];
+		if (strlen(mode) != count || strncasecmp(mode, buf, count))
+			continue;
+
+		lid_wake_mode = i;
+		return count;
+	}
+	return -EINVAL;
+}
+static DEVICE_ATTR(lid_wake_mode, S_IWUSR | S_IRUGO, lid_wake_mode_show,
+		   lid_wake_mode_set);
+
+/*
+ * Process all items in the EC's SCI queue.
+ *
+ * This is handled in a workqueue because olpc_ec_cmd can be slow (and
+ * can even timeout).
+ *
+ * If propagate_events is false, the queue is drained without events being
+ * generated for the interrupts.
+ */
+static void process_sci_queue(bool propagate_events)
+{
+	int r;
+	u16 data;
+
+	do {
+		r = olpc_ec_sci_query(&data);
+		if (r || !data)
+			break;
+
+		pr_debug(PFX "SCI 0x%x received\n", data);
+
+		switch (data) {
+		case EC_SCI_SRC_BATERR:
+		case EC_SCI_SRC_BATSOC:
+		case EC_SCI_SRC_BATTERY:
+		case EC_SCI_SRC_BATCRIT:
+			battery_status_changed();
+			break;
+		case EC_SCI_SRC_ACPWR:
+			ac_status_changed();
+			break;
+		}
+
+		if (data == EC_SCI_SRC_EBOOK && propagate_events)
+			send_ebook_state();
+	} while (data);
+
+	if (r)
+		pr_err(PFX "Failed to clear SCI queue");
+}
+
+static void process_sci_queue_work(struct work_struct *work)
+{
+	process_sci_queue(true);
+}
+
+static DECLARE_WORK(sci_work, process_sci_queue_work);
+
+static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	u32 sts;
+	u32 gpe;
+
+	sts = inl(acpi_base + CS5536_PM1_STS);
+	outl(sts | 0xffff, acpi_base + CS5536_PM1_STS);
+
+	gpe = inl(acpi_base + CS5536_PM_GPE0_STS);
+	outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+
+	dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe);
+
+	if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) {
+		input_report_key(power_button_idev, KEY_POWER, 1);
+		input_sync(power_button_idev);
+		input_report_key(power_button_idev, KEY_POWER, 0);
+		input_sync(power_button_idev);
+	}
+
+	if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */
+		cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
+		schedule_work(&sci_work);
+	}
+
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+	detect_lid_state();
+	send_lid_state();
+
+	return IRQ_HANDLED;
+}
+
+static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	if (device_may_wakeup(&power_button_idev->dev))
+		olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN);
+	else
+		olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN);
+
+	if (device_may_wakeup(&ebook_switch_idev->dev))
+		olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK);
+	else
+		olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);
+
+	if (!device_may_wakeup(&lid_switch_idev->dev)) {
+		cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+	} else if ((lid_open && lid_wake_mode == LID_WAKE_OPEN) ||
+		   (!lid_open && lid_wake_mode == LID_WAKE_CLOSE)) {
+		flip_lid_inverter();
+
+		/* we may have just caused an event */
+		cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+		cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+
+		cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+	}
+
+	return 0;
+}
+
+static int xo1_sci_resume(struct platform_device *pdev)
+{
+	/*
+	 * We don't know what may have happened while we were asleep.
+	 * Reestablish our lid setup so we're sure to catch all transitions.
+	 */
+	detect_lid_state();
+	send_lid_state();
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+
+	/* Enable all EC events */
+	olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+	/* Power/battery status might have changed too */
+	battery_status_changed();
+	ac_status_changed();
+	return 0;
+}
+
+static int __devinit setup_sci_interrupt(struct platform_device *pdev)
+{
+	u32 lo, hi;
+	u32 sts;
+	int r;
+
+	rdmsr(0x51400020, lo, hi);
+	sci_irq = (lo >> 20) & 15;
+
+	if (sci_irq) {
+		dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq);
+	} else {
+		/* Zero means masked */
+		dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n");
+		sci_irq = 3;
+		lo |= 0x00300000;
+		wrmsrl(0x51400020, lo);
+	}
+
+	/* Select level triggered in PIC */
+	if (sci_irq < 8) {
+		lo = inb(CS5536_PIC_INT_SEL1);
+		lo |= 1 << sci_irq;
+		outb(lo, CS5536_PIC_INT_SEL1);
+	} else {
+		lo = inb(CS5536_PIC_INT_SEL2);
+		lo |= 1 << (sci_irq - 8);
+		outb(lo, CS5536_PIC_INT_SEL2);
+	}
+
+	/* Enable SCI from power button, and clear pending interrupts */
+	sts = inl(acpi_base + CS5536_PM1_STS);
+	outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS);
+
+	r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev);
+	if (r)
+		dev_err(&pdev->dev, "can't request interrupt\n");
+
+	return r;
+}
+
+static int __devinit setup_ec_sci(void)
+{
+	int r;
+
+	r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI");
+	if (r)
+		return r;
+
+	gpio_direction_input(OLPC_GPIO_ECSCI);
+
+	/* Clear pending EC SCI events */
+	cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS);
+	cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS);
+
+	/*
+	 * Enable EC SCI events, and map them to both a PME and the SCI
+	 * interrupt.
+	 *
+	 * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can
+	 * be mapped to regular interrupts *or* Geode-specific Power
+	 * Management Events (PMEs) - events that bring the system out of
+	 * suspend. In this case, we want both of those things - the system
+	 * wakeup, *and* the ability to get an interrupt when an event occurs.
+	 *
+	 * To achieve this, we map the GPIO to a PME, and then we use one
+	 * of the many generic knobs on the CS5535 PIC to additionally map the
+	 * PME to the regular SCI interrupt line.
+	 */
+	cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE);
+
+	/* Set the SCI to cause a PME event on group 7 */
+	cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1);
+
+	/* And have group 7 also fire the SCI interrupt */
+	cs5535_pic_unreqz_select_high(7, sci_irq);
+
+	return 0;
+}
+
+static void free_ec_sci(void)
+{
+	gpio_free(OLPC_GPIO_ECSCI);
+}
+
+static int __devinit setup_lid_events(void)
+{
+	int r;
+
+	r = gpio_request(OLPC_GPIO_LID, "OLPC-LID");
+	if (r)
+		return r;
+
+	gpio_direction_input(OLPC_GPIO_LID);
+
+	cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
+	lid_inverted = 0;
+
+	/* Clear edge detection and event enable for now */
+	cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+	cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_EN);
+	cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_EN);
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
+
+	/* Set the LID to cause an PME event on group 6 */
+	cs5535_gpio_setup_event(OLPC_GPIO_LID, 6, 1);
+
+	/* Set PME group 6 to fire the SCI interrupt */
+	cs5535_gpio_set_irq(6, sci_irq);
+
+	/* Enable the event */
+	cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
+
+	return 0;
+}
+
+static void free_lid_events(void)
+{
+	gpio_free(OLPC_GPIO_LID);
+}
+
+static int __devinit setup_power_button(struct platform_device *pdev)
+{
+	int r;
+
+	power_button_idev = input_allocate_device();
+	if (!power_button_idev)
+		return -ENOMEM;
+
+	power_button_idev->name = "Power Button";
+	power_button_idev->phys = DRV_NAME "/input0";
+	set_bit(EV_KEY, power_button_idev->evbit);
+	set_bit(KEY_POWER, power_button_idev->keybit);
+
+	power_button_idev->dev.parent = &pdev->dev;
+	device_init_wakeup(&power_button_idev->dev, 1);
+
+	r = input_register_device(power_button_idev);
+	if (r) {
+		dev_err(&pdev->dev, "failed to register power button: %d\n", r);
+		input_free_device(power_button_idev);
+	}
+
+	return r;
+}
+
+static void free_power_button(void)
+{
+	input_unregister_device(power_button_idev);
+	input_free_device(power_button_idev);
+}
+
+static int __devinit setup_ebook_switch(struct platform_device *pdev)
+{
+	int r;
+
+	ebook_switch_idev = input_allocate_device();
+	if (!ebook_switch_idev)
+		return -ENOMEM;
+
+	ebook_switch_idev->name = "EBook Switch";
+	ebook_switch_idev->phys = DRV_NAME "/input1";
+	set_bit(EV_SW, ebook_switch_idev->evbit);
+	set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit);
+
+	ebook_switch_idev->dev.parent = &pdev->dev;
+	device_set_wakeup_capable(&ebook_switch_idev->dev, true);
+
+	r = input_register_device(ebook_switch_idev);
+	if (r) {
+		dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r);
+		input_free_device(ebook_switch_idev);
+	}
+
+	return r;
+}
+
+static void free_ebook_switch(void)
+{
+	input_unregister_device(ebook_switch_idev);
+	input_free_device(ebook_switch_idev);
+}
+
+static int __devinit setup_lid_switch(struct platform_device *pdev)
+{
+	int r;
+
+	lid_switch_idev = input_allocate_device();
+	if (!lid_switch_idev)
+		return -ENOMEM;
+
+	lid_switch_idev->name = "Lid Switch";
+	lid_switch_idev->phys = DRV_NAME "/input2";
+	set_bit(EV_SW, lid_switch_idev->evbit);
+	set_bit(SW_LID, lid_switch_idev->swbit);
+
+	lid_switch_idev->dev.parent = &pdev->dev;
+	device_set_wakeup_capable(&lid_switch_idev->dev, true);
+
+	r = input_register_device(lid_switch_idev);
+	if (r) {
+		dev_err(&pdev->dev, "failed to register lid switch: %d\n", r);
+		goto err_register;
+	}
+
+	r = device_create_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
+	if (r) {
+		dev_err(&pdev->dev, "failed to create wake mode attr: %d\n", r);
+		goto err_create_attr;
+	}
+
+	return 0;
+
+err_create_attr:
+	input_unregister_device(lid_switch_idev);
+err_register:
+	input_free_device(lid_switch_idev);
+	return r;
+}
+
+static void free_lid_switch(void)
+{
+	device_remove_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
+	input_unregister_device(lid_switch_idev);
+	input_free_device(lid_switch_idev);
+}
+
+static int __devinit xo1_sci_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int r;
+
+	/* don't run on non-XOs */
+	if (!machine_is_olpc())
+		return -ENODEV;
+
+	r = mfd_cell_enable(pdev);
+	if (r)
+		return r;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't fetch device resource info\n");
+		return -EIO;
+	}
+	acpi_base = res->start;
+
+	r = setup_power_button(pdev);
+	if (r)
+		return r;
+
+	r = setup_ebook_switch(pdev);
+	if (r)
+		goto err_ebook;
+
+	r = setup_lid_switch(pdev);
+	if (r)
+		goto err_lid;
+
+	r = setup_lid_events();
+	if (r)
+		goto err_lidevt;
+
+	r = setup_ec_sci();
+	if (r)
+		goto err_ecsci;
+
+	/* Enable PME generation for EC-generated events */
+	outl(CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN,
+		acpi_base + CS5536_PM_GPE0_EN);
+
+	/* Clear pending events */
+	outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
+	process_sci_queue(false);
+
+	/* Initial sync */
+	send_ebook_state();
+	detect_lid_state();
+	send_lid_state();
+
+	r = setup_sci_interrupt(pdev);
+	if (r)
+		goto err_sci;
+
+	/* Enable all EC events */
+	olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+	return r;
+
+err_sci:
+	free_ec_sci();
+err_ecsci:
+	free_lid_events();
+err_lidevt:
+	free_lid_switch();
+err_lid:
+	free_ebook_switch();
+err_ebook:
+	free_power_button();
+	return r;
+}
+
+static int __devexit xo1_sci_remove(struct platform_device *pdev)
+{
+	mfd_cell_disable(pdev);
+	free_irq(sci_irq, pdev);
+	cancel_work_sync(&sci_work);
+	free_ec_sci();
+	free_lid_events();
+	free_lid_switch();
+	free_ebook_switch();
+	free_power_button();
+	acpi_base = 0;
+	return 0;
+}
+
+static struct platform_driver xo1_sci_driver = {
+	.driver = {
+		.name = "olpc-xo1-sci-acpi",
+	},
+	.probe = xo1_sci_probe,
+	.remove = __devexit_p(xo1_sci_remove),
+	.suspend = xo1_sci_suspend,
+	.resume = xo1_sci_resume,
+};
+
+static int __init xo1_sci_init(void)
+{
+	return platform_driver_register(&xo1_sci_driver);
+}
+arch_initcall(xo1_sci_init);
diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
deleted file mode 100644
index ab81fb2..0000000
--- a/arch/x86/platform/olpc/olpc-xo1.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Support for features of the OLPC XO-1 laptop
- *
- * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
- * Copyright (C) 2010 One Laptop per Child
- * Copyright (C) 2006 Red Hat, Inc.
- * Copyright (C) 2006 Advanced Micro Devices, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/mfd/core.h>
-
-#include <asm/io.h>
-#include <asm/olpc.h>
-
-#define DRV_NAME "olpc-xo1"
-
-/* PMC registers (PMS block) */
-#define PM_SCLK		0x10
-#define PM_IN_SLPCTL	0x20
-#define PM_WKXD		0x34
-#define PM_WKD		0x30
-#define PM_SSC		0x54
-
-/* PM registers (ACPI block) */
-#define PM1_CNT		0x08
-#define PM_GPE0_STS	0x18
-
-static unsigned long acpi_base;
-static unsigned long pms_base;
-
-static void xo1_power_off(void)
-{
-	printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
-
-	/* Enable all of these controls with 0 delay */
-	outl(0x40000000, pms_base + PM_SCLK);
-	outl(0x40000000, pms_base + PM_IN_SLPCTL);
-	outl(0x40000000, pms_base + PM_WKXD);
-	outl(0x40000000, pms_base + PM_WKD);
-
-	/* Clear status bits (possibly unnecessary) */
-	outl(0x0002ffff, pms_base  + PM_SSC);
-	outl(0xffffffff, acpi_base + PM_GPE0_STS);
-
-	/* Write SLP_EN bit to start the machinery */
-	outl(0x00002000, acpi_base + PM1_CNT);
-}
-
-static int __devinit olpc_xo1_probe(struct platform_device *pdev)
-{
-	struct resource *res;
-	int err;
-
-	/* don't run on non-XOs */
-	if (!machine_is_olpc())
-		return -ENODEV;
-
-	err = mfd_cell_enable(pdev);
-	if (err)
-		return err;
-
-	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
-	if (!res) {
-		dev_err(&pdev->dev, "can't fetch device resource info\n");
-		return -EIO;
-	}
-	if (strcmp(pdev->name, "cs5535-pms") == 0)
-		pms_base = res->start;
-	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
-		acpi_base = res->start;
-
-	/* If we have both addresses, we can override the poweroff hook */
-	if (pms_base && acpi_base) {
-		pm_power_off = xo1_power_off;
-		printk(KERN_INFO "OLPC XO-1 support registered\n");
-	}
-
-	return 0;
-}
-
-static int __devexit olpc_xo1_remove(struct platform_device *pdev)
-{
-	mfd_cell_disable(pdev);
-
-	if (strcmp(pdev->name, "cs5535-pms") == 0)
-		pms_base = 0;
-	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
-		acpi_base = 0;
-
-	pm_power_off = NULL;
-	return 0;
-}
-
-static struct platform_driver cs5535_pms_drv = {
-	.driver = {
-		.name = "cs5535-pms",
-		.owner = THIS_MODULE,
-	},
-	.probe = olpc_xo1_probe,
-	.remove = __devexit_p(olpc_xo1_remove),
-};
-
-static struct platform_driver cs5535_acpi_drv = {
-	.driver = {
-		.name = "olpc-xo1-pm-acpi",
-		.owner = THIS_MODULE,
-	},
-	.probe = olpc_xo1_probe,
-	.remove = __devexit_p(olpc_xo1_remove),
-};
-
-static int __init olpc_xo1_init(void)
-{
-	int r;
-
-	r = platform_driver_register(&cs5535_pms_drv);
-	if (r)
-		return r;
-
-	r = platform_driver_register(&cs5535_acpi_drv);
-	if (r)
-		platform_driver_unregister(&cs5535_pms_drv);
-
-	return r;
-}
-
-static void __exit olpc_xo1_exit(void)
-{
-	platform_driver_unregister(&cs5535_acpi_drv);
-	platform_driver_unregister(&cs5535_pms_drv);
-}
-
-MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:cs5535-pms");
-
-module_init(olpc_xo1_init);
-module_exit(olpc_xo1_exit);
diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c
new file mode 100644
index 0000000..2b235b7
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc-xo15-sci.c
@@ -0,0 +1,168 @@
+/*
+ * Support for OLPC XO-1.5 System Control Interrupts (SCI)
+ *
+ * Copyright (C) 2009-2010 One Laptop per Child
+ *
+ * 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/device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME			"olpc-xo15-sci"
+#define PFX				DRV_NAME ": "
+#define XO15_SCI_CLASS			DRV_NAME
+#define XO15_SCI_DEVICE_NAME		"OLPC XO-1.5 SCI"
+
+static unsigned long xo15_sci_gpe;
+
+static void battery_status_changed(void)
+{
+	struct power_supply *psy = power_supply_get_by_name("olpc-battery");
+
+	if (psy) {
+		power_supply_changed(psy);
+		put_device(psy->dev);
+	}
+}
+
+static void ac_status_changed(void)
+{
+	struct power_supply *psy = power_supply_get_by_name("olpc-ac");
+
+	if (psy) {
+		power_supply_changed(psy);
+		put_device(psy->dev);
+	}
+}
+
+static void process_sci_queue(void)
+{
+	u16 data;
+	int r;
+
+	do {
+		r = olpc_ec_sci_query(&data);
+		if (r || !data)
+			break;
+
+		pr_debug(PFX "SCI 0x%x received\n", data);
+
+		switch (data) {
+		case EC_SCI_SRC_BATERR:
+		case EC_SCI_SRC_BATSOC:
+		case EC_SCI_SRC_BATTERY:
+		case EC_SCI_SRC_BATCRIT:
+			battery_status_changed();
+			break;
+		case EC_SCI_SRC_ACPWR:
+			ac_status_changed();
+			break;
+		}
+	} while (data);
+
+	if (r)
+		pr_err(PFX "Failed to clear SCI queue");
+}
+
+static void process_sci_queue_work(struct work_struct *work)
+{
+	process_sci_queue();
+}
+
+static DECLARE_WORK(sci_work, process_sci_queue_work);
+
+static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
+{
+	schedule_work(&sci_work);
+	return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
+}
+
+static int xo15_sci_add(struct acpi_device *device)
+{
+	unsigned long long tmp;
+	acpi_status status;
+
+	if (!device)
+		return -EINVAL;
+
+	strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME);
+	strcpy(acpi_device_class(device), XO15_SCI_CLASS);
+
+	/* Get GPE bit assignment (EC events). */
+	status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp);
+	if (ACPI_FAILURE(status))
+		return -EINVAL;
+
+	xo15_sci_gpe = tmp;
+	status = acpi_install_gpe_handler(NULL, xo15_sci_gpe,
+					  ACPI_GPE_EDGE_TRIGGERED,
+					  xo15_sci_gpe_handler, device);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
+
+	/* Flush queue, and enable all SCI events */
+	process_sci_queue();
+	olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+	acpi_enable_gpe(NULL, xo15_sci_gpe);
+
+	/* Enable wake-on-EC */
+	if (device->wakeup.flags.valid)
+		device_init_wakeup(&device->dev, true);
+
+	return 0;
+}
+
+static int xo15_sci_remove(struct acpi_device *device, int type)
+{
+	acpi_disable_gpe(NULL, xo15_sci_gpe);
+	acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
+	cancel_work_sync(&sci_work);
+	return 0;
+}
+
+static int xo15_sci_resume(struct acpi_device *device)
+{
+	/* Enable all EC events */
+	olpc_ec_mask_write(EC_SCI_SRC_ALL);
+
+	/* Power/battery status might have changed */
+	battery_status_changed();
+	ac_status_changed();
+
+	return 0;
+}
+
+static const struct acpi_device_id xo15_sci_device_ids[] = {
+	{"XO15EC", 0},
+	{"", 0},
+};
+
+static struct acpi_driver xo15_sci_drv = {
+	.name = DRV_NAME,
+	.class = XO15_SCI_CLASS,
+	.ids = xo15_sci_device_ids,
+	.ops = {
+		.add = xo15_sci_add,
+		.remove = xo15_sci_remove,
+		.resume = xo15_sci_resume,
+	},
+};
+
+static int __init xo15_sci_init(void)
+{
+	return acpi_bus_register_driver(&xo15_sci_drv);
+}
+device_initcall(xo15_sci_init);
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index 0060fd5..8b9940e 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
+#include <linux/syscore_ops.h>
 
 #include <asm/geode.h>
 #include <asm/setup.h>
@@ -30,6 +31,9 @@
 
 static DEFINE_SPINLOCK(ec_lock);
 
+/* EC event mask to be applied during suspend (defining wakeup sources). */
+static u16 ec_wakeup_mask;
+
 /* what the timeout *should* be (in ms) */
 #define EC_BASE_TIMEOUT 20
 
@@ -188,6 +192,88 @@
 }
 EXPORT_SYMBOL_GPL(olpc_ec_cmd);
 
+void olpc_ec_wakeup_set(u16 value)
+{
+	ec_wakeup_mask |= value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
+
+void olpc_ec_wakeup_clear(u16 value)
+{
+	ec_wakeup_mask &= ~value;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
+
+/*
+ * Returns true if the compile and runtime configurations allow for EC events
+ * to wake the system.
+ */
+bool olpc_ec_wakeup_available(void)
+{
+	if (!machine_is_olpc())
+		return false;
+
+	/*
+	 * XO-1 EC wakeups are available when olpc-xo1-sci driver is
+	 * compiled in
+	 */
+#ifdef CONFIG_OLPC_XO1_SCI
+	if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
+		return true;
+#endif
+
+	/*
+	 * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
+	 * compiled in
+	 */
+#ifdef CONFIG_OLPC_XO15_SCI
+	if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
+		return true;
+#endif
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
+
+int olpc_ec_mask_write(u16 bits)
+{
+	if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
+		__be16 ec_word = cpu_to_be16(bits);
+		return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
+				   NULL, 0);
+	} else {
+		unsigned char ec_byte = bits & 0xff;
+		return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
+	}
+}
+EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
+
+int olpc_ec_sci_query(u16 *sci_value)
+{
+	int ret;
+
+	if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
+		__be16 ec_word;
+		ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
+			NULL, 0, (void *) &ec_word, 2);
+		if (ret == 0)
+			*sci_value = be16_to_cpu(ec_word);
+	} else {
+		unsigned char ec_byte;
+		ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
+		if (ret == 0)
+			*sci_value = ec_byte;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
+
+static int olpc_ec_suspend(void)
+{
+	return olpc_ec_mask_write(ec_wakeup_mask);
+}
+
 static bool __init check_ofw_architecture(struct device_node *root)
 {
 	const char *olpc_arch;
@@ -242,6 +328,10 @@
 	return 0;
 }
 
+static struct syscore_ops olpc_syscore_ops = {
+	.suspend = olpc_ec_suspend,
+};
+
 static int __init olpc_init(void)
 {
 	int r = 0;
@@ -266,6 +356,9 @@
 			!cs5535_has_vsa2())
 		x86_init.pci.arch_init = pci_olpc_init;
 #endif
+	/* EC version 0x5f adds support for wide SCI mask */
+	if (olpc_platform_info.ecver >= 0x5f)
+		olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
 
 	printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
 			((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
@@ -278,6 +371,8 @@
 			return r;
 	}
 
+	register_syscore_ops(&olpc_syscore_ops);
+
 	return 0;
 }
 
diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c
index d39f63d..d6ee929 100644
--- a/arch/x86/platform/olpc/olpc_dt.c
+++ b/arch/x86/platform/olpc/olpc_dt.c
@@ -165,6 +165,107 @@
 	.pkg2path = olpc_dt_pkg2path,
 };
 
+static phandle __init olpc_dt_finddevice(const char *path)
+{
+	phandle node;
+	const void *args[] = { path };
+	void *res[] = { &node };
+
+	if (olpc_ofw("finddevice", args, res)) {
+		pr_err("olpc_dt: finddevice failed!\n");
+		return 0;
+	}
+
+	if ((s32) node == -1)
+		return 0;
+
+	return node;
+}
+
+static int __init olpc_dt_interpret(const char *words)
+{
+	int result;
+	const void *args[] = { words };
+	void *res[] = { &result };
+
+	if (olpc_ofw("interpret", args, res)) {
+		pr_err("olpc_dt: interpret failed!\n");
+		return -1;
+	}
+
+	return result;
+}
+
+/*
+ * Extract board revision directly from OFW device tree.
+ * We can't use olpc_platform_info because that hasn't been set up yet.
+ */
+static u32 __init olpc_dt_get_board_revision(void)
+{
+	phandle node;
+	__be32 rev;
+	int r;
+
+	node = olpc_dt_finddevice("/");
+	if (!node)
+		return 0;
+
+	r = olpc_dt_getproperty(node, "board-revision-int",
+				(char *) &rev, sizeof(rev));
+	if (r < 0)
+		return 0;
+
+	return be32_to_cpu(rev);
+}
+
+void __init olpc_dt_fixup(void)
+{
+	int r;
+	char buf[64];
+	phandle node;
+	u32 board_rev;
+
+	node = olpc_dt_finddevice("/battery@0");
+	if (!node)
+		return;
+
+	/*
+	 * If the battery node has a compatible property, we are running a new
+	 * enough firmware and don't have fixups to make.
+	 */
+	r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
+	if (r > 0)
+		return;
+
+	pr_info("PROM DT: Old firmware detected, applying fixes\n");
+
+	/* Add olpc,xo1-battery compatible marker to battery node */
+	olpc_dt_interpret("\" /battery@0\" find-device"
+		" \" olpc,xo1-battery\" +compatible"
+		" device-end");
+
+	board_rev = olpc_dt_get_board_revision();
+	if (!board_rev)
+		return;
+
+	if (board_rev >= olpc_board_pre(0xd0)) {
+		/* XO-1.5: add dcon device */
+		olpc_dt_interpret("\" /pci/display@1\" find-device"
+			" new-device"
+			" \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+			" finish-device device-end");
+	} else {
+		/* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
+		olpc_dt_interpret("\" /pci/display@1,1\" find-device"
+			" new-device"
+			" \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+			" finish-device device-end"
+			" \" /rtc\" find-device"
+			" \" olpc,xo1-rtc\" +compatible"
+			" device-end");
+	}
+}
+
 void __init olpc_dt_build_devicetree(void)
 {
 	phandle root;
@@ -172,6 +273,8 @@
 	if (!olpc_ofw_is_installed())
 		return;
 
+	olpc_dt_fixup();
+
 	root = olpc_dt_getsibling(0);
 	if (!root) {
 		pr_err("PROM: unable to get root node from OFW!\n");
diff --git a/arch/x86/platform/olpc/xo1-wakeup.S b/arch/x86/platform/olpc/xo1-wakeup.S
new file mode 100644
index 0000000..948deb2
--- /dev/null
+++ b/arch/x86/platform/olpc/xo1-wakeup.S
@@ -0,0 +1,124 @@
+.text
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable_32.h>
+
+	.macro writepost,value
+		movb $0x34, %al
+		outb %al, $0x70
+		movb $\value, %al
+		outb %al, $0x71
+	.endm
+
+wakeup_start:
+	# OFW lands us here, running in protected mode, with a
+	# kernel-compatible GDT already setup.
+
+	# Clear any dangerous flags
+	pushl $0
+	popfl
+
+	writepost 0x31
+
+	# Set up %cr3
+	movl $initial_page_table - __PAGE_OFFSET, %eax
+	movl %eax, %cr3
+
+	movl saved_cr4, %eax
+	movl %eax, %cr4
+
+	movl saved_cr0, %eax
+	movl %eax, %cr0
+
+	# Control registers were modified, pipeline resync is needed
+	jmp 1f
+1:
+
+	movw    $__KERNEL_DS, %ax
+	movw    %ax, %ss
+	movw    %ax, %ds
+	movw    %ax, %es
+	movw    %ax, %fs
+	movw    %ax, %gs
+
+	lgdt    saved_gdt
+	lidt    saved_idt
+	lldt    saved_ldt
+	ljmp    $(__KERNEL_CS),$1f
+1:
+	movl    %cr3, %eax
+	movl    %eax, %cr3
+	wbinvd
+
+	# Go back to the return point
+	jmp ret_point
+
+save_registers:
+	sgdt  saved_gdt
+	sidt  saved_idt
+	sldt  saved_ldt
+
+	pushl %edx
+	movl %cr4, %edx
+	movl %edx, saved_cr4
+
+	movl %cr0, %edx
+	movl %edx, saved_cr0
+
+	popl %edx
+
+	movl %ebx, saved_context_ebx
+	movl %ebp, saved_context_ebp
+	movl %esi, saved_context_esi
+	movl %edi, saved_context_edi
+
+	pushfl
+	popl saved_context_eflags
+
+	ret
+
+restore_registers:
+	movl saved_context_ebp, %ebp
+	movl saved_context_ebx, %ebx
+	movl saved_context_esi, %esi
+	movl saved_context_edi, %edi
+
+	pushl saved_context_eflags
+	popfl
+
+	ret
+
+ENTRY(do_olpc_suspend_lowlevel)
+	call	save_processor_state
+	call	save_registers
+
+	# This is the stack context we want to remember
+	movl %esp, saved_context_esp
+
+	pushl	$3
+	call	xo1_do_sleep
+
+	jmp	wakeup_start
+	.p2align 4,,7
+ret_point:
+	movl    saved_context_esp, %esp
+
+	writepost 0x32
+
+	call	restore_registers
+	call	restore_processor_state
+	ret
+
+.data
+saved_gdt:             .long   0,0
+saved_idt:             .long   0,0
+saved_ldt:             .long   0
+saved_cr4:             .long   0
+saved_cr0:             .long   0
+saved_context_esp:     .long   0
+saved_context_edi:     .long   0
+saved_context_esi:     .long   0
+saved_context_ebx:     .long   0
+saved_context_ebp:     .long   0
+saved_context_eflags:  .long   0
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile
index ccf73b2..45e94ac 100644
--- a/arch/x86/xen/Makefile
+++ b/arch/x86/xen/Makefile
@@ -13,7 +13,9 @@
 obj-y		:= enlighten.o setup.o multicalls.o mmu.o irq.o \
 			time.o xen-asm.o xen-asm_$(BITS).o \
 			grant-table.o suspend.o platform-pci-unplug.o \
-			p2m.o trace.o
+			p2m.o
+
+obj-$(CONFIG_FUNCTION_TRACER) += trace.o
 
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
diff --git a/arch/xtensa/include/asm/atomic.h b/arch/xtensa/include/asm/atomic.h
index a96a061..23592ef 100644
--- a/arch/xtensa/include/asm/atomic.h
+++ b/arch/xtensa/include/asm/atomic.h
@@ -225,15 +225,15 @@
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 
 /**
- * atomic_add_unless - add unless the number is a given value
+ * __atomic_add_unless - add unless the number is a given value
  * @v: pointer of type atomic_t
  * @a: the amount to add to v...
  * @u: ...unless v is equal to u.
  *
  * Atomically adds @a to @v, so long as it was not @u.
- * Returns non-zero if @v was not @u, and zero otherwise.
+ * Returns the old value of @v.
  */
-static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
+static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 {
 	int c, old;
 	c = atomic_read(v);
@@ -245,10 +245,9 @@
 			break;
 		c = old;
 	}
-	return c != (u);
+	return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
 static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
 {
@@ -292,7 +291,6 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
 #endif /* __KERNEL__ */
 
 #endif /* _XTENSA_ATOMIC_H */
diff --git a/arch/xtensa/include/asm/bitops.h b/arch/xtensa/include/asm/bitops.h
index c8fac8d..40aa7fe 100644
--- a/arch/xtensa/include/asm/bitops.h
+++ b/arch/xtensa/include/asm/bitops.h
@@ -108,19 +108,7 @@
 #include <asm-generic/bitops/find.h>
 #include <asm-generic/bitops/le.h>
 
-#ifdef __XTENSA_EL__
-# define ext2_set_bit_atomic(lock,nr,addr)				\
-	test_and_set_bit((nr), (unsigned long*)(addr))
-# define ext2_clear_bit_atomic(lock,nr,addr)				\
-	test_and_clear_bit((nr), (unsigned long*)(addr))
-#elif defined(__XTENSA_EB__)
-# define ext2_set_bit_atomic(lock,nr,addr)				\
-	test_and_set_bit((nr) ^ 0x18, (unsigned long*)(addr))
-# define ext2_clear_bit_atomic(lock,nr,addr)				\
-	test_and_clear_bit((nr) ^ 0x18, (unsigned long*)(addr))
-#else
-# error processor byte order undefined!
-#endif
+#include <asm-generic/bitops/ext2-atomic-setbit.h>
 
 #include <asm-generic/bitops/hweight.h>
 #include <asm-generic/bitops/lock.h>
diff --git a/arch/xtensa/include/asm/posix_types.h b/arch/xtensa/include/asm/posix_types.h
index 43f9dd1..6b2190c 100644
--- a/arch/xtensa/include/asm/posix_types.h
+++ b/arch/xtensa/include/asm/posix_types.h
@@ -58,7 +58,7 @@
 
 #define	__FD_SET(d, set)	((set)->fds_bits[__FDELT(d)] |= __FDMASK(d))
 #define	__FD_CLR(d, set)	((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d))
-#define	__FD_ISSET(d, set)	((set)->fds_bits[__FDELT(d)] & __FDMASK(d))
+#define	__FD_ISSET(d, set)	(!!((set)->fds_bits[__FDELT(d)] & __FDMASK(d)))
 #define	__FD_ZERO(set)	\
   ((void) memset ((void *) (set), 0, sizeof (__kernel_fd_set)))
 
diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h
index 0d42c93..d85d38d 100644
--- a/arch/xtensa/include/asm/ptrace.h
+++ b/arch/xtensa/include/asm/ptrace.h
@@ -120,7 +120,6 @@
   (task_stack_page(tsk) + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4) - 1)
 # define user_mode(regs) (((regs)->ps & 0x00000020)!=0)
 # define instruction_pointer(regs) ((regs)->pc)
-extern void show_regs(struct pt_regs *);
 
 # ifndef CONFIG_SMP
 #  define profile_pc(regs) instruction_pointer(regs)
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index e3558b9..47041e7 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -40,7 +40,7 @@
 #include <asm/platform.h>
 #include <asm/mmu.h>
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/asm-offsets.h>
 #include <asm/regs.h>
 
diff --git a/block/blk-core.c b/block/blk-core.c
index f8cb099..b850bed 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1283,7 +1283,7 @@
 
 	if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) ||
 	    bio_flagged(bio, BIO_CPU_AFFINE))
-		req->cpu = smp_processor_id();
+		req->cpu = raw_smp_processor_id();
 
 	plug = current->plug;
 	if (plug) {
@@ -1361,14 +1361,9 @@
 }
 __setup("fail_make_request=", setup_fail_make_request);
 
-static int should_fail_request(struct bio *bio)
+static bool should_fail_request(struct hd_struct *part, unsigned int bytes)
 {
-	struct hd_struct *part = bio->bi_bdev->bd_part;
-
-	if (part_to_disk(part)->part0.make_it_fail || part->make_it_fail)
-		return should_fail(&fail_make_request, bio->bi_size);
-
-	return 0;
+	return part->make_it_fail && should_fail(&fail_make_request, bytes);
 }
 
 static int __init fail_make_request_debugfs(void)
@@ -1381,9 +1376,10 @@
 
 #else /* CONFIG_FAIL_MAKE_REQUEST */
 
-static inline int should_fail_request(struct bio *bio)
+static inline bool should_fail_request(struct hd_struct *part,
+					unsigned int bytes)
 {
-	return 0;
+	return false;
 }
 
 #endif /* CONFIG_FAIL_MAKE_REQUEST */
@@ -1466,6 +1462,7 @@
 	old_dev = 0;
 	do {
 		char b[BDEVNAME_SIZE];
+		struct hd_struct *part;
 
 		q = bdev_get_queue(bio->bi_bdev);
 		if (unlikely(!q)) {
@@ -1489,7 +1486,10 @@
 		if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
 			goto end_io;
 
-		if (should_fail_request(bio))
+		part = bio->bi_bdev->bd_part;
+		if (should_fail_request(part, bio->bi_size) ||
+		    should_fail_request(&part_to_disk(part)->part0,
+					bio->bi_size))
 			goto end_io;
 
 		/*
@@ -1704,11 +1704,9 @@
 	if (blk_rq_check_limits(q, rq))
 		return -EIO;
 
-#ifdef CONFIG_FAIL_MAKE_REQUEST
-	if (rq->rq_disk && rq->rq_disk->part0.make_it_fail &&
-	    should_fail(&fail_make_request, blk_rq_bytes(rq)))
+	if (rq->rq_disk &&
+	    should_fail_request(&rq->rq_disk->part0, blk_rq_bytes(rq)))
 		return -EIO;
-#endif
 
 	spin_lock_irqsave(q->queue_lock, flags);
 
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index 940d70c..ac33d5f 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -12,7 +12,7 @@
  *
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <crypto/if_alg.h>
 #include <linux/crypto.h>
 #include <linux/init.h>
diff --git a/crypto/proc.c b/crypto/proc.c
index 58fef67..3808697 100644
--- a/crypto/proc.c
+++ b/crypto/proc.c
@@ -13,7 +13,7 @@
  *
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/init.h>
 #include <linux/crypto.h>
 #include <linux/rwsem.h>
diff --git a/crypto/rng.c b/crypto/rng.c
index f93cb53..45229ae 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -12,7 +12,7 @@
  *
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <crypto/internal/rng.h>
 #include <linux/err.h>
 #include <linux/module.h>
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index db39e9e..ada4b4d 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -46,7 +46,6 @@
 
 #define PREFIX "ACPI: "
 
-#define ACPI_VIDEO_CLASS		"video"
 #define ACPI_VIDEO_BUS_NAME		"Video Bus"
 #define ACPI_VIDEO_DEVICE_NAME		"Video Device"
 #define ACPI_VIDEO_NOTIFY_SWITCH	0x80
@@ -1445,7 +1444,8 @@
 	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,
 					 * most likely via hotkey. */
 		acpi_bus_generate_proc_event(device, event, 0);
-		keycode = KEY_SWITCHVIDEOMODE;
+		if (!acpi_notifier_call_chain(device, event, 0))
+			keycode = KEY_SWITCHVIDEOMODE;
 		break;
 
 	case ACPI_VIDEO_NOTIFY_PROBE:	/* User plugged in or removed a video
@@ -1475,7 +1475,8 @@
 		break;
 	}
 
-	acpi_notifier_call_chain(device, event, 0);
+	if (event != ACPI_VIDEO_NOTIFY_SWITCH)
+		acpi_notifier_call_chain(device, event, 0);
 
 	if (keycode) {
 		input_report_key(input, keycode, 1);
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index bb3b016..f8f41e0 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -38,7 +38,7 @@
 #include <linux/ihex.h>
 #include <linux/slab.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index 0b06250..b22d71c 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -11,7 +11,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 
 extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 3230ea0..9307141 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -21,7 +21,7 @@
 #include <linux/slab.h>
 #include <asm/system.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/string.h>
 #include <asm/byteorder.h>
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
index 493a693..dc9a62c 100644
--- a/drivers/atm/eni.h
+++ b/drivers/atm/eni.h
@@ -14,7 +14,7 @@
 #include <linux/time.h>
 #include <linux/pci.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "midway.h"
 
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
index 7c7b571..5072f8a 100644
--- a/drivers/atm/firestream.c
+++ b/drivers/atm/firestream.c
@@ -52,7 +52,7 @@
 #include <asm/system.h>
 #include <asm/string.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <linux/wait.h>
 
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index bc9e702..361f5ae 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -44,7 +44,7 @@
 #include <asm/dma.h>
 #include <asm/byteorder.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_SBUS
 #include <linux/of.h>
diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c
index 2875061..b812103 100644
--- a/drivers/atm/horizon.c
+++ b/drivers/atm/horizon.c
@@ -45,7 +45,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/string.h>
 #include <asm/byteorder.h>
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index be0dbfe..db06f34 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -46,7 +46,7 @@
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 #ifdef CONFIG_ATM_IDT77252_USE_SUNI
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 957106f..cb90f7a 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -58,7 +58,7 @@
 #include <linux/slab.h>
 #include <asm/system.h>  
 #include <asm/io.h>  
-#include <asm/atomic.h>  
+#include <linux/atomic.h>
 #include <asm/uaccess.h>  
 #include <asm/string.h>  
 #include <asm/byteorder.h>  
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index 6b313ee..1c70c45 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -51,7 +51,7 @@
 #include <linux/idr.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "nicstar.h"
 #ifdef CONFIG_ATM_NICSTAR_USE_SUNI
 #include "suni.h"
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
index 41c56ea..90f1ccc 100644
--- a/drivers/atm/suni.c
+++ b/drivers/atm/suni.c
@@ -25,7 +25,7 @@
 #include <asm/system.h>
 #include <asm/param.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "suni.h"
 
diff --git a/drivers/atm/uPD98402.c b/drivers/atm/uPD98402.c
index c45ae05..5120a96 100644
--- a/drivers/atm/uPD98402.c
+++ b/drivers/atm/uPD98402.c
@@ -11,7 +11,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "uPD98402.h"
 
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index 7f8c513..d889f56 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -27,7 +27,7 @@
 #include <asm/system.h>
 #include <asm/string.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "uPD98401.h"
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 45d7c8f..2840ed4 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -24,7 +24,7 @@
 #include <linux/stat.h>
 #include <linux/slab.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 static DEFINE_MUTEX(mem_sysfs_mutex);
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index 5cc1232..b23de18 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -453,7 +453,7 @@
 static int opp_set_availability(struct device *dev, unsigned long freq,
 		bool availability_req)
 {
-	struct device_opp *tmp_dev_opp, *dev_opp = NULL;
+	struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV);
 	struct opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
 	int r = 0;
 
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 942d6a7..17b7934 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -5,7 +5,7 @@
 #include <linux/device.h>
 #include <linux/string.h>
 #include <linux/pm_runtime.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/jiffies.h>
 #include "power.h"
 
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 6961002..951a4e3 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -33,7 +33,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 1278098..15f65b5 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -630,6 +630,14 @@
 }
 
 /*
+ * returns the size of an object in the image
+ */
+static u64 rbd_obj_bytes(struct rbd_image_header *header)
+{
+	return 1 << header->obj_order;
+}
+
+/*
  * bio helpers
  */
 
@@ -1253,6 +1261,35 @@
 	return ret;
 }
 
+/*
+ * Request sync osd unwatch
+ */
+static int rbd_req_sync_unwatch(struct rbd_device *dev,
+				const char *obj)
+{
+	struct ceph_osd_req_op *ops;
+
+	int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
+	if (ret < 0)
+		return ret;
+
+	ops[0].watch.ver = 0;
+	ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+	ops[0].watch.flag = 0;
+
+	ret = rbd_req_sync_op(dev, NULL,
+			      CEPH_NOSNAP,
+			      0,
+			      CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
+			      ops,
+			      1, obj, 0, 0, NULL, NULL, NULL);
+
+	rbd_destroy_ops(ops);
+	ceph_osdc_cancel_event(dev->watch_event);
+	dev->watch_event = NULL;
+	return ret;
+}
+
 struct rbd_notify_info {
 	struct rbd_device *dev;
 };
@@ -1736,6 +1773,13 @@
 	q = blk_init_queue(rbd_rq_fn, &rbd_dev->lock);
 	if (!q)
 		goto out_disk;
+
+	/* set io sizes to object size */
+	blk_queue_max_hw_sectors(q, rbd_obj_bytes(&rbd_dev->header) / 512ULL);
+	blk_queue_max_segment_size(q, rbd_obj_bytes(&rbd_dev->header));
+	blk_queue_io_min(q, rbd_obj_bytes(&rbd_dev->header));
+	blk_queue_io_opt(q, rbd_obj_bytes(&rbd_dev->header));
+
 	blk_queue_merge_bvec(q, rbd_merge_bvec);
 	disk->queue = q;
 
@@ -2290,7 +2334,7 @@
 		ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc,
 						    rbd_dev->watch_request);
 	if (rbd_dev->watch_event)
-		ceph_osdc_cancel_event(rbd_dev->watch_event);
+		rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name);
 
 	rbd_put_client(rbd_dev);
 
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index ac6739e..c3de70d 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -1,6 +1,6 @@
 /* n2-drv.c: Niagara-2 RNG driver.
  *
- * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2008, 2011 David S. Miller <davem@davemloft.net>
  */
 
 #include <linux/kernel.h>
@@ -22,8 +22,8 @@
 
 #define DRV_MODULE_NAME		"n2rng"
 #define PFX DRV_MODULE_NAME	": "
-#define DRV_MODULE_VERSION	"0.1"
-#define DRV_MODULE_RELDATE	"May 15, 2008"
+#define DRV_MODULE_VERSION	"0.2"
+#define DRV_MODULE_RELDATE	"July 27, 2011"
 
 static char version[] __devinitdata =
 	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -623,14 +623,14 @@
 static int __devinit n2rng_probe(struct platform_device *op)
 {
 	const struct of_device_id *match;
-	int victoria_falls;
+	int multi_capable;
 	int err = -ENOMEM;
 	struct n2rng *np;
 
 	match = of_match_device(n2rng_match, &op->dev);
 	if (!match)
 		return -EINVAL;
-	victoria_falls = (match->data != NULL);
+	multi_capable = (match->data != NULL);
 
 	n2rng_driver_version();
 	np = kzalloc(sizeof(*np), GFP_KERNEL);
@@ -640,8 +640,8 @@
 
 	INIT_DELAYED_WORK(&np->work, n2rng_work);
 
-	if (victoria_falls)
-		np->flags |= N2RNG_FLAG_VF;
+	if (multi_capable)
+		np->flags |= N2RNG_FLAG_MULTI;
 
 	err = -ENODEV;
 	np->hvapi_major = 2;
@@ -658,10 +658,10 @@
 		}
 	}
 
-	if (np->flags & N2RNG_FLAG_VF) {
+	if (np->flags & N2RNG_FLAG_MULTI) {
 		if (np->hvapi_major < 2) {
-			dev_err(&op->dev, "VF RNG requires HVAPI major "
-				"version 2 or later, got %lu\n",
+			dev_err(&op->dev, "multi-unit-capable RNG requires "
+				"HVAPI major version 2 or later, got %lu\n",
 				np->hvapi_major);
 			goto out_hvapi_unregister;
 		}
@@ -688,8 +688,8 @@
 		goto out_free_units;
 
 	dev_info(&op->dev, "Found %s RNG, units: %d\n",
-		 ((np->flags & N2RNG_FLAG_VF) ?
-		  "Victoria Falls" : "Niagara2"),
+		 ((np->flags & N2RNG_FLAG_MULTI) ?
+		  "multi-unit-capable" : "single-unit"),
 		 np->num_units);
 
 	np->hwrng.name = "n2rng";
@@ -751,6 +751,11 @@
 		.compatible	= "SUNW,vf-rng",
 		.data		= (void *) 1,
 	},
+	{
+		.name		= "random-number-generator",
+		.compatible	= "SUNW,kt-rng",
+		.data		= (void *) 1,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, n2rng_match);
diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h
index 4bea07f..f244ac8 100644
--- a/drivers/char/hw_random/n2rng.h
+++ b/drivers/char/hw_random/n2rng.h
@@ -68,7 +68,7 @@
 	struct platform_device	*op;
 
 	unsigned long		flags;
-#define N2RNG_FLAG_VF		0x00000001 /* Victoria Falls RNG, else N2 */
+#define N2RNG_FLAG_MULTI	0x00000001 /* Multi-unit capable RNG */
 #define N2RNG_FLAG_CONTROL	0x00000002 /* Operating in control domain */
 #define N2RNG_FLAG_READY	0x00000008 /* Ready for hw-rng layer      */
 #define N2RNG_FLAG_SHUTDOWN	0x00000010 /* Driver unregistering        */
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 320668f..3302586 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -52,7 +52,7 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_X86
 /*
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 25d139c..5c0d96a 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -46,7 +46,7 @@
 #include <asm/page.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/tlbflush.h>
 #include <asm/uncached.h>
 #include <asm/sn/addrs.h>
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c
index 1a9f5f6..fca0c51 100644
--- a/drivers/char/ramoops.c
+++ b/drivers/char/ramoops.c
@@ -19,18 +19,26 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
+#include <linux/err.h>
 #include <linux/module.h>
 #include <linux/kmsg_dump.h>
 #include <linux/time.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 #include <linux/ramoops.h>
 
 #define RAMOOPS_KERNMSG_HDR "===="
+#define MIN_MEM_SIZE 4096UL
 
-#define RECORD_SIZE 4096UL
+static ulong record_size = MIN_MEM_SIZE;
+module_param(record_size, ulong, 0400);
+MODULE_PARM_DESC(record_size,
+		"size of each dump done on oops/panic");
 
 static ulong mem_address;
 module_param(mem_address, ulong, 0400);
@@ -52,10 +60,15 @@
 	void *virt_addr;
 	phys_addr_t phys_addr;
 	unsigned long size;
+	unsigned long record_size;
+	int dump_oops;
 	int count;
 	int max_count;
 } oops_cxt;
 
+static struct platform_device *dummy;
+static struct ramoops_platform_data *dummy_data;
+
 static void ramoops_do_dump(struct kmsg_dumper *dumper,
 		enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
 		const char *s2, unsigned long l2)
@@ -74,13 +87,13 @@
 		return;
 
 	/* Only dump oopses if dump_oops is set */
-	if (reason == KMSG_DUMP_OOPS && !dump_oops)
+	if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
 		return;
 
-	buf = cxt->virt_addr + (cxt->count * RECORD_SIZE);
+	buf = cxt->virt_addr + (cxt->count * cxt->record_size);
 	buf_orig = buf;
 
-	memset(buf, '\0', RECORD_SIZE);
+	memset(buf, '\0', cxt->record_size);
 	res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
 	buf += res;
 	do_gettimeofday(&timestamp);
@@ -88,8 +101,8 @@
 	buf += res;
 
 	hdr_size = buf - buf_orig;
-	l2_cpy = min(l2, RECORD_SIZE - hdr_size);
-	l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy);
+	l2_cpy = min(l2, cxt->record_size - hdr_size);
+	l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy);
 
 	s2_start = l2 - l2_cpy;
 	s1_start = l1 - l1_cpy;
@@ -106,44 +119,51 @@
 	struct ramoops_context *cxt = &oops_cxt;
 	int err = -EINVAL;
 
-	if (pdata) {
-		mem_size = pdata->mem_size;
-		mem_address = pdata->mem_address;
-	}
-
-	if (!mem_size) {
-		printk(KERN_ERR "ramoops: invalid size specification");
+	if (!pdata->mem_size || !pdata->record_size) {
+		pr_err("The memory size and the record size must be "
+			"non-zero\n");
 		goto fail3;
 	}
 
-	rounddown_pow_of_two(mem_size);
+	rounddown_pow_of_two(pdata->mem_size);
+	rounddown_pow_of_two(pdata->record_size);
 
-	if (mem_size < RECORD_SIZE) {
-		printk(KERN_ERR "ramoops: size too small");
+	/* Check for the minimum memory size */
+	if (pdata->mem_size < MIN_MEM_SIZE &&
+			pdata->record_size < MIN_MEM_SIZE) {
+		pr_err("memory size too small, minium is %lu\n", MIN_MEM_SIZE);
 		goto fail3;
 	}
 
-	cxt->max_count = mem_size / RECORD_SIZE;
+	if (pdata->mem_size < pdata->record_size) {
+		pr_err("The memory size must be larger than the "
+			"records size\n");
+		goto fail3;
+	}
+
+	cxt->max_count = pdata->mem_size / pdata->record_size;
 	cxt->count = 0;
-	cxt->size = mem_size;
-	cxt->phys_addr = mem_address;
+	cxt->size = pdata->mem_size;
+	cxt->phys_addr = pdata->mem_address;
+	cxt->record_size = pdata->record_size;
+	cxt->dump_oops = pdata->dump_oops;
 
 	if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
-		printk(KERN_ERR "ramoops: request mem region failed");
+		pr_err("request mem region failed\n");
 		err = -EINVAL;
 		goto fail3;
 	}
 
 	cxt->virt_addr = ioremap(cxt->phys_addr,  cxt->size);
 	if (!cxt->virt_addr) {
-		printk(KERN_ERR "ramoops: ioremap failed");
+		pr_err("ioremap failed\n");
 		goto fail2;
 	}
 
 	cxt->dump.dump = ramoops_do_dump;
 	err = kmsg_dump_register(&cxt->dump);
 	if (err) {
-		printk(KERN_ERR "ramoops: registering kmsg dumper failed");
+		pr_err("registering kmsg dumper failed\n");
 		goto fail1;
 	}
 
@@ -162,7 +182,7 @@
 	struct ramoops_context *cxt = &oops_cxt;
 
 	if (kmsg_dump_unregister(&cxt->dump) < 0)
-		printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper");
+		pr_warn("could not unregister kmsg_dumper\n");
 
 	iounmap(cxt->virt_addr);
 	release_mem_region(cxt->phys_addr, cxt->size);
@@ -179,12 +199,39 @@
 
 static int __init ramoops_init(void)
 {
-	return platform_driver_probe(&ramoops_driver, ramoops_probe);
+	int ret;
+	ret = platform_driver_probe(&ramoops_driver, ramoops_probe);
+	if (ret == -ENODEV) {
+		/*
+		 * If we didn't find a platform device, we use module parameters
+		 * building platform data on the fly.
+		 */
+		pr_info("platform device not found, using module parameters\n");
+		dummy_data = kzalloc(sizeof(struct ramoops_platform_data),
+				     GFP_KERNEL);
+		if (!dummy_data)
+			return -ENOMEM;
+		dummy_data->mem_size = mem_size;
+		dummy_data->mem_address = mem_address;
+		dummy_data->record_size = record_size;
+		dummy_data->dump_oops = dump_oops;
+		dummy = platform_create_bundle(&ramoops_driver, ramoops_probe,
+			NULL, 0, dummy_data,
+			sizeof(struct ramoops_platform_data));
+
+		if (IS_ERR(dummy))
+			ret = PTR_ERR(dummy);
+		else
+			ret = 0;
+	}
+
+	return ret;
 }
 
 static void __exit ramoops_exit(void)
 {
 	platform_driver_unregister(&ramoops_driver);
+	kfree(dummy_data);
 }
 
 module_init(ramoops_init);
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 7beb0e2..caf8012 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -534,6 +534,7 @@
 	struct duration_t *duration_cap;
 	ssize_t rc;
 	u32 timeout;
+	unsigned int scale = 1;
 
 	tpm_cmd.header.in = tpm_getcap_header;
 	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -545,24 +546,30 @@
 	if (rc)
 		goto duration;
 
-	if (be32_to_cpu(tpm_cmd.header.out.length)
-	    != 4 * sizeof(u32))
-		goto duration;
+	if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+	    be32_to_cpu(tpm_cmd.header.out.length)
+	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
+		return;
 
 	timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
 	/* Don't overwrite default if value is 0 */
 	timeout = be32_to_cpu(timeout_cap->a);
+	if (timeout && timeout < 1000) {
+		/* timeouts in msec rather usec */
+		scale = 1000;
+		chip->vendor.timeout_adjusted = true;
+	}
 	if (timeout)
-		chip->vendor.timeout_a = usecs_to_jiffies(timeout);
+		chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
 	timeout = be32_to_cpu(timeout_cap->b);
 	if (timeout)
-		chip->vendor.timeout_b = usecs_to_jiffies(timeout);
+		chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
 	timeout = be32_to_cpu(timeout_cap->c);
 	if (timeout)
-		chip->vendor.timeout_c = usecs_to_jiffies(timeout);
+		chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
 	timeout = be32_to_cpu(timeout_cap->d);
 	if (timeout)
-		chip->vendor.timeout_d = usecs_to_jiffies(timeout);
+		chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
 
 duration:
 	tpm_cmd.header.in = tpm_getcap_header;
@@ -575,23 +582,31 @@
 	if (rc)
 		return;
 
-	if (be32_to_cpu(tpm_cmd.header.out.return_code)
-	    != 3 * sizeof(u32))
+	if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+	    be32_to_cpu(tpm_cmd.header.out.length)
+	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
 		return;
+
 	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->vendor.duration[TPM_SHORT] =
 	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
-	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
-	 * value wrong and apparently reports msecs rather than usecs. So we
-	 * fix up the resulting too-small TPM_SHORT value to make things work.
-	 */
-	if (chip->vendor.duration[TPM_SHORT] < (HZ/100))
-		chip->vendor.duration[TPM_SHORT] = HZ;
-
 	chip->vendor.duration[TPM_MEDIUM] =
 	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
 	chip->vendor.duration[TPM_LONG] =
 	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+
+	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
+	 * value wrong and apparently reports msecs rather than usecs. So we
+	 * fix up the resulting too-small TPM_SHORT value to make things work.
+	 * We also scale the TPM_MEDIUM and -_LONG values by 1000.
+	 */
+	if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
+		chip->vendor.duration[TPM_SHORT] = HZ;
+		chip->vendor.duration[TPM_MEDIUM] *= 1000;
+		chip->vendor.duration[TPM_LONG] *= 1000;
+		chip->vendor.duration_adjusted = true;
+		dev_info(chip->dev, "Adjusting TPM timeout parameters.");
+	}
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
@@ -600,7 +615,7 @@
 	u8 data[] = {
 		0, 193,			/* TPM_TAG_RQU_COMMAND */
 		0, 0, 0, 10,		/* length */
-		0, 0, 0, 83,		/* TPM_ORD_GetCapability */
+		0, 0, 0, 83,		/* TPM_ORD_ContinueSelfTest */
 	};
 
 	tpm_transmit(chip, data, sizeof(data));
@@ -863,18 +878,24 @@
 	data = tpm_cmd.params.readpubek_out_buffer;
 	str +=
 	    sprintf(str,
-		    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
-		    "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
-		    " %02X %02X %02X %02X %02X %02X %02X %02X\n"
-		    "Modulus length: %d\nModulus: \n",
-		    data[10], data[11], data[12], data[13], data[14],
-		    data[15], data[16], data[17], data[22], data[23],
-		    data[24], data[25], data[26], data[27], data[28],
-		    data[29], data[30], data[31], data[32], data[33],
-		    be32_to_cpu(*((__be32 *) (data + 34))));
+		    "Algorithm: %02X %02X %02X %02X\n"
+		    "Encscheme: %02X %02X\n"
+		    "Sigscheme: %02X %02X\n"
+		    "Parameters: %02X %02X %02X %02X "
+		    "%02X %02X %02X %02X "
+		    "%02X %02X %02X %02X\n"
+		    "Modulus length: %d\n"
+		    "Modulus:\n",
+		    data[0], data[1], data[2], data[3],
+		    data[4], data[5],
+		    data[6], data[7],
+		    data[12], data[13], data[14], data[15],
+		    data[16], data[17], data[18], data[19],
+		    data[20], data[21], data[22], data[23],
+		    be32_to_cpu(*((__be32 *) (data + 24))));
 
 	for (i = 0; i < 256; i++) {
-		str += sprintf(str, "%02X ", data[i + 38]);
+		str += sprintf(str, "%02X ", data[i + 28]);
 		if ((i + 1) % 16 == 0)
 			str += sprintf(str, "\n");
 	}
@@ -937,6 +958,35 @@
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
 
+ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d %d %d [%s]\n",
+		       jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+		       jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+		       jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
+		       chip->vendor.duration_adjusted
+		       ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_durations);
+
+ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d %d %d %d [%s]\n",
+		       jiffies_to_usecs(chip->vendor.timeout_a),
+		       jiffies_to_usecs(chip->vendor.timeout_b),
+		       jiffies_to_usecs(chip->vendor.timeout_c),
+		       jiffies_to_usecs(chip->vendor.timeout_d),
+		       chip->vendor.timeout_adjusted
+		       ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_timeouts);
+
 ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
 			const char *buf, size_t count)
 {
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 72ddb03..9c4163c 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -56,6 +56,10 @@
 				char *);
 extern ssize_t tpm_show_temp_deactivated(struct device *,
 					 struct device_attribute *attr, char *);
+extern ssize_t tpm_show_durations(struct device *,
+				  struct device_attribute *attr, char *);
+extern ssize_t tpm_show_timeouts(struct device *,
+				 struct device_attribute *attr, char *);
 
 struct tpm_chip;
 
@@ -67,6 +71,7 @@
 	unsigned long base;		/* TPM base address */
 
 	int irq;
+	int probed_irq;
 
 	int region_size;
 	int have_region;
@@ -81,7 +86,9 @@
 	struct list_head list;
 	int locality;
 	unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+	bool timeout_adjusted;
 	unsigned long duration[3]; /* jiffies */
+	bool duration_adjusted;
 
 	wait_queue_head_t read_queue;
 	wait_queue_head_t int_queue;
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index a605cb7..82facc9 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -330,12 +330,12 @@
 	pdev->dev.driver = &nsc_drv.driver;
 	pdev->dev.release = tpm_nsc_remove;
 
-	if ((rc = platform_device_register(pdev)) < 0)
-		goto err_free_dev;
+	if ((rc = platform_device_add(pdev)) < 0)
+		goto err_put_dev;
 
 	if (request_region(base, 2, "tpm_nsc0") == NULL ) {
 		rc = -EBUSY;
-		goto err_unreg_dev;
+		goto err_del_dev;
 	}
 
 	if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
@@ -382,10 +382,10 @@
 
 err_rel_reg:
 	release_region(base, 2);
-err_unreg_dev:
-	platform_device_unregister(pdev);
-err_free_dev:
-	kfree(pdev);
+err_del_dev:
+	platform_device_del(pdev);
+err_put_dev:
+	platform_device_put(pdev);
 err_unreg_drv:
 	platform_driver_unregister(&nsc_drv);
 	return rc;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index dd21df5..7fc2f10 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/acpi.h>
+#include <linux/freezer.h>
 #include "tpm.h"
 
 #define TPM_HEADER_SIZE 10
@@ -79,7 +80,7 @@
 static LIST_HEAD(tis_chips);
 static DEFINE_SPINLOCK(tis_lock);
 
-#ifdef CONFIG_ACPI
+#ifdef CONFIG_PNP
 static int is_itpm(struct pnp_dev *dev)
 {
 	struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -92,11 +93,6 @@
 
 	return 0;
 }
-#else
-static int is_itpm(struct pnp_dev *dev)
-{
-	return 0;
-}
 #endif
 
 static int check_locality(struct tpm_chip *chip, int l)
@@ -120,7 +116,7 @@
 
 static int request_locality(struct tpm_chip *chip, int l)
 {
-	unsigned long stop;
+	unsigned long stop, timeout;
 	long rc;
 
 	if (check_locality(chip, l) >= 0)
@@ -129,17 +125,25 @@
 	iowrite8(TPM_ACCESS_REQUEST_USE,
 		 chip->vendor.iobase + TPM_ACCESS(l));
 
+	stop = jiffies + chip->vendor.timeout_a;
+
 	if (chip->vendor.irq) {
+again:
+		timeout = stop - jiffies;
+		if ((long)timeout <= 0)
+			return -1;
 		rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
 						      (check_locality
 						       (chip, l) >= 0),
-						      chip->vendor.timeout_a);
+						      timeout);
 		if (rc > 0)
 			return l;
-
+		if (rc == -ERESTARTSYS && freezing(current)) {
+			clear_thread_flag(TIF_SIGPENDING);
+			goto again;
+		}
 	} else {
 		/* wait for burstcount */
-		stop = jiffies + chip->vendor.timeout_a;
 		do {
 			if (check_locality(chip, l) >= 0)
 				return l;
@@ -196,15 +200,24 @@
 	if ((status & mask) == mask)
 		return 0;
 
+	stop = jiffies + timeout;
+
 	if (chip->vendor.irq) {
+again:
+		timeout = stop - jiffies;
+		if ((long)timeout <= 0)
+			return -ETIME;
 		rc = wait_event_interruptible_timeout(*queue,
 						      ((tpm_tis_status
 							(chip) & mask) ==
 						       mask), timeout);
 		if (rc > 0)
 			return 0;
+		if (rc == -ERESTARTSYS && freezing(current)) {
+			clear_thread_flag(TIF_SIGPENDING);
+			goto again;
+		}
 	} else {
-		stop = jiffies + timeout;
 		do {
 			msleep(TPM_TIMEOUT);
 			status = tpm_tis_status(chip);
@@ -288,11 +301,10 @@
  * tpm.c can skip polling for the data to be available as the interrupt is
  * waited for here
  */
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
 {
 	int rc, status, burstcnt;
 	size_t count = 0;
-	u32 ordinal;
 
 	if (request_locality(chip, 0) < 0)
 		return -EBUSY;
@@ -327,8 +339,7 @@
 
 	/* write last byte */
 	iowrite8(buf[count],
-		 chip->vendor.iobase +
-		 TPM_DATA_FIFO(chip->vendor.locality));
+		 chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
 	wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
 		      &chip->vendor.int_queue);
 	status = tpm_tis_status(chip);
@@ -337,6 +348,28 @@
 		goto out_err;
 	}
 
+	return 0;
+
+out_err:
+	tpm_tis_ready(chip);
+	release_locality(chip, chip->vendor.locality, 0);
+	return rc;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+	int rc;
+	u32 ordinal;
+
+	rc = tpm_tis_send_data(chip, buf, len);
+	if (rc < 0)
+		return rc;
+
 	/* go and do it */
 	iowrite8(TPM_STS_GO,
 		 chip->vendor.iobase + TPM_STS(chip->vendor.locality));
@@ -358,6 +391,47 @@
 	return rc;
 }
 
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+	int rc = 0;
+	u8 cmd_getticks[] = {
+		0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+		0x00, 0x00, 0x00, 0xf1
+	};
+	size_t len = sizeof(cmd_getticks);
+	int rem_itpm = itpm;
+
+	itpm = 0;
+
+	rc = tpm_tis_send_data(chip, cmd_getticks, len);
+	if (rc == 0)
+		goto out;
+
+	tpm_tis_ready(chip);
+	release_locality(chip, chip->vendor.locality, 0);
+
+	itpm = 1;
+
+	rc = tpm_tis_send_data(chip, cmd_getticks, len);
+	if (rc == 0) {
+		dev_info(chip->dev, "Detected an iTPM.\n");
+		rc = 1;
+	} else
+		rc = -EFAULT;
+
+out:
+	itpm = rem_itpm;
+	tpm_tis_ready(chip);
+	release_locality(chip, chip->vendor.locality, 0);
+
+	return rc;
+}
+
 static const struct file_operations tis_ops = {
 	.owner = THIS_MODULE,
 	.llseek = no_llseek,
@@ -376,6 +450,8 @@
 		   NULL);
 static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
 
 static struct attribute *tis_attrs[] = {
 	&dev_attr_pubek.attr,
@@ -385,7 +461,9 @@
 	&dev_attr_owned.attr,
 	&dev_attr_temp_deactivated.attr,
 	&dev_attr_caps.attr,
-	&dev_attr_cancel.attr, NULL,
+	&dev_attr_cancel.attr,
+	&dev_attr_durations.attr,
+	&dev_attr_timeouts.attr, NULL,
 };
 
 static struct attribute_group tis_attr_grp = {
@@ -416,7 +494,7 @@
 	if (interrupt == 0)
 		return IRQ_NONE;
 
-	chip->vendor.irq = irq;
+	chip->vendor.probed_irq = irq;
 
 	/* Clear interrupts handled with TPM_EOI */
 	iowrite32(interrupt,
@@ -464,7 +542,7 @@
 			resource_size_t len, unsigned int irq)
 {
 	u32 vendor, intfcaps, intmask;
-	int rc, i;
+	int rc, i, irq_s, irq_e;
 	struct tpm_chip *chip;
 
 	if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
@@ -493,6 +571,14 @@
 		 "1.2 TPM (device-id 0x%X, rev-id %d)\n",
 		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
+	if (!itpm) {
+		itpm = probe_itpm(chip);
+		if (itpm < 0) {
+			rc = -ENODEV;
+			goto out_err;
+		}
+	}
+
 	if (itpm)
 		dev_info(dev, "Intel iTPM workaround enabled\n");
 
@@ -522,6 +608,9 @@
 	if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
 		dev_dbg(dev, "\tData Avail Int Support\n");
 
+	/* get the timeouts before testing for irqs */
+	tpm_get_timeouts(chip);
+
 	/* INTERRUPT Setup */
 	init_waitqueue_head(&chip->vendor.read_queue);
 	init_waitqueue_head(&chip->vendor.int_queue);
@@ -540,13 +629,19 @@
 	if (interrupts)
 		chip->vendor.irq = irq;
 	if (interrupts && !chip->vendor.irq) {
-		chip->vendor.irq =
+		irq_s =
 		    ioread8(chip->vendor.iobase +
 			    TPM_INT_VECTOR(chip->vendor.locality));
+		if (irq_s) {
+			irq_e = irq_s;
+		} else {
+			irq_s = 3;
+			irq_e = 15;
+		}
 
-		for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+		for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
 			iowrite8(i, chip->vendor.iobase +
-				    TPM_INT_VECTOR(chip->vendor.locality));
+				 TPM_INT_VECTOR(chip->vendor.locality));
 			if (request_irq
 			    (i, tis_int_probe, IRQF_SHARED,
 			     chip->vendor.miscdev.name, chip) != 0) {
@@ -568,9 +663,22 @@
 				  chip->vendor.iobase +
 				  TPM_INT_ENABLE(chip->vendor.locality));
 
+			chip->vendor.probed_irq = 0;
+
 			/* Generate Interrupts */
 			tpm_gen_interrupt(chip);
 
+			chip->vendor.irq = chip->vendor.probed_irq;
+
+			/* free_irq will call into tis_int_probe;
+			   clear all irqs we haven't seen while doing
+			   tpm_gen_interrupt */
+			iowrite32(ioread32
+				  (chip->vendor.iobase +
+				   TPM_INT_STATUS(chip->vendor.locality)),
+				  chip->vendor.iobase +
+				  TPM_INT_STATUS(chip->vendor.locality));
+
 			/* Turn off */
 			iowrite32(intmask,
 				  chip->vendor.iobase +
@@ -609,7 +717,6 @@
 	list_add(&chip->vendor.list, &tis_chips);
 	spin_unlock(&tis_lock);
 
-	tpm_get_timeouts(chip);
 	tpm_continue_selftest(chip);
 
 	return 0;
@@ -619,6 +726,29 @@
 	tpm_remove_hardware(chip->dev);
 	return rc;
 }
+
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+	u32 intmask;
+
+	/* reenable interrupts that device may have lost or
+	   BIOS/firmware may have disabled */
+	iowrite8(chip->vendor.irq, chip->vendor.iobase +
+		 TPM_INT_VECTOR(chip->vendor.locality));
+
+	intmask =
+	    ioread32(chip->vendor.iobase +
+		     TPM_INT_ENABLE(chip->vendor.locality));
+
+	intmask |= TPM_INTF_CMD_READY_INT
+	    | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+	    | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+	iowrite32(intmask,
+		  chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+}
+
+
 #ifdef CONFIG_PNP
 static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
 				      const struct pnp_device_id *pnp_id)
@@ -650,6 +780,9 @@
 	struct tpm_chip *chip = pnp_get_drvdata(dev);
 	int ret;
 
+	if (chip->vendor.irq)
+		tpm_tis_reenable_interrupts(chip);
+
 	ret = tpm_pm_resume(&dev->dev);
 	if (!ret)
 		tpm_continue_selftest(chip);
@@ -702,6 +835,11 @@
 
 static int tpm_tis_resume(struct platform_device *dev)
 {
+	struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
+
+	if (chip->vendor.irq)
+		tpm_tis_reenable_interrupts(chip);
+
 	return tpm_pm_resume(&dev->dev);
 }
 static struct platform_driver tis_drv = {
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4168c88..3530927 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -2,3 +2,6 @@
 config CLKDEV_LOOKUP
 	bool
 	select HAVE_CLK
+
+config HAVE_MACH_CLKDEV
+	bool
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 0debc17..3ee1fdb 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -29,7 +29,8 @@
 #include <linux/connector.h>
 #include <linux/gfp.h>
 #include <linux/ptrace.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
+
 #include <asm/unaligned.h>
 
 #include <linux/cn_proc.h>
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 0a5bea9..987a165 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1199,6 +1199,26 @@
 }
 EXPORT_SYMBOL(cpufreq_quick_get);
 
+/**
+ * cpufreq_quick_get_max - get the max reported CPU frequency for this CPU
+ * @cpu: CPU number
+ *
+ * Just return the max possible frequency for a given CPU.
+ */
+unsigned int cpufreq_quick_get_max(unsigned int cpu)
+{
+	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+	unsigned int ret_freq = 0;
+
+	if (policy) {
+		ret_freq = policy->max;
+		cpufreq_cpu_put(policy);
+	}
+
+	return ret_freq;
+}
+EXPORT_SYMBOL(cpufreq_quick_get_max);
+
 
 static unsigned int __cpufreq_get(unsigned int cpu)
 {
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 2e5b204..d0183dd 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1,6 +1,6 @@
 /* n2_core.c: Niagara2 Stream Processing Unit (SPU) crypto support.
  *
- * Copyright (C) 2010 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2010, 2011 David S. Miller <davem@davemloft.net>
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -31,8 +31,8 @@
 #include "n2_core.h"
 
 #define DRV_MODULE_NAME		"n2_crypto"
-#define DRV_MODULE_VERSION	"0.1"
-#define DRV_MODULE_RELDATE	"April 29, 2010"
+#define DRV_MODULE_VERSION	"0.2"
+#define DRV_MODULE_RELDATE	"July 28, 2011"
 
 static char version[] __devinitdata =
 	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -1823,22 +1823,17 @@
 static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node,
 				   struct spu_mdesc_info *ip)
 {
-	const u64 *intr, *ino;
-	int intr_len, ino_len;
+	const u64 *ino;
+	int ino_len;
 	int i;
 
-	intr = mdesc_get_property(mdesc, node, "intr", &intr_len);
-	if (!intr)
-		return -ENODEV;
-
 	ino = mdesc_get_property(mdesc, node, "ino", &ino_len);
-	if (!ino)
+	if (!ino) {
+		printk("NO 'ino'\n");
 		return -ENODEV;
+	}
 
-	if (intr_len != ino_len)
-		return -EINVAL;
-
-	ip->num_intrs = intr_len / sizeof(u64);
+	ip->num_intrs = ino_len / sizeof(u64);
 	ip->ino_table = kzalloc((sizeof(struct ino_blob) *
 				 ip->num_intrs),
 				GFP_KERNEL);
@@ -1847,7 +1842,7 @@
 
 	for (i = 0; i < ip->num_intrs; i++) {
 		struct ino_blob *b = &ip->ino_table[i];
-		b->intr = intr[i];
+		b->intr = i + 1;
 		b->ino = ino[i];
 	}
 
@@ -2204,6 +2199,10 @@
 		.name = "n2cp",
 		.compatible = "SUNW,vf-cwq",
 	},
+	{
+		.name = "n2cp",
+		.compatible = "SUNW,kt-cwq",
+	},
 	{},
 };
 
@@ -2228,6 +2227,10 @@
 		.name = "ncp",
 		.compatible = "SUNW,vf-mau",
 	},
+	{
+		.name = "ncp",
+		.compatible = "SUNW,kt-mau",
+	},
 	{},
 };
 
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index b6d1455..1eb60de 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -32,6 +32,8 @@
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <asm/irq.h>
 #include <mach/sdma.h>
@@ -65,8 +67,8 @@
 #define SDMA_ONCE_RTB		0x060
 #define SDMA_XTRIG_CONF1	0x070
 #define SDMA_XTRIG_CONF2	0x074
-#define SDMA_CHNENBL0_V2	0x200
-#define SDMA_CHNENBL0_V1	0x080
+#define SDMA_CHNENBL0_IMX35	0x200
+#define SDMA_CHNENBL0_IMX31	0x080
 #define SDMA_CHNPRI_0		0x100
 
 /*
@@ -299,13 +301,18 @@
 	u32	ram_code_size;
 };
 
+enum sdma_devtype {
+	IMX31_SDMA,	/* runs on i.mx31 */
+	IMX35_SDMA,	/* runs on i.mx35 and later */
+};
+
 struct sdma_engine {
 	struct device			*dev;
 	struct device_dma_parameters	dma_parms;
 	struct sdma_channel		channel[MAX_DMA_CHANNELS];
 	struct sdma_channel_control	*channel_control;
 	void __iomem			*regs;
-	unsigned int			version;
+	enum sdma_devtype		devtype;
 	unsigned int			num_events;
 	struct sdma_context_data	*context;
 	dma_addr_t			context_phys;
@@ -314,6 +321,26 @@
 	struct sdma_script_start_addrs	*script_addrs;
 };
 
+static struct platform_device_id sdma_devtypes[] = {
+	{
+		.name = "imx31-sdma",
+		.driver_data = IMX31_SDMA,
+	}, {
+		.name = "imx35-sdma",
+		.driver_data = IMX35_SDMA,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, sdma_devtypes);
+
+static const struct of_device_id sdma_dt_ids[] = {
+	{ .compatible = "fsl,imx31-sdma", .data = &sdma_devtypes[IMX31_SDMA], },
+	{ .compatible = "fsl,imx35-sdma", .data = &sdma_devtypes[IMX35_SDMA], },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdma_dt_ids);
+
 #define SDMA_H_CONFIG_DSPDMA	(1 << 12) /* indicates if the DSPDMA is used */
 #define SDMA_H_CONFIG_RTD_PINS	(1 << 11) /* indicates if Real-Time Debug pins are enabled */
 #define SDMA_H_CONFIG_ACR	(1 << 4)  /* indicates if AHB freq /core freq = 2 or 1 */
@@ -321,8 +348,8 @@
 
 static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event)
 {
-	u32 chnenbl0 = (sdma->version == 2 ? SDMA_CHNENBL0_V2 : SDMA_CHNENBL0_V1);
-
+	u32 chnenbl0 = (sdma->devtype == IMX31_SDMA ? SDMA_CHNENBL0_IMX31 :
+						      SDMA_CHNENBL0_IMX35);
 	return chnenbl0 + event * 4;
 }
 
@@ -1105,25 +1132,17 @@
 }
 
 static int __init sdma_get_firmware(struct sdma_engine *sdma,
-		const char *cpu_name, int to_version)
+		const char *fw_name)
 {
 	const struct firmware *fw;
-	char *fwname;
 	const struct sdma_firmware_header *header;
 	int ret;
 	const struct sdma_script_start_addrs *addr;
 	unsigned short *ram_code;
 
-	fwname = kasprintf(GFP_KERNEL, "sdma-%s-to%d.bin", cpu_name, to_version);
-	if (!fwname)
-		return -ENOMEM;
-
-	ret = request_firmware(&fw, fwname, sdma->dev);
-	if (ret) {
-		kfree(fwname);
+	ret = request_firmware(&fw, fw_name, sdma->dev);
+	if (ret)
 		return ret;
-	}
-	kfree(fwname);
 
 	if (fw->size < sizeof(*header))
 		goto err_firmware;
@@ -1162,15 +1181,16 @@
 	int i, ret;
 	dma_addr_t ccb_phys;
 
-	switch (sdma->version) {
-	case 1:
+	switch (sdma->devtype) {
+	case IMX31_SDMA:
 		sdma->num_events = 32;
 		break;
-	case 2:
+	case IMX35_SDMA:
 		sdma->num_events = 48;
 		break;
 	default:
-		dev_err(sdma->dev, "Unknown version %d. aborting\n", sdma->version);
+		dev_err(sdma->dev, "Unknown sdma type %d. aborting\n",
+			sdma->devtype);
 		return -ENODEV;
 	}
 
@@ -1239,6 +1259,10 @@
 
 static int __init sdma_probe(struct platform_device *pdev)
 {
+	const struct of_device_id *of_id =
+			of_match_device(sdma_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
+	const char *fw_name;
 	int ret;
 	int irq;
 	struct resource *iores;
@@ -1254,7 +1278,7 @@
 
 	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	irq = platform_get_irq(pdev, 0);
-	if (!iores || irq < 0 || !pdata) {
+	if (!iores || irq < 0) {
 		ret = -EINVAL;
 		goto err_irq;
 	}
@@ -1284,7 +1308,9 @@
 	if (!sdma->script_addrs)
 		goto err_alloc;
 
-	sdma->version = pdata->sdma_version;
+	if (of_id)
+		pdev->id_entry = of_id->data;
+	sdma->devtype = pdev->id_entry->driver_data;
 
 	dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask);
 	dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask);
@@ -1314,10 +1340,30 @@
 	if (ret)
 		goto err_init;
 
-	if (pdata->script_addrs)
+	if (pdata && pdata->script_addrs)
 		sdma_add_scripts(sdma, pdata->script_addrs);
 
-	sdma_get_firmware(sdma, pdata->cpu_name, pdata->to_version);
+	if (pdata) {
+		sdma_get_firmware(sdma, pdata->fw_name);
+	} else {
+		/*
+		 * Because that device tree does not encode ROM script address,
+		 * the RAM script in firmware is mandatory for device tree
+		 * probe, otherwise it fails.
+		 */
+		ret = of_property_read_string(np, "fsl,sdma-ram-script-name",
+					      &fw_name);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get firmware name\n");
+			goto err_init;
+		}
+
+		ret = sdma_get_firmware(sdma, fw_name);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get firmware\n");
+			goto err_init;
+		}
+	}
 
 	sdma->dma_device.dev = &pdev->dev;
 
@@ -1365,7 +1411,9 @@
 static struct platform_driver sdma_driver = {
 	.driver		= {
 		.name	= "imx-sdma",
+		.of_match_table = sdma_dt_ids,
 	},
+	.id_table	= sdma_devtypes,
 	.remove		= __exit_p(sdma_remove),
 };
 
diff --git a/drivers/edac/edac_stub.c b/drivers/edac/edac_stub.c
index aab9707..86ad2ee 100644
--- a/drivers/edac/edac_stub.c
+++ b/drivers/edac/edac_stub.c
@@ -14,7 +14,7 @@
  */
 #include <linux/module.h>
 #include <linux/edac.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/edac.h>
 
 int edac_op_state = EDAC_OPSTATE_INVAL;
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 11e1a5d..8af8e86 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -854,11 +854,11 @@
 		mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
 
 	if (err_detect & DDR_EDE_SBE)
-		edac_mc_handle_ce(mci, pfn, err_addr & PAGE_MASK,
+		edac_mc_handle_ce(mci, pfn, err_addr & ~PAGE_MASK,
 				  syndrome, row_index, 0, mci->ctl_name);
 
 	if (err_detect & DDR_EDE_MBE)
-		edac_mc_handle_ue(mci, pfn, err_addr & PAGE_MASK,
+		edac_mc_handle_ue(mci, pfn, err_addr & ~PAGE_MASK,
 				  row_index, mci->ctl_name);
 
 	out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 29d2423..85661b0 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -32,7 +32,7 @@
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 #include "core.h"
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index 95a4714..8ba7f79 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -38,7 +38,7 @@
 #include <linux/string.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 #include <asm/system.h>
 
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 193ed92..94d3b49 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -29,7 +29,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 #include <asm/system.h>
 
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 0fe4e4e..b45be57 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -9,7 +9,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct device;
 struct fw_card;
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c
index 0618145..763626b 100644
--- a/drivers/firewire/nosy.c
+++ b/drivers/firewire/nosy.c
@@ -37,7 +37,7 @@
 #include <linux/uaccess.h>
 #include <linux/wait.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 #include "nosy.h"
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 9236965..f88a9b2 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -560,6 +560,11 @@
 			mode_changed = true;
 		} else if (set->fb == NULL) {
 			mode_changed = true;
+		} else if (set->fb->depth != set->crtc->fb->depth) {
+			mode_changed = true;
+		} else if (set->fb->bits_per_pixel !=
+			   set->crtc->fb->bits_per_pixel) {
+			mode_changed = true;
 		} else
 			fb_changed = true;
 	}
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 0929219..756af4d 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -185,8 +185,8 @@
 bad:
 	if (raw_edid) {
 		printk(KERN_ERR "Raw EDID:\n");
-		print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH);
-		printk(KERN_ERR "\n");
+		print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
+			       raw_edid, EDID_LENGTH, false);
 	}
 	return 0;
 }
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4012fe4..186d62e 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -129,7 +129,7 @@
 }
 
 /**
- * Initialize an already allocate GEM object of the specified size with
+ * Initialize an already allocated GEM object of the specified size with
  * shmfs backing store.
  */
 int drm_gem_object_init(struct drm_device *dev,
@@ -151,6 +151,27 @@
 EXPORT_SYMBOL(drm_gem_object_init);
 
 /**
+ * Initialize an already allocated GEM object of the specified size with
+ * no GEM provided backing store. Instead the caller is responsible for
+ * backing the object and handling it.
+ */
+int drm_gem_private_object_init(struct drm_device *dev,
+			struct drm_gem_object *obj, size_t size)
+{
+	BUG_ON((size & (PAGE_SIZE - 1)) != 0);
+
+	obj->dev = dev;
+	obj->filp = NULL;
+
+	kref_init(&obj->refcount);
+	atomic_set(&obj->handle_count, 0);
+	obj->size = size;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_gem_private_object_init);
+
+/**
  * Allocate a GEM object of the specified size with shmfs backing store
  */
 struct drm_gem_object *
@@ -211,6 +232,8 @@
 	idr_remove(&filp->object_idr, handle);
 	spin_unlock(&filp->table_lock);
 
+	if (dev->driver->gem_close_object)
+		dev->driver->gem_close_object(obj, filp);
 	drm_gem_object_handle_unreference_unlocked(obj);
 
 	return 0;
@@ -227,7 +250,8 @@
 		       struct drm_gem_object *obj,
 		       u32 *handlep)
 {
-	int	ret;
+	struct drm_device *dev = obj->dev;
+	int ret;
 
 	/*
 	 * Get the user-visible handle using idr.
@@ -248,6 +272,15 @@
 		return ret;
 
 	drm_gem_object_handle_reference(obj);
+
+	if (dev->driver->gem_open_object) {
+		ret = dev->driver->gem_open_object(obj, file_priv);
+		if (ret) {
+			drm_gem_handle_delete(file_priv, *handlep);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL(drm_gem_handle_create);
@@ -402,7 +435,12 @@
 static int
 drm_gem_object_release_handle(int id, void *ptr, void *data)
 {
+	struct drm_file *file_priv = data;
 	struct drm_gem_object *obj = ptr;
+	struct drm_device *dev = obj->dev;
+
+	if (dev->driver->gem_close_object)
+		dev->driver->gem_close_object(obj, file_priv);
 
 	drm_gem_object_handle_unreference_unlocked(obj);
 
@@ -418,7 +456,7 @@
 drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
 {
 	idr_for_each(&file_private->object_idr,
-		     &drm_gem_object_release_handle, NULL);
+		     &drm_gem_object_release_handle, file_private);
 
 	idr_remove_all(&file_private->object_idr);
 	idr_destroy(&file_private->object_idr);
@@ -427,7 +465,8 @@
 void
 drm_gem_object_release(struct drm_gem_object *obj)
 {
-	fput(obj->filp);
+	if (obj->filp)
+	    fput(obj->filp);
 }
 EXPORT_SYMBOL(drm_gem_object_release);
 
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index c2d32f2..ad74fb4 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -994,9 +994,10 @@
 {
 	const char *name;
 	unsigned int namelen;
-	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
+	bool res_specified = false, bpp_specified = false, refresh_specified = false;
 	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
-	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
+	bool yres_specified = false, cvt = false, rb = false;
+	bool interlace = false, margins = false, was_digit = false;
 	int i;
 	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
 
@@ -1015,54 +1016,65 @@
 	for (i = namelen-1; i >= 0; i--) {
 		switch (name[i]) {
 		case '@':
-			namelen = i;
 			if (!refresh_specified && !bpp_specified &&
-			    !yres_specified) {
+			    !yres_specified && !cvt && !rb && was_digit) {
 				refresh = simple_strtol(&name[i+1], NULL, 10);
-				refresh_specified = 1;
-				if (cvt || rb)
-					cvt = 0;
+				refresh_specified = true;
+				was_digit = false;
 			} else
 				goto done;
 			break;
 		case '-':
-			namelen = i;
-			if (!bpp_specified && !yres_specified) {
+			if (!bpp_specified && !yres_specified && !cvt &&
+			    !rb && was_digit) {
 				bpp = simple_strtol(&name[i+1], NULL, 10);
-				bpp_specified = 1;
-				if (cvt || rb)
-					cvt = 0;
+				bpp_specified = true;
+				was_digit = false;
 			} else
 				goto done;
 			break;
 		case 'x':
-			if (!yres_specified) {
+			if (!yres_specified && was_digit) {
 				yres = simple_strtol(&name[i+1], NULL, 10);
-				yres_specified = 1;
+				yres_specified = true;
+				was_digit = false;
 			} else
 				goto done;
 		case '0' ... '9':
+			was_digit = true;
 			break;
 		case 'M':
-			if (!yres_specified)
-				cvt = 1;
+			if (yres_specified || cvt || was_digit)
+				goto done;
+			cvt = true;
 			break;
 		case 'R':
-			if (cvt)
-				rb = 1;
+			if (yres_specified || cvt || rb || was_digit)
+				goto done;
+			rb = true;
 			break;
 		case 'm':
-			if (!cvt)
-				margins = 1;
+			if (cvt || yres_specified || was_digit)
+				goto done;
+			margins = true;
 			break;
 		case 'i':
-			if (!cvt)
-				interlace = 1;
+			if (cvt || yres_specified || was_digit)
+				goto done;
+			interlace = true;
 			break;
 		case 'e':
+			if (yres_specified || bpp_specified || refresh_specified ||
+			    was_digit || (force != DRM_FORCE_UNSPECIFIED))
+				goto done;
+
 			force = DRM_FORCE_ON;
 			break;
 		case 'D':
+			if (yres_specified || bpp_specified || refresh_specified ||
+			    was_digit || (force != DRM_FORCE_UNSPECIFIED))
+				goto done;
+
 			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
 			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
 				force = DRM_FORCE_ON;
@@ -1070,17 +1082,37 @@
 				force = DRM_FORCE_ON_DIGITAL;
 			break;
 		case 'd':
+			if (yres_specified || bpp_specified || refresh_specified ||
+			    was_digit || (force != DRM_FORCE_UNSPECIFIED))
+				goto done;
+
 			force = DRM_FORCE_OFF;
 			break;
 		default:
 			goto done;
 		}
 	}
+
 	if (i < 0 && yres_specified) {
-		xres = simple_strtol(name, NULL, 10);
-		res_specified = 1;
+		char *ch;
+		xres = simple_strtol(name, &ch, 10);
+		if ((ch != NULL) && (*ch == 'x'))
+			res_specified = true;
+		else
+			i = ch - name;
+	} else if (!yres_specified && was_digit) {
+		/* catch mode that begins with digits but has no 'x' */
+		i = 0;
 	}
 done:
+	if (i >= 0) {
+		printk(KERN_WARNING
+			"parse error at position %i in video mode '%s'\n",
+			i, name);
+		mode->specified = false;
+		return false;
+	}
+
 	if (res_specified) {
 		mode->specified = true;
 		mode->xres = xres;
@@ -1096,9 +1128,10 @@
 		mode->bpp_specified = true;
 		mode->bpp = bpp;
 	}
-	mode->rb = rb ? true : false;
-	mode->cvt = cvt  ? true : false;
-	mode->interlace = interlace ? true : false;
+	mode->rb = rb;
+	mode->cvt = cvt;
+	mode->interlace = interlace;
+	mode->margins = margins;
 	mode->force = force;
 
 	return true;
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 7223f06..2a8b626 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -123,14 +123,15 @@
 {
 	int len, ret;
 
-	master->unique_len = 10 + strlen(dev->platformdev->name);
+	master->unique_len = 13 + strlen(dev->platformdev->name);
+	master->unique_size = master->unique_len;
 	master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
 
 	if (master->unique == NULL)
 		return -ENOMEM;
 
 	len = snprintf(master->unique, master->unique_len,
-		       "platform:%s", dev->platformdev->name);
+			"platform:%s:%02d", dev->platformdev->name, dev->platformdev->id);
 
 	if (len > master->unique_len) {
 		DRM_ERROR("Unique buffer overflowed\n");
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 0a893f7..e2662497 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -865,7 +865,7 @@
 			   MEMSTAT_VID_SHIFT);
 		seq_printf(m, "Current P-state: %d\n",
 			   (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
-	} else if (IS_GEN6(dev)) {
+	} else if (IS_GEN6(dev) || IS_GEN7(dev)) {
 		u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
 		u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
 		u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
@@ -1123,6 +1123,44 @@
 	return 0;
 }
 
+static int i915_ring_freq_table(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int ret;
+	int gpu_freq, ia_freq;
+
+	if (!(IS_GEN6(dev) || IS_GEN7(dev))) {
+		seq_printf(m, "unsupported on this chipset\n");
+		return 0;
+	}
+
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		return ret;
+
+	seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");
+
+	for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
+	     gpu_freq++) {
+		I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
+		I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
+			   GEN6_PCODE_READ_MIN_FREQ_TABLE);
+		if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
+			      GEN6_PCODE_READY) == 0, 10)) {
+			DRM_ERROR("pcode read of freq table timed out\n");
+			continue;
+		}
+		ia_freq = I915_READ(GEN6_PCODE_DATA);
+		seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
 static int i915_gfxec(struct seq_file *m, void *unused)
 {
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -1430,6 +1468,7 @@
 	{"i915_inttoext_table", i915_inttoext_table, 0},
 	{"i915_drpc_info", i915_drpc_info, 0},
 	{"i915_emon_status", i915_emon_status, 0},
+	{"i915_ring_freq_table", i915_ring_freq_table, 0},
 	{"i915_gfxec", i915_gfxec, 0},
 	{"i915_fbc_status", i915_fbc_status, 0},
 	{"i915_sr_status", i915_sr_status, 0},
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 296fbd6..1271282 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1073,6 +1073,9 @@
 	unsigned long cfb_base;
 	unsigned long ll_base = 0;
 
+	/* Just in case the BIOS is doing something questionable. */
+	intel_disable_fbc(dev);
+
 	compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
 	if (compressed_fb)
 		compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
@@ -1099,7 +1102,6 @@
 
 	dev_priv->cfb_size = size;
 
-	intel_disable_fbc(dev);
 	dev_priv->compressed_fb = compressed_fb;
 	if (HAS_PCH_SPLIT(dev))
 		I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index eb91e2d..ce045a8 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -37,38 +37,70 @@
 #include <linux/console.h>
 #include "drm_crtc_helper.h"
 
-static int i915_modeset = -1;
+static int i915_modeset __read_mostly = -1;
 module_param_named(modeset, i915_modeset, int, 0400);
+MODULE_PARM_DESC(modeset,
+		"Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, "
+		"1=on, -1=force vga console preference [default])");
 
-unsigned int i915_fbpercrtc = 0;
+unsigned int i915_fbpercrtc __always_unused = 0;
 module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
 
-int i915_panel_ignore_lid = 0;
+int i915_panel_ignore_lid __read_mostly = 0;
 module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600);
+MODULE_PARM_DESC(panel_ignore_lid,
+		"Override lid status (0=autodetect [default], 1=lid open, "
+		"-1=lid closed)");
 
-unsigned int i915_powersave = 1;
+unsigned int i915_powersave __read_mostly = 1;
 module_param_named(powersave, i915_powersave, int, 0600);
+MODULE_PARM_DESC(powersave,
+		"Enable powersavings, fbc, downclocking, etc. (default: true)");
 
-unsigned int i915_semaphores = 0;
+unsigned int i915_semaphores __read_mostly = 0;
 module_param_named(semaphores, i915_semaphores, int, 0600);
+MODULE_PARM_DESC(semaphores,
+		"Use semaphores for inter-ring sync (default: false)");
 
-unsigned int i915_enable_rc6 = 0;
+unsigned int i915_enable_rc6 __read_mostly = 0;
 module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600);
+MODULE_PARM_DESC(i915_enable_rc6,
+		"Enable power-saving render C-state 6 (default: true)");
 
-unsigned int i915_enable_fbc = 0;
+unsigned int i915_enable_fbc __read_mostly = 1;
 module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600);
+MODULE_PARM_DESC(i915_enable_fbc,
+		"Enable frame buffer compression for power savings "
+		"(default: false)");
 
-unsigned int i915_lvds_downclock = 0;
+unsigned int i915_lvds_downclock __read_mostly = 0;
 module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
+MODULE_PARM_DESC(lvds_downclock,
+		"Use panel (LVDS/eDP) downclocking for power savings "
+		"(default: false)");
 
-unsigned int i915_panel_use_ssc = 1;
+unsigned int i915_panel_use_ssc __read_mostly = 1;
 module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600);
+MODULE_PARM_DESC(lvds_use_ssc,
+		"Use Spread Spectrum Clock with panels [LVDS/eDP] "
+		"(default: true)");
 
-int i915_vbt_sdvo_panel_type = -1;
+int i915_vbt_sdvo_panel_type __read_mostly = -1;
 module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600);
+MODULE_PARM_DESC(vbt_sdvo_panel_type,
+		"Override selection of SDVO panel mode in the VBT "
+		"(default: auto)");
 
-static bool i915_try_reset = true;
+static bool i915_try_reset __read_mostly = true;
 module_param_named(reset, i915_try_reset, bool, 0600);
+MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)");
+
+bool i915_enable_hangcheck __read_mostly = true;
+module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644);
+MODULE_PARM_DESC(enable_hangcheck,
+		"Periodically check GPU activity for detecting hangs. "
+		"WARNING: Disabling this can cause system wide hangs. "
+		"(default: true)");
 
 static struct drm_driver driver;
 extern int intel_agp_enabled;
@@ -345,12 +377,17 @@
 
 void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
 {
-	int loop = 500;
-	u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
-	while (fifo < 20 && loop--) {
-		udelay(10);
-		fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+	if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES ) {
+		int loop = 500;
+		u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+		while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
+			udelay(10);
+			fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
+		}
+		WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES);
+		dev_priv->gt_fifo_count = fifo;
 	}
+	dev_priv->gt_fifo_count--;
 }
 
 static int i915_drm_freeze(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index ce7914c..6867e19 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -214,6 +214,8 @@
 	int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
 			  struct drm_framebuffer *fb,
 			  struct drm_i915_gem_object *obj);
+	int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+			    int x, int y);
 	/* clock updates for mode set */
 	/* cursor updates */
 	/* render clock increase/decrease */
@@ -265,6 +267,7 @@
 #define QUIRK_LVDS_SSC_DISABLE (1<<1)
 
 struct intel_fbdev;
+struct intel_fbc_work;
 
 typedef struct drm_i915_private {
 	struct drm_device *dev;
@@ -275,6 +278,7 @@
 	int relative_constants_mode;
 
 	void __iomem *regs;
+	u32 gt_fifo_count;
 
 	struct intel_gmbus {
 		struct i2c_adapter adapter;
@@ -329,11 +333,10 @@
 	uint32_t last_instdone1;
 
 	unsigned long cfb_size;
-	unsigned long cfb_pitch;
-	unsigned long cfb_offset;
-	int cfb_fence;
-	int cfb_plane;
+	unsigned int cfb_fb;
+	enum plane cfb_plane;
 	int cfb_y;
+	struct intel_fbc_work *fbc_work;
 
 	struct intel_opregion opregion;
 
@@ -986,15 +989,16 @@
 
 extern struct drm_ioctl_desc i915_ioctls[];
 extern int i915_max_ioctl;
-extern unsigned int i915_fbpercrtc;
-extern int i915_panel_ignore_lid;
-extern unsigned int i915_powersave;
-extern unsigned int i915_semaphores;
-extern unsigned int i915_lvds_downclock;
-extern unsigned int i915_panel_use_ssc;
-extern int i915_vbt_sdvo_panel_type;
-extern unsigned int i915_enable_rc6;
-extern unsigned int i915_enable_fbc;
+extern unsigned int i915_fbpercrtc __always_unused;
+extern int i915_panel_ignore_lid __read_mostly;
+extern unsigned int i915_powersave __read_mostly;
+extern unsigned int i915_semaphores __read_mostly;
+extern unsigned int i915_lvds_downclock __read_mostly;
+extern unsigned int i915_panel_use_ssc __read_mostly;
+extern int i915_vbt_sdvo_panel_type __read_mostly;
+extern unsigned int i915_enable_rc6 __read_mostly;
+extern unsigned int i915_enable_fbc __read_mostly;
+extern bool i915_enable_hangcheck __read_mostly;
 
 extern int i915_suspend(struct drm_device *dev, pm_message_t state);
 extern int i915_resume(struct drm_device *dev);
@@ -1164,7 +1168,7 @@
 int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj,
 					    uint32_t read_domains,
 					    uint32_t write_domain);
-int __must_check i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj);
+int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_init_ringbuffer(struct drm_device *dev);
 void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
 void i915_gem_do_init(struct drm_device *dev,
@@ -1183,7 +1187,8 @@
 i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
 				  bool write);
 int __must_check
-i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj,
+i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
+				     u32 alignment,
 				     struct intel_ring_buffer *pipelined);
 int i915_gem_attach_phys_object(struct drm_device *dev,
 				struct drm_i915_gem_object *obj,
@@ -1199,9 +1204,14 @@
 				    uint32_t size,
 				    int tiling_mode);
 
+int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
+				    enum i915_cache_level cache_level);
+
 /* i915_gem_gtt.c */
 void i915_gem_restore_gtt_mappings(struct drm_device *dev);
 int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj);
+void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
+				enum i915_cache_level cache_level);
 void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj);
 
 /* i915_gem_evict.c */
@@ -1283,12 +1293,8 @@
 extern void intel_modeset_gem_init(struct drm_device *dev);
 extern void intel_modeset_cleanup(struct drm_device *dev);
 extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
-extern void i8xx_disable_fbc(struct drm_device *dev);
-extern void g4x_disable_fbc(struct drm_device *dev);
-extern void ironlake_disable_fbc(struct drm_device *dev);
-extern void intel_disable_fbc(struct drm_device *dev);
-extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
 extern bool intel_fbc_enabled(struct drm_device *dev);
+extern void intel_disable_fbc(struct drm_device *dev);
 extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
 extern void ironlake_enable_rc6(struct drm_device *dev);
 extern void gen6_set_rps(struct drm_device *dev, u8 val);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index a087e1b..d1cd8b8 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1763,8 +1763,11 @@
 	ring->outstanding_lazy_request = false;
 
 	if (!dev_priv->mm.suspended) {
-		mod_timer(&dev_priv->hangcheck_timer,
-			  jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+		if (i915_enable_hangcheck) {
+			mod_timer(&dev_priv->hangcheck_timer,
+				  jiffies +
+				  msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+		}
 		if (was_empty)
 			queue_delayed_work(dev_priv->wq,
 					   &dev_priv->mm.retire_work, HZ);
@@ -2135,6 +2138,30 @@
 	return 0;
 }
 
+static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
+{
+	u32 old_write_domain, old_read_domains;
+
+	/* Act a barrier for all accesses through the GTT */
+	mb();
+
+	/* Force a pagefault for domain tracking on next user access */
+	i915_gem_release_mmap(obj);
+
+	if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
+		return;
+
+	old_read_domains = obj->base.read_domains;
+	old_write_domain = obj->base.write_domain;
+
+	obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT;
+	obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT;
+
+	trace_i915_gem_object_change_domain(obj,
+					    old_read_domains,
+					    old_write_domain);
+}
+
 /**
  * Unbinds an object from the GTT aperture.
  */
@@ -2151,23 +2178,28 @@
 		return -EINVAL;
 	}
 
-	/* blow away mappings if mapped through GTT */
-	i915_gem_release_mmap(obj);
-
-	/* Move the object to the CPU domain to ensure that
-	 * any possible CPU writes while it's not in the GTT
-	 * are flushed when we go to remap it. This will
-	 * also ensure that all pending GPU writes are finished
-	 * before we unbind.
-	 */
-	ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+	ret = i915_gem_object_finish_gpu(obj);
 	if (ret == -ERESTARTSYS)
 		return ret;
 	/* Continue on if we fail due to EIO, the GPU is hung so we
 	 * should be safe and we need to cleanup or else we might
 	 * cause memory corruption through use-after-free.
 	 */
+
+	i915_gem_object_finish_gtt(obj);
+
+	/* Move the object to the CPU domain to ensure that
+	 * any possible CPU writes while it's not in the GTT
+	 * are flushed when we go to remap it.
+	 */
+	if (ret == 0)
+		ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+	if (ret == -ERESTARTSYS)
+		return ret;
 	if (ret) {
+		/* In the event of a disaster, abandon all caches and
+		 * hope for the best.
+		 */
 		i915_gem_clflush_object(obj);
 		obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
 	}
@@ -2996,51 +3028,139 @@
 	return 0;
 }
 
-/*
- * Prepare buffer for display plane. Use uninterruptible for possible flush
- * wait, as in modesetting process we're not supposed to be interrupted.
- */
-int
-i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj,
-				     struct intel_ring_buffer *pipelined)
+int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
+				    enum i915_cache_level cache_level)
 {
-	uint32_t old_read_domains;
 	int ret;
 
-	/* Not valid to be called on unbound objects. */
-	if (obj->gtt_space == NULL)
-		return -EINVAL;
+	if (obj->cache_level == cache_level)
+		return 0;
+
+	if (obj->pin_count) {
+		DRM_DEBUG("can not change the cache level of pinned objects\n");
+		return -EBUSY;
+	}
+
+	if (obj->gtt_space) {
+		ret = i915_gem_object_finish_gpu(obj);
+		if (ret)
+			return ret;
+
+		i915_gem_object_finish_gtt(obj);
+
+		/* Before SandyBridge, you could not use tiling or fence
+		 * registers with snooped memory, so relinquish any fences
+		 * currently pointing to our region in the aperture.
+		 */
+		if (INTEL_INFO(obj->base.dev)->gen < 6) {
+			ret = i915_gem_object_put_fence(obj);
+			if (ret)
+				return ret;
+		}
+
+		i915_gem_gtt_rebind_object(obj, cache_level);
+	}
+
+	if (cache_level == I915_CACHE_NONE) {
+		u32 old_read_domains, old_write_domain;
+
+		/* If we're coming from LLC cached, then we haven't
+		 * actually been tracking whether the data is in the
+		 * CPU cache or not, since we only allow one bit set
+		 * in obj->write_domain and have been skipping the clflushes.
+		 * Just set it to the CPU cache for now.
+		 */
+		WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU);
+		WARN_ON(obj->base.read_domains & ~I915_GEM_DOMAIN_CPU);
+
+		old_read_domains = obj->base.read_domains;
+		old_write_domain = obj->base.write_domain;
+
+		obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+		obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+
+		trace_i915_gem_object_change_domain(obj,
+						    old_read_domains,
+						    old_write_domain);
+	}
+
+	obj->cache_level = cache_level;
+	return 0;
+}
+
+/*
+ * Prepare buffer for display plane (scanout, cursors, etc).
+ * Can be called from an uninterruptible phase (modesetting) and allows
+ * any flushes to be pipelined (for pageflips).
+ *
+ * For the display plane, we want to be in the GTT but out of any write
+ * domains. So in many ways this looks like set_to_gtt_domain() apart from the
+ * ability to pipeline the waits, pinning and any additional subtleties
+ * that may differentiate the display plane from ordinary buffers.
+ */
+int
+i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
+				     u32 alignment,
+				     struct intel_ring_buffer *pipelined)
+{
+	u32 old_read_domains, old_write_domain;
+	int ret;
 
 	ret = i915_gem_object_flush_gpu_write_domain(obj);
 	if (ret)
 		return ret;
 
-
-	/* Currently, we are always called from an non-interruptible context. */
 	if (pipelined != obj->ring) {
 		ret = i915_gem_object_wait_rendering(obj);
 		if (ret)
 			return ret;
 	}
 
+	/* The display engine is not coherent with the LLC cache on gen6.  As
+	 * a result, we make sure that the pinning that is about to occur is
+	 * done with uncached PTEs. This is lowest common denominator for all
+	 * chipsets.
+	 *
+	 * However for gen6+, we could do better by using the GFDT bit instead
+	 * of uncaching, which would allow us to flush all the LLC-cached data
+	 * with that bit in the PTE to main memory with just one PIPE_CONTROL.
+	 */
+	ret = i915_gem_object_set_cache_level(obj, I915_CACHE_NONE);
+	if (ret)
+		return ret;
+
+	/* As the user may map the buffer once pinned in the display plane
+	 * (e.g. libkms for the bootup splash), we have to ensure that we
+	 * always use map_and_fenceable for all scanout buffers.
+	 */
+	ret = i915_gem_object_pin(obj, alignment, true);
+	if (ret)
+		return ret;
+
 	i915_gem_object_flush_cpu_write_domain(obj);
 
+	old_write_domain = obj->base.write_domain;
 	old_read_domains = obj->base.read_domains;
+
+	/* It should now be out of any other write domains, and we can update
+	 * the domain values for our changes.
+	 */
+	BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
 	obj->base.read_domains |= I915_GEM_DOMAIN_GTT;
 
 	trace_i915_gem_object_change_domain(obj,
 					    old_read_domains,
-					    obj->base.write_domain);
+					    old_write_domain);
 
 	return 0;
 }
 
 int
-i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj)
+i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj)
 {
 	int ret;
 
-	if (!obj->active)
+	if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0)
 		return 0;
 
 	if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
@@ -3049,6 +3169,9 @@
 			return ret;
 	}
 
+	/* Ensure that we invalidate the GPU's caches and TLBs. */
+	obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
+
 	return i915_gem_object_wait_rendering(obj);
 }
 
@@ -3575,7 +3698,23 @@
 	obj->base.write_domain = I915_GEM_DOMAIN_CPU;
 	obj->base.read_domains = I915_GEM_DOMAIN_CPU;
 
-	obj->cache_level = I915_CACHE_NONE;
+	if (IS_GEN6(dev)) {
+		/* On Gen6, we can have the GPU use the LLC (the CPU
+		 * cache) for about a 10% performance improvement
+		 * compared to uncached.  Graphics requests other than
+		 * display scanout are coherent with the CPU in
+		 * accessing this cache.  This means in this mode we
+		 * don't need to clflush on the CPU side, and on the
+		 * GPU side we only need to flush internal caches to
+		 * get data visible to the CPU.
+		 *
+		 * However, we maintain the display planes as UC, and so
+		 * need to rebind when first used as such.
+		 */
+		obj->cache_level = I915_CACHE_LLC;
+	} else
+		obj->cache_level = I915_CACHE_NONE;
+
 	obj->base.driver_private = NULL;
 	obj->fence_reg = I915_FENCE_REG_NONE;
 	INIT_LIST_HEAD(&obj->mm_list);
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index e46b645..7a709cd 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -59,24 +59,8 @@
 			      (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
 
 	list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
-		unsigned int agp_type =
-			cache_level_to_agp_type(dev, obj->cache_level);
-
 		i915_gem_clflush_object(obj);
-
-		if (dev_priv->mm.gtt->needs_dmar) {
-			BUG_ON(!obj->sg_list);
-
-			intel_gtt_insert_sg_entries(obj->sg_list,
-						    obj->num_sg,
-						    obj->gtt_space->start >> PAGE_SHIFT,
-						    agp_type);
-		} else
-			intel_gtt_insert_pages(obj->gtt_space->start
-						   >> PAGE_SHIFT,
-					       obj->base.size >> PAGE_SHIFT,
-					       obj->pages,
-					       agp_type);
+		i915_gem_gtt_rebind_object(obj, obj->cache_level);
 	}
 
 	intel_gtt_chipset_flush();
@@ -110,6 +94,27 @@
 	return 0;
 }
 
+void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
+				enum i915_cache_level cache_level)
+{
+	struct drm_device *dev = obj->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	unsigned int agp_type = cache_level_to_agp_type(dev, cache_level);
+
+	if (dev_priv->mm.gtt->needs_dmar) {
+		BUG_ON(!obj->sg_list);
+
+		intel_gtt_insert_sg_entries(obj->sg_list,
+					    obj->num_sg,
+					    obj->gtt_space->start >> PAGE_SHIFT,
+					    agp_type);
+	} else
+		intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
+				       obj->base.size >> PAGE_SHIFT,
+				       obj->pages,
+				       agp_type);
+}
+
 void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
 {
 	intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT,
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 3b03f85..23d1ae6 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -361,10 +361,12 @@
 
 	ring->irq_seqno = seqno;
 	wake_up_all(&ring->irq_queue);
-
-	dev_priv->hangcheck_count = 0;
-	mod_timer(&dev_priv->hangcheck_timer,
-		  jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+	if (i915_enable_hangcheck) {
+		dev_priv->hangcheck_count = 0;
+		mod_timer(&dev_priv->hangcheck_timer,
+			  jiffies +
+			  msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+	}
 }
 
 static void gen6_pm_rps_work(struct work_struct *work)
@@ -1664,6 +1666,9 @@
 	uint32_t acthd, instdone, instdone1;
 	bool err = false;
 
+	if (!i915_enable_hangcheck)
+		return;
+
 	/* If all work is done then ACTHD clearly hasn't advanced. */
 	if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) &&
 	    i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) &&
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 5d5def7..02db299 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -579,6 +579,7 @@
 #define   DPFC_CTL_PLANEA	(0<<30)
 #define   DPFC_CTL_PLANEB	(1<<30)
 #define   DPFC_CTL_FENCE_EN	(1<<29)
+#define   DPFC_CTL_PERSISTENT_MODE	(1<<25)
 #define   DPFC_SR_EN		(1<<10)
 #define   DPFC_CTL_LIMIT_1X	(0<<6)
 #define   DPFC_CTL_LIMIT_2X	(1<<6)
@@ -3360,6 +3361,7 @@
 #define  FORCEWAKE_ACK				0x130090
 
 #define  GT_FIFO_FREE_ENTRIES			0x120008
+#define    GT_FIFO_NUM_RESERVED_ENTRIES		20
 
 #define GEN6_RPNSWREQ				0xA008
 #define   GEN6_TURBO_DISABLE			(1<<31)
@@ -3434,7 +3436,9 @@
 #define GEN6_PCODE_MAILBOX			0x138124
 #define   GEN6_PCODE_READY			(1<<31)
 #define   GEN6_READ_OC_PARAMS			0xc
-#define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE	0x9
+#define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE	0x8
+#define   GEN6_PCODE_READ_MIN_FREQ_TABLE	0x9
 #define GEN6_PCODE_DATA				0x138128
+#define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT	8
 
 #endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 5257cfc..2857586 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -760,15 +760,13 @@
 	/* FIXME: restore TV & SDVO state */
 
 	/* only restore FBC info on the platform that supports FBC*/
+	intel_disable_fbc(dev);
 	if (I915_HAS_FBC(dev)) {
 		if (HAS_PCH_SPLIT(dev)) {
-			ironlake_disable_fbc(dev);
 			I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
 		} else if (IS_GM45(dev)) {
-			g4x_disable_fbc(dev);
 			I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
 		} else {
-			i8xx_disable_fbc(dev);
 			I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE);
 			I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE);
 			I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2);
@@ -878,8 +876,10 @@
 		intel_init_emon(dev);
 	}
 
-	if (IS_GEN6(dev))
+	if (IS_GEN6(dev)) {
 		gen6_enable_rps(dev_priv);
+		gen6_update_ring_freq(dev_priv);
+	}
 
 	mutex_lock(&dev->struct_mutex);
 
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 927442a..61abef8 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -74,7 +74,7 @@
 
 static void
 fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
-			struct lvds_dvo_timing *dvo_timing)
+			const struct lvds_dvo_timing *dvo_timing)
 {
 	panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
 		dvo_timing->hactive_lo;
@@ -115,20 +115,75 @@
 	drm_mode_set_name(panel_fixed_mode);
 }
 
+static bool
+lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a,
+			   const struct lvds_dvo_timing *b)
+{
+	if (a->hactive_hi != b->hactive_hi ||
+	    a->hactive_lo != b->hactive_lo)
+		return false;
+
+	if (a->hsync_off_hi != b->hsync_off_hi ||
+	    a->hsync_off_lo != b->hsync_off_lo)
+		return false;
+
+	if (a->hsync_pulse_width != b->hsync_pulse_width)
+		return false;
+
+	if (a->hblank_hi != b->hblank_hi ||
+	    a->hblank_lo != b->hblank_lo)
+		return false;
+
+	if (a->vactive_hi != b->vactive_hi ||
+	    a->vactive_lo != b->vactive_lo)
+		return false;
+
+	if (a->vsync_off != b->vsync_off)
+		return false;
+
+	if (a->vsync_pulse_width != b->vsync_pulse_width)
+		return false;
+
+	if (a->vblank_hi != b->vblank_hi ||
+	    a->vblank_lo != b->vblank_lo)
+		return false;
+
+	return true;
+}
+
+static const struct lvds_dvo_timing *
+get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
+		    const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs,
+		    int index)
+{
+	/*
+	 * the size of fp_timing varies on the different platform.
+	 * So calculate the DVO timing relative offset in LVDS data
+	 * entry to get the DVO timing entry
+	 */
+
+	int lfp_data_size =
+		lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
+		lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
+	int dvo_timing_offset =
+		lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
+		lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
+	char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index;
+
+	return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
+}
+
 /* Try to find integrated panel data */
 static void
 parse_lfp_panel_data(struct drm_i915_private *dev_priv,
 			    struct bdb_header *bdb)
 {
-	struct bdb_lvds_options *lvds_options;
-	struct bdb_lvds_lfp_data *lvds_lfp_data;
-	struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
-	struct bdb_lvds_lfp_data_entry *entry;
-	struct lvds_dvo_timing *dvo_timing;
+	const struct bdb_lvds_options *lvds_options;
+	const struct bdb_lvds_lfp_data *lvds_lfp_data;
+	const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
+	const struct lvds_dvo_timing *panel_dvo_timing;
 	struct drm_display_mode *panel_fixed_mode;
-	int lfp_data_size, dvo_timing_offset;
-	int i, temp_downclock;
-	struct drm_display_mode *temp_mode;
+	int i, downclock;
 
 	lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
 	if (!lvds_options)
@@ -150,75 +205,44 @@
 
 	dev_priv->lvds_vbt = 1;
 
-	lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
-		lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
-	entry = (struct bdb_lvds_lfp_data_entry *)
-		((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
-						   lvds_options->panel_type));
-	dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
-		lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
-
-	/*
-	 * the size of fp_timing varies on the different platform.
-	 * So calculate the DVO timing relative offset in LVDS data
-	 * entry to get the DVO timing entry
-	 */
-	dvo_timing = (struct lvds_dvo_timing *)
-			((unsigned char *)entry + dvo_timing_offset);
+	panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
+					       lvds_lfp_data_ptrs,
+					       lvds_options->panel_type);
 
 	panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
 	if (!panel_fixed_mode)
 		return;
 
-	fill_detail_timing_data(panel_fixed_mode, dvo_timing);
+	fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
 
 	dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
 
 	DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
 	drm_mode_debug_printmodeline(panel_fixed_mode);
 
-	temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
-	temp_downclock = panel_fixed_mode->clock;
 	/*
-	 * enumerate the LVDS panel timing info entry in VBT to check whether
-	 * the LVDS downclock is found.
+	 * Iterate over the LVDS panel timing info to find the lowest clock
+	 * for the native resolution.
 	 */
+	downclock = panel_dvo_timing->clock;
 	for (i = 0; i < 16; i++) {
-		entry = (struct bdb_lvds_lfp_data_entry *)
-			((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
-		dvo_timing = (struct lvds_dvo_timing *)
-			((unsigned char *)entry + dvo_timing_offset);
+		const struct lvds_dvo_timing *dvo_timing;
 
-		fill_detail_timing_data(temp_mode, dvo_timing);
-
-		if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
-		temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
-		temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
-		temp_mode->htotal == panel_fixed_mode->htotal &&
-		temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
-		temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
-		temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
-		temp_mode->vtotal == panel_fixed_mode->vtotal &&
-		temp_mode->clock < temp_downclock) {
-			/*
-			 * downclock is already found. But we expect
-			 * to find the lower downclock.
-			 */
-			temp_downclock = temp_mode->clock;
-		}
-		/* clear it to zero */
-		memset(temp_mode, 0, sizeof(*temp_mode));
+		dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
+						 lvds_lfp_data_ptrs,
+						 i);
+		if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) &&
+		    dvo_timing->clock < downclock)
+			downclock = dvo_timing->clock;
 	}
-	kfree(temp_mode);
-	if (temp_downclock < panel_fixed_mode->clock &&
-	    i915_lvds_downclock) {
+
+	if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
 		dev_priv->lvds_downclock_avail = 1;
-		dev_priv->lvds_downclock = temp_downclock;
+		dev_priv->lvds_downclock = downclock * 10;
 		DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
 			      "Normal Clock %dKHz, downclock %dKHz\n",
-			      temp_downclock, panel_fixed_mode->clock);
+			      panel_fixed_mode->clock, 10*downclock);
 	}
-	return;
 }
 
 /* Try to find sdvo panel data */
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 0f1c799..393a399 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -24,6 +24,7 @@
  *	Eric Anholt <eric@anholt.net>
  */
 
+#include <linux/cpufreq.h>
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/i2c.h>
@@ -1157,12 +1158,15 @@
 
 	reg = TRANSCONF(pipe);
 	val = I915_READ(reg);
-	/*
-	 * make the BPC in transcoder be consistent with
-	 * that in pipeconf reg.
-	 */
-	val &= ~PIPE_BPC_MASK;
-	val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+
+	if (HAS_PCH_IBX(dev_priv->dev)) {
+		/*
+		 * make the BPC in transcoder be consistent with
+		 * that in pipeconf reg.
+		 */
+		val &= ~PIPE_BPC_MASK;
+		val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+	}
 	I915_WRITE(reg, val | TRANS_ENABLE);
 	if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
 		DRM_ERROR("failed to enable transcoder %d\n", pipe);
@@ -1380,62 +1384,7 @@
 	disable_pch_hdmi(dev_priv, pipe, HDMID);
 }
 
-static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct drm_framebuffer *fb = crtc->fb;
-	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-	struct drm_i915_gem_object *obj = intel_fb->obj;
-	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-	int plane, i;
-	u32 fbc_ctl, fbc_ctl2;
-
-	if (fb->pitch == dev_priv->cfb_pitch &&
-	    obj->fence_reg == dev_priv->cfb_fence &&
-	    intel_crtc->plane == dev_priv->cfb_plane &&
-	    I915_READ(FBC_CONTROL) & FBC_CTL_EN)
-		return;
-
-	i8xx_disable_fbc(dev);
-
-	dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
-
-	if (fb->pitch < dev_priv->cfb_pitch)
-		dev_priv->cfb_pitch = fb->pitch;
-
-	/* FBC_CTL wants 64B units */
-	dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-	dev_priv->cfb_fence = obj->fence_reg;
-	dev_priv->cfb_plane = intel_crtc->plane;
-	plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
-
-	/* Clear old tags */
-	for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
-		I915_WRITE(FBC_TAG + (i * 4), 0);
-
-	/* Set it up... */
-	fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
-	if (obj->tiling_mode != I915_TILING_NONE)
-		fbc_ctl2 |= FBC_CTL_CPU_FENCE;
-	I915_WRITE(FBC_CONTROL2, fbc_ctl2);
-	I915_WRITE(FBC_FENCE_OFF, crtc->y);
-
-	/* enable it... */
-	fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
-	if (IS_I945GM(dev))
-		fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
-	fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
-	fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
-	if (obj->tiling_mode != I915_TILING_NONE)
-		fbc_ctl |= dev_priv->cfb_fence;
-	I915_WRITE(FBC_CONTROL, fbc_ctl);
-
-	DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
-		      dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
-}
-
-void i8xx_disable_fbc(struct drm_device *dev)
+static void i8xx_disable_fbc(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 fbc_ctl;
@@ -1457,6 +1406,49 @@
 	DRM_DEBUG_KMS("disabled FBC\n");
 }
 
+static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_framebuffer *fb = crtc->fb;
+	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+	struct drm_i915_gem_object *obj = intel_fb->obj;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int cfb_pitch;
+	int plane, i;
+	u32 fbc_ctl, fbc_ctl2;
+
+	cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
+	if (fb->pitch < cfb_pitch)
+		cfb_pitch = fb->pitch;
+
+	/* FBC_CTL wants 64B units */
+	cfb_pitch = (cfb_pitch / 64) - 1;
+	plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
+
+	/* Clear old tags */
+	for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
+		I915_WRITE(FBC_TAG + (i * 4), 0);
+
+	/* Set it up... */
+	fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
+	fbc_ctl2 |= plane;
+	I915_WRITE(FBC_CONTROL2, fbc_ctl2);
+	I915_WRITE(FBC_FENCE_OFF, crtc->y);
+
+	/* enable it... */
+	fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
+	if (IS_I945GM(dev))
+		fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
+	fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
+	fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
+	fbc_ctl |= obj->fence_reg;
+	I915_WRITE(FBC_CONTROL, fbc_ctl);
+
+	DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
+		      cfb_pitch, crtc->y, intel_crtc->plane);
+}
+
 static bool i8xx_fbc_enabled(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1476,30 +1468,9 @@
 	unsigned long stall_watermark = 200;
 	u32 dpfc_ctl;
 
-	dpfc_ctl = I915_READ(DPFC_CONTROL);
-	if (dpfc_ctl & DPFC_CTL_EN) {
-		if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-		    dev_priv->cfb_fence == obj->fence_reg &&
-		    dev_priv->cfb_plane == intel_crtc->plane &&
-		    dev_priv->cfb_y == crtc->y)
-			return;
-
-		I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
-		intel_wait_for_vblank(dev, intel_crtc->pipe);
-	}
-
-	dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-	dev_priv->cfb_fence = obj->fence_reg;
-	dev_priv->cfb_plane = intel_crtc->plane;
-	dev_priv->cfb_y = crtc->y;
-
 	dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
-	if (obj->tiling_mode != I915_TILING_NONE) {
-		dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence;
-		I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
-	} else {
-		I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY);
-	}
+	dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
+	I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
 
 	I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
 		   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1512,7 +1483,7 @@
 	DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
 }
 
-void g4x_disable_fbc(struct drm_device *dev)
+static void g4x_disable_fbc(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 dpfc_ctl;
@@ -1567,32 +1538,12 @@
 	u32 dpfc_ctl;
 
 	dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
-	if (dpfc_ctl & DPFC_CTL_EN) {
-		if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-		    dev_priv->cfb_fence == obj->fence_reg &&
-		    dev_priv->cfb_plane == intel_crtc->plane &&
-		    dev_priv->cfb_offset == obj->gtt_offset &&
-		    dev_priv->cfb_y == crtc->y)
-			return;
-
-		I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
-		intel_wait_for_vblank(dev, intel_crtc->pipe);
-	}
-
-	dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-	dev_priv->cfb_fence = obj->fence_reg;
-	dev_priv->cfb_plane = intel_crtc->plane;
-	dev_priv->cfb_offset = obj->gtt_offset;
-	dev_priv->cfb_y = crtc->y;
-
 	dpfc_ctl &= DPFC_RESERVED;
 	dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
-	if (obj->tiling_mode != I915_TILING_NONE) {
-		dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence);
-		I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
-	} else {
-		I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY);
-	}
+	/* Set persistent mode for front-buffer rendering, ala X. */
+	dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
+	dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
+	I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
 
 	I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
 		   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
@@ -1604,7 +1555,7 @@
 
 	if (IS_GEN6(dev)) {
 		I915_WRITE(SNB_DPFC_CTL_SA,
-			   SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence);
+			   SNB_CPU_FENCE_ENABLE | obj->fence_reg);
 		I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
 		sandybridge_blit_fbc_update(dev);
 	}
@@ -1612,7 +1563,7 @@
 	DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
 }
 
-void ironlake_disable_fbc(struct drm_device *dev)
+static void ironlake_disable_fbc(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 dpfc_ctl;
@@ -1644,24 +1595,109 @@
 	return dev_priv->display.fbc_enabled(dev);
 }
 
-void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void intel_fbc_work_fn(struct work_struct *__work)
 {
-	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+	struct intel_fbc_work *work =
+		container_of(to_delayed_work(__work),
+			     struct intel_fbc_work, work);
+	struct drm_device *dev = work->crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	mutex_lock(&dev->struct_mutex);
+	if (work == dev_priv->fbc_work) {
+		/* Double check that we haven't switched fb without cancelling
+		 * the prior work.
+		 */
+		if (work->crtc->fb == work->fb) {
+			dev_priv->display.enable_fbc(work->crtc,
+						     work->interval);
+
+			dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane;
+			dev_priv->cfb_fb = work->crtc->fb->base.id;
+			dev_priv->cfb_y = work->crtc->y;
+		}
+
+		dev_priv->fbc_work = NULL;
+	}
+	mutex_unlock(&dev->struct_mutex);
+
+	kfree(work);
+}
+
+static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv)
+{
+	if (dev_priv->fbc_work == NULL)
+		return;
+
+	DRM_DEBUG_KMS("cancelling pending FBC enable\n");
+
+	/* Synchronisation is provided by struct_mutex and checking of
+	 * dev_priv->fbc_work, so we can perform the cancellation
+	 * entirely asynchronously.
+	 */
+	if (cancel_delayed_work(&dev_priv->fbc_work->work))
+		/* tasklet was killed before being run, clean up */
+		kfree(dev_priv->fbc_work);
+
+	/* Mark the work as no longer wanted so that if it does
+	 * wake-up (because the work was already running and waiting
+	 * for our mutex), it will discover that is no longer
+	 * necessary to run.
+	 */
+	dev_priv->fbc_work = NULL;
+}
+
+static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+{
+	struct intel_fbc_work *work;
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
 
 	if (!dev_priv->display.enable_fbc)
 		return;
 
-	dev_priv->display.enable_fbc(crtc, interval);
+	intel_cancel_fbc_work(dev_priv);
+
+	work = kzalloc(sizeof *work, GFP_KERNEL);
+	if (work == NULL) {
+		dev_priv->display.enable_fbc(crtc, interval);
+		return;
+	}
+
+	work->crtc = crtc;
+	work->fb = crtc->fb;
+	work->interval = interval;
+	INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
+
+	dev_priv->fbc_work = work;
+
+	DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
+
+	/* Delay the actual enabling to let pageflipping cease and the
+	 * display to settle before starting the compression. Note that
+	 * this delay also serves a second purpose: it allows for a
+	 * vblank to pass after disabling the FBC before we attempt
+	 * to modify the control registers.
+	 *
+	 * A more complicated solution would involve tracking vblanks
+	 * following the termination of the page-flipping sequence
+	 * and indeed performing the enable as a co-routine and not
+	 * waiting synchronously upon the vblank.
+	 */
+	schedule_delayed_work(&work->work, msecs_to_jiffies(50));
 }
 
 void intel_disable_fbc(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	intel_cancel_fbc_work(dev_priv);
+
 	if (!dev_priv->display.disable_fbc)
 		return;
 
 	dev_priv->display.disable_fbc(dev);
+	dev_priv->cfb_plane = -1;
 }
 
 /**
@@ -1760,8 +1796,13 @@
 		dev_priv->no_fbc_reason = FBC_BAD_PLANE;
 		goto out_disable;
 	}
-	if (obj->tiling_mode != I915_TILING_X) {
-		DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
+
+	/* The use of a CPU fence is mandatory in order to detect writes
+	 * by the CPU to the scanout and trigger updates to the FBC.
+	 */
+	if (obj->tiling_mode != I915_TILING_X ||
+	    obj->fence_reg == I915_FENCE_REG_NONE) {
+		DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
 		dev_priv->no_fbc_reason = FBC_NOT_TILED;
 		goto out_disable;
 	}
@@ -1770,6 +1811,44 @@
 	if (in_dbg_master())
 		goto out_disable;
 
+	/* If the scanout has not changed, don't modify the FBC settings.
+	 * Note that we make the fundamental assumption that the fb->obj
+	 * cannot be unpinned (and have its GTT offset and fence revoked)
+	 * without first being decoupled from the scanout and FBC disabled.
+	 */
+	if (dev_priv->cfb_plane == intel_crtc->plane &&
+	    dev_priv->cfb_fb == fb->base.id &&
+	    dev_priv->cfb_y == crtc->y)
+		return;
+
+	if (intel_fbc_enabled(dev)) {
+		/* We update FBC along two paths, after changing fb/crtc
+		 * configuration (modeswitching) and after page-flipping
+		 * finishes. For the latter, we know that not only did
+		 * we disable the FBC at the start of the page-flip
+		 * sequence, but also more than one vblank has passed.
+		 *
+		 * For the former case of modeswitching, it is possible
+		 * to switch between two FBC valid configurations
+		 * instantaneously so we do need to disable the FBC
+		 * before we can modify its control registers. We also
+		 * have to wait for the next vblank for that to take
+		 * effect. However, since we delay enabling FBC we can
+		 * assume that a vblank has passed since disabling and
+		 * that we can safely alter the registers in the deferred
+		 * callback.
+		 *
+		 * In the scenario that we go from a valid to invalid
+		 * and then back to valid FBC configuration we have
+		 * no strict enforcement that a vblank occurred since
+		 * disabling the FBC. However, along all current pipe
+		 * disabling paths we do need to wait for a vblank at
+		 * some point. And we wait before enabling FBC anyway.
+		 */
+		DRM_DEBUG_KMS("disabling active FBC for update\n");
+		intel_disable_fbc(dev);
+	}
+
 	intel_enable_fbc(crtc, 500);
 	return;
 
@@ -1812,14 +1891,10 @@
 	}
 
 	dev_priv->mm.interruptible = false;
-	ret = i915_gem_object_pin(obj, alignment, true);
+	ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined);
 	if (ret)
 		goto err_interruptible;
 
-	ret = i915_gem_object_set_to_display_plane(obj, pipelined);
-	if (ret)
-		goto err_unpin;
-
 	/* Install a fence for tiled scan-out. Pre-i965 always needs a
 	 * fence, whereas 965+ only requires a fence if using
 	 * framebuffer compression.  For simplicity, we always install
@@ -1841,10 +1916,8 @@
 	return ret;
 }
 
-/* Assume fb object is pinned & idle & fenced and just update base pointers */
-static int
-intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-			   int x, int y, enum mode_set_atomic state)
+static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+			     int x, int y)
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1887,7 +1960,7 @@
 		dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
 		break;
 	default:
-		DRM_ERROR("Unknown color depth\n");
+		DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
 		return -EINVAL;
 	}
 	if (INTEL_INFO(dev)->gen >= 4) {
@@ -1897,10 +1970,6 @@
 			dspcntr &= ~DISPPLANE_TILED;
 	}
 
-	if (HAS_PCH_SPLIT(dev))
-		/* must disable */
-		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
-
 	I915_WRITE(reg, dspcntr);
 
 	Start = obj->gtt_offset;
@@ -1917,6 +1986,99 @@
 		I915_WRITE(DSPADDR(plane), Start + Offset);
 	POSTING_READ(reg);
 
+	return 0;
+}
+
+static int ironlake_update_plane(struct drm_crtc *crtc,
+				 struct drm_framebuffer *fb, int x, int y)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_framebuffer *intel_fb;
+	struct drm_i915_gem_object *obj;
+	int plane = intel_crtc->plane;
+	unsigned long Start, Offset;
+	u32 dspcntr;
+	u32 reg;
+
+	switch (plane) {
+	case 0:
+	case 1:
+		break;
+	default:
+		DRM_ERROR("Can't update plane %d in SAREA\n", plane);
+		return -EINVAL;
+	}
+
+	intel_fb = to_intel_framebuffer(fb);
+	obj = intel_fb->obj;
+
+	reg = DSPCNTR(plane);
+	dspcntr = I915_READ(reg);
+	/* Mask out pixel format bits in case we change it */
+	dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+	switch (fb->bits_per_pixel) {
+	case 8:
+		dspcntr |= DISPPLANE_8BPP;
+		break;
+	case 16:
+		if (fb->depth != 16)
+			return -EINVAL;
+
+		dspcntr |= DISPPLANE_16BPP;
+		break;
+	case 24:
+	case 32:
+		if (fb->depth == 24)
+			dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+		else if (fb->depth == 30)
+			dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
+		else
+			return -EINVAL;
+		break;
+	default:
+		DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
+		return -EINVAL;
+	}
+
+	if (obj->tiling_mode != I915_TILING_NONE)
+		dspcntr |= DISPPLANE_TILED;
+	else
+		dspcntr &= ~DISPPLANE_TILED;
+
+	/* must disable */
+	dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
+	I915_WRITE(reg, dspcntr);
+
+	Start = obj->gtt_offset;
+	Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
+
+	DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
+		      Start, Offset, x, y, fb->pitch);
+	I915_WRITE(DSPSTRIDE(plane), fb->pitch);
+	I915_WRITE(DSPSURF(plane), Start);
+	I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
+	I915_WRITE(DSPADDR(plane), Offset);
+	POSTING_READ(reg);
+
+	return 0;
+}
+
+/* Assume fb object is pinned & idle & fenced and just update base pointers */
+static int
+intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+			   int x, int y, enum mode_set_atomic state)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret;
+
+	ret = dev_priv->display.update_plane(crtc, fb, x, y);
+	if (ret)
+		return ret;
+
 	intel_update_fbc(dev);
 	intel_increase_pllclock(crtc);
 
@@ -1971,7 +2133,7 @@
 		 * This should only fail upon a hung GPU, in which case we
 		 * can safely continue.
 		 */
-		ret = i915_gem_object_flush_gpu(obj);
+		ret = i915_gem_object_finish_gpu(obj);
 		(void) ret;
 	}
 
@@ -2622,6 +2784,7 @@
 	/* For PCH DP, enable TRANS_DP_CTL */
 	if (HAS_PCH_CPT(dev) &&
 	    intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+		u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5;
 		reg = TRANS_DP_CTL(pipe);
 		temp = I915_READ(reg);
 		temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -2629,7 +2792,7 @@
 			  TRANS_DP_BPC_MASK);
 		temp |= (TRANS_DP_OUTPUT_ENABLE |
 			 TRANS_DP_ENH_FRAMING);
-		temp |= TRANS_DP_8BPC;
+		temp |= bpc << 9; /* same format but at 11:9 */
 
 		if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
 			temp |= TRANS_DP_HSYNC_ACTIVE_HIGH;
@@ -2732,9 +2895,8 @@
 
 	intel_disable_plane(dev_priv, plane, pipe);
 
-	if (dev_priv->cfb_plane == plane &&
-	    dev_priv->display.disable_fbc)
-		dev_priv->display.disable_fbc(dev);
+	if (dev_priv->cfb_plane == plane)
+		intel_disable_fbc(dev);
 
 	intel_disable_pipe(dev_priv, pipe);
 
@@ -2898,9 +3060,8 @@
 	intel_crtc_dpms_overlay(intel_crtc, false);
 	intel_crtc_update_cursor(crtc, false);
 
-	if (dev_priv->cfb_plane == plane &&
-	    dev_priv->display.disable_fbc)
-		dev_priv->display.disable_fbc(dev);
+	if (dev_priv->cfb_plane == plane)
+		intel_disable_fbc(dev);
 
 	intel_disable_plane(dev_priv, plane, pipe);
 	intel_disable_pipe(dev_priv, pipe);
@@ -4309,6 +4470,133 @@
 		&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
 }
 
+/**
+ * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send
+ * @crtc: CRTC structure
+ *
+ * A pipe may be connected to one or more outputs.  Based on the depth of the
+ * attached framebuffer, choose a good color depth to use on the pipe.
+ *
+ * If possible, match the pipe depth to the fb depth.  In some cases, this
+ * isn't ideal, because the connected output supports a lesser or restricted
+ * set of depths.  Resolve that here:
+ *    LVDS typically supports only 6bpc, so clamp down in that case
+ *    HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc
+ *    Displays may support a restricted set as well, check EDID and clamp as
+ *      appropriate.
+ *
+ * RETURNS:
+ * Dithering requirement (i.e. false if display bpc and pipe bpc match,
+ * true if they don't match).
+ */
+static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
+					 unsigned int *pipe_bpp)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	unsigned int display_bpc = UINT_MAX, bpc;
+
+	/* Walk the encoders & connectors on this crtc, get min bpc */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+		if (encoder->crtc != crtc)
+			continue;
+
+		if (intel_encoder->type == INTEL_OUTPUT_LVDS) {
+			unsigned int lvds_bpc;
+
+			if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) ==
+			    LVDS_A3_POWER_UP)
+				lvds_bpc = 8;
+			else
+				lvds_bpc = 6;
+
+			if (lvds_bpc < display_bpc) {
+				DRM_DEBUG_DRIVER("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc);
+				display_bpc = lvds_bpc;
+			}
+			continue;
+		}
+
+		if (intel_encoder->type == INTEL_OUTPUT_EDP) {
+			/* Use VBT settings if we have an eDP panel */
+			unsigned int edp_bpc = dev_priv->edp.bpp / 3;
+
+			if (edp_bpc < display_bpc) {
+				DRM_DEBUG_DRIVER("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
+				display_bpc = edp_bpc;
+			}
+			continue;
+		}
+
+		/* Not one of the known troublemakers, check the EDID */
+		list_for_each_entry(connector, &dev->mode_config.connector_list,
+				    head) {
+			if (connector->encoder != encoder)
+				continue;
+
+			if (connector->display_info.bpc < display_bpc) {
+				DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
+				display_bpc = connector->display_info.bpc;
+			}
+		}
+
+		/*
+		 * HDMI is either 12 or 8, so if the display lets 10bpc sneak
+		 * through, clamp it down.  (Note: >12bpc will be caught below.)
+		 */
+		if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
+			if (display_bpc > 8 && display_bpc < 12) {
+				DRM_DEBUG_DRIVER("forcing bpc to 12 for HDMI\n");
+				display_bpc = 12;
+			} else {
+				DRM_DEBUG_DRIVER("forcing bpc to 8 for HDMI\n");
+				display_bpc = 8;
+			}
+		}
+	}
+
+	/*
+	 * We could just drive the pipe at the highest bpc all the time and
+	 * enable dithering as needed, but that costs bandwidth.  So choose
+	 * the minimum value that expresses the full color range of the fb but
+	 * also stays within the max display bpc discovered above.
+	 */
+
+	switch (crtc->fb->depth) {
+	case 8:
+		bpc = 8; /* since we go through a colormap */
+		break;
+	case 15:
+	case 16:
+		bpc = 6; /* min is 18bpp */
+		break;
+	case 24:
+		bpc = min((unsigned int)8, display_bpc);
+		break;
+	case 30:
+		bpc = min((unsigned int)10, display_bpc);
+		break;
+	case 48:
+		bpc = min((unsigned int)12, display_bpc);
+		break;
+	default:
+		DRM_DEBUG("unsupported depth, assuming 24 bits\n");
+		bpc = min((unsigned int)8, display_bpc);
+		break;
+	}
+
+	DRM_DEBUG_DRIVER("setting pipe bpc to %d (max display bpc %d)\n",
+			 bpc, display_bpc);
+
+	*pipe_bpp = bpc * 3;
+
+	return display_bpc != bpc;
+}
+
 static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
 			      struct drm_display_mode *mode,
 			      struct drm_display_mode *adjusted_mode,
@@ -4721,7 +5009,9 @@
 	struct fdi_m_n m_n = {0};
 	u32 temp;
 	u32 lvds_sync = 0;
-	int target_clock, pixel_multiplier, lane, link_bw, bpp, factor;
+	int target_clock, pixel_multiplier, lane, link_bw, factor;
+	unsigned int pipe_bpp;
+	bool dither;
 
 	list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
 		if (encoder->base.crtc != crtc)
@@ -4848,56 +5138,37 @@
 	/* determine panel color depth */
 	temp = I915_READ(PIPECONF(pipe));
 	temp &= ~PIPE_BPC_MASK;
-	if (is_lvds) {
-		/* the BPC will be 6 if it is 18-bit LVDS panel */
-		if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
-			temp |= PIPE_8BPC;
-		else
-			temp |= PIPE_6BPC;
-	} else if (has_edp_encoder) {
-		switch (dev_priv->edp.bpp/3) {
-		case 8:
-			temp |= PIPE_8BPC;
-			break;
-		case 10:
-			temp |= PIPE_10BPC;
-			break;
-		case 6:
-			temp |= PIPE_6BPC;
-			break;
-		case 12:
-			temp |= PIPE_12BPC;
-			break;
-		}
-	} else
+	dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp);
+	switch (pipe_bpp) {
+	case 18:
+		temp |= PIPE_6BPC;
+		break;
+	case 24:
 		temp |= PIPE_8BPC;
-	I915_WRITE(PIPECONF(pipe), temp);
-
-	switch (temp & PIPE_BPC_MASK) {
-	case PIPE_8BPC:
-		bpp = 24;
 		break;
-	case PIPE_10BPC:
-		bpp = 30;
+	case 30:
+		temp |= PIPE_10BPC;
 		break;
-	case PIPE_6BPC:
-		bpp = 18;
-		break;
-	case PIPE_12BPC:
-		bpp = 36;
+	case 36:
+		temp |= PIPE_12BPC;
 		break;
 	default:
-		DRM_ERROR("unknown pipe bpc value\n");
-		bpp = 24;
+		WARN(1, "intel_choose_pipe_bpp returned invalid value\n");
+		temp |= PIPE_8BPC;
+		pipe_bpp = 24;
+		break;
 	}
 
+	intel_crtc->bpp = pipe_bpp;
+	I915_WRITE(PIPECONF(pipe), temp);
+
 	if (!lane) {
 		/*
 		 * Account for spread spectrum to avoid
 		 * oversubscribing the link. Max center spread
 		 * is 2.5%; use 5% for safety's sake.
 		 */
-		u32 bps = target_clock * bpp * 21 / 20;
+		u32 bps = target_clock * intel_crtc->bpp * 21 / 20;
 		lane = bps / (link_bw * 8) + 1;
 	}
 
@@ -4905,7 +5176,8 @@
 
 	if (pixel_multiplier > 1)
 		link_bw *= pixel_multiplier;
-	ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+	ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
+			     &m_n);
 
 	/* Ironlake: try to setup display ref clock before DPLL
 	 * enabling. This is only under driver's control after
@@ -5108,14 +5380,12 @@
 		I915_WRITE(PCH_LVDS, temp);
 	}
 
-	/* set the dithering flag and clear for anything other than a panel. */
 	pipeconf &= ~PIPECONF_DITHER_EN;
 	pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
-	if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+	if ((is_lvds && dev_priv->lvds_dither) || dither) {
 		pipeconf |= PIPECONF_DITHER_EN;
 		pipeconf |= PIPECONF_DITHER_TYPE_ST1;
 	}
-
 	if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
 		intel_dp_set_m_n(crtc, mode, adjusted_mode);
 	} else {
@@ -5435,21 +5705,15 @@
 			goto fail_locked;
 		}
 
-		ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
-		if (ret) {
-			DRM_ERROR("failed to pin cursor bo\n");
-			goto fail_locked;
-		}
-
-		ret = i915_gem_object_set_to_gtt_domain(obj, 0);
+		ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL);
 		if (ret) {
 			DRM_ERROR("failed to move cursor bo into the GTT\n");
-			goto fail_unpin;
+			goto fail_locked;
 		}
 
 		ret = i915_gem_object_put_fence(obj);
 		if (ret) {
-			DRM_ERROR("failed to move cursor bo into the GTT\n");
+			DRM_ERROR("failed to release fence for cursor");
 			goto fail_unpin;
 		}
 
@@ -6152,6 +6416,7 @@
 	drm_gem_object_unreference(&work->pending_flip_obj->base);
 	drm_gem_object_unreference(&work->old_fb_obj->base);
 
+	intel_update_fbc(work->dev);
 	mutex_unlock(&work->dev->struct_mutex);
 	kfree(work);
 }
@@ -6516,6 +6781,7 @@
 	if (ret)
 		goto cleanup_pending;
 
+	intel_disable_fbc(dev);
 	mutex_unlock(&dev->struct_mutex);
 
 	trace_i915_flip_request(intel_crtc->plane, obj);
@@ -6644,6 +6910,7 @@
 
 	intel_crtc_reset(&intel_crtc->base);
 	intel_crtc->active = true; /* force the pipe off on setup_init_config */
+	intel_crtc->bpp = 24; /* default for pre-Ironlake */
 
 	if (HAS_PCH_SPLIT(dev)) {
 		intel_helper_funcs.prepare = ironlake_crtc_prepare;
@@ -6870,6 +7137,11 @@
 	switch (mode_cmd->bpp) {
 	case 8:
 	case 16:
+		/* Only pre-ILK can handle 5:5:5 */
+		if (mode_cmd->depth == 15 && !HAS_PCH_SPLIT(dev))
+			return -EINVAL;
+		break;
+
 	case 24:
 	case 32:
 		break;
@@ -7284,6 +7556,59 @@
 	mutex_unlock(&dev_priv->dev->struct_mutex);
 }
 
+void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
+{
+	int min_freq = 15;
+	int gpu_freq, ia_freq, max_ia_freq;
+	int scaling_factor = 180;
+
+	max_ia_freq = cpufreq_quick_get_max(0);
+	/*
+	 * Default to measured freq if none found, PCU will ensure we don't go
+	 * over
+	 */
+	if (!max_ia_freq)
+		max_ia_freq = tsc_khz;
+
+	/* Convert from kHz to MHz */
+	max_ia_freq /= 1000;
+
+	mutex_lock(&dev_priv->dev->struct_mutex);
+
+	/*
+	 * For each potential GPU frequency, load a ring frequency we'd like
+	 * to use for memory access.  We do this by specifying the IA frequency
+	 * the PCU should use as a reference to determine the ring frequency.
+	 */
+	for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
+	     gpu_freq--) {
+		int diff = dev_priv->max_delay - gpu_freq;
+
+		/*
+		 * For GPU frequencies less than 750MHz, just use the lowest
+		 * ring freq.
+		 */
+		if (gpu_freq < min_freq)
+			ia_freq = 800;
+		else
+			ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
+		ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
+
+		I915_WRITE(GEN6_PCODE_DATA,
+			   (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
+			   gpu_freq);
+		I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
+			   GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
+		if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
+			      GEN6_PCODE_READY) == 0, 10)) {
+			DRM_ERROR("pcode write of freq table timed out\n");
+			continue;
+		}
+	}
+
+	mutex_unlock(&dev_priv->dev->struct_mutex);
+}
+
 static void ironlake_init_clock_gating(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7640,9 +7965,11 @@
 	if (HAS_PCH_SPLIT(dev)) {
 		dev_priv->display.dpms = ironlake_crtc_dpms;
 		dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
+		dev_priv->display.update_plane = ironlake_update_plane;
 	} else {
 		dev_priv->display.dpms = i9xx_crtc_dpms;
 		dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
+		dev_priv->display.update_plane = i9xx_update_plane;
 	}
 
 	if (I915_HAS_FBC(dev)) {
@@ -7939,8 +8266,10 @@
 		intel_init_emon(dev);
 	}
 
-	if (IS_GEN6(dev))
+	if (IS_GEN6(dev) || IS_GEN7(dev)) {
 		gen6_enable_rps(dev_priv);
+		gen6_update_ring_freq(dev_priv);
+	}
 
 	INIT_WORK(&dev_priv->idle_work, intel_idle_update);
 	setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
@@ -7976,12 +8305,11 @@
 		intel_increase_pllclock(crtc);
 	}
 
-	if (dev_priv->display.disable_fbc)
-		dev_priv->display.disable_fbc(dev);
+	intel_disable_fbc(dev);
 
 	if (IS_IRONLAKE_M(dev))
 		ironlake_disable_drps(dev);
-	if (IS_GEN6(dev))
+	if (IS_GEN6(dev) || IS_GEN7(dev))
 		gen6_disable_rps(dev);
 
 	if (IS_IRONLAKE_M(dev))
@@ -7994,6 +8322,9 @@
 	drm_irq_uninstall(dev);
 	cancel_work_sync(&dev_priv->hotplug_work);
 
+	/* flush any delayed tasks or pending work */
+	flush_scheduled_work();
+
 	/* Shut off idle work before the crtcs get freed. */
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		intel_crtc = to_intel_crtc(crtc);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index e2aced6..f797fb5 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -178,12 +178,14 @@
 static int
 intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock)
 {
-	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc = intel_dp->base.base.crtc;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int bpp = 24;
 
-	if (is_edp(intel_dp))
-		return (pixel_clock * dev_priv->edp.bpp + 7) / 8;
-	else
-		return pixel_clock * 3;
+	if (intel_crtc)
+		bpp = intel_crtc->bpp;
+
+	return (pixel_clock * bpp + 7) / 8;
 }
 
 static int
@@ -681,7 +683,7 @@
 	struct drm_encoder *encoder;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-	int lane_count = 4, bpp = 24;
+	int lane_count = 4;
 	struct intel_dp_m_n m_n;
 	int pipe = intel_crtc->pipe;
 
@@ -700,7 +702,6 @@
 			break;
 		} else if (is_edp(intel_dp)) {
 			lane_count = dev_priv->edp.lanes;
-			bpp = dev_priv->edp.bpp;
 			break;
 		}
 	}
@@ -710,7 +711,7 @@
 	 * the number of bytes_per_pixel post-LUT, which we always
 	 * set up for 8-bits of R/G/B, or 3 bytes total.
 	 */
-	intel_dp_compute_m_n(bpp, lane_count,
+	intel_dp_compute_m_n(intel_crtc->bpp, lane_count,
 			     mode->clock, adjusted_mode->clock, &m_n);
 
 	if (HAS_PCH_SPLIT(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9ffa61e..6e990f9 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -170,6 +170,7 @@
 	int16_t cursor_x, cursor_y;
 	int16_t cursor_width, cursor_height;
 	bool cursor_visible;
+	unsigned int bpp;
 };
 
 #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
@@ -233,6 +234,13 @@
 	bool enable_stall_check;
 };
 
+struct intel_fbc_work {
+	struct delayed_work work;
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *fb;
+	int interval;
+};
+
 int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
 extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
 
@@ -317,6 +325,7 @@
 extern void ironlake_enable_drps(struct drm_device *dev);
 extern void ironlake_disable_drps(struct drm_device *dev);
 extern void gen6_enable_rps(struct drm_i915_private *dev_priv);
+extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv);
 extern void gen6_disable_rps(struct drm_device *dev);
 extern void intel_init_emon(struct drm_device *dev);
 
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index aa0a8e8..1ed8e69 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -124,12 +124,18 @@
 	u32 sdvox;
 
 	sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
-	sdvox |= intel_hdmi->color_range;
+	if (!HAS_PCH_SPLIT(dev))
+		sdvox |= intel_hdmi->color_range;
 	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
 		sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
 	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
 		sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
 
+	if (intel_crtc->bpp > 24)
+		sdvox |= COLOR_FORMAT_12bpc;
+	else
+		sdvox |= COLOR_FORMAT_8bpc;
+
 	/* Required on CPT */
 	if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
 		sdvox |= HDMI_MODE_SELECT;
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index d2c7104..b7c5ddb 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -297,19 +297,26 @@
 	/* The only video events relevant to opregion are 0x80. These indicate
 	   either a docking event, lid switch or display switch request. In
 	   Linux, these are handled by the dock, button and video drivers.
-	   We might want to fix the video driver to be opregion-aware in
-	   future, but right now we just indicate to the firmware that the
-	   request has been handled */
+	*/
 
 	struct opregion_acpi *acpi;
+	struct acpi_bus_event *event = data;
+	int ret = NOTIFY_OK;
+
+	if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0)
+		return NOTIFY_DONE;
 
 	if (!system_opregion)
 		return NOTIFY_DONE;
 
 	acpi = system_opregion->acpi;
+
+	if (event->type == 0x80 && !(acpi->cevt & 0x1))
+		ret = NOTIFY_BAD;
+
 	acpi->csts = 0;
 
-	return NOTIFY_OK;
+	return ret;
 }
 
 static struct notifier_block intel_opregion_notifier = {
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 9e2959b..d360380 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -773,14 +773,10 @@
 	if (ret != 0)
 		return ret;
 
-	ret = i915_gem_object_pin(new_bo, PAGE_SIZE, true);
+	ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL);
 	if (ret != 0)
 		return ret;
 
-	ret = i915_gem_object_set_to_gtt_domain(new_bo, 0);
-	if (ret != 0)
-		goto out_unpin;
-
 	ret = i915_gem_object_put_fence(new_bo);
 	if (ret)
 		goto out_unpin;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 95c4b14..e961568 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -236,7 +236,8 @@
 		ret = -ENOMEM;
 		goto err;
 	}
-	obj->cache_level = I915_CACHE_LLC;
+
+	i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
 
 	ret = i915_gem_object_pin(obj, 4096, true);
 	if (ret)
@@ -776,7 +777,8 @@
 		ret = -ENOMEM;
 		goto err;
 	}
-	obj->cache_level = I915_CACHE_LLC;
+
+	i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
 
 	ret = i915_gem_object_pin(obj, 4096, true);
 	if (ret != 0) {
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 113e4e7..210d570 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1236,6 +1236,8 @@
 		      struct drm_connector *connector)
 {
 	struct drm_encoder *encoder = &intel_tv->base.base;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	struct drm_device *dev = encoder->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	unsigned long irqflags;
@@ -1258,6 +1260,10 @@
 	/* Poll for TV detection */
 	tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
 	tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+	if (intel_crtc->pipe == 1)
+		tv_ctl |= TV_ENC_PIPEB_SELECT;
+	else
+		tv_ctl &= ~TV_ENC_PIPEB_SELECT;
 
 	tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
 	tv_dac |= (TVDAC_STATE_CHG_EN |
@@ -1277,26 +1283,26 @@
 			      to_intel_crtc(intel_tv->base.base.crtc)->pipe);
 
 	type = -1;
-	if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) {
-		DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
-		/*
-		 *  A B C
-		 *  0 1 1 Composite
-		 *  1 0 X svideo
-		 *  0 0 0 Component
-		 */
-		if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
-			DRM_DEBUG_KMS("Detected Composite TV connection\n");
-			type = DRM_MODE_CONNECTOR_Composite;
-		} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
-			DRM_DEBUG_KMS("Detected S-Video TV connection\n");
-			type = DRM_MODE_CONNECTOR_SVIDEO;
-		} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
-			DRM_DEBUG_KMS("Detected Component TV connection\n");
-			type = DRM_MODE_CONNECTOR_Component;
-		} else {
-			DRM_DEBUG_KMS("Unrecognised TV connection\n");
-		}
+	tv_dac = I915_READ(TV_DAC);
+	DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
+	/*
+	 *  A B C
+	 *  0 1 1 Composite
+	 *  1 0 X svideo
+	 *  0 0 0 Component
+	 */
+	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+		DRM_DEBUG_KMS("Detected Composite TV connection\n");
+		type = DRM_MODE_CONNECTOR_Composite;
+	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+		DRM_DEBUG_KMS("Detected S-Video TV connection\n");
+		type = DRM_MODE_CONNECTOR_SVIDEO;
+	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+		DRM_DEBUG_KMS("Detected Component TV connection\n");
+		type = DRM_MODE_CONNECTOR_Component;
+	} else {
+		DRM_DEBUG_KMS("Unrecognised TV connection\n");
+		type = -1;
 	}
 
 	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 729d5fd..b311fab 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -135,13 +135,14 @@
 	int i;
 
 	if (dev_priv->card_type >= NV_50) {
-		uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
-
-		if (!vbios_vram)
-			vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
+		u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8;
+		if (!addr) {
+			addr  = (u64)nv_rd32(dev, 0x1700) << 16;
+			addr += 0xf0000;
+		}
 
 		old_bar0_pramin = nv_rd32(dev, 0x1700);
-		nv_wr32(dev, 0x1700, vbios_vram >> 16);
+		nv_wr32(dev, 0x1700, addr >> 16);
 	}
 
 	/* bail if no rom signature */
@@ -5186,7 +5187,7 @@
 	load_table_ptr = ROM16(bios->data[bitentry->offset]);
 
 	if (load_table_ptr == 0x0) {
-		NV_ERROR(dev, "Pointer to BIT loadval table invalid\n");
+		NV_DEBUG(dev, "Pointer to BIT loadval table invalid\n");
 		return -EINVAL;
 	}
 
@@ -5965,6 +5966,12 @@
 		if (cte->type == DCB_CONNECTOR_HDMI_1)
 			cte->type = DCB_CONNECTOR_DVI_I;
 	}
+
+	/* Gigabyte GV-NX86T512H */
+	if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
+		if (cte->type == DCB_CONNECTOR_HDMI_1)
+			cte->type = DCB_CONNECTOR_DVI_I;
+	}
 }
 
 static const u8 hpd_gpio[16] = {
@@ -6377,6 +6384,37 @@
 		}
 	}
 
+	/* Some other twisted XFX board (rhbz#694914)
+	 *
+	 * The DVI/VGA encoder combo that's supposed to represent the
+	 * DVI-I connector actually point at two different ones, and
+	 * the HDMI connector ends up paired with the VGA instead.
+	 *
+	 * Connector table is missing anything for VGA at all, pointing it
+	 * an invalid conntab entry 2 so we figure it out ourself.
+	 */
+	if (nv_match_device(dev, 0x0615, 0x1682, 0x2605)) {
+		if (idx == 0) {
+			*conn = 0x02002300; /* VGA, connector 2 */
+			*conf = 0x00000028;
+		} else
+		if (idx == 1) {
+			*conn = 0x01010312; /* DVI, connector 0 */
+			*conf = 0x00020030;
+		} else
+		if (idx == 2) {
+			*conn = 0x04020310; /* VGA, connector 0 */
+			*conf = 0x00000028;
+		} else
+		if (idx == 3) {
+			*conn = 0x02021322; /* HDMI, connector 1 */
+			*conf = 0x00020010;
+		} else {
+			*conn = 0x0000000e; /* EOL */
+			*conf = 0x00000000;
+		}
+	}
+
 	return true;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 2ad49cb..890d50e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -49,16 +49,12 @@
 		DRM_ERROR("bo %p still attached to GEM object\n", bo);
 
 	nv10_mem_put_tile_region(dev, nvbo->tile, NULL);
-	if (nvbo->vma.node) {
-		nouveau_vm_unmap(&nvbo->vma);
-		nouveau_vm_put(&nvbo->vma);
-	}
 	kfree(nvbo);
 }
 
 static void
 nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
-		       int *align, int *size, int *page_shift)
+		       int *align, int *size)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
 
@@ -82,67 +78,51 @@
 			}
 		}
 	} else {
-		if (likely(dev_priv->chan_vm)) {
-			if (!(flags & TTM_PL_FLAG_TT) &&  *size > 256 * 1024)
-				*page_shift = dev_priv->chan_vm->lpg_shift;
-			else
-				*page_shift = dev_priv->chan_vm->spg_shift;
-		} else {
-			*page_shift = 12;
-		}
-
-		*size = roundup(*size, (1 << *page_shift));
-		*align = max((1 << *page_shift), *align);
+		*size = roundup(*size, (1 << nvbo->page_shift));
+		*align = max((1 <<  nvbo->page_shift), *align);
 	}
 
 	*size = roundup(*size, PAGE_SIZE);
 }
 
 int
-nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
-	       int size, int align, uint32_t flags, uint32_t tile_mode,
-	       uint32_t tile_flags, struct nouveau_bo **pnvbo)
+nouveau_bo_new(struct drm_device *dev, int size, int align,
+	       uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
+	       struct nouveau_bo **pnvbo)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_bo *nvbo;
-	int ret = 0, page_shift = 0;
+	int ret;
 
 	nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
 	if (!nvbo)
 		return -ENOMEM;
 	INIT_LIST_HEAD(&nvbo->head);
 	INIT_LIST_HEAD(&nvbo->entry);
+	INIT_LIST_HEAD(&nvbo->vma_list);
 	nvbo->tile_mode = tile_mode;
 	nvbo->tile_flags = tile_flags;
 	nvbo->bo.bdev = &dev_priv->ttm.bdev;
 
-	nouveau_bo_fixup_align(nvbo, flags, &align, &size, &page_shift);
-	align >>= PAGE_SHIFT;
-
-	if (dev_priv->chan_vm) {
-		ret = nouveau_vm_get(dev_priv->chan_vm, size, page_shift,
-				     NV_MEM_ACCESS_RW, &nvbo->vma);
-		if (ret) {
-			kfree(nvbo);
-			return ret;
-		}
+	nvbo->page_shift = 12;
+	if (dev_priv->bar1_vm) {
+		if (!(flags & TTM_PL_FLAG_TT) && size > 256 * 1024)
+			nvbo->page_shift = dev_priv->bar1_vm->lpg_shift;
 	}
 
+	nouveau_bo_fixup_align(nvbo, flags, &align, &size);
 	nvbo->bo.mem.num_pages = size >> PAGE_SHIFT;
 	nouveau_bo_placement_set(nvbo, flags, 0);
 
-	nvbo->channel = chan;
 	ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
-			  ttm_bo_type_device, &nvbo->placement, align, 0,
-			  false, NULL, size, nouveau_bo_del_ttm);
+			  ttm_bo_type_device, &nvbo->placement,
+			  align >> PAGE_SHIFT, 0, false, NULL, size,
+			  nouveau_bo_del_ttm);
 	if (ret) {
 		/* ttm will call nouveau_bo_del_ttm if it fails.. */
 		return ret;
 	}
-	nvbo->channel = NULL;
 
-	if (nvbo->vma.node)
-		nvbo->bo.offset = nvbo->vma.offset;
 	*pnvbo = nvbo;
 	return 0;
 }
@@ -312,8 +292,6 @@
 	if (ret)
 		return ret;
 
-	if (nvbo->vma.node)
-		nvbo->bo.offset = nvbo->vma.offset;
 	return 0;
 }
 
@@ -440,7 +418,6 @@
 				     TTM_MEMTYPE_FLAG_CMA;
 			man->available_caching = TTM_PL_MASK_CACHING;
 			man->default_caching = TTM_PL_FLAG_CACHED;
-			man->gpu_offset = dev_priv->gart_info.aper_base;
 			break;
 		default:
 			NV_ERROR(dev, "Unknown GART type: %d\n",
@@ -501,19 +478,12 @@
 nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
 		  struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
 {
-	struct nouveau_mem *old_node = old_mem->mm_node;
-	struct nouveau_mem *new_node = new_mem->mm_node;
-	struct nouveau_bo *nvbo = nouveau_bo(bo);
+	struct nouveau_mem *node = old_mem->mm_node;
+	u64 src_offset = node->vma[0].offset;
+	u64 dst_offset = node->vma[1].offset;
 	u32 page_count = new_mem->num_pages;
-	u64 src_offset, dst_offset;
 	int ret;
 
-	src_offset = old_node->tmp_vma.offset;
-	if (new_node->tmp_vma.node)
-		dst_offset = new_node->tmp_vma.offset;
-	else
-		dst_offset = nvbo->vma.offset;
-
 	page_count = new_mem->num_pages;
 	while (page_count) {
 		int line_count = (page_count > 2047) ? 2047 : page_count;
@@ -547,19 +517,13 @@
 nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
 		  struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
 {
-	struct nouveau_mem *old_node = old_mem->mm_node;
-	struct nouveau_mem *new_node = new_mem->mm_node;
+	struct nouveau_mem *node = old_mem->mm_node;
 	struct nouveau_bo *nvbo = nouveau_bo(bo);
 	u64 length = (new_mem->num_pages << PAGE_SHIFT);
-	u64 src_offset, dst_offset;
+	u64 src_offset = node->vma[0].offset;
+	u64 dst_offset = node->vma[1].offset;
 	int ret;
 
-	src_offset = old_node->tmp_vma.offset;
-	if (new_node->tmp_vma.node)
-		dst_offset = new_node->tmp_vma.offset;
-	else
-		dst_offset = nvbo->vma.offset;
-
 	while (length) {
 		u32 amount, stride, height;
 
@@ -695,6 +659,27 @@
 }
 
 static int
+nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo,
+		   struct ttm_mem_reg *mem, struct nouveau_vma *vma)
+{
+	struct nouveau_mem *node = mem->mm_node;
+	int ret;
+
+	ret = nouveau_vm_get(chan->vm, mem->num_pages << PAGE_SHIFT,
+			     node->page_shift, NV_MEM_ACCESS_RO, vma);
+	if (ret)
+		return ret;
+
+	if (mem->mem_type == TTM_PL_VRAM)
+		nouveau_vm_map(vma, node);
+	else
+		nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT,
+				  node, node->pages);
+
+	return 0;
+}
+
+static int
 nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
 		     bool no_wait_reserve, bool no_wait_gpu,
 		     struct ttm_mem_reg *new_mem)
@@ -711,31 +696,20 @@
 		mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
 	}
 
-	/* create temporary vma for old memory, this will get cleaned
-	 * up after ttm destroys the ttm_mem_reg
+	/* create temporary vmas for the transfer and attach them to the
+	 * old nouveau_mem node, these will get cleaned up after ttm has
+	 * destroyed the ttm_mem_reg
 	 */
 	if (dev_priv->card_type >= NV_50) {
 		struct nouveau_mem *node = old_mem->mm_node;
-		if (!node->tmp_vma.node) {
-			u32 page_shift = nvbo->vma.node->type;
-			if (old_mem->mem_type == TTM_PL_TT)
-				page_shift = nvbo->vma.vm->spg_shift;
 
-			ret = nouveau_vm_get(chan->vm,
-					     old_mem->num_pages << PAGE_SHIFT,
-					     page_shift, NV_MEM_ACCESS_RO,
-					     &node->tmp_vma);
-			if (ret)
-				goto out;
-		}
+		ret = nouveau_vma_getmap(chan, nvbo, old_mem, &node->vma[0]);
+		if (ret)
+			goto out;
 
-		if (old_mem->mem_type == TTM_PL_VRAM)
-			nouveau_vm_map(&node->tmp_vma, node);
-		else {
-			nouveau_vm_map_sg(&node->tmp_vma, 0,
-					  old_mem->num_pages << PAGE_SHIFT,
-					  node, node->pages);
-		}
+		ret = nouveau_vma_getmap(chan, nvbo, new_mem, &node->vma[1]);
+		if (ret)
+			goto out;
 	}
 
 	if (dev_priv->card_type < NV_50)
@@ -762,7 +736,6 @@
 		      bool no_wait_reserve, bool no_wait_gpu,
 		      struct ttm_mem_reg *new_mem)
 {
-	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
 	u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
 	struct ttm_placement placement;
 	struct ttm_mem_reg tmp_mem;
@@ -782,23 +755,7 @@
 	if (ret)
 		goto out;
 
-	if (dev_priv->card_type >= NV_50) {
-		struct nouveau_bo *nvbo = nouveau_bo(bo);
-		struct nouveau_mem *node = tmp_mem.mm_node;
-		struct nouveau_vma *vma = &nvbo->vma;
-		if (vma->node->type != vma->vm->spg_shift)
-			vma = &node->tmp_vma;
-		nouveau_vm_map_sg(vma, 0, tmp_mem.num_pages << PAGE_SHIFT,
-				  node, node->pages);
-	}
-
 	ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, &tmp_mem);
-
-	if (dev_priv->card_type >= NV_50) {
-		struct nouveau_bo *nvbo = nouveau_bo(bo);
-		nouveau_vm_unmap(&nvbo->vma);
-	}
-
 	if (ret)
 		goto out;
 
@@ -844,30 +801,22 @@
 static void
 nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem)
 {
-	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
 	struct nouveau_mem *node = new_mem->mm_node;
 	struct nouveau_bo *nvbo = nouveau_bo(bo);
-	struct nouveau_vma *vma = &nvbo->vma;
-	struct nouveau_vm *vm = vma->vm;
+	struct nouveau_vma *vma;
 
-	if (dev_priv->card_type < NV_50)
-		return;
-
-	switch (new_mem->mem_type) {
-	case TTM_PL_VRAM:
-		nouveau_vm_map(vma, node);
-		break;
-	case TTM_PL_TT:
-		if (vma->node->type != vm->spg_shift) {
+	list_for_each_entry(vma, &nvbo->vma_list, head) {
+		if (new_mem->mem_type == TTM_PL_VRAM) {
+			nouveau_vm_map(vma, new_mem->mm_node);
+		} else
+		if (new_mem->mem_type == TTM_PL_TT &&
+		    nvbo->page_shift == vma->vm->spg_shift) {
+			nouveau_vm_map_sg(vma, 0, new_mem->
+					  num_pages << PAGE_SHIFT,
+					  node, node->pages);
+		} else {
 			nouveau_vm_unmap(vma);
-			vma = &node->tmp_vma;
 		}
-		nouveau_vm_map_sg(vma, 0, new_mem->num_pages << PAGE_SHIFT,
-				  node, node->pages);
-		break;
-	default:
-		nouveau_vm_unmap(&nvbo->vma);
-		break;
 	}
 }
 
@@ -1113,3 +1062,54 @@
 	.io_mem_free = &nouveau_ttm_io_mem_free,
 };
 
+struct nouveau_vma *
+nouveau_bo_vma_find(struct nouveau_bo *nvbo, struct nouveau_vm *vm)
+{
+	struct nouveau_vma *vma;
+	list_for_each_entry(vma, &nvbo->vma_list, head) {
+		if (vma->vm == vm)
+			return vma;
+	}
+
+	return NULL;
+}
+
+int
+nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm,
+		   struct nouveau_vma *vma)
+{
+	const u32 size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
+	struct nouveau_mem *node = nvbo->bo.mem.mm_node;
+	int ret;
+
+	ret = nouveau_vm_get(vm, size, nvbo->page_shift,
+			     NV_MEM_ACCESS_RW, vma);
+	if (ret)
+		return ret;
+
+	if (nvbo->bo.mem.mem_type == TTM_PL_VRAM)
+		nouveau_vm_map(vma, nvbo->bo.mem.mm_node);
+	else
+	if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+		nouveau_vm_map_sg(vma, 0, size, node, node->pages);
+
+	list_add_tail(&vma->head, &nvbo->vma_list);
+	vma->refcount = 1;
+	return 0;
+}
+
+void
+nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
+{
+	if (vma->node) {
+		if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) {
+			spin_lock(&nvbo->bo.bdev->fence_lock);
+			ttm_bo_wait(&nvbo->bo, false, false, false);
+			spin_unlock(&nvbo->bo.bdev->fence_lock);
+			nouveau_vm_unmap(vma);
+		}
+
+		nouveau_vm_put(vma);
+		list_del(&vma->head);
+	}
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index a7583a8..b0d753f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -27,40 +27,63 @@
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
+#include "nouveau_ramht.h"
 
 static int
-nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
+nouveau_channel_pushbuf_init(struct nouveau_channel *chan)
 {
+	u32 mem = nouveau_vram_pushbuf ? TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT;
 	struct drm_device *dev = chan->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_bo *pb = chan->pushbuf_bo;
-	struct nouveau_gpuobj *pushbuf = NULL;
-	int ret = 0;
+	int ret;
 
+	/* allocate buffer object */
+	ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, &chan->pushbuf_bo);
+	if (ret)
+		goto out;
+
+	ret = nouveau_bo_pin(chan->pushbuf_bo, mem);
+	if (ret)
+		goto out;
+
+	ret = nouveau_bo_map(chan->pushbuf_bo);
+	if (ret)
+		goto out;
+
+	/* create DMA object covering the entire memtype where the push
+	 * buffer resides, userspace can submit its own push buffers from
+	 * anywhere within the same memtype.
+	 */
+	chan->pushbuf_base = chan->pushbuf_bo->bo.offset;
 	if (dev_priv->card_type >= NV_50) {
+		ret = nouveau_bo_vma_add(chan->pushbuf_bo, chan->vm,
+					 &chan->pushbuf_vma);
+		if (ret)
+			goto out;
+
 		if (dev_priv->card_type < NV_C0) {
 			ret = nouveau_gpuobj_dma_new(chan,
 						     NV_CLASS_DMA_IN_MEMORY, 0,
 						     (1ULL << 40),
 						     NV_MEM_ACCESS_RO,
 						     NV_MEM_TARGET_VM,
-						     &pushbuf);
+						     &chan->pushbuf);
 		}
-		chan->pushbuf_base = pb->bo.offset;
+		chan->pushbuf_base = chan->pushbuf_vma.offset;
 	} else
-	if (pb->bo.mem.mem_type == TTM_PL_TT) {
+	if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_TT) {
 		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
 					     dev_priv->gart_info.aper_size,
 					     NV_MEM_ACCESS_RO,
-					     NV_MEM_TARGET_GART, &pushbuf);
-		chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+					     NV_MEM_TARGET_GART,
+					     &chan->pushbuf);
 	} else
 	if (dev_priv->card_type != NV_04) {
 		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
 					     dev_priv->fb_available_size,
 					     NV_MEM_ACCESS_RO,
-					     NV_MEM_TARGET_VRAM, &pushbuf);
-		chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+					     NV_MEM_TARGET_VRAM,
+					     &chan->pushbuf);
 	} else {
 		/* NV04 cmdbuf hack, from original ddx.. not sure of it's
 		 * exact reason for existing :)  PCI access to cmdbuf in
@@ -70,47 +93,22 @@
 					     pci_resource_start(dev->pdev, 1),
 					     dev_priv->fb_available_size,
 					     NV_MEM_ACCESS_RO,
-					     NV_MEM_TARGET_PCI, &pushbuf);
-		chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
+					     NV_MEM_TARGET_PCI,
+					     &chan->pushbuf);
 	}
 
-	nouveau_gpuobj_ref(pushbuf, &chan->pushbuf);
-	nouveau_gpuobj_ref(NULL, &pushbuf);
-	return ret;
-}
-
-static struct nouveau_bo *
-nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
-{
-	struct nouveau_bo *pushbuf = NULL;
-	int location, ret;
-
-	if (nouveau_vram_pushbuf)
-		location = TTM_PL_FLAG_VRAM;
-	else
-		location = TTM_PL_FLAG_TT;
-
-	ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, &pushbuf);
+out:
 	if (ret) {
-		NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
-		return NULL;
+		NV_ERROR(dev, "error initialising pushbuf: %d\n", ret);
+		nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
+		nouveau_gpuobj_ref(NULL, &chan->pushbuf);
+		if (chan->pushbuf_bo) {
+			nouveau_bo_unmap(chan->pushbuf_bo);
+			nouveau_bo_ref(NULL, &chan->pushbuf_bo);
+		}
 	}
 
-	ret = nouveau_bo_pin(pushbuf, location);
-	if (ret) {
-		NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
-		nouveau_bo_ref(NULL, &pushbuf);
-		return NULL;
-	}
-
-	ret = nouveau_bo_map(pushbuf);
-	if (ret) {
-		nouveau_bo_unpin(pushbuf);
-		nouveau_bo_ref(NULL, &pushbuf);
-		return NULL;
-	}
-
-	return pushbuf;
+	return 0;
 }
 
 /* allocates and initializes a fifo for user space consumption */
@@ -121,6 +119,7 @@
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
 	struct nouveau_channel *chan;
 	unsigned long flags;
 	int ret;
@@ -160,19 +159,14 @@
 	INIT_LIST_HEAD(&chan->nvsw.flip);
 	INIT_LIST_HEAD(&chan->fence.pending);
 
-	/* Allocate DMA push buffer */
-	chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
-	if (!chan->pushbuf_bo) {
-		ret = -ENOMEM;
-		NV_ERROR(dev, "pushbuf %d\n", ret);
+	/* setup channel's memory and vm */
+	ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+	if (ret) {
+		NV_ERROR(dev, "gpuobj %d\n", ret);
 		nouveau_channel_put(&chan);
 		return ret;
 	}
 
-	nouveau_dma_pre_init(chan);
-	chan->user_put = 0x40;
-	chan->user_get = 0x44;
-
 	/* Allocate space for per-channel fixed notifier memory */
 	ret = nouveau_notifier_init_channel(chan);
 	if (ret) {
@@ -181,21 +175,17 @@
 		return ret;
 	}
 
-	/* Setup channel's default objects */
-	ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle);
+	/* Allocate DMA push buffer */
+	ret = nouveau_channel_pushbuf_init(chan);
 	if (ret) {
-		NV_ERROR(dev, "gpuobj %d\n", ret);
+		NV_ERROR(dev, "pushbuf %d\n", ret);
 		nouveau_channel_put(&chan);
 		return ret;
 	}
 
-	/* Create a dma object for the push buffer */
-	ret = nouveau_channel_pushbuf_ctxdma_init(chan);
-	if (ret) {
-		NV_ERROR(dev, "pbctxdma %d\n", ret);
-		nouveau_channel_put(&chan);
-		return ret;
-	}
+	nouveau_dma_pre_init(chan);
+	chan->user_put = 0x40;
+	chan->user_get = 0x44;
 
 	/* disable the fifo caches */
 	pfifo->reassign(dev, false);
@@ -220,6 +210,11 @@
 	nouveau_debugfs_channel_init(chan);
 
 	NV_DEBUG(dev, "channel %d initialised\n", chan->id);
+	if (fpriv) {
+		spin_lock(&fpriv->lock);
+		list_add(&chan->list, &fpriv->channels);
+		spin_unlock(&fpriv->lock);
+	}
 	*chan_ret = chan;
 	return 0;
 }
@@ -236,29 +231,23 @@
 }
 
 struct nouveau_channel *
-nouveau_channel_get(struct drm_device *dev, struct drm_file *file_priv, int id)
+nouveau_channel_get(struct drm_file *file_priv, int id)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
 	struct nouveau_channel *chan;
-	unsigned long flags;
 
-	if (unlikely(id < 0 || id >= NOUVEAU_MAX_CHANNEL_NR))
-		return ERR_PTR(-EINVAL);
-
-	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	chan = nouveau_channel_get_unlocked(dev_priv->channels.ptr[id]);
-	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
-
-	if (unlikely(!chan))
-		return ERR_PTR(-EINVAL);
-
-	if (unlikely(file_priv && chan->file_priv != file_priv)) {
-		nouveau_channel_put_unlocked(&chan);
-		return ERR_PTR(-EINVAL);
+	spin_lock(&fpriv->lock);
+	list_for_each_entry(chan, &fpriv->channels, list) {
+		if (chan->id == id) {
+			chan = nouveau_channel_get_unlocked(chan);
+			spin_unlock(&fpriv->lock);
+			mutex_lock(&chan->mutex);
+			return chan;
+		}
 	}
+	spin_unlock(&fpriv->lock);
 
-	mutex_lock(&chan->mutex);
-	return chan;
+	return ERR_PTR(-EINVAL);
 }
 
 void
@@ -312,12 +301,14 @@
 	/* destroy any resources the channel owned */
 	nouveau_gpuobj_ref(NULL, &chan->pushbuf);
 	if (chan->pushbuf_bo) {
+		nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma);
 		nouveau_bo_unmap(chan->pushbuf_bo);
 		nouveau_bo_unpin(chan->pushbuf_bo);
 		nouveau_bo_ref(NULL, &chan->pushbuf_bo);
 	}
-	nouveau_gpuobj_channel_takedown(chan);
+	nouveau_ramht_ref(NULL, &chan->ramht, chan);
 	nouveau_notifier_takedown_channel(chan);
+	nouveau_gpuobj_channel_takedown(chan);
 
 	nouveau_channel_ref(NULL, pchan);
 }
@@ -383,10 +374,11 @@
 
 	NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
 	for (i = 0; i < engine->fifo.channels; i++) {
-		chan = nouveau_channel_get(dev, file_priv, i);
+		chan = nouveau_channel_get(file_priv, i);
 		if (IS_ERR(chan))
 			continue;
 
+		list_del(&chan->list);
 		atomic_dec(&chan->users);
 		nouveau_channel_put(&chan);
 	}
@@ -459,10 +451,11 @@
 	struct drm_nouveau_channel_free *req = data;
 	struct nouveau_channel *chan;
 
-	chan = nouveau_channel_get(dev, file_priv, req->channel);
+	chan = nouveau_channel_get(file_priv, req->channel);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
+	list_del(&chan->list);
 	atomic_dec(&chan->users);
 	nouveau_channel_put(&chan);
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 1595d0b..939d4df 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -40,7 +40,7 @@
 static void nouveau_connector_hotplug(void *, int);
 
 static struct nouveau_encoder *
-find_encoder_by_type(struct drm_connector *connector, int type)
+find_encoder(struct drm_connector *connector, int type)
 {
 	struct drm_device *dev = connector->dev;
 	struct nouveau_encoder *nv_encoder;
@@ -170,8 +170,8 @@
 	struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev);
 
 	if (!dn ||
-	    !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) ||
-	      (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG))))
+	    !((nv_encoder = find_encoder(connector, OUTPUT_TMDS)) ||
+	      (nv_encoder = find_encoder(connector, OUTPUT_ANALOG))))
 		return NULL;
 
 	for_each_child_of_node(dn, cn) {
@@ -233,6 +233,7 @@
 	struct drm_device *dev = connector->dev;
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	struct nouveau_encoder *nv_encoder = NULL;
+	struct nouveau_encoder *nv_partner;
 	struct nouveau_i2c_chan *i2c;
 	int type;
 
@@ -266,19 +267,22 @@
 		 * same i2c channel so the value returned from ddc_detect
 		 * isn't necessarily correct.
 		 */
-		if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
+		nv_partner = NULL;
+		if (nv_encoder->dcb->type == OUTPUT_TMDS)
+			nv_partner = find_encoder(connector, OUTPUT_ANALOG);
+		if (nv_encoder->dcb->type == OUTPUT_ANALOG)
+			nv_partner = find_encoder(connector, OUTPUT_TMDS);
+
+		if (nv_partner && ((nv_encoder->dcb->type == OUTPUT_ANALOG &&
+				    nv_partner->dcb->type == OUTPUT_TMDS) ||
+				   (nv_encoder->dcb->type == OUTPUT_TMDS &&
+				    nv_partner->dcb->type == OUTPUT_ANALOG))) {
 			if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
 				type = OUTPUT_TMDS;
 			else
 				type = OUTPUT_ANALOG;
 
-			nv_encoder = find_encoder_by_type(connector, type);
-			if (!nv_encoder) {
-				NV_ERROR(dev, "Detected %d encoder on %s, "
-					      "but no object!\n", type,
-					 drm_get_connector_name(connector));
-				return connector_status_disconnected;
-			}
+			nv_encoder = find_encoder(connector, type);
 		}
 
 		nouveau_connector_set_encoder(connector, nv_encoder);
@@ -292,9 +296,9 @@
 	}
 
 detect_analog:
-	nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
+	nv_encoder = find_encoder(connector, OUTPUT_ANALOG);
 	if (!nv_encoder && !nouveau_tv_disable)
-		nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
+		nv_encoder = find_encoder(connector, OUTPUT_TV);
 	if (nv_encoder && force) {
 		struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
 		struct drm_encoder_helper_funcs *helper =
@@ -327,7 +331,7 @@
 		nv_connector->edid = NULL;
 	}
 
-	nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+	nv_encoder = find_encoder(connector, OUTPUT_LVDS);
 	if (!nv_encoder)
 		return connector_status_disconnected;
 
@@ -405,7 +409,7 @@
 	} else
 		type = OUTPUT_ANY;
 
-	nv_encoder = find_encoder_by_type(connector, type);
+	nv_encoder = find_encoder(connector, type);
 	if (!nv_encoder) {
 		NV_ERROR(connector->dev, "can't find encoder to force %s on!\n",
 			 drm_get_connector_name(connector));
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 568caed..00bc6ea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -167,8 +167,13 @@
 	      int delta, int length)
 {
 	struct nouveau_bo *pb = chan->pushbuf_bo;
-	uint64_t offset = bo->bo.offset + delta;
+	struct nouveau_vma *vma;
 	int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;
+	u64 offset;
+
+	vma = nouveau_bo_vma_find(bo, chan->vm);
+	BUG_ON(!vma);
+	offset = vma->offset + delta;
 
 	BUG_ON(chan->dma.ib_free < 1);
 	nouveau_bo_wr32(pb, ip++, lower_32_bits(offset));
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 02c6f37..b30ddd8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -73,7 +73,7 @@
 module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
 
 MODULE_PARM_DESC(noaccel, "Disable all acceleration");
-int nouveau_noaccel = 0;
+int nouveau_noaccel = -1;
 module_param_named(noaccel, nouveau_noaccel, int, 0400);
 
 MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
@@ -119,6 +119,10 @@
 int nouveau_msi;
 module_param_named(msi, nouveau_msi, int, 0400);
 
+MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n");
+int nouveau_ctxfw;
+module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
+
 int nouveau_fbpercrtc;
 #if 0
 module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
@@ -210,10 +214,13 @@
 	pfifo->unload_context(dev);
 
 	for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
-		if (dev_priv->eng[e]) {
-			ret = dev_priv->eng[e]->fini(dev, e);
-			if (ret)
-				goto out_abort;
+		if (!dev_priv->eng[e])
+			continue;
+
+		ret = dev_priv->eng[e]->fini(dev, e, true);
+		if (ret) {
+			NV_ERROR(dev, "... engine %d failed: %d\n", i, ret);
+			goto out_abort;
 		}
 	}
 
@@ -354,7 +361,7 @@
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
-		u32 offset = nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT;
+		u32 offset = nv_crtc->cursor.nvbo->bo.offset;
 
 		nv_crtc->cursor.set_offset(nv_crtc, offset);
 		nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
@@ -389,7 +396,9 @@
 	.firstopen = nouveau_firstopen,
 	.lastclose = nouveau_lastclose,
 	.unload = nouveau_unload,
+	.open = nouveau_open,
 	.preclose = nouveau_preclose,
+	.postclose = nouveau_postclose,
 #if defined(CONFIG_DRM_NOUVEAU_DEBUG)
 	.debugfs_init = nouveau_debugfs_init,
 	.debugfs_cleanup = nouveau_debugfs_takedown,
@@ -420,6 +429,8 @@
 
 	.gem_init_object = nouveau_gem_object_new,
 	.gem_free_object = nouveau_gem_object_del,
+	.gem_open_object = nouveau_gem_object_open,
+	.gem_close_object = nouveau_gem_object_close,
 
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 9c56331..d7d51de 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -46,9 +46,17 @@
 #include "ttm/ttm_module.h"
 
 struct nouveau_fpriv {
-	struct ttm_object_file *tfile;
+	spinlock_t lock;
+	struct list_head channels;
+	struct nouveau_vm *vm;
 };
 
+static inline struct nouveau_fpriv *
+nouveau_fpriv(struct drm_file *file_priv)
+{
+	return file_priv ? file_priv->driver_priv : NULL;
+}
+
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 
 #include "nouveau_drm.h"
@@ -69,7 +77,7 @@
 	struct drm_device *dev;
 
 	struct nouveau_vma bar_vma;
-	struct nouveau_vma tmp_vma;
+	struct nouveau_vma vma[2];
 	u8  page_shift;
 
 	struct drm_mm_node *tag;
@@ -107,7 +115,8 @@
 
 	struct nouveau_channel *channel;
 
-	struct nouveau_vma vma;
+	struct list_head vma_list;
+	unsigned page_shift;
 
 	uint32_t tile_mode;
 	uint32_t tile_flags;
@@ -176,9 +185,10 @@
 	uint32_t flags;
 
 	u32 size;
-	u32 pinst;
-	u32 cinst;
-	u64 vinst;
+	u32 pinst;	/* PRAMIN BAR offset */
+	u32 cinst;	/* Channel offset */
+	u64 vinst;	/* VRAM address */
+	u64 linst;	/* VM address */
 
 	uint32_t engine;
 	uint32_t class;
@@ -201,6 +211,7 @@
 
 struct nouveau_channel {
 	struct drm_device *dev;
+	struct list_head list;
 	int id;
 
 	/* references to the channel data structure */
@@ -228,15 +239,18 @@
 		uint32_t sequence;
 		uint32_t sequence_ack;
 		atomic_t last_sequence_irq;
+		struct nouveau_vma vma;
 	} fence;
 
 	/* DMA push buffer */
 	struct nouveau_gpuobj *pushbuf;
 	struct nouveau_bo     *pushbuf_bo;
+	struct nouveau_vma     pushbuf_vma;
 	uint32_t               pushbuf_base;
 
 	/* Notifier memory */
 	struct nouveau_bo *notifier_bo;
+	struct nouveau_vma notifier_vma;
 	struct drm_mm notifier_heap;
 
 	/* PFIFO context */
@@ -278,6 +292,7 @@
 
 	uint32_t sw_subchannel[8];
 
+	struct nouveau_vma dispc_vma[2];
 	struct {
 		struct nouveau_gpuobj *vblsem;
 		uint32_t vblsem_head;
@@ -297,7 +312,7 @@
 struct nouveau_exec_engine {
 	void (*destroy)(struct drm_device *, int engine);
 	int  (*init)(struct drm_device *, int engine);
-	int  (*fini)(struct drm_device *, int engine);
+	int  (*fini)(struct drm_device *, int engine, bool suspend);
 	int  (*context_new)(struct nouveau_channel *, int engine);
 	void (*context_del)(struct nouveau_channel *, int engine);
 	int  (*object_new)(struct nouveau_channel *, int engine,
@@ -314,7 +329,8 @@
 	int	(*suspend)(struct drm_device *dev);
 	void	(*resume)(struct drm_device *dev);
 
-	int	(*get)(struct nouveau_gpuobj *, u32 size, u32 align);
+	int	(*get)(struct nouveau_gpuobj *, struct nouveau_channel *,
+		       u32 size, u32 align);
 	void	(*put)(struct nouveau_gpuobj *);
 	int	(*map)(struct nouveau_gpuobj *);
 	void	(*unmap)(struct nouveau_gpuobj *);
@@ -445,9 +461,9 @@
 struct nouveau_pm_temp_sensor_constants {
 	u16 offset_constant;
 	s16 offset_mult;
-	u16 offset_div;
-	u16 slope_mult;
-	u16 slope_div;
+	s16 offset_div;
+	s16 slope_mult;
+	s16 slope_div;
 };
 
 struct nouveau_pm_threshold_temp {
@@ -488,7 +504,10 @@
 };
 
 struct nouveau_vram_engine {
+	struct nouveau_mm *mm;
+
 	int  (*init)(struct drm_device *);
+	void (*takedown)(struct drm_device *dev);
 	int  (*get)(struct drm_device *, u64, u32 align, u32 size_nc,
 		    u32 type, struct nouveau_mem **);
 	void (*put)(struct drm_device *, struct nouveau_mem **);
@@ -608,6 +627,7 @@
 
 struct drm_nouveau_private {
 	struct drm_device *dev;
+	bool noaccel;
 
 	/* the card type, takes NV_* as values */
 	enum nouveau_card_type card_type;
@@ -700,7 +720,6 @@
 	/* VRAM/fb configuration */
 	uint64_t vram_size;
 	uint64_t vram_sys_base;
-	u32 vram_rblock_size;
 
 	uint64_t fb_phys;
 	uint64_t fb_available_size;
@@ -784,12 +803,15 @@
 extern char *nouveau_perflvl;
 extern int nouveau_perflvl_wr;
 extern int nouveau_msi;
+extern int nouveau_ctxfw;
 
 extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
 extern int nouveau_pci_resume(struct pci_dev *pdev);
 
 /* nouveau_state.c */
+extern int  nouveau_open(struct drm_device *, struct drm_file *);
 extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
+extern void nouveau_postclose(struct drm_device *, struct drm_file *);
 extern int  nouveau_load(struct drm_device *, unsigned long flags);
 extern int  nouveau_firstopen(struct drm_device *);
 extern void nouveau_lastclose(struct drm_device *);
@@ -847,7 +869,7 @@
 extern struct nouveau_channel *
 nouveau_channel_get_unlocked(struct nouveau_channel *);
 extern struct nouveau_channel *
-nouveau_channel_get(struct drm_device *, struct drm_file *, int id);
+nouveau_channel_get(struct drm_file *, int id);
 extern void nouveau_channel_put_unlocked(struct nouveau_channel **);
 extern void nouveau_channel_put(struct nouveau_channel **);
 extern void nouveau_channel_ref(struct nouveau_channel *chan,
@@ -1120,7 +1142,6 @@
 
 /* nv04_graph.c */
 extern int  nv04_graph_create(struct drm_device *);
-extern void nv04_graph_fifo_access(struct drm_device *, bool);
 extern int  nv04_graph_object_new(struct nouveau_channel *, int, u32, u16);
 extern int  nv04_graph_mthd_page_flip(struct nouveau_channel *chan,
 				      u32 class, u32 mthd, u32 data);
@@ -1169,7 +1190,8 @@
 extern void nv04_instmem_takedown(struct drm_device *);
 extern int  nv04_instmem_suspend(struct drm_device *);
 extern void nv04_instmem_resume(struct drm_device *);
-extern int  nv04_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align);
+extern int  nv04_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *,
+			     u32 size, u32 align);
 extern void nv04_instmem_put(struct nouveau_gpuobj *);
 extern int  nv04_instmem_map(struct nouveau_gpuobj *);
 extern void nv04_instmem_unmap(struct nouveau_gpuobj *);
@@ -1180,7 +1202,8 @@
 extern void nv50_instmem_takedown(struct drm_device *);
 extern int  nv50_instmem_suspend(struct drm_device *);
 extern void nv50_instmem_resume(struct drm_device *);
-extern int  nv50_instmem_get(struct nouveau_gpuobj *, u32 size, u32 align);
+extern int  nv50_instmem_get(struct nouveau_gpuobj *, struct nouveau_channel *,
+			     u32 size, u32 align);
 extern void nv50_instmem_put(struct nouveau_gpuobj *);
 extern int  nv50_instmem_map(struct nouveau_gpuobj *);
 extern void nv50_instmem_unmap(struct nouveau_gpuobj *);
@@ -1247,10 +1270,9 @@
 
 /* nouveau_bo.c */
 extern struct ttm_bo_driver nouveau_bo_driver;
-extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *,
-			  int size, int align, uint32_t flags,
-			  uint32_t tile_mode, uint32_t tile_flags,
-			  struct nouveau_bo **);
+extern int nouveau_bo_new(struct drm_device *, int size, int align,
+			  uint32_t flags, uint32_t tile_mode,
+			  uint32_t tile_flags, struct nouveau_bo **);
 extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
 extern int nouveau_bo_unpin(struct nouveau_bo *);
 extern int nouveau_bo_map(struct nouveau_bo *);
@@ -1265,6 +1287,12 @@
 extern int nouveau_bo_validate(struct nouveau_bo *, bool interruptible,
 			       bool no_wait_reserve, bool no_wait_gpu);
 
+extern struct nouveau_vma *
+nouveau_bo_vma_find(struct nouveau_bo *, struct nouveau_vm *);
+extern int  nouveau_bo_vma_add(struct nouveau_bo *, struct nouveau_vm *,
+			       struct nouveau_vma *);
+extern void nouveau_bo_vma_del(struct nouveau_bo *, struct nouveau_vma *);
+
 /* nouveau_fence.c */
 struct nouveau_fence;
 extern int nouveau_fence_init(struct drm_device *);
@@ -1310,12 +1338,14 @@
 }
 
 /* nouveau_gem.c */
-extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
-			   int size, int align, uint32_t domain,
-			   uint32_t tile_mode, uint32_t tile_flags,
-			   struct nouveau_bo **);
+extern int nouveau_gem_new(struct drm_device *, int size, int align,
+			   uint32_t domain, uint32_t tile_mode,
+			   uint32_t tile_flags, struct nouveau_bo **);
 extern int nouveau_gem_object_new(struct drm_gem_object *);
 extern void nouveau_gem_object_del(struct drm_gem_object *);
+extern int nouveau_gem_object_open(struct drm_gem_object *, struct drm_file *);
+extern void nouveau_gem_object_close(struct drm_gem_object *,
+				     struct drm_file *);
 extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
 				 struct drm_file *);
 extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
index a3a88ad..95c843e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fb.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
@@ -30,6 +30,7 @@
 struct nouveau_framebuffer {
 	struct drm_framebuffer base;
 	struct nouveau_bo *nvbo;
+	struct nouveau_vma vma;
 	u32 r_dma;
 	u32 r_format;
 	u32 r_pitch;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 39aee6d..14a8627 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -279,6 +279,7 @@
 	struct fb_info *info;
 	struct drm_framebuffer *fb;
 	struct nouveau_framebuffer *nouveau_fb;
+	struct nouveau_channel *chan;
 	struct nouveau_bo *nvbo;
 	struct drm_mode_fb_cmd mode_cmd;
 	struct pci_dev *pdev = dev->pdev;
@@ -296,8 +297,8 @@
 	size = mode_cmd.pitch * mode_cmd.height;
 	size = roundup(size, PAGE_SIZE);
 
-	ret = nouveau_gem_new(dev, dev_priv->channel, size, 0,
-			      NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, &nvbo);
+	ret = nouveau_gem_new(dev, size, 0, NOUVEAU_GEM_DOMAIN_VRAM,
+			      0, 0x0000, &nvbo);
 	if (ret) {
 		NV_ERROR(dev, "failed to allocate framebuffer\n");
 		goto out;
@@ -318,6 +319,15 @@
 		goto out;
 	}
 
+	chan = nouveau_nofbaccel ? NULL : dev_priv->channel;
+	if (chan && dev_priv->card_type >= NV_50) {
+		ret = nouveau_bo_vma_add(nvbo, chan->vm, &nfbdev->nouveau_fb.vma);
+		if (ret) {
+			NV_ERROR(dev, "failed to map fb into chan: %d\n", ret);
+			chan = NULL;
+		}
+	}
+
 	mutex_lock(&dev->struct_mutex);
 
 	info = framebuffer_alloc(0, device);
@@ -448,6 +458,7 @@
 
 	if (nouveau_fb->nvbo) {
 		nouveau_bo_unmap(nouveau_fb->nvbo);
+		nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma);
 		drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
 		nouveau_fb->nvbo = NULL;
 	}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 7347075..8d02d87 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -336,6 +336,7 @@
 {
 	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
 	struct nouveau_fence *fence = NULL;
+	u64 offset = chan->fence.vma.offset + sema->mem->start;
 	int ret;
 
 	if (dev_priv->chipset < 0x84) {
@@ -345,13 +346,10 @@
 
 		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3);
 		OUT_RING  (chan, NvSema);
-		OUT_RING  (chan, sema->mem->start);
+		OUT_RING  (chan, offset);
 		OUT_RING  (chan, 1);
 	} else
 	if (dev_priv->chipset < 0xc0) {
-		struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
-		u64 offset = vma->offset + sema->mem->start;
-
 		ret = RING_SPACE(chan, 7);
 		if (ret)
 			return ret;
@@ -364,9 +362,6 @@
 		OUT_RING  (chan, 1);
 		OUT_RING  (chan, 1); /* ACQUIRE_EQ */
 	} else {
-		struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
-		u64 offset = vma->offset + sema->mem->start;
-
 		ret = RING_SPACE(chan, 5);
 		if (ret)
 			return ret;
@@ -394,6 +389,7 @@
 {
 	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
 	struct nouveau_fence *fence = NULL;
+	u64 offset = chan->fence.vma.offset + sema->mem->start;
 	int ret;
 
 	if (dev_priv->chipset < 0x84) {
@@ -403,14 +399,11 @@
 
 		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2);
 		OUT_RING  (chan, NvSema);
-		OUT_RING  (chan, sema->mem->start);
+		OUT_RING  (chan, offset);
 		BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1);
 		OUT_RING  (chan, 1);
 	} else
 	if (dev_priv->chipset < 0xc0) {
-		struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
-		u64 offset = vma->offset + sema->mem->start;
-
 		ret = RING_SPACE(chan, 7);
 		if (ret)
 			return ret;
@@ -423,9 +416,6 @@
 		OUT_RING  (chan, 1);
 		OUT_RING  (chan, 2); /* RELEASE */
 	} else {
-		struct nouveau_vma *vma = &dev_priv->fence.bo->vma;
-		u64 offset = vma->offset + sema->mem->start;
-
 		ret = RING_SPACE(chan, 5);
 		if (ret)
 			return ret;
@@ -540,6 +530,12 @@
 		nouveau_gpuobj_ref(NULL, &obj);
 		if (ret)
 			return ret;
+	} else {
+		/* map fence bo into channel's vm */
+		ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm,
+					 &chan->fence.vma);
+		if (ret)
+			return ret;
 	}
 
 	INIT_LIST_HEAD(&chan->fence.pending);
@@ -551,10 +547,10 @@
 void
 nouveau_fence_channel_fini(struct nouveau_channel *chan)
 {
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
 	struct nouveau_fence *tmp, *fence;
 
 	spin_lock(&chan->fence.lock);
-
 	list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
 		fence->signalled = true;
 		list_del(&fence->entry);
@@ -564,8 +560,9 @@
 
 		kref_put(&fence->refcount, nouveau_fence_del);
 	}
-
 	spin_unlock(&chan->fence.lock);
+
+	nouveau_bo_vma_del(dev_priv->fence.bo, &chan->fence.vma);
 }
 
 int
@@ -577,7 +574,7 @@
 
 	/* Create a shared VRAM heap for cross-channel sync. */
 	if (USE_SEMA(dev)) {
-		ret = nouveau_bo_new(dev, NULL, size, 0, TTM_PL_FLAG_VRAM,
+		ret = nouveau_bo_new(dev, size, 0, TTM_PL_FLAG_VRAM,
 				     0, 0, &dev_priv->fence.bo);
 		if (ret)
 			return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index b52e460..5f0bc57 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -60,9 +60,71 @@
 }
 
 int
-nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
-		int size, int align, uint32_t domain, uint32_t tile_mode,
-		uint32_t tile_flags, struct nouveau_bo **pnvbo)
+nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
+{
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+	struct nouveau_vma *vma;
+	int ret;
+
+	if (!fpriv->vm)
+		return 0;
+
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	if (ret)
+		return ret;
+
+	vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+	if (!vma) {
+		vma = kzalloc(sizeof(*vma), GFP_KERNEL);
+		if (!vma) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		ret = nouveau_bo_vma_add(nvbo, fpriv->vm, vma);
+		if (ret) {
+			kfree(vma);
+			goto out;
+		}
+	} else {
+		vma->refcount++;
+	}
+
+out:
+	ttm_bo_unreserve(&nvbo->bo);
+	return ret;
+}
+
+void
+nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
+{
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+	struct nouveau_vma *vma;
+	int ret;
+
+	if (!fpriv->vm)
+		return;
+
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	if (ret)
+		return;
+
+	vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+	if (vma) {
+		if (--vma->refcount == 0) {
+			nouveau_bo_vma_del(nvbo, vma);
+			kfree(vma);
+		}
+	}
+	ttm_bo_unreserve(&nvbo->bo);
+}
+
+int
+nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain,
+		uint32_t tile_mode, uint32_t tile_flags,
+		struct nouveau_bo **pnvbo)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_bo *nvbo;
@@ -76,7 +138,7 @@
 	if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU)
 		flags |= TTM_PL_FLAG_SYSTEM;
 
-	ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
+	ret = nouveau_bo_new(dev, size, align, flags, tile_mode,
 			     tile_flags, pnvbo);
 	if (ret)
 		return ret;
@@ -103,17 +165,28 @@
 }
 
 static int
-nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
+nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
+		 struct drm_nouveau_gem_info *rep)
 {
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
 	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+	struct nouveau_vma *vma;
 
 	if (nvbo->bo.mem.mem_type == TTM_PL_TT)
 		rep->domain = NOUVEAU_GEM_DOMAIN_GART;
 	else
 		rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
 
-	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
 	rep->offset = nvbo->bo.offset;
+	if (fpriv->vm) {
+		vma = nouveau_bo_vma_find(nvbo, fpriv->vm);
+		if (!vma)
+			return -EINVAL;
+
+		rep->offset = vma->offset;
+	}
+
+	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
 	rep->map_handle = nvbo->bo.addr_space_offset;
 	rep->tile_mode = nvbo->tile_mode;
 	rep->tile_flags = nvbo->tile_flags;
@@ -127,7 +200,6 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct drm_nouveau_gem_new *req = data;
 	struct nouveau_bo *nvbo = NULL;
-	struct nouveau_channel *chan = NULL;
 	int ret = 0;
 
 	if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
@@ -138,28 +210,21 @@
 		return -EINVAL;
 	}
 
-	if (req->channel_hint) {
-		chan = nouveau_channel_get(dev, file_priv, req->channel_hint);
-		if (IS_ERR(chan))
-			return PTR_ERR(chan);
-	}
-
-	ret = nouveau_gem_new(dev, chan, req->info.size, req->align,
+	ret = nouveau_gem_new(dev, req->info.size, req->align,
 			      req->info.domain, req->info.tile_mode,
 			      req->info.tile_flags, &nvbo);
-	if (chan)
-		nouveau_channel_put(&chan);
 	if (ret)
 		return ret;
 
-	ret = nouveau_gem_info(nvbo->gem, &req->info);
-	if (ret)
-		goto out;
-
 	ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
+	if (ret == 0) {
+		ret = nouveau_gem_info(file_priv, nvbo->gem, &req->info);
+		if (ret)
+			drm_gem_handle_delete(file_priv, req->info.handle);
+	}
+
 	/* drop reference from allocate - handle holds it now */
 	drm_gem_object_unreference_unlocked(nvbo->gem);
-out:
 	return ret;
 }
 
@@ -318,6 +383,7 @@
 validate_list(struct nouveau_channel *chan, struct list_head *list,
 	      struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
 {
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
 	struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
 				(void __force __user *)(uintptr_t)user_pbbo_ptr;
 	struct drm_device *dev = chan->dev;
@@ -356,24 +422,26 @@
 			return ret;
 		}
 
-		if (nvbo->bo.offset == b->presumed.offset &&
-		    ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
-		      b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
-		     (nvbo->bo.mem.mem_type == TTM_PL_TT &&
-		      b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART)))
-			continue;
+		if (dev_priv->card_type < NV_50) {
+			if (nvbo->bo.offset == b->presumed.offset &&
+			    ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
+			      b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
+			     (nvbo->bo.mem.mem_type == TTM_PL_TT &&
+			      b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART)))
+				continue;
 
-		if (nvbo->bo.mem.mem_type == TTM_PL_TT)
-			b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
-		else
-			b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
-		b->presumed.offset = nvbo->bo.offset;
-		b->presumed.valid = 0;
-		relocs++;
+			if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+				b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
+			else
+				b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
+			b->presumed.offset = nvbo->bo.offset;
+			b->presumed.valid = 0;
+			relocs++;
 
-		if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed,
-				     &b->presumed, sizeof(b->presumed)))
-			return -EFAULT;
+			if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed,
+					     &b->presumed, sizeof(b->presumed)))
+				return -EFAULT;
+		}
 	}
 
 	return relocs;
@@ -548,7 +616,7 @@
 	struct nouveau_fence *fence = NULL;
 	int i, j, ret = 0, do_reloc = 0;
 
-	chan = nouveau_channel_get(dev, file_priv, req->channel);
+	chan = nouveau_channel_get(file_priv, req->channel);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
@@ -782,7 +850,7 @@
 	if (!gem)
 		return -ENOENT;
 
-	ret = nouveau_gem_info(gem, req);
+	ret = nouveau_gem_info(file_priv, gem, req);
 	drm_gem_object_unreference_unlocked(gem);
 	return ret;
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 2ba7265..868c7fd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -79,7 +79,7 @@
 	int i;
 
 	stat = nv_rd32(dev, NV03_PMC_INTR_0);
-	if (!stat)
+	if (stat == 0 || stat == ~0)
 		return IRQ_NONE;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 5ee14d2..f9ae2fc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -397,7 +397,7 @@
 		if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
 			dma_bits = 40;
 	} else
-	if (0 && drm_pci_device_is_pcie(dev) &&
+	if (0 && pci_is_pcie(dev->pdev) &&
 	    dev_priv->chipset  > 0x40 &&
 	    dev_priv->chipset != 0x45) {
 		if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39)))
@@ -423,38 +423,6 @@
 		return ret;
 	}
 
-	/* reserve space at end of VRAM for PRAMIN */
-	if (dev_priv->card_type >= NV_50) {
-		dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024;
-	} else
-	if (dev_priv->card_type >= NV_40) {
-		u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8);
-		u32 rsvd;
-
-		/* estimate grctx size, the magics come from nv40_grctx.c */
-		if      (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs;
-		else if (dev_priv->chipset  < 0x43) rsvd = 0x4f00 * vs;
-		else if (nv44_graph_class(dev))	    rsvd = 0x4980 * vs;
-		else				    rsvd = 0x4a40 * vs;
-		rsvd += 16 * 1024;
-		rsvd *= dev_priv->engine.fifo.channels;
-
-		/* pciegart table */
-		if (drm_pci_device_is_pcie(dev))
-			rsvd += 512 * 1024;
-
-		/* object storage */
-		rsvd += 512 * 1024;
-
-		dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096);
-	} else {
-		dev_priv->ramin_rsvd_vram = 512 * 1024;
-	}
-
-	ret = dev_priv->engine.vram.init(dev);
-	if (ret)
-		return ret;
-
 	NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
 	if (dev_priv->vram_sys_base) {
 		NV_INFO(dev, "Stolen system memory at: 0x%010llx\n",
@@ -479,7 +447,7 @@
 	}
 
 	if (dev_priv->card_type < NV_50) {
-		ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM,
+		ret = nouveau_bo_new(dev, 256*1024, 0, TTM_PL_FLAG_VRAM,
 				     0, 0, &dev_priv->vga_ram);
 		if (ret == 0)
 			ret = nouveau_bo_pin(dev_priv->vga_ram,
@@ -729,53 +697,42 @@
 }
 
 static int
-nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long p_size)
+nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize)
 {
-	struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
-	struct nouveau_mm *mm;
-	u64 size, block, rsvd;
-	int ret;
-
-	rsvd  = (256 * 1024); /* vga memory */
-	size  = (p_size << PAGE_SHIFT) - rsvd;
-	block = dev_priv->vram_rblock_size;
-
-	ret = nouveau_mm_init(&mm, rsvd >> 12, size >> 12, block >> 12);
-	if (ret)
-		return ret;
-
-	man->priv = mm;
+	/* nothing to do */
 	return 0;
 }
 
 static int
 nouveau_vram_manager_fini(struct ttm_mem_type_manager *man)
 {
-	struct nouveau_mm *mm = man->priv;
-	int ret;
-
-	ret = nouveau_mm_fini(&mm);
-	if (ret)
-		return ret;
-
-	man->priv = NULL;
+	/* nothing to do */
 	return 0;
 }
 
+static inline void
+nouveau_mem_node_cleanup(struct nouveau_mem *node)
+{
+	if (node->vma[0].node) {
+		nouveau_vm_unmap(&node->vma[0]);
+		nouveau_vm_put(&node->vma[0]);
+	}
+
+	if (node->vma[1].node) {
+		nouveau_vm_unmap(&node->vma[1]);
+		nouveau_vm_put(&node->vma[1]);
+	}
+}
+
 static void
 nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
 			 struct ttm_mem_reg *mem)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
 	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
-	struct nouveau_mem *node = mem->mm_node;
 	struct drm_device *dev = dev_priv->dev;
 
-	if (node->tmp_vma.node) {
-		nouveau_vm_unmap(&node->tmp_vma);
-		nouveau_vm_put(&node->tmp_vma);
-	}
-
+	nouveau_mem_node_cleanup(mem->mm_node);
 	vram->put(dev, (struct nouveau_mem **)&mem->mm_node);
 }
 
@@ -794,7 +751,7 @@
 	int ret;
 
 	if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG)
-		size_nc = 1 << nvbo->vma.node->type;
+		size_nc = 1 << nvbo->page_shift;
 
 	ret = vram->get(dev, mem->num_pages << PAGE_SHIFT,
 			mem->page_alignment << PAGE_SHIFT, size_nc,
@@ -804,9 +761,7 @@
 		return (ret == -ENOSPC) ? 0 : ret;
 	}
 
-	node->page_shift = 12;
-	if (nvbo->vma.node)
-		node->page_shift = nvbo->vma.node->type;
+	node->page_shift = nvbo->page_shift;
 
 	mem->mm_node = node;
 	mem->start   = node->offset >> PAGE_SHIFT;
@@ -862,15 +817,9 @@
 nouveau_gart_manager_del(struct ttm_mem_type_manager *man,
 			 struct ttm_mem_reg *mem)
 {
-	struct nouveau_mem *node = mem->mm_node;
-
-	if (node->tmp_vma.node) {
-		nouveau_vm_unmap(&node->tmp_vma);
-		nouveau_vm_put(&node->tmp_vma);
-	}
-
+	nouveau_mem_node_cleanup(mem->mm_node);
+	kfree(mem->mm_node);
 	mem->mm_node = NULL;
-	kfree(node);
 }
 
 static int
@@ -880,11 +829,7 @@
 			 struct ttm_mem_reg *mem)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
-	struct nouveau_bo *nvbo = nouveau_bo(bo);
-	struct nouveau_vma *vma = &nvbo->vma;
-	struct nouveau_vm *vm = vma->vm;
 	struct nouveau_mem *node;
-	int ret;
 
 	if (unlikely((mem->num_pages << PAGE_SHIFT) >=
 		     dev_priv->gart_info.aper_size))
@@ -893,24 +838,8 @@
 	node = kzalloc(sizeof(*node), GFP_KERNEL);
 	if (!node)
 		return -ENOMEM;
+	node->page_shift = 12;
 
-	/* This node must be for evicting large-paged VRAM
-	 * to system memory.  Due to a nv50 limitation of
-	 * not being able to mix large/small pages within
-	 * the same PDE, we need to create a temporary
-	 * small-paged VMA for the eviction.
-	 */
-	if (vma->node->type != vm->spg_shift) {
-		ret = nouveau_vm_get(vm, (u64)vma->node->length << 12,
-				     vm->spg_shift, NV_MEM_ACCESS_RW,
-				     &node->tmp_vma);
-		if (ret) {
-			kfree(node);
-			return ret;
-		}
-	}
-
-	node->page_shift = nvbo->vma.node->type;
 	mem->mm_node = node;
 	mem->start   = 0;
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c
index 7609756..1640dec 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.c
@@ -158,11 +158,18 @@
 nouveau_mm_fini(struct nouveau_mm **prmm)
 {
 	struct nouveau_mm *rmm = *prmm;
-	struct nouveau_mm_node *heap =
+	struct nouveau_mm_node *node, *heap =
 		list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);
 
-	if (!list_is_singular(&rmm->nodes))
+	if (!list_is_singular(&rmm->nodes)) {
+		printk(KERN_ERR "nouveau_mm not empty at destroy time!\n");
+		list_for_each_entry(node, &rmm->nodes, nl_entry) {
+			printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
+			       node->type, node->offset, node->length);
+		}
+		WARN_ON(1);
 		return -EBUSY;
+	}
 
 	kfree(heap);
 	kfree(rmm);
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h
index 1f7483a..b9c016d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.h
@@ -52,6 +52,7 @@
 void nouveau_mm_put(struct nouveau_mm *, struct nouveau_mm_node *);
 
 int  nv50_vram_init(struct drm_device *);
+void nv50_vram_fini(struct drm_device *);
 int  nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc,
 		    u32 memtype, struct nouveau_mem **);
 void nv50_vram_del(struct drm_device *, struct nouveau_mem **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
index 5b39718..6abdbe6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_notifier.c
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
@@ -34,6 +34,7 @@
 nouveau_notifier_init_channel(struct nouveau_channel *chan)
 {
 	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_bo *ntfy = NULL;
 	uint32_t flags, ttmpl;
 	int ret;
@@ -46,7 +47,7 @@
 		ttmpl = TTM_PL_FLAG_TT;
 	}
 
-	ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
+	ret = nouveau_gem_new(dev, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
 	if (ret)
 		return ret;
 
@@ -58,14 +59,22 @@
 	if (ret)
 		goto out_err;
 
+	if (dev_priv->card_type >= NV_50) {
+		ret = nouveau_bo_vma_add(ntfy, chan->vm, &chan->notifier_vma);
+		if (ret)
+			goto out_err;
+	}
+
 	ret = drm_mm_init(&chan->notifier_heap, 0, ntfy->bo.mem.size);
 	if (ret)
 		goto out_err;
 
 	chan->notifier_bo = ntfy;
 out_err:
-	if (ret)
+	if (ret) {
+		nouveau_bo_vma_del(ntfy, &chan->notifier_vma);
 		drm_gem_object_unreference_unlocked(ntfy->gem);
+	}
 
 	return ret;
 }
@@ -78,6 +87,7 @@
 	if (!chan->notifier_bo)
 		return;
 
+	nouveau_bo_vma_del(chan->notifier_bo, &chan->notifier_vma);
 	nouveau_bo_unmap(chan->notifier_bo);
 	mutex_lock(&dev->struct_mutex);
 	nouveau_bo_unpin(chan->notifier_bo);
@@ -122,10 +132,10 @@
 			target = NV_MEM_TARGET_VRAM;
 		else
 			target = NV_MEM_TARGET_GART;
-		offset  = chan->notifier_bo->bo.mem.start << PAGE_SHIFT;
+		offset  = chan->notifier_bo->bo.offset;
 	} else {
 		target = NV_MEM_TARGET_VM;
-		offset = chan->notifier_bo->vma.offset;
+		offset = chan->notifier_vma.offset;
 	}
 	offset += mem->start;
 
@@ -183,7 +193,7 @@
 	if (unlikely(dev_priv->card_type >= NV_C0))
 		return -EINVAL;
 
-	chan = nouveau_channel_get(dev, file_priv, na->channel);
+	chan = nouveau_channel_get(file_priv, na->channel);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index 8f97016..159b7c4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -125,7 +125,7 @@
 	int ret = -EINVAL;
 
 	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	if (chid > 0 && chid < dev_priv->engine.fifo.channels)
+	if (chid >= 0 && chid < dev_priv->engine.fifo.channels)
 		chan = dev_priv->channels.ptr[chid];
 	if (chan)
 		ret = nouveau_gpuobj_mthd_call(chan, class, mthd, data);
@@ -191,7 +191,7 @@
 	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
 	spin_unlock(&dev_priv->ramin_lock);
 
-	if (chan) {
+	if (!(flags & NVOBJ_FLAG_VM) && chan) {
 		ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0);
 		if (ramin)
 			ramin = drm_mm_get_block(ramin, size, align);
@@ -208,7 +208,7 @@
 		gpuobj->vinst = ramin->start + chan->ramin->vinst;
 		gpuobj->node  = ramin;
 	} else {
-		ret = instmem->get(gpuobj, size, align);
+		ret = instmem->get(gpuobj, chan, size, align);
 		if (ret) {
 			nouveau_gpuobj_ref(NULL, &gpuobj);
 			return ret;
@@ -690,35 +690,64 @@
 	return 0;
 }
 
+static int
+nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
+{
+	struct drm_device *dev = chan->dev;
+	struct nouveau_gpuobj *pgd = NULL;
+	struct nouveau_vm_pgd *vpgd;
+	int ret, i;
+
+	ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0, &chan->ramin);
+	if (ret)
+		return ret;
+
+	/* create page directory for this vm if none currently exists,
+	 * will be destroyed automagically when last reference to the
+	 * vm is removed
+	 */
+	if (list_empty(&vm->pgd_list)) {
+		ret = nouveau_gpuobj_new(dev, NULL, 65536, 0x1000, 0, &pgd);
+		if (ret)
+			return ret;
+	}
+	nouveau_vm_ref(vm, &chan->vm, pgd);
+	nouveau_gpuobj_ref(NULL, &pgd);
+
+	/* point channel at vm's page directory */
+	vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head);
+	nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst));
+	nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst));
+	nv_wo32(chan->ramin, 0x0208, 0xffffffff);
+	nv_wo32(chan->ramin, 0x020c, 0x000000ff);
+
+	/* map display semaphore buffers into channel's vm */
+	for (i = 0; i < 2; i++) {
+		struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i];
+
+		ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm,
+					 &chan->dispc_vma[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 int
 nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
 			    uint32_t vram_h, uint32_t tt_h)
 {
 	struct drm_device *dev = chan->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(chan->file_priv);
+	struct nouveau_vm *vm = fpriv ? fpriv->vm : dev_priv->chan_vm;
 	struct nouveau_gpuobj *vram = NULL, *tt = NULL;
 	int ret, i;
 
 	NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
-
-	if (dev_priv->card_type == NV_C0) {
-		struct nouveau_vm *vm = dev_priv->chan_vm;
-		struct nouveau_vm_pgd *vpgd;
-
-		ret = nouveau_gpuobj_new(dev, NULL, 4096, 0x1000, 0,
-					 &chan->ramin);
-		if (ret)
-			return ret;
-
-		nouveau_vm_ref(vm, &chan->vm, NULL);
-
-		vpgd = list_first_entry(&vm->pgd_list, struct nouveau_vm_pgd, head);
-		nv_wo32(chan->ramin, 0x0200, lower_32_bits(vpgd->obj->vinst));
-		nv_wo32(chan->ramin, 0x0204, upper_32_bits(vpgd->obj->vinst));
-		nv_wo32(chan->ramin, 0x0208, 0xffffffff);
-		nv_wo32(chan->ramin, 0x020c, 0x000000ff);
-		return 0;
-	}
+	if (dev_priv->card_type == NV_C0)
+		return nvc0_gpuobj_channel_init(chan, vm);
 
 	/* Allocate a chunk of memory for per-channel object storage */
 	ret = nouveau_gpuobj_channel_init_pramin(chan);
@@ -731,7 +760,7 @@
 	 *  - Allocate per-channel page-directory
 	 *  - Link with shared channel VM
 	 */
-	if (dev_priv->chan_vm) {
+	if (vm) {
 		u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200;
 		u64 vm_vinst = chan->ramin->vinst + pgd_offs;
 		u32 vm_pinst = chan->ramin->pinst;
@@ -744,7 +773,7 @@
 		if (ret)
 			return ret;
 
-		nouveau_vm_ref(dev_priv->chan_vm, &chan->vm, chan->vm_pd);
+		nouveau_vm_ref(vm, &chan->vm, chan->vm_pd);
 	}
 
 	/* RAMHT */
@@ -768,7 +797,7 @@
 			struct nouveau_gpuobj *sem = NULL;
 			struct nv50_display_crtc *dispc =
 				&nv50_display(dev)->crtc[i];
-			u64 offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT;
+			u64 offset = dispc->sem.bo->bo.offset;
 
 			ret = nouveau_gpuobj_dma_new(chan, 0x3d, offset, 0xfff,
 						     NV_MEM_ACCESS_RW,
@@ -841,13 +870,22 @@
 nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
 {
 	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i;
 
 	NV_DEBUG(dev, "ch%d\n", chan->id);
 
-	nouveau_ramht_ref(NULL, &chan->ramht, chan);
+	if (dev_priv->card_type >= NV_50) {
+		struct nv50_display *disp = nv50_display(dev);
 
-	nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
-	nouveau_gpuobj_ref(NULL, &chan->vm_pd);
+		for (i = 0; i < 2; i++) {
+			struct nv50_display_crtc *dispc = &disp->crtc[i];
+			nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]);
+		}
+
+		nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
+		nouveau_gpuobj_ref(NULL, &chan->vm_pd);
+	}
 
 	if (drm_mm_initialized(&chan->ramin_heap))
 		drm_mm_takedown(&chan->ramin_heap);
@@ -909,7 +947,7 @@
 	if (init->handle == ~0)
 		return -EINVAL;
 
-	chan = nouveau_channel_get(dev, file_priv, init->channel);
+	chan = nouveau_channel_get(file_priv, init->channel);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
@@ -936,7 +974,7 @@
 	struct nouveau_channel *chan;
 	int ret;
 
-	chan = nouveau_channel_get(dev, file_priv, objfree->channel);
+	chan = nouveau_channel_get(file_priv, objfree->channel);
 	if (IS_ERR(chan))
 		return PTR_ERR(chan);
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index 82fad91..c444cad 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -429,7 +429,7 @@
 	u32 aper_size, align;
 	int ret;
 
-	if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev))
+	if (dev_priv->card_type >= NV_40 && pci_is_pcie(dev->pdev))
 		aper_size = 512 * 1024 * 1024;
 	else
 		aper_size = 64 * 1024 * 1024;
@@ -458,7 +458,7 @@
 		dev_priv->gart_info.type = NOUVEAU_GART_HW;
 		dev_priv->gart_info.func = &nv50_sgdma_backend;
 	} else
-	if (0 && drm_pci_device_is_pcie(dev) &&
+	if (0 && pci_is_pcie(dev->pdev) &&
 	    dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) {
 		if (nv44_graph_class(dev)) {
 			dev_priv->gart_info.func = &nv44_sgdma_backend;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 731acea..10656e4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -91,6 +91,7 @@
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
 		engine->vram.init		= nouveau_mem_detect;
+		engine->vram.takedown		= nouveau_stub_takedown;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x10:
@@ -139,6 +140,7 @@
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
 		engine->vram.init		= nouveau_mem_detect;
+		engine->vram.takedown		= nouveau_stub_takedown;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x20:
@@ -187,6 +189,7 @@
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
 		engine->vram.init		= nouveau_mem_detect;
+		engine->vram.takedown		= nouveau_stub_takedown;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x30:
@@ -237,6 +240,7 @@
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
 		engine->vram.init		= nouveau_mem_detect;
+		engine->vram.takedown		= nouveau_stub_takedown;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x40:
@@ -289,6 +293,7 @@
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
 		engine->pm.temp_get		= nv40_temp_get;
 		engine->vram.init		= nouveau_mem_detect;
+		engine->vram.takedown		= nouveau_stub_takedown;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x50:
@@ -366,6 +371,7 @@
 		else
 			engine->pm.temp_get	= nv40_temp_get;
 		engine->vram.init		= nv50_vram_init;
+		engine->vram.takedown		= nv50_vram_fini;
 		engine->vram.get		= nv50_vram_new;
 		engine->vram.put		= nv50_vram_del;
 		engine->vram.flags_valid	= nv50_vram_flags_valid;
@@ -411,9 +417,11 @@
 		engine->gpio.irq_unregister	= nv50_gpio_irq_unregister;
 		engine->gpio.irq_enable		= nv50_gpio_irq_enable;
 		engine->vram.init		= nvc0_vram_init;
+		engine->vram.takedown		= nv50_vram_fini;
 		engine->vram.get		= nvc0_vram_new;
 		engine->vram.put		= nv50_vram_del;
 		engine->vram.flags_valid	= nvc0_vram_flags_valid;
+		engine->pm.temp_get		= nv84_temp_get;
 		break;
 	default:
 		NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
@@ -447,8 +455,8 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	int ret;
 
-	ret = nouveau_channel_alloc(dev, &dev_priv->channel,
-				    (struct drm_file *)-2, NvDmaFB, NvDmaTT);
+	ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
+				    NvDmaFB, NvDmaTT);
 	if (ret)
 		return ret;
 
@@ -527,7 +535,7 @@
 
 	nouveau_pm_init(dev);
 
-	ret = nouveau_mem_vram_init(dev);
+	ret = engine->vram.init(dev);
 	if (ret)
 		goto out_bios;
 
@@ -539,10 +547,14 @@
 	if (ret)
 		goto out_gpuobj;
 
-	ret = nouveau_mem_gart_init(dev);
+	ret = nouveau_mem_vram_init(dev);
 	if (ret)
 		goto out_instmem;
 
+	ret = nouveau_mem_gart_init(dev);
+	if (ret)
+		goto out_ttmvram;
+
 	/* PMC */
 	ret = engine->mc.init(dev);
 	if (ret)
@@ -563,7 +575,7 @@
 	if (ret)
 		goto out_timer;
 
-	if (!nouveau_noaccel) {
+	if (!dev_priv->noaccel) {
 		switch (dev_priv->card_type) {
 		case NV_04:
 			nv04_graph_create(dev);
@@ -675,14 +687,14 @@
 	drm_vblank_cleanup(dev);
 	engine->display.destroy(dev);
 out_fifo:
-	if (!nouveau_noaccel)
+	if (!dev_priv->noaccel)
 		engine->fifo.takedown(dev);
 out_engine:
-	if (!nouveau_noaccel) {
+	if (!dev_priv->noaccel) {
 		for (e = e - 1; e >= 0; e--) {
 			if (!dev_priv->eng[e])
 				continue;
-			dev_priv->eng[e]->fini(dev, e);
+			dev_priv->eng[e]->fini(dev, e, false);
 			dev_priv->eng[e]->destroy(dev,e );
 		}
 	}
@@ -696,12 +708,14 @@
 	engine->mc.takedown(dev);
 out_gart:
 	nouveau_mem_gart_fini(dev);
+out_ttmvram:
+	nouveau_mem_vram_fini(dev);
 out_instmem:
 	engine->instmem.takedown(dev);
 out_gpuobj:
 	nouveau_gpuobj_takedown(dev);
 out_vram:
-	nouveau_mem_vram_fini(dev);
+	engine->vram.takedown(dev);
 out_bios:
 	nouveau_pm_fini(dev);
 	nouveau_bios_takedown(dev);
@@ -718,16 +732,21 @@
 	struct nouveau_engine *engine = &dev_priv->engine;
 	int e;
 
+	drm_kms_helper_poll_fini(dev);
+	nouveau_fbcon_fini(dev);
+
 	if (dev_priv->channel) {
-		nouveau_fence_fini(dev);
 		nouveau_channel_put_unlocked(&dev_priv->channel);
+		nouveau_fence_fini(dev);
 	}
 
-	if (!nouveau_noaccel) {
+	engine->display.destroy(dev);
+
+	if (!dev_priv->noaccel) {
 		engine->fifo.takedown(dev);
 		for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
 			if (dev_priv->eng[e]) {
-				dev_priv->eng[e]->fini(dev, e);
+				dev_priv->eng[e]->fini(dev, e, false);
 				dev_priv->eng[e]->destroy(dev,e );
 			}
 		}
@@ -748,10 +767,11 @@
 	ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
 	mutex_unlock(&dev->struct_mutex);
 	nouveau_mem_gart_fini(dev);
+	nouveau_mem_vram_fini(dev);
 
 	engine->instmem.takedown(dev);
 	nouveau_gpuobj_takedown(dev);
-	nouveau_mem_vram_fini(dev);
+	engine->vram.takedown(dev);
 
 	nouveau_irq_fini(dev);
 	drm_vblank_cleanup(dev);
@@ -762,6 +782,41 @@
 	vga_client_register(dev->pdev, NULL, NULL, NULL);
 }
 
+int
+nouveau_open(struct drm_device *dev, struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fpriv *fpriv;
+	int ret;
+
+	fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+	if (unlikely(!fpriv))
+		return -ENOMEM;
+
+	spin_lock_init(&fpriv->lock);
+	INIT_LIST_HEAD(&fpriv->channels);
+
+	if (dev_priv->card_type == NV_50) {
+		ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0020000000ULL,
+				     &fpriv->vm);
+		if (ret) {
+			kfree(fpriv);
+			return ret;
+		}
+	} else
+	if (dev_priv->card_type >= NV_C0) {
+		ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL,
+				     &fpriv->vm);
+		if (ret) {
+			kfree(fpriv);
+			return ret;
+		}
+	}
+
+	file_priv->driver_priv = fpriv;
+	return 0;
+}
+
 /* here a client dies, release the stuff that was allocated for its
  * file_priv */
 void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
@@ -769,6 +824,14 @@
 	nouveau_channel_cleanup(dev, file_priv);
 }
 
+void
+nouveau_postclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
+	nouveau_vm_ref(NULL, &fpriv->vm, NULL);
+	kfree(fpriv);
+}
+
 /* first module load, setup the mmio/fb mapping */
 /* KMS: we need mmio at load time, not when the first drm client opens. */
 int nouveau_firstopen(struct drm_device *dev)
@@ -933,6 +996,25 @@
 	NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
 		dev_priv->card_type, reg0);
 
+	/* Determine whether we'll attempt acceleration or not, some
+	 * cards are disabled by default here due to them being known
+	 * non-functional, or never been tested due to lack of hw.
+	 */
+	dev_priv->noaccel = !!nouveau_noaccel;
+	if (nouveau_noaccel == -1) {
+		switch (dev_priv->chipset) {
+		case 0xc1: /* known broken */
+		case 0xc8: /* never tested */
+			NV_INFO(dev, "acceleration disabled by default, pass "
+				     "noaccel=0 to force enable\n");
+			dev_priv->noaccel = true;
+			break;
+		default:
+			dev_priv->noaccel = false;
+			break;
+		}
+	}
+
 	ret = nouveau_remove_conflicting_drivers(dev);
 	if (ret)
 		goto err_mmio;
@@ -997,11 +1079,7 @@
 int nouveau_unload(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_engine *engine = &dev_priv->engine;
 
-	drm_kms_helper_poll_fini(dev);
-	nouveau_fbcon_fini(dev);
-	engine->display.destroy(dev);
 	nouveau_card_takedown(dev);
 
 	iounmap(dev_priv->mmio);
@@ -1031,7 +1109,7 @@
 	case NOUVEAU_GETPARAM_BUS_TYPE:
 		if (drm_pci_device_is_agp(dev))
 			getparam->value = NV_AGP;
-		else if (drm_pci_device_is_pcie(dev))
+		else if (pci_is_pcie(dev->pdev))
 			getparam->value = NV_PCIE;
 		else
 			getparam->value = NV_PCI;
diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c
index 649b041..081ca7b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_temp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_temp.c
@@ -43,7 +43,7 @@
 
 	/* Set the default sensor's contants */
 	sensor->offset_constant = 0;
-	sensor->offset_mult = 1;
+	sensor->offset_mult = 0;
 	sensor->offset_div = 1;
 	sensor->slope_mult = 1;
 	sensor->slope_div = 1;
@@ -99,6 +99,13 @@
 			sensor->slope_mult = 431;
 			sensor->slope_div = 10000;
 			break;
+
+		case 0x67:
+			sensor->offset_mult = -26149;
+			sensor->offset_div = 100;
+			sensor->slope_mult = 484;
+			sensor->slope_div = 10000;
+			break;
 		}
 	}
 
@@ -109,7 +116,7 @@
 
 	/* Read the entries from the table */
 	for (i = 0; i < entries; i++) {
-		u16 value = ROM16(temp[1]);
+		s16 value = ROM16(temp[1]);
 
 		switch (temp[0]) {
 		case 0x01:
@@ -160,8 +167,8 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
 	struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
-	u32 offset = sensor->offset_mult / sensor->offset_div;
-	u32 sensor_calibration;
+	s32 offset = sensor->offset_mult / sensor->offset_div;
+	s32 sensor_calibration;
 
 	/* set up the sensors */
 	sensor_calibration = 120 - offset - sensor->offset_constant;
diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c
index 519a6b4..244fd38 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vm.c
@@ -369,23 +369,26 @@
 }
 
 static void
-nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
+nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
 {
 	struct nouveau_vm_pgd *vpgd, *tmp;
+	struct nouveau_gpuobj *pgd = NULL;
 
-	if (!pgd)
+	if (!mpgd)
 		return;
 
 	mutex_lock(&vm->mm->mutex);
 	list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
-		if (vpgd->obj != pgd)
-			continue;
-
-		list_del(&vpgd->head);
-		nouveau_gpuobj_ref(NULL, &vpgd->obj);
-		kfree(vpgd);
+		if (vpgd->obj == mpgd) {
+			pgd = vpgd->obj;
+			list_del(&vpgd->head);
+			kfree(vpgd);
+			break;
+		}
 	}
 	mutex_unlock(&vm->mm->mutex);
+
+	nouveau_gpuobj_ref(NULL, &pgd);
 }
 
 static void
@@ -396,8 +399,8 @@
 	list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
 		nouveau_vm_unlink(vm, vpgd->obj);
 	}
-	WARN_ON(nouveau_mm_fini(&vm->mm) != 0);
 
+	nouveau_mm_fini(&vm->mm);
 	kfree(vm->pgt);
 	kfree(vm);
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h
index c48a9fc..579ca8c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_vm.h
@@ -41,6 +41,8 @@
 };
 
 struct nouveau_vma {
+	struct list_head head;
+	int refcount;
 	struct nouveau_vm *vm;
 	struct nouveau_mm_node *node;
 	u64 offset;
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index f1a3ae4..118261d 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -1035,7 +1035,7 @@
 	drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
 	drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
 
-	ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+	ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
 			     0, 0x0000, &nv_crtc->cursor.nvbo);
 	if (!ret) {
 		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
index 3626ee7..dbdea8e 100644
--- a/drivers/gpu/drm/nouveau/nv04_graph.c
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
@@ -450,13 +450,13 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv04_graph_fifo_access(dev, false);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
 
 	/* Unload the context if it's the currently active one */
 	if (nv04_graph_channel(dev) == chan)
 		nv04_graph_unload_context(dev);
 
-	nv04_graph_fifo_access(dev, true);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	/* Free the context resources */
@@ -538,24 +538,18 @@
 }
 
 static int
-nv04_graph_fini(struct drm_device *dev, int engine)
+nv04_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+	if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+		nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+		return -EBUSY;
+	}
 	nv04_graph_unload_context(dev);
 	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
 	return 0;
 }
 
-void
-nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
-{
-	if (enabled)
-		nv_wr32(dev, NV04_PGRAPH_FIFO,
-					nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
-	else
-		nv_wr32(dev, NV04_PGRAPH_FIFO,
-					nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
-}
-
 static int
 nv04_graph_mthd_set_ref(struct nouveau_channel *chan,
 			u32 class, u32 mthd, u32 data)
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
index b8611b9..c1248e0 100644
--- a/drivers/gpu/drm/nouveau/nv04_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
@@ -28,6 +28,31 @@
 	/* RAMIN always available */
 	dev_priv->ramin_available = true;
 
+	/* Reserve space at end of VRAM for PRAMIN */
+	if (dev_priv->card_type >= NV_40) {
+		u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8);
+		u32 rsvd;
+
+		/* estimate grctx size, the magics come from nv40_grctx.c */
+		if      (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs;
+		else if (dev_priv->chipset  < 0x43) rsvd = 0x4f00 * vs;
+		else if (nv44_graph_class(dev))	    rsvd = 0x4980 * vs;
+		else				    rsvd = 0x4a40 * vs;
+		rsvd += 16 * 1024;
+		rsvd *= dev_priv->engine.fifo.channels;
+
+		/* pciegart table */
+		if (pci_is_pcie(dev->pdev))
+			rsvd += 512 * 1024;
+
+		/* object storage */
+		rsvd += 512 * 1024;
+
+		dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096);
+	} else {
+		dev_priv->ramin_rsvd_vram = 512 * 1024;
+	}
+
 	/* Setup shared RAMHT */
 	ret = nouveau_gpuobj_new_fake(dev, 0x10000, ~0, 4096,
 				      NVOBJ_FLAG_ZERO_ALLOC, &ramht);
@@ -112,7 +137,8 @@
 }
 
 int
-nv04_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
+nv04_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan,
+		 u32 size, u32 align)
 {
 	struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
 	struct drm_mm_node *ramin = NULL;
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index 0930c6c..7255e4a 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -708,8 +708,8 @@
 		0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c);
 	nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst);
 	nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000);
-	nv04_graph_fifo_access(dev, true);
-	nv04_graph_fifo_access(dev, false);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
 
 	/* Restore the FIFO state */
 	for (i = 0; i < ARRAY_SIZE(fifo); i++)
@@ -879,13 +879,13 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv04_graph_fifo_access(dev, false);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
 
 	/* Unload the context if it's the currently active one */
 	if (nv10_graph_channel(dev) == chan)
 		nv10_graph_unload_context(dev);
 
-	nv04_graph_fifo_access(dev, true);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	/* Free the context resources */
@@ -957,8 +957,13 @@
 }
 
 static int
-nv10_graph_fini(struct drm_device *dev, int engine)
+nv10_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+	if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+		nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+		return -EBUSY;
+	}
 	nv10_graph_unload_context(dev);
 	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
index affc7d7..183e375 100644
--- a/drivers/gpu/drm/nouveau/nv20_graph.c
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
@@ -454,13 +454,13 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv04_graph_fifo_access(dev, false);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
 
 	/* Unload the context if it's the currently active one */
 	if (nv10_graph_channel(dev) == chan)
 		nv20_graph_unload_context(dev);
 
-	nv04_graph_fifo_access(dev, true);
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	/* Free the context resources */
@@ -654,8 +654,13 @@
 }
 
 int
-nv20_graph_fini(struct drm_device *dev, int engine)
+nv20_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
+	nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000000);
+	if (!nv_wait(dev, NV04_PGRAPH_STATUS, ~0, 0) && suspend) {
+		nv_mask(dev, NV04_PGRAPH_FIFO, 0x00000001, 0x00000001);
+		return -EBUSY;
+	}
 	nv20_graph_unload_context(dev);
 	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
 	return 0;
@@ -753,6 +758,7 @@
 			break;
 		default:
 			NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+			kfree(pgraph);
 			return 0;
 		}
 	} else {
@@ -774,6 +780,7 @@
 			break;
 		default:
 			NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+			kfree(pgraph);
 			return 0;
 		}
 	}
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
index 5beb01b..ba14a93 100644
--- a/drivers/gpu/drm/nouveau/nv40_graph.c
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
@@ -35,89 +35,6 @@
 	u32 grctx_size;
 };
 
-static struct nouveau_channel *
-nv40_graph_channel(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_gpuobj *grctx;
-	uint32_t inst;
-	int i;
-
-	inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
-	if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
-		return NULL;
-	inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
-
-	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
-		if (!dev_priv->channels.ptr[i])
-			continue;
-
-		grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR];
-		if (grctx && grctx->pinst == inst)
-			return dev_priv->channels.ptr[i];
-	}
-
-	return NULL;
-}
-
-static int
-nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
-{
-	uint32_t old_cp, tv = 1000, tmp;
-	int i;
-
-	old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
-
-	tmp  = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
-	tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
-		      NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
-	nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
-
-	tmp  = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
-	tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
-	nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
-
-	nouveau_wait_for_idle(dev);
-
-	for (i = 0; i < tv; i++) {
-		if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
-			break;
-	}
-
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
-
-	if (i == tv) {
-		uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
-		NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
-		NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
-			 ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
-			 ucstat  & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
-		NV_ERROR(dev, "0x40030C = 0x%08x\n",
-			 nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
-		return -EBUSY;
-	}
-
-	return 0;
-}
-
-static int
-nv40_graph_unload_context(struct drm_device *dev)
-{
-	uint32_t inst;
-	int ret;
-
-	inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
-	if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
-		return 0;
-	inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
-
-	ret = nv40_graph_transfer_context(dev, inst, 1);
-
-	nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
-	return ret;
-}
-
 static int
 nv40_graph_context_new(struct nouveau_channel *chan, int engine)
 {
@@ -163,16 +80,16 @@
 	struct nouveau_gpuobj *grctx = chan->engctx[engine];
 	struct drm_device *dev = chan->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	u32 inst = 0x01000000 | (grctx->pinst >> 4);
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv04_graph_fifo_access(dev, false);
-
-	/* Unload the context if it's the currently active one */
-	if (nv40_graph_channel(dev) == chan)
-		nv40_graph_unload_context(dev);
-
-	nv04_graph_fifo_access(dev, true);
+	nv_mask(dev, 0x400720, 0x00000000, 0x00000001);
+	if (nv_rd32(dev, 0x40032c) == inst)
+		nv_mask(dev, 0x40032c, 0x01000000, 0x00000000);
+	if (nv_rd32(dev, 0x400330) == inst)
+		nv_mask(dev, 0x400330, 0x01000000, 0x00000000);
+	nv_mask(dev, 0x400720, 0x00000001, 0x00000001);
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	/* Free the context resources */
@@ -429,9 +346,20 @@
 }
 
 static int
-nv40_graph_fini(struct drm_device *dev, int engine)
+nv40_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
-	nv40_graph_unload_context(dev);
+	u32 inst = nv_rd32(dev, 0x40032c);
+	if (inst & 0x01000000) {
+		nv_wr32(dev, 0x400720, 0x00000000);
+		nv_wr32(dev, 0x400784, inst);
+		nv_mask(dev, 0x400310, 0x00000020, 0x00000020);
+		nv_mask(dev, 0x400304, 0x00000001, 0x00000001);
+		if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) {
+			u32 insn = nv_rd32(dev, 0x400308);
+			NV_ERROR(dev, "PGRAPH: ctxprog timeout 0x%08x\n", insn);
+		}
+		nv_mask(dev, 0x40032c, 0x01000000, 0x00000000);
+	}
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c
index 6d2af29..ad03a0e 100644
--- a/drivers/gpu/drm/nouveau/nv40_mpeg.c
+++ b/drivers/gpu/drm/nouveau/nv40_mpeg.c
@@ -137,7 +137,7 @@
 }
 
 static int
-nv40_mpeg_fini(struct drm_device *dev, int engine)
+nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	/*XXX: context save? */
 	nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index ebabacf..46ad59e 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -104,7 +104,7 @@
 		OUT_RING(evo, nv_crtc->lut.depth == 8 ?
 				NV50_EVO_CRTC_CLUT_MODE_OFF :
 				NV50_EVO_CRTC_CLUT_MODE_ON);
-		OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.start << PAGE_SHIFT) >> 8);
+		OUT_RING(evo, nv_crtc->lut.nvbo->bo.offset >> 8);
 		if (dev_priv->chipset != 0x50) {
 			BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
 			OUT_RING(evo, NvEvoVRAM);
@@ -372,7 +372,7 @@
 
 	nouveau_bo_unmap(cursor);
 
-	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.mem.start << PAGE_SHIFT);
+	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
 	nv_crtc->cursor.show(nv_crtc, true);
 
 out:
@@ -546,7 +546,7 @@
 		}
 	}
 
-	nv_crtc->fb.offset = fb->nvbo->bo.mem.start << PAGE_SHIFT;
+	nv_crtc->fb.offset = fb->nvbo->bo.offset;
 	nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo);
 	nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
 	if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
@@ -747,7 +747,7 @@
 	}
 	nv_crtc->lut.depth = 0;
 
-	ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
+	ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM,
 			     0, 0x0000, &nv_crtc->lut.nvbo);
 	if (!ret) {
 		ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
@@ -773,7 +773,7 @@
 	drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
 	drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
 
-	ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+	ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
 			     0, 0x0000, &nv_crtc->cursor.nvbo);
 	if (!ret) {
 		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 08da478..db1a5f4 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -415,8 +415,6 @@
 
 	/* synchronise with the rendering channel, if necessary */
 	if (likely(chan)) {
-		u64 offset = dispc->sem.bo->vma.offset + dispc->sem.offset;
-
 		ret = RING_SPACE(chan, 10);
 		if (ret) {
 			WIND_RING(evo);
@@ -438,6 +436,8 @@
 			else
 				OUT_RING  (chan, chan->vram_handle);
 		} else {
+			u64 offset = chan->dispc_vma[nv_crtc->index].offset;
+			offset += dispc->sem.offset;
 			BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
 			OUT_RING  (chan, upper_32_bits(offset));
 			OUT_RING  (chan, lower_32_bits(offset));
@@ -484,7 +484,7 @@
 	OUT_RING  (evo, 0x00000000);
 	OUT_RING  (evo, 0x00000000);
 	BEGIN_RING(evo, 0, 0x0800, 5);
-	OUT_RING  (evo, (nv_fb->nvbo->bo.mem.start << PAGE_SHIFT) >> 8);
+	OUT_RING  (evo, nv_fb->nvbo->bo.offset >> 8);
 	OUT_RING  (evo, 0);
 	OUT_RING  (evo, (fb->height << 16) | fb->width);
 	OUT_RING  (evo, nv_fb->r_pitch);
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c
index c8e83c1..c99d975 100644
--- a/drivers/gpu/drm/nouveau/nv50_evo.c
+++ b/drivers/gpu/drm/nouveau/nv50_evo.c
@@ -38,6 +38,7 @@
 		return;
 	*pevo = NULL;
 
+	nouveau_ramht_ref(NULL, &evo->ramht, evo);
 	nouveau_gpuobj_channel_takedown(evo);
 	nouveau_bo_unmap(evo->pushbuf_bo);
 	nouveau_bo_ref(NULL, &evo->pushbuf_bo);
@@ -116,7 +117,7 @@
 	evo->user_get = 4;
 	evo->user_put = 0;
 
-	ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
+	ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
 			     &evo->pushbuf_bo);
 	if (ret == 0)
 		ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM);
@@ -153,7 +154,7 @@
 {
 	struct drm_device *dev = evo->dev;
 	int id = evo->id, ret, i;
-	u64 pushbuf = evo->pushbuf_bo->bo.mem.start << PAGE_SHIFT;
+	u64 pushbuf = evo->pushbuf_bo->bo.offset;
 	u32 tmp;
 
 	tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id));
@@ -331,16 +332,15 @@
 		if (ret)
 			goto err;
 
-		ret = nouveau_bo_new(dev, NULL, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+		ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
 				     0, 0x0000, &dispc->sem.bo);
 		if (!ret) {
-			offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT;
-
 			ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM);
 			if (!ret)
 				ret = nouveau_bo_map(dispc->sem.bo);
 			if (ret)
 				nouveau_bo_ref(NULL, &dispc->sem.bo);
+			offset = dispc->sem.bo->bo.offset;
 		}
 
 		if (ret)
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 791ded1..dc75a72 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -159,7 +159,7 @@
 	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
-	struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo;
+	struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb;
 	int ret, format;
 
 	switch (info->var.bits_per_pixel) {
@@ -247,8 +247,8 @@
 	OUT_RING(chan, info->fix.line_length);
 	OUT_RING(chan, info->var.xres_virtual);
 	OUT_RING(chan, info->var.yres_virtual);
-	OUT_RING(chan, upper_32_bits(nvbo->vma.offset));
-	OUT_RING(chan, lower_32_bits(nvbo->vma.offset));
+	OUT_RING(chan, upper_32_bits(fb->vma.offset));
+	OUT_RING(chan, lower_32_bits(fb->vma.offset));
 	BEGIN_RING(chan, NvSub2D, 0x0230, 2);
 	OUT_RING(chan, format);
 	OUT_RING(chan, 1);
@@ -256,8 +256,8 @@
 	OUT_RING(chan, info->fix.line_length);
 	OUT_RING(chan, info->var.xres_virtual);
 	OUT_RING(chan, info->var.yres_virtual);
-	OUT_RING(chan, upper_32_bits(nvbo->vma.offset));
-	OUT_RING(chan, lower_32_bits(nvbo->vma.offset));
+	OUT_RING(chan, upper_32_bits(fb->vma.offset));
+	OUT_RING(chan, lower_32_bits(fb->vma.offset));
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index 40680f2b..d43c46c 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -124,7 +124,6 @@
 nv50_graph_init_reset(struct drm_device *dev)
 {
 	uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
-
 	NV_DEBUG(dev, "\n");
 
 	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
@@ -254,9 +253,13 @@
 }
 
 static int
-nv50_graph_fini(struct drm_device *dev, int engine)
+nv50_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
-	NV_DEBUG(dev, "\n");
+	nv_mask(dev, 0x400500, 0x00010001, 0x00000000);
+	if (!nv_wait(dev, 0x400700, ~0, 0) && suspend) {
+		nv_mask(dev, 0x400500, 0x00010001, 0x00010001);
+		return -EBUSY;
+	}
 	nv50_graph_unload_context(dev);
 	nv_wr32(dev, 0x40013c, 0x00000000);
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 4f95a1e..a7c12c9 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -305,9 +305,9 @@
 	u32 align;
 };
 
-
 int
-nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
+nv50_instmem_get(struct nouveau_gpuobj *gpuobj, struct nouveau_channel *chan,
+		 u32 size, u32 align)
 {
 	struct drm_device *dev = gpuobj->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -336,7 +336,7 @@
 		if (!(gpuobj->flags & NVOBJ_FLAG_VM_USER))
 			flags |= NV_MEM_ACCESS_SYS;
 
-		ret = nouveau_vm_get(dev_priv->chan_vm, size, 12, flags,
+		ret = nouveau_vm_get(chan->vm, size, 12, flags,
 				     &node->chan_vma);
 		if (ret) {
 			vram->put(dev, &node->vram);
@@ -345,7 +345,7 @@
 		}
 
 		nouveau_vm_map(&node->chan_vma, node->vram);
-		gpuobj->vinst = node->chan_vma.offset;
+		gpuobj->linst = node->chan_vma.offset;
 	}
 
 	gpuobj->size = size;
diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c
index 1dc5913..b57a2d1 100644
--- a/drivers/gpu/drm/nouveau/nv50_mpeg.c
+++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c
@@ -160,7 +160,7 @@
 }
 
 static int
-nv50_mpeg_fini(struct drm_device *dev, int engine)
+nv50_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	/*XXX: context save for s/r */
 	nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index c25c593..ffe8b48 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -318,6 +318,8 @@
 		uint32_t tmp;
 
 		tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
+		if (!tmp)
+			tmp = nv_rd32(dev, 0x610798 + (or * 8));
 
 		switch ((tmp & 0x00000f00) >> 8) {
 		case 8:
diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c
index 1a0dd49..40b84f2 100644
--- a/drivers/gpu/drm/nouveau/nv50_vm.c
+++ b/drivers/gpu/drm/nouveau/nv50_vm.c
@@ -156,7 +156,7 @@
 	pinstmem->flush(vm->dev);
 
 	/* BAR */
-	if (vm != dev_priv->chan_vm) {
+	if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm) {
 		nv50_vm_flush_engine(vm->dev, 6);
 		return;
 	}
diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c
index ffbc3d8..af32dae 100644
--- a/drivers/gpu/drm/nouveau/nv50_vram.c
+++ b/drivers/gpu/drm/nouveau/nv50_vram.c
@@ -51,9 +51,7 @@
 nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
-	struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
-	struct nouveau_mm *mm = man->priv;
+	struct nouveau_mm *mm = dev_priv->engine.vram.mm;
 	struct nouveau_mm_node *this;
 	struct nouveau_mem *mem;
 
@@ -84,9 +82,7 @@
 	      u32 memtype, struct nouveau_mem **pmem)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
-	struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
-	struct nouveau_mm *mm = man->priv;
+	struct nouveau_mm *mm = dev_priv->engine.vram.mm;
 	struct nouveau_mm_node *r;
 	struct nouveau_mem *mem;
 	int comp = (memtype & 0x300) >> 8;
@@ -190,22 +186,35 @@
 nv50_vram_init(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+	u32 rblock, length;
 
 	dev_priv->vram_size  = nv_rd32(dev, 0x10020c);
 	dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
 	dev_priv->vram_size &= 0xffffffff00ULL;
 
-	switch (dev_priv->chipset) {
-	case 0xaa:
-	case 0xac:
-	case 0xaf:
+	/* IGPs, no funky reordering happens here, they don't have VRAM */
+	if (dev_priv->chipset == 0xaa ||
+	    dev_priv->chipset == 0xac ||
+	    dev_priv->chipset == 0xaf) {
 		dev_priv->vram_sys_base = (u64)nv_rd32(dev, 0x100e10) << 12;
-		dev_priv->vram_rblock_size = 4096;
-		break;
-	default:
-		dev_priv->vram_rblock_size = nv50_vram_rblock(dev);
-		break;
+		rblock = 4096 >> 12;
+	} else {
+		rblock = nv50_vram_rblock(dev) >> 12;
 	}
 
-	return 0;
+	length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+
+	return nouveau_mm_init(&vram->mm, rsvd_head, length, rblock);
+}
+
+void
+nv50_vram_fini(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+
+	nouveau_mm_fini(&vram->mm);
 }
diff --git a/drivers/gpu/drm/nouveau/nv84_crypt.c b/drivers/gpu/drm/nouveau/nv84_crypt.c
index 75b809a..edece9c 100644
--- a/drivers/gpu/drm/nouveau/nv84_crypt.c
+++ b/drivers/gpu/drm/nouveau/nv84_crypt.c
@@ -138,7 +138,7 @@
 }
 
 static int
-nv84_crypt_fini(struct drm_device *dev, int engine)
+nv84_crypt_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	nv_wr32(dev, 0x102140, 0x00000000);
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c
index b86820a..8f356d5 100644
--- a/drivers/gpu/drm/nouveau/nva3_copy.c
+++ b/drivers/gpu/drm/nouveau/nva3_copy.c
@@ -140,7 +140,7 @@
 }
 
 static int
-nva3_copy_fini(struct drm_device *dev, int engine)
+nva3_copy_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	nv_mask(dev, 0x104048, 0x00000003, 0x00000000);
 
diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.c b/drivers/gpu/drm/nouveau/nvc0_copy.c
index 208fa7a..dddf006 100644
--- a/drivers/gpu/drm/nouveau/nvc0_copy.c
+++ b/drivers/gpu/drm/nouveau/nvc0_copy.c
@@ -48,14 +48,14 @@
 	struct nouveau_gpuobj *ctx = NULL;
 	int ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 256, 256,
+	ret = nouveau_gpuobj_new(dev, chan, 256, 256,
 				 NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER |
 				 NVOBJ_FLAG_ZERO_ALLOC, &ctx);
 	if (ret)
 		return ret;
 
-	nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->vinst));
-	nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->vinst));
+	nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->linst));
+	nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->linst));
 	dev_priv->engine.instmem.flush(dev);
 
 	chan->engctx[engine] = ctx;
@@ -127,7 +127,7 @@
 }
 
 static int
-nvc0_copy_fini(struct drm_device *dev, int engine)
+nvc0_copy_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
 
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
index 26a9960..08e6b11 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fb.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 Red Hat Inc.
+ * Copyright 2011 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -23,16 +23,80 @@
  */
 
 #include "drmP.h"
-
+#include "drm.h"
 #include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+struct nvc0_fb_priv {
+	struct page *r100c10_page;
+	dma_addr_t r100c10;
+};
+
+static void
+nvc0_fb_destroy(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+	struct nvc0_fb_priv *priv = pfb->priv;
+
+	if (priv->r100c10_page) {
+		pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE,
+			       PCI_DMA_BIDIRECTIONAL);
+		__free_page(priv->r100c10_page);
+	}
+
+	kfree(priv);
+	pfb->priv = NULL;
+}
+
+static int
+nvc0_fb_create(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+	struct nvc0_fb_priv *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	pfb->priv = priv;
+
+	priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (!priv->r100c10_page) {
+		nvc0_fb_destroy(dev);
+		return -ENOMEM;
+	}
+
+	priv->r100c10 = pci_map_page(dev->pdev, priv->r100c10_page, 0,
+				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+	if (pci_dma_mapping_error(dev->pdev, priv->r100c10)) {
+		nvc0_fb_destroy(dev);
+		return -EFAULT;
+	}
+
+	return 0;
+}
 
 int
 nvc0_fb_init(struct drm_device *dev)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvc0_fb_priv *priv;
+	int ret;
+
+	if (!dev_priv->engine.fb.priv) {
+		ret = nvc0_fb_create(dev);
+		if (ret)
+			return ret;
+	}
+	priv = dev_priv->engine.fb.priv;
+
+	nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
 	return 0;
 }
 
 void
 nvc0_fb_takedown(struct drm_device *dev)
 {
+	nvc0_fb_destroy(dev);
 }
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
index fa5d4c2..a495e48 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
@@ -159,7 +159,7 @@
 	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
-	struct nouveau_bo *nvbo = nfbdev->nouveau_fb.nvbo;
+	struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb;
 	int ret, format;
 
 	ret = nouveau_gpuobj_gr_new(chan, 0x902d, 0x902d);
@@ -203,8 +203,8 @@
 	BEGIN_NVC0(chan, 2, NvSub2D, 0x0000, 1);
 	OUT_RING  (chan, 0x0000902d);
 	BEGIN_NVC0(chan, 2, NvSub2D, 0x0104, 2);
-	OUT_RING  (chan, upper_32_bits(chan->notifier_bo->bo.offset));
-	OUT_RING  (chan, lower_32_bits(chan->notifier_bo->bo.offset));
+	OUT_RING  (chan, upper_32_bits(chan->notifier_vma.offset));
+	OUT_RING  (chan, lower_32_bits(chan->notifier_vma.offset));
 	BEGIN_NVC0(chan, 2, NvSub2D, 0x0290, 1);
 	OUT_RING  (chan, 0);
 	BEGIN_NVC0(chan, 2, NvSub2D, 0x0888, 1);
@@ -249,8 +249,8 @@
 	OUT_RING  (chan, info->fix.line_length);
 	OUT_RING  (chan, info->var.xres_virtual);
 	OUT_RING  (chan, info->var.yres_virtual);
-	OUT_RING  (chan, upper_32_bits(nvbo->vma.offset));
-	OUT_RING  (chan, lower_32_bits(nvbo->vma.offset));
+	OUT_RING  (chan, upper_32_bits(fb->vma.offset));
+	OUT_RING  (chan, lower_32_bits(fb->vma.offset));
 	BEGIN_NVC0(chan, 2, NvSub2D, 0x0230, 10);
 	OUT_RING  (chan, format);
 	OUT_RING  (chan, 1);
@@ -260,8 +260,8 @@
 	OUT_RING  (chan, info->fix.line_length);
 	OUT_RING  (chan, info->var.xres_virtual);
 	OUT_RING  (chan, info->var.yres_virtual);
-	OUT_RING  (chan, upper_32_bits(nvbo->vma.offset));
-	OUT_RING  (chan, lower_32_bits(nvbo->vma.offset));
+	OUT_RING  (chan, upper_32_bits(fb->vma.offset));
+	OUT_RING  (chan, lower_32_bits(fb->vma.offset));
 	FIRE_RING (chan);
 
 	return 0;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index fb4f594..6f9f341 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -210,10 +210,10 @@
 	int i;
 
 	for (i = 0; i < 128; i++) {
-		if (!(nv_rd32(dev, 0x003004 + (i * 4)) & 1))
+		if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1))
 			continue;
 
-		nv_mask(dev, 0x003004 + (i * 4), 0x00000001, 0x00000000);
+		nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000);
 		nv_wr32(dev, 0x002634, i);
 		if (!nv_wait(dev, 0x002634, 0xffffffff, i)) {
 			NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n",
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
index ca6db20..5b2f6f4 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -28,7 +28,34 @@
 
 #include "nouveau_drv.h"
 #include "nouveau_mm.h"
+
 #include "nvc0_graph.h"
+#include "nvc0_grhub.fuc.h"
+#include "nvc0_grgpc.fuc.h"
+
+static void
+nvc0_graph_ctxctl_debug_unit(struct drm_device *dev, u32 base)
+{
+	NV_INFO(dev, "PGRAPH: %06x - done 0x%08x\n", base,
+		nv_rd32(dev, base + 0x400));
+	NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+		nv_rd32(dev, base + 0x800), nv_rd32(dev, base + 0x804),
+		nv_rd32(dev, base + 0x808), nv_rd32(dev, base + 0x80c));
+	NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+		nv_rd32(dev, base + 0x810), nv_rd32(dev, base + 0x814),
+		nv_rd32(dev, base + 0x818), nv_rd32(dev, base + 0x81c));
+}
+
+static void
+nvc0_graph_ctxctl_debug(struct drm_device *dev)
+{
+	u32 gpcnr = nv_rd32(dev, 0x409604) & 0xffff;
+	u32 gpc;
+
+	nvc0_graph_ctxctl_debug_unit(dev, 0x409000);
+	for (gpc = 0; gpc < gpcnr; gpc++)
+		nvc0_graph_ctxctl_debug_unit(dev, 0x502000 + (gpc * 0x8000));
+}
 
 static int
 nvc0_graph_load_context(struct nouveau_channel *chan)
@@ -72,24 +99,44 @@
 	if (!ctx)
 		return -ENOMEM;
 
-	nvc0_graph_load_context(chan);
+	if (!nouveau_ctxfw) {
+		nv_wr32(dev, 0x409840, 0x80000000);
+		nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
+		nv_wr32(dev, 0x409504, 0x00000001);
+		if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+			NV_ERROR(dev, "PGRAPH: HUB_SET_CHAN timeout\n");
+			nvc0_graph_ctxctl_debug(dev);
+			ret = -EBUSY;
+			goto err;
+		}
+	} else {
+		nvc0_graph_load_context(chan);
 
-	nv_wo32(grch->grctx, 0x1c, 1);
-	nv_wo32(grch->grctx, 0x20, 0);
-	nv_wo32(grch->grctx, 0x28, 0);
-	nv_wo32(grch->grctx, 0x2c, 0);
-	dev_priv->engine.instmem.flush(dev);
-
-	ret = nvc0_grctx_generate(chan);
-	if (ret) {
-		kfree(ctx);
-		return ret;
+		nv_wo32(grch->grctx, 0x1c, 1);
+		nv_wo32(grch->grctx, 0x20, 0);
+		nv_wo32(grch->grctx, 0x28, 0);
+		nv_wo32(grch->grctx, 0x2c, 0);
+		dev_priv->engine.instmem.flush(dev);
 	}
 
-	ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst);
-	if (ret) {
-		kfree(ctx);
-		return ret;
+	ret = nvc0_grctx_generate(chan);
+	if (ret)
+		goto err;
+
+	if (!nouveau_ctxfw) {
+		nv_wr32(dev, 0x409840, 0x80000000);
+		nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
+		nv_wr32(dev, 0x409504, 0x00000002);
+		if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+			NV_ERROR(dev, "PGRAPH: HUB_CTX_SAVE timeout\n");
+			nvc0_graph_ctxctl_debug(dev);
+			ret = -EBUSY;
+			goto err;
+		}
+	} else {
+		ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst);
+		if (ret)
+			goto err;
 	}
 
 	for (i = 0; i < priv->grctx_size; i += 4)
@@ -97,6 +144,10 @@
 
 	priv->grctx_vals = ctx;
 	return 0;
+
+err:
+	kfree(ctx);
+	return ret;
 }
 
 static int
@@ -108,50 +159,50 @@
 	int i = 0, gpc, tp, ret;
 	u32 magic;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 0x2000, 256, NVOBJ_FLAG_VM,
+	ret = nouveau_gpuobj_new(dev, chan, 0x2000, 256, NVOBJ_FLAG_VM,
 				 &grch->unk408004);
 	if (ret)
 		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 256, NVOBJ_FLAG_VM,
+	ret = nouveau_gpuobj_new(dev, chan, 0x8000, 256, NVOBJ_FLAG_VM,
 				 &grch->unk40800c);
 	if (ret)
 		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 384 * 1024, 4096,
+	ret = nouveau_gpuobj_new(dev, chan, 384 * 1024, 4096,
 				 NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER,
 				 &grch->unk418810);
 	if (ret)
 		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0, NVOBJ_FLAG_VM,
+	ret = nouveau_gpuobj_new(dev, chan, 0x1000, 0, NVOBJ_FLAG_VM,
 				 &grch->mmio);
 	if (ret)
 		return ret;
 
 
 	nv_wo32(grch->mmio, i++ * 4, 0x00408004);
-	nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8);
+	nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8);
 	nv_wo32(grch->mmio, i++ * 4, 0x00408008);
 	nv_wo32(grch->mmio, i++ * 4, 0x80000018);
 
 	nv_wo32(grch->mmio, i++ * 4, 0x0040800c);
-	nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8);
+	nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8);
 	nv_wo32(grch->mmio, i++ * 4, 0x00408010);
 	nv_wo32(grch->mmio, i++ * 4, 0x80000000);
 
 	nv_wo32(grch->mmio, i++ * 4, 0x00418810);
-	nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->vinst >> 12);
+	nv_wo32(grch->mmio, i++ * 4, 0x80000000 | grch->unk418810->linst >> 12);
 	nv_wo32(grch->mmio, i++ * 4, 0x00419848);
-	nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->vinst >> 12);
+	nv_wo32(grch->mmio, i++ * 4, 0x10000000 | grch->unk418810->linst >> 12);
 
 	nv_wo32(grch->mmio, i++ * 4, 0x00419004);
-	nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->vinst >> 8);
+	nv_wo32(grch->mmio, i++ * 4, grch->unk40800c->linst >> 8);
 	nv_wo32(grch->mmio, i++ * 4, 0x00419008);
 	nv_wo32(grch->mmio, i++ * 4, 0x00000000);
 
 	nv_wo32(grch->mmio, i++ * 4, 0x00418808);
-	nv_wo32(grch->mmio, i++ * 4, grch->unk408004->vinst >> 8);
+	nv_wo32(grch->mmio, i++ * 4, grch->unk408004->linst >> 8);
 	nv_wo32(grch->mmio, i++ * 4, 0x0041880c);
 	nv_wo32(grch->mmio, i++ * 4, 0x80000018);
 
@@ -159,7 +210,7 @@
 	nv_wo32(grch->mmio, i++ * 4, 0x00405830);
 	nv_wo32(grch->mmio, i++ * 4, magic);
 	for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-		for (tp = 0; tp < priv->tp_nr[gpc]; tp++, magic += 0x02fc) {
+		for (tp = 0; tp < priv->tp_nr[gpc]; tp++, magic += 0x0324) {
 			u32 reg = 0x504520 + (gpc * 0x8000) + (tp * 0x0800);
 			nv_wo32(grch->mmio, i++ * 4, reg);
 			nv_wo32(grch->mmio, i++ * 4, magic);
@@ -186,7 +237,7 @@
 		return -ENOMEM;
 	chan->engctx[NVOBJ_ENGINE_GR] = grch;
 
-	ret = nouveau_gpuobj_new(dev, NULL, priv->grctx_size, 256,
+	ret = nouveau_gpuobj_new(dev, chan, priv->grctx_size, 256,
 				 NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC,
 				 &grch->grctx);
 	if (ret)
@@ -197,8 +248,8 @@
 	if (ret)
 		goto error;
 
-	nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->vinst) | 4);
-	nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->vinst));
+	nv_wo32(chan->ramin, 0x0210, lower_32_bits(grctx->linst) | 4);
+	nv_wo32(chan->ramin, 0x0214, upper_32_bits(grctx->linst));
 	pinstmem->flush(dev);
 
 	if (!priv->grctx_vals) {
@@ -210,15 +261,20 @@
 	for (i = 0; i < priv->grctx_size; i += 4)
 		nv_wo32(grctx, i, priv->grctx_vals[i / 4]);
 
-	nv_wo32(grctx, 0xf4, 0);
-	nv_wo32(grctx, 0xf8, 0);
-	nv_wo32(grctx, 0x10, grch->mmio_nr);
-	nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst));
-	nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst));
-	nv_wo32(grctx, 0x1c, 1);
-	nv_wo32(grctx, 0x20, 0);
-	nv_wo32(grctx, 0x28, 0);
-	nv_wo32(grctx, 0x2c, 0);
+	if (!nouveau_ctxfw) {
+		nv_wo32(grctx, 0x00, grch->mmio_nr);
+		nv_wo32(grctx, 0x04, grch->mmio->linst >> 8);
+	} else {
+		nv_wo32(grctx, 0xf4, 0);
+		nv_wo32(grctx, 0xf8, 0);
+		nv_wo32(grctx, 0x10, grch->mmio_nr);
+		nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->linst));
+		nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->linst));
+		nv_wo32(grctx, 0x1c, 1);
+		nv_wo32(grctx, 0x20, 0);
+		nv_wo32(grctx, 0x28, 0);
+		nv_wo32(grctx, 0x2c, 0);
+	}
 	pinstmem->flush(dev);
 	return 0;
 
@@ -248,7 +304,7 @@
 }
 
 static int
-nvc0_graph_fini(struct drm_device *dev, int engine)
+nvc0_graph_fini(struct drm_device *dev, int engine, bool suspend)
 {
 	return 0;
 }
@@ -296,6 +352,7 @@
 nvc0_graph_init_gpc_0(struct drm_device *dev)
 {
 	struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
+	const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tp_total);
 	u32 data[TP_MAX / 8];
 	u8  tpnr[GPC_MAX];
 	int i, gpc, tpc;
@@ -307,13 +364,6 @@
 	 * 465: 3/4/4/0 4        7
 	 * 470: 3/3/4/4 5        5
 	 * 480: 3/4/4/4 6        6
-	 *
-	 * magicgpc918
-	 * 450: 00200000 00000000001000000000000000000000
-	 * 460: 00124925 00000000000100100100100100100101
-	 * 465: 000ba2e9 00000000000010111010001011101001
-	 * 470: 00092493 00000000000010010010010010010011
-	 * 480: 00088889 00000000000010001000100010001001
 	 */
 
 	memset(data, 0x00, sizeof(data));
@@ -336,10 +386,10 @@
 		nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 |
 						  priv->tp_nr[gpc]);
 		nv_wr32(dev, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tp_total);
-		nv_wr32(dev, GPC_UNIT(gpc, 0x0918), priv->magicgpc918);
+		nv_wr32(dev, GPC_UNIT(gpc, 0x0918), magicgpc918);
 	}
 
-	nv_wr32(dev, GPC_BCAST(0x1bd4), priv->magicgpc918);
+	nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918);
 	nv_wr32(dev, GPC_BCAST(0x08ac), priv->rop_nr);
 }
 
@@ -419,8 +469,51 @@
 static int
 nvc0_graph_init_ctxctl(struct drm_device *dev)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
 	u32 r000260;
+	int i;
+
+	if (!nouveau_ctxfw) {
+		/* load HUB microcode */
+		r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
+		nv_wr32(dev, 0x4091c0, 0x01000000);
+		for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++)
+			nv_wr32(dev, 0x4091c4, nvc0_grhub_data[i]);
+
+		nv_wr32(dev, 0x409180, 0x01000000);
+		for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) {
+			if ((i & 0x3f) == 0)
+				nv_wr32(dev, 0x409188, i >> 6);
+			nv_wr32(dev, 0x409184, nvc0_grhub_code[i]);
+		}
+
+		/* load GPC microcode */
+		nv_wr32(dev, 0x41a1c0, 0x01000000);
+		for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++)
+			nv_wr32(dev, 0x41a1c4, nvc0_grgpc_data[i]);
+
+		nv_wr32(dev, 0x41a180, 0x01000000);
+		for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) {
+			if ((i & 0x3f) == 0)
+				nv_wr32(dev, 0x41a188, i >> 6);
+			nv_wr32(dev, 0x41a184, nvc0_grgpc_code[i]);
+		}
+		nv_wr32(dev, 0x000260, r000260);
+
+		/* start HUB ucode running, it'll init the GPCs */
+		nv_wr32(dev, 0x409800, dev_priv->chipset);
+		nv_wr32(dev, 0x40910c, 0x00000000);
+		nv_wr32(dev, 0x409100, 0x00000002);
+		if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
+			NV_ERROR(dev, "PGRAPH: HUB_INIT timed out\n");
+			nvc0_graph_ctxctl_debug(dev);
+			return -EBUSY;
+		}
+
+		priv->grctx_size = nv_rd32(dev, 0x409804);
+		return 0;
+	}
 
 	/* load fuc microcode */
 	r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
@@ -528,6 +621,22 @@
 }
 
 static void
+nvc0_graph_ctxctl_isr(struct drm_device *dev)
+{
+	u32 ustat = nv_rd32(dev, 0x409c18);
+
+	if (ustat & 0x00000001)
+		NV_INFO(dev, "PGRAPH: CTXCTRL ucode error\n");
+	if (ustat & 0x00080000)
+		NV_INFO(dev, "PGRAPH: CTXCTRL watchdog timeout\n");
+	if (ustat & ~0x00080001)
+		NV_INFO(dev, "PGRAPH: CTXCTRL 0x%08x\n", ustat);
+
+	nvc0_graph_ctxctl_debug(dev);
+	nv_wr32(dev, 0x409c20, ustat);
+}
+
+static void
 nvc0_graph_isr(struct drm_device *dev)
 {
 	u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12;
@@ -578,11 +687,7 @@
 	}
 
 	if (stat & 0x00080000) {
-		u32 ustat = nv_rd32(dev, 0x409c18);
-
-		NV_INFO(dev, "PGRAPH: CTXCTRL ustat 0x%08x\n", ustat);
-
-		nv_wr32(dev, 0x409c20, ustat);
+		nvc0_graph_ctxctl_isr(dev);
 		nv_wr32(dev, 0x400100, 0x00080000);
 		stat &= ~0x00080000;
 	}
@@ -606,7 +711,7 @@
 		u32 st0 = nv_mask(dev, reg + 0x1020, 0, 0);
 		u32 st1 = nv_mask(dev, reg + 0x1420, 0, 0);
 
-		NV_INFO(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1);
+		NV_DEBUG(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1);
 		units &= ~(1 << unit);
 	}
 }
@@ -651,10 +756,12 @@
 {
 	struct nvc0_graph_priv *priv = nv_engine(dev, engine);
 
-	nvc0_graph_destroy_fw(&priv->fuc409c);
-	nvc0_graph_destroy_fw(&priv->fuc409d);
-	nvc0_graph_destroy_fw(&priv->fuc41ac);
-	nvc0_graph_destroy_fw(&priv->fuc41ad);
+	if (nouveau_ctxfw) {
+		nvc0_graph_destroy_fw(&priv->fuc409c);
+		nvc0_graph_destroy_fw(&priv->fuc409d);
+		nvc0_graph_destroy_fw(&priv->fuc41ac);
+		nvc0_graph_destroy_fw(&priv->fuc41ad);
+	}
 
 	nouveau_irq_unregister(dev, 12);
 	nouveau_irq_unregister(dev, 25);
@@ -675,13 +782,10 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nvc0_graph_priv *priv;
 	int ret, gpc, i;
+	u32 fermi;
 
-	switch (dev_priv->chipset) {
-	case 0xc0:
-	case 0xc3:
-	case 0xc4:
-		break;
-	default:
+	fermi = nvc0_graph_class(dev);
+	if (!fermi) {
 		NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n");
 		return 0;
 	}
@@ -701,15 +805,17 @@
 	nouveau_irq_register(dev, 12, nvc0_graph_isr);
 	nouveau_irq_register(dev, 25, nvc0_runk140_isr);
 
-	if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
-	    nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
-	    nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
-	    nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
-		ret = 0;
-		goto error;
+	if (nouveau_ctxfw) {
+		NV_INFO(dev, "PGRAPH: using external firmware\n");
+		if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
+		    nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
+		    nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
+		    nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
+			ret = 0;
+			goto error;
+		}
 	}
 
-
 	ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4);
 	if (ret)
 		goto error;
@@ -735,25 +841,28 @@
 	case 0xc0:
 		if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */
 			priv->magic_not_rop_nr = 0x07;
-			/* filled values up to tp_total, the rest 0 */
-			priv->magicgpc918      = 0x000ba2e9;
 		} else
 		if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */
 			priv->magic_not_rop_nr = 0x05;
-			priv->magicgpc918      = 0x00092493;
 		} else
 		if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */
 			priv->magic_not_rop_nr = 0x06;
-			priv->magicgpc918      = 0x00088889;
 		}
 		break;
 	case 0xc3: /* 450, 4/0/0/0, 2 */
 		priv->magic_not_rop_nr = 0x03;
-		priv->magicgpc918      = 0x00200000;
 		break;
 	case 0xc4: /* 460, 3/4/0/0, 4 */
 		priv->magic_not_rop_nr = 0x01;
-		priv->magicgpc918      = 0x00124925;
+		break;
+	case 0xc1: /* 2/0/0/0, 1 */
+		priv->magic_not_rop_nr = 0x01;
+		break;
+	case 0xc8: /* 4/4/3/4, 5 */
+		priv->magic_not_rop_nr = 0x06;
+		break;
+	case 0xce: /* 4/4/0/0, 4 */
+		priv->magic_not_rop_nr = 0x03;
 		break;
 	}
 
@@ -763,13 +872,16 @@
 			 priv->tp_nr[3], priv->rop_nr);
 		/* use 0xc3's values... */
 		priv->magic_not_rop_nr = 0x03;
-		priv->magicgpc918      = 0x00200000;
 	}
 
 	NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
 	NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
 	NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
 	NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
+	if (fermi >= 0x9197)
+		NVOBJ_CLASS(dev, 0x9197, GR); /* 3D (NVC1-) */
+	if (fermi >= 0x9297)
+		NVOBJ_CLASS(dev, 0x9297, GR); /* 3D (NVC8-) */
 	NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */
 	return 0;
 
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.fuc b/drivers/gpu/drm/nouveau/nvc0_graph.fuc
new file mode 100644
index 0000000..2a4b6dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.fuc
@@ -0,0 +1,400 @@
+/* fuc microcode util functions for nvc0 PGRAPH
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)')
+define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))')
+
+ifdef(`include_code', `
+// Error codes
+define(`E_BAD_COMMAND', 0x01)
+define(`E_CMD_OVERFLOW', 0x02)
+
+// Util macros to help with debugging ucode hangs etc
+define(`T_WAIT', 0)
+define(`T_MMCTX', 1)
+define(`T_STRWAIT', 2)
+define(`T_STRINIT', 3)
+define(`T_AUTO', 4)
+define(`T_CHAN', 5)
+define(`T_LOAD', 6)
+define(`T_SAVE', 7)
+define(`T_LCHAN', 8)
+define(`T_LCTXH', 9)
+
+define(`trace_set', `
+	mov $r8 0x83c
+	shl b32 $r8 6
+	clear b32 $r9
+	bset $r9 $1
+	iowr I[$r8 + 0x000] $r9		// CC_SCRATCH[7]
+')
+
+define(`trace_clr', `
+	mov $r8 0x85c
+	shl b32 $r8 6
+	clear b32 $r9
+	bset $r9 $1
+	iowr I[$r8 + 0x000] $r9		// CC_SCRATCH[7]
+')
+
+// queue_put - add request to queue
+//
+// In : $r13 queue pointer
+//	$r14 command
+//	$r15 data
+//
+queue_put:
+	// make sure we have space..
+	ld b32 $r8 D[$r13 + 0x0]	// GET
+	ld b32 $r9 D[$r13 + 0x4]	// PUT
+	xor $r8 8
+	cmpu b32 $r8 $r9
+	bra ne queue_put_next
+		mov $r15 E_CMD_OVERFLOW
+		call error
+		ret
+
+	// store cmd/data on queue
+	queue_put_next:
+	and $r8 $r9 7
+	shl b32 $r8 3
+	add b32 $r8 $r13
+	add b32 $r8 8
+	st b32 D[$r8 + 0x0] $r14
+	st b32 D[$r8 + 0x4] $r15
+
+	// update PUT
+	add b32 $r9 1
+	and $r9 0xf
+	st b32 D[$r13 + 0x4] $r9
+	ret
+
+// queue_get - fetch request from queue
+//
+// In : $r13 queue pointer
+//
+// Out:	$p1  clear on success (data available)
+//	$r14 command
+// 	$r15 data
+//
+queue_get:
+	bset $flags $p1
+	ld b32 $r8 D[$r13 + 0x0]	// GET
+	ld b32 $r9 D[$r13 + 0x4]	// PUT
+	cmpu b32 $r8 $r9
+	bra e queue_get_done
+		// fetch first cmd/data pair
+		and $r9 $r8 7
+		shl b32 $r9 3
+		add b32 $r9 $r13
+		add b32 $r9 8
+		ld b32 $r14 D[$r9 + 0x0]
+		ld b32 $r15 D[$r9 + 0x4]
+
+		// update GET
+		add b32 $r8 1
+		and $r8 0xf
+		st b32 D[$r13 + 0x0] $r8
+		bclr $flags $p1
+queue_get_done:
+	ret
+
+// nv_rd32 - read 32-bit value from nv register
+//
+// In : $r14 register
+// Out: $r15 value
+//
+nv_rd32:
+	mov $r11 0x728
+	shl b32 $r11 6
+	mov b32 $r12 $r14
+	bset $r12 31			// MMIO_CTRL_PENDING
+	iowr I[$r11 + 0x000] $r12	// MMIO_CTRL
+	nv_rd32_wait:
+		iord $r12 I[$r11 + 0x000]
+		xbit $r12 $r12 31
+		bra ne nv_rd32_wait
+	mov $r10 6			// DONE_MMIO_RD
+	call wait_doneo
+	iord $r15 I[$r11 + 0x100]	// MMIO_RDVAL
+	ret
+
+// nv_wr32 - write 32-bit value to nv register
+//
+// In : $r14 register
+//      $r15 value
+//
+nv_wr32:
+	mov $r11 0x728
+	shl b32 $r11 6
+	iowr I[$r11 + 0x200] $r15	// MMIO_WRVAL
+	mov b32 $r12 $r14
+	bset $r12 31			// MMIO_CTRL_PENDING
+	bset $r12 30			// MMIO_CTRL_WRITE
+	iowr I[$r11 + 0x000] $r12	// MMIO_CTRL
+	nv_wr32_wait:
+		iord $r12 I[$r11 + 0x000]
+		xbit $r12 $r12 31
+		bra ne nv_wr32_wait
+	ret
+
+// (re)set watchdog timer
+//
+// In : $r15 timeout
+//
+watchdog_reset:
+	mov $r8 0x430
+	shl b32 $r8 6
+	bset $r15 31
+	iowr I[$r8 + 0x000] $r15
+	ret
+
+// clear watchdog timer
+watchdog_clear:
+	mov $r8 0x430
+	shl b32 $r8 6
+	iowr I[$r8 + 0x000] $r0
+	ret
+
+// wait_done{z,o} - wait on FUC_DONE bit to become clear/set
+//
+// In : $r10 bit to wait on
+//
+define(`wait_done', `
+$1:
+	trace_set(T_WAIT);
+	mov $r8 0x818
+	shl b32 $r8 6
+	iowr I[$r8 + 0x000] $r10	// CC_SCRATCH[6] = wait bit
+	wait_done_$1:
+		mov $r8 0x400
+		shl b32 $r8 6
+		iord $r8 I[$r8 + 0x000]	// DONE
+		xbit $r8 $r8 $r10
+		bra $2 wait_done_$1
+	trace_clr(T_WAIT)
+	ret
+')
+wait_done(wait_donez, ne)
+wait_done(wait_doneo, e)
+
+// mmctx_size - determine size of a mmio list transfer
+//
+// In : $r14 mmio list head
+//      $r15 mmio list tail
+// Out: $r15 transfer size (in bytes)
+//
+mmctx_size:
+	clear b32 $r9
+	nv_mmctx_size_loop:
+		ld b32 $r8 D[$r14]
+		shr b32 $r8 26
+		add b32 $r8 1
+		shl b32 $r8 2
+		add b32 $r9 $r8
+		add b32 $r14 4
+		cmpu b32 $r14 $r15
+		bra ne nv_mmctx_size_loop
+	mov b32 $r15 $r9
+	ret
+
+// mmctx_xfer - execute a list of mmio transfers
+//
+// In : $r10 flags
+//		bit 0: direction (0 = save, 1 = load)
+//		bit 1: set if first transfer
+//		bit 2: set if last transfer
+//	$r11 base
+//	$r12 mmio list head
+//	$r13 mmio list tail
+//	$r14 multi_stride
+//	$r15 multi_mask
+//
+mmctx_xfer:
+	trace_set(T_MMCTX)
+	mov $r8 0x710
+	shl b32 $r8 6
+	clear b32 $r9
+	or $r11 $r11
+	bra e mmctx_base_disabled
+		iowr I[$r8 + 0x000] $r11	// MMCTX_BASE
+		bset $r9 0			// BASE_EN
+	mmctx_base_disabled:
+	or $r14 $r14
+	bra e mmctx_multi_disabled
+		iowr I[$r8 + 0x200] $r14 	// MMCTX_MULTI_STRIDE
+		iowr I[$r8 + 0x300] $r15 	// MMCTX_MULTI_MASK
+		bset $r9 1			// MULTI_EN
+	mmctx_multi_disabled:
+	add b32 $r8 0x100
+
+	xbit $r11 $r10 0
+	shl b32 $r11 16			// DIR
+	bset $r11 12			// QLIMIT = 0x10
+	xbit $r14 $r10 1
+	shl b32 $r14 17
+	or $r11 $r14			// START_TRIGGER
+	iowr I[$r8 + 0x000] $r11	// MMCTX_CTRL
+
+	// loop over the mmio list, and send requests to the hw
+	mmctx_exec_loop:
+		// wait for space in mmctx queue
+		mmctx_wait_free:
+			iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
+			and $r14 0x1f
+			bra e mmctx_wait_free
+
+		// queue up an entry
+		ld b32 $r14 D[$r12]
+		or $r14 $r9
+		iowr I[$r8 + 0x300] $r14
+		add b32 $r12 4
+		cmpu b32 $r12 $r13
+		bra ne mmctx_exec_loop
+
+	xbit $r11 $r10 2
+	bra ne mmctx_stop
+		// wait for queue to empty
+		mmctx_fini_wait:
+			iord $r11 I[$r8 + 0x000]	// MMCTX_CTRL
+			and $r11 0x1f
+			cmpu b32 $r11 0x10
+			bra ne mmctx_fini_wait
+		mov $r10 2				// DONE_MMCTX
+		call wait_donez
+		bra mmctx_done
+	mmctx_stop:
+		xbit $r11 $r10 0
+		shl b32 $r11 16			// DIR
+		bset $r11 12			// QLIMIT = 0x10
+		bset $r11 18			// STOP_TRIGGER
+		iowr I[$r8 + 0x000] $r11	// MMCTX_CTRL
+		mmctx_stop_wait:
+			// wait for STOP_TRIGGER to clear
+			iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
+			xbit $r11 $r11 18
+			bra ne mmctx_stop_wait
+	mmctx_done:
+	trace_clr(T_MMCTX)
+	ret
+
+// Wait for DONE_STRAND
+//
+strand_wait:
+	push $r10
+	mov $r10 2
+	call wait_donez
+	pop $r10
+	ret
+
+// unknown - call before issuing strand commands
+//
+strand_pre:
+	mov $r8 0x4afc
+	sethi $r8 0x20000
+	mov $r9 0xc
+	iowr I[$r8] $r9
+	call strand_wait
+	ret
+
+// unknown - call after issuing strand commands
+//
+strand_post:
+	mov $r8 0x4afc
+	sethi $r8 0x20000
+	mov $r9 0xd
+	iowr I[$r8] $r9
+	call strand_wait
+	ret
+
+// Selects strand set?!
+//
+// In: $r14 id
+//
+strand_set:
+	mov $r10 0x4ffc
+	sethi $r10 0x20000
+	sub b32 $r11 $r10 0x500
+	mov $r12 0xf
+	iowr I[$r10 + 0x000] $r12		// 0x93c = 0xf
+	mov $r12 0xb
+	iowr I[$r11 + 0x000] $r12		// 0x928 = 0xb
+	call strand_wait
+	iowr I[$r10 + 0x000] $r14		// 0x93c = <id>
+	mov $r12 0xa
+	iowr I[$r11 + 0x000] $r12		// 0x928 = 0xa
+	call strand_wait
+	ret
+
+// Initialise strand context data
+//
+// In : $r15 context base
+// Out: $r15 context size (in bytes)
+//
+// Strandset(?) 3 hardcoded currently
+//
+strand_ctx_init:
+	trace_set(T_STRINIT)
+	call strand_pre
+	mov $r14 3
+	call strand_set
+	mov $r10 0x46fc
+	sethi $r10 0x20000
+	add b32 $r11 $r10 0x400
+	iowr I[$r10 + 0x100] $r0	// STRAND_FIRST_GENE = 0
+	mov $r12 1
+	iowr I[$r11 + 0x000] $r12	// STRAND_CMD = LATCH_FIRST_GENE
+	call strand_wait
+	sub b32 $r12 $r0 1
+	iowr I[$r10 + 0x000] $r12	// STRAND_GENE_CNT = 0xffffffff
+	mov $r12 2
+	iowr I[$r11 + 0x000] $r12	// STRAND_CMD = LATCH_GENE_CNT
+	call strand_wait
+	call strand_post
+
+	// read the size of each strand, poke the context offset of
+	// each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
+	// about it later then.
+	mov $r8 0x880
+	shl b32 $r8 6
+	iord $r9 I[$r8 + 0x000]		// STRANDS
+	add b32 $r8 0x2200
+	shr b32 $r14 $r15 8
+	ctx_init_strand_loop:
+		iowr I[$r8 + 0x000] $r14	// STRAND_SAVE_SWBASE
+		iowr I[$r8 + 0x100] $r14	// STRAND_LOAD_SWBASE
+		iord $r10 I[$r8 + 0x200]	// STRAND_SIZE
+		shr b32 $r10 6
+		add b32 $r10 1
+		add b32 $r14 $r10
+		add b32 $r8 4
+		sub b32 $r9 1
+		bra ne ctx_init_strand_loop
+
+	shl b32 $r14 8
+	sub b32 $r15 $r14 $r15
+	trace_clr(T_STRINIT)
+	ret
+')
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h
index f5d184e0..55689e9 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.h
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.h
@@ -57,8 +57,7 @@
 	struct nouveau_gpuobj *unk4188b4;
 	struct nouveau_gpuobj *unk4188b8;
 
-	u8  magic_not_rop_nr;
-	u32 magicgpc918;
+	u8 magic_not_rop_nr;
 };
 
 struct nvc0_graph_chan {
@@ -72,4 +71,25 @@
 
 int nvc0_grctx_generate(struct nouveau_channel *);
 
+/* nvc0_graph.c uses this also to determine supported chipsets */
+static inline u32
+nvc0_graph_class(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	switch (dev_priv->chipset) {
+	case 0xc0:
+	case 0xc3:
+	case 0xc4:
+	case 0xce: /* guess, mmio trace shows only 0x9097 state */
+		return 0x9097;
+	case 0xc1:
+		return 0x9197;
+	case 0xc8:
+		return 0x9297;
+	default:
+		return 0;
+	}
+}
+
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c
index 6df0661..31018ea 100644
--- a/drivers/gpu/drm/nouveau/nvc0_grctx.c
+++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c
@@ -45,6 +45,9 @@
 static void
 nvc0_grctx_generate_9097(struct drm_device *dev)
 {
+	u32 fermi = nvc0_graph_class(dev);
+	u32 mthd;
+
 	nv_mthd(dev, 0x9097, 0x0800, 0x00000000);
 	nv_mthd(dev, 0x9097, 0x0840, 0x00000000);
 	nv_mthd(dev, 0x9097, 0x0880, 0x00000000);
@@ -824,134 +827,10 @@
 	nv_mthd(dev, 0x9097, 0x1eb8, 0x00000001);
 	nv_mthd(dev, 0x9097, 0x1ed8, 0x00000001);
 	nv_mthd(dev, 0x9097, 0x1ef8, 0x00000001);
-	nv_mthd(dev, 0x9097, 0x3400, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3404, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3408, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x340c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3410, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3414, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3418, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x341c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3420, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3424, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3428, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x342c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3430, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3434, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3438, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x343c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3440, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3444, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3448, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x344c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3450, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3454, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3458, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x345c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3460, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3464, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3468, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x346c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3470, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3474, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3478, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x347c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3480, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3484, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3488, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x348c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3490, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3494, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3498, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x349c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34a0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34a4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34a8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34ac, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34b0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34b4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34b8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34bc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34c0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34c4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34c8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34cc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34d0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34d4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34d8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34dc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34e0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34e4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34e8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34ec, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34f0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34f4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34f8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x34fc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3500, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3504, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3508, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x350c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3510, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3514, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3518, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x351c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3520, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3524, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3528, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x352c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3530, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3534, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3538, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x353c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3540, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3544, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3548, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x354c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3550, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3554, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3558, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x355c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3560, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3564, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3568, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x356c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3570, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3574, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3578, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x357c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3580, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3584, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3588, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x358c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3590, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3594, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x3598, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x359c, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35a0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35a4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35a8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35ac, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35b0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35b4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35b8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35bc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35c0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35c4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35c8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35cc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35d0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35d4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35d8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35dc, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35e0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35e4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35e8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35ec, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35f0, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35f4, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35f8, 0x00000000);
-	nv_mthd(dev, 0x9097, 0x35fc, 0x00000000);
+	if (fermi == 0x9097) {
+		for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+			nv_mthd(dev, 0x9097, mthd, 0x00000000);
+	}
 	nv_mthd(dev, 0x9097, 0x030c, 0x00000001);
 	nv_mthd(dev, 0x9097, 0x1944, 0x00000000);
 	nv_mthd(dev, 0x9097, 0x1514, 0x00000000);
@@ -1321,6 +1200,37 @@
 }
 
 static void
+nvc0_grctx_generate_9197(struct drm_device *dev)
+{
+	u32 fermi = nvc0_graph_class(dev);
+	u32 mthd;
+
+	if (fermi == 0x9197) {
+		for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+			nv_mthd(dev, 0x9197, mthd, 0x00000000);
+	}
+	nv_mthd(dev, 0x9197, 0x02e4, 0x0000b001);
+}
+
+static void
+nvc0_grctx_generate_9297(struct drm_device *dev)
+{
+	u32 fermi = nvc0_graph_class(dev);
+	u32 mthd;
+
+	if (fermi == 0x9297) {
+		for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
+			nv_mthd(dev, 0x9297, mthd, 0x00000000);
+	}
+	nv_mthd(dev, 0x9297, 0x036c, 0x00000000);
+	nv_mthd(dev, 0x9297, 0x0370, 0x00000000);
+	nv_mthd(dev, 0x9297, 0x07a4, 0x00000000);
+	nv_mthd(dev, 0x9297, 0x07a8, 0x00000000);
+	nv_mthd(dev, 0x9297, 0x0374, 0x00000000);
+	nv_mthd(dev, 0x9297, 0x0378, 0x00000020);
+}
+
+static void
 nvc0_grctx_generate_902d(struct drm_device *dev)
 {
 	nv_mthd(dev, 0x902d, 0x0200, 0x000000cf);
@@ -1559,8 +1469,15 @@
 static void
 nvc0_grctx_generate_shaders(struct drm_device *dev)
 {
-	nv_wr32(dev, 0x405800, 0x078000bf);
-	nv_wr32(dev, 0x405830, 0x02180000);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->chipset != 0xc1) {
+		nv_wr32(dev, 0x405800, 0x078000bf);
+		nv_wr32(dev, 0x405830, 0x02180000);
+	} else {
+		nv_wr32(dev, 0x405800, 0x0f8000bf);
+		nv_wr32(dev, 0x405830, 0x02180218);
+	}
 	nv_wr32(dev, 0x405834, 0x00000000);
 	nv_wr32(dev, 0x405838, 0x00000000);
 	nv_wr32(dev, 0x405854, 0x00000000);
@@ -1586,10 +1503,16 @@
 static void
 nvc0_grctx_generate_unk64xx(struct drm_device *dev)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
 	nv_wr32(dev, 0x4064a8, 0x00000000);
 	nv_wr32(dev, 0x4064ac, 0x00003fff);
 	nv_wr32(dev, 0x4064b4, 0x00000000);
 	nv_wr32(dev, 0x4064b8, 0x00000000);
+	if (dev_priv->chipset == 0xc1) {
+		nv_wr32(dev, 0x4064c0, 0x80140078);
+		nv_wr32(dev, 0x4064c4, 0x0086ffff);
+	}
 }
 
 static void
@@ -1622,21 +1545,14 @@
 nvc0_grctx_generate_rop(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chipset = dev_priv->chipset;
 
 	/* ROPC_BROADCAST */
 	nv_wr32(dev, 0x408800, 0x02802a3c);
 	nv_wr32(dev, 0x408804, 0x00000040);
-	nv_wr32(dev, 0x408808, 0x0003e00d);
-	switch (dev_priv->chipset) {
-	case 0xc0:
-		nv_wr32(dev, 0x408900, 0x0080b801);
-		break;
-	case 0xc3:
-	case 0xc4:
-		nv_wr32(dev, 0x408900, 0x3080b801);
-		break;
-	}
-	nv_wr32(dev, 0x408904, 0x02000001);
+	nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005);
+	nv_wr32(dev, 0x408900, 0x3080b801);
+	nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001);
 	nv_wr32(dev, 0x408908, 0x00c80929);
 	nv_wr32(dev, 0x40890c, 0x00000000);
 	nv_wr32(dev, 0x408980, 0x0000011d);
@@ -1645,6 +1561,8 @@
 static void
 nvc0_grctx_generate_gpc(struct drm_device *dev)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chipset = dev_priv->chipset;
 	int i;
 
 	/* GPC_BROADCAST */
@@ -1676,7 +1594,7 @@
 	nv_wr32(dev, 0x41880c, 0x00000000);
 	nv_wr32(dev, 0x418810, 0x00000000);
 	nv_wr32(dev, 0x418828, 0x00008442);
-	nv_wr32(dev, 0x418830, 0x00000001);
+	nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001);
 	nv_wr32(dev, 0x4188d8, 0x00000008);
 	nv_wr32(dev, 0x4188e0, 0x01000000);
 	nv_wr32(dev, 0x4188e8, 0x00000000);
@@ -1684,7 +1602,7 @@
 	nv_wr32(dev, 0x4188f0, 0x00000000);
 	nv_wr32(dev, 0x4188f4, 0x00000000);
 	nv_wr32(dev, 0x4188f8, 0x00000000);
-	nv_wr32(dev, 0x4188fc, 0x00100000);
+	nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018);
 	nv_wr32(dev, 0x41891c, 0x00ff00ff);
 	nv_wr32(dev, 0x418924, 0x00000000);
 	nv_wr32(dev, 0x418928, 0x00ffff00);
@@ -1715,6 +1633,8 @@
 	nv_wr32(dev, 0x418c24, 0x00000000);
 	nv_wr32(dev, 0x418c28, 0x00000000);
 	nv_wr32(dev, 0x418c2c, 0x00000000);
+	if (chipset == 0xc1)
+		nv_wr32(dev, 0x418c6c, 0x00000001);
 	nv_wr32(dev, 0x418c80, 0x20200004);
 	nv_wr32(dev, 0x418c8c, 0x00000001);
 	nv_wr32(dev, 0x419000, 0x00000780);
@@ -1727,10 +1647,13 @@
 nvc0_grctx_generate_tp(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chipset = dev_priv->chipset;
 
 	/* GPC_BROADCAST.TP_BROADCAST */
+	nv_wr32(dev, 0x419818, 0x00000000);
+	nv_wr32(dev, 0x41983c, 0x00038bc7);
 	nv_wr32(dev, 0x419848, 0x00000000);
-	nv_wr32(dev, 0x419864, 0x0000012a);
+	nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129);
 	nv_wr32(dev, 0x419888, 0x00000000);
 	nv_wr32(dev, 0x419a00, 0x000001f0);
 	nv_wr32(dev, 0x419a04, 0x00000001);
@@ -1740,8 +1663,8 @@
 	nv_wr32(dev, 0x419a14, 0x00000200);
 	nv_wr32(dev, 0x419a1c, 0x00000000);
 	nv_wr32(dev, 0x419a20, 0x00000800);
-	if (dev_priv->chipset != 0xc0)
-		nv_wr32(dev, 0x00419ac4, 0x0007f440); /* 0xc3 */
+	if (chipset != 0xc0 && chipset != 0xc8)
+		nv_wr32(dev, 0x00419ac4, 0x0007f440);
 	nv_wr32(dev, 0x419b00, 0x0a418820);
 	nv_wr32(dev, 0x419b04, 0x062080e6);
 	nv_wr32(dev, 0x419b08, 0x020398a4);
@@ -1749,17 +1672,19 @@
 	nv_wr32(dev, 0x419b10, 0x0a418820);
 	nv_wr32(dev, 0x419b14, 0x000000e6);
 	nv_wr32(dev, 0x419bd0, 0x00900103);
-	nv_wr32(dev, 0x419be0, 0x00000001);
+	nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001);
 	nv_wr32(dev, 0x419be4, 0x00000000);
 	nv_wr32(dev, 0x419c00, 0x00000002);
 	nv_wr32(dev, 0x419c04, 0x00000006);
 	nv_wr32(dev, 0x419c08, 0x00000002);
 	nv_wr32(dev, 0x419c20, 0x00000000);
-	nv_wr32(dev, 0x419cbc, 0x28137606);
+	nv_wr32(dev, 0x419cb0, 0x00060048); //XXX: 0xce 0x00020048
 	nv_wr32(dev, 0x419ce8, 0x00000000);
 	nv_wr32(dev, 0x419cf4, 0x00000183);
-	nv_wr32(dev, 0x419d20, 0x02180000);
+	nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000);
 	nv_wr32(dev, 0x419d24, 0x00001fff);
+	if (chipset == 0xc1)
+		nv_wr32(dev, 0x419d44, 0x02180218);
 	nv_wr32(dev, 0x419e04, 0x00000000);
 	nv_wr32(dev, 0x419e08, 0x00000000);
 	nv_wr32(dev, 0x419e0c, 0x00000000);
@@ -1785,11 +1710,11 @@
 	nv_wr32(dev, 0x419e8c, 0x00000000);
 	nv_wr32(dev, 0x419e90, 0x00000000);
 	nv_wr32(dev, 0x419e98, 0x00000000);
-	if (dev_priv->chipset != 0xc0)
+	if (chipset != 0xc0 && chipset != 0xc8)
 		nv_wr32(dev, 0x419ee0, 0x00011110);
 	nv_wr32(dev, 0x419f50, 0x00000000);
 	nv_wr32(dev, 0x419f54, 0x00000000);
-	if (dev_priv->chipset != 0xc0)
+	if (chipset != 0xc0 && chipset != 0xc8)
 		nv_wr32(dev, 0x419f58, 0x00000000);
 }
 
@@ -1801,6 +1726,7 @@
 	struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
 	struct drm_device *dev = chan->dev;
 	int i, gpc, tp, id;
+	u32 fermi = nvc0_graph_class(dev);
 	u32 r000260, tmp;
 
 	r000260 = nv_rd32(dev, 0x000260);
@@ -1857,10 +1783,11 @@
 	nv_wr32(dev, 0x40587c, 0x00000000);
 
 	if (1) {
-		const u8 chipset_tp_max[] = { 16, 0, 0, 4, 8 };
+		const u8 chipset_tp_max[] = { 16, 4, 0, 4, 8, 0, 0, 0,
+					      16, 0, 0, 0, 0, 0, 8, 0 };
 		u8 max = chipset_tp_max[dev_priv->chipset & 0x0f];
 		u8 tpnr[GPC_MAX];
-		u8 data[32];
+		u8 data[TP_MAX];
 
 		memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
 		memset(data, 0x1f, sizeof(data));
@@ -2633,6 +2560,8 @@
 	nv_icmd(dev, 0x0000053f, 0xffff0000);
 	nv_icmd(dev, 0x00000585, 0x0000003f);
 	nv_icmd(dev, 0x00000576, 0x00000003);
+	if (dev_priv->chipset == 0xc1)
+		nv_icmd(dev, 0x0000057b, 0x00000059);
 	nv_icmd(dev, 0x00000586, 0x00000040);
 	nv_icmd(dev, 0x00000582, 0x00000080);
 	nv_icmd(dev, 0x00000583, 0x00000080);
@@ -2865,6 +2794,10 @@
 	nv_wr32(dev, 0x404154, 0x00000400);
 
 	nvc0_grctx_generate_9097(dev);
+	if (fermi >= 0x9197)
+		nvc0_grctx_generate_9197(dev);
+	if (fermi >= 0x9297)
+		nvc0_grctx_generate_9297(dev);
 	nvc0_grctx_generate_902d(dev);
 	nvc0_grctx_generate_9039(dev);
 	nvc0_grctx_generate_90c0(dev);
diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc
new file mode 100644
index 0000000..0ec2add
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc
@@ -0,0 +1,474 @@
+/* fuc microcode for nvc0 PGRAPH/GPC
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/* To build:
+ *    m4 nvc0_grgpc.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grgpc.fuc.h
+ */
+
+/* TODO
+ * - bracket certain functions with scratch writes, useful for debugging
+ * - watchdog timer around ctx operations
+ */
+
+.section nvc0_grgpc_data
+include(`nvc0_graph.fuc')
+gpc_id:			.b32 0
+gpc_mmio_list_head:	.b32 0
+gpc_mmio_list_tail:	.b32 0
+
+tpc_count:		.b32 0
+tpc_mask:		.b32 0
+tpc_mmio_list_head:	.b32 0
+tpc_mmio_list_tail:	.b32 0
+
+cmd_queue:		queue_init
+
+// chipset descriptions
+chipsets:
+.b8  0xc0 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc0_tpc_mmio_tail
+.b8  0xc1 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc1_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc1_tpc_mmio_tail
+.b8  0xc3 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8  0xc4 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8  0xc8 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc0_tpc_mmio_tail
+.b8  0xce 0 0 0
+.b16 nvc0_gpc_mmio_head
+.b16 nvc0_gpc_mmio_tail
+.b16 nvc0_tpc_mmio_head
+.b16 nvc3_tpc_mmio_tail
+.b8  0 0 0 0
+
+// GPC mmio lists
+nvc0_gpc_mmio_head:
+mmctx_data(0x000380, 1)
+mmctx_data(0x000400, 6)
+mmctx_data(0x000450, 9)
+mmctx_data(0x000600, 1)
+mmctx_data(0x000684, 1)
+mmctx_data(0x000700, 5)
+mmctx_data(0x000800, 1)
+mmctx_data(0x000808, 3)
+mmctx_data(0x000828, 1)
+mmctx_data(0x000830, 1)
+mmctx_data(0x0008d8, 1)
+mmctx_data(0x0008e0, 1)
+mmctx_data(0x0008e8, 6)
+mmctx_data(0x00091c, 1)
+mmctx_data(0x000924, 3)
+mmctx_data(0x000b00, 1)
+mmctx_data(0x000b08, 6)
+mmctx_data(0x000bb8, 1)
+mmctx_data(0x000c08, 1)
+mmctx_data(0x000c10, 8)
+mmctx_data(0x000c80, 1)
+mmctx_data(0x000c8c, 1)
+mmctx_data(0x001000, 3)
+mmctx_data(0x001014, 1)
+nvc0_gpc_mmio_tail:
+mmctx_data(0x000c6c, 1);
+nvc1_gpc_mmio_tail:
+
+// TPC mmio lists
+nvc0_tpc_mmio_head:
+mmctx_data(0x000018, 1)
+mmctx_data(0x00003c, 1)
+mmctx_data(0x000048, 1)
+mmctx_data(0x000064, 1)
+mmctx_data(0x000088, 1)
+mmctx_data(0x000200, 6)
+mmctx_data(0x00021c, 2)
+mmctx_data(0x000300, 6)
+mmctx_data(0x0003d0, 1)
+mmctx_data(0x0003e0, 2)
+mmctx_data(0x000400, 3)
+mmctx_data(0x000420, 1)
+mmctx_data(0x0004b0, 1)
+mmctx_data(0x0004e8, 1)
+mmctx_data(0x0004f4, 1)
+mmctx_data(0x000520, 2)
+mmctx_data(0x000604, 4)
+mmctx_data(0x000644, 20)
+mmctx_data(0x000698, 1)
+mmctx_data(0x000750, 2)
+nvc0_tpc_mmio_tail:
+mmctx_data(0x000758, 1)
+mmctx_data(0x0002c4, 1)
+mmctx_data(0x0004bc, 1)
+mmctx_data(0x0006e0, 1)
+nvc3_tpc_mmio_tail:
+mmctx_data(0x000544, 1)
+nvc1_tpc_mmio_tail:
+
+
+.section nvc0_grgpc_code
+bra init
+define(`include_code')
+include(`nvc0_graph.fuc')
+
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0_graph.fuc)
+//
+error:
+	push $r14
+	mov $r14 -0x67ec 	// 0x9814
+	sethi $r14 0x400000
+	call nv_wr32		// HUB_CTXCTL_CC_SCRATCH[5] = error code
+	add b32 $r14 0x41c
+	mov $r15 1
+	call nv_wr32		// HUB_CTXCTL_INTR_UP_SET
+	pop $r14
+	ret
+
+// GPC fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Input:
+//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
+//   CC_SCRATCH[1]: context base
+//
+// Output:
+//   CC_SCRATCH[0]:
+//	     31:31: set to signal completion
+//   CC_SCRATCH[1]:
+//	      31:0: GPC context size
+//
+init:
+	clear b32 $r0
+	mov $sp $r0
+
+	// enable fifo access
+	mov $r1 0x1200
+	mov $r2 2
+	iowr I[$r1 + 0x000] $r2		// FIFO_ENABLE
+
+	// setup i0 handler, and route all interrupts to it
+	mov $r1 ih
+	mov $iv0 $r1
+	mov $r1 0x400
+	iowr I[$r1 + 0x300] $r0		// INTR_DISPATCH
+
+	// enable fifo interrupt
+	mov $r2 4
+	iowr I[$r1 + 0x000] $r2		// INTR_EN_SET
+
+	// enable interrupts
+	bset $flags ie0
+
+	// figure out which GPC we are, and how many TPCs we have
+	mov $r1 0x608
+	shl b32 $r1 6
+	iord $r2 I[$r1 + 0x000]		// UNITS
+	mov $r3 1
+	and $r2 0x1f
+	shl b32 $r3 $r2
+	sub b32 $r3 1
+	st b32 D[$r0 + tpc_count] $r2
+	st b32 D[$r0 + tpc_mask] $r3
+	add b32 $r1 0x400
+	iord $r2 I[$r1 + 0x000]		// MYINDEX
+	st b32 D[$r0 + gpc_id] $r2
+
+	// find context data for this chipset
+	mov $r2 0x800
+	shl b32 $r2 6
+	iord $r2 I[$r2 + 0x000]		// CC_SCRATCH[0]
+	mov $r1 chipsets - 12
+	init_find_chipset:
+		add b32 $r1 12
+		ld b32 $r3 D[$r1 + 0x00]
+		cmpu b32 $r3 $r2
+		bra e init_context
+		cmpu b32 $r3 0
+		bra ne init_find_chipset
+		// unknown chipset
+		ret
+
+	// initialise context base, and size tracking
+	init_context:
+	mov $r2 0x800
+	shl b32 $r2 6
+	iord $r2 I[$r2 + 0x100]	// CC_SCRATCH[1], initial base
+	clear b32 $r3		// track GPC context size here
+
+	// set mmctx base addresses now so we don't have to do it later,
+	// they don't currently ever change
+	mov $r4 0x700
+	shl b32 $r4 6
+	shr b32 $r5 $r2 8
+	iowr I[$r4 + 0x000] $r5		// MMCTX_SAVE_SWBASE
+	iowr I[$r4 + 0x100] $r5		// MMCTX_LOAD_SWBASE
+
+	// calculate GPC mmio context size, store the chipset-specific
+	// mmio list pointers somewhere we can get at them later without
+	// re-parsing the chipset list
+	clear b32 $r14
+	clear b32 $r15
+	ld b16 $r14 D[$r1 + 4]
+	ld b16 $r15 D[$r1 + 6]
+	st b16 D[$r0 + gpc_mmio_list_head] $r14
+	st b16 D[$r0 + gpc_mmio_list_tail] $r15
+	call mmctx_size
+	add b32 $r2 $r15
+	add b32 $r3 $r15
+
+	// calculate per-TPC mmio context size, store the list pointers
+	ld b16 $r14 D[$r1 + 8]
+	ld b16 $r15 D[$r1 + 10]
+	st b16 D[$r0 + tpc_mmio_list_head] $r14
+	st b16 D[$r0 + tpc_mmio_list_tail] $r15
+	call mmctx_size
+	ld b32 $r14 D[$r0 + tpc_count]
+	mulu $r14 $r15
+	add b32 $r2 $r14
+	add b32 $r3 $r14
+
+	// round up base/size to 256 byte boundary (for strand SWBASE)
+	add b32 $r4 0x1300
+	shr b32 $r3 2
+	iowr I[$r4 + 0x000] $r3		// MMCTX_LOAD_COUNT, wtf for?!?
+	shr b32 $r2 8
+	shr b32 $r3 6
+	add b32 $r2 1
+	add b32 $r3 1
+	shl b32 $r2 8
+	shl b32 $r3 8
+
+	// calculate size of strand context data
+	mov b32 $r15 $r2
+	call strand_ctx_init
+	add b32 $r3 $r15
+
+	// save context size, and tell HUB we're done
+	mov $r1 0x800
+	shl b32 $r1 6
+	iowr I[$r1 + 0x100] $r3		// CC_SCRATCH[1]  = context size
+	add b32 $r1 0x800
+	clear b32 $r2
+	bset $r2 31
+	iowr I[$r1 + 0x000] $r2		// CC_SCRATCH[0] |= 0x80000000
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+	bset $flags $p0
+	sleep $p0
+	mov $r13 cmd_queue
+	call queue_get
+	bra $p1 main
+
+	// 0x0000-0x0003 are all context transfers
+	cmpu b32 $r14 0x04
+	bra nc main_not_ctx_xfer
+		// fetch $flags and mask off $p1/$p2
+		mov $r1 $flags
+		mov $r2 0x0006
+		not b32 $r2
+		and $r1 $r2
+		// set $p1/$p2 according to transfer type
+		shl b32 $r14 1
+		or $r1 $r14
+		mov $flags $r1
+		// transfer context data
+		call ctx_xfer
+		bra main
+
+	main_not_ctx_xfer:
+	shl b32 $r15 $r14 16
+	or $r15 E_BAD_COMMAND
+	call error
+	bra main
+
+// interrupt handler
+ih:
+	push $r8
+	mov $r8 $flags
+	push $r8
+	push $r9
+	push $r10
+	push $r11
+	push $r13
+	push $r14
+	push $r15
+
+	// incoming fifo command?
+	iord $r10 I[$r0 + 0x200]	// INTR
+	and $r11 $r10 0x00000004
+	bra e ih_no_fifo
+		// queue incoming fifo command for later processing
+		mov $r11 0x1900
+		mov $r13 cmd_queue
+		iord $r14 I[$r11 + 0x100]	// FIFO_CMD
+		iord $r15 I[$r11 + 0x000]	// FIFO_DATA
+		call queue_put
+		add b32 $r11 0x400
+		mov $r14 1
+		iowr I[$r11 + 0x000] $r14	// FIFO_ACK
+
+	// ack, and wake up main()
+	ih_no_fifo:
+	iowr I[$r0 + 0x100] $r10	// INTR_ACK
+
+	pop $r15
+	pop $r14
+	pop $r13
+	pop $r11
+	pop $r10
+	pop $r9
+	pop $r8
+	mov $flags $r8
+	pop $r8
+	bclr $flags $p0
+	iret
+
+// Set this GPC's bit in HUB_BAR, used to signal completion of various
+// activities to the HUB fuc
+//
+hub_barrier_done:
+	mov $r15 1
+	ld b32 $r14 D[$r0 + gpc_id]
+	shl b32 $r15 $r14
+	mov $r14 -0x6be8 	// 0x409418 - HUB_BAR_SET
+	sethi $r14 0x400000
+	call nv_wr32
+	ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off?  Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+	mov $r14 0x614
+	shl b32 $r14 6
+	mov $r15 0x020
+	iowr I[$r14] $r15	// GPC_RED_SWITCH = POWER
+	mov $r15 8
+	ctx_redswitch_delay:
+		sub b32 $r15 1
+		bra ne ctx_redswitch_delay
+	mov $r15 0xa20
+	iowr I[$r14] $r15	// GPC_RED_SWITCH = UNK11, ENABLE, POWER
+	ret
+
+// Transfer GPC context data between GPU and storage area
+//
+// In: $r15 context base address
+//     $p1 clear on save, set on load
+//     $p2 set if opposite direction done/will be done, so:
+//		on save it means: "a load will follow this save"
+//		on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+	// set context base address
+	mov $r1 0xa04
+	shl b32 $r1 6
+	iowr I[$r1 + 0x000] $r15// MEM_BASE
+	bra not $p1 ctx_xfer_not_load
+		call ctx_redswitch
+	ctx_xfer_not_load:
+
+	// strands
+	mov $r1 0x4afc
+	sethi $r1 0x20000
+	mov $r2 0xc
+	iowr I[$r1] $r2		// STRAND_CMD(0x3f) = 0x0c
+	call strand_wait
+	mov $r2 0x47fc
+	sethi $r2 0x20000
+	iowr I[$r2] $r0		// STRAND_FIRST_GENE(0x3f) = 0x00
+	xbit $r2 $flags $p1
+	add b32 $r2 3
+	iowr I[$r1] $r2		// STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+	// mmio context
+	xbit $r10 $flags $p1	// direction
+	or $r10 2		// first
+	mov $r11 0x0000
+	sethi $r11 0x500000
+	ld b32 $r12 D[$r0 + gpc_id]
+	shl b32 $r12 15
+	add b32 $r11 $r12	// base = NV_PGRAPH_GPCn
+	ld b32 $r12 D[$r0 + gpc_mmio_list_head]
+	ld b32 $r13 D[$r0 + gpc_mmio_list_tail]
+	mov $r14 0		// not multi
+	call mmctx_xfer
+
+	// per-TPC mmio context
+	xbit $r10 $flags $p1	// direction
+	or $r10 4		// last
+	mov $r11 0x4000
+	sethi $r11 0x500000	// base = NV_PGRAPH_GPC0_TPC0
+	ld b32 $r12 D[$r0 + gpc_id]
+	shl b32 $r12 15
+	add b32 $r11 $r12	// base = NV_PGRAPH_GPCn_TPC0
+	ld b32 $r12 D[$r0 + tpc_mmio_list_head]
+	ld b32 $r13 D[$r0 + tpc_mmio_list_tail]
+	ld b32 $r15 D[$r0 + tpc_mask]
+	mov $r14 0x800		// stride = 0x800
+	call mmctx_xfer
+
+	// wait for strands to finish
+	call strand_wait
+
+	// if load, or a save without a load following, do some
+	// unknown stuff that's done after finishing a block of
+	// strand commands
+	bra $p1 ctx_xfer_post
+	bra not $p2 ctx_xfer_done
+	ctx_xfer_post:
+		mov $r1 0x4afc
+		sethi $r1 0x20000
+		mov $r2 0xd
+		iowr I[$r1] $r2		// STRAND_CMD(0x3f) = 0x0d
+		call strand_wait
+
+	// mark completion in HUB's barrier
+	ctx_xfer_done:
+	call hub_barrier_done
+	ret
+
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h
new file mode 100644
index 0000000..1896c89
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grgpc.fuc.h
@@ -0,0 +1,483 @@
+uint32_t nvc0_grgpc_data[] = {
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x000000c0,
+	0x011000b0,
+	0x01640114,
+	0x000000c1,
+	0x011400b0,
+	0x01780114,
+	0x000000c3,
+	0x011000b0,
+	0x01740114,
+	0x000000c4,
+	0x011000b0,
+	0x01740114,
+	0x000000c8,
+	0x011000b0,
+	0x01640114,
+	0x000000ce,
+	0x011000b0,
+	0x01740114,
+	0x00000000,
+	0x00000380,
+	0x14000400,
+	0x20000450,
+	0x00000600,
+	0x00000684,
+	0x10000700,
+	0x00000800,
+	0x08000808,
+	0x00000828,
+	0x00000830,
+	0x000008d8,
+	0x000008e0,
+	0x140008e8,
+	0x0000091c,
+	0x08000924,
+	0x00000b00,
+	0x14000b08,
+	0x00000bb8,
+	0x00000c08,
+	0x1c000c10,
+	0x00000c80,
+	0x00000c8c,
+	0x08001000,
+	0x00001014,
+	0x00000c6c,
+	0x00000018,
+	0x0000003c,
+	0x00000048,
+	0x00000064,
+	0x00000088,
+	0x14000200,
+	0x0400021c,
+	0x14000300,
+	0x000003d0,
+	0x040003e0,
+	0x08000400,
+	0x00000420,
+	0x000004b0,
+	0x000004e8,
+	0x000004f4,
+	0x04000520,
+	0x0c000604,
+	0x4c000644,
+	0x00000698,
+	0x04000750,
+	0x00000758,
+	0x000002c4,
+	0x000004bc,
+	0x000006e0,
+	0x00000544,
+};
+
+uint32_t nvc0_grgpc_code[] = {
+	0x03060ef5,
+	0x9800d898,
+	0x86f001d9,
+	0x0489b808,
+	0xf00c1bf4,
+	0x21f502f7,
+	0x00f802ec,
+	0xb60798c4,
+	0x8dbb0384,
+	0x0880b600,
+	0x80008e80,
+	0x90b6018f,
+	0x0f94f001,
+	0xf801d980,
+	0x0131f400,
+	0x9800d898,
+	0x89b801d9,
+	0x210bf404,
+	0xb60789c4,
+	0x9dbb0394,
+	0x0890b600,
+	0x98009e98,
+	0x80b6019f,
+	0x0f84f001,
+	0xf400d880,
+	0x00f80132,
+	0x0728b7f1,
+	0xb906b4b6,
+	0xc9f002ec,
+	0x00bcd01f,
+	0xc800bccf,
+	0x1bf41fcc,
+	0x06a7f0fa,
+	0x010321f5,
+	0xf840bfcf,
+	0x28b7f100,
+	0x06b4b607,
+	0xb980bfd0,
+	0xc9f002ec,
+	0x1ec9f01f,
+	0xcf00bcd0,
+	0xccc800bc,
+	0xfa1bf41f,
+	0x87f100f8,
+	0x84b60430,
+	0x1ff9f006,
+	0xf8008fd0,
+	0x3087f100,
+	0x0684b604,
+	0xf80080d0,
+	0x3c87f100,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d000,
+	0x081887f1,
+	0xd00684b6,
+	0x87f1008a,
+	0x84b60400,
+	0x0088cf06,
+	0xf4888aff,
+	0x87f1f31b,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00099,
+	0xf100f800,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00099f0,
+	0x87f10089,
+	0x84b60818,
+	0x008ad006,
+	0x040087f1,
+	0xcf0684b6,
+	0x8aff0088,
+	0xf30bf488,
+	0x085c87f1,
+	0xbd0684b6,
+	0x0099f094,
+	0xf80089d0,
+	0x9894bd00,
+	0x85b600e8,
+	0x0180b61a,
+	0xbb0284b6,
+	0xe0b60098,
+	0x04efb804,
+	0xb9eb1bf4,
+	0x00f8029f,
+	0x083c87f1,
+	0xbd0684b6,
+	0x0199f094,
+	0xf10089d0,
+	0xb6071087,
+	0x94bd0684,
+	0xf405bbfd,
+	0x8bd0090b,
+	0x0099f000,
+	0xf405eefd,
+	0x8ed00c0b,
+	0xc08fd080,
+	0xb70199f0,
+	0xc8010080,
+	0xb4b600ab,
+	0x0cb9f010,
+	0xb601aec8,
+	0xbefd11e4,
+	0x008bd005,
+	0xf0008ecf,
+	0x0bf41fe4,
+	0x00ce98fa,
+	0xd005e9fd,
+	0xc0b6c08e,
+	0x04cdb804,
+	0xc8e81bf4,
+	0x1bf402ab,
+	0x008bcf18,
+	0xb01fb4f0,
+	0x1bf410b4,
+	0x02a7f0f7,
+	0xf4c921f4,
+	0xabc81b0e,
+	0x10b4b600,
+	0xf00cb9f0,
+	0x8bd012b9,
+	0x008bcf00,
+	0xf412bbc8,
+	0x87f1fa1b,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00199,
+	0xf900f800,
+	0x02a7f0a0,
+	0xfcc921f4,
+	0xf100f8a0,
+	0xf04afc87,
+	0x97f00283,
+	0x0089d00c,
+	0x020721f5,
+	0x87f100f8,
+	0x83f04afc,
+	0x0d97f002,
+	0xf50089d0,
+	0xf8020721,
+	0xfca7f100,
+	0x02a3f04f,
+	0x0500aba2,
+	0xd00fc7f0,
+	0xc7f000ac,
+	0x00bcd00b,
+	0x020721f5,
+	0xf000aed0,
+	0xbcd00ac7,
+	0x0721f500,
+	0xf100f802,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00399f0,
+	0x21f50089,
+	0xe7f00213,
+	0x3921f503,
+	0xfca7f102,
+	0x02a3f046,
+	0x0400aba0,
+	0xf040a0d0,
+	0xbcd001c7,
+	0x0721f500,
+	0x010c9202,
+	0xf000acd0,
+	0xbcd002c7,
+	0x0721f500,
+	0x2621f502,
+	0x8087f102,
+	0x0684b608,
+	0xb70089cf,
+	0x95220080,
+	0x8ed008fe,
+	0x408ed000,
+	0xb6808acf,
+	0xa0b606a5,
+	0x00eabb01,
+	0xb60480b6,
+	0x1bf40192,
+	0x08e4b6e8,
+	0xf1f2efbc,
+	0xb6085c87,
+	0x94bd0684,
+	0xd00399f0,
+	0x00f80089,
+	0xe7f1e0f9,
+	0xe3f09814,
+	0x8d21f440,
+	0x041ce0b7,
+	0xf401f7f0,
+	0xe0fc8d21,
+	0x04bd00f8,
+	0xf10004fe,
+	0xf0120017,
+	0x12d00227,
+	0x3e17f100,
+	0x0010fe04,
+	0x040017f1,
+	0xf0c010d0,
+	0x12d00427,
+	0x1031f400,
+	0x060817f1,
+	0xcf0614b6,
+	0x37f00012,
+	0x1f24f001,
+	0xb60432bb,
+	0x02800132,
+	0x04038003,
+	0x040010b7,
+	0x800012cf,
+	0x27f10002,
+	0x24b60800,
+	0x0022cf06,
+	0xb65817f0,
+	0x13980c10,
+	0x0432b800,
+	0xb00b0bf4,
+	0x1bf40034,
+	0xf100f8f1,
+	0xb6080027,
+	0x22cf0624,
+	0xf134bd40,
+	0xb6070047,
+	0x25950644,
+	0x0045d008,
+	0xbd4045d0,
+	0x58f4bde4,
+	0x1f58021e,
+	0x020e4003,
+	0xf5040f40,
+	0xbb013d21,
+	0x3fbb002f,
+	0x041e5800,
+	0x40051f58,
+	0x0f400a0e,
+	0x3d21f50c,
+	0x030e9801,
+	0xbb00effd,
+	0x3ebb002e,
+	0x0040b700,
+	0x0235b613,
+	0xb60043d0,
+	0x35b60825,
+	0x0120b606,
+	0xb60130b6,
+	0x34b60824,
+	0x022fb908,
+	0x026321f5,
+	0xf1003fbb,
+	0xb6080017,
+	0x13d00614,
+	0x0010b740,
+	0xf024bd08,
+	0x12d01f29,
+	0x0031f400,
+	0xf00028f4,
+	0x21f41cd7,
+	0xf401f439,
+	0xf404e4b0,
+	0x81fe1e18,
+	0x0627f001,
+	0x12fd20bd,
+	0x01e4b604,
+	0xfe051efd,
+	0x21f50018,
+	0x0ef404c3,
+	0x10ef94d3,
+	0xf501f5f0,
+	0xf402ec21,
+	0x80f9c60e,
+	0xf90188fe,
+	0xf990f980,
+	0xf9b0f9a0,
+	0xf9e0f9d0,
+	0x800acff0,
+	0xf404abc4,
+	0xb7f11d0b,
+	0xd7f01900,
+	0x40becf1c,
+	0xf400bfcf,
+	0xb0b70421,
+	0xe7f00400,
+	0x00bed001,
+	0xfc400ad0,
+	0xfce0fcf0,
+	0xfcb0fcd0,
+	0xfc90fca0,
+	0x0088fe80,
+	0x32f480fc,
+	0xf001f800,
+	0x0e9801f7,
+	0x04febb00,
+	0x9418e7f1,
+	0xf440e3f0,
+	0x00f88d21,
+	0x0614e7f1,
+	0xf006e4b6,
+	0xefd020f7,
+	0x08f7f000,
+	0xf401f2b6,
+	0xf7f1fd1b,
+	0xefd00a20,
+	0xf100f800,
+	0xb60a0417,
+	0x1fd00614,
+	0x0711f400,
+	0x04a421f5,
+	0x4afc17f1,
+	0xf00213f0,
+	0x12d00c27,
+	0x0721f500,
+	0xfc27f102,
+	0x0223f047,
+	0xf00020d0,
+	0x20b6012c,
+	0x0012d003,
+	0xf001acf0,
+	0xb7f002a5,
+	0x50b3f000,
+	0xb6000c98,
+	0xbcbb0fc4,
+	0x010c9800,
+	0xf0020d98,
+	0x21f500e7,
+	0xacf0015c,
+	0x04a5f001,
+	0x4000b7f1,
+	0x9850b3f0,
+	0xc4b6000c,
+	0x00bcbb0f,
+	0x98050c98,
+	0x0f98060d,
+	0x00e7f104,
+	0x5c21f508,
+	0x0721f501,
+	0x0601f402,
+	0xf11412f4,
+	0xf04afc17,
+	0x27f00213,
+	0x0012d00d,
+	0x020721f5,
+	0x048f21f5,
+	0x000000f8,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc
new file mode 100644
index 0000000..a1a5991
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc
@@ -0,0 +1,808 @@
+/* fuc microcode for nvc0 PGRAPH/HUB
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/* To build:
+ *    m4 nvc0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grhub.fuc.h
+ */
+
+.section nvc0_grhub_data
+include(`nvc0_graph.fuc')
+gpc_count:		.b32 0
+rop_count:		.b32 0
+cmd_queue:		queue_init
+hub_mmio_list_head:	.b32 0
+hub_mmio_list_tail:	.b32 0
+
+ctx_current:		.b32 0
+
+chipsets:
+.b8  0xc0 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8  0xc1 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc1_hub_mmio_tail
+.b8  0xc3 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8  0xc4 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8  0xc8 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8  0xce 0 0 0
+.b16 nvc0_hub_mmio_head
+.b16 nvc0_hub_mmio_tail
+.b8  0 0 0 0
+
+nvc0_hub_mmio_head:
+mmctx_data(0x17e91c, 2)
+mmctx_data(0x400204, 2)
+mmctx_data(0x404004, 11)
+mmctx_data(0x404044, 1)
+mmctx_data(0x404094, 14)
+mmctx_data(0x4040d0, 7)
+mmctx_data(0x4040f8, 1)
+mmctx_data(0x404130, 3)
+mmctx_data(0x404150, 3)
+mmctx_data(0x404164, 2)
+mmctx_data(0x404174, 3)
+mmctx_data(0x404200, 8)
+mmctx_data(0x404404, 14)
+mmctx_data(0x404460, 4)
+mmctx_data(0x404480, 1)
+mmctx_data(0x404498, 1)
+mmctx_data(0x404604, 4)
+mmctx_data(0x404618, 32)
+mmctx_data(0x404698, 21)
+mmctx_data(0x4046f0, 2)
+mmctx_data(0x404700, 22)
+mmctx_data(0x405800, 1)
+mmctx_data(0x405830, 3)
+mmctx_data(0x405854, 1)
+mmctx_data(0x405870, 4)
+mmctx_data(0x405a00, 2)
+mmctx_data(0x405a18, 1)
+mmctx_data(0x406020, 1)
+mmctx_data(0x406028, 4)
+mmctx_data(0x4064a8, 2)
+mmctx_data(0x4064b4, 2)
+mmctx_data(0x407804, 1)
+mmctx_data(0x40780c, 6)
+mmctx_data(0x4078bc, 1)
+mmctx_data(0x408000, 7)
+mmctx_data(0x408064, 1)
+mmctx_data(0x408800, 3)
+mmctx_data(0x408900, 4)
+mmctx_data(0x408980, 1)
+nvc0_hub_mmio_tail:
+mmctx_data(0x4064c0, 2)
+nvc1_hub_mmio_tail:
+
+.align 256
+chan_data:
+chan_mmio_count:	.b32 0
+chan_mmio_address:	.b32 0
+
+.align 256
+xfer_data: 		.b32 0
+
+.section nvc0_grhub_code
+bra init
+define(`include_code')
+include(`nvc0_graph.fuc')
+
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0_graph.fuc)
+//
+error:
+	push $r14
+	mov $r14 0x814
+	shl b32 $r14 6
+	iowr I[$r14 + 0x000] $r15	// CC_SCRATCH[5] = error code
+	mov $r14 0xc1c
+	shl b32 $r14 6
+	mov $r15 1
+	iowr I[$r14 + 0x000] $r15	// INTR_UP_SET
+	pop $r14
+	ret
+
+// HUB fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Input:
+//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
+//
+// Output:
+//   CC_SCRATCH[0]:
+//	     31:31: set to signal completion
+//   CC_SCRATCH[1]:
+//	      31:0: total PGRAPH context size
+//
+init:
+	clear b32 $r0
+	mov $sp $r0
+	mov $xdbase $r0
+
+	// enable fifo access
+	mov $r1 0x1200
+	mov $r2 2
+	iowr I[$r1 + 0x000] $r2	// FIFO_ENABLE
+
+	// setup i0 handler, and route all interrupts to it
+	mov $r1 ih
+	mov $iv0 $r1
+	mov $r1 0x400
+	iowr I[$r1 + 0x300] $r0	// INTR_DISPATCH
+
+	// route HUB_CHANNEL_SWITCH to fuc interrupt 8
+	mov $r3 0x404
+	shl b32 $r3 6
+	mov $r2 0x2003		// { HUB_CHANNEL_SWITCH, ZERO } -> intr 8
+	iowr I[$r3 + 0x000] $r2
+
+	// not sure what these are, route them because NVIDIA does, and
+	// the IRQ handler will signal the host if we ever get one.. we
+	// may find out if/why we need to handle these if so..
+	//
+	mov $r2 0x2004
+	iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9
+	mov $r2 0x200b
+	iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10
+	mov $r2 0x200c
+	iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15
+
+	// enable all INTR_UP interrupts
+	mov $r2 0xc24
+	shl b32 $r2 6
+	not b32 $r3 $r0
+	iowr I[$r2] $r3
+
+	// enable fifo, ctxsw, 9, 10, 15 interrupts
+	mov $r2 -0x78fc		// 0x8704
+	sethi $r2 0
+	iowr I[$r1 + 0x000] $r2	// INTR_EN_SET
+
+	// fifo level triggered, rest edge
+	sub b32 $r1 0x100
+	mov $r2 4
+	iowr I[$r1] $r2
+
+	// enable interrupts
+	bset $flags ie0
+
+	// fetch enabled GPC/ROP counts
+	mov $r14 -0x69fc	// 0x409604
+	sethi $r14 0x400000
+	call nv_rd32
+	extr $r1 $r15 16:20
+	st b32 D[$r0 + rop_count] $r1
+	and $r15 0x1f
+	st b32 D[$r0 + gpc_count] $r15
+
+	// set BAR_REQMASK to GPC mask
+	mov $r1 1
+	shl b32 $r1 $r15
+	sub b32 $r1 1
+	mov $r2 0x40c
+	shl b32 $r2 6
+	iowr I[$r2 + 0x000] $r1
+	iowr I[$r2 + 0x100] $r1
+
+	// find context data for this chipset
+	mov $r2 0x800
+	shl b32 $r2 6
+	iord $r2 I[$r2 + 0x000]		// CC_SCRATCH[0]
+	mov $r15 chipsets - 8
+	init_find_chipset:
+		add b32 $r15 8
+		ld b32 $r3 D[$r15 + 0x00]
+		cmpu b32 $r3 $r2
+		bra e init_context
+		cmpu b32 $r3 0
+		bra ne init_find_chipset
+		// unknown chipset
+		ret
+
+	// context size calculation, reserve first 256 bytes for use by fuc
+	init_context:
+	mov $r1 256
+
+	// calculate size of mmio context data
+	ld b16 $r14 D[$r15 + 4]
+	ld b16 $r15 D[$r15 + 6]
+	sethi $r14 0
+	st b32 D[$r0 + hub_mmio_list_head] $r14
+	st b32 D[$r0 + hub_mmio_list_tail] $r15
+	call mmctx_size
+
+	// set mmctx base addresses now so we don't have to do it later,
+	// they don't (currently) ever change
+	mov $r3 0x700
+	shl b32 $r3 6
+	shr b32 $r4 $r1 8
+	iowr I[$r3 + 0x000] $r4		// MMCTX_SAVE_SWBASE
+	iowr I[$r3 + 0x100] $r4		// MMCTX_LOAD_SWBASE
+	add b32 $r3 0x1300
+	add b32 $r1 $r15
+	shr b32 $r15 2
+	iowr I[$r3 + 0x000] $r15	// MMCTX_LOAD_COUNT, wtf for?!?
+
+	// strands, base offset needs to be aligned to 256 bytes
+	shr b32 $r1 8
+	add b32 $r1 1
+	shl b32 $r1 8
+	mov b32 $r15 $r1
+	call strand_ctx_init
+	add b32 $r1 $r15
+
+	// initialise each GPC in sequence by passing in the offset of its
+	// context data in GPCn_CC_SCRATCH[1], and starting its FUC (which
+	// has previously been uploaded by the host) running.
+	//
+	// the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31
+	// when it has completed, and return the size of its context data
+	// in GPCn_CC_SCRATCH[1]
+	//
+	ld b32 $r3 D[$r0 + gpc_count]
+	mov $r4 0x2000
+	sethi $r4 0x500000
+	init_gpc:
+		// setup, and start GPC ucode running
+		add b32 $r14 $r4 0x804
+		mov b32 $r15 $r1
+		call nv_wr32			// CC_SCRATCH[1] = ctx offset
+		add b32 $r14 $r4 0x800
+		mov b32 $r15 $r2
+		call nv_wr32			// CC_SCRATCH[0] = chipset
+		add b32 $r14 $r4 0x10c
+		clear b32 $r15
+		call nv_wr32
+		add b32 $r14 $r4 0x104
+		call nv_wr32			// ENTRY
+		add b32 $r14 $r4 0x100
+		mov $r15 2			// CTRL_START_TRIGGER
+		call nv_wr32			// CTRL
+
+		// wait for it to complete, and adjust context size
+		add b32 $r14 $r4 0x800
+		init_gpc_wait:
+			call nv_rd32
+			xbit $r15 $r15 31
+			bra e init_gpc_wait
+		add b32 $r14 $r4 0x804
+		call nv_rd32
+		add b32 $r1 $r15
+
+		// next!
+		add b32 $r4 0x8000
+		sub b32 $r3 1
+		bra ne init_gpc
+
+	// save context size, and tell host we're ready
+	mov $r2 0x800
+	shl b32 $r2 6
+	iowr I[$r2 + 0x100] $r1		// CC_SCRATCH[1]  = context size
+	add b32 $r2 0x800
+	clear b32 $r1
+	bset $r1 31
+	iowr I[$r2 + 0x000] $r1		// CC_SCRATCH[0] |= 0x80000000
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+	// sleep until we have something to do
+	bset $flags $p0
+	sleep $p0
+	mov $r13 cmd_queue
+	call queue_get
+	bra $p1 main
+
+	// context switch, requested by GPU?
+	cmpu b32 $r14 0x4001
+	bra ne main_not_ctx_switch
+		trace_set(T_AUTO)
+		mov $r1 0xb00
+		shl b32 $r1 6
+		iord $r2 I[$r1 + 0x100]		// CHAN_NEXT
+		iord $r1 I[$r1 + 0x000]		// CHAN_CUR
+
+		xbit $r3 $r1 31
+		bra e chsw_no_prev
+			xbit $r3 $r2 31
+			bra e chsw_prev_no_next
+				push $r2
+				mov b32 $r2 $r1
+				trace_set(T_SAVE)
+				bclr $flags $p1
+				bset $flags $p2
+				call ctx_xfer
+				trace_clr(T_SAVE);
+				pop $r2
+				trace_set(T_LOAD);
+				bset $flags $p1
+				call ctx_xfer
+				trace_clr(T_LOAD);
+				bra chsw_done
+			chsw_prev_no_next:
+				push $r2
+				mov b32 $r2 $r1
+				bclr $flags $p1
+				bclr $flags $p2
+				call ctx_xfer
+				pop $r2
+				mov $r1 0xb00
+				shl b32 $r1 6
+				iowr I[$r1] $r2
+				bra chsw_done
+		chsw_no_prev:
+			xbit $r3 $r2 31
+			bra e chsw_done
+				bset $flags $p1
+				bclr $flags $p2
+				call ctx_xfer
+
+		// ack the context switch request
+		chsw_done:
+		mov $r1 0xb0c
+		shl b32 $r1 6
+		mov $r2 1
+		iowr I[$r1 + 0x000] $r2		// 0x409b0c
+		trace_clr(T_AUTO)
+		bra main
+
+	// request to set current channel? (*not* a context switch)
+	main_not_ctx_switch:
+	cmpu b32 $r14 0x0001
+	bra ne main_not_ctx_chan
+		mov b32 $r2 $r15
+		call ctx_chan
+		bra main_done
+
+	// request to store current channel context?
+	main_not_ctx_chan:
+	cmpu b32 $r14 0x0002
+	bra ne main_not_ctx_save
+		trace_set(T_SAVE)
+		bclr $flags $p1
+		bclr $flags $p2
+		call ctx_xfer
+		trace_clr(T_SAVE)
+		bra main_done
+
+	main_not_ctx_save:
+		shl b32 $r15 $r14 16
+		or $r15 E_BAD_COMMAND
+		call error
+		bra main
+
+	main_done:
+	mov $r1 0x820
+	shl b32 $r1 6
+	clear b32 $r2
+	bset $r2 31
+	iowr I[$r1 + 0x000] $r2		// CC_SCRATCH[0] |= 0x80000000
+	bra main
+
+// interrupt handler
+ih:
+	push $r8
+	mov $r8 $flags
+	push $r8
+	push $r9
+	push $r10
+	push $r11
+	push $r13
+	push $r14
+	push $r15
+
+	// incoming fifo command?
+	iord $r10 I[$r0 + 0x200]	// INTR
+	and $r11 $r10 0x00000004
+	bra e ih_no_fifo
+		// queue incoming fifo command for later processing
+		mov $r11 0x1900
+		mov $r13 cmd_queue
+		iord $r14 I[$r11 + 0x100]	// FIFO_CMD
+		iord $r15 I[$r11 + 0x000]	// FIFO_DATA
+		call queue_put
+		add b32 $r11 0x400
+		mov $r14 1
+		iowr I[$r11 + 0x000] $r14	// FIFO_ACK
+
+	// context switch request?
+	ih_no_fifo:
+	and $r11 $r10 0x00000100
+	bra e ih_no_ctxsw
+		// enqueue a context switch for later processing
+		mov $r13 cmd_queue
+		mov $r14 0x4001
+		call queue_put
+
+	// anything we didn't handle, bring it to the host's attention
+	ih_no_ctxsw:
+	mov $r11 0x104
+	not b32 $r11
+	and $r11 $r10 $r11
+	bra e ih_no_other
+		mov $r10 0xc1c
+		shl b32 $r10 6
+		iowr I[$r10] $r11	// INTR_UP_SET
+
+	// ack, and wake up main()
+	ih_no_other:
+	iowr I[$r0 + 0x100] $r10	// INTR_ACK
+
+	pop $r15
+	pop $r14
+	pop $r13
+	pop $r11
+	pop $r10
+	pop $r9
+	pop $r8
+	mov $flags $r8
+	pop $r8
+	bclr $flags $p0
+	iret
+
+// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done
+ctx_4160s:
+	mov $r14 0x4160
+	sethi $r14 0x400000
+	mov $r15 1
+	call nv_wr32
+	ctx_4160s_wait:
+		call nv_rd32
+		xbit $r15 $r15 4
+		bra e ctx_4160s_wait
+	ret
+
+// Without clearing again at end of xfer, some things cause PGRAPH
+// to hang with STATUS=0x00000007 until it's cleared.. fbcon can
+// still function with it set however...
+ctx_4160c:
+	mov $r14 0x4160
+	sethi $r14 0x400000
+	clear b32 $r15
+	call nv_wr32
+	ret
+
+// Again, not real sure
+//
+// In: $r15 value to set 0x404170 to
+//
+ctx_4170s:
+	mov $r14 0x4170
+	sethi $r14 0x400000
+	or $r15 0x10
+	call nv_wr32
+	ret
+
+// Waits for a ctx_4170s() call to complete
+//
+ctx_4170w:
+	mov $r14 0x4170
+	sethi $r14 0x400000
+	call nv_rd32
+	and $r15 0x10
+	bra ne ctx_4170w
+	ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off?  Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+	mov $r14 0x614
+	shl b32 $r14 6
+	mov $r15 0x270
+	iowr I[$r14] $r15	// HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL
+	mov $r15 8
+	ctx_redswitch_delay:
+		sub b32 $r15 1
+		bra ne ctx_redswitch_delay
+	mov $r15 0x770
+	iowr I[$r14] $r15	// HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
+	ret
+
+// Not a clue what this is for, except that unless the value is 0x10, the
+// strand context is saved (and presumably restored) incorrectly..
+//
+// In: $r15 value to set to (0x00/0x10 are used)
+//
+ctx_86c:
+	mov $r14 0x86c
+	shl b32 $r14 6
+	iowr I[$r14] $r15	// HUB(0x86c) = val
+	mov $r14 -0x75ec
+	sethi $r14 0x400000
+	call nv_wr32		// ROP(0xa14) = val
+	mov $r14 -0x5794
+	sethi $r14 0x410000
+	call nv_wr32		// GPC(0x86c) = val
+	ret
+
+// ctx_load - load's a channel's ctxctl data, and selects its vm
+//
+// In: $r2 channel address
+//
+ctx_load:
+	trace_set(T_CHAN)
+
+	// switch to channel, somewhat magic in parts..
+	mov $r10 12		// DONE_UNK12
+	call wait_donez
+	mov $r1 0xa24
+	shl b32 $r1 6
+	iowr I[$r1 + 0x000] $r0	// 0x409a24
+	mov $r3 0xb00
+	shl b32 $r3 6
+	iowr I[$r3 + 0x100] $r2	// CHAN_NEXT
+	mov $r1 0xa0c
+	shl b32 $r1 6
+	mov $r4 7
+	iowr I[$r1 + 0x000] $r2 // MEM_CHAN
+	iowr I[$r1 + 0x100] $r4	// MEM_CMD
+	ctx_chan_wait_0:
+		iord $r4 I[$r1 + 0x100]
+		and $r4 0x1f
+		bra ne ctx_chan_wait_0
+	iowr I[$r3 + 0x000] $r2	// CHAN_CUR
+
+	// load channel header, fetch PGRAPH context pointer
+	mov $xtargets $r0
+	bclr $r2 31
+	shl b32 $r2 4
+	add b32 $r2 2
+
+	trace_set(T_LCHAN)
+	mov $r1 0xa04
+	shl b32 $r1 6
+	iowr I[$r1 + 0x000] $r2		// MEM_BASE
+	mov $r1 0xa20
+	shl b32 $r1 6
+	mov $r2 0x0002
+	sethi $r2 0x80000000
+	iowr I[$r1 + 0x000] $r2		// MEM_TARGET = vram
+	mov $r1 0x10			// chan + 0x0210
+	mov $r2 xfer_data
+	sethi $r2 0x00020000		// 16 bytes
+	xdld $r1 $r2
+	xdwait
+	trace_clr(T_LCHAN)
+
+	// update current context
+	ld b32 $r1 D[$r0 + xfer_data + 4]
+	shl b32 $r1 24
+	ld b32 $r2 D[$r0 + xfer_data + 0]
+	shr b32 $r2 8
+	or $r1 $r2
+	st b32 D[$r0 + ctx_current] $r1
+
+	// set transfer base to start of context, and fetch context header
+	trace_set(T_LCTXH)
+	mov $r2 0xa04
+	shl b32 $r2 6
+	iowr I[$r2 + 0x000] $r1		// MEM_BASE
+	mov $r2 1
+	mov $r1 0xa20
+	shl b32 $r1 6
+	iowr I[$r1 + 0x000] $r2		// MEM_TARGET = vm
+	mov $r1 chan_data
+	sethi $r1 0x00060000		// 256 bytes
+	xdld $r0 $r1
+	xdwait
+	trace_clr(T_LCTXH)
+
+	trace_clr(T_CHAN)
+	ret
+
+// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as
+//            the active channel for ctxctl, but not actually transfer
+//            any context data.  intended for use only during initial
+//            context construction.
+//
+// In: $r2 channel address
+//
+ctx_chan:
+	call ctx_4160s
+	call ctx_load
+	mov $r10 12			// DONE_UNK12
+	call wait_donez
+	mov $r1 0xa10
+	shl b32 $r1 6
+	mov $r2 5
+	iowr I[$r1 + 0x000] $r2		// MEM_CMD = 5 (???)
+	ctx_chan_wait:
+		iord $r2 I[$r1 + 0x000]
+		or $r2 $r2
+		bra ne ctx_chan_wait
+	call ctx_4160c
+	ret
+
+// Execute per-context state overrides list
+//
+// Only executed on the first load of a channel.  Might want to look into
+// removing this and having the host directly modify the channel's context
+// to change this state...  The nouveau DRM already builds this list as
+// it's definitely needed for NVIDIA's, so we may as well use it for now
+//
+// Input: $r1 mmio list length
+//
+ctx_mmio_exec:
+	// set transfer base to be the mmio list
+	ld b32 $r3 D[$r0 + chan_mmio_address]
+	mov $r2 0xa04
+	shl b32 $r2 6
+	iowr I[$r2 + 0x000] $r3		// MEM_BASE
+
+	clear b32 $r3
+	ctx_mmio_loop:
+		// fetch next 256 bytes of mmio list if necessary
+		and $r4 $r3 0xff
+		bra ne ctx_mmio_pull
+			mov $r5 xfer_data
+			sethi $r5 0x00060000	// 256 bytes
+			xdld $r3 $r5
+			xdwait
+
+		// execute a single list entry
+		ctx_mmio_pull:
+		ld b32 $r14 D[$r4 + xfer_data + 0x00]
+		ld b32 $r15 D[$r4 + xfer_data + 0x04]
+		call nv_wr32
+
+		// next!
+		add b32 $r3 8
+		sub b32 $r1 1
+		bra ne ctx_mmio_loop
+
+	// set transfer base back to the current context
+	ctx_mmio_done:
+	ld b32 $r3 D[$r0 + ctx_current]
+	iowr I[$r2 + 0x000] $r3		// MEM_BASE
+
+	// disable the mmio list now, we don't need/want to execute it again
+	st b32 D[$r0 + chan_mmio_count] $r0
+	mov $r1 chan_data
+	sethi $r1 0x00060000		// 256 bytes
+	xdst $r0 $r1
+	xdwait
+	ret
+
+// Transfer HUB context data between GPU and storage area
+//
+// In: $r2 channel address
+//     $p1 clear on save, set on load
+//     $p2 set if opposite direction done/will be done, so:
+//		on save it means: "a load will follow this save"
+//		on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+	bra not $p1 ctx_xfer_pre
+	bra $p2 ctx_xfer_pre_load
+	ctx_xfer_pre:
+		mov $r15 0x10
+		call ctx_86c
+		call ctx_4160s
+		bra not $p1 ctx_xfer_exec
+
+	ctx_xfer_pre_load:
+		mov $r15 2
+		call ctx_4170s
+		call ctx_4170w
+		call ctx_redswitch
+		clear b32 $r15
+		call ctx_4170s
+		call ctx_load
+
+	// fetch context pointer, and initiate xfer on all GPCs
+	ctx_xfer_exec:
+	ld b32 $r1 D[$r0 + ctx_current]
+	mov $r2 0x414
+	shl b32 $r2 6
+	iowr I[$r2 + 0x000] $r0	// BAR_STATUS = reset
+	mov $r14 -0x5b00
+	sethi $r14 0x410000
+	mov b32 $r15 $r1
+	call nv_wr32		// GPC_BCAST_WRCMD_DATA = ctx pointer
+	add b32 $r14 4
+	xbit $r15 $flags $p1
+	xbit $r2 $flags $p2
+	shl b32 $r2 1
+	or $r15 $r2
+	call nv_wr32		// GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
+
+	// strands
+	mov $r1 0x4afc
+	sethi $r1 0x20000
+	mov $r2 0xc
+	iowr I[$r1] $r2		// STRAND_CMD(0x3f) = 0x0c
+	call strand_wait
+	mov $r2 0x47fc
+	sethi $r2 0x20000
+	iowr I[$r2] $r0		// STRAND_FIRST_GENE(0x3f) = 0x00
+	xbit $r2 $flags $p1
+	add b32 $r2 3
+	iowr I[$r1] $r2		// STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+	// mmio context
+	xbit $r10 $flags $p1	// direction
+	or $r10 6		// first, last
+	mov $r11 0		// base = 0
+	ld b32 $r12 D[$r0 + hub_mmio_list_head]
+	ld b32 $r13 D[$r0 + hub_mmio_list_tail]
+	mov $r14 0		// not multi
+	call mmctx_xfer
+
+	// wait for GPCs to all complete
+	mov $r10 8		// DONE_BAR
+	call wait_doneo
+
+	// wait for strand xfer to complete
+	call strand_wait
+
+	// post-op
+	bra $p1 ctx_xfer_post
+		mov $r10 12		// DONE_UNK12
+		call wait_donez
+		mov $r1 0xa10
+		shl b32 $r1 6
+		mov $r2 5
+		iowr I[$r1] $r2		// MEM_CMD
+		ctx_xfer_post_save_wait:
+			iord $r2 I[$r1]
+			or $r2 $r2
+			bra ne ctx_xfer_post_save_wait
+
+	bra $p2 ctx_xfer_done
+	ctx_xfer_post:
+		mov $r15 2
+		call ctx_4170s
+		clear b32 $r15
+		call ctx_86c
+		call strand_post
+		call ctx_4170w
+		clear b32 $r15
+		call ctx_4170s
+
+		bra not $p1 ctx_xfer_no_post_mmio
+		ld b32 $r1 D[$r0 + chan_mmio_count]
+		or $r1 $r1
+		bra e ctx_xfer_no_post_mmio
+			call ctx_mmio_exec
+
+		ctx_xfer_no_post_mmio:
+		call ctx_4160c
+
+	ctx_xfer_done:
+	ret
+
+.align 256
diff --git a/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h
new file mode 100644
index 0000000..b3b541b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_grhub.fuc.h
@@ -0,0 +1,838 @@
+uint32_t nvc0_grhub_data[] = {
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x000000c0,
+	0x012c0090,
+	0x000000c1,
+	0x01300090,
+	0x000000c3,
+	0x012c0090,
+	0x000000c4,
+	0x012c0090,
+	0x000000c8,
+	0x012c0090,
+	0x000000ce,
+	0x012c0090,
+	0x00000000,
+	0x0417e91c,
+	0x04400204,
+	0x28404004,
+	0x00404044,
+	0x34404094,
+	0x184040d0,
+	0x004040f8,
+	0x08404130,
+	0x08404150,
+	0x04404164,
+	0x08404174,
+	0x1c404200,
+	0x34404404,
+	0x0c404460,
+	0x00404480,
+	0x00404498,
+	0x0c404604,
+	0x7c404618,
+	0x50404698,
+	0x044046f0,
+	0x54404700,
+	0x00405800,
+	0x08405830,
+	0x00405854,
+	0x0c405870,
+	0x04405a00,
+	0x00405a18,
+	0x00406020,
+	0x0c406028,
+	0x044064a8,
+	0x044064b4,
+	0x00407804,
+	0x1440780c,
+	0x004078bc,
+	0x18408000,
+	0x00408064,
+	0x08408800,
+	0x0c408900,
+	0x00408980,
+	0x044064c0,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+};
+
+uint32_t nvc0_grhub_code[] = {
+	0x03090ef5,
+	0x9800d898,
+	0x86f001d9,
+	0x0489b808,
+	0xf00c1bf4,
+	0x21f502f7,
+	0x00f802ec,
+	0xb60798c4,
+	0x8dbb0384,
+	0x0880b600,
+	0x80008e80,
+	0x90b6018f,
+	0x0f94f001,
+	0xf801d980,
+	0x0131f400,
+	0x9800d898,
+	0x89b801d9,
+	0x210bf404,
+	0xb60789c4,
+	0x9dbb0394,
+	0x0890b600,
+	0x98009e98,
+	0x80b6019f,
+	0x0f84f001,
+	0xf400d880,
+	0x00f80132,
+	0x0728b7f1,
+	0xb906b4b6,
+	0xc9f002ec,
+	0x00bcd01f,
+	0xc800bccf,
+	0x1bf41fcc,
+	0x06a7f0fa,
+	0x010321f5,
+	0xf840bfcf,
+	0x28b7f100,
+	0x06b4b607,
+	0xb980bfd0,
+	0xc9f002ec,
+	0x1ec9f01f,
+	0xcf00bcd0,
+	0xccc800bc,
+	0xfa1bf41f,
+	0x87f100f8,
+	0x84b60430,
+	0x1ff9f006,
+	0xf8008fd0,
+	0x3087f100,
+	0x0684b604,
+	0xf80080d0,
+	0x3c87f100,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d000,
+	0x081887f1,
+	0xd00684b6,
+	0x87f1008a,
+	0x84b60400,
+	0x0088cf06,
+	0xf4888aff,
+	0x87f1f31b,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00099,
+	0xf100f800,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00099f0,
+	0x87f10089,
+	0x84b60818,
+	0x008ad006,
+	0x040087f1,
+	0xcf0684b6,
+	0x8aff0088,
+	0xf30bf488,
+	0x085c87f1,
+	0xbd0684b6,
+	0x0099f094,
+	0xf80089d0,
+	0x9894bd00,
+	0x85b600e8,
+	0x0180b61a,
+	0xbb0284b6,
+	0xe0b60098,
+	0x04efb804,
+	0xb9eb1bf4,
+	0x00f8029f,
+	0x083c87f1,
+	0xbd0684b6,
+	0x0199f094,
+	0xf10089d0,
+	0xb6071087,
+	0x94bd0684,
+	0xf405bbfd,
+	0x8bd0090b,
+	0x0099f000,
+	0xf405eefd,
+	0x8ed00c0b,
+	0xc08fd080,
+	0xb70199f0,
+	0xc8010080,
+	0xb4b600ab,
+	0x0cb9f010,
+	0xb601aec8,
+	0xbefd11e4,
+	0x008bd005,
+	0xf0008ecf,
+	0x0bf41fe4,
+	0x00ce98fa,
+	0xd005e9fd,
+	0xc0b6c08e,
+	0x04cdb804,
+	0xc8e81bf4,
+	0x1bf402ab,
+	0x008bcf18,
+	0xb01fb4f0,
+	0x1bf410b4,
+	0x02a7f0f7,
+	0xf4c921f4,
+	0xabc81b0e,
+	0x10b4b600,
+	0xf00cb9f0,
+	0x8bd012b9,
+	0x008bcf00,
+	0xf412bbc8,
+	0x87f1fa1b,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00199,
+	0xf900f800,
+	0x02a7f0a0,
+	0xfcc921f4,
+	0xf100f8a0,
+	0xf04afc87,
+	0x97f00283,
+	0x0089d00c,
+	0x020721f5,
+	0x87f100f8,
+	0x83f04afc,
+	0x0d97f002,
+	0xf50089d0,
+	0xf8020721,
+	0xfca7f100,
+	0x02a3f04f,
+	0x0500aba2,
+	0xd00fc7f0,
+	0xc7f000ac,
+	0x00bcd00b,
+	0x020721f5,
+	0xf000aed0,
+	0xbcd00ac7,
+	0x0721f500,
+	0xf100f802,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00399f0,
+	0x21f50089,
+	0xe7f00213,
+	0x3921f503,
+	0xfca7f102,
+	0x02a3f046,
+	0x0400aba0,
+	0xf040a0d0,
+	0xbcd001c7,
+	0x0721f500,
+	0x010c9202,
+	0xf000acd0,
+	0xbcd002c7,
+	0x0721f500,
+	0x2621f502,
+	0x8087f102,
+	0x0684b608,
+	0xb70089cf,
+	0x95220080,
+	0x8ed008fe,
+	0x408ed000,
+	0xb6808acf,
+	0xa0b606a5,
+	0x00eabb01,
+	0xb60480b6,
+	0x1bf40192,
+	0x08e4b6e8,
+	0xf1f2efbc,
+	0xb6085c87,
+	0x94bd0684,
+	0xd00399f0,
+	0x00f80089,
+	0xe7f1e0f9,
+	0xe4b60814,
+	0x00efd006,
+	0x0c1ce7f1,
+	0xf006e4b6,
+	0xefd001f7,
+	0xf8e0fc00,
+	0xfe04bd00,
+	0x07fe0004,
+	0x0017f100,
+	0x0227f012,
+	0xf10012d0,
+	0xfe05b917,
+	0x17f10010,
+	0x10d00400,
+	0x0437f1c0,
+	0x0634b604,
+	0x200327f1,
+	0xf10032d0,
+	0xd0200427,
+	0x27f10132,
+	0x32d0200b,
+	0x0c27f102,
+	0x0732d020,
+	0x0c2427f1,
+	0xb90624b6,
+	0x23d00003,
+	0x0427f100,
+	0x0023f087,
+	0xb70012d0,
+	0xf0010012,
+	0x12d00427,
+	0x1031f400,
+	0x9604e7f1,
+	0xf440e3f0,
+	0xf1c76821,
+	0x01018090,
+	0x801ff4f0,
+	0x17f0000f,
+	0x041fbb01,
+	0xf10112b6,
+	0xb6040c27,
+	0x21d00624,
+	0x4021d000,
+	0x080027f1,
+	0xcf0624b6,
+	0xf7f00022,
+	0x08f0b654,
+	0xb800f398,
+	0x0bf40432,
+	0x0034b00b,
+	0xf8f11bf4,
+	0x0017f100,
+	0x02fe5801,
+	0xf003ff58,
+	0x0e8000e3,
+	0x150f8014,
+	0x013d21f5,
+	0x070037f1,
+	0x950634b6,
+	0x34d00814,
+	0x4034d000,
+	0x130030b7,
+	0xb6001fbb,
+	0x3fd002f5,
+	0x0815b600,
+	0xb60110b6,
+	0x1fb90814,
+	0x6321f502,
+	0x001fbb02,
+	0xf1000398,
+	0xf0200047,
+	0x4ea05043,
+	0x1fb90804,
+	0x8d21f402,
+	0x08004ea0,
+	0xf4022fb9,
+	0x4ea08d21,
+	0xf4bd010c,
+	0xa08d21f4,
+	0xf401044e,
+	0x4ea08d21,
+	0xf7f00100,
+	0x8d21f402,
+	0x08004ea0,
+	0xc86821f4,
+	0x0bf41fff,
+	0x044ea0fa,
+	0x6821f408,
+	0xb7001fbb,
+	0xb6800040,
+	0x1bf40132,
+	0x0027f1b4,
+	0x0624b608,
+	0xb74021d0,
+	0xbd080020,
+	0x1f19f014,
+	0xf40021d0,
+	0x28f40031,
+	0x08d7f000,
+	0xf43921f4,
+	0xe4b1f401,
+	0x1bf54001,
+	0x87f100d1,
+	0x84b6083c,
+	0xf094bd06,
+	0x89d00499,
+	0x0017f100,
+	0x0614b60b,
+	0xcf4012cf,
+	0x13c80011,
+	0x7e0bf41f,
+	0xf41f23c8,
+	0x20f95a0b,
+	0xf10212b9,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00799f0,
+	0x32f40089,
+	0x0231f401,
+	0x082921f5,
+	0x085c87f1,
+	0xbd0684b6,
+	0x0799f094,
+	0xfc0089d0,
+	0x3c87f120,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d006,
+	0xf50131f4,
+	0xf1082921,
+	0xb6085c87,
+	0x94bd0684,
+	0xd00699f0,
+	0x0ef40089,
+	0xb920f931,
+	0x32f40212,
+	0x0232f401,
+	0x082921f5,
+	0x17f120fc,
+	0x14b60b00,
+	0x0012d006,
+	0xc8130ef4,
+	0x0bf41f23,
+	0x0131f40d,
+	0xf50232f4,
+	0xf1082921,
+	0xb60b0c17,
+	0x27f00614,
+	0x0012d001,
+	0x085c87f1,
+	0xbd0684b6,
+	0x0499f094,
+	0xf50089d0,
+	0xb0ff200e,
+	0x1bf401e4,
+	0x02f2b90d,
+	0x07b521f5,
+	0xb0420ef4,
+	0x1bf402e4,
+	0x3c87f12e,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d007,
+	0xf40132f4,
+	0x21f50232,
+	0x87f10829,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00799,
+	0x110ef400,
+	0xf010ef94,
+	0x21f501f5,
+	0x0ef502ec,
+	0x17f1fed1,
+	0x14b60820,
+	0xf024bd06,
+	0x12d01f29,
+	0xbe0ef500,
+	0xfe80f9fe,
+	0x80f90188,
+	0xa0f990f9,
+	0xd0f9b0f9,
+	0xf0f9e0f9,
+	0xc4800acf,
+	0x0bf404ab,
+	0x00b7f11d,
+	0x08d7f019,
+	0xcf40becf,
+	0x21f400bf,
+	0x00b0b704,
+	0x01e7f004,
+	0xe400bed0,
+	0xf40100ab,
+	0xd7f00d0b,
+	0x01e7f108,
+	0x0421f440,
+	0x0104b7f1,
+	0xabffb0bd,
+	0x0d0bf4b4,
+	0x0c1ca7f1,
+	0xd006a4b6,
+	0x0ad000ab,
+	0xfcf0fc40,
+	0xfcd0fce0,
+	0xfca0fcb0,
+	0xfe80fc90,
+	0x80fc0088,
+	0xf80032f4,
+	0x60e7f101,
+	0x40e3f041,
+	0xf401f7f0,
+	0x21f48d21,
+	0x04ffc868,
+	0xf8fa0bf4,
+	0x60e7f100,
+	0x40e3f041,
+	0x21f4f4bd,
+	0xf100f88d,
+	0xf04170e7,
+	0xf5f040e3,
+	0x8d21f410,
+	0xe7f100f8,
+	0xe3f04170,
+	0x6821f440,
+	0xf410f4f0,
+	0x00f8f31b,
+	0x0614e7f1,
+	0xf106e4b6,
+	0xd00270f7,
+	0xf7f000ef,
+	0x01f2b608,
+	0xf1fd1bf4,
+	0xd00770f7,
+	0x00f800ef,
+	0x086ce7f1,
+	0xd006e4b6,
+	0xe7f100ef,
+	0xe3f08a14,
+	0x8d21f440,
+	0xa86ce7f1,
+	0xf441e3f0,
+	0x00f88d21,
+	0x083c87f1,
+	0xbd0684b6,
+	0x0599f094,
+	0xf00089d0,
+	0x21f40ca7,
+	0x2417f1c9,
+	0x0614b60a,
+	0xf10010d0,
+	0xb60b0037,
+	0x32d00634,
+	0x0c17f140,
+	0x0614b60a,
+	0xd00747f0,
+	0x14d00012,
+	0x4014cf40,
+	0xf41f44f0,
+	0x32d0fa1b,
+	0x000bfe00,
+	0xb61f2af0,
+	0x20b60424,
+	0x3c87f102,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d008,
+	0x0a0417f1,
+	0xd00614b6,
+	0x17f10012,
+	0x14b60a20,
+	0x0227f006,
+	0x800023f1,
+	0xf00012d0,
+	0x27f11017,
+	0x23f00300,
+	0x0512fa02,
+	0x87f103f8,
+	0x84b6085c,
+	0xf094bd06,
+	0x89d00899,
+	0xc1019800,
+	0x981814b6,
+	0x25b6c002,
+	0x0512fd08,
+	0xf1160180,
+	0xb6083c87,
+	0x94bd0684,
+	0xd00999f0,
+	0x27f10089,
+	0x24b60a04,
+	0x0021d006,
+	0xf10127f0,
+	0xb60a2017,
+	0x12d00614,
+	0x0017f100,
+	0x0613f002,
+	0xf80501fa,
+	0x5c87f103,
+	0x0684b608,
+	0x99f094bd,
+	0x0089d009,
+	0x085c87f1,
+	0xbd0684b6,
+	0x0599f094,
+	0xf80089d0,
+	0x3121f500,
+	0xb821f506,
+	0x0ca7f006,
+	0xf1c921f4,
+	0xb60a1017,
+	0x27f00614,
+	0x0012d005,
+	0xfd0012cf,
+	0x1bf40522,
+	0x4921f5fa,
+	0x9800f806,
+	0x27f18103,
+	0x24b60a04,
+	0x0023d006,
+	0x34c434bd,
+	0x0f1bf4ff,
+	0x030057f1,
+	0xfa0653f0,
+	0x03f80535,
+	0x98c04e98,
+	0x21f4c14f,
+	0x0830b68d,
+	0xf40112b6,
+	0x0398df1b,
+	0x0023d016,
+	0xf1800080,
+	0xf0020017,
+	0x01fa0613,
+	0xf803f806,
+	0x0611f400,
+	0xf01102f4,
+	0x21f510f7,
+	0x21f50698,
+	0x11f40631,
+	0x02f7f01c,
+	0x065721f5,
+	0x066621f5,
+	0x067821f5,
+	0x21f5f4bd,
+	0x21f50657,
+	0x019806b8,
+	0x1427f116,
+	0x0624b604,
+	0xf10020d0,
+	0xf0a500e7,
+	0x1fb941e3,
+	0x8d21f402,
+	0xf004e0b6,
+	0x2cf001fc,
+	0x0124b602,
+	0xf405f2fd,
+	0x17f18d21,
+	0x13f04afc,
+	0x0c27f002,
+	0xf50012d0,
+	0xf1020721,
+	0xf047fc27,
+	0x20d00223,
+	0x012cf000,
+	0xd00320b6,
+	0xacf00012,
+	0x06a5f001,
+	0x9800b7f0,
+	0x0d98140c,
+	0x00e7f015,
+	0x015c21f5,
+	0xf508a7f0,
+	0xf5010321,
+	0xf4020721,
+	0xa7f02201,
+	0xc921f40c,
+	0x0a1017f1,
+	0xf00614b6,
+	0x12d00527,
+	0x0012cf00,
+	0xf40522fd,
+	0x02f4fa1b,
+	0x02f7f032,
+	0x065721f5,
+	0x21f5f4bd,
+	0x21f50698,
+	0x21f50226,
+	0xf4bd0666,
+	0x065721f5,
+	0x981011f4,
+	0x11fd8001,
+	0x070bf405,
+	0x07df21f5,
+	0x064921f5,
+	0x000000f8,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c
index 82357d2..b701c43 100644
--- a/drivers/gpu/drm/nouveau/nvc0_instmem.c
+++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c
@@ -32,7 +32,6 @@
 	struct nouveau_channel *bar1;
 	struct nouveau_gpuobj  *bar3_pgd;
 	struct nouveau_channel *bar3;
-	struct nouveau_gpuobj  *chan_pgd;
 };
 
 int
@@ -181,17 +180,11 @@
 		goto error;
 
 	/* channel vm */
-	ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL, &vm);
+	ret = nouveau_vm_new(dev, 0, (1ULL << 40), 0x0008000000ULL,
+			     &dev_priv->chan_vm);
 	if (ret)
 		goto error;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 4096, 0, &priv->chan_pgd);
-	if (ret)
-		goto error;
-
-	nouveau_vm_ref(vm, &dev_priv->chan_vm, priv->chan_pgd);
-	nouveau_vm_ref(NULL, &vm, NULL);
-
 	nvc0_instmem_resume(dev);
 	return 0;
 error:
@@ -211,8 +204,7 @@
 	nv_wr32(dev, 0x1704, 0x00000000);
 	nv_wr32(dev, 0x1714, 0x00000000);
 
-	nouveau_vm_ref(NULL, &dev_priv->chan_vm, priv->chan_pgd);
-	nouveau_gpuobj_ref(NULL, &priv->chan_pgd);
+	nouveau_vm_ref(NULL, &dev_priv->chan_vm, NULL);
 
 	nvc0_channel_del(&priv->bar1);
 	nouveau_vm_ref(NULL, &dev_priv->bar1_vm, priv->bar1_pgd);
diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c
index a179e6c..9e35294 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vm.c
@@ -105,7 +105,11 @@
 	struct drm_device *dev = vm->dev;
 	struct nouveau_vm_pgd *vpgd;
 	unsigned long flags;
-	u32 engine = (dev_priv->chan_vm == vm) ? 1 : 5;
+	u32 engine;
+
+	engine = 1;
+	if (vm == dev_priv->bar1_vm || vm == dev_priv->bar3_vm)
+		engine |= 4;
 
 	pinstmem->flush(vm->dev);
 
diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c
index 67c6ec6..e45a24d 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vram.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vram.c
@@ -61,9 +61,7 @@
 	      u32 type, struct nouveau_mem **pmem)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
-	struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
-	struct nouveau_mm *mm = man->priv;
+	struct nouveau_mm *mm = dev_priv->engine.vram.mm;
 	struct nouveau_mm_node *r;
 	struct nouveau_mem *mem;
 	int ret;
@@ -105,9 +103,15 @@
 nvc0_vram_init(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
+	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+	u32 length;
 
 	dev_priv->vram_size  = nv_rd32(dev, 0x10f20c) << 20;
 	dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
-	dev_priv->vram_rblock_size = 4096;
-	return 0;
+
+	length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+
+	return nouveau_mm_init(&vram->mm, rsvd_head, length, 1);
 }
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 9541995..c742944 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -764,7 +764,7 @@
 }
 
 static void atombios_crtc_program_pll(struct drm_crtc *crtc,
-				      int crtc_id,
+				      u32 crtc_id,
 				      int pll_id,
 				      u32 encoder_mode,
 				      u32 encoder_id,
@@ -851,8 +851,7 @@
 			args.v5.ucPpll = pll_id;
 			break;
 		case 6:
-			args.v6.ulCrtcPclkFreq.ucCRTC = crtc_id;
-			args.v6.ulCrtcPclkFreq.ulPixelClock = cpu_to_le32(clock / 10);
+			args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10);
 			args.v6.ucRefDiv = ref_div;
 			args.v6.usFbDiv = cpu_to_le16(fb_div);
 			args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 8c0f9e3..645b84b 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -627,6 +627,7 @@
 	u8 train_set[4];
 	u8 link_status[DP_LINK_STATUS_SIZE];
 	u8 tries;
+	bool use_dpencoder;
 };
 
 static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
@@ -646,7 +647,7 @@
 	int rtp = 0;
 
 	/* set training pattern on the source */
-	if (ASIC_IS_DCE4(dp_info->rdev)) {
+	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) {
 		switch (tp) {
 		case DP_TRAINING_PATTERN_1:
 			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
@@ -706,7 +707,7 @@
 	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);
 
 	/* start training on the source */
-	if (ASIC_IS_DCE4(dp_info->rdev))
+	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
 		atombios_dig_encoder_setup(dp_info->encoder,
 					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
 	else
@@ -731,7 +732,7 @@
 			      DP_TRAINING_PATTERN_DISABLE);
 
 	/* disable the training pattern on the source */
-	if (ASIC_IS_DCE4(dp_info->rdev))
+	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
 		atombios_dig_encoder_setup(dp_info->encoder,
 					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
 	else
@@ -869,7 +870,8 @@
 	struct radeon_connector *radeon_connector;
 	struct radeon_connector_atom_dig *dig_connector;
 	struct radeon_dp_link_train_info dp_info;
- 	u8 tmp;
+	int index;
+	u8 tmp, frev, crev;
 
 	if (!radeon_encoder->enc_priv)
 		return;
@@ -884,6 +886,18 @@
 	    (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP))
 		return;
 
+	/* DPEncoderService newer than 1.1 can't program properly the
+	 * training pattern. When facing such version use the
+	 * DIGXEncoderControl (X== 1 | 2)
+	 */
+	dp_info.use_dpencoder = true;
+	index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
+	if (atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) {
+		if (crev > 1) {
+			dp_info.use_dpencoder = false;
+		}
+	}
+
 	dp_info.enc_id = 0;
 	if (dig->dig_encoder)
 		dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 15bd047..14dce9f 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1382,9 +1382,6 @@
 
 	/* set the wb address wether it's enabled or not */
 	WREG32(CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
-	       RB_RPTR_SWAP(2) |
-#endif
 	       ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC));
 	WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
 	WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
@@ -2047,6 +2044,7 @@
 	rdev->config.evergreen.tile_config |=
 		((gb_addr_config & 0x30000000) >> 28) << 12;
 
+	rdev->config.evergreen.backend_map = gb_backend_map;
 	WREG32(GB_BACKEND_MAP, gb_backend_map);
 	WREG32(GB_ADDR_CONFIG, gb_addr_config);
 	WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
@@ -2761,6 +2759,9 @@
 		return IRQ_NONE;
 	}
 restart_ih:
+	/* Order reading of wptr vs. reading of IH ring data */
+	rmb();
+
 	/* display interrupts */
 	evergreen_irq_ack(rdev);
 
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 23d3641..189e865 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -856,7 +856,6 @@
 	case SQ_PGM_START_PS:
 	case SQ_PGM_START_HS:
 	case SQ_PGM_START_LS:
-	case GDS_ADDR_BASE:
 	case SQ_CONST_MEM_BASE:
 	case SQ_ALU_CONST_CACHE_GS_0:
 	case SQ_ALU_CONST_CACHE_GS_1:
@@ -946,6 +945,34 @@
 		}
 		ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
 		break;
+	case SX_MEMORY_EXPORT_BASE:
+		if (p->rdev->family >= CHIP_CAYMAN) {
+			dev_warn(p->dev, "bad SET_CONFIG_REG "
+				 "0x%04X\n", reg);
+			return -EINVAL;
+		}
+		r = evergreen_cs_packet_next_reloc(p, &reloc);
+		if (r) {
+			dev_warn(p->dev, "bad SET_CONFIG_REG "
+					"0x%04X\n", reg);
+			return -EINVAL;
+		}
+		ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+		break;
+	case CAYMAN_SX_SCATTER_EXPORT_BASE:
+		if (p->rdev->family < CHIP_CAYMAN) {
+			dev_warn(p->dev, "bad SET_CONTEXT_REG "
+				 "0x%04X\n", reg);
+			return -EINVAL;
+		}
+		r = evergreen_cs_packet_next_reloc(p, &reloc);
+		if (r) {
+			dev_warn(p->dev, "bad SET_CONTEXT_REG "
+					"0x%04X\n", reg);
+			return -EINVAL;
+		}
+		ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+		break;
 	default:
 		dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
 		return -EINVAL;
@@ -1153,6 +1180,34 @@
 			return r;
 		}
 		break;
+	case PACKET3_DISPATCH_DIRECT:
+		if (pkt->count != 3) {
+			DRM_ERROR("bad DISPATCH_DIRECT\n");
+			return -EINVAL;
+		}
+		r = evergreen_cs_track_check(p);
+		if (r) {
+			dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
+			return r;
+		}
+		break;
+	case PACKET3_DISPATCH_INDIRECT:
+		if (pkt->count != 1) {
+			DRM_ERROR("bad DISPATCH_INDIRECT\n");
+			return -EINVAL;
+		}
+		r = evergreen_cs_packet_next_reloc(p, &reloc);
+		if (r) {
+			DRM_ERROR("bad DISPATCH_INDIRECT\n");
+			return -EINVAL;
+		}
+		ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
+		r = evergreen_cs_track_check(p);
+		if (r) {
+			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
+			return r;
+		}
+		break;
 	case PACKET3_WAIT_REG_MEM:
 		if (pkt->count != 5) {
 			DRM_ERROR("bad WAIT_REG_MEM\n");
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index b7b2714..7363d9d 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -351,6 +351,7 @@
 #define		COLOR_BUFFER_SIZE(x)				((x) << 0)
 #define		POSITION_BUFFER_SIZE(x)				((x) << 8)
 #define		SMX_BUFFER_SIZE(x)				((x) << 16)
+#define	SX_MEMORY_EXPORT_BASE				0x9010
 #define	SX_MISC						0x28350
 
 #define CB_PERF_CTR0_SEL_0				0x9A20
@@ -1122,6 +1123,7 @@
 #define CAYMAN_PA_SC_AA_CONFIG				0x28BE0
 #define         CAYMAN_MSAA_NUM_SAMPLES_SHIFT           0
 #define         CAYMAN_MSAA_NUM_SAMPLES_MASK            0x7
+#define CAYMAN_SX_SCATTER_EXPORT_BASE			0x28358
 /* cayman packet3 addition */
 #define	CAYMAN_PACKET3_DEALLOC_STATE			0x14
 
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 559dbd4..44c4750 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -833,6 +833,7 @@
 	rdev->config.cayman.tile_config |=
 		((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12;
 
+	rdev->config.cayman.backend_map = gb_backend_map;
 	WREG32(GB_BACKEND_MAP, gb_backend_map);
 	WREG32(GB_ADDR_CONFIG, gb_addr_config);
 	WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index bc54b26..aa5571b 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -1662,6 +1662,7 @@
 									       R6XX_MAX_BACKENDS_MASK) >> 16)),
 							(cc_rb_backend_disable >> 16));
 	rdev->config.r600.tile_config = tiling_config;
+	rdev->config.r600.backend_map = backend_map;
 	tiling_config |= BACKEND_MAP(backend_map);
 	WREG32(GB_TILING_CONFIG, tiling_config);
 	WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff);
@@ -2212,9 +2213,6 @@
 
 	/* set the wb address whether it's enabled or not */
 	WREG32(CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
-	       RB_RPTR_SWAP(2) |
-#endif
 	       ((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC));
 	WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
 	WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
@@ -2994,10 +2992,6 @@
 	/* RPTR_REARM only works if msi's are enabled */
 	if (rdev->msi_enabled)
 		ih_cntl |= RPTR_REARM;
-
-#ifdef __BIG_ENDIAN
-	ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT);
-#endif
 	WREG32(IH_CNTL, ih_cntl);
 
 	/* force the active interrupt state to all disabled */
@@ -3308,6 +3302,10 @@
 	if (!rdev->ih.enabled || rdev->shutdown)
 		return IRQ_NONE;
 
+	/* No MSIs, need a dummy read to flush PCI DMAs */
+	if (!rdev->msi_enabled)
+		RREG32(IH_RB_WPTR);
+
 	wptr = r600_get_ih_wptr(rdev);
 	rptr = rdev->ih.rptr;
 	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
@@ -3320,6 +3318,9 @@
 	}
 
 restart_ih:
+	/* Order reading of wptr vs. reading of IH ring data */
+	rmb();
+
 	/* display interrupts */
 	r600_irq_ack(rdev);
 
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index c3ab959..45fd592 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -1802,8 +1802,8 @@
 	/* Set ring buffer size */
 #ifdef __BIG_ENDIAN
 	RADEON_WRITE(R600_CP_RB_CNTL,
-		     RADEON_BUF_SWAP_32BIT |
-		     RADEON_RB_NO_UPDATE |
+		     R600_BUF_SWAP_32BIT |
+		     R600_RB_NO_UPDATE |
 		     (dev_priv->ring.rptr_update_l2qw << 8) |
 		     dev_priv->ring.size_l2qw);
 #else
@@ -1820,15 +1820,15 @@
 
 #ifdef __BIG_ENDIAN
 	RADEON_WRITE(R600_CP_RB_CNTL,
-		     RADEON_BUF_SWAP_32BIT |
-		     RADEON_RB_NO_UPDATE |
-		     RADEON_RB_RPTR_WR_ENA |
+		     R600_BUF_SWAP_32BIT |
+		     R600_RB_NO_UPDATE |
+		     R600_RB_RPTR_WR_ENA |
 		     (dev_priv->ring.rptr_update_l2qw << 8) |
 		     dev_priv->ring.size_l2qw);
 #else
 	RADEON_WRITE(R600_CP_RB_CNTL,
-		     RADEON_RB_NO_UPDATE |
-		     RADEON_RB_RPTR_WR_ENA |
+		     R600_RB_NO_UPDATE |
+		     R600_RB_RPTR_WR_ENA |
 		     (dev_priv->ring.rptr_update_l2qw << 8) |
 		     dev_priv->ring.size_l2qw);
 #endif
@@ -1851,13 +1851,8 @@
 			- ((unsigned long) dev->sg->virtual)
 			+ dev_priv->gart_vm_start;
 	}
-	RADEON_WRITE(R600_CP_RB_RPTR_ADDR,
-#ifdef __BIG_ENDIAN
-		     (2 << 0) |
-#endif
-		     (rptr_addr & 0xfffffffc));
-	RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI,
-		     upper_32_bits(rptr_addr));
+	RADEON_WRITE(R600_CP_RB_RPTR_ADDR, (rptr_addr & 0xfffffffc));
+	RADEON_WRITE(R600_CP_RB_RPTR_ADDR_HI, upper_32_bits(rptr_addr));
 
 #ifdef __BIG_ENDIAN
 	RADEON_WRITE(R600_CP_RB_CNTL,
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 909bda8..db8ef19 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -1200,6 +1200,15 @@
 		}
 		ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
 		break;
+	case SX_MEMORY_EXPORT_BASE:
+		r = r600_cs_packet_next_reloc(p, &reloc);
+		if (r) {
+			dev_warn(p->dev, "bad SET_CONFIG_REG "
+					"0x%04X\n", reg);
+			return -EINVAL;
+		}
+		ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+		break;
 	default:
 		dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
 		return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index ef0e0e0..32807ba 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -60,7 +60,7 @@
  *                          are considered as fatal)
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/wait.h>
 #include <linux/list.h>
 #include <linux/kref.h>
@@ -1003,6 +1003,7 @@
 	unsigned		tiling_npipes;
 	unsigned		tiling_group_size;
 	unsigned		tile_config;
+	unsigned		backend_map;
 	struct r100_gpu_lockup	lockup;
 };
 
@@ -1028,6 +1029,7 @@
 	unsigned		tiling_npipes;
 	unsigned		tiling_group_size;
 	unsigned		tile_config;
+	unsigned		backend_map;
 	struct r100_gpu_lockup	lockup;
 };
 
@@ -1054,6 +1056,7 @@
 	unsigned tiling_npipes;
 	unsigned tiling_group_size;
 	unsigned tile_config;
+	unsigned backend_map;
 	struct r100_gpu_lockup	lockup;
 };
 
@@ -1174,7 +1177,7 @@
 	/* Register mmio */
 	resource_size_t			rmmio_base;
 	resource_size_t			rmmio_size;
-	void				*rmmio;
+	void __iomem			*rmmio;
 	radeon_rreg_t			mc_rreg;
 	radeon_wreg_t			mc_wreg;
 	radeon_rreg_t			pll_rreg;
@@ -1251,20 +1254,20 @@
 static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
 {
 	if (reg < rdev->rmmio_size)
-		return readl(((void __iomem *)rdev->rmmio) + reg);
+		return readl((rdev->rmmio) + reg);
 	else {
-		writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
-		return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+		writel(reg, (rdev->rmmio) + RADEON_MM_INDEX);
+		return readl((rdev->rmmio) + RADEON_MM_DATA);
 	}
 }
 
 static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
 {
 	if (reg < rdev->rmmio_size)
-		writel(v, ((void __iomem *)rdev->rmmio) + reg);
+		writel(v, (rdev->rmmio) + reg);
 	else {
-		writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
-		writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+		writel(reg, (rdev->rmmio) + RADEON_MM_INDEX);
+		writel(v, (rdev->rmmio) + RADEON_MM_DATA);
 	}
 }
 
@@ -1296,10 +1299,10 @@
 /*
  * Registers read & write functions.
  */
-#define RREG8(reg) readb(((void __iomem *)rdev->rmmio) + (reg))
-#define WREG8(reg, v) writeb(v, ((void __iomem *)rdev->rmmio) + (reg))
-#define RREG16(reg) readw(((void __iomem *)rdev->rmmio) + (reg))
-#define WREG16(reg, v) writew(v, ((void __iomem *)rdev->rmmio) + (reg))
+#define RREG8(reg) readb((rdev->rmmio) + (reg))
+#define WREG8(reg, v) writeb(v, (rdev->rmmio) + (reg))
+#define RREG16(reg) readw((rdev->rmmio) + (reg))
+#define WREG16(reg, v) writew(v, (rdev->rmmio) + (reg))
 #define RREG32(reg) r100_mm_rreg(rdev, (reg))
 #define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg)))
 #define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v))
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index b244962..df8218b 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -625,7 +625,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &r600_cs_parse,
 	.copy_blit = &r600_copy_blit,
-	.copy_dma = &r600_copy_blit,
+	.copy_dma = NULL,
 	.copy = &r600_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -672,7 +672,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &r600_cs_parse,
 	.copy_blit = &r600_copy_blit,
-	.copy_dma = &r600_copy_blit,
+	.copy_dma = NULL,
 	.copy = &r600_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -719,7 +719,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &r600_cs_parse,
 	.copy_blit = &r600_copy_blit,
-	.copy_dma = &r600_copy_blit,
+	.copy_dma = NULL,
 	.copy = &r600_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -766,7 +766,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &evergreen_cs_parse,
 	.copy_blit = &evergreen_copy_blit,
-	.copy_dma = &evergreen_copy_blit,
+	.copy_dma = NULL,
 	.copy = &evergreen_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -813,7 +813,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &evergreen_cs_parse,
 	.copy_blit = &evergreen_copy_blit,
-	.copy_dma = &evergreen_copy_blit,
+	.copy_dma = NULL,
 	.copy = &evergreen_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -860,7 +860,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &evergreen_cs_parse,
 	.copy_blit = &evergreen_copy_blit,
-	.copy_dma = &evergreen_copy_blit,
+	.copy_dma = NULL,
 	.copy = &evergreen_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
@@ -907,7 +907,7 @@
 	.fence_ring_emit = &r600_fence_ring_emit,
 	.cs_parse = &evergreen_cs_parse,
 	.copy_blit = &evergreen_copy_blit,
-	.copy_dma = &evergreen_copy_blit,
+	.copy_dma = NULL,
 	.copy = &evergreen_copy_blit,
 	.get_engine_clock = &radeon_atom_get_engine_clock,
 	.set_engine_clock = &radeon_atom_set_engine_clock,
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
index 2d48e7a..dcd0863e 100644
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
@@ -96,7 +96,7 @@
  * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
  * tree. Hopefully, ATI OF driver is kind enough to fill these
  */
-static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+static bool radeon_read_clocks_OF(struct drm_device *dev)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	struct device_node *dp = rdev->pdev->dev.of_node;
@@ -166,7 +166,7 @@
 	return true;
 }
 #else
-static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+static bool radeon_read_clocks_OF(struct drm_device *dev)
 {
 	return false;
 }
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index e459467..a74217c 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -779,7 +779,8 @@
 				}
 			}
 		}
-	} else if (rdev->family >= CHIP_R200) {
+	} else if ((rdev->family == CHIP_R200) ||
+		   (rdev->family >= CHIP_R300)) {
 		/* 0x68 */
 		i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
 		rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index 7586779..045ec59 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -2115,7 +2115,7 @@
 
 	if (drm_pci_device_is_agp(dev))
 		dev_priv->flags |= RADEON_IS_AGP;
-	else if (drm_pci_device_is_pcie(dev))
+	else if (pci_is_pcie(dev->pdev))
 		dev_priv->flags |= RADEON_IS_PCIE;
 	else
 		dev_priv->flags |= RADEON_IS_PCI;
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 292f73f..28f4655 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -282,7 +282,7 @@
 	spin_lock_irqsave(&rdev->ddev->event_lock, flags);
 	work = radeon_crtc->unpin_work;
 	if (work == NULL ||
-	    !radeon_fence_signaled(work->fence)) {
+	    (work->fence && !radeon_fence_signaled(work->fence))) {
 		spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
 		return;
 	}
@@ -348,7 +348,6 @@
 	struct radeon_framebuffer *new_radeon_fb;
 	struct drm_gem_object *obj;
 	struct radeon_bo *rbo;
-	struct radeon_fence *fence;
 	struct radeon_unpin_work *work;
 	unsigned long flags;
 	u32 tiling_flags, pitch_pixels;
@@ -359,16 +358,9 @@
 	if (work == NULL)
 		return -ENOMEM;
 
-	r = radeon_fence_create(rdev, &fence);
-	if (unlikely(r != 0)) {
-		kfree(work);
-		DRM_ERROR("flip queue: failed to create fence.\n");
-		return -ENOMEM;
-	}
 	work->event = event;
 	work->rdev = rdev;
 	work->crtc_id = radeon_crtc->crtc_id;
-	work->fence = radeon_fence_ref(fence);
 	old_radeon_fb = to_radeon_framebuffer(crtc->fb);
 	new_radeon_fb = to_radeon_framebuffer(fb);
 	/* schedule unpin of the old buffer */
@@ -377,6 +369,10 @@
 	drm_gem_object_reference(obj);
 	rbo = gem_to_radeon_bo(obj);
 	work->old_rbo = rbo;
+	obj = new_radeon_fb->obj;
+	rbo = gem_to_radeon_bo(obj);
+	if (rbo->tbo.sync_obj)
+		work->fence = radeon_fence_ref(rbo->tbo.sync_obj);
 	INIT_WORK(&work->work, radeon_unpin_work_func);
 
 	/* We borrow the event spin lock for protecting unpin_work */
@@ -391,9 +387,6 @@
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	/* pin the new buffer */
-	obj = new_radeon_fb->obj;
-	rbo = gem_to_radeon_bo(obj);
-
 	DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n",
 			 work->old_rbo, rbo);
 
@@ -461,37 +454,18 @@
 		goto pflip_cleanup1;
 	}
 
-	/* 32 ought to cover us */
-	r = radeon_ring_lock(rdev, 32);
-	if (r) {
-		DRM_ERROR("failed to lock the ring before flip\n");
-		goto pflip_cleanup2;
-	}
-
-	/* emit the fence */
-	radeon_fence_emit(rdev, fence);
 	/* set the proper interrupt */
 	radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
-	/* fire the ring */
-	radeon_ring_unlock_commit(rdev);
 
 	return 0;
 
-pflip_cleanup2:
-	drm_vblank_put(dev, radeon_crtc->crtc_id);
-
 pflip_cleanup1:
-	r = radeon_bo_reserve(rbo, false);
-	if (unlikely(r != 0)) {
+	if (unlikely(radeon_bo_reserve(rbo, false) != 0)) {
 		DRM_ERROR("failed to reserve new rbo in error path\n");
 		goto pflip_cleanup;
 	}
-	r = radeon_bo_unpin(rbo);
-	if (unlikely(r != 0)) {
-		radeon_bo_unreserve(rbo);
-		r = -EINVAL;
+	if (unlikely(radeon_bo_unpin(rbo) != 0)) {
 		DRM_ERROR("failed to unpin new rbo in error path\n");
-		goto pflip_cleanup;
 	}
 	radeon_bo_unreserve(rbo);
 
@@ -501,7 +475,7 @@
 unlock_free:
 	drm_gem_object_unreference_unlocked(old_radeon_fb->obj);
 	spin_unlock_irqrestore(&dev->event_lock, flags);
-	radeon_fence_unref(&fence);
+	radeon_fence_unref(&work->fence);
 	kfree(work);
 
 	return r;
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 73dfbe8..85f033f 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -50,10 +50,11 @@
  *   2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
  *   2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query
  *   2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query
- *   2.10.0 - fusion 2D tiling
+ *   2.10.0 - fusion 2D tiling, initial compute support for the CS checker
+ *   2.11.0 - backend map
  */
 #define KMS_DRIVER_MAJOR	2
-#define KMS_DRIVER_MINOR	10
+#define KMS_DRIVER_MINOR	11
 #define KMS_DRIVER_PATCHLEVEL	0
 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
 int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 021d2b6..7fd4e3e 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -29,7 +29,7 @@
  *    Dave Airlie
  */
 #include <linux/seq_file.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/wait.h>
 #include <linux/list.h>
 #include <linux/kref.h>
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index bd58af6..be2c122 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -60,7 +60,7 @@
 	/* update BUS flag */
 	if (drm_pci_device_is_agp(dev)) {
 		flags |= RADEON_IS_AGP;
-	} else if (drm_pci_device_is_pcie(dev)) {
+	} else if (pci_is_pcie(dev->pdev)) {
 		flags |= RADEON_IS_PCIE;
 	} else {
 		flags |= RADEON_IS_PCI;
@@ -237,6 +237,19 @@
 	case RADEON_INFO_FUSION_GART_WORKING:
 		value = 1;
 		break;
+	case RADEON_INFO_BACKEND_MAP:
+		if (rdev->family >= CHIP_CAYMAN)
+			value = rdev->config.cayman.backend_map;
+		else if (rdev->family >= CHIP_CEDAR)
+			value = rdev->config.evergreen.backend_map;
+		else if (rdev->family >= CHIP_RV770)
+			value = rdev->config.rv770.backend_map;
+		else if (rdev->family >= CHIP_R600)
+			value = rdev->config.r600.backend_map;
+		else {
+			return -EINVAL;
+		}
+		break;
 	default:
 		DRM_DEBUG_KMS("Invalid request %d\n", info->request);
 		return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index aaa19dc..6fabe89 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -594,6 +594,9 @@
 			if (rdev->pm.default_vddc)
 				radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
 							SET_VOLTAGE_TYPE_ASIC_VDDC);
+			if (rdev->pm.default_vddci)
+				radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+							SET_VOLTAGE_TYPE_ASIC_VDDCI);
 			if (rdev->pm.default_sclk)
 				radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
 			if (rdev->pm.default_mclk)
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index bc44a3d..b4ce864 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -3295,7 +3295,7 @@
 #	define RADEON_RB_BUFSZ_MASK		(0x3f << 0)
 #	define RADEON_RB_BLKSZ_SHIFT		8
 #	define RADEON_RB_BLKSZ_MASK		(0x3f << 8)
-#	define RADEON_BUF_SWAP_32BIT		(1 << 17)
+#	define RADEON_BUF_SWAP_32BIT		(2 << 16)
 #	define RADEON_MAX_FETCH_SHIFT		18
 #	define RADEON_MAX_FETCH_MASK		(0x3 << 18)
 #	define RADEON_RB_NO_UPDATE		(1 << 27)
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index 0aa8e85..2316977 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -208,6 +208,7 @@
 0x0002834C PA_SC_VPORT_ZMAX_15
 0x00028350 SX_MISC
 0x00028354 SX_SURFACE_SYNC
+0x0002835C SX_SCATTER_EXPORT_SIZE
 0x00028380 SQ_VTX_SEMANTIC_0
 0x00028384 SQ_VTX_SEMANTIC_1
 0x00028388 SQ_VTX_SEMANTIC_2
@@ -432,6 +433,7 @@
 0x00028700 SPI_STACK_MGMT
 0x00028704 SPI_WAVE_MGMT_1
 0x00028708 SPI_WAVE_MGMT_2
+0x00028720 GDS_ADDR_BASE
 0x00028724 GDS_ADDR_SIZE
 0x00028780 CB_BLEND0_CONTROL
 0x00028784 CB_BLEND1_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 0e28cae..161737a 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -44,6 +44,7 @@
 0x00008E28 SQ_STATIC_THREAD_MGMT_3
 0x00008E2C SQ_LDS_RESOURCE_MGMT
 0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS
+0x00009014 SX_MEMORY_EXPORT_SIZE
 0x00009100 SPI_CONFIG_CNTL
 0x0000913C SPI_CONFIG_CNTL_1
 0x00009508 TA_CNTL_AUX
@@ -442,7 +443,9 @@
 0x000286EC SPI_COMPUTE_NUM_THREAD_X
 0x000286F0 SPI_COMPUTE_NUM_THREAD_Y
 0x000286F4 SPI_COMPUTE_NUM_THREAD_Z
+0x00028720 GDS_ADDR_BASE
 0x00028724 GDS_ADDR_SIZE
+0x00028728 GDS_ORDERED_WAVE_PER_SE
 0x00028780 CB_BLEND0_CONTROL
 0x00028784 CB_BLEND1_CONTROL
 0x00028788 CB_BLEND2_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index ea49752..0380c5c 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -429,6 +429,7 @@
 0x00028438 SX_ALPHA_REF
 0x00028410 SX_ALPHA_TEST_CONTROL
 0x00028350 SX_MISC
+0x00009014 SX_MEMORY_EXPORT_SIZE
 0x00009604 TC_INVALIDATE
 0x00009400 TD_FILTER4
 0x00009404 TD_FILTER4_1
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 1f5850e..4b5d0e6 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -530,7 +530,7 @@
 	addr = addr & 0xFFFFFFFFFFFFF000ULL;
 	addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED;
 	addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE;
-	writeq(addr, ((void __iomem *)ptr) + (i * 8));
+	writeq(addr, ptr + (i * 8));
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 4de5189..4720d00 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -778,6 +778,7 @@
 								(cc_rb_backend_disable >> 16));
 
 	rdev->config.rv770.tile_config = gb_tiling_config;
+	rdev->config.rv770.backend_map = backend_map;
 	gb_tiling_config |= BACKEND_MAP(backend_map);
 
 	WREG32(GB_TILING_CONFIG, gb_tiling_config);
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 2e618b5..56619f6 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -37,7 +37,7 @@
 #include <linux/mm.h>
 #include <linux/file.h>
 #include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define TTM_ASSERT_LOCKED(param)
 #define TTM_DEBUG(fmt, arg...)
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
index de41e55..075daf4 100644
--- a/drivers/gpu/drm/ttm/ttm_lock.c
+++ b/drivers/gpu/drm/ttm/ttm_lock.c
@@ -30,7 +30,7 @@
 
 #include "ttm/ttm_lock.h"
 #include "ttm/ttm_module.h"
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/errno.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index ebddd44..93577f2 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -55,7 +55,7 @@
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct ttm_object_file {
 	struct ttm_object_device *tdev;
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index d948575..727e93d 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -40,7 +40,7 @@
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "ttm/ttm_bo_driver.h"
 #include "ttm/ttm_page_alloc.h"
@@ -355,7 +355,7 @@
 			if (nr_free)
 				goto restart;
 
-			/* Not allowed to fall tough or break because
+			/* Not allowed to fall through or break because
 			 * following context is inside spinlock while we are
 			 * outside here.
 			 */
@@ -556,7 +556,7 @@
 }
 
 /**
- * Fill the given pool if there isn't enough pages and requested number of
+ * Fill the given pool if there aren't enough pages and the requested number of
  * pages is small.
  */
 static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
@@ -576,8 +576,8 @@
 
 	pool->fill_lock = true;
 
-	/* If allocation request is small and there is not enough
-	 * pages in pool we fill the pool first */
+	/* If allocation request is small and there are not enough
+	 * pages in a pool we fill the pool up first. */
 	if (count < _manager->options.small
 		&& count > pool->npages) {
 		struct list_head new_pages;
@@ -614,9 +614,9 @@
 }
 
 /**
- * Cut count nubmer of pages from the pool and put them to return list
+ * Cut 'count' number of pages from the pool and put them on the return list.
  *
- * @return count of pages still to allocate to fill the request.
+ * @return count of pages still required to fulfill the request.
  */
 static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool,
 		struct list_head *pages, int ttm_flags,
@@ -637,7 +637,7 @@
 		goto out;
 	}
 	/* find the last pages to include for requested number of pages. Split
-	 * pool to begin and halves to reduce search space. */
+	 * pool to begin and halve it to reduce search space. */
 	if (count <= pool->npages/2) {
 		i = 0;
 		list_for_each(p, &pool->list) {
@@ -651,7 +651,7 @@
 				break;
 		}
 	}
-	/* Cut count number of pages from pool */
+	/* Cut 'count' number of pages from the pool */
 	list_cut_position(pages, &pool->list, p);
 	pool->npages -= count;
 	count = 0;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0598cd2..0b62c3c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -623,7 +623,7 @@
 	  LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
 	  Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
 	  MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
-	  and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips.
+	  Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips.
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm90.
@@ -694,14 +694,24 @@
 	  be called ltc4261.
 
 config SENSORS_LM95241
-	tristate "National Semiconductor LM95241 sensor chip"
+	tristate "National Semiconductor LM95241 and compatibles"
 	depends on I2C
 	help
-	  If you say yes here you get support for LM95241 sensor chip.
+	  If you say yes here you get support for LM95231 and LM95241 sensor
+	  chips.
 
 	  This driver can also be built as a module.  If so, the module
 	  will be called lm95241.
 
+config SENSORS_LM95245
+	tristate "National Semiconductor LM95245 sensor chip"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for LM95245 sensor chip.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called lm95245.
+
 config SENSORS_MAX1111
 	tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"
 	depends on SPI_MASTER
@@ -736,6 +746,16 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called max1619.
 
+config SENSORS_MAX1668
+	tristate "Maxim MAX1668 and compatibles"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for MAX1668, MAX1989 and
+	  MAX1805 chips.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called max1668.
+
 config SENSORS_MAX6639
 	tristate "Maxim MAX6639 sensor chip"
 	depends on I2C && EXPERIMENTAL
@@ -767,6 +787,20 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called max6650.
 
+config SENSORS_NTC_THERMISTOR
+	tristate "NTC thermistor support"
+	depends on EXPERIMENTAL
+	help
+	  This driver supports NTC thermistors sensor reading and its
+	  interpretation. The driver can also monitor the temperature and
+	  send notifications about the temperature.
+
+	  Currently, this driver supports
+	  NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ntc-thermistor.
+
 config SENSORS_PC87360
 	tristate "National Semiconductor PC87360 family"
 	select HWMON_VID
@@ -807,92 +841,7 @@
 	  These devices are hard to detect and rarely found on mainstream
 	  hardware.  If unsure, say N.
 
-config PMBUS
-	tristate "PMBus support"
-	depends on I2C && EXPERIMENTAL
-	default n
-	help
-	  Say yes here if you want to enable PMBus support.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called pmbus_core.
-
-if PMBUS
-
-config SENSORS_PMBUS
-	tristate "Generic PMBus devices"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for generic
-	  PMBus devices, including but not limited to BMR450, BMR451, BMR453,
-	  BMR454, and LTC2978.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called pmbus.
-
-config SENSORS_ADM1275
-	tristate "Analog Devices ADM1275"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for Analog
-	  Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called adm1275.
-
-config SENSORS_MAX16064
-	tristate "Maxim MAX16064"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for Maxim
-	  MAX16064.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called max16064.
-
-config SENSORS_MAX34440
-	tristate "Maxim MAX34440/MAX34441"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for Maxim
-	  MAX34440 and MAX34441.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called max34440.
-
-config SENSORS_MAX8688
-	tristate "Maxim MAX8688"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for Maxim
-	  MAX8688.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called max8688.
-
-config SENSORS_UCD9000
-	tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for TI
-	  UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
-	  Controllers.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called ucd9000.
-
-config SENSORS_UCD9200
-	tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
-	default n
-	help
-	  If you say yes here you get hardware monitoring support for TI
-	  UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
-	  Digital PWM System Controllers.
-
-	  This driver can also be built as a module. If so, the module will
-	  be called ucd9200.
-
-endif # PMBUS
+source drivers/hwmon/pmbus/Kconfig
 
 config SENSORS_SHT15
 	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d7995a1..3c9ccef 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -80,6 +80,7 @@
 obj-$(CONFIG_SENSORS_LM92)	+= lm92.o
 obj-$(CONFIG_SENSORS_LM93)	+= lm93.o
 obj-$(CONFIG_SENSORS_LM95241)	+= lm95241.o
+obj-$(CONFIG_SENSORS_LM95245)	+= lm95245.o
 obj-$(CONFIG_SENSORS_LTC4151)	+= ltc4151.o
 obj-$(CONFIG_SENSORS_LTC4215)	+= ltc4215.o
 obj-$(CONFIG_SENSORS_LTC4245)	+= ltc4245.o
@@ -87,10 +88,12 @@
 obj-$(CONFIG_SENSORS_MAX1111)	+= max1111.o
 obj-$(CONFIG_SENSORS_MAX16065)	+= max16065.o
 obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o
+obj-$(CONFIG_SENSORS_MAX1668)	+= max1668.o
 obj-$(CONFIG_SENSORS_MAX6639)	+= max6639.o
 obj-$(CONFIG_SENSORS_MAX6642)	+= max6642.o
 obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
@@ -121,15 +124,7 @@
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
 
-# PMBus drivers
-obj-$(CONFIG_PMBUS)		+= pmbus_core.o
-obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o
-obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
-obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o
-obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o
-obj-$(CONFIG_SENSORS_MAX8688)	+= max8688.o
-obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o
-obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o
+obj-$(CONFIG_PMBUS)		+= pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
 
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 0070d54..59d83e8 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -44,7 +44,9 @@
 #define BASE_SYSFS_ATTR_NO	2	/* Sysfs Base attr no for coretemp */
 #define NUM_REAL_CORES		16	/* Number of Real cores per cpu */
 #define CORETEMP_NAME_LENGTH	17	/* String Length of attrs */
-#define MAX_ATTRS		5	/* Maximum no of per-core attrs */
+#define MAX_CORE_ATTRS		4	/* Maximum no of basic attrs */
+#define MAX_THRESH_ATTRS	3	/* Maximum no of Threshold attrs */
+#define TOTAL_ATTRS		(MAX_CORE_ATTRS + MAX_THRESH_ATTRS)
 #define MAX_CORE_DATA		(NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
 
 #ifdef CONFIG_SMP
@@ -67,6 +69,9 @@
  *		This value is passed as "id" field to rdmsr/wrmsr functions.
  * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
  *		from where the temperature values should be read.
+ * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT,
+ *		from where the thresholds are read.
+ * @attr_size:  Total number of pre-core attrs displayed in the sysfs.
  * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
  *		Otherwise, temp_data holds coretemp data.
  * @valid: If this is 1, the current temperature is valid.
@@ -74,15 +79,18 @@
 struct temp_data {
 	int temp;
 	int ttarget;
+	int tmin;
 	int tjmax;
 	unsigned long last_updated;
 	unsigned int cpu;
 	u32 cpu_core_id;
 	u32 status_reg;
+	u32 intrpt_reg;
+	int attr_size;
 	bool is_pkg_data;
 	bool valid;
-	struct sensor_device_attribute sd_attrs[MAX_ATTRS];
-	char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
+	struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
+	char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
 	struct mutex update_lock;
 };
 
@@ -135,6 +143,19 @@
 	return sprintf(buf, "%d\n", (eax >> 5) & 1);
 }
 
+static ssize_t show_max_alarm(struct device *dev,
+				struct device_attribute *devattr, char *buf)
+{
+	u32 eax, edx;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct temp_data *tdata = pdata->core_data[attr->index];
+
+	rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+
+	return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1));
+}
+
 static ssize_t show_tjmax(struct device *dev,
 			struct device_attribute *devattr, char *buf)
 {
@@ -153,6 +174,83 @@
 	return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
 }
 
+static ssize_t store_ttarget(struct device *dev,
+				struct device_attribute *devattr,
+				const char *buf, size_t count)
+{
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct temp_data *tdata = pdata->core_data[attr->index];
+	u32 eax, edx;
+	unsigned long val;
+	int diff;
+
+	if (strict_strtoul(buf, 10, &val))
+		return -EINVAL;
+
+	/*
+	 * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms
+	 * of milli degree celsius. Hence don't accept val > (127 * 1000)
+	 */
+	if (val > tdata->tjmax || val > 127000)
+		return -EINVAL;
+
+	diff = (tdata->tjmax - val) / 1000;
+
+	mutex_lock(&tdata->update_lock);
+	rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
+	eax = (eax & ~THERM_MASK_THRESHOLD1) |
+				(diff << THERM_SHIFT_THRESHOLD1);
+	wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
+	tdata->ttarget = val;
+	mutex_unlock(&tdata->update_lock);
+
+	return count;
+}
+
+static ssize_t show_tmin(struct device *dev,
+			struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct platform_data *pdata = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin);
+}
+
+static ssize_t store_tmin(struct device *dev,
+				struct device_attribute *devattr,
+				const char *buf, size_t count)
+{
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct temp_data *tdata = pdata->core_data[attr->index];
+	u32 eax, edx;
+	unsigned long val;
+	int diff;
+
+	if (strict_strtoul(buf, 10, &val))
+		return -EINVAL;
+
+	/*
+	 * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms
+	 * of milli degree celsius. Hence don't accept val > (127 * 1000)
+	 */
+	if (val > tdata->tjmax || val > 127000)
+		return -EINVAL;
+
+	diff = (tdata->tjmax - val) / 1000;
+
+	mutex_lock(&tdata->update_lock);
+	rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
+	eax = (eax & ~THERM_MASK_THRESHOLD0) |
+				(diff << THERM_SHIFT_THRESHOLD0);
+	wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
+	tdata->tmin = val;
+	mutex_unlock(&tdata->update_lock);
+
+	return count;
+}
+
 static ssize_t show_temp(struct device *dev,
 			struct device_attribute *devattr, char *buf)
 {
@@ -344,23 +442,31 @@
 				int attr_no)
 {
 	int err, i;
-	static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
+	static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev,
 			struct device_attribute *devattr, char *buf) = {
-			show_label, show_crit_alarm, show_ttarget,
-			show_temp, show_tjmax };
-	static const char *names[MAX_ATTRS] = {
+			show_label, show_crit_alarm, show_temp, show_tjmax,
+			show_max_alarm, show_ttarget, show_tmin };
+	static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev,
+			struct device_attribute *devattr, const char *buf,
+			size_t count) = { NULL, NULL, NULL, NULL, NULL,
+					store_ttarget, store_tmin };
+	static const char *names[TOTAL_ATTRS] = {
 					"temp%d_label", "temp%d_crit_alarm",
-					"temp%d_max", "temp%d_input",
-					"temp%d_crit" };
+					"temp%d_input", "temp%d_crit",
+					"temp%d_max_alarm", "temp%d_max",
+					"temp%d_max_hyst" };
 
-	for (i = 0; i < MAX_ATTRS; i++) {
+	for (i = 0; i < tdata->attr_size; i++) {
 		snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
 			attr_no);
 		sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
 		tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
 		tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
+		if (rw_ptr[i]) {
+			tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR;
+			tdata->sd_attrs[i].dev_attr.store = rw_ptr[i];
+		}
 		tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
-		tdata->sd_attrs[i].dev_attr.store = NULL;
 		tdata->sd_attrs[i].index = attr_no;
 		err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
 		if (err)
@@ -374,38 +480,6 @@
 	return err;
 }
 
-static void update_ttarget(__u8 cpu_model, struct temp_data *tdata,
-				struct device *dev)
-{
-	int err;
-	u32 eax, edx;
-
-	/*
-	 * Initialize ttarget value. Eventually this will be
-	 * initialized with the value from MSR_IA32_THERM_INTERRUPT
-	 * register. If IA32_TEMPERATURE_TARGET is supported, this
-	 * value will be over written below.
-	 * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT
-	 */
-	tdata->ttarget = tdata->tjmax - 20000;
-
-	/*
-	 * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists
-	 * on older CPUs but not in this register,
-	 * Atoms don't have it either.
-	 */
-	if (cpu_model > 0xe && cpu_model != 0x1c) {
-		err = rdmsr_safe_on_cpu(tdata->cpu,
-				MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
-		if (err) {
-			dev_warn(dev,
-			"Unable to read IA32_TEMPERATURE_TARGET MSR\n");
-		} else {
-			tdata->ttarget = tdata->tjmax -
-					((eax >> 8) & 0xff) * 1000;
-		}
-	}
-}
 
 static int __devinit chk_ucode_version(struct platform_device *pdev)
 {
@@ -464,9 +538,12 @@
 
 	tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
 							MSR_IA32_THERM_STATUS;
+	tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT :
+						MSR_IA32_THERM_INTERRUPT;
 	tdata->is_pkg_data = pkg_flag;
 	tdata->cpu = cpu;
 	tdata->cpu_core_id = TO_CORE_ID(cpu);
+	tdata->attr_size = MAX_CORE_ATTRS;
 	mutex_init(&tdata->update_lock);
 	return tdata;
 }
@@ -516,7 +593,17 @@
 	else
 		tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
 
-	update_ttarget(c->x86_model, tdata, &pdev->dev);
+	/*
+	 * Test if we can access the intrpt register. If so, increase the
+	 * 'size' enough to have ttarget/tmin/max_alarm interfaces.
+	 * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT
+	 */
+	err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx);
+	if (!err) {
+		tdata->attr_size += MAX_THRESH_ATTRS;
+		tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
+	}
+
 	pdata->core_data[attr_no] = tdata;
 
 	/* Create sysfs interfaces */
@@ -553,7 +640,7 @@
 	struct temp_data *tdata = pdata->core_data[indx];
 
 	/* Remove the sysfs attributes */
-	for (i = 0; i < MAX_ATTRS; i++)
+	for (i = 0; i < tdata->attr_size; i++)
 		device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
 
 	kfree(pdata->core_data[indx]);
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 2f94f95..90ddb87 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -54,6 +54,9 @@
  * and extended mode. They are mostly compatible with LM90 except for a data
  * format difference for the temperature value registers.
  *
+ * This driver also supports the SA56004 from Philips. This device is
+ * pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
+ *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
  * concern all supported chipsets, unless mentioned otherwise.
@@ -96,13 +99,15 @@
  * MAX6659 can have address 0x4c, 0x4d or 0x4e.
  * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
  * 0x4c, 0x4d or 0x4e.
+ * SA56004 can have address 0x48 through 0x4F.
  */
 
 static const unsigned short normal_i2c[] = {
-	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+	0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
-	max6646, w83l771, max6696 };
+	max6646, w83l771, max6696, sa56004 };
 
 /*
  * The LM90 registers
@@ -152,6 +157,10 @@
 #define MAX6659_REG_R_LOCAL_EMERG	0x17
 #define MAX6659_REG_W_LOCAL_EMERG	0x17
 
+/*  SA56004 registers */
+
+#define SA56004_REG_R_LOCAL_TEMPL 0x22
+
 #define LM90_DEF_CONVRATE_RVAL	6	/* Def conversion rate register value */
 #define LM90_MAX_CONVRATE_MS	16000	/* Maximum conversion rate in ms */
 
@@ -161,7 +170,6 @@
 #define LM90_FLAG_ADT7461_EXT	(1 << 0) /* ADT7461 extended mode	*/
 /* Device features */
 #define LM90_HAVE_OFFSET	(1 << 1) /* temperature offset register	*/
-#define LM90_HAVE_LOCAL_EXT	(1 << 2) /* extended local temperature	*/
 #define LM90_HAVE_REM_LIMIT_EXT	(1 << 3) /* extended remote limit	*/
 #define LM90_HAVE_EMERGENCY	(1 << 4) /* 3rd upper (emergency) limit	*/
 #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm		*/
@@ -192,6 +200,7 @@
 	{ "max6696", max6696 },
 	{ "nct1008", adt7461 },
 	{ "w83l771", w83l771 },
+	{ "sa56004", sa56004 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -204,6 +213,7 @@
 	u16 alert_alarms;	/* Which alarm bits trigger ALERT# */
 				/* Upper 8 bits for max6695/96 */
 	u8 max_convrate;	/* Maximum conversion rate register value */
+	u8 reg_local_ext;	/* Extended local temp register (optional) */
 };
 
 static const struct lm90_params lm90_params[] = {
@@ -235,19 +245,20 @@
 		.max_convrate = 9,
 	},
 	[max6646] = {
-		.flags = LM90_HAVE_LOCAL_EXT,
 		.alert_alarms = 0x7c,
 		.max_convrate = 6,
+		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
 	},
 	[max6657] = {
-		.flags = LM90_HAVE_LOCAL_EXT,
 		.alert_alarms = 0x7c,
 		.max_convrate = 8,
+		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
 	},
 	[max6659] = {
-		.flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY,
+		.flags = LM90_HAVE_EMERGENCY,
 		.alert_alarms = 0x7c,
 		.max_convrate = 8,
+		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
 	},
 	[max6680] = {
 		.flags = LM90_HAVE_OFFSET,
@@ -255,16 +266,23 @@
 		.max_convrate = 7,
 	},
 	[max6696] = {
-		.flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY
+		.flags = LM90_HAVE_EMERGENCY
 		  | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,
 		.alert_alarms = 0x187c,
 		.max_convrate = 6,
+		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
 	},
 	[w83l771] = {
 		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
 		.alert_alarms = 0x7c,
 		.max_convrate = 8,
 	},
+	[sa56004] = {
+		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
+		.alert_alarms = 0x7b,
+		.max_convrate = 9,
+		.reg_local_ext = SA56004_REG_R_LOCAL_TEMPL,
+	},
 };
 
 /*
@@ -286,6 +304,7 @@
 	u16 alert_alarms;	/* Which alarm bits trigger ALERT# */
 				/* Upper 8 bits for max6695/96 */
 	u8 max_convrate;	/* Maximum conversion rate */
+	u8 reg_local_ext;	/* local extension register offset */
 
 	/* registers values */
 	s8 temp8[8];	/* 0: local low limit
@@ -452,9 +471,9 @@
 		lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
 		lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
 
-		if (data->flags & LM90_HAVE_LOCAL_EXT) {
+		if (data->reg_local_ext) {
 			lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
-				    MAX6657_REG_R_LOCAL_TEMPL,
+				    data->reg_local_ext,
 				    &data->temp11[4]);
 		} else {
 			if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
@@ -1092,7 +1111,7 @@
 	struct i2c_adapter *adapter = new_client->adapter;
 	int address = new_client->addr;
 	const char *name = NULL;
-	int man_id, chip_id, reg_config1, reg_convrate;
+	int man_id, chip_id, reg_config1, reg_config2, reg_convrate;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		return -ENODEV;
@@ -1108,15 +1127,16 @@
 						LM90_REG_R_CONVRATE)) < 0)
 		return -ENODEV;
 
-	if ((address == 0x4C || address == 0x4D)
-	 && man_id == 0x01) { /* National Semiconductor */
-		int reg_config2;
-
+	if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) {
 		reg_config2 = i2c_smbus_read_byte_data(new_client,
 						LM90_REG_R_CONFIG2);
 		if (reg_config2 < 0)
 			return -ENODEV;
+	} else
+		reg_config2 = 0;	/* Make compiler happy */
 
+	if ((address == 0x4C || address == 0x4D)
+	 && man_id == 0x01) { /* National Semiconductor */
 		if ((reg_config1 & 0x2A) == 0x00
 		 && (reg_config2 & 0xF8) == 0x00
 		 && reg_convrate <= 0x09) {
@@ -1245,13 +1265,6 @@
 	} else
 	if (address == 0x4C
 	 && man_id == 0x5C) { /* Winbond/Nuvoton */
-		int reg_config2;
-
-		reg_config2 = i2c_smbus_read_byte_data(new_client,
-						LM90_REG_R_CONFIG2);
-		if (reg_config2 < 0)
-			return -ENODEV;
-
 		if ((reg_config1 & 0x2A) == 0x00
 		 && (reg_config2 & 0xF8) == 0x00) {
 			if (chip_id == 0x01 /* W83L771W/G */
@@ -1263,6 +1276,15 @@
 				name = "w83l771";
 			}
 		}
+	} else
+	if (address >= 0x48 && address <= 0x4F
+	 && man_id == 0xA1) { /*  NXP Semiconductor/Philips */
+		if (chip_id == 0x00
+		 && (reg_config1 & 0x2A) == 0x00
+		 && (reg_config2 & 0xFE) == 0x00
+		 && reg_convrate <= 0x09) {
+			name = "sa56004";
+		}
 	}
 
 	if (!name) { /* identification failed */
@@ -1368,6 +1390,7 @@
 
 	/* Set chip capabilities */
 	data->flags = lm90_params[data->kind].flags;
+	data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
 
 	/* Set maximum conversion rate */
 	data->max_convrate = lm90_params[data->kind].max_convrate;
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index d3b464b..513901d 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -74,8 +74,9 @@
 #define TT_OFF 0
 #define TT_ON 1
 #define TT_MASK 7
-#define MANUFACTURER_ID 0x01
-#define DEFAULT_REVISION 0xA4
+#define NATSEMI_MAN_ID	0x01
+#define LM95231_CHIP_ID	0xA1
+#define LM95241_CHIP_ID	0xA4
 
 static const u8 lm95241_reg_address[] = {
 	LM95241_REG_R_LOCAL_TEMPH,
@@ -338,20 +339,25 @@
 			  struct i2c_board_info *info)
 {
 	struct i2c_adapter *adapter = new_client->adapter;
-	int address = new_client->addr;
 	const char *name;
+	int mfg_id, chip_id;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 		return -ENODEV;
 
-	if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
-	     == MANUFACTURER_ID)
-	    && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
-		== DEFAULT_REVISION)) {
-		name = DEVNAME;
-	} else {
-		dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n",
-			address);
+	mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID);
+	if (mfg_id != NATSEMI_MAN_ID)
+		return -ENODEV;
+
+	chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID);
+	switch (chip_id) {
+	case LM95231_CHIP_ID:
+		name = "lm95231";
+		break;
+	case LM95241_CHIP_ID:
+		name = "lm95241";
+		break;
+	default:
 		return -ENODEV;
 	}
 
@@ -431,7 +437,8 @@
 
 /* Driver data (common to all clients) */
 static const struct i2c_device_id lm95241_id[] = {
-	{ DEVNAME, 0 },
+	{ "lm95231", 0 },
+	{ "lm95241", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, lm95241_id);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
new file mode 100644
index 0000000..dce9e68
--- /dev/null
+++ b/drivers/hwmon/lm95245.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
+ *
+ * The LM95245 is a sensor chip made by National Semiconductors.
+ * It reports up to two temperatures (its own plus an external one).
+ * Complete datasheet can be obtained from National's website at:
+ *   http://www.national.com/ds.cgi/LM/LM95245.pdf
+ *
+ * This driver is based on lm95241.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DEVNAME "lm95245"
+
+static const unsigned short normal_i2c[] = {
+	0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
+
+/* LM95245 registers */
+/* general registers */
+#define LM95245_REG_RW_CONFIG1		0x03
+#define LM95245_REG_RW_CONVERS_RATE	0x04
+#define LM95245_REG_W_ONE_SHOT		0x0F
+
+/* diode configuration */
+#define LM95245_REG_RW_CONFIG2		0xBF
+#define LM95245_REG_RW_REMOTE_OFFH	0x11
+#define LM95245_REG_RW_REMOTE_OFFL	0x12
+
+/* status registers */
+#define LM95245_REG_R_STATUS1		0x02
+#define LM95245_REG_R_STATUS2		0x33
+
+/* limit registers */
+#define LM95245_REG_RW_REMOTE_OS_LIMIT		0x07
+#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT	0x20
+#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT	0x19
+#define LM95245_REG_RW_COMMON_HYSTERESIS	0x21
+
+/* temperature signed */
+#define LM95245_REG_R_LOCAL_TEMPH_S	0x00
+#define LM95245_REG_R_LOCAL_TEMPL_S	0x30
+#define LM95245_REG_R_REMOTE_TEMPH_S	0x01
+#define LM95245_REG_R_REMOTE_TEMPL_S	0x10
+/* temperature unsigned */
+#define LM95245_REG_R_REMOTE_TEMPH_U	0x31
+#define LM95245_REG_R_REMOTE_TEMPL_U	0x32
+
+/* id registers */
+#define LM95245_REG_R_MAN_ID		0xFE
+#define LM95245_REG_R_CHIP_ID		0xFF
+
+/* LM95245 specific bitfields */
+#define CFG_STOP		0x40
+#define CFG_REMOTE_TCRIT_MASK	0x10
+#define CFG_REMOTE_OS_MASK	0x08
+#define CFG_LOCAL_TCRIT_MASK	0x04
+#define CFG_LOCAL_OS_MASK	0x02
+
+#define CFG2_OS_A0		0x40
+#define CFG2_DIODE_FAULT_OS	0x20
+#define CFG2_DIODE_FAULT_TCRIT	0x10
+#define CFG2_REMOTE_TT		0x08
+#define CFG2_REMOTE_FILTER_DIS	0x00
+#define CFG2_REMOTE_FILTER_EN	0x06
+
+/* conversation rate in ms */
+#define RATE_CR0063	0x00
+#define RATE_CR0364	0x01
+#define RATE_CR1000	0x02
+#define RATE_CR2500	0x03
+
+#define STATUS1_DIODE_FAULT	0x04
+#define STATUS1_RTCRIT		0x02
+#define STATUS1_LOC		0x01
+
+#define MANUFACTURER_ID		0x01
+#define DEFAULT_REVISION	0xB3
+
+static const u8 lm95245_reg_address[] = {
+	LM95245_REG_R_LOCAL_TEMPH_S,
+	LM95245_REG_R_LOCAL_TEMPL_S,
+	LM95245_REG_R_REMOTE_TEMPH_S,
+	LM95245_REG_R_REMOTE_TEMPL_S,
+	LM95245_REG_R_REMOTE_TEMPH_U,
+	LM95245_REG_R_REMOTE_TEMPL_U,
+	LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+	LM95245_REG_RW_REMOTE_TCRIT_LIMIT,
+	LM95245_REG_RW_COMMON_HYSTERESIS,
+	LM95245_REG_R_STATUS1,
+};
+
+/* Client data (each client gets its own) */
+struct lm95245_data {
+	struct device *hwmon_dev;
+	struct mutex update_lock;
+	unsigned long last_updated;	/* in jiffies */
+	unsigned long interval;	/* in msecs */
+	bool valid;		/* zero until following fields are valid */
+	/* registers values */
+	u8 regs[ARRAY_SIZE(lm95245_reg_address)];
+	u8 config1, config2;
+};
+
+/* Conversions */
+static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
+{
+	return val_h * 1000 + val_l * 1000 / 256;
+}
+
+static int temp_from_reg_signed(u8 val_h, u8 val_l)
+{
+	if (val_h & 0x80)
+		return (val_h - 0x100) * 1000;
+	return temp_from_reg_unsigned(val_h, val_l);
+}
+
+static struct lm95245_data *lm95245_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+
+	mutex_lock(&data->update_lock);
+
+	if (time_after(jiffies, data->last_updated
+		+ msecs_to_jiffies(data->interval)) || !data->valid) {
+		int i;
+
+		dev_dbg(&client->dev, "Updating lm95245 data.\n");
+		for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
+			data->regs[i]
+			  = i2c_smbus_read_byte_data(client,
+						     lm95245_reg_address[i]);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	mutex_unlock(&data->update_lock);
+
+	return data;
+}
+
+static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
+{
+	int rate;
+	unsigned long interval;
+
+	rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
+
+	switch (rate) {
+	case RATE_CR0063:
+		interval = 63;
+		break;
+	case RATE_CR0364:
+		interval = 364;
+		break;
+	case RATE_CR1000:
+		interval = 1000;
+		break;
+	case RATE_CR2500:
+	default:
+		interval = 2500;
+		break;
+	}
+
+	return interval;
+}
+
+static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
+			unsigned long interval)
+{
+	int rate;
+
+	if (interval <= 63) {
+		interval = 63;
+		rate = RATE_CR0063;
+	} else if (interval <= 364) {
+		interval = 364;
+		rate = RATE_CR0364;
+	} else if (interval <= 1000) {
+		interval = 1000;
+		rate = RATE_CR1000;
+	} else {
+		interval = 2500;
+		rate = RATE_CR2500;
+	}
+
+	i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate);
+
+	return interval;
+}
+
+/* Sysfs stuff */
+static ssize_t show_input(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct lm95245_data *data = lm95245_update_device(dev);
+	int temp;
+	int index = to_sensor_dev_attr(attr)->index;
+
+	/*
+	 * Index 0 (Local temp) is always signed
+	 * Index 2 (Remote temp) has both signed and unsigned data
+	 * use signed calculation for remote if signed bit is set
+	 */
+	if (index == 0 || data->regs[index] & 0x80)
+		temp = temp_from_reg_signed(data->regs[index],
+			    data->regs[index + 1]);
+	else
+		temp = temp_from_reg_unsigned(data->regs[index + 2],
+			    data->regs[index + 3]);
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
+}
+
+static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct lm95245_data *data = lm95245_update_device(dev);
+	int index = to_sensor_dev_attr(attr)->index;
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+			data->regs[index] * 1000);
+}
+
+static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+	int index = to_sensor_dev_attr(attr)->index;
+	unsigned long val;
+
+	if (strict_strtoul(buf, 10, &val) < 0)
+		return -EINVAL;
+
+	val /= 1000;
+
+	val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255));
+
+	mutex_lock(&data->update_lock);
+
+	data->valid = 0;
+
+	i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val);
+
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+
+	if (strict_strtoul(buf, 10, &val) < 0)
+		return -EINVAL;
+
+	val /= 1000;
+
+	val = SENSORS_LIMIT(val, 0, 31);
+
+	mutex_lock(&data->update_lock);
+
+	data->valid = 0;
+
+	/* shared crit hysteresis */
+	i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
+		val);
+
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+
+	return snprintf(buf, PAGE_SIZE - 1,
+		data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+
+	if (strict_strtoul(buf, 10, &val) < 0)
+		return -EINVAL;
+	if (val != 1 && val != 2)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+
+	if (val == 1)
+		data->config2 |= CFG2_REMOTE_TT;
+	else
+		data->config2 &= ~CFG2_REMOTE_TT;
+
+	data->valid = 0;
+
+	i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
+				  data->config2);
+
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct lm95245_data *data = lm95245_update_device(dev);
+	int index = to_sensor_dev_attr(attr)->index;
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+			!!(data->regs[9] & index));
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct lm95245_data *data = lm95245_update_device(dev);
+
+	return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval);
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm95245_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+
+	if (strict_strtoul(buf, 10, &val) < 0)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+
+	data->interval = lm95245_set_conversion_rate(client, val);
+
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
+		set_limit, 6);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+		set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
+		STATUS1_LOC);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
+		set_limit, 7);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+		set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+		STATUS1_RTCRIT);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
+		set_type, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
+		STATUS1_DIODE_FAULT);
+
+static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
+		set_interval);
+
+static struct attribute *lm95245_attributes[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp2_input.dev_attr.attr,
+	&sensor_dev_attr_temp2_crit.dev_attr.attr,
+	&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp2_type.dev_attr.attr,
+	&sensor_dev_attr_temp2_fault.dev_attr.attr,
+	&dev_attr_update_interval.attr,
+	NULL
+};
+
+static const struct attribute_group lm95245_group = {
+	.attrs = lm95245_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm95245_detect(struct i2c_client *new_client,
+			  struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = new_client->adapter;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
+			!= MANUFACTURER_ID
+		|| i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
+			!= DEFAULT_REVISION)
+		return -ENODEV;
+
+	strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
+	return 0;
+}
+
+static void lm95245_init_client(struct i2c_client *client)
+{
+	struct lm95245_data *data = i2c_get_clientdata(client);
+
+	data->valid = 0;
+	data->interval = lm95245_read_conversion_rate(client);
+
+	data->config1 = i2c_smbus_read_byte_data(client,
+		LM95245_REG_RW_CONFIG1);
+	data->config2 = i2c_smbus_read_byte_data(client,
+		LM95245_REG_RW_CONFIG2);
+
+	if (data->config1 & CFG_STOP) {
+		/* Clear the standby bit */
+		data->config1 &= ~CFG_STOP;
+		i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
+			data->config1);
+	}
+}
+
+static int lm95245_probe(struct i2c_client *new_client,
+			 const struct i2c_device_id *id)
+{
+	struct lm95245_data *data;
+	int err;
+
+	data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	i2c_set_clientdata(new_client, data);
+	mutex_init(&data->update_lock);
+
+	/* Initialize the LM95245 chip */
+	lm95245_init_client(new_client);
+
+	/* Register sysfs hooks */
+	err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group);
+	if (err)
+		goto exit_free;
+
+	data->hwmon_dev = hwmon_device_register(&new_client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_files;
+	}
+
+	return 0;
+
+exit_remove_files:
+	sysfs_remove_group(&new_client->dev.kobj, &lm95245_group);
+exit_free:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int lm95245_remove(struct i2c_client *client)
+{
+	struct lm95245_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &lm95245_group);
+
+	kfree(data);
+	return 0;
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95245_id[] = {
+	{ DEVNAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm95245_id);
+
+static struct i2c_driver lm95245_driver = {
+	.class		= I2C_CLASS_HWMON,
+	.driver = {
+		.name	= DEVNAME,
+	},
+	.probe		= lm95245_probe,
+	.remove		= lm95245_remove,
+	.id_table	= lm95245_id,
+	.detect		= lm95245_detect,
+	.address_list	= normal_i2c,
+};
+
+static int __init sensors_lm95245_init(void)
+{
+	return i2c_add_driver(&lm95245_driver);
+}
+
+static void __exit sensors_lm95245_exit(void)
+{
+	i2c_del_driver(&lm95245_driver);
+}
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("LM95245 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm95245_init);
+module_exit(sensors_lm95245_exit);
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
new file mode 100644
index 0000000..20d1b2d
--- /dev/null
+++ b/drivers/hwmon/max1668.c
@@ -0,0 +1,502 @@
+/*
+    Copyright (c) 2011 David George <david.george@ska.ac.za>
+
+    based on adm1021.c
+    some credit to Christoph Scheurer, but largely a rewrite
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static unsigned short max1668_addr_list[] = {
+	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* max1668 registers */
+
+#define MAX1668_REG_TEMP(nr)	(nr)
+#define MAX1668_REG_STAT1	0x05
+#define MAX1668_REG_STAT2	0x06
+#define MAX1668_REG_MAN_ID	0xfe
+#define MAX1668_REG_DEV_ID	0xff
+
+/* limits */
+
+/* write high limits */
+#define MAX1668_REG_LIMH_WR(nr)	(0x13 + 2 * (nr))
+/* write low limits */
+#define MAX1668_REG_LIML_WR(nr)	(0x14 + 2 * (nr))
+/* read high limits */
+#define MAX1668_REG_LIMH_RD(nr)	(0x08 + 2 * (nr))
+/* read low limits */
+#define MAX1668_REG_LIML_RD(nr)	(0x09 + 2 * (nr))
+
+/* manufacturer and device ID Constants */
+#define MAN_ID_MAXIM		0x4d
+#define DEV_ID_MAX1668		0x3
+#define DEV_ID_MAX1805		0x5
+#define DEV_ID_MAX1989		0xb
+
+/* read only mode module parameter */
+static int read_only;
+module_param(read_only, bool, 0);
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+enum chips { max1668, max1805, max1989 };
+
+struct max1668_data {
+	struct device *hwmon_dev;
+	enum chips type;
+
+	struct mutex update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	/* 1x local and 4x remote */
+	s8 temp_max[5];
+	s8 temp_min[5];
+	s8 temp[5];
+	u16 alarms;
+};
+
+static struct max1668_data *max1668_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max1668_data *data = i2c_get_clientdata(client);
+	struct max1668_data *ret = data;
+	s32 val;
+	int i;
+
+	mutex_lock(&data->update_lock);
+
+	if (data->valid && !time_after(jiffies,
+			data->last_updated + HZ + HZ / 2))
+		goto abort;
+
+	for (i = 0; i < 5; i++) {
+		val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
+		if (unlikely(val < 0)) {
+			ret = ERR_PTR(val);
+			goto abort;
+		}
+		data->temp[i] = (s8) val;
+
+		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
+		if (unlikely(val < 0)) {
+			ret = ERR_PTR(val);
+			goto abort;
+		}
+		data->temp_max[i] = (s8) val;
+
+		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
+		if (unlikely(val < 0)) {
+			ret = ERR_PTR(val);
+			goto abort;
+		}
+		data->temp_min[i] = (s8) val;
+	}
+
+	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
+	if (unlikely(val < 0)) {
+		ret = ERR_PTR(val);
+		goto abort;
+	}
+	data->alarms = val << 8;
+
+	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
+	if (unlikely(val < 0)) {
+		ret = ERR_PTR(val);
+		goto abort;
+	}
+	data->alarms |= val;
+
+	data->last_updated = jiffies;
+	data->valid = 1;
+abort:
+	mutex_unlock(&data->update_lock);
+
+	return ret;
+}
+
+static ssize_t show_temp(struct device *dev,
+			 struct device_attribute *devattr, char *buf)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct max1668_data *data = max1668_update_device(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n", data->temp[index] * 1000);
+}
+
+static ssize_t show_temp_max(struct device *dev,
+			     struct device_attribute *devattr, char *buf)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct max1668_data *data = max1668_update_device(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
+}
+
+static ssize_t show_temp_min(struct device *dev,
+			     struct device_attribute *devattr, char *buf)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct max1668_data *data = max1668_update_device(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int index = to_sensor_dev_attr(attr)->index;
+	struct max1668_data *data = max1668_update_device(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t show_fault(struct device *dev,
+			  struct device_attribute *devattr, char *buf)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct max1668_data *data = max1668_update_device(dev);
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return sprintf(buf, "%u\n",
+		       (data->alarms & (1 << 12)) && data->temp[index] == 127);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+			    struct device_attribute *devattr,
+			    const char *buf, size_t count)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max1668_data *data = i2c_get_clientdata(client);
+	long temp;
+	int ret;
+
+	ret = kstrtol(buf, 10, &temp);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&data->update_lock);
+	data->temp_max[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+	if (i2c_smbus_write_byte_data(client,
+					MAX1668_REG_LIMH_WR(index),
+					data->temp_max[index]))
+		count = -EIO;
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t set_temp_min(struct device *dev,
+			    struct device_attribute *devattr,
+			    const char *buf, size_t count)
+{
+	int index = to_sensor_dev_attr(devattr)->index;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max1668_data *data = i2c_get_clientdata(client);
+	long temp;
+	int ret;
+
+	ret = kstrtol(buf, 10, &temp);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&data->update_lock);
+	data->temp_min[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+	if (i2c_smbus_write_byte_data(client,
+					MAX1668_REG_LIML_WR(index),
+					data->temp_max[index]))
+		count = -EIO;
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
+				set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
+				set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
+				set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
+				set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
+				set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
+				set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
+				set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
+				set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
+				set_temp_max, 4);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
+				set_temp_min, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
+
+/* Attributes common to MAX1668, MAX1989 and MAX1805 */
+static struct attribute *max1668_attribute_common[] = {
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
+	&sensor_dev_attr_temp1_min.dev_attr.attr,
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp2_max.dev_attr.attr,
+	&sensor_dev_attr_temp2_min.dev_attr.attr,
+	&sensor_dev_attr_temp2_input.dev_attr.attr,
+	&sensor_dev_attr_temp3_max.dev_attr.attr,
+	&sensor_dev_attr_temp3_min.dev_attr.attr,
+	&sensor_dev_attr_temp3_input.dev_attr.attr,
+
+	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+
+	&sensor_dev_attr_temp2_fault.dev_attr.attr,
+	&sensor_dev_attr_temp3_fault.dev_attr.attr,
+	NULL
+};
+
+/* Attributes not present on MAX1805 */
+static struct attribute *max1668_attribute_unique[] = {
+	&sensor_dev_attr_temp4_max.dev_attr.attr,
+	&sensor_dev_attr_temp4_min.dev_attr.attr,
+	&sensor_dev_attr_temp4_input.dev_attr.attr,
+	&sensor_dev_attr_temp5_max.dev_attr.attr,
+	&sensor_dev_attr_temp5_min.dev_attr.attr,
+	&sensor_dev_attr_temp5_input.dev_attr.attr,
+
+	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+
+	&sensor_dev_attr_temp4_fault.dev_attr.attr,
+	&sensor_dev_attr_temp5_fault.dev_attr.attr,
+	NULL
+};
+
+static mode_t max1668_attribute_mode(struct kobject *kobj,
+				     struct attribute *attr, int index)
+{
+	int ret = S_IRUGO;
+	if (read_only)
+		return ret;
+	if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
+	    attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
+		ret |= S_IWUSR;
+	return ret;
+}
+
+static const struct attribute_group max1668_group_common = {
+	.attrs = max1668_attribute_common,
+	.is_visible = max1668_attribute_mode
+};
+
+static const struct attribute_group max1668_group_unique = {
+	.attrs = max1668_attribute_unique,
+	.is_visible = max1668_attribute_mode
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max1668_detect(struct i2c_client *client,
+			  struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	const char *type_name;
+	int man_id, dev_id;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	/* Check for unsupported part */
+	man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID);
+	if (man_id != MAN_ID_MAXIM)
+		return -ENODEV;
+
+	dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID);
+	if (dev_id < 0)
+		return -ENODEV;
+
+	type_name = NULL;
+	if (dev_id == DEV_ID_MAX1668)
+		type_name = "max1668";
+	else if (dev_id == DEV_ID_MAX1805)
+		type_name = "max1805";
+	else if (dev_id == DEV_ID_MAX1989)
+		type_name = "max1989";
+
+	if (!type_name)
+		return -ENODEV;
+
+	strlcpy(info->type, type_name, I2C_NAME_SIZE);
+
+	return 0;
+}
+
+static int max1668_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	struct max1668_data *data;
+	int err;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+	data->type = id->driver_data;
+	mutex_init(&data->update_lock);
+
+	/* Register sysfs hooks */
+	err = sysfs_create_group(&client->dev.kobj, &max1668_group_common);
+	if (err)
+		goto error_free;
+
+	if (data->type == max1668 || data->type == max1989) {
+		err = sysfs_create_group(&client->dev.kobj,
+					 &max1668_group_unique);
+		if (err)
+			goto error_sysrem0;
+	}
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto error_sysrem1;
+	}
+
+	return 0;
+
+error_sysrem1:
+	if (data->type == max1668 || data->type == max1989)
+		sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+error_sysrem0:
+	sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+error_free:
+	kfree(data);
+	return err;
+}
+
+static int max1668_remove(struct i2c_client *client)
+{
+	struct max1668_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	if (data->type == max1668 || data->type == max1989)
+		sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+
+	sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+
+	kfree(data);
+	return 0;
+}
+
+static const struct i2c_device_id max1668_id[] = {
+	{ "max1668", max1668 },
+	{ "max1805", max1805 },
+	{ "max1989", max1989 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max1668_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max1668_driver = {
+	.class = I2C_CLASS_HWMON,
+	.driver = {
+		  .name	= "max1668",
+		  },
+	.probe = max1668_probe,
+	.remove	= max1668_remove,
+	.id_table = max1668_id,
+	.detect	= max1668_detect,
+	.address_list = max1668_addr_list,
+};
+
+static int __init sensors_max1668_init(void)
+{
+	return i2c_add_driver(&max1668_driver);
+}
+
+static void __exit sensors_max1668_exit(void)
+{
+	i2c_del_driver(&max1668_driver);
+}
+
+MODULE_AUTHOR("David George <david.george@ska.ac.za>");
+MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_max1668_init)
+module_exit(sensors_max1668_exit)
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
new file mode 100644
index 0000000..d7926f4
--- /dev/null
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -0,0 +1,453 @@
+/*
+ * ntc_thermistor.c - NTC Thermistors
+ *
+ *  Copyright (C) 2010 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.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/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/math64.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/platform_data/ntc_thermistor.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+struct ntc_compensation {
+	int		temp_C;
+	unsigned int	ohm;
+};
+
+/*
+ * A compensation table should be sorted by the values of .ohm
+ * in descending order.
+ * The following compensation tables are from the specification of Murata NTC
+ * Thermistors Datasheet
+ */
+const struct ntc_compensation ncpXXwb473[] = {
+	{ .temp_C	= -40, .ohm	= 1747920 },
+	{ .temp_C	= -35, .ohm	= 1245428 },
+	{ .temp_C	= -30, .ohm	= 898485 },
+	{ .temp_C	= -25, .ohm	= 655802 },
+	{ .temp_C	= -20, .ohm	= 483954 },
+	{ .temp_C	= -15, .ohm	= 360850 },
+	{ .temp_C	= -10, .ohm	= 271697 },
+	{ .temp_C	= -5, .ohm	= 206463 },
+	{ .temp_C	= 0, .ohm	= 158214 },
+	{ .temp_C	= 5, .ohm	= 122259 },
+	{ .temp_C	= 10, .ohm	= 95227 },
+	{ .temp_C	= 15, .ohm	= 74730 },
+	{ .temp_C	= 20, .ohm	= 59065 },
+	{ .temp_C	= 25, .ohm	= 47000 },
+	{ .temp_C	= 30, .ohm	= 37643 },
+	{ .temp_C	= 35, .ohm	= 30334 },
+	{ .temp_C	= 40, .ohm	= 24591 },
+	{ .temp_C	= 45, .ohm	= 20048 },
+	{ .temp_C	= 50, .ohm	= 16433 },
+	{ .temp_C	= 55, .ohm	= 13539 },
+	{ .temp_C	= 60, .ohm	= 11209 },
+	{ .temp_C	= 65, .ohm	= 9328 },
+	{ .temp_C	= 70, .ohm	= 7798 },
+	{ .temp_C	= 75, .ohm	= 6544 },
+	{ .temp_C	= 80, .ohm	= 5518 },
+	{ .temp_C	= 85, .ohm	= 4674 },
+	{ .temp_C	= 90, .ohm	= 3972 },
+	{ .temp_C	= 95, .ohm	= 3388 },
+	{ .temp_C	= 100, .ohm	= 2902 },
+	{ .temp_C	= 105, .ohm	= 2494 },
+	{ .temp_C	= 110, .ohm	= 2150 },
+	{ .temp_C	= 115, .ohm	= 1860 },
+	{ .temp_C	= 120, .ohm	= 1615 },
+	{ .temp_C	= 125, .ohm	= 1406 },
+};
+const struct ntc_compensation ncpXXwl333[] = {
+	{ .temp_C	= -40, .ohm	= 1610154 },
+	{ .temp_C	= -35, .ohm	= 1130850 },
+	{ .temp_C	= -30, .ohm	= 802609 },
+	{ .temp_C	= -25, .ohm	= 575385 },
+	{ .temp_C	= -20, .ohm	= 416464 },
+	{ .temp_C	= -15, .ohm	= 304219 },
+	{ .temp_C	= -10, .ohm	= 224193 },
+	{ .temp_C	= -5, .ohm	= 166623 },
+	{ .temp_C	= 0, .ohm	= 124850 },
+	{ .temp_C	= 5, .ohm	= 94287 },
+	{ .temp_C	= 10, .ohm	= 71747 },
+	{ .temp_C	= 15, .ohm	= 54996 },
+	{ .temp_C	= 20, .ohm	= 42455 },
+	{ .temp_C	= 25, .ohm	= 33000 },
+	{ .temp_C	= 30, .ohm	= 25822 },
+	{ .temp_C	= 35, .ohm	= 20335 },
+	{ .temp_C	= 40, .ohm	= 16115 },
+	{ .temp_C	= 45, .ohm	= 12849 },
+	{ .temp_C	= 50, .ohm	= 10306 },
+	{ .temp_C	= 55, .ohm	= 8314 },
+	{ .temp_C	= 60, .ohm	= 6746 },
+	{ .temp_C	= 65, .ohm	= 5503 },
+	{ .temp_C	= 70, .ohm	= 4513 },
+	{ .temp_C	= 75, .ohm	= 3721 },
+	{ .temp_C	= 80, .ohm	= 3084 },
+	{ .temp_C	= 85, .ohm	= 2569 },
+	{ .temp_C	= 90, .ohm	= 2151 },
+	{ .temp_C	= 95, .ohm	= 1809 },
+	{ .temp_C	= 100, .ohm	= 1529 },
+	{ .temp_C	= 105, .ohm	= 1299 },
+	{ .temp_C	= 110, .ohm	= 1108 },
+	{ .temp_C	= 115, .ohm	= 949 },
+	{ .temp_C	= 120, .ohm	= 817 },
+	{ .temp_C	= 125, .ohm	= 707 },
+};
+
+struct ntc_data {
+	struct device *hwmon_dev;
+	struct ntc_thermistor_platform_data *pdata;
+	const struct ntc_compensation *comp;
+	struct device *dev;
+	int n_comp;
+	char name[PLATFORM_NAME_SIZE];
+};
+
+static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
+{
+	if (divisor == 0 && dividend == 0)
+		return 0;
+	if (divisor == 0)
+		return UINT_MAX;
+	return div64_u64(dividend, divisor);
+}
+
+static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
+		unsigned int uV)
+{
+	struct ntc_thermistor_platform_data *pdata = data->pdata;
+	u64 mV = uV / 1000;
+	u64 pmV = pdata->pullup_uV / 1000;
+	u64 N, puO, pdO;
+	puO = pdata->pullup_ohm;
+	pdO = pdata->pulldown_ohm;
+
+	if (mV == 0) {
+		if (pdata->connect == NTC_CONNECTED_POSITIVE)
+			return UINT_MAX;
+		return 0;
+	}
+	if (mV >= pmV)
+		return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+			0 : UINT_MAX;
+
+	if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
+		N = div64_u64_safe(pdO * (pmV - mV), mV);
+	else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0)
+		N = div64_u64_safe(puO * mV, pmV - mV);
+	else if (pdata->connect == NTC_CONNECTED_POSITIVE)
+		N = div64_u64_safe(pdO * puO * (pmV - mV),
+				puO * mV - pdO * (pmV - mV));
+	else
+		N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
+
+	return (unsigned int) N;
+}
+
+static int lookup_comp(struct ntc_data *data,
+		unsigned int ohm, int *i_low, int *i_high)
+{
+	int start, end, mid = -1;
+
+	/* Do a binary search on compensation table */
+	start = 0;
+	end = data->n_comp;
+
+	while (end > start) {
+		mid = start + (end - start) / 2;
+		if (data->comp[mid].ohm < ohm)
+			end = mid;
+		else if (data->comp[mid].ohm > ohm)
+			start = mid + 1;
+		else
+			break;
+	}
+
+	if (mid == 0) {
+		if (data->comp[mid].ohm > ohm) {
+			*i_high = mid;
+			*i_low = mid + 1;
+			return 0;
+		} else {
+			*i_low = mid;
+			*i_high = -1;
+			return -EINVAL;
+		}
+	}
+	if (mid == (data->n_comp - 1)) {
+		if (data->comp[mid].ohm <= ohm) {
+			*i_low = mid;
+			*i_high = mid - 1;
+			return 0;
+		} else {
+			*i_low = -1;
+			*i_high = mid;
+			return -EINVAL;
+		}
+	}
+
+	if (data->comp[mid].ohm <= ohm) {
+		*i_low = mid;
+		*i_high = mid - 1;
+	}
+	if (data->comp[mid].ohm > ohm) {
+		*i_low = mid + 1;
+		*i_high = mid;
+	}
+
+	return 0;
+}
+
+static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp)
+{
+	int low, high;
+	int ret;
+
+	ret = lookup_comp(data, ohm, &low, &high);
+	if (ret) {
+		/* Unable to use linear approximation */
+		if (low != -1)
+			*temp = data->comp[low].temp_C * 1000;
+		else if (high != -1)
+			*temp = data->comp[high].temp_C * 1000;
+		else
+			return ret;
+	} else {
+		*temp = data->comp[low].temp_C * 1000 +
+			((data->comp[high].temp_C - data->comp[low].temp_C) *
+			 1000 * ((int)ohm - (int)data->comp[low].ohm)) /
+			((int)data->comp[high].ohm - (int)data->comp[low].ohm);
+	}
+
+	return 0;
+}
+
+static int ntc_thermistor_read(struct ntc_data *data, int *temp)
+{
+	int ret;
+	int read_ohm, read_uV;
+	unsigned int ohm = 0;
+
+	if (data->pdata->read_ohm) {
+		read_ohm = data->pdata->read_ohm();
+		if (read_ohm < 0)
+			return read_ohm;
+		ohm = (unsigned int)read_ohm;
+	}
+
+	if (data->pdata->read_uV) {
+		read_uV = data->pdata->read_uV();
+		if (read_uV < 0)
+			return read_uV;
+		ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV);
+	}
+
+	ret = get_temp_mC(data, ohm, temp);
+	if (ret) {
+		dev_dbg(data->dev, "Sensor reading function not available.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static ssize_t ntc_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct ntc_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t ntc_show_type(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "4\n");
+}
+
+static ssize_t ntc_show_temp(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct ntc_data *data = dev_get_drvdata(dev);
+	int temp, ret;
+
+	ret = ntc_thermistor_read(data, &temp);
+	if (ret)
+		return ret;
+	return sprintf(buf, "%d\n", temp);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
+static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
+
+static struct attribute *ntc_attributes[] = {
+	&dev_attr_name.attr,
+	&sensor_dev_attr_temp1_type.dev_attr.attr,
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ntc_attr_group = {
+	.attrs = ntc_attributes,
+};
+
+static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
+{
+	struct ntc_data *data;
+	struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data;
+	int ret = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+
+	/* Either one of the two is required. */
+	if (!pdata->read_uV && !pdata->read_ohm) {
+		dev_err(&pdev->dev, "Both read_uV and read_ohm missing."
+				"Need either one of the two.\n");
+		return -EINVAL;
+	}
+
+	if (pdata->read_uV && pdata->read_ohm) {
+		dev_warn(&pdev->dev, "Only one of read_uV and read_ohm "
+				"is needed; ignoring read_uV.\n");
+		pdata->read_uV = NULL;
+	}
+
+	if (pdata->read_uV && (pdata->pullup_uV == 0 ||
+				(pdata->pullup_ohm == 0 && pdata->connect ==
+				 NTC_CONNECTED_GROUND) ||
+				(pdata->pulldown_ohm == 0 && pdata->connect ==
+				 NTC_CONNECTED_POSITIVE) ||
+				(pdata->connect != NTC_CONNECTED_POSITIVE &&
+				 pdata->connect != NTC_CONNECTED_GROUND))) {
+		dev_err(&pdev->dev, "Required data to use read_uV not "
+				"supplied.\n");
+		return -EINVAL;
+	}
+
+	data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = &pdev->dev;
+	data->pdata = pdata;
+	strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE);
+
+	switch (pdev->id_entry->driver_data) {
+	case TYPE_NCPXXWB473:
+		data->comp = ncpXXwb473;
+		data->n_comp = ARRAY_SIZE(ncpXXwb473);
+		break;
+	case TYPE_NCPXXWL333:
+		data->comp = ncpXXwl333;
+		data->n_comp = ARRAY_SIZE(ncpXXwl333);
+		break;
+	default:
+		dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
+				pdev->id_entry->driver_data,
+				pdev->id_entry->name);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, data);
+
+	ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
+	if (ret) {
+		dev_err(data->dev, "unable to create sysfs files\n");
+		goto err;
+	}
+
+	data->hwmon_dev = hwmon_device_register(data->dev);
+	if (IS_ERR_OR_NULL(data->hwmon_dev)) {
+		dev_err(data->dev, "unable to register as hwmon device.\n");
+		ret = -EINVAL;
+		goto err_after_sysfs;
+	}
+
+	dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n",
+			pdev->name, pdev->id, pdev->id_entry->name,
+			pdev->id_entry->driver_data);
+	return 0;
+err_after_sysfs:
+	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+err:
+	kfree(data);
+	return ret;
+}
+
+static int __devexit ntc_thermistor_remove(struct platform_device *pdev)
+{
+	struct ntc_data *data = platform_get_drvdata(pdev);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(data);
+
+	return 0;
+}
+
+static const struct platform_device_id ntc_thermistor_id[] = {
+	{ "ncp15wb473", TYPE_NCPXXWB473 },
+	{ "ncp18wb473", TYPE_NCPXXWB473 },
+	{ "ncp21wb473", TYPE_NCPXXWB473 },
+	{ "ncp03wb473", TYPE_NCPXXWB473 },
+	{ "ncp15wl333", TYPE_NCPXXWL333 },
+	{ },
+};
+
+static struct platform_driver ntc_thermistor_driver = {
+	.driver = {
+		.name = "ntc-thermistor",
+		.owner = THIS_MODULE,
+	},
+	.probe = ntc_thermistor_probe,
+	.remove = __devexit_p(ntc_thermistor_remove),
+	.id_table = ntc_thermistor_id,
+};
+
+static int __init ntc_thermistor_init(void)
+{
+	return platform_driver_register(&ntc_thermistor_driver);
+}
+
+module_init(ntc_thermistor_init);
+
+static void __exit ntc_thermistor_cleanup(void)
+{
+	platform_driver_unregister(&ntc_thermistor_driver);
+}
+
+module_exit(ntc_thermistor_cleanup);
+
+MODULE_DESCRIPTION("NTC Thermistor Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ntc-thermistor");
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
new file mode 100644
index 0000000..c9237b9
--- /dev/null
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -0,0 +1,100 @@
+#
+# PMBus chip drivers configuration
+#
+
+menuconfig PMBUS
+	tristate "PMBus support"
+	depends on I2C && EXPERIMENTAL
+	default n
+	help
+	  Say yes here if you want to enable PMBus support.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called pmbus_core.
+
+if PMBUS
+
+config SENSORS_PMBUS
+	tristate "Generic PMBus devices"
+	default y
+	help
+	  If you say yes here you get hardware monitoring support for generic
+	  PMBus devices, including but not limited to ADP4000, BMR450, BMR451,
+	  BMR453, BMR454, LTC2978, NCP4200, and NCP4208.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called pmbus.
+
+config SENSORS_ADM1275
+	tristate "Analog Devices ADM1275"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for Analog
+	  Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called adm1275.
+
+config SENSORS_LM25066
+	tristate "National Semiconductor LM25066 and compatibles"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for National
+	  Semiconductor LM25066, LM5064, and LM5066.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called lm25066.
+
+config SENSORS_MAX16064
+	tristate "Maxim MAX16064"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for Maxim
+	  MAX16064.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called max16064.
+
+config SENSORS_MAX34440
+	tristate "Maxim MAX34440/MAX34441"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for Maxim
+	  MAX34440 and MAX34441.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called max34440.
+
+config SENSORS_MAX8688
+	tristate "Maxim MAX8688"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for Maxim
+	  MAX8688.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called max8688.
+
+config SENSORS_UCD9000
+	tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for TI
+	  UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
+	  Controllers.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called ucd9000.
+
+config SENSORS_UCD9200
+	tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
+	default n
+	help
+	  If you say yes here you get hardware monitoring support for TI
+	  UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
+	  Digital PWM System Controllers.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called ucd9200.
+
+endif # PMBUS
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
new file mode 100644
index 0000000..623eedb
--- /dev/null
+++ b/drivers/hwmon/pmbus/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for PMBus chip drivers.
+#
+
+obj-$(CONFIG_PMBUS)		+= pmbus_core.o
+obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o
+obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
+obj-$(CONFIG_SENSORS_LM25066)	+= lm25066.o
+obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o
+obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o
+obj-$(CONFIG_SENSORS_MAX8688)	+= max8688.o
+obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o
+obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o
diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
similarity index 67%
rename from drivers/hwmon/adm1275.c
rename to drivers/hwmon/pmbus/adm1275.c
index 8bc1bd6..c936e27 100644
--- a/drivers/hwmon/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -23,11 +23,68 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define ADM1275_PEAK_IOUT		0xd0
+#define ADM1275_PEAK_VIN		0xd1
+#define ADM1275_PEAK_VOUT		0xd2
 #define ADM1275_PMON_CONFIG		0xd4
 
 #define ADM1275_VIN_VOUT_SELECT		(1 << 6)
 #define ADM1275_VRANGE			(1 << 5)
 
+static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	int ret;
+
+	if (page)
+		return -EINVAL;
+
+	switch (reg) {
+	case PMBUS_VIRT_READ_IOUT_MAX:
+		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
+		break;
+	case PMBUS_VIRT_READ_VOUT_MAX:
+		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT);
+		break;
+	case PMBUS_VIRT_READ_VIN_MAX:
+		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
+		break;
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+	case PMBUS_VIRT_RESET_VIN_HISTORY:
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
+static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
+				   u16 word)
+{
+	int ret;
+
+	if (page)
+		return -EINVAL;
+
+	switch (reg) {
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
+		break;
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
+		break;
+	case PMBUS_VIRT_RESET_VIN_HISTORY:
+		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
 static int adm1275_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -50,14 +107,17 @@
 	}
 
 	info->pages = 1;
-	info->direct[PSC_VOLTAGE_IN] = true;
-	info->direct[PSC_VOLTAGE_OUT] = true;
-	info->direct[PSC_CURRENT_OUT] = true;
+	info->format[PSC_VOLTAGE_IN] = direct;
+	info->format[PSC_VOLTAGE_OUT] = direct;
+	info->format[PSC_CURRENT_OUT] = direct;
 	info->m[PSC_CURRENT_OUT] = 807;
 	info->b[PSC_CURRENT_OUT] = 20475;
 	info->R[PSC_CURRENT_OUT] = -1;
 	info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
 
+	info->read_word_data = adm1275_read_word_data;
+	info->write_word_data = adm1275_write_word_data;
+
 	if (config & ADM1275_VRANGE) {
 		info->m[PSC_VOLTAGE_IN] = 19199;
 		info->b[PSC_VOLTAGE_IN] = 0;
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
new file mode 100644
index 0000000..d4bc114
--- /dev/null
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -0,0 +1,340 @@
+/*
+ * Hardware monitoring driver for LM25066 / LM5064 / LM5066
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { lm25066, lm5064, lm5066 };
+
+#define LM25066_READ_VAUX		0xd0
+#define LM25066_MFR_READ_IIN		0xd1
+#define LM25066_MFR_READ_PIN		0xd2
+#define LM25066_MFR_IIN_OC_WARN_LIMIT	0xd3
+#define LM25066_MFR_PIN_OP_WARN_LIMIT	0xd4
+#define LM25066_READ_PIN_PEAK		0xd5
+#define LM25066_CLEAR_PIN_PEAK		0xd6
+#define LM25066_DEVICE_SETUP		0xd9
+#define LM25066_READ_AVG_VIN		0xdc
+#define LM25066_READ_AVG_VOUT		0xdd
+#define LM25066_READ_AVG_IIN		0xde
+#define LM25066_READ_AVG_PIN		0xdf
+
+#define LM25066_DEV_SETUP_CL		(1 << 4)	/* Current limit */
+
+struct lm25066_data {
+	int id;
+	struct pmbus_driver_info info;
+};
+
+#define to_lm25066_data(x)  container_of(x, struct lm25066_data, info)
+
+static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+	const struct lm25066_data *data = to_lm25066_data(info);
+	int ret;
+
+	if (page > 1)
+		return -EINVAL;
+
+	/* Map READ_VAUX into READ_VOUT register on page 1 */
+	if (page == 1) {
+		switch (reg) {
+		case PMBUS_READ_VOUT:
+			ret = pmbus_read_word_data(client, 0,
+						   LM25066_READ_VAUX);
+			if (ret < 0)
+				break;
+			/* Adjust returned value to match VOUT coefficients */
+			switch (data->id) {
+			case lm25066:
+				/* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
+				ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+				break;
+			case lm5064:
+				/* VOUT: 4.53 mV VAUX: 700 uV LSB */
+				ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+				break;
+			case lm5066:
+				/* VOUT: 2.18 mV VAUX: 725 uV LSB */
+				ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
+				break;
+			}
+			break;
+		default:
+			/* No other valid registers on page 1 */
+			ret = -EINVAL;
+			break;
+		}
+		goto done;
+	}
+
+	switch (reg) {
+	case PMBUS_READ_IIN:
+		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
+		break;
+	case PMBUS_READ_PIN:
+		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN);
+		break;
+	case PMBUS_IIN_OC_WARN_LIMIT:
+		ret = pmbus_read_word_data(client, 0,
+					   LM25066_MFR_IIN_OC_WARN_LIMIT);
+		break;
+	case PMBUS_PIN_OP_WARN_LIMIT:
+		ret = pmbus_read_word_data(client, 0,
+					   LM25066_MFR_PIN_OP_WARN_LIMIT);
+		break;
+	case PMBUS_VIRT_READ_VIN_AVG:
+		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
+		break;
+	case PMBUS_VIRT_READ_VOUT_AVG:
+		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT);
+		break;
+	case PMBUS_VIRT_READ_IIN_AVG:
+		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
+		break;
+	case PMBUS_VIRT_READ_PIN_AVG:
+		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
+		break;
+	case PMBUS_VIRT_READ_PIN_MAX:
+		ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK);
+		break;
+	case PMBUS_VIRT_RESET_PIN_HISTORY:
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+done:
+	return ret;
+}
+
+static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
+				   u16 word)
+{
+	int ret;
+
+	if (page > 1)
+		return -EINVAL;
+
+	switch (reg) {
+	case PMBUS_IIN_OC_WARN_LIMIT:
+		ret = pmbus_write_word_data(client, 0,
+					    LM25066_MFR_IIN_OC_WARN_LIMIT,
+					    word);
+		break;
+	case PMBUS_PIN_OP_WARN_LIMIT:
+		ret = pmbus_write_word_data(client, 0,
+					    LM25066_MFR_PIN_OP_WARN_LIMIT,
+					    word);
+		break;
+	case PMBUS_VIRT_RESET_PIN_HISTORY:
+		ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
+static int lm25066_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	int config;
+	int ret;
+	struct lm25066_data *data;
+	struct pmbus_driver_info *info;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_BYTE_DATA))
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP);
+	if (config < 0) {
+		ret = config;
+		goto err_mem;
+	}
+
+	data->id = id->driver_data;
+	info = &data->info;
+
+	info->pages = 2;
+	info->format[PSC_VOLTAGE_IN] = direct;
+	info->format[PSC_VOLTAGE_OUT] = direct;
+	info->format[PSC_CURRENT_IN] = direct;
+	info->format[PSC_TEMPERATURE] = direct;
+	info->format[PSC_POWER] = direct;
+
+	info->m[PSC_TEMPERATURE] = 16;
+	info->b[PSC_TEMPERATURE] = 0;
+	info->R[PSC_TEMPERATURE] = 0;
+
+	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
+	  | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN
+	  | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+	info->func[1] = PMBUS_HAVE_VOUT;
+
+	info->read_word_data = lm25066_read_word_data;
+	info->write_word_data = lm25066_write_word_data;
+
+	switch (id->driver_data) {
+	case lm25066:
+		info->m[PSC_VOLTAGE_IN] = 22070;
+		info->b[PSC_VOLTAGE_IN] = 0;
+		info->R[PSC_VOLTAGE_IN] = -2;
+		info->m[PSC_VOLTAGE_OUT] = 22070;
+		info->b[PSC_VOLTAGE_OUT] = 0;
+		info->R[PSC_VOLTAGE_OUT] = -2;
+
+		if (config & LM25066_DEV_SETUP_CL) {
+			info->m[PSC_CURRENT_IN] = 6852;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 369;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -2;
+		} else {
+			info->m[PSC_CURRENT_IN] = 13661;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 736;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -2;
+		}
+		break;
+	case lm5064:
+		info->m[PSC_VOLTAGE_IN] = 22075;
+		info->b[PSC_VOLTAGE_IN] = 0;
+		info->R[PSC_VOLTAGE_IN] = -2;
+		info->m[PSC_VOLTAGE_OUT] = 22075;
+		info->b[PSC_VOLTAGE_OUT] = 0;
+		info->R[PSC_VOLTAGE_OUT] = -2;
+
+		if (config & LM25066_DEV_SETUP_CL) {
+			info->m[PSC_CURRENT_IN] = 6713;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 3619;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -3;
+		} else {
+			info->m[PSC_CURRENT_IN] = 13426;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 7238;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -3;
+		}
+		break;
+	case lm5066:
+		info->m[PSC_VOLTAGE_IN] = 4587;
+		info->b[PSC_VOLTAGE_IN] = 0;
+		info->R[PSC_VOLTAGE_IN] = -2;
+		info->m[PSC_VOLTAGE_OUT] = 4587;
+		info->b[PSC_VOLTAGE_OUT] = 0;
+		info->R[PSC_VOLTAGE_OUT] = -2;
+
+		if (config & LM25066_DEV_SETUP_CL) {
+			info->m[PSC_CURRENT_IN] = 10753;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 1204;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -3;
+		} else {
+			info->m[PSC_CURRENT_IN] = 5405;
+			info->b[PSC_CURRENT_IN] = 0;
+			info->R[PSC_CURRENT_IN] = -2;
+			info->m[PSC_POWER] = 605;
+			info->b[PSC_POWER] = 0;
+			info->R[PSC_POWER] = -3;
+		}
+		break;
+	default:
+		ret = -ENODEV;
+		goto err_mem;
+	}
+
+	ret = pmbus_do_probe(client, id, info);
+	if (ret)
+		goto err_mem;
+	return 0;
+
+err_mem:
+	kfree(data);
+	return ret;
+}
+
+static int lm25066_remove(struct i2c_client *client)
+{
+	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+	const struct lm25066_data *data = to_lm25066_data(info);
+	int ret;
+
+	ret = pmbus_do_remove(client);
+	kfree(data);
+	return ret;
+}
+
+static const struct i2c_device_id lm25066_id[] = {
+	{"lm25066", lm25066},
+	{"lm5064", lm5064},
+	{"lm5066", lm5066},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, lm25066_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm25066_driver = {
+	.driver = {
+		   .name = "lm25066",
+		   },
+	.probe = lm25066_probe,
+	.remove = lm25066_remove,
+	.id_table = lm25066_id,
+};
+
+static int __init lm25066_init(void)
+{
+	return i2c_add_driver(&lm25066_driver);
+}
+
+static void __exit lm25066_exit(void)
+{
+	i2c_del_driver(&lm25066_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
+MODULE_LICENSE("GPL");
+module_init(lm25066_init);
+module_exit(lm25066_exit);
diff --git a/drivers/hwmon/max16064.c b/drivers/hwmon/pmbus/max16064.c
similarity index 66%
rename from drivers/hwmon/max16064.c
rename to drivers/hwmon/pmbus/max16064.c
index 1d6d717..e50b296 100644
--- a/drivers/hwmon/max16064.c
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -25,11 +25,60 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define MAX16064_MFR_VOUT_PEAK		0xd4
+#define MAX16064_MFR_TEMPERATURE_PEAK	0xd6
+
+static int max16064_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIRT_READ_VOUT_MAX:
+		ret = pmbus_read_word_data(client, page,
+					   MAX16064_MFR_VOUT_PEAK);
+		break;
+	case PMBUS_VIRT_READ_TEMP_MAX:
+		ret = pmbus_read_word_data(client, page,
+					   MAX16064_MFR_TEMPERATURE_PEAK);
+		break;
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
+static int max16064_write_word_data(struct i2c_client *client, int page,
+				    int reg, u16 word)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+		ret = pmbus_write_word_data(client, page,
+					    MAX16064_MFR_VOUT_PEAK, 0);
+		break;
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = pmbus_write_word_data(client, page,
+					    MAX16064_MFR_TEMPERATURE_PEAK,
+					    0xffff);
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
 static struct pmbus_driver_info max16064_info = {
 	.pages = 4,
-	.direct[PSC_VOLTAGE_IN] = true,
-	.direct[PSC_VOLTAGE_OUT] = true,
-	.direct[PSC_TEMPERATURE] = true,
+	.format[PSC_VOLTAGE_IN] = direct,
+	.format[PSC_VOLTAGE_OUT] = direct,
+	.format[PSC_TEMPERATURE] = direct,
 	.m[PSC_VOLTAGE_IN] = 19995,
 	.b[PSC_VOLTAGE_IN] = 0,
 	.R[PSC_VOLTAGE_IN] = -1,
@@ -44,6 +93,8 @@
 	.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
 	.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
 	.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+	.read_word_data = max16064_read_word_data,
+	.write_word_data = max16064_write_word_data,
 };
 
 static int max16064_probe(struct i2c_client *client,
diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/pmbus/max34440.c
similarity index 76%
rename from drivers/hwmon/max34440.c
rename to drivers/hwmon/pmbus/max34440.c
index db11e1a..fda621d 100644
--- a/drivers/hwmon/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -27,11 +27,70 @@
 
 enum chips { max34440, max34441 };
 
+#define MAX34440_MFR_VOUT_PEAK		0xd4
+#define MAX34440_MFR_IOUT_PEAK		0xd5
+#define MAX34440_MFR_TEMPERATURE_PEAK	0xd6
+
 #define MAX34440_STATUS_OC_WARN		(1 << 0)
 #define MAX34440_STATUS_OC_FAULT	(1 << 1)
 #define MAX34440_STATUS_OT_FAULT	(1 << 5)
 #define MAX34440_STATUS_OT_WARN		(1 << 6)
 
+static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIRT_READ_VOUT_MAX:
+		ret = pmbus_read_word_data(client, page,
+					   MAX34440_MFR_VOUT_PEAK);
+		break;
+	case PMBUS_VIRT_READ_IOUT_MAX:
+		ret = pmbus_read_word_data(client, page,
+					   MAX34440_MFR_IOUT_PEAK);
+		break;
+	case PMBUS_VIRT_READ_TEMP_MAX:
+		ret = pmbus_read_word_data(client, page,
+					   MAX34440_MFR_TEMPERATURE_PEAK);
+		break;
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
+static int max34440_write_word_data(struct i2c_client *client, int page,
+				    int reg, u16 word)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+		ret = pmbus_write_word_data(client, page,
+					    MAX34440_MFR_VOUT_PEAK, 0);
+		break;
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+		ret = pmbus_write_word_data(client, page,
+					    MAX34440_MFR_IOUT_PEAK, 0);
+		break;
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = pmbus_write_word_data(client, page,
+					    MAX34440_MFR_TEMPERATURE_PEAK,
+					    0xffff);
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
 static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
 {
 	int ret;
@@ -72,10 +131,10 @@
 static struct pmbus_driver_info max34440_info[] = {
 	[max34440] = {
 		.pages = 14,
-		.direct[PSC_VOLTAGE_IN] = true,
-		.direct[PSC_VOLTAGE_OUT] = true,
-		.direct[PSC_TEMPERATURE] = true,
-		.direct[PSC_CURRENT_OUT] = true,
+		.format[PSC_VOLTAGE_IN] = direct,
+		.format[PSC_VOLTAGE_OUT] = direct,
+		.format[PSC_TEMPERATURE] = direct,
+		.format[PSC_CURRENT_OUT] = direct,
 		.m[PSC_VOLTAGE_IN] = 1,
 		.b[PSC_VOLTAGE_IN] = 0,
 		.R[PSC_VOLTAGE_IN] = 3,	    /* R = 0 in datasheet reflects mV */
@@ -109,14 +168,16 @@
 		.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
 		.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
 		.read_byte_data = max34440_read_byte_data,
+		.read_word_data = max34440_read_word_data,
+		.write_word_data = max34440_write_word_data,
 	},
 	[max34441] = {
 		.pages = 12,
-		.direct[PSC_VOLTAGE_IN] = true,
-		.direct[PSC_VOLTAGE_OUT] = true,
-		.direct[PSC_TEMPERATURE] = true,
-		.direct[PSC_CURRENT_OUT] = true,
-		.direct[PSC_FAN] = true,
+		.format[PSC_VOLTAGE_IN] = direct,
+		.format[PSC_VOLTAGE_OUT] = direct,
+		.format[PSC_TEMPERATURE] = direct,
+		.format[PSC_CURRENT_OUT] = direct,
+		.format[PSC_FAN] = direct,
 		.m[PSC_VOLTAGE_IN] = 1,
 		.b[PSC_VOLTAGE_IN] = 0,
 		.R[PSC_VOLTAGE_IN] = 3,
@@ -150,6 +211,8 @@
 		.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
 		.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
 		.read_byte_data = max34440_read_byte_data,
+		.read_word_data = max34440_read_word_data,
+		.write_word_data = max34440_write_word_data,
 	},
 };
 
diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/pmbus/max8688.c
similarity index 72%
rename from drivers/hwmon/max8688.c
rename to drivers/hwmon/pmbus/max8688.c
index 7fb93f4..c3e72f1 100644
--- a/drivers/hwmon/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -25,6 +25,9 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define MAX8688_MFR_VOUT_PEAK		0xd4
+#define MAX8688_MFR_IOUT_PEAK		0xd5
+#define MAX8688_MFR_TEMPERATURE_PEAK	0xd6
 #define MAX8688_MFG_STATUS		0xd8
 
 #define MAX8688_STATUS_OC_FAULT		(1 << 4)
@@ -37,6 +40,62 @@
 #define MAX8688_STATUS_OT_FAULT		(1 << 13)
 #define MAX8688_STATUS_OT_WARNING	(1 << 14)
 
+static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	int ret;
+
+	if (page)
+		return -EINVAL;
+
+	switch (reg) {
+	case PMBUS_VIRT_READ_VOUT_MAX:
+		ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK);
+		break;
+	case PMBUS_VIRT_READ_IOUT_MAX:
+		ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK);
+		break;
+	case PMBUS_VIRT_READ_TEMP_MAX:
+		ret = pmbus_read_word_data(client, 0,
+					   MAX8688_MFR_TEMPERATURE_PEAK);
+		break;
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
+static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
+				   u16 word)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
+					    0);
+		break;
+	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
+					    0);
+		break;
+	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+		ret = pmbus_write_word_data(client, 0,
+					    MAX8688_MFR_TEMPERATURE_PEAK,
+					    0xffff);
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+	return ret;
+}
+
 static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
 {
 	int ret = 0;
@@ -91,10 +150,10 @@
 
 static struct pmbus_driver_info max8688_info = {
 	.pages = 1,
-	.direct[PSC_VOLTAGE_IN] = true,
-	.direct[PSC_VOLTAGE_OUT] = true,
-	.direct[PSC_TEMPERATURE] = true,
-	.direct[PSC_CURRENT_OUT] = true,
+	.format[PSC_VOLTAGE_IN] = direct,
+	.format[PSC_VOLTAGE_OUT] = direct,
+	.format[PSC_TEMPERATURE] = direct,
+	.format[PSC_CURRENT_OUT] = direct,
 	.m[PSC_VOLTAGE_IN] = 19995,
 	.b[PSC_VOLTAGE_IN] = 0,
 	.R[PSC_VOLTAGE_IN] = -1,
@@ -111,6 +170,8 @@
 		| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
 		| PMBUS_HAVE_STATUS_TEMP,
 	.read_byte_data = max8688_read_byte_data,
+	.read_word_data = max8688_read_word_data,
+	.write_word_data = max8688_write_word_data,
 };
 
 static int max8688_probe(struct i2c_client *client,
diff --git a/drivers/hwmon/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
similarity index 89%
rename from drivers/hwmon/pmbus.c
rename to drivers/hwmon/pmbus/pmbus.c
index 9b1f0c3..73de9f1 100644
--- a/drivers/hwmon/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -96,6 +96,8 @@
 static int pmbus_identify(struct i2c_client *client,
 			  struct pmbus_driver_info *info)
 {
+	int ret = 0;
+
 	if (!info->pages) {
 		/*
 		 * Check if the PAGE command is supported. If it is,
@@ -117,6 +119,27 @@
 		}
 	}
 
+	if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
+		int vout_mode;
+
+		vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+		if (vout_mode >= 0 && vout_mode != 0xff) {
+			switch (vout_mode >> 5) {
+			case 0:
+				break;
+			case 1:
+				info->format[PSC_VOLTAGE_OUT] = vid;
+				break;
+			case 2:
+				info->format[PSC_VOLTAGE_OUT] = direct;
+				break;
+			default:
+				ret = -ENODEV;
+				goto abort;
+			}
+		}
+	}
+
 	/*
 	 * We should check if the COEFFICIENTS register is supported.
 	 * If it is, and the chip is configured for direct mode, we can read
@@ -125,13 +148,18 @@
 	 *
 	 * To do this, we will need access to a chip which actually supports the
 	 * COEFFICIENTS command, since the command is too complex to implement
-	 * without testing it.
+	 * without testing it. Until then, abort if a chip configured for direct
+	 * mode was detected.
 	 */
+	if (info->format[PSC_VOLTAGE_OUT] == direct) {
+		ret = -ENODEV;
+		goto abort;
+	}
 
 	/* Try to find sensor groups  */
 	pmbus_find_sensor_groups(client, info);
-
-	return 0;
+abort:
+	return ret;
 }
 
 static int pmbus_probe(struct i2c_client *client,
@@ -172,11 +200,14 @@
  * Use driver_data to set the number of pages supported by the chip.
  */
 static const struct i2c_device_id pmbus_id[] = {
+	{"adp4000", 1},
 	{"bmr450", 1},
 	{"bmr451", 1},
 	{"bmr453", 1},
 	{"bmr454", 1},
 	{"ltc2978", 8},
+	{"ncp4200", 1},
+	{"ncp4208", 1},
 	{"pmbus", 0},
 	{}
 };
diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
similarity index 79%
rename from drivers/hwmon/pmbus.h
rename to drivers/hwmon/pmbus/pmbus.h
index 50647ab..0808d98 100644
--- a/drivers/hwmon/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -126,6 +126,42 @@
 #define PMBUS_MFR_SERIAL		0x9E
 
 /*
+ * Virtual registers.
+ * Useful to support attributes which are not supported by standard PMBus
+ * registers but exist as manufacturer specific registers on individual chips.
+ * Must be mapped to real registers in device specific code.
+ *
+ * Semantics:
+ * Virtual registers are all word size.
+ * READ registers are read-only; writes are either ignored or return an error.
+ * RESET registers are read/write. Reading returns zero (used for detection),
+ * writing any value causes the associated history to be reset.
+ */
+#define PMBUS_VIRT_BASE			0x100
+#define PMBUS_VIRT_READ_TEMP_MIN	(PMBUS_VIRT_BASE + 0)
+#define PMBUS_VIRT_READ_TEMP_MAX	(PMBUS_VIRT_BASE + 1)
+#define PMBUS_VIRT_RESET_TEMP_HISTORY	(PMBUS_VIRT_BASE + 2)
+#define PMBUS_VIRT_READ_VIN_AVG		(PMBUS_VIRT_BASE + 3)
+#define PMBUS_VIRT_READ_VIN_MIN		(PMBUS_VIRT_BASE + 4)
+#define PMBUS_VIRT_READ_VIN_MAX		(PMBUS_VIRT_BASE + 5)
+#define PMBUS_VIRT_RESET_VIN_HISTORY	(PMBUS_VIRT_BASE + 6)
+#define PMBUS_VIRT_READ_IIN_AVG		(PMBUS_VIRT_BASE + 7)
+#define PMBUS_VIRT_READ_IIN_MIN		(PMBUS_VIRT_BASE + 8)
+#define PMBUS_VIRT_READ_IIN_MAX		(PMBUS_VIRT_BASE + 9)
+#define PMBUS_VIRT_RESET_IIN_HISTORY	(PMBUS_VIRT_BASE + 10)
+#define PMBUS_VIRT_READ_PIN_AVG		(PMBUS_VIRT_BASE + 11)
+#define PMBUS_VIRT_READ_PIN_MAX		(PMBUS_VIRT_BASE + 12)
+#define PMBUS_VIRT_RESET_PIN_HISTORY	(PMBUS_VIRT_BASE + 13)
+#define PMBUS_VIRT_READ_VOUT_AVG	(PMBUS_VIRT_BASE + 14)
+#define PMBUS_VIRT_READ_VOUT_MIN	(PMBUS_VIRT_BASE + 15)
+#define PMBUS_VIRT_READ_VOUT_MAX	(PMBUS_VIRT_BASE + 16)
+#define PMBUS_VIRT_RESET_VOUT_HISTORY	(PMBUS_VIRT_BASE + 17)
+#define PMBUS_VIRT_READ_IOUT_AVG	(PMBUS_VIRT_BASE + 18)
+#define PMBUS_VIRT_READ_IOUT_MIN	(PMBUS_VIRT_BASE + 19)
+#define PMBUS_VIRT_READ_IOUT_MAX	(PMBUS_VIRT_BASE + 20)
+#define PMBUS_VIRT_RESET_IOUT_HISTORY	(PMBUS_VIRT_BASE + 21)
+
+/*
  * CAPABILITY
  */
 #define PB_CAPABILITY_SMBALERT		(1<<4)
@@ -266,11 +302,11 @@
 #define PMBUS_HAVE_STATUS_FAN12	(1 << 16)
 #define PMBUS_HAVE_STATUS_FAN34	(1 << 17)
 
+enum pmbus_data_format { linear = 0, direct, vid };
+
 struct pmbus_driver_info {
 	int pages;		/* Total number of pages */
-	bool direct[PSC_NUM_CLASSES];
-				/* true if device uses direct data format
-				   for the given sensor class */
+	enum pmbus_data_format format[PSC_NUM_CLASSES];
 	/*
 	 * Support one set of coefficients for each sensor type
 	 * Used for chips providing data in direct mode.
@@ -286,6 +322,9 @@
 	 * necessary.
 	 */
 	int (*read_byte_data)(struct i2c_client *client, int page, int reg);
+	int (*read_word_data)(struct i2c_client *client, int page, int reg);
+	int (*write_word_data)(struct i2c_client *client, int page, int reg,
+			       u16 word);
 	/*
 	 * The identify function determines supported PMBus functionality.
 	 * This function is only necessary if a chip driver supports multiple
@@ -299,6 +338,9 @@
 
 int pmbus_set_page(struct i2c_client *client, u8 page);
 int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
 void pmbus_clear_faults(struct i2c_client *client);
 bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
 bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
similarity index 84%
rename from drivers/hwmon/pmbus_core.c
rename to drivers/hwmon/pmbus/pmbus_core.c
index 8e31a8e..5c1b6cf 100644
--- a/drivers/hwmon/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -33,14 +33,18 @@
 /*
  * Constants needed to determine number of sensors, booleans, and labels.
  */
-#define PMBUS_MAX_INPUT_SENSORS		11	/* 6*volt, 3*curr, 2*power */
-#define PMBUS_VOUT_SENSORS_PER_PAGE	5	/* input, min, max, lcrit,
-						   crit */
-#define PMBUS_IOUT_SENSORS_PER_PAGE	4	/* input, min, max, crit */
+#define PMBUS_MAX_INPUT_SENSORS		22	/* 10*volt, 7*curr, 5*power */
+#define PMBUS_VOUT_SENSORS_PER_PAGE	9	/* input, min, max, lcrit,
+						   crit, lowest, highest, avg,
+						   reset */
+#define PMBUS_IOUT_SENSORS_PER_PAGE	8	/* input, min, max, crit,
+						   lowest, highest, avg,
+						   reset */
 #define PMBUS_POUT_SENSORS_PER_PAGE	4	/* input, cap, max, crit */
 #define PMBUS_MAX_SENSORS_PER_FAN	1	/* input */
-#define PMBUS_MAX_SENSORS_PER_TEMP	5	/* input, min, max, lcrit,
-						   crit */
+#define PMBUS_MAX_SENSORS_PER_TEMP	8	/* input, min, max, lcrit,
+						   crit, lowest, highest,
+						   reset */
 
 #define PMBUS_MAX_INPUT_BOOLEANS	7	/* v: min_alarm, max_alarm,
 						   lcrit_alarm, crit_alarm;
@@ -74,11 +78,13 @@
 #define PB_STATUS_INPUT_BASE	(PB_STATUS_FAN34_BASE + PMBUS_PAGES)
 #define PB_STATUS_TEMP_BASE	(PB_STATUS_INPUT_BASE + 1)
 
+#define PMBUS_NAME_SIZE		24
+
 struct pmbus_sensor {
-	char name[I2C_NAME_SIZE];	/* sysfs sensor name */
+	char name[PMBUS_NAME_SIZE];	/* sysfs sensor name */
 	struct sensor_device_attribute attribute;
 	u8 page;		/* page number */
-	u8 reg;			/* register */
+	u16 reg;		/* register */
 	enum pmbus_sensor_classes class;	/* sensor class */
 	bool update;		/* runtime sensor update needed */
 	int data;		/* Sensor data.
@@ -86,14 +92,14 @@
 };
 
 struct pmbus_boolean {
-	char name[I2C_NAME_SIZE];	/* sysfs boolean name */
+	char name[PMBUS_NAME_SIZE];	/* sysfs boolean name */
 	struct sensor_device_attribute attribute;
 };
 
 struct pmbus_label {
-	char name[I2C_NAME_SIZE];	/* sysfs label name */
+	char name[PMBUS_NAME_SIZE];	/* sysfs label name */
 	struct sensor_device_attribute attribute;
-	char label[I2C_NAME_SIZE];	/* label */
+	char label[PMBUS_NAME_SIZE];	/* label */
 };
 
 struct pmbus_data {
@@ -162,19 +168,21 @@
 }
 EXPORT_SYMBOL_GPL(pmbus_set_page);
 
-static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value)
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
 {
 	int rv;
 
-	rv = pmbus_set_page(client, page);
-	if (rv < 0)
-		return rv;
+	if (page >= 0) {
+		rv = pmbus_set_page(client, page);
+		if (rv < 0)
+			return rv;
+	}
 
 	return i2c_smbus_write_byte(client, value);
 }
+EXPORT_SYMBOL_GPL(pmbus_write_byte);
 
-static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
-				 u16 word)
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
 {
 	int rv;
 
@@ -184,6 +192,28 @@
 
 	return i2c_smbus_write_word_data(client, reg, word);
 }
+EXPORT_SYMBOL_GPL(pmbus_write_word_data);
+
+/*
+ * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
+				  u16 word)
+{
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	const struct pmbus_driver_info *info = data->info;
+	int status;
+
+	if (info->write_word_data) {
+		status = info->write_word_data(client, page, reg, word);
+		if (status != -ENODATA)
+			return status;
+	}
+	if (reg >= PMBUS_VIRT_BASE)
+		return -EINVAL;
+	return pmbus_write_word_data(client, page, reg, word);
+}
 
 int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
 {
@@ -197,16 +227,57 @@
 }
 EXPORT_SYMBOL_GPL(pmbus_read_word_data);
 
-static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)
+/*
+ * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)
+{
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	const struct pmbus_driver_info *info = data->info;
+	int status;
+
+	if (info->read_word_data) {
+		status = info->read_word_data(client, page, reg);
+		if (status != -ENODATA)
+			return status;
+	}
+	if (reg >= PMBUS_VIRT_BASE)
+		return -EINVAL;
+	return pmbus_read_word_data(client, page, reg);
+}
+
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
 {
 	int rv;
 
-	rv = pmbus_set_page(client, page);
-	if (rv < 0)
-		return rv;
+	if (page >= 0) {
+		rv = pmbus_set_page(client, page);
+		if (rv < 0)
+			return rv;
+	}
 
 	return i2c_smbus_read_byte_data(client, reg);
 }
+EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	const struct pmbus_driver_info *info = data->info;
+	int status;
+
+	if (info->read_byte_data) {
+		status = info->read_byte_data(client, page, reg);
+		if (status != -ENODATA)
+			return status;
+	}
+	return pmbus_read_byte_data(client, page, reg);
+}
 
 static void pmbus_clear_fault_page(struct i2c_client *client, int page)
 {
@@ -223,13 +294,13 @@
 }
 EXPORT_SYMBOL_GPL(pmbus_clear_faults);
 
-static int pmbus_check_status_cml(struct i2c_client *client, int page)
+static int pmbus_check_status_cml(struct i2c_client *client)
 {
 	int status, status2;
 
-	status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
+	status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE);
 	if (status < 0 || (status & PB_STATUS_CML)) {
-		status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML);
+		status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
 		if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
 			return -EINVAL;
 	}
@@ -241,10 +312,10 @@
 	int rv;
 	struct pmbus_data *data = i2c_get_clientdata(client);
 
-	rv = pmbus_read_byte_data(client, page, reg);
+	rv = _pmbus_read_byte_data(client, page, reg);
 	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
-		rv = pmbus_check_status_cml(client, page);
-	pmbus_clear_fault_page(client, page);
+		rv = pmbus_check_status_cml(client);
+	pmbus_clear_fault_page(client, -1);
 	return rv >= 0;
 }
 EXPORT_SYMBOL_GPL(pmbus_check_byte_register);
@@ -254,10 +325,10 @@
 	int rv;
 	struct pmbus_data *data = i2c_get_clientdata(client);
 
-	rv = pmbus_read_word_data(client, page, reg);
+	rv = _pmbus_read_word_data(client, page, reg);
 	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
-		rv = pmbus_check_status_cml(client, page);
-	pmbus_clear_fault_page(client, page);
+		rv = pmbus_check_status_cml(client);
+	pmbus_clear_fault_page(client, -1);
 	return rv >= 0;
 }
 EXPORT_SYMBOL_GPL(pmbus_check_word_register);
@@ -270,24 +341,6 @@
 }
 EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
 
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping funcion exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
-	struct pmbus_data *data = i2c_get_clientdata(client);
-	const struct pmbus_driver_info *info = data->info;
-	int status;
-
-	if (info->read_byte_data) {
-		status = info->read_byte_data(client, page, reg);
-		if (status != -ENODATA)
-			return status;
-	}
-	return pmbus_read_byte_data(client, page, reg);
-}
-
 static struct pmbus_data *pmbus_update_device(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
@@ -347,8 +400,9 @@
 
 			if (!data->valid || sensor->update)
 				sensor->data
-				    = pmbus_read_word_data(client, sensor->page,
-							   sensor->reg);
+				    = _pmbus_read_word_data(client,
+							    sensor->page,
+							    sensor->reg);
 		}
 		pmbus_clear_faults(client);
 		data->last_updated = jiffies;
@@ -443,15 +497,37 @@
 	return (val - b) / m;
 }
 
+/*
+ * Convert VID sensor values to milli- or micro-units
+ * depending on sensor type.
+ * We currently only support VR11.
+ */
+static long pmbus_reg2data_vid(struct pmbus_data *data,
+			       struct pmbus_sensor *sensor)
+{
+	long val = sensor->data;
+
+	if (val < 0x02 || val > 0xb2)
+		return 0;
+	return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
+}
+
 static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
 {
 	long val;
 
-	if (data->info->direct[sensor->class])
+	switch (data->info->format[sensor->class]) {
+	case direct:
 		val = pmbus_reg2data_direct(data, sensor);
-	else
+		break;
+	case vid:
+		val = pmbus_reg2data_vid(data, sensor);
+		break;
+	case linear:
+	default:
 		val = pmbus_reg2data_linear(data, sensor);
-
+		break;
+	}
 	return val;
 }
 
@@ -561,16 +637,31 @@
 	return val;
 }
 
+static u16 pmbus_data2reg_vid(struct pmbus_data *data,
+			      enum pmbus_sensor_classes class, long val)
+{
+	val = SENSORS_LIMIT(val, 500, 1600);
+
+	return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625);
+}
+
 static u16 pmbus_data2reg(struct pmbus_data *data,
 			  enum pmbus_sensor_classes class, long val)
 {
 	u16 regval;
 
-	if (data->info->direct[class])
+	switch (data->info->format[class]) {
+	case direct:
 		regval = pmbus_data2reg_direct(data, class, val);
-	else
+		break;
+	case vid:
+		regval = pmbus_data2reg_vid(data, class, val);
+		break;
+	case linear:
+	default:
 		regval = pmbus_data2reg_linear(data, class, val);
-
+		break;
+	}
 	return regval;
 }
 
@@ -682,7 +773,7 @@
 
 	mutex_lock(&data->update_lock);
 	regval = pmbus_data2reg(data, sensor->class, val);
-	ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
+	ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
 	if (ret < 0)
 		rv = ret;
 	else
@@ -867,7 +958,8 @@
  * and its associated alarm attribute.
  */
 struct pmbus_limit_attr {
-	u8 reg;			/* Limit register */
+	u16 reg;		/* Limit register */
+	bool update;		/* True if register needs updates */
 	const char *attr;	/* Attribute name */
 	const char *alarm;	/* Alarm attribute name */
 	u32 sbit;		/* Alarm attribute status bit */
@@ -912,9 +1004,10 @@
 		if (pmbus_check_word_register(client, page, l->reg)) {
 			cindex = data->num_sensors;
 			pmbus_add_sensor(data, name, l->attr, index, page,
-					 l->reg, attr->class, attr->update,
+					 l->reg, attr->class,
+					 attr->update || l->update,
 					 false);
-			if (info->func[page] & attr->sfunc) {
+			if (l->sbit && (info->func[page] & attr->sfunc)) {
 				if (attr->compare) {
 					pmbus_add_boolean_cmp(data, name,
 						l->alarm, index,
@@ -953,9 +1046,11 @@
 						   index, page, cbase, attr);
 		/*
 		 * Add generic alarm attribute only if there are no individual
-		 * alarm attributes, and if there is a global alarm bit.
+		 * alarm attributes, if there is a global alarm bit, and if
+		 * the generic status register for this page is accessible.
 		 */
-		if (!have_alarm && attr->gbit)
+		if (!have_alarm && attr->gbit &&
+		    pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE))
 			pmbus_add_boolean_reg(data, name, "alarm", index,
 					      PB_STATUS_BASE + page,
 					      attr->gbit);
@@ -1008,6 +1103,21 @@
 		.attr = "crit",
 		.alarm = "crit_alarm",
 		.sbit = PB_VOLTAGE_OV_FAULT,
+	}, {
+		.reg = PMBUS_VIRT_READ_VIN_AVG,
+		.update = true,
+		.attr = "average",
+	}, {
+		.reg = PMBUS_VIRT_READ_VIN_MIN,
+		.update = true,
+		.attr = "lowest",
+	}, {
+		.reg = PMBUS_VIRT_READ_VIN_MAX,
+		.update = true,
+		.attr = "highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_VIN_HISTORY,
+		.attr = "reset_history",
 	},
 };
 
@@ -1032,6 +1142,21 @@
 		.attr = "crit",
 		.alarm = "crit_alarm",
 		.sbit = PB_VOLTAGE_OV_FAULT,
+	}, {
+		.reg = PMBUS_VIRT_READ_VOUT_AVG,
+		.update = true,
+		.attr = "average",
+	}, {
+		.reg = PMBUS_VIRT_READ_VOUT_MIN,
+		.update = true,
+		.attr = "lowest",
+	}, {
+		.reg = PMBUS_VIRT_READ_VOUT_MAX,
+		.update = true,
+		.attr = "highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
+		.attr = "reset_history",
 	}
 };
 
@@ -1078,6 +1203,21 @@
 		.attr = "crit",
 		.alarm = "crit_alarm",
 		.sbit = PB_IIN_OC_FAULT,
+	}, {
+		.reg = PMBUS_VIRT_READ_IIN_AVG,
+		.update = true,
+		.attr = "average",
+	}, {
+		.reg = PMBUS_VIRT_READ_IIN_MIN,
+		.update = true,
+		.attr = "lowest",
+	}, {
+		.reg = PMBUS_VIRT_READ_IIN_MAX,
+		.update = true,
+		.attr = "highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_IIN_HISTORY,
+		.attr = "reset_history",
 	}
 };
 
@@ -1097,6 +1237,21 @@
 		.attr = "crit",
 		.alarm = "crit_alarm",
 		.sbit = PB_IOUT_OC_FAULT,
+	}, {
+		.reg = PMBUS_VIRT_READ_IOUT_AVG,
+		.update = true,
+		.attr = "average",
+	}, {
+		.reg = PMBUS_VIRT_READ_IOUT_MIN,
+		.update = true,
+		.attr = "lowest",
+	}, {
+		.reg = PMBUS_VIRT_READ_IOUT_MAX,
+		.update = true,
+		.attr = "highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
+		.attr = "reset_history",
 	}
 };
 
@@ -1132,6 +1287,17 @@
 		.attr = "max",
 		.alarm = "alarm",
 		.sbit = PB_PIN_OP_WARNING,
+	}, {
+		.reg = PMBUS_VIRT_READ_PIN_AVG,
+		.update = true,
+		.attr = "average",
+	}, {
+		.reg = PMBUS_VIRT_READ_PIN_MAX,
+		.update = true,
+		.attr = "input_highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_PIN_HISTORY,
+		.attr = "reset_history",
 	}
 };
 
@@ -1200,6 +1366,39 @@
 		.attr = "crit",
 		.alarm = "crit_alarm",
 		.sbit = PB_TEMP_OT_FAULT,
+	}, {
+		.reg = PMBUS_VIRT_READ_TEMP_MIN,
+		.attr = "lowest",
+	}, {
+		.reg = PMBUS_VIRT_READ_TEMP_MAX,
+		.attr = "highest",
+	}, {
+		.reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
+		.attr = "reset_history",
+	}
+};
+
+static const struct pmbus_limit_attr temp_limit_attrs23[] = {
+	{
+		.reg = PMBUS_UT_WARN_LIMIT,
+		.attr = "min",
+		.alarm = "min_alarm",
+		.sbit = PB_TEMP_UT_WARNING,
+	}, {
+		.reg = PMBUS_UT_FAULT_LIMIT,
+		.attr = "lcrit",
+		.alarm = "lcrit_alarm",
+		.sbit = PB_TEMP_UT_FAULT,
+	}, {
+		.reg = PMBUS_OT_WARN_LIMIT,
+		.attr = "max",
+		.alarm = "max_alarm",
+		.sbit = PB_TEMP_OT_WARNING,
+	}, {
+		.reg = PMBUS_OT_FAULT_LIMIT,
+		.attr = "crit",
+		.alarm = "crit_alarm",
+		.sbit = PB_TEMP_OT_FAULT,
 	}
 };
 
@@ -1226,8 +1425,8 @@
 		.sfunc = PMBUS_HAVE_STATUS_TEMP,
 		.sbase = PB_STATUS_TEMP_BASE,
 		.gbit = PB_STATUS_TEMPERATURE,
-		.limit = temp_limit_attrs,
-		.nlimit = ARRAY_SIZE(temp_limit_attrs),
+		.limit = temp_limit_attrs23,
+		.nlimit = ARRAY_SIZE(temp_limit_attrs23),
 	}, {
 		.reg = PMBUS_READ_TEMPERATURE_3,
 		.class = PSC_TEMPERATURE,
@@ -1238,8 +1437,8 @@
 		.sfunc = PMBUS_HAVE_STATUS_TEMP,
 		.sbase = PB_STATUS_TEMP_BASE,
 		.gbit = PB_STATUS_TEMPERATURE,
-		.limit = temp_limit_attrs,
-		.nlimit = ARRAY_SIZE(temp_limit_attrs),
+		.limit = temp_limit_attrs23,
+		.nlimit = ARRAY_SIZE(temp_limit_attrs23),
 	}
 };
 
@@ -1380,7 +1579,7 @@
 		 */
 		switch (vout_mode >> 5) {
 		case 0:	/* linear mode      */
-			if (data->info->direct[PSC_VOLTAGE_OUT])
+			if (data->info->format[PSC_VOLTAGE_OUT] != linear)
 				return -ENODEV;
 
 			exponent = vout_mode & 0x1f;
@@ -1389,8 +1588,12 @@
 				exponent |= ~0x1f;
 			data->exponent = exponent;
 			break;
+		case 1: /* VID mode         */
+			if (data->info->format[PSC_VOLTAGE_OUT] != vid)
+				return -ENODEV;
+			break;
 		case 2:	/* direct mode      */
-			if (!data->info->direct[PSC_VOLTAGE_OUT])
+			if (data->info->format[PSC_VOLTAGE_OUT] != direct)
 				return -ENODEV;
 			break;
 		default:
@@ -1457,18 +1660,6 @@
 		ret = -EINVAL;
 		goto out_data;
 	}
-	/*
-	 * Bail out if more than one page was configured, but we can not
-	 * select the highest page. This is an indication that the wrong
-	 * chip type was selected. Better bail out now than keep
-	 * returning errors later on.
-	 */
-	if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) {
-		dev_err(&client->dev, "Failed to select page %d\n",
-			info->pages - 1);
-		ret = -EINVAL;
-		goto out_data;
-	}
 
 	ret = pmbus_identify_common(client, data);
 	if (ret < 0) {
diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
similarity index 100%
rename from drivers/hwmon/ucd9000.c
rename to drivers/hwmon/pmbus/ucd9000.c
diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
similarity index 100%
rename from drivers/hwmon/ucd9200.c
rename to drivers/hwmon/pmbus/ucd9200.c
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index 7d231cf..fe4104c 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -32,7 +32,7 @@
 #include <linux/sht15.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /* Commands */
 #define SHT15_MEASURE_TEMP		0x03
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 8abfa4a..ce1a32b 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -673,32 +673,33 @@
 	/* transfer not completed */
 	adap->pch_i2c_xfer_in_progress = true;
 
-	pmsg = &msgs[0];
-	pmsg->flags |= adap->pch_buff_mode_en;
-	status = pmsg->flags;
-	pch_dbg(adap,
-		"After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
-	/* calculate sub address length and message length */
-	/* these are applicable only for buffer mode */
-	subaddrlen = pmsg->buf[0];
-	/* calculate actual message length excluding
-	 * the sub address fields */
-	msglen = (pmsg->len) - (subaddrlen + 1);
-	if (status & (I2C_M_RD)) {
-		pch_dbg(adap, "invoking pch_i2c_readbytes\n");
-		ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
-				   (i == 0));
-	} else {
-		pch_dbg(adap, "invoking pch_i2c_writebytes\n");
-		ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num),
-				    (i == 0));
+	for (i = 0; i < num && ret >= 0; i++) {
+		pmsg = &msgs[i];
+		pmsg->flags |= adap->pch_buff_mode_en;
+		status = pmsg->flags;
+		pch_dbg(adap,
+			"After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
+		/* calculate sub address length and message length */
+		/* these are applicable only for buffer mode */
+		subaddrlen = pmsg->buf[0];
+		/* calculate actual message length excluding
+		 * the sub address fields */
+		msglen = (pmsg->len) - (subaddrlen + 1);
+
+		if ((status & (I2C_M_RD)) != false) {
+			ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
+						(i == 0));
+		} else {
+			ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num),
+						 (i == 0));
+		}
 	}
 
 	adap->pch_i2c_xfer_in_progress = false;	/* transfer completed */
 
 	mutex_unlock(&pch_mutex);
 
-	return ret;
+	return (ret < 0) ? ret : num;
 }
 
 /**
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index fb3b4f8..2440b74 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/i2c-tegra.h>
+#include <linux/of_i2c.h>
 
 #include <asm/unaligned.h>
 
@@ -546,6 +547,7 @@
 	struct resource *iomem;
 	struct clk *clk;
 	struct clk *i2c_clk;
+	const unsigned int *prop;
 	void *base;
 	int irq;
 	int ret = 0;
@@ -603,7 +605,17 @@
 	i2c_dev->irq = irq;
 	i2c_dev->cont_id = pdev->id;
 	i2c_dev->dev = &pdev->dev;
-	i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000;
+
+	i2c_dev->bus_clk_rate = 100000; /* default clock rate */
+	if (pdata) {
+		i2c_dev->bus_clk_rate = pdata->bus_clk_rate;
+
+	} else if (i2c_dev->dev->of_node) {    /* if there is a device tree node ... */
+		prop = of_get_property(i2c_dev->dev->of_node,
+				"clock-frequency", NULL);
+		if (prop)
+			i2c_dev->bus_clk_rate = be32_to_cpup(prop);
+	}
 
 	if (pdev->id == 3)
 		i2c_dev->is_dvc = 1;
@@ -633,6 +645,7 @@
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
 	i2c_dev->adapter.dev.parent = &pdev->dev;
 	i2c_dev->adapter.nr = pdev->id;
+	i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
 
 	ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
 	if (ret) {
@@ -640,6 +653,8 @@
 		goto err_free_irq;
 	}
 
+	of_i2c_register_devices(&i2c_dev->adapter);
+
 	return 0;
 err_free_irq:
 	free_irq(i2c_dev->irq, i2c_dev);
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 0347eed..40c8353 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -31,7 +31,7 @@
  */
 
 #include <rdma/ib_umem.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "iw_cxgb4.h"
 
diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h
index f09914c..54c0d23 100644
--- a/drivers/infiniband/hw/ehca/ehca_tools.h
+++ b/drivers/infiniband/hw/ehca/ehca_tools.h
@@ -58,7 +58,7 @@
 #include <linux/cpu.h>
 #include <linux/device.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/abs_addr.h>
 #include <asm/ibmebus.h>
 #include <asm/io.h>
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 73bc184..c118663 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -34,7 +34,7 @@
 
 #define TCPOPT_TIMESTAMP 8
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 7b6985a..b3cc1e0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -45,7 +45,7 @@
 
 #include <net/neighbour.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_pack.h>
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 95a08a8..5745b7f 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -271,7 +271,7 @@
 	unsigned long edtl;
 	int err;
 	struct iser_data_buf *data_buf;
-	struct iscsi_cmd *hdr =  (struct iscsi_cmd *)task->hdr;
+	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
 	struct scsi_cmnd *sc  =  task->sc;
 	struct iser_tx_desc *tx_desc = &iser_task->desc;
 
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 7d5109b..0bfa545 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -39,7 +39,7 @@
 #include <linux/random.h>
 #include <linux/jiffies.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 56abf3d..d728875 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -154,10 +154,13 @@
 	{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
 	{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
 	{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
 	{ 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
 	{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
 	{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
 	{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
 	{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
 	{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
 	{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
@@ -236,9 +239,10 @@
 	XPAD_XBOX360_VENDOR(0x046d),		/* Logitech X-Box 360 style controllers */
 	XPAD_XBOX360_VENDOR(0x0738),		/* Mad Catz X-Box 360 controllers */
 	XPAD_XBOX360_VENDOR(0x0e6f),		/* 0x0e6f X-Box 360 controllers */
+	XPAD_XBOX360_VENDOR(0x12ab),		/* X-Box 360 dance pads */
 	XPAD_XBOX360_VENDOR(0x1430),		/* RedOctane X-Box 360 controllers */
 	XPAD_XBOX360_VENDOR(0x146b),		/* BigBen Interactive Controllers */
-	XPAD_XBOX360_VENDOR(0x1bad),		/* Rock Band Drums */
+	XPAD_XBOX360_VENDOR(0x1bad),		/* Harminix Rock Band Guitar and Drums */
 	XPAD_XBOX360_VENDOR(0x0f0d),            /* Hori Controllers */
 	{ }
 };
@@ -545,7 +549,7 @@
 	struct usb_endpoint_descriptor *ep_irq_out;
 	int error;
 
-	if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
+	if (xpad->xtype == XTYPE_UNKNOWN)
 		return 0;
 
 	xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
@@ -579,13 +583,13 @@
 
 static void xpad_stop_output(struct usb_xpad *xpad)
 {
-	if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX)
+	if (xpad->xtype != XTYPE_UNKNOWN)
 		usb_kill_urb(xpad->irq_out);
 }
 
 static void xpad_deinit_output(struct usb_xpad *xpad)
 {
-	if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX) {
+	if (xpad->xtype != XTYPE_UNKNOWN) {
 		usb_free_urb(xpad->irq_out);
 		usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
 				xpad->odata, xpad->odata_dma);
@@ -632,6 +636,23 @@
 
 			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
 
+		case XTYPE_XBOX360W:
+			xpad->odata[0] = 0x00;
+			xpad->odata[1] = 0x01;
+			xpad->odata[2] = 0x0F;
+			xpad->odata[3] = 0xC0;
+			xpad->odata[4] = 0x00;
+			xpad->odata[5] = strong / 256;
+			xpad->odata[6] = weak / 256;
+			xpad->odata[7] = 0x00;
+			xpad->odata[8] = 0x00;
+			xpad->odata[9] = 0x00;
+			xpad->odata[10] = 0x00;
+			xpad->odata[11] = 0x00;
+			xpad->irq_out->transfer_buffer_length = 12;
+
+			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
 		default:
 			dbg("%s - rumble command sent to unsupported xpad type: %d",
 				__func__, xpad->xtype);
@@ -644,7 +665,7 @@
 
 static int xpad_init_ff(struct usb_xpad *xpad)
 {
-	if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
+	if (xpad->xtype == XTYPE_UNKNOWN)
 		return 0;
 
 	input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index af45d27..7b404e5 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -9,7 +9,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 6315986..c770826 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -8,7 +8,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 11478eb..19cfc0c 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -1578,14 +1578,14 @@
 	atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
 	atkbd_platform_fixup_data = id->driver_data;
 
-	return 0;
+	return 1;
 }
 
 static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
 {
 	atkbd_platform_scancode_fixup = id->driver_data;
 
-	return 0;
+	return 1;
 }
 
 static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 6e6145b..ce281d1 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -2,6 +2,7 @@
  * Driver for keys on GPIO lines capable of generating interrupts.
  *
  * Copyright 2005 Phil Blundell
+ * Copyright 2010, 2011 David Jander <david@protonic.nl>
  *
  * 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
@@ -25,6 +26,8 @@
 #include <linux/gpio_keys.h>
 #include <linux/workqueue.h>
 #include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
 
 struct gpio_button_data {
 	struct gpio_keys_button *button;
@@ -415,7 +418,7 @@
 	if (!button->can_disable)
 		irqflags |= IRQF_SHARED;
 
-	error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
+	error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata);
 	if (error < 0) {
 		dev_err(dev, "Unable to claim irq %d; error %d\n",
 			irq, error);
@@ -445,15 +448,120 @@
 		ddata->disable(input->dev.parent);
 }
 
+/*
+ * Handlers for alternative sources of platform_data
+ */
+#ifdef CONFIG_OF
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static int gpio_keys_get_devtree_pdata(struct device *dev,
+			    struct gpio_keys_platform_data *pdata)
+{
+	struct device_node *node, *pp;
+	int i;
+	struct gpio_keys_button *buttons;
+	const u32 *reg;
+	int len;
+
+	node = dev->of_node;
+	if (node == NULL)
+		return -ENODEV;
+
+	memset(pdata, 0, sizeof *pdata);
+
+	pdata->rep = !!of_get_property(node, "autorepeat", &len);
+
+	/* First count the subnodes */
+	pdata->nbuttons = 0;
+	pp = NULL;
+	while ((pp = of_get_next_child(node, pp)))
+		pdata->nbuttons++;
+
+	if (pdata->nbuttons == 0)
+		return -ENODEV;
+
+	buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL);
+	if (!buttons)
+		return -ENODEV;
+
+	pp = NULL;
+	i = 0;
+	while ((pp = of_get_next_child(node, pp))) {
+		enum of_gpio_flags flags;
+
+		if (!of_find_property(pp, "gpios", NULL)) {
+			pdata->nbuttons--;
+			dev_warn(dev, "Found button without gpios\n");
+			continue;
+		}
+		buttons[i].gpio = of_get_gpio_flags(pp, 0, &flags);
+		buttons[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+		reg = of_get_property(pp, "linux,code", &len);
+		if (!reg) {
+			dev_err(dev, "Button without keycode: 0x%x\n", buttons[i].gpio);
+			goto out_fail;
+		}
+		buttons[i].code = be32_to_cpup(reg);
+
+		buttons[i].desc = of_get_property(pp, "label", &len);
+
+		reg = of_get_property(pp, "linux,input-type", &len);
+		buttons[i].type = reg ? be32_to_cpup(reg) : EV_KEY;
+
+		buttons[i].wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
+
+		reg = of_get_property(pp, "debounce-interval", &len);
+		buttons[i].debounce_interval = reg ? be32_to_cpup(reg) : 5;
+
+		i++;
+	}
+
+	pdata->buttons = buttons;
+
+	return 0;
+
+out_fail:
+	kfree(buttons);
+	return -ENODEV;
+}
+
+static struct of_device_id gpio_keys_of_match[] = {
+	{ .compatible = "gpio-keys", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
+
+#else
+
+static int gpio_keys_get_devtree_pdata(struct device *dev,
+			    struct gpio_keys_platform_data *altp)
+{
+	return -ENODEV;
+}
+
+#define gpio_keys_of_match NULL
+
+#endif
+
 static int __devinit gpio_keys_probe(struct platform_device *pdev)
 {
 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
 	struct gpio_keys_drvdata *ddata;
 	struct device *dev = &pdev->dev;
+	struct gpio_keys_platform_data alt_pdata;
 	struct input_dev *input;
 	int i, error;
 	int wakeup = 0;
 
+	if (!pdata) {
+		error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
+		if (error)
+			return error;
+		pdata = &alt_pdata;
+	}
+
 	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
 			pdata->nbuttons * sizeof(struct gpio_button_data),
 			GFP_KERNEL);
@@ -544,13 +652,15 @@
  fail1:
 	input_free_device(input);
 	kfree(ddata);
+	/* If we have no platform_data, we allocated buttons dynamically. */
+	if (!pdev->dev.platform_data)
+		kfree(pdata->buttons);
 
 	return error;
 }
 
 static int __devexit gpio_keys_remove(struct platform_device *pdev)
 {
-	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
 	struct input_dev *input = ddata->input;
 	int i;
@@ -559,31 +669,39 @@
 
 	device_init_wakeup(&pdev->dev, 0);
 
-	for (i = 0; i < pdata->nbuttons; i++) {
-		int irq = gpio_to_irq(pdata->buttons[i].gpio);
+	for (i = 0; i < ddata->n_buttons; i++) {
+		int irq = gpio_to_irq(ddata->data[i].button->gpio);
 		free_irq(irq, &ddata->data[i]);
 		if (ddata->data[i].timer_debounce)
 			del_timer_sync(&ddata->data[i].timer);
 		cancel_work_sync(&ddata->data[i].work);
-		gpio_free(pdata->buttons[i].gpio);
+		gpio_free(ddata->data[i].button->gpio);
 	}
 
 	input_unregister_device(input);
 
+	/*
+	 * If we had no platform_data, we allocated buttons dynamically, and
+	 * must free them here. ddata->data[0].button is the pointer to the
+	 * beginning of the allocated array.
+	 */
+	if (!pdev->dev.platform_data)
+		kfree(ddata->data[0].button);
+
+	kfree(ddata);
+
 	return 0;
 }
 
-
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int gpio_keys_suspend(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
 	int i;
 
-	if (device_may_wakeup(&pdev->dev)) {
-		for (i = 0; i < pdata->nbuttons; i++) {
-			struct gpio_keys_button *button = &pdata->buttons[i];
+	if (device_may_wakeup(dev)) {
+		for (i = 0; i < ddata->n_buttons; i++) {
+			struct gpio_keys_button *button = ddata->data[i].button;
 			if (button->wakeup) {
 				int irq = gpio_to_irq(button->gpio);
 				enable_irq_wake(irq);
@@ -596,15 +714,13 @@
 
 static int gpio_keys_resume(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
-	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
 	int i;
 
-	for (i = 0; i < pdata->nbuttons; i++) {
+	for (i = 0; i < ddata->n_buttons; i++) {
 
-		struct gpio_keys_button *button = &pdata->buttons[i];
-		if (button->wakeup && device_may_wakeup(&pdev->dev)) {
+		struct gpio_keys_button *button = ddata->data[i].button;
+		if (button->wakeup && device_may_wakeup(dev)) {
 			int irq = gpio_to_irq(button->gpio);
 			disable_irq_wake(irq);
 		}
@@ -615,22 +731,18 @@
 
 	return 0;
 }
-
-static const struct dev_pm_ops gpio_keys_pm_ops = {
-	.suspend	= gpio_keys_suspend,
-	.resume		= gpio_keys_resume,
-};
 #endif
 
+static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+
 static struct platform_driver gpio_keys_device_driver = {
 	.probe		= gpio_keys_probe,
 	.remove		= __devexit_p(gpio_keys_remove),
 	.driver		= {
 		.name	= "gpio-keys",
 		.owner	= THIS_MODULE,
-#ifdef CONFIG_PM
 		.pm	= &gpio_keys_pm_ops,
-#endif
+		.of_match_table = gpio_keys_of_match,
 	}
 };
 
@@ -644,10 +756,10 @@
 	platform_driver_unregister(&gpio_keys_device_driver);
 }
 
-module_init(gpio_keys_init);
+late_initcall(gpio_keys_init);
 module_exit(gpio_keys_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
-MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
+MODULE_DESCRIPTION("Keyboard driver for GPIOs");
 MODULE_ALIAS("platform:gpio-keys");
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
index 71f744a8..ab0acaf 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -146,7 +146,6 @@
 	/* device lock */
 	struct mutex		lock;
 	struct i2c_client	*client;
-	struct work_struct	work;
 	struct input_dev	*idev;
 	bool			kp_enabled;
 	bool			pm_suspend;
@@ -162,7 +161,6 @@
 
 #define client_to_lm8323(c)	container_of(c, struct lm8323_chip, client)
 #define dev_to_lm8323(d)	container_of(d, struct lm8323_chip, client->dev)
-#define work_to_lm8323(w)	container_of(w, struct lm8323_chip, work)
 #define cdev_to_pwm(c)		container_of(c, struct lm8323_pwm, cdev)
 #define work_to_pwm(w)		container_of(w, struct lm8323_pwm, work)
 
@@ -375,9 +373,9 @@
  * Bottom half: handle the interrupt by posting key events, or dealing with
  * errors appropriately.
  */
-static void lm8323_work(struct work_struct *work)
+static irqreturn_t lm8323_irq(int irq, void *_lm)
 {
-	struct lm8323_chip *lm = work_to_lm8323(work);
+	struct lm8323_chip *lm = _lm;
 	u8 ints;
 	int i;
 
@@ -409,16 +407,6 @@
 	}
 
 	mutex_unlock(&lm->lock);
-}
-
-/*
- * We cannot use I2C in interrupt context, so we just schedule work.
- */
-static irqreturn_t lm8323_irq(int irq, void *data)
-{
-	struct lm8323_chip *lm = data;
-
-	schedule_work(&lm->work);
 
 	return IRQ_HANDLED;
 }
@@ -675,7 +663,6 @@
 	lm->client = client;
 	lm->idev = idev;
 	mutex_init(&lm->lock);
-	INIT_WORK(&lm->work, lm8323_work);
 
 	lm->size_x = pdata->size_x;
 	lm->size_y = pdata->size_y;
@@ -746,9 +733,8 @@
 		goto fail3;
 	}
 
-	err = request_irq(client->irq, lm8323_irq,
-			  IRQF_TRIGGER_FALLING | IRQF_DISABLED,
-			  "lm8323", lm);
+	err = request_threaded_irq(client->irq, NULL, lm8323_irq,
+			  IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm);
 	if (err) {
 		dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
 		goto fail4;
@@ -783,7 +769,6 @@
 
 	disable_irq_wake(client->irq);
 	free_irq(client->irq, lm);
-	cancel_work_sync(&lm->work);
 
 	input_unregister_device(lm->idev);
 
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index 0a9e811..1c1615d 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -43,14 +43,15 @@
  * enabled capacitance sensing inputs and its run/suspend mode.
  */
 #define ELECTRODE_CONF_ADDR		0x5e
+#define ELECTRODE_CONF_QUICK_CHARGE	0x80
 #define AUTO_CONFIG_CTRL_ADDR		0x7b
 #define AUTO_CONFIG_USL_ADDR		0x7d
 #define AUTO_CONFIG_LSL_ADDR		0x7e
 #define AUTO_CONFIG_TL_ADDR		0x7f
 
 /* Threshold of touch/release trigger */
-#define TOUCH_THRESHOLD			0x0f
-#define RELEASE_THRESHOLD		0x0a
+#define TOUCH_THRESHOLD			0x08
+#define RELEASE_THRESHOLD		0x05
 /* Masks for touch and release triggers */
 #define TOUCH_STATUS_MASK		0xfff
 /* MPR121 has 12 keys */
@@ -127,7 +128,7 @@
 				      struct i2c_client *client)
 {
 	const struct mpr121_init_register *reg;
-	unsigned char usl, lsl, tl;
+	unsigned char usl, lsl, tl, eleconf;
 	int i, t, vdd, ret;
 
 	/* Set up touch/release threshold for ele0-ele11 */
@@ -163,8 +164,15 @@
 	ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
 	ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
 	ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
+
+	/*
+	 * Quick charge bit will let the capacitive charge to ready
+	 * state quickly, or the buttons may not function after system
+	 * boot.
+	 */
+	eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE;
 	ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
-					 mpr121->keycount);
+					 eleconf);
 	if (ret != 0)
 		goto err_i2c_write;
 
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 6229c3e..e7cc51d 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -700,9 +700,9 @@
 	return 0;
 
 err_pmic_reg_read:
-	free_irq(kp->key_stuck_irq, NULL);
+	free_irq(kp->key_stuck_irq, kp);
 err_req_stuck_irq:
-	free_irq(kp->key_sense_irq, NULL);
+	free_irq(kp->key_sense_irq, kp);
 err_gpio_config:
 err_get_irq:
 	input_free_device(kp->input);
@@ -717,8 +717,8 @@
 	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
 
 	device_init_wakeup(&pdev->dev, 0);
-	free_irq(kp->key_stuck_irq, NULL);
-	free_irq(kp->key_sense_irq, NULL);
+	free_irq(kp->key_stuck_irq, kp);
+	free_irq(kp->key_sense_irq, kp);
 	input_unregister_device(kp->input);
 	kfree(kp);
 
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index ca7b891..b21bf5b 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -239,8 +239,6 @@
 	input_unregister_device(data->input);
 	kfree(data);
 
-	i2c_set_clientdata(client, NULL);
-
 	return 0;
 }
 
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 6876700..934aeb583 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -291,7 +291,7 @@
 	return 0;
 }
 
-#if CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
 static int sh_keysc_suspend(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 2b3b73e..da3828f 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -657,7 +657,7 @@
 
 	input_set_drvdata(input_dev, kbc);
 
-	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
 	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 
 	input_dev->keycode = kbc->keycode;
diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
index c8f097a..1c58681 100644
--- a/drivers/input/keyboard/tnetv107x-keypad.c
+++ b/drivers/input/keyboard/tnetv107x-keypad.c
@@ -337,5 +337,5 @@
 
 MODULE_AUTHOR("Cyril Chemparathy");
 MODULE_DESCRIPTION("TNETV107X Keypad Driver");
-MODULE_ALIAS("platform: tnetv107x-keypad");
+MODULE_ALIAS("platform:tnetv107x-keypad");
 MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 45dc6aa..c9104bb 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -100,6 +100,27 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called max8925_onkey.
 
+config INPUT_MMA8450
+	tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
+	depends on I2C
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want to support Freescale's MMA8450 Accelerometer
+	  through I2C interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mma8450.
+
+config INPUT_MPU3050
+	tristate "MPU3050 Triaxial gyroscope sensor"
+	depends on I2C
+	help
+	  Say Y here if you want to support InvenSense MPU3050
+	  connected via an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mpu3050.
+
 config INPUT_APANEL
 	tristate "Fujitsu Lifebook Application Panel buttons"
 	depends on X86 && I2C && LEDS_CLASS
@@ -209,6 +230,23 @@
 	  To compile this driver as a module, choose M here: the module will
 	  be called keyspan_remote.
 
+config INPUT_KXTJ9
+	tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+	depends on I2C
+	help
+	  Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
+	  accelerometer.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called kxtj9.
+
+config INPUT_KXTJ9_POLLED_MODE
+	bool "Enable polling mode support"
+	depends on INPUT_KXTJ9
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you need accelerometer to work in polling mode.
+
 config INPUT_POWERMATE
 	tristate "Griffin PowerMate and Contour Jog support"
 	depends on USB_ARCH_HAS_HCD
@@ -267,7 +305,7 @@
 config INPUT_TWL4030_VIBRA
 	tristate "Support for TWL4030 Vibrator"
 	depends on TWL4030_CORE
-	select TWL4030_CODEC
+	select MFD_TWL4030_AUDIO
 	select INPUT_FF_MEMLESS
 	help
 	  This option enables support for TWL4030 Vibrator Driver.
@@ -275,6 +313,17 @@
 	  To compile this driver as a module, choose M here. The module will
 	  be called twl4030_vibra.
 
+config INPUT_TWL6040_VIBRA
+	tristate "Support for TWL6040 Vibrator"
+	depends on TWL4030_CORE
+	select TWL6040_CORE
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables support for TWL6040 Vibrator Driver.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called twl6040_vibra.
+
 config INPUT_UINPUT
 	tristate "User level driver support"
 	help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 38efb2c..299ad5e 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,8 +25,11 @@
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
+obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)	+= max8925_onkey.o
+obj-$(CONFIG_INPUT_MMA8450)		+= mma8450.o
+obj-$(CONFIG_INPUT_MPU3050)		+= mpu3050.o
 obj-$(CONFIG_INPUT_PCAP)		+= pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)		+= pcf8574_keypad.o
@@ -40,9 +43,9 @@
 obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
+obj-$(CONFIG_INPUT_TWL6040_VIBRA)	+= twl6040-vibra.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
-
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
index 4f72bdd..d00edc9 100644
--- a/drivers/input/misc/bfin_rotary.c
+++ b/drivers/input/misc/bfin_rotary.c
@@ -6,7 +6,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
new file mode 100644
index 0000000..c456f63
--- /dev/null
+++ b/drivers/input/misc/kxtj9.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input/kxtj9.h>
+#include <linux/input-polldev.h>
+
+#define NAME			"kxtj9"
+#define G_MAX			8000
+/* OUTPUT REGISTERS */
+#define XOUT_L			0x06
+#define WHO_AM_I		0x0F
+/* CONTROL REGISTERS */
+#define INT_REL			0x1A
+#define CTRL_REG1		0x1B
+#define INT_CTRL1		0x1E
+#define DATA_CTRL		0x21
+/* CONTROL REGISTER 1 BITS */
+#define PC1_OFF			0x7F
+#define PC1_ON			(1 << 7)
+/* Data ready funtion enable bit: set during probe if using irq mode */
+#define DRDYE			(1 << 5)
+/* INTERRUPT CONTROL REGISTER 1 BITS */
+/* Set these during probe if using irq mode */
+#define KXTJ9_IEL		(1 << 3)
+#define KXTJ9_IEA		(1 << 4)
+#define KXTJ9_IEN		(1 << 5)
+/* INPUT_ABS CONSTANTS */
+#define FUZZ			3
+#define FLAT			3
+/* RESUME STATE INDICES */
+#define RES_DATA_CTRL		0
+#define RES_CTRL_REG1		1
+#define RES_INT_CTRL1		2
+#define RESUME_ENTRIES		3
+
+/*
+ * The following table lists the maximum appropriate poll interval for each
+ * available output data rate.
+ */
+static const struct {
+	unsigned int cutoff;
+	u8 mask;
+} kxtj9_odr_table[] = {
+	{ 3,	ODR800F },
+	{ 5,	ODR400F },
+	{ 10,	ODR200F },
+	{ 20,	ODR100F },
+	{ 40,	ODR50F  },
+	{ 80,	ODR25F  },
+	{ 0,	ODR12_5F},
+};
+
+struct kxtj9_data {
+	struct i2c_client *client;
+	struct kxtj9_platform_data pdata;
+	struct input_dev *input_dev;
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+	struct input_polled_dev *poll_dev;
+#endif
+	unsigned int last_poll_interval;
+	u8 shift;
+	u8 ctrl_reg1;
+	u8 data_ctrl;
+	u8 int_ctrl;
+};
+
+static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = tj9->client->addr,
+			.flags = tj9->client->flags,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = tj9->client->addr,
+			.flags = tj9->client->flags | I2C_M_RD,
+			.len = len,
+			.buf = data,
+		},
+	};
+
+	return i2c_transfer(tj9->client->adapter, msgs, 2);
+}
+
+static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
+{
+	s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+	s16 x, y, z;
+	int err;
+
+	err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
+	if (err < 0)
+		dev_err(&tj9->client->dev, "accelerometer data read failed\n");
+
+	x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift;
+	y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift;
+	z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift;
+
+	input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
+	input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
+	input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
+	input_sync(tj9->input_dev);
+}
+
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+	struct kxtj9_data *tj9 = dev;
+	int err;
+
+	/* data ready is the only possible interrupt type */
+	kxtj9_report_acceleration_data(tj9);
+
+	err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+	if (err < 0)
+		dev_err(&tj9->client->dev,
+			"error clearing interrupt status: %d\n", err);
+
+	return IRQ_HANDLED;
+}
+
+static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
+{
+	switch (new_g_range) {
+	case KXTJ9_G_2G:
+		tj9->shift = 4;
+		break;
+	case KXTJ9_G_4G:
+		tj9->shift = 3;
+		break;
+	case KXTJ9_G_8G:
+		tj9->shift = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tj9->ctrl_reg1 &= 0xe7;
+	tj9->ctrl_reg1 |= new_g_range;
+
+	return 0;
+}
+
+static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
+{
+	int err;
+	int i;
+
+	/* Use the lowest ODR that can support the requested poll interval */
+	for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
+		tj9->data_ctrl = kxtj9_odr_table[i].mask;
+		if (poll_interval < kxtj9_odr_table[i].cutoff)
+			break;
+	}
+
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+	if (err < 0)
+		return err;
+
+	err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
+	if (err < 0)
+		return err;
+
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+	if (tj9->pdata.power_on)
+		return tj9->pdata.power_on();
+
+	return 0;
+}
+
+static void kxtj9_device_power_off(struct kxtj9_data *tj9)
+{
+	int err;
+
+	tj9->ctrl_reg1 &= PC1_OFF;
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		dev_err(&tj9->client->dev, "soft power off failed\n");
+
+	if (tj9->pdata.power_off)
+		tj9->pdata.power_off();
+}
+
+static int kxtj9_enable(struct kxtj9_data *tj9)
+{
+	int err;
+
+	err = kxtj9_device_power_on(tj9);
+	if (err < 0)
+		return err;
+
+	/* ensure that PC1 is cleared before updating control registers */
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+	if (err < 0)
+		return err;
+
+	/* only write INT_CTRL_REG1 if in irq mode */
+	if (tj9->client->irq) {
+		err = i2c_smbus_write_byte_data(tj9->client,
+						INT_CTRL1, tj9->int_ctrl);
+		if (err < 0)
+			return err;
+	}
+
+	err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
+	if (err < 0)
+		return err;
+
+	/* turn on outputs */
+	tj9->ctrl_reg1 |= PC1_ON;
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		return err;
+
+	err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
+	if (err < 0)
+		return err;
+
+	/* clear initial interrupt if in irq mode */
+	if (tj9->client->irq) {
+		err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+		if (err < 0) {
+			dev_err(&tj9->client->dev,
+				"error clearing interrupt: %d\n", err);
+			goto fail;
+		}
+	}
+
+	return 0;
+
+fail:
+	kxtj9_device_power_off(tj9);
+	return err;
+}
+
+static void kxtj9_disable(struct kxtj9_data *tj9)
+{
+	kxtj9_device_power_off(tj9);
+}
+
+static int kxtj9_input_open(struct input_dev *input)
+{
+	struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+	return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+	struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+	kxtj9_disable(tj9);
+}
+
+static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
+					      struct input_dev *input_dev)
+{
+	__set_bit(EV_ABS, input_dev->evbit);
+	input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+	input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+	input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+	input_dev->name = "kxtj9_accel";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &tj9->client->dev;
+}
+
+static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&tj9->client->dev, "input device allocate failed\n");
+		return -ENOMEM;
+	}
+
+	tj9->input_dev = input_dev;
+
+	input_dev->open = kxtj9_input_open;
+	input_dev->close = kxtj9_input_close;
+	input_set_drvdata(input_dev, tj9);
+
+	kxtj9_init_input_device(tj9, input_dev);
+
+	err = input_register_device(tj9->input_dev);
+	if (err) {
+		dev_err(&tj9->client->dev,
+			"unable to register input polled device %s: %d\n",
+			tj9->input_dev->name, err);
+		input_free_device(tj9->input_dev);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part.  For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument.  In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval.  The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+
+/* Returns currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* Allow users to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+	unsigned int interval;
+	int error;
+
+	error = kstrtouint(buf, 10, &interval);
+	if (error < 0)
+		return error;
+
+	/* Lock the device to prevent races with open/close (and itself) */
+	mutex_lock(&input_dev->mutex);
+
+	disable_irq(client->irq);
+
+	/*
+	 * Set current interval to the greater of the minimum interval or
+	 * the requested interval
+	 */
+	tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
+
+	kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+	enable_irq(client->irq);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+	&dev_attr_poll.attr,
+	NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+	.attrs = kxtj9_attributes
+};
+
+
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+static void kxtj9_poll(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+	unsigned int poll_interval = dev->poll_interval;
+
+	kxtj9_report_acceleration_data(tj9);
+
+	if (poll_interval != tj9->last_poll_interval) {
+		kxtj9_update_odr(tj9, poll_interval);
+		tj9->last_poll_interval = poll_interval;
+	}
+}
+
+static void kxtj9_polled_input_open(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+
+	kxtj9_enable(tj9);
+}
+
+static void kxtj9_polled_input_close(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+
+	kxtj9_disable(tj9);
+}
+
+static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+	int err;
+	struct input_polled_dev *poll_dev;
+	poll_dev = input_allocate_polled_device();
+
+	if (!poll_dev) {
+		dev_err(&tj9->client->dev,
+			"Failed to allocate polled device\n");
+		return -ENOMEM;
+	}
+
+	tj9->poll_dev = poll_dev;
+	tj9->input_dev = poll_dev->input;
+
+	poll_dev->private = tj9;
+	poll_dev->poll = kxtj9_poll;
+	poll_dev->open = kxtj9_polled_input_open;
+	poll_dev->close = kxtj9_polled_input_close;
+
+	kxtj9_init_input_device(tj9, poll_dev->input);
+
+	err = input_register_polled_device(poll_dev);
+	if (err) {
+		dev_err(&tj9->client->dev,
+			"Unable to register polled device, err=%d\n", err);
+		input_free_polled_device(poll_dev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __devexit kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+	input_unregister_polled_device(tj9->poll_dev);
+	input_free_polled_device(tj9->poll_dev);
+}
+
+#else
+
+static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+	return -ENOSYS;
+}
+
+static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+}
+
+#endif
+
+static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
+{
+	int retval;
+
+	retval = kxtj9_device_power_on(tj9);
+	if (retval < 0)
+		return retval;
+
+	retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
+	if (retval < 0) {
+		dev_err(&tj9->client->dev, "read err int source\n");
+		goto out;
+	}
+
+	retval = retval != 0x06 ? -EIO : 0;
+
+out:
+	kxtj9_device_power_off(tj9);
+	return retval;
+}
+
+static int __devinit kxtj9_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	const struct kxtj9_platform_data *pdata = client->dev.platform_data;
+	struct kxtj9_data *tj9;
+	int err;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "client is not i2c capable\n");
+		return -ENXIO;
+	}
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is NULL; exiting\n");
+		return -EINVAL;
+	}
+
+	tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+	if (!tj9) {
+		dev_err(&client->dev,
+			"failed to allocate memory for module data\n");
+		return -ENOMEM;
+	}
+
+	tj9->client = client;
+	tj9->pdata = *pdata;
+
+	if (pdata->init) {
+		err = pdata->init();
+		if (err < 0)
+			goto err_free_mem;
+	}
+
+	err = kxtj9_verify(tj9);
+	if (err < 0) {
+		dev_err(&client->dev, "device not recognized\n");
+		goto err_pdata_exit;
+	}
+
+	i2c_set_clientdata(client, tj9);
+
+	tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+	tj9->data_ctrl = tj9->pdata.data_odr_init;
+
+	if (client->irq) {
+		/* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+		tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+		tj9->ctrl_reg1 |= DRDYE;
+
+		err = kxtj9_setup_input_device(tj9);
+		if (err)
+			goto err_pdata_exit;
+
+		err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					   "kxtj9-irq", tj9);
+		if (err) {
+			dev_err(&client->dev, "request irq failed: %d\n", err);
+			goto err_destroy_input;
+		}
+
+		err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+		if (err) {
+			dev_err(&client->dev, "sysfs create failed: %d\n", err);
+			goto err_free_irq;
+		}
+
+	} else {
+		err = kxtj9_setup_polled_device(tj9);
+		if (err)
+			goto err_pdata_exit;
+	}
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, tj9);
+err_destroy_input:
+	input_unregister_device(tj9->input_dev);
+err_pdata_exit:
+	if (tj9->pdata.exit)
+		tj9->pdata.exit();
+err_free_mem:
+	kfree(tj9);
+	return err;
+}
+
+static int __devexit kxtj9_remove(struct i2c_client *client)
+{
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+	if (client->irq) {
+		sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+		free_irq(client->irq, tj9);
+		input_unregister_device(tj9->input_dev);
+	} else {
+		kxtj9_teardown_polled_device(tj9);
+	}
+
+	if (tj9->pdata.exit)
+		tj9->pdata.exit();
+
+	kfree(tj9);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kxtj9_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		kxtj9_disable(tj9);
+
+	mutex_unlock(&input_dev->mutex);
+	return 0;
+}
+
+static int kxtj9_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+	int retval = 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		kxtj9_enable(tj9);
+
+	mutex_unlock(&input_dev->mutex);
+	return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+
+static const struct i2c_device_id kxtj9_id[] = {
+	{ NAME, 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtj9_id);
+
+static struct i2c_driver kxtj9_driver = {
+	.driver = {
+		.name	= NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &kxtj9_pm_ops,
+	},
+	.probe		= kxtj9_probe,
+	.remove		= __devexit_p(kxtj9_remove),
+	.id_table	= kxtj9_id,
+};
+
+static int __init kxtj9_init(void)
+{
+	return i2c_add_driver(&kxtj9_driver);
+}
+module_init(kxtj9_init);
+
+static void __exit kxtj9_exit(void)
+{
+	i2c_del_driver(&kxtj9_driver);
+}
+module_exit(kxtj9_exit);
+
+MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
+MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
new file mode 100644
index 0000000..20f8f92
--- /dev/null
+++ b/drivers/input/misc/mma8450.c
@@ -0,0 +1,256 @@
+/*
+ *  Driver for Freescale's 3-Axis Accelerometer MMA8450
+ *
+ *  Copyright (C) 2011 Freescale Semiconductor, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+
+#define MMA8450_DRV_NAME	"mma8450"
+
+#define MODE_CHANGE_DELAY_MS	100
+#define POLL_INTERVAL		100
+#define POLL_INTERVAL_MAX	500
+
+/* register definitions */
+#define MMA8450_STATUS		0x00
+#define MMA8450_STATUS_ZXYDR	0x08
+
+#define MMA8450_OUT_X8		0x01
+#define MMA8450_OUT_Y8		0x02
+#define MMA8450_OUT_Z8		0x03
+
+#define MMA8450_OUT_X_LSB	0x05
+#define MMA8450_OUT_X_MSB	0x06
+#define MMA8450_OUT_Y_LSB	0x07
+#define MMA8450_OUT_Y_MSB	0x08
+#define MMA8450_OUT_Z_LSB	0x09
+#define MMA8450_OUT_Z_MSB	0x0a
+
+#define MMA8450_XYZ_DATA_CFG	0x16
+
+#define MMA8450_CTRL_REG1	0x38
+#define MMA8450_CTRL_REG2	0x39
+
+/* mma8450 status */
+struct mma8450 {
+	struct i2c_client	*client;
+	struct input_polled_dev	*idev;
+};
+
+static int mma8450_read(struct mma8450 *m, unsigned off)
+{
+	struct i2c_client *c = m->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(c, off);
+	if (ret < 0)
+		dev_err(&c->dev,
+			"failed to read register 0x%02x, error %d\n",
+			off, ret);
+
+	return ret;
+}
+
+static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+{
+	struct i2c_client *c = m->client;
+	int error;
+
+	error = i2c_smbus_write_byte_data(c, off, v);
+	if (error < 0) {
+		dev_err(&c->dev,
+			"failed to write to register 0x%02x, error %d\n",
+			off, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int mma8450_read_xyz(struct mma8450 *m, int *x, int *y, int *z)
+{
+	struct i2c_client *c = m->client;
+	u8 buff[6];
+	int err;
+
+	err = i2c_smbus_read_i2c_block_data(c, MMA8450_OUT_X_LSB, 6, buff);
+	if (err < 0) {
+		dev_err(&c->dev,
+			"failed to read block data at 0x%02x, error %d\n",
+			MMA8450_OUT_X_LSB, err);
+		return err;
+	}
+
+	*x = ((buff[1] << 4) & 0xff0) | (buff[0] & 0xf);
+	*y = ((buff[3] << 4) & 0xff0) | (buff[2] & 0xf);
+	*z = ((buff[5] << 4) & 0xff0) | (buff[4] & 0xf);
+
+	return 0;
+}
+
+static void mma8450_poll(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+	int x, y, z;
+	int ret;
+	int err;
+
+	ret = mma8450_read(m, MMA8450_STATUS);
+	if (ret < 0)
+		return;
+
+	if (!(ret & MMA8450_STATUS_ZXYDR))
+		return;
+
+	err = mma8450_read_xyz(m, &x, &y, &z);
+	if (err)
+		return;
+
+	input_report_abs(dev->input, ABS_X, x);
+	input_report_abs(dev->input, ABS_Y, y);
+	input_report_abs(dev->input, ABS_Z, z);
+	input_sync(dev->input);
+}
+
+/* Initialize the MMA8450 chip */
+static void mma8450_open(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+	int err;
+
+	/* enable all events from X/Y/Z, no FIFO */
+	err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+	if (err)
+		return;
+
+	/*
+	 * Sleep mode poll rate - 50Hz
+	 * System output data rate - 400Hz
+	 * Full scale selection - Active, +/- 2G
+	 */
+	err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
+	if (err < 0)
+		return;
+
+	msleep(MODE_CHANGE_DELAY_MS);
+}
+
+static void mma8450_close(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+
+	mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
+	mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int __devinit mma8450_probe(struct i2c_client *c,
+				   const struct i2c_device_id *id)
+{
+	struct input_polled_dev *idev;
+	struct mma8450 *m;
+	int err;
+
+	m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
+	idev = input_allocate_polled_device();
+	if (!m || !idev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	m->client = c;
+	m->idev = idev;
+
+	idev->private		= m;
+	idev->input->name	= MMA8450_DRV_NAME;
+	idev->input->id.bustype	= BUS_I2C;
+	idev->poll		= mma8450_poll;
+	idev->poll_interval	= POLL_INTERVAL;
+	idev->poll_interval_max	= POLL_INTERVAL_MAX;
+	idev->open		= mma8450_open;
+	idev->close		= mma8450_close;
+
+	__set_bit(EV_ABS, idev->input->evbit);
+	input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
+	input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
+	input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+
+	err = input_register_polled_device(idev);
+	if (err) {
+		dev_err(&c->dev, "failed to register polled input device\n");
+		goto err_free_mem;
+	}
+
+	return 0;
+
+err_free_mem:
+	input_free_polled_device(idev);
+	kfree(m);
+	return err;
+}
+
+static int __devexit mma8450_remove(struct i2c_client *c)
+{
+	struct mma8450 *m = i2c_get_clientdata(c);
+	struct input_polled_dev *idev = m->idev;
+
+	input_unregister_polled_device(idev);
+	input_free_polled_device(idev);
+	kfree(m);
+
+	return 0;
+}
+
+static const struct i2c_device_id mma8450_id[] = {
+	{ MMA8450_DRV_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_id);
+
+static struct i2c_driver mma8450_driver = {
+	.driver = {
+		.name	= MMA8450_DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= mma8450_probe,
+	.remove		= __devexit_p(mma8450_remove),
+	.id_table	= mma8450_id,
+};
+
+static int __init mma8450_init(void)
+{
+	return i2c_add_driver(&mma8450_driver);
+}
+module_init(mma8450_init);
+
+static void __exit mma8450_exit(void)
+{
+	i2c_del_driver(&mma8450_driver);
+}
+module_exit(mma8450_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
new file mode 100644
index 0000000..b95fac1
--- /dev/null
+++ b/drivers/input/misc/mpu3050.c
@@ -0,0 +1,376 @@
+/*
+ * MPU3050 Tri-axis gyroscope driver
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Trimmed down by Alan Cox <alan@linux.intel.com> to produce this version
+ *
+ * This is a 'lite' version of the driver, while we consider the right way
+ * to present the other features to user space. In particular it requires the
+ * device has an IRQ, and it only provides an input interface, so is not much
+ * use for device orientation. A fuller version is available from the Meego
+ * tree.
+ *
+ * This program is based on bma023.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define MPU3050_CHIP_ID_REG	0x00
+#define MPU3050_CHIP_ID		0x69
+#define MPU3050_XOUT_H		0x1D
+#define MPU3050_PWR_MGM		0x3E
+#define MPU3050_PWR_MGM_POS	6
+#define MPU3050_PWR_MGM_MASK	0x40
+
+#define MPU3050_AUTO_DELAY	1000
+
+#define MPU3050_MIN_VALUE	-32768
+#define MPU3050_MAX_VALUE	32767
+
+struct axis_data {
+	s16 x;
+	s16 y;
+	s16 z;
+};
+
+struct mpu3050_sensor {
+	struct i2c_client *client;
+	struct device *dev;
+	struct input_dev *idev;
+};
+
+/**
+ *	mpu3050_xyz_read_reg	-	read the axes values
+ *	@buffer: provide register addr and get register
+ *	@length: length of register
+ *
+ *	Reads the register values in one transaction or returns a negative
+ *	error code on failure.
+ */
+static int mpu3050_xyz_read_reg(struct i2c_client *client,
+			       u8 *buffer, int length)
+{
+	/*
+	 * Annoying we can't make this const because the i2c layer doesn't
+	 * declare input buffers const.
+	 */
+	char cmd = MPU3050_XOUT_H;
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &cmd,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = buffer,
+		},
+	};
+
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ *	mpu3050_read_xyz	-	get co-ordinates from device
+ *	@client: i2c address of sensor
+ *	@coords: co-ordinates to update
+ *
+ *	Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void mpu3050_read_xyz(struct i2c_client *client,
+			     struct axis_data *coords)
+{
+	u16 buffer[3];
+
+	mpu3050_xyz_read_reg(client, (u8 *)buffer, 6);
+	coords->x = be16_to_cpu(buffer[0]);
+	coords->y = be16_to_cpu(buffer[1]);
+	coords->z = be16_to_cpu(buffer[2]);
+	dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+					coords->x, coords->y, coords->z);
+}
+
+/**
+ *	mpu3050_set_power_mode	-	set the power mode
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ *	Put device to normal-power mode or low-power mode.
+ */
+static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
+{
+	u8 value;
+
+	value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+	value = (value & ~MPU3050_PWR_MGM_MASK) |
+		(((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
+		 MPU3050_PWR_MGM_MASK);
+	i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+}
+
+/**
+ *	mpu3050_input_open	-	called on input event open
+ *	@input: input dev of opened device
+ *
+ *	The input layer calls this function when input event is opened. The
+ *	function will push the device to resume. Then, the device is ready
+ *	to provide data.
+ */
+static int mpu3050_input_open(struct input_dev *input)
+{
+	struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+	pm_runtime_get(sensor->dev);
+
+	return 0;
+}
+
+/**
+ *	mpu3050_input_close	-	called on input event close
+ *	@input: input dev of closed device
+ *
+ *	The input layer calls this function when input event is closed. The
+ *	function will push the device to suspend.
+ */
+static void mpu3050_input_close(struct input_dev *input)
+{
+	struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+	pm_runtime_put(sensor->dev);
+}
+
+/**
+ *	mpu3050_interrupt_thread	-	handle an IRQ
+ *	@irq: interrupt numner
+ *	@data: the sensor
+ *
+ *	Called by the kernel single threaded after an interrupt occurs. Read
+ *	the sensor data and generate an input event for it.
+ */
+static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+{
+	struct mpu3050_sensor *sensor = data;
+	struct axis_data axis;
+
+	mpu3050_read_xyz(sensor->client, &axis);
+
+	input_report_abs(sensor->idev, ABS_X, axis.x);
+	input_report_abs(sensor->idev, ABS_Y, axis.y);
+	input_report_abs(sensor->idev, ABS_Z, axis.z);
+	input_sync(sensor->idev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ *	mpu3050_probe	-	device detection callback
+ *	@client: i2c client of found device
+ *	@id: id match information
+ *
+ *	The I2C layer calls us when it believes a sensor is present at this
+ *	address. Probe to see if this is correct and to validate the device.
+ *
+ *	If present install the relevant sysfs interfaces and input device.
+ */
+static int __devinit mpu3050_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	struct mpu3050_sensor *sensor;
+	struct input_dev *idev;
+	int ret;
+	int error;
+
+	sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!sensor || !idev) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	sensor->client = client;
+	sensor->dev = &client->dev;
+	sensor->idev = idev;
+
+	mpu3050_set_power_mode(client, 1);
+	msleep(10);
+
+	ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to detect device\n");
+		error = -ENXIO;
+		goto err_free_mem;
+	}
+
+	if (ret != MPU3050_CHIP_ID) {
+		dev_err(&client->dev, "unsupported chip id\n");
+		error = -ENXIO;
+		goto err_free_mem;
+	}
+
+	idev->name = "MPU3050";
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &client->dev;
+
+	idev->open = mpu3050_input_open;
+	idev->close = mpu3050_input_close;
+
+	__set_bit(EV_ABS, idev->evbit);
+	input_set_abs_params(idev, ABS_X,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Y,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Z,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+
+	input_set_drvdata(idev, sensor);
+
+	pm_runtime_set_active(&client->dev);
+
+	error = request_threaded_irq(client->irq,
+				     NULL, mpu3050_interrupt_thread,
+				     IRQF_TRIGGER_RISING,
+				     "mpu_int", sensor);
+	if (error) {
+		dev_err(&client->dev,
+			"can't get IRQ %d, error %d\n", client->irq, error);
+		goto err_pm_set_suspended;
+	}
+
+	error = input_register_device(idev);
+	if (error) {
+		dev_err(&client->dev, "failed to register input device\n");
+		goto err_free_irq;
+	}
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, sensor);
+err_pm_set_suspended:
+	pm_runtime_set_suspended(&client->dev);
+err_free_mem:
+	input_unregister_device(idev);
+	kfree(sensor);
+	return error;
+}
+
+/**
+ *	mpu3050_remove	-	remove a sensor
+ *	@client: i2c client of sensor being removed
+ *
+ *	Our sensor is going away, clean up the resources.
+ */
+static int __devexit mpu3050_remove(struct i2c_client *client)
+{
+	struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	free_irq(client->irq, sensor);
+	input_unregister_device(sensor->idev);
+	kfree(sensor);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ *	mpu3050_suspend		-	called on device suspend
+ *	@dev: device being suspended
+ *
+ *	Put the device into sleep mode before we suspend the machine.
+ */
+static int mpu3050_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	mpu3050_set_power_mode(client, 0);
+
+	return 0;
+}
+
+/**
+ *	mpu3050_resume		-	called on device resume
+ *	@dev: device being resumed
+ *
+ *	Put the device into powered mode on resume.
+ */
+static int mpu3050_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	mpu3050_set_power_mode(client, 1);
+	msleep(100);  /* wait for gyro chip resume */
+
+	return 0;
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);
+
+static const struct i2c_device_id mpu3050_ids[] = {
+	{ "mpu3050", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_ids);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+	.driver	= {
+		.name	= "mpu3050",
+		.owner	= THIS_MODULE,
+		.pm	= &mpu3050_pm,
+	},
+	.probe		= mpu3050_probe,
+	.remove		= __devexit_p(mpu3050_remove),
+	.id_table	= mpu3050_ids,
+};
+
+static int __init mpu3050_init(void)
+{
+	return i2c_add_driver(&mpu3050_i2c_driver);
+}
+module_init(mpu3050_init);
+
+static void __exit mpu3050_exit(void)
+{
+	i2c_del_driver(&mpu3050_i2c_driver);
+}
+module_exit(mpu3050_exit);
+
+MODULE_AUTHOR("Wistron Corp.");
+MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 014dd4a..3c1a432 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -28,7 +28,7 @@
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
 #include <linux/i2c/twl.h>
-#include <linux/mfd/twl4030-codec.h>
+#include <linux/mfd/twl4030-audio.h>
 #include <linux/input.h>
 #include <linux/slab.h>
 
@@ -67,7 +67,7 @@
 {
 	u8 reg;
 
-	twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
+	twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
 
 	/* turn H-Bridge on */
 	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
@@ -75,7 +75,7 @@
 	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
 			 (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
 
-	twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+	twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
 
 	info->enabled = true;
 }
@@ -90,8 +90,8 @@
 	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
 			 (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
 
-	twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
-	twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
+	twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
+	twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
 
 	info->enabled = false;
 }
@@ -196,7 +196,7 @@
 
 static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
 {
-	struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
+	struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
 	struct vibra_info *info;
 	int ret;
 
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
new file mode 100644
index 0000000..c43002e
--- /dev/null
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -0,0 +1,423 @@
+/*
+ * twl6040-vibra.c - TWL6040 Vibrator driver
+ *
+ * Author:      Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ * Author:      Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright:   (C) 2011 Texas Instruments, Inc.
+ *
+ * Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
+ *				Felipe Balbi <felipe.balbi@nokia.com>
+ *				Jari Vanhala <ext-javi.vanhala@nokia.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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#define EFFECT_DIR_180_DEG	0x8000
+
+/* Recommended modulation index 85% */
+#define TWL6040_VIBRA_MOD	85
+
+#define TWL6040_NUM_SUPPLIES 2
+
+struct vibra_info {
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct workqueue_struct *workqueue;
+	struct work_struct play_work;
+	struct mutex mutex;
+	int irq;
+
+	bool enabled;
+	int weak_speed;
+	int strong_speed;
+	int direction;
+
+	unsigned int vibldrv_res;
+	unsigned int vibrdrv_res;
+	unsigned int viblmotor_res;
+	unsigned int vibrmotor_res;
+
+	struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES];
+
+	struct twl6040 *twl6040;
+};
+
+static irqreturn_t twl6040_vib_irq_handler(int irq, void *data)
+{
+	struct vibra_info *info = data;
+	struct twl6040 *twl6040 = info->twl6040;
+	u8 status;
+
+	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+	if (status & TWL6040_VIBLOCDET) {
+		dev_warn(info->dev, "Left Vibrator overcurrent detected\n");
+		twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL,
+				   TWL6040_VIBENAL);
+	}
+	if (status & TWL6040_VIBROCDET) {
+		dev_warn(info->dev, "Right Vibrator overcurrent detected\n");
+		twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR,
+				   TWL6040_VIBENAR);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void twl6040_vibra_enable(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies);
+	if (ret) {
+		dev_err(info->dev, "failed to enable regulators %d\n", ret);
+		return;
+	}
+
+	twl6040_power(info->twl6040, 1);
+	if (twl6040->rev <= TWL6040_REV_ES1_1) {
+		/*
+		 * ERRATA: Disable overcurrent protection for at least
+		 * 3ms when enabling vibrator drivers to avoid false
+		 * overcurrent detection
+		 */
+		twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+				  TWL6040_VIBENAL | TWL6040_VIBCTRLL);
+		twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+				  TWL6040_VIBENAR | TWL6040_VIBCTRLR);
+		usleep_range(3000, 3500);
+	}
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+			  TWL6040_VIBENAL);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+			  TWL6040_VIBENAR);
+
+	info->enabled = true;
+}
+
+static void twl6040_vibra_disable(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00);
+	twl6040_power(info->twl6040, 0);
+
+	regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies);
+
+	info->enabled = false;
+}
+
+static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res,
+			     int speed, int direction)
+{
+	int vpk, max_code;
+	u8 vibdat;
+
+	/* output swing */
+	vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) /
+		(100 * (vibdrv_res + motor_res));
+
+	/* 50mV per VIBDAT code step */
+	max_code = vpk / 50;
+	if (max_code > TWL6040_VIBDAT_MAX)
+		max_code = TWL6040_VIBDAT_MAX;
+
+	/* scale speed to max allowed code */
+	vibdat = (u8)((speed * max_code) / USHRT_MAX);
+
+	/* 2's complement for direction > 180 degrees */
+	vibdat *= direction;
+
+	return vibdat;
+}
+
+static void twl6040_vibra_set_effect(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+	u8 vibdatl, vibdatr;
+	int volt;
+
+	/* weak motor */
+	volt = regulator_get_voltage(info->supplies[0].consumer) / 1000;
+	vibdatl = twl6040_vibra_code(volt, info->vibldrv_res,
+				     info->viblmotor_res,
+				     info->weak_speed, info->direction);
+
+	/* strong motor */
+	volt = regulator_get_voltage(info->supplies[1].consumer) / 1000;
+	vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res,
+				     info->vibrmotor_res,
+				     info->strong_speed, info->direction);
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+	struct vibra_info *info = container_of(work,
+				struct vibra_info, play_work);
+
+	mutex_lock(&info->mutex);
+
+	if (info->weak_speed || info->strong_speed) {
+		if (!info->enabled)
+			twl6040_vibra_enable(info);
+
+		twl6040_vibra_set_effect(info);
+	} else if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+}
+
+static int vibra_play(struct input_dev *input, void *data,
+		      struct ff_effect *effect)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+	int ret;
+
+	info->weak_speed = effect->u.rumble.weak_magnitude;
+	info->strong_speed = effect->u.rumble.strong_magnitude;
+	info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
+
+	ret = queue_work(info->workqueue, &info->play_work);
+	if (!ret) {
+		dev_info(&input->dev, "work is already on queue\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void twl6040_vibra_close(struct input_dev *input)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+
+	cancel_work_sync(&info->play_work);
+
+	mutex_lock(&info->mutex);
+
+	if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+}
+
+#if CONFIG_PM_SLEEP
+static int twl6040_vibra_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vibra_info *info = platform_get_drvdata(pdev);
+
+	mutex_lock(&info->mutex);
+
+	if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+
+	return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
+
+static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
+{
+	struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
+	struct vibra_info *info;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform_data not available\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		dev_err(&pdev->dev, "couldn't allocate memory\n");
+		return -ENOMEM;
+	}
+
+	info->dev = &pdev->dev;
+	info->twl6040 = dev_get_drvdata(pdev->dev.parent);
+	info->vibldrv_res = pdata->vibldrv_res;
+	info->vibrdrv_res = pdata->vibrdrv_res;
+	info->viblmotor_res = pdata->viblmotor_res;
+	info->vibrmotor_res = pdata->vibrmotor_res;
+	if ((!info->vibldrv_res && !info->viblmotor_res) ||
+	    (!info->vibrdrv_res && !info->vibrmotor_res)) {
+		dev_err(info->dev, "invalid vibra driver/motor resistance\n");
+		ret = -EINVAL;
+		goto err_kzalloc;
+	}
+
+	info->irq = platform_get_irq(pdev, 0);
+	if (info->irq < 0) {
+		dev_err(info->dev, "invalid irq\n");
+		ret = -EINVAL;
+		goto err_kzalloc;
+	}
+
+	mutex_init(&info->mutex);
+
+	info->input_dev = input_allocate_device();
+	if (info->input_dev == NULL) {
+		dev_err(info->dev, "couldn't allocate input device\n");
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	input_set_drvdata(info->input_dev, info);
+
+	info->input_dev->name = "twl6040:vibrator";
+	info->input_dev->id.version = 1;
+	info->input_dev->dev.parent = pdev->dev.parent;
+	info->input_dev->close = twl6040_vibra_close;
+	__set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+	ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+	if (ret < 0) {
+		dev_err(info->dev, "couldn't register vibrator to FF\n");
+		goto err_ialloc;
+	}
+
+	ret = input_register_device(info->input_dev);
+	if (ret < 0) {
+		dev_err(info->dev, "couldn't register input device\n");
+		goto err_iff;
+	}
+
+	platform_set_drvdata(pdev, info);
+
+	ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
+				   "twl6040_irq_vib", info);
+	if (ret) {
+		dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
+		goto err_irq;
+	}
+
+	info->supplies[0].supply = "vddvibl";
+	info->supplies[1].supply = "vddvibr";
+	ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies),
+				 info->supplies);
+	if (ret) {
+		dev_err(info->dev, "couldn't get regulators %d\n", ret);
+		goto err_regulator;
+	}
+
+	if (pdata->vddvibl_uV) {
+		ret = regulator_set_voltage(info->supplies[0].consumer,
+					    pdata->vddvibl_uV,
+					    pdata->vddvibl_uV);
+		if (ret) {
+			dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
+				ret);
+			goto err_voltage;
+		}
+	}
+
+	if (pdata->vddvibr_uV) {
+		ret = regulator_set_voltage(info->supplies[1].consumer,
+					    pdata->vddvibr_uV,
+					    pdata->vddvibr_uV);
+		if (ret) {
+			dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
+				ret);
+			goto err_voltage;
+		}
+	}
+
+	info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
+	if (info->workqueue == NULL) {
+		dev_err(info->dev, "couldn't create workqueue\n");
+		ret = -ENOMEM;
+		goto err_voltage;
+	}
+	INIT_WORK(&info->play_work, vibra_play_work);
+
+	return 0;
+
+err_voltage:
+	regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+err_regulator:
+	free_irq(info->irq, info);
+err_irq:
+	input_unregister_device(info->input_dev);
+	info->input_dev = NULL;
+err_iff:
+	if (info->input_dev)
+		input_ff_destroy(info->input_dev);
+err_ialloc:
+	input_free_device(info->input_dev);
+err_kzalloc:
+	kfree(info);
+	return ret;
+}
+
+static int __devexit twl6040_vibra_remove(struct platform_device *pdev)
+{
+	struct vibra_info *info = platform_get_drvdata(pdev);
+
+	input_unregister_device(info->input_dev);
+	free_irq(info->irq, info);
+	regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+	destroy_workqueue(info->workqueue);
+	kfree(info);
+
+	return 0;
+}
+
+static struct platform_driver twl6040_vibra_driver = {
+	.probe		= twl6040_vibra_probe,
+	.remove		= __devexit_p(twl6040_vibra_remove),
+	.driver		= {
+		.name	= "twl6040-vibra",
+		.owner	= THIS_MODULE,
+		.pm	= &twl6040_vibra_pm_ops,
+	},
+};
+
+static int __init twl6040_vibra_init(void)
+{
+	return platform_driver_register(&twl6040_vibra_driver);
+}
+module_init(twl6040_vibra_init);
+
+static void __exit twl6040_vibra_exit(void)
+{
+	platform_driver_unregister(&twl6040_vibra_driver);
+}
+module_exit(twl6040_vibra_exit);
+
+MODULE_ALIAS("platform:twl6040-vibra");
+MODULE_DESCRIPTION("TWL6040 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 62bae99..ad2e51c 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -373,7 +373,7 @@
 
 static int __init xenkbd_init(void)
 {
-	if (!xen_pv_domain())
+	if (!xen_domain())
 		return -ENODEV;
 
 	/* Nothing to do if running in dom0. */
diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c
index 7b6ce17..58902fb 100644
--- a/drivers/input/mouse/gpio_mouse.c
+++ b/drivers/input/mouse/gpio_mouse.c
@@ -191,7 +191,7 @@
 }
 module_exit(gpio_mouse_exit);
 
-MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 MODULE_DESCRIPTION("GPIO mouse driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index c31ad11..83bcaba 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -33,7 +33,7 @@
 static int lifebook_limit_serio3(const struct dmi_system_id *d)
 {
 	desired_serio_phys = "isa0060/serio3";
-	return 0;
+	return 1;
 }
 
 static bool lifebook_use_6byte_proto;
@@ -41,7 +41,7 @@
 static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
 {
 	lifebook_use_6byte_proto = true;
-	return 0;
+	return 1;
 }
 
 static const struct dmi_system_id __initconst lifebook_dmi_table[] = {
diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c
index 943cfec..6c5d84f 100644
--- a/drivers/input/mouse/pxa930_trkball.c
+++ b/drivers/input/mouse/pxa930_trkball.c
@@ -12,7 +12,6 @@
 
 #include <linux/init.h>
 #include <linux/input.h>
-#include <linux/version.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 1242775..2fc887a 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -20,7 +20,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/input.h>
 #include <linux/ctype.h>
 #include <linux/libps2.h>
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index e06e045..5538fc6 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -207,27 +207,37 @@
 static int synaptics_resolution(struct psmouse *psmouse)
 {
 	struct synaptics_data *priv = psmouse->private;
-	unsigned char res[3];
-	unsigned char max[3];
+	unsigned char resp[3];
 
 	if (SYN_ID_MAJOR(priv->identity) < 4)
 		return 0;
 
-	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) {
-		if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) {
-			priv->x_res = res[0]; /* x resolution in units/mm */
-			priv->y_res = res[2]; /* y resolution in units/mm */
+	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+		if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
+			priv->x_res = resp[0]; /* x resolution in units/mm */
+			priv->y_res = resp[2]; /* y resolution in units/mm */
 		}
 	}
 
 	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
 	    SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
-		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) {
-			printk(KERN_ERR "Synaptics claims to have dimensions query,"
-			       " but I'm not able to read it.\n");
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+			printk(KERN_ERR "Synaptics claims to have max coordinates"
+			       " query, but I'm not able to read it.\n");
 		} else {
-			priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1);
-			priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3);
+			priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+			priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+		}
+	}
+
+	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 &&
+	    SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+			printk(KERN_ERR "Synaptics claims to have min coordinates"
+			       " query, but I'm not able to read it.\n");
+		} else {
+			priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+			priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
 		}
 	}
 
@@ -406,26 +416,10 @@
 	memset(hw, 0, sizeof(struct synaptics_hw_state));
 
 	if (SYN_MODEL_NEWABS(priv->model_id)) {
-		hw->x = (((buf[3] & 0x10) << 8) |
-			 ((buf[1] & 0x0f) << 8) |
-			 buf[4]);
-		hw->y = (((buf[3] & 0x20) << 7) |
-			 ((buf[1] & 0xf0) << 4) |
-			 buf[5]);
-
-		hw->z = buf[2];
 		hw->w = (((buf[0] & 0x30) >> 2) |
 			 ((buf[0] & 0x04) >> 1) |
 			 ((buf[3] & 0x04) >> 2));
 
-		if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
-			/* Gesture packet: (x, y, z) at half resolution */
-			priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
-			priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
-			priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
-			return 1;
-		}
-
 		hw->left  = (buf[0] & 0x01) ? 1 : 0;
 		hw->right = (buf[0] & 0x02) ? 1 : 0;
 
@@ -448,6 +442,22 @@
 			hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
 		}
 
+		if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
+			/* Gesture packet: (x, y, z) at half resolution */
+			priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+			priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+			priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+			return 1;
+		}
+
+		hw->x = (((buf[3] & 0x10) << 8) |
+			 ((buf[1] & 0x0f) << 8) |
+			 buf[4]);
+		hw->y = (((buf[3] & 0x20) << 7) |
+			 ((buf[1] & 0xf0) << 4) |
+			 buf[5]);
+		hw->z = buf[2];
+
 		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
 		    ((buf[0] ^ buf[3]) & 0x02)) {
 			switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
@@ -485,7 +495,8 @@
 	return 0;
 }
 
-static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
+static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
+					  bool active, int x, int y)
 {
 	input_mt_slot(dev, slot);
 	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
@@ -502,14 +513,16 @@
 					  int num_fingers)
 {
 	if (num_fingers >= 2) {
-		set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
-		set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
+		synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x),
+					      min(a->y, b->y));
+		synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x),
+					      max(a->y, b->y));
 	} else if (num_fingers == 1) {
-		set_slot(dev, 0, true, a->x, a->y);
-		set_slot(dev, 1, false, 0, 0);
+		synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y);
+		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
 	} else {
-		set_slot(dev, 0, false, 0, 0);
-		set_slot(dev, 1, false, 0, 0);
+		synaptics_report_semi_mt_slot(dev, 0, false, 0, 0);
+		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
 	}
 }
 
@@ -684,23 +697,36 @@
 static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
 {
 	int i;
+	int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+			SYN_REDUCED_FILTER_FUZZ : 0;
 
 	__set_bit(INPUT_PROP_POINTER, dev->propbit);
 
 	__set_bit(EV_ABS, dev->evbit);
 	input_set_abs_params(dev, ABS_X,
-			     XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
+			     priv->x_min ?: XMIN_NOMINAL,
+			     priv->x_max ?: XMAX_NOMINAL,
+			     fuzz, 0);
 	input_set_abs_params(dev, ABS_Y,
-			     YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
+			     priv->y_min ?: YMIN_NOMINAL,
+			     priv->y_max ?: YMAX_NOMINAL,
+			     fuzz, 0);
 	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
 
 	if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
 		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
 		input_mt_init_slots(dev, 2);
-		input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
-				     priv->x_max ?: XMAX_NOMINAL, 0, 0);
-		input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
-				     priv->y_max ?: YMAX_NOMINAL, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_X,
+				     priv->x_min ?: XMIN_NOMINAL,
+				     priv->x_max ?: XMAX_NOMINAL,
+				     fuzz, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y,
+				     priv->y_min ?: YMIN_NOMINAL,
+				     priv->y_max ?: YMAX_NOMINAL,
+				     fuzz, 0);
+
+		input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
+		input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
 	}
 
 	if (SYN_CAP_PALMDETECT(priv->capabilities))
@@ -971,4 +997,3 @@
 }
 
 #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
-
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 7453938..ca040aa 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -19,7 +19,8 @@
 #define SYN_QUE_RESOLUTION		0x08
 #define SYN_QUE_EXT_CAPAB		0x09
 #define SYN_QUE_EXT_CAPAB_0C		0x0c
-#define SYN_QUE_EXT_DIMENSIONS		0x0d
+#define SYN_QUE_EXT_MAX_COORDS		0x0d
+#define SYN_QUE_EXT_MIN_COORDS		0x0f
 
 /* synatics modes */
 #define SYN_BIT_ABSOLUTE_MODE		(1 << 7)
@@ -66,18 +67,21 @@
  * 1	0x60	multifinger mode	identifies firmware finger counting
  *					(not reporting!) algorithm.
  *					Not particularly meaningful
- * 1	0x80    covered pad		W clipped to 14, 15 == pad mostly covered
- * 2	0x01    clickpad bit 1		2-button ClickPad
- * 2	0x02    deluxe LED controls	touchpad support LED commands
+ * 1	0x80	covered pad		W clipped to 14, 15 == pad mostly covered
+ * 2	0x01	clickpad bit 1		2-button ClickPad
+ * 2	0x02	deluxe LED controls	touchpad support LED commands
  *					ala multimedia control bar
  * 2	0x04	reduced filtering	firmware does less filtering on
  *					position data, driver should watch
  *					for noise.
+ * 2	0x20	report min		query 0x0f gives min coord reported
  */
 #define SYN_CAP_CLICKPAD(ex0c)		((ex0c) & 0x100000) /* 1-button ClickPad */
 #define SYN_CAP_CLICKPAD2BTN(ex0c)	((ex0c) & 0x000100) /* 2-button ClickPad */
 #define SYN_CAP_MAX_DIMENSIONS(ex0c)	((ex0c) & 0x020000)
+#define SYN_CAP_MIN_DIMENSIONS(ex0c)	((ex0c) & 0x002000)
 #define SYN_CAP_ADV_GESTURE(ex0c)	((ex0c) & 0x080000)
+#define SYN_CAP_REDUCED_FILTERING(ex0c)	((ex0c) & 0x000400)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
@@ -104,6 +108,9 @@
 #define SYN_NEWABS_RELAXED		2
 #define SYN_OLDABS			3
 
+/* amount to fuzz position data when touchpad reports reduced filtering */
+#define SYN_REDUCED_FILTER_FUZZ		8
+
 /*
  * A structure to describe the state of the touchpad hardware (buttons and pad)
  */
@@ -130,7 +137,8 @@
 	unsigned long int ext_cap_0c;		/* Ext Caps from 0x0c query */
 	unsigned long int identity;		/* Identification */
 	unsigned int x_res, y_res;		/* X/Y resolution in units/mm */
-	unsigned int x_max, y_max;		/* Max dimensions (from FW) */
+	unsigned int x_max, y_max;		/* Max coordinates (from FW) */
+	unsigned int x_min, y_min;		/* Min coordinates (from FW) */
 
 	unsigned char pkt_type;			/* packet type - old, new, etc */
 	unsigned char mode;			/* current mode byte */
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
index 6ee8f0d..95280f9 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -372,6 +372,6 @@
 module_init(psif_init);
 module_exit(psif_exit);
 
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 4220620..979c443 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -795,7 +795,7 @@
 
 /************************* Keepalive timer task *********************/
 
-void hp_sdc_kicker (unsigned long data)
+static void hp_sdc_kicker(unsigned long data)
 {
 	tasklet_schedule(&hp_sdc.task);
 	/* Re-insert the periodic task. */
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index 0a619c5..6d89fd1 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -225,7 +225,6 @@
 	/* toolMode codes
 	 */
 #define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
-#define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
 #define AIPTEK_TOOL_BUTTON_PENCIL_MODE			BTN_TOOL_PENCIL
 #define AIPTEK_TOOL_BUTTON_BRUSH_MODE			BTN_TOOL_BRUSH
 #define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE		BTN_TOOL_AIRBRUSH
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 08ba5ad..03ebcc8 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -15,6 +15,7 @@
 #include "wacom_wac.h"
 #include "wacom.h"
 #include <linux/input/mt.h>
+#include <linux/hid.h>
 
 /* resolution for penabled devices */
 #define WACOM_PL_RES		20
@@ -264,6 +265,7 @@
 			wacom->id[0] = 0;
 		input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
 		input_report_key(input, wacom->tool[0], prox);
+		input_event(input, EV_MSC, MSC_SERIAL, 1);
 		input_sync(input); /* sync last event */
 	}
 
@@ -273,11 +275,10 @@
 		prox = data[7] & 0xf8;
 		if (prox || wacom->id[1]) {
 			wacom->id[1] = PAD_DEVICE_ID;
-			input_report_key(input, BTN_0, (data[7] & 0x40));
-			input_report_key(input, BTN_4, (data[7] & 0x80));
+			input_report_key(input, BTN_BACK, (data[7] & 0x40));
+			input_report_key(input, BTN_FORWARD, (data[7] & 0x80));
 			rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
 			input_report_rel(input, REL_WHEEL, rw);
-			input_report_key(input, BTN_TOOL_FINGER, 0xf0);
 			if (!prox)
 				wacom->id[1] = 0;
 			input_report_abs(input, ABS_MISC, wacom->id[1]);
@@ -290,18 +291,17 @@
 		prox = (data[7] & 0xf8) || data[8];
 		if (prox || wacom->id[1]) {
 			wacom->id[1] = PAD_DEVICE_ID;
-			input_report_key(input, BTN_0, (data[7] & 0x08));
-			input_report_key(input, BTN_1, (data[7] & 0x20));
-			input_report_key(input, BTN_4, (data[7] & 0x10));
-			input_report_key(input, BTN_5, (data[7] & 0x40));
+			input_report_key(input, BTN_BACK, (data[7] & 0x08));
+			input_report_key(input, BTN_LEFT, (data[7] & 0x20));
+			input_report_key(input, BTN_FORWARD, (data[7] & 0x10));
+			input_report_key(input, BTN_RIGHT, (data[7] & 0x40));
 			input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f));
-			input_report_key(input, BTN_TOOL_FINGER, 0xf0);
 			if (!prox)
 				wacom->id[1] = 0;
 			input_report_abs(input, ABS_MISC, wacom->id[1]);
 			input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+			retval = 1;
 		}
-		retval = 1;
 		break;
 	}
 exit:
@@ -494,10 +494,6 @@
 
 	/* pad packets. Works as a second tool and is always in prox */
 	if (data[0] == WACOM_REPORT_INTUOSPAD) {
-		/* initiate the pad as a device */
-		if (wacom->tool[1] != BTN_TOOL_FINGER)
-			wacom->tool[1] = BTN_TOOL_FINGER;
-
 		if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
 			input_report_key(input, BTN_0, (data[2] & 0x01));
 			input_report_key(input, BTN_1, (data[3] & 0x01));
@@ -1080,18 +1076,14 @@
 
 	switch (wacom_wac->features.type) {
 	case WACOM_MO:
-		__set_bit(BTN_1, input_dev->keybit);
-		__set_bit(BTN_5, input_dev->keybit);
-
 		input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
 		/* fall through */
 
 	case WACOM_G4:
 		input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
 
-		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-		__set_bit(BTN_0, input_dev->keybit);
-		__set_bit(BTN_4, input_dev->keybit);
+		__set_bit(BTN_BACK, input_dev->keybit);
+		__set_bit(BTN_FORWARD, input_dev->keybit);
 		/* fall through */
 
 	case GRAPHIRE:
@@ -1127,10 +1119,12 @@
 	case CINTIQ:
 		for (i = 0; i < 8; i++)
 			__set_bit(BTN_0 + i, input_dev->keybit);
-		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
 
-		input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
-		input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+		if (wacom_wac->features.type != WACOM_21UX2) {
+			input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+			input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+		}
+
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		wacom_setup_cintiq(wacom_wac);
 		break;
@@ -1151,8 +1145,6 @@
 		__set_bit(BTN_2, input_dev->keybit);
 		__set_bit(BTN_3, input_dev->keybit);
 
-		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-
 		input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		/* fall through */
@@ -1170,7 +1162,6 @@
 	case INTUOS4S:
 		for (i = 0; i < 7; i++)
 			__set_bit(BTN_0 + i, input_dev->keybit);
-		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
 
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		wacom_setup_intuos(wacom_wac);
@@ -1295,6 +1286,12 @@
 static const struct wacom_features wacom_features_0x69 =
 	{ "Wacom Bamboo1",        WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511,
 	  63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+static const struct wacom_features wacom_features_0x6A =
+	{ "Wacom Bamboo1 4x6",    WACOM_PKGLEN_GRAPHIRE,  14760,  9225, 1023,
+	  63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x6B =
+	{ "Wacom Bamboo1 5x8",    WACOM_PKGLEN_GRAPHIRE,  21648, 13530, 1023,
+	  63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x20 =
 	{ "Wacom Intuos 4x5",     WACOM_PKGLEN_INTUOS,    12700, 10600, 1023,
 	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1427,6 +1424,9 @@
 static const struct wacom_features wacom_features_0x93 =
 	{ "Wacom ISDv4 93",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
 	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x97 =
+	{ "Wacom ISDv4 97",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  511,
+	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x9A =
 	{ "Wacom ISDv4 9A",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
 	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1458,7 +1458,7 @@
 	{ "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN,     21648, 13530, 1023,
 	  63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xD4 =
-	{ "Wacom Bamboo Pen",     WACOM_PKGLEN_BBFUN,     14720,  9200,  255,
+	{ "Wacom Bamboo Pen",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
 	  63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xD6 =
 	{ "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN,   14720,  9200, 1023,
@@ -1483,6 +1483,11 @@
 	USB_DEVICE(USB_VENDOR_ID_WACOM, prod),			\
 	.driver_info = (kernel_ulong_t)&wacom_features_##prod
 
+#define USB_DEVICE_DETAILED(prod, class, sub, proto)			\
+	USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class,	\
+				      sub, proto),			\
+	.driver_info = (kernel_ulong_t)&wacom_features_##prod
+
 #define USB_DEVICE_LENOVO(prod)					\
 	USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),			\
 	.driver_info = (kernel_ulong_t)&wacom_features_##prod
@@ -1506,6 +1511,8 @@
 	{ USB_DEVICE_WACOM(0x64) },
 	{ USB_DEVICE_WACOM(0x65) },
 	{ USB_DEVICE_WACOM(0x69) },
+	{ USB_DEVICE_WACOM(0x6A) },
+	{ USB_DEVICE_WACOM(0x6B) },
 	{ USB_DEVICE_WACOM(0x20) },
 	{ USB_DEVICE_WACOM(0x21) },
 	{ USB_DEVICE_WACOM(0x22) },
@@ -1545,7 +1552,13 @@
 	{ USB_DEVICE_WACOM(0xC5) },
 	{ USB_DEVICE_WACOM(0xC6) },
 	{ USB_DEVICE_WACOM(0xC7) },
-	{ USB_DEVICE_WACOM(0xCE) },
+	/*
+	 * DTU-2231 has two interfaces on the same configuration,
+	 * only one is used.
+	 */
+	{ USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
+			      USB_INTERFACE_SUBCLASS_BOOT,
+			      USB_INTERFACE_PROTOCOL_MOUSE) },
 	{ USB_DEVICE_WACOM(0xD0) },
 	{ USB_DEVICE_WACOM(0xD1) },
 	{ USB_DEVICE_WACOM(0xD2) },
@@ -1560,6 +1573,7 @@
 	{ USB_DEVICE_WACOM(0xCC) },
 	{ USB_DEVICE_WACOM(0x90) },
 	{ USB_DEVICE_WACOM(0x93) },
+	{ USB_DEVICE_WACOM(0x97) },
 	{ USB_DEVICE_WACOM(0x9A) },
 	{ USB_DEVICE_WACOM(0x9F) },
 	{ USB_DEVICE_WACOM(0xE2) },
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 5196861..d507b9b 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -967,17 +967,12 @@
 		ts->get_pendown_state = pdata->get_pendown_state;
 	} else if (gpio_is_valid(pdata->gpio_pendown)) {
 
-		err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
+		err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
+				       "ads7846_pendown");
 		if (err) {
-			dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
-				pdata->gpio_pendown);
-			return err;
-		}
-		err = gpio_direction_input(pdata->gpio_pendown);
-		if (err) {
-			dev_err(&spi->dev, "failed to setup pendown GPIO%d\n",
-				pdata->gpio_pendown);
-			gpio_free(pdata->gpio_pendown);
+			dev_err(&spi->dev,
+				"failed to request/setup pendown GPIO%d: %d\n",
+				pdata->gpio_pendown, err);
 			return err;
 		}
 
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
index fa8e56b..8034cbb 100644
--- a/drivers/input/touchscreen/atmel-wm97xx.c
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -164,7 +164,7 @@
 
 		data = ac97c_readl(atmel_wm97xx, CBRHR);
 		value = data & 0x0fff;
-		source = data & WM97XX_ADCSRC_MASK;
+		source = data & WM97XX_ADCSEL_MASK;
 		pen_down = (data & WM97XX_PEN_DOWN) >> 8;
 
 		if (source == WM97XX_ADCSEL_X)
@@ -442,6 +442,6 @@
 }
 module_exit(atmel_wm97xx_exit);
 
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
 MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
 MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 1e61387..ae00604 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -48,41 +48,47 @@
 #define MXT_OBJECT_SIZE		6
 
 /* Object types */
-#define MXT_DEBUG_DIAGNOSTIC	37
-#define MXT_GEN_MESSAGE		5
-#define MXT_GEN_COMMAND		6
-#define MXT_GEN_POWER		7
-#define MXT_GEN_ACQUIRE		8
-#define MXT_TOUCH_MULTI		9
-#define MXT_TOUCH_KEYARRAY	15
-#define MXT_TOUCH_PROXIMITY	23
-#define MXT_PROCI_GRIPFACE	20
-#define MXT_PROCG_NOISE		22
-#define MXT_PROCI_ONETOUCH	24
-#define MXT_PROCI_TWOTOUCH	27
-#define MXT_PROCI_GRIP		40
-#define MXT_PROCI_PALM		41
-#define MXT_SPT_COMMSCONFIG	18
-#define MXT_SPT_GPIOPWM		19
-#define MXT_SPT_SELFTEST	25
-#define MXT_SPT_CTECONFIG	28
-#define MXT_SPT_USERDATA	38
-#define MXT_SPT_DIGITIZER	43
-#define MXT_SPT_MESSAGECOUNT	44
+#define MXT_DEBUG_DIAGNOSTIC_T37	37
+#define MXT_GEN_MESSAGE_T5		5
+#define MXT_GEN_COMMAND_T6		6
+#define MXT_GEN_POWER_T7		7
+#define MXT_GEN_ACQUIRE_T8		8
+#define MXT_GEN_DATASOURCE_T53		53
+#define MXT_TOUCH_MULTI_T9		9
+#define MXT_TOUCH_KEYARRAY_T15		15
+#define MXT_TOUCH_PROXIMITY_T23		23
+#define MXT_TOUCH_PROXKEY_T52		52
+#define MXT_PROCI_GRIPFACE_T20		20
+#define MXT_PROCG_NOISE_T22		22
+#define MXT_PROCI_ONETOUCH_T24		24
+#define MXT_PROCI_TWOTOUCH_T27		27
+#define MXT_PROCI_GRIP_T40		40
+#define MXT_PROCI_PALM_T41		41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42	42
+#define MXT_PROCI_STYLUS_T47		47
+#define MXT_PROCG_NOISESUPPRESSION_T48	48
+#define MXT_SPT_COMMSCONFIG_T18		18
+#define MXT_SPT_GPIOPWM_T19		19
+#define MXT_SPT_SELFTEST_T25		25
+#define MXT_SPT_CTECONFIG_T28		28
+#define MXT_SPT_USERDATA_T38		38
+#define MXT_SPT_DIGITIZER_T43		43
+#define MXT_SPT_MESSAGECOUNT_T44	44
+#define MXT_SPT_CTECONFIG_T46		46
 
-/* MXT_GEN_COMMAND field */
+/* MXT_GEN_COMMAND_T6 field */
 #define MXT_COMMAND_RESET	0
 #define MXT_COMMAND_BACKUPNV	1
 #define MXT_COMMAND_CALIBRATE	2
 #define MXT_COMMAND_REPORTALL	3
 #define MXT_COMMAND_DIAGNOSTIC	5
 
-/* MXT_GEN_POWER field */
+/* MXT_GEN_POWER_T7 field */
 #define MXT_POWER_IDLEACQINT	0
 #define MXT_POWER_ACTVACQINT	1
 #define MXT_POWER_ACTV2IDLETO	2
 
-/* MXT_GEN_ACQUIRE field */
+/* MXT_GEN_ACQUIRE_T8 field */
 #define MXT_ACQUIRE_CHRGTIME	0
 #define MXT_ACQUIRE_TCHDRIFT	2
 #define MXT_ACQUIRE_DRIFTST	3
@@ -91,7 +97,7 @@
 #define MXT_ACQUIRE_ATCHCALST	6
 #define MXT_ACQUIRE_ATCHCALSTHR	7
 
-/* MXT_TOUCH_MULTI field */
+/* MXT_TOUCH_MULTI_T9 field */
 #define MXT_TOUCH_CTRL		0
 #define MXT_TOUCH_XORIGIN	1
 #define MXT_TOUCH_YORIGIN	2
@@ -121,7 +127,7 @@
 #define MXT_TOUCH_YEDGEDIST	29
 #define MXT_TOUCH_JUMPLIMIT	30
 
-/* MXT_PROCI_GRIPFACE field */
+/* MXT_PROCI_GRIPFACE_T20 field */
 #define MXT_GRIPFACE_CTRL	0
 #define MXT_GRIPFACE_XLOGRIP	1
 #define MXT_GRIPFACE_XHIGRIP	2
@@ -151,11 +157,11 @@
 #define MXT_NOISE_FREQ4		15
 #define MXT_NOISE_IDLEGCAFVALID	16
 
-/* MXT_SPT_COMMSCONFIG */
+/* MXT_SPT_COMMSCONFIG_T18 */
 #define MXT_COMMS_CTRL		0
 #define MXT_COMMS_CMD		1
 
-/* MXT_SPT_CTECONFIG field */
+/* MXT_SPT_CTECONFIG_T28 field */
 #define MXT_CTE_CTRL		0
 #define MXT_CTE_CMD		1
 #define MXT_CTE_MODE		2
@@ -166,7 +172,7 @@
 #define MXT_VOLTAGE_DEFAULT	2700000
 #define MXT_VOLTAGE_STEP	10000
 
-/* Define for MXT_GEN_COMMAND */
+/* Define for MXT_GEN_COMMAND_T6 */
 #define MXT_BOOT_VALUE		0xa5
 #define MXT_BACKUP_VALUE	0x55
 #define MXT_BACKUP_TIME		25	/* msec */
@@ -256,24 +262,31 @@
 static bool mxt_object_readable(unsigned int type)
 {
 	switch (type) {
-	case MXT_GEN_MESSAGE:
-	case MXT_GEN_COMMAND:
-	case MXT_GEN_POWER:
-	case MXT_GEN_ACQUIRE:
-	case MXT_TOUCH_MULTI:
-	case MXT_TOUCH_KEYARRAY:
-	case MXT_TOUCH_PROXIMITY:
-	case MXT_PROCI_GRIPFACE:
-	case MXT_PROCG_NOISE:
-	case MXT_PROCI_ONETOUCH:
-	case MXT_PROCI_TWOTOUCH:
-	case MXT_PROCI_GRIP:
-	case MXT_PROCI_PALM:
-	case MXT_SPT_COMMSCONFIG:
-	case MXT_SPT_GPIOPWM:
-	case MXT_SPT_SELFTEST:
-	case MXT_SPT_CTECONFIG:
-	case MXT_SPT_USERDATA:
+	case MXT_GEN_MESSAGE_T5:
+	case MXT_GEN_COMMAND_T6:
+	case MXT_GEN_POWER_T7:
+	case MXT_GEN_ACQUIRE_T8:
+	case MXT_GEN_DATASOURCE_T53:
+	case MXT_TOUCH_MULTI_T9:
+	case MXT_TOUCH_KEYARRAY_T15:
+	case MXT_TOUCH_PROXIMITY_T23:
+	case MXT_TOUCH_PROXKEY_T52:
+	case MXT_PROCI_GRIPFACE_T20:
+	case MXT_PROCG_NOISE_T22:
+	case MXT_PROCI_ONETOUCH_T24:
+	case MXT_PROCI_TWOTOUCH_T27:
+	case MXT_PROCI_GRIP_T40:
+	case MXT_PROCI_PALM_T41:
+	case MXT_PROCI_TOUCHSUPPRESSION_T42:
+	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCG_NOISESUPPRESSION_T48:
+	case MXT_SPT_COMMSCONFIG_T18:
+	case MXT_SPT_GPIOPWM_T19:
+	case MXT_SPT_SELFTEST_T25:
+	case MXT_SPT_CTECONFIG_T28:
+	case MXT_SPT_USERDATA_T38:
+	case MXT_SPT_DIGITIZER_T43:
+	case MXT_SPT_CTECONFIG_T46:
 		return true;
 	default:
 		return false;
@@ -283,21 +296,28 @@
 static bool mxt_object_writable(unsigned int type)
 {
 	switch (type) {
-	case MXT_GEN_COMMAND:
-	case MXT_GEN_POWER:
-	case MXT_GEN_ACQUIRE:
-	case MXT_TOUCH_MULTI:
-	case MXT_TOUCH_KEYARRAY:
-	case MXT_TOUCH_PROXIMITY:
-	case MXT_PROCI_GRIPFACE:
-	case MXT_PROCG_NOISE:
-	case MXT_PROCI_ONETOUCH:
-	case MXT_PROCI_TWOTOUCH:
-	case MXT_PROCI_GRIP:
-	case MXT_PROCI_PALM:
-	case MXT_SPT_GPIOPWM:
-	case MXT_SPT_SELFTEST:
-	case MXT_SPT_CTECONFIG:
+	case MXT_GEN_COMMAND_T6:
+	case MXT_GEN_POWER_T7:
+	case MXT_GEN_ACQUIRE_T8:
+	case MXT_TOUCH_MULTI_T9:
+	case MXT_TOUCH_KEYARRAY_T15:
+	case MXT_TOUCH_PROXIMITY_T23:
+	case MXT_TOUCH_PROXKEY_T52:
+	case MXT_PROCI_GRIPFACE_T20:
+	case MXT_PROCG_NOISE_T22:
+	case MXT_PROCI_ONETOUCH_T24:
+	case MXT_PROCI_TWOTOUCH_T27:
+	case MXT_PROCI_GRIP_T40:
+	case MXT_PROCI_PALM_T41:
+	case MXT_PROCI_TOUCHSUPPRESSION_T42:
+	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCG_NOISESUPPRESSION_T48:
+	case MXT_SPT_COMMSCONFIG_T18:
+	case MXT_SPT_GPIOPWM_T19:
+	case MXT_SPT_SELFTEST_T25:
+	case MXT_SPT_CTECONFIG_T28:
+	case MXT_SPT_DIGITIZER_T43:
+	case MXT_SPT_CTECONFIG_T46:
 		return true;
 	default:
 		return false;
@@ -455,7 +475,7 @@
 	struct mxt_object *object;
 	u16 reg;
 
-	object = mxt_get_object(data, MXT_GEN_MESSAGE);
+	object = mxt_get_object(data, MXT_GEN_MESSAGE_T5);
 	if (!object)
 		return -EINVAL;
 
@@ -597,8 +617,8 @@
 
 		reportid = message.reportid;
 
-		/* whether reportid is thing of MXT_TOUCH_MULTI */
-		object = mxt_get_object(data, MXT_TOUCH_MULTI);
+		/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
+		object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
 		if (!object)
 			goto end;
 
@@ -635,7 +655,9 @@
 		if (!mxt_object_writable(object->type))
 			continue;
 
-		for (j = 0; j < object->size + 1; j++) {
+		for (j = 0;
+		     j < (object->size + 1) * (object->instances + 1);
+		     j++) {
 			config_offset = index + j;
 			if (config_offset > pdata->config_length) {
 				dev_err(dev, "Not enough config data!\n");
@@ -644,7 +666,7 @@
 			mxt_write_object(data, object->type, j,
 					 pdata->config[config_offset]);
 		}
-		index += object->size + 1;
+		index += (object->size + 1) * (object->instances + 1);
 	}
 
 	return 0;
@@ -678,31 +700,31 @@
 	u8 voltage;
 
 	/* Set touchscreen lines */
-	mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
 			pdata->x_line);
-	mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
 			pdata->y_line);
 
 	/* Set touchscreen orient */
-	mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
 			pdata->orient);
 
 	/* Set touchscreen burst length */
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_BLEN, pdata->blen);
 
 	/* Set touchscreen threshold */
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_TCHTHR, pdata->threshold);
 
 	/* Set touchscreen resolution */
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
-	mxt_write_object(data, MXT_TOUCH_MULTI,
+	mxt_write_object(data, MXT_TOUCH_MULTI_T9,
 			MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
 
 	/* Set touchscreen voltage */
@@ -715,7 +737,7 @@
 			voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
 				MXT_VOLTAGE_STEP;
 
-		mxt_write_object(data, MXT_SPT_CTECONFIG,
+		mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
 				MXT_CTE_VOLTAGE, voltage);
 	}
 }
@@ -819,13 +841,13 @@
 	mxt_handle_pdata(data);
 
 	/* Backup to memory */
-	mxt_write_object(data, MXT_GEN_COMMAND,
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
 			MXT_COMMAND_BACKUPNV,
 			MXT_BACKUP_VALUE);
 	msleep(MXT_BACKUP_TIME);
 
 	/* Soft reset */
-	mxt_write_object(data, MXT_GEN_COMMAND,
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
 			MXT_COMMAND_RESET, 1);
 	msleep(MXT_RESET_TIME);
 
@@ -921,7 +943,7 @@
 	}
 
 	/* Change to the bootloader mode */
-	mxt_write_object(data, MXT_GEN_COMMAND,
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
 			MXT_COMMAND_RESET, MXT_BOOT_VALUE);
 	msleep(MXT_RESET_TIME);
 
@@ -1027,14 +1049,14 @@
 {
 	/* Touch enable */
 	mxt_write_object(data,
-			MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83);
+			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
 }
 
 static void mxt_stop(struct mxt_data *data)
 {
 	/* Touch disable */
 	mxt_write_object(data,
-			MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0);
+			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
 }
 
 static int mxt_input_open(struct input_dev *dev)
@@ -1182,7 +1204,7 @@
 	struct input_dev *input_dev = data->input_dev;
 
 	/* Soft reset */
-	mxt_write_object(data, MXT_GEN_COMMAND,
+	mxt_write_object(data, MXT_GEN_COMMAND_T6,
 			MXT_COMMAND_RESET, 1);
 
 	msleep(MXT_RESET_TIME);
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index a93c5c2..d8815c5 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -84,9 +84,9 @@
 	memcpy(i2c_data + 1, value, len);
 
 	ret = i2c_master_send(client, i2c_data, len + 1);
-	if (ret != 1) {
+	if (ret != len + 1) {
 		dev_err(&client->dev, "i2c write data cmd failed\n");
-		return ret ? ret : -EIO;
+		return ret < 0 ? ret : -EIO;
 	}
 
 	return 0;
@@ -193,6 +193,8 @@
 
 	ts->client = client;
 	ts->input = input_dev;
+	ts->reset_pin = pdata->reset_pin;
+	ts->irq_pin = pdata->irq_pin;
 
 	snprintf(ts->phys, sizeof(ts->phys),
 		 "%s/input0", dev_name(&client->dev));
@@ -328,7 +330,7 @@
 	return 0;
 }
 
-static struct i2c_device_id cy8ctmg110_idtable[] = {
+static const struct i2c_device_id cy8ctmg110_idtable[] = {
 	{ CY8CTMG110_DRIVER_NAME, 1 },
 	{ }
 };
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
index 66c96bf..3276952 100644
--- a/drivers/input/touchscreen/intel-mid-touch.c
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -448,15 +448,11 @@
  */
 static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
 {
-	int err, i, found;
+	int found = 0;
+	int err, i;
 	u8 r8;
 
-	found = -1;
-
 	for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
-		if (found >= 0)
-			break;
-
 		err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
 		if (err)
 			return err;
@@ -466,16 +462,15 @@
 			break;
 		}
 	}
-	if (found < 0)
-		return 0;
 
 	if (tsdev->vendor == PMIC_VENDOR_FS) {
-		if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
+		if (found > MRSTOUCH_MAX_CHANNELS - 18)
 			return -ENOSPC;
 	} else {
-		if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
+		if (found > MRSTOUCH_MAX_CHANNELS - 4)
 			return -ENOSPC;
 	}
+
 	return found;
 }
 
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index 3242e70..e966c29 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -157,9 +157,9 @@
 			x, y, p);
 
 		/* are samples valid */
-		if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
-		    (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
-		    (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
+		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
 			goto up;
 
 		/* coordinate is good */
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
index 22a3411..089b0a0 100644
--- a/drivers/input/touchscreen/tnetv107x-ts.c
+++ b/drivers/input/touchscreen/tnetv107x-ts.c
@@ -393,5 +393,5 @@
 
 MODULE_AUTHOR("Cyril Chemparathy");
 MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
-MODULE_ALIAS("platform: tnetv107x-ts");
+MODULE_ALIAS("platform:tnetv107x-ts");
 MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
index 98e6117..adc13a5 100644
--- a/drivers/input/touchscreen/wm9705.c
+++ b/drivers/input/touchscreen/wm9705.c
@@ -215,8 +215,9 @@
 static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
 {
 	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
 
-	if (!wm->pen_probably_down) {
+	if (wants_pen && !wm->pen_probably_down) {
 		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
 		if (!(data & WM97XX_PEN_DOWN))
 			return RC_PENUP;
@@ -224,13 +225,10 @@
 	}
 
 	/* set up digitiser */
-	if (adcsel & 0x8000)
-		adcsel = ((adcsel & 0x7fff) + 3) << 12;
-
 	if (wm->mach_ops && wm->mach_ops->pre_sample)
 		wm->mach_ops->pre_sample(adcsel);
-	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
-			 adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+				| WM97XX_POLL | WM97XX_DELAY(delay));
 
 	/* wait 3 AC97 time slots + delay for conversion */
 	poll_delay(delay);
@@ -256,13 +254,14 @@
 		wm->mach_ops->post_sample(adcsel);
 
 	/* check we have correct sample */
-	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
-		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
-		*sample & WM97XX_ADCSEL_MASK);
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
 		return RC_PENUP;
 	}
 
-	if (!(*sample & WM97XX_PEN_DOWN)) {
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
 		wm->pen_probably_down = 0;
 		return RC_PENUP;
 	}
@@ -277,14 +276,14 @@
 {
 	int rc;
 
-	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
 	if (rc != RC_VALID)
 		return rc;
-	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
 	if (rc != RC_VALID)
 		return rc;
 	if (pil) {
-		rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p);
+		rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
 		if (rc != RC_VALID)
 			return rc;
 	} else
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
index 2bc2fb8..6e743e3 100644
--- a/drivers/input/touchscreen/wm9712.c
+++ b/drivers/input/touchscreen/wm9712.c
@@ -255,8 +255,9 @@
 static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
 {
 	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
 
-	if (!wm->pen_probably_down) {
+	if (wants_pen && !wm->pen_probably_down) {
 		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
 		if (!(data & WM97XX_PEN_DOWN))
 			return RC_PENUP;
@@ -264,13 +265,10 @@
 	}
 
 	/* set up digitiser */
-	if (adcsel & 0x8000)
-		adcsel = ((adcsel & 0x7fff) + 3) << 12;
-
 	if (wm->mach_ops && wm->mach_ops->pre_sample)
 		wm->mach_ops->pre_sample(adcsel);
-	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
-			 adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+				| WM97XX_POLL | WM97XX_DELAY(delay));
 
 	/* wait 3 AC97 time slots + delay for conversion */
 	poll_delay(delay);
@@ -296,13 +294,14 @@
 		wm->mach_ops->post_sample(adcsel);
 
 	/* check we have correct sample */
-	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
-		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
-		*sample & WM97XX_ADCSEL_MASK);
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
 		return RC_PENUP;
 	}
 
-	if (!(*sample & WM97XX_PEN_DOWN)) {
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
 		wm->pen_probably_down = 0;
 		return RC_PENUP;
 	}
@@ -387,16 +386,18 @@
 		if (rc != RC_VALID)
 			return rc;
 	} else {
-		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN,
+					&data->x);
 		if (rc != RC_VALID)
 			return rc;
 
-		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN,
+					&data->y);
 		if (rc != RC_VALID)
 			return rc;
 
 		if (pil && !five_wire) {
-			rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES,
+			rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
 						&data->p);
 			if (rc != RC_VALID)
 				return rc;
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
index 73ec995..7405353 100644
--- a/drivers/input/touchscreen/wm9713.c
+++ b/drivers/input/touchscreen/wm9713.c
@@ -261,8 +261,9 @@
 {
 	u16 dig1;
 	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
 
-	if (!wm->pen_probably_down) {
+	if (wants_pen && !wm->pen_probably_down) {
 		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
 		if (!(data & WM97XX_PEN_DOWN))
 			return RC_PENUP;
@@ -270,15 +271,14 @@
 	}
 
 	/* set up digitiser */
-	if (adcsel & 0x8000)
-		adcsel = 1 << ((adcsel & 0x7fff) + 3);
-
 	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
 	dig1 &= ~WM9713_ADCSEL_MASK;
+	/* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */
+	dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);
 
 	if (wm->mach_ops && wm->mach_ops->pre_sample)
 		wm->mach_ops->pre_sample(adcsel);
-	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);
 
 	/* wait 3 AC97 time slots + delay for conversion */
 	poll_delay(delay);
@@ -304,13 +304,14 @@
 		wm->mach_ops->post_sample(adcsel);
 
 	/* check we have correct sample */
-	if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
-		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
-			*sample & WM97XX_ADCSRC_MASK);
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
 		return RC_PENUP;
 	}
 
-	if (!(*sample & WM97XX_PEN_DOWN)) {
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
 		wm->pen_probably_down = 0;
 		return RC_PENUP;
 	}
@@ -400,14 +401,14 @@
 		if (rc != RC_VALID)
 			return rc;
 	} else {
-		rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x);
+		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
 		if (rc != RC_VALID)
 			return rc;
-		rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y);
+		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
 		if (rc != RC_VALID)
 			return rc;
 		if (pil) {
-			rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES,
+			rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
 						&data->p);
 			if (rc != RC_VALID)
 				return rc;
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
index 5b0f15e..f6328c0 100644
--- a/drivers/input/touchscreen/zylonite-wm97xx.c
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -122,9 +122,9 @@
 			x, y, p);
 
 		/* are samples valid */
-		if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
-		    (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
-		    (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
+		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
 			goto up;
 
 		/* coordinate is good */
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 6dd3607..212efaf 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -34,7 +34,7 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/list.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define GIG_VERSION {0, 5, 0, 0}
 #define GIG_COMPAT  {0, 4, 0, 0}
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 48e9cc0..1f73d7f 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -2532,6 +2532,9 @@
 
 	/* Setup the generic properties */
 	dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+
+	/* isdn prepends a header in the tx path, can't share skbs */
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->header_ops = NULL;
 	dev->netdev_ops = &isdn_netdev_ops;
 
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 574b09a..0dc6546 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -29,7 +29,6 @@
 #include "md.h"
 #include "bitmap.h"
 
-#include <linux/dm-dirty-log.h>
 /* debug macros */
 
 #define DEBUG 0
@@ -775,10 +774,8 @@
  * 0 or page 1
  */
 static inline struct page *filemap_get_page(struct bitmap *bitmap,
-					unsigned long chunk)
+					    unsigned long chunk)
 {
-	if (bitmap->filemap == NULL)
-		return NULL;
 	if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
 		return NULL;
 	return bitmap->filemap[file_page_index(bitmap, chunk)
@@ -878,28 +875,19 @@
 static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
 				enum bitmap_page_attr attr)
 {
-	if (page)
-		__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
-	else
-		__set_bit(attr, &bitmap->logattrs);
+	__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
 }
 
 static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
 				enum bitmap_page_attr attr)
 {
-	if (page)
-		__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
-	else
-		__clear_bit(attr, &bitmap->logattrs);
+	__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
 }
 
 static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
 					   enum bitmap_page_attr attr)
 {
-	if (page)
-		return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
-	else
-		return test_bit(attr, &bitmap->logattrs);
+	return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
 }
 
 /*
@@ -912,30 +900,26 @@
 static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
 {
 	unsigned long bit;
-	struct page *page = NULL;
+	struct page *page;
 	void *kaddr;
 	unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
 
-	if (!bitmap->filemap) {
-		struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
-		if (log)
-			log->type->mark_region(log, chunk);
-	} else {
+	if (!bitmap->filemap)
+		return;
 
-		page = filemap_get_page(bitmap, chunk);
-		if (!page)
-			return;
-		bit = file_page_offset(bitmap, chunk);
+	page = filemap_get_page(bitmap, chunk);
+	if (!page)
+		return;
+	bit = file_page_offset(bitmap, chunk);
 
-		/* set the bit */
-		kaddr = kmap_atomic(page, KM_USER0);
-		if (bitmap->flags & BITMAP_HOSTENDIAN)
-			set_bit(bit, kaddr);
-		else
-			__test_and_set_bit_le(bit, kaddr);
-		kunmap_atomic(kaddr, KM_USER0);
-		PRINTK("set file bit %lu page %lu\n", bit, page->index);
-	}
+	/* set the bit */
+	kaddr = kmap_atomic(page, KM_USER0);
+	if (bitmap->flags & BITMAP_HOSTENDIAN)
+		set_bit(bit, kaddr);
+	else
+		__set_bit_le(bit, kaddr);
+	kunmap_atomic(kaddr, KM_USER0);
+	PRINTK("set file bit %lu page %lu\n", bit, page->index);
 	/* record page number so it gets flushed to disk when unplug occurs */
 	set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
 }
@@ -952,16 +936,6 @@
 
 	if (!bitmap)
 		return;
-	if (!bitmap->filemap) {
-		/* Must be using a dirty_log */
-		struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
-		dirty = test_and_clear_bit(BITMAP_PAGE_DIRTY, &bitmap->logattrs);
-		need_write = test_and_clear_bit(BITMAP_PAGE_NEEDWRITE, &bitmap->logattrs);
-		if (dirty || need_write)
-			if (log->type->flush(log))
-				bitmap->flags |= BITMAP_WRITE_ERROR;
-		goto out;
-	}
 
 	/* look at each page to see if there are any set bits that need to be
 	 * flushed out to disk */
@@ -990,7 +964,6 @@
 		else
 			md_super_wait(bitmap->mddev);
 	}
-out:
 	if (bitmap->flags & BITMAP_WRITE_ERROR)
 		bitmap_file_kick(bitmap);
 }
@@ -1199,7 +1172,6 @@
 	struct page *page = NULL, *lastpage = NULL;
 	sector_t blocks;
 	void *paddr;
-	struct dm_dirty_log *log = mddev->bitmap_info.log;
 
 	/* Use a mutex to guard daemon_work against
 	 * bitmap_destroy.
@@ -1224,12 +1196,11 @@
 	spin_lock_irqsave(&bitmap->lock, flags);
 	for (j = 0; j < bitmap->chunks; j++) {
 		bitmap_counter_t *bmc;
-		if (!bitmap->filemap) {
-			if (!log)
-				/* error or shutdown */
-				break;
-		} else
-			page = filemap_get_page(bitmap, j);
+		if (!bitmap->filemap)
+			/* error or shutdown */
+			break;
+
+		page = filemap_get_page(bitmap, j);
 
 		if (page != lastpage) {
 			/* skip this page unless it's marked as needing cleaning */
@@ -1298,17 +1269,16 @@
 						  -1);
 
 				/* clear the bit */
-				if (page) {
-					paddr = kmap_atomic(page, KM_USER0);
-					if (bitmap->flags & BITMAP_HOSTENDIAN)
-						clear_bit(file_page_offset(bitmap, j),
-							  paddr);
-					else
-						__test_and_clear_bit_le(file_page_offset(bitmap, j),
-							       paddr);
-					kunmap_atomic(paddr, KM_USER0);
-				} else
-					log->type->clear_region(log, j);
+				paddr = kmap_atomic(page, KM_USER0);
+				if (bitmap->flags & BITMAP_HOSTENDIAN)
+					clear_bit(file_page_offset(bitmap, j),
+						  paddr);
+				else
+					__clear_bit_le(
+							file_page_offset(bitmap,
+									 j),
+							paddr);
+				kunmap_atomic(paddr, KM_USER0);
 			}
 		} else
 			j |= PAGE_COUNTER_MASK;
@@ -1316,16 +1286,12 @@
 	spin_unlock_irqrestore(&bitmap->lock, flags);
 
 	/* now sync the final page */
-	if (lastpage != NULL || log != NULL) {
+	if (lastpage != NULL) {
 		spin_lock_irqsave(&bitmap->lock, flags);
 		if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
 			clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
 			spin_unlock_irqrestore(&bitmap->lock, flags);
-			if (lastpage)
-				write_page(bitmap, lastpage, 0);
-			else
-				if (log->type->flush(log))
-					bitmap->flags |= BITMAP_WRITE_ERROR;
+			write_page(bitmap, lastpage, 0);
 		} else {
 			set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
 			spin_unlock_irqrestore(&bitmap->lock, flags);
@@ -1767,12 +1733,10 @@
 	BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
 
 	if (!file
-	    && !mddev->bitmap_info.offset
-	    && !mddev->bitmap_info.log) /* bitmap disabled, nothing to do */
+	    && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
 		return 0;
 
 	BUG_ON(file && mddev->bitmap_info.offset);
-	BUG_ON(mddev->bitmap_info.offset && mddev->bitmap_info.log);
 
 	bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
 	if (!bitmap)
@@ -1863,6 +1827,7 @@
 int bitmap_load(mddev_t *mddev)
 {
 	int err = 0;
+	sector_t start = 0;
 	sector_t sector = 0;
 	struct bitmap *bitmap = mddev->bitmap;
 
@@ -1881,24 +1846,14 @@
 	}
 	bitmap_close_sync(bitmap);
 
-	if (mddev->bitmap_info.log) {
-		unsigned long i;
-		struct dm_dirty_log *log = mddev->bitmap_info.log;
-		for (i = 0; i < bitmap->chunks; i++)
-			if (!log->type->in_sync(log, i, 1))
-				bitmap_set_memory_bits(bitmap,
-						       (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
-						       1);
-	} else {
-		sector_t start = 0;
-		if (mddev->degraded == 0
-		    || bitmap->events_cleared == mddev->events)
-			/* no need to keep dirty bits to optimise a
-			 * re-add of a missing device */
-			start = mddev->recovery_cp;
+	if (mddev->degraded == 0
+	    || bitmap->events_cleared == mddev->events)
+		/* no need to keep dirty bits to optimise a
+		 * re-add of a missing device */
+		start = mddev->recovery_cp;
 
-		err = bitmap_init_from_disk(bitmap, start);
-	}
+	err = bitmap_init_from_disk(bitmap, start);
+
 	if (err)
 		goto out;
 
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index b2a127e..a28f2e5 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -212,10 +212,6 @@
 	unsigned long file_pages; /* number of pages in the file */
 	int last_page_size; /* bytes in the last page */
 
-	unsigned long logattrs; /* used when filemap_attr doesn't exist
-				 * because we are working with a dirty_log
-				 */
-
 	unsigned long flags;
 
 	int allclean;
@@ -237,7 +233,6 @@
 	wait_queue_head_t behind_wait;
 
 	struct sysfs_dirent *sysfs_can_clear;
-
 };
 
 /* the bitmap API */
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index c8827ff..bae6c4e 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -19,7 +19,7 @@
 #include <linux/workqueue.h>
 #include <linux/backing-dev.h>
 #include <linux/percpu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/scatterlist.h>
 #include <asm/page.h>
 #include <asm/unaligned.h>
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 819e37e..320401d 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -10,7 +10,7 @@
  */
 
 #include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/blkdev.h>
 #include <linux/fs.h>
 #include <linux/init.h>
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index aa4e570..c354701 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -19,7 +19,7 @@
 #include <linux/time.h>
 #include <linux/workqueue.h>
 #include <scsi/scsi_dh.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define DM_MSG_PREFIX "multipath"
 #define MESG_STR(x) x, sizeof(x)
diff --git a/drivers/md/dm-queue-length.c b/drivers/md/dm-queue-length.c
index f92b6ce..03a837a 100644
--- a/drivers/md/dm-queue-length.c
+++ b/drivers/md/dm-queue-length.c
@@ -20,7 +20,7 @@
 #include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define DM_MSG_PREFIX	"multipath queue-length"
 #define QL_MIN_IO	128
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 451c3bb..bfe9c23 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -17,7 +17,7 @@
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define DM_MSG_PREFIX "table"
 
diff --git a/drivers/md/md.c b/drivers/md/md.c
index dfc9425..8e221a2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -215,6 +215,55 @@
 }
 EXPORT_SYMBOL_GPL(bio_clone_mddev);
 
+void md_trim_bio(struct bio *bio, int offset, int size)
+{
+	/* 'bio' is a cloned bio which we need to trim to match
+	 * the given offset and size.
+	 * This requires adjusting bi_sector, bi_size, and bi_io_vec
+	 */
+	int i;
+	struct bio_vec *bvec;
+	int sofar = 0;
+
+	size <<= 9;
+	if (offset == 0 && size == bio->bi_size)
+		return;
+
+	bio->bi_sector += offset;
+	bio->bi_size = size;
+	offset <<= 9;
+	clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
+	while (bio->bi_idx < bio->bi_vcnt &&
+	       bio->bi_io_vec[bio->bi_idx].bv_len <= offset) {
+		/* remove this whole bio_vec */
+		offset -= bio->bi_io_vec[bio->bi_idx].bv_len;
+		bio->bi_idx++;
+	}
+	if (bio->bi_idx < bio->bi_vcnt) {
+		bio->bi_io_vec[bio->bi_idx].bv_offset += offset;
+		bio->bi_io_vec[bio->bi_idx].bv_len -= offset;
+	}
+	/* avoid any complications with bi_idx being non-zero*/
+	if (bio->bi_idx) {
+		memmove(bio->bi_io_vec, bio->bi_io_vec+bio->bi_idx,
+			(bio->bi_vcnt - bio->bi_idx) * sizeof(struct bio_vec));
+		bio->bi_vcnt -= bio->bi_idx;
+		bio->bi_idx = 0;
+	}
+	/* Make sure vcnt and last bv are not too big */
+	bio_for_each_segment(bvec, bio, i) {
+		if (sofar + bvec->bv_len > size)
+			bvec->bv_len = size - sofar;
+		if (bvec->bv_len == 0) {
+			bio->bi_vcnt = i;
+			break;
+		}
+		sofar += bvec->bv_len;
+	}
+}
+EXPORT_SYMBOL_GPL(md_trim_bio);
+
 /*
  * We have a system wide 'event count' that is incremented
  * on any 'interesting' event, and readers of /proc/mdstat
@@ -757,6 +806,10 @@
 		rdev->sb_start = 0;
 		rdev->sectors = 0;
 	}
+	if (rdev->bb_page) {
+		put_page(rdev->bb_page);
+		rdev->bb_page = NULL;
+	}
 }
 
 
@@ -1025,7 +1078,7 @@
 	ret = -EINVAL;
 
 	bdevname(rdev->bdev, b);
-	sb = (mdp_super_t*)page_address(rdev->sb_page);
+	sb = page_address(rdev->sb_page);
 
 	if (sb->md_magic != MD_SB_MAGIC) {
 		printk(KERN_ERR "md: invalid raid superblock magic on %s\n",
@@ -1054,6 +1107,7 @@
 	rdev->preferred_minor = sb->md_minor;
 	rdev->data_offset = 0;
 	rdev->sb_size = MD_SB_BYTES;
+	rdev->badblocks.shift = -1;
 
 	if (sb->level == LEVEL_MULTIPATH)
 		rdev->desc_nr = -1;
@@ -1064,7 +1118,7 @@
 		ret = 1;
 	} else {
 		__u64 ev1, ev2;
-		mdp_super_t *refsb = (mdp_super_t*)page_address(refdev->sb_page);
+		mdp_super_t *refsb = page_address(refdev->sb_page);
 		if (!uuid_equal(refsb, sb)) {
 			printk(KERN_WARNING "md: %s has different UUID to %s\n",
 				b, bdevname(refdev->bdev,b2));
@@ -1099,7 +1153,7 @@
 static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
 {
 	mdp_disk_t *desc;
-	mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page);
+	mdp_super_t *sb = page_address(rdev->sb_page);
 	__u64 ev1 = md_event(sb);
 
 	rdev->raid_disk = -1;
@@ -1230,7 +1284,7 @@
 
 	rdev->sb_size = MD_SB_BYTES;
 
-	sb = (mdp_super_t*)page_address(rdev->sb_page);
+	sb = page_address(rdev->sb_page);
 
 	memset(sb, 0, sizeof(*sb));
 
@@ -1395,6 +1449,8 @@
 	return cpu_to_le32(csum);
 }
 
+static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
+			    int acknowledged);
 static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
 {
 	struct mdp_superblock_1 *sb;
@@ -1435,7 +1491,7 @@
 	if (ret) return ret;
 
 
-	sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+	sb = page_address(rdev->sb_page);
 
 	if (sb->magic != cpu_to_le32(MD_SB_MAGIC) ||
 	    sb->major_version != cpu_to_le32(1) ||
@@ -1473,12 +1529,52 @@
 	else
 		rdev->desc_nr = le32_to_cpu(sb->dev_number);
 
+	if (!rdev->bb_page) {
+		rdev->bb_page = alloc_page(GFP_KERNEL);
+		if (!rdev->bb_page)
+			return -ENOMEM;
+	}
+	if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BAD_BLOCKS) &&
+	    rdev->badblocks.count == 0) {
+		/* need to load the bad block list.
+		 * Currently we limit it to one page.
+		 */
+		s32 offset;
+		sector_t bb_sector;
+		u64 *bbp;
+		int i;
+		int sectors = le16_to_cpu(sb->bblog_size);
+		if (sectors > (PAGE_SIZE / 512))
+			return -EINVAL;
+		offset = le32_to_cpu(sb->bblog_offset);
+		if (offset == 0)
+			return -EINVAL;
+		bb_sector = (long long)offset;
+		if (!sync_page_io(rdev, bb_sector, sectors << 9,
+				  rdev->bb_page, READ, true))
+			return -EIO;
+		bbp = (u64 *)page_address(rdev->bb_page);
+		rdev->badblocks.shift = sb->bblog_shift;
+		for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
+			u64 bb = le64_to_cpu(*bbp);
+			int count = bb & (0x3ff);
+			u64 sector = bb >> 10;
+			sector <<= sb->bblog_shift;
+			count <<= sb->bblog_shift;
+			if (bb + 1 == 0)
+				break;
+			if (md_set_badblocks(&rdev->badblocks,
+					     sector, count, 1) == 0)
+				return -EINVAL;
+		}
+	} else if (sb->bblog_offset == 0)
+		rdev->badblocks.shift = -1;
+
 	if (!refdev) {
 		ret = 1;
 	} else {
 		__u64 ev1, ev2;
-		struct mdp_superblock_1 *refsb = 
-			(struct mdp_superblock_1*)page_address(refdev->sb_page);
+		struct mdp_superblock_1 *refsb = page_address(refdev->sb_page);
 
 		if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 ||
 		    sb->level != refsb->level ||
@@ -1513,7 +1609,7 @@
 
 static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
 {
-	struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+	struct mdp_superblock_1 *sb = page_address(rdev->sb_page);
 	__u64 ev1 = le64_to_cpu(sb->events);
 
 	rdev->raid_disk = -1;
@@ -1619,13 +1715,12 @@
 	int max_dev, i;
 	/* make rdev->sb match mddev and rdev data. */
 
-	sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+	sb = page_address(rdev->sb_page);
 
 	sb->feature_map = 0;
 	sb->pad0 = 0;
 	sb->recovery_offset = cpu_to_le64(0);
 	memset(sb->pad1, 0, sizeof(sb->pad1));
-	memset(sb->pad2, 0, sizeof(sb->pad2));
 	memset(sb->pad3, 0, sizeof(sb->pad3));
 
 	sb->utime = cpu_to_le64((__u64)mddev->utime);
@@ -1665,6 +1760,40 @@
 		sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors);
 	}
 
+	if (rdev->badblocks.count == 0)
+		/* Nothing to do for bad blocks*/ ;
+	else if (sb->bblog_offset == 0)
+		/* Cannot record bad blocks on this device */
+		md_error(mddev, rdev);
+	else {
+		struct badblocks *bb = &rdev->badblocks;
+		u64 *bbp = (u64 *)page_address(rdev->bb_page);
+		u64 *p = bb->page;
+		sb->feature_map |= cpu_to_le32(MD_FEATURE_BAD_BLOCKS);
+		if (bb->changed) {
+			unsigned seq;
+
+retry:
+			seq = read_seqbegin(&bb->lock);
+
+			memset(bbp, 0xff, PAGE_SIZE);
+
+			for (i = 0 ; i < bb->count ; i++) {
+				u64 internal_bb = *p++;
+				u64 store_bb = ((BB_OFFSET(internal_bb) << 10)
+						| BB_LEN(internal_bb));
+				*bbp++ = cpu_to_le64(store_bb);
+			}
+			if (read_seqretry(&bb->lock, seq))
+				goto retry;
+
+			bb->sector = (rdev->sb_start +
+				      (int)le32_to_cpu(sb->bblog_offset));
+			bb->size = le16_to_cpu(sb->bblog_size);
+			bb->changed = 0;
+		}
+	}
+
 	max_dev = 0;
 	list_for_each_entry(rdev2, &mddev->disks, same_set)
 		if (rdev2->desc_nr+1 > max_dev)
@@ -1724,7 +1853,7 @@
 			num_sectors = max_sectors;
 		rdev->sb_start = sb_start;
 	}
-	sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page);
+	sb = page_address(rdev->sb_page);
 	sb->data_size = cpu_to_le64(num_sectors);
 	sb->super_offset = rdev->sb_start;
 	sb->sb_csum = calc_sb_1_csum(sb);
@@ -1922,7 +2051,7 @@
 	bd_link_disk_holder(rdev->bdev, mddev->gendisk);
 
 	/* May as well allow recovery to be retried once */
-	mddev->recovery_disabled = 0;
+	mddev->recovery_disabled++;
 
 	return 0;
 
@@ -1953,6 +2082,9 @@
 	sysfs_remove_link(&rdev->kobj, "block");
 	sysfs_put(rdev->sysfs_state);
 	rdev->sysfs_state = NULL;
+	kfree(rdev->badblocks.page);
+	rdev->badblocks.count = 0;
+	rdev->badblocks.page = NULL;
 	/* We need to delay this, otherwise we can deadlock when
 	 * writing to 'remove' to "dev/state".  We also need
 	 * to delay it due to rcu usage.
@@ -2127,10 +2259,10 @@
 		printk(KERN_INFO "md: rdev superblock (MJ:%d):\n", major_version);
 		switch (major_version) {
 		case 0:
-			print_sb_90((mdp_super_t*)page_address(rdev->sb_page));
+			print_sb_90(page_address(rdev->sb_page));
 			break;
 		case 1:
-			print_sb_1((struct mdp_superblock_1 *)page_address(rdev->sb_page));
+			print_sb_1(page_address(rdev->sb_page));
 			break;
 		}
 	} else
@@ -2194,6 +2326,7 @@
 	mdk_rdev_t *rdev;
 	int sync_req;
 	int nospares = 0;
+	int any_badblocks_changed = 0;
 
 repeat:
 	/* First make sure individual recovery_offsets are correct */
@@ -2208,8 +2341,18 @@
 	if (!mddev->persistent) {
 		clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
 		clear_bit(MD_CHANGE_DEVS, &mddev->flags);
-		if (!mddev->external)
+		if (!mddev->external) {
 			clear_bit(MD_CHANGE_PENDING, &mddev->flags);
+			list_for_each_entry(rdev, &mddev->disks, same_set) {
+				if (rdev->badblocks.changed) {
+					md_ack_all_badblocks(&rdev->badblocks);
+					md_error(mddev, rdev);
+				}
+				clear_bit(Blocked, &rdev->flags);
+				clear_bit(BlockedBadBlocks, &rdev->flags);
+				wake_up(&rdev->blocked_wait);
+			}
+		}
 		wake_up(&mddev->sb_wait);
 		return;
 	}
@@ -2265,6 +2408,14 @@
 		MD_BUG();
 		mddev->events --;
 	}
+
+	list_for_each_entry(rdev, &mddev->disks, same_set) {
+		if (rdev->badblocks.changed)
+			any_badblocks_changed++;
+		if (test_bit(Faulty, &rdev->flags))
+			set_bit(FaultRecorded, &rdev->flags);
+	}
+
 	sync_sbs(mddev, nospares);
 	spin_unlock_irq(&mddev->write_lock);
 
@@ -2290,6 +2441,13 @@
 				bdevname(rdev->bdev,b),
 				(unsigned long long)rdev->sb_start);
 			rdev->sb_events = mddev->events;
+			if (rdev->badblocks.size) {
+				md_super_write(mddev, rdev,
+					       rdev->badblocks.sector,
+					       rdev->badblocks.size << 9,
+					       rdev->bb_page);
+				rdev->badblocks.size = 0;
+			}
 
 		} else
 			dprintk(")\n");
@@ -2313,6 +2471,15 @@
 	if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
 		sysfs_notify(&mddev->kobj, NULL, "sync_completed");
 
+	list_for_each_entry(rdev, &mddev->disks, same_set) {
+		if (test_and_clear_bit(FaultRecorded, &rdev->flags))
+			clear_bit(Blocked, &rdev->flags);
+
+		if (any_badblocks_changed)
+			md_ack_all_badblocks(&rdev->badblocks);
+		clear_bit(BlockedBadBlocks, &rdev->flags);
+		wake_up(&rdev->blocked_wait);
+	}
 }
 
 /* words written to sysfs files may, or may not, be \n terminated.
@@ -2347,7 +2514,8 @@
 	char *sep = "";
 	size_t len = 0;
 
-	if (test_bit(Faulty, &rdev->flags)) {
+	if (test_bit(Faulty, &rdev->flags) ||
+	    rdev->badblocks.unacked_exist) {
 		len+= sprintf(page+len, "%sfaulty",sep);
 		sep = ",";
 	}
@@ -2359,7 +2527,8 @@
 		len += sprintf(page+len, "%swrite_mostly",sep);
 		sep = ",";
 	}
-	if (test_bit(Blocked, &rdev->flags)) {
+	if (test_bit(Blocked, &rdev->flags) ||
+	    rdev->badblocks.unacked_exist) {
 		len += sprintf(page+len, "%sblocked", sep);
 		sep = ",";
 	}
@@ -2368,6 +2537,10 @@
 		len += sprintf(page+len, "%sspare", sep);
 		sep = ",";
 	}
+	if (test_bit(WriteErrorSeen, &rdev->flags)) {
+		len += sprintf(page+len, "%swrite_error", sep);
+		sep = ",";
+	}
 	return len+sprintf(page+len, "\n");
 }
 
@@ -2375,13 +2548,15 @@
 state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 {
 	/* can write
-	 *  faulty  - simulates and error
+	 *  faulty  - simulates an error
 	 *  remove  - disconnects the device
 	 *  writemostly - sets write_mostly
 	 *  -writemostly - clears write_mostly
-	 *  blocked - sets the Blocked flag
-	 *  -blocked - clears the Blocked flag
+	 *  blocked - sets the Blocked flags
+	 *  -blocked - clears the Blocked and possibly simulates an error
 	 *  insync - sets Insync providing device isn't active
+	 *  write_error - sets WriteErrorSeen
+	 *  -write_error - clears WriteErrorSeen
 	 */
 	int err = -EINVAL;
 	if (cmd_match(buf, "faulty") && rdev->mddev->pers) {
@@ -2408,7 +2583,15 @@
 		set_bit(Blocked, &rdev->flags);
 		err = 0;
 	} else if (cmd_match(buf, "-blocked")) {
+		if (!test_bit(Faulty, &rdev->flags) &&
+		    test_bit(BlockedBadBlocks, &rdev->flags)) {
+			/* metadata handler doesn't understand badblocks,
+			 * so we need to fail the device
+			 */
+			md_error(rdev->mddev, rdev);
+		}
 		clear_bit(Blocked, &rdev->flags);
+		clear_bit(BlockedBadBlocks, &rdev->flags);
 		wake_up(&rdev->blocked_wait);
 		set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
 		md_wakeup_thread(rdev->mddev->thread);
@@ -2417,6 +2600,12 @@
 	} else if (cmd_match(buf, "insync") && rdev->raid_disk == -1) {
 		set_bit(In_sync, &rdev->flags);
 		err = 0;
+	} else if (cmd_match(buf, "write_error")) {
+		set_bit(WriteErrorSeen, &rdev->flags);
+		err = 0;
+	} else if (cmd_match(buf, "-write_error")) {
+		clear_bit(WriteErrorSeen, &rdev->flags);
+		err = 0;
 	}
 	if (!err)
 		sysfs_notify_dirent_safe(rdev->sysfs_state);
@@ -2459,7 +2648,6 @@
 {
 	char *e;
 	int err;
-	char nm[20];
 	int slot = simple_strtoul(buf, &e, 10);
 	if (strncmp(buf, "none", 4)==0)
 		slot = -1;
@@ -2482,8 +2670,7 @@
 			hot_remove_disk(rdev->mddev, rdev->raid_disk);
 		if (err)
 			return err;
-		sprintf(nm, "rd%d", rdev->raid_disk);
-		sysfs_remove_link(&rdev->mddev->kobj, nm);
+		sysfs_unlink_rdev(rdev->mddev, rdev);
 		rdev->raid_disk = -1;
 		set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
 		md_wakeup_thread(rdev->mddev->thread);
@@ -2522,8 +2709,7 @@
 			return err;
 		} else
 			sysfs_notify_dirent_safe(rdev->sysfs_state);
-		sprintf(nm, "rd%d", rdev->raid_disk);
-		if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
+		if (sysfs_link_rdev(rdev->mddev, rdev))
 			/* failure here is OK */;
 		/* don't wakeup anyone, leave that to userspace. */
 	} else {
@@ -2712,6 +2898,39 @@
 static struct rdev_sysfs_entry rdev_recovery_start =
 __ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
 
+
+static ssize_t
+badblocks_show(struct badblocks *bb, char *page, int unack);
+static ssize_t
+badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack);
+
+static ssize_t bb_show(mdk_rdev_t *rdev, char *page)
+{
+	return badblocks_show(&rdev->badblocks, page, 0);
+}
+static ssize_t bb_store(mdk_rdev_t *rdev, const char *page, size_t len)
+{
+	int rv = badblocks_store(&rdev->badblocks, page, len, 0);
+	/* Maybe that ack was all we needed */
+	if (test_and_clear_bit(BlockedBadBlocks, &rdev->flags))
+		wake_up(&rdev->blocked_wait);
+	return rv;
+}
+static struct rdev_sysfs_entry rdev_bad_blocks =
+__ATTR(bad_blocks, S_IRUGO|S_IWUSR, bb_show, bb_store);
+
+
+static ssize_t ubb_show(mdk_rdev_t *rdev, char *page)
+{
+	return badblocks_show(&rdev->badblocks, page, 1);
+}
+static ssize_t ubb_store(mdk_rdev_t *rdev, const char *page, size_t len)
+{
+	return badblocks_store(&rdev->badblocks, page, len, 1);
+}
+static struct rdev_sysfs_entry rdev_unack_bad_blocks =
+__ATTR(unacknowledged_bad_blocks, S_IRUGO|S_IWUSR, ubb_show, ubb_store);
+
 static struct attribute *rdev_default_attrs[] = {
 	&rdev_state.attr,
 	&rdev_errors.attr,
@@ -2719,6 +2938,8 @@
 	&rdev_offset.attr,
 	&rdev_size.attr,
 	&rdev_recovery_start.attr,
+	&rdev_bad_blocks.attr,
+	&rdev_unack_bad_blocks.attr,
 	NULL,
 };
 static ssize_t
@@ -2782,7 +3003,7 @@
 	.default_attrs	= rdev_default_attrs,
 };
 
-void md_rdev_init(mdk_rdev_t *rdev)
+int md_rdev_init(mdk_rdev_t *rdev)
 {
 	rdev->desc_nr = -1;
 	rdev->saved_raid_disk = -1;
@@ -2792,12 +3013,27 @@
 	rdev->sb_events = 0;
 	rdev->last_read_error.tv_sec  = 0;
 	rdev->last_read_error.tv_nsec = 0;
+	rdev->sb_loaded = 0;
+	rdev->bb_page = NULL;
 	atomic_set(&rdev->nr_pending, 0);
 	atomic_set(&rdev->read_errors, 0);
 	atomic_set(&rdev->corrected_errors, 0);
 
 	INIT_LIST_HEAD(&rdev->same_set);
 	init_waitqueue_head(&rdev->blocked_wait);
+
+	/* Add space to store bad block list.
+	 * This reserves the space even on arrays where it cannot
+	 * be used - I wonder if that matters
+	 */
+	rdev->badblocks.count = 0;
+	rdev->badblocks.shift = 0;
+	rdev->badblocks.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	seqlock_init(&rdev->badblocks.lock);
+	if (rdev->badblocks.page == NULL)
+		return -ENOMEM;
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(md_rdev_init);
 /*
@@ -2823,8 +3059,11 @@
 		return ERR_PTR(-ENOMEM);
 	}
 
-	md_rdev_init(rdev);
-	if ((err = alloc_disk_sb(rdev)))
+	err = md_rdev_init(rdev);
+	if (err)
+		goto abort_free;
+	err = alloc_disk_sb(rdev);
+	if (err)
 		goto abort_free;
 
 	err = lock_rdev(rdev, newdev, super_format == -2);
@@ -2860,15 +3099,17 @@
 			goto abort_free;
 		}
 	}
+	if (super_format == -1)
+		/* hot-add for 0.90, or non-persistent: so no badblocks */
+		rdev->badblocks.shift = -1;
 
 	return rdev;
 
 abort_free:
-	if (rdev->sb_page) {
-		if (rdev->bdev)
-			unlock_rdev(rdev);
-		free_disk_sb(rdev);
-	}
+	if (rdev->bdev)
+		unlock_rdev(rdev);
+	free_disk_sb(rdev);
+	kfree(rdev->badblocks.page);
 	kfree(rdev);
 	return ERR_PTR(err);
 }
@@ -3149,15 +3390,13 @@
 	}
 
 	list_for_each_entry(rdev, &mddev->disks, same_set) {
-		char nm[20];
 		if (rdev->raid_disk < 0)
 			continue;
 		if (rdev->new_raid_disk >= mddev->raid_disks)
 			rdev->new_raid_disk = -1;
 		if (rdev->new_raid_disk == rdev->raid_disk)
 			continue;
-		sprintf(nm, "rd%d", rdev->raid_disk);
-		sysfs_remove_link(&mddev->kobj, nm);
+		sysfs_unlink_rdev(mddev, rdev);
 	}
 	list_for_each_entry(rdev, &mddev->disks, same_set) {
 		if (rdev->raid_disk < 0)
@@ -3168,11 +3407,10 @@
 		if (rdev->raid_disk < 0)
 			clear_bit(In_sync, &rdev->flags);
 		else {
-			char nm[20];
-			sprintf(nm, "rd%d", rdev->raid_disk);
-			if(sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
-				printk("md: cannot register %s for %s after level change\n",
-				       nm, mdname(mddev));
+			if (sysfs_link_rdev(mddev, rdev))
+				printk(KERN_WARNING "md: cannot register rd%d"
+				       " for %s after level change\n",
+				       rdev->raid_disk, mdname(mddev));
 		}
 	}
 
@@ -4504,7 +4742,8 @@
 	}
 
 	if (mddev->bio_set == NULL)
-		mddev->bio_set = bioset_create(BIO_POOL_SIZE, sizeof(mddev));
+		mddev->bio_set = bioset_create(BIO_POOL_SIZE,
+					       sizeof(mddev_t *));
 
 	spin_lock(&pers_lock);
 	pers = find_pers(mddev->level, mddev->clevel);
@@ -4621,12 +4860,9 @@
 	smp_wmb();
 	mddev->ready = 1;
 	list_for_each_entry(rdev, &mddev->disks, same_set)
-		if (rdev->raid_disk >= 0) {
-			char nm[20];
-			sprintf(nm, "rd%d", rdev->raid_disk);
-			if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
+		if (rdev->raid_disk >= 0)
+			if (sysfs_link_rdev(mddev, rdev))
 				/* failure here is OK */;
-		}
 	
 	set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
 	
@@ -4854,11 +5090,8 @@
 		sysfs_notify_dirent_safe(mddev->sysfs_state);
 
 		list_for_each_entry(rdev, &mddev->disks, same_set)
-			if (rdev->raid_disk >= 0) {
-				char nm[20];
-				sprintf(nm, "rd%d", rdev->raid_disk);
-				sysfs_remove_link(&mddev->kobj, nm);
-			}
+			if (rdev->raid_disk >= 0)
+				sysfs_unlink_rdev(mddev, rdev);
 
 		set_capacity(disk, 0);
 		mutex_unlock(&mddev->open_mutex);
@@ -6198,18 +6431,7 @@
 	if (!rdev || test_bit(Faulty, &rdev->flags))
 		return;
 
-	if (mddev->external)
-		set_bit(Blocked, &rdev->flags);
-/*
-	dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n",
-		mdname(mddev),
-		MAJOR(rdev->bdev->bd_dev), MINOR(rdev->bdev->bd_dev),
-		__builtin_return_address(0),__builtin_return_address(1),
-		__builtin_return_address(2),__builtin_return_address(3));
-*/
-	if (!mddev->pers)
-		return;
-	if (!mddev->pers->error_handler)
+	if (!mddev->pers || !mddev->pers->error_handler)
 		return;
 	mddev->pers->error_handler(mddev,rdev);
 	if (mddev->degraded)
@@ -6933,11 +7155,14 @@
 			atomic_add(sectors, &mddev->recovery_active);
 		}
 
+		if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+			break;
+
 		j += sectors;
 		if (j>1) mddev->curr_resync = j;
 		mddev->curr_mark_cnt = io_sectors;
 		if (last_check == 0)
-			/* this is the earliers that rebuilt will be
+			/* this is the earliest that rebuild will be
 			 * visible in /proc/mdstat
 			 */
 			md_new_event(mddev);
@@ -6946,10 +7171,6 @@
 			continue;
 
 		last_check = io_sectors;
-
-		if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
-			break;
-
 	repeat:
 		if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {
 			/* step marks */
@@ -7067,29 +7288,23 @@
 		    atomic_read(&rdev->nr_pending)==0) {
 			if (mddev->pers->hot_remove_disk(
 				    mddev, rdev->raid_disk)==0) {
-				char nm[20];
-				sprintf(nm,"rd%d", rdev->raid_disk);
-				sysfs_remove_link(&mddev->kobj, nm);
+				sysfs_unlink_rdev(mddev, rdev);
 				rdev->raid_disk = -1;
 			}
 		}
 
-	if (mddev->degraded && !mddev->recovery_disabled) {
+	if (mddev->degraded) {
 		list_for_each_entry(rdev, &mddev->disks, same_set) {
 			if (rdev->raid_disk >= 0 &&
 			    !test_bit(In_sync, &rdev->flags) &&
-			    !test_bit(Faulty, &rdev->flags) &&
-			    !test_bit(Blocked, &rdev->flags))
+			    !test_bit(Faulty, &rdev->flags))
 				spares++;
 			if (rdev->raid_disk < 0
 			    && !test_bit(Faulty, &rdev->flags)) {
 				rdev->recovery_offset = 0;
 				if (mddev->pers->
 				    hot_add_disk(mddev, rdev) == 0) {
-					char nm[20];
-					sprintf(nm, "rd%d", rdev->raid_disk);
-					if (sysfs_create_link(&mddev->kobj,
-							      &rdev->kobj, nm))
+					if (sysfs_link_rdev(mddev, rdev))
 						/* failure here is OK */;
 					spares++;
 					md_new_event(mddev);
@@ -7138,6 +7353,8 @@
 	set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
 	sysfs_notify_dirent_safe(mddev->sysfs_action);
 	md_new_event(mddev);
+	if (mddev->event_work.func)
+		queue_work(md_misc_wq, &mddev->event_work);
 }
 
 /*
@@ -7170,9 +7387,6 @@
 	if (mddev->bitmap)
 		bitmap_daemon_work(mddev);
 
-	if (mddev->ro)
-		return;
-
 	if (signal_pending(current)) {
 		if (mddev->pers->sync_request && !mddev->external) {
 			printk(KERN_INFO "md: %s in immediate safe mode\n",
@@ -7209,9 +7423,7 @@
 				    atomic_read(&rdev->nr_pending)==0) {
 					if (mddev->pers->hot_remove_disk(
 						    mddev, rdev->raid_disk)==0) {
-						char nm[20];
-						sprintf(nm,"rd%d", rdev->raid_disk);
-						sysfs_remove_link(&mddev->kobj, nm);
+						sysfs_unlink_rdev(mddev, rdev);
 						rdev->raid_disk = -1;
 					}
 				}
@@ -7331,12 +7543,499 @@
 {
 	sysfs_notify_dirent_safe(rdev->sysfs_state);
 	wait_event_timeout(rdev->blocked_wait,
-			   !test_bit(Blocked, &rdev->flags),
+			   !test_bit(Blocked, &rdev->flags) &&
+			   !test_bit(BlockedBadBlocks, &rdev->flags),
 			   msecs_to_jiffies(5000));
 	rdev_dec_pending(rdev, mddev);
 }
 EXPORT_SYMBOL(md_wait_for_blocked_rdev);
 
+
+/* Bad block management.
+ * We can record which blocks on each device are 'bad' and so just
+ * fail those blocks, or that stripe, rather than the whole device.
+ * Entries in the bad-block table are 64bits wide.  This comprises:
+ * Length of bad-range, in sectors: 0-511 for lengths 1-512
+ * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes)
+ *  A 'shift' can be set so that larger blocks are tracked and
+ *  consequently larger devices can be covered.
+ * 'Acknowledged' flag - 1 bit. - the most significant bit.
+ *
+ * Locking of the bad-block table uses a seqlock so md_is_badblock
+ * might need to retry if it is very unlucky.
+ * We will sometimes want to check for bad blocks in a bi_end_io function,
+ * so we use the write_seqlock_irq variant.
+ *
+ * When looking for a bad block we specify a range and want to
+ * know if any block in the range is bad.  So we binary-search
+ * to the last range that starts at-or-before the given endpoint,
+ * (or "before the sector after the target range")
+ * then see if it ends after the given start.
+ * We return
+ *  0 if there are no known bad blocks in the range
+ *  1 if there are known bad block which are all acknowledged
+ * -1 if there are bad blocks which have not yet been acknowledged in metadata.
+ * plus the start/length of the first bad section we overlap.
+ */
+int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
+		   sector_t *first_bad, int *bad_sectors)
+{
+	int hi;
+	int lo = 0;
+	u64 *p = bb->page;
+	int rv = 0;
+	sector_t target = s + sectors;
+	unsigned seq;
+
+	if (bb->shift > 0) {
+		/* round the start down, and the end up */
+		s >>= bb->shift;
+		target += (1<<bb->shift) - 1;
+		target >>= bb->shift;
+		sectors = target - s;
+	}
+	/* 'target' is now the first block after the bad range */
+
+retry:
+	seq = read_seqbegin(&bb->lock);
+
+	hi = bb->count;
+
+	/* Binary search between lo and hi for 'target'
+	 * i.e. for the last range that starts before 'target'
+	 */
+	/* INVARIANT: ranges before 'lo' and at-or-after 'hi'
+	 * are known not to be the last range before target.
+	 * VARIANT: hi-lo is the number of possible
+	 * ranges, and decreases until it reaches 1
+	 */
+	while (hi - lo > 1) {
+		int mid = (lo + hi) / 2;
+		sector_t a = BB_OFFSET(p[mid]);
+		if (a < target)
+			/* This could still be the one, earlier ranges
+			 * could not. */
+			lo = mid;
+		else
+			/* This and later ranges are definitely out. */
+			hi = mid;
+	}
+	/* 'lo' might be the last that started before target, but 'hi' isn't */
+	if (hi > lo) {
+		/* need to check all range that end after 's' to see if
+		 * any are unacknowledged.
+		 */
+		while (lo >= 0 &&
+		       BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+			if (BB_OFFSET(p[lo]) < target) {
+				/* starts before the end, and finishes after
+				 * the start, so they must overlap
+				 */
+				if (rv != -1 && BB_ACK(p[lo]))
+					rv = 1;
+				else
+					rv = -1;
+				*first_bad = BB_OFFSET(p[lo]);
+				*bad_sectors = BB_LEN(p[lo]);
+			}
+			lo--;
+		}
+	}
+
+	if (read_seqretry(&bb->lock, seq))
+		goto retry;
+
+	return rv;
+}
+EXPORT_SYMBOL_GPL(md_is_badblock);
+
+/*
+ * Add a range of bad blocks to the table.
+ * This might extend the table, or might contract it
+ * if two adjacent ranges can be merged.
+ * We binary-search to find the 'insertion' point, then
+ * decide how best to handle it.
+ */
+static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
+			    int acknowledged)
+{
+	u64 *p;
+	int lo, hi;
+	int rv = 1;
+
+	if (bb->shift < 0)
+		/* badblocks are disabled */
+		return 0;
+
+	if (bb->shift) {
+		/* round the start down, and the end up */
+		sector_t next = s + sectors;
+		s >>= bb->shift;
+		next += (1<<bb->shift) - 1;
+		next >>= bb->shift;
+		sectors = next - s;
+	}
+
+	write_seqlock_irq(&bb->lock);
+
+	p = bb->page;
+	lo = 0;
+	hi = bb->count;
+	/* Find the last range that starts at-or-before 's' */
+	while (hi - lo > 1) {
+		int mid = (lo + hi) / 2;
+		sector_t a = BB_OFFSET(p[mid]);
+		if (a <= s)
+			lo = mid;
+		else
+			hi = mid;
+	}
+	if (hi > lo && BB_OFFSET(p[lo]) > s)
+		hi = lo;
+
+	if (hi > lo) {
+		/* we found a range that might merge with the start
+		 * of our new range
+		 */
+		sector_t a = BB_OFFSET(p[lo]);
+		sector_t e = a + BB_LEN(p[lo]);
+		int ack = BB_ACK(p[lo]);
+		if (e >= s) {
+			/* Yes, we can merge with a previous range */
+			if (s == a && s + sectors >= e)
+				/* new range covers old */
+				ack = acknowledged;
+			else
+				ack = ack && acknowledged;
+
+			if (e < s + sectors)
+				e = s + sectors;
+			if (e - a <= BB_MAX_LEN) {
+				p[lo] = BB_MAKE(a, e-a, ack);
+				s = e;
+			} else {
+				/* does not all fit in one range,
+				 * make p[lo] maximal
+				 */
+				if (BB_LEN(p[lo]) != BB_MAX_LEN)
+					p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
+				s = a + BB_MAX_LEN;
+			}
+			sectors = e - s;
+		}
+	}
+	if (sectors && hi < bb->count) {
+		/* 'hi' points to the first range that starts after 's'.
+		 * Maybe we can merge with the start of that range */
+		sector_t a = BB_OFFSET(p[hi]);
+		sector_t e = a + BB_LEN(p[hi]);
+		int ack = BB_ACK(p[hi]);
+		if (a <= s + sectors) {
+			/* merging is possible */
+			if (e <= s + sectors) {
+				/* full overlap */
+				e = s + sectors;
+				ack = acknowledged;
+			} else
+				ack = ack && acknowledged;
+
+			a = s;
+			if (e - a <= BB_MAX_LEN) {
+				p[hi] = BB_MAKE(a, e-a, ack);
+				s = e;
+			} else {
+				p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
+				s = a + BB_MAX_LEN;
+			}
+			sectors = e - s;
+			lo = hi;
+			hi++;
+		}
+	}
+	if (sectors == 0 && hi < bb->count) {
+		/* we might be able to combine lo and hi */
+		/* Note: 's' is at the end of 'lo' */
+		sector_t a = BB_OFFSET(p[hi]);
+		int lolen = BB_LEN(p[lo]);
+		int hilen = BB_LEN(p[hi]);
+		int newlen = lolen + hilen - (s - a);
+		if (s >= a && newlen < BB_MAX_LEN) {
+			/* yes, we can combine them */
+			int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
+			p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
+			memmove(p + hi, p + hi + 1,
+				(bb->count - hi - 1) * 8);
+			bb->count--;
+		}
+	}
+	while (sectors) {
+		/* didn't merge (it all).
+		 * Need to add a range just before 'hi' */
+		if (bb->count >= MD_MAX_BADBLOCKS) {
+			/* No room for more */
+			rv = 0;
+			break;
+		} else {
+			int this_sectors = sectors;
+			memmove(p + hi + 1, p + hi,
+				(bb->count - hi) * 8);
+			bb->count++;
+
+			if (this_sectors > BB_MAX_LEN)
+				this_sectors = BB_MAX_LEN;
+			p[hi] = BB_MAKE(s, this_sectors, acknowledged);
+			sectors -= this_sectors;
+			s += this_sectors;
+		}
+	}
+
+	bb->changed = 1;
+	if (!acknowledged)
+		bb->unacked_exist = 1;
+	write_sequnlock_irq(&bb->lock);
+
+	return rv;
+}
+
+int rdev_set_badblocks(mdk_rdev_t *rdev, sector_t s, int sectors,
+		       int acknowledged)
+{
+	int rv = md_set_badblocks(&rdev->badblocks,
+				  s + rdev->data_offset, sectors, acknowledged);
+	if (rv) {
+		/* Make sure they get written out promptly */
+		set_bit(MD_CHANGE_CLEAN, &rdev->mddev->flags);
+		md_wakeup_thread(rdev->mddev->thread);
+	}
+	return rv;
+}
+EXPORT_SYMBOL_GPL(rdev_set_badblocks);
+
+/*
+ * Remove a range of bad blocks from the table.
+ * This may involve extending the table if we spilt a region,
+ * but it must not fail.  So if the table becomes full, we just
+ * drop the remove request.
+ */
+static int md_clear_badblocks(struct badblocks *bb, sector_t s, int sectors)
+{
+	u64 *p;
+	int lo, hi;
+	sector_t target = s + sectors;
+	int rv = 0;
+
+	if (bb->shift > 0) {
+		/* When clearing we round the start up and the end down.
+		 * This should not matter as the shift should align with
+		 * the block size and no rounding should ever be needed.
+		 * However it is better the think a block is bad when it
+		 * isn't than to think a block is not bad when it is.
+		 */
+		s += (1<<bb->shift) - 1;
+		s >>= bb->shift;
+		target >>= bb->shift;
+		sectors = target - s;
+	}
+
+	write_seqlock_irq(&bb->lock);
+
+	p = bb->page;
+	lo = 0;
+	hi = bb->count;
+	/* Find the last range that starts before 'target' */
+	while (hi - lo > 1) {
+		int mid = (lo + hi) / 2;
+		sector_t a = BB_OFFSET(p[mid]);
+		if (a < target)
+			lo = mid;
+		else
+			hi = mid;
+	}
+	if (hi > lo) {
+		/* p[lo] is the last range that could overlap the
+		 * current range.  Earlier ranges could also overlap,
+		 * but only this one can overlap the end of the range.
+		 */
+		if (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) {
+			/* Partial overlap, leave the tail of this range */
+			int ack = BB_ACK(p[lo]);
+			sector_t a = BB_OFFSET(p[lo]);
+			sector_t end = a + BB_LEN(p[lo]);
+
+			if (a < s) {
+				/* we need to split this range */
+				if (bb->count >= MD_MAX_BADBLOCKS) {
+					rv = 0;
+					goto out;
+				}
+				memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
+				bb->count++;
+				p[lo] = BB_MAKE(a, s-a, ack);
+				lo++;
+			}
+			p[lo] = BB_MAKE(target, end - target, ack);
+			/* there is no longer an overlap */
+			hi = lo;
+			lo--;
+		}
+		while (lo >= 0 &&
+		       BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+			/* This range does overlap */
+			if (BB_OFFSET(p[lo]) < s) {
+				/* Keep the early parts of this range. */
+				int ack = BB_ACK(p[lo]);
+				sector_t start = BB_OFFSET(p[lo]);
+				p[lo] = BB_MAKE(start, s - start, ack);
+				/* now low doesn't overlap, so.. */
+				break;
+			}
+			lo--;
+		}
+		/* 'lo' is strictly before, 'hi' is strictly after,
+		 * anything between needs to be discarded
+		 */
+		if (hi - lo > 1) {
+			memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
+			bb->count -= (hi - lo - 1);
+		}
+	}
+
+	bb->changed = 1;
+out:
+	write_sequnlock_irq(&bb->lock);
+	return rv;
+}
+
+int rdev_clear_badblocks(mdk_rdev_t *rdev, sector_t s, int sectors)
+{
+	return md_clear_badblocks(&rdev->badblocks,
+				  s + rdev->data_offset,
+				  sectors);
+}
+EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
+
+/*
+ * Acknowledge all bad blocks in a list.
+ * This only succeeds if ->changed is clear.  It is used by
+ * in-kernel metadata updates
+ */
+void md_ack_all_badblocks(struct badblocks *bb)
+{
+	if (bb->page == NULL || bb->changed)
+		/* no point even trying */
+		return;
+	write_seqlock_irq(&bb->lock);
+
+	if (bb->changed == 0) {
+		u64 *p = bb->page;
+		int i;
+		for (i = 0; i < bb->count ; i++) {
+			if (!BB_ACK(p[i])) {
+				sector_t start = BB_OFFSET(p[i]);
+				int len = BB_LEN(p[i]);
+				p[i] = BB_MAKE(start, len, 1);
+			}
+		}
+		bb->unacked_exist = 0;
+	}
+	write_sequnlock_irq(&bb->lock);
+}
+EXPORT_SYMBOL_GPL(md_ack_all_badblocks);
+
+/* sysfs access to bad-blocks list.
+ * We present two files.
+ * 'bad-blocks' lists sector numbers and lengths of ranges that
+ *    are recorded as bad.  The list is truncated to fit within
+ *    the one-page limit of sysfs.
+ *    Writing "sector length" to this file adds an acknowledged
+ *    bad block list.
+ * 'unacknowledged-bad-blocks' lists bad blocks that have not yet
+ *    been acknowledged.  Writing to this file adds bad blocks
+ *    without acknowledging them.  This is largely for testing.
+ */
+
+static ssize_t
+badblocks_show(struct badblocks *bb, char *page, int unack)
+{
+	size_t len;
+	int i;
+	u64 *p = bb->page;
+	unsigned seq;
+
+	if (bb->shift < 0)
+		return 0;
+
+retry:
+	seq = read_seqbegin(&bb->lock);
+
+	len = 0;
+	i = 0;
+
+	while (len < PAGE_SIZE && i < bb->count) {
+		sector_t s = BB_OFFSET(p[i]);
+		unsigned int length = BB_LEN(p[i]);
+		int ack = BB_ACK(p[i]);
+		i++;
+
+		if (unack && ack)
+			continue;
+
+		len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
+				(unsigned long long)s << bb->shift,
+				length << bb->shift);
+	}
+	if (unack && len == 0)
+		bb->unacked_exist = 0;
+
+	if (read_seqretry(&bb->lock, seq))
+		goto retry;
+
+	return len;
+}
+
+#define DO_DEBUG 1
+
+static ssize_t
+badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack)
+{
+	unsigned long long sector;
+	int length;
+	char newline;
+#ifdef DO_DEBUG
+	/* Allow clearing via sysfs *only* for testing/debugging.
+	 * Normally only a successful write may clear a badblock
+	 */
+	int clear = 0;
+	if (page[0] == '-') {
+		clear = 1;
+		page++;
+	}
+#endif /* DO_DEBUG */
+
+	switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
+	case 3:
+		if (newline != '\n')
+			return -EINVAL;
+	case 2:
+		if (length <= 0)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+#ifdef DO_DEBUG
+	if (clear) {
+		md_clear_badblocks(bb, sector, length);
+		return len;
+	}
+#endif /* DO_DEBUG */
+	if (md_set_badblocks(bb, sector, length, !unack))
+		return len;
+	else
+		return -ENOSPC;
+}
+
 static int md_notify_reboot(struct notifier_block *this,
 			    unsigned long code, void *x)
 {
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 1c26c7a..1e586bb 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -29,6 +29,13 @@
 typedef struct mddev_s mddev_t;
 typedef struct mdk_rdev_s mdk_rdev_t;
 
+/* Bad block numbers are stored sorted in a single page.
+ * 64bits is used for each block or extent.
+ * 54 bits are sector number, 9 bits are extent size,
+ * 1 bit is an 'acknowledged' flag.
+ */
+#define MD_MAX_BADBLOCKS	(PAGE_SIZE/8)
+
 /*
  * MD's 'extended' device
  */
@@ -48,7 +55,7 @@
 	struct block_device *meta_bdev;
 	struct block_device *bdev;	/* block device handle */
 
-	struct page	*sb_page;
+	struct page	*sb_page, *bb_page;
 	int		sb_loaded;
 	__u64		sb_events;
 	sector_t	data_offset;	/* start of data in array */
@@ -74,9 +81,29 @@
 #define	In_sync		2		/* device is in_sync with rest of array */
 #define	WriteMostly	4		/* Avoid reading if at all possible */
 #define	AutoDetected	7		/* added by auto-detect */
-#define Blocked		8		/* An error occurred on an externally
-					 * managed array, don't allow writes
+#define Blocked		8		/* An error occurred but has not yet
+					 * been acknowledged by the metadata
+					 * handler, so don't allow writes
 					 * until it is cleared */
+#define WriteErrorSeen	9		/* A write error has been seen on this
+					 * device
+					 */
+#define FaultRecorded	10		/* Intermediate state for clearing
+					 * Blocked.  The Fault is/will-be
+					 * recorded in the metadata, but that
+					 * metadata hasn't been stored safely
+					 * on disk yet.
+					 */
+#define BlockedBadBlocks 11		/* A writer is blocked because they
+					 * found an unacknowledged bad-block.
+					 * This can safely be cleared at any
+					 * time, and the writer will re-check.
+					 * It may be set at any time, and at
+					 * worst the writer will timeout and
+					 * re-check.  So setting it as
+					 * accurately as possible is good, but
+					 * not absolutely critical.
+					 */
 	wait_queue_head_t blocked_wait;
 
 	int desc_nr;			/* descriptor index in the superblock */
@@ -111,8 +138,54 @@
 
 	struct sysfs_dirent *sysfs_state; /* handle for 'state'
 					   * sysfs entry */
+
+	struct badblocks {
+		int	count;		/* count of bad blocks */
+		int	unacked_exist;	/* there probably are unacknowledged
+					 * bad blocks.  This is only cleared
+					 * when a read discovers none
+					 */
+		int	shift;		/* shift from sectors to block size
+					 * a -ve shift means badblocks are
+					 * disabled.*/
+		u64	*page;		/* badblock list */
+		int	changed;
+		seqlock_t lock;
+
+		sector_t sector;
+		sector_t size;		/* in sectors */
+	} badblocks;
 };
 
+#define BB_LEN_MASK	(0x00000000000001FFULL)
+#define BB_OFFSET_MASK	(0x7FFFFFFFFFFFFE00ULL)
+#define BB_ACK_MASK	(0x8000000000000000ULL)
+#define BB_MAX_LEN	512
+#define BB_OFFSET(x)	(((x) & BB_OFFSET_MASK) >> 9)
+#define BB_LEN(x)	(((x) & BB_LEN_MASK) + 1)
+#define BB_ACK(x)	(!!((x) & BB_ACK_MASK))
+#define BB_MAKE(a, l, ack) (((a)<<9) | ((l)-1) | ((u64)(!!(ack)) << 63))
+
+extern int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
+			  sector_t *first_bad, int *bad_sectors);
+static inline int is_badblock(mdk_rdev_t *rdev, sector_t s, int sectors,
+			      sector_t *first_bad, int *bad_sectors)
+{
+	if (unlikely(rdev->badblocks.count)) {
+		int rv = md_is_badblock(&rdev->badblocks, rdev->data_offset + s,
+					sectors,
+					first_bad, bad_sectors);
+		if (rv)
+			*first_bad -= rdev->data_offset;
+		return rv;
+	}
+	return 0;
+}
+extern int rdev_set_badblocks(mdk_rdev_t *rdev, sector_t s, int sectors,
+			      int acknowledged);
+extern int rdev_clear_badblocks(mdk_rdev_t *rdev, sector_t s, int sectors);
+extern void md_ack_all_badblocks(struct badblocks *bb);
+
 struct mddev_s
 {
 	void				*private;
@@ -239,9 +312,12 @@
 #define	MD_RECOVERY_FROZEN	9
 
 	unsigned long			recovery;
-	int				recovery_disabled; /* if we detect that recovery
-							    * will always fail, set this
-							    * so we don't loop trying */
+	/* If a RAID personality determines that recovery (of a particular
+	 * device) will fail due to a read error on the source device, it
+	 * takes a copy of this number and does not attempt recovery again
+	 * until this number changes.
+	 */
+	int				recovery_disabled;
 
 	int				in_sync;	/* know to not need resync */
 	/* 'open_mutex' avoids races between 'md_open' and 'do_md_stop', so
@@ -304,11 +380,6 @@
 							 * hot-adding a bitmap.  It should
 							 * eventually be settable by sysfs.
 							 */
-		/* When md is serving under dm, it might use a
-		 * dirty_log to store the bits.
-		 */
-		struct dm_dirty_log *log;
-
 		struct mutex		mutex;
 		unsigned long		chunksize;
 		unsigned long		daemon_sleep; /* how many jiffies between updates? */
@@ -413,6 +484,20 @@
 	return mddev->gendisk ? mddev->gendisk->disk_name : "mdX";
 }
 
+static inline int sysfs_link_rdev(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+	char nm[20];
+	sprintf(nm, "rd%d", rdev->raid_disk);
+	return sysfs_create_link(&mddev->kobj, &rdev->kobj, nm);
+}
+
+static inline void sysfs_unlink_rdev(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+	char nm[20];
+	sprintf(nm, "rd%d", rdev->raid_disk);
+	sysfs_remove_link(&mddev->kobj, nm);
+}
+
 /*
  * iterates through some rdev ringlist. It's safe to remove the
  * current 'rdev'. Dont touch 'tmp' though.
@@ -505,7 +590,7 @@
 extern int md_run(mddev_t *mddev);
 extern void md_stop(mddev_t *mddev);
 extern void md_stop_writes(mddev_t *mddev);
-extern void md_rdev_init(mdk_rdev_t *rdev);
+extern int md_rdev_init(mdk_rdev_t *rdev);
 
 extern void mddev_suspend(mddev_t *mddev);
 extern void mddev_resume(mddev_t *mddev);
@@ -514,4 +599,5 @@
 extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
 				   mddev_t *mddev);
 extern int mddev_check_plugged(mddev_t *mddev);
+extern void md_trim_bio(struct bio *bio, int offset, int size);
 #endif /* _MD_MD_H */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index f7431b6..32323f0 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -35,16 +35,13 @@
 #include <linux/delay.h>
 #include <linux/blkdev.h>
 #include <linux/seq_file.h>
+#include <linux/ratelimit.h>
 #include "md.h"
 #include "raid1.h"
 #include "bitmap.h"
 
 #define DEBUG 0
-#if DEBUG
-#define PRINTK(x...) printk(x)
-#else
-#define PRINTK(x...)
-#endif
+#define PRINTK(x...) do { if (DEBUG) printk(x); } while (0)
 
 /*
  * Number of guaranteed r1bios in case of extreme VM load:
@@ -166,7 +163,7 @@
 
 	for (i = 0; i < conf->raid_disks; i++) {
 		struct bio **bio = r1_bio->bios + i;
-		if (*bio && *bio != IO_BLOCKED)
+		if (!BIO_SPECIAL(*bio))
 			bio_put(*bio);
 		*bio = NULL;
 	}
@@ -176,12 +173,6 @@
 {
 	conf_t *conf = r1_bio->mddev->private;
 
-	/*
-	 * Wake up any possible resync thread that waits for the device
-	 * to go idle.
-	 */
-	allow_barrier(conf);
-
 	put_all_bios(conf, r1_bio);
 	mempool_free(r1_bio, conf->r1bio_pool);
 }
@@ -222,6 +213,33 @@
  * operation and are ready to return a success/failure code to the buffer
  * cache layer.
  */
+static void call_bio_endio(r1bio_t *r1_bio)
+{
+	struct bio *bio = r1_bio->master_bio;
+	int done;
+	conf_t *conf = r1_bio->mddev->private;
+
+	if (bio->bi_phys_segments) {
+		unsigned long flags;
+		spin_lock_irqsave(&conf->device_lock, flags);
+		bio->bi_phys_segments--;
+		done = (bio->bi_phys_segments == 0);
+		spin_unlock_irqrestore(&conf->device_lock, flags);
+	} else
+		done = 1;
+
+	if (!test_bit(R1BIO_Uptodate, &r1_bio->state))
+		clear_bit(BIO_UPTODATE, &bio->bi_flags);
+	if (done) {
+		bio_endio(bio, 0);
+		/*
+		 * Wake up any possible resync thread that waits for the device
+		 * to go idle.
+		 */
+		allow_barrier(conf);
+	}
+}
+
 static void raid_end_bio_io(r1bio_t *r1_bio)
 {
 	struct bio *bio = r1_bio->master_bio;
@@ -234,8 +252,7 @@
 			(unsigned long long) bio->bi_sector +
 				(bio->bi_size >> 9) - 1);
 
-		bio_endio(bio,
-			test_bit(R1BIO_Uptodate, &r1_bio->state) ? 0 : -EIO);
+		call_bio_endio(r1_bio);
 	}
 	free_r1bio(r1_bio);
 }
@@ -287,36 +304,52 @@
 		 * oops, read error:
 		 */
 		char b[BDEVNAME_SIZE];
-		if (printk_ratelimit())
-			printk(KERN_ERR "md/raid1:%s: %s: rescheduling sector %llu\n",
-			       mdname(conf->mddev),
-			       bdevname(conf->mirrors[mirror].rdev->bdev,b), (unsigned long long)r1_bio->sector);
+		printk_ratelimited(
+			KERN_ERR "md/raid1:%s: %s: "
+			"rescheduling sector %llu\n",
+			mdname(conf->mddev),
+			bdevname(conf->mirrors[mirror].rdev->bdev,
+				 b),
+			(unsigned long long)r1_bio->sector);
+		set_bit(R1BIO_ReadError, &r1_bio->state);
 		reschedule_retry(r1_bio);
 	}
 
 	rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev);
 }
 
+static void close_write(r1bio_t *r1_bio)
+{
+	/* it really is the end of this request */
+	if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+		/* free extra copy of the data pages */
+		int i = r1_bio->behind_page_count;
+		while (i--)
+			safe_put_page(r1_bio->behind_bvecs[i].bv_page);
+		kfree(r1_bio->behind_bvecs);
+		r1_bio->behind_bvecs = NULL;
+	}
+	/* clear the bitmap if all writes complete successfully */
+	bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
+			r1_bio->sectors,
+			!test_bit(R1BIO_Degraded, &r1_bio->state),
+			test_bit(R1BIO_BehindIO, &r1_bio->state));
+	md_write_end(r1_bio->mddev);
+}
+
 static void r1_bio_write_done(r1bio_t *r1_bio)
 {
-	if (atomic_dec_and_test(&r1_bio->remaining))
-	{
-		/* it really is the end of this request */
-		if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
-			/* free extra copy of the data pages */
-			int i = r1_bio->behind_page_count;
-			while (i--)
-				safe_put_page(r1_bio->behind_pages[i]);
-			kfree(r1_bio->behind_pages);
-			r1_bio->behind_pages = NULL;
-		}
-		/* clear the bitmap if all writes complete successfully */
-		bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
-				r1_bio->sectors,
-				!test_bit(R1BIO_Degraded, &r1_bio->state),
-				test_bit(R1BIO_BehindIO, &r1_bio->state));
-		md_write_end(r1_bio->mddev);
-		raid_end_bio_io(r1_bio);
+	if (!atomic_dec_and_test(&r1_bio->remaining))
+		return;
+
+	if (test_bit(R1BIO_WriteError, &r1_bio->state))
+		reschedule_retry(r1_bio);
+	else {
+		close_write(r1_bio);
+		if (test_bit(R1BIO_MadeGood, &r1_bio->state))
+			reschedule_retry(r1_bio);
+		else
+			raid_end_bio_io(r1_bio);
 	}
 }
 
@@ -336,13 +369,11 @@
 	/*
 	 * 'one mirror IO has finished' event handler:
 	 */
-	r1_bio->bios[mirror] = NULL;
-	to_put = bio;
 	if (!uptodate) {
-		md_error(r1_bio->mddev, conf->mirrors[mirror].rdev);
-		/* an I/O failed, we can't clear the bitmap */
-		set_bit(R1BIO_Degraded, &r1_bio->state);
-	} else
+		set_bit(WriteErrorSeen,
+			&conf->mirrors[mirror].rdev->flags);
+		set_bit(R1BIO_WriteError, &r1_bio->state);
+	} else {
 		/*
 		 * Set R1BIO_Uptodate in our master bio, so that we
 		 * will return a good error code for to the higher
@@ -353,8 +384,22 @@
 		 * to user-side. So if something waits for IO, then it
 		 * will wait for the 'master' bio.
 		 */
+		sector_t first_bad;
+		int bad_sectors;
+
+		r1_bio->bios[mirror] = NULL;
+		to_put = bio;
 		set_bit(R1BIO_Uptodate, &r1_bio->state);
 
+		/* Maybe we can clear some bad blocks. */
+		if (is_badblock(conf->mirrors[mirror].rdev,
+				r1_bio->sector, r1_bio->sectors,
+				&first_bad, &bad_sectors)) {
+			r1_bio->bios[mirror] = IO_MADE_GOOD;
+			set_bit(R1BIO_MadeGood, &r1_bio->state);
+		}
+	}
+
 	update_head_pos(mirror, r1_bio);
 
 	if (behind) {
@@ -377,11 +422,13 @@
 				       (unsigned long long) mbio->bi_sector,
 				       (unsigned long long) mbio->bi_sector +
 				       (mbio->bi_size >> 9) - 1);
-				bio_endio(mbio, 0);
+				call_bio_endio(r1_bio);
 			}
 		}
 	}
-	rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev);
+	if (r1_bio->bios[mirror] == NULL)
+		rdev_dec_pending(conf->mirrors[mirror].rdev,
+				 conf->mddev);
 
 	/*
 	 * Let's see if all mirrored write operations have finished
@@ -408,10 +455,11 @@
  *
  * The rdev for the device selected will have nr_pending incremented.
  */
-static int read_balance(conf_t *conf, r1bio_t *r1_bio)
+static int read_balance(conf_t *conf, r1bio_t *r1_bio, int *max_sectors)
 {
 	const sector_t this_sector = r1_bio->sector;
-	const int sectors = r1_bio->sectors;
+	int sectors;
+	int best_good_sectors;
 	int start_disk;
 	int best_disk;
 	int i;
@@ -426,8 +474,11 @@
 	 * We take the first readable disk when above the resync window.
 	 */
  retry:
+	sectors = r1_bio->sectors;
 	best_disk = -1;
 	best_dist = MaxSector;
+	best_good_sectors = 0;
+
 	if (conf->mddev->recovery_cp < MaxSector &&
 	    (this_sector + sectors >= conf->next_resync)) {
 		choose_first = 1;
@@ -439,6 +490,9 @@
 
 	for (i = 0 ; i < conf->raid_disks ; i++) {
 		sector_t dist;
+		sector_t first_bad;
+		int bad_sectors;
+
 		int disk = start_disk + i;
 		if (disk >= conf->raid_disks)
 			disk -= conf->raid_disks;
@@ -461,6 +515,35 @@
 		/* This is a reasonable device to use.  It might
 		 * even be best.
 		 */
+		if (is_badblock(rdev, this_sector, sectors,
+				&first_bad, &bad_sectors)) {
+			if (best_dist < MaxSector)
+				/* already have a better device */
+				continue;
+			if (first_bad <= this_sector) {
+				/* cannot read here. If this is the 'primary'
+				 * device, then we must not read beyond
+				 * bad_sectors from another device..
+				 */
+				bad_sectors -= (this_sector - first_bad);
+				if (choose_first && sectors > bad_sectors)
+					sectors = bad_sectors;
+				if (best_good_sectors > sectors)
+					best_good_sectors = sectors;
+
+			} else {
+				sector_t good_sectors = first_bad - this_sector;
+				if (good_sectors > best_good_sectors) {
+					best_good_sectors = good_sectors;
+					best_disk = disk;
+				}
+				if (choose_first)
+					break;
+			}
+			continue;
+		} else
+			best_good_sectors = sectors;
+
 		dist = abs(this_sector - conf->mirrors[disk].head_position);
 		if (choose_first
 		    /* Don't change to another disk for sequential reads */
@@ -489,10 +572,12 @@
 			rdev_dec_pending(rdev, conf->mddev);
 			goto retry;
 		}
+		sectors = best_good_sectors;
 		conf->next_seq_sect = this_sector + sectors;
 		conf->last_used = best_disk;
 	}
 	rcu_read_unlock();
+	*max_sectors = sectors;
 
 	return best_disk;
 }
@@ -672,30 +757,31 @@
 {
 	int i;
 	struct bio_vec *bvec;
-	struct page **pages = kzalloc(bio->bi_vcnt * sizeof(struct page*),
+	struct bio_vec *bvecs = kzalloc(bio->bi_vcnt * sizeof(struct bio_vec),
 					GFP_NOIO);
-	if (unlikely(!pages))
+	if (unlikely(!bvecs))
 		return;
 
 	bio_for_each_segment(bvec, bio, i) {
-		pages[i] = alloc_page(GFP_NOIO);
-		if (unlikely(!pages[i]))
+		bvecs[i] = *bvec;
+		bvecs[i].bv_page = alloc_page(GFP_NOIO);
+		if (unlikely(!bvecs[i].bv_page))
 			goto do_sync_io;
-		memcpy(kmap(pages[i]) + bvec->bv_offset,
-			kmap(bvec->bv_page) + bvec->bv_offset, bvec->bv_len);
-		kunmap(pages[i]);
+		memcpy(kmap(bvecs[i].bv_page) + bvec->bv_offset,
+		       kmap(bvec->bv_page) + bvec->bv_offset, bvec->bv_len);
+		kunmap(bvecs[i].bv_page);
 		kunmap(bvec->bv_page);
 	}
-	r1_bio->behind_pages = pages;
+	r1_bio->behind_bvecs = bvecs;
 	r1_bio->behind_page_count = bio->bi_vcnt;
 	set_bit(R1BIO_BehindIO, &r1_bio->state);
 	return;
 
 do_sync_io:
 	for (i = 0; i < bio->bi_vcnt; i++)
-		if (pages[i])
-			put_page(pages[i]);
-	kfree(pages);
+		if (bvecs[i].bv_page)
+			put_page(bvecs[i].bv_page);
+	kfree(bvecs);
 	PRINTK("%dB behind alloc failed, doing sync I/O\n", bio->bi_size);
 }
 
@@ -705,7 +791,7 @@
 	mirror_info_t *mirror;
 	r1bio_t *r1_bio;
 	struct bio *read_bio;
-	int i, targets = 0, disks;
+	int i, disks;
 	struct bitmap *bitmap;
 	unsigned long flags;
 	const int rw = bio_data_dir(bio);
@@ -713,6 +799,9 @@
 	const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA));
 	mdk_rdev_t *blocked_rdev;
 	int plugged;
+	int first_clone;
+	int sectors_handled;
+	int max_sectors;
 
 	/*
 	 * Register the new request and wait if the reconstruction
@@ -759,11 +848,24 @@
 	r1_bio->mddev = mddev;
 	r1_bio->sector = bio->bi_sector;
 
+	/* We might need to issue multiple reads to different
+	 * devices if there are bad blocks around, so we keep
+	 * track of the number of reads in bio->bi_phys_segments.
+	 * If this is 0, there is only one r1_bio and no locking
+	 * will be needed when requests complete.  If it is
+	 * non-zero, then it is the number of not-completed requests.
+	 */
+	bio->bi_phys_segments = 0;
+	clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
 	if (rw == READ) {
 		/*
 		 * read balancing logic:
 		 */
-		int rdisk = read_balance(conf, r1_bio);
+		int rdisk;
+
+read_again:
+		rdisk = read_balance(conf, r1_bio, &max_sectors);
 
 		if (rdisk < 0) {
 			/* couldn't find anywhere to read from */
@@ -784,6 +886,8 @@
 		r1_bio->read_disk = rdisk;
 
 		read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+		md_trim_bio(read_bio, r1_bio->sector - bio->bi_sector,
+			    max_sectors);
 
 		r1_bio->bios[rdisk] = read_bio;
 
@@ -793,16 +897,52 @@
 		read_bio->bi_rw = READ | do_sync;
 		read_bio->bi_private = r1_bio;
 
-		generic_make_request(read_bio);
+		if (max_sectors < r1_bio->sectors) {
+			/* could not read all from this device, so we will
+			 * need another r1_bio.
+			 */
+
+			sectors_handled = (r1_bio->sector + max_sectors
+					   - bio->bi_sector);
+			r1_bio->sectors = max_sectors;
+			spin_lock_irq(&conf->device_lock);
+			if (bio->bi_phys_segments == 0)
+				bio->bi_phys_segments = 2;
+			else
+				bio->bi_phys_segments++;
+			spin_unlock_irq(&conf->device_lock);
+			/* Cannot call generic_make_request directly
+			 * as that will be queued in __make_request
+			 * and subsequent mempool_alloc might block waiting
+			 * for it.  So hand bio over to raid1d.
+			 */
+			reschedule_retry(r1_bio);
+
+			r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+
+			r1_bio->master_bio = bio;
+			r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+			r1_bio->state = 0;
+			r1_bio->mddev = mddev;
+			r1_bio->sector = bio->bi_sector + sectors_handled;
+			goto read_again;
+		} else
+			generic_make_request(read_bio);
 		return 0;
 	}
 
 	/*
 	 * WRITE:
 	 */
-	/* first select target devices under spinlock and
+	/* first select target devices under rcu_lock and
 	 * inc refcount on their rdev.  Record them by setting
 	 * bios[x] to bio
+	 * If there are known/acknowledged bad blocks on any device on
+	 * which we have seen a write error, we want to avoid writing those
+	 * blocks.
+	 * This potentially requires several writes to write around
+	 * the bad blocks.  Each set of writes gets it's own r1bio
+	 * with a set of bios attached.
 	 */
 	plugged = mddev_check_plugged(mddev);
 
@@ -810,6 +950,7 @@
  retry_write:
 	blocked_rdev = NULL;
 	rcu_read_lock();
+	max_sectors = r1_bio->sectors;
 	for (i = 0;  i < disks; i++) {
 		mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
 		if (rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
@@ -817,17 +958,56 @@
 			blocked_rdev = rdev;
 			break;
 		}
-		if (rdev && !test_bit(Faulty, &rdev->flags)) {
-			atomic_inc(&rdev->nr_pending);
-			if (test_bit(Faulty, &rdev->flags)) {
-				rdev_dec_pending(rdev, mddev);
-				r1_bio->bios[i] = NULL;
-			} else {
-				r1_bio->bios[i] = bio;
-				targets++;
+		r1_bio->bios[i] = NULL;
+		if (!rdev || test_bit(Faulty, &rdev->flags)) {
+			set_bit(R1BIO_Degraded, &r1_bio->state);
+			continue;
+		}
+
+		atomic_inc(&rdev->nr_pending);
+		if (test_bit(WriteErrorSeen, &rdev->flags)) {
+			sector_t first_bad;
+			int bad_sectors;
+			int is_bad;
+
+			is_bad = is_badblock(rdev, r1_bio->sector,
+					     max_sectors,
+					     &first_bad, &bad_sectors);
+			if (is_bad < 0) {
+				/* mustn't write here until the bad block is
+				 * acknowledged*/
+				set_bit(BlockedBadBlocks, &rdev->flags);
+				blocked_rdev = rdev;
+				break;
 			}
-		} else
-			r1_bio->bios[i] = NULL;
+			if (is_bad && first_bad <= r1_bio->sector) {
+				/* Cannot write here at all */
+				bad_sectors -= (r1_bio->sector - first_bad);
+				if (bad_sectors < max_sectors)
+					/* mustn't write more than bad_sectors
+					 * to other devices yet
+					 */
+					max_sectors = bad_sectors;
+				rdev_dec_pending(rdev, mddev);
+				/* We don't set R1BIO_Degraded as that
+				 * only applies if the disk is
+				 * missing, so it might be re-added,
+				 * and we want to know to recover this
+				 * chunk.
+				 * In this case the device is here,
+				 * and the fact that this chunk is not
+				 * in-sync is recorded in the bad
+				 * block log
+				 */
+				continue;
+			}
+			if (is_bad) {
+				int good_sectors = first_bad - r1_bio->sector;
+				if (good_sectors < max_sectors)
+					max_sectors = good_sectors;
+			}
+		}
+		r1_bio->bios[i] = bio;
 	}
 	rcu_read_unlock();
 
@@ -838,51 +1018,57 @@
 		for (j = 0; j < i; j++)
 			if (r1_bio->bios[j])
 				rdev_dec_pending(conf->mirrors[j].rdev, mddev);
-
+		r1_bio->state = 0;
 		allow_barrier(conf);
 		md_wait_for_blocked_rdev(blocked_rdev, mddev);
 		wait_barrier(conf);
 		goto retry_write;
 	}
 
-	BUG_ON(targets == 0); /* we never fail the last device */
-
-	if (targets < conf->raid_disks) {
-		/* array is degraded, we will not clear the bitmap
-		 * on I/O completion (see raid1_end_write_request) */
-		set_bit(R1BIO_Degraded, &r1_bio->state);
+	if (max_sectors < r1_bio->sectors) {
+		/* We are splitting this write into multiple parts, so
+		 * we need to prepare for allocating another r1_bio.
+		 */
+		r1_bio->sectors = max_sectors;
+		spin_lock_irq(&conf->device_lock);
+		if (bio->bi_phys_segments == 0)
+			bio->bi_phys_segments = 2;
+		else
+			bio->bi_phys_segments++;
+		spin_unlock_irq(&conf->device_lock);
 	}
-
-	/* do behind I/O ?
-	 * Not if there are too many, or cannot allocate memory,
-	 * or a reader on WriteMostly is waiting for behind writes 
-	 * to flush */
-	if (bitmap &&
-	    (atomic_read(&bitmap->behind_writes)
-	     < mddev->bitmap_info.max_write_behind) &&
-	    !waitqueue_active(&bitmap->behind_wait))
-		alloc_behind_pages(bio, r1_bio);
+	sectors_handled = r1_bio->sector + max_sectors - bio->bi_sector;
 
 	atomic_set(&r1_bio->remaining, 1);
 	atomic_set(&r1_bio->behind_remaining, 0);
 
-	bitmap_startwrite(bitmap, bio->bi_sector, r1_bio->sectors,
-				test_bit(R1BIO_BehindIO, &r1_bio->state));
+	first_clone = 1;
 	for (i = 0; i < disks; i++) {
 		struct bio *mbio;
 		if (!r1_bio->bios[i])
 			continue;
 
 		mbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
-		r1_bio->bios[i] = mbio;
+		md_trim_bio(mbio, r1_bio->sector - bio->bi_sector, max_sectors);
 
-		mbio->bi_sector	= r1_bio->sector + conf->mirrors[i].rdev->data_offset;
-		mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
-		mbio->bi_end_io	= raid1_end_write_request;
-		mbio->bi_rw = WRITE | do_flush_fua | do_sync;
-		mbio->bi_private = r1_bio;
+		if (first_clone) {
+			/* do behind I/O ?
+			 * Not if there are too many, or cannot
+			 * allocate memory, or a reader on WriteMostly
+			 * is waiting for behind writes to flush */
+			if (bitmap &&
+			    (atomic_read(&bitmap->behind_writes)
+			     < mddev->bitmap_info.max_write_behind) &&
+			    !waitqueue_active(&bitmap->behind_wait))
+				alloc_behind_pages(mbio, r1_bio);
 
-		if (r1_bio->behind_pages) {
+			bitmap_startwrite(bitmap, r1_bio->sector,
+					  r1_bio->sectors,
+					  test_bit(R1BIO_BehindIO,
+						   &r1_bio->state));
+			first_clone = 0;
+		}
+		if (r1_bio->behind_bvecs) {
 			struct bio_vec *bvec;
 			int j;
 
@@ -894,11 +1080,20 @@
 			 * them all
 			 */
 			__bio_for_each_segment(bvec, mbio, j, 0)
-				bvec->bv_page = r1_bio->behind_pages[j];
+				bvec->bv_page = r1_bio->behind_bvecs[j].bv_page;
 			if (test_bit(WriteMostly, &conf->mirrors[i].rdev->flags))
 				atomic_inc(&r1_bio->behind_remaining);
 		}
 
+		r1_bio->bios[i] = mbio;
+
+		mbio->bi_sector	= (r1_bio->sector +
+				   conf->mirrors[i].rdev->data_offset);
+		mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
+		mbio->bi_end_io	= raid1_end_write_request;
+		mbio->bi_rw = WRITE | do_flush_fua | do_sync;
+		mbio->bi_private = r1_bio;
+
 		atomic_inc(&r1_bio->remaining);
 		spin_lock_irqsave(&conf->device_lock, flags);
 		bio_list_add(&conf->pending_bio_list, mbio);
@@ -909,6 +1104,19 @@
 	/* In case raid1d snuck in to freeze_array */
 	wake_up(&conf->wait_barrier);
 
+	if (sectors_handled < (bio->bi_size >> 9)) {
+		/* We need another r1_bio.  It has already been counted
+		 * in bio->bi_phys_segments
+		 */
+		r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+		r1_bio->master_bio = bio;
+		r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+		r1_bio->state = 0;
+		r1_bio->mddev = mddev;
+		r1_bio->sector = bio->bi_sector + sectors_handled;
+		goto retry_write;
+	}
+
 	if (do_sync || !bitmap || !plugged)
 		md_wakeup_thread(mddev->thread);
 
@@ -952,9 +1160,10 @@
 		 * However don't try a recovery from this drive as
 		 * it is very likely to fail.
 		 */
-		mddev->recovery_disabled = 1;
+		conf->recovery_disabled = mddev->recovery_disabled;
 		return;
 	}
+	set_bit(Blocked, &rdev->flags);
 	if (test_and_clear_bit(In_sync, &rdev->flags)) {
 		unsigned long flags;
 		spin_lock_irqsave(&conf->device_lock, flags);
@@ -1027,7 +1236,7 @@
 		    && !test_bit(Faulty, &rdev->flags)
 		    && !test_and_set_bit(In_sync, &rdev->flags)) {
 			count++;
-			sysfs_notify_dirent(rdev->sysfs_state);
+			sysfs_notify_dirent_safe(rdev->sysfs_state);
 		}
 	}
 	spin_lock_irqsave(&conf->device_lock, flags);
@@ -1048,6 +1257,9 @@
 	int first = 0;
 	int last = mddev->raid_disks - 1;
 
+	if (mddev->recovery_disabled == conf->recovery_disabled)
+		return -EBUSY;
+
 	if (rdev->raid_disk >= 0)
 		first = last = rdev->raid_disk;
 
@@ -1103,7 +1315,7 @@
 		 * is not possible.
 		 */
 		if (!test_bit(Faulty, &rdev->flags) &&
-		    !mddev->recovery_disabled &&
+		    mddev->recovery_disabled != conf->recovery_disabled &&
 		    mddev->degraded < conf->raid_disks) {
 			err = -EBUSY;
 			goto abort;
@@ -1155,6 +1367,8 @@
 	conf_t *conf = mddev->private;
 	int i;
 	int mirror=0;
+	sector_t first_bad;
+	int bad_sectors;
 
 	for (i = 0; i < conf->raid_disks; i++)
 		if (r1_bio->bios[i] == bio) {
@@ -1172,18 +1386,48 @@
 			s += sync_blocks;
 			sectors_to_go -= sync_blocks;
 		} while (sectors_to_go > 0);
-		md_error(mddev, conf->mirrors[mirror].rdev);
-	}
+		set_bit(WriteErrorSeen,
+			&conf->mirrors[mirror].rdev->flags);
+		set_bit(R1BIO_WriteError, &r1_bio->state);
+	} else if (is_badblock(conf->mirrors[mirror].rdev,
+			       r1_bio->sector,
+			       r1_bio->sectors,
+			       &first_bad, &bad_sectors) &&
+		   !is_badblock(conf->mirrors[r1_bio->read_disk].rdev,
+				r1_bio->sector,
+				r1_bio->sectors,
+				&first_bad, &bad_sectors)
+		)
+		set_bit(R1BIO_MadeGood, &r1_bio->state);
 
 	update_head_pos(mirror, r1_bio);
 
 	if (atomic_dec_and_test(&r1_bio->remaining)) {
-		sector_t s = r1_bio->sectors;
-		put_buf(r1_bio);
-		md_done_sync(mddev, s, uptodate);
+		int s = r1_bio->sectors;
+		if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+		    test_bit(R1BIO_WriteError, &r1_bio->state))
+			reschedule_retry(r1_bio);
+		else {
+			put_buf(r1_bio);
+			md_done_sync(mddev, s, uptodate);
+		}
 	}
 }
 
+static int r1_sync_page_io(mdk_rdev_t *rdev, sector_t sector,
+			    int sectors, struct page *page, int rw)
+{
+	if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
+		/* success */
+		return 1;
+	if (rw == WRITE)
+		set_bit(WriteErrorSeen, &rdev->flags);
+	/* need to record an error - either for the block or the device */
+	if (!rdev_set_badblocks(rdev, sector, sectors, 0))
+		md_error(rdev->mddev, rdev);
+	return 0;
+}
+
 static int fix_sync_read_error(r1bio_t *r1_bio)
 {
 	/* Try some synchronous reads of other devices to get
@@ -1193,6 +1437,9 @@
 	 * We don't need to freeze the array, because being in an
 	 * active sync request, there is no normal IO, and
 	 * no overlapping syncs.
+	 * We don't need to check is_badblock() again as we
+	 * made sure that anything with a bad block in range
+	 * will have bi_end_io clear.
 	 */
 	mddev_t *mddev = r1_bio->mddev;
 	conf_t *conf = mddev->private;
@@ -1217,9 +1464,7 @@
 				 * active, and resync is currently active
 				 */
 				rdev = conf->mirrors[d].rdev;
-				if (sync_page_io(rdev,
-						 sect,
-						 s<<9,
+				if (sync_page_io(rdev, sect, s<<9,
 						 bio->bi_io_vec[idx].bv_page,
 						 READ, false)) {
 					success = 1;
@@ -1233,16 +1478,36 @@
 
 		if (!success) {
 			char b[BDEVNAME_SIZE];
-			/* Cannot read from anywhere, array is toast */
-			md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+			int abort = 0;
+			/* Cannot read from anywhere, this block is lost.
+			 * Record a bad block on each device.  If that doesn't
+			 * work just disable and interrupt the recovery.
+			 * Don't fail devices as that won't really help.
+			 */
 			printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O read error"
 			       " for block %llu\n",
 			       mdname(mddev),
 			       bdevname(bio->bi_bdev, b),
 			       (unsigned long long)r1_bio->sector);
-			md_done_sync(mddev, r1_bio->sectors, 0);
-			put_buf(r1_bio);
-			return 0;
+			for (d = 0; d < conf->raid_disks; d++) {
+				rdev = conf->mirrors[d].rdev;
+				if (!rdev || test_bit(Faulty, &rdev->flags))
+					continue;
+				if (!rdev_set_badblocks(rdev, sect, s, 0))
+					abort = 1;
+			}
+			if (abort) {
+				mddev->recovery_disabled = 1;
+				set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+				md_done_sync(mddev, r1_bio->sectors, 0);
+				put_buf(r1_bio);
+				return 0;
+			}
+			/* Try next page */
+			sectors -= s;
+			sect += s;
+			idx++;
+			continue;
 		}
 
 		start = d;
@@ -1254,16 +1519,12 @@
 			if (r1_bio->bios[d]->bi_end_io != end_sync_read)
 				continue;
 			rdev = conf->mirrors[d].rdev;
-			if (sync_page_io(rdev,
-					 sect,
-					 s<<9,
-					 bio->bi_io_vec[idx].bv_page,
-					 WRITE, false) == 0) {
+			if (r1_sync_page_io(rdev, sect, s,
+					    bio->bi_io_vec[idx].bv_page,
+					    WRITE) == 0) {
 				r1_bio->bios[d]->bi_end_io = NULL;
 				rdev_dec_pending(rdev, mddev);
-				md_error(mddev, rdev);
-			} else
-				atomic_add(s, &rdev->corrected_errors);
+			}
 		}
 		d = start;
 		while (d != r1_bio->read_disk) {
@@ -1273,12 +1534,10 @@
 			if (r1_bio->bios[d]->bi_end_io != end_sync_read)
 				continue;
 			rdev = conf->mirrors[d].rdev;
-			if (sync_page_io(rdev,
-					 sect,
-					 s<<9,
-					 bio->bi_io_vec[idx].bv_page,
-					 READ, false) == 0)
-				md_error(mddev, rdev);
+			if (r1_sync_page_io(rdev, sect, s,
+					    bio->bi_io_vec[idx].bv_page,
+					    READ) != 0)
+				atomic_add(s, &rdev->corrected_errors);
 		}
 		sectors -= s;
 		sect += s;
@@ -1420,7 +1679,7 @@
  *
  *	1.	Retries failed read operations on working mirrors.
  *	2.	Updates the raid superblock when problems encounter.
- *	3.	Performs writes following reads for array syncronising.
+ *	3.	Performs writes following reads for array synchronising.
  */
 
 static void fix_read_error(conf_t *conf, int read_disk,
@@ -1443,9 +1702,14 @@
 			 * which is the thread that might remove
 			 * a device.  If raid1d ever becomes multi-threaded....
 			 */
+			sector_t first_bad;
+			int bad_sectors;
+
 			rdev = conf->mirrors[d].rdev;
 			if (rdev &&
 			    test_bit(In_sync, &rdev->flags) &&
+			    is_badblock(rdev, sect, s,
+					&first_bad, &bad_sectors) == 0 &&
 			    sync_page_io(rdev, sect, s<<9,
 					 conf->tmppage, READ, false))
 				success = 1;
@@ -1457,8 +1721,10 @@
 		} while (!success && d != read_disk);
 
 		if (!success) {
-			/* Cannot read from anywhere -- bye bye array */
-			md_error(mddev, conf->mirrors[read_disk].rdev);
+			/* Cannot read from anywhere - mark it bad */
+			mdk_rdev_t *rdev = conf->mirrors[read_disk].rdev;
+			if (!rdev_set_badblocks(rdev, sect, s, 0))
+				md_error(mddev, rdev);
 			break;
 		}
 		/* write it back and re-read */
@@ -1469,13 +1735,9 @@
 			d--;
 			rdev = conf->mirrors[d].rdev;
 			if (rdev &&
-			    test_bit(In_sync, &rdev->flags)) {
-				if (sync_page_io(rdev, sect, s<<9,
-						 conf->tmppage, WRITE, false)
-				    == 0)
-					/* Well, this device is dead */
-					md_error(mddev, rdev);
-			}
+			    test_bit(In_sync, &rdev->flags))
+				r1_sync_page_io(rdev, sect, s,
+						conf->tmppage, WRITE);
 		}
 		d = start;
 		while (d != read_disk) {
@@ -1486,12 +1748,8 @@
 			rdev = conf->mirrors[d].rdev;
 			if (rdev &&
 			    test_bit(In_sync, &rdev->flags)) {
-				if (sync_page_io(rdev, sect, s<<9,
-						 conf->tmppage, READ, false)
-				    == 0)
-					/* Well, this device is dead */
-					md_error(mddev, rdev);
-				else {
+				if (r1_sync_page_io(rdev, sect, s,
+						    conf->tmppage, READ)) {
 					atomic_add(s, &rdev->corrected_errors);
 					printk(KERN_INFO
 					       "md/raid1:%s: read error corrected "
@@ -1508,21 +1766,255 @@
 	}
 }
 
+static void bi_complete(struct bio *bio, int error)
+{
+	complete((struct completion *)bio->bi_private);
+}
+
+static int submit_bio_wait(int rw, struct bio *bio)
+{
+	struct completion event;
+	rw |= REQ_SYNC;
+
+	init_completion(&event);
+	bio->bi_private = &event;
+	bio->bi_end_io = bi_complete;
+	submit_bio(rw, bio);
+	wait_for_completion(&event);
+
+	return test_bit(BIO_UPTODATE, &bio->bi_flags);
+}
+
+static int narrow_write_error(r1bio_t *r1_bio, int i)
+{
+	mddev_t *mddev = r1_bio->mddev;
+	conf_t *conf = mddev->private;
+	mdk_rdev_t *rdev = conf->mirrors[i].rdev;
+	int vcnt, idx;
+	struct bio_vec *vec;
+
+	/* bio has the data to be written to device 'i' where
+	 * we just recently had a write error.
+	 * We repeatedly clone the bio and trim down to one block,
+	 * then try the write.  Where the write fails we record
+	 * a bad block.
+	 * It is conceivable that the bio doesn't exactly align with
+	 * blocks.  We must handle this somehow.
+	 *
+	 * We currently own a reference on the rdev.
+	 */
+
+	int block_sectors;
+	sector_t sector;
+	int sectors;
+	int sect_to_write = r1_bio->sectors;
+	int ok = 1;
+
+	if (rdev->badblocks.shift < 0)
+		return 0;
+
+	block_sectors = 1 << rdev->badblocks.shift;
+	sector = r1_bio->sector;
+	sectors = ((sector + block_sectors)
+		   & ~(sector_t)(block_sectors - 1))
+		- sector;
+
+	if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+		vcnt = r1_bio->behind_page_count;
+		vec = r1_bio->behind_bvecs;
+		idx = 0;
+		while (vec[idx].bv_page == NULL)
+			idx++;
+	} else {
+		vcnt = r1_bio->master_bio->bi_vcnt;
+		vec = r1_bio->master_bio->bi_io_vec;
+		idx = r1_bio->master_bio->bi_idx;
+	}
+	while (sect_to_write) {
+		struct bio *wbio;
+		if (sectors > sect_to_write)
+			sectors = sect_to_write;
+		/* Write at 'sector' for 'sectors'*/
+
+		wbio = bio_alloc_mddev(GFP_NOIO, vcnt, mddev);
+		memcpy(wbio->bi_io_vec, vec, vcnt * sizeof(struct bio_vec));
+		wbio->bi_sector = r1_bio->sector;
+		wbio->bi_rw = WRITE;
+		wbio->bi_vcnt = vcnt;
+		wbio->bi_size = r1_bio->sectors << 9;
+		wbio->bi_idx = idx;
+
+		md_trim_bio(wbio, sector - r1_bio->sector, sectors);
+		wbio->bi_sector += rdev->data_offset;
+		wbio->bi_bdev = rdev->bdev;
+		if (submit_bio_wait(WRITE, wbio) == 0)
+			/* failure! */
+			ok = rdev_set_badblocks(rdev, sector,
+						sectors, 0)
+				&& ok;
+
+		bio_put(wbio);
+		sect_to_write -= sectors;
+		sector += sectors;
+		sectors = block_sectors;
+	}
+	return ok;
+}
+
+static void handle_sync_write_finished(conf_t *conf, r1bio_t *r1_bio)
+{
+	int m;
+	int s = r1_bio->sectors;
+	for (m = 0; m < conf->raid_disks ; m++) {
+		mdk_rdev_t *rdev = conf->mirrors[m].rdev;
+		struct bio *bio = r1_bio->bios[m];
+		if (bio->bi_end_io == NULL)
+			continue;
+		if (test_bit(BIO_UPTODATE, &bio->bi_flags) &&
+		    test_bit(R1BIO_MadeGood, &r1_bio->state)) {
+			rdev_clear_badblocks(rdev, r1_bio->sector, s);
+		}
+		if (!test_bit(BIO_UPTODATE, &bio->bi_flags) &&
+		    test_bit(R1BIO_WriteError, &r1_bio->state)) {
+			if (!rdev_set_badblocks(rdev, r1_bio->sector, s, 0))
+				md_error(conf->mddev, rdev);
+		}
+	}
+	put_buf(r1_bio);
+	md_done_sync(conf->mddev, s, 1);
+}
+
+static void handle_write_finished(conf_t *conf, r1bio_t *r1_bio)
+{
+	int m;
+	for (m = 0; m < conf->raid_disks ; m++)
+		if (r1_bio->bios[m] == IO_MADE_GOOD) {
+			mdk_rdev_t *rdev = conf->mirrors[m].rdev;
+			rdev_clear_badblocks(rdev,
+					     r1_bio->sector,
+					     r1_bio->sectors);
+			rdev_dec_pending(rdev, conf->mddev);
+		} else if (r1_bio->bios[m] != NULL) {
+			/* This drive got a write error.  We need to
+			 * narrow down and record precise write
+			 * errors.
+			 */
+			if (!narrow_write_error(r1_bio, m)) {
+				md_error(conf->mddev,
+					 conf->mirrors[m].rdev);
+				/* an I/O failed, we can't clear the bitmap */
+				set_bit(R1BIO_Degraded, &r1_bio->state);
+			}
+			rdev_dec_pending(conf->mirrors[m].rdev,
+					 conf->mddev);
+		}
+	if (test_bit(R1BIO_WriteError, &r1_bio->state))
+		close_write(r1_bio);
+	raid_end_bio_io(r1_bio);
+}
+
+static void handle_read_error(conf_t *conf, r1bio_t *r1_bio)
+{
+	int disk;
+	int max_sectors;
+	mddev_t *mddev = conf->mddev;
+	struct bio *bio;
+	char b[BDEVNAME_SIZE];
+	mdk_rdev_t *rdev;
+
+	clear_bit(R1BIO_ReadError, &r1_bio->state);
+	/* we got a read error. Maybe the drive is bad.  Maybe just
+	 * the block and we can fix it.
+	 * We freeze all other IO, and try reading the block from
+	 * other devices.  When we find one, we re-write
+	 * and check it that fixes the read error.
+	 * This is all done synchronously while the array is
+	 * frozen
+	 */
+	if (mddev->ro == 0) {
+		freeze_array(conf);
+		fix_read_error(conf, r1_bio->read_disk,
+			       r1_bio->sector, r1_bio->sectors);
+		unfreeze_array(conf);
+	} else
+		md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+
+	bio = r1_bio->bios[r1_bio->read_disk];
+	bdevname(bio->bi_bdev, b);
+read_more:
+	disk = read_balance(conf, r1_bio, &max_sectors);
+	if (disk == -1) {
+		printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O"
+		       " read error for block %llu\n",
+		       mdname(mddev), b, (unsigned long long)r1_bio->sector);
+		raid_end_bio_io(r1_bio);
+	} else {
+		const unsigned long do_sync
+			= r1_bio->master_bio->bi_rw & REQ_SYNC;
+		if (bio) {
+			r1_bio->bios[r1_bio->read_disk] =
+				mddev->ro ? IO_BLOCKED : NULL;
+			bio_put(bio);
+		}
+		r1_bio->read_disk = disk;
+		bio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev);
+		md_trim_bio(bio, r1_bio->sector - bio->bi_sector, max_sectors);
+		r1_bio->bios[r1_bio->read_disk] = bio;
+		rdev = conf->mirrors[disk].rdev;
+		printk_ratelimited(KERN_ERR
+				   "md/raid1:%s: redirecting sector %llu"
+				   " to other mirror: %s\n",
+				   mdname(mddev),
+				   (unsigned long long)r1_bio->sector,
+				   bdevname(rdev->bdev, b));
+		bio->bi_sector = r1_bio->sector + rdev->data_offset;
+		bio->bi_bdev = rdev->bdev;
+		bio->bi_end_io = raid1_end_read_request;
+		bio->bi_rw = READ | do_sync;
+		bio->bi_private = r1_bio;
+		if (max_sectors < r1_bio->sectors) {
+			/* Drat - have to split this up more */
+			struct bio *mbio = r1_bio->master_bio;
+			int sectors_handled = (r1_bio->sector + max_sectors
+					       - mbio->bi_sector);
+			r1_bio->sectors = max_sectors;
+			spin_lock_irq(&conf->device_lock);
+			if (mbio->bi_phys_segments == 0)
+				mbio->bi_phys_segments = 2;
+			else
+				mbio->bi_phys_segments++;
+			spin_unlock_irq(&conf->device_lock);
+			generic_make_request(bio);
+			bio = NULL;
+
+			r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
+
+			r1_bio->master_bio = mbio;
+			r1_bio->sectors = (mbio->bi_size >> 9)
+					  - sectors_handled;
+			r1_bio->state = 0;
+			set_bit(R1BIO_ReadError, &r1_bio->state);
+			r1_bio->mddev = mddev;
+			r1_bio->sector = mbio->bi_sector + sectors_handled;
+
+			goto read_more;
+		} else
+			generic_make_request(bio);
+	}
+}
+
 static void raid1d(mddev_t *mddev)
 {
 	r1bio_t *r1_bio;
-	struct bio *bio;
 	unsigned long flags;
 	conf_t *conf = mddev->private;
 	struct list_head *head = &conf->retry_list;
-	mdk_rdev_t *rdev;
 	struct blk_plug plug;
 
 	md_check_recovery(mddev);
 
 	blk_start_plug(&plug);
 	for (;;) {
-		char b[BDEVNAME_SIZE];
 
 		if (atomic_read(&mddev->plug_cnt) == 0)
 			flush_pending_writes(conf);
@@ -1539,62 +2031,26 @@
 
 		mddev = r1_bio->mddev;
 		conf = mddev->private;
-		if (test_bit(R1BIO_IsSync, &r1_bio->state))
-			sync_request_write(mddev, r1_bio);
-		else {
-			int disk;
-
-			/* we got a read error. Maybe the drive is bad.  Maybe just
-			 * the block and we can fix it.
-			 * We freeze all other IO, and try reading the block from
-			 * other devices.  When we find one, we re-write
-			 * and check it that fixes the read error.
-			 * This is all done synchronously while the array is
-			 * frozen
+		if (test_bit(R1BIO_IsSync, &r1_bio->state)) {
+			if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+			    test_bit(R1BIO_WriteError, &r1_bio->state))
+				handle_sync_write_finished(conf, r1_bio);
+			else
+				sync_request_write(mddev, r1_bio);
+		} else if (test_bit(R1BIO_MadeGood, &r1_bio->state) ||
+			   test_bit(R1BIO_WriteError, &r1_bio->state))
+			handle_write_finished(conf, r1_bio);
+		else if (test_bit(R1BIO_ReadError, &r1_bio->state))
+			handle_read_error(conf, r1_bio);
+		else
+			/* just a partial read to be scheduled from separate
+			 * context
 			 */
-			if (mddev->ro == 0) {
-				freeze_array(conf);
-				fix_read_error(conf, r1_bio->read_disk,
-					       r1_bio->sector,
-					       r1_bio->sectors);
-				unfreeze_array(conf);
-			} else
-				md_error(mddev,
-					 conf->mirrors[r1_bio->read_disk].rdev);
+			generic_make_request(r1_bio->bios[r1_bio->read_disk]);
 
-			bio = r1_bio->bios[r1_bio->read_disk];
-			if ((disk=read_balance(conf, r1_bio)) == -1) {
-				printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O"
-				       " read error for block %llu\n",
-				       mdname(mddev),
-				       bdevname(bio->bi_bdev,b),
-				       (unsigned long long)r1_bio->sector);
-				raid_end_bio_io(r1_bio);
-			} else {
-				const unsigned long do_sync = r1_bio->master_bio->bi_rw & REQ_SYNC;
-				r1_bio->bios[r1_bio->read_disk] =
-					mddev->ro ? IO_BLOCKED : NULL;
-				r1_bio->read_disk = disk;
-				bio_put(bio);
-				bio = bio_clone_mddev(r1_bio->master_bio,
-						      GFP_NOIO, mddev);
-				r1_bio->bios[r1_bio->read_disk] = bio;
-				rdev = conf->mirrors[disk].rdev;
-				if (printk_ratelimit())
-					printk(KERN_ERR "md/raid1:%s: redirecting sector %llu to"
-					       " other mirror: %s\n",
-					       mdname(mddev),
-					       (unsigned long long)r1_bio->sector,
-					       bdevname(rdev->bdev,b));
-				bio->bi_sector = r1_bio->sector + rdev->data_offset;
-				bio->bi_bdev = rdev->bdev;
-				bio->bi_end_io = raid1_end_read_request;
-				bio->bi_rw = READ | do_sync;
-				bio->bi_private = r1_bio;
-				generic_make_request(bio);
-			}
-		}
 		cond_resched();
+		if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+			md_check_recovery(mddev);
 	}
 	blk_finish_plug(&plug);
 }
@@ -1636,6 +2092,8 @@
 	int write_targets = 0, read_targets = 0;
 	sector_t sync_blocks;
 	int still_degraded = 0;
+	int good_sectors = RESYNC_SECTORS;
+	int min_bad = 0; /* number of sectors that are bad in all devices */
 
 	if (!conf->r1buf_pool)
 		if (init_resync(conf))
@@ -1723,36 +2181,89 @@
 
 		rdev = rcu_dereference(conf->mirrors[i].rdev);
 		if (rdev == NULL ||
-			   test_bit(Faulty, &rdev->flags)) {
+		    test_bit(Faulty, &rdev->flags)) {
 			still_degraded = 1;
-			continue;
 		} else if (!test_bit(In_sync, &rdev->flags)) {
 			bio->bi_rw = WRITE;
 			bio->bi_end_io = end_sync_write;
 			write_targets ++;
 		} else {
 			/* may need to read from here */
-			bio->bi_rw = READ;
-			bio->bi_end_io = end_sync_read;
-			if (test_bit(WriteMostly, &rdev->flags)) {
-				if (wonly < 0)
-					wonly = i;
-			} else {
-				if (disk < 0)
-					disk = i;
+			sector_t first_bad = MaxSector;
+			int bad_sectors;
+
+			if (is_badblock(rdev, sector_nr, good_sectors,
+					&first_bad, &bad_sectors)) {
+				if (first_bad > sector_nr)
+					good_sectors = first_bad - sector_nr;
+				else {
+					bad_sectors -= (sector_nr - first_bad);
+					if (min_bad == 0 ||
+					    min_bad > bad_sectors)
+						min_bad = bad_sectors;
+				}
 			}
-			read_targets++;
+			if (sector_nr < first_bad) {
+				if (test_bit(WriteMostly, &rdev->flags)) {
+					if (wonly < 0)
+						wonly = i;
+				} else {
+					if (disk < 0)
+						disk = i;
+				}
+				bio->bi_rw = READ;
+				bio->bi_end_io = end_sync_read;
+				read_targets++;
+			}
 		}
-		atomic_inc(&rdev->nr_pending);
-		bio->bi_sector = sector_nr + rdev->data_offset;
-		bio->bi_bdev = rdev->bdev;
-		bio->bi_private = r1_bio;
+		if (bio->bi_end_io) {
+			atomic_inc(&rdev->nr_pending);
+			bio->bi_sector = sector_nr + rdev->data_offset;
+			bio->bi_bdev = rdev->bdev;
+			bio->bi_private = r1_bio;
+		}
 	}
 	rcu_read_unlock();
 	if (disk < 0)
 		disk = wonly;
 	r1_bio->read_disk = disk;
 
+	if (read_targets == 0 && min_bad > 0) {
+		/* These sectors are bad on all InSync devices, so we
+		 * need to mark them bad on all write targets
+		 */
+		int ok = 1;
+		for (i = 0 ; i < conf->raid_disks ; i++)
+			if (r1_bio->bios[i]->bi_end_io == end_sync_write) {
+				mdk_rdev_t *rdev =
+					rcu_dereference(conf->mirrors[i].rdev);
+				ok = rdev_set_badblocks(rdev, sector_nr,
+							min_bad, 0
+					) && ok;
+			}
+		set_bit(MD_CHANGE_DEVS, &mddev->flags);
+		*skipped = 1;
+		put_buf(r1_bio);
+
+		if (!ok) {
+			/* Cannot record the badblocks, so need to
+			 * abort the resync.
+			 * If there are multiple read targets, could just
+			 * fail the really bad ones ???
+			 */
+			conf->recovery_disabled = mddev->recovery_disabled;
+			set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+			return 0;
+		} else
+			return min_bad;
+
+	}
+	if (min_bad > 0 && min_bad < good_sectors) {
+		/* only resync enough to reach the next bad->good
+		 * transition */
+		good_sectors = min_bad;
+	}
+
 	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && read_targets > 0)
 		/* extra read targets are also write targets */
 		write_targets += read_targets-1;
@@ -1769,6 +2280,8 @@
 
 	if (max_sector > mddev->resync_max)
 		max_sector = mddev->resync_max; /* Don't do IO beyond here */
+	if (max_sector > sector_nr + good_sectors)
+		max_sector = sector_nr + good_sectors;
 	nr_sectors = 0;
 	sync_blocks = 0;
 	do {
@@ -2154,18 +2667,13 @@
 	for (d = d2 = 0; d < conf->raid_disks; d++) {
 		mdk_rdev_t *rdev = conf->mirrors[d].rdev;
 		if (rdev && rdev->raid_disk != d2) {
-			char nm[20];
-			sprintf(nm, "rd%d", rdev->raid_disk);
-			sysfs_remove_link(&mddev->kobj, nm);
+			sysfs_unlink_rdev(mddev, rdev);
 			rdev->raid_disk = d2;
-			sprintf(nm, "rd%d", rdev->raid_disk);
-			sysfs_remove_link(&mddev->kobj, nm);
-			if (sysfs_create_link(&mddev->kobj,
-					      &rdev->kobj, nm))
+			sysfs_unlink_rdev(mddev, rdev);
+			if (sysfs_link_rdev(mddev, rdev))
 				printk(KERN_WARNING
-				       "md/raid1:%s: cannot register "
-				       "%s\n",
-				       mdname(mddev), nm);
+				       "md/raid1:%s: cannot register rd%d\n",
+				       mdname(mddev), rdev->raid_disk);
 		}
 		if (rdev)
 			newmirrors[d2++].rdev = rdev;
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index e743a64..e0d676b 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -48,6 +48,12 @@
 					    * (fresh device added).
 					    * Cleared when a sync completes.
 					    */
+	int			recovery_disabled; /* when the same as
+						    * mddev->recovery_disabled
+						    * we don't allow recovery
+						    * to be attempted as we
+						    * expect a read error
+						    */
 
 	wait_queue_head_t	wait_barrier;
 
@@ -95,7 +101,7 @@
 
 	struct list_head	retry_list;
 	/* Next two are only valid when R1BIO_BehindIO is set */
-	struct page		**behind_pages;
+	struct bio_vec		*behind_bvecs;
 	int			behind_page_count;
 	/*
 	 * if the IO is in WRITE direction, then multiple bios are used.
@@ -110,13 +116,24 @@
  * correct the read error.  To keep track of bad blocks on a per-bio
  * level, we store IO_BLOCKED in the appropriate 'bios' pointer
  */
-#define IO_BLOCKED ((struct bio*)1)
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context.  So we record
+ * the success by setting bios[n] to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
 
 /* bits for r1bio.state */
 #define	R1BIO_Uptodate	0
 #define	R1BIO_IsSync	1
 #define	R1BIO_Degraded	2
 #define	R1BIO_BehindIO	3
+/* Set ReadError on bios that experience a readerror so that
+ * raid1d knows what to do with them.
+ */
+#define R1BIO_ReadError 4
 /* For write-behind requests, we call bi_end_io when
  * the last non-write-behind device completes, providing
  * any write was successful.  Otherwise we call when
@@ -125,6 +142,11 @@
  * Record that bi_end_io was called with this flag...
  */
 #define	R1BIO_Returned 6
+/* If a write for this request means we can clear some
+ * known-bad-block records, we set this flag
+ */
+#define	R1BIO_MadeGood 7
+#define	R1BIO_WriteError 8
 
 extern int md_raid1_congested(mddev_t *mddev, int bits);
 
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 6e84668..8b29cd4 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -22,6 +22,7 @@
 #include <linux/delay.h>
 #include <linux/blkdev.h>
 #include <linux/seq_file.h>
+#include <linux/ratelimit.h>
 #include "md.h"
 #include "raid10.h"
 #include "raid0.h"
@@ -123,7 +124,14 @@
 	for (j = 0 ; j < nalloc; j++) {
 		bio = r10_bio->devs[j].bio;
 		for (i = 0; i < RESYNC_PAGES; i++) {
-			page = alloc_page(gfp_flags);
+			if (j == 1 && !test_bit(MD_RECOVERY_SYNC,
+						&conf->mddev->recovery)) {
+				/* we can share bv_page's during recovery */
+				struct bio *rbio = r10_bio->devs[0].bio;
+				page = rbio->bi_io_vec[i].bv_page;
+				get_page(page);
+			} else
+				page = alloc_page(gfp_flags);
 			if (unlikely(!page))
 				goto out_free_pages;
 
@@ -173,7 +181,7 @@
 
 	for (i = 0; i < conf->copies; i++) {
 		struct bio **bio = & r10_bio->devs[i].bio;
-		if (*bio && *bio != IO_BLOCKED)
+		if (!BIO_SPECIAL(*bio))
 			bio_put(*bio);
 		*bio = NULL;
 	}
@@ -183,12 +191,6 @@
 {
 	conf_t *conf = r10_bio->mddev->private;
 
-	/*
-	 * Wake up any possible resync thread that waits for the device
-	 * to go idle.
-	 */
-	allow_barrier(conf);
-
 	put_all_bios(conf, r10_bio);
 	mempool_free(r10_bio, conf->r10bio_pool);
 }
@@ -227,9 +229,27 @@
 static void raid_end_bio_io(r10bio_t *r10_bio)
 {
 	struct bio *bio = r10_bio->master_bio;
+	int done;
+	conf_t *conf = r10_bio->mddev->private;
 
-	bio_endio(bio,
-		test_bit(R10BIO_Uptodate, &r10_bio->state) ? 0 : -EIO);
+	if (bio->bi_phys_segments) {
+		unsigned long flags;
+		spin_lock_irqsave(&conf->device_lock, flags);
+		bio->bi_phys_segments--;
+		done = (bio->bi_phys_segments == 0);
+		spin_unlock_irqrestore(&conf->device_lock, flags);
+	} else
+		done = 1;
+	if (!test_bit(R10BIO_Uptodate, &r10_bio->state))
+		clear_bit(BIO_UPTODATE, &bio->bi_flags);
+	if (done) {
+		bio_endio(bio, 0);
+		/*
+		 * Wake up any possible resync thread that waits for the device
+		 * to go idle.
+		 */
+		allow_barrier(conf);
+	}
 	free_r10bio(r10_bio);
 }
 
@@ -244,6 +264,26 @@
 		r10_bio->devs[slot].addr + (r10_bio->sectors);
 }
 
+/*
+ * Find the disk number which triggered given bio
+ */
+static int find_bio_disk(conf_t *conf, r10bio_t *r10_bio,
+			 struct bio *bio, int *slotp)
+{
+	int slot;
+
+	for (slot = 0; slot < conf->copies; slot++)
+		if (r10_bio->devs[slot].bio == bio)
+			break;
+
+	BUG_ON(slot == conf->copies);
+	update_head_pos(slot, r10_bio);
+
+	if (slotp)
+		*slotp = slot;
+	return r10_bio->devs[slot].devnum;
+}
+
 static void raid10_end_read_request(struct bio *bio, int error)
 {
 	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
@@ -277,34 +317,45 @@
 		 * oops, read error - keep the refcount on the rdev
 		 */
 		char b[BDEVNAME_SIZE];
-		if (printk_ratelimit())
-			printk(KERN_ERR "md/raid10:%s: %s: rescheduling sector %llu\n",
-			       mdname(conf->mddev),
-			       bdevname(conf->mirrors[dev].rdev->bdev,b), (unsigned long long)r10_bio->sector);
+		printk_ratelimited(KERN_ERR
+				   "md/raid10:%s: %s: rescheduling sector %llu\n",
+				   mdname(conf->mddev),
+				   bdevname(conf->mirrors[dev].rdev->bdev, b),
+				   (unsigned long long)r10_bio->sector);
+		set_bit(R10BIO_ReadError, &r10_bio->state);
 		reschedule_retry(r10_bio);
 	}
 }
 
+static void close_write(r10bio_t *r10_bio)
+{
+	/* clear the bitmap if all writes complete successfully */
+	bitmap_endwrite(r10_bio->mddev->bitmap, r10_bio->sector,
+			r10_bio->sectors,
+			!test_bit(R10BIO_Degraded, &r10_bio->state),
+			0);
+	md_write_end(r10_bio->mddev);
+}
+
 static void raid10_end_write_request(struct bio *bio, int error)
 {
 	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
 	r10bio_t *r10_bio = bio->bi_private;
-	int slot, dev;
+	int dev;
+	int dec_rdev = 1;
 	conf_t *conf = r10_bio->mddev->private;
+	int slot;
 
-	for (slot = 0; slot < conf->copies; slot++)
-		if (r10_bio->devs[slot].bio == bio)
-			break;
-	dev = r10_bio->devs[slot].devnum;
+	dev = find_bio_disk(conf, r10_bio, bio, &slot);
 
 	/*
 	 * this branch is our 'one mirror IO has finished' event handler:
 	 */
 	if (!uptodate) {
-		md_error(r10_bio->mddev, conf->mirrors[dev].rdev);
-		/* an I/O failed, we can't clear the bitmap */
-		set_bit(R10BIO_Degraded, &r10_bio->state);
-	} else
+		set_bit(WriteErrorSeen,	&conf->mirrors[dev].rdev->flags);
+		set_bit(R10BIO_WriteError, &r10_bio->state);
+		dec_rdev = 0;
+	} else {
 		/*
 		 * Set R10BIO_Uptodate in our master bio, so that
 		 * we will return a good error code for to the higher
@@ -314,9 +365,22 @@
 		 * user-side. So if something waits for IO, then it will
 		 * wait for the 'master' bio.
 		 */
+		sector_t first_bad;
+		int bad_sectors;
+
 		set_bit(R10BIO_Uptodate, &r10_bio->state);
 
-	update_head_pos(slot, r10_bio);
+		/* Maybe we can clear some bad blocks. */
+		if (is_badblock(conf->mirrors[dev].rdev,
+				r10_bio->devs[slot].addr,
+				r10_bio->sectors,
+				&first_bad, &bad_sectors)) {
+			bio_put(bio);
+			r10_bio->devs[slot].bio = IO_MADE_GOOD;
+			dec_rdev = 0;
+			set_bit(R10BIO_MadeGood, &r10_bio->state);
+		}
+	}
 
 	/*
 	 *
@@ -324,16 +388,18 @@
 	 * already.
 	 */
 	if (atomic_dec_and_test(&r10_bio->remaining)) {
-		/* clear the bitmap if all writes complete successfully */
-		bitmap_endwrite(r10_bio->mddev->bitmap, r10_bio->sector,
-				r10_bio->sectors,
-				!test_bit(R10BIO_Degraded, &r10_bio->state),
-				0);
-		md_write_end(r10_bio->mddev);
-		raid_end_bio_io(r10_bio);
+		if (test_bit(R10BIO_WriteError, &r10_bio->state))
+			reschedule_retry(r10_bio);
+		else {
+			close_write(r10_bio);
+			if (test_bit(R10BIO_MadeGood, &r10_bio->state))
+				reschedule_retry(r10_bio);
+			else
+				raid_end_bio_io(r10_bio);
+		}
 	}
-
-	rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev);
+	if (dec_rdev)
+		rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev);
 }
 
 
@@ -484,11 +550,12 @@
  * FIXME: possibly should rethink readbalancing and do it differently
  * depending on near_copies / far_copies geometry.
  */
-static int read_balance(conf_t *conf, r10bio_t *r10_bio)
+static int read_balance(conf_t *conf, r10bio_t *r10_bio, int *max_sectors)
 {
 	const sector_t this_sector = r10_bio->sector;
 	int disk, slot;
-	const int sectors = r10_bio->sectors;
+	int sectors = r10_bio->sectors;
+	int best_good_sectors;
 	sector_t new_distance, best_dist;
 	mdk_rdev_t *rdev;
 	int do_balance;
@@ -497,8 +564,10 @@
 	raid10_find_phys(conf, r10_bio);
 	rcu_read_lock();
 retry:
+	sectors = r10_bio->sectors;
 	best_slot = -1;
 	best_dist = MaxSector;
+	best_good_sectors = 0;
 	do_balance = 1;
 	/*
 	 * Check if we can balance. We can balance on the whole
@@ -511,6 +580,10 @@
 		do_balance = 0;
 
 	for (slot = 0; slot < conf->copies ; slot++) {
+		sector_t first_bad;
+		int bad_sectors;
+		sector_t dev_sector;
+
 		if (r10_bio->devs[slot].bio == IO_BLOCKED)
 			continue;
 		disk = r10_bio->devs[slot].devnum;
@@ -520,6 +593,37 @@
 		if (!test_bit(In_sync, &rdev->flags))
 			continue;
 
+		dev_sector = r10_bio->devs[slot].addr;
+		if (is_badblock(rdev, dev_sector, sectors,
+				&first_bad, &bad_sectors)) {
+			if (best_dist < MaxSector)
+				/* Already have a better slot */
+				continue;
+			if (first_bad <= dev_sector) {
+				/* Cannot read here.  If this is the
+				 * 'primary' device, then we must not read
+				 * beyond 'bad_sectors' from another device.
+				 */
+				bad_sectors -= (dev_sector - first_bad);
+				if (!do_balance && sectors > bad_sectors)
+					sectors = bad_sectors;
+				if (best_good_sectors > sectors)
+					best_good_sectors = sectors;
+			} else {
+				sector_t good_sectors =
+					first_bad - dev_sector;
+				if (good_sectors > best_good_sectors) {
+					best_good_sectors = good_sectors;
+					best_slot = slot;
+				}
+				if (!do_balance)
+					/* Must read from here */
+					break;
+			}
+			continue;
+		} else
+			best_good_sectors = sectors;
+
 		if (!do_balance)
 			break;
 
@@ -561,6 +665,7 @@
 	} else
 		disk = -1;
 	rcu_read_unlock();
+	*max_sectors = best_good_sectors;
 
 	return disk;
 }
@@ -734,6 +839,8 @@
 	unsigned long flags;
 	mdk_rdev_t *blocked_rdev;
 	int plugged;
+	int sectors_handled;
+	int max_sectors;
 
 	if (unlikely(bio->bi_rw & REQ_FLUSH)) {
 		md_flush_request(mddev, bio);
@@ -808,12 +915,26 @@
 	r10_bio->sector = bio->bi_sector;
 	r10_bio->state = 0;
 
+	/* We might need to issue multiple reads to different
+	 * devices if there are bad blocks around, so we keep
+	 * track of the number of reads in bio->bi_phys_segments.
+	 * If this is 0, there is only one r10_bio and no locking
+	 * will be needed when the request completes.  If it is
+	 * non-zero, then it is the number of not-completed requests.
+	 */
+	bio->bi_phys_segments = 0;
+	clear_bit(BIO_SEG_VALID, &bio->bi_flags);
+
 	if (rw == READ) {
 		/*
 		 * read balancing logic:
 		 */
-		int disk = read_balance(conf, r10_bio);
-		int slot = r10_bio->read_slot;
+		int disk;
+		int slot;
+
+read_again:
+		disk = read_balance(conf, r10_bio, &max_sectors);
+		slot = r10_bio->read_slot;
 		if (disk < 0) {
 			raid_end_bio_io(r10_bio);
 			return 0;
@@ -821,6 +942,8 @@
 		mirror = conf->mirrors + disk;
 
 		read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+		md_trim_bio(read_bio, r10_bio->sector - bio->bi_sector,
+			    max_sectors);
 
 		r10_bio->devs[slot].bio = read_bio;
 
@@ -831,7 +954,37 @@
 		read_bio->bi_rw = READ | do_sync;
 		read_bio->bi_private = r10_bio;
 
-		generic_make_request(read_bio);
+		if (max_sectors < r10_bio->sectors) {
+			/* Could not read all from this device, so we will
+			 * need another r10_bio.
+			 */
+			sectors_handled = (r10_bio->sectors + max_sectors
+					   - bio->bi_sector);
+			r10_bio->sectors = max_sectors;
+			spin_lock_irq(&conf->device_lock);
+			if (bio->bi_phys_segments == 0)
+				bio->bi_phys_segments = 2;
+			else
+				bio->bi_phys_segments++;
+			spin_unlock(&conf->device_lock);
+			/* Cannot call generic_make_request directly
+			 * as that will be queued in __generic_make_request
+			 * and subsequent mempool_alloc might block
+			 * waiting for it.  so hand bio over to raid10d.
+			 */
+			reschedule_retry(r10_bio);
+
+			r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
+
+			r10_bio->master_bio = bio;
+			r10_bio->sectors = ((bio->bi_size >> 9)
+					    - sectors_handled);
+			r10_bio->state = 0;
+			r10_bio->mddev = mddev;
+			r10_bio->sector = bio->bi_sector + sectors_handled;
+			goto read_again;
+		} else
+			generic_make_request(read_bio);
 		return 0;
 	}
 
@@ -841,13 +994,22 @@
 	/* first select target devices under rcu_lock and
 	 * inc refcount on their rdev.  Record them by setting
 	 * bios[x] to bio
+	 * If there are known/acknowledged bad blocks on any device
+	 * on which we have seen a write error, we want to avoid
+	 * writing to those blocks.  This potentially requires several
+	 * writes to write around the bad blocks.  Each set of writes
+	 * gets its own r10_bio with a set of bios attached.  The number
+	 * of r10_bios is recored in bio->bi_phys_segments just as with
+	 * the read case.
 	 */
 	plugged = mddev_check_plugged(mddev);
 
 	raid10_find_phys(conf, r10_bio);
- retry_write:
+retry_write:
 	blocked_rdev = NULL;
 	rcu_read_lock();
+	max_sectors = r10_bio->sectors;
+
 	for (i = 0;  i < conf->copies; i++) {
 		int d = r10_bio->devs[i].devnum;
 		mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[d].rdev);
@@ -856,13 +1018,55 @@
 			blocked_rdev = rdev;
 			break;
 		}
-		if (rdev && !test_bit(Faulty, &rdev->flags)) {
-			atomic_inc(&rdev->nr_pending);
-			r10_bio->devs[i].bio = bio;
-		} else {
-			r10_bio->devs[i].bio = NULL;
+		r10_bio->devs[i].bio = NULL;
+		if (!rdev || test_bit(Faulty, &rdev->flags)) {
 			set_bit(R10BIO_Degraded, &r10_bio->state);
+			continue;
 		}
+		if (test_bit(WriteErrorSeen, &rdev->flags)) {
+			sector_t first_bad;
+			sector_t dev_sector = r10_bio->devs[i].addr;
+			int bad_sectors;
+			int is_bad;
+
+			is_bad = is_badblock(rdev, dev_sector,
+					     max_sectors,
+					     &first_bad, &bad_sectors);
+			if (is_bad < 0) {
+				/* Mustn't write here until the bad block
+				 * is acknowledged
+				 */
+				atomic_inc(&rdev->nr_pending);
+				set_bit(BlockedBadBlocks, &rdev->flags);
+				blocked_rdev = rdev;
+				break;
+			}
+			if (is_bad && first_bad <= dev_sector) {
+				/* Cannot write here at all */
+				bad_sectors -= (dev_sector - first_bad);
+				if (bad_sectors < max_sectors)
+					/* Mustn't write more than bad_sectors
+					 * to other devices yet
+					 */
+					max_sectors = bad_sectors;
+				/* We don't set R10BIO_Degraded as that
+				 * only applies if the disk is missing,
+				 * so it might be re-added, and we want to
+				 * know to recover this chunk.
+				 * In this case the device is here, and the
+				 * fact that this chunk is not in-sync is
+				 * recorded in the bad block log.
+				 */
+				continue;
+			}
+			if (is_bad) {
+				int good_sectors = first_bad - dev_sector;
+				if (good_sectors < max_sectors)
+					max_sectors = good_sectors;
+			}
+		}
+		r10_bio->devs[i].bio = bio;
+		atomic_inc(&rdev->nr_pending);
 	}
 	rcu_read_unlock();
 
@@ -882,8 +1086,22 @@
 		goto retry_write;
 	}
 
+	if (max_sectors < r10_bio->sectors) {
+		/* We are splitting this into multiple parts, so
+		 * we need to prepare for allocating another r10_bio.
+		 */
+		r10_bio->sectors = max_sectors;
+		spin_lock_irq(&conf->device_lock);
+		if (bio->bi_phys_segments == 0)
+			bio->bi_phys_segments = 2;
+		else
+			bio->bi_phys_segments++;
+		spin_unlock_irq(&conf->device_lock);
+	}
+	sectors_handled = r10_bio->sector + max_sectors - bio->bi_sector;
+
 	atomic_set(&r10_bio->remaining, 1);
-	bitmap_startwrite(mddev->bitmap, bio->bi_sector, r10_bio->sectors, 0);
+	bitmap_startwrite(mddev->bitmap, r10_bio->sector, r10_bio->sectors, 0);
 
 	for (i = 0; i < conf->copies; i++) {
 		struct bio *mbio;
@@ -892,10 +1110,12 @@
 			continue;
 
 		mbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+		md_trim_bio(mbio, r10_bio->sector - bio->bi_sector,
+			    max_sectors);
 		r10_bio->devs[i].bio = mbio;
 
-		mbio->bi_sector	= r10_bio->devs[i].addr+
-			conf->mirrors[d].rdev->data_offset;
+		mbio->bi_sector	= (r10_bio->devs[i].addr+
+				   conf->mirrors[d].rdev->data_offset);
 		mbio->bi_bdev = conf->mirrors[d].rdev->bdev;
 		mbio->bi_end_io	= raid10_end_write_request;
 		mbio->bi_rw = WRITE | do_sync | do_fua;
@@ -920,6 +1140,21 @@
 	/* In case raid10d snuck in to freeze_array */
 	wake_up(&conf->wait_barrier);
 
+	if (sectors_handled < (bio->bi_size >> 9)) {
+		/* We need another r10_bio.  It has already been counted
+		 * in bio->bi_phys_segments.
+		 */
+		r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
+
+		r10_bio->master_bio = bio;
+		r10_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+
+		r10_bio->mddev = mddev;
+		r10_bio->sector = bio->bi_sector + sectors_handled;
+		r10_bio->state = 0;
+		goto retry_write;
+	}
+
 	if (do_sync || !mddev->bitmap || !plugged)
 		md_wakeup_thread(mddev->thread);
 	return 0;
@@ -949,6 +1184,30 @@
 	seq_printf(seq, "]");
 }
 
+/* check if there are enough drives for
+ * every block to appear on atleast one.
+ * Don't consider the device numbered 'ignore'
+ * as we might be about to remove it.
+ */
+static int enough(conf_t *conf, int ignore)
+{
+	int first = 0;
+
+	do {
+		int n = conf->copies;
+		int cnt = 0;
+		while (n--) {
+			if (conf->mirrors[first].rdev &&
+			    first != ignore)
+				cnt++;
+			first = (first+1) % conf->raid_disks;
+		}
+		if (cnt == 0)
+			return 0;
+	} while (first != 0);
+	return 1;
+}
+
 static void error(mddev_t *mddev, mdk_rdev_t *rdev)
 {
 	char b[BDEVNAME_SIZE];
@@ -961,13 +1220,9 @@
 	 * else mark the drive as failed
 	 */
 	if (test_bit(In_sync, &rdev->flags)
-	    && conf->raid_disks-mddev->degraded == 1)
+	    && !enough(conf, rdev->raid_disk))
 		/*
 		 * Don't fail the drive, just return an IO error.
-		 * The test should really be more sophisticated than
-		 * "working_disks == 1", but it isn't critical, and
-		 * can wait until we do more sophisticated "is the drive
-		 * really dead" tests...
 		 */
 		return;
 	if (test_and_clear_bit(In_sync, &rdev->flags)) {
@@ -980,6 +1235,7 @@
 		 */
 		set_bit(MD_RECOVERY_INTR, &mddev->recovery);
 	}
+	set_bit(Blocked, &rdev->flags);
 	set_bit(Faulty, &rdev->flags);
 	set_bit(MD_CHANGE_DEVS, &mddev->flags);
 	printk(KERN_ALERT
@@ -1022,27 +1278,6 @@
 	conf->r10buf_pool = NULL;
 }
 
-/* check if there are enough drives for
- * every block to appear on atleast one
- */
-static int enough(conf_t *conf)
-{
-	int first = 0;
-
-	do {
-		int n = conf->copies;
-		int cnt = 0;
-		while (n--) {
-			if (conf->mirrors[first].rdev)
-				cnt++;
-			first = (first+1) % conf->raid_disks;
-		}
-		if (cnt == 0)
-			return 0;
-	} while (first != 0);
-	return 1;
-}
-
 static int raid10_spare_active(mddev_t *mddev)
 {
 	int i;
@@ -1078,7 +1313,6 @@
 	conf_t *conf = mddev->private;
 	int err = -EEXIST;
 	int mirror;
-	mirror_info_t *p;
 	int first = 0;
 	int last = conf->raid_disks - 1;
 
@@ -1087,44 +1321,47 @@
 		 * very different from resync
 		 */
 		return -EBUSY;
-	if (!enough(conf))
+	if (!enough(conf, -1))
 		return -EINVAL;
 
 	if (rdev->raid_disk >= 0)
 		first = last = rdev->raid_disk;
 
-	if (rdev->saved_raid_disk >= 0 &&
-	    rdev->saved_raid_disk >= first &&
+	if (rdev->saved_raid_disk >= first &&
 	    conf->mirrors[rdev->saved_raid_disk].rdev == NULL)
 		mirror = rdev->saved_raid_disk;
 	else
 		mirror = first;
-	for ( ; mirror <= last ; mirror++)
-		if ( !(p=conf->mirrors+mirror)->rdev) {
+	for ( ; mirror <= last ; mirror++) {
+		mirror_info_t *p = &conf->mirrors[mirror];
+		if (p->recovery_disabled == mddev->recovery_disabled)
+			continue;
+		if (!p->rdev)
+			continue;
 
-			disk_stack_limits(mddev->gendisk, rdev->bdev,
-					  rdev->data_offset << 9);
-			/* as we don't honour merge_bvec_fn, we must
-			 * never risk violating it, so limit
-			 * ->max_segments to one lying with a single
-			 * page, as a one page request is never in
-			 * violation.
-			 */
-			if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
-				blk_queue_max_segments(mddev->queue, 1);
-				blk_queue_segment_boundary(mddev->queue,
-							   PAGE_CACHE_SIZE - 1);
-			}
-
-			p->head_position = 0;
-			rdev->raid_disk = mirror;
-			err = 0;
-			if (rdev->saved_raid_disk != mirror)
-				conf->fullsync = 1;
-			rcu_assign_pointer(p->rdev, rdev);
-			break;
+		disk_stack_limits(mddev->gendisk, rdev->bdev,
+				  rdev->data_offset << 9);
+		/* as we don't honour merge_bvec_fn, we must
+		 * never risk violating it, so limit
+		 * ->max_segments to one lying with a single
+		 * page, as a one page request is never in
+		 * violation.
+		 */
+		if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
+			blk_queue_max_segments(mddev->queue, 1);
+			blk_queue_segment_boundary(mddev->queue,
+						   PAGE_CACHE_SIZE - 1);
 		}
 
+		p->head_position = 0;
+		rdev->raid_disk = mirror;
+		err = 0;
+		if (rdev->saved_raid_disk != mirror)
+			conf->fullsync = 1;
+		rcu_assign_pointer(p->rdev, rdev);
+		break;
+	}
+
 	md_integrity_add_rdev(rdev, mddev);
 	print_conf(conf);
 	return err;
@@ -1149,7 +1386,8 @@
 		 * is not possible.
 		 */
 		if (!test_bit(Faulty, &rdev->flags) &&
-		    enough(conf)) {
+		    mddev->recovery_disabled != p->recovery_disabled &&
+		    enough(conf, -1)) {
 			err = -EBUSY;
 			goto abort;
 		}
@@ -1174,24 +1412,18 @@
 {
 	r10bio_t *r10_bio = bio->bi_private;
 	conf_t *conf = r10_bio->mddev->private;
-	int i,d;
+	int d;
 
-	for (i=0; i<conf->copies; i++)
-		if (r10_bio->devs[i].bio == bio)
-			break;
-	BUG_ON(i == conf->copies);
-	update_head_pos(i, r10_bio);
-	d = r10_bio->devs[i].devnum;
+	d = find_bio_disk(conf, r10_bio, bio, NULL);
 
 	if (test_bit(BIO_UPTODATE, &bio->bi_flags))
 		set_bit(R10BIO_Uptodate, &r10_bio->state);
-	else {
+	else
+		/* The write handler will notice the lack of
+		 * R10BIO_Uptodate and record any errors etc
+		 */
 		atomic_add(r10_bio->sectors,
 			   &conf->mirrors[d].rdev->corrected_errors);
-		if (!test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery))
-			md_error(r10_bio->mddev,
-				 conf->mirrors[d].rdev);
-	}
 
 	/* for reconstruct, we always reschedule after a read.
 	 * for resync, only after all reads
@@ -1206,38 +1438,58 @@
 	}
 }
 
+static void end_sync_request(r10bio_t *r10_bio)
+{
+	mddev_t *mddev = r10_bio->mddev;
+
+	while (atomic_dec_and_test(&r10_bio->remaining)) {
+		if (r10_bio->master_bio == NULL) {
+			/* the primary of several recovery bios */
+			sector_t s = r10_bio->sectors;
+			if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+			    test_bit(R10BIO_WriteError, &r10_bio->state))
+				reschedule_retry(r10_bio);
+			else
+				put_buf(r10_bio);
+			md_done_sync(mddev, s, 1);
+			break;
+		} else {
+			r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio;
+			if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+			    test_bit(R10BIO_WriteError, &r10_bio->state))
+				reschedule_retry(r10_bio);
+			else
+				put_buf(r10_bio);
+			r10_bio = r10_bio2;
+		}
+	}
+}
+
 static void end_sync_write(struct bio *bio, int error)
 {
 	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
 	r10bio_t *r10_bio = bio->bi_private;
 	mddev_t *mddev = r10_bio->mddev;
 	conf_t *conf = mddev->private;
-	int i,d;
+	int d;
+	sector_t first_bad;
+	int bad_sectors;
+	int slot;
 
-	for (i = 0; i < conf->copies; i++)
-		if (r10_bio->devs[i].bio == bio)
-			break;
-	d = r10_bio->devs[i].devnum;
+	d = find_bio_disk(conf, r10_bio, bio, &slot);
 
-	if (!uptodate)
-		md_error(mddev, conf->mirrors[d].rdev);
-
-	update_head_pos(i, r10_bio);
+	if (!uptodate) {
+		set_bit(WriteErrorSeen, &conf->mirrors[d].rdev->flags);
+		set_bit(R10BIO_WriteError, &r10_bio->state);
+	} else if (is_badblock(conf->mirrors[d].rdev,
+			     r10_bio->devs[slot].addr,
+			     r10_bio->sectors,
+			     &first_bad, &bad_sectors))
+		set_bit(R10BIO_MadeGood, &r10_bio->state);
 
 	rdev_dec_pending(conf->mirrors[d].rdev, mddev);
-	while (atomic_dec_and_test(&r10_bio->remaining)) {
-		if (r10_bio->master_bio == NULL) {
-			/* the primary of several recovery bios */
-			sector_t s = r10_bio->sectors;
-			put_buf(r10_bio);
-			md_done_sync(mddev, s, 1);
-			break;
-		} else {
-			r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio;
-			put_buf(r10_bio);
-			r10_bio = r10_bio2;
-		}
-	}
+
+	end_sync_request(r10_bio);
 }
 
 /*
@@ -1299,11 +1551,12 @@
 			if (j == vcnt)
 				continue;
 			mddev->resync_mismatches += r10_bio->sectors;
+			if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
+				/* Don't fix anything. */
+				continue;
 		}
-		if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
-			/* Don't fix anything. */
-			continue;
-		/* Ok, we need to write this bio
+		/* Ok, we need to write this bio, either to correct an
+		 * inconsistency or to correct an unreadable block.
 		 * First we need to fixup bv_offset, bv_len and
 		 * bi_vecs, as the read request might have corrupted these
 		 */
@@ -1355,32 +1608,107 @@
  * The second for writing.
  *
  */
+static void fix_recovery_read_error(r10bio_t *r10_bio)
+{
+	/* We got a read error during recovery.
+	 * We repeat the read in smaller page-sized sections.
+	 * If a read succeeds, write it to the new device or record
+	 * a bad block if we cannot.
+	 * If a read fails, record a bad block on both old and
+	 * new devices.
+	 */
+	mddev_t *mddev = r10_bio->mddev;
+	conf_t *conf = mddev->private;
+	struct bio *bio = r10_bio->devs[0].bio;
+	sector_t sect = 0;
+	int sectors = r10_bio->sectors;
+	int idx = 0;
+	int dr = r10_bio->devs[0].devnum;
+	int dw = r10_bio->devs[1].devnum;
+
+	while (sectors) {
+		int s = sectors;
+		mdk_rdev_t *rdev;
+		sector_t addr;
+		int ok;
+
+		if (s > (PAGE_SIZE>>9))
+			s = PAGE_SIZE >> 9;
+
+		rdev = conf->mirrors[dr].rdev;
+		addr = r10_bio->devs[0].addr + sect,
+		ok = sync_page_io(rdev,
+				  addr,
+				  s << 9,
+				  bio->bi_io_vec[idx].bv_page,
+				  READ, false);
+		if (ok) {
+			rdev = conf->mirrors[dw].rdev;
+			addr = r10_bio->devs[1].addr + sect;
+			ok = sync_page_io(rdev,
+					  addr,
+					  s << 9,
+					  bio->bi_io_vec[idx].bv_page,
+					  WRITE, false);
+			if (!ok)
+				set_bit(WriteErrorSeen, &rdev->flags);
+		}
+		if (!ok) {
+			/* We don't worry if we cannot set a bad block -
+			 * it really is bad so there is no loss in not
+			 * recording it yet
+			 */
+			rdev_set_badblocks(rdev, addr, s, 0);
+
+			if (rdev != conf->mirrors[dw].rdev) {
+				/* need bad block on destination too */
+				mdk_rdev_t *rdev2 = conf->mirrors[dw].rdev;
+				addr = r10_bio->devs[1].addr + sect;
+				ok = rdev_set_badblocks(rdev2, addr, s, 0);
+				if (!ok) {
+					/* just abort the recovery */
+					printk(KERN_NOTICE
+					       "md/raid10:%s: recovery aborted"
+					       " due to read error\n",
+					       mdname(mddev));
+
+					conf->mirrors[dw].recovery_disabled
+						= mddev->recovery_disabled;
+					set_bit(MD_RECOVERY_INTR,
+						&mddev->recovery);
+					break;
+				}
+			}
+		}
+
+		sectors -= s;
+		sect += s;
+		idx++;
+	}
+}
 
 static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio)
 {
 	conf_t *conf = mddev->private;
-	int i, d;
-	struct bio *bio, *wbio;
+	int d;
+	struct bio *wbio;
 
+	if (!test_bit(R10BIO_Uptodate, &r10_bio->state)) {
+		fix_recovery_read_error(r10_bio);
+		end_sync_request(r10_bio);
+		return;
+	}
 
-	/* move the pages across to the second bio
+	/*
+	 * share the pages with the first bio
 	 * and submit the write request
 	 */
-	bio = r10_bio->devs[0].bio;
 	wbio = r10_bio->devs[1].bio;
-	for (i=0; i < wbio->bi_vcnt; i++) {
-		struct page *p = bio->bi_io_vec[i].bv_page;
-		bio->bi_io_vec[i].bv_page = wbio->bi_io_vec[i].bv_page;
-		wbio->bi_io_vec[i].bv_page = p;
-	}
 	d = r10_bio->devs[1].devnum;
 
 	atomic_inc(&conf->mirrors[d].rdev->nr_pending);
 	md_sync_acct(conf->mirrors[d].rdev->bdev, wbio->bi_size >> 9);
-	if (test_bit(R10BIO_Uptodate, &r10_bio->state))
-		generic_make_request(wbio);
-	else
-		bio_endio(wbio, -EIO);
+	generic_make_request(wbio);
 }
 
 
@@ -1421,6 +1749,26 @@
 		atomic_set(&rdev->read_errors, read_errors >> hours_since_last);
 }
 
+static int r10_sync_page_io(mdk_rdev_t *rdev, sector_t sector,
+			    int sectors, struct page *page, int rw)
+{
+	sector_t first_bad;
+	int bad_sectors;
+
+	if (is_badblock(rdev, sector, sectors, &first_bad, &bad_sectors)
+	    && (rw == READ || test_bit(WriteErrorSeen, &rdev->flags)))
+		return -1;
+	if (sync_page_io(rdev, sector, sectors << 9, page, rw, false))
+		/* success */
+		return 1;
+	if (rw == WRITE)
+		set_bit(WriteErrorSeen, &rdev->flags);
+	/* need to record an error - either for the block or the device */
+	if (!rdev_set_badblocks(rdev, sector, sectors, 0))
+		md_error(rdev->mddev, rdev);
+	return 0;
+}
+
 /*
  * This is a kernel thread which:
  *
@@ -1476,10 +1824,15 @@
 
 		rcu_read_lock();
 		do {
+			sector_t first_bad;
+			int bad_sectors;
+
 			d = r10_bio->devs[sl].devnum;
 			rdev = rcu_dereference(conf->mirrors[d].rdev);
 			if (rdev &&
-			    test_bit(In_sync, &rdev->flags)) {
+			    test_bit(In_sync, &rdev->flags) &&
+			    is_badblock(rdev, r10_bio->devs[sl].addr + sect, s,
+					&first_bad, &bad_sectors) == 0) {
 				atomic_inc(&rdev->nr_pending);
 				rcu_read_unlock();
 				success = sync_page_io(rdev,
@@ -1499,9 +1852,19 @@
 		rcu_read_unlock();
 
 		if (!success) {
-			/* Cannot read from anywhere -- bye bye array */
+			/* Cannot read from anywhere, just mark the block
+			 * as bad on the first device to discourage future
+			 * reads.
+			 */
 			int dn = r10_bio->devs[r10_bio->read_slot].devnum;
-			md_error(mddev, conf->mirrors[dn].rdev);
+			rdev = conf->mirrors[dn].rdev;
+
+			if (!rdev_set_badblocks(
+				    rdev,
+				    r10_bio->devs[r10_bio->read_slot].addr
+				    + sect,
+				    s, 0))
+				md_error(mddev, rdev);
 			break;
 		}
 
@@ -1516,80 +1879,82 @@
 			sl--;
 			d = r10_bio->devs[sl].devnum;
 			rdev = rcu_dereference(conf->mirrors[d].rdev);
-			if (rdev &&
-			    test_bit(In_sync, &rdev->flags)) {
-				atomic_inc(&rdev->nr_pending);
-				rcu_read_unlock();
-				atomic_add(s, &rdev->corrected_errors);
-				if (sync_page_io(rdev,
-						 r10_bio->devs[sl].addr +
-						 sect,
-						 s<<9, conf->tmppage, WRITE, false)
-				    == 0) {
-					/* Well, this device is dead */
-					printk(KERN_NOTICE
-					       "md/raid10:%s: read correction "
-					       "write failed"
-					       " (%d sectors at %llu on %s)\n",
-					       mdname(mddev), s,
-					       (unsigned long long)(
-						       sect + rdev->data_offset),
-					       bdevname(rdev->bdev, b));
-					printk(KERN_NOTICE "md/raid10:%s: %s: failing "
-					       "drive\n",
-					       mdname(mddev),
-					       bdevname(rdev->bdev, b));
-					md_error(mddev, rdev);
-				}
-				rdev_dec_pending(rdev, mddev);
-				rcu_read_lock();
+			if (!rdev ||
+			    !test_bit(In_sync, &rdev->flags))
+				continue;
+
+			atomic_inc(&rdev->nr_pending);
+			rcu_read_unlock();
+			if (r10_sync_page_io(rdev,
+					     r10_bio->devs[sl].addr +
+					     sect,
+					     s<<9, conf->tmppage, WRITE)
+			    == 0) {
+				/* Well, this device is dead */
+				printk(KERN_NOTICE
+				       "md/raid10:%s: read correction "
+				       "write failed"
+				       " (%d sectors at %llu on %s)\n",
+				       mdname(mddev), s,
+				       (unsigned long long)(
+					       sect + rdev->data_offset),
+				       bdevname(rdev->bdev, b));
+				printk(KERN_NOTICE "md/raid10:%s: %s: failing "
+				       "drive\n",
+				       mdname(mddev),
+				       bdevname(rdev->bdev, b));
 			}
+			rdev_dec_pending(rdev, mddev);
+			rcu_read_lock();
 		}
 		sl = start;
 		while (sl != r10_bio->read_slot) {
+			char b[BDEVNAME_SIZE];
 
 			if (sl==0)
 				sl = conf->copies;
 			sl--;
 			d = r10_bio->devs[sl].devnum;
 			rdev = rcu_dereference(conf->mirrors[d].rdev);
-			if (rdev &&
-			    test_bit(In_sync, &rdev->flags)) {
-				char b[BDEVNAME_SIZE];
-				atomic_inc(&rdev->nr_pending);
-				rcu_read_unlock();
-				if (sync_page_io(rdev,
-						 r10_bio->devs[sl].addr +
-						 sect,
-						 s<<9, conf->tmppage,
-						 READ, false) == 0) {
-					/* Well, this device is dead */
-					printk(KERN_NOTICE
-					       "md/raid10:%s: unable to read back "
-					       "corrected sectors"
-					       " (%d sectors at %llu on %s)\n",
-					       mdname(mddev), s,
-					       (unsigned long long)(
-						       sect + rdev->data_offset),
-					       bdevname(rdev->bdev, b));
-					printk(KERN_NOTICE "md/raid10:%s: %s: failing drive\n",
-					       mdname(mddev),
-					       bdevname(rdev->bdev, b));
+			if (!rdev ||
+			    !test_bit(In_sync, &rdev->flags))
+				continue;
 
-					md_error(mddev, rdev);
-				} else {
-					printk(KERN_INFO
-					       "md/raid10:%s: read error corrected"
-					       " (%d sectors at %llu on %s)\n",
-					       mdname(mddev), s,
-					       (unsigned long long)(
-						       sect + rdev->data_offset),
-					       bdevname(rdev->bdev, b));
-				}
-
-				rdev_dec_pending(rdev, mddev);
-				rcu_read_lock();
+			atomic_inc(&rdev->nr_pending);
+			rcu_read_unlock();
+			switch (r10_sync_page_io(rdev,
+					     r10_bio->devs[sl].addr +
+					     sect,
+					     s<<9, conf->tmppage,
+						 READ)) {
+			case 0:
+				/* Well, this device is dead */
+				printk(KERN_NOTICE
+				       "md/raid10:%s: unable to read back "
+				       "corrected sectors"
+				       " (%d sectors at %llu on %s)\n",
+				       mdname(mddev), s,
+				       (unsigned long long)(
+					       sect + rdev->data_offset),
+				       bdevname(rdev->bdev, b));
+				printk(KERN_NOTICE "md/raid10:%s: %s: failing "
+				       "drive\n",
+				       mdname(mddev),
+				       bdevname(rdev->bdev, b));
+				break;
+			case 1:
+				printk(KERN_INFO
+				       "md/raid10:%s: read error corrected"
+				       " (%d sectors at %llu on %s)\n",
+				       mdname(mddev), s,
+				       (unsigned long long)(
+					       sect + rdev->data_offset),
+				       bdevname(rdev->bdev, b));
+				atomic_add(s, &rdev->corrected_errors);
 			}
+
+			rdev_dec_pending(rdev, mddev);
+			rcu_read_lock();
 		}
 		rcu_read_unlock();
 
@@ -1598,21 +1963,254 @@
 	}
 }
 
+static void bi_complete(struct bio *bio, int error)
+{
+	complete((struct completion *)bio->bi_private);
+}
+
+static int submit_bio_wait(int rw, struct bio *bio)
+{
+	struct completion event;
+	rw |= REQ_SYNC;
+
+	init_completion(&event);
+	bio->bi_private = &event;
+	bio->bi_end_io = bi_complete;
+	submit_bio(rw, bio);
+	wait_for_completion(&event);
+
+	return test_bit(BIO_UPTODATE, &bio->bi_flags);
+}
+
+static int narrow_write_error(r10bio_t *r10_bio, int i)
+{
+	struct bio *bio = r10_bio->master_bio;
+	mddev_t *mddev = r10_bio->mddev;
+	conf_t *conf = mddev->private;
+	mdk_rdev_t *rdev = conf->mirrors[r10_bio->devs[i].devnum].rdev;
+	/* bio has the data to be written to slot 'i' where
+	 * we just recently had a write error.
+	 * We repeatedly clone the bio and trim down to one block,
+	 * then try the write.  Where the write fails we record
+	 * a bad block.
+	 * It is conceivable that the bio doesn't exactly align with
+	 * blocks.  We must handle this.
+	 *
+	 * We currently own a reference to the rdev.
+	 */
+
+	int block_sectors;
+	sector_t sector;
+	int sectors;
+	int sect_to_write = r10_bio->sectors;
+	int ok = 1;
+
+	if (rdev->badblocks.shift < 0)
+		return 0;
+
+	block_sectors = 1 << rdev->badblocks.shift;
+	sector = r10_bio->sector;
+	sectors = ((r10_bio->sector + block_sectors)
+		   & ~(sector_t)(block_sectors - 1))
+		- sector;
+
+	while (sect_to_write) {
+		struct bio *wbio;
+		if (sectors > sect_to_write)
+			sectors = sect_to_write;
+		/* Write at 'sector' for 'sectors' */
+		wbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
+		md_trim_bio(wbio, sector - bio->bi_sector, sectors);
+		wbio->bi_sector = (r10_bio->devs[i].addr+
+				   rdev->data_offset+
+				   (sector - r10_bio->sector));
+		wbio->bi_bdev = rdev->bdev;
+		if (submit_bio_wait(WRITE, wbio) == 0)
+			/* Failure! */
+			ok = rdev_set_badblocks(rdev, sector,
+						sectors, 0)
+				&& ok;
+
+		bio_put(wbio);
+		sect_to_write -= sectors;
+		sector += sectors;
+		sectors = block_sectors;
+	}
+	return ok;
+}
+
+static void handle_read_error(mddev_t *mddev, r10bio_t *r10_bio)
+{
+	int slot = r10_bio->read_slot;
+	int mirror = r10_bio->devs[slot].devnum;
+	struct bio *bio;
+	conf_t *conf = mddev->private;
+	mdk_rdev_t *rdev;
+	char b[BDEVNAME_SIZE];
+	unsigned long do_sync;
+	int max_sectors;
+
+	/* we got a read error. Maybe the drive is bad.  Maybe just
+	 * the block and we can fix it.
+	 * We freeze all other IO, and try reading the block from
+	 * other devices.  When we find one, we re-write
+	 * and check it that fixes the read error.
+	 * This is all done synchronously while the array is
+	 * frozen.
+	 */
+	if (mddev->ro == 0) {
+		freeze_array(conf);
+		fix_read_error(conf, mddev, r10_bio);
+		unfreeze_array(conf);
+	}
+	rdev_dec_pending(conf->mirrors[mirror].rdev, mddev);
+
+	bio = r10_bio->devs[slot].bio;
+	bdevname(bio->bi_bdev, b);
+	r10_bio->devs[slot].bio =
+		mddev->ro ? IO_BLOCKED : NULL;
+read_more:
+	mirror = read_balance(conf, r10_bio, &max_sectors);
+	if (mirror == -1) {
+		printk(KERN_ALERT "md/raid10:%s: %s: unrecoverable I/O"
+		       " read error for block %llu\n",
+		       mdname(mddev), b,
+		       (unsigned long long)r10_bio->sector);
+		raid_end_bio_io(r10_bio);
+		bio_put(bio);
+		return;
+	}
+
+	do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
+	if (bio)
+		bio_put(bio);
+	slot = r10_bio->read_slot;
+	rdev = conf->mirrors[mirror].rdev;
+	printk_ratelimited(
+		KERN_ERR
+		"md/raid10:%s: %s: redirecting"
+		"sector %llu to another mirror\n",
+		mdname(mddev),
+		bdevname(rdev->bdev, b),
+		(unsigned long long)r10_bio->sector);
+	bio = bio_clone_mddev(r10_bio->master_bio,
+			      GFP_NOIO, mddev);
+	md_trim_bio(bio,
+		    r10_bio->sector - bio->bi_sector,
+		    max_sectors);
+	r10_bio->devs[slot].bio = bio;
+	bio->bi_sector = r10_bio->devs[slot].addr
+		+ rdev->data_offset;
+	bio->bi_bdev = rdev->bdev;
+	bio->bi_rw = READ | do_sync;
+	bio->bi_private = r10_bio;
+	bio->bi_end_io = raid10_end_read_request;
+	if (max_sectors < r10_bio->sectors) {
+		/* Drat - have to split this up more */
+		struct bio *mbio = r10_bio->master_bio;
+		int sectors_handled =
+			r10_bio->sector + max_sectors
+			- mbio->bi_sector;
+		r10_bio->sectors = max_sectors;
+		spin_lock_irq(&conf->device_lock);
+		if (mbio->bi_phys_segments == 0)
+			mbio->bi_phys_segments = 2;
+		else
+			mbio->bi_phys_segments++;
+		spin_unlock_irq(&conf->device_lock);
+		generic_make_request(bio);
+		bio = NULL;
+
+		r10_bio = mempool_alloc(conf->r10bio_pool,
+					GFP_NOIO);
+		r10_bio->master_bio = mbio;
+		r10_bio->sectors = (mbio->bi_size >> 9)
+			- sectors_handled;
+		r10_bio->state = 0;
+		set_bit(R10BIO_ReadError,
+			&r10_bio->state);
+		r10_bio->mddev = mddev;
+		r10_bio->sector = mbio->bi_sector
+			+ sectors_handled;
+
+		goto read_more;
+	} else
+		generic_make_request(bio);
+}
+
+static void handle_write_completed(conf_t *conf, r10bio_t *r10_bio)
+{
+	/* Some sort of write request has finished and it
+	 * succeeded in writing where we thought there was a
+	 * bad block.  So forget the bad block.
+	 * Or possibly if failed and we need to record
+	 * a bad block.
+	 */
+	int m;
+	mdk_rdev_t *rdev;
+
+	if (test_bit(R10BIO_IsSync, &r10_bio->state) ||
+	    test_bit(R10BIO_IsRecover, &r10_bio->state)) {
+		for (m = 0; m < conf->copies; m++) {
+			int dev = r10_bio->devs[m].devnum;
+			rdev = conf->mirrors[dev].rdev;
+			if (r10_bio->devs[m].bio == NULL)
+				continue;
+			if (test_bit(BIO_UPTODATE,
+				     &r10_bio->devs[m].bio->bi_flags)) {
+				rdev_clear_badblocks(
+					rdev,
+					r10_bio->devs[m].addr,
+					r10_bio->sectors);
+			} else {
+				if (!rdev_set_badblocks(
+					    rdev,
+					    r10_bio->devs[m].addr,
+					    r10_bio->sectors, 0))
+					md_error(conf->mddev, rdev);
+			}
+		}
+		put_buf(r10_bio);
+	} else {
+		for (m = 0; m < conf->copies; m++) {
+			int dev = r10_bio->devs[m].devnum;
+			struct bio *bio = r10_bio->devs[m].bio;
+			rdev = conf->mirrors[dev].rdev;
+			if (bio == IO_MADE_GOOD) {
+				rdev_clear_badblocks(
+					rdev,
+					r10_bio->devs[m].addr,
+					r10_bio->sectors);
+				rdev_dec_pending(rdev, conf->mddev);
+			} else if (bio != NULL &&
+				   !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+				if (!narrow_write_error(r10_bio, m)) {
+					md_error(conf->mddev, rdev);
+					set_bit(R10BIO_Degraded,
+						&r10_bio->state);
+				}
+				rdev_dec_pending(rdev, conf->mddev);
+			}
+		}
+		if (test_bit(R10BIO_WriteError,
+			     &r10_bio->state))
+			close_write(r10_bio);
+		raid_end_bio_io(r10_bio);
+	}
+}
+
 static void raid10d(mddev_t *mddev)
 {
 	r10bio_t *r10_bio;
-	struct bio *bio;
 	unsigned long flags;
 	conf_t *conf = mddev->private;
 	struct list_head *head = &conf->retry_list;
-	mdk_rdev_t *rdev;
 	struct blk_plug plug;
 
 	md_check_recovery(mddev);
 
 	blk_start_plug(&plug);
 	for (;;) {
-		char b[BDEVNAME_SIZE];
 
 		flush_pending_writes(conf);
 
@@ -1628,64 +2226,26 @@
 
 		mddev = r10_bio->mddev;
 		conf = mddev->private;
-		if (test_bit(R10BIO_IsSync, &r10_bio->state))
+		if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
+		    test_bit(R10BIO_WriteError, &r10_bio->state))
+			handle_write_completed(conf, r10_bio);
+		else if (test_bit(R10BIO_IsSync, &r10_bio->state))
 			sync_request_write(mddev, r10_bio);
 		else if (test_bit(R10BIO_IsRecover, &r10_bio->state))
 			recovery_request_write(mddev, r10_bio);
+		else if (test_bit(R10BIO_ReadError, &r10_bio->state))
+			handle_read_error(mddev, r10_bio);
 		else {
-			int slot = r10_bio->read_slot;
-			int mirror = r10_bio->devs[slot].devnum;
-			/* we got a read error. Maybe the drive is bad.  Maybe just
-			 * the block and we can fix it.
-			 * We freeze all other IO, and try reading the block from
-			 * other devices.  When we find one, we re-write
-			 * and check it that fixes the read error.
-			 * This is all done synchronously while the array is
-			 * frozen.
+			/* just a partial read to be scheduled from a
+			 * separate context
 			 */
-			if (mddev->ro == 0) {
-				freeze_array(conf);
-				fix_read_error(conf, mddev, r10_bio);
-				unfreeze_array(conf);
-			}
-			rdev_dec_pending(conf->mirrors[mirror].rdev, mddev);
-
-			bio = r10_bio->devs[slot].bio;
-			r10_bio->devs[slot].bio =
-				mddev->ro ? IO_BLOCKED : NULL;
-			mirror = read_balance(conf, r10_bio);
-			if (mirror == -1) {
-				printk(KERN_ALERT "md/raid10:%s: %s: unrecoverable I/O"
-				       " read error for block %llu\n",
-				       mdname(mddev),
-				       bdevname(bio->bi_bdev,b),
-				       (unsigned long long)r10_bio->sector);
-				raid_end_bio_io(r10_bio);
-				bio_put(bio);
-			} else {
-				const unsigned long do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
-				bio_put(bio);
-				slot = r10_bio->read_slot;
-				rdev = conf->mirrors[mirror].rdev;
-				if (printk_ratelimit())
-					printk(KERN_ERR "md/raid10:%s: %s: redirecting sector %llu to"
-					       " another mirror\n",
-					       mdname(mddev),
-					       bdevname(rdev->bdev,b),
-					       (unsigned long long)r10_bio->sector);
-				bio = bio_clone_mddev(r10_bio->master_bio,
-						      GFP_NOIO, mddev);
-				r10_bio->devs[slot].bio = bio;
-				bio->bi_sector = r10_bio->devs[slot].addr
-					+ rdev->data_offset;
-				bio->bi_bdev = rdev->bdev;
-				bio->bi_rw = READ | do_sync;
-				bio->bi_private = r10_bio;
-				bio->bi_end_io = raid10_end_read_request;
-				generic_make_request(bio);
-			}
+			int slot = r10_bio->read_slot;
+			generic_make_request(r10_bio->devs[slot].bio);
 		}
+
 		cond_resched();
+		if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+			md_check_recovery(mddev);
 	}
 	blk_finish_plug(&plug);
 }
@@ -1746,7 +2306,6 @@
 	int i;
 	int max_sync;
 	sector_t sync_blocks;
-
 	sector_t sectors_skipped = 0;
 	int chunks_skipped = 0;
 
@@ -1828,7 +2387,7 @@
 	max_sync = RESYNC_PAGES << (PAGE_SHIFT-9);
 	if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
 		/* recovery... the complicated one */
-		int j, k;
+		int j;
 		r10_bio = NULL;
 
 		for (i=0 ; i<conf->raid_disks; i++) {
@@ -1836,6 +2395,7 @@
 			r10bio_t *rb2;
 			sector_t sect;
 			int must_sync;
+			int any_working;
 
 			if (conf->mirrors[i].rdev == NULL ||
 			    test_bit(In_sync, &conf->mirrors[i].rdev->flags)) 
@@ -1887,19 +2447,42 @@
 			must_sync = bitmap_start_sync(mddev->bitmap, sect,
 						      &sync_blocks, still_degraded);
 
+			any_working = 0;
 			for (j=0; j<conf->copies;j++) {
+				int k;
 				int d = r10_bio->devs[j].devnum;
+				sector_t from_addr, to_addr;
+				mdk_rdev_t *rdev;
+				sector_t sector, first_bad;
+				int bad_sectors;
 				if (!conf->mirrors[d].rdev ||
 				    !test_bit(In_sync, &conf->mirrors[d].rdev->flags))
 					continue;
 				/* This is where we read from */
+				any_working = 1;
+				rdev = conf->mirrors[d].rdev;
+				sector = r10_bio->devs[j].addr;
+
+				if (is_badblock(rdev, sector, max_sync,
+						&first_bad, &bad_sectors)) {
+					if (first_bad > sector)
+						max_sync = first_bad - sector;
+					else {
+						bad_sectors -= (sector
+								- first_bad);
+						if (max_sync > bad_sectors)
+							max_sync = bad_sectors;
+						continue;
+					}
+				}
 				bio = r10_bio->devs[0].bio;
 				bio->bi_next = biolist;
 				biolist = bio;
 				bio->bi_private = r10_bio;
 				bio->bi_end_io = end_sync_read;
 				bio->bi_rw = READ;
-				bio->bi_sector = r10_bio->devs[j].addr +
+				from_addr = r10_bio->devs[j].addr;
+				bio->bi_sector = from_addr +
 					conf->mirrors[d].rdev->data_offset;
 				bio->bi_bdev = conf->mirrors[d].rdev->bdev;
 				atomic_inc(&conf->mirrors[d].rdev->nr_pending);
@@ -1916,26 +2499,48 @@
 				bio->bi_private = r10_bio;
 				bio->bi_end_io = end_sync_write;
 				bio->bi_rw = WRITE;
-				bio->bi_sector = r10_bio->devs[k].addr +
+				to_addr = r10_bio->devs[k].addr;
+				bio->bi_sector = to_addr +
 					conf->mirrors[i].rdev->data_offset;
 				bio->bi_bdev = conf->mirrors[i].rdev->bdev;
 
 				r10_bio->devs[0].devnum = d;
+				r10_bio->devs[0].addr = from_addr;
 				r10_bio->devs[1].devnum = i;
+				r10_bio->devs[1].addr = to_addr;
 
 				break;
 			}
 			if (j == conf->copies) {
-				/* Cannot recover, so abort the recovery */
+				/* Cannot recover, so abort the recovery or
+				 * record a bad block */
 				put_buf(r10_bio);
 				if (rb2)
 					atomic_dec(&rb2->remaining);
 				r10_bio = rb2;
-				if (!test_and_set_bit(MD_RECOVERY_INTR,
-						      &mddev->recovery))
-					printk(KERN_INFO "md/raid10:%s: insufficient "
-					       "working devices for recovery.\n",
-					       mdname(mddev));
+				if (any_working) {
+					/* problem is that there are bad blocks
+					 * on other device(s)
+					 */
+					int k;
+					for (k = 0; k < conf->copies; k++)
+						if (r10_bio->devs[k].devnum == i)
+							break;
+					if (!rdev_set_badblocks(
+						    conf->mirrors[i].rdev,
+						    r10_bio->devs[k].addr,
+						    max_sync, 0))
+						any_working = 0;
+				}
+				if (!any_working)  {
+					if (!test_and_set_bit(MD_RECOVERY_INTR,
+							      &mddev->recovery))
+						printk(KERN_INFO "md/raid10:%s: insufficient "
+						       "working devices for recovery.\n",
+						       mdname(mddev));
+					conf->mirrors[i].recovery_disabled
+						= mddev->recovery_disabled;
+				}
 				break;
 			}
 		}
@@ -1979,12 +2584,28 @@
 
 		for (i=0; i<conf->copies; i++) {
 			int d = r10_bio->devs[i].devnum;
+			sector_t first_bad, sector;
+			int bad_sectors;
+
 			bio = r10_bio->devs[i].bio;
 			bio->bi_end_io = NULL;
 			clear_bit(BIO_UPTODATE, &bio->bi_flags);
 			if (conf->mirrors[d].rdev == NULL ||
 			    test_bit(Faulty, &conf->mirrors[d].rdev->flags))
 				continue;
+			sector = r10_bio->devs[i].addr;
+			if (is_badblock(conf->mirrors[d].rdev,
+					sector, max_sync,
+					&first_bad, &bad_sectors)) {
+				if (first_bad > sector)
+					max_sync = first_bad - sector;
+				else {
+					bad_sectors -= (sector - first_bad);
+					if (max_sync > bad_sectors)
+						max_sync = max_sync;
+					continue;
+				}
+			}
 			atomic_inc(&conf->mirrors[d].rdev->nr_pending);
 			atomic_inc(&r10_bio->remaining);
 			bio->bi_next = biolist;
@@ -1992,7 +2613,7 @@
 			bio->bi_private = r10_bio;
 			bio->bi_end_io = end_sync_read;
 			bio->bi_rw = READ;
-			bio->bi_sector = r10_bio->devs[i].addr +
+			bio->bi_sector = sector +
 				conf->mirrors[d].rdev->data_offset;
 			bio->bi_bdev = conf->mirrors[d].rdev->bdev;
 			count++;
@@ -2079,7 +2700,8 @@
 	return sectors_skipped + nr_sectors;
  giveup:
 	/* There is nowhere to write, so all non-sync
-	 * drives must be failed, so try the next chunk...
+	 * drives must be failed or in resync, all drives
+	 * have a bad block, so try the next chunk...
 	 */
 	if (sector_nr + max_sync < max_sector)
 		max_sector = sector_nr + max_sync;
@@ -2249,6 +2871,7 @@
 				 (conf->raid_disks / conf->near_copies));
 
 	list_for_each_entry(rdev, &mddev->disks, same_set) {
+
 		disk_idx = rdev->raid_disk;
 		if (disk_idx >= conf->raid_disks
 		    || disk_idx < 0)
@@ -2271,7 +2894,7 @@
 		disk->head_position = 0;
 	}
 	/* need to check that every block has at least one working mirror */
-	if (!enough(conf)) {
+	if (!enough(conf, -1)) {
 		printk(KERN_ERR "md/raid10:%s: not enough operational mirrors.\n",
 		       mdname(mddev));
 		goto out_free_conf;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 944b110..79cb52a 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -6,6 +6,11 @@
 struct mirror_info {
 	mdk_rdev_t	*rdev;
 	sector_t	head_position;
+	int		recovery_disabled;	/* matches
+						 * mddev->recovery_disabled
+						 * when we shouldn't try
+						 * recovering this device.
+						 */
 };
 
 typedef struct r10bio_s r10bio_t;
@@ -113,10 +118,26 @@
  * level, we store IO_BLOCKED in the appropriate 'bios' pointer
  */
 #define IO_BLOCKED ((struct bio*)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context.  So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
 
 /* bits for r10bio.state */
 #define	R10BIO_Uptodate	0
 #define	R10BIO_IsSync	1
 #define	R10BIO_IsRecover 2
 #define	R10BIO_Degraded 3
+/* Set ReadError on bios that experience a read error
+ * so that raid10d knows what to do with them.
+ */
+#define	R10BIO_ReadError 4
+/* If a write for this request means we can clear some
+ * known-bad-block records, we set this flag.
+ */
+#define	R10BIO_MadeGood 5
+#define	R10BIO_WriteError 6
 #endif
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b72edf3..dbae459 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -51,6 +51,7 @@
 #include <linux/seq_file.h>
 #include <linux/cpu.h>
 #include <linux/slab.h>
+#include <linux/ratelimit.h>
 #include "md.h"
 #include "raid5.h"
 #include "raid0.h"
@@ -96,8 +97,6 @@
 #define __inline__
 #endif
 
-#define printk_rl(args...) ((void) (printk_ratelimit() && printk(args)))
-
 /*
  * We maintain a biased count of active stripes in the bottom 16 bits of
  * bi_phys_segments, and a count of processed stripes in the upper 16 bits
@@ -341,7 +340,7 @@
 			       (unsigned long long)sh->sector, i, dev->toread,
 			       dev->read, dev->towrite, dev->written,
 			       test_bit(R5_LOCKED, &dev->flags));
-			BUG();
+			WARN_ON(1);
 		}
 		dev->flags = 0;
 		raid5_build_block(sh, i, previous);
@@ -527,6 +526,36 @@
 			atomic_inc(&rdev->nr_pending);
 		rcu_read_unlock();
 
+		/* We have already checked bad blocks for reads.  Now
+		 * need to check for writes.
+		 */
+		while ((rw & WRITE) && rdev &&
+		       test_bit(WriteErrorSeen, &rdev->flags)) {
+			sector_t first_bad;
+			int bad_sectors;
+			int bad = is_badblock(rdev, sh->sector, STRIPE_SECTORS,
+					      &first_bad, &bad_sectors);
+			if (!bad)
+				break;
+
+			if (bad < 0) {
+				set_bit(BlockedBadBlocks, &rdev->flags);
+				if (!conf->mddev->external &&
+				    conf->mddev->flags) {
+					/* It is very unlikely, but we might
+					 * still need to write out the
+					 * bad block log - better give it
+					 * a chance*/
+					md_check_recovery(conf->mddev);
+				}
+				md_wait_for_blocked_rdev(rdev, conf->mddev);
+			} else {
+				/* Acknowledged bad block - skip the write */
+				rdev_dec_pending(rdev, conf->mddev);
+				rdev = NULL;
+			}
+		}
+
 		if (rdev) {
 			if (s->syncing || s->expanding || s->expanded)
 				md_sync_acct(rdev->bdev, STRIPE_SECTORS);
@@ -548,10 +577,6 @@
 			bi->bi_io_vec[0].bv_offset = 0;
 			bi->bi_size = STRIPE_SIZE;
 			bi->bi_next = NULL;
-			if ((rw & WRITE) &&
-			    test_bit(R5_ReWrite, &sh->dev[i].flags))
-				atomic_add(STRIPE_SECTORS,
-					&rdev->corrected_errors);
 			generic_make_request(bi);
 		} else {
 			if (rw & WRITE)
@@ -1020,12 +1045,12 @@
 		if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) {
 			struct bio *wbi;
 
-			spin_lock(&sh->lock);
+			spin_lock_irq(&sh->raid_conf->device_lock);
 			chosen = dev->towrite;
 			dev->towrite = NULL;
 			BUG_ON(dev->written);
 			wbi = dev->written = chosen;
-			spin_unlock(&sh->lock);
+			spin_unlock_irq(&sh->raid_conf->device_lock);
 
 			while (wbi && wbi->bi_sector <
 				dev->sector + STRIPE_SECTORS) {
@@ -1315,12 +1340,11 @@
 static int grow_one_stripe(raid5_conf_t *conf)
 {
 	struct stripe_head *sh;
-	sh = kmem_cache_alloc(conf->slab_cache, GFP_KERNEL);
+	sh = kmem_cache_zalloc(conf->slab_cache, GFP_KERNEL);
 	if (!sh)
 		return 0;
-	memset(sh, 0, sizeof(*sh) + (conf->pool_size-1)*sizeof(struct r5dev));
+
 	sh->raid_conf = conf;
-	spin_lock_init(&sh->lock);
 	#ifdef CONFIG_MULTICORE_RAID456
 	init_waitqueue_head(&sh->ops.wait_for_ops);
 	#endif
@@ -1435,14 +1459,11 @@
 		return -ENOMEM;
 
 	for (i = conf->max_nr_stripes; i; i--) {
-		nsh = kmem_cache_alloc(sc, GFP_KERNEL);
+		nsh = kmem_cache_zalloc(sc, GFP_KERNEL);
 		if (!nsh)
 			break;
 
-		memset(nsh, 0, sizeof(*nsh) + (newsize-1)*sizeof(struct r5dev));
-
 		nsh->raid_conf = conf;
-		spin_lock_init(&nsh->lock);
 		#ifdef CONFIG_MULTICORE_RAID456
 		init_waitqueue_head(&nsh->ops.wait_for_ops);
 		#endif
@@ -1587,12 +1608,15 @@
 		set_bit(R5_UPTODATE, &sh->dev[i].flags);
 		if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
 			rdev = conf->disks[i].rdev;
-			printk_rl(KERN_INFO "md/raid:%s: read error corrected"
-				  " (%lu sectors at %llu on %s)\n",
-				  mdname(conf->mddev), STRIPE_SECTORS,
-				  (unsigned long long)(sh->sector
-						       + rdev->data_offset),
-				  bdevname(rdev->bdev, b));
+			printk_ratelimited(
+				KERN_INFO
+				"md/raid:%s: read error corrected"
+				" (%lu sectors at %llu on %s)\n",
+				mdname(conf->mddev), STRIPE_SECTORS,
+				(unsigned long long)(sh->sector
+						     + rdev->data_offset),
+				bdevname(rdev->bdev, b));
+			atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
 			clear_bit(R5_ReadError, &sh->dev[i].flags);
 			clear_bit(R5_ReWrite, &sh->dev[i].flags);
 		}
@@ -1606,22 +1630,24 @@
 		clear_bit(R5_UPTODATE, &sh->dev[i].flags);
 		atomic_inc(&rdev->read_errors);
 		if (conf->mddev->degraded >= conf->max_degraded)
-			printk_rl(KERN_WARNING
-				  "md/raid:%s: read error not correctable "
-				  "(sector %llu on %s).\n",
-				  mdname(conf->mddev),
-				  (unsigned long long)(sh->sector
-						       + rdev->data_offset),
-				  bdn);
+			printk_ratelimited(
+				KERN_WARNING
+				"md/raid:%s: read error not correctable "
+				"(sector %llu on %s).\n",
+				mdname(conf->mddev),
+				(unsigned long long)(sh->sector
+						     + rdev->data_offset),
+				bdn);
 		else if (test_bit(R5_ReWrite, &sh->dev[i].flags))
 			/* Oh, no!!! */
-			printk_rl(KERN_WARNING
-				  "md/raid:%s: read error NOT corrected!! "
-				  "(sector %llu on %s).\n",
-				  mdname(conf->mddev),
-				  (unsigned long long)(sh->sector
-						       + rdev->data_offset),
-				  bdn);
+			printk_ratelimited(
+				KERN_WARNING
+				"md/raid:%s: read error NOT corrected!! "
+				"(sector %llu on %s).\n",
+				mdname(conf->mddev),
+				(unsigned long long)(sh->sector
+						     + rdev->data_offset),
+				bdn);
 		else if (atomic_read(&rdev->read_errors)
 			 > conf->max_nr_stripes)
 			printk(KERN_WARNING
@@ -1649,6 +1675,8 @@
 	raid5_conf_t *conf = sh->raid_conf;
 	int disks = sh->disks, i;
 	int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
+	sector_t first_bad;
+	int bad_sectors;
 
 	for (i=0 ; i<disks; i++)
 		if (bi == &sh->dev[i].req)
@@ -1662,8 +1690,12 @@
 		return;
 	}
 
-	if (!uptodate)
-		md_error(conf->mddev, conf->disks[i].rdev);
+	if (!uptodate) {
+		set_bit(WriteErrorSeen, &conf->disks[i].rdev->flags);
+		set_bit(R5_WriteError, &sh->dev[i].flags);
+	} else if (is_badblock(conf->disks[i].rdev, sh->sector, STRIPE_SECTORS,
+			       &first_bad, &bad_sectors))
+		set_bit(R5_MadeGood, &sh->dev[i].flags);
 
 	rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
 	
@@ -1710,6 +1742,7 @@
 		 */
 		set_bit(MD_RECOVERY_INTR, &mddev->recovery);
 	}
+	set_bit(Blocked, &rdev->flags);
 	set_bit(Faulty, &rdev->flags);
 	set_bit(MD_CHANGE_DEVS, &mddev->flags);
 	printk(KERN_ALERT
@@ -1760,7 +1793,7 @@
 	/*
 	 * Select the parity disk based on the user selected algorithm.
 	 */
-	pd_idx = qd_idx = ~0;
+	pd_idx = qd_idx = -1;
 	switch(conf->level) {
 	case 4:
 		pd_idx = data_disks;
@@ -2143,12 +2176,11 @@
 	raid5_conf_t *conf = sh->raid_conf;
 	int firstwrite=0;
 
-	pr_debug("adding bh b#%llu to stripe s#%llu\n",
+	pr_debug("adding bi b#%llu to stripe s#%llu\n",
 		(unsigned long long)bi->bi_sector,
 		(unsigned long long)sh->sector);
 
 
-	spin_lock(&sh->lock);
 	spin_lock_irq(&conf->device_lock);
 	if (forwrite) {
 		bip = &sh->dev[dd_idx].towrite;
@@ -2169,19 +2201,6 @@
 		bi->bi_next = *bip;
 	*bip = bi;
 	bi->bi_phys_segments++;
-	spin_unlock_irq(&conf->device_lock);
-	spin_unlock(&sh->lock);
-
-	pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
-		(unsigned long long)bi->bi_sector,
-		(unsigned long long)sh->sector, dd_idx);
-
-	if (conf->mddev->bitmap && firstwrite) {
-		bitmap_startwrite(conf->mddev->bitmap, sh->sector,
-				  STRIPE_SECTORS, 0);
-		sh->bm_seq = conf->seq_flush+1;
-		set_bit(STRIPE_BIT_DELAY, &sh->state);
-	}
 
 	if (forwrite) {
 		/* check if page is covered */
@@ -2196,12 +2215,23 @@
 		if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
 			set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
 	}
+	spin_unlock_irq(&conf->device_lock);
+
+	pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
+		(unsigned long long)(*bip)->bi_sector,
+		(unsigned long long)sh->sector, dd_idx);
+
+	if (conf->mddev->bitmap && firstwrite) {
+		bitmap_startwrite(conf->mddev->bitmap, sh->sector,
+				  STRIPE_SECTORS, 0);
+		sh->bm_seq = conf->seq_flush+1;
+		set_bit(STRIPE_BIT_DELAY, &sh->state);
+	}
 	return 1;
 
  overlap:
 	set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
 	spin_unlock_irq(&conf->device_lock);
-	spin_unlock(&sh->lock);
 	return 0;
 }
 
@@ -2238,9 +2268,18 @@
 			rcu_read_lock();
 			rdev = rcu_dereference(conf->disks[i].rdev);
 			if (rdev && test_bit(In_sync, &rdev->flags))
-				/* multiple read failures in one stripe */
-				md_error(conf->mddev, rdev);
+				atomic_inc(&rdev->nr_pending);
+			else
+				rdev = NULL;
 			rcu_read_unlock();
+			if (rdev) {
+				if (!rdev_set_badblocks(
+					    rdev,
+					    sh->sector,
+					    STRIPE_SECTORS, 0))
+					md_error(conf->mddev, rdev);
+				rdev_dec_pending(rdev, conf->mddev);
+			}
 		}
 		spin_lock_irq(&conf->device_lock);
 		/* fail all writes first */
@@ -2308,6 +2347,10 @@
 		if (bitmap_end)
 			bitmap_endwrite(conf->mddev->bitmap, sh->sector,
 					STRIPE_SECTORS, 0, 0);
+		/* If we were in the middle of a write the parity block might
+		 * still be locked - so just clear all R5_LOCKED flags
+		 */
+		clear_bit(R5_LOCKED, &sh->dev[i].flags);
 	}
 
 	if (test_and_clear_bit(STRIPE_FULL_WRITE, &sh->state))
@@ -2315,17 +2358,53 @@
 			md_wakeup_thread(conf->mddev->thread);
 }
 
-/* fetch_block5 - checks the given member device to see if its data needs
+static void
+handle_failed_sync(raid5_conf_t *conf, struct stripe_head *sh,
+		   struct stripe_head_state *s)
+{
+	int abort = 0;
+	int i;
+
+	md_done_sync(conf->mddev, STRIPE_SECTORS, 0);
+	clear_bit(STRIPE_SYNCING, &sh->state);
+	s->syncing = 0;
+	/* There is nothing more to do for sync/check/repair.
+	 * For recover we need to record a bad block on all
+	 * non-sync devices, or abort the recovery
+	 */
+	if (!test_bit(MD_RECOVERY_RECOVER, &conf->mddev->recovery))
+		return;
+	/* During recovery devices cannot be removed, so locking and
+	 * refcounting of rdevs is not needed
+	 */
+	for (i = 0; i < conf->raid_disks; i++) {
+		mdk_rdev_t *rdev = conf->disks[i].rdev;
+		if (!rdev
+		    || test_bit(Faulty, &rdev->flags)
+		    || test_bit(In_sync, &rdev->flags))
+			continue;
+		if (!rdev_set_badblocks(rdev, sh->sector,
+					STRIPE_SECTORS, 0))
+			abort = 1;
+	}
+	if (abort) {
+		conf->recovery_disabled = conf->mddev->recovery_disabled;
+		set_bit(MD_RECOVERY_INTR, &conf->mddev->recovery);
+	}
+}
+
+/* fetch_block - checks the given member device to see if its data needs
  * to be read or computed to satisfy a request.
  *
  * Returns 1 when no more member devices need to be checked, otherwise returns
- * 0 to tell the loop in handle_stripe_fill5 to continue
+ * 0 to tell the loop in handle_stripe_fill to continue
  */
-static int fetch_block5(struct stripe_head *sh, struct stripe_head_state *s,
-			int disk_idx, int disks)
+static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
+		       int disk_idx, int disks)
 {
 	struct r5dev *dev = &sh->dev[disk_idx];
-	struct r5dev *failed_dev = &sh->dev[s->failed_num];
+	struct r5dev *fdev[2] = { &sh->dev[s->failed_num[0]],
+				  &sh->dev[s->failed_num[1]] };
 
 	/* is the data in this block needed, and can we get it? */
 	if (!test_bit(R5_LOCKED, &dev->flags) &&
@@ -2333,91 +2412,19 @@
 	    (dev->toread ||
 	     (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
 	     s->syncing || s->expanding ||
-	     (s->failed &&
-	      (failed_dev->toread ||
-	       (failed_dev->towrite &&
-		!test_bit(R5_OVERWRITE, &failed_dev->flags)))))) {
-		/* We would like to get this block, possibly by computing it,
-		 * otherwise read it if the backing disk is insync
-		 */
-		if ((s->uptodate == disks - 1) &&
-		    (s->failed && disk_idx == s->failed_num)) {
-			set_bit(STRIPE_COMPUTE_RUN, &sh->state);
-			set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request);
-			set_bit(R5_Wantcompute, &dev->flags);
-			sh->ops.target = disk_idx;
-			sh->ops.target2 = -1;
-			s->req_compute = 1;
-			/* Careful: from this point on 'uptodate' is in the eye
-			 * of raid_run_ops which services 'compute' operations
-			 * before writes. R5_Wantcompute flags a block that will
-			 * be R5_UPTODATE by the time it is needed for a
-			 * subsequent operation.
-			 */
-			s->uptodate++;
-			return 1; /* uptodate + compute == disks */
-		} else if (test_bit(R5_Insync, &dev->flags)) {
-			set_bit(R5_LOCKED, &dev->flags);
-			set_bit(R5_Wantread, &dev->flags);
-			s->locked++;
-			pr_debug("Reading block %d (sync=%d)\n", disk_idx,
-				s->syncing);
-		}
-	}
-
-	return 0;
-}
-
-/**
- * handle_stripe_fill5 - read or compute data to satisfy pending requests.
- */
-static void handle_stripe_fill5(struct stripe_head *sh,
-			struct stripe_head_state *s, int disks)
-{
-	int i;
-
-	/* look for blocks to read/compute, skip this if a compute
-	 * is already in flight, or if the stripe contents are in the
-	 * midst of changing due to a write
-	 */
-	if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state &&
-	    !sh->reconstruct_state)
-		for (i = disks; i--; )
-			if (fetch_block5(sh, s, i, disks))
-				break;
-	set_bit(STRIPE_HANDLE, &sh->state);
-}
-
-/* fetch_block6 - checks the given member device to see if its data needs
- * to be read or computed to satisfy a request.
- *
- * Returns 1 when no more member devices need to be checked, otherwise returns
- * 0 to tell the loop in handle_stripe_fill6 to continue
- */
-static int fetch_block6(struct stripe_head *sh, struct stripe_head_state *s,
-			 struct r6_state *r6s, int disk_idx, int disks)
-{
-	struct r5dev *dev = &sh->dev[disk_idx];
-	struct r5dev *fdev[2] = { &sh->dev[r6s->failed_num[0]],
-				  &sh->dev[r6s->failed_num[1]] };
-
-	if (!test_bit(R5_LOCKED, &dev->flags) &&
-	    !test_bit(R5_UPTODATE, &dev->flags) &&
-	    (dev->toread ||
-	     (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
-	     s->syncing || s->expanding ||
-	     (s->failed >= 1 &&
-	      (fdev[0]->toread || s->to_write)) ||
-	     (s->failed >= 2 &&
-	      (fdev[1]->toread || s->to_write)))) {
+	     (s->failed >= 1 && fdev[0]->toread) ||
+	     (s->failed >= 2 && fdev[1]->toread) ||
+	     (sh->raid_conf->level <= 5 && s->failed && fdev[0]->towrite &&
+	      !test_bit(R5_OVERWRITE, &fdev[0]->flags)) ||
+	     (sh->raid_conf->level == 6 && s->failed && s->to_write))) {
 		/* we would like to get this block, possibly by computing it,
 		 * otherwise read it if the backing disk is insync
 		 */
 		BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
 		BUG_ON(test_bit(R5_Wantread, &dev->flags));
 		if ((s->uptodate == disks - 1) &&
-		    (s->failed && (disk_idx == r6s->failed_num[0] ||
-				   disk_idx == r6s->failed_num[1]))) {
+		    (s->failed && (disk_idx == s->failed_num[0] ||
+				   disk_idx == s->failed_num[1]))) {
 			/* have disk failed, and we're requested to fetch it;
 			 * do compute it
 			 */
@@ -2429,6 +2436,12 @@
 			sh->ops.target = disk_idx;
 			sh->ops.target2 = -1; /* no 2nd target */
 			s->req_compute = 1;
+			/* Careful: from this point on 'uptodate' is in the eye
+			 * of raid_run_ops which services 'compute' operations
+			 * before writes. R5_Wantcompute flags a block that will
+			 * be R5_UPTODATE by the time it is needed for a
+			 * subsequent operation.
+			 */
 			s->uptodate++;
 			return 1;
 		} else if (s->uptodate == disks-2 && s->failed >= 2) {
@@ -2469,11 +2482,11 @@
 }
 
 /**
- * handle_stripe_fill6 - read or compute data to satisfy pending requests.
+ * handle_stripe_fill - read or compute data to satisfy pending requests.
  */
-static void handle_stripe_fill6(struct stripe_head *sh,
-			struct stripe_head_state *s, struct r6_state *r6s,
-			int disks)
+static void handle_stripe_fill(struct stripe_head *sh,
+			       struct stripe_head_state *s,
+			       int disks)
 {
 	int i;
 
@@ -2484,7 +2497,7 @@
 	if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state &&
 	    !sh->reconstruct_state)
 		for (i = disks; i--; )
-			if (fetch_block6(sh, s, r6s, i, disks))
+			if (fetch_block(sh, s, i, disks))
 				break;
 	set_bit(STRIPE_HANDLE, &sh->state);
 }
@@ -2540,11 +2553,19 @@
 			md_wakeup_thread(conf->mddev->thread);
 }
 
-static void handle_stripe_dirtying5(raid5_conf_t *conf,
-		struct stripe_head *sh,	struct stripe_head_state *s, int disks)
+static void handle_stripe_dirtying(raid5_conf_t *conf,
+				   struct stripe_head *sh,
+				   struct stripe_head_state *s,
+				   int disks)
 {
 	int rmw = 0, rcw = 0, i;
-	for (i = disks; i--; ) {
+	if (conf->max_degraded == 2) {
+		/* RAID6 requires 'rcw' in current implementation
+		 * Calculate the real rcw later - for now fake it
+		 * look like rcw is cheaper
+		 */
+		rcw = 1; rmw = 2;
+	} else for (i = disks; i--; ) {
 		/* would I have to read this buffer for read_modify_write */
 		struct r5dev *dev = &sh->dev[i];
 		if ((dev->towrite || i == sh->pd_idx) &&
@@ -2591,16 +2612,19 @@
 				}
 			}
 		}
-	if (rcw <= rmw && rcw > 0)
+	if (rcw <= rmw && rcw > 0) {
 		/* want reconstruct write, but need to get some data */
+		rcw = 0;
 		for (i = disks; i--; ) {
 			struct r5dev *dev = &sh->dev[i];
 			if (!test_bit(R5_OVERWRITE, &dev->flags) &&
-			    i != sh->pd_idx &&
+			    i != sh->pd_idx && i != sh->qd_idx &&
 			    !test_bit(R5_LOCKED, &dev->flags) &&
 			    !(test_bit(R5_UPTODATE, &dev->flags) ||
-			    test_bit(R5_Wantcompute, &dev->flags)) &&
-			    test_bit(R5_Insync, &dev->flags)) {
+			      test_bit(R5_Wantcompute, &dev->flags))) {
+				rcw++;
+				if (!test_bit(R5_Insync, &dev->flags))
+					continue; /* it's a failed drive */
 				if (
 				  test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
 					pr_debug("Read_old block "
@@ -2614,6 +2638,7 @@
 				}
 			}
 		}
+	}
 	/* now if nothing is locked, and if we have enough data,
 	 * we can start a write request
 	 */
@@ -2630,53 +2655,6 @@
 		schedule_reconstruction(sh, s, rcw == 0, 0);
 }
 
-static void handle_stripe_dirtying6(raid5_conf_t *conf,
-		struct stripe_head *sh,	struct stripe_head_state *s,
-		struct r6_state *r6s, int disks)
-{
-	int rcw = 0, pd_idx = sh->pd_idx, i;
-	int qd_idx = sh->qd_idx;
-
-	set_bit(STRIPE_HANDLE, &sh->state);
-	for (i = disks; i--; ) {
-		struct r5dev *dev = &sh->dev[i];
-		/* check if we haven't enough data */
-		if (!test_bit(R5_OVERWRITE, &dev->flags) &&
-		    i != pd_idx && i != qd_idx &&
-		    !test_bit(R5_LOCKED, &dev->flags) &&
-		    !(test_bit(R5_UPTODATE, &dev->flags) ||
-		      test_bit(R5_Wantcompute, &dev->flags))) {
-			rcw++;
-			if (!test_bit(R5_Insync, &dev->flags))
-				continue; /* it's a failed drive */
-
-			if (
-			  test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
-				pr_debug("Read_old stripe %llu "
-					"block %d for Reconstruct\n",
-				     (unsigned long long)sh->sector, i);
-				set_bit(R5_LOCKED, &dev->flags);
-				set_bit(R5_Wantread, &dev->flags);
-				s->locked++;
-			} else {
-				pr_debug("Request delayed stripe %llu "
-					"block %d for Reconstruct\n",
-				     (unsigned long long)sh->sector, i);
-				set_bit(STRIPE_DELAYED, &sh->state);
-				set_bit(STRIPE_HANDLE, &sh->state);
-			}
-		}
-	}
-	/* now if nothing is locked, and if we have enough data, we can start a
-	 * write request
-	 */
-	if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) &&
-	    s->locked == 0 && rcw == 0 &&
-	    !test_bit(STRIPE_BIT_DELAY, &sh->state)) {
-		schedule_reconstruction(sh, s, 1, 0);
-	}
-}
-
 static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,
 				struct stripe_head_state *s, int disks)
 {
@@ -2695,7 +2673,7 @@
 			s->uptodate--;
 			break;
 		}
-		dev = &sh->dev[s->failed_num];
+		dev = &sh->dev[s->failed_num[0]];
 		/* fall through */
 	case check_state_compute_result:
 		sh->check_state = check_state_idle;
@@ -2767,7 +2745,7 @@
 
 static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,
 				  struct stripe_head_state *s,
-				  struct r6_state *r6s, int disks)
+				  int disks)
 {
 	int pd_idx = sh->pd_idx;
 	int qd_idx = sh->qd_idx;
@@ -2786,14 +2764,14 @@
 	switch (sh->check_state) {
 	case check_state_idle:
 		/* start a new check operation if there are < 2 failures */
-		if (s->failed == r6s->q_failed) {
+		if (s->failed == s->q_failed) {
 			/* The only possible failed device holds Q, so it
 			 * makes sense to check P (If anything else were failed,
 			 * we would have used P to recreate it).
 			 */
 			sh->check_state = check_state_run;
 		}
-		if (!r6s->q_failed && s->failed < 2) {
+		if (!s->q_failed && s->failed < 2) {
 			/* Q is not failed, and we didn't use it to generate
 			 * anything, so it makes sense to check it
 			 */
@@ -2835,13 +2813,13 @@
 		 */
 		BUG_ON(s->uptodate < disks - 1); /* We don't need Q to recover */
 		if (s->failed == 2) {
-			dev = &sh->dev[r6s->failed_num[1]];
+			dev = &sh->dev[s->failed_num[1]];
 			s->locked++;
 			set_bit(R5_LOCKED, &dev->flags);
 			set_bit(R5_Wantwrite, &dev->flags);
 		}
 		if (s->failed >= 1) {
-			dev = &sh->dev[r6s->failed_num[0]];
+			dev = &sh->dev[s->failed_num[0]];
 			s->locked++;
 			set_bit(R5_LOCKED, &dev->flags);
 			set_bit(R5_Wantwrite, &dev->flags);
@@ -2928,8 +2906,7 @@
 	}
 }
 
-static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
-				struct r6_state *r6s)
+static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh)
 {
 	int i;
 
@@ -2971,7 +2948,7 @@
 			set_bit(R5_UPTODATE, &sh2->dev[dd_idx].flags);
 			for (j = 0; j < conf->raid_disks; j++)
 				if (j != sh2->pd_idx &&
-				    (!r6s || j != sh2->qd_idx) &&
+				    j != sh2->qd_idx &&
 				    !test_bit(R5_Expanded, &sh2->dev[j].flags))
 					break;
 			if (j == conf->raid_disks) {
@@ -3006,43 +2983,35 @@
  *
  */
 
-static void handle_stripe5(struct stripe_head *sh)
+static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
 {
 	raid5_conf_t *conf = sh->raid_conf;
-	int disks = sh->disks, i;
-	struct bio *return_bi = NULL;
-	struct stripe_head_state s;
+	int disks = sh->disks;
 	struct r5dev *dev;
-	mdk_rdev_t *blocked_rdev = NULL;
-	int prexor;
-	int dec_preread_active = 0;
+	int i;
 
-	memset(&s, 0, sizeof(s));
-	pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d "
-		 "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state,
-		 atomic_read(&sh->count), sh->pd_idx, sh->check_state,
-		 sh->reconstruct_state);
+	memset(s, 0, sizeof(*s));
 
-	spin_lock(&sh->lock);
-	clear_bit(STRIPE_HANDLE, &sh->state);
-	clear_bit(STRIPE_DELAYED, &sh->state);
-
-	s.syncing = test_bit(STRIPE_SYNCING, &sh->state);
-	s.expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
-	s.expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
+	s->syncing = test_bit(STRIPE_SYNCING, &sh->state);
+	s->expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
+	s->expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
+	s->failed_num[0] = -1;
+	s->failed_num[1] = -1;
 
 	/* Now to look around and see what can be done */
 	rcu_read_lock();
+	spin_lock_irq(&conf->device_lock);
 	for (i=disks; i--; ) {
 		mdk_rdev_t *rdev;
+		sector_t first_bad;
+		int bad_sectors;
+		int is_bad = 0;
 
 		dev = &sh->dev[i];
 
-		pr_debug("check %d: state 0x%lx toread %p read %p write %p "
-			"written %p\n",	i, dev->flags, dev->toread, dev->read,
-			dev->towrite, dev->written);
-
-		/* maybe we can request a biofill operation
+		pr_debug("check %d: state 0x%lx read %p write %p written %p\n",
+			i, dev->flags, dev->toread, dev->towrite, dev->written);
+		/* maybe we can reply to a read
 		 *
 		 * new wantfill requests are only permitted while
 		 * ops_complete_biofill is guaranteed to be inactive
@@ -3052,37 +3021,74 @@
 			set_bit(R5_Wantfill, &dev->flags);
 
 		/* now count some things */
-		if (test_bit(R5_LOCKED, &dev->flags)) s.locked++;
-		if (test_bit(R5_UPTODATE, &dev->flags)) s.uptodate++;
-		if (test_bit(R5_Wantcompute, &dev->flags)) s.compute++;
+		if (test_bit(R5_LOCKED, &dev->flags))
+			s->locked++;
+		if (test_bit(R5_UPTODATE, &dev->flags))
+			s->uptodate++;
+		if (test_bit(R5_Wantcompute, &dev->flags)) {
+			s->compute++;
+			BUG_ON(s->compute > 2);
+		}
 
 		if (test_bit(R5_Wantfill, &dev->flags))
-			s.to_fill++;
+			s->to_fill++;
 		else if (dev->toread)
-			s.to_read++;
+			s->to_read++;
 		if (dev->towrite) {
-			s.to_write++;
+			s->to_write++;
 			if (!test_bit(R5_OVERWRITE, &dev->flags))
-				s.non_overwrite++;
+				s->non_overwrite++;
 		}
 		if (dev->written)
-			s.written++;
+			s->written++;
 		rdev = rcu_dereference(conf->disks[i].rdev);
-		if (blocked_rdev == NULL &&
-		    rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
-			blocked_rdev = rdev;
-			atomic_inc(&rdev->nr_pending);
+		if (rdev) {
+			is_bad = is_badblock(rdev, sh->sector, STRIPE_SECTORS,
+					     &first_bad, &bad_sectors);
+			if (s->blocked_rdev == NULL
+			    && (test_bit(Blocked, &rdev->flags)
+				|| is_bad < 0)) {
+				if (is_bad < 0)
+					set_bit(BlockedBadBlocks,
+						&rdev->flags);
+				s->blocked_rdev = rdev;
+				atomic_inc(&rdev->nr_pending);
+			}
 		}
 		clear_bit(R5_Insync, &dev->flags);
 		if (!rdev)
 			/* Not in-sync */;
-		else if (test_bit(In_sync, &rdev->flags))
+		else if (is_bad) {
+			/* also not in-sync */
+			if (!test_bit(WriteErrorSeen, &rdev->flags)) {
+				/* treat as in-sync, but with a read error
+				 * which we can now try to correct
+				 */
+				set_bit(R5_Insync, &dev->flags);
+				set_bit(R5_ReadError, &dev->flags);
+			}
+		} else if (test_bit(In_sync, &rdev->flags))
 			set_bit(R5_Insync, &dev->flags);
 		else {
-			/* could be in-sync depending on recovery/reshape status */
+			/* in sync if before recovery_offset */
 			if (sh->sector + STRIPE_SECTORS <= rdev->recovery_offset)
 				set_bit(R5_Insync, &dev->flags);
 		}
+		if (test_bit(R5_WriteError, &dev->flags)) {
+			clear_bit(R5_Insync, &dev->flags);
+			if (!test_bit(Faulty, &rdev->flags)) {
+				s->handle_bad_blocks = 1;
+				atomic_inc(&rdev->nr_pending);
+			} else
+				clear_bit(R5_WriteError, &dev->flags);
+		}
+		if (test_bit(R5_MadeGood, &dev->flags)) {
+			if (!test_bit(Faulty, &rdev->flags)) {
+				s->handle_bad_blocks = 1;
+				atomic_inc(&rdev->nr_pending);
+			} else
+				clear_bit(R5_MadeGood, &dev->flags);
+		}
 		if (!test_bit(R5_Insync, &dev->flags)) {
 			/* The ReadError flag will just be confusing now */
 			clear_bit(R5_ReadError, &dev->flags);
@@ -3091,21 +3097,60 @@
 		if (test_bit(R5_ReadError, &dev->flags))
 			clear_bit(R5_Insync, &dev->flags);
 		if (!test_bit(R5_Insync, &dev->flags)) {
-			s.failed++;
-			s.failed_num = i;
+			if (s->failed < 2)
+				s->failed_num[s->failed] = i;
+			s->failed++;
 		}
 	}
+	spin_unlock_irq(&conf->device_lock);
 	rcu_read_unlock();
+}
 
-	if (unlikely(blocked_rdev)) {
+static void handle_stripe(struct stripe_head *sh)
+{
+	struct stripe_head_state s;
+	raid5_conf_t *conf = sh->raid_conf;
+	int i;
+	int prexor;
+	int disks = sh->disks;
+	struct r5dev *pdev, *qdev;
+
+	clear_bit(STRIPE_HANDLE, &sh->state);
+	if (test_and_set_bit(STRIPE_ACTIVE, &sh->state)) {
+		/* already being handled, ensure it gets handled
+		 * again when current action finishes */
+		set_bit(STRIPE_HANDLE, &sh->state);
+		return;
+	}
+
+	if (test_and_clear_bit(STRIPE_SYNC_REQUESTED, &sh->state)) {
+		set_bit(STRIPE_SYNCING, &sh->state);
+		clear_bit(STRIPE_INSYNC, &sh->state);
+	}
+	clear_bit(STRIPE_DELAYED, &sh->state);
+
+	pr_debug("handling stripe %llu, state=%#lx cnt=%d, "
+		"pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n",
+	       (unsigned long long)sh->sector, sh->state,
+	       atomic_read(&sh->count), sh->pd_idx, sh->qd_idx,
+	       sh->check_state, sh->reconstruct_state);
+
+	analyse_stripe(sh, &s);
+
+	if (s.handle_bad_blocks) {
+		set_bit(STRIPE_HANDLE, &sh->state);
+		goto finish;
+	}
+
+	if (unlikely(s.blocked_rdev)) {
 		if (s.syncing || s.expanding || s.expanded ||
 		    s.to_write || s.written) {
 			set_bit(STRIPE_HANDLE, &sh->state);
-			goto unlock;
+			goto finish;
 		}
 		/* There is nothing for the blocked_rdev to block */
-		rdev_dec_pending(blocked_rdev, conf->mddev);
-		blocked_rdev = NULL;
+		rdev_dec_pending(s.blocked_rdev, conf->mddev);
+		s.blocked_rdev = NULL;
 	}
 
 	if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) {
@@ -3114,38 +3159,46 @@
 	}
 
 	pr_debug("locked=%d uptodate=%d to_read=%d"
-		" to_write=%d failed=%d failed_num=%d\n",
-		s.locked, s.uptodate, s.to_read, s.to_write,
-		s.failed, s.failed_num);
-	/* check if the array has lost two devices and, if so, some requests might
-	 * need to be failed
+	       " to_write=%d failed=%d failed_num=%d,%d\n",
+	       s.locked, s.uptodate, s.to_read, s.to_write, s.failed,
+	       s.failed_num[0], s.failed_num[1]);
+	/* check if the array has lost more than max_degraded devices and,
+	 * if so, some requests might need to be failed.
 	 */
-	if (s.failed > 1 && s.to_read+s.to_write+s.written)
-		handle_failed_stripe(conf, sh, &s, disks, &return_bi);
-	if (s.failed > 1 && s.syncing) {
-		md_done_sync(conf->mddev, STRIPE_SECTORS,0);
-		clear_bit(STRIPE_SYNCING, &sh->state);
-		s.syncing = 0;
-	}
+	if (s.failed > conf->max_degraded && s.to_read+s.to_write+s.written)
+		handle_failed_stripe(conf, sh, &s, disks, &s.return_bi);
+	if (s.failed > conf->max_degraded && s.syncing)
+		handle_failed_sync(conf, sh, &s);
 
-	/* might be able to return some write requests if the parity block
-	 * is safe, or on a failed drive
+	/*
+	 * might be able to return some write requests if the parity blocks
+	 * are safe, or on a failed drive
 	 */
-	dev = &sh->dev[sh->pd_idx];
-	if ( s.written &&
-	     ((test_bit(R5_Insync, &dev->flags) &&
-	       !test_bit(R5_LOCKED, &dev->flags) &&
-	       test_bit(R5_UPTODATE, &dev->flags)) ||
-	       (s.failed == 1 && s.failed_num == sh->pd_idx)))
-		handle_stripe_clean_event(conf, sh, disks, &return_bi);
+	pdev = &sh->dev[sh->pd_idx];
+	s.p_failed = (s.failed >= 1 && s.failed_num[0] == sh->pd_idx)
+		|| (s.failed >= 2 && s.failed_num[1] == sh->pd_idx);
+	qdev = &sh->dev[sh->qd_idx];
+	s.q_failed = (s.failed >= 1 && s.failed_num[0] == sh->qd_idx)
+		|| (s.failed >= 2 && s.failed_num[1] == sh->qd_idx)
+		|| conf->level < 6;
+
+	if (s.written &&
+	    (s.p_failed || ((test_bit(R5_Insync, &pdev->flags)
+			     && !test_bit(R5_LOCKED, &pdev->flags)
+			     && test_bit(R5_UPTODATE, &pdev->flags)))) &&
+	    (s.q_failed || ((test_bit(R5_Insync, &qdev->flags)
+			     && !test_bit(R5_LOCKED, &qdev->flags)
+			     && test_bit(R5_UPTODATE, &qdev->flags)))))
+		handle_stripe_clean_event(conf, sh, disks, &s.return_bi);
 
 	/* Now we might consider reading some blocks, either to check/generate
 	 * parity, or to satisfy requests
 	 * or to load a block that is being partially written.
 	 */
-	if (s.to_read || s.non_overwrite ||
-	    (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
-		handle_stripe_fill5(sh, &s, disks);
+	if (s.to_read || s.non_overwrite
+	    || (conf->level == 6 && s.to_write && s.failed)
+	    || (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
+		handle_stripe_fill(sh, &s, disks);
 
 	/* Now we check to see if any write operations have recently
 	 * completed
@@ -3161,21 +3214,25 @@
 		 * be written back to disk
 		 */
 		BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags));
+		BUG_ON(sh->qd_idx >= 0 &&
+		       !test_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags));
 		for (i = disks; i--; ) {
-			dev = &sh->dev[i];
+			struct r5dev *dev = &sh->dev[i];
 			if (test_bit(R5_LOCKED, &dev->flags) &&
-				(i == sh->pd_idx || dev->written)) {
+				(i == sh->pd_idx || i == sh->qd_idx ||
+				 dev->written)) {
 				pr_debug("Writing block %d\n", i);
 				set_bit(R5_Wantwrite, &dev->flags);
 				if (prexor)
 					continue;
 				if (!test_bit(R5_Insync, &dev->flags) ||
-				    (i == sh->pd_idx && s.failed == 0))
+				    ((i == sh->pd_idx || i == sh->qd_idx)  &&
+				     s.failed == 0))
 					set_bit(STRIPE_INSYNC, &sh->state);
 			}
 		}
 		if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
-			dec_preread_active = 1;
+			s.dec_preread_active = 1;
 	}
 
 	/* Now to consider new write requests and what else, if anything
@@ -3185,7 +3242,7 @@
 	 *    block.
 	 */
 	if (s.to_write && !sh->reconstruct_state && !sh->check_state)
-		handle_stripe_dirtying5(conf, sh, &s, disks);
+		handle_stripe_dirtying(conf, sh, &s, disks);
 
 	/* maybe we need to check and possibly fix the parity for this stripe
 	 * Any reads will already have been scheduled, so we just see if enough
@@ -3195,54 +3252,61 @@
 	if (sh->check_state ||
 	    (s.syncing && s.locked == 0 &&
 	     !test_bit(STRIPE_COMPUTE_RUN, &sh->state) &&
-	     !test_bit(STRIPE_INSYNC, &sh->state)))
-		handle_parity_checks5(conf, sh, &s, disks);
+	     !test_bit(STRIPE_INSYNC, &sh->state))) {
+		if (conf->level == 6)
+			handle_parity_checks6(conf, sh, &s, disks);
+		else
+			handle_parity_checks5(conf, sh, &s, disks);
+	}
 
 	if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
-		md_done_sync(conf->mddev, STRIPE_SECTORS,1);
+		md_done_sync(conf->mddev, STRIPE_SECTORS, 1);
 		clear_bit(STRIPE_SYNCING, &sh->state);
 	}
 
-	/* If the failed drive is just a ReadError, then we might need to progress
-	 * the repair/check process
+	/* If the failed drives are just a ReadError, then we might need
+	 * to progress the repair/check process
 	 */
-	if (s.failed == 1 && !conf->mddev->ro &&
-	    test_bit(R5_ReadError, &sh->dev[s.failed_num].flags)
-	    && !test_bit(R5_LOCKED, &sh->dev[s.failed_num].flags)
-	    && test_bit(R5_UPTODATE, &sh->dev[s.failed_num].flags)
-		) {
-		dev = &sh->dev[s.failed_num];
-		if (!test_bit(R5_ReWrite, &dev->flags)) {
-			set_bit(R5_Wantwrite, &dev->flags);
-			set_bit(R5_ReWrite, &dev->flags);
-			set_bit(R5_LOCKED, &dev->flags);
-			s.locked++;
-		} else {
-			/* let's read it back */
-			set_bit(R5_Wantread, &dev->flags);
-			set_bit(R5_LOCKED, &dev->flags);
-			s.locked++;
+	if (s.failed <= conf->max_degraded && !conf->mddev->ro)
+		for (i = 0; i < s.failed; i++) {
+			struct r5dev *dev = &sh->dev[s.failed_num[i]];
+			if (test_bit(R5_ReadError, &dev->flags)
+			    && !test_bit(R5_LOCKED, &dev->flags)
+			    && test_bit(R5_UPTODATE, &dev->flags)
+				) {
+				if (!test_bit(R5_ReWrite, &dev->flags)) {
+					set_bit(R5_Wantwrite, &dev->flags);
+					set_bit(R5_ReWrite, &dev->flags);
+					set_bit(R5_LOCKED, &dev->flags);
+					s.locked++;
+				} else {
+					/* let's read it back */
+					set_bit(R5_Wantread, &dev->flags);
+					set_bit(R5_LOCKED, &dev->flags);
+					s.locked++;
+				}
+			}
 		}
-	}
+
 
 	/* Finish reconstruct operations initiated by the expansion process */
 	if (sh->reconstruct_state == reconstruct_state_result) {
-		struct stripe_head *sh2
+		struct stripe_head *sh_src
 			= get_active_stripe(conf, sh->sector, 1, 1, 1);
-		if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
-			/* sh cannot be written until sh2 has been read.
+		if (sh_src && test_bit(STRIPE_EXPAND_SOURCE, &sh_src->state)) {
+			/* sh cannot be written until sh_src has been read.
 			 * so arrange for sh to be delayed a little
 			 */
 			set_bit(STRIPE_DELAYED, &sh->state);
 			set_bit(STRIPE_HANDLE, &sh->state);
 			if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE,
-					      &sh2->state))
+					      &sh_src->state))
 				atomic_inc(&conf->preread_active_stripes);
-			release_stripe(sh2);
-			goto unlock;
+			release_stripe(sh_src);
+			goto finish;
 		}
-		if (sh2)
-			release_stripe(sh2);
+		if (sh_src)
+			release_stripe(sh_src);
 
 		sh->reconstruct_state = reconstruct_state_idle;
 		clear_bit(STRIPE_EXPANDING, &sh->state);
@@ -3268,323 +3332,39 @@
 
 	if (s.expanding && s.locked == 0 &&
 	    !test_bit(STRIPE_COMPUTE_RUN, &sh->state))
-		handle_stripe_expansion(conf, sh, NULL);
+		handle_stripe_expansion(conf, sh);
 
- unlock:
-	spin_unlock(&sh->lock);
-
+finish:
 	/* wait for this device to become unblocked */
-	if (unlikely(blocked_rdev))
-		md_wait_for_blocked_rdev(blocked_rdev, conf->mddev);
+	if (unlikely(s.blocked_rdev))
+		md_wait_for_blocked_rdev(s.blocked_rdev, conf->mddev);
 
-	if (s.ops_request)
-		raid_run_ops(sh, s.ops_request);
-
-	ops_run_io(sh, &s);
-
-	if (dec_preread_active) {
-		/* We delay this until after ops_run_io so that if make_request
-		 * is waiting on a flush, it won't continue until the writes
-		 * have actually been submitted.
-		 */
-		atomic_dec(&conf->preread_active_stripes);
-		if (atomic_read(&conf->preread_active_stripes) <
-		    IO_THRESHOLD)
-			md_wakeup_thread(conf->mddev->thread);
-	}
-	return_io(return_bi);
-}
-
-static void handle_stripe6(struct stripe_head *sh)
-{
-	raid5_conf_t *conf = sh->raid_conf;
-	int disks = sh->disks;
-	struct bio *return_bi = NULL;
-	int i, pd_idx = sh->pd_idx, qd_idx = sh->qd_idx;
-	struct stripe_head_state s;
-	struct r6_state r6s;
-	struct r5dev *dev, *pdev, *qdev;
-	mdk_rdev_t *blocked_rdev = NULL;
-	int dec_preread_active = 0;
-
-	pr_debug("handling stripe %llu, state=%#lx cnt=%d, "
-		"pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n",
-	       (unsigned long long)sh->sector, sh->state,
-	       atomic_read(&sh->count), pd_idx, qd_idx,
-	       sh->check_state, sh->reconstruct_state);
-	memset(&s, 0, sizeof(s));
-
-	spin_lock(&sh->lock);
-	clear_bit(STRIPE_HANDLE, &sh->state);
-	clear_bit(STRIPE_DELAYED, &sh->state);
-
-	s.syncing = test_bit(STRIPE_SYNCING, &sh->state);
-	s.expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
-	s.expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
-	/* Now to look around and see what can be done */
-
-	rcu_read_lock();
-	for (i=disks; i--; ) {
-		mdk_rdev_t *rdev;
-		dev = &sh->dev[i];
-
-		pr_debug("check %d: state 0x%lx read %p write %p written %p\n",
-			i, dev->flags, dev->toread, dev->towrite, dev->written);
-		/* maybe we can reply to a read
-		 *
-		 * new wantfill requests are only permitted while
-		 * ops_complete_biofill is guaranteed to be inactive
-		 */
-		if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread &&
-		    !test_bit(STRIPE_BIOFILL_RUN, &sh->state))
-			set_bit(R5_Wantfill, &dev->flags);
-
-		/* now count some things */
-		if (test_bit(R5_LOCKED, &dev->flags)) s.locked++;
-		if (test_bit(R5_UPTODATE, &dev->flags)) s.uptodate++;
-		if (test_bit(R5_Wantcompute, &dev->flags)) {
-			s.compute++;
-			BUG_ON(s.compute > 2);
-		}
-
-		if (test_bit(R5_Wantfill, &dev->flags)) {
-			s.to_fill++;
-		} else if (dev->toread)
-			s.to_read++;
-		if (dev->towrite) {
-			s.to_write++;
-			if (!test_bit(R5_OVERWRITE, &dev->flags))
-				s.non_overwrite++;
-		}
-		if (dev->written)
-			s.written++;
-		rdev = rcu_dereference(conf->disks[i].rdev);
-		if (blocked_rdev == NULL &&
-		    rdev && unlikely(test_bit(Blocked, &rdev->flags))) {
-			blocked_rdev = rdev;
-			atomic_inc(&rdev->nr_pending);
-		}
-		clear_bit(R5_Insync, &dev->flags);
-		if (!rdev)
-			/* Not in-sync */;
-		else if (test_bit(In_sync, &rdev->flags))
-			set_bit(R5_Insync, &dev->flags);
-		else {
-			/* in sync if before recovery_offset */
-			if (sh->sector + STRIPE_SECTORS <= rdev->recovery_offset)
-				set_bit(R5_Insync, &dev->flags);
-		}
-		if (!test_bit(R5_Insync, &dev->flags)) {
-			/* The ReadError flag will just be confusing now */
-			clear_bit(R5_ReadError, &dev->flags);
-			clear_bit(R5_ReWrite, &dev->flags);
-		}
-		if (test_bit(R5_ReadError, &dev->flags))
-			clear_bit(R5_Insync, &dev->flags);
-		if (!test_bit(R5_Insync, &dev->flags)) {
-			if (s.failed < 2)
-				r6s.failed_num[s.failed] = i;
-			s.failed++;
-		}
-	}
-	rcu_read_unlock();
-
-	if (unlikely(blocked_rdev)) {
-		if (s.syncing || s.expanding || s.expanded ||
-		    s.to_write || s.written) {
-			set_bit(STRIPE_HANDLE, &sh->state);
-			goto unlock;
-		}
-		/* There is nothing for the blocked_rdev to block */
-		rdev_dec_pending(blocked_rdev, conf->mddev);
-		blocked_rdev = NULL;
-	}
-
-	if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) {
-		set_bit(STRIPE_OP_BIOFILL, &s.ops_request);
-		set_bit(STRIPE_BIOFILL_RUN, &sh->state);
-	}
-
-	pr_debug("locked=%d uptodate=%d to_read=%d"
-	       " to_write=%d failed=%d failed_num=%d,%d\n",
-	       s.locked, s.uptodate, s.to_read, s.to_write, s.failed,
-	       r6s.failed_num[0], r6s.failed_num[1]);
-	/* check if the array has lost >2 devices and, if so, some requests
-	 * might need to be failed
-	 */
-	if (s.failed > 2 && s.to_read+s.to_write+s.written)
-		handle_failed_stripe(conf, sh, &s, disks, &return_bi);
-	if (s.failed > 2 && s.syncing) {
-		md_done_sync(conf->mddev, STRIPE_SECTORS,0);
-		clear_bit(STRIPE_SYNCING, &sh->state);
-		s.syncing = 0;
-	}
-
-	/*
-	 * might be able to return some write requests if the parity blocks
-	 * are safe, or on a failed drive
-	 */
-	pdev = &sh->dev[pd_idx];
-	r6s.p_failed = (s.failed >= 1 && r6s.failed_num[0] == pd_idx)
-		|| (s.failed >= 2 && r6s.failed_num[1] == pd_idx);
-	qdev = &sh->dev[qd_idx];
-	r6s.q_failed = (s.failed >= 1 && r6s.failed_num[0] == qd_idx)
-		|| (s.failed >= 2 && r6s.failed_num[1] == qd_idx);
-
-	if ( s.written &&
-	     ( r6s.p_failed || ((test_bit(R5_Insync, &pdev->flags)
-			     && !test_bit(R5_LOCKED, &pdev->flags)
-			     && test_bit(R5_UPTODATE, &pdev->flags)))) &&
-	     ( r6s.q_failed || ((test_bit(R5_Insync, &qdev->flags)
-			     && !test_bit(R5_LOCKED, &qdev->flags)
-			     && test_bit(R5_UPTODATE, &qdev->flags)))))
-		handle_stripe_clean_event(conf, sh, disks, &return_bi);
-
-	/* Now we might consider reading some blocks, either to check/generate
-	 * parity, or to satisfy requests
-	 * or to load a block that is being partially written.
-	 */
-	if (s.to_read || s.non_overwrite || (s.to_write && s.failed) ||
-	    (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding)
-		handle_stripe_fill6(sh, &s, &r6s, disks);
-
-	/* Now we check to see if any write operations have recently
-	 * completed
-	 */
-	if (sh->reconstruct_state == reconstruct_state_drain_result) {
-
-		sh->reconstruct_state = reconstruct_state_idle;
-		/* All the 'written' buffers and the parity blocks are ready to
-		 * be written back to disk
-		 */
-		BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags));
-		BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[qd_idx].flags));
+	if (s.handle_bad_blocks)
 		for (i = disks; i--; ) {
-			dev = &sh->dev[i];
-			if (test_bit(R5_LOCKED, &dev->flags) &&
-			    (i == sh->pd_idx || i == qd_idx ||
-			     dev->written)) {
-				pr_debug("Writing block %d\n", i);
-				BUG_ON(!test_bit(R5_UPTODATE, &dev->flags));
-				set_bit(R5_Wantwrite, &dev->flags);
-				if (!test_bit(R5_Insync, &dev->flags) ||
-				    ((i == sh->pd_idx || i == qd_idx) &&
-				      s.failed == 0))
-					set_bit(STRIPE_INSYNC, &sh->state);
+			mdk_rdev_t *rdev;
+			struct r5dev *dev = &sh->dev[i];
+			if (test_and_clear_bit(R5_WriteError, &dev->flags)) {
+				/* We own a safe reference to the rdev */
+				rdev = conf->disks[i].rdev;
+				if (!rdev_set_badblocks(rdev, sh->sector,
+							STRIPE_SECTORS, 0))
+					md_error(conf->mddev, rdev);
+				rdev_dec_pending(rdev, conf->mddev);
+			}
+			if (test_and_clear_bit(R5_MadeGood, &dev->flags)) {
+				rdev = conf->disks[i].rdev;
+				rdev_clear_badblocks(rdev, sh->sector,
+						     STRIPE_SECTORS);
+				rdev_dec_pending(rdev, conf->mddev);
 			}
 		}
-		if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
-			dec_preread_active = 1;
-	}
-
-	/* Now to consider new write requests and what else, if anything
-	 * should be read.  We do not handle new writes when:
-	 * 1/ A 'write' operation (copy+gen_syndrome) is already in flight.
-	 * 2/ A 'check' operation is in flight, as it may clobber the parity
-	 *    block.
-	 */
-	if (s.to_write && !sh->reconstruct_state && !sh->check_state)
-		handle_stripe_dirtying6(conf, sh, &s, &r6s, disks);
-
-	/* maybe we need to check and possibly fix the parity for this stripe
-	 * Any reads will already have been scheduled, so we just see if enough
-	 * data is available.  The parity check is held off while parity
-	 * dependent operations are in flight.
-	 */
-	if (sh->check_state ||
-	    (s.syncing && s.locked == 0 &&
-	     !test_bit(STRIPE_COMPUTE_RUN, &sh->state) &&
-	     !test_bit(STRIPE_INSYNC, &sh->state)))
-		handle_parity_checks6(conf, sh, &s, &r6s, disks);
-
-	if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
-		md_done_sync(conf->mddev, STRIPE_SECTORS,1);
-		clear_bit(STRIPE_SYNCING, &sh->state);
-	}
-
-	/* If the failed drives are just a ReadError, then we might need
-	 * to progress the repair/check process
-	 */
-	if (s.failed <= 2 && !conf->mddev->ro)
-		for (i = 0; i < s.failed; i++) {
-			dev = &sh->dev[r6s.failed_num[i]];
-			if (test_bit(R5_ReadError, &dev->flags)
-			    && !test_bit(R5_LOCKED, &dev->flags)
-			    && test_bit(R5_UPTODATE, &dev->flags)
-				) {
-				if (!test_bit(R5_ReWrite, &dev->flags)) {
-					set_bit(R5_Wantwrite, &dev->flags);
-					set_bit(R5_ReWrite, &dev->flags);
-					set_bit(R5_LOCKED, &dev->flags);
-					s.locked++;
-				} else {
-					/* let's read it back */
-					set_bit(R5_Wantread, &dev->flags);
-					set_bit(R5_LOCKED, &dev->flags);
-					s.locked++;
-				}
-			}
-		}
-
-	/* Finish reconstruct operations initiated by the expansion process */
-	if (sh->reconstruct_state == reconstruct_state_result) {
-		sh->reconstruct_state = reconstruct_state_idle;
-		clear_bit(STRIPE_EXPANDING, &sh->state);
-		for (i = conf->raid_disks; i--; ) {
-			set_bit(R5_Wantwrite, &sh->dev[i].flags);
-			set_bit(R5_LOCKED, &sh->dev[i].flags);
-			s.locked++;
-		}
-	}
-
-	if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) &&
-	    !sh->reconstruct_state) {
-		struct stripe_head *sh2
-			= get_active_stripe(conf, sh->sector, 1, 1, 1);
-		if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) {
-			/* sh cannot be written until sh2 has been read.
-			 * so arrange for sh to be delayed a little
-			 */
-			set_bit(STRIPE_DELAYED, &sh->state);
-			set_bit(STRIPE_HANDLE, &sh->state);
-			if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE,
-					      &sh2->state))
-				atomic_inc(&conf->preread_active_stripes);
-			release_stripe(sh2);
-			goto unlock;
-		}
-		if (sh2)
-			release_stripe(sh2);
-
-		/* Need to write out all blocks after computing P&Q */
-		sh->disks = conf->raid_disks;
-		stripe_set_idx(sh->sector, conf, 0, sh);
-		schedule_reconstruction(sh, &s, 1, 1);
-	} else if (s.expanded && !sh->reconstruct_state && s.locked == 0) {
-		clear_bit(STRIPE_EXPAND_READY, &sh->state);
-		atomic_dec(&conf->reshape_stripes);
-		wake_up(&conf->wait_for_overlap);
-		md_done_sync(conf->mddev, STRIPE_SECTORS, 1);
-	}
-
-	if (s.expanding && s.locked == 0 &&
-	    !test_bit(STRIPE_COMPUTE_RUN, &sh->state))
-		handle_stripe_expansion(conf, sh, &r6s);
-
- unlock:
-	spin_unlock(&sh->lock);
-
-	/* wait for this device to become unblocked */
-	if (unlikely(blocked_rdev))
-		md_wait_for_blocked_rdev(blocked_rdev, conf->mddev);
 
 	if (s.ops_request)
 		raid_run_ops(sh, s.ops_request);
 
 	ops_run_io(sh, &s);
 
-
-	if (dec_preread_active) {
+	if (s.dec_preread_active) {
 		/* We delay this until after ops_run_io so that if make_request
 		 * is waiting on a flush, it won't continue until the writes
 		 * have actually been submitted.
@@ -3595,15 +3375,9 @@
 			md_wakeup_thread(conf->mddev->thread);
 	}
 
-	return_io(return_bi);
-}
+	return_io(s.return_bi);
 
-static void handle_stripe(struct stripe_head *sh)
-{
-	if (sh->raid_conf->level == 6)
-		handle_stripe6(sh);
-	else
-		handle_stripe5(sh);
+	clear_bit(STRIPE_ACTIVE, &sh->state);
 }
 
 static void raid5_activate_delayed(raid5_conf_t *conf)
@@ -3833,6 +3607,9 @@
 	rcu_read_lock();
 	rdev = rcu_dereference(conf->disks[dd_idx].rdev);
 	if (rdev && test_bit(In_sync, &rdev->flags)) {
+		sector_t first_bad;
+		int bad_sectors;
+
 		atomic_inc(&rdev->nr_pending);
 		rcu_read_unlock();
 		raid_bio->bi_next = (void*)rdev;
@@ -3840,8 +3617,10 @@
 		align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
 		align_bi->bi_sector += rdev->data_offset;
 
-		if (!bio_fits_rdev(align_bi)) {
-			/* too big in some way */
+		if (!bio_fits_rdev(align_bi) ||
+		    is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9,
+				&first_bad, &bad_sectors)) {
+			/* too big in some way, or has a known bad block */
 			bio_put(align_bi);
 			rdev_dec_pending(rdev, mddev);
 			return 0;
@@ -4016,7 +3795,7 @@
 				}
 			}
 
-			if (bio_data_dir(bi) == WRITE &&
+			if (rw == WRITE &&
 			    logical_sector >= mddev->suspend_lo &&
 			    logical_sector < mddev->suspend_hi) {
 				release_stripe(sh);
@@ -4034,7 +3813,7 @@
 			}
 
 			if (test_bit(STRIPE_EXPANDING, &sh->state) ||
-			    !add_stripe_bio(sh, bi, dd_idx, (bi->bi_rw&RW_MASK))) {
+			    !add_stripe_bio(sh, bi, dd_idx, rw)) {
 				/* Stripe is busy expanding or
 				 * add failed due to overlap.  Flush everything
 				 * and wait a while
@@ -4375,10 +4154,7 @@
 
 	bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
 
-	spin_lock(&sh->lock);
-	set_bit(STRIPE_SYNCING, &sh->state);
-	clear_bit(STRIPE_INSYNC, &sh->state);
-	spin_unlock(&sh->lock);
+	set_bit(STRIPE_SYNC_REQUESTED, &sh->state);
 
 	handle_stripe(sh);
 	release_stripe(sh);
@@ -4509,6 +4285,9 @@
 		release_stripe(sh);
 		cond_resched();
 
+		if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+			md_check_recovery(mddev);
+
 		spin_lock_irq(&conf->device_lock);
 	}
 	pr_debug("%d stripes handled\n", handled);
@@ -5313,6 +5092,7 @@
 		 * isn't possible.
 		 */
 		if (!test_bit(Faulty, &rdev->flags) &&
+		    mddev->recovery_disabled != conf->recovery_disabled &&
 		    !has_failed(conf) &&
 		    number < conf->raid_disks) {
 			err = -EBUSY;
@@ -5341,6 +5121,9 @@
 	int first = 0;
 	int last = conf->raid_disks - 1;
 
+	if (mddev->recovery_disabled == conf->recovery_disabled)
+		return -EBUSY;
+
 	if (has_failed(conf))
 		/* no point adding a device */
 		return -EINVAL;
@@ -5519,16 +5302,14 @@
 			if (rdev->raid_disk < 0 &&
 			    !test_bit(Faulty, &rdev->flags)) {
 				if (raid5_add_disk(mddev, rdev) == 0) {
-					char nm[20];
 					if (rdev->raid_disk
 					    >= conf->previous_raid_disks) {
 						set_bit(In_sync, &rdev->flags);
 						added_devices++;
 					} else
 						rdev->recovery_offset = 0;
-					sprintf(nm, "rd%d", rdev->raid_disk);
-					if (sysfs_create_link(&mddev->kobj,
-							      &rdev->kobj, nm))
+
+					if (sysfs_link_rdev(mddev, rdev))
 						/* Failure here is OK */;
 				}
 			} else if (rdev->raid_disk >= conf->previous_raid_disks
@@ -5624,9 +5405,7 @@
 			     d++) {
 				mdk_rdev_t *rdev = conf->disks[d].rdev;
 				if (rdev && raid5_remove_disk(mddev, d) == 0) {
-					char nm[20];
-					sprintf(nm, "rd%d", rdev->raid_disk);
-					sysfs_remove_link(&mddev->kobj, nm);
+					sysfs_unlink_rdev(mddev, rdev);
 					rdev->raid_disk = -1;
 				}
 			}
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 3ca77a2..11b9566 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -6,11 +6,11 @@
 
 /*
  *
- * Each stripe contains one buffer per disc.  Each buffer can be in
+ * Each stripe contains one buffer per device.  Each buffer can be in
  * one of a number of states stored in "flags".  Changes between
- * these states happen *almost* exclusively under a per-stripe
- * spinlock.  Some very specific changes can happen in bi_end_io, and
- * these are not protected by the spin lock.
+ * these states happen *almost* exclusively under the protection of the
+ * STRIPE_ACTIVE flag.  Some very specific changes can happen in bi_end_io, and
+ * these are not protected by STRIPE_ACTIVE.
  *
  * The flag bits that are used to represent these states are:
  *   R5_UPTODATE and R5_LOCKED
@@ -76,12 +76,10 @@
  * block and the cached buffer are successfully written, any buffer on
  * a written list can be returned with b_end_io.
  *
- * The write list and read list both act as fifos.  The read list is
- * protected by the device_lock.  The write and written lists are
- * protected by the stripe lock.  The device_lock, which can be
- * claimed while the stipe lock is held, is only for list
- * manipulations and will only be held for a very short time.  It can
- * be claimed from interrupts.
+ * The write list and read list both act as fifos.  The read list,
+ * write list and written list are protected by the device_lock.
+ * The device_lock is only for list manipulations and will only be
+ * held for a very short time.  It can be claimed from interrupts.
  *
  *
  * Stripes in the stripe cache can be on one of two lists (or on
@@ -96,7 +94,6 @@
  *
  * The inactive_list, handle_list and hash bucket lists are all protected by the
  * device_lock.
- *  - stripes on the inactive_list never have their stripe_lock held.
  *  - stripes have a reference counter. If count==0, they are on a list.
  *  - If a stripe might need handling, STRIPE_HANDLE is set.
  *  - When refcount reaches zero, then if STRIPE_HANDLE it is put on
@@ -116,10 +113,10 @@
  *  attach a request to an active stripe (add_stripe_bh())
  *     lockdev attach-buffer unlockdev
  *  handle a stripe (handle_stripe())
- *     lockstripe clrSTRIPE_HANDLE ...
+ *     setSTRIPE_ACTIVE,  clrSTRIPE_HANDLE ...
  *		(lockdev check-buffers unlockdev) ..
  *		change-state ..
- *		record io/ops needed unlockstripe schedule io/ops
+ *		record io/ops needed clearSTRIPE_ACTIVE schedule io/ops
  *  release an active stripe (release_stripe())
  *     lockdev if (!--cnt) { if  STRIPE_HANDLE, add to handle_list else add to inactive-list } unlockdev
  *
@@ -128,8 +125,7 @@
  * on a cached buffer, and plus one if the stripe is undergoing stripe
  * operations.
  *
- * Stripe operations are performed outside the stripe lock,
- * the stripe operations are:
+ * The stripe operations are:
  * -copying data between the stripe cache and user application buffers
  * -computing blocks to save a disk access, or to recover a missing block
  * -updating the parity on a write operation (reconstruct write and
@@ -159,7 +155,8 @@
  */
 
 /*
- * Operations state - intermediate states that are visible outside of sh->lock
+ * Operations state - intermediate states that are visible outside of 
+ *   STRIPE_ACTIVE.
  * In general _idle indicates nothing is running, _run indicates a data
  * processing operation is active, and _result means the data processing result
  * is stable and can be acted upon.  For simple operations like biofill and
@@ -209,7 +206,6 @@
 	short			ddf_layout;/* use DDF ordering to calculate Q */
 	unsigned long		state;		/* state flags */
 	atomic_t		count;	      /* nr of active thread/requests */
-	spinlock_t		lock;
 	int			bm_seq;	/* sequence number for bitmap flushes */
 	int			disks;		/* disks in stripe */
 	enum check_states	check_state;
@@ -240,19 +236,20 @@
 };
 
 /* stripe_head_state - collects and tracks the dynamic state of a stripe_head
- *     for handle_stripe.  It is only valid under spin_lock(sh->lock);
+ *     for handle_stripe.
  */
 struct stripe_head_state {
 	int syncing, expanding, expanded;
 	int locked, uptodate, to_read, to_write, failed, written;
 	int to_fill, compute, req_compute, non_overwrite;
-	int failed_num;
+	int failed_num[2];
+	int p_failed, q_failed;
+	int dec_preread_active;
 	unsigned long ops_request;
-};
 
-/* r6_state - extra state data only relevant to r6 */
-struct r6_state {
-	int p_failed, q_failed, failed_num[2];
+	struct bio *return_bi;
+	mdk_rdev_t *blocked_rdev;
+	int handle_bad_blocks;
 };
 
 /* Flags */
@@ -268,14 +265,16 @@
 #define	R5_ReWrite	9	/* have tried to over-write the readerror */
 
 #define	R5_Expanded	10	/* This block now has post-expand data */
-#define	R5_Wantcompute	11 /* compute_block in progress treat as
-				    * uptodate
-				    */
-#define	R5_Wantfill	12 /* dev->toread contains a bio that needs
-				    * filling
-				    */
-#define R5_Wantdrain	13 /* dev->towrite needs to be drained */
-#define R5_WantFUA	14	/* Write should be FUA */
+#define	R5_Wantcompute	11	/* compute_block in progress treat as
+				 * uptodate
+				 */
+#define	R5_Wantfill	12	/* dev->toread contains a bio that needs
+				 * filling
+				 */
+#define	R5_Wantdrain	13	/* dev->towrite needs to be drained */
+#define	R5_WantFUA	14	/* Write should be FUA */
+#define	R5_WriteError	15	/* got a write error - need to record it */
+#define	R5_MadeGood	16	/* A bad block has been fixed by writing to it*/
 /*
  * Write method
  */
@@ -289,21 +288,25 @@
 /*
  * Stripe state
  */
-#define STRIPE_HANDLE		2
-#define	STRIPE_SYNCING		3
-#define	STRIPE_INSYNC		4
-#define	STRIPE_PREREAD_ACTIVE	5
-#define	STRIPE_DELAYED		6
-#define	STRIPE_DEGRADED		7
-#define	STRIPE_BIT_DELAY	8
-#define	STRIPE_EXPANDING	9
-#define	STRIPE_EXPAND_SOURCE	10
-#define	STRIPE_EXPAND_READY	11
-#define	STRIPE_IO_STARTED	12 /* do not count towards 'bypass_count' */
-#define	STRIPE_FULL_WRITE	13 /* all blocks are set to be overwritten */
-#define	STRIPE_BIOFILL_RUN	14
-#define	STRIPE_COMPUTE_RUN	15
-#define	STRIPE_OPS_REQ_PENDING	16
+enum {
+	STRIPE_ACTIVE,
+	STRIPE_HANDLE,
+	STRIPE_SYNC_REQUESTED,
+	STRIPE_SYNCING,
+	STRIPE_INSYNC,
+	STRIPE_PREREAD_ACTIVE,
+	STRIPE_DELAYED,
+	STRIPE_DEGRADED,
+	STRIPE_BIT_DELAY,
+	STRIPE_EXPANDING,
+	STRIPE_EXPAND_SOURCE,
+	STRIPE_EXPAND_READY,
+	STRIPE_IO_STARTED,	/* do not count towards 'bypass_count' */
+	STRIPE_FULL_WRITE,	/* all blocks are set to be overwritten */
+	STRIPE_BIOFILL_RUN,
+	STRIPE_COMPUTE_RUN,
+	STRIPE_OPS_REQ_PENDING,
+};
 
 /*
  * Operation request flags
@@ -336,7 +339,7 @@
  * PREREAD_ACTIVE.
  * In stripe_handle, if we find pre-reading is necessary, we do it if
  * PREREAD_ACTIVE is set, else we set DELAYED which will send it to the delayed queue.
- * HANDLE gets cleared if stripe_handle leave nothing locked.
+ * HANDLE gets cleared if stripe_handle leaves nothing locked.
  */
 
 
@@ -399,7 +402,7 @@
 					    * (fresh device added).
 					    * Cleared when a sync completes.
 					    */
-
+	int			recovery_disabled;
 	/* per cpu variables */
 	struct raid5_percpu {
 		struct page	*spare_page; /* Used when checking P/Q in raid6 */
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 6995940..9575db4 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -68,7 +68,6 @@
 
 config DVB_CORE
 	tristate "DVB for Linux"
-	depends on NET && INET
 	select CRC32
 	help
 	  DVB core utility functions for device handling, software fallbacks etc.
@@ -85,6 +84,19 @@
 
 	  If unsure say N.
 
+config DVB_NET
+	bool "DVB Network Support"
+	default (NET && INET)
+	depends on NET && INET && DVB_CORE
+	help
+	  This option enables DVB Network Support which is a part of the DVB
+	  standard. It is used, for example, by automatic firmware updates used
+	  on Set-Top-Boxes. It can also be used to access the Internet via the
+	  DVB card, if the network provider supports it.
+
+	  You may want to disable the network support on embedded devices. If
+	  unsure say Y.
+
 config VIDEO_MEDIA
 	tristate
 	default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV)
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 22d3ca3..996302a 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -23,6 +23,7 @@
 	depends on VIDEO_MEDIA && I2C
 	select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+	select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE
@@ -152,6 +153,15 @@
 	  This device is only used inside a SiP called together with a
 	  demodulator for now.
 
+config MEDIA_TUNER_XC4000
+	tristate "Xceive XC4000 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMISE
+	help
+	  A driver for the silicon tuner XC4000 from Xceive.
+	  This device is only used inside a SiP called together with a
+	  demodulator for now.
+
 config MEDIA_TUNER_MXL5005S
 	tristate "MaxLinear MSL5005S silicon tuner"
 	depends on VIDEO_MEDIA && I2C
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 2cb4f53..20d24fc 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
 obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
 obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
 obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
 obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index afba6dc..94a603a 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -1805,6 +1805,10 @@
 		.name   = "Xceive 5000 tuner",
 		/* see xc5000.c for details */
 	},
+	[TUNER_XC4000] = { /* Xceive 4000 */
+		.name   = "Xceive 4000 tuner",
+		/* see xc4000.c for details */
+	},
 	[TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */
 		.name   = "TCL tuner MF02GIP-5N-E",
 		.params = tuner_tcl_mf02gip_5n_params,
diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c
new file mode 100644
index 0000000..634f4d9
--- /dev/null
+++ b/drivers/media/common/tuners/xc4000.c
@@ -0,0 +1,1691 @@
+/*
+ *  Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
+ *
+ *  Copyright (c) 2007 Xceive Corporation
+ *  Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *  Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *  Copyright (c) 2009 Davide Ferri <d.ferri@zero11.it>
+ *  Copyright (c) 2010 Istvan Varga <istvan_v@mailbox.hu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/unaligned.h>
+
+#include "dvb_frontend.h"
+
+#include "xc4000.h"
+#include "tuner-i2c.h"
+#include "tuner-xc2028-types.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off)).");
+
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, "
+	"0 (default): use device-specific default mode).");
+
+static int audio_std;
+module_param(audio_std, int, 0644);
+MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly "
+	"needs to know what audio standard is needed for some video standards "
+	"with audio A2 or NICAM. The valid settings are a sum of:\n"
+	" 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n"
+	" 2: use A2 instead of NICAM or BTSC\n"
+	" 4: use SECAM/K3 instead of K1\n"
+	" 8: use PAL-D/K audio for SECAM-D/K\n"
+	"16: use FM radio input 1 instead of input 2\n"
+	"32: use mono audio (the lower three bits are ignored)");
+
+static char firmware_name[30];
+module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
+MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
+	"default firmware name.");
+
+static DEFINE_MUTEX(xc4000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+	printk(KERN_INFO "%s: " fmt, "xc4000", ## arg)
+
+/* struct for storing firmware table */
+struct firmware_description {
+	unsigned int  type;
+	v4l2_std_id   id;
+	__u16         int_freq;
+	unsigned char *ptr;
+	unsigned int  size;
+};
+
+struct firmware_properties {
+	unsigned int	type;
+	v4l2_std_id	id;
+	v4l2_std_id	std_req;
+	__u16		int_freq;
+	unsigned int	scode_table;
+	int		scode_nr;
+};
+
+struct xc4000_priv {
+	struct tuner_i2c_props i2c_props;
+	struct list_head hybrid_tuner_instance_list;
+	struct firmware_description *firm;
+	int	firm_size;
+	u32	if_khz;
+	u32	freq_hz;
+	u32	bandwidth;
+	u8	video_standard;
+	u8	rf_mode;
+	u8	default_pm;
+	u8	dvb_amplitude;
+	u8	set_smoothedcvbs;
+	u8	ignore_i2c_write_errors;
+	__u16	firm_version;
+	struct firmware_properties cur_fw;
+	__u16	hwmodel;
+	__u16	hwvers;
+	struct mutex	lock;
+};
+
+#define XC4000_AUDIO_STD_B		 1
+#define XC4000_AUDIO_STD_A2		 2
+#define XC4000_AUDIO_STD_K3		 4
+#define XC4000_AUDIO_STD_L		 8
+#define XC4000_AUDIO_STD_INPUT1		16
+#define XC4000_AUDIO_STD_MONO		32
+
+#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw"
+
+/* Misc Defines */
+#define MAX_TV_STANDARD			24
+#define XC_MAX_I2C_WRITE_LENGTH		64
+#define XC_POWERED_DOWN			0x80000000U
+
+/* Signal Types */
+#define XC_RF_MODE_AIR			0
+#define XC_RF_MODE_CABLE		1
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED	0x2000
+#define XC_PRODUCT_ID_XC4000		0x0FA0
+#define XC_PRODUCT_ID_XC4100		0x1004
+
+/* Registers (Write-only) */
+#define XREG_INIT         0x00
+#define XREG_VIDEO_MODE   0x01
+#define XREG_AUDIO_MODE   0x02
+#define XREG_RF_FREQ      0x03
+#define XREG_D_CODE       0x04
+#define XREG_DIRECTSITTING_MODE 0x05
+#define XREG_SEEK_MODE    0x06
+#define XREG_POWER_DOWN   0x08
+#define XREG_SIGNALSOURCE 0x0A
+#define XREG_SMOOTHEDCVBS 0x0E
+#define XREG_AMPLITUDE    0x10
+
+/* Registers (Read-only) */
+#define XREG_ADC_ENV      0x00
+#define XREG_QUALITY      0x01
+#define XREG_FRAME_LINES  0x02
+#define XREG_HSYNC_FREQ   0x03
+#define XREG_LOCK         0x04
+#define XREG_FREQ_ERROR   0x05
+#define XREG_SNR          0x06
+#define XREG_VERSION      0x07
+#define XREG_PRODUCT_ID   0x08
+
+/*
+   Basic firmware description. This will remain with
+   the driver for documentation purposes.
+
+   This represents an I2C firmware file encoded as a
+   string of unsigned char. Format is as follows:
+
+   char[0  ]=len0_MSB  -> len = len_MSB * 256 + len_LSB
+   char[1  ]=len0_LSB  -> length of first write transaction
+   char[2  ]=data0 -> first byte to be sent
+   char[3  ]=data1
+   char[4  ]=data2
+   char[   ]=...
+   char[M  ]=dataN  -> last byte to be sent
+   char[M+1]=len1_MSB  -> len = len_MSB * 256 + len_LSB
+   char[M+2]=len1_LSB  -> length of second write transaction
+   char[M+3]=data0
+   char[M+4]=data1
+   ...
+   etc.
+
+   The [len] value should be interpreted as follows:
+
+   len= len_MSB _ len_LSB
+   len=1111_1111_1111_1111   : End of I2C_SEQUENCE
+   len=0000_0000_0000_0000   : Reset command: Do hardware reset
+   len=0NNN_NNNN_NNNN_NNNN   : Normal transaction: number of bytes = {1:32767)
+   len=1WWW_WWWW_WWWW_WWWW   : Wait command: wait for {1:32767} ms
+
+   For the RESET and WAIT commands, the two following bytes will contain
+   immediately the length of the following transaction.
+*/
+
+struct XC_TV_STANDARD {
+	const char  *Name;
+	u16	    audio_mode;
+	u16	    video_mode;
+	u16	    int_freq;
+};
+
+/* Tuner standards */
+#define XC4000_MN_NTSC_PAL_BTSC		0
+#define XC4000_MN_NTSC_PAL_A2		1
+#define XC4000_MN_NTSC_PAL_EIAJ		2
+#define XC4000_MN_NTSC_PAL_Mono		3
+#define XC4000_BG_PAL_A2		4
+#define XC4000_BG_PAL_NICAM		5
+#define XC4000_BG_PAL_MONO		6
+#define XC4000_I_PAL_NICAM		7
+#define XC4000_I_PAL_NICAM_MONO		8
+#define XC4000_DK_PAL_A2		9
+#define XC4000_DK_PAL_NICAM		10
+#define XC4000_DK_PAL_MONO		11
+#define XC4000_DK_SECAM_A2DK1		12
+#define XC4000_DK_SECAM_A2LDK3		13
+#define XC4000_DK_SECAM_A2MONO		14
+#define XC4000_DK_SECAM_NICAM		15
+#define XC4000_L_SECAM_NICAM		16
+#define XC4000_LC_SECAM_NICAM		17
+#define XC4000_DTV6			18
+#define XC4000_DTV8			19
+#define XC4000_DTV7_8			20
+#define XC4000_DTV7			21
+#define XC4000_FM_Radio_INPUT2		22
+#define XC4000_FM_Radio_INPUT1		23
+
+static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = {
+	{"M/N-NTSC/PAL-BTSC",	0x0000, 0x80A0, 4500},
+	{"M/N-NTSC/PAL-A2",	0x0000, 0x80A0, 4600},
+	{"M/N-NTSC/PAL-EIAJ",	0x0040, 0x80A0, 4500},
+	{"M/N-NTSC/PAL-Mono",	0x0078, 0x80A0, 4500},
+	{"B/G-PAL-A2",		0x0000, 0x8159, 5640},
+	{"B/G-PAL-NICAM",	0x0004, 0x8159, 5740},
+	{"B/G-PAL-MONO",	0x0078, 0x8159, 5500},
+	{"I-PAL-NICAM",		0x0080, 0x8049, 6240},
+	{"I-PAL-NICAM-MONO",	0x0078, 0x8049, 6000},
+	{"D/K-PAL-A2",		0x0000, 0x8049, 6380},
+	{"D/K-PAL-NICAM",	0x0080, 0x8049, 6200},
+	{"D/K-PAL-MONO",	0x0078, 0x8049, 6500},
+	{"D/K-SECAM-A2 DK1",	0x0000, 0x8049, 6340},
+	{"D/K-SECAM-A2 L/DK3",	0x0000, 0x8049, 6000},
+	{"D/K-SECAM-A2 MONO",	0x0078, 0x8049, 6500},
+	{"D/K-SECAM-NICAM",	0x0080, 0x8049, 6200},
+	{"L-SECAM-NICAM",	0x8080, 0x0009, 6200},
+	{"L'-SECAM-NICAM",	0x8080, 0x4009, 6200},
+	{"DTV6",		0x00C0, 0x8002,    0},
+	{"DTV8",		0x00C0, 0x800B,    0},
+	{"DTV7/8",		0x00C0, 0x801B,    0},
+	{"DTV7",		0x00C0, 0x8007,    0},
+	{"FM Radio-INPUT2",	0x0008, 0x9800, 10700},
+	{"FM Radio-INPUT1",	0x0008, 0x9000, 10700}
+};
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val);
+static int xc4000_tuner_reset(struct dvb_frontend *fe);
+static void xc_debug_dump(struct xc4000_priv *priv);
+
+static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
+{
+	struct i2c_msg msg = { .addr = priv->i2c_props.addr,
+			       .flags = 0, .buf = buf, .len = len };
+	if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
+		if (priv->ignore_i2c_write_errors == 0) {
+			printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n",
+			       len);
+			if (len == 4) {
+				printk(KERN_ERR "bytes %02x %02x %02x %02x\n", buf[0],
+				       buf[1], buf[2], buf[3]);
+			}
+			return -EREMOTEIO;
+		}
+	}
+	return 0;
+}
+
+static int xc4000_tuner_reset(struct dvb_frontend *fe)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int ret;
+
+	dprintk(1, "%s()\n", __func__);
+
+	if (fe->callback) {
+		ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+					   fe->dvb->priv :
+					   priv->i2c_props.adap->algo_data,
+					   DVB_FRONTEND_COMPONENT_TUNER,
+					   XC4000_TUNER_RESET, 0);
+		if (ret) {
+			printk(KERN_ERR "xc4000: reset failed\n");
+			return -EREMOTEIO;
+		}
+	} else {
+		printk(KERN_ERR "xc4000: no tuner reset callback function, "
+				"fatal\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
+{
+	u8 buf[4];
+	int result;
+
+	buf[0] = (regAddr >> 8) & 0xFF;
+	buf[1] = regAddr & 0xFF;
+	buf[2] = (i2cData >> 8) & 0xFF;
+	buf[3] = i2cData & 0xFF;
+	result = xc_send_i2c_data(priv, buf, 4);
+
+	return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+
+	int i, nbytes_to_send, result;
+	unsigned int len, pos, index;
+	u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+	index = 0;
+	while ((i2c_sequence[index] != 0xFF) ||
+		(i2c_sequence[index + 1] != 0xFF)) {
+		len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
+		if (len == 0x0000) {
+			/* RESET command */
+			/* NOTE: this is ignored, as the reset callback was */
+			/* already called by check_firmware() */
+			index += 2;
+		} else if (len & 0x8000) {
+			/* WAIT command */
+			msleep(len & 0x7FFF);
+			index += 2;
+		} else {
+			/* Send i2c data whilst ensuring individual transactions
+			 * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+			 */
+			index += 2;
+			buf[0] = i2c_sequence[index];
+			buf[1] = i2c_sequence[index + 1];
+			pos = 2;
+			while (pos < len) {
+				if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
+					nbytes_to_send =
+						XC_MAX_I2C_WRITE_LENGTH;
+				else
+					nbytes_to_send = (len - pos + 2);
+				for (i = 2; i < nbytes_to_send; i++) {
+					buf[i] = i2c_sequence[index + pos +
+						i - 2];
+				}
+				result = xc_send_i2c_data(priv, buf,
+					nbytes_to_send);
+
+				if (result != 0)
+					return result;
+
+				pos += nbytes_to_send - 2;
+			}
+			index += len;
+		}
+	}
+	return 0;
+}
+
+static int xc_set_tv_standard(struct xc4000_priv *priv,
+	u16 video_mode, u16 audio_mode)
+{
+	int ret;
+	dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode);
+	dprintk(1, "%s() Standard = %s\n",
+		__func__,
+		xc4000_standard[priv->video_standard].Name);
+
+	/* Don't complain when the request fails because of i2c stretching */
+	priv->ignore_i2c_write_errors = 1;
+
+	ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode);
+	if (ret == 0)
+		ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);
+
+	priv->ignore_i2c_write_errors = 0;
+
+	return ret;
+}
+
+static int xc_set_signal_source(struct xc4000_priv *priv, u16 rf_mode)
+{
+	dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
+		rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+	if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
+		rf_mode = XC_RF_MODE_CABLE;
+		printk(KERN_ERR
+			"%s(), Invalid mode, defaulting to CABLE",
+			__func__);
+	}
+	return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops;
+
+static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz)
+{
+	u16 freq_code;
+
+	dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+	if ((freq_hz > xc4000_tuner_ops.info.frequency_max) ||
+	    (freq_hz < xc4000_tuner_ops.info.frequency_min))
+		return -EINVAL;
+
+	freq_code = (u16)(freq_hz / 15625);
+
+	/* WAS: Starting in firmware version 1.1.44, Xceive recommends using the
+	   FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
+	   only be used for fast scanning for channel lock) */
+	/* WAS: XREG_FINERFREQ */
+	return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+}
+
+static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope)
+{
+	return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz)
+{
+	int result;
+	u16 regData;
+	u32 tmp;
+
+	result = xc4000_readreg(priv, XREG_FREQ_ERROR, &regData);
+	if (result != 0)
+		return result;
+
+	tmp = (u32)regData & 0xFFFFU;
+	tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp);
+	(*freq_error_hz) = tmp * 15625;
+	return result;
+}
+
+static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status)
+{
+	return xc4000_readreg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc4000_priv *priv,
+	u8 *hw_majorversion, u8 *hw_minorversion,
+	u8 *fw_majorversion, u8 *fw_minorversion)
+{
+	u16 data;
+	int result;
+
+	result = xc4000_readreg(priv, XREG_VERSION, &data);
+	if (result != 0)
+		return result;
+
+	(*hw_majorversion) = (data >> 12) & 0x0F;
+	(*hw_minorversion) = (data >>  8) & 0x0F;
+	(*fw_majorversion) = (data >>  4) & 0x0F;
+	(*fw_minorversion) = data & 0x0F;
+
+	return 0;
+}
+
+static int xc_get_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz)
+{
+	u16 regData;
+	int result;
+
+	result = xc4000_readreg(priv, XREG_HSYNC_FREQ, &regData);
+	if (result != 0)
+		return result;
+
+	(*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+	return result;
+}
+
+static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines)
+{
+	return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc4000_priv *priv, u16 *quality)
+{
+	return xc4000_readreg(priv, XREG_QUALITY, quality);
+}
+
+static u16 xc_wait_for_lock(struct xc4000_priv *priv)
+{
+	u16	lock_state = 0;
+	int	watchdog_count = 40;
+
+	while ((lock_state == 0) && (watchdog_count > 0)) {
+		xc_get_lock_status(priv, &lock_state);
+		if (lock_state != 1) {
+			msleep(5);
+			watchdog_count--;
+		}
+	}
+	return lock_state;
+}
+
+static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz)
+{
+	int	found = 1;
+	int	result;
+
+	dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+	/* Don't complain when the request fails because of i2c stretching */
+	priv->ignore_i2c_write_errors = 1;
+	result = xc_set_rf_frequency(priv, freq_hz);
+	priv->ignore_i2c_write_errors = 0;
+
+	if (result != 0)
+		return 0;
+
+	/* wait for lock only in analog TV mode */
+	if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) {
+		if (xc_wait_for_lock(priv) != 1)
+			found = 0;
+	}
+
+	/* Wait for stats to stabilize.
+	 * Frame Lines needs two frame times after initial lock
+	 * before it is valid.
+	 */
+	msleep(debug ? 100 : 10);
+
+	if (debug)
+		xc_debug_dump(priv);
+
+	return found;
+}
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
+{
+	u8 buf[2] = { reg >> 8, reg & 0xff };
+	u8 bval[2] = { 0, 0 };
+	struct i2c_msg msg[2] = {
+		{ .addr = priv->i2c_props.addr,
+			.flags = 0, .buf = &buf[0], .len = 2 },
+		{ .addr = priv->i2c_props.addr,
+			.flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+	};
+
+	if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
+		printk(KERN_ERR "xc4000: I2C read failed\n");
+		return -EREMOTEIO;
+	}
+
+	*val = (bval[0] << 8) | bval[1];
+	return 0;
+}
+
+#define dump_firm_type(t)	dump_firm_type_and_int_freq(t, 0)
+static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+	 if (type & BASE)
+		printk(KERN_CONT "BASE ");
+	 if (type & INIT1)
+		printk(KERN_CONT "INIT1 ");
+	 if (type & F8MHZ)
+		printk(KERN_CONT "F8MHZ ");
+	 if (type & MTS)
+		printk(KERN_CONT "MTS ");
+	 if (type & D2620)
+		printk(KERN_CONT "D2620 ");
+	 if (type & D2633)
+		printk(KERN_CONT "D2633 ");
+	 if (type & DTV6)
+		printk(KERN_CONT "DTV6 ");
+	 if (type & QAM)
+		printk(KERN_CONT "QAM ");
+	 if (type & DTV7)
+		printk(KERN_CONT "DTV7 ");
+	 if (type & DTV78)
+		printk(KERN_CONT "DTV78 ");
+	 if (type & DTV8)
+		printk(KERN_CONT "DTV8 ");
+	 if (type & FM)
+		printk(KERN_CONT "FM ");
+	 if (type & INPUT1)
+		printk(KERN_CONT "INPUT1 ");
+	 if (type & LCD)
+		printk(KERN_CONT "LCD ");
+	 if (type & NOGD)
+		printk(KERN_CONT "NOGD ");
+	 if (type & MONO)
+		printk(KERN_CONT "MONO ");
+	 if (type & ATSC)
+		printk(KERN_CONT "ATSC ");
+	 if (type & IF)
+		printk(KERN_CONT "IF ");
+	 if (type & LG60)
+		printk(KERN_CONT "LG60 ");
+	 if (type & ATI638)
+		printk(KERN_CONT "ATI638 ");
+	 if (type & OREN538)
+		printk(KERN_CONT "OREN538 ");
+	 if (type & OREN36)
+		printk(KERN_CONT "OREN36 ");
+	 if (type & TOYOTA388)
+		printk(KERN_CONT "TOYOTA388 ");
+	 if (type & TOYOTA794)
+		printk(KERN_CONT "TOYOTA794 ");
+	 if (type & DIBCOM52)
+		printk(KERN_CONT "DIBCOM52 ");
+	 if (type & ZARLINK456)
+		printk(KERN_CONT "ZARLINK456 ");
+	 if (type & CHINA)
+		printk(KERN_CONT "CHINA ");
+	 if (type & F6MHZ)
+		printk(KERN_CONT "F6MHZ ");
+	 if (type & INPUT2)
+		printk(KERN_CONT "INPUT2 ");
+	 if (type & SCODE)
+		printk(KERN_CONT "SCODE ");
+	 if (type & HAS_IF)
+		printk(KERN_CONT "HAS_IF_%d ", int_freq);
+}
+
+static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
+			 v4l2_std_id *id)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int		i, best_i = -1;
+	unsigned int	best_nr_diffs = 255U;
+
+	if (!priv->firm) {
+		printk(KERN_ERR "Error! firmware not loaded\n");
+		return -EINVAL;
+	}
+
+	if (((type & ~SCODE) == 0) && (*id == 0))
+		*id = V4L2_STD_PAL;
+
+	/* Seek for generic video standard match */
+	for (i = 0; i < priv->firm_size; i++) {
+		v4l2_std_id	id_diff_mask =
+			(priv->firm[i].id ^ (*id)) & (*id);
+		unsigned int	type_diff_mask =
+			(priv->firm[i].type ^ type)
+			& (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE);
+		unsigned int	nr_diffs;
+
+		if (type_diff_mask
+		    & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE))
+			continue;
+
+		nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask);
+		if (!nr_diffs)	/* Supports all the requested standards */
+			goto found;
+
+		if (nr_diffs < best_nr_diffs) {
+			best_nr_diffs = nr_diffs;
+			best_i = i;
+		}
+	}
+
+	/* FIXME: Would make sense to seek for type "hint" match ? */
+	if (best_i < 0) {
+		i = -ENOENT;
+		goto ret;
+	}
+
+	if (best_nr_diffs > 0U) {
+		printk(KERN_WARNING
+		       "Selecting best matching firmware (%u bits differ) for "
+		       "type=(%x), id %016llx:\n",
+		       best_nr_diffs, type, (unsigned long long)*id);
+		i = best_i;
+	}
+
+found:
+	*id = priv->firm[i].id;
+
+ret:
+	if (debug) {
+		printk(KERN_DEBUG "%s firmware for type=",
+		       (i < 0) ? "Can't find" : "Found");
+		dump_firm_type(type);
+		printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id);
+	}
+	return i;
+}
+
+static int load_firmware(struct dvb_frontend *fe, unsigned int type,
+			 v4l2_std_id *id)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int                pos, rc;
+	unsigned char      *p;
+
+	pos = seek_firmware(fe, type, id);
+	if (pos < 0)
+		return pos;
+
+	p = priv->firm[pos].ptr;
+
+	/* Don't complain when the request fails because of i2c stretching */
+	priv->ignore_i2c_write_errors = 1;
+
+	rc = xc_load_i2c_sequence(fe, p);
+
+	priv->ignore_i2c_write_errors = 0;
+
+	return rc;
+}
+
+static int xc4000_fwupload(struct dvb_frontend *fe)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	const struct firmware *fw   = NULL;
+	const unsigned char   *p, *endp;
+	int                   rc = 0;
+	int		      n, n_array;
+	char		      name[33];
+	const char	      *fname;
+
+	if (firmware_name[0] != '\0')
+		fname = firmware_name;
+	else
+		fname = XC4000_DEFAULT_FIRMWARE;
+
+	dprintk(1, "Reading firmware %s\n", fname);
+	rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
+	if (rc < 0) {
+		if (rc == -ENOENT)
+			printk(KERN_ERR "Error: firmware %s not found.\n", fname);
+		else
+			printk(KERN_ERR "Error %d while requesting firmware %s\n",
+			       rc, fname);
+
+		return rc;
+	}
+	p = fw->data;
+	endp = p + fw->size;
+
+	if (fw->size < sizeof(name) - 1 + 2 + 2) {
+		printk(KERN_ERR "Error: firmware file %s has invalid size!\n",
+		       fname);
+		goto corrupt;
+	}
+
+	memcpy(name, p, sizeof(name) - 1);
+	name[sizeof(name) - 1] = '\0';
+	p += sizeof(name) - 1;
+
+	priv->firm_version = get_unaligned_le16(p);
+	p += 2;
+
+	n_array = get_unaligned_le16(p);
+	p += 2;
+
+	dprintk(1, "Loading %d firmware images from %s, type: %s, ver %d.%d\n",
+		n_array, fname, name,
+		priv->firm_version >> 8, priv->firm_version & 0xff);
+
+	priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
+	if (priv->firm == NULL) {
+		printk(KERN_ERR "Not enough memory to load firmware file.\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	priv->firm_size = n_array;
+
+	n = -1;
+	while (p < endp) {
+		__u32 type, size;
+		v4l2_std_id id;
+		__u16 int_freq = 0;
+
+		n++;
+		if (n >= n_array) {
+			printk(KERN_ERR "More firmware images in file than "
+			       "were expected!\n");
+			goto corrupt;
+		}
+
+		/* Checks if there's enough bytes to read */
+		if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
+			goto header;
+
+		type = get_unaligned_le32(p);
+		p += sizeof(type);
+
+		id = get_unaligned_le64(p);
+		p += sizeof(id);
+
+		if (type & HAS_IF) {
+			int_freq = get_unaligned_le16(p);
+			p += sizeof(int_freq);
+			if (endp - p < sizeof(size))
+				goto header;
+		}
+
+		size = get_unaligned_le32(p);
+		p += sizeof(size);
+
+		if (!size || size > endp - p) {
+			printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n",
+			       type, (unsigned long long)id,
+			       (unsigned)(endp - p), size);
+			goto corrupt;
+		}
+
+		priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
+		if (priv->firm[n].ptr == NULL) {
+			printk(KERN_ERR "Not enough memory to load firmware file.\n");
+			rc = -ENOMEM;
+			goto done;
+		}
+
+		if (debug) {
+			printk(KERN_DEBUG "Reading firmware type ");
+			dump_firm_type_and_int_freq(type, int_freq);
+			printk(KERN_DEBUG "(%x), id %llx, size=%d.\n",
+			       type, (unsigned long long)id, size);
+		}
+
+		memcpy(priv->firm[n].ptr, p, size);
+		priv->firm[n].type = type;
+		priv->firm[n].id   = id;
+		priv->firm[n].size = size;
+		priv->firm[n].int_freq = int_freq;
+
+		p += size;
+	}
+
+	if (n + 1 != priv->firm_size) {
+		printk(KERN_ERR "Firmware file is incomplete!\n");
+		goto corrupt;
+	}
+
+	goto done;
+
+header:
+	printk(KERN_ERR "Firmware header is incomplete!\n");
+corrupt:
+	rc = -EINVAL;
+	printk(KERN_ERR "Error: firmware file is corrupted!\n");
+
+done:
+	release_firmware(fw);
+	if (rc == 0)
+		dprintk(1, "Firmware files loaded.\n");
+
+	return rc;
+}
+
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+			 v4l2_std_id *id, __u16 int_freq, int scode)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int		pos, rc;
+	unsigned char	*p;
+	u8		scode_buf[13];
+	u8		indirect_mode[5];
+
+	dprintk(1, "%s called int_freq=%d\n", __func__, int_freq);
+
+	if (!int_freq) {
+		pos = seek_firmware(fe, type, id);
+		if (pos < 0)
+			return pos;
+	} else {
+		for (pos = 0; pos < priv->firm_size; pos++) {
+			if ((priv->firm[pos].int_freq == int_freq) &&
+			    (priv->firm[pos].type & HAS_IF))
+				break;
+		}
+		if (pos == priv->firm_size)
+			return -ENOENT;
+	}
+
+	p = priv->firm[pos].ptr;
+
+	if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+		return -EINVAL;
+	p += 12 * scode;
+
+	if (debug) {
+		tuner_info("Loading SCODE for type=");
+		dump_firm_type_and_int_freq(priv->firm[pos].type,
+					    priv->firm[pos].int_freq);
+		printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
+		       (unsigned long long)*id);
+	}
+
+	scode_buf[0] = 0x00;
+	memcpy(&scode_buf[1], p, 12);
+
+	/* Enter direct-mode */
+	rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0);
+	if (rc < 0) {
+		printk(KERN_ERR "failed to put device into direct mode!\n");
+		return -EIO;
+	}
+
+	rc = xc_send_i2c_data(priv, scode_buf, 13);
+	if (rc != 0) {
+		/* Even if the send failed, make sure we set back to indirect
+		   mode */
+		printk(KERN_ERR "Failed to set scode %d\n", rc);
+	}
+
+	/* Switch back to indirect-mode */
+	memset(indirect_mode, 0, sizeof(indirect_mode));
+	indirect_mode[4] = 0x88;
+	xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode));
+	msleep(10);
+
+	return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+			  v4l2_std_id std, __u16 int_freq)
+{
+	struct xc4000_priv         *priv = fe->tuner_priv;
+	struct firmware_properties new_fw;
+	int			   rc = 0, is_retry = 0;
+	u16			   hwmodel;
+	v4l2_std_id		   std0;
+	u8			   hw_major, hw_minor, fw_major, fw_minor;
+
+	dprintk(1, "%s called\n", __func__);
+
+	if (!priv->firm) {
+		rc = xc4000_fwupload(fe);
+		if (rc < 0)
+			return rc;
+	}
+
+retry:
+	new_fw.type = type;
+	new_fw.id = std;
+	new_fw.std_req = std;
+	new_fw.scode_table = SCODE;
+	new_fw.scode_nr = 0;
+	new_fw.int_freq = int_freq;
+
+	dprintk(1, "checking firmware, user requested type=");
+	if (debug) {
+		dump_firm_type(new_fw.type);
+		printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
+		       (unsigned long long)new_fw.std_req);
+		if (!int_freq)
+			printk(KERN_CONT "scode_tbl ");
+		else
+			printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
+		printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
+	}
+
+	/* No need to reload base firmware if it matches */
+	if (priv->cur_fw.type & BASE) {
+		dprintk(1, "BASE firmware not changed.\n");
+		goto skip_base;
+	}
+
+	/* Updating BASE - forget about all currently loaded firmware */
+	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+	/* Reset is needed before loading firmware */
+	rc = xc4000_tuner_reset(fe);
+	if (rc < 0)
+		goto fail;
+
+	/* BASE firmwares are all std0 */
+	std0 = 0;
+	rc = load_firmware(fe, BASE, &std0);
+	if (rc < 0) {
+		printk(KERN_ERR "Error %d while loading base firmware\n", rc);
+		goto fail;
+	}
+
+	/* Load INIT1, if needed */
+	dprintk(1, "Load init1 firmware, if exists\n");
+
+	rc = load_firmware(fe, BASE | INIT1, &std0);
+	if (rc == -ENOENT)
+		rc = load_firmware(fe, BASE | INIT1, &std0);
+	if (rc < 0 && rc != -ENOENT) {
+		tuner_err("Error %d while loading init1 firmware\n",
+			  rc);
+		goto fail;
+	}
+
+skip_base:
+	/*
+	 * No need to reload standard specific firmware if base firmware
+	 * was not reloaded and requested video standards have not changed.
+	 */
+	if (priv->cur_fw.type == (BASE | new_fw.type) &&
+	    priv->cur_fw.std_req == std) {
+		dprintk(1, "Std-specific firmware already loaded.\n");
+		goto skip_std_specific;
+	}
+
+	/* Reloading std-specific firmware forces a SCODE update */
+	priv->cur_fw.scode_table = 0;
+
+	/* Load the standard firmware */
+	rc = load_firmware(fe, new_fw.type, &new_fw.id);
+
+	if (rc < 0)
+		goto fail;
+
+skip_std_specific:
+	if (priv->cur_fw.scode_table == new_fw.scode_table &&
+	    priv->cur_fw.scode_nr == new_fw.scode_nr) {
+		dprintk(1, "SCODE firmware already loaded.\n");
+		goto check_device;
+	}
+
+	/* Load SCODE firmware, if exists */
+	rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+			new_fw.int_freq, new_fw.scode_nr);
+	if (rc != 0)
+		dprintk(1, "load scode failed %d\n", rc);
+
+check_device:
+	rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel);
+
+	if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major,
+			   &fw_minor) != 0) {
+		printk(KERN_ERR "Unable to read tuner registers.\n");
+		goto fail;
+	}
+
+	dprintk(1, "Device is Xceive %d version %d.%d, "
+		"firmware version %d.%d\n",
+		hwmodel, hw_major, hw_minor, fw_major, fw_minor);
+
+	/* Check firmware version against what we downloaded. */
+	if (priv->firm_version != ((fw_major << 8) | fw_minor)) {
+		printk(KERN_WARNING
+		       "Incorrect readback of firmware version %d.%d.\n",
+		       fw_major, fw_minor);
+		goto fail;
+	}
+
+	/* Check that the tuner hardware model remains consistent over time. */
+	if (priv->hwmodel == 0 &&
+	    (hwmodel == XC_PRODUCT_ID_XC4000 ||
+	     hwmodel == XC_PRODUCT_ID_XC4100)) {
+		priv->hwmodel = hwmodel;
+		priv->hwvers = (hw_major << 8) | hw_minor;
+	} else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+		   priv->hwvers != ((hw_major << 8) | hw_minor)) {
+		printk(KERN_WARNING
+		       "Read invalid device hardware information - tuner "
+		       "hung?\n");
+		goto fail;
+	}
+
+	memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+	/*
+	 * By setting BASE in cur_fw.type only after successfully loading all
+	 * firmwares, we can:
+	 * 1. Identify that BASE firmware with type=0 has been loaded;
+	 * 2. Tell whether BASE firmware was just changed the next time through.
+	 */
+	priv->cur_fw.type |= BASE;
+
+	return 0;
+
+fail:
+	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+	if (!is_retry) {
+		msleep(50);
+		is_retry = 1;
+		dprintk(1, "Retrying firmware load\n");
+		goto retry;
+	}
+
+	if (rc == -ENOENT)
+		rc = -EINVAL;
+	return rc;
+}
+
+static void xc_debug_dump(struct xc4000_priv *priv)
+{
+	u16	adc_envelope;
+	u32	freq_error_hz = 0;
+	u16	lock_status;
+	u32	hsync_freq_hz = 0;
+	u16	frame_lines;
+	u16	quality;
+	u8	hw_majorversion = 0, hw_minorversion = 0;
+	u8	fw_majorversion = 0, fw_minorversion = 0;
+
+	xc_get_adc_envelope(priv, &adc_envelope);
+	dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+	xc_get_frequency_error(priv, &freq_error_hz);
+	dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+	xc_get_lock_status(priv, &lock_status);
+	dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+		lock_status);
+
+	xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+		       &fw_majorversion, &fw_minorversion);
+	dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
+		hw_majorversion, hw_minorversion,
+		fw_majorversion, fw_minorversion);
+
+	if (priv->video_standard < XC4000_DTV6) {
+		xc_get_hsync_freq(priv, &hsync_freq_hz);
+		dprintk(1, "*** Horizontal sync frequency = %d Hz\n",
+			hsync_freq_hz);
+
+		xc_get_frame_lines(priv, &frame_lines);
+		dprintk(1, "*** Frame lines = %d\n", frame_lines);
+	}
+
+	xc_get_quality(priv, &quality);
+	dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
+}
+
+static int xc4000_set_params(struct dvb_frontend *fe,
+	struct dvb_frontend_parameters *params)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	unsigned int type;
+	int	ret = -EREMOTEIO;
+
+	dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
+
+	mutex_lock(&priv->lock);
+
+	if (fe->ops.info.type == FE_ATSC) {
+		dprintk(1, "%s() ATSC\n", __func__);
+		switch (params->u.vsb.modulation) {
+		case VSB_8:
+		case VSB_16:
+			dprintk(1, "%s() VSB modulation\n", __func__);
+			priv->rf_mode = XC_RF_MODE_AIR;
+			priv->freq_hz = params->frequency - 1750000;
+			priv->bandwidth = BANDWIDTH_6_MHZ;
+			priv->video_standard = XC4000_DTV6;
+			type = DTV6;
+			break;
+		case QAM_64:
+		case QAM_256:
+		case QAM_AUTO:
+			dprintk(1, "%s() QAM modulation\n", __func__);
+			priv->rf_mode = XC_RF_MODE_CABLE;
+			priv->freq_hz = params->frequency - 1750000;
+			priv->bandwidth = BANDWIDTH_6_MHZ;
+			priv->video_standard = XC4000_DTV6;
+			type = DTV6;
+			break;
+		default:
+			ret = -EINVAL;
+			goto fail;
+		}
+	} else if (fe->ops.info.type == FE_OFDM) {
+		dprintk(1, "%s() OFDM\n", __func__);
+		switch (params->u.ofdm.bandwidth) {
+		case BANDWIDTH_6_MHZ:
+			priv->bandwidth = BANDWIDTH_6_MHZ;
+			priv->video_standard = XC4000_DTV6;
+			priv->freq_hz = params->frequency - 1750000;
+			type = DTV6;
+			break;
+		case BANDWIDTH_7_MHZ:
+			priv->bandwidth = BANDWIDTH_7_MHZ;
+			priv->video_standard = XC4000_DTV7;
+			priv->freq_hz = params->frequency - 2250000;
+			type = DTV7;
+			break;
+		case BANDWIDTH_8_MHZ:
+			priv->bandwidth = BANDWIDTH_8_MHZ;
+			priv->video_standard = XC4000_DTV8;
+			priv->freq_hz = params->frequency - 2750000;
+			type = DTV8;
+			break;
+		case BANDWIDTH_AUTO:
+			if (params->frequency < 400000000) {
+				priv->bandwidth = BANDWIDTH_7_MHZ;
+				priv->freq_hz = params->frequency - 2250000;
+			} else {
+				priv->bandwidth = BANDWIDTH_8_MHZ;
+				priv->freq_hz = params->frequency - 2750000;
+			}
+			priv->video_standard = XC4000_DTV7_8;
+			type = DTV78;
+			break;
+		default:
+			printk(KERN_ERR "xc4000 bandwidth not set!\n");
+			ret = -EINVAL;
+			goto fail;
+		}
+		priv->rf_mode = XC_RF_MODE_AIR;
+	} else {
+		printk(KERN_ERR "xc4000 modulation type not supported!\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	dprintk(1, "%s() frequency=%d (compensated)\n",
+		__func__, priv->freq_hz);
+
+	/* Make sure the correct firmware type is loaded */
+	if (check_firmware(fe, type, 0, priv->if_khz) != 0)
+		goto fail;
+
+	ret = xc_set_signal_source(priv, priv->rf_mode);
+	if (ret != 0) {
+		printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n",
+		       priv->rf_mode);
+		goto fail;
+	} else {
+		u16	video_mode, audio_mode;
+		video_mode = xc4000_standard[priv->video_standard].video_mode;
+		audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+		if (type == DTV6 && priv->firm_version != 0x0102)
+			video_mode |= 0x0001;
+		ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+		if (ret != 0) {
+			printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+			/* DJH - do not return when it fails... */
+			/* goto fail; */
+		}
+	}
+
+	if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+		ret = 0;
+	if (priv->dvb_amplitude != 0) {
+		if (xc_write_reg(priv, XREG_AMPLITUDE,
+				 (priv->firm_version != 0x0102 ||
+				  priv->dvb_amplitude != 134 ?
+				  priv->dvb_amplitude : 132)) != 0)
+			ret = -EREMOTEIO;
+	}
+	if (priv->set_smoothedcvbs != 0) {
+		if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+			ret = -EREMOTEIO;
+	}
+	if (ret != 0) {
+		printk(KERN_ERR "xc4000: setting registers failed\n");
+		/* goto fail; */
+	}
+
+	xc_tune_channel(priv, priv->freq_hz);
+
+	ret = 0;
+
+fail:
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int xc4000_set_analog_params(struct dvb_frontend *fe,
+	struct analog_parameters *params)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	unsigned int type = 0;
+	int	ret = -EREMOTEIO;
+
+	if (params->mode == V4L2_TUNER_RADIO) {
+		dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n",
+			__func__, params->frequency);
+
+		mutex_lock(&priv->lock);
+
+		params->std = 0;
+		priv->freq_hz = params->frequency * 125L / 2;
+
+		if (audio_std & XC4000_AUDIO_STD_INPUT1) {
+			priv->video_standard = XC4000_FM_Radio_INPUT1;
+			type = FM | INPUT1;
+		} else {
+			priv->video_standard = XC4000_FM_Radio_INPUT2;
+			type = FM | INPUT2;
+		}
+
+		goto tune_channel;
+	}
+
+	dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+		__func__, params->frequency);
+
+	mutex_lock(&priv->lock);
+
+	/* params->frequency is in units of 62.5khz */
+	priv->freq_hz = params->frequency * 62500;
+
+	params->std &= V4L2_STD_ALL;
+	/* if std is not defined, choose one */
+	if (!params->std)
+		params->std = V4L2_STD_PAL_BG;
+
+	if (audio_std & XC4000_AUDIO_STD_MONO)
+		type = MONO;
+
+	if (params->std & V4L2_STD_MN) {
+		params->std = V4L2_STD_MN;
+		if (audio_std & XC4000_AUDIO_STD_MONO) {
+			priv->video_standard = XC4000_MN_NTSC_PAL_Mono;
+		} else if (audio_std & XC4000_AUDIO_STD_A2) {
+			params->std |= V4L2_STD_A2;
+			priv->video_standard = XC4000_MN_NTSC_PAL_A2;
+		} else {
+			params->std |= V4L2_STD_BTSC;
+			priv->video_standard = XC4000_MN_NTSC_PAL_BTSC;
+		}
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_PAL_BG) {
+		params->std = V4L2_STD_PAL_BG;
+		if (audio_std & XC4000_AUDIO_STD_MONO) {
+			priv->video_standard = XC4000_BG_PAL_MONO;
+		} else if (!(audio_std & XC4000_AUDIO_STD_A2)) {
+			if (!(audio_std & XC4000_AUDIO_STD_B)) {
+				params->std |= V4L2_STD_NICAM_A;
+				priv->video_standard = XC4000_BG_PAL_NICAM;
+			} else {
+				params->std |= V4L2_STD_NICAM_B;
+				priv->video_standard = XC4000_BG_PAL_NICAM;
+			}
+		} else {
+			if (!(audio_std & XC4000_AUDIO_STD_B)) {
+				params->std |= V4L2_STD_A2_A;
+				priv->video_standard = XC4000_BG_PAL_A2;
+			} else {
+				params->std |= V4L2_STD_A2_B;
+				priv->video_standard = XC4000_BG_PAL_A2;
+			}
+		}
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_PAL_I) {
+		/* default to NICAM audio standard */
+		params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM;
+		if (audio_std & XC4000_AUDIO_STD_MONO)
+			priv->video_standard = XC4000_I_PAL_NICAM_MONO;
+		else
+			priv->video_standard = XC4000_I_PAL_NICAM;
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_PAL_DK) {
+		params->std = V4L2_STD_PAL_DK;
+		if (audio_std & XC4000_AUDIO_STD_MONO) {
+			priv->video_standard = XC4000_DK_PAL_MONO;
+		} else if (audio_std & XC4000_AUDIO_STD_A2) {
+			params->std |= V4L2_STD_A2;
+			priv->video_standard = XC4000_DK_PAL_A2;
+		} else {
+			params->std |= V4L2_STD_NICAM;
+			priv->video_standard = XC4000_DK_PAL_NICAM;
+		}
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_SECAM_DK) {
+		/* default to A2 audio standard */
+		params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2;
+		if (audio_std & XC4000_AUDIO_STD_L) {
+			type = 0;
+			priv->video_standard = XC4000_DK_SECAM_NICAM;
+		} else if (audio_std & XC4000_AUDIO_STD_MONO) {
+			priv->video_standard = XC4000_DK_SECAM_A2MONO;
+		} else if (audio_std & XC4000_AUDIO_STD_K3) {
+			params->std |= V4L2_STD_SECAM_K3;
+			priv->video_standard = XC4000_DK_SECAM_A2LDK3;
+		} else {
+			priv->video_standard = XC4000_DK_SECAM_A2DK1;
+		}
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_SECAM_L) {
+		/* default to NICAM audio standard */
+		type = 0;
+		params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM;
+		priv->video_standard = XC4000_L_SECAM_NICAM;
+		goto tune_channel;
+	}
+
+	if (params->std & V4L2_STD_SECAM_LC) {
+		/* default to NICAM audio standard */
+		type = 0;
+		params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM;
+		priv->video_standard = XC4000_LC_SECAM_NICAM;
+		goto tune_channel;
+	}
+
+tune_channel:
+	/* FIXME: it could be air. */
+	priv->rf_mode = XC_RF_MODE_CABLE;
+
+	if (check_firmware(fe, type, params->std,
+			   xc4000_standard[priv->video_standard].int_freq) != 0)
+		goto fail;
+
+	ret = xc_set_signal_source(priv, priv->rf_mode);
+	if (ret != 0) {
+		printk(KERN_ERR
+		       "xc4000: xc_set_signal_source(%d) failed\n",
+		       priv->rf_mode);
+		goto fail;
+	} else {
+		u16	video_mode, audio_mode;
+		video_mode = xc4000_standard[priv->video_standard].video_mode;
+		audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+		if (priv->video_standard < XC4000_BG_PAL_A2) {
+			if (type & NOGD)
+				video_mode &= 0xFF7F;
+		} else if (priv->video_standard < XC4000_I_PAL_NICAM) {
+			if (priv->firm_version == 0x0102)
+				video_mode &= 0xFEFF;
+			if (audio_std & XC4000_AUDIO_STD_B)
+				video_mode |= 0x0080;
+		}
+		ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+		if (ret != 0) {
+			printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+			goto fail;
+		}
+	}
+
+	if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+		ret = 0;
+	if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0)
+		ret = -EREMOTEIO;
+	if (priv->set_smoothedcvbs != 0) {
+		if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+			ret = -EREMOTEIO;
+	}
+	if (ret != 0) {
+		printk(KERN_ERR "xc4000: setting registers failed\n");
+		goto fail;
+	}
+
+	xc_tune_channel(priv, priv->freq_hz);
+
+	ret = 0;
+
+fail:
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+
+	*freq = priv->freq_hz;
+
+	if (debug) {
+		mutex_lock(&priv->lock);
+		if ((priv->cur_fw.type
+		     & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
+			u16	snr = 0;
+			if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
+				mutex_unlock(&priv->lock);
+				dprintk(1, "%s() freq = %u, SNR = %d\n",
+					__func__, *freq, snr);
+				return 0;
+			}
+		}
+		mutex_unlock(&priv->lock);
+	}
+
+	dprintk(1, "%s()\n", __func__);
+
+	return 0;
+}
+
+static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	dprintk(1, "%s()\n", __func__);
+
+	*bw = priv->bandwidth;
+	return 0;
+}
+
+static int xc4000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	u16	lock_status = 0;
+
+	mutex_lock(&priv->lock);
+
+	if (priv->cur_fw.type & BASE)
+		xc_get_lock_status(priv, &lock_status);
+
+	*status = (lock_status == 1 ?
+		   TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0);
+	if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8))
+		*status &= (~TUNER_STATUS_STEREO);
+
+	mutex_unlock(&priv->lock);
+
+	dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
+
+	return 0;
+}
+
+static int xc4000_sleep(struct dvb_frontend *fe)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int	ret = 0;
+
+	dprintk(1, "%s()\n", __func__);
+
+	mutex_lock(&priv->lock);
+
+	/* Avoid firmware reload on slow devices */
+	if ((no_poweroff == 2 ||
+	     (no_poweroff == 0 && priv->default_pm != 0)) &&
+	    (priv->cur_fw.type & BASE) != 0) {
+		/* force reset and firmware reload */
+		priv->cur_fw.type = XC_POWERED_DOWN;
+
+		if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) {
+			printk(KERN_ERR
+			       "xc4000: %s() unable to shutdown tuner\n",
+			       __func__);
+			ret = -EREMOTEIO;
+		}
+		msleep(20);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int xc4000_init(struct dvb_frontend *fe)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	return 0;
+}
+
+static int xc4000_release(struct dvb_frontend *fe)
+{
+	struct xc4000_priv *priv = fe->tuner_priv;
+
+	dprintk(1, "%s()\n", __func__);
+
+	mutex_lock(&xc4000_list_mutex);
+
+	if (priv)
+		hybrid_tuner_release_state(priv);
+
+	mutex_unlock(&xc4000_list_mutex);
+
+	fe->tuner_priv = NULL;
+
+	return 0;
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops = {
+	.info = {
+		.name           = "Xceive XC4000",
+		.frequency_min  =    1000000,
+		.frequency_max  = 1023000000,
+		.frequency_step =      50000,
+	},
+
+	.release	   = xc4000_release,
+	.init		   = xc4000_init,
+	.sleep		   = xc4000_sleep,
+
+	.set_params	   = xc4000_set_params,
+	.set_analog_params = xc4000_set_analog_params,
+	.get_frequency	   = xc4000_get_frequency,
+	.get_bandwidth	   = xc4000_get_bandwidth,
+	.get_status	   = xc4000_get_status
+};
+
+struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   struct xc4000_config *cfg)
+{
+	struct xc4000_priv *priv = NULL;
+	int	instance;
+	u16	id = 0;
+
+	dprintk(1, "%s(%d-%04x)\n", __func__,
+		i2c ? i2c_adapter_id(i2c) : -1,
+		cfg ? cfg->i2c_address : -1);
+
+	mutex_lock(&xc4000_list_mutex);
+
+	instance = hybrid_tuner_request_state(struct xc4000_priv, priv,
+					      hybrid_tuner_instance_list,
+					      i2c, cfg->i2c_address, "xc4000");
+	switch (instance) {
+	case 0:
+		goto fail;
+		break;
+	case 1:
+		/* new tuner instance */
+		priv->bandwidth = BANDWIDTH_6_MHZ;
+		/* set default configuration */
+		priv->if_khz = 4560;
+		priv->default_pm = 0;
+		priv->dvb_amplitude = 134;
+		priv->set_smoothedcvbs = 1;
+		mutex_init(&priv->lock);
+		fe->tuner_priv = priv;
+		break;
+	default:
+		/* existing tuner instance */
+		fe->tuner_priv = priv;
+		break;
+	}
+
+	if (cfg->if_khz != 0) {
+		/* copy configuration if provided by the caller */
+		priv->if_khz = cfg->if_khz;
+		priv->default_pm = cfg->default_pm;
+		priv->dvb_amplitude = cfg->dvb_amplitude;
+		priv->set_smoothedcvbs = cfg->set_smoothedcvbs;
+	}
+
+	/* Check if firmware has been loaded. It is possible that another
+	   instance of the driver has loaded the firmware.
+	 */
+
+	if (instance == 1) {
+		if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
+			goto fail;
+	} else {
+		id = ((priv->cur_fw.type & BASE) != 0 ?
+		      priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED);
+	}
+
+	switch (id) {
+	case XC_PRODUCT_ID_XC4000:
+	case XC_PRODUCT_ID_XC4100:
+		printk(KERN_INFO
+			"xc4000: Successfully identified at address 0x%02x\n",
+			cfg->i2c_address);
+		printk(KERN_INFO
+			"xc4000: Firmware has been loaded previously\n");
+		break;
+	case XC_PRODUCT_ID_FW_NOT_LOADED:
+		printk(KERN_INFO
+			"xc4000: Successfully identified at address 0x%02x\n",
+			cfg->i2c_address);
+		printk(KERN_INFO
+			"xc4000: Firmware has not been loaded previously\n");
+		break;
+	default:
+		printk(KERN_ERR
+			"xc4000: Device not found at addr 0x%02x (0x%x)\n",
+			cfg->i2c_address, id);
+		goto fail;
+	}
+
+	mutex_unlock(&xc4000_list_mutex);
+
+	memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops,
+		sizeof(struct dvb_tuner_ops));
+
+	if (instance == 1) {
+		int	ret;
+		mutex_lock(&priv->lock);
+		ret = xc4000_fwupload(fe);
+		mutex_unlock(&priv->lock);
+		if (ret != 0)
+			goto fail2;
+	}
+
+	return fe;
+fail:
+	mutex_unlock(&xc4000_list_mutex);
+fail2:
+	xc4000_release(fe);
+	return NULL;
+}
+EXPORT_SYMBOL(xc4000_attach);
+
+MODULE_AUTHOR("Steven Toth, Davide Ferri");
+MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/xc4000.h b/drivers/media/common/tuners/xc4000.h
new file mode 100644
index 0000000..e6a44d1
--- /dev/null
+++ b/drivers/media/common/tuners/xc4000.h
@@ -0,0 +1,67 @@
+/*
+ *  Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
+ *
+ *  Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __XC4000_H__
+#define __XC4000_H__
+
+#include <linux/firmware.h>
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct xc4000_config {
+	u8	i2c_address;
+	/* if non-zero, power management is enabled by default */
+	u8	default_pm;
+	/* value to be written to XREG_AMPLITUDE in DVB-T mode (0: no write) */
+	u8	dvb_amplitude;
+	/* if non-zero, register 0x0E is set to filter analog TV video output */
+	u8	set_smoothedcvbs;
+	/* IF for DVB-T */
+	u32	if_khz;
+};
+
+/* xc4000 callback command */
+#define XC4000_TUNER_RESET		0
+
+/* For each bridge framework, when it attaches either analog or digital,
+ * it has to store a reference back to its _core equivalent structure,
+ * so that it can service the hardware by steering gpio's etc.
+ * Each bridge implementation is different so cast devptr accordingly.
+ * The xc4000 driver cares not for this value, other than ensuring
+ * it's passed back to a bridge during tuner_callback().
+ */
+
+#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+					  struct i2c_adapter *i2c,
+					  struct xc4000_config *cfg);
+#else
+static inline struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+						 struct i2c_adapter *i2c,
+						 struct xc4000_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index ee214c3..f6e40b3 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -80,6 +80,10 @@
 	depends on DVB_CORE && PCI && I2C
 	source "drivers/media/dvb/ngene/Kconfig"
 
+comment "Supported ddbridge ('Octopus') Adapters"
+	depends on DVB_CORE && PCI && I2C
+	source "drivers/media/dvb/ddbridge/Kconfig"
+
 comment "Supported DVB Frontends"
 	depends on DVB_CORE
 source "drivers/media/dvb/frontends/Kconfig"
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index a1a0875..b2cefe6 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -15,6 +15,7 @@
 		dm1105/		\
 		pt1/		\
 		mantis/		\
-		ngene/
+		ngene/		\
+		ddbridge/
 
 obj-$(CONFIG_DVB_FIREDTV)	+= firewire/
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 1e1106d..521d691 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -892,7 +892,7 @@
 	if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
 		printk("dvb_bt8xx: no pci device for card %d\n", card->bttv_nr);
 		kfree(card);
-		return -EFAULT;
+		return -ENODEV;
 	}
 
 	if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
@@ -902,7 +902,7 @@
 		       "installed, try removing it.\n");
 
 		kfree(card);
-		return -EFAULT;
+		return -ENODEV;
 	}
 
 	mutex_init(&card->bt->gpio_lock);
diff --git a/drivers/media/dvb/ddbridge/Kconfig b/drivers/media/dvb/ddbridge/Kconfig
new file mode 100644
index 0000000..d099e1a
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DDBRIDGE
+	tristate "Digital Devices bridge support"
+	depends on DVB_CORE && PCI && I2C
+	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+	select DVB_STV6110x if !DVB_FE_CUSTOMISE
+	select DVB_STV090x if !DVB_FE_CUSTOMISE
+	select DVB_DRXK if !DVB_FE_CUSTOMISE
+	select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
+	---help---
+	  Support for cards with the Digital Devices PCI express bridge:
+	  - Octopus PCIe Bridge
+	  - Octopus mini PCIe Bridge
+	  - Octopus LE
+	  - DuoFlex S2 Octopus
+	  - DuoFlex CT Octopus
+	  - cineS2(v6)
+
+	  Say Y if you own such a card and want to use it.
diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile
new file mode 100644
index 0000000..de4fe19
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the ddbridge device driver
+#
+
+ddbridge-objs := ddbridge-core.o
+
+obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
+EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+
+# For the staging CI driver cxd2099
+EXTRA_CFLAGS += -Idrivers/staging/cxd2099/
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
new file mode 100644
index 0000000..573d540
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -0,0 +1,1719 @@
+/*
+ * ddbridge.c: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/swab.h>
+#include <linux/vmalloc.h>
+#include "ddbridge.h"
+
+#include "ddbridge-regs.h"
+
+#include "tda18271c2dd.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "drxk.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* MSI had problems with lost interrupts, fixed but needs testing */
+#undef CONFIG_PCI_MSI
+
+/******************************************************************************/
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+	struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
+				   .buf  = &reg, .len   = 1 },
+				  {.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+			  u16 reg, u8 *val)
+{
+	u8 msg[2] = {reg>>8, reg&0xff};
+	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+				   .buf  = msg, .len   = 2},
+				  {.addr = adr, .flags = I2C_M_RD,
+				   .buf  = val, .len   = 1} };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
+{
+	struct ddb *dev = i2c->dev;
+	int stat;
+	u32 val;
+
+	i2c->done = 0;
+	ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
+	stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
+	if (stat <= 0) {
+		printk(KERN_ERR "I2C timeout\n");
+		{ /* MSI debugging*/
+			u32 istat = ddbreadl(INTERRUPT_STATUS);
+			printk(KERN_ERR "IRS %08x\n", istat);
+			ddbwritel(istat, INTERRUPT_ACK);
+		}
+		return -EIO;
+	}
+	val = ddbreadl(i2c->regs+I2C_COMMAND);
+	if (val & 0x70000)
+		return -EIO;
+	return 0;
+}
+
+static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
+			       struct i2c_msg msg[], int num)
+{
+	struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
+	struct ddb *dev = i2c->dev;
+	u8 addr = 0;
+
+	if (num)
+		addr = msg[0].addr;
+
+	if (num == 2 && msg[1].flags & I2C_M_RD &&
+	    !(msg[0].flags & I2C_M_RD)) {
+		memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
+			    msg[0].buf, msg[0].len);
+		ddbwritel(msg[0].len|(msg[1].len << 16),
+			  i2c->regs+I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 1)) {
+			memcpy_fromio(msg[1].buf,
+				      dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
+				      msg[1].len);
+			return num;
+		}
+	}
+
+	if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+		ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
+		ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 2))
+			return num;
+	}
+	if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+		ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
+		if (!ddb_i2c_cmd(i2c, addr, 3)) {
+			ddbcpyfrom(msg[0].buf,
+				   I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
+			return num;
+		}
+	}
+	return -EIO;
+}
+
+
+static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm ddb_i2c_algo = {
+	.master_xfer   = ddb_i2c_master_xfer,
+	.functionality = ddb_i2c_functionality,
+};
+
+static void ddb_i2c_release(struct ddb *dev)
+{
+	int i;
+	struct ddb_i2c *i2c;
+	struct i2c_adapter *adap;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		i2c = &dev->i2c[i];
+		adap = &i2c->adap;
+		i2c_del_adapter(adap);
+	}
+}
+
+static int ddb_i2c_init(struct ddb *dev)
+{
+	int i, j, stat = 0;
+	struct ddb_i2c *i2c;
+	struct i2c_adapter *adap;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		i2c = &dev->i2c[i];
+		i2c->dev = dev;
+		i2c->nr = i;
+		i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
+		i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
+		i2c->regs = 0x80 + i * 0x20;
+		ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
+		ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
+			  i2c->regs + I2C_TASKADDRESS);
+		init_waitqueue_head(&i2c->wq);
+
+		adap = &i2c->adap;
+		i2c_set_adapdata(adap, i2c);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+		adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
+#else
+#ifdef I2C_CLASS_TV_ANALOG
+		adap->class = I2C_CLASS_TV_ANALOG;
+#endif
+#endif
+		strcpy(adap->name, "ddbridge");
+		adap->algo = &ddb_i2c_algo;
+		adap->algo_data = (void *)i2c;
+		adap->dev.parent = &dev->pdev->dev;
+		stat = i2c_add_adapter(adap);
+		if (stat)
+			break;
+	}
+	if (stat)
+		for (j = 0; j < i; j++) {
+			i2c = &dev->i2c[j];
+			adap = &i2c->adap;
+			i2c_del_adapter(adap);
+		}
+	return stat;
+}
+
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static void set_table(struct ddb *dev, u32 off,
+		      dma_addr_t *pbuf, u32 num)
+{
+	u32 i, base;
+	u64 mem;
+
+	base = DMA_BASE_ADDRESS_TABLE + off;
+	for (i = 0; i < num; i++) {
+		mem = pbuf[i];
+		ddbwritel(mem & 0xffffffff, base + i * 8);
+		ddbwritel(mem >> 32, base + i * 8 + 4);
+	}
+}
+#endif
+
+static void ddb_address_table(struct ddb *dev)
+{
+	u32 i, j, base;
+	u64 mem;
+	dma_addr_t *pbuf;
+
+	for (i = 0; i < dev->info->port_num * 2; i++) {
+		base = DMA_BASE_ADDRESS_TABLE + i * 0x100;
+		pbuf = dev->input[i].pbuf;
+		for (j = 0; j < dev->input[i].dma_buf_num; j++) {
+			mem = pbuf[j];
+			ddbwritel(mem & 0xffffffff, base + j * 8);
+			ddbwritel(mem >> 32, base + j * 8 + 4);
+		}
+	}
+	for (i = 0; i < dev->info->port_num; i++) {
+		base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100;
+		pbuf = dev->output[i].pbuf;
+		for (j = 0; j < dev->output[i].dma_buf_num; j++) {
+			mem = pbuf[j];
+			ddbwritel(mem & 0xffffffff, base + j * 8);
+			ddbwritel(mem >> 32, base + j * 8 + 4);
+		}
+	}
+}
+
+static void io_free(struct pci_dev *pdev, u8 **vbuf,
+		    dma_addr_t *pbuf, u32 size, int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (vbuf[i]) {
+			pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
+			vbuf[i] = 0;
+		}
+	}
+}
+
+static int io_alloc(struct pci_dev *pdev, u8 **vbuf,
+		    dma_addr_t *pbuf, u32 size, int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]);
+		if (!vbuf[i])
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static int ddb_buffers_alloc(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		switch (port->class) {
+		case DDB_PORT_TUNER:
+			if (io_alloc(dev->pdev, port->input[0]->vbuf,
+				     port->input[0]->pbuf,
+				     port->input[0]->dma_buf_size,
+				     port->input[0]->dma_buf_num) < 0)
+				return -1;
+			if (io_alloc(dev->pdev, port->input[1]->vbuf,
+				     port->input[1]->pbuf,
+				     port->input[1]->dma_buf_size,
+				     port->input[1]->dma_buf_num) < 0)
+				return -1;
+			break;
+		case DDB_PORT_CI:
+			if (io_alloc(dev->pdev, port->input[0]->vbuf,
+				     port->input[0]->pbuf,
+				     port->input[0]->dma_buf_size,
+				     port->input[0]->dma_buf_num) < 0)
+				return -1;
+			if (io_alloc(dev->pdev, port->output->vbuf,
+				     port->output->pbuf,
+				     port->output->dma_buf_size,
+				     port->output->dma_buf_num) < 0)
+				return -1;
+			break;
+		default:
+			break;
+		}
+	}
+	ddb_address_table(dev);
+	return 0;
+}
+
+static void ddb_buffers_free(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		io_free(dev->pdev, port->input[0]->vbuf,
+			port->input[0]->pbuf,
+			port->input[0]->dma_buf_size,
+			port->input[0]->dma_buf_num);
+		io_free(dev->pdev, port->input[1]->vbuf,
+			port->input[1]->pbuf,
+			port->input[1]->dma_buf_size,
+			port->input[1]->dma_buf_num);
+		io_free(dev->pdev, port->output->vbuf,
+			port->output->pbuf,
+			port->output->dma_buf_size,
+			port->output->dma_buf_num);
+	}
+}
+
+static void ddb_input_start(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+
+	spin_lock_irq(&input->lock);
+	input->cbuf = 0;
+	input->coff = 0;
+
+	/* reset */
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(2, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+
+	ddbwritel((1 << 16) |
+		  (input->dma_buf_num << 11) |
+		  (input->dma_buf_size >> 7),
+		  DMA_BUFFER_SIZE(input->nr));
+	ddbwritel(0, DMA_BUFFER_ACK(input->nr));
+
+	ddbwritel(1, DMA_BASE_WRITE);
+	ddbwritel(3, DMA_BUFFER_CONTROL(input->nr));
+	ddbwritel(9, TS_INPUT_CONTROL(input->nr));
+	input->running = 1;
+	spin_unlock_irq(&input->lock);
+}
+
+static void ddb_input_stop(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+
+	spin_lock_irq(&input->lock);
+	ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+	ddbwritel(0, DMA_BUFFER_CONTROL(input->nr));
+	input->running = 0;
+	spin_unlock_irq(&input->lock);
+}
+
+static void ddb_output_start(struct ddb_output *output)
+{
+	struct ddb *dev = output->port->dev;
+
+	spin_lock_irq(&output->lock);
+	output->cbuf = 0;
+	output->coff = 0;
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(2, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel((1 << 16) |
+		  (output->dma_buf_num << 11) |
+		  (output->dma_buf_size >> 7),
+		  DMA_BUFFER_SIZE(output->nr + 8));
+	ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8));
+
+	ddbwritel(1, DMA_BASE_READ);
+	ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8));
+	/* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */
+	ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr));
+	output->running = 1;
+	spin_unlock_irq(&output->lock);
+}
+
+static void ddb_output_stop(struct ddb_output *output)
+{
+	struct ddb *dev = output->port->dev;
+
+	spin_lock_irq(&output->lock);
+	ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+	ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8));
+	output->running = 0;
+	spin_unlock_irq(&output->lock);
+}
+
+static u32 ddb_output_free(struct ddb_output *output)
+{
+	u32 idx, off, stat = output->stat;
+	s32 diff;
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	if (output->cbuf != idx) {
+		if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+		    (output->dma_buf_size - output->coff <= 188))
+			return 0;
+		return 188;
+	}
+	diff = off - output->coff;
+	if (diff <= 0 || diff > 188)
+		return 188;
+	return 0;
+}
+
+static ssize_t ddb_output_write(struct ddb_output *output,
+				const u8 *buf, size_t count)
+{
+	struct ddb *dev = output->port->dev;
+	u32 idx, off, stat = output->stat;
+	u32 left = count, len;
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	while (left) {
+		len = output->dma_buf_size - output->coff;
+		if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+		    (off == 0)) {
+			if (len <= 188)
+				break;
+			len -= 188;
+		}
+		if (output->cbuf == idx) {
+			if (off > output->coff) {
+#if 1
+				len = off - output->coff;
+				len -= (len % 188);
+				if (len <= 188)
+
+#endif
+					break;
+				len -= 188;
+			}
+		}
+		if (len > left)
+			len = left;
+		if (copy_from_user(output->vbuf[output->cbuf] + output->coff,
+				   buf, len))
+			return -EIO;
+		left -= len;
+		buf += len;
+		output->coff += len;
+		if (output->coff == output->dma_buf_size) {
+			output->coff = 0;
+			output->cbuf = ((output->cbuf + 1) % output->dma_buf_num);
+		}
+		ddbwritel((output->cbuf << 11) | (output->coff >> 7),
+			  DMA_BUFFER_ACK(output->nr + 8));
+	}
+	return count - left;
+}
+
+static u32 ddb_input_avail(struct ddb_input *input)
+{
+	struct ddb *dev = input->port->dev;
+	u32 idx, off, stat = input->stat;
+	u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr));
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	if (ctrl & 4) {
+		printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
+		ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
+		return 0;
+	}
+	if (input->cbuf != idx)
+		return 188;
+	return 0;
+}
+
+static size_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+{
+	struct ddb *dev = input->port->dev;
+	u32 left = count;
+	u32 idx, off, free, stat = input->stat;
+	int ret;
+
+	idx = (stat >> 11) & 0x1f;
+	off = (stat & 0x7ff) << 7;
+
+	while (left) {
+		if (input->cbuf == idx)
+			return count - left;
+		free = input->dma_buf_size - input->coff;
+		if (free > left)
+			free = left;
+		ret = copy_to_user(buf, input->vbuf[input->cbuf] +
+				   input->coff, free);
+		input->coff += free;
+		if (input->coff == input->dma_buf_size) {
+			input->coff = 0;
+			input->cbuf = (input->cbuf+1) % input->dma_buf_num;
+		}
+		left -= free;
+		ddbwritel((input->cbuf << 11) | (input->coff >> 7),
+			  DMA_BUFFER_ACK(input->nr));
+	}
+	return count;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe)
+{
+	int i;
+
+	for (i = 0; i < dev->info->port_num * 2; i++) {
+		if (dev->input[i].fe == fe)
+			return &dev->input[i];
+	}
+	return NULL;
+}
+#endif
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct ddb_input *input = fe->sec_priv;
+	struct ddb_port *port = input->port;
+	int status;
+
+	if (enable) {
+		mutex_lock(&port->i2c_gate_lock);
+		status = input->gate_ctrl(fe, 1);
+	} else {
+		status = input->gate_ctrl(fe, 0);
+		mutex_unlock(&port->i2c_gate_lock);
+	}
+	return status;
+}
+
+static int demod_attach_drxk(struct ddb_input *input)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct dvb_frontend *fe;
+	struct drxk_config config;
+
+	memset(&config, 0, sizeof(config));
+	config.adr = 0x29 + (input->nr & 1);
+
+	fe = input->fe = dvb_attach(drxk_attach, &config, i2c, &input->fe2);
+	if (!input->fe) {
+		printk(KERN_ERR "No DRXK found!\n");
+		return -ENODEV;
+	}
+	fe->sec_priv = input;
+	input->gate_ctrl = fe->ops.i2c_gate_ctrl;
+	fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+	return 0;
+}
+
+static int tuner_attach_tda18271(struct ddb_input *input)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct dvb_frontend *fe;
+
+	if (input->fe->ops.i2c_gate_ctrl)
+		input->fe->ops.i2c_gate_ctrl(input->fe, 1);
+	fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
+	if (!fe) {
+		printk(KERN_ERR "No TDA18271 found!\n");
+		return -ENODEV;
+	}
+	if (input->fe->ops.i2c_gate_ctrl)
+		input->fe->ops.i2c_gate_ctrl(input->fe, 0);
+	return 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct stv090x_config stv0900 = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x69,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+};
+
+static struct stv090x_config stv0900_aa = {
+	.device         = STV0900,
+	.demod_mode     = STV090x_DUAL,
+	.clk_mode       = STV090x_CLK_EXT,
+
+	.xtal           = 27000000,
+	.address        = 0x68,
+
+	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+	.repeater_level = STV090x_RPTLEVEL_16,
+
+	.adc1_range	= STV090x_ADC_1Vpp,
+	.adc2_range	= STV090x_ADC_1Vpp,
+
+	.diseqc_envelope_mode = true,
+};
+
+static struct stv6110x_config stv6110a = {
+	.addr    = 0x60,
+	.refclk	 = 27000000,
+	.clk_div = 1,
+};
+
+static struct stv6110x_config stv6110b = {
+	.addr    = 0x63,
+	.refclk	 = 27000000,
+	.clk_div = 1,
+};
+
+static int demod_attach_stv0900(struct ddb_input *input, int type)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+
+	input->fe = dvb_attach(stv090x_attach, feconf, i2c,
+			       (input->nr & 1) ? STV090x_DEMODULATOR_1
+			       : STV090x_DEMODULATOR_0);
+	if (!input->fe) {
+		printk(KERN_ERR "No STV0900 found!\n");
+		return -ENODEV;
+	}
+	if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
+			0, (input->nr & 1) ?
+			(0x09 - type) : (0x0b - type))) {
+		printk(KERN_ERR "No LNBH24 found!\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int tuner_attach_stv6110(struct ddb_input *input, int type)
+{
+	struct i2c_adapter *i2c = &input->port->i2c->adap;
+	struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+	struct stv6110x_config *tunerconf = (input->nr & 1) ?
+		&stv6110b : &stv6110a;
+	struct stv6110x_devctl *ctl;
+
+	ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
+	if (!ctl) {
+		printk(KERN_ERR "No STV6110X found!\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "attach tuner input %d adr %02x\n",
+			 input->nr, tunerconf->addr);
+
+	feconf->tuner_init          = ctl->tuner_init;
+	feconf->tuner_sleep         = ctl->tuner_sleep;
+	feconf->tuner_set_mode      = ctl->tuner_set_mode;
+	feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+	feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+	feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+	feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+	feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
+	feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
+	feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
+	feconf->tuner_get_status    = ctl->tuner_get_status;
+
+	return 0;
+}
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+			    int (*start_feed)(struct dvb_demux_feed *),
+			    int (*stop_feed)(struct dvb_demux_feed *),
+			    void *priv)
+{
+	dvbdemux->priv = priv;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = start_feed;
+	dvbdemux->stop_feed = stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+				      DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+	return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+			       struct dvb_demux *dvbdemux,
+			       struct dmx_frontend *hw_frontend,
+			       struct dmx_frontend *mem_frontend,
+			       struct dvb_adapter *dvb_adapter)
+{
+	int ret;
+
+	dmxdev->filternum = 256;
+	dmxdev->demux = &dvbdemux->dmx;
+	dmxdev->capabilities = 0;
+	ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+	if (ret < 0)
+		return ret;
+
+	hw_frontend->source = DMX_FRONTEND_0;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+	mem_frontend->source = DMX_MEMORY_FE;
+	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+	return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ddb_input *input = dvbdmx->priv;
+
+	if (!input->users)
+		ddb_input_start(input);
+
+	return ++input->users;
+}
+
+static int stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ddb_input *input = dvbdmx->priv;
+
+	if (--input->users)
+		return input->users;
+
+	ddb_input_stop(input);
+	return 0;
+}
+
+
+static void dvb_input_detach(struct ddb_input *input)
+{
+	struct dvb_adapter *adap = &input->adap;
+	struct dvb_demux *dvbdemux = &input->demux;
+
+	switch (input->attached) {
+	case 5:
+		if (input->fe2)
+			dvb_unregister_frontend(input->fe2);
+		if (input->fe) {
+			dvb_unregister_frontend(input->fe);
+			dvb_frontend_detach(input->fe);
+			input->fe = NULL;
+		}
+	case 4:
+		dvb_net_release(&input->dvbnet);
+
+	case 3:
+		dvbdemux->dmx.close(&dvbdemux->dmx);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &input->hw_frontend);
+		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+					      &input->mem_frontend);
+		dvb_dmxdev_release(&input->dmxdev);
+
+	case 2:
+		dvb_dmx_release(&input->demux);
+
+	case 1:
+		dvb_unregister_adapter(adap);
+	}
+	input->attached = 0;
+}
+
+static int dvb_input_attach(struct ddb_input *input)
+{
+	int ret;
+	struct ddb_port *port = input->port;
+	struct dvb_adapter *adap = &input->adap;
+	struct dvb_demux *dvbdemux = &input->demux;
+
+	ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
+				   &input->port->dev->pdev->dev,
+				   adapter_nr);
+	if (ret < 0) {
+		printk(KERN_ERR "ddbridge: Could not register adapter."
+		       "Check if you enabled enough adapters in dvb-core!\n");
+		return ret;
+	}
+	input->attached = 1;
+
+	ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+				      start_feed,
+				      stop_feed, input);
+	if (ret < 0)
+		return ret;
+	input->attached = 2;
+
+	ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux,
+					 &input->hw_frontend,
+					 &input->mem_frontend, adap);
+	if (ret < 0)
+		return ret;
+	input->attached = 3;
+
+	ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux);
+	if (ret < 0)
+		return ret;
+	input->attached = 4;
+
+	input->fe = 0;
+	switch (port->type) {
+	case DDB_TUNER_DVBS_ST:
+		if (demod_attach_stv0900(input, 0) < 0)
+			return -ENODEV;
+		if (tuner_attach_stv6110(input, 0) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		break;
+	case DDB_TUNER_DVBS_ST_AA:
+		if (demod_attach_stv0900(input, 1) < 0)
+			return -ENODEV;
+		if (tuner_attach_stv6110(input, 1) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		break;
+	case DDB_TUNER_DVBCT_TR:
+		if (demod_attach_drxk(input) < 0)
+			return -ENODEV;
+		if (tuner_attach_tda18271(input) < 0)
+			return -ENODEV;
+		if (input->fe) {
+			if (dvb_register_frontend(adap, input->fe) < 0)
+				return -ENODEV;
+		}
+		if (input->fe2) {
+			if (dvb_register_frontend(adap, input->fe2) < 0)
+				return -ENODEV;
+			input->fe2->tuner_priv = input->fe->tuner_priv;
+			memcpy(&input->fe2->ops.tuner_ops,
+			       &input->fe->ops.tuner_ops,
+			       sizeof(struct dvb_tuner_ops));
+		}
+		break;
+	}
+	input->attached = 5;
+	return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static ssize_t ts_write(struct file *file, const char *buf,
+			size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	size_t left = count;
+	int stat;
+
+	while (left) {
+		if (ddb_output_free(output) < 188) {
+			if (file->f_flags & O_NONBLOCK)
+				break;
+			if (wait_event_interruptible(
+				    output->wq, ddb_output_free(output) >= 188) < 0)
+				break;
+		}
+		stat = ddb_output_write(output, buf, left);
+		if (stat < 0)
+			break;
+		buf += stat;
+		left -= stat;
+	}
+	return (left == count) ? -EAGAIN : (count - left);
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+		       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	struct ddb_input *input = output->port->input[0];
+	int left, read;
+
+	count -= count % 188;
+	left = count;
+	while (left) {
+		if (ddb_input_avail(input) < 188) {
+			if (file->f_flags & O_NONBLOCK)
+				break;
+			if (wait_event_interruptible(
+				    input->wq, ddb_input_avail(input) >= 188) < 0)
+				break;
+		}
+		read = ddb_input_read(input, buf, left);
+		left -= read;
+		buf += read;
+	}
+	return (left == count) ? -EAGAIN : (count - left);
+}
+
+static unsigned int ts_poll(struct file *file, poll_table *wait)
+{
+	/*
+	struct dvb_device *dvbdev = file->private_data;
+	struct ddb_output *output = dvbdev->priv;
+	struct ddb_input *input = output->port->input[0];
+	*/
+	unsigned int mask = 0;
+
+#if 0
+	if (data_avail_to_read)
+		mask |= POLLIN | POLLRDNORM;
+	if (data_avail_to_write)
+		mask |= POLLOUT | POLLWRNORM;
+
+	poll_wait(file, &read_queue, wait);
+	poll_wait(file, &write_queue, wait);
+#endif
+	return mask;
+}
+
+static const struct file_operations ci_fops = {
+	.owner   = THIS_MODULE,
+	.read    = ts_read,
+	.write   = ts_write,
+	.open    = dvb_generic_open,
+	.release = dvb_generic_release,
+	.poll    = ts_poll,
+	.mmap    = 0,
+};
+
+static struct dvb_device dvbdev_ci = {
+	.priv    = 0,
+	.readers = -1,
+	.writers = -1,
+	.users   = -1,
+	.fops    = &ci_fops,
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void input_tasklet(unsigned long data)
+{
+	struct ddb_input *input = (struct ddb_input *) data;
+	struct ddb *dev = input->port->dev;
+
+	spin_lock(&input->lock);
+	if (!input->running) {
+		spin_unlock(&input->lock);
+		return;
+	}
+	input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+
+	if (input->port->class == DDB_PORT_TUNER) {
+		if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
+			printk(KERN_ERR "Overflow input %d\n", input->nr);
+		while (input->cbuf != ((input->stat >> 11) & 0x1f)
+		       || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
+			dvb_dmx_swfilter_packets(&input->demux,
+						 input->vbuf[input->cbuf],
+						 input->dma_buf_size / 188);
+
+			input->cbuf = (input->cbuf + 1) % input->dma_buf_num;
+			ddbwritel((input->cbuf << 11),
+				  DMA_BUFFER_ACK(input->nr));
+			input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+		       }
+	}
+	if (input->port->class == DDB_PORT_CI)
+		wake_up(&input->wq);
+	spin_unlock(&input->lock);
+}
+
+static void output_tasklet(unsigned long data)
+{
+	struct ddb_output *output = (struct ddb_output *) data;
+	struct ddb *dev = output->port->dev;
+
+	spin_lock(&output->lock);
+	if (!output->running) {
+		spin_unlock(&output->lock);
+		return;
+	}
+	output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8));
+	wake_up(&output->wq);
+	spin_unlock(&output->lock);
+}
+
+
+struct cxd2099_cfg cxd_cfg = {
+	.bitrate =  62000,
+	.adr     =  0x40,
+	.polarity = 1,
+	.clock_mode = 1,
+};
+
+static int ddb_ci_attach(struct ddb_port *port)
+{
+	int ret;
+
+	ret = dvb_register_adapter(&port->output->adap,
+				   "DDBridge",
+				   THIS_MODULE,
+				   &port->dev->pdev->dev,
+				   adapter_nr);
+	if (ret < 0)
+		return ret;
+	port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
+	if (!port->en) {
+		dvb_unregister_adapter(&port->output->adap);
+		return -ENODEV;
+	}
+	ddb_input_start(port->input[0]);
+	ddb_output_start(port->output);
+	dvb_ca_en50221_init(&port->output->adap,
+			    port->en, 0, 1);
+	ret = dvb_register_device(&port->output->adap, &port->output->dev,
+				  &dvbdev_ci, (void *) port->output,
+				  DVB_DEVICE_SEC);
+	return ret;
+}
+
+static int ddb_port_attach(struct ddb_port *port)
+{
+	int ret = 0;
+
+	switch (port->class) {
+	case DDB_PORT_TUNER:
+		ret = dvb_input_attach(port->input[0]);
+		if (ret < 0)
+			break;
+		ret = dvb_input_attach(port->input[1]);
+		break;
+	case DDB_PORT_CI:
+		ret = ddb_ci_attach(port);
+		break;
+	default:
+		break;
+	}
+	if (ret < 0)
+		printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
+	return ret;
+}
+
+static int ddb_ports_attach(struct ddb *dev)
+{
+	int i, ret = 0;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		ret = ddb_port_attach(port);
+		if (ret < 0)
+			break;
+	}
+	return ret;
+}
+
+static void ddb_ports_detach(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		switch (port->class) {
+		case DDB_PORT_TUNER:
+			dvb_input_detach(port->input[0]);
+			dvb_input_detach(port->input[1]);
+			break;
+		case DDB_PORT_CI:
+			if (port->output->dev)
+				dvb_unregister_device(port->output->dev);
+			if (port->en) {
+				ddb_input_stop(port->input[0]);
+				ddb_output_stop(port->output);
+				dvb_ca_en50221_release(port->en);
+				kfree(port->en);
+				port->en = 0;
+				dvb_unregister_adapter(&port->output->adap);
+			}
+			break;
+		}
+	}
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static int port_has_ci(struct ddb_port *port)
+{
+	u8 val;
+	return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1;
+}
+
+static int port_has_stv0900(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_stv0900_aa(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_drxks(struct ddb_port *port)
+{
+	u8 val;
+	if (i2c_read(&port->i2c->adap, 0x29, &val) < 0)
+		return 0;
+	if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static void ddb_port_probe(struct ddb_port *port)
+{
+	struct ddb *dev = port->dev;
+	char *modname = "NO MODULE";
+
+	port->class = DDB_PORT_NONE;
+
+	if (port_has_ci(port)) {
+		modname = "CI";
+		port->class = DDB_PORT_CI;
+		ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_stv0900(port)) {
+		modname = "DUAL DVB-S2";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBS_ST;
+		ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_stv0900_aa(port)) {
+		modname = "DUAL DVB-S2";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBS_ST_AA;
+		ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+	} else if (port_has_drxks(port)) {
+		modname = "DUAL DVB-C/T";
+		port->class = DDB_PORT_TUNER;
+		port->type = DDB_TUNER_DVBCT_TR;
+		ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+	}
+	printk(KERN_INFO "Port %d (TAB %d): %s\n",
+			 port->nr, port->nr+1, modname);
+}
+
+static void ddb_input_init(struct ddb_port *port, int nr)
+{
+	struct ddb *dev = port->dev;
+	struct ddb_input *input = &dev->input[nr];
+
+	input->nr = nr;
+	input->port = port;
+	input->dma_buf_num = INPUT_DMA_BUFS;
+	input->dma_buf_size = INPUT_DMA_SIZE;
+	ddbwritel(0, TS_INPUT_CONTROL(nr));
+	ddbwritel(2, TS_INPUT_CONTROL(nr));
+	ddbwritel(0, TS_INPUT_CONTROL(nr));
+	ddbwritel(0, DMA_BUFFER_ACK(nr));
+	tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input);
+	spin_lock_init(&input->lock);
+	init_waitqueue_head(&input->wq);
+}
+
+static void ddb_output_init(struct ddb_port *port, int nr)
+{
+	struct ddb *dev = port->dev;
+	struct ddb_output *output = &dev->output[nr];
+	output->nr = nr;
+	output->port = port;
+	output->dma_buf_num = OUTPUT_DMA_BUFS;
+	output->dma_buf_size = OUTPUT_DMA_SIZE;
+
+	ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+	ddbwritel(2, TS_OUTPUT_CONTROL(nr));
+	ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+	tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output);
+	init_waitqueue_head(&output->wq);
+}
+
+static void ddb_ports_init(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		port->dev = dev;
+		port->nr = i;
+		port->i2c = &dev->i2c[i];
+		port->input[0] = &dev->input[2 * i];
+		port->input[1] = &dev->input[2 * i + 1];
+		port->output = &dev->output[i];
+
+		mutex_init(&port->i2c_gate_lock);
+		ddb_port_probe(port);
+		ddb_input_init(port, 2 * i);
+		ddb_input_init(port, 2 * i + 1);
+		ddb_output_init(port, i);
+	}
+}
+
+static void ddb_ports_release(struct ddb *dev)
+{
+	int i;
+	struct ddb_port *port;
+
+	for (i = 0; i < dev->info->port_num; i++) {
+		port = &dev->port[i];
+		port->dev = dev;
+		tasklet_kill(&port->input[0]->tasklet);
+		tasklet_kill(&port->input[1]->tasklet);
+		tasklet_kill(&port->output->tasklet);
+	}
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void irq_handle_i2c(struct ddb *dev, int n)
+{
+	struct ddb_i2c *i2c = &dev->i2c[n];
+
+	i2c->done = 1;
+	wake_up(&i2c->wq);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+	struct ddb *dev = (struct ddb *) dev_id;
+	u32 s = ddbreadl(INTERRUPT_STATUS);
+
+	if (!s)
+		return IRQ_NONE;
+
+	do {
+		ddbwritel(s, INTERRUPT_ACK);
+
+		if (s & 0x00000001)
+			irq_handle_i2c(dev, 0);
+		if (s & 0x00000002)
+			irq_handle_i2c(dev, 1);
+		if (s & 0x00000004)
+			irq_handle_i2c(dev, 2);
+		if (s & 0x00000008)
+			irq_handle_i2c(dev, 3);
+
+		if (s & 0x00000100)
+			tasklet_schedule(&dev->input[0].tasklet);
+		if (s & 0x00000200)
+			tasklet_schedule(&dev->input[1].tasklet);
+		if (s & 0x00000400)
+			tasklet_schedule(&dev->input[2].tasklet);
+		if (s & 0x00000800)
+			tasklet_schedule(&dev->input[3].tasklet);
+		if (s & 0x00001000)
+			tasklet_schedule(&dev->input[4].tasklet);
+		if (s & 0x00002000)
+			tasklet_schedule(&dev->input[5].tasklet);
+		if (s & 0x00004000)
+			tasklet_schedule(&dev->input[6].tasklet);
+		if (s & 0x00008000)
+			tasklet_schedule(&dev->input[7].tasklet);
+
+		if (s & 0x00010000)
+			tasklet_schedule(&dev->output[0].tasklet);
+		if (s & 0x00020000)
+			tasklet_schedule(&dev->output[1].tasklet);
+		if (s & 0x00040000)
+			tasklet_schedule(&dev->output[2].tasklet);
+		if (s & 0x00080000)
+			tasklet_schedule(&dev->output[3].tasklet);
+
+		/* if (s & 0x000f0000)	printk(KERN_DEBUG "%08x\n", istat); */
+	} while ((s = ddbreadl(INTERRUPT_STATUS)));
+
+	return IRQ_HANDLED;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+	u32 data, shift;
+
+	if (wlen > 4)
+		ddbwritel(1, SPI_CONTROL);
+	while (wlen > 4) {
+		/* FIXME: check for big-endian */
+		data = swab32(*(u32 *)wbuf);
+		wbuf += 4;
+		wlen -= 4;
+		ddbwritel(data, SPI_DATA);
+		while (ddbreadl(SPI_CONTROL) & 0x0004)
+			;
+	}
+
+	if (rlen)
+		ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+	else
+		ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+
+	data = 0;
+	shift = ((4 - wlen) * 8);
+	while (wlen) {
+		data <<= 8;
+		data |= *wbuf;
+		wlen--;
+		wbuf++;
+	}
+	if (shift)
+		data <<= shift;
+	ddbwritel(data, SPI_DATA);
+	while (ddbreadl(SPI_CONTROL) & 0x0004)
+		;
+
+	if (!rlen) {
+		ddbwritel(0, SPI_CONTROL);
+		return 0;
+	}
+	if (rlen > 4)
+		ddbwritel(1, SPI_CONTROL);
+
+	while (rlen > 4) {
+		ddbwritel(0xffffffff, SPI_DATA);
+		while (ddbreadl(SPI_CONTROL) & 0x0004)
+			;
+		data = ddbreadl(SPI_DATA);
+		*(u32 *) rbuf = swab32(data);
+		rbuf += 4;
+		rlen -= 4;
+	}
+	ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
+	ddbwritel(0xffffffff, SPI_DATA);
+	while (ddbreadl(SPI_CONTROL) & 0x0004)
+		;
+
+	data = ddbreadl(SPI_DATA);
+	ddbwritel(0, SPI_CONTROL);
+
+	if (rlen < 4)
+		data <<= ((4 - rlen) * 8);
+
+	while (rlen > 0) {
+		*rbuf = ((data >> 24) & 0xff);
+		data <<= 8;
+		rbuf++;
+		rlen--;
+	}
+	return 0;
+}
+
+#define DDB_MAGIC 'd'
+
+struct ddb_flashio {
+	__u8 *write_buf;
+	__u32 write_len;
+	__u8 *read_buf;
+	__u32 read_len;
+};
+
+#define IOCTL_DDB_FLASHIO  _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
+
+#define DDB_NAME "ddbridge"
+
+static u32 ddb_num;
+static struct ddb *ddbs[32];
+static struct class *ddb_class;
+static int ddb_major;
+
+static int ddb_open(struct inode *inode, struct file *file)
+{
+	struct ddb *dev = ddbs[iminor(inode)];
+
+	file->private_data = dev;
+	return 0;
+}
+
+static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ddb *dev = file->private_data;
+	void *parg = (void *)arg;
+	int res = -EFAULT;
+
+	switch (cmd) {
+	case IOCTL_DDB_FLASHIO:
+	{
+		struct ddb_flashio fio;
+		u8 *rbuf, *wbuf;
+
+		if (copy_from_user(&fio, parg, sizeof(fio)))
+			break;
+		if (fio.write_len + fio.read_len > 1028) {
+			printk(KERN_ERR "IOBUF too small\n");
+			return -ENOMEM;
+		}
+		wbuf = &dev->iobuf[0];
+		if (!wbuf)
+			return -ENOMEM;
+		rbuf = wbuf + fio.write_len;
+		if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) {
+			vfree(wbuf);
+			break;
+		}
+		res = flashio(dev, wbuf, fio.write_len,
+			      rbuf, fio.read_len);
+		if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
+			res = -EFAULT;
+		break;
+	}
+	default:
+		break;
+	}
+	return res;
+}
+
+static const struct file_operations ddb_fops = {
+	.unlocked_ioctl = ddb_ioctl,
+	.open           = ddb_open,
+};
+
+static char *ddb_devnode(struct device *device, mode_t *mode)
+{
+	struct ddb *dev = dev_get_drvdata(device);
+
+	return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr);
+}
+
+static int ddb_class_create(void)
+{
+	ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
+	if (ddb_major < 0)
+		return ddb_major;
+
+	ddb_class = class_create(THIS_MODULE, DDB_NAME);
+	if (IS_ERR(ddb_class)) {
+		unregister_chrdev(ddb_major, DDB_NAME);
+		return -1;
+	}
+	ddb_class->devnode = ddb_devnode;
+	return 0;
+}
+
+static void ddb_class_destroy(void)
+{
+	class_destroy(ddb_class);
+	unregister_chrdev(ddb_major, DDB_NAME);
+}
+
+static int ddb_device_create(struct ddb *dev)
+{
+	dev->nr = ddb_num++;
+	dev->ddb_dev = device_create(ddb_class, NULL,
+				     MKDEV(ddb_major, dev->nr),
+				     dev, "ddbridge%d", dev->nr);
+	ddbs[dev->nr] = dev;
+	if (IS_ERR(dev->ddb_dev))
+		return -1;
+	return 0;
+}
+
+static void ddb_device_destroy(struct ddb *dev)
+{
+	ddb_num--;
+	if (IS_ERR(dev->ddb_dev))
+		return;
+	device_destroy(ddb_class, MKDEV(ddb_major, 0));
+}
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void ddb_unmap(struct ddb *dev)
+{
+	if (dev->regs)
+		iounmap(dev->regs);
+	vfree(dev);
+}
+
+
+static void __devexit ddb_remove(struct pci_dev *pdev)
+{
+	struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
+
+	ddb_ports_detach(dev);
+	ddb_i2c_release(dev);
+
+	ddbwritel(0, INTERRUPT_ENABLE);
+	free_irq(dev->pdev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+	if (dev->msi)
+		pci_disable_msi(dev->pdev);
+#endif
+	ddb_ports_release(dev);
+	ddb_buffers_free(dev);
+	ddb_device_destroy(dev);
+
+	ddb_unmap(dev);
+	pci_set_drvdata(pdev, 0);
+	pci_disable_device(pdev);
+}
+
+
+static int __devinit ddb_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	struct ddb *dev;
+	int stat = 0;
+	int irq_flag = IRQF_SHARED;
+
+	if (pci_enable_device(pdev) < 0)
+		return -ENODEV;
+
+	dev = vmalloc(sizeof(struct ddb));
+	if (dev == NULL)
+		return -ENOMEM;
+	memset(dev, 0, sizeof(struct ddb));
+
+	dev->pdev = pdev;
+	pci_set_drvdata(pdev, dev);
+	dev->info = (struct ddb_info *) id->driver_data;
+	printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
+
+	dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
+			    pci_resource_len(dev->pdev, 0));
+	if (!dev->regs) {
+		stat = -ENOMEM;
+		goto fail;
+	}
+	printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
+
+#ifdef CONFIG_PCI_MSI
+	if (pci_msi_enabled())
+		stat = pci_enable_msi(dev->pdev);
+	if (stat) {
+		printk(KERN_INFO ": MSI not available.\n");
+	} else {
+		irq_flag = 0;
+		dev->msi = 1;
+	}
+#endif
+	stat = request_irq(dev->pdev->irq, irq_handler,
+			   irq_flag, "DDBridge", (void *) dev);
+	if (stat < 0)
+		goto fail1;
+	ddbwritel(0, DMA_BASE_WRITE);
+	ddbwritel(0, DMA_BASE_READ);
+	ddbwritel(0xffffffff, INTERRUPT_ACK);
+	ddbwritel(0xfff0f, INTERRUPT_ENABLE);
+	ddbwritel(0, MSI1_ENABLE);
+
+	if (ddb_i2c_init(dev) < 0)
+		goto fail1;
+	ddb_ports_init(dev);
+	if (ddb_buffers_alloc(dev) < 0) {
+		printk(KERN_INFO ": Could not allocate buffer memory\n");
+		goto fail2;
+	}
+	if (ddb_ports_attach(dev) < 0)
+		goto fail3;
+	ddb_device_create(dev);
+	return 0;
+
+fail3:
+	ddb_ports_detach(dev);
+	printk(KERN_ERR "fail3\n");
+	ddb_ports_release(dev);
+fail2:
+	printk(KERN_ERR "fail2\n");
+	ddb_buffers_free(dev);
+fail1:
+	printk(KERN_ERR "fail1\n");
+	if (dev->msi)
+		pci_disable_msi(dev->pdev);
+	free_irq(dev->pdev->irq, dev);
+fail:
+	printk(KERN_ERR "fail\n");
+	ddb_unmap(dev);
+	pci_set_drvdata(pdev, 0);
+	pci_disable_device(pdev);
+	return -1;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct ddb_info ddb_none = {
+	.type     = DDB_NONE,
+	.name     = "Digital Devices PCIe bridge",
+};
+
+static struct ddb_info ddb_octopus = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Octopus DVB adapter",
+	.port_num = 4,
+};
+
+static struct ddb_info ddb_octopus_le = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Octopus LE DVB adapter",
+	.port_num = 2,
+};
+
+static struct ddb_info ddb_v6 = {
+	.type     = DDB_OCTOPUS,
+	.name     = "Digital Devices Cine S2 V6 DVB adapter",
+	.port_num = 3,
+};
+
+#define DDVID 0xdd01 /* Digital Devices Vendor ID */
+
+#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) {	\
+	.vendor      = _vend,    .device    = _dev, \
+	.subvendor   = _subvend, .subdevice = _subdev, \
+	.driver_data = (unsigned long)&_driverdata }
+
+static const struct pci_device_id ddb_id_tbl[] __devinitdata = {
+	DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus),
+	DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
+	/* in case sub-ids got deleted in flash */
+	DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+	{0}
+};
+MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
+
+
+static struct pci_driver ddb_pci_driver = {
+	.name        = "DDBridge",
+	.id_table    = ddb_id_tbl,
+	.probe       = ddb_probe,
+	.remove      = ddb_remove,
+};
+
+static __init int module_init_ddbridge(void)
+{
+	printk(KERN_INFO "Digital Devices PCIE bridge driver, "
+	       "Copyright (C) 2010-11 Digital Devices GmbH\n");
+	if (ddb_class_create())
+		return -1;
+	return pci_register_driver(&ddb_pci_driver);
+}
+
+static __exit void module_exit_ddbridge(void)
+{
+	pci_unregister_driver(&ddb_pci_driver);
+	ddb_class_destroy();
+}
+
+module_init(module_init_ddbridge);
+module_exit(module_exit_ddbridge);
+
+MODULE_DESCRIPTION("Digital Devices PCIe Bridge");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/ddbridge/ddbridge-regs.h b/drivers/media/dvb/ddbridge/ddbridge-regs.h
new file mode 100644
index 0000000..a3ccb31
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge-regs.h
@@ -0,0 +1,151 @@
+/*
+ * ddbridge-regs.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */
+
+/* Register Definitions */
+
+#define CUR_REGISTERMAP_VERSION 0x10000
+
+#define HARDWARE_VERSION       0x00
+#define REGISTERMAP_VERSION    0x04
+
+/* ------------------------------------------------------------------------- */
+/* SPI Controller */
+
+#define SPI_CONTROL     0x10
+#define SPI_DATA        0x14
+
+/* ------------------------------------------------------------------------- */
+
+/* Interrupt controller                                     */
+/* How many MSI's are available depends on HW (Min 2 max 8) */
+/* How many are usable also depends on Host platform        */
+
+#define INTERRUPT_BASE   (0x40)
+
+#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00)
+#define MSI0_ENABLE      (INTERRUPT_BASE + 0x00)
+#define MSI1_ENABLE      (INTERRUPT_BASE + 0x04)
+#define MSI2_ENABLE      (INTERRUPT_BASE + 0x08)
+#define MSI3_ENABLE      (INTERRUPT_BASE + 0x0C)
+#define MSI4_ENABLE      (INTERRUPT_BASE + 0x10)
+#define MSI5_ENABLE      (INTERRUPT_BASE + 0x14)
+#define MSI6_ENABLE      (INTERRUPT_BASE + 0x18)
+#define MSI7_ENABLE      (INTERRUPT_BASE + 0x1C)
+
+#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20)
+#define INTERRUPT_ACK    (INTERRUPT_BASE + 0x20)
+
+#define INTMASK_I2C1        (0x00000001)
+#define INTMASK_I2C2        (0x00000002)
+#define INTMASK_I2C3        (0x00000004)
+#define INTMASK_I2C4        (0x00000008)
+
+#define INTMASK_CIRQ1       (0x00000010)
+#define INTMASK_CIRQ2       (0x00000020)
+#define INTMASK_CIRQ3       (0x00000040)
+#define INTMASK_CIRQ4       (0x00000080)
+
+#define INTMASK_TSINPUT1    (0x00000100)
+#define INTMASK_TSINPUT2    (0x00000200)
+#define INTMASK_TSINPUT3    (0x00000400)
+#define INTMASK_TSINPUT4    (0x00000800)
+#define INTMASK_TSINPUT5    (0x00001000)
+#define INTMASK_TSINPUT6    (0x00002000)
+#define INTMASK_TSINPUT7    (0x00004000)
+#define INTMASK_TSINPUT8    (0x00008000)
+
+#define INTMASK_TSOUTPUT1   (0x00010000)
+#define INTMASK_TSOUTPUT2   (0x00020000)
+#define INTMASK_TSOUTPUT3   (0x00040000)
+#define INTMASK_TSOUTPUT4   (0x00080000)
+
+/* ------------------------------------------------------------------------- */
+/* I2C Master Controller */
+
+#define I2C_BASE        (0x80)  /* Byte offset */
+
+#define I2C_COMMAND     (0x00)
+#define I2C_TIMING      (0x04)
+#define I2C_TASKLENGTH  (0x08)     /* High read, low write */
+#define I2C_TASKADDRESS (0x0C)     /* High read, low write */
+
+#define I2C_MONITOR     (0x1C)
+
+#define I2C_BASE_1      (I2C_BASE + 0x00)
+#define I2C_BASE_2      (I2C_BASE + 0x20)
+#define I2C_BASE_3      (I2C_BASE + 0x40)
+#define I2C_BASE_4      (I2C_BASE + 0x60)
+
+#define I2C_BASE_N(i)   (I2C_BASE + (i) * 0x20)
+
+#define I2C_TASKMEM_BASE    (0x1000)    /* Byte offset */
+#define I2C_TASKMEM_SIZE    (0x1000)
+
+#define I2C_SPEED_400   (0x04030404)
+#define I2C_SPEED_200   (0x09080909)
+#define I2C_SPEED_154   (0x0C0B0C0C)
+#define I2C_SPEED_100   (0x13121313)
+#define I2C_SPEED_77    (0x19181919)
+#define I2C_SPEED_50    (0x27262727)
+
+
+/* ------------------------------------------------------------------------- */
+/* DMA  Controller */
+
+#define DMA_BASE_WRITE        (0x100)
+#define DMA_BASE_READ         (0x140)
+
+#define DMA_CONTROL     (0x00)                  /* 64 */
+#define DMA_ERROR       (0x04)                  /* 65 ( only read instance ) */
+
+#define DMA_DIAG_CONTROL                (0x1C)  /* 71 */
+#define DMA_DIAG_PACKETCOUNTER_LOW      (0x20)  /* 72 */
+#define DMA_DIAG_PACKETCOUNTER_HIGH     (0x24)  /* 73 */
+#define DMA_DIAG_TIMECOUNTER_LOW        (0x28)  /* 74 */
+#define DMA_DIAG_TIMECOUNTER_HIGH       (0x2C)  /* 75 */
+#define DMA_DIAG_RECHECKCOUNTER         (0x30)  /* 76  ( Split completions on read ) */
+#define DMA_DIAG_WAITTIMEOUTINIT        (0x34)  /* 77 */
+#define DMA_DIAG_WAITOVERFLOWCOUNTER    (0x38)  /* 78 */
+#define DMA_DIAG_WAITCOUNTER            (0x3C)  /* 79 */
+
+/* ------------------------------------------------------------------------- */
+/* DMA  Buffer */
+
+#define TS_INPUT_BASE       (0x200)
+#define TS_INPUT_CONTROL(i)         (TS_INPUT_BASE + (i) * 16 + 0x00)
+
+#define TS_OUTPUT_BASE       (0x280)
+#define TS_OUTPUT_CONTROL(i)         (TS_OUTPUT_BASE + (i) * 16 + 0x00)
+
+#define DMA_BUFFER_BASE     (0x300)
+
+#define DMA_BUFFER_CONTROL(i)       (DMA_BUFFER_BASE + (i) * 16 + 0x00)
+#define DMA_BUFFER_ACK(i)           (DMA_BUFFER_BASE + (i) * 16 + 0x04)
+#define DMA_BUFFER_CURRENT(i)       (DMA_BUFFER_BASE + (i) * 16 + 0x08)
+#define DMA_BUFFER_SIZE(i)          (DMA_BUFFER_BASE + (i) * 16 + 0x0c)
+
+#define DMA_BASE_ADDRESS_TABLE  (0x2000)
+#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512)
+
diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h
new file mode 100644
index 0000000..6d14893
--- /dev/null
+++ b/drivers/media/dvb/ddbridge/ddbridge.h
@@ -0,0 +1,187 @@
+/*
+ * ddbridge.h: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _DDBRIDGE_H_
+#define _DDBRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/dma.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <linux/socket.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_ca_en50221.h"
+#include "dvb_net.h"
+#include "cxd2099.h"
+
+#define DDB_MAX_I2C     4
+#define DDB_MAX_PORT    4
+#define DDB_MAX_INPUT   8
+#define DDB_MAX_OUTPUT  4
+
+struct ddb_info {
+	int   type;
+#define DDB_NONE         0
+#define DDB_OCTOPUS      1
+	char *name;
+	int   port_num;
+	u32   port_type[DDB_MAX_PORT];
+};
+
+/* DMA_SIZE MUST be divisible by 188 and 128 !!! */
+
+#define INPUT_DMA_MAX_BUFS 32      /* hardware table limit */
+#define INPUT_DMA_BUFS 8
+#define INPUT_DMA_SIZE (128*47*21)
+
+#define OUTPUT_DMA_MAX_BUFS 32
+#define OUTPUT_DMA_BUFS 8
+#define OUTPUT_DMA_SIZE (128*47*21)
+
+struct ddb;
+struct ddb_port;
+
+struct ddb_input {
+	struct ddb_port       *port;
+	u32                    nr;
+	int                    attached;
+
+	dma_addr_t             pbuf[INPUT_DMA_MAX_BUFS];
+	u8                    *vbuf[INPUT_DMA_MAX_BUFS];
+	u32                    dma_buf_num;
+	u32                    dma_buf_size;
+
+	struct tasklet_struct  tasklet;
+	spinlock_t             lock;
+	wait_queue_head_t      wq;
+	int                    running;
+	u32                    stat;
+	u32                    cbuf;
+	u32                    coff;
+
+	struct dvb_adapter     adap;
+	struct dvb_device     *dev;
+	struct dvb_frontend   *fe;
+	struct dvb_frontend   *fe2;
+	struct dmxdev          dmxdev;
+	struct dvb_demux       demux;
+	struct dvb_net         dvbnet;
+	struct dmx_frontend    hw_frontend;
+	struct dmx_frontend    mem_frontend;
+	int                    users;
+	int (*gate_ctrl)(struct dvb_frontend *, int);
+};
+
+struct ddb_output {
+	struct ddb_port       *port;
+	u32                    nr;
+	dma_addr_t             pbuf[OUTPUT_DMA_MAX_BUFS];
+	u8                    *vbuf[OUTPUT_DMA_MAX_BUFS];
+	u32                    dma_buf_num;
+	u32                    dma_buf_size;
+	struct tasklet_struct  tasklet;
+	spinlock_t             lock;
+	wait_queue_head_t      wq;
+	int                    running;
+	u32                    stat;
+	u32                    cbuf;
+	u32                    coff;
+
+	struct dvb_adapter     adap;
+	struct dvb_device     *dev;
+};
+
+struct ddb_i2c {
+	struct ddb            *dev;
+	u32                    nr;
+	struct i2c_adapter     adap;
+	struct i2c_adapter     adap2;
+	u32                    regs;
+	u32                    rbuf;
+	u32                    wbuf;
+	int                    done;
+	wait_queue_head_t      wq;
+};
+
+struct ddb_port {
+	struct ddb            *dev;
+	u32                    nr;
+	struct ddb_i2c        *i2c;
+	struct mutex           i2c_gate_lock;
+	u32                    class;
+#define DDB_PORT_NONE           0
+#define DDB_PORT_CI             1
+#define DDB_PORT_TUNER          2
+	u32                    type;
+#define DDB_TUNER_NONE          0
+#define DDB_TUNER_DVBS_ST       1
+#define DDB_TUNER_DVBS_ST_AA    2
+#define DDB_TUNER_DVBCT_TR     16
+#define DDB_TUNER_DVBCT_ST     17
+	u32                    adr;
+
+	struct ddb_input      *input[2];
+	struct ddb_output     *output;
+	struct dvb_ca_en50221 *en;
+};
+
+struct ddb {
+	struct pci_dev        *pdev;
+	unsigned char         *regs;
+	struct ddb_port        port[DDB_MAX_PORT];
+	struct ddb_i2c         i2c[DDB_MAX_I2C];
+	struct ddb_input       input[DDB_MAX_INPUT];
+	struct ddb_output      output[DDB_MAX_OUTPUT];
+
+	struct device         *ddb_dev;
+	int                    nr;
+	u8                     iobuf[1028];
+
+	struct ddb_info       *info;
+	int                    msi;
+};
+
+/****************************************************************************/
+
+#define ddbwritel(_val, _adr)        writel((_val), \
+				     (char *) (dev->regs+(_adr)))
+#define ddbreadl(_adr)               readl((char *) (dev->regs+(_adr)))
+#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *)	\
+				     (dev->regs+(_adr)), (_src), (_count))
+#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \
+				       (dev->regs+(_adr)), (_count))
+
+/****************************************************************************/
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
index 0b51828..8f22bcd 100644
--- a/drivers/media/dvb/dvb-core/Makefile
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -2,8 +2,10 @@
 # Makefile for the kernel DVB device drivers.
 #
 
+dvb-net-$(CONFIG_DVB_NET) := dvb_net.o
+
 dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o 	\
 		 dvb_ca_en50221.o dvb_frontend.o 		\
-		 dvb_net.o dvb_ringbuffer.o dvb_math.o
+		 $(dvb-net-y) dvb_ringbuffer.o dvb_math.o
 
 obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 5b6b451..efe9c30 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -904,7 +904,7 @@
 	.buffer = b \
 }
 
-static struct dtv_cmds_h dtv_cmds[] = {
+static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
 	_DTV_CMD(DTV_TUNE, 1, 0),
 	_DTV_CMD(DTV_CLEAR, 1, 0),
 
@@ -966,6 +966,7 @@
 	_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0),
 
 	_DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
+	_DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0),
 
 	/* Get */
 	_DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
index 3a3126ca..1e53acd 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.h
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -32,6 +32,8 @@
 
 #define DVB_NET_DEVICES_MAX 10
 
+#ifdef CONFIG_DVB_NET
+
 struct dvb_net {
 	struct dvb_device *dvbdev;
 	struct net_device *device[DVB_NET_DEVICES_MAX];
@@ -40,8 +42,25 @@
 	struct dmx_demux *demux;
 };
 
-
 void dvb_net_release(struct dvb_net *);
 int  dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
 
+#else
+
+struct dvb_net {
+	struct dvb_device *dvbdev;
+};
+
+static inline void dvb_net_release(struct dvb_net *dvbnet)
+{
+}
+
+static inline int dvb_net_init(struct dvb_adapter *adap,
+			       struct dvb_net *dvbnet, struct dmx_demux *dmx)
+{
+	return 0;
+}
+
+#endif /* ifdef CONFIG_DVB_NET */
+
 #endif
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index e85304c..5d73dec 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -81,6 +81,7 @@
 	select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+	select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
 	help
 	  Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 100ebc3..d7ad05f 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -91,7 +91,6 @@
 	case GET_CONFIG:
 	case READ_MEMORY:
 	case RECONNECT_USB:
-	case GET_IR_CODE:
 		write = 0;
 		break;
 	case READ_I2C:
@@ -164,13 +163,6 @@
 	deb_xfer("<<< ");
 	debug_dump(buf, act_len, deb_xfer);
 
-	/* remote controller query status is 1 if remote code is not received */
-	if (req->cmd == GET_IR_CODE && buf[1] == 1) {
-		buf[1] = 0; /* clear command "error" status */
-		memset(&buf[2], 0, req->data_len);
-		buf[3] = 1; /* no remote code received mark */
-	}
-
 	/* check status */
 	if (buf[1]) {
 		err("command failed:%d", buf[1]);
@@ -292,6 +284,10 @@
 		}
 
 		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+			if (msg[i].len > 3 || msg[i+1].len > 61) {
+				ret = -EOPNOTSUPP;
+				goto error;
+			}
 			if (msg[i].addr ==
 				af9015_af9013_config[0].demod_address)
 				req.cmd = READ_MEMORY;
@@ -306,12 +302,16 @@
 			ret = af9015_ctrl_msg(d, &req);
 			i += 2;
 		} else if (msg[i].flags & I2C_M_RD) {
-			ret = -EINVAL;
-			if (msg[i].addr ==
-				af9015_af9013_config[0].demod_address)
+			if (msg[i].len > 61) {
+				ret = -EOPNOTSUPP;
 				goto error;
-			else
-				req.cmd = READ_I2C;
+			}
+			if (msg[i].addr ==
+				af9015_af9013_config[0].demod_address) {
+				ret = -EINVAL;
+				goto error;
+			}
+			req.cmd = READ_I2C;
 			req.i2c_addr = msg[i].addr;
 			req.addr = addr;
 			req.mbox = mbox;
@@ -321,6 +321,10 @@
 			ret = af9015_ctrl_msg(d, &req);
 			i += 1;
 		} else {
+			if (msg[i].len > 21) {
+				ret = -EOPNOTSUPP;
+				goto error;
+			}
 			if (msg[i].addr ==
 				af9015_af9013_config[0].demod_address)
 				req.cmd = WRITE_MEMORY;
@@ -735,6 +739,7 @@
 	{ 0xb8feb708, RC_MAP_MSI_DIGIVOX_II },
 	{ 0xa3703d00, RC_MAP_ALINK_DTU_M },
 	{ 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */
+	{ 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */
 	{ }
 };
 
@@ -749,6 +754,8 @@
 		RC_MAP_AZUREWAVE_AD_TU700 },
 	{ (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGI_VOX_MINI_III,
 		RC_MAP_MSI_DIGIVOX_III },
+	{ (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGIVOX_DUO,
+		RC_MAP_MSI_DIGIVOX_III },
 	{ (USB_VID_LEADTEK << 16) + USB_PID_WINFAST_DTV_DONGLE_GOLD,
 		RC_MAP_LEADTEK_Y04G0051 },
 	{ (USB_VID_AVERMEDIA << 16) + USB_PID_AVERMEDIA_VOLAR_X,
@@ -759,6 +766,8 @@
 		RC_MAP_DIGITALNOW_TINYTWIN },
 	{ (USB_VID_GTEK << 16) + USB_PID_TINYTWIN_3,
 		RC_MAP_DIGITALNOW_TINYTWIN },
+	{ (USB_VID_KWORLD_2 << 16) + USB_PID_SVEON_STV22,
+		RC_MAP_MSI_DIGIVOX_III },
 	{ }
 };
 
@@ -1082,44 +1091,11 @@
 	return ret;
 }
 
-/* init 2nd I2C adapter */
-static int af9015_i2c_init(struct dvb_usb_device *d)
-{
-	int ret;
-	struct af9015_state *state = d->priv;
-	deb_info("%s:\n", __func__);
-
-	strncpy(state->i2c_adap.name, d->desc->name,
-		sizeof(state->i2c_adap.name));
-	state->i2c_adap.algo      = d->props.i2c_algo;
-	state->i2c_adap.algo_data = NULL;
-	state->i2c_adap.dev.parent = &d->udev->dev;
-
-	i2c_set_adapdata(&state->i2c_adap, d);
-
-	ret = i2c_add_adapter(&state->i2c_adap);
-	if (ret < 0)
-		err("could not add i2c adapter");
-
-	return ret;
-}
-
 static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	int ret;
-	struct af9015_state *state = adap->dev->priv;
-	struct i2c_adapter *i2c_adap;
 
-	if (adap->id == 0) {
-		/* select I2C adapter */
-		i2c_adap = &adap->dev->i2c_adap;
-
-		deb_info("%s: init I2C\n", __func__);
-		ret = af9015_i2c_init(adap->dev);
-	} else {
-		/* select I2C adapter */
-		i2c_adap = &state->i2c_adap;
-
+	if (adap->id == 1) {
 		/* copy firmware to 2nd demodulator */
 		if (af9015_config.dual_mode) {
 			ret = af9015_copy_firmware(adap->dev);
@@ -1136,7 +1112,7 @@
 
 	/* attach demodulator */
 	adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
-		i2c_adap);
+		&adap->dev->i2c_adap);
 
 	return adap->fe == NULL ? -ENODEV : 0;
 }
@@ -1206,57 +1182,56 @@
 
 static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	struct af9015_state *state = adap->dev->priv;
-	struct i2c_adapter *i2c_adap;
 	int ret;
 	deb_info("%s:\n", __func__);
 
-	/* select I2C adapter */
-	if (adap->id == 0)
-		i2c_adap = &adap->dev->i2c_adap;
-	else
-		i2c_adap = &state->i2c_adap;
-
 	switch (af9015_af9013_config[adap->id].tuner) {
 	case AF9013_TUNER_MT2060:
 	case AF9013_TUNER_MT2060_2:
-		ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(mt2060_attach, adap->fe, &adap->dev->i2c_adap,
 			&af9015_mt2060_config,
 			af9015_config.mt2060_if1[adap->id])
 			== NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_QT1010:
 	case AF9013_TUNER_QT1010A:
-		ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
 			&af9015_qt1010_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_TDA18271:
-		ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+		ret = dvb_attach(tda18271_attach, adap->fe, 0xc0,
+			&adap->dev->i2c_adap,
 			&af9015_tda18271_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_TDA18218:
-		ret = dvb_attach(tda18218_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(tda18218_attach, adap->fe,
+			&adap->dev->i2c_adap,
 			&af9015_tda18218_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_MXL5003D:
-		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(mxl5005s_attach, adap->fe,
+			&adap->dev->i2c_adap,
 			&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_MXL5005D:
 	case AF9013_TUNER_MXL5005R:
-		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(mxl5005s_attach, adap->fe,
+			&adap->dev->i2c_adap,
 			&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_ENV77H11D5:
-		ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+		ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0,
+			&adap->dev->i2c_adap,
 			DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_MC44S803:
-		ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(mc44s803_attach, adap->fe,
+			&adap->dev->i2c_adap,
 			&af9015_mc44s803_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_MXL5007T:
-		ret = dvb_attach(mxl5007t_attach, adap->fe, i2c_adap,
+		ret = dvb_attach(mxl5007t_attach, adap->fe,
+			&adap->dev->i2c_adap,
 			0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
 		break;
 	case AF9013_TUNER_UNKNOWN:
@@ -1309,6 +1284,7 @@
 		USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)},
 /* 35 */{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
 	{USB_DEVICE(USB_VID_GTEK,      USB_PID_TINYTWIN_3)},
+	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_SVEON_STV22)},
 	{0},
 };
 MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1502,7 +1478,7 @@
 
 		.i2c_algo = &af9015_i2c_algo,
 
-		.num_device_descs = 9, /* check max from dvb-usb.h */
+		.num_device_descs = 10, /* check max from dvb-usb.h */
 		.devices = {
 			{
 				.name = "Xtensions XD-380",
@@ -1554,6 +1530,11 @@
 				.cold_ids = {&af9015_usb_table[20], NULL},
 				.warm_ids = {NULL},
 			},
+			{
+				.name = "Sveon STV22 Dual USB DVB-T Tuner HDTV",
+				.cold_ids = {&af9015_usb_table[37], NULL},
+				.warm_ids = {NULL},
+			},
 		}
 	}, {
 		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -1704,33 +1685,11 @@
 	return ret;
 }
 
-static void af9015_i2c_exit(struct dvb_usb_device *d)
-{
-	struct af9015_state *state = d->priv;
-	deb_info("%s:\n", __func__);
-
-	/* remove 2nd I2C adapter */
-	if (d->state & DVB_USB_STATE_I2C)
-		i2c_del_adapter(&state->i2c_adap);
-}
-
-static void af9015_usb_device_exit(struct usb_interface *intf)
-{
-	struct dvb_usb_device *d = usb_get_intfdata(intf);
-	deb_info("%s:\n", __func__);
-
-	/* remove 2nd I2C adapter */
-	if (d != NULL && d->desc != NULL)
-		af9015_i2c_exit(d);
-
-	dvb_usb_device_exit(intf);
-}
-
 /* usb specific object needed to register this driver with the usb subsystem */
 static struct usb_driver af9015_usb_driver = {
 	.name = "dvb_usb_af9015",
 	.probe = af9015_usb_probe,
-	.disconnect = af9015_usb_device_exit,
+	.disconnect = dvb_usb_device_exit,
 	.id_table = af9015_usb_table,
 };
 
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index beb3004..6252ea6c 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -99,7 +99,6 @@
 };
 
 struct af9015_state {
-	struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
 	u8 rc_repeat;
 	u32 rc_keycode;
 	u8 rc_last[4];
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 7c327b5..2cbf19a 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -347,15 +347,17 @@
  * PCB: ?
  * parts: DNOS404ZH102A(MT352, DTT7579(?))
  *
- * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=????????
- * PCB: ?
+ * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)"
+ * PCB: PCB 507T (rev1.61)
  * parts: DNOS404ZH103A(ZL10353, DTT7579(?))
+ * OEA=0a OEB=00 OEC=00 OED=ff OEE=00
+ * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00
  *
  * E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee"
  * PCB: 507CD (rev1.1)
  * parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEF=fe
- * IOA=4f IOB=ff IOC=00 IOD=06 IOF=01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01
  * IOD[0] ZL10353 1=enabled
  * IOA[7] TS 0=enabled
  * tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not)
@@ -363,30 +365,30 @@
  * E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)"
  * PCB: 507DC (rev0.2)
  * parts: TDA10023, DTOS403IH102B TM, CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEF=fe
- * IOA=4f IOB=ff IOC=00 IOD=26 IOF=01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01
  * IOD[0] TDA10023 1=enabled
  *
  * E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)"
  * PCB: 507SI (rev2.1)
  * parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=fe
- * IOA=4d IOB=ff IOC=00 IOD=26 IOF=01
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe
+ * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01
  * IOD[0] CX24116 1=enabled
  *
  * E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
  * PCB: 507FA (rev0.4)
  * parts: TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOF=c0
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
  * IOD[5] TDA10023 1=enabled
  * IOE[0] tuner 1=enabled
  *
  * E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
  * PCB: 507FA (rev1.1)
  * parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEF=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOF=c0
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
  * DVB-C:
  * IOD[5] TDA10023 1=enabled
  * IOE[0] tuner 1=enabled
@@ -398,8 +400,8 @@
  * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)"
  * PCB: 508TC (rev0.6)
  * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
- * OEA=80 OEB=00 OEC=03 OED=f7 OEF=ff
- * IOA=4d IOB=00 IOC=cc IOD=48 IOF=e4
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
  * IOA[7] TS 1=enabled
  * IOE[4] TDA18212 1=enabled
  * DVB-C:
@@ -414,11 +416,34 @@
  * E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)"
  * PCB: 508S2 (rev0.7)
  * parts: DNBU10512IST(STV0903, STV6110), ISL6423
- * OEA=80 OEB=00 OEC=03 OED=f7 OEF=ff
- * IOA=4d IOB=00 IOC=c4 IOD=08 IOF=e4
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
  * IOA[7] TS 1=enabled
  * IOE[5] STV0903 1=enabled
  *
+ * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)"
+ * PCB: 508PTC (rev0.5)
+ * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[4] TDA18212 1=enabled
+ * DVB-C:
+ * IOD[6] ZL10353 0=disabled
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] IF 1=enabled
+ * DVB-T:
+ * IOD[5] TDA10023 0=disabled
+ * IOD[6] ZL10353 1=enabled
+ * IOE[0] IF 0=enabled
+ *
+ * E7 S2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)"
+ * PCB: 508PS2 (rev0.4)
+ * parts: DNBU10512IST(STV0903, STV6110), ISL6423
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[5] STV0903 1=enabled
  */
 
 static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
@@ -459,7 +484,7 @@
 	state->hw = hw_info[0];
 
 	switch (state->hw) {
-	case ANYSEE_HW_02: /* 2 */
+	case ANYSEE_HW_507T: /* 2 */
 		/* E30 */
 
 		/* attach demod */
@@ -593,7 +618,9 @@
 
 		break;
 	case ANYSEE_HW_508TC: /* 18 */
+	case ANYSEE_HW_508PTC: /* 21 */
 		/* E7 TC */
+		/* E7 PTC */
 
 		/* enable transport stream on IOA[7] */
 		ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80);
@@ -650,7 +677,9 @@
 
 		break;
 	case ANYSEE_HW_508S2: /* 19 */
+	case ANYSEE_HW_508PS2: /* 22 */
 		/* E7 S2 */
+		/* E7 PS2 */
 
 		/* enable transport stream on IOA[7] */
 		ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (1 << 7), 0x80);
@@ -687,7 +716,7 @@
 	deb_info("%s:\n", __func__);
 
 	switch (state->hw) {
-	case ANYSEE_HW_02: /* 2 */
+	case ANYSEE_HW_507T: /* 2 */
 		/* E30 */
 
 		/* attach tuner */
@@ -762,7 +791,9 @@
 
 		break;
 	case ANYSEE_HW_508TC: /* 18 */
+	case ANYSEE_HW_508PTC: /* 21 */
 		/* E7 TC */
+		/* E7 PTC */
 
 		/* enable tuner on IOE[4] */
 		ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 4), 0x10);
@@ -775,7 +806,9 @@
 
 		break;
 	case ANYSEE_HW_508S2: /* 19 */
+	case ANYSEE_HW_508PS2: /* 22 */
 		/* E7 S2 */
+		/* E7 PS2 */
 
 		/* attach tuner */
 		fe = dvb_attach(stv6110_attach, adap->fe,
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
index a7673aa..ad6ccd1 100644
--- a/drivers/media/dvb/dvb-usb/anysee.h
+++ b/drivers/media/dvb/dvb-usb/anysee.h
@@ -61,13 +61,15 @@
 	u8 seq;
 };
 
-#define ANYSEE_HW_02     2 /* E30 */
-#define ANYSEE_HW_507CD  6 /* E30 Plus */
-#define ANYSEE_HW_507DC 10 /* E30 C Plus */
-#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
-#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
-#define ANYSEE_HW_508TC 18 /* E7 TC */
-#define ANYSEE_HW_508S2 19 /* E7 S2 */
+#define ANYSEE_HW_507T    2 /* E30 */
+#define ANYSEE_HW_507CD   6 /* E30 Plus */
+#define ANYSEE_HW_507DC  10 /* E30 C Plus */
+#define ANYSEE_HW_507SI  11 /* E30 S2 Plus */
+#define ANYSEE_HW_507FA  15 /* E30 Combo Plus / E30 C Plus */
+#define ANYSEE_HW_508TC  18 /* E7 TC */
+#define ANYSEE_HW_508S2  19 /* E7 S2 */
+#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */
+#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */
 
 #define REG_IOA       0x80 /* Port A (bit addressable) */
 #define REG_IOB       0x90 /* Port B (bit addressable) */
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index c519ad5..d0ea5b6 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -17,6 +17,7 @@
 #include "mt2266.h"
 #include "tuner-xc2028.h"
 #include "xc5000.h"
+#include "xc4000.h"
 #include "s5h1411.h"
 #include "dib0070.h"
 #include "dib0090.h"
@@ -2655,6 +2656,156 @@
 		== NULL ? -ENODEV : 0;
 }
 
+static int dib0700_xc4000_tuner_callback(void *priv, int component,
+					 int command, int arg)
+{
+	struct dvb_usb_adapter *adap = priv;
+
+	if (command == XC4000_TUNER_RESET) {
+		/* Reset the tuner */
+		dib7000p_set_gpio(adap->fe, 8, 0, 0);
+		msleep(10);
+		dib7000p_set_gpio(adap->fe, 8, 0, 1);
+	} else {
+		err("xc4000: unknown tuner callback command: %d\n", command);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = {
+	.band_caps = BAND_UHF | BAND_VHF,
+	.setup = 0x64,
+	.inv_gain = 0x02c8,
+	.time_stabiliz = 0x15,
+	.alpha_level = 0x00,
+	.thlock = 0x76,
+	.wbd_inv = 0x01,
+	.wbd_ref = 0x0b33,
+	.wbd_sel = 0x00,
+	.wbd_alpha = 0x02,
+	.agc1_max = 0x00,
+	.agc1_min = 0x00,
+	.agc2_max = 0x9b26,
+	.agc2_min = 0x26ca,
+	.agc1_pt1 = 0x00,
+	.agc1_pt2 = 0x00,
+	.agc1_pt3 = 0x00,
+	.agc1_slope1 = 0x00,
+	.agc1_slope2 = 0x00,
+	.agc2_pt1 = 0x00,
+	.agc2_pt2 = 0x80,
+	.agc2_slope1 = 0x1d,
+	.agc2_slope2 = 0x1d,
+	.alpha_mant = 0x11,
+	.alpha_exp = 0x1b,
+	.beta_mant = 0x17,
+	.beta_exp = 0x33,
+	.perform_agc_softsplit = 0x00,
+};
+
+static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = {
+	60000, 30000,	/* internal, sampling */
+	1, 8, 3, 1, 0,	/* pll_cfg: prediv, ratio, range, reset, bypass */
+	0, 0, 1, 1, 0,	/* misc: refdiv, bypclk_div, IO_CLK_en_core, */
+			/* ADClkSrc, modulo */
+	(3 << 14) | (1 << 12) | 524,	/* sad_cfg: refsel, sel, freq_15k */
+	39370534,	/* ifreq */
+	20452225,	/* timf */
+	30000000	/* xtal */
+};
+
+/* FIXME: none of these inputs are validated yet */
+static struct dib7000p_config pctv_340e_config = {
+	.output_mpeg2_in_188_bytes = 1,
+
+	.agc_config_count = 1,
+	.agc = &stk7700p_7000p_xc4000_agc_config,
+	.bw  = &stk7700p_xc4000_pll_config,
+
+	.gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+	.gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+	.gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
+};
+
+/* PCTV 340e GPIOs map:
+   dib0700:
+   GPIO2  - CX25843 sleep
+   GPIO3  - CS5340 reset
+   GPIO5  - IRD
+   GPIO6  - Power Supply
+   GPIO8  - LNA (1=off 0=on)
+   GPIO10 - CX25843 reset
+   dib7000:
+   GPIO8  - xc4000 reset
+ */
+static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dib0700_state *st = adap->dev->priv;
+
+	/* Power Supply on */
+	dib0700_set_gpio(adap->dev, GPIO6,  GPIO_OUT, 0);
+	msleep(50);
+	dib0700_set_gpio(adap->dev, GPIO6,  GPIO_OUT, 1);
+	msleep(100); /* Allow power supply to settle before probing */
+
+	/* cx25843 reset */
+	dib0700_set_gpio(adap->dev, GPIO10,  GPIO_OUT, 0);
+	msleep(1); /* cx25843 datasheet say 350us required */
+	dib0700_set_gpio(adap->dev, GPIO10,  GPIO_OUT, 1);
+
+	/* LNA off for now */
+	dib0700_set_gpio(adap->dev, GPIO8,  GPIO_OUT, 1);
+
+	/* Put the CX25843 to sleep for now since we're in digital mode */
+	dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
+
+	/* FIXME: not verified yet */
+	dib0700_ctrl_clock(adap->dev, 72, 1);
+
+	msleep(500);
+
+	if (dib7000pc_detection(&adap->dev->i2c_adap) == 0) {
+		/* Demodulator not found for some reason? */
+		return -ENODEV;
+	}
+
+	adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12,
+			      &pctv_340e_config);
+	st->is_dib7000pc = 1;
+
+	return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct xc4000_config dib7000p_xc4000_tunerconfig = {
+	.i2c_address	  = 0x61,
+	.default_pm	  = 1,
+	.dvb_amplitude	  = 0,
+	.set_smoothedcvbs = 0,
+	.if_khz		  = 5400
+};
+
+static int xc4000_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct i2c_adapter *tun_i2c;
+
+	/* The xc4000 is not on the main i2c bus */
+	tun_i2c = dib7000p_get_i2c_master(adap->fe,
+					  DIBX000_I2C_INTERFACE_TUNER, 1);
+	if (tun_i2c == NULL) {
+		printk(KERN_ERR "Could not reach tuner i2c bus\n");
+		return 0;
+	}
+
+	/* Setup the reset callback */
+	adap->fe->callback = dib0700_xc4000_tuner_callback;
+
+	return dvb_attach(xc4000_attach, adap->fe, tun_i2c,
+			  &dib7000p_xc4000_tunerconfig)
+		== NULL ? -ENODEV : 0;
+}
+
 static struct lgdt3305_config hcw_lgdt3305_config = {
 	.i2c_addr           = 0x0e,
 	.mpeg_mode          = LGDT3305_MPEG_PARALLEL,
@@ -2802,6 +2953,8 @@
 	{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_TFE7090PVR) },
 	{ USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) },
 /* 75 */{ USB_DEVICE(USB_VID_MEDION,    USB_PID_CREATIX_CTX1921) },
+	{ USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV340E) },
+	{ USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV340E_SE) },
 	{ 0 }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -3772,6 +3925,41 @@
 					    RC_TYPE_NEC,
 			.change_protocol  = dib0700_change_protocol,
 		},
+	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+		.num_adapters = 1,
+		.adapter = {
+			{
+				.frontend_attach  = pctv340e_frontend_attach,
+				.tuner_attach     = xc4000_tuner_attach,
+
+				DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+				.size_of_priv = sizeof(struct
+						dib0700_adapter_state),
+			},
+		},
+
+		.num_device_descs = 2,
+		.devices = {
+			{   "Pinnacle PCTV 340e HD Pro USB Stick",
+				{ &dib0700_usb_id_table[76], NULL },
+				{ NULL },
+			},
+			{   "Pinnacle PCTV Hybrid Stick Solo",
+				{ &dib0700_usb_id_table[77], NULL },
+				{ NULL },
+			},
+		},
+		.rc.core = {
+			.rc_interval      = DEFAULT_RC_INTERVAL,
+			.rc_codes         = RC_MAP_DIB0700_RC5_TABLE,
+			.module_name	  = "dib0700",
+			.rc_query         = dib0700_rc_query_old_firmware,
+			.allowed_protos   = RC_TYPE_RC5 |
+					    RC_TYPE_RC6 |
+					    RC_TYPE_NEC,
+			.change_protocol  = dib0700_change_protocol,
+		},
 	},
 };
 
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 21b1549..2a79b8f 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -230,6 +230,8 @@
 #define USB_PID_PINNACLE_PCTV310E			0x3211
 #define USB_PID_PINNACLE_PCTV801E			0x023a
 #define USB_PID_PINNACLE_PCTV801E_SE			0x023b
+#define USB_PID_PINNACLE_PCTV340E			0x023d
+#define USB_PID_PINNACLE_PCTV340E_SE			0x023e
 #define USB_PID_PINNACLE_PCTV73A			0x0243
 #define USB_PID_PINNACLE_PCTV73ESE			0x0245
 #define USB_PID_PINNACLE_PCTV74E			0x0246
@@ -313,6 +315,7 @@
 #define USB_PID_FRIIO_WHITE				0x0001
 #define USB_PID_TVWAY_PLUS				0x0002
 #define USB_PID_SVEON_STV20				0xe39d
+#define USB_PID_SVEON_STV22				0xe401
 #define USB_PID_AZUREWAVE_AZ6027			0x3275
 #define USB_PID_TERRATEC_DVBS2CI_V1			0x10a4
 #define USB_PID_TERRATEC_DVBS2CI_V2			0x10ac
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index 76a8096..7d35d07 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -85,7 +85,7 @@
 	return key->scancode & 0xff;
 }
 
-static inline u8 rc5_scan(struct rc_map_table *key)
+static inline u16 rc5_scan(struct rc_map_table *key)
 {
 	return key->scancode & 0xffff;
 }
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h
index 831749a..ed32b9d 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -78,9 +78,6 @@
 #define ADV_MOD_DVB_BPSK 9     /* DVB-S BPSK */
 
 #define GET_USB_SPEED                     0x07
- #define USB_SPEED_LOW                    0
- #define USB_SPEED_FULL                   1
- #define USB_SPEED_HIGH                   2
 
 #define RESET_FX2                         0x13
 
diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c
index 08f8842..473b95e 100644
--- a/drivers/media/dvb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/dvb/dvb-usb/technisat-usb2.c
@@ -765,10 +765,8 @@
 	/* work and stuff was only created when the device is is hot-state */
 	if (dev != NULL) {
 		struct technisat_usb2_state *state = dev->priv;
-		if (state != NULL) {
+		if (state != NULL)
 			cancel_delayed_work_sync(&state->green_led_work);
-			flush_scheduled_work();
-		}
 	}
 
 	dvb_usb_device_exit(intf);
diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/dvb/dvb-usb/vp7045.h
index 969688f..cf5ec46 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.h
+++ b/drivers/media/dvb/dvb-usb/vp7045.h
@@ -36,9 +36,6 @@
  #define Tuner_Power_OFF                  0
 
 #define GET_USB_SPEED                     0x07
- #define USB_SPEED_LOW                    0
- #define USB_SPEED_FULL                   1
- #define USB_SPEED_HIGH                   2
 
 #define LOCK_TUNER_COMMAND                0x09
 
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index 21c52e3..489ae82 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -1208,7 +1208,7 @@
 	if (r->response != AVC_RESPONSE_ACCEPTED) {
 		dev_err(fdtv->device,
 			"CA PMT failed with response 0x%x\n", r->response);
-		ret = -EFAULT;
+		ret = -EACCES;
 	}
 out:
 	mutex_unlock(&fdtv->avc_mutex);
diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c
index 8ffb565..e5ebdbf 100644
--- a/drivers/media/dvb/firewire/firedtv-ci.c
+++ b/drivers/media/dvb/firewire/firedtv-ci.c
@@ -45,11 +45,6 @@
 	return flags;
 }
 
-static int fdtv_ca_reset(struct firedtv *fdtv)
-{
-	return avc_ca_reset(fdtv) ? -EFAULT : 0;
-}
-
 static int fdtv_ca_get_caps(void *arg)
 {
 	struct ca_caps *cap = arg;
@@ -65,12 +60,14 @@
 {
 	struct firedtv_tuner_status stat;
 	struct ca_slot_info *slot = arg;
+	int err;
 
-	if (avc_tuner_status(fdtv, &stat))
-		return -EFAULT;
+	err = avc_tuner_status(fdtv, &stat);
+	if (err)
+		return err;
 
 	if (slot->num != 0)
-		return -EFAULT;
+		return -EACCES;
 
 	slot->type = CA_CI;
 	slot->flags = fdtv_get_ca_flags(&stat);
@@ -81,21 +78,21 @@
 {
 	struct ca_msg *reply = arg;
 
-	return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;
+	return avc_ca_app_info(fdtv, reply->msg, &reply->length);
 }
 
 static int fdtv_ca_info(struct firedtv *fdtv, void *arg)
 {
 	struct ca_msg *reply = arg;
 
-	return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;
+	return avc_ca_info(fdtv, reply->msg, &reply->length);
 }
 
 static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg)
 {
 	struct ca_msg *reply = arg;
 
-	return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;
+	return avc_ca_get_mmi(fdtv, reply->msg, &reply->length);
 }
 
 static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
@@ -111,14 +108,15 @@
 		err = fdtv_ca_info(fdtv, arg);
 		break;
 	default:
-		if (avc_tuner_status(fdtv, &stat))
-			err = -EFAULT;
-		else if (stat.ca_mmi == 1)
+		err = avc_tuner_status(fdtv, &stat);
+		if (err)
+			break;
+		if (stat.ca_mmi == 1)
 			err = fdtv_ca_get_mmi(fdtv, arg);
 		else {
 			dev_info(fdtv->device, "unhandled CA message 0x%08x\n",
 				 fdtv->ca_last_command);
-			err = -EFAULT;
+			err = -EACCES;
 		}
 	}
 	fdtv->ca_last_command = 0;
@@ -141,7 +139,7 @@
 		data_length = msg->msg[3];
 	}
 
-	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0;
+	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
 }
 
 static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
@@ -170,7 +168,7 @@
 	default:
 		dev_err(fdtv->device, "unhandled CA message 0x%08x\n",
 			fdtv->ca_last_command);
-		err = -EFAULT;
+		err = -EACCES;
 	}
 	return err;
 }
@@ -184,7 +182,7 @@
 
 	switch (cmd) {
 	case CA_RESET:
-		err = fdtv_ca_reset(fdtv);
+		err = avc_ca_reset(fdtv);
 		break;
 	case CA_GET_CAP:
 		err = fdtv_ca_get_caps(arg);
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 44b816f..32e08e3 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -49,6 +49,27 @@
 	help
 	  A Silicon tuner that supports DVB-S and DVB-S2 modes
 
+comment "Multistandard (cable + terrestrial) frontends"
+	depends on DVB_CORE
+
+config DVB_DRXK
+	tristate "Micronas DRXK based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  Micronas DRX-K DVB-C/T demodulator.
+
+	  Say Y when you want to support this frontend.
+
+config DVB_TDA18271C2DD
+	tristate "NXP TDA18271C2 silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  NXP TDA18271 silicon tuner.
+
+	  Say Y when you want to support this tuner.
+
 comment "DVB-S (satellite) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 2f3a6f7..6a6ba05 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -10,6 +10,7 @@
 au8522-objs = au8522_dig.o au8522_decoder.o
 drxd-objs = drxd_firm.o drxd_hard.o
 cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
+drxk-objs := drxk_hard.o
 
 obj-$(CONFIG_DVB_PLL) += dvb-pll.o
 obj-$(CONFIG_DVB_STV0299) += stv0299.o
@@ -88,4 +89,6 @@
 obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
 obj-$(CONFIG_DVB_STV0367) += stv0367.o
 obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
+obj-$(CONFIG_DVB_DRXK) += drxk.o
+obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
 
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index b537891..2b248c1 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -692,7 +692,7 @@
 	/* Interrogate the decoder to see if we are getting a real signal */
 	lock_status = au8522_readreg(state, 0x00);
 	if (lock_status == 0xa2)
-		vt->signal = 0x01;
+		vt->signal = 0xffff;
 	else
 		vt->signal = 0x00;
 
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
index e9ee555..c341d57 100644
--- a/drivers/media/dvb/frontends/cx24113.c
+++ b/drivers/media/dvb/frontends/cx24113.c
@@ -31,8 +31,8 @@
 
 static int debug;
 
-#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
-#define err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
+#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
+#define cx_err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
 
 #define dprintk(args...) \
 	do { \
@@ -341,7 +341,7 @@
 	} while (N < 6 && R < 3);
 
 	if (N < 6) {
-		err("strange frequency: N < 6\n");
+		cx_err("strange frequency: N < 6\n");
 		return;
 	}
 	F = freq_hz;
@@ -563,7 +563,7 @@
 		kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
 	int rc;
 	if (state == NULL) {
-		err("Unable to kzalloc\n");
+		cx_err("Unable to kzalloc\n");
 		goto error;
 	}
 
@@ -571,7 +571,7 @@
 	state->config = config;
 	state->i2c = i2c;
 
-	info("trying to detect myself\n");
+	cx_info("trying to detect myself\n");
 
 	/* making a dummy read, because of some expected troubles
 	 * after power on */
@@ -579,24 +579,24 @@
 
 	rc = cx24113_readreg(state, 0x00);
 	if (rc < 0) {
-		info("CX24113 not found.\n");
+		cx_info("CX24113 not found.\n");
 		goto error;
 	}
 	state->rev = rc;
 
 	switch (rc) {
 	case 0x43:
-		info("detected CX24113 variant\n");
+		cx_info("detected CX24113 variant\n");
 		break;
 	case REV_CX24113:
-		info("successfully detected\n");
+		cx_info("successfully detected\n");
 		break;
 	default:
-		err("unsupported device id: %x\n", state->rev);
+		cx_err("unsupported device id: %x\n", state->rev);
 		goto error;
 	}
 	state->ver = cx24113_readreg(state, 0x01);
-	info("version: %x\n", state->ver);
+	cx_info("version: %x\n", state->ver);
 
 	/* create dvb_frontend */
 	memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
index 95c6465..ccd0525 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -1452,11 +1452,7 @@
 	cmd.args[0x00] = CMD_BANDWIDTH;
 	cmd.args[0x01] = 0x00;
 	cmd.len = 0x02;
-	ret = cx24116_cmd_execute(fe, &cmd);
-	if (ret != 0)
-		return ret;
-
-	return ret;
+	return cx24116_cmd_execute(fe, &cmd);
 }
 
 static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params,
diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h
index ad17845..2906582 100644
--- a/drivers/media/dvb/frontends/cxd2820r.h
+++ b/drivers/media/dvb/frontends/cxd2820r.h
@@ -55,13 +55,13 @@
 	 * Default: 0
 	 * Values: 0, 1
 	 */
-	int if_agc_polarity:1;
+	bool if_agc_polarity;
 
 	/* Spectrum inversion.
 	 * Default: 0
 	 * Values: 0, 1
 	 */
-	int spec_inv:1;
+	bool spec_inv;
 
 	/* IFs for all used modes.
 	 * Default: none, must set
diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
index 0779f69..d416e85 100644
--- a/drivers/media/dvb/frontends/cxd2820r_core.c
+++ b/drivers/media/dvb/frontends/cxd2820r_core.c
@@ -314,6 +314,8 @@
 			} else if (c->delivery_system == SYS_DVBT2) {
 				/* DVB-T => DVB-T2 */
 				ret = cxd2820r_sleep_t(fe);
+				if (ret)
+					break;
 				ret = cxd2820r_set_frontend_t2(fe, p);
 			}
 			break;
@@ -324,6 +326,8 @@
 			} else if (c->delivery_system == SYS_DVBT) {
 				/* DVB-T2 => DVB-T */
 				ret = cxd2820r_sleep_t2(fe);
+				if (ret)
+					break;
 				ret = cxd2820r_set_frontend_t(fe, p);
 			}
 			break;
@@ -740,12 +744,13 @@
 	struct i2c_msg msg[], int num)
 {
 	struct cxd2820r_priv *priv = i2c_get_adapdata(i2c_adap);
-	u8 obuf[msg[0].len + 2];
+	int ret;
+	u8 *obuf = kmalloc(msg[0].len + 2, GFP_KERNEL);
 	struct i2c_msg msg2[2] = {
 		{
 			.addr = priv->cfg.i2c_address,
 			.flags = 0,
-			.len = sizeof(obuf),
+			.len = msg[0].len + 2,
 			.buf = obuf,
 		}, {
 			.addr = priv->cfg.i2c_address,
@@ -755,15 +760,24 @@
 		}
 	};
 
+	if (!obuf)
+		return -ENOMEM;
+
 	obuf[0] = 0x09;
 	obuf[1] = (msg[0].addr << 1);
 	if (num == 2) { /* I2C read */
 		obuf[1] = (msg[0].addr << 1) | I2C_M_RD; /* I2C RD flag */
-		msg2[0].len = sizeof(obuf) - 1; /* maybe HW bug ? */
+		msg2[0].len = msg[0].len + 2 - 1; /* '-1' maybe HW bug ? */
 	}
 	memcpy(&obuf[2], msg[0].buf, msg[0].len);
 
-	return i2c_transfer(priv->i2c, msg2, num);
+	ret = i2c_transfer(priv->i2c, msg2, num);
+	if (ret < 0)
+		warn("tuner i2c failed ret:%d", ret);
+
+	kfree(obuf);
+
+	return ret;
 }
 
 static struct i2c_algorithm cxd2820r_tuner_i2c_algo = {
diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h
index 25adbee..0c0ebc9 100644
--- a/drivers/media/dvb/frontends/cxd2820r_priv.h
+++ b/drivers/media/dvb/frontends/cxd2820r_priv.h
@@ -55,13 +55,13 @@
 	struct mutex fe_lock; /* FE lock */
 	int active_fe:2; /* FE lock, -1=NONE, 0=DVB-T/T2, 1=DVB-C */
 
-	int ber_running:1;
+	bool ber_running;
 
 	u8 bank[2];
 	u8 gpio[3];
 
 	fe_delivery_system_t delivery_system;
-	int last_tune_failed:1; /* for switch between T and T2 tune */
+	bool last_tune_failed; /* for switch between T and T2 tune */
 };
 
 /* cxd2820r_core.c */
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 0c9f40c..a64a538 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -2336,6 +2336,11 @@
 		request_firmware() will hit an OOPS (this should be moved somewhere
 		more common) */
 
+	/* FIXME: make sure the dev.parent field is initialized, or else
+	   request_firmware() will hit an OOPS (this should be moved somewhere
+	   more common) */
+	st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
+
 	dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
 
 	/* init 7090 tuner adapter */
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c
index ea4c1c3..2238bf0 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb/frontends/drxd_hard.c
@@ -28,7 +28,6 @@
 #include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/i2c.h>
-#include <linux/version.h>
 #include <asm/div64.h>
 
 #include "dvb_frontend.h"
@@ -233,7 +232,7 @@
 	return 0;
 }
 
-inline u32 MulDiv32(u32 a, u32 b, u32 c)
+static inline u32 MulDiv32(u32 a, u32 b, u32 c)
 {
 	u64 tmp64;
 
@@ -910,14 +909,16 @@
 		return -EIO;
 	}
 
-	state->microcode = kzalloc(fw->size, GFP_KERNEL);
+	state->microcode = kmalloc(fw->size, GFP_KERNEL);
 	if (state->microcode == NULL) {
-		printk(KERN_ERR "drxd: firmware load failure: nomemory\n");
+		release_firmware(fw);
+		printk(KERN_ERR "drxd: firmware load failure: no memory\n");
 		return -ENOMEM;
 	}
 
 	memcpy(state->microcode, fw->data, fw->size);
 	state->microcode_length = fw->size;
+	release_firmware(fw);
 	return 0;
 }
 
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
new file mode 100644
index 0000000..58baf41
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk.h
@@ -0,0 +1,47 @@
+#ifndef _DRXK_H_
+#define _DRXK_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/**
+ * struct drxk_config - Configure the initial parameters for DRX-K
+ *
+ * adr:			I2C Address of the DRX-K
+ * single_master:	Device is on the single master mode
+ * no_i2c_bridge:	Don't switch the I2C bridge to talk with tuner
+ * antenna_gpio:	GPIO bit used to control the antenna
+ * antenna_dvbt:	GPIO bit for changing antenna to DVB-C. A value of 1
+ *			means that 1=DVBC, 0 = DVBT. Zero means the opposite.
+ * microcode_name:	Name of the firmware file with the microcode
+ *
+ * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
+ * UIO-3.
+ */
+struct drxk_config {
+	u8	adr;
+	bool	single_master;
+	bool	no_i2c_bridge;
+
+	bool	antenna_dvbt;
+	u16	antenna_gpio;
+
+	const char *microcode_name;
+};
+
+#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
+        && defined(MODULE))
+extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+					struct i2c_adapter *i2c,
+					struct dvb_frontend **fe_t);
+#else
+static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+					struct i2c_adapter *i2c,
+					struct dvb_frontend **fe_t)
+{
+        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+        return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
new file mode 100644
index 0000000..41b0838
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -0,0 +1,6454 @@
+/*
+ * drxk_hard: DRX-K DVB-C/T demodulator driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "drxk.h"
+#include "drxk_hard.h"
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode);
+static int PowerDownQAM(struct drxk_state *state);
+static int SetDVBTStandard(struct drxk_state *state,
+			   enum OperationMode oMode);
+static int SetQAMStandard(struct drxk_state *state,
+			  enum OperationMode oMode);
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+		  s32 tunerFreqOffset);
+static int SetDVBTStandard(struct drxk_state *state,
+			   enum OperationMode oMode);
+static int DVBTStart(struct drxk_state *state);
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+		   s32 tunerFreqOffset);
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int SwitchAntennaToQAM(struct drxk_state *state);
+static int SwitchAntennaToDVBT(struct drxk_state *state);
+
+static bool IsDVBT(struct drxk_state *state)
+{
+	return state->m_OperationMode == OM_DVBT;
+}
+
+static bool IsQAM(struct drxk_state *state)
+{
+	return state->m_OperationMode == OM_QAM_ITU_A ||
+	    state->m_OperationMode == OM_QAM_ITU_B ||
+	    state->m_OperationMode == OM_QAM_ITU_C;
+}
+
+bool IsA1WithPatchCode(struct drxk_state *state)
+{
+	return state->m_DRXK_A1_PATCH_CODE;
+}
+
+bool IsA1WithRomCode(struct drxk_state *state)
+{
+	return state->m_DRXK_A1_ROM_CODE;
+}
+
+#define NOA1ROM 0
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr)  (((addr) & 0xFC30FF80) != 0)
+
+#define DEFAULT_MER_83  165
+#define DEFAULT_MER_93  250
+
+#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02)
+#endif
+
+#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
+#endif
+
+#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH
+#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06)
+#endif
+
+#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
+#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
+
+#ifndef DRXK_KI_RAGC_ATV
+#define DRXK_KI_RAGC_ATV   4
+#endif
+#ifndef DRXK_KI_IAGC_ATV
+#define DRXK_KI_IAGC_ATV   6
+#endif
+#ifndef DRXK_KI_DAGC_ATV
+#define DRXK_KI_DAGC_ATV   7
+#endif
+
+#ifndef DRXK_KI_RAGC_QAM
+#define DRXK_KI_RAGC_QAM   3
+#endif
+#ifndef DRXK_KI_IAGC_QAM
+#define DRXK_KI_IAGC_QAM   4
+#endif
+#ifndef DRXK_KI_DAGC_QAM
+#define DRXK_KI_DAGC_QAM   7
+#endif
+#ifndef DRXK_KI_RAGC_DVBT
+#define DRXK_KI_RAGC_DVBT  (IsA1WithPatchCode(state) ? 3 : 2)
+#endif
+#ifndef DRXK_KI_IAGC_DVBT
+#define DRXK_KI_IAGC_DVBT  (IsA1WithPatchCode(state) ? 4 : 2)
+#endif
+#ifndef DRXK_KI_DAGC_DVBT
+#define DRXK_KI_DAGC_DVBT  (IsA1WithPatchCode(state) ? 10 : 7)
+#endif
+
+#ifndef DRXK_AGC_DAC_OFFSET
+#define DRXK_AGC_DAC_OFFSET (0x800)
+#endif
+
+#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ
+#define DRXK_BANDWIDTH_8MHZ_IN_HZ  (0x8B8249L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ
+#define DRXK_BANDWIDTH_7MHZ_IN_HZ  (0x7A1200L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ
+#define DRXK_BANDWIDTH_6MHZ_IN_HZ  (0x68A1B6L)
+#endif
+
+#ifndef DRXK_QAM_SYMBOLRATE_MAX
+#define DRXK_QAM_SYMBOLRATE_MAX         (7233000)
+#endif
+
+#define DRXK_BL_ROM_OFFSET_TAPS_DVBT    56
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A   64
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C   0x5FE0
+#define DRXK_BL_ROM_OFFSET_TAPS_BG      24
+#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP  32
+#define DRXK_BL_ROM_OFFSET_TAPS_NTSC    40
+#define DRXK_BL_ROM_OFFSET_TAPS_FM      48
+#define DRXK_BL_ROM_OFFSET_UCODE        0
+
+#define DRXK_BLC_TIMEOUT                100
+
+#define DRXK_BLCC_NR_ELEMENTS_TAPS      2
+#define DRXK_BLCC_NR_ELEMENTS_UCODE     6
+
+#define DRXK_BLDC_NR_ELEMENTS_TAPS      28
+
+#ifndef DRXK_OFDM_NE_NOTCH_WIDTH
+#define DRXK_OFDM_NE_NOTCH_WIDTH             (4)
+#endif
+
+#define DRXK_QAM_SL_SIG_POWER_QAM16       (40960)
+#define DRXK_QAM_SL_SIG_POWER_QAM32       (20480)
+#define DRXK_QAM_SL_SIG_POWER_QAM64       (43008)
+#define DRXK_QAM_SL_SIG_POWER_QAM128      (20992)
+#define DRXK_QAM_SL_SIG_POWER_QAM256      (43520)
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+#define dprintk(level, fmt, arg...) do {			\
+if (debug >= level)						\
+	printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg);	\
+} while (0)
+
+
+static inline u32 MulDiv32(u32 a, u32 b, u32 c)
+{
+	u64 tmp64;
+
+	tmp64 = (u64) a * (u64) b;
+	do_div(tmp64, c);
+
+	return (u32) tmp64;
+}
+
+inline u32 Frac28a(u32 a, u32 c)
+{
+	int i = 0;
+	u32 Q1 = 0;
+	u32 R0 = 0;
+
+	R0 = (a % c) << 4;	/* 32-28 == 4 shifts possible at max */
+	Q1 = a / c;		/* integer part, only the 4 least significant bits
+				   will be visible in the result */
+
+	/* division using radix 16, 7 nibbles in the result */
+	for (i = 0; i < 7; i++) {
+		Q1 = (Q1 << 4) | (R0 / c);
+		R0 = (R0 % c) << 4;
+	}
+	/* rounding */
+	if ((R0 >> 3) >= c)
+		Q1++;
+
+	return Q1;
+}
+
+static u32 Log10Times100(u32 x)
+{
+	static const u8 scale = 15;
+	static const u8 indexWidth = 5;
+	u8 i = 0;
+	u32 y = 0;
+	u32 d = 0;
+	u32 k = 0;
+	u32 r = 0;
+	/*
+	   log2lut[n] = (1<<scale) * 200 * log2(1.0 + ((1.0/(1<<INDEXWIDTH)) * n))
+	   0 <= n < ((1<<INDEXWIDTH)+1)
+	 */
+
+	static const u32 log2lut[] = {
+		0,		/* 0.000000 */
+		290941,		/* 290941.300628 */
+		573196,		/* 573196.476418 */
+		847269,		/* 847269.179851 */
+		1113620,	/* 1113620.489452 */
+		1372674,	/* 1372673.576986 */
+		1624818,	/* 1624817.752104 */
+		1870412,	/* 1870411.981536 */
+		2109788,	/* 2109787.962654 */
+		2343253,	/* 2343252.817465 */
+		2571091,	/* 2571091.461923 */
+		2793569,	/* 2793568.696416 */
+		3010931,	/* 3010931.055901 */
+		3223408,	/* 3223408.452106 */
+		3431216,	/* 3431215.635215 */
+		3634553,	/* 3634553.498355 */
+		3833610,	/* 3833610.244726 */
+		4028562,	/* 4028562.434393 */
+		4219576,	/* 4219575.925308 */
+		4406807,	/* 4406806.721144 */
+		4590402,	/* 4590401.736809 */
+		4770499,	/* 4770499.491025 */
+		4947231,	/* 4947230.734179 */
+		5120719,	/* 5120719.018555 */
+		5291081,	/* 5291081.217197 */
+		5458428,	/* 5458427.996830 */
+		5622864,	/* 5622864.249668 */
+		5784489,	/* 5784489.488298 */
+		5943398,	/* 5943398.207380 */
+		6099680,	/* 6099680.215452 */
+		6253421,	/* 6253420.939751 */
+		6404702,	/* 6404701.706649 */
+		6553600,	/* 6553600.000000 */
+	};
+
+
+	if (x == 0)
+		return 0;
+
+	/* Scale x (normalize) */
+	/* computing y in log(x/y) = log(x) - log(y) */
+	if ((x & ((0xffffffff) << (scale + 1))) == 0) {
+		for (k = scale; k > 0; k--) {
+			if (x & (((u32) 1) << scale))
+				break;
+			x <<= 1;
+		}
+	} else {
+		for (k = scale; k < 31; k++) {
+			if ((x & (((u32) (-1)) << (scale + 1))) == 0)
+				break;
+			x >>= 1;
+		}
+	}
+	/*
+	   Now x has binary point between bit[scale] and bit[scale-1]
+	   and 1.0 <= x < 2.0 */
+
+	/* correction for divison: log(x) = log(x/y)+log(y) */
+	y = k * ((((u32) 1) << scale) * 200);
+
+	/* remove integer part */
+	x &= ((((u32) 1) << scale) - 1);
+	/* get index */
+	i = (u8) (x >> (scale - indexWidth));
+	/* compute delta (x - a) */
+	d = x & ((((u32) 1) << (scale - indexWidth)) - 1);
+	/* compute log, multiplication (d* (..)) must be within range ! */
+	y += log2lut[i] +
+	    ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth));
+	/* Conver to log10() */
+	y /= 108853;		/* (log2(10) << scale) */
+	r = (y >> 1);
+	/* rounding */
+	if (y & ((u32) 1))
+		r++;
+	return r;
+}
+
+/****************************************************************************/
+/* I2C **********************************************************************/
+/****************************************************************************/
+
+static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+	struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
+				    .buf = val, .len = 1}
+	};
+
+	return i2c_transfer(adapter, msgs, 1);
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+	int status;
+	struct i2c_msg msg = {
+	    .addr = adr, .flags = 0, .buf = data, .len = len };
+
+	dprintk(3, ":");
+	if (debug > 2) {
+		int i;
+		for (i = 0; i < len; i++)
+			printk(KERN_CONT " %02x", data[i]);
+		printk(KERN_CONT "\n");
+	}
+	status = i2c_transfer(adap, &msg, 1);
+	if (status >= 0 && status != 1)
+		status = -EIO;
+
+	if (status < 0)
+		printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr);
+
+	return status;
+}
+
+static int i2c_read(struct i2c_adapter *adap,
+		    u8 adr, u8 *msg, int len, u8 *answ, int alen)
+{
+	int status;
+	struct i2c_msg msgs[2] = {
+		{.addr = adr, .flags = 0,
+				    .buf = msg, .len = len},
+		{.addr = adr, .flags = I2C_M_RD,
+		 .buf = answ, .len = alen}
+	};
+
+	status = i2c_transfer(adap, msgs, 2);
+	if (status != 2) {
+		if (debug > 2)
+			printk(KERN_CONT ": ERROR!\n");
+		if (status >= 0)
+			status = -EIO;
+
+		printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr);
+		return status;
+	}
+	if (debug > 2) {
+		int i;
+		dprintk(2, ": read from ");
+		for (i = 0; i < len; i++)
+			printk(KERN_CONT " %02x", msg[i]);
+		printk(KERN_CONT "Value = ");
+		for (i = 0; i < alen; i++)
+			printk(KERN_CONT " %02x", answ[i]);
+		printk(KERN_CONT "\n");
+	}
+	return 0;
+}
+
+static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
+{
+	int status;
+	u8 adr = state->demod_address, mm1[4], mm2[2], len;
+
+	if (state->single_master)
+		flags |= 0xC0;
+
+	if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+		mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+		mm1[1] = ((reg >> 16) & 0xFF);
+		mm1[2] = ((reg >> 24) & 0xFF) | flags;
+		mm1[3] = ((reg >> 7) & 0xFF);
+		len = 4;
+	} else {
+		mm1[0] = ((reg << 1) & 0xFF);
+		mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+		len = 2;
+	}
+	dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+	status = i2c_read(state->i2c, adr, mm1, len, mm2, 2);
+	if (status < 0)
+		return status;
+	if (data)
+		*data = mm2[0] | (mm2[1] << 8);
+
+	return 0;
+}
+
+static int read16(struct drxk_state *state, u32 reg, u16 *data)
+{
+	return read16_flags(state, reg, data, 0);
+}
+
+static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
+{
+	int status;
+	u8 adr = state->demod_address, mm1[4], mm2[4], len;
+
+	if (state->single_master)
+		flags |= 0xC0;
+
+	if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+		mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+		mm1[1] = ((reg >> 16) & 0xFF);
+		mm1[2] = ((reg >> 24) & 0xFF) | flags;
+		mm1[3] = ((reg >> 7) & 0xFF);
+		len = 4;
+	} else {
+		mm1[0] = ((reg << 1) & 0xFF);
+		mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+		len = 2;
+	}
+	dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+	status = i2c_read(state->i2c, adr, mm1, len, mm2, 4);
+	if (status < 0)
+		return status;
+	if (data)
+		*data = mm2[0] | (mm2[1] << 8) |
+		    (mm2[2] << 16) | (mm2[3] << 24);
+
+	return 0;
+}
+
+static int read32(struct drxk_state *state, u32 reg, u32 *data)
+{
+	return read32_flags(state, reg, data, 0);
+}
+
+static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
+{
+	u8 adr = state->demod_address, mm[6], len;
+
+	if (state->single_master)
+		flags |= 0xC0;
+	if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+		mm[0] = (((reg << 1) & 0xFF) | 0x01);
+		mm[1] = ((reg >> 16) & 0xFF);
+		mm[2] = ((reg >> 24) & 0xFF) | flags;
+		mm[3] = ((reg >> 7) & 0xFF);
+		len = 4;
+	} else {
+		mm[0] = ((reg << 1) & 0xFF);
+		mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+		len = 2;
+	}
+	mm[len] = data & 0xff;
+	mm[len + 1] = (data >> 8) & 0xff;
+
+	dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
+	return i2c_write(state->i2c, adr, mm, len + 2);
+}
+
+static int write16(struct drxk_state *state, u32 reg, u16 data)
+{
+	return write16_flags(state, reg, data, 0);
+}
+
+static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
+{
+	u8 adr = state->demod_address, mm[8], len;
+
+	if (state->single_master)
+		flags |= 0xC0;
+	if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+		mm[0] = (((reg << 1) & 0xFF) | 0x01);
+		mm[1] = ((reg >> 16) & 0xFF);
+		mm[2] = ((reg >> 24) & 0xFF) | flags;
+		mm[3] = ((reg >> 7) & 0xFF);
+		len = 4;
+	} else {
+		mm[0] = ((reg << 1) & 0xFF);
+		mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+		len = 2;
+	}
+	mm[len] = data & 0xff;
+	mm[len + 1] = (data >> 8) & 0xff;
+	mm[len + 2] = (data >> 16) & 0xff;
+	mm[len + 3] = (data >> 24) & 0xff;
+	dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
+
+	return i2c_write(state->i2c, adr, mm, len + 4);
+}
+
+static int write32(struct drxk_state *state, u32 reg, u32 data)
+{
+	return write32_flags(state, reg, data, 0);
+}
+
+static int write_block(struct drxk_state *state, u32 Address,
+		      const int BlockSize, const u8 pBlock[])
+{
+	int status = 0, BlkSize = BlockSize;
+	u8 Flags = 0;
+
+	if (state->single_master)
+		Flags |= 0xC0;
+
+	while (BlkSize > 0) {
+		int Chunk = BlkSize > state->m_ChunkSize ?
+		    state->m_ChunkSize : BlkSize;
+		u8 *AdrBuf = &state->Chunk[0];
+		u32 AdrLength = 0;
+
+		if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) {
+			AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01);
+			AdrBuf[1] = ((Address >> 16) & 0xFF);
+			AdrBuf[2] = ((Address >> 24) & 0xFF);
+			AdrBuf[3] = ((Address >> 7) & 0xFF);
+			AdrBuf[2] |= Flags;
+			AdrLength = 4;
+			if (Chunk == state->m_ChunkSize)
+				Chunk -= 2;
+		} else {
+			AdrBuf[0] = ((Address << 1) & 0xFF);
+			AdrBuf[1] = (((Address >> 16) & 0x0F) |
+				     ((Address >> 18) & 0xF0));
+			AdrLength = 2;
+		}
+		memcpy(&state->Chunk[AdrLength], pBlock, Chunk);
+		dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags);
+		if (debug > 1) {
+			int i;
+			if (pBlock)
+				for (i = 0; i < Chunk; i++)
+					printk(KERN_CONT " %02x", pBlock[i]);
+			printk(KERN_CONT "\n");
+		}
+		status = i2c_write(state->i2c, state->demod_address,
+				   &state->Chunk[0], Chunk + AdrLength);
+		if (status < 0) {
+			printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
+			       __func__, Address);
+			break;
+		}
+		pBlock += Chunk;
+		Address += (Chunk >> 1);
+		BlkSize -= Chunk;
+	}
+	return status;
+}
+
+#ifndef DRXK_MAX_RETRIES_POWERUP
+#define DRXK_MAX_RETRIES_POWERUP 20
+#endif
+
+int PowerUpDevice(struct drxk_state *state)
+{
+	int status;
+	u8 data = 0;
+	u16 retryCount = 0;
+
+	dprintk(1, "\n");
+
+	status = i2c_read1(state->i2c, state->demod_address, &data);
+	if (status < 0) {
+		do {
+			data = 0;
+			status = i2c_write(state->i2c, state->demod_address,
+					   &data, 1);
+			msleep(10);
+			retryCount++;
+			if (status < 0)
+				continue;
+			status = i2c_read1(state->i2c, state->demod_address,
+					   &data);
+		} while (status < 0 &&
+			 (retryCount < DRXK_MAX_RETRIES_POWERUP));
+		if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP)
+			goto error;
+	}
+
+	/* Make sure all clk domains are active */
+	status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+	if (status < 0)
+		goto error;
+	/* Enable pll lock tests */
+	status = write16(state, SIO_CC_PLL_LOCK__A, 1);
+	if (status < 0)
+		goto error;
+
+	state->m_currentPowerMode = DRX_POWER_UP;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+
+static int init_state(struct drxk_state *state)
+{
+	/*
+	 * FIXME: most (all?) of the values bellow should be moved into
+	 * struct drxk_config, as they are probably board-specific
+	 */
+	u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO;
+	u32 ulVSBIfAgcOutputLevel = 0;
+	u32 ulVSBIfAgcMinLevel = 0;
+	u32 ulVSBIfAgcMaxLevel = 0x7FFF;
+	u32 ulVSBIfAgcSpeed = 3;
+
+	u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO;
+	u32 ulVSBRfAgcOutputLevel = 0;
+	u32 ulVSBRfAgcMinLevel = 0;
+	u32 ulVSBRfAgcMaxLevel = 0x7FFF;
+	u32 ulVSBRfAgcSpeed = 3;
+	u32 ulVSBRfAgcTop = 9500;
+	u32 ulVSBRfAgcCutOffCurrent = 4000;
+
+	u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO;
+	u32 ulATVIfAgcOutputLevel = 0;
+	u32 ulATVIfAgcMinLevel = 0;
+	u32 ulATVIfAgcMaxLevel = 0;
+	u32 ulATVIfAgcSpeed = 3;
+
+	u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF;
+	u32 ulATVRfAgcOutputLevel = 0;
+	u32 ulATVRfAgcMinLevel = 0;
+	u32 ulATVRfAgcMaxLevel = 0;
+	u32 ulATVRfAgcTop = 9500;
+	u32 ulATVRfAgcCutOffCurrent = 4000;
+	u32 ulATVRfAgcSpeed = 3;
+
+	u32 ulQual83 = DEFAULT_MER_83;
+	u32 ulQual93 = DEFAULT_MER_93;
+
+	u32 ulDVBTStaticTSClock = 1;
+	u32 ulDVBCStaticTSClock = 1;
+
+	u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+	u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+
+	/* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+	/* io_pad_cfg_mode output mode is drive always */
+	/* io_pad_cfg_drive is set to power 2 (23 mA) */
+	u32 ulGPIOCfg = 0x0113;
+	u32 ulSerialMode = 1;
+	u32 ulInvertTSClock = 0;
+	u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
+	u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH;
+	u32 ulDVBTBitrate = 50000000;
+	u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
+
+	u32 ulInsertRSByte = 0;
+
+	u32 ulRfMirror = 1;
+	u32 ulPowerDown = 0;
+
+	dprintk(1, "\n");
+
+	state->m_hasLNA = false;
+	state->m_hasDVBT = false;
+	state->m_hasDVBC = false;
+	state->m_hasATV = false;
+	state->m_hasOOB = false;
+	state->m_hasAudio = false;
+
+	state->m_ChunkSize = 124;
+
+	state->m_oscClockFreq = 0;
+	state->m_smartAntInverted = false;
+	state->m_bPDownOpenBridge = false;
+
+	/* real system clock frequency in kHz */
+	state->m_sysClockFreq = 151875;
+	/* Timing div, 250ns/Psys */
+	/* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
+	state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) *
+				   HI_I2C_DELAY) / 1000;
+	/* Clipping */
+	if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+		state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+	state->m_HICfgWakeUpKey = (state->demod_address << 1);
+	/* port/bridge/power down ctrl */
+	state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+	state->m_bPowerDown = (ulPowerDown != 0);
+
+	state->m_DRXK_A1_PATCH_CODE = false;
+	state->m_DRXK_A1_ROM_CODE = false;
+	state->m_DRXK_A2_ROM_CODE = false;
+	state->m_DRXK_A3_ROM_CODE = false;
+	state->m_DRXK_A2_PATCH_CODE = false;
+	state->m_DRXK_A3_PATCH_CODE = false;
+
+	/* Init AGC and PGA parameters */
+	/* VSB IF */
+	state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode);
+	state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel);
+	state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel);
+	state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel);
+	state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed);
+	state->m_vsbPgaCfg = 140;
+
+	/* VSB RF */
+	state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode);
+	state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel);
+	state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel);
+	state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel);
+	state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed);
+	state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop);
+	state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent);
+	state->m_vsbPreSawCfg.reference = 0x07;
+	state->m_vsbPreSawCfg.usePreSaw = true;
+
+	state->m_Quality83percent = DEFAULT_MER_83;
+	state->m_Quality93percent = DEFAULT_MER_93;
+	if (ulQual93 <= 500 && ulQual83 < ulQual93) {
+		state->m_Quality83percent = ulQual83;
+		state->m_Quality93percent = ulQual93;
+	}
+
+	/* ATV IF */
+	state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode);
+	state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel);
+	state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel);
+	state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel);
+	state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed);
+
+	/* ATV RF */
+	state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode);
+	state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel);
+	state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel);
+	state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel);
+	state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed);
+	state->m_atvRfAgcCfg.top = (ulATVRfAgcTop);
+	state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent);
+	state->m_atvPreSawCfg.reference = 0x04;
+	state->m_atvPreSawCfg.usePreSaw = true;
+
+
+	/* DVBT RF */
+	state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+	state->m_dvbtRfAgcCfg.outputLevel = 0;
+	state->m_dvbtRfAgcCfg.minOutputLevel = 0;
+	state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF;
+	state->m_dvbtRfAgcCfg.top = 0x2100;
+	state->m_dvbtRfAgcCfg.cutOffCurrent = 4000;
+	state->m_dvbtRfAgcCfg.speed = 1;
+
+
+	/* DVBT IF */
+	state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+	state->m_dvbtIfAgcCfg.outputLevel = 0;
+	state->m_dvbtIfAgcCfg.minOutputLevel = 0;
+	state->m_dvbtIfAgcCfg.maxOutputLevel = 9000;
+	state->m_dvbtIfAgcCfg.top = 13424;
+	state->m_dvbtIfAgcCfg.cutOffCurrent = 0;
+	state->m_dvbtIfAgcCfg.speed = 3;
+	state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30;
+	state->m_dvbtIfAgcCfg.IngainTgtMax = 30000;
+	/* state->m_dvbtPgaCfg = 140; */
+
+	state->m_dvbtPreSawCfg.reference = 4;
+	state->m_dvbtPreSawCfg.usePreSaw = false;
+
+	/* QAM RF */
+	state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+	state->m_qamRfAgcCfg.outputLevel = 0;
+	state->m_qamRfAgcCfg.minOutputLevel = 6023;
+	state->m_qamRfAgcCfg.maxOutputLevel = 27000;
+	state->m_qamRfAgcCfg.top = 0x2380;
+	state->m_qamRfAgcCfg.cutOffCurrent = 4000;
+	state->m_qamRfAgcCfg.speed = 3;
+
+	/* QAM IF */
+	state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+	state->m_qamIfAgcCfg.outputLevel = 0;
+	state->m_qamIfAgcCfg.minOutputLevel = 0;
+	state->m_qamIfAgcCfg.maxOutputLevel = 9000;
+	state->m_qamIfAgcCfg.top = 0x0511;
+	state->m_qamIfAgcCfg.cutOffCurrent = 0;
+	state->m_qamIfAgcCfg.speed = 3;
+	state->m_qamIfAgcCfg.IngainTgtMax = 5119;
+	state->m_qamIfAgcCfg.FastClipCtrlDelay = 50;
+
+	state->m_qamPgaCfg = 140;
+	state->m_qamPreSawCfg.reference = 4;
+	state->m_qamPreSawCfg.usePreSaw = false;
+
+	state->m_OperationMode = OM_NONE;
+	state->m_DrxkState = DRXK_UNINITIALIZED;
+
+	/* MPEG output configuration */
+	state->m_enableMPEGOutput = true;	/* If TRUE; enable MPEG ouput */
+	state->m_insertRSByte = false;	/* If TRUE; insert RS byte */
+	state->m_enableParallel = true;	/* If TRUE;
+					   parallel out otherwise serial */
+	state->m_invertDATA = false;	/* If TRUE; invert DATA signals */
+	state->m_invertERR = false;	/* If TRUE; invert ERR signal */
+	state->m_invertSTR = false;	/* If TRUE; invert STR signals */
+	state->m_invertVAL = false;	/* If TRUE; invert VAL signals */
+	state->m_invertCLK = (ulInvertTSClock != 0);	/* If TRUE; invert CLK signals */
+	state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0);
+	state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0);
+	/* If TRUE; static MPEG clockrate will be used;
+	   otherwise clockrate will adapt to the bitrate of the TS */
+
+	state->m_DVBTBitrate = ulDVBTBitrate;
+	state->m_DVBCBitrate = ulDVBCBitrate;
+
+	state->m_TSDataStrength = (ulTSDataStrength & 0x07);
+	state->m_TSClockkStrength = (ulTSClockkStrength & 0x07);
+
+	/* Maximum bitrate in b/s in case static clockrate is selected */
+	state->m_mpegTsStaticBitrate = 19392658;
+	state->m_disableTEIhandling = false;
+
+	if (ulInsertRSByte)
+		state->m_insertRSByte = true;
+
+	state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+	if (ulMpegLockTimeOut < 10000)
+		state->m_MpegLockTimeOut = ulMpegLockTimeOut;
+	state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+	if (ulDemodLockTimeOut < 10000)
+		state->m_DemodLockTimeOut = ulDemodLockTimeOut;
+
+	/* QAM defaults */
+	state->m_Constellation = DRX_CONSTELLATION_AUTO;
+	state->m_qamInterleaveMode = DRXK_QAM_I12_J17;
+	state->m_fecRsPlen = 204 * 8;	/* fecRsPlen  annex A */
+	state->m_fecRsPrescale = 1;
+
+	state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM;
+	state->m_agcFastClipCtrlDelay = 0;
+
+	state->m_GPIOCfg = (ulGPIOCfg);
+
+	state->m_bPowerDown = false;
+	state->m_currentPowerMode = DRX_POWER_DOWN;
+
+	state->m_enableParallel = (ulSerialMode == 0);
+
+	state->m_rfmirror = (ulRfMirror == 0);
+	state->m_IfAgcPol = false;
+	return 0;
+}
+
+static int DRXX_Open(struct drxk_state *state)
+{
+	int status = 0;
+	u32 jtag = 0;
+	u16 bid = 0;
+	u16 key = 0;
+
+	dprintk(1, "\n");
+	/* stop lock indicator process */
+	status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+	if (status < 0)
+		goto error;
+	/* Check device id */
+	status = read16(state, SIO_TOP_COMM_KEY__A, &key);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+	if (status < 0)
+		goto error;
+	status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag);
+	if (status < 0)
+		goto error;
+	status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_TOP_COMM_KEY__A, key);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int GetDeviceCapabilities(struct drxk_state *state)
+{
+	u16 sioPdrOhwCfg = 0;
+	u32 sioTopJtagidLo = 0;
+	int status;
+	const char *spin = "";
+
+	dprintk(1, "\n");
+
+	/* driver 0.9.0 */
+	/* stop lock indicator process */
+	status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+	if (status < 0)
+		goto error;
+	status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+	if (status < 0)
+		goto error;
+
+	switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+	case 0:
+		/* ignore (bypass ?) */
+		break;
+	case 1:
+		/* 27 MHz */
+		state->m_oscClockFreq = 27000;
+		break;
+	case 2:
+		/* 20.25 MHz */
+		state->m_oscClockFreq = 20250;
+		break;
+	case 3:
+		/* 4 MHz */
+		state->m_oscClockFreq = 20250;
+		break;
+	default:
+		printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n");
+		return -EINVAL;
+	}
+	/*
+		Determine device capabilities
+		Based on pinning v14
+		*/
+	status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo);
+	if (status < 0)
+		goto error;
+	/* driver 0.9.0 */
+	switch ((sioTopJtagidLo >> 29) & 0xF) {
+	case 0:
+		state->m_deviceSpin = DRXK_SPIN_A1;
+		spin = "A1";
+		break;
+	case 2:
+		state->m_deviceSpin = DRXK_SPIN_A2;
+		spin = "A2";
+		break;
+	case 3:
+		state->m_deviceSpin = DRXK_SPIN_A3;
+		spin = "A3";
+		break;
+	default:
+		state->m_deviceSpin = DRXK_SPIN_UNKNOWN;
+		status = -EINVAL;
+		printk(KERN_ERR "drxk: Spin unknown\n");
+		goto error2;
+	}
+	switch ((sioTopJtagidLo >> 12) & 0xFF) {
+	case 0x13:
+		/* typeId = DRX3913K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = false;
+		state->m_hasAudio = false;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = true;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = false;
+		state->m_hasGPIO1 = false;
+		state->m_hasIRQN = false;
+		break;
+	case 0x15:
+		/* typeId = DRX3915K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = false;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = false;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x16:
+		/* typeId = DRX3916K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = false;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = false;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x18:
+		/* typeId = DRX3918K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = true;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = false;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x21:
+		/* typeId = DRX3921K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = true;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = true;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x23:
+		/* typeId = DRX3923K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = true;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = true;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x25:
+		/* typeId = DRX3925K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = true;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = true;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	case 0x26:
+		/* typeId = DRX3926K_TYPE_ID */
+		state->m_hasLNA = false;
+		state->m_hasOOB = false;
+		state->m_hasATV = true;
+		state->m_hasAudio = false;
+		state->m_hasDVBT = true;
+		state->m_hasDVBC = true;
+		state->m_hasSAWSW = true;
+		state->m_hasGPIO2 = true;
+		state->m_hasGPIO1 = true;
+		state->m_hasIRQN = false;
+		break;
+	default:
+		printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n",
+			((sioTopJtagidLo >> 12) & 0xFF));
+		status = -EINVAL;
+		goto error2;
+	}
+
+	printk(KERN_INFO
+	       "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
+	       ((sioTopJtagidLo >> 12) & 0xFF), spin,
+	       state->m_oscClockFreq / 1000,
+	       state->m_oscClockFreq % 1000);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+error2:
+	return status;
+}
+
+static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult)
+{
+	int status;
+	bool powerdown_cmd;
+
+	dprintk(1, "\n");
+
+	/* Write command */
+	status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd);
+	if (status < 0)
+		goto error;
+	if (cmd == SIO_HI_RA_RAM_CMD_RESET)
+		msleep(1);
+
+	powerdown_cmd =
+	    (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+		    ((state->m_HICfgCtrl) &
+		     SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
+		    SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
+	if (powerdown_cmd == false) {
+		/* Wait until command rdy */
+		u32 retryCount = 0;
+		u16 waitCmd;
+
+		do {
+			msleep(1);
+			retryCount += 1;
+			status = read16(state, SIO_HI_RA_RAM_CMD__A,
+					  &waitCmd);
+		} while ((status < 0) && (retryCount < DRXK_MAX_RETRIES)
+			 && (waitCmd != 0));
+		if (status < 0)
+			goto error;
+		status = read16(state, SIO_HI_RA_RAM_RES__A, pResult);
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int HI_CfgCommand(struct drxk_state *state)
+{
+	int status;
+
+	dprintk(1, "\n");
+
+	mutex_lock(&state->mutex);
+
+	status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+	if (status < 0)
+		goto error;
+	status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
+	if (status < 0)
+		goto error;
+
+	state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+error:
+	mutex_unlock(&state->mutex);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int InitHI(struct drxk_state *state)
+{
+	dprintk(1, "\n");
+
+	state->m_HICfgWakeUpKey = (state->demod_address << 1);
+	state->m_HICfgTimeout = 0x96FF;
+	/* port/bridge/power down ctrl */
+	state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+	return HI_CfgCommand(state);
+}
+
+static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
+{
+	int status = -1;
+	u16 sioPdrMclkCfg = 0;
+	u16 sioPdrMdxCfg = 0;
+
+	dprintk(1, "\n");
+
+	/* stop lock indicator process */
+	status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+	if (status < 0)
+		goto error;
+
+	/*  MPEG TS pad configuration */
+	status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+	if (status < 0)
+		goto error;
+
+	if (mpegEnable == false) {
+		/*  Set MPEG TS pads to inputmode */
+		status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+		if (status < 0)
+			goto error;
+	} else {
+		/* Enable MPEG output */
+		sioPdrMdxCfg =
+			((state->m_TSDataStrength <<
+			SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
+		sioPdrMclkCfg = ((state->m_TSClockkStrength <<
+					SIO_PDR_MCLK_CFG_DRIVE__B) |
+					0x0003);
+
+		status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);	/* Disable */
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);	/* Disable */
+		if (status < 0)
+			goto error;
+		if (state->m_enableParallel == true) {
+			/* paralel -> enable MD1 to MD7 */
+			status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg);
+			if (status < 0)
+				goto error;
+		} else {
+			sioPdrMdxCfg = ((state->m_TSDataStrength <<
+						SIO_PDR_MD0_CFG_DRIVE__B)
+					| 0x0003);
+			/* serial -> disable MD1 to MD7 */
+			status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+			status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+			if (status < 0)
+				goto error;
+		}
+		status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg);
+		if (status < 0)
+			goto error;
+	}
+	/*  Enable MB output over MPEG pads and ctl input */
+	status = write16(state, SIO_PDR_MON_CFG__A, 0x0000);
+	if (status < 0)
+		goto error;
+	/*  Write nomagic word to enable pdr reg write */
+	status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int MPEGTSDisable(struct drxk_state *state)
+{
+	dprintk(1, "\n");
+
+	return MPEGTSConfigurePins(state, false);
+}
+
+static int BLChainCmd(struct drxk_state *state,
+		      u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+	u16 blStatus = 0;
+	int status;
+	unsigned long end;
+
+	dprintk(1, "\n");
+	mutex_lock(&state->mutex);
+	status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+	if (status < 0)
+		goto error;
+
+	end = jiffies + msecs_to_jiffies(timeOut);
+	do {
+		msleep(1);
+		status = read16(state, SIO_BL_STATUS__A, &blStatus);
+		if (status < 0)
+			goto error;
+	} while ((blStatus == 0x1) &&
+			((time_is_after_jiffies(end))));
+
+	if (blStatus == 0x1) {
+		printk(KERN_ERR "drxk: SIO not ready\n");
+		status = -EINVAL;
+		goto error2;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+	mutex_unlock(&state->mutex);
+	return status;
+}
+
+
+static int DownloadMicrocode(struct drxk_state *state,
+			     const u8 pMCImage[], u32 Length)
+{
+	const u8 *pSrc = pMCImage;
+	u16 Flags;
+	u16 Drain;
+	u32 Address;
+	u16 nBlocks;
+	u16 BlockSize;
+	u16 BlockCRC;
+	u32 offset = 0;
+	u32 i;
+	int status = 0;
+
+	dprintk(1, "\n");
+
+	/* down the drain (we don care about MAGIC_WORD) */
+	Drain = (pSrc[0] << 8) | pSrc[1];
+	pSrc += sizeof(u16);
+	offset += sizeof(u16);
+	nBlocks = (pSrc[0] << 8) | pSrc[1];
+	pSrc += sizeof(u16);
+	offset += sizeof(u16);
+
+	for (i = 0; i < nBlocks; i += 1) {
+		Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
+		    (pSrc[2] << 8) | pSrc[3];
+		pSrc += sizeof(u32);
+		offset += sizeof(u32);
+
+		BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
+		pSrc += sizeof(u16);
+		offset += sizeof(u16);
+
+		Flags = (pSrc[0] << 8) | pSrc[1];
+		pSrc += sizeof(u16);
+		offset += sizeof(u16);
+
+		BlockCRC = (pSrc[0] << 8) | pSrc[1];
+		pSrc += sizeof(u16);
+		offset += sizeof(u16);
+
+		if (offset + BlockSize > Length) {
+			printk(KERN_ERR "drxk: Firmware is corrupted.\n");
+			return -EINVAL;
+		}
+
+		status = write_block(state, Address, BlockSize, pSrc);
+		if (status < 0) {
+			printk(KERN_ERR "drxk: Error %d while loading firmware\n", status);
+			break;
+		}
+		pSrc += BlockSize;
+		offset += BlockSize;
+	}
+	return status;
+}
+
+static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable)
+{
+	int status;
+	u16 data = 0;
+	u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
+	u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
+	unsigned long end;
+
+	dprintk(1, "\n");
+
+	if (enable == false) {
+		desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
+		desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
+	}
+
+	status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+	if (status >= 0 && data == desiredStatus) {
+		/* tokenring already has correct status */
+		return status;
+	}
+	/* Disable/enable dvbt tokenring bridge   */
+	status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl);
+
+	end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
+	do {
+		status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+		if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end))
+			break;
+		msleep(1);
+	} while (1);
+	if (data != desiredStatus) {
+		printk(KERN_ERR "drxk: SIO not ready\n");
+		return -EINVAL;
+	}
+	return status;
+}
+
+static int MPEGTSStop(struct drxk_state *state)
+{
+	int status = 0;
+	u16 fecOcSncMode = 0;
+	u16 fecOcIprMode = 0;
+
+	dprintk(1, "\n");
+
+	/* Gracefull shutdown (byte boundaries) */
+	status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+	if (status < 0)
+		goto error;
+	fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
+	status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+	if (status < 0)
+		goto error;
+
+	/* Suppress MCLK during absence of data */
+	status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode);
+	if (status < 0)
+		goto error;
+	fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
+	status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int scu_command(struct drxk_state *state,
+		       u16 cmd, u8 parameterLen,
+		       u16 *parameter, u8 resultLen, u16 *result)
+{
+#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
+#error DRXK register mapping no longer compatible with this routine!
+#endif
+	u16 curCmd = 0;
+	int status = -EINVAL;
+	unsigned long end;
+	u8 buffer[34];
+	int cnt = 0, ii;
+	const char *p;
+	char errname[30];
+
+	dprintk(1, "\n");
+
+	if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) ||
+	    ((resultLen > 0) && (result == NULL)))
+		goto error;
+
+	mutex_lock(&state->mutex);
+
+	/* assume that the command register is ready
+		since it is checked afterwards */
+	for (ii = parameterLen - 1; ii >= 0; ii -= 1) {
+		buffer[cnt++] = (parameter[ii] & 0xFF);
+		buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
+	}
+	buffer[cnt++] = (cmd & 0xFF);
+	buffer[cnt++] = ((cmd >> 8) & 0xFF);
+
+	write_block(state, SCU_RAM_PARAM_0__A -
+			(parameterLen - 1), cnt, buffer);
+	/* Wait until SCU has processed command */
+	end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
+	do {
+		msleep(1);
+		status = read16(state, SCU_RAM_COMMAND__A, &curCmd);
+		if (status < 0)
+			goto error;
+	} while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
+	if (curCmd != DRX_SCU_READY) {
+		printk(KERN_ERR "drxk: SCU not ready\n");
+		status = -EIO;
+		goto error2;
+	}
+	/* read results */
+	if ((resultLen > 0) && (result != NULL)) {
+		s16 err;
+		int ii;
+
+		for (ii = resultLen - 1; ii >= 0; ii -= 1) {
+			status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]);
+			if (status < 0)
+				goto error;
+		}
+
+		/* Check if an error was reported by SCU */
+		err = (s16)result[0];
+		if (err >= 0)
+			goto error;
+
+		/* check for the known error codes */
+		switch (err) {
+		case SCU_RESULT_UNKCMD:
+			p = "SCU_RESULT_UNKCMD";
+			break;
+		case SCU_RESULT_UNKSTD:
+			p = "SCU_RESULT_UNKSTD";
+			break;
+		case SCU_RESULT_SIZE:
+			p = "SCU_RESULT_SIZE";
+			break;
+		case SCU_RESULT_INVPAR:
+			p = "SCU_RESULT_INVPAR";
+			break;
+		default: /* Other negative values are errors */
+			sprintf(errname, "ERROR: %d\n", err);
+			p = errname;
+		}
+		printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd);
+		print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
+		status = -EINVAL;
+		goto error2;
+	}
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+	mutex_unlock(&state->mutex);
+	return status;
+}
+
+static int SetIqmAf(struct drxk_state *state, bool active)
+{
+	u16 data = 0;
+	int status;
+
+	dprintk(1, "\n");
+
+	/* Configure IQM */
+	status = read16(state, IQM_AF_STDBY__A, &data);
+	if (status < 0)
+		goto error;
+
+	if (!active) {
+		data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY
+				| IQM_AF_STDBY_STDBY_AMP_STANDBY
+				| IQM_AF_STDBY_STDBY_PD_STANDBY
+				| IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY
+				| IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY);
+	} else {
+		data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY)
+				& (~IQM_AF_STDBY_STDBY_AMP_STANDBY)
+				& (~IQM_AF_STDBY_STDBY_PD_STANDBY)
+				& (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY)
+				& (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY)
+			);
+	}
+	status = write16(state, IQM_AF_STDBY__A, data);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode)
+{
+	int status = 0;
+	u16 sioCcPwdMode = 0;
+
+	dprintk(1, "\n");
+
+	/* Check arguments */
+	if (mode == NULL)
+		return -EINVAL;
+
+	switch (*mode) {
+	case DRX_POWER_UP:
+		sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE;
+		break;
+	case DRXK_POWER_DOWN_OFDM:
+		sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM;
+		break;
+	case DRXK_POWER_DOWN_CORE:
+		sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+		break;
+	case DRXK_POWER_DOWN_PLL:
+		sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL;
+		break;
+	case DRX_POWER_DOWN:
+		sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC;
+		break;
+	default:
+		/* Unknow sleep mode */
+		return -EINVAL;
+	}
+
+	/* If already in requested power mode, do nothing */
+	if (state->m_currentPowerMode == *mode)
+		return 0;
+
+	/* For next steps make sure to start from DRX_POWER_UP mode */
+	if (state->m_currentPowerMode != DRX_POWER_UP) {
+		status = PowerUpDevice(state);
+		if (status < 0)
+			goto error;
+		status = DVBTEnableOFDMTokenRing(state, true);
+		if (status < 0)
+			goto error;
+	}
+
+	if (*mode == DRX_POWER_UP) {
+		/* Restore analog & pin configuartion */
+	} else {
+		/* Power down to requested mode */
+		/* Backup some register settings */
+		/* Set pins with possible pull-ups connected
+		   to them in input mode */
+		/* Analog power down */
+		/* ADC power down */
+		/* Power down device */
+		/* stop all comm_exec */
+		/* Stop and power down previous standard */
+		switch (state->m_OperationMode) {
+		case OM_DVBT:
+			status = MPEGTSStop(state);
+			if (status < 0)
+				goto error;
+			status = PowerDownDVBT(state, false);
+			if (status < 0)
+				goto error;
+			break;
+		case OM_QAM_ITU_A:
+		case OM_QAM_ITU_C:
+			status = MPEGTSStop(state);
+			if (status < 0)
+				goto error;
+			status = PowerDownQAM(state);
+			if (status < 0)
+				goto error;
+			break;
+		default:
+			break;
+		}
+		status = DVBTEnableOFDMTokenRing(state, false);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+		if (status < 0)
+			goto error;
+
+		if (*mode != DRXK_POWER_DOWN_OFDM) {
+			state->m_HICfgCtrl |=
+				SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+			status = HI_CfgCommand(state);
+			if (status < 0)
+				goto error;
+		}
+	}
+	state->m_currentPowerMode = *mode;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode)
+{
+	enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+	u16 cmdResult = 0;
+	u16 data = 0;
+	int status;
+
+	dprintk(1, "\n");
+
+	status = read16(state, SCU_COMM_EXEC__A, &data);
+	if (status < 0)
+		goto error;
+	if (data == SCU_COMM_EXEC_ACTIVE) {
+		/* Send OFDM stop command */
+		status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+		if (status < 0)
+			goto error;
+		/* Send OFDM reset command */
+		status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+		if (status < 0)
+			goto error;
+	}
+
+	/* Reset datapath for OFDM, processors first */
+	status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+	if (status < 0)
+		goto error;
+
+	/* powerdown AFE                   */
+	status = SetIqmAf(state, false);
+	if (status < 0)
+		goto error;
+
+	/* powerdown to OFDM mode          */
+	if (setPowerMode) {
+		status = CtrlPowerMode(state, &powerMode);
+		if (status < 0)
+			goto error;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SetOperationMode(struct drxk_state *state,
+			    enum OperationMode oMode)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+	/*
+	   Stop and power down previous standard
+	   TODO investigate total power down instead of partial
+	   power down depending on "previous" standard.
+	 */
+
+	/* disable HW lock indicator */
+	status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+	if (status < 0)
+		goto error;
+
+	/* Device is already at the required mode */
+	if (state->m_OperationMode == oMode)
+		return 0;
+
+	switch (state->m_OperationMode) {
+		/* OM_NONE was added for start up */
+	case OM_NONE:
+		break;
+	case OM_DVBT:
+		status = MPEGTSStop(state);
+		if (status < 0)
+			goto error;
+		status = PowerDownDVBT(state, true);
+		if (status < 0)
+			goto error;
+		state->m_OperationMode = OM_NONE;
+		break;
+	case OM_QAM_ITU_A:	/* fallthrough */
+	case OM_QAM_ITU_C:
+		status = MPEGTSStop(state);
+		if (status < 0)
+			goto error;
+		status = PowerDownQAM(state);
+		if (status < 0)
+			goto error;
+		state->m_OperationMode = OM_NONE;
+		break;
+	case OM_QAM_ITU_B:
+	default:
+		status = -EINVAL;
+		goto error;
+	}
+
+	/*
+		Power up new standard
+		*/
+	switch (oMode) {
+	case OM_DVBT:
+		state->m_OperationMode = oMode;
+		status = SetDVBTStandard(state, oMode);
+		if (status < 0)
+			goto error;
+		break;
+	case OM_QAM_ITU_A:	/* fallthrough */
+	case OM_QAM_ITU_C:
+		state->m_OperationMode = oMode;
+		status = SetQAMStandard(state, oMode);
+		if (status < 0)
+			goto error;
+		break;
+	case OM_QAM_ITU_B:
+	default:
+		status = -EINVAL;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int Start(struct drxk_state *state, s32 offsetFreq,
+		 s32 IntermediateFrequency)
+{
+	int status = -EINVAL;
+
+	u16 IFreqkHz;
+	s32 OffsetkHz = offsetFreq / 1000;
+
+	dprintk(1, "\n");
+	if (state->m_DrxkState != DRXK_STOPPED &&
+		state->m_DrxkState != DRXK_DTV_STARTED)
+		goto error;
+
+	state->m_bMirrorFreqSpect = (state->param.inversion == INVERSION_ON);
+
+	if (IntermediateFrequency < 0) {
+		state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect;
+		IntermediateFrequency = -IntermediateFrequency;
+	}
+
+	switch (state->m_OperationMode) {
+	case OM_QAM_ITU_A:
+	case OM_QAM_ITU_C:
+		IFreqkHz = (IntermediateFrequency / 1000);
+		status = SetQAM(state, IFreqkHz, OffsetkHz);
+		if (status < 0)
+			goto error;
+		state->m_DrxkState = DRXK_DTV_STARTED;
+		break;
+	case OM_DVBT:
+		IFreqkHz = (IntermediateFrequency / 1000);
+		status = MPEGTSStop(state);
+		if (status < 0)
+			goto error;
+		status = SetDVBT(state, IFreqkHz, OffsetkHz);
+		if (status < 0)
+			goto error;
+		status = DVBTStart(state);
+		if (status < 0)
+			goto error;
+		state->m_DrxkState = DRXK_DTV_STARTED;
+		break;
+	default:
+		break;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int ShutDown(struct drxk_state *state)
+{
+	dprintk(1, "\n");
+
+	MPEGTSStop(state);
+	return 0;
+}
+
+static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus,
+			 u32 Time)
+{
+	int status = -EINVAL;
+
+	dprintk(1, "\n");
+
+	if (pLockStatus == NULL)
+		goto error;
+
+	*pLockStatus = NOT_LOCKED;
+
+	/* define the SCU command code */
+	switch (state->m_OperationMode) {
+	case OM_QAM_ITU_A:
+	case OM_QAM_ITU_B:
+	case OM_QAM_ITU_C:
+		status = GetQAMLockStatus(state, pLockStatus);
+		break;
+	case OM_DVBT:
+		status = GetDVBTLockStatus(state, pLockStatus);
+		break;
+	default:
+		break;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int MPEGTSStart(struct drxk_state *state)
+{
+	int status;
+
+	u16 fecOcSncMode = 0;
+
+	/* Allow OC to sync again */
+	status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+	if (status < 0)
+		goto error;
+	fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
+	status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int MPEGTSDtoInit(struct drxk_state *state)
+{
+	int status;
+
+	dprintk(1, "\n");
+
+	/* Rate integration settings */
+	status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4);
+	if (status < 0)
+		goto error;
+
+	/* Additional configuration */
+	status = write16(state, FEC_OC_OCR_INVERT__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_SNC_LWM__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_SNC_HWM__A, 12);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int MPEGTSDtoSetup(struct drxk_state *state,
+			  enum OperationMode oMode)
+{
+	int status;
+
+	u16 fecOcRegMode = 0;	/* FEC_OC_MODE       register value */
+	u16 fecOcRegIprMode = 0;	/* FEC_OC_IPR_MODE   register value */
+	u16 fecOcDtoMode = 0;	/* FEC_OC_IPR_INVERT register value */
+	u16 fecOcFctMode = 0;	/* FEC_OC_IPR_INVERT register value */
+	u16 fecOcDtoPeriod = 2;	/* FEC_OC_IPR_INVERT register value */
+	u16 fecOcDtoBurstLen = 188;	/* FEC_OC_IPR_INVERT register value */
+	u32 fecOcRcnCtlRate = 0;	/* FEC_OC_IPR_INVERT register value */
+	u16 fecOcTmdMode = 0;
+	u16 fecOcTmdIntUpdRate = 0;
+	u32 maxBitRate = 0;
+	bool staticCLK = false;
+
+	dprintk(1, "\n");
+
+	/* Check insertion of the Reed-Solomon parity bytes */
+	status = read16(state, FEC_OC_MODE__A, &fecOcRegMode);
+	if (status < 0)
+		goto error;
+	status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode);
+	if (status < 0)
+		goto error;
+	fecOcRegMode &= (~FEC_OC_MODE_PARITY__M);
+	fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+	if (state->m_insertRSByte == true) {
+		/* enable parity symbol forward */
+		fecOcRegMode |= FEC_OC_MODE_PARITY__M;
+		/* MVAL disable during parity bytes */
+		fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+		/* TS burst length to 204 */
+		fecOcDtoBurstLen = 204;
+	}
+
+	/* Check serial or parrallel output */
+	fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+	if (state->m_enableParallel == false) {
+		/* MPEG data output is serial -> set ipr_mode[0] */
+		fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M;
+	}
+
+	switch (oMode) {
+	case OM_DVBT:
+		maxBitRate = state->m_DVBTBitrate;
+		fecOcTmdMode = 3;
+		fecOcRcnCtlRate = 0xC00000;
+		staticCLK = state->m_DVBTStaticCLK;
+		break;
+	case OM_QAM_ITU_A:	/* fallthrough */
+	case OM_QAM_ITU_C:
+		fecOcTmdMode = 0x0004;
+		fecOcRcnCtlRate = 0xD2B4EE;	/* good for >63 Mb/s */
+		maxBitRate = state->m_DVBCBitrate;
+		staticCLK = state->m_DVBCStaticCLK;
+		break;
+	default:
+		status = -EINVAL;
+	}		/* switch (standard) */
+	if (status < 0)
+		goto error;
+
+	/* Configure DTO's */
+	if (staticCLK) {
+		u32 bitRate = 0;
+
+		/* Rational DTO for MCLK source (static MCLK rate),
+			Dynamic DTO for optimal grouping
+			(avoid intra-packet gaps),
+			DTO offset enable to sync TS burst with MSTRT */
+		fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M |
+				FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
+		fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M |
+				FEC_OC_FCT_MODE_VIRT_ENA__M);
+
+		/* Check user defined bitrate */
+		bitRate = maxBitRate;
+		if (bitRate > 75900000UL) {	/* max is 75.9 Mb/s */
+			bitRate = 75900000UL;
+		}
+		/* Rational DTO period:
+			dto_period = (Fsys / bitrate) - 2
+
+			Result should be floored,
+			to make sure >= requested bitrate
+			*/
+		fecOcDtoPeriod = (u16) (((state->m_sysClockFreq)
+						* 1000) / bitRate);
+		if (fecOcDtoPeriod <= 2)
+			fecOcDtoPeriod = 0;
+		else
+			fecOcDtoPeriod -= 2;
+		fecOcTmdIntUpdRate = 8;
+	} else {
+		/* (commonAttr->staticCLK == false) => dynamic mode */
+		fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M;
+		fecOcFctMode = FEC_OC_FCT_MODE__PRE;
+		fecOcTmdIntUpdRate = 5;
+	}
+
+	/* Write appropriate registers with requested configuration */
+	status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_MODE__A, fecOcRegMode);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode);
+	if (status < 0)
+		goto error;
+
+	/* Rate integration settings */
+	status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int MPEGTSConfigurePolarity(struct drxk_state *state)
+{
+	u16 fecOcRegIprInvert = 0;
+
+	/* Data mask for the output data byte */
+	u16 InvertDataMask =
+	    FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+	    FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+	    FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+	    FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+	dprintk(1, "\n");
+
+	/* Control selective inversion of output bits */
+	fecOcRegIprInvert &= (~(InvertDataMask));
+	if (state->m_invertDATA == true)
+		fecOcRegIprInvert |= InvertDataMask;
+	fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+	if (state->m_invertERR == true)
+		fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M;
+	fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+	if (state->m_invertSTR == true)
+		fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M;
+	fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+	if (state->m_invertVAL == true)
+		fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M;
+	fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+	if (state->m_invertCLK == true)
+		fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M;
+
+	return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert);
+}
+
+#define   SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+
+static int SetAgcRf(struct drxk_state *state,
+		    struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+	int status = -EINVAL;
+	u16 data = 0;
+	struct SCfgAgc *pIfAgcSettings;
+
+	dprintk(1, "\n");
+
+	if (pAgcCfg == NULL)
+		goto error;
+
+	switch (pAgcCfg->ctrlMode) {
+	case DRXK_AGC_CTRL_AUTO:
+		/* Enable RF AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+
+		/* Enable SCU RF AGC loop */
+		data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+
+		/* Polarity */
+		if (state->m_RfAgcPol)
+			data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+		else
+			data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Set speed (using complementary reduction value) */
+		status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+		if (status < 0)
+			goto error;
+
+		data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+		data |= (~(pAgcCfg->speed <<
+				SCU_RAM_AGC_KI_RED_RAGC_RED__B)
+				& SCU_RAM_AGC_KI_RED_RAGC_RED__M);
+
+		status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+		if (status < 0)
+			goto error;
+
+		if (IsDVBT(state))
+			pIfAgcSettings = &state->m_dvbtIfAgcCfg;
+		else if (IsQAM(state))
+			pIfAgcSettings = &state->m_qamIfAgcCfg;
+		else
+			pIfAgcSettings = &state->m_atvIfAgcCfg;
+		if (pIfAgcSettings == NULL) {
+			status = -EINVAL;
+			goto error;
+		}
+
+		/* Set TOP, only if IF-AGC is in AUTO mode */
+		if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO)
+			status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top);
+			if (status < 0)
+				goto error;
+
+		/* Cut-Off current */
+		status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent);
+		if (status < 0)
+			goto error;
+
+		/* Max. output level */
+		status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel);
+		if (status < 0)
+			goto error;
+
+		break;
+
+	case DRXK_AGC_CTRL_USER:
+		/* Enable RF AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Disable SCU RF AGC loop */
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+		data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+		if (state->m_RfAgcPol)
+			data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+		else
+			data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+
+		/* SCU c.o.c. to 0, enabling full control range */
+		status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0);
+		if (status < 0)
+			goto error;
+
+		/* Write value to output pin */
+		status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel);
+		if (status < 0)
+			goto error;
+		break;
+
+	case DRXK_AGC_CTRL_OFF:
+		/* Disable RF AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Disable SCU RF AGC loop */
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+		data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+		break;
+
+	default:
+		status = -EINVAL;
+
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+
+static int SetAgcIf(struct drxk_state *state,
+		    struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+	u16 data = 0;
+	int status = 0;
+	struct SCfgAgc *pRfAgcSettings;
+
+	dprintk(1, "\n");
+
+	switch (pAgcCfg->ctrlMode) {
+	case DRXK_AGC_CTRL_AUTO:
+
+		/* Enable IF AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+
+		/* Enable SCU IF AGC loop */
+		data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+		/* Polarity */
+		if (state->m_IfAgcPol)
+			data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+		else
+			data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Set speed (using complementary reduction value) */
+		status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+		if (status < 0)
+			goto error;
+		data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+		data |= (~(pAgcCfg->speed <<
+				SCU_RAM_AGC_KI_RED_IAGC_RED__B)
+				& SCU_RAM_AGC_KI_RED_IAGC_RED__M);
+
+		status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+		if (status < 0)
+			goto error;
+
+		if (IsQAM(state))
+			pRfAgcSettings = &state->m_qamRfAgcCfg;
+		else
+			pRfAgcSettings = &state->m_atvRfAgcCfg;
+		if (pRfAgcSettings == NULL)
+			return -1;
+		/* Restore TOP */
+		status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top);
+		if (status < 0)
+			goto error;
+		break;
+
+	case DRXK_AGC_CTRL_USER:
+
+		/* Enable IF AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+
+		/* Disable SCU IF AGC loop */
+		data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+		/* Polarity */
+		if (state->m_IfAgcPol)
+			data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+		else
+			data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Write value to output pin */
+		status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel);
+		if (status < 0)
+			goto error;
+		break;
+
+	case DRXK_AGC_CTRL_OFF:
+
+		/* Disable If AGC DAC */
+		status = read16(state, IQM_AF_STDBY__A, &data);
+		if (status < 0)
+			goto error;
+		data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+		status = write16(state, IQM_AF_STDBY__A, data);
+		if (status < 0)
+			goto error;
+
+		/* Disable SCU IF AGC loop */
+		status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+		if (status < 0)
+			goto error;
+		data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+		status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+		if (status < 0)
+			goto error;
+		break;
+	}		/* switch (agcSettingsIf->ctrlMode) */
+
+	/* always set the top to support
+		configurations without if-loop */
+	status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int ReadIFAgc(struct drxk_state *state, u32 *pValue)
+{
+	u16 agcDacLvl;
+	int status;
+	u16 Level = 0;
+
+	dprintk(1, "\n");
+
+	status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl);
+	if (status < 0) {
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+		return status;
+	}
+
+	*pValue = 0;
+
+	if (agcDacLvl > DRXK_AGC_DAC_OFFSET)
+		Level = agcDacLvl - DRXK_AGC_DAC_OFFSET;
+	if (Level < 14000)
+		*pValue = (14000 - Level) / 4;
+	else
+		*pValue = 0;
+
+	return status;
+}
+
+static int GetQAMSignalToNoise(struct drxk_state *state,
+			       s32 *pSignalToNoise)
+{
+	int status = 0;
+	u16 qamSlErrPower = 0;	/* accum. error between
+					raw and sliced symbols */
+	u32 qamSlSigPower = 0;	/* used for MER, depends of
+					QAM constellation */
+	u32 qamSlMer = 0;	/* QAM MER */
+
+	dprintk(1, "\n");
+
+	/* MER calculation */
+
+	/* get the register value needed for MER */
+	status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower);
+	if (status < 0) {
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+		return -EINVAL;
+	}
+
+	switch (state->param.u.qam.modulation) {
+	case QAM_16:
+		qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
+		break;
+	case QAM_32:
+		qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
+		break;
+	case QAM_64:
+		qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
+		break;
+	case QAM_128:
+		qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
+		break;
+	default:
+	case QAM_256:
+		qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
+		break;
+	}
+
+	if (qamSlErrPower > 0) {
+		qamSlMer = Log10Times100(qamSlSigPower) -
+			Log10Times100((u32) qamSlErrPower);
+	}
+	*pSignalToNoise = qamSlMer;
+
+	return status;
+}
+
+static int GetDVBTSignalToNoise(struct drxk_state *state,
+				s32 *pSignalToNoise)
+{
+	int status;
+	u16 regData = 0;
+	u32 EqRegTdSqrErrI = 0;
+	u32 EqRegTdSqrErrQ = 0;
+	u16 EqRegTdSqrErrExp = 0;
+	u16 EqRegTdTpsPwrOfs = 0;
+	u16 EqRegTdReqSmbCnt = 0;
+	u32 tpsCnt = 0;
+	u32 SqrErrIQ = 0;
+	u32 a = 0;
+	u32 b = 0;
+	u32 c = 0;
+	u32 iMER = 0;
+	u16 transmissionParams = 0;
+
+	dprintk(1, "\n");
+
+	status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs);
+	if (status < 0)
+		goto error;
+	status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt);
+	if (status < 0)
+		goto error;
+	status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp);
+	if (status < 0)
+		goto error;
+	status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, &regData);
+	if (status < 0)
+		goto error;
+	/* Extend SQR_ERR_I operational range */
+	EqRegTdSqrErrI = (u32) regData;
+	if ((EqRegTdSqrErrExp > 11) &&
+		(EqRegTdSqrErrI < 0x00000FFFUL)) {
+		EqRegTdSqrErrI += 0x00010000UL;
+	}
+	status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, &regData);
+	if (status < 0)
+		goto error;
+	/* Extend SQR_ERR_Q operational range */
+	EqRegTdSqrErrQ = (u32) regData;
+	if ((EqRegTdSqrErrExp > 11) &&
+		(EqRegTdSqrErrQ < 0x00000FFFUL))
+		EqRegTdSqrErrQ += 0x00010000UL;
+
+	status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams);
+	if (status < 0)
+		goto error;
+
+	/* Check input data for MER */
+
+	/* MER calculation (in 0.1 dB) without math.h */
+	if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0))
+		iMER = 0;
+	else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) {
+		/* No error at all, this must be the HW reset value
+			* Apparently no first measurement yet
+			* Set MER to 0.0 */
+		iMER = 0;
+	} else {
+		SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) <<
+			EqRegTdSqrErrExp;
+		if ((transmissionParams &
+			OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
+			== OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
+			tpsCnt = 17;
+		else
+			tpsCnt = 68;
+
+		/* IMER = 100 * log10 (x)
+			where x = (EqRegTdTpsPwrOfs^2 *
+			EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ
+
+			=> IMER = a + b -c
+			where a = 100 * log10 (EqRegTdTpsPwrOfs^2)
+			b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt)
+			c = 100 * log10 (SqrErrIQ)
+			*/
+
+		/* log(x) x = 9bits * 9bits->18 bits  */
+		a = Log10Times100(EqRegTdTpsPwrOfs *
+					EqRegTdTpsPwrOfs);
+		/* log(x) x = 16bits * 7bits->23 bits  */
+		b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt);
+		/* log(x) x = (16bits + 16bits) << 15 ->32 bits  */
+		c = Log10Times100(SqrErrIQ);
+
+		iMER = a + b;
+		/* No negative MER, clip to zero */
+		if (iMER > c)
+			iMER -= c;
+		else
+			iMER = 0;
+	}
+	*pSignalToNoise = iMER;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise)
+{
+	dprintk(1, "\n");
+
+	*pSignalToNoise = 0;
+	switch (state->m_OperationMode) {
+	case OM_DVBT:
+		return GetDVBTSignalToNoise(state, pSignalToNoise);
+	case OM_QAM_ITU_A:
+	case OM_QAM_ITU_C:
+		return GetQAMSignalToNoise(state, pSignalToNoise);
+	default:
+		break;
+	}
+	return 0;
+}
+
+#if 0
+static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality)
+{
+	/* SNR Values for quasi errorfree reception rom Nordig 2.2 */
+	int status = 0;
+
+	dprintk(1, "\n");
+
+	static s32 QE_SN[] = {
+		51,		/* QPSK 1/2 */
+		69,		/* QPSK 2/3 */
+		79,		/* QPSK 3/4 */
+		89,		/* QPSK 5/6 */
+		97,		/* QPSK 7/8 */
+		108,		/* 16-QAM 1/2 */
+		131,		/* 16-QAM 2/3 */
+		146,		/* 16-QAM 3/4 */
+		156,		/* 16-QAM 5/6 */
+		160,		/* 16-QAM 7/8 */
+		165,		/* 64-QAM 1/2 */
+		187,		/* 64-QAM 2/3 */
+		202,		/* 64-QAM 3/4 */
+		216,		/* 64-QAM 5/6 */
+		225,		/* 64-QAM 7/8 */
+	};
+
+	*pQuality = 0;
+
+	do {
+		s32 SignalToNoise = 0;
+		u16 Constellation = 0;
+		u16 CodeRate = 0;
+		u32 SignalToNoiseRel;
+		u32 BERQuality;
+
+		status = GetDVBTSignalToNoise(state, &SignalToNoise);
+		if (status < 0)
+			break;
+		status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation);
+		if (status < 0)
+			break;
+		Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
+
+		status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate);
+		if (status < 0)
+			break;
+		CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
+
+		if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
+		    CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
+			break;
+		SignalToNoiseRel = SignalToNoise -
+		    QE_SN[Constellation * 5 + CodeRate];
+		BERQuality = 100;
+
+		if (SignalToNoiseRel < -70)
+			*pQuality = 0;
+		else if (SignalToNoiseRel < 30)
+			*pQuality = ((SignalToNoiseRel + 70) *
+				     BERQuality) / 100;
+		else
+			*pQuality = BERQuality;
+	} while (0);
+	return 0;
+};
+
+static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality)
+{
+	int status = 0;
+	*pQuality = 0;
+
+	dprintk(1, "\n");
+
+	do {
+		u32 SignalToNoise = 0;
+		u32 BERQuality = 100;
+		u32 SignalToNoiseRel = 0;
+
+		status = GetQAMSignalToNoise(state, &SignalToNoise);
+		if (status < 0)
+			break;
+
+		switch (state->param.u.qam.modulation) {
+		case QAM_16:
+			SignalToNoiseRel = SignalToNoise - 200;
+			break;
+		case QAM_32:
+			SignalToNoiseRel = SignalToNoise - 230;
+			break;	/* Not in NorDig */
+		case QAM_64:
+			SignalToNoiseRel = SignalToNoise - 260;
+			break;
+		case QAM_128:
+			SignalToNoiseRel = SignalToNoise - 290;
+			break;
+		default:
+		case QAM_256:
+			SignalToNoiseRel = SignalToNoise - 320;
+			break;
+		}
+
+		if (SignalToNoiseRel < -70)
+			*pQuality = 0;
+		else if (SignalToNoiseRel < 30)
+			*pQuality = ((SignalToNoiseRel + 70) *
+				     BERQuality) / 100;
+		else
+			*pQuality = BERQuality;
+	} while (0);
+
+	return status;
+}
+
+static int GetQuality(struct drxk_state *state, s32 *pQuality)
+{
+	dprintk(1, "\n");
+
+	switch (state->m_OperationMode) {
+	case OM_DVBT:
+		return GetDVBTQuality(state, pQuality);
+	case OM_QAM_ITU_A:
+		return GetDVBCQuality(state, pQuality);
+	default:
+		break;
+	}
+
+	return 0;
+}
+#endif
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A   0x420060
+
+#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXK_HI_ATOMIC_BUF_END   (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXK_HI_ATOMIC_READ      SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXK_HI_ATOMIC_WRITE     SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr)  (((addr) >> 22) & 0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr)   (((addr) >> 16) & 0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
+
+static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
+{
+	int status = -EINVAL;
+
+	dprintk(1, "\n");
+
+	if (state->m_DrxkState == DRXK_UNINITIALIZED)
+		goto error;
+	if (state->m_DrxkState == DRXK_POWERED_DOWN)
+		goto error;
+
+	if (state->no_i2c_bridge)
+		return 0;
+
+	status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+	if (status < 0)
+		goto error;
+	if (bEnableBridge) {
+		status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
+		if (status < 0)
+			goto error;
+	} else {
+		status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
+		if (status < 0)
+			goto error;
+	}
+
+	status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SetPreSaw(struct drxk_state *state,
+		     struct SCfgPreSaw *pPreSawCfg)
+{
+	int status = -EINVAL;
+
+	dprintk(1, "\n");
+
+	if ((pPreSawCfg == NULL)
+	    || (pPreSawCfg->reference > IQM_AF_PDREF__M))
+		goto error;
+
+	status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int BLDirectCmd(struct drxk_state *state, u32 targetAddr,
+		       u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+	u16 blStatus = 0;
+	u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF);
+	u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF);
+	int status;
+	unsigned long end;
+
+	dprintk(1, "\n");
+
+	mutex_lock(&state->mutex);
+	status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_TGT_HDR__A, blockbank);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_TGT_ADDR__A, offset);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_SRC_ADDR__A, romOffset);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+	if (status < 0)
+		goto error;
+
+	end = jiffies + msecs_to_jiffies(timeOut);
+	do {
+		status = read16(state, SIO_BL_STATUS__A, &blStatus);
+		if (status < 0)
+			goto error;
+	} while ((blStatus == 0x1) && time_is_after_jiffies(end));
+	if (blStatus == 0x1) {
+		printk(KERN_ERR "drxk: SIO not ready\n");
+		status = -EINVAL;
+		goto error2;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+	mutex_unlock(&state->mutex);
+	return status;
+
+}
+
+static int ADCSyncMeasurement(struct drxk_state *state, u16 *count)
+{
+	u16 data = 0;
+	int status;
+
+	dprintk(1, "\n");
+
+	/* Start measurement */
+	status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_START_LOCK__A, 1);
+	if (status < 0)
+		goto error;
+
+	*count = 0;
+	status = read16(state, IQM_AF_PHASE0__A, &data);
+	if (status < 0)
+		goto error;
+	if (data == 127)
+		*count = *count + 1;
+	status = read16(state, IQM_AF_PHASE1__A, &data);
+	if (status < 0)
+		goto error;
+	if (data == 127)
+		*count = *count + 1;
+	status = read16(state, IQM_AF_PHASE2__A, &data);
+	if (status < 0)
+		goto error;
+	if (data == 127)
+		*count = *count + 1;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int ADCSynchronization(struct drxk_state *state)
+{
+	u16 count = 0;
+	int status;
+
+	dprintk(1, "\n");
+
+	status = ADCSyncMeasurement(state, &count);
+	if (status < 0)
+		goto error;
+
+	if (count == 1) {
+		/* Try sampling on a diffrent edge */
+		u16 clkNeg = 0;
+
+		status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
+		if (status < 0)
+			goto error;
+		if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+			IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
+			clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+			clkNeg |=
+				IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
+		} else {
+			clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+			clkNeg |=
+				IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
+		}
+		status = write16(state, IQM_AF_CLKNEG__A, clkNeg);
+		if (status < 0)
+			goto error;
+		status = ADCSyncMeasurement(state, &count);
+		if (status < 0)
+			goto error;
+	}
+
+	if (count < 2)
+		status = -EINVAL;
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SetFrequencyShifter(struct drxk_state *state,
+			       u16 intermediateFreqkHz,
+			       s32 tunerFreqOffset, bool isDTV)
+{
+	bool selectPosImage = false;
+	u32 rfFreqResidual = tunerFreqOffset;
+	u32 fmFrequencyShift = 0;
+	bool tunerMirror = !state->m_bMirrorFreqSpect;
+	u32 adcFreq;
+	bool adcFlip;
+	int status;
+	u32 ifFreqActual;
+	u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3);
+	u32 frequencyShift;
+	bool imageToSelect;
+
+	dprintk(1, "\n");
+
+	/*
+	   Program frequency shifter
+	   No need to account for mirroring on RF
+	 */
+	if (isDTV) {
+		if ((state->m_OperationMode == OM_QAM_ITU_A) ||
+		    (state->m_OperationMode == OM_QAM_ITU_C) ||
+		    (state->m_OperationMode == OM_DVBT))
+			selectPosImage = true;
+		else
+			selectPosImage = false;
+	}
+	if (tunerMirror)
+		/* tuner doesn't mirror */
+		ifFreqActual = intermediateFreqkHz +
+		    rfFreqResidual + fmFrequencyShift;
+	else
+		/* tuner mirrors */
+		ifFreqActual = intermediateFreqkHz -
+		    rfFreqResidual - fmFrequencyShift;
+	if (ifFreqActual > samplingFrequency / 2) {
+		/* adc mirrors */
+		adcFreq = samplingFrequency - ifFreqActual;
+		adcFlip = true;
+	} else {
+		/* adc doesn't mirror */
+		adcFreq = ifFreqActual;
+		adcFlip = false;
+	}
+
+	frequencyShift = adcFreq;
+	imageToSelect = state->m_rfmirror ^ tunerMirror ^
+	    adcFlip ^ selectPosImage;
+	state->m_IqmFsRateOfs =
+	    Frac28a((frequencyShift), samplingFrequency);
+
+	if (imageToSelect)
+		state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1;
+
+	/* Program frequency shifter with tuner offset compensation */
+	/* frequencyShift += tunerFreqOffset; TODO */
+	status = write32(state, IQM_FS_RATE_OFS_LO__A,
+			 state->m_IqmFsRateOfs);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int InitAGC(struct drxk_state *state, bool isDTV)
+{
+	u16 ingainTgt = 0;
+	u16 ingainTgtMin = 0;
+	u16 ingainTgtMax = 0;
+	u16 clpCyclen = 0;
+	u16 clpSumMin = 0;
+	u16 clpDirTo = 0;
+	u16 snsSumMin = 0;
+	u16 snsSumMax = 0;
+	u16 clpSumMax = 0;
+	u16 snsDirTo = 0;
+	u16 kiInnergainMin = 0;
+	u16 ifIaccuHiTgt = 0;
+	u16 ifIaccuHiTgtMin = 0;
+	u16 ifIaccuHiTgtMax = 0;
+	u16 data = 0;
+	u16 fastClpCtrlDelay = 0;
+	u16 clpCtrlMode = 0;
+	int status = 0;
+
+	dprintk(1, "\n");
+
+	/* Common settings */
+	snsSumMax = 1023;
+	ifIaccuHiTgtMin = 2047;
+	clpCyclen = 500;
+	clpSumMax = 1023;
+
+	/* AGCInit() not available for DVBT; init done in microcode */
+	if (!IsQAM(state)) {
+		printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode);
+		return -EINVAL;
+	}
+
+	/* FIXME: Analog TV AGC require different settings */
+
+	/* Standard specific settings */
+	clpSumMin = 8;
+	clpDirTo = (u16) -9;
+	clpCtrlMode = 0;
+	snsSumMin = 8;
+	snsDirTo = (u16) -9;
+	kiInnergainMin = (u16) -1030;
+	ifIaccuHiTgtMax = 0x2380;
+	ifIaccuHiTgt = 0x2380;
+	ingainTgtMin = 0x0511;
+	ingainTgt = 0x0511;
+	ingainTgtMax = 5119;
+	fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay;
+
+	status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500);
+	if (status < 0)
+		goto error;
+
+	/* Initialize inner-loop KI gain factors */
+	status = read16(state, SCU_RAM_AGC_KI__A, &data);
+	if (status < 0)
+		goto error;
+
+	data = 0x0657;
+	data &= ~SCU_RAM_AGC_KI_RF__M;
+	data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B);
+	data &= ~SCU_RAM_AGC_KI_IF__M;
+	data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B);
+
+	status = write16(state, SCU_RAM_AGC_KI__A, data);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr)
+{
+	int status;
+
+	dprintk(1, "\n");
+	if (packetErr == NULL)
+		status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
+	else
+		status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int DVBTScCommand(struct drxk_state *state,
+			 u16 cmd, u16 subcmd,
+			 u16 param0, u16 param1, u16 param2,
+			 u16 param3, u16 param4)
+{
+	u16 curCmd = 0;
+	u16 errCode = 0;
+	u16 retryCnt = 0;
+	u16 scExec = 0;
+	int status;
+
+	dprintk(1, "\n");
+	status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec);
+	if (scExec != 1) {
+		/* SC is not running */
+		status = -EINVAL;
+	}
+	if (status < 0)
+		goto error;
+
+	/* Wait until sc is ready to receive command */
+	retryCnt = 0;
+	do {
+		msleep(1);
+		status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+		retryCnt++;
+	} while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+	if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+		goto error;
+
+	/* Write sub-command */
+	switch (cmd) {
+		/* All commands using sub-cmd */
+	case OFDM_SC_RA_RAM_CMD_PROC_START:
+	case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+	case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+		status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd);
+		if (status < 0)
+			goto error;
+		break;
+	default:
+		/* Do nothing */
+		break;
+	}
+
+	/* Write needed parameters and the command */
+	switch (cmd) {
+		/* All commands using 5 parameters */
+		/* All commands using 4 parameters */
+		/* All commands using 3 parameters */
+		/* All commands using 2 parameters */
+	case OFDM_SC_RA_RAM_CMD_PROC_START:
+	case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+	case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+		status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
+		/* All commands using 1 parameters */
+	case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+	case OFDM_SC_RA_RAM_CMD_USER_IO:
+		status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
+		/* All commands using 0 parameters */
+	case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+	case OFDM_SC_RA_RAM_CMD_NULL:
+		/* Write command */
+		status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd);
+		break;
+	default:
+		/* Unknown command */
+		status = -EINVAL;
+	}
+	if (status < 0)
+		goto error;
+
+	/* Wait until sc is ready processing command */
+	retryCnt = 0;
+	do {
+		msleep(1);
+		status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+		retryCnt++;
+	} while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+	if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+		goto error;
+
+	/* Check for illegal cmd */
+	status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode);
+	if (errCode == 0xFFFF) {
+		/* illegal command */
+		status = -EINVAL;
+	}
+	if (status < 0)
+		goto error;
+
+	/* Retreive results parameters from SC */
+	switch (cmd) {
+		/* All commands yielding 5 results */
+		/* All commands yielding 4 results */
+		/* All commands yielding 3 results */
+		/* All commands yielding 2 results */
+		/* All commands yielding 1 result */
+	case OFDM_SC_RA_RAM_CMD_USER_IO:
+	case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+		status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0));
+		/* All commands yielding 0 results */
+	case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+	case OFDM_SC_RA_RAM_CMD_SET_TIMER:
+	case OFDM_SC_RA_RAM_CMD_PROC_START:
+	case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+	case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+	case OFDM_SC_RA_RAM_CMD_NULL:
+		break;
+	default:
+		/* Unknown command */
+		status = -EINVAL;
+		break;
+	}			/* switch (cmd->cmd) */
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int PowerUpDVBT(struct drxk_state *state)
+{
+	enum DRXPowerMode powerMode = DRX_POWER_UP;
+	int status;
+
+	dprintk(1, "\n");
+	status = CtrlPowerMode(state, &powerMode);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled)
+{
+	int status;
+
+	dprintk(1, "\n");
+	if (*enabled == true)
+		status = write16(state, IQM_CF_BYPASSDET__A, 0);
+	else
+		status = write16(state, IQM_CF_BYPASSDET__A, 1);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+#define DEFAULT_FR_THRES_8K     4000
+static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled)
+{
+
+	int status;
+
+	dprintk(1, "\n");
+	if (*enabled == true) {
+		/* write mask to 1 */
+		status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
+				   DEFAULT_FR_THRES_8K);
+	} else {
+		/* write mask to 0 */
+		status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
+	}
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int DVBTCtrlSetEchoThreshold(struct drxk_state *state,
+				    struct DRXKCfgDvbtEchoThres_t *echoThres)
+{
+	u16 data = 0;
+	int status;
+
+	dprintk(1, "\n");
+	status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data);
+	if (status < 0)
+		goto error;
+
+	switch (echoThres->fftMode) {
+	case DRX_FFTMODE_2K:
+		data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
+		data |= ((echoThres->threshold <<
+			OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
+			& (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
+		break;
+	case DRX_FFTMODE_8K:
+		data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
+		data |= ((echoThres->threshold <<
+			OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
+			& (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int DVBTCtrlSetSqiSpeed(struct drxk_state *state,
+			       enum DRXKCfgDvbtSqiSpeed *speed)
+{
+	int status = -EINVAL;
+
+	dprintk(1, "\n");
+
+	switch (*speed) {
+	case DRXK_DVBT_SQI_SPEED_FAST:
+	case DRXK_DVBT_SQI_SPEED_MEDIUM:
+	case DRXK_DVBT_SQI_SPEED_SLOW:
+		break;
+	default:
+		goto error;
+	}
+	status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A,
+			   (u16) *speed);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Activate DVBT specific presets
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* Called in DVBTSetStandard
+*
+*/
+static int DVBTActivatePresets(struct drxk_state *state)
+{
+	int status;
+	bool setincenable = false;
+	bool setfrenable = true;
+
+	struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K };
+	struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K };
+
+	dprintk(1, "\n");
+	status = DVBTCtrlSetIncEnable(state, &setincenable);
+	if (status < 0)
+		goto error;
+	status = DVBTCtrlSetFrEnable(state, &setfrenable);
+	if (status < 0)
+		goto error;
+	status = DVBTCtrlSetEchoThreshold(state, &echoThres2k);
+	if (status < 0)
+		goto error;
+	status = DVBTCtrlSetEchoThreshold(state, &echoThres8k);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Initialize channelswitch-independent settings for DVBT.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* For ROM code channel filter taps are loaded from the bootloader. For microcode
+* the DVB-T taps from the drxk_filters.h are used.
+*/
+static int SetDVBTStandard(struct drxk_state *state,
+			   enum OperationMode oMode)
+{
+	u16 cmdResult = 0;
+	u16 data = 0;
+	int status;
+
+	dprintk(1, "\n");
+
+	PowerUpDVBT(state);
+	/* added antenna switch */
+	SwitchAntennaToDVBT(state);
+	/* send OFDM reset command */
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+	if (status < 0)
+		goto error;
+
+	/* send OFDM setenv command */
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult);
+	if (status < 0)
+		goto error;
+
+	/* reset datapath for OFDM, processors first */
+	status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+	if (status < 0)
+		goto error;
+
+	/* IQM setup */
+	/* synchronize on ofdstate->m_festart */
+	status = write16(state, IQM_AF_UPD_SEL__A, 1);
+	if (status < 0)
+		goto error;
+	/* window size for clipping ADC detection */
+	status = write16(state, IQM_AF_CLP_LEN__A, 0);
+	if (status < 0)
+		goto error;
+	/* window size for for sense pre-SAW detection */
+	status = write16(state, IQM_AF_SNS_LEN__A, 0);
+	if (status < 0)
+		goto error;
+	/* sense threshold for sense pre-SAW detection */
+	status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+	if (status < 0)
+		goto error;
+	status = SetIqmAf(state, true);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_AF_AGC_RF__A, 0);
+	if (status < 0)
+		goto error;
+
+	/* Impulse noise cruncher setup */
+	status = write16(state, IQM_AF_INC_LCT__A, 0);	/* crunch in IQM_CF */
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_DET_LCT__A, 0);	/* detect in IQM_CF */
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_WND_LEN__A, 3);	/* peak detector window length */
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_RC_STRETCH__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_OUT_ENA__A, 0x4);	/* enable output 2 */
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_DS_ENA__A, 0x4);	/* decimate output 2 */
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_SCALE__A, 1600);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_SCALE_SH__A, 0);
+	if (status < 0)
+		goto error;
+
+	/* virtual clipping threshold for clipping ADC detection */
+	status = write16(state, IQM_AF_CLP_TH__A, 448);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_DATATH__A, 495);	/* crunching threshold */
+	if (status < 0)
+		goto error;
+
+	status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_CF_PKDTH__A, 2);	/* peak detector threshold */
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2);
+	if (status < 0)
+		goto error;
+	/* enable power measurement interrupt */
+	status = write16(state, IQM_CF_COMM_INT_MSK__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+	if (status < 0)
+		goto error;
+
+	/* IQM will not be reset from here, sync ADC and update/init AGC */
+	status = ADCSynchronization(state);
+	if (status < 0)
+		goto error;
+	status = SetPreSaw(state, &state->m_dvbtPreSawCfg);
+	if (status < 0)
+		goto error;
+
+	/* Halt SCU to enable safe non-atomic accesses */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+	if (status < 0)
+		goto error;
+
+	status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true);
+	if (status < 0)
+		goto error;
+	status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true);
+	if (status < 0)
+		goto error;
+
+	/* Set Noise Estimation notch width and enable DC fix */
+	status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data);
+	if (status < 0)
+		goto error;
+	data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M;
+	status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data);
+	if (status < 0)
+		goto error;
+
+	/* Activate SCU to enable SCU commands */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+
+	if (!state->m_DRXK_A3_ROM_CODE) {
+		/* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay  */
+		status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay);
+		if (status < 0)
+			goto error;
+	}
+
+	/* OFDM_SC setup */
+#ifdef COMPILE_FOR_NONRT
+	status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2);
+	if (status < 0)
+		goto error;
+#endif
+
+	/* FEC setup */
+	status = write16(state, FEC_DI_INPUT_CTL__A, 1);	/* OFDM input */
+	if (status < 0)
+		goto error;
+
+
+#ifdef COMPILE_FOR_NONRT
+	status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400);
+	if (status < 0)
+		goto error;
+#else
+	status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000);
+	if (status < 0)
+		goto error;
+#endif
+	status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001);
+	if (status < 0)
+		goto error;
+
+	/* Setup MPEG bus */
+	status = MPEGTSDtoSetup(state, OM_DVBT);
+	if (status < 0)
+		goto error;
+	/* Set DVBT Presets */
+	status = DVBTActivatePresets(state);
+	if (status < 0)
+		goto error;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+/**
+* \brief Start dvbt demodulating for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*/
+static int DVBTStart(struct drxk_state *state)
+{
+	u16 param1;
+	int status;
+	/* DRXKOfdmScCmd_t scCmd; */
+
+	dprintk(1, "\n");
+	/* Start correct processes to get in lock */
+	/* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
+	param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
+	status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0);
+	if (status < 0)
+		goto error;
+	/* Start FEC OC */
+	status = MPEGTSStart(state);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Set up dvbt demodulator for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+* // original DVBTSetChannel()
+*/
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+		   s32 tunerFreqOffset)
+{
+	u16 cmdResult = 0;
+	u16 transmissionParams = 0;
+	u16 operationMode = 0;
+	u32 iqmRcRateOfs = 0;
+	u32 bandwidth = 0;
+	u16 param1;
+	int status;
+
+	dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset);
+
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+	if (status < 0)
+		goto error;
+
+	/* Halt SCU to enable safe non-atomic accesses */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+	if (status < 0)
+		goto error;
+
+	/* Stop processors */
+	status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+
+	/* Mandatory fix, always stop CP, required to set spl offset back to
+		hardware default (is set to 0 by ucode during pilot detection */
+	status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+
+	/*== Write channel settings to device =====================================*/
+
+	/* mode */
+	switch (state->param.u.ofdm.transmission_mode) {
+	case TRANSMISSION_MODE_AUTO:
+	default:
+		operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
+		/* fall through , try first guess DRX_FFTMODE_8K */
+	case TRANSMISSION_MODE_8K:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
+		break;
+	case TRANSMISSION_MODE_2K:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
+		break;
+	}
+
+	/* guard */
+	switch (state->param.u.ofdm.guard_interval) {
+	default:
+	case GUARD_INTERVAL_AUTO:
+		operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
+		/* fall through , try first guess DRX_GUARD_1DIV4 */
+	case GUARD_INTERVAL_1_4:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
+		break;
+	case GUARD_INTERVAL_1_32:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
+		break;
+	case GUARD_INTERVAL_1_16:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
+		break;
+	case GUARD_INTERVAL_1_8:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
+		break;
+	}
+
+	/* hierarchy */
+	switch (state->param.u.ofdm.hierarchy_information) {
+	case HIERARCHY_AUTO:
+	case HIERARCHY_NONE:
+	default:
+		operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
+		/* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
+		/* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
+		/* break; */
+	case HIERARCHY_1:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
+		break;
+	case HIERARCHY_2:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
+		break;
+	case HIERARCHY_4:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
+		break;
+	}
+
+
+	/* constellation */
+	switch (state->param.u.ofdm.constellation) {
+	case QAM_AUTO:
+	default:
+		operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
+		/* fall through , try first guess DRX_CONSTELLATION_QAM64 */
+	case QAM_64:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
+		break;
+	case QPSK:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
+		break;
+	case QAM_16:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
+		break;
+	}
+#if 0
+	/* No hierachical channels support in BDA */
+	/* Priority (only for hierarchical channels) */
+	switch (channel->priority) {
+	case DRX_PRIORITY_LOW:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
+		WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+			OFDM_EC_SB_PRIOR_LO);
+		break;
+	case DRX_PRIORITY_HIGH:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+		WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+			OFDM_EC_SB_PRIOR_HI));
+		break;
+	case DRX_PRIORITY_UNKNOWN:	/* fall through */
+	default:
+		status = -EINVAL;
+		goto error;
+	}
+#else
+	/* Set Priorty high */
+	transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+	status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
+	if (status < 0)
+		goto error;
+#endif
+
+	/* coderate */
+	switch (state->param.u.ofdm.code_rate_HP) {
+	case FEC_AUTO:
+	default:
+		operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
+		/* fall through , try first guess DRX_CODERATE_2DIV3 */
+	case FEC_2_3:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
+		break;
+	case FEC_1_2:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
+		break;
+	case FEC_3_4:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
+		break;
+	case FEC_5_6:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
+		break;
+	case FEC_7_8:
+		transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
+		break;
+	}
+
+	/* SAW filter selection: normaly not necesarry, but if wanted
+		the application can select a SAW filter via the driver by using UIOs */
+	/* First determine real bandwidth (Hz) */
+	/* Also set delay for impulse noise cruncher */
+	/* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed
+		by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC
+		functions */
+	switch (state->param.u.ofdm.bandwidth) {
+	case BANDWIDTH_AUTO:
+	case BANDWIDTH_8_MHZ:
+		bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
+		status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052);
+		if (status < 0)
+			goto error;
+		/* cochannel protection for PAL 8 MHz */
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+		if (status < 0)
+			goto error;
+		break;
+	case BANDWIDTH_7_MHZ:
+		bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
+		status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491);
+		if (status < 0)
+			goto error;
+		/* cochannel protection for PAL 7 MHz */
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+		if (status < 0)
+			goto error;
+		break;
+	case BANDWIDTH_6_MHZ:
+		bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
+		status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073);
+		if (status < 0)
+			goto error;
+		/* cochannel protection for NTSC 6 MHz */
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14);
+		if (status < 0)
+			goto error;
+		status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+		if (status < 0)
+			goto error;
+		break;
+	default:
+		status = -EINVAL;
+		goto error;
+	}
+
+	if (iqmRcRateOfs == 0) {
+		/* Now compute IQM_RC_RATE_OFS
+			(((SysFreq/BandWidth)/2)/2) -1) * 2^23)
+			=>
+			((SysFreq / BandWidth) * (2^21)) - (2^23)
+			*/
+		/* (SysFreq / BandWidth) * (2^28)  */
+		/* assert (MAX(sysClk)/MIN(bandwidth) < 16)
+			=> assert(MAX(sysClk) < 16*MIN(bandwidth))
+			=> assert(109714272 > 48000000) = true so Frac 28 can be used  */
+		iqmRcRateOfs = Frac28a((u32)
+					((state->m_sysClockFreq *
+						1000) / 3), bandwidth);
+		/* (SysFreq / BandWidth) * (2^21), rounding before truncating  */
+		if ((iqmRcRateOfs & 0x7fL) >= 0x40)
+			iqmRcRateOfs += 0x80L;
+		iqmRcRateOfs = iqmRcRateOfs >> 7;
+		/* ((SysFreq / BandWidth) * (2^21)) - (2^23)  */
+		iqmRcRateOfs = iqmRcRateOfs - (1 << 23);
+	}
+
+	iqmRcRateOfs &=
+		((((u32) IQM_RC_RATE_OFS_HI__M) <<
+		IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
+	status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs);
+	if (status < 0)
+		goto error;
+
+	/* Bandwidth setting done */
+
+#if 0
+	status = DVBTSetFrequencyShift(demod, channel, tunerOffset);
+	if (status < 0)
+		goto error;
+#endif
+	status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+	if (status < 0)
+		goto error;
+
+	/*== Start SC, write channel settings to SC ===============================*/
+
+	/* Activate SCU to enable SCU commands */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+
+	/* Enable SC after setting all other parameters */
+	status = write16(state, OFDM_SC_COMM_STATE__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, OFDM_SC_COMM_EXEC__A, 1);
+	if (status < 0)
+		goto error;
+
+
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+	if (status < 0)
+		goto error;
+
+	/* Write SC parameter registers, set all AUTO flags in operation mode */
+	param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M |
+			OFDM_SC_RA_RAM_OP_AUTO_GUARD__M |
+			OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
+			OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
+			OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
+	status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
+				0, transmissionParams, param1, 0, 0, 0);
+	if (status < 0)
+		goto error;
+
+	if (!state->m_DRXK_A3_ROM_CODE)
+		status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Retreive lock status .
+* \param demod    Pointer to demodulator instance.
+* \param lockStat Pointer to lock status structure.
+* \return DRXStatus_t.
+*
+*/
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+	int status;
+	const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
+				    OFDM_SC_RA_RAM_LOCK_FEC__M);
+	const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
+	const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
+
+	u16 ScRaRamLock = 0;
+	u16 ScCommExec = 0;
+
+	dprintk(1, "\n");
+
+	*pLockStatus = NOT_LOCKED;
+	/* driver 0.9.0 */
+	/* Check if SC is running */
+	status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec);
+	if (status < 0)
+		goto end;
+	if (ScCommExec == OFDM_SC_COMM_EXEC_STOP)
+		goto end;
+
+	status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock);
+	if (status < 0)
+		goto end;
+
+	if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask)
+		*pLockStatus = MPEG_LOCK;
+	else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask)
+		*pLockStatus = FEC_LOCK;
+	else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask)
+		*pLockStatus = DEMOD_LOCK;
+	else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
+		*pLockStatus = NEVER_LOCK;
+end:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int PowerUpQAM(struct drxk_state *state)
+{
+	enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+	int status;
+
+	dprintk(1, "\n");
+	status = CtrlPowerMode(state, &powerMode);
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+
+/** Power Down QAM */
+static int PowerDownQAM(struct drxk_state *state)
+{
+	u16 data = 0;
+	u16 cmdResult;
+	int status = 0;
+
+	dprintk(1, "\n");
+	status = read16(state, SCU_COMM_EXEC__A, &data);
+	if (status < 0)
+		goto error;
+	if (data == SCU_COMM_EXEC_ACTIVE) {
+		/*
+			STOP demodulator
+			QAM and HW blocks
+			*/
+		/* stop all comstate->m_exec */
+		status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+		if (status < 0)
+			goto error;
+		status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+		if (status < 0)
+			goto error;
+	}
+	/* powerdown AFE                   */
+	status = SetIqmAf(state, false);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Setup of the QAM Measurement intervals for signal quality
+* \param demod instance of demod.
+* \param constellation current constellation.
+* \return DRXStatus_t.
+*
+*  NOTE:
+*  Take into account that for certain settings the errorcounters can overflow.
+*  The implementation does not check this.
+*
+*/
+static int SetQAMMeasurement(struct drxk_state *state,
+			     enum EDrxkConstellation constellation,
+			     u32 symbolRate)
+{
+	u32 fecBitsDesired = 0;	/* BER accounting period */
+	u32 fecRsPeriodTotal = 0;	/* Total period */
+	u16 fecRsPrescale = 0;	/* ReedSolomon Measurement Prescale */
+	u16 fecRsPeriod = 0;	/* Value for corresponding I2C register */
+	int status = 0;
+
+	dprintk(1, "\n");
+
+	fecRsPrescale = 1;
+	/* fecBitsDesired = symbolRate [kHz] *
+		FrameLenght [ms] *
+		(constellation + 1) *
+		SyncLoss (== 1) *
+		ViterbiLoss (==1)
+		*/
+	switch (constellation) {
+	case DRX_CONSTELLATION_QAM16:
+		fecBitsDesired = 4 * symbolRate;
+		break;
+	case DRX_CONSTELLATION_QAM32:
+		fecBitsDesired = 5 * symbolRate;
+		break;
+	case DRX_CONSTELLATION_QAM64:
+		fecBitsDesired = 6 * symbolRate;
+		break;
+	case DRX_CONSTELLATION_QAM128:
+		fecBitsDesired = 7 * symbolRate;
+		break;
+	case DRX_CONSTELLATION_QAM256:
+		fecBitsDesired = 8 * symbolRate;
+		break;
+	default:
+		status = -EINVAL;
+	}
+	if (status < 0)
+		goto error;
+
+	fecBitsDesired /= 1000;	/* symbolRate [Hz] -> symbolRate [kHz]  */
+	fecBitsDesired *= 500;	/* meas. period [ms] */
+
+	/* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
+	/* fecRsPeriodTotal = fecBitsDesired / 1632 */
+	fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1;	/* roughly ceil */
+
+	/* fecRsPeriodTotal =  fecRsPrescale * fecRsPeriod  */
+	fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16);
+	if (fecRsPrescale == 0) {
+		/* Divide by zero (though impossible) */
+		status = -EINVAL;
+		if (status < 0)
+			goto error;
+	}
+	fecRsPeriod =
+		((u16) fecRsPeriodTotal +
+		(fecRsPrescale >> 1)) / fecRsPrescale;
+
+	/* write corresponding registers */
+	status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SetQAM16(struct drxk_state *state)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+	/* QAM Equalizer Setup */
+	/* Equalizer */
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517);
+	if (status < 0)
+		goto error;
+	/* Decision Feedback Equalizer */
+	status = write16(state, QAM_DQ_QUAL_FUN0__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN1__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN2__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN3__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN4__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+	if (status < 0)
+		goto error;
+
+	/* QAM Slicer Settings */
+	status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16);
+	if (status < 0)
+		goto error;
+
+	/* QAM Loop Controller Coeficients */
+	status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM State Machine (FSM) Thresholds */
+
+	status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM FSM Tracking Parameters */
+
+	status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127);
+	if (status < 0)
+		goto error;
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM32(struct drxk_state *state)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+
+	/* QAM Equalizer Setup */
+	/* Equalizer */
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707);
+	if (status < 0)
+		goto error;
+
+	/* Decision Feedback Equalizer */
+	status = write16(state, QAM_DQ_QUAL_FUN0__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN1__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN2__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN3__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+	if (status < 0)
+		goto error;
+
+	/* QAM Slicer Settings */
+
+	status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM Loop Controller Coeficients */
+
+	status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM State Machine (FSM) Thresholds */
+
+	status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM FSM Tracking Parameters */
+
+	status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM64(struct drxk_state *state)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+	/* QAM Equalizer Setup */
+	/* Equalizer */
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609);
+	if (status < 0)
+		goto error;
+
+	/* Decision Feedback Equalizer */
+	status = write16(state, QAM_DQ_QUAL_FUN0__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN1__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN2__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN3__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+	if (status < 0)
+		goto error;
+
+	/* QAM Slicer Settings */
+	status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM Loop Controller Coeficients */
+
+	status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM State Machine (FSM) Thresholds */
+
+	status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM FSM Tracking Parameters */
+
+	status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM128(struct drxk_state *state)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+	/* QAM Equalizer Setup */
+	/* Equalizer */
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238);
+	if (status < 0)
+		goto error;
+
+	/* Decision Feedback Equalizer */
+	status = write16(state, QAM_DQ_QUAL_FUN0__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN1__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN2__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN3__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN4__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM Slicer Settings */
+
+	status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM Loop Controller Coeficients */
+
+	status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM State Machine (FSM) Thresholds */
+
+	status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+	if (status < 0)
+		goto error;
+
+	/* QAM FSM Tracking Parameters */
+
+	status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM256(struct drxk_state *state)
+{
+	int status = 0;
+
+	dprintk(1, "\n");
+	/* QAM Equalizer Setup */
+	/* Equalizer */
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385);
+	if (status < 0)
+		goto error;
+
+	/* Decision Feedback Equalizer */
+	status = write16(state, QAM_DQ_QUAL_FUN0__A, 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN1__A, 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN2__A, 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN3__A, 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN4__A, 6);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+	if (status < 0)
+		goto error;
+
+	/* QAM Slicer Settings */
+
+	status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM Loop Controller Coeficients */
+
+	status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM State Machine (FSM) Thresholds */
+
+	status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+	if (status < 0)
+		goto error;
+
+
+	/* QAM FSM Tracking Parameters */
+
+	status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+
+/*============================================================================*/
+/**
+* \brief Reset QAM block.
+* \param demod:   instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMResetQAM(struct drxk_state *state)
+{
+	int status;
+	u16 cmdResult;
+
+	dprintk(1, "\n");
+	/* Stop QAM comstate->m_exec */
+	status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Set QAM symbolrate.
+* \param demod:   instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMSetSymbolrate(struct drxk_state *state)
+{
+	u32 adcFrequency = 0;
+	u32 symbFreq = 0;
+	u32 iqmRcRate = 0;
+	u16 ratesel = 0;
+	u32 lcSymbRate = 0;
+	int status;
+
+	dprintk(1, "\n");
+	/* Select & calculate correct IQM rate */
+	adcFrequency = (state->m_sysClockFreq * 1000) / 3;
+	ratesel = 0;
+	/* printk(KERN_DEBUG "drxk: SR %d\n", state->param.u.qam.symbol_rate); */
+	if (state->param.u.qam.symbol_rate <= 1188750)
+		ratesel = 3;
+	else if (state->param.u.qam.symbol_rate <= 2377500)
+		ratesel = 2;
+	else if (state->param.u.qam.symbol_rate <= 4755000)
+		ratesel = 1;
+	status = write16(state, IQM_FD_RATESEL__A, ratesel);
+	if (status < 0)
+		goto error;
+
+	/*
+		IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
+		*/
+	symbFreq = state->param.u.qam.symbol_rate * (1 << ratesel);
+	if (symbFreq == 0) {
+		/* Divide by zero */
+		status = -EINVAL;
+		goto error;
+	}
+	iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) +
+		(Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) -
+		(1 << 23);
+	status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate);
+	if (status < 0)
+		goto error;
+	state->m_iqmRcRate = iqmRcRate;
+	/*
+		LcSymbFreq = round (.125 *  symbolrate / adcFreq * (1<<15))
+		*/
+	symbFreq = state->param.u.qam.symbol_rate;
+	if (adcFrequency == 0) {
+		/* Divide by zero */
+		status = -EINVAL;
+		goto error;
+	}
+	lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) +
+		(Frac28a((symbFreq % adcFrequency), adcFrequency) >>
+		16);
+	if (lcSymbRate > 511)
+		lcSymbRate = 511;
+	status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate);
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Get QAM lock status.
+* \param demod:   instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+	int status;
+	u16 Result[2] = { 0, 0 };
+
+	dprintk(1, "\n");
+	*pLockStatus = NOT_LOCKED;
+	status = scu_command(state,
+			SCU_RAM_COMMAND_STANDARD_QAM |
+			SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
+			Result);
+	if (status < 0)
+		printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status);
+
+	if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
+		/* 0x0000 NOT LOCKED */
+	} else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
+		/* 0x4000 DEMOD LOCKED */
+		*pLockStatus = DEMOD_LOCK;
+	} else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
+		/* 0x8000 DEMOD + FEC LOCKED (system lock) */
+		*pLockStatus = MPEG_LOCK;
+	} else {
+		/* 0xC000 NEVER LOCKED */
+		/* (system will never be able to lock to the signal) */
+		/* TODO: check this, intermediate & standard specific lock states are not
+		   taken into account here */
+		*pLockStatus = NEVER_LOCK;
+	}
+	return status;
+}
+
+#define QAM_MIRROR__M         0x03
+#define QAM_MIRROR_NORMAL     0x00
+#define QAM_MIRRORED          0x01
+#define QAM_MIRROR_AUTO_ON    0x02
+#define QAM_LOCKRANGE__M      0x10
+#define QAM_LOCKRANGE_NORMAL  0x10
+
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+		  s32 tunerFreqOffset)
+{
+	int status;
+	u16 setParamParameters[4] = { 0, 0, 0, 0 };
+	u16 cmdResult;
+
+	dprintk(1, "\n");
+	/*
+	 * STEP 1: reset demodulator
+	 *	resets FEC DI and FEC RS
+	 *	resets QAM block
+	 *	resets SCU variables
+	 */
+	status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
+	if (status < 0)
+		goto error;
+	status = QAMResetQAM(state);
+	if (status < 0)
+		goto error;
+
+	/*
+	 * STEP 2: configure demodulator
+	 *	-set params; resets IQM,QAM,FEC HW; initializes some
+	 *       SCU variables
+	 */
+	status = QAMSetSymbolrate(state);
+	if (status < 0)
+		goto error;
+
+	/* Set params */
+	switch (state->param.u.qam.modulation) {
+	case QAM_256:
+		state->m_Constellation = DRX_CONSTELLATION_QAM256;
+		break;
+	case QAM_AUTO:
+	case QAM_64:
+		state->m_Constellation = DRX_CONSTELLATION_QAM64;
+		break;
+	case QAM_16:
+		state->m_Constellation = DRX_CONSTELLATION_QAM16;
+		break;
+	case QAM_32:
+		state->m_Constellation = DRX_CONSTELLATION_QAM32;
+		break;
+	case QAM_128:
+		state->m_Constellation = DRX_CONSTELLATION_QAM128;
+		break;
+	default:
+		status = -EINVAL;
+		break;
+	}
+	if (status < 0)
+		goto error;
+	setParamParameters[0] = state->m_Constellation;	/* constellation     */
+	setParamParameters[1] = DRXK_QAM_I12_J17;	/* interleave mode   */
+	if (state->m_OperationMode == OM_QAM_ITU_C)
+		setParamParameters[2] = QAM_TOP_ANNEX_C;
+	else
+		setParamParameters[2] = QAM_TOP_ANNEX_A;
+	setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+	/* Env parameters */
+	/* check for LOCKRANGE Extented */
+	/* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult);
+	if (status < 0) {
+		/* Fall-back to the simpler call */
+		if (state->m_OperationMode == OM_QAM_ITU_C)
+			setParamParameters[0] = QAM_TOP_ANNEX_C;
+		else
+			setParamParameters[0] = QAM_TOP_ANNEX_A;
+		status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult);
+		if (status < 0)
+			goto error;
+
+		setParamParameters[0] = state->m_Constellation; /* constellation     */
+		setParamParameters[1] = DRXK_QAM_I12_J17;       /* interleave mode   */
+		status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult);
+	}
+	if (status < 0)
+		goto error;
+
+	/*
+	 * STEP 3: enable the system in a mode where the ADC provides valid
+	 * signal setup constellation independent registers
+	 */
+#if 0
+	status = SetFrequency(channel, tunerFreqOffset));
+	if (status < 0)
+		goto error;
+#endif
+	status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+	if (status < 0)
+		goto error;
+
+	/* Setup BER measurement */
+	status = SetQAMMeasurement(state, state->m_Constellation, state->param.u. qam.symbol_rate);
+	if (status < 0)
+		goto error;
+
+	/* Reset default values */
+	status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE);
+	if (status < 0)
+		goto error;
+
+	/* Reset default LC values */
+	status = write16(state, QAM_LC_RATE_LIMIT__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_LPF_FACTORP__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_LPF_FACTORI__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_MODE__A, 7);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, QAM_LC_QUAL_TAB0__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB1__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB2__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB3__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB4__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB5__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB6__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB8__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB9__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB10__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB12__A, 2);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB15__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB16__A, 3);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB20__A, 4);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_LC_QUAL_TAB25__A, 4);
+	if (status < 0)
+		goto error;
+
+	/* Mirroring, QAM-block starting point not inverted */
+	status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS);
+	if (status < 0)
+		goto error;
+
+	/* Halt SCU to enable safe non-atomic accesses */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+	if (status < 0)
+		goto error;
+
+	/* STEP 4: constellation specific setup */
+	switch (state->param.u.qam.modulation) {
+	case QAM_16:
+		status = SetQAM16(state);
+		break;
+	case QAM_32:
+		status = SetQAM32(state);
+		break;
+	case QAM_AUTO:
+	case QAM_64:
+		status = SetQAM64(state);
+		break;
+	case QAM_128:
+		status = SetQAM128(state);
+		break;
+	case QAM_256:
+		status = SetQAM256(state);
+		break;
+	default:
+		status = -EINVAL;
+		break;
+	}
+	if (status < 0)
+		goto error;
+
+	/* Activate SCU to enable SCU commands */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+
+	/* Re-configure MPEG output, requires knowledge of channel bitrate */
+	/* extAttr->currentChannel.constellation = channel->constellation; */
+	/* extAttr->currentChannel.symbolrate    = channel->symbolrate; */
+	status = MPEGTSDtoSetup(state, state->m_OperationMode);
+	if (status < 0)
+		goto error;
+
+	/* Start processes */
+	status = MPEGTSStart(state);
+	if (status < 0)
+		goto error;
+	status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+	status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+	if (status < 0)
+		goto error;
+
+	/* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+	status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+	if (status < 0)
+		goto error;
+
+	/* update global DRXK data container */
+/*?     extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */
+
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SetQAMStandard(struct drxk_state *state,
+			  enum OperationMode oMode)
+{
+	int status;
+#ifdef DRXK_QAM_TAPS
+#define DRXK_QAMA_TAPS_SELECT
+#include "drxk_filters.h"
+#undef DRXK_QAMA_TAPS_SELECT
+#endif
+
+	dprintk(1, "\n");
+
+	/* added antenna switch */
+	SwitchAntennaToQAM(state);
+
+	/* Ensure correct power-up mode */
+	status = PowerUpQAM(state);
+	if (status < 0)
+		goto error;
+	/* Reset QAM block */
+	status = QAMResetQAM(state);
+	if (status < 0)
+		goto error;
+
+	/* Setup IQM */
+
+	status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+	if (status < 0)
+		goto error;
+
+	/* Upload IQM Channel Filter settings by
+		boot loader from ROM table */
+	switch (oMode) {
+	case OM_QAM_ITU_A:
+		status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+		break;
+	case OM_QAM_ITU_C:
+		status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+		if (status < 0)
+			goto error;
+		status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+		break;
+	default:
+		status = -EINVAL;
+	}
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B));
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_SYMMETRIC__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_RC_STRETCH__A, 21);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_CLP_LEN__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_CLP_TH__A, 448);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_SNS_LEN__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, IQM_FS_ADJ_SEL__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_RC_ADJ_SEL__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_ADJ_SEL__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_UPD_SEL__A, 0);
+	if (status < 0)
+		goto error;
+
+	/* IQM Impulse Noise Processing Unit */
+	status = write16(state, IQM_CF_CLP_VAL__A, 500);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_DATATH__A, 1000);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_BYPASSDET__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_DET_LCT__A, 0);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_WND_LEN__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_CF_PKDTH__A, 1);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_INC_BYPASS__A, 1);
+	if (status < 0)
+		goto error;
+
+	/* turn on IQMAF. Must be done before setAgc**() */
+	status = SetIqmAf(state, true);
+	if (status < 0)
+		goto error;
+	status = write16(state, IQM_AF_START_LOCK__A, 0x01);
+	if (status < 0)
+		goto error;
+
+	/* IQM will not be reset from here, sync ADC and update/init AGC */
+	status = ADCSynchronization(state);
+	if (status < 0)
+		goto error;
+
+	/* Set the FSM step period */
+	status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000);
+	if (status < 0)
+		goto error;
+
+	/* Halt SCU to enable safe non-atomic accesses */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+	if (status < 0)
+		goto error;
+
+	/* No more resets of the IQM, current standard correctly set =>
+		now AGCs can be configured. */
+
+	status = InitAGC(state, true);
+	if (status < 0)
+		goto error;
+	status = SetPreSaw(state, &(state->m_qamPreSawCfg));
+	if (status < 0)
+		goto error;
+
+	/* Configure AGC's */
+	status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true);
+	if (status < 0)
+		goto error;
+	status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true);
+	if (status < 0)
+		goto error;
+
+	/* Activate SCU to enable SCU commands */
+	status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int WriteGPIO(struct drxk_state *state)
+{
+	int status;
+	u16 value = 0;
+
+	dprintk(1, "\n");
+	/* stop lock indicator process */
+	status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+	if (status < 0)
+		goto error;
+
+	/*  Write magic word to enable pdr reg write               */
+	status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+	if (status < 0)
+		goto error;
+
+	if (state->m_hasSAWSW) {
+		if (state->UIO_mask & 0x0001) { /* UIO-1 */
+			/* write to io pad configuration register - output mode */
+			status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+			if (status < 0)
+				goto error;
+
+			/* use corresponding bit in io data output registar */
+			status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+			if (status < 0)
+				goto error;
+			if ((state->m_GPIO & 0x0001) == 0)
+				value &= 0x7FFF;	/* write zero to 15th bit - 1st UIO */
+			else
+				value |= 0x8000;	/* write one to 15th bit - 1st UIO */
+			/* write back to io data output register */
+			status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+			if (status < 0)
+				goto error;
+		}
+		if (state->UIO_mask & 0x0002) { /* UIO-2 */
+			/* write to io pad configuration register - output mode */
+			status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+			if (status < 0)
+				goto error;
+
+			/* use corresponding bit in io data output registar */
+			status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+			if (status < 0)
+				goto error;
+			if ((state->m_GPIO & 0x0002) == 0)
+				value &= 0xBFFF;	/* write zero to 14th bit - 2st UIO */
+			else
+				value |= 0x4000;	/* write one to 14th bit - 2st UIO */
+			/* write back to io data output register */
+			status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+			if (status < 0)
+				goto error;
+		}
+		if (state->UIO_mask & 0x0004) { /* UIO-3 */
+			/* write to io pad configuration register - output mode */
+			status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+			if (status < 0)
+				goto error;
+
+			/* use corresponding bit in io data output registar */
+			status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+			if (status < 0)
+				goto error;
+			if ((state->m_GPIO & 0x0004) == 0)
+				value &= 0xFFFB;            /* write zero to 2nd bit - 3rd UIO */
+			else
+				value |= 0x0004;            /* write one to 2nd bit - 3rd UIO */
+			/* write back to io data output register */
+			status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+			if (status < 0)
+				goto error;
+		}
+	}
+	/*  Write magic word to disable pdr reg write               */
+	status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SwitchAntennaToQAM(struct drxk_state *state)
+{
+	int status = 0;
+	bool gpio_state;
+
+	dprintk(1, "\n");
+
+	if (!state->antenna_gpio)
+		return 0;
+
+	gpio_state = state->m_GPIO & state->antenna_gpio;
+
+	if (state->antenna_dvbt ^ gpio_state) {
+		/* Antenna is on DVB-T mode. Switch */
+		if (state->antenna_dvbt)
+			state->m_GPIO &= ~state->antenna_gpio;
+		else
+			state->m_GPIO |= state->antenna_gpio;
+		status = WriteGPIO(state);
+	}
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+static int SwitchAntennaToDVBT(struct drxk_state *state)
+{
+	int status = 0;
+	bool gpio_state;
+
+	dprintk(1, "\n");
+
+	if (!state->antenna_gpio)
+		return 0;
+
+	gpio_state = state->m_GPIO & state->antenna_gpio;
+
+	if (!(state->antenna_dvbt ^ gpio_state)) {
+		/* Antenna is on DVB-C mode. Switch */
+		if (state->antenna_dvbt)
+			state->m_GPIO |= state->antenna_gpio;
+		else
+			state->m_GPIO &= ~state->antenna_gpio;
+		status = WriteGPIO(state);
+	}
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+	return status;
+}
+
+
+static int PowerDownDevice(struct drxk_state *state)
+{
+	/* Power down to requested mode */
+	/* Backup some register settings */
+	/* Set pins with possible pull-ups connected to them in input mode */
+	/* Analog power down */
+	/* ADC power down */
+	/* Power down device */
+	int status;
+
+	dprintk(1, "\n");
+	if (state->m_bPDownOpenBridge) {
+		/* Open I2C bridge before power down of DRXK */
+		status = ConfigureI2CBridge(state, true);
+		if (status < 0)
+			goto error;
+	}
+	/* driver 0.9.0 */
+	status = DVBTEnableOFDMTokenRing(state, false);
+	if (status < 0)
+		goto error;
+
+	status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK);
+	if (status < 0)
+		goto error;
+	status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+	if (status < 0)
+		goto error;
+	state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+	status = HI_CfgCommand(state);
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static int load_microcode(struct drxk_state *state, const char *mc_name)
+{
+	const struct firmware *fw = NULL;
+	int err = 0;
+
+	dprintk(1, "\n");
+
+	err = request_firmware(&fw, mc_name, state->i2c->dev.parent);
+	if (err < 0) {
+		printk(KERN_ERR
+		       "drxk: Could not load firmware file %s.\n", mc_name);
+		printk(KERN_INFO
+		       "drxk: Copy %s to your hotplug directory!\n", mc_name);
+		return err;
+	}
+	err = DownloadMicrocode(state, fw->data, fw->size);
+	release_firmware(fw);
+	return err;
+}
+
+static int init_drxk(struct drxk_state *state)
+{
+	int status = 0;
+	enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+	u16 driverVersion;
+
+	dprintk(1, "\n");
+	if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+		status = PowerUpDevice(state);
+		if (status < 0)
+			goto error;
+		status = DRXX_Open(state);
+		if (status < 0)
+			goto error;
+		/* Soft reset of OFDM-, sys- and osc-clockdomain */
+		status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M);
+		if (status < 0)
+			goto error;
+		status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+		if (status < 0)
+			goto error;
+		/* TODO is this needed, if yes how much delay in worst case scenario */
+		msleep(1);
+		state->m_DRXK_A3_PATCH_CODE = true;
+		status = GetDeviceCapabilities(state);
+		if (status < 0)
+			goto error;
+
+		/* Bridge delay, uses oscilator clock */
+		/* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
+		/* SDA brdige delay */
+		state->m_HICfgBridgeDelay =
+			(u16) ((state->m_oscClockFreq / 1000) *
+				HI_I2C_BRIDGE_DELAY) / 1000;
+		/* Clipping */
+		if (state->m_HICfgBridgeDelay >
+			SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
+			state->m_HICfgBridgeDelay =
+				SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+		}
+		/* SCL bridge delay, same as SDA for now */
+		state->m_HICfgBridgeDelay +=
+			state->m_HICfgBridgeDelay <<
+			SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
+
+		status = InitHI(state);
+		if (status < 0)
+			goto error;
+		/* disable various processes */
+#if NOA1ROM
+		if (!(state->m_DRXK_A1_ROM_CODE)
+			&& !(state->m_DRXK_A2_ROM_CODE))
+#endif
+		{
+			status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+			if (status < 0)
+				goto error;
+		}
+
+		/* disable MPEG port */
+		status = MPEGTSDisable(state);
+		if (status < 0)
+			goto error;
+
+		/* Stop AUD and SCU */
+		status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP);
+		if (status < 0)
+			goto error;
+		status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP);
+		if (status < 0)
+			goto error;
+
+		/* enable token-ring bus through OFDM block for possible ucode upload */
+		status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
+		if (status < 0)
+			goto error;
+
+		/* include boot loader section */
+		status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE);
+		if (status < 0)
+			goto error;
+		status = BLChainCmd(state, 0, 6, 100);
+		if (status < 0)
+			goto error;
+
+		if (!state->microcode_name)
+			load_microcode(state, "drxk_a3.mc");
+		else
+			load_microcode(state, state->microcode_name);
+
+		/* disable token-ring bus through OFDM block for possible ucode upload */
+		status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
+		if (status < 0)
+			goto error;
+
+		/* Run SCU for a little while to initialize microcode version numbers */
+		status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+		if (status < 0)
+			goto error;
+		status = DRXX_Open(state);
+		if (status < 0)
+			goto error;
+		/* added for test */
+		msleep(30);
+
+		powerMode = DRXK_POWER_DOWN_OFDM;
+		status = CtrlPowerMode(state, &powerMode);
+		if (status < 0)
+			goto error;
+
+		/* Stamp driver version number in SCU data RAM in BCD code
+			Done to enable field application engineers to retreive drxdriver version
+			via I2C from SCU RAM.
+			Not using SCU command interface for SCU register access since no
+			microcode may be present.
+			*/
+		driverVersion =
+			(((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
+			(((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
+			((DRXK_VERSION_MAJOR % 10) << 4) +
+			(DRXK_VERSION_MINOR % 10);
+		status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion);
+		if (status < 0)
+			goto error;
+		driverVersion =
+			(((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
+			(((DRXK_VERSION_PATCH / 100) % 10) << 8) +
+			(((DRXK_VERSION_PATCH / 10) % 10) << 4) +
+			(DRXK_VERSION_PATCH % 10);
+		status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion);
+		if (status < 0)
+			goto error;
+
+		printk(KERN_INFO "DRXK driver version %d.%d.%d\n",
+			DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
+			DRXK_VERSION_PATCH);
+
+		/* Dirty fix of default values for ROM/PATCH microcode
+			Dirty because this fix makes it impossible to setup suitable values
+			before calling DRX_Open. This solution requires changes to RF AGC speed
+			to be done via the CTRL function after calling DRX_Open */
+
+		/* m_dvbtRfAgcCfg.speed = 3; */
+
+		/* Reset driver debug flags to 0 */
+		status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
+		if (status < 0)
+			goto error;
+		/* driver 0.9.0 */
+		/* Setup FEC OC:
+			NOTE: No more full FEC resets allowed afterwards!! */
+		status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP);
+		if (status < 0)
+			goto error;
+		/* MPEGTS functions are still the same */
+		status = MPEGTSDtoInit(state);
+		if (status < 0)
+			goto error;
+		status = MPEGTSStop(state);
+		if (status < 0)
+			goto error;
+		status = MPEGTSConfigurePolarity(state);
+		if (status < 0)
+			goto error;
+		status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput);
+		if (status < 0)
+			goto error;
+		/* added: configure GPIO */
+		status = WriteGPIO(state);
+		if (status < 0)
+			goto error;
+
+		state->m_DrxkState = DRXK_STOPPED;
+
+		if (state->m_bPowerDown) {
+			status = PowerDownDevice(state);
+			if (status < 0)
+				goto error;
+			state->m_DrxkState = DRXK_POWERED_DOWN;
+		} else
+			state->m_DrxkState = DRXK_STOPPED;
+	}
+error:
+	if (status < 0)
+		printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+	return status;
+}
+
+static void drxk_c_release(struct dvb_frontend *fe)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "\n");
+	kfree(state);
+}
+
+static int drxk_c_init(struct dvb_frontend *fe)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "\n");
+	if (mutex_trylock(&state->ctlock) == 0)
+		return -EBUSY;
+	SetOperationMode(state, OM_QAM_ITU_A);
+	return 0;
+}
+
+static int drxk_c_sleep(struct dvb_frontend *fe)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "\n");
+	ShutDown(state);
+	mutex_unlock(&state->ctlock);
+	return 0;
+}
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "%s\n", enable ? "enable" : "disable");
+	return ConfigureI2CBridge(state, enable ? true : false);
+}
+
+static int drxk_set_parameters(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *p)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+	u32 IF;
+
+	dprintk(1, "\n");
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe, p);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	state->param = *p;
+	fe->ops.tuner_ops.get_frequency(fe, &IF);
+	Start(state, 0, IF);
+
+	/* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
+
+	return 0;
+}
+
+static int drxk_c_get_frontend(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *p)
+{
+	dprintk(1, "\n");
+	return 0;
+}
+
+static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+	u32 stat;
+
+	dprintk(1, "\n");
+	*status = 0;
+	GetLockStatus(state, &stat, 0);
+	if (stat == MPEG_LOCK)
+		*status |= 0x1f;
+	if (stat == FEC_LOCK)
+		*status |= 0x0f;
+	if (stat == DEMOD_LOCK)
+		*status |= 0x07;
+	return 0;
+}
+
+static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	dprintk(1, "\n");
+
+	*ber = 0;
+	return 0;
+}
+
+static int drxk_read_signal_strength(struct dvb_frontend *fe,
+				     u16 *strength)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+	u32 val = 0;
+
+	dprintk(1, "\n");
+	ReadIFAgc(state, &val);
+	*strength = val & 0xffff;
+	return 0;
+}
+
+static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+	s32 snr2;
+
+	dprintk(1, "\n");
+	GetSignalToNoise(state, &snr2);
+	*snr = snr2 & 0xffff;
+	return 0;
+}
+
+static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+	u16 err;
+
+	dprintk(1, "\n");
+	DVBTQAMGetAccPktErr(state, &err);
+	*ucblocks = (u32) err;
+	return 0;
+}
+
+static int drxk_c_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
+				    *sets)
+{
+	dprintk(1, "\n");
+	sets->min_delay_ms = 3000;
+	sets->max_drift = 0;
+	sets->step_size = 0;
+	return 0;
+}
+
+static void drxk_t_release(struct dvb_frontend *fe)
+{
+	/*
+	 * There's nothing to release here, as the state struct
+	 * is already freed by drxk_c_release.
+	 */
+}
+
+static int drxk_t_init(struct dvb_frontend *fe)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "\n");
+	if (mutex_trylock(&state->ctlock) == 0)
+		return -EBUSY;
+	SetOperationMode(state, OM_DVBT);
+	return 0;
+}
+
+static int drxk_t_sleep(struct dvb_frontend *fe)
+{
+	struct drxk_state *state = fe->demodulator_priv;
+
+	dprintk(1, "\n");
+	mutex_unlock(&state->ctlock);
+	return 0;
+}
+
+static int drxk_t_get_frontend(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *p)
+{
+	dprintk(1, "\n");
+
+	return 0;
+}
+
+static struct dvb_frontend_ops drxk_c_ops = {
+	.info = {
+		 .name = "DRXK DVB-C",
+		 .type = FE_QAM,
+		 .frequency_stepsize = 62500,
+		 .frequency_min = 47000000,
+		 .frequency_max = 862000000,
+		 .symbol_rate_min = 870000,
+		 .symbol_rate_max = 11700000,
+		 .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+		 FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
+	.release = drxk_c_release,
+	.init = drxk_c_init,
+	.sleep = drxk_c_sleep,
+	.i2c_gate_ctrl = drxk_gate_ctrl,
+
+	.set_frontend = drxk_set_parameters,
+	.get_frontend = drxk_c_get_frontend,
+	.get_tune_settings = drxk_c_get_tune_settings,
+
+	.read_status = drxk_read_status,
+	.read_ber = drxk_read_ber,
+	.read_signal_strength = drxk_read_signal_strength,
+	.read_snr = drxk_read_snr,
+	.read_ucblocks = drxk_read_ucblocks,
+};
+
+static struct dvb_frontend_ops drxk_t_ops = {
+	.info = {
+		 .name = "DRXK DVB-T",
+		 .type = FE_OFDM,
+		 .frequency_min = 47125000,
+		 .frequency_max = 865000000,
+		 .frequency_stepsize = 166667,
+		 .frequency_tolerance = 0,
+		 .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+		 FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+		 FE_CAN_FEC_AUTO |
+		 FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+		 FE_CAN_QAM_AUTO |
+		 FE_CAN_TRANSMISSION_MODE_AUTO |
+		 FE_CAN_GUARD_INTERVAL_AUTO |
+		 FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS},
+	.release = drxk_t_release,
+	.init = drxk_t_init,
+	.sleep = drxk_t_sleep,
+	.i2c_gate_ctrl = drxk_gate_ctrl,
+
+	.set_frontend = drxk_set_parameters,
+	.get_frontend = drxk_t_get_frontend,
+
+	.read_status = drxk_read_status,
+	.read_ber = drxk_read_ber,
+	.read_signal_strength = drxk_read_signal_strength,
+	.read_snr = drxk_read_snr,
+	.read_ucblocks = drxk_read_ucblocks,
+};
+
+struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+				 struct i2c_adapter *i2c,
+				 struct dvb_frontend **fe_t)
+{
+	struct drxk_state *state = NULL;
+	u8 adr = config->adr;
+
+	dprintk(1, "\n");
+	state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	state->i2c = i2c;
+	state->demod_address = adr;
+	state->single_master = config->single_master;
+	state->microcode_name = config->microcode_name;
+	state->no_i2c_bridge = config->no_i2c_bridge;
+	state->antenna_gpio = config->antenna_gpio;
+	state->antenna_dvbt = config->antenna_dvbt;
+
+	/* NOTE: as more UIO bits will be used, add them to the mask */
+	state->UIO_mask = config->antenna_gpio;
+
+	/* Default gpio to DVB-C */
+	if (!state->antenna_dvbt && state->antenna_gpio)
+		state->m_GPIO |= state->antenna_gpio;
+	else
+		state->m_GPIO &= ~state->antenna_gpio;
+
+	mutex_init(&state->mutex);
+	mutex_init(&state->ctlock);
+
+	memcpy(&state->c_frontend.ops, &drxk_c_ops,
+	       sizeof(struct dvb_frontend_ops));
+	memcpy(&state->t_frontend.ops, &drxk_t_ops,
+	       sizeof(struct dvb_frontend_ops));
+	state->c_frontend.demodulator_priv = state;
+	state->t_frontend.demodulator_priv = state;
+
+	init_state(state);
+	if (init_drxk(state) < 0)
+		goto error;
+	*fe_t = &state->t_frontend;
+
+	return &state->c_frontend;
+
+error:
+	printk(KERN_ERR "drxk: not found\n");
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(drxk_attach);
+
+MODULE_DESCRIPTION("DRX-K driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h
new file mode 100644
index 0000000..a05c32e
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_hard.h
@@ -0,0 +1,348 @@
+#include "drxk_map.h"
+
+#define DRXK_VERSION_MAJOR 0
+#define DRXK_VERSION_MINOR 9
+#define DRXK_VERSION_PATCH 4300
+
+#define HI_I2C_DELAY        42
+#define HI_I2C_BRIDGE_DELAY 350
+#define DRXK_MAX_RETRIES    100
+
+#define DRIVER_4400 1
+
+#define DRXX_JTAGID   0x039210D9
+#define DRXX_J_JTAGID 0x239310D9
+#define DRXX_K_JTAGID 0x039210D9
+
+#define DRX_UNKNOWN     254
+#define DRX_AUTO        255
+
+#define DRX_SCU_READY   0
+#define DRXK_MAX_WAITTIME (200)
+#define SCU_RESULT_OK      0
+#define SCU_RESULT_SIZE   -4
+#define SCU_RESULT_INVPAR -3
+#define SCU_RESULT_UNKSTD -2
+#define SCU_RESULT_UNKCMD -1
+
+#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT
+#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200)
+#endif
+
+#define DRXK_8VSB_MPEG_BIT_RATE     19392658UL  /*bps*/
+#define DRXK_DVBT_MPEG_BIT_RATE     32000000UL  /*bps*/
+#define DRXK_QAM16_MPEG_BIT_RATE    27000000UL  /*bps*/
+#define DRXK_QAM32_MPEG_BIT_RATE    33000000UL  /*bps*/
+#define DRXK_QAM64_MPEG_BIT_RATE    40000000UL  /*bps*/
+#define DRXK_QAM128_MPEG_BIT_RATE   46000000UL  /*bps*/
+#define DRXK_QAM256_MPEG_BIT_RATE   52000000UL  /*bps*/
+#define DRXK_MAX_MPEG_BIT_RATE      52000000UL  /*bps*/
+
+#define   IQM_CF_OUT_ENA_OFDM__M                                            0x4
+#define     IQM_FS_ADJ_SEL_B_QAM                                            0x1
+#define     IQM_FS_ADJ_SEL_B_OFF                                            0x0
+#define     IQM_FS_ADJ_SEL_B_VSB                                            0x2
+#define     IQM_RC_ADJ_SEL_B_OFF                                            0x0
+#define     IQM_RC_ADJ_SEL_B_QAM                                            0x1
+#define     IQM_RC_ADJ_SEL_B_VSB                                            0x2
+
+enum OperationMode {
+	OM_NONE,
+	OM_QAM_ITU_A,
+	OM_QAM_ITU_B,
+	OM_QAM_ITU_C,
+	OM_DVBT
+};
+
+enum DRXPowerMode {
+	DRX_POWER_UP = 0,
+	DRX_POWER_MODE_1,
+	DRX_POWER_MODE_2,
+	DRX_POWER_MODE_3,
+	DRX_POWER_MODE_4,
+	DRX_POWER_MODE_5,
+	DRX_POWER_MODE_6,
+	DRX_POWER_MODE_7,
+	DRX_POWER_MODE_8,
+
+	DRX_POWER_MODE_9,
+	DRX_POWER_MODE_10,
+	DRX_POWER_MODE_11,
+	DRX_POWER_MODE_12,
+	DRX_POWER_MODE_13,
+	DRX_POWER_MODE_14,
+	DRX_POWER_MODE_15,
+	DRX_POWER_MODE_16,
+	DRX_POWER_DOWN = 255
+};
+
+
+/** /brief Intermediate power mode for DRXK, power down OFDM clock domain */
+#ifndef DRXK_POWER_DOWN_OFDM
+#define DRXK_POWER_DOWN_OFDM        DRX_POWER_MODE_1
+#endif
+
+/** /brief Intermediate power mode for DRXK, power down core (sysclk) */
+#ifndef DRXK_POWER_DOWN_CORE
+#define DRXK_POWER_DOWN_CORE        DRX_POWER_MODE_9
+#endif
+
+/** /brief Intermediate power mode for DRXK, power down pll (only osc runs) */
+#ifndef DRXK_POWER_DOWN_PLL
+#define DRXK_POWER_DOWN_PLL         DRX_POWER_MODE_10
+#endif
+
+
+enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF };
+enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN };
+enum EDrxkCoefArrayIndex {
+	DRXK_COEF_IDX_MN = 0,
+	DRXK_COEF_IDX_FM    ,
+	DRXK_COEF_IDX_L     ,
+	DRXK_COEF_IDX_LP    ,
+	DRXK_COEF_IDX_BG    ,
+	DRXK_COEF_IDX_DK    ,
+	DRXK_COEF_IDX_I     ,
+	DRXK_COEF_IDX_MAX
+};
+enum EDrxkSifAttenuation {
+	DRXK_SIF_ATTENUATION_0DB,
+	DRXK_SIF_ATTENUATION_3DB,
+	DRXK_SIF_ATTENUATION_6DB,
+	DRXK_SIF_ATTENUATION_9DB
+};
+enum EDrxkConstellation {
+	DRX_CONSTELLATION_BPSK = 0,
+	DRX_CONSTELLATION_QPSK,
+	DRX_CONSTELLATION_PSK8,
+	DRX_CONSTELLATION_QAM16,
+	DRX_CONSTELLATION_QAM32,
+	DRX_CONSTELLATION_QAM64,
+	DRX_CONSTELLATION_QAM128,
+	DRX_CONSTELLATION_QAM256,
+	DRX_CONSTELLATION_QAM512,
+	DRX_CONSTELLATION_QAM1024,
+	DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
+	DRX_CONSTELLATION_AUTO    = DRX_AUTO
+};
+enum EDrxkInterleaveMode {
+	DRXK_QAM_I12_J17    = 16,
+	DRXK_QAM_I_UNKNOWN  = DRX_UNKNOWN
+};
+enum {
+	DRXK_SPIN_A1 = 0,
+	DRXK_SPIN_A2,
+	DRXK_SPIN_A3,
+	DRXK_SPIN_UNKNOWN
+};
+
+enum DRXKCfgDvbtSqiSpeed {
+	DRXK_DVBT_SQI_SPEED_FAST = 0,
+	DRXK_DVBT_SQI_SPEED_MEDIUM,
+	DRXK_DVBT_SQI_SPEED_SLOW,
+	DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
+} ;
+
+enum DRXFftmode_t {
+	DRX_FFTMODE_2K = 0,
+	DRX_FFTMODE_4K,
+	DRX_FFTMODE_8K,
+	DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN,
+	DRX_FFTMODE_AUTO    = DRX_AUTO
+};
+
+enum DRXMPEGStrWidth_t {
+	DRX_MPEG_STR_WIDTH_1,
+	DRX_MPEG_STR_WIDTH_8
+};
+
+enum DRXQamLockRange_t {
+	DRX_QAM_LOCKRANGE_NORMAL,
+	DRX_QAM_LOCKRANGE_EXTENDED
+};
+
+struct DRXKCfgDvbtEchoThres_t {
+	u16             threshold;
+	enum DRXFftmode_t      fftMode;
+} ;
+
+struct SCfgAgc {
+	enum AGC_CTRL_MODE     ctrlMode;        /* off, user, auto */
+	u16            outputLevel;     /* range dependent on AGC */
+	u16            minOutputLevel;  /* range dependent on AGC */
+	u16            maxOutputLevel;  /* range dependent on AGC */
+	u16            speed;           /* range dependent on AGC */
+	u16            top;             /* rf-agc take over point */
+	u16            cutOffCurrent;   /* rf-agc is accelerated if output current
+					   is below cut-off current */
+	u16            IngainTgtMax;
+	u16            FastClipCtrlDelay;
+};
+
+struct SCfgPreSaw {
+	u16        reference; /* pre SAW reference value, range 0 .. 31 */
+	bool          usePreSaw; /* TRUE algorithms must use pre SAW sense */
+};
+
+struct DRXKOfdmScCmd_t {
+	u16 cmd;        /**< Command number */
+	u16 subcmd;     /**< Sub-command parameter*/
+	u16 param0;     /**< General purpous param */
+	u16 param1;     /**< General purpous param */
+	u16 param2;     /**< General purpous param */
+	u16 param3;     /**< General purpous param */
+	u16 param4;     /**< General purpous param */
+};
+
+struct drxk_state {
+	struct dvb_frontend c_frontend;
+	struct dvb_frontend t_frontend;
+	struct dvb_frontend_parameters param;
+	struct device *dev;
+
+	struct i2c_adapter *i2c;
+	u8     demod_address;
+	void  *priv;
+
+	struct mutex mutex;
+	struct mutex ctlock;
+
+	u32    m_Instance;           /**< Channel 1,2,3 or 4 */
+
+	int    m_ChunkSize;
+	u8 Chunk[256];
+
+	bool   m_hasLNA;
+	bool   m_hasDVBT;
+	bool   m_hasDVBC;
+	bool   m_hasAudio;
+	bool   m_hasATV;
+	bool   m_hasOOB;
+	bool   m_hasSAWSW;         /**< TRUE if mat_tx is available */
+	bool   m_hasGPIO1;         /**< TRUE if mat_rx is available */
+	bool   m_hasGPIO2;         /**< TRUE if GPIO is available */
+	bool   m_hasIRQN;          /**< TRUE if IRQN is available */
+	u16    m_oscClockFreq;
+	u16    m_HICfgTimingDiv;
+	u16    m_HICfgBridgeDelay;
+	u16    m_HICfgWakeUpKey;
+	u16    m_HICfgTimeout;
+	u16    m_HICfgCtrl;
+	s32    m_sysClockFreq;      /**< system clock frequency in kHz */
+
+	enum EDrxkState    m_DrxkState;      /**< State of Drxk (init,stopped,started) */
+	enum OperationMode m_OperationMode;  /**< digital standards */
+	struct SCfgAgc     m_vsbRfAgcCfg;    /**< settings for VSB RF-AGC */
+	struct SCfgAgc     m_vsbIfAgcCfg;    /**< settings for VSB IF-AGC */
+	u16                m_vsbPgaCfg;      /**< settings for VSB PGA */
+	struct SCfgPreSaw  m_vsbPreSawCfg;   /**< settings for pre SAW sense */
+	s32    m_Quality83percent;  /**< MER level (*0.1 dB) for 83% quality indication */
+	s32    m_Quality93percent;  /**< MER level (*0.1 dB) for 93% quality indication */
+	bool   m_smartAntInverted;
+	bool   m_bDebugEnableBridge;
+	bool   m_bPDownOpenBridge;  /**< only open DRXK bridge before power-down once it has been accessed */
+	bool   m_bPowerDown;        /**< Power down when not used */
+
+	u32    m_IqmFsRateOfs;      /**< frequency shift as written to DRXK register (28bit fixpoint) */
+
+	bool   m_enableMPEGOutput;  /**< If TRUE, enable MPEG output */
+	bool   m_insertRSByte;      /**< If TRUE, insert RS byte */
+	bool   m_enableParallel;    /**< If TRUE, parallel out otherwise serial */
+	bool   m_invertDATA;        /**< If TRUE, invert DATA signals */
+	bool   m_invertERR;         /**< If TRUE, invert ERR signal */
+	bool   m_invertSTR;         /**< If TRUE, invert STR signals */
+	bool   m_invertVAL;         /**< If TRUE, invert VAL signals */
+	bool   m_invertCLK;         /**< If TRUE, invert CLK signals */
+	bool   m_DVBCStaticCLK;
+	bool   m_DVBTStaticCLK;     /**< If TRUE, static MPEG clockrate will
+					 be used, otherwise clockrate will
+					 adapt to the bitrate of the TS */
+	u32    m_DVBTBitrate;
+	u32    m_DVBCBitrate;
+
+	u8     m_TSDataStrength;
+	u8     m_TSClockkStrength;
+
+	enum DRXMPEGStrWidth_t  m_widthSTR;    /**< MPEG start width */
+	u32    m_mpegTsStaticBitrate;          /**< Maximum bitrate in b/s in case
+						    static clockrate is selected */
+
+	/* LARGE_INTEGER   m_StartTime; */     /**< Contains the time of the last demod start */
+	s32    m_MpegLockTimeOut;      /**< WaitForLockStatus Timeout (counts from start time) */
+	s32    m_DemodLockTimeOut;     /**< WaitForLockStatus Timeout (counts from start time) */
+
+	bool   m_disableTEIhandling;
+
+	bool   m_RfAgcPol;
+	bool   m_IfAgcPol;
+
+	struct SCfgAgc    m_atvRfAgcCfg;  /**< settings for ATV RF-AGC */
+	struct SCfgAgc    m_atvIfAgcCfg;  /**< settings for ATV IF-AGC */
+	struct SCfgPreSaw m_atvPreSawCfg; /**< settings for ATV pre SAW sense */
+	bool              m_phaseCorrectionBypass;
+	s16               m_atvTopVidPeak;
+	u16               m_atvTopNoiseTh;
+	enum EDrxkSifAttenuation m_sifAttenuation;
+	bool              m_enableCVBSOutput;
+	bool              m_enableSIFOutput;
+	bool              m_bMirrorFreqSpect;
+	enum EDrxkConstellation  m_Constellation; /**< Constellation type of the channel */
+	u32               m_CurrSymbolRate;       /**< Current QAM symbol rate */
+	struct SCfgAgc    m_qamRfAgcCfg;          /**< settings for QAM RF-AGC */
+	struct SCfgAgc    m_qamIfAgcCfg;          /**< settings for QAM IF-AGC */
+	u16               m_qamPgaCfg;            /**< settings for QAM PGA */
+	struct SCfgPreSaw m_qamPreSawCfg;         /**< settings for QAM pre SAW sense */
+	enum EDrxkInterleaveMode m_qamInterleaveMode; /**< QAM Interleave mode */
+	u16               m_fecRsPlen;
+	u16               m_fecRsPrescale;
+
+	enum DRXKCfgDvbtSqiSpeed m_sqiSpeed;
+
+	u16               m_GPIO;
+	u16               m_GPIOCfg;
+
+	struct SCfgAgc    m_dvbtRfAgcCfg;     /**< settings for QAM RF-AGC */
+	struct SCfgAgc    m_dvbtIfAgcCfg;     /**< settings for QAM IF-AGC */
+	struct SCfgPreSaw m_dvbtPreSawCfg;    /**< settings for QAM pre SAW sense */
+
+	u16               m_agcFastClipCtrlDelay;
+	bool              m_adcCompPassed;
+	u16               m_adcCompCoef[64];
+	u16               m_adcState;
+
+	u8               *m_microcode;
+	int               m_microcode_length;
+	bool              m_DRXK_A1_PATCH_CODE;
+	bool              m_DRXK_A1_ROM_CODE;
+	bool              m_DRXK_A2_ROM_CODE;
+	bool              m_DRXK_A3_ROM_CODE;
+	bool              m_DRXK_A2_PATCH_CODE;
+	bool              m_DRXK_A3_PATCH_CODE;
+
+	bool              m_rfmirror;
+	u8                m_deviceSpin;
+	u32               m_iqmRcRate;
+
+	enum DRXPowerMode m_currentPowerMode;
+
+	/*
+	 * Configurable parameters at the driver. They stores the values found
+	 * at struct drxk_config.
+	 */
+
+	u16	UIO_mask;	/* Bits used by UIO */
+
+	bool	single_master;
+	bool	no_i2c_bridge;
+	bool	antenna_dvbt;
+	u16	antenna_gpio;
+
+	const char *microcode_name;
+};
+
+#define NEVER_LOCK 0
+#define NOT_LOCKED 1
+#define DEMOD_LOCK 2
+#define FEC_LOCK   3
+#define MPEG_LOCK  4
+
diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h
new file mode 100644
index 0000000..9b11a83
--- /dev/null
+++ b/drivers/media/dvb/frontends/drxk_map.h
@@ -0,0 +1,449 @@
+#define  AUD_COMM_EXEC__A                                                  0x1000000
+#define    AUD_COMM_EXEC_STOP                                              0x0
+#define  FEC_COMM_EXEC__A                                                  0x1C00000
+#define    FEC_COMM_EXEC_STOP                                              0x0
+#define    FEC_COMM_EXEC_ACTIVE                                            0x1
+#define  FEC_DI_COMM_EXEC__A                                               0x1C20000
+#define    FEC_DI_COMM_EXEC_STOP                                           0x0
+#define  FEC_DI_INPUT_CTL__A                                               0x1C20016
+#define  FEC_RS_COMM_EXEC__A                                               0x1C30000
+#define    FEC_RS_COMM_EXEC_STOP                                           0x0
+#define  FEC_RS_MEASUREMENT_PERIOD__A                                      0x1C30012
+#define  FEC_RS_MEASUREMENT_PRESCALE__A                                    0x1C30013
+#define  FEC_OC_MODE__A                                                    0x1C40011
+#define    FEC_OC_MODE_PARITY__M                                           0x1
+#define  FEC_OC_DTO_MODE__A                                                0x1C40014
+#define    FEC_OC_DTO_MODE_DYNAMIC__M                                      0x1
+#define    FEC_OC_DTO_MODE_OFFSET_ENABLE__M                                0x4
+#define  FEC_OC_DTO_PERIOD__A                                              0x1C40015
+#define  FEC_OC_DTO_BURST_LEN__A                                           0x1C40018
+#define  FEC_OC_FCT_MODE__A                                                0x1C4001A
+#define  FEC_OC_FCT_MODE__PRE                                              0x0
+#define    FEC_OC_FCT_MODE_RAT_ENA__M                                      0x1
+#define    FEC_OC_FCT_MODE_VIRT_ENA__M                                     0x2
+#define  FEC_OC_TMD_MODE__A                                                0x1C4001E
+#define  FEC_OC_TMD_COUNT__A                                               0x1C4001F
+#define  FEC_OC_TMD_HI_MARGIN__A                                           0x1C40020
+#define  FEC_OC_TMD_LO_MARGIN__A                                           0x1C40021
+#define  FEC_OC_TMD_INT_UPD_RATE__A                                        0x1C40023
+#define  FEC_OC_AVR_PARM_A__A                                              0x1C40026
+#define  FEC_OC_AVR_PARM_B__A                                              0x1C40027
+#define  FEC_OC_RCN_GAIN__A                                                0x1C4002E
+#define  FEC_OC_RCN_CTL_RATE_LO__A                                         0x1C40030
+#define  FEC_OC_RCN_CTL_STEP_LO__A                                         0x1C40032
+#define  FEC_OC_RCN_CTL_STEP_HI__A                                         0x1C40033
+#define  FEC_OC_SNC_MODE__A                                                0x1C40040
+#define    FEC_OC_SNC_MODE_SHUTDOWN__M                                     0x10
+#define  FEC_OC_SNC_LWM__A                                                 0x1C40041
+#define  FEC_OC_SNC_HWM__A                                                 0x1C40042
+#define  FEC_OC_SNC_UNLOCK__A                                              0x1C40043
+#define  FEC_OC_SNC_FAIL_PERIOD__A                                         0x1C40046
+#define  FEC_OC_IPR_MODE__A                                                0x1C40048
+#define    FEC_OC_IPR_MODE_SERIAL__M                                       0x1
+#define    FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M                             0x4
+#define    FEC_OC_IPR_MODE_MVAL_DIS_PAR__M                                 0x10
+#define  FEC_OC_IPR_INVERT__A                                              0x1C40049
+#define    FEC_OC_IPR_INVERT_MD0__M                                        0x1
+#define    FEC_OC_IPR_INVERT_MD1__M                                        0x2
+#define    FEC_OC_IPR_INVERT_MD2__M                                        0x4
+#define    FEC_OC_IPR_INVERT_MD3__M                                        0x8
+#define    FEC_OC_IPR_INVERT_MD4__M                                        0x10
+#define    FEC_OC_IPR_INVERT_MD5__M                                        0x20
+#define    FEC_OC_IPR_INVERT_MD6__M                                        0x40
+#define    FEC_OC_IPR_INVERT_MD7__M                                        0x80
+#define    FEC_OC_IPR_INVERT_MERR__M                                       0x100
+#define    FEC_OC_IPR_INVERT_MSTRT__M                                      0x200
+#define    FEC_OC_IPR_INVERT_MVAL__M                                       0x400
+#define    FEC_OC_IPR_INVERT_MCLK__M                                       0x800
+#define  FEC_OC_OCR_INVERT__A                                              0x1C40052
+#define  IQM_COMM_EXEC__A                                                  0x1800000
+#define      IQM_COMM_EXEC_B_STOP                                          0x0
+#define      IQM_COMM_EXEC_B_ACTIVE                                        0x1
+#define  IQM_FS_RATE_OFS_LO__A                                             0x1820010
+#define  IQM_FS_ADJ_SEL__A                                                 0x1820014
+#define      IQM_FS_ADJ_SEL_B_OFF                                          0x0
+#define      IQM_FS_ADJ_SEL_B_QAM                                          0x1
+#define      IQM_FS_ADJ_SEL_B_VSB                                          0x2
+#define  IQM_FD_RATESEL__A                                                 0x1830010
+#define  IQM_RC_RATE_OFS_LO__A                                             0x1840010
+#define  IQM_RC_RATE_OFS_LO__W                                             16
+#define  IQM_RC_RATE_OFS_LO__M                                             0xFFFF
+#define  IQM_RC_RATE_OFS_HI__M                                             0xFF
+#define  IQM_RC_ADJ_SEL__A                                                 0x1840014
+#define      IQM_RC_ADJ_SEL_B_OFF                                          0x0
+#define      IQM_RC_ADJ_SEL_B_QAM                                          0x1
+#define      IQM_RC_ADJ_SEL_B_VSB                                          0x2
+#define  IQM_RC_STRETCH__A                                                 0x1840016
+#define  IQM_CF_COMM_INT_MSK__A                                            0x1860006
+#define  IQM_CF_SYMMETRIC__A                                               0x1860010
+#define  IQM_CF_MIDTAP__A                                                  0x1860011
+#define    IQM_CF_MIDTAP_RE__B                                             0
+#define    IQM_CF_MIDTAP_IM__B                                             1
+#define  IQM_CF_OUT_ENA__A                                                 0x1860012
+#define    IQM_CF_OUT_ENA_QAM__B                                           1
+#define    IQM_CF_OUT_ENA_OFDM__M                                          0x4
+#define  IQM_CF_ADJ_SEL__A                                                 0x1860013
+#define  IQM_CF_SCALE__A                                                   0x1860014
+#define  IQM_CF_SCALE_SH__A                                                0x1860015
+#define  IQM_CF_SCALE_SH__PRE                                              0x0
+#define  IQM_CF_POW_MEAS_LEN__A                                            0x1860017
+#define  IQM_CF_DS_ENA__A                                                  0x1860019
+#define  IQM_CF_TAP_RE0__A                                                 0x1860020
+#define  IQM_CF_TAP_IM0__A                                                 0x1860040
+#define  IQM_CF_CLP_VAL__A                                                 0x1860060
+#define  IQM_CF_DATATH__A                                                  0x1860061
+#define  IQM_CF_PKDTH__A                                                   0x1860062
+#define  IQM_CF_WND_LEN__A                                                 0x1860063
+#define  IQM_CF_DET_LCT__A                                                 0x1860064
+#define  IQM_CF_BYPASSDET__A                                               0x1860067
+#define  IQM_AF_COMM_EXEC__A                                               0x1870000
+#define    IQM_AF_COMM_EXEC_ACTIVE                                         0x1
+#define  IQM_AF_CLKNEG__A                                                  0x1870012
+#define    IQM_AF_CLKNEG_CLKNEGDATA__M                                     0x2
+#define      IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS                     0x0
+#define      IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG                     0x2
+#define  IQM_AF_START_LOCK__A                                              0x187001B
+#define  IQM_AF_PHASE0__A                                                  0x187001C
+#define  IQM_AF_PHASE1__A                                                  0x187001D
+#define  IQM_AF_PHASE2__A                                                  0x187001E
+#define  IQM_AF_CLP_LEN__A                                                 0x1870023
+#define  IQM_AF_CLP_TH__A                                                  0x1870024
+#define  IQM_AF_SNS_LEN__A                                                 0x1870026
+#define  IQM_AF_AGC_IF__A                                                  0x1870028
+#define  IQM_AF_AGC_RF__A                                                  0x1870029
+#define  IQM_AF_PDREF__A                                                   0x187002B
+#define  IQM_AF_PDREF__M                                                   0x1F
+#define  IQM_AF_STDBY__A                                                   0x187002C
+#define      IQM_AF_STDBY_STDBY_ADC_STANDBY                                0x2
+#define      IQM_AF_STDBY_STDBY_AMP_STANDBY                                0x4
+#define      IQM_AF_STDBY_STDBY_PD_STANDBY                                 0x8
+#define      IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY                            0x10
+#define      IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY                            0x20
+#define  IQM_AF_AMUX__A                                                    0x187002D
+#define    IQM_AF_AMUX_SIGNAL2ADC                                          0x1
+#define  IQM_AF_UPD_SEL__A                                                 0x187002F
+#define  IQM_AF_INC_LCT__A                                                 0x1870034
+#define  IQM_AF_INC_BYPASS__A                                              0x1870036
+#define  OFDM_CP_COMM_EXEC__A                                              0x2800000
+#define    OFDM_CP_COMM_EXEC_STOP                                          0x0
+#define  OFDM_EC_SB_PRIOR__A                                               0x3410013
+#define    OFDM_EC_SB_PRIOR_HI                                             0x0
+#define    OFDM_EC_SB_PRIOR_LO                                             0x1
+#define  OFDM_EQ_TOP_TD_TPS_CONST__A                                       0x3010054
+#define  OFDM_EQ_TOP_TD_TPS_CONST__M                                       0x3
+#define    OFDM_EQ_TOP_TD_TPS_CONST_64QAM                                  0x2
+#define  OFDM_EQ_TOP_TD_TPS_CODE_HP__A                                     0x3010056
+#define  OFDM_EQ_TOP_TD_TPS_CODE_HP__M                                     0x7
+#define    OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8                                  0x4
+#define  OFDM_EQ_TOP_TD_SQR_ERR_I__A                                       0x301005E
+#define  OFDM_EQ_TOP_TD_SQR_ERR_Q__A                                       0x301005F
+#define  OFDM_EQ_TOP_TD_SQR_ERR_EXP__A                                     0x3010060
+#define  OFDM_EQ_TOP_TD_REQ_SMB_CNT__A                                     0x3010061
+#define  OFDM_EQ_TOP_TD_TPS_PWR_OFS__A                                     0x3010062
+#define  OFDM_LC_COMM_EXEC__A                                              0x3800000
+#define    OFDM_LC_COMM_EXEC_STOP                                          0x0
+#define  OFDM_SC_COMM_EXEC__A                                              0x3C00000
+#define    OFDM_SC_COMM_EXEC_STOP                                          0x0
+#define  OFDM_SC_COMM_STATE__A                                             0x3C00001
+#define  OFDM_SC_RA_RAM_PARAM0__A                                          0x3C20040
+#define  OFDM_SC_RA_RAM_PARAM1__A                                          0x3C20041
+#define  OFDM_SC_RA_RAM_CMD_ADDR__A                                        0x3C20042
+#define  OFDM_SC_RA_RAM_CMD__A                                             0x3C20043
+#define    OFDM_SC_RA_RAM_CMD_NULL                                         0x0
+#define    OFDM_SC_RA_RAM_CMD_PROC_START                                   0x1
+#define    OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM                               0x3
+#define    OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM                                0x4
+#define    OFDM_SC_RA_RAM_CMD_GET_OP_PARAM                                 0x5
+#define    OFDM_SC_RA_RAM_CMD_USER_IO                                      0x6
+#define    OFDM_SC_RA_RAM_CMD_SET_TIMER                                    0x7
+#define    OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING                              0x8
+#define    OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M                            0x1
+#define    OFDM_SC_RA_RAM_LOCKTRACK_MIN                                    0x1
+#define  OFDM_SC_RA_RAM_OP_PARAM__A                                        0x3C20048
+#define    OFDM_SC_RA_RAM_OP_PARAM_MODE__M                                 0x3
+#define      OFDM_SC_RA_RAM_OP_PARAM_MODE_2K                               0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_MODE_8K                               0x1
+#define      OFDM_SC_RA_RAM_OP_PARAM_GUARD_32                              0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_GUARD_16                              0x4
+#define      OFDM_SC_RA_RAM_OP_PARAM_GUARD_8                               0x8
+#define      OFDM_SC_RA_RAM_OP_PARAM_GUARD_4                               0xC
+#define      OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK                            0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16                           0x10
+#define      OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64                           0x20
+#define      OFDM_SC_RA_RAM_OP_PARAM_HIER_NO                               0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_HIER_A1                               0x40
+#define      OFDM_SC_RA_RAM_OP_PARAM_HIER_A2                               0x80
+#define      OFDM_SC_RA_RAM_OP_PARAM_HIER_A4                               0xC0
+#define      OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2                              0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3                              0x200
+#define      OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4                              0x400
+#define      OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6                              0x600
+#define      OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8                              0x800
+#define      OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI                               0x0
+#define      OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO                               0x1000
+#define    OFDM_SC_RA_RAM_OP_AUTO_MODE__M                                  0x1
+#define    OFDM_SC_RA_RAM_OP_AUTO_GUARD__M                                 0x2
+#define    OFDM_SC_RA_RAM_OP_AUTO_CONST__M                                 0x4
+#define    OFDM_SC_RA_RAM_OP_AUTO_HIER__M                                  0x8
+#define    OFDM_SC_RA_RAM_OP_AUTO_RATE__M                                  0x10
+#define  OFDM_SC_RA_RAM_LOCK__A                                            0x3C2004B
+#define    OFDM_SC_RA_RAM_LOCK_DEMOD__M                                    0x1
+#define    OFDM_SC_RA_RAM_LOCK_FEC__M                                      0x2
+#define    OFDM_SC_RA_RAM_LOCK_MPEG__M                                     0x4
+#define    OFDM_SC_RA_RAM_LOCK_NODVBT__M                                   0x8
+#define  OFDM_SC_RA_RAM_BE_OPT_DELAY__A                                    0x3C2004D
+#define  OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A                               0x3C2004E
+#define  OFDM_SC_RA_RAM_ECHO_THRES__A                                      0x3C2004F
+#define    OFDM_SC_RA_RAM_ECHO_THRES_8K__B                                 0
+#define    OFDM_SC_RA_RAM_ECHO_THRES_8K__M                                 0xFF
+#define    OFDM_SC_RA_RAM_ECHO_THRES_2K__B                                 8
+#define    OFDM_SC_RA_RAM_ECHO_THRES_2K__M                                 0xFF00
+#define  OFDM_SC_RA_RAM_CONFIG__A                                          0x3C20050
+#define    OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M                          0x800
+#define  OFDM_SC_RA_RAM_FR_THRES_8K__A                                     0x3C2007D
+#define  OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A                             0x3C200E0
+#define  OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A                            0x3C200E1
+#define  OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A                             0x3C200E3
+#define  OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A                            0x3C200E4
+#define  OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A                                0x3C200F8
+#define  QAM_COMM_EXEC__A                                                  0x1400000
+#define    QAM_COMM_EXEC_STOP                                              0x0
+#define    QAM_COMM_EXEC_ACTIVE                                            0x1
+#define    QAM_TOP_ANNEX_A                                                 0x0
+#define    QAM_TOP_ANNEX_C                                                 0x2
+#define  QAM_SL_ERR_POWER__A                                               0x1430017
+#define  QAM_DQ_QUAL_FUN0__A                                               0x1440018
+#define  QAM_DQ_QUAL_FUN1__A                                               0x1440019
+#define  QAM_DQ_QUAL_FUN2__A                                               0x144001A
+#define  QAM_DQ_QUAL_FUN3__A                                               0x144001B
+#define  QAM_DQ_QUAL_FUN4__A                                               0x144001C
+#define  QAM_DQ_QUAL_FUN5__A                                               0x144001D
+#define  QAM_LC_MODE__A                                                    0x1450010
+#define  QAM_LC_QUAL_TAB0__A                                               0x1450018
+#define  QAM_LC_QUAL_TAB1__A                                               0x1450019
+#define  QAM_LC_QUAL_TAB2__A                                               0x145001A
+#define  QAM_LC_QUAL_TAB3__A                                               0x145001B
+#define  QAM_LC_QUAL_TAB4__A                                               0x145001C
+#define  QAM_LC_QUAL_TAB5__A                                               0x145001D
+#define  QAM_LC_QUAL_TAB6__A                                               0x145001E
+#define  QAM_LC_QUAL_TAB8__A                                               0x145001F
+#define  QAM_LC_QUAL_TAB9__A                                               0x1450020
+#define  QAM_LC_QUAL_TAB10__A                                              0x1450021
+#define  QAM_LC_QUAL_TAB12__A                                              0x1450022
+#define  QAM_LC_QUAL_TAB15__A                                              0x1450023
+#define  QAM_LC_QUAL_TAB16__A                                              0x1450024
+#define  QAM_LC_QUAL_TAB20__A                                              0x1450025
+#define  QAM_LC_QUAL_TAB25__A                                              0x1450026
+#define  QAM_LC_LPF_FACTORP__A                                             0x1450028
+#define  QAM_LC_LPF_FACTORI__A                                             0x1450029
+#define  QAM_LC_RATE_LIMIT__A                                              0x145002A
+#define  QAM_LC_SYMBOL_FREQ__A                                             0x145002B
+#define  QAM_SY_TIMEOUT__A                                                 0x1470011
+#define  QAM_SY_TIMEOUT__PRE                                               0x3A98
+#define  QAM_SY_SYNC_LWM__A                                                0x1470012
+#define  QAM_SY_SYNC_AWM__A                                                0x1470013
+#define  QAM_SY_SYNC_HWM__A                                                0x1470014
+#define  QAM_SY_SP_INV__A                                                  0x1470017
+#define    QAM_SY_SP_INV_SPECTRUM_INV_DIS                                  0x0
+#define  SCU_COMM_EXEC__A                                                  0x800000
+#define    SCU_COMM_EXEC_STOP                                              0x0
+#define    SCU_COMM_EXEC_ACTIVE                                            0x1
+#define    SCU_COMM_EXEC_HOLD                                              0x2
+#define  SCU_RAM_DRIVER_DEBUG__A                                           0x831EBF
+#define  SCU_RAM_QAM_FSM_STEP_PERIOD__A                                    0x831EC4
+#define  SCU_RAM_GPIO__A                                                   0x831EC7
+#define      SCU_RAM_GPIO_HW_LOCK_IND_DISABLE                              0x0
+#define  SCU_RAM_AGC_CLP_CTRL_MODE__A                                      0x831EC8
+#define  SCU_RAM_FEC_ACCUM_PKT_FAILURES__A                                 0x831ECB
+#define  SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A                               0x831F05
+#define  SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A                                0x831F15
+#define  SCU_RAM_AGC_KI_CYCLEN__A                                          0x831F17
+#define  SCU_RAM_AGC_SNS_CYCLEN__A                                         0x831F18
+#define  SCU_RAM_AGC_RF_SNS_DEV_MAX__A                                     0x831F19
+#define  SCU_RAM_AGC_RF_SNS_DEV_MIN__A                                     0x831F1A
+#define  SCU_RAM_AGC_RF_MAX__A                                             0x831F1B
+#define  SCU_RAM_AGC_CONFIG__A                                             0x831F24
+#define    SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M                            0x1
+#define    SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M                            0x2
+#define    SCU_RAM_AGC_CONFIG_INV_IF_POL__M                                0x100
+#define    SCU_RAM_AGC_CONFIG_INV_RF_POL__M                                0x200
+#define  SCU_RAM_AGC_KI__A                                                 0x831F25
+#define    SCU_RAM_AGC_KI_RF__B                                            4
+#define    SCU_RAM_AGC_KI_RF__M                                            0xF0
+#define    SCU_RAM_AGC_KI_IF__B                                            8
+#define    SCU_RAM_AGC_KI_IF__M                                            0xF00
+#define  SCU_RAM_AGC_KI_RED__A                                             0x831F26
+#define    SCU_RAM_AGC_KI_RED_RAGC_RED__B                                  2
+#define    SCU_RAM_AGC_KI_RED_RAGC_RED__M                                  0xC
+#define    SCU_RAM_AGC_KI_RED_IAGC_RED__B                                  4
+#define    SCU_RAM_AGC_KI_RED_IAGC_RED__M                                  0x30
+#define  SCU_RAM_AGC_KI_INNERGAIN_MIN__A                                   0x831F27
+#define  SCU_RAM_AGC_KI_MINGAIN__A                                         0x831F28
+#define  SCU_RAM_AGC_KI_MAXGAIN__A                                         0x831F29
+#define  SCU_RAM_AGC_KI_MAXMINGAIN_TH__A                                   0x831F2A
+#define  SCU_RAM_AGC_KI_MIN__A                                             0x831F2B
+#define  SCU_RAM_AGC_KI_MAX__A                                             0x831F2C
+#define  SCU_RAM_AGC_CLP_SUM__A                                            0x831F2D
+#define  SCU_RAM_AGC_CLP_SUM_MIN__A                                        0x831F2E
+#define  SCU_RAM_AGC_CLP_SUM_MAX__A                                        0x831F2F
+#define  SCU_RAM_AGC_CLP_CYCLEN__A                                         0x831F30
+#define  SCU_RAM_AGC_CLP_CYCCNT__A                                         0x831F31
+#define  SCU_RAM_AGC_CLP_DIR_TO__A                                         0x831F32
+#define  SCU_RAM_AGC_CLP_DIR_WD__A                                         0x831F33
+#define  SCU_RAM_AGC_CLP_DIR_STP__A                                        0x831F34
+#define  SCU_RAM_AGC_SNS_SUM__A                                            0x831F35
+#define  SCU_RAM_AGC_SNS_SUM_MIN__A                                        0x831F36
+#define  SCU_RAM_AGC_SNS_SUM_MAX__A                                        0x831F37
+#define  SCU_RAM_AGC_SNS_CYCCNT__A                                         0x831F38
+#define  SCU_RAM_AGC_SNS_DIR_TO__A                                         0x831F39
+#define  SCU_RAM_AGC_SNS_DIR_WD__A                                         0x831F3A
+#define  SCU_RAM_AGC_SNS_DIR_STP__A                                        0x831F3B
+#define  SCU_RAM_AGC_INGAIN_TGT__A                                         0x831F3D
+#define  SCU_RAM_AGC_INGAIN_TGT_MIN__A                                     0x831F3E
+#define  SCU_RAM_AGC_INGAIN_TGT_MAX__A                                     0x831F3F
+#define  SCU_RAM_AGC_IF_IACCU_HI__A                                        0x831F40
+#define  SCU_RAM_AGC_IF_IACCU_LO__A                                        0x831F41
+#define  SCU_RAM_AGC_IF_IACCU_HI_TGT__A                                    0x831F42
+#define  SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A                                0x831F43
+#define  SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A                                0x831F44
+#define  SCU_RAM_AGC_RF_IACCU_HI__A                                        0x831F45
+#define  SCU_RAM_AGC_RF_IACCU_LO__A                                        0x831F46
+#define  SCU_RAM_AGC_RF_IACCU_HI_CO__A                                     0x831F47
+#define  SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A                                 0x831F84
+#define  SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A                                0x831F85
+#define  SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A                                  0x831F86
+#define  SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A                                  0x831F87
+#define  SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A                                  0x831F88
+#define  SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A                                  0x831F89
+#define  SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A                                  0x831F8A
+#define  SCU_RAM_QAM_FSM_RTH__A                                            0x831F8E
+#define  SCU_RAM_QAM_FSM_FTH__A                                            0x831F8F
+#define  SCU_RAM_QAM_FSM_PTH__A                                            0x831F90
+#define  SCU_RAM_QAM_FSM_MTH__A                                            0x831F91
+#define  SCU_RAM_QAM_FSM_CTH__A                                            0x831F92
+#define  SCU_RAM_QAM_FSM_QTH__A                                            0x831F93
+#define  SCU_RAM_QAM_FSM_RATE_LIM__A                                       0x831F94
+#define  SCU_RAM_QAM_FSM_FREQ_LIM__A                                       0x831F95
+#define  SCU_RAM_QAM_FSM_COUNT_LIM__A                                      0x831F96
+#define  SCU_RAM_QAM_LC_CA_COARSE__A                                       0x831F97
+#define  SCU_RAM_QAM_LC_CA_FINE__A                                         0x831F99
+#define  SCU_RAM_QAM_LC_CP_COARSE__A                                       0x831F9A
+#define  SCU_RAM_QAM_LC_CP_MEDIUM__A                                       0x831F9B
+#define  SCU_RAM_QAM_LC_CP_FINE__A                                         0x831F9C
+#define  SCU_RAM_QAM_LC_CI_COARSE__A                                       0x831F9D
+#define  SCU_RAM_QAM_LC_CI_MEDIUM__A                                       0x831F9E
+#define  SCU_RAM_QAM_LC_CI_FINE__A                                         0x831F9F
+#define  SCU_RAM_QAM_LC_EP_COARSE__A                                       0x831FA0
+#define  SCU_RAM_QAM_LC_EP_MEDIUM__A                                       0x831FA1
+#define  SCU_RAM_QAM_LC_EP_FINE__A                                         0x831FA2
+#define  SCU_RAM_QAM_LC_EI_COARSE__A                                       0x831FA3
+#define  SCU_RAM_QAM_LC_EI_MEDIUM__A                                       0x831FA4
+#define  SCU_RAM_QAM_LC_EI_FINE__A                                         0x831FA5
+#define  SCU_RAM_QAM_LC_CF_COARSE__A                                       0x831FA6
+#define  SCU_RAM_QAM_LC_CF_MEDIUM__A                                       0x831FA7
+#define  SCU_RAM_QAM_LC_CF_FINE__A                                         0x831FA8
+#define  SCU_RAM_QAM_LC_CF1_COARSE__A                                      0x831FA9
+#define  SCU_RAM_QAM_LC_CF1_MEDIUM__A                                      0x831FAA
+#define  SCU_RAM_QAM_LC_CF1_FINE__A                                        0x831FAB
+#define  SCU_RAM_QAM_SL_SIG_POWER__A                                       0x831FAC
+#define  SCU_RAM_QAM_EQ_CMA_RAD0__A                                        0x831FAD
+#define  SCU_RAM_QAM_EQ_CMA_RAD1__A                                        0x831FAE
+#define  SCU_RAM_QAM_EQ_CMA_RAD2__A                                        0x831FAF
+#define  SCU_RAM_QAM_EQ_CMA_RAD3__A                                        0x831FB0
+#define  SCU_RAM_QAM_EQ_CMA_RAD4__A                                        0x831FB1
+#define  SCU_RAM_QAM_EQ_CMA_RAD5__A                                        0x831FB2
+#define      SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED                        0x4000
+#define      SCU_RAM_QAM_LOCKED_LOCKED_LOCKED                              0x8000
+#define      SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK                          0xC000
+#define  SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A                                0x831FEA
+#define  SCU_RAM_DRIVER_VER_HI__A                                          0x831FEB
+#define  SCU_RAM_DRIVER_VER_LO__A                                          0x831FEC
+#define  SCU_RAM_PARAM_15__A                                               0x831FED
+#define  SCU_RAM_PARAM_0__A                                                0x831FFC
+#define  SCU_RAM_COMMAND__A                                                0x831FFD
+#define    SCU_RAM_COMMAND_CMD_DEMOD_RESET                                 0x1
+#define    SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV                               0x2
+#define    SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM                             0x3
+#define    SCU_RAM_COMMAND_CMD_DEMOD_START                                 0x4
+#define    SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK                              0x5
+#define    SCU_RAM_COMMAND_CMD_DEMOD_STOP                                  0x9
+#define      SCU_RAM_COMMAND_STANDARD_QAM                                  0x200
+#define      SCU_RAM_COMMAND_STANDARD_OFDM                                 0x400
+#define  SIO_TOP_COMM_KEY__A                                               0x41000F
+#define    SIO_TOP_COMM_KEY_KEY                                            0xFABA
+#define  SIO_TOP_JTAGID_LO__A                                              0x410012
+#define  SIO_HI_RA_RAM_RES__A                                              0x420031
+#define  SIO_HI_RA_RAM_CMD__A                                              0x420032
+#define    SIO_HI_RA_RAM_CMD_RESET                                         0x2
+#define    SIO_HI_RA_RAM_CMD_CONFIG                                        0x3
+#define    SIO_HI_RA_RAM_CMD_BRDCTRL                                       0x7
+#define  SIO_HI_RA_RAM_PAR_1__A                                            0x420033
+#define      SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY                              0x3945
+#define  SIO_HI_RA_RAM_PAR_2__A                                            0x420034
+#define    SIO_HI_RA_RAM_PAR_2_CFG_DIV__M                                  0x7F
+#define      SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN                              0x0
+#define      SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED                            0x4
+#define  SIO_HI_RA_RAM_PAR_3__A                                            0x420035
+#define    SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M                              0x7F
+#define    SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B                              7
+#define      SIO_HI_RA_RAM_PAR_3_ACP_RW_READ                               0x0
+#define      SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE                              0x8
+#define  SIO_HI_RA_RAM_PAR_4__A                                            0x420036
+#define  SIO_HI_RA_RAM_PAR_5__A                                            0x420037
+#define      SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE                            0x1
+#define    SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M                                0x8
+#define      SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ                             0x8
+#define  SIO_HI_RA_RAM_PAR_6__A                                            0x420038
+#define  SIO_CC_PLL_LOCK__A                                                0x450012
+#define  SIO_CC_PWD_MODE__A                                                0x450015
+#define      SIO_CC_PWD_MODE_LEVEL_NONE                                    0x0
+#define      SIO_CC_PWD_MODE_LEVEL_OFDM                                    0x1
+#define      SIO_CC_PWD_MODE_LEVEL_CLOCK                                   0x2
+#define      SIO_CC_PWD_MODE_LEVEL_PLL                                     0x3
+#define      SIO_CC_PWD_MODE_LEVEL_OSC                                     0x4
+#define  SIO_CC_SOFT_RST__A                                                0x450016
+#define    SIO_CC_SOFT_RST_OFDM__M                                         0x1
+#define    SIO_CC_SOFT_RST_SYS__M                                          0x2
+#define    SIO_CC_SOFT_RST_OSC__M                                          0x4
+#define  SIO_CC_UPDATE__A                                                  0x450017
+#define    SIO_CC_UPDATE_KEY                                               0xFABA
+#define  SIO_OFDM_SH_OFDM_RING_ENABLE__A                                   0x470010
+#define    SIO_OFDM_SH_OFDM_RING_ENABLE_OFF                                0x0
+#define    SIO_OFDM_SH_OFDM_RING_ENABLE_ON                                 0x1
+#define  SIO_OFDM_SH_OFDM_RING_STATUS__A                                   0x470012
+#define    SIO_OFDM_SH_OFDM_RING_STATUS_DOWN                               0x0
+#define    SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED                            0x1
+#define  SIO_BL_COMM_EXEC__A                                               0x480000
+#define    SIO_BL_COMM_EXEC_ACTIVE                                         0x1
+#define  SIO_BL_STATUS__A                                                  0x480010
+#define  SIO_BL_MODE__A                                                    0x480011
+#define    SIO_BL_MODE_DIRECT                                              0x0
+#define    SIO_BL_MODE_CHAIN                                               0x1
+#define  SIO_BL_ENABLE__A                                                  0x480012
+#define    SIO_BL_ENABLE_ON                                                0x1
+#define  SIO_BL_TGT_HDR__A                                                 0x480014
+#define  SIO_BL_TGT_ADDR__A                                                0x480015
+#define  SIO_BL_SRC_ADDR__A                                                0x480016
+#define  SIO_BL_SRC_LEN__A                                                 0x480017
+#define  SIO_BL_CHAIN_ADDR__A                                              0x480018
+#define  SIO_BL_CHAIN_LEN__A                                               0x480019
+#define  SIO_PDR_MON_CFG__A                                                0x7F0010
+#define  SIO_PDR_UIO_IN_HI__A                                              0x7F0015
+#define  SIO_PDR_UIO_OUT_LO__A                                             0x7F0016
+#define  SIO_PDR_OHW_CFG__A                                                0x7F001F
+#define    SIO_PDR_OHW_CFG_FREF_SEL__M                                     0x3
+#define  SIO_PDR_MSTRT_CFG__A                                              0x7F0025
+#define  SIO_PDR_MERR_CFG__A                                               0x7F0026
+#define  SIO_PDR_MCLK_CFG__A                                               0x7F0028
+#define    SIO_PDR_MCLK_CFG_DRIVE__B                                       3
+#define  SIO_PDR_MVAL_CFG__A                                               0x7F0029
+#define  SIO_PDR_MD0_CFG__A                                                0x7F002A
+#define    SIO_PDR_MD0_CFG_DRIVE__B                                        3
+#define  SIO_PDR_MD1_CFG__A                                                0x7F002B
+#define  SIO_PDR_MD2_CFG__A                                                0x7F002C
+#define  SIO_PDR_MD3_CFG__A                                                0x7F002D
+#define  SIO_PDR_MD4_CFG__A                                                0x7F002F
+#define  SIO_PDR_MD5_CFG__A                                                0x7F0030
+#define  SIO_PDR_MD6_CFG__A                                                0x7F0031
+#define  SIO_PDR_MD7_CFG__A                                                0x7F0032
+#define  SIO_PDR_SMA_TX_CFG__A                                             0x7F0038
diff --git a/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb/frontends/itd1000.c
index f7a40a1..aa9ccb8 100644
--- a/drivers/media/dvb/frontends/itd1000.c
+++ b/drivers/media/dvb/frontends/itd1000.c
@@ -35,21 +35,18 @@
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 
-#define deb(args...)  do { \
+#define itd_dbg(args...)  do { \
 	if (debug) { \
 		printk(KERN_DEBUG   "ITD1000: " args);\
-		printk("\n"); \
 	} \
 } while (0)
 
-#define warn(args...) do { \
+#define itd_warn(args...) do { \
 	printk(KERN_WARNING "ITD1000: " args); \
-	printk("\n"); \
 } while (0)
 
-#define info(args...) do { \
+#define itd_info(args...) do { \
 	printk(KERN_INFO    "ITD1000: " args); \
-	printk("\n"); \
 } while (0)
 
 /* don't write more than one byte with flexcop behind */
@@ -62,7 +59,7 @@
 	buf[0] = reg;
 	memcpy(&buf[1], v, len);
 
-	/* deb("wr %02x: %02x", reg, v[0]); */
+	/* itd_dbg("wr %02x: %02x\n", reg, v[0]); */
 
 	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
 		printk(KERN_WARNING "itd1000 I2C write failed\n");
@@ -83,7 +80,7 @@
 	itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1);
 
 	if (i2c_transfer(state->i2c, msg, 2) != 2) {
-		warn("itd1000 I2C read failed");
+		itd_warn("itd1000 I2C read failed\n");
 		return -EREMOTEIO;
 	}
 	return val;
@@ -127,14 +124,14 @@
 	u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0;
 	u8 bw      = itd1000_read_reg(state, BW)      & 0xf0;
 
-	deb("symbol_rate = %d", symbol_rate);
+	itd_dbg("symbol_rate = %d\n", symbol_rate);
 
 	/* not sure what is that ? - starting to download the table */
 	itd1000_write_reg(state, CON1, con1 | (1 << 1));
 
 	for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++)
 		if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) {
-			deb("symrate: index: %d pgaext: %x, bbgvmin: %x", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin);
+			itd_dbg("symrate: index: %d pgaext: %x, bbgvmin: %x\n", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin);
 			itd1000_write_reg(state, PLLFH,   pllfh | (itd1000_lpf_pga[i].pgaext << 4));
 			itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin));
 			itd1000_write_reg(state, BW,      bw | (i & 0x0f));
@@ -182,7 +179,7 @@
 
 			adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f;
 
-			deb("VCO: %dkHz: %d -> ADCOUT: %d %02x", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c);
+			itd_dbg("VCO: %dkHz: %d -> ADCOUT: %d %02x\n", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c);
 
 			if (adcout > 13) {
 				if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15))
@@ -232,7 +229,7 @@
 	pllf = (u32) tmp;
 
 	state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF;
-	deb("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d", freq_khz, state->frequency, pllf, plln);
+	itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln);
 
 	itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */;
 	itd1000_write_reg(state, PLLNL, plln & 0xff);
@@ -242,7 +239,7 @@
 
 	for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) {
 		if (freq_khz <= itd1000_fre_values[i].freq) {
-			deb("fre_values: %d", i);
+			itd_dbg("fre_values: %d\n", i);
 			itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]);
 			for (j = 0; j < 9; j++)
 				itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]);
@@ -382,7 +379,7 @@
 		kfree(state);
 		return NULL;
 	}
-	info("successfully identified (ID: %d)", i);
+	itd_info("successfully identified (ID: %d)\n", i);
 
 	memset(state->shadow, 0xff, sizeof(state->shadow));
 	for (i = 0x65; i < 0x9c; i++)
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
index a763ec75..6599b8f 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -50,7 +50,7 @@
 	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
 		dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret);
 
-	return (ret != 1) ? -EFAULT : 0;
+	return (ret != 1) ? -EIO : 0;
 }
 
 static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg)
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
index 17f8cdf..3879d2e 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -634,7 +634,7 @@
 	struct s5h1420_state* state = fe->demodulator_priv;
 	int frequency_delta;
 	struct dvb_frontend_tune_settings fesettings;
-	uint8_t clock_settting;
+	uint8_t clock_setting;
 
 	dprintk("enter %s\n", __func__);
 
@@ -684,19 +684,19 @@
 	switch (state->fclk) {
 	default:
 	case 88000000:
-		clock_settting = 80;
+		clock_setting = 80;
 		break;
 	case 86000000:
-		clock_settting = 78;
+		clock_setting = 78;
 		break;
 	case 80000000:
-		clock_settting = 72;
+		clock_setting = 72;
 		break;
 	case 59000000:
-		clock_settting = 51;
+		clock_setting = 51;
 		break;
 	case 44000000:
-		clock_settting = 36;
+		clock_setting = 36;
 		break;
 	}
 	dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c
new file mode 100644
index 0000000..0384e8d
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd.c
@@ -0,0 +1,1251 @@
+/*
+ * tda18271c2dd: Driver for the TDA18271C2 tuner
+ *
+ * Copyright (C) 2010 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+
+struct SStandardParam {
+	s32   m_IFFrequency;
+	u32   m_BandWidth;
+	u8    m_EP3_4_0;
+	u8    m_EB22;
+};
+
+struct SMap {
+	u32   m_Frequency;
+	u8    m_Param;
+};
+
+struct SMapI {
+	u32   m_Frequency;
+	s32    m_Param;
+};
+
+struct SMap2 {
+	u32   m_Frequency;
+	u8    m_Param1;
+	u8    m_Param2;
+};
+
+struct SRFBandMap {
+	u32   m_RF_max;
+	u32   m_RF1_Default;
+	u32   m_RF2_Default;
+	u32   m_RF3_Default;
+};
+
+enum ERegister {
+	ID = 0,
+	TM,
+	PL,
+	EP1, EP2, EP3, EP4, EP5,
+	CPD, CD1, CD2, CD3,
+	MPD, MD1, MD2, MD3,
+	EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10,
+	EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20,
+	EB21, EB22, EB23,
+	NUM_REGS
+};
+
+struct tda_state {
+	struct i2c_adapter *i2c;
+	u8 adr;
+
+	u32   m_Frequency;
+	u32   IF;
+
+	u8    m_IFLevelAnalog;
+	u8    m_IFLevelDigital;
+	u8    m_IFLevelDVBC;
+	u8    m_IFLevelDVBT;
+
+	u8    m_EP4;
+	u8    m_EP3_Standby;
+
+	bool  m_bMaster;
+
+	s32   m_SettlingTime;
+
+	u8    m_Regs[NUM_REGS];
+
+	/* Tracking filter settings for band 0..6 */
+	u32   m_RF1[7];
+	s32   m_RF_A1[7];
+	s32   m_RF_B1[7];
+	u32   m_RF2[7];
+	s32   m_RF_A2[7];
+	s32   m_RF_B2[7];
+	u32   m_RF3[7];
+
+	u8    m_TMValue_RFCal;    /* Calibration temperatur */
+
+	bool  m_bFMInput;         /* true to use Pin 8 for FM Radio */
+
+};
+
+static int PowerScan(struct tda_state *state,
+		     u8 RFBand, u32 RF_in,
+		     u32 *pRF_Out, bool *pbcal);
+
+static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len)
+{
+	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = data, .len   = len} };
+	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+	struct i2c_msg msg = {.addr = adr, .flags = 0,
+			      .buf = data, .len = len};
+
+	if (i2c_transfer(adap, &msg, 1) != 1) {
+		printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr);
+		return -1;
+	}
+	return 0;
+}
+
+static int WriteRegs(struct tda_state *state,
+		     u8 SubAddr, u8 *Regs, u16 nRegs)
+{
+	u8 data[nRegs+1];
+
+	data[0] = SubAddr;
+	memcpy(data + 1, Regs, nRegs);
+	return i2c_write(state->i2c, state->adr, data, nRegs+1);
+}
+
+static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg)
+{
+	u8 msg[2] = {SubAddr, Reg};
+
+	return i2c_write(state->i2c, state->adr, msg, 2);
+}
+
+static int Read(struct tda_state *state, u8 * Regs)
+{
+	return i2c_readn(state->i2c, state->adr, Regs, 16);
+}
+
+static int ReadExtented(struct tda_state *state, u8 * Regs)
+{
+	return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS);
+}
+
+static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo)
+{
+	return WriteRegs(state, RegFrom,
+			 &state->m_Regs[RegFrom], RegTo-RegFrom+1);
+}
+static int UpdateReg(struct tda_state *state, u8 Reg)
+{
+	return WriteReg(state, Reg, state->m_Regs[Reg]);
+}
+
+#include "tda18271c2dd_maps.h"
+
+static void reset(struct tda_state *state)
+{
+	u32   ulIFLevelAnalog = 0;
+	u32   ulIFLevelDigital = 2;
+	u32   ulIFLevelDVBC = 7;
+	u32   ulIFLevelDVBT = 6;
+	u32   ulXTOut = 0;
+	u32   ulStandbyMode = 0x06;    /* Send in stdb, but leave osc on */
+	u32   ulSlave = 0;
+	u32   ulFMInput = 0;
+	u32   ulSettlingTime = 100;
+
+	state->m_Frequency         = 0;
+	state->m_SettlingTime = 100;
+	state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2;
+	state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2;
+	state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2;
+	state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2;
+
+	state->m_EP4 = 0x20;
+	if (ulXTOut != 0)
+		state->m_EP4 |= 0x40;
+
+	state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F;
+	state->m_bMaster = (ulSlave == 0);
+
+	state->m_SettlingTime = ulSettlingTime;
+
+	state->m_bFMInput = (ulFMInput == 2);
+}
+
+static bool SearchMap1(struct SMap Map[],
+		       u32 Frequency, u8 *pParam)
+{
+	int i = 0;
+
+	while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency))
+		i += 1;
+	if (Map[i].m_Frequency == 0)
+		return false;
+	*pParam = Map[i].m_Param;
+	return true;
+}
+
+static bool SearchMap2(struct SMapI Map[],
+		       u32 Frequency, s32 *pParam)
+{
+	int i = 0;
+
+	while ((Map[i].m_Frequency != 0) &&
+	       (Frequency > Map[i].m_Frequency))
+		i += 1;
+	if (Map[i].m_Frequency == 0)
+		return false;
+	*pParam = Map[i].m_Param;
+	return true;
+}
+
+static bool SearchMap3(struct SMap2 Map[], u32 Frequency,
+		       u8 *pParam1, u8 *pParam2)
+{
+	int i = 0;
+
+	while ((Map[i].m_Frequency != 0) &&
+	       (Frequency > Map[i].m_Frequency))
+		i += 1;
+	if (Map[i].m_Frequency == 0)
+		return false;
+	*pParam1 = Map[i].m_Param1;
+	*pParam2 = Map[i].m_Param2;
+	return true;
+}
+
+static bool SearchMap4(struct SRFBandMap Map[],
+		       u32 Frequency, u8 *pRFBand)
+{
+	int i = 0;
+
+	while (i < 7 && (Frequency > Map[i].m_RF_max))
+		i += 1;
+	if (i == 7)
+		return false;
+	*pRFBand = i;
+	return true;
+}
+
+static int ThermometerRead(struct tda_state *state, u8 *pTM_Value)
+{
+	int status = 0;
+
+	do {
+		u8 Regs[16];
+		state->m_Regs[TM] |= 0x10;
+		status = UpdateReg(state, TM);
+		if (status < 0)
+			break;
+		status = Read(state, Regs);
+		if (status < 0)
+			break;
+		if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) ||
+		    ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) {
+			state->m_Regs[TM] ^= 0x20;
+			status = UpdateReg(state, TM);
+			if (status < 0)
+				break;
+			msleep(10);
+			status = Read(state, Regs);
+			if (status < 0)
+				break;
+		}
+		*pTM_Value = (Regs[TM] & 0x20)
+				? m_Thermometer_Map_2[Regs[TM] & 0x0F]
+				: m_Thermometer_Map_1[Regs[TM] & 0x0F] ;
+		state->m_Regs[TM] &= ~0x10;        /* Thermometer off */
+		status = UpdateReg(state, TM);
+		if (status < 0)
+			break;
+		state->m_Regs[EP4] &= ~0x03;       /* CAL_mode = 0 ????????? */
+		status = UpdateReg(state, EP4);
+		if (status < 0)
+			break;
+	} while (0);
+
+	return status;
+}
+
+static int StandBy(struct tda_state *state)
+{
+	int status = 0;
+	do {
+		state->m_Regs[EB12] &= ~0x20;  /* PD_AGC1_Det = 0 */
+		status = UpdateReg(state, EB12);
+		if (status < 0)
+			break;
+		state->m_Regs[EB18] &= ~0x83;  /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */
+		status = UpdateReg(state, EB18);
+		if (status < 0)
+			break;
+		state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */
+		state->m_Regs[EP3] = state->m_EP3_Standby;
+		status = UpdateReg(state, EP3);
+		if (status < 0)
+			break;
+		state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */
+		status = UpdateRegs(state, EB21, EB23);
+		if (status < 0)
+			break;
+	} while (0);
+	return status;
+}
+
+static int CalcMainPLL(struct tda_state *state, u32 freq)
+{
+
+	u8  PostDiv;
+	u8  Div;
+	u64 OscFreq;
+	u32 MainDiv;
+
+	if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div))
+		return -EINVAL;
+
+	OscFreq = (u64) freq * (u64) Div;
+	OscFreq *= (u64) 16384;
+	do_div(OscFreq, (u64)16000000);
+	MainDiv = OscFreq;
+
+	state->m_Regs[MPD] = PostDiv & 0x77;
+	state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F);
+	state->m_Regs[MD2] = ((MainDiv >>  8) & 0xFF);
+	state->m_Regs[MD3] = (MainDiv & 0xFF);
+
+	return UpdateRegs(state, MPD, MD3);
+}
+
+static int CalcCalPLL(struct tda_state *state, u32 freq)
+{
+	u8 PostDiv;
+	u8 Div;
+	u64 OscFreq;
+	u32 CalDiv;
+
+	if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div))
+		return -EINVAL;
+
+	OscFreq = (u64)freq * (u64)Div;
+	/* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
+	OscFreq *= (u64)16384;
+	do_div(OscFreq, (u64)16000000);
+	CalDiv = OscFreq;
+
+	state->m_Regs[CPD] = PostDiv;
+	state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF);
+	state->m_Regs[CD2] = ((CalDiv >>  8) & 0xFF);
+	state->m_Regs[CD3] = (CalDiv & 0xFF);
+
+	return UpdateRegs(state, CPD, CD3);
+}
+
+static int CalibrateRF(struct tda_state *state,
+		       u8 RFBand, u32 freq, s32 *pCprog)
+{
+	int status = 0;
+	u8 Regs[NUM_REGS];
+	do {
+		u8 BP_Filter = 0;
+		u8 GainTaper = 0;
+		u8 RFC_K = 0;
+		u8 RFC_M = 0;
+
+		state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */
+		status = UpdateReg(state, EP4);
+		if (status < 0)
+			break;
+		state->m_Regs[EB18] |= 0x03;  /* AGC1_Gain = 3 */
+		status = UpdateReg(state, EB18);
+		if (status < 0)
+			break;
+
+		/* Switching off LT (as datasheet says) causes calibration on C1 to fail */
+		/* (Readout of Cprog is allways 255) */
+		if (state->m_Regs[ID] != 0x83)    /* C1: ID == 83, C2: ID == 84 */
+			state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */
+
+		if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) &&
+			SearchMap1(m_GainTaper_Map, freq, &GainTaper) &&
+			SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M)))
+			return -EINVAL;
+
+		state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter;
+		state->m_Regs[EP2] = (RFBand << 5) | GainTaper;
+
+		state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2);
+
+		status = UpdateRegs(state, EP1, EP3);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EB13);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB4] |= 0x20;    /* LO_ForceSrce = 1 */
+		status = UpdateReg(state, EB4);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB7] |= 0x20;    /* CAL_ForceSrce = 1 */
+		status = UpdateReg(state, EB7);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */
+		status = UpdateReg(state, EB14);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB20] &= ~0x20;  /* ForceLock = 0; */
+		status = UpdateReg(state, EB20);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EP4] |= 0x03;  /* CAL_Mode = 3 */
+		status = UpdateRegs(state, EP4, EP5);
+		if (status < 0)
+			break;
+
+		status = CalcCalPLL(state, freq);
+		if (status < 0)
+			break;
+		status = CalcMainPLL(state, freq + 1000000);
+		if (status < 0)
+			break;
+
+		msleep(5);
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB4] &= ~0x20;    /* LO_ForceSrce = 0 */
+		status = UpdateReg(state, EB4);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EB7] &= ~0x20;    /* CAL_ForceSrce = 0 */
+		status = UpdateReg(state, EB7);
+		if (status < 0)
+			break;
+		msleep(10);
+
+		state->m_Regs[EB20] |= 0x20;  /* ForceLock = 1; */
+		status = UpdateReg(state, EB20);
+		if (status < 0)
+			break;
+		msleep(60);
+
+		state->m_Regs[EP4] &= ~0x03;  /* CAL_Mode = 0 */
+		state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */
+		state->m_Regs[EB18] &= ~0x03;  /* AGC1_Gain = 0 */
+		status = UpdateReg(state, EB18);
+		if (status < 0)
+			break;
+		status = UpdateRegs(state, EP3, EP4);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+
+		status = ReadExtented(state, Regs);
+		if (status < 0)
+			break;
+
+		*pCprog = Regs[EB14];
+
+	} while (0);
+	return status;
+}
+
+static int RFTrackingFiltersInit(struct tda_state *state,
+				 u8 RFBand)
+{
+	int status = 0;
+
+	u32   RF1 = m_RF_Band_Map[RFBand].m_RF1_Default;
+	u32   RF2 = m_RF_Band_Map[RFBand].m_RF2_Default;
+	u32   RF3 = m_RF_Band_Map[RFBand].m_RF3_Default;
+	bool    bcal = false;
+
+	s32    Cprog_cal1 = 0;
+	s32    Cprog_table1 = 0;
+	s32    Cprog_cal2 = 0;
+	s32    Cprog_table2 = 0;
+	s32    Cprog_cal3 = 0;
+	s32    Cprog_table3 = 0;
+
+	state->m_RF_A1[RFBand] = 0;
+	state->m_RF_B1[RFBand] = 0;
+	state->m_RF_A2[RFBand] = 0;
+	state->m_RF_B2[RFBand] = 0;
+
+	do {
+		status = PowerScan(state, RFBand, RF1, &RF1, &bcal);
+		if (status < 0)
+			break;
+		if (bcal) {
+			status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1);
+			if (status < 0)
+				break;
+		}
+		SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1);
+		if (!bcal)
+			Cprog_cal1 = Cprog_table1;
+		state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1;
+		/* state->m_RF_A1[RF_Band] = ???? */
+
+		if (RF2 == 0)
+			break;
+
+		status = PowerScan(state, RFBand, RF2, &RF2, &bcal);
+		if (status < 0)
+			break;
+		if (bcal) {
+			status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2);
+			if (status < 0)
+				break;
+		}
+		SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2);
+		if (!bcal)
+			Cprog_cal2 = Cprog_table2;
+
+		state->m_RF_A1[RFBand] =
+			(Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) /
+			((s32)(RF2) - (s32)(RF1));
+
+		if (RF3 == 0)
+			break;
+
+		status = PowerScan(state, RFBand, RF3, &RF3, &bcal);
+		if (status < 0)
+			break;
+		if (bcal) {
+			status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3);
+			if (status < 0)
+				break;
+		}
+		SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3);
+		if (!bcal)
+			Cprog_cal3 = Cprog_table3;
+		state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2));
+		state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2;
+
+	} while (0);
+
+	state->m_RF1[RFBand] = RF1;
+	state->m_RF2[RFBand] = RF2;
+	state->m_RF3[RFBand] = RF3;
+
+#if 0
+	printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__,
+	       RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2,
+	       state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3);
+#endif
+
+	return status;
+}
+
+static int PowerScan(struct tda_state *state,
+		     u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal)
+{
+	int status = 0;
+	do {
+		u8   Gain_Taper = 0;
+		s32  RFC_Cprog = 0;
+		u8   CID_Target = 0;
+		u8   CountLimit = 0;
+		u32  freq_MainPLL;
+		u8   Regs[NUM_REGS];
+		u8   CID_Gain;
+		s32  Count = 0;
+		int  sign  = 1;
+		bool wait = false;
+
+		if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) &&
+		      SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) &&
+		      SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) {
+
+			printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__);
+			return -EINVAL;
+		}
+
+		state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper;
+		state->m_Regs[EB14] = (RFC_Cprog);
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EB14);
+		if (status < 0)
+			break;
+
+		freq_MainPLL = RF_in + 1000000;
+		status = CalcMainPLL(state, freq_MainPLL);
+		if (status < 0)
+			break;
+		msleep(5);
+		state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1;    /* CAL_mode = 1 */
+		status = UpdateReg(state, EP4);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP2);  /* Launch power measurement */
+		if (status < 0)
+			break;
+		status = ReadExtented(state, Regs);
+		if (status < 0)
+			break;
+		CID_Gain = Regs[EB10] & 0x3F;
+		state->m_Regs[ID] = Regs[ID];  /* Chip version, (needed for C1 workarround in CalibrateRF) */
+
+		*pRF_Out = RF_in;
+
+		while (CID_Gain < CID_Target) {
+			freq_MainPLL = RF_in + sign * Count + 1000000;
+			status = CalcMainPLL(state, freq_MainPLL);
+			if (status < 0)
+				break;
+			msleep(wait ? 5 : 1);
+			wait = false;
+			status = UpdateReg(state, EP2);  /* Launch power measurement */
+			if (status < 0)
+				break;
+			status = ReadExtented(state, Regs);
+			if (status < 0)
+				break;
+			CID_Gain = Regs[EB10] & 0x3F;
+			Count += 200000;
+
+			if (Count < CountLimit * 100000)
+				continue;
+			if (sign < 0)
+				break;
+
+			sign = -sign;
+			Count = 200000;
+			wait = true;
+		}
+		status = status;
+		if (status < 0)
+			break;
+		if (CID_Gain >= CID_Target) {
+			*pbcal = true;
+			*pRF_Out = freq_MainPLL - 1000000;
+		} else
+			*pbcal = false;
+	} while (0);
+
+	return status;
+}
+
+static int PowerScanInit(struct tda_state *state)
+{
+	int status = 0;
+	do {
+		state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12;
+		state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */
+		status = UpdateRegs(state, EP3, EP4);
+		if (status < 0)
+			break;
+		state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */
+		status = UpdateReg(state, EB18);
+		if (status < 0)
+			break;
+		state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */
+		state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
+		status = UpdateRegs(state, EB21, EB23);
+		if (status < 0)
+			break;
+	} while (0);
+	return status;
+}
+
+static int CalcRFFilterCurve(struct tda_state *state)
+{
+	int status = 0;
+	do {
+		msleep(200);      /* Temperature stabilisation */
+		status = PowerScanInit(state);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 0);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 1);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 2);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 3);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 4);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 5);
+		if (status < 0)
+			break;
+		status = RFTrackingFiltersInit(state, 6);
+		if (status < 0)
+			break;
+		status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */
+		if (status < 0)
+			break;
+	} while (0);
+
+	return status;
+}
+
+static int FixedContentsI2CUpdate(struct tda_state *state)
+{
+	static u8 InitRegs[] = {
+		0x08, 0x80, 0xC6,
+		0xDF, 0x16, 0x60, 0x80,
+		0x80, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+		0xFC, 0x01, 0x84, 0x41,
+		0x01, 0x84, 0x40, 0x07,
+		0x00, 0x00, 0x96, 0x3F,
+		0xC1, 0x00, 0x8F, 0x00,
+		0x00, 0x8C, 0x00, 0x20,
+		0xB3, 0x48, 0xB0,
+	};
+	int status = 0;
+	memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1);
+	do {
+		status = UpdateRegs(state, TM, EB23);
+		if (status < 0)
+			break;
+
+		/* AGC1 gain setup */
+		state->m_Regs[EB17] = 0x00;
+		status = UpdateReg(state, EB17);
+		if (status < 0)
+			break;
+		state->m_Regs[EB17] = 0x03;
+		status = UpdateReg(state, EB17);
+		if (status < 0)
+			break;
+		state->m_Regs[EB17] = 0x43;
+		status = UpdateReg(state, EB17);
+		if (status < 0)
+			break;
+		state->m_Regs[EB17] = 0x4C;
+		status = UpdateReg(state, EB17);
+		if (status < 0)
+			break;
+
+		/* IRC Cal Low band */
+		state->m_Regs[EP3] = 0x1F;
+		state->m_Regs[EP4] = 0x66;
+		state->m_Regs[EP5] = 0x81;
+		state->m_Regs[CPD] = 0xCC;
+		state->m_Regs[CD1] = 0x6C;
+		state->m_Regs[CD2] = 0x00;
+		state->m_Regs[CD3] = 0x00;
+		state->m_Regs[MPD] = 0xC5;
+		state->m_Regs[MD1] = 0x77;
+		state->m_Regs[MD2] = 0x08;
+		state->m_Regs[MD3] = 0x00;
+		status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */
+		if (status < 0)
+			break;
+
+#if 0
+		state->m_Regs[EB4] = 0x61;          /* missing in sw */
+		status = UpdateReg(state, EB4);
+		if (status < 0)
+			break;
+		msleep(1);
+		state->m_Regs[EB4] = 0x41;
+		status = UpdateReg(state, EB4);
+		if (status < 0)
+			break;
+#endif
+
+		msleep(5);
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+		msleep(5);
+
+		state->m_Regs[EP5] = 0x85;
+		state->m_Regs[CPD] = 0xCB;
+		state->m_Regs[CD1] = 0x66;
+		state->m_Regs[CD2] = 0x70;
+		status = UpdateRegs(state, EP3, CD3);
+		if (status < 0)
+			break;
+		msleep(5);
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		msleep(30);
+
+		/* IRC Cal mid band */
+		state->m_Regs[EP5] = 0x82;
+		state->m_Regs[CPD] = 0xA8;
+		state->m_Regs[CD2] = 0x00;
+		state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */
+		state->m_Regs[MD1] = 0x73;
+		state->m_Regs[MD2] = 0x1A;
+		status = UpdateRegs(state, EP3, MD3);
+		if (status < 0)
+			break;
+
+		msleep(5);
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+		msleep(5);
+
+		state->m_Regs[EP5] = 0x86;
+		state->m_Regs[CPD] = 0xA8;
+		state->m_Regs[CD1] = 0x66;
+		state->m_Regs[CD2] = 0xA0;
+		status = UpdateRegs(state, EP3, CD3);
+		if (status < 0)
+			break;
+		msleep(5);
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		msleep(30);
+
+		/* IRC Cal high band */
+		state->m_Regs[EP5] = 0x83;
+		state->m_Regs[CPD] = 0x98;
+		state->m_Regs[CD1] = 0x65;
+		state->m_Regs[CD2] = 0x00;
+		state->m_Regs[MPD] = 0x91;  /* Datasheet = 0x91 */
+		state->m_Regs[MD1] = 0x71;
+		state->m_Regs[MD2] = 0xCD;
+		status = UpdateRegs(state, EP3, MD3);
+		if (status < 0)
+			break;
+		msleep(5);
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+		msleep(5);
+		state->m_Regs[EP5] = 0x87;
+		state->m_Regs[CD1] = 0x65;
+		state->m_Regs[CD2] = 0x50;
+		status = UpdateRegs(state, EP3, CD3);
+		if (status < 0)
+			break;
+		msleep(5);
+		status = UpdateReg(state, EP2);
+		if (status < 0)
+			break;
+		msleep(30);
+
+		/* Back to normal */
+		state->m_Regs[EP4] = 0x64;
+		status = UpdateReg(state, EP4);
+		if (status < 0)
+			break;
+		status = UpdateReg(state, EP1);
+		if (status < 0)
+			break;
+
+	} while (0);
+	return status;
+}
+
+static int InitCal(struct tda_state *state)
+{
+	int status = 0;
+
+	do {
+		status = FixedContentsI2CUpdate(state);
+		if (status < 0)
+			break;
+		status = CalcRFFilterCurve(state);
+		if (status < 0)
+			break;
+		status = StandBy(state);
+		if (status < 0)
+			break;
+		/* m_bInitDone = true; */
+	} while (0);
+	return status;
+};
+
+static int RFTrackingFiltersCorrection(struct tda_state *state,
+				       u32 Frequency)
+{
+	int status = 0;
+	s32 Cprog_table;
+	u8 RFBand;
+	u8 dCoverdT;
+
+	if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) ||
+	    !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) ||
+	    !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT))
+
+		return -EINVAL;
+
+	do {
+		u8 TMValue_Current;
+		u32   RF1 = state->m_RF1[RFBand];
+		u32   RF2 = state->m_RF1[RFBand];
+		u32   RF3 = state->m_RF1[RFBand];
+		s32    RF_A1 = state->m_RF_A1[RFBand];
+		s32    RF_B1 = state->m_RF_B1[RFBand];
+		s32    RF_A2 = state->m_RF_A2[RFBand];
+		s32    RF_B2 = state->m_RF_B2[RFBand];
+		s32 Capprox = 0;
+		int TComp;
+
+		state->m_Regs[EP3] &= ~0xE0;  /* Power up */
+		status = UpdateReg(state, EP3);
+		if (status < 0)
+			break;
+
+		status = ThermometerRead(state, &TMValue_Current);
+		if (status < 0)
+			break;
+
+		if (RF3 == 0 || Frequency < RF2)
+			Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table;
+		else
+			Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table;
+
+		TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000;
+
+		Capprox += TComp;
+
+		if (Capprox < 0)
+			Capprox = 0;
+		else if (Capprox > 255)
+			Capprox = 255;
+
+
+		/* TODO Temperature compensation. There is defenitely a scale factor */
+		/*      missing in the datasheet, so leave it out for now.           */
+		state->m_Regs[EB14] = Capprox;
+
+		status = UpdateReg(state, EB14);
+		if (status < 0)
+			break;
+
+	} while (0);
+	return status;
+}
+
+static int ChannelConfiguration(struct tda_state *state,
+				u32 Frequency, int Standard)
+{
+
+	s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency;
+	int status = 0;
+
+	u8 BP_Filter = 0;
+	u8 RF_Band = 0;
+	u8 GainTaper = 0;
+	u8 IR_Meas = 0;
+
+	state->IF = IntermediateFrequency;
+	/* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */
+	/* get values from tables */
+
+	if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) &&
+	       SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) &&
+	       SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) &&
+	       SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) {
+
+		printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__);
+		return -EINVAL;
+	}
+
+	do {
+		state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0;
+		state->m_Regs[EP3] &= ~0x04;   /* switch RFAGC to high speed mode */
+
+		/* m_EP4 default for XToutOn, CAL_Mode (0) */
+		state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog);
+		/* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */
+		if (Standard <= HF_AnalogMax)
+			state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog;
+		else if (Standard <= HF_ATSC)
+			state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT;
+		else if (Standard <= HF_DVBC)
+			state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC;
+		else
+			state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital;
+
+		if ((Standard == HF_FM_Radio) && state->m_bFMInput)
+			state->m_Regs[EP4] |= 80;
+
+		state->m_Regs[MPD] &= ~0x80;
+		if (Standard > HF_AnalogMax)
+			state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */
+
+		state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22;
+
+		/* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */
+		if (Standard == HF_FM_Radio)
+			state->m_Regs[EB23] |=  0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */
+		else
+			state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */
+
+		status = UpdateRegs(state, EB22, EB23);
+		if (status < 0)
+			break;
+
+		state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter;   /* Dis_Power_level = 1, Filter */
+		state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas;
+		state->m_Regs[EP2] = (RF_Band << 5) | GainTaper;
+
+		state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) |
+			(state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */
+		/* AGC1_always_master = 0 */
+		/* AGC_firstn = 0 */
+		status = UpdateReg(state, EB1);
+		if (status < 0)
+			break;
+
+		if (state->m_bMaster) {
+			status = CalcMainPLL(state, Frequency + IntermediateFrequency);
+			if (status < 0)
+				break;
+			status = UpdateRegs(state, TM, EP5);
+			if (status < 0)
+				break;
+			state->m_Regs[EB4] |= 0x20;    /* LO_forceSrce = 1 */
+			status = UpdateReg(state, EB4);
+			if (status < 0)
+				break;
+			msleep(1);
+			state->m_Regs[EB4] &= ~0x20;   /* LO_forceSrce = 0 */
+			status = UpdateReg(state, EB4);
+			if (status < 0)
+				break;
+		} else {
+			u8 PostDiv = 0;
+			u8 Div;
+			status = CalcCalPLL(state, Frequency + IntermediateFrequency);
+			if (status < 0)
+				break;
+
+			SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div);
+			state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77);
+			status = UpdateReg(state, MPD);
+			if (status < 0)
+				break;
+			status = UpdateRegs(state, TM, EP5);
+			if (status < 0)
+				break;
+
+			state->m_Regs[EB7] |= 0x20;    /* CAL_forceSrce = 1 */
+			status = UpdateReg(state, EB7);
+			if (status < 0)
+				break;
+			msleep(1);
+			state->m_Regs[EB7] &= ~0x20;   /* CAL_forceSrce = 0 */
+			status = UpdateReg(state, EB7);
+			if (status < 0)
+				break;
+		}
+		msleep(20);
+		if (Standard != HF_FM_Radio)
+			state->m_Regs[EP3] |= 0x04;    /* RFAGC to normal mode */
+		status = UpdateReg(state, EP3);
+		if (status < 0)
+			break;
+
+	} while (0);
+	return status;
+}
+
+static int sleep(struct dvb_frontend *fe)
+{
+	struct tda_state *state = fe->tuner_priv;
+
+	StandBy(state);
+	return 0;
+}
+
+static int init(struct dvb_frontend *fe)
+{
+	return 0;
+}
+
+static int release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+/*
+ * As defined on EN 300 429 Annex A and on ITU-T J.83 annex A, the DVB-C
+ * roll-off factor is 0.15.
+ * According with the specs, the amount of the needed bandwith is given by:
+ *	Bw = Symbol_rate * (1 + 0.15)
+ * As such, the maximum symbol rate supported by 6 MHz is
+ *	max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
+ *NOTE: For ITU-T J.83 Annex C, the roll-off factor is 0.13. So:
+ *	max_symbol_rate = 6 MHz / 1.13 = 5309735 Baud
+ *	That means that an adjustment is needed for Japan,
+ *	but, as currently DRX-K is hardcoded to Annex A, let's stick
+ *	with 0.15 roll-off factor.
+ */
+#define MAX_SYMBOL_RATE_6MHz	5217391
+
+static int set_params(struct dvb_frontend *fe,
+		      struct dvb_frontend_parameters *params)
+{
+	struct tda_state *state = fe->tuner_priv;
+	int status = 0;
+	int Standard;
+
+	state->m_Frequency = params->frequency;
+
+	if (fe->ops.info.type == FE_OFDM)
+		switch (params->u.ofdm.bandwidth) {
+		case BANDWIDTH_6_MHZ:
+			Standard = HF_DVBT_6MHZ;
+			break;
+		case BANDWIDTH_7_MHZ:
+			Standard = HF_DVBT_7MHZ;
+			break;
+		default:
+		case BANDWIDTH_8_MHZ:
+			Standard = HF_DVBT_8MHZ;
+			break;
+		}
+	else if (fe->ops.info.type == FE_QAM) {
+		if (params->u.qam.symbol_rate <= MAX_SYMBOL_RATE_6MHz)
+			Standard = HF_DVBC_6MHZ;
+		else
+			Standard = HF_DVBC_8MHZ;
+	} else
+		return -EINVAL;
+	do {
+		status = RFTrackingFiltersCorrection(state, params->frequency);
+		if (status < 0)
+			break;
+		status = ChannelConfiguration(state, params->frequency, Standard);
+		if (status < 0)
+			break;
+
+		msleep(state->m_SettlingTime);  /* Allow AGC's to settle down */
+	} while (0);
+	return status;
+}
+
+#if 0
+static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc)
+{
+	if (IFAgc < 500) {
+		/* Scale this from 0 to 50000 */
+		*pSignalStrength = IFAgc * 100;
+	} else {
+		/* Scale range 500-1500 to 50000-80000 */
+		*pSignalStrength = 50000 + (IFAgc - 500) * 30;
+	}
+
+	return 0;
+}
+#endif
+
+static int get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct tda_state *state = fe->tuner_priv;
+
+	*frequency = state->IF;
+	return 0;
+}
+
+static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	/* struct tda_state *state = fe->tuner_priv; */
+	/* *bandwidth = priv->bandwidth; */
+	return 0;
+}
+
+
+static struct dvb_tuner_ops tuner_ops = {
+	.info = {
+		.name = "NXP TDA18271C2D",
+		.frequency_min  =  47125000,
+		.frequency_max  = 865000000,
+		.frequency_step =     62500
+	},
+	.init              = init,
+	.sleep             = sleep,
+	.set_params        = set_params,
+	.release           = release,
+	.get_frequency     = get_frequency,
+	.get_bandwidth     = get_bandwidth,
+};
+
+struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+					 struct i2c_adapter *i2c, u8 adr)
+{
+	struct tda_state *state;
+
+	state = kzalloc(sizeof(struct tda_state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	fe->tuner_priv = state;
+	state->adr = adr;
+	state->i2c = i2c;
+	memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops));
+	reset(state);
+	InitCal(state);
+
+	return fe;
+}
+EXPORT_SYMBOL_GPL(tda18271c2dd_attach);
+
+MODULE_DESCRIPTION("TDA18271C2 driver");
+MODULE_AUTHOR("DD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.h b/drivers/media/dvb/frontends/tda18271c2dd.h
new file mode 100644
index 0000000..1389c74
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd.h
@@ -0,0 +1,16 @@
+#ifndef _TDA18271C2DD_H_
+#define _TDA18271C2DD_H_
+#if defined(CONFIG_DVB_TDA18271C2DD) || (defined(CONFIG_DVB_TDA18271C2DD_MODULE) \
+        && defined(MODULE))
+struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+					 struct i2c_adapter *i2c, u8 adr);
+#else
+static inline struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
+					 struct i2c_adapter *i2c, u8 adr)
+{
+        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+        return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/tda18271c2dd_maps.h b/drivers/media/dvb/frontends/tda18271c2dd_maps.h
new file mode 100644
index 0000000..b87661b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271c2dd_maps.h
@@ -0,0 +1,814 @@
+enum HF_S {
+	HF_None = 0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio,
+	HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ,
+	HF_DVBT, HF_ATSC,  HF_DVBC_6MHZ,  HF_DVBC_7MHZ,
+	HF_DVBC_8MHZ, HF_DVBC
+};
+
+struct SStandardParam m_StandardTable[] = {
+	{       0,        0, 0x00, 0x00 },    /* HF_None */
+	{ 6000000,  7000000, 0x1D, 0x2C },    /* HF_B, */
+	{ 6900000,  8000000, 0x1E, 0x2C },    /* HF_DK, */
+	{ 7100000,  8000000, 0x1E, 0x2C },    /* HF_G, */
+	{ 7250000,  8000000, 0x1E, 0x2C },    /* HF_I, */
+	{ 6900000,  8000000, 0x1E, 0x2C },    /* HF_L, */
+	{ 1250000,  8000000, 0x1E, 0x2C },    /* HF_L1, */
+	{ 5400000,  6000000, 0x1C, 0x2C },    /* HF_MN, */
+	{ 1250000,   500000, 0x18, 0x2C },    /* HF_FM_Radio, */
+	{       0,        0, 0x00, 0x00 },    /* HF_AnalogMax (Unused) */
+	{ 3300000,  6000000, 0x1C, 0x58 },    /* HF_DVBT_6MHZ */
+	{ 3500000,  7000000, 0x1C, 0x37 },    /* HF_DVBT_7MHZ */
+	{ 4000000,  8000000, 0x1D, 0x37 },    /* HF_DVBT_8MHZ */
+	{       0,        0, 0x00, 0x00 },    /* HF_DVBT (Unused) */
+	{ 5000000,  6000000, 0x1C, 0x37 },    /* HF_ATSC  (center = 3.25 MHz) */
+	{ 4000000,  6000000, 0x1D, 0x58 },    /* HF_DVBC_6MHZ (Chicago) */
+	{ 4500000,  7000000, 0x1E, 0x37 },    /* HF_DVBC_7MHZ (not documented by NXP) */
+	{ 5000000,  8000000, 0x1F, 0x37 },    /* HF_DVBC_8MHZ */
+	{       0,        0, 0x00, 0x00 },    /* HF_DVBC (Unused) */
+};
+
+struct SMap  m_BP_Filter_Map[] = {
+	{   62000000,  0x00 },
+	{   84000000,  0x01 },
+	{  100000000,  0x02 },
+	{  140000000,  0x03 },
+	{  170000000,  0x04 },
+	{  180000000,  0x05 },
+	{  865000000,  0x06 },
+	{          0,  0x00 },    /* Table End */
+};
+
+static struct SMapI m_RF_Cal_Map[] = {
+	{   41000000,  0x0F },
+	{   43000000,  0x1C },
+	{   45000000,  0x2F },
+	{   46000000,  0x39 },
+	{   47000000,  0x40 },
+	{   47900000,  0x50 },
+	{   49100000,  0x16 },
+	{   50000000,  0x18 },
+	{   51000000,  0x20 },
+	{   53000000,  0x28 },
+	{   55000000,  0x2B },
+	{   56000000,  0x32 },
+	{   57000000,  0x35 },
+	{   58000000,  0x3E },
+	{   59000000,  0x43 },
+	{   60000000,  0x4E },
+	{   61100000,  0x55 },
+	{   63000000,  0x0F },
+	{   64000000,  0x11 },
+	{   65000000,  0x12 },
+	{   66000000,  0x15 },
+	{   67000000,  0x16 },
+	{   68000000,  0x17 },
+	{   70000000,  0x19 },
+	{   71000000,  0x1C },
+	{   72000000,  0x1D },
+	{   73000000,  0x1F },
+	{   74000000,  0x20 },
+	{   75000000,  0x21 },
+	{   76000000,  0x24 },
+	{   77000000,  0x25 },
+	{   78000000,  0x27 },
+	{   80000000,  0x28 },
+	{   81000000,  0x29 },
+	{   82000000,  0x2D },
+	{   83000000,  0x2E },
+	{   84000000,  0x2F },
+	{   85000000,  0x31 },
+	{   86000000,  0x33 },
+	{   87000000,  0x34 },
+	{   88000000,  0x35 },
+	{   89000000,  0x37 },
+	{   90000000,  0x38 },
+	{   91000000,  0x39 },
+	{   93000000,  0x3C },
+	{   94000000,  0x3E },
+	{   95000000,  0x3F },
+	{   96000000,  0x40 },
+	{   97000000,  0x42 },
+	{   99000000,  0x45 },
+	{  100000000,  0x46 },
+	{  102000000,  0x48 },
+	{  103000000,  0x4A },
+	{  105000000,  0x4D },
+	{  106000000,  0x4E },
+	{  107000000,  0x50 },
+	{  108000000,  0x51 },
+	{  110000000,  0x54 },
+	{  111000000,  0x56 },
+	{  112000000,  0x57 },
+	{  113000000,  0x58 },
+	{  114000000,  0x59 },
+	{  115000000,  0x5C },
+	{  116000000,  0x5D },
+	{  117000000,  0x5F },
+	{  119000000,  0x60 },
+	{  120000000,  0x64 },
+	{  121000000,  0x65 },
+	{  122000000,  0x66 },
+	{  123000000,  0x68 },
+	{  124000000,  0x69 },
+	{  125000000,  0x6C },
+	{  126000000,  0x6D },
+	{  127000000,  0x6E },
+	{  128000000,  0x70 },
+	{  129000000,  0x71 },
+	{  130000000,  0x75 },
+	{  131000000,  0x77 },
+	{  132000000,  0x78 },
+	{  133000000,  0x7B },
+	{  134000000,  0x7E },
+	{  135000000,  0x81 },
+	{  136000000,  0x82 },
+	{  137000000,  0x87 },
+	{  138000000,  0x88 },
+	{  139000000,  0x8D },
+	{  140000000,  0x8E },
+	{  141000000,  0x91 },
+	{  142000000,  0x95 },
+	{  143000000,  0x9A },
+	{  144000000,  0x9D },
+	{  145000000,  0xA1 },
+	{  146000000,  0xA2 },
+	{  147000000,  0xA4 },
+	{  148000000,  0xA9 },
+	{  149000000,  0xAE },
+	{  150000000,  0xB0 },
+	{  151000000,  0xB1 },
+	{  152000000,  0xB7 },
+	{  152600000,  0xBD },
+	{  154000000,  0x20 },
+	{  155000000,  0x22 },
+	{  156000000,  0x24 },
+	{  157000000,  0x25 },
+	{  158000000,  0x27 },
+	{  159000000,  0x29 },
+	{  160000000,  0x2C },
+	{  161000000,  0x2D },
+	{  163000000,  0x2E },
+	{  164000000,  0x2F },
+	{  164700000,  0x30 },
+	{  166000000,  0x11 },
+	{  167000000,  0x12 },
+	{  168000000,  0x13 },
+	{  169000000,  0x14 },
+	{  170000000,  0x15 },
+	{  172000000,  0x16 },
+	{  173000000,  0x17 },
+	{  174000000,  0x18 },
+	{  175000000,  0x1A },
+	{  176000000,  0x1B },
+	{  178000000,  0x1D },
+	{  179000000,  0x1E },
+	{  180000000,  0x1F },
+	{  181000000,  0x20 },
+	{  182000000,  0x21 },
+	{  183000000,  0x22 },
+	{  184000000,  0x24 },
+	{  185000000,  0x25 },
+	{  186000000,  0x26 },
+	{  187000000,  0x27 },
+	{  188000000,  0x29 },
+	{  189000000,  0x2A },
+	{  190000000,  0x2C },
+	{  191000000,  0x2D },
+	{  192000000,  0x2E },
+	{  193000000,  0x2F },
+	{  194000000,  0x30 },
+	{  195000000,  0x33 },
+	{  196000000,  0x35 },
+	{  198000000,  0x36 },
+	{  200000000,  0x38 },
+	{  201000000,  0x3C },
+	{  202000000,  0x3D },
+	{  203500000,  0x3E },
+	{  206000000,  0x0E },
+	{  208000000,  0x0F },
+	{  212000000,  0x10 },
+	{  216000000,  0x11 },
+	{  217000000,  0x12 },
+	{  218000000,  0x13 },
+	{  220000000,  0x14 },
+	{  222000000,  0x15 },
+	{  225000000,  0x16 },
+	{  228000000,  0x17 },
+	{  231000000,  0x18 },
+	{  234000000,  0x19 },
+	{  235000000,  0x1A },
+	{  236000000,  0x1B },
+	{  237000000,  0x1C },
+	{  240000000,  0x1D },
+	{  242000000,  0x1E },
+	{  244000000,  0x1F },
+	{  247000000,  0x20 },
+	{  249000000,  0x21 },
+	{  252000000,  0x22 },
+	{  253000000,  0x23 },
+	{  254000000,  0x24 },
+	{  256000000,  0x25 },
+	{  259000000,  0x26 },
+	{  262000000,  0x27 },
+	{  264000000,  0x28 },
+	{  267000000,  0x29 },
+	{  269000000,  0x2A },
+	{  271000000,  0x2B },
+	{  273000000,  0x2C },
+	{  275000000,  0x2D },
+	{  277000000,  0x2E },
+	{  279000000,  0x2F },
+	{  282000000,  0x30 },
+	{  284000000,  0x31 },
+	{  286000000,  0x32 },
+	{  287000000,  0x33 },
+	{  290000000,  0x34 },
+	{  293000000,  0x35 },
+	{  295000000,  0x36 },
+	{  297000000,  0x37 },
+	{  300000000,  0x38 },
+	{  303000000,  0x39 },
+	{  305000000,  0x3A },
+	{  306000000,  0x3B },
+	{  307000000,  0x3C },
+	{  310000000,  0x3D },
+	{  312000000,  0x3E },
+	{  315000000,  0x3F },
+	{  318000000,  0x40 },
+	{  320000000,  0x41 },
+	{  323000000,  0x42 },
+	{  324000000,  0x43 },
+	{  325000000,  0x44 },
+	{  327000000,  0x45 },
+	{  331000000,  0x46 },
+	{  334000000,  0x47 },
+	{  337000000,  0x48 },
+	{  339000000,  0x49 },
+	{  340000000,  0x4A },
+	{  341000000,  0x4B },
+	{  343000000,  0x4C },
+	{  345000000,  0x4D },
+	{  349000000,  0x4E },
+	{  352000000,  0x4F },
+	{  353000000,  0x50 },
+	{  355000000,  0x51 },
+	{  357000000,  0x52 },
+	{  359000000,  0x53 },
+	{  361000000,  0x54 },
+	{  362000000,  0x55 },
+	{  364000000,  0x56 },
+	{  368000000,  0x57 },
+	{  370000000,  0x58 },
+	{  372000000,  0x59 },
+	{  375000000,  0x5A },
+	{  376000000,  0x5B },
+	{  377000000,  0x5C },
+	{  379000000,  0x5D },
+	{  382000000,  0x5E },
+	{  384000000,  0x5F },
+	{  385000000,  0x60 },
+	{  386000000,  0x61 },
+	{  388000000,  0x62 },
+	{  390000000,  0x63 },
+	{  393000000,  0x64 },
+	{  394000000,  0x65 },
+	{  396000000,  0x66 },
+	{  397000000,  0x67 },
+	{  398000000,  0x68 },
+	{  400000000,  0x69 },
+	{  402000000,  0x6A },
+	{  403000000,  0x6B },
+	{  407000000,  0x6C },
+	{  408000000,  0x6D },
+	{  409000000,  0x6E },
+	{  410000000,  0x6F },
+	{  411000000,  0x70 },
+	{  412000000,  0x71 },
+	{  413000000,  0x72 },
+	{  414000000,  0x73 },
+	{  417000000,  0x74 },
+	{  418000000,  0x75 },
+	{  420000000,  0x76 },
+	{  422000000,  0x77 },
+	{  423000000,  0x78 },
+	{  424000000,  0x79 },
+	{  427000000,  0x7A },
+	{  428000000,  0x7B },
+	{  429000000,  0x7D },
+	{  432000000,  0x7F },
+	{  434000000,  0x80 },
+	{  435000000,  0x81 },
+	{  436000000,  0x83 },
+	{  437000000,  0x84 },
+	{  438000000,  0x85 },
+	{  439000000,  0x86 },
+	{  440000000,  0x87 },
+	{  441000000,  0x88 },
+	{  442000000,  0x89 },
+	{  445000000,  0x8A },
+	{  446000000,  0x8B },
+	{  447000000,  0x8C },
+	{  448000000,  0x8E },
+	{  449000000,  0x8F },
+	{  450000000,  0x90 },
+	{  452000000,  0x91 },
+	{  453000000,  0x93 },
+	{  454000000,  0x94 },
+	{  456000000,  0x96 },
+	{  457800000,  0x98 },
+	{  461000000,  0x11 },
+	{  468000000,  0x12 },
+	{  472000000,  0x13 },
+	{  473000000,  0x14 },
+	{  474000000,  0x15 },
+	{  481000000,  0x16 },
+	{  486000000,  0x17 },
+	{  491000000,  0x18 },
+	{  498000000,  0x19 },
+	{  499000000,  0x1A },
+	{  501000000,  0x1B },
+	{  506000000,  0x1C },
+	{  511000000,  0x1D },
+	{  516000000,  0x1E },
+	{  520000000,  0x1F },
+	{  521000000,  0x20 },
+	{  525000000,  0x21 },
+	{  529000000,  0x22 },
+	{  533000000,  0x23 },
+	{  539000000,  0x24 },
+	{  541000000,  0x25 },
+	{  547000000,  0x26 },
+	{  549000000,  0x27 },
+	{  551000000,  0x28 },
+	{  556000000,  0x29 },
+	{  561000000,  0x2A },
+	{  563000000,  0x2B },
+	{  565000000,  0x2C },
+	{  569000000,  0x2D },
+	{  571000000,  0x2E },
+	{  577000000,  0x2F },
+	{  580000000,  0x30 },
+	{  582000000,  0x31 },
+	{  584000000,  0x32 },
+	{  588000000,  0x33 },
+	{  591000000,  0x34 },
+	{  596000000,  0x35 },
+	{  598000000,  0x36 },
+	{  603000000,  0x37 },
+	{  604000000,  0x38 },
+	{  606000000,  0x39 },
+	{  612000000,  0x3A },
+	{  615000000,  0x3B },
+	{  617000000,  0x3C },
+	{  621000000,  0x3D },
+	{  622000000,  0x3E },
+	{  625000000,  0x3F },
+	{  632000000,  0x40 },
+	{  633000000,  0x41 },
+	{  634000000,  0x42 },
+	{  642000000,  0x43 },
+	{  643000000,  0x44 },
+	{  647000000,  0x45 },
+	{  650000000,  0x46 },
+	{  652000000,  0x47 },
+	{  657000000,  0x48 },
+	{  661000000,  0x49 },
+	{  662000000,  0x4A },
+	{  665000000,  0x4B },
+	{  667000000,  0x4C },
+	{  670000000,  0x4D },
+	{  673000000,  0x4E },
+	{  676000000,  0x4F },
+	{  677000000,  0x50 },
+	{  681000000,  0x51 },
+	{  683000000,  0x52 },
+	{  686000000,  0x53 },
+	{  688000000,  0x54 },
+	{  689000000,  0x55 },
+	{  691000000,  0x56 },
+	{  695000000,  0x57 },
+	{  698000000,  0x58 },
+	{  703000000,  0x59 },
+	{  704000000,  0x5A },
+	{  705000000,  0x5B },
+	{  707000000,  0x5C },
+	{  710000000,  0x5D },
+	{  712000000,  0x5E },
+	{  717000000,  0x5F },
+	{  718000000,  0x60 },
+	{  721000000,  0x61 },
+	{  722000000,  0x62 },
+	{  723000000,  0x63 },
+	{  725000000,  0x64 },
+	{  727000000,  0x65 },
+	{  730000000,  0x66 },
+	{  732000000,  0x67 },
+	{  735000000,  0x68 },
+	{  740000000,  0x69 },
+	{  741000000,  0x6A },
+	{  742000000,  0x6B },
+	{  743000000,  0x6C },
+	{  745000000,  0x6D },
+	{  747000000,  0x6E },
+	{  748000000,  0x6F },
+	{  750000000,  0x70 },
+	{  752000000,  0x71 },
+	{  754000000,  0x72 },
+	{  757000000,  0x73 },
+	{  758000000,  0x74 },
+	{  760000000,  0x75 },
+	{  763000000,  0x76 },
+	{  764000000,  0x77 },
+	{  766000000,  0x78 },
+	{  767000000,  0x79 },
+	{  768000000,  0x7A },
+	{  773000000,  0x7B },
+	{  774000000,  0x7C },
+	{  776000000,  0x7D },
+	{  777000000,  0x7E },
+	{  778000000,  0x7F },
+	{  779000000,  0x80 },
+	{  781000000,  0x81 },
+	{  783000000,  0x82 },
+	{  784000000,  0x83 },
+	{  785000000,  0x84 },
+	{  786000000,  0x85 },
+	{  793000000,  0x86 },
+	{  794000000,  0x87 },
+	{  795000000,  0x88 },
+	{  797000000,  0x89 },
+	{  799000000,  0x8A },
+	{  801000000,  0x8B },
+	{  802000000,  0x8C },
+	{  803000000,  0x8D },
+	{  804000000,  0x8E },
+	{  810000000,  0x90 },
+	{  811000000,  0x91 },
+	{  812000000,  0x92 },
+	{  814000000,  0x93 },
+	{  816000000,  0x94 },
+	{  817000000,  0x96 },
+	{  818000000,  0x97 },
+	{  820000000,  0x98 },
+	{  821000000,  0x99 },
+	{  822000000,  0x9A },
+	{  828000000,  0x9B },
+	{  829000000,  0x9D },
+	{  830000000,  0x9F },
+	{  831000000,  0xA0 },
+	{  833000000,  0xA1 },
+	{  835000000,  0xA2 },
+	{  836000000,  0xA3 },
+	{  837000000,  0xA4 },
+	{  838000000,  0xA6 },
+	{  840000000,  0xA8 },
+	{  842000000,  0xA9 },
+	{  845000000,  0xAA },
+	{  846000000,  0xAB },
+	{  847000000,  0xAD },
+	{  848000000,  0xAE },
+	{  852000000,  0xAF },
+	{  853000000,  0xB0 },
+	{  858000000,  0xB1 },
+	{  860000000,  0xB2 },
+	{  861000000,  0xB3 },
+	{  862000000,  0xB4 },
+	{  863000000,  0xB6 },
+	{  864000000,  0xB8 },
+	{  865000000,  0xB9 },
+	{          0,  0x00 },    /* Table End */
+};
+
+
+static struct SMap2  m_KM_Map[] = {
+	{   47900000,  3, 2 },
+	{   61100000,  3, 1 },
+	{  350000000,  3, 0 },
+	{  720000000,  2, 1 },
+	{  865000000,  3, 3 },
+	{          0,  0x00 },    /* Table End */
+};
+
+static struct SMap2 m_Main_PLL_Map[] = {
+	{  33125000, 0x57, 0xF0 },
+	{  35500000, 0x56, 0xE0 },
+	{  38188000, 0x55, 0xD0 },
+	{  41375000, 0x54, 0xC0 },
+	{  45125000, 0x53, 0xB0 },
+	{  49688000, 0x52, 0xA0 },
+	{  55188000, 0x51, 0x90 },
+	{  62125000, 0x50, 0x80 },
+	{  66250000, 0x47, 0x78 },
+	{  71000000, 0x46, 0x70 },
+	{  76375000, 0x45, 0x68 },
+	{  82750000, 0x44, 0x60 },
+	{  90250000, 0x43, 0x58 },
+	{  99375000, 0x42, 0x50 },
+	{ 110375000, 0x41, 0x48 },
+	{ 124250000, 0x40, 0x40 },
+	{ 132500000, 0x37, 0x3C },
+	{ 142000000, 0x36, 0x38 },
+	{ 152750000, 0x35, 0x34 },
+	{ 165500000, 0x34, 0x30 },
+	{ 180500000, 0x33, 0x2C },
+	{ 198750000, 0x32, 0x28 },
+	{ 220750000, 0x31, 0x24 },
+	{ 248500000, 0x30, 0x20 },
+	{ 265000000, 0x27, 0x1E },
+	{ 284000000, 0x26, 0x1C },
+	{ 305500000, 0x25, 0x1A },
+	{ 331000000, 0x24, 0x18 },
+	{ 361000000, 0x23, 0x16 },
+	{ 397500000, 0x22, 0x14 },
+	{ 441500000, 0x21, 0x12 },
+	{ 497000000, 0x20, 0x10 },
+	{ 530000000, 0x17, 0x0F },
+	{ 568000000, 0x16, 0x0E },
+	{ 611000000, 0x15, 0x0D },
+	{ 662000000, 0x14, 0x0C },
+	{ 722000000, 0x13, 0x0B },
+	{ 795000000, 0x12, 0x0A },
+	{ 883000000, 0x11, 0x09 },
+	{ 994000000, 0x10, 0x08 },
+	{         0, 0x00, 0x00 },    /* Table End */
+};
+
+static struct SMap2 m_Cal_PLL_Map[] = {
+	{  33813000, 0xDD, 0xD0 },
+	{  36625000, 0xDC, 0xC0 },
+	{  39938000, 0xDB, 0xB0 },
+	{  43938000, 0xDA, 0xA0 },
+	{  48813000, 0xD9, 0x90 },
+	{  54938000, 0xD8, 0x80 },
+	{  62813000, 0xD3, 0x70 },
+	{  67625000, 0xCD, 0x68 },
+	{  73250000, 0xCC, 0x60 },
+	{  79875000, 0xCB, 0x58 },
+	{  87875000, 0xCA, 0x50 },
+	{  97625000, 0xC9, 0x48 },
+	{ 109875000, 0xC8, 0x40 },
+	{ 125625000, 0xC3, 0x38 },
+	{ 135250000, 0xBD, 0x34 },
+	{ 146500000, 0xBC, 0x30 },
+	{ 159750000, 0xBB, 0x2C },
+	{ 175750000, 0xBA, 0x28 },
+	{ 195250000, 0xB9, 0x24 },
+	{ 219750000, 0xB8, 0x20 },
+	{ 251250000, 0xB3, 0x1C },
+	{ 270500000, 0xAD, 0x1A },
+	{ 293000000, 0xAC, 0x18 },
+	{ 319500000, 0xAB, 0x16 },
+	{ 351500000, 0xAA, 0x14 },
+	{ 390500000, 0xA9, 0x12 },
+	{ 439500000, 0xA8, 0x10 },
+	{ 502500000, 0xA3, 0x0E },
+	{ 541000000, 0x9D, 0x0D },
+	{ 586000000, 0x9C, 0x0C },
+	{ 639000000, 0x9B, 0x0B },
+	{ 703000000, 0x9A, 0x0A },
+	{ 781000000, 0x99, 0x09 },
+	{ 879000000, 0x98, 0x08 },
+	{         0, 0x00, 0x00 },    /* Table End */
+};
+
+static struct SMap  m_GainTaper_Map[] = {
+	{  45400000, 0x1F },
+	{  45800000, 0x1E },
+	{  46200000, 0x1D },
+	{  46700000, 0x1C },
+	{  47100000, 0x1B },
+	{  47500000, 0x1A },
+	{  47900000, 0x19 },
+	{  49600000, 0x17 },
+	{  51200000, 0x16 },
+	{  52900000, 0x15 },
+	{  54500000, 0x14 },
+	{  56200000, 0x13 },
+	{  57800000, 0x12 },
+	{  59500000, 0x11 },
+	{  61100000, 0x10 },
+	{  67600000, 0x0D },
+	{  74200000, 0x0C },
+	{  80700000, 0x0B },
+	{  87200000, 0x0A },
+	{  93800000, 0x09 },
+	{ 100300000, 0x08 },
+	{ 106900000, 0x07 },
+	{ 113400000, 0x06 },
+	{ 119900000, 0x05 },
+	{ 126500000, 0x04 },
+	{ 133000000, 0x03 },
+	{ 139500000, 0x02 },
+	{ 146100000, 0x01 },
+	{ 152600000, 0x00 },
+	{ 154300000, 0x1F },
+	{ 156100000, 0x1E },
+	{ 157800000, 0x1D },
+	{ 159500000, 0x1C },
+	{ 161200000, 0x1B },
+	{ 163000000, 0x1A },
+	{ 164700000, 0x19 },
+	{ 170200000, 0x17 },
+	{ 175800000, 0x16 },
+	{ 181300000, 0x15 },
+	{ 186900000, 0x14 },
+	{ 192400000, 0x13 },
+	{ 198000000, 0x12 },
+	{ 203500000, 0x11 },
+	{ 216200000, 0x14 },
+	{ 228900000, 0x13 },
+	{ 241600000, 0x12 },
+	{ 254400000, 0x11 },
+	{ 267100000, 0x10 },
+	{ 279800000, 0x0F },
+	{ 292500000, 0x0E },
+	{ 305200000, 0x0D },
+	{ 317900000, 0x0C },
+	{ 330700000, 0x0B },
+	{ 343400000, 0x0A },
+	{ 356100000, 0x09 },
+	{ 368800000, 0x08 },
+	{ 381500000, 0x07 },
+	{ 394200000, 0x06 },
+	{ 406900000, 0x05 },
+	{ 419700000, 0x04 },
+	{ 432400000, 0x03 },
+	{ 445100000, 0x02 },
+	{ 457800000, 0x01 },
+	{ 476300000, 0x19 },
+	{ 494800000, 0x18 },
+	{ 513300000, 0x17 },
+	{ 531800000, 0x16 },
+	{ 550300000, 0x15 },
+	{ 568900000, 0x14 },
+	{ 587400000, 0x13 },
+	{ 605900000, 0x12 },
+	{ 624400000, 0x11 },
+	{ 642900000, 0x10 },
+	{ 661400000, 0x0F },
+	{ 679900000, 0x0E },
+	{ 698400000, 0x0D },
+	{ 716900000, 0x0C },
+	{ 735400000, 0x0B },
+	{ 753900000, 0x0A },
+	{ 772500000, 0x09 },
+	{ 791000000, 0x08 },
+	{ 809500000, 0x07 },
+	{ 828000000, 0x06 },
+	{ 846500000, 0x05 },
+	{ 865000000, 0x04 },
+	{         0, 0x00 },    /* Table End */
+};
+
+static struct SMap m_RF_Cal_DC_Over_DT_Map[] = {
+	{  47900000, 0x00 },
+	{  55000000, 0x00 },
+	{  61100000, 0x0A },
+	{  64000000, 0x0A },
+	{  82000000, 0x14 },
+	{  84000000, 0x19 },
+	{ 119000000, 0x1C },
+	{ 124000000, 0x20 },
+	{ 129000000, 0x2A },
+	{ 134000000, 0x32 },
+	{ 139000000, 0x39 },
+	{ 144000000, 0x3E },
+	{ 149000000, 0x3F },
+	{ 152600000, 0x40 },
+	{ 154000000, 0x40 },
+	{ 164700000, 0x41 },
+	{ 203500000, 0x32 },
+	{ 353000000, 0x19 },
+	{ 356000000, 0x1A },
+	{ 359000000, 0x1B },
+	{ 363000000, 0x1C },
+	{ 366000000, 0x1D },
+	{ 369000000, 0x1E },
+	{ 373000000, 0x1F },
+	{ 376000000, 0x20 },
+	{ 379000000, 0x21 },
+	{ 383000000, 0x22 },
+	{ 386000000, 0x23 },
+	{ 389000000, 0x24 },
+	{ 393000000, 0x25 },
+	{ 396000000, 0x26 },
+	{ 399000000, 0x27 },
+	{ 402000000, 0x28 },
+	{ 404000000, 0x29 },
+	{ 407000000, 0x2A },
+	{ 409000000, 0x2B },
+	{ 412000000, 0x2C },
+	{ 414000000, 0x2D },
+	{ 417000000, 0x2E },
+	{ 419000000, 0x2F },
+	{ 422000000, 0x30 },
+	{ 424000000, 0x31 },
+	{ 427000000, 0x32 },
+	{ 429000000, 0x33 },
+	{ 432000000, 0x34 },
+	{ 434000000, 0x35 },
+	{ 437000000, 0x36 },
+	{ 439000000, 0x37 },
+	{ 442000000, 0x38 },
+	{ 444000000, 0x39 },
+	{ 447000000, 0x3A },
+	{ 449000000, 0x3B },
+	{ 457800000, 0x3C },
+	{ 465000000, 0x0F },
+	{ 477000000, 0x12 },
+	{ 483000000, 0x14 },
+	{ 502000000, 0x19 },
+	{ 508000000, 0x1B },
+	{ 519000000, 0x1C },
+	{ 522000000, 0x1D },
+	{ 524000000, 0x1E },
+	{ 534000000, 0x1F },
+	{ 549000000, 0x20 },
+	{ 554000000, 0x22 },
+	{ 584000000, 0x24 },
+	{ 589000000, 0x26 },
+	{ 658000000, 0x27 },
+	{ 664000000, 0x2C },
+	{ 669000000, 0x2D },
+	{ 699000000, 0x2E },
+	{ 704000000, 0x30 },
+	{ 709000000, 0x31 },
+	{ 714000000, 0x32 },
+	{ 724000000, 0x33 },
+	{ 729000000, 0x36 },
+	{ 739000000, 0x38 },
+	{ 744000000, 0x39 },
+	{ 749000000, 0x3B },
+	{ 754000000, 0x3C },
+	{ 759000000, 0x3D },
+	{ 764000000, 0x3E },
+	{ 769000000, 0x3F },
+	{ 774000000, 0x40 },
+	{ 779000000, 0x41 },
+	{ 784000000, 0x43 },
+	{ 789000000, 0x46 },
+	{ 794000000, 0x48 },
+	{ 799000000, 0x4B },
+	{ 804000000, 0x4F },
+	{ 809000000, 0x54 },
+	{ 814000000, 0x59 },
+	{ 819000000, 0x5D },
+	{ 824000000, 0x61 },
+	{ 829000000, 0x68 },
+	{ 834000000, 0x6E },
+	{ 839000000, 0x75 },
+	{ 844000000, 0x7E },
+	{ 849000000, 0x82 },
+	{ 854000000, 0x84 },
+	{ 859000000, 0x8F },
+	{ 865000000, 0x9A },
+	{         0, 0x00 },    /* Table End */
+};
+
+
+static struct SMap  m_IR_Meas_Map[] = {
+	{ 200000000, 0x05 },
+	{ 400000000, 0x06 },
+	{ 865000000, 0x07 },
+	{         0, 0x00 },    /* Table End */
+};
+
+static struct SMap2 m_CID_Target_Map[] = {
+	{  46000000, 0x04, 18 },
+	{  52200000, 0x0A, 15 },
+	{  70100000, 0x01, 40 },
+	{ 136800000, 0x18, 40 },
+	{ 156700000, 0x18, 40 },
+	{ 186250000, 0x0A, 40 },
+	{ 230000000, 0x0A, 40 },
+	{ 345000000, 0x18, 40 },
+	{ 426000000, 0x0E, 40 },
+	{ 489500000, 0x1E, 40 },
+	{ 697500000, 0x32, 40 },
+	{ 842000000, 0x3A, 40 },
+	{         0, 0x00,  0 },    /* Table End */
+};
+
+static struct SRFBandMap  m_RF_Band_Map[7] = {
+	{   47900000,   46000000,           0,          0},
+	{   61100000,   52200000,           0,          0},
+	{  152600000,   70100000,   136800000,          0},
+	{  164700000,  156700000,           0,          0},
+	{  203500000,  186250000,           0,          0},
+	{  457800000,  230000000,   345000000,  426000000},
+	{  865000000,  489500000,   697500000,  842000000},
+};
+
+u8 m_Thermometer_Map_1[16] = {
+	60, 62, 66, 64,
+	74, 72, 68, 70,
+	90, 88, 84, 86,
+	76, 78, 82, 80,
+};
+
+u8 m_Thermometer_Map_2[16] = {
+	92, 94, 98, 96,
+	106, 104, 100, 102,
+	122, 120, 116, 118,
+	108, 110, 114, 112,
+};
diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig
index cec242b..64c8470 100644
--- a/drivers/media/dvb/ngene/Kconfig
+++ b/drivers/media/dvb/ngene/Kconfig
@@ -5,6 +5,8 @@
 	select DVB_STV6110x if !DVB_FE_CUSTOMISE
 	select DVB_STV090x if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_DRXK if !DVB_FE_CUSTOMISE
+	select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
 	---help---
 	  Support for Micronas PCI express cards with nGene bridge.
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index fcf4be9..0564192 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -40,6 +40,8 @@
 #include "lnbh24.h"
 #include "lgdt330x.h"
 #include "mt2131.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
 
 
 /****************************************************************************/
@@ -83,6 +85,49 @@
 }
 
 
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct ngene_channel *chan = fe->sec_priv;
+	int status;
+
+	if (enable) {
+		down(&chan->dev->pll_mutex);
+		status = chan->gate_ctrl(fe, 1);
+	} else {
+		status = chan->gate_ctrl(fe, 0);
+		up(&chan->dev->pll_mutex);
+	}
+	return status;
+}
+
+static int tuner_attach_tda18271(struct ngene_channel *chan)
+{
+	struct i2c_adapter *i2c;
+	struct dvb_frontend *fe;
+
+	i2c = &chan->dev->channel[0].i2c_adapter;
+	if (chan->fe->ops.i2c_gate_ctrl)
+		chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+	fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+	if (chan->fe->ops.i2c_gate_ctrl)
+		chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+	if (!fe) {
+		printk(KERN_ERR "No TDA18271 found!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int tuner_attach_probe(struct ngene_channel *chan)
+{
+	if (chan->demod_type == 0)
+		return tuner_attach_stv6110(chan);
+	if (chan->demod_type == 1)
+		return tuner_attach_tda18271(chan);
+	return -EINVAL;
+}
+
 static int demod_attach_stv0900(struct ngene_channel *chan)
 {
 	struct i2c_adapter *i2c;
@@ -130,6 +175,60 @@
 		up(&chan->dev->pll_mutex);
 }
 
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = val,  .len   = 1 } };
+	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+			  u16 reg, u8 *val)
+{
+	u8 msg[2] = {reg>>8, reg&0xff};
+	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+				   .buf  = msg, .len   = 2},
+				  {.addr = adr, .flags = I2C_M_RD,
+				   .buf  = val, .len   = 1} };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+{
+	u8 val;
+	if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int port_has_drxk(struct i2c_adapter *i2c, int port)
+{
+	u8 val;
+
+	if (i2c_read(i2c, 0x29+port, &val) < 0)
+		return 0;
+	return 1;
+}
+
+static int demod_attach_drxk(struct ngene_channel *chan,
+			     struct i2c_adapter *i2c)
+{
+	struct drxk_config config;
+
+	memset(&config, 0, sizeof(config));
+	config.adr = 0x29 + (chan->number ^ 2);
+
+	chan->fe = dvb_attach(drxk_attach, &config, i2c, &chan->fe2);
+	if (!chan->fe) {
+		printk(KERN_ERR "No DRXK found!\n");
+		return -ENODEV;
+	}
+	chan->fe->sec_priv = chan;
+	chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+	chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+	return 0;
+}
+
 static int cineS2_probe(struct ngene_channel *chan)
 {
 	struct i2c_adapter *i2c;
@@ -144,43 +243,42 @@
 	else
 		i2c = &chan->dev->channel[1].i2c_adapter;
 
-	fe_conf = chan->dev->card_info->fe_config[chan->number];
-	i2c_msg.addr = fe_conf->address;
+	if (port_has_stv0900(i2c, chan->number)) {
+		chan->demod_type = 0;
+		fe_conf = chan->dev->card_info->fe_config[chan->number];
+		/* demod found, attach it */
+		rc = demod_attach_stv0900(chan);
+		if (rc < 0 || chan->number < 2)
+			return rc;
 
-	/* probe demod */
-	i2c_msg.len = 2;
-	buf[0] = 0xf1;
-	buf[1] = 0x00;
-	rc = i2c_transfer(i2c, &i2c_msg, 1);
-	if (rc != 1)
-		return -ENODEV;
-
-	/* demod found, attach it */
-	rc = demod_attach_stv0900(chan);
-	if (rc < 0 || chan->number < 2)
-		return rc;
-
-	/* demod #2: reprogram outputs DPN1 & DPN2 */
-	i2c_msg.len = 3;
-	buf[0] = 0xf1;
-	switch (chan->number) {
-	case 2:
-		buf[1] = 0x5c;
-		buf[2] = 0xc2;
-		break;
-	case 3:
-		buf[1] = 0x61;
-		buf[2] = 0xcc;
-		break;
-	default:
+		/* demod #2: reprogram outputs DPN1 & DPN2 */
+		i2c_msg.addr = fe_conf->address;
+		i2c_msg.len = 3;
+		buf[0] = 0xf1;
+		switch (chan->number) {
+		case 2:
+			buf[1] = 0x5c;
+			buf[2] = 0xc2;
+			break;
+		case 3:
+			buf[1] = 0x61;
+			buf[2] = 0xcc;
+			break;
+		default:
+			return -ENODEV;
+		}
+		rc = i2c_transfer(i2c, &i2c_msg, 1);
+		if (rc != 1) {
+			printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+			return -EIO;
+		}
+	} else if (port_has_drxk(i2c, chan->number^2)) {
+		chan->demod_type = 1;
+		demod_attach_drxk(chan, i2c);
+	} else {
+		printk(KERN_ERR "No demod found on chan %d\n", chan->number);
 		return -ENODEV;
 	}
-	rc = i2c_transfer(i2c, &i2c_msg, 1);
-	if (rc != 1) {
-		printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
-		return -EIO;
-	}
-
 	return 0;
 }
 
@@ -306,7 +404,7 @@
 	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
 			   NGENE_IO_TSOUT},
 	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
-	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
 	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
 	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
 	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
@@ -321,7 +419,7 @@
 	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
 			   NGENE_IO_TSOUT},
 	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
-	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
 	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
 	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
 	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
@@ -331,13 +429,13 @@
 };
 
 
-static struct ngene_info ngene_info_duoFlexS2 = {
+static struct ngene_info ngene_info_duoFlex = {
 	.type           = NGENE_SIDEWINDER,
-	.name           = "Digital Devices DuoFlex S2 miniPCIe",
+	.name           = "Digital Devices DuoFlex PCIe or miniPCIe",
 	.io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
 			   NGENE_IO_TSOUT},
 	.demod_attach   = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
-	.tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_stv6110},
+	.tuner_attach   = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
 	.fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
 	.tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
 	.lnb            = {0x0a, 0x08, 0x0b, 0x09},
@@ -385,8 +483,8 @@
 	NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
 	NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
 	NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
-	NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlexS2),
-	NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlexS2),
+	NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+	NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
 	NGENE_ID(0x1461, 0x062e, ngene_info_m780),
 	{0}
 };
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
index 6927c72..f129a93 100644
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -41,7 +41,7 @@
 
 #include "ngene.h"
 
-static int one_adapter = 1;
+static int one_adapter;
 module_param(one_adapter, int, 0444);
 MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
 
@@ -461,7 +461,7 @@
 	0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
 	0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXH */
 	0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXHser */
-	0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* S2ser */
+	0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* S2ser */
 	0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
 };
 
@@ -507,7 +507,7 @@
 {
 	u32 *ptr = Buffer;
 
-	memset(Buffer, 0xff, Length);
+	memset(Buffer, TS_FILLER, Length);
 	while (Length > 0) {
 		if (Flags & DF_SWAP32)
 			*ptr = 0x471FFF10;
@@ -1443,6 +1443,9 @@
 		chan->ci_dev = NULL;
 	}
 
+	if (chan->fe2)
+		dvb_unregister_frontend(chan->fe2);
+
 	if (chan->fe) {
 		dvb_unregister_frontend(chan->fe);
 		dvb_frontend_detach(chan->fe);
@@ -1534,6 +1537,14 @@
 			goto err;
 		chan->has_demux = true;
 	}
+	if (chan->fe2) {
+		if (dvb_register_frontend(adapter, chan->fe2) < 0)
+			goto err;
+		chan->fe2->tuner_priv = chan->fe->tuner_priv;
+		memcpy(&chan->fe2->ops.tuner_ops,
+		       &chan->fe->ops.tuner_ops,
+		       sizeof(struct dvb_tuner_ops));
+	}
 
 	if (chan->has_demux) {
 		ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
@@ -1571,11 +1582,18 @@
 	return 0;
 }
 
+static struct cxd2099_cfg cxd_cfg = {
+	.bitrate = 62000,
+	.adr = 0x40,
+	.polarity = 0,
+	.clock_mode = 0,
+};
+
 static void cxd_attach(struct ngene *dev)
 {
 	struct ngene_ci *ci = &dev->ci;
 
-	ci->en = cxd2099_attach(0x40, dev, &dev->channel[0].i2c_adapter);
+	ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
 	ci->dev = dev;
 	return;
 }
diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c
index 0b49432..fcb16a6 100644
--- a/drivers/media/dvb/ngene/ngene-dvb.c
+++ b/drivers/media/dvb/ngene/ngene-dvb.c
@@ -118,6 +118,16 @@
 	}
 }
 
+/* start of filler packet */
+static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
+
+/* #define DEBUG_CI_XFER */
+#ifdef DEBUG_CI_XFER
+static u32 ok;
+static u32 overflow;
+static u32 stripped;
+#endif
+
 void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 {
 	struct ngene_channel *chan = priv;
@@ -126,21 +136,41 @@
 
 	if (flags & DF_SWAP32)
 		swap_buffer(buf, len);
+
 	if (dev->ci.en && chan->number == 2) {
-		if (dvb_ringbuffer_free(&dev->tsin_rbuf) > len) {
-			dvb_ringbuffer_write(&dev->tsin_rbuf, buf, len);
-			wake_up_interruptible(&dev->tsin_rbuf.queue);
+		while (len >= 188) {
+			if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) {
+				if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
+					dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
+					wake_up(&dev->tsin_rbuf.queue);
+#ifdef DEBUG_CI_XFER
+					ok++;
+#endif
+				}
+#ifdef DEBUG_CI_XFER
+				else
+					overflow++;
+#endif
+			}
+#ifdef DEBUG_CI_XFER
+			else
+				stripped++;
+
+			if (ok % 100 == 0 && overflow)
+				printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped);
+#endif
+			buf += 188;
+			len -= 188;
 		}
-		return 0;
+		return NULL;
 	}
-	if (chan->users > 0) {
+
+	if (chan->users > 0)
 		dvb_dmx_swfilter(&chan->demux, buf, len);
-	}
+
 	return NULL;
 }
 
-u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 };
-
 void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 {
 	struct ngene_channel *chan = priv;
diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h
index 40fce9e..5443dc0 100644
--- a/drivers/media/dvb/ngene/ngene.h
+++ b/drivers/media/dvb/ngene/ngene.h
@@ -641,8 +641,11 @@
 	int                   mode;
 	bool                  has_adapter;
 	bool                  has_demux;
+	int                   demod_type;
+	int (*gate_ctrl)(struct dvb_frontend *, int);
 
 	struct dvb_frontend  *fe;
+	struct dvb_frontend  *fe2;
 	struct dmxdev         dmxdev;
 	struct dvb_demux      demux;
 	struct dvb_net        dvbnet;
@@ -786,6 +789,8 @@
 	u8                    uart_rbuf[UART_RBUF_LEN];
 	int                   uart_rp, uart_wp;
 
+#define TS_FILLER  0x6f
+
 	u8                   *tsout_buf;
 #define TSOUT_BUF_SIZE (512*188*8)
 	struct dvb_ringbuffer tsout_rbuf;
@@ -852,7 +857,7 @@
 };
 
 #ifdef NGENE_V4L
-struct ngene_format{
+struct ngene_format {
 	char *name;
 	int   fourcc;          /* video4linux 2      */
 	int   btformat;        /* BT848_COLOR_FMT_*  */
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 78765ed..7331e84 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1147,7 +1147,7 @@
 
 	if (!client) {
 		sms_err("bad parameter.");
-		return -EFAULT;
+		return -EINVAL;
 	}
 	registered_client = smscore_find_client(coredev, data_type, id);
 	if (registered_client == client)
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
index 8ecadec..c592ae0 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/dvb/siano/smscoreapi.h
@@ -22,7 +22,6 @@
 #ifndef __SMS_CORE_API_H__
 #define __SMS_CORE_API_H__
 
-#include <linux/version.h>
 #include <linux/device.h>
 #include <linux/list.h>
 #include <linux/mm.h>
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index 3d8cc42..25e58cb 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -102,10 +102,7 @@
 /*
  * Version Information
  */
-#include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/
-
-#define DRIVER_VERSION "v0.46"
-#define RADIO_VERSION KERNEL_VERSION(0, 4, 6)
+#define DRIVER_VERSION "0.4.7"
 
 #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
 #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
@@ -335,7 +332,6 @@
 	strlcpy(v->driver, "dsbr100", sizeof(v->driver));
 	strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
 	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
@@ -647,3 +643,4 @@
 MODULE_AUTHOR( DRIVER_AUTHOR );
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 4ce10db..1c3f844 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -33,7 +33,6 @@
 #include <linux/ioport.h>	/* request_region		*/
 #include <linux/delay.h>	/* msleep			*/
 #include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/
 #include <linux/io.h>		/* outb, outb_p			*/
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -41,6 +40,7 @@
 MODULE_AUTHOR("M.Kirkwood");
 MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 #ifndef CONFIG_RADIO_RTRACK_PORT
 #define CONFIG_RADIO_RTRACK_PORT -1
@@ -53,8 +53,6 @@
 MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct rtrack
 {
 	struct v4l2_device v4l2_dev;
@@ -223,7 +221,6 @@
 	strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
 	strlcpy(v->card, "RadioTrack", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index dd8a6ab..eed7b08 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -30,7 +30,6 @@
 #include <linux/ioport.h>	/* request_region		*/
 #include <linux/delay.h>	/* udelay			*/
 #include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -38,6 +37,7 @@
 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
 
@@ -53,8 +53,6 @@
 module_param(radio_nr, int, 0);
 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct aztech
 {
 	struct v4l2_device v4l2_dev;
@@ -188,7 +186,6 @@
 	strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
 	strlcpy(v->card, "Aztech Radio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index bc9ad08..16a089f 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -30,7 +30,6 @@
  *		Changed API to V4L2
  */
 
-#include <linux/version.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
@@ -46,6 +45,7 @@
 MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3.4");
 
 static int io = -1;		/* default to isapnp activation */
 static int radio_nr = -1;
@@ -54,8 +54,6 @@
 MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
 module_param(radio_nr, int, 0);
 
-#define CADET_VERSION KERNEL_VERSION(0, 3, 3)
-
 #define RDS_BUFFER 256
 #define RDS_RX_FLAG 1
 #define MBS_RX_FLAG 2
@@ -361,7 +359,6 @@
 	strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
 	strlcpy(v->card, "ADS Cadet", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = CADET_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
 			  V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
 	return 0;
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 2599364..edadc84 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -21,21 +21,19 @@
 #include <linux/ioport.h>	/* request_region		*/
 #include <linux/delay.h>	/* udelay			*/
 #include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/
 #include <linux/mutex.h>
 #include <linux/io.h>		/* outb, outb_p			*/
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 3)
-
 /*
  * Module info.
  */
 
-MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
+MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
 MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
 
 /*
  * Module params.
@@ -387,7 +385,6 @@
 	strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
 	strlcpy(v->card, "GemTek", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index e83e840..f872a54 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -40,15 +40,18 @@
 #include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/videodev2.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 
+#define DRIVER_VERSION	"0.7.8"
+
+
 MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
 MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
 
 static int radio_nr = -1;
 module_param(radio_nr, int, 0);
@@ -58,10 +61,6 @@
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "activates debug info");
 
-#define DRIVER_VERSION	"0.77"
-
-#define RADIO_VERSION KERNEL_VERSION(0, 7, 7)
-
 #define dprintk(dev, num, fmt, arg...) \
 	v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
 
@@ -195,7 +194,6 @@
 	strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
 	strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
 	snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index b3a635b..1742bd8 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -63,18 +63,17 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <linux/usb.h>
-#include <linux/version.h>	/* for KERNEL_VERSION MACRO */
 #include <linux/mutex.h>
 
 /* driver and module definitions */
 #define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
 #define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
-#define DRIVER_VERSION "0.11"
-#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
+#define DRIVER_VERSION "0.1.2"
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
 
 #define USB_AMRADIO_VENDOR 0x07ca
 #define USB_AMRADIO_PRODUCT 0xb800
@@ -301,7 +300,6 @@
 	strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
 	strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
 	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 8d6ea59..3628be6 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -15,7 +15,6 @@
 #include <linux/delay.h>	/* udelay			*/
 #include <linux/videodev2.h>	/* kernel radio structs		*/
 #include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -23,6 +22,7 @@
 MODULE_AUTHOR("Ben Pfaff");
 MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 #ifndef CONFIG_RADIO_RTRACK2_PORT
 #define CONFIG_RADIO_RTRACK2_PORT -1
@@ -35,8 +35,6 @@
 MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct rtrack2
 {
 	struct v4l2_device v4l2_dev;
@@ -121,7 +119,6 @@
 	strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
 	strlcpy(v->card, "RadioTrack II", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index b5a5f89..22c5743 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -16,7 +16,6 @@
  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
  */
 
-#include <linux/version.h>
 #include <linux/kernel.h>	/* __setup			*/
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
@@ -32,6 +31,7 @@
 MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
 MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 static int io = -1;
 static int radio_nr = -1;
@@ -40,8 +40,6 @@
 MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct fmi
 {
 	struct v4l2_device v4l2_dev;
@@ -134,7 +132,6 @@
 	strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
 	strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 87bad76..2dd4859 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -1,441 +1,209 @@
-/* SF16FMR2 radio driver for Linux radio support
- * heavily based on fmi driver...
- * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+/* SF16-FMR2 radio driver for Linux
+ * Copyright (c) 2011 Ondrej Zary
  *
- * Notes on the hardware
- *
- *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
- *  No volume control - only mute/unmute - you have to use line volume
- *
- *  For read stereo/mono you must wait 0.1 sec after set frequency and
- *  card unmuted so I set frequency on unmute
- *  Signal handling seem to work only on autoscanning (not implemented)
- *
- *  Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ * but almost nothing remained here after conversion to generic TEA575x
+ * implementation
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>	/* Modules 			*/
 #include <linux/init.h>		/* Initdata			*/
 #include <linux/ioport.h>	/* request_region		*/
-#include <linux/delay.h>	/* udelay			*/
-#include <linux/videodev2.h>	/* kernel radio structs		*/
-#include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
+#include <sound/tea575x-tuner.h>
 
-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
 MODULE_LICENSE("GPL");
 
-static int io = 0x384;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
-
-#define AUD_VOL_INDEX 1
-
-#undef DEBUG
-//#define DEBUG 1
-
-#ifdef DEBUG
-# define  debug_print(s) printk s
-#else
-# define  debug_print(s)
-#endif
-
-/* this should be static vars for module size */
-struct fmr2
-{
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct mutex lock;
+struct fmr2 {
 	int io;
-	int curvol; /* 0-15 */
-	int mute;
-	int stereo; /* card is producing stereo audio */
-	unsigned long curfreq; /* freq in kHz */
-	int card_type;
+	struct snd_tea575x tea;
+	struct v4l2_ctrl *volume;
+	struct v4l2_ctrl *balance;
 };
 
+/* the port is hardwired so no need to support multiple cards */
+#define FMR2_PORT	0x384
 static struct fmr2 fmr2_card;
 
-/* hw precision is 12.5 kHz
- * It is only useful to give freq in interval of 200 (=0.0125Mhz),
- * other bits will be truncated
- */
-#define RSF16_ENCODE(x)	((x) / 200 + 856)
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
+/* TEA575x tuner pins */
+#define STR_DATA	(1 << 0)
+#define STR_CLK		(1 << 1)
+#define STR_WREN	(1 << 2)
+#define STR_MOST	(1 << 3)
+/* PT2254A/TC9154A volume control pins */
+#define PT_ST		(1 << 4)
+#define PT_CK		(1 << 5)
+#define PT_DATA		(1 << 6)
+/* volume control presence pin */
+#define FMR2_HASVOL	(1 << 7)
 
-static inline void wait(int n, int io)
+static void fmr2_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
-	for (; n; --n)
-		inb(io);
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = 0;
+
+	bits |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+	bits |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+	/* WRITE_ENABLE is inverted, DATA must be high during read */
+	bits |= (pins & TEA575X_WREN) ? 0 : STR_WREN | STR_DATA;
+
+	outb(bits, fmr2->io);
 }
 
-static void outbits(int bits, unsigned int data, int nWait, int io)
+static u8 fmr2_tea575x_get_pins(struct snd_tea575x *tea)
 {
-	int bit;
+	struct fmr2 *fmr2 = tea->private_data;
+	u8 bits = inb(fmr2->io);
 
-	for (; --bits >= 0;) {
-		bit = (data >> bits) & 1;
-		outb(bit, io);
-		wait(nWait, io);
-		outb(bit | 2, io);
-		wait(nWait, io);
-		outb(bit, io);
-		wait(nWait, io);
+	return  (bits & STR_DATA) ? TEA575X_DATA : 0 |
+		(bits & STR_MOST) ? TEA575X_MOST : 0;
+}
+
+static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+}
+
+static struct snd_tea575x_ops fmr2_tea_ops = {
+	.set_pins = fmr2_tea575x_set_pins,
+	.get_pins = fmr2_tea575x_get_pins,
+	.set_direction = fmr2_tea575x_set_direction,
+};
+
+/* TC9154A/PT2254A volume control */
+
+/* 18-bit shift register bit definitions */
+#define TC9154A_ATT_MAJ_0DB	(1 << 0)
+#define TC9154A_ATT_MAJ_10DB	(1 << 1)
+#define TC9154A_ATT_MAJ_20DB	(1 << 2)
+#define TC9154A_ATT_MAJ_30DB	(1 << 3)
+#define TC9154A_ATT_MAJ_40DB	(1 << 4)
+#define TC9154A_ATT_MAJ_50DB	(1 << 5)
+#define TC9154A_ATT_MAJ_60DB	(1 << 6)
+
+#define TC9154A_ATT_MIN_0DB	(1 << 7)
+#define TC9154A_ATT_MIN_2DB	(1 << 8)
+#define TC9154A_ATT_MIN_4DB	(1 << 9)
+#define TC9154A_ATT_MIN_6DB	(1 << 10)
+#define TC9154A_ATT_MIN_8DB	(1 << 11)
+/* bit 12 is ignored */
+#define TC9154A_CHANNEL_LEFT	(1 << 13)
+#define TC9154A_CHANNEL_RIGHT	(1 << 14)
+/* bits 15, 16, 17 must be 0 */
+
+#define	TC9154A_ATT_MAJ(x)	(1 << x)
+#define TC9154A_ATT_MIN(x)	(1 << (7 + x))
+
+static void tc9154a_set_pins(struct fmr2 *fmr2, u8 pins)
+{
+	if (!fmr2->tea.mute)
+		pins |= STR_WREN;
+
+	outb(pins, fmr2->io);
+}
+
+static void tc9154a_set_attenuation(struct fmr2 *fmr2, int att, u32 channel)
+{
+	int i;
+	u32 reg;
+	u8 bit;
+
+	reg = TC9154A_ATT_MAJ(att / 10) | TC9154A_ATT_MIN((att % 10) / 2);
+	reg |= channel;
+	/* write 18-bit shift register, LSB first */
+	for (i = 0; i < 18; i++) {
+		bit = reg & (1 << i) ? PT_DATA : 0;
+		tc9154a_set_pins(fmr2, bit);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit | PT_CK);
+		udelay(5);
+		tc9154a_set_pins(fmr2, bit);
 	}
+
+	/* latch register data */
+	udelay(5);
+	tc9154a_set_pins(fmr2, PT_ST);
+	udelay(5);
+	tc9154a_set_pins(fmr2, 0);
 }
 
-static inline void fmr2_mute(int io)
+static int fmr2_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	outb(0x00, io);
-	wait(4, io);
-}
-
-static inline void fmr2_unmute(int io)
-{
-	outb(0x04, io);
-	wait(4, io);
-}
-
-static inline int fmr2_stereo_mode(int io)
-{
-	int n = inb(io);
-
-	outb(6, io);
-	inb(io);
-	n = ((n >> 3) & 1) ^ 1;
-	debug_print((KERN_DEBUG "stereo: %d\n", n));
-	return n;
-}
-
-static int fmr2_product_info(struct fmr2 *dev)
-{
-	int n = inb(dev->io);
-
-	n &= 0xC1;
-	if (n == 0) {
-		/* this should support volume set */
-		dev->card_type = 12;
-		return 0;
-	}
-	/* not volume (mine is 11) */
-	dev->card_type = (n == 128) ? 11 : 0;
-	return n;
-}
-
-static inline int fmr2_getsigstr(struct fmr2 *dev)
-{
-	/* !!! works only if scanning freq */
-	int res = 0xffff;
-
-	outb(5, dev->io);
-	wait(4, dev->io);
-	if (!(inb(dev->io) & 1))
-		res = 0;
-	debug_print((KERN_DEBUG "signal: %d\n", res));
-	return res;
-}
-
-/* set frequency and unmute card */
-static int fmr2_setfreq(struct fmr2 *dev)
-{
-	unsigned long freq = dev->curfreq;
-
-	fmr2_mute(dev->io);
-
-	/* 0x42 for mono output
-	 * 0x102 forward scanning
-	 * 0x182 scansione avanti
-	 */
-	outbits(9, 0x2, 3, dev->io);
-	outbits(16, RSF16_ENCODE(freq), 2, dev->io);
-
-	fmr2_unmute(dev->io);
-
-	/* wait 0.11 sec */
-	msleep(110);
-
-	/* NOTE if mute this stop radio
-	   you must set freq on unmute */
-	dev->stereo = fmr2_stereo_mode(dev->io);
-	return 0;
-}
-
-/* !!! not tested, in my card this doesn't work !!! */
-static int fmr2_setvolume(struct fmr2 *dev)
-{
-	int vol[16] = { 0x021, 0x084, 0x090, 0x104,
-			0x110, 0x204, 0x210, 0x402,
-			0x404, 0x408, 0x410, 0x801,
-			0x802, 0x804, 0x808, 0x810 };
-	int i, a;
-	int n = vol[dev->curvol & 0x0f];
-
-	if (dev->card_type != 11)
-		return 1;
-
-	for (i = 12; --i >= 0; ) {
-		a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	for (i = 6; --i >= 0; ) {
-		a = ((0x18 >> i) & 1) << 6;
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-		outb(a | 0x24, dev->io);
-		wait(4, dev->io);
-		outb(a | 4, dev->io);
-		wait(4, dev->io);
-	}
-	wait(4, dev->io);
-	outb(0x14, dev->io);
-	return 0;
-}
-
-static int vidioc_querycap(struct file *file, void  *priv,
-					struct v4l2_capability *v)
-{
-	strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
-	strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
-	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-	return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (v->index > 0)
-		return -EINVAL;
-
-	strlcpy(v->name, "FM", sizeof(v->name));
-	v->type = V4L2_TUNER_RADIO;
-
-	v->rangelow = RSF16_MINFREQ;
-	v->rangehigh = RSF16_MAXFREQ;
-	v->rxsubchans = fmr2->stereo ? V4L2_TUNER_SUB_STEREO :
-					V4L2_TUNER_SUB_MONO;
-	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-	v->audmode = V4L2_TUNER_MODE_STEREO;
-	mutex_lock(&fmr2->lock);
-	v->signal = fmr2_getsigstr(fmr2);
-	mutex_unlock(&fmr2->lock);
-	return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
-					struct v4l2_tuner *v)
-{
-	return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
-		return -EINVAL;
-	if (f->frequency < RSF16_MINFREQ ||
-			f->frequency > RSF16_MAXFREQ)
-		return -EINVAL;
-	/* rounding in steps of 200 to match the freq
-	   that will be used */
-	fmr2->curfreq = (f->frequency / 200) * 200;
-
-	/* set card freq (if not muted) */
-	if (fmr2->curvol && !fmr2->mute) {
-		mutex_lock(&fmr2->lock);
-		fmr2_setfreq(fmr2);
-		mutex_unlock(&fmr2->lock);
-	}
-	return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
-					struct v4l2_frequency *f)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	if (f->tuner != 0)
-		return -EINVAL;
-	f->type = V4L2_TUNER_RADIO;
-	f->frequency = fmr2->curfreq;
-	return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (qc->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-	case V4L2_CID_AUDIO_VOLUME:
-		/* Only card_type == 11 implements volume */
-		if (fmr2->card_type == 11)
-			return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
-		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
+	struct fmr2 *fmr2 = tea->private_data;
+	int volume, balance, left, right;
 
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = fmr2->mute;
-		return 0;
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = fmr2->curvol;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct fmr2 *fmr2 = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		fmr2->mute = ctrl->value;
+		volume = ctrl->val;
+		balance = fmr2->balance->cur.val;
 		break;
-	case V4L2_CID_AUDIO_VOLUME:
-		fmr2->curvol = ctrl->value;
+	case V4L2_CID_AUDIO_BALANCE:
+		balance = ctrl->val;
+		volume = fmr2->volume->cur.val;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-#ifdef DEBUG
-	if (fmr2->curvol && !fmr2->mute)
-		printk(KERN_DEBUG "unmute\n");
-	else
-		printk(KERN_DEBUG "mute\n");
-#endif
+	left = right = volume;
+	if (balance < 0)
+		right = max(0, right + balance);
+	if (balance > 0)
+		left = max(0, left - balance);
 
-	mutex_lock(&fmr2->lock);
-	if (fmr2->curvol && !fmr2->mute) {
-		fmr2_setvolume(fmr2);
-		/* Set frequency and unmute card */
-		fmr2_setfreq(fmr2);
-	} else
-		fmr2_mute(fmr2->io);
-	mutex_unlock(&fmr2->lock);
+	tc9154a_set_attenuation(fmr2, abs(left - 68), TC9154A_CHANNEL_LEFT);
+	tc9154a_set_attenuation(fmr2, abs(right - 68), TC9154A_CHANNEL_RIGHT);
+
 	return 0;
 }
 
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	a->index = 0;
-	strlcpy(a->name, "Radio", sizeof(a->name));
-	a->capability = V4L2_AUDCAP_STEREO;
-	return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-					struct v4l2_audio *a)
-{
-	return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations fmr2_fops = {
-	.owner          = THIS_MODULE,
-	.unlocked_ioctl = video_ioctl2,
+static const struct v4l2_ctrl_ops fmr2_ctrl_ops = {
+	.s_ctrl = fmr2_s_ctrl,
 };
 
-static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
-	.vidioc_querycap    = vidioc_querycap,
-	.vidioc_g_tuner     = vidioc_g_tuner,
-	.vidioc_s_tuner     = vidioc_s_tuner,
-	.vidioc_g_audio     = vidioc_g_audio,
-	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
-	.vidioc_g_frequency = vidioc_g_frequency,
-	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
-};
+static int fmr2_tea_ext_init(struct snd_tea575x *tea)
+{
+	struct fmr2 *fmr2 = tea->private_data;
+
+	if (inb(fmr2->io) & FMR2_HASVOL) {
+		fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
+		fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
+		if (tea->ctrl_handler.error) {
+			printk(KERN_ERR "radio-sf16fmr2: can't initialize contrls\n");
+			return tea->ctrl_handler.error;
+		}
+	}
+
+	return 0;
+}
 
 static int __init fmr2_init(void)
 {
 	struct fmr2 *fmr2 = &fmr2_card;
-	struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
-	int res;
 
-	strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
-	fmr2->io = io;
-	fmr2->stereo = 1;
-	mutex_init(&fmr2->lock);
+	fmr2->io = FMR2_PORT;
 
-	if (!request_region(fmr2->io, 2, "sf16fmr2")) {
-		v4l2_err(v4l2_dev, "request_region failed!\n");
+	if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+		printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
 		return -EBUSY;
 	}
 
-	res = v4l2_device_register(NULL, v4l2_dev);
-	if (res < 0) {
+	fmr2->tea.private_data = fmr2;
+	fmr2->tea.ops = &fmr2_tea_ops;
+	fmr2->tea.ext_init = fmr2_tea_ext_init;
+	strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
+	strcpy(fmr2->tea.bus_info, "ISA");
+
+	if (snd_tea575x_init(&fmr2->tea)) {
+		printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
 		release_region(fmr2->io, 2);
-		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-		return res;
+		return -ENODEV;
 	}
 
-	strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
-	fmr2->vdev.v4l2_dev = v4l2_dev;
-	fmr2->vdev.fops = &fmr2_fops;
-	fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
-	fmr2->vdev.release = video_device_release_empty;
-	video_set_drvdata(&fmr2->vdev, fmr2);
-
-	/* mute card - prevents noisy bootups */
-	fmr2_mute(fmr2->io);
-	fmr2_product_info(fmr2);
-
-	if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-		v4l2_device_unregister(v4l2_dev);
-		release_region(fmr2->io, 2);
-		return -EINVAL;
-	}
-
-	v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
-	debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+	printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
 	return 0;
 }
 
@@ -443,22 +211,9 @@
 {
 	struct fmr2 *fmr2 = &fmr2_card;
 
-	video_unregister_device(&fmr2->vdev);
-	v4l2_device_unregister(&fmr2->v4l2_dev);
+	snd_tea575x_exit(&fmr2->tea);
 	release_region(fmr2->io, 2);
 }
 
 module_init(fmr2_init);
 module_exit(fmr2_exit);
-
-#ifndef MODULE
-
-static int __init fmr2_setup_io(char *str)
-{
-	get_option(&str, &io);
-	return 1;
-}
-
-__setup("sf16fmr2=", fmr2_setup_io);
-
-#endif
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 0e71d81..95ddcc4 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -39,10 +39,8 @@
 #include <linux/i2c.h>			/* I2C				*/
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
-#include <linux/version.h>      	/* for KERNEL_VERSION MACRO     */
 
-#define DRIVER_VERSION	"v0.01"
-#define RADIO_VERSION	KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION	"0.0.2"
 
 #define DRIVER_AUTHOR	"Fabio Belavenuto <belavenuto@gmail.com>"
 #define DRIVER_DESC	"A driver for the TEA5764 radio chip for EZX Phones."
@@ -300,7 +298,6 @@
 	strlcpy(v->card, dev->name, sizeof(v->card));
 	snprintf(v->bus_info, sizeof(v->bus_info),
 		 "I2C:%s", dev_name(&dev->dev));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
@@ -595,8 +592,9 @@
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
 
-module_param(use_xtal, int, 1);
+module_param(use_xtal, int, 0);
 MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
 module_param(radio_nr, int, 0);
 MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index a326639..f2ed9cc 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -29,7 +29,6 @@
 #include <linux/ioport.h>	/* request_region		*/
 #include <linux/videodev2.h>	/* kernel radio structs		*/
 #include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p			*/
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -37,6 +36,7 @@
 MODULE_AUTHOR("R.OFFERMANNS & others");
 MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 #ifndef CONFIG_RADIO_TERRATEC_PORT
 #define CONFIG_RADIO_TERRATEC_PORT 0x590
@@ -49,8 +49,6 @@
 MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 static struct v4l2_queryctrl radio_qctrl[] = {
 	{
 		.id            = V4L2_CID_AUDIO_MUTE,
@@ -205,7 +203,6 @@
 	strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
 	strlcpy(v->card, "ActiveRadio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index a185610..f17b540 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -16,7 +16,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/version.h>
 #include <linux/io.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
@@ -44,7 +43,6 @@
 	strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver));
 	strlcpy(v->card, "Timberdale Radio", sizeof(v->card));
 	snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
-	v->version = KERNEL_VERSION(0, 0, 1);
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
@@ -245,4 +243,5 @@
 MODULE_DESCRIPTION("Timberdale Radio driver");
 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
 MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.0.2");
 MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 22fa9cc..b3f45a0 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/videodev2.h>
 #include <linux/io.h>
 #include <media/v4l2-device.h>
@@ -28,6 +27,7 @@
 MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
 
@@ -42,8 +42,6 @@
 MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct trust {
 	struct v4l2_device v4l2_dev;
 	struct video_device vdev;
@@ -196,7 +194,6 @@
 	strlcpy(v->driver, "radio-trust", sizeof(v->driver));
 	strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 8dbbf08..398726a 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -31,15 +31,17 @@
 #include <linux/module.h>	/* Modules                        */
 #include <linux/init.h>		/* Initdata                       */
 #include <linux/ioport.h>	/* request_region		  */
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/videodev2.h>	/* kernel radio structs           */
 #include <linux/io.h>		/* outb, outb_p                   */
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 
+#define DRIVER_VERSION "0.1.2"
+
 MODULE_AUTHOR("Dr. Henrik Seidel");
 MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
 
 #ifndef CONFIG_RADIO_TYPHOON_PORT
 #define CONFIG_RADIO_TYPHOON_PORT -1
@@ -61,9 +63,7 @@
 module_param(mutefreq, ulong, 0);
 MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
 
-#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
-
-#define BANNER "Typhoon Radio Card driver v0.1.1\n"
+#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
 
 struct typhoon {
 	struct v4l2_device v4l2_dev;
@@ -171,7 +171,6 @@
 	strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
 	strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 459f727..46cacf8 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1382,7 +1382,7 @@
 
 	switch (ctrl->id) {
 	case  V4L2_CID_TUNE_ANTENNA_CAPACITOR:
-		ctrl->cur.val = wl1273_fm_get_tx_ctune(radio);
+		ctrl->val = wl1273_fm_get_tx_ctune(radio);
 		break;
 
 	default:
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index af99c5b..f5613b9 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -35,7 +35,6 @@
 #include <linux/delay.h>	/* udelay, msleep                 */
 #include <linux/videodev2.h>	/* kernel radio structs           */
 #include <linux/mutex.h>
-#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 #include <linux/io.h>		/* outb, outb_p                   */
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -43,6 +42,7 @@
 MODULE_AUTHOR("C.van Schaik");
 MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
 
 #ifndef CONFIG_RADIO_ZOLTRIX_PORT
 #define CONFIG_RADIO_ZOLTRIX_PORT -1
@@ -55,8 +55,6 @@
 MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
 module_param(radio_nr, int, 0);
 
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-
 struct zoltrix {
 	struct v4l2_device v4l2_dev;
 	struct video_device vdev;
@@ -228,7 +226,6 @@
 	strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
 	strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-	v->version = RADIO_VERSION;
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index a2a6777..fd3541b 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -24,10 +24,9 @@
 
 /* driver definitions */
 #define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 1)
 #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
 #define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.1"
+#define DRIVER_VERSION "1.0.2"
 
 /* kernel includes */
 #include <linux/i2c.h>
@@ -248,7 +247,6 @@
 {
 	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
 	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
-	capability->version = DRIVER_KERNEL_VERSION;
 	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
 		V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 392e84f..4cf5370 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -29,7 +29,6 @@
 
 /* driver definitions */
 #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
 #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
 #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
 #define DRIVER_VERSION "1.0.10"
@@ -626,7 +625,6 @@
 	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
 	usb_make_path(radio->usbdev, capability->bus_info,
 			sizeof(capability->bus_info));
-	capability->version = DRIVER_KERNEL_VERSION;
 	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
 		V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
 
@@ -699,7 +697,7 @@
 	radio->videodev = video_device_alloc();
 	if (!radio->videodev) {
 		retval = -ENOMEM;
-		goto err_intbuffer;
+		goto err_urb;
 	}
 	memcpy(radio->videodev, &si470x_viddev_template,
 			sizeof(si470x_viddev_template));
@@ -790,6 +788,8 @@
 	kfree(radio->buffer);
 err_video:
 	video_device_release(radio->videodev);
+err_urb:
+	usb_free_urb(radio->int_in_urb);
 err_intbuffer:
 	kfree(radio->int_in_buffer);
 err_radio:
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 68da001..f300a55 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -32,7 +32,6 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/input.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/mutex.h>
 #include <media/v4l2-common.h>
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index 1a45a5d..d84ad9d 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -28,14 +28,11 @@
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <linux/timer.h>
-#include <linux/version.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ctrls.h>
 
-#define FM_DRV_VERSION            "0.10"
-/* Should match with FM_DRV_VERSION */
-#define FM_DRV_RADIO_VERSION      KERNEL_VERSION(0, 0, 1)
+#define FM_DRV_VERSION            "0.1.1"
 #define FM_DRV_NAME               "ti_fmdrv"
 #define FM_DRV_CARD_SHORT_NAME    "TI FM Radio"
 #define FM_DRV_CARD_LONG_NAME     "Texas Instruments FM Radio"
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 8701072..8c0e192 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -175,7 +175,6 @@
 	strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
 			sizeof(capability->card));
 	sprintf(capability->bus_info, "UART");
-	capability->version = FM_DRV_RADIO_VERSION;
 	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
 		V4L2_CAP_RADIO | V4L2_CAP_MODULATOR |
 		V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
@@ -191,7 +190,7 @@
 
 	switch (ctrl->id) {
 	case  V4L2_CID_TUNE_ANTENNA_CAPACITOR:
-		ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev);
+		ctrl->val = fm_tx_get_tune_cap_val(fmdev);
 		break;
 	default:
 		fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 7d4bbc2..899f783 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -87,6 +87,17 @@
 	   uses an IR protocol that is almost standard RC-5, but not quite,
 	   as it uses an additional bit).
 
+config IR_MCE_KBD_DECODER
+	tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have a Microsoft Remote Keyboard for
+	   Windows Media Center Edition, which you would like to use with
+	   a raw IR receiver in your system.
+
 config IR_LIRC_CODEC
 	tristate "Enable IR to LIRC bridge"
 	depends on RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 52830e5..f224db0 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
 obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
 obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
+obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
 obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
 
 # stand-alone IR receivers/transmitters
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index a43ed6c..2b9c2569 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -953,13 +953,13 @@
 }
 
 /* outside interface: transmit */
-static int ene_transmit(struct rc_dev *rdev, int *buf, u32 n)
+static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n)
 {
 	struct ene_device *dev = rdev->priv;
 	unsigned long flags;
 
 	dev->tx_buffer = buf;
-	dev->tx_len = n / sizeof(int);
+	dev->tx_len = n;
 	dev->tx_pos = 0;
 	dev->tx_reg = 0;
 	dev->tx_done = 0;
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
index 337a41d..017c209 100644
--- a/drivers/media/rc/ene_ir.h
+++ b/drivers/media/rc/ene_ir.h
@@ -235,7 +235,7 @@
 	bool tx_sample_pulse;			/* current sample is pulse */
 
 	/* TX buffer */
-	int *tx_buffer;				/* input samples buffer*/
+	unsigned *tx_buffer;			/* input samples buffer*/
 	int tx_pos;				/* position in that bufer */
 	int tx_len;				/* current len of tx buffer */
 	int tx_done;				/* done transmitting */
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 1c5cc65..e5eeec4 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -103,19 +103,19 @@
 {
 	struct lirc_codec *lirc;
 	struct rc_dev *dev;
-	int *txbuf; /* buffer with values to transmit */
-	int ret = 0;
+	unsigned int *txbuf; /* buffer with values to transmit */
+	ssize_t ret = 0;
 	size_t count;
 
 	lirc = lirc_get_pdata(file);
 	if (!lirc)
 		return -EFAULT;
 
-	if (n % sizeof(int))
+	if (n < sizeof(unsigned) || n % sizeof(unsigned))
 		return -EINVAL;
 
-	count = n / sizeof(int);
-	if (count > LIRCBUF_SIZE || count % 2 == 0 || n % sizeof(int) != 0)
+	count = n / sizeof(unsigned);
+	if (count > LIRCBUF_SIZE || count % 2 == 0)
 		return -EINVAL;
 
 	txbuf = memdup_user(buf, n);
@@ -129,7 +129,10 @@
 	}
 
 	if (dev->tx_ir)
-		ret = dev->tx_ir(dev, txbuf, (u32)n);
+		ret = dev->tx_ir(dev, txbuf, count);
+
+	if (ret > 0)
+		ret *= sizeof(unsigned);
 
 out:
 	kfree(txbuf);
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
new file mode 100644
index 0000000..3784ebf
--- /dev/null
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -0,0 +1,449 @@
+/* ir-mce_kbd-decoder.c - A decoder for the RC6-ish keyboard/mouse IR protocol
+ * used by the Microsoft Remote Keyboard for Windows Media Center Edition,
+ * referred to by Microsoft's Windows Media Center remote specification docs
+ * as "an internal protocol called MCIR-2".
+ *
+ * Copyright (C) 2011 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+
+#include "rc-core-priv.h"
+
+/*
+ * This decoder currently supports:
+ * - MCIR-2 29-bit IR signals used for mouse movement and buttons
+ * - MCIR-2 32-bit IR signals used for standard keyboard keys
+ *
+ * The media keys on the keyboard send RC-6 signals that are inditinguishable
+ * from the keys of the same name on the stock MCE remote, and will be handled
+ * by the standard RC-6 decoder, and be made available to the system via the
+ * input device for the remote, rather than the keyboard/mouse one.
+ */
+
+#define MCIR2_UNIT		333333	/* ns */
+#define MCIR2_HEADER_NBITS	5
+#define MCIR2_MOUSE_NBITS	29
+#define MCIR2_KEYBOARD_NBITS	32
+#define MCIR2_PREFIX_PULSE	(8 * MCIR2_UNIT)
+#define MCIR2_PREFIX_SPACE	(1 * MCIR2_UNIT)
+#define MCIR2_MAX_LEN		(3 * MCIR2_UNIT)
+#define MCIR2_BIT_START		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_END		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_0		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_SET		(2 * MCIR2_UNIT)
+#define MCIR2_MODE_MASK		0xf	/* for the header bits */
+#define MCIR2_KEYBOARD_HEADER	0x4
+#define MCIR2_MOUSE_HEADER	0x1
+#define MCIR2_MASK_KEYS_START	0xe0
+
+enum mce_kbd_mode {
+	MCIR2_MODE_KEYBOARD,
+	MCIR2_MODE_MOUSE,
+	MCIR2_MODE_UNKNOWN,
+};
+
+enum mce_kbd_state {
+	STATE_INACTIVE,
+	STATE_HEADER_BIT_START,
+	STATE_HEADER_BIT_END,
+	STATE_BODY_BIT_START,
+	STATE_BODY_BIT_END,
+	STATE_FINISHED,
+};
+
+static unsigned char kbd_keycodes[256] = {
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_A,
+	KEY_B,		KEY_C,		KEY_D,		KEY_E,		KEY_F,
+	KEY_G,		KEY_H,		KEY_I,		KEY_J,		KEY_K,
+	KEY_L,		KEY_M,		KEY_N,		KEY_O,		KEY_P,
+	KEY_Q,		KEY_R,		KEY_S,		KEY_T,		KEY_U,
+	KEY_V,		KEY_W,		KEY_X,		KEY_Y,		KEY_Z,
+	KEY_1,		KEY_2,		KEY_3,		KEY_4,		KEY_5,
+	KEY_6,		KEY_7,		KEY_8,		KEY_9,		KEY_0,
+	KEY_ENTER,	KEY_ESC,	KEY_BACKSPACE,	KEY_TAB,	KEY_SPACE,
+	KEY_MINUS,	KEY_EQUAL,	KEY_LEFTBRACE,	KEY_RIGHTBRACE,	KEY_BACKSLASH,
+	KEY_RESERVED,	KEY_SEMICOLON,	KEY_APOSTROPHE,	KEY_GRAVE,	KEY_COMMA,
+	KEY_DOT,	KEY_SLASH,	KEY_CAPSLOCK,	KEY_F1,		KEY_F2,
+	KEY_F3,		KEY_F4,		KEY_F5,		KEY_F6,		KEY_F7,
+	KEY_F8,		KEY_F9,		KEY_F10,	KEY_F11,	KEY_F12,
+	KEY_SYSRQ,	KEY_SCROLLLOCK,	KEY_PAUSE,	KEY_INSERT,	KEY_HOME,
+	KEY_PAGEUP,	KEY_DELETE,	KEY_END,	KEY_PAGEDOWN,	KEY_RIGHT,
+	KEY_LEFT,	KEY_DOWN,	KEY_UP,		KEY_NUMLOCK,	KEY_KPSLASH,
+	KEY_KPASTERISK,	KEY_KPMINUS,	KEY_KPPLUS,	KEY_KPENTER,	KEY_KP1,
+	KEY_KP2,	KEY_KP3,	KEY_KP4,	KEY_KP5,	KEY_KP6,
+	KEY_KP7,	KEY_KP8,	KEY_KP9,	KEY_KP0,	KEY_KPDOT,
+	KEY_102ND,	KEY_COMPOSE,	KEY_POWER,	KEY_KPEQUAL,	KEY_F13,
+	KEY_F14,	KEY_F15,	KEY_F16,	KEY_F17,	KEY_F18,
+	KEY_F19,	KEY_F20,	KEY_F21,	KEY_F22,	KEY_F23,
+	KEY_F24,	KEY_OPEN,	KEY_HELP,	KEY_PROPS,	KEY_FRONT,
+	KEY_STOP,	KEY_AGAIN,	KEY_UNDO,	KEY_CUT,	KEY_COPY,
+	KEY_PASTE,	KEY_FIND,	KEY_MUTE,	KEY_VOLUMEUP,	KEY_VOLUMEDOWN,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_KPCOMMA,	KEY_RESERVED,
+	KEY_RO,		KEY_KATAKANAHIRAGANA, KEY_YEN,	KEY_HENKAN,	KEY_MUHENKAN,
+	KEY_KPJPCOMMA,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_HANGUEL,
+	KEY_HANJA,	KEY_KATAKANA,	KEY_HIRAGANA,	KEY_ZENKAKUHANKAKU, KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_LEFTCTRL,
+	KEY_LEFTSHIFT,	KEY_LEFTALT,	KEY_LEFTMETA,	KEY_RIGHTCTRL,	KEY_RIGHTSHIFT,
+	KEY_RIGHTALT,	KEY_RIGHTMETA,	KEY_PLAYPAUSE,	KEY_STOPCD,	KEY_PREVIOUSSONG,
+	KEY_NEXTSONG,	KEY_EJECTCD,	KEY_VOLUMEUP,	KEY_VOLUMEDOWN,	KEY_MUTE,
+	KEY_WWW,	KEY_BACK,	KEY_FORWARD,	KEY_STOP,	KEY_FIND,
+	KEY_SCROLLUP,	KEY_SCROLLDOWN,	KEY_EDIT,	KEY_SLEEP,	KEY_COFFEE,
+	KEY_REFRESH,	KEY_CALC,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED
+};
+
+static void mce_kbd_rx_timeout(unsigned long data)
+{
+	struct mce_kbd_dec *mce_kbd = (struct mce_kbd_dec *)data;
+	int i;
+	unsigned char maskcode;
+
+	IR_dprintk(2, "timer callback clearing all keys\n");
+
+	for (i = 0; i < 7; i++) {
+		maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+		input_report_key(mce_kbd->idev, maskcode, 0);
+	}
+
+	for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+		input_report_key(mce_kbd->idev, kbd_keycodes[i], 0);
+}
+
+static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
+{
+	switch (data->header & MCIR2_MODE_MASK) {
+	case MCIR2_KEYBOARD_HEADER:
+		return MCIR2_MODE_KEYBOARD;
+	case MCIR2_MOUSE_HEADER:
+		return MCIR2_MODE_MOUSE;
+	default:
+		return MCIR2_MODE_UNKNOWN;
+	}
+}
+
+static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
+					     u32 scancode)
+{
+	u8 keydata   = (scancode >> 8) & 0xff;
+	u8 shiftmask = scancode & 0xff;
+	unsigned char keycode, maskcode;
+	int i, keystate;
+
+	IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
+		   keydata, shiftmask);
+
+	for (i = 0; i < 7; i++) {
+		maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+		if (shiftmask & (1 << i))
+			keystate = 1;
+		else
+			keystate = 0;
+		input_report_key(idev, maskcode, keystate);
+	}
+
+	if (keydata) {
+		keycode = kbd_keycodes[keydata];
+		input_report_key(idev, keycode, 1);
+	} else {
+		for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+			input_report_key(idev, kbd_keycodes[i], 0);
+	}
+}
+
+static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
+{
+	/* raw mouse coordinates */
+	u8 xdata = (scancode >> 7) & 0x7f;
+	u8 ydata = (scancode >> 14) & 0x7f;
+	int x, y;
+	/* mouse buttons */
+	bool right = scancode & 0x40;
+	bool left  = scancode & 0x20;
+
+	if (xdata & 0x40)
+		x = -((~xdata & 0x7f) + 1);
+	else
+		x = xdata;
+
+	if (ydata & 0x40)
+		y = -((~ydata & 0x7f) + 1);
+	else
+		y = ydata;
+
+	IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n",
+		   x, y, left ? "L" : "", right ? "R" : "");
+
+	input_report_rel(idev, REL_X, x);
+	input_report_rel(idev, REL_Y, y);
+
+	input_report_key(idev, BTN_LEFT, left);
+	input_report_key(idev, BTN_RIGHT, right);
+}
+
+/**
+ * ir_mce_kbd_decode() - Decode one mce_kbd pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct mce_kbd_dec *data = &dev->raw->mce_kbd;
+	u32 scancode;
+	unsigned long delay;
+
+	if (!(dev->raw->enabled_protocols & RC_TYPE_MCE_KBD))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+		goto out;
+
+again:
+	IR_dprintk(2, "started at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+		return 0;
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		/* Note: larger margin on first pulse since each MCIR2_UNIT
+		   is quite short and some hardware takes some time to
+		   adjust to the signal */
+		if (!eq_margin(ev.duration, MCIR2_PREFIX_PULSE, MCIR2_UNIT))
+			break;
+
+		data->state = STATE_HEADER_BIT_START;
+		data->count = 0;
+		data->header = 0;
+		return 0;
+
+	case STATE_HEADER_BIT_START:
+		if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+			break;
+
+		data->header <<= 1;
+		if (ev.pulse)
+			data->header |= 1;
+		data->count++;
+		data->state = STATE_HEADER_BIT_END;
+		return 0;
+
+	case STATE_HEADER_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		decrease_duration(&ev, MCIR2_BIT_END);
+
+		if (data->count != MCIR2_HEADER_NBITS) {
+			data->state = STATE_HEADER_BIT_START;
+			goto again;
+		}
+
+		switch (mce_kbd_mode(data)) {
+		case MCIR2_MODE_KEYBOARD:
+			data->wanted_bits = MCIR2_KEYBOARD_NBITS;
+			break;
+		case MCIR2_MODE_MOUSE:
+			data->wanted_bits = MCIR2_MOUSE_NBITS;
+			break;
+		default:
+			IR_dprintk(1, "not keyboard or mouse data\n");
+			goto out;
+		}
+
+		data->count = 0;
+		data->body = 0;
+		data->state = STATE_BODY_BIT_START;
+		goto again;
+
+	case STATE_BODY_BIT_START:
+		if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+			break;
+
+		data->body <<= 1;
+		if (ev.pulse)
+			data->body |= 1;
+		data->count++;
+		data->state = STATE_BODY_BIT_END;
+		return 0;
+
+	case STATE_BODY_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		if (data->count == data->wanted_bits)
+			data->state = STATE_FINISHED;
+		else
+			data->state = STATE_BODY_BIT_START;
+
+		decrease_duration(&ev, MCIR2_BIT_END);
+		goto again;
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		switch (data->wanted_bits) {
+		case MCIR2_KEYBOARD_NBITS:
+			scancode = data->body & 0xffff;
+			IR_dprintk(1, "keyboard data 0x%08x\n", data->body);
+			if (dev->timeout)
+				delay = usecs_to_jiffies(dev->timeout / 1000);
+			else
+				delay = msecs_to_jiffies(100);
+			mod_timer(&data->rx_timeout, jiffies + delay);
+			/* Pass data to keyboard buffer parser */
+			ir_mce_kbd_process_keyboard_data(data->idev, scancode);
+			break;
+		case MCIR2_MOUSE_NBITS:
+			scancode = data->body & 0x1fffff;
+			IR_dprintk(1, "mouse data 0x%06x\n", scancode);
+			/* Pass data to mouse buffer parser */
+			ir_mce_kbd_process_mouse_data(data->idev, scancode);
+			break;
+		default:
+			IR_dprintk(1, "not keyboard or mouse data\n");
+			goto out;
+		}
+
+		data->state = STATE_INACTIVE;
+		input_sync(data->idev);
+		return 0;
+	}
+
+out:
+	IR_dprintk(1, "failed at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	input_sync(data->idev);
+	return -EINVAL;
+}
+
+static int ir_mce_kbd_register(struct rc_dev *dev)
+{
+	struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+	struct input_dev *idev;
+	int i, ret;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	snprintf(mce_kbd->name, sizeof(mce_kbd->name),
+		 "MCE IR Keyboard/Mouse (%s)", dev->driver_name);
+	strlcat(mce_kbd->phys, "/input0", sizeof(mce_kbd->phys));
+
+	idev->name = mce_kbd->name;
+	idev->phys = mce_kbd->phys;
+
+	/* Keyboard bits */
+	set_bit(EV_KEY, idev->evbit);
+	set_bit(EV_REP, idev->evbit);
+	for (i = 0; i < sizeof(kbd_keycodes); i++)
+		set_bit(kbd_keycodes[i], idev->keybit);
+
+	/* Mouse bits */
+	set_bit(EV_REL, idev->evbit);
+	set_bit(REL_X, idev->relbit);
+	set_bit(REL_Y, idev->relbit);
+	set_bit(BTN_LEFT, idev->keybit);
+	set_bit(BTN_RIGHT, idev->keybit);
+
+	/* Report scancodes too */
+	set_bit(EV_MSC, idev->evbit);
+	set_bit(MSC_SCAN, idev->mscbit);
+
+	setup_timer(&mce_kbd->rx_timeout, mce_kbd_rx_timeout,
+		    (unsigned long)mce_kbd);
+
+	input_set_drvdata(idev, mce_kbd);
+
+#if 0
+	/* Adding this reference means two input devices are associated with
+	 * this rc-core device, which ir-keytable doesn't cope with yet */
+	idev->dev.parent = &dev->dev;
+#endif
+
+	ret = input_register_device(idev);
+	if (ret < 0) {
+		input_free_device(idev);
+		return -EIO;
+	}
+
+	mce_kbd->idev = idev;
+
+	return 0;
+}
+
+static int ir_mce_kbd_unregister(struct rc_dev *dev)
+{
+	struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+	struct input_dev *idev = mce_kbd->idev;
+
+	del_timer_sync(&mce_kbd->rx_timeout);
+	input_unregister_device(idev);
+
+	return 0;
+}
+
+static struct ir_raw_handler mce_kbd_handler = {
+	.protocols	= RC_TYPE_MCE_KBD,
+	.decode		= ir_mce_kbd_decode,
+	.raw_register	= ir_mce_kbd_register,
+	.raw_unregister	= ir_mce_kbd_unregister,
+};
+
+static int __init ir_mce_kbd_decode_init(void)
+{
+	ir_raw_handler_register(&mce_kbd_handler);
+
+	printk(KERN_INFO "IR MCE Keyboard/mouse protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_mce_kbd_decode_exit(void)
+{
+	ir_raw_handler_unregister(&mce_kbd_handler);
+}
+
+module_init(ir_mce_kbd_decode_init);
+module_exit(ir_mce_kbd_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_DESCRIPTION("MCE Keyboard/mouse IR protocol decoder");
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 423ed45..27808bb 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -355,6 +355,7 @@
 	load_rc6_decode();
 	load_jvc_decode();
 	load_sony_decode();
+	load_mce_kbd_decode();
 	load_lirc_codec();
 
 	/* If needed, we may later add some init code. In this case,
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index d20168f..682009d 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -382,7 +382,7 @@
 /* transmit out IR pulses; what you get here is a batch of alternating
  * pulse/space/pulse/space lengths that we should write out completely through
  * the FIFO, blocking on a full FIFO */
-static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n)
+static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n)
 {
 	unsigned long flags;
 	struct ite_dev *dev = rcdev->priv;
@@ -398,9 +398,6 @@
 	/* clear the array just in case */
 	memset(last_sent, 0, ARRAY_SIZE(last_sent));
 
-	/* n comes in bytes; convert to ints */
-	n /= sizeof(int);
-
 	spin_lock_irqsave(&dev->lock, flags);
 
 	/* let everybody know we're now transmitting */
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
index 01b69bc..c3907e2 100644
--- a/drivers/media/rc/keymaps/rc-rc6-mce.c
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -29,7 +29,7 @@
 
 	{ 0x800f040a, KEY_DELETE },
 	{ 0x800f040b, KEY_ENTER },
-	{ 0x800f040c, KEY_POWER },		/* PC Power */
+	{ 0x800f040c, KEY_SLEEP },		/* Formerly PC Power */
 	{ 0x800f040d, KEY_MEDIA },		/* Windows MCE button */
 	{ 0x800f040e, KEY_MUTE },
 	{ 0x800f040f, KEY_INFO },
@@ -44,7 +44,6 @@
 	{ 0x800f0416, KEY_PLAY },
 	{ 0x800f0417, KEY_RECORD },
 	{ 0x800f0418, KEY_PAUSE },
-	{ 0x800f046e, KEY_PLAYPAUSE },
 	{ 0x800f0419, KEY_STOP },
 	{ 0x800f041a, KEY_NEXT },
 	{ 0x800f041b, KEY_PREVIOUS },
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index ec972dc..85ff9a1 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -692,20 +692,18 @@
 }
 
 /* Send data out the IR blaster port(s) */
-static int mceusb_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
 {
 	struct mceusb_dev *ir = dev->priv;
 	int i, ret = 0;
-	int count, cmdcount = 0;
+	int cmdcount = 0;
 	unsigned char *cmdbuf; /* MCE command buffer */
 	long signal_duration = 0; /* Singnal length in us */
 	struct timeval start_time, end_time;
 
 	do_gettimeofday(&start_time);
 
-	count = n / sizeof(int);
-
-	cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL);
+	cmdbuf = kzalloc(sizeof(unsigned) * MCE_CMDBUF_SIZE, GFP_KERNEL);
 	if (!cmdbuf)
 		return -ENOMEM;
 
@@ -774,7 +772,7 @@
 
 out:
 	kfree(cmdbuf);
-	return ret ? ret : n;
+	return ret ? ret : count;
 }
 
 /* Sets active IR outputs -- mce devices typically have two */
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index ce595f9..eae05b5 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -546,24 +546,18 @@
  * number may larger than TXFCONT (0xff). So in interrupt_handler, it has to
  * set TXFCONT as 0xff, until buf_count less than 0xff.
  */
-static int nvt_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int nvt_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned n)
 {
 	struct nvt_dev *nvt = dev->priv;
 	unsigned long flags;
-	size_t cur_count;
 	unsigned int i;
 	u8 iren;
 	int ret;
 
 	spin_lock_irqsave(&nvt->tx.lock, flags);
 
-	if (n >= TX_BUF_LEN) {
-		nvt->tx.buf_count = cur_count = TX_BUF_LEN;
-		ret = TX_BUF_LEN;
-	} else {
-		nvt->tx.buf_count = cur_count = n;
-		ret = n;
-	}
+	ret = min((unsigned)(TX_BUF_LEN / sizeof(unsigned)), n);
+	nvt->tx.buf_count = (ret * sizeof(unsigned));
 
 	memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count);
 
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 873b387..04c2c72 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -84,6 +84,17 @@
 		unsigned count;
 		unsigned wanted_bits;
 	} rc5_sz;
+	struct mce_kbd_dec {
+		struct input_dev *idev;
+		struct timer_list rx_timeout;
+		char name[64];
+		char phys[64];
+		int state;
+		u8 header;
+		u32 body;
+		unsigned count;
+		unsigned wanted_bits;
+	} mce_kbd;
 	struct lirc_codec {
 		struct rc_dev *dev;
 		struct lirc_driver *drv;
@@ -182,6 +193,13 @@
 #define load_sony_decode()	0
 #endif
 
+/* from ir-mce_kbd-decoder.c */
+#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
+#define load_mce_kbd_decode()	request_module("ir-mce_kbd-decoder")
+#else
+#define load_mce_kbd_decode()	0
+#endif
+
 /* from ir-lirc-codec.c */
 #ifdef CONFIG_IR_LIRC_CODEC_MODULE
 #define load_lirc_codec()	request_module("ir-lirc-codec")
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index cc846b2..efc6a51 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -101,21 +101,14 @@
 	return 0;
 }
 
-static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
+static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
 {
 	struct loopback_dev *lodev = dev->priv;
 	u32 rxmask;
-	unsigned count;
 	unsigned total_duration = 0;
 	unsigned i;
 	DEFINE_IR_RAW_EVENT(rawir);
 
-	if (n == 0 || n % sizeof(int)) {
-		dprintk("invalid tx buffer size\n");
-		return -EINVAL;
-	}
-
-	count = n / sizeof(int);
 	for (i = 0; i < count; i++)
 		total_duration += abs(txbuf[i]);
 
@@ -142,7 +135,7 @@
 
 	for (i = 0; i < count; i++) {
 		rawir.pulse = i % 2 ? false : true;
-		rawir.duration = abs(txbuf[i]) * 1000;
+		rawir.duration = txbuf[i] * 1000;
 		if (rawir.duration)
 			ir_raw_event_store_with_filter(dev, &rawir);
 	}
@@ -158,7 +151,7 @@
 	/* Lirc expects this function to take as long as the total duration */
 	set_current_state(TASK_INTERRUPTIBLE);
 	schedule_timeout(usecs_to_jiffies(total_duration));
-	return n;
+	return count;
 }
 
 static void loop_set_idle(struct rc_dev *dev, bool enable)
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 3186ac7..51a23f4 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -735,6 +735,7 @@
 	{ RC_TYPE_JVC,		"jvc"		},
 	{ RC_TYPE_SONY,		"sony"		},
 	{ RC_TYPE_RC5_SZ,	"rc-5-sz"	},
+	{ RC_TYPE_MCE_KBD,	"mce_kbd"	},
 	{ RC_TYPE_LIRC,		"lirc"		},
 	{ RC_TYPE_OTHER,	"other"		},
 };
@@ -1099,7 +1100,6 @@
 		if (rc < 0)
 			goto out_input;
 	}
-	mutex_unlock(&dev->lock);
 
 	if (dev->change_protocol) {
 		rc = dev->change_protocol(dev, rc_map->rc_type);
@@ -1107,6 +1107,8 @@
 			goto out_raw;
 	}
 
+	mutex_unlock(&dev->lock);
+
 	IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n",
 		   dev->devno,
 		   dev->driver_name ? dev->driver_name : "unknown",
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 5147767..a166044 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -205,6 +205,7 @@
 
 	/* rx signal timeout timer */
 	struct timer_list rx_timeout;
+	u32 hw_timeout;
 
 	/* Is the device currently receiving? */
 	bool recv_in_progress;
@@ -414,20 +415,10 @@
 
 }
 
-/* timer callback to send long trailing space on receive timeout */
+/* timer callback to send reset event */
 static void redrat3_rx_timeout(unsigned long data)
 {
 	struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
-	DEFINE_IR_RAW_EVENT(rawir);
-
-	rawir.pulse = false;
-	rawir.duration = rr3->rc->timeout;
-	rr3_dbg(rr3->dev, "storing trailing space with duration %d\n",
-		rawir.duration);
-	ir_raw_event_store_with_filter(rr3->rc, &rawir);
-
-	rr3_dbg(rr3->dev, "calling ir_raw_event_handle\n");
-	ir_raw_event_handle(rr3->rc);
 
 	rr3_dbg(rr3->dev, "calling ir_raw_event_reset\n");
 	ir_raw_event_reset(rr3->rc);
@@ -438,7 +429,7 @@
 	DEFINE_IR_RAW_EVENT(rawir);
 	struct redrat3_signal_header header;
 	struct device *dev;
-	int i;
+	int i, trailer = 0;
 	unsigned long delay;
 	u32 mod_freq, single_len;
 	u16 *len_vals;
@@ -464,7 +455,8 @@
 	if (!(header.length >= RR3_HEADER_LENGTH))
 		dev_warn(dev, "read returned less than rr3 header len\n");
 
-	delay = usecs_to_jiffies(rr3->rc->timeout / 1000);
+	/* Make sure we reset the IR kfifo after a bit of inactivity */
+	delay = usecs_to_jiffies(rr3->hw_timeout);
 	mod_timer(&rr3->rx_timeout, jiffies + delay);
 
 	memcpy(&tmp32, sig_data + RR3_PAUSE_OFFSET, sizeof(tmp32));
@@ -506,9 +498,6 @@
 		u16 val = len_vals[data_vals[i]];
 		single_len = redrat3_len_to_us((u32)be16_to_cpu(val));
 
-		/* cap the value to IR_MAX_DURATION */
-		single_len &= IR_MAX_DURATION;
-
 		/* we should always get pulse/space/pulse/space samples */
 		if (i % 2)
 			rawir.pulse = false;
@@ -516,6 +505,12 @@
 			rawir.pulse = true;
 
 		rawir.duration = US_TO_NS(single_len);
+		/* Save initial pulse length to fudge trailer */
+		if (i == 0)
+			trailer = rawir.duration;
+		/* cap the value to IR_MAX_DURATION */
+		rawir.duration &= IR_MAX_DURATION;
+
 		rr3_dbg(dev, "storing %s with duration %d (i: %d)\n",
 			rawir.pulse ? "pulse" : "space", rawir.duration, i);
 		ir_raw_event_store_with_filter(rr3->rc, &rawir);
@@ -525,7 +520,10 @@
 	if (i % 2) {
 		rawir.pulse = false;
 		/* this duration is made up, and may not be ideal... */
-		rawir.duration = rr3->rc->timeout / 2;
+		if (trailer < US_TO_NS(1000))
+			rawir.duration = US_TO_NS(2800);
+		else
+			rawir.duration = trailer;
 		rr3_dbg(dev, "storing trailing space with duration %d\n",
 			rawir.duration);
 		ir_raw_event_store_with_filter(rr3->rc, &rawir);
@@ -629,36 +627,31 @@
 	kfree(rr3);
 }
 
-static u32 redrat3_get_timeout(struct device *dev,
-			       struct rc_dev *rc, struct usb_device *udev)
+static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
 {
 	u32 *tmp;
-	u32 timeout = MS_TO_NS(150); /* a sane default, if things go haywire */
+	u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */
 	int len, ret, pipe;
 
 	len = sizeof(*tmp);
 	tmp = kzalloc(len, GFP_KERNEL);
 	if (!tmp) {
-		dev_warn(dev, "Memory allocation faillure\n");
+		dev_warn(rr3->dev, "Memory allocation faillure\n");
 		return timeout;
 	}
 
-	pipe = usb_rcvctrlpipe(udev, 0);
-	ret = usb_control_msg(udev, pipe, RR3_GET_IR_PARAM,
+	pipe = usb_rcvctrlpipe(rr3->udev, 0);
+	ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM,
 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
 			      RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5);
 	if (ret != len) {
-		dev_warn(dev, "Failed to read timeout from hardware\n");
+		dev_warn(rr3->dev, "Failed to read timeout from hardware\n");
 		return timeout;
 	}
 
-	timeout = US_TO_NS(redrat3_len_to_us(be32_to_cpu(*tmp)));
-	if (timeout < rc->min_timeout)
-		timeout = rc->min_timeout;
-	else if (timeout > rc->max_timeout)
-		timeout = rc->max_timeout;
+	timeout = redrat3_len_to_us(be32_to_cpu(*tmp));
 
-	rr3_dbg(dev, "Got timeout of %d ms\n", timeout / (1000 * 1000));
+	rr3_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000);
 	return timeout;
 }
 
@@ -1110,9 +1103,7 @@
 	rc->priv = rr3;
 	rc->driver_type = RC_DRIVER_IR_RAW;
 	rc->allowed_protos = RC_TYPE_ALL;
-	rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT);
-	rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT);
-	rc->timeout = redrat3_get_timeout(dev, rc, rr3->udev);
+	rc->timeout = US_TO_NS(2750);
 	rc->tx_ir = redrat3_transmit_ir;
 	rc->s_tx_carrier = redrat3_set_tx_carrier;
 	rc->driver_name = DRIVER_NAME;
@@ -1186,7 +1177,7 @@
 	rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL);
 	if (rr3 == NULL) {
 		dev_err(dev, "Memory allocation failure\n");
-		goto error;
+		goto no_endpoints;
 	}
 
 	rr3->dev = &intf->dev;
@@ -1242,6 +1233,9 @@
 	if (retval < 0)
 		goto error;
 
+	/* store current hardware timeout, in us, will use for kfifo resets */
+	rr3->hw_timeout = redrat3_get_timeout(rr3);
+
 	/* default.. will get overridden by any sends with a freq defined */
 	rr3->carrier = 38000;
 
@@ -1280,6 +1274,7 @@
 
 	usb_set_intfdata(intf, NULL);
 	rc_unregister_device(rr3->rc);
+	del_timer_sync(&rr3->rx_timeout);
 	redrat3_delete(rr3, udev);
 
 	rr3_ftr(&intf->dev, "RedRat3 IR Transceiver now disconnected\n");
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 5d06b89..bec8abc 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -6,8 +6,8 @@
  *  could probably support others (Winbond WEC102X, NatSemi, etc)
  *  with minor modifications.
  *
- *  Original Author: David Härdeman <david@hardeman.nu>
- *     Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu>
+ *  Original Author: David Härdeman <david@hardeman.nu>
+ *     Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
  *
  *  Dedicated to my daughter Matilda, without whose loving attention this
  *  driver would have been finished in half the time and with a fraction
@@ -577,16 +577,12 @@
 }
 
 static int
-wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize)
+wbcir_tx(struct rc_dev *dev, unsigned *buf, unsigned count)
 {
 	struct wbcir_data *data = dev->priv;
-	u32 count;
 	unsigned i;
 	unsigned long flags;
 
-	/* bufsize has been sanity checked by the caller */
-	count = bufsize / sizeof(int);
-
 	/* Not sure if this is possible, but better safe than sorry */
 	spin_lock_irqsave(&data->spinlock, flags);
 	if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
@@ -876,18 +872,8 @@
 	/* prescaler 1.0, tx/rx fifo lvl 16 */
 	outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
 
-	/* Set baud divisor to generate one byte per bit/cell */
-	switch (protocol) {
-	case IR_PROTOCOL_RC5:
-		outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
-		break;
-	case IR_PROTOCOL_RC6:
-		outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
-		break;
-	case IR_PROTOCOL_NEC:
-		outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
-		break;
-	}
+	/* Set baud divisor to sample every 10 us */
+	outb(0x0F, data->sbase + WBCIR_REG_SP3_BGDL);
 	outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
 
 	/* Set CEIR mode */
@@ -896,9 +882,9 @@
 	inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
 	inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
 
-	/* Disable RX demod, run-length encoding/decoding, set freq span */
+	/* Disable RX demod, enable run-length enc/dec, set freq span */
 	wbcir_select_bank(data, WBCIR_BANK_7);
-	outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+	outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG);
 
 	/* Disable timer */
 	wbcir_select_bank(data, WBCIR_BANK_4);
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index bb53de7..f574dc0 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -489,6 +489,15 @@
 	  This is a driver for the Toshiba TCM825x VGA camera sensor.
 	  It is used for example in Nokia N800.
 
+comment "Flash devices"
+
+config VIDEO_ADP1653
+	tristate "ADP1653 flash support"
+	depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+	---help---
+	  This is a driver for the ADP1653 flash controller. It is used for
+	  example in Nokia N900.
+
 comment "Video improvement chips"
 
 config VIDEO_UPD64031A
@@ -707,6 +716,8 @@
 
 source "drivers/media/video/saa7164/Kconfig"
 
+source "drivers/media/video/marvell-ccic/Kconfig"
+
 config VIDEO_M32R_AR
 	tristate "AR devices"
 	depends on M32R && VIDEO_V4L2
@@ -726,15 +737,6 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called arv.
 
-config VIDEO_CAFE_CCIC
-	tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
-	depends on PCI && I2C && VIDEO_V4L2
-	select VIDEO_OV7670
-	---help---
-	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
-	  CMOS camera controller.  This is the controller found on first-
-	  generation OLPC systems.
-
 config VIDEO_SR030PC30
 	tristate "SR030PC30 VGA camera sensor support"
 	depends on I2C && VIDEO_V4L2
@@ -846,6 +848,12 @@
 	help
 	  This is a ov2640 camera driver
 
+config SOC_CAMERA_OV5642
+	tristate "ov5642 camera support"
+	depends on SOC_CAMERA && I2C
+	help
+	  This is a V4L2 camera driver for the OmniVision OV5642 sensor
+
 config SOC_CAMERA_OV6650
 	tristate "ov6650 sensor support"
 	depends on SOC_CAMERA && I2C
@@ -952,6 +960,14 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called s5p-fimc.
 
+config VIDEO_ATMEL_ISI
+	tristate "ATMEL Image Sensor Interface (ISI) support"
+	depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+	select VIDEOBUF2_DMA_CONTIG
+	---help---
+	  This module makes the ATMEL Image Sensor Interface available
+	  as a v4l2 device.
+
 config VIDEO_S5P_MIPI_CSIS
 	tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
 	depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
@@ -961,6 +977,8 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called s5p-csis.
 
+source "drivers/media/video/s5p-tv/Kconfig"
+
 #
 # USB Multimedia device configuration
 #
@@ -1056,4 +1074,12 @@
 	  framework.
 
 
+config VIDEO_SAMSUNG_S5P_MFC
+	tristate "Samsung S5P MFC 5.1 Video Codec"
+	depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+	select VIDEOBUF2_DMA_CONTIG
+	default n
+	help
+	    MFC 5.1 driver for V4L2.
+
 endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index f0fecd6..2723900 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -70,6 +70,7 @@
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
+obj-$(CONFIG_VIDEO_ADP1653)	+= adp1653.o
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
@@ -78,6 +79,7 @@
 obj-$(CONFIG_SOC_CAMERA_MT9T112)	+= mt9t112.o
 obj-$(CONFIG_SOC_CAMERA_MT9V022)	+= mt9v022.o
 obj-$(CONFIG_SOC_CAMERA_OV2640)		+= ov2640.o
+obj-$(CONFIG_SOC_CAMERA_OV5642)		+= ov5642.o
 obj-$(CONFIG_SOC_CAMERA_OV6650)		+= ov6650.o
 obj-$(CONFIG_SOC_CAMERA_OV772X)		+= ov772x.o
 obj-$(CONFIG_SOC_CAMERA_OV9640)		+= ov9640.o
@@ -127,7 +129,8 @@
 
 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
 
-obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
 
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 
@@ -166,8 +169,11 @@
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_OMAP1)		+= omap1_camera.o
+obj-$(CONFIG_VIDEO_ATMEL_ISI)		+= atmel-isi.o
 
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
 
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
new file mode 100644
index 0000000..be7befd
--- /dev/null
+++ b/drivers/media/video/adp1653.c
@@ -0,0 +1,491 @@
+/*
+ * drivers/media/video/adp1653.c
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ *	Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *	Tuukka Toivonen <tuukkat76@gmail.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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - fault interrupt handling
+ * - hardware strobe
+ * - power doesn't need to be ON if all lights are off
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/adp1653.h>
+#include <media/v4l2-device.h>
+
+#define TIMEOUT_MAX		820000
+#define TIMEOUT_STEP		54600
+#define TIMEOUT_MIN		(TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
+				 * TIMEOUT_STEP)
+#define TIMEOUT_US_TO_CODE(t)	((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
+				 / TIMEOUT_STEP)
+#define TIMEOUT_CODE_TO_US(c)	(TIMEOUT_MAX - (c) * TIMEOUT_STEP)
+
+/* Write values into ADP1653 registers. */
+static int adp1653_update_hw(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	u8 out_sel;
+	u8 config = 0;
+	int rval;
+
+	out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+		flash->indicator_intensity->val)
+		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
+
+	switch (flash->led_mode->val) {
+	case V4L2_FLASH_LED_MODE_NONE:
+		break;
+	case V4L2_FLASH_LED_MODE_FLASH:
+		/* Flash mode, light on with strobe, duration from timer */
+		config = ADP1653_REG_CONFIG_TMR_CFG;
+		config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
+			  << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
+		break;
+	case V4L2_FLASH_LED_MODE_TORCH:
+		/* Torch mode, light immediately on, duration indefinite */
+		out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+			flash->torch_intensity->val)
+			<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+		break;
+	}
+
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+	if (rval < 0)
+		return rval;
+
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
+	if (rval < 0)
+		return rval;
+
+	return 0;
+}
+
+static int adp1653_get_fault(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	int fault;
+	int rval;
+
+	fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+	if (IS_ERR_VALUE(fault))
+		return fault;
+
+	flash->fault |= fault;
+
+	if (!flash->fault)
+		return 0;
+
+	/* Clear faults. */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+
+	flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
+
+	rval = adp1653_update_hw(flash);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+
+	return flash->fault;
+}
+
+static int adp1653_strobe(struct adp1653_flash *flash, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+		flash->indicator_intensity->val)
+		<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
+	int rval;
+
+	if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
+		return -EBUSY;
+
+	if (!enable)
+		return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
+						 out_sel);
+
+	out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+		flash->flash_intensity->val)
+		<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+	if (rval)
+		return rval;
+
+	/* Software strobe using i2c */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
+		ADP1653_REG_SW_STROBE_SW_STROBE);
+	if (rval)
+		return rval;
+	return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct adp1653_flash *flash =
+		container_of(ctrl->handler, struct adp1653_flash, ctrls);
+	int rval;
+
+	rval = adp1653_get_fault(flash);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+
+	ctrl->cur.val = 0;
+
+	if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+	if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
+		ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+	flash->fault = 0;
+
+	return 0;
+}
+
+static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct adp1653_flash *flash =
+		container_of(ctrl->handler, struct adp1653_flash, ctrls);
+	int rval;
+
+	rval = adp1653_get_fault(flash);
+	if (IS_ERR_VALUE(rval))
+		return rval;
+	if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
+		     ADP1653_REG_FAULT_FLT_OT |
+		     ADP1653_REG_FAULT_FLT_OV)) &&
+	    (ctrl->id == V4L2_CID_FLASH_STROBE ||
+	     ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
+	     ctrl->id == V4L2_CID_FLASH_LED_MODE))
+		return -EBUSY;
+
+	switch (ctrl->id) {
+	case V4L2_CID_FLASH_STROBE:
+		return adp1653_strobe(flash, 1);
+	case V4L2_CID_FLASH_STROBE_STOP:
+		return adp1653_strobe(flash, 0);
+	}
+
+	return adp1653_update_hw(flash);
+}
+
+static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
+	.g_volatile_ctrl = adp1653_get_ctrl,
+	.s_ctrl = adp1653_set_ctrl,
+};
+
+static int adp1653_init_controls(struct adp1653_flash *flash)
+{
+	struct v4l2_ctrl *fault;
+
+	v4l2_ctrl_handler_init(&flash->ctrls, 9);
+
+	flash->led_mode =
+		v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+				       V4L2_CID_FLASH_LED_MODE,
+				       V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
+	v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+			       V4L2_CID_FLASH_STROBE_SOURCE,
+			       V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
+	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+			  V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+	v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+			  V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+	flash->flash_timeout =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
+				  flash->platform_data->max_flash_timeout,
+				  TIMEOUT_STEP,
+				  flash->platform_data->max_flash_timeout);
+	flash->flash_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_INTENSITY,
+				  ADP1653_FLASH_INTENSITY_MIN,
+				  flash->platform_data->max_flash_intensity,
+				  1, flash->platform_data->max_flash_intensity);
+	flash->torch_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_TORCH_INTENSITY,
+				  ADP1653_TORCH_INTENSITY_MIN,
+				  flash->platform_data->max_torch_intensity,
+				  ADP1653_FLASH_INTENSITY_STEP,
+				  flash->platform_data->max_torch_intensity);
+	flash->indicator_intensity =
+		v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_INDICATOR_INTENSITY,
+				  ADP1653_INDICATOR_INTENSITY_MIN,
+				  flash->platform_data->max_indicator_intensity,
+				  ADP1653_INDICATOR_INTENSITY_STEP,
+				  ADP1653_INDICATOR_INTENSITY_MIN);
+	fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+				  V4L2_CID_FLASH_FAULT, 0,
+				  V4L2_FLASH_FAULT_OVER_VOLTAGE
+				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+
+	if (flash->ctrls.error)
+		return flash->ctrls.error;
+
+	fault->is_volatile = 1;
+
+	flash->subdev.ctrl_handler = &flash->ctrls;
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int
+adp1653_init_device(struct adp1653_flash *flash)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+	int rval;
+
+	/* Clear FAULT register by writing zero to OUT_SEL */
+	rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+	if (rval < 0) {
+		dev_err(&client->dev, "failed writing fault register\n");
+		return -EIO;
+	}
+
+	mutex_lock(&flash->ctrls.lock);
+	/* Reset faults before reading new ones. */
+	flash->fault = 0;
+	rval = adp1653_get_fault(flash);
+	mutex_unlock(&flash->ctrls.lock);
+	if (rval > 0) {
+		dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
+		return -EIO;
+	}
+
+	mutex_lock(&flash->ctrls.lock);
+	rval = adp1653_update_hw(flash);
+	mutex_unlock(&flash->ctrls.lock);
+	if (rval) {
+		dev_err(&client->dev,
+			"adp1653_update_hw failed at %s\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int
+__adp1653_set_power(struct adp1653_flash *flash, int on)
+{
+	int ret;
+
+	ret = flash->platform_data->power(&flash->subdev, on);
+	if (ret < 0)
+		return ret;
+
+	if (!on)
+		return 0;
+
+	ret = adp1653_init_device(flash);
+	if (ret < 0)
+		flash->platform_data->power(&flash->subdev, 0);
+
+	return ret;
+}
+
+static int
+adp1653_set_power(struct v4l2_subdev *subdev, int on)
+{
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+	int ret = 0;
+
+	mutex_lock(&flash->power_lock);
+
+	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
+	 * update the power state.
+	 */
+	if (flash->power_count == !on) {
+		ret = __adp1653_set_power(flash, !!on);
+		if (ret < 0)
+			goto done;
+	}
+
+	/* Update the power count. */
+	flash->power_count += on ? 1 : -1;
+	WARN_ON(flash->power_count < 0);
+
+done:
+	mutex_unlock(&flash->power_lock);
+	return ret;
+}
+
+static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return adp1653_set_power(sd, 1);
+}
+
+static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return adp1653_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops adp1653_core_ops = {
+	.s_power = adp1653_set_power,
+};
+
+static const struct v4l2_subdev_ops adp1653_ops = {
+	.core = &adp1653_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
+	.open = adp1653_open,
+	.close = adp1653_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int adp1653_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	if (!flash->power_count)
+		return 0;
+
+	return __adp1653_set_power(flash, 0);
+}
+
+static int adp1653_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	if (!flash->power_count)
+		return 0;
+
+	return __adp1653_set_power(flash, 1);
+}
+
+#else
+
+#define adp1653_suspend	NULL
+#define adp1653_resume	NULL
+
+#endif /* CONFIG_PM */
+
+static int adp1653_probe(struct i2c_client *client,
+			 const struct i2c_device_id *devid)
+{
+	struct adp1653_flash *flash;
+	int ret;
+
+	flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+	if (flash == NULL)
+		return -ENOMEM;
+
+	flash->platform_data = client->dev.platform_data;
+
+	mutex_init(&flash->power_lock);
+
+	v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
+	flash->subdev.internal_ops = &adp1653_internal_ops;
+	flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	adp1653_init_controls(flash);
+
+	ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+	if (ret < 0)
+		kfree(flash);
+
+	return ret;
+}
+
+static int __exit adp1653_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+	struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+	v4l2_device_unregister_subdev(&flash->subdev);
+	v4l2_ctrl_handler_free(&flash->ctrls);
+	media_entity_cleanup(&flash->subdev.entity);
+	kfree(flash);
+	return 0;
+}
+
+static const struct i2c_device_id adp1653_id_table[] = {
+	{ ADP1653_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
+
+static struct dev_pm_ops adp1653_pm_ops = {
+	.suspend	= adp1653_suspend,
+	.resume		= adp1653_resume,
+};
+
+static struct i2c_driver adp1653_i2c_driver = {
+	.driver		= {
+		.name	= ADP1653_NAME,
+		.pm	= &adp1653_pm_ops,
+	},
+	.probe		= adp1653_probe,
+	.remove		= __exit_p(adp1653_remove),
+	.id_table	= adp1653_id_table,
+};
+
+static int __init adp1653_init(void)
+{
+	int rval;
+
+	rval = i2c_add_driver(&adp1653_i2c_driver);
+	if (rval)
+		printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
+
+	return rval;
+}
+
+static void __exit adp1653_exit(void)
+{
+	i2c_del_driver(&adp1653_i2c_driver);
+}
+
+module_init(adp1653_init);
+module_exit(adp1653_exit);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index f989f28..b6ed44a 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -27,7 +27,6 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
@@ -54,7 +53,7 @@
  */
 #define USE_INT		0	/* Don't modify */
 
-#define VERSION	"0.04"
+#define VERSION	"0.0.5"
 
 #define ar_inl(addr) 		inl((unsigned long)(addr))
 #define ar_outl(val, addr)	outl((unsigned long)(val), (unsigned long)(addr))
@@ -404,7 +403,6 @@
 	strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
 	strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
 	strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
-	vcap->version = KERNEL_VERSION(0, 0, 4);
 	vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
 	return 0;
 }
@@ -879,3 +877,4 @@
 MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
 MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(VERSION);
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
new file mode 100644
index 0000000..7b89f00
--- /dev/null
+++ b/drivers/media/video/atmel-isi.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (c) 2011 Atmel Corporation
+ * Josh Wu, <josh.wu@atmel.com>
+ *
+ * Based on previous work by Lars Haring, <lars.haring@atmel.com>
+ * and Sedji Gaouaou
+ * Based on the bttv driver for Bt848 with respective copyright holders
+ *
+ * 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/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/atmel-isi.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define MAX_BUFFER_NUM			32
+#define MAX_SUPPORT_WIDTH		2048
+#define MAX_SUPPORT_HEIGHT		2048
+#define VID_LIMIT_BYTES			(16 * 1024 * 1024)
+#define MIN_FRAME_RATE			15
+#define FRAME_INTERVAL_MILLI_SEC	(1000 / MIN_FRAME_RATE)
+
+/* ISI states */
+enum {
+	ISI_STATE_IDLE = 0,
+	ISI_STATE_READY,
+	ISI_STATE_WAIT_SOF,
+};
+
+/* Frame buffer descriptor */
+struct fbd {
+	/* Physical address of the frame buffer */
+	u32 fb_address;
+	/* DMA Control Register(only in HISI2) */
+	u32 dma_ctrl;
+	/* Physical address of the next fbd */
+	u32 next_fbd_address;
+};
+
+static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl)
+{
+	fb_desc->dma_ctrl = ctrl;
+}
+
+struct isi_dma_desc {
+	struct list_head list;
+	struct fbd *p_fbd;
+	u32 fbd_phys;
+};
+
+/* Frame buffer data */
+struct frame_buffer {
+	struct vb2_buffer vb;
+	struct isi_dma_desc *p_dma_desc;
+	struct list_head list;
+};
+
+struct atmel_isi {
+	/* Protects the access of variables shared with the ISR */
+	spinlock_t			lock;
+	void __iomem			*regs;
+
+	int				sequence;
+	/* State of the ISI module in capturing mode */
+	int				state;
+
+	/* Wait queue for waiting for SOF */
+	wait_queue_head_t		vsync_wq;
+
+	struct vb2_alloc_ctx		*alloc_ctx;
+
+	/* Allocate descriptors for dma buffer use */
+	struct fbd			*p_fb_descriptors;
+	u32				fb_descriptors_phys;
+	struct				list_head dma_desc_head;
+	struct isi_dma_desc		dma_desc[MAX_BUFFER_NUM];
+
+	struct completion		complete;
+	struct clk			*pclk;
+	unsigned int			irq;
+
+	struct isi_platform_data	*pdata;
+
+	struct list_head		video_buffer_list;
+	struct frame_buffer		*active;
+
+	struct soc_camera_device	*icd;
+	struct soc_camera_host		soc_host;
+};
+
+static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val)
+{
+	writel(val, isi->regs + reg);
+}
+static u32 isi_readl(struct atmel_isi *isi, u32 reg)
+{
+	return readl(isi->regs + reg);
+}
+
+static int configure_geometry(struct atmel_isi *isi, u32 width,
+			u32 height, enum v4l2_mbus_pixelcode code)
+{
+	u32 cfg2, cr;
+
+	switch (code) {
+	/* YUV, including grey */
+	case V4L2_MBUS_FMT_Y8_1X8:
+		cr = ISI_CFG2_GRAYSCALE;
+		break;
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		cr = ISI_CFG2_YCC_SWAP_MODE_3;
+		break;
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+		cr = ISI_CFG2_YCC_SWAP_MODE_2;
+		break;
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+		cr = ISI_CFG2_YCC_SWAP_MODE_1;
+		break;
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+		cr = ISI_CFG2_YCC_SWAP_DEFAULT;
+		break;
+	/* RGB, TODO */
+	default:
+		return -EINVAL;
+	}
+
+	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+	cfg2 = isi_readl(isi, ISI_CFG2);
+	cfg2 |= cr;
+	/* Set width */
+	cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK);
+	cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) &
+			ISI_CFG2_IM_HSIZE_MASK;
+	/* Set height */
+	cfg2 &= ~(ISI_CFG2_IM_VSIZE_MASK);
+	cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET)
+			& ISI_CFG2_IM_VSIZE_MASK;
+	isi_writel(isi, ISI_CFG2, cfg2);
+
+	return 0;
+}
+
+static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
+{
+	if (isi->active) {
+		struct vb2_buffer *vb = &isi->active->vb;
+		struct frame_buffer *buf = isi->active;
+
+		list_del_init(&buf->list);
+		do_gettimeofday(&vb->v4l2_buf.timestamp);
+		vb->v4l2_buf.sequence = isi->sequence++;
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	}
+
+	if (list_empty(&isi->video_buffer_list)) {
+		isi->active = NULL;
+	} else {
+		/* start next dma frame. */
+		isi->active = list_entry(isi->video_buffer_list.next,
+					struct frame_buffer, list);
+		isi_writel(isi, ISI_DMA_C_DSCR,
+			isi->active->p_dma_desc->fbd_phys);
+		isi_writel(isi, ISI_DMA_C_CTRL,
+			ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+		isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+	}
+	return IRQ_HANDLED;
+}
+
+/* ISI interrupt service routine */
+static irqreturn_t isi_interrupt(int irq, void *dev_id)
+{
+	struct atmel_isi *isi = dev_id;
+	u32 status, mask, pending;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock(&isi->lock);
+
+	status = isi_readl(isi, ISI_STATUS);
+	mask = isi_readl(isi, ISI_INTMASK);
+	pending = status & mask;
+
+	if (pending & ISI_CTRL_SRST) {
+		complete(&isi->complete);
+		isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST);
+		ret = IRQ_HANDLED;
+	} else if (pending & ISI_CTRL_DIS) {
+		complete(&isi->complete);
+		isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);
+		ret = IRQ_HANDLED;
+	} else {
+		if ((pending & ISI_SR_VSYNC) &&
+				(isi->state == ISI_STATE_IDLE)) {
+			isi->state = ISI_STATE_READY;
+			wake_up_interruptible(&isi->vsync_wq);
+			ret = IRQ_HANDLED;
+		}
+		if (likely(pending & ISI_SR_CXFR_DONE))
+			ret = atmel_isi_handle_streaming(isi);
+	}
+
+	spin_unlock(&isi->lock);
+	return ret;
+}
+
+#define	WAIT_ISI_RESET		1
+#define	WAIT_ISI_DISABLE	0
+static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
+{
+	unsigned long timeout;
+	/*
+	 * The reset or disable will only succeed if we have a
+	 * pixel clock from the camera.
+	 */
+	init_completion(&isi->complete);
+
+	if (wait_reset) {
+		isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST);
+		isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST);
+	} else {
+		isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS);
+		isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+	}
+
+	timeout = wait_for_completion_timeout(&isi->complete,
+			msecs_to_jiffies(100));
+	if (timeout == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------
+	Videobuf operations
+   ------------------------------------------------------------------*/
+static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned long sizes[],
+				void *alloc_ctxs[])
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	unsigned long size;
+	int ret, bytes_per_line;
+
+	/* Reset ISI */
+	ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
+	if (ret < 0) {
+		dev_err(icd->parent, "Reset ISI timed out\n");
+		return ret;
+	}
+	/* Disable all interrupts */
+	isi_writel(isi, ISI_INTDIS, ~0UL);
+
+	bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+						icd->current_fmt->host_fmt);
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	size = bytes_per_line * icd->user_height;
+
+	if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM)
+		*nbuffers = MAX_BUFFER_NUM;
+
+	if (size * *nbuffers > VID_LIMIT_BYTES)
+		*nbuffers = VID_LIMIT_BYTES / size;
+
+	*nplanes = 1;
+	sizes[0] = size;
+	alloc_ctxs[0] = isi->alloc_ctx;
+
+	isi->sequence = 0;
+	isi->active = NULL;
+
+	dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__,
+		*nbuffers, size);
+
+	return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+	struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+	buf->p_dma_desc = NULL;
+	INIT_LIST_HEAD(&buf->list);
+
+	return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+	struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	unsigned long size;
+	struct isi_dma_desc *desc;
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+						icd->current_fmt->host_fmt);
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	size = bytes_per_line * icd->user_height;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n",
+				__func__, vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(&buf->vb, 0, size);
+
+	if (!buf->p_dma_desc) {
+		if (list_empty(&isi->dma_desc_head)) {
+			dev_err(icd->parent, "Not enough dma descriptors.\n");
+			return -EINVAL;
+		} else {
+			/* Get an available descriptor */
+			desc = list_entry(isi->dma_desc_head.next,
+						struct isi_dma_desc, list);
+			/* Delete the descriptor since now it is used */
+			list_del_init(&desc->list);
+
+			/* Initialize the dma descriptor */
+			desc->p_fbd->fb_address =
+					vb2_dma_contig_plane_paddr(vb, 0);
+			desc->p_fbd->next_fbd_address = 0;
+			set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB);
+
+			buf->p_dma_desc = desc;
+		}
+	}
+	return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+	/* This descriptor is available now and we add to head list */
+	if (buf->p_dma_desc)
+		list_add(&buf->p_dma_desc->list, &isi->dma_desc_head);
+}
+
+static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
+{
+	u32 ctrl, cfg1;
+
+	cfg1 = isi_readl(isi, ISI_CFG1);
+	/* Enable irq: cxfr for the codec path, pxfr for the preview path */
+	isi_writel(isi, ISI_INTEN,
+			ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+	/* Check if already in a frame */
+	if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
+		dev_err(isi->icd->parent, "Already in frame handling.\n");
+		return;
+	}
+
+	isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys);
+	isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+	isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+
+	/* Enable linked list */
+	cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR;
+
+	/* Enable codec path and ISI */
+	ctrl = ISI_CTRL_CDC | ISI_CTRL_EN;
+	isi_writel(isi, ISI_CTRL, ctrl);
+	isi_writel(isi, ISI_CFG1, cfg1);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&isi->lock, flags);
+	list_add_tail(&buf->list, &isi->video_buffer_list);
+
+	if (isi->active == NULL) {
+		isi->active = buf;
+		start_dma(isi, buf);
+	}
+	spin_unlock_irqrestore(&isi->lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq)
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+
+	u32 sr = 0;
+	int ret;
+
+	spin_lock_irq(&isi->lock);
+	isi->state = ISI_STATE_IDLE;
+	/* Clear any pending SOF interrupt */
+	sr = isi_readl(isi, ISI_STATUS);
+	/* Enable VSYNC interrupt for SOF */
+	isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC);
+	isi_writel(isi, ISI_CTRL, ISI_CTRL_EN);
+	spin_unlock_irq(&isi->lock);
+
+	dev_dbg(icd->parent, "Waiting for SOF\n");
+	ret = wait_event_interruptible(isi->vsync_wq,
+				       isi->state != ISI_STATE_IDLE);
+	if (ret)
+		return ret;
+
+	if (isi->state != ISI_STATE_READY)
+		return -EIO;
+
+	spin_lock_irq(&isi->lock);
+	isi->state = ISI_STATE_WAIT_SOF;
+	isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC);
+	spin_unlock_irq(&isi->lock);
+
+	return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	struct frame_buffer *buf, *node;
+	int ret = 0;
+	unsigned long timeout;
+
+	spin_lock_irq(&isi->lock);
+	isi->active = NULL;
+	/* Release all active buffers */
+	list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) {
+		list_del_init(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irq(&isi->lock);
+
+	timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ;
+	/* Wait until the end of the current frame. */
+	while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) &&
+			time_before(jiffies, timeout))
+		msleep(1);
+
+	if (time_after(jiffies, timeout)) {
+		dev_err(icd->parent,
+			"Timeout waiting for finishing codec request\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Disable interrupts */
+	isi_writel(isi, ISI_INTDIS,
+			ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+	/* Disable ISI and wait for it is done */
+	ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
+	if (ret < 0)
+		dev_err(icd->parent, "Disable ISI timed out\n");
+
+	return ret;
+}
+
+static struct vb2_ops isi_video_qops = {
+	.queue_setup		= queue_setup,
+	.buf_init		= buffer_init,
+	.buf_prepare		= buffer_prepare,
+	.buf_cleanup		= buffer_cleanup,
+	.buf_queue		= buffer_queue,
+	.start_streaming	= start_streaming,
+	.stop_streaming		= stop_streaming,
+	.wait_prepare		= soc_camera_unlock,
+	.wait_finish		= soc_camera_lock,
+};
+
+/* ------------------------------------------------------------------
+	SOC camera operations for the device
+   ------------------------------------------------------------------*/
+static int isi_camera_init_videobuf(struct vb2_queue *q,
+				     struct soc_camera_device *icd)
+{
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP;
+	q->drv_priv = icd;
+	q->buf_struct_size = sizeof(struct frame_buffer);
+	q->ops = &isi_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+
+	return vb2_queue_init(q);
+}
+
+static int isi_camera_set_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+	if (!xlate) {
+		dev_warn(icd->parent, "Format %x not found\n",
+			 pix->pixelformat);
+		return -EINVAL;
+	}
+
+	dev_dbg(icd->parent, "Plan to set format %dx%d\n",
+			pix->width, pix->height);
+
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+	if (ret < 0)
+		return ret;
+
+	if (mf.code != xlate->code)
+		return -EINVAL;
+
+	ret = configure_geometry(isi, pix->width, pix->height, xlate->code);
+	if (ret < 0)
+		return ret;
+
+	pix->width		= mf.width;
+	pix->height		= mf.height;
+	pix->field		= mf.field;
+	pix->colorspace		= mf.colorspace;
+	icd->current_fmt	= xlate;
+
+	dev_dbg(icd->parent, "Finally set format %dx%d\n",
+		pix->width, pix->height);
+
+	return ret;
+}
+
+static int isi_camera_try_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	u32 pixfmt = pix->pixelformat;
+	int ret;
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+	if (pixfmt && !xlate) {
+		dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+		return -EINVAL;
+	}
+
+	/* limit to Atmel ISI hardware capabilities */
+	if (pix->height > MAX_SUPPORT_HEIGHT)
+		pix->height = MAX_SUPPORT_HEIGHT;
+	if (pix->width > MAX_SUPPORT_WIDTH)
+		pix->width = MAX_SUPPORT_WIDTH;
+
+	/* limit to sensor capabilities */
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+	if (ret < 0)
+		return ret;
+
+	pix->width	= mf.width;
+	pix->height	= mf.height;
+	pix->colorspace	= mf.colorspace;
+
+	switch (mf.field) {
+	case V4L2_FIELD_ANY:
+		pix->field = V4L2_FIELD_NONE;
+		break;
+	case V4L2_FIELD_NONE:
+		break;
+	default:
+		dev_err(icd->parent, "Field type %d unsupported.\n",
+			mf.field);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct soc_mbus_pixelfmt isi_camera_formats[] = {
+	{
+		.fourcc			= V4L2_PIX_FMT_YUYV,
+		.name			= "Packed YUV422 16 bit",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_LE,
+	},
+};
+
+/* This will be corrected as we get more formats */
+static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+	return	fmt->packing == SOC_MBUS_PACKING_NONE ||
+		(fmt->bits_per_sample == 8 &&
+		 fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+		(fmt->bits_per_sample > 8 &&
+		 fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static unsigned long make_bus_param(struct atmel_isi *isi)
+{
+	unsigned long flags;
+	/*
+	 * Platform specified synchronization and pixel clock polarities are
+	 * only a recommendation and are only used during probing. Atmel ISI
+	 * camera interface only works in master mode, i.e., uses HSYNC and
+	 * VSYNC signals from the sensor
+	 */
+	flags = SOCAM_MASTER |
+		SOCAM_HSYNC_ACTIVE_HIGH |
+		SOCAM_HSYNC_ACTIVE_LOW |
+		SOCAM_VSYNC_ACTIVE_HIGH |
+		SOCAM_VSYNC_ACTIVE_LOW |
+		SOCAM_PCLK_SAMPLE_RISING |
+		SOCAM_PCLK_SAMPLE_FALLING |
+		SOCAM_DATA_ACTIVE_HIGH;
+
+	if (isi->pdata->data_width_flags & ISI_DATAWIDTH_10)
+		flags |= SOCAM_DATAWIDTH_10;
+
+	if (isi->pdata->data_width_flags & ISI_DATAWIDTH_8)
+		flags |= SOCAM_DATAWIDTH_8;
+
+	if (flags & SOCAM_DATAWIDTH_MASK)
+		return flags;
+
+	return 0;
+}
+
+static int isi_camera_try_bus_param(struct soc_camera_device *icd,
+				    unsigned char buswidth)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	unsigned long camera_flags;
+	int ret;
+
+	camera_flags = icd->ops->query_bus_param(icd);
+	ret = soc_camera_bus_param_compatible(camera_flags,
+					make_bus_param(isi));
+	if (!ret)
+		return -EINVAL;
+	return 0;
+}
+
+
+static int isi_camera_get_formats(struct soc_camera_device *icd,
+				  unsigned int idx,
+				  struct soc_camera_format_xlate *xlate)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	int formats = 0, ret;
+	/* sensor format */
+	enum v4l2_mbus_pixelcode code;
+	/* soc camera host format */
+	const struct soc_mbus_pixelfmt *fmt;
+
+	ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+	if (ret < 0)
+		/* No more formats */
+		return 0;
+
+	fmt = soc_mbus_get_fmtdesc(code);
+	if (!fmt) {
+		dev_err(icd->parent,
+			"Invalid format code #%u: %d\n", idx, code);
+		return 0;
+	}
+
+	/* This also checks support for the requested bits-per-sample */
+	ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample);
+	if (ret < 0) {
+		dev_err(icd->parent,
+			"Fail to try the bus parameters.\n");
+		return 0;
+	}
+
+	switch (code) {
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+		formats++;
+		if (xlate) {
+			xlate->host_fmt	= &isi_camera_formats[0];
+			xlate->code	= code;
+			xlate++;
+			dev_dbg(icd->parent, "Providing format %s using code %d\n",
+				isi_camera_formats[0].name, code);
+		}
+		break;
+	default:
+		if (!isi_camera_packing_supported(fmt))
+			return 0;
+		if (xlate)
+			dev_dbg(icd->parent,
+				"Providing format %s in pass-through mode\n",
+				fmt->name);
+	}
+
+	/* Generic pass-through */
+	formats++;
+	if (xlate) {
+		xlate->host_fmt	= fmt;
+		xlate->code	= code;
+		xlate++;
+	}
+
+	return formats;
+}
+
+/* Called with .video_lock held */
+static int isi_camera_add_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	int ret;
+
+	if (isi->icd)
+		return -EBUSY;
+
+	ret = clk_enable(isi->pclk);
+	if (ret)
+		return ret;
+
+	isi->icd = icd;
+	dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+		 icd->devnum);
+	return 0;
+}
+/* Called with .video_lock held */
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+
+	BUG_ON(icd != isi->icd);
+
+	clk_disable(isi->pclk);
+	isi->icd = NULL;
+
+	dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+		 icd->devnum);
+}
+
+static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
+{
+	struct soc_camera_device *icd = file->private_data;
+
+	return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int isi_camera_querycap(struct soc_camera_host *ici,
+			       struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "atmel-isi");
+	strcpy(cap->card, "Atmel Image Sensor Interface");
+	cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+				V4L2_CAP_STREAMING);
+	return 0;
+}
+
+static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct atmel_isi *isi = ici->priv;
+	unsigned long bus_flags, camera_flags, common_flags;
+	int ret;
+	u32 cfg1 = 0;
+
+	camera_flags = icd->ops->query_bus_param(icd);
+
+	bus_flags = make_bus_param(isi);
+	common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
+	dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
+		camera_flags, bus_flags, common_flags);
+	if (!common_flags)
+		return -EINVAL;
+
+	/* Make choises, based on platform preferences */
+	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+		if (isi->pdata->hsync_act_low)
+			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+		else
+			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+	}
+
+	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
+	    (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+		if (isi->pdata->vsync_act_low)
+			common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+		else
+			common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+	}
+
+	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
+	    (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+		if (isi->pdata->pclk_act_falling)
+			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+		else
+			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+	}
+
+	ret = icd->ops->set_bus_param(icd, common_flags);
+	if (ret < 0) {
+		dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n",
+			common_flags, ret);
+		return ret;
+	}
+
+	/* set bus param for ISI */
+	if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+		cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
+	if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+		cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
+	if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+		cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
+
+	if (isi->pdata->has_emb_sync)
+		cfg1 |= ISI_CFG1_EMB_SYNC;
+	if (isi->pdata->isi_full_mode)
+		cfg1 |= ISI_CFG1_FULL_MODE;
+
+	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+	isi_writel(isi, ISI_CFG1, cfg1);
+
+	return 0;
+}
+
+static struct soc_camera_host_ops isi_soc_camera_host_ops = {
+	.owner		= THIS_MODULE,
+	.add		= isi_camera_add_device,
+	.remove		= isi_camera_remove_device,
+	.set_fmt	= isi_camera_set_fmt,
+	.try_fmt	= isi_camera_try_fmt,
+	.get_formats	= isi_camera_get_formats,
+	.init_videobuf2	= isi_camera_init_videobuf,
+	.poll		= isi_camera_poll,
+	.querycap	= isi_camera_querycap,
+	.set_bus_param	= isi_camera_set_bus_param,
+};
+
+/* -----------------------------------------------------------------------*/
+static int __devexit atmel_isi_remove(struct platform_device *pdev)
+{
+	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+	struct atmel_isi *isi = container_of(soc_host,
+					struct atmel_isi, soc_host);
+
+	free_irq(isi->irq, isi);
+	soc_camera_host_unregister(soc_host);
+	vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+	dma_free_coherent(&pdev->dev,
+			sizeof(struct fbd) * MAX_BUFFER_NUM,
+			isi->p_fb_descriptors,
+			isi->fb_descriptors_phys);
+
+	iounmap(isi->regs);
+	clk_put(isi->pclk);
+	kfree(isi);
+
+	return 0;
+}
+
+static int __devinit atmel_isi_probe(struct platform_device *pdev)
+{
+	unsigned int irq;
+	struct atmel_isi *isi;
+	struct clk *pclk;
+	struct resource *regs;
+	int ret, i;
+	struct device *dev = &pdev->dev;
+	struct soc_camera_host *soc_host;
+	struct isi_platform_data *pdata;
+
+	pdata = dev->platform_data;
+	if (!pdata || !pdata->data_width_flags) {
+		dev_err(&pdev->dev,
+			"No config available for Atmel ISI\n");
+		return -EINVAL;
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	pclk = clk_get(&pdev->dev, "isi_clk");
+	if (IS_ERR(pclk))
+		return PTR_ERR(pclk);
+
+	isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL);
+	if (!isi) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "Can't allocate interface!\n");
+		goto err_alloc_isi;
+	}
+
+	isi->pclk = pclk;
+	isi->pdata = pdata;
+	isi->active = NULL;
+	spin_lock_init(&isi->lock);
+	init_waitqueue_head(&isi->vsync_wq);
+	INIT_LIST_HEAD(&isi->video_buffer_list);
+	INIT_LIST_HEAD(&isi->dma_desc_head);
+
+	isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,
+				sizeof(struct fbd) * MAX_BUFFER_NUM,
+				&isi->fb_descriptors_phys,
+				GFP_KERNEL);
+	if (!isi->p_fb_descriptors) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "Can't allocate descriptors!\n");
+		goto err_alloc_descriptors;
+	}
+
+	for (i = 0; i < MAX_BUFFER_NUM; i++) {
+		isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i;
+		isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys +
+					i * sizeof(struct fbd);
+		list_add(&isi->dma_desc[i].list, &isi->dma_desc_head);
+	}
+
+	isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(isi->alloc_ctx)) {
+		ret = PTR_ERR(isi->alloc_ctx);
+		goto err_alloc_ctx;
+	}
+
+	isi->regs = ioremap(regs->start, resource_size(regs));
+	if (!isi->regs) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_req_irq;
+	}
+
+	ret = request_irq(irq, isi_interrupt, 0, "isi", isi);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+		goto err_req_irq;
+	}
+	isi->irq = irq;
+
+	soc_host		= &isi->soc_host;
+	soc_host->drv_name	= "isi-camera";
+	soc_host->ops		= &isi_soc_camera_host_ops;
+	soc_host->priv		= isi;
+	soc_host->v4l2_dev.dev	= &pdev->dev;
+	soc_host->nr		= pdev->id;
+
+	ret = soc_camera_host_register(soc_host);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register soc camera host\n");
+		goto err_register_soc_camera_host;
+	}
+	return 0;
+
+err_register_soc_camera_host:
+	free_irq(isi->irq, isi);
+err_req_irq:
+	iounmap(isi->regs);
+err_ioremap:
+	vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+err_alloc_ctx:
+	dma_free_coherent(&pdev->dev,
+			sizeof(struct fbd) * MAX_BUFFER_NUM,
+			isi->p_fb_descriptors,
+			isi->fb_descriptors_phys);
+err_alloc_descriptors:
+	kfree(isi);
+err_alloc_isi:
+	clk_put(isi->pclk);
+
+	return ret;
+}
+
+static struct platform_driver atmel_isi_driver = {
+	.probe		= atmel_isi_probe,
+	.remove		= __devexit_p(atmel_isi_remove),
+	.driver		= {
+		.name = "atmel_isi",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init atmel_isi_init_module(void)
+{
+	return  platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe);
+}
+
+static void __exit atmel_isi_exit(void)
+{
+	platform_driver_unregister(&atmel_isi_driver);
+}
+module_init(atmel_isi_init_module);
+module_exit(atmel_isi_exit);
+
+MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>");
+MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
index ca342e4..1e4ce50 100644
--- a/drivers/media/video/au0828/au0828-core.c
+++ b/drivers/media/video/au0828/au0828-core.c
@@ -292,3 +292,4 @@
 MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index c03eb29..0b3e481 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -33,7 +33,6 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/suspend.h>
-#include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-chip-ident.h>
@@ -43,8 +42,6 @@
 
 static DEFINE_MUTEX(au0828_sysfs_lock);
 
-#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1)
-
 /* ------------------------------------------------------------------
 	Videobuf operations
    ------------------------------------------------------------------*/
@@ -1254,8 +1251,6 @@
 	strlcpy(cap->card, dev->board.name, sizeof(cap->card));
 	strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
 
-	cap->version = AU0828_VERSION_CODE;
-
 	/*set the device capabilities */
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_VBI_CAPTURE |
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 3c9e6c7..5b15f63 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -2892,13 +2892,10 @@
 {
 	unsigned int gpiobits;
 	int i,type;
-	unsigned short tmp;
 
 	/* read PCI subsystem ID */
-	pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_ID, &tmp);
-	btv->cardid = tmp << 16;
-	pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
-	btv->cardid |= tmp;
+	btv->cardid  = btv->c.pci->subsystem_device << 16;
+	btv->cardid |= btv->c.pci->subsystem_vendor;
 
 	if (0 != btv->cardid && 0xffffffff != btv->cardid) {
 		/* look for the card */
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 834a483..14444de 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -57,6 +57,7 @@
 
 #include <media/saa6588.h>
 
+#define BTTV_VERSION "0.9.19"
 
 unsigned int bttv_num;			/* number of Bt848s in use */
 struct bttv *bttvs[BTTV_MAX];
@@ -163,6 +164,7 @@
 MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
 MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
 
 /* ----------------------------------------------------------------------- */
 /* sysfs                                                                   */
@@ -2616,7 +2618,6 @@
 	strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
 	snprintf(cap->bus_info, sizeof(cap->bus_info),
 		 "PCI:%s", pci_name(btv->c.pci));
-	cap->version = BTTV_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_VBI_CAPTURE |
@@ -3416,7 +3417,6 @@
 	strcpy(cap->driver, "bttv");
 	strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
 	sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
-	cap->version = BTTV_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_TUNER;
 
 	return 0;
@@ -4585,14 +4585,8 @@
 
 	bttv_num = 0;
 
-	printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n",
-	       (BTTV_VERSION_CODE >> 16) & 0xff,
-	       (BTTV_VERSION_CODE >> 8) & 0xff,
-	       BTTV_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "bttv: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "bttv: driver version %s loaded\n",
+	       BTTV_VERSION);
 	if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
 		gbuffers = 2;
 	if (gbufsize > BTTV_MAX_FBUF)
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index 9b776fa..318edf2 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -25,9 +25,6 @@
 #ifndef _BTTVP_H_
 #define _BTTVP_H_
 
-#include <linux/version.h>
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,18)
-
 #include <linux/types.h>
 #include <linux/wait.h>
 #include <linux/i2c.h>
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index c119350..f09df9d 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -71,7 +71,6 @@
 #include <linux/mm.h>
 #include <linux/parport.h>
 #include <linux/sched.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/mutex.h>
 #include <asm/uaccess.h>
@@ -647,7 +646,6 @@
 	strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
 	strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card));
 	strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
-	vcap->version = KERNEL_VERSION(0, 0, 2);
 	vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
 	return 0;
 }
@@ -895,6 +893,7 @@
 
 	if (v4l2_device_register(NULL, v4l2_dev) < 0) {
 		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+		kfree(qcam);
 		return NULL;
 	}
 
@@ -1092,3 +1091,4 @@
 module_exit(exit_bw_qcams);
 
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 24fc009..cd8ff04 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -35,7 +35,6 @@
 #include <linux/sched.h>
 #include <linux/mutex.h>
 #include <linux/jiffies.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <asm/uaccess.h>
 #include <media/v4l2-device.h>
@@ -517,7 +516,6 @@
 	strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
 	strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
 	strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
-	vcap->version = KERNEL_VERSION(0, 0, 3);
 	vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
 	return 0;
 }
@@ -752,6 +750,7 @@
 
 	if (v4l2_device_register(NULL, v4l2_dev) < 0) {
 		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+		kfree(qcam);
 		return NULL;
 	}
 
@@ -886,6 +885,7 @@
 MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
 MODULE_DESCRIPTION(BANNER);
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
 
 module_init(cqcam_init);
 module_exit(cqcam_cleanup);
diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h
deleted file mode 100644
index 8e2a87c..0000000
--- a/drivers/media/video/cafe_ccic-regs.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Register definitions for the m88alp01 camera interface.  Offsets in bytes
- * as given in the spec.
- *
- * Copyright 2006 One Laptop Per Child Association, Inc.
- *
- * Written by Jonathan Corbet, corbet@lwn.net.
- *
- * This file may be distributed under the terms of the GNU General
- * Public License, version 2.
- */
-#define REG_Y0BAR	0x00
-#define REG_Y1BAR	0x04
-#define REG_Y2BAR	0x08
-/* ... */
-
-#define REG_IMGPITCH	0x24	/* Image pitch register */
-#define   IMGP_YP_SHFT	  2		/* Y pitch params */
-#define   IMGP_YP_MASK	  0x00003ffc	/* Y pitch field */
-#define	  IMGP_UVP_SHFT	  18		/* UV pitch (planar) */
-#define   IMGP_UVP_MASK   0x3ffc0000
-#define REG_IRQSTATRAW	0x28	/* RAW IRQ Status */
-#define   IRQ_EOF0	  0x00000001	/* End of frame 0 */
-#define   IRQ_EOF1	  0x00000002	/* End of frame 1 */
-#define   IRQ_EOF2	  0x00000004	/* End of frame 2 */
-#define   IRQ_SOF0	  0x00000008	/* Start of frame 0 */
-#define   IRQ_SOF1	  0x00000010	/* Start of frame 1 */
-#define   IRQ_SOF2	  0x00000020	/* Start of frame 2 */
-#define   IRQ_OVERFLOW	  0x00000040	/* FIFO overflow */
-#define   IRQ_TWSIW	  0x00010000	/* TWSI (smbus) write */
-#define   IRQ_TWSIR	  0x00020000	/* TWSI read */
-#define   IRQ_TWSIE	  0x00040000	/* TWSI error */
-#define   TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
-#define   FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
-#define   ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
-#define REG_IRQMASK	0x2c	/* IRQ mask - same bits as IRQSTAT */
-#define REG_IRQSTAT	0x30	/* IRQ status / clear */
-
-#define REG_IMGSIZE	0x34	/* Image size */
-#define  IMGSZ_V_MASK	  0x1fff0000
-#define  IMGSZ_V_SHIFT	  16
-#define	 IMGSZ_H_MASK	  0x00003fff
-#define REG_IMGOFFSET	0x38	/* IMage offset */
-
-#define REG_CTRL0	0x3c	/* Control 0 */
-#define   C0_ENABLE	  0x00000001	/* Makes the whole thing go */
-
-/* Mask for all the format bits */
-#define   C0_DF_MASK	  0x00fffffc    /* Bits 2-23 */
-
-/* RGB ordering */
-#define   C0_RGB4_RGBX	  0x00000000
-#define	  C0_RGB4_XRGB	  0x00000004
-#define	  C0_RGB4_BGRX	  0x00000008
-#define   C0_RGB4_XBGR	  0x0000000c
-#define   C0_RGB5_RGGB	  0x00000000
-#define	  C0_RGB5_GRBG	  0x00000004
-#define	  C0_RGB5_GBRG	  0x00000008
-#define   C0_RGB5_BGGR	  0x0000000c
-
-/* Spec has two fields for DIN and DOUT, but they must match, so
-   combine them here. */
-#define   C0_DF_YUV	  0x00000000    /* Data is YUV	    */
-#define   C0_DF_RGB	  0x000000a0	/* ... RGB		    */
-#define   C0_DF_BAYER     0x00000140	/* ... Bayer                */
-/* 8-8-8 must be missing from the below - ask */
-#define   C0_RGBF_565	  0x00000000
-#define   C0_RGBF_444	  0x00000800
-#define   C0_RGB_BGR	  0x00001000	/* Blue comes first */
-#define   C0_YUV_PLANAR	  0x00000000	/* YUV 422 planar format */
-#define   C0_YUV_PACKED	  0x00008000	/* YUV 422 packed	*/
-#define   C0_YUV_420PL	  0x0000a000	/* YUV 420 planar	*/
-/* Think that 420 packed must be 111 - ask */
-#define	  C0_YUVE_YUYV	  0x00000000	/* Y1CbY0Cr 		*/
-#define	  C0_YUVE_YVYU	  0x00010000	/* Y1CrY0Cb 		*/
-#define	  C0_YUVE_VYUY	  0x00020000	/* CrY1CbY0 		*/
-#define	  C0_YUVE_UYVY	  0x00030000	/* CbY1CrY0 		*/
-#define   C0_YUVE_XYUV	  0x00000000    /* 420: .YUV		*/
-#define	  C0_YUVE_XYVU	  0x00010000	/* 420: .YVU 		*/
-#define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY 		*/
-#define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY 		*/
-/* Bayer bits 18,19 if needed */
-#define   C0_HPOL_LOW	  0x01000000	/* HSYNC polarity active low */
-#define   C0_VPOL_LOW	  0x02000000	/* VSYNC polarity active low */
-#define   C0_VCLK_LOW	  0x04000000	/* VCLK on falling edge */
-#define   C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
-#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
-#define   C0_SIF_HVSYNC	  0x00000000	/* Use H/VSYNC */
-#define   CO_SOF_NOSYNC	  0x40000000	/* Use inband active signaling */
-
-
-#define REG_CTRL1	0x40	/* Control 1 */
-#define   C1_444ALPHA	  0x00f00000	/* Alpha field in RGB444 */
-#define   C1_ALPHA_SHFT	  20
-#define   C1_DMAB32	  0x00000000	/* 32-byte DMA burst */
-#define   C1_DMAB16	  0x02000000	/* 16-byte DMA burst */
-#define	  C1_DMAB64	  0x04000000	/* 64-byte DMA burst */
-#define	  C1_DMAB_MASK	  0x06000000
-#define   C1_TWOBUFS	  0x08000000	/* Use only two DMA buffers */
-#define   C1_PWRDWN	  0x10000000	/* Power down */
-
-#define REG_CLKCTRL	0x88	/* Clock control */
-#define   CLK_DIV_MASK	  0x0000ffff	/* Upper bits RW "reserved" */
-
-#define REG_GPR		0xb4	/* General purpose register.  This
-				   controls inputs to the power and reset
-				   pins on the OV7670 used with OLPC;
-				   other deployments could differ.  */
-#define   GPR_C1EN	  0x00000020	/* Pad 1 (power down) enable */
-#define   GPR_C0EN	  0x00000010	/* Pad 0 (reset) enable */
-#define	  GPR_C1	  0x00000002	/* Control 1 value */
-/*
- * Control 0 is wired to reset on OLPC machines.  For ov7x sensors,
- * it is active low, for 0v6x, instead, it's active high.  What
- * fun.
- */
-#define   GPR_C0	  0x00000001	/* Control 0 value */
-
-#define REG_TWSIC0	0xb8	/* TWSI (smbus) control 0 */
-#define   TWSIC0_EN       0x00000001	/* TWSI enable */
-#define   TWSIC0_MODE	  0x00000002	/* 1 = 16-bit, 0 = 8-bit */
-#define   TWSIC0_SID	  0x000003fc	/* Slave ID */
-#define   TWSIC0_SID_SHIFT 2
-#define   TWSIC0_CLKDIV   0x0007fc00	/* Clock divider */
-#define   TWSIC0_MASKACK  0x00400000	/* Mask ack from sensor */
-#define   TWSIC0_OVMAGIC  0x00800000	/* Make it work on OV sensors */
-
-#define REG_TWSIC1	0xbc	/* TWSI control 1 */
-#define   TWSIC1_DATA	  0x0000ffff	/* Data to/from camchip */
-#define   TWSIC1_ADDR	  0x00ff0000	/* Address (register) */
-#define   TWSIC1_ADDR_SHIFT 16
-#define   TWSIC1_READ	  0x01000000	/* Set for read op */
-#define   TWSIC1_WSTAT	  0x02000000	/* Write status */
-#define   TWSIC1_RVALID	  0x04000000	/* Read data valid */
-#define   TWSIC1_ERROR	  0x08000000	/* Something screwed up */
-
-
-#define REG_UBAR	0xc4	/* Upper base address register */
-
-/*
- * Here's the weird global control registers which are said to live
- * way up here.
- */
-#define REG_GL_CSR     0x3004  /* Control/status register */
-#define   GCSR_SRS	 0x00000001	/* SW Reset set */
-#define   GCSR_SRC  	 0x00000002	/* SW Reset clear */
-#define	  GCSR_MRS	 0x00000004	/* Master reset set */
-#define	  GCSR_MRC	 0x00000008	/* HW Reset clear */
-#define   GCSR_CCIC_EN   0x00004000    /* CCIC Clock enable */
-#define REG_GL_IMASK   0x300c  /* Interrupt mask register */
-#define   GIMSK_CCIC_EN          0x00000004    /* CCIC Interrupt enable */
-
-#define REG_GL_FCR	0x3038  /* GPIO functional control register */
-#define	  GFCR_GPIO_ON	  0x08		/* Camera GPIO enabled */
-#define REG_GL_GPIOR	0x315c	/* GPIO register */
-#define   GGPIO_OUT  		0x80000	/* GPIO output */
-#define   GGPIO_VAL  		0x00008	/* Output pin value */
-
-#define REG_LEN                REG_GL_IMASK + 4
-
-
-/*
- * Useful stuff that probably belongs somewhere global.
- */
-#define VGA_WIDTH	640
-#define VGA_HEIGHT	480
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
deleted file mode 100644
index 6647033..0000000
--- a/drivers/media/video/cafe_ccic.c
+++ /dev/null
@@ -1,2267 +0,0 @@
-/*
- * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
- * multifunction chip.  Currently works with the Omnivision OV7670
- * sensor.
- *
- * The data sheet for this device can be found at:
- *    http://www.marvell.com/products/pc_connectivity/88alp01/ 
- *
- * Copyright 2006 One Laptop Per Child Association, Inc.
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- *
- * Written by Jonathan Corbet, corbet@lwn.net.
- *
- * v4l2_device/v4l2_subdev conversion by:
- * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: this conversion is untested! Please contact the linux-media
- * mailinglist if you can test this, together with the test results.
- *
- * This file may be distributed under the terms of the GNU General
- * Public License, version 2.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/dmi.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/jiffies.h>
-#include <linux/vmalloc.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#include "ov7670.h"
-#include "cafe_ccic-regs.h"
-
-#define CAFE_VERSION 0x000002
-
-
-/*
- * Parameters.
- */
-MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
-MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("Video");
-
-/*
- * Internal DMA buffer management.  Since the controller cannot do S/G I/O,
- * we must have physically contiguous buffers to bring frames into.
- * These parameters control how many buffers we use, whether we
- * allocate them at load time (better chance of success, but nails down
- * memory) or when somebody tries to use the camera (riskier), and,
- * for load-time allocation, how big they should be.
- *
- * The controller can cycle through three buffers.  We could use
- * more by flipping pointers around, but it probably makes little
- * sense.
- */
-
-#define MAX_DMA_BUFS 3
-static int alloc_bufs_at_read;
-module_param(alloc_bufs_at_read, bool, 0444);
-MODULE_PARM_DESC(alloc_bufs_at_read,
-		"Non-zero value causes DMA buffers to be allocated when the "
-		"video capture device is read, rather than at module load "
-		"time.  This saves memory, but decreases the chances of "
-		"successfully getting those buffers.");
-
-static int n_dma_bufs = 3;
-module_param(n_dma_bufs, uint, 0644);
-MODULE_PARM_DESC(n_dma_bufs,
-		"The number of DMA buffers to allocate.  Can be either two "
-		"(saves memory, makes timing tighter) or three.");
-
-static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2;  /* Worst case */
-module_param(dma_buf_size, uint, 0444);
-MODULE_PARM_DESC(dma_buf_size,
-		"The size of the allocated DMA buffers.  If actual operating "
-		"parameters require larger buffers, an attempt to reallocate "
-		"will be made.");
-
-static int min_buffers = 1;
-module_param(min_buffers, uint, 0644);
-MODULE_PARM_DESC(min_buffers,
-		"The minimum number of streaming I/O buffers we are willing "
-		"to work with.");
-
-static int max_buffers = 10;
-module_param(max_buffers, uint, 0644);
-MODULE_PARM_DESC(max_buffers,
-		"The maximum number of streaming I/O buffers an application "
-		"will be allowed to allocate.  These buffers are big and live "
-		"in vmalloc space.");
-
-static int flip;
-module_param(flip, bool, 0444);
-MODULE_PARM_DESC(flip,
-		"If set, the sensor will be instructed to flip the image "
-		"vertically.");
-
-
-enum cafe_state {
-	S_NOTREADY,	/* Not yet initialized */
-	S_IDLE,		/* Just hanging around */
-	S_FLAKED,	/* Some sort of problem */
-	S_SINGLEREAD,	/* In read() */
-	S_SPECREAD,   	/* Speculative read (for future read()) */
-	S_STREAMING	/* Streaming data */
-};
-
-/*
- * Tracking of streaming I/O buffers.
- */
-struct cafe_sio_buffer {
-	struct list_head list;
-	struct v4l2_buffer v4lbuf;
-	char *buffer;   /* Where it lives in kernel space */
-	int mapcount;
-	struct cafe_camera *cam;
-};
-
-/*
- * A description of one of our devices.
- * Locking: controlled by s_mutex.  Certain fields, however, require
- * 	    the dev_lock spinlock; they are marked as such by comments.
- *	    dev_lock is also required for access to device registers.
- */
-struct cafe_camera
-{
-	struct v4l2_device v4l2_dev;
-	enum cafe_state state;
-	unsigned long flags;   		/* Buffer status, mainly (dev_lock) */
-	int users;			/* How many open FDs */
-	struct file *owner;		/* Who has data access (v4l2) */
-
-	/*
-	 * Subsystem structures.
-	 */
-	struct pci_dev *pdev;
-	struct video_device vdev;
-	struct i2c_adapter i2c_adapter;
-	struct v4l2_subdev *sensor;
-	unsigned short sensor_addr;
-
-	unsigned char __iomem *regs;
-	struct list_head dev_list;	/* link to other devices */
-
-	/* DMA buffers */
-	unsigned int nbufs;		/* How many are alloc'd */
-	int next_buf;			/* Next to consume (dev_lock) */
-	unsigned int dma_buf_size;  	/* allocated size */
-	void *dma_bufs[MAX_DMA_BUFS];	/* Internal buffer addresses */
-	dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
-	unsigned int specframes;	/* Unconsumed spec frames (dev_lock) */
-	unsigned int sequence;		/* Frame sequence number */
-	unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */
-
-	/* Streaming buffers */
-	unsigned int n_sbufs;		/* How many we have */
-	struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */
-	struct list_head sb_avail;	/* Available for data (we own) (dev_lock) */
-	struct list_head sb_full;	/* With data (user space owns) (dev_lock) */
-	struct tasklet_struct s_tasklet;
-
-	/* Current operating parameters */
-	u32 sensor_type;		/* Currently ov7670 only */
-	struct v4l2_pix_format pix_format;
-	enum v4l2_mbus_pixelcode mbus_code;
-
-	/* Locks */
-	struct mutex s_mutex; /* Access to this structure */
-	spinlock_t dev_lock;  /* Access to device */
-
-	/* Misc */
-	wait_queue_head_t smbus_wait;	/* Waiting on i2c events */
-	wait_queue_head_t iowait;	/* Waiting on frame data */
-};
-
-/*
- * Status flags.  Always manipulated with bit operations.
- */
-#define CF_BUF0_VALID	 0	/* Buffers valid - first three */
-#define CF_BUF1_VALID	 1
-#define CF_BUF2_VALID	 2
-#define CF_DMA_ACTIVE	 3	/* A frame is incoming */
-#define CF_CONFIG_NEEDED 4	/* Must configure hardware */
-
-#define sensor_call(cam, o, f, args...) \
-	v4l2_subdev_call(cam->sensor, o, f, ##args)
-
-static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
-{
-	return container_of(dev, struct cafe_camera, v4l2_dev);
-}
-
-static struct cafe_format_struct {
-	__u8 *desc;
-	__u32 pixelformat;
-	int bpp;   /* Bytes per pixel */
-	enum v4l2_mbus_pixelcode mbus_code;
-} cafe_formats[] = {
-	{
-		.desc		= "YUYV 4:2:2",
-		.pixelformat	= V4L2_PIX_FMT_YUYV,
-		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
-		.bpp		= 2,
-	},
-	{
-		.desc		= "RGB 444",
-		.pixelformat	= V4L2_PIX_FMT_RGB444,
-		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
-		.bpp		= 2,
-	},
-	{
-		.desc		= "RGB 565",
-		.pixelformat	= V4L2_PIX_FMT_RGB565,
-		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
-		.bpp		= 2,
-	},
-	{
-		.desc		= "Raw RGB Bayer",
-		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
-		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
-		.bpp		= 1
-	},
-};
-#define N_CAFE_FMTS ARRAY_SIZE(cafe_formats)
-
-static struct cafe_format_struct *cafe_find_format(u32 pixelformat)
-{
-	unsigned i;
-
-	for (i = 0; i < N_CAFE_FMTS; i++)
-		if (cafe_formats[i].pixelformat == pixelformat)
-			return cafe_formats + i;
-	/* Not found? Then return the first format. */
-	return cafe_formats;
-}
-
-/*
- * Start over with DMA buffers - dev_lock needed.
- */
-static void cafe_reset_buffers(struct cafe_camera *cam)
-{
-	int i;
-
-	cam->next_buf = -1;
-	for (i = 0; i < cam->nbufs; i++)
-		clear_bit(i, &cam->flags);
-	cam->specframes = 0;
-}
-
-static inline int cafe_needs_config(struct cafe_camera *cam)
-{
-	return test_bit(CF_CONFIG_NEEDED, &cam->flags);
-}
-
-static void cafe_set_config_needed(struct cafe_camera *cam, int needed)
-{
-	if (needed)
-		set_bit(CF_CONFIG_NEEDED, &cam->flags);
-	else
-		clear_bit(CF_CONFIG_NEEDED, &cam->flags);
-}
-
-
-
-
-/*
- * Debugging and related.
- */
-#define cam_err(cam, fmt, arg...) \
-	dev_err(&(cam)->pdev->dev, fmt, ##arg);
-#define cam_warn(cam, fmt, arg...) \
-	dev_warn(&(cam)->pdev->dev, fmt, ##arg);
-#define cam_dbg(cam, fmt, arg...) \
-	dev_dbg(&(cam)->pdev->dev, fmt, ##arg);
-
-
-/* ---------------------------------------------------------------------*/
-
-/*
- * Device register I/O
- */
-static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg,
-		unsigned int val)
-{
-	iowrite32(val, cam->regs + reg);
-}
-
-static inline unsigned int cafe_reg_read(struct cafe_camera *cam,
-		unsigned int reg)
-{
-	return ioread32(cam->regs + reg);
-}
-
-
-static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg,
-		unsigned int val, unsigned int mask)
-{
-	unsigned int v = cafe_reg_read(cam, reg);
-
-	v = (v & ~mask) | (val & mask);
-	cafe_reg_write(cam, reg, v);
-}
-
-static inline void cafe_reg_clear_bit(struct cafe_camera *cam,
-		unsigned int reg, unsigned int val)
-{
-	cafe_reg_write_mask(cam, reg, 0, val);
-}
-
-static inline void cafe_reg_set_bit(struct cafe_camera *cam,
-		unsigned int reg, unsigned int val)
-{
-	cafe_reg_write_mask(cam, reg, val, val);
-}
-
-
-
-/* -------------------------------------------------------------------- */
-/*
- * The I2C/SMBUS interface to the camera itself starts here.  The
- * controller handles SMBUS itself, presenting a relatively simple register
- * interface; all we have to do is to tell it where to route the data.
- */
-#define CAFE_SMBUS_TIMEOUT (HZ)  /* generous */
-
-static int cafe_smbus_write_done(struct cafe_camera *cam)
-{
-	unsigned long flags;
-	int c1;
-
-	/*
-	 * We must delay after the interrupt, or the controller gets confused
-	 * and never does give us good status.  Fortunately, we don't do this
-	 * often.
-	 */
-	udelay(20);
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	c1 = cafe_reg_read(cam, REG_TWSIC1);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
-}
-
-static int cafe_smbus_write_data(struct cafe_camera *cam,
-		u16 addr, u8 command, u8 value)
-{
-	unsigned int rval;
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
-	rval |= TWSIC0_OVMAGIC;  /* Make OV sensors work */
-	/*
-	 * Marvell sez set clkdiv to all 1's for now.
-	 */
-	rval |= TWSIC0_CLKDIV;
-	cafe_reg_write(cam, REG_TWSIC0, rval);
-	(void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
-	rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
-	cafe_reg_write(cam, REG_TWSIC1, rval);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-
-	/* Unfortunately, reading TWSIC1 too soon after sending a command
-	 * causes the device to die.
-	 * Use a busy-wait because we often send a large quantity of small
-	 * commands at-once; using msleep() would cause a lot of context
-	 * switches which take longer than 2ms, resulting in a noticeable
-	 * boot-time and capture-start delays.
-	 */
-	mdelay(2);
-
-	/*
-	 * Another sad fact is that sometimes, commands silently complete but
-	 * cafe_smbus_write_done() never becomes aware of this.
-	 * This happens at random and appears to possible occur with any
-	 * command.
-	 * We don't understand why this is. We work around this issue
-	 * with the timeout in the wait below, assuming that all commands
-	 * complete within the timeout.
-	 */
-	wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
-			CAFE_SMBUS_TIMEOUT);
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	rval = cafe_reg_read(cam, REG_TWSIC1);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-
-	if (rval & TWSIC1_WSTAT) {
-		cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
-				command, value);
-		return -EIO;
-	}
-	if (rval & TWSIC1_ERROR) {
-		cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
-				command, value);
-		return -EIO;
-	}
-	return 0;
-}
-
-
-
-static int cafe_smbus_read_done(struct cafe_camera *cam)
-{
-	unsigned long flags;
-	int c1;
-
-	/*
-	 * We must delay after the interrupt, or the controller gets confused
-	 * and never does give us good status.  Fortunately, we don't do this
-	 * often.
-	 */
-	udelay(20);
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	c1 = cafe_reg_read(cam, REG_TWSIC1);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
-}
-
-
-
-static int cafe_smbus_read_data(struct cafe_camera *cam,
-		u16 addr, u8 command, u8 *value)
-{
-	unsigned int rval;
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
-	rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
-	/*
-	 * Marvel sez set clkdiv to all 1's for now.
-	 */
-	rval |= TWSIC0_CLKDIV;
-	cafe_reg_write(cam, REG_TWSIC0, rval);
-	(void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
-	rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
-	cafe_reg_write(cam, REG_TWSIC1, rval);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-
-	wait_event_timeout(cam->smbus_wait,
-			cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT);
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	rval = cafe_reg_read(cam, REG_TWSIC1);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-
-	if (rval & TWSIC1_ERROR) {
-		cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
-		return -EIO;
-	}
-	if (! (rval & TWSIC1_RVALID)) {
-		cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
-				command);
-		return -EIO;
-	}
-	*value = rval & 0xff;
-	return 0;
-}
-
-/*
- * Perform a transfer over SMBUS.  This thing is called under
- * the i2c bus lock, so we shouldn't race with ourselves...
- */
-static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
-		unsigned short flags, char rw, u8 command,
-		int size, union i2c_smbus_data *data)
-{
-	struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
-	struct cafe_camera *cam = to_cam(v4l2_dev);
-	int ret = -EINVAL;
-
-	/*
-	 * This interface would appear to only do byte data ops.  OK
-	 * it can do word too, but the cam chip has no use for that.
-	 */
-	if (size != I2C_SMBUS_BYTE_DATA) {
-		cam_err(cam, "funky xfer size %d\n", size);
-		return -EINVAL;
-	}
-
-	if (rw == I2C_SMBUS_WRITE)
-		ret = cafe_smbus_write_data(cam, addr, command, data->byte);
-	else if (rw == I2C_SMBUS_READ)
-		ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
-	return ret;
-}
-
-
-static void cafe_smbus_enable_irq(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-static u32 cafe_smbus_func(struct i2c_adapter *adapter)
-{
-	return I2C_FUNC_SMBUS_READ_BYTE_DATA  |
-	       I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
-}
-
-static struct i2c_algorithm cafe_smbus_algo = {
-	.smbus_xfer = cafe_smbus_xfer,
-	.functionality = cafe_smbus_func
-};
-
-/* Somebody is on the bus */
-static void cafe_ctlr_stop_dma(struct cafe_camera *cam);
-static void cafe_ctlr_power_down(struct cafe_camera *cam);
-
-static int cafe_smbus_setup(struct cafe_camera *cam)
-{
-	struct i2c_adapter *adap = &cam->i2c_adapter;
-	int ret;
-
-	cafe_smbus_enable_irq(cam);
-	adap->owner = THIS_MODULE;
-	adap->algo = &cafe_smbus_algo;
-	strcpy(adap->name, "cafe_ccic");
-	adap->dev.parent = &cam->pdev->dev;
-	i2c_set_adapdata(adap, &cam->v4l2_dev);
-	ret = i2c_add_adapter(adap);
-	if (ret)
-		printk(KERN_ERR "Unable to register cafe i2c adapter\n");
-	return ret;
-}
-
-static void cafe_smbus_shutdown(struct cafe_camera *cam)
-{
-	i2c_del_adapter(&cam->i2c_adapter);
-}
-
-
-/* ------------------------------------------------------------------- */
-/*
- * Deal with the controller.
- */
-
-/*
- * Do everything we think we need to have the interface operating
- * according to the desired format.
- */
-static void cafe_ctlr_dma(struct cafe_camera *cam)
-{
-	/*
-	 * Store the first two Y buffers (we aren't supporting
-	 * planar formats for now, so no UV bufs).  Then either
-	 * set the third if it exists, or tell the controller
-	 * to just use two.
-	 */
-	cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
-	cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
-	if (cam->nbufs > 2) {
-		cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
-		cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
-	}
-	else
-		cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
-	cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */
-}
-
-static void cafe_ctlr_image(struct cafe_camera *cam)
-{
-	int imgsz;
-	struct v4l2_pix_format *fmt = &cam->pix_format;
-
-	imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
-		(fmt->bytesperline & IMGSZ_H_MASK);
-	cafe_reg_write(cam, REG_IMGSIZE, imgsz);
-	cafe_reg_write(cam, REG_IMGOFFSET, 0);
-	/* YPITCH just drops the last two bits */
-	cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
-			IMGP_YP_MASK);
-	/*
-	 * Tell the controller about the image format we are using.
-	 */
-	switch (cam->pix_format.pixelformat) {
-	case V4L2_PIX_FMT_YUYV:
-	    cafe_reg_write_mask(cam, REG_CTRL0,
-			    C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
-			    C0_DF_MASK);
-	    break;
-
-	case V4L2_PIX_FMT_RGB444:
-	    cafe_reg_write_mask(cam, REG_CTRL0,
-			    C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
-			    C0_DF_MASK);
-		/* Alpha value? */
-	    break;
-
-	case V4L2_PIX_FMT_RGB565:
-	    cafe_reg_write_mask(cam, REG_CTRL0,
-			    C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
-			    C0_DF_MASK);
-	    break;
-
-	default:
-	    cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
-	    break;
-	}
-	/*
-	 * Make sure it knows we want to use hsync/vsync.
-	 */
-	cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
-			C0_SIFM_MASK);
-}
-
-
-/*
- * Configure the controller for operation; caller holds the
- * device mutex.
- */
-static int cafe_ctlr_configure(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_ctlr_dma(cam);
-	cafe_ctlr_image(cam);
-	cafe_set_config_needed(cam, 0);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	return 0;
-}
-
-static void cafe_ctlr_irq_enable(struct cafe_camera *cam)
-{
-	/*
-	 * Clear any pending interrupts, since we do not
-	 * expect to have I/O active prior to enabling.
-	 */
-	cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
-	cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
-}
-
-static void cafe_ctlr_irq_disable(struct cafe_camera *cam)
-{
-	cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
-}
-
-/*
- * Make the controller start grabbing images.  Everything must
- * be set up before doing this.
- */
-static void cafe_ctlr_start(struct cafe_camera *cam)
-{
-	/* set_bit performs a read, so no other barrier should be
-	   needed here */
-	cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
-}
-
-static void cafe_ctlr_stop(struct cafe_camera *cam)
-{
-	cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
-}
-
-static void cafe_ctlr_init(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	/*
-	 * Added magic to bring up the hardware on the B-Test board
-	 */
-	cafe_reg_write(cam, 0x3038, 0x8);
-	cafe_reg_write(cam, 0x315c, 0x80008);
-	/*
-	 * Go through the dance needed to wake the device up.
-	 * Note that these registers are global and shared
-	 * with the NAND and SD devices.  Interaction between the
-	 * three still needs to be examined.
-	 */
-	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
-	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
-	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
-	/*
-	 * Here we must wait a bit for the controller to come around.
-	 */
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	msleep(5);
-	spin_lock_irqsave(&cam->dev_lock, flags);
-
-	cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
-	cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
-	/*
-	 * Make sure it's not powered down.
-	 */
-	cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
-	/*
-	 * Turn off the enable bit.  It sure should be off anyway,
-	 * but it's good to be sure.
-	 */
-	cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
-	/*
-	 * Mask all interrupts.
-	 */
-	cafe_reg_write(cam, REG_IRQMASK, 0);
-	/*
-	 * Clock the sensor appropriately.  Controller clock should
-	 * be 48MHz, sensor "typical" value is half that.
-	 */
-	cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-
-/*
- * Stop the controller, and don't return until we're really sure that no
- * further DMA is going on.
- */
-static void cafe_ctlr_stop_dma(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	/*
-	 * Theory: stop the camera controller (whether it is operating
-	 * or not).  Delay briefly just in case we race with the SOF
-	 * interrupt, then wait until no DMA is active.
-	 */
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_ctlr_stop(cam);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	mdelay(1);
-	wait_event_timeout(cam->iowait,
-			!test_bit(CF_DMA_ACTIVE, &cam->flags), HZ);
-	if (test_bit(CF_DMA_ACTIVE, &cam->flags))
-		cam_err(cam, "Timeout waiting for DMA to end\n");
-		/* This would be bad news - what now? */
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cam->state = S_IDLE;
-	cafe_ctlr_irq_disable(cam);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-/*
- * Power up and down.
- */
-static void cafe_ctlr_power_up(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
-	/*
-	 * Part one of the sensor dance: turn the global
-	 * GPIO signal on.
-	 */
-	cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
-	cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
-	/*
-	 * Put the sensor into operational mode (assumes OLPC-style
-	 * wiring).  Control 0 is reset - set to 1 to operate.
-	 * Control 1 is power down, set to 0 to operate.
-	 */
-	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
-/*	mdelay(1); */ /* Marvell says 1ms will do it */
-	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
-/*	mdelay(1); */ /* Enough? */
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	msleep(5); /* Just to be sure */
-}
-
-static void cafe_ctlr_power_down(struct cafe_camera *cam)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
-	cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
-	cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT);
-	cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-/* -------------------------------------------------------------------- */
-/*
- * Communications with the sensor.
- */
-
-static int __cafe_cam_reset(struct cafe_camera *cam)
-{
-	return sensor_call(cam, core, reset, 0);
-}
-
-/*
- * We have found the sensor on the i2c.  Let's try to have a
- * conversation.
- */
-static int cafe_cam_init(struct cafe_camera *cam)
-{
-	struct v4l2_dbg_chip_ident chip;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	if (cam->state != S_NOTREADY)
-		cam_warn(cam, "Cam init with device in funky state %d",
-				cam->state);
-	ret = __cafe_cam_reset(cam);
-	if (ret)
-		goto out;
-	chip.ident = V4L2_IDENT_NONE;
-	chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
-	chip.match.addr = cam->sensor_addr;
-	ret = sensor_call(cam, core, g_chip_ident, &chip);
-	if (ret)
-		goto out;
-	cam->sensor_type = chip.ident;
-	if (cam->sensor_type != V4L2_IDENT_OV7670) {
-		cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
-		ret = -EINVAL;
-		goto out;
-	}
-/* Get/set parameters? */
-	ret = 0;
-	cam->state = S_IDLE;
-  out:
-	cafe_ctlr_power_down(cam);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-/*
- * Configure the sensor to match the parameters we have.  Caller should
- * hold s_mutex
- */
-static int cafe_cam_set_flip(struct cafe_camera *cam)
-{
-	struct v4l2_control ctrl;
-
-	memset(&ctrl, 0, sizeof(ctrl));
-	ctrl.id = V4L2_CID_VFLIP;
-	ctrl.value = flip;
-	return sensor_call(cam, core, s_ctrl, &ctrl);
-}
-
-
-static int cafe_cam_configure(struct cafe_camera *cam)
-{
-	struct v4l2_mbus_framefmt mbus_fmt;
-	int ret;
-
-	v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
-	ret = sensor_call(cam, core, init, 0);
-	if (ret == 0)
-		ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
-	/*
-	 * OV7670 does weird things if flip is set *before* format...
-	 */
-	ret += cafe_cam_set_flip(cam);
-	return ret;
-}
-
-/* -------------------------------------------------------------------- */
-/*
- * DMA buffer management.  These functions need s_mutex held.
- */
-
-/* FIXME: this is inefficient as hell, since dma_alloc_coherent just
- * does a get_free_pages() call, and we waste a good chunk of an orderN
- * allocation.  Should try to allocate the whole set in one chunk.
- */
-static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime)
-{
-	int i;
-
-	cafe_set_config_needed(cam, 1);
-	if (loadtime)
-		cam->dma_buf_size = dma_buf_size;
-	else
-		cam->dma_buf_size = cam->pix_format.sizeimage;
-	if (n_dma_bufs > 3)
-		n_dma_bufs = 3;
-
-	cam->nbufs = 0;
-	for (i = 0; i < n_dma_bufs; i++) {
-		cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev,
-				cam->dma_buf_size, cam->dma_handles + i,
-				GFP_KERNEL);
-		if (cam->dma_bufs[i] == NULL) {
-			cam_warn(cam, "Failed to allocate DMA buffer\n");
-			break;
-		}
-		/* For debug, remove eventually */
-		memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size);
-		(cam->nbufs)++;
-	}
-
-	switch (cam->nbufs) {
-	case 1:
-	    dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
-			    cam->dma_bufs[0], cam->dma_handles[0]);
-	    cam->nbufs = 0;
-	case 0:
-	    cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
-	    return -ENOMEM;
-
-	case 2:
-	    if (n_dma_bufs > 2)
-		    cam_warn(cam, "Will limp along with only 2 buffers\n");
-	    break;
-	}
-	return 0;
-}
-
-static void cafe_free_dma_bufs(struct cafe_camera *cam)
-{
-	int i;
-
-	for (i = 0; i < cam->nbufs; i++) {
-		dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
-				cam->dma_bufs[i], cam->dma_handles[i]);
-		cam->dma_bufs[i] = NULL;
-	}
-	cam->nbufs = 0;
-}
-
-
-
-
-
-/* ----------------------------------------------------------------------- */
-/*
- * Here starts the V4L2 interface code.
- */
-
-/*
- * Read an image from the device.
- */
-static ssize_t cafe_deliver_buffer(struct cafe_camera *cam,
-		char __user *buffer, size_t len, loff_t *pos)
-{
-	int bufno;
-	unsigned long flags;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	if (cam->next_buf < 0) {
-		cam_err(cam, "deliver_buffer: No next buffer\n");
-		spin_unlock_irqrestore(&cam->dev_lock, flags);
-		return -EIO;
-	}
-	bufno = cam->next_buf;
-	clear_bit(bufno, &cam->flags);
-	if (++(cam->next_buf) >= cam->nbufs)
-		cam->next_buf = 0;
-	if (! test_bit(cam->next_buf, &cam->flags))
-		cam->next_buf = -1;
-	cam->specframes = 0;
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-
-	if (len > cam->pix_format.sizeimage)
-		len = cam->pix_format.sizeimage;
-	if (copy_to_user(buffer, cam->dma_bufs[bufno], len))
-		return -EFAULT;
-	(*pos) += len;
-	return len;
-}
-
-/*
- * Get everything ready, and start grabbing frames.
- */
-static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state)
-{
-	int ret;
-	unsigned long flags;
-
-	/*
-	 * Configuration.  If we still don't have DMA buffers,
-	 * make one last, desperate attempt.
-	 */
-	if (cam->nbufs == 0)
-		if (cafe_alloc_dma_bufs(cam, 0))
-			return -ENOMEM;
-
-	if (cafe_needs_config(cam)) {
-		cafe_cam_configure(cam);
-		ret = cafe_ctlr_configure(cam);
-		if (ret)
-			return ret;
-	}
-
-	/*
-	 * Turn it loose.
-	 */
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	cafe_reset_buffers(cam);
-	cafe_ctlr_irq_enable(cam);
-	cam->state = state;
-	cafe_ctlr_start(cam);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	return 0;
-}
-
-
-static ssize_t cafe_v4l_read(struct file *filp,
-		char __user *buffer, size_t len, loff_t *pos)
-{
-	struct cafe_camera *cam = filp->private_data;
-	int ret = 0;
-
-	/*
-	 * Perhaps we're in speculative read mode and already
-	 * have data?
-	 */
-	mutex_lock(&cam->s_mutex);
-	if (cam->state == S_SPECREAD) {
-		if (cam->next_buf >= 0) {
-			ret = cafe_deliver_buffer(cam, buffer, len, pos);
-			if (ret != 0)
-				goto out_unlock;
-		}
-	} else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) {
-		ret = -EIO;
-		goto out_unlock;
-	} else if (cam->state != S_IDLE) {
-		ret = -EBUSY;
-		goto out_unlock;
-	}
-
-	/*
-	 * v4l2: multiple processes can open the device, but only
-	 * one gets to grab data from it.
-	 */
-	if (cam->owner && cam->owner != filp) {
-		ret = -EBUSY;
-		goto out_unlock;
-	}
-	cam->owner = filp;
-
-	/*
-	 * Do setup if need be.
-	 */
-	if (cam->state != S_SPECREAD) {
-		ret = cafe_read_setup(cam, S_SINGLEREAD);
-		if (ret)
-			goto out_unlock;
-	}
-	/*
-	 * Wait for something to happen.  This should probably
-	 * be interruptible (FIXME).
-	 */
-	wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ);
-	if (cam->next_buf < 0) {
-		cam_err(cam, "read() operation timed out\n");
-		cafe_ctlr_stop_dma(cam);
-		ret = -EIO;
-		goto out_unlock;
-	}
-	/*
-	 * Give them their data and we should be done.
-	 */
-	ret = cafe_deliver_buffer(cam, buffer, len, pos);
-
-  out_unlock:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-
-
-
-
-
-
-/*
- * Streaming I/O support.
- */
-
-
-
-static int cafe_vidioc_streamon(struct file *filp, void *priv,
-		enum v4l2_buf_type type)
-{
-	struct cafe_camera *cam = filp->private_data;
-	int ret = -EINVAL;
-
-	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		goto out;
-	mutex_lock(&cam->s_mutex);
-	if (cam->state != S_IDLE || cam->n_sbufs == 0)
-		goto out_unlock;
-
-	cam->sequence = 0;
-	ret = cafe_read_setup(cam, S_STREAMING);
-
-  out_unlock:
-	mutex_unlock(&cam->s_mutex);
-  out:
-	return ret;
-}
-
-
-static int cafe_vidioc_streamoff(struct file *filp, void *priv,
-		enum v4l2_buf_type type)
-{
-	struct cafe_camera *cam = filp->private_data;
-	int ret = -EINVAL;
-
-	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		goto out;
-	mutex_lock(&cam->s_mutex);
-	if (cam->state != S_STREAMING)
-		goto out_unlock;
-
-	cafe_ctlr_stop_dma(cam);
-	ret = 0;
-
-  out_unlock:
-	mutex_unlock(&cam->s_mutex);
-  out:
-	return ret;
-}
-
-
-
-static int cafe_setup_siobuf(struct cafe_camera *cam, int index)
-{
-	struct cafe_sio_buffer *buf = cam->sb_bufs + index;
-
-	INIT_LIST_HEAD(&buf->list);
-	buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage);
-	buf->buffer = vmalloc_user(buf->v4lbuf.length);
-	if (buf->buffer == NULL)
-		return -ENOMEM;
-	buf->mapcount = 0;
-	buf->cam = cam;
-
-	buf->v4lbuf.index = index;
-	buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	buf->v4lbuf.field = V4L2_FIELD_NONE;
-	buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
-	/*
-	 * Offset: must be 32-bit even on a 64-bit system.  videobuf-dma-sg
-	 * just uses the length times the index, but the spec warns
-	 * against doing just that - vma merging problems.  So we
-	 * leave a gap between each pair of buffers.
-	 */
-	buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
-	return 0;
-}
-
-static int cafe_free_sio_buffers(struct cafe_camera *cam)
-{
-	int i;
-
-	/*
-	 * If any buffers are mapped, we cannot free them at all.
-	 */
-	for (i = 0; i < cam->n_sbufs; i++)
-		if (cam->sb_bufs[i].mapcount > 0)
-			return -EBUSY;
-	/*
-	 * OK, let's do it.
-	 */
-	for (i = 0; i < cam->n_sbufs; i++)
-		vfree(cam->sb_bufs[i].buffer);
-	cam->n_sbufs = 0;
-	kfree(cam->sb_bufs);
-	cam->sb_bufs = NULL;
-	INIT_LIST_HEAD(&cam->sb_avail);
-	INIT_LIST_HEAD(&cam->sb_full);
-	return 0;
-}
-
-
-
-static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
-		struct v4l2_requestbuffers *req)
-{
-	struct cafe_camera *cam = filp->private_data;
-	int ret = 0;  /* Silence warning */
-
-	/*
-	 * Make sure it's something we can do.  User pointers could be
-	 * implemented without great pain, but that's not been done yet.
-	 */
-	if (req->memory != V4L2_MEMORY_MMAP)
-		return -EINVAL;
-	/*
-	 * If they ask for zero buffers, they really want us to stop streaming
-	 * (if it's happening) and free everything.  Should we check owner?
-	 */
-	mutex_lock(&cam->s_mutex);
-	if (req->count == 0) {
-		if (cam->state == S_STREAMING)
-			cafe_ctlr_stop_dma(cam);
-		ret = cafe_free_sio_buffers (cam);
-		goto out;
-	}
-	/*
-	 * Device needs to be idle and working.  We *could* try to do the
-	 * right thing in S_SPECREAD by shutting things down, but it
-	 * probably doesn't matter.
-	 */
-	if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) {
-		ret = -EBUSY;
-		goto out;
-	}
-	cam->owner = filp;
-
-	if (req->count < min_buffers)
-		req->count = min_buffers;
-	else if (req->count > max_buffers)
-		req->count = max_buffers;
-	if (cam->n_sbufs > 0) {
-		ret = cafe_free_sio_buffers(cam);
-		if (ret)
-			goto out;
-	}
-
-	cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer),
-			GFP_KERNEL);
-	if (cam->sb_bufs == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) {
-		ret = cafe_setup_siobuf(cam, cam->n_sbufs);
-		if (ret)
-			break;
-	}
-
-	if (cam->n_sbufs == 0)  /* no luck at all - ret already set */
-		kfree(cam->sb_bufs);
-	req->count = cam->n_sbufs;  /* In case of partial success */
-
-  out:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-static int cafe_vidioc_querybuf(struct file *filp, void *priv,
-		struct v4l2_buffer *buf)
-{
-	struct cafe_camera *cam = filp->private_data;
-	int ret = -EINVAL;
-
-	mutex_lock(&cam->s_mutex);
-	if (buf->index >= cam->n_sbufs)
-		goto out;
-	*buf = cam->sb_bufs[buf->index].v4lbuf;
-	ret = 0;
-  out:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-static int cafe_vidioc_qbuf(struct file *filp, void *priv,
-		struct v4l2_buffer *buf)
-{
-	struct cafe_camera *cam = filp->private_data;
-	struct cafe_sio_buffer *sbuf;
-	int ret = -EINVAL;
-	unsigned long flags;
-
-	mutex_lock(&cam->s_mutex);
-	if (buf->index >= cam->n_sbufs)
-		goto out;
-	sbuf = cam->sb_bufs + buf->index;
-	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
-		ret = 0; /* Already queued?? */
-		goto out;
-	}
-	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) {
-		/* Spec doesn't say anything, seems appropriate tho */
-		ret = -EBUSY;
-		goto out;
-	}
-	sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	list_add(&sbuf->list, &cam->sb_avail);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-	ret = 0;
-  out:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-static int cafe_vidioc_dqbuf(struct file *filp, void *priv,
-		struct v4l2_buffer *buf)
-{
-	struct cafe_camera *cam = filp->private_data;
-	struct cafe_sio_buffer *sbuf;
-	int ret = -EINVAL;
-	unsigned long flags;
-
-	mutex_lock(&cam->s_mutex);
-	if (cam->state != S_STREAMING)
-		goto out_unlock;
-	if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) {
-		ret = -EAGAIN;
-		goto out_unlock;
-	}
-
-	while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) {
-		mutex_unlock(&cam->s_mutex);
-		if (wait_event_interruptible(cam->iowait,
-						!list_empty(&cam->sb_full))) {
-			ret = -ERESTARTSYS;
-			goto out;
-		}
-		mutex_lock(&cam->s_mutex);
-	}
-
-	if (cam->state != S_STREAMING)
-		ret = -EINTR;
-	else {
-		spin_lock_irqsave(&cam->dev_lock, flags);
-		/* Should probably recheck !list_empty() here */
-		sbuf = list_entry(cam->sb_full.next,
-				struct cafe_sio_buffer, list);
-		list_del_init(&sbuf->list);
-		spin_unlock_irqrestore(&cam->dev_lock, flags);
-		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
-		*buf = sbuf->v4lbuf;
-		ret = 0;
-	}
-
-  out_unlock:
-	mutex_unlock(&cam->s_mutex);
-  out:
-	return ret;
-}
-
-
-
-static void cafe_v4l_vm_open(struct vm_area_struct *vma)
-{
-	struct cafe_sio_buffer *sbuf = vma->vm_private_data;
-	/*
-	 * Locking: done under mmap_sem, so we don't need to
-	 * go back to the camera lock here.
-	 */
-	sbuf->mapcount++;
-}
-
-
-static void cafe_v4l_vm_close(struct vm_area_struct *vma)
-{
-	struct cafe_sio_buffer *sbuf = vma->vm_private_data;
-
-	mutex_lock(&sbuf->cam->s_mutex);
-	sbuf->mapcount--;
-	/* Docs say we should stop I/O too... */
-	if (sbuf->mapcount == 0)
-		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
-	mutex_unlock(&sbuf->cam->s_mutex);
-}
-
-static const struct vm_operations_struct cafe_v4l_vm_ops = {
-	.open = cafe_v4l_vm_open,
-	.close = cafe_v4l_vm_close
-};
-
-
-static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
-{
-	struct cafe_camera *cam = filp->private_data;
-	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-	int ret = -EINVAL;
-	int i;
-	struct cafe_sio_buffer *sbuf = NULL;
-
-	if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED))
-		return -EINVAL;
-	/*
-	 * Find the buffer they are looking for.
-	 */
-	mutex_lock(&cam->s_mutex);
-	for (i = 0; i < cam->n_sbufs; i++)
-		if (cam->sb_bufs[i].v4lbuf.m.offset == offset) {
-			sbuf = cam->sb_bufs + i;
-			break;
-		}
-	if (sbuf == NULL)
-		goto out;
-
-	ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
-	if (ret)
-		goto out;
-	vma->vm_flags |= VM_DONTEXPAND;
-	vma->vm_private_data = sbuf;
-	vma->vm_ops = &cafe_v4l_vm_ops;
-	sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
-	cafe_v4l_vm_open(vma);
-	ret = 0;
-  out:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-
-static int cafe_v4l_open(struct file *filp)
-{
-	struct cafe_camera *cam = video_drvdata(filp);
-
-	filp->private_data = cam;
-
-	mutex_lock(&cam->s_mutex);
-	if (cam->users == 0) {
-		cafe_ctlr_power_up(cam);
-		__cafe_cam_reset(cam);
-		cafe_set_config_needed(cam, 1);
-	/* FIXME make sure this is complete */
-	}
-	(cam->users)++;
-	mutex_unlock(&cam->s_mutex);
-	return 0;
-}
-
-
-static int cafe_v4l_release(struct file *filp)
-{
-	struct cafe_camera *cam = filp->private_data;
-
-	mutex_lock(&cam->s_mutex);
-	(cam->users)--;
-	if (filp == cam->owner) {
-		cafe_ctlr_stop_dma(cam);
-		cafe_free_sio_buffers(cam);
-		cam->owner = NULL;
-	}
-	if (cam->users == 0) {
-		cafe_ctlr_power_down(cam);
-		if (alloc_bufs_at_read)
-			cafe_free_dma_bufs(cam);
-	}
-	mutex_unlock(&cam->s_mutex);
-	return 0;
-}
-
-
-
-static unsigned int cafe_v4l_poll(struct file *filp,
-		struct poll_table_struct *pt)
-{
-	struct cafe_camera *cam = filp->private_data;
-
-	poll_wait(filp, &cam->iowait, pt);
-	if (cam->next_buf >= 0)
-		return POLLIN | POLLRDNORM;
-	return 0;
-}
-
-
-
-static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
-		struct v4l2_queryctrl *qc)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, core, queryctrl, qc);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
-		struct v4l2_control *ctrl)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, core, g_ctrl, ctrl);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-static int cafe_vidioc_s_ctrl(struct file *filp, void *priv,
-		struct v4l2_control *ctrl)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, core, s_ctrl, ctrl);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-
-
-
-
-static int cafe_vidioc_querycap(struct file *file, void *priv,
-		struct v4l2_capability *cap)
-{
-	strcpy(cap->driver, "cafe_ccic");
-	strcpy(cap->card, "cafe_ccic");
-	cap->version = CAFE_VERSION;
-	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
-		V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
-	return 0;
-}
-
-
-/*
- * The default format we use until somebody says otherwise.
- */
-static const struct v4l2_pix_format cafe_def_pix_format = {
-	.width		= VGA_WIDTH,
-	.height		= VGA_HEIGHT,
-	.pixelformat	= V4L2_PIX_FMT_YUYV,
-	.field		= V4L2_FIELD_NONE,
-	.bytesperline	= VGA_WIDTH*2,
-	.sizeimage	= VGA_WIDTH*VGA_HEIGHT*2,
-};
-
-static const enum v4l2_mbus_pixelcode cafe_def_mbus_code =
-					V4L2_MBUS_FMT_YUYV8_2X8;
-
-static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
-		void *priv, struct v4l2_fmtdesc *fmt)
-{
-	if (fmt->index >= N_CAFE_FMTS)
-		return -EINVAL;
-	strlcpy(fmt->description, cafe_formats[fmt->index].desc,
-			sizeof(fmt->description));
-	fmt->pixelformat = cafe_formats[fmt->index].pixelformat;
-	return 0;
-}
-
-static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
-		struct v4l2_format *fmt)
-{
-	struct cafe_camera *cam = priv;
-	struct cafe_format_struct *f;
-	struct v4l2_pix_format *pix = &fmt->fmt.pix;
-	struct v4l2_mbus_framefmt mbus_fmt;
-	int ret;
-
-	f = cafe_find_format(pix->pixelformat);
-	pix->pixelformat = f->pixelformat;
-	v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
-	mutex_unlock(&cam->s_mutex);
-	v4l2_fill_pix_format(pix, &mbus_fmt);
-	pix->bytesperline = pix->width * f->bpp;
-	pix->sizeimage = pix->height * pix->bytesperline;
-	return ret;
-}
-
-static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
-		struct v4l2_format *fmt)
-{
-	struct cafe_camera *cam = priv;
-	struct cafe_format_struct *f;
-	int ret;
-
-	/*
-	 * Can't do anything if the device is not idle
-	 * Also can't if there are streaming buffers in place.
-	 */
-	if (cam->state != S_IDLE || cam->n_sbufs > 0)
-		return -EBUSY;
-
-	f = cafe_find_format(fmt->fmt.pix.pixelformat);
-
-	/*
-	 * See if the formatting works in principle.
-	 */
-	ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt);
-	if (ret)
-		return ret;
-	/*
-	 * Now we start to change things for real, so let's do it
-	 * under lock.
-	 */
-	mutex_lock(&cam->s_mutex);
-	cam->pix_format = fmt->fmt.pix;
-	cam->mbus_code = f->mbus_code;
-
-	/*
-	 * Make sure we have appropriate DMA buffers.
-	 */
-	ret = -ENOMEM;
-	if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
-		cafe_free_dma_bufs(cam);
-	if (cam->nbufs == 0) {
-		if (cafe_alloc_dma_bufs(cam, 0))
-			goto out;
-	}
-	/*
-	 * It looks like this might work, so let's program the sensor.
-	 */
-	ret = cafe_cam_configure(cam);
-	if (! ret)
-		ret = cafe_ctlr_configure(cam);
-  out:
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-/*
- * Return our stored notion of how the camera is/should be configured.
- * The V4l2 spec wants us to be smarter, and actually get this from
- * the camera (and not mess with it at open time).  Someday.
- */
-static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
-		struct v4l2_format *f)
-{
-	struct cafe_camera *cam = priv;
-
-	f->fmt.pix = cam->pix_format;
-	return 0;
-}
-
-/*
- * We only have one input - the sensor - so minimize the nonsense here.
- */
-static int cafe_vidioc_enum_input(struct file *filp, void *priv,
-		struct v4l2_input *input)
-{
-	if (input->index != 0)
-		return -EINVAL;
-
-	input->type = V4L2_INPUT_TYPE_CAMERA;
-	input->std = V4L2_STD_ALL; /* Not sure what should go here */
-	strcpy(input->name, "Camera");
-	return 0;
-}
-
-static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	if (i != 0)
-		return -EINVAL;
-	return 0;
-}
-
-/* from vivi.c */
-static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
-{
-	return 0;
-}
-
-/*
- * G/S_PARM.  Most of this is done by the sensor, but we are
- * the level which controls the number of read buffers.
- */
-static int cafe_vidioc_g_parm(struct file *filp, void *priv,
-		struct v4l2_streamparm *parms)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, g_parm, parms);
-	mutex_unlock(&cam->s_mutex);
-	parms->parm.capture.readbuffers = n_dma_bufs;
-	return ret;
-}
-
-static int cafe_vidioc_s_parm(struct file *filp, void *priv,
-		struct v4l2_streamparm *parms)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, s_parm, parms);
-	mutex_unlock(&cam->s_mutex);
-	parms->parm.capture.readbuffers = n_dma_bufs;
-	return ret;
-}
-
-static int cafe_vidioc_g_chip_ident(struct file *file, void *priv,
-		struct v4l2_dbg_chip_ident *chip)
-{
-	struct cafe_camera *cam = priv;
-
-	chip->ident = V4L2_IDENT_NONE;
-	chip->revision = 0;
-	if (v4l2_chip_match_host(&chip->match)) {
-		chip->ident = V4L2_IDENT_CAFE;
-		return 0;
-	}
-	return sensor_call(cam, core, g_chip_ident, chip);
-}
-
-static int cafe_vidioc_enum_framesizes(struct file *filp, void *priv,
-		struct v4l2_frmsizeenum *sizes)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, enum_framesizes, sizes);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-static int cafe_vidioc_enum_frameintervals(struct file *filp, void *priv,
-		struct v4l2_frmivalenum *interval)
-{
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, enum_frameintervals, interval);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int cafe_vidioc_g_register(struct file *file, void *priv,
-		struct v4l2_dbg_register *reg)
-{
-	struct cafe_camera *cam = priv;
-
-	if (v4l2_chip_match_host(&reg->match)) {
-		reg->val = cafe_reg_read(cam, reg->reg);
-		reg->size = 4;
-		return 0;
-	}
-	return sensor_call(cam, core, g_register, reg);
-}
-
-static int cafe_vidioc_s_register(struct file *file, void *priv,
-		struct v4l2_dbg_register *reg)
-{
-	struct cafe_camera *cam = priv;
-
-	if (v4l2_chip_match_host(&reg->match)) {
-		cafe_reg_write(cam, reg->reg, reg->val);
-		return 0;
-	}
-	return sensor_call(cam, core, s_register, reg);
-}
-#endif
-
-/*
- * This template device holds all of those v4l2 methods; we
- * clone it for specific real devices.
- */
-
-static const struct v4l2_file_operations cafe_v4l_fops = {
-	.owner = THIS_MODULE,
-	.open = cafe_v4l_open,
-	.release = cafe_v4l_release,
-	.read = cafe_v4l_read,
-	.poll = cafe_v4l_poll,
-	.mmap = cafe_v4l_mmap,
-	.unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = {
-	.vidioc_querycap 	= cafe_vidioc_querycap,
-	.vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
-	.vidioc_try_fmt_vid_cap	= cafe_vidioc_try_fmt_vid_cap,
-	.vidioc_s_fmt_vid_cap	= cafe_vidioc_s_fmt_vid_cap,
-	.vidioc_g_fmt_vid_cap	= cafe_vidioc_g_fmt_vid_cap,
-	.vidioc_enum_input	= cafe_vidioc_enum_input,
-	.vidioc_g_input		= cafe_vidioc_g_input,
-	.vidioc_s_input		= cafe_vidioc_s_input,
-	.vidioc_s_std		= cafe_vidioc_s_std,
-	.vidioc_reqbufs		= cafe_vidioc_reqbufs,
-	.vidioc_querybuf	= cafe_vidioc_querybuf,
-	.vidioc_qbuf		= cafe_vidioc_qbuf,
-	.vidioc_dqbuf		= cafe_vidioc_dqbuf,
-	.vidioc_streamon	= cafe_vidioc_streamon,
-	.vidioc_streamoff	= cafe_vidioc_streamoff,
-	.vidioc_queryctrl	= cafe_vidioc_queryctrl,
-	.vidioc_g_ctrl		= cafe_vidioc_g_ctrl,
-	.vidioc_s_ctrl		= cafe_vidioc_s_ctrl,
-	.vidioc_g_parm		= cafe_vidioc_g_parm,
-	.vidioc_s_parm		= cafe_vidioc_s_parm,
-	.vidioc_enum_framesizes = cafe_vidioc_enum_framesizes,
-	.vidioc_enum_frameintervals = cafe_vidioc_enum_frameintervals,
-	.vidioc_g_chip_ident    = cafe_vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-	.vidioc_g_register 	= cafe_vidioc_g_register,
-	.vidioc_s_register 	= cafe_vidioc_s_register,
-#endif
-};
-
-static struct video_device cafe_v4l_template = {
-	.name = "cafe",
-	.tvnorms = V4L2_STD_NTSC_M,
-	.current_norm = V4L2_STD_NTSC_M,  /* make mplayer happy */
-
-	.fops = &cafe_v4l_fops,
-	.ioctl_ops = &cafe_v4l_ioctl_ops,
-	.release = video_device_release_empty,
-};
-
-
-/* ---------------------------------------------------------------------- */
-/*
- * Interrupt handler stuff
- */
-
-
-
-static void cafe_frame_tasklet(unsigned long data)
-{
-	struct cafe_camera *cam = (struct cafe_camera *) data;
-	int i;
-	unsigned long flags;
-	struct cafe_sio_buffer *sbuf;
-
-	spin_lock_irqsave(&cam->dev_lock, flags);
-	for (i = 0; i < cam->nbufs; i++) {
-		int bufno = cam->next_buf;
-		if (bufno < 0) {  /* "will never happen" */
-			cam_err(cam, "No valid bufs in tasklet!\n");
-			break;
-		}
-		if (++(cam->next_buf) >= cam->nbufs)
-			cam->next_buf = 0;
-		if (! test_bit(bufno, &cam->flags))
-			continue;
-		if (list_empty(&cam->sb_avail))
-			break;  /* Leave it valid, hope for better later */
-		clear_bit(bufno, &cam->flags);
-		sbuf = list_entry(cam->sb_avail.next,
-				struct cafe_sio_buffer, list);
-		/*
-		 * Drop the lock during the big copy.  This *should* be safe...
-		 */
-		spin_unlock_irqrestore(&cam->dev_lock, flags);
-		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
-				cam->pix_format.sizeimage);
-		sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
-		sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
-		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
-		sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
-		spin_lock_irqsave(&cam->dev_lock, flags);
-		list_move_tail(&sbuf->list, &cam->sb_full);
-	}
-	if (! list_empty(&cam->sb_full))
-		wake_up(&cam->iowait);
-	spin_unlock_irqrestore(&cam->dev_lock, flags);
-}
-
-
-
-static void cafe_frame_complete(struct cafe_camera *cam, int frame)
-{
-	/*
-	 * Basic frame housekeeping.
-	 */
-	if (test_bit(frame, &cam->flags) && printk_ratelimit())
-		cam_err(cam, "Frame overrun on %d, frames lost\n", frame);
-	set_bit(frame, &cam->flags);
-	clear_bit(CF_DMA_ACTIVE, &cam->flags);
-	if (cam->next_buf < 0)
-		cam->next_buf = frame;
-	cam->buf_seq[frame] = ++(cam->sequence);
-
-	switch (cam->state) {
-	/*
-	 * If in single read mode, try going speculative.
-	 */
-	    case S_SINGLEREAD:
-		cam->state = S_SPECREAD;
-		cam->specframes = 0;
-		wake_up(&cam->iowait);
-		break;
-
-	/*
-	 * If we are already doing speculative reads, and nobody is
-	 * reading them, just stop.
-	 */
-	    case S_SPECREAD:
-		if (++(cam->specframes) >= cam->nbufs) {
-			cafe_ctlr_stop(cam);
-			cafe_ctlr_irq_disable(cam);
-			cam->state = S_IDLE;
-		}
-		wake_up(&cam->iowait);
-		break;
-	/*
-	 * For the streaming case, we defer the real work to the
-	 * camera tasklet.
-	 *
-	 * FIXME: if the application is not consuming the buffers,
-	 * we should eventually put things on hold and restart in
-	 * vidioc_dqbuf().
-	 */
-	    case S_STREAMING:
-		tasklet_schedule(&cam->s_tasklet);
-		break;
-
-	    default:
-		cam_err(cam, "Frame interrupt in non-operational state\n");
-		break;
-	}
-}
-
-
-
-
-static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs)
-{
-	unsigned int frame;
-
-	cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
-	/*
-	 * Handle any frame completions.  There really should
-	 * not be more than one of these, or we have fallen
-	 * far behind.
-	 */
-	for (frame = 0; frame < cam->nbufs; frame++)
-		if (irqs & (IRQ_EOF0 << frame))
-			cafe_frame_complete(cam, frame);
-	/*
-	 * If a frame starts, note that we have DMA active.  This
-	 * code assumes that we won't get multiple frame interrupts
-	 * at once; may want to rethink that.
-	 */
-	if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2))
-		set_bit(CF_DMA_ACTIVE, &cam->flags);
-}
-
-
-
-static irqreturn_t cafe_irq(int irq, void *data)
-{
-	struct cafe_camera *cam = data;
-	unsigned int irqs;
-
-	spin_lock(&cam->dev_lock);
-	irqs = cafe_reg_read(cam, REG_IRQSTAT);
-	if ((irqs & ALLIRQS) == 0) {
-		spin_unlock(&cam->dev_lock);
-		return IRQ_NONE;
-	}
-	if (irqs & FRAMEIRQS)
-		cafe_frame_irq(cam, irqs);
-	if (irqs & TWSIIRQS) {
-		cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS);
-		wake_up(&cam->smbus_wait);
-	}
-	spin_unlock(&cam->dev_lock);
-	return IRQ_HANDLED;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/*
- * PCI interface stuff.
- */
-
-static const struct dmi_system_id olpc_xo1_dmi[] = {
-	{
-		.matches = {
-			DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
-			DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
-			DMI_MATCH(DMI_PRODUCT_VERSION, "1"),
-		},
-	},
-	{ }
-};
-
-static int cafe_pci_probe(struct pci_dev *pdev,
-		const struct pci_device_id *id)
-{
-	int ret;
-	struct cafe_camera *cam;
-	struct ov7670_config sensor_cfg = {
-		/* This controller only does SMBUS */
-		.use_smbus = true,
-
-		/*
-		 * Exclude QCIF mode, because it only captures a tiny portion
-		 * of the sensor FOV
-		 */
-		.min_width = 320,
-		.min_height = 240,
-	};
-	struct i2c_board_info ov7670_info = {
-		.type = "ov7670",
-		.addr = 0x42,
-		.platform_data = &sensor_cfg,
-	};
-
-	/*
-	 * Start putting together one of our big camera structures.
-	 */
-	ret = -ENOMEM;
-	cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
-	if (cam == NULL)
-		goto out;
-	ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
-	if (ret)
-		goto out_free;
-
-	mutex_init(&cam->s_mutex);
-	spin_lock_init(&cam->dev_lock);
-	cam->state = S_NOTREADY;
-	cafe_set_config_needed(cam, 1);
-	init_waitqueue_head(&cam->smbus_wait);
-	init_waitqueue_head(&cam->iowait);
-	cam->pdev = pdev;
-	cam->pix_format = cafe_def_pix_format;
-	cam->mbus_code = cafe_def_mbus_code;
-	INIT_LIST_HEAD(&cam->dev_list);
-	INIT_LIST_HEAD(&cam->sb_avail);
-	INIT_LIST_HEAD(&cam->sb_full);
-	tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam);
-	/*
-	 * Get set up on the PCI bus.
-	 */
-	ret = pci_enable_device(pdev);
-	if (ret)
-		goto out_unreg;
-	pci_set_master(pdev);
-
-	ret = -EIO;
-	cam->regs = pci_iomap(pdev, 0, 0);
-	if (! cam->regs) {
-		printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
-		goto out_unreg;
-	}
-	ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
-	if (ret)
-		goto out_iounmap;
-	/*
-	 * Initialize the controller and leave it powered up.  It will
-	 * stay that way until the sensor driver shows up.
-	 */
-	cafe_ctlr_init(cam);
-	cafe_ctlr_power_up(cam);
-	/*
-	 * Set up I2C/SMBUS communications.  We have to drop the mutex here
-	 * because the sensor could attach in this call chain, leading to
-	 * unsightly deadlocks.
-	 */
-	ret = cafe_smbus_setup(cam);
-	if (ret)
-		goto out_freeirq;
-
-	/* Apply XO-1 clock speed */
-	if (dmi_check_system(olpc_xo1_dmi))
-		sensor_cfg.clock_speed = 45;
-
-	cam->sensor_addr = ov7670_info.addr;
-	cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, &cam->i2c_adapter,
-			&ov7670_info, NULL);
-	if (cam->sensor == NULL) {
-		ret = -ENODEV;
-		goto out_smbus;
-	}
-
-	ret = cafe_cam_init(cam);
-	if (ret)
-		goto out_smbus;
-
-	/*
-	 * Get the v4l2 setup done.
-	 */
-	mutex_lock(&cam->s_mutex);
-	cam->vdev = cafe_v4l_template;
-	cam->vdev.debug = 0;
-/*	cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/
-	cam->vdev.v4l2_dev = &cam->v4l2_dev;
-	ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
-	if (ret)
-		goto out_unlock;
-	video_set_drvdata(&cam->vdev, cam);
-
-	/*
-	 * If so requested, try to get our DMA buffers now.
-	 */
-	if (!alloc_bufs_at_read) {
-		if (cafe_alloc_dma_bufs(cam, 1))
-			cam_warn(cam, "Unable to alloc DMA buffers at load"
-					" will try again later.");
-	}
-
-	mutex_unlock(&cam->s_mutex);
-	return 0;
-
-out_unlock:
-	mutex_unlock(&cam->s_mutex);
-out_smbus:
-	cafe_smbus_shutdown(cam);
-out_freeirq:
-	cafe_ctlr_power_down(cam);
-	free_irq(pdev->irq, cam);
-out_iounmap:
-	pci_iounmap(pdev, cam->regs);
-out_free:
-	v4l2_device_unregister(&cam->v4l2_dev);
-out_unreg:
-	kfree(cam);
-out:
-	return ret;
-}
-
-
-/*
- * Shut down an initialized device
- */
-static void cafe_shutdown(struct cafe_camera *cam)
-{
-/* FIXME: Make sure we take care of everything here */
-	if (cam->n_sbufs > 0)
-		/* What if they are still mapped?  Shouldn't be, but... */
-		cafe_free_sio_buffers(cam);
-	cafe_ctlr_stop_dma(cam);
-	cafe_ctlr_power_down(cam);
-	cafe_smbus_shutdown(cam);
-	cafe_free_dma_bufs(cam);
-	free_irq(cam->pdev->irq, cam);
-	pci_iounmap(cam->pdev, cam->regs);
-	video_unregister_device(&cam->vdev);
-}
-
-
-static void cafe_pci_remove(struct pci_dev *pdev)
-{
-	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
-	struct cafe_camera *cam = to_cam(v4l2_dev);
-
-	if (cam == NULL) {
-		printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
-		return;
-	}
-	mutex_lock(&cam->s_mutex);
-	if (cam->users > 0)
-		cam_warn(cam, "Removing a device with users!\n");
-	cafe_shutdown(cam);
-	v4l2_device_unregister(&cam->v4l2_dev);
-	kfree(cam);
-/* No unlock - it no longer exists */
-}
-
-
-#ifdef CONFIG_PM
-/*
- * Basic power management.
- */
-static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
-	struct cafe_camera *cam = to_cam(v4l2_dev);
-	int ret;
-	enum cafe_state cstate;
-
-	ret = pci_save_state(pdev);
-	if (ret)
-		return ret;
-	cstate = cam->state; /* HACK - stop_dma sets to idle */
-	cafe_ctlr_stop_dma(cam);
-	cafe_ctlr_power_down(cam);
-	pci_disable_device(pdev);
-	cam->state = cstate;
-	return 0;
-}
-
-
-static int cafe_pci_resume(struct pci_dev *pdev)
-{
-	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
-	struct cafe_camera *cam = to_cam(v4l2_dev);
-	int ret = 0;
-
-	pci_restore_state(pdev);
-	ret = pci_enable_device(pdev);
-
-	if (ret) {
-		cam_warn(cam, "Unable to re-enable device on resume!\n");
-		return ret;
-	}
-	cafe_ctlr_init(cam);
-
-	mutex_lock(&cam->s_mutex);
-	if (cam->users > 0) {
-		cafe_ctlr_power_up(cam);
-		__cafe_cam_reset(cam);
-	} else {
-		cafe_ctlr_power_down(cam);
-	}
-	mutex_unlock(&cam->s_mutex);
-
-	set_bit(CF_CONFIG_NEEDED, &cam->flags);
-	if (cam->state == S_SPECREAD)
-		cam->state = S_IDLE;  /* Don't bother restarting */
-	else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING)
-		ret = cafe_read_setup(cam, cam->state);
-	return ret;
-}
-
-#endif  /* CONFIG_PM */
-
-
-static struct pci_device_id cafe_ids[] = {
-	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
-		     PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, cafe_ids);
-
-static struct pci_driver cafe_pci_driver = {
-	.name = "cafe1000-ccic",
-	.id_table = cafe_ids,
-	.probe = cafe_pci_probe,
-	.remove = cafe_pci_remove,
-#ifdef CONFIG_PM
-	.suspend = cafe_pci_suspend,
-	.resume = cafe_pci_resume,
-#endif
-};
-
-
-
-
-static int __init cafe_init(void)
-{
-	int ret;
-
-	printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
-			CAFE_VERSION);
-	ret = pci_register_driver(&cafe_pci_driver);
-	if (ret) {
-		printk(KERN_ERR "Unable to register cafe_ccic driver\n");
-		goto out;
-	}
-	ret = 0;
-
-  out:
-	return ret;
-}
-
-
-static void __exit cafe_exit(void)
-{
-	pci_unregister_driver(&cafe_pci_driver);
-}
-
-module_init(cafe_init);
-module_exit(cafe_exit);
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
index 6d6d184..ab25218 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -31,7 +31,6 @@
 #ifndef __CPIA2_H__
 #define __CPIA2_H__
 
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <linux/usb.h>
@@ -43,10 +42,6 @@
 /* define for verbose debug output */
 //#define _CPIA2_DEBUG_
 
-#define CPIA2_MAJ_VER	3
-#define CPIA2_MIN_VER   0
-#define CPIA2_PATCH_VER	0
-
 /***
  * Image defines
  ***/
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 40eb632..077eb1d 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -29,8 +29,7 @@
  *		Alan Cox <alan@lxorguk.ukuu.org.uk>
  ****************************************************************************/
 
-#include <linux/version.h>
-
+#define CPIA_VERSION "3.0.1"
 
 #include <linux/module.h>
 #include <linux/time.h>
@@ -80,6 +79,7 @@
 MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
 MODULE_SUPPORTED_DEVICE("video");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CPIA_VERSION);
 
 #define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
 
@@ -465,9 +465,6 @@
 	if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
 		memset(vc->bus_info,0, sizeof(vc->bus_info));
 
-	vc->version = KERNEL_VERSION(CPIA2_MAJ_VER, CPIA2_MIN_VER,
-				     CPIA2_PATCH_VER);
-
 	vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
 			   V4L2_CAP_READWRITE |
 			   V4L2_CAP_STREAMING;
@@ -1558,8 +1555,8 @@
  *****************************************************************************/
 static int __init cpia2_init(void)
 {
-	LOG("%s v%d.%d.%d\n",
-	    ABOUT, CPIA2_MAJ_VER, CPIA2_MIN_VER, CPIA2_PATCH_VER);
+	LOG("%s v%s\n",
+	    ABOUT, CPIA_VERSION);
 	check_parameters();
 	cpia2_usb_init();
 	return 0;
@@ -1579,4 +1576,3 @@
 
 module_init(cpia2_init);
 module_exit(cpia2_exit);
-
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c
index d50d69d..a1e6c2a 100644
--- a/drivers/media/video/cx18/cx18-alsa-main.c
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -192,6 +192,7 @@
 err_exit_free:
 	if (sc != NULL)
 		snd_card_free(sc);
+	kfree(cxsc);
 err_exit:
 	return ret;
 }
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 0864272..1834207 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -25,7 +25,6 @@
 #ifndef CX18_DRIVER_H
 #define CX18_DRIVER_H
 
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index e80134f..afe0a29 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -469,7 +469,6 @@
 	strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
 	snprintf(vcap->bus_info, sizeof(vcap->bus_info),
 		 "PCI:%s", pci_name(cx->pci_dev));
-	vcap->version = CX18_DRIVER_VERSION; 	    /* version */
 	vcap->capabilities = cx->v4l2_cap; 	    /* capabilities */
 	return 0;
 }
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index cd189b6..fed48b6 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -23,12 +23,6 @@
 #define CX18_VERSION_H
 
 #define CX18_DRIVER_NAME "cx18"
-#define CX18_DRIVER_VERSION_MAJOR 1
-#define CX18_DRIVER_VERSION_MINOR 5
-#define CX18_DRIVER_VERSION_PATCHLEVEL 0
-
-#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
-#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
-	CX18_DRIVER_VERSION_MINOR, CX18_DRIVER_VERSION_PATCHLEVEL)
+#define CX18_VERSION "1.5.1"
 
 #endif
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index 8d78134..53ff26e 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -355,6 +355,8 @@
 	case CX231XX_BOARD_HAUPPAUGE_EXETER:
 	case CX231XX_BOARD_HAUPPAUGE_USBLIVE2:
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
 		if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
 			while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
 						FLD_PWRDN_ENABLE_PLL)) {
@@ -1733,6 +1735,8 @@
 		break;
 	case CX231XX_BOARD_CNXT_RDE_253S:
 	case CX231XX_BOARD_CNXT_RDU_253S:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
 		func_mode = 0x01;
 		break;
 	default:
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 2270381..53dae2a 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -387,6 +387,7 @@
 		.norm = V4L2_STD_NTSC,
 		.no_alt_vanc = 1,
 		.external_av = 1,
+		.dont_use_port_3 = 1,
 		.input = {{
 			.type = CX231XX_VMUX_COMPOSITE1,
 			.vmux = CX231XX_VIN_2_1,
@@ -532,6 +533,76 @@
 			.gpio = NULL,
 		} },
 	},
+	[CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = {
+		.name = "Hauppauge WinTV USB2 FM (PAL)",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+			.type = CX231XX_VMUX_TELEVISION,
+			.vmux = CX231XX_VIN_3_1,
+			.amux = CX231XX_AMUX_VIDEO,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		} },
+	},
+	[CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = {
+		.name = "Hauppauge WinTV USB2 FM (NTSC)",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.norm = V4L2_STD_NTSC,
+
+		.input = {{
+			.type = CX231XX_VMUX_TELEVISION,
+			.vmux = CX231XX_VIN_3_1,
+			.amux = CX231XX_AMUX_VIDEO,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		} },
+	},
 };
 const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
 
@@ -553,6 +624,10 @@
 	 .driver_info = CX231XX_BOARD_CNXT_RDE_250},
 	{USB_DEVICE(0x0572, 0x58A0),
 	 .driver_info = CX231XX_BOARD_CNXT_RDU_250},
+	{USB_DEVICE(0x2040, 0xb110),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL},
+	{USB_DEVICE(0x2040, 0xb111),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
 	{USB_DEVICE(0x2040, 0xb120),
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
 	{USB_DEVICE(0x2040, 0xb140),
@@ -1051,6 +1126,9 @@
 	if (assoc_desc->bFirstInterface != ifnum) {
 		cx231xx_err(DRIVER_NAME ": Not found "
 			    "matching IAD interface\n");
+		cx231xx_devused &= ~(1 << nr);
+		kfree(dev);
+		dev = NULL;
 		return -ENODEV;
 	}
 
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index abe500f..d4457f9 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -742,6 +742,8 @@
 		case CX231XX_BOARD_CNXT_RDU_253S:
 		case CX231XX_BOARD_HAUPPAUGE_EXETER:
 		case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+		case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+		case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
 		errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
 			break;
 		default:
@@ -1381,6 +1383,8 @@
 	case CX231XX_BOARD_CNXT_RDU_253S:
 	case CX231XX_BOARD_HAUPPAUGE_EXETER:
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
 	errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
 		break;
 	default:
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index a69c24d..6e81f97 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -29,7 +29,6 @@
 #include <linux/bitmap.h>
 #include <linux/usb.h>
 #include <linux/i2c.h>
-#include <linux/version.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -45,7 +44,7 @@
 #include "cx231xx.h"
 #include "cx231xx-vbi.h"
 
-#define CX231XX_VERSION_CODE            KERNEL_VERSION(0, 0, 1)
+#define CX231XX_VERSION "0.0.2"
 
 #define DRIVER_AUTHOR   "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
 #define DRIVER_DESC     "Conexant cx231xx based USB video device driver"
@@ -70,6 +69,7 @@
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX231XX_VERSION);
 
 static unsigned int card[]     = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
 static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
@@ -1179,7 +1179,8 @@
 {
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
-	unsigned int n;
+	u32 gen_stat;
+	unsigned int ret, n;
 
 	n = i->index;
 	if (n >= MAX_CX231XX_INPUT)
@@ -1198,6 +1199,18 @@
 
 	i->std = dev->vdev->tvnorms;
 
+	/* If they are asking about the active input, read signal status */
+	if (n == dev->video_input) {
+		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+					    GEN_STAT, 2, &gen_stat, 4);
+		if (ret > 0) {
+			if ((gen_stat & FLD_VPRES) == 0x00)
+				i->status |= V4L2_IN_ST_NO_SIGNAL;
+			if ((gen_stat & FLD_HLOCK) == 0x00)
+				i->status |= V4L2_IN_ST_NO_H_LOCK;
+		}
+	}
+
 	return 0;
 }
 
@@ -1869,8 +1882,6 @@
 	strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
-	cap->version = CX231XX_VERSION_CODE;
-
 	cap->capabilities = V4L2_CAP_VBI_CAPTURE |
 #if 0
 		V4L2_CAP_SLICED_VBI_CAPTURE |
@@ -2057,7 +2068,6 @@
 	strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
-	cap->version = CX231XX_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
@@ -2570,11 +2580,8 @@
 {
 	int ret;
 
-	cx231xx_info("%s: v4l2 driver version %d.%d.%d\n",
-		     dev->name,
-		     (CX231XX_VERSION_CODE >> 16) & 0xff,
-		     (CX231XX_VERSION_CODE >> 8) & 0xff,
-		     CX231XX_VERSION_CODE & 0xff);
+	cx231xx_info("%s: v4l2 driver version %s\n",
+		     dev->name, CX231XX_VERSION);
 
 	/* set default norm */
 	/*dev->norm = cx231xx_video_template.current_norm; */
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index 46dd840..2000bc6 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -43,7 +43,7 @@
 #include "cx231xx-conf-reg.h"
 
 #define DRIVER_NAME                     "cx231xx"
-#define PWR_SLEEP_INTERVAL              5
+#define PWR_SLEEP_INTERVAL              10
 
 /* I2C addresses for control block in Cx231xx */
 #define     AFE_DEVICE_ADDRESS		0x60
@@ -67,6 +67,8 @@
 #define CX231XX_BOARD_PV_XCAPTURE_USB 11
 #define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12
 #define CX231XX_BOARD_ICONBIT_U100 13
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15
 
 /* Limits minimum and default number of buffers */
 #define CX231XX_MIN_BUF                 4
@@ -112,7 +114,6 @@
 	V4L2_STD_PAL_BG |  V4L2_STD_PAL_DK    |  V4L2_STD_PAL_I    | \
 	V4L2_STD_PAL_M  |  V4L2_STD_PAL_N     |  V4L2_STD_PAL_Nc   | \
 	V4L2_STD_PAL_60 |  V4L2_STD_SECAM_L   |  V4L2_STD_SECAM_DK)
-#define CX231xx_VERSION_CODE KERNEL_VERSION(0, 0, 2)
 
 #define SLEEP_S5H1432    30
 #define CX23417_OSC_EN   8
diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c
index 678539b..1fa8927 100644
--- a/drivers/media/video/cx23885/altera-ci.c
+++ b/drivers/media/video/cx23885/altera-ci.c
@@ -52,7 +52,6 @@
  * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
  * +-------+-------+-------+-------+-------+-------+-------+-------+
  */
-#include <linux/version.h>
 #include <media/videobuf-dma-sg.h>
 #include <media/videobuf-dvb.h>
 #include "altera-ci.h"
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 9a98dc5..67c4a59 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -1359,7 +1359,6 @@
 	strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
 		sizeof(cap->card));
 	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-	cap->version = CX23885_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_READWRITE     |
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 934185c..76b7563 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -29,11 +29,17 @@
 #include "../../../staging/altera-stapl/altera.h"
 #include "cx23885.h"
 #include "tuner-xc2028.h"
+#include "netup-eeprom.h"
 #include "netup-init.h"
 #include "altera-ci.h"
+#include "xc4000.h"
 #include "xc5000.h"
 #include "cx23888-ir.h"
 
+static unsigned int netup_card_rev = 1;
+module_param(netup_card_rev, int, 0644);
+MODULE_PARM_DESC(netup_card_rev,
+		"NetUP Dual DVB-T/C CI card revision");
 static unsigned int enable_885_ir;
 module_param(enable_885_ir, int, 0644);
 MODULE_PARM_DESC(enable_885_ir,
@@ -175,6 +181,34 @@
 		.name		= "Leadtek Winfast PxDVR3200 H",
 		.portc		= CX23885_MPEG_DVB,
 	},
+	[CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
+		.name		= "Leadtek Winfast PxDVR3200 H XC4000",
+		.porta		= CX23885_ANALOG_VIDEO,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_XC4000,
+		.tuner_addr	= 0x61,
+		.radio_type	= TUNER_XC4000,
+		.radio_addr	= 0x61,
+		.input		= {{
+			.type	= CX23885_VMUX_TELEVISION,
+			.vmux	= CX25840_VIN2_CH1 |
+				  CX25840_VIN5_CH2 |
+				  CX25840_NONE0_CH3,
+		}, {
+			.type	= CX23885_VMUX_COMPOSITE1,
+			.vmux	= CX25840_COMPOSITE1,
+		}, {
+			.type	= CX23885_VMUX_SVIDEO,
+			.vmux	= CX25840_SVIDEO_LUMA3 |
+				  CX25840_SVIDEO_CHROMA4,
+		}, {
+			.type	= CX23885_VMUX_COMPONENT,
+			.vmux	= CX25840_VIN7_CH1 |
+				  CX25840_VIN6_CH2 |
+				  CX25840_VIN8_CH3 |
+				  CX25840_COMPONENT_ON,
+		} },
+	},
 	[CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
 		.name		= "Compro VideoMate E650F",
 		.portc		= CX23885_MPEG_DVB,
@@ -433,6 +467,10 @@
 		.subdevice = 0x6681,
 		.card      = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
 	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f39,
+		.card	   = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
+	}, {
 		.subvendor = 0x185b,
 		.subdevice = 0xe800,
 		.card      = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
@@ -749,6 +787,7 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
@@ -909,6 +948,7 @@
 		cx_set(GP0_IO, 0x000f000f);
 		break;
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
@@ -1097,12 +1137,19 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1800:
 	case CX23885_BOARD_HAUPPAUGE_HVR1200:
 	case CX23885_BOARD_HAUPPAUGE_HVR1400:
-	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1275:
 	case CX23885_BOARD_HAUPPAUGE_HVR1255:
 	case CX23885_BOARD_HAUPPAUGE_HVR1210:
 		/* FIXME: Implement me */
 		break;
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
+		ret = cx23888_ir_probe(dev);
+		if (ret)
+			break;
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 		ret = cx23888_ir_probe(dev);
@@ -1156,6 +1203,7 @@
 void cx23885_ir_fini(struct cx23885_dev *dev)
 {
 	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 		cx23885_irq_remove(dev, PCI_MSK_IR);
@@ -1199,6 +1247,7 @@
 void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
 {
 	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 		if (dev->sd_ir)
@@ -1325,6 +1374,7 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1700:
 	case CX23885_BOARD_HAUPPAUGE_HVR1400:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
 	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1275:
@@ -1353,10 +1403,12 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
 	case CX23885_BOARD_HAUPPAUGE_HVR1700:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
 	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_MYGICA_X8506:
 	case CX23885_BOARD_MAGICPRO_PROHDTVE2:
@@ -1383,6 +1435,7 @@
 		const struct firmware *fw;
 		const char *filename = "dvb-netup-altera-01.fw";
 		char *action = "configure";
+		static struct netup_card_info cinfo;
 		struct altera_config netup_config = {
 			.dev = dev,
 			.action = action,
@@ -1391,6 +1444,21 @@
 
 		netup_initialize(dev);
 
+		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+		if (netup_card_rev)
+			cinfo.rev = netup_card_rev;
+
+		switch (cinfo.rev) {
+		case 0x4:
+			filename = "dvb-netup-altera-04.fw";
+			break;
+		default:
+			filename = "dvb-netup-altera-01.fw";
+			break;
+		}
+		printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
+				cinfo.rev, filename);
+
 		ret = request_firmware(&fw, filename, &dev->pci->dev);
 		if (ret != 0)
 			printk(KERN_ERR "did not find the firmware file. (%s) "
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 419777a..ee41a88 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -42,6 +42,7 @@
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX23885_VERSION);
 
 static unsigned int debug;
 module_param(debug, int, 0644);
@@ -2147,14 +2148,8 @@
 
 static int __init cx23885_init(void)
 {
-	printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n",
-	       (CX23885_VERSION_CODE >> 16) & 0xff,
-	       (CX23885_VERSION_CODE >>  8) & 0xff,
-	       CX23885_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx23885: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx23885 driver version %s loaded\n",
+		CX23885_VERSION);
 	return pci_register_driver(&cx23885_pci_driver);
 }
 
@@ -2165,5 +2160,3 @@
 
 module_init(cx23885_init);
 module_exit(cx23885_fini);
-
-/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 3c315f9..aa83f07 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -37,6 +37,7 @@
 #include "tda8290.h"
 #include "tda18271.h"
 #include "lgdt330x.h"
+#include "xc4000.h"
 #include "xc5000.h"
 #include "max2165.h"
 #include "tda10048.h"
@@ -921,6 +922,26 @@
 				fe->ops.tuner_ops.set_config(fe, &ctl);
 		}
 		break;
+	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+		i2c_bus = &dev->i2c_bus[0];
+
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &dvico_fusionhdtv_xc3028,
+					       &i2c_bus->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			struct dvb_frontend	*fe;
+			struct xc4000_config	cfg = {
+				.i2c_address	  = 0x61,
+				.default_pm	  = 0,
+				.dvb_amplitude	  = 134,
+				.set_smoothedcvbs = 1,
+				.if_khz		  = 4560
+			};
+
+			fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
+					&dev->i2c_bus[1].i2c_adap, &cfg);
+		}
+		break;
 	case CX23885_BOARD_TBS_6920:
 		i2c_bus = &dev->i2c_bus[1];
 
@@ -1249,7 +1270,7 @@
 	 * implement MFE support.
 	 */
 	fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
-	if (fe0->dvb.frontend)
+	if (fe0 && fe0->dvb.frontend)
 		videobuf_dvb_unregister_bus(&port->frontends);
 
 	switch (port->dev->board) {
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index e97cafd..ce765e3 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -82,6 +82,7 @@
 		return;
 
 	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_TEVII_S470:
@@ -133,6 +134,7 @@
 
 	v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
 	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -229,6 +231,9 @@
 		v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
 		v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
 	}
+	flush_work_sync(&dev->cx25840_work);
+	flush_work_sync(&dev->ir_rx_work);
+	flush_work_sync(&dev->ir_tx_work);
 }
 
 static void cx23885_input_ir_close(struct rc_dev *rc)
@@ -257,6 +262,7 @@
 		return -ENODEV;
 
 	switch (dev->board) {
+	case CX23885_BOARD_HAUPPAUGE_HVR1270:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index ee57f6b..896bb32 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -1000,7 +1000,6 @@
 	strlcpy(cap->card, cx23885_boards[dev->board].name,
 		sizeof(cap->card));
 	sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
-	cap->version = CX23885_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_READWRITE     |
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index c186473..d86bc0b 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -36,10 +36,9 @@
 #include "cx23885-reg.h"
 #include "media/cx2341x.h"
 
-#include <linux/version.h>
 #include <linux/mutex.h>
 
-#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 2)
+#define CX23885_VERSION "0.0.3"
 
 #define UNSET (-1U)
 
@@ -86,6 +85,7 @@
 #define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
 #define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID     29
 #define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 423c1af..68d1240 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -113,6 +113,8 @@
 MODULE_AUTHOR("Ricardo Cerqueira");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
 MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
 			"{{Conexant,23882},"
 			"{{Conexant,23883}");
@@ -973,14 +975,8 @@
  */
 static int __init cx88_audio_init(void)
 {
-	printk(KERN_INFO "cx2388x alsa driver version %d.%d.%d loaded\n",
-	       (CX88_VERSION_CODE >> 16) & 0xff,
-	       (CX88_VERSION_CODE >>  8) & 0xff,
-	       CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
+	       CX88_VERSION);
 	return pci_register_driver(&cx88_audio_pci_driver);
 }
 
@@ -994,10 +990,3 @@
 
 module_init(cx88_audio_init);
 module_exit(cx88_audio_fini);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 11e49bb..e46446a 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -42,6 +42,7 @@
 MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
 MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
 
 static unsigned int mpegbufs = 32;
 module_param(mpegbufs,int,0644);
@@ -730,7 +731,6 @@
 	strcpy(cap->driver, "cx88_blackbird");
 	strlcpy(cap->card, core->board.name, sizeof(cap->card));
 	sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
-	cap->version = CX88_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_READWRITE     |
@@ -1368,14 +1368,8 @@
 
 static int __init blackbird_init(void)
 {
-	printk(KERN_INFO "cx2388x blackbird driver version %d.%d.%d loaded\n",
-	       (CX88_VERSION_CODE >> 16) & 0xff,
-	       (CX88_VERSION_CODE >>  8) & 0xff,
-	       CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
+	       CX88_VERSION);
 	return cx8802_register_driver(&cx8802_blackbird_driver);
 }
 
@@ -1389,11 +1383,3 @@
 
 module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
 MODULE_PARM_DESC(debug,"enable debug messages [video]");
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 27222c9..0d719fa 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -28,6 +28,7 @@
 
 #include "cx88.h"
 #include "tea5767.h"
+#include "xc4000.h"
 
 static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
 static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
@@ -2119,6 +2120,99 @@
 		},
 		.mpeg           = CX88_MPEG_DVB,
 	},
+	[CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
+		.name		= "Leadtek WinFast DTV1800 H (XC4000)",
+		.tuner_type	= TUNER_XC4000,
+		.radio_type	= TUNER_XC4000,
+		.tuner_addr	= 0x61,
+		.radio_addr	= 0x61,
+		/*
+		 * GPIO setting
+		 *
+		 *  2: mute (0=off,1=on)
+		 * 12: tuner reset pin
+		 * 13: audio source (0=tuner audio,1=line in)
+		 * 14: FM (0=on,1=off ???)
+		 */
+		.input		= {{
+			.type	= CX88_VMUX_TELEVISION,
+			.vmux	= 0,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6040,	/* pin 13 = 0, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6060,	/* pin 13 = 1, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6060,	/* pin 13 = 1, pin 14 = 1 */
+			.gpio2	= 0x0000,
+		}},
+		.radio = {
+			.type	= CX88_RADIO,
+			.gpio0	= 0x0400,	/* pin 2 = 0 */
+			.gpio1	= 0x6000,	/* pin 13 = 0, pin 14 = 0 */
+			.gpio2	= 0x0000,
+		},
+		.mpeg		= CX88_MPEG_DVB,
+	},
+	[CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
+		.name		= "Leadtek WinFast DTV2000 H PLUS",
+		.tuner_type	= TUNER_XC4000,
+		.radio_type	= TUNER_XC4000,
+		.tuner_addr	= 0x61,
+		.radio_addr	= 0x61,
+		/*
+		 * GPIO
+		 *   2: 1: mute audio
+		 *  12: 0: reset XC4000
+		 *  13: 1: audio input is line in (0: tuner)
+		 *  14: 0: FM radio
+		 *  16: 0: RF input is cable
+		 */
+		.input		= {{
+			.type	= CX88_VMUX_TELEVISION,
+			.vmux	= 0,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF0D7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_CABLE,
+			.vmux	= 0,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF0D7,
+			.gpio2	= 0x0100,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_COMPOSITE1,
+			.vmux	= 1,
+			.gpio0	= 0x0403,	/* was 0x0407 */
+			.gpio1	= 0xF0F7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}, {
+			.type	= CX88_VMUX_SVIDEO,
+			.vmux	= 2,
+			.gpio0	= 0x0403,	/* was 0x0407 */
+			.gpio1	= 0xF0F7,
+			.gpio2	= 0x0101,
+			.gpio3	= 0x0000,
+		}},
+		.radio = {
+			.type	= CX88_RADIO,
+			.gpio0	= 0x0403,
+			.gpio1	= 0xF097,
+			.gpio2	= 0x0100,
+			.gpio3	= 0x0000,
+		},
+		.mpeg		= CX88_MPEG_DVB,
+	},
 	[CX88_BOARD_PROF_7301] = {
 		.name           = "Prof 7301 DVB-S/S2",
 		.tuner_type     = UNSET,
@@ -2581,6 +2675,15 @@
 		.subdevice = 0x6654,
 		.card      = CX88_BOARD_WINFAST_DTV1800H,
 	}, {
+		/* WinFast DTV1800 H with XC4000 tuner */
+		.subvendor = 0x107d,
+		.subdevice = 0x6f38,
+		.card      = CX88_BOARD_WINFAST_DTV1800H_XC4000,
+	}, {
+		.subvendor = 0x107d,
+		.subdevice = 0x6f42,
+		.card      = CX88_BOARD_WINFAST_DTV2000H_PLUS,
+	}, {
 		/* PVR2000 PAL Model [107d:6630] */
 		.subvendor = 0x107d,
 		.subdevice = 0x6630,
@@ -2846,6 +2949,23 @@
 	return -EINVAL;
 }
 
+static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
+						  int command, int arg)
+{
+	switch (command) {
+	case XC4000_TUNER_RESET:
+		/* GPIO 12 (xc4000 tuner reset) */
+		cx_set(MO_GP1_IO, 0x1010);
+		mdelay(50);
+		cx_clear(MO_GP1_IO, 0x10);
+		mdelay(75);
+		cx_set(MO_GP1_IO, 0x10);
+		mdelay(75);
+		return 0;
+	}
+	return -EINVAL;
+}
+
 /* ------------------------------------------------------------------- */
 /* some Divco specific stuff                                           */
 static int cx88_pv_8000gt_callback(struct cx88_core *core,
@@ -2948,6 +3068,19 @@
 	return -EINVAL;
 }
 
+static int cx88_xc4000_tuner_callback(struct cx88_core *core,
+				      int command, int arg)
+{
+	/* Board-specific callbacks */
+	switch (core->boardnr) {
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		return cx88_xc4000_winfast2000h_plus_callback(core,
+							      command, arg);
+	}
+	return -EINVAL;
+}
+
 /* ----------------------------------------------------------------------- */
 /* Tuner callback function. Currently only needed for the Pinnacle 	   *
  * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both	   *
@@ -3022,6 +3155,9 @@
 		case TUNER_XC2028:
 			info_printk(core, "Calling XC2028/3028 callback\n");
 			return cx88_xc2028_tuner_callback(core, command, arg);
+		case TUNER_XC4000:
+			info_printk(core, "Calling XC4000 callback\n");
+			return cx88_xc4000_tuner_callback(core, command, arg);
 		case TUNER_XC5000:
 			info_printk(core, "Calling XC5000 callback\n");
 			return cx88_xc5000_tuner_callback(core, command, arg);
@@ -3109,13 +3245,13 @@
 
 	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
 	case CX88_BOARD_WINFAST_DTV1800H:
-		/* GPIO 12 (xc3028 tuner reset) */
-		cx_set(MO_GP1_IO, 0x1010);
-		mdelay(50);
-		cx_clear(MO_GP1_IO, 0x10);
-		mdelay(50);
-		cx_set(MO_GP1_IO, 0x10);
-		mdelay(50);
+		cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
+		break;
+
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		cx88_xc4000_winfast2000h_plus_callback(core,
+						       XC4000_TUNER_RESET, 0);
 		break;
 
 	case CX88_BOARD_TWINHAN_VP1027_DVBS:
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 2e145f0..fbcaa1c 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -636,6 +636,9 @@
 	cx_write(MO_PCI_INTSTAT,   0xFFFFFFFF); // Clear PCI int
 	cx_write(MO_INT1_STAT,     0xFFFFFFFF); // Clear RISC int
 
+	/* set default notch filter */
+	cx_andor(MO_HTOTAL, 0x1800, (HLNotchFilter4xFsc << 11));
+
 	/* Reset on-board parts */
 	cx_write(MO_SRST_IO, 0);
 	msleep(10);
@@ -759,8 +762,8 @@
 	if (nocomb)
 		value |= (3 << 5); // disable comb filter
 
-	cx_write(MO_FILTER_EVEN,  value);
-	cx_write(MO_FILTER_ODD,   value);
+	cx_andor(MO_FILTER_EVEN,  0x7ffc7f, value); /* preserve PEAKEN, PSEL */
+	cx_andor(MO_FILTER_ODD,   0x7ffc7f, value);
 	dprintk(1,"set_scale: filter  0x%04x\n", value);
 
 	return 0;
@@ -994,10 +997,10 @@
 	// htotal
 	tmp64 = norm_htotal(norm) * (u64)vdec_clock;
 	do_div(tmp64, fsc8);
-	htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
+	htotal = (u32)tmp64;
 	dprintk(1,"set_tvnorm: MO_HTOTAL        0x%08x [old=0x%08x,htotal=%d]\n",
 		htotal, cx_read(MO_HTOTAL), (u32)tmp64);
-	cx_write(MO_HTOTAL, htotal);
+	cx_andor(MO_HTOTAL, 0x07ff, htotal);
 
 	// vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
 	// the effective vbi offset ~244 samples, the same as the Bt8x8
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index c69df7e..cf3d33a 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -41,6 +41,7 @@
 #include "or51132.h"
 #include "lgdt330x.h"
 #include "s5h1409.h"
+#include "xc4000.h"
 #include "xc5000.h"
 #include "nxt200x.h"
 #include "cx24123.h"
@@ -63,6 +64,7 @@
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
 
 static unsigned int debug;
 module_param(debug, int, 0644);
@@ -605,6 +607,39 @@
 	return 0;
 }
 
+static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
+{
+	struct dvb_frontend *fe;
+	struct videobuf_dvb_frontend *fe0 = NULL;
+
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	if (!fe0->dvb.frontend) {
+		printk(KERN_ERR "%s/2: dvb frontend not attached. "
+				"Can't attach xc4000\n",
+		       dev->core->name);
+		return -EINVAL;
+	}
+
+	fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
+			cfg);
+	if (!fe) {
+		printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+		       dev->core->name);
+		dvb_frontend_detach(fe0->dvb.frontend);
+		dvb_unregister_frontend(fe0->dvb.frontend);
+		fe0->dvb.frontend = NULL;
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
+
+	return 0;
+}
+
 static int cx24116_set_ts_param(struct dvb_frontend *fe,
 	int is_punctured)
 {
@@ -1294,7 +1329,25 @@
 				goto frontend_detach;
 		}
 		break;
-	 case CX88_BOARD_GENIATECH_X8000_MT:
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+					       &cx88_pinnacle_hybrid_pctv,
+					       &core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			struct xc4000_config cfg = {
+				.i2c_address	  = 0x61,
+				.default_pm	  = 0,
+				.dvb_amplitude	  = 134,
+				.set_smoothedcvbs = 1,
+				.if_khz		  = 4560
+			};
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+			if (attach_xc4000(dev, &cfg) < 0)
+				goto frontend_detach;
+		}
+		break;
+	case CX88_BOARD_GENIATECH_X8000_MT:
 		dev->ts_gen_cntrl = 0x00;
 
 		fe0->dvb.frontend = dvb_attach(zl10353_attach,
@@ -1577,6 +1630,11 @@
 		udelay(1000);
 		break;
 
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+		/* set RF input to AIR for DVB-T (GPIO 16) */
+		cx_write(MO_GP2_IO, 0x0101);
+		break;
+
 	default:
 		err = -ENODEV;
 	}
@@ -1692,14 +1750,8 @@
 
 static int __init dvb_init(void)
 {
-	printk(KERN_INFO "cx88/2: cx2388x dvb driver version %d.%d.%d loaded\n",
-	       (CX88_VERSION_CODE >> 16) & 0xff,
-	       (CX88_VERSION_CODE >>  8) & 0xff,
-	       CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
+	       CX88_VERSION);
 	return cx8802_register_driver(&cx8802_dvb_driver);
 }
 
@@ -1710,10 +1762,3 @@
 
 module_init(dvb_init);
 module_exit(dvb_fini);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * compile-command: "make DVB=1"
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 3f44200..e614201 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -100,6 +100,8 @@
 		break;
 	case CX88_BOARD_WINFAST_DTV1000:
 	case CX88_BOARD_WINFAST_DTV1800H:
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
 	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
 		gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900);
 		auxgpio = gpio;
@@ -289,6 +291,8 @@
 	case CX88_BOARD_WINFAST_DTV2000H:
 	case CX88_BOARD_WINFAST_DTV2000H_J:
 	case CX88_BOARD_WINFAST_DTV1800H:
+	case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+	case CX88_BOARD_WINFAST_DTV2000H_PLUS:
 		ir_codes = RC_MAP_WINFAST;
 		ir->gpio_addr = MO_GP0_IO;
 		ir->mask_keycode = 0x8f8;
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 1a7b983..cd5386e 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -39,6 +39,7 @@
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
 
 static unsigned int debug;
 module_param(debug,int,0644);
@@ -613,13 +614,17 @@
 	    core->active_type_id != drv->type_id)
 		return -EBUSY;
 
-	core->input = 0;
-	for (i = 0;
-	     i < (sizeof(core->board.input) / sizeof(struct cx88_input));
-	     i++) {
-		if (core->board.input[i].type == CX88_VMUX_DVB) {
-			core->input = i;
-			break;
+	if (drv->type_id == CX88_MPEG_DVB) {
+		/* When switching to DVB, always set the input to the tuner */
+		core->last_analog_input = core->input;
+		core->input = 0;
+		for (i = 0;
+		     i < (sizeof(core->board.input) / sizeof(struct cx88_input));
+		     i++) {
+			if (core->board.input[i].type == CX88_VMUX_DVB) {
+				core->input = i;
+				break;
+			}
 		}
 	}
 
@@ -644,6 +649,12 @@
 
 	if (drv->advise_release && --core->active_ref == 0)
 	{
+		if (drv->type_id == CX88_MPEG_DVB) {
+			/* If the DVB driver is releasing, reset the input
+			   state to the last configured analog input */
+			core->input = core->last_analog_input;
+		}
+
 		drv->advise_release(drv);
 		core->active_type_id = CX88_BOARD_NONE;
 		mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
@@ -890,14 +901,8 @@
 
 static int __init cx8802_init(void)
 {
-	printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %d.%d.%d loaded\n",
-	       (CX88_VERSION_CODE >> 16) & 0xff,
-	       (CX88_VERSION_CODE >>  8) & 0xff,
-	       CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
+	       CX88_VERSION);
 	return pci_register_driver(&cx8802_pci_driver);
 }
 
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index cef4f28..60d28fd 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -45,6 +45,7 @@
 MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
 
 /* ------------------------------------------------------------------ */
 
@@ -220,7 +221,23 @@
 		.reg                   = MO_UV_SATURATION,
 		.mask                  = 0x00ff,
 		.shift                 = 0,
-	},{
+	}, {
+		.v = {
+			.id            = V4L2_CID_SHARPNESS,
+			.name          = "Sharpness",
+			.minimum       = 0,
+			.maximum       = 4,
+			.step          = 1,
+			.default_value = 0x0,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.off                   = 0,
+		/* NOTE: the value is converted and written to both even
+		   and odd registers in the code */
+		.reg                   = MO_FILTER_ODD,
+		.mask                  = 7 << 7,
+		.shift                 = 7,
+	}, {
 		.v = {
 			.id            = V4L2_CID_CHROMA_AGC,
 			.name          = "Chroma AGC",
@@ -245,6 +262,20 @@
 		.mask                  = 1 << 9,
 		.shift                 = 9,
 	}, {
+		.v = {
+			.id            = V4L2_CID_BAND_STOP_FILTER,
+			.name          = "Notch filter",
+			.minimum       = 0,
+			.maximum       = 3,
+			.step          = 1,
+			.default_value = 0x0,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.off                   = 0,
+		.reg                   = MO_HTOTAL,
+		.mask                  = 3 << 11,
+		.shift                 = 11,
+	}, {
 	/* --- audio --- */
 		.v = {
 			.id            = V4L2_CID_AUDIO_MUTE,
@@ -300,8 +331,10 @@
 	V4L2_CID_AUDIO_VOLUME,
 	V4L2_CID_AUDIO_BALANCE,
 	V4L2_CID_AUDIO_MUTE,
+	V4L2_CID_SHARPNESS,
 	V4L2_CID_CHROMA_AGC,
 	V4L2_CID_COLOR_KILLER,
+	V4L2_CID_BAND_STOP_FILTER,
 	0
 };
 EXPORT_SYMBOL(cx88_user_ctrls);
@@ -962,6 +995,10 @@
 	case V4L2_CID_AUDIO_VOLUME:
 		ctl->value = 0x3f - (value & 0x3f);
 		break;
+	case V4L2_CID_SHARPNESS:
+		ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
+						 : 0);
+		break;
 	default:
 		ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
 		break;
@@ -1039,6 +1076,12 @@
 		}
 		mask=0xffff;
 		break;
+	case V4L2_CID_SHARPNESS:
+		/* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+		value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
+		/* needs to be set for both fields */
+		cx_andor(MO_FILTER_EVEN, mask, value);
+		break;
 	case V4L2_CID_CHROMA_AGC:
 		/* Do not allow chroma AGC to be enabled for SECAM */
 		value = ((ctl->value - c->off) << c->shift) & c->mask;
@@ -1161,7 +1204,6 @@
 	strcpy(cap->driver, "cx8800");
 	strlcpy(cap->card, core->board.name, sizeof(cap->card));
 	sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
-	cap->version = CX88_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_READWRITE     |
@@ -1480,7 +1522,6 @@
 	strcpy(cap->driver, "cx8800");
 	strlcpy(cap->card, core->board.name, sizeof(cap->card));
 	sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
-	cap->version = CX88_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
@@ -2139,14 +2180,8 @@
 
 static int __init cx8800_init(void)
 {
-	printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %d.%d.%d loaded\n",
-	       (CX88_VERSION_CODE >> 16) & 0xff,
-	       (CX88_VERSION_CODE >>  8) & 0xff,
-	       CX88_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
+	       CX88_VERSION);
 	return pci_register_driver(&cx8800_pci_driver);
 }
 
@@ -2157,11 +2192,3 @@
 
 module_init(cx8800_init);
 module_exit(cx8800_fini);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index a399a8b..fa8d307 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -39,9 +39,9 @@
 #include "cx88-reg.h"
 #include "tuner-xc2028.h"
 
-#include <linux/version.h>
 #include <linux/mutex.h>
-#define CX88_VERSION_CODE KERNEL_VERSION(0, 0, 8)
+
+#define CX88_VERSION "0.0.9"
 
 #define UNSET (-1U)
 
@@ -242,6 +242,8 @@
 #define CX88_BOARD_SAMSUNG_SMT_7020        84
 #define CX88_BOARD_TWINHAN_VP1027_DVBS     85
 #define CX88_BOARD_TEVII_S464              86
+#define CX88_BOARD_WINFAST_DTV2000H_PLUS   87
+#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
@@ -375,6 +377,7 @@
 	u32                        audiomode_manual;
 	u32                        audiomode_current;
 	u32                        input;
+	u32                        last_analog_input;
 	u32                        astat;
 	u32			   use_nicam;
 	unsigned long		   last_change;
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
index 6b19540..60a456e 100644
--- a/drivers/media/video/davinci/Kconfig
+++ b/drivers/media/video/davinci/Kconfig
@@ -91,3 +91,26 @@
 
 	   To compile this driver as a module, choose M here: the
 	   module will be called vpfe.
+
+config VIDEO_DM644X_VPBE
+	tristate "DM644X VPBE HW module"
+	depends on ARCH_DAVINCI_DM644x
+	select VIDEO_VPSS_SYSTEM
+	select VIDEOBUF_DMA_CONTIG
+	help
+	    Enables VPBE modules used for display on a DM644x
+	    SoC.
+
+	    To compile this driver as a module, choose M here: the
+	    module will be called vpbe.
+
+
+config VIDEO_VPBE_DISPLAY
+	tristate "VPBE V4L2 Display driver"
+	depends on ARCH_DAVINCI_DM644x
+	select VIDEO_DM644X_VPBE
+	help
+	    Enables VPBE V4L2 Display driver on a DM644x device
+
+	    To compile this driver as a module, choose M here: the
+	    module will be called vpbe_display.
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile
index a379557..ae7dafb 100644
--- a/drivers/media/video/davinci/Makefile
+++ b/drivers/media/video/davinci/Makefile
@@ -16,3 +16,5 @@
 obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
 obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o
 obj-$(CONFIG_VIDEO_ISIF) += isif.o
+obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o
+obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o
diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c
new file mode 100644
index 0000000..d773d30
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe.c
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpss.h>
+#include <media/davinci/vpbe_venc.h>
+
+#define VPBE_DEFAULT_OUTPUT	"Composite"
+#define VPBE_DEFAULT_MODE	"ntsc"
+
+static char *def_output = VPBE_DEFAULT_OUTPUT;
+static char *def_mode = VPBE_DEFAULT_MODE;
+static int debug;
+
+module_param(def_output, charp, S_IRUGO);
+module_param(def_mode, charp, S_IRUGO);
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)");
+MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/**
+ * vpbe_current_encoder_info - Get config info for current encoder
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Return ptr to current encoder config info
+ */
+static struct encoder_config_info*
+vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int index = vpbe_dev->current_sd_index;
+
+	return ((index == 0) ? &cfg->venc :
+				&cfg->ext_encoders[index-1]);
+}
+
+/**
+ * vpbe_find_encoder_sd_index - Given a name find encoder sd index
+ *
+ * @vpbe_config - ptr to vpbe cfg
+ * @output_index - index used by application
+ *
+ * Return sd index of the encoder
+ */
+static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
+			     int index)
+{
+	char *encoder_name = cfg->outputs[index].subdev_name;
+	int i;
+
+	/* Venc is always first	*/
+	if (!strcmp(encoder_name, cfg->venc.module_name))
+		return 0;
+
+	for (i = 0; i < cfg->num_ext_encoders; i++) {
+		if (!strcmp(encoder_name,
+		     cfg->ext_encoders[i].module_name))
+			return i+1;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vpbe_g_cropcap - Get crop capabilities of the display
+ * @vpbe_dev - vpbe device ptr
+ * @cropcap - cropcap is a ptr to struct v4l2_cropcap
+ *
+ * Update the crop capabilities in crop cap for current
+ * mode
+ */
+static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev,
+			  struct v4l2_cropcap *cropcap)
+{
+	if (NULL == cropcap)
+		return -EINVAL;
+	cropcap->bounds.left = 0;
+	cropcap->bounds.top = 0;
+	cropcap->bounds.width = vpbe_dev->current_timings.xres;
+	cropcap->bounds.height = vpbe_dev->current_timings.yres;
+	cropcap->defrect = cropcap->bounds;
+
+	return 0;
+}
+
+/**
+ * vpbe_enum_outputs - enumerate outputs
+ * @vpbe_dev - vpbe device ptr
+ * @output - ptr to v4l2_output structure
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev,
+			     struct v4l2_output *output)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int temp_index = output->index;
+
+	if (temp_index >= cfg->num_outputs)
+		return -EINVAL;
+
+	*output = cfg->outputs[temp_index].output;
+	output->index = temp_index;
+
+	return 0;
+}
+
+static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	struct vpbe_enc_mode_info var;
+	int curr_output = vpbe_dev->current_out_index;
+	int i;
+
+	if (NULL == mode)
+		return -EINVAL;
+
+	for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) {
+		var = cfg->outputs[curr_output].modes[i];
+		if (!strcmp(mode, var.name)) {
+			vpbe_dev->current_timings = var;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev,
+				      struct vpbe_enc_mode_info *mode_info)
+{
+	if (NULL == mode_info)
+		return -EINVAL;
+
+	*mode_info = vpbe_dev->current_timings;
+
+	return 0;
+}
+
+static int vpbe_get_dv_preset_info(struct vpbe_device *vpbe_dev,
+				   unsigned int dv_preset)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	struct vpbe_enc_mode_info var;
+	int curr_output = vpbe_dev->current_out_index;
+	int i;
+
+	for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+		var = cfg->outputs[curr_output].modes[i];
+		if ((var.timings_type & VPBE_ENC_DV_PRESET) &&
+		  (var.timings.dv_preset == dv_preset)) {
+			vpbe_dev->current_timings = var;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/* Get std by std id */
+static int vpbe_get_std_info(struct vpbe_device *vpbe_dev,
+			     v4l2_std_id std_id)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	struct vpbe_enc_mode_info var;
+	int curr_output = vpbe_dev->current_out_index;
+	int i;
+
+	for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+		var = cfg->outputs[curr_output].modes[i];
+		if ((var.timings_type & VPBE_ENC_STD) &&
+		  (var.timings.std_id & std_id)) {
+			vpbe_dev->current_timings = var;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
+				char *std_name)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	struct vpbe_enc_mode_info var;
+	int curr_output = vpbe_dev->current_out_index;
+	int i;
+
+	for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+		var = cfg->outputs[curr_output].modes[i];
+		if (!strcmp(var.name, std_name)) {
+			vpbe_dev->current_timings = var;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vpbe_set_output - Set output
+ * @vpbe_dev - vpbe device ptr
+ * @index - index of output
+ *
+ * Set vpbe output to the output specified by the index
+ */
+static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
+{
+	struct encoder_config_info *curr_enc_info =
+			vpbe_current_encoder_info(vpbe_dev);
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int enc_out_index;
+	int sd_index;
+	int ret = 0;
+
+	if (index >= cfg->num_outputs)
+		return -EINVAL;
+
+	mutex_lock(&vpbe_dev->lock);
+
+	sd_index = vpbe_dev->current_sd_index;
+	enc_out_index = cfg->outputs[index].output.index;
+	/*
+	 * Currently we switch the encoder based on output selected
+	 * by the application. If media controller is implemented later
+	 * there is will be an API added to setup_link between venc
+	 * and external encoder. So in that case below comparison always
+	 * match and encoder will not be switched. But if application
+	 * chose not to use media controller, then this provides current
+	 * way of switching encoder at the venc output.
+	 */
+	if (strcmp(curr_enc_info->module_name,
+		   cfg->outputs[index].subdev_name)) {
+		/* Need to switch the encoder at the output */
+		sd_index = vpbe_find_encoder_sd_index(cfg, index);
+		if (sd_index < 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		if (ret)
+			goto out;
+	}
+
+	/* Set output at the encoder */
+	ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+				       s_routing, 0, enc_out_index, 0);
+	if (ret)
+		goto out;
+
+	/*
+	 * It is assumed that venc or extenal encoder will set a default
+	 * mode in the sub device. For external encoder or LCD pannel output,
+	 * we also need to set up the lcd port for the required mode. So setup
+	 * the lcd port for the default mode that is configured in the board
+	 * arch/arm/mach-davinci/board-dm355-evm.setup file for the external
+	 * encoder.
+	 */
+	ret = vpbe_get_mode_info(vpbe_dev,
+				 cfg->outputs[index].default_mode);
+	if (!ret) {
+		struct osd_state *osd_device = vpbe_dev->osd_device;
+
+		osd_device->ops.set_left_margin(osd_device,
+			vpbe_dev->current_timings.left_margin);
+		osd_device->ops.set_top_margin(osd_device,
+		vpbe_dev->current_timings.upper_margin);
+		vpbe_dev->current_sd_index = sd_index;
+		vpbe_dev->current_out_index = index;
+	}
+out:
+	mutex_unlock(&vpbe_dev->lock);
+	return ret;
+}
+
+static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < cfg->num_outputs; i++) {
+		if (!strcmp(def_output,
+			    cfg->outputs[i].output.name)) {
+			ret = vpbe_set_output(vpbe_dev, i);
+			if (!ret)
+				vpbe_dev->current_out_index = i;
+			return ret;
+		}
+	}
+	return ret;
+}
+
+/**
+ * vpbe_get_output - Get output
+ * @vpbe_dev - vpbe device ptr
+ *
+ * return current vpbe output to the the index
+ */
+static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
+{
+	return vpbe_dev->current_out_index;
+}
+
+/**
+ * vpbe_s_dv_preset - Set the given preset timings in the encoder
+ *
+ * Sets the preset if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_dv_preset(struct vpbe_device *vpbe_dev,
+		     struct v4l2_dv_preset *dv_preset)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int out_index = vpbe_dev->current_out_index;
+	int sd_index = vpbe_dev->current_sd_index;
+	int ret;
+
+
+	if (!(cfg->outputs[out_index].output.capabilities &
+	    V4L2_OUT_CAP_PRESETS))
+		return -EINVAL;
+
+	ret = vpbe_get_dv_preset_info(vpbe_dev, dv_preset->preset);
+
+	if (ret)
+		return ret;
+
+	mutex_lock(&vpbe_dev->lock);
+
+
+	ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+					s_dv_preset, dv_preset);
+	/* set the lcd controller output for the given mode */
+	if (!ret) {
+		struct osd_state *osd_device = vpbe_dev->osd_device;
+
+		osd_device->ops.set_left_margin(osd_device,
+		vpbe_dev->current_timings.left_margin);
+		osd_device->ops.set_top_margin(osd_device,
+		vpbe_dev->current_timings.upper_margin);
+	}
+	mutex_unlock(&vpbe_dev->lock);
+
+	return ret;
+}
+
+/**
+ * vpbe_g_dv_preset - Get the preset in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_dv_preset(struct vpbe_device *vpbe_dev,
+		     struct v4l2_dv_preset *dv_preset)
+{
+	if (vpbe_dev->current_timings.timings_type &
+	  VPBE_ENC_DV_PRESET) {
+		dv_preset->preset = vpbe_dev->current_timings.timings.dv_preset;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vpbe_enum_dv_presets - Enumerate the dv presets in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_enum_dv_presets(struct vpbe_device *vpbe_dev,
+			 struct v4l2_dv_enum_preset *preset_info)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int out_index = vpbe_dev->current_out_index;
+	struct vpbe_output *output = &cfg->outputs[out_index];
+	int j = 0;
+	int i;
+
+	if (!(output->output.capabilities & V4L2_OUT_CAP_PRESETS))
+		return -EINVAL;
+
+	for (i = 0; i < output->num_modes; i++) {
+		if (output->modes[i].timings_type == VPBE_ENC_DV_PRESET) {
+			if (j == preset_info->index)
+				break;
+			j++;
+		}
+	}
+
+	if (i == output->num_modes)
+		return -EINVAL;
+
+	return v4l_fill_dv_preset_info(output->modes[i].timings.dv_preset,
+					preset_info);
+}
+
+/**
+ * vpbe_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	int out_index = vpbe_dev->current_out_index;
+	int sd_index = vpbe_dev->current_sd_index;
+	int ret;
+
+	if (!(cfg->outputs[out_index].output.capabilities &
+		V4L2_OUT_CAP_STD))
+		return -EINVAL;
+
+	ret = vpbe_get_std_info(vpbe_dev, *std_id);
+	if (ret)
+		return ret;
+
+	mutex_lock(&vpbe_dev->lock);
+
+	ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+			       s_std_output, *std_id);
+	/* set the lcd controller output for the given mode */
+	if (!ret) {
+		struct osd_state *osd_device = vpbe_dev->osd_device;
+
+		osd_device->ops.set_left_margin(osd_device,
+		vpbe_dev->current_timings.left_margin);
+		osd_device->ops.set_top_margin(osd_device,
+		vpbe_dev->current_timings.upper_margin);
+	}
+	mutex_unlock(&vpbe_dev->lock);
+
+	return ret;
+}
+
+/**
+ * vpbe_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+	struct vpbe_enc_mode_info cur_timings = vpbe_dev->current_timings;
+
+	if (cur_timings.timings_type & VPBE_ENC_STD) {
+		*std_id = cur_timings.timings.std_id;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vpbe_set_mode - Set mode in the current encoder using mode info
+ *
+ * Use the mode string to decide what timings to set in the encoder
+ * This is typically useful when fbset command is used to change the current
+ * timings by specifying a string to indicate the timings.
+ */
+static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
+			 struct vpbe_enc_mode_info *mode_info)
+{
+	struct vpbe_enc_mode_info *preset_mode = NULL;
+	struct vpbe_config *cfg = vpbe_dev->cfg;
+	struct v4l2_dv_preset dv_preset;
+	struct osd_state *osd_device;
+	int out_index = vpbe_dev->current_out_index;
+	int ret = 0;
+	int i;
+
+	if ((NULL == mode_info) || (NULL == mode_info->name))
+		return -EINVAL;
+
+	for (i = 0; i < cfg->outputs[out_index].num_modes; i++) {
+		if (!strcmp(mode_info->name,
+		     cfg->outputs[out_index].modes[i].name)) {
+			preset_mode = &cfg->outputs[out_index].modes[i];
+			/*
+			 * it may be one of the 3 timings type. Check and
+			 * invoke right API
+			 */
+			if (preset_mode->timings_type & VPBE_ENC_STD)
+				return vpbe_s_std(vpbe_dev,
+						 &preset_mode->timings.std_id);
+			if (preset_mode->timings_type & VPBE_ENC_DV_PRESET) {
+				dv_preset.preset =
+					preset_mode->timings.dv_preset;
+				return vpbe_s_dv_preset(vpbe_dev, &dv_preset);
+			}
+		}
+	}
+
+	/* Only custom timing should reach here */
+	if (preset_mode == NULL)
+		return -EINVAL;
+
+	mutex_lock(&vpbe_dev->lock);
+
+	osd_device = vpbe_dev->osd_device;
+	vpbe_dev->current_timings = *preset_mode;
+	osd_device->ops.set_left_margin(osd_device,
+		vpbe_dev->current_timings.left_margin);
+	osd_device->ops.set_top_margin(osd_device,
+		vpbe_dev->current_timings.upper_margin);
+
+	mutex_unlock(&vpbe_dev->lock);
+
+	return ret;
+}
+
+static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev)
+{
+	int ret;
+
+	ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode);
+	if (ret)
+		return ret;
+
+	/* set the default mode in the encoder */
+	return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings);
+}
+
+static int platform_device_get(struct device *dev, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vpbe_device *vpbe_dev = data;
+
+	if (strcmp("vpbe-osd", pdev->name) == 0)
+		vpbe_dev->osd_device = platform_get_drvdata(pdev);
+
+	return 0;
+}
+
+/**
+ * vpbe_initialize() - Initialize the vpbe display controller
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Master frame buffer device drivers calls this to initialize vpbe
+ * display controller. This will then registers v4l2 device and the sub
+ * devices and sets a current encoder sub device for display. v4l2 display
+ * device driver is the master and frame buffer display device driver is
+ * the slave. Frame buffer display driver checks the initialized during
+ * probe and exit if not initialized. Returns status.
+ */
+static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+	struct encoder_config_info *enc_info;
+	struct v4l2_subdev **enc_subdev;
+	struct osd_state *osd_device;
+	struct i2c_adapter *i2c_adap;
+	int output_index;
+	int num_encoders;
+	int ret = 0;
+	int err;
+	int i;
+
+	/*
+	 * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer
+	 * from the platform device by iteration of platform drivers and
+	 * matching with device name
+	 */
+	if (NULL == vpbe_dev || NULL == dev) {
+		printk(KERN_ERR "Null device pointers.\n");
+		return -ENODEV;
+	}
+
+	if (vpbe_dev->initialized)
+		return 0;
+
+	mutex_lock(&vpbe_dev->lock);
+
+	if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
+		/* We have dac clock available for platform */
+		vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac");
+		if (IS_ERR(vpbe_dev->dac_clk)) {
+			ret =  PTR_ERR(vpbe_dev->dac_clk);
+			goto vpbe_unlock;
+		}
+		if (clk_enable(vpbe_dev->dac_clk)) {
+			ret =  -ENODEV;
+			goto vpbe_unlock;
+		}
+	}
+
+	/* first enable vpss clocks */
+	vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
+
+	/* First register a v4l2 device */
+	ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev);
+	if (ret) {
+		v4l2_err(dev->driver,
+			"Unable to register v4l2 device.\n");
+		goto vpbe_fail_clock;
+	}
+	v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n");
+
+	err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
+			       platform_device_get);
+	if (err < 0)
+		return err;
+
+	vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
+					   vpbe_dev->cfg->venc.module_name);
+	/* register venc sub device */
+	if (vpbe_dev->venc == NULL) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"vpbe unable to init venc sub device\n");
+		ret = -ENODEV;
+		goto vpbe_fail_v4l2_device;
+	}
+	/* initialize osd device */
+	osd_device = vpbe_dev->osd_device;
+
+	if (NULL != osd_device->ops.initialize) {
+		err = osd_device->ops.initialize(osd_device);
+		if (err) {
+			v4l2_err(&vpbe_dev->v4l2_dev,
+				 "unable to initialize the OSD device");
+			err = -ENOMEM;
+			goto vpbe_fail_v4l2_device;
+		}
+	}
+
+	/*
+	 * Register any external encoders that are configured. At index 0 we
+	 * store venc sd index.
+	 */
+	num_encoders = vpbe_dev->cfg->num_ext_encoders + 1;
+	vpbe_dev->encoders = kmalloc(
+				sizeof(struct v4l2_subdev *)*num_encoders,
+				GFP_KERNEL);
+	if (NULL == vpbe_dev->encoders) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"unable to allocate memory for encoders sub devices");
+		ret = -ENOMEM;
+		goto vpbe_fail_v4l2_device;
+	}
+
+	i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id);
+	for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) {
+		if (i == 0) {
+			/* venc is at index 0 */
+			enc_subdev = &vpbe_dev->encoders[i];
+			*enc_subdev = vpbe_dev->venc;
+			continue;
+		}
+		enc_info = &vpbe_dev->cfg->ext_encoders[i];
+		if (enc_info->is_i2c) {
+			enc_subdev = &vpbe_dev->encoders[i];
+			*enc_subdev = v4l2_i2c_new_subdev_board(
+						&vpbe_dev->v4l2_dev, i2c_adap,
+						&enc_info->board_info, NULL);
+			if (*enc_subdev)
+				v4l2_info(&vpbe_dev->v4l2_dev,
+					  "v4l2 sub device %s registered\n",
+					  enc_info->module_name);
+			else {
+				v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s"
+					 " failed to register",
+					 enc_info->module_name);
+				ret = -ENODEV;
+				goto vpbe_fail_sd_register;
+			}
+		} else
+			v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders"
+				 " currently not supported");
+	}
+
+	/* set the current encoder and output to that of venc by default */
+	vpbe_dev->current_sd_index = 0;
+	vpbe_dev->current_out_index = 0;
+	output_index = 0;
+
+	mutex_unlock(&vpbe_dev->lock);
+
+	printk(KERN_NOTICE "Setting default output to %s\n", def_output);
+	ret = vpbe_set_default_output(vpbe_dev);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s",
+			 def_output);
+		return ret;
+	}
+
+	printk(KERN_NOTICE "Setting default mode to %s\n", def_mode);
+	ret = vpbe_set_default_mode(vpbe_dev);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s",
+			 def_mode);
+		return ret;
+	}
+	vpbe_dev->initialized = 1;
+	/* TBD handling of bootargs for default output and mode */
+	return 0;
+
+vpbe_fail_sd_register:
+	kfree(vpbe_dev->encoders);
+vpbe_fail_v4l2_device:
+	v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+vpbe_fail_clock:
+	if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+		clk_put(vpbe_dev->dac_clk);
+vpbe_unlock:
+	mutex_unlock(&vpbe_dev->lock);
+	return ret;
+}
+
+/**
+ * vpbe_deinitialize() - de-initialize the vpbe display controller
+ * @dev - Master and slave device ptr
+ *
+ * vpbe_master and slave frame buffer devices calls this to de-initialize
+ * the display controller. It is called when master and slave device
+ * driver modules are removed and no longer requires the display controller.
+ */
+static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+	v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+	if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+		clk_put(vpbe_dev->dac_clk);
+
+	kfree(vpbe_dev->encoders);
+	vpbe_dev->initialized = 0;
+	/* disable vpss clocks */
+	vpss_enable_clock(VPSS_VPBE_CLOCK, 0);
+}
+
+static struct vpbe_device_ops vpbe_dev_ops = {
+	.g_cropcap = vpbe_g_cropcap,
+	.enum_outputs = vpbe_enum_outputs,
+	.set_output = vpbe_set_output,
+	.get_output = vpbe_get_output,
+	.s_dv_preset = vpbe_s_dv_preset,
+	.g_dv_preset = vpbe_g_dv_preset,
+	.enum_dv_presets = vpbe_enum_dv_presets,
+	.s_std = vpbe_s_std,
+	.g_std = vpbe_g_std,
+	.initialize = vpbe_initialize,
+	.deinitialize = vpbe_deinitialize,
+	.get_mode_info = vpbe_get_current_mode_info,
+	.set_mode = vpbe_set_mode,
+};
+
+static __devinit int vpbe_probe(struct platform_device *pdev)
+{
+	struct vpbe_device *vpbe_dev;
+	struct vpbe_config *cfg;
+	int ret = -EINVAL;
+
+	if (pdev->dev.platform_data == NULL) {
+		v4l2_err(pdev->dev.driver, "No platform data\n");
+		return -ENODEV;
+	}
+	cfg = pdev->dev.platform_data;
+
+	if (!cfg->module_name[0] ||
+	    !cfg->osd.module_name[0] ||
+	    !cfg->venc.module_name[0]) {
+		v4l2_err(pdev->dev.driver, "vpbe display module names not"
+			 " defined\n");
+		return ret;
+	}
+
+	vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL);
+	if (vpbe_dev == NULL) {
+		v4l2_err(pdev->dev.driver, "Unable to allocate memory"
+			 " for vpbe_device\n");
+		return -ENOMEM;
+	}
+	vpbe_dev->cfg = cfg;
+	vpbe_dev->ops = vpbe_dev_ops;
+	vpbe_dev->pdev = &pdev->dev;
+
+	if (cfg->outputs->num_modes > 0)
+		vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0];
+	else
+		return -ENODEV;
+
+	/* set the driver data in platform device */
+	platform_set_drvdata(pdev, vpbe_dev);
+	mutex_init(&vpbe_dev->lock);
+
+	return 0;
+}
+
+static int vpbe_remove(struct platform_device *device)
+{
+	struct vpbe_device *vpbe_dev = platform_get_drvdata(device);
+
+	kfree(vpbe_dev);
+
+	return 0;
+}
+
+static struct platform_driver vpbe_driver = {
+	.driver	= {
+		.name	= "vpbe_controller",
+		.owner	= THIS_MODULE,
+	},
+	.probe = vpbe_probe,
+	.remove = vpbe_remove,
+};
+
+/**
+ * vpbe_init: initialize the vpbe driver
+ *
+ * This function registers device and driver to the kernel
+ */
+static __init int vpbe_init(void)
+{
+	return platform_driver_register(&vpbe_driver);
+}
+
+/**
+ * vpbe_cleanup : cleanup function for vpbe driver
+ *
+ * This will un-registers the device and driver to the kernel
+ */
+static void vpbe_cleanup(void)
+{
+	platform_driver_unregister(&vpbe_driver);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpbe_init);
+module_exit(vpbe_cleanup);
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
new file mode 100644
index 0000000..7f1d83a
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -0,0 +1,1860 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
+#include <mach/cputype.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_display.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpbe_osd.h>
+#include "vpbe_venc_regs.h"
+
+#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
+
+static int debug;
+
+#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2)
+#define VPBE_DEFAULT_NUM_BUFS 3
+
+module_param(debug, int, 0644);
+
+static int venc_is_second_field(struct vpbe_display *disp_dev)
+{
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	int ret;
+	int val;
+
+	ret = v4l2_subdev_call(vpbe_dev->venc,
+			       core,
+			       ioctl,
+			       VENC_GET_FLD,
+			       &val);
+	if (ret < 0) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			 "Error in getting Field ID 0\n");
+	}
+	return val;
+}
+
+static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
+				struct vpbe_layer *layer)
+{
+	struct timespec timevalue;
+
+	if (layer->cur_frm == layer->next_frm)
+		return;
+	ktime_get_ts(&timevalue);
+	layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
+	layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
+	layer->cur_frm->state = VIDEOBUF_DONE;
+	wake_up_interruptible(&layer->cur_frm->done);
+	/* Make cur_frm pointing to next_frm */
+	layer->cur_frm = layer->next_frm;
+}
+
+static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
+				struct vpbe_layer *layer)
+{
+	struct osd_state *osd_device = disp_obj->osd_device;
+	unsigned long addr;
+
+	spin_lock(&disp_obj->dma_queue_lock);
+	if (list_empty(&layer->dma_queue) ||
+		(layer->cur_frm != layer->next_frm)) {
+		spin_unlock(&disp_obj->dma_queue_lock);
+		return;
+	}
+	/*
+	 * one field is displayed configure
+	 * the next frame if it is available
+	 * otherwise hold on current frame
+	 * Get next from the buffer queue
+	 */
+	layer->next_frm = list_entry(
+				layer->dma_queue.next,
+				struct  videobuf_buffer,
+				queue);
+	/* Remove that from the buffer queue */
+	list_del(&layer->next_frm->queue);
+	spin_unlock(&disp_obj->dma_queue_lock);
+	/* Mark state of the frame to active */
+	layer->next_frm->state = VIDEOBUF_ACTIVE;
+	addr = videobuf_to_dma_contig(layer->next_frm);
+	osd_device->ops.start_layer(osd_device,
+			layer->layer_info.id,
+			addr,
+			disp_obj->cbcr_ofst);
+}
+
+/* interrupt service routine */
+static irqreturn_t venc_isr(int irq, void *arg)
+{
+	struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
+	struct vpbe_layer *layer;
+	static unsigned last_event;
+	unsigned event = 0;
+	int fid;
+	int i;
+
+	if ((NULL == arg) || (NULL == disp_dev->dev[0]))
+		return IRQ_HANDLED;
+
+	if (venc_is_second_field(disp_dev))
+		event |= VENC_SECOND_FIELD;
+	else
+		event |= VENC_FIRST_FIELD;
+
+	if (event == (last_event & ~VENC_END_OF_FRAME)) {
+		/*
+		* If the display is non-interlaced, then we need to flag the
+		* end-of-frame event at every interrupt regardless of the
+		* value of the FIDST bit.  We can conclude that the display is
+		* non-interlaced if the value of the FIDST bit is unchanged
+		* from the previous interrupt.
+		*/
+		event |= VENC_END_OF_FRAME;
+	} else if (event == VENC_SECOND_FIELD) {
+		/* end-of-frame for interlaced display */
+		event |= VENC_END_OF_FRAME;
+	}
+	last_event = event;
+
+	for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+		layer = disp_dev->dev[i];
+		/* If streaming is started in this layer */
+		if (!layer->started)
+			continue;
+
+		if (layer->layer_first_int) {
+			layer->layer_first_int = 0;
+			continue;
+		}
+		/* Check the field format */
+		if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
+			(event & VENC_END_OF_FRAME)) {
+			/* Progressive mode */
+
+			vpbe_isr_even_field(disp_dev, layer);
+			vpbe_isr_odd_field(disp_dev, layer);
+		} else {
+		/* Interlaced mode */
+
+			layer->field_id ^= 1;
+			if (event & VENC_FIRST_FIELD)
+				fid = 0;
+			else
+				fid = 1;
+
+			/*
+			* If field id does not match with store
+			* field id
+			*/
+			if (fid != layer->field_id) {
+				/* Make them in sync */
+				layer->field_id = fid;
+				continue;
+			}
+			/*
+			* device field id and local field id are
+			* in sync. If this is even field
+			*/
+			if (0 == fid)
+				vpbe_isr_even_field(disp_dev, layer);
+			else  /* odd field */
+				vpbe_isr_odd_field(disp_dev, layer);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * vpbe_buffer_prepare()
+ * This is the callback function called from videobuf_qbuf() function
+ * the buffer is prepared and user space virtual address is converted into
+ * physical address
+ */
+static int vpbe_buffer_prepare(struct videobuf_queue *q,
+				  struct videobuf_buffer *vb,
+				  enum v4l2_field field)
+{
+	struct vpbe_fh *fh = q->priv_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	unsigned long addr;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+				"vpbe_buffer_prepare\n");
+
+	/* If buffer is not initialized, initialize it */
+	if (VIDEOBUF_NEEDS_INIT == vb->state) {
+		vb->width = layer->pix_fmt.width;
+		vb->height = layer->pix_fmt.height;
+		vb->size = layer->pix_fmt.sizeimage;
+		vb->field = field;
+
+		ret = videobuf_iolock(q, vb, NULL);
+		if (ret < 0) {
+			v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
+				user address\n");
+			return -EINVAL;
+		}
+
+		addr = videobuf_to_dma_contig(vb);
+
+		if (q->streaming) {
+			if (!IS_ALIGNED(addr, 8)) {
+				v4l2_err(&vpbe_dev->v4l2_dev,
+					"buffer_prepare:offset is \
+					not aligned to 32 bytes\n");
+				return -EINVAL;
+			}
+		}
+		vb->state = VIDEOBUF_PREPARED;
+	}
+	return 0;
+}
+
+/*
+ * vpbe_buffer_setup()
+ * This function allocates memory for the buffers
+ */
+static int vpbe_buffer_setup(struct videobuf_queue *q,
+				unsigned int *count,
+				unsigned int *size)
+{
+	/* Get the file handle object and layer object */
+	struct vpbe_fh *fh = q->priv_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
+
+	*size = layer->pix_fmt.sizeimage;
+
+	/* Store number of buffers allocated in numbuffer member */
+	if (*count < VPBE_DEFAULT_NUM_BUFS)
+		*count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+
+	return 0;
+}
+
+/*
+ * vpbe_buffer_queue()
+ * This function adds the buffer to DMA queue
+ */
+static void vpbe_buffer_queue(struct videobuf_queue *q,
+				 struct videobuf_buffer *vb)
+{
+	/* Get the file handle object and layer object */
+	struct vpbe_fh *fh = q->priv_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_display *disp = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	unsigned long flags;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"vpbe_buffer_queue\n");
+
+	/* add the buffer to the DMA queue */
+	spin_lock_irqsave(&disp->dma_queue_lock, flags);
+	list_add_tail(&vb->queue, &layer->dma_queue);
+	spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
+	/* Change state of the buffer */
+	vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * vpbe_buffer_release()
+ * This function is called from the videobuf layer to free memory allocated to
+ * the buffers
+ */
+static void vpbe_buffer_release(struct videobuf_queue *q,
+				   struct videobuf_buffer *vb)
+{
+	/* Get the file handle object and layer object */
+	struct vpbe_fh *fh = q->priv_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"vpbe_buffer_release\n");
+
+	if (V4L2_MEMORY_USERPTR != layer->memory)
+		videobuf_dma_contig_free(q, vb);
+
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops video_qops = {
+	.buf_setup = vpbe_buffer_setup,
+	.buf_prepare = vpbe_buffer_prepare,
+	.buf_queue = vpbe_buffer_queue,
+	.buf_release = vpbe_buffer_release,
+};
+
+static
+struct vpbe_layer*
+_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
+			struct vpbe_layer *layer)
+{
+	enum vpbe_display_device_id thiswin, otherwin;
+	thiswin = layer->device_id;
+
+	otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
+	VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
+	return disp_dev->dev[otherwin];
+}
+
+static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
+			struct vpbe_layer *layer)
+{
+	struct osd_layer_config *cfg  = &layer->layer_info.config;
+	struct osd_state *osd_device = disp_dev->osd_device;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	unsigned long addr;
+	int ret;
+
+	addr = videobuf_to_dma_contig(layer->cur_frm);
+	/* Set address in the display registers */
+	osd_device->ops.start_layer(osd_device,
+				    layer->layer_info.id,
+				    addr,
+				    disp_dev->cbcr_ofst);
+
+	ret = osd_device->ops.enable_layer(osd_device,
+				layer->layer_info.id, 0);
+	if (ret < 0) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"Error in enabling osd window layer 0\n");
+		return -1;
+	}
+
+	/* Enable the window */
+	layer->layer_info.enable = 1;
+	if (cfg->pixfmt == PIXFMT_NV12) {
+		struct vpbe_layer *otherlayer =
+			_vpbe_display_get_other_win_layer(disp_dev, layer);
+
+		ret = osd_device->ops.enable_layer(osd_device,
+				otherlayer->layer_info.id, 1);
+		if (ret < 0) {
+			v4l2_err(&vpbe_dev->v4l2_dev,
+				"Error in enabling osd window layer 1\n");
+			return -1;
+		}
+		otherlayer->layer_info.enable = 1;
+	}
+	return 0;
+}
+
+static void
+vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
+			struct vpbe_layer *layer,
+			int expected_xsize, int expected_ysize)
+{
+	struct display_layer_info *layer_info = &layer->layer_info;
+	struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
+	struct osd_layer_config *cfg  = &layer->layer_info.config;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	int calculated_xsize;
+	int h_exp = 0;
+	int v_exp = 0;
+	int h_scale;
+	int v_scale;
+
+	v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id;
+
+	/*
+	 * Application initially set the image format. Current display
+	 * size is obtained from the vpbe display controller. expected_xsize
+	 * and expected_ysize are set through S_CROP ioctl. Based on this,
+	 * driver will calculate the scale factors for vertical and
+	 * horizontal direction so that the image is displayed scaled
+	 * and expanded. Application uses expansion to display the image
+	 * in a square pixel. Otherwise it is displayed using displays
+	 * pixel aspect ratio.It is expected that application chooses
+	 * the crop coordinates for cropped or scaled display. if crop
+	 * size is less than the image size, it is displayed cropped or
+	 * it is displayed scaled and/or expanded.
+	 *
+	 * to begin with, set the crop window same as expected. Later we
+	 * will override with scaled window size
+	 */
+
+	cfg->xsize = pixfmt->width;
+	cfg->ysize = pixfmt->height;
+	layer_info->h_zoom = ZOOM_X1;	/* no horizontal zoom */
+	layer_info->v_zoom = ZOOM_X1;	/* no horizontal zoom */
+	layer_info->h_exp = H_EXP_OFF;	/* no horizontal zoom */
+	layer_info->v_exp = V_EXP_OFF;	/* no horizontal zoom */
+
+	if (pixfmt->width < expected_xsize) {
+		h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
+		if (h_scale < 2)
+			h_scale = 1;
+		else if (h_scale >= 4)
+			h_scale = 4;
+		else
+			h_scale = 2;
+		cfg->xsize *= h_scale;
+		if (cfg->xsize < expected_xsize) {
+			if ((standard_id & V4L2_STD_525_60) ||
+			(standard_id & V4L2_STD_625_50)) {
+				calculated_xsize = (cfg->xsize *
+					VPBE_DISPLAY_H_EXP_RATIO_N) /
+					VPBE_DISPLAY_H_EXP_RATIO_D;
+				if (calculated_xsize <= expected_xsize) {
+					h_exp = 1;
+					cfg->xsize = calculated_xsize;
+				}
+			}
+		}
+		if (h_scale == 2)
+			layer_info->h_zoom = ZOOM_X2;
+		else if (h_scale == 4)
+			layer_info->h_zoom = ZOOM_X4;
+		if (h_exp)
+			layer_info->h_exp = H_EXP_9_OVER_8;
+	} else {
+		/* no scaling, only cropping. Set display area to crop area */
+		cfg->xsize = expected_xsize;
+	}
+
+	if (pixfmt->height < expected_ysize) {
+		v_scale = expected_ysize / pixfmt->height;
+		if (v_scale < 2)
+			v_scale = 1;
+		else if (v_scale >= 4)
+			v_scale = 4;
+		else
+			v_scale = 2;
+		cfg->ysize *= v_scale;
+		if (cfg->ysize < expected_ysize) {
+			if ((standard_id & V4L2_STD_625_50)) {
+				calculated_xsize = (cfg->ysize *
+					VPBE_DISPLAY_V_EXP_RATIO_N) /
+					VPBE_DISPLAY_V_EXP_RATIO_D;
+				if (calculated_xsize <= expected_ysize) {
+					v_exp = 1;
+					cfg->ysize = calculated_xsize;
+				}
+			}
+		}
+		if (v_scale == 2)
+			layer_info->v_zoom = ZOOM_X2;
+		else if (v_scale == 4)
+			layer_info->v_zoom = ZOOM_X4;
+		if (v_exp)
+			layer_info->h_exp = V_EXP_6_OVER_5;
+	} else {
+		/* no scaling, only cropping. Set display area to crop area */
+		cfg->ysize = expected_ysize;
+	}
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"crop display xsize = %d, ysize = %d\n",
+		cfg->xsize, cfg->ysize);
+}
+
+static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
+			struct vpbe_layer *layer,
+			int top, int left)
+{
+	struct osd_layer_config *cfg = &layer->layer_info.config;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+	cfg->xpos = min((unsigned int)left,
+			vpbe_dev->current_timings.xres - cfg->xsize);
+	cfg->ypos = min((unsigned int)top,
+			vpbe_dev->current_timings.yres - cfg->ysize);
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"new xpos = %d, ypos = %d\n",
+		cfg->xpos, cfg->ypos);
+}
+
+static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
+			struct v4l2_rect *c)
+{
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+	if ((c->width == 0) ||
+	  ((c->width + c->left) > vpbe_dev->current_timings.xres))
+		c->width = vpbe_dev->current_timings.xres - c->left;
+
+	if ((c->height == 0) || ((c->height + c->top) >
+	  vpbe_dev->current_timings.yres))
+		c->height = vpbe_dev->current_timings.yres - c->top;
+
+	/* window height must be even for interlaced display */
+	if (vpbe_dev->current_timings.interlaced)
+		c->height &= (~0x01);
+
+}
+
+/**
+ * vpbe_try_format()
+ * If user application provides width and height, and have bytesperline set
+ * to zero, driver calculates bytesperline and sizeimage based on hardware
+ * limits.
+ */
+static int vpbe_try_format(struct vpbe_display *disp_dev,
+			struct v4l2_pix_format *pixfmt, int check)
+{
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	int min_height = 1;
+	int min_width = 32;
+	int max_height;
+	int max_width;
+	int bpp;
+
+	if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
+	    (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
+		/* choose default as V4L2_PIX_FMT_UYVY */
+		pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+	/* Check the field format */
+	if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
+		(pixfmt->field != V4L2_FIELD_NONE)) {
+		if (vpbe_dev->current_timings.interlaced)
+			pixfmt->field = V4L2_FIELD_INTERLACED;
+		else
+			pixfmt->field = V4L2_FIELD_NONE;
+	}
+
+	if (pixfmt->field == V4L2_FIELD_INTERLACED)
+		min_height = 2;
+
+	if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+		bpp = 1;
+	else
+		bpp = 2;
+
+	max_width = vpbe_dev->current_timings.xres;
+	max_height = vpbe_dev->current_timings.yres;
+
+	min_width /= bpp;
+
+	if (!pixfmt->width || (pixfmt->width < min_width) ||
+		(pixfmt->width > max_width)) {
+		pixfmt->width = vpbe_dev->current_timings.xres;
+	}
+
+	if (!pixfmt->height || (pixfmt->height  < min_height) ||
+		(pixfmt->height  > max_height)) {
+		pixfmt->height = vpbe_dev->current_timings.yres;
+	}
+
+	if (pixfmt->bytesperline < (pixfmt->width * bpp))
+		pixfmt->bytesperline = pixfmt->width * bpp;
+
+	/* Make the bytesperline 32 byte aligned */
+	pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
+
+	if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
+				(pixfmt->bytesperline * pixfmt->height >> 1);
+	else
+		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+	return 0;
+}
+
+static int vpbe_display_g_priority(struct file *file, void *priv,
+				enum v4l2_priority *p)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+
+	*p = v4l2_prio_max(&layer->prio);
+
+	return 0;
+}
+
+static int vpbe_display_s_priority(struct file *file, void *priv,
+				enum v4l2_priority p)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	int ret;
+
+	ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
+
+	return ret;
+}
+
+static int vpbe_display_querycap(struct file *file, void  *priv,
+			       struct v4l2_capability *cap)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	cap->version = VPBE_DISPLAY_VERSION_CODE;
+	cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+	strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
+	strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+	strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
+
+	return 0;
+}
+
+static int vpbe_display_s_crop(struct file *file, void *priv,
+			     struct v4l2_crop *crop)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_display *disp_dev = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	struct osd_layer_config *cfg = &layer->layer_info.config;
+	struct osd_state *osd_device = disp_dev->osd_device;
+	struct v4l2_rect *rect = &crop->c;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"VIDIOC_S_CROP, layer id = %d\n", layer->device_id);
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+		return -EINVAL;
+	}
+
+	if (rect->top < 0)
+		rect->top = 0;
+	if (rect->left < 0)
+		rect->left = 0;
+
+	vpbe_disp_check_window_params(disp_dev, rect);
+
+	osd_device->ops.get_layer_config(osd_device,
+			layer->layer_info.id, cfg);
+
+	vpbe_disp_calculate_scale_factor(disp_dev, layer,
+					rect->width,
+					rect->height);
+	vpbe_disp_adj_position(disp_dev, layer, rect->top,
+					rect->left);
+	ret = osd_device->ops.set_layer_config(osd_device,
+				layer->layer_info.id, cfg);
+	if (ret < 0) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"Error in set layer config:\n");
+		return -EINVAL;
+	}
+
+	/* apply zooming and h or v expansion */
+	osd_device->ops.set_zoom(osd_device,
+			layer->layer_info.id,
+			layer->layer_info.h_zoom,
+			layer->layer_info.v_zoom);
+	ret = osd_device->ops.set_vid_expansion(osd_device,
+			layer->layer_info.h_exp,
+			layer->layer_info.v_exp);
+	if (ret < 0) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+		"Error in set vid expansion:\n");
+		return -EINVAL;
+	}
+
+	if ((layer->layer_info.h_zoom != ZOOM_X1) ||
+		(layer->layer_info.v_zoom != ZOOM_X1) ||
+		(layer->layer_info.h_exp != H_EXP_OFF) ||
+		(layer->layer_info.v_exp != V_EXP_OFF))
+		/* Enable expansion filter */
+		osd_device->ops.set_interpolation_filter(osd_device, 1);
+	else
+		osd_device->ops.set_interpolation_filter(osd_device, 0);
+
+	return 0;
+}
+
+static int vpbe_display_g_crop(struct file *file, void *priv,
+			     struct v4l2_crop *crop)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct osd_layer_config *cfg = &layer->layer_info.config;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	struct osd_state *osd_device = fh->disp_dev->osd_device;
+	struct v4l2_rect *rect = &crop->c;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"VIDIOC_G_CROP, layer id = %d\n",
+			layer->device_id);
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+		ret = -EINVAL;
+	}
+	osd_device->ops.get_layer_config(osd_device,
+				layer->layer_info.id, cfg);
+	rect->top = cfg->ypos;
+	rect->left = cfg->xpos;
+	rect->width = cfg->xsize;
+	rect->height = cfg->ysize;
+
+	return 0;
+}
+
+static int vpbe_display_cropcap(struct file *file, void *priv,
+			      struct v4l2_cropcap *cropcap)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
+
+	cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	cropcap->bounds.left = 0;
+	cropcap->bounds.top = 0;
+	cropcap->bounds.width = vpbe_dev->current_timings.xres;
+	cropcap->bounds.height = vpbe_dev->current_timings.yres;
+	cropcap->pixelaspect = vpbe_dev->current_timings.aspect;
+	cropcap->defrect = cropcap->bounds;
+	return 0;
+}
+
+static int vpbe_display_g_fmt(struct file *file, void *priv,
+				struct v4l2_format *fmt)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"VIDIOC_G_FMT, layer id = %d\n",
+			layer->device_id);
+
+	/* If buffer type is video output */
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+		return -EINVAL;
+	}
+	/* Fill in the information about format */
+	fmt->fmt.pix = layer->pix_fmt;
+
+	return 0;
+}
+
+static int vpbe_display_enum_fmt(struct file *file, void  *priv,
+				   struct v4l2_fmtdesc *fmt)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	unsigned int index = 0;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+				"VIDIOC_ENUM_FMT, layer id = %d\n",
+				layer->device_id);
+	if (fmt->index > 1) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
+		return -EINVAL;
+	}
+
+	/* Fill in the information about format */
+	index = fmt->index;
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = index;
+	fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	if (index == 0) {
+		strcpy(fmt->description, "YUV 4:2:2 - UYVY");
+		fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+	} else {
+		strcpy(fmt->description, "Y/CbCr 4:2:0");
+		fmt->pixelformat = V4L2_PIX_FMT_NV12;
+	}
+
+	return 0;
+}
+
+static int vpbe_display_s_fmt(struct file *file, void *priv,
+				struct v4l2_format *fmt)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_display *disp_dev = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	struct osd_layer_config *cfg  = &layer->layer_info.config;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+	struct osd_state *osd_device = disp_dev->osd_device;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"VIDIOC_S_FMT, layer id = %d\n",
+			layer->device_id);
+
+	/* If streaming is started, return error */
+	if (layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+		v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
+		return -EINVAL;
+	}
+	/* Check for valid pixel format */
+	ret = vpbe_try_format(disp_dev, pixfmt, 1);
+	if (ret)
+		return ret;
+
+	/* YUV420 is requested, check availability of the
+	other video window */
+
+	layer->pix_fmt = *pixfmt;
+
+	/* Get osd layer config */
+	osd_device->ops.get_layer_config(osd_device,
+			layer->layer_info.id, cfg);
+	/* Store the pixel format in the layer object */
+	cfg->xsize = pixfmt->width;
+	cfg->ysize = pixfmt->height;
+	cfg->line_length = pixfmt->bytesperline;
+	cfg->ypos = 0;
+	cfg->xpos = 0;
+	cfg->interlaced = vpbe_dev->current_timings.interlaced;
+
+	if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
+		cfg->pixfmt = PIXFMT_YCbCrI;
+
+	/* Change of the default pixel format for both video windows */
+	if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
+		struct vpbe_layer *otherlayer;
+		cfg->pixfmt = PIXFMT_NV12;
+		otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
+								layer);
+		otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
+	}
+
+	/* Set the layer config in the osd window */
+	ret = osd_device->ops.set_layer_config(osd_device,
+				layer->layer_info.id, cfg);
+	if (ret < 0) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+				"Error in S_FMT params:\n");
+		return -EINVAL;
+	}
+
+	/* Readback and fill the local copy of current pix format */
+	osd_device->ops.get_layer_config(osd_device,
+			layer->layer_info.id, cfg);
+
+	return 0;
+}
+
+static int vpbe_display_try_fmt(struct file *file, void *priv,
+				  struct v4l2_format *fmt)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_display *disp_dev = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+		return -EINVAL;
+	}
+
+	/* Check for valid field format */
+	return  vpbe_try_format(disp_dev, pixfmt, 0);
+
+}
+
+/**
+ * vpbe_display_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_display_s_std(struct file *file, void *priv,
+				v4l2_std_id *std_id)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
+
+	/* If streaming is started, return error */
+	if (layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+	if (NULL != vpbe_dev->ops.s_std) {
+		ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
+		if (ret) {
+			v4l2_err(&vpbe_dev->v4l2_dev,
+			"Failed to set standard for sub devices\n");
+			return -EINVAL;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vpbe_display_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_display_g_std(struct file *file, void *priv,
+				v4l2_std_id *std_id)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,	"VIDIOC_G_STD\n");
+
+	/* Get the standard from the current encoder */
+	if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
+		*std_id = vpbe_dev->current_timings.timings.std_id;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vpbe_display_enum_output - enumerate outputs
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_display_enum_output(struct file *file, void *priv,
+				    struct v4l2_output *output)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,	"VIDIOC_ENUM_OUTPUT\n");
+
+	/* Enumerate outputs */
+
+	if (NULL == vpbe_dev->ops.enum_outputs)
+		return -EINVAL;
+
+	ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
+	if (ret) {
+		v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"Failed to enumerate outputs\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vpbe_display_s_output - Set output to
+ * the output specified by the index
+ */
+static int vpbe_display_s_output(struct file *file, void *priv,
+				unsigned int i)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,	"VIDIOC_S_OUTPUT\n");
+	/* If streaming is started, return error */
+	if (layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+	if (NULL == vpbe_dev->ops.set_output)
+		return -EINVAL;
+
+	ret = vpbe_dev->ops.set_output(vpbe_dev, i);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"Failed to set output for sub devices\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vpbe_display_g_output - Get output from subdevice
+ * for a given by the index
+ */
+static int vpbe_display_g_output(struct file *file, void *priv,
+				unsigned int *i)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
+	/* Get the standard from the current encoder */
+	*i = vpbe_dev->current_out_index;
+
+	return 0;
+}
+
+/**
+ * vpbe_display_enum_dv_presets - Enumerate the dv presets
+ *
+ * enum the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_enum_dv_presets(struct file *file, void *priv,
+			struct v4l2_dv_enum_preset *preset)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n");
+
+	/* Enumerate outputs */
+	if (NULL == vpbe_dev->ops.enum_dv_presets)
+		return -EINVAL;
+
+	ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"Failed to enumerate dv presets info\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vpbe_display_s_dv_preset - Set the dv presets
+ *
+ * Set the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_s_dv_preset(struct file *file, void *priv,
+				struct v4l2_dv_preset *preset)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n");
+
+
+	/* If streaming is started, return error */
+	if (layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+
+	/* Set the given standard in the encoder */
+	if (NULL != vpbe_dev->ops.s_dv_preset)
+		return -EINVAL;
+
+	ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"Failed to set the dv presets info\n");
+		return -EINVAL;
+	}
+	/* set the current norm to zero to be consistent. If STD is used
+	 * v4l2 layer will set the norm properly on successful s_std call
+	 */
+	layer->video_dev.current_norm = 0;
+
+	return 0;
+}
+
+/**
+ * vpbe_display_g_dv_preset - Set the dv presets
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_g_dv_preset(struct file *file, void *priv,
+				struct v4l2_dv_preset *dv_preset)
+{
+	struct vpbe_fh *fh = priv;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n");
+
+	/* Get the given standard in the encoder */
+
+	if (vpbe_dev->current_timings.timings_type &
+				VPBE_ENC_DV_PRESET) {
+		dv_preset->preset =
+			vpbe_dev->current_timings.timings.dv_preset;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vpbe_display_streamoff(struct file *file, void *priv,
+				enum v4l2_buf_type buf_type)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	struct osd_state *osd_device = fh->disp_dev->osd_device;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"VIDIOC_STREAMOFF,layer id = %d\n",
+			layer->device_id);
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	/* If io is allowed for this file handle, return error */
+	if (!fh->io_allowed) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+		return -EACCES;
+	}
+
+	/* If streaming is not started, return error */
+	if (!layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
+			" id = %d\n", layer->device_id);
+		return -EINVAL;
+	}
+
+	osd_device->ops.disable_layer(osd_device,
+			layer->layer_info.id);
+	layer->started = 0;
+	ret = videobuf_streamoff(&layer->buffer_queue);
+
+	return ret;
+}
+
+static int vpbe_display_streamon(struct file *file, void *priv,
+			 enum v4l2_buf_type buf_type)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_display *disp_dev = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	struct osd_state *osd_device = disp_dev->osd_device;
+	int ret;
+
+	osd_device->ops.disable_layer(osd_device,
+			layer->layer_info.id);
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
+						layer->device_id);
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	/* If file handle is not allowed IO, return error */
+	if (!fh->io_allowed) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+		return -EACCES;
+	}
+	/* If Streaming is already started, return error */
+	if (layer->started) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
+		return -EBUSY;
+	}
+
+	/*
+	 * Call videobuf_streamon to start streaming
+	 * in videobuf
+	 */
+	ret = videobuf_streamon(&layer->buffer_queue);
+	if (ret) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+		"error in videobuf_streamon\n");
+		return ret;
+	}
+	/* If buffer queue is empty, return error */
+	if (list_empty(&layer->dma_queue)) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
+		goto streamoff;
+	}
+	/* Get the next frame from the buffer queue */
+	layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
+				struct videobuf_buffer, queue);
+	/* Remove buffer from the buffer queue */
+	list_del(&layer->cur_frm->queue);
+	/* Mark state of the current frame to active */
+	layer->cur_frm->state = VIDEOBUF_ACTIVE;
+	/* Initialize field_id and started member */
+	layer->field_id = 0;
+
+	/* Set parameters in OSD and VENC */
+	ret = vpbe_set_osd_display_params(disp_dev, layer);
+	if (ret < 0)
+		goto streamoff;
+
+	/*
+	 * if request format is yuv420 semiplanar, need to
+	 * enable both video windows
+	 */
+	layer->started = 1;
+
+	layer->layer_first_int = 1;
+
+	return ret;
+streamoff:
+	ret = videobuf_streamoff(&layer->buffer_queue);
+	return ret;
+}
+
+static int vpbe_display_dqbuf(struct file *file, void *priv,
+		      struct v4l2_buffer *buf)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"VIDIOC_DQBUF, layer id = %d\n",
+		layer->device_id);
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+	/* If this file handle is not allowed to do IO, return error */
+	if (!fh->io_allowed) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+		return -EACCES;
+	}
+	if (file->f_flags & O_NONBLOCK)
+		/* Call videobuf_dqbuf for non blocking mode */
+		ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
+	else
+		/* Call videobuf_dqbuf for blocking mode */
+		ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
+
+	return ret;
+}
+
+static int vpbe_display_qbuf(struct file *file, void *priv,
+		     struct v4l2_buffer *p)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"VIDIOC_QBUF, layer id = %d\n",
+		layer->device_id);
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	/* If this file handle is not allowed to do IO, return error */
+	if (!fh->io_allowed) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+		return -EACCES;
+	}
+
+	return videobuf_qbuf(&layer->buffer_queue, p);
+}
+
+static int vpbe_display_querybuf(struct file *file, void *priv,
+			 struct v4l2_buffer *buf)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+		"VIDIOC_QUERYBUF, layer id = %d\n",
+		layer->device_id);
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	/* Call videobuf_querybuf to get information */
+	ret = videobuf_querybuf(&layer->buffer_queue, buf);
+
+	return ret;
+}
+
+static int vpbe_display_reqbufs(struct file *file, void *priv,
+			struct v4l2_requestbuffers *req_buf)
+{
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	int ret;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	/* If io users of the layer is not zero, return error */
+	if (0 != layer->io_usrs) {
+		v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
+		return -EBUSY;
+	}
+	/* Initialize videobuf queue as per the buffer type */
+	videobuf_queue_dma_contig_init(&layer->buffer_queue,
+				&video_qops,
+				vpbe_dev->pdev,
+				&layer->irqlock,
+				V4L2_BUF_TYPE_VIDEO_OUTPUT,
+				layer->pix_fmt.field,
+				sizeof(struct videobuf_buffer),
+				fh, NULL);
+
+	/* Set io allowed member of file handle to TRUE */
+	fh->io_allowed = 1;
+	/* Increment io usrs member of layer object to 1 */
+	layer->io_usrs = 1;
+	/* Store type of memory requested in layer object */
+	layer->memory = req_buf->memory;
+	/* Initialize buffer queue */
+	INIT_LIST_HEAD(&layer->dma_queue);
+	/* Allocate buffers */
+	ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
+
+	return ret;
+}
+
+/*
+ * vpbe_display_mmap()
+ * It is used to map kernel space buffers into user spaces
+ */
+static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+	/* Get the layer object and file handle object */
+	struct vpbe_fh *fh = filep->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
+
+	return videobuf_mmap_mapper(&layer->buffer_queue, vma);
+}
+
+/* vpbe_display_poll(): It is used for select/poll system call
+ */
+static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
+{
+	struct vpbe_fh *fh = filep->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+	unsigned int err = 0;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
+	if (layer->started)
+		err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
+	return err;
+}
+
+/*
+ * vpbe_display_open()
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpbe_display_open(struct file *file)
+{
+	struct vpbe_fh *fh = NULL;
+	struct vpbe_layer *layer = video_drvdata(file);
+	struct vpbe_display *disp_dev = layer->disp_dev;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	struct osd_state *osd_device = disp_dev->osd_device;
+	int err;
+
+	/* Allocate memory for the file handle object */
+	fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
+	if (fh == NULL) {
+		v4l2_err(&vpbe_dev->v4l2_dev,
+			"unable to allocate memory for file handle object\n");
+		return -ENOMEM;
+	}
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"vpbe display open plane = %d\n",
+			layer->device_id);
+
+	/* store pointer to fh in private_data member of filep */
+	file->private_data = fh;
+	fh->layer = layer;
+	fh->disp_dev = disp_dev;
+
+	if (!layer->usrs) {
+
+		/* First claim the layer for this device */
+		err = osd_device->ops.request_layer(osd_device,
+						layer->layer_info.id);
+		if (err < 0) {
+			/* Couldn't get layer */
+			v4l2_err(&vpbe_dev->v4l2_dev,
+				"Display Manager failed to allocate layer\n");
+			kfree(fh);
+			return -EINVAL;
+		}
+	}
+	/* Increment layer usrs counter */
+	layer->usrs++;
+	/* Set io_allowed member to false */
+	fh->io_allowed = 0;
+	/* Initialize priority of this instance to default priority */
+	fh->prio = V4L2_PRIORITY_UNSET;
+	v4l2_prio_open(&layer->prio, &fh->prio);
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+			"vpbe display device opened successfully\n");
+	return 0;
+}
+
+/*
+ * vpbe_display_release()
+ * This function deletes buffer queue, frees the buffers and the davinci
+ * display file * handle
+ */
+static int vpbe_display_release(struct file *file)
+{
+	/* Get the layer object and file handle object */
+	struct vpbe_fh *fh = file->private_data;
+	struct vpbe_layer *layer = fh->layer;
+	struct osd_layer_config *cfg  = &layer->layer_info.config;
+	struct vpbe_display *disp_dev = fh->disp_dev;
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	struct osd_state *osd_device = disp_dev->osd_device;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
+
+	/* if this instance is doing IO */
+	if (fh->io_allowed) {
+		/* Reset io_usrs member of layer object */
+		layer->io_usrs = 0;
+
+		osd_device->ops.disable_layer(osd_device,
+				layer->layer_info.id);
+		layer->started = 0;
+		/* Free buffers allocated */
+		videobuf_queue_cancel(&layer->buffer_queue);
+		videobuf_mmap_free(&layer->buffer_queue);
+	}
+
+	/* Decrement layer usrs counter */
+	layer->usrs--;
+	/* If this file handle has initialize encoder device, reset it */
+	if (!layer->usrs) {
+		if (cfg->pixfmt == PIXFMT_NV12) {
+			struct vpbe_layer *otherlayer;
+			otherlayer =
+			_vpbe_display_get_other_win_layer(disp_dev, layer);
+			osd_device->ops.disable_layer(osd_device,
+					otherlayer->layer_info.id);
+			osd_device->ops.release_layer(osd_device,
+					otherlayer->layer_info.id);
+		}
+		osd_device->ops.disable_layer(osd_device,
+				layer->layer_info.id);
+		osd_device->ops.release_layer(osd_device,
+				layer->layer_info.id);
+	}
+	/* Close the priority */
+	v4l2_prio_close(&layer->prio, fh->prio);
+	file->private_data = NULL;
+
+	/* Free memory allocated to file handle object */
+	kfree(fh);
+
+	disp_dev->cbcr_ofst = 0;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vpbe_display_g_register(struct file *file, void *priv,
+			struct v4l2_dbg_register *reg)
+{
+	struct v4l2_dbg_match *match = &reg->match;
+
+	if (match->type >= 2) {
+		v4l2_subdev_call(vpbe_dev->venc,
+				 core,
+				 g_register,
+				 reg);
+	}
+
+	return 0;
+}
+
+static int vpbe_display_s_register(struct file *file, void *priv,
+			struct v4l2_dbg_register *reg)
+{
+	return 0;
+}
+#endif
+
+/* vpbe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
+	.vidioc_querycap	 = vpbe_display_querycap,
+	.vidioc_g_fmt_vid_out    = vpbe_display_g_fmt,
+	.vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
+	.vidioc_s_fmt_vid_out    = vpbe_display_s_fmt,
+	.vidioc_try_fmt_vid_out  = vpbe_display_try_fmt,
+	.vidioc_reqbufs		 = vpbe_display_reqbufs,
+	.vidioc_querybuf	 = vpbe_display_querybuf,
+	.vidioc_qbuf		 = vpbe_display_qbuf,
+	.vidioc_dqbuf		 = vpbe_display_dqbuf,
+	.vidioc_streamon	 = vpbe_display_streamon,
+	.vidioc_streamoff	 = vpbe_display_streamoff,
+	.vidioc_cropcap		 = vpbe_display_cropcap,
+	.vidioc_g_crop		 = vpbe_display_g_crop,
+	.vidioc_s_crop		 = vpbe_display_s_crop,
+	.vidioc_g_priority	 = vpbe_display_g_priority,
+	.vidioc_s_priority	 = vpbe_display_s_priority,
+	.vidioc_s_std		 = vpbe_display_s_std,
+	.vidioc_g_std		 = vpbe_display_g_std,
+	.vidioc_enum_output	 = vpbe_display_enum_output,
+	.vidioc_s_output	 = vpbe_display_s_output,
+	.vidioc_g_output	 = vpbe_display_g_output,
+	.vidioc_s_dv_preset	 = vpbe_display_s_dv_preset,
+	.vidioc_g_dv_preset	 = vpbe_display_g_dv_preset,
+	.vidioc_enum_dv_presets	 = vpbe_display_enum_dv_presets,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register	 = vpbe_display_g_register,
+	.vidioc_s_register	 = vpbe_display_s_register,
+#endif
+};
+
+static struct v4l2_file_operations vpbe_fops = {
+	.owner = THIS_MODULE,
+	.open = vpbe_display_open,
+	.release = vpbe_display_release,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = vpbe_display_mmap,
+	.poll = vpbe_display_poll
+};
+
+static int vpbe_device_get(struct device *dev, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vpbe_display *vpbe_disp  = data;
+
+	if (strcmp("vpbe_controller", pdev->name) == 0)
+		vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
+
+	if (strcmp("vpbe-osd", pdev->name) == 0)
+		vpbe_disp->osd_device = platform_get_drvdata(pdev);
+
+	return 0;
+}
+
+static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
+				     struct platform_device *pdev)
+{
+	struct vpbe_layer *vpbe_display_layer = NULL;
+	struct video_device *vbd = NULL;
+
+	/* Allocate memory for four plane display objects */
+
+	disp_dev->dev[i] =
+		kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL);
+
+	/* If memory allocation fails, return error */
+	if (!disp_dev->dev[i]) {
+		printk(KERN_ERR "ran out of memory\n");
+		return  -ENOMEM;
+	}
+	spin_lock_init(&disp_dev->dev[i]->irqlock);
+	mutex_init(&disp_dev->dev[i]->opslock);
+
+	/* Get the pointer to the layer object */
+	vpbe_display_layer = disp_dev->dev[i];
+	vbd = &vpbe_display_layer->video_dev;
+	/* Initialize field of video device */
+	vbd->release	= video_device_release_empty;
+	vbd->fops	= &vpbe_fops;
+	vbd->ioctl_ops	= &vpbe_ioctl_ops;
+	vbd->minor	= -1;
+	vbd->v4l2_dev   = &disp_dev->vpbe_dev->v4l2_dev;
+	vbd->lock	= &vpbe_display_layer->opslock;
+
+	if (disp_dev->vpbe_dev->current_timings.timings_type &
+			VPBE_ENC_STD) {
+		vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
+		vbd->current_norm =
+			disp_dev->vpbe_dev->
+			current_timings.timings.std_id;
+	} else
+		vbd->current_norm = 0;
+
+	snprintf(vbd->name, sizeof(vbd->name),
+			"DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
+			(VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
+			(VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
+			(VPBE_DISPLAY_VERSION_CODE) & 0xff);
+
+	vpbe_display_layer->device_id = i;
+
+	vpbe_display_layer->layer_info.id =
+		((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
+
+	/* Initialize prio member of layer object */
+	v4l2_prio_init(&vpbe_display_layer->prio);
+
+	return 0;
+}
+
+static __devinit int register_device(struct vpbe_layer *vpbe_display_layer,
+					struct vpbe_display *disp_dev,
+					struct platform_device *pdev) {
+	int err;
+
+	v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+		  "Trying to register VPBE display device.\n");
+	v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+		  "layer=%x,layer->video_dev=%x\n",
+		  (int)vpbe_display_layer,
+		  (int)&vpbe_display_layer->video_dev);
+
+	err = video_register_device(&vpbe_display_layer->video_dev,
+				    VFL_TYPE_GRABBER,
+				    -1);
+	if (err)
+		return -ENODEV;
+
+	vpbe_display_layer->disp_dev = disp_dev;
+	/* set the driver data in platform device */
+	platform_set_drvdata(pdev, disp_dev);
+	video_set_drvdata(&vpbe_display_layer->video_dev,
+			  vpbe_display_layer);
+
+	return 0;
+}
+
+
+
+/*
+ * vpbe_display_probe()
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each layer objects
+ */
+static __devinit int vpbe_display_probe(struct platform_device *pdev)
+{
+	struct vpbe_layer *vpbe_display_layer;
+	struct vpbe_display *disp_dev;
+	struct resource *res = NULL;
+	int k;
+	int i;
+	int err;
+	int irq;
+
+	printk(KERN_DEBUG "vpbe_display_probe\n");
+	/* Allocate memory for vpbe_display */
+	disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
+	if (!disp_dev) {
+		printk(KERN_ERR "ran out of memory\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&disp_dev->dma_queue_lock);
+	/*
+	 * Scan all the platform devices to find the vpbe
+	 * controller device and get the vpbe_dev object
+	 */
+	err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
+			vpbe_device_get);
+	if (err < 0)
+		return err;
+	/* Initialize the vpbe display controller */
+	if (NULL != disp_dev->vpbe_dev->ops.initialize) {
+		err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
+							 disp_dev->vpbe_dev);
+		if (err) {
+			v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+					"Error initing vpbe\n");
+			err = -ENOMEM;
+			goto probe_out;
+		}
+	}
+
+	for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+		if (init_vpbe_layer(i, disp_dev, pdev)) {
+			err = -ENODEV;
+			goto probe_out;
+		}
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+			 "Unable to get VENC interrupt resource\n");
+		err = -ENODEV;
+		goto probe_out;
+	}
+
+	irq = res->start;
+	if (request_irq(irq, venc_isr,  IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
+		disp_dev)) {
+		v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+				"Unable to request interrupt\n");
+		err = -ENODEV;
+		goto probe_out;
+	}
+
+	for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+		if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
+			err = -ENODEV;
+			goto probe_out;
+		}
+	}
+
+	printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
+	return 0;
+
+probe_out:
+	free_irq(res->start, disp_dev);
+	for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
+		/* Get the pointer to the layer object */
+		vpbe_display_layer = disp_dev->dev[k];
+		/* Unregister video device */
+		if (vpbe_display_layer) {
+			video_unregister_device(
+				&vpbe_display_layer->video_dev);
+				kfree(disp_dev->dev[k]);
+		}
+	}
+	kfree(disp_dev);
+	return err;
+}
+
+/*
+ * vpbe_display_remove()
+ * It un-register hardware layer from V4L2 driver
+ */
+static int vpbe_display_remove(struct platform_device *pdev)
+{
+	struct vpbe_layer *vpbe_display_layer;
+	struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
+	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+	struct resource *res;
+	int i;
+
+	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
+
+	/* unregister irq */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	free_irq(res->start, disp_dev);
+
+	/* deinitialize the vpbe display controller */
+	if (NULL != vpbe_dev->ops.deinitialize)
+		vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
+	/* un-register device */
+	for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+		/* Get the pointer to the layer object */
+		vpbe_display_layer = disp_dev->dev[i];
+		/* Unregister video device */
+		video_unregister_device(&vpbe_display_layer->video_dev);
+
+	}
+	for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+		kfree(disp_dev->dev[i]);
+		disp_dev->dev[i] = NULL;
+	}
+
+	return 0;
+}
+
+static struct platform_driver vpbe_display_driver = {
+	.driver = {
+		.name = VPBE_DISPLAY_DRIVER,
+		.owner = THIS_MODULE,
+		.bus = &platform_bus_type,
+	},
+	.probe = vpbe_display_probe,
+	.remove = __devexit_p(vpbe_display_remove),
+};
+
+/*
+ * vpbe_display_init()
+ * This function registers device and driver to the kernel, requests irq
+ * handler and allocates memory for layer objects
+ */
+static __devinit int vpbe_display_init(void)
+{
+	int err;
+
+	printk(KERN_DEBUG "vpbe_display_init\n");
+
+	/* Register driver to the kernel */
+	err = platform_driver_register(&vpbe_display_driver);
+	if (0 != err)
+		return err;
+
+	printk(KERN_DEBUG "vpbe_display_init:"
+			"VPBE V4L2 Display Driver V1.0 loaded\n");
+	return 0;
+}
+
+/*
+ * vpbe_display_cleanup()
+ * This function un-registers device and driver to the kernel, frees requested
+ * irq handler and de-allocates memory allocated for layer objects.
+ */
+static void vpbe_display_cleanup(void)
+{
+	printk(KERN_DEBUG "vpbe_display_cleanup\n");
+
+	/* platform driver unregister */
+	platform_driver_unregister(&vpbe_display_driver);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpbe_display_init);
+module_exit(vpbe_display_cleanup);
+
+MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c
new file mode 100644
index 0000000..5352884
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd.c
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (C) 2007-2010 Texas Instruments Inc
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ *
+ * Andy Lowe (alowe@mvista.com), MontaVista Software
+ * - Initial version
+ * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd.
+ * - ported to sub device interface
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <mach/io.h>
+#include <mach/cputype.h>
+#include <mach/hardware.h>
+
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_osd.h>
+
+#include <linux/io.h>
+#include "vpbe_osd_regs.h"
+
+#define MODULE_NAME	VPBE_OSD_SUBDEV_NAME
+
+/* register access routines */
+static inline u32 osd_read(struct osd_state *sd, u32 offset)
+{
+	struct osd_state *osd = sd;
+
+	return readl(osd->osd_base + offset);
+}
+
+static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset)
+{
+	struct osd_state *osd = sd;
+
+	writel(val, osd->osd_base + offset);
+
+	return val;
+}
+
+static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset)
+{
+	struct osd_state *osd = sd;
+
+	u32 addr = osd->osd_base + offset;
+	u32 val = readl(addr) | mask;
+
+	writel(val, addr);
+
+	return val;
+}
+
+static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset)
+{
+	struct osd_state *osd = sd;
+
+	u32 addr = osd->osd_base + offset;
+	u32 val = readl(addr) & ~mask;
+
+	writel(val, addr);
+
+	return val;
+}
+
+static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
+				 u32 offset)
+{
+	struct osd_state *osd = sd;
+
+	u32 addr = osd->osd_base + offset;
+	u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+	writel(new_val, addr);
+
+	return new_val;
+}
+
+/* define some macros for layer and pixfmt classification */
+#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1))
+#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1))
+#define is_rgb_pixfmt(pixfmt) \
+	(((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
+#define is_yc_pixfmt(pixfmt) \
+	(((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \
+	((pixfmt) == PIXFMT_NV12))
+#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
+#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
+
+/**
+ * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446
+ * @sd - ptr to struct osd_state
+ * @field_inversion - inversion flag
+ * @fb_base_phys - frame buffer address
+ * @lconfig - ptr to layer config
+ *
+ * This routine implements a workaround for the field signal inversion silicon
+ * erratum described in Advisory 1.3.8 for the DM6446.  The fb_base_phys and
+ * lconfig parameters apply to the vid0 window.  This routine should be called
+ * whenever the vid0 layer configuration or start address is modified, or when
+ * the OSD field inversion setting is modified.
+ * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or
+ *          0 otherwise
+ */
+static int _osd_dm6446_vid0_pingpong(struct osd_state *sd,
+				     int field_inversion,
+				     unsigned long fb_base_phys,
+				     const struct osd_layer_config *lconfig)
+{
+	struct osd_platform_data *pdata;
+
+	pdata = (struct osd_platform_data *)sd->dev->platform_data;
+	if (pdata->field_inv_wa_enable) {
+
+		if (!field_inversion || !lconfig->interlaced) {
+			osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+			osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR);
+			osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0,
+				   OSD_MISCCTL);
+			return 0;
+		} else {
+			unsigned miscctl = OSD_MISCCTL_PPRV;
+
+			osd_write(sd,
+				(fb_base_phys & ~0x1F) - lconfig->line_length,
+				OSD_VIDWIN0ADR);
+			osd_write(sd,
+				(fb_base_phys & ~0x1F) + lconfig->line_length,
+				OSD_PPVWIN0ADR);
+			osd_modify(sd,
+				OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl,
+				OSD_MISCCTL);
+
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void _osd_set_field_inversion(struct osd_state *sd, int enable)
+{
+	unsigned fsinv = 0;
+
+	if (enable)
+		fsinv = OSD_MODE_FSINV;
+
+	osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE);
+}
+
+static void _osd_set_blink_attribute(struct osd_state *sd, int enable,
+				     enum osd_blink_interval blink)
+{
+	u32 osdatrmd = 0;
+
+	if (enable) {
+		osdatrmd |= OSD_OSDATRMD_BLNK;
+		osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT;
+	}
+	/* caller must ensure that OSD1 is configured in attribute mode */
+	osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd,
+		  OSD_OSDATRMD);
+}
+
+static void _osd_set_rom_clut(struct osd_state *sd,
+			      enum osd_rom_clut rom_clut)
+{
+	if (rom_clut == ROM_CLUT0)
+		osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+	else
+		osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+}
+
+static void _osd_set_palette_map(struct osd_state *sd,
+				 enum osd_win_layer osdwin,
+				 unsigned char pixel_value,
+				 unsigned char clut_index,
+				 enum osd_pix_format pixfmt)
+{
+	static const int map_2bpp[] = { 0, 5, 10, 15 };
+	static const int map_1bpp[] = { 0, 15 };
+	int bmp_offset;
+	int bmp_shift;
+	int bmp_mask;
+	int bmp_reg;
+
+	switch (pixfmt) {
+	case PIXFMT_1BPP:
+		bmp_reg = map_1bpp[pixel_value & 0x1];
+		break;
+	case PIXFMT_2BPP:
+		bmp_reg = map_2bpp[pixel_value & 0x3];
+		break;
+	case PIXFMT_4BPP:
+		bmp_reg = pixel_value & 0xf;
+		break;
+	default:
+		return;
+	}
+
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32);
+		break;
+	case OSDWIN_OSD1:
+		bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32);
+		break;
+	default:
+		return;
+	}
+
+	if (bmp_reg & 1) {
+		bmp_shift = 8;
+		bmp_mask = 0xff << 8;
+	} else {
+		bmp_shift = 0;
+		bmp_mask = 0xff;
+	}
+
+	osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset);
+}
+
+static void _osd_set_rec601_attenuation(struct osd_state *sd,
+					enum osd_win_layer osdwin, int enable)
+{
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
+			  enable ? OSD_OSDWIN0MD_ATN0E : 0,
+			  OSD_OSDWIN0MD);
+		break;
+	case OSDWIN_OSD1:
+		osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
+			  enable ? OSD_OSDWIN1MD_ATN1E : 0,
+			  OSD_OSDWIN1MD);
+		break;
+	}
+}
+
+static void _osd_set_blending_factor(struct osd_state *sd,
+				     enum osd_win_layer osdwin,
+				     enum osd_blending_factor blend)
+{
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		osd_modify(sd, OSD_OSDWIN0MD_BLND0,
+			  blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD);
+		break;
+	case OSDWIN_OSD1:
+		osd_modify(sd, OSD_OSDWIN1MD_BLND1,
+			  blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD);
+		break;
+	}
+}
+
+static void _osd_enable_color_key(struct osd_state *sd,
+				  enum osd_win_layer osdwin,
+				  unsigned colorkey,
+				  enum osd_pix_format pixfmt)
+{
+	switch (pixfmt) {
+	case PIXFMT_RGB565:
+		osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS,
+			  OSD_TRANSPVAL);
+		break;
+	default:
+		break;
+	}
+
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+		break;
+	case OSDWIN_OSD1:
+		osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+		break;
+	}
+}
+
+static void _osd_disable_color_key(struct osd_state *sd,
+				   enum osd_win_layer osdwin)
+{
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+		break;
+	case OSDWIN_OSD1:
+		osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+		break;
+	}
+}
+
+static void _osd_set_osd_clut(struct osd_state *sd,
+			      enum osd_win_layer osdwin,
+			      enum osd_clut clut)
+{
+	u32 winmd = 0;
+
+	switch (osdwin) {
+	case OSDWIN_OSD0:
+		if (clut == RAM_CLUT)
+			winmd |= OSD_OSDWIN0MD_CLUTS0;
+		osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD);
+		break;
+	case OSDWIN_OSD1:
+		if (clut == RAM_CLUT)
+			winmd |= OSD_OSDWIN1MD_CLUTS1;
+		osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD);
+		break;
+	}
+}
+
+static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer,
+			  enum osd_zoom_factor h_zoom,
+			  enum osd_zoom_factor v_zoom)
+{
+	u32 winmd = 0;
+
+	switch (layer) {
+	case WIN_OSD0:
+		winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT);
+		winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT);
+		osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd,
+			  OSD_OSDWIN0MD);
+		break;
+	case WIN_VID0:
+		winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT);
+		winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT);
+		osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd,
+			  OSD_VIDWINMD);
+		break;
+	case WIN_OSD1:
+		winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT);
+		winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT);
+		osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd,
+			  OSD_OSDWIN1MD);
+		break;
+	case WIN_VID1:
+		winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT);
+		winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT);
+		osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd,
+			  OSD_VIDWINMD);
+		break;
+	}
+}
+
+static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	switch (layer) {
+	case WIN_OSD0:
+		osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+		break;
+	case WIN_VID0:
+		osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+		break;
+	case WIN_OSD1:
+		/* disable attribute mode as well as disabling the window */
+		osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+			  OSD_OSDWIN1MD);
+		break;
+	case WIN_VID1:
+		osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+		break;
+	}
+}
+
+static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	if (!win->is_enabled) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return;
+	}
+	win->is_enabled = 0;
+
+	_osd_disable_layer(sd, layer);
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void _osd_enable_attribute_mode(struct osd_state *sd)
+{
+	/* enable attribute mode for OSD1 */
+	osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD);
+}
+
+static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	switch (layer) {
+	case WIN_OSD0:
+		osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+		break;
+	case WIN_VID0:
+		osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+		break;
+	case WIN_OSD1:
+		/* enable OSD1 and disable attribute mode */
+		osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+			  OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD);
+		break;
+	case WIN_VID1:
+		osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+		break;
+	}
+}
+
+static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer,
+			    int otherwin)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	struct osd_layer_config *cfg = &win->lconfig;
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	/*
+	 * use otherwin flag to know this is the other vid window
+	 * in YUV420 mode, if is, skip this check
+	 */
+	if (!otherwin && (!win->is_allocated ||
+			!win->fb_base_phys ||
+			!cfg->line_length ||
+			!cfg->xsize ||
+			!cfg->ysize)) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return -1;
+	}
+
+	if (win->is_enabled) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return 0;
+	}
+	win->is_enabled = 1;
+
+	if (cfg->pixfmt != PIXFMT_OSD_ATTR)
+		_osd_enable_layer(sd, layer);
+	else {
+		_osd_enable_attribute_mode(sd);
+		_osd_set_blink_attribute(sd, osd->is_blinking, osd->blink);
+	}
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+
+	return 0;
+}
+
+static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+			     unsigned long fb_base_phys,
+			     unsigned long cbcr_ofst)
+{
+	switch (layer) {
+	case WIN_OSD0:
+		osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR);
+		break;
+	case WIN_VID0:
+		osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+		break;
+	case WIN_OSD1:
+		osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR);
+		break;
+	case WIN_VID1:
+		osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR);
+		break;
+	}
+}
+
+static void osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+			    unsigned long fb_base_phys,
+			    unsigned long cbcr_ofst)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	struct osd_layer_config *cfg = &win->lconfig;
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	win->fb_base_phys = fb_base_phys & ~0x1F;
+	_osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst);
+
+	if (layer == WIN_VID0) {
+		osd->pingpong =
+		    _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+						       win->fb_base_phys,
+						       cfg);
+	}
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer,
+				 struct osd_layer_config *lconfig)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	*lconfig = win->lconfig;
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+/**
+ * try_layer_config() - Try a specific configuration for the layer
+ * @sd  - ptr to struct osd_state
+ * @layer - layer to configure
+ * @lconfig - layer configuration to try
+ *
+ * If the requested lconfig is completely rejected and the value of lconfig on
+ * exit is the current lconfig, then try_layer_config() returns 1.  Otherwise,
+ * try_layer_config() returns 0.  A return value of 0 does not necessarily mean
+ * that the value of lconfig on exit is identical to the value of lconfig on
+ * entry, but merely that it represents a change from the current lconfig.
+ */
+static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
+			    struct osd_layer_config *lconfig)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	int bad_config;
+
+	/* verify that the pixel format is compatible with the layer */
+	switch (lconfig->pixfmt) {
+	case PIXFMT_1BPP:
+	case PIXFMT_2BPP:
+	case PIXFMT_4BPP:
+	case PIXFMT_8BPP:
+	case PIXFMT_RGB565:
+		bad_config = !is_osd_win(layer);
+		break;
+	case PIXFMT_YCbCrI:
+	case PIXFMT_YCrCbI:
+		bad_config = !is_vid_win(layer);
+		break;
+	case PIXFMT_RGB888:
+		bad_config = !is_vid_win(layer);
+		break;
+	case PIXFMT_NV12:
+		bad_config = 1;
+		break;
+	case PIXFMT_OSD_ATTR:
+		bad_config = (layer != WIN_OSD1);
+		break;
+	default:
+		bad_config = 1;
+		break;
+	}
+	if (bad_config) {
+		/*
+		 * The requested pixel format is incompatible with the layer,
+		 * so keep the current layer configuration.
+		 */
+		*lconfig = win->lconfig;
+		return bad_config;
+	}
+
+	/* DM6446: */
+	/* only one OSD window at a time can use RGB pixel formats */
+	if (is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
+		enum osd_pix_format pixfmt;
+		if (layer == WIN_OSD0)
+			pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt;
+		else
+			pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt;
+
+		if (is_rgb_pixfmt(pixfmt)) {
+			/*
+			 * The other OSD window is already configured for an
+			 * RGB, so keep the current layer configuration.
+			 */
+			*lconfig = win->lconfig;
+			return 1;
+		}
+	}
+
+	/* DM6446: only one video window at a time can use RGB888 */
+	if (is_vid_win(layer) && lconfig->pixfmt == PIXFMT_RGB888) {
+		enum osd_pix_format pixfmt;
+
+		if (layer == WIN_VID0)
+			pixfmt = osd->win[WIN_VID1].lconfig.pixfmt;
+		else
+			pixfmt = osd->win[WIN_VID0].lconfig.pixfmt;
+
+		if (pixfmt == PIXFMT_RGB888) {
+			/*
+			 * The other video window is already configured for
+			 * RGB888, so keep the current layer configuration.
+			 */
+			*lconfig = win->lconfig;
+			return 1;
+		}
+	}
+
+	/* window dimensions must be non-zero */
+	if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) {
+		*lconfig = win->lconfig;
+		return 1;
+	}
+
+	/* round line_length up to a multiple of 32 */
+	lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32;
+	lconfig->line_length =
+	    min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH);
+	lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE);
+	lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE);
+	lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE);
+	lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE);
+	lconfig->interlaced = (lconfig->interlaced != 0);
+	if (lconfig->interlaced) {
+		/* ysize and ypos must be even for interlaced displays */
+		lconfig->ysize &= ~1;
+		lconfig->ypos &= ~1;
+	}
+
+	return 0;
+}
+
+static void _osd_disable_vid_rgb888(struct osd_state *sd)
+{
+	/*
+	 * The DM6446 supports RGB888 pixel format in a single video window.
+	 * This routine disables RGB888 pixel format for both video windows.
+	 * The caller must ensure that neither video window is currently
+	 * configured for RGB888 pixel format.
+	 */
+	osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+}
+
+static void _osd_enable_vid_rgb888(struct osd_state *sd,
+				   enum osd_layer layer)
+{
+	/*
+	 * The DM6446 supports RGB888 pixel format in a single video window.
+	 * This routine enables RGB888 pixel format for the specified video
+	 * window.  The caller must ensure that the other video window is not
+	 * currently configured for RGB888 pixel format, as this routine will
+	 * disable RGB888 pixel format for the other window.
+	 */
+	if (layer == WIN_VID0) {
+		osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+			  OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+	} else if (layer == WIN_VID1) {
+		osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+			  OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+			  OSD_MISCCTL);
+	}
+}
+
+static void _osd_set_cbcr_order(struct osd_state *sd,
+				enum osd_pix_format pixfmt)
+{
+	/*
+	 * The caller must ensure that all windows using YC pixfmt use the same
+	 * Cb/Cr order.
+	 */
+	if (pixfmt == PIXFMT_YCbCrI)
+		osd_clear(sd, OSD_MODE_CS, OSD_MODE);
+	else if (pixfmt == PIXFMT_YCrCbI)
+		osd_set(sd, OSD_MODE_CS, OSD_MODE);
+}
+
+static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+				  const struct osd_layer_config *lconfig)
+{
+	u32 winmd = 0, winmd_mask = 0, bmw = 0;
+
+	_osd_set_cbcr_order(sd, lconfig->pixfmt);
+
+	switch (layer) {
+	case WIN_OSD0:
+		winmd_mask |= OSD_OSDWIN0MD_RGB0E;
+		if (lconfig->pixfmt == PIXFMT_RGB565)
+			winmd |= OSD_OSDWIN0MD_RGB0E;
+
+		winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0;
+
+		switch (lconfig->pixfmt) {
+		case PIXFMT_1BPP:
+			bmw = 0;
+			break;
+		case PIXFMT_2BPP:
+			bmw = 1;
+			break;
+		case PIXFMT_4BPP:
+			bmw = 2;
+			break;
+		case PIXFMT_8BPP:
+			bmw = 3;
+			break;
+		default:
+			break;
+		}
+		winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT);
+
+		if (lconfig->interlaced)
+			winmd |= OSD_OSDWIN0MD_OFF0;
+
+		osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD);
+		osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST);
+		osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP);
+		osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL);
+		if (lconfig->interlaced) {
+			osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP);
+			osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL);
+		} else {
+			osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP);
+			osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL);
+		}
+		break;
+	case WIN_VID0:
+		winmd_mask |= OSD_VIDWINMD_VFF0;
+		if (lconfig->interlaced)
+			winmd |= OSD_VIDWINMD_VFF0;
+
+		osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+		osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST);
+		osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
+		osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
+		/*
+		 * For YUV420P format the register contents are
+		 * duplicated in both VID registers
+		 */
+		if (lconfig->interlaced) {
+			osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP);
+			osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL);
+		} else {
+			osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
+			osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
+		}
+		break;
+	case WIN_OSD1:
+		/*
+		 * The caller must ensure that OSD1 is disabled prior to
+		 * switching from a normal mode to attribute mode or from
+		 * attribute mode to a normal mode.
+		 */
+		if (lconfig->pixfmt == PIXFMT_OSD_ATTR) {
+			winmd_mask |=
+			    OSD_OSDWIN1MD_ATN1E | OSD_OSDWIN1MD_RGB1E |
+			    OSD_OSDWIN1MD_CLUTS1 |
+			    OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1;
+		} else {
+			winmd_mask |= OSD_OSDWIN1MD_RGB1E;
+			if (lconfig->pixfmt == PIXFMT_RGB565)
+				winmd |= OSD_OSDWIN1MD_RGB1E;
+
+			winmd_mask |= OSD_OSDWIN1MD_BMW1;
+			switch (lconfig->pixfmt) {
+			case PIXFMT_1BPP:
+				bmw = 0;
+				break;
+			case PIXFMT_2BPP:
+				bmw = 1;
+				break;
+			case PIXFMT_4BPP:
+				bmw = 2;
+				break;
+			case PIXFMT_8BPP:
+				bmw = 3;
+				break;
+			default:
+				break;
+			}
+			winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT);
+		}
+
+		winmd_mask |= OSD_OSDWIN1MD_OFF1;
+		if (lconfig->interlaced)
+			winmd |= OSD_OSDWIN1MD_OFF1;
+
+		osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD);
+		osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST);
+		osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP);
+		osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL);
+		if (lconfig->interlaced) {
+			osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP);
+			osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL);
+		} else {
+			osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP);
+			osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL);
+		}
+		break;
+	case WIN_VID1:
+		winmd_mask |= OSD_VIDWINMD_VFF1;
+		if (lconfig->interlaced)
+			winmd |= OSD_VIDWINMD_VFF1;
+
+		osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+		osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST);
+		osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
+		osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
+		/*
+		 * For YUV420P format the register contents are
+		 * duplicated in both VID registers
+		 */
+		osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D,
+			   OSD_MISCCTL);
+
+		if (lconfig->interlaced) {
+			osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP);
+			osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL);
+		} else {
+			osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
+			osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
+		}
+		break;
+	}
+}
+
+static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+				struct osd_layer_config *lconfig)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	struct osd_layer_config *cfg = &win->lconfig;
+	unsigned long flags;
+	int reject_config;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	reject_config = try_layer_config(sd, layer, lconfig);
+	if (reject_config) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return reject_config;
+	}
+
+	/* update the current Cb/Cr order */
+	if (is_yc_pixfmt(lconfig->pixfmt))
+		osd->yc_pixfmt = lconfig->pixfmt;
+
+	/*
+	 * If we are switching OSD1 from normal mode to attribute mode or from
+	 * attribute mode to normal mode, then we must disable the window.
+	 */
+	if (layer == WIN_OSD1) {
+		if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+		  (cfg->pixfmt != PIXFMT_OSD_ATTR)) ||
+		  ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+		  (cfg->pixfmt == PIXFMT_OSD_ATTR))) {
+			win->is_enabled = 0;
+			_osd_disable_layer(sd, layer);
+		}
+	}
+
+	_osd_set_layer_config(sd, layer, lconfig);
+
+	if (layer == WIN_OSD1) {
+		struct osd_osdwin_state *osdwin_state =
+		    &osd->osdwin[OSDWIN_OSD1];
+
+		if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+		  (cfg->pixfmt == PIXFMT_OSD_ATTR)) {
+			/*
+			 * We just switched OSD1 from attribute mode to normal
+			 * mode, so we must initialize the CLUT select, the
+			 * blend factor, transparency colorkey enable, and
+			 * attenuation enable (DM6446 only) bits in the
+			 * OSDWIN1MD register.
+			 */
+			_osd_set_osd_clut(sd, OSDWIN_OSD1,
+						   osdwin_state->clut);
+			_osd_set_blending_factor(sd, OSDWIN_OSD1,
+							  osdwin_state->blend);
+			if (osdwin_state->colorkey_blending) {
+				_osd_enable_color_key(sd, OSDWIN_OSD1,
+							       osdwin_state->
+							       colorkey,
+							       lconfig->pixfmt);
+			} else
+				_osd_disable_color_key(sd, OSDWIN_OSD1);
+			_osd_set_rec601_attenuation(sd, OSDWIN_OSD1,
+						    osdwin_state->
+						    rec601_attenuation);
+		} else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+		  (cfg->pixfmt != PIXFMT_OSD_ATTR)) {
+			/*
+			 * We just switched OSD1 from normal mode to attribute
+			 * mode, so we must initialize the blink enable and
+			 * blink interval bits in the OSDATRMD register.
+			 */
+			_osd_set_blink_attribute(sd, osd->is_blinking,
+							  osd->blink);
+		}
+	}
+
+	/*
+	 * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format
+	 * then configure a default palette map.
+	 */
+	if ((lconfig->pixfmt != cfg->pixfmt) &&
+	  ((lconfig->pixfmt == PIXFMT_1BPP) ||
+	  (lconfig->pixfmt == PIXFMT_2BPP) ||
+	  (lconfig->pixfmt == PIXFMT_4BPP))) {
+		enum osd_win_layer osdwin =
+		    ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1);
+		struct osd_osdwin_state *osdwin_state =
+		    &osd->osdwin[osdwin];
+		unsigned char clut_index;
+		unsigned char clut_entries = 0;
+
+		switch (lconfig->pixfmt) {
+		case PIXFMT_1BPP:
+			clut_entries = 2;
+			break;
+		case PIXFMT_2BPP:
+			clut_entries = 4;
+			break;
+		case PIXFMT_4BPP:
+			clut_entries = 16;
+			break;
+		default:
+			break;
+		}
+		/*
+		 * The default palette map maps the pixel value to the clut
+		 * index, i.e. pixel value 0 maps to clut entry 0, pixel value
+		 * 1 maps to clut entry 1, etc.
+		 */
+		for (clut_index = 0; clut_index < 16; clut_index++) {
+			osdwin_state->palette_map[clut_index] = clut_index;
+			if (clut_index < clut_entries) {
+				_osd_set_palette_map(sd, osdwin, clut_index,
+						     clut_index,
+						     lconfig->pixfmt);
+			}
+		}
+	}
+
+	*cfg = *lconfig;
+	/* DM6446: configure the RGB888 enable and window selection */
+	if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888)
+		_osd_enable_vid_rgb888(sd, WIN_VID0);
+	else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888)
+		_osd_enable_vid_rgb888(sd, WIN_VID1);
+	else
+		_osd_disable_vid_rgb888(sd);
+
+	if (layer == WIN_VID0) {
+		osd->pingpong =
+		    _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+						       win->fb_base_phys,
+						       cfg);
+	}
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+
+	return 0;
+}
+
+static void osd_init_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	enum osd_win_layer osdwin;
+	struct osd_osdwin_state *osdwin_state;
+	struct osd_layer_config *cfg = &win->lconfig;
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	win->is_enabled = 0;
+	_osd_disable_layer(sd, layer);
+
+	win->h_zoom = ZOOM_X1;
+	win->v_zoom = ZOOM_X1;
+	_osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom);
+
+	win->fb_base_phys = 0;
+	_osd_start_layer(sd, layer, win->fb_base_phys, 0);
+
+	cfg->line_length = 0;
+	cfg->xsize = 0;
+	cfg->ysize = 0;
+	cfg->xpos = 0;
+	cfg->ypos = 0;
+	cfg->interlaced = 0;
+	switch (layer) {
+	case WIN_OSD0:
+	case WIN_OSD1:
+		osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1;
+		osdwin_state = &osd->osdwin[osdwin];
+		/*
+		 * Other code relies on the fact that OSD windows default to a
+		 * bitmap pixel format when they are deallocated, so don't
+		 * change this default pixel format.
+		 */
+		cfg->pixfmt = PIXFMT_8BPP;
+		_osd_set_layer_config(sd, layer, cfg);
+		osdwin_state->clut = RAM_CLUT;
+		_osd_set_osd_clut(sd, osdwin, osdwin_state->clut);
+		osdwin_state->colorkey_blending = 0;
+		_osd_disable_color_key(sd, osdwin);
+		osdwin_state->blend = OSD_8_VID_0;
+		_osd_set_blending_factor(sd, osdwin, osdwin_state->blend);
+		osdwin_state->rec601_attenuation = 0;
+		_osd_set_rec601_attenuation(sd, osdwin,
+						     osdwin_state->
+						     rec601_attenuation);
+		if (osdwin == OSDWIN_OSD1) {
+			osd->is_blinking = 0;
+			osd->blink = BLINK_X1;
+		}
+		break;
+	case WIN_VID0:
+	case WIN_VID1:
+		cfg->pixfmt = osd->yc_pixfmt;
+		_osd_set_layer_config(sd, layer, cfg);
+		break;
+	}
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_release_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	if (!win->is_allocated) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return;
+	}
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+	osd_init_layer(sd, layer);
+	spin_lock_irqsave(&osd->lock, flags);
+
+	win->is_allocated = 0;
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static int osd_request_layer(struct osd_state *sd, enum osd_layer layer)
+{
+	struct osd_state *osd = sd;
+	struct osd_window_state *win = &osd->win[layer];
+	unsigned long flags;
+
+	spin_lock_irqsave(&osd->lock, flags);
+
+	if (win->is_allocated) {
+		spin_unlock_irqrestore(&osd->lock, flags);
+		return -1;
+	}
+	win->is_allocated = 1;
+
+	spin_unlock_irqrestore(&osd->lock, flags);
+
+	return 0;
+}
+
+static void _osd_init(struct osd_state *sd)
+{
+	osd_write(sd, 0, OSD_MODE);
+	osd_write(sd, 0, OSD_VIDWINMD);
+	osd_write(sd, 0, OSD_OSDWIN0MD);
+	osd_write(sd, 0, OSD_OSDWIN1MD);
+	osd_write(sd, 0, OSD_RECTCUR);
+	osd_write(sd, 0, OSD_MISCCTL);
+}
+
+static void osd_set_left_margin(struct osd_state *sd, u32 val)
+{
+	osd_write(sd, val, OSD_BASEPX);
+}
+
+static void osd_set_top_margin(struct osd_state *sd, u32 val)
+{
+	osd_write(sd, val, OSD_BASEPY);
+}
+
+static int osd_initialize(struct osd_state *osd)
+{
+	if (osd == NULL)
+		return -ENODEV;
+	_osd_init(osd);
+
+	/* set default Cb/Cr order */
+	osd->yc_pixfmt = PIXFMT_YCbCrI;
+
+	_osd_set_field_inversion(osd, osd->field_inversion);
+	_osd_set_rom_clut(osd, osd->rom_clut);
+
+	osd_init_layer(osd, WIN_OSD0);
+	osd_init_layer(osd, WIN_VID0);
+	osd_init_layer(osd, WIN_OSD1);
+	osd_init_layer(osd, WIN_VID1);
+
+	return 0;
+}
+
+static const struct vpbe_osd_ops osd_ops = {
+	.initialize = osd_initialize,
+	.request_layer = osd_request_layer,
+	.release_layer = osd_release_layer,
+	.enable_layer = osd_enable_layer,
+	.disable_layer = osd_disable_layer,
+	.set_layer_config = osd_set_layer_config,
+	.get_layer_config = osd_get_layer_config,
+	.start_layer = osd_start_layer,
+	.set_left_margin = osd_set_left_margin,
+	.set_top_margin = osd_set_top_margin,
+};
+
+static int osd_probe(struct platform_device *pdev)
+{
+	struct osd_platform_data *pdata;
+	struct osd_state *osd;
+	struct resource *res;
+	int ret = 0;
+
+	osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
+	if (osd == NULL)
+		return -ENOMEM;
+
+	osd->dev = &pdev->dev;
+	pdata = (struct osd_platform_data *)pdev->dev.platform_data;
+	osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type;
+	if (NULL == pdev->dev.platform_data) {
+		dev_err(osd->dev, "No platform data defined for OSD"
+			" sub device\n");
+		ret = -ENOENT;
+		goto free_mem;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(osd->dev, "Unable to get OSD register address map\n");
+		ret = -ENODEV;
+		goto free_mem;
+	}
+	osd->osd_base_phys = res->start;
+	osd->osd_size = res->end - res->start + 1;
+	if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
+				MODULE_NAME)) {
+		dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
+		ret = -ENODEV;
+		goto free_mem;
+	}
+	osd->osd_base = (unsigned long)ioremap_nocache(res->start,
+							osd->osd_size);
+	if (!osd->osd_base) {
+		dev_err(osd->dev, "Unable to map the OSD region\n");
+		ret = -ENODEV;
+		goto release_mem_region;
+	}
+	spin_lock_init(&osd->lock);
+	osd->ops = osd_ops;
+	platform_set_drvdata(pdev, osd);
+	dev_notice(osd->dev, "OSD sub device probe success\n");
+	return ret;
+
+release_mem_region:
+	release_mem_region(osd->osd_base_phys, osd->osd_size);
+free_mem:
+	kfree(osd);
+	return ret;
+}
+
+static int osd_remove(struct platform_device *pdev)
+{
+	struct osd_state *osd = platform_get_drvdata(pdev);
+
+	iounmap((void *)osd->osd_base);
+	release_mem_region(osd->osd_base_phys, osd->osd_size);
+	kfree(osd);
+	return 0;
+}
+
+static struct platform_driver osd_driver = {
+	.probe		= osd_probe,
+	.remove		= osd_remove,
+	.driver		= {
+		.name	= MODULE_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int osd_init(void)
+{
+	if (platform_driver_register(&osd_driver)) {
+		printk(KERN_ERR "Unable to register davinci osd driver\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void osd_exit(void)
+{
+	platform_driver_unregister(&osd_driver);
+}
+
+module_init(osd_init);
+module_exit(osd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DaVinci OSD Manager Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h
new file mode 100644
index 0000000..584520f
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd_regs.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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
+ */
+#ifndef _VPBE_OSD_REGS_H
+#define _VPBE_OSD_REGS_H
+
+/* VPBE Global Registers */
+#define VPBE_PID				0x0
+#define VPBE_PCR				0x4
+
+/* VPSS CLock Registers */
+#define VPSSCLK_PID				0x00
+#define VPSSCLK_CLKCTRL				0x04
+
+/* VPSS Buffer Logic Registers */
+#define VPSSBL_PID				0x00
+#define VPSSBL_PCR				0x04
+#define VPSSBL_BCR				0x08
+#define VPSSBL_INTSTAT				0x0C
+#define VPSSBL_INTSEL				0x10
+#define VPSSBL_EVTSEL				0x14
+#define VPSSBL_MEMCTRL				0x18
+#define VPSSBL_CCDCMUX				0x1C
+
+/* DM365 ISP5 system configuration */
+#define ISP5_PID				0x0
+#define ISP5_PCCR				0x4
+#define ISP5_BCR				0x8
+#define ISP5_INTSTAT				0xC
+#define ISP5_INTSEL1				0x10
+#define ISP5_INTSEL2				0x14
+#define ISP5_INTSEL3				0x18
+#define ISP5_EVTSEL				0x1c
+#define ISP5_CCDCMUX				0x20
+
+/* VPBE On-Screen Display Subsystem Registers (OSD) */
+#define OSD_MODE				0x00
+#define OSD_VIDWINMD				0x04
+#define OSD_OSDWIN0MD				0x08
+#define OSD_OSDWIN1MD				0x0C
+#define OSD_OSDATRMD				0x0C
+#define OSD_RECTCUR				0x10
+#define OSD_VIDWIN0OFST				0x18
+#define OSD_VIDWIN1OFST				0x1C
+#define OSD_OSDWIN0OFST				0x20
+#define OSD_OSDWIN1OFST				0x24
+#define OSD_VIDWINADH				0x28
+#define OSD_VIDWIN0ADL				0x2C
+#define OSD_VIDWIN0ADR				0x2C
+#define OSD_VIDWIN1ADL				0x30
+#define OSD_VIDWIN1ADR				0x30
+#define OSD_OSDWINADH				0x34
+#define OSD_OSDWIN0ADL				0x38
+#define OSD_OSDWIN0ADR				0x38
+#define OSD_OSDWIN1ADL				0x3C
+#define OSD_OSDWIN1ADR				0x3C
+#define OSD_BASEPX				0x40
+#define OSD_BASEPY				0x44
+#define OSD_VIDWIN0XP				0x48
+#define OSD_VIDWIN0YP				0x4C
+#define OSD_VIDWIN0XL				0x50
+#define OSD_VIDWIN0YL				0x54
+#define OSD_VIDWIN1XP				0x58
+#define OSD_VIDWIN1YP				0x5C
+#define OSD_VIDWIN1XL				0x60
+#define OSD_VIDWIN1YL				0x64
+#define OSD_OSDWIN0XP				0x68
+#define OSD_OSDWIN0YP				0x6C
+#define OSD_OSDWIN0XL				0x70
+#define OSD_OSDWIN0YL				0x74
+#define OSD_OSDWIN1XP				0x78
+#define OSD_OSDWIN1YP				0x7C
+#define OSD_OSDWIN1XL				0x80
+#define OSD_OSDWIN1YL				0x84
+#define OSD_CURXP				0x88
+#define OSD_CURYP				0x8C
+#define OSD_CURXL				0x90
+#define OSD_CURYL				0x94
+#define OSD_W0BMP01				0xA0
+#define OSD_W0BMP23				0xA4
+#define OSD_W0BMP45				0xA8
+#define OSD_W0BMP67				0xAC
+#define OSD_W0BMP89				0xB0
+#define OSD_W0BMPAB				0xB4
+#define OSD_W0BMPCD				0xB8
+#define OSD_W0BMPEF				0xBC
+#define OSD_W1BMP01				0xC0
+#define OSD_W1BMP23				0xC4
+#define OSD_W1BMP45				0xC8
+#define OSD_W1BMP67				0xCC
+#define OSD_W1BMP89				0xD0
+#define OSD_W1BMPAB				0xD4
+#define OSD_W1BMPCD				0xD8
+#define OSD_W1BMPEF				0xDC
+#define OSD_VBNDRY				0xE0
+#define OSD_EXTMODE				0xE4
+#define OSD_MISCCTL				0xE8
+#define OSD_CLUTRAMYCB				0xEC
+#define OSD_CLUTRAMCR				0xF0
+#define OSD_TRANSPVAL				0xF4
+#define OSD_TRANSPVALL				0xF4
+#define OSD_TRANSPVALU				0xF8
+#define OSD_TRANSPBMPIDX			0xFC
+#define OSD_PPVWIN0ADR				0xFC
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV			(1 << 1)
+#define VPBE_PCR_CLK_OFF			(1 << 0)
+
+#define VPSSBL_INTSTAT_HSSIINT			(1 << 14)
+#define VPSSBL_INTSTAT_CFALDINT			(1 << 13)
+#define VPSSBL_INTSTAT_IPIPE_INT5		(1 << 12)
+#define VPSSBL_INTSTAT_IPIPE_INT4		(1 << 11)
+#define VPSSBL_INTSTAT_IPIPE_INT3		(1 << 10)
+#define VPSSBL_INTSTAT_IPIPE_INT2		(1 << 9)
+#define VPSSBL_INTSTAT_IPIPE_INT1		(1 << 8)
+#define VPSSBL_INTSTAT_IPIPE_INT0		(1 << 7)
+#define VPSSBL_INTSTAT_IPIPEIFINT		(1 << 6)
+#define VPSSBL_INTSTAT_OSDINT			(1 << 5)
+#define VPSSBL_INTSTAT_VENCINT			(1 << 4)
+#define VPSSBL_INTSTAT_H3AINT			(1 << 3)
+#define VPSSBL_INTSTAT_CCDC_VDINT2		(1 << 2)
+#define VPSSBL_INTSTAT_CCDC_VDINT1		(1 << 1)
+#define VPSSBL_INTSTAT_CCDC_VDINT0		(1 << 0)
+
+/* DM365 ISP5 bit definitions */
+#define ISP5_INTSTAT_VENCINT			(1 << 21)
+#define ISP5_INTSTAT_OSDINT			(1 << 20)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC				0
+#define SDTV_PAL				1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P				0
+#define HDTV_625P				1
+#define HDTV_1080I				2
+#define HDTV_720P				3
+
+#define OSD_MODE_CS				(1 << 15)
+#define OSD_MODE_OVRSZ				(1 << 14)
+#define OSD_MODE_OHRSZ				(1 << 13)
+#define OSD_MODE_EF				(1 << 12)
+#define OSD_MODE_VVRSZ				(1 << 11)
+#define OSD_MODE_VHRSZ				(1 << 10)
+#define OSD_MODE_FSINV				(1 << 9)
+#define OSD_MODE_BCLUT				(1 << 8)
+#define OSD_MODE_CABG_SHIFT			0
+#define OSD_MODE_CABG				(0xff << 0)
+
+#define OSD_VIDWINMD_VFINV			(1 << 15)
+#define OSD_VIDWINMD_V1EFC			(1 << 14)
+#define OSD_VIDWINMD_VHZ1_SHIFT			12
+#define OSD_VIDWINMD_VHZ1			(3 << 12)
+#define OSD_VIDWINMD_VVZ1_SHIFT			10
+#define OSD_VIDWINMD_VVZ1			(3 << 10)
+#define OSD_VIDWINMD_VFF1			(1 << 9)
+#define OSD_VIDWINMD_ACT1			(1 << 8)
+#define OSD_VIDWINMD_V0EFC			(1 << 6)
+#define OSD_VIDWINMD_VHZ0_SHIFT			4
+#define OSD_VIDWINMD_VHZ0			(3 << 4)
+#define OSD_VIDWINMD_VVZ0_SHIFT			2
+#define OSD_VIDWINMD_VVZ0			(3 << 2)
+#define OSD_VIDWINMD_VFF0			(1 << 1)
+#define OSD_VIDWINMD_ACT0			(1 << 0)
+
+#define OSD_OSDWIN0MD_ATN0E			(1 << 14)
+#define OSD_OSDWIN0MD_RGB0E			(1 << 13)
+#define OSD_OSDWIN0MD_BMP0MD_SHIFT		13
+#define OSD_OSDWIN0MD_BMP0MD			(3 << 13)
+#define OSD_OSDWIN0MD_CLUTS0			(1 << 12)
+#define OSD_OSDWIN0MD_OHZ0_SHIFT		10
+#define OSD_OSDWIN0MD_OHZ0			(3 << 10)
+#define OSD_OSDWIN0MD_OVZ0_SHIFT		8
+#define OSD_OSDWIN0MD_OVZ0			(3 << 8)
+#define OSD_OSDWIN0MD_BMW0_SHIFT		6
+#define OSD_OSDWIN0MD_BMW0			(3 << 6)
+#define OSD_OSDWIN0MD_BLND0_SHIFT		3
+#define OSD_OSDWIN0MD_BLND0			(7 << 3)
+#define OSD_OSDWIN0MD_TE0			(1 << 2)
+#define OSD_OSDWIN0MD_OFF0			(1 << 1)
+#define OSD_OSDWIN0MD_OACT0			(1 << 0)
+
+#define OSD_OSDWIN1MD_OASW			(1 << 15)
+#define OSD_OSDWIN1MD_ATN1E			(1 << 14)
+#define OSD_OSDWIN1MD_RGB1E			(1 << 13)
+#define OSD_OSDWIN1MD_BMP1MD_SHIFT		13
+#define OSD_OSDWIN1MD_BMP1MD			(3 << 13)
+#define OSD_OSDWIN1MD_CLUTS1			(1 << 12)
+#define OSD_OSDWIN1MD_OHZ1_SHIFT		10
+#define OSD_OSDWIN1MD_OHZ1			(3 << 10)
+#define OSD_OSDWIN1MD_OVZ1_SHIFT		8
+#define OSD_OSDWIN1MD_OVZ1			(3 << 8)
+#define OSD_OSDWIN1MD_BMW1_SHIFT		6
+#define OSD_OSDWIN1MD_BMW1			(3 << 6)
+#define OSD_OSDWIN1MD_BLND1_SHIFT		3
+#define OSD_OSDWIN1MD_BLND1			(7 << 3)
+#define OSD_OSDWIN1MD_TE1			(1 << 2)
+#define OSD_OSDWIN1MD_OFF1			(1 << 1)
+#define OSD_OSDWIN1MD_OACT1			(1 << 0)
+
+#define OSD_OSDATRMD_OASW			(1 << 15)
+#define OSD_OSDATRMD_OHZA_SHIFT			10
+#define OSD_OSDATRMD_OHZA			(3 << 10)
+#define OSD_OSDATRMD_OVZA_SHIFT			8
+#define OSD_OSDATRMD_OVZA			(3 << 8)
+#define OSD_OSDATRMD_BLNKINT_SHIFT		6
+#define OSD_OSDATRMD_BLNKINT			(3 << 6)
+#define OSD_OSDATRMD_OFFA			(1 << 1)
+#define OSD_OSDATRMD_BLNK			(1 << 0)
+
+#define OSD_RECTCUR_RCAD_SHIFT			8
+#define OSD_RECTCUR_RCAD			(0xff << 8)
+#define OSD_RECTCUR_CLUTSR			(1 << 7)
+#define OSD_RECTCUR_RCHW_SHIFT			4
+#define OSD_RECTCUR_RCHW			(7 << 4)
+#define OSD_RECTCUR_RCVW_SHIFT			1
+#define OSD_RECTCUR_RCVW			(7 << 1)
+#define OSD_RECTCUR_RCACT			(1 << 0)
+
+#define OSD_VIDWIN0OFST_V0LO			(0x1ff << 0)
+
+#define OSD_VIDWIN1OFST_V1LO			(0x1ff << 0)
+
+#define OSD_OSDWIN0OFST_O0LO			(0x1ff << 0)
+
+#define OSD_OSDWIN1OFST_O1LO			(0x1ff << 0)
+
+#define OSD_WINOFST_AH_SHIFT			9
+
+#define OSD_VIDWIN0OFST_V0AH			(0xf << 9)
+#define OSD_VIDWIN1OFST_V1AH			(0xf << 9)
+#define OSD_OSDWIN0OFST_O0AH			(0xf << 9)
+#define OSD_OSDWIN1OFST_O1AH			(0xf << 9)
+
+#define OSD_VIDWINADH_V1AH_SHIFT		8
+#define OSD_VIDWINADH_V1AH			(0x7f << 8)
+#define OSD_VIDWINADH_V0AH_SHIFT		0
+#define OSD_VIDWINADH_V0AH			(0x7f << 0)
+
+#define OSD_VIDWIN0ADL_V0AL			(0xffff << 0)
+
+#define OSD_VIDWIN1ADL_V1AL			(0xffff << 0)
+
+#define OSD_OSDWINADH_O1AH_SHIFT		8
+#define OSD_OSDWINADH_O1AH			(0x7f << 8)
+#define OSD_OSDWINADH_O0AH_SHIFT		0
+#define OSD_OSDWINADH_O0AH			(0x7f << 0)
+
+#define OSD_OSDWIN0ADL_O0AL			(0xffff << 0)
+
+#define OSD_OSDWIN1ADL_O1AL			(0xffff << 0)
+
+#define OSD_BASEPX_BPX				(0x3ff << 0)
+
+#define OSD_BASEPY_BPY				(0x1ff << 0)
+
+#define OSD_VIDWIN0XP_V0X			(0x7ff << 0)
+
+#define OSD_VIDWIN0YP_V0Y			(0x7ff << 0)
+
+#define OSD_VIDWIN0XL_V0W			(0x7ff << 0)
+
+#define OSD_VIDWIN0YL_V0H			(0x7ff << 0)
+
+#define OSD_VIDWIN1XP_V1X			(0x7ff << 0)
+
+#define OSD_VIDWIN1YP_V1Y			(0x7ff << 0)
+
+#define OSD_VIDWIN1XL_V1W			(0x7ff << 0)
+
+#define OSD_VIDWIN1YL_V1H			(0x7ff << 0)
+
+#define OSD_OSDWIN0XP_W0X			(0x7ff << 0)
+
+#define OSD_OSDWIN0YP_W0Y			(0x7ff << 0)
+
+#define OSD_OSDWIN0XL_W0W			(0x7ff << 0)
+
+#define OSD_OSDWIN0YL_W0H			(0x7ff << 0)
+
+#define OSD_OSDWIN1XP_W1X			(0x7ff << 0)
+
+#define OSD_OSDWIN1YP_W1Y			(0x7ff << 0)
+
+#define OSD_OSDWIN1XL_W1W			(0x7ff << 0)
+
+#define OSD_OSDWIN1YL_W1H			(0x7ff << 0)
+
+#define OSD_CURXP_RCSX				(0x7ff << 0)
+
+#define OSD_CURYP_RCSY				(0x7ff << 0)
+
+#define OSD_CURXL_RCSW				(0x7ff << 0)
+
+#define OSD_CURYL_RCSH				(0x7ff << 0)
+
+#define OSD_EXTMODE_EXPMDSEL			(1 << 15)
+#define OSD_EXTMODE_SCRNHEXP_SHIFT		13
+#define OSD_EXTMODE_SCRNHEXP			(3 << 13)
+#define OSD_EXTMODE_SCRNVEXP			(1 << 12)
+#define OSD_EXTMODE_OSD1BLDCHR			(1 << 11)
+#define OSD_EXTMODE_OSD0BLDCHR			(1 << 10)
+#define OSD_EXTMODE_ATNOSD1EN			(1 << 9)
+#define OSD_EXTMODE_ATNOSD0EN			(1 << 8)
+#define OSD_EXTMODE_OSDHRSZ15			(1 << 7)
+#define OSD_EXTMODE_VIDHRSZ15			(1 << 6)
+#define OSD_EXTMODE_ZMFILV1HEN			(1 << 5)
+#define OSD_EXTMODE_ZMFILV1VEN			(1 << 4)
+#define OSD_EXTMODE_ZMFILV0HEN			(1 << 3)
+#define OSD_EXTMODE_ZMFILV0VEN			(1 << 2)
+#define OSD_EXTMODE_EXPFILHEN			(1 << 1)
+#define OSD_EXTMODE_EXPFILVEN			(1 << 0)
+
+#define OSD_MISCCTL_BLDSEL			(1 << 15)
+#define OSD_MISCCTL_S420D			(1 << 14)
+#define OSD_MISCCTL_BMAPT			(1 << 13)
+#define OSD_MISCCTL_DM365M			(1 << 12)
+#define OSD_MISCCTL_RGBEN			(1 << 7)
+#define OSD_MISCCTL_RGBWIN			(1 << 6)
+#define OSD_MISCCTL_DMANG			(1 << 6)
+#define OSD_MISCCTL_TMON			(1 << 5)
+#define OSD_MISCCTL_RSEL			(1 << 4)
+#define OSD_MISCCTL_CPBSY			(1 << 3)
+#define OSD_MISCCTL_PPSW			(1 << 2)
+#define OSD_MISCCTL_PPRV			(1 << 1)
+
+#define OSD_CLUTRAMYCB_Y_SHIFT			8
+#define OSD_CLUTRAMYCB_Y			(0xff << 8)
+#define OSD_CLUTRAMYCB_CB_SHIFT			0
+#define OSD_CLUTRAMYCB_CB			(0xff << 0)
+
+#define OSD_CLUTRAMCR_CR_SHIFT			8
+#define OSD_CLUTRAMCR_CR			(0xff << 8)
+#define OSD_CLUTRAMCR_CADDR_SHIFT		0
+#define OSD_CLUTRAMCR_CADDR			(0xff << 0)
+
+#define OSD_TRANSPVAL_RGBTRANS			(0xffff << 0)
+
+#define OSD_TRANSPVALL_RGBL			(0xffff << 0)
+
+#define OSD_TRANSPVALU_Y_SHIFT			8
+#define OSD_TRANSPVALU_Y			(0xff << 8)
+#define OSD_TRANSPVALU_RGBU_SHIFT		0
+#define OSD_TRANSPVALU_RGBU			(0xff << 0)
+
+#define OSD_TRANSPBMPIDX_BMP1_SHIFT		8
+#define OSD_TRANSPBMPIDX_BMP1			(0xff << 8)
+#define OSD_TRANSPBMPIDX_BMP0_SHIFT		0
+#define OSD_TRANSPBMPIDX_BMP0			0xff
+
+#endif				/* _DAVINCI_VPBE_H_ */
diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c
new file mode 100644
index 0000000..03a3e5c
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/mux.h>
+#include <mach/io.h>
+#include <mach/i2c.h>
+
+#include <linux/io.h>
+
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+
+#include "vpbe_venc_regs.h"
+
+#define MODULE_NAME	VPBE_VENC_SUBDEV_NAME
+
+static int debug = 2;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-2");
+
+struct venc_state {
+	struct v4l2_subdev sd;
+	struct venc_callback *callback;
+	struct venc_platform_data *pdata;
+	struct device *pdev;
+	u32 output;
+	v4l2_std_id std;
+	spinlock_t lock;
+	void __iomem *venc_base;
+	void __iomem *vdaccfg_reg;
+};
+
+static inline struct venc_state *to_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct venc_state, sd);
+}
+
+static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
+{
+	struct venc_state *venc = to_state(sd);
+
+	return readl(venc->venc_base + offset);
+}
+
+static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
+{
+	struct venc_state *venc = to_state(sd);
+
+	writel(val, (venc->venc_base + offset));
+
+	return val;
+}
+
+static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
+				 u32 val, u32 mask)
+{
+	u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
+
+	venc_write(sd, offset, new_val);
+
+	return new_val;
+}
+
+static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
+{
+	struct venc_state *venc = to_state(sd);
+
+	writel(val, venc->vdaccfg_reg);
+
+	val = readl(venc->vdaccfg_reg);
+
+	return val;
+}
+
+/* This function sets the dac of the VPBE for various outputs
+ */
+static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
+{
+	switch (out_index) {
+	case 0:
+		v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
+		venc_write(sd, VENC_DACSEL, 0);
+		break;
+	case 1:
+		v4l2_dbg(debug, 1, sd, "Setting output to S-Video\n");
+		venc_write(sd, VENC_DACSEL, 0x210);
+		break;
+	case  2:
+		venc_write(sd, VENC_DACSEL, 0x543);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
+{
+	v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
+
+	if (benable) {
+		venc_write(sd, VENC_VMOD, 0);
+		venc_write(sd, VENC_CVBS, 0);
+		venc_write(sd, VENC_LCDOUT, 0);
+		venc_write(sd, VENC_HSPLS, 0);
+		venc_write(sd, VENC_HSTART, 0);
+		venc_write(sd, VENC_HVALID, 0);
+		venc_write(sd, VENC_HINT, 0);
+		venc_write(sd, VENC_VSPLS, 0);
+		venc_write(sd, VENC_VSTART, 0);
+		venc_write(sd, VENC_VVALID, 0);
+		venc_write(sd, VENC_VINT, 0);
+		venc_write(sd, VENC_YCCCTL, 0);
+		venc_write(sd, VENC_DACSEL, 0);
+
+	} else {
+		venc_write(sd, VENC_VMOD, 0);
+		/* disable VCLK output pin enable */
+		venc_write(sd, VENC_VIDCTL, 0x141);
+
+		/* Disable output sync pins */
+		venc_write(sd, VENC_SYNCCTL, 0);
+
+		/* Disable DCLOCK */
+		venc_write(sd, VENC_DCLKCTL, 0);
+		venc_write(sd, VENC_DRGBX1, 0x0000057C);
+
+		/* Disable LCD output control (accepting default polarity) */
+		venc_write(sd, VENC_LCDOUT, 0);
+		venc_write(sd, VENC_CMPNT, 0x100);
+		venc_write(sd, VENC_HSPLS, 0);
+		venc_write(sd, VENC_HINT, 0);
+		venc_write(sd, VENC_HSTART, 0);
+		venc_write(sd, VENC_HVALID, 0);
+
+		venc_write(sd, VENC_VSPLS, 0);
+		venc_write(sd, VENC_VINT, 0);
+		venc_write(sd, VENC_VSTART, 0);
+		venc_write(sd, VENC_VVALID, 0);
+
+		venc_write(sd, VENC_HSDLY, 0);
+		venc_write(sd, VENC_VSDLY, 0);
+
+		venc_write(sd, VENC_YCCCTL, 0);
+		venc_write(sd, VENC_VSTARTA, 0);
+
+		/* Set OSD clock and OSD Sync Adavance registers */
+		venc_write(sd, VENC_OSDCLK0, 1);
+		venc_write(sd, VENC_OSDCLK1, 2);
+	}
+}
+
+/*
+ * setting NTSC mode
+ */
+static int venc_set_ntsc(struct v4l2_subdev *sd)
+{
+	struct venc_state *venc = to_state(sd);
+	struct venc_platform_data *pdata = venc->pdata;
+
+	v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
+
+	/* Setup clock at VPSS & VENC for SD */
+	vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+	if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
+		return -EINVAL;
+
+	venc_enabledigitaloutput(sd, 0);
+
+	/* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+	venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+	/* Set REC656 Mode */
+	venc_write(sd, VENC_YCCCTL, 0x1);
+	venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
+	venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
+
+	venc_write(sd, VENC_VMOD, 0);
+	venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+			VENC_VMOD_VIE);
+	venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+	venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
+			VENC_VMOD_TVTYP);
+	venc_write(sd, VENC_DACTST, 0x0);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+	return 0;
+}
+
+/*
+ * setting PAL mode
+ */
+static int venc_set_pal(struct v4l2_subdev *sd)
+{
+	struct venc_state *venc = to_state(sd);
+
+	v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
+
+	/* Setup clock at VPSS & VENC for SD */
+	vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+	if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
+		return -EINVAL;
+
+	venc_enabledigitaloutput(sd, 0);
+
+	/* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+	venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+	/* Set REC656 Mode */
+	venc_write(sd, VENC_YCCCTL, 0x1);
+
+	venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
+			VENC_SYNCCTL_OVD);
+	venc_write(sd, VENC_VMOD, 0);
+	venc_modify(sd, VENC_VMOD,
+			(1 << VENC_VMOD_VIE_SHIFT),
+			VENC_VMOD_VIE);
+	venc_modify(sd, VENC_VMOD,
+			(0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+	venc_modify(sd, VENC_VMOD,
+			(1 << VENC_VMOD_TVTYP_SHIFT),
+			VENC_VMOD_TVTYP);
+	venc_write(sd, VENC_DACTST, 0x0);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+	return 0;
+}
+
+/*
+ * venc_set_480p59_94
+ *
+ * This function configures the video encoder to EDTV(525p) component setting.
+ */
+static int venc_set_480p59_94(struct v4l2_subdev *sd)
+{
+	struct venc_state *venc = to_state(sd);
+	struct venc_platform_data *pdata = venc->pdata;
+
+	v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
+
+	/* Setup clock at VPSS & VENC for SD */
+	if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0)
+		return -EINVAL;
+
+	venc_enabledigitaloutput(sd, 0);
+
+	venc_write(sd, VENC_OSDCLK0, 0);
+	venc_write(sd, VENC_OSDCLK1, 1);
+	venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+		    VENC_VDPRO_DAFRQ);
+	venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+		    VENC_VDPRO_DAUPS);
+	venc_write(sd, VENC_VMOD, 0);
+	venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+		    VENC_VMOD_VIE);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+	venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
+		    VENC_VMOD_TVTYP);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+		    VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+	return 0;
+}
+
+/*
+ * venc_set_625p
+ *
+ * This function configures the video encoder to HDTV(625p) component setting
+ */
+static int venc_set_576p50(struct v4l2_subdev *sd)
+{
+	struct venc_state *venc = to_state(sd);
+	struct venc_platform_data *pdata = venc->pdata;
+
+	v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
+
+	/* Setup clock at VPSS & VENC for SD */
+	if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0)
+		return -EINVAL;
+
+	venc_enabledigitaloutput(sd, 0);
+
+	venc_write(sd, VENC_OSDCLK0, 0);
+	venc_write(sd, VENC_OSDCLK1, 1);
+
+	venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+		    VENC_VDPRO_DAFRQ);
+	venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+		    VENC_VDPRO_DAUPS);
+
+	venc_write(sd, VENC_VMOD, 0);
+	venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+		    VENC_VMOD_VIE);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+	venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
+		    VENC_VMOD_TVTYP);
+
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+		    VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+	venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+	return 0;
+}
+
+static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+	v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
+
+	if (norm & V4L2_STD_525_60)
+		return venc_set_ntsc(sd);
+	else if (norm & V4L2_STD_625_50)
+		return venc_set_pal(sd);
+
+	return -EINVAL;
+}
+
+static int venc_s_dv_preset(struct v4l2_subdev *sd,
+			    struct v4l2_dv_preset *dv_preset)
+{
+	v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n");
+
+	if (dv_preset->preset == V4L2_DV_576P50)
+		return venc_set_576p50(sd);
+	else if (dv_preset->preset == V4L2_DV_480P59_94)
+		return venc_set_480p59_94(sd);
+
+	return -EINVAL;
+}
+
+static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+			  u32 config)
+{
+	struct venc_state *venc = to_state(sd);
+	int ret;
+
+	v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
+
+	ret = venc_set_dac(sd, output);
+	if (!ret)
+		venc->output = output;
+
+	return ret;
+}
+
+static long venc_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd,
+			void *arg)
+{
+	u32 val;
+
+	switch (cmd) {
+	case VENC_GET_FLD:
+		val = venc_read(sd, VENC_VSTAT);
+		*((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
+		VENC_VSTAT_FIDST);
+		break;
+	default:
+		v4l2_err(sd, "Wrong IOCTL cmd\n");
+		break;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops venc_core_ops = {
+	.ioctl      = venc_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops venc_video_ops = {
+	.s_routing = venc_s_routing,
+	.s_std_output = venc_s_std_output,
+	.s_dv_preset = venc_s_dv_preset,
+};
+
+static const struct v4l2_subdev_ops venc_ops = {
+	.core = &venc_core_ops,
+	.video = &venc_video_ops,
+};
+
+static int venc_initialize(struct v4l2_subdev *sd)
+{
+	struct venc_state *venc = to_state(sd);
+	int ret;
+
+	/* Set default to output to composite and std to NTSC */
+	venc->output = 0;
+	venc->std = V4L2_STD_525_60;
+
+	ret = venc_s_routing(sd, 0, venc->output, 0);
+	if (ret < 0) {
+		v4l2_err(sd, "Error setting output during init\n");
+		return -EINVAL;
+	}
+
+	ret = venc_s_std_output(sd, venc->std);
+	if (ret < 0) {
+		v4l2_err(sd, "Error setting std during init\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int venc_device_get(struct device *dev, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct venc_state **venc = data;
+
+	if (strcmp(MODULE_NAME, pdev->name) == 0)
+		*venc = platform_get_drvdata(pdev);
+
+	return 0;
+}
+
+struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
+		const char *venc_name)
+{
+	struct venc_state *venc;
+	int err;
+
+	err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
+			venc_device_get);
+	if (venc == NULL)
+		return NULL;
+
+	v4l2_subdev_init(&venc->sd, &venc_ops);
+
+	strcpy(venc->sd.name, venc_name);
+	if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
+		v4l2_err(v4l2_dev,
+			"vpbe unable to register venc sub device\n");
+		return NULL;
+	}
+	if (venc_initialize(&venc->sd)) {
+		v4l2_err(v4l2_dev,
+			"vpbe venc initialization failed\n");
+		return NULL;
+	}
+
+	return &venc->sd;
+}
+EXPORT_SYMBOL(venc_sub_dev_init);
+
+static int venc_probe(struct platform_device *pdev)
+{
+	struct venc_state *venc;
+	struct resource *res;
+	int ret;
+
+	venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+	if (venc == NULL)
+		return -ENOMEM;
+
+	venc->pdev = &pdev->dev;
+	venc->pdata = pdev->dev.platform_data;
+	if (NULL == venc->pdata) {
+		dev_err(venc->pdev, "Unable to get platform data for"
+			" VENC sub device");
+		ret = -ENOENT;
+		goto free_mem;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(venc->pdev,
+			"Unable to get VENC register address map\n");
+		ret = -ENODEV;
+		goto free_mem;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), "venc")) {
+		dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
+		ret = -ENODEV;
+		goto free_mem;
+	}
+
+	venc->venc_base = ioremap_nocache(res->start, resource_size(res));
+	if (!venc->venc_base) {
+		dev_err(venc->pdev, "Unable to map VENC IO space\n");
+		ret = -ENODEV;
+		goto release_venc_mem_region;
+	}
+
+	spin_lock_init(&venc->lock);
+	platform_set_drvdata(pdev, venc);
+	dev_notice(venc->pdev, "VENC sub device probe success\n");
+	return 0;
+
+release_venc_mem_region:
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+free_mem:
+	kfree(venc);
+	return ret;
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+	struct venc_state *venc = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	iounmap((void *)venc->venc_base);
+	release_mem_region(res->start, resource_size(res));
+	kfree(venc);
+
+	return 0;
+}
+
+static struct platform_driver venc_driver = {
+	.probe		= venc_probe,
+	.remove		= venc_remove,
+	.driver		= {
+		.name	= MODULE_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int venc_init(void)
+{
+	if (platform_driver_register(&venc_driver)) {
+		printk(KERN_ERR "Unable to register venc driver\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void venc_exit(void)
+{
+	platform_driver_unregister(&venc_driver);
+	return;
+}
+
+module_init(venc_init);
+module_exit(venc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VPBE VENC Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h
new file mode 100644
index 0000000..947cb15
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc_regs.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2..
+ *
+ * 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
+ */
+#ifndef _VPBE_VENC_REGS_H
+#define _VPBE_VENC_REGS_H
+
+/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
+#define VENC_VMOD				0x00
+#define VENC_VIDCTL				0x04
+#define VENC_VDPRO				0x08
+#define VENC_SYNCCTL				0x0C
+#define VENC_HSPLS				0x10
+#define VENC_VSPLS				0x14
+#define VENC_HINT				0x18
+#define VENC_HSTART				0x1C
+#define VENC_HVALID				0x20
+#define VENC_VINT				0x24
+#define VENC_VSTART				0x28
+#define VENC_VVALID				0x2C
+#define VENC_HSDLY				0x30
+#define VENC_VSDLY				0x34
+#define VENC_YCCCTL				0x38
+#define VENC_RGBCTL				0x3C
+#define VENC_RGBCLP				0x40
+#define VENC_LINECTL				0x44
+#define VENC_CULLLINE				0x48
+#define VENC_LCDOUT				0x4C
+#define VENC_BRTS				0x50
+#define VENC_BRTW				0x54
+#define VENC_ACCTL				0x58
+#define VENC_PWMP				0x5C
+#define VENC_PWMW				0x60
+#define VENC_DCLKCTL				0x64
+#define VENC_DCLKPTN0				0x68
+#define VENC_DCLKPTN1				0x6C
+#define VENC_DCLKPTN2				0x70
+#define VENC_DCLKPTN3				0x74
+#define VENC_DCLKPTN0A				0x78
+#define VENC_DCLKPTN1A				0x7C
+#define VENC_DCLKPTN2A				0x80
+#define VENC_DCLKPTN3A				0x84
+#define VENC_DCLKHS				0x88
+#define VENC_DCLKHSA				0x8C
+#define VENC_DCLKHR				0x90
+#define VENC_DCLKVS				0x94
+#define VENC_DCLKVR				0x98
+#define VENC_CAPCTL				0x9C
+#define VENC_CAPDO				0xA0
+#define VENC_CAPDE				0xA4
+#define VENC_ATR0				0xA8
+#define VENC_ATR1				0xAC
+#define VENC_ATR2				0xB0
+#define VENC_VSTAT				0xB8
+#define VENC_RAMADR				0xBC
+#define VENC_RAMPORT				0xC0
+#define VENC_DACTST				0xC4
+#define VENC_YCOLVL				0xC8
+#define VENC_SCPROG				0xCC
+#define VENC_CVBS				0xDC
+#define VENC_CMPNT				0xE0
+#define VENC_ETMG0				0xE4
+#define VENC_ETMG1				0xE8
+#define VENC_ETMG2				0xEC
+#define VENC_ETMG3				0xF0
+#define VENC_DACSEL				0xF4
+#define VENC_ARGBX0				0x100
+#define VENC_ARGBX1				0x104
+#define VENC_ARGBX2				0x108
+#define VENC_ARGBX3				0x10C
+#define VENC_ARGBX4				0x110
+#define VENC_DRGBX0				0x114
+#define VENC_DRGBX1				0x118
+#define VENC_DRGBX2				0x11C
+#define VENC_DRGBX3				0x120
+#define VENC_DRGBX4				0x124
+#define VENC_VSTARTA				0x128
+#define VENC_OSDCLK0				0x12C
+#define VENC_OSDCLK1				0x130
+#define VENC_HVLDCL0				0x134
+#define VENC_HVLDCL1				0x138
+#define VENC_OSDHADV				0x13C
+#define VENC_CLKCTL				0x140
+#define VENC_GAMCTL				0x144
+#define VENC_XHINTVL				0x174
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV			(1 << 1)
+#define VPBE_PCR_CLK_OFF			(1 << 0)
+
+#define VENC_VMOD_VDMD_SHIFT			12
+#define VENC_VMOD_VDMD_YCBCR16			0
+#define VENC_VMOD_VDMD_YCBCR8			1
+#define VENC_VMOD_VDMD_RGB666			2
+#define VENC_VMOD_VDMD_RGB8			3
+#define VENC_VMOD_VDMD_EPSON			4
+#define VENC_VMOD_VDMD_CASIO			5
+#define VENC_VMOD_VDMD_UDISPQVGA		6
+#define VENC_VMOD_VDMD_STNLCD			7
+#define VENC_VMOD_VIE_SHIFT			1
+#define VENC_VMOD_VDMD				(7 << 12)
+#define VENC_VMOD_ITLCL				(1 << 11)
+#define VENC_VMOD_ITLC				(1 << 10)
+#define VENC_VMOD_NSIT				(1 << 9)
+#define VENC_VMOD_HDMD				(1 << 8)
+#define VENC_VMOD_TVTYP_SHIFT			6
+#define VENC_VMOD_TVTYP				(3 << 6)
+#define VENC_VMOD_SLAVE				(1 << 5)
+#define VENC_VMOD_VMD				(1 << 4)
+#define VENC_VMOD_BLNK				(1 << 3)
+#define VENC_VMOD_VIE				(1 << 1)
+#define VENC_VMOD_VENC				(1 << 0)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC				0
+#define SDTV_PAL				1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P				0
+#define HDTV_625P				1
+#define HDTV_1080I				2
+#define HDTV_720P				3
+
+#define VENC_VIDCTL_VCLKP			(1 << 14)
+#define VENC_VIDCTL_VCLKE_SHIFT			13
+#define VENC_VIDCTL_VCLKE			(1 << 13)
+#define VENC_VIDCTL_VCLKZ_SHIFT			12
+#define VENC_VIDCTL_VCLKZ			(1 << 12)
+#define VENC_VIDCTL_SYDIR_SHIFT			8
+#define VENC_VIDCTL_SYDIR			(1 << 8)
+#define VENC_VIDCTL_DOMD_SHIFT			4
+#define VENC_VIDCTL_DOMD			(3 << 4)
+#define VENC_VIDCTL_YCDIR_SHIFT			0
+#define VENC_VIDCTL_YCDIR			(1 << 0)
+
+#define VENC_VDPRO_ATYCC_SHIFT			5
+#define VENC_VDPRO_ATYCC			(1 << 5)
+#define VENC_VDPRO_ATCOM_SHIFT			4
+#define VENC_VDPRO_ATCOM			(1 << 4)
+#define VENC_VDPRO_DAFRQ			(1 << 3)
+#define VENC_VDPRO_DAUPS			(1 << 2)
+#define VENC_VDPRO_CUPS				(1 << 1)
+#define VENC_VDPRO_YUPS				(1 << 0)
+
+#define VENC_SYNCCTL_VPL_SHIFT			3
+#define VENC_SYNCCTL_VPL			(1 << 3)
+#define VENC_SYNCCTL_HPL_SHIFT			2
+#define VENC_SYNCCTL_HPL			(1 << 2)
+#define VENC_SYNCCTL_SYEV_SHIFT			1
+#define VENC_SYNCCTL_SYEV			(1 << 1)
+#define VENC_SYNCCTL_SYEH_SHIFT			0
+#define VENC_SYNCCTL_SYEH			(1 << 0)
+#define VENC_SYNCCTL_OVD_SHIFT			14
+#define VENC_SYNCCTL_OVD			(1 << 14)
+
+#define VENC_DCLKCTL_DCKEC_SHIFT		11
+#define VENC_DCLKCTL_DCKEC			(1 << 11)
+#define VENC_DCLKCTL_DCKPW_SHIFT		0
+#define VENC_DCLKCTL_DCKPW			(0x3f << 0)
+
+#define VENC_VSTAT_FIDST			(1 << 4)
+
+#define VENC_CMPNT_MRGB_SHIFT			14
+#define VENC_CMPNT_MRGB				(1 << 14)
+
+#endif				/* _VPBE_VENC_REGS_H */
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index d93ad74..49e4deb 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -33,7 +33,6 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
-#include <linux/version.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -44,6 +43,7 @@
 
 MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_CAPTURE_VERSION);
 
 #define vpif_err(fmt, arg...)	v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
 #define vpif_dbg(level, debug, fmt, arg...)	\
@@ -1677,7 +1677,6 @@
 {
 	struct vpif_capture_config *config = vpif_dev->platform_data;
 
-	cap->version = VPIF_CAPTURE_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 	strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
 	strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
@@ -2211,10 +2210,8 @@
 		vfd->v4l2_dev = &vpif_obj.v4l2_dev;
 		vfd->release = video_device_release;
 		snprintf(vfd->name, sizeof(vfd->name),
-			 "DM646x_VPIFCapture_DRIVER_V%d.%d.%d",
-			 (VPIF_CAPTURE_VERSION_CODE >> 16) & 0xff,
-			 (VPIF_CAPTURE_VERSION_CODE >> 8) & 0xff,
-			 (VPIF_CAPTURE_VERSION_CODE) & 0xff);
+			 "DM646x_VPIFCapture_DRIVER_V%s",
+			 VPIF_CAPTURE_VERSION);
 		/* Set video_dev to the video device */
 		ch->video_dev = vfd;
 	}
diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h
index 7a4196d..064550f 100644
--- a/drivers/media/video/davinci/vpif_capture.h
+++ b/drivers/media/video/davinci/vpif_capture.h
@@ -23,7 +23,6 @@
 
 /* Header files */
 #include <linux/videodev2.h>
-#include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-core.h>
@@ -33,11 +32,7 @@
 #include "vpif.h"
 
 /* Macros */
-#define VPIF_MAJOR_RELEASE		0
-#define VPIF_MINOR_RELEASE		0
-#define VPIF_BUILD			1
-#define VPIF_CAPTURE_VERSION_CODE	((VPIF_MAJOR_RELEASE << 16) | \
-	(VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
+#define VPIF_CAPTURE_VERSION		"0.0.2"
 
 #define VPIF_VALID_FIELD(field)		(((V4L2_FIELD_ANY == field) || \
 	(V4L2_FIELD_NONE == field)) || \
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index cdf659a..286f029 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -29,7 +29,6 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
-#include <linux/version.h>
 #include <linux/slab.h>
 
 #include <asm/irq.h>
@@ -47,6 +46,7 @@
 
 MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_DISPLAY_VERSION);
 
 #define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
 
@@ -701,7 +701,6 @@
 {
 	struct vpif_display_config *config = vpif_dev->platform_data;
 
-	cap->version = VPIF_DISPLAY_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
 	strlcpy(cap->driver, "vpif display", sizeof(cap->driver));
 	strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info));
@@ -1740,10 +1739,8 @@
 		vfd->v4l2_dev = &vpif_obj.v4l2_dev;
 		vfd->release = video_device_release;
 		snprintf(vfd->name, sizeof(vfd->name),
-			 "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d",
-			 (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff,
-			 (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff,
-			 (VPIF_DISPLAY_VERSION_CODE) & 0xff);
+			 "DM646x_VPIFDisplay_DRIVER_V%s",
+			 VPIF_DISPLAY_VERSION);
 
 		/* Set video_dev to the video device */
 		ch->video_dev = vfd;
diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h
index b53aaa8..5d1936d 100644
--- a/drivers/media/video/davinci/vpif_display.h
+++ b/drivers/media/video/davinci/vpif_display.h
@@ -18,7 +18,6 @@
 
 /* Header files */
 #include <linux/videodev2.h>
-#include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-core.h>
@@ -27,12 +26,7 @@
 #include "vpif.h"
 
 /* Macros */
-#define VPIF_MAJOR_RELEASE	(0)
-#define VPIF_MINOR_RELEASE	(0)
-#define VPIF_BUILD		(1)
-
-#define VPIF_DISPLAY_VERSION_CODE \
-	((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
+#define VPIF_DISPLAY_VERSION	"0.0.2"
 
 #define VPIF_VALID_FIELD(field) \
 	(((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 3cb78f2..281ee42 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -3,7 +3,6 @@
 	depends on VIDEO_DEV && I2C
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
-	depends on RC_CORE
 	select VIDEOBUF_VMALLOC
 	select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
 	select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
@@ -40,7 +39,18 @@
 	select DVB_S921 if !DVB_FE_CUSTOMISE
 	select DVB_DRXD if !DVB_FE_CUSTOMISE
 	select DVB_CXD2820R if !DVB_FE_CUSTOMISE
+	select DVB_DRXK if !DVB_FE_CUSTOMISE
+	select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
 	select VIDEOBUF_DVB
 	---help---
 	  This adds support for DVB cards based on the
 	  Empiatech em28xx chips.
+
+config VIDEO_EM28XX_RC
+        bool "EM28XX Remote Controller support"
+        depends on RC_CORE
+        depends on VIDEO_EM28XX
+        depends on !(RC_CORE=m && VIDEO_EM28XX=y)
+        default y
+        ---help---
+          Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index d0f093d..38aaa00 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -1,5 +1,7 @@
-em28xx-objs     := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
-		   em28xx-input.o em28xx-vbi.o
+em28xx-y :=	em28xx-video.o em28xx-i2c.o em28xx-cards.o
+em28xx-y +=	em28xx-core.o  em28xx-vbi.o
+
+em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o
 
 em28xx-alsa-objs := em28xx-audio.o
 
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 3c48a72..cff0768 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -3,9 +3,9 @@
  *
  *  Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
  *
- *  Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *  Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
  *	- Port to work with the in-kernel driver
- *	- Several cleanups
+ *	- Cleanups, fixes, alsa-controls, etc.
  *
  *  This driver is based on my previous au600 usb pstn audio driver
  *  and inherits all the copyrights
@@ -41,6 +41,7 @@
 #include <sound/info.h>
 #include <sound/initval.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <media/v4l2-common.h>
 #include "em28xx.h"
 
@@ -212,9 +213,12 @@
 	for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
 		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
 		if (errCode) {
+			em28xx_errdev("submit of audio urb failed\n");
 			em28xx_deinit_isoc_audio(dev);
+			atomic_set(&dev->stream_started, 0);
 			return errCode;
 		}
+
 	}
 
 	return 0;
@@ -245,6 +249,7 @@
 	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
 		SNDRV_PCM_INFO_MMAP           |
 		SNDRV_PCM_INFO_INTERLEAVED    |
+		SNDRV_PCM_INFO_BATCH	      |
 		SNDRV_PCM_INFO_MMAP_VALID,
 
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -276,24 +281,27 @@
 		return -ENODEV;
 	}
 
-	/* Sets volume, mute, etc */
-
-	dev->mute = 0;
-	mutex_lock(&dev->lock);
-	ret = em28xx_audio_analog_set(dev);
-	if (ret < 0)
-		goto err;
-
 	runtime->hw = snd_em28xx_hw_capture;
-	if (dev->alt == 0 && dev->adev.users == 0) {
-		int errCode;
-		dev->alt = 7;
-		dprintk("changing alternate number to 7\n");
-		errCode = usb_set_interface(dev->udev, 0, 7);
-	}
+	if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
+		if (dev->audio_ifnum)
+			dev->alt = 1;
+		else
+			dev->alt = 7;
 
-	dev->adev.users++;
-	mutex_unlock(&dev->lock);
+		dprintk("changing alternate number on interface %d to %d\n",
+			dev->audio_ifnum, dev->alt);
+		usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
+
+		/* Sets volume, mute, etc */
+		dev->mute = 0;
+		mutex_lock(&dev->lock);
+		ret = em28xx_audio_analog_set(dev);
+		if (ret < 0)
+			goto err;
+
+		dev->adev.users++;
+		mutex_unlock(&dev->lock);
+	}
 
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 	dev->adev.capture_pcm_substream = substream;
@@ -342,6 +350,8 @@
 
 	ret = snd_pcm_alloc_vmalloc_buffer(substream,
 				params_buffer_bytes(hw_params));
+	if (ret < 0)
+		return ret;
 	format = params_format(hw_params);
 	rate = params_rate(hw_params);
 	channels = params_channels(hw_params);
@@ -393,20 +403,24 @@
 				      int cmd)
 {
 	struct em28xx *dev = snd_pcm_substream_chip(substream);
-	int retval;
+	int retval = 0;
 
 	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+	case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
 	case SNDRV_PCM_TRIGGER_START:
 		atomic_set(&dev->stream_started, 1);
 		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+	case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
 	case SNDRV_PCM_TRIGGER_STOP:
-		atomic_set(&dev->stream_started, 1);
+		atomic_set(&dev->stream_started, 0);
 		break;
 	default:
 		retval = -EINVAL;
 	}
 	schedule_work(&dev->wq_trigger);
-	return 0;
+	return retval;
 }
 
 static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
@@ -432,6 +446,179 @@
 	return vmalloc_to_page(pageptr);
 }
 
+/*
+ * AC97 volume control support
+ */
+static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *info)
+{
+	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	info->count = 2;
+	info->value.integer.min = 0;
+	info->value.integer.max = 0x1f;
+
+	return 0;
+}
+
+static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+	u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
+		  (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
+	int rc;
+
+	mutex_lock(&dev->lock);
+	rc = em28xx_read_ac97(dev, kcontrol->private_value);
+	if (rc < 0)
+		goto err;
+
+	val |= rc & 0x8000;	/* Preserve the mute flag */
+
+	rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
+	if (rc < 0)
+		goto err;
+
+	dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+		(val & 0x8000) ? "muted " : "",
+		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+		val, (int)kcontrol->private_value);
+
+err:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+	int val;
+
+	mutex_lock(&dev->lock);
+	val = em28xx_read_ac97(dev, kcontrol->private_value);
+	mutex_unlock(&dev->lock);
+	if (val < 0)
+		return val;
+
+	dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+		(val & 0x8000) ? "muted " : "",
+		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+		val, (int)kcontrol->private_value);
+
+	value->value.integer.value[0] = 0x1f - (val & 0x1f);
+	value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
+
+	return 0;
+}
+
+static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+	u16 val = value->value.integer.value[0];
+	int rc;
+
+	mutex_lock(&dev->lock);
+	rc = em28xx_read_ac97(dev, kcontrol->private_value);
+	if (rc < 0)
+		goto err;
+
+	if (val)
+		rc &= 0x1f1f;
+	else
+		rc |= 0x8000;
+
+	rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
+	if (rc < 0)
+		goto err;
+
+	dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+		(val & 0x8000) ? "muted " : "",
+		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+		val, (int)kcontrol->private_value);
+
+err:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+	int val;
+
+	mutex_lock(&dev->lock);
+	val = em28xx_read_ac97(dev, kcontrol->private_value);
+	mutex_unlock(&dev->lock);
+	if (val < 0)
+		return val;
+
+	if (val & 0x8000)
+		value->value.integer.value[0] = 0;
+	else
+		value->value.integer.value[0] = 1;
+
+	dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+		(val & 0x8000) ? "muted " : "",
+		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+		val, (int)kcontrol->private_value);
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
+
+static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
+			   char *name, int id)
+{
+	int err;
+	char ctl_name[44];
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_new tmp;
+
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	tmp.private_value = id,
+	tmp.name  = ctl_name,
+
+	/* Add Mute Control */
+	sprintf(ctl_name, "%s Switch", name);
+	tmp.get  = em28xx_vol_get_mute;
+	tmp.put  = em28xx_vol_put_mute;
+	tmp.info = snd_ctl_boolean_mono_info;
+	kctl = snd_ctl_new1(&tmp, dev);
+	err = snd_ctl_add(card, kctl);
+	if (err < 0)
+		return err;
+	dprintk("Added control %s for ac97 volume control 0x%04x\n",
+		ctl_name, id);
+
+	memset (&tmp, 0, sizeof(tmp));
+	tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	tmp.private_value = id,
+	tmp.name  = ctl_name,
+
+	/* Add Volume Control */
+	sprintf(ctl_name, "%s Volume", name);
+	tmp.get   = em28xx_vol_get;
+	tmp.put   = em28xx_vol_put;
+	tmp.info  = em28xx_vol_info;
+	tmp.tlv.p = em28xx_db_scale,
+	kctl = snd_ctl_new1(&tmp, dev);
+	err = snd_ctl_add(card, kctl);
+	if (err < 0)
+		return err;
+	dprintk("Added control %s for ac97 volume control 0x%04x\n",
+		ctl_name, id);
+
+	return 0;
+}
+
+/*
+ * register/unregister code and data
+ */
 static struct snd_pcm_ops snd_em28xx_pcm_capture = {
 	.open      = snd_em28xx_capture_open,
 	.close     = snd_em28xx_pcm_close,
@@ -452,17 +639,17 @@
 	static int          devnr;
 	int                 err;
 
-	if (dev->has_alsa_audio != 1) {
+	if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
 		/* This device does not support the extension (in this case
 		   the device is expecting the snd-usb-audio module or
 		   doesn't have analog audio support at all) */
 		return 0;
 	}
 
-	printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
-			 "non standard usbaudio\n");
+	printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
 	printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
 			 "Rechberger\n");
+	printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
 
 	err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
 			      &card);
@@ -488,6 +675,22 @@
 
 	INIT_WORK(&dev->wq_trigger, audio_trigger);
 
+	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+		em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL);
+		em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL);
+		em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL);
+		em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL);
+		em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL);
+		em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL);
+		em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL);
+
+		em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL);
+		em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL);
+		em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL);
+		em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL);
+		em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL);
+	}
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
@@ -538,7 +741,7 @@
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
 MODULE_DESCRIPTION("Em28xx Audio driver");
 
 module_init(em28xx_alsa_register);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 4e37375..3e3959f 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -289,7 +289,7 @@
 	{	-1,		-1,	-1,	-1},
 };
 
-/* 2013:024f PCTV Systems nanoStick T2 290e
+/* 2013:024f PCTV nanoStick T2 290e
  * GPIO_6 - demod reset
  * GPIO_7 - LED
  */
@@ -300,6 +300,23 @@
 	{-1,			-1,	-1,		-1},
 };
 
+#if 0
+static struct em28xx_reg_seq terratec_h5_gpio[] = {
+	{EM28XX_R08_GPIO,	0xff,	0xff,	10},
+	{EM2874_R80_GPIO,	0xf6,	0xff,	100},
+	{EM2874_R80_GPIO,	0xf2,	0xff,	50},
+	{EM2874_R80_GPIO,	0xf6,	0xff,	50},
+	{ -1,			-1,	-1,	-1},
+};
+
+static struct em28xx_reg_seq terratec_h5_digital[] = {
+	{EM2874_R80_GPIO,	0xf6,	0xff,	10},
+	{EM2874_R80_GPIO,	0xe6,	0xff,	100},
+	{EM2874_R80_GPIO,	0xa6,	0xff,	10},
+	{ -1,			-1,	-1,	-1},
+};
+#endif
+
 /*
  *  Board definitions
  */
@@ -843,6 +860,19 @@
 			.gpio     = terratec_cinergy_USB_XS_FR_analog,
 		} },
 	},
+	[EM2884_BOARD_TERRATEC_H5] = {
+		.name         = "Terratec Cinergy H5",
+		.has_dvb      = 1,
+#if 0
+		.tuner_type   = TUNER_PHILIPS_TDA8290,
+		.tuner_addr   = 0x41,
+		.dvb_gpio     = terratec_h5_digital, /* FIXME: probably wrong */
+		.tuner_gpio   = terratec_h5_gpio,
+#endif
+		.i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+				EM28XX_I2C_CLK_WAIT_ENABLE |
+				EM28XX_I2C_FREQ_400_KHZ,
+	},
 	[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
 		.name         = "Hauppauge WinTV HVR 900",
 		.tda9887_conf = TDA9887_PRESENT,
@@ -1259,7 +1289,7 @@
 		} },
 	},
 
-	[EM2874_LEADERSHIP_ISDBT] = {
+	[EM2874_BOARD_LEADERSHIP_ISDBT] = {
 		.i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
 				  EM28XX_I2C_CLK_WAIT_ENABLE |
 				  EM28XX_I2C_FREQ_100_KHZ,
@@ -1319,7 +1349,6 @@
 	},
 	[EM2880_BOARD_KWORLD_DVB_305U] = {
 		.name	      = "KWorld DVB-T 305U",
-		.valid        = EM28XX_BOARD_NOT_VALIDATED,
 		.tuner_type   = TUNER_XC2028,
 		.tuner_gpio   = default_tuner_gpio,
 		.decoder      = EM28XX_TVP5150,
@@ -1770,16 +1799,16 @@
 		.dvb_gpio   = kworld_a340_digital,
 		.tuner_gpio = default_tuner_gpio,
 	},
-	/* 2013:024f PCTV Systems nanoStick T2 290e.
+	/* 2013:024f PCTV nanoStick T2 290e.
 	 * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
 	[EM28174_BOARD_PCTV_290E] = {
+		.name          = "PCTV nanoStick T2 290e",
 		.i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
 			EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
-		.xclk          = EM28XX_XCLK_FREQUENCY_12MHZ,
-		.name          = "PCTV Systems nanoStick T2 290e",
 		.tuner_type    = TUNER_ABSENT,
 		.tuner_gpio    = pctv_290e,
 		.has_dvb       = 1,
+		.ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
 	},
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1855,8 +1884,10 @@
 	{ USB_DEVICE(0x0ccd, 0x0042),
 			.driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
 	{ USB_DEVICE(0x0ccd, 0x0043),
-			.driver_info = EM2870_BOARD_TERRATEC_XS },
-	{ USB_DEVICE(0x0ccd, 0x0047),
+			.driver_info = EM2884_BOARD_TERRATEC_H5 },
+	{ USB_DEVICE(0x0ccd, 0x10a2),	/* Rev. 1 */
+			.driver_info = EM2884_BOARD_TERRATEC_H5 },
+	{ USB_DEVICE(0x0ccd, 0x10ad),	/* Rev. 2 */
 			.driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS },
 	{ USB_DEVICE(0x0ccd, 0x0084),
 			.driver_info = EM2860_BOARD_TERRATEC_AV350 },
@@ -1937,7 +1968,7 @@
 	{0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
 	{0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
 	{0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
-	{0x6b800080, EM2874_LEADERSHIP_ISDBT, TUNER_ABSENT},
+	{0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
 };
 
 /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
@@ -2660,10 +2691,9 @@
 			.addr = 0xba >> 1,
 			.platform_data = &pdata,
 		};
-		struct v4l2_subdev *sd;
 
 		pdata.xtal = dev->sensor_xtal;
-		sd = v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+		v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
 				&mt9v011_info, NULL);
 	}
 
@@ -2842,11 +2872,26 @@
 			em28xx_info("chip ID is em2882/em2883\n");
 			dev->wait_after_write = 0;
 			break;
+		case CHIP_ID_EM2884:
+			em28xx_info("chip ID is em2884\n");
+			dev->reg_gpio_num = EM2874_R80_GPIO;
+			dev->wait_after_write = 0;
+			break;
 		default:
 			em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
 		}
 	}
 
+	if (dev->is_audio_only) {
+		errCode = em28xx_audio_setup(dev);
+		if (errCode)
+			return -ENODEV;
+		em28xx_add_into_devlist(dev);
+		em28xx_init_extension(dev);
+
+		return 0;
+	}
+
 	/* Prepopulate cached GPO register content */
 	retval = em28xx_read_reg(dev, dev->reg_gpo_num);
 	if (retval >= 0)
@@ -2947,6 +2992,9 @@
 	return retval;
 }
 
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
 /*
  * em28xx_usb_probe()
  * checks for supported devices
@@ -2956,15 +3004,15 @@
 {
 	const struct usb_endpoint_descriptor *endpoint;
 	struct usb_device *udev;
-	struct usb_interface *uif;
 	struct em28xx *dev = NULL;
 	int retval;
-	int i, nr, ifnum, isoc_pipe;
+	bool is_audio_only = false, has_audio = false;
+	int i, nr, isoc_pipe;
+	const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
 	char *speed;
 	char descr[255] = "";
 
 	udev = usb_get_dev(interface_to_usbdev(interface));
-	ifnum = interface->altsetting[0].desc.bInterfaceNumber;
 
 	/* Check to see next free device and mark as used */
 	nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
@@ -2984,6 +3032,19 @@
 		goto err;
 	}
 
+	/* Get endpoints */
+	for (i = 0; i < interface->num_altsetting; i++) {
+		int ep;
+
+		for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+			struct usb_host_endpoint	*e;
+			e = &interface->altsetting[i].endpoint[ep];
+
+			if (e->desc.bEndpointAddress == 0x83)
+				has_audio = true;
+		}
+	}
+
 	endpoint = &interface->cur_altsetting->endpoint[0].desc;
 
 	/* check if the device has the iso in endpoint at the correct place */
@@ -3003,19 +3064,22 @@
 			check_interface = 0;
 
 		if (!check_interface) {
-			em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
-				"interface %i, class %i found.\n",
-				le16_to_cpu(udev->descriptor.idVendor),
-				le16_to_cpu(udev->descriptor.idProduct),
-				ifnum,
-				interface->altsetting[0].desc.bInterfaceClass);
+			if (has_audio) {
+				is_audio_only = true;
+			} else {
+				em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
+					"interface %i, class %i found.\n",
+					le16_to_cpu(udev->descriptor.idVendor),
+					le16_to_cpu(udev->descriptor.idProduct),
+					ifnum,
+					interface->altsetting[0].desc.bInterfaceClass);
+				em28xx_err(DRIVER_NAME " This is an anciliary "
+					"interface not used by the driver\n");
 
-			em28xx_err(DRIVER_NAME " This is an anciliary "
-				"interface not used by the driver\n");
-
-			em28xx_devused &= ~(1<<nr);
-			retval = -ENODEV;
-			goto err;
+				em28xx_devused &= ~(1<<nr);
+				retval = -ENODEV;
+				goto err;
+			}
 		}
 	}
 
@@ -3045,8 +3109,8 @@
 	if (*descr)
 		strlcat(descr, " ", sizeof(descr));
 
-	printk(DRIVER_NAME ": New device %s@ %s Mbps "
-		"(%04x:%04x, interface %d, class %d)\n",
+	printk(KERN_INFO DRIVER_NAME
+		": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n",
 		descr,
 		speed,
 		le16_to_cpu(udev->descriptor.idVendor),
@@ -3054,6 +3118,11 @@
 		ifnum,
 		interface->altsetting->desc.bInterfaceNumber);
 
+	if (has_audio)
+		printk(KERN_INFO DRIVER_NAME
+		       ": Audio Vendor Class interface %i found\n",
+		       ifnum);
+
 	/*
 	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
 	 * video stream wouldn't likely work, since 12 Mbps is generally
@@ -3089,10 +3158,13 @@
 	dev->devno = nr;
 	dev->model = id->driver_info;
 	dev->alt   = -1;
+	dev->is_audio_only = is_audio_only;
+	dev->has_alsa_audio = has_audio;
+	dev->audio_ifnum = ifnum;
 
 	/* Checks if audio is provided by some interface */
 	for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
-		uif = udev->config->interface[i];
+		struct usb_interface *uif = udev->config->interface[i];
 		if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
 			dev->has_audio_class = 1;
 			break;
@@ -3100,9 +3172,7 @@
 	}
 
 	/* compute alternate max packet sizes */
-	uif = udev->actconfig->interface[0];
-
-	dev->num_alt = uif->num_altsetting;
+	dev->num_alt = interface->num_altsetting;
 	dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
 
 	if (dev->alt_max_pkt_size == NULL) {
@@ -3114,14 +3184,21 @@
 	}
 
 	for (i = 0; i < dev->num_alt ; i++) {
-		u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
-		dev->alt_max_pkt_size[i] =
-		    (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+		u16 tmp = le16_to_cpu(interface->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+		unsigned int size = tmp & 0x7ff;
+
+		if (udev->speed == USB_SPEED_HIGH)
+			size = size * hb_mult(tmp);
+
+		dev->alt_max_pkt_size[i] = size;
 	}
 
 	if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
 		dev->model = card[nr];
 
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+
 	/* allocate device struct */
 	mutex_init(&dev->lock);
 	mutex_lock(&dev->lock);
@@ -3133,9 +3210,6 @@
 		goto err;
 	}
 
-	/* save our data pointer in this interface device */
-	usb_set_intfdata(interface, dev);
-
 	request_modules(dev);
 
 	/* Should be the last thing to do, to avoid newer udev's to
@@ -3164,6 +3238,13 @@
 	if (!dev)
 		return;
 
+	if (dev->is_audio_only) {
+		mutex_lock(&dev->lock);
+		em28xx_close_extension(dev);
+		mutex_unlock(&dev->lock);
+		return;
+	}
+
 	em28xx_info("disconnecting %s\n", dev->vdev->name);
 
 	flush_request_modules(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index e33f145..57b1b5c 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -211,6 +211,7 @@
 {
 	return em28xx_write_regs(dev, reg, &val, 1);
 }
+EXPORT_SYMBOL_GPL(em28xx_write_reg);
 
 /*
  * em28xx_write_reg_bits()
@@ -286,6 +287,7 @@
 		return ret;
 	return le16_to_cpu(val);
 }
+EXPORT_SYMBOL_GPL(em28xx_read_ac97);
 
 /*
  * em28xx_write_ac97()
@@ -313,13 +315,14 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(em28xx_write_ac97);
 
-struct em28xx_vol_table {
+struct em28xx_vol_itable {
 	enum em28xx_amux mux;
 	u8		 reg;
 };
 
-static struct em28xx_vol_table inputs[] = {
+static struct em28xx_vol_itable inputs[] = {
 	{ EM28XX_AMUX_VIDEO, 	AC97_VIDEO_VOL   },
 	{ EM28XX_AMUX_LINE_IN,	AC97_LINEIN_VOL  },
 	{ EM28XX_AMUX_PHONE,	AC97_PHONE_VOL   },
@@ -403,7 +406,12 @@
 	return ret;
 }
 
-static const struct em28xx_vol_table outputs[] = {
+struct em28xx_vol_otable {
+	enum em28xx_aout mux;
+	u8		 reg;
+};
+
+static const struct em28xx_vol_otable outputs[] = {
 	{ EM28XX_AOUT_MASTER, AC97_MASTER_VOL      },
 	{ EM28XX_AOUT_LINE,   AC97_LINE_LEVEL_VOL  },
 	{ EM28XX_AOUT_MONO,   AC97_MASTER_MONO_VOL },
@@ -492,17 +500,13 @@
 	if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
 		|| dev->chip_id == CHIP_ID_EM28174) {
 		/* Digital only device - don't load any alsa module */
-		dev->audio_mode.has_audio = 0;
-		dev->has_audio_class = 0;
-		dev->has_alsa_audio = 0;
+		dev->audio_mode.has_audio = false;
+		dev->has_audio_class = false;
+		dev->has_alsa_audio = false;
 		return 0;
 	}
 
-	/* If device doesn't support Usb Audio Class, use vendor class */
-	if (!dev->has_audio_class)
-		dev->has_alsa_audio = 1;
-
-	dev->audio_mode.has_audio = 1;
+	dev->audio_mode.has_audio = true;
 
 	/* See how this device is configured */
 	cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
@@ -512,8 +516,8 @@
 		cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
 	} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
 		/* The device doesn't have vendor audio at all */
-		dev->has_alsa_audio = 0;
-		dev->audio_mode.has_audio = 0;
+		dev->has_alsa_audio = false;
+		dev->audio_mode.has_audio = false;
 		return 0;
 	} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
 		   EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
@@ -542,8 +546,8 @@
 		 */
 		em28xx_warn("AC97 chip type couldn't be determined\n");
 		dev->audio_mode.ac97 = EM28XX_NO_AC97;
-		dev->has_alsa_audio = 0;
-		dev->audio_mode.has_audio = 0;
+		dev->has_alsa_audio = false;
+		dev->audio_mode.has_audio = false;
 		goto init_audio;
 	}
 
@@ -615,7 +619,9 @@
 {
 	int rc;
 
-	if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
+	if (dev->chip_id == CHIP_ID_EM2874 ||
+	    dev->chip_id == CHIP_ID_EM2884 ||
+	    dev->chip_id == CHIP_ID_EM28174) {
 		/* The Transport Stream Enable Register moved in em2874 */
 		if (!start) {
 			rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
@@ -884,6 +890,7 @@
 	}
 	return rc;
 }
+EXPORT_SYMBOL_GPL(em28xx_gpio_set);
 
 int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
 {
@@ -917,7 +924,7 @@
 static void em28xx_irq_callback(struct urb *urb)
 {
 	struct em28xx *dev = urb->context;
-	int rc, i;
+	int i;
 
 	switch (urb->status) {
 	case 0:             /* success */
@@ -934,7 +941,7 @@
 
 	/* Copy data from URB */
 	spin_lock(&dev->slock);
-	rc = dev->isoc_ctl.isoc_copy(dev, urb);
+	dev->isoc_ctl.isoc_copy(dev, urb);
 	spin_unlock(&dev->slock);
 
 	/* Reset urb buffers */
@@ -1106,17 +1113,19 @@
 int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev)
 {
 	unsigned int chip_cfg2;
-	unsigned int packet_size = 564;
+	unsigned int packet_size;
 
-	if (dev->chip_id == CHIP_ID_EM2874) {
-		/* FIXME - for now assume 564 like it was before, but the
-		   em2874 code should be added to return the proper value... */
-		packet_size = 564;
-	} else if (dev->chip_id == CHIP_ID_EM28174) {
-		/* FIXME same as em2874. 564 was enough for 22 Mbit DVB-T
-		   but too much for 44 Mbit DVB-C. */
-		packet_size = 752;
-	} else {
+	switch (dev->chip_id) {
+	case CHIP_ID_EM2710:
+	case CHIP_ID_EM2750:
+	case CHIP_ID_EM2800:
+	case CHIP_ID_EM2820:
+	case CHIP_ID_EM2840:
+	case CHIP_ID_EM2860:
+		/* No DVB support */
+		return -EINVAL;
+	case CHIP_ID_EM2870:
+	case CHIP_ID_EM2883:
 		/* TS max packet size stored in bits 1-0 of R01 */
 		chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2);
 		switch (chip_cfg2 & EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK) {
@@ -1133,9 +1142,24 @@
 			packet_size = 752;
 			break;
 		}
+		break;
+	case CHIP_ID_EM2874:
+		/*
+		 * FIXME: for now assumes 564 like it was before, but the
+		 * em2874 code should be added to return the proper value
+		 */
+		packet_size = 564;
+		break;
+	case CHIP_ID_EM2884:
+	case CHIP_ID_EM28174:
+	default:
+		/*
+		 * FIXME: same as em2874. 564 was enough for 22 Mbit DVB-T
+		 * but not enough for 44 Mbit DVB-C.
+		 */
+		packet_size = 752;
 	}
 
-	em28xx_coredbg("dvb max packet size=%d\n", packet_size);
 	return packet_size;
 }
 EXPORT_SYMBOL_GPL(em28xx_isoc_dvb_max_packetsize);
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 7904ca4..e5916de 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -1,7 +1,7 @@
 /*
  DVB device driver for em28xx
 
- (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
+ (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
 
  (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
 	- Fixes for the driver to properly work with HVR-950
@@ -40,6 +40,8 @@
 #include "s921.h"
 #include "drxd.h"
 #include "cxd2820r.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
 
 MODULE_DESCRIPTION("driver for em28xx based DVB cards");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -73,6 +75,11 @@
 	struct dmx_frontend        fe_hw;
 	struct dmx_frontend        fe_mem;
 	struct dvb_net             net;
+
+	/* Due to DRX-K - probably need changes */
+	int (*gate_ctrl)(struct dvb_frontend *, int);
+	struct semaphore      pll_mutex;
+	bool			dont_attach_fe1;
 };
 
 
@@ -160,6 +167,11 @@
 		return rc;
 
 	max_dvb_packet_size = em28xx_isoc_dvb_max_packetsize(dev);
+	if (max_dvb_packet_size < 0)
+		return max_dvb_packet_size;
+	dprintk(1, "Using %d buffers each with %d bytes\n",
+		EM28XX_DVB_NUM_BUFS,
+		max_dvb_packet_size);
 
 	return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
 				EM28XX_DVB_NUM_BUFS, max_dvb_packet_size,
@@ -295,6 +307,79 @@
 	.disable_i2c_gate_ctrl = 1,
 };
 
+struct drxk_config terratec_h5_drxk = {
+	.adr = 0x29,
+	.single_master = 1,
+	.no_i2c_bridge = 1,
+	.microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct em28xx_dvb *dvb = fe->sec_priv;
+	int status;
+
+	if (!dvb)
+		return -EINVAL;
+
+	if (enable) {
+		down(&dvb->pll_mutex);
+		status = dvb->gate_ctrl(fe, 1);
+	} else {
+		status = dvb->gate_ctrl(fe, 0);
+		up(&dvb->pll_mutex);
+	}
+	return status;
+}
+
+static void terratec_h5_init(struct em28xx *dev)
+{
+	int i;
+	struct em28xx_reg_seq terratec_h5_init[] = {
+		{EM28XX_R08_GPIO,	0xff,	0xff,	10},
+		{EM2874_R80_GPIO,	0xf6,	0xff,	100},
+		{EM2874_R80_GPIO,	0xf2,	0xff,	50},
+		{EM2874_R80_GPIO,	0xf6,	0xff,	100},
+		{ -1,                   -1,     -1,     -1},
+	};
+	struct em28xx_reg_seq terratec_h5_end[] = {
+		{EM2874_R80_GPIO,	0xe6,	0xff,	100},
+		{EM2874_R80_GPIO,	0xa6,	0xff,	50},
+		{EM2874_R80_GPIO,	0xe6,	0xff,	100},
+		{ -1,                   -1,     -1,     -1},
+	};
+	struct {
+		unsigned char r[4];
+		int len;
+	} regs[] = {
+		{{ 0x06, 0x02, 0x00, 0x31 }, 4},
+		{{ 0x01, 0x02 }, 2},
+		{{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+		{{ 0x01, 0x00 }, 2},
+		{{ 0x01, 0x00, 0xff, 0xaf }, 4},
+		{{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+		{{ 0x01, 0x00 }, 2},
+		{{ 0x01, 0x00, 0x73, 0xaf }, 4},
+		{{ 0x04, 0x00 }, 2},
+		{{ 0x00, 0x04 }, 2},
+		{{ 0x00, 0x04, 0x00, 0x0a }, 4},
+		{{ 0x04, 0x14 }, 2},
+		{{ 0x04, 0x14, 0x00, 0x00 }, 4},
+	};
+
+	em28xx_gpio_set(dev, terratec_h5_init);
+	em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+	msleep(10);
+	em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+	msleep(10);
+
+	dev->i2c_client.addr = 0x82 >> 1;
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++)
+		i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+	em28xx_gpio_set(dev, terratec_h5_end);
+};
+
 static int mt352_terratec_xs_init(struct dvb_frontend *fe)
 {
 	/* Values extracted from a USB trace of the Terratec Windows driver */
@@ -516,7 +601,7 @@
 	if (dvb->fe[1])
 		dvb_unregister_frontend(dvb->fe[1]);
 	dvb_unregister_frontend(dvb->fe[0]);
-	if (dvb->fe[1])
+	if (dvb->fe[1] && !dvb->dont_attach_fe1)
 		dvb_frontend_detach(dvb->fe[1]);
 	dvb_frontend_detach(dvb->fe[0]);
 	dvb_unregister_adapter(&dvb->adapter);
@@ -546,7 +631,7 @@
 	em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
 	/* init frontend */
 	switch (dev->model) {
-	case EM2874_LEADERSHIP_ISDBT:
+	case EM2874_BOARD_LEADERSHIP_ISDBT:
 		dvb->fe[0] = dvb_attach(s921_attach,
 				&sharp_isdbt, &dev->i2c_adap);
 
@@ -689,6 +774,41 @@
 			}
 		}
 		break;
+	case EM2884_BOARD_TERRATEC_H5:
+		terratec_h5_init(dev);
+
+		dvb->dont_attach_fe1 = 1;
+
+		dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap, &dvb->fe[1]);
+		if (!dvb->fe[0]) {
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		/* FIXME: do we need a pll semaphore? */
+		dvb->fe[0]->sec_priv = dvb;
+		sema_init(&dvb->pll_mutex, 1);
+		dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+		dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+		dvb->fe[1]->id = 1;
+
+		/* Attach tda18271 to DVB-C frontend */
+		if (dvb->fe[0]->ops.i2c_gate_ctrl)
+			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+		if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) {
+			result = -EINVAL;
+			goto out_free;
+		}
+		if (dvb->fe[0]->ops.i2c_gate_ctrl)
+			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+		/* Hack - needed by drxk/tda18271c2dd */
+		dvb->fe[1]->tuner_priv = dvb->fe[0]->tuner_priv;
+		memcpy(&dvb->fe[1]->ops.tuner_ops,
+		       &dvb->fe[0]->ops.tuner_ops,
+		       sizeof(dvb->fe[0]->ops.tuner_ops));
+
+		break;
 	default:
 		em28xx_errdev("/2: The frontend of your DVB/ATSC card"
 				" isn't supported yet\n");
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 4739fc7..36f5a9b 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -181,16 +181,25 @@
 
 /*
  * em28xx_i2c_send_bytes()
- * untested for more than 4 bytes
  */
 static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf,
 				 short len, int stop)
 {
 	int wrcount = 0;
 	struct em28xx *dev = (struct em28xx *)data;
+	int write_timeout, ret;
 
 	wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len);
 
+	/* Seems to be required after a write */
+	for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
+	     write_timeout -= 5) {
+		ret = dev->em28xx_read_reg(dev, 0x05);
+		if (!ret)
+			break;
+		msleep(5);
+	}
+
 	return wrcount;
 }
 
@@ -218,9 +227,7 @@
  */
 static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
 {
-	char msg;
 	int ret;
-	msg = addr;
 
 	ret = dev->em28xx_read_reg_req(dev, 2, addr);
 	if (ret < 0) {
@@ -332,7 +339,9 @@
 	struct em28xx_eeprom *em_eeprom = (void *)eedata;
 	int i, err, size = len, block;
 
-	if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
+	if (dev->chip_id == CHIP_ID_EM2874 ||
+	    dev->chip_id == CHIP_ID_EM28174 ||
+	    dev->chip_id == CHIP_ID_EM2884) {
 		/* Empia switched to a 16-bit addressable eeprom in newer
 		   devices.  While we could certainly write a routine to read
 		   the eeprom, there is nothing of use in there that cannot be
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index ba1ba86..5d12b14 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -372,6 +372,7 @@
 		ir->get_key = default_polling_getkey;
 		break;
 	case CHIP_ID_EM2874:
+	case CHIP_ID_EM28174:
 		ir->get_key = em2874_polling_getkey;
 		em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
 		break;
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index e92a28e..66f7923 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -201,6 +201,7 @@
 	CHIP_ID_EM2870 = 35,
 	CHIP_ID_EM2883 = 36,
 	CHIP_ID_EM2874 = 65,
+	CHIP_ID_EM2884 = 68,
 	CHIP_ID_EM28174 = 113,
 };
 
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 7b6461d..d176dc0 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -32,7 +32,6 @@
 #include <linux/bitmap.h>
 #include <linux/usb.h>
 #include <linux/i2c.h>
-#include <linux/version.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -50,7 +49,8 @@
 		      "Sascha Sommer <saschasommer@freenet.de>"
 
 #define DRIVER_DESC         "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 1, 2)
+
+#define EM28XX_VERSION "0.1.3"
 
 #define em28xx_videodbg(fmt, arg...) do {\
 	if (video_debug) \
@@ -72,6 +72,7 @@
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(EM28XX_VERSION);
 
 static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
 static unsigned int vbi_nr[]   = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
@@ -1757,8 +1758,6 @@
 	strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
-	cap->version = EM28XX_VERSION_CODE;
-
 	cap->capabilities =
 			V4L2_CAP_SLICED_VBI_CAPTURE |
 			V4L2_CAP_VIDEO_CAPTURE |
@@ -1976,7 +1975,6 @@
 	strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
-	cap->version = EM28XX_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
@@ -2450,10 +2448,8 @@
       u8 val;
 	int ret;
 
-	printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
-		dev->name,
-		(EM28XX_VERSION_CODE >> 16) & 0xff,
-		(EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
+	printk(KERN_INFO "%s: v4l2 driver version %s\n",
+		dev->name, EM28XX_VERSION);
 
 	/* set default norm */
 	dev->norm = em28xx_video_template.current_norm;
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 3cca331..d80658b 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -117,9 +117,9 @@
 #define EM2800_BOARD_VC211A			  74
 #define EM2882_BOARD_DIKOM_DK300		  75
 #define EM2870_BOARD_KWORLD_A340		  76
-#define EM2874_LEADERSHIP_ISDBT			  77
+#define EM2874_BOARD_LEADERSHIP_ISDBT		  77
 #define EM28174_BOARD_PCTV_290E                   78
-
+#define EM2884_BOARD_TERRATEC_H5		  79
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -487,6 +487,8 @@
 	int devno;		/* marks the number of this device */
 	enum em28xx_chip_id chip_id;
 
+	int audio_ifnum;
+
 	struct v4l2_device v4l2_dev;
 	struct em28xx_board board;
 
@@ -503,6 +505,7 @@
 
 	unsigned int has_audio_class:1;
 	unsigned int has_alsa_audio:1;
+	unsigned int is_audio_only:1;
 
 	/* Controls audio streaming */
 	struct work_struct wq_trigger;              /* Trigger to start/stop audio for alsa module */
@@ -697,6 +700,9 @@
 void em28xx_release_resources(struct em28xx *dev);
 
 /* Provided by em28xx-input.c */
+
+#ifdef CONFIG_VIDEO_EM28XX_RC
+
 int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -709,6 +715,20 @@
 int em28xx_ir_init(struct em28xx *dev);
 int em28xx_ir_fini(struct em28xx *dev);
 
+#else
+
+#define em28xx_get_key_terratec			NULL
+#define em28xx_get_key_em_haup			NULL
+#define em28xx_get_key_pinnacle_usb_grey	NULL
+#define em28xx_get_key_winfast_usbii_deluxe	NULL
+
+static inline void em28xx_register_snapshot_button(struct em28xx *dev) {}
+static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {}
+static inline int em28xx_ir_init(struct em28xx *dev) { return 0; }
+static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; }
+
+#endif
+
 /* Provided by em28xx-vbi.c */
 extern struct videobuf_queue_ops em28xx_vbi_qops;
 
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
index bf66189..14bb907 100644
--- a/drivers/media/video/et61x251/et61x251.h
+++ b/drivers/media/video/et61x251/et61x251.h
@@ -21,7 +21,6 @@
 #ifndef _ET61X251_H_
 #define _ET61X251_H_
 
-#include <linux/version.h>
 #include <linux/usb.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index a982750..9a1e80a 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
  ***************************************************************************/
 
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -48,8 +49,7 @@
 #define ET61X251_MODULE_AUTHOR  "(C) 2006-2007 Luca Risolia"
 #define ET61X251_AUTHOR_EMAIL   "<luca.risolia@studio.unibo.it>"
 #define ET61X251_MODULE_LICENSE "GPL"
-#define ET61X251_MODULE_VERSION "1:1.09"
-#define ET61X251_MODULE_VERSION_CODE  KERNEL_VERSION(1, 1, 9)
+#define ET61X251_MODULE_VERSION "1.1.10"
 
 /*****************************************************************************/
 
@@ -1579,7 +1579,7 @@
 {
 	struct v4l2_capability cap = {
 		.driver = "et61x251",
-		.version = ET61X251_MODULE_VERSION_CODE,
+		.version = LINUX_VERSION_CODE,
 		.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
 				V4L2_CAP_STREAMING,
 	};
@@ -2480,16 +2480,8 @@
 	case VIDIOC_S_PARM:
 		return et61x251_vidioc_s_parm(cam, arg);
 
-	case VIDIOC_G_STD:
-	case VIDIOC_S_STD:
-	case VIDIOC_QUERYSTD:
-	case VIDIOC_ENUMSTD:
-	case VIDIOC_QUERYMENU:
-	case VIDIOC_ENUM_FRAMEINTERVALS:
-		return -EINVAL;
-
 	default:
-		return -EINVAL;
+		return -ENOTTY;
 
 	}
 }
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 908d701..27cb197 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -23,19 +23,13 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf-dma-contig.h>
 
 #define DRV_NAME		"fsl_viu"
-#define VIU_MAJOR_VERSION	0
-#define VIU_MINOR_VERSION	5
-#define VIU_RELEASE		0
-#define VIU_VERSION		KERNEL_VERSION(VIU_MAJOR_VERSION, \
-					       VIU_MINOR_VERSION, \
-					       VIU_RELEASE)
+#define VIU_VERSION		"0.5.1"
 
 #define BUFFER_TIMEOUT		msecs_to_jiffies(500)  /* 0.5 seconds */
 
@@ -610,7 +604,6 @@
 {
 	strcpy(cap->driver, "viu");
 	strcpy(cap->card, "viu");
-	cap->version = VIU_VERSION;
 	cap->capabilities =	V4L2_CAP_VIDEO_CAPTURE |
 				V4L2_CAP_STREAMING     |
 				V4L2_CAP_VIDEO_OVERLAY |
@@ -1684,3 +1677,4 @@
 MODULE_DESCRIPTION("Freescale Video-In(VIU)");
 MODULE_AUTHOR("Hongjun Chen");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(VIU_VERSION);
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 34ae2c2..43d9a20 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -179,6 +179,16 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called gspca_pac7311.
 
+config USB_GSPCA_SE401
+	tristate "SE401 USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	 Say Y here if you want support for cameras based on the
+	 Endpoints (formerly known as AOX) se401 chip.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called gspca_se401.
+
 config USB_GSPCA_SN9C2028
 	tristate "SONIX Dual-Mode USB Camera Driver"
 	depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 802fbe1..d6364a8 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_USB_GSPCA_PAC207)   += gspca_pac207.o
 obj-$(CONFIG_USB_GSPCA_PAC7302)  += gspca_pac7302.o
 obj-$(CONFIG_USB_GSPCA_PAC7311)  += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SE401)    += gspca_se401.o
 obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o
 obj-$(CONFIG_USB_GSPCA_SN9C20X)  += gspca_sn9c20x.o
 obj-$(CONFIG_USB_GSPCA_SONIXB)   += gspca_sonixb.o
@@ -58,6 +59,7 @@
 gspca_pac207-objs   := pac207.o
 gspca_pac7302-objs  := pac7302.o
 gspca_pac7311-objs  := pac7311.o
+gspca_se401-objs    := se401.o
 gspca_sn9c2028-objs := sn9c2028.o
 gspca_sn9c20x-objs  := sn9c20x.o
 gspca_sonixb-objs   := sonixb.o
diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h
index 49ad4ac..0330a02 100644
--- a/drivers/media/video/gspca/gl860/gl860.h
+++ b/drivers/media/video/gspca/gl860/gl860.h
@@ -18,7 +18,6 @@
  */
 #ifndef GL860_DEV_H
 #define GL860_DEV_H
-#include <linux/version.h>
 
 #include "gspca.h"
 
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 08ce994..5da4879 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -24,7 +24,6 @@
 #define MODULE_NAME "gspca"
 
 #include <linux/init.h>
-#include <linux/version.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
 #include <linux/sched.h>
@@ -51,11 +50,12 @@
 #error "DEF_NURBS too big"
 #endif
 
+#define DRIVER_VERSION_NUMBER	"2.13.0"
+
 MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_LICENSE("GPL");
-
-#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(2, 13, 0)
+MODULE_VERSION(DRIVER_VERSION_NUMBER);
 
 #ifdef GSPCA_DEBUG
 int gspca_debug = D_ERR | D_PROBE;
@@ -443,8 +443,11 @@
 	} else {
 		switch (gspca_dev->last_packet_type) {
 		case DISCARD_PACKET:
-			if (packet_type == LAST_PACKET)
+			if (packet_type == LAST_PACKET) {
 				gspca_dev->last_packet_type = packet_type;
+				gspca_dev->image = NULL;
+				gspca_dev->image_len = 0;
+			}
 			return;
 		case LAST_PACKET:
 			return;
@@ -1278,10 +1281,10 @@
 		ret = -ENODEV;
 		goto out;
 	}
-	strncpy((char *) cap->driver, gspca_dev->sd_desc->name,
+	strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
 			sizeof cap->driver);
 	if (gspca_dev->dev->product != NULL) {
-		strncpy((char *) cap->card, gspca_dev->dev->product,
+		strlcpy((char *) cap->card, gspca_dev->dev->product,
 			sizeof cap->card);
 	} else {
 		snprintf((char *) cap->card, sizeof cap->card,
@@ -1291,7 +1294,6 @@
 	}
 	usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
 			sizeof(cap->bus_info));
-	cap->version = DRIVER_VERSION_NUMBER;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
 			  | V4L2_CAP_STREAMING
 			  | V4L2_CAP_READWRITE;
@@ -1460,7 +1462,7 @@
 		return -EINVAL;
 	input->type = V4L2_INPUT_TYPE_CAMERA;
 	input->status = gspca_dev->cam.input_flags;
-	strncpy(input->name, gspca_dev->sd_desc->name,
+	strlcpy(input->name, gspca_dev->sd_desc->name,
 		sizeof input->name);
 	return 0;
 }
@@ -2478,10 +2480,7 @@
 /* -- module insert / remove -- */
 static int __init gspca_init(void)
 {
-	info("v%d.%d.%d registered",
-		(DRIVER_VERSION_NUMBER >> 16) & 0xff,
-		(DRIVER_VERSION_NUMBER >> 8) & 0xff,
-		DRIVER_VERSION_NUMBER & 0xff);
+	info("v" DRIVER_VERSION_NUMBER " registered");
 	return 0;
 }
 static void __exit gspca_exit(void)
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 057e287..0800433 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -134,6 +134,7 @@
 	SEN_OV7670,
 	SEN_OV76BE,
 	SEN_OV8610,
+	SEN_OV9600,
 };
 
 /* Note this is a bit of a hack, but the w9968cf driver needs the code for all
@@ -340,6 +341,10 @@
 			(1 << EXPOSURE) |
 			(1 << AUTOGAIN) |
 			(1 << FREQ),
+[SEN_OV9600] =		((1 << NCTRL) - 1)	/* no control */
+			^ ((1 << EXPOSURE)	/* but exposure */
+			 | (1 << AUTOGAIN)),	/* and autogain */
+
 };
 
 static const struct v4l2_pix_format ov519_vga_mode[] = {
@@ -525,6 +530,17 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0},
 };
+static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
+	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 1},
+	{1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+		.bytesperline = 1280,
+		.sizeimage = 1280 * 1024,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
 
 /* Registers common to OV511 / OV518 */
 #define R51x_FIFO_PSIZE			0x30	/* 2 bytes wide w/ OV518(+) */
@@ -1807,6 +1823,22 @@
 			| OV7670_COM8_AEC},
 	{0xa1, 0xc8}
 };
+static const struct ov_i2c_regvals norm_9600[] = {
+	{0x12, 0x80},
+	{0x0c, 0x28},
+	{0x11, 0x80},
+	{0x13, 0xb5},
+	{0x14, 0x3e},
+	{0x1b, 0x04},
+	{0x24, 0xb0},
+	{0x25, 0x90},
+	{0x26, 0x94},
+	{0x35, 0x90},
+	{0x37, 0x07},
+	{0x38, 0x08},
+	{0x01, 0x8e},
+	{0x02, 0x85}
+};
 
 /* 7670. Defaults taken from OmniVision provided data,
 *  as provided by Jonathan Corbet of OLPC		*/
@@ -2400,9 +2432,12 @@
 
 	/* Initiate 2-byte write cycle */
 	reg_w(sd, R518_I2C_CTL, 0x03);
+	reg_r8(sd, R518_I2C_CTL);
 
 	/* Initiate 2-byte read cycle */
 	reg_w(sd, R518_I2C_CTL, 0x05);
+	reg_r8(sd, R518_I2C_CTL);
+
 	value = reg_r(sd, R51x_I2C_DATA);
 	PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
 	return value;
@@ -2686,7 +2721,7 @@
  *
  ***************************************************************************/
 
-/* This initializes the OV2x10 / OV3610 / OV3620 */
+/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
 static void ov_hires_configure(struct sd *sd)
 {
 	int high, low;
@@ -2702,19 +2737,32 @@
 	high = i2c_r(sd, 0x0a);
 	low = i2c_r(sd, 0x0b);
 	/* info("%x, %x", high, low); */
-	if (high == 0x96 && low == 0x40) {
-		PDEBUG(D_PROBE, "Sensor is an OV2610");
-		sd->sensor = SEN_OV2610;
-	} else if (high == 0x96 && low == 0x41) {
-		PDEBUG(D_PROBE, "Sensor is an OV2610AE");
-		sd->sensor = SEN_OV2610AE;
-	} else if (high == 0x36 && (low & 0x0f) == 0x00) {
-		PDEBUG(D_PROBE, "Sensor is an OV3610");
-		sd->sensor = SEN_OV3610;
-	} else {
-		err("Error unknown sensor type: %02x%02x",
-			high, low);
+	switch (high) {
+	case 0x96:
+		switch (low) {
+		case 0x40:
+			PDEBUG(D_PROBE, "Sensor is a OV2610");
+			sd->sensor = SEN_OV2610;
+			return;
+		case 0x41:
+			PDEBUG(D_PROBE, "Sensor is a OV2610AE");
+			sd->sensor = SEN_OV2610AE;
+			return;
+		case 0xb1:
+			PDEBUG(D_PROBE, "Sensor is a OV9600");
+			sd->sensor = SEN_OV9600;
+			return;
+		}
+		break;
+	case 0x36:
+		if ((low & 0x0f) == 0x00) {
+			PDEBUG(D_PROBE, "Sensor is a OV3610");
+			sd->sensor = SEN_OV3610;
+			return;
+		}
+		break;
 	}
+	err("Error unknown sensor type: %02x%02x", high, low);
 }
 
 /* This initializes the OV8110, OV8610 sensor. The OV8110 uses
@@ -3400,6 +3448,10 @@
 			cam->cam_mode = ovfx2_ov3610_mode;
 			cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
 			break;
+		case SEN_OV9600:
+			cam->cam_mode = ovfx2_ov9600_mode;
+			cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
+			break;
 		default:
 			if (sd->sif) {
 				cam->cam_mode = ov519_sif_mode;
@@ -3497,6 +3549,12 @@
 	case SEN_OV8610:
 		write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
 		break;
+	case SEN_OV9600:
+		write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
+
+		/* enable autoexpo */
+/*		i2c_w_mask(sd, 0x13, 0x05, 0x05); */
+		break;
 	}
 	return gspca_dev->usb_err;
 error:
@@ -4085,6 +4143,33 @@
 		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
 		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
 		break;
+	case SEN_OV9600: {
+		const struct ov_i2c_regvals *vals;
+		static const struct ov_i2c_regvals sxga_15[] = {
+			{0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+		};
+		static const struct ov_i2c_regvals sxga_7_5[] = {
+			{0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+		};
+		static const struct ov_i2c_regvals vga_30[] = {
+			{0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
+		};
+		static const struct ov_i2c_regvals vga_15[] = {
+			{0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
+		};
+
+		/* frame rates:
+		 *	15fps / 7.5 fps for 1280x1024
+		 *	30fps / 15fps for 640x480
+		 */
+		i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
+		if (qvga)
+			vals = sd->frame_rate < 30 ? vga_15 : vga_30;
+		else
+			vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
+		write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
+		return;
+	    }
 	default:
 		return;
 	}
@@ -4120,6 +4205,7 @@
 	case SEN_OV2610AE:
 	case SEN_OV3610:
 	case SEN_OV7670:
+	case SEN_OV9600:
 		mode_init_ov_sensor_regs(sd);
 		return;
 	case SEN_OV7660:
@@ -4920,7 +5006,8 @@
 static const struct usb_device_id device_table[] = {
 	{USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
 	{USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
-	{USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
+	{USB_DEVICE(0x041e, 0x405f),
+		.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
 	{USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
 	{USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
 	{USB_DEVICE(0x041e, 0x4064),
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c
new file mode 100644
index 0000000..4c283c2
--- /dev/null
+++ b/drivers/media/video/gspca/se401.c
@@ -0,0 +1,774 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define MODULE_NAME "se401"
+
+#define BULK_SIZE 4096
+#define PACKET_SIZE 1024
+#define READ_REQ_SIZE 64
+#define MAX_MODES ((READ_REQ_SIZE - 6) / 4)
+/* The se401 compression algorithm uses a fixed quant factor, which
+   can be configured by setting the high nibble of the SE401_OPERATINGMODE
+   feature. This needs to exactly match what is in libv4l! */
+#define SE401_QUANT_FACT 8
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+#include "se401.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Endpoints se401");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+	BRIGHTNESS,
+	GAIN,
+	EXPOSURE,
+	FREQ,
+	NCTRL	/* number of controls */
+};
+
+/* exposure change state machine states */
+enum {
+	EXPO_CHANGED,
+	EXPO_DROP_FRAME,
+	EXPO_NO_CHANGE,
+};
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct gspca_ctrl ctrls[NCTRL];
+	struct v4l2_pix_format fmts[MAX_MODES];
+	int pixels_read;
+	int packet_read;
+	u8 packet[PACKET_SIZE];
+	u8 restart_stream;
+	u8 button_state;
+	u8 resetlevel;
+	u8 resetlevel_frame_count;
+	int resetlevel_adjust_dir;
+	int expo_change_state;
+};
+
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRL] = {
+[BRIGHTNESS] = {
+		{
+			.id      = V4L2_CID_BRIGHTNESS,
+			.type    = V4L2_CTRL_TYPE_INTEGER,
+			.name    = "Brightness",
+			.minimum = 0,
+			.maximum = 255,
+			.step    = 1,
+			.default_value = 15,
+		},
+		.set_control = setbrightness
+	},
+[GAIN] = {
+		{
+			.id      = V4L2_CID_GAIN,
+			.type    = V4L2_CTRL_TYPE_INTEGER,
+			.name    = "Gain",
+			.minimum = 0,
+			.maximum = 50, /* Really 63 but > 50 is not pretty */
+			.step    = 1,
+			.default_value = 25,
+		},
+		.set_control = setgain
+	},
+[EXPOSURE] = {
+		{
+			.id = V4L2_CID_EXPOSURE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 32767,
+			.step = 1,
+			.default_value = 15000,
+		},
+		.set_control = setexposure
+	},
+[FREQ] = {
+		{
+			.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
+			.type    = V4L2_CTRL_TYPE_MENU,
+			.name    = "Light frequency filter",
+			.minimum = 0,
+			.maximum = 2,
+			.step    = 1,
+			.default_value = 0,
+		},
+		.set_control = setexposure
+	},
+};
+
+static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
+			    int silent)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0), req,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      value, 0, NULL, 0, 1000);
+	if (err < 0) {
+		if (!silent)
+			err("write req failed req %#04x val %#04x error %d",
+			    req, value, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	if (USB_BUF_SZ < READ_REQ_SIZE) {
+		err("USB_BUF_SZ too small!!");
+		gspca_dev->usb_err = -ENOBUFS;
+		return;
+	}
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_rcvctrlpipe(gspca_dev->dev, 0), req,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000);
+	if (err < 0) {
+		if (!silent)
+			err("read req failed req %#04x error %d", req, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static void se401_set_feature(struct gspca_dev *gspca_dev,
+			      u16 selector, u16 param)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_sndctrlpipe(gspca_dev->dev, 0),
+			      SE401_REQ_SET_EXT_FEATURE,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      param, selector, NULL, 0, 1000);
+	if (err < 0) {
+		err("set feature failed sel %#04x param %#04x error %d",
+		    selector, param, err);
+		gspca_dev->usb_err = err;
+	}
+}
+
+static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
+{
+	int err;
+
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
+
+	if (USB_BUF_SZ < 2) {
+		err("USB_BUF_SZ too small!!");
+		gspca_dev->usb_err = -ENOBUFS;
+		return gspca_dev->usb_err;
+	}
+
+	err = usb_control_msg(gspca_dev->dev,
+			      usb_rcvctrlpipe(gspca_dev->dev, 0),
+			      SE401_REQ_GET_EXT_FEATURE,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, selector, gspca_dev->usb_buf, 2, 1000);
+	if (err < 0) {
+		err("get feature failed sel %#04x error %d", selector, err);
+		gspca_dev->usb_err = err;
+		return err;
+	}
+	return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+		return;
+
+	/* HDG: this does not seem to do anything on my cam */
+	se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
+			sd->ctrls[BRIGHTNESS].val, 0);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u16 gain = 63 - sd->ctrls[GAIN].val;
+
+	/* red color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
+	/* green color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain);
+	/* blue color gain */
+	se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int integration = sd->ctrls[EXPOSURE].val << 6;
+	u8 expose_h, expose_m, expose_l;
+
+	/* Do this before the set_feature calls, for proper timing wrt
+	   the interrupt driven pkt_scan. Note we may still race but that
+	   is not a big issue, the expo change state machine is merely for
+	   avoiding underexposed frames getting send out, if one sneaks
+	   through so be it */
+	sd->expo_change_state = EXPO_CHANGED;
+
+	if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+		integration = integration - integration % 106667;
+	if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+		integration = integration - integration % 88889;
+
+	expose_h = (integration >> 16);
+	expose_m = (integration >> 8);
+	expose_l = integration;
+
+	/* integration time low */
+	se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l);
+	/* integration time mid */
+	se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m);
+	/* integration time high */
+	se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h);
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	struct cam *cam = &gspca_dev->cam;
+	u8 *cd = gspca_dev->usb_buf;
+	int i, j, n;
+	int widths[MAX_MODES], heights[MAX_MODES];
+
+	/* Read the camera descriptor */
+	se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1);
+	if (gspca_dev->usb_err) {
+		/* Sometimes after being idle for a while the se401 won't
+		   respond and needs a good kicking  */
+		usb_reset_device(gspca_dev->dev);
+		gspca_dev->usb_err = 0;
+		se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0);
+	}
+
+	/* Some cameras start with their LED on */
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	if (cd[1] != 0x41) {
+		err("Wrong descriptor type");
+		return -ENODEV;
+	}
+
+	if (!(cd[2] & SE401_FORMAT_BAYER)) {
+		err("Bayer format not supported!");
+		return -ENODEV;
+	}
+
+	if (cd[3])
+		info("ExtraFeatures: %d", cd[3]);
+
+	n = cd[4] | (cd[5] << 8);
+	if (n > MAX_MODES) {
+		err("Too many frame sizes");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < n ; i++) {
+		widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8);
+		heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8);
+	}
+
+	for (i = 0; i < n ; i++) {
+		sd->fmts[i].width = widths[i];
+		sd->fmts[i].height = heights[i];
+		sd->fmts[i].field = V4L2_FIELD_NONE;
+		sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB;
+		sd->fmts[i].priv = 1;
+
+		/* janggu compression only works for 1/4th or 1/16th res */
+		for (j = 0; j < n; j++) {
+			if (widths[j] / 2 == widths[i] &&
+			    heights[j] / 2 == heights[i]) {
+				sd->fmts[i].priv = 2;
+				break;
+			}
+		}
+		/* 1/16th if available too is better then 1/4th, because
+		   we then use a larger area of the sensor */
+		for (j = 0; j < n; j++) {
+			if (widths[j] / 4 == widths[i] &&
+			    heights[j] / 4 == heights[i]) {
+				sd->fmts[i].priv = 4;
+				break;
+			}
+		}
+
+		if (sd->fmts[i].priv == 1) {
+			/* Not a 1/4th or 1/16th res, use bayer */
+			sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8;
+			sd->fmts[i].bytesperline = widths[i];
+			sd->fmts[i].sizeimage = widths[i] * heights[i];
+			info("Frame size: %dx%d bayer", widths[i], heights[i]);
+		} else {
+			/* Found a match use janggu compression */
+			sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401;
+			sd->fmts[i].bytesperline = 0;
+			sd->fmts[i].sizeimage = widths[i] * heights[i] * 3;
+			info("Frame size: %dx%d 1/%dth janggu",
+			     widths[i], heights[i],
+			     sd->fmts[i].priv * sd->fmts[i].priv);
+		}
+	}
+
+	cam->cam_mode = sd->fmts;
+	cam->nmodes = n;
+	cam->bulk = 1;
+	cam->bulk_size = BULK_SIZE;
+	cam->bulk_nurbs = 4;
+	cam->ctrls = sd->ctrls;
+	gspca_dev->nbalt = 1;  /* Ignore the bogus isoc alt settings */
+	sd->resetlevel = 0x2d; /* Set initial resetlevel */
+
+	/* See if the camera supports brightness */
+	se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
+	if (gspca_dev->usb_err) {
+		gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
+		gspca_dev->usb_err = 0;
+	}
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	int mode = 0;
+
+	se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1);
+	if (gspca_dev->usb_err) {
+		/* Sometimes after being idle for a while the se401 won't
+		   respond and needs a good kicking  */
+		usb_reset_device(gspca_dev->dev);
+		gspca_dev->usb_err = 0;
+		se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0);
+	}
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0);
+
+	se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05);
+
+	/* set size + mode */
+	se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH,
+			gspca_dev->width * mult, 0);
+	se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT,
+			gspca_dev->height * mult, 0);
+	/*
+	 * HDG: disabled this as it does not seem to do anything
+	 * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE,
+	 *		   SE401_FORMAT_BAYER, 0);
+	 */
+
+	switch (mult) {
+	case 1: /* Raw bayer */
+		mode = 0x03; break;
+	case 2: /* 1/4th janggu */
+		mode = SE401_QUANT_FACT << 4; break;
+	case 4: /* 1/16th janggu */
+		mode = (SE401_QUANT_FACT << 4) | 0x02; break;
+	}
+	se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
+
+	setbrightness(gspca_dev);
+	setgain(gspca_dev);
+	setexposure(gspca_dev);
+	se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+
+	sd->packet_read = 0;
+	sd->pixels_read = 0;
+	sd->restart_stream = 0;
+	sd->resetlevel_frame_count = 0;
+	sd->resetlevel_adjust_dir = 0;
+	sd->expo_change_state = EXPO_NO_CHANGE;
+
+	se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0);
+
+	return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0);
+	se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+	se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	unsigned int ahrc, alrc;
+	int oldreset, adjust_dir;
+
+	/* Restart the stream if requested do so by pkt_scan */
+	if (sd->restart_stream) {
+		sd_stopN(gspca_dev);
+		sd_start(gspca_dev);
+		sd->restart_stream = 0;
+	}
+
+	/* Automatically adjust sensor reset level
+	   Hyundai have some really nice docs about this and other sensor
+	   related stuff on their homepage: www.hei.co.kr */
+	sd->resetlevel_frame_count++;
+	if (sd->resetlevel_frame_count < 20)
+		return;
+
+	/* For some reason this normally read-only register doesn't get reset
+	   to zero after reading them just once... */
+	se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH);
+	se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+	se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH);
+	se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+	ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) +
+	    se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+	alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) +
+	    se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+
+	/* Not an exact science, but it seems to work pretty well... */
+	oldreset = sd->resetlevel;
+	if (alrc > 10) {
+		while (alrc >= 10 && sd->resetlevel < 63) {
+			sd->resetlevel++;
+			alrc /= 2;
+		}
+	} else if (ahrc > 20) {
+		while (ahrc >= 20 && sd->resetlevel > 0) {
+			sd->resetlevel--;
+			ahrc /= 2;
+		}
+	}
+	/* Detect ping-pong-ing and halve adjustment to avoid overshoot */
+	if (sd->resetlevel > oldreset)
+		adjust_dir = 1;
+	else
+		adjust_dir = -1;
+	if (sd->resetlevel_adjust_dir &&
+	    sd->resetlevel_adjust_dir != adjust_dir)
+		sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2;
+
+	if (sd->resetlevel != oldreset) {
+		sd->resetlevel_adjust_dir = adjust_dir;
+		se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+	}
+
+	sd->resetlevel_frame_count = 0;
+}
+
+static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+
+	switch (sd->expo_change_state) {
+	case EXPO_CHANGED:
+		/* The exposure was changed while this frame
+		   was being send, so this frame is ok */
+		sd->expo_change_state = EXPO_DROP_FRAME;
+		break;
+	case EXPO_DROP_FRAME:
+		/* The exposure was changed while this frame
+		   was being captured, drop it! */
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		sd->expo_change_state = EXPO_NO_CHANGE;
+		break;
+	case EXPO_NO_CHANGE:
+		break;
+	}
+	gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+}
+
+static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	int imagesize = gspca_dev->width * gspca_dev->height;
+	int i, plen, bits, pixels, info, count;
+
+	if (sd->restart_stream)
+		return;
+
+	/* Sometimes a 1024 bytes garbage bulk packet is send between frames */
+	if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) {
+		gspca_dev->last_packet_type = DISCARD_PACKET;
+		return;
+	}
+
+	i = 0;
+	while (i < len) {
+		/* Read header if not already be present from prev bulk pkt */
+		if (sd->packet_read < 4) {
+			count = 4 - sd->packet_read;
+			if (count > len - i)
+				count = len - i;
+			memcpy(&sd->packet[sd->packet_read], &data[i], count);
+			sd->packet_read += count;
+			i += count;
+			if (sd->packet_read < 4)
+				break;
+		}
+		bits   = sd->packet[3] + (sd->packet[2] << 8);
+		pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8);
+		info   = (sd->packet[0] & 0xc0) >> 6;
+		plen   = ((bits + 47) >> 4) << 1;
+		/* Sanity checks */
+		if (plen > 1024) {
+			err("invalid packet len %d restarting stream", plen);
+			goto error;
+		}
+		if (info == 3) {
+			err("unknown frame info value restarting stream");
+			goto error;
+		}
+
+		/* Read (remainder of) packet contents */
+		count = plen - sd->packet_read;
+		if (count > len - i)
+			count = len - i;
+		memcpy(&sd->packet[sd->packet_read], &data[i], count);
+		sd->packet_read += count;
+		i += count;
+		if (sd->packet_read < plen)
+			break;
+
+		sd->pixels_read += pixels;
+		sd->packet_read = 0;
+
+		switch (info) {
+		case 0: /* Frame data */
+			gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet,
+					plen);
+			break;
+		case 1: /* EOF */
+			if (sd->pixels_read != imagesize) {
+				err("frame size %d expected %d",
+				    sd->pixels_read, imagesize);
+				goto error;
+			}
+			sd_complete_frame(gspca_dev, sd->packet, plen);
+			return; /* Discard the rest of the bulk packet !! */
+		case 2: /* SOF */
+			gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet,
+					plen);
+			sd->pixels_read = pixels;
+			break;
+		}
+	}
+	return;
+
+error:
+	sd->restart_stream = 1;
+	/* Give userspace a 0 bytes frame, so our dq callback gets
+	   called and it can restart the stream */
+	gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct cam *cam = &gspca_dev->cam;
+	int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+	if (gspca_dev->image_len == 0) {
+		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+		return;
+	}
+
+	if (gspca_dev->image_len + len >= imagesize) {
+		sd_complete_frame(gspca_dev, data, len);
+		return;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+	if (len == 0)
+		return;
+
+	if (mult == 1) /* mult == 1 means raw bayer */
+		sd_pkt_scan_bayer(gspca_dev, data, len);
+	else
+		sd_pkt_scan_janggu(gspca_dev, data, len);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+			struct v4l2_querymenu *menu)
+{
+	switch (menu->id) {
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		switch (menu->index) {
+		case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
+			strcpy((char *) menu->name, "NoFliker");
+			return 0;
+		case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
+			strcpy((char *) menu->name, "50 Hz");
+			return 0;
+		case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
+			strcpy((char *) menu->name, "60 Hz");
+			return 0;
+		}
+		break;
+	}
+	return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *)gspca_dev;
+	u8 state;
+
+	if (len != 2)
+		return -EINVAL;
+
+	switch (data[0]) {
+	case 0:
+	case 1:
+		state = data[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (sd->button_state != state) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+		input_sync(gspca_dev->input_dev);
+		sd->button_state = state;
+	}
+
+	return 0;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.dq_callback = sd_dq_callback,
+	.pkt_scan = sd_pkt_scan,
+	.querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+	.int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */
+	{USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */
+	{USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */
+	{USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */
+	{USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */
+	{}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+				THIS_MODULE);
+}
+
+static int sd_pre_reset(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static int sd_post_reset(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+#endif
+	.pre_reset = sd_pre_reset,
+	.post_reset = sd_post_reset,
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+	return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+	usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/se401.h b/drivers/media/video/gspca/se401.h
new file mode 100644
index 0000000..96d8ebf
--- /dev/null
+++ b/drivers/media/video/gspca/se401.h
@@ -0,0 +1,90 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define SE401_REQ_GET_CAMERA_DESCRIPTOR		0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE	0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE	0x42
+#define SE401_REQ_CAPTURE_FRAME			0x43
+#define SE401_REQ_GET_BRT			0x44
+#define SE401_REQ_SET_BRT			0x45
+#define SE401_REQ_GET_WIDTH			0x4c
+#define SE401_REQ_SET_WIDTH			0x4d
+#define SE401_REQ_GET_HEIGHT			0x4e
+#define SE401_REQ_SET_HEIGHT			0x4f
+#define SE401_REQ_GET_OUTPUT_MODE		0x50
+#define SE401_REQ_SET_OUTPUT_MODE		0x51
+#define SE401_REQ_GET_EXT_FEATURE		0x52
+#define SE401_REQ_SET_EXT_FEATURE		0x53
+#define SE401_REQ_CAMERA_POWER			0x56
+#define SE401_REQ_LED_CONTROL			0x57
+#define SE401_REQ_BIOS				0xff
+
+#define SE401_BIOS_READ				0x07
+
+#define SE401_FORMAT_BAYER	0x40
+
+/* Hyundai hv7131b registers
+   7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A		0x00
+#define HV7131_REG_MODE_B		0x01
+#define HV7131_REG_MODE_C		0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU		0x10
+#define HV7131_REG_FRSL		0x11
+#define HV7131_REG_FCSU		0x12
+#define HV7131_REG_FCSL		0x13
+#define HV7131_REG_FWHU		0x14
+#define HV7131_REG_FWHL		0x15
+#define HV7131_REG_FWWU		0x16
+#define HV7131_REG_FWWL		0x17
+/* Timing registers: */
+#define HV7131_REG_THBU		0x20
+#define HV7131_REG_THBL		0x21
+#define HV7131_REG_TVBU		0x22
+#define HV7131_REG_TVBL		0x23
+#define HV7131_REG_TITU		0x25
+#define HV7131_REG_TITM		0x26
+#define HV7131_REG_TITL		0x27
+#define HV7131_REG_TMCD		0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV		0x30
+#define HV7131_REG_ARCG		0x31
+#define HV7131_REG_AGCG		0x32
+#define HV7131_REG_ABCG		0x33
+#define HV7131_REG_APBV		0x34
+#define HV7131_REG_ASLP		0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR		0x50
+#define HV7131_REG_OFSG		0x51
+#define HV7131_REG_OFSB		0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH	0x57
+#define HV7131_REG_LOREFNOL	0x58
+#define HV7131_REG_HIREFNOH	0x59
+#define HV7131_REG_HIREFNOL	0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE	0x2000
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index b089c0d..6ec2329 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -247,7 +247,6 @@
 	{0x30, 0x0004, 0x000a},
 	{0xb0, 0x0001, 0x0000},
 
-
 	{0xa1, 0x0080, 0x0001},
 	{0x30, 0x0049, 0x0000},
 	{0x30, 0x0060, 0x0005},
@@ -256,8 +255,6 @@
 	{0x00, 0x0000, 0x2000},
 	{0x00, 0x0013, 0x2301},
 	{0x00, 0x0003, 0x2000},
-	{0x00, 0x0000, 0x2000},
-
 };
 
 /* clicksmart 420 open data ? */
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 7e762d5..d1d733b 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -1387,7 +1387,7 @@
 		return 0;
 	case V4L2_CID_EFFECTS:
 		if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
-			strncpy((char *) menu->name,
+			strlcpy((char *) menu->name,
 				effects_control[menu->index],
 				sizeof menu->name);
 			return 0;
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index a27d93b..441dacf 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -17,7 +17,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/usb.h>
 #include <linux/mutex.h>
 #include <linux/i2c.h>
@@ -474,5 +474,6 @@
 module_exit(hdpvr_exit);
 
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2.1");
 MODULE_AUTHOR("Janne Grunau");
 MODULE_DESCRIPTION("Hauppauge HD PVR driver");
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 514aea7..087f7c0 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -17,7 +17,6 @@
 #include <linux/uaccess.h>
 #include <linux/usb.h>
 #include <linux/mutex.h>
-#include <linux/version.h>
 #include <linux/workqueue.h>
 
 #include <linux/videodev2.h>
@@ -574,7 +573,6 @@
 	strcpy(cap->driver, "hdpvr");
 	strcpy(cap->card, "Hauppauge HD PVR");
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-	cap->version = HDPVR_VERSION;
 	cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE |
 				V4L2_CAP_AUDIO         |
 				V4L2_CAP_READWRITE;
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
index 072f23c..d6439db 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -18,12 +18,6 @@
 #include <media/v4l2-device.h>
 #include <media/ir-kbd-i2c.h>
 
-#define HDPVR_MAJOR_VERSION 0
-#define HDPVR_MINOR_VERSION 2
-#define HDPVR_RELEASE 0
-#define HDPVR_VERSION \
-	KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE)
-
 #define HDPVR_MAX 8
 #define HDPVR_I2C_MAX_SIZE 128
 
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 84bdf0f..8f9cc17 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -36,7 +36,6 @@
  *                using information provided by Jiun-Kuei Jung @ AVerMedia.
  */
 
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/delay.h>
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index a7f54b0..38f0522 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -722,8 +722,8 @@
 
 	/* If there are subscribed events, then only use the new event
 	   API instead of the old video.h based API. */
-	if (!list_empty(&id->fh.events->subscribed)) {
-		poll_wait(filp, &id->fh.events->wait, wait);
+	if (!list_empty(&id->fh.subscribed)) {
+		poll_wait(filp, &id->fh.wait, wait);
 		/* Turn off the old-style vsync events */
 		clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
 		if (v4l2_event_pending(&id->fh))
@@ -750,6 +750,7 @@
 	struct ivtv *itv = id->itv;
 	struct ivtv_stream *s = &itv->streams[id->type];
 	int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+	unsigned res = 0;
 
 	/* Start a capture if there is none */
 	if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
@@ -769,12 +770,16 @@
 	/* add stream's waitq to the poll list */
 	IVTV_DEBUG_HI_FILE("Encoder poll\n");
 	poll_wait(filp, &s->waitq, wait);
+	if (v4l2_event_pending(&id->fh))
+		res |= POLLPRI;
+	else
+		poll_wait(filp, &id->fh.wait, wait);
 
 	if (s->q_full.length || s->q_io.length)
-		return POLLIN | POLLRDNORM;
+		return res | POLLIN | POLLRDNORM;
 	if (eof)
-		return POLLHUP;
-	return 0;
+		return res | POLLHUP;
+	return res;
 }
 
 void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
@@ -961,10 +966,6 @@
 		return -ENOMEM;
 	}
 	v4l2_fh_init(&item->fh, s->vdev);
-	if (s->type == IVTV_DEC_STREAM_TYPE_YUV ||
-	    s->type == IVTV_DEC_STREAM_TYPE_MPG) {
-		res = v4l2_event_alloc(&item->fh, 60);
-	}
 	if (res < 0) {
 		v4l2_fh_exit(&item->fh);
 		kfree(item);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 120c7d8..3e5c090 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -757,7 +757,6 @@
 	strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
 	strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
 	snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
-	vcap->version = IVTV_DRIVER_VERSION; 	    /* version */
 	vcap->capabilities = itv->v4l2_cap; 	    /* capabilities */
 	return 0;
 }
@@ -1451,11 +1450,11 @@
 	switch (sub->type) {
 	case V4L2_EVENT_VSYNC:
 	case V4L2_EVENT_EOS:
-		break;
+	case V4L2_EVENT_CTRL:
+		return v4l2_event_subscribe(fh, sub, 0);
 	default:
 		return -EINVAL;
 	}
-	return v4l2_event_subscribe(fh, sub);
 }
 
 static int ivtv_log_status(struct file *file, void *fh)
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
index b67a404..a20f346 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -21,11 +21,6 @@
 #define IVTV_VERSION_H
 
 #define IVTV_DRIVER_NAME "ivtv"
-#define IVTV_DRIVER_VERSION_MAJOR 1
-#define IVTV_DRIVER_VERSION_MINOR 4
-#define IVTV_DRIVER_VERSION_PATCHLEVEL 2
-
-#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
-#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_VERSION "1.4.3"
 
 #endif
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index a45d8f0..3248ac8 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -18,7 +18,6 @@
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/version.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/videodev2.h>
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 43c68f5..fb8e4a7 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -18,7 +18,6 @@
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/version.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/videodev2.h>
diff --git a/drivers/media/video/marvell-ccic/Kconfig b/drivers/media/video/marvell-ccic/Kconfig
new file mode 100644
index 0000000..bf739e3
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Kconfig
@@ -0,0 +1,23 @@
+config VIDEO_CAFE_CCIC
+	tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
+	depends on PCI && I2C && VIDEO_V4L2
+	select VIDEO_OV7670
+	select VIDEOBUF2_VMALLOC
+	select VIDEOBUF2_DMA_CONTIG
+	---help---
+	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
+	  CMOS camera controller.  This is the controller found on first-
+	  generation OLPC systems.
+
+config VIDEO_MMP_CAMERA
+	tristate "Marvell Armada 610 integrated camera controller support"
+	depends on ARCH_MMP && I2C && VIDEO_V4L2
+	select VIDEO_OV7670
+	select I2C_GPIO
+	select VIDEOBUF2_DMA_SG
+	---help---
+	  This is a Video4Linux2 driver for the integrated camera
+	  controller found on Marvell Armada 610 application
+	  processors (and likely beyond).  This is the controller found
+	  in OLPC XO 1.75 systems.
+
diff --git a/drivers/media/video/marvell-ccic/Makefile b/drivers/media/video/marvell-ccic/Makefile
new file mode 100644
index 0000000..05a792c
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+cafe_ccic-y := cafe-driver.o mcam-core.o
+
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
+mmp_camera-y := mmp-driver.o mcam-core.o
+
diff --git a/drivers/media/video/marvell-ccic/cafe-driver.c b/drivers/media/video/marvell-ccic/cafe-driver.c
new file mode 100644
index 0000000..d030f9b
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/cafe-driver.c
@@ -0,0 +1,654 @@
+/*
+ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
+ * multifunction chip.  Currently works with the Omnivision OV7670
+ * sensor.
+ *
+ * The data sheet for this device can be found at:
+ *    http://www.marvell.com/products/pc_connectivity/88alp01/
+ *
+ * Copyright 2006-11 One Laptop Per Child Association, Inc.
+ * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net>
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "mcam-core.h"
+
+#define CAFE_VERSION 0x000002
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Video");
+
+
+
+
+struct cafe_camera {
+	int registered;			/* Fully initialized? */
+	struct mcam_camera mcam;
+	struct pci_dev *pdev;
+	wait_queue_head_t smbus_wait;	/* Waiting on i2c events */
+};
+
+/*
+ * Most of the camera controller registers are defined in mcam-core.h,
+ * but the Cafe platform has some additional registers of its own;
+ * they are described here.
+ */
+
+/*
+ * "General purpose register" has a couple of GPIOs used for sensor
+ * power and reset on OLPC XO 1.0 systems.
+ */
+#define REG_GPR		0xb4
+#define	  GPR_C1EN	  0x00000020	/* Pad 1 (power down) enable */
+#define	  GPR_C0EN	  0x00000010	/* Pad 0 (reset) enable */
+#define	  GPR_C1	  0x00000002	/* Control 1 value */
+/*
+ * Control 0 is wired to reset on OLPC machines.  For ov7x sensors,
+ * it is active low.
+ */
+#define	  GPR_C0	  0x00000001	/* Control 0 value */
+
+/*
+ * These registers control the SMBUS module for communicating
+ * with the sensor.
+ */
+#define REG_TWSIC0	0xb8	/* TWSI (smbus) control 0 */
+#define	  TWSIC0_EN	  0x00000001	/* TWSI enable */
+#define	  TWSIC0_MODE	  0x00000002	/* 1 = 16-bit, 0 = 8-bit */
+#define	  TWSIC0_SID	  0x000003fc	/* Slave ID */
+/*
+ * Subtle trickery: the slave ID field starts with bit 2.  But the
+ * Linux i2c stack wants to treat the bottommost bit as a separate
+ * read/write bit, which is why slave ID's are usually presented
+ * >>1.  For consistency with that behavior, we shift over three
+ * bits instead of two.
+ */
+#define	  TWSIC0_SID_SHIFT 3
+#define	  TWSIC0_CLKDIV	  0x0007fc00	/* Clock divider */
+#define	  TWSIC0_MASKACK  0x00400000	/* Mask ack from sensor */
+#define	  TWSIC0_OVMAGIC  0x00800000	/* Make it work on OV sensors */
+
+#define REG_TWSIC1	0xbc	/* TWSI control 1 */
+#define	  TWSIC1_DATA	  0x0000ffff	/* Data to/from camchip */
+#define	  TWSIC1_ADDR	  0x00ff0000	/* Address (register) */
+#define	  TWSIC1_ADDR_SHIFT 16
+#define	  TWSIC1_READ	  0x01000000	/* Set for read op */
+#define	  TWSIC1_WSTAT	  0x02000000	/* Write status */
+#define	  TWSIC1_RVALID	  0x04000000	/* Read data valid */
+#define	  TWSIC1_ERROR	  0x08000000	/* Something screwed up */
+
+/*
+ * Here's the weird global control registers
+ */
+#define REG_GL_CSR     0x3004  /* Control/status register */
+#define	  GCSR_SRS	 0x00000001	/* SW Reset set */
+#define	  GCSR_SRC	 0x00000002	/* SW Reset clear */
+#define	  GCSR_MRS	 0x00000004	/* Master reset set */
+#define	  GCSR_MRC	 0x00000008	/* HW Reset clear */
+#define	  GCSR_CCIC_EN	 0x00004000    /* CCIC Clock enable */
+#define REG_GL_IMASK   0x300c  /* Interrupt mask register */
+#define	  GIMSK_CCIC_EN		 0x00000004    /* CCIC Interrupt enable */
+
+#define REG_GL_FCR	0x3038	/* GPIO functional control register */
+#define	  GFCR_GPIO_ON	  0x08		/* Camera GPIO enabled */
+#define REG_GL_GPIOR	0x315c	/* GPIO register */
+#define	  GGPIO_OUT		0x80000	/* GPIO output */
+#define	  GGPIO_VAL		0x00008	/* Output pin value */
+
+#define REG_LEN		       (REG_GL_IMASK + 4)
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+	dev_err(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+	dev_warn(&(cam)->pdev->dev, fmt, ##arg);
+
+/* -------------------------------------------------------------------- */
+/*
+ * The I2C/SMBUS interface to the camera itself starts here.  The
+ * controller handles SMBUS itself, presenting a relatively simple register
+ * interface; all we have to do is to tell it where to route the data.
+ */
+#define CAFE_SMBUS_TIMEOUT (HZ)  /* generous */
+
+static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
+{
+	struct mcam_camera *m = container_of(dev, struct mcam_camera, v4l2_dev);
+	return container_of(m, struct cafe_camera, mcam);
+}
+
+
+static int cafe_smbus_write_done(struct mcam_camera *mcam)
+{
+	unsigned long flags;
+	int c1;
+
+	/*
+	 * We must delay after the interrupt, or the controller gets confused
+	 * and never does give us good status.  Fortunately, we don't do this
+	 * often.
+	 */
+	udelay(20);
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	c1 = mcam_reg_read(mcam, REG_TWSIC1);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+	return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
+}
+
+static int cafe_smbus_write_data(struct cafe_camera *cam,
+		u16 addr, u8 command, u8 value)
+{
+	unsigned int rval;
+	unsigned long flags;
+	struct mcam_camera *mcam = &cam->mcam;
+
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+	rval |= TWSIC0_OVMAGIC;  /* Make OV sensors work */
+	/*
+	 * Marvell sez set clkdiv to all 1's for now.
+	 */
+	rval |= TWSIC0_CLKDIV;
+	mcam_reg_write(mcam, REG_TWSIC0, rval);
+	(void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+	rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+	mcam_reg_write(mcam, REG_TWSIC1, rval);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+	/* Unfortunately, reading TWSIC1 too soon after sending a command
+	 * causes the device to die.
+	 * Use a busy-wait because we often send a large quantity of small
+	 * commands at-once; using msleep() would cause a lot of context
+	 * switches which take longer than 2ms, resulting in a noticeable
+	 * boot-time and capture-start delays.
+	 */
+	mdelay(2);
+
+	/*
+	 * Another sad fact is that sometimes, commands silently complete but
+	 * cafe_smbus_write_done() never becomes aware of this.
+	 * This happens at random and appears to possible occur with any
+	 * command.
+	 * We don't understand why this is. We work around this issue
+	 * with the timeout in the wait below, assuming that all commands
+	 * complete within the timeout.
+	 */
+	wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(mcam),
+			CAFE_SMBUS_TIMEOUT);
+
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	rval = mcam_reg_read(mcam, REG_TWSIC1);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+	if (rval & TWSIC1_WSTAT) {
+		cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
+				command, value);
+		return -EIO;
+	}
+	if (rval & TWSIC1_ERROR) {
+		cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
+				command, value);
+		return -EIO;
+	}
+	return 0;
+}
+
+
+
+static int cafe_smbus_read_done(struct mcam_camera *mcam)
+{
+	unsigned long flags;
+	int c1;
+
+	/*
+	 * We must delay after the interrupt, or the controller gets confused
+	 * and never does give us good status.  Fortunately, we don't do this
+	 * often.
+	 */
+	udelay(20);
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	c1 = mcam_reg_read(mcam, REG_TWSIC1);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+	return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
+}
+
+
+
+static int cafe_smbus_read_data(struct cafe_camera *cam,
+		u16 addr, u8 command, u8 *value)
+{
+	unsigned int rval;
+	unsigned long flags;
+	struct mcam_camera *mcam = &cam->mcam;
+
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+	rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+	/*
+	 * Marvel sez set clkdiv to all 1's for now.
+	 */
+	rval |= TWSIC0_CLKDIV;
+	mcam_reg_write(mcam, REG_TWSIC0, rval);
+	(void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+	rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+	mcam_reg_write(mcam, REG_TWSIC1, rval);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+	wait_event_timeout(cam->smbus_wait,
+			cafe_smbus_read_done(mcam), CAFE_SMBUS_TIMEOUT);
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	rval = mcam_reg_read(mcam, REG_TWSIC1);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+	if (rval & TWSIC1_ERROR) {
+		cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
+		return -EIO;
+	}
+	if (!(rval & TWSIC1_RVALID)) {
+		cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
+				command);
+		return -EIO;
+	}
+	*value = rval & 0xff;
+	return 0;
+}
+
+/*
+ * Perform a transfer over SMBUS.  This thing is called under
+ * the i2c bus lock, so we shouldn't race with ourselves...
+ */
+static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+		unsigned short flags, char rw, u8 command,
+		int size, union i2c_smbus_data *data)
+{
+	struct cafe_camera *cam = i2c_get_adapdata(adapter);
+	int ret = -EINVAL;
+
+	/*
+	 * This interface would appear to only do byte data ops.  OK
+	 * it can do word too, but the cam chip has no use for that.
+	 */
+	if (size != I2C_SMBUS_BYTE_DATA) {
+		cam_err(cam, "funky xfer size %d\n", size);
+		return -EINVAL;
+	}
+
+	if (rw == I2C_SMBUS_WRITE)
+		ret = cafe_smbus_write_data(cam, addr, command, data->byte);
+	else if (rw == I2C_SMBUS_READ)
+		ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
+	return ret;
+}
+
+
+static void cafe_smbus_enable_irq(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->mcam.dev_lock, flags);
+	mcam_reg_set_bit(&cam->mcam, REG_IRQMASK, TWSIIRQS);
+	spin_unlock_irqrestore(&cam->mcam.dev_lock, flags);
+}
+
+static u32 cafe_smbus_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_READ_BYTE_DATA  |
+	       I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+static struct i2c_algorithm cafe_smbus_algo = {
+	.smbus_xfer = cafe_smbus_xfer,
+	.functionality = cafe_smbus_func
+};
+
+static int cafe_smbus_setup(struct cafe_camera *cam)
+{
+	struct i2c_adapter *adap;
+	int ret;
+
+	adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+	if (adap == NULL)
+		return -ENOMEM;
+	cam->mcam.i2c_adapter = adap;
+	cafe_smbus_enable_irq(cam);
+	adap->owner = THIS_MODULE;
+	adap->algo = &cafe_smbus_algo;
+	strcpy(adap->name, "cafe_ccic");
+	adap->dev.parent = &cam->pdev->dev;
+	i2c_set_adapdata(adap, cam);
+	ret = i2c_add_adapter(adap);
+	if (ret)
+		printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+	return ret;
+}
+
+static void cafe_smbus_shutdown(struct cafe_camera *cam)
+{
+	i2c_del_adapter(cam->mcam.i2c_adapter);
+	kfree(cam->mcam.i2c_adapter);
+}
+
+
+/*
+ * Controller-level stuff
+ */
+
+static void cafe_ctlr_init(struct mcam_camera *mcam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+	/*
+	 * Added magic to bring up the hardware on the B-Test board
+	 */
+	mcam_reg_write(mcam, 0x3038, 0x8);
+	mcam_reg_write(mcam, 0x315c, 0x80008);
+	/*
+	 * Go through the dance needed to wake the device up.
+	 * Note that these registers are global and shared
+	 * with the NAND and SD devices.  Interaction between the
+	 * three still needs to be examined.
+	 */
+	mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
+	mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
+	mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+	/*
+	 * Here we must wait a bit for the controller to come around.
+	 */
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+	msleep(5);
+	spin_lock_irqsave(&mcam->dev_lock, flags);
+
+	mcam_reg_write(mcam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
+	mcam_reg_set_bit(mcam, REG_GL_IMASK, GIMSK_CCIC_EN);
+	/*
+	 * Mask all interrupts.
+	 */
+	mcam_reg_write(mcam, REG_IRQMASK, 0);
+	spin_unlock_irqrestore(&mcam->dev_lock, flags);
+}
+
+
+static void cafe_ctlr_power_up(struct mcam_camera *mcam)
+{
+	/*
+	 * Part one of the sensor dance: turn the global
+	 * GPIO signal on.
+	 */
+	mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+	mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
+	/*
+	 * Put the sensor into operational mode (assumes OLPC-style
+	 * wiring).  Control 0 is reset - set to 1 to operate.
+	 * Control 1 is power down, set to 0 to operate.
+	 */
+	mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+	mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+}
+
+static void cafe_ctlr_power_down(struct mcam_camera *mcam)
+{
+	mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+	mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+	mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT);
+}
+
+
+
+/*
+ * The platform interrupt handler.
+ */
+static irqreturn_t cafe_irq(int irq, void *data)
+{
+	struct cafe_camera *cam = data;
+	struct mcam_camera *mcam = &cam->mcam;
+	unsigned int irqs, handled;
+
+	spin_lock(&mcam->dev_lock);
+	irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+	handled = cam->registered && mccic_irq(mcam, irqs);
+	if (irqs & TWSIIRQS) {
+		mcam_reg_write(mcam, REG_IRQSTAT, TWSIIRQS);
+		wake_up(&cam->smbus_wait);
+		handled = 1;
+	}
+	spin_unlock(&mcam->dev_lock);
+	return IRQ_RETVAL(handled);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/*
+ * PCI interface stuff.
+ */
+
+static int cafe_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	int ret;
+	struct cafe_camera *cam;
+	struct mcam_camera *mcam;
+
+	/*
+	 * Start putting together one of our big camera structures.
+	 */
+	ret = -ENOMEM;
+	cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+	if (cam == NULL)
+		goto out;
+	cam->pdev = pdev;
+	mcam = &cam->mcam;
+	mcam->chip_id = V4L2_IDENT_CAFE;
+	spin_lock_init(&mcam->dev_lock);
+	init_waitqueue_head(&cam->smbus_wait);
+	mcam->plat_power_up = cafe_ctlr_power_up;
+	mcam->plat_power_down = cafe_ctlr_power_down;
+	mcam->dev = &pdev->dev;
+	/*
+	 * Set the clock speed for the XO 1; I don't believe this
+	 * driver has ever run anywhere else.
+	 */
+	mcam->clock_speed = 45;
+	mcam->use_smbus = 1;
+	/*
+	 * Vmalloc mode for buffers is traditional with this driver.
+	 * We *might* be able to run DMA_contig, especially on a system
+	 * with CMA in it.
+	 */
+	mcam->buffer_mode = B_vmalloc;
+	/*
+	 * Get set up on the PCI bus.
+	 */
+	ret = pci_enable_device(pdev);
+	if (ret)
+		goto out_free;
+	pci_set_master(pdev);
+
+	ret = -EIO;
+	mcam->regs = pci_iomap(pdev, 0, 0);
+	if (!mcam->regs) {
+		printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+		goto out_disable;
+	}
+	ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+	if (ret)
+		goto out_iounmap;
+
+	/*
+	 * Initialize the controller and leave it powered up.  It will
+	 * stay that way until the sensor driver shows up.
+	 */
+	cafe_ctlr_init(mcam);
+	cafe_ctlr_power_up(mcam);
+	/*
+	 * Set up I2C/SMBUS communications.  We have to drop the mutex here
+	 * because the sensor could attach in this call chain, leading to
+	 * unsightly deadlocks.
+	 */
+	ret = cafe_smbus_setup(cam);
+	if (ret)
+		goto out_pdown;
+
+	ret = mccic_register(mcam);
+	if (ret == 0) {
+		cam->registered = 1;
+		return 0;
+	}
+
+	cafe_smbus_shutdown(cam);
+out_pdown:
+	cafe_ctlr_power_down(mcam);
+	free_irq(pdev->irq, cam);
+out_iounmap:
+	pci_iounmap(pdev, mcam->regs);
+out_disable:
+	pci_disable_device(pdev);
+out_free:
+	kfree(cam);
+out:
+	return ret;
+}
+
+
+/*
+ * Shut down an initialized device
+ */
+static void cafe_shutdown(struct cafe_camera *cam)
+{
+	mccic_shutdown(&cam->mcam);
+	cafe_smbus_shutdown(cam);
+	free_irq(cam->pdev->irq, cam);
+	pci_iounmap(cam->pdev, cam->mcam.regs);
+}
+
+
+static void cafe_pci_remove(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+	struct cafe_camera *cam = to_cam(v4l2_dev);
+
+	if (cam == NULL) {
+		printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
+		return;
+	}
+	cafe_shutdown(cam);
+	kfree(cam);
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+	struct cafe_camera *cam = to_cam(v4l2_dev);
+	int ret;
+
+	ret = pci_save_state(pdev);
+	if (ret)
+		return ret;
+	mccic_suspend(&cam->mcam);
+	pci_disable_device(pdev);
+	return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+	struct cafe_camera *cam = to_cam(v4l2_dev);
+	int ret = 0;
+
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+
+	if (ret) {
+		cam_warn(cam, "Unable to re-enable device on resume!\n");
+		return ret;
+	}
+	cafe_ctlr_init(&cam->mcam);
+	return mccic_resume(&cam->mcam);
+}
+
+#endif  /* CONFIG_PM */
+
+static struct pci_device_id cafe_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
+		     PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_ids);
+
+static struct pci_driver cafe_pci_driver = {
+	.name = "cafe1000-ccic",
+	.id_table = cafe_ids,
+	.probe = cafe_pci_probe,
+	.remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+	.suspend = cafe_pci_suspend,
+	.resume = cafe_pci_resume,
+#endif
+};
+
+
+
+
+static int __init cafe_init(void)
+{
+	int ret;
+
+	printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+			CAFE_VERSION);
+	ret = pci_register_driver(&cafe_pci_driver);
+	if (ret) {
+		printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+		goto out;
+	}
+	ret = 0;
+
+out:
+	return ret;
+}
+
+
+static void __exit cafe_exit(void)
+{
+	pci_unregister_driver(&cafe_pci_driver);
+}
+
+module_init(cafe_init);
+module_exit(cafe_exit);
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
new file mode 100644
index 0000000..83c1451
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -0,0 +1,1843 @@
+/*
+ * The Marvell camera core.  This device appears in a number of settings,
+ * so it needs platform-specific support outside of the core.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/ov7670.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "mcam-core.h"
+
+/*
+ * Basic frame stats - to be deleted shortly
+ */
+static int frames;
+static int singles;
+static int delivered;
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Internal DMA buffer management.  Since the controller cannot do S/G I/O,
+ * we must have physically contiguous buffers to bring frames into.
+ * These parameters control how many buffers we use, whether we
+ * allocate them at load time (better chance of success, but nails down
+ * memory) or when somebody tries to use the camera (riskier), and,
+ * for load-time allocation, how big they should be.
+ *
+ * The controller can cycle through three buffers.  We could use
+ * more by flipping pointers around, but it probably makes little
+ * sense.
+ */
+
+static int alloc_bufs_at_read;
+module_param(alloc_bufs_at_read, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_read,
+		"Non-zero value causes DMA buffers to be allocated when the "
+		"video capture device is read, rather than at module load "
+		"time.  This saves memory, but decreases the chances of "
+		"successfully getting those buffers.  This parameter is "
+		"only used in the vmalloc buffer mode");
+
+static int n_dma_bufs = 3;
+module_param(n_dma_bufs, uint, 0644);
+MODULE_PARM_DESC(n_dma_bufs,
+		"The number of DMA buffers to allocate.  Can be either two "
+		"(saves memory, makes timing tighter) or three.");
+
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2;  /* Worst case */
+module_param(dma_buf_size, uint, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+		"The size of the allocated DMA buffers.  If actual operating "
+		"parameters require larger buffers, an attempt to reallocate "
+		"will be made.");
+#else /* MCAM_MODE_VMALLOC */
+static const int alloc_bufs_at_read = 0;
+static const int n_dma_bufs = 3;  /* Used by S/G_PARM */
+#endif /* MCAM_MODE_VMALLOC */
+
+static int flip;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+		"If set, the sensor will be instructed to flip the image "
+		"vertically.");
+
+static int buffer_mode = -1;
+module_param(buffer_mode, int, 0444);
+MODULE_PARM_DESC(buffer_mode,
+		"Set the buffer mode to be used; default is to go with what "
+		"the platform driver asks for.  Set to 0 for vmalloc, 1 for "
+		"DMA contiguous.");
+
+/*
+ * Status flags.  Always manipulated with bit operations.
+ */
+#define CF_BUF0_VALID	 0	/* Buffers valid - first three */
+#define CF_BUF1_VALID	 1
+#define CF_BUF2_VALID	 2
+#define CF_DMA_ACTIVE	 3	/* A frame is incoming */
+#define CF_CONFIG_NEEDED 4	/* Must configure hardware */
+#define CF_SINGLE_BUFFER 5	/* Running with a single buffer */
+#define CF_SG_RESTART	 6	/* SG restart needed */
+
+#define sensor_call(cam, o, f, args...) \
+	v4l2_subdev_call(cam->sensor, o, f, ##args)
+
+static struct mcam_format_struct {
+	__u8 *desc;
+	__u32 pixelformat;
+	int bpp;   /* Bytes per pixel */
+	enum v4l2_mbus_pixelcode mbus_code;
+} mcam_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "RGB 444",
+		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "Raw RGB Bayer",
+		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
+		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
+		.bpp		= 1
+	},
+};
+#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
+
+static struct mcam_format_struct *mcam_find_format(u32 pixelformat)
+{
+	unsigned i;
+
+	for (i = 0; i < N_MCAM_FMTS; i++)
+		if (mcam_formats[i].pixelformat == pixelformat)
+			return mcam_formats + i;
+	/* Not found? Then return the first format. */
+	return mcam_formats;
+}
+
+/*
+ * The default format we use until somebody says otherwise.
+ */
+static const struct v4l2_pix_format mcam_def_pix_format = {
+	.width		= VGA_WIDTH,
+	.height		= VGA_HEIGHT,
+	.pixelformat	= V4L2_PIX_FMT_YUYV,
+	.field		= V4L2_FIELD_NONE,
+	.bytesperline	= VGA_WIDTH*2,
+	.sizeimage	= VGA_WIDTH*VGA_HEIGHT*2,
+};
+
+static const enum v4l2_mbus_pixelcode mcam_def_mbus_code =
+					V4L2_MBUS_FMT_YUYV8_2X8;
+
+
+/*
+ * The two-word DMA descriptor format used by the Armada 610 and like.  There
+ * Is a three-word format as well (set C1_DESC_3WORD) where the third
+ * word is a pointer to the next descriptor, but we don't use it.  Two-word
+ * descriptors have to be contiguous in memory.
+ */
+struct mcam_dma_desc {
+	u32 dma_addr;
+	u32 segment_len;
+};
+
+/*
+ * Our buffer type for working with videobuf2.  Note that the vb2
+ * developers have decreed that struct vb2_buffer must be at the
+ * beginning of this structure.
+ */
+struct mcam_vb_buffer {
+	struct vb2_buffer vb_buf;
+	struct list_head queue;
+	struct mcam_dma_desc *dma_desc;	/* Descriptor virtual address */
+	dma_addr_t dma_desc_pa;		/* Descriptor physical address */
+	int dma_desc_nent;		/* Number of mapped descriptors */
+};
+
+static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct mcam_vb_buffer, vb_buf);
+}
+
+/*
+ * Hand a completed buffer back to user space.
+ */
+static void mcam_buffer_done(struct mcam_camera *cam, int frame,
+		struct vb2_buffer *vbuf)
+{
+	vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage;
+	vbuf->v4l2_buf.sequence = cam->buf_seq[frame];
+	vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage);
+	vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE);
+}
+
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+	dev_err((cam)->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+	dev_warn((cam)->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+	dev_dbg((cam)->dev, fmt, ##arg);
+
+
+/*
+ * Flag manipulation helpers
+ */
+static void mcam_reset_buffers(struct mcam_camera *cam)
+{
+	int i;
+
+	cam->next_buf = -1;
+	for (i = 0; i < cam->nbufs; i++)
+		clear_bit(i, &cam->flags);
+}
+
+static inline int mcam_needs_config(struct mcam_camera *cam)
+{
+	return test_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+static void mcam_set_config_needed(struct mcam_camera *cam, int needed)
+{
+	if (needed)
+		set_bit(CF_CONFIG_NEEDED, &cam->flags);
+	else
+		clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+/* ------------------------------------------------------------------- */
+/*
+ * Make the controller start grabbing images.  Everything must
+ * be set up before doing this.
+ */
+static void mcam_ctlr_start(struct mcam_camera *cam)
+{
+	/* set_bit performs a read, so no other barrier should be
+	   needed here */
+	mcam_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void mcam_ctlr_stop(struct mcam_camera *cam)
+{
+	mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+/* ------------------------------------------------------------------- */
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Code specific to the vmalloc buffer mode.
+ */
+
+/*
+ * Allocate in-kernel DMA buffers for vmalloc mode.
+ */
+static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+	int i;
+
+	mcam_set_config_needed(cam, 1);
+	if (loadtime)
+		cam->dma_buf_size = dma_buf_size;
+	else
+		cam->dma_buf_size = cam->pix_format.sizeimage;
+	if (n_dma_bufs > 3)
+		n_dma_bufs = 3;
+
+	cam->nbufs = 0;
+	for (i = 0; i < n_dma_bufs; i++) {
+		cam->dma_bufs[i] = dma_alloc_coherent(cam->dev,
+				cam->dma_buf_size, cam->dma_handles + i,
+				GFP_KERNEL);
+		if (cam->dma_bufs[i] == NULL) {
+			cam_warn(cam, "Failed to allocate DMA buffer\n");
+			break;
+		}
+		(cam->nbufs)++;
+	}
+
+	switch (cam->nbufs) {
+	case 1:
+		dma_free_coherent(cam->dev, cam->dma_buf_size,
+				cam->dma_bufs[0], cam->dma_handles[0]);
+		cam->nbufs = 0;
+	case 0:
+		cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
+		return -ENOMEM;
+
+	case 2:
+		if (n_dma_bufs > 2)
+			cam_warn(cam, "Will limp along with only 2 buffers\n");
+		break;
+	}
+	return 0;
+}
+
+static void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+	int i;
+
+	for (i = 0; i < cam->nbufs; i++) {
+		dma_free_coherent(cam->dev, cam->dma_buf_size,
+				cam->dma_bufs[i], cam->dma_handles[i]);
+		cam->dma_bufs[i] = NULL;
+	}
+	cam->nbufs = 0;
+}
+
+
+/*
+ * Set up DMA buffers when operating in vmalloc mode
+ */
+static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
+{
+	/*
+	 * Store the first two Y buffers (we aren't supporting
+	 * planar formats for now, so no UV bufs).  Then either
+	 * set the third if it exists, or tell the controller
+	 * to just use two.
+	 */
+	mcam_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
+	mcam_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
+	if (cam->nbufs > 2) {
+		mcam_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
+		mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
+	} else
+		mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+	if (cam->chip_id == V4L2_IDENT_CAFE)
+		mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
+}
+
+/*
+ * Copy data out to user space in the vmalloc case
+ */
+static void mcam_frame_tasklet(unsigned long data)
+{
+	struct mcam_camera *cam = (struct mcam_camera *) data;
+	int i;
+	unsigned long flags;
+	struct mcam_vb_buffer *buf;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	for (i = 0; i < cam->nbufs; i++) {
+		int bufno = cam->next_buf;
+
+		if (cam->state != S_STREAMING || bufno < 0)
+			break;  /* I/O got stopped */
+		if (++(cam->next_buf) >= cam->nbufs)
+			cam->next_buf = 0;
+		if (!test_bit(bufno, &cam->flags))
+			continue;
+		if (list_empty(&cam->buffers)) {
+			singles++;
+			break;  /* Leave it valid, hope for better later */
+		}
+		delivered++;
+		clear_bit(bufno, &cam->flags);
+		buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
+				queue);
+		list_del_init(&buf->queue);
+		/*
+		 * Drop the lock during the big copy.  This *should* be safe...
+		 */
+		spin_unlock_irqrestore(&cam->dev_lock, flags);
+		memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno],
+				cam->pix_format.sizeimage);
+		mcam_buffer_done(cam, bufno, &buf->vb_buf);
+		spin_lock_irqsave(&cam->dev_lock, flags);
+	}
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Make sure our allocated buffers are up to the task.
+ */
+static int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+	if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
+			mcam_free_dma_bufs(cam);
+	if (cam->nbufs == 0)
+		return mcam_alloc_dma_bufs(cam, 0);
+	return 0;
+}
+
+static void mcam_vmalloc_done(struct mcam_camera *cam, int frame)
+{
+	tasklet_schedule(&cam->s_tasklet);
+}
+
+#else /* MCAM_MODE_VMALLOC */
+
+static inline int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+	return 0;
+}
+
+static inline void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+	return;
+}
+
+static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+	return 0;
+}
+
+
+
+#endif /* MCAM_MODE_VMALLOC */
+
+
+#ifdef MCAM_MODE_DMA_CONTIG
+/* ---------------------------------------------------------------------- */
+/*
+ * DMA-contiguous code.
+ */
+/*
+ * Set up a contiguous buffer for the given frame.  Here also is where
+ * the underrun strategy is set: if there is no buffer available, reuse
+ * the buffer from the other BAR and set the CF_SINGLE_BUFFER flag to
+ * keep the interrupt handler from giving that buffer back to user
+ * space.  In this way, we always have a buffer to DMA to and don't
+ * have to try to play games stopping and restarting the controller.
+ */
+static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
+{
+	struct mcam_vb_buffer *buf;
+	/*
+	 * If there are no available buffers, go into single mode
+	 */
+	if (list_empty(&cam->buffers)) {
+		buf = cam->vb_bufs[frame ^ 0x1];
+		cam->vb_bufs[frame] = buf;
+		mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+				vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+		set_bit(CF_SINGLE_BUFFER, &cam->flags);
+		singles++;
+		return;
+	}
+	/*
+	 * OK, we have a buffer we can use.
+	 */
+	buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+	list_del_init(&buf->queue);
+	mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+			vb2_dma_contig_plane_paddr(&buf->vb_buf, 0));
+	cam->vb_bufs[frame] = buf;
+	clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+}
+
+/*
+ * Initial B_DMA_contig setup.
+ */
+static void mcam_ctlr_dma_contig(struct mcam_camera *cam)
+{
+	mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+	cam->nbufs = 2;
+	mcam_set_contig_buffer(cam, 0);
+	mcam_set_contig_buffer(cam, 1);
+}
+
+/*
+ * Frame completion handling.
+ */
+static void mcam_dma_contig_done(struct mcam_camera *cam, int frame)
+{
+	struct mcam_vb_buffer *buf = cam->vb_bufs[frame];
+
+	if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) {
+		delivered++;
+		mcam_buffer_done(cam, frame, &buf->vb_buf);
+	}
+	mcam_set_contig_buffer(cam, frame);
+}
+
+#endif /* MCAM_MODE_DMA_CONTIG */
+
+#ifdef MCAM_MODE_DMA_SG
+/* ---------------------------------------------------------------------- */
+/*
+ * Scatter/gather-specific code.
+ */
+
+/*
+ * Set up the next buffer for S/G I/O; caller should be sure that
+ * the controller is stopped and a buffer is available.
+ */
+static void mcam_sg_next_buffer(struct mcam_camera *cam)
+{
+	struct mcam_vb_buffer *buf;
+
+	buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+	list_del_init(&buf->queue);
+	mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa);
+	mcam_reg_write(cam, REG_DESC_LEN_Y,
+			buf->dma_desc_nent*sizeof(struct mcam_dma_desc));
+	mcam_reg_write(cam, REG_DESC_LEN_U, 0);
+	mcam_reg_write(cam, REG_DESC_LEN_V, 0);
+	cam->vb_bufs[0] = buf;
+}
+
+/*
+ * Initial B_DMA_sg setup
+ */
+static void mcam_ctlr_dma_sg(struct mcam_camera *cam)
+{
+	mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD);
+	mcam_sg_next_buffer(cam);
+	mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
+	cam->nbufs = 3;
+}
+
+
+/*
+ * Frame completion with S/G is trickier.  We can't muck with
+ * a descriptor chain on the fly, since the controller buffers it
+ * internally.  So we have to actually stop and restart; Marvell
+ * says this is the way to do it.
+ *
+ * Of course, stopping is easier said than done; experience shows
+ * that the controller can start a frame *after* C0_ENABLE has been
+ * cleared.  So when running in S/G mode, the controller is "stopped"
+ * on receipt of the start-of-frame interrupt.  That means we can
+ * safely change the DMA descriptor array here and restart things
+ * (assuming there's another buffer waiting to go).
+ */
+static void mcam_dma_sg_done(struct mcam_camera *cam, int frame)
+{
+	struct mcam_vb_buffer *buf = cam->vb_bufs[0];
+
+	/*
+	 * Very Bad Not Good Things happen if you don't clear
+	 * C1_DESC_ENA before making any descriptor changes.
+	 */
+	mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
+	/*
+	 * If we have another buffer available, put it in and
+	 * restart the engine.
+	 */
+	if (!list_empty(&cam->buffers)) {
+		mcam_sg_next_buffer(cam);
+		mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
+		mcam_ctlr_start(cam);
+	/*
+	 * Otherwise set CF_SG_RESTART and the controller will
+	 * be restarted once another buffer shows up.
+	 */
+	} else {
+		set_bit(CF_SG_RESTART, &cam->flags);
+		singles++;
+	}
+	/*
+	 * Now we can give the completed frame back to user space.
+	 */
+	delivered++;
+	mcam_buffer_done(cam, frame, &buf->vb_buf);
+}
+
+
+/*
+ * Scatter/gather mode requires stopping the controller between
+ * frames so we can put in a new DMA descriptor array.  If no new
+ * buffer exists at frame completion, the controller is left stopped;
+ * this function is charged with gettig things going again.
+ */
+static void mcam_sg_restart(struct mcam_camera *cam)
+{
+	mcam_ctlr_dma_sg(cam);
+	mcam_ctlr_start(cam);
+	clear_bit(CF_SG_RESTART, &cam->flags);
+}
+
+#else /* MCAM_MODE_DMA_SG */
+
+static inline void mcam_sg_restart(struct mcam_camera *cam)
+{
+	return;
+}
+
+#endif /* MCAM_MODE_DMA_SG */
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Buffer-mode-independent controller code.
+ */
+
+/*
+ * Image format setup
+ */
+static void mcam_ctlr_image(struct mcam_camera *cam)
+{
+	int imgsz;
+	struct v4l2_pix_format *fmt = &cam->pix_format;
+
+	imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
+		(fmt->bytesperline & IMGSZ_H_MASK);
+	mcam_reg_write(cam, REG_IMGSIZE, imgsz);
+	mcam_reg_write(cam, REG_IMGOFFSET, 0);
+	/* YPITCH just drops the last two bits */
+	mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
+			IMGP_YP_MASK);
+	/*
+	 * Tell the controller about the image format we are using.
+	 */
+	switch (cam->pix_format.pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	    mcam_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
+			    C0_DF_MASK);
+	    break;
+
+	case V4L2_PIX_FMT_RGB444:
+	    mcam_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+			    C0_DF_MASK);
+		/* Alpha value? */
+	    break;
+
+	case V4L2_PIX_FMT_RGB565:
+	    mcam_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+			    C0_DF_MASK);
+	    break;
+
+	default:
+	    cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
+	    break;
+	}
+	/*
+	 * Make sure it knows we want to use hsync/vsync.
+	 */
+	mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
+			C0_SIFM_MASK);
+}
+
+
+/*
+ * Configure the controller for operation; caller holds the
+ * device mutex.
+ */
+static int mcam_ctlr_configure(struct mcam_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cam->dma_setup(cam);
+	mcam_ctlr_image(cam);
+	mcam_set_config_needed(cam, 0);
+	clear_bit(CF_SG_RESTART, &cam->flags);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return 0;
+}
+
+static void mcam_ctlr_irq_enable(struct mcam_camera *cam)
+{
+	/*
+	 * Clear any pending interrupts, since we do not
+	 * expect to have I/O active prior to enabling.
+	 */
+	mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
+	mcam_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+static void mcam_ctlr_irq_disable(struct mcam_camera *cam)
+{
+	mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+
+
+static void mcam_ctlr_init(struct mcam_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	/*
+	 * Make sure it's not powered down.
+	 */
+	mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+	/*
+	 * Turn off the enable bit.  It sure should be off anyway,
+	 * but it's good to be sure.
+	 */
+	mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+	/*
+	 * Clock the sensor appropriately.  Controller clock should
+	 * be 48MHz, sensor "typical" value is half that.
+	 */
+	mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Stop the controller, and don't return until we're really sure that no
+ * further DMA is going on.
+ */
+static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
+{
+	unsigned long flags;
+
+	/*
+	 * Theory: stop the camera controller (whether it is operating
+	 * or not).  Delay briefly just in case we race with the SOF
+	 * interrupt, then wait until no DMA is active.
+	 */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	clear_bit(CF_SG_RESTART, &cam->flags);
+	mcam_ctlr_stop(cam);
+	cam->state = S_IDLE;
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	msleep(40);
+	if (test_bit(CF_DMA_ACTIVE, &cam->flags))
+		cam_err(cam, "Timeout waiting for DMA to end\n");
+		/* This would be bad news - what now? */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	mcam_ctlr_irq_disable(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/*
+ * Power up and down.
+ */
+static void mcam_ctlr_power_up(struct mcam_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cam->plat_power_up(cam);
+	mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	msleep(5); /* Just to be sure */
+}
+
+static void mcam_ctlr_power_down(struct mcam_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	/*
+	 * School of hard knocks department: be sure we do any register
+	 * twiddling on the controller *before* calling the platform
+	 * power down routine.
+	 */
+	mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
+	cam->plat_power_down(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * Communications with the sensor.
+ */
+
+static int __mcam_cam_reset(struct mcam_camera *cam)
+{
+	return sensor_call(cam, core, reset, 0);
+}
+
+/*
+ * We have found the sensor on the i2c.  Let's try to have a
+ * conversation.
+ */
+static int mcam_cam_init(struct mcam_camera *cam)
+{
+	struct v4l2_dbg_chip_ident chip;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	if (cam->state != S_NOTREADY)
+		cam_warn(cam, "Cam init with device in funky state %d",
+				cam->state);
+	ret = __mcam_cam_reset(cam);
+	if (ret)
+		goto out;
+	chip.ident = V4L2_IDENT_NONE;
+	chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
+	chip.match.addr = cam->sensor_addr;
+	ret = sensor_call(cam, core, g_chip_ident, &chip);
+	if (ret)
+		goto out;
+	cam->sensor_type = chip.ident;
+	if (cam->sensor_type != V4L2_IDENT_OV7670) {
+		cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
+		ret = -EINVAL;
+		goto out;
+	}
+/* Get/set parameters? */
+	ret = 0;
+	cam->state = S_IDLE;
+out:
+	mcam_ctlr_power_down(cam);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+/*
+ * Configure the sensor to match the parameters we have.  Caller should
+ * hold s_mutex
+ */
+static int mcam_cam_set_flip(struct mcam_camera *cam)
+{
+	struct v4l2_control ctrl;
+
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.id = V4L2_CID_VFLIP;
+	ctrl.value = flip;
+	return sensor_call(cam, core, s_ctrl, &ctrl);
+}
+
+
+static int mcam_cam_configure(struct mcam_camera *cam)
+{
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
+	ret = sensor_call(cam, core, init, 0);
+	if (ret == 0)
+		ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
+	/*
+	 * OV7670 does weird things if flip is set *before* format...
+	 */
+	ret += mcam_cam_set_flip(cam);
+	return ret;
+}
+
+/*
+ * Get everything ready, and start grabbing frames.
+ */
+static int mcam_read_setup(struct mcam_camera *cam)
+{
+	int ret;
+	unsigned long flags;
+
+	/*
+	 * Configuration.  If we still don't have DMA buffers,
+	 * make one last, desperate attempt.
+	 */
+	if (cam->buffer_mode == B_vmalloc && cam->nbufs == 0 &&
+			mcam_alloc_dma_bufs(cam, 0))
+		return -ENOMEM;
+
+	if (mcam_needs_config(cam)) {
+		mcam_cam_configure(cam);
+		ret = mcam_ctlr_configure(cam);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Turn it loose.
+	 */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	mcam_reset_buffers(cam);
+	mcam_ctlr_irq_enable(cam);
+	cam->state = S_STREAMING;
+	mcam_ctlr_start(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Videobuf2 interface code.
+ */
+
+static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
+		unsigned int *num_planes, unsigned long sizes[],
+		void *alloc_ctxs[])
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vq);
+	int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2;
+
+	sizes[0] = cam->pix_format.sizeimage;
+	*num_planes = 1; /* Someday we have to support planar formats... */
+	if (*nbufs < minbufs)
+		*nbufs = minbufs;
+	if (cam->buffer_mode == B_DMA_contig)
+		alloc_ctxs[0] = cam->vb_alloc_ctx;
+	return 0;
+}
+
+
+static void mcam_vb_buf_queue(struct vb2_buffer *vb)
+{
+	struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long flags;
+	int start;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
+	list_add(&mvb->queue, &cam->buffers);
+	if (test_bit(CF_SG_RESTART, &cam->flags))
+		mcam_sg_restart(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	if (start)
+		mcam_read_setup(cam);
+}
+
+
+/*
+ * vb2 uses these to release the mutex when waiting in dqbuf.  I'm
+ * not actually sure we need to do this (I'm not sure that vb2_dqbuf() needs
+ * to be called with the mutex held), but better safe than sorry.
+ */
+static void mcam_vb_wait_prepare(struct vb2_queue *vq)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+	mutex_unlock(&cam->s_mutex);
+}
+
+static void mcam_vb_wait_finish(struct vb2_queue *vq)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+	mutex_lock(&cam->s_mutex);
+}
+
+/*
+ * These need to be called with the mutex held from vb2
+ */
+static int mcam_vb_start_streaming(struct vb2_queue *vq)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+	if (cam->state != S_IDLE)
+		return -EINVAL;
+	cam->sequence = 0;
+	/*
+	 * Videobuf2 sneakily hoards all the buffers and won't
+	 * give them to us until *after* streaming starts.  But
+	 * we can't actually start streaming until we have a
+	 * destination.  So go into a wait state and hope they
+	 * give us buffers soon.
+	 */
+	if (cam->buffer_mode != B_vmalloc && list_empty(&cam->buffers)) {
+		cam->state = S_BUFWAIT;
+		return 0;
+	}
+	return mcam_read_setup(cam);
+}
+
+static int mcam_vb_stop_streaming(struct vb2_queue *vq)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vq);
+	unsigned long flags;
+
+	if (cam->state == S_BUFWAIT) {
+		/* They never gave us buffers */
+		cam->state = S_IDLE;
+		return 0;
+	}
+	if (cam->state != S_STREAMING)
+		return -EINVAL;
+	mcam_ctlr_stop_dma(cam);
+	/*
+	 * VB2 reclaims the buffers, so we need to forget
+	 * about them.
+	 */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	INIT_LIST_HEAD(&cam->buffers);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return 0;
+}
+
+
+static const struct vb2_ops mcam_vb2_ops = {
+	.queue_setup		= mcam_vb_queue_setup,
+	.buf_queue		= mcam_vb_buf_queue,
+	.start_streaming	= mcam_vb_start_streaming,
+	.stop_streaming		= mcam_vb_stop_streaming,
+	.wait_prepare		= mcam_vb_wait_prepare,
+	.wait_finish		= mcam_vb_wait_finish,
+};
+
+
+#ifdef MCAM_MODE_DMA_SG
+/*
+ * Scatter/gather mode uses all of the above functions plus a
+ * few extras to deal with DMA mapping.
+ */
+static int mcam_vb_sg_buf_init(struct vb2_buffer *vb)
+{
+	struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+	int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+	mvb->dma_desc = dma_alloc_coherent(cam->dev,
+			ndesc * sizeof(struct mcam_dma_desc),
+			&mvb->dma_desc_pa, GFP_KERNEL);
+	if (mvb->dma_desc == NULL) {
+		cam_err(cam, "Unable to get DMA descriptor array\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
+{
+	struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+	struct mcam_dma_desc *desc = mvb->dma_desc;
+	struct scatterlist *sg;
+	int i;
+
+	mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages,
+			DMA_FROM_DEVICE);
+	if (mvb->dma_desc_nent <= 0)
+		return -EIO;  /* Not sure what's right here */
+	for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) {
+		desc->dma_addr = sg_dma_address(sg);
+		desc->segment_len = sg_dma_len(sg);
+		desc++;
+	}
+	return 0;
+}
+
+static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+
+	dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE);
+	return 0;
+}
+
+static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+	struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+	int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+	dma_free_coherent(cam->dev, ndesc * sizeof(struct mcam_dma_desc),
+			mvb->dma_desc, mvb->dma_desc_pa);
+}
+
+
+static const struct vb2_ops mcam_vb2_sg_ops = {
+	.queue_setup		= mcam_vb_queue_setup,
+	.buf_init		= mcam_vb_sg_buf_init,
+	.buf_prepare		= mcam_vb_sg_buf_prepare,
+	.buf_queue		= mcam_vb_buf_queue,
+	.buf_finish		= mcam_vb_sg_buf_finish,
+	.buf_cleanup		= mcam_vb_sg_buf_cleanup,
+	.start_streaming	= mcam_vb_start_streaming,
+	.stop_streaming		= mcam_vb_stop_streaming,
+	.wait_prepare		= mcam_vb_wait_prepare,
+	.wait_finish		= mcam_vb_wait_finish,
+};
+
+#endif /* MCAM_MODE_DMA_SG */
+
+static int mcam_setup_vb2(struct mcam_camera *cam)
+{
+	struct vb2_queue *vq = &cam->vb_queue;
+
+	memset(vq, 0, sizeof(*vq));
+	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vq->drv_priv = cam;
+	INIT_LIST_HEAD(&cam->buffers);
+	switch (cam->buffer_mode) {
+	case B_DMA_contig:
+#ifdef MCAM_MODE_DMA_CONTIG
+		vq->ops = &mcam_vb2_ops;
+		vq->mem_ops = &vb2_dma_contig_memops;
+		cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
+		vq->io_modes = VB2_MMAP | VB2_USERPTR;
+		cam->dma_setup = mcam_ctlr_dma_contig;
+		cam->frame_complete = mcam_dma_contig_done;
+#endif
+		break;
+	case B_DMA_sg:
+#ifdef MCAM_MODE_DMA_SG
+		vq->ops = &mcam_vb2_sg_ops;
+		vq->mem_ops = &vb2_dma_sg_memops;
+		vq->io_modes = VB2_MMAP | VB2_USERPTR;
+		cam->dma_setup = mcam_ctlr_dma_sg;
+		cam->frame_complete = mcam_dma_sg_done;
+#endif
+		break;
+	case B_vmalloc:
+#ifdef MCAM_MODE_VMALLOC
+		tasklet_init(&cam->s_tasklet, mcam_frame_tasklet,
+				(unsigned long) cam);
+		vq->ops = &mcam_vb2_ops;
+		vq->mem_ops = &vb2_vmalloc_memops;
+		vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
+		vq->io_modes = VB2_MMAP;
+		cam->dma_setup = mcam_ctlr_dma_vmalloc;
+		cam->frame_complete = mcam_vmalloc_done;
+#endif
+		break;
+	}
+	return vb2_queue_init(vq);
+}
+
+static void mcam_cleanup_vb2(struct mcam_camera *cam)
+{
+	vb2_queue_release(&cam->vb_queue);
+#ifdef MCAM_MODE_DMA_CONTIG
+	if (cam->buffer_mode == B_DMA_contig)
+		vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
+#endif
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The long list of V4L2 ioctl() operations.
+ */
+
+static int mcam_vidioc_streamon(struct file *filp, void *priv,
+		enum v4l2_buf_type type)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_streamon(&cam->vb_queue, type);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_streamoff(struct file *filp, void *priv,
+		enum v4l2_buf_type type)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_streamoff(&cam->vb_queue, type);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_reqbufs(struct file *filp, void *priv,
+		struct v4l2_requestbuffers *req)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_reqbufs(&cam->vb_queue, req);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_querybuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_querybuf(&cam->vb_queue, buf);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int mcam_vidioc_qbuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_qbuf(&cam->vb_queue, buf);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int mcam_vidioc_dqbuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+static int mcam_vidioc_queryctrl(struct file *filp, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, core, queryctrl, qc);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_g_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, core, g_ctrl, ctrl);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_s_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, core, s_ctrl, ctrl);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "marvell_ccic");
+	strcpy(cap->card, "marvell_ccic");
+	cap->version = 1;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+
+static int mcam_vidioc_enum_fmt_vid_cap(struct file *filp,
+		void *priv, struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->index >= N_MCAM_FMTS)
+		return -EINVAL;
+	strlcpy(fmt->description, mcam_formats[fmt->index].desc,
+			sizeof(fmt->description));
+	fmt->pixelformat = mcam_formats[fmt->index].pixelformat;
+	return 0;
+}
+
+static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct mcam_camera *cam = priv;
+	struct mcam_format_struct *f;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	f = mcam_find_format(pix->pixelformat);
+	pix->pixelformat = f->pixelformat;
+	v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
+	mutex_unlock(&cam->s_mutex);
+	v4l2_fill_pix_format(pix, &mbus_fmt);
+	pix->bytesperline = pix->width * f->bpp;
+	pix->sizeimage = pix->height * pix->bytesperline;
+	return ret;
+}
+
+static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct mcam_camera *cam = priv;
+	struct mcam_format_struct *f;
+	int ret;
+
+	/*
+	 * Can't do anything if the device is not idle
+	 * Also can't if there are streaming buffers in place.
+	 */
+	if (cam->state != S_IDLE || cam->vb_queue.num_buffers > 0)
+		return -EBUSY;
+
+	f = mcam_find_format(fmt->fmt.pix.pixelformat);
+
+	/*
+	 * See if the formatting works in principle.
+	 */
+	ret = mcam_vidioc_try_fmt_vid_cap(filp, priv, fmt);
+	if (ret)
+		return ret;
+	/*
+	 * Now we start to change things for real, so let's do it
+	 * under lock.
+	 */
+	mutex_lock(&cam->s_mutex);
+	cam->pix_format = fmt->fmt.pix;
+	cam->mbus_code = f->mbus_code;
+
+	/*
+	 * Make sure we have appropriate DMA buffers.
+	 */
+	if (cam->buffer_mode == B_vmalloc) {
+		ret = mcam_check_dma_buffers(cam);
+		if (ret)
+			goto out;
+	}
+	mcam_set_config_needed(cam, 1);
+	ret = 0;
+out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+/*
+ * Return our stored notion of how the camera is/should be configured.
+ * The V4l2 spec wants us to be smarter, and actually get this from
+ * the camera (and not mess with it at open time).  Someday.
+ */
+static int mcam_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *f)
+{
+	struct mcam_camera *cam = priv;
+
+	f->fmt.pix = cam->pix_format;
+	return 0;
+}
+
+/*
+ * We only have one input - the sensor - so minimize the nonsense here.
+ */
+static int mcam_vidioc_enum_input(struct file *filp, void *priv,
+		struct v4l2_input *input)
+{
+	if (input->index != 0)
+		return -EINVAL;
+
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->std = V4L2_STD_ALL; /* Not sure what should go here */
+	strcpy(input->name, "Camera");
+	return 0;
+}
+
+static int mcam_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+/* from vivi.c */
+static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+	return 0;
+}
+
+/*
+ * G/S_PARM.  Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int mcam_vidioc_g_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, g_parm, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+static int mcam_vidioc_s_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, s_parm, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+static int mcam_vidioc_g_chip_ident(struct file *file, void *priv,
+		struct v4l2_dbg_chip_ident *chip)
+{
+	struct mcam_camera *cam = priv;
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	if (v4l2_chip_match_host(&chip->match)) {
+		chip->ident = cam->chip_id;
+		return 0;
+	}
+	return sensor_call(cam, core, g_chip_ident, chip);
+}
+
+static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv,
+		struct v4l2_frmsizeenum *sizes)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, enum_framesizes, sizes);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv,
+		struct v4l2_frmivalenum *interval)
+{
+	struct mcam_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, enum_frameintervals, interval);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mcam_vidioc_g_register(struct file *file, void *priv,
+		struct v4l2_dbg_register *reg)
+{
+	struct mcam_camera *cam = priv;
+
+	if (v4l2_chip_match_host(&reg->match)) {
+		reg->val = mcam_reg_read(cam, reg->reg);
+		reg->size = 4;
+		return 0;
+	}
+	return sensor_call(cam, core, g_register, reg);
+}
+
+static int mcam_vidioc_s_register(struct file *file, void *priv,
+		struct v4l2_dbg_register *reg)
+{
+	struct mcam_camera *cam = priv;
+
+	if (v4l2_chip_match_host(&reg->match)) {
+		mcam_reg_write(cam, reg->reg, reg->val);
+		return 0;
+	}
+	return sensor_call(cam, core, s_register, reg);
+}
+#endif
+
+static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = {
+	.vidioc_querycap	= mcam_vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = mcam_vidioc_enum_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= mcam_vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= mcam_vidioc_s_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= mcam_vidioc_g_fmt_vid_cap,
+	.vidioc_enum_input	= mcam_vidioc_enum_input,
+	.vidioc_g_input		= mcam_vidioc_g_input,
+	.vidioc_s_input		= mcam_vidioc_s_input,
+	.vidioc_s_std		= mcam_vidioc_s_std,
+	.vidioc_reqbufs		= mcam_vidioc_reqbufs,
+	.vidioc_querybuf	= mcam_vidioc_querybuf,
+	.vidioc_qbuf		= mcam_vidioc_qbuf,
+	.vidioc_dqbuf		= mcam_vidioc_dqbuf,
+	.vidioc_streamon	= mcam_vidioc_streamon,
+	.vidioc_streamoff	= mcam_vidioc_streamoff,
+	.vidioc_queryctrl	= mcam_vidioc_queryctrl,
+	.vidioc_g_ctrl		= mcam_vidioc_g_ctrl,
+	.vidioc_s_ctrl		= mcam_vidioc_s_ctrl,
+	.vidioc_g_parm		= mcam_vidioc_g_parm,
+	.vidioc_s_parm		= mcam_vidioc_s_parm,
+	.vidioc_enum_framesizes = mcam_vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals,
+	.vidioc_g_chip_ident	= mcam_vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register	= mcam_vidioc_g_register,
+	.vidioc_s_register	= mcam_vidioc_s_register,
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Our various file operations.
+ */
+static int mcam_v4l_open(struct file *filp)
+{
+	struct mcam_camera *cam = video_drvdata(filp);
+	int ret = 0;
+
+	filp->private_data = cam;
+
+	frames = singles = delivered = 0;
+	mutex_lock(&cam->s_mutex);
+	if (cam->users == 0) {
+		ret = mcam_setup_vb2(cam);
+		if (ret)
+			goto out;
+		mcam_ctlr_power_up(cam);
+		__mcam_cam_reset(cam);
+		mcam_set_config_needed(cam, 1);
+	}
+	(cam->users)++;
+out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_v4l_release(struct file *filp)
+{
+	struct mcam_camera *cam = filp->private_data;
+
+	cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
+			singles, delivered);
+	mutex_lock(&cam->s_mutex);
+	(cam->users)--;
+	if (filp == cam->owner) {
+		mcam_ctlr_stop_dma(cam);
+		cam->owner = NULL;
+	}
+	if (cam->users == 0) {
+		mcam_cleanup_vb2(cam);
+		mcam_ctlr_power_down(cam);
+		if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
+			mcam_free_dma_bufs(cam);
+	}
+	mutex_unlock(&cam->s_mutex);
+	return 0;
+}
+
+static ssize_t mcam_v4l_read(struct file *filp,
+		char __user *buffer, size_t len, loff_t *pos)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_read(&cam->vb_queue, buffer, len, pos,
+			filp->f_flags & O_NONBLOCK);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+static unsigned int mcam_v4l_poll(struct file *filp,
+		struct poll_table_struct *pt)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_poll(&cam->vb_queue, filp, pt);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int mcam_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct mcam_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = vb2_mmap(&cam->vb_queue, vma);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+static const struct v4l2_file_operations mcam_v4l_fops = {
+	.owner = THIS_MODULE,
+	.open = mcam_v4l_open,
+	.release = mcam_v4l_release,
+	.read = mcam_v4l_read,
+	.poll = mcam_v4l_poll,
+	.mmap = mcam_v4l_mmap,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+
+/*
+ * This template device holds all of those v4l2 methods; we
+ * clone it for specific real devices.
+ */
+static struct video_device mcam_v4l_template = {
+	.name = "mcam",
+	.tvnorms = V4L2_STD_NTSC_M,
+	.current_norm = V4L2_STD_NTSC_M,  /* make mplayer happy */
+
+	.fops = &mcam_v4l_fops,
+	.ioctl_ops = &mcam_v4l_ioctl_ops,
+	.release = video_device_release_empty,
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+static void mcam_frame_complete(struct mcam_camera *cam, int frame)
+{
+	/*
+	 * Basic frame housekeeping.
+	 */
+	set_bit(frame, &cam->flags);
+	clear_bit(CF_DMA_ACTIVE, &cam->flags);
+	cam->next_buf = frame;
+	cam->buf_seq[frame] = ++(cam->sequence);
+	frames++;
+	/*
+	 * "This should never happen"
+	 */
+	if (cam->state != S_STREAMING)
+		return;
+	/*
+	 * Process the frame and set up the next one.
+	 */
+	cam->frame_complete(cam, frame);
+}
+
+
+/*
+ * The interrupt handler; this needs to be called from the
+ * platform irq handler with the lock held.
+ */
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
+{
+	unsigned int frame, handled = 0;
+
+	mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
+	/*
+	 * Handle any frame completions.  There really should
+	 * not be more than one of these, or we have fallen
+	 * far behind.
+	 *
+	 * When running in S/G mode, the frame number lacks any
+	 * real meaning - there's only one descriptor array - but
+	 * the controller still picks a different one to signal
+	 * each time.
+	 */
+	for (frame = 0; frame < cam->nbufs; frame++)
+		if (irqs & (IRQ_EOF0 << frame)) {
+			mcam_frame_complete(cam, frame);
+			handled = 1;
+		}
+	/*
+	 * If a frame starts, note that we have DMA active.  This
+	 * code assumes that we won't get multiple frame interrupts
+	 * at once; may want to rethink that.
+	 */
+	if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
+		set_bit(CF_DMA_ACTIVE, &cam->flags);
+		handled = 1;
+		if (cam->buffer_mode == B_DMA_sg)
+			mcam_ctlr_stop(cam);
+	}
+	return handled;
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Registration and such.
+ */
+static struct ov7670_config sensor_cfg = {
+	/*
+	 * Exclude QCIF mode, because it only captures a tiny portion
+	 * of the sensor FOV
+	 */
+	.min_width = 320,
+	.min_height = 240,
+};
+
+
+int mccic_register(struct mcam_camera *cam)
+{
+	struct i2c_board_info ov7670_info = {
+		.type = "ov7670",
+		.addr = 0x42 >> 1,
+		.platform_data = &sensor_cfg,
+	};
+	int ret;
+
+	/*
+	 * Validate the requested buffer mode.
+	 */
+	if (buffer_mode >= 0)
+		cam->buffer_mode = buffer_mode;
+	if (cam->buffer_mode == B_DMA_sg &&
+			cam->chip_id == V4L2_IDENT_CAFE) {
+		printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, "
+			"attempting vmalloc mode instead\n");
+		cam->buffer_mode = B_vmalloc;
+	}
+	if (!mcam_buffer_mode_supported(cam->buffer_mode)) {
+		printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n",
+				cam->buffer_mode);
+		return -EINVAL;
+	}
+	/*
+	 * Register with V4L
+	 */
+	ret = v4l2_device_register(cam->dev, &cam->v4l2_dev);
+	if (ret)
+		return ret;
+
+	mutex_init(&cam->s_mutex);
+	cam->state = S_NOTREADY;
+	mcam_set_config_needed(cam, 1);
+	cam->pix_format = mcam_def_pix_format;
+	cam->mbus_code = mcam_def_mbus_code;
+	INIT_LIST_HEAD(&cam->buffers);
+	mcam_ctlr_init(cam);
+
+	/*
+	 * Try to find the sensor.
+	 */
+	sensor_cfg.clock_speed = cam->clock_speed;
+	sensor_cfg.use_smbus = cam->use_smbus;
+	cam->sensor_addr = ov7670_info.addr;
+	cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev,
+			cam->i2c_adapter, &ov7670_info, NULL);
+	if (cam->sensor == NULL) {
+		ret = -ENODEV;
+		goto out_unregister;
+	}
+
+	ret = mcam_cam_init(cam);
+	if (ret)
+		goto out_unregister;
+	/*
+	 * Get the v4l2 setup done.
+	 */
+	mutex_lock(&cam->s_mutex);
+	cam->vdev = mcam_v4l_template;
+	cam->vdev.debug = 0;
+	cam->vdev.v4l2_dev = &cam->v4l2_dev;
+	ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+	if (ret)
+		goto out;
+	video_set_drvdata(&cam->vdev, cam);
+
+	/*
+	 * If so requested, try to get our DMA buffers now.
+	 */
+	if (cam->buffer_mode == B_vmalloc && !alloc_bufs_at_read) {
+		if (mcam_alloc_dma_bufs(cam, 1))
+			cam_warn(cam, "Unable to alloc DMA buffers at load"
+					" will try again later.");
+	}
+
+out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+out_unregister:
+	v4l2_device_unregister(&cam->v4l2_dev);
+	return ret;
+}
+
+
+void mccic_shutdown(struct mcam_camera *cam)
+{
+	/*
+	 * If we have no users (and we really, really should have no
+	 * users) the device will already be powered down.  Trying to
+	 * take it down again will wedge the machine, which is frowned
+	 * upon.
+	 */
+	if (cam->users > 0) {
+		cam_warn(cam, "Removing a device with users!\n");
+		mcam_ctlr_power_down(cam);
+	}
+	vb2_queue_release(&cam->vb_queue);
+	if (cam->buffer_mode == B_vmalloc)
+		mcam_free_dma_bufs(cam);
+	video_unregister_device(&cam->vdev);
+	v4l2_device_unregister(&cam->v4l2_dev);
+}
+
+/*
+ * Power management
+ */
+#ifdef CONFIG_PM
+
+void mccic_suspend(struct mcam_camera *cam)
+{
+	enum mcam_state cstate = cam->state;
+
+	mcam_ctlr_stop_dma(cam);
+	mcam_ctlr_power_down(cam);
+	cam->state = cstate;
+}
+
+int mccic_resume(struct mcam_camera *cam)
+{
+	int ret = 0;
+
+	mutex_lock(&cam->s_mutex);
+	if (cam->users > 0) {
+		mcam_ctlr_power_up(cam);
+		__mcam_cam_reset(cam);
+	} else {
+		mcam_ctlr_power_down(cam);
+	}
+	mutex_unlock(&cam->s_mutex);
+
+	set_bit(CF_CONFIG_NEEDED, &cam->flags);
+	if (cam->state == S_STREAMING)
+		ret = mcam_read_setup(cam);
+	return ret;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h
new file mode 100644
index 0000000..917200e
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.h
@@ -0,0 +1,323 @@
+/*
+ * Marvell camera core structures.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#ifndef _MCAM_CORE_H
+#define _MCAM_CORE_H
+
+#include <linux/list.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * Create our own symbols for the supported buffer modes, but, for now,
+ * base them entirely on which videobuf2 options have been selected.
+ */
+#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE)
+#define MCAM_MODE_VMALLOC 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE)
+#define MCAM_MODE_DMA_CONTIG 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE)
+#define MCAM_MODE_DMA_SG 1
+#endif
+
+#if !defined(MCAM_MODE_VMALLOC) && !defined(MCAM_MODE_DMA_CONTIG) && \
+	!defined(MCAM_MODE_DMA_SG)
+#error One of the videobuf buffer modes must be selected in the config
+#endif
+
+
+enum mcam_state {
+	S_NOTREADY,	/* Not yet initialized */
+	S_IDLE,		/* Just hanging around */
+	S_FLAKED,	/* Some sort of problem */
+	S_STREAMING,	/* Streaming data */
+	S_BUFWAIT	/* streaming requested but no buffers yet */
+};
+#define MAX_DMA_BUFS 3
+
+/*
+ * Different platforms work best with different buffer modes, so we
+ * let the platform pick.
+ */
+enum mcam_buffer_mode {
+	B_vmalloc = 0,
+	B_DMA_contig = 1,
+	B_DMA_sg = 2
+};
+
+/*
+ * Is a given buffer mode supported by the current kernel configuration?
+ */
+static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode)
+{
+	switch (mode) {
+#ifdef MCAM_MODE_VMALLOC
+	case B_vmalloc:
+#endif
+#ifdef MCAM_MODE_DMA_CONTIG
+	case B_DMA_contig:
+#endif
+#ifdef MCAM_MODE_DMA_SG
+	case B_DMA_sg:
+#endif
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+
+/*
+ * A description of one of our devices.
+ * Locking: controlled by s_mutex.  Certain fields, however, require
+ *          the dev_lock spinlock; they are marked as such by comments.
+ *          dev_lock is also required for access to device registers.
+ */
+struct mcam_camera {
+	/*
+	 * These fields should be set by the platform code prior to
+	 * calling mcam_register().
+	 */
+	struct i2c_adapter *i2c_adapter;
+	unsigned char __iomem *regs;
+	spinlock_t dev_lock;
+	struct device *dev; /* For messages, dma alloc */
+	unsigned int chip_id;
+	short int clock_speed;	/* Sensor clock speed, default 30 */
+	short int use_smbus;	/* SMBUS or straight I2c? */
+	enum mcam_buffer_mode buffer_mode;
+	/*
+	 * Callbacks from the core to the platform code.
+	 */
+	void (*plat_power_up) (struct mcam_camera *cam);
+	void (*plat_power_down) (struct mcam_camera *cam);
+
+	/*
+	 * Everything below here is private to the mcam core and
+	 * should not be touched by the platform code.
+	 */
+	struct v4l2_device v4l2_dev;
+	enum mcam_state state;
+	unsigned long flags;		/* Buffer status, mainly (dev_lock) */
+	int users;			/* How many open FDs */
+	struct file *owner;		/* Who has data access (v4l2) */
+
+	/*
+	 * Subsystem structures.
+	 */
+	struct video_device vdev;
+	struct v4l2_subdev *sensor;
+	unsigned short sensor_addr;
+
+	/* Videobuf2 stuff */
+	struct vb2_queue vb_queue;
+	struct list_head buffers;	/* Available frames */
+
+	unsigned int nbufs;		/* How many are alloc'd */
+	int next_buf;			/* Next to consume (dev_lock) */
+
+	/* DMA buffers - vmalloc mode */
+#ifdef MCAM_MODE_VMALLOC
+	unsigned int dma_buf_size;	/* allocated size */
+	void *dma_bufs[MAX_DMA_BUFS];	/* Internal buffer addresses */
+	dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
+	struct tasklet_struct s_tasklet;
+#endif
+	unsigned int sequence;		/* Frame sequence number */
+	unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */
+
+	/* DMA buffers - DMA modes */
+	struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS];
+	struct vb2_alloc_ctx *vb_alloc_ctx;
+
+	/* Mode-specific ops, set at open time */
+	void (*dma_setup)(struct mcam_camera *cam);
+	void (*frame_complete)(struct mcam_camera *cam, int frame);
+
+	/* Current operating parameters */
+	u32 sensor_type;		/* Currently ov7670 only */
+	struct v4l2_pix_format pix_format;
+	enum v4l2_mbus_pixelcode mbus_code;
+
+	/* Locks */
+	struct mutex s_mutex; /* Access to this structure */
+};
+
+
+/*
+ * Register I/O functions.  These are here because the platform code
+ * may legitimately need to mess with the register space.
+ */
+/*
+ * Device register I/O
+ */
+static inline void mcam_reg_write(struct mcam_camera *cam, unsigned int reg,
+		unsigned int val)
+{
+	iowrite32(val, cam->regs + reg);
+}
+
+static inline unsigned int mcam_reg_read(struct mcam_camera *cam,
+		unsigned int reg)
+{
+	return ioread32(cam->regs + reg);
+}
+
+
+static inline void mcam_reg_write_mask(struct mcam_camera *cam, unsigned int reg,
+		unsigned int val, unsigned int mask)
+{
+	unsigned int v = mcam_reg_read(cam, reg);
+
+	v = (v & ~mask) | (val & mask);
+	mcam_reg_write(cam, reg, v);
+}
+
+static inline void mcam_reg_clear_bit(struct mcam_camera *cam,
+		unsigned int reg, unsigned int val)
+{
+	mcam_reg_write_mask(cam, reg, 0, val);
+}
+
+static inline void mcam_reg_set_bit(struct mcam_camera *cam,
+		unsigned int reg, unsigned int val)
+{
+	mcam_reg_write_mask(cam, reg, val, val);
+}
+
+/*
+ * Functions for use by platform code.
+ */
+int mccic_register(struct mcam_camera *cam);
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs);
+void mccic_shutdown(struct mcam_camera *cam);
+#ifdef CONFIG_PM
+void mccic_suspend(struct mcam_camera *cam);
+int mccic_resume(struct mcam_camera *cam);
+#endif
+
+/*
+ * Register definitions for the m88alp01 camera interface.  Offsets in bytes
+ * as given in the spec.
+ */
+#define REG_Y0BAR	0x00
+#define REG_Y1BAR	0x04
+#define REG_Y2BAR	0x08
+/* ... */
+
+#define REG_IMGPITCH	0x24	/* Image pitch register */
+#define   IMGP_YP_SHFT	  2		/* Y pitch params */
+#define   IMGP_YP_MASK	  0x00003ffc	/* Y pitch field */
+#define	  IMGP_UVP_SHFT	  18		/* UV pitch (planar) */
+#define   IMGP_UVP_MASK   0x3ffc0000
+#define REG_IRQSTATRAW	0x28	/* RAW IRQ Status */
+#define   IRQ_EOF0	  0x00000001	/* End of frame 0 */
+#define   IRQ_EOF1	  0x00000002	/* End of frame 1 */
+#define   IRQ_EOF2	  0x00000004	/* End of frame 2 */
+#define   IRQ_SOF0	  0x00000008	/* Start of frame 0 */
+#define   IRQ_SOF1	  0x00000010	/* Start of frame 1 */
+#define   IRQ_SOF2	  0x00000020	/* Start of frame 2 */
+#define   IRQ_OVERFLOW	  0x00000040	/* FIFO overflow */
+#define   IRQ_TWSIW	  0x00010000	/* TWSI (smbus) write */
+#define   IRQ_TWSIR	  0x00020000	/* TWSI read */
+#define   IRQ_TWSIE	  0x00040000	/* TWSI error */
+#define   TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
+#define   FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
+#define   ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
+#define REG_IRQMASK	0x2c	/* IRQ mask - same bits as IRQSTAT */
+#define REG_IRQSTAT	0x30	/* IRQ status / clear */
+
+#define REG_IMGSIZE	0x34	/* Image size */
+#define  IMGSZ_V_MASK	  0x1fff0000
+#define  IMGSZ_V_SHIFT	  16
+#define	 IMGSZ_H_MASK	  0x00003fff
+#define REG_IMGOFFSET	0x38	/* IMage offset */
+
+#define REG_CTRL0	0x3c	/* Control 0 */
+#define   C0_ENABLE	  0x00000001	/* Makes the whole thing go */
+
+/* Mask for all the format bits */
+#define   C0_DF_MASK	  0x00fffffc    /* Bits 2-23 */
+
+/* RGB ordering */
+#define	  C0_RGB4_RGBX	  0x00000000
+#define	  C0_RGB4_XRGB	  0x00000004
+#define	  C0_RGB4_BGRX	  0x00000008
+#define	  C0_RGB4_XBGR	  0x0000000c
+#define	  C0_RGB5_RGGB	  0x00000000
+#define	  C0_RGB5_GRBG	  0x00000004
+#define	  C0_RGB5_GBRG	  0x00000008
+#define	  C0_RGB5_BGGR	  0x0000000c
+
+/* Spec has two fields for DIN and DOUT, but they must match, so
+   combine them here. */
+#define	  C0_DF_YUV	  0x00000000	/* Data is YUV	    */
+#define	  C0_DF_RGB	  0x000000a0	/* ... RGB		    */
+#define	  C0_DF_BAYER	  0x00000140	/* ... Bayer		    */
+/* 8-8-8 must be missing from the below - ask */
+#define	  C0_RGBF_565	  0x00000000
+#define	  C0_RGBF_444	  0x00000800
+#define	  C0_RGB_BGR	  0x00001000	/* Blue comes first */
+#define	  C0_YUV_PLANAR	  0x00000000	/* YUV 422 planar format */
+#define	  C0_YUV_PACKED	  0x00008000	/* YUV 422 packed	*/
+#define	  C0_YUV_420PL	  0x0000a000	/* YUV 420 planar	*/
+/* Think that 420 packed must be 111 - ask */
+#define	  C0_YUVE_YUYV	  0x00000000	/* Y1CbY0Cr		*/
+#define	  C0_YUVE_YVYU	  0x00010000	/* Y1CrY0Cb		*/
+#define	  C0_YUVE_VYUY	  0x00020000	/* CrY1CbY0		*/
+#define	  C0_YUVE_UYVY	  0x00030000	/* CbY1CrY0		*/
+#define	  C0_YUVE_XYUV	  0x00000000	/* 420: .YUV		*/
+#define	  C0_YUVE_XYVU	  0x00010000	/* 420: .YVU		*/
+#define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY		*/
+#define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY		*/
+/* Bayer bits 18,19 if needed */
+#define	  C0_HPOL_LOW	  0x01000000	/* HSYNC polarity active low */
+#define	  C0_VPOL_LOW	  0x02000000	/* VSYNC polarity active low */
+#define	  C0_VCLK_LOW	  0x04000000	/* VCLK on falling edge */
+#define	  C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
+#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
+#define	  C0_SIF_HVSYNC	  0x00000000	/* Use H/VSYNC */
+#define	  CO_SOF_NOSYNC	  0x40000000	/* Use inband active signaling */
+
+/* Bits below C1_444ALPHA are not present in Cafe */
+#define REG_CTRL1	0x40	/* Control 1 */
+#define	  C1_CLKGATE	  0x00000001	/* Sensor clock gate */
+#define   C1_DESC_ENA	  0x00000100	/* DMA descriptor enable */
+#define   C1_DESC_3WORD   0x00000200	/* Three-word descriptors used */
+#define	  C1_444ALPHA	  0x00f00000	/* Alpha field in RGB444 */
+#define	  C1_ALPHA_SHFT	  20
+#define	  C1_DMAB32	  0x00000000	/* 32-byte DMA burst */
+#define	  C1_DMAB16	  0x02000000	/* 16-byte DMA burst */
+#define	  C1_DMAB64	  0x04000000	/* 64-byte DMA burst */
+#define	  C1_DMAB_MASK	  0x06000000
+#define	  C1_TWOBUFS	  0x08000000	/* Use only two DMA buffers */
+#define	  C1_PWRDWN	  0x10000000	/* Power down */
+
+#define REG_CLKCTRL	0x88	/* Clock control */
+#define	  CLK_DIV_MASK	  0x0000ffff	/* Upper bits RW "reserved" */
+
+/* This appears to be a Cafe-only register */
+#define REG_UBAR	0xc4	/* Upper base address register */
+
+/* Armada 610 DMA descriptor registers */
+#define	REG_DMA_DESC_Y	0x200
+#define	REG_DMA_DESC_U	0x204
+#define	REG_DMA_DESC_V	0x208
+#define REG_DESC_LEN_Y	0x20c	/* Lengths are in bytes */
+#define	REG_DESC_LEN_U	0x210
+#define REG_DESC_LEN_V	0x214
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
+
+#endif /* _MCAM_CORE_H */
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
new file mode 100644
index 0000000..d6b7645
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -0,0 +1,340 @@
+/*
+ * Support for the camera device found on Marvell MMP processors; known
+ * to work with the Armada 610 as used in the OLPC 1.75 system.
+ *
+ * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/mmp-camera.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+
+#include "mcam-core.h"
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_LICENSE("GPL");
+
+struct mmp_camera {
+	void *power_regs;
+	struct platform_device *pdev;
+	struct mcam_camera mcam;
+	struct list_head devlist;
+	int irq;
+};
+
+static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam)
+{
+	return container_of(mcam, struct mmp_camera, mcam);
+}
+
+/*
+ * A silly little infrastructure so we can keep track of our devices.
+ * Chances are that we will never have more than one of them, but
+ * the Armada 610 *does* have two controllers...
+ */
+
+static LIST_HEAD(mmpcam_devices);
+static struct mutex mmpcam_devices_lock;
+
+static void mmpcam_add_device(struct mmp_camera *cam)
+{
+	mutex_lock(&mmpcam_devices_lock);
+	list_add(&cam->devlist, &mmpcam_devices);
+	mutex_unlock(&mmpcam_devices_lock);
+}
+
+static void mmpcam_remove_device(struct mmp_camera *cam)
+{
+	mutex_lock(&mmpcam_devices_lock);
+	list_del(&cam->devlist);
+	mutex_unlock(&mmpcam_devices_lock);
+}
+
+/*
+ * Platform dev remove passes us a platform_device, and there's
+ * no handy unused drvdata to stash a backpointer in.  So just
+ * dig it out of our list.
+ */
+static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
+{
+	struct mmp_camera *cam;
+
+	mutex_lock(&mmpcam_devices_lock);
+	list_for_each_entry(cam, &mmpcam_devices, devlist) {
+		if (cam->pdev == pdev) {
+			mutex_unlock(&mmpcam_devices_lock);
+			return cam;
+		}
+	}
+	mutex_unlock(&mmpcam_devices_lock);
+	return NULL;
+}
+
+
+
+
+/*
+ * Power-related registers; this almost certainly belongs
+ * somewhere else.
+ *
+ * ARMADA 610 register manual, sec 7.2.1, p1842.
+ */
+#define CPU_SUBSYS_PMU_BASE	0xd4282800
+#define REG_CCIC_DCGCR		0x28	/* CCIC dyn clock gate ctrl reg */
+#define REG_CCIC_CRCR		0x50	/* CCIC clk reset ctrl reg	*/
+
+/*
+ * Power control.
+ */
+static void mmpcam_power_up(struct mcam_camera *mcam)
+{
+	struct mmp_camera *cam = mcam_to_cam(mcam);
+	struct mmp_camera_platform_data *pdata;
+/*
+ * Turn on power and clocks to the controller.
+ */
+	iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
+	iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
+	mdelay(1);
+/*
+ * Provide power to the sensor.
+ */
+	mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002);
+	pdata = cam->pdev->dev.platform_data;
+	gpio_set_value(pdata->sensor_power_gpio, 1);
+	mdelay(5);
+	mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000);
+	gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */
+	mdelay(5);
+	gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
+	mdelay(5);
+}
+
+static void mmpcam_power_down(struct mcam_camera *mcam)
+{
+	struct mmp_camera *cam = mcam_to_cam(mcam);
+	struct mmp_camera_platform_data *pdata;
+/*
+ * Turn off clocks and set reset lines
+ */
+	iowrite32(0, cam->power_regs + REG_CCIC_DCGCR);
+	iowrite32(0, cam->power_regs + REG_CCIC_CRCR);
+/*
+ * Shut down the sensor.
+ */
+	pdata = cam->pdev->dev.platform_data;
+	gpio_set_value(pdata->sensor_power_gpio, 0);
+	gpio_set_value(pdata->sensor_reset_gpio, 0);
+}
+
+
+static irqreturn_t mmpcam_irq(int irq, void *data)
+{
+	struct mcam_camera *mcam = data;
+	unsigned int irqs, handled;
+
+	spin_lock(&mcam->dev_lock);
+	irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+	handled = mccic_irq(mcam, irqs);
+	spin_unlock(&mcam->dev_lock);
+	return IRQ_RETVAL(handled);
+}
+
+
+static int mmpcam_probe(struct platform_device *pdev)
+{
+	struct mmp_camera *cam;
+	struct mcam_camera *mcam;
+	struct resource *res;
+	struct mmp_camera_platform_data *pdata;
+	int ret;
+
+	cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+	if (cam == NULL)
+		return -ENOMEM;
+	cam->pdev = pdev;
+	INIT_LIST_HEAD(&cam->devlist);
+
+	mcam = &cam->mcam;
+	mcam->platform = MHP_Armada610;
+	mcam->plat_power_up = mmpcam_power_up;
+	mcam->plat_power_down = mmpcam_power_down;
+	mcam->dev = &pdev->dev;
+	mcam->use_smbus = 0;
+	mcam->chip_id = V4L2_IDENT_ARMADA610;
+	mcam->buffer_mode = B_DMA_sg;
+	spin_lock_init(&mcam->dev_lock);
+	/*
+	 * Get our I/O memory.
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no iomem resource!\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+	mcam->regs = ioremap(res->start, resource_size(res));
+	if (mcam->regs == NULL) {
+		dev_err(&pdev->dev, "MMIO ioremap fail\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+	/*
+	 * Power/clock memory is elsewhere; get it too.  Perhaps this
+	 * should really be managed outside of this driver?
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no power resource!\n");
+		ret = -ENODEV;
+		goto out_unmap1;
+	}
+	cam->power_regs = ioremap(res->start, resource_size(res));
+	if (cam->power_regs == NULL) {
+		dev_err(&pdev->dev, "power MMIO ioremap fail\n");
+		ret = -ENODEV;
+		goto out_unmap1;
+	}
+	/*
+	 * Find the i2c adapter.  This assumes, of course, that the
+	 * i2c bus is already up and functioning.
+	 */
+	pdata = pdev->dev.platform_data;
+	mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
+	if (mcam->i2c_adapter == NULL) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "No i2c adapter\n");
+		goto out_unmap2;
+	}
+	/*
+	 * Sensor GPIO pins.
+	 */
+	ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get sensor power gpio %d",
+				pdata->sensor_power_gpio);
+		goto out_unmap2;
+	}
+	gpio_direction_output(pdata->sensor_power_gpio, 0);
+	ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
+				pdata->sensor_reset_gpio);
+		goto out_gpio;
+	}
+	gpio_direction_output(pdata->sensor_reset_gpio, 0);
+	/*
+	 * Power the device up and hand it off to the core.
+	 */
+	mmpcam_power_up(mcam);
+	ret = mccic_register(mcam);
+	if (ret)
+		goto out_gpio2;
+	/*
+	 * Finally, set up our IRQ now that the core is ready to
+	 * deal with it.
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		ret = -ENODEV;
+		goto out_unregister;
+	}
+	cam->irq = res->start;
+	ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
+			"mmp-camera", mcam);
+	if (ret == 0) {
+		mmpcam_add_device(cam);
+		return 0;
+	}
+
+out_unregister:
+	mccic_shutdown(mcam);
+out_gpio2:
+	mmpcam_power_down(mcam);
+	gpio_free(pdata->sensor_reset_gpio);
+out_gpio:
+	gpio_free(pdata->sensor_power_gpio);
+out_unmap2:
+	iounmap(cam->power_regs);
+out_unmap1:
+	iounmap(mcam->regs);
+out_free:
+	kfree(cam);
+	return ret;
+}
+
+
+static int mmpcam_remove(struct mmp_camera *cam)
+{
+	struct mcam_camera *mcam = &cam->mcam;
+	struct mmp_camera_platform_data *pdata;
+
+	mmpcam_remove_device(cam);
+	free_irq(cam->irq, mcam);
+	mccic_shutdown(mcam);
+	mmpcam_power_down(mcam);
+	pdata = cam->pdev->dev.platform_data;
+	gpio_free(pdata->sensor_reset_gpio);
+	gpio_free(pdata->sensor_power_gpio);
+	iounmap(cam->power_regs);
+	iounmap(mcam->regs);
+	kfree(cam);
+	return 0;
+}
+
+static int mmpcam_platform_remove(struct platform_device *pdev)
+{
+	struct mmp_camera *cam = mmpcam_find_device(pdev);
+
+	if (cam == NULL)
+		return -ENODEV;
+	return mmpcam_remove(cam);
+}
+
+
+static struct platform_driver mmpcam_driver = {
+	.probe		= mmpcam_probe,
+	.remove		= mmpcam_platform_remove,
+	.driver = {
+		.name	= "mmp-camera",
+		.owner	= THIS_MODULE
+	}
+};
+
+
+static int __init mmpcam_init_module(void)
+{
+	mutex_init(&mmpcam_devices_lock);
+	return platform_driver_register(&mmpcam_driver);
+}
+
+static void __exit mmpcam_exit_module(void)
+{
+	platform_driver_unregister(&mmpcam_driver);
+	/*
+	 * platform_driver_unregister() should have emptied the list
+	 */
+	if (!list_empty(&mmpcam_devices))
+		printk(KERN_ERR "mmp_camera leaving devices behind\n");
+}
+
+module_init(mmpcam_init_module);
+module_exit(mmpcam_exit_module);
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index b03d74e..166bf93 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
-#include <linux/version.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -35,7 +34,7 @@
 MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
 MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
 MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.1.1");
 
 #define MIN_W 32
 #define MIN_H 32
@@ -380,7 +379,6 @@
 	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
 	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
 	cap->bus_info[0] = 0;
-	cap->version = KERNEL_VERSION(0, 1, 0);
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
 			  | V4L2_CAP_STREAMING;
 
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index e2bbd8c..4da9cca 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -603,13 +603,9 @@
 	unsigned long flags;
 	int ret;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/* Enable the chip */
 	data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
@@ -675,8 +671,8 @@
 {
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 
-	dev_dbg(&icd->dev, "Video removed: %p, %p\n",
-		icd->dev.parent, icd->vdev);
+	dev_dbg(icd->pdev, "Video removed: %p, %p\n",
+		icd->parent, icd->vdev);
 	if (icl->free_bus)
 		icl->free_bus(icl);
 }
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index ebebed9..a357aa8 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -63,6 +63,12 @@
 #define MT9M111_RESET_RESTART_FRAME	(1 << 1)
 #define MT9M111_RESET_RESET_MODE	(1 << 0)
 
+#define MT9M111_RM_FULL_POWER_RD	(0 << 10)
+#define MT9M111_RM_LOW_POWER_RD		(1 << 10)
+#define MT9M111_RM_COL_SKIP_4X		(1 << 5)
+#define MT9M111_RM_ROW_SKIP_4X		(1 << 4)
+#define MT9M111_RM_COL_SKIP_2X		(1 << 3)
+#define MT9M111_RM_ROW_SKIP_2X		(1 << 2)
 #define MT9M111_RMB_MIRROR_COLS		(1 << 1)
 #define MT9M111_RMB_MIRROR_ROWS		(1 << 0)
 #define MT9M111_CTXT_CTRL_RESTART	(1 << 15)
@@ -95,7 +101,8 @@
 
 #define MT9M111_OPMODE_AUTOEXPO_EN	(1 << 14)
 #define MT9M111_OPMODE_AUTOWHITEBAL_EN	(1 << 1)
-
+#define MT9M111_OUTFMT_FLIP_BAYER_COL	(1 << 9)
+#define MT9M111_OUTFMT_FLIP_BAYER_ROW	(1 << 8)
 #define MT9M111_OUTFMT_PROCESSED_BAYER	(1 << 14)
 #define MT9M111_OUTFMT_BYPASS_IFP	(1 << 10)
 #define MT9M111_OUTFMT_INV_PIX_CLOCK	(1 << 9)
@@ -110,9 +117,8 @@
 #define MT9M111_OUTFMT_TST_RAMP_FRAME	(3 << 4)
 #define MT9M111_OUTFMT_SHIFT_3_UP	(1 << 3)
 #define MT9M111_OUTFMT_AVG_CHROMA	(1 << 2)
-#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y	(1 << 1)
-#define MT9M111_OUTFMT_SWAP_RGB_EVEN	(1 << 1)
-#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr	(1 << 0)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN	(1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B	(1 << 0)
 
 /*
  * Camera control register addresses (0x200..0x2ff not implemented)
@@ -122,6 +128,8 @@
 #define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
 #define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
 #define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
+#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
+		(val), (mask))
 
 #define MT9M111_MIN_DARK_ROWS	8
 #define MT9M111_MIN_DARK_COLS	26
@@ -153,7 +161,11 @@
 	{V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
 	{V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
 	{V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+	{V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
 	{V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+	{V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+	{V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+	{V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
 	{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
 	{V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
 };
@@ -169,6 +181,8 @@
 			 * from v4l2-chip-ident.h */
 	enum mt9m111_context context;
 	struct v4l2_rect rect;
+	struct mutex power_lock; /* lock to protect power_count */
+	int power_count;
 	const struct mt9m111_datafmt *fmt;
 	unsigned int gain;
 	unsigned char autoexposure;
@@ -176,10 +190,6 @@
 	unsigned int powered:1;
 	unsigned int hflip:1;
 	unsigned int vflip:1;
-	unsigned int swap_rgb_even_odd:1;
-	unsigned int swap_rgb_red_blue:1;
-	unsigned int swap_yuv_y_chromas:1;
-	unsigned int swap_yuv_cb_cr:1;
 	unsigned int autowhitebalance:1;
 };
 
@@ -248,12 +258,26 @@
 	int ret;
 
 	ret = mt9m111_reg_read(client, reg);
-	return mt9m111_reg_write(client, reg, ret & ~data);
+	if (ret >= 0)
+		ret = mt9m111_reg_write(client, reg, ret & ~data);
+	return ret;
 }
 
-static int mt9m111_set_context(struct i2c_client *client,
+static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
+			    const u16 data, const u16 mask)
+{
+	int ret;
+
+	ret = mt9m111_reg_read(client, reg);
+	if (ret >= 0)
+		ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
+	return ret;
+}
+
+static int mt9m111_set_context(struct mt9m111 *mt9m111,
 			       enum mt9m111_context ctxt)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B
 		| MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B
 		| MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B
@@ -267,10 +291,10 @@
 		return reg_write(CONTEXT_CONTROL, valA);
 }
 
-static int mt9m111_setup_rect(struct i2c_client *client,
+static int mt9m111_setup_rect(struct mt9m111 *mt9m111,
 			      struct v4l2_rect *rect)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret, is_raw_format;
 	int width = rect->width;
 	int height = rect->height;
@@ -312,81 +336,9 @@
 	return ret;
 }
 
-static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt)
+static int mt9m111_enable(struct mt9m111 *mt9m111)
 {
-	int ret;
-	u16 mask = MT9M111_OUTFMT_PROCESSED_BAYER | MT9M111_OUTFMT_RGB |
-		MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_SWAP_RGB_EVEN |
-		MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
-		MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr |
-		MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
-	ret = reg_read(OUTPUT_FORMAT_CTRL2_A);
-	if (ret >= 0)
-		ret = reg_write(OUTPUT_FORMAT_CTRL2_A, (ret & ~mask) | outfmt);
-	if (!ret)
-		ret = reg_read(OUTPUT_FORMAT_CTRL2_B);
-	if (ret >= 0)
-		ret = reg_write(OUTPUT_FORMAT_CTRL2_B, (ret & ~mask) | outfmt);
-
-	return ret;
-}
-
-static int mt9m111_setfmt_bayer8(struct i2c_client *client)
-{
-	return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER |
-				    MT9M111_OUTFMT_RGB);
-}
-
-static int mt9m111_setfmt_bayer10(struct i2c_client *client)
-{
-	return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP);
-}
-
-static int mt9m111_setfmt_rgb565(struct i2c_client *client)
-{
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-	int val = 0;
-
-	if (mt9m111->swap_rgb_red_blue)
-		val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-	if (mt9m111->swap_rgb_even_odd)
-		val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
-	val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
-
-	return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_rgb555(struct i2c_client *client)
-{
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-	int val = 0;
-
-	if (mt9m111->swap_rgb_red_blue)
-		val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-	if (mt9m111->swap_rgb_even_odd)
-		val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
-	val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
-
-	return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_setfmt_yuv(struct i2c_client *client)
-{
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-	int val = 0;
-
-	if (mt9m111->swap_yuv_cb_cr)
-		val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
-	if (mt9m111->swap_yuv_y_chromas)
-		val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
-
-	return mt9m111_setup_pixfmt(client, val);
-}
-
-static int mt9m111_enable(struct i2c_client *client)
-{
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE);
@@ -395,8 +347,9 @@
 	return ret;
 }
 
-static int mt9m111_reset(struct i2c_client *client)
+static int mt9m111_reset(struct mt9m111 *mt9m111)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
@@ -424,11 +377,9 @@
 	return 0;
 }
 
-static int mt9m111_make_rect(struct i2c_client *client,
+static int mt9m111_make_rect(struct mt9m111 *mt9m111,
 			     struct v4l2_rect *rect)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-
 	if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
 	    mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
 		/* Bayer format - even size lengths */
@@ -444,14 +395,14 @@
 	soc_camera_limit_side(&rect->top, &rect->height,
 		     MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
 
-	return mt9m111_setup_rect(client, rect);
+	return mt9m111_setup_rect(mt9m111, rect);
 }
 
 static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct v4l2_rect rect = a->c;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	int ret;
 
 	dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
@@ -460,7 +411,7 @@
 	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	ret = mt9m111_make_rect(client, &rect);
+	ret = mt9m111_make_rect(mt9m111, &rect);
 	if (!ret)
 		mt9m111->rect = rect;
 	return ret;
@@ -468,8 +419,7 @@
 
 static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
 	a->c	= mt9m111->rect;
 	a->type	= V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -496,8 +446,7 @@
 static int mt9m111_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
 	mf->width	= mt9m111->rect.width;
 	mf->height	= mt9m111->rect.height;
@@ -508,51 +457,73 @@
 	return 0;
 }
 
-static int mt9m111_set_pixfmt(struct i2c_client *client,
+static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
 			      enum v4l2_mbus_pixelcode code)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+	u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+		MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
+		MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
+		MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
+		MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+		MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
 	int ret;
 
 	switch (code) {
 	case V4L2_MBUS_FMT_SBGGR8_1X8:
-		ret = mt9m111_setfmt_bayer8(client);
+		data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+			MT9M111_OUTFMT_RGB;
 		break;
 	case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
-		ret = mt9m111_setfmt_bayer10(client);
+		data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
 		break;
 	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
-		ret = mt9m111_setfmt_rgb555(client);
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
+			MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+		break;
+	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
 		break;
 	case V4L2_MBUS_FMT_RGB565_2X8_LE:
-		ret = mt9m111_setfmt_rgb565(client);
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+			MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+		break;
+	case V4L2_MBUS_FMT_RGB565_2X8_BE:
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+		break;
+	case V4L2_MBUS_FMT_BGR565_2X8_BE:
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+			MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+		break;
+	case V4L2_MBUS_FMT_BGR565_2X8_LE:
+		data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+			MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+			MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
 		break;
 	case V4L2_MBUS_FMT_UYVY8_2X8:
-		mt9m111->swap_yuv_y_chromas = 0;
-		mt9m111->swap_yuv_cb_cr = 0;
-		ret = mt9m111_setfmt_yuv(client);
+		data_outfmt2 = 0;
 		break;
 	case V4L2_MBUS_FMT_VYUY8_2X8:
-		mt9m111->swap_yuv_y_chromas = 0;
-		mt9m111->swap_yuv_cb_cr = 1;
-		ret = mt9m111_setfmt_yuv(client);
+		data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
 		break;
 	case V4L2_MBUS_FMT_YUYV8_2X8:
-		mt9m111->swap_yuv_y_chromas = 1;
-		mt9m111->swap_yuv_cb_cr = 0;
-		ret = mt9m111_setfmt_yuv(client);
+		data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
 		break;
 	case V4L2_MBUS_FMT_YVYU8_2X8:
-		mt9m111->swap_yuv_y_chromas = 1;
-		mt9m111->swap_yuv_cb_cr = 1;
-		ret = mt9m111_setfmt_yuv(client);
+		data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+			MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
 		break;
 	default:
-		dev_err(&client->dev, "Pixel format not handled : %x\n",
-			code);
-		ret = -EINVAL;
+		dev_err(&client->dev, "Pixel format not handled: %x\n", code);
+		return -EINVAL;
 	}
 
+	ret = reg_mask(OUTPUT_FORMAT_CTRL2_A, data_outfmt2,
+		       mask_outfmt2);
+	if (!ret)
+		ret = reg_mask(OUTPUT_FORMAT_CTRL2_B, data_outfmt2,
+			       mask_outfmt2);
+
 	return ret;
 }
 
@@ -561,7 +532,7 @@
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const struct mt9m111_datafmt *fmt;
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	struct v4l2_rect rect = {
 		.left	= mt9m111->rect.left,
 		.top	= mt9m111->rect.top,
@@ -579,9 +550,9 @@
 		"%s code=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
 		mf->code, rect.left, rect.top, rect.width, rect.height);
 
-	ret = mt9m111_make_rect(client, &rect);
+	ret = mt9m111_make_rect(mt9m111, &rect);
 	if (!ret)
-		ret = mt9m111_set_pixfmt(client, mf->code);
+		ret = mt9m111_set_pixfmt(mt9m111, mf->code);
 	if (!ret) {
 		mt9m111->rect	= rect;
 		mt9m111->fmt	= fmt;
@@ -594,8 +565,7 @@
 static int mt9m111_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	const struct mt9m111_datafmt *fmt;
 	bool bayer = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
 		mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
@@ -635,7 +605,7 @@
 				struct v4l2_dbg_chip_ident *id)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
 		return -EINVAL;
@@ -726,21 +696,16 @@
 	}
 };
 
-static int mt9m111_resume(struct soc_camera_device *icd);
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state);
-
 static struct soc_camera_ops mt9m111_ops = {
-	.suspend		= mt9m111_suspend,
-	.resume			= mt9m111_resume,
 	.query_bus_param	= mt9m111_query_bus_param,
 	.set_bus_param		= mt9m111_set_bus_param,
 	.controls		= mt9m111_controls,
 	.num_controls		= ARRAY_SIZE(mt9m111_controls),
 };
 
-static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
+static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	if (mt9m111->context == HIGHPOWER) {
@@ -758,8 +723,9 @@
 	return ret;
 }
 
-static int mt9m111_get_global_gain(struct i2c_client *client)
+static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int data;
 
 	data = reg_read(GLOBAL_GAIN);
@@ -769,9 +735,9 @@
 	return data;
 }
 
-static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
+static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	u16 val;
 
 	if (gain > 63 * 2 * 2)
@@ -788,9 +754,9 @@
 	return reg_write(GLOBAL_GAIN, val);
 }
 
-static int mt9m111_set_autoexposure(struct i2c_client *client, int on)
+static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	if (on)
@@ -804,9 +770,9 @@
 	return ret;
 }
 
-static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on)
+static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	if (on)
@@ -823,7 +789,7 @@
 static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	int data;
 
 	switch (ctrl->id) {
@@ -848,7 +814,7 @@
 		ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
 		break;
 	case V4L2_CID_GAIN:
-		data = mt9m111_get_global_gain(client);
+		data = mt9m111_get_global_gain(mt9m111);
 		if (data < 0)
 			return data;
 		ctrl->value = data;
@@ -865,8 +831,7 @@
 
 static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
 	const struct v4l2_queryctrl *qctrl;
 	int ret;
 
@@ -877,22 +842,22 @@
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
 		mt9m111->vflip = ctrl->value;
-		ret = mt9m111_set_flip(client, ctrl->value,
+		ret = mt9m111_set_flip(mt9m111, ctrl->value,
 					MT9M111_RMB_MIRROR_ROWS);
 		break;
 	case V4L2_CID_HFLIP:
 		mt9m111->hflip = ctrl->value;
-		ret = mt9m111_set_flip(client, ctrl->value,
+		ret = mt9m111_set_flip(mt9m111, ctrl->value,
 					MT9M111_RMB_MIRROR_COLS);
 		break;
 	case V4L2_CID_GAIN:
-		ret = mt9m111_set_global_gain(client, ctrl->value);
+		ret = mt9m111_set_global_gain(mt9m111, ctrl->value);
 		break;
 	case V4L2_CID_EXPOSURE_AUTO:
-		ret =  mt9m111_set_autoexposure(client, ctrl->value);
+		ret =  mt9m111_set_autoexposure(mt9m111, ctrl->value);
 		break;
 	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ret =  mt9m111_set_autowhitebalance(client, ctrl->value);
+		ret =  mt9m111_set_autowhitebalance(mt9m111, ctrl->value);
 		break;
 	default:
 		ret = -EINVAL;
@@ -901,60 +866,52 @@
 	return ret;
 }
 
-static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state)
+static int mt9m111_suspend(struct mt9m111 *mt9m111)
 {
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-
-	mt9m111->gain = mt9m111_get_global_gain(client);
+	mt9m111->gain = mt9m111_get_global_gain(mt9m111);
 
 	return 0;
 }
 
-static int mt9m111_restore_state(struct i2c_client *client)
+static void mt9m111_restore_state(struct mt9m111 *mt9m111)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
-
-	mt9m111_set_context(client, mt9m111->context);
-	mt9m111_set_pixfmt(client, mt9m111->fmt->code);
-	mt9m111_setup_rect(client, &mt9m111->rect);
-	mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
-	mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
-	mt9m111_set_global_gain(client, mt9m111->gain);
-	mt9m111_set_autoexposure(client, mt9m111->autoexposure);
-	mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance);
-	return 0;
+	mt9m111_set_context(mt9m111, mt9m111->context);
+	mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
+	mt9m111_setup_rect(mt9m111, &mt9m111->rect);
+	mt9m111_set_flip(mt9m111, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
+	mt9m111_set_flip(mt9m111, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
+	mt9m111_set_global_gain(mt9m111, mt9m111->gain);
+	mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
+	mt9m111_set_autowhitebalance(mt9m111, mt9m111->autowhitebalance);
 }
 
-static int mt9m111_resume(struct soc_camera_device *icd)
+static int mt9m111_resume(struct mt9m111 *mt9m111)
 {
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	int ret = 0;
 
 	if (mt9m111->powered) {
-		ret = mt9m111_enable(client);
+		ret = mt9m111_enable(mt9m111);
 		if (!ret)
-			ret = mt9m111_reset(client);
+			ret = mt9m111_reset(mt9m111);
 		if (!ret)
-			ret = mt9m111_restore_state(client);
+			mt9m111_restore_state(mt9m111);
 	}
 	return ret;
 }
 
-static int mt9m111_init(struct i2c_client *client)
+static int mt9m111_init(struct mt9m111 *mt9m111)
 {
-	struct mt9m111 *mt9m111 = to_mt9m111(client);
+	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
 	int ret;
 
 	mt9m111->context = HIGHPOWER;
-	ret = mt9m111_enable(client);
+	ret = mt9m111_enable(mt9m111);
 	if (!ret)
-		ret = mt9m111_reset(client);
+		ret = mt9m111_reset(mt9m111);
 	if (!ret)
-		ret = mt9m111_set_context(client, mt9m111->context);
+		ret = mt9m111_set_context(mt9m111, mt9m111->context);
 	if (!ret)
-		ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
+		ret = mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
 	if (ret)
 		dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
 	return ret;
@@ -971,20 +928,13 @@
 	s32 data;
 	int ret;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	mt9m111->autoexposure = 1;
 	mt9m111->autowhitebalance = 1;
 
-	mt9m111->swap_rgb_even_odd = 1;
-	mt9m111->swap_rgb_red_blue = 1;
-
 	data = reg_read(CHIP_VERSION);
 
 	switch (data) {
@@ -1005,16 +955,51 @@
 		goto ei2c;
 	}
 
-	ret = mt9m111_init(client);
+	ret = mt9m111_init(mt9m111);
 
 ei2c:
 	return ret;
 }
 
+static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	mutex_lock(&mt9m111->power_lock);
+
+	/*
+	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
+	 * update the power state.
+	 */
+	if (mt9m111->power_count == !on) {
+		if (on) {
+			ret = mt9m111_resume(mt9m111);
+			if (ret) {
+				dev_err(&client->dev,
+					"Failed to resume the sensor: %d\n", ret);
+				goto out;
+			}
+		} else {
+			mt9m111_suspend(mt9m111);
+		}
+	}
+
+	/* Update the power count. */
+	mt9m111->power_count += on ? 1 : -1;
+	WARN_ON(mt9m111->power_count < 0);
+
+out:
+	mutex_unlock(&mt9m111->power_lock);
+	return ret;
+}
+
 static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
 	.g_ctrl		= mt9m111_g_ctrl,
 	.s_ctrl		= mt9m111_s_ctrl,
 	.g_chip_ident	= mt9m111_g_chip_ident,
+	.s_power	= mt9m111_s_power,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= mt9m111_g_register,
 	.s_register	= mt9m111_s_register,
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 7ce279c..30547cc 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -700,8 +700,7 @@
 static int mt9t031_runtime_resume(struct device *dev)
 {
 	struct video_device *vdev = to_video_device(dev);
-	struct soc_camera_device *icd = container_of(vdev->parent,
-		struct soc_camera_device, dev);
+	struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index bffa9ee..d2e0a50 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -1057,13 +1057,9 @@
 	const char          *devname;
 	int                  chipid;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * check and show chip ID
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
index 4904d25..893a8b8 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/video/mt9v011.c
@@ -54,11 +54,20 @@
 		.type = V4L2_CTRL_TYPE_INTEGER,
 		.name = "Gain",
 		.minimum = 0,
-		.maximum = (1 << 10) - 1,
+		.maximum = (1 << 12) - 1 - 0x0020,
 		.step = 1,
 		.default_value = 0x0020,
 		.flags = 0,
 	}, {
+		.id = V4L2_CID_EXPOSURE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Exposure",
+		.minimum = 0,
+		.maximum = 2047,
+		.step = 1,
+		.default_value = 0x01fc,
+		.flags = 0,
+	}, {
 		.id = V4L2_CID_RED_BALANCE,
 		.type = V4L2_CTRL_TYPE_INTEGER,
 		.name = "Red Balance",
@@ -105,7 +114,8 @@
 	unsigned hflip:1;
 	unsigned vflip:1;
 
-	u16 global_gain, red_bal, blue_bal;
+	u16 global_gain, exposure;
+	s16 red_bal, blue_bal;
 };
 
 static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd)
@@ -180,24 +190,68 @@
 		{ R07_MT9V011_OUT_CTRL, 0x0002 },	/* chip enable */
 };
 
+
+static u16 calc_mt9v011_gain(s16 lineargain)
+{
+
+	u16 digitalgain = 0;
+	u16 analogmult = 0;
+	u16 analoginit = 0;
+
+	if (lineargain < 0)
+		lineargain = 0;
+
+	/* recommended minimum */
+	lineargain += 0x0020;
+
+	if (lineargain > 2047)
+		lineargain = 2047;
+
+	if (lineargain > 1023) {
+		digitalgain = 3;
+		analogmult = 3;
+		analoginit = lineargain / 16;
+	} else if (lineargain > 511) {
+		digitalgain = 1;
+		analogmult = 3;
+		analoginit = lineargain / 8;
+	} else if (lineargain > 255) {
+		analogmult = 3;
+		analoginit = lineargain / 4;
+	} else if (lineargain > 127) {
+		analogmult = 1;
+		analoginit = lineargain / 2;
+	} else
+		analoginit = lineargain;
+
+	return analoginit + (analogmult << 7) + (digitalgain << 9);
+
+}
+
 static void set_balance(struct v4l2_subdev *sd)
 {
 	struct mt9v011 *core = to_mt9v011(sd);
-	u16 green1_gain, green2_gain, blue_gain, red_gain;
+	u16 green_gain, blue_gain, red_gain;
+	u16 exposure;
+	s16 bal;
 
-	green1_gain = core->global_gain;
-	green2_gain = core->global_gain;
+	exposure = core->exposure;
 
-	blue_gain = core->global_gain +
-		    core->global_gain * core->blue_bal / (1 << 9);
+	green_gain = calc_mt9v011_gain(core->global_gain);
 
-	red_gain = core->global_gain +
-		   core->global_gain * core->blue_bal / (1 << 9);
+	bal = core->global_gain;
+	bal += (core->blue_bal * core->global_gain / (1 << 7));
+	blue_gain = calc_mt9v011_gain(bal);
 
-	mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green1_gain);
-	mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN,  green1_gain);
+	bal = core->global_gain;
+	bal += (core->red_bal * core->global_gain / (1 << 7));
+	red_gain = calc_mt9v011_gain(bal);
+
+	mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain);
+	mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain);
 	mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);
 	mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
+	mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure);
 }
 
 static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)
@@ -286,7 +340,7 @@
 	 * be missing.
 	 */
 
-	hstart = 14 + (640 - core->width) / 2;
+	hstart = 20 + (640 - core->width) / 2;
 	mt9v011_write(sd, R02_MT9V011_COLSTART, hstart);
 	mt9v011_write(sd, R04_MT9V011_WIDTH, core->width);
 	mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width);
@@ -338,6 +392,9 @@
 	case V4L2_CID_GAIN:
 		ctrl->value = core->global_gain;
 		return 0;
+	case V4L2_CID_EXPOSURE:
+		ctrl->value = core->exposure;
+		return 0;
 	case V4L2_CID_RED_BALANCE:
 		ctrl->value = core->red_bal;
 		return 0;
@@ -392,6 +449,9 @@
 	case V4L2_CID_GAIN:
 		core->global_gain = ctrl->value;
 		break;
+	case V4L2_CID_EXPOSURE:
+		core->exposure = ctrl->value;
+		break;
 	case V4L2_CID_RED_BALANCE:
 		core->red_bal = ctrl->value;
 		break;
@@ -598,6 +658,7 @@
 	}
 
 	core->global_gain = 0x0024;
+	core->exposure = 0x01fc;
 	core->width  = 640;
 	core->height = 480;
 	core->xtal = 27000000;	/* Hz */
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index fc76ed1..51b0fcc 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -728,9 +728,9 @@
 	int ret;
 	unsigned long flags;
 
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/* Read out the chip version register */
 	data = reg_read(client, MT9V022_CHIP_VERSION);
@@ -809,8 +809,8 @@
 {
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 
-	dev_dbg(&icd->dev, "Video removed: %p, %p\n",
-		icd->dev.parent, icd->vdev);
+	dev_dbg(icd->pdev, "Video removed: %p, %p\n",
+		icd->parent, icd->vdev);
 	if (icl->free_bus)
 		icl->free_bus(icl);
 }
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
index 1319c2c..c64e1dc 100644
--- a/drivers/media/video/mt9v032.c
+++ b/drivers/media/video/mt9v032.c
@@ -31,14 +31,14 @@
 #define MT9V032_CHIP_VERSION				0x00
 #define		MT9V032_CHIP_ID_REV1			0x1311
 #define		MT9V032_CHIP_ID_REV3			0x1313
-#define MT9V032_ROW_START				0x01
-#define		MT9V032_ROW_START_MIN			4
-#define		MT9V032_ROW_START_DEF			10
-#define		MT9V032_ROW_START_MAX			482
-#define MT9V032_COLUMN_START				0x02
+#define MT9V032_COLUMN_START				0x01
 #define		MT9V032_COLUMN_START_MIN		1
-#define		MT9V032_COLUMN_START_DEF		2
+#define		MT9V032_COLUMN_START_DEF		1
 #define		MT9V032_COLUMN_START_MAX		752
+#define MT9V032_ROW_START				0x02
+#define		MT9V032_ROW_START_MIN			4
+#define		MT9V032_ROW_START_DEF			5
+#define		MT9V032_ROW_START_MAX			482
 #define MT9V032_WINDOW_HEIGHT				0x03
 #define		MT9V032_WINDOW_HEIGHT_MIN		1
 #define		MT9V032_WINDOW_HEIGHT_DEF		480
@@ -420,13 +420,13 @@
 	struct v4l2_rect *__crop;
 	struct v4l2_rect rect;
 
-	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
-	 * pixels.
+	/* Clamp the crop rectangle boundaries and align them to a non multiple
+	 * of 2 pixels to ensure a GRBG Bayer pattern.
 	 */
-	rect.left = clamp(ALIGN(crop->rect.left, 2),
+	rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
 			  MT9V032_COLUMN_START_MIN,
 			  MT9V032_COLUMN_START_MAX);
-	rect.top = clamp(ALIGN(crop->rect.top, 2),
+	rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
 			 MT9V032_ROW_START_MIN,
 			 MT9V032_ROW_START_MAX);
 	rect.width = clamp(ALIGN(crop->rect.width, 2),
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 63f8a0c..087db12 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -31,7 +31,6 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/time.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 
 #include <media/soc_camera.h>
@@ -73,7 +72,7 @@
 #define CSISR_SOF_INT		(1 << 16)
 #define CSISR_DRDY		(1 << 0)
 
-#define VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION "0.0.2"
 #define DRIVER_NAME "mx1-camera"
 
 #define CSI_IRQ_MASK	(CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
@@ -142,7 +141,7 @@
 	if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
 		*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
 
-	dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
+	dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
 
 	return 0;
 }
@@ -154,7 +153,7 @@
 
 	BUG_ON(in_interrupt());
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	/*
@@ -179,7 +178,7 @@
 	if (bytes_per_line < 0)
 		return bytes_per_line;
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	/* Added list head initialization on alloc */
@@ -232,7 +231,7 @@
 static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
 {
 	struct videobuf_buffer *vbuf = &pcdev->active->vb;
-	struct device *dev = pcdev->icd->dev.parent;
+	struct device *dev = pcdev->icd->parent;
 	int ret;
 
 	if (unlikely(!pcdev->active)) {
@@ -256,11 +255,11 @@
 						struct videobuf_buffer *vb)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 	struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	list_add_tail(&vb->queue, &pcdev->capture);
@@ -287,7 +286,7 @@
 	struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
 #ifdef DEBUG
 	struct soc_camera_device *icd = vq->priv_data;
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 
 	dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
@@ -343,7 +342,7 @@
 static void mx1_camera_dma_irq(int channel, void *data)
 {
 	struct mx1_camera_dev *pcdev = data;
-	struct device *dev = pcdev->icd->dev.parent;
+	struct device *dev = pcdev->icd->parent;
 	struct mx1_buffer *buf;
 	struct videobuf_buffer *vb;
 	unsigned long flags;
@@ -378,10 +377,10 @@
 static void mx1_camera_init_videobuf(struct videobuf_queue *q,
 				     struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
-	videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent,
+	videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
 				&pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				V4L2_FIELD_NONE,
 				sizeof(struct mx1_buffer), icd, &icd->video_lock);
@@ -401,7 +400,7 @@
 	 */
 	div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
 
-	dev_dbg(pcdev->icd->dev.parent,
+	dev_dbg(pcdev->icd->parent,
 		"System clock %lukHz, target freq %dkHz, divisor %lu\n",
 		lcdclk / 1000, mclk / 1000, div);
 
@@ -412,7 +411,7 @@
 {
 	unsigned int csicr1 = CSICR1_EN;
 
-	dev_dbg(pcdev->icd->dev.parent, "Activate device\n");
+	dev_dbg(pcdev->icd->parent, "Activate device\n");
 
 	clk_enable(pcdev->clk);
 
@@ -428,7 +427,7 @@
 
 static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
 {
-	dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n");
+	dev_dbg(pcdev->icd->parent, "Deactivate device\n");
 
 	/* Disable all CSI interface */
 	__raw_writel(0x00, pcdev->base + CSICR1);
@@ -442,13 +441,13 @@
  */
 static int mx1_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 
 	if (pcdev->icd)
 		return -EBUSY;
 
-	dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n",
+	dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	mx1_camera_activate(pcdev);
@@ -460,7 +459,7 @@
 
 static void mx1_camera_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned int csicr1;
 
@@ -473,7 +472,7 @@
 	/* Stop DMA engine */
 	imx_dma_disable(pcdev->dma_chan);
 
-	dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n",
+	dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
 		 icd->devnum);
 
 	mx1_camera_deactivate(pcdev);
@@ -491,7 +490,7 @@
 
 static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
 	unsigned long camera_flags, common_flags;
 	unsigned int csicr1;
@@ -562,14 +561,14 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n",
+		dev_warn(icd->parent, "Format %x not found\n",
 			 pix->pixelformat);
 		return -EINVAL;
 	}
 
 	buswidth = xlate->host_fmt->bits_per_sample;
 	if (buswidth > 8) {
-		dev_warn(icd->dev.parent,
+		dev_warn(icd->parent,
 			 "bits-per-sample %d for format %x unsupported\n",
 			 buswidth, pix->pixelformat);
 		return -EINVAL;
@@ -609,7 +608,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n",
+		dev_warn(icd->parent, "Format %x not found\n",
 			 pix->pixelformat);
 		return -EINVAL;
 	}
@@ -676,7 +675,6 @@
 {
 	/* cap->name is set by the friendly caller:-> */
 	strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card));
-	cap->version = VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
@@ -883,4 +881,5 @@
 MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
 MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
 MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
 MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 4eab1c6..ec2410c 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -23,7 +23,6 @@
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
 #include <linux/time.h>
-#include <linux/version.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
@@ -47,7 +46,7 @@
 #include <asm/dma.h>
 
 #define MX2_CAM_DRV_NAME "mx2-camera"
-#define MX2_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
+#define MX2_CAM_VERSION "0.0.6"
 #define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
 
 /* reset values */
@@ -278,7 +277,7 @@
  */
 static int mx2_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	int ret;
 	u32 csicr1;
@@ -303,7 +302,7 @@
 
 	pcdev->icd = icd;
 
-	dev_info(icd->dev.parent, "Camera driver attached to camera %d\n",
+	dev_info(icd->parent, "Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	return 0;
@@ -311,12 +310,12 @@
 
 static void mx2_camera_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
 	BUG_ON(icd != pcdev->icd);
 
-	dev_info(icd->dev.parent, "Camera driver detached from camera %d\n",
+	dev_info(icd->parent, "Camera driver detached from camera %d\n",
 		 icd->devnum);
 
 	mx2_camera_deactivate(pcdev);
@@ -437,7 +436,7 @@
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 			icd->current_fmt->host_fmt);
 
-	dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+	dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
 
 	if (bytes_per_line < 0)
 		return bytes_per_line;
@@ -457,7 +456,7 @@
 	struct soc_camera_device *icd = vq->priv_data;
 	struct videobuf_buffer *vb = &buf->vb;
 
-	dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	/*
@@ -467,7 +466,7 @@
 	videobuf_waiton(vq, vb, 0, 0);
 
 	videobuf_dma_contig_free(vq, vb);
-	dev_dbg(&icd->dev, "%s freed\n", __func__);
+	dev_dbg(icd->parent, "%s freed\n", __func__);
 
 	vb->state = VIDEOBUF_NEEDS_INIT;
 }
@@ -481,7 +480,7 @@
 			icd->current_fmt->host_fmt);
 	int ret = 0;
 
-	dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	if (bytes_per_line < 0)
@@ -533,12 +532,12 @@
 {
 	struct soc_camera_device *icd = vq->priv_data;
 	struct soc_camera_host *ici =
-		to_soc_camera_host(icd->dev.parent);
+		to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
 	unsigned long flags;
 
-	dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	spin_lock_irqsave(&pcdev->lock, flags);
@@ -611,27 +610,27 @@
 				 struct videobuf_buffer *vb)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
 	unsigned long flags;
 
 #ifdef DEBUG
-	dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
 
 	switch (vb->state) {
 	case VIDEOBUF_ACTIVE:
-		dev_info(&icd->dev, "%s (active)\n", __func__);
+		dev_info(icd->parent, "%s (active)\n", __func__);
 		break;
 	case VIDEOBUF_QUEUED:
-		dev_info(&icd->dev, "%s (queued)\n", __func__);
+		dev_info(icd->parent, "%s (queued)\n", __func__);
 		break;
 	case VIDEOBUF_PREPARED:
-		dev_info(&icd->dev, "%s (prepared)\n", __func__);
+		dev_info(icd->parent, "%s (prepared)\n", __func__);
 		break;
 	default:
-		dev_info(&icd->dev, "%s (unknown) %d\n", __func__,
+		dev_info(icd->parent, "%s (unknown) %d\n", __func__,
 				vb->state);
 		break;
 	}
@@ -678,7 +677,7 @@
 static void mx2_camera_init_videobuf(struct videobuf_queue *q,
 			      struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
 	videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev,
@@ -719,7 +718,7 @@
 		int bytesperline)
 {
 	struct soc_camera_host *ici =
-		to_soc_camera_host(icd->dev.parent);
+		to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 
 	writel(pcdev->discard_buffer_dma,
@@ -772,7 +771,7 @@
 		__u32 pixfmt)
 {
 	struct soc_camera_host *ici =
-		to_soc_camera_host(icd->dev.parent);
+		to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
 	unsigned long camera_flags, common_flags;
 	int ret = 0;
@@ -891,7 +890,7 @@
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
+	dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
 		mf.width, mf.height);
 
 	icd->user_width		= mf.width;
@@ -911,7 +910,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n",
+		dev_warn(icd->parent, "Format %x not found\n",
 				pix->pixelformat);
 		return -EINVAL;
 	}
@@ -951,7 +950,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
 	if (pixfmt && !xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+		dev_warn(icd->parent, "Format %x not found\n", pixfmt);
 		return -EINVAL;
 	}
 
@@ -974,11 +973,16 @@
 		if (pix->bytesperline < 0)
 			return pix->bytesperline;
 		pix->sizeimage = pix->height * pix->bytesperline;
-		if (pix->sizeimage > (4 * 0x3ffff)) { /* CSIRXCNT limit */
-			dev_warn(icd->dev.parent,
-					"Image size (%u) above limit\n",
-					pix->sizeimage);
-			return -EINVAL;
+		/* Check against the CSIRXCNT limit */
+		if (pix->sizeimage > 4 * 0x3ffff) {
+			/* Adjust geometry, preserve aspect ratio */
+			unsigned int new_height = int_sqrt(4 * 0x3ffff *
+					pix->height / pix->bytesperline);
+			pix->width = new_height * pix->width / pix->height;
+			pix->height = new_height;
+			pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+							xlate->host_fmt);
+			BUG_ON(pix->bytesperline < 0);
 		}
 	}
 
@@ -996,7 +1000,7 @@
 	if (mf.field == V4L2_FIELD_ANY)
 		mf.field = V4L2_FIELD_NONE;
 	if (mf.field != V4L2_FIELD_NONE) {
-		dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+		dev_err(icd->parent, "Field type %d unsupported.\n",
 				mf.field);
 		return -EINVAL;
 	}
@@ -1014,7 +1018,6 @@
 {
 	/* cap->name is set by the friendly caller:-> */
 	strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
-	cap->version = MX2_CAM_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
@@ -1523,3 +1526,4 @@
 MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
 MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c7680eb..c045b47 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -11,7 +11,6 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
@@ -195,7 +194,7 @@
 			unsigned long sizes[], void *alloc_ctxs[])
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 						icd->current_fmt->host_fmt);
@@ -224,7 +223,7 @@
 static int mx3_videobuf_prepare(struct vb2_buffer *vb)
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
 	struct scatterlist *sg;
@@ -242,7 +241,7 @@
 	new_size = bytes_per_line * icd->user_height;
 
 	if (vb2_plane_size(vb, 0) < new_size) {
-		dev_err(icd->dev.parent, "Buffer too small (%lu < %zu)\n",
+		dev_err(icd->parent, "Buffer too small (%lu < %zu)\n",
 			vb2_plane_size(vb, 0), new_size);
 		return -ENOBUFS;
 	}
@@ -284,7 +283,7 @@
 static void mx3_videobuf_queue(struct vb2_buffer *vb)
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct mx3_camera_buffer *buf = to_mx3_vb(vb);
 	struct dma_async_tx_descriptor *txd = buf->txd;
@@ -337,7 +336,7 @@
 	spin_unlock_irq(&mx3_cam->lock);
 
 	cookie = txd->tx_submit(txd);
-	dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n",
+	dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
 		cookie, sg_dma_address(&buf->sg));
 
 	if (cookie >= 0)
@@ -358,13 +357,13 @@
 static void mx3_videobuf_release(struct vb2_buffer *vb)
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct mx3_camera_buffer *buf = to_mx3_vb(vb);
 	struct dma_async_tx_descriptor *txd = buf->txd;
 	unsigned long flags;
 
-	dev_dbg(icd->dev.parent,
+	dev_dbg(icd->parent,
 		"Release%s DMA 0x%08x, queue %sempty\n",
 		mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
 		list_empty(&buf->queue) ? "" : "not ");
@@ -403,7 +402,7 @@
 static int mx3_stop_streaming(struct vb2_queue *q)
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(q);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
 	struct dma_chan *chan;
@@ -499,7 +498,7 @@
 
 	clk_enable(mx3_cam->clk);
 	rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
-	dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+	dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
 	if (rate)
 		clk_set_rate(mx3_cam->clk, rate);
 }
@@ -507,7 +506,7 @@
 /* Called with .video_lock held */
 static int mx3_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 
 	if (mx3_cam->icd)
@@ -517,7 +516,7 @@
 
 	mx3_cam->icd = icd;
 
-	dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n",
+	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	return 0;
@@ -526,7 +525,7 @@
 /* Called with .video_lock held */
 static void mx3_camera_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
 
@@ -541,7 +540,7 @@
 
 	mx3_cam->icd = NULL;
 
-	dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n",
+	dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
 		 icd->devnum);
 }
 
@@ -608,12 +607,12 @@
 static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
 				    const unsigned int depth)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	unsigned long bus_flags, camera_flags;
 	int ret = test_platform_param(mx3_cam, depth, &bus_flags);
 
-	dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret);
+	dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
 
 	if (ret < 0)
 		return ret;
@@ -622,7 +621,7 @@
 
 	ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
 	if (ret < 0)
-		dev_warn(icd->dev.parent,
+		dev_warn(icd->parent,
 			 "Flags incompatible: camera %lx, host %lx\n",
 			 camera_flags, bus_flags);
 
@@ -676,7 +675,7 @@
 				  struct soc_camera_format_xlate *xlate)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	int formats = 0, ret;
 	enum v4l2_mbus_pixelcode code;
 	const struct soc_mbus_pixelfmt *fmt;
@@ -688,7 +687,7 @@
 
 	fmt = soc_mbus_get_fmtdesc(code);
 	if (!fmt) {
-		dev_warn(icd->dev.parent,
+		dev_warn(icd->parent,
 			 "Unsupported format code #%u: %d\n", idx, code);
 		return 0;
 	}
@@ -816,7 +815,7 @@
 			       struct v4l2_crop *a)
 {
 	struct v4l2_rect *rect = &a->c;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct v4l2_mbus_framefmt mf;
@@ -849,7 +848,7 @@
 		configure_geometry(mx3_cam, mf.width, mf.height,
 				   icd->current_fmt->host_fmt);
 
-	dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
+	dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
 		mf.width, mf.height);
 
 	icd->user_width		= mf.width;
@@ -861,7 +860,7 @@
 static int mx3_camera_set_fmt(struct soc_camera_device *icd,
 			      struct v4l2_format *f)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	const struct soc_camera_format_xlate *xlate;
@@ -871,13 +870,13 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n",
+		dev_warn(icd->parent, "Format %x not found\n",
 			 pix->pixelformat);
 		return -EINVAL;
 	}
 
 	stride_align(&pix->width);
-	dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height);
+	dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
 
 	/*
 	 * Might have to perform a complete interface initialisation like in
@@ -913,13 +912,7 @@
 	pix->colorspace		= mf.colorspace;
 	icd->current_fmt	= xlate;
 
-	pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
-						    xlate->host_fmt);
-	if (pix->bytesperline < 0)
-		return pix->bytesperline;
-	pix->sizeimage = pix->height * pix->bytesperline;
-
-	dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
+	dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
 
 	return ret;
 }
@@ -936,7 +929,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
 	if (pixfmt && !xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+		dev_warn(icd->parent, "Format %x not found\n", pixfmt);
 		return -EINVAL;
 	}
 
@@ -946,12 +939,6 @@
 	if (pix->width > 4096)
 		pix->width = 4096;
 
-	pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
-						    xlate->host_fmt);
-	if (pix->bytesperline < 0)
-		return pix->bytesperline;
-	pix->sizeimage = pix->height * pix->bytesperline;
-
 	/* limit to sensor capabilities */
 	mf.width	= pix->width;
 	mf.height	= pix->height;
@@ -974,7 +961,7 @@
 	case V4L2_FIELD_NONE:
 		break;
 	default:
-		dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+		dev_err(icd->parent, "Field type %d unsupported.\n",
 			mf.field);
 		ret = -EINVAL;
 	}
@@ -1000,7 +987,6 @@
 {
 	/* cap->name is set by the firendly caller:-> */
 	strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
-	cap->version = KERNEL_VERSION(0, 2, 2);
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
@@ -1008,7 +994,7 @@
 
 static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	unsigned long bus_flags, camera_flags, common_flags;
 	u32 dw, sens_conf;
@@ -1016,7 +1002,7 @@
 	int buswidth;
 	int ret;
 	const struct soc_camera_format_xlate *xlate;
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 
 	fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
 	if (!fmt)
@@ -1325,4 +1311,5 @@
 MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
 MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
 MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
 MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig
index e63233fd..390ab09 100644
--- a/drivers/media/video/omap/Kconfig
+++ b/drivers/media/video/omap/Kconfig
@@ -1,11 +1,14 @@
+config VIDEO_OMAP2_VOUT_VRFB
+	bool
+
 config VIDEO_OMAP2_VOUT
 	tristate "OMAP2/OMAP3 V4L2-Display driver"
 	depends on ARCH_OMAP2 || ARCH_OMAP3
 	select VIDEOBUF_GEN
 	select VIDEOBUF_DMA_CONTIG
 	select OMAP2_DSS
-	select OMAP2_VRAM
-	select OMAP2_VRFB
+	select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
+	select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
 	default n
 	---help---
 	  V4L2 Display driver support for OMAP2/3 based boards.
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
index b287880..fc410b4 100644
--- a/drivers/media/video/omap/Makefile
+++ b/drivers/media/video/omap/Makefile
@@ -4,4 +4,5 @@
 
 # OMAP2/3 Display driver
 omap-vout-y := omap_vout.o omap_voutlib.o
+omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o
 obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index a647894..b5ef362 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -35,28 +35,26 @@
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
 #include <linux/irq.h>
 #include <linux/videodev2.h>
-#include <linux/slab.h>
+#include <linux/dma-mapping.h>
 
 #include <media/videobuf-dma-contig.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 
 #include <plat/dma.h>
-#include <plat/vram.h>
 #include <plat/vrfb.h>
 #include <video/omapdss.h>
 
 #include "omap_voutlib.h"
 #include "omap_voutdef.h"
+#include "omap_vout_vrfb.h"
 
 MODULE_AUTHOR("Texas Instruments");
 MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
 MODULE_LICENSE("GPL");
 
-
 /* Driver Configuration macros */
 #define VOUT_NAME		"omap_vout"
 
@@ -65,31 +63,6 @@
 	OMAP_VIDEO2,
 };
 
-enum dma_channel_state {
-	DMA_CHAN_NOT_ALLOTED,
-	DMA_CHAN_ALLOTED,
-};
-
-#define QQVGA_WIDTH		160
-#define QQVGA_HEIGHT		120
-
-/* Max Resolution supported by the driver */
-#define VID_MAX_WIDTH		1280	/* Largest width */
-#define VID_MAX_HEIGHT		720	/* Largest height */
-
-/* Mimimum requirement is 2x2 for DSS */
-#define VID_MIN_WIDTH		2
-#define VID_MIN_HEIGHT		2
-
-/* 2048 x 2048 is max res supported by OMAP display controller */
-#define MAX_PIXELS_PER_LINE     2048
-
-#define VRFB_TX_TIMEOUT         1000
-#define VRFB_NUM_BUFS		4
-
-/* Max buffer size tobe allocated during init */
-#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
-
 static struct videobuf_queue_ops video_vbq_ops;
 /* Variables configurable through module params*/
 static u32 video1_numbuffers = 3;
@@ -172,84 +145,6 @@
 #define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
 
 /*
- * Allocate buffers
- */
-static unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
-{
-	u32 order, size;
-	unsigned long virt_addr, addr;
-
-	size = PAGE_ALIGN(buf_size);
-	order = get_order(size);
-	virt_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, order);
-	addr = virt_addr;
-
-	if (virt_addr) {
-		while (size > 0) {
-			SetPageReserved(virt_to_page(addr));
-			addr += PAGE_SIZE;
-			size -= PAGE_SIZE;
-		}
-	}
-	*phys_addr = (u32) virt_to_phys((void *) virt_addr);
-	return virt_addr;
-}
-
-/*
- * Free buffers
- */
-static void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
-{
-	u32 order, size;
-	unsigned long addr = virtaddr;
-
-	size = PAGE_ALIGN(buf_size);
-	order = get_order(size);
-
-	while (size > 0) {
-		ClearPageReserved(virt_to_page(addr));
-		addr += PAGE_SIZE;
-		size -= PAGE_SIZE;
-	}
-	free_pages((unsigned long) virtaddr, order);
-}
-
-/*
- * Function for allocating video buffers
- */
-static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
-		unsigned int *count, int startindex)
-{
-	int i, j;
-
-	for (i = 0; i < *count; i++) {
-		if (!vout->smsshado_virt_addr[i]) {
-			vout->smsshado_virt_addr[i] =
-				omap_vout_alloc_buffer(vout->smsshado_size,
-						&vout->smsshado_phy_addr[i]);
-		}
-		if (!vout->smsshado_virt_addr[i] && startindex != -1) {
-			if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex)
-				break;
-		}
-		if (!vout->smsshado_virt_addr[i]) {
-			for (j = 0; j < i; j++) {
-				omap_vout_free_buffer(
-						vout->smsshado_virt_addr[j],
-						vout->smsshado_size);
-				vout->smsshado_virt_addr[j] = 0;
-				vout->smsshado_phy_addr[j] = 0;
-			}
-			*count = 0;
-			return -ENOMEM;
-		}
-		memset((void *) vout->smsshado_virt_addr[i], 0,
-				vout->smsshado_size);
-	}
-	return 0;
-}
-
-/*
  * Try format
  */
 static int omap_vout_try_format(struct v4l2_pix_format *pix)
@@ -342,73 +237,9 @@
 }
 
 /*
- * Wakes up the application once the DMA transfer to VRFB space is completed.
- */
-static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
-	struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
-
-	t->tx_status = 1;
-	wake_up_interruptible(&t->wait);
-}
-
-/*
- * Release the VRFB context once the module exits
- */
-static void omap_vout_release_vrfb(struct omap_vout_device *vout)
-{
-	int i;
-
-	for (i = 0; i < VRFB_NUM_BUFS; i++)
-		omap_vrfb_release_ctx(&vout->vrfb_context[i]);
-
-	if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
-		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
-		omap_free_dma(vout->vrfb_dma_tx.dma_ch);
-	}
-}
-
-/*
- * Return true if rotation is 90 or 270
- */
-static inline int rotate_90_or_270(const struct omap_vout_device *vout)
-{
-	return (vout->rotation == dss_rotation_90_degree ||
-			vout->rotation == dss_rotation_270_degree);
-}
-
-/*
- * Return true if rotation is enabled
- */
-static inline int rotation_enabled(const struct omap_vout_device *vout)
-{
-	return vout->rotation || vout->mirror;
-}
-
-/*
- * Reverse the rotation degree if mirroring is enabled
- */
-static inline int calc_rotation(const struct omap_vout_device *vout)
-{
-	if (!vout->mirror)
-		return vout->rotation;
-
-	switch (vout->rotation) {
-	case dss_rotation_90_degree:
-		return dss_rotation_270_degree;
-	case dss_rotation_270_degree:
-		return dss_rotation_90_degree;
-	case dss_rotation_180_degree:
-		return dss_rotation_0_degree;
-	default:
-		return dss_rotation_180_degree;
-	}
-}
-
-/*
  * Free the V4L2 buffers
  */
-static void omap_vout_free_buffers(struct omap_vout_device *vout)
+void omap_vout_free_buffers(struct omap_vout_device *vout)
 {
 	int i, numbuffers;
 
@@ -425,52 +256,6 @@
 }
 
 /*
- * Free VRFB buffers
- */
-static void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
-{
-	int j;
-
-	for (j = 0; j < VRFB_NUM_BUFS; j++) {
-		omap_vout_free_buffer(vout->smsshado_virt_addr[j],
-				vout->smsshado_size);
-		vout->smsshado_virt_addr[j] = 0;
-		vout->smsshado_phy_addr[j] = 0;
-	}
-}
-
-/*
- * Allocate the buffers for the VRFB space.  Data is copied from V4L2
- * buffers to the VRFB buffers using the DMA engine.
- */
-static int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
-			  unsigned int *count, unsigned int startindex)
-{
-	int i;
-	bool yuv_mode;
-
-	/* Allocate the VRFB buffers only if the buffers are not
-	 * allocated during init time.
-	 */
-	if ((rotation_enabled(vout)) && !vout->vrfb_static_allocation)
-		if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
-			return -ENOMEM;
-
-	if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
-			vout->dss_mode == OMAP_DSS_COLOR_UYVY)
-		yuv_mode = true;
-	else
-		yuv_mode = false;
-
-	for (i = 0; i < *count; i++)
-		omap_vrfb_setup(&vout->vrfb_context[i],
-				vout->smsshado_phy_addr[i], vout->pix.width,
-				vout->pix.height, vout->bpp, yuv_mode);
-
-	return 0;
-}
-
-/*
  * Convert V4L2 rotation to DSS rotation
  *	V4L2 understand 0, 90, 180, 270.
  *	Convert to 0, 1, 2 and 3 respectively for DSS
@@ -499,124 +284,38 @@
 	return ret;
 }
 
-/*
- * Calculate the buffer offsets from which the streaming should
- * start. This offset calculation is mainly required because of
- * the VRFB 32 pixels alignment with rotation.
- */
 static int omap_vout_calculate_offset(struct omap_vout_device *vout)
 {
-	struct omap_overlay *ovl;
-	enum dss_rotation rotation;
 	struct omapvideo_info *ovid;
-	bool mirroring = vout->mirror;
-	struct omap_dss_device *cur_display;
 	struct v4l2_rect *crop = &vout->crop;
 	struct v4l2_pix_format *pix = &vout->pix;
 	int *cropped_offset = &vout->cropped_offset;
-	int vr_ps = 1, ps = 2, temp_ps = 2;
-	int offset = 0, ctop = 0, cleft = 0, line_length = 0;
+	int ps = 2, line_length = 0;
 
 	ovid = &vout->vid_info;
-	ovl = ovid->overlays[0];
-	/* get the display device attached to the overlay */
-	if (!ovl->manager || !ovl->manager->device)
-		return -1;
 
-	cur_display = ovl->manager->device;
-	rotation = calc_rotation(vout);
-
-	if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
-			V4L2_PIX_FMT_UYVY == pix->pixelformat) {
-		if (rotation_enabled(vout)) {
-			/*
-			 * ps    - Actual pixel size for YUYV/UYVY for
-			 *         VRFB/Mirroring is 4 bytes
-			 * vr_ps - Virtually pixel size for YUYV/UYVY is
-			 *         2 bytes
-			 */
-			ps = 4;
-			vr_ps = 2;
-		} else {
-			ps = 2;	/* otherwise the pixel size is 2 byte */
-		}
-	} else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
-		ps = 4;
-	} else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
-		ps = 3;
-	}
-	vout->ps = ps;
-	vout->vr_ps = vr_ps;
-
-	if (rotation_enabled(vout)) {
-		line_length = MAX_PIXELS_PER_LINE;
-		ctop = (pix->height - crop->height) - crop->top;
-		cleft = (pix->width - crop->width) - crop->left;
+	if (ovid->rotation_type == VOUT_ROT_VRFB) {
+		omap_vout_calculate_vrfb_offset(vout);
 	} else {
-		line_length = pix->width;
-	}
-	vout->line_length = line_length;
-	switch (rotation) {
-	case dss_rotation_90_degree:
-		offset = vout->vrfb_context[0].yoffset *
-			vout->vrfb_context[0].bytespp;
-		temp_ps = ps / vr_ps;
-		if (mirroring == 0) {
-			*cropped_offset = offset + line_length *
-				temp_ps * cleft + crop->top * temp_ps;
-		} else {
-			*cropped_offset = offset + line_length * temp_ps *
-				cleft + crop->top * temp_ps + (line_length *
-				((crop->width / (vr_ps)) - 1) * ps);
-		}
-		break;
-	case dss_rotation_180_degree:
-		offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
-			vout->vrfb_context[0].bytespp) +
-			(vout->vrfb_context[0].xoffset *
-			vout->vrfb_context[0].bytespp));
-		if (mirroring == 0) {
-			*cropped_offset = offset + (line_length * ps * ctop) +
-				(cleft / vr_ps) * ps;
+		vout->line_length = line_length = pix->width;
 
-		} else {
-			*cropped_offset = offset + (line_length * ps * ctop) +
-				(cleft / vr_ps) * ps + (line_length *
-				(crop->height - 1) * ps);
-		}
-		break;
-	case dss_rotation_270_degree:
-		offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
-			vout->vrfb_context[0].bytespp;
-		temp_ps = ps / vr_ps;
-		if (mirroring == 0) {
-			*cropped_offset = offset + line_length *
-			    temp_ps * crop->left + ctop * ps;
-		} else {
-			*cropped_offset = offset + line_length *
-				temp_ps * crop->left + ctop * ps +
-				(line_length * ((crop->width / vr_ps) - 1) *
-				 ps);
-		}
-		break;
-	case dss_rotation_0_degree:
-		if (mirroring == 0) {
-			*cropped_offset = (line_length * ps) *
-				crop->top + (crop->left / vr_ps) * ps;
-		} else {
-			*cropped_offset = (line_length * ps) *
-				crop->top + (crop->left / vr_ps) * ps +
-				(line_length * (crop->height - 1) * ps);
-		}
-		break;
-	default:
-		*cropped_offset = (line_length * ps * crop->top) /
-			vr_ps + (crop->left * ps) / vr_ps +
-			((crop->width / vr_ps) - 1) * ps;
-		break;
+		if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+			V4L2_PIX_FMT_UYVY == pix->pixelformat)
+			ps = 2;
+		else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
+			ps = 4;
+		else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+			ps = 3;
+
+		vout->ps = ps;
+
+		*cropped_offset = (line_length * ps) *
+			crop->top + crop->left * ps;
 	}
+
 	v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
-			__func__, *cropped_offset);
+			__func__, vout->cropped_offset);
+
 	return 0;
 }
 
@@ -664,7 +363,7 @@
 /*
  * Setup the overlay
  */
-int omapvid_setup_overlay(struct omap_vout_device *vout,
+static int omapvid_setup_overlay(struct omap_vout_device *vout,
 		struct omap_overlay *ovl, int posx, int posy, int outw,
 		int outh, u32 addr)
 {
@@ -687,7 +386,7 @@
 	/* Setup the input plane parameters according to
 	 * rotation value selected.
 	 */
-	if (rotate_90_or_270(vout)) {
+	if (is_rotation_90_or_270(vout)) {
 		cropheight = vout->crop.width;
 		cropwidth = vout->crop.height;
 		pixheight = vout->pix.width;
@@ -711,7 +410,7 @@
 	info.out_width = outw;
 	info.out_height = outh;
 	info.global_alpha = vout->win.global_alpha;
-	if (!rotation_enabled(vout)) {
+	if (!is_rotation_enabled(vout)) {
 		info.rotation = 0;
 		info.rotation_type = OMAP_DSS_ROT_DMA;
 		info.screen_width = pixwidth;
@@ -744,7 +443,7 @@
 /*
  * Initialize the overlay structure
  */
-int omapvid_init(struct omap_vout_device *vout, u32 addr)
+static int omapvid_init(struct omap_vout_device *vout, u32 addr)
 {
 	int ret = 0, i;
 	struct v4l2_window *win;
@@ -809,7 +508,7 @@
 /*
  * Apply the changes set the go bit of DSS
  */
-int omapvid_apply_changes(struct omap_vout_device *vout)
+static int omapvid_apply_changes(struct omap_vout_device *vout)
 {
 	int i;
 	struct omap_overlay *ovl;
@@ -825,7 +524,7 @@
 	return 0;
 }
 
-void omap_vout_isr(void *arg, unsigned int irqstatus)
+static void omap_vout_isr(void *arg, unsigned int irqstatus)
 {
 	int ret;
 	u32 addr, fid;
@@ -848,10 +547,20 @@
 
 	spin_lock(&vout->vbq_lock);
 	do_gettimeofday(&timevalue);
-	if (cur_display->type == OMAP_DISPLAY_TYPE_DPI) {
-		if (!(irqstatus & DISPC_IRQ_VSYNC))
-			goto vout_isr_err;
 
+	if (cur_display->type != OMAP_DISPLAY_TYPE_VENC) {
+		switch (cur_display->type) {
+		case OMAP_DISPLAY_TYPE_DPI:
+			if (!(irqstatus & (DISPC_IRQ_VSYNC | DISPC_IRQ_VSYNC2)))
+				goto vout_isr_err;
+			break;
+		case OMAP_DISPLAY_TYPE_HDMI:
+			if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+				goto vout_isr_err;
+			break;
+		default:
+			goto vout_isr_err;
+		}
 		if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
 			vout->cur_frm->ts = timevalue;
 			vout->cur_frm->state = VIDEOBUF_DONE;
@@ -875,7 +584,7 @@
 		ret = omapvid_init(vout, addr);
 		if (ret)
 			printk(KERN_ERR VOUT_NAME
-					"failed to set overlay info\n");
+				"failed to set overlay info\n");
 		/* Enable the pipeline and set the Go bit */
 		ret = omapvid_apply_changes(vout);
 		if (ret)
@@ -954,6 +663,7 @@
 	int startindex = 0, i, j;
 	u32 phy_addr = 0, virt_addr = 0;
 	struct omap_vout_device *vout = q->priv_data;
+	struct omapvideo_info *ovid = &vout->vid_info;
 
 	if (!vout)
 		return -EINVAL;
@@ -966,13 +676,10 @@
 	if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
 		*count = startindex;
 
-	if ((rotation_enabled(vout)) && *count > VRFB_NUM_BUFS)
-		*count = VRFB_NUM_BUFS;
-
-	/* If rotation is enabled, allocate memory for VRFB space also */
-	if (rotation_enabled(vout))
+	if (ovid->rotation_type == VOUT_ROT_VRFB) {
 		if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
 			return -ENOMEM;
+	}
 
 	if (V4L2_MEMORY_MMAP != vout->memory)
 		return 0;
@@ -996,8 +703,11 @@
 		virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
 				&phy_addr);
 		if (!virt_addr) {
-			if (!rotation_enabled(vout))
+			if (ovid->rotation_type == VOUT_ROT_NONE) {
 				break;
+			} else {
+				if (!is_rotation_enabled(vout))
+					break;
 			/* Free the VRFB buffers if no space for V4L2 buffers */
 			for (j = i; j < *count; j++) {
 				omap_vout_free_buffer(
@@ -1005,6 +715,7 @@
 						vout->smsshado_size);
 				vout->smsshado_virt_addr[j] = 0;
 				vout->smsshado_phy_addr[j] = 0;
+				}
 			}
 		}
 		vout->buf_virt_addr[i] = virt_addr;
@@ -1017,9 +728,9 @@
 
 /*
  * Free the V4L2 buffers additionally allocated than default
- * number of buffers and free all the VRFB buffers
+ * number of buffers
  */
-static void omap_vout_free_allbuffers(struct omap_vout_device *vout)
+static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
 {
 	int num_buffers = 0, i;
 
@@ -1034,20 +745,6 @@
 		vout->buf_virt_addr[i] = 0;
 		vout->buf_phy_addr[i] = 0;
 	}
-	/* Free the VRFB buffers only if they are allocated
-	 * during reqbufs.  Don't free if init time allocated
-	 */
-	if (!vout->vrfb_static_allocation) {
-		for (i = 0; i < VRFB_NUM_BUFS; i++) {
-			if (vout->smsshado_virt_addr[i]) {
-				omap_vout_free_buffer(
-						vout->smsshado_virt_addr[i],
-						vout->smsshado_size);
-				vout->smsshado_virt_addr[i] = 0;
-				vout->smsshado_phy_addr[i] = 0;
-			}
-		}
-	}
 	vout->buffer_allocated = num_buffers;
 }
 
@@ -1059,16 +756,11 @@
  * buffer into VRFB memory space before giving it to the DSS.
  */
 static int omap_vout_buffer_prepare(struct videobuf_queue *q,
-			    struct videobuf_buffer *vb,
-			    enum v4l2_field field)
+			struct videobuf_buffer *vb,
+			enum v4l2_field field)
 {
-	dma_addr_t dmabuf;
-	struct vid_vrfb_dma *tx;
-	enum dss_rotation rotation;
 	struct omap_vout_device *vout = q->priv_data;
-	u32 dest_frame_index = 0, src_element_index = 0;
-	u32 dest_element_index = 0, src_frame_index = 0;
-	u32 elem_count = 0, frame_count = 0, pixsize = 2;
+	struct omapvideo_info *ovid = &vout->vid_info;
 
 	if (VIDEOBUF_NEEDS_INIT == vb->state) {
 		vb->width = vout->pix.width;
@@ -1087,66 +779,24 @@
 		vout->queued_buf_addr[vb->i] = (u8 *)
 			omap_vout_uservirt_to_phys(vb->baddr);
 	} else {
+		u32 addr, dma_addr;
+		unsigned long size;
+
+		addr = (unsigned long) vout->buf_virt_addr[vb->i];
+		size = (unsigned long) vb->size;
+
+		dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+				size, DMA_TO_DEVICE);
+		if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+			v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
+
 		vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
 	}
 
-	if (!rotation_enabled(vout))
+	if (ovid->rotation_type == VOUT_ROT_VRFB)
+		return omap_vout_prepare_vrfb(vout, vb);
+	else
 		return 0;
-
-	dmabuf = vout->buf_phy_addr[vb->i];
-	/* If rotation is enabled, copy input buffer into VRFB
-	 * memory space using DMA. We are copying input buffer
-	 * into VRFB memory space of desired angle and DSS will
-	 * read image VRFB memory for 0 degree angle
-	 */
-	pixsize = vout->bpp * vout->vrfb_bpp;
-	/*
-	 * DMA transfer in double index mode
-	 */
-
-	/* Frame index */
-	dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
-			(vout->pix.width * vout->bpp)) + 1;
-
-	/* Source and destination parameters */
-	src_element_index = 0;
-	src_frame_index = 0;
-	dest_element_index = 1;
-	/* Number of elements per frame */
-	elem_count = vout->pix.width * vout->bpp;
-	frame_count = vout->pix.height;
-	tx = &vout->vrfb_dma_tx;
-	tx->tx_status = 0;
-	omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
-			(elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
-			tx->dev_id, 0x0);
-	/* src_port required only for OMAP1 */
-	omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-			dmabuf, src_element_index, src_frame_index);
-	/*set dma source burst mode for VRFB */
-	omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
-	rotation = calc_rotation(vout);
-
-	/* dest_port required only for OMAP1 */
-	omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
-			vout->vrfb_context[vb->i].paddr[0], dest_element_index,
-			dest_frame_index);
-	/*set dma dest burst mode for VRFB */
-	omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
-	omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
-
-	omap_start_dma(tx->dma_ch);
-	interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
-
-	if (tx->tx_status == 0) {
-		omap_stop_dma(tx->dma_ch);
-		return -EINVAL;
-	}
-	/* Store buffers physical address into an array. Addresses
-	 * from this array will be used to configure DSS */
-	vout->queued_buf_addr[vb->i] = (u8 *)
-		vout->vrfb_context[vb->i].paddr[rotation];
-	return 0;
 }
 
 /*
@@ -1298,7 +948,15 @@
 				"Unable to apply changes\n");
 
 	/* Free all buffers */
-	omap_vout_free_allbuffers(vout);
+	omap_vout_free_extra_buffers(vout);
+
+	/* Free the VRFB buffers only if they are allocated
+	 * during reqbufs.  Don't free if init time allocated
+	 */
+	if (ovid->rotation_type == VOUT_ROT_VRFB) {
+		if (!vout->vrfb_static_allocation)
+			omap_vout_free_vrfb_buffers(vout);
+	}
 	videobuf_mmap_free(q);
 
 	/* Even if apply changes fails we should continue
@@ -1307,7 +965,7 @@
 		u32 mask = 0;
 
 		mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
-			DISPC_IRQ_EVSYNC_ODD;
+			DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
 		omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
 		vout->streaming = 0;
 
@@ -1383,10 +1041,7 @@
 			struct v4l2_fmtdesc *fmt)
 {
 	int index = fmt->index;
-	enum v4l2_buf_type type = fmt->type;
 
-	fmt->index = index;
-	fmt->type = type;
 	if (index >= NUM_OUTPUT_FORMATS)
 		return -EINVAL;
 
@@ -1457,7 +1112,7 @@
 
 	/* We dont support RGB24-packed mode if vrfb rotation
 	 * is enabled*/
-	if ((rotation_enabled(vout)) &&
+	if ((is_rotation_enabled(vout)) &&
 			f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
 		ret = -EINVAL;
 		goto s_fmt_vid_out_exit;
@@ -1465,7 +1120,7 @@
 
 	/* get the framebuffer parameters */
 
-	if (rotate_90_or_270(vout)) {
+	if (is_rotation_90_or_270(vout)) {
 		vout->fbuf.fmt.height = timing->x_res;
 		vout->fbuf.fmt.width = timing->y_res;
 	} else {
@@ -1555,10 +1210,7 @@
 			struct v4l2_fmtdesc *fmt)
 {
 	int index = fmt->index;
-	enum v4l2_buf_type type = fmt->type;
 
-	fmt->index = index;
-	fmt->type = type;
 	if (index >= NUM_OUTPUT_FORMATS)
 		return -EINVAL;
 
@@ -1645,7 +1297,7 @@
 	/* get the display device attached to the overlay */
 	timing = &ovl->manager->device->panel.timings;
 
-	if (rotate_90_or_270(vout)) {
+	if (is_rotation_90_or_270(vout)) {
 		vout->fbuf.fmt.height = timing->x_res;
 		vout->fbuf.fmt.width = timing->y_res;
 	} else {
@@ -1725,9 +1377,17 @@
 	switch (a->id) {
 	case V4L2_CID_ROTATE:
 	{
+		struct omapvideo_info *ovid;
 		int rotation = a->value;
 
+		ovid = &vout->vid_info;
+
 		mutex_lock(&vout->lock);
+		if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+			mutex_unlock(&vout->lock);
+			ret = -ERANGE;
+			break;
+		}
 
 		if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
 			mutex_unlock(&vout->lock);
@@ -1783,6 +1443,11 @@
 		ovl = ovid->overlays[0];
 
 		mutex_lock(&vout->lock);
+		if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+			mutex_unlock(&vout->lock);
+			ret = -ERANGE;
+			break;
+		}
 
 		if (mirror  && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
 			mutex_unlock(&vout->lock);
@@ -1893,7 +1558,7 @@
 		}
 	}
 
-	if ((rotation_enabled(vout)) &&
+	if ((is_rotation_enabled(vout)) &&
 			vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
 		v4l2_warn(&vout->vid_dev->v4l2_dev,
 				"DMA Channel not allocated for Rotation\n");
@@ -1908,15 +1573,28 @@
 	struct omap_vout_device *vout = fh;
 	struct videobuf_queue *q = &vout->vbq;
 
+	int ret;
+	u32 addr;
+	unsigned long size;
+	struct videobuf_buffer *vb;
+
+	vb = q->bufs[b->index];
+
 	if (!vout->streaming)
 		return -EINVAL;
 
 	if (file->f_flags & O_NONBLOCK)
 		/* Call videobuf_dqbuf for non blocking mode */
-		return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+		ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
 	else
 		/* Call videobuf_dqbuf for  blocking mode */
-		return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+		ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+
+	addr = (unsigned long) vout->buf_phy_addr[vb->i];
+	size = (unsigned long) vb->size;
+	dma_unmap_single(vout->vid_dev->v4l2_dev.dev,  addr,
+				size, DMA_TO_DEVICE);
+	return ret;
 }
 
 static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
@@ -1965,7 +1643,8 @@
 	addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
 		+ vout->cropped_offset;
 
-	mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
+	mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+		| DISPC_IRQ_VSYNC2;
 
 	omap_dispc_register_isr(omap_vout_isr, vout, mask);
 
@@ -2015,7 +1694,8 @@
 		return -EINVAL;
 
 	vout->streaming = 0;
-	mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
+	mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+		| DISPC_IRQ_VSYNC2;
 
 	omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
 
@@ -2228,7 +1908,8 @@
 	vout->mirror = 0;
 	vout->control[2].id = V4L2_CID_HFLIP;
 	vout->control[2].value = 0;
-	vout->vrfb_bpp = 2;
+	if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+		vout->vrfb_bpp = 2;
 
 	control[1].id = V4L2_CID_BG_COLOR;
 	control[1].value = 0;
@@ -2260,17 +1941,15 @@
 		int vid_num)
 {
 	u32 numbuffers;
-	int ret = 0, i, j;
-	int image_width, image_height;
-	struct video_device *vfd;
+	int ret = 0, i;
+	struct omapvideo_info *ovid;
 	struct omap_vout_device *vout;
-	int static_vrfb_allocation = 0, vrfb_num_bufs = VRFB_NUM_BUFS;
 	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
 	struct omap2video_device *vid_dev =
 		container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
 
 	vout = vid_dev->vouts[vid_num];
-	vfd = vout->vfd;
+	ovid = &vout->vid_info;
 
 	numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
 	vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
@@ -2287,66 +1966,16 @@
 		}
 	}
 
-	for (i = 0; i < VRFB_NUM_BUFS; i++) {
-		if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
-			dev_info(&pdev->dev, ": VRFB allocation failed\n");
-			for (j = 0; j < i; j++)
-				omap_vrfb_release_ctx(&vout->vrfb_context[j]);
-			ret = -ENOMEM;
-			goto free_buffers;
-		}
-	}
 	vout->cropped_offset = 0;
 
-	/* Calculate VRFB memory size */
-	/* allocate for worst case size */
-	image_width = VID_MAX_WIDTH / TILE_SIZE;
-	if (VID_MAX_WIDTH % TILE_SIZE)
-		image_width++;
-
-	image_width = image_width * TILE_SIZE;
-	image_height = VID_MAX_HEIGHT / TILE_SIZE;
-
-	if (VID_MAX_HEIGHT % TILE_SIZE)
-		image_height++;
-
-	image_height = image_height * TILE_SIZE;
-	vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
-
-	/*
-	 * Request and Initialize DMA, for DMA based VRFB transfer
-	 */
-	vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
-	vout->vrfb_dma_tx.dma_ch = -1;
-	vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
-	ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
-			omap_vout_vrfb_dma_tx_callback,
-			(void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
-	if (ret < 0) {
-		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
-		dev_info(&pdev->dev, ": failed to allocate DMA Channel for"
-				" video%d\n", vfd->minor);
+	if (ovid->rotation_type == VOUT_ROT_VRFB) {
+		int static_vrfb_allocation = (vid_num == 0) ?
+			vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+		ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+				static_vrfb_allocation);
 	}
-	init_waitqueue_head(&vout->vrfb_dma_tx.wait);
 
-	/* Allocate VRFB buffers if selected through bootargs */
-	static_vrfb_allocation = (vid_num == 0) ?
-		vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
-
-	/* statically allocated the VRFB buffer is done through
-	   commands line aruments */
-	if (static_vrfb_allocation) {
-		if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
-			ret =  -ENOMEM;
-			goto release_vrfb_ctx;
-		}
-		vout->vrfb_static_allocation = 1;
-	}
-	return 0;
-
-release_vrfb_ctx:
-	for (j = 0; j < VRFB_NUM_BUFS; j++)
-		omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+	return ret;
 
 free_buffers:
 	for (i = 0; i < numbuffers; i++) {
@@ -2389,6 +2018,10 @@
 		vout->vid_info.num_overlays = 1;
 		vout->vid_info.id = k + 1;
 
+		/* Set VRFB as rotation_type for omap2 and omap3 */
+		if (cpu_is_omap24xx() || cpu_is_omap34xx())
+			vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+
 		/* Setup the default configuration for the video devices
 		 */
 		if (omap_vout_setup_video_data(vout) != 0) {
@@ -2422,7 +2055,8 @@
 			goto success;
 
 error2:
-		omap_vout_release_vrfb(vout);
+		if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+			omap_vout_release_vrfb(vout);
 		omap_vout_free_buffers(vout);
 error1:
 		video_device_release(vfd);
@@ -2443,11 +2077,13 @@
 static void omap_vout_cleanup_device(struct omap_vout_device *vout)
 {
 	struct video_device *vfd;
+	struct omapvideo_info *ovid;
 
 	if (!vout)
 		return;
 
 	vfd = vout->vfd;
+	ovid = &vout->vid_info;
 	if (vfd) {
 		if (!video_is_registered(vfd)) {
 			/*
@@ -2463,14 +2099,15 @@
 			video_unregister_device(vfd);
 		}
 	}
-
-	omap_vout_release_vrfb(vout);
+	if (ovid->rotation_type == VOUT_ROT_VRFB) {
+		omap_vout_release_vrfb(vout);
+		/* Free the VRFB buffer if allocated
+		 * init time
+		 */
+		if (vout->vrfb_static_allocation)
+			omap_vout_free_vrfb_buffers(vout);
+	}
 	omap_vout_free_buffers(vout);
-	/* Free the VRFB buffer if allocated
-	 * init time
-	 */
-	if (vout->vrfb_static_allocation)
-		omap_vout_free_vrfb_buffers(vout);
 
 	kfree(vout);
 }
diff --git a/drivers/media/video/omap/omap_vout_vrfb.c b/drivers/media/video/omap/omap_vout_vrfb.c
new file mode 100644
index 0000000..ebebcac4
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.c
@@ -0,0 +1,390 @@
+/*
+ * omap_vout_vrfb.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-device.h>
+
+#include <plat/dma.h>
+#include <plat/vrfb.h>
+
+#include "omap_voutdef.h"
+#include "omap_voutlib.h"
+
+/*
+ * Function for allocating video buffers
+ */
+static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
+		unsigned int *count, int startindex)
+{
+	int i, j;
+
+	for (i = 0; i < *count; i++) {
+		if (!vout->smsshado_virt_addr[i]) {
+			vout->smsshado_virt_addr[i] =
+				omap_vout_alloc_buffer(vout->smsshado_size,
+						&vout->smsshado_phy_addr[i]);
+		}
+		if (!vout->smsshado_virt_addr[i] && startindex != -1) {
+			if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex)
+				break;
+		}
+		if (!vout->smsshado_virt_addr[i]) {
+			for (j = 0; j < i; j++) {
+				omap_vout_free_buffer(
+						vout->smsshado_virt_addr[j],
+						vout->smsshado_size);
+				vout->smsshado_virt_addr[j] = 0;
+				vout->smsshado_phy_addr[j] = 0;
+			}
+			*count = 0;
+			return -ENOMEM;
+		}
+		memset((void *) vout->smsshado_virt_addr[i], 0,
+				vout->smsshado_size);
+	}
+	return 0;
+}
+
+/*
+ * Wakes up the application once the DMA transfer to VRFB space is completed.
+ */
+static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
+{
+	struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
+
+	t->tx_status = 1;
+	wake_up_interruptible(&t->wait);
+}
+
+/*
+ * Free VRFB buffers
+ */
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
+{
+	int j;
+
+	for (j = 0; j < VRFB_NUM_BUFS; j++) {
+		omap_vout_free_buffer(vout->smsshado_virt_addr[j],
+				vout->smsshado_size);
+		vout->smsshado_virt_addr[j] = 0;
+		vout->smsshado_phy_addr[j] = 0;
+	}
+}
+
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+			u32 static_vrfb_allocation)
+{
+	int ret = 0, i, j;
+	struct omap_vout_device *vout;
+	struct video_device *vfd;
+	int image_width, image_height;
+	int vrfb_num_bufs = VRFB_NUM_BUFS;
+	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+	struct omap2video_device *vid_dev =
+		container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+
+	vout = vid_dev->vouts[vid_num];
+	vfd = vout->vfd;
+
+	for (i = 0; i < VRFB_NUM_BUFS; i++) {
+		if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
+			dev_info(&pdev->dev, ": VRFB allocation failed\n");
+			for (j = 0; j < i; j++)
+				omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+			ret = -ENOMEM;
+			goto free_buffers;
+		}
+	}
+
+	/* Calculate VRFB memory size */
+	/* allocate for worst case size */
+	image_width = VID_MAX_WIDTH / TILE_SIZE;
+	if (VID_MAX_WIDTH % TILE_SIZE)
+		image_width++;
+
+	image_width = image_width * TILE_SIZE;
+	image_height = VID_MAX_HEIGHT / TILE_SIZE;
+
+	if (VID_MAX_HEIGHT % TILE_SIZE)
+		image_height++;
+
+	image_height = image_height * TILE_SIZE;
+	vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
+
+	/*
+	 * Request and Initialize DMA, for DMA based VRFB transfer
+	 */
+	vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
+	vout->vrfb_dma_tx.dma_ch = -1;
+	vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
+	ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
+			omap_vout_vrfb_dma_tx_callback,
+			(void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
+	if (ret < 0) {
+		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+		dev_info(&pdev->dev, ": failed to allocate DMA Channel for"
+				" video%d\n", vfd->minor);
+	}
+	init_waitqueue_head(&vout->vrfb_dma_tx.wait);
+
+	/* statically allocated the VRFB buffer is done through
+	   commands line aruments */
+	if (static_vrfb_allocation) {
+		if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
+			ret =  -ENOMEM;
+			goto release_vrfb_ctx;
+		}
+		vout->vrfb_static_allocation = 1;
+	}
+	return 0;
+
+release_vrfb_ctx:
+	for (j = 0; j < VRFB_NUM_BUFS; j++)
+		omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+free_buffers:
+	omap_vout_free_buffers(vout);
+
+	return ret;
+}
+
+/*
+ * Release the VRFB context once the module exits
+ */
+void omap_vout_release_vrfb(struct omap_vout_device *vout)
+{
+	int i;
+
+	for (i = 0; i < VRFB_NUM_BUFS; i++)
+		omap_vrfb_release_ctx(&vout->vrfb_context[i]);
+
+	if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
+		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+		omap_free_dma(vout->vrfb_dma_tx.dma_ch);
+	}
+}
+
+/*
+ * Allocate the buffers for the VRFB space.  Data is copied from V4L2
+ * buffers to the VRFB buffers using the DMA engine.
+ */
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+			  unsigned int *count, unsigned int startindex)
+{
+	int i;
+	bool yuv_mode;
+
+	if (!is_rotation_enabled(vout))
+		return 0;
+
+	/* If rotation is enabled, allocate memory for VRFB space also */
+	*count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count;
+
+	/* Allocate the VRFB buffers only if the buffers are not
+	 * allocated during init time.
+	 */
+	if (!vout->vrfb_static_allocation)
+		if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
+			return -ENOMEM;
+
+	if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
+			vout->dss_mode == OMAP_DSS_COLOR_UYVY)
+		yuv_mode = true;
+	else
+		yuv_mode = false;
+
+	for (i = 0; i < *count; i++)
+		omap_vrfb_setup(&vout->vrfb_context[i],
+				vout->smsshado_phy_addr[i], vout->pix.width,
+				vout->pix.height, vout->bpp, yuv_mode);
+
+	return 0;
+}
+
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+				struct videobuf_buffer *vb)
+{
+	dma_addr_t dmabuf;
+	struct vid_vrfb_dma *tx;
+	enum dss_rotation rotation;
+	u32 dest_frame_index = 0, src_element_index = 0;
+	u32 dest_element_index = 0, src_frame_index = 0;
+	u32 elem_count = 0, frame_count = 0, pixsize = 2;
+
+	if (!is_rotation_enabled(vout))
+		return 0;
+
+	dmabuf = vout->buf_phy_addr[vb->i];
+	/* If rotation is enabled, copy input buffer into VRFB
+	 * memory space using DMA. We are copying input buffer
+	 * into VRFB memory space of desired angle and DSS will
+	 * read image VRFB memory for 0 degree angle
+	 */
+	pixsize = vout->bpp * vout->vrfb_bpp;
+	/*
+	 * DMA transfer in double index mode
+	 */
+
+	/* Frame index */
+	dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
+			(vout->pix.width * vout->bpp)) + 1;
+
+	/* Source and destination parameters */
+	src_element_index = 0;
+	src_frame_index = 0;
+	dest_element_index = 1;
+	/* Number of elements per frame */
+	elem_count = vout->pix.width * vout->bpp;
+	frame_count = vout->pix.height;
+	tx = &vout->vrfb_dma_tx;
+	tx->tx_status = 0;
+	omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
+			(elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
+			tx->dev_id, 0x0);
+	/* src_port required only for OMAP1 */
+	omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+			dmabuf, src_element_index, src_frame_index);
+	/*set dma source burst mode for VRFB */
+	omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+	rotation = calc_rotation(vout);
+
+	/* dest_port required only for OMAP1 */
+	omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
+			vout->vrfb_context[vb->i].paddr[0], dest_element_index,
+			dest_frame_index);
+	/*set dma dest burst mode for VRFB */
+	omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+	omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
+
+	omap_start_dma(tx->dma_ch);
+	interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
+
+	if (tx->tx_status == 0) {
+		omap_stop_dma(tx->dma_ch);
+		return -EINVAL;
+	}
+	/* Store buffers physical address into an array. Addresses
+	 * from this array will be used to configure DSS */
+	vout->queued_buf_addr[vb->i] = (u8 *)
+		vout->vrfb_context[vb->i].paddr[rotation];
+	return 0;
+}
+
+/*
+ * Calculate the buffer offsets from which the streaming should
+ * start. This offset calculation is mainly required because of
+ * the VRFB 32 pixels alignment with rotation.
+ */
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
+{
+	enum dss_rotation rotation;
+	bool mirroring = vout->mirror;
+	struct v4l2_rect *crop = &vout->crop;
+	struct v4l2_pix_format *pix = &vout->pix;
+	int *cropped_offset = &vout->cropped_offset;
+	int vr_ps = 1, ps = 2, temp_ps = 2;
+	int offset = 0, ctop = 0, cleft = 0, line_length = 0;
+
+	rotation = calc_rotation(vout);
+
+	if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+			V4L2_PIX_FMT_UYVY == pix->pixelformat) {
+		if (is_rotation_enabled(vout)) {
+			/*
+			 * ps    - Actual pixel size for YUYV/UYVY for
+			 *         VRFB/Mirroring is 4 bytes
+			 * vr_ps - Virtually pixel size for YUYV/UYVY is
+			 *         2 bytes
+			 */
+			ps = 4;
+			vr_ps = 2;
+		} else {
+			ps = 2;	/* otherwise the pixel size is 2 byte */
+		}
+	} else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
+		ps = 4;
+	} else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
+		ps = 3;
+	}
+	vout->ps = ps;
+	vout->vr_ps = vr_ps;
+
+	if (is_rotation_enabled(vout)) {
+		line_length = MAX_PIXELS_PER_LINE;
+		ctop = (pix->height - crop->height) - crop->top;
+		cleft = (pix->width - crop->width) - crop->left;
+	} else {
+		line_length = pix->width;
+	}
+	vout->line_length = line_length;
+	switch (rotation) {
+	case dss_rotation_90_degree:
+		offset = vout->vrfb_context[0].yoffset *
+			vout->vrfb_context[0].bytespp;
+		temp_ps = ps / vr_ps;
+		if (mirroring == 0) {
+			*cropped_offset = offset + line_length *
+				temp_ps * cleft + crop->top * temp_ps;
+		} else {
+			*cropped_offset = offset + line_length * temp_ps *
+				cleft + crop->top * temp_ps + (line_length *
+				((crop->width / (vr_ps)) - 1) * ps);
+		}
+		break;
+	case dss_rotation_180_degree:
+		offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
+			vout->vrfb_context[0].bytespp) +
+			(vout->vrfb_context[0].xoffset *
+			vout->vrfb_context[0].bytespp));
+		if (mirroring == 0) {
+			*cropped_offset = offset + (line_length * ps * ctop) +
+				(cleft / vr_ps) * ps;
+
+		} else {
+			*cropped_offset = offset + (line_length * ps * ctop) +
+				(cleft / vr_ps) * ps + (line_length *
+				(crop->height - 1) * ps);
+		}
+		break;
+	case dss_rotation_270_degree:
+		offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
+			vout->vrfb_context[0].bytespp;
+		temp_ps = ps / vr_ps;
+		if (mirroring == 0) {
+			*cropped_offset = offset + line_length *
+			    temp_ps * crop->left + ctop * ps;
+		} else {
+			*cropped_offset = offset + line_length *
+				temp_ps * crop->left + ctop * ps +
+				(line_length * ((crop->width / vr_ps) - 1) *
+				 ps);
+		}
+		break;
+	case dss_rotation_0_degree:
+		if (mirroring == 0) {
+			*cropped_offset = (line_length * ps) *
+				crop->top + (crop->left / vr_ps) * ps;
+		} else {
+			*cropped_offset = (line_length * ps) *
+				crop->top + (crop->left / vr_ps) * ps +
+				(line_length * (crop->height - 1) * ps);
+		}
+		break;
+	default:
+		*cropped_offset = (line_length * ps * crop->top) /
+			vr_ps + (crop->left * ps) / vr_ps +
+			((crop->width / vr_ps) - 1) * ps;
+		break;
+	}
+}
diff --git a/drivers/media/video/omap/omap_vout_vrfb.h b/drivers/media/video/omap/omap_vout_vrfb.h
new file mode 100644
index 0000000..ffde741
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.h
@@ -0,0 +1,40 @@
+/*
+ * omap_vout_vrfb.h
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OMAP_VOUT_VRFB_H
+#define OMAP_VOUT_VRFB_H
+
+#ifdef CONFIG_VIDEO_OMAP2_VOUT_VRFB
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout);
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+			u32 static_vrfb_allocation);
+void omap_vout_release_vrfb(struct omap_vout_device *vout);
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+			unsigned int *count, unsigned int startindex);
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+			struct videobuf_buffer *vb);
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout);
+#else
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+			u32 static_vrfb_allocation)
+		{ return 0; }
+void omap_vout_release_vrfb(struct omap_vout_device *vout) { }
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+			unsigned int *count, unsigned int startindex)
+		{ return 0; }
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+			struct videobuf_buffer *vb)
+		{ return 0; }
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }
+#endif
+
+#endif
diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h
index 659497b..d793501 100644
--- a/drivers/media/video/omap/omap_voutdef.h
+++ b/drivers/media/video/omap/omap_voutdef.h
@@ -12,6 +12,7 @@
 #define OMAP_VOUTDEF_H
 
 #include <video/omapdss.h>
+#include <plat/vrfb.h>
 
 #define YUYV_BPP        2
 #define RGB565_BPP      2
@@ -27,6 +28,31 @@
 #define MAX_DISPLAYS	3
 #define MAX_MANAGERS	3
 
+#define QQVGA_WIDTH		160
+#define QQVGA_HEIGHT		120
+
+/* Max Resolution supported by the driver */
+#define VID_MAX_WIDTH		1280	/* Largest width */
+#define VID_MAX_HEIGHT		720	/* Largest height */
+
+/* Mimimum requirement is 2x2 for DSS */
+#define VID_MIN_WIDTH		2
+#define VID_MIN_HEIGHT		2
+
+/* 2048 x 2048 is max res supported by OMAP display controller */
+#define MAX_PIXELS_PER_LINE     2048
+
+#define VRFB_TX_TIMEOUT         1000
+#define VRFB_NUM_BUFS		4
+
+/* Max buffer size tobe allocated during init */
+#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
+
+enum dma_channel_state {
+	DMA_CHAN_NOT_ALLOTED,
+	DMA_CHAN_ALLOTED,
+};
+
 /* Enum for Rotation
  * DSS understands rotation in 0, 1, 2, 3 context
  * while V4L2 driver understands it as 0, 90, 180, 270
@@ -37,6 +63,18 @@
 	dss_rotation_180_degree	= 2,
 	dss_rotation_270_degree = 3,
 };
+
+/* Enum for choosing rotation type for vout
+ * DSS2 doesn't understand no rotation as an
+ * option while V4L2 driver doesn't support
+ * rotation in the case where VRFB is not built in
+ * the kernel
+ */
+enum vout_rotaion_type {
+	VOUT_ROT_NONE	= 0,
+	VOUT_ROT_VRFB	= 1,
+};
+
 /*
  * This structure is used to store the DMA transfer parameters
  * for VRFB hidden buffer
@@ -53,6 +91,7 @@
 	int id;
 	int num_overlays;
 	struct omap_overlay *overlays[MAX_OVLS];
+	enum vout_rotaion_type rotation_type;
 };
 
 struct omap2video_device {
@@ -144,4 +183,43 @@
 	int io_allowed;
 
 };
+
+/*
+ * Return true if rotation is 90 or 270
+ */
+static inline int is_rotation_90_or_270(const struct omap_vout_device *vout)
+{
+	return (vout->rotation == dss_rotation_90_degree ||
+			vout->rotation == dss_rotation_270_degree);
+}
+
+/*
+ * Return true if rotation is enabled
+ */
+static inline int is_rotation_enabled(const struct omap_vout_device *vout)
+{
+	return vout->rotation || vout->mirror;
+}
+
+/*
+ * Reverse the rotation degree if mirroring is enabled
+ */
+static inline int calc_rotation(const struct omap_vout_device *vout)
+{
+	if (!vout->mirror)
+		return vout->rotation;
+
+	switch (vout->rotation) {
+	case dss_rotation_90_degree:
+		return dss_rotation_270_degree;
+	case dss_rotation_270_degree:
+		return dss_rotation_90_degree;
+	case dss_rotation_180_degree:
+		return dss_rotation_0_degree;
+	default:
+		return dss_rotation_180_degree;
+	}
+}
+
+void omap_vout_free_buffers(struct omap_vout_device *vout);
 #endif	/* ifndef OMAP_VOUTDEF_H */
diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c
index 8ae7481..115408b 100644
--- a/drivers/media/video/omap/omap_voutlib.c
+++ b/drivers/media/video/omap/omap_voutlib.c
@@ -24,8 +24,12 @@
 #include <linux/types.h>
 #include <linux/videodev2.h>
 
+#include <linux/dma-mapping.h>
+
 #include <plat/cpu.h>
 
+#include "omap_voutlib.h"
+
 MODULE_AUTHOR("Texas Instruments");
 MODULE_DESCRIPTION("OMAP Video library");
 MODULE_LICENSE("GPL");
@@ -291,3 +295,45 @@
 }
 EXPORT_SYMBOL_GPL(omap_vout_new_format);
 
+/*
+ * Allocate buffers
+ */
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
+{
+	u32 order, size;
+	unsigned long virt_addr, addr;
+
+	size = PAGE_ALIGN(buf_size);
+	order = get_order(size);
+	virt_addr = __get_free_pages(GFP_KERNEL, order);
+	addr = virt_addr;
+
+	if (virt_addr) {
+		while (size > 0) {
+			SetPageReserved(virt_to_page(addr));
+			addr += PAGE_SIZE;
+			size -= PAGE_SIZE;
+		}
+	}
+	*phys_addr = (u32) virt_to_phys((void *) virt_addr);
+	return virt_addr;
+}
+
+/*
+ * Free buffers
+ */
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
+{
+	u32 order, size;
+	unsigned long addr = virtaddr;
+
+	size = PAGE_ALIGN(buf_size);
+	order = get_order(size);
+
+	while (size > 0) {
+		ClearPageReserved(virt_to_page(addr));
+		addr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+	free_pages((unsigned long) virtaddr, order);
+}
diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h
index a60b16e..e51750a 100644
--- a/drivers/media/video/omap/omap_voutlib.h
+++ b/drivers/media/video/omap/omap_voutlib.h
@@ -12,23 +12,25 @@
 #ifndef OMAP_VOUTLIB_H
 #define OMAP_VOUTLIB_H
 
-extern void omap_vout_default_crop(struct v4l2_pix_format *pix,
+void omap_vout_default_crop(struct v4l2_pix_format *pix,
 		struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop);
 
-extern int omap_vout_new_crop(struct v4l2_pix_format *pix,
+int omap_vout_new_crop(struct v4l2_pix_format *pix,
 		struct v4l2_rect *crop, struct v4l2_window *win,
 		struct v4l2_framebuffer *fbuf,
 		const struct v4l2_rect *new_crop);
 
-extern int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
+int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
 		struct v4l2_window *new_win);
 
-extern int omap_vout_new_window(struct v4l2_rect *crop,
+int omap_vout_new_window(struct v4l2_rect *crop,
 		struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
 		struct v4l2_window *new_win);
 
-extern void omap_vout_new_format(struct v4l2_pix_format *pix,
+void omap_vout_new_format(struct v4l2_pix_format *pix,
 		struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop,
 		struct v4l2_window *win);
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr);
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size);
 #endif	/* #ifndef OMAP_VOUTLIB_H */
 
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index e7cfc85..8a947e6 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -26,7 +26,6 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 
 #include <media/omap1_camera.h>
 #include <media/soc_camera.h>
@@ -38,7 +37,7 @@
 
 
 #define DRIVER_NAME		"omap1-camera"
-#define VERSION_CODE		KERNEL_VERSION(0, 0, 1)
+#define DRIVER_VERSION		"0.0.2"
 
 
 /*
@@ -208,7 +207,7 @@
 	struct soc_camera_device *icd = vq->priv_data;
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 			icd->current_fmt->host_fmt);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 
 	if (bytes_per_line < 0)
@@ -222,7 +221,7 @@
 	if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
 		*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
 
-	dev_dbg(icd->dev.parent,
+	dev_dbg(icd->parent,
 			"%s: count=%d, size=%d\n", __func__, *count, *size);
 
 	return 0;
@@ -241,7 +240,7 @@
 		videobuf_dma_contig_free(vq, vb);
 	} else {
 		struct soc_camera_device *icd = vq->priv_data;
-		struct device *dev = icd->dev.parent;
+		struct device *dev = icd->parent;
 		struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
 
 		videobuf_dma_unmap(dev, dma);
@@ -258,7 +257,7 @@
 	struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 			icd->current_fmt->host_fmt);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	int ret;
 
@@ -490,7 +489,7 @@
 						struct videobuf_buffer *vb)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	struct omap1_cam_buf *buf;
 	u32 mode;
@@ -519,7 +518,7 @@
 	pcdev->active = buf;
 	pcdev->ready = NULL;
 
-	dev_dbg(icd->dev.parent,
+	dev_dbg(icd->parent,
 		"%s: capture not active, setup FIFO, start DMA\n", __func__);
 	mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
 	mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
@@ -543,8 +542,8 @@
 	struct omap1_cam_buf *buf =
 			container_of(vb, struct omap1_cam_buf, vb);
 	struct soc_camera_device *icd = vq->priv_data;
-	struct device *dev = icd->dev.parent;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct omap1_cam_dev *pcdev = ici->priv;
 
 	switch (vb->state) {
@@ -573,7 +572,7 @@
 {
 	struct omap1_cam_buf *buf = pcdev->active;
 	struct videobuf_buffer *vb;
-	struct device *dev = pcdev->icd->dev.parent;
+	struct device *dev = pcdev->icd->parent;
 
 	if (WARN_ON(!buf)) {
 		suspend_capture(pcdev);
@@ -799,7 +798,7 @@
 static irqreturn_t cam_isr(int irq, void *data)
 {
 	struct omap1_cam_dev *pcdev = data;
-	struct device *dev = pcdev->icd->dev.parent;
+	struct device *dev = pcdev->icd->parent;
 	struct omap1_cam_buf *buf = pcdev->active;
 	u32 it_status;
 	unsigned long flags;
@@ -909,7 +908,7 @@
  */
 static int omap1_cam_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -952,14 +951,14 @@
 
 	pcdev->icd = icd;
 
-	dev_dbg(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n",
+	dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
 			icd->devnum);
 	return 0;
 }
 
 static void omap1_cam_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	u32 ctrlclock;
 
@@ -985,7 +984,7 @@
 
 	pcdev->icd = NULL;
 
-	dev_dbg(icd->dev.parent,
+	dev_dbg(icd->parent,
 		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
 }
 
@@ -1070,7 +1069,7 @@
 		unsigned int idx, struct soc_camera_format_xlate *xlate)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	int formats = 0, ret;
 	enum v4l2_mbus_pixelcode code;
 	const struct soc_mbus_pixelfmt *fmt;
@@ -1222,9 +1221,9 @@
 	struct v4l2_rect *rect = &crop->c;
 	const struct soc_camera_format_xlate *xlate = icd->current_fmt;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct omap1_cam_dev *pcdev = ici->priv;
-	struct device *dev = icd->dev.parent;
 	struct v4l2_mbus_framefmt mf;
 	int ret;
 
@@ -1270,8 +1269,8 @@
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	const struct soc_camera_format_xlate *xlate;
-	struct device *dev = icd->dev.parent;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 	struct v4l2_mbus_framefmt mf;
@@ -1326,7 +1325,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %#x not found\n",
+		dev_warn(icd->parent, "Format %#x not found\n",
 			 pix->pixelformat);
 		return -EINVAL;
 	}
@@ -1362,7 +1361,7 @@
 				  struct vm_area_struct *vma)
 {
 	struct soc_camera_device *icd = q->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	int ret;
 
@@ -1377,17 +1376,17 @@
 static void omap1_cam_init_videobuf(struct videobuf_queue *q,
 				     struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct omap1_cam_dev *pcdev = ici->priv;
 
 	if (!sg_mode)
 		videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
-				icd->dev.parent, &pcdev->lock,
+				icd->parent, &pcdev->lock,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
 				sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
 	else
 		videobuf_queue_sg_init(q, &omap1_videobuf_ops,
-				icd->dev.parent, &pcdev->lock,
+				icd->parent, &pcdev->lock,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
 				sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
 
@@ -1431,7 +1430,6 @@
 {
 	/* cap->name is set by the friendly caller:-> */
 	strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
-	cap->version = VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
@@ -1440,9 +1438,9 @@
 static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
 		__u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct omap1_cam_dev *pcdev = ici->priv;
-	struct device *dev = icd->dev.parent;
 	const struct soc_camera_format_xlate *xlate;
 	const struct soc_mbus_pixelfmt *fmt;
 	unsigned long camera_flags, common_flags;
@@ -1718,4 +1716,5 @@
 MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
 MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
 MODULE_LICENSE("GPL v2");
+MODULE_LICENSE(DRIVER_VERSION);
 MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index 69b60ba..eb97bff 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -31,7 +31,6 @@
 #include <linux/interrupt.h>
 #include <linux/videodev2.h>
 #include <linux/pci.h>		/* needed for videobufs */
-#include <linux/version.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/io.h>
@@ -43,7 +42,7 @@
 
 #include "omap24xxcam.h"
 
-#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+#define OMAP24XXCAM_VERSION "0.0.1"
 
 #define RESET_TIMEOUT_NS 10000
 
@@ -309,11 +308,11 @@
 			order--;
 
 		/* try to allocate as many contiguous pages as possible */
-		page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+		page = alloc_pages(GFP_KERNEL, order);
 		/* if allocation fails, try to allocate smaller amount */
 		while (page == NULL) {
 			order--;
-			page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+			page = alloc_pages(GFP_KERNEL, order);
 			if (page == NULL && !order) {
 				err = -ENOMEM;
 				goto out;
@@ -993,7 +992,6 @@
 
 	strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
 	strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
-	cap->version = OMAP24XXCAM_VERSION;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
@@ -1888,6 +1886,7 @@
 MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
 MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(OMAP24XXCAM_VERSION);
 module_param(video_nr, int, 0);
 MODULE_PARM_DESC(video_nr,
 		 "Minor number for video device (-1 ==> auto assign)");
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 94b6ed8..5cea2bb 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -2234,3 +2234,4 @@
 MODULE_AUTHOR("Nokia Corporation");
 MODULE_DESCRIPTION("TI OMAP3 ISP driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
index 2620c40..529e582 100644
--- a/drivers/media/video/omap3isp/isp.h
+++ b/drivers/media/video/omap3isp/isp.h
@@ -139,6 +139,10 @@
  *		3 - CAMEXT[13:6] -> CAM[7:0]
  * @clk_pol: Pixel clock polarity
  *		0 - Non Inverted, 1 - Inverted
+ * @hs_pol: Horizontal synchronization polarity
+ *		0 - Active high, 1 - Active low
+ * @vs_pol: Vertical synchronization polarity
+ *		0 - Active high, 1 - Active low
  * @bridge: CCDC Bridge input control
  *		ISPCTRL_PAR_BRIDGE_DISABLE - Disable
  *		ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian
@@ -147,6 +151,8 @@
 struct isp_parallel_platform_data {
 	unsigned int data_lane_shift:2;
 	unsigned int clk_pol:1;
+	unsigned int hs_pol:1;
+	unsigned int vs_pol:1;
 	unsigned int bridge:4;
 };
 
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 39d501b..9d3459d 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -1148,6 +1148,8 @@
 	omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
 
 	ccdc->syncif.datsz = depth_out;
+	ccdc->syncif.hdpol = pdata ? pdata->hs_pol : 0;
+	ccdc->syncif.vdpol = pdata ? pdata->vs_pol : 0;
 	ccdc_config_sync_if(ccdc, &ccdc->syncif);
 
 	/* CCDC_PAD_SINK */
@@ -1691,7 +1693,7 @@
 	if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS)
 		return -EINVAL;
 
-	return v4l2_event_subscribe(fh, sub);
+	return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
 }
 
 static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
@@ -2162,7 +2164,6 @@
 	sd->grp_id = 1 << 16;	/* group ID for isp subdevs */
 	v4l2_set_subdevdata(sd, ccdc);
 	sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
-	sd->nevents = OMAP3ISP_CCDC_NEVENTS;
 
 	pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 	pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
@@ -2257,8 +2258,6 @@
 	ccdc->syncif.fldout = 0;
 	ccdc->syncif.fldpol = 0;
 	ccdc->syncif.fldstat = 0;
-	ccdc->syncif.hdpol = 0;
-	ccdc->syncif.vdpol = 0;
 
 	ccdc->clamp.oblen = 0;
 	ccdc->clamp.dcsubval = 0;
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
index 0e16cab..ec9e395 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -30,6 +30,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
 
 #include "isp.h"
 #include "ispreg.h"
@@ -163,6 +164,9 @@
 	struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
 	int i;
 
+	if (enable && ccp2->vdds_csib)
+		regulator_enable(ccp2->vdds_csib);
+
 	/* Enable/Disable all the LCx channels */
 	for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
 		isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i),
@@ -186,6 +190,9 @@
 				    ISPCCP2_LC01_IRQENABLE,
 				    ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
 	}
+
+	if (!enable && ccp2->vdds_csib)
+		regulator_disable(ccp2->vdds_csib);
 }
 
 /*
@@ -1137,6 +1144,9 @@
  */
 void omap3isp_ccp2_cleanup(struct isp_device *isp)
 {
+	struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+
+	regulator_put(ccp2->vdds_csib);
 }
 
 /*
@@ -1151,14 +1161,27 @@
 
 	init_waitqueue_head(&ccp2->wait);
 
-	/* On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
+	/*
+	 * On the OMAP34xx the CSI1 receiver is operated in the CSIb IO
+	 * complex, which is powered by vdds_csib power rail. Hence the
+	 * request for the regulator.
+	 *
+	 * On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
 	 * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly
 	 * configured.
 	 *
 	 * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c).
 	 */
-	if (isp->revision == ISP_REVISION_15_0)
+	if (isp->revision == ISP_REVISION_2_0) {
+		ccp2->vdds_csib = regulator_get(isp->dev, "vdds_csib");
+		if (IS_ERR(ccp2->vdds_csib)) {
+			dev_dbg(isp->dev,
+				"Could not get regulator vdds_csib\n");
+			ccp2->vdds_csib = NULL;
+		}
+	} else if (isp->revision == ISP_REVISION_15_0) {
 		ccp2->phy = &isp->isp_csiphy1;
+	}
 
 	ret = ccp2_init_entities(ccp2);
 	if (ret < 0)
diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/video/omap3isp/ispccp2.h
index 5505a86..6674e9d 100644
--- a/drivers/media/video/omap3isp/ispccp2.h
+++ b/drivers/media/video/omap3isp/ispccp2.h
@@ -81,6 +81,7 @@
 	struct isp_interface_mem_config mem_cfg;
 	struct isp_video video_in;
 	struct isp_csiphy *phy;
+	struct regulator *vdds_csib;
 	unsigned int error;
 	enum isp_pipeline_stream_state state;
 	wait_queue_head_t wait;
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
index b44cb685..8080659 100644
--- a/drivers/media/video/omap3isp/ispstat.c
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -1032,7 +1032,6 @@
 	snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
 	subdev->grp_id = 1 << 16;	/* group ID for isp subdevs */
 	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
-	subdev->nevents = STAT_NEVENTS;
 	v4l2_set_subdevdata(subdev, stat);
 
 	stat->pad.flags = MEDIA_PAD_FL_SINK;
@@ -1050,7 +1049,7 @@
 	if (sub->type != stat->event_type)
 		return -EINVAL;
 
-	return v4l2_event_subscribe(fh, sub);
+	return v4l2_event_subscribe(fh, sub, STAT_NEVENTS);
 }
 
 int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index 9cd8f1a..fd965ad 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -695,7 +695,6 @@
 	strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
 	strlcpy(cap->card, video->video.name, sizeof(cap->card));
 	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
-	cap->version = ISP_VIDEO_DRIVER_VERSION;
 
 	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index 911bea6..53160aa 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -27,7 +27,6 @@
 #define OMAP3_ISP_VIDEO_H
 
 #include <linux/v4l2-mediabus.h>
-#include <linux/version.h>
 #include <media/media-entity.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
@@ -35,7 +34,7 @@
 #include "ispqueue.h"
 
 #define ISP_VIDEO_DRIVER_NAME		"ispvideo"
-#define ISP_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
+#define ISP_VIDEO_DRIVER_VERSION	"0.0.2"
 
 struct isp_device;
 struct isp_video;
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index 0cea0cf..9ce2fa0 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -1031,16 +1031,9 @@
 	const char *devname;
 	int ret;
 
-	/*
-	 * we must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
-		dev_err(&client->dev, "Parent missing or invalid!\n");
-		ret = -ENODEV;
-		goto err;
-	}
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * check and show product ID and manufacturer ID
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
new file mode 100644
index 0000000..349a4ad
--- /dev/null
+++ b/drivers/media/video/ov5642.c
@@ -0,0 +1,1012 @@
+/*
+ * Driver for OV5642 CMOS Image Sensor from Omnivision
+ *
+ * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com>
+ *
+ * Based on Sony IMX074 Camera Driver
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on Omnivision OV7670 Camera Driver
+ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/* OV5642 registers */
+#define REG_CHIP_ID_HIGH		0x300a
+#define REG_CHIP_ID_LOW			0x300b
+
+#define REG_WINDOW_START_X_HIGH		0x3800
+#define REG_WINDOW_START_X_LOW		0x3801
+#define REG_WINDOW_START_Y_HIGH		0x3802
+#define REG_WINDOW_START_Y_LOW		0x3803
+#define REG_WINDOW_WIDTH_HIGH		0x3804
+#define REG_WINDOW_WIDTH_LOW		0x3805
+#define REG_WINDOW_HEIGHT_HIGH 		0x3806
+#define REG_WINDOW_HEIGHT_LOW		0x3807
+#define REG_OUT_WIDTH_HIGH		0x3808
+#define REG_OUT_WIDTH_LOW		0x3809
+#define REG_OUT_HEIGHT_HIGH		0x380a
+#define REG_OUT_HEIGHT_LOW		0x380b
+#define REG_OUT_TOTAL_WIDTH_HIGH	0x380c
+#define REG_OUT_TOTAL_WIDTH_LOW		0x380d
+#define REG_OUT_TOTAL_HEIGHT_HIGH	0x380e
+#define REG_OUT_TOTAL_HEIGHT_LOW	0x380f
+
+/*
+ * define standard resolution.
+ * Works currently only for up to 720 lines
+ * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720
+ */
+
+#define OV5642_WIDTH		1280
+#define OV5642_HEIGHT		720
+#define OV5642_TOTAL_WIDTH	3200
+#define OV5642_TOTAL_HEIGHT	2000
+#define OV5642_SENSOR_SIZE_X	2592
+#define OV5642_SENSOR_SIZE_Y	1944
+
+struct regval_list {
+	u16 reg_num;
+	u8 value;
+};
+
+static struct regval_list ov5642_default_regs_init[] = {
+	{ 0x3103, 0x93 },
+	{ 0x3008, 0x82 },
+	{ 0x3017, 0x7f },
+	{ 0x3018, 0xfc },
+	{ 0x3810, 0xc2 },
+	{ 0x3615, 0xf0 },
+	{ 0x3000, 0x0  },
+	{ 0x3001, 0x0  },
+	{ 0x3002, 0x0  },
+	{ 0x3003, 0x0  },
+	{ 0x3004, 0xff },
+	{ 0x3030, 0x2b },
+	{ 0x3011, 0x8  },
+	{ 0x3010, 0x10 },
+	{ 0x3604, 0x60 },
+	{ 0x3622, 0x60 },
+	{ 0x3621, 0x9  },
+	{ 0x3709, 0x0  },
+	{ 0x4000, 0x21 },
+	{ 0x401d, 0x22 },
+	{ 0x3600, 0x54 },
+	{ 0x3605, 0x4  },
+	{ 0x3606, 0x3f },
+	{ 0x3c01, 0x80 },
+	{ 0x300d, 0x22 },
+	{ 0x3623, 0x22 },
+	{ 0x5000, 0x4f },
+	{ 0x5020, 0x4  },
+	{ 0x5181, 0x79 },
+	{ 0x5182, 0x0  },
+	{ 0x5185, 0x22 },
+	{ 0x5197, 0x1  },
+	{ 0x5500, 0xa  },
+	{ 0x5504, 0x0  },
+	{ 0x5505, 0x7f },
+	{ 0x5080, 0x8  },
+	{ 0x300e, 0x18 },
+	{ 0x4610, 0x0  },
+	{ 0x471d, 0x5  },
+	{ 0x4708, 0x6  },
+	{ 0x370c, 0xa0 },
+	{ 0x5687, 0x94 },
+	{ 0x501f, 0x0  },
+	{ 0x5000, 0x4f },
+	{ 0x5001, 0xcf },
+	{ 0x4300, 0x30 },
+	{ 0x4300, 0x30 },
+	{ 0x460b, 0x35 },
+	{ 0x471d, 0x0  },
+	{ 0x3002, 0xc  },
+	{ 0x3002, 0x0  },
+	{ 0x4713, 0x3  },
+	{ 0x471c, 0x50 },
+	{ 0x4721, 0x2  },
+	{ 0x4402, 0x90 },
+	{ 0x460c, 0x22 },
+	{ 0x3815, 0x44 },
+	{ 0x3503, 0x7  },
+	{ 0x3501, 0x73 },
+	{ 0x3502, 0x80 },
+	{ 0x350b, 0x0  },
+	{ 0x3818, 0xc8 },
+	{ 0x3824, 0x11 },
+	{ 0x3a00, 0x78 },
+	{ 0x3a1a, 0x4  },
+	{ 0x3a13, 0x30 },
+	{ 0x3a18, 0x0  },
+	{ 0x3a19, 0x7c },
+	{ 0x3a08, 0x12 },
+	{ 0x3a09, 0xc0 },
+	{ 0x3a0a, 0xf  },
+	{ 0x3a0b, 0xa0 },
+	{ 0x350c, 0x7  },
+	{ 0x350d, 0xd0 },
+	{ 0x3a0d, 0x8  },
+	{ 0x3a0e, 0x6  },
+	{ 0x3500, 0x0  },
+	{ 0x3501, 0x0  },
+	{ 0x3502, 0x0  },
+	{ 0x350a, 0x0  },
+	{ 0x350b, 0x0  },
+	{ 0x3503, 0x0  },
+	{ 0x3a0f, 0x3c },
+	{ 0x3a10, 0x32 },
+	{ 0x3a1b, 0x3c },
+	{ 0x3a1e, 0x32 },
+	{ 0x3a11, 0x80 },
+	{ 0x3a1f, 0x20 },
+	{ 0x3030, 0x2b },
+	{ 0x3a02, 0x0  },
+	{ 0x3a03, 0x7d },
+	{ 0x3a04, 0x0  },
+	{ 0x3a14, 0x0  },
+	{ 0x3a15, 0x7d },
+	{ 0x3a16, 0x0  },
+	{ 0x3a00, 0x78 },
+	{ 0x3a08, 0x9  },
+	{ 0x3a09, 0x60 },
+	{ 0x3a0a, 0x7  },
+	{ 0x3a0b, 0xd0 },
+	{ 0x3a0d, 0x10 },
+	{ 0x3a0e, 0xd  },
+	{ 0x4407, 0x4  },
+	{ 0x5193, 0x70 },
+	{ 0x589b, 0x0  },
+	{ 0x589a, 0xc0 },
+	{ 0x401e, 0x20 },
+	{ 0x4001, 0x42 },
+	{ 0x401c, 0x6  },
+	{ 0x3825, 0xac },
+	{ 0x3827, 0xc  },
+	{ 0x528a, 0x1  },
+	{ 0x528b, 0x4  },
+	{ 0x528c, 0x8  },
+	{ 0x528d, 0x10 },
+	{ 0x528e, 0x20 },
+	{ 0x528f, 0x28 },
+	{ 0x5290, 0x30 },
+	{ 0x5292, 0x0  },
+	{ 0x5293, 0x1  },
+	{ 0x5294, 0x0  },
+	{ 0x5295, 0x4  },
+	{ 0x5296, 0x0  },
+	{ 0x5297, 0x8  },
+	{ 0x5298, 0x0  },
+	{ 0x5299, 0x10 },
+	{ 0x529a, 0x0  },
+	{ 0x529b, 0x20 },
+	{ 0x529c, 0x0  },
+	{ 0x529d, 0x28 },
+	{ 0x529e, 0x0  },
+	{ 0x529f, 0x30 },
+	{ 0x5282, 0x0  },
+	{ 0x5300, 0x0  },
+	{ 0x5301, 0x20 },
+	{ 0x5302, 0x0  },
+	{ 0x5303, 0x7c },
+	{ 0x530c, 0x0  },
+	{ 0x530d, 0xc  },
+	{ 0x530e, 0x20 },
+	{ 0x530f, 0x80 },
+	{ 0x5310, 0x20 },
+	{ 0x5311, 0x80 },
+	{ 0x5308, 0x20 },
+	{ 0x5309, 0x40 },
+	{ 0x5304, 0x0  },
+	{ 0x5305, 0x30 },
+	{ 0x5306, 0x0  },
+	{ 0x5307, 0x80 },
+	{ 0x5314, 0x8  },
+	{ 0x5315, 0x20 },
+	{ 0x5319, 0x30 },
+	{ 0x5316, 0x10 },
+	{ 0x5317, 0x0  },
+	{ 0x5318, 0x2  },
+	{ 0x5380, 0x1  },
+	{ 0x5381, 0x0  },
+	{ 0x5382, 0x0  },
+	{ 0x5383, 0x4e },
+	{ 0x5384, 0x0  },
+	{ 0x5385, 0xf  },
+	{ 0x5386, 0x0  },
+	{ 0x5387, 0x0  },
+	{ 0x5388, 0x1  },
+	{ 0x5389, 0x15 },
+	{ 0x538a, 0x0  },
+	{ 0x538b, 0x31 },
+	{ 0x538c, 0x0  },
+	{ 0x538d, 0x0  },
+	{ 0x538e, 0x0  },
+	{ 0x538f, 0xf  },
+	{ 0x5390, 0x0  },
+	{ 0x5391, 0xab },
+	{ 0x5392, 0x0  },
+	{ 0x5393, 0xa2 },
+	{ 0x5394, 0x8  },
+	{ 0x5480, 0x14 },
+	{ 0x5481, 0x21 },
+	{ 0x5482, 0x36 },
+	{ 0x5483, 0x57 },
+	{ 0x5484, 0x65 },
+	{ 0x5485, 0x71 },
+	{ 0x5486, 0x7d },
+	{ 0x5487, 0x87 },
+	{ 0x5488, 0x91 },
+	{ 0x5489, 0x9a },
+	{ 0x548a, 0xaa },
+	{ 0x548b, 0xb8 },
+	{ 0x548c, 0xcd },
+	{ 0x548d, 0xdd },
+	{ 0x548e, 0xea },
+	{ 0x548f, 0x1d },
+	{ 0x5490, 0x5  },
+	{ 0x5491, 0x0  },
+	{ 0x5492, 0x4  },
+	{ 0x5493, 0x20 },
+	{ 0x5494, 0x3  },
+	{ 0x5495, 0x60 },
+	{ 0x5496, 0x2  },
+	{ 0x5497, 0xb8 },
+	{ 0x5498, 0x2  },
+	{ 0x5499, 0x86 },
+	{ 0x549a, 0x2  },
+	{ 0x549b, 0x5b },
+	{ 0x549c, 0x2  },
+	{ 0x549d, 0x3b },
+	{ 0x549e, 0x2  },
+	{ 0x549f, 0x1c },
+	{ 0x54a0, 0x2  },
+	{ 0x54a1, 0x4  },
+	{ 0x54a2, 0x1  },
+	{ 0x54a3, 0xed },
+	{ 0x54a4, 0x1  },
+	{ 0x54a5, 0xc5 },
+	{ 0x54a6, 0x1  },
+	{ 0x54a7, 0xa5 },
+	{ 0x54a8, 0x1  },
+	{ 0x54a9, 0x6c },
+	{ 0x54aa, 0x1  },
+	{ 0x54ab, 0x41 },
+	{ 0x54ac, 0x1  },
+	{ 0x54ad, 0x20 },
+	{ 0x54ae, 0x0  },
+	{ 0x54af, 0x16 },
+	{ 0x54b0, 0x1  },
+	{ 0x54b1, 0x20 },
+	{ 0x54b2, 0x0  },
+	{ 0x54b3, 0x10 },
+	{ 0x54b4, 0x0  },
+	{ 0x54b5, 0xf0 },
+	{ 0x54b6, 0x0  },
+	{ 0x54b7, 0xdf },
+	{ 0x5402, 0x3f },
+	{ 0x5403, 0x0  },
+	{ 0x3406, 0x0  },
+	{ 0x5180, 0xff },
+	{ 0x5181, 0x52 },
+	{ 0x5182, 0x11 },
+	{ 0x5183, 0x14 },
+	{ 0x5184, 0x25 },
+	{ 0x5185, 0x24 },
+	{ 0x5186, 0x6  },
+	{ 0x5187, 0x8  },
+	{ 0x5188, 0x8  },
+	{ 0x5189, 0x7c },
+	{ 0x518a, 0x60 },
+	{ 0x518b, 0xb2 },
+	{ 0x518c, 0xb2 },
+	{ 0x518d, 0x44 },
+	{ 0x518e, 0x3d },
+	{ 0x518f, 0x58 },
+	{ 0x5190, 0x46 },
+	{ 0x5191, 0xf8 },
+	{ 0x5192, 0x4  },
+	{ 0x5193, 0x70 },
+	{ 0x5194, 0xf0 },
+	{ 0x5195, 0xf0 },
+	{ 0x5196, 0x3  },
+	{ 0x5197, 0x1  },
+	{ 0x5198, 0x4  },
+	{ 0x5199, 0x12 },
+	{ 0x519a, 0x4  },
+	{ 0x519b, 0x0  },
+	{ 0x519c, 0x6  },
+	{ 0x519d, 0x82 },
+	{ 0x519e, 0x0  },
+	{ 0x5025, 0x80 },
+	{ 0x3a0f, 0x38 },
+	{ 0x3a10, 0x30 },
+	{ 0x3a1b, 0x3a },
+	{ 0x3a1e, 0x2e },
+	{ 0x3a11, 0x60 },
+	{ 0x3a1f, 0x10 },
+	{ 0x5688, 0xa6 },
+	{ 0x5689, 0x6a },
+	{ 0x568a, 0xea },
+	{ 0x568b, 0xae },
+	{ 0x568c, 0xa6 },
+	{ 0x568d, 0x6a },
+	{ 0x568e, 0x62 },
+	{ 0x568f, 0x26 },
+	{ 0x5583, 0x40 },
+	{ 0x5584, 0x40 },
+	{ 0x5580, 0x2  },
+	{ 0x5000, 0xcf },
+	{ 0x5800, 0x27 },
+	{ 0x5801, 0x19 },
+	{ 0x5802, 0x12 },
+	{ 0x5803, 0xf  },
+	{ 0x5804, 0x10 },
+	{ 0x5805, 0x15 },
+	{ 0x5806, 0x1e },
+	{ 0x5807, 0x2f },
+	{ 0x5808, 0x15 },
+	{ 0x5809, 0xd  },
+	{ 0x580a, 0xa  },
+	{ 0x580b, 0x9  },
+	{ 0x580c, 0xa  },
+	{ 0x580d, 0xc  },
+	{ 0x580e, 0x12 },
+	{ 0x580f, 0x19 },
+	{ 0x5810, 0xb  },
+	{ 0x5811, 0x7  },
+	{ 0x5812, 0x4  },
+	{ 0x5813, 0x3  },
+	{ 0x5814, 0x3  },
+	{ 0x5815, 0x6  },
+	{ 0x5816, 0xa  },
+	{ 0x5817, 0xf  },
+	{ 0x5818, 0xa  },
+	{ 0x5819, 0x5  },
+	{ 0x581a, 0x1  },
+	{ 0x581b, 0x0  },
+	{ 0x581c, 0x0  },
+	{ 0x581d, 0x3  },
+	{ 0x581e, 0x8  },
+	{ 0x581f, 0xc  },
+	{ 0x5820, 0xa  },
+	{ 0x5821, 0x5  },
+	{ 0x5822, 0x1  },
+	{ 0x5823, 0x0  },
+	{ 0x5824, 0x0  },
+	{ 0x5825, 0x3  },
+	{ 0x5826, 0x8  },
+	{ 0x5827, 0xc  },
+	{ 0x5828, 0xe  },
+	{ 0x5829, 0x8  },
+	{ 0x582a, 0x6  },
+	{ 0x582b, 0x4  },
+	{ 0x582c, 0x5  },
+	{ 0x582d, 0x7  },
+	{ 0x582e, 0xb  },
+	{ 0x582f, 0x12 },
+	{ 0x5830, 0x18 },
+	{ 0x5831, 0x10 },
+	{ 0x5832, 0xc  },
+	{ 0x5833, 0xa  },
+	{ 0x5834, 0xb  },
+	{ 0x5835, 0xe  },
+	{ 0x5836, 0x15 },
+	{ 0x5837, 0x19 },
+	{ 0x5838, 0x32 },
+	{ 0x5839, 0x1f },
+	{ 0x583a, 0x18 },
+	{ 0x583b, 0x16 },
+	{ 0x583c, 0x17 },
+	{ 0x583d, 0x1e },
+	{ 0x583e, 0x26 },
+	{ 0x583f, 0x53 },
+	{ 0x5840, 0x10 },
+	{ 0x5841, 0xf  },
+	{ 0x5842, 0xd  },
+	{ 0x5843, 0xc  },
+	{ 0x5844, 0xe  },
+	{ 0x5845, 0x9  },
+	{ 0x5846, 0x11 },
+	{ 0x5847, 0x10 },
+	{ 0x5848, 0x10 },
+	{ 0x5849, 0x10 },
+	{ 0x584a, 0x10 },
+	{ 0x584b, 0xe  },
+	{ 0x584c, 0x10 },
+	{ 0x584d, 0x10 },
+	{ 0x584e, 0x11 },
+	{ 0x584f, 0x10 },
+	{ 0x5850, 0xf  },
+	{ 0x5851, 0xc  },
+	{ 0x5852, 0xf  },
+	{ 0x5853, 0x10 },
+	{ 0x5854, 0x10 },
+	{ 0x5855, 0xf  },
+	{ 0x5856, 0xe  },
+	{ 0x5857, 0xb  },
+	{ 0x5858, 0x10 },
+	{ 0x5859, 0xd  },
+	{ 0x585a, 0xd  },
+	{ 0x585b, 0xc  },
+	{ 0x585c, 0xc  },
+	{ 0x585d, 0xc  },
+	{ 0x585e, 0xb  },
+	{ 0x585f, 0xc  },
+	{ 0x5860, 0xc  },
+	{ 0x5861, 0xc  },
+	{ 0x5862, 0xd  },
+	{ 0x5863, 0x8  },
+	{ 0x5864, 0x11 },
+	{ 0x5865, 0x18 },
+	{ 0x5866, 0x18 },
+	{ 0x5867, 0x19 },
+	{ 0x5868, 0x17 },
+	{ 0x5869, 0x19 },
+	{ 0x586a, 0x16 },
+	{ 0x586b, 0x13 },
+	{ 0x586c, 0x13 },
+	{ 0x586d, 0x12 },
+	{ 0x586e, 0x13 },
+	{ 0x586f, 0x16 },
+	{ 0x5870, 0x14 },
+	{ 0x5871, 0x12 },
+	{ 0x5872, 0x10 },
+	{ 0x5873, 0x11 },
+	{ 0x5874, 0x11 },
+	{ 0x5875, 0x16 },
+	{ 0x5876, 0x14 },
+	{ 0x5877, 0x11 },
+	{ 0x5878, 0x10 },
+	{ 0x5879, 0xf  },
+	{ 0x587a, 0x10 },
+	{ 0x587b, 0x14 },
+	{ 0x587c, 0x13 },
+	{ 0x587d, 0x12 },
+	{ 0x587e, 0x11 },
+	{ 0x587f, 0x11 },
+	{ 0x5880, 0x12 },
+	{ 0x5881, 0x15 },
+	{ 0x5882, 0x14 },
+	{ 0x5883, 0x15 },
+	{ 0x5884, 0x15 },
+	{ 0x5885, 0x15 },
+	{ 0x5886, 0x13 },
+	{ 0x5887, 0x17 },
+	{ 0x3710, 0x10 },
+	{ 0x3632, 0x51 },
+	{ 0x3702, 0x10 },
+	{ 0x3703, 0xb2 },
+	{ 0x3704, 0x18 },
+	{ 0x370b, 0x40 },
+	{ 0x370d, 0x3  },
+	{ 0x3631, 0x1  },
+	{ 0x3632, 0x52 },
+	{ 0x3606, 0x24 },
+	{ 0x3620, 0x96 },
+	{ 0x5785, 0x7  },
+	{ 0x3a13, 0x30 },
+	{ 0x3600, 0x52 },
+	{ 0x3604, 0x48 },
+	{ 0x3606, 0x1b },
+	{ 0x370d, 0xb  },
+	{ 0x370f, 0xc0 },
+	{ 0x3709, 0x1  },
+	{ 0x3823, 0x0  },
+	{ 0x5007, 0x0  },
+	{ 0x5009, 0x0  },
+	{ 0x5011, 0x0  },
+	{ 0x5013, 0x0  },
+	{ 0x519e, 0x0  },
+	{ 0x5086, 0x0  },
+	{ 0x5087, 0x0  },
+	{ 0x5088, 0x0  },
+	{ 0x5089, 0x0  },
+	{ 0x302b, 0x0  },
+	{ 0x3503, 0x7  },
+	{ 0x3011, 0x8  },
+	{ 0x350c, 0x2  },
+	{ 0x350d, 0xe4 },
+	{ 0x3621, 0xc9 },
+	{ 0x370a, 0x81 },
+	{ 0xffff, 0xff },
+};
+
+static struct regval_list ov5642_default_regs_finalise[] = {
+	{ 0x3810, 0xc2 },
+	{ 0x3818, 0xc9 },
+	{ 0x381c, 0x10 },
+	{ 0x381d, 0xa0 },
+	{ 0x381e, 0x5  },
+	{ 0x381f, 0xb0 },
+	{ 0x3820, 0x0  },
+	{ 0x3821, 0x0  },
+	{ 0x3824, 0x11 },
+	{ 0x3a08, 0x1b },
+	{ 0x3a09, 0xc0 },
+	{ 0x3a0a, 0x17 },
+	{ 0x3a0b, 0x20 },
+	{ 0x3a0d, 0x2  },
+	{ 0x3a0e, 0x1  },
+	{ 0x401c, 0x4  },
+	{ 0x5682, 0x5  },
+	{ 0x5683, 0x0  },
+	{ 0x5686, 0x2  },
+	{ 0x5687, 0xcc },
+	{ 0x5001, 0x4f },
+	{ 0x589b, 0x6  },
+	{ 0x589a, 0xc5 },
+	{ 0x3503, 0x0  },
+	{ 0x460c, 0x20 },
+	{ 0x460b, 0x37 },
+	{ 0x471c, 0xd0 },
+	{ 0x471d, 0x5  },
+	{ 0x3815, 0x1  },
+	{ 0x3818, 0xc1 },
+	{ 0x501f, 0x0  },
+	{ 0x5002, 0xe0 },
+	{ 0x4300, 0x32 }, /* UYVY */
+	{ 0x3002, 0x1c },
+	{ 0x4800, 0x14 },
+	{ 0x4801, 0xf  },
+	{ 0x3007, 0x3b },
+	{ 0x300e, 0x4  },
+	{ 0x4803, 0x50 },
+	{ 0x3815, 0x1  },
+	{ 0x4713, 0x2  },
+	{ 0x4842, 0x1  },
+	{ 0x300f, 0xe  },
+	{ 0x3003, 0x3  },
+	{ 0x3003, 0x1  },
+	{ 0xffff, 0xff },
+};
+
+struct ov5642_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+struct ov5642 {
+	struct v4l2_subdev		subdev;
+	const struct ov5642_datafmt	*fmt;
+};
+
+static const struct ov5642_datafmt ov5642_colour_fmts[] = {
+	{V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5642 *to_ov5642(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5642_datafmt
+			*ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++)
+		if (ov5642_colour_fmts[i].code == code)
+			return ov5642_colour_fmts + i;
+
+	return NULL;
+}
+
+static int reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret;
+	/* We have 16-bit i2c addresses - care for endianess */
+	unsigned char data[2] = { reg >> 8, reg & 0xff };
+
+	ret = i2c_master_send(client, data, 2);
+	if (ret < 2) {
+		dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+			__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	ret = i2c_master_recv(client, val, 1);
+	if (ret < 1) {
+		dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+				__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+	return 0;
+}
+
+static int reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { reg >> 8, reg & 0xff, val };
+
+	ret = i2c_master_send(client, data, 3);
+	if (ret < 3) {
+		dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
+			__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	return 0;
+}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val;
+
+	if (reg->reg & ~0xffff)
+		return -EINVAL;
+
+	reg->size = 1;
+
+	ret = reg_read(client, reg->reg, &val);
+	if (!ret)
+		reg->val = (__u64)val;
+
+	return ret;
+}
+
+static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (reg->reg & ~0xffff || reg->val & ~0xff)
+		return -EINVAL;
+
+	return reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov5642_write_array(struct i2c_client *client,
+				struct regval_list *vals)
+{
+	while (vals->reg_num != 0xffff || vals->value != 0xff) {
+		int ret = reg_write(client, vals->reg_num, vals->value);
+		if (ret < 0)
+			return ret;
+		vals++;
+	}
+	dev_dbg(&client->dev, "Register list loaded\n");
+	return 0;
+}
+
+static int ov5642_set_resolution(struct i2c_client *client)
+{
+	int ret;
+	u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8;
+	u8 start_x_low  = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff;
+	u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8;
+	u8 start_y_low  = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff;
+
+	u8 width_high	= OV5642_WIDTH  >> 8;
+	u8 width_low	= OV5642_WIDTH  & 0xff;
+	u8 height_high	= OV5642_HEIGHT >> 8;
+	u8 height_low	= OV5642_HEIGHT & 0xff;
+
+	u8 total_width_high  = OV5642_TOTAL_WIDTH  >> 8;
+	u8 total_width_low   = OV5642_TOTAL_WIDTH  & 0xff;
+	u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8;
+	u8 total_height_low  = OV5642_TOTAL_HEIGHT & 0xff;
+
+	ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low);
+
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high);
+	if (!ret)
+		ret = reg_write(client, REG_WINDOW_HEIGHT_LOW,  height_low);
+
+	if (!ret)
+		ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_HEIGHT_LOW,  height_low);
+
+	if (!ret)
+		ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high);
+	if (!ret)
+		ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW,  total_height_low);
+
+	return ret;
+}
+
+static int ov5642_try_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *mf)
+{
+	const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
+
+	dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n",
+			__func__, mf->code, mf->width, mf->height);
+
+	if (!fmt) {
+		mf->code	= ov5642_colour_fmts[0].code;
+		mf->colorspace	= ov5642_colour_fmts[0].colorspace;
+	}
+
+	mf->width	= OV5642_WIDTH;
+	mf->height	= OV5642_HEIGHT;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov5642_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
+
+	dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+	/* MIPI CSI could have changed the format, double-check */
+	if (!ov5642_find_datafmt(mf->code))
+		return -EINVAL;
+
+	ov5642_try_fmt(sd, mf);
+
+	priv->fmt = ov5642_find_datafmt(mf->code);
+
+	ov5642_write_array(client, ov5642_default_regs_init);
+	ov5642_set_resolution(client);
+	ov5642_write_array(client, ov5642_default_regs_finalise);
+
+	return 0;
+}
+
+static int ov5642_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
+
+	const struct ov5642_datafmt *fmt = priv->fmt;
+
+	mf->code	= fmt->code;
+	mf->colorspace	= fmt->colorspace;
+	mf->width	= OV5642_WIDTH;
+	mf->height	= OV5642_HEIGHT;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	if (index >= ARRAY_SIZE(ov5642_colour_fmts))
+		return -EINVAL;
+
+	*code = ov5642_colour_fmts[index].code;
+	return 0;
+}
+
+static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
+			       struct v4l2_dbg_chip_ident *id)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+		return -EINVAL;
+
+	if (id->match.addr != client->addr)
+		return -ENODEV;
+
+	id->ident	= V4L2_IDENT_OV5642;
+	id->revision	= 0;
+
+	return 0;
+}
+
+static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct v4l2_rect *rect = &a->c;
+
+	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	rect->top	= 0;
+	rect->left	= 0;
+	rect->width	= OV5642_WIDTH;
+	rect->height	= OV5642_HEIGHT;
+
+	return 0;
+}
+
+static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	a->bounds.left			= 0;
+	a->bounds.top			= 0;
+	a->bounds.width			= OV5642_WIDTH;
+	a->bounds.height		= OV5642_HEIGHT;
+	a->defrect			= a->bounds;
+	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->pixelaspect.numerator	= 1;
+	a->pixelaspect.denominator	= 1;
+
+	return 0;
+}
+
+static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
+	.s_mbus_fmt	= ov5642_s_fmt,
+	.g_mbus_fmt	= ov5642_g_fmt,
+	.try_mbus_fmt	= ov5642_try_fmt,
+	.enum_mbus_fmt	= ov5642_enum_fmt,
+	.g_crop		= ov5642_g_crop,
+	.cropcap	= ov5642_cropcap,
+};
+
+static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+	.g_chip_ident	= ov5642_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register	= ov5642_get_register,
+	.s_register	= ov5642_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+	.core	= &ov5642_subdev_core_ops,
+	.video	= &ov5642_subdev_video_ops,
+};
+
+/*
+ * We have to provide soc-camera operations, but we don't have anything to say
+ * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
+ */
+static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd)
+{
+	return 0;
+}
+
+static int soc_ov5642_set_bus_param(struct soc_camera_device *icd,
+				 unsigned long flags)
+{
+	return -EINVAL;
+}
+
+static struct soc_camera_ops soc_ov5642_ops = {
+	.query_bus_param	= soc_ov5642_query_bus_param,
+	.set_bus_param		= soc_ov5642_set_bus_param,
+};
+
+static int ov5642_video_probe(struct soc_camera_device *icd,
+			      struct i2c_client *client)
+{
+	int ret;
+	u8 id_high, id_low;
+	u16 id;
+
+	/* Read sensor Model ID */
+	ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high);
+	if (ret < 0)
+		return ret;
+
+	id = id_high << 8;
+
+	ret = reg_read(client, REG_CHIP_ID_LOW, &id_low);
+	if (ret < 0)
+		return ret;
+
+	id |= id_low;
+
+	dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+	if (id != 0x5642)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int ov5642_probe(struct i2c_client *client,
+			const struct i2c_device_id *did)
+{
+	struct ov5642 *priv;
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl;
+	int ret;
+
+	if (!icd) {
+		dev_err(&client->dev, "OV5642: missing soc-camera data!\n");
+		return -EINVAL;
+	}
+
+	icl = to_soc_camera_link(icd);
+	if (!icl) {
+		dev_err(&client->dev, "OV5642: missing platform data!\n");
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
+
+	icd->ops	= &soc_ov5642_ops;
+	priv->fmt	= &ov5642_colour_fmts[0];
+
+	ret = ov5642_video_probe(icd, client);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	icd->ops = NULL;
+	kfree(priv);
+	return ret;
+}
+
+static int ov5642_remove(struct i2c_client *client)
+{
+	struct ov5642 *priv = to_ov5642(client);
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+	icd->ops = NULL;
+	if (icl->free_bus)
+		icl->free_bus(icl);
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+	{ "ov5642", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static struct i2c_driver ov5642_i2c_driver = {
+	.driver = {
+		.name = "ov5642",
+	},
+	.probe		= ov5642_probe,
+	.remove		= ov5642_remove,
+	.id_table	= ov5642_id,
+};
+
+static int __init ov5642_mod_init(void)
+{
+	return i2c_add_driver(&ov5642_i2c_driver);
+}
+
+static void __exit ov5642_mod_exit(void)
+{
+	i2c_del_driver(&ov5642_i2c_driver);
+}
+
+module_init(ov5642_mod_init);
+module_exit(ov5642_mod_exit);
+
+MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index d4e7c11..8aa0585 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -19,8 +19,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-mediabus.h>
-
-#include "ov7670.h"
+#include <media/ov7670.h>
 
 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
 MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 48895ef..397870f 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -1032,13 +1032,9 @@
 	u8                  pid, ver;
 	const char         *devname;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * check and show product ID and manufacturer ID
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 5173ac4..3681a6f 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -657,16 +657,9 @@
 	const char	*devname;
 	int		ret = 0;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
-		dev_err(&client->dev, "Parent missing or invalid!\n");
-		ret = -ENODEV;
-		goto err;
-	}
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * check and show product ID and manufacturer ID
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
index 4d4ee4f..edd1ffc 100644
--- a/drivers/media/video/ov9740.c
+++ b/drivers/media/video/ov9740.c
@@ -44,12 +44,12 @@
 #define OV9740_Y_ADDR_START_LO		0x0347
 #define OV9740_X_ADDR_END_HI		0x0348
 #define OV9740_X_ADDR_END_LO		0x0349
-#define OV9740_Y_ADDR_END_HI		0x034A
-#define OV9740_Y_ADDR_END_LO		0x034B
-#define OV9740_X_OUTPUT_SIZE_HI		0x034C
-#define OV9740_X_OUTPUT_SIZE_LO		0x034D
-#define OV9740_Y_OUTPUT_SIZE_HI		0x034E
-#define OV9740_Y_OUTPUT_SIZE_LO		0x034F
+#define OV9740_Y_ADDR_END_HI		0x034a
+#define OV9740_Y_ADDR_END_LO		0x034b
+#define OV9740_X_OUTPUT_SIZE_HI		0x034c
+#define OV9740_X_OUTPUT_SIZE_LO		0x034d
+#define OV9740_Y_OUTPUT_SIZE_HI		0x034e
+#define OV9740_Y_OUTPUT_SIZE_LO		0x034f
 
 /* IO Control Registers */
 #define OV9740_IO_CREL00		0x3002
@@ -68,6 +68,7 @@
 #define OV9740_ANALOG_CTRL04		0x3604
 #define OV9740_ANALOG_CTRL10		0x3610
 #define OV9740_ANALOG_CTRL12		0x3612
+#define OV9740_ANALOG_CTRL15		0x3615
 #define OV9740_ANALOG_CTRL20		0x3620
 #define OV9740_ANALOG_CTRL21		0x3621
 #define OV9740_ANALOG_CTRL22		0x3622
@@ -89,28 +90,28 @@
 #define OV9740_TIMING_CTRL35		0x3835
 
 /* Banding Filter */
-#define OV9740_AEC_MAXEXPO_60_H		0x3A02
-#define OV9740_AEC_MAXEXPO_60_L		0x3A03
-#define OV9740_AEC_B50_STEP_HI		0x3A08
-#define OV9740_AEC_B50_STEP_LO		0x3A09
-#define OV9740_AEC_B60_STEP_HI		0x3A0A
-#define OV9740_AEC_B60_STEP_LO		0x3A0B
-#define OV9740_AEC_CTRL0D		0x3A0D
-#define OV9740_AEC_CTRL0E		0x3A0E
-#define OV9740_AEC_MAXEXPO_50_H		0x3A14
-#define OV9740_AEC_MAXEXPO_50_L		0x3A15
+#define OV9740_AEC_MAXEXPO_60_H		0x3a02
+#define OV9740_AEC_MAXEXPO_60_L		0x3a03
+#define OV9740_AEC_B50_STEP_HI		0x3a08
+#define OV9740_AEC_B50_STEP_LO		0x3a09
+#define OV9740_AEC_B60_STEP_HI		0x3a0a
+#define OV9740_AEC_B60_STEP_LO		0x3a0b
+#define OV9740_AEC_CTRL0D		0x3a0d
+#define OV9740_AEC_CTRL0E		0x3a0e
+#define OV9740_AEC_MAXEXPO_50_H		0x3a14
+#define OV9740_AEC_MAXEXPO_50_L		0x3a15
 
 /* AEC/AGC Control */
 #define OV9740_AEC_ENABLE		0x3503
-#define OV9740_GAIN_CEILING_01		0x3A18
-#define OV9740_GAIN_CEILING_02		0x3A19
-#define OV9740_AEC_HI_THRESHOLD		0x3A11
-#define OV9740_AEC_3A1A			0x3A1A
-#define OV9740_AEC_CTRL1B_WPT2		0x3A1B
-#define OV9740_AEC_CTRL0F_WPT		0x3A0F
-#define OV9740_AEC_CTRL10_BPT		0x3A10
-#define OV9740_AEC_CTRL1E_BPT2		0x3A1E
-#define OV9740_AEC_LO_THRESHOLD		0x3A1F
+#define OV9740_GAIN_CEILING_01		0x3a18
+#define OV9740_GAIN_CEILING_02		0x3a19
+#define OV9740_AEC_HI_THRESHOLD		0x3a11
+#define OV9740_AEC_3A1A			0x3a1a
+#define OV9740_AEC_CTRL1B_WPT2		0x3a1b
+#define OV9740_AEC_CTRL0F_WPT		0x3a0f
+#define OV9740_AEC_CTRL10_BPT		0x3a10
+#define OV9740_AEC_CTRL1E_BPT2		0x3a1e
+#define OV9740_AEC_LO_THRESHOLD		0x3a1f
 
 /* BLC Control */
 #define OV9740_BLC_AUTO_ENABLE		0x4002
@@ -132,7 +133,7 @@
 #define OV9740_VT_SYS_CLK_DIV		0x0303
 #define OV9740_VT_PIX_CLK_DIV		0x0301
 #define OV9740_PLL_CTRL3010		0x3010
-#define OV9740_VFIFO_CTRL00		0x460E
+#define OV9740_VFIFO_CTRL00		0x460e
 
 /* ISP Control */
 #define OV9740_ISP_CTRL00		0x5000
@@ -141,9 +142,9 @@
 #define OV9740_ISP_CTRL05		0x5005
 #define OV9740_ISP_CTRL12		0x5012
 #define OV9740_ISP_CTRL19		0x5019
-#define OV9740_ISP_CTRL1A		0x501A
-#define OV9740_ISP_CTRL1E		0x501E
-#define OV9740_ISP_CTRL1F		0x501F
+#define OV9740_ISP_CTRL1A		0x501a
+#define OV9740_ISP_CTRL1E		0x501e
+#define OV9740_ISP_CTRL1F		0x501f
 #define OV9740_ISP_CTRL20		0x5020
 #define OV9740_ISP_CTRL21		0x5021
 
@@ -158,12 +159,12 @@
 #define OV9740_AWB_ADV_CTRL04		0x5187
 #define OV9740_AWB_ADV_CTRL05		0x5188
 #define OV9740_AWB_ADV_CTRL06		0x5189
-#define OV9740_AWB_ADV_CTRL07		0x518A
-#define OV9740_AWB_ADV_CTRL08		0x518B
-#define OV9740_AWB_ADV_CTRL09		0x518C
-#define OV9740_AWB_ADV_CTRL10		0x518D
-#define OV9740_AWB_ADV_CTRL11		0x518E
-#define OV9740_AWB_CTRL0F		0x518F
+#define OV9740_AWB_ADV_CTRL07		0x518a
+#define OV9740_AWB_ADV_CTRL08		0x518b
+#define OV9740_AWB_ADV_CTRL09		0x518c
+#define OV9740_AWB_ADV_CTRL10		0x518d
+#define OV9740_AWB_ADV_CTRL11		0x518e
+#define OV9740_AWB_CTRL0F		0x518f
 #define OV9740_AWB_CTRL10		0x5190
 #define OV9740_AWB_CTRL11		0x5191
 #define OV9740_AWB_CTRL12		0x5192
@@ -180,27 +181,8 @@
 #define OV9740_MIPI_CTRL_3012		0x3012
 #define OV9740_SC_CMMM_MIPI_CTR		0x3014
 
-/* supported resolutions */
-enum {
-	OV9740_VGA,
-	OV9740_720P,
-};
-
-struct ov9740_resolution {
-	unsigned int width;
-	unsigned int height;
-};
-
-static struct ov9740_resolution ov9740_resolutions[] = {
-	[OV9740_VGA] = {
-		.width	= 640,
-		.height	= 480,
-	},
-	[OV9740_720P] = {
-		.width	= 1280,
-		.height	= 720,
-	},
-};
+#define OV9740_MAX_WIDTH		1280
+#define OV9740_MAX_HEIGHT		720
 
 /* Misc. structures */
 struct ov9740_reg {
@@ -219,9 +201,16 @@
 
 	bool				flag_vflip;
 	bool				flag_hflip;
+
+	/* For suspend/resume. */
+	struct v4l2_mbus_framefmt	current_mf;
+	bool				current_enable;
 };
 
 static const struct ov9740_reg ov9740_defaults[] = {
+	/* Software Reset */
+	{ OV9740_SOFTWARE_RESET,	0x01 },
+
 	/* Banding Filter */
 	{ OV9740_AEC_B50_STEP_HI,	0x00 },
 	{ OV9740_AEC_B50_STEP_LO,	0xe8 },
@@ -241,36 +230,36 @@
 	/* Un-documented OV9740 registers */
 	{ 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 },
 	{ 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c },
-	{ 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580A, 0x0e }, { 0x580B, 0x16 },
-	{ 0x580C, 0x06 }, { 0x580D, 0x02 }, { 0x580E, 0x00 }, { 0x580F, 0x00 },
+	{ 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 },
+	{ 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 },
 	{ 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 },
 	{ 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 },
-	{ 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581A, 0x07 }, { 0x581B, 0x08 },
-	{ 0x581C, 0x0b }, { 0x581D, 0x14 }, { 0x581E, 0x28 }, { 0x581F, 0x23 },
+	{ 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 },
+	{ 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 },
 	{ 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a },
 	{ 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f },
-	{ 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582A, 0x8f }, { 0x582B, 0x9e },
-	{ 0x582C, 0x8f }, { 0x582D, 0x9f }, { 0x582E, 0x4f }, { 0x582F, 0x87 },
+	{ 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e },
+	{ 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 },
 	{ 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f },
 	{ 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf },
-	{ 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583A, 0x9f }, { 0x583B, 0x7f },
-	{ 0x583C, 0x5f },
+	{ 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f },
+	{ 0x583c, 0x5f },
 
 	/* Y Gamma */
 	{ 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e },
 	{ 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 },
-	{ 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548A, 0xa4 }, { 0x548B, 0xb1 },
-	{ 0x548C, 0xc6 }, { 0x548D, 0xd8 }, { 0x548E, 0xe9 },
+	{ 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 },
+	{ 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 },
 
 	/* UV Gamma */
 	{ 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 },
 	{ 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 },
-	{ 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549A, 0x02 }, { 0x549B, 0xeb },
-	{ 0x549C, 0x02 }, { 0x549D, 0xa0 }, { 0x549E, 0x02 }, { 0x549F, 0x67 },
-	{ 0x54A0, 0x02 }, { 0x54A1, 0x3b }, { 0x54A2, 0x02 }, { 0x54A3, 0x18 },
-	{ 0x54A4, 0x01 }, { 0x54A5, 0xe7 }, { 0x54A6, 0x01 }, { 0x54A7, 0xc3 },
-	{ 0x54A8, 0x01 }, { 0x54A9, 0x94 }, { 0x54AA, 0x01 }, { 0x54AB, 0x72 },
-	{ 0x54AC, 0x01 }, { 0x54AD, 0x57 },
+	{ 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb },
+	{ 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 },
+	{ 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 },
+	{ 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 },
+	{ 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 },
+	{ 0x54ac, 0x01 }, { 0x54ad, 0x57 },
 
 	/* AWB */
 	{ OV9740_AWB_CTRL00,		0xf0 },
@@ -296,18 +285,18 @@
 	{ OV9740_AWB_CTRL14,		0x00 },
 
 	/* CIP */
-	{ 0x530D, 0x12 },
+	{ 0x530d, 0x12 },
 
 	/* CMX */
 	{ 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 },
 	{ 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 },
-	{ 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538A, 0x00 }, { 0x538B, 0x20 },
-	{ 0x538C, 0x00 }, { 0x538D, 0x00 }, { 0x538E, 0x00 }, { 0x538F, 0x16 },
+	{ 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 },
+	{ 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 },
 	{ 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 },
 	{ 0x5394, 0x18 },
 
 	/* 50/60 Detection */
-	{ 0x3C0A, 0x9c }, { 0x3C0B, 0x3f },
+	{ 0x3c0a, 0x9c }, { 0x3c0b, 0x3f },
 
 	/* Output Select */
 	{ OV9740_IO_OUTPUT_SEL01,	0x00 },
@@ -333,6 +322,7 @@
 	{ OV9740_ANALOG_CTRL10,		0xa1 },
 	{ OV9740_ANALOG_CTRL12,		0x24 },
 	{ OV9740_ANALOG_CTRL22,		0x9f },
+	{ OV9740_ANALOG_CTRL15,		0xf0 },
 
 	/* Sensor Control */
 	{ OV9740_SENSOR_CTRL03,		0x42 },
@@ -385,7 +375,7 @@
 	{ OV9740_LN_LENGTH_PCK_LO,	0x62 },
 
 	/* MIPI Control */
-	{ OV9740_MIPI_CTRL00,		0x44 },
+	{ OV9740_MIPI_CTRL00,		0x44 }, /* 0x64 for discontinuous clk */
 	{ OV9740_MIPI_3837,		0x01 },
 	{ OV9740_MIPI_CTRL01,		0x0f },
 	{ OV9740_MIPI_CTRL03,		0x05 },
@@ -393,54 +383,9 @@
 	{ OV9740_VFIFO_RD_CTRL,		0x16 },
 	{ OV9740_MIPI_CTRL_3012,	0x70 },
 	{ OV9740_SC_CMMM_MIPI_CTR,	0x01 },
-};
 
-static const struct ov9740_reg ov9740_regs_vga[] = {
-	{ OV9740_X_ADDR_START_HI,	0x00 },
-	{ OV9740_X_ADDR_START_LO,	0xa0 },
-	{ OV9740_Y_ADDR_START_HI,	0x00 },
-	{ OV9740_Y_ADDR_START_LO,	0x00 },
-	{ OV9740_X_ADDR_END_HI,		0x04 },
-	{ OV9740_X_ADDR_END_LO,		0x63 },
-	{ OV9740_Y_ADDR_END_HI,		0x02 },
-	{ OV9740_Y_ADDR_END_LO,		0xd3 },
-	{ OV9740_X_OUTPUT_SIZE_HI,	0x02 },
-	{ OV9740_X_OUTPUT_SIZE_LO,	0x80 },
-	{ OV9740_Y_OUTPUT_SIZE_HI,	0x01 },
-	{ OV9740_Y_OUTPUT_SIZE_LO,	0xe0 },
-	{ OV9740_ISP_CTRL1E,		0x03 },
-	{ OV9740_ISP_CTRL1F,		0xc0 },
-	{ OV9740_ISP_CTRL20,		0x02 },
-	{ OV9740_ISP_CTRL21,		0xd0 },
-	{ OV9740_VFIFO_READ_START_HI,	0x01 },
-	{ OV9740_VFIFO_READ_START_LO,	0x40 },
-	{ OV9740_ISP_CTRL00,		0xff },
-	{ OV9740_ISP_CTRL01,		0xff },
-	{ OV9740_ISP_CTRL03,		0xff },
-};
-
-static const struct ov9740_reg ov9740_regs_720p[] = {
-	{ OV9740_X_ADDR_START_HI,	0x00 },
-	{ OV9740_X_ADDR_START_LO,	0x00 },
-	{ OV9740_Y_ADDR_START_HI,	0x00 },
-	{ OV9740_Y_ADDR_START_LO,	0x00 },
-	{ OV9740_X_ADDR_END_HI,		0x05 },
-	{ OV9740_X_ADDR_END_LO,		0x03 },
-	{ OV9740_Y_ADDR_END_HI,		0x02 },
-	{ OV9740_Y_ADDR_END_LO,		0xd3 },
-	{ OV9740_X_OUTPUT_SIZE_HI,	0x05 },
-	{ OV9740_X_OUTPUT_SIZE_LO,	0x00 },
-	{ OV9740_Y_OUTPUT_SIZE_HI,	0x02 },
-	{ OV9740_Y_OUTPUT_SIZE_LO,	0xd0 },
-	{ OV9740_ISP_CTRL1E,		0x05 },
-	{ OV9740_ISP_CTRL1F,		0x00 },
-	{ OV9740_ISP_CTRL20,		0x02 },
-	{ OV9740_ISP_CTRL21,		0xd0 },
-	{ OV9740_VFIFO_READ_START_HI,	0x02 },
-	{ OV9740_VFIFO_READ_START_LO,	0x30 },
-	{ OV9740_ISP_CTRL00,		0xff },
-	{ OV9740_ISP_CTRL01,		0xef },
-	{ OV9740_ISP_CTRL03,		0xff },
+	/* YUYV order */
+	{ OV9740_ISP_CTRL19,		0x02 },
 };
 
 static enum v4l2_mbus_pixelcode ov9740_codes[] = {
@@ -537,7 +482,8 @@
 	ret = ov9740_reg_read(client, reg, &val);
 	if (ret < 0) {
 		dev_err(&client->dev,
-			"[Read]-Modify-Write of register %02x failed!\n", reg);
+			"[Read]-Modify-Write of register 0x%04x failed!\n",
+			reg);
 		return ret;
 	}
 
@@ -547,7 +493,8 @@
 	ret = ov9740_reg_write(client, reg, val);
 	if (ret < 0) {
 		dev_err(&client->dev,
-			"Read-Modify-[Write] of register %02x failed!\n", reg);
+			"Read-Modify-[Write] of register 0x%04x failed!\n",
+			reg);
 		return ret;
 	}
 
@@ -608,6 +555,8 @@
 					       0x00);
 	}
 
+	priv->current_enable = enable;
+
 	return ret;
 }
 
@@ -630,6 +579,215 @@
 	return soc_camera_apply_sensor_flags(icl, flags);
 }
 
+/* select nearest higher resolution for capture */
+static void ov9740_res_roundup(u32 *width, u32 *height)
+{
+	/* Width must be a multiple of 4 pixels. */
+	*width = ALIGN(*width, 4);
+
+	/* Max resolution is 1280x720 (720p). */
+	if (*width > OV9740_MAX_WIDTH)
+		*width = OV9740_MAX_WIDTH;
+
+	if (*height > OV9740_MAX_HEIGHT)
+		*height = OV9740_MAX_HEIGHT;
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
+{
+	u32 x_start;
+	u32 y_start;
+	u32 x_end;
+	u32 y_end;
+	bool scaling = 0;
+	u32 scale_input_x;
+	u32 scale_input_y;
+	int ret;
+
+	if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
+		scaling = 1;
+
+	/*
+	 * Try to use as much of the sensor area as possible when supporting
+	 * smaller resolutions.  Depending on the aspect ratio of the
+	 * chosen resolution, we can either use the full width of the sensor,
+	 * or the full height of the sensor (or both if the aspect ratio is
+	 * the same as 1280x720.
+	 */
+	if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) {
+		scale_input_x = (OV9740_MAX_HEIGHT * width) / height;
+		scale_input_y = OV9740_MAX_HEIGHT;
+	} else {
+		scale_input_x = OV9740_MAX_WIDTH;
+		scale_input_y = (OV9740_MAX_WIDTH * height) / width;
+	}
+
+	/* These describe the area of the sensor to use. */
+	x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2;
+	y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2;
+	x_end = x_start + scale_input_x - 1;
+	y_end = y_start + scale_input_y - 1;
+
+	ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff);
+	if (ret)
+		goto done;
+
+	ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff);
+	if (ret)
+		goto done;
+
+	ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff);
+	if (ret)
+		goto done;
+
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff);
+	if (ret)
+		goto done;
+
+	ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI,
+			       (scale_input_x - width) >> 8);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO,
+			       (scale_input_x - width) & 0xff);
+	if (ret)
+		goto done;
+
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff);
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef |
+							  (scaling << 4));
+	if (ret)
+		goto done;
+	ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff);
+
+done:
+	return ret;
+}
+
+/* set the format we will capture in */
+static int ov9740_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov9740_priv *priv = to_ov9740(sd);
+	enum v4l2_colorspace cspace;
+	enum v4l2_mbus_pixelcode code = mf->code;
+	int ret;
+
+	ov9740_res_roundup(&mf->width, &mf->height);
+
+	switch (code) {
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+		cspace = V4L2_COLORSPACE_SRGB;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = ov9740_reg_write_array(client, ov9740_defaults,
+				     ARRAY_SIZE(ov9740_defaults));
+	if (ret < 0)
+		return ret;
+
+	ret = ov9740_set_res(client, mf->width, mf->height);
+	if (ret < 0)
+		return ret;
+
+	mf->code	= code;
+	mf->colorspace	= cspace;
+
+	memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt));
+
+	return ret;
+}
+
+static int ov9740_try_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *mf)
+{
+	ov9740_res_roundup(&mf->width, &mf->height);
+
+	mf->field = V4L2_FIELD_NONE;
+	mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+	return 0;
+}
+
+static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	if (index >= ARRAY_SIZE(ov9740_codes))
+		return -EINVAL;
+
+	*code = ov9740_codes[index];
+
+	return 0;
+}
+
+static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	a->bounds.left		= 0;
+	a->bounds.top		= 0;
+	a->bounds.width		= OV9740_MAX_WIDTH;
+	a->bounds.height	= OV9740_MAX_HEIGHT;
+	a->defrect		= a->bounds;
+	a->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->pixelaspect.numerator	= 1;
+	a->pixelaspect.denominator	= 1;
+
+	return 0;
+}
+
+static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	a->c.left		= 0;
+	a->c.top		= 0;
+	a->c.width		= OV9740_MAX_WIDTH;
+	a->c.height		= OV9740_MAX_HEIGHT;
+	a->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	return 0;
+}
+
 /* Get status of additional camera capabilities */
 static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
@@ -680,6 +838,24 @@
 	return 0;
 }
 
+static int ov9740_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov9740_priv *priv = to_ov9740(sd);
+
+	if (!priv->current_enable)
+		return 0;
+
+	if (on) {
+		ov9740_s_fmt(sd, &priv->current_mf);
+		ov9740_s_stream(sd, priv->current_enable);
+	} else {
+		ov9740_s_stream(sd, 0);
+		priv->current_enable = true;
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int ov9740_get_register(struct v4l2_subdev *sd,
 			       struct v4l2_dbg_register *reg)
@@ -714,127 +890,6 @@
 }
 #endif
 
-/* select nearest higher resolution for capture */
-static void ov9740_res_roundup(u32 *width, u32 *height)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++)
-		if ((ov9740_resolutions[i].width >= *width) &&
-		    (ov9740_resolutions[i].height >= *height)) {
-			*width = ov9740_resolutions[i].width;
-			*height = ov9740_resolutions[i].height;
-			return;
-		}
-
-	*width = ov9740_resolutions[OV9740_720P].width;
-	*height = ov9740_resolutions[OV9740_720P].height;
-}
-
-/* Setup registers according to resolution and color encoding */
-static int ov9740_set_res(struct i2c_client *client, u32 width)
-{
-	int ret;
-
-	/* select register configuration for given resolution */
-	if (width == ov9740_resolutions[OV9740_VGA].width) {
-		dev_dbg(&client->dev, "Setting image size to 640x480\n");
-		ret = ov9740_reg_write_array(client, ov9740_regs_vga,
-					     ARRAY_SIZE(ov9740_regs_vga));
-	} else if (width == ov9740_resolutions[OV9740_720P].width) {
-		dev_dbg(&client->dev, "Setting image size to 1280x720\n");
-		ret = ov9740_reg_write_array(client, ov9740_regs_720p,
-					     ARRAY_SIZE(ov9740_regs_720p));
-	} else {
-		dev_err(&client->dev, "Failed to select resolution!\n");
-		return -EINVAL;
-	}
-
-	return ret;
-}
-
-/* set the format we will capture in */
-static int ov9740_s_fmt(struct v4l2_subdev *sd,
-			struct v4l2_mbus_framefmt *mf)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	enum v4l2_colorspace cspace;
-	enum v4l2_mbus_pixelcode code = mf->code;
-	int ret;
-
-	ov9740_res_roundup(&mf->width, &mf->height);
-
-	switch (code) {
-	case V4L2_MBUS_FMT_YUYV8_2X8:
-		cspace = V4L2_COLORSPACE_SRGB;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	ret = ov9740_reg_write_array(client, ov9740_defaults,
-				     ARRAY_SIZE(ov9740_defaults));
-	if (ret < 0)
-		return ret;
-
-	ret = ov9740_set_res(client, mf->width);
-	if (ret < 0)
-		return ret;
-
-	mf->code	= code;
-	mf->colorspace	= cspace;
-
-	return ret;
-}
-
-static int ov9740_try_fmt(struct v4l2_subdev *sd,
-			  struct v4l2_mbus_framefmt *mf)
-{
-	ov9740_res_roundup(&mf->width, &mf->height);
-
-	mf->field = V4L2_FIELD_NONE;
-	mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
-	mf->colorspace = V4L2_COLORSPACE_SRGB;
-
-	return 0;
-}
-
-static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
-			   enum v4l2_mbus_pixelcode *code)
-{
-	if (index >= ARRAY_SIZE(ov9740_codes))
-		return -EINVAL;
-
-	*code = ov9740_codes[index];
-
-	return 0;
-}
-
-static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
-	a->bounds.left		= 0;
-	a->bounds.top		= 0;
-	a->bounds.width		= ov9740_resolutions[OV9740_720P].width;
-	a->bounds.height	= ov9740_resolutions[OV9740_720P].height;
-	a->defrect		= a->bounds;
-	a->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	a->pixelaspect.numerator	= 1;
-	a->pixelaspect.denominator	= 1;
-
-	return 0;
-}
-
-static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
-	a->c.left		= 0;
-	a->c.top		= 0;
-	a->c.width		= ov9740_resolutions[OV9740_720P].width;
-	a->c.height		= ov9740_resolutions[OV9740_720P].height;
-	a->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
-	return 0;
-}
-
 static int ov9740_video_probe(struct soc_camera_device *icd,
 			      struct i2c_client *client)
 {
@@ -843,16 +898,9 @@
 	u8 modelhi, modello;
 	int ret;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
-		dev_err(&client->dev, "Parent missing or invalid!\n");
-		ret = -ENODEV;
-		goto err;
-	}
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * check and show product ID and manufacturer ID
@@ -901,17 +949,6 @@
 	.num_controls		= ARRAY_SIZE(ov9740_controls),
 };
 
-static struct v4l2_subdev_core_ops ov9740_core_ops = {
-	.g_ctrl			= ov9740_g_ctrl,
-	.s_ctrl			= ov9740_s_ctrl,
-	.g_chip_ident		= ov9740_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-	.g_register		= ov9740_get_register,
-	.s_register		= ov9740_set_register,
-#endif
-
-};
-
 static struct v4l2_subdev_video_ops ov9740_video_ops = {
 	.s_stream		= ov9740_s_stream,
 	.s_mbus_fmt		= ov9740_s_fmt,
@@ -921,6 +958,17 @@
 	.g_crop			= ov9740_g_crop,
 };
 
+static struct v4l2_subdev_core_ops ov9740_core_ops = {
+	.g_ctrl			= ov9740_g_ctrl,
+	.s_ctrl			= ov9740_s_ctrl,
+	.g_chip_ident		= ov9740_g_chip_ident,
+	.s_power		= ov9740_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= ov9740_get_register,
+	.s_register		= ov9740_set_register,
+#endif
+};
+
 static struct v4l2_subdev_ops ov9740_subdev_ops = {
 	.core			= &ov9740_core_ops,
 	.video			= &ov9740_video_ops,
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 7551907..e753b5e 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -28,7 +28,6 @@
 #include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
-#include <linux/version.h>
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
 #include <asm/io.h>
@@ -39,7 +38,7 @@
 #include <media/v4l2-device.h>
 
 MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.0.4");
 
 #define MOTOROLA	1
 #define PHILIPS2	2               /* SAA7191 */
@@ -678,7 +677,6 @@
 	strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
 	strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
 	strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
-	vcap->version = KERNEL_VERSION(0, 0, 3);
 	vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
 	return 0;
 }
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index 2254194..c1d9bb6 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -168,6 +168,7 @@
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.9.1");
 
 
 /*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 3876114..e27f8ab 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -91,7 +91,7 @@
 	.driver         = "pvrusb2",
 	.card           = "Hauppauge WinTV pvr-usb2",
 	.bus_info       = "usb",
-	.version        = KERNEL_VERSION(0, 9, 0),
+	.version        = LINUX_VERSION_CODE,
 	.capabilities   = (V4L2_CAP_VIDEO_CAPTURE |
 			   V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
 			   V4L2_CAP_READWRITE),
@@ -369,11 +369,6 @@
 		break;
 	}
 
-	case VIDIOC_S_AUDIO:
-	{
-		ret = -EINVAL;
-		break;
-	}
 	case VIDIOC_G_TUNER:
 	{
 		struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
@@ -850,7 +845,7 @@
 #endif
 
 	default :
-		ret = -EINVAL;
+		ret = -ENOTTY;
 		break;
 	}
 
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
index 8da42e4..d63d0a8 100644
--- a/drivers/media/video/pwc/Kconfig
+++ b/drivers/media/video/pwc/Kconfig
@@ -1,6 +1,7 @@
 config USB_PWC
 	tristate "USB Philips Cameras"
 	depends on VIDEO_V4L2
+	select VIDEOBUF2_VMALLOC
 	---help---
 	  Say Y or M here if you want to use one of these Philips & OEM
 	  webcams:
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 760b4de..3977add 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -3,6 +3,7 @@
    video modes.
    (C) 1999-2003 Nemosoft Unv.
    (C) 2004-2006 Luc Saillard (luc@saillard.org)
+   (C) 2011 Hans de Goede <hdegoede@redhat.com>
 
    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
    driver and thus may have bugs that are not present in the original version.
@@ -43,61 +44,12 @@
 #include <asm/errno.h>
 
 #include "pwc.h"
-#include "pwc-uncompress.h"
 #include "pwc-kiara.h"
 #include "pwc-timon.h"
 #include "pwc-dec1.h"
 #include "pwc-dec23.h"
 
-/* Request types: video */
-#define SET_LUM_CTL			0x01
-#define GET_LUM_CTL			0x02
-#define SET_CHROM_CTL			0x03
-#define GET_CHROM_CTL			0x04
-#define SET_STATUS_CTL			0x05
-#define GET_STATUS_CTL			0x06
-#define SET_EP_STREAM_CTL		0x07
-#define GET_EP_STREAM_CTL		0x08
-#define GET_XX_CTL			0x09
-#define SET_XX_CTL			0x0A
-#define GET_XY_CTL			0x0B
-#define SET_XY_CTL			0x0C
-#define SET_MPT_CTL			0x0D
-#define GET_MPT_CTL			0x0E
-
-/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
-#define AGC_MODE_FORMATTER			0x2000
-#define PRESET_AGC_FORMATTER			0x2100
-#define SHUTTER_MODE_FORMATTER			0x2200
-#define PRESET_SHUTTER_FORMATTER		0x2300
-#define PRESET_CONTOUR_FORMATTER		0x2400
-#define AUTO_CONTOUR_FORMATTER			0x2500
-#define BACK_LIGHT_COMPENSATION_FORMATTER	0x2600
-#define CONTRAST_FORMATTER			0x2700
-#define DYNAMIC_NOISE_CONTROL_FORMATTER		0x2800
-#define FLICKERLESS_MODE_FORMATTER		0x2900
-#define AE_CONTROL_SPEED			0x2A00
-#define BRIGHTNESS_FORMATTER			0x2B00
-#define GAMMA_FORMATTER				0x2C00
-
-/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
-#define WB_MODE_FORMATTER			0x1000
-#define AWB_CONTROL_SPEED_FORMATTER		0x1100
-#define AWB_CONTROL_DELAY_FORMATTER		0x1200
-#define PRESET_MANUAL_RED_GAIN_FORMATTER	0x1300
-#define PRESET_MANUAL_BLUE_GAIN_FORMATTER	0x1400
-#define COLOUR_MODE_FORMATTER			0x1500
-#define SATURATION_MODE_FORMATTER1		0x1600
-#define SATURATION_MODE_FORMATTER2		0x1700
-
-/* Selectors for the Status controls [GS]ET_STATUS_CTL */
-#define SAVE_USER_DEFAULTS_FORMATTER		0x0200
-#define RESTORE_USER_DEFAULTS_FORMATTER		0x0300
-#define RESTORE_FACTORY_DEFAULTS_FORMATTER	0x0400
-#define READ_AGC_FORMATTER			0x0500
-#define READ_SHUTTER_FORMATTER			0x0600
-#define READ_RED_GAIN_FORMATTER			0x0700
-#define READ_BLUE_GAIN_FORMATTER		0x0800
+/* Selectors for status controls used only in this file */
 #define GET_STATUS_B00				0x0B00
 #define SENSOR_TYPE_FORMATTER1			0x0C00
 #define GET_STATUS_3000				0x3000
@@ -116,11 +68,6 @@
 /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
 #define VIDEO_OUTPUT_CONTROL_FORMATTER		0x0100
 
-/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
-#define PT_RELATIVE_CONTROL_FORMATTER		0x01
-#define PT_RESET_CONTROL_FORMATTER		0x02
-#define PT_STATUS_FORMATTER			0x03
-
 static const char *size2name[PSZ_MAX] =
 {
 	"subQCIF",
@@ -160,7 +107,7 @@
 /****************************************************************************/
 
 static int _send_control_msg(struct pwc_device *pdev,
-	u8 request, u16 value, int index, void *buf, int buflen, int timeout)
+	u8 request, u16 value, int index, void *buf, int buflen)
 {
 	int rc;
 	void *kbuf = NULL;
@@ -177,7 +124,7 @@
 		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 		value,
 		index,
-		kbuf, buflen, timeout);
+		kbuf, buflen, USB_CTRL_SET_TIMEOUT);
 
 	kfree(kbuf);
 	return rc;
@@ -197,9 +144,13 @@
 		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 		value,
 		pdev->vcinterface,
-		kbuf, buflen, 500);
+		kbuf, buflen, USB_CTRL_GET_TIMEOUT);
 	memcpy(buf, kbuf, buflen);
 	kfree(kbuf);
+
+	if (rc < 0)
+		PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",
+			  rc, request, value);
 	return rc;
 }
 
@@ -210,18 +161,16 @@
 		SET_EP_STREAM_CTL,
 		VIDEO_OUTPUT_CONTROL_FORMATTER,
 		index,
-		buf, buflen, 1000);
+		buf, buflen);
 }
 
-static inline int send_control_msg(struct pwc_device *pdev,
+int send_control_msg(struct pwc_device *pdev,
 	u8 request, u16 value, void *buf, int buflen)
 {
 	return _send_control_msg(pdev,
-		request, value, pdev->vcinterface, buf, buflen, 500);
+		request, value, pdev->vcinterface, buf, buflen);
 }
 
-
-
 static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
 {
 	unsigned char buf[3];
@@ -261,8 +210,11 @@
 		PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret);
 		return ret;
 	}
-	if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
-		pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+	if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+		ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf);
+		if (ret < 0)
+			return ret;
+	}
 
 	pdev->cmd_len = 3;
 	memcpy(pdev->cmd_buf, buf, 3);
@@ -321,8 +273,11 @@
 	if (ret < 0)
 		return ret;
 
-	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
-		pwc_dec23_init(pdev, pdev->type, buf);
+	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+		ret = pwc_dec23_init(pdev, pdev->type, buf);
+		if (ret < 0)
+			return ret;
+	}
 
 	pdev->cmd_len = 13;
 	memcpy(pdev->cmd_buf, buf, 13);
@@ -394,8 +349,11 @@
 	if (ret < 0)
 		return ret;
 
-	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
-		pwc_dec23_init(pdev, pdev->type, buf);
+	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
+		ret = pwc_dec23_init(pdev, pdev->type, buf);
+		if (ret < 0)
+			return ret;
+	}
 
 	pdev->cmd_len = 12;
 	memcpy(pdev->cmd_buf, buf, 12);
@@ -452,6 +410,7 @@
 	}
 	pdev->view.x = width;
 	pdev->view.y = height;
+	pdev->vcompression = compression;
 	pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
 	pwc_set_image_buffer_size(pdev);
 	PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
@@ -511,13 +470,9 @@
 	return ret;
 }
 
-#define BLACK_Y 0
-#define BLACK_U 128
-#define BLACK_V 128
-
 static void pwc_set_image_buffer_size(struct pwc_device *pdev)
 {
-	int i, factor = 0;
+	int factor = 0;
 
 	/* for V4L2_PIX_FMT_YUV420 */
 	switch (pdev->pixfmt) {
@@ -541,442 +496,108 @@
 	 */
 	pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
 	pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
-
-	/* Fill buffers with black colors */
-	for (i = 0; i < pwc_mbufs; i++) {
-		unsigned char *p = pdev->image_data + pdev->images[i].offset;
-		memset(p, BLACK_Y, pdev->view.x * pdev->view.y);
-		p += pdev->view.x * pdev->view.y;
-		memset(p, BLACK_U, pdev->view.x * pdev->view.y/4);
-		p += pdev->view.x * pdev->view.y/4;
-		memset(p, BLACK_V, pdev->view.x * pdev->view.y/4);
-	}
 }
 
-
-
-/* BRIGHTNESS */
-
-int pwc_get_brightness(struct pwc_device *pdev)
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
 {
-	char buf;
 	int ret;
+	u8 buf;
 
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf));
+	ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
 	if (ret < 0)
 		return ret;
-	return buf;
-}
 
-int pwc_set_brightness(struct pwc_device *pdev, int value)
-{
-	char buf;
-
-	if (value < 0)
-		value = 0;
-	if (value > 0xffff)
-		value = 0xffff;
-	buf = (value >> 9) & 0x7f;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf));
-}
-
-/* CONTRAST */
-
-int pwc_get_contrast(struct pwc_device *pdev)
-{
-	char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	return buf;
-}
-
-int pwc_set_contrast(struct pwc_device *pdev, int value)
-{
-	char buf;
-
-	if (value < 0)
-		value = 0;
-	if (value > 0xffff)
-		value = 0xffff;
-	buf = (value >> 10) & 0x3f;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf));
-}
-
-/* GAMMA */
-
-int pwc_get_gamma(struct pwc_device *pdev)
-{
-	char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	return buf;
-}
-
-int pwc_set_gamma(struct pwc_device *pdev, int value)
-{
-	char buf;
-
-	if (value < 0)
-		value = 0;
-	if (value > 0xffff)
-		value = 0xffff;
-	buf = (value >> 11) & 0x1f;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf));
-}
-
-
-/* SATURATION */
-
-/* return a value between [-100 , 100] */
-int pwc_get_saturation(struct pwc_device *pdev, int *value)
-{
-	char buf;
-	int ret, saturation_register;
-
-	if (pdev->type < 675)
-		return -EINVAL;
-	if (pdev->type < 730)
-		saturation_register = SATURATION_MODE_FORMATTER2;
-	else
-		saturation_register = SATURATION_MODE_FORMATTER1;
-	ret = recv_control_msg(pdev,
-		GET_CHROM_CTL, saturation_register, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*value = (signed)buf;
+	*data = buf;
 	return 0;
 }
 
-/* @param value saturation color between [-100 , 100] */
-int pwc_set_saturation(struct pwc_device *pdev, int value)
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)
 {
-	char buf;
-	int saturation_register;
-
-	if (pdev->type < 675)
-		return -EINVAL;
-	if (value < -100)
-		value = -100;
-	if (value > 100)
-		value = 100;
-	if (pdev->type < 730)
-		saturation_register = SATURATION_MODE_FORMATTER2;
-	else
-		saturation_register = SATURATION_MODE_FORMATTER1;
-	return send_control_msg(pdev,
-		SET_CHROM_CTL, saturation_register, &buf, sizeof(buf));
-}
-
-/* AGC */
-
-int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
-{
-	char buf;
 	int ret;
 
-	if (mode)
-		buf = 0x0; /* auto */
-	else
-		buf = 0xff; /* fixed */
-
-	ret = send_control_msg(pdev,
-		SET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf));
-
-	if (!mode && ret >= 0) {
-		if (value < 0)
-			value = 0;
-		if (value > 0xffff)
-			value = 0xffff;
-		buf = (value >> 10) & 0x3F;
-		ret = send_control_msg(pdev,
-			SET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf));
-	}
+	ret = send_control_msg(pdev, request, value, &data, sizeof(data));
 	if (ret < 0)
 		return ret;
-	return 0;
-}
-
-int pwc_get_agc(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-
-	if (buf != 0) { /* fixed */
-		ret = recv_control_msg(pdev,
-			GET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf));
-		if (ret < 0)
-			return ret;
-		if (buf > 0x3F)
-			buf = 0x3F;
-		*value = (buf << 10);
-	}
-	else { /* auto */
-		ret = recv_control_msg(pdev,
-			GET_STATUS_CTL, READ_AGC_FORMATTER, &buf, sizeof(buf));
-		if (ret < 0)
-			return ret;
-		/* Gah... this value ranges from 0x00 ... 0x9F */
-		if (buf > 0x9F)
-			buf = 0x9F;
-		*value = -(48 + buf * 409);
-	}
 
 	return 0;
 }
 
-int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
 {
-	char buf[2];
-	int speed, ret;
-
-
-	if (mode)
-		buf[0] = 0x0;	/* auto */
-	else
-		buf[0] = 0xff; /* fixed */
-
-	ret = send_control_msg(pdev,
-		SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1);
-
-	if (!mode && ret >= 0) {
-		if (value < 0)
-			value = 0;
-		if (value > 0xffff)
-			value = 0xffff;
-
-		if (DEVICE_USE_CODEC2(pdev->type)) {
-			/* speed ranges from 0x0 to 0x290 (656) */
-			speed = (value / 100);
-			buf[1] = speed >> 8;
-			buf[0] = speed & 0xff;
-		} else if (DEVICE_USE_CODEC3(pdev->type)) {
-			/* speed seems to range from 0x0 to 0xff */
-			buf[1] = 0;
-			buf[0] = value >> 8;
-		}
-
-		ret = send_control_msg(pdev,
-			SET_LUM_CTL, PRESET_SHUTTER_FORMATTER,
-			&buf, sizeof(buf));
-	}
-	return ret;
-}
-
-/* This function is not exported to v4l1, so output values between 0 -> 256 */
-int pwc_get_shutter_speed(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf[2];
 	int ret;
+	s8 buf;
 
-	ret = recv_control_msg(pdev,
-		GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &buf, sizeof(buf));
+	ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
 	if (ret < 0)
 		return ret;
-	*value = buf[0] + (buf[1] << 8);
-	if (DEVICE_USE_CODEC2(pdev->type)) {
-		/* speed ranges from 0x0 to 0x290 (656) */
-		*value *= 256/656;
-	} else if (DEVICE_USE_CODEC3(pdev->type)) {
-		/* speed seems to range from 0x0 to 0xff */
-	}
+
+	*data = buf;
 	return 0;
 }
 
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
+{
+	int ret;
+	u8 buf[2];
+
+	ret = recv_control_msg(pdev, request, value, buf, sizeof(buf));
+	if (ret < 0)
+		return ret;
+
+	*data = (buf[1] << 8) | buf[0];
+	return 0;
+}
+
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)
+{
+	int ret;
+	u8 buf[2];
+
+	buf[0] = data & 0xff;
+	buf[1] = data >> 8;
+	ret = send_control_msg(pdev, request, value, buf, sizeof(buf));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value)
+{
+	int ret;
+
+	ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
 
 /* POWER */
-
-int pwc_camera_power(struct pwc_device *pdev, int power)
+void pwc_camera_power(struct pwc_device *pdev, int power)
 {
 	char buf;
+	int r;
+
+	if (!pdev->power_save)
+		return;
 
 	if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
-		return 0;	/* Not supported by Nala or Timon < release 6 */
+		return;	/* Not supported by Nala or Timon < release 6 */
 
 	if (power)
 		buf = 0x00; /* active */
 	else
 		buf = 0xFF; /* power save */
-	return send_control_msg(pdev,
+	r = send_control_msg(pdev,
 		SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER,
 		&buf, sizeof(buf));
+
+	if (r < 0)
+		PWC_ERROR("Failed to power %s camera (%d)\n",
+			  power ? "on" : "off", r);
 }
 
-
-
-/* private calls */
-
-int pwc_restore_user(struct pwc_device *pdev)
-{
-	return send_control_msg(pdev,
-		SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, NULL, 0);
-}
-
-int pwc_save_user(struct pwc_device *pdev)
-{
-	return send_control_msg(pdev,
-		SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, NULL, 0);
-}
-
-int pwc_restore_factory(struct pwc_device *pdev)
-{
-	return send_control_msg(pdev,
-		SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, NULL, 0);
-}
-
- /* ************************************************* */
- /* Patch by Alvarado: (not in the original version   */
-
- /*
-  * the camera recognizes modes from 0 to 4:
-  *
-  * 00: indoor (incandescant lighting)
-  * 01: outdoor (sunlight)
-  * 02: fluorescent lighting
-  * 03: manual
-  * 04: auto
-  */
-int pwc_set_awb(struct pwc_device *pdev, int mode)
-{
-	char buf;
-	int ret;
-
-	if (mode < 0)
-	    mode = 0;
-
-	if (mode > 4)
-	    mode = 4;
-
-	buf = mode & 0x07; /* just the lowest three bits */
-
-	ret = send_control_msg(pdev,
-		SET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf));
-
-	if (ret < 0)
-		return ret;
-	return 0;
-}
-
-int pwc_get_awb(struct pwc_device *pdev)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf));
-
-	if (ret < 0)
-		return ret;
-	return buf;
-}
-
-int pwc_set_red_gain(struct pwc_device *pdev, int value)
-{
-	unsigned char buf;
-
-	if (value < 0)
-		value = 0;
-	if (value > 0xffff)
-		value = 0xffff;
-	/* only the msb is considered */
-	buf = value >> 8;
-	return send_control_msg(pdev,
-		SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER,
-		&buf, sizeof(buf));
-}
-
-int pwc_get_red_gain(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER,
-		&buf, sizeof(buf));
-	if (ret < 0)
-	    return ret;
-	*value = buf << 8;
-	return 0;
-}
-
-
-int pwc_set_blue_gain(struct pwc_device *pdev, int value)
-{
-	unsigned char buf;
-
-	if (value < 0)
-		value = 0;
-	if (value > 0xffff)
-		value = 0xffff;
-	/* only the msb is considered */
-	buf = value >> 8;
-	return send_control_msg(pdev,
-		SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER,
-		&buf, sizeof(buf));
-}
-
-int pwc_get_blue_gain(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER,
-		&buf, sizeof(buf));
-	if (ret < 0)
-	    return ret;
-	*value = buf << 8;
-	return 0;
-}
-
-
-/* The following two functions are different, since they only read the
-   internal red/blue gains, which may be different from the manual
-   gains set or read above.
- */
-static int pwc_read_red_gain(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*value = buf << 8;
-	return 0;
-}
-
-static int pwc_read_blue_gain(struct pwc_device *pdev, int *value)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*value = buf << 8;
-	return 0;
-}
-
-
 static int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
 {
 	unsigned char buf;
@@ -1028,6 +649,7 @@
 int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
 {
 	unsigned char buf[2];
+	int r;
 
 	if (pdev->type < 730)
 		return 0;
@@ -1045,8 +667,12 @@
 	buf[0] = on_value;
 	buf[1] = off_value;
 
-	return send_control_msg(pdev,
+	r = send_control_msg(pdev,
 		SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
+	if (r < 0)
+		PWC_ERROR("Failed to set LED on/off time (%d)\n", r);
+
+	return r;
 }
 
 static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
@@ -1069,164 +695,6 @@
 	return 0;
 }
 
-int pwc_set_contour(struct pwc_device *pdev, int contour)
-{
-	unsigned char buf;
-	int ret;
-
-	if (contour < 0)
-		buf = 0xff; /* auto contour on */
-	else
-		buf = 0x0; /* auto contour off */
-	ret = send_control_msg(pdev,
-		SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-
-	if (contour < 0)
-		return 0;
-	if (contour > 0xffff)
-		contour = 0xffff;
-
-	buf = (contour >> 10); /* contour preset is [0..3f] */
-	ret = send_control_msg(pdev,
-		SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	return 0;
-}
-
-int pwc_get_contour(struct pwc_device *pdev, int *contour)
-{
-	unsigned char buf;
-	int ret;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-
-	if (buf == 0) {
-		/* auto mode off, query current preset value */
-		ret = recv_control_msg(pdev,
-			GET_LUM_CTL, PRESET_CONTOUR_FORMATTER,
-			&buf, sizeof(buf));
-		if (ret < 0)
-			return ret;
-		*contour = buf << 10;
-	}
-	else
-		*contour = -1;
-	return 0;
-}
-
-
-int pwc_set_backlight(struct pwc_device *pdev, int backlight)
-{
-	unsigned char buf;
-
-	if (backlight)
-		buf = 0xff;
-	else
-		buf = 0x0;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER,
-		&buf, sizeof(buf));
-}
-
-int pwc_get_backlight(struct pwc_device *pdev, int *backlight)
-{
-	int ret;
-	unsigned char buf;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER,
-		&buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*backlight = !!buf;
-	return 0;
-}
-
-int pwc_set_colour_mode(struct pwc_device *pdev, int colour)
-{
-	unsigned char buf;
-
-	if (colour)
-		buf = 0xff;
-	else
-		buf = 0x0;
-	return send_control_msg(pdev,
-		SET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf));
-}
-
-int pwc_get_colour_mode(struct pwc_device *pdev, int *colour)
-{
-	int ret;
-	unsigned char buf;
-
-	ret = recv_control_msg(pdev,
-		GET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*colour = !!buf;
-	return 0;
-}
-
-
-int pwc_set_flicker(struct pwc_device *pdev, int flicker)
-{
-	unsigned char buf;
-
-	if (flicker)
-		buf = 0xff;
-	else
-		buf = 0x0;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf));
-}
-
-int pwc_get_flicker(struct pwc_device *pdev, int *flicker)
-{
-	int ret;
-	unsigned char buf;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*flicker = !!buf;
-	return 0;
-}
-
-int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
-{
-	unsigned char buf;
-
-	if (noise < 0)
-		noise = 0;
-	if (noise > 3)
-		noise = 3;
-	buf = noise;
-	return send_control_msg(pdev,
-		SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER,
-		&buf, sizeof(buf));
-}
-
-int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise)
-{
-	int ret;
-	unsigned char buf;
-
-	ret = recv_control_msg(pdev,
-		GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER,
-		&buf, sizeof(buf));
-	if (ret < 0)
-		return ret;
-	*noise = buf;
-	return 0;
-}
-
 static int _pwc_mpt_reset(struct pwc_device *pdev, int flags)
 {
 	unsigned char buf;
@@ -1309,7 +777,7 @@
 	return 0;
 }
 
-
+#ifdef CONFIG_USB_PWC_DEBUG
 int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
 {
 	unsigned char buf;
@@ -1332,7 +800,7 @@
 		*sensor = buf;
 	return 0;
 }
-
+#endif
 
  /* End of Add-Ons                                    */
  /* ************************************************* */
@@ -1356,37 +824,41 @@
 /* copy local variable to arg */
 #define ARG_OUT(ARG_name) /* nothing */
 
+/*
+ * Our ctrls use native values, but the old custom pwc ioctl interface expects
+ * values from 0 - 65535, define 2 helper functions to scale things. */
+static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+	return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum;
+}
+
+static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val)
+{
+	return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535);
+}
+
 long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
 {
 	long ret = 0;
 
 	switch(cmd) {
 	case VIDIOCPWCRUSER:
-	{
-		if (pwc_restore_user(pdev))
-			ret = -EINVAL;
+		ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
 		break;
-	}
 
 	case VIDIOCPWCSUSER:
-	{
-		if (pwc_save_user(pdev))
-			ret = -EINVAL;
+		ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
 		break;
-	}
 
 	case VIDIOCPWCFACTORY:
-	{
-		if (pwc_restore_factory(pdev))
-			ret = -EINVAL;
+		ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER);
 		break;
-	}
 
 	case VIDIOCPWCSCQUAL:
 	{
 		ARG_DEF(int, qual)
 
-		if (pdev->iso_init) {
+		if (vb2_is_streaming(&pdev->vb_queue)) {
 			ret = -EBUSY;
 			break;
 		}
@@ -1396,8 +868,6 @@
 			ret = -EINVAL;
 		else
 			ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
-		if (ret >= 0)
-			pdev->vcompression = ARGR(qual);
 		break;
 	}
 
@@ -1432,71 +902,59 @@
 	case VIDIOCPWCSAGC:
 	{
 		ARG_DEF(int, agc)
-
 		ARG_IN(agc)
-		if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc)))
-			ret = -EINVAL;
+		ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0);
+		if (ret == 0 && ARGR(agc) >= 0)
+			ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc));
 		break;
 	}
 
 	case VIDIOCPWCGAGC:
 	{
 		ARG_DEF(int, agc)
-
-		if (pwc_get_agc(pdev, ARGA(agc)))
-			ret = -EINVAL;
+		if (v4l2_ctrl_g_ctrl(pdev->autogain))
+			ARGR(agc) = -1;
+		else
+			ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain);
 		ARG_OUT(agc)
 		break;
 	}
 
 	case VIDIOCPWCSSHUTTER:
 	{
-		ARG_DEF(int, shutter_speed)
-
-		ARG_IN(shutter_speed)
-		ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed));
+		ARG_DEF(int, shutter)
+		ARG_IN(shutter)
+		ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto,
+				       /* Menu idx 0 = auto, idx 1 = manual */
+				       ARGR(shutter) >= 0);
+		if (ret == 0 && ARGR(shutter) >= 0)
+			ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter));
 		break;
 	}
 
 	case VIDIOCPWCSAWB:
 	{
 		ARG_DEF(struct pwc_whitebalance, wb)
-
 		ARG_IN(wb)
-		ret = pwc_set_awb(pdev, ARGR(wb).mode);
-		if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) {
-			pwc_set_red_gain(pdev, ARGR(wb).manual_red);
-			pwc_set_blue_gain(pdev, ARGR(wb).manual_blue);
-		}
+		ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance,
+				       ARGR(wb).mode);
+		if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
+			ret = pwc_ioctl_s_ctrl(pdev->red_balance,
+					       ARGR(wb).manual_red);
+		if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
+			ret = pwc_ioctl_s_ctrl(pdev->blue_balance,
+					       ARGR(wb).manual_blue);
 		break;
 	}
 
 	case VIDIOCPWCGAWB:
 	{
 		ARG_DEF(struct pwc_whitebalance, wb)
-
-		memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance));
-		ARGR(wb).mode = pwc_get_awb(pdev);
-		if (ARGR(wb).mode < 0)
-			ret = -EINVAL;
-		else {
-			if (ARGR(wb).mode == PWC_WB_MANUAL) {
-				ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red);
-				if (ret < 0)
-					break;
-				ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue);
-				if (ret < 0)
-					break;
-			}
-			if (ARGR(wb).mode == PWC_WB_AUTO) {
-				ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
-				if (ret < 0)
-					break;
-				ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
-				if (ret < 0)
-					break;
-			}
-		}
+		ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance);
+		ARGR(wb).manual_red = ARGR(wb).read_red =
+			pwc_ioctl_g_ctrl(pdev->red_balance);
+		ARGR(wb).manual_blue = ARGR(wb).read_blue =
+			pwc_ioctl_g_ctrl(pdev->blue_balance);
 		ARG_OUT(wb)
 		break;
 	}
@@ -1550,17 +1008,20 @@
 	case VIDIOCPWCSCONTOUR:
 	{
 		ARG_DEF(int, contour)
-
 		ARG_IN(contour)
-		ret = pwc_set_contour(pdev, ARGR(contour));
+		ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0);
+		if (ret == 0 && ARGR(contour) >= 0)
+			ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour));
 		break;
 	}
 
 	case VIDIOCPWCGCONTOUR:
 	{
 		ARG_DEF(int, contour)
-
-		ret = pwc_get_contour(pdev, ARGA(contour));
+		if (v4l2_ctrl_g_ctrl(pdev->autocontour))
+			ARGR(contour) = -1;
+		else
+			ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour);
 		ARG_OUT(contour)
 		break;
 	}
@@ -1568,17 +1029,15 @@
 	case VIDIOCPWCSBACKLIGHT:
 	{
 		ARG_DEF(int, backlight)
-
 		ARG_IN(backlight)
-		ret = pwc_set_backlight(pdev, ARGR(backlight));
+		ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight));
 		break;
 	}
 
 	case VIDIOCPWCGBACKLIGHT:
 	{
 		ARG_DEF(int, backlight)
-
-		ret = pwc_get_backlight(pdev, ARGA(backlight));
+		ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight);
 		ARG_OUT(backlight)
 		break;
 	}
@@ -1586,17 +1045,15 @@
 	case VIDIOCPWCSFLICKER:
 	{
 		ARG_DEF(int, flicker)
-
 		ARG_IN(flicker)
-		ret = pwc_set_flicker(pdev, ARGR(flicker));
+		ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker));
 		break;
 	}
 
 	case VIDIOCPWCGFLICKER:
 	{
 		ARG_DEF(int, flicker)
-
-		ret = pwc_get_flicker(pdev, ARGA(flicker));
+		ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker);
 		ARG_OUT(flicker)
 		break;
 	}
@@ -1604,17 +1061,15 @@
 	case VIDIOCPWCSDYNNOISE:
 	{
 		ARG_DEF(int, dynnoise)
-
 		ARG_IN(dynnoise)
-		ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise));
+		ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise));
 		break;
 	}
 
 	case VIDIOCPWCGDYNNOISE:
 	{
 		ARG_DEF(int, dynnoise)
-
-		ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise));
+		ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction);
 		ARG_OUT(dynnoise);
 		break;
 	}
diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c
index c29593f..be0e02c 100644
--- a/drivers/media/video/pwc/pwc-dec1.c
+++ b/drivers/media/video/pwc/pwc-dec1.c
@@ -22,29 +22,19 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
-
-
-
 #include "pwc-dec1.h"
 
-
-void pwc_dec1_init(int type, int release, void *buffer, void *table)
+int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer)
 {
+	struct pwc_dec1_private *pdec;
 
-}
+	if (pwc->decompress_data == NULL) {
+		pdec = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL);
+		if (pdec == NULL)
+			return -ENOMEM;
+		pwc->decompress_data = pdec;
+	}
+	pdec = pwc->decompress_data;
 
-void pwc_dec1_exit(void)
-{
-
-
-
-}
-
-int pwc_dec1_alloc(struct pwc_device *pwc)
-{
-	pwc->decompress_data = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL);
-	if (pwc->decompress_data == NULL)
-		return -ENOMEM;
 	return 0;
 }
-
diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h
index 8b62ddc..a57d860 100644
--- a/drivers/media/video/pwc/pwc-dec1.h
+++ b/drivers/media/video/pwc/pwc-dec1.h
@@ -22,8 +22,6 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-
-
 #ifndef PWC_DEC1_H
 #define PWC_DEC1_H
 
@@ -32,12 +30,8 @@
 struct pwc_dec1_private
 {
 	int version;
-
 };
 
-int  pwc_dec1_alloc(struct pwc_device *pwc);
-void pwc_dec1_init(int type, int release, void *buffer, void *private_data);
-void pwc_dec1_exit(void);
+int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer);
 
 #endif
-
diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c
index 0c801b8..06a4e87 100644
--- a/drivers/media/video/pwc/pwc-dec23.c
+++ b/drivers/media/video/pwc/pwc-dec23.c
@@ -916,27 +916,5 @@
 			pout_planar_v += pwc->view.x;
 
 		}
-
 	}
-
 }
-
-void pwc_dec23_exit(void)
-{
-	/* Do nothing */
-
-}
-
-/**
- * Allocate a private structure used by lookup table.
- * You must call kfree() to free the memory allocated.
- */
-int pwc_dec23_alloc(struct pwc_device *pwc)
-{
-	pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
-	if (pwc->decompress_data == NULL)
-		return -ENOMEM;
-	return 0;
-}
-
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h
index 1c55298..a0ac4f3 100644
--- a/drivers/media/video/pwc/pwc-dec23.h
+++ b/drivers/media/video/pwc/pwc-dec23.h
@@ -49,19 +49,9 @@
 
 };
 
-
-int pwc_dec23_alloc(struct pwc_device *pwc);
 int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd);
-void pwc_dec23_exit(void);
 void pwc_dec23_decompress(const struct pwc_device *pwc,
 			  const void *src,
 			  void *dst,
 			  int flags);
-
-
-
 #endif
-
-
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
-
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index b0bde5a..51ca358 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -2,6 +2,7 @@
    USB and Video4Linux interface part.
    (C) 1999-2004 Nemosoft Unv.
    (C) 2004-2006 Luc Saillard (luc@saillard.org)
+   (C) 2011 Hans de Goede <hdegoede@redhat.com>
 
    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
    driver and thus may have bugs that are not present in the original version.
@@ -74,7 +75,6 @@
 #include "pwc-timon.h"
 #include "pwc-dec23.h"
 #include "pwc-dec1.h"
-#include "pwc-uncompress.h"
 
 /* Function prototypes and driver templates */
 
@@ -116,6 +116,7 @@
 
 static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
 static void usb_pwc_disconnect(struct usb_interface *intf);
+static void pwc_isoc_cleanup(struct pwc_device *pdev);
 
 static struct usb_driver pwc_driver = {
 	.name =			"Philips webcam",	/* name */
@@ -127,14 +128,11 @@
 #define MAX_DEV_HINTS	20
 #define MAX_ISOC_ERRORS	20
 
-static int default_size = PSZ_QCIF;
 static int default_fps = 10;
-static int default_fbufs = 3;   /* Default number of frame buffers */
-	int pwc_mbufs = 2;	/* Default number of mmap() buffers */
 #ifdef CONFIG_USB_PWC_DEBUG
 	int pwc_trace = PWC_DEBUG_LEVEL;
 #endif
-static int power_save;
+static int power_save = -1;
 static int led_on = 100, led_off; /* defaults to LED that is on while in use */
 static int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */
 static struct {
@@ -173,389 +171,20 @@
 /***************************************************************************/
 /* Private functions */
 
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the area.
- */
-
-
-
-static void *pwc_rvmalloc(unsigned long size)
+struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
 {
-	void * mem;
-	unsigned long adr;
+	unsigned long flags = 0;
+	struct pwc_frame_buf *buf = NULL;
 
-	mem=vmalloc_32(size);
-	if (!mem)
-		return NULL;
+	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+	if (list_empty(&pdev->queued_bufs))
+		goto leave;
 
-	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
-	adr=(unsigned long) mem;
-	while (size > 0)
-	 {
-	   SetPageReserved(vmalloc_to_page((void *)adr));
-	   adr  += PAGE_SIZE;
-	   size -= PAGE_SIZE;
-	 }
-	return mem;
-}
-
-static void pwc_rvfree(void * mem, unsigned long size)
-{
-	unsigned long adr;
-
-	if (!mem)
-		return;
-
-	adr=(unsigned long) mem;
-	while ((long) size > 0)
-	 {
-	   ClearPageReserved(vmalloc_to_page((void *)adr));
-	   adr  += PAGE_SIZE;
-	   size -= PAGE_SIZE;
-	 }
-	vfree(mem);
-}
-
-
-
-
-static int pwc_allocate_buffers(struct pwc_device *pdev)
-{
-	int i, err;
-	void *kbuf;
-
-	PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
-
-	if (pdev == NULL)
-		return -ENXIO;
-
-	/* Allocate Isochronuous pipe buffers */
-	for (i = 0; i < MAX_ISO_BUFS; i++) {
-		if (pdev->sbuf[i].data == NULL) {
-			kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
-			if (kbuf == NULL) {
-				PWC_ERROR("Failed to allocate iso buffer %d.\n", i);
-				return -ENOMEM;
-			}
-			PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf);
-			pdev->sbuf[i].data = kbuf;
-		}
-	}
-
-	/* Allocate frame buffer structure */
-	if (pdev->fbuf == NULL) {
-		kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
-		if (kbuf == NULL) {
-			PWC_ERROR("Failed to allocate frame buffer structure.\n");
-			return -ENOMEM;
-		}
-		PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf);
-		pdev->fbuf = kbuf;
-	}
-
-	/* create frame buffers, and make circular ring */
-	for (i = 0; i < default_fbufs; i++) {
-		if (pdev->fbuf[i].data == NULL) {
-			kbuf = vzalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
-			if (kbuf == NULL) {
-				PWC_ERROR("Failed to allocate frame buffer %d.\n", i);
-				return -ENOMEM;
-			}
-			PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf);
-			pdev->fbuf[i].data = kbuf;
-		}
-	}
-
-	/* Allocate decompressor table space */
-	if (DEVICE_USE_CODEC1(pdev->type))
-		err = pwc_dec1_alloc(pdev);
-	else
-		err = pwc_dec23_alloc(pdev);
-
-	if (err) {
-		PWC_ERROR("Failed to allocate decompress table.\n");
-		return err;
-	}
-
-	/* Allocate image buffer; double buffer for mmap() */
-	kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image);
-	if (kbuf == NULL) {
-		PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n",
-				pwc_mbufs * pdev->len_per_image);
-		return -ENOMEM;
-	}
-	PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf);
-	pdev->image_data = kbuf;
-	for (i = 0; i < pwc_mbufs; i++) {
-		pdev->images[i].offset = i * pdev->len_per_image;
-		pdev->images[i].vma_use_count = 0;
-	}
-	for (; i < MAX_IMAGES; i++) {
-		pdev->images[i].offset = 0;
-	}
-
-	kbuf = NULL;
-
-	PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n");
-	return 0;
-}
-
-static void pwc_free_buffers(struct pwc_device *pdev)
-{
-	int i;
-
-	PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev);
-
-	if (pdev == NULL)
-		return;
-	/* Release Iso-pipe buffers */
-	for (i = 0; i < MAX_ISO_BUFS; i++)
-		if (pdev->sbuf[i].data != NULL) {
-			PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
-			kfree(pdev->sbuf[i].data);
-			pdev->sbuf[i].data = NULL;
-		}
-
-	/* The same for frame buffers */
-	if (pdev->fbuf != NULL) {
-		for (i = 0; i < default_fbufs; i++) {
-			if (pdev->fbuf[i].data != NULL) {
-				PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
-				vfree(pdev->fbuf[i].data);
-				pdev->fbuf[i].data = NULL;
-			}
-		}
-		kfree(pdev->fbuf);
-		pdev->fbuf = NULL;
-	}
-
-	/* Intermediate decompression buffer & tables */
-	if (pdev->decompress_data != NULL) {
-		PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data);
-		kfree(pdev->decompress_data);
-		pdev->decompress_data = NULL;
-	}
-
-	/* Release image buffers */
-	if (pdev->image_data != NULL) {
-		PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data);
-		pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image);
-	}
-	pdev->image_data = NULL;
-
-	PWC_DEBUG_MEMORY("Leaving free_buffers().\n");
-}
-
-/* The frame & image buffer mess.
-
-   Yes, this is a mess. Well, it used to be simple, but alas...  In this
-   module, 3 buffers schemes are used to get the data from the USB bus to
-   the user program. The first scheme involves the ISO buffers (called thus
-   since they transport ISO data from the USB controller), and not really
-   interesting. Suffices to say the data from this buffer is quickly
-   gathered in an interrupt handler (pwc_isoc_handler) and placed into the
-   frame buffer.
-
-   The frame buffer is the second scheme, and is the central element here.
-   It collects the data from a single frame from the camera (hence, the
-   name). Frames are delimited by the USB camera with a short USB packet,
-   so that's easy to detect. The frame buffers form a list that is filled
-   by the camera+USB controller and drained by the user process through
-   either read() or mmap().
-
-   The image buffer is the third scheme, in which frames are decompressed
-   and converted into planar format. For mmap() there is more than
-   one image buffer available.
-
-   The frame buffers provide the image buffering. In case the user process
-   is a bit slow, this introduces lag and some undesired side-effects.
-   The problem arises when the frame buffer is full. I used to drop the last
-   frame, which makes the data in the queue stale very quickly. But dropping
-   the frame at the head of the queue proved to be a litte bit more difficult.
-   I tried a circular linked scheme, but this introduced more problems than
-   it solved.
-
-   Because filling and draining are completely asynchronous processes, this
-   requires some fiddling with pointers and mutexes.
-
-   Eventually, I came up with a system with 2 lists: an 'empty' frame list
-   and a 'full' frame list:
-     * Initially, all frame buffers but one are on the 'empty' list; the one
-       remaining buffer is our initial fill frame.
-     * If a frame is needed for filling, we try to take it from the 'empty'
-       list, unless that list is empty, in which case we take the buffer at
-       the head of the 'full' list.
-     * When our fill buffer has been filled, it is appended to the 'full'
-       list.
-     * If a frame is needed by read() or mmap(), it is taken from the head of
-       the 'full' list, handled, and then appended to the 'empty' list. If no
-       buffer is present on the 'full' list, we wait.
-   The advantage is that the buffer that is currently being decompressed/
-   converted, is on neither list, and thus not in our way (any other scheme
-   I tried had the problem of old data lingering in the queue).
-
-   Whatever strategy you choose, it always remains a tradeoff: with more
-   frame buffers the chances of a missed frame are reduced. On the other
-   hand, on slower machines it introduces lag because the queue will
-   always be full.
- */
-
-/**
-  \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
- */
-static int pwc_next_fill_frame(struct pwc_device *pdev)
-{
-	int ret;
-	unsigned long flags;
-
-	ret = 0;
-	spin_lock_irqsave(&pdev->ptrlock, flags);
-	if (pdev->fill_frame != NULL) {
-		/* append to 'full' list */
-		if (pdev->full_frames == NULL) {
-			pdev->full_frames = pdev->fill_frame;
-			pdev->full_frames_tail = pdev->full_frames;
-		}
-		else {
-			pdev->full_frames_tail->next = pdev->fill_frame;
-			pdev->full_frames_tail = pdev->fill_frame;
-		}
-	}
-	if (pdev->empty_frames != NULL) {
-		/* We have empty frames available. That's easy */
-		pdev->fill_frame = pdev->empty_frames;
-		pdev->empty_frames = pdev->empty_frames->next;
-	}
-	else {
-		/* Hmm. Take it from the full list */
-		/* sanity check */
-		if (pdev->full_frames == NULL) {
-			PWC_ERROR("Neither empty or full frames available!\n");
-			spin_unlock_irqrestore(&pdev->ptrlock, flags);
-			return -EINVAL;
-		}
-		pdev->fill_frame = pdev->full_frames;
-		pdev->full_frames = pdev->full_frames->next;
-		ret = 1;
-	}
-	pdev->fill_frame->next = NULL;
-	spin_unlock_irqrestore(&pdev->ptrlock, flags);
-	return ret;
-}
-
-
-/**
-  \brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
-
-  If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
- */
-static void pwc_reset_buffers(struct pwc_device *pdev)
-{
-	int i;
-	unsigned long flags;
-
-	PWC_DEBUG_MEMORY(">> %s __enter__\n", __func__);
-
-	spin_lock_irqsave(&pdev->ptrlock, flags);
-	pdev->full_frames = NULL;
-	pdev->full_frames_tail = NULL;
-	for (i = 0; i < default_fbufs; i++) {
-		pdev->fbuf[i].filled = 0;
-		if (i > 0)
-			pdev->fbuf[i].next = &pdev->fbuf[i - 1];
-		else
-			pdev->fbuf->next = NULL;
-	}
-	pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
-	pdev->empty_frames_tail = pdev->fbuf;
-	pdev->read_frame = NULL;
-	pdev->fill_frame = pdev->empty_frames;
-	pdev->empty_frames = pdev->empty_frames->next;
-
-	pdev->image_read_pos = 0;
-	pdev->fill_image = 0;
-	spin_unlock_irqrestore(&pdev->ptrlock, flags);
-
-	PWC_DEBUG_MEMORY("<< %s __leaving__\n", __func__);
-}
-
-
-/**
-  \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
- */
-int pwc_handle_frame(struct pwc_device *pdev)
-{
-	int ret = 0;
-	unsigned long flags;
-
-	spin_lock_irqsave(&pdev->ptrlock, flags);
-	/* First grab our read_frame; this is removed from all lists, so
-	   we can release the lock after this without problems */
-	if (pdev->read_frame != NULL) {
-		/* This can't theoretically happen */
-		PWC_ERROR("Huh? Read frame still in use?\n");
-		spin_unlock_irqrestore(&pdev->ptrlock, flags);
-		return ret;
-	}
-
-
-	if (pdev->full_frames == NULL) {
-		PWC_ERROR("Woops. No frames ready.\n");
-	}
-	else {
-		pdev->read_frame = pdev->full_frames;
-		pdev->full_frames = pdev->full_frames->next;
-		pdev->read_frame->next = NULL;
-	}
-
-	if (pdev->read_frame != NULL) {
-		/* Decompression is a lengthy process, so it's outside of the lock.
-		   This gives the isoc_handler the opportunity to fill more frames
-		   in the mean time.
-		*/
-		spin_unlock_irqrestore(&pdev->ptrlock, flags);
-		ret = pwc_decompress(pdev);
-		spin_lock_irqsave(&pdev->ptrlock, flags);
-
-		/* We're done with read_buffer, tack it to the end of the empty buffer list */
-		if (pdev->empty_frames == NULL) {
-			pdev->empty_frames = pdev->read_frame;
-			pdev->empty_frames_tail = pdev->empty_frames;
-		}
-		else {
-			pdev->empty_frames_tail->next = pdev->read_frame;
-			pdev->empty_frames_tail = pdev->read_frame;
-		}
-		pdev->read_frame = NULL;
-	}
-	spin_unlock_irqrestore(&pdev->ptrlock, flags);
-	return ret;
-}
-
-/**
-  \brief Advance pointers of image buffer (after each user request)
-*/
-void pwc_next_image(struct pwc_device *pdev)
-{
-	pdev->image_used[pdev->fill_image] = 0;
-	pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;
-}
-
-/**
- * Print debug information when a frame is discarded because all of our buffer
- * is full
- */
-static void pwc_frame_dumped(struct pwc_device *pdev)
-{
-	pdev->vframes_dumped++;
-	if (pdev->vframe_count < FRAME_LOWMARK)
-		return;
-
-	if (pdev->vframes_dumped < 20)
-		PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count);
-	else if (pdev->vframes_dumped == 20)
-		PWC_DEBUG_FLOW("Dumping frame %d (last message)\n",
-				pdev->vframe_count);
+	buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
+	list_del(&buf->list);
+leave:
+	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+	return buf;
 }
 
 static void pwc_snapshot_button(struct pwc_device *pdev, int down)
@@ -575,9 +204,9 @@
 #endif
 }
 
-static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf)
+static void pwc_frame_complete(struct pwc_device *pdev)
 {
-	int awake = 0;
+	struct pwc_frame_buf *fbuf = pdev->fill_buf;
 
 	/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
 	   frames on the USB wire after an exposure change. This conditition is
@@ -589,7 +218,6 @@
 		if (ptr[1] == 1 && ptr[0] & 0x10) {
 			PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
 			pdev->drop_frames += 2;
-			pdev->vframes_error++;
 		}
 		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
 			pwc_snapshot_button(pdev, ptr[0] & 0x01);
@@ -612,8 +240,7 @@
 		   */
 		if (fbuf->filled == 4)
 			pdev->drop_frames++;
-	}
-	else if (pdev->type == 740 || pdev->type == 720) {
+	} else if (pdev->type == 740 || pdev->type == 720) {
 		unsigned char *ptr = (unsigned char *)fbuf->data;
 		if ((ptr[0] ^ pdev->vmirror) & 0x01) {
 			pwc_snapshot_button(pdev, ptr[0] & 0x01);
@@ -621,33 +248,23 @@
 		pdev->vmirror = ptr[0] & 0x03;
 	}
 
-	/* In case we were instructed to drop the frame, do so silently.
-	   The buffer pointers are not updated either (but the counters are reset below).
-	   */
-	if (pdev->drop_frames > 0)
+	/* In case we were instructed to drop the frame, do so silently. */
+	if (pdev->drop_frames > 0) {
 		pdev->drop_frames--;
-	else {
+	} else {
 		/* Check for underflow first */
 		if (fbuf->filled < pdev->frame_total_size) {
 			PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
 				       " discarded.\n", fbuf->filled);
-			pdev->vframes_error++;
-		}
-		else {
-			/* Send only once per EOF */
-			awake = 1; /* delay wake_ups */
-
-			/* Find our next frame to fill. This will always succeed, since we
-			 * nick a frame from either empty or full list, but if we had to
-			 * take it from the full list, it means a frame got dropped.
-			 */
-			if (pwc_next_fill_frame(pdev))
-				pwc_frame_dumped(pdev);
-
+		} else {
+			fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+			fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
+			vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+			pdev->fill_buf = NULL;
+			pdev->vsync = 0;
 		}
 	} /* !drop_frames */
 	pdev->vframe_count++;
-	return awake;
 }
 
 /* This gets called for the Isochronous pipe (video). This is done in
@@ -655,24 +272,20 @@
  */
 static void pwc_isoc_handler(struct urb *urb)
 {
-	struct pwc_device *pdev;
+	struct pwc_device *pdev = (struct pwc_device *)urb->context;
 	int i, fst, flen;
-	int awake;
-	struct pwc_frame_buf *fbuf;
-	unsigned char *fillptr = NULL, *iso_buf = NULL;
+	unsigned char *iso_buf = NULL;
 
-	awake = 0;
-	pdev = (struct pwc_device *)urb->context;
-	if (pdev == NULL) {
-		PWC_ERROR("isoc_handler() called with NULL device?!\n");
-		return;
-	}
-
-	if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+	if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
+	    urb->status == -ESHUTDOWN) {
 		PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
 		return;
 	}
-	if (urb->status != -EINPROGRESS && urb->status != 0) {
+
+	if (pdev->fill_buf == NULL)
+		pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+
+	if (urb->status != 0) {
 		const char *errmsg;
 
 		errmsg = "Unknown";
@@ -684,29 +297,21 @@
 			case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)"; break;
 			case -ETIME:		errmsg = "Device does not respond"; break;
 		}
-		PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
-		/* Give up after a number of contiguous errors on the USB bus.
-		   Appearantly something is wrong so we simulate an unplug event.
-		 */
+		PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
+			  urb->status, errmsg);
+		/* Give up after a number of contiguous errors */
 		if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
 		{
-			PWC_INFO("Too many ISOC errors, bailing out.\n");
-			pdev->error_status = EIO;
-			awake = 1;
-			wake_up_interruptible(&pdev->frameq);
+			PWC_ERROR("Too many ISOC errors, bailing out.\n");
+			if (pdev->fill_buf) {
+				vb2_buffer_done(&pdev->fill_buf->vb,
+						VB2_BUF_STATE_ERROR);
+				pdev->fill_buf = NULL;
+			}
 		}
-		goto handler_end; // ugly, but practical
-	}
-
-	fbuf = pdev->fill_frame;
-	if (fbuf == NULL) {
-		PWC_ERROR("pwc_isoc_handler without valid fill frame.\n");
-		awake = 1;
+		pdev->vsync = 0; /* Drop the current frame */
 		goto handler_end;
 	}
-	else {
-		fillptr = fbuf->data + fbuf->filled;
-	}
 
 	/* Reset ISOC error counter. We did get here, after all. */
 	pdev->visoc_errors = 0;
@@ -720,89 +325,73 @@
 		fst  = urb->iso_frame_desc[i].status;
 		flen = urb->iso_frame_desc[i].actual_length;
 		iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-		if (fst == 0) {
-			if (flen > 0) { /* if valid data... */
-				if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */
-					pdev->vsync = 2;
+		if (fst != 0) {
+			PWC_ERROR("Iso frame %d has error %d\n", i, fst);
+			continue;
+		}
+		if (flen > 0 && pdev->vsync) {
+			struct pwc_frame_buf *fbuf = pdev->fill_buf;
 
-					/* ...copy data to frame buffer, if possible */
-					if (flen + fbuf->filled > pdev->frame_total_size) {
-						PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
-						pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
-						pdev->vframes_error++;
-					}
-					else {
-						memmove(fillptr, iso_buf, flen);
-						fillptr += flen;
-					}
-				}
-				fbuf->filled += flen;
-			} /* ..flen > 0 */
-
-			if (flen < pdev->vlast_packet_size) {
-				/* Shorter packet... We probably have the end of an image-frame;
-				   wake up read() process and let select()/poll() do something.
-				   Decompression is done in user time over there.
-				   */
-				if (pdev->vsync == 2) {
-					if (pwc_rcv_short_packet(pdev, fbuf)) {
-						awake = 1;
-						fbuf = pdev->fill_frame;
-					}
-				}
-				fbuf->filled = 0;
-				fillptr = fbuf->data;
-				pdev->vsync = 1;
+			if (pdev->vsync == 1) {
+				do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
+				pdev->vsync = 2;
 			}
 
-			pdev->vlast_packet_size = flen;
-		} /* ..status == 0 */
-		else {
-			/* This is normally not interesting to the user, unless
-			 * you are really debugging something, default = 0 */
-			static int iso_error;
-			iso_error++;
-			if (iso_error < 20)
-				PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst);
+			if (flen + fbuf->filled > pdev->frame_total_size) {
+				PWC_ERROR("Frame overflow (%d > %d)\n",
+					  flen + fbuf->filled,
+					  pdev->frame_total_size);
+				pdev->vsync = 0; /* Let's wait for an EOF */
+			} else {
+				memcpy(fbuf->data + fbuf->filled, iso_buf,
+				       flen);
+				fbuf->filled += flen;
+			}
 		}
+		if (flen < pdev->vlast_packet_size) {
+			/* Shorter packet... end of frame */
+			if (pdev->vsync == 2)
+				pwc_frame_complete(pdev);
+			if (pdev->fill_buf == NULL)
+				pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+			if (pdev->fill_buf) {
+				pdev->fill_buf->filled = 0;
+				pdev->vsync = 1;
+			}
+		}
+		pdev->vlast_packet_size = flen;
 	}
 
 handler_end:
-	if (awake)
-		wake_up_interruptible(&pdev->frameq);
-
-	urb->dev = pdev->udev;
 	i = usb_submit_urb(urb, GFP_ATOMIC);
 	if (i != 0)
 		PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
 }
 
-
-int pwc_isoc_init(struct pwc_device *pdev)
+static int pwc_isoc_init(struct pwc_device *pdev)
 {
 	struct usb_device *udev;
 	struct urb *urb;
 	int i, j, ret;
-
 	struct usb_interface *intf;
 	struct usb_host_interface *idesc = NULL;
 
-	if (pdev == NULL)
-		return -EFAULT;
 	if (pdev->iso_init)
 		return 0;
+
 	pdev->vsync = 0;
+	pdev->vlast_packet_size = 0;
+	pdev->fill_buf = NULL;
+	pdev->vframe_count = 0;
+	pdev->visoc_errors = 0;
 	udev = pdev->udev;
 
 	/* Get the current alternate interface, adjust packet size */
-	if (!udev->actconfig)
-		return -EFAULT;
 	intf = usb_ifnum_to_if(udev, 0);
 	if (intf)
 		idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
-
 	if (!idesc)
-		return -EFAULT;
+		return -EIO;
 
 	/* Search video endpoint */
 	pdev->vmax_packet_size = -1;
@@ -825,34 +414,32 @@
 	if (ret < 0)
 		return ret;
 
+	/* Allocate and init Isochronuous urbs */
 	for (i = 0; i < MAX_ISO_BUFS; i++) {
 		urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
 		if (urb == NULL) {
 			PWC_ERROR("Failed to allocate urb %d\n", i);
-			ret = -ENOMEM;
-			break;
+			pdev->iso_init = 1;
+			pwc_isoc_cleanup(pdev);
+			return -ENOMEM;
 		}
-		pdev->sbuf[i].urb = urb;
+		pdev->urbs[i] = urb;
 		PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
-	}
-	if (ret) {
-		/* De-allocate in reverse order */
-		while (i--) {
-			usb_free_urb(pdev->sbuf[i].urb);
-			pdev->sbuf[i].urb = NULL;
-		}
-		return ret;
-	}
-
-	/* init URB structure */
-	for (i = 0; i < MAX_ISO_BUFS; i++) {
-		urb = pdev->sbuf[i].urb;
 
 		urb->interval = 1; // devik
 		urb->dev = udev;
 		urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
-		urb->transfer_flags = URB_ISO_ASAP;
-		urb->transfer_buffer = pdev->sbuf[i].data;
+		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		urb->transfer_buffer = usb_alloc_coherent(udev,
+							  ISO_BUFFER_SIZE,
+							  GFP_KERNEL,
+							  &urb->transfer_dma);
+		if (urb->transfer_buffer == NULL) {
+			PWC_ERROR("Failed to allocate urb buffer %d\n", i);
+			pdev->iso_init = 1;
+			pwc_isoc_cleanup(pdev);
+			return -ENOMEM;
+		}
 		urb->transfer_buffer_length = ISO_BUFFER_SIZE;
 		urb->complete = pwc_isoc_handler;
 		urb->context = pdev;
@@ -866,14 +453,14 @@
 
 	/* link */
 	for (i = 0; i < MAX_ISO_BUFS; i++) {
-		ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
+		ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
 		if (ret) {
 			PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
 			pdev->iso_init = 1;
 			pwc_isoc_cleanup(pdev);
 			return ret;
 		}
-		PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+		PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
 	}
 
 	/* All is done... */
@@ -888,12 +475,9 @@
 
 	/* Unlinking ISOC buffers one by one */
 	for (i = 0; i < MAX_ISO_BUFS; i++) {
-		struct urb *urb;
-
-		urb = pdev->sbuf[i].urb;
-		if (urb) {
-			PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb);
-			usb_kill_urb(urb);
+		if (pdev->urbs[i]) {
+			PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
+			usb_kill_urb(pdev->urbs[i]);
 		}
 	}
 }
@@ -904,40 +488,51 @@
 
 	/* Freeing ISOC buffers one by one */
 	for (i = 0; i < MAX_ISO_BUFS; i++) {
-		struct urb *urb;
-
-		urb = pdev->sbuf[i].urb;
-		if (urb) {
+		if (pdev->urbs[i]) {
 			PWC_DEBUG_MEMORY("Freeing URB\n");
-			usb_free_urb(urb);
-			pdev->sbuf[i].urb = NULL;
+			if (pdev->urbs[i]->transfer_buffer) {
+				usb_free_coherent(pdev->udev,
+					pdev->urbs[i]->transfer_buffer_length,
+					pdev->urbs[i]->transfer_buffer,
+					pdev->urbs[i]->transfer_dma);
+			}
+			usb_free_urb(pdev->urbs[i]);
+			pdev->urbs[i] = NULL;
 		}
 	}
 }
 
-void pwc_isoc_cleanup(struct pwc_device *pdev)
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
 {
 	PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
-	if (pdev == NULL)
-		return;
+
 	if (pdev->iso_init == 0)
 		return;
 
 	pwc_iso_stop(pdev);
 	pwc_iso_free(pdev);
-
-	/* Stop camera, but only if we are sure the camera is still there (unplug
-	   is signalled by EPIPE)
-	 */
-	if (pdev->error_status != EPIPE) {
-		PWC_DEBUG_OPEN("Setting alternate interface 0.\n");
-		usb_set_interface(pdev->udev, 0, 0);
-	}
+	usb_set_interface(pdev->udev, 0, 0);
 
 	pdev->iso_init = 0;
 	PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
 }
 
+/*
+ * Release all queued buffers, no need to take queued_bufs_lock, since all
+ * iso urbs have been killed when we're called so pwc_isoc_handler won't run.
+ */
+static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
+{
+	while (!list_empty(&pdev->queued_bufs)) {
+		struct pwc_frame_buf *buf;
+
+		buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
+				 list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+}
+
 /*********
  * sysfs
  *********/
@@ -1051,98 +646,15 @@
 
 static int pwc_video_open(struct file *file)
 {
-	int i, ret;
 	struct video_device *vdev = video_devdata(file);
 	struct pwc_device *pdev;
 
 	PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev);
 
 	pdev = video_get_drvdata(vdev);
-	BUG_ON(!pdev);
-	if (pdev->vopen) {
-		PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n");
-		return -EBUSY;
-	}
+	if (!pdev->udev)
+		return -ENODEV;
 
-	pwc_construct(pdev); /* set min/max sizes correct */
-	if (!pdev->usb_init) {
-		PWC_DEBUG_OPEN("Doing first time initialization.\n");
-		pdev->usb_init = 1;
-
-		/* Query sensor type */
-		ret = pwc_get_cmos_sensor(pdev, &i);
-		if (ret >= 0)
-		{
-			PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
-					pdev->vdev.name,
-					pwc_sensor_type_to_string(i), i);
-		}
-	}
-
-	/* Turn on camera */
-	if (power_save) {
-		i = pwc_camera_power(pdev, 1);
-		if (i < 0)
-			PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i);
-	}
-	/* Set LED on/off time */
-	if (pwc_set_leds(pdev, led_on, led_off) < 0)
-		PWC_DEBUG_OPEN("Failed to set LED on/off time.\n");
-
-
-	/* So far, so good. Allocate memory. */
-	i = pwc_allocate_buffers(pdev);
-	if (i < 0) {
-		PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n");
-		pwc_free_buffers(pdev);
-		return i;
-	}
-
-	/* Reset buffers & parameters */
-	pwc_reset_buffers(pdev);
-	for (i = 0; i < pwc_mbufs; i++)
-		pdev->image_used[i] = 0;
-	pdev->vframe_count = 0;
-	pdev->vframes_dumped = 0;
-	pdev->vframes_error = 0;
-	pdev->visoc_errors = 0;
-	pdev->error_status = 0;
-	pwc_construct(pdev); /* set min/max sizes correct */
-
-	/* Set some defaults */
-	pdev->vsnapshot = 0;
-
-	/* Set video size, first try the last used video size
-	   (or the default one); if that fails try QCIF/10 or QSIF/10;
-	   it that fails too, give up.
-	 */
-	i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
-	if (i)	{
-		unsigned int default_resolution;
-		PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n");
-		if (pdev->type>= 730)
-			default_resolution = PSZ_QSIF;
-		else
-			default_resolution = PSZ_QCIF;
-
-		i = pwc_set_video_mode(pdev,
-				       pwc_image_sizes[default_resolution].x,
-				       pwc_image_sizes[default_resolution].y,
-				       10,
-				       pdev->vcompression,
-				       0);
-	}
-	if (i) {
-		PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n");
-		pwc_free_buffers(pdev);
-		return i;
-	}
-
-	/* Initialize the webcam to sane value */
-	pwc_set_brightness(pdev, 0x7fff);
-	pwc_set_agc(pdev, 1, 0);
-
-	pdev->vopen++;
 	file->private_data = vdev;
 	PWC_DEBUG_OPEN("<< video_open() returns 0.\n");
 	return 0;
@@ -1158,239 +670,211 @@
 		if (device_hint[hint].pdev == pdev)
 			device_hint[hint].pdev = NULL;
 
+	/* Free intermediate decompression buffer & tables */
+	if (pdev->decompress_data != NULL) {
+		PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n",
+				 pdev->decompress_data);
+		kfree(pdev->decompress_data);
+		pdev->decompress_data = NULL;
+	}
+
+	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+
 	kfree(pdev);
 }
 
-/* Note that all cleanup is done in the reverse order as in _open */
 static int pwc_video_close(struct file *file)
 {
 	struct video_device *vdev = file->private_data;
 	struct pwc_device *pdev;
-	int i;
 
 	PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
 
 	pdev = video_get_drvdata(vdev);
-	if (pdev->vopen == 0)
-		PWC_DEBUG_MODULE("video_close() called on closed device?\n");
-
-	/* Dump statistics, but only if a reasonable amount of frames were
-	   processed (to prevent endless log-entries in case of snap-shot
-	   programs)
-	 */
-	if (pdev->vframe_count > 20)
-		PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
-
-	if (DEVICE_USE_CODEC1(pdev->type))
-	    pwc_dec1_exit();
-	else
-	    pwc_dec23_exit();
-
-	pwc_isoc_cleanup(pdev);
-	pwc_free_buffers(pdev);
-
-	/* Turn off LEDS and power down camera, but only when not unplugged */
-	if (!pdev->unplugged) {
-		/* Turn LEDs off */
-		if (pwc_set_leds(pdev, 0, 0) < 0)
-			PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
-		if (power_save) {
-			i = pwc_camera_power(pdev, 0);
-			if (i < 0)
-				PWC_ERROR("Failed to power down camera (%d)\n", i);
-		}
-		pdev->vopen--;
-		PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
+	if (pdev->capt_file == file) {
+		vb2_queue_release(&pdev->vb_queue);
+		pdev->capt_file = NULL;
 	}
 
+	PWC_DEBUG_OPEN("<< video_close()\n");
 	return 0;
 }
 
-/*
- *	FIXME: what about two parallel reads ????
- *      ANSWER: Not supported. You can't open the device more than once,
-		despite what the V4L1 interface says. First, I don't see
-		the need, second there's no mechanism of alerting the
-		2nd/3rd/... process of events like changing image size.
-		And I don't see the point of blocking that for the
-		2nd/3rd/... process.
-		In multi-threaded environments reading parallel from any
-		device is tricky anyhow.
- */
-
 static ssize_t pwc_video_read(struct file *file, char __user *buf,
-			  size_t count, loff_t *ppos)
+			      size_t count, loff_t *ppos)
 {
 	struct video_device *vdev = file->private_data;
-	struct pwc_device *pdev;
-	int noblock = file->f_flags & O_NONBLOCK;
-	DECLARE_WAITQUEUE(wait, current);
-	int bytes_to_read, rv = 0;
-	void *image_buffer_addr;
+	struct pwc_device *pdev = video_get_drvdata(vdev);
 
-	PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n",
-			vdev, buf, count);
-	if (vdev == NULL)
-		return -EFAULT;
-	pdev = video_get_drvdata(vdev);
-	if (pdev == NULL)
-		return -EFAULT;
+	if (!pdev->udev)
+		return -ENODEV;
 
-	if (pdev->error_status) {
-		rv = -pdev->error_status; /* Something happened, report what. */
-		goto err_out;
-	}
+	if (pdev->capt_file != NULL &&
+	    pdev->capt_file != file)
+		return -EBUSY;
 
-	/* Start the stream (if not already started) */
-	rv = pwc_isoc_init(pdev);
-	if (rv)
-		goto err_out;
+	pdev->capt_file = file;
 
-	/* In case we're doing partial reads, we don't have to wait for a frame */
-	if (pdev->image_read_pos == 0) {
-		/* Do wait queueing according to the (doc)book */
-		add_wait_queue(&pdev->frameq, &wait);
-		while (pdev->full_frames == NULL) {
-			/* Check for unplugged/etc. here */
-			if (pdev->error_status) {
-				remove_wait_queue(&pdev->frameq, &wait);
-				set_current_state(TASK_RUNNING);
-				rv = -pdev->error_status ;
-				goto err_out;
-			}
-			if (noblock) {
-				remove_wait_queue(&pdev->frameq, &wait);
-				set_current_state(TASK_RUNNING);
-				rv = -EWOULDBLOCK;
-				goto err_out;
-			}
-			if (signal_pending(current)) {
-				remove_wait_queue(&pdev->frameq, &wait);
-				set_current_state(TASK_RUNNING);
-				rv = -ERESTARTSYS;
-				goto err_out;
-			}
-			mutex_unlock(&pdev->modlock);
-			schedule();
-			set_current_state(TASK_INTERRUPTIBLE);
-			mutex_lock(&pdev->modlock);
-		}
-		remove_wait_queue(&pdev->frameq, &wait);
-		set_current_state(TASK_RUNNING);
-
-		/* Decompress and release frame */
-		if (pwc_handle_frame(pdev)) {
-			rv = -EFAULT;
-			goto err_out;
-		}
-	}
-
-	PWC_DEBUG_READ("Copying data to user space.\n");
-	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
-		bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame);
-	else
-		bytes_to_read = pdev->view.size;
-
-	/* copy bytes to user space; we allow for partial reads */
-	if (count + pdev->image_read_pos > bytes_to_read)
-		count = bytes_to_read - pdev->image_read_pos;
-	image_buffer_addr = pdev->image_data;
-	image_buffer_addr += pdev->images[pdev->fill_image].offset;
-	image_buffer_addr += pdev->image_read_pos;
-	if (copy_to_user(buf, image_buffer_addr, count)) {
-		rv = -EFAULT;
-		goto err_out;
-	}
-	pdev->image_read_pos += count;
-	if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
-		pdev->image_read_pos = 0;
-		pwc_next_image(pdev);
-	}
-	return count;
-err_out:
-	return rv;
+	return vb2_read(&pdev->vb_queue, buf, count, ppos,
+			file->f_flags & O_NONBLOCK);
 }
 
 static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
 {
 	struct video_device *vdev = file->private_data;
-	struct pwc_device *pdev;
-	int ret;
+	struct pwc_device *pdev = video_get_drvdata(vdev);
 
-	if (vdev == NULL)
-		return -EFAULT;
-	pdev = video_get_drvdata(vdev);
-	if (pdev == NULL)
-		return -EFAULT;
+	if (!pdev->udev)
+		return POLL_ERR;
 
-	/* Start the stream (if not already started) */
-	ret = pwc_isoc_init(pdev);
-	if (ret)
-		return ret;
-
-	poll_wait(file, &pdev->frameq, wait);
-	if (pdev->error_status)
-		return POLLERR;
-	if (pdev->full_frames != NULL) /* we have frames waiting */
-		return (POLLIN | POLLRDNORM);
-
-	return 0;
+	return vb2_poll(&pdev->vb_queue, file, wait);
 }
 
 static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct video_device *vdev = file->private_data;
-	struct pwc_device *pdev;
-	unsigned long start;
-	unsigned long size;
-	unsigned long page, pos = 0;
-	int index;
+	struct pwc_device *pdev = video_get_drvdata(vdev);
 
-	PWC_DEBUG_MEMORY(">> %s\n", __func__);
-	pdev = video_get_drvdata(vdev);
-	size = vma->vm_end - vma->vm_start;
-	start = vma->vm_start;
+	if (pdev->capt_file != file)
+		return -EBUSY;
 
-	/* Find the idx buffer for this mapping */
-	for (index = 0; index < pwc_mbufs; index++) {
-		pos = pdev->images[index].offset;
-		if ((pos>>PAGE_SHIFT) == vma->vm_pgoff)
-			break;
-	}
-	if (index == MAX_IMAGES)
-		return -EINVAL;
-	if (index == 0) {
-		/*
-		 * Special case for v4l1. In v4l1, we map only one big buffer,
-		 * but in v4l2 each buffer is mapped
-		 */
-		unsigned long total_size;
-		total_size = pwc_mbufs * pdev->len_per_image;
-		if (size != pdev->len_per_image && size != total_size) {
-			PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n",
-				   size, pdev->len_per_image, total_size);
-			return -EINVAL;
-		}
-	} else if (size > pdev->len_per_image)
-		return -EINVAL;
+	return vb2_mmap(&pdev->vb_queue, vma);
+}
 
-	vma->vm_flags |= VM_IO;	/* from 2.6.9-acX */
+/***************************************************************************/
+/* Videobuf2 operations */
 
-	pos += (unsigned long)pdev->image_data;
-	while (size > 0) {
-		page = vmalloc_to_pfn((void *)pos);
-		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
-			return -EAGAIN;
-		start += PAGE_SIZE;
-		pos += PAGE_SIZE;
-		if (size > PAGE_SIZE)
-			size -= PAGE_SIZE;
-		else
-			size = 0;
-	}
+static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned long sizes[],
+				void *alloc_ctxs[])
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+	if (*nbuffers < MIN_FRAMES)
+		*nbuffers = MIN_FRAMES;
+	else if (*nbuffers > MAX_FRAMES)
+		*nbuffers = MAX_FRAMES;
+
+	*nplanes = 1;
+
+	sizes[0] = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
+
 	return 0;
 }
 
+static int buffer_init(struct vb2_buffer *vb)
+{
+	struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+	/* need vmalloc since frame buffer > 128K */
+	buf->data = vzalloc(PWC_FRAME_SIZE);
+	if (buf->data == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+
+	/* Don't allow queing new buffers after device disconnection */
+	if (!pdev->udev)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int buffer_finish(struct vb2_buffer *vb)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+	struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+	/*
+	 * Application has called dqbuf and is getting back a buffer we've
+	 * filled, take the pwc data we've stored in buf->data and decompress
+	 * it into a usable format, storing the result in the vb2_buffer
+	 */
+	return pwc_decompress(pdev, buf);
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+	struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+	vfree(buf->data);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+	struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+	list_add_tail(&buf->list, &pdev->queued_bufs);
+	spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+	if (!pdev->udev)
+		return -ENODEV;
+
+	/* Turn on camera and set LEDS on */
+	pwc_camera_power(pdev, 1);
+	if (pdev->power_save) {
+		/* Restore video mode */
+		pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y,
+				   pdev->vframes, pdev->vcompression,
+				   pdev->vsnapshot);
+	}
+	pwc_set_leds(pdev, led_on, led_off);
+
+	return pwc_isoc_init(pdev);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+	if (pdev->udev) {
+		pwc_set_leds(pdev, 0, 0);
+		pwc_camera_power(pdev, 0);
+		pwc_isoc_cleanup(pdev);
+	}
+	pwc_cleanup_queued_bufs(pdev);
+
+	return 0;
+}
+
+static void pwc_lock(struct vb2_queue *vq)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vq);
+	mutex_lock(&pdev->modlock);
+}
+
+static void pwc_unlock(struct vb2_queue *vq)
+{
+	struct pwc_device *pdev = vb2_get_drv_priv(vq);
+	mutex_unlock(&pdev->modlock);
+}
+
+static struct vb2_ops pwc_vb_queue_ops = {
+	.queue_setup		= queue_setup,
+	.buf_init		= buffer_init,
+	.buf_prepare		= buffer_prepare,
+	.buf_finish		= buffer_finish,
+	.buf_cleanup		= buffer_cleanup,
+	.buf_queue		= buffer_queue,
+	.start_streaming	= start_streaming,
+	.stop_streaming		= stop_streaming,
+	.wait_prepare		= pwc_unlock,
+	.wait_finish		= pwc_lock,
+};
+
 /***************************************************************************/
 /* USB functions */
 
@@ -1406,6 +890,7 @@
 	int hint, rc;
 	int features = 0;
 	int video_nr = -1; /* default: use next available device */
+	int my_power_save = power_save;
 	char serial_number[30], *name;
 
 	vendor_id = le16_to_cpu(udev->descriptor.idVendor);
@@ -1513,6 +998,8 @@
 			PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
 			name = "Logitech QuickCam Pro 4000";
 			type_id = 740; /* CCD sensor */
+			if (my_power_save == -1)
+				my_power_save = 1;
 			break;
 		case 0x08b3:
 			PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
@@ -1523,12 +1010,15 @@
 			PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
 			name = "Logitech QuickCam Zoom";
 			type_id = 740; /* CCD sensor */
-			power_save = 1;
+			if (my_power_save == -1)
+				my_power_save = 1;
 			break;
 		case 0x08b5:
 			PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
 			name = "Logitech QuickCam Orbit";
 			type_id = 740; /* CCD sensor */
+			if (my_power_save == -1)
+				my_power_save = 1;
 			features |= FEATURE_MOTOR_PANTILT;
 			break;
 		case 0x08b6:
@@ -1583,6 +1073,8 @@
 			PWC_INFO("Creative Labs Webcam 5 detected.\n");
 			name = "Creative Labs Webcam 5";
 			type_id = 730;
+			if (my_power_save == -1)
+				my_power_save = 1;
 			break;
 		case 0x4011:
 			PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
@@ -1640,6 +1132,9 @@
 	else
 		return -ENODEV; /* Not any of the know types; but the list keeps growing. */
 
+	if (my_power_save == -1)
+		my_power_save = 0;
+
 	memset(serial_number, 0, 30);
 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
 	PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
@@ -1654,7 +1149,6 @@
 		return -ENOMEM;
 	}
 	pdev->type = type_id;
-	pdev->vsize = default_size;
 	pdev->vframes = default_fps;
 	strcpy(pdev->serial, serial_number);
 	pdev->features = features;
@@ -1668,13 +1162,26 @@
 		pdev->angle_range.tilt_min = -3000;
 		pdev->angle_range.tilt_max =  2500;
 	}
+	pwc_construct(pdev); /* set min/max sizes correct */
 
 	mutex_init(&pdev->modlock);
-	spin_lock_init(&pdev->ptrlock);
+	mutex_init(&pdev->udevlock);
+	spin_lock_init(&pdev->queued_bufs_lock);
+	INIT_LIST_HEAD(&pdev->queued_bufs);
 
 	pdev->udev = udev;
-	init_waitqueue_head(&pdev->frameq);
 	pdev->vcompression = pwc_preferred_compression;
+	pdev->power_save = my_power_save;
+
+	/* Init videobuf2 queue structure */
+	memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue));
+	pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+	pdev->vb_queue.drv_priv = pdev;
+	pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
+	pdev->vb_queue.ops = &pwc_vb_queue_ops;
+	pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	vb2_queue_init(&pdev->vb_queue);
 
 	/* Init video_device structure */
 	memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
@@ -1707,14 +1214,40 @@
 	PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
 	usb_set_intfdata(intf, pdev);
 
+#ifdef CONFIG_USB_PWC_DEBUG
+	/* Query sensor type */
+	if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
+		PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+				pdev->vdev.name,
+				pwc_sensor_type_to_string(rc), rc);
+	}
+#endif
+
 	/* Set the leds off */
 	pwc_set_leds(pdev, 0, 0);
+
+	/* Setup intial videomode */
+	rc = pwc_set_video_mode(pdev, pdev->view_max.x, pdev->view_max.y,
+				pdev->vframes, pdev->vcompression, 0);
+	if (rc)
+		goto err_free_mem;
+
+	/* Register controls (and read default values from camera */
+	rc = pwc_init_controls(pdev);
+	if (rc) {
+		PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
+		goto err_free_mem;
+	}
+
+	pdev->vdev.ctrl_handler = &pdev->ctrl_handler;
+
+	/* And powerdown the camera until streaming starts */
 	pwc_camera_power(pdev, 0);
 
 	rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
 	if (rc < 0) {
 		PWC_ERROR("Failed to register as video device (%d).\n", rc);
-		goto err_free_mem;
+		goto err_free_controls;
 	}
 	rc = pwc_create_sysfs_files(pdev);
 	if (rc)
@@ -1757,7 +1290,10 @@
 	if (hint < MAX_DEV_HINTS)
 		device_hint[hint].pdev = NULL;
 	video_unregister_device(&pdev->vdev);
+err_free_controls:
+	v4l2_ctrl_handler_free(&pdev->ctrl_handler);
 err_free_mem:
+	usb_set_intfdata(intf, NULL);
 	kfree(pdev);
 	return rc;
 }
@@ -1767,33 +1303,17 @@
 {
 	struct pwc_device *pdev  = usb_get_intfdata(intf);
 
+	mutex_lock(&pdev->udevlock);
 	mutex_lock(&pdev->modlock);
-	usb_set_intfdata (intf, NULL);
-	if (pdev == NULL) {
-		PWC_ERROR("pwc_disconnect() Called without private pointer.\n");
-		goto disconnect_out;
-	}
-	if (pdev->udev == NULL) {
-		PWC_ERROR("pwc_disconnect() already called for %p\n", pdev);
-		goto disconnect_out;
-	}
-	if (pdev->udev != interface_to_usbdev(intf)) {
-		PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
-		goto disconnect_out;
-	}
 
-	/* We got unplugged; this is signalled by an EPIPE error code */
-	pdev->error_status = EPIPE;
-	pdev->unplugged = 1;
-
-	/* Alert waiting processes */
-	wake_up_interruptible(&pdev->frameq);
-
+	usb_set_intfdata(intf, NULL);
 	/* No need to keep the urbs around after disconnection */
 	pwc_isoc_cleanup(pdev);
+	pwc_cleanup_queued_bufs(pdev);
+	pdev->udev = NULL;
 
-disconnect_out:
 	mutex_unlock(&pdev->modlock);
+	mutex_unlock(&pdev->udevlock);
 
 	pwc_remove_sysfs_files(pdev);
 	video_unregister_device(&pdev->vdev);
@@ -1809,36 +1329,27 @@
  * Initialization code & module stuff
  */
 
-static char *size;
 static int fps;
-static int fbufs;
-static int mbufs;
 static int compression = -1;
 static int leds[2] = { -1, -1 };
 static unsigned int leds_nargs;
 static char *dev_hint[MAX_DEV_HINTS];
 static unsigned int dev_hint_nargs;
 
-module_param(size, charp, 0444);
 module_param(fps, int, 0444);
-module_param(fbufs, int, 0444);
-module_param(mbufs, int, 0444);
 #ifdef CONFIG_USB_PWC_DEBUG
 module_param_named(trace, pwc_trace, int, 0644);
 #endif
-module_param(power_save, int, 0444);
+module_param(power_save, int, 0644);
 module_param(compression, int, 0444);
 module_param_array(leds, int, &leds_nargs, 0444);
 module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
 
-MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
 MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
-MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
-MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
 #ifdef CONFIG_USB_PWC_DEBUG
 MODULE_PARM_DESC(trace, "For debugging purposes");
 #endif
-MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
+MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
 MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
 MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
 MODULE_PARM_DESC(dev_hint, "Device node hints");
@@ -1851,14 +1362,19 @@
 
 static int __init usb_pwc_init(void)
 {
-	int i, sz;
-	char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
+	int i;
 
+#ifdef CONFIG_USB_PWC_DEBUG
 	PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n");
 	PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
 	PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
 	PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
 
+	if (pwc_trace >= 0) {
+		PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
+	}
+#endif
+
 	if (fps) {
 		if (fps < 4 || fps > 30) {
 			PWC_ERROR("Framerate out of bounds (4-30).\n");
@@ -1868,41 +1384,6 @@
 		PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps);
 	}
 
-	if (size) {
-		/* string; try matching with array */
-		for (sz = 0; sz < PSZ_MAX; sz++) {
-			if (!strcmp(sizenames[sz], size)) { /* Found! */
-				default_size = sz;
-				break;
-			}
-		}
-		if (sz == PSZ_MAX) {
-			PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
-			return -EINVAL;
-		}
-		PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
-	}
-	if (mbufs) {
-		if (mbufs < 1 || mbufs > MAX_IMAGES) {
-			PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
-			return -EINVAL;
-		}
-		pwc_mbufs = mbufs;
-		PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs);
-	}
-	if (fbufs) {
-		if (fbufs < 2 || fbufs > MAX_FRAMES) {
-			PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
-			return -EINVAL;
-		}
-		default_fbufs = fbufs;
-		PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs);
-	}
-#ifdef CONFIG_USB_PWC_DEBUG
-	if (pwc_trace >= 0) {
-		PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
-	}
-#endif
 	if (compression >= 0) {
 		if (compression > 3) {
 			PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
@@ -1911,8 +1392,6 @@
 		pwc_preferred_compression = compression;
 		PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression);
 	}
-	if (power_save)
-		PWC_DEBUG_MODULE("Enabling power save on open/close.\n");
 	if (leds[0] >= 0)
 		led_on = leds[0];
 	if (leds[1] >= 0)
diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h
deleted file mode 100644
index 8c0cae7..0000000
--- a/drivers/media/video/pwc/pwc-ioctl.h
+++ /dev/null
@@ -1,323 +0,0 @@
-#ifndef PWC_IOCTL_H
-#define PWC_IOCTL_H
-
-/* (C) 2001-2004 Nemosoft Unv.
-   (C) 2004-2006 Luc Saillard (luc@saillard.org)
-
-   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
-   driver and thus may have bugs that are not present in the original version.
-   Please send bug reports and support requests to <luc@saillard.org>.
-   The decompression routines have been implemented by reverse-engineering the
-   Nemosoft binary pwcx module. Caveat emptor.
-
-   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
-*/
-
-/* This is pwc-ioctl.h belonging to PWC 10.0.10
-   It contains structures and defines to communicate from user space
-   directly to the driver.
- */
-
-/*
-   Changes
-   2001/08/03  Alvarado   Added ioctl constants to access methods for
-			  changing white balance and red/blue gains
-   2002/12/15  G. H. Fernandez-Toribio   VIDIOCGREALSIZE
-   2003/12/13  Nemosft Unv. Some modifications to make interfacing to
-	       PWCX easier
- */
-
-/* These are private ioctl() commands, specific for the Philips webcams.
-   They contain functions not found in other webcams, and settings not
-   specified in the Video4Linux API.
-
-   The #define names are built up like follows:
-   VIDIOC		VIDeo IOCtl prefix
-	 PWC		Philps WebCam
-	    G           optional: Get
-	    S           optional: Set
-	     ... 	the function
- */
-
-#include <linux/types.h>
-#include <linux/version.h>
-
- /* Enumeration of image sizes */
-#define PSZ_SQCIF	0x00
-#define PSZ_QSIF	0x01
-#define PSZ_QCIF	0x02
-#define PSZ_SIF		0x03
-#define PSZ_CIF		0x04
-#define PSZ_VGA		0x05
-#define PSZ_MAX		6
-
-
-/* The frame rate is encoded in the video_window.flags parameter using
-   the upper 16 bits, since some flags are defined nowadays. The following
-   defines provide a mask and shift to filter out this value.
-   This value can also be passing using the private flag when using v4l2 and
-   VIDIOC_S_FMT ioctl.
-
-   In 'Snapshot' mode the camera freezes its automatic exposure and colour
-   balance controls.
- */
-#define PWC_FPS_SHIFT		16
-#define PWC_FPS_MASK		0x00FF0000
-#define PWC_FPS_FRMASK		0x003F0000
-#define PWC_FPS_SNAPSHOT	0x00400000
-#define PWC_QLT_MASK		0x03000000
-#define PWC_QLT_SHIFT		24
-
-
-/* structure for transferring x & y coordinates */
-struct pwc_coord
-{
-	int x, y;		/* guess what */
-	int size;		/* size, or offset */
-};
-
-
-/* Used with VIDIOCPWCPROBE */
-struct pwc_probe
-{
-	char name[32];
-	int type;
-};
-
-struct pwc_serial
-{
-	char serial[30];	/* String with serial number. Contains terminating 0 */
-};
-
-/* pwc_whitebalance.mode values */
-#define PWC_WB_INDOOR		0
-#define PWC_WB_OUTDOOR		1
-#define PWC_WB_FL		2
-#define PWC_WB_MANUAL		3
-#define PWC_WB_AUTO		4
-
-/* Used with VIDIOCPWC[SG]AWB (Auto White Balance).
-   Set mode to one of the PWC_WB_* values above.
-   *red and *blue are the respective gains of these colour components inside
-   the camera; range 0..65535
-   When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read;
-   otherwise undefined.
-   'read_red' and 'read_blue' are read-only.
-*/
-struct pwc_whitebalance
-{
-	int mode;
-	int manual_red, manual_blue;	/* R/W */
-	int read_red, read_blue;	/* R/O */
-};
-
-/*
-   'control_speed' and 'control_delay' are used in automatic whitebalance mode,
-   and tell the camera how fast it should react to changes in lighting, and
-   with how much delay. Valid values are 0..65535.
-*/
-struct pwc_wb_speed
-{
-	int control_speed;
-	int control_delay;
-
-};
-
-/* Used with VIDIOCPWC[SG]LED */
-struct pwc_leds
-{
-	int led_on;			/* Led on-time; range = 0..25000 */
-	int led_off;			/* Led off-time; range = 0..25000  */
-};
-
-/* Image size (used with GREALSIZE) */
-struct pwc_imagesize
-{
-	int width;
-	int height;
-};
-
-/* Defines and structures for Motorized Pan & Tilt */
-#define PWC_MPT_PAN		0x01
-#define PWC_MPT_TILT		0x02
-#define PWC_MPT_TIMEOUT		0x04 /* for status */
-
-/* Set angles; when absolute != 0, the angle is absolute and the
-   driver calculates the relative offset for you. This can only
-   be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
-   absolute angles.
- */
-struct pwc_mpt_angles
-{
-	int absolute;		/* write-only */
-	int pan;		/* degrees * 100 */
-	int tilt;		/* degress * 100 */
-};
-
-/* Range of angles of the camera, both horizontally and vertically.
- */
-struct pwc_mpt_range
-{
-	int pan_min, pan_max;		/* degrees * 100 */
-	int tilt_min, tilt_max;
-};
-
-struct pwc_mpt_status
-{
-	int status;
-	int time_pan;
-	int time_tilt;
-};
-
-
-/* This is used for out-of-kernel decompression. With it, you can get
-   all the necessary information to initialize and use the decompressor
-   routines in standalone applications.
- */
-struct pwc_video_command
-{
-	int type;		/* camera type (645, 675, 730, etc.) */
-	int release;		/* release number */
-
-	int size;		/* one of PSZ_* */
-	int alternate;
-	int command_len;	/* length of USB video command */
-	unsigned char command_buf[13];	/* Actual USB video command */
-	int bandlength;		/* >0 = compressed */
-	int frame_size;		/* Size of one (un)compressed frame */
-};
-
-/* Flags for PWCX subroutines. Not all modules honour all flags. */
-#define PWCX_FLAG_PLANAR	0x0001
-#define PWCX_FLAG_BAYER		0x0008
-
-
-/* IOCTL definitions */
-
- /* Restore user settings */
-#define VIDIOCPWCRUSER		_IO('v', 192)
- /* Save user settings */
-#define VIDIOCPWCSUSER		_IO('v', 193)
- /* Restore factory settings */
-#define VIDIOCPWCFACTORY	_IO('v', 194)
-
- /* You can manipulate the compression factor. A compression preference of 0
-    means use uncompressed modes when available; 1 is low compression, 2 is
-    medium and 3 is high compression preferred. Of course, the higher the
-    compression, the lower the bandwidth used but more chance of artefacts
-    in the image. The driver automatically chooses a higher compression when
-    the preferred mode is not available.
-  */
- /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
-#define VIDIOCPWCSCQUAL		_IOW('v', 195, int)
- /* Get preferred compression quality */
-#define VIDIOCPWCGCQUAL		_IOR('v', 195, int)
-
-
-/* Retrieve serial number of camera */
-#define VIDIOCPWCGSERIAL	_IOR('v', 198, struct pwc_serial)
-
- /* This is a probe function; since so many devices are supported, it
-    becomes difficult to include all the names in programs that want to
-    check for the enhanced Philips stuff. So in stead, try this PROBE;
-    it returns a structure with the original name, and the corresponding
-    Philips type.
-    To use, fill the structure with zeroes, call PROBE and if that succeeds,
-    compare the name with that returned from VIDIOCGCAP; they should be the
-    same. If so, you can be assured it is a Philips (OEM) cam and the type
-    is valid.
- */
-#define VIDIOCPWCPROBE		_IOR('v', 199, struct pwc_probe)
-
- /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
-#define VIDIOCPWCSAGC		_IOW('v', 200, int)
- /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
-#define VIDIOCPWCGAGC		_IOR('v', 200, int)
- /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
-#define VIDIOCPWCSSHUTTER	_IOW('v', 201, int)
-
- /* Color compensation (Auto White Balance) */
-#define VIDIOCPWCSAWB           _IOW('v', 202, struct pwc_whitebalance)
-#define VIDIOCPWCGAWB           _IOR('v', 202, struct pwc_whitebalance)
-
- /* Auto WB speed */
-#define VIDIOCPWCSAWBSPEED	_IOW('v', 203, struct pwc_wb_speed)
-#define VIDIOCPWCGAWBSPEED	_IOR('v', 203, struct pwc_wb_speed)
-
- /* LEDs on/off/blink; int range 0..65535 */
-#define VIDIOCPWCSLED           _IOW('v', 205, struct pwc_leds)
-#define VIDIOCPWCGLED           _IOR('v', 205, struct pwc_leds)
-
-  /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
-#define VIDIOCPWCSCONTOUR	_IOW('v', 206, int)
-#define VIDIOCPWCGCONTOUR	_IOR('v', 206, int)
-
-  /* Backlight compensation; 0 = off, otherwise on */
-#define VIDIOCPWCSBACKLIGHT	_IOW('v', 207, int)
-#define VIDIOCPWCGBACKLIGHT	_IOR('v', 207, int)
-
-  /* Flickerless mode; = 0 off, otherwise on */
-#define VIDIOCPWCSFLICKER	_IOW('v', 208, int)
-#define VIDIOCPWCGFLICKER	_IOR('v', 208, int)
-
-  /* Dynamic noise reduction; 0 off, 3 = high noise reduction */
-#define VIDIOCPWCSDYNNOISE	_IOW('v', 209, int)
-#define VIDIOCPWCGDYNNOISE	_IOR('v', 209, int)
-
- /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
-#define VIDIOCPWCGREALSIZE	_IOR('v', 210, struct pwc_imagesize)
-
- /* Motorized pan & tilt functions */
-#define VIDIOCPWCMPTRESET	_IOW('v', 211, int)
-#define VIDIOCPWCMPTGRANGE	_IOR('v', 211, struct pwc_mpt_range)
-#define VIDIOCPWCMPTSANGLE	_IOW('v', 212, struct pwc_mpt_angles)
-#define VIDIOCPWCMPTGANGLE	_IOR('v', 212, struct pwc_mpt_angles)
-#define VIDIOCPWCMPTSTATUS	_IOR('v', 213, struct pwc_mpt_status)
-
- /* Get the USB set-video command; needed for initializing libpwcx */
-#define VIDIOCPWCGVIDCMD	_IOR('v', 215, struct pwc_video_command)
-struct pwc_table_init_buffer {
-   int len;
-   char *buffer;
-
-};
-#define VIDIOCPWCGVIDTABLE	_IOR('v', 216, struct pwc_table_init_buffer)
-
-/*
- * This is private command used when communicating with v4l2.
- * In the future all private ioctl will be remove/replace to
- * use interface offer by v4l2.
- */
-
-#define V4L2_CID_PRIVATE_SAVE_USER       (V4L2_CID_PRIVATE_BASE + 0)
-#define V4L2_CID_PRIVATE_RESTORE_USER    (V4L2_CID_PRIVATE_BASE + 1)
-#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2)
-#define V4L2_CID_PRIVATE_COLOUR_MODE     (V4L2_CID_PRIVATE_BASE + 3)
-#define V4L2_CID_PRIVATE_AUTOCONTOUR     (V4L2_CID_PRIVATE_BASE + 4)
-#define V4L2_CID_PRIVATE_CONTOUR         (V4L2_CID_PRIVATE_BASE + 5)
-#define V4L2_CID_PRIVATE_BACKLIGHT       (V4L2_CID_PRIVATE_BASE + 6)
-#define V4L2_CID_PRIVATE_FLICKERLESS     (V4L2_CID_PRIVATE_BASE + 7)
-#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8)
-
-struct pwc_raw_frame {
-   __le16 type;		/* type of the webcam */
-   __le16 vbandlength;	/* Size of 4lines compressed (used by the decompressor) */
-   __u8   cmd[4];	/* the four byte of the command (in case of nala,
-			   only the first 3 bytes is filled) */
-   __u8   rawframe[0];	/* frame_size = H/4*vbandlength */
-} __attribute__ ((packed));
-
-
-#endif
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
index f4ae83c..e5f4fd8 100644
--- a/drivers/media/video/pwc/pwc-kiara.c
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -40,7 +40,6 @@
 
 
 #include "pwc-kiara.h"
-#include "pwc-uncompress.h"
 
 const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 };
 
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c
index 6af5bb5..0b03133 100644
--- a/drivers/media/video/pwc/pwc-misc.c
+++ b/drivers/media/video/pwc/pwc-misc.c
@@ -126,8 +126,4 @@
 	pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */
 	pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
 	pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
-	/* length of image, in YUV format; always allocate enough memory. */
-	pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
 }
-
-
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c
index 3b73f29..5126509 100644
--- a/drivers/media/video/pwc/pwc-uncompress.c
+++ b/drivers/media/video/pwc/pwc-uncompress.c
@@ -30,26 +30,17 @@
 #include <asm/types.h>
 
 #include "pwc.h"
-#include "pwc-uncompress.h"
 #include "pwc-dec1.h"
 #include "pwc-dec23.h"
 
-int pwc_decompress(struct pwc_device *pdev)
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf)
 {
-	struct pwc_frame_buf *fbuf;
 	int n, line, col, stride;
 	void *yuv, *image;
 	u16 *src;
 	u16 *dsty, *dstu, *dstv;
 
-	if (pdev == NULL)
-		return -EFAULT;
-
-	fbuf = pdev->read_frame;
-	if (fbuf == NULL)
-		return -EFAULT;
-	image  = pdev->image_data;
-	image += pdev->images[pdev->fill_image].offset;
+	image = vb2_plane_vaddr(&fbuf->vb, 0);
 
 	yuv = fbuf->data + pdev->frame_header_size;  /* Skip header */
 
@@ -64,9 +55,13 @@
 			 * determine this using the type of the webcam */
 		memcpy(raw_frame->cmd, pdev->cmd_buf, 4);
 		memcpy(raw_frame+1, yuv, pdev->frame_size);
+		vb2_set_plane_payload(&fbuf->vb, 0,
+			pdev->frame_size + sizeof(struct pwc_raw_frame));
 		return 0;
 	}
 
+	vb2_set_plane_payload(&fbuf->vb, 0, pdev->view.size);
+
 	if (pdev->vbandlength == 0) {
 		/* Uncompressed mode.
 		 * We copy the data into the output buffer, using the viewport
diff --git a/drivers/media/video/pwc/pwc-uncompress.h b/drivers/media/video/pwc/pwc-uncompress.h
deleted file mode 100644
index 43028e7..0000000
--- a/drivers/media/video/pwc/pwc-uncompress.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* (C) 1999-2003 Nemosoft Unv.
-   (C) 2004-2006 Luc Saillard (luc@saillard.org)
-
-   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
-   driver and thus may have bugs that are not present in the original version.
-   Please send bug reports and support requests to <luc@saillard.org>.
-   The decompression routines have been implemented by reverse-engineering the
-   Nemosoft binary pwcx module. Caveat emptor.
-
-   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
-*/
-
-/* This file is the bridge between the kernel module and the plugin; it
-   describes the structures and datatypes used in both modules. Any
-   significant change should be reflected by increasing the
-   pwc_decompressor_version major number.
- */
-#ifndef PWC_UNCOMPRESS_H
-#define PWC_UNCOMPRESS_H
-
-
-#include <media/pwc-ioctl.h>
-
-/* from pwc-dec.h */
-#define PWCX_FLAG_PLANAR        0x0001
-/* */
-
-#endif
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index f85c512..e9a0e94 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -2,6 +2,7 @@
    USB and Video4Linux interface part.
    (C) 1999-2004 Nemosoft Unv.
    (C) 2004-2006 Luc Saillard (luc@saillard.org)
+   (C) 2011 Hans de Goede <hdegoede@redhat.com>
 
    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
    driver and thus may have bugs that are not present in the original version.
@@ -31,184 +32,330 @@
 #include <linux/module.h>
 #include <linux/poll.h>
 #include <linux/vmalloc.h>
+#include <linux/jiffies.h>
 #include <asm/io.h>
 
 #include "pwc.h"
 
-static struct v4l2_queryctrl pwc_controls[] = {
-	{
-	    .id      = V4L2_CID_BRIGHTNESS,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Brightness",
-	    .minimum = 0,
-	    .maximum = 128,
-	    .step    = 1,
-	    .default_value = 64,
-	},
-	{
-	    .id      = V4L2_CID_CONTRAST,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Contrast",
-	    .minimum = 0,
-	    .maximum = 64,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_SATURATION,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Saturation",
-	    .minimum = -100,
-	    .maximum = 100,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_GAMMA,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Gamma",
-	    .minimum = 0,
-	    .maximum = 32,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_RED_BALANCE,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Red Gain",
-	    .minimum = 0,
-	    .maximum = 256,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_BLUE_BALANCE,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Blue Gain",
-	    .minimum = 0,
-	    .maximum = 256,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_AUTO_WHITE_BALANCE,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Auto White Balance",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_EXPOSURE,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Shutter Speed (Exposure)",
-	    .minimum = 0,
-	    .maximum = 256,
-	    .step    = 1,
-	    .default_value = 200,
-	},
-	{
-	    .id      = V4L2_CID_AUTOGAIN,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Auto Gain Enabled",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 1,
-	},
-	{
-	    .id      = V4L2_CID_GAIN,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Gain Level",
-	    .minimum = 0,
-	    .maximum = 256,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_SAVE_USER,
-	    .type    = V4L2_CTRL_TYPE_BUTTON,
-	    .name    = "Save User Settings",
-	    .minimum = 0,
-	    .maximum = 0,
-	    .step    = 0,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_RESTORE_USER,
-	    .type    = V4L2_CTRL_TYPE_BUTTON,
-	    .name    = "Restore User Settings",
-	    .minimum = 0,
-	    .maximum = 0,
-	    .step    = 0,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_RESTORE_FACTORY,
-	    .type    = V4L2_CTRL_TYPE_BUTTON,
-	    .name    = "Restore Factory Settings",
-	    .minimum = 0,
-	    .maximum = 0,
-	    .step    = 0,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_COLOUR_MODE,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Colour mode",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_AUTOCONTOUR,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Auto contour",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_CONTOUR,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Contour",
-	    .minimum = 0,
-	    .maximum = 63,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_BACKLIGHT,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Backlight compensation",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	  .id      = V4L2_CID_PRIVATE_FLICKERLESS,
-	    .type    = V4L2_CTRL_TYPE_BOOLEAN,
-	    .name    = "Flickerless",
-	    .minimum = 0,
-	    .maximum = 1,
-	    .step    = 1,
-	    .default_value = 0,
-	},
-	{
-	    .id      = V4L2_CID_PRIVATE_NOISE_REDUCTION,
-	    .type    = V4L2_CTRL_TYPE_INTEGER,
-	    .name    = "Noise reduction",
-	    .minimum = 0,
-	    .maximum = 3,
-	    .step    = 1,
-	    .default_value = 0,
-	},
+#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl)
+
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops pwc_ctrl_ops = {
+	.g_volatile_ctrl = pwc_g_volatile_ctrl,
+	.s_ctrl = pwc_s_ctrl,
 };
 
+enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto };
+enum { custom_autocontour, custom_contour, custom_noise_reduction,
+	custom_save_user, custom_restore_user, custom_restore_factory };
+
+const char * const pwc_auto_whitebal_qmenu[] = {
+	"Indoor (Incandescant Lighting) Mode",
+	"Outdoor (Sunlight) Mode",
+	"Indoor (Fluorescent Lighting) Mode",
+	"Manual Mode",
+	"Auto Mode",
+	NULL
+};
+
+static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= V4L2_CID_AUTO_WHITE_BALANCE,
+	.type	= V4L2_CTRL_TYPE_MENU,
+	.max	= awb_auto,
+	.qmenu	= pwc_auto_whitebal_qmenu,
+};
+
+static const struct v4l2_ctrl_config pwc_autocontour_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(autocontour),
+	.type	= V4L2_CTRL_TYPE_BOOLEAN,
+	.name	= "Auto contour",
+	.min	= 0,
+	.max	= 1,
+	.step	= 1,
+};
+
+static const struct v4l2_ctrl_config pwc_contour_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(contour),
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	= "Contour",
+	.min	= 0,
+	.max	= 63,
+	.step	= 1,
+};
+
+static const struct v4l2_ctrl_config pwc_backlight_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= V4L2_CID_BACKLIGHT_COMPENSATION,
+	.type	= V4L2_CTRL_TYPE_BOOLEAN,
+	.min	= 0,
+	.max	= 1,
+	.step	= 1,
+};
+
+static const struct v4l2_ctrl_config pwc_flicker_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= V4L2_CID_BAND_STOP_FILTER,
+	.type	= V4L2_CTRL_TYPE_BOOLEAN,
+	.min	= 0,
+	.max	= 1,
+	.step	= 1,
+};
+
+static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(noise_reduction),
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.name	= "Dynamic Noise Reduction",
+	.min	= 0,
+	.max	= 3,
+	.step	= 1,
+};
+
+static const struct v4l2_ctrl_config pwc_save_user_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(save_user),
+	.type	= V4L2_CTRL_TYPE_BUTTON,
+	.name    = "Save User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_user_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(restore_user),
+	.type	= V4L2_CTRL_TYPE_BUTTON,
+	.name    = "Restore User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_factory_cfg = {
+	.ops	= &pwc_ctrl_ops,
+	.id	= PWC_CID_CUSTOM(restore_factory),
+	.type	= V4L2_CTRL_TYPE_BUTTON,
+	.name    = "Restore Factory Settings",
+};
+
+int pwc_init_controls(struct pwc_device *pdev)
+{
+	struct v4l2_ctrl_handler *hdl;
+	struct v4l2_ctrl_config cfg;
+	int r, def;
+
+	hdl = &pdev->ctrl_handler;
+	r = v4l2_ctrl_handler_init(hdl, 20);
+	if (r)
+		return r;
+
+	/* Brightness, contrast, saturation, gamma */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def);
+	if (r || def > 127)
+		def = 63;
+	pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_BRIGHTNESS, 0, 127, 1, def);
+
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def);
+	if (r || def > 63)
+		def = 31;
+	pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_CONTRAST, 0, 63, 1, def);
+
+	if (pdev->type >= 675) {
+		if (pdev->type < 730)
+			pdev->saturation_fmt = SATURATION_MODE_FORMATTER2;
+		else
+			pdev->saturation_fmt = SATURATION_MODE_FORMATTER1;
+		r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt,
+				    &def);
+		if (r || def < -100 || def > 100)
+			def = 0;
+		pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				      V4L2_CID_SATURATION, -100, 100, 1, def);
+	}
+
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def);
+	if (r || def > 31)
+		def = 15;
+	pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_GAMMA, 0, 31, 1, def);
+
+	/* auto white balance, red gain, blue gain */
+	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def);
+	if (r || def > awb_auto)
+		def = awb_auto;
+	cfg = pwc_auto_white_balance_cfg;
+	cfg.name = v4l2_ctrl_get_name(cfg.id);
+	cfg.def = def;
+	pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+	/* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */
+	if (!pdev->auto_white_balance)
+		return hdl->error;
+
+	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+			    PRESET_MANUAL_RED_GAIN_FORMATTER, &def);
+	if (r)
+		def = 127;
+	pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_RED_BALANCE, 0, 255, 1, def);
+
+	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+			    PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def);
+	if (r)
+		def = 127;
+	pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
+
+	v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual,
+			       pdev->auto_white_balance->cur.val == awb_auto);
+
+	/* autogain, gain */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
+	if (r || (def != 0 && def != 0xff))
+		def = 0;
+	/* Note a register value if 0 means auto gain is on */
+	pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0);
+	if (!pdev->autogain)
+		return hdl->error;
+
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def);
+	if (r || def > 63)
+		def = 31;
+	pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_GAIN, 0, 63, 1, def);
+
+	/* auto exposure, exposure */
+	if (DEVICE_USE_CODEC2(pdev->type)) {
+		r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER,
+				    &def);
+		if (r || (def != 0 && def != 0xff))
+			def = 0;
+		/*
+		 * def = 0 auto, def = ff manual
+		 * menu idx 0 = auto, idx 1 = manual
+		 */
+		pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl,
+					&pwc_ctrl_ops,
+					V4L2_CID_EXPOSURE_AUTO,
+					1, 0, def != 0);
+		if (!pdev->exposure_auto)
+			return hdl->error;
+
+		/* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+		r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+				     READ_SHUTTER_FORMATTER, &def);
+		if (r || def > 655)
+			def = 655;
+		pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+					V4L2_CID_EXPOSURE, 0, 655, 1, def);
+		/* CODEC2: separate auto gain & auto exposure */
+		v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true);
+		v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto,
+				       V4L2_EXPOSURE_MANUAL, true);
+	} else if (DEVICE_USE_CODEC3(pdev->type)) {
+		/* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+		r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+				     READ_SHUTTER_FORMATTER, &def);
+		if (r || def > 255)
+			def = 255;
+		pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+					V4L2_CID_EXPOSURE, 0, 255, 1, def);
+		/* CODEC3: both gain and exposure controlled by autogain */
+		pdev->autogain_expo_cluster[0] = pdev->autogain;
+		pdev->autogain_expo_cluster[1] = pdev->gain;
+		pdev->autogain_expo_cluster[2] = pdev->exposure;
+		v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster,
+				       0, true);
+	}
+
+	/* color / bw setting */
+	r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER,
+			 &def);
+	if (r || (def != 0 && def != 0xff))
+		def = 0xff;
+	/* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */
+	pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
+				V4L2_CID_COLORFX, 1, 0, def == 0);
+
+	/* autocontour, contour */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def);
+	if (r || (def != 0 && def != 0xff))
+		def = 0;
+	cfg = pwc_autocontour_cfg;
+	cfg.def = def == 0;
+	pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+	if (!pdev->autocontour)
+		return hdl->error;
+
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def);
+	if (r || def > 63)
+		def = 31;
+	cfg = pwc_contour_cfg;
+	cfg.def = def;
+	pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+	v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false);
+
+	/* backlight */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+			    BACK_LIGHT_COMPENSATION_FORMATTER, &def);
+	if (r || (def != 0 && def != 0xff))
+		def = 0;
+	cfg = pwc_backlight_cfg;
+	cfg.name = v4l2_ctrl_get_name(cfg.id);
+	cfg.def = def == 0;
+	pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+	/* flikker rediction */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+			    FLICKERLESS_MODE_FORMATTER, &def);
+	if (r || (def != 0 && def != 0xff))
+		def = 0;
+	cfg = pwc_flicker_cfg;
+	cfg.name = v4l2_ctrl_get_name(cfg.id);
+	cfg.def = def == 0;
+	pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+	/* Dynamic noise reduction */
+	r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+			    DYNAMIC_NOISE_CONTROL_FORMATTER, &def);
+	if (r || def > 3)
+		def = 2;
+	cfg = pwc_noise_reduction_cfg;
+	cfg.def = def;
+	pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+	/* Save / Restore User / Factory Settings */
+	pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL);
+	pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
+						  NULL);
+	if (pdev->restore_user)
+		pdev->restore_user->flags = V4L2_CTRL_FLAG_UPDATE;
+	pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
+						     &pwc_restore_factory_cfg,
+						     NULL);
+	if (pdev->restore_factory)
+		pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE;
+
+	if (!pdev->features & FEATURE_MOTOR_PANTILT)
+		return hdl->error;
+
+	/* Motor pan / tilt / reset */
+	pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0);
+	if (!pdev->motor_pan)
+		return hdl->error;
+	pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0);
+	pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_PAN_RESET, 0, 0, 0, 0);
+	pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+				V4L2_CID_TILT_RESET, 0, 0, 0, 0);
+	v4l2_ctrl_cluster(4, &pdev->motor_pan);
+
+	return hdl->error;
+}
 
 static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f)
 {
@@ -284,10 +431,21 @@
 }
 
 /* ioctl(VIDIOC_SET_FMT) */
-static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
+
+static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
 {
+	struct pwc_device *pdev = video_drvdata(file);
 	int ret, fps, snapshot, compression, pixelformat;
 
+	if (!pdev->udev)
+		return -ENODEV;
+
+	if (pdev->capt_file != NULL &&
+	    pdev->capt_file != file)
+		return -EBUSY;
+
+	pdev->capt_file = file;
+
 	ret = pwc_vidioc_try_fmt(pdev, f);
 	if (ret<0)
 		return ret;
@@ -309,7 +467,7 @@
 	    pixelformat != V4L2_PIX_FMT_PWC2)
 		return -EINVAL;
 
-	if (pdev->iso_init)
+	if (vb2_is_streaming(&pdev->vb_queue))
 		return -EBUSY;
 
 	PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
@@ -343,13 +501,14 @@
 
 static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
 {
-	struct video_device *vdev = video_devdata(file);
 	struct pwc_device *pdev = video_drvdata(file);
 
+	if (!pdev->udev)
+		return -ENODEV;
+
 	strcpy(cap->driver, PWC_NAME);
-	strlcpy(cap->card, vdev->name, sizeof(cap->card));
+	strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
 	usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
-	cap->version = PWC_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE	|
 		V4L2_CAP_STREAMING	|
@@ -377,255 +536,396 @@
 	return i ? -EINVAL : 0;
 }
 
-static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i, idx;
-	u32 id;
+	struct pwc_device *pdev =
+		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+	int ret = 0;
 
-	id = c->id;
-	if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
-		id &= V4L2_CTRL_ID_MASK;
-		id++;
-		idx = -1;
-		for (i = 0; i < ARRAY_SIZE(pwc_controls); i++) {
-			if (pwc_controls[i].id < id)
-				continue;
-			if (idx >= 0
-			 && pwc_controls[i].id > pwc_controls[idx].id)
-				continue;
-			idx = i;
-		}
-		if (idx < 0)
-			return -EINVAL;
-		memcpy(c, &pwc_controls[idx], sizeof pwc_controls[0]);
-		return 0;
+	/*
+	 * Sometimes it can take quite long for the pwc to complete usb control
+	 * transfers, so release the modlock to give streaming by another
+	 * process / thread the chance to continue with a dqbuf.
+	 */
+	mutex_unlock(&pdev->modlock);
+
+	/*
+	 * Take the udev-lock to protect against the disconnect handler
+	 * completing and setting dev->udev to NULL underneath us. Other code
+	 * does not need to do this since it is protected by the modlock.
+	 */
+	mutex_lock(&pdev->udevlock);
+
+	if (!pdev->udev) {
+		ret = -ENODEV;
+		goto leave;
 	}
-	for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) {
-		if (pwc_controls[i].id == c->id) {
-			PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
-			memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl));
-			return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		if (pdev->color_bal_valid && time_before(jiffies,
+				pdev->last_color_bal_update + HZ / 4)) {
+			pdev->red_balance->val  = pdev->last_red_balance;
+			pdev->blue_balance->val = pdev->last_blue_balance;
+			break;
 		}
+		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+				      READ_RED_GAIN_FORMATTER,
+				      &pdev->red_balance->val);
+		if (ret)
+			break;
+		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+				      READ_BLUE_GAIN_FORMATTER,
+				      &pdev->blue_balance->val);
+		if (ret)
+			break;
+		pdev->last_red_balance  = pdev->red_balance->val;
+		pdev->last_blue_balance = pdev->blue_balance->val;
+		pdev->last_color_bal_update = jiffies;
+		pdev->color_bal_valid = true;
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (pdev->gain_valid && time_before(jiffies,
+				pdev->last_gain_update + HZ / 4)) {
+			pdev->gain->val = pdev->last_gain;
+			break;
+		}
+		ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+				      READ_AGC_FORMATTER, &pdev->gain->val);
+		if (ret)
+			break;
+		pdev->last_gain = pdev->gain->val;
+		pdev->last_gain_update = jiffies;
+		pdev->gain_valid = true;
+		if (!DEVICE_USE_CODEC3(pdev->type))
+			break;
+		/* Fall through for CODEC3 where autogain also controls expo */
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (pdev->exposure_valid && time_before(jiffies,
+				pdev->last_exposure_update + HZ / 4)) {
+			pdev->exposure->val = pdev->last_exposure;
+			break;
+		}
+		ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+				       READ_SHUTTER_FORMATTER,
+				       &pdev->exposure->val);
+		if (ret)
+			break;
+		pdev->last_exposure = pdev->exposure->val;
+		pdev->last_exposure_update = jiffies;
+		pdev->exposure_valid = true;
+		break;
+	default:
+		ret = -EINVAL;
 	}
-	return -EINVAL;
+
+	if (ret)
+		PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
+
+leave:
+	mutex_unlock(&pdev->udevlock);
+	mutex_lock(&pdev->modlock);
+	return ret;
 }
 
-static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+static int pwc_set_awb(struct pwc_device *pdev)
 {
-	struct pwc_device *pdev = video_drvdata(file);
-	int ret;
+	int ret = 0;
 
-	switch (c->id) {
-	case V4L2_CID_BRIGHTNESS:
-		c->value = pwc_get_brightness(pdev);
-		if (c->value < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_CONTRAST:
-		c->value = pwc_get_contrast(pdev);
-		if (c->value < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_SATURATION:
-		ret = pwc_get_saturation(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_GAMMA:
-		c->value = pwc_get_gamma(pdev);
-		if (c->value < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_RED_BALANCE:
-		ret = pwc_get_red_gain(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value >>= 8;
-		return 0;
-	case V4L2_CID_BLUE_BALANCE:
-		ret = pwc_get_blue_gain(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value >>= 8;
-		return 0;
-	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ret = pwc_get_awb(pdev);
-		if (ret < 0)
-			return -EINVAL;
-		c->value = (ret == PWC_WB_MANUAL) ? 0 : 1;
-		return 0;
-	case V4L2_CID_GAIN:
-		ret = pwc_get_agc(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value >>= 8;
-		return 0;
-	case V4L2_CID_AUTOGAIN:
-		ret = pwc_get_agc(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value = (c->value < 0) ? 1 : 0;
-		return 0;
-	case V4L2_CID_EXPOSURE:
-		ret = pwc_get_shutter_speed(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_COLOUR_MODE:
-		ret = pwc_get_colour_mode(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_AUTOCONTOUR:
-		ret = pwc_get_contour(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value = (c->value == -1 ? 1 : 0);
-		return 0;
-	case V4L2_CID_PRIVATE_CONTOUR:
-		ret = pwc_get_contour(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value >>= 10;
-		return 0;
-	case V4L2_CID_PRIVATE_BACKLIGHT:
-		ret = pwc_get_backlight(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_FLICKERLESS:
-		ret = pwc_get_flicker(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		c->value = (c->value ? 1 : 0);
-		return 0;
-	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
-		ret = pwc_get_dynamic_noise(pdev, &c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
+	if (pdev->auto_white_balance->is_new) {
+		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+				      WB_MODE_FORMATTER,
+				      pdev->auto_white_balance->val);
+		if (ret)
+			return ret;
 
-	case V4L2_CID_PRIVATE_SAVE_USER:
-	case V4L2_CID_PRIVATE_RESTORE_USER:
-	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
-		return -EINVAL;
+		/* Update val when coming from auto or going to a preset */
+		if (pdev->red_balance->is_volatile ||
+		    pdev->auto_white_balance->val == awb_indoor ||
+		    pdev->auto_white_balance->val == awb_outdoor ||
+		    pdev->auto_white_balance->val == awb_fl) {
+			if (!pdev->red_balance->is_new)
+				pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+					READ_RED_GAIN_FORMATTER,
+					&pdev->red_balance->val);
+			if (!pdev->blue_balance->is_new)
+				pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+					READ_BLUE_GAIN_FORMATTER,
+					&pdev->blue_balance->val);
+		}
+		if (pdev->auto_white_balance->val == awb_auto) {
+			pdev->red_balance->is_volatile = true;
+			pdev->blue_balance->is_volatile = true;
+			pdev->color_bal_valid = false; /* Force cache update */
+		} else {
+			pdev->red_balance->is_volatile = false;
+			pdev->blue_balance->is_volatile = false;
+		}
 	}
-	return -EINVAL;
+
+	if (ret == 0 && pdev->red_balance->is_new) {
+		if (pdev->auto_white_balance->val != awb_manual)
+			return -EBUSY;
+		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+				      PRESET_MANUAL_RED_GAIN_FORMATTER,
+				      pdev->red_balance->val);
+	}
+
+	if (ret == 0 && pdev->blue_balance->is_new) {
+		if (pdev->auto_white_balance->val != awb_manual)
+			return -EBUSY;
+		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+				      PRESET_MANUAL_BLUE_GAIN_FORMATTER,
+				      pdev->blue_balance->val);
+	}
+	return ret;
 }
 
-static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_autogain(struct pwc_device *pdev)
 {
-	struct pwc_device *pdev = video_drvdata(file);
-	int ret;
+	int ret = 0;
 
-	switch (c->id) {
-	case V4L2_CID_BRIGHTNESS:
-		c->value <<= 9;
-		ret = pwc_set_brightness(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_CONTRAST:
-		c->value <<= 10;
-		ret = pwc_set_contrast(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_SATURATION:
-		ret = pwc_set_saturation(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_GAMMA:
-		c->value <<= 11;
-		ret = pwc_set_gamma(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_RED_BALANCE:
-		c->value <<= 8;
-		ret = pwc_set_red_gain(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_BLUE_BALANCE:
-		c->value <<= 8;
-		ret = pwc_set_blue_gain(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_AUTO_WHITE_BALANCE:
-		c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO;
-		ret = pwc_set_awb(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_EXPOSURE:
-		c->value <<= 8;
-		ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_AUTOGAIN:
-		/* autogain off means nothing without a gain */
-		if (c->value == 0)
-			return 0;
-		ret = pwc_set_agc(pdev, c->value, 0);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_GAIN:
-		c->value <<= 8;
-		ret = pwc_set_agc(pdev, 0, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_SAVE_USER:
-		if (pwc_save_user(pdev))
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_RESTORE_USER:
-		if (pwc_restore_user(pdev))
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_RESTORE_FACTORY:
-		if (pwc_restore_factory(pdev))
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_COLOUR_MODE:
-		ret = pwc_set_colour_mode(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_AUTOCONTOUR:
-		c->value = (c->value == 1) ? -1 : 0;
-		ret = pwc_set_contour(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_CONTOUR:
-		c->value <<= 10;
-		ret = pwc_set_contour(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_BACKLIGHT:
-		ret = pwc_set_backlight(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-	case V4L2_CID_PRIVATE_FLICKERLESS:
-		ret = pwc_set_flicker(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-	case V4L2_CID_PRIVATE_NOISE_REDUCTION:
-		ret = pwc_set_dynamic_noise(pdev, c->value);
-		if (ret < 0)
-			return -EINVAL;
-		return 0;
-
+	if (pdev->autogain->is_new) {
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      AGC_MODE_FORMATTER,
+				      pdev->autogain->val ? 0 : 0xff);
+		if (ret)
+			return ret;
+		if (pdev->autogain->val)
+			pdev->gain_valid = false; /* Force cache update */
+		else if (!pdev->gain->is_new)
+			pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+					READ_AGC_FORMATTER,
+					&pdev->gain->val);
 	}
-	return -EINVAL;
+	if (ret == 0 && pdev->gain->is_new) {
+		if (pdev->autogain->val)
+			return -EBUSY;
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      PRESET_AGC_FORMATTER,
+				      pdev->gain->val);
+	}
+	return ret;
+}
+
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_exposure_auto(struct pwc_device *pdev)
+{
+	int ret = 0;
+	int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
+
+	if (pdev->exposure_auto->is_new) {
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      SHUTTER_MODE_FORMATTER,
+				      is_auto ? 0 : 0xff);
+		if (ret)
+			return ret;
+		if (is_auto)
+			pdev->exposure_valid = false; /* Force cache update */
+		else if (!pdev->exposure->is_new)
+			pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+					 READ_SHUTTER_FORMATTER,
+					 &pdev->exposure->val);
+	}
+	if (ret == 0 && pdev->exposure->is_new) {
+		if (is_auto)
+			return -EBUSY;
+		ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+				       PRESET_SHUTTER_FORMATTER,
+				       pdev->exposure->val);
+	}
+	return ret;
+}
+
+/* For CODEC3 models which have autogain controlling both gain and exposure */
+static int pwc_set_autogain_expo(struct pwc_device *pdev)
+{
+	int ret = 0;
+
+	if (pdev->autogain->is_new) {
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      AGC_MODE_FORMATTER,
+				      pdev->autogain->val ? 0 : 0xff);
+		if (ret)
+			return ret;
+		if (pdev->autogain->val) {
+			pdev->gain_valid     = false; /* Force cache update */
+			pdev->exposure_valid = false; /* Force cache update */
+		} else {
+			if (!pdev->gain->is_new)
+				pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+						READ_AGC_FORMATTER,
+						&pdev->gain->val);
+			if (!pdev->exposure->is_new)
+				pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+						 READ_SHUTTER_FORMATTER,
+						 &pdev->exposure->val);
+		}
+	}
+	if (ret == 0 && pdev->gain->is_new) {
+		if (pdev->autogain->val)
+			return -EBUSY;
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      PRESET_AGC_FORMATTER,
+				      pdev->gain->val);
+	}
+	if (ret == 0 && pdev->exposure->is_new) {
+		if (pdev->autogain->val)
+			return -EBUSY;
+		ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+				       PRESET_SHUTTER_FORMATTER,
+				       pdev->exposure->val);
+	}
+	return ret;
+}
+
+static int pwc_set_motor(struct pwc_device *pdev)
+{
+	int ret;
+	u8 buf[4];
+
+	buf[0] = 0;
+	if (pdev->motor_pan_reset->is_new)
+		buf[0] |= 0x01;
+	if (pdev->motor_tilt_reset->is_new)
+		buf[0] |= 0x02;
+	if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) {
+		ret = send_control_msg(pdev, SET_MPT_CTL,
+				       PT_RESET_CONTROL_FORMATTER, buf, 1);
+		if (ret < 0)
+			return ret;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	if (pdev->motor_pan->is_new) {
+		buf[0] = pdev->motor_pan->val & 0xFF;
+		buf[1] = (pdev->motor_pan->val >> 8);
+	}
+	if (pdev->motor_tilt->is_new) {
+		buf[2] = pdev->motor_tilt->val & 0xFF;
+		buf[3] = (pdev->motor_tilt->val >> 8);
+	}
+	if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) {
+		ret = send_control_msg(pdev, SET_MPT_CTL,
+				       PT_RELATIVE_CONTROL_FORMATTER,
+				       buf, sizeof(buf));
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct pwc_device *pdev =
+		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+	int ret = 0;
+
+	/* See the comments on locking in pwc_g_volatile_ctrl */
+	mutex_unlock(&pdev->modlock);
+	mutex_lock(&pdev->udevlock);
+
+	if (!pdev->udev) {
+		ret = -ENODEV;
+		goto leave;
+	}
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      BRIGHTNESS_FORMATTER, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      CONTRAST_FORMATTER, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL,
+				      pdev->saturation_fmt, ctrl->val);
+		break;
+	case V4L2_CID_GAMMA:
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      GAMMA_FORMATTER, ctrl->val);
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = pwc_set_awb(pdev);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		if (DEVICE_USE_CODEC2(pdev->type))
+			ret = pwc_set_autogain(pdev);
+		else if (DEVICE_USE_CODEC3(pdev->type))
+			ret = pwc_set_autogain_expo(pdev);
+		else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (DEVICE_USE_CODEC2(pdev->type))
+			ret = pwc_set_exposure_auto(pdev);
+		else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_COLORFX:
+		ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+				      COLOUR_MODE_FORMATTER,
+				      ctrl->val ? 0 : 0xff);
+		break;
+	case PWC_CID_CUSTOM(autocontour):
+		if (pdev->autocontour->is_new) {
+			ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+					AUTO_CONTOUR_FORMATTER,
+					pdev->autocontour->val ? 0 : 0xff);
+		}
+		if (ret == 0 && pdev->contour->is_new) {
+			if (pdev->autocontour->val) {
+				ret = -EBUSY;
+				break;
+			}
+			ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+					      PRESET_CONTOUR_FORMATTER,
+					      pdev->contour->val);
+		}
+		break;
+	case V4L2_CID_BACKLIGHT_COMPENSATION:
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      BACK_LIGHT_COMPENSATION_FORMATTER,
+				      ctrl->val ? 0 : 0xff);
+		break;
+	case V4L2_CID_BAND_STOP_FILTER:
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      FLICKERLESS_MODE_FORMATTER,
+				      ctrl->val ? 0 : 0xff);
+		break;
+	case PWC_CID_CUSTOM(noise_reduction):
+		ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+				      DYNAMIC_NOISE_CONTROL_FORMATTER,
+				      ctrl->val);
+		break;
+	case PWC_CID_CUSTOM(save_user):
+		ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
+		break;
+	case PWC_CID_CUSTOM(restore_user):
+		ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
+		break;
+	case PWC_CID_CUSTOM(restore_factory):
+		ret = pwc_button_ctrl(pdev,
+				      RESTORE_FACTORY_DEFAULTS_FORMATTER);
+		break;
+	case V4L2_CID_PAN_RELATIVE:
+		ret = pwc_set_motor(pdev);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret)
+		PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
+
+leave:
+	mutex_unlock(&pdev->udevlock);
+	mutex_lock(&pdev->modlock);
+	return ret;
 }
 
 static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
@@ -667,157 +967,77 @@
 	return pwc_vidioc_try_fmt(pdev, f);
 }
 
-static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int pwc_reqbufs(struct file *file, void *fh,
+		       struct v4l2_requestbuffers *rb)
 {
 	struct pwc_device *pdev = video_drvdata(file);
 
-	return pwc_vidioc_set_fmt(pdev, f);
-}
+	if (pdev->capt_file != NULL &&
+	    pdev->capt_file != file)
+		return -EBUSY;
 
-static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
-{
-	int nbuffers;
+	pdev->capt_file = file;
 
-	PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
-	if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-	if (rb->memory != V4L2_MEMORY_MMAP)
-		return -EINVAL;
-
-	nbuffers = rb->count;
-	if (nbuffers < 2)
-		nbuffers = 2;
-	else if (nbuffers > pwc_mbufs)
-		nbuffers = pwc_mbufs;
-	/* Force to use our # of buffers */
-	rb->count = pwc_mbufs;
-	return 0;
+	return vb2_reqbufs(&pdev->vb_queue, rb);
 }
 
 static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
 {
 	struct pwc_device *pdev = video_drvdata(file);
-	int index;
 
-	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
-	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
-		return -EINVAL;
-	}
-	index = buf->index;
-	if (index < 0 || index >= pwc_mbufs) {
-		PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
-		return -EINVAL;
-	}
-
-	buf->m.offset = index * pdev->len_per_image;
-	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
-		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
-	else
-		buf->bytesused = pdev->view.size;
-	buf->field = V4L2_FIELD_NONE;
-	buf->memory = V4L2_MEMORY_MMAP;
-	/*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
-	buf->length = pdev->len_per_image;
-
-	PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index);
-	PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
-	PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
-
-	return 0;
+	return vb2_querybuf(&pdev->vb_queue, buf);
 }
 
 static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
 {
-	PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index);
-	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-	if (buf->memory != V4L2_MEMORY_MMAP)
-		return -EINVAL;
-	if (buf->index >= pwc_mbufs)
-		return -EINVAL;
+	struct pwc_device *pdev = video_drvdata(file);
 
-	buf->flags |= V4L2_BUF_FLAG_QUEUED;
-	buf->flags &= ~V4L2_BUF_FLAG_DONE;
+	if (!pdev->udev)
+		return -ENODEV;
 
-	return 0;
+	if (pdev->capt_file != file)
+		return -EBUSY;
+
+	return vb2_qbuf(&pdev->vb_queue, buf);
 }
 
 static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
 {
-	DECLARE_WAITQUEUE(wait, current);
 	struct pwc_device *pdev = video_drvdata(file);
-	int ret;
 
-	PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
+	if (!pdev->udev)
+		return -ENODEV;
 
-	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
+	if (pdev->capt_file != file)
+		return -EBUSY;
 
-	add_wait_queue(&pdev->frameq, &wait);
-	while (pdev->full_frames == NULL) {
-		if (pdev->error_status) {
-			remove_wait_queue(&pdev->frameq, &wait);
-			set_current_state(TASK_RUNNING);
-			return -pdev->error_status;
-		}
-
-		if (signal_pending(current)) {
-			remove_wait_queue(&pdev->frameq, &wait);
-			set_current_state(TASK_RUNNING);
-			return -ERESTARTSYS;
-		}
-		mutex_unlock(&pdev->modlock);
-		schedule();
-		set_current_state(TASK_INTERRUPTIBLE);
-		mutex_lock(&pdev->modlock);
-	}
-	remove_wait_queue(&pdev->frameq, &wait);
-	set_current_state(TASK_RUNNING);
-
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
-	/* Decompress data in pdev->images[pdev->fill_image] */
-	ret = pwc_handle_frame(pdev);
-	if (ret)
-		return -EFAULT;
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
-
-	buf->index = pdev->fill_image;
-	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
-		buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
-	else
-		buf->bytesused = pdev->view.size;
-	buf->flags = V4L2_BUF_FLAG_MAPPED;
-	buf->field = V4L2_FIELD_NONE;
-	do_gettimeofday(&buf->timestamp);
-	buf->sequence = 0;
-	buf->memory = V4L2_MEMORY_MMAP;
-	buf->m.offset = pdev->fill_image * pdev->len_per_image;
-	buf->length = pdev->len_per_image;
-	pwc_next_image(pdev);
-
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
-	PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
-	return 0;
-
+	return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK);
 }
 
 static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
 {
 	struct pwc_device *pdev = video_drvdata(file);
 
-	return pwc_isoc_init(pdev);
+	if (!pdev->udev)
+		return -ENODEV;
+
+	if (pdev->capt_file != file)
+		return -EBUSY;
+
+	return vb2_streamon(&pdev->vb_queue, i);
 }
 
 static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
 {
 	struct pwc_device *pdev = video_drvdata(file);
 
-	pwc_isoc_cleanup(pdev);
-	return 0;
+	if (!pdev->udev)
+		return -ENODEV;
+
+	if (pdev->capt_file != file)
+		return -EBUSY;
+
+	return vb2_streamoff(&pdev->vb_queue, i);
 }
 
 static int pwc_enum_framesizes(struct file *file, void *fh,
@@ -896,9 +1116,6 @@
 	.vidioc_g_fmt_vid_cap		    = pwc_g_fmt_vid_cap,
 	.vidioc_s_fmt_vid_cap		    = pwc_s_fmt_vid_cap,
 	.vidioc_try_fmt_vid_cap		    = pwc_try_fmt_vid_cap,
-	.vidioc_queryctrl		    = pwc_queryctrl,
-	.vidioc_g_ctrl			    = pwc_g_ctrl,
-	.vidioc_s_ctrl			    = pwc_s_ctrl,
 	.vidioc_reqbufs			    = pwc_reqbufs,
 	.vidioc_querybuf		    = pwc_querybuf,
 	.vidioc_qbuf			    = pwc_qbuf,
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 083f8b1..0e4e2d7 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -29,7 +29,6 @@
 #include <linux/usb.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
-#include <linux/version.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
@@ -37,19 +36,16 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-vmalloc.h>
 #ifdef CONFIG_USB_PWC_INPUT_EVDEV
 #include <linux/input.h>
 #endif
 
-#include "pwc-uncompress.h"
 #include <media/pwc-ioctl.h>
 
 /* Version block */
-#define PWC_MAJOR	10
-#define PWC_MINOR	0
-#define PWC_EXTRAMINOR	12
-#define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR)
-#define PWC_VERSION	"10.0.14"
+#define PWC_VERSION	"10.0.15"
 #define PWC_NAME 	"pwc"
 #define PFX		PWC_NAME ": "
 
@@ -81,9 +77,9 @@
 #define PWC_DEBUG_LEVEL	(PWC_DEBUG_LEVEL_MODULE)
 
 #define PWC_DEBUG(level, fmt, args...) do {\
-	  if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \
-	     printk(KERN_DEBUG PFX fmt, ##args); \
-	  } while(0)
+	if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \
+		printk(KERN_DEBUG PFX fmt, ##args); \
+	} while (0)
 
 #define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
 #define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
@@ -110,25 +106,21 @@
 #define FEATURE_CODEC1			0x0002
 #define FEATURE_CODEC2			0x0004
 
-/* Turn certain features on/off */
-#define PWC_INT_PIPE 0
-
 /* Ignore errors in the first N frames, to allow for startup delays */
 #define FRAME_LOWMARK 5
 
 /* Size and number of buffers for the ISO pipe. */
-#define MAX_ISO_BUFS		2
+#define MAX_ISO_BUFS		3
 #define ISO_FRAMES_PER_DESC	10
 #define ISO_MAX_FRAME_SIZE	960
 #define ISO_BUFFER_SIZE 	(ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
 
-/* Frame buffers: contains compressed or uncompressed video data. */
-#define MAX_FRAMES		5
 /* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
 #define PWC_FRAME_SIZE 		(460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
 
-/* Absolute maximum number of buffers available for mmap() */
-#define MAX_IMAGES 		10
+/* Absolute minimum and maximum number of buffers available for mmap() */
+#define MIN_FRAMES		2
+#define MAX_FRAMES		16
 
 /* Some macros to quickly find the type of a webcam */
 #define DEVICE_USE_CODEC1(x) ((x)<675)
@@ -136,149 +128,221 @@
 #define DEVICE_USE_CODEC3(x) ((x)>=700)
 #define DEVICE_USE_CODEC23(x) ((x)>=675)
 
-/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
-struct pwc_iso_buf
-{
-	void *data;
-	int  length;
-	int  read;
-	struct urb *urb;
-};
+/* from pwc-dec.h */
+#define PWCX_FLAG_PLANAR        0x0001
+
+/* Request types: video */
+#define SET_LUM_CTL			0x01
+#define GET_LUM_CTL			0x02
+#define SET_CHROM_CTL			0x03
+#define GET_CHROM_CTL			0x04
+#define SET_STATUS_CTL			0x05
+#define GET_STATUS_CTL			0x06
+#define SET_EP_STREAM_CTL		0x07
+#define GET_EP_STREAM_CTL		0x08
+#define GET_XX_CTL			0x09
+#define SET_XX_CTL			0x0A
+#define GET_XY_CTL			0x0B
+#define SET_XY_CTL			0x0C
+#define SET_MPT_CTL			0x0D
+#define GET_MPT_CTL			0x0E
+
+/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
+#define AGC_MODE_FORMATTER			0x2000
+#define PRESET_AGC_FORMATTER			0x2100
+#define SHUTTER_MODE_FORMATTER			0x2200
+#define PRESET_SHUTTER_FORMATTER		0x2300
+#define PRESET_CONTOUR_FORMATTER		0x2400
+#define AUTO_CONTOUR_FORMATTER			0x2500
+#define BACK_LIGHT_COMPENSATION_FORMATTER	0x2600
+#define CONTRAST_FORMATTER			0x2700
+#define DYNAMIC_NOISE_CONTROL_FORMATTER		0x2800
+#define FLICKERLESS_MODE_FORMATTER		0x2900
+#define AE_CONTROL_SPEED			0x2A00
+#define BRIGHTNESS_FORMATTER			0x2B00
+#define GAMMA_FORMATTER				0x2C00
+
+/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
+#define WB_MODE_FORMATTER			0x1000
+#define AWB_CONTROL_SPEED_FORMATTER		0x1100
+#define AWB_CONTROL_DELAY_FORMATTER		0x1200
+#define PRESET_MANUAL_RED_GAIN_FORMATTER	0x1300
+#define PRESET_MANUAL_BLUE_GAIN_FORMATTER	0x1400
+#define COLOUR_MODE_FORMATTER			0x1500
+#define SATURATION_MODE_FORMATTER1		0x1600
+#define SATURATION_MODE_FORMATTER2		0x1700
+
+/* Selectors for the Status controls [GS]ET_STATUS_CTL */
+#define SAVE_USER_DEFAULTS_FORMATTER		0x0200
+#define RESTORE_USER_DEFAULTS_FORMATTER		0x0300
+#define RESTORE_FACTORY_DEFAULTS_FORMATTER	0x0400
+#define READ_AGC_FORMATTER			0x0500
+#define READ_SHUTTER_FORMATTER			0x0600
+#define READ_RED_GAIN_FORMATTER			0x0700
+#define READ_BLUE_GAIN_FORMATTER		0x0800
+
+/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
+#define PT_RELATIVE_CONTROL_FORMATTER		0x01
+#define PT_RESET_CONTROL_FORMATTER		0x02
+#define PT_STATUS_FORMATTER			0x03
 
 /* intermediate buffers with raw data from the USB cam */
 struct pwc_frame_buf
 {
-   void *data;
-   volatile int filled;		/* number of bytes filled */
-   struct pwc_frame_buf *next;	/* list */
-};
-
-/* additionnal informations used when dealing image between kernel and userland */
-struct pwc_imgbuf
-{
-	unsigned long offset;	/* offset of this buffer in the big array of image_data */
-	int   vma_use_count;	/* count the number of time this memory is mapped */
+	struct vb2_buffer vb;	/* common v4l buffer stuff -- must be first */
+	struct list_head list;
+	void *data;
+	int filled;		/* number of bytes filled */
 };
 
 struct pwc_device
 {
 	struct video_device vdev;
+	struct mutex modlock;
 
-   /* Pointer to our usb_device, may be NULL after unplug */
-   struct usb_device *udev;
+	/* Pointer to our usb_device, may be NULL after unplug */
+	struct usb_device *udev;
+	/* Protects the setting of udev to NULL by our disconnect handler */
+	struct mutex udevlock;
 
-   int type;                    /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
-   int release;			/* release number */
-   int features;		/* feature bits */
-   char serial[30];		/* serial number (string) */
-   int error_status;		/* set when something goes wrong with the cam (unplugged, USB errors) */
-   int usb_init;		/* set when the cam has been initialized over USB */
+	/* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
+	int type;
+	int release;		/* release number */
+	int features;		/* feature bits */
+	char serial[30];	/* serial number (string) */
 
-   /*** Video data ***/
-   int vopen;			/* flag */
-   int vendpoint;		/* video isoc endpoint */
-   int vcinterface;		/* video control interface */
-   int valternate;		/* alternate interface needed */
-   int vframes, vsize;		/* frames-per-second & size (see PSZ_*) */
-   int pixfmt;			/* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */
-   int vframe_count;		/* received frames */
-   int vframes_dumped; 		/* counter for dumped frames */
-   int vframes_error;		/* frames received in error */
-   int vmax_packet_size;	/* USB maxpacket size */
-   int vlast_packet_size;	/* for frame synchronisation */
-   int visoc_errors;		/* number of contiguous ISOC errors */
-   int vcompression;		/* desired compression factor */
-   int vbandlength;		/* compressed band length; 0 is uncompressed */
-   char vsnapshot;		/* snapshot mode */
-   char vsync;			/* used by isoc handler */
-   char vmirror;		/* for ToUCaM series */
-	char unplugged;
+	/*** Video data ***/
+	struct file *capt_file;	/* file doing video capture */
+	int vendpoint;		/* video isoc endpoint */
+	int vcinterface;	/* video control interface */
+	int valternate;		/* alternate interface needed */
+	int vframes, vsize;	/* frames-per-second & size (see PSZ_*) */
+	int pixfmt;		/* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */
+	int vframe_count;	/* received frames */
+	int vmax_packet_size;	/* USB maxpacket size */
+	int vlast_packet_size;	/* for frame synchronisation */
+	int visoc_errors;	/* number of contiguous ISOC errors */
+	int vcompression;	/* desired compression factor */
+	int vbandlength;	/* compressed band length; 0 is uncompressed */
+	char vsnapshot;		/* snapshot mode */
+	char vsync;		/* used by isoc handler */
+	char vmirror;		/* for ToUCaM series */
+	char power_save;	/* Do powersaving for this cam */
 
-   int cmd_len;
-   unsigned char cmd_buf[13];
+	int cmd_len;
+	unsigned char cmd_buf[13];
 
-   /* The image acquisition requires 3 to 4 steps:
-      1. data is gathered in short packets from the USB controller
-      2. data is synchronized and packed into a frame buffer
-      3a. in case data is compressed, decompress it directly into image buffer
-      3b. in case data is uncompressed, copy into image buffer with viewport
-      4. data is transferred to the user process
+	struct urb *urbs[MAX_ISO_BUFS];
+	char iso_init;
 
-      Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
-      We have in effect a back-to-back-double-buffer system.
-    */
-   /* 1: isoc */
-   struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
-   char iso_init;
+	/* videobuf2 queue and queued buffers list */
+	struct vb2_queue vb_queue;
+	struct list_head queued_bufs;
+	spinlock_t queued_bufs_lock;
 
-   /* 2: frame */
-   struct pwc_frame_buf *fbuf;	/* all frames */
-   struct pwc_frame_buf *empty_frames, *empty_frames_tail;	/* all empty frames */
-   struct pwc_frame_buf *full_frames, *full_frames_tail;	/* all filled frames */
-   struct pwc_frame_buf *fill_frame;	/* frame currently being filled */
-   struct pwc_frame_buf *read_frame;	/* frame currently read by user process */
-   int frame_header_size, frame_trailer_size;
-   int frame_size;
-   int frame_total_size; /* including header & trailer */
-   int drop_frames;
+	/*
+	 * Frame currently being filled, this only gets touched by the
+	 * isoc urb complete handler, and by stream start / stop since
+	 * start / stop touch it before / after starting / killing the urbs
+	 * no locking is needed around this
+	 */
+	struct pwc_frame_buf *fill_buf;
 
-   /* 3: decompression */
-   void *decompress_data;		/* private data for decompression engine */
+	int frame_header_size, frame_trailer_size;
+	int frame_size;
+	int frame_total_size;	/* including header & trailer */
+	int drop_frames;
 
-   /* 4: image */
-   /* We have an 'image' and a 'view', where 'image' is the fixed-size image
-      as delivered by the camera, and 'view' is the size requested by the
-      program. The camera image is centered in this viewport, laced with
-      a gray or black border. view_min <= image <= view <= view_max;
-    */
-   int image_mask;			/* bitmask of supported sizes */
-   struct pwc_coord view_min, view_max;	/* minimum and maximum viewable sizes */
-   struct pwc_coord abs_max;            /* maximum supported size with compression */
-   struct pwc_coord image, view;	/* image and viewport size */
-   struct pwc_coord offset;		/* offset within the viewport */
+	void *decompress_data;	/* private data for decompression engine */
 
-   void *image_data;			/* total buffer, which is subdivided into ... */
-   struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */
-   int fill_image;			/* ...which are rotated. */
-   int len_per_image;			/* length per image */
-   int image_read_pos;			/* In case we read data in pieces, keep track of were we are in the imagebuffer */
-   int image_used[MAX_IMAGES];		/* For MCAPTURE and SYNC */
+	/*
+	 * We have an 'image' and a 'view', where 'image' is the fixed-size img
+	 * as delivered by the camera, and 'view' is the size requested by the
+	 * program. The camera image is centered in this viewport, laced with
+	 * a gray or black border. view_min <= image <= view <= view_max;
+	 */
+	int image_mask;				/* supported sizes */
+	struct pwc_coord view_min, view_max;	/* minimum and maximum view */
+	struct pwc_coord abs_max;		/* maximum supported size */
+	struct pwc_coord image, view;		/* image and viewport size */
+	struct pwc_coord offset;		/* offset of the viewport */
 
-   struct mutex modlock;		/* to prevent races in video_open(), etc */
-   spinlock_t ptrlock;			/* for manipulating the buffer pointers */
+	/*** motorized pan/tilt feature */
+	struct pwc_mpt_range angle_range;
+	int pan_angle;			/* in degrees * 100 */
+	int tilt_angle;			/* absolute angle; 0,0 is home */
 
-   /*** motorized pan/tilt feature */
-   struct pwc_mpt_range angle_range;
-   int pan_angle;			/* in degrees * 100 */
-   int tilt_angle;			/* absolute angle; 0,0 is home position */
-   int snapshot_button_status;		/* set to 1 when the user push the button, reset to 0 when this value is read */
+	/*
+	 * Set to 1 when the user push the button, reset to 0
+	 * when this value is read from sysfs.
+	 */
+	int snapshot_button_status;
 #ifdef CONFIG_USB_PWC_INPUT_EVDEV
-   struct input_dev *button_dev;	/* webcam snapshot button input */
-   char button_phys[64];
+	struct input_dev *button_dev;	/* webcam snapshot button input */
+	char button_phys[64];
 #endif
 
-   /*** Misc. data ***/
-   wait_queue_head_t frameq;		/* When waiting for a frame to finish... */
-#if PWC_INT_PIPE
-   void *usb_int_handler;		/* for the interrupt endpoint */
-#endif
+	/* controls */
+	struct v4l2_ctrl_handler	ctrl_handler;
+	u16				saturation_fmt;
+	struct v4l2_ctrl		*brightness;
+	struct v4l2_ctrl		*contrast;
+	struct v4l2_ctrl		*saturation;
+	struct v4l2_ctrl		*gamma;
+	struct {
+		/* awb / red-blue balance cluster */
+		struct v4l2_ctrl	*auto_white_balance;
+		struct v4l2_ctrl	*red_balance;
+		struct v4l2_ctrl	*blue_balance;
+		/* usb ctrl transfers are slow, so we cache things */
+		int			color_bal_valid;
+		unsigned long		last_color_bal_update; /* In jiffies */
+		s32			last_red_balance;
+		s32			last_blue_balance;
+	};
+	struct {
+		/* autogain / gain cluster */
+		struct v4l2_ctrl	*autogain;
+		struct v4l2_ctrl	*gain;
+		int			gain_valid;
+		unsigned long		last_gain_update; /* In jiffies */
+		s32			last_gain;
+	};
+	struct {
+		/* exposure_auto / exposure cluster */
+		struct v4l2_ctrl	*exposure_auto;
+		struct v4l2_ctrl	*exposure;
+		int			exposure_valid;
+		unsigned long		last_exposure_update; /* In jiffies */
+		s32			last_exposure;
+	};
+	struct v4l2_ctrl		*colorfx;
+	struct {
+		/* autocontour/contour cluster */
+		struct v4l2_ctrl	*autocontour;
+		struct v4l2_ctrl	*contour;
+	};
+	struct v4l2_ctrl		*backlight;
+	struct v4l2_ctrl		*flicker;
+	struct v4l2_ctrl		*noise_reduction;
+	struct v4l2_ctrl		*save_user;
+	struct v4l2_ctrl		*restore_user;
+	struct v4l2_ctrl		*restore_factory;
+	struct {
+		/* motor control cluster */
+		struct v4l2_ctrl	*motor_pan;
+		struct v4l2_ctrl	*motor_tilt;
+		struct v4l2_ctrl	*motor_pan_reset;
+		struct v4l2_ctrl	*motor_tilt_reset;
+	};
+	/* CODEC3 models have both gain and exposure controlled by autogain */
+	struct v4l2_ctrl		*autogain_expo_cluster[3];
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /* Global variables */
 #ifdef CONFIG_USB_PWC_DEBUG
 extern int pwc_trace;
 #endif
-extern int pwc_mbufs;
-
-/** functions in pwc-if.c */
-int pwc_handle_frame(struct pwc_device *pdev);
-void pwc_next_image(struct pwc_device *pdev);
-int pwc_isoc_init(struct pwc_device *pdev);
-void pwc_isoc_cleanup(struct pwc_device *pdev);
 
 /** Functions in pwc-misc.c */
 /* sizes in pixels */
@@ -291,50 +355,25 @@
 /* Request a certain video mode. Returns < 0 if not possible */
 extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
 extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size);
-/* Calculate the number of bytes per image (not frame) */
 extern int pwc_mpt_reset(struct pwc_device *pdev, int flags);
 extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt);
-
-/* Various controls; should be obvious. Value 0..65535, or < 0 on error */
-extern int pwc_get_brightness(struct pwc_device *pdev);
-extern int pwc_set_brightness(struct pwc_device *pdev, int value);
-extern int pwc_get_contrast(struct pwc_device *pdev);
-extern int pwc_set_contrast(struct pwc_device *pdev, int value);
-extern int pwc_get_gamma(struct pwc_device *pdev);
-extern int pwc_set_gamma(struct pwc_device *pdev, int value);
-extern int pwc_get_saturation(struct pwc_device *pdev, int *value);
-extern int pwc_set_saturation(struct pwc_device *pdev, int value);
 extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
 extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
-extern int pwc_restore_user(struct pwc_device *pdev);
-extern int pwc_save_user(struct pwc_device *pdev);
-extern int pwc_restore_factory(struct pwc_device *pdev);
+extern int send_control_msg(struct pwc_device *pdev,
+			    u8 request, u16 value, void *buf, int buflen);
 
-/* exported for use by v4l2 controls */
-extern int pwc_get_red_gain(struct pwc_device *pdev, int *value);
-extern int pwc_set_red_gain(struct pwc_device *pdev, int value);
-extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value);
-extern int pwc_set_blue_gain(struct pwc_device *pdev, int value);
-extern int pwc_get_awb(struct pwc_device *pdev);
-extern int pwc_set_awb(struct pwc_device *pdev, int mode);
-extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value);
-extern int pwc_get_agc(struct pwc_device *pdev, int *value);
-extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value);
-extern int pwc_get_shutter_speed(struct pwc_device *pdev, int *value);
-
-extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour);
-extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour);
-extern int pwc_set_contour(struct pwc_device *pdev, int contour);
-extern int pwc_get_contour(struct pwc_device *pdev, int *contour);
-extern int pwc_set_backlight(struct pwc_device *pdev, int backlight);
-extern int pwc_get_backlight(struct pwc_device *pdev, int *backlight);
-extern int pwc_set_flicker(struct pwc_device *pdev, int flicker);
-extern int pwc_get_flicker(struct pwc_device *pdev, int *flicker);
-extern int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise);
-extern int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise);
+/* Control get / set helpers */
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data);
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+#define pwc_set_s8_ctrl pwc_set_u8_ctrl
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat);
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data);
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value);
+int pwc_init_controls(struct pwc_device *pdev);
 
 /* Power down or up the camera; not supported by all models */
-extern int pwc_camera_power(struct pwc_device *pdev, int power);
+extern void pwc_camera_power(struct pwc_device *pdev, int power);
 
 /* Private ioctl()s; see pwc-ioctl.h */
 extern long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
@@ -343,12 +382,6 @@
 
 /** pwc-uncompress.c */
 /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
-extern int pwc_decompress(struct pwc_device *pdev);
-
-#ifdef __cplusplus
-}
-#endif
-
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf);
 
 #endif
-/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index b42bfa5..d07df22 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -22,7 +22,6 @@
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
 #include <linux/time.h>
-#include <linux/version.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
@@ -40,7 +39,7 @@
 #include <mach/dma.h>
 #include <mach/camera.h>
 
-#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
+#define PXA_CAM_VERSION "0.0.6"
 #define PXA_CAM_DRV_NAME "pxa27x-camera"
 
 /* Camera Interface */
@@ -247,7 +246,7 @@
 	if (bytes_per_line < 0)
 		return bytes_per_line;
 
-	dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
+	dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
 
 	*size = bytes_per_line * icd->user_height;
 
@@ -262,13 +261,13 @@
 static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
 	int i;
 
 	BUG_ON(in_interrupt());
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		&buf->vb, buf->vb.baddr, buf->vb.bsize);
 
 	/*
@@ -429,7 +428,7 @@
 		struct videobuf_buffer *vb, enum v4l2_field field)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	struct device *dev = pcdev->soc_host.v4l2_dev.dev;
 	struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
@@ -636,11 +635,11 @@
 			       struct videobuf_buffer *vb)
 {
 	struct soc_camera_device *icd = vq->priv_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
 		__func__, vb, vb->baddr, vb->bsize, pcdev->active);
 
 	list_add_tail(&vb->queue, &pcdev->capture);
@@ -658,7 +657,7 @@
 	struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
 #ifdef DEBUG
 	struct soc_camera_device *icd = vq->priv_data;
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 
 	dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
 		vb, vb->baddr, vb->bsize);
@@ -843,7 +842,7 @@
 static void pxa_camera_init_videobuf(struct videobuf_queue *q,
 			      struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
 	/*
@@ -972,7 +971,7 @@
  */
 static int pxa_camera_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
 	if (pcdev->icd)
@@ -982,7 +981,7 @@
 
 	pcdev->icd = icd;
 
-	dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n",
+	dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
 		 icd->devnum);
 
 	return 0;
@@ -991,12 +990,12 @@
 /* Called with .video_lock held */
 static void pxa_camera_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 
 	BUG_ON(icd != pcdev->icd);
 
-	dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n",
+	dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
 		 icd->devnum);
 
 	/* disable capture, disable interrupts */
@@ -1057,7 +1056,7 @@
 static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
 				  unsigned long flags, __u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	unsigned long dw, bpp;
@@ -1152,7 +1151,7 @@
 
 static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	unsigned long bus_flags, camera_flags, common_flags;
 	int ret;
@@ -1210,7 +1209,7 @@
 static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
 				    unsigned char buswidth)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	unsigned long bus_flags, camera_flags;
 	int ret = test_platform_param(pcdev, buswidth, &bus_flags);
@@ -1247,7 +1246,7 @@
 				  struct soc_camera_format_xlate *xlate)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	int formats = 0, ret;
 	struct pxa_cam *cam;
 	enum v4l2_mbus_pixelcode code;
@@ -1335,9 +1334,9 @@
 			       struct v4l2_crop *a)
 {
 	struct v4l2_rect *rect = &a->c;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct pxa_camera_dev *pcdev = ici->priv;
-	struct device *dev = icd->dev.parent;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_sense sense = {
 		.master_clock = pcdev->mclk,
@@ -1379,7 +1378,7 @@
 			return ret;
 
 		if (pxa_camera_check_frame(mf.width, mf.height)) {
-			dev_warn(icd->dev.parent,
+			dev_warn(icd->parent,
 				 "Inconsistent state. Use S_FMT to repair\n");
 			return -EINVAL;
 		}
@@ -1406,9 +1405,9 @@
 static int pxa_camera_set_fmt(struct soc_camera_device *icd,
 			      struct v4l2_format *f)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct pxa_camera_dev *pcdev = ici->priv;
-	struct device *dev = icd->dev.parent;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	const struct soc_camera_format_xlate *xlate = NULL;
 	struct soc_camera_sense sense = {
@@ -1485,7 +1484,7 @@
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+		dev_warn(icd->parent, "Format %x not found\n", pixfmt);
 		return -EINVAL;
 	}
 
@@ -1499,16 +1498,11 @@
 			      &pix->height, 32, 2048, 0,
 			      pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
 
-	pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
-						    xlate->host_fmt);
-	if (pix->bytesperline < 0)
-		return pix->bytesperline;
-	pix->sizeimage = pix->height * pix->bytesperline;
-
 	/* limit to sensor capabilities */
 	mf.width	= pix->width;
 	mf.height	= pix->height;
-	mf.field	= pix->field;
+	/* Only progressive video supported so far */
+	mf.field	= V4L2_FIELD_NONE;
 	mf.colorspace	= pix->colorspace;
 	mf.code		= xlate->code;
 
@@ -1527,7 +1521,7 @@
 		break;
 	default:
 		/* TODO: support interlaced at least in pass-through mode */
-		dev_err(icd->dev.parent, "Field type %d unsupported.\n",
+		dev_err(icd->parent, "Field type %d unsupported.\n",
 			mf.field);
 		return -EINVAL;
 	}
@@ -1578,15 +1572,14 @@
 {
 	/* cap->name is set by the firendly caller:-> */
 	strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card));
-	cap->version = PXA_CAM_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 
 	return 0;
 }
 
-static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
+static int pxa_camera_suspend(struct device *dev)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	int i = 0, ret = 0;
 
@@ -1596,15 +1589,19 @@
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
 	pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
 
-	if ((pcdev->icd) && (pcdev->icd->ops->suspend))
-		ret = pcdev->icd->ops->suspend(pcdev->icd, state);
+	if (pcdev->icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+		ret = v4l2_subdev_call(sd, core, s_power, 0);
+		if (ret == -ENOIOCTLCMD)
+			ret = 0;
+	}
 
 	return ret;
 }
 
-static int pxa_camera_resume(struct soc_camera_device *icd)
+static int pxa_camera_resume(struct device *dev)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct pxa_camera_dev *pcdev = ici->priv;
 	int i = 0, ret = 0;
 
@@ -1618,8 +1615,12 @@
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
 	__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
 
-	if ((pcdev->icd) && (pcdev->icd->ops->resume))
-		ret = pcdev->icd->ops->resume(pcdev->icd);
+	if (pcdev->icd) {
+		struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+		ret = v4l2_subdev_call(sd, core, s_power, 1);
+		if (ret == -ENOIOCTLCMD)
+			ret = 0;
+	}
 
 	/* Restart frame capture if active buffer exists */
 	if (!ret && pcdev->active)
@@ -1632,8 +1633,6 @@
 	.owner		= THIS_MODULE,
 	.add		= pxa_camera_add_device,
 	.remove		= pxa_camera_remove_device,
-	.suspend	= pxa_camera_suspend,
-	.resume		= pxa_camera_resume,
 	.set_crop	= pxa_camera_set_crop,
 	.get_formats	= pxa_camera_get_formats,
 	.put_formats	= pxa_camera_put_formats,
@@ -1818,9 +1817,15 @@
 	return 0;
 }
 
+static struct dev_pm_ops pxa_camera_pm = {
+	.suspend	= pxa_camera_suspend,
+	.resume		= pxa_camera_resume,
+};
+
 static struct platform_driver pxa_camera_driver = {
 	.driver 	= {
 		.name	= PXA_CAM_DRV_NAME,
+		.pm	= &pxa_camera_pm,
 	},
 	.probe		= pxa_camera_probe,
 	.remove		= __devexit_p(pxa_camera_remove),
@@ -1843,4 +1848,5 @@
 MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
 MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(PXA_CAM_VERSION);
 MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 57e11b6..847ccc0 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -1364,10 +1364,9 @@
 	int data1, data2;
 	int ret;
 
-	/* This could be a BUG_ON() or a WARN_ON(), or remove it completely */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/* Read out the chip version register */
 	data1 = reg_read(client, RJ54N1_DEV_CODE);
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 5b9dce8..803c9c8 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -16,15 +16,10 @@
  * Example maximum bandwidth utilization:
  *
  * -full size, color mode YUYV or YUV422P: 2 channels at once
- *
  * -full or half size Grey scale: all 4 channels at once
- *
  * -half size, color mode YUYV or YUV422P: all 4 channels at once
- *
  * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
  *  at once.
- *  (TODO: Incorporate videodev2 frame rate(FR) enumeration,
- *  which is currently experimental.)
  *
  * 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
@@ -47,7 +42,6 @@
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
-#include <linux/version.h>
 #include <linux/mm.h>
 #include <media/videobuf-vmalloc.h>
 #include <media/v4l2-common.h>
@@ -56,12 +50,7 @@
 #include <linux/vmalloc.h>
 #include <linux/usb.h>
 
-#define S2255_MAJOR_VERSION	1
-#define S2255_MINOR_VERSION	21
-#define S2255_RELEASE		0
-#define S2255_VERSION		KERNEL_VERSION(S2255_MAJOR_VERSION, \
-					       S2255_MINOR_VERSION, \
-					       S2255_RELEASE)
+#define S2255_VERSION		"1.22.1"
 #define FIRMWARE_FILE_NAME "f2255usb.bin"
 
 /* default JPEG quality */
@@ -126,7 +115,7 @@
 #define MASK_COLOR       0x000000ff
 #define MASK_JPG_QUALITY 0x0000ff00
 #define MASK_INPUT_TYPE  0x000f0000
-/* frame decimation. Not implemented by V4L yet(experimental in V4L) */
+/* frame decimation. */
 #define FDEC_1		1	/* capture every frame. default */
 #define FDEC_2		2	/* capture every 2nd frame */
 #define FDEC_3		3	/* capture every 3rd frame */
@@ -312,9 +301,9 @@
 };
 
 /* current cypress EEPROM firmware version */
-#define S2255_CUR_USB_FWVER	((3 << 8) | 11)
+#define S2255_CUR_USB_FWVER	((3 << 8) | 12)
 /* current DSP FW version */
-#define S2255_CUR_DSP_FWVER     10102
+#define S2255_CUR_DSP_FWVER     10104
 /* Need DSP version 5+ for video status feature */
 #define S2255_MIN_DSP_STATUS      5
 #define S2255_MIN_DSP_COLORFILTER 8
@@ -502,7 +491,7 @@
 
 static void s2255_reset_dsppower(struct s2255_dev *dev)
 {
-	s2255_vendor_req(dev, 0x40, 0x0b0b, 0x0b01, NULL, 0, 1);
+	s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
 	msleep(10);
 	s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
 	msleep(600);
@@ -856,7 +845,6 @@
 	strlcpy(cap->driver, "s2255", sizeof(cap->driver));
 	strlcpy(cap->card, "s2255", sizeof(cap->card));
 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-	cap->version = S2255_VERSION;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 	return 0;
 }
@@ -1984,9 +1972,8 @@
 			  video_device_node_name(&channel->vdev));
 
 	}
-	printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n",
-	       S2255_MAJOR_VERSION,
-	       S2255_MINOR_VERSION);
+	printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
+	       S2255_VERSION);
 	/* if no channels registered, return error and probe will fail*/
 	if (atomic_read(&dev->num_channels) == 0) {
 		v4l2_device_unregister(&dev->v4l2_dev);
@@ -2302,15 +2289,12 @@
 	/* query the firmware */
 	fw_ver = s2255_get_fx2fw(dev);
 
-	printk(KERN_INFO "2255 usb firmware version %d.%d\n",
+	printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
 	       (fw_ver >> 8) & 0xff,
 	       fw_ver & 0xff);
 
 	if (fw_ver < S2255_CUR_USB_FWVER)
-		dev_err(&dev->udev->dev,
-			"usb firmware not up to date %d.%d\n",
-			(fw_ver >> 8) & 0xff,
-			fw_ver & 0xff);
+		printk(KERN_INFO "s2255: newer USB firmware available\n");
 
 	for (j = 0; j < MAX_CHANNELS; j++) {
 		struct s2255_channel *channel = &dev->channel[j];
@@ -2721,3 +2705,4 @@
 MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
 MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(S2255_VERSION);
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 81b4a82..0d730e5 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -11,7 +11,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/bug.h>
@@ -451,7 +450,6 @@
 	strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
 	strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
 	cap->bus_info[0] = 0;
-	cap->version = KERNEL_VERSION(1, 0, 0);
 	cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
 			    V4L2_CAP_VIDEO_CAPTURE_MPLANE;
 
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index bdf19ad..aa55066 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -12,7 +12,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/bug.h>
@@ -774,7 +773,6 @@
 	strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
 	strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
 	cap->bus_info[0] = 0;
-	cap->version = KERNEL_VERSION(1, 0, 0);
 	cap->capabilities = V4L2_CAP_STREAMING |
 		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
 		V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
@@ -1937,3 +1935,4 @@
 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
 MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.1");
diff --git a/drivers/media/video/s5p-mfc/Makefile b/drivers/media/video/s5p-mfc/Makefile
new file mode 100644
index 0000000..d066340
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
+s5p-mfc-y += s5p_mfc.o s5p_mfc_intr.o s5p_mfc_opr.o
+s5p-mfc-y += s5p_mfc_dec.o s5p_mfc_enc.o
+s5p-mfc-y += s5p_mfc_ctrl.o s5p_mfc_cmd.o
+s5p-mfc-y += s5p_mfc_pm.o s5p_mfc_shm.o
diff --git a/drivers/media/video/s5p-mfc/regs-mfc.h b/drivers/media/video/s5p-mfc/regs-mfc.h
new file mode 100644
index 0000000..053a8a8
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/regs-mfc.h
@@ -0,0 +1,413 @@
+/*
+ * Register definition file for Samsung MFC V5.1 Interface (FIMV) driver
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _REGS_FIMV_H
+#define _REGS_FIMV_H
+
+#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
+#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR) / 4)
+
+/* Number of bits that the buffer address should be shifted for particular
+ * MFC buffers.  */
+#define S5P_FIMV_START_ADDR		0x0000
+#define S5P_FIMV_END_ADDR		0xe008
+
+#define S5P_FIMV_SW_RESET		0x0000
+#define S5P_FIMV_RISC_HOST_INT		0x0008
+
+/* Command from HOST to RISC */
+#define S5P_FIMV_HOST2RISC_CMD		0x0030
+#define S5P_FIMV_HOST2RISC_ARG1		0x0034
+#define S5P_FIMV_HOST2RISC_ARG2		0x0038
+#define S5P_FIMV_HOST2RISC_ARG3		0x003c
+#define S5P_FIMV_HOST2RISC_ARG4		0x0040
+
+/* Command from RISC to HOST */
+#define S5P_FIMV_RISC2HOST_CMD		0x0044
+#define S5P_FIMV_RISC2HOST_CMD_MASK	0x1FFFF
+#define S5P_FIMV_RISC2HOST_ARG1		0x0048
+#define S5P_FIMV_RISC2HOST_ARG2		0x004c
+#define S5P_FIMV_RISC2HOST_ARG3		0x0050
+#define S5P_FIMV_RISC2HOST_ARG4		0x0054
+
+#define S5P_FIMV_FW_VERSION		0x0058
+#define S5P_FIMV_SYS_MEM_SZ		0x005c
+#define S5P_FIMV_FW_STATUS		0x0080
+
+/* Memory controller register */
+#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
+#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
+#define S5P_FIMV_MC_STATUS		0x0510
+
+/* Common register */
+#define S5P_FIMV_COMMON_BASE_A		0x0600
+#define S5P_FIMV_COMMON_BASE_B		0x0700
+
+/* Decoder */
+#define S5P_FIMV_DEC_CHROMA_ADR		(S5P_FIMV_COMMON_BASE_A)
+#define S5P_FIMV_DEC_LUMA_ADR		(S5P_FIMV_COMMON_BASE_B)
+
+/* H.264 decoding */
+#define S5P_FIMV_H264_VERT_NB_MV_ADR	(S5P_FIMV_COMMON_BASE_A + 0x8c)
+					/* vertical neighbor motion vector */
+#define S5P_FIMV_H264_NB_IP_ADR		(S5P_FIMV_COMMON_BASE_A + 0x90)
+					/* neighbor pixels for intra pred */
+#define S5P_FIMV_H264_MV_ADR		(S5P_FIMV_COMMON_BASE_B + 0x80)
+					/* H264 motion vector */
+
+/* MPEG4 decoding */
+#define S5P_FIMV_MPEG4_NB_DCAC_ADR	(S5P_FIMV_COMMON_BASE_A + 0x8c)
+					/* neighbor AC/DC coeff. */
+#define S5P_FIMV_MPEG4_UP_NB_MV_ADR	(S5P_FIMV_COMMON_BASE_A + 0x90)
+					/* upper neighbor motion vector */
+#define S5P_FIMV_MPEG4_SA_MV_ADR	(S5P_FIMV_COMMON_BASE_A + 0x94)
+					/* subseq. anchor motion vector */
+#define S5P_FIMV_MPEG4_OT_LINE_ADR	(S5P_FIMV_COMMON_BASE_A + 0x98)
+					/* overlap transform line */
+#define S5P_FIMV_MPEG4_SP_ADR		(S5P_FIMV_COMMON_BASE_A + 0xa8)
+					/* syntax parser */
+
+/* H.263 decoding */
+#define S5P_FIMV_H263_NB_DCAC_ADR	(S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_H263_UP_NB_MV_ADR	(S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_H263_SA_MV_ADR		(S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_H263_OT_LINE_ADR	(S5P_FIMV_COMMON_BASE_A + 0x98)
+
+/* VC-1 decoding */
+#define S5P_FIMV_VC1_NB_DCAC_ADR	(S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_VC1_UP_NB_MV_ADR	(S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_VC1_SA_MV_ADR		(S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_VC1_OT_LINE_ADR	(S5P_FIMV_COMMON_BASE_A + 0x98)
+#define S5P_FIMV_VC1_BITPLANE3_ADR	(S5P_FIMV_COMMON_BASE_A + 0x9c)
+					/* bitplane3 */
+#define S5P_FIMV_VC1_BITPLANE2_ADR	(S5P_FIMV_COMMON_BASE_A + 0xa0)
+					/* bitplane2 */
+#define S5P_FIMV_VC1_BITPLANE1_ADR	(S5P_FIMV_COMMON_BASE_A + 0xa4)
+					/* bitplane1 */
+
+/* Encoder */
+#define S5P_FIMV_ENC_REF0_LUMA_ADR	(S5P_FIMV_COMMON_BASE_A + 0x1c)
+#define S5P_FIMV_ENC_REF1_LUMA_ADR	(S5P_FIMV_COMMON_BASE_A + 0x20)
+					/* reconstructed luma */
+#define S5P_FIMV_ENC_REF0_CHROMA_ADR	(S5P_FIMV_COMMON_BASE_B)
+#define S5P_FIMV_ENC_REF1_CHROMA_ADR	(S5P_FIMV_COMMON_BASE_B + 0x04)
+					/* reconstructed chroma */
+#define S5P_FIMV_ENC_REF2_LUMA_ADR	(S5P_FIMV_COMMON_BASE_B + 0x10)
+#define S5P_FIMV_ENC_REF2_CHROMA_ADR	(S5P_FIMV_COMMON_BASE_B + 0x08)
+#define S5P_FIMV_ENC_REF3_LUMA_ADR	(S5P_FIMV_COMMON_BASE_B + 0x14)
+#define S5P_FIMV_ENC_REF3_CHROMA_ADR	(S5P_FIMV_COMMON_BASE_B + 0x0c)
+
+/* H.264 encoding */
+#define S5P_FIMV_H264_UP_MV_ADR		(S5P_FIMV_COMMON_BASE_A)
+					/* upper motion vector */
+#define S5P_FIMV_H264_NBOR_INFO_ADR	(S5P_FIMV_COMMON_BASE_A + 0x04)
+					/* entropy engine's neighbor info. */
+#define S5P_FIMV_H264_UP_INTRA_MD_ADR	(S5P_FIMV_COMMON_BASE_A + 0x08)
+					/* upper intra MD */
+#define S5P_FIMV_H264_COZERO_FLAG_ADR	(S5P_FIMV_COMMON_BASE_A + 0x10)
+					/* direct cozero flag */
+#define S5P_FIMV_H264_UP_INTRA_PRED_ADR	(S5P_FIMV_COMMON_BASE_B + 0x40)
+					/* upper intra PRED */
+
+/* H.263 encoding */
+#define S5P_FIMV_H263_UP_MV_ADR		(S5P_FIMV_COMMON_BASE_A)
+					/* upper motion vector */
+#define S5P_FIMV_H263_ACDC_COEF_ADR	(S5P_FIMV_COMMON_BASE_A + 0x04)
+					/* upper Q coeff. */
+
+/* MPEG4 encoding */
+#define S5P_FIMV_MPEG4_UP_MV_ADR	(S5P_FIMV_COMMON_BASE_A)
+					/* upper motion vector */
+#define S5P_FIMV_MPEG4_ACDC_COEF_ADR	(S5P_FIMV_COMMON_BASE_A + 0x04)
+					/* upper Q coeff. */
+#define S5P_FIMV_MPEG4_COZERO_FLAG_ADR	(S5P_FIMV_COMMON_BASE_A + 0x10)
+					/* direct cozero flag */
+
+#define S5P_FIMV_ENC_REF_B_LUMA_ADR     0x062c /* ref B Luma addr */
+#define S5P_FIMV_ENC_REF_B_CHROMA_ADR   0x0630 /* ref B Chroma addr */
+
+#define S5P_FIMV_ENC_CUR_LUMA_ADR	0x0718 /* current Luma addr */
+#define S5P_FIMV_ENC_CUR_CHROMA_ADR	0x071C /* current Chroma addr */
+
+/* Codec common register */
+#define S5P_FIMV_ENC_HSIZE_PX		0x0818 /* frame width at encoder */
+#define S5P_FIMV_ENC_VSIZE_PX		0x081c /* frame height at encoder */
+#define S5P_FIMV_ENC_PROFILE		0x0830 /* profile register */
+#define S5P_FIMV_ENC_PROFILE_H264_MAIN			0
+#define S5P_FIMV_ENC_PROFILE_H264_HIGH			1
+#define S5P_FIMV_ENC_PROFILE_H264_BASELINE		2
+#define S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE		0
+#define S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE	1
+#define S5P_FIMV_ENC_PIC_STRUCT		0x083c /* picture field/frame flag */
+#define S5P_FIMV_ENC_LF_CTRL		0x0848 /* loop filter control */
+#define S5P_FIMV_ENC_ALPHA_OFF		0x084c /* loop filter alpha offset */
+#define S5P_FIMV_ENC_BETA_OFF		0x0850 /* loop filter beta offset */
+#define S5P_FIMV_MR_BUSIF_CTRL		0x0854 /* hidden, bus interface ctrl */
+#define S5P_FIMV_ENC_PXL_CACHE_CTRL	0x0a00 /* pixel cache control */
+
+/* Channel & stream interface register */
+#define S5P_FIMV_SI_RTN_CHID		0x2000 /* Return CH inst ID register */
+#define S5P_FIMV_SI_CH0_INST_ID		0x2040 /* codec instance ID */
+#define S5P_FIMV_SI_CH1_INST_ID		0x2080 /* codec instance ID */
+/* Decoder */
+#define S5P_FIMV_SI_VRESOL		0x2004 /* vertical res of decoder */
+#define S5P_FIMV_SI_HRESOL		0x2008 /* horizontal res of decoder */
+#define S5P_FIMV_SI_BUF_NUMBER		0x200c /* number of frames in the
+								decoded pic */
+#define S5P_FIMV_SI_DISPLAY_Y_ADR	0x2010 /* luma addr of displayed pic */
+#define S5P_FIMV_SI_DISPLAY_C_ADR	0x2014 /* chroma addrof displayed pic */
+#define S5P_FIMV_SI_CONSUMED_BYTES	0x2018 /* Consumed number of bytes to
+							decode a frame */
+#define S5P_FIMV_SI_DISPLAY_STATUS	0x201c /* status of decoded picture */
+
+#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf */
+#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH0_CPB_SIZE	0x2058 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH0_DESC_SIZE	0x205c /* max size of descriptor buf */
+
+#define S5P_FIMV_SI_CH1_SB_ST_ADR	0x2084 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH1_SB_FRM_SIZE	0x2088 /* size of stream buf */
+#define S5P_FIMV_SI_CH1_DESC_ADR	0x208c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH1_CPB_SIZE	0x2098 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH1_DESC_SIZE	0x209c /* max size of descriptor buf */
+
+#define S5P_FIMV_CRC_LUMA0		0x2030 /* luma crc data per frame
+								(top field) */
+#define S5P_FIMV_CRC_CHROMA0		0x2034 /* chroma crc data per frame
+								(top field) */
+#define S5P_FIMV_CRC_LUMA1		0x2038 /* luma crc data per bottom
+								field */
+#define S5P_FIMV_CRC_CHROMA1		0x203c /* chroma crc data per bottom
+								field */
+
+/* Display status */
+#define S5P_FIMV_DEC_STATUS_DECODING_ONLY		0
+#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY		1
+#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY		2
+#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY		3
+#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK	7
+#define S5P_FIMV_DEC_STATUS_PROGRESSIVE			(0<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE			(1<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK		(1<<3)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO		(0<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR		(1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK		(1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_GENERATED		(1<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED		(0<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_MASK			(1<<5)
+
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_MASK		(3<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_INC		(1<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_DEC		(2<<4)
+
+/* Decode frame address */
+#define S5P_FIMV_DECODE_Y_ADR			0x2024
+#define S5P_FIMV_DECODE_C_ADR			0x2028
+
+/* Decoded frame tpe */
+#define S5P_FIMV_DECODE_FRAME_TYPE		0x2020
+#define S5P_FIMV_DECODE_FRAME_MASK		7
+
+#define S5P_FIMV_DECODE_FRAME_SKIPPED		0
+#define S5P_FIMV_DECODE_FRAME_I_FRAME		1
+#define S5P_FIMV_DECODE_FRAME_P_FRAME		2
+#define S5P_FIMV_DECODE_FRAME_B_FRAME		3
+#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME	4
+
+/* Sizes of buffers required for decoding */
+#define S5P_FIMV_DEC_NB_IP_SIZE			(32 * 1024)
+#define S5P_FIMV_DEC_VERT_NB_MV_SIZE		(16 * 1024)
+#define S5P_FIMV_DEC_NB_DCAC_SIZE		(16 * 1024)
+#define S5P_FIMV_DEC_UPNB_MV_SIZE		(68 * 1024)
+#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE		(136 * 1024)
+#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE     (32 * 1024)
+#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE		(2 * 1024)
+#define S5P_FIMV_DEC_STX_PARSER_SIZE		(68 * 1024)
+
+#define S5P_FIMV_DEC_BUF_ALIGN			(8 * 1024)
+#define S5P_FIMV_ENC_BUF_ALIGN			(8 * 1024)
+#define S5P_FIMV_NV12M_HALIGN			16
+#define S5P_FIMV_NV12M_LVALIGN			16
+#define S5P_FIMV_NV12M_CVALIGN			8
+#define S5P_FIMV_NV12MT_HALIGN			128
+#define S5P_FIMV_NV12MT_VALIGN			32
+#define S5P_FIMV_NV12M_SALIGN			2048
+#define S5P_FIMV_NV12MT_SALIGN			8192
+
+/* Sizes of buffers required for encoding */
+#define S5P_FIMV_ENC_UPMV_SIZE		0x10000
+#define S5P_FIMV_ENC_COLFLG_SIZE	0x10000
+#define S5P_FIMV_ENC_INTRAMD_SIZE	0x10000
+#define S5P_FIMV_ENC_INTRAPRED_SIZE	0x4000
+#define S5P_FIMV_ENC_NBORINFO_SIZE	0x10000
+#define S5P_FIMV_ENC_ACDCCOEF_SIZE	0x10000
+
+/* Encoder */
+#define S5P_FIMV_ENC_SI_STRM_SIZE	0x2004 /* stream size */
+#define S5P_FIMV_ENC_SI_PIC_CNT		0x2008 /* picture count */
+#define S5P_FIMV_ENC_SI_WRITE_PTR	0x200c /* write pointer */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE	0x2010 /* slice type(I/P/B/IDR) */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_NON_CODED	0
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_I		1
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_P		2
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_B		3
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_SKIPPED	4
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_OTHERS	5
+#define S5P_FIMV_ENCODED_Y_ADDR         0x2014 /* the addr of the encoded
+								luma pic */
+#define S5P_FIMV_ENCODED_C_ADDR         0x2018 /* the addr of the encoded
+								chroma pic */
+
+#define S5P_FIMV_ENC_SI_CH0_SB_ADR	0x2044 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_SB_SIZE	0x204c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR	0x2050 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR	0x2054 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH0_FRAME_INS	0x2058 /* frame insertion */
+
+#define S5P_FIMV_ENC_SI_CH1_SB_ADR	0x2084 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_SB_SIZE	0x208c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR	0x2090 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR	0x2094 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH1_FRAME_INS	0x2098 /* frame insertion */
+
+#define S5P_FIMV_ENC_PIC_TYPE_CTRL	0xc504 /* pic type level control */
+#define S5P_FIMV_ENC_B_RECON_WRITE_ON	0xc508 /* B frame recon write ctrl */
+#define S5P_FIMV_ENC_MSLICE_CTRL	0xc50c /* multi slice control */
+#define S5P_FIMV_ENC_MSLICE_MB		0xc510 /* MB number in the one slice */
+#define S5P_FIMV_ENC_MSLICE_BIT		0xc514 /* bit count for one slice */
+#define S5P_FIMV_ENC_CIR_CTRL		0xc518 /* number of intra refresh MB */
+#define S5P_FIMV_ENC_MAP_FOR_CUR	0xc51c /* linear or tiled mode */
+#define S5P_FIMV_ENC_PADDING_CTRL	0xc520 /* padding control */
+
+#define S5P_FIMV_ENC_RC_CONFIG		0xc5a0 /* RC config */
+#define S5P_FIMV_ENC_RC_BIT_RATE	0xc5a8 /* bit rate */
+#define S5P_FIMV_ENC_RC_QBOUND		0xc5ac /* max/min QP */
+#define S5P_FIMV_ENC_RC_RPARA		0xc5b0 /* rate control reaction coeff */
+#define S5P_FIMV_ENC_RC_MB_CTRL		0xc5b4 /* MB adaptive scaling */
+
+/* Encoder for H264 only */
+#define S5P_FIMV_ENC_H264_ENTROPY_MODE	0xd004 /* CAVLC or CABAC */
+#define S5P_FIMV_ENC_H264_ALPHA_OFF	0xd008 /* loop filter alpha offset */
+#define S5P_FIMV_ENC_H264_BETA_OFF	0xd00c /* loop filter beta offset */
+#define S5P_FIMV_ENC_H264_NUM_OF_REF	0xd010 /* number of reference for P/B */
+#define S5P_FIMV_ENC_H264_TRANS_FLAG	0xd034 /* 8x8 transform flag in PPS &
+								high profile */
+
+#define S5P_FIMV_ENC_RC_FRAME_RATE	0xd0d0 /* frame rate */
+
+/* Encoder for MPEG4 only */
+#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation ctrl */
+
+/* Additional */
+#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control Register */
+#define S5P_FIMV_SLICE_INT_MASK		1
+#define S5P_FIMV_SLICE_INT_SHIFT	31
+#define S5P_FIMV_DDELAY_ENA_SHIFT	30
+#define S5P_FIMV_DDELAY_VAL_MASK	0xff
+#define S5P_FIMV_DDELAY_VAL_SHIFT	16
+#define S5P_FIMV_DPB_COUNT_MASK		0xffff
+#define S5P_FIMV_DPB_FLUSH_MASK		1
+#define S5P_FIMV_DPB_FLUSH_SHIFT	14
+
+
+#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer register */
+#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064 /* address of shared memory */
+
+/* Codec numbers  */
+#define S5P_FIMV_CODEC_NONE		-1
+
+#define S5P_FIMV_CODEC_H264_DEC		0
+#define S5P_FIMV_CODEC_VC1_DEC		1
+#define S5P_FIMV_CODEC_MPEG4_DEC	2
+#define S5P_FIMV_CODEC_MPEG2_DEC	3
+#define S5P_FIMV_CODEC_H263_DEC		4
+#define S5P_FIMV_CODEC_VC1RCV_DEC	5
+
+#define S5P_FIMV_CODEC_H264_ENC		16
+#define S5P_FIMV_CODEC_MPEG4_ENC	17
+#define S5P_FIMV_CODEC_H263_ENC		18
+
+/* Channel Control Register */
+#define S5P_FIMV_CH_SEQ_HEADER		1
+#define S5P_FIMV_CH_FRAME_START		2
+#define S5P_FIMV_CH_LAST_FRAME		3
+#define S5P_FIMV_CH_INIT_BUFS		4
+#define S5P_FIMV_CH_FRAME_START_REALLOC	5
+#define S5P_FIMV_CH_MASK		7
+#define S5P_FIMV_CH_SHIFT		16
+
+
+/* Host to RISC command */
+#define S5P_FIMV_H2R_CMD_EMPTY		0
+#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE	1
+#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE	2
+#define S5P_FIMV_H2R_CMD_SYS_INIT	3
+#define S5P_FIMV_H2R_CMD_FLUSH		4
+#define S5P_FIMV_H2R_CMD_SLEEP		5
+#define S5P_FIMV_H2R_CMD_WAKEUP		6
+
+#define S5P_FIMV_R2H_CMD_EMPTY			0
+#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET	1
+#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET	2
+#define S5P_FIMV_R2H_CMD_RSV_RET		3
+#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET		4
+#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET		5
+#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET		6
+#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET	7
+#define S5P_FIMV_R2H_CMD_SYS_INIT_RET		8
+#define S5P_FIMV_R2H_CMD_FW_STATUS_RET		9
+#define S5P_FIMV_R2H_CMD_SLEEP_RET		10
+#define S5P_FIMV_R2H_CMD_WAKEUP_RET		11
+#define S5P_FIMV_R2H_CMD_FLUSH_RET		12
+#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET	15
+#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16
+#define S5P_FIMV_R2H_CMD_ERR_RET		32
+
+/* Error handling defines */
+#define S5P_FIMV_ERR_WARNINGS_START		145
+#define S5P_FIMV_ERR_DEC_MASK			0xFFFF
+#define S5P_FIMV_ERR_DEC_SHIFT			0
+#define S5P_FIMV_ERR_DSPL_MASK			0xFFFF0000
+#define S5P_FIMV_ERR_DSPL_SHIFT			16
+
+/* Shared memory registers' offsets */
+
+/* An offset of the start position in the stream when
+ * the start position is not aligned */
+#define S5P_FIMV_SHARED_CROP_INFO_H		0x0020
+#define S5P_FIMV_SHARED_CROP_LEFT_MASK		0xFFFF
+#define S5P_FIMV_SHARED_CROP_LEFT_SHIFT		0
+#define S5P_FIMV_SHARED_CROP_RIGHT_MASK		0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_RIGHT_SHIFT	16
+#define S5P_FIMV_SHARED_CROP_INFO_V		0x0024
+#define S5P_FIMV_SHARED_CROP_TOP_MASK		0xFFFF
+#define S5P_FIMV_SHARED_CROP_TOP_SHIFT		0
+#define S5P_FIMV_SHARED_CROP_BOTTOM_MASK	0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT	16
+#define S5P_FIMV_SHARED_SET_FRAME_TAG		0x0004
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_TOP	0x0008
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_BOT	0x000C
+#define S5P_FIMV_SHARED_START_BYTE_NUM		0x0018
+#define S5P_FIMV_SHARED_RC_VOP_TIMING		0x0030
+#define S5P_FIMV_SHARED_LUMA_DPB_SIZE		0x0064
+#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE		0x0068
+#define S5P_FIMV_SHARED_MV_SIZE			0x006C
+#define S5P_FIMV_SHARED_PIC_TIME_TOP		0x0010
+#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM		0x0014
+#define S5P_FIMV_SHARED_EXT_ENC_CONTROL		0x0028
+#define S5P_FIMV_SHARED_P_B_FRAME_QP		0x0070
+#define S5P_FIMV_SHARED_ASPECT_RATIO_IDC	0x0074
+#define S5P_FIMV_SHARED_EXTENDED_SAR		0x0078
+#define S5P_FIMV_SHARED_H264_I_PERIOD		0x009C
+#define S5P_FIMV_SHARED_RC_CONTROL_CONFIG	0x00A0
+
+#endif /* _REGS_FIMV_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
new file mode 100644
index 0000000..7dc7eab
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -0,0 +1,1274 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.1
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.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/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+#define S5P_MFC_NAME		"s5p-mfc"
+#define S5P_MFC_DEC_NAME	"s5p-mfc-dec"
+#define S5P_MFC_ENC_NAME	"s5p-mfc-enc"
+
+int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
+
+/* Helper functions for interrupt processing */
+/* Remove from hw execution round robin */
+static void clear_work_bit(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	spin_lock(&dev->condlock);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock(&dev->condlock);
+}
+
+/* Wake up context wait_queue */
+static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+			unsigned int err)
+{
+	ctx->int_cond = 1;
+	ctx->int_type = reason;
+	ctx->int_err = err;
+	wake_up(&ctx->queue);
+}
+
+/* Wake up device wait_queue */
+static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+			unsigned int err)
+{
+	dev->int_cond = 1;
+	dev->int_type = reason;
+	dev->int_err = err;
+	wake_up(&dev->queue);
+}
+
+void s5p_mfc_watchdog(unsigned long arg)
+{
+	struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
+
+	if (test_bit(0, &dev->hw_lock))
+		atomic_inc(&dev->watchdog_cnt);
+	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
+		/* This means that hw is busy and no interrupts were
+		 * generated by hw for the Nth time of running this
+		 * watchdog timer. This usually means a serious hw
+		 * error. Now it is time to kill all instances and
+		 * reset the MFC. */
+		mfc_err("Time out during waiting for HW\n");
+		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
+	}
+	dev->watchdog_timer.expires = jiffies +
+					msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+	add_timer(&dev->watchdog_timer);
+}
+
+static void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+	struct s5p_mfc_dev *dev;
+	struct s5p_mfc_ctx *ctx;
+	unsigned long flags;
+	int mutex_locked;
+	int i, ret;
+
+	dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
+
+	mfc_err("Driver timeout error handling\n");
+	/* Lock the mutex that protects open and release.
+	 * This is necessary as they may load and unload firmware. */
+	mutex_locked = mutex_trylock(&dev->mfc_mutex);
+	if (!mutex_locked)
+		mfc_err("Error: some instance may be closing/opening\n");
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	s5p_mfc_clock_off();
+
+	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+		ctx = dev->ctx[i];
+		if (!ctx)
+			continue;
+		ctx->state = MFCINST_ERROR;
+		s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+		s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+		clear_work_bit(ctx);
+		wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_ERR_RET, 0);
+	}
+	clear_bit(0, &dev->hw_lock);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	/* Double check if there is at least one instance running.
+	 * If no instance is in memory than no firmware should be present */
+	if (dev->num_inst > 0) {
+		ret = s5p_mfc_reload_firmware(dev);
+		if (ret) {
+			mfc_err("Failed to reload FW\n");
+			goto unlock;
+		}
+		s5p_mfc_clock_on();
+		ret = s5p_mfc_init_hw(dev);
+		if (ret)
+			mfc_err("Failed to reinit FW\n");
+	}
+unlock:
+	if (mutex_locked)
+		mutex_unlock(&dev->mfc_mutex);
+}
+
+static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	if (!vdev) {
+		mfc_err("failed to get video_device");
+		return MFCNODE_INVALID;
+	}
+	if (vdev->index == 0)
+		return MFCNODE_DECODER;
+	else if (vdev->index == 1)
+		return MFCNODE_ENCODER;
+	return MFCNODE_INVALID;
+}
+
+static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
+{
+	mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
+	mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+	mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
+}
+
+static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_buf *dst_buf;
+
+	ctx->state = MFCINST_FINISHED;
+	ctx->sequence++;
+	while (!list_empty(&ctx->dst_queue)) {
+		dst_buf = list_entry(ctx->dst_queue.next,
+				     struct s5p_mfc_buf, list);
+		mfc_debug(2, "Cleaning up buffer: %d\n",
+					  dst_buf->b->v4l2_buf.index);
+		vb2_set_plane_payload(dst_buf->b, 0, 0);
+		vb2_set_plane_payload(dst_buf->b, 1, 0);
+		list_del(&dst_buf->list);
+		ctx->dst_queue_cnt--;
+		dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
+
+		if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+			s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+			dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+		else
+			dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+
+		ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
+		vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
+	}
+}
+
+static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf  *dst_buf, *src_buf;
+	size_t dec_y_addr = s5p_mfc_get_dec_y_adr();
+	unsigned int frame_type = s5p_mfc_get_frame_type();
+
+	/* Copy timestamp / timecode from decoded src to dst and set
+	   appropraite flags */
+	src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+		if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dec_y_addr) {
+			memcpy(&dst_buf->b->v4l2_buf.timecode,
+				&src_buf->b->v4l2_buf.timecode,
+				sizeof(struct v4l2_timecode));
+			memcpy(&dst_buf->b->v4l2_buf.timestamp,
+				&src_buf->b->v4l2_buf.timestamp,
+				sizeof(struct timeval));
+			switch (frame_type) {
+			case S5P_FIMV_DECODE_FRAME_I_FRAME:
+				dst_buf->b->v4l2_buf.flags |=
+						V4L2_BUF_FLAG_KEYFRAME;
+				break;
+			case S5P_FIMV_DECODE_FRAME_P_FRAME:
+				dst_buf->b->v4l2_buf.flags |=
+						V4L2_BUF_FLAG_PFRAME;
+				break;
+			case S5P_FIMV_DECODE_FRAME_B_FRAME:
+				dst_buf->b->v4l2_buf.flags |=
+						V4L2_BUF_FLAG_BFRAME;
+				break;
+			}
+			break;
+		}
+	}
+}
+
+static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf  *dst_buf;
+	size_t dspl_y_addr = s5p_mfc_get_dspl_y_adr();
+	unsigned int frame_type = s5p_mfc_get_frame_type();
+	unsigned int index;
+
+	/* If frame is same as previous then skip and do not dequeue */
+	if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
+		if (!ctx->after_packed_pb)
+			ctx->sequence++;
+		ctx->after_packed_pb = 0;
+		return;
+	}
+	ctx->sequence++;
+	/* The MFC returns address of the buffer, now we have to
+	 * check which videobuf does it correspond to */
+	list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+		/* Check if this is the buffer we're looking for */
+		if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dspl_y_addr) {
+			list_del(&dst_buf->list);
+			ctx->dst_queue_cnt--;
+			dst_buf->b->v4l2_buf.sequence = ctx->sequence;
+			if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+				s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+				dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+			else
+				dst_buf->b->v4l2_buf.field =
+							V4L2_FIELD_INTERLACED;
+			vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
+			vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
+			clear_bit(dst_buf->b->v4l2_buf.index,
+							&ctx->dec_dst_flag);
+
+			vb2_buffer_done(dst_buf->b,
+				err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+			index = dst_buf->b->v4l2_buf.index;
+			break;
+		}
+	}
+}
+
+/* Handle frame decoding interrupt */
+static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
+					unsigned int reason, unsigned int err)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned int dst_frame_status;
+	struct s5p_mfc_buf *src_buf;
+	unsigned long flags;
+	unsigned int res_change;
+
+	unsigned int index;
+
+	dst_frame_status = s5p_mfc_get_dspl_status()
+				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+	res_change = s5p_mfc_get_dspl_status()
+				& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK;
+	mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
+	if (ctx->state == MFCINST_RES_CHANGE_INIT)
+		ctx->state = MFCINST_RES_CHANGE_FLUSH;
+	if (res_change) {
+		ctx->state = MFCINST_RES_CHANGE_INIT;
+		s5p_mfc_clear_int_flags(dev);
+		wake_up_ctx(ctx, reason, err);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		s5p_mfc_clock_off();
+		s5p_mfc_try_run(dev);
+		return;
+	}
+	if (ctx->dpb_flush_flag)
+		ctx->dpb_flush_flag = 0;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	/* All frames remaining in the buffer have been extracted  */
+	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+		if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+			s5p_mfc_handle_frame_all_extracted(ctx);
+			ctx->state = MFCINST_RES_CHANGE_END;
+			goto leave_handle_frame;
+		} else {
+			s5p_mfc_handle_frame_all_extracted(ctx);
+		}
+	}
+
+	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
+		dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+		s5p_mfc_handle_frame_copy_time(ctx);
+
+	/* A frame has been decoded and is in the buffer  */
+	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
+		s5p_mfc_handle_frame_new(ctx, err);
+	} else {
+		mfc_debug(2, "No frame decode\n");
+	}
+	/* Mark source buffer as complete */
+	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
+		&& !list_empty(&ctx->src_queue)) {
+		src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+								list);
+		ctx->consumed_stream += s5p_mfc_get_consumed_stream();
+		if (ctx->codec_mode != S5P_FIMV_CODEC_H264_DEC &&
+			s5p_mfc_get_frame_type() == S5P_FIMV_DECODE_FRAME_P_FRAME
+					&& ctx->consumed_stream + STUFF_BYTE <
+					src_buf->b->v4l2_planes[0].bytesused) {
+			/* Run MFC again on the same buffer */
+			mfc_debug(2, "Running again the same buffer\n");
+			ctx->after_packed_pb = 1;
+		} else {
+			index = src_buf->b->v4l2_buf.index;
+			mfc_debug(2, "MFC needs next buffer\n");
+			ctx->consumed_stream = 0;
+			list_del(&src_buf->list);
+			ctx->src_queue_cnt--;
+			if (s5p_mfc_err_dec(err) > 0)
+				vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR);
+			else
+				vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
+		}
+	}
+leave_handle_frame:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
+				    || ctx->dst_queue_cnt < ctx->dpb_count)
+		clear_work_bit(ctx);
+	s5p_mfc_clear_int_flags(dev);
+	wake_up_ctx(ctx, reason, err);
+	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+		BUG();
+	s5p_mfc_clock_off();
+	s5p_mfc_try_run(dev);
+}
+
+/* Error handling for interrupt */
+static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
+				 unsigned int reason, unsigned int err)
+{
+	struct s5p_mfc_dev *dev;
+	unsigned long flags;
+
+	/* If no context is available then all necessary
+	 * processing has been done. */
+	if (ctx == 0)
+		return;
+
+	dev = ctx->dev;
+	mfc_err("Interrupt Error: %08x\n", err);
+	s5p_mfc_clear_int_flags(dev);
+	wake_up_dev(dev, reason, err);
+
+	/* Error recovery is dependent on the state of context */
+	switch (ctx->state) {
+	case MFCINST_INIT:
+		/* This error had to happen while acquireing instance */
+	case MFCINST_GOT_INST:
+		/* This error had to happen while parsing the header */
+	case MFCINST_HEAD_PARSED:
+		/* This error had to happen while setting dst buffers */
+	case MFCINST_RETURN_INST:
+		/* This error had to happen while releasing instance */
+		clear_work_bit(ctx);
+		wake_up_ctx(ctx, reason, err);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		s5p_mfc_clock_off();
+		ctx->state = MFCINST_ERROR;
+		break;
+	case MFCINST_FINISHING:
+	case MFCINST_FINISHED:
+	case MFCINST_RUNNING:
+		/* It is higly probable that an error occured
+		 * while decoding a frame */
+		clear_work_bit(ctx);
+		ctx->state = MFCINST_ERROR;
+		/* Mark all dst buffers as having an error */
+		spin_lock_irqsave(&dev->irqlock, flags);
+		s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+		/* Mark all src buffers as having an error */
+		s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		s5p_mfc_clock_off();
+		break;
+	default:
+		mfc_err("Encountered an error interrupt which had not been handled\n");
+		break;
+	}
+	return;
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
+				 unsigned int reason, unsigned int err)
+{
+	struct s5p_mfc_dev *dev;
+	unsigned int guard_width, guard_height;
+
+	if (ctx == 0)
+		return;
+	dev = ctx->dev;
+	if (ctx->c_ops->post_seq_start) {
+		if (ctx->c_ops->post_seq_start(ctx))
+			mfc_err("post_seq_start() failed\n");
+	} else {
+		ctx->img_width = s5p_mfc_get_img_width();
+		ctx->img_height = s5p_mfc_get_img_height();
+
+		ctx->buf_width = ALIGN(ctx->img_width,
+						S5P_FIMV_NV12MT_HALIGN);
+		ctx->buf_height = ALIGN(ctx->img_height,
+						S5P_FIMV_NV12MT_VALIGN);
+		mfc_debug(2, "SEQ Done: Movie dimensions %dx%d, "
+			"buffer dimensions: %dx%d\n", ctx->img_width,
+				ctx->img_height, ctx->buf_width,
+						ctx->buf_height);
+		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+			ctx->luma_size = ALIGN(ctx->buf_width *
+				ctx->buf_height, S5P_FIMV_DEC_BUF_ALIGN);
+			ctx->chroma_size = ALIGN(ctx->buf_width *
+					 ALIGN((ctx->img_height >> 1),
+					       S5P_FIMV_NV12MT_VALIGN),
+					       S5P_FIMV_DEC_BUF_ALIGN);
+			ctx->mv_size = ALIGN(ctx->buf_width *
+					ALIGN((ctx->buf_height >> 2),
+					S5P_FIMV_NV12MT_VALIGN),
+					S5P_FIMV_DEC_BUF_ALIGN);
+		} else {
+			guard_width = ALIGN(ctx->img_width + 24,
+					S5P_FIMV_NV12MT_HALIGN);
+			guard_height = ALIGN(ctx->img_height + 16,
+						S5P_FIMV_NV12MT_VALIGN);
+			ctx->luma_size = ALIGN(guard_width *
+				guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+			guard_width = ALIGN(ctx->img_width + 16,
+						S5P_FIMV_NV12MT_HALIGN);
+			guard_height = ALIGN((ctx->img_height >> 1) + 4,
+						S5P_FIMV_NV12MT_VALIGN);
+			ctx->chroma_size = ALIGN(guard_width *
+				guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+			ctx->mv_size = 0;
+		}
+		ctx->dpb_count = s5p_mfc_get_dpb_count();
+		if (ctx->img_width == 0 || ctx->img_width == 0)
+			ctx->state = MFCINST_ERROR;
+		else
+			ctx->state = MFCINST_HEAD_PARSED;
+	}
+	s5p_mfc_clear_int_flags(dev);
+	clear_work_bit(ctx);
+	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+		BUG();
+	s5p_mfc_clock_off();
+	s5p_mfc_try_run(dev);
+	wake_up_ctx(ctx, reason, err);
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
+				 unsigned int reason, unsigned int err)
+{
+	struct s5p_mfc_buf *src_buf;
+	struct s5p_mfc_dev *dev;
+	unsigned long flags;
+
+	if (ctx == 0)
+		return;
+	dev = ctx->dev;
+	s5p_mfc_clear_int_flags(dev);
+	ctx->int_type = reason;
+	ctx->int_err = err;
+	ctx->int_cond = 1;
+	spin_lock(&dev->condlock);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock(&dev->condlock);
+	if (err == 0) {
+		ctx->state = MFCINST_RUNNING;
+		if (!ctx->dpb_flush_flag) {
+			spin_lock_irqsave(&dev->irqlock, flags);
+			if (!list_empty(&ctx->src_queue)) {
+				src_buf = list_entry(ctx->src_queue.next,
+					     struct s5p_mfc_buf, list);
+				list_del(&src_buf->list);
+				ctx->src_queue_cnt--;
+				vb2_buffer_done(src_buf->b,
+						VB2_BUF_STATE_DONE);
+			}
+			spin_unlock_irqrestore(&dev->irqlock, flags);
+		} else {
+			ctx->dpb_flush_flag = 0;
+		}
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+
+		s5p_mfc_clock_off();
+
+		wake_up(&ctx->queue);
+		s5p_mfc_try_run(dev);
+	} else {
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+
+		s5p_mfc_clock_off();
+
+		wake_up(&ctx->queue);
+	}
+}
+
+/* Interrupt processing */
+static irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+	struct s5p_mfc_dev *dev = priv;
+	struct s5p_mfc_ctx *ctx;
+	unsigned int reason;
+	unsigned int err;
+
+	mfc_debug_enter();
+	/* Reset the timeout watchdog */
+	atomic_set(&dev->watchdog_cnt, 0);
+	ctx = dev->ctx[dev->curr_ctx];
+	/* Get the reason of interrupt and the error code */
+	reason = s5p_mfc_get_int_reason();
+	err = s5p_mfc_get_int_err();
+	mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
+	switch (reason) {
+	case S5P_FIMV_R2H_CMD_ERR_RET:
+		/* An error has occured */
+		if (ctx->state == MFCINST_RUNNING &&
+			s5p_mfc_err_dec(err) >= S5P_FIMV_ERR_WARNINGS_START)
+			s5p_mfc_handle_frame(ctx, reason, err);
+		else
+			s5p_mfc_handle_error(ctx, reason, err);
+		clear_bit(0, &dev->enter_suspend);
+		break;
+
+	case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+	case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+		if (ctx->c_ops->post_frame_start) {
+			if (ctx->c_ops->post_frame_start(ctx))
+				mfc_err("post_frame_start() failed\n");
+			s5p_mfc_clear_int_flags(dev);
+			wake_up_ctx(ctx, reason, err);
+			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+				BUG();
+			s5p_mfc_clock_off();
+			s5p_mfc_try_run(dev);
+		} else {
+			s5p_mfc_handle_frame(ctx, reason, err);
+		}
+		break;
+
+	case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+		s5p_mfc_handle_seq_done(ctx, reason, err);
+		break;
+
+	case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+		ctx->inst_no = s5p_mfc_get_inst_no();
+		ctx->state = MFCINST_GOT_INST;
+		clear_work_bit(ctx);
+		wake_up(&ctx->queue);
+		goto irq_cleanup_hw;
+
+	case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+		clear_work_bit(ctx);
+		ctx->state = MFCINST_FREE;
+		wake_up(&ctx->queue);
+		goto irq_cleanup_hw;
+
+	case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+	case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+	case S5P_FIMV_R2H_CMD_SLEEP_RET:
+	case S5P_FIMV_R2H_CMD_WAKEUP_RET:
+		if (ctx)
+			clear_work_bit(ctx);
+		s5p_mfc_clear_int_flags(dev);
+		wake_up_dev(dev, reason, err);
+		clear_bit(0, &dev->hw_lock);
+		clear_bit(0, &dev->enter_suspend);
+		break;
+
+	case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+		s5p_mfc_handle_init_buffers(ctx, reason, err);
+		break;
+	default:
+		mfc_debug(2, "Unknown int reason\n");
+		s5p_mfc_clear_int_flags(dev);
+	}
+	mfc_debug_leave();
+	return IRQ_HANDLED;
+irq_cleanup_hw:
+	s5p_mfc_clear_int_flags(dev);
+	ctx->int_type = reason;
+	ctx->int_err = err;
+	ctx->int_cond = 1;
+	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+		mfc_err("Failed to unlock hw\n");
+
+	s5p_mfc_clock_off();
+
+	s5p_mfc_try_run(dev);
+	mfc_debug(2, "Exit via irq_cleanup_hw\n");
+	return IRQ_HANDLED;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+	struct s5p_mfc_ctx *ctx = NULL;
+	struct vb2_queue *q;
+	unsigned long flags;
+	int ret = 0;
+
+	mfc_debug_enter();
+	dev->num_inst++;	/* It is guarded by mfc_mutex in vfd */
+	/* Allocate memory for context */
+	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+	if (!ctx) {
+		mfc_err("Not enough memory\n");
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+	ctx->dev = dev;
+	INIT_LIST_HEAD(&ctx->src_queue);
+	INIT_LIST_HEAD(&ctx->dst_queue);
+	ctx->src_queue_cnt = 0;
+	ctx->dst_queue_cnt = 0;
+	/* Get context number */
+	ctx->num = 0;
+	while (dev->ctx[ctx->num]) {
+		ctx->num++;
+		if (ctx->num >= MFC_NUM_CONTEXTS) {
+			mfc_err("Too many open contexts\n");
+			ret = -EBUSY;
+			goto err_no_ctx;
+		}
+	}
+	/* Mark context as idle */
+	spin_lock_irqsave(&dev->condlock, flags);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock_irqrestore(&dev->condlock, flags);
+	dev->ctx[ctx->num] = ctx;
+	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+		ctx->type = MFCINST_DECODER;
+		ctx->c_ops = get_dec_codec_ops();
+		/* Setup ctrl handler */
+		ret = s5p_mfc_dec_ctrls_setup(ctx);
+		if (ret) {
+			mfc_err("Failed to setup mfc controls\n");
+			goto err_ctrls_setup;
+		}
+	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+		ctx->type = MFCINST_ENCODER;
+		ctx->c_ops = get_enc_codec_ops();
+		/* only for encoder */
+		INIT_LIST_HEAD(&ctx->ref_queue);
+		ctx->ref_queue_cnt = 0;
+		/* Setup ctrl handler */
+		ret = s5p_mfc_enc_ctrls_setup(ctx);
+		if (ret) {
+			mfc_err("Failed to setup mfc controls\n");
+			goto err_ctrls_setup;
+		}
+	} else {
+		ret = -ENOENT;
+		goto err_bad_node;
+	}
+	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+	ctx->inst_no = -1;
+	/* Load firmware if this is the first instance */
+	if (dev->num_inst == 1) {
+		dev->watchdog_timer.expires = jiffies +
+					msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+		add_timer(&dev->watchdog_timer);
+		ret = s5p_mfc_power_on();
+		if (ret < 0) {
+			mfc_err("power on failed\n");
+			goto err_pwr_enable;
+		}
+		s5p_mfc_clock_on();
+		ret = s5p_mfc_alloc_and_load_firmware(dev);
+		if (ret)
+			goto err_alloc_fw;
+		/* Init the FW */
+		ret = s5p_mfc_init_hw(dev);
+		if (ret)
+			goto err_init_hw;
+		s5p_mfc_clock_off();
+	}
+	/* Init videobuf2 queue for CAPTURE */
+	q = &ctx->vq_dst;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->drv_priv = &ctx->fh;
+	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+		q->io_modes = VB2_MMAP;
+		q->ops = get_dec_queue_ops();
+	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+		q->io_modes = VB2_MMAP | VB2_USERPTR;
+		q->ops = get_enc_queue_ops();
+	} else {
+		ret = -ENOENT;
+		goto err_queue_init;
+	}
+	q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+	ret = vb2_queue_init(q);
+	if (ret) {
+		mfc_err("Failed to initialize videobuf2 queue(capture)\n");
+		goto err_queue_init;
+	}
+	/* Init videobuf2 queue for OUTPUT */
+	q = &ctx->vq_src;
+	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	q->io_modes = VB2_MMAP;
+	q->drv_priv = &ctx->fh;
+	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+		q->io_modes = VB2_MMAP;
+		q->ops = get_dec_queue_ops();
+	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+		q->io_modes = VB2_MMAP | VB2_USERPTR;
+		q->ops = get_enc_queue_ops();
+	} else {
+		ret = -ENOENT;
+		goto err_queue_init;
+	}
+	q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+	ret = vb2_queue_init(q);
+	if (ret) {
+		mfc_err("Failed to initialize videobuf2 queue(output)\n");
+		goto err_queue_init;
+	}
+	init_waitqueue_head(&ctx->queue);
+	mfc_debug_leave();
+	return ret;
+	/* Deinit when failure occured */
+err_queue_init:
+err_init_hw:
+	s5p_mfc_release_firmware(dev);
+err_alloc_fw:
+	dev->ctx[ctx->num] = 0;
+	del_timer_sync(&dev->watchdog_timer);
+	s5p_mfc_clock_off();
+err_pwr_enable:
+	if (dev->num_inst == 1) {
+		if (s5p_mfc_power_off() < 0)
+			mfc_err("power off failed\n");
+		s5p_mfc_release_firmware(dev);
+	}
+err_ctrls_setup:
+	s5p_mfc_dec_ctrls_delete(ctx);
+err_bad_node:
+err_no_ctx:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+err_alloc:
+	dev->num_inst--;
+	mfc_debug_leave();
+	return ret;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+
+	mfc_debug_enter();
+	s5p_mfc_clock_on();
+	vb2_queue_release(&ctx->vq_src);
+	vb2_queue_release(&ctx->vq_dst);
+	/* Mark context as idle */
+	spin_lock_irqsave(&dev->condlock, flags);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock_irqrestore(&dev->condlock, flags);
+	/* If instance was initialised then
+	 * return instance and free reosurces */
+	if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+		mfc_debug(2, "Has to free instance\n");
+		ctx->state = MFCINST_RETURN_INST;
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_try_run(dev);
+		/* Wait until instance is returned or timeout occured */
+		if (s5p_mfc_wait_for_done_ctx
+		    (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
+			s5p_mfc_clock_off();
+			mfc_err("Err returning instance\n");
+		}
+		mfc_debug(2, "After free instance\n");
+		/* Free resources */
+		s5p_mfc_release_codec_buffers(ctx);
+		s5p_mfc_release_instance_buffer(ctx);
+		if (ctx->type == MFCINST_DECODER)
+			s5p_mfc_release_dec_desc_buffer(ctx);
+
+		ctx->inst_no = MFC_NO_INSTANCE_SET;
+	}
+	/* hardware locking scheme */
+	if (dev->curr_ctx == ctx->num)
+		clear_bit(0, &dev->hw_lock);
+	dev->num_inst--;
+	if (dev->num_inst == 0) {
+		mfc_debug(2, "Last instance - release firmware\n");
+		/* reset <-> F/W release */
+		s5p_mfc_reset(dev);
+		s5p_mfc_release_firmware(dev);
+		del_timer_sync(&dev->watchdog_timer);
+		if (s5p_mfc_power_off() < 0)
+			mfc_err("Power off failed\n");
+	}
+	mfc_debug(2, "Shutting down clock\n");
+	s5p_mfc_clock_off();
+	dev->ctx[ctx->num] = 0;
+	s5p_mfc_dec_ctrls_delete(ctx);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+	mfc_debug_leave();
+	return 0;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct vb2_queue *src_q, *dst_q;
+	struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+	unsigned int rc = 0;
+	unsigned long flags;
+
+	src_q = &ctx->vq_src;
+	dst_q = &ctx->vq_dst;
+	/*
+	 * There has to be at least one buffer queued on each queued_list, which
+	 * means either in driver already or waiting for driver to claim it
+	 * and start processing.
+	 */
+	if ((!src_q->streaming || list_empty(&src_q->queued_list))
+		&& (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+		rc = POLLERR;
+		goto end;
+	}
+	mutex_unlock(&dev->mfc_mutex);
+	poll_wait(file, &src_q->done_wq, wait);
+	poll_wait(file, &dst_q->done_wq, wait);
+	mutex_lock(&dev->mfc_mutex);
+	spin_lock_irqsave(&src_q->done_lock, flags);
+	if (!list_empty(&src_q->done_list))
+		src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+								done_entry);
+	if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+				|| src_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLOUT | POLLWRNORM;
+	spin_unlock_irqrestore(&src_q->done_lock, flags);
+	spin_lock_irqsave(&dst_q->done_lock, flags);
+	if (!list_empty(&dst_q->done_list))
+		dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+								done_entry);
+	if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+				|| dst_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&dst_q->done_lock, flags);
+end:
+	return rc;
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	int ret;
+	if (offset < DST_QUEUE_OFF_BASE) {
+		mfc_debug(2, "mmaping source\n");
+		ret = vb2_mmap(&ctx->vq_src, vma);
+	} else {		/* capture */
+		mfc_debug(2, "mmaping destination\n");
+		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+		ret = vb2_mmap(&ctx->vq_dst, vma);
+	}
+	return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+	.owner = THIS_MODULE,
+	.open = s5p_mfc_open,
+	.release = s5p_mfc_release,
+	.poll = s5p_mfc_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = s5p_mfc_mmap,
+};
+
+static int match_child(struct device *dev, void *data)
+{
+	if (!dev_name(dev))
+		return 0;
+	return !strcmp(dev_name(dev), (char *)data);
+}
+
+
+/* MFC probe function */
+static int __devinit s5p_mfc_probe(struct platform_device *pdev)
+{
+	struct s5p_mfc_dev *dev;
+	struct video_device *vfd;
+	struct resource *res;
+	int ret;
+
+	pr_debug("%s++\n", __func__);
+	dev = kzalloc(sizeof *dev, GFP_KERNEL);
+	if (!dev) {
+		dev_err(&pdev->dev, "Not enough memory for MFC device\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&dev->irqlock);
+	spin_lock_init(&dev->condlock);
+	dev->plat_dev = pdev;
+	if (!dev->plat_dev) {
+		dev_err(&pdev->dev, "No platform data specified\n");
+		ret = -ENODEV;
+		goto err_dev;
+	}
+
+	ret = s5p_mfc_init_pm(dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get mfc clock source\n");
+		goto err_clk;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get memory region resource\n");
+		ret = -ENOENT;
+		goto err_res;
+	}
+
+	dev->mfc_mem = request_mem_region(res->start, resource_size(res),
+					  pdev->name);
+	if (dev->mfc_mem == NULL) {
+		dev_err(&pdev->dev, "failed to get memory region\n");
+		ret = -ENOENT;
+		goto err_mem_reg;
+	}
+	dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+	if (dev->regs_base == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap address region\n");
+		ret = -ENOENT;
+		goto err_ioremap;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get irq resource\n");
+		ret = -ENOENT;
+		goto err_get_res;
+	}
+	dev->irq = res->start;
+	ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
+									dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+		goto err_req_irq;
+	}
+
+	dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
+					   match_child);
+	if (!dev->mem_dev_l) {
+		mfc_err("Mem child (L) device get failed\n");
+		ret = -ENODEV;
+		goto err_find_child;
+	}
+	dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
+					   match_child);
+	if (!dev->mem_dev_r) {
+		mfc_err("Mem child (R) device get failed\n");
+		ret = -ENODEV;
+		goto err_find_child;
+	}
+
+	dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
+	if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
+		ret = PTR_ERR(dev->alloc_ctx[0]);
+		goto err_mem_init_ctx_0;
+	}
+	dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
+	if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
+		ret = PTR_ERR(dev->alloc_ctx[1]);
+		goto err_mem_init_ctx_1;
+	}
+
+	mutex_init(&dev->mfc_mutex);
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		goto err_v4l2_dev_reg;
+	init_waitqueue_head(&dev->queue);
+
+	/* decoder */
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto err_dec_alloc;
+	}
+	vfd->fops	= &s5p_mfc_fops,
+	vfd->ioctl_ops	= get_dec_v4l2_ioctl_ops();
+	vfd->release	= video_device_release,
+	vfd->lock	= &dev->mfc_mutex;
+	vfd->v4l2_dev	= &dev->v4l2_dev;
+	snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
+	dev->vfd_dec	= vfd;
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto err_dec_reg;
+	}
+	v4l2_info(&dev->v4l2_dev,
+		  "decoder registered as /dev/video%d\n", vfd->num);
+	video_set_drvdata(vfd, dev);
+
+	/* encoder */
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto err_enc_alloc;
+	}
+	vfd->fops	= &s5p_mfc_fops,
+	vfd->ioctl_ops	= get_enc_v4l2_ioctl_ops();
+	vfd->release	= video_device_release,
+	vfd->lock	= &dev->mfc_mutex;
+	vfd->v4l2_dev	= &dev->v4l2_dev;
+	snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
+	dev->vfd_enc	= vfd;
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto err_enc_reg;
+	}
+	v4l2_info(&dev->v4l2_dev,
+		  "encoder registered as /dev/video%d\n", vfd->num);
+	video_set_drvdata(vfd, dev);
+	platform_set_drvdata(pdev, dev);
+
+	dev->hw_lock = 0;
+	dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME);
+	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+	atomic_set(&dev->watchdog_cnt, 0);
+	init_timer(&dev->watchdog_timer);
+	dev->watchdog_timer.data = (unsigned long)dev;
+	dev->watchdog_timer.function = s5p_mfc_watchdog;
+
+	pr_debug("%s--\n", __func__);
+	return 0;
+
+/* Deinit MFC if probe had failed */
+err_enc_reg:
+	video_device_release(dev->vfd_enc);
+err_enc_alloc:
+	video_unregister_device(dev->vfd_dec);
+err_dec_reg:
+	video_device_release(dev->vfd_dec);
+err_dec_alloc:
+	v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_dev_reg:
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+err_mem_init_ctx_1:
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+err_mem_init_ctx_0:
+err_find_child:
+	free_irq(dev->irq, dev);
+err_req_irq:
+err_get_res:
+	iounmap(dev->regs_base);
+	dev->regs_base = NULL;
+err_ioremap:
+	release_resource(dev->mfc_mem);
+	kfree(dev->mfc_mem);
+err_mem_reg:
+err_res:
+	s5p_mfc_final_pm(dev);
+err_clk:
+err_dev:
+	kfree(dev);
+	pr_debug("%s-- with error\n", __func__);
+	return ret;
+
+}
+
+/* Remove the driver */
+static int __devexit s5p_mfc_remove(struct platform_device *pdev)
+{
+	struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+
+	del_timer_sync(&dev->watchdog_timer);
+	flush_workqueue(dev->watchdog_workqueue);
+	destroy_workqueue(dev->watchdog_workqueue);
+
+	video_unregister_device(dev->vfd_enc);
+	video_unregister_device(dev->vfd_dec);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+
+	free_irq(dev->irq, dev);
+	iounmap(dev->regs_base);
+	if (dev->mfc_mem) {
+		release_resource(dev->mfc_mem);
+		kfree(dev->mfc_mem);
+		dev->mfc_mem = NULL;
+	}
+	s5p_mfc_final_pm(dev);
+	kfree(dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int s5p_mfc_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+	int ret;
+
+	if (m_dev->num_inst == 0)
+		return 0;
+	return s5p_mfc_sleep(m_dev);
+	if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
+		mfc_err("Error: going to suspend for a second time\n");
+		return -EIO;
+	}
+
+	/* Check if we're processing then wait if it necessary. */
+	while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
+		/* Try and lock the HW */
+		/* Wait on the interrupt waitqueue */
+		ret = wait_event_interruptible_timeout(m_dev->queue,
+			m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
+			msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+		if (ret == 0) {
+			mfc_err("Waiting for hardware to finish timed out\n");
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+	if (m_dev->num_inst == 0)
+		return 0;
+	return s5p_mfc_wakeup(m_dev);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5p_mfc_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+	atomic_set(&m_dev->pm.power, 0);
+	return 0;
+}
+
+static int s5p_mfc_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+	int pre_power;
+
+	if (!m_dev->alloc_ctx)
+		return 0;
+	pre_power = atomic_read(&m_dev->pm.power);
+	atomic_set(&m_dev->pm.power, 1);
+	return 0;
+}
+#endif
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
+	SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
+			   NULL)
+};
+
+static struct platform_driver s5p_mfc_pdrv = {
+	.probe	= s5p_mfc_probe,
+	.remove	= __devexit_p(s5p_mfc_remove),
+	.driver	= {
+		.name	= S5P_MFC_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &s5p_mfc_pm_ops
+	},
+};
+
+static char banner[] __initdata =
+			"S5P MFC V4L2 Driver, (C) 2011 Samsung Electronics\n";
+
+static int __init s5p_mfc_init(void)
+{
+	int ret;
+
+	pr_info("%s", banner);
+	ret = platform_driver_register(&s5p_mfc_pdrv);
+	if (ret)
+		pr_err("Platform device registration failed.\n");
+	return ret;
+}
+
+static void __devexit s5p_mfc_exit(void)
+{
+	platform_driver_unregister(&s5p_mfc_pdrv);
+}
+
+module_init(s5p_mfc_init);
+module_exit(s5p_mfc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
new file mode 100644
index 0000000..f0665ed
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
@@ -0,0 +1,120 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+/* This function is used to send a command to the MFC */
+static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd,
+						struct s5p_mfc_cmd_args *args)
+{
+	int cur_cmd;
+	unsigned long timeout;
+
+	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+	/* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
+	do {
+		if (time_after(jiffies, timeout)) {
+			mfc_err("Timeout while waiting for hardware\n");
+			return -EIO;
+		}
+		cur_cmd = mfc_read(dev, S5P_FIMV_HOST2RISC_CMD);
+	} while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
+	mfc_write(dev, args->arg[0], S5P_FIMV_HOST2RISC_ARG1);
+	mfc_write(dev, args->arg[1], S5P_FIMV_HOST2RISC_ARG2);
+	mfc_write(dev, args->arg[2], S5P_FIMV_HOST2RISC_ARG3);
+	mfc_write(dev, args->arg[3], S5P_FIMV_HOST2RISC_ARG4);
+	/* Issue the command */
+	mfc_write(dev, cmd, S5P_FIMV_HOST2RISC_CMD);
+	return 0;
+}
+
+/* Initialize the MFC */
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev)
+{
+	struct s5p_mfc_cmd_args h2r_args;
+
+	memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+	h2r_args.arg[0] = dev->fw_size;
+	return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT, &h2r_args);
+}
+
+/* Suspend the MFC hardware */
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev)
+{
+	struct s5p_mfc_cmd_args h2r_args;
+
+	memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+	return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP, &h2r_args);
+}
+
+/* Wake up the MFC hardware */
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev)
+{
+	struct s5p_mfc_cmd_args h2r_args;
+
+	memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+	return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP, &h2r_args);
+}
+
+
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_cmd_args h2r_args;
+	int ret;
+
+	/* Preparing decoding - getting instance number */
+	mfc_debug(2, "Getting instance number (codec: %d)\n", ctx->codec_mode);
+	dev->curr_ctx = ctx->num;
+	memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+	h2r_args.arg[0] = ctx->codec_mode;
+	h2r_args.arg[1] = 0; /* no crc & no pixelcache */
+	h2r_args.arg[2] = ctx->ctx_ofs;
+	h2r_args.arg[3] = ctx->ctx_size;
+	ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE,
+								&h2r_args);
+	if (ret) {
+		mfc_err("Failed to create a new instance\n");
+		ctx->state = MFCINST_ERROR;
+	}
+	return ret;
+}
+
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_cmd_args h2r_args;
+	int ret;
+
+	if (ctx->state == MFCINST_FREE) {
+		mfc_err("Instance already returned\n");
+		ctx->state = MFCINST_ERROR;
+		return -EINVAL;
+	}
+	/* Closing decoding instance  */
+	mfc_debug(2, "Returning instance number %d\n", ctx->inst_no);
+	dev->curr_ctx = ctx->num;
+	memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+	h2r_args.arg[0] = ctx->inst_no;
+	ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE,
+								&h2r_args);
+	if (ret) {
+		mfc_err("Failed to return an instance\n");
+		ctx->state = MFCINST_ERROR;
+		return -EINVAL;
+	}
+	return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
new file mode 100644
index 0000000..5ceebfe
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
@@ -0,0 +1,30 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_CMD_H_
+#define S5P_MFC_CMD_H_
+
+#include "s5p_mfc_common.h"
+
+#define MAX_H2R_ARG	4
+
+struct s5p_mfc_cmd_args {
+	unsigned int	arg[MAX_H2R_ARG];
+};
+
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_CMD_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
new file mode 100644
index 0000000..91146fa
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
@@ -0,0 +1,572 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains definitions of enums and structs used by the codec
+ * driver.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.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 S5P_MFC_COMMON_H_
+#define S5P_MFC_COMMON_H_
+
+#include "regs-mfc.h"
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+
+/* Definitions related to MFC memory */
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+*  while mmaping */
+#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
+
+/* Offset used by the hardware to store addresses */
+#define MFC_OFFSET_SHIFT	11
+
+#define FIRMWARE_ALIGN		0x20000		/* 128KB */
+#define MFC_H264_CTX_BUF_SIZE	0x96000		/* 600KB per H264 instance */
+#define MFC_CTX_BUF_SIZE	0x2800		/* 10KB per instance */
+#define DESC_BUF_SIZE		0x20000		/* 128KB for DESC buffer */
+#define SHARED_BUF_SIZE		0x2000		/* 8KB for shared buffer */
+
+#define DEF_CPB_SIZE		0x40000		/* 512KB */
+
+#define MFC_BANK1_ALLOC_CTX	0
+#define MFC_BANK2_ALLOC_CTX	1
+
+#define MFC_BANK1_ALIGN_ORDER	13
+#define MFC_BANK2_ALIGN_ORDER	13
+#define MFC_BASE_ALIGN_ORDER	17
+
+#include <media/videobuf2-dma-contig.h>
+
+static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
+{
+	/* Same functionality as the vb2_dma_contig_plane_paddr */
+	dma_addr_t *paddr = vb2_dma_contig_memops.cookie(b);
+
+	return *paddr;
+}
+
+/* MFC definitions */
+#define MFC_MAX_EXTRA_DPB       5
+#define MFC_MAX_BUFFERS		32
+#define MFC_NUM_CONTEXTS	4
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT		2000
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT		500
+/* Watchdog interval */
+#define MFC_WATCHDOG_INTERVAL   1000
+/* After how many executions watchdog should assume lock up */
+#define MFC_WATCHDOG_CNT        10
+#define MFC_NO_INSTANCE_SET	-1
+#define MFC_ENC_CAP_PLANE_COUNT	1
+#define MFC_ENC_OUT_PLANE_COUNT	2
+#define STUFF_BYTE		4
+#define MFC_MAX_CTRLS		64
+
+#define mfc_read(dev, offset)		readl(dev->regs_base + (offset))
+#define mfc_write(dev, data, offset)	writel((data), dev->regs_base + \
+								(offset))
+
+/**
+ * enum s5p_mfc_fmt_type - type of the pixelformat
+ */
+enum s5p_mfc_fmt_type {
+	MFC_FMT_DEC,
+	MFC_FMT_ENC,
+	MFC_FMT_RAW,
+};
+
+/**
+ * enum s5p_mfc_node_type - The type of an MFC device node.
+ */
+enum s5p_mfc_node_type {
+	MFCNODE_INVALID = -1,
+	MFCNODE_DECODER = 0,
+	MFCNODE_ENCODER = 1,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC instance.
+ */
+enum s5p_mfc_inst_type {
+	MFCINST_INVALID,
+	MFCINST_DECODER,
+	MFCINST_ENCODER,
+};
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+	MFCINST_FREE = 0,
+	MFCINST_INIT = 100,
+	MFCINST_GOT_INST,
+	MFCINST_HEAD_PARSED,
+	MFCINST_BUFS_SET,
+	MFCINST_RUNNING,
+	MFCINST_FINISHING,
+	MFCINST_FINISHED,
+	MFCINST_RETURN_INST,
+	MFCINST_ERROR,
+	MFCINST_ABORT,
+	MFCINST_RES_CHANGE_INIT,
+	MFCINST_RES_CHANGE_FLUSH,
+	MFCINST_RES_CHANGE_END,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+	QUEUE_FREE,
+	QUEUE_BUFS_REQUESTED,
+	QUEUE_BUFS_QUERIED,
+	QUEUE_BUFS_MMAPED,
+};
+
+/**
+ * enum s5p_mfc_decode_arg - type of frame decoding
+ */
+enum s5p_mfc_decode_arg {
+	MFC_DEC_FRAME,
+	MFC_DEC_LAST_FRAME,
+	MFC_DEC_RES_CHANGE,
+};
+
+struct s5p_mfc_ctx;
+
+/**
+ * struct s5p_mfc_buf - MFC buffer
+ */
+struct s5p_mfc_buf {
+	struct list_head list;
+	struct vb2_buffer *b;
+	union {
+		struct {
+			size_t luma;
+			size_t chroma;
+		} raw;
+		size_t stream;
+	} cookie;
+	int used;
+};
+
+/**
+ * struct s5p_mfc_pm - power management data structure
+ */
+struct s5p_mfc_pm {
+	struct clk	*clock;
+	struct clk	*clock_gate;
+	atomic_t	power;
+	struct device	*device;
+};
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ *
+ * @v4l2_dev:		v4l2_device
+ * @vfd_dec:		video device for decoding
+ * @vfd_enc:		video device for encoding
+ * @plat_dev:		platform device
+ * @mem_dev_l:		child device of the left memory bank (0)
+ * @mem_dev_r:		child device of the right memory bank (1)
+ * @regs_base:		base address of the MFC hw registers
+ * @irq:		irq resource
+ * @mfc_mem:		MFC registers memory resource
+ * @dec_ctrl_handler:	control framework handler for decoding
+ * @enc_ctrl_handler:	control framework handler for encoding
+ * @pm:			power management control
+ * @num_inst:		couter of active MFC instances
+ * @irqlock:		lock for operations on videobuf2 queues
+ * @condlock:		lock for changing/checking if a context is ready to be
+ *			processed
+ * @mfc_mutex:		lock for video_device
+ * @int_cond:		variable used by the waitqueue
+ * @int_type:		type of last interrupt
+ * @int_err:		error number for last interrupt
+ * @queue:		waitqueue for waiting for completion of device commands
+ * @fw_size:		size of firmware
+ * @bank1:		address of the beggining of bank 1 memory
+ * @bank2:		address of the beggining of bank 2 memory
+ * @hw_lock:		used for hardware locking
+ * @ctx:		array of driver contexts
+ * @curr_ctx:		number of the currently running context
+ * @ctx_work_bits:	used to mark which contexts are waiting for hardware
+ * @watchdog_cnt:	counter for the watchdog
+ * @watchdog_workqueue:	workqueue for the watchdog
+ * @watchdog_work:	worker for the watchdog
+ * @alloc_ctx:		videobuf2 allocator contexts for two memory banks
+ * @enter_suspend:	flag set when entering suspend
+ *
+ */
+struct s5p_mfc_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd_dec;
+	struct video_device	*vfd_enc;
+	struct platform_device	*plat_dev;
+	struct device		*mem_dev_l;
+	struct device		*mem_dev_r;
+	void __iomem		*regs_base;
+	int			irq;
+	struct resource		*mfc_mem;
+	struct v4l2_ctrl_handler dec_ctrl_handler;
+	struct v4l2_ctrl_handler enc_ctrl_handler;
+	struct s5p_mfc_pm	pm;
+	int num_inst;
+	spinlock_t irqlock;	/* lock when operating on videobuf2 queues */
+	spinlock_t condlock;	/* lock when changing/checking if a context is
+					ready to be processed */
+	struct mutex mfc_mutex; /* video_device lock */
+	int int_cond;
+	int int_type;
+	unsigned int int_err;
+	wait_queue_head_t queue;
+	size_t fw_size;
+	size_t bank1;
+	size_t bank2;
+	unsigned long hw_lock;
+	struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+	int curr_ctx;
+	unsigned long ctx_work_bits;
+	atomic_t watchdog_cnt;
+	struct timer_list watchdog_timer;
+	struct workqueue_struct *watchdog_workqueue;
+	struct work_struct watchdog_work;
+	void *alloc_ctx[2];
+	unsigned long enter_suspend;
+};
+
+/**
+ * struct s5p_mfc_h264_enc_params - encoding parameters for h264
+ */
+struct s5p_mfc_h264_enc_params {
+	enum v4l2_mpeg_video_h264_profile profile;
+	enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
+	s8 loop_filter_alpha;
+	s8 loop_filter_beta;
+	enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+	u8 max_ref_pic;
+	u8 num_ref_pic_4p;
+	int _8x8_transform;
+	int rc_mb;
+	int rc_mb_dark;
+	int rc_mb_smooth;
+	int rc_mb_static;
+	int rc_mb_activity;
+	int vui_sar;
+	u8 vui_sar_idc;
+	u16 vui_ext_sar_width;
+	u16 vui_ext_sar_height;
+	int open_gop;
+	u16 open_gop_size;
+	u8 rc_frame_qp;
+	u8 rc_min_qp;
+	u8 rc_max_qp;
+	u8 rc_p_frame_qp;
+	u8 rc_b_frame_qp;
+	enum v4l2_mpeg_video_h264_level level_v4l2;
+	int level;
+	u16 cpb_size;
+};
+
+/**
+ * struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
+ */
+struct s5p_mfc_mpeg4_enc_params {
+	/* MPEG4 Only */
+	enum v4l2_mpeg_video_mpeg4_profile profile;
+	int quarter_pixel;
+	/* Common for MPEG4, H263 */
+	u16 vop_time_res;
+	u16 vop_frm_delta;
+	u8 rc_frame_qp;
+	u8 rc_min_qp;
+	u8 rc_max_qp;
+	u8 rc_p_frame_qp;
+	u8 rc_b_frame_qp;
+	enum v4l2_mpeg_video_mpeg4_level level_v4l2;
+	int level;
+};
+
+/**
+ * struct s5p_mfc_enc_params - general encoding parameters
+ */
+struct s5p_mfc_enc_params {
+	u16 width;
+	u16 height;
+
+	u16 gop_size;
+	enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+	u16 slice_mb;
+	u32 slice_bit;
+	u16 intra_refresh_mb;
+	int pad;
+	u8 pad_luma;
+	u8 pad_cb;
+	u8 pad_cr;
+	int rc_frame;
+	u32 rc_bitrate;
+	u16 rc_reaction_coeff;
+	u16 vbv_size;
+
+	enum v4l2_mpeg_video_header_mode seq_hdr_mode;
+	enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
+	int fixed_target_bit;
+
+	u8 num_b_frame;
+	u32 rc_framerate_num;
+	u32 rc_framerate_denom;
+	int interlace;
+
+	union {
+		struct s5p_mfc_h264_enc_params h264;
+		struct s5p_mfc_mpeg4_enc_params mpeg4;
+	} codec;
+
+};
+
+/**
+ * struct s5p_mfc_codec_ops - codec ops, used by encoding
+ */
+struct s5p_mfc_codec_ops {
+	/* initialization routines */
+	int (*pre_seq_start) (struct s5p_mfc_ctx *ctx);
+	int (*post_seq_start) (struct s5p_mfc_ctx *ctx);
+	/* execution routines */
+	int (*pre_frame_start) (struct s5p_mfc_ctx *ctx);
+	int (*post_frame_start) (struct s5p_mfc_ctx *ctx);
+};
+
+#define call_cop(c, op, args...)				\
+	(((c)->c_ops->op) ?					\
+		((c)->c_ops->op(args)) : 0)
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ *
+ * @dev:		pointer to the s5p_mfc_dev of the device
+ * @fh:			struct v4l2_fh
+ * @num:		number of the context that this structure describes
+ * @int_cond:		variable used by the waitqueue
+ * @int_type:		type of the last interrupt
+ * @int_err:		error number received from MFC hw in the interrupt
+ * @queue:		waitqueue that can be used to wait for this context to
+ *			finish
+ * @src_fmt:		source pixelformat information
+ * @dst_fmt:		destination pixelformat information
+ * @vq_src:		vb2 queue for source buffers
+ * @vq_dst:		vb2 queue for destination buffers
+ * @src_queue:		driver internal queue for source buffers
+ * @dst_queue:		driver internal queue for destination buffers
+ * @src_queue_cnt:	number of buffers queued on the source internal queue
+ * @dst_queue_cnt:	number of buffers queued on the dest internal queue
+ * @type:		type of the instance - decoder or encoder
+ * @state:		state of the context
+ * @inst_no:		number of hw instance associated with the context
+ * @img_width:		width of the image that is decoded or encoded
+ * @img_height:		height of the image that is decoded or encoded
+ * @buf_width:		width of the buffer for processed image
+ * @buf_height:		height of the buffer for processed image
+ * @luma_size:		size of a luma plane
+ * @chroma_size:	size of a chroma plane
+ * @mv_size:		size of a motion vectors buffer
+ * @consumed_stream:	number of bytes that have been used so far from the
+ *			decoding buffer
+ * @dpb_flush_flag:	flag used to indicate that a DPB buffers are being
+ *			flushed
+ * @bank1_buf:		handle to memory allocated for temporary buffers from
+ *			memory bank 1
+ * @bank1_phys:		address of the temporary buffers from memory bank 1
+ * @bank1_size:		size of the memory allocated for temporary buffers from
+ *			memory bank 1
+ * @bank2_buf:		handle to memory allocated for temporary buffers from
+ *			memory bank 2
+ * @bank2_phys:		address of the temporary buffers from memory bank 2
+ * @bank2_size:		size of the memory allocated for temporary buffers from
+ *			memory bank 2
+ * @capture_state:	state of the capture buffers queue
+ * @output_state:	state of the output buffers queue
+ * @src_bufs:		information on allocated source buffers
+ * @dst_bufs:		information on allocated destination buffers
+ * @sequence:		counter for the sequence number for v4l2
+ * @dec_dst_flag:	flags for buffers queued in the hardware
+ * @dec_src_buf_size:	size of the buffer for source buffers in decoding
+ * @codec_mode:		number of codec mode used by MFC hw
+ * @slice_interface:	slice interface flag
+ * @loop_filter_mpeg4:	loop filter for MPEG4 flag
+ * @display_delay:	value of the display delay for H264
+ * @display_delay_enable:	display delay for H264 enable flag
+ * @after_packed_pb:	flag used to track buffer when stream is in
+ *			Packed PB format
+ * @dpb_count:		count of the DPB buffers required by MFC hw
+ * @total_dpb_count:	count of DPB buffers with additional buffers
+ *			requested by the application
+ * @ctx_buf:		handle to the memory associated with this context
+ * @ctx_phys:		address of the memory associated with this context
+ * @ctx_size:		size of the memory associated with this context
+ * @desc_buf:		description buffer for decoding handle
+ * @desc_phys:		description buffer for decoding address
+ * @shm_alloc:		handle for the shared memory buffer
+ * @shm:		virtual address for the shared memory buffer
+ * @shm_ofs:		address offset for shared memory
+ * @enc_params:		encoding parameters for MFC
+ * @enc_dst_buf_size:	size of the buffers for encoder output
+ * @frame_type:		used to force the type of the next encoded frame
+ * @ref_queue:		list of the reference buffers for encoding
+ * @ref_queue_cnt:	number of the buffers in the reference list
+ * @c_ops:		ops for encoding
+ * @ctrls:		array of controls, used when adding controls to the
+ *			v4l2 control framework
+ * @ctrl_handler:	handler for v4l2 framework
+ */
+struct s5p_mfc_ctx {
+	struct s5p_mfc_dev *dev;
+	struct v4l2_fh fh;
+
+	int num;
+
+	int int_cond;
+	int int_type;
+	unsigned int int_err;
+	wait_queue_head_t queue;
+
+	struct s5p_mfc_fmt *src_fmt;
+	struct s5p_mfc_fmt *dst_fmt;
+
+	struct vb2_queue vq_src;
+	struct vb2_queue vq_dst;
+
+	struct list_head src_queue;
+	struct list_head dst_queue;
+
+	unsigned int src_queue_cnt;
+	unsigned int dst_queue_cnt;
+
+	enum s5p_mfc_inst_type type;
+	enum s5p_mfc_inst_state state;
+	int inst_no;
+
+	/* Image parameters */
+	int img_width;
+	int img_height;
+	int buf_width;
+	int buf_height;
+
+	int luma_size;
+	int chroma_size;
+	int mv_size;
+
+	unsigned long consumed_stream;
+
+	unsigned int dpb_flush_flag;
+
+	/* Buffers */
+	void *bank1_buf;
+	size_t bank1_phys;
+	size_t bank1_size;
+
+	void *bank2_buf;
+	size_t bank2_phys;
+	size_t bank2_size;
+
+	enum s5p_mfc_queue_state capture_state;
+	enum s5p_mfc_queue_state output_state;
+
+	struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
+	int src_bufs_cnt;
+	struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
+	int dst_bufs_cnt;
+
+	unsigned int sequence;
+	unsigned long dec_dst_flag;
+	size_t dec_src_buf_size;
+
+	/* Control values */
+	int codec_mode;
+	int slice_interface;
+	int loop_filter_mpeg4;
+	int display_delay;
+	int display_delay_enable;
+	int after_packed_pb;
+
+	int dpb_count;
+	int total_dpb_count;
+
+	/* Buffers */
+	void *ctx_buf;
+	size_t ctx_phys;
+	size_t ctx_ofs;
+	size_t ctx_size;
+
+	void *desc_buf;
+	size_t desc_phys;
+
+
+	void *shm_alloc;
+	void *shm;
+	size_t shm_ofs;
+
+	struct s5p_mfc_enc_params enc_params;
+
+	size_t enc_dst_buf_size;
+
+	enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
+
+	struct list_head ref_queue;
+	unsigned int ref_queue_cnt;
+
+	struct s5p_mfc_codec_ops *c_ops;
+
+	struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
+	struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct s5p_mfc_fmt -	structure used to store information about pixelformats
+ *			used by the MFC
+ */
+struct s5p_mfc_fmt {
+	char *name;
+	u32 fourcc;
+	u32 codec_mode;
+	enum s5p_mfc_fmt_type type;
+	u32 num_planes;
+};
+
+/**
+ * struct mfc_control -	structure used to store information about MFC controls
+ *			it is used to initialize the control framework.
+ */
+struct mfc_control {
+	__u32			id;
+	enum v4l2_ctrl_type	type;
+	__u8			name[32];  /* Whatever */
+	__s32			minimum;   /* Note signedness */
+	__s32			maximum;
+	__s32			step;
+	__u32			menu_skip_mask;
+	__s32			default_value;
+	__u32			flags;
+	__u32			reserved[2];
+	__u8			is_volatile;
+};
+
+
+#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
+#define ctrl_to_ctx(__ctrl) \
+	container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
+
+#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
new file mode 100644
index 0000000..5f4da80
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -0,0 +1,343 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_pm.h"
+
+static void *s5p_mfc_bitproc_buf;
+static size_t s5p_mfc_bitproc_phys;
+static unsigned char *s5p_mfc_bitproc_virt;
+
+/* Allocate and load firmware */
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
+{
+	struct firmware *fw_blob;
+	size_t bank2_base_phys;
+	void *b_base;
+	int err;
+
+	/* Firmare has to be present as a separate file or compiled
+	 * into kernel. */
+	mfc_debug_enter();
+	err = request_firmware((const struct firmware **)&fw_blob,
+				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+	if (err != 0) {
+		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+		return -EINVAL;
+	}
+	dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
+	if (s5p_mfc_bitproc_buf) {
+		mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
+		release_firmware(fw_blob);
+		return -ENOMEM;
+	}
+	s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
+	if (IS_ERR(s5p_mfc_bitproc_buf)) {
+		s5p_mfc_bitproc_buf = 0;
+		mfc_err("Allocating bitprocessor buffer failed\n");
+		release_firmware(fw_blob);
+		return -ENOMEM;
+	}
+	s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
+	if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+		mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
+		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+		s5p_mfc_bitproc_phys = 0;
+		s5p_mfc_bitproc_buf = 0;
+		release_firmware(fw_blob);
+		return -EIO;
+	}
+	s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
+	if (!s5p_mfc_bitproc_virt) {
+		mfc_err("Bitprocessor memory remap failed\n");
+		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+		s5p_mfc_bitproc_phys = 0;
+		s5p_mfc_bitproc_buf = 0;
+		release_firmware(fw_blob);
+		return -EIO;
+	}
+	dev->bank1 = s5p_mfc_bitproc_phys;
+	b_base = vb2_dma_contig_memops.alloc(
+		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BANK2_ALIGN_ORDER);
+	if (IS_ERR(b_base)) {
+		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+		s5p_mfc_bitproc_phys = 0;
+		s5p_mfc_bitproc_buf = 0;
+		mfc_err("Allocating bank2 base failed\n");
+	release_firmware(fw_blob);
+		return -ENOMEM;
+	}
+	bank2_base_phys = s5p_mfc_mem_cookie(
+		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
+	vb2_dma_contig_memops.put(b_base);
+	if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+		mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
+		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+		s5p_mfc_bitproc_phys = 0;
+		s5p_mfc_bitproc_buf = 0;
+		release_firmware(fw_blob);
+		return -EIO;
+	}
+	dev->bank2 = bank2_base_phys;
+	memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+	wmb();
+	release_firmware(fw_blob);
+	mfc_debug_leave();
+	return 0;
+}
+
+/* Reload firmware to MFC */
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
+{
+	struct firmware *fw_blob;
+	int err;
+
+	/* Firmare has to be present as a separate file or compiled
+	 * into kernel. */
+	mfc_debug_enter();
+	err = request_firmware((const struct firmware **)&fw_blob,
+				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+	if (err != 0) {
+		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+		return -EINVAL;
+	}
+	if (fw_blob->size > dev->fw_size) {
+		mfc_err("MFC firmware is too big to be loaded\n");
+		release_firmware(fw_blob);
+		return -ENOMEM;
+	}
+	if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) {
+		mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
+		release_firmware(fw_blob);
+		return -EINVAL;
+	}
+	memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+	wmb();
+	release_firmware(fw_blob);
+	mfc_debug_leave();
+	return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
+{
+	/* Before calling this function one has to make sure
+	 * that MFC is no longer processing */
+	if (!s5p_mfc_bitproc_buf)
+		return -EINVAL;
+	vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+	s5p_mfc_bitproc_virt =  0;
+	s5p_mfc_bitproc_phys = 0;
+	s5p_mfc_bitproc_buf = 0;
+	return 0;
+}
+
+/* Reset the device */
+int s5p_mfc_reset(struct s5p_mfc_dev *dev)
+{
+	unsigned int mc_status;
+	unsigned long timeout;
+
+	mfc_debug_enter();
+	/* Stop procedure */
+	/*  reset RISC */
+	mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
+	/*  All reset except for MC */
+	mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
+	mdelay(10);
+
+	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+	/* Check MC status */
+	do {
+		if (time_after(jiffies, timeout)) {
+			mfc_err("Timeout while resetting MFC\n");
+			return -EIO;
+		}
+
+		mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
+
+	} while (mc_status & 0x3);
+
+	mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
+	mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
+	mfc_debug_leave();
+	return 0;
+}
+
+static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
+{
+	mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
+	mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
+	mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
+}
+
+static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
+{
+	mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
+	mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
+	mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+	mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
+}
+
+/* Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+	unsigned int ver;
+	int ret;
+
+	mfc_debug_enter();
+	if (!s5p_mfc_bitproc_buf)
+		return -EINVAL;
+
+	/* 0. MFC reset */
+	mfc_debug(2, "MFC reset..\n");
+	s5p_mfc_clock_on();
+	ret = s5p_mfc_reset(dev);
+	if (ret) {
+		mfc_err("Failed to reset MFC - timeout\n");
+		return ret;
+	}
+	mfc_debug(2, "Done MFC reset..\n");
+	/* 1. Set DRAM base Addr */
+	s5p_mfc_init_memctrl(dev);
+	/* 2. Initialize registers of channel I/F */
+	s5p_mfc_clear_cmds(dev);
+	/* 3. Release reset signal to the RISC */
+	s5p_mfc_clean_dev_int_flags(dev);
+	mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+	mfc_debug(2, "Will now wait for completion of firmware transfer\n");
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+		mfc_err("Failed to load firmware\n");
+		s5p_mfc_reset(dev);
+		s5p_mfc_clock_off();
+		return -EIO;
+	}
+	s5p_mfc_clean_dev_int_flags(dev);
+	/* 4. Initialize firmware */
+	ret = s5p_mfc_sys_init_cmd(dev);
+	if (ret) {
+		mfc_err("Failed to send command to MFC - timeout\n");
+		s5p_mfc_reset(dev);
+		s5p_mfc_clock_off();
+		return ret;
+	}
+	mfc_debug(2, "Ok, now will write a command to init the system\n");
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+		mfc_err("Failed to load firmware\n");
+		s5p_mfc_reset(dev);
+		s5p_mfc_clock_off();
+		return -EIO;
+	}
+	dev->int_cond = 0;
+	if (dev->int_err != 0 || dev->int_type !=
+					S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+		/* Failure. */
+		mfc_err("Failed to init firmware - error: %d int: %d\n",
+						dev->int_err, dev->int_type);
+		s5p_mfc_reset(dev);
+		s5p_mfc_clock_off();
+		return -EIO;
+	}
+	ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
+	mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
+		(ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
+	s5p_mfc_clock_off();
+	mfc_debug_leave();
+	return 0;
+}
+
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
+{
+	int ret;
+
+	mfc_debug_enter();
+	s5p_mfc_clock_on();
+	s5p_mfc_clean_dev_int_flags(dev);
+	ret = s5p_mfc_sleep_cmd(dev);
+	if (ret) {
+		mfc_err("Failed to send command to MFC - timeout\n");
+		return ret;
+	}
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
+		mfc_err("Failed to sleep\n");
+		return -EIO;
+	}
+	s5p_mfc_clock_off();
+	dev->int_cond = 0;
+	if (dev->int_err != 0 || dev->int_type !=
+						S5P_FIMV_R2H_CMD_SLEEP_RET) {
+		/* Failure. */
+		mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
+								dev->int_type);
+		return -EIO;
+	}
+	mfc_debug_leave();
+	return ret;
+}
+
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
+{
+	int ret;
+
+	mfc_debug_enter();
+	/* 0. MFC reset */
+	mfc_debug(2, "MFC reset..\n");
+	s5p_mfc_clock_on();
+	ret = s5p_mfc_reset(dev);
+	if (ret) {
+		mfc_err("Failed to reset MFC - timeout\n");
+		return ret;
+	}
+	mfc_debug(2, "Done MFC reset..\n");
+	/* 1. Set DRAM base Addr */
+	s5p_mfc_init_memctrl(dev);
+	/* 2. Initialize registers of channel I/F */
+	s5p_mfc_clear_cmds(dev);
+	s5p_mfc_clean_dev_int_flags(dev);
+	/* 3. Initialize firmware */
+	ret = s5p_mfc_wakeup_cmd(dev);
+	if (ret) {
+		mfc_err("Failed to send command to MFC - timeout\n");
+		return ret;
+	}
+	/* 4. Release reset signal to the RISC */
+	mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+	mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
+		mfc_err("Failed to load firmware\n");
+		return -EIO;
+	}
+	s5p_mfc_clock_off();
+	dev->int_cond = 0;
+	if (dev->int_err != 0 || dev->int_type !=
+						S5P_FIMV_R2H_CMD_WAKEUP_RET) {
+		/* Failure. */
+		mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
+								dev->int_type);
+		return -EIO;
+	}
+	mfc_debug_leave();
+	return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
new file mode 100644
index 0000000..61dc23b
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_CTRL_H
+#define S5P_MFC_CTRL_H
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_reset(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_debug.h b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
new file mode 100644
index 0000000..ecb8616
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_debug.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains debug macros
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_DEBUG_H_
+#define S5P_MFC_DEBUG_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+extern int debug;
+
+#define mfc_debug(level, fmt, args...)				\
+	do {							\
+		if (debug >= level)				\
+			printk(KERN_DEBUG "%s:%d: " fmt,	\
+				__func__, __LINE__, ##args);	\
+	} while (0)
+#else
+#define mfc_debug(level, fmt, args...)
+#endif
+
+#define mfc_debug_enter() mfc_debug(5, "enter")
+#define mfc_debug_leave() mfc_debug(5, "leave")
+
+#define mfc_err(fmt, args...)				\
+	do {						\
+		printk(KERN_ERR "%s:%d: " fmt,		\
+		       __func__, __LINE__, ##args);	\
+	} while (0)
+
+#define mfc_info(fmt, args...)				\
+	do {						\
+		printk(KERN_INFO "%s:%d: " fmt,		\
+		       __func__, __LINE__, ##args);	\
+	} while (0)
+
+#endif /* S5P_MFC_DEBUG_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
new file mode 100644
index 0000000..b2c5052
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -0,0 +1,1036 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ * Kamil Debski, <k.debski@samsung.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/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+static struct s5p_mfc_fmt formats[] = {
+	{
+		.name		= "4:2:0 2 Planes 64x32 Tiles",
+		.fourcc		= V4L2_PIX_FMT_NV12MT,
+		.codec_mode	= S5P_FIMV_CODEC_NONE,
+		.type		= MFC_FMT_RAW,
+		.num_planes	= 2,
+	 },
+	{
+		.name = "4:2:0 2 Planes",
+		.fourcc = V4L2_PIX_FMT_NV12M,
+		.codec_mode = S5P_FIMV_CODEC_NONE,
+		.type = MFC_FMT_RAW,
+		.num_planes = 2,
+	},
+	{
+		.name = "H264 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.codec_mode = S5P_FIMV_CODEC_H264_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "H263 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_H263,
+		.codec_mode = S5P_FIMV_CODEC_H263_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "MPEG1 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_MPEG1,
+		.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "MPEG2 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_MPEG2,
+		.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "MPEG4 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_MPEG4,
+		.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "XviD Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_XVID,
+		.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "VC1 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+		.codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+	{
+		.name = "VC1 RCV Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+		.codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
+		.type = MFC_FMT_DEC,
+		.num_planes = 1,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+		    formats[i].type == t)
+			return &formats[i];
+	}
+	return NULL;
+}
+
+static struct mfc_control controls[] = {
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H264 Display Delay",
+		.minimum = 0,
+		.maximum = 16383,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "H264 Display Delay Enable",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Mpeg4 Loop Filter Enable",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Slice Interface Enable",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Minimum number of cap bufs",
+		.minimum = 1,
+		.maximum = 32,
+		.step = 1,
+		.default_value = 1,
+		.is_volatile = 1,
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+/* Check whether a context should be run on hardware */
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+	/* Context is to parse header */
+	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_GOT_INST)
+		return 1;
+	/* Context is to decode a frame */
+	if (ctx->src_queue_cnt >= 1 &&
+	    ctx->state == MFCINST_RUNNING &&
+	    ctx->dst_queue_cnt >= ctx->dpb_count)
+		return 1;
+	/* Context is to return last frame */
+	if (ctx->state == MFCINST_FINISHING &&
+	    ctx->dst_queue_cnt >= ctx->dpb_count)
+		return 1;
+	/* Context is to set buffers */
+	if (ctx->src_queue_cnt >= 1 &&
+	    ctx->state == MFCINST_HEAD_PARSED &&
+	    ctx->capture_state == QUEUE_BUFS_MMAPED)
+		return 1;
+	/* Resolution change */
+	if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
+		ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
+		ctx->dst_queue_cnt >= ctx->dpb_count)
+		return 1;
+	if (ctx->state == MFCINST_RES_CHANGE_END &&
+		ctx->src_queue_cnt >= 1)
+		return 1;
+	mfc_debug(2, "ctx is not ready\n");
+	return 0;
+}
+
+static struct s5p_mfc_codec_ops decoder_codec_ops = {
+	.pre_seq_start		= NULL,
+	.post_seq_start		= NULL,
+	.pre_frame_start	= NULL,
+	.post_frame_start	= NULL,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+
+	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->version = KERNEL_VERSION(1, 0, 0);
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+						    | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+/* Enumerate format */
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+	struct s5p_mfc_fmt *fmt;
+	int i, j = 0;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (mplane && formats[i].num_planes == 1)
+			continue;
+		else if (!mplane && formats[i].num_planes > 1)
+			continue;
+		if (out && formats[i].type != MFC_FMT_DEC)
+			continue;
+		else if (!out && formats[i].type != MFC_FMT_RAW)
+			continue;
+
+		if (j == f->index)
+			break;
+		++j;
+	}
+	if (i == ARRAY_SIZE(formats))
+		return -EINVAL;
+	fmt = &formats[i];
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, true);
+}
+
+/* Get format */
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	struct v4l2_pix_format_mplane *pix_mp;
+
+	mfc_debug_enter();
+	pix_mp = &f->fmt.pix_mp;
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+	    (ctx->state == MFCINST_GOT_INST || ctx->state ==
+						MFCINST_RES_CHANGE_END)) {
+		/* If the MFC is parsing the header,
+		 * so wait until it is finished */
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
+									0);
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+	    ctx->state >= MFCINST_HEAD_PARSED &&
+	    ctx->state < MFCINST_ABORT) {
+		/* This is run on CAPTURE (decode output) */
+		/* Width and height are set to the dimensions
+		   of the movie, the buffer is bigger and
+		   further processing stages should crop to this
+		   rectangle. */
+		pix_mp->width = ctx->buf_width;
+		pix_mp->height = ctx->buf_height;
+		pix_mp->field = V4L2_FIELD_NONE;
+		pix_mp->num_planes = 2;
+		/* Set pixelformat to the format in which MFC
+		   outputs the decoded frame */
+		pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
+		pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+		pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+		pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+		pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* This is run on OUTPUT
+		   The buffer contains compressed image
+		   so width and height have no meaning */
+		pix_mp->width = 0;
+		pix_mp->height = 0;
+		pix_mp->field = V4L2_FIELD_NONE;
+		pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
+		pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
+		pix_mp->pixelformat = ctx->src_fmt->fourcc;
+		pix_mp->num_planes = ctx->src_fmt->num_planes;
+	} else {
+		mfc_err("Format could not be read\n");
+		mfc_debug(2, "%s-- with error\n", __func__);
+		return -EINVAL;
+	}
+	mfc_debug_leave();
+	return 0;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_fmt *fmt;
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_err("This node supports decoding only\n");
+		return -EINVAL;
+	}
+	fmt = find_format(f, MFC_FMT_DEC);
+	if (!fmt) {
+		mfc_err("Unsupported format\n");
+		return -EINVAL;
+	}
+	if (fmt->type != MFC_FMT_DEC) {
+		mfc_err("\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	int ret = 0;
+	struct s5p_mfc_fmt *fmt;
+	struct v4l2_pix_format_mplane *pix_mp;
+
+	mfc_debug_enter();
+	ret = vidioc_try_fmt(file, priv, f);
+	pix_mp = &f->fmt.pix_mp;
+	if (ret)
+		return ret;
+	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+		ret = -EBUSY;
+		goto out;
+	}
+	fmt = find_format(f, MFC_FMT_DEC);
+	if (!fmt || fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
+		mfc_err("Unknown codec\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (fmt->type != MFC_FMT_DEC) {
+		mfc_err("Wrong format selected, you should choose "
+					"format for decoding\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	ctx->src_fmt = fmt;
+	ctx->codec_mode = fmt->codec_mode;
+	mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
+	pix_mp->height = 0;
+	pix_mp->width = 0;
+	if (pix_mp->plane_fmt[0].sizeimage)
+		ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+	else
+		pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
+								DEF_CPB_SIZE;
+	pix_mp->plane_fmt[0].bytesperline = 0;
+	ctx->state = MFCINST_INIT;
+out:
+	mfc_debug_leave();
+	return ret;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+					  struct v4l2_requestbuffers *reqbufs)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	int ret = 0;
+	unsigned long flags;
+
+	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+		mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+		return -EINVAL;
+	}
+	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* Can only request buffers after an instance has been opened.*/
+		if (ctx->state == MFCINST_INIT) {
+			ctx->src_bufs_cnt = 0;
+			if (reqbufs->count == 0) {
+				mfc_debug(2, "Freeing buffers\n");
+				s5p_mfc_clock_on();
+				ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+				s5p_mfc_clock_off();
+				return ret;
+			}
+			/* Decoding */
+			if (ctx->output_state != QUEUE_FREE) {
+				mfc_err("Bufs have already been requested\n");
+				return -EINVAL;
+			}
+			s5p_mfc_clock_on();
+			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+			s5p_mfc_clock_off();
+			if (ret) {
+				mfc_err("vb2_reqbufs on output failed\n");
+				return ret;
+			}
+			mfc_debug(2, "vb2_reqbufs: %d\n", ret);
+			ctx->output_state = QUEUE_BUFS_REQUESTED;
+		}
+	} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ctx->dst_bufs_cnt = 0;
+		if (reqbufs->count == 0) {
+			mfc_debug(2, "Freeing buffers\n");
+			s5p_mfc_clock_on();
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			s5p_mfc_clock_off();
+			return ret;
+		}
+		if (ctx->capture_state != QUEUE_FREE) {
+			mfc_err("Bufs have already been requested\n");
+			return -EINVAL;
+		}
+		ctx->capture_state = QUEUE_BUFS_REQUESTED;
+		s5p_mfc_clock_on();
+		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+		s5p_mfc_clock_off();
+		if (ret) {
+			mfc_err("vb2_reqbufs on capture failed\n");
+			return ret;
+		}
+		if (reqbufs->count < ctx->dpb_count) {
+			mfc_err("Not enough buffers allocated\n");
+			reqbufs->count = 0;
+			s5p_mfc_clock_on();
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			s5p_mfc_clock_off();
+			return -ENOMEM;
+		}
+		ctx->total_dpb_count = reqbufs->count;
+		ret = s5p_mfc_alloc_codec_buffers(ctx);
+		if (ret) {
+			mfc_err("Failed to allocate decoding buffers\n");
+			reqbufs->count = 0;
+			s5p_mfc_clock_on();
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			s5p_mfc_clock_off();
+			return -ENOMEM;
+		}
+		if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
+			ctx->capture_state = QUEUE_BUFS_MMAPED;
+		} else {
+			mfc_err("Not all buffers passed to buf_init\n");
+			reqbufs->count = 0;
+			s5p_mfc_clock_on();
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			s5p_mfc_release_codec_buffers(ctx);
+			s5p_mfc_clock_off();
+			return -ENOMEM;
+		}
+		if (s5p_mfc_ctx_ready(ctx)) {
+			spin_lock_irqsave(&dev->condlock, flags);
+			set_bit(ctx->num, &dev->ctx_work_bits);
+			spin_unlock_irqrestore(&dev->condlock, flags);
+		}
+		s5p_mfc_try_run(dev);
+		s5p_mfc_wait_for_done_ctx(ctx,
+					 S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET, 0);
+	}
+	return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+						   struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+	int i;
+
+	if (buf->memory != V4L2_MEMORY_MMAP) {
+		mfc_err("Only mmaped buffers can be used\n");
+		return -EINVAL;
+	}
+	mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
+	if (ctx->state == MFCINST_INIT &&
+			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = vb2_querybuf(&ctx->vq_src, buf);
+	} else if (ctx->state == MFCINST_RUNNING &&
+			buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ret = vb2_querybuf(&ctx->vq_dst, buf);
+		for (i = 0; i < buf->length; i++)
+			buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
+	} else {
+		mfc_err("vidioc_querybuf called in an inappropriate state\n");
+		ret = -EINVAL;
+	}
+	mfc_debug_leave();
+	return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (ctx->state == MFCINST_ERROR) {
+		mfc_err("Call on QBUF after unrecoverable error\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_qbuf(&ctx->vq_src, buf);
+	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_qbuf(&ctx->vq_dst, buf);
+	return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (ctx->state == MFCINST_ERROR) {
+		mfc_err("Call on DQBUF after unrecoverable error\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+	return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	mfc_debug_enter();
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+
+		if (ctx->state == MFCINST_INIT) {
+			ctx->dst_bufs_cnt = 0;
+			ctx->src_bufs_cnt = 0;
+			ctx->capture_state = QUEUE_FREE;
+			ctx->output_state = QUEUE_FREE;
+			s5p_mfc_alloc_instance_buffer(ctx);
+			s5p_mfc_alloc_dec_temp_buffers(ctx);
+			spin_lock_irqsave(&dev->condlock, flags);
+			set_bit(ctx->num, &dev->ctx_work_bits);
+			spin_unlock_irqrestore(&dev->condlock, flags);
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			s5p_mfc_try_run(dev);
+
+			if (s5p_mfc_wait_for_done_ctx(ctx,
+				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+				/* Error or timeout */
+				mfc_err("Error getting instance from hardware\n");
+				s5p_mfc_release_instance_buffer(ctx);
+				s5p_mfc_release_dec_desc_buffer(ctx);
+				return -EIO;
+			}
+			mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+		}
+		ret = vb2_streamon(&ctx->vq_src, type);
+		}
+	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ret = vb2_streamon(&ctx->vq_dst, type);
+	mfc_debug_leave();
+	return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_streamoff(&ctx->vq_src, type);
+	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_streamoff(&ctx->vq_dst, type);
+	return -EINVAL;
+}
+
+/* Set controls - v4l2 control framework */
+static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+		ctx->loop_filter_mpeg4 = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
+		ctx->display_delay_enable = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+		ctx->display_delay = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+		ctx->slice_interface = ctrl->val;
+		break;
+	default:
+		mfc_err("Invalid control 0x%08x\n", ctrl->id);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+		if (ctx->state >= MFCINST_HEAD_PARSED &&
+		    ctx->state < MFCINST_ABORT) {
+			ctrl->val = ctx->dpb_count;
+			break;
+		} else if (ctx->state != MFCINST_INIT) {
+			v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+			return -EINVAL;
+		}
+		/* Should wait for the header to be parsed */
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_wait_for_done_ctx(ctx,
+				S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 0);
+		if (ctx->state >= MFCINST_HEAD_PARSED &&
+		    ctx->state < MFCINST_ABORT) {
+			ctrl->val = ctx->dpb_count;
+		} else {
+			v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+			return -EINVAL;
+		}
+		break;
+	}
+	return 0;
+}
+
+
+static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
+	.s_ctrl = s5p_mfc_dec_s_ctrl,
+	.g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
+};
+
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+		struct v4l2_crop *cr)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	u32 left, right, top, bottom;
+
+	if (ctx->state != MFCINST_HEAD_PARSED &&
+	ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
+					&& ctx->state != MFCINST_FINISHED) {
+			mfc_err("Cannont set crop\n");
+			return -EINVAL;
+		}
+	if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
+		left = s5p_mfc_read_shm(ctx, CROP_INFO_H);
+		right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
+		left = left & S5P_FIMV_SHARED_CROP_LEFT_MASK;
+		top = s5p_mfc_read_shm(ctx, CROP_INFO_V);
+		bottom = top >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
+		top = top & S5P_FIMV_SHARED_CROP_TOP_MASK;
+		cr->c.left = left;
+		cr->c.top = top;
+		cr->c.width = ctx->img_width - left - right;
+		cr->c.height = ctx->img_height - top - bottom;
+		mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
+			"w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
+			cr->c.width, cr->c.height, right, bottom,
+			ctx->buf_width, ctx->buf_height);
+	} else {
+		cr->c.left = 0;
+		cr->c.top = 0;
+		cr->c.width = ctx->img_width;
+		cr->c.height = ctx->img_height;
+		mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
+			"fh=%d\n", cr->c.width,	cr->c.height, ctx->buf_width,
+							ctx->buf_height);
+	}
+	return 0;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+	.vidioc_g_crop = vidioc_g_crop,
+};
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count,
+			       unsigned int *plane_count, unsigned long psize[],
+			       void *allocators[])
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+	/* Video output for decoding (source)
+	 * this can be set after getting an instance */
+	if (ctx->state == MFCINST_INIT &&
+	    vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* A single plane is required for input */
+		*plane_count = 1;
+		if (*buf_count < 1)
+			*buf_count = 1;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+	/* Video capture for decoding (destination)
+	 * this can be set after the header was parsed */
+	} else if (ctx->state == MFCINST_HEAD_PARSED &&
+		   vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* Output plane count is 2 - one for Y and one for CbCr */
+		*plane_count = 2;
+		/* Setup buffer count */
+		if (*buf_count < ctx->dpb_count)
+			*buf_count = ctx->dpb_count;
+		if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
+			*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+	} else {
+		mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+							ctx->state, vq->type);
+		return -EINVAL;
+	}
+	mfc_debug(2, "Buffer count=%d, plane count=%d\n",
+						*buf_count, *plane_count);
+	if (ctx->state == MFCINST_HEAD_PARSED &&
+	    vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		psize[0] = ctx->luma_size;
+		psize[1] = ctx->chroma_size;
+		allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+		allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+		   ctx->state == MFCINST_INIT) {
+		psize[0] = ctx->dec_src_buf_size;
+		allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+	} else {
+		mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	unsigned int i;
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->capture_state == QUEUE_BUFS_MMAPED)
+			return 0;
+		for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+			if (IS_ERR_OR_NULL(ERR_PTR(
+					vb2_dma_contig_plane_paddr(vb, i)))) {
+				mfc_err("Plane mem not allocated\n");
+				return -EINVAL;
+			}
+		}
+		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+			vb2_plane_size(vb, 1) < ctx->chroma_size) {
+			mfc_err("Plane buffer (CAPTURE) is too small\n");
+			return -EINVAL;
+		}
+		i = vb->v4l2_buf.index;
+		ctx->dst_bufs[i].b = vb;
+		ctx->dst_bufs[i].cookie.raw.luma =
+					vb2_dma_contig_plane_paddr(vb, 0);
+		ctx->dst_bufs[i].cookie.raw.chroma =
+					vb2_dma_contig_plane_paddr(vb, 1);
+		ctx->dst_bufs_cnt++;
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		if (IS_ERR_OR_NULL(ERR_PTR(
+					vb2_dma_contig_plane_paddr(vb, 0)))) {
+			mfc_err("Plane memory not allocated\n");
+			return -EINVAL;
+		}
+		if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
+			mfc_err("Plane buffer (OUTPUT) is too small\n");
+			return -EINVAL;
+		}
+
+		i = vb->v4l2_buf.index;
+		ctx->src_bufs[i].b = vb;
+		ctx->src_bufs[i].cookie.stream =
+					vb2_dma_contig_plane_paddr(vb, 0);
+		ctx->src_bufs_cnt++;
+	} else {
+		mfc_err("s5p_mfc_buf_init: unknown queue type\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+
+	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+	if (ctx->state == MFCINST_FINISHING ||
+		ctx->state == MFCINST_FINISHED)
+		ctx->state = MFCINST_RUNNING;
+	/* If context is ready then dev = work->data;schedule it to run */
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run(dev);
+	return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+	unsigned long flags;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	int aborted = 0;
+
+	if ((ctx->state == MFCINST_FINISHING ||
+		ctx->state ==  MFCINST_RUNNING) &&
+		dev->curr_ctx == ctx->num && dev->hw_lock) {
+		ctx->state = MFCINST_ABORT;
+		s5p_mfc_wait_for_done_ctx(ctx,
+					S5P_FIMV_R2H_CMD_FRAME_DONE_RET, 0);
+		aborted = 1;
+	}
+	spin_lock_irqsave(&dev->irqlock, flags);
+	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+		INIT_LIST_HEAD(&ctx->dst_queue);
+		ctx->dst_queue_cnt = 0;
+		ctx->dpb_flush_flag = 1;
+		ctx->dec_dst_flag = 0;
+	}
+	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+		INIT_LIST_HEAD(&ctx->src_queue);
+		ctx->src_queue_cnt = 0;
+	}
+	if (aborted)
+		ctx->state = MFCINST_RUNNING;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return 0;
+}
+
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *mfc_buf;
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+		mfc_buf->used = 0;
+		spin_lock_irqsave(&dev->irqlock, flags);
+		list_add_tail(&mfc_buf->list, &ctx->src_queue);
+		ctx->src_queue_cnt++;
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+		mfc_buf->used = 0;
+		/* Mark destination as available for use by MFC */
+		spin_lock_irqsave(&dev->irqlock, flags);
+		set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
+		list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+		ctx->dst_queue_cnt++;
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+	} else {
+		mfc_err("Unsupported buffer type (%d)\n", vq->type);
+	}
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_dec_qops = {
+	.queue_setup		= s5p_mfc_queue_setup,
+	.wait_prepare		= s5p_mfc_unlock,
+	.wait_finish		= s5p_mfc_lock,
+	.buf_init		= s5p_mfc_buf_init,
+	.start_streaming	= s5p_mfc_start_streaming,
+	.stop_streaming		= s5p_mfc_stop_streaming,
+	.buf_queue		= s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
+{
+	return &decoder_codec_ops;
+}
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+	return &s5p_mfc_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+	return &s5p_mfc_dec_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+						&& V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+	struct v4l2_ctrl_config cfg;
+	int i;
+
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+	if (ctx->ctrl_handler.error) {
+		mfc_err("v4l2_ctrl_handler_init failed\n");
+		return ctx->ctrl_handler.error;
+	}
+
+	for (i = 0; i < NUM_CTRLS; i++) {
+		if (IS_MFC51_PRIV(controls[i].id)) {
+			cfg.ops = &s5p_mfc_dec_ctrl_ops;
+			cfg.id = controls[i].id;
+			cfg.min = controls[i].minimum;
+			cfg.max = controls[i].maximum;
+			cfg.def = controls[i].default_value;
+			cfg.name = controls[i].name;
+			cfg.type = controls[i].type;
+
+			cfg.step = controls[i].step;
+			cfg.menu_skip_mask = 0;
+
+			ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&cfg, NULL);
+		} else {
+			ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+					&s5p_mfc_dec_ctrl_ops,
+					controls[i].id, controls[i].minimum,
+					controls[i].maximum, controls[i].step,
+					controls[i].default_value);
+		}
+		if (ctx->ctrl_handler.error) {
+			mfc_err("Adding control (%d) failed\n", i);
+			return ctx->ctrl_handler.error;
+		}
+		if (controls[i].is_volatile && ctx->ctrls[i])
+			ctx->ctrls[i]->is_volatile = 1;
+	}
+	return 0;
+}
+
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+	int i;
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+	for (i = 0; i < NUM_CTRLS; i++)
+		ctx->ctrls[i] = NULL;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.h b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
new file mode 100644
index 0000000..fb8b215
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_DEC_H_
+#define S5P_MFC_DEC_H_
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_DEC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
new file mode 100644
index 0000000..fee094a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -0,0 +1,1829 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * Jeongtae Park	<jtp.park@samsung.com>
+ * Kamil Debski		<k.debski@samsung.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/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+
+static struct s5p_mfc_fmt formats[] = {
+	{
+		.name = "4:2:0 2 Planes 64x32 Tiles",
+		.fourcc = V4L2_PIX_FMT_NV12MT,
+		.codec_mode = S5P_FIMV_CODEC_NONE,
+		.type = MFC_FMT_RAW,
+		.num_planes = 2,
+	},
+	{
+		.name = "4:2:0 2 Planes",
+		.fourcc = V4L2_PIX_FMT_NV12M,
+		.codec_mode = S5P_FIMV_CODEC_NONE,
+		.type = MFC_FMT_RAW,
+		.num_planes = 2,
+	},
+	{
+		.name = "H264 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.codec_mode = S5P_FIMV_CODEC_H264_ENC,
+		.type = MFC_FMT_ENC,
+		.num_planes = 1,
+	},
+	{
+		.name = "MPEG4 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_MPEG4,
+		.codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
+		.type = MFC_FMT_ENC,
+		.num_planes = 1,
+	},
+	{
+		.name = "H264 Encoded Stream",
+		.fourcc = V4L2_PIX_FMT_H263,
+		.codec_mode = S5P_FIMV_CODEC_H263_ENC,
+		.type = MFC_FMT_ENC,
+		.num_planes = 1,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+		    formats[i].type == t)
+			return &formats[i];
+	}
+	return NULL;
+}
+
+static struct mfc_control controls[] = {
+	{
+		.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+		.default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1900,
+		.maximum = (1 << 30) - 1,
+		.step = 1,
+		.default_value = 1900,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Padding Control Enable",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Padding Color YUV Value",
+		.minimum = 0,
+		.maximum = (1 << 25) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = (1 << 30) - 1,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Rate Control Reaction Coeff.",
+		.minimum = 1,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.name = "Force frame type",
+		.minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+		.maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
+		.default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+		.maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+		.default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.name = "Frame Skip Enable",
+		.minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+		.maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+		.menu_skip_mask = 0,
+		.default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Fixed Target Bit Enable",
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 2,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+		.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.menu_skip_mask = ~(
+				(1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+				(1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+				(1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+				),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
+		.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.menu_skip_mask = ~(
+				(1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+				(1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+				(1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+				(1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_1)
+				),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+		.maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+		.default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+		.maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
+		.default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+		.default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "The Number of Ref. Pic for P",
+		.minimum = 1,
+		.maximum = 2,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H263 I-Frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H263 Minimum QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H263 Maximum QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H263 P frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "H263 B frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "MPEG4 I-Frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "MPEG4 Minimum QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "MPEG4 Maximum QP value",
+		.minimum = 0,
+		.maximum = 51,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "MPEG4 P frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "MPEG4 B frame QP value",
+		.minimum = 1,
+		.maximum = 31,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "H264 Dark Reg Adaptive RC",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "H264 Smooth Reg Adaptive RC",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "H264 Static Reg Adaptive RC",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "H264 Activity Reg Adaptive RC",
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+		.maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+		.default_value = 0,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (1 << 16) - 1,
+		.step = 1,
+		.default_value = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+		.maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
+		.default_value = 0,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.step = 1,
+		.default_value = 0,
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+static const char * const *mfc51_get_menu(u32 id)
+{
+	static const char * const mfc51_video_frame_skip[] = {
+		"Disabled",
+		"Level Limit",
+		"VBV/CPB Limit",
+		NULL,
+	};
+	static const char * const mfc51_video_force_frame[] = {
+		"Disabled",
+		"I Frame",
+		"Not Coded",
+		NULL,
+	};
+	switch (id) {
+	case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+		return mfc51_video_frame_skip;
+	case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+		return mfc51_video_force_frame;
+	}
+	return NULL;
+}
+
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+	mfc_debug(2, "src=%d, dst=%d, state=%d\n",
+		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
+	/* context is ready to make header */
+	if (ctx->state == MFCINST_GOT_INST && ctx->dst_queue_cnt >= 1)
+		return 1;
+	/* context is ready to encode a frame */
+	if (ctx->state == MFCINST_RUNNING &&
+		ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+		return 1;
+	/* context is ready to encode remain frames */
+	if (ctx->state == MFCINST_FINISHING &&
+		ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+		return 1;
+	mfc_debug(2, "ctx is not ready\n");
+	return 0;
+}
+
+static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_buf *mb_entry;
+	unsigned long mb_y_addr, mb_c_addr;
+
+	/* move buffers in ref queue to src queue */
+	while (!list_empty(&ctx->ref_queue)) {
+		mb_entry = list_entry((&ctx->ref_queue)->next,
+						struct s5p_mfc_buf, list);
+		mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
+		mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+		list_del(&mb_entry->list);
+		ctx->ref_queue_cnt--;
+		list_add_tail(&mb_entry->list, &ctx->src_queue);
+		ctx->src_queue_cnt++;
+	}
+	mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+		  ctx->src_queue_cnt, ctx->ref_queue_cnt);
+	INIT_LIST_HEAD(&ctx->ref_queue);
+	ctx->ref_queue_cnt = 0;
+}
+
+static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf *dst_mb;
+	unsigned long dst_addr;
+	unsigned int dst_size;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+	dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+	dst_size = vb2_plane_size(dst_mb->b, 0);
+	s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return 0;
+}
+
+static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	struct s5p_mfc_buf *dst_mb;
+	unsigned long flags;
+
+	if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
+		spin_lock_irqsave(&dev->irqlock, flags);
+		dst_mb = list_entry(ctx->dst_queue.next,
+				struct s5p_mfc_buf, list);
+		list_del(&dst_mb->list);
+		ctx->dst_queue_cnt--;
+		vb2_set_plane_payload(dst_mb->b, 0,
+						s5p_mfc_get_enc_strm_size());
+		vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+	}
+	ctx->state = MFCINST_RUNNING;
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run(dev);
+	return 0;
+}
+
+static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf *dst_mb;
+	struct s5p_mfc_buf *src_mb;
+	unsigned long flags;
+	unsigned long src_y_addr, src_c_addr, dst_addr;
+	unsigned int dst_size;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0);
+	src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1);
+	s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+	dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+	dst_size = vb2_plane_size(dst_mb->b, 0);
+	s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	return 0;
+}
+
+static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf *mb_entry;
+	unsigned long enc_y_addr, enc_c_addr;
+	unsigned long mb_y_addr, mb_c_addr;
+	int slice_type;
+	unsigned int strm_size;
+	unsigned long flags;
+
+	slice_type = s5p_mfc_get_enc_slice_type();
+	strm_size = s5p_mfc_get_enc_strm_size();
+	mfc_debug(2, "Encoded slice type: %d", slice_type);
+	mfc_debug(2, "Encoded stream size: %d", strm_size);
+	mfc_debug(2, "Display order: %d",
+		  mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
+	spin_lock_irqsave(&dev->irqlock, flags);
+	if (slice_type >= 0) {
+		s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
+		list_for_each_entry(mb_entry, &ctx->src_queue, list) {
+			mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
+			mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+			if ((enc_y_addr == mb_y_addr) &&
+						(enc_c_addr == mb_c_addr)) {
+				list_del(&mb_entry->list);
+				ctx->src_queue_cnt--;
+				vb2_buffer_done(mb_entry->b,
+							VB2_BUF_STATE_DONE);
+				break;
+			}
+		}
+		list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
+			mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0);
+			mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1);
+			if ((enc_y_addr == mb_y_addr) &&
+						(enc_c_addr == mb_c_addr)) {
+				list_del(&mb_entry->list);
+				ctx->ref_queue_cnt--;
+				vb2_buffer_done(mb_entry->b,
+							VB2_BUF_STATE_DONE);
+				break;
+			}
+		}
+	}
+	if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) {
+		mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+									list);
+		if (mb_entry->used) {
+			list_del(&mb_entry->list);
+			ctx->src_queue_cnt--;
+			list_add_tail(&mb_entry->list, &ctx->ref_queue);
+			ctx->ref_queue_cnt++;
+		}
+		mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+			  ctx->src_queue_cnt, ctx->ref_queue_cnt);
+	}
+	if (strm_size > 0) {
+		/* at least one more dest. buffers exist always  */
+		mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
+									list);
+		list_del(&mb_entry->list);
+		ctx->dst_queue_cnt--;
+		switch (slice_type) {
+		case S5P_FIMV_ENC_SI_SLICE_TYPE_I:
+			mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+			break;
+		case S5P_FIMV_ENC_SI_SLICE_TYPE_P:
+			mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+			break;
+		case S5P_FIMV_ENC_SI_SLICE_TYPE_B:
+			mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
+			break;
+		}
+		vb2_set_plane_payload(mb_entry->b, 0, strm_size);
+		vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0)) {
+		spin_lock(&dev->condlock);
+		clear_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock(&dev->condlock);
+	}
+	return 0;
+}
+
+static struct s5p_mfc_codec_ops encoder_codec_ops = {
+	.pre_seq_start		= enc_pre_seq_start,
+	.post_seq_start		= enc_post_seq_start,
+	.pre_frame_start	= enc_pre_frame_start,
+	.post_frame_start	= enc_post_frame_start,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+
+	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->version = KERNEL_VERSION(1, 0, 0);
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+			  | V4L2_CAP_VIDEO_OUTPUT
+			  | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+	struct s5p_mfc_fmt *fmt;
+	int i, j = 0;
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (mplane && formats[i].num_planes == 1)
+			continue;
+		else if (!mplane && formats[i].num_planes > 1)
+			continue;
+		if (out && formats[i].type != MFC_FMT_RAW)
+			continue;
+		else if (!out && formats[i].type != MFC_FMT_ENC)
+			continue;
+		if (j == f->index) {
+			fmt = &formats[i];
+			strlcpy(f->description, fmt->name,
+				sizeof(f->description));
+			f->pixelformat = fmt->fourcc;
+			return 0;
+		}
+		++j;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+				   struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+					  struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+				   struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+					  struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+	mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* This is run on output (encoder dest) */
+		pix_fmt_mp->width = 0;
+		pix_fmt_mp->height = 0;
+		pix_fmt_mp->field = V4L2_FIELD_NONE;
+		pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+		pix_fmt_mp->num_planes = ctx->dst_fmt->num_planes;
+
+		pix_fmt_mp->plane_fmt[0].bytesperline = ctx->enc_dst_buf_size;
+		pix_fmt_mp->plane_fmt[0].sizeimage = ctx->enc_dst_buf_size;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* This is run on capture (encoder src) */
+		pix_fmt_mp->width = ctx->img_width;
+		pix_fmt_mp->height = ctx->img_height;
+
+		pix_fmt_mp->field = V4L2_FIELD_NONE;
+		pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+		pix_fmt_mp->num_planes = ctx->src_fmt->num_planes;
+
+		pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+		pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+		pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+		pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+	} else {
+		mfc_err("invalid buf type\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_fmt *fmt;
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = find_format(f, MFC_FMT_ENC);
+		if (!fmt) {
+			mfc_err("failed to try output format\n");
+			return -EINVAL;
+		}
+
+		if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+			mfc_err("must be set encoding output size\n");
+			return -EINVAL;
+		}
+
+		pix_fmt_mp->plane_fmt[0].bytesperline =
+			pix_fmt_mp->plane_fmt[0].sizeimage;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = find_format(f, MFC_FMT_RAW);
+		if (!fmt) {
+			mfc_err("failed to try output format\n");
+			return -EINVAL;
+		}
+
+		if (fmt->num_planes != pix_fmt_mp->num_planes) {
+			mfc_err("failed to try output format\n");
+			return -EINVAL;
+		}
+	} else {
+		mfc_err("invalid buf type\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_dev *dev = video_drvdata(file);
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	struct s5p_mfc_fmt *fmt;
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+	unsigned long flags;
+	int ret = 0;
+
+	ret = vidioc_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+		ret = -EBUSY;
+		goto out;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = find_format(f, MFC_FMT_ENC);
+		if (!fmt) {
+			mfc_err("failed to set capture format\n");
+			return -EINVAL;
+		}
+		ctx->state = MFCINST_INIT;
+		ctx->dst_fmt = fmt;
+		ctx->codec_mode = ctx->dst_fmt->codec_mode;
+		ctx->enc_dst_buf_size =	pix_fmt_mp->plane_fmt[0].sizeimage;
+		pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+		ctx->dst_bufs_cnt = 0;
+		ctx->capture_state = QUEUE_FREE;
+		s5p_mfc_alloc_instance_buffer(ctx);
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_try_run(dev);
+		if (s5p_mfc_wait_for_done_ctx(ctx, \
+				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
+				/* Error or timeout */
+			mfc_err("Error getting instance from hardware\n");
+			s5p_mfc_release_instance_buffer(ctx);
+			ret = -EIO;
+			goto out;
+		}
+		mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = find_format(f, MFC_FMT_RAW);
+		if (!fmt) {
+			mfc_err("failed to set output format\n");
+			return -EINVAL;
+		}
+		if (fmt->num_planes != pix_fmt_mp->num_planes) {
+			mfc_err("failed to set output format\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		ctx->src_fmt = fmt;
+		ctx->img_width = pix_fmt_mp->width;
+		ctx->img_height = pix_fmt_mp->height;
+		mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
+		mfc_debug(2, "fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
+			pix_fmt_mp->width, pix_fmt_mp->height,
+			ctx->img_width, ctx->img_height);
+		if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
+			ctx->buf_width = ALIGN(ctx->img_width,
+							S5P_FIMV_NV12M_HALIGN);
+			ctx->luma_size = ALIGN(ctx->img_width,
+				S5P_FIMV_NV12M_HALIGN) * ALIGN(ctx->img_height,
+				S5P_FIMV_NV12M_LVALIGN);
+			ctx->chroma_size = ALIGN(ctx->img_width,
+				S5P_FIMV_NV12M_HALIGN) * ALIGN((ctx->img_height
+				>> 1), S5P_FIMV_NV12M_CVALIGN);
+
+			ctx->luma_size = ALIGN(ctx->luma_size,
+							S5P_FIMV_NV12M_SALIGN);
+			ctx->chroma_size = ALIGN(ctx->chroma_size,
+							S5P_FIMV_NV12M_SALIGN);
+
+			pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+			pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+			pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+			pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+
+		} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+			ctx->buf_width = ALIGN(ctx->img_width,
+							S5P_FIMV_NV12MT_HALIGN);
+			ctx->luma_size = ALIGN(ctx->img_width,
+				S5P_FIMV_NV12MT_HALIGN)	* ALIGN(ctx->img_height,
+				S5P_FIMV_NV12MT_VALIGN);
+			ctx->chroma_size = ALIGN(ctx->img_width,
+				S5P_FIMV_NV12MT_HALIGN) * ALIGN((ctx->img_height
+				>> 1), S5P_FIMV_NV12MT_VALIGN);
+			ctx->luma_size = ALIGN(ctx->luma_size,
+							S5P_FIMV_NV12MT_SALIGN);
+			ctx->chroma_size = ALIGN(ctx->chroma_size,
+							S5P_FIMV_NV12MT_SALIGN);
+
+			pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+			pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+			pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+			pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+		}
+		ctx->src_bufs_cnt = 0;
+		ctx->output_state = QUEUE_FREE;
+	} else {
+		mfc_err("invalid buf type\n");
+		return -EINVAL;
+	}
+out:
+	mfc_debug_leave();
+	return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+					  struct v4l2_requestbuffers *reqbufs)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	int ret = 0;
+
+	/* if memory is not mmp or userptr return error */
+	if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
+		(reqbufs->memory != V4L2_MEMORY_USERPTR))
+		return -EINVAL;
+	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->capture_state != QUEUE_FREE) {
+			mfc_err("invalid capture state: %d\n",
+							ctx->capture_state);
+			return -EINVAL;
+		}
+		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+		if (ret != 0) {
+			mfc_err("error in vb2_reqbufs() for E(D)\n");
+			return ret;
+		}
+		ctx->capture_state = QUEUE_BUFS_REQUESTED;
+		ret = s5p_mfc_alloc_codec_buffers(ctx);
+		if (ret) {
+			mfc_err("Failed to allocate encoding buffers\n");
+			reqbufs->count = 0;
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			return -ENOMEM;
+		}
+	} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		if (ctx->output_state != QUEUE_FREE) {
+			mfc_err("invalid output state: %d\n",
+							ctx->output_state);
+			return -EINVAL;
+		}
+		ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+		if (ret != 0) {
+			mfc_err("error in vb2_reqbufs() for E(S)\n");
+			return ret;
+		}
+		ctx->output_state = QUEUE_BUFS_REQUESTED;
+	} else {
+		mfc_err("invalid buf type\n");
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+						   struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+	int ret = 0;
+
+	/* if memory is not mmp or userptr return error */
+	if ((buf->memory != V4L2_MEMORY_MMAP) &&
+		(buf->memory != V4L2_MEMORY_USERPTR))
+		return -EINVAL;
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->state != MFCINST_GOT_INST) {
+			mfc_err("invalid context state: %d\n", ctx->state);
+			return -EINVAL;
+		}
+		ret = vb2_querybuf(&ctx->vq_dst, buf);
+		if (ret != 0) {
+			mfc_err("error in vb2_querybuf() for E(D)\n");
+			return ret;
+		}
+		buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+	} else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = vb2_querybuf(&ctx->vq_src, buf);
+		if (ret != 0) {
+			mfc_err("error in vb2_querybuf() for E(S)\n");
+			return ret;
+		}
+	} else {
+		mfc_err("invalid buf type\n");
+		return -EINVAL;
+	}
+	return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (ctx->state == MFCINST_ERROR) {
+		mfc_err("Call on QBUF after unrecoverable error\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_qbuf(&ctx->vq_src, buf);
+	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_qbuf(&ctx->vq_dst, buf);
+	return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (ctx->state == MFCINST_ERROR) {
+		mfc_err("Call on DQBUF after unrecoverable error\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+	return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_streamon(&ctx->vq_src, type);
+	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_streamon(&ctx->vq_dst, type);
+	return -EINVAL;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_streamoff(&ctx->vq_src, type);
+	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return vb2_streamoff(&ctx->vq_dst, type);
+	return -EINVAL;
+}
+
+static inline int h264_level(enum v4l2_mpeg_video_h264_level lvl)
+{
+	static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_4_0 + 1] = {
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_1_0   */ 10,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_1B    */ 9,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_1_1   */ 11,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_1_2   */ 12,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_1_3   */ 13,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_2_0   */ 20,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_2_1   */ 21,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_2_2   */ 22,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_3_0   */ 30,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_3_1   */ 31,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_3_2   */ 32,
+		/* V4L2_MPEG_VIDEO_H264_LEVEL_4_0   */ 40,
+	};
+	return t[lvl];
+}
+
+static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
+{
+	static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 + 1] = {
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0    */ 0,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B   */ 9,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1    */ 1,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2    */ 2,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3    */ 3,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B   */ 7,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4    */ 4,
+		/* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5    */ 5,
+	};
+	return t[lvl];
+}
+
+static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
+{
+	static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED     */ 0,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1             */ 1,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11           */ 2,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11           */ 3,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11           */ 4,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33           */ 5,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11           */ 6,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11           */ 7,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11           */ 8,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33           */ 9,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11           */ 10,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11           */ 11,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33           */ 12,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99          */ 13,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3             */ 14,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2             */ 15,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1             */ 16,
+		/* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED        */ 255,
+	};
+	return t[sar];
+}
+
+static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		p->gop_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		p->slice_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+		p->slice_mb = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+		p->slice_bit = ctrl->val * 8;
+		break;
+	case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+		p->intra_refresh_mb = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
+		p->pad = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
+		p->pad_luma = (ctrl->val >> 16) & 0xff;
+		p->pad_cb = (ctrl->val >> 8) & 0xff;
+		p->pad_cr = (ctrl->val >> 0) & 0xff;
+		break;
+	case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+		p->rc_frame = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		p->rc_bitrate = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
+		p->rc_reaction_coeff = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+		ctx->force_frame_type = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+		p->vbv_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+		p->codec.h264.cpb_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		p->seq_hdr_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+		p->frame_skip_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
+		p->fixed_target_bit = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		p->num_b_frame = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			p->codec.h264.profile =
+					S5P_FIMV_ENC_PROFILE_H264_MAIN;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			p->codec.h264.profile =
+					S5P_FIMV_ENC_PROFILE_H264_HIGH;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			p->codec.h264.profile =
+				S5P_FIMV_ENC_PROFILE_H264_BASELINE;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		p->codec.h264.level_v4l2 = ctrl->val;
+		p->codec.h264.level = h264_level(ctrl->val);
+		if (p->codec.h264.level < 0) {
+			mfc_err("Level number is wrong\n");
+			ret = p->codec.h264.level;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+		p->codec.mpeg4.level_v4l2 = ctrl->val;
+		p->codec.mpeg4.level = mpeg4_level(ctrl->val);
+		if (p->codec.mpeg4.level < 0) {
+			mfc_err("Level number is wrong\n");
+			ret = p->codec.mpeg4.level;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+		p->codec.h264.loop_filter_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+		p->codec.h264.loop_filter_alpha = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+		p->codec.h264.loop_filter_beta = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		p->codec.h264.entropy_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
+		p->codec.h264.num_ref_pic_4p = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+		p->codec.h264._8x8_transform = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+		p->codec.h264.rc_mb = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+		p->codec.h264.rc_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+		p->codec.h264.rc_min_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		p->codec.h264.rc_max_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+		p->codec.h264.rc_p_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+		p->codec.h264.rc_b_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+		p->codec.mpeg4.rc_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+		p->codec.mpeg4.rc_min_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+		p->codec.mpeg4.rc_max_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+		p->codec.mpeg4.rc_p_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+		p->codec.mpeg4.rc_b_frame_qp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
+		p->codec.h264.rc_mb_dark = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
+		p->codec.h264.rc_mb_smooth = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
+		p->codec.h264.rc_mb_static = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
+		p->codec.h264.rc_mb_activity = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+		p->codec.h264.vui_sar = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+		p->codec.h264.vui_sar_idc = vui_sar_idc(ctrl->val);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+		p->codec.h264.vui_ext_sar_width = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+		p->codec.h264.vui_ext_sar_height = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+		p->codec.h264.open_gop = !ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+		p->codec.h264.open_gop_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+			p->codec.mpeg4.profile =
+				S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE;
+			break;
+		case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+			p->codec.mpeg4.profile =
+			S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+		p->codec.mpeg4.quarter_pixel = ctrl->val;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+							ctrl->id, ctrl->val);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
+	.s_ctrl = s5p_mfc_enc_s_ctrl,
+};
+
+int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ctx->enc_params.rc_framerate_num =
+					a->parm.output.timeperframe.denominator;
+		ctx->enc_params.rc_framerate_denom =
+					a->parm.output.timeperframe.numerator;
+	} else {
+		mfc_err("Setting FPS is only possible for the output queue\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+	if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		a->parm.output.timeperframe.denominator =
+					ctx->enc_params.rc_framerate_num;
+		a->parm.output.timeperframe.numerator =
+					ctx->enc_params.rc_framerate_denom;
+	} else {
+		mfc_err("Setting FPS is only possible for the output queue\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+	.vidioc_s_parm = vidioc_s_parm,
+	.vidioc_g_parm = vidioc_g_parm,
+};
+
+static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
+{
+	int i;
+
+	if (!fmt)
+		return -EINVAL;
+	if (fmt->num_planes != vb->num_planes) {
+		mfc_err("invalid plane number for the format\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < fmt->num_planes; i++) {
+		if (!vb2_dma_contig_plane_paddr(vb, i)) {
+			mfc_err("failed to get plane cookie\n");
+			return -EINVAL;
+		}
+		mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
+				vb->v4l2_buf.index, i,
+				vb2_dma_contig_plane_paddr(vb, i));
+	}
+	return 0;
+}
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+		       unsigned int *buf_count, unsigned int *plane_count,
+		       unsigned long psize[], void *allocators[])
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+	if (ctx->state != MFCINST_GOT_INST) {
+		mfc_err("inavlid state: %d\n", ctx->state);
+		return -EINVAL;
+	}
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->dst_fmt)
+			*plane_count = ctx->dst_fmt->num_planes;
+		else
+			*plane_count = MFC_ENC_CAP_PLANE_COUNT;
+		if (*buf_count < 1)
+			*buf_count = 1;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+		psize[0] = ctx->enc_dst_buf_size;
+		allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		if (ctx->src_fmt)
+			*plane_count = ctx->src_fmt->num_planes;
+		else
+			*plane_count = MFC_ENC_OUT_PLANE_COUNT;
+
+		if (*buf_count < 1)
+			*buf_count = 1;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+		psize[0] = ctx->luma_size;
+		psize[1] = ctx->chroma_size;
+		allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+		allocators[1] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+	} else {
+		mfc_err("inavlid queue type: %d\n", vq->type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	unsigned int i;
+	int ret;
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+		if (ret < 0)
+			return ret;
+		i = vb->v4l2_buf.index;
+		ctx->dst_bufs[i].b = vb;
+		ctx->dst_bufs[i].cookie.stream =
+					vb2_dma_contig_plane_paddr(vb, 0);
+		ctx->dst_bufs_cnt++;
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = check_vb_with_fmt(ctx->src_fmt, vb);
+		if (ret < 0)
+			return ret;
+		i = vb->v4l2_buf.index;
+		ctx->src_bufs[i].b = vb;
+		ctx->src_bufs[i].cookie.raw.luma =
+					vb2_dma_contig_plane_paddr(vb, 0);
+		ctx->src_bufs[i].cookie.raw.chroma =
+					vb2_dma_contig_plane_paddr(vb, 1);
+		ctx->src_bufs_cnt++;
+	} else {
+		mfc_err("inavlid queue type: %d\n", vq->type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	int ret;
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+		if (ret < 0)
+			return ret;
+		mfc_debug(2, "plane size: %ld, dst size: %d\n",
+			vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
+		if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
+			mfc_err("plane size is too small for capture\n");
+			return -EINVAL;
+		}
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = check_vb_with_fmt(ctx->src_fmt, vb);
+		if (ret < 0)
+			return ret;
+		mfc_debug(2, "plane size: %ld, luma size: %d\n",
+			vb2_plane_size(vb, 0), ctx->luma_size);
+		mfc_debug(2, "plane size: %ld, chroma size: %d\n",
+			vb2_plane_size(vb, 1), ctx->chroma_size);
+		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+		    vb2_plane_size(vb, 1) < ctx->chroma_size) {
+			mfc_err("plane size is too small for output\n");
+			return -EINVAL;
+		}
+	} else {
+		mfc_err("inavlid queue type: %d\n", vq->type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q)
+{
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+
+	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+	/* If context is ready then dev = work->data;schedule it to run */
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run(dev);
+	return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+	unsigned long flags;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	if ((ctx->state == MFCINST_FINISHING ||
+		ctx->state == MFCINST_RUNNING) &&
+		dev->curr_ctx == ctx->num && dev->hw_lock) {
+		ctx->state = MFCINST_ABORT;
+		s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_FRAME_DONE_RET,
+					  0);
+	}
+	ctx->state = MFCINST_FINISHED;
+	spin_lock_irqsave(&dev->irqlock, flags);
+	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+		INIT_LIST_HEAD(&ctx->dst_queue);
+		ctx->dst_queue_cnt = 0;
+	}
+	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		cleanup_ref_queue(ctx);
+		s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+		INIT_LIST_HEAD(&ctx->src_queue);
+		ctx->src_queue_cnt = 0;
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return 0;
+}
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *mfc_buf;
+
+	if (ctx->state == MFCINST_ERROR) {
+		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+		cleanup_ref_queue(ctx);
+		return;
+	}
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+		mfc_buf->used = 0;
+		/* Mark destination as available for use by MFC */
+		spin_lock_irqsave(&dev->irqlock, flags);
+		list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+		ctx->dst_queue_cnt++;
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+		mfc_buf->used = 0;
+		spin_lock_irqsave(&dev->irqlock, flags);
+		if (vb->v4l2_planes[0].bytesused == 0) {
+			mfc_debug(1, "change state to FINISHING\n");
+			ctx->state = MFCINST_FINISHING;
+			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+			cleanup_ref_queue(ctx);
+		} else {
+			list_add_tail(&mfc_buf->list, &ctx->src_queue);
+			ctx->src_queue_cnt++;
+		}
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+	} else {
+		mfc_err("unsupported buffer type (%d)\n", vq->type);
+	}
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_enc_qops = {
+	.queue_setup		= s5p_mfc_queue_setup,
+	.wait_prepare		= s5p_mfc_unlock,
+	.wait_finish		= s5p_mfc_lock,
+	.buf_init		= s5p_mfc_buf_init,
+	.buf_prepare		= s5p_mfc_buf_prepare,
+	.start_streaming	= s5p_mfc_start_streaming,
+	.stop_streaming		= s5p_mfc_stop_streaming,
+	.buf_queue		= s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
+{
+	return &encoder_codec_ops;
+}
+
+struct vb2_ops *get_enc_queue_ops(void)
+{
+	return &s5p_mfc_enc_qops;
+}
+
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
+{
+	return &s5p_mfc_enc_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+						&& V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+	struct v4l2_ctrl_config cfg;
+	int i;
+
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+	if (ctx->ctrl_handler.error) {
+		mfc_err("v4l2_ctrl_handler_init failed\n");
+		return ctx->ctrl_handler.error;
+	}
+	for (i = 0; i < NUM_CTRLS; i++) {
+		if (IS_MFC51_PRIV(controls[i].id)) {
+			cfg.ops = &s5p_mfc_enc_ctrl_ops;
+			cfg.id = controls[i].id;
+			cfg.min = controls[i].minimum;
+			cfg.max = controls[i].maximum;
+			cfg.def = controls[i].default_value;
+			cfg.name = controls[i].name;
+			cfg.type = controls[i].type;
+			cfg.flags = 0;
+
+			if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+				cfg.step = 0;
+				cfg.menu_skip_mask = cfg.menu_skip_mask;
+				cfg.qmenu = mfc51_get_menu(cfg.id);
+			} else {
+				cfg.step = controls[i].step;
+				cfg.menu_skip_mask = 0;
+			}
+			ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&cfg, NULL);
+		} else {
+			if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+				ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
+					&ctx->ctrl_handler,
+					&s5p_mfc_enc_ctrl_ops, controls[i].id,
+					controls[i].maximum, 0,
+					controls[i].default_value);
+			} else {
+				ctx->ctrls[i] = v4l2_ctrl_new_std(
+					&ctx->ctrl_handler,
+					&s5p_mfc_enc_ctrl_ops, controls[i].id,
+					controls[i].minimum,
+					controls[i].maximum, controls[i].step,
+					controls[i].default_value);
+			}
+		}
+		if (ctx->ctrl_handler.error) {
+			mfc_err("Adding control (%d) failed\n", i);
+			return ctx->ctrl_handler.error;
+		}
+		if (controls[i].is_volatile && ctx->ctrls[i])
+			ctx->ctrls[i]->is_volatile = 1;
+	}
+	return 0;
+}
+
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+	int i;
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+	for (i = 0; i < NUM_CTRLS; i++)
+		ctx->ctrls[i] = NULL;
+}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.h b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
new file mode 100644
index 0000000..405bdd3
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_ENC_H_
+#define S5P_MFC_ENC_H_
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
+struct vb2_ops *get_enc_queue_ops(void);
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_ENC_H_  */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
new file mode 100644
index 0000000..8f2f8bf
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
@@ -0,0 +1,92 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
+ *
+ * C file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains functions used to wait for command completion.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+	int ret;
+
+	ret = wait_event_interruptible_timeout(dev->queue,
+		(dev->int_cond && (dev->int_type == command
+		|| dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+		msecs_to_jiffies(MFC_INT_TIMEOUT));
+	if (ret == 0) {
+		mfc_err("Interrupt (dev->int_type:%d, command:%d) timed out\n",
+							dev->int_type, command);
+		return 1;
+	} else if (ret == -ERESTARTSYS) {
+		mfc_err("Interrupted by a signal\n");
+		return 1;
+	}
+	mfc_debug(1, "Finished waiting (dev->int_type:%d, command: %d)\n",
+							dev->int_type, command);
+	if (dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+		return 1;
+	return 0;
+}
+
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+	dev->int_cond = 0;
+	dev->int_type = 0;
+	dev->int_err = 0;
+}
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+				    int command, int interrupt)
+{
+	int ret;
+
+	if (interrupt) {
+		ret = wait_event_interruptible_timeout(ctx->queue,
+				(ctx->int_cond && (ctx->int_type == command
+			|| ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+					msecs_to_jiffies(MFC_INT_TIMEOUT));
+	} else {
+		ret = wait_event_timeout(ctx->queue,
+				(ctx->int_cond && (ctx->int_type == command
+			|| ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+					msecs_to_jiffies(MFC_INT_TIMEOUT));
+	}
+	if (ret == 0) {
+		mfc_err("Interrupt (ctx->int_type:%d, command:%d) timed out\n",
+							ctx->int_type, command);
+		return 1;
+	} else if (ret == -ERESTARTSYS) {
+		mfc_err("Interrupted by a signal\n");
+		return 1;
+	}
+	mfc_debug(1, "Finished waiting (ctx->int_type:%d, command: %d)\n",
+							ctx->int_type, command);
+	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+		return 1;
+	return 0;
+}
+
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+	ctx->int_cond = 0;
+	ctx->int_type = 0;
+	ctx->int_err = 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
new file mode 100644
index 0000000..122d773
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * It contains waiting functions declarations.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_INTR_H_
+#define S5P_MFC_INTR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+			      int command, int interrupt);
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_INTR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
new file mode 100644
index 0000000..7b23916
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -0,0 +1,1397 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
+ *
+ * Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.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 "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#define OFFSETA(x)		(((x) - dev->bank1) >> MFC_OFFSET_SHIFT)
+#define OFFSETB(x)		(((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
+
+/* Allocate temporary buffers for decoding */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
+{
+	void *desc_virt;
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	ctx->desc_buf = vb2_dma_contig_memops.alloc(
+			dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
+	if (IS_ERR_VALUE((int)ctx->desc_buf)) {
+		ctx->desc_buf = 0;
+		mfc_err("Allocating DESC buffer failed\n");
+		return -ENOMEM;
+	}
+	ctx->desc_phys = s5p_mfc_mem_cookie(
+			dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->desc_buf);
+	BUG_ON(ctx->desc_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+	desc_virt = vb2_dma_contig_memops.vaddr(ctx->desc_buf);
+	if (desc_virt == NULL) {
+		vb2_dma_contig_memops.put(ctx->desc_buf);
+		ctx->desc_phys = 0;
+		ctx->desc_buf = 0;
+		mfc_err("Remapping DESC buffer failed\n");
+		return -ENOMEM;
+	}
+	memset(desc_virt, 0, DESC_BUF_SIZE);
+	wmb();
+	return 0;
+}
+
+/* Release temporary buffers for decoding */
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+	if (ctx->desc_phys) {
+		vb2_dma_contig_memops.put(ctx->desc_buf);
+		ctx->desc_phys = 0;
+		ctx->desc_buf = 0;
+	}
+}
+
+/* Allocate codec buffers */
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned int enc_ref_y_size = 0;
+	unsigned int enc_ref_c_size = 0;
+	unsigned int guard_width, guard_height;
+
+	if (ctx->type == MFCINST_DECODER) {
+		mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
+			  ctx->luma_size, ctx->chroma_size, ctx->mv_size);
+		mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
+	} else if (ctx->type == MFCINST_ENCODER) {
+		enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+			* ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+		enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+
+		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+			enc_ref_c_size = ALIGN(ctx->img_width,
+						S5P_FIMV_NV12MT_HALIGN)
+						* ALIGN(ctx->img_height >> 1,
+						S5P_FIMV_NV12MT_VALIGN);
+			enc_ref_c_size = ALIGN(enc_ref_c_size,
+							S5P_FIMV_NV12MT_SALIGN);
+		} else {
+			guard_width = ALIGN(ctx->img_width + 16,
+							S5P_FIMV_NV12MT_HALIGN);
+			guard_height = ALIGN((ctx->img_height >> 1) + 4,
+							S5P_FIMV_NV12MT_VALIGN);
+			enc_ref_c_size = ALIGN(guard_width * guard_height,
+					       S5P_FIMV_NV12MT_SALIGN);
+		}
+		mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+			  enc_ref_y_size, enc_ref_c_size);
+	} else {
+		return -EINVAL;
+	}
+	/* Codecs have different memory requirements */
+	switch (ctx->codec_mode) {
+	case S5P_FIMV_CODEC_H264_DEC:
+		ctx->bank1_size =
+		    ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
+					S5P_FIMV_DEC_VERT_NB_MV_SIZE,
+					S5P_FIMV_DEC_BUF_ALIGN);
+		ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
+		break;
+	case S5P_FIMV_CODEC_MPEG4_DEC:
+		ctx->bank1_size =
+		    ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
+				     S5P_FIMV_DEC_UPNB_MV_SIZE +
+				     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+				     S5P_FIMV_DEC_STX_PARSER_SIZE +
+				     S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
+				     S5P_FIMV_DEC_BUF_ALIGN);
+		ctx->bank2_size = 0;
+		break;
+	case S5P_FIMV_CODEC_VC1RCV_DEC:
+	case S5P_FIMV_CODEC_VC1_DEC:
+		ctx->bank1_size =
+		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+			     S5P_FIMV_DEC_UPNB_MV_SIZE +
+			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+			     S5P_FIMV_DEC_NB_DCAC_SIZE +
+			     3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
+			     S5P_FIMV_DEC_BUF_ALIGN);
+		ctx->bank2_size = 0;
+		break;
+	case S5P_FIMV_CODEC_MPEG2_DEC:
+		ctx->bank1_size = 0;
+		ctx->bank2_size = 0;
+		break;
+	case S5P_FIMV_CODEC_H263_DEC:
+		ctx->bank1_size =
+		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+			     S5P_FIMV_DEC_UPNB_MV_SIZE +
+			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+			     S5P_FIMV_DEC_NB_DCAC_SIZE,
+			     S5P_FIMV_DEC_BUF_ALIGN);
+		ctx->bank2_size = 0;
+		break;
+	case S5P_FIMV_CODEC_H264_ENC:
+		ctx->bank1_size = (enc_ref_y_size * 2) +
+				   S5P_FIMV_ENC_UPMV_SIZE +
+				   S5P_FIMV_ENC_COLFLG_SIZE +
+				   S5P_FIMV_ENC_INTRAMD_SIZE +
+				   S5P_FIMV_ENC_NBORINFO_SIZE;
+		ctx->bank2_size = (enc_ref_y_size * 2) +
+				   (enc_ref_c_size * 4) +
+				   S5P_FIMV_ENC_INTRAPRED_SIZE;
+		break;
+	case S5P_FIMV_CODEC_MPEG4_ENC:
+		ctx->bank1_size = (enc_ref_y_size * 2) +
+				   S5P_FIMV_ENC_UPMV_SIZE +
+				   S5P_FIMV_ENC_COLFLG_SIZE +
+				   S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		ctx->bank2_size = (enc_ref_y_size * 2) +
+				   (enc_ref_c_size * 4);
+		break;
+	case S5P_FIMV_CODEC_H263_ENC:
+		ctx->bank1_size = (enc_ref_y_size * 2) +
+				   S5P_FIMV_ENC_UPMV_SIZE +
+				   S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		ctx->bank2_size = (enc_ref_y_size * 2) +
+				   (enc_ref_c_size * 4);
+		break;
+	default:
+		break;
+	}
+	/* Allocate only if memory from bank 1 is necessary */
+	if (ctx->bank1_size > 0) {
+		ctx->bank1_buf = vb2_dma_contig_memops.alloc(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
+		if (IS_ERR(ctx->bank1_buf)) {
+			ctx->bank1_buf = 0;
+			printk(KERN_ERR
+			       "Buf alloc for decoding failed (port A)\n");
+			return -ENOMEM;
+		}
+		ctx->bank1_phys = s5p_mfc_mem_cookie(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
+		BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+	}
+	/* Allocate only if memory from bank 2 is necessary */
+	if (ctx->bank2_size > 0) {
+		ctx->bank2_buf = vb2_dma_contig_memops.alloc(
+		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
+		if (IS_ERR(ctx->bank2_buf)) {
+			ctx->bank2_buf = 0;
+			mfc_err("Buf alloc for decoding failed (port B)\n");
+			return -ENOMEM;
+		}
+		ctx->bank2_phys = s5p_mfc_mem_cookie(
+		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
+		BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
+	}
+	return 0;
+}
+
+/* Release buffers allocated for codec */
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+	if (ctx->bank1_buf) {
+		vb2_dma_contig_memops.put(ctx->bank1_buf);
+		ctx->bank1_buf = 0;
+		ctx->bank1_phys = 0;
+		ctx->bank1_size = 0;
+	}
+	if (ctx->bank2_buf) {
+		vb2_dma_contig_memops.put(ctx->bank2_buf);
+		ctx->bank2_buf = 0;
+		ctx->bank2_phys = 0;
+		ctx->bank2_size = 0;
+	}
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+	void *context_virt;
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
+		ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+		ctx->ctx_size = MFC_H264_CTX_BUF_SIZE;
+	else
+		ctx->ctx_size = MFC_CTX_BUF_SIZE;
+	ctx->ctx_buf = vb2_dma_contig_memops.alloc(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_size);
+	if (IS_ERR(ctx->ctx_buf)) {
+		mfc_err("Allocating context buffer failed\n");
+		ctx->ctx_phys = 0;
+		ctx->ctx_buf = 0;
+		return -ENOMEM;
+	}
+	ctx->ctx_phys = s5p_mfc_mem_cookie(
+		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_buf);
+	BUG_ON(ctx->ctx_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+	ctx->ctx_ofs = OFFSETA(ctx->ctx_phys);
+	context_virt = vb2_dma_contig_memops.vaddr(ctx->ctx_buf);
+	if (context_virt == NULL) {
+		mfc_err("Remapping instance buffer failed\n");
+		vb2_dma_contig_memops.put(ctx->ctx_buf);
+		ctx->ctx_phys = 0;
+		ctx->ctx_buf = 0;
+		return -ENOMEM;
+	}
+	/* Zero content of the allocated memory */
+	memset(context_virt, 0, ctx->ctx_size);
+	wmb();
+	if (s5p_mfc_init_shm(ctx) < 0) {
+		vb2_dma_contig_memops.put(ctx->ctx_buf);
+		ctx->ctx_phys = 0;
+		ctx->ctx_buf = 0;
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+	if (ctx->ctx_buf) {
+		vb2_dma_contig_memops.put(ctx->ctx_buf);
+		ctx->ctx_phys = 0;
+		ctx->ctx_buf = 0;
+	}
+	if (ctx->shm_alloc) {
+		vb2_dma_contig_memops.put(ctx->shm_alloc);
+		ctx->shm_alloc = 0;
+		ctx->shm = 0;
+	}
+}
+
+/* Set registers for decoding temporary buffers */
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mfc_write(dev, OFFSETA(ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
+	mfc_write(dev, DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
+}
+
+/* Set registers for shared buffer */
+void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+		  unsigned int start_num_byte, unsigned int buf_size)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mfc_write(dev, OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
+	mfc_write(dev, ctx->dec_src_buf_size, S5P_FIMV_SI_CH0_CPB_SIZE);
+	mfc_write(dev, buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
+	s5p_mfc_write_shm(ctx, start_num_byte, START_BYTE_NUM);
+	return 0;
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx)
+{
+	unsigned int frame_size, i;
+	unsigned int frame_size_ch, frame_size_mv;
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned int dpb;
+	size_t buf_addr1, buf_addr2;
+	int buf_size1, buf_size2;
+
+	buf_addr1 = ctx->bank1_phys;
+	buf_size1 = ctx->bank1_size;
+	buf_addr2 = ctx->bank2_phys;
+	buf_size2 = ctx->bank2_size;
+	dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+						~S5P_FIMV_DPB_COUNT_MASK;
+	mfc_write(dev, ctx->total_dpb_count | dpb,
+						S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+	s5p_mfc_set_shared_buffer(ctx);
+	switch (ctx->codec_mode) {
+	case S5P_FIMV_CODEC_H264_DEC:
+		mfc_write(dev, OFFSETA(buf_addr1),
+						S5P_FIMV_H264_VERT_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_NB_IP_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
+		break;
+	case S5P_FIMV_CODEC_MPEG4_DEC:
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SP_ADR);
+		buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		break;
+	case S5P_FIMV_CODEC_H263_DEC:
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		break;
+	case S5P_FIMV_CODEC_VC1_DEC:
+	case S5P_FIMV_CODEC_VC1RCV_DEC:
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE3_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE2_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE1_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		break;
+	case S5P_FIMV_CODEC_MPEG2_DEC:
+		break;
+	default:
+		mfc_err("Unknown codec for decoding (%x)\n",
+			ctx->codec_mode);
+		return -EINVAL;
+		break;
+	}
+	frame_size = ctx->luma_size;
+	frame_size_ch = ctx->chroma_size;
+	frame_size_mv = ctx->mv_size;
+	mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+								frame_size_mv);
+	for (i = 0; i < ctx->total_dpb_count; i++) {
+		/* Bank2 */
+		mfc_debug(2, "Luma %d: %x\n", i,
+					ctx->dst_bufs[i].cookie.raw.luma);
+		mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
+						S5P_FIMV_DEC_LUMA_ADR + i * 4);
+		mfc_debug(2, "\tChroma %d: %x\n", i,
+					ctx->dst_bufs[i].cookie.raw.chroma);
+		mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
+					       S5P_FIMV_DEC_CHROMA_ADR + i * 4);
+		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+			mfc_debug(2, "\tBuf2: %x, size: %d\n",
+							buf_addr2, buf_size2);
+			mfc_write(dev, OFFSETB(buf_addr2),
+						S5P_FIMV_H264_MV_ADR + i * 4);
+			buf_addr2 += frame_size_mv;
+			buf_size2 -= frame_size_mv;
+		}
+	}
+	mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+	mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
+			buf_size1,  buf_size2, ctx->total_dpb_count);
+	if (buf_size1 < 0 || buf_size2 < 0) {
+		mfc_debug(2, "Not enough memory has been allocated\n");
+		return -ENOMEM;
+	}
+	s5p_mfc_write_shm(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+	s5p_mfc_write_shm(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
+	if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+		s5p_mfc_write_shm(ctx, frame_size_mv, ALLOC_MV_SIZE);
+	mfc_write(dev, ((S5P_FIMV_CH_INIT_BUFS & S5P_FIMV_CH_MASK)
+					<< S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+						S5P_FIMV_SI_CH0_INST_ID);
+	return 0;
+}
+
+/* Set registers for encoding stream buffer */
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long addr, unsigned int size)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mfc_write(dev, OFFSETA(addr), S5P_FIMV_ENC_SI_CH0_SB_ADR);
+	mfc_write(dev, size, S5P_FIMV_ENC_SI_CH0_SB_SIZE);
+	return 0;
+}
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long y_addr, unsigned long c_addr)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mfc_write(dev, OFFSETB(y_addr), S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR);
+	mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
+}
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long *y_addr, unsigned long *c_addr)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	*y_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_Y_ADDR)
+							<< MFC_OFFSET_SHIFT);
+	*c_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_C_ADDR)
+							<< MFC_OFFSET_SHIFT);
+}
+
+/* Set encoding ref & codec buffer */
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	size_t buf_addr1, buf_addr2;
+	size_t buf_size1, buf_size2;
+	unsigned int enc_ref_y_size, enc_ref_c_size;
+	unsigned int guard_width, guard_height;
+	int i;
+
+	buf_addr1 = ctx->bank1_phys;
+	buf_size1 = ctx->bank1_size;
+	buf_addr2 = ctx->bank2_phys;
+	buf_size2 = ctx->bank2_size;
+	enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+		* ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+	enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+	if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+		enc_ref_c_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+			* ALIGN((ctx->img_height >> 1), S5P_FIMV_NV12MT_VALIGN);
+		enc_ref_c_size = ALIGN(enc_ref_c_size, S5P_FIMV_NV12MT_SALIGN);
+	} else {
+		guard_width = ALIGN(ctx->img_width + 16,
+						S5P_FIMV_NV12MT_HALIGN);
+		guard_height = ALIGN((ctx->img_height >> 1) + 4,
+						S5P_FIMV_NV12MT_VALIGN);
+		enc_ref_c_size = ALIGN(guard_width * guard_height,
+				       S5P_FIMV_NV12MT_SALIGN);
+	}
+	mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+	switch (ctx->codec_mode) {
+	case S5P_FIMV_CODEC_H264_ENC:
+		for (i = 0; i < 2; i++) {
+			mfc_write(dev, OFFSETA(buf_addr1),
+				S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+			buf_addr1 += enc_ref_y_size;
+			buf_size1 -= enc_ref_y_size;
+
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_y_size;
+			buf_size2 -= enc_ref_y_size;
+		}
+		for (i = 0; i < 4; i++) {
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_c_size;
+			buf_size2 -= enc_ref_c_size;
+		}
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_UP_MV_ADR);
+		buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1),
+					S5P_FIMV_H264_COZERO_FLAG_ADR);
+		buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1),
+					S5P_FIMV_H264_UP_INTRA_MD_ADR);
+		buf_addr1 += S5P_FIMV_ENC_INTRAMD_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_INTRAMD_SIZE;
+		mfc_write(dev, OFFSETB(buf_addr2),
+					S5P_FIMV_H264_UP_INTRA_PRED_ADR);
+		buf_addr2 += S5P_FIMV_ENC_INTRAPRED_SIZE;
+		buf_size2 -= S5P_FIMV_ENC_INTRAPRED_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1),
+					S5P_FIMV_H264_NBOR_INFO_ADR);
+		buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
+		mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+			buf_size1, buf_size2);
+		break;
+	case S5P_FIMV_CODEC_MPEG4_ENC:
+		for (i = 0; i < 2; i++) {
+			mfc_write(dev, OFFSETA(buf_addr1),
+				S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+			buf_addr1 += enc_ref_y_size;
+			buf_size1 -= enc_ref_y_size;
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_y_size;
+			buf_size2 -= enc_ref_y_size;
+		}
+		for (i = 0; i < 4; i++) {
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_c_size;
+			buf_size2 -= enc_ref_c_size;
+		}
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_MV_ADR);
+		buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1),
+						S5P_FIMV_MPEG4_COZERO_FLAG_ADR);
+		buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1),
+						S5P_FIMV_MPEG4_ACDC_COEF_ADR);
+		buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+			buf_size1, buf_size2);
+		break;
+	case S5P_FIMV_CODEC_H263_ENC:
+		for (i = 0; i < 2; i++) {
+			mfc_write(dev, OFFSETA(buf_addr1),
+				S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+			buf_addr1 += enc_ref_y_size;
+			buf_size1 -= enc_ref_y_size;
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_y_size;
+			buf_size2 -= enc_ref_y_size;
+		}
+		for (i = 0; i < 4; i++) {
+			mfc_write(dev, OFFSETB(buf_addr2),
+				S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+			buf_addr2 += enc_ref_c_size;
+			buf_size2 -= enc_ref_c_size;
+		}
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_MV_ADR);
+		buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+		mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
+		buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+		mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+			buf_size1, buf_size2);
+		break;
+	default:
+		mfc_err("Unknown codec set for encoding: %d\n",
+			ctx->codec_mode);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	unsigned int reg;
+	unsigned int shm;
+
+	/* width */
+	mfc_write(dev, ctx->img_width, S5P_FIMV_ENC_HSIZE_PX);
+	/* height */
+	mfc_write(dev, ctx->img_height, S5P_FIMV_ENC_VSIZE_PX);
+	/* pictype : enable, IDR period */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	reg |= (1 << 18);
+	reg &= ~(0xFFFF);
+	reg |= p->gop_size;
+	mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	mfc_write(dev, 0, S5P_FIMV_ENC_B_RECON_WRITE_ON);
+	/* multi-slice control */
+	/* multi-slice MB number or bit size */
+	mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL);
+	if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+		mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB);
+	} else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
+		mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT);
+	} else {
+		mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB);
+		mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_BIT);
+	}
+	/* cyclic intra refresh */
+	mfc_write(dev, p->intra_refresh_mb, S5P_FIMV_ENC_CIR_CTRL);
+	/* memory structure cur. frame */
+	if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+		mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+	else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+		mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+	/* padding control & value */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PADDING_CTRL);
+	if (p->pad) {
+		/** enable */
+		reg |= (1 << 31);
+		/** cr value */
+		reg &= ~(0xFF << 16);
+		reg |= (p->pad_cr << 16);
+		/** cb value */
+		reg &= ~(0xFF << 8);
+		reg |= (p->pad_cb << 8);
+		/** y value */
+		reg &= ~(0xFF);
+		reg |= (p->pad_luma);
+	} else {
+		/** disable & all value clear */
+		reg = 0;
+	}
+	mfc_write(dev, reg, S5P_FIMV_ENC_PADDING_CTRL);
+	/* rate control config. */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+	/** frame-level rate control */
+	reg &= ~(0x1 << 9);
+	reg |= (p->rc_frame << 9);
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+	/* bit rate */
+	if (p->rc_frame)
+		mfc_write(dev, p->rc_bitrate,
+			S5P_FIMV_ENC_RC_BIT_RATE);
+	else
+		mfc_write(dev, 0, S5P_FIMV_ENC_RC_BIT_RATE);
+	/* reaction coefficient */
+	if (p->rc_frame)
+		mfc_write(dev, p->rc_reaction_coeff, S5P_FIMV_ENC_RC_RPARA);
+	shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+	/* seq header ctrl */
+	shm &= ~(0x1 << 3);
+	shm |= (p->seq_hdr_mode << 3);
+	/* frame skip mode */
+	shm &= ~(0x3 << 1);
+	shm |= (p->frame_skip_mode << 1);
+	s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+	/* fixed target bit */
+	s5p_mfc_write_shm(ctx, p->fixed_target_bit, RC_CONTROL_CONFIG);
+	return 0;
+}
+
+static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+	unsigned int reg;
+	unsigned int shm;
+
+	s5p_mfc_set_enc_params(ctx);
+	/* pictype : number of B */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	/* num_b_frame - 0 ~ 2 */
+	reg &= ~(0x3 << 16);
+	reg |= (p->num_b_frame << 16);
+	mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	/* profile & level */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+	/* level */
+	reg &= ~(0xFF << 8);
+	reg |= (p_264->level << 8);
+	/* profile - 0 ~ 2 */
+	reg &= ~(0x3F);
+	reg |= p_264->profile;
+	mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+	/* interlace  */
+	mfc_write(dev, p->interlace, S5P_FIMV_ENC_PIC_STRUCT);
+	/* height */
+	if (p->interlace)
+		mfc_write(dev, ctx->img_height >> 1, S5P_FIMV_ENC_VSIZE_PX);
+	/* loopfilter ctrl */
+	mfc_write(dev, p_264->loop_filter_mode, S5P_FIMV_ENC_LF_CTRL);
+	/* loopfilter alpha offset */
+	if (p_264->loop_filter_alpha < 0) {
+		reg = 0x10;
+		reg |= (0xFF - p_264->loop_filter_alpha) + 1;
+	} else {
+		reg = 0x00;
+		reg |= (p_264->loop_filter_alpha & 0xF);
+	}
+	mfc_write(dev, reg, S5P_FIMV_ENC_ALPHA_OFF);
+	/* loopfilter beta offset */
+	if (p_264->loop_filter_beta < 0) {
+		reg = 0x10;
+		reg |= (0xFF - p_264->loop_filter_beta) + 1;
+	} else {
+		reg = 0x00;
+		reg |= (p_264->loop_filter_beta & 0xF);
+	}
+	mfc_write(dev, reg, S5P_FIMV_ENC_BETA_OFF);
+	/* entropy coding mode */
+	if (p_264->entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+		mfc_write(dev, 1, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+	else
+		mfc_write(dev, 0, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+	/* number of ref. picture */
+	reg = mfc_read(dev, S5P_FIMV_ENC_H264_NUM_OF_REF);
+	/* num of ref. pictures of P */
+	reg &= ~(0x3 << 5);
+	reg |= (p_264->num_ref_pic_4p << 5);
+	/* max number of ref. pictures */
+	reg &= ~(0x1F);
+	reg |= p_264->max_ref_pic;
+	mfc_write(dev, reg, S5P_FIMV_ENC_H264_NUM_OF_REF);
+	/* 8x8 transform enable */
+	mfc_write(dev, p_264->_8x8_transform, S5P_FIMV_ENC_H264_TRANS_FLAG);
+	/* rate control config. */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+	/* macroblock level rate control */
+	reg &= ~(0x1 << 8);
+	reg |= (p_264->rc_mb << 8);
+	/* frame QP */
+	reg &= ~(0x3F);
+	reg |= p_264->rc_frame_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+	/* frame rate */
+	if (p->rc_frame && p->rc_framerate_denom)
+		mfc_write(dev, p->rc_framerate_num * 1000
+			/ p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+	else
+		mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+	/* max & min value of QP */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+	/* max QP */
+	reg &= ~(0x3F << 8);
+	reg |= (p_264->rc_max_qp << 8);
+	/* min QP */
+	reg &= ~(0x3F);
+	reg |= p_264->rc_min_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+	/* macroblock adaptive scaling features */
+	if (p_264->rc_mb) {
+		reg = mfc_read(dev, S5P_FIMV_ENC_RC_MB_CTRL);
+		/* dark region */
+		reg &= ~(0x1 << 3);
+		reg |= (p_264->rc_mb_dark << 3);
+		/* smooth region */
+		reg &= ~(0x1 << 2);
+		reg |= (p_264->rc_mb_smooth << 2);
+		/* static region */
+		reg &= ~(0x1 << 1);
+		reg |= (p_264->rc_mb_static << 1);
+		/* high activity region */
+		reg &= ~(0x1);
+		reg |= p_264->rc_mb_activity;
+		mfc_write(dev, reg, S5P_FIMV_ENC_RC_MB_CTRL);
+	}
+	if (!p->rc_frame &&
+	    !p_264->rc_mb) {
+		shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+		shm &= ~(0xFFF);
+		shm |= ((p_264->rc_b_frame_qp & 0x3F) << 6);
+		shm |= (p_264->rc_p_frame_qp & 0x3F);
+		s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+	}
+	/* extended encoder ctrl */
+	shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+	/* AR VUI control */
+	shm &= ~(0x1 << 15);
+	shm |= (p_264->vui_sar << 1);
+	s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+	if (p_264->vui_sar) {
+		/* aspect ration IDC */
+		shm = s5p_mfc_read_shm(ctx, SAMPLE_ASPECT_RATIO_IDC);
+		shm &= ~(0xFF);
+		shm |= p_264->vui_sar_idc;
+		s5p_mfc_write_shm(ctx, shm, SAMPLE_ASPECT_RATIO_IDC);
+		if (p_264->vui_sar_idc == 0xFF) {
+			/* sample  AR info */
+			shm = s5p_mfc_read_shm(ctx, EXTENDED_SAR);
+			shm &= ~(0xFFFFFFFF);
+			shm |= p_264->vui_ext_sar_width << 16;
+			shm |= p_264->vui_ext_sar_height;
+			s5p_mfc_write_shm(ctx, shm, EXTENDED_SAR);
+		}
+	}
+	/* intra picture period for H.264 */
+	shm = s5p_mfc_read_shm(ctx, H264_I_PERIOD);
+	/* control */
+	shm &= ~(0x1 << 16);
+	shm |= (p_264->open_gop << 16);
+	/* value */
+	if (p_264->open_gop) {
+		shm &= ~(0xFFFF);
+		shm |= p_264->open_gop_size;
+	}
+	s5p_mfc_write_shm(ctx, shm, H264_I_PERIOD);
+	/* extended encoder ctrl */
+	shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+	/* vbv buffer size */
+	if (p->frame_skip_mode ==
+			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+		shm &= ~(0xFFFF << 16);
+		shm |= (p_264->cpb_size << 16);
+	}
+	s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+	return 0;
+}
+
+static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+	unsigned int reg;
+	unsigned int shm;
+	unsigned int framerate;
+
+	s5p_mfc_set_enc_params(ctx);
+	/* pictype : number of B */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	/* num_b_frame - 0 ~ 2 */
+	reg &= ~(0x3 << 16);
+	reg |= (p->num_b_frame << 16);
+	mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+	/* profile & level */
+	reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+	/* level */
+	reg &= ~(0xFF << 8);
+	reg |= (p_mpeg4->level << 8);
+	/* profile - 0 ~ 2 */
+	reg &= ~(0x3F);
+	reg |= p_mpeg4->profile;
+	mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+	/* quarter_pixel */
+	mfc_write(dev, p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL);
+	/* qp */
+	if (!p->rc_frame) {
+		shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+		shm &= ~(0xFFF);
+		shm |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 6);
+		shm |= (p_mpeg4->rc_p_frame_qp & 0x3F);
+		s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+	}
+	/* frame rate */
+	if (p->rc_frame) {
+		if (p->rc_framerate_denom > 0) {
+			framerate = p->rc_framerate_num * 1000 /
+						p->rc_framerate_denom;
+			mfc_write(dev, framerate,
+				S5P_FIMV_ENC_RC_FRAME_RATE);
+			shm = s5p_mfc_read_shm(ctx, RC_VOP_TIMING);
+			shm &= ~(0xFFFFFFFF);
+			shm |= (1 << 31);
+			shm |= ((p->rc_framerate_num & 0x7FFF) << 16);
+			shm |= (p->rc_framerate_denom & 0xFFFF);
+			s5p_mfc_write_shm(ctx, shm, RC_VOP_TIMING);
+		}
+	} else {
+		mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+	}
+	/* rate control config. */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+	/* frame QP */
+	reg &= ~(0x3F);
+	reg |= p_mpeg4->rc_frame_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+	/* max & min value of QP */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+	/* max QP */
+	reg &= ~(0x3F << 8);
+	reg |= (p_mpeg4->rc_max_qp << 8);
+	/* min QP */
+	reg &= ~(0x3F);
+	reg |= p_mpeg4->rc_min_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+	/* extended encoder ctrl */
+	shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+	/* vbv buffer size */
+	if (p->frame_skip_mode ==
+			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+		shm &= ~(0xFFFF << 16);
+		shm |= (p->vbv_size << 16);
+	}
+	s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+	return 0;
+}
+
+static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_enc_params *p = &ctx->enc_params;
+	struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
+	unsigned int reg;
+	unsigned int shm;
+
+	s5p_mfc_set_enc_params(ctx);
+	/* qp */
+	if (!p->rc_frame) {
+		shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+		shm &= ~(0xFFF);
+		shm |= (p_h263->rc_p_frame_qp & 0x3F);
+		s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+	}
+	/* frame rate */
+	if (p->rc_frame && p->rc_framerate_denom)
+		mfc_write(dev, p->rc_framerate_num * 1000
+			/ p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+	else
+		mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+	/* rate control config. */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+	/* frame QP */
+	reg &= ~(0x3F);
+	reg |= p_h263->rc_frame_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+	/* max & min value of QP */
+	reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+	/* max QP */
+	reg &= ~(0x3F << 8);
+	reg |= (p_h263->rc_max_qp << 8);
+	/* min QP */
+	reg &= ~(0x3F);
+	reg |= p_h263->rc_min_qp;
+	mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+	/* extended encoder ctrl */
+	shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+	/* vbv buffer size */
+	if (p->frame_skip_mode ==
+			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+		shm &= ~(0xFFFF << 16);
+		shm |= (p->vbv_size << 16);
+	}
+	s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+	return 0;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	s5p_mfc_set_shared_buffer(ctx);
+	/* Setup loop filter, for decoding this is only valid for MPEG4 */
+	if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
+		mfc_write(dev, ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
+	else
+		mfc_write(dev, 0, S5P_FIMV_ENC_LF_CTRL);
+	mfc_write(dev, ((ctx->slice_interface & S5P_FIMV_SLICE_INT_MASK) <<
+		S5P_FIMV_SLICE_INT_SHIFT) | (ctx->display_delay_enable <<
+		S5P_FIMV_DDELAY_ENA_SHIFT) | ((ctx->display_delay &
+		S5P_FIMV_DDELAY_VAL_MASK) << S5P_FIMV_DDELAY_VAL_SHIFT),
+		S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+	mfc_write(dev,
+	((S5P_FIMV_CH_SEQ_HEADER & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
+				| (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+	return 0;
+}
+
+static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned int dpb;
+
+	if (flush)
+		dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (
+			S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+	else
+		dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+			~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+	mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+					enum s5p_mfc_decode_arg last_frame)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	mfc_write(dev, ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
+	s5p_mfc_set_shared_buffer(ctx);
+	s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
+	/* Issue different commands to instance basing on whether it
+	 * is the last frame or not. */
+	switch (last_frame) {
+	case MFC_DEC_FRAME:
+		mfc_write(dev, ((S5P_FIMV_CH_FRAME_START & S5P_FIMV_CH_MASK) <<
+		S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+		break;
+	case MFC_DEC_LAST_FRAME:
+		mfc_write(dev, ((S5P_FIMV_CH_LAST_FRAME & S5P_FIMV_CH_MASK) <<
+		S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+		break;
+	case MFC_DEC_RES_CHANGE:
+		mfc_write(dev, ((S5P_FIMV_CH_FRAME_START_REALLOC &
+		S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+		S5P_FIMV_SI_CH0_INST_ID);
+		break;
+	}
+	mfc_debug(2, "Decoding a usual frame\n");
+	return 0;
+}
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+		s5p_mfc_set_enc_params_h264(ctx);
+	else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
+		s5p_mfc_set_enc_params_mpeg4(ctx);
+	else if (ctx->codec_mode == S5P_FIMV_CODEC_H263_ENC)
+		s5p_mfc_set_enc_params_h263(ctx);
+	else {
+		mfc_err("Unknown codec for encoding (%x)\n",
+			ctx->codec_mode);
+		return -EINVAL;
+	}
+	s5p_mfc_set_shared_buffer(ctx);
+	mfc_write(dev, ((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) |
+		(ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+	return 0;
+}
+
+/* Encode a single frame */
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	/* memory structure cur. frame */
+	if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+		mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+	else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+		mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+	s5p_mfc_set_shared_buffer(ctx);
+	mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
+		(ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+	return 0;
+}
+
+static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+	unsigned long flags;
+	int new_ctx;
+	int cnt;
+
+	spin_lock_irqsave(&dev->condlock, flags);
+	new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+	cnt = 0;
+	while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
+		new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
+		if (++cnt > MFC_NUM_CONTEXTS) {
+			/* No contexts to run */
+			spin_unlock_irqrestore(&dev->condlock, flags);
+			return -EAGAIN;
+		}
+	}
+	spin_unlock_irqrestore(&dev->condlock, flags);
+	return new_ctx;
+}
+
+static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+
+	s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	s5p_mfc_decode_one_frame(ctx, MFC_DEC_RES_CHANGE);
+}
+
+static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	struct s5p_mfc_buf *temp_vb;
+	unsigned long flags;
+	unsigned int index;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	/* Frames are being decoded */
+	if (list_empty(&ctx->src_queue)) {
+		mfc_debug(2, "No src buffers\n");
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		return -EAGAIN;
+	}
+	/* Get the next source buffer */
+	temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	temp_vb->used = 1;
+	s5p_mfc_set_dec_stream_buffer(ctx,
+		vb2_dma_contig_plane_paddr(temp_vb->b, 0), ctx->consumed_stream,
+					temp_vb->b->v4l2_planes[0].bytesused);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	index = temp_vb->b->v4l2_buf.index;
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
+		last_frame = MFC_DEC_LAST_FRAME;
+		mfc_debug(2, "Setting ctx->state to FINISHING\n");
+		ctx->state = MFCINST_FINISHING;
+	}
+	s5p_mfc_decode_one_frame(ctx, last_frame);
+	return 0;
+}
+
+static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *dst_mb;
+	struct s5p_mfc_buf *src_mb;
+	unsigned long src_y_addr, src_c_addr, dst_addr;
+	unsigned int dst_size;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	if (list_empty(&ctx->src_queue)) {
+		mfc_debug(2, "no src buffers\n");
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		return -EAGAIN;
+	}
+	if (list_empty(&ctx->dst_queue)) {
+		mfc_debug(2, "no dst buffers\n");
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		return -EAGAIN;
+	}
+	src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	src_mb->used = 1;
+	src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0);
+	src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1);
+	s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+	dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+	dst_mb->used = 1;
+	dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+	dst_size = vb2_plane_size(dst_mb->b, 0);
+	s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	s5p_mfc_encode_one_frame(ctx);
+	return 0;
+}
+
+static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *temp_vb;
+
+	/* Initializing decoding - parsing header */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	mfc_debug(2, "Preparing to init decoding\n");
+	temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	s5p_mfc_set_dec_desc_buffer(ctx);
+	mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+	s5p_mfc_set_dec_stream_buffer(ctx,
+				vb2_dma_contig_plane_paddr(temp_vb->b, 0),
+				0, temp_vb->b->v4l2_planes[0].bytesused);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	s5p_mfc_init_decode(ctx);
+}
+
+static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *dst_mb;
+	unsigned long dst_addr;
+	unsigned int dst_size;
+
+	s5p_mfc_set_enc_ref_buffer(ctx);
+	spin_lock_irqsave(&dev->irqlock, flags);
+	dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+	dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0);
+	dst_size = vb2_plane_size(dst_mb->b, 0);
+	s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	s5p_mfc_init_encode(ctx);
+}
+
+static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	unsigned long flags;
+	struct s5p_mfc_buf *temp_vb;
+	int ret;
+
+	/*
+	 * Header was parsed now starting processing
+	 * First set the output frame buffers
+	 */
+	if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
+		mfc_err("It seems that not all destionation buffers were "
+			"mmaped\nMFC requires that all destination are mmaped "
+			"before starting processing\n");
+		return -EAGAIN;
+	}
+	spin_lock_irqsave(&dev->irqlock, flags);
+	if (list_empty(&ctx->src_queue)) {
+		mfc_err("Header has been deallocated in the middle of"
+			" initialization\n");
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		return -EIO;
+	}
+	temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+	mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+	s5p_mfc_set_dec_stream_buffer(ctx,
+				vb2_dma_contig_plane_paddr(temp_vb->b, 0),
+				0, temp_vb->b->v4l2_planes[0].bytesused);
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	dev->curr_ctx = ctx->num;
+	s5p_mfc_clean_ctx_int_flags(ctx);
+	ret = s5p_mfc_set_dec_frame_buffer(ctx);
+	if (ret) {
+		mfc_err("Failed to alloc frame mem\n");
+		ctx->state = MFCINST_ERROR;
+	}
+	return ret;
+}
+
+/* Try running an operation on hardware */
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
+{
+	struct s5p_mfc_ctx *ctx;
+	int new_ctx;
+	unsigned int ret = 0;
+
+	if (test_bit(0, &dev->enter_suspend)) {
+		mfc_debug(1, "Entering suspend so do not schedule any jobs\n");
+		return;
+	}
+	/* Check whether hardware is not running */
+	if (test_and_set_bit(0, &dev->hw_lock) != 0) {
+		/* This is perfectly ok, the scheduled ctx should wait */
+		mfc_debug(1, "Couldn't lock HW\n");
+		return;
+	}
+	/* Choose the context to run */
+	new_ctx = s5p_mfc_get_new_ctx(dev);
+	if (new_ctx < 0) {
+		/* No contexts to run */
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+			mfc_err("Failed to unlock hardware\n");
+			return;
+		}
+		mfc_debug(1, "No ctx is scheduled to be run\n");
+		return;
+	}
+	ctx = dev->ctx[new_ctx];
+	/* Got context to run in ctx */
+	/*
+	 * Last frame has already been sent to MFC.
+	 * Now obtaining frames from MFC buffer
+	 */
+	s5p_mfc_clock_on();
+	if (ctx->type == MFCINST_DECODER) {
+		s5p_mfc_set_dec_desc_buffer(ctx);
+		switch (ctx->state) {
+		case MFCINST_FINISHING:
+			s5p_mfc_run_dec_frame(ctx, MFC_DEC_LAST_FRAME);
+			break;
+		case MFCINST_RUNNING:
+			ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+			break;
+		case MFCINST_INIT:
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			ret = s5p_mfc_open_inst_cmd(ctx);
+			break;
+		case MFCINST_RETURN_INST:
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			ret = s5p_mfc_close_inst_cmd(ctx);
+			break;
+		case MFCINST_GOT_INST:
+			s5p_mfc_run_init_dec(ctx);
+			break;
+		case MFCINST_HEAD_PARSED:
+			ret = s5p_mfc_run_init_dec_buffers(ctx);
+			mfc_debug(1, "head parsed\n");
+			break;
+		case MFCINST_RES_CHANGE_INIT:
+			s5p_mfc_run_res_change(ctx);
+			break;
+		case MFCINST_RES_CHANGE_FLUSH:
+			s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+			break;
+		case MFCINST_RES_CHANGE_END:
+			mfc_debug(2, "Finished remaining frames after resolution change\n");
+			ctx->capture_state = QUEUE_FREE;
+			mfc_debug(2, "Will re-init the codec\n");
+			s5p_mfc_run_init_dec(ctx);
+			break;
+		default:
+			ret = -EAGAIN;
+		}
+	} else if (ctx->type == MFCINST_ENCODER) {
+		switch (ctx->state) {
+		case MFCINST_FINISHING:
+		case MFCINST_RUNNING:
+			ret = s5p_mfc_run_enc_frame(ctx);
+			break;
+		case MFCINST_INIT:
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			ret = s5p_mfc_open_inst_cmd(ctx);
+			break;
+		case MFCINST_RETURN_INST:
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			ret = s5p_mfc_close_inst_cmd(ctx);
+			break;
+		case MFCINST_GOT_INST:
+			s5p_mfc_run_init_enc(ctx);
+			break;
+		default:
+			ret = -EAGAIN;
+		}
+	} else {
+		mfc_err("Invalid context type: %d\n", ctx->type);
+		ret = -EAGAIN;
+	}
+
+	if (ret) {
+		/* Free hardware lock */
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			mfc_err("Failed to unlock hardware\n");
+
+		/* This is in deed imporant, as no operation has been
+		 * scheduled, reduce the clock count as no one will
+		 * ever do this, because no interrupt related to this try_run
+		 * will ever come from hardware. */
+		s5p_mfc_clock_off();
+	}
+}
+
+
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
+{
+	struct s5p_mfc_buf *b;
+	int i;
+
+	while (!list_empty(lh)) {
+		b = list_entry(lh->next, struct s5p_mfc_buf, list);
+		for (i = 0; i < b->b->num_planes; i++)
+			vb2_set_plane_payload(b->b, i, 0);
+		vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR);
+		list_del(&b->list);
+	}
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
new file mode 100644
index 0000000..db83836
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
@@ -0,0 +1,91 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains declarations of hw related functions.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_OPR_H_
+#define S5P_MFC_OPR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Decoding functions */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+						  unsigned int start_num_byte,
+						  unsigned int buf_size);
+
+/* Encoding functions */
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long y_addr, unsigned long c_addr);
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long addr, unsigned int size);
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+		unsigned long *y_addr, unsigned long *c_addr);
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+					enum s5p_mfc_decode_arg last_frame);
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Memory allocation */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx);
+
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
+
+#define s5p_mfc_get_dspl_y_adr()	(readl(dev->regs_base + \
+					S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+					MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dec_y_adr()		(readl(dev->regs_base + \
+					S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+					MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dspl_status()	readl(dev->regs_base + \
+						S5P_FIMV_SI_DISPLAY_STATUS)
+#define s5p_mfc_get_frame_type()	(readl(dev->regs_base + \
+						S5P_FIMV_DECODE_FRAME_TYPE) \
+					& S5P_FIMV_DECODE_FRAME_MASK)
+#define s5p_mfc_get_consumed_stream()	readl(dev->regs_base + \
+						S5P_FIMV_SI_CONSUMED_BYTES)
+#define s5p_mfc_get_int_reason()	(readl(dev->regs_base + \
+					S5P_FIMV_RISC2HOST_CMD) & \
+					S5P_FIMV_RISC2HOST_CMD_MASK)
+#define s5p_mfc_get_int_err()		readl(dev->regs_base + \
+						S5P_FIMV_RISC2HOST_ARG2)
+#define s5p_mfc_err_dec(x)		(((x) & S5P_FIMV_ERR_DEC_MASK) >> \
+							S5P_FIMV_ERR_DEC_SHIFT)
+#define s5p_mfc_err_dspl(x)		(((x) & S5P_FIMV_ERR_DSPL_MASK) >> \
+							S5P_FIMV_ERR_DSPL_SHIFT)
+#define s5p_mfc_get_img_width()		readl(dev->regs_base + \
+						S5P_FIMV_SI_HRESOL)
+#define s5p_mfc_get_img_height()	readl(dev->regs_base + \
+						S5P_FIMV_SI_VRESOL)
+#define s5p_mfc_get_dpb_count()		readl(dev->regs_base + \
+						S5P_FIMV_SI_BUF_NUMBER)
+#define s5p_mfc_get_inst_no()		readl(dev->regs_base + \
+						S5P_FIMV_RISC2HOST_ARG1)
+#define s5p_mfc_get_enc_strm_size()	readl(dev->regs_base + \
+						S5P_FIMV_ENC_SI_STRM_SIZE)
+#define s5p_mfc_get_enc_slice_type()	readl(dev->regs_base + \
+						S5P_FIMV_ENC_SI_SLICE_TYPE)
+
+#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
new file mode 100644
index 0000000..f6a3035
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
@@ -0,0 +1,117 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_pm.h"
+
+#define MFC_CLKNAME		"sclk_mfc"
+#define MFC_GATE_CLK_NAME	"mfc"
+
+#define CLK_DEBUG
+
+static struct s5p_mfc_pm *pm;
+static struct s5p_mfc_dev *p_dev;
+
+#ifdef CLK_DEBUG
+atomic_t clk_ref;
+#endif
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
+{
+	int ret = 0;
+
+	pm = &dev->pm;
+	p_dev = dev;
+	pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
+	if (IS_ERR(pm->clock_gate)) {
+		mfc_err("Failed to get clock-gating control\n");
+		ret = -ENOENT;
+		goto err_g_ip_clk;
+	}
+	pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
+	if (IS_ERR(pm->clock)) {
+		mfc_err("Failed to get MFC clock\n");
+		ret = -ENOENT;
+		goto err_g_ip_clk_2;
+	}
+	atomic_set(&pm->power, 0);
+#ifdef CONFIG_PM_RUNTIME
+	pm->device = &dev->plat_dev->dev;
+	pm_runtime_enable(pm->device);
+#endif
+#ifdef CLK_DEBUG
+	atomic_set(&clk_ref, 0);
+#endif
+	return 0;
+err_g_ip_clk_2:
+	clk_put(pm->clock_gate);
+err_g_ip_clk:
+	return ret;
+}
+
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
+{
+	clk_put(pm->clock_gate);
+	clk_put(pm->clock);
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_disable(pm->device);
+#endif
+}
+
+int s5p_mfc_clock_on(void)
+{
+	int ret;
+#ifdef CLK_DEBUG
+	atomic_inc(&clk_ref);
+	mfc_debug(3, "+ %d", atomic_read(&clk_ref));
+#endif
+	ret = clk_enable(pm->clock_gate);
+	return ret;
+}
+
+void s5p_mfc_clock_off(void)
+{
+#ifdef CLK_DEBUG
+	atomic_dec(&clk_ref);
+	mfc_debug(3, "- %d", atomic_read(&clk_ref));
+#endif
+	clk_disable(pm->clock_gate);
+}
+
+int s5p_mfc_power_on(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+	return pm_runtime_get_sync(pm->device);
+#else
+	atomic_set(&pm->power, 1);
+	return 0;
+#endif
+}
+
+int s5p_mfc_power_off(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+	return pm_runtime_put_sync(pm->device);
+#else
+	atomic_set(&pm->power, 0);
+	return 0;
+#endif
+}
+
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.h b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
new file mode 100644
index 0000000..5107914
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
@@ -0,0 +1,24 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_PM_H_
+#define S5P_MFC_PM_H_
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev);
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_clock_on(void);
+void s5p_mfc_clock_off(void);
+int s5p_mfc_power_on(void);
+int s5p_mfc_power_off(void);
+
+#endif /* S5P_MFC_PM_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.c b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
new file mode 100644
index 0000000..91fdbac8
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
@@ -0,0 +1,47 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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.
+ */
+
+#ifdef CONFIG_ARCH_EXYNOS4
+#include <linux/dma-mapping.h>
+#endif
+#include <linux/io.h>
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx)
+{
+	struct s5p_mfc_dev *dev = ctx->dev;
+	void *shm_alloc_ctx = dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+
+	ctx->shm_alloc = vb2_dma_contig_memops.alloc(shm_alloc_ctx,
+							SHARED_BUF_SIZE);
+	if (IS_ERR(ctx->shm_alloc)) {
+		mfc_err("failed to allocate shared memory\n");
+		return PTR_ERR(ctx->shm_alloc);
+	}
+	/* shm_ofs only keeps the offset from base (port a) */
+	ctx->shm_ofs = s5p_mfc_mem_cookie(shm_alloc_ctx, ctx->shm_alloc)
+								- dev->bank1;
+	BUG_ON(ctx->shm_ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+	ctx->shm = vb2_dma_contig_memops.vaddr(ctx->shm_alloc);
+	if (!ctx->shm) {
+		vb2_dma_contig_memops.put(ctx->shm_alloc);
+		ctx->shm_ofs = 0;
+		ctx->shm_alloc = NULL;
+		mfc_err("failed to virt addr of shared memory\n");
+		return -ENOMEM;
+	}
+	memset((void *)ctx->shm, 0, SHARED_BUF_SIZE);
+	wmb();
+	return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.h b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
new file mode 100644
index 0000000..764eac6
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
@@ -0,0 +1,91 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.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 S5P_MFC_SHM_H_
+#define S5P_MFC_SHM_H_
+
+enum MFC_SHM_OFS
+{
+	EXTENEDED_DECODE_STATUS	= 0x00,	/* D */
+	SET_FRAME_TAG		= 0x04, /* D */
+	GET_FRAME_TAG_TOP	= 0x08, /* D */
+	GET_FRAME_TAG_BOT	= 0x0C, /* D */
+	PIC_TIME_TOP		= 0x10, /* D */
+	PIC_TIME_BOT		= 0x14, /* D */
+	START_BYTE_NUM		= 0x18, /* D */
+
+	CROP_INFO_H		= 0x20, /* D */
+	CROP_INFO_V		= 0x24, /* D */
+	EXT_ENC_CONTROL		= 0x28,	/* E */
+	ENC_PARAM_CHANGE	= 0x2C,	/* E */
+	RC_VOP_TIMING		= 0x30,	/* E, MPEG4 */
+	HEC_PERIOD		= 0x34,	/* E, MPEG4 */
+	METADATA_ENABLE		= 0x38, /* C */
+	METADATA_STATUS		= 0x3C, /* C */
+	METADATA_DISPLAY_INDEX	= 0x40,	/* C */
+	EXT_METADATA_START_ADDR	= 0x44, /* C */
+	PUT_EXTRADATA		= 0x48, /* C */
+	EXTRADATA_ADDR		= 0x4C, /* C */
+
+	ALLOC_LUMA_DPB_SIZE	= 0x64,	/* D */
+	ALLOC_CHROMA_DPB_SIZE	= 0x68,	/* D */
+	ALLOC_MV_SIZE		= 0x6C,	/* D */
+	P_B_FRAME_QP		= 0x70,	/* E */
+	SAMPLE_ASPECT_RATIO_IDC	= 0x74, /* E, H.264, depend on
+				ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+	EXTENDED_SAR		= 0x78, /* E, H.264, depned on
+				ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+	DISP_PIC_PROFILE	= 0x7C, /* D */
+	FLUSH_CMD_TYPE		= 0x80, /* C */
+	FLUSH_CMD_INBUF1	= 0x84, /* C */
+	FLUSH_CMD_INBUF2	= 0x88, /* C */
+	FLUSH_CMD_OUTBUF	= 0x8C, /* E */
+	NEW_RC_BIT_RATE		= 0x90, /* E, format as RC_BIT_RATE(0xC5A8)
+			depend on RC_BIT_RATE_CHANGE in ENC_PARAM_CHANGE */
+	NEW_RC_FRAME_RATE	= 0x94, /* E, format as RC_FRAME_RATE(0xD0D0)
+			depend on RC_FRAME_RATE_CHANGE in ENC_PARAM_CHANGE */
+	NEW_I_PERIOD		= 0x98, /* E, format as I_FRM_CTRL(0xC504)
+			depend on I_PERIOD_CHANGE in ENC_PARAM_CHANGE */
+	H264_I_PERIOD		= 0x9C, /* E, H.264, open GOP */
+	RC_CONTROL_CONFIG	= 0xA0, /* E */
+	BATCH_INPUT_ADDR	= 0xA4, /* E */
+	BATCH_OUTPUT_ADDR	= 0xA8, /* E */
+	BATCH_OUTPUT_SIZE	= 0xAC, /* E */
+	MIN_LUMA_DPB_SIZE	= 0xB0, /* D */
+	DEVICE_FORMAT_ID	= 0xB4, /* C */
+	H264_POC_TYPE		= 0xB8, /* D */
+	MIN_CHROMA_DPB_SIZE	= 0xBC, /* D */
+	DISP_PIC_FRAME_TYPE	= 0xC0, /* D */
+	FREE_LUMA_DPB		= 0xC4, /* D, VC1 MPEG4 */
+	ASPECT_RATIO_INFO	= 0xC8, /* D, MPEG4 */
+	EXTENDED_PAR		= 0xCC, /* D, MPEG4 */
+	DBG_HISTORY_INPUT0	= 0xD0, /* C */
+	DBG_HISTORY_INPUT1	= 0xD4,	/* C */
+	DBG_HISTORY_OUTPUT	= 0xD8,	/* C */
+	HIERARCHICAL_P_QP	= 0xE0, /* E, H.264 */
+};
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx);
+
+#define s5p_mfc_write_shm(ctx, x, ofs)		\
+	do {					\
+		writel(x, (ctx->shm + ofs));	\
+		wmb();				\
+	} while (0)
+
+static inline u32 s5p_mfc_read_shm(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+{
+	rmb();
+	return readl(ctx->shm + ofs);
+}
+
+#endif /* S5P_MFC_SHM_H_ */
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
new file mode 100644
index 0000000..9c37dee
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -0,0 +1,76 @@
+# drivers/media/video/s5p-tv/Kconfig
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+#	http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+config VIDEO_SAMSUNG_S5P_TV
+	bool "Samsung TV driver for S5P platform (experimental)"
+	depends on PLAT_S5P
+	depends on EXPERIMENTAL
+	default n
+	---help---
+	  Say Y here to enable selecting the TV output devices for
+	  Samsung S5P platform.
+
+if VIDEO_SAMSUNG_S5P_TV
+
+config VIDEO_SAMSUNG_S5P_HDMI
+	tristate "Samsung HDMI Driver"
+	depends on VIDEO_V4L2
+	depends on VIDEO_SAMSUNG_S5P_TV
+	select VIDEO_SAMSUNG_S5P_HDMIPHY
+	help
+	  Say Y here if you want support for the HDMI output
+	  interface in S5P Samsung SoC. The driver can be compiled
+	  as module. It is an auxiliary driver, that exposes a V4L2
+	  subdev for use by other drivers. This driver requires
+	  hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+	bool "Enable debug for HDMI Driver"
+	depends on VIDEO_SAMSUNG_S5P_HDMI
+	default n
+	help
+	  Enables debugging for HDMI driver.
+
+config VIDEO_SAMSUNG_S5P_HDMIPHY
+	tristate "Samsung HDMIPHY Driver"
+	depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+	depends on VIDEO_SAMSUNG_S5P_TV
+	help
+	  Say Y here if you want support for the physical HDMI
+	  interface in S5P Samsung SoC. The driver can be compiled
+	  as module. It is an I2C driver, that exposes a V4L2
+	  subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SDO
+	tristate "Samsung Analog TV Driver"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on VIDEO_SAMSUNG_S5P_TV
+	help
+	  Say Y here if you want support for the analog TV output
+	  interface in S5P Samsung SoC. The driver can be compiled
+	  as module. It is an auxiliary driver, that exposes a V4L2
+	  subdev for use by other drivers. This driver requires
+	  hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_MIXER
+	tristate "Samsung Mixer and Video Processor Driver"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on VIDEO_SAMSUNG_S5P_TV
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	  Say Y here if you want support for the Mixer in Samsung S5P SoCs.
+	  This device produce image data to one of output interfaces.
+
+config VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+	bool "Enable debug for Mixer Driver"
+	depends on VIDEO_SAMSUNG_S5P_MIXER
+	default n
+	help
+	  Enables debugging for Mixer driver.
+
+endif # VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
new file mode 100644
index 0000000..37e4c17
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Makefile
@@ -0,0 +1,17 @@
+# drivers/media/video/samsung/tvout/Makefile
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+#	http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
+s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
+s5p-hdmi-y += hdmi_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
+s5p-sdo-y += sdo_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o
+s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o
+
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
new file mode 100644
index 0000000..06d6663
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -0,0 +1,1042 @@
+/*
+ * Samsung HDMI interface driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#include "regs-hdmi.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI");
+MODULE_LICENSE("GPL");
+
+/* default preset configured on probe */
+#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60
+
+struct hdmi_resources {
+	struct clk *hdmi;
+	struct clk *sclk_hdmi;
+	struct clk *sclk_pixel;
+	struct clk *sclk_hdmiphy;
+	struct clk *hdmiphy;
+	struct regulator_bulk_data *regul_bulk;
+	int regul_count;
+};
+
+struct hdmi_device {
+	/** base address of HDMI registers */
+	void __iomem *regs;
+	/** HDMI interrupt */
+	unsigned int irq;
+	/** pointer to device parent */
+	struct device *dev;
+	/** subdev generated by HDMI device */
+	struct v4l2_subdev sd;
+	/** V4L2 device structure */
+	struct v4l2_device v4l2_dev;
+	/** subdev of HDMIPHY interface */
+	struct v4l2_subdev *phy_sd;
+	/** configuration of current graphic mode */
+	const struct hdmi_preset_conf *cur_conf;
+	/** current preset */
+	u32 cur_preset;
+	/** other resources */
+	struct hdmi_resources res;
+};
+
+struct hdmi_driver_data {
+	int hdmiphy_bus;
+};
+
+struct hdmi_tg_regs {
+	u8 cmd;
+	u8 h_fsz_l;
+	u8 h_fsz_h;
+	u8 hact_st_l;
+	u8 hact_st_h;
+	u8 hact_sz_l;
+	u8 hact_sz_h;
+	u8 v_fsz_l;
+	u8 v_fsz_h;
+	u8 vsync_l;
+	u8 vsync_h;
+	u8 vsync2_l;
+	u8 vsync2_h;
+	u8 vact_st_l;
+	u8 vact_st_h;
+	u8 vact_sz_l;
+	u8 vact_sz_h;
+	u8 field_chg_l;
+	u8 field_chg_h;
+	u8 vact_st2_l;
+	u8 vact_st2_h;
+	u8 vsync_top_hdmi_l;
+	u8 vsync_top_hdmi_h;
+	u8 vsync_bot_hdmi_l;
+	u8 vsync_bot_hdmi_h;
+	u8 field_top_hdmi_l;
+	u8 field_top_hdmi_h;
+	u8 field_bot_hdmi_l;
+	u8 field_bot_hdmi_h;
+};
+
+struct hdmi_core_regs {
+	u8 h_blank[2];
+	u8 v_blank[3];
+	u8 h_v_line[3];
+	u8 vsync_pol[1];
+	u8 int_pro_mode[1];
+	u8 v_blank_f[3];
+	u8 h_sync_gen[3];
+	u8 v_sync_gen1[3];
+	u8 v_sync_gen2[3];
+	u8 v_sync_gen3[3];
+};
+
+struct hdmi_preset_conf {
+	struct hdmi_core_regs core;
+	struct hdmi_tg_regs tg;
+	struct v4l2_mbus_framefmt mbus_fmt;
+};
+
+/* I2C module and id for HDMIPHY */
+static struct i2c_board_info hdmiphy_info = {
+	I2C_BOARD_INFO("hdmiphy", 0x38),
+};
+
+static struct hdmi_driver_data hdmi_driver_data[] = {
+	{ .hdmiphy_bus = 3 },
+	{ .hdmiphy_bus = 8 },
+};
+
+static struct platform_device_id hdmi_driver_types[] = {
+	{
+		.name		= "s5pv210-hdmi",
+		.driver_data	= (unsigned long)&hdmi_driver_data[0],
+	}, {
+		.name		= "exynos4-hdmi",
+		.driver_data	= (unsigned long)&hdmi_driver_data[1],
+	}, {
+		/* end node */
+	}
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops;
+
+static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct hdmi_device, sd);
+}
+
+static inline
+void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
+{
+	writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
+{
+	u32 old = readl(hdev->regs + reg_id);
+	value = (value & mask) | (old & ~mask);
+	writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
+{
+	writeb(value, hdev->regs + reg_id);
+}
+
+static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
+{
+	return readl(hdev->regs + reg_id);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
+{
+	struct hdmi_device *hdev = dev_data;
+	u32 intc_flag;
+
+	(void)irq;
+	intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG);
+	/* clearing flags for HPD plug/unplug */
+	if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+		printk(KERN_INFO "unplugged\n");
+		hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+			HDMI_INTC_FLAG_HPD_UNPLUG);
+	}
+	if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+		printk(KERN_INFO "plugged\n");
+		hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+			HDMI_INTC_FLAG_HPD_PLUG);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void hdmi_reg_init(struct hdmi_device *hdev)
+{
+	/* enable HPD interrupts */
+	hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
+		HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+	/* choose HDMI mode */
+	hdmi_write_mask(hdev, HDMI_MODE_SEL,
+		HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
+	/* disable bluescreen */
+	hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+	/* choose bluescreen (fecal) color */
+	hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
+	hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
+	hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
+	/* enable AVI packet every vsync, fixes purple line problem */
+	hdmi_writeb(hdev, HDMI_AVI_CON, 0x02);
+	/* force YUV444, look to CEA-861-D, table 7 for more detail */
+	hdmi_writeb(hdev, HDMI_AVI_BYTE(0), 2 << 5);
+	hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5);
+}
+
+static void hdmi_timing_apply(struct hdmi_device *hdev,
+	const struct hdmi_preset_conf *conf)
+{
+	const struct hdmi_core_regs *core = &conf->core;
+	const struct hdmi_tg_regs *tg = &conf->tg;
+
+	/* setting core registers */
+	hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]);
+	hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]);
+	hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]);
+	hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]);
+	hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]);
+	hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]);
+	hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
+	hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
+	hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
+	hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
+	hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
+	hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+	/* Timing generator registers */
+	hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
+	hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
+	hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l);
+	hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h);
+	hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
+	hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
+	hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
+	hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h);
+	hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l);
+	hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h);
+	hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
+	hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
+	hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
+	hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
+	hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
+	hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+}
+
+static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
+{
+	struct device *dev = hdmi_dev->dev;
+	const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf;
+	struct v4l2_dv_preset preset;
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	/* reset hdmiphy */
+	hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+	mdelay(10);
+	hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT,  0, HDMI_PHY_SW_RSTOUT);
+	mdelay(10);
+
+	/* configure presets */
+	preset.preset = hdmi_dev->cur_preset;
+	ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
+	if (ret) {
+		dev_err(dev, "failed to set preset (%u)\n", preset.preset);
+		return ret;
+	}
+
+	/* resetting HDMI core */
+	hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT,  0, HDMI_CORE_SW_RSTOUT);
+	mdelay(10);
+	hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+	mdelay(10);
+
+	hdmi_reg_init(hdmi_dev);
+
+	/* setting core registers */
+	hdmi_timing_apply(hdmi_dev, conf);
+
+	return 0;
+}
+
+static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
+{
+#define DUMPREG(reg_id) \
+	dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \
+		readl(hdev->regs + reg_id))
+
+	dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix);
+	DUMPREG(HDMI_INTC_FLAG);
+	DUMPREG(HDMI_INTC_CON);
+	DUMPREG(HDMI_HPD_STATUS);
+	DUMPREG(HDMI_PHY_RSTOUT);
+	DUMPREG(HDMI_PHY_VPLL);
+	DUMPREG(HDMI_PHY_CMU);
+	DUMPREG(HDMI_CORE_RSTOUT);
+
+	dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix);
+	DUMPREG(HDMI_CON_0);
+	DUMPREG(HDMI_CON_1);
+	DUMPREG(HDMI_CON_2);
+	DUMPREG(HDMI_SYS_STATUS);
+	DUMPREG(HDMI_PHY_STATUS);
+	DUMPREG(HDMI_STATUS_EN);
+	DUMPREG(HDMI_HPD);
+	DUMPREG(HDMI_MODE_SEL);
+	DUMPREG(HDMI_HPD_GEN);
+	DUMPREG(HDMI_DC_CONTROL);
+	DUMPREG(HDMI_VIDEO_PATTERN_GEN);
+
+	dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+	DUMPREG(HDMI_H_BLANK_0);
+	DUMPREG(HDMI_H_BLANK_1);
+	DUMPREG(HDMI_V_BLANK_0);
+	DUMPREG(HDMI_V_BLANK_1);
+	DUMPREG(HDMI_V_BLANK_2);
+	DUMPREG(HDMI_H_V_LINE_0);
+	DUMPREG(HDMI_H_V_LINE_1);
+	DUMPREG(HDMI_H_V_LINE_2);
+	DUMPREG(HDMI_VSYNC_POL);
+	DUMPREG(HDMI_INT_PRO_MODE);
+	DUMPREG(HDMI_V_BLANK_F_0);
+	DUMPREG(HDMI_V_BLANK_F_1);
+	DUMPREG(HDMI_V_BLANK_F_2);
+	DUMPREG(HDMI_H_SYNC_GEN_0);
+	DUMPREG(HDMI_H_SYNC_GEN_1);
+	DUMPREG(HDMI_H_SYNC_GEN_2);
+	DUMPREG(HDMI_V_SYNC_GEN_1_0);
+	DUMPREG(HDMI_V_SYNC_GEN_1_1);
+	DUMPREG(HDMI_V_SYNC_GEN_1_2);
+	DUMPREG(HDMI_V_SYNC_GEN_2_0);
+	DUMPREG(HDMI_V_SYNC_GEN_2_1);
+	DUMPREG(HDMI_V_SYNC_GEN_2_2);
+	DUMPREG(HDMI_V_SYNC_GEN_3_0);
+	DUMPREG(HDMI_V_SYNC_GEN_3_1);
+	DUMPREG(HDMI_V_SYNC_GEN_3_2);
+
+	dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix);
+	DUMPREG(HDMI_TG_CMD);
+	DUMPREG(HDMI_TG_H_FSZ_L);
+	DUMPREG(HDMI_TG_H_FSZ_H);
+	DUMPREG(HDMI_TG_HACT_ST_L);
+	DUMPREG(HDMI_TG_HACT_ST_H);
+	DUMPREG(HDMI_TG_HACT_SZ_L);
+	DUMPREG(HDMI_TG_HACT_SZ_H);
+	DUMPREG(HDMI_TG_V_FSZ_L);
+	DUMPREG(HDMI_TG_V_FSZ_H);
+	DUMPREG(HDMI_TG_VSYNC_L);
+	DUMPREG(HDMI_TG_VSYNC_H);
+	DUMPREG(HDMI_TG_VSYNC2_L);
+	DUMPREG(HDMI_TG_VSYNC2_H);
+	DUMPREG(HDMI_TG_VACT_ST_L);
+	DUMPREG(HDMI_TG_VACT_ST_H);
+	DUMPREG(HDMI_TG_VACT_SZ_L);
+	DUMPREG(HDMI_TG_VACT_SZ_H);
+	DUMPREG(HDMI_TG_FIELD_CHG_L);
+	DUMPREG(HDMI_TG_FIELD_CHG_H);
+	DUMPREG(HDMI_TG_VACT_ST2_L);
+	DUMPREG(HDMI_TG_VACT_ST2_H);
+	DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+	DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+	DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+	DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+	DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+	DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+	DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+	DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static const struct hdmi_preset_conf hdmi_conf_480p = {
+	.core = {
+		.h_blank = {0x8a, 0x00},
+		.v_blank = {0x0d, 0x6a, 0x01},
+		.h_v_line = {0x0d, 0xa2, 0x35},
+		.vsync_pol = {0x01},
+		.int_pro_mode = {0x00},
+		.v_blank_f = {0x00, 0x00, 0x00},
+		.h_sync_gen = {0x0e, 0x30, 0x11},
+		.v_sync_gen1 = {0x0f, 0x90, 0x00},
+		/* other don't care */
+	},
+	.tg = {
+		0x00, /* cmd */
+		0x5a, 0x03, /* h_fsz */
+		0x8a, 0x00, 0xd0, 0x02, /* hact */
+		0x0d, 0x02, /* v_fsz */
+		0x01, 0x00, 0x33, 0x02, /* vsync */
+		0x2d, 0x00, 0xe0, 0x01, /* vact */
+		0x33, 0x02, /* field_chg */
+		0x49, 0x02, /* vact_st2 */
+		0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+		0x01, 0x00, 0x33, 0x02, /* field top/bot */
+	},
+	.mbus_fmt = {
+		.width = 720,
+		.height = 480,
+		.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+		.field = V4L2_FIELD_NONE,
+	},
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+	.core = {
+		.h_blank = {0x72, 0x01},
+		.v_blank = {0xee, 0xf2, 0x00},
+		.h_v_line = {0xee, 0x22, 0x67},
+		.vsync_pol = {0x00},
+		.int_pro_mode = {0x00},
+		.v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+		.h_sync_gen = {0x6c, 0x50, 0x02},
+		.v_sync_gen1 = {0x0a, 0x50, 0x00},
+		/* other don't care */
+	},
+	.tg = {
+		0x00, /* cmd */
+		0x72, 0x06, /* h_fsz */
+		0x72, 0x01, 0x00, 0x05, /* hact */
+		0xee, 0x02, /* v_fsz */
+		0x01, 0x00, 0x33, 0x02, /* vsync */
+		0x1e, 0x00, 0xd0, 0x02, /* vact */
+		0x33, 0x02, /* field_chg */
+		0x49, 0x02, /* vact_st2 */
+		0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+		0x01, 0x00, 0x33, 0x02, /* field top/bot */
+	},
+	.mbus_fmt = {
+		.width = 1280,
+		.height = 720,
+		.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+		.field = V4L2_FIELD_NONE,
+	},
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+	.core = {
+		.h_blank = {0xd0, 0x02},
+		.v_blank = {0x65, 0x6c, 0x01},
+		.h_v_line = {0x65, 0x04, 0xa5},
+		.vsync_pol = {0x00},
+		.int_pro_mode = {0x00},
+		.v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+		.h_sync_gen = {0x0e, 0xea, 0x08},
+		.v_sync_gen1 = {0x09, 0x40, 0x00},
+		/* other don't care */
+	},
+	.tg = {
+		0x00, /* cmd */
+		0x98, 0x08, /* h_fsz */
+		0x18, 0x01, 0x80, 0x07, /* hact */
+		0x65, 0x04, /* v_fsz */
+		0x01, 0x00, 0x33, 0x02, /* vsync */
+		0x2d, 0x00, 0x38, 0x04, /* vact */
+		0x33, 0x02, /* field_chg */
+		0x49, 0x02, /* vact_st2 */
+		0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+		0x01, 0x00, 0x33, 0x02, /* field top/bot */
+	},
+	.mbus_fmt = {
+		.width = 1920,
+		.height = 1080,
+		.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+		.field = V4L2_FIELD_NONE,
+	},
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+	.core = {
+		.h_blank = {0x18, 0x01},
+		.v_blank = {0x65, 0x6c, 0x01},
+		.h_v_line = {0x65, 0x84, 0x89},
+		.vsync_pol = {0x00},
+		.int_pro_mode = {0x00},
+		.v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+		.h_sync_gen = {0x56, 0x08, 0x02},
+		.v_sync_gen1 = {0x09, 0x40, 0x00},
+		/* other don't care */
+	},
+	.tg = {
+		0x00, /* cmd */
+		0x98, 0x08, /* h_fsz */
+		0x18, 0x01, 0x80, 0x07, /* hact */
+		0x65, 0x04, /* v_fsz */
+		0x01, 0x00, 0x33, 0x02, /* vsync */
+		0x2d, 0x00, 0x38, 0x04, /* vact */
+		0x33, 0x02, /* field_chg */
+		0x48, 0x02, /* vact_st2 */
+		0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+		0x01, 0x00, 0x33, 0x02, /* field top/bot */
+	},
+	.mbus_fmt = {
+		.width = 1920,
+		.height = 1080,
+		.code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+		.field = V4L2_FIELD_NONE,
+	},
+};
+
+static const struct {
+	u32 preset;
+	const struct hdmi_preset_conf *conf;
+} hdmi_conf[] = {
+	{ V4L2_DV_480P59_94, &hdmi_conf_480p },
+	{ V4L2_DV_720P59_94, &hdmi_conf_720p60 },
+	{ V4L2_DV_1080P50, &hdmi_conf_1080p50 },
+	{ V4L2_DV_1080P30, &hdmi_conf_1080p60 },
+	{ V4L2_DV_1080P60, &hdmi_conf_1080p60 },
+};
+
+static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i)
+		if (hdmi_conf[i].preset == preset)
+			return  hdmi_conf[i].conf;
+	return NULL;
+}
+
+static int hdmi_streamon(struct hdmi_device *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hdmi_resources *res = &hdev->res;
+	int ret, tries;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
+	if (ret)
+		return ret;
+
+	/* waiting for HDMIPHY's PLL to get to steady state */
+	for (tries = 100; tries; --tries) {
+		u32 val = hdmi_read(hdev, HDMI_PHY_STATUS);
+		if (val & HDMI_PHY_STATUS_READY)
+			break;
+		mdelay(1);
+	}
+	/* steady state not achieved */
+	if (tries == 0) {
+		dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
+		v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+		hdmi_dumpregs(hdev, "s_stream");
+		return -EIO;
+	}
+
+	/* hdmiphy clock is used for HDMI in streaming mode */
+	clk_disable(res->sclk_hdmi);
+	clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
+	clk_enable(res->sclk_hdmi);
+
+	/* enable HDMI and timing generator */
+	hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN);
+	hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+	hdmi_dumpregs(hdev, "streamon");
+	return 0;
+}
+
+static int hdmi_streamoff(struct hdmi_device *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hdmi_resources *res = &hdev->res;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN);
+	hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN);
+
+	/* pixel(vpll) clock is used for HDMI in config mode */
+	clk_disable(res->sclk_hdmi);
+	clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+	clk_enable(res->sclk_hdmi);
+
+	v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+
+	hdmi_dumpregs(hdev, "streamoff");
+	return 0;
+}
+
+static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	struct device *dev = hdev->dev;
+
+	dev_dbg(dev, "%s(%d)\n", __func__, enable);
+	if (enable)
+		return hdmi_streamon(hdev);
+	return hdmi_streamoff(hdev);
+}
+
+static void hdmi_resource_poweron(struct hdmi_resources *res)
+{
+	/* turn HDMI power on */
+	regulator_bulk_enable(res->regul_count, res->regul_bulk);
+	/* power-on hdmi physical interface */
+	clk_enable(res->hdmiphy);
+	/* use VPP as parent clock; HDMIPHY is not working yet */
+	clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+	/* turn clocks on */
+	clk_enable(res->sclk_hdmi);
+}
+
+static void hdmi_resource_poweroff(struct hdmi_resources *res)
+{
+	/* turn clocks off */
+	clk_disable(res->sclk_hdmi);
+	/* power-off hdmiphy */
+	clk_disable(res->hdmiphy);
+	/* turn HDMI power off */
+	regulator_bulk_disable(res->regul_count, res->regul_bulk);
+}
+
+static int hdmi_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	int ret;
+
+	if (on)
+		ret = pm_runtime_get_sync(hdev->dev);
+	else
+		ret = pm_runtime_put_sync(hdev->dev);
+	/* only values < 0 indicate errors */
+	return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
+	struct v4l2_dv_preset *preset)
+{
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	struct device *dev = hdev->dev;
+	const struct hdmi_preset_conf *conf;
+
+	conf = hdmi_preset2conf(preset->preset);
+	if (conf == NULL) {
+		dev_err(dev, "preset (%u) not supported\n", preset->preset);
+		return -EINVAL;
+	}
+	hdev->cur_conf = conf;
+	hdev->cur_preset = preset->preset;
+	return 0;
+}
+
+static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
+	struct v4l2_dv_preset *preset)
+{
+	memset(preset, 0, sizeof(*preset));
+	preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
+	return 0;
+}
+
+static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
+	  struct v4l2_mbus_framefmt *fmt)
+{
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	struct device *dev = hdev->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+	if (!hdev->cur_conf)
+		return -EINVAL;
+	*fmt = hdev->cur_conf->mbus_fmt;
+	return 0;
+}
+
+static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
+	struct v4l2_dv_enum_preset *preset)
+{
+	if (preset->index >= ARRAY_SIZE(hdmi_conf))
+		return -EINVAL;
+	return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset);
+}
+
+static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
+	.s_power = hdmi_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
+	.s_dv_preset = hdmi_s_dv_preset,
+	.g_dv_preset = hdmi_g_dv_preset,
+	.enum_dv_presets = hdmi_enum_dv_presets,
+	.g_mbus_fmt = hdmi_g_mbus_fmt,
+	.s_stream = hdmi_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops = {
+	.core = &hdmi_sd_core_ops,
+	.video = &hdmi_sd_video_ops,
+};
+
+static int hdmi_runtime_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+
+	dev_dbg(dev, "%s\n", __func__);
+	hdmi_resource_poweroff(&hdev->res);
+	return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+	int ret = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	hdmi_resource_poweron(&hdev->res);
+
+	ret = hdmi_conf_apply(hdev);
+	if (ret)
+		goto fail;
+
+	dev_dbg(dev, "poweron succeed\n");
+
+	return 0;
+
+fail:
+	hdmi_resource_poweroff(&hdev->res);
+	dev_err(dev, "poweron failed\n");
+
+	return ret;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+	.runtime_suspend = hdmi_runtime_suspend,
+	.runtime_resume	 = hdmi_runtime_resume,
+};
+
+static void hdmi_resources_cleanup(struct hdmi_device *hdev)
+{
+	struct hdmi_resources *res = &hdev->res;
+
+	dev_dbg(hdev->dev, "HDMI resource cleanup\n");
+	/* put clocks, power */
+	if (res->regul_count)
+		regulator_bulk_free(res->regul_count, res->regul_bulk);
+	/* kfree is NULL-safe */
+	kfree(res->regul_bulk);
+	if (!IS_ERR_OR_NULL(res->hdmiphy))
+		clk_put(res->hdmiphy);
+	if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+		clk_put(res->sclk_hdmiphy);
+	if (!IS_ERR_OR_NULL(res->sclk_pixel))
+		clk_put(res->sclk_pixel);
+	if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+		clk_put(res->sclk_hdmi);
+	if (!IS_ERR_OR_NULL(res->hdmi))
+		clk_put(res->hdmi);
+	memset(res, 0, sizeof *res);
+}
+
+static int hdmi_resources_init(struct hdmi_device *hdev)
+{
+	struct device *dev = hdev->dev;
+	struct hdmi_resources *res = &hdev->res;
+	static char *supply[] = {
+		"hdmi-en",
+		"vdd",
+		"vdd_osc",
+		"vdd_pll",
+	};
+	int i, ret;
+
+	dev_dbg(dev, "HDMI resource init\n");
+
+	memset(res, 0, sizeof *res);
+	/* get clocks, power */
+
+	res->hdmi = clk_get(dev, "hdmi");
+	if (IS_ERR_OR_NULL(res->hdmi)) {
+		dev_err(dev, "failed to get clock 'hdmi'\n");
+		goto fail;
+	}
+	res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+	if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+		dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
+		goto fail;
+	}
+	res->sclk_pixel = clk_get(dev, "sclk_pixel");
+	if (IS_ERR_OR_NULL(res->sclk_pixel)) {
+		dev_err(dev, "failed to get clock 'sclk_pixel'\n");
+		goto fail;
+	}
+	res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
+	if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
+		dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
+		goto fail;
+	}
+	res->hdmiphy = clk_get(dev, "hdmiphy");
+	if (IS_ERR_OR_NULL(res->hdmiphy)) {
+		dev_err(dev, "failed to get clock 'hdmiphy'\n");
+		goto fail;
+	}
+	res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
+		sizeof res->regul_bulk[0], GFP_KERNEL);
+	if (!res->regul_bulk) {
+		dev_err(dev, "failed to get memory for regulators\n");
+		goto fail;
+	}
+	for (i = 0; i < ARRAY_SIZE(supply); ++i) {
+		res->regul_bulk[i].supply = supply[i];
+		res->regul_bulk[i].consumer = NULL;
+	}
+
+	ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+	if (ret) {
+		dev_err(dev, "failed to get regulators\n");
+		goto fail;
+	}
+	res->regul_count = ARRAY_SIZE(supply);
+
+	return 0;
+fail:
+	dev_err(dev, "HDMI resource init - failed\n");
+	hdmi_resources_cleanup(hdev);
+	return -ENODEV;
+}
+
+static int __devinit hdmi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct i2c_adapter *phy_adapter;
+	struct v4l2_subdev *sd;
+	struct hdmi_device *hdmi_dev = NULL;
+	struct hdmi_driver_data *drv_data;
+	int ret;
+
+	dev_dbg(dev, "probe start\n");
+
+	hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL);
+	if (!hdmi_dev) {
+		dev_err(dev, "out of memory\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hdmi_dev->dev = dev;
+
+	ret = hdmi_resources_init(hdmi_dev);
+	if (ret)
+		goto fail_hdev;
+
+	/* mapping HDMI registers */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(dev, "get memory resource failed.\n");
+		ret = -ENXIO;
+		goto fail_init;
+	}
+
+	hdmi_dev->regs = ioremap(res->start, resource_size(res));
+	if (hdmi_dev->regs == NULL) {
+		dev_err(dev, "register mapping failed.\n");
+		ret = -ENXIO;
+		goto fail_hdev;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(dev, "get interrupt resource failed.\n");
+		ret = -ENXIO;
+		goto fail_regs;
+	}
+
+	ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev);
+	if (ret) {
+		dev_err(dev, "request interrupt failed.\n");
+		goto fail_regs;
+	}
+	hdmi_dev->irq = res->start;
+
+	/* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
+	strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
+		sizeof(hdmi_dev->v4l2_dev.name));
+	/* passing NULL owner prevents driver from erasing drvdata */
+	ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "could not register v4l2 device.\n");
+		goto fail_irq;
+	}
+
+	drv_data = (struct hdmi_driver_data *)
+		platform_get_device_id(pdev)->driver_data;
+	phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus);
+	if (phy_adapter == NULL) {
+		dev_err(dev, "adapter request failed\n");
+		ret = -ENXIO;
+		goto fail_vdev;
+	}
+
+	hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
+		phy_adapter, &hdmiphy_info, NULL);
+	/* on failure or not adapter is no longer useful */
+	i2c_put_adapter(phy_adapter);
+	if (hdmi_dev->phy_sd == NULL) {
+		dev_err(dev, "missing subdev for hdmiphy\n");
+		ret = -ENODEV;
+		goto fail_vdev;
+	}
+
+	clk_enable(hdmi_dev->res.hdmi);
+
+	pm_runtime_enable(dev);
+
+	sd = &hdmi_dev->sd;
+	v4l2_subdev_init(sd, &hdmi_sd_ops);
+	sd->owner = THIS_MODULE;
+
+	strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
+	hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
+	/* FIXME: missing fail preset is not supported */
+	hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset);
+
+	/* storing subdev for call that have only access to struct device */
+	dev_set_drvdata(dev, sd);
+
+	dev_info(dev, "probe sucessful\n");
+
+	return 0;
+
+fail_vdev:
+	v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+
+fail_irq:
+	free_irq(hdmi_dev->irq, hdmi_dev);
+
+fail_regs:
+	iounmap(hdmi_dev->regs);
+
+fail_init:
+	hdmi_resources_cleanup(hdmi_dev);
+
+fail_hdev:
+	kfree(hdmi_dev);
+
+fail:
+	dev_err(dev, "probe failed\n");
+	return ret;
+}
+
+static int __devexit hdmi_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd);
+
+	pm_runtime_disable(dev);
+	clk_disable(hdmi_dev->res.hdmi);
+	v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+	disable_irq(hdmi_dev->irq);
+	free_irq(hdmi_dev->irq, hdmi_dev);
+	iounmap(hdmi_dev->regs);
+	hdmi_resources_cleanup(hdmi_dev);
+	kfree(hdmi_dev);
+	dev_info(dev, "remove sucessful\n");
+
+	return 0;
+}
+
+static struct platform_driver hdmi_driver __refdata = {
+	.probe = hdmi_probe,
+	.remove = __devexit_p(hdmi_remove),
+	.id_table = hdmi_driver_types,
+	.driver = {
+		.name = "s5p-hdmi",
+		.owner = THIS_MODULE,
+		.pm = &hdmi_pm_ops,
+	}
+};
+
+/* D R I V E R   I N I T I A L I Z A T I O N */
+
+static int __init hdmi_init(void)
+{
+	int ret;
+	static const char banner[] __initdata = KERN_INFO \
+		"Samsung HDMI output driver, "
+		"(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+	printk(banner);
+
+	ret = platform_driver_register(&hdmi_driver);
+	if (ret)
+		printk(KERN_ERR "HDMI platform driver register failed\n");
+
+	return ret;
+}
+module_init(hdmi_init);
+
+static void __exit hdmi_exit(void)
+{
+	platform_driver_unregister(&hdmi_driver);
+}
+module_exit(hdmi_exit);
+
+
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c
new file mode 100644
index 0000000..6693f4a
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c
@@ -0,0 +1,188 @@
+/*
+ * Samsung HDMI Physical interface driver
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.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/i2c.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
+MODULE_LICENSE("GPL");
+
+struct hdmiphy_conf {
+	u32 preset;
+	const u8 *data;
+};
+
+static const u8 hdmiphy_conf27[32] = {
+	0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+	0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87,
+	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+	0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_175[32] = {
+	0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
+	0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
+	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+	0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+	0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
+	0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
+	0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0,
+	0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+	0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
+	0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
+	0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+	0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+};
+
+static const struct hdmiphy_conf hdmiphy_conf[] = {
+	{ V4L2_DV_480P59_94, hdmiphy_conf27 },
+	{ V4L2_DV_1080P30, hdmiphy_conf74_175 },
+	{ V4L2_DV_720P59_94, hdmiphy_conf74_175 },
+	{ V4L2_DV_720P60, hdmiphy_conf74_25 },
+	{ V4L2_DV_1080P50, hdmiphy_conf148_5 },
+	{ V4L2_DV_1080P60, hdmiphy_conf148_5 },
+};
+
+const u8 *hdmiphy_preset2conf(u32 preset)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i)
+		if (hdmiphy_conf[i].preset == preset)
+			return hdmiphy_conf[i].data;
+	return NULL;
+}
+
+static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
+{
+	/* to be implemented */
+	return 0;
+}
+
+static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
+	struct v4l2_dv_preset *preset)
+{
+	const u8 *data;
+	u8 buffer[32];
+	int ret;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *dev = &client->dev;
+
+	dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
+	data = hdmiphy_preset2conf(preset->preset);
+	if (!data) {
+		dev_err(dev, "format not supported\n");
+		return -EINVAL;
+	}
+
+	/* storing configuration to the device */
+	memcpy(buffer, data, 32);
+	ret = i2c_master_send(client, buffer, 32);
+	if (ret != 32) {
+		dev_err(dev, "failed to configure HDMIPHY via I2C\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *dev = &client->dev;
+	u8 buffer[2];
+	int ret;
+
+	dev_info(dev, "s_stream(%d)\n", enable);
+	/* going to/from configuration from/to operation mode */
+	buffer[0] = 0x1f;
+	buffer[1] = enable ? 0x80 : 0x00;
+
+	ret = i2c_master_send(client, buffer, 2);
+	if (ret != 2) {
+		dev_err(dev, "stream (%d) failed\n", enable);
+		return -EIO;
+	}
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
+	.s_power =  hdmiphy_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
+	.s_dv_preset = hdmiphy_s_dv_preset,
+	.s_stream =  hdmiphy_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmiphy_ops = {
+	.core = &hdmiphy_core_ops,
+	.video = &hdmiphy_video_ops,
+};
+
+static int __devinit hdmiphy_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	static struct v4l2_subdev sd;
+
+	v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops);
+	dev_info(&client->dev, "probe successful\n");
+	return 0;
+}
+
+static int __devexit hdmiphy_remove(struct i2c_client *client)
+{
+	dev_info(&client->dev, "remove successful\n");
+	return 0;
+}
+
+static const struct i2c_device_id hdmiphy_id[] = {
+	{ "hdmiphy", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
+
+static struct i2c_driver hdmiphy_driver = {
+	.driver = {
+		.name	= "s5p-hdmiphy",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= hdmiphy_probe,
+	.remove		= __devexit_p(hdmiphy_remove),
+	.id_table = hdmiphy_id,
+};
+
+static int __init hdmiphy_init(void)
+{
+	return i2c_add_driver(&hdmiphy_driver);
+}
+module_init(hdmiphy_init);
+
+static void __exit hdmiphy_exit(void)
+{
+	i2c_del_driver(&hdmiphy_driver);
+}
+module_exit(hdmiphy_exit);
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h
new file mode 100644
index 0000000..e224224
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer.h
@@ -0,0 +1,354 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifndef SAMSUNG_MIXER_H
+#define SAMSUNG_MIXER_H
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+	#define DEBUG
+#endif
+
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+#include "regs-mixer.h"
+
+/** maximum number of output interfaces */
+#define MXR_MAX_OUTPUTS 2
+/** maximum number of input interfaces (layers) */
+#define MXR_MAX_LAYERS 3
+#define MXR_DRIVER_NAME "s5p-mixer"
+/** maximal number of planes for every layer */
+#define MXR_MAX_PLANES	2
+
+#define MXR_ENABLE 1
+#define MXR_DISABLE 0
+
+/** description of a macroblock for packed formats */
+struct mxr_block {
+	/** vertical number of pixels in macroblock */
+	unsigned int width;
+	/** horizontal number of pixels in macroblock */
+	unsigned int height;
+	/** size of block in bytes */
+	unsigned int size;
+};
+
+/** description of supported format */
+struct mxr_format {
+	/** format name/mnemonic */
+	const char *name;
+	/** fourcc identifier */
+	u32 fourcc;
+	/** colorspace identifier */
+	enum v4l2_colorspace colorspace;
+	/** number of planes in image data */
+	int num_planes;
+	/** description of block for each plane */
+	struct mxr_block plane[MXR_MAX_PLANES];
+	/** number of subframes in image data */
+	int num_subframes;
+	/** specifies to which subframe belong given plane */
+	int plane2subframe[MXR_MAX_PLANES];
+	/** internal code, driver dependant */
+	unsigned long cookie;
+};
+
+/** description of crop configuration for image */
+struct mxr_crop {
+	/** width of layer in pixels */
+	unsigned int full_width;
+	/** height of layer in pixels */
+	unsigned int full_height;
+	/** horizontal offset of first pixel to be displayed */
+	unsigned int x_offset;
+	/** vertical offset of first pixel to be displayed */
+	unsigned int y_offset;
+	/** width of displayed data in pixels */
+	unsigned int width;
+	/** height of displayed data in pixels */
+	unsigned int height;
+	/** indicate which fields are present in buffer */
+	unsigned int field;
+};
+
+/** description of transformation from source to destination image */
+struct mxr_geometry {
+	/** cropping for source image */
+	struct mxr_crop src;
+	/** cropping for destination image */
+	struct mxr_crop dst;
+	/** layer-dependant description of horizontal scaling */
+	unsigned int x_ratio;
+	/** layer-dependant description of vertical scaling */
+	unsigned int y_ratio;
+};
+
+/** instance of a buffer */
+struct mxr_buffer {
+	/** common v4l buffer stuff -- must be first */
+	struct vb2_buffer	vb;
+	/** node for layer's lists */
+	struct list_head	list;
+};
+
+
+/** internal states of layer */
+enum mxr_layer_state {
+	/** layers is not shown */
+	MXR_LAYER_IDLE = 0,
+	/** state between STREAMON and hardware start */
+	MXR_LAYER_STREAMING_START,
+	/** layer is shown */
+	MXR_LAYER_STREAMING,
+	/** state before STREAMOFF is finished */
+	MXR_LAYER_STREAMING_FINISH,
+};
+
+/** forward declarations */
+struct mxr_device;
+struct mxr_layer;
+
+/** callback for layers operation */
+struct mxr_layer_ops {
+	/* TODO: try to port it to subdev API */
+	/** handler for resource release function */
+	void (*release)(struct mxr_layer *);
+	/** setting buffer to HW */
+	void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *);
+	/** setting format and geometry in HW */
+	void (*format_set)(struct mxr_layer *);
+	/** streaming stop/start */
+	void (*stream_set)(struct mxr_layer *, int);
+	/** adjusting geometry */
+	void (*fix_geometry)(struct mxr_layer *);
+};
+
+/** layer instance, a single window and content displayed on output */
+struct mxr_layer {
+	/** parent mixer device */
+	struct mxr_device *mdev;
+	/** layer index (unique identifier) */
+	int idx;
+	/** callbacks for layer methods */
+	struct mxr_layer_ops ops;
+	/** format array */
+	const struct mxr_format **fmt_array;
+	/** size of format array */
+	unsigned long fmt_array_size;
+
+	/** lock for protection of list and state fields */
+	spinlock_t enq_slock;
+	/** list for enqueued buffers */
+	struct list_head enq_list;
+	/** buffer currently owned by hardware in temporary registers */
+	struct mxr_buffer *update_buf;
+	/** buffer currently owned by hardware in shadow registers */
+	struct mxr_buffer *shadow_buf;
+	/** state of layer IDLE/STREAMING */
+	enum mxr_layer_state state;
+
+	/** mutex for protection of fields below */
+	struct mutex mutex;
+	/** handler for video node */
+	struct video_device vfd;
+	/** queue for output buffers */
+	struct vb2_queue vb_queue;
+	/** current image format */
+	const struct mxr_format *fmt;
+	/** current geometry of image */
+	struct mxr_geometry geo;
+};
+
+/** description of mixers output interface */
+struct mxr_output {
+	/** name of output */
+	char name[32];
+	/** output subdev */
+	struct v4l2_subdev *sd;
+	/** cookie used for configuration of registers */
+	int cookie;
+};
+
+/** specify source of output subdevs */
+struct mxr_output_conf {
+	/** name of output (connector) */
+	char *output_name;
+	/** name of module that generates output subdev */
+	char *module_name;
+	/** cookie need for mixer HW */
+	int cookie;
+};
+
+struct clk;
+struct regulator;
+
+/** auxiliary resources used my mixer */
+struct mxr_resources {
+	/** interrupt index */
+	int irq;
+	/** pointer to Mixer registers */
+	void __iomem *mxr_regs;
+	/** pointer to Video Processor registers */
+	void __iomem *vp_regs;
+	/** other resources, should used under mxr_device.mutex */
+	struct clk *mixer;
+	struct clk *vp;
+	struct clk *sclk_mixer;
+	struct clk *sclk_hdmi;
+	struct clk *sclk_dac;
+};
+
+/* event flags used  */
+enum mxr_devide_flags {
+	MXR_EVENT_VSYNC = 0,
+};
+
+/** drivers instance */
+struct mxr_device {
+	/** master device */
+	struct device *dev;
+	/** state of each layer */
+	struct mxr_layer *layer[MXR_MAX_LAYERS];
+	/** state of each output */
+	struct mxr_output *output[MXR_MAX_OUTPUTS];
+	/** number of registered outputs */
+	int output_cnt;
+
+	/* video resources */
+
+	/** V4L2 device */
+	struct v4l2_device v4l2_dev;
+	/** context of allocator */
+	void *alloc_ctx;
+	/** event wait queue */
+	wait_queue_head_t event_queue;
+	/** state flags */
+	unsigned long event_flags;
+
+	/** spinlock for protection of registers */
+	spinlock_t reg_slock;
+
+	/** mutex for protection of fields below */
+	struct mutex mutex;
+	/** number of entities depndant on output configuration */
+	int n_output;
+	/** number of users that do streaming */
+	int n_streamer;
+	/** index of current output */
+	int current_output;
+	/** auxiliary resources used my mixer */
+	struct mxr_resources res;
+};
+
+/** transform device structure into mixer device */
+static inline struct mxr_device *to_mdev(struct device *dev)
+{
+	struct v4l2_device *vdev = dev_get_drvdata(dev);
+	return container_of(vdev, struct mxr_device, v4l2_dev);
+}
+
+/** get current output data, should be called under mdev's mutex */
+static inline struct mxr_output *to_output(struct mxr_device *mdev)
+{
+	return mdev->output[mdev->current_output];
+}
+
+/** get current output subdev, should be called under mdev's mutex */
+static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev)
+{
+	struct mxr_output *out = to_output(mdev);
+	return out ? out->sd : NULL;
+}
+
+/** forward declaration for mixer platform data */
+struct mxr_platform_data;
+
+/** acquiring common video resources */
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+	struct mxr_output_conf *output_cont, int output_count);
+
+/** releasing common video resources */
+void __devexit mxr_release_video(struct mxr_device *mdev);
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+	int idx, char *name, struct mxr_layer_ops *ops);
+
+void mxr_base_layer_release(struct mxr_layer *layer);
+void mxr_layer_release(struct mxr_layer *layer);
+
+int mxr_base_layer_register(struct mxr_layer *layer);
+void mxr_base_layer_unregister(struct mxr_layer *layer);
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+	unsigned int width, unsigned int height);
+
+/** adds new consumer for mixer's power */
+int __must_check mxr_power_get(struct mxr_device *mdev);
+/** removes consumer for mixer's power */
+void mxr_power_put(struct mxr_device *mdev);
+/** add new client for output configuration */
+void mxr_output_get(struct mxr_device *mdev);
+/** removes new client for output configuration */
+void mxr_output_put(struct mxr_device *mdev);
+/** add new client for streaming */
+void mxr_streamer_get(struct mxr_device *mdev);
+/** removes new client for streaming */
+void mxr_streamer_put(struct mxr_device *mdev);
+/** returns format of data delivared to current output */
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+	struct v4l2_mbus_framefmt *mbus_fmt);
+
+/* Debug */
+
+#define mxr_err(mdev, fmt, ...)  dev_err(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__)
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+	#define mxr_dbg(mdev, fmt, ...)  dev_dbg(mdev->dev, fmt, ##__VA_ARGS__)
+#else
+	#define mxr_dbg(mdev, fmt, ...)  do { (void) mdev; } while (0)
+#endif
+
+/* accessing Mixer's and Video Processor's registers */
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en);
+void mxr_reg_reset(struct mxr_device *mdev);
+irqreturn_t mxr_irq_handler(int irq, void *dev_data);
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie);
+void mxr_reg_streamon(struct mxr_device *mdev);
+void mxr_reg_streamoff(struct mxr_device *mdev);
+int mxr_reg_wait4vsync(struct mxr_device *mdev);
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+	struct v4l2_mbus_framefmt *fmt);
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en);
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr);
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+	const struct mxr_format *fmt, const struct mxr_geometry *geo);
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en);
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+	dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]);
+void mxr_reg_vp_format(struct mxr_device *mdev,
+	const struct mxr_format *fmt, const struct mxr_geometry *geo);
+void mxr_reg_dump(struct mxr_device *mdev);
+
+#endif /* SAMSUNG_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
new file mode 100644
index 0000000..0064309
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_drv.c
@@ -0,0 +1,487 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MIXER");
+MODULE_LICENSE("GPL");
+
+/* --------- DRIVER PARAMETERS ---------- */
+
+static struct mxr_output_conf mxr_output_conf[] = {
+	{
+		.output_name = "S5P HDMI connector",
+		.module_name = "s5p-hdmi",
+		.cookie = 1,
+	},
+	{
+		.output_name = "S5P SDO connector",
+		.module_name = "s5p-sdo",
+		.cookie = 0,
+	},
+};
+
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+	struct v4l2_mbus_framefmt *mbus_fmt)
+{
+	struct v4l2_subdev *sd;
+	int ret;
+
+	mutex_lock(&mdev->mutex);
+	sd = to_outsd(mdev);
+	ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt);
+	WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+	mutex_unlock(&mdev->mutex);
+}
+
+void mxr_streamer_get(struct mxr_device *mdev)
+{
+	mutex_lock(&mdev->mutex);
+	++mdev->n_streamer;
+	mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+	if (mdev->n_streamer == 1) {
+		struct v4l2_subdev *sd = to_outsd(mdev);
+		struct v4l2_mbus_framefmt mbus_fmt;
+		struct mxr_resources *res = &mdev->res;
+		int ret;
+
+		if (to_output(mdev)->cookie == 0)
+			clk_set_parent(res->sclk_mixer, res->sclk_dac);
+		else
+			clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
+		mxr_reg_s_output(mdev, to_output(mdev)->cookie);
+
+		ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt);
+		WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+		ret = v4l2_subdev_call(sd, video, s_stream, 1);
+		WARN(ret, "starting stream failed for output %s\n", sd->name);
+
+		mxr_reg_set_mbus_fmt(mdev, &mbus_fmt);
+		mxr_reg_streamon(mdev);
+		ret = mxr_reg_wait4vsync(mdev);
+		WARN(ret, "failed to get vsync (%d) from output\n", ret);
+	}
+	mutex_unlock(&mdev->mutex);
+	mxr_reg_dump(mdev);
+	/* FIXME: what to do when streaming fails? */
+}
+
+void mxr_streamer_put(struct mxr_device *mdev)
+{
+	mutex_lock(&mdev->mutex);
+	--mdev->n_streamer;
+	mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+	if (mdev->n_streamer == 0) {
+		int ret;
+		struct v4l2_subdev *sd = to_outsd(mdev);
+
+		mxr_reg_streamoff(mdev);
+		/* vsync applies Mixer setup */
+		ret = mxr_reg_wait4vsync(mdev);
+		WARN(ret, "failed to get vsync (%d) from output\n", ret);
+		ret = v4l2_subdev_call(sd, video, s_stream, 0);
+		WARN(ret, "stopping stream failed for output %s\n", sd->name);
+	}
+	WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
+		mdev->n_streamer);
+	mutex_unlock(&mdev->mutex);
+	mxr_reg_dump(mdev);
+}
+
+void mxr_output_get(struct mxr_device *mdev)
+{
+	mutex_lock(&mdev->mutex);
+	++mdev->n_output;
+	mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+	/* turn on auxiliary driver */
+	if (mdev->n_output == 1)
+		v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
+	mutex_unlock(&mdev->mutex);
+}
+
+void mxr_output_put(struct mxr_device *mdev)
+{
+	mutex_lock(&mdev->mutex);
+	--mdev->n_output;
+	mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+	/* turn on auxiliary driver */
+	if (mdev->n_output == 0)
+		v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
+	WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
+		mdev->n_output);
+	mutex_unlock(&mdev->mutex);
+}
+
+int mxr_power_get(struct mxr_device *mdev)
+{
+	int ret = pm_runtime_get_sync(mdev->dev);
+
+	/* returning 1 means that power is already enabled,
+	 * so zero success be returned */
+	if (IS_ERR_VALUE(ret))
+		return ret;
+	return 0;
+}
+
+void mxr_power_put(struct mxr_device *mdev)
+{
+	pm_runtime_put_sync(mdev->dev);
+}
+
+/* --------- RESOURCE MANAGEMENT -------------*/
+
+static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
+	struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
+	if (res == NULL) {
+		mxr_err(mdev, "get memory resource failed.\n");
+		ret = -ENXIO;
+		goto fail;
+	}
+
+	mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
+	if (mdev->res.mxr_regs == NULL) {
+		mxr_err(mdev, "register mapping failed.\n");
+		ret = -ENXIO;
+		goto fail;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
+	if (res == NULL) {
+		mxr_err(mdev, "get memory resource failed.\n");
+		ret = -ENXIO;
+		goto fail_mxr_regs;
+	}
+
+	mdev->res.vp_regs = ioremap(res->start, resource_size(res));
+	if (mdev->res.vp_regs == NULL) {
+		mxr_err(mdev, "register mapping failed.\n");
+		ret = -ENXIO;
+		goto fail_mxr_regs;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
+	if (res == NULL) {
+		mxr_err(mdev, "get interrupt resource failed.\n");
+		ret = -ENXIO;
+		goto fail_vp_regs;
+	}
+
+	ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
+	if (ret) {
+		mxr_err(mdev, "request interrupt failed.\n");
+		goto fail_vp_regs;
+	}
+	mdev->res.irq = res->start;
+
+	return 0;
+
+fail_vp_regs:
+	iounmap(mdev->res.vp_regs);
+
+fail_mxr_regs:
+	iounmap(mdev->res.mxr_regs);
+
+fail:
+	return ret;
+}
+
+static void mxr_release_plat_resources(struct mxr_device *mdev)
+{
+	free_irq(mdev->res.irq, mdev);
+	iounmap(mdev->res.vp_regs);
+	iounmap(mdev->res.mxr_regs);
+}
+
+static void mxr_release_clocks(struct mxr_device *mdev)
+{
+	struct mxr_resources *res = &mdev->res;
+
+	if (!IS_ERR_OR_NULL(res->sclk_dac))
+		clk_put(res->sclk_dac);
+	if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+		clk_put(res->sclk_hdmi);
+	if (!IS_ERR_OR_NULL(res->sclk_mixer))
+		clk_put(res->sclk_mixer);
+	if (!IS_ERR_OR_NULL(res->vp))
+		clk_put(res->vp);
+	if (!IS_ERR_OR_NULL(res->mixer))
+		clk_put(res->mixer);
+}
+
+static int mxr_acquire_clocks(struct mxr_device *mdev)
+{
+	struct mxr_resources *res = &mdev->res;
+	struct device *dev = mdev->dev;
+
+	res->mixer = clk_get(dev, "mixer");
+	if (IS_ERR_OR_NULL(res->mixer)) {
+		mxr_err(mdev, "failed to get clock 'mixer'\n");
+		goto fail;
+	}
+	res->vp = clk_get(dev, "vp");
+	if (IS_ERR_OR_NULL(res->vp)) {
+		mxr_err(mdev, "failed to get clock 'vp'\n");
+		goto fail;
+	}
+	res->sclk_mixer = clk_get(dev, "sclk_mixer");
+	if (IS_ERR_OR_NULL(res->sclk_mixer)) {
+		mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
+		goto fail;
+	}
+	res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+	if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+		mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
+		goto fail;
+	}
+	res->sclk_dac = clk_get(dev, "sclk_dac");
+	if (IS_ERR_OR_NULL(res->sclk_dac)) {
+		mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
+		goto fail;
+	}
+
+	return 0;
+fail:
+	mxr_release_clocks(mdev);
+	return -ENODEV;
+}
+
+static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
+	struct platform_device *pdev)
+{
+	int ret;
+	ret = mxr_acquire_plat_resources(mdev, pdev);
+
+	if (ret)
+		goto fail;
+
+	ret = mxr_acquire_clocks(mdev);
+	if (ret)
+		goto fail_plat;
+
+	mxr_info(mdev, "resources acquired\n");
+	return 0;
+
+fail_plat:
+	mxr_release_plat_resources(mdev);
+fail:
+	mxr_err(mdev, "resources acquire failed\n");
+	return ret;
+}
+
+static void mxr_release_resources(struct mxr_device *mdev)
+{
+	mxr_release_clocks(mdev);
+	mxr_release_plat_resources(mdev);
+	memset(&mdev->res, 0, sizeof mdev->res);
+}
+
+static void mxr_release_layers(struct mxr_device *mdev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
+		if (mdev->layer[i])
+			mxr_layer_release(mdev->layer[i]);
+}
+
+static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
+	struct mxr_platform_data *pdata)
+{
+	mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
+	mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
+	mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
+
+	if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
+		mxr_err(mdev, "failed to acquire layers\n");
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	mxr_release_layers(mdev);
+	return -ENODEV;
+}
+
+/* ---------- POWER MANAGEMENT ----------- */
+
+static int mxr_runtime_resume(struct device *dev)
+{
+	struct mxr_device *mdev = to_mdev(dev);
+	struct mxr_resources *res = &mdev->res;
+
+	mxr_dbg(mdev, "resume - start\n");
+	mutex_lock(&mdev->mutex);
+	/* turn clocks on */
+	clk_enable(res->mixer);
+	clk_enable(res->vp);
+	clk_enable(res->sclk_mixer);
+	/* apply default configuration */
+	mxr_reg_reset(mdev);
+	mxr_dbg(mdev, "resume - finished\n");
+
+	mutex_unlock(&mdev->mutex);
+	return 0;
+}
+
+static int mxr_runtime_suspend(struct device *dev)
+{
+	struct mxr_device *mdev = to_mdev(dev);
+	struct mxr_resources *res = &mdev->res;
+	mxr_dbg(mdev, "suspend - start\n");
+	mutex_lock(&mdev->mutex);
+	/* turn clocks off */
+	clk_disable(res->sclk_mixer);
+	clk_disable(res->vp);
+	clk_disable(res->mixer);
+	mutex_unlock(&mdev->mutex);
+	mxr_dbg(mdev, "suspend - finished\n");
+	return 0;
+}
+
+static const struct dev_pm_ops mxr_pm_ops = {
+	.runtime_suspend = mxr_runtime_suspend,
+	.runtime_resume	 = mxr_runtime_resume,
+};
+
+/* --------- DRIVER INITIALIZATION ---------- */
+
+static int __devinit mxr_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mxr_platform_data *pdata = dev->platform_data;
+	struct mxr_device *mdev;
+	int ret;
+
+	/* mdev does not exist yet so no mxr_dbg is used */
+	dev_info(dev, "probe start\n");
+
+	mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
+	if (!mdev) {
+		mxr_err(mdev, "not enough memory.\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* setup pointer to master device */
+	mdev->dev = dev;
+
+	mutex_init(&mdev->mutex);
+	spin_lock_init(&mdev->reg_slock);
+	init_waitqueue_head(&mdev->event_queue);
+
+	/* acquire resources: regs, irqs, clocks, regulators */
+	ret = mxr_acquire_resources(mdev, pdev);
+	if (ret)
+		goto fail_mem;
+
+	/* configure resources for video output */
+	ret = mxr_acquire_video(mdev, mxr_output_conf,
+		ARRAY_SIZE(mxr_output_conf));
+	if (ret)
+		goto fail_resources;
+
+	/* configure layers */
+	ret = mxr_acquire_layers(mdev, pdata);
+	if (ret)
+		goto fail_video;
+
+	pm_runtime_enable(dev);
+
+	mxr_info(mdev, "probe successful\n");
+	return 0;
+
+fail_video:
+	mxr_release_video(mdev);
+
+fail_resources:
+	mxr_release_resources(mdev);
+
+fail_mem:
+	kfree(mdev);
+
+fail:
+	dev_info(dev, "probe failed\n");
+	return ret;
+}
+
+static int __devexit mxr_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mxr_device *mdev = to_mdev(dev);
+
+	pm_runtime_disable(dev);
+
+	mxr_release_layers(mdev);
+	mxr_release_video(mdev);
+	mxr_release_resources(mdev);
+
+	kfree(mdev);
+
+	dev_info(dev, "remove sucessful\n");
+	return 0;
+}
+
+static struct platform_driver mxr_driver __refdata = {
+	.probe = mxr_probe,
+	.remove = __devexit_p(mxr_remove),
+	.driver = {
+		.name = MXR_DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.pm = &mxr_pm_ops,
+	}
+};
+
+static int __init mxr_init(void)
+{
+	int i, ret;
+	static const char banner[] __initdata = KERN_INFO
+		"Samsung TV Mixer driver, "
+		"(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+	printk(banner);
+
+	/* Loading auxiliary modules */
+	for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
+		request_module(mxr_output_conf[i].module_name);
+
+	ret = platform_driver_register(&mxr_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "registration of MIXER driver failed\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+module_init(mxr_init);
+
+static void __exit mxr_exit(void)
+{
+	platform_driver_unregister(&mxr_driver);
+}
+module_exit(mxr_exit);
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c
new file mode 100644
index 0000000..58f0ba4
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c
@@ -0,0 +1,185 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+
+static const struct mxr_format mxr_fb_fmt_rgb565 = {
+	.name = "RGB565",
+	.fourcc = V4L2_PIX_FMT_RGB565,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.num_planes = 1,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 2 },
+	},
+	.num_subframes = 1,
+	.cookie = 4,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb1555 = {
+	.name = "ARGB1555",
+	.num_planes = 1,
+	.fourcc = V4L2_PIX_FMT_RGB555,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 2 },
+	},
+	.num_subframes = 1,
+	.cookie = 5,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb4444 = {
+	.name = "ARGB4444",
+	.num_planes = 1,
+	.fourcc = V4L2_PIX_FMT_RGB444,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 2 },
+	},
+	.num_subframes = 1,
+	.cookie = 6,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb8888 = {
+	.name = "ARGB8888",
+	.fourcc = V4L2_PIX_FMT_BGR32,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.num_planes = 1,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 4 },
+	},
+	.num_subframes = 1,
+	.cookie = 7,
+};
+
+static const struct mxr_format *mxr_graph_format[] = {
+	&mxr_fb_fmt_rgb565,
+	&mxr_fb_fmt_argb1555,
+	&mxr_fb_fmt_argb4444,
+	&mxr_fb_fmt_argb8888,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_graph_layer_release(struct mxr_layer *layer)
+{
+	mxr_base_layer_unregister(layer);
+	mxr_base_layer_release(layer);
+}
+
+static void mxr_graph_buffer_set(struct mxr_layer *layer,
+	struct mxr_buffer *buf)
+{
+	dma_addr_t addr = 0;
+
+	if (buf)
+		addr = vb2_dma_contig_plane_paddr(&buf->vb, 0);
+	mxr_reg_graph_buffer(layer->mdev, layer->idx, addr);
+}
+
+static void mxr_graph_stream_set(struct mxr_layer *layer, int en)
+{
+	mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en);
+}
+
+static void mxr_graph_format_set(struct mxr_layer *layer)
+{
+	mxr_reg_graph_format(layer->mdev, layer->idx,
+		layer->fmt, &layer->geo);
+}
+
+static void mxr_graph_fix_geometry(struct mxr_layer *layer)
+{
+	struct mxr_geometry *geo = &layer->geo;
+
+	/* limit to boundary size */
+	geo->src.full_width = clamp_val(geo->src.full_width, 1, 32767);
+	geo->src.full_height = clamp_val(geo->src.full_height, 1, 2047);
+	geo->src.width = clamp_val(geo->src.width, 1, geo->src.full_width);
+	geo->src.width = min(geo->src.width, 2047U);
+	/* not possible to crop of Y axis */
+	geo->src.y_offset = min(geo->src.y_offset, geo->src.full_height - 1);
+	geo->src.height = geo->src.full_height - geo->src.y_offset;
+	/* limitting offset */
+	geo->src.x_offset = min(geo->src.x_offset,
+		geo->src.full_width - geo->src.width);
+
+	/* setting position in output */
+	geo->dst.width = min(geo->dst.width, geo->dst.full_width);
+	geo->dst.height = min(geo->dst.height, geo->dst.full_height);
+
+	/* Mixer supports only 1x and 2x scaling */
+	if (geo->dst.width >= 2 * geo->src.width) {
+		geo->x_ratio = 1;
+		geo->dst.width = 2 * geo->src.width;
+	} else {
+		geo->x_ratio = 0;
+		geo->dst.width = geo->src.width;
+	}
+
+	if (geo->dst.height >= 2 * geo->src.height) {
+		geo->y_ratio = 1;
+		geo->dst.height = 2 * geo->src.height;
+	} else {
+		geo->y_ratio = 0;
+		geo->dst.height = geo->src.height;
+	}
+
+	geo->dst.x_offset = min(geo->dst.x_offset,
+		geo->dst.full_width - geo->dst.width);
+	geo->dst.y_offset = min(geo->dst.y_offset,
+		geo->dst.full_height - geo->dst.height);
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx)
+{
+	struct mxr_layer *layer;
+	int ret;
+	struct mxr_layer_ops ops = {
+		.release = mxr_graph_layer_release,
+		.buffer_set = mxr_graph_buffer_set,
+		.stream_set = mxr_graph_stream_set,
+		.format_set = mxr_graph_format_set,
+		.fix_geometry = mxr_graph_fix_geometry,
+	};
+	char name[32];
+
+	sprintf(name, "graph%d", idx);
+
+	layer = mxr_base_layer_create(mdev, idx, name, &ops);
+	if (layer == NULL) {
+		mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+		goto fail;
+	}
+
+	layer->fmt_array = mxr_graph_format;
+	layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format);
+
+	ret = mxr_base_layer_register(layer);
+	if (ret)
+		goto fail_layer;
+
+	return layer;
+
+fail_layer:
+	mxr_base_layer_release(layer);
+
+fail:
+	return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c
new file mode 100644
index 0000000..38dac67
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_reg.c
@@ -0,0 +1,541 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+#include "regs-mixer.h"
+#include "regs-vp.h"
+
+#include <linux/delay.h>
+
+/* Register access subroutines */
+
+static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id)
+{
+	return readl(mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+	writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id,
+	u32 val, u32 mask)
+{
+	u32 old = vp_read(mdev, reg_id);
+
+	val = (val & mask) | (old & ~mask);
+	writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id)
+{
+	return readl(mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+	writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id,
+	u32 val, u32 mask)
+{
+	u32 old = mxr_read(mdev, reg_id);
+
+	val = (val & mask) | (old & ~mask);
+	writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en)
+{
+	/* block update on vsync */
+	mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0,
+		MXR_STATUS_SYNC_ENABLE);
+	vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0);
+}
+
+static void __mxr_reg_vp_reset(struct mxr_device *mdev)
+{
+	int tries = 100;
+
+	vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING);
+	for (tries = 100; tries; --tries) {
+		/* waiting until VP_SRESET_PROCESSING is 0 */
+		if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING)
+			break;
+		mdelay(10);
+	}
+	WARN(tries == 0, "failed to reset Video Processor\n");
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev);
+
+void mxr_reg_reset(struct mxr_device *mdev)
+{
+	unsigned long flags;
+	u32 val; /* value stored to register */
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	/* set output in RGB888 mode */
+	mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_YUV444);
+
+	/* 16 beat burst in DMA */
+	mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST,
+		MXR_STATUS_BURST_MASK);
+
+	/* setting default layer priority: layer1 > video > layer0
+	 * because typical usage scenario would be
+	 * layer0 - framebuffer
+	 * video - video overlay
+	 * layer1 - OSD
+	 */
+	val  = MXR_LAYER_CFG_GRP0_VAL(1);
+	val |= MXR_LAYER_CFG_VP_VAL(2);
+	val |= MXR_LAYER_CFG_GRP1_VAL(3);
+	mxr_write(mdev, MXR_LAYER_CFG, val);
+
+	/* use dark gray background color */
+	mxr_write(mdev, MXR_BG_COLOR0, 0x808080);
+	mxr_write(mdev, MXR_BG_COLOR1, 0x808080);
+	mxr_write(mdev, MXR_BG_COLOR2, 0x808080);
+
+	/* setting graphical layers */
+
+	val  = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
+	val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */
+	val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
+
+	/* the same configuration for both layers */
+	mxr_write(mdev, MXR_GRAPHIC_CFG(0), val);
+	mxr_write(mdev, MXR_GRAPHIC_CFG(1), val);
+
+	/* configuration of Video Processor Registers */
+	__mxr_reg_vp_reset(mdev);
+	mxr_reg_vp_default_filter(mdev);
+
+	/* enable all interrupts */
+	mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+	const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	/* setup format */
+	mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx),
+		MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK);
+
+	/* setup geometry */
+	mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width);
+	val  = MXR_GRP_WH_WIDTH(geo->src.width);
+	val |= MXR_GRP_WH_HEIGHT(geo->src.height);
+	val |= MXR_GRP_WH_H_SCALE(geo->x_ratio);
+	val |= MXR_GRP_WH_V_SCALE(geo->y_ratio);
+	mxr_write(mdev, MXR_GRAPHIC_WH(idx), val);
+
+	/* setup offsets in source image */
+	val  = MXR_GRP_SXY_SX(geo->src.x_offset);
+	val |= MXR_GRP_SXY_SY(geo->src.y_offset);
+	mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val);
+
+	/* setup offsets in display image */
+	val  = MXR_GRP_DXY_DX(geo->dst.x_offset);
+	val |= MXR_GRP_DXY_DY(geo->dst.y_offset);
+	mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_format(struct mxr_device *mdev,
+	const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK);
+
+	/* setting size of input image */
+	vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) |
+		VP_IMG_VSIZE(geo->src.full_height));
+	/* chroma height has to reduced by 2 to avoid chroma distorions */
+	vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) |
+		VP_IMG_VSIZE(geo->src.full_height / 2));
+
+	vp_write(mdev, VP_SRC_WIDTH, geo->src.width);
+	vp_write(mdev, VP_SRC_HEIGHT, geo->src.height);
+	vp_write(mdev, VP_SRC_H_POSITION,
+		VP_SRC_H_POSITION_VAL(geo->src.x_offset));
+	vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset);
+
+	vp_write(mdev, VP_DST_WIDTH, geo->dst.width);
+	vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset);
+	if (geo->dst.field == V4L2_FIELD_INTERLACED) {
+		vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2);
+		vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2);
+	} else {
+		vp_write(mdev, VP_DST_HEIGHT, geo->dst.height);
+		vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset);
+	}
+
+	vp_write(mdev, VP_H_RATIO, geo->x_ratio);
+	vp_write(mdev, VP_V_RATIO, geo->y_ratio);
+
+	vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+
+}
+
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr)
+{
+	u32 val = addr ? ~0 : 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	if (idx == 0)
+		mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
+	else
+		mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
+	mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+	dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2])
+{
+	u32 val = luma_addr[0] ? ~0 : 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE);
+	vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON);
+	/* TODO: fix tiled mode */
+	vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]);
+	vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]);
+	vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]);
+	vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+static void mxr_irq_layer_handle(struct mxr_layer *layer)
+{
+	struct list_head *head = &layer->enq_list;
+	struct mxr_buffer *done;
+
+	/* skip non-existing layer */
+	if (layer == NULL)
+		return;
+
+	spin_lock(&layer->enq_slock);
+	if (layer->state == MXR_LAYER_IDLE)
+		goto done;
+
+	done = layer->shadow_buf;
+	layer->shadow_buf = layer->update_buf;
+
+	if (list_empty(head)) {
+		if (layer->state != MXR_LAYER_STREAMING)
+			layer->update_buf = NULL;
+	} else {
+		struct mxr_buffer *next;
+		next = list_first_entry(head, struct mxr_buffer, list);
+		list_del(&next->list);
+		layer->update_buf = next;
+	}
+
+	layer->ops.buffer_set(layer, layer->update_buf);
+
+	if (done && done != layer->shadow_buf)
+		vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE);
+
+done:
+	spin_unlock(&layer->enq_slock);
+}
+
+irqreturn_t mxr_irq_handler(int irq, void *dev_data)
+{
+	struct mxr_device *mdev = dev_data;
+	u32 i, val;
+
+	spin_lock(&mdev->reg_slock);
+	val = mxr_read(mdev, MXR_INT_STATUS);
+
+	/* wake up process waiting for VSYNC */
+	if (val & MXR_INT_STATUS_VSYNC) {
+		set_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+		wake_up(&mdev->event_queue);
+	}
+
+	/* clear interrupts */
+	if (~val & MXR_INT_EN_VSYNC) {
+		/* vsync interrupt use different bit for read and clear */
+		val &= ~MXR_INT_EN_VSYNC;
+		val |= MXR_INT_CLEAR_VSYNC;
+	}
+	mxr_write(mdev, MXR_INT_STATUS, val);
+
+	spin_unlock(&mdev->reg_slock);
+	/* leave on non-vsync event */
+	if (~val & MXR_INT_CLEAR_VSYNC)
+		return IRQ_HANDLED;
+	for (i = 0; i < MXR_MAX_LAYERS; ++i)
+		mxr_irq_layer_handle(mdev->layer[i]);
+	return IRQ_HANDLED;
+}
+
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie)
+{
+	u32 val;
+
+	val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI;
+	mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK);
+}
+
+void mxr_reg_streamon(struct mxr_device *mdev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	/* single write -> no need to block vsync update */
+
+	/* start MIXER */
+	mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
+
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_streamoff(struct mxr_device *mdev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	/* single write -> no need to block vsync update */
+
+	/* stop MIXER */
+	mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
+
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+int mxr_reg_wait4vsync(struct mxr_device *mdev)
+{
+	int ret;
+
+	clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+	/* TODO: consider adding interruptible */
+	ret = wait_event_timeout(mdev->event_queue,
+		test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
+		msecs_to_jiffies(1000));
+	if (ret > 0)
+		return 0;
+	if (ret < 0)
+		return ret;
+	mxr_warn(mdev, "no vsync detected - timeout\n");
+	return -ETIME;
+}
+
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+	struct v4l2_mbus_framefmt *fmt)
+{
+	u32 val = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->reg_slock, flags);
+	mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+	/* choosing between interlace and progressive mode */
+	if (fmt->field == V4L2_FIELD_INTERLACED)
+		val |= MXR_CFG_SCAN_INTERLACE;
+	else
+		val |= MXR_CFG_SCAN_PROGRASSIVE;
+
+	/* choosing between porper HD and SD mode */
+	if (fmt->height == 480)
+		val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
+	else if (fmt->height == 576)
+		val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
+	else if (fmt->height == 720)
+		val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+	else if (fmt->height == 1080)
+		val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
+	else
+		WARN(1, "unrecognized mbus height %u!\n", fmt->height);
+
+	mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK);
+
+	val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0;
+	vp_write_mask(mdev, VP_MODE, val,
+		VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING);
+
+	mxr_vsync_set_update(mdev, MXR_ENABLE);
+	spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en)
+{
+	/* no extra actions need to be done */
+}
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en)
+{
+	/* no extra actions need to be done */
+}
+
+static const u8 filter_y_horiz_tap8[] = {
+	0,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	0,	0,	0,
+	0,	2,	4,	5,	6,	6,	6,	6,
+	6,	5,	5,	4,	3,	2,	1,	1,
+	0,	-6,	-12,	-16,	-18,	-20,	-21,	-20,
+	-20,	-18,	-16,	-13,	-10,	-8,	-5,	-2,
+	127,	126,	125,	121,	114,	107,	99,	89,
+	79,	68,	57,	46,	35,	25,	16,	8,
+};
+
+static const u8 filter_y_vert_tap4[] = {
+	0,	-3,	-6,	-8,	-8,	-8,	-8,	-7,
+	-6,	-5,	-4,	-3,	-2,	-1,	-1,	0,
+	127,	126,	124,	118,	111,	102,	92,	81,
+	70,	59,	48,	37,	27,	19,	11,	5,
+	0,	5,	11,	19,	27,	37,	48,	59,
+	70,	81,	92,	102,	111,	118,	124,	126,
+	0,	0,	-1,	-1,	-2,	-3,	-4,	-5,
+	-6,	-7,	-8,	-8,	-8,	-8,	-6,	-3,
+};
+
+static const u8 filter_cr_horiz_tap4[] = {
+	0,	-3,	-6,	-8,	-8,	-8,	-8,	-7,
+	-6,	-5,	-4,	-3,	-2,	-1,	-1,	0,
+	127,	126,	124,	118,	111,	102,	92,	81,
+	70,	59,	48,	37,	27,	19,	11,	5,
+};
+
+static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev,
+	int reg_id, const u8 *data, unsigned int size)
+{
+	/* assure 4-byte align */
+	BUG_ON(size & 3);
+	for (; size; size -= 4, reg_id += 4, data += 4) {
+		u32 val = (data[0] << 24) |  (data[1] << 16) |
+			(data[2] << 8) | data[3];
+		vp_write(mdev, reg_id, val);
+	}
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev)
+{
+	mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL,
+		filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
+	mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL,
+		filter_y_vert_tap4, sizeof filter_y_vert_tap4);
+	mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL,
+		filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
+}
+
+static void mxr_reg_mxr_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+	mxr_dbg(mdev, #reg_id " = %08x\n", \
+		(u32)readl(mdev->res.mxr_regs + reg_id)); \
+} while (0)
+
+	DUMPREG(MXR_STATUS);
+	DUMPREG(MXR_CFG);
+	DUMPREG(MXR_INT_EN);
+	DUMPREG(MXR_INT_STATUS);
+
+	DUMPREG(MXR_LAYER_CFG);
+	DUMPREG(MXR_VIDEO_CFG);
+
+	DUMPREG(MXR_GRAPHIC0_CFG);
+	DUMPREG(MXR_GRAPHIC0_BASE);
+	DUMPREG(MXR_GRAPHIC0_SPAN);
+	DUMPREG(MXR_GRAPHIC0_WH);
+	DUMPREG(MXR_GRAPHIC0_SXY);
+	DUMPREG(MXR_GRAPHIC0_DXY);
+
+	DUMPREG(MXR_GRAPHIC1_CFG);
+	DUMPREG(MXR_GRAPHIC1_BASE);
+	DUMPREG(MXR_GRAPHIC1_SPAN);
+	DUMPREG(MXR_GRAPHIC1_WH);
+	DUMPREG(MXR_GRAPHIC1_SXY);
+	DUMPREG(MXR_GRAPHIC1_DXY);
+#undef DUMPREG
+}
+
+static void mxr_reg_vp_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+	mxr_dbg(mdev, #reg_id " = %08x\n", \
+		(u32) readl(mdev->res.vp_regs + reg_id)); \
+} while (0)
+
+
+	DUMPREG(VP_ENABLE);
+	DUMPREG(VP_SRESET);
+	DUMPREG(VP_SHADOW_UPDATE);
+	DUMPREG(VP_FIELD_ID);
+	DUMPREG(VP_MODE);
+	DUMPREG(VP_IMG_SIZE_Y);
+	DUMPREG(VP_IMG_SIZE_C);
+	DUMPREG(VP_PER_RATE_CTRL);
+	DUMPREG(VP_TOP_Y_PTR);
+	DUMPREG(VP_BOT_Y_PTR);
+	DUMPREG(VP_TOP_C_PTR);
+	DUMPREG(VP_BOT_C_PTR);
+	DUMPREG(VP_ENDIAN_MODE);
+	DUMPREG(VP_SRC_H_POSITION);
+	DUMPREG(VP_SRC_V_POSITION);
+	DUMPREG(VP_SRC_WIDTH);
+	DUMPREG(VP_SRC_HEIGHT);
+	DUMPREG(VP_DST_H_POSITION);
+	DUMPREG(VP_DST_V_POSITION);
+	DUMPREG(VP_DST_WIDTH);
+	DUMPREG(VP_DST_HEIGHT);
+	DUMPREG(VP_H_RATIO);
+	DUMPREG(VP_V_RATIO);
+
+#undef DUMPREG
+}
+
+void mxr_reg_dump(struct mxr_device *mdev)
+{
+	mxr_reg_mxr_dump(mdev);
+	mxr_reg_vp_dump(mdev);
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
new file mode 100644
index 0000000..43ac22f
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -0,0 +1,1006 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 "mixer.h"
+
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <media/videobuf2-dma-contig.h>
+
+static int find_reg_callback(struct device *dev, void *p)
+{
+	struct v4l2_subdev **sd = p;
+
+	*sd = dev_get_drvdata(dev);
+	/* non-zero value stops iteration */
+	return 1;
+}
+
+static struct v4l2_subdev *find_and_register_subdev(
+	struct mxr_device *mdev, char *module_name)
+{
+	struct device_driver *drv;
+	struct v4l2_subdev *sd = NULL;
+	int ret;
+
+	/* TODO: add waiting until probe is finished */
+	drv = driver_find(module_name, &platform_bus_type);
+	if (!drv) {
+		mxr_warn(mdev, "module %s is missing\n", module_name);
+		return NULL;
+	}
+	/* driver refcnt is increased, it is safe to iterate over devices */
+	ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback);
+	/* ret == 0 means that find_reg_callback was never executed */
+	if (sd == NULL) {
+		mxr_warn(mdev, "module %s provides no subdev!\n", module_name);
+		goto done;
+	}
+	/* v4l2_device_register_subdev detects if sd is NULL */
+	ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd);
+	if (ret) {
+		mxr_warn(mdev, "failed to register subdev %s\n", sd->name);
+		sd = NULL;
+	}
+
+done:
+	put_driver(drv);
+	return sd;
+}
+
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+	struct mxr_output_conf *output_conf, int output_count)
+{
+	struct device *dev = mdev->dev;
+	struct v4l2_device *v4l2_dev = &mdev->v4l2_dev;
+	int i;
+	int ret = 0;
+	struct v4l2_subdev *sd;
+
+	strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name));
+	/* prepare context for V4L2 device */
+	ret = v4l2_device_register(dev, v4l2_dev);
+	if (ret) {
+		mxr_err(mdev, "could not register v4l2 device.\n");
+		goto fail;
+	}
+
+	mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
+	if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+		mxr_err(mdev, "could not acquire vb2 allocator\n");
+		goto fail_v4l2_dev;
+	}
+
+	/* registering outputs */
+	mdev->output_cnt = 0;
+	for (i = 0; i < output_count; ++i) {
+		struct mxr_output_conf *conf = &output_conf[i];
+		struct mxr_output *out;
+
+		sd = find_and_register_subdev(mdev, conf->module_name);
+		/* trying to register next output */
+		if (sd == NULL)
+			continue;
+		out = kzalloc(sizeof *out, GFP_KERNEL);
+		if (out == NULL) {
+			mxr_err(mdev, "no memory for '%s'\n",
+				conf->output_name);
+			ret = -ENOMEM;
+			/* registered subdevs are removed in fail_v4l2_dev */
+			goto fail_output;
+		}
+		strlcpy(out->name, conf->output_name, sizeof(out->name));
+		out->sd = sd;
+		out->cookie = conf->cookie;
+		mdev->output[mdev->output_cnt++] = out;
+		mxr_info(mdev, "added output '%s' from module '%s'\n",
+			conf->output_name, conf->module_name);
+		/* checking if maximal number of outputs is reached */
+		if (mdev->output_cnt >= MXR_MAX_OUTPUTS)
+			break;
+	}
+
+	if (mdev->output_cnt == 0) {
+		mxr_err(mdev, "failed to register any output\n");
+		ret = -ENODEV;
+		/* skipping fail_output because there is nothing to free */
+		goto fail_vb2_allocator;
+	}
+
+	return 0;
+
+fail_output:
+	/* kfree is NULL-safe */
+	for (i = 0; i < mdev->output_cnt; ++i)
+		kfree(mdev->output[i]);
+	memset(mdev->output, 0, sizeof mdev->output);
+
+fail_vb2_allocator:
+	/* freeing allocator context */
+	vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+
+fail_v4l2_dev:
+	/* NOTE: automatically unregister all subdevs */
+	v4l2_device_unregister(v4l2_dev);
+
+fail:
+	return ret;
+}
+
+void __devexit mxr_release_video(struct mxr_device *mdev)
+{
+	int i;
+
+	/* kfree is NULL-safe */
+	for (i = 0; i < mdev->output_cnt; ++i)
+		kfree(mdev->output[i]);
+
+	vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+	v4l2_device_unregister(&mdev->v4l2_dev);
+}
+
+static int mxr_querycap(struct file *file, void *priv,
+	struct v4l2_capability *cap)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+	strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
+	strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
+	sprintf(cap->bus_info, "%d", layer->idx);
+	cap->version = KERNEL_VERSION(0, 1, 0);
+	cap->capabilities = V4L2_CAP_STREAMING |
+		V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+	return 0;
+}
+
+/* Geometry handling */
+static void mxr_layer_geo_fix(struct mxr_layer *layer)
+{
+	struct mxr_device *mdev = layer->mdev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+
+	/* TODO: add some dirty flag to avoid unnecessary adjustments */
+	mxr_get_mbus_fmt(mdev, &mbus_fmt);
+	layer->geo.dst.full_width = mbus_fmt.width;
+	layer->geo.dst.full_height = mbus_fmt.height;
+	layer->geo.dst.field = mbus_fmt.field;
+	layer->ops.fix_geometry(layer);
+}
+
+static void mxr_layer_default_geo(struct mxr_layer *layer)
+{
+	struct mxr_device *mdev = layer->mdev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+
+	memset(&layer->geo, 0, sizeof layer->geo);
+
+	mxr_get_mbus_fmt(mdev, &mbus_fmt);
+
+	layer->geo.dst.full_width = mbus_fmt.width;
+	layer->geo.dst.full_height = mbus_fmt.height;
+	layer->geo.dst.width = layer->geo.dst.full_width;
+	layer->geo.dst.height = layer->geo.dst.full_height;
+	layer->geo.dst.field = mbus_fmt.field;
+
+	layer->geo.src.full_width = mbus_fmt.width;
+	layer->geo.src.full_height = mbus_fmt.height;
+	layer->geo.src.width = layer->geo.src.full_width;
+	layer->geo.src.height = layer->geo.src.full_height;
+
+	layer->ops.fix_geometry(layer);
+}
+
+static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
+{
+	mxr_dbg(mdev, "src.full_size = (%u, %u)\n",
+		geo->src.full_width, geo->src.full_height);
+	mxr_dbg(mdev, "src.size = (%u, %u)\n",
+		geo->src.width, geo->src.height);
+	mxr_dbg(mdev, "src.offset = (%u, %u)\n",
+		geo->src.x_offset, geo->src.y_offset);
+	mxr_dbg(mdev, "dst.full_size = (%u, %u)\n",
+		geo->dst.full_width, geo->dst.full_height);
+	mxr_dbg(mdev, "dst.size = (%u, %u)\n",
+		geo->dst.width, geo->dst.height);
+	mxr_dbg(mdev, "dst.offset = (%u, %u)\n",
+		geo->dst.x_offset, geo->dst.y_offset);
+	mxr_dbg(mdev, "ratio = (%u, %u)\n",
+		geo->x_ratio, geo->y_ratio);
+}
+
+
+static const struct mxr_format *find_format_by_fourcc(
+	struct mxr_layer *layer, unsigned long fourcc);
+static const struct mxr_format *find_format_by_index(
+	struct mxr_layer *layer, unsigned long index);
+
+static int mxr_enum_fmt(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *f)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	const struct mxr_format *fmt;
+
+	mxr_dbg(mdev, "%s\n", __func__);
+	fmt = find_format_by_index(layer, f->index);
+	if (fmt == NULL)
+		return -EINVAL;
+
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+
+static int mxr_s_fmt(struct file *file, void *priv,
+	struct v4l2_format *f)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	const struct mxr_format *fmt;
+	struct v4l2_pix_format_mplane *pix;
+	struct mxr_device *mdev = layer->mdev;
+	struct mxr_geometry *geo = &layer->geo;
+
+	mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+
+	pix = &f->fmt.pix_mp;
+	fmt = find_format_by_fourcc(layer, pix->pixelformat);
+	if (fmt == NULL) {
+		mxr_warn(mdev, "not recognized fourcc: %08x\n",
+			pix->pixelformat);
+		return -EINVAL;
+	}
+	layer->fmt = fmt;
+	geo->src.full_width = pix->width;
+	geo->src.width = pix->width;
+	geo->src.full_height = pix->height;
+	geo->src.height = pix->height;
+	/* assure consistency of geometry */
+	mxr_layer_geo_fix(layer);
+	mxr_dbg(mdev, "width=%u height=%u span=%u\n",
+		geo->src.width, geo->src.height, geo->src.full_width);
+
+	return 0;
+}
+
+static unsigned int divup(unsigned int divident, unsigned int divisor)
+{
+	return (divident + divisor - 1) / divisor;
+}
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+	unsigned int width, unsigned int height)
+{
+	unsigned int bl_width = divup(width, blk->width);
+	unsigned int bl_height = divup(height, blk->height);
+
+	return bl_width * bl_height * blk->size;
+}
+
+static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes,
+	const struct mxr_format *fmt, u32 width, u32 height)
+{
+	int i;
+
+	memset(planes, 0, sizeof(*planes) * fmt->num_subframes);
+	for (i = 0; i < fmt->num_planes; ++i) {
+		struct v4l2_plane_pix_format *plane = planes
+			+ fmt->plane2subframe[i];
+		const struct mxr_block *blk = &fmt->plane[i];
+		u32 bl_width = divup(width, blk->width);
+		u32 bl_height = divup(height, blk->height);
+		u32 sizeimage = bl_width * bl_height * blk->size;
+		u16 bytesperline = bl_width * blk->size / blk->height;
+
+		plane->sizeimage += sizeimage;
+		plane->bytesperline = max(plane->bytesperline, bytesperline);
+	}
+}
+
+static int mxr_g_fmt(struct file *file, void *priv,
+			     struct v4l2_format *f)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+	pix->width = layer->geo.src.full_width;
+	pix->height = layer->geo.src.full_height;
+	pix->field = V4L2_FIELD_NONE;
+	pix->pixelformat = layer->fmt->fourcc;
+	pix->colorspace = layer->fmt->colorspace;
+	mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height);
+
+	return 0;
+}
+
+static inline struct mxr_crop *choose_crop_by_type(struct mxr_geometry *geo,
+	enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		return &geo->dst;
+	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+		return &geo->src;
+	default:
+		return NULL;
+	}
+}
+
+static int mxr_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_crop *crop;
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	crop = choose_crop_by_type(&layer->geo, a->type);
+	if (crop == NULL)
+		return -EINVAL;
+	mxr_layer_geo_fix(layer);
+	a->c.left = crop->x_offset;
+	a->c.top = crop->y_offset;
+	a->c.width = crop->width;
+	a->c.height = crop->height;
+	return 0;
+}
+
+static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_crop *crop;
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	crop = choose_crop_by_type(&layer->geo, a->type);
+	if (crop == NULL)
+		return -EINVAL;
+	crop->x_offset = a->c.left;
+	crop->y_offset = a->c.top;
+	crop->width = a->c.width;
+	crop->height = a->c.height;
+	mxr_layer_geo_fix(layer);
+	return 0;
+}
+
+static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_crop *crop;
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	crop = choose_crop_by_type(&layer->geo, a->type);
+	if (crop == NULL)
+		return -EINVAL;
+	mxr_layer_geo_fix(layer);
+	a->bounds.left = 0;
+	a->bounds.top = 0;
+	a->bounds.width = crop->full_width;
+	a->bounds.top = crop->full_height;
+	a->defrect = a->bounds;
+	/* setting pixel aspect to 1/1 */
+	a->pixelaspect.numerator = 1;
+	a->pixelaspect.denominator = 1;
+	return 0;
+}
+
+static int mxr_enum_dv_presets(struct file *file, void *fh,
+	struct v4l2_dv_enum_preset *preset)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	/* lock protects from changing sd_out */
+	mutex_lock(&mdev->mutex);
+	ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
+	mutex_unlock(&mdev->mutex);
+
+	return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_dv_preset(struct file *file, void *fh,
+	struct v4l2_dv_preset *preset)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	/* lock protects from changing sd_out */
+	mutex_lock(&mdev->mutex);
+
+	/* preset change cannot be done while there is an entity
+	 * dependant on output configuration
+	 */
+	if (mdev->n_output > 0) {
+		mutex_unlock(&mdev->mutex);
+		return -EBUSY;
+	}
+
+	ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
+
+	mutex_unlock(&mdev->mutex);
+
+	/* any failure should return EINVAL according to V4L2 doc */
+	return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_dv_preset(struct file *file, void *fh,
+	struct v4l2_dv_preset *preset)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	/* lock protects from changing sd_out */
+	mutex_lock(&mdev->mutex);
+	ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
+	mutex_unlock(&mdev->mutex);
+
+	return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	/* lock protects from changing sd_out */
+	mutex_lock(&mdev->mutex);
+
+	/* standard change cannot be done while there is an entity
+	 * dependant on output configuration
+	 */
+	if (mdev->n_output > 0) {
+		mutex_unlock(&mdev->mutex);
+		return -EBUSY;
+	}
+
+	ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
+
+	mutex_unlock(&mdev->mutex);
+
+	return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	/* lock protects from changing sd_out */
+	mutex_lock(&mdev->mutex);
+	ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm);
+	mutex_unlock(&mdev->mutex);
+
+	return ret ? -EINVAL : 0;
+}
+
+static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	struct mxr_output *out;
+	struct v4l2_subdev *sd;
+
+	if (a->index >= mdev->output_cnt)
+		return -EINVAL;
+	out = mdev->output[a->index];
+	BUG_ON(out == NULL);
+	sd = out->sd;
+	strlcpy(a->name, out->name, sizeof(a->name));
+
+	/* try to obtain supported tv norms */
+	v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
+	a->capabilities = 0;
+	if (sd->ops->video && sd->ops->video->s_dv_preset)
+		a->capabilities |= V4L2_OUT_CAP_PRESETS;
+	if (sd->ops->video && sd->ops->video->s_std_output)
+		a->capabilities |= V4L2_OUT_CAP_STD;
+	a->type = V4L2_OUTPUT_TYPE_ANALOG;
+
+	return 0;
+}
+
+static int mxr_s_output(struct file *file, void *fh, unsigned int i)
+{
+	struct video_device *vfd = video_devdata(file);
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret = 0;
+
+	if (i >= mdev->output_cnt || mdev->output[i] == NULL)
+		return -EINVAL;
+
+	mutex_lock(&mdev->mutex);
+	if (mdev->n_output > 0) {
+		ret = -EBUSY;
+		goto done;
+	}
+	mdev->current_output = i;
+	vfd->tvnorms = 0;
+	v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output,
+		&vfd->tvnorms);
+	mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms);
+
+done:
+	mutex_unlock(&mdev->mutex);
+	return ret;
+}
+
+static int mxr_g_output(struct file *file, void *fh, unsigned int *p)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+
+	mutex_lock(&mdev->mutex);
+	*p = mdev->current_output;
+	mutex_unlock(&mdev->mutex);
+
+	return 0;
+}
+
+static int mxr_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *p)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	return vb2_reqbufs(&layer->vb_queue, p);
+}
+
+static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	return vb2_querybuf(&layer->vb_queue, p);
+}
+
+static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index);
+	return vb2_qbuf(&layer->vb_queue, p);
+}
+
+static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
+}
+
+static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	return vb2_streamon(&layer->vb_queue, i);
+}
+
+static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	return vb2_streamoff(&layer->vb_queue, i);
+}
+
+static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
+	.vidioc_querycap = mxr_querycap,
+	/* format handling */
+	.vidioc_enum_fmt_vid_out = mxr_enum_fmt,
+	.vidioc_s_fmt_vid_out_mplane = mxr_s_fmt,
+	.vidioc_g_fmt_vid_out_mplane = mxr_g_fmt,
+	/* buffer control */
+	.vidioc_reqbufs = mxr_reqbufs,
+	.vidioc_querybuf = mxr_querybuf,
+	.vidioc_qbuf = mxr_qbuf,
+	.vidioc_dqbuf = mxr_dqbuf,
+	/* Streaming control */
+	.vidioc_streamon = mxr_streamon,
+	.vidioc_streamoff = mxr_streamoff,
+	/* Preset functions */
+	.vidioc_enum_dv_presets = mxr_enum_dv_presets,
+	.vidioc_s_dv_preset = mxr_s_dv_preset,
+	.vidioc_g_dv_preset = mxr_g_dv_preset,
+	/* analog TV standard functions */
+	.vidioc_s_std = mxr_s_std,
+	.vidioc_g_std = mxr_g_std,
+	/* Output handling */
+	.vidioc_enum_output = mxr_enum_output,
+	.vidioc_s_output = mxr_s_output,
+	.vidioc_g_output = mxr_g_output,
+	/* Crop ioctls */
+	.vidioc_g_crop = mxr_g_crop,
+	.vidioc_s_crop = mxr_s_crop,
+	.vidioc_cropcap = mxr_cropcap,
+};
+
+static int mxr_video_open(struct file *file)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+	struct mxr_device *mdev = layer->mdev;
+	int ret = 0;
+
+	mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+	/* assure device probe is finished */
+	wait_for_device_probe();
+	/* creating context for file descriptor */
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		mxr_err(mdev, "v4l2_fh_open failed\n");
+		return ret;
+	}
+
+	/* leaving if layer is already initialized */
+	if (!v4l2_fh_is_singular_file(file))
+		return 0;
+
+	/* FIXME: should power be enabled on open? */
+	ret = mxr_power_get(mdev);
+	if (ret) {
+		mxr_err(mdev, "power on failed\n");
+		goto fail_fh_open;
+	}
+
+	ret = vb2_queue_init(&layer->vb_queue);
+	if (ret != 0) {
+		mxr_err(mdev, "failed to initialize vb2 queue\n");
+		goto fail_power;
+	}
+	/* set default format, first on the list */
+	layer->fmt = layer->fmt_array[0];
+	/* setup default geometry */
+	mxr_layer_default_geo(layer);
+
+	return 0;
+
+fail_power:
+	mxr_power_put(mdev);
+
+fail_fh_open:
+	v4l2_fh_release(file);
+
+	return ret;
+}
+
+static unsigned int
+mxr_video_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+	return vb2_poll(&layer->vb_queue, file, wait);
+}
+
+static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+	return vb2_mmap(&layer->vb_queue, vma);
+}
+
+static int mxr_video_release(struct file *file)
+{
+	struct mxr_layer *layer = video_drvdata(file);
+
+	mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+	if (v4l2_fh_is_singular_file(file)) {
+		vb2_queue_release(&layer->vb_queue);
+		mxr_power_put(layer->mdev);
+	}
+	v4l2_fh_release(file);
+	return 0;
+}
+
+static const struct v4l2_file_operations mxr_fops = {
+	.owner = THIS_MODULE,
+	.open = mxr_video_open,
+	.poll = mxr_video_poll,
+	.mmap = mxr_video_mmap,
+	.release = mxr_video_release,
+	.unlocked_ioctl = video_ioctl2,
+};
+
+static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+	unsigned int *nplanes, unsigned long sizes[],
+	void *alloc_ctxs[])
+{
+	struct mxr_layer *layer = vb2_get_drv_priv(vq);
+	const struct mxr_format *fmt = layer->fmt;
+	int i;
+	struct mxr_device *mdev = layer->mdev;
+	struct v4l2_plane_pix_format planes[3];
+
+	mxr_dbg(mdev, "%s\n", __func__);
+	/* checking if format was configured */
+	if (fmt == NULL)
+		return -EINVAL;
+	mxr_dbg(mdev, "fmt = %s\n", fmt->name);
+	mxr_mplane_fill(planes, fmt, layer->geo.src.full_width,
+		layer->geo.src.full_height);
+
+	*nplanes = fmt->num_subframes;
+	for (i = 0; i < fmt->num_subframes; ++i) {
+		alloc_ctxs[i] = layer->mdev->alloc_ctx;
+		sizes[i] = PAGE_ALIGN(planes[i].sizeimage);
+		mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]);
+	}
+
+	if (*nbuffers == 0)
+		*nbuffers = 1;
+
+	return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+	struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
+	struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
+	struct mxr_device *mdev = layer->mdev;
+	unsigned long flags;
+	int must_start = 0;
+
+	spin_lock_irqsave(&layer->enq_slock, flags);
+	if (layer->state == MXR_LAYER_STREAMING_START) {
+		layer->state = MXR_LAYER_STREAMING;
+		must_start = 1;
+	}
+	list_add_tail(&buffer->list, &layer->enq_list);
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+	if (must_start) {
+		layer->ops.stream_set(layer, MXR_ENABLE);
+		mxr_streamer_get(mdev);
+	}
+
+	mxr_dbg(mdev, "queuing buffer\n");
+}
+
+static void wait_lock(struct vb2_queue *vq)
+{
+	struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+	mxr_dbg(layer->mdev, "%s\n", __func__);
+	mutex_lock(&layer->mutex);
+}
+
+static void wait_unlock(struct vb2_queue *vq)
+{
+	struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+	mxr_dbg(layer->mdev, "%s\n", __func__);
+	mutex_unlock(&layer->mutex);
+}
+
+static int start_streaming(struct vb2_queue *vq)
+{
+	struct mxr_layer *layer = vb2_get_drv_priv(vq);
+	struct mxr_device *mdev = layer->mdev;
+	unsigned long flags;
+
+	mxr_dbg(mdev, "%s\n", __func__);
+	/* block any changes in output configuration */
+	mxr_output_get(mdev);
+
+	/* update layers geometry */
+	mxr_layer_geo_fix(layer);
+	mxr_geometry_dump(mdev, &layer->geo);
+
+	layer->ops.format_set(layer);
+	/* enabling layer in hardware */
+	spin_lock_irqsave(&layer->enq_slock, flags);
+	layer->state = MXR_LAYER_STREAMING_START;
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+	return 0;
+}
+
+static void mxr_watchdog(unsigned long arg)
+{
+	struct mxr_layer *layer = (struct mxr_layer *) arg;
+	struct mxr_device *mdev = layer->mdev;
+	unsigned long flags;
+
+	mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
+
+	spin_lock_irqsave(&layer->enq_slock, flags);
+
+	if (layer->update_buf == layer->shadow_buf)
+		layer->update_buf = NULL;
+	if (layer->update_buf) {
+		vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
+		layer->update_buf = NULL;
+	}
+	if (layer->shadow_buf) {
+		vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
+		layer->shadow_buf = NULL;
+	}
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+	struct mxr_layer *layer = vb2_get_drv_priv(vq);
+	struct mxr_device *mdev = layer->mdev;
+	unsigned long flags;
+	struct timer_list watchdog;
+	struct mxr_buffer *buf, *buf_tmp;
+
+	mxr_dbg(mdev, "%s\n", __func__);
+
+	spin_lock_irqsave(&layer->enq_slock, flags);
+
+	/* reset list */
+	layer->state = MXR_LAYER_STREAMING_FINISH;
+
+	/* set all buffer to be done */
+	list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+	/* give 1 seconds to complete to complete last buffers */
+	setup_timer_on_stack(&watchdog, mxr_watchdog,
+		(unsigned long)layer);
+	mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
+
+	/* wait until all buffers are goes to done state */
+	vb2_wait_for_all_buffers(vq);
+
+	/* stop timer if all synchronization is done */
+	del_timer_sync(&watchdog);
+	destroy_timer_on_stack(&watchdog);
+
+	/* stopping hardware */
+	spin_lock_irqsave(&layer->enq_slock, flags);
+	layer->state = MXR_LAYER_IDLE;
+	spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+	/* disabling layer in hardware */
+	layer->ops.stream_set(layer, MXR_DISABLE);
+	/* remove one streamer */
+	mxr_streamer_put(mdev);
+	/* allow changes in output configuration */
+	mxr_output_put(mdev);
+	return 0;
+}
+
+static struct vb2_ops mxr_video_qops = {
+	.queue_setup = queue_setup,
+	.buf_queue = buf_queue,
+	.wait_prepare = wait_unlock,
+	.wait_finish = wait_lock,
+	.start_streaming = start_streaming,
+	.stop_streaming = stop_streaming,
+};
+
+/* FIXME: try to put this functions to mxr_base_layer_create */
+int mxr_base_layer_register(struct mxr_layer *layer)
+{
+	struct mxr_device *mdev = layer->mdev;
+	int ret;
+
+	ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1);
+	if (ret)
+		mxr_err(mdev, "failed to register video device\n");
+	else
+		mxr_info(mdev, "registered layer %s as /dev/video%d\n",
+			layer->vfd.name, layer->vfd.num);
+	return ret;
+}
+
+void mxr_base_layer_unregister(struct mxr_layer *layer)
+{
+	video_unregister_device(&layer->vfd);
+}
+
+void mxr_layer_release(struct mxr_layer *layer)
+{
+	if (layer->ops.release)
+		layer->ops.release(layer);
+}
+
+void mxr_base_layer_release(struct mxr_layer *layer)
+{
+	kfree(layer);
+}
+
+static void mxr_vfd_release(struct video_device *vdev)
+{
+	printk(KERN_INFO "video device release\n");
+}
+
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+	int idx, char *name, struct mxr_layer_ops *ops)
+{
+	struct mxr_layer *layer;
+
+	layer = kzalloc(sizeof *layer, GFP_KERNEL);
+	if (layer == NULL) {
+		mxr_err(mdev, "not enough memory for layer.\n");
+		goto fail;
+	}
+
+	layer->mdev = mdev;
+	layer->idx = idx;
+	layer->ops = *ops;
+
+	spin_lock_init(&layer->enq_slock);
+	INIT_LIST_HEAD(&layer->enq_list);
+	mutex_init(&layer->mutex);
+
+	layer->vfd = (struct video_device) {
+		.minor = -1,
+		.release = mxr_vfd_release,
+		.fops = &mxr_fops,
+		.ioctl_ops = &mxr_ioctl_ops,
+	};
+	strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name));
+	/* let framework control PRIORITY */
+	set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
+
+	video_set_drvdata(&layer->vfd, layer);
+	layer->vfd.lock = &layer->mutex;
+	layer->vfd.v4l2_dev = &mdev->v4l2_dev;
+
+	layer->vb_queue = (struct vb2_queue) {
+		.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.io_modes = VB2_MMAP | VB2_USERPTR,
+		.drv_priv = layer,
+		.buf_struct_size = sizeof(struct mxr_buffer),
+		.ops = &mxr_video_qops,
+		.mem_ops = &vb2_dma_contig_memops,
+	};
+
+	return layer;
+
+fail:
+	return NULL;
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+	struct mxr_layer *layer, unsigned long fourcc)
+{
+	int i;
+
+	for (i = 0; i < layer->fmt_array_size; ++i)
+		if (layer->fmt_array[i]->fourcc == fourcc)
+			return layer->fmt_array[i];
+	return NULL;
+}
+
+static const struct mxr_format *find_format_by_index(
+	struct mxr_layer *layer, unsigned long index)
+{
+	if (index >= layer->fmt_array_size)
+		return NULL;
+	return layer->fmt_array[index];
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c
new file mode 100644
index 0000000..6950ed8
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c
@@ -0,0 +1,211 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include "regs-vp.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+static const struct mxr_format mxr_fmt_nv12 = {
+	.name = "NV12",
+	.fourcc = V4L2_PIX_FMT_NV12,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.num_planes = 2,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 1 },
+		{ .width = 2, .height = 2, .size = 2 },
+	},
+	.num_subframes = 1,
+	.cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv21 = {
+	.name = "NV21",
+	.fourcc = V4L2_PIX_FMT_NV21,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.num_planes = 2,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 1 },
+		{ .width = 2, .height = 2, .size = 2 },
+	},
+	.num_subframes = 1,
+	.cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12m = {
+	.name = "NV12 (mplane)",
+	.fourcc = V4L2_PIX_FMT_NV12M,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.num_planes = 2,
+	.plane = {
+		{ .width = 1, .height = 1, .size = 1 },
+		{ .width = 2, .height = 2, .size = 2 },
+	},
+	.num_subframes = 2,
+	.plane2subframe = {0, 1},
+	.cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12mt = {
+	.name = "NV12 tiled (mplane)",
+	.fourcc = V4L2_PIX_FMT_NV12MT,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+	.num_planes = 2,
+	.plane = {
+		{ .width = 128, .height = 32, .size = 4096 },
+		{ .width = 128, .height = 32, .size = 2048 },
+	},
+	.num_subframes = 2,
+	.plane2subframe = {0, 1},
+	.cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED,
+};
+
+static const struct mxr_format *mxr_video_format[] = {
+	&mxr_fmt_nv12,
+	&mxr_fmt_nv21,
+	&mxr_fmt_nv12m,
+	&mxr_fmt_nv12mt,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_vp_layer_release(struct mxr_layer *layer)
+{
+	mxr_base_layer_unregister(layer);
+	mxr_base_layer_release(layer);
+}
+
+static void mxr_vp_buffer_set(struct mxr_layer *layer,
+	struct mxr_buffer *buf)
+{
+	dma_addr_t luma_addr[2] = {0, 0};
+	dma_addr_t chroma_addr[2] = {0, 0};
+
+	if (buf == NULL) {
+		mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+		return;
+	}
+	luma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 0);
+	if (layer->fmt->num_subframes == 2) {
+		chroma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 1);
+	} else {
+		/* FIXME: mxr_get_plane_size compute integer division,
+		 * which is slow and should not be performed in interrupt */
+		chroma_addr[0] = luma_addr[0] + mxr_get_plane_size(
+			&layer->fmt->plane[0], layer->geo.src.full_width,
+			layer->geo.src.full_height);
+	}
+	if (layer->fmt->cookie & VP_MODE_MEM_TILED) {
+		luma_addr[1] = luma_addr[0] + 0x40;
+		chroma_addr[1] = chroma_addr[0] + 0x40;
+	} else {
+		luma_addr[1] = luma_addr[0] + layer->geo.src.full_width;
+		chroma_addr[1] = chroma_addr[0];
+	}
+	mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+}
+
+static void mxr_vp_stream_set(struct mxr_layer *layer, int en)
+{
+	mxr_reg_vp_layer_stream(layer->mdev, en);
+}
+
+static void mxr_vp_format_set(struct mxr_layer *layer)
+{
+	mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo);
+}
+
+static void mxr_vp_fix_geometry(struct mxr_layer *layer)
+{
+	struct mxr_geometry *geo = &layer->geo;
+
+	/* align horizontal size to 8 pixels */
+	geo->src.full_width = ALIGN(geo->src.full_width, 8);
+	/* limit to boundary size */
+	geo->src.full_width = clamp_val(geo->src.full_width, 8, 8192);
+	geo->src.full_height = clamp_val(geo->src.full_height, 1, 8192);
+	geo->src.width = clamp_val(geo->src.width, 32, geo->src.full_width);
+	geo->src.width = min(geo->src.width, 2047U);
+	geo->src.height = clamp_val(geo->src.height, 4, geo->src.full_height);
+	geo->src.height = min(geo->src.height, 2047U);
+
+	/* setting size of output window */
+	geo->dst.width = clamp_val(geo->dst.width, 8, geo->dst.full_width);
+	geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height);
+
+	/* ensure that scaling is in range 1/4x to 16x */
+	if (geo->src.width >= 4 * geo->dst.width)
+		geo->src.width = 4 * geo->dst.width;
+	if (geo->dst.width >= 16 * geo->src.width)
+		geo->dst.width = 16 * geo->src.width;
+	if (geo->src.height >= 4 * geo->dst.height)
+		geo->src.height = 4 * geo->dst.height;
+	if (geo->dst.height >= 16 * geo->src.height)
+		geo->dst.height = 16 * geo->src.height;
+
+	/* setting scaling ratio */
+	geo->x_ratio = (geo->src.width << 16) / geo->dst.width;
+	geo->y_ratio = (geo->src.height << 16) / geo->dst.height;
+
+	/* adjust offsets */
+	geo->src.x_offset = min(geo->src.x_offset,
+		geo->src.full_width - geo->src.width);
+	geo->src.y_offset = min(geo->src.y_offset,
+		geo->src.full_height - geo->src.height);
+	geo->dst.x_offset = min(geo->dst.x_offset,
+		geo->dst.full_width - geo->dst.width);
+	geo->dst.y_offset = min(geo->dst.y_offset,
+		geo->dst.full_height - geo->dst.height);
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx)
+{
+	struct mxr_layer *layer;
+	int ret;
+	struct mxr_layer_ops ops = {
+		.release = mxr_vp_layer_release,
+		.buffer_set = mxr_vp_buffer_set,
+		.stream_set = mxr_vp_stream_set,
+		.format_set = mxr_vp_format_set,
+		.fix_geometry = mxr_vp_fix_geometry,
+	};
+	char name[32];
+
+	sprintf(name, "video%d", idx);
+
+	layer = mxr_base_layer_create(mdev, idx, name, &ops);
+	if (layer == NULL) {
+		mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+		goto fail;
+	}
+
+	layer->fmt_array = mxr_video_format;
+	layer->fmt_array_size = ARRAY_SIZE(mxr_video_format);
+
+	ret = mxr_base_layer_register(layer);
+	if (ret)
+		goto fail_layer;
+
+	return layer;
+
+fail_layer:
+	mxr_base_layer_release(layer);
+
+fail:
+	return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h
new file mode 100644
index 0000000..ac93ad6
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-hdmi.h
@@ -0,0 +1,141 @@
+/* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * HDMI register header file for Samsung TVOUT 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 SAMSUNG_REGS_HDMI_H
+#define SAMSUNG_REGS_HDMI_H
+
+/*
+ * Register part
+*/
+
+#define HDMI_CTRL_BASE(x)		((x) + 0x00000000)
+#define HDMI_CORE_BASE(x)		((x) + 0x00010000)
+#define HDMI_TG_BASE(x)			((x) + 0x00050000)
+
+/* Control registers */
+#define HDMI_INTC_CON			HDMI_CTRL_BASE(0x0000)
+#define HDMI_INTC_FLAG			HDMI_CTRL_BASE(0x0004)
+#define HDMI_HPD_STATUS			HDMI_CTRL_BASE(0x000C)
+#define HDMI_PHY_RSTOUT			HDMI_CTRL_BASE(0x0014)
+#define HDMI_PHY_VPLL			HDMI_CTRL_BASE(0x0018)
+#define HDMI_PHY_CMU			HDMI_CTRL_BASE(0x001C)
+#define HDMI_CORE_RSTOUT		HDMI_CTRL_BASE(0x0020)
+
+/* Core registers */
+#define HDMI_CON_0			HDMI_CORE_BASE(0x0000)
+#define HDMI_CON_1			HDMI_CORE_BASE(0x0004)
+#define HDMI_CON_2			HDMI_CORE_BASE(0x0008)
+#define HDMI_SYS_STATUS			HDMI_CORE_BASE(0x0010)
+#define HDMI_PHY_STATUS			HDMI_CORE_BASE(0x0014)
+#define HDMI_STATUS_EN			HDMI_CORE_BASE(0x0020)
+#define HDMI_HPD			HDMI_CORE_BASE(0x0030)
+#define HDMI_MODE_SEL			HDMI_CORE_BASE(0x0040)
+#define HDMI_BLUE_SCREEN_0		HDMI_CORE_BASE(0x0050)
+#define HDMI_BLUE_SCREEN_1		HDMI_CORE_BASE(0x0054)
+#define HDMI_BLUE_SCREEN_2		HDMI_CORE_BASE(0x0058)
+#define HDMI_H_BLANK_0			HDMI_CORE_BASE(0x00A0)
+#define HDMI_H_BLANK_1			HDMI_CORE_BASE(0x00A4)
+#define HDMI_V_BLANK_0			HDMI_CORE_BASE(0x00B0)
+#define HDMI_V_BLANK_1			HDMI_CORE_BASE(0x00B4)
+#define HDMI_V_BLANK_2			HDMI_CORE_BASE(0x00B8)
+#define HDMI_H_V_LINE_0			HDMI_CORE_BASE(0x00C0)
+#define HDMI_H_V_LINE_1			HDMI_CORE_BASE(0x00C4)
+#define HDMI_H_V_LINE_2			HDMI_CORE_BASE(0x00C8)
+#define HDMI_VSYNC_POL			HDMI_CORE_BASE(0x00E4)
+#define HDMI_INT_PRO_MODE		HDMI_CORE_BASE(0x00E8)
+#define HDMI_V_BLANK_F_0		HDMI_CORE_BASE(0x0110)
+#define HDMI_V_BLANK_F_1		HDMI_CORE_BASE(0x0114)
+#define HDMI_V_BLANK_F_2		HDMI_CORE_BASE(0x0118)
+#define HDMI_H_SYNC_GEN_0		HDMI_CORE_BASE(0x0120)
+#define HDMI_H_SYNC_GEN_1		HDMI_CORE_BASE(0x0124)
+#define HDMI_H_SYNC_GEN_2		HDMI_CORE_BASE(0x0128)
+#define HDMI_V_SYNC_GEN_1_0		HDMI_CORE_BASE(0x0130)
+#define HDMI_V_SYNC_GEN_1_1		HDMI_CORE_BASE(0x0134)
+#define HDMI_V_SYNC_GEN_1_2		HDMI_CORE_BASE(0x0138)
+#define HDMI_V_SYNC_GEN_2_0		HDMI_CORE_BASE(0x0140)
+#define HDMI_V_SYNC_GEN_2_1		HDMI_CORE_BASE(0x0144)
+#define HDMI_V_SYNC_GEN_2_2		HDMI_CORE_BASE(0x0148)
+#define HDMI_V_SYNC_GEN_3_0		HDMI_CORE_BASE(0x0150)
+#define HDMI_V_SYNC_GEN_3_1		HDMI_CORE_BASE(0x0154)
+#define HDMI_V_SYNC_GEN_3_2		HDMI_CORE_BASE(0x0158)
+#define HDMI_AVI_CON			HDMI_CORE_BASE(0x0300)
+#define HDMI_AVI_BYTE(n)		HDMI_CORE_BASE(0x0320 + 4 * (n))
+#define	HDMI_DC_CONTROL			HDMI_CORE_BASE(0x05C0)
+#define HDMI_VIDEO_PATTERN_GEN		HDMI_CORE_BASE(0x05C4)
+#define HDMI_HPD_GEN			HDMI_CORE_BASE(0x05C8)
+
+/* Timing generator registers */
+#define HDMI_TG_CMD			HDMI_TG_BASE(0x0000)
+#define HDMI_TG_H_FSZ_L			HDMI_TG_BASE(0x0018)
+#define HDMI_TG_H_FSZ_H			HDMI_TG_BASE(0x001C)
+#define HDMI_TG_HACT_ST_L		HDMI_TG_BASE(0x0020)
+#define HDMI_TG_HACT_ST_H		HDMI_TG_BASE(0x0024)
+#define HDMI_TG_HACT_SZ_L		HDMI_TG_BASE(0x0028)
+#define HDMI_TG_HACT_SZ_H		HDMI_TG_BASE(0x002C)
+#define HDMI_TG_V_FSZ_L			HDMI_TG_BASE(0x0030)
+#define HDMI_TG_V_FSZ_H			HDMI_TG_BASE(0x0034)
+#define HDMI_TG_VSYNC_L			HDMI_TG_BASE(0x0038)
+#define HDMI_TG_VSYNC_H			HDMI_TG_BASE(0x003C)
+#define HDMI_TG_VSYNC2_L		HDMI_TG_BASE(0x0040)
+#define HDMI_TG_VSYNC2_H		HDMI_TG_BASE(0x0044)
+#define HDMI_TG_VACT_ST_L		HDMI_TG_BASE(0x0048)
+#define HDMI_TG_VACT_ST_H		HDMI_TG_BASE(0x004C)
+#define HDMI_TG_VACT_SZ_L		HDMI_TG_BASE(0x0050)
+#define HDMI_TG_VACT_SZ_H		HDMI_TG_BASE(0x0054)
+#define HDMI_TG_FIELD_CHG_L		HDMI_TG_BASE(0x0058)
+#define HDMI_TG_FIELD_CHG_H		HDMI_TG_BASE(0x005C)
+#define HDMI_TG_VACT_ST2_L		HDMI_TG_BASE(0x0060)
+#define HDMI_TG_VACT_ST2_H		HDMI_TG_BASE(0x0064)
+#define HDMI_TG_VSYNC_TOP_HDMI_L	HDMI_TG_BASE(0x0078)
+#define HDMI_TG_VSYNC_TOP_HDMI_H	HDMI_TG_BASE(0x007C)
+#define HDMI_TG_VSYNC_BOT_HDMI_L	HDMI_TG_BASE(0x0080)
+#define HDMI_TG_VSYNC_BOT_HDMI_H	HDMI_TG_BASE(0x0084)
+#define HDMI_TG_FIELD_TOP_HDMI_L	HDMI_TG_BASE(0x0088)
+#define HDMI_TG_FIELD_TOP_HDMI_H	HDMI_TG_BASE(0x008C)
+#define HDMI_TG_FIELD_BOT_HDMI_L	HDMI_TG_BASE(0x0090)
+#define HDMI_TG_FIELD_BOT_HDMI_H	HDMI_TG_BASE(0x0094)
+
+/*
+ * Bit definition part
+ */
+
+/* HDMI_INTC_CON */
+#define HDMI_INTC_EN_GLOBAL		(1 << 6)
+#define HDMI_INTC_EN_HPD_PLUG		(1 << 3)
+#define HDMI_INTC_EN_HPD_UNPLUG		(1 << 2)
+
+/* HDMI_INTC_FLAG */
+#define HDMI_INTC_FLAG_HPD_PLUG		(1 << 3)
+#define HDMI_INTC_FLAG_HPD_UNPLUG	(1 << 2)
+
+/* HDMI_PHY_RSTOUT */
+#define HDMI_PHY_SW_RSTOUT		(1 << 0)
+
+/* HDMI_CORE_RSTOUT */
+#define HDMI_CORE_SW_RSTOUT		(1 << 0)
+
+/* HDMI_CON_0 */
+#define HDMI_BLUE_SCR_EN		(1 << 5)
+#define HDMI_EN				(1 << 0)
+
+/* HDMI_PHY_STATUS */
+#define HDMI_PHY_STATUS_READY		(1 << 0)
+
+/* HDMI_MODE_SEL */
+#define HDMI_MODE_HDMI_EN		(1 << 1)
+#define HDMI_MODE_DVI_EN		(1 << 0)
+#define HDMI_MODE_MASK			(3 << 0)
+
+/* HDMI_TG_CMD */
+#define HDMI_TG_EN			(1 << 0)
+
+#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h
new file mode 100644
index 0000000..3c84426
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-mixer.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Mixer register header file for Samsung Mixer 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 SAMSUNG_REGS_MIXER_H
+#define SAMSUNG_REGS_MIXER_H
+
+/*
+ * Register part
+ */
+#define MXR_STATUS			0x0000
+#define MXR_CFG				0x0004
+#define MXR_INT_EN			0x0008
+#define MXR_INT_STATUS			0x000C
+#define MXR_LAYER_CFG			0x0010
+#define MXR_VIDEO_CFG			0x0014
+#define MXR_GRAPHIC0_CFG		0x0020
+#define MXR_GRAPHIC0_BASE		0x0024
+#define MXR_GRAPHIC0_SPAN		0x0028
+#define MXR_GRAPHIC0_SXY		0x002C
+#define MXR_GRAPHIC0_WH			0x0030
+#define MXR_GRAPHIC0_DXY		0x0034
+#define MXR_GRAPHIC0_BLANK		0x0038
+#define MXR_GRAPHIC1_CFG		0x0040
+#define MXR_GRAPHIC1_BASE		0x0044
+#define MXR_GRAPHIC1_SPAN		0x0048
+#define MXR_GRAPHIC1_SXY		0x004C
+#define MXR_GRAPHIC1_WH			0x0050
+#define MXR_GRAPHIC1_DXY		0x0054
+#define MXR_GRAPHIC1_BLANK		0x0058
+#define MXR_BG_CFG			0x0060
+#define MXR_BG_COLOR0			0x0064
+#define MXR_BG_COLOR1			0x0068
+#define MXR_BG_COLOR2			0x006C
+
+/* for parametrized access to layer registers */
+#define MXR_GRAPHIC_CFG(i)		(0x0020 + (i) * 0x20)
+#define MXR_GRAPHIC_BASE(i)		(0x0024 + (i) * 0x20)
+#define MXR_GRAPHIC_SPAN(i)		(0x0028 + (i) * 0x20)
+#define MXR_GRAPHIC_SXY(i)		(0x002C + (i) * 0x20)
+#define MXR_GRAPHIC_WH(i)		(0x0030 + (i) * 0x20)
+#define MXR_GRAPHIC_DXY(i)		(0x0034 + (i) * 0x20)
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+#define MXR_MASK(high_bit, low_bit) \
+	(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define MXR_MASK_VAL(val, high_bit, low_bit) \
+	(((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
+
+/* bits for MXR_STATUS */
+#define MXR_STATUS_16_BURST		(1 << 7)
+#define MXR_STATUS_BURST_MASK		(1 << 7)
+#define MXR_STATUS_SYNC_ENABLE		(1 << 2)
+#define MXR_STATUS_REG_RUN		(1 << 0)
+
+/* bits for MXR_CFG */
+#define MXR_CFG_OUT_YUV444		(0 << 8)
+#define MXR_CFG_OUT_RGB888		(1 << 8)
+#define MXR_CFG_DST_SDO			(0 << 7)
+#define MXR_CFG_DST_HDMI		(1 << 7)
+#define MXR_CFG_DST_MASK		(1 << 7)
+#define MXR_CFG_SCAN_HD_720		(0 << 6)
+#define MXR_CFG_SCAN_HD_1080		(1 << 6)
+#define MXR_CFG_GRP1_ENABLE		(1 << 5)
+#define MXR_CFG_GRP0_ENABLE		(1 << 4)
+#define MXR_CFG_VP_ENABLE		(1 << 3)
+#define MXR_CFG_SCAN_INTERLACE		(0 << 2)
+#define MXR_CFG_SCAN_PROGRASSIVE	(1 << 2)
+#define MXR_CFG_SCAN_NTSC		(0 << 1)
+#define MXR_CFG_SCAN_PAL		(1 << 1)
+#define MXR_CFG_SCAN_SD			(0 << 0)
+#define MXR_CFG_SCAN_HD			(1 << 0)
+#define MXR_CFG_SCAN_MASK		0x47
+
+/* bits for MXR_GRAPHICn_CFG */
+#define MXR_GRP_CFG_COLOR_KEY_DISABLE	(1 << 21)
+#define MXR_GRP_CFG_BLEND_PRE_MUL	(1 << 20)
+#define MXR_GRP_CFG_FORMAT_VAL(x)	MXR_MASK_VAL(x, 11, 8)
+#define MXR_GRP_CFG_FORMAT_MASK		MXR_GRP_CFG_FORMAT_VAL(~0)
+#define MXR_GRP_CFG_ALPHA_VAL(x)	MXR_MASK_VAL(x, 7, 0)
+
+/* bits for MXR_GRAPHICn_WH */
+#define MXR_GRP_WH_H_SCALE(x)		MXR_MASK_VAL(x, 28, 28)
+#define MXR_GRP_WH_V_SCALE(x)		MXR_MASK_VAL(x, 12, 12)
+#define MXR_GRP_WH_WIDTH(x)		MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_WH_HEIGHT(x)		MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_SXY */
+#define MXR_GRP_SXY_SX(x)		MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_SXY_SY(x)		MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_DXY */
+#define MXR_GRP_DXY_DX(x)		MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_DXY_DY(x)		MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_INT_EN */
+#define MXR_INT_EN_VSYNC		(1 << 11)
+#define MXR_INT_EN_ALL			(0x0f << 8)
+
+/* bit for MXR_INT_STATUS */
+#define MXR_INT_CLEAR_VSYNC		(1 << 11)
+#define MXR_INT_STATUS_VSYNC		(1 << 0)
+
+/* bit for MXR_LAYER_CFG */
+#define MXR_LAYER_CFG_GRP1_VAL(x)	MXR_MASK_VAL(x, 11, 8)
+#define MXR_LAYER_CFG_GRP0_VAL(x)	MXR_MASK_VAL(x, 7, 4)
+#define MXR_LAYER_CFG_VP_VAL(x)		MXR_MASK_VAL(x, 3, 0)
+
+#endif /* SAMSUNG_REGS_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/regs-sdo.h b/drivers/media/video/s5p-tv/regs-sdo.h
new file mode 100644
index 0000000..7f7c2b8
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-sdo.h
@@ -0,0 +1,63 @@
+/* drivers/media/video/s5p-tv/regs-sdo.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * SDO register description file
+ *
+ * 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 SAMSUNG_REGS_SDO_H
+#define SAMSUNG_REGS_SDO_H
+
+/*
+ * Register part
+ */
+
+#define SDO_CLKCON			0x0000
+#define SDO_CONFIG			0x0008
+#define SDO_VBI				0x0014
+#define SDO_DAC				0x003C
+#define SDO_CCCON			0x0180
+#define SDO_IRQ				0x0280
+#define SDO_IRQMASK			0x0284
+#define SDO_VERSION			0x03D8
+
+/*
+ * Bit definition part
+ */
+
+/* SDO Clock Control Register (SDO_CLKCON) */
+#define SDO_TVOUT_SW_RESET		(1 << 4)
+#define SDO_TVOUT_CLOCK_READY		(1 << 1)
+#define SDO_TVOUT_CLOCK_ON		(1 << 0)
+
+/* SDO Video Standard Configuration Register (SDO_CONFIG) */
+#define SDO_PROGRESSIVE			(1 << 4)
+#define SDO_NTSC_M			0
+#define SDO_PAL_M			1
+#define SDO_PAL_BGHID			2
+#define SDO_PAL_N			3
+#define SDO_PAL_NC			4
+#define SDO_NTSC_443			8
+#define SDO_PAL_60			9
+#define SDO_STANDARD_MASK		0xf
+
+/* SDO VBI Configuration Register (SDO_VBI) */
+#define SDO_CVBS_WSS_INS		(1 << 14)
+#define SDO_CVBS_CLOSED_CAPTION_MASK	(3 << 12)
+
+/* SDO DAC Configuration Register (SDO_DAC) */
+#define SDO_POWER_ON_DAC		(1 << 0)
+
+/* SDO Color Compensation On/Off Control (SDO_CCCON) */
+#define SDO_COMPENSATION_BHS_ADJ_OFF	(1 << 4)
+#define SDO_COMPENSATION_CVBS_COMP_OFF	(1 << 0)
+
+/* SDO Interrupt Request Register (SDO_IRQ) */
+#define SDO_VSYNC_IRQ_PEND		(1 << 0)
+
+#endif /* SAMSUNG_REGS_SDO_H */
diff --git a/drivers/media/video/s5p-tv/regs-vp.h b/drivers/media/video/s5p-tv/regs-vp.h
new file mode 100644
index 0000000..6c63984
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-vp.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * Video processor register header file for Samsung Mixer 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 SAMSUNG_REGS_VP_H
+#define SAMSUNG_REGS_VP_H
+
+/*
+ * Register part
+ */
+
+#define VP_ENABLE			0x0000
+#define VP_SRESET			0x0004
+#define VP_SHADOW_UPDATE		0x0008
+#define VP_FIELD_ID			0x000C
+#define VP_MODE				0x0010
+#define VP_IMG_SIZE_Y			0x0014
+#define VP_IMG_SIZE_C			0x0018
+#define VP_PER_RATE_CTRL		0x001C
+#define VP_TOP_Y_PTR			0x0028
+#define VP_BOT_Y_PTR			0x002C
+#define VP_TOP_C_PTR			0x0030
+#define VP_BOT_C_PTR			0x0034
+#define VP_ENDIAN_MODE			0x03CC
+#define VP_SRC_H_POSITION		0x0044
+#define VP_SRC_V_POSITION		0x0048
+#define VP_SRC_WIDTH			0x004C
+#define VP_SRC_HEIGHT			0x0050
+#define VP_DST_H_POSITION		0x0054
+#define VP_DST_V_POSITION		0x0058
+#define VP_DST_WIDTH			0x005C
+#define VP_DST_HEIGHT			0x0060
+#define VP_H_RATIO			0x0064
+#define VP_V_RATIO			0x0068
+#define VP_POLY8_Y0_LL			0x006C
+#define VP_POLY4_Y0_LL			0x00EC
+#define VP_POLY4_C0_LL			0x012C
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+
+#define VP_MASK(high_bit, low_bit) \
+	(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define VP_MASK_VAL(val, high_bit, low_bit) \
+	(((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
+
+ /* VP_ENABLE */
+#define VP_ENABLE_ON			(1 << 0)
+
+/* VP_SRESET */
+#define VP_SRESET_PROCESSING		(1 << 0)
+
+/* VP_SHADOW_UPDATE */
+#define VP_SHADOW_UPDATE_ENABLE		(1 << 0)
+
+/* VP_MODE */
+#define VP_MODE_NV12			(0 << 6)
+#define VP_MODE_NV21			(1 << 6)
+#define VP_MODE_LINE_SKIP		(1 << 5)
+#define VP_MODE_MEM_LINEAR		(0 << 4)
+#define VP_MODE_MEM_TILED		(1 << 4)
+#define VP_MODE_FMT_MASK		(5 << 4)
+#define VP_MODE_FIELD_ID_AUTO_TOGGLING	(1 << 2)
+#define VP_MODE_2D_IPC			(1 << 1)
+
+/* VP_IMG_SIZE_Y */
+/* VP_IMG_SIZE_C */
+#define VP_IMG_HSIZE(x)			VP_MASK_VAL(x, 29, 16)
+#define VP_IMG_VSIZE(x)			VP_MASK_VAL(x, 13, 0)
+
+/* VP_SRC_H_POSITION */
+#define VP_SRC_H_POSITION_VAL(x)	VP_MASK_VAL(x, 14, 4)
+
+/* VP_ENDIAN_MODE */
+#define VP_ENDIAN_MODE_LITTLE		(1 << 0)
+
+#endif /* SAMSUNG_REGS_VP_H */
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
new file mode 100644
index 0000000..4dddd6b
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -0,0 +1,479 @@
+/*
+ * Samsung Standard Definition Output (SDO) driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "regs-sdo.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)");
+MODULE_LICENSE("GPL");
+
+#define SDO_DEFAULT_STD	V4L2_STD_PAL
+
+struct sdo_format {
+	v4l2_std_id id;
+	/* all modes are 720 pixels wide */
+	unsigned int height;
+	unsigned int cookie;
+};
+
+struct sdo_device {
+	/** pointer to device parent */
+	struct device *dev;
+	/** base address of SDO registers */
+	void __iomem *regs;
+	/** SDO interrupt */
+	unsigned int irq;
+	/** DAC source clock */
+	struct clk *sclk_dac;
+	/** DAC clock */
+	struct clk *dac;
+	/** DAC physical interface */
+	struct clk *dacphy;
+	/** clock for control of VPLL */
+	struct clk *fout_vpll;
+	/** regulator for SDO IP power */
+	struct regulator *vdac;
+	/** regulator for SDO plug detection */
+	struct regulator *vdet;
+	/** subdev used as device interface */
+	struct v4l2_subdev sd;
+	/** current format */
+	const struct sdo_format *fmt;
+};
+
+static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct sdo_device, sd);
+}
+
+static inline
+void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask)
+{
+	u32 old = readl(sdev->regs + reg_id);
+	value = (value & mask) | (old & ~mask);
+	writel(value, sdev->regs + reg_id);
+}
+
+static inline
+void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value)
+{
+	writel(value, sdev->regs + reg_id);
+}
+
+static inline
+u32 sdo_read(struct sdo_device *sdev, u32 reg_id)
+{
+	return readl(sdev->regs + reg_id);
+}
+
+static irqreturn_t sdo_irq_handler(int irq, void *dev_data)
+{
+	struct sdo_device *sdev = dev_data;
+
+	/* clear interrupt */
+	sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND);
+	return IRQ_HANDLED;
+}
+
+static void sdo_reg_debug(struct sdo_device *sdev)
+{
+#define DBGREG(reg_id) \
+	dev_info(sdev->dev, #reg_id " = %08x\n", \
+		sdo_read(sdev, reg_id))
+
+	DBGREG(SDO_CLKCON);
+	DBGREG(SDO_CONFIG);
+	DBGREG(SDO_VBI);
+	DBGREG(SDO_DAC);
+	DBGREG(SDO_IRQ);
+	DBGREG(SDO_IRQMASK);
+	DBGREG(SDO_VERSION);
+}
+
+static const struct sdo_format sdo_format[] = {
+	{ V4L2_STD_PAL_N,	.height = 576, .cookie = SDO_PAL_N },
+	{ V4L2_STD_PAL_Nc,	.height = 576, .cookie = SDO_PAL_NC },
+	{ V4L2_STD_PAL_M,	.height = 480, .cookie = SDO_PAL_M },
+	{ V4L2_STD_PAL_60,	.height = 480, .cookie = SDO_PAL_60 },
+	{ V4L2_STD_NTSC_443,	.height = 480, .cookie = SDO_NTSC_443 },
+	{ V4L2_STD_PAL,		.height = 576, .cookie = SDO_PAL_BGHID },
+	{ V4L2_STD_NTSC_M,	.height = 480, .cookie = SDO_NTSC_M },
+};
+
+static const struct sdo_format *sdo_find_format(v4l2_std_id id)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(sdo_format); ++i)
+		if (sdo_format[i].id & id)
+			return &sdo_format[i];
+	return NULL;
+}
+
+static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	*std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL |
+		V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+		V4L2_STD_NTSC_443 | V4L2_STD_PAL_60;
+	return 0;
+}
+
+static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct sdo_device *sdev = sd_to_sdev(sd);
+	const struct sdo_format *fmt;
+	fmt = sdo_find_format(std);
+	if (fmt == NULL)
+		return -EINVAL;
+	sdev->fmt = fmt;
+	return 0;
+}
+
+static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	*std = sd_to_sdev(sd)->fmt->id;
+	return 0;
+}
+
+static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
+	struct v4l2_mbus_framefmt *fmt)
+{
+	struct sdo_device *sdev = sd_to_sdev(sd);
+
+	if (!sdev->fmt)
+		return -ENXIO;
+	/* all modes are 720 pixels wide */
+	fmt->width = 720;
+	fmt->height = sdev->fmt->height;
+	fmt->code = V4L2_MBUS_FMT_FIXED;
+	fmt->field = V4L2_FIELD_INTERLACED;
+	return 0;
+}
+
+static int sdo_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct sdo_device *sdev = sd_to_sdev(sd);
+	struct device *dev = sdev->dev;
+	int ret;
+
+	dev_info(dev, "sdo_s_power(%d)\n", on);
+
+	if (on)
+		ret = pm_runtime_get_sync(dev);
+	else
+		ret = pm_runtime_put_sync(dev);
+
+	/* only values < 0 indicate errors */
+	return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sdo_streamon(struct sdo_device *sdev)
+{
+	/* set proper clock for Timing Generator */
+	clk_set_rate(sdev->fout_vpll, 54000000);
+	dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
+	clk_get_rate(sdev->fout_vpll));
+	/* enable clock in SDO */
+	sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
+	clk_enable(sdev->dacphy);
+	/* enable DAC */
+	sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
+	sdo_reg_debug(sdev);
+	return 0;
+}
+
+static int sdo_streamoff(struct sdo_device *sdev)
+{
+	int tries;
+
+	sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
+	clk_disable(sdev->dacphy);
+	sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
+	for (tries = 100; tries; --tries) {
+		if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
+			break;
+		mdelay(1);
+	}
+	if (tries == 0)
+		dev_err(sdev->dev, "failed to stop streaming\n");
+	return tries ? 0 : -EIO;
+}
+
+static int sdo_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct sdo_device *sdev = sd_to_sdev(sd);
+	return on ? sdo_streamon(sdev) : sdo_streamoff(sdev);
+}
+
+static const struct v4l2_subdev_core_ops sdo_sd_core_ops = {
+	.s_power = sdo_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sdo_sd_video_ops = {
+	.s_std_output = sdo_s_std_output,
+	.g_std_output = sdo_g_std_output,
+	.g_tvnorms_output = sdo_g_tvnorms_output,
+	.g_mbus_fmt = sdo_g_mbus_fmt,
+	.s_stream = sdo_s_stream,
+};
+
+static const struct v4l2_subdev_ops sdo_sd_ops = {
+	.core = &sdo_sd_core_ops,
+	.video = &sdo_sd_video_ops,
+};
+
+static int sdo_runtime_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct sdo_device *sdev = sd_to_sdev(sd);
+
+	dev_info(dev, "suspend\n");
+	regulator_disable(sdev->vdet);
+	regulator_disable(sdev->vdac);
+	clk_disable(sdev->sclk_dac);
+	return 0;
+}
+
+static int sdo_runtime_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct sdo_device *sdev = sd_to_sdev(sd);
+
+	dev_info(dev, "resume\n");
+	clk_enable(sdev->sclk_dac);
+	regulator_enable(sdev->vdac);
+	regulator_enable(sdev->vdet);
+
+	/* software reset */
+	sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
+	mdelay(10);
+	sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET);
+
+	/* setting TV mode */
+	sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK);
+	/* XXX: forcing interlaced mode using undocumented bit */
+	sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE);
+	/* turn all VBI off */
+	sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS |
+		SDO_CVBS_CLOSED_CAPTION_MASK);
+	/* turn all post processing off */
+	sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF |
+		SDO_COMPENSATION_CVBS_COMP_OFF);
+	sdo_reg_debug(sdev);
+	return 0;
+}
+
+static const struct dev_pm_ops sdo_pm_ops = {
+	.runtime_suspend = sdo_runtime_suspend,
+	.runtime_resume	 = sdo_runtime_resume,
+};
+
+static int __devinit sdo_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sdo_device *sdev;
+	struct resource *res;
+	int ret = 0;
+	struct clk *sclk_vpll;
+
+	dev_info(dev, "probe start\n");
+	sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+	if (!sdev) {
+		dev_err(dev, "not enough memory.\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+	sdev->dev = dev;
+
+	/* mapping registers */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(dev, "get memory resource failed.\n");
+		ret = -ENXIO;
+		goto fail_sdev;
+	}
+
+	sdev->regs = ioremap(res->start, resource_size(res));
+	if (sdev->regs == NULL) {
+		dev_err(dev, "register mapping failed.\n");
+		ret = -ENXIO;
+		goto fail_sdev;
+	}
+
+	/* acquiring interrupt */
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(dev, "get interrupt resource failed.\n");
+		ret = -ENXIO;
+		goto fail_regs;
+	}
+	ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev);
+	if (ret) {
+		dev_err(dev, "request interrupt failed.\n");
+		goto fail_regs;
+	}
+	sdev->irq = res->start;
+
+	/* acquire clocks */
+	sdev->sclk_dac = clk_get(dev, "sclk_dac");
+	if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
+		dev_err(dev, "failed to get clock 'sclk_dac'\n");
+		ret = -ENXIO;
+		goto fail_irq;
+	}
+	sdev->dac = clk_get(dev, "dac");
+	if (IS_ERR_OR_NULL(sdev->dac)) {
+		dev_err(dev, "failed to get clock 'dac'\n");
+		ret = -ENXIO;
+		goto fail_sclk_dac;
+	}
+	sdev->dacphy = clk_get(dev, "dacphy");
+	if (IS_ERR_OR_NULL(sdev->dacphy)) {
+		dev_err(dev, "failed to get clock 'dacphy'\n");
+		ret = -ENXIO;
+		goto fail_dac;
+	}
+	sclk_vpll = clk_get(dev, "sclk_vpll");
+	if (IS_ERR_OR_NULL(sclk_vpll)) {
+		dev_err(dev, "failed to get clock 'sclk_vpll'\n");
+		ret = -ENXIO;
+		goto fail_dacphy;
+	}
+	clk_set_parent(sdev->sclk_dac, sclk_vpll);
+	clk_put(sclk_vpll);
+	sdev->fout_vpll = clk_get(dev, "fout_vpll");
+	if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
+		dev_err(dev, "failed to get clock 'fout_vpll'\n");
+		goto fail_dacphy;
+	}
+	dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
+
+	/* acquire regulator */
+	sdev->vdac = regulator_get(dev, "vdd33a_dac");
+	if (IS_ERR_OR_NULL(sdev->vdac)) {
+		dev_err(dev, "failed to get regulator 'vdac'\n");
+		goto fail_fout_vpll;
+	}
+	sdev->vdet = regulator_get(dev, "vdet");
+	if (IS_ERR_OR_NULL(sdev->vdet)) {
+		dev_err(dev, "failed to get regulator 'vdet'\n");
+		goto fail_vdac;
+	}
+
+	/* enable gate for dac clock, because mixer uses it */
+	clk_enable(sdev->dac);
+
+	/* configure power management */
+	pm_runtime_enable(dev);
+
+	/* configuration of interface subdevice */
+	v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
+	sdev->sd.owner = THIS_MODULE;
+	strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
+
+	/* set default format */
+	sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
+	BUG_ON(sdev->fmt == NULL);
+
+	/* keeping subdev in device's private for use by other drivers */
+	dev_set_drvdata(dev, &sdev->sd);
+
+	dev_info(dev, "probe succeeded\n");
+	return 0;
+
+fail_vdac:
+	regulator_put(sdev->vdac);
+fail_fout_vpll:
+	clk_put(sdev->fout_vpll);
+fail_dacphy:
+	clk_put(sdev->dacphy);
+fail_dac:
+	clk_put(sdev->dac);
+fail_sclk_dac:
+	clk_put(sdev->sclk_dac);
+fail_irq:
+	free_irq(sdev->irq, sdev);
+fail_regs:
+	iounmap(sdev->regs);
+fail_sdev:
+	kfree(sdev);
+fail:
+	dev_info(dev, "probe failed\n");
+	return ret;
+}
+
+static int __devexit sdo_remove(struct platform_device *pdev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+	struct sdo_device *sdev = sd_to_sdev(sd);
+
+	pm_runtime_disable(&pdev->dev);
+	clk_disable(sdev->dac);
+	regulator_put(sdev->vdet);
+	regulator_put(sdev->vdac);
+	clk_put(sdev->fout_vpll);
+	clk_put(sdev->dacphy);
+	clk_put(sdev->dac);
+	clk_put(sdev->sclk_dac);
+	free_irq(sdev->irq, sdev);
+	iounmap(sdev->regs);
+	kfree(sdev);
+
+	dev_info(&pdev->dev, "remove successful\n");
+	return 0;
+}
+
+static struct platform_driver sdo_driver __refdata = {
+	.probe = sdo_probe,
+	.remove = __devexit_p(sdo_remove),
+	.driver = {
+		.name = "s5p-sdo",
+		.owner = THIS_MODULE,
+		.pm = &sdo_pm_ops,
+	}
+};
+
+static int __init sdo_init(void)
+{
+	int ret;
+	static const char banner[] __initdata = KERN_INFO \
+		"Samsung Standard Definition Output (SDO) driver, "
+		"(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+	printk(banner);
+
+	ret = platform_driver_register(&sdo_driver);
+	if (ret)
+		printk(KERN_ERR "SDO platform driver register failed\n");
+
+	return ret;
+}
+module_init(sdo_init);
+
+static void __exit sdo_exit(void)
+{
+	platform_driver_unregister(&sdo_driver);
+}
+module_exit(sdo_exit);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 0db9092..f2ae405 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -757,8 +757,8 @@
 	switch (ctrl->id) {
 	case V4L2_CID_CHROMA_AGC:
 		/* chroma gain cluster */
-		if (state->agc->cur.val)
-			state->gain->cur.val =
+		if (state->agc->val)
+			state->gain->val =
 				saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
 		break;
 	}
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index e2062b2..0f9fb99 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -4951,8 +4951,9 @@
 		.audio_clock    = 0x00187de7,
 		.tuner_type     = TUNER_XC2028,
 		.radio_type     = UNSET,
-		.tuner_addr	= ADDR_UNSET,
+		.tuner_addr	= 0x61,
 		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
 		.inputs = {{
 			.name   = name_tv,
 			.vmux   = 3,
@@ -6992,6 +6993,11 @@
 			msleep(10);
 			saa7134_set_gpio(dev, 18, 1);
 		break;
+		case SAA7134_BOARD_VIDEOMATE_T750:
+			saa7134_set_gpio(dev, 20, 0);
+			msleep(10);
+			saa7134_set_gpio(dev, 20, 1);
+		break;
 		}
 	return 0;
 	}
@@ -7451,6 +7457,11 @@
 		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x0e050000, 0x0c050000);
 		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000);
 		break;
+	case SAA7134_BOARD_VIDEOMATE_T750:
+		/* enable the analog tuner */
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x00008000, 0x00008000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+		break;
 	}
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index f9be737..ca65cda 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -39,6 +39,8 @@
 MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(SAA7134_VERSION);
+
 
 /* ------------------------------------------------------------------ */
 
@@ -1332,14 +1334,8 @@
 static int __init saa7134_init(void)
 {
 	INIT_LIST_HEAD(&saa7134_devlist);
-	printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n",
-	       (SAA7134_VERSION_CODE >> 16) & 0xff,
-	       (SAA7134_VERSION_CODE >>  8) & 0xff,
-	       SAA7134_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
-	printk(KERN_INFO "saa7130/34: snapshot date %04d-%02d-%02d\n",
-	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
+	printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n",
+	       SAA7134_VERSION);
 	return pci_register_driver(&saa7134_pci_driver);
 }
 
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 996a206..1e4ef16 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -56,6 +56,7 @@
 #include "lgs8gxx.h"
 
 #include "zl10353.h"
+#include "qt1010.h"
 
 #include "zl10036.h"
 #include "zl10039.h"
@@ -939,6 +940,18 @@
 	.disable_i2c_gate_ctrl = 1,
 };
 
+static struct zl10353_config videomate_t750_zl10353_config = {
+	.demod_address         = 0x0f,
+	.no_tuner              = 1,
+	.parallel_ts           = 1,
+	.disable_i2c_gate_ctrl = 1,
+};
+
+static struct qt1010_config videomate_t750_qt1010_config = {
+	.i2c_address = 0x62
+};
+
+
 /* ==================================================================
  * tda10086 based DVB-S cards, helper functions
  */
@@ -1650,6 +1663,18 @@
 					__func__);
 
 		break;
+	case SAA7134_BOARD_VIDEOMATE_T750:
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
+						&videomate_t750_zl10353_config,
+						&dev->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
+			if (dvb_attach(qt1010_attach,
+					fe0->dvb.frontend,
+					&dev->i2c_adap,
+					&videomate_t750_qt1010_config) == NULL)
+				wprintk("error attaching QT1010\n");
+		}
+		break;
 	case SAA7134_BOARD_ZOLID_HYBRID_PCI:
 		fe0->dvb.frontend = dvb_attach(tda10048_attach,
 					       &zolid_tda10048_config,
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 18294db..dde361a 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -172,7 +172,6 @@
 	strlcpy(cap->card, saa7134_boards[dev->board].name,
 		sizeof(cap->card));
 	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-	cap->version = SAA7134_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_READWRITE |
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 776ba2d..9cf7914f 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1810,7 +1810,6 @@
 	strlcpy(cap->card, saa7134_boards[dev->board].name,
 		sizeof(cap->card));
 	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-	cap->version = SAA7134_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_VBI_CAPTURE |
@@ -2307,7 +2306,6 @@
 	strcpy(cap->driver, "saa7134");
 	strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
 	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-	cap->version = SAA7134_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_TUNER;
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 28eb103..bc8d6bb 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -19,8 +19,7 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/version.h>
-#define SAA7134_VERSION_CODE KERNEL_VERSION(0, 2, 16)
+#define SAA7134_VERSION "0, 2, 17"
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index 4003645..2fd38a0 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -1246,7 +1246,6 @@
 	struct saa7164_encoder_fh *fh =
 		(struct saa7164_encoder_fh *)file->private_data;
 	struct saa7164_port *port = fh->port;
-	struct saa7164_user_buffer *ubuf;
 	unsigned int mask = 0;
 
 	port->last_poll_msecs_diff = port->last_poll_msecs;
@@ -1278,10 +1277,7 @@
 	}
 
 	/* Pull the first buffer from the used list */
-	ubuf = list_first_entry(&port->list_buf_used.list,
-		struct saa7164_user_buffer, list);
-
-	if (ubuf)
+	if (!list_empty(&port->list_buf_used.list))
 		mask |= POLLIN | POLLRDNORM;
 
 	return mask;
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
index bc1fced..e2e0341 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -1192,7 +1192,6 @@
 {
 	struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
 	struct saa7164_port *port = fh->port;
-	struct saa7164_user_buffer *ubuf;
 	unsigned int mask = 0;
 
 	port->last_poll_msecs_diff = port->last_poll_msecs;
@@ -1224,10 +1223,7 @@
 	}
 
 	/* Pull the first buffer from the used list */
-	ubuf = list_first_entry(&port->list_buf_used.list,
-		struct saa7164_user_buffer, list);
-
-	if (ubuf)
+	if (!list_empty(&port->list_buf_used.list))
 		mask |= POLLIN | POLLRDNORM;
 
 	return mask;
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 16745d2..6678bf1 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -48,7 +48,6 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 #include <linux/kdev_t.h>
-#include <linux/version.h>
 #include <linux/mutex.h>
 #include <linux/crc32.h>
 #include <linux/kthread.h>
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 3ae5c9c..e540898 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -27,7 +27,6 @@
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
 #include <linux/time.h>
-#include <linux/version.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
@@ -39,6 +38,7 @@
 #include <media/v4l2-dev.h>
 #include <media/soc_camera.h>
 #include <media/sh_mobile_ceu.h>
+#include <media/sh_mobile_csi2.h>
 #include <media/videobuf2-dma-contig.h>
 #include <media/v4l2-mediabus.h>
 #include <media/soc_mediabus.h>
@@ -96,6 +96,7 @@
 struct sh_mobile_ceu_dev {
 	struct soc_camera_host ici;
 	struct soc_camera_device *icd;
+	struct platform_device *csi2_pdev;
 
 	unsigned int irq;
 	void __iomem *base;
@@ -205,7 +206,7 @@
 
 
 	if (2 != success) {
-		dev_warn(&icd->dev, "soft reset time out\n");
+		dev_warn(icd->pdev, "soft reset time out\n");
 		return -EIO;
 	}
 
@@ -220,7 +221,7 @@
 			unsigned long sizes[], void *alloc_ctxs[])
 {
 	struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 						icd->current_fmt->host_fmt);
@@ -242,7 +243,7 @@
 			*count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
 	}
 
-	dev_dbg(icd->dev.parent, "count=%d, size=%lu\n", *count, sizes[0]);
+	dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]);
 
 	return 0;
 }
@@ -351,7 +352,7 @@
 
 	buf = to_ceu_vb(vb);
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
 		vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
 
 	/* Added list head initialization on alloc */
@@ -371,7 +372,7 @@
 	size = icd->user_height * bytes_per_line;
 
 	if (vb2_plane_size(vb, 0) < size) {
-		dev_err(icd->dev.parent, "Buffer too small (%lu < %lu)\n",
+		dev_err(icd->parent, "Buffer too small (%lu < %lu)\n",
 			vb2_plane_size(vb, 0), size);
 		return -ENOBUFS;
 	}
@@ -384,11 +385,11 @@
 static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
 {
 	struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
 
-	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
 		vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
 
 	spin_lock_irq(&pcdev->lock);
@@ -409,7 +410,7 @@
 static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
 {
 	struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 
@@ -421,8 +422,12 @@
 		pcdev->active = NULL;
 	}
 
-	/* Doesn't hurt also if the list is empty */
-	list_del_init(&buf->queue);
+	/*
+	 * Doesn't hurt also if the list is empty, but it hurts, if queuing the
+	 * buffer failed, and .buf_init() hasn't been called
+	 */
+	if (buf->queue.next)
+		list_del_init(&buf->queue);
 
 	spin_unlock_irq(&pcdev->lock);
 }
@@ -437,7 +442,7 @@
 static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
 {
 	struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct list_head *buf_head, *tmp;
 
@@ -499,25 +504,48 @@
 	return IRQ_HANDLED;
 }
 
+static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
+{
+	struct v4l2_subdev *sd;
+
+	if (!pcdev->csi2_pdev)
+		return NULL;
+
+	v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+		if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
+			return sd;
+
+	return NULL;
+}
+
 /* Called with .video_lock held */
 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+	struct v4l2_subdev *csi2_sd;
 	int ret;
 
 	if (pcdev->icd)
 		return -EBUSY;
 
-	dev_info(icd->dev.parent,
+	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver attached to camera %d\n",
 		 icd->devnum);
 
 	pm_runtime_get_sync(ici->v4l2_dev.dev);
 
 	ret = sh_mobile_ceu_soft_reset(pcdev);
-	if (!ret)
+
+	csi2_sd = find_csi2(pcdev);
+
+	ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+	if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) {
+		pm_runtime_put_sync(ici->v4l2_dev.dev);
+	} else {
 		pcdev->icd = icd;
+		ret = 0;
+	}
 
 	return ret;
 }
@@ -525,11 +553,13 @@
 /* Called with .video_lock held */
 static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+	struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
 
 	BUG_ON(icd != pcdev->icd);
 
+	v4l2_subdev_call(csi2_sd, core, s_power, 0);
 	/* disable capture, disable interrupts */
 	ceu_write(pcdev, CEIER, 0);
 	sh_mobile_ceu_soft_reset(pcdev);
@@ -545,7 +575,7 @@
 
 	pm_runtime_put_sync(ici->v4l2_dev.dev);
 
-	dev_info(icd->dev.parent,
+	dev_info(icd->parent,
 		 "SuperH Mobile CEU driver detached from camera %d\n",
 		 icd->devnum);
 
@@ -585,14 +615,14 @@
 /* rect is guaranteed to not exceed the scaled camera rectangle */
 static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	unsigned int height, width, cdwdr_width, in_width, in_height;
 	unsigned int left_offset, top_offset;
 	u32 camor;
 
-	dev_geo(icd->dev.parent, "Crop %ux%u@%u:%u\n",
+	dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
 		icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
 
 	left_offset	= cam->ceu_left;
@@ -641,7 +671,7 @@
 	}
 
 	/* CSI2 special configuration */
-	if (pcdev->pdata->csi2_dev) {
+	if (pcdev->pdata->csi2) {
 		in_width = ((in_width - 2) * 2);
 		left_offset *= 2;
 	}
@@ -649,7 +679,7 @@
 	/* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
 	camor = left_offset | (top_offset << 16);
 
-	dev_geo(icd->dev.parent,
+	dev_geo(icd->parent,
 		"CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
 		(in_height << 16) | in_width, (height << 16) | width,
 		cdwdr_width);
@@ -697,7 +727,7 @@
 static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
 				       __u32 pixfmt)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	int ret;
 	unsigned long camera_flags, common_flags, value;
@@ -783,7 +813,7 @@
 	value |= pcdev->is_16bit ? 1 << 12 : 0;
 
 	/* CSI2 mode */
-	if (pcdev->pdata->csi2_dev)
+	if (pcdev->pdata->csi2)
 		value |= 3 << 12;
 
 	ceu_write(pcdev, CAMCR, value);
@@ -806,7 +836,7 @@
 	sh_mobile_ceu_set_rect(icd);
 	mdelay(1);
 
-	dev_geo(icd->dev.parent, "CFLCR 0x%x\n", pcdev->cflcr);
+	dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
 	ceu_write(pcdev, CFLCR, pcdev->cflcr);
 
 	/*
@@ -829,7 +859,7 @@
 	ceu_write(pcdev, CDOCR, value);
 	ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
 
-	dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n",
+	dev_dbg(icd->parent, "S_FMT successful for %c%c%c%c %ux%u\n",
 		pixfmt & 0xff, (pixfmt >> 8) & 0xff,
 		(pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
 		icd->user_width, icd->user_height);
@@ -843,7 +873,7 @@
 static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
 				       unsigned char buswidth)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	unsigned long camera_flags, common_flags;
 
@@ -901,7 +931,7 @@
 				     struct soc_camera_format_xlate *xlate)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	int ret, k, n;
@@ -921,7 +951,7 @@
 		return 0;
 	}
 
-	if (!pcdev->pdata->csi2_dev) {
+	if (!pcdev->pdata->csi2) {
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
 			return 0;
@@ -1244,7 +1274,7 @@
 {
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
 	unsigned int max_width, max_height;
 	struct v4l2_cropcap cap;
@@ -1313,7 +1343,7 @@
 			bool ceu_can_scale)
 {
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	struct v4l2_mbus_framefmt mf_tmp = *mf;
 	unsigned int scale_h, scale_v;
 	int ret;
@@ -1363,13 +1393,13 @@
 				  struct v4l2_crop *a)
 {
 	struct v4l2_rect *rect = &a->c;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct v4l2_crop cam_crop;
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
 	struct v4l2_rect *cam_rect = &cam_crop.c;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct device *dev = icd->dev.parent;
 	struct v4l2_mbus_framefmt mf;
 	unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
 		out_width, out_height;
@@ -1511,7 +1541,7 @@
 		struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
 {
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
-	struct device *dev = icd->dev.parent;
+	struct device *dev = icd->parent;
 	struct v4l2_rect *cam_subrect = &cam->subrect;
 	unsigned int scale_v, scale_h;
 
@@ -1555,12 +1585,12 @@
 static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
 				 struct v4l2_format *f)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct device *dev = icd->parent;
+	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 	struct v4l2_mbus_framefmt mf;
-	struct device *dev = icd->dev.parent;
 	__u32 pixfmt = pix->pixelformat;
 	const struct soc_camera_format_xlate *xlate;
 	/* Keep Compiler Happy */
@@ -1684,12 +1714,12 @@
 	int width, height;
 	int ret;
 
-	dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
+	dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
 		 pixfmt, pix->width, pix->height);
 
 	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
 	if (!xlate) {
-		dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
+		dev_warn(icd->parent, "Format %x not found\n", pixfmt);
 		return -EINVAL;
 	}
 
@@ -1701,11 +1731,6 @@
 	width = pix->width;
 	height = pix->height;
 
-	pix->bytesperline = soc_mbus_bytes_per_line(width, xlate->host_fmt);
-	if ((int)pix->bytesperline < 0)
-		return pix->bytesperline;
-	pix->sizeimage = height * pix->bytesperline;
-
 	/* limit to sensor capabilities */
 	mf.width	= pix->width;
 	mf.height	= pix->height;
@@ -1741,7 +1766,7 @@
 							 try_mbus_fmt, &mf);
 			if (ret < 0) {
 				/* Shouldn't actually happen... */
-				dev_err(icd->dev.parent,
+				dev_err(icd->parent,
 					"FIXME: client try_fmt() = %d\n", ret);
 				return ret;
 			}
@@ -1753,7 +1778,7 @@
 			pix->height = height;
 	}
 
-	dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
+	dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
 		__func__, ret, pix->pixelformat, pix->width, pix->height);
 
 	return ret;
@@ -1763,7 +1788,7 @@
 				      struct v4l2_crop *a)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	u32 out_width = icd->user_width, out_height = icd->user_height;
 	int ret;
@@ -1775,13 +1800,13 @@
 	/* Stop the client */
 	ret = v4l2_subdev_call(sd, video, s_stream, 0);
 	if (ret < 0)
-		dev_warn(icd->dev.parent,
+		dev_warn(icd->parent,
 			 "Client failed to stop the stream: %d\n", ret);
 	else
 		/* Do the crop, if it fails, there's nothing more we can do */
 		sh_mobile_ceu_set_crop(icd, a);
 
-	dev_geo(icd->dev.parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
+	dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
 
 	if (icd->user_width != out_width || icd->user_height != out_height) {
 		struct v4l2_format f = {
@@ -1827,7 +1852,6 @@
 				  struct v4l2_capability *cap)
 {
 	strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
-	cap->version = KERNEL_VERSION(0, 0, 5);
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
 	return 0;
 }
@@ -1848,7 +1872,7 @@
 static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
 				  struct v4l2_control *ctrl)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 	u32 val;
 
@@ -1864,7 +1888,7 @@
 static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
 				  struct v4l2_control *ctrl)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
 
 	switch (ctrl->id) {
@@ -1950,7 +1974,7 @@
 		.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
 		.notifier.notifier_call = bus_notify,
 	};
-	struct device *csi2;
+	struct sh_mobile_ceu_companion *csi2;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	irq = platform_get_irq(pdev, 0);
@@ -2023,26 +2047,61 @@
 	pcdev->ici.drv_name = dev_name(&pdev->dev);
 	pcdev->ici.ops = &sh_mobile_ceu_host_ops;
 
+	pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(pcdev->alloc_ctx)) {
+		err = PTR_ERR(pcdev->alloc_ctx);
+		goto exit_free_clk;
+	}
+
+	err = soc_camera_host_register(&pcdev->ici);
+	if (err)
+		goto exit_free_ctx;
+
 	/* CSI2 interfacing */
-	csi2 = pcdev->pdata->csi2_dev;
+	csi2 = pcdev->pdata->csi2;
 	if (csi2) {
-		wait.dev = csi2;
+		struct platform_device *csi2_pdev =
+			platform_device_alloc("sh-mobile-csi2", csi2->id);
+		struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
+
+		if (!csi2_pdev) {
+			err = -ENOMEM;
+			goto exit_host_unregister;
+		}
+
+		pcdev->csi2_pdev		= csi2_pdev;
+
+		err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+		if (err < 0)
+			goto exit_pdev_put;
+
+		csi2_pdata			= csi2_pdev->dev.platform_data;
+		csi2_pdata->v4l2_dev		= &pcdev->ici.v4l2_dev;
+
+		csi2_pdev->resource		= csi2->resource;
+		csi2_pdev->num_resources	= csi2->num_resources;
+
+		err = platform_device_add(csi2_pdev);
+		if (err < 0)
+			goto exit_pdev_put;
+
+		wait.dev = &csi2_pdev->dev;
 
 		err = bus_register_notifier(&platform_bus_type, &wait.notifier);
 		if (err < 0)
-			goto exit_free_clk;
+			goto exit_pdev_unregister;
 
 		/*
 		 * From this point the driver module will not unload, until
 		 * we complete the completion.
 		 */
 
-		if (!csi2->driver) {
+		if (!csi2_pdev->dev.driver) {
 			complete(&wait.completion);
 			/* Either too late, or probing failed */
 			bus_unregister_notifier(&platform_bus_type, &wait.notifier);
 			err = -ENXIO;
-			goto exit_free_clk;
+			goto exit_pdev_unregister;
 		}
 
 		/*
@@ -2051,34 +2110,28 @@
 		 * the "owner" is safe!
 		 */
 
-		err = try_module_get(csi2->driver->owner);
+		err = try_module_get(csi2_pdev->dev.driver->owner);
 
 		/* Let notifier complete, if it has been locked */
 		complete(&wait.completion);
 		bus_unregister_notifier(&platform_bus_type, &wait.notifier);
 		if (!err) {
 			err = -ENODEV;
-			goto exit_free_clk;
+			goto exit_pdev_unregister;
 		}
 	}
 
-	pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-	if (IS_ERR(pcdev->alloc_ctx)) {
-		err = PTR_ERR(pcdev->alloc_ctx);
-		goto exit_module_put;
-	}
-
-	err = soc_camera_host_register(&pcdev->ici);
-	if (err)
-		goto exit_free_ctx;
-
 	return 0;
 
+exit_pdev_unregister:
+	platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+	pcdev->csi2_pdev->resource = NULL;
+	platform_device_put(pcdev->csi2_pdev);
+exit_host_unregister:
+	soc_camera_host_unregister(&pcdev->ici);
 exit_free_ctx:
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-exit_module_put:
-	if (csi2 && csi2->driver)
-		module_put(csi2->driver->owner);
 exit_free_clk:
 	pm_runtime_disable(&pdev->dev);
 	free_irq(pcdev->irq, pcdev);
@@ -2098,7 +2151,7 @@
 	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
 	struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
 					struct sh_mobile_ceu_dev, ici);
-	struct device *csi2 = pcdev->pdata->csi2_dev;
+	struct platform_device *csi2_pdev = pcdev->csi2_pdev;
 
 	soc_camera_host_unregister(soc_host);
 	pm_runtime_disable(&pdev->dev);
@@ -2107,8 +2160,13 @@
 		dma_release_declared_memory(&pdev->dev);
 	iounmap(pcdev->base);
 	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-	if (csi2 && csi2->driver)
-		module_put(csi2->driver->owner);
+	if (csi2_pdev && csi2_pdev->dev.driver) {
+		struct module *csi2_drv = csi2_pdev->dev.driver->owner;
+		platform_device_del(csi2_pdev);
+		csi2_pdev->resource = NULL;
+		platform_device_put(csi2_pdev);
+		module_put(csi2_drv);
+	}
 	kfree(pcdev);
 
 	return 0;
@@ -2158,4 +2216,5 @@
 MODULE_DESCRIPTION("SuperH Mobile CEU driver");
 MODULE_AUTHOR("Magnus Damm");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.6");
 MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
index 98b8748..2893a01 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 
+#include <media/sh_mobile_ceu.h>
 #include <media/sh_mobile_csi2.h>
 #include <media/soc_camera.h>
 #include <media/v4l2-common.h>
@@ -33,7 +34,6 @@
 struct sh_csi2 {
 	struct v4l2_subdev		subdev;
 	struct list_head		list;
-	struct notifier_block		notifier;
 	unsigned int			irq;
 	void __iomem			*base;
 	struct platform_device		*pdev;
@@ -132,13 +132,6 @@
 	.try_mbus_fmt	= sh_csi2_try_fmt,
 };
 
-static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops;
-
-static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
-	.core	= &sh_csi2_subdev_core_ops,
-	.video	= &sh_csi2_subdev_video_ops,
-};
-
 static void sh_csi2_hwinit(struct sh_csi2 *priv)
 {
 	struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
@@ -186,65 +179,84 @@
 	return soc_camera_apply_sensor_flags(icl, flags);
 }
 
-static int sh_csi2_notify(struct notifier_block *nb,
-			  unsigned long action, void *data)
+static int sh_csi2_client_connect(struct sh_csi2 *priv)
 {
-	struct device *dev = data;
-	struct soc_camera_device *icd = to_soc_camera_dev(dev);
-	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent);
-	struct sh_csi2 *priv =
-		container_of(nb, struct sh_csi2, notifier);
 	struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
-	int ret, i;
+	struct v4l2_subdev *sd, *csi2_sd = &priv->subdev;
+	struct soc_camera_device *icd = NULL;
+	struct device *dev = v4l2_get_subdevdata(&priv->subdev);
+	int i;
+
+	v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev)
+		if (sd->grp_id) {
+			icd = (struct soc_camera_device *)sd->grp_id;
+			break;
+		}
+
+	if (!icd)
+		return -EINVAL;
 
 	for (i = 0; i < pdata->num_clients; i++)
 		if (&pdata->clients[i].pdev->dev == icd->pdev)
 			break;
 
-	dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i);
+	dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
 
 	if (i == pdata->num_clients)
-		return NOTIFY_DONE;
+		return -ENODEV;
 
-	switch (action) {
-	case BUS_NOTIFY_BOUND_DRIVER:
-		snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s",
-			 dev_name(v4l2_dev->dev), ".mipi-csi");
-		priv->subdev.grp_id = (long)icd;
-		ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev);
-		dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
-		if (ret < 0)
-			return NOTIFY_DONE;
+	priv->client = pdata->clients + i;
 
-		priv->client = pdata->clients + i;
+	priv->set_bus_param		= icd->ops->set_bus_param;
+	priv->query_bus_param		= icd->ops->query_bus_param;
+	icd->ops->set_bus_param		= sh_csi2_set_bus_param;
+	icd->ops->query_bus_param	= sh_csi2_query_bus_param;
 
-		priv->set_bus_param		= icd->ops->set_bus_param;
-		priv->query_bus_param		= icd->ops->query_bus_param;
-		icd->ops->set_bus_param		= sh_csi2_set_bus_param;
-		icd->ops->query_bus_param	= sh_csi2_query_bus_param;
+	csi2_sd->grp_id = (long)icd;
 
-		pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev));
+	pm_runtime_get_sync(dev);
 
-		sh_csi2_hwinit(priv);
-		break;
-	case BUS_NOTIFY_UNBIND_DRIVER:
-		priv->client = NULL;
+	sh_csi2_hwinit(priv);
 
-		/* Driver is about to be unbound */
-		icd->ops->set_bus_param		= priv->set_bus_param;
-		icd->ops->query_bus_param	= priv->query_bus_param;
-		priv->set_bus_param		= NULL;
-		priv->query_bus_param		= NULL;
-
-		v4l2_device_unregister_subdev(&priv->subdev);
-
-		pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
-		break;
-	}
-
-	return NOTIFY_OK;
+	return 0;
 }
 
+static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
+{
+	struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+
+	priv->client = NULL;
+	priv->subdev.grp_id = 0;
+
+	/* Driver is about to be unbound */
+	icd->ops->set_bus_param		= priv->set_bus_param;
+	icd->ops->query_bus_param	= priv->query_bus_param;
+	priv->set_bus_param		= NULL;
+	priv->query_bus_param		= NULL;
+
+	pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
+}
+
+static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+	if (on)
+		return sh_csi2_client_connect(priv);
+
+	sh_csi2_client_disconnect(priv);
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
+	.s_power	= sh_csi2_s_power,
+};
+
+static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
+	.core	= &sh_csi2_subdev_core_ops,
+	.video	= &sh_csi2_subdev_video_ops,
+};
+
 static __devinit int sh_csi2_probe(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -274,14 +286,6 @@
 		return -ENOMEM;
 
 	priv->irq = irq;
-	priv->notifier.notifier_call = sh_csi2_notify;
-
-	/* We MUST attach after the MIPI sensor */
-	ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "CSI2 cannot register notifier\n");
-		goto ernotify;
-	}
 
 	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
 		dev_err(&pdev->dev, "CSI2 register region already claimed\n");
@@ -297,11 +301,17 @@
 	}
 
 	priv->pdev = pdev;
+	platform_set_drvdata(pdev, priv);
 
 	v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
 	v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
 
-	platform_set_drvdata(pdev, priv);
+	snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
+		 dev_name(pdata->v4l2_dev->dev));
+	ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
+	dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+	if (ret < 0)
+		goto esdreg;
 
 	pm_runtime_enable(&pdev->dev);
 
@@ -309,11 +319,11 @@
 
 	return 0;
 
+esdreg:
+	iounmap(priv->base);
 eremap:
 	release_mem_region(res->start, resource_size(res));
 ereqreg:
-	bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
-ernotify:
 	kfree(priv);
 
 	return ret;
@@ -324,7 +334,7 @@
 	struct sh_csi2 *priv = platform_get_drvdata(pdev);
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-	bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
+	v4l2_device_unregister_subdev(&priv->subdev);
 	pm_runtime_disable(&pdev->dev);
 	iounmap(priv->base);
 	release_mem_region(res->start, resource_size(res));
@@ -335,8 +345,9 @@
 }
 
 static struct platform_driver __refdata sh_csi2_pdrv = {
-	.remove  = __devexit_p(sh_csi2_remove),
-	.driver  = {
+	.remove	= __devexit_p(sh_csi2_remove),
+	.probe	= sh_csi2_probe,
+	.driver	= {
 		.name	= "sh-mobile-csi2",
 		.owner	= THIS_MODULE,
 	},
@@ -344,7 +355,7 @@
 
 static int __init sh_csi2_init(void)
 {
-	return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe);
+	return platform_driver_register(&sh_csi2_pdrv);
 }
 
 static void __exit sh_csi2_exit(void)
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
index 07cf0c6..6a72987 100644
--- a/drivers/media/video/sh_vou.c
+++ b/drivers/media/video/sh_vou.c
@@ -19,7 +19,6 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 
 #include <media/sh_vou.h>
@@ -393,7 +392,6 @@
 	dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
 
 	strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
-	cap->version = KERNEL_VERSION(0, 1, 0);
 	cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
 	return 0;
 }
@@ -1490,4 +1488,5 @@
 MODULE_DESCRIPTION("SuperH VOU driver");
 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
 MODULE_ALIAS("platform:sh-vou");
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
index cbfc444..22ea211 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -21,7 +21,6 @@
 #ifndef _SN9C102_H_
 #define _SN9C102_H_
 
-#include <linux/version.h>
 #include <linux/usb.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 0e07c49..16cb07c5 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -33,6 +33,7 @@
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
+#include <linux/version.h>
 #include <linux/page-flags.h>
 #include <asm/byteorder.h>
 #include <asm/page.h>
@@ -47,8 +48,7 @@
 #define SN9C102_MODULE_AUTHOR   "(C) 2004-2007 Luca Risolia"
 #define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
 #define SN9C102_MODULE_LICENSE  "GPL"
-#define SN9C102_MODULE_VERSION  "1:1.47pre49"
-#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 1, 47)
+#define SN9C102_MODULE_VERSION  "1:1.48"
 
 /*****************************************************************************/
 
@@ -2158,7 +2158,7 @@
 {
 	struct v4l2_capability cap = {
 		.driver = "sn9c102",
-		.version = SN9C102_MODULE_VERSION_CODE,
+		.version = LINUX_VERSION_CODE,
 		.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
 				V4L2_CAP_STREAMING,
 	};
@@ -3187,16 +3187,8 @@
 	case VIDIOC_S_AUDIO:
 		return sn9c102_vidioc_s_audio(cam, arg);
 
-	case VIDIOC_G_STD:
-	case VIDIOC_S_STD:
-	case VIDIOC_QUERYSTD:
-	case VIDIOC_ENUMSTD:
-	case VIDIOC_QUERYMENU:
-	case VIDIOC_ENUM_FRAMEINTERVALS:
-		return -EINVAL;
-
 	default:
-		return -EINVAL;
+		return -ENOTTY;
 
 	}
 }
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 4e4d412..5bdfe7e 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -60,14 +60,14 @@
 		ret = regulator_bulk_enable(icl->num_regulators,
 					    icl->regulators);
 		if (ret < 0) {
-			dev_err(&icd->dev, "Cannot enable regulators\n");
+			dev_err(icd->pdev, "Cannot enable regulators\n");
 			return ret;
 		}
 
 		if (icl->power)
 			ret = icl->power(icd->pdev, power_on);
 		if (ret < 0) {
-			dev_err(&icd->dev,
+			dev_err(icd->pdev,
 				"Platform failed to power-on the camera.\n");
 
 			regulator_bulk_disable(icl->num_regulators,
@@ -79,7 +79,7 @@
 		if (icl->power)
 			ret = icl->power(icd->pdev, 0);
 		if (ret < 0) {
-			dev_err(&icd->dev,
+			dev_err(icd->pdev,
 				"Platform failed to power-off the camera.\n");
 			return ret;
 		}
@@ -87,7 +87,7 @@
 		ret = regulator_bulk_disable(icl->num_regulators,
 					     icl->regulators);
 		if (ret < 0) {
-			dev_err(&icd->dev, "Cannot disable regulators\n");
+			dev_err(icd->pdev, "Cannot disable regulators\n");
 			return ret;
 		}
 	}
@@ -147,11 +147,11 @@
 static int soc_camera_try_fmt(struct soc_camera_device *icd,
 			      struct v4l2_format *f)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 	int ret;
 
-	dev_dbg(&icd->dev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
+	dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
 		pixfmtstr(pix->pixelformat), pix->width, pix->height);
 
 	pix->bytesperline = 0;
@@ -199,22 +199,15 @@
 static int soc_camera_enum_input(struct file *file, void *priv,
 				 struct v4l2_input *inp)
 {
-	struct soc_camera_device *icd = file->private_data;
-	int ret = 0;
-
 	if (inp->index != 0)
 		return -EINVAL;
 
-	if (icd->ops->enum_input)
-		ret = icd->ops->enum_input(icd, inp);
-	else {
-		/* default is camera */
-		inp->type = V4L2_INPUT_TYPE_CAMERA;
-		inp->std  = V4L2_STD_UNKNOWN;
-		strcpy(inp->name, "Camera");
-	}
+	/* default is camera */
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std  = V4L2_STD_UNKNOWN;
+	strcpy(inp->name, "Camera");
 
-	return ret;
+	return 0;
 }
 
 static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
@@ -244,7 +237,7 @@
 					 struct v4l2_frmsizeenum *fsize)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	return ici->ops->enum_fsizes(icd, fsize);
 }
@@ -254,7 +247,7 @@
 {
 	int ret;
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -281,7 +274,7 @@
 			       struct v4l2_buffer *p)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -295,7 +288,7 @@
 			   struct v4l2_buffer *p)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -312,7 +305,7 @@
 			    struct v4l2_buffer *p)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -329,7 +322,7 @@
 static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	unsigned int i, fmts = 0, raw_fmts = 0;
 	int ret;
 	enum v4l2_mbus_pixelcode code;
@@ -363,7 +356,7 @@
 	if (!icd->user_formats)
 		return -ENOMEM;
 
-	dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts);
+	dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts);
 
 	/* Second pass - actually fill data formats */
 	fmts = 0;
@@ -395,7 +388,7 @@
 /* Always entered with .video_lock held */
 static void soc_camera_free_user_formats(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	if (ici->ops->put_formats)
 		ici->ops->put_formats(icd);
@@ -409,11 +402,11 @@
 static int soc_camera_set_fmt(struct soc_camera_device *icd,
 			      struct v4l2_format *f)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 	int ret;
 
-	dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n",
+	dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n",
 		pixfmtstr(pix->pixelformat), pix->width, pix->height);
 
 	/* We always call try_fmt() before set_fmt() or set_crop() */
@@ -426,7 +419,7 @@
 		return ret;
 	} else if (!icd->current_fmt ||
 		   icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
-		dev_err(&icd->dev,
+		dev_err(icd->pdev,
 			"Host driver hasn't set up current format correctly!\n");
 		return -EINVAL;
 	}
@@ -440,7 +433,7 @@
 	if (ici->ops->init_videobuf)
 		icd->vb_vidq.field = pix->field;
 
-	dev_dbg(&icd->dev, "set width: %d height: %d\n",
+	dev_dbg(icd->pdev, "set width: %d height: %d\n",
 		icd->user_width, icd->user_height);
 
 	/* set physical bus parameters */
@@ -450,9 +443,7 @@
 static int soc_camera_open(struct file *file)
 {
 	struct video_device *vdev = video_devdata(file);
-	struct soc_camera_device *icd = container_of(vdev->parent,
-						     struct soc_camera_device,
-						     dev);
+	struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 	struct soc_camera_host *ici;
 	int ret;
@@ -461,10 +452,10 @@
 		/* No device driver attached */
 		return -ENODEV;
 
-	ici = to_soc_camera_host(icd->dev.parent);
+	ici = to_soc_camera_host(icd->parent);
 
 	if (!try_module_get(ici->ops->owner)) {
-		dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
+		dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
 		return -EINVAL;
 	}
 
@@ -495,7 +486,7 @@
 
 		ret = ici->ops->add(icd);
 		if (ret < 0) {
-			dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
+			dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
 			goto eiciadd;
 		}
 
@@ -524,7 +515,7 @@
 	}
 
 	file->private_data = icd;
-	dev_dbg(&icd->dev, "camera device open\n");
+	dev_dbg(icd->pdev, "camera device open\n");
 
 	return 0;
 
@@ -549,7 +540,7 @@
 static int soc_camera_close(struct file *file)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	icd->use_count--;
 	if (!icd->use_count) {
@@ -570,7 +561,7 @@
 
 	module_put(ici->ops->owner);
 
-	dev_dbg(&icd->dev, "camera device close\n");
+	dev_dbg(icd->pdev, "camera device close\n");
 
 	return 0;
 }
@@ -581,7 +572,7 @@
 	struct soc_camera_device *icd = file->private_data;
 	int err = -EINVAL;
 
-	dev_err(&icd->dev, "camera device read not implemented\n");
+	dev_err(icd->pdev, "camera device read not implemented\n");
 
 	return err;
 }
@@ -589,10 +580,10 @@
 static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	int err;
 
-	dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+	dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
 
 	if (icd->streamer != file)
 		return -EBUSY;
@@ -602,7 +593,7 @@
 	else
 		err = vb2_mmap(&icd->vb2_vidq, vma);
 
-	dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n",
+	dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
 		(unsigned long)vma->vm_start,
 		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
 		err);
@@ -613,13 +604,13 @@
 static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	if (icd->streamer != file)
 		return -EBUSY;
 
 	if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) {
-		dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
+		dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
 		return POLLERR;
 	}
 
@@ -659,15 +650,15 @@
 	WARN_ON(priv != file->private_data);
 
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		dev_warn(&icd->dev, "Wrong buf-type %d\n", f->type);
+		dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type);
 		return -EINVAL;
 	}
 
 	if (icd->streamer && icd->streamer != file)
 		return -EBUSY;
 
-	if (is_streaming(to_soc_camera_host(icd->dev.parent), icd)) {
-		dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
+	if (is_streaming(to_soc_camera_host(icd->parent), icd)) {
+		dev_err(icd->pdev, "S_FMT denied: queue initialised\n");
 		return -EBUSY;
 	}
 
@@ -716,7 +707,7 @@
 	pix->field		= icd->field;
 	pix->pixelformat	= icd->current_fmt->host_fmt->fourcc;
 	pix->colorspace		= icd->colorspace;
-	dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n",
+	dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n",
 		icd->current_fmt->host_fmt->fourcc);
 	return 0;
 }
@@ -725,7 +716,7 @@
 			       struct v4l2_capability *cap)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -737,7 +728,7 @@
 			       enum v4l2_buf_type i)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
 
@@ -766,7 +757,7 @@
 {
 	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	WARN_ON(priv != file->private_data);
 
@@ -794,7 +785,7 @@
 				struct v4l2_queryctrl *qc)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	int i;
 
 	WARN_ON(priv != file->private_data);
@@ -825,7 +816,7 @@
 			     struct v4l2_control *ctrl)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
 
@@ -844,7 +835,7 @@
 			     struct v4l2_control *ctrl)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
 
@@ -863,7 +854,7 @@
 			      struct v4l2_cropcap *a)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	return ici->ops->cropcap(icd, a);
 }
@@ -872,7 +863,7 @@
 			     struct v4l2_crop *a)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	int ret;
 
 	ret = ici->ops->get_crop(icd, a);
@@ -889,7 +880,7 @@
 			     struct v4l2_crop *a)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct v4l2_rect *rect = &a->c;
 	struct v4l2_crop current_crop;
 	int ret;
@@ -897,7 +888,7 @@
 	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
+	dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
 		rect->width, rect->height, rect->left, rect->top);
 
 	/* If get_crop fails, we'll let host and / or client drivers decide */
@@ -905,7 +896,7 @@
 
 	/* Prohibit window size change with initialised buffers */
 	if (ret < 0) {
-		dev_err(&icd->dev,
+		dev_err(icd->pdev,
 			"S_CROP denied: getting current crop failed\n");
 	} else if ((a->c.width == current_crop.c.width &&
 		    a->c.height == current_crop.c.height) ||
@@ -915,7 +906,7 @@
 	} else if (ici->ops->set_livecrop) {
 		ret = ici->ops->set_livecrop(icd, a);
 	} else {
-		dev_err(&icd->dev,
+		dev_err(icd->pdev,
 			"S_CROP denied: queue initialised and sizes differ\n");
 		ret = -EBUSY;
 	}
@@ -927,7 +918,7 @@
 			     struct v4l2_streamparm *a)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	if (ici->ops->get_parm)
 		return ici->ops->get_parm(icd, a);
@@ -939,7 +930,7 @@
 			     struct v4l2_streamparm *a)
 {
 	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 
 	if (ici->ops->set_parm)
 		return ici->ops->set_parm(icd, a);
@@ -976,6 +967,8 @@
 }
 #endif
 
+static int soc_camera_probe(struct soc_camera_device *icd);
+
 /* So far this function cannot fail */
 static void scan_add_host(struct soc_camera_host *ici)
 {
@@ -986,15 +979,9 @@
 	list_for_each_entry(icd, &devices, list) {
 		if (icd->iface == ici->nr) {
 			int ret;
-			icd->dev.parent = ici->v4l2_dev.dev;
-			dev_set_name(&icd->dev, "%u-%u", icd->iface,
-				     icd->devnum);
-			ret = device_register(&icd->dev);
-			if (ret < 0) {
-				icd->dev.parent = NULL;
-				dev_err(&icd->dev,
-					"Cannot register device: %d\n", ret);
-			}
+
+			icd->parent = ici->v4l2_dev.dev;
+			ret = soc_camera_probe(icd);
 		}
 	}
 
@@ -1006,12 +993,12 @@
 			       struct soc_camera_link *icl)
 {
 	struct i2c_client *client;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
 	struct v4l2_subdev *subdev;
 
 	if (!adap) {
-		dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n",
+		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
 			icl->i2c_adapter_id);
 		goto ei2cga;
 	}
@@ -1026,7 +1013,7 @@
 	client = v4l2_get_subdevdata(subdev);
 
 	/* Use to_i2c_client(dev) to recover the i2c client */
-	dev_set_drvdata(&icd->dev, &client->dev);
+	icd->control = &client->dev;
 
 	return 0;
 ei2cnd:
@@ -1040,7 +1027,8 @@
 	struct i2c_client *client =
 		to_i2c_client(to_soc_camera_control(icd));
 	struct i2c_adapter *adap = client->adapter;
-	dev_set_drvdata(&icd->dev, NULL);
+
+	icd->control = NULL;
 	v4l2_device_unregister_subdev(i2c_get_clientdata(client));
 	i2c_unregister_device(client);
 	i2c_put_adapter(adap);
@@ -1053,17 +1041,16 @@
 static int soc_camera_video_start(struct soc_camera_device *icd);
 static int video_dev_create(struct soc_camera_device *icd);
 /* Called during host-driver probe */
-static int soc_camera_probe(struct device *dev)
+static int soc_camera_probe(struct soc_camera_device *icd)
 {
-	struct soc_camera_device *icd = to_soc_camera_dev(dev);
-	struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 	struct device *control = NULL;
 	struct v4l2_subdev *sd;
 	struct v4l2_mbus_framefmt mf;
 	int ret;
 
-	dev_info(dev, "Probing %s\n", dev_name(dev));
+	dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
 
 	ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
 				 icl->regulators);
@@ -1099,7 +1086,7 @@
 		if (icl->module_name)
 			ret = request_module(icl->module_name);
 
-		ret = icl->add_device(icl, &icd->dev);
+		ret = icl->add_device(icd);
 		if (ret < 0)
 			goto eadddev;
 
@@ -1110,7 +1097,7 @@
 		control = to_soc_camera_control(icd);
 		if (!control || !control->driver || !dev_get_drvdata(control) ||
 		    !try_module_get(control->driver->owner)) {
-			icl->del_device(icl);
+			icl->del_device(icd);
 			goto enodrv;
 		}
 	}
@@ -1125,8 +1112,6 @@
 
 	icd->field = V4L2_FIELD_ANY;
 
-	icd->vdev->lock = &icd->video_lock;
-
 	/*
 	 * ..._video_start() will create a device node, video_register_device()
 	 * itself is protected against concurrent open() calls, but we also have
@@ -1146,11 +1131,6 @@
 		icd->field		= mf.field;
 	}
 
-	/* Do we have to sysfs_remove_link() before device_unregister()? */
-	if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
-			      "control"))
-		dev_warn(&icd->dev, "Failed creating the control symlink\n");
-
 	ici->ops->remove(icd);
 
 	soc_camera_power_set(icd, icl, 0);
@@ -1166,7 +1146,7 @@
 	if (icl->board_info) {
 		soc_camera_free_i2c(icd);
 	} else {
-		icl->del_device(icl);
+		icl->del_device(icd);
 		module_put(control->driver->owner);
 	}
 enodrv:
@@ -1186,13 +1166,12 @@
  * This is called on device_unregister, which only means we have to disconnect
  * from the host, but not remove ourselves from the device list
  */
-static int soc_camera_remove(struct device *dev)
+static int soc_camera_remove(struct soc_camera_device *icd)
 {
-	struct soc_camera_device *icd = to_soc_camera_dev(dev);
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 	struct video_device *vdev = icd->vdev;
 
-	BUG_ON(!dev->parent);
+	BUG_ON(!icd->parent);
 
 	if (vdev) {
 		video_unregister_device(vdev);
@@ -1202,10 +1181,9 @@
 	if (icl->board_info) {
 		soc_camera_free_i2c(icd);
 	} else {
-		struct device_driver *drv = to_soc_camera_control(icd) ?
-			to_soc_camera_control(icd)->driver : NULL;
+		struct device_driver *drv = to_soc_camera_control(icd)->driver;
 		if (drv) {
-			icl->del_device(icl);
+			icl->del_device(icd);
 			module_put(drv->owner);
 		}
 	}
@@ -1216,49 +1194,6 @@
 	return 0;
 }
 
-static int soc_camera_suspend(struct device *dev, pm_message_t state)
-{
-	struct soc_camera_device *icd = to_soc_camera_dev(dev);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-	int ret = 0;
-
-	if (ici->ops->suspend)
-		ret = ici->ops->suspend(icd, state);
-
-	return ret;
-}
-
-static int soc_camera_resume(struct device *dev)
-{
-	struct soc_camera_device *icd = to_soc_camera_dev(dev);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
-	int ret = 0;
-
-	if (ici->ops->resume)
-		ret = ici->ops->resume(icd);
-
-	return ret;
-}
-
-struct bus_type soc_camera_bus_type = {
-	.name		= "soc-camera",
-	.probe		= soc_camera_probe,
-	.remove		= soc_camera_remove,
-	.suspend	= soc_camera_suspend,
-	.resume		= soc_camera_resume,
-};
-EXPORT_SYMBOL_GPL(soc_camera_bus_type);
-
-static struct device_driver ic_drv = {
-	.name	= "camera",
-	.bus	= &soc_camera_bus_type,
-	.owner	= THIS_MODULE,
-};
-
-static void dummy_release(struct device *dev)
-{
-}
-
 static int default_cropcap(struct soc_camera_device *icd,
 			   struct v4l2_cropcap *a)
 {
@@ -1317,13 +1252,6 @@
 	return 0;
 }
 
-static void soc_camera_device_init(struct device *dev, void *pdata)
-{
-	dev->platform_data	= pdata;
-	dev->bus		= &soc_camera_bus_type;
-	dev->release		= dummy_release;
-}
-
 int soc_camera_host_register(struct soc_camera_host *ici)
 {
 	struct soc_camera_host *ix;
@@ -1389,24 +1317,9 @@
 	mutex_lock(&list_lock);
 
 	list_del(&ici->list);
-
-	list_for_each_entry(icd, &devices, list) {
-		if (icd->iface == ici->nr) {
-			void *pdata = icd->dev.platform_data;
-			/* The bus->remove will be called */
-			device_unregister(&icd->dev);
-			/*
-			 * Not before device_unregister(), .remove
-			 * needs parent to call ici->ops->remove().
-			 * If the host module is loaded again, device_register()
-			 * would complain "already initialised," since 2.6.32
-			 * this is also needed to prevent use-after-free of the
-			 * device private data.
-			 */
-			memset(&icd->dev, 0, sizeof(icd->dev));
-			soc_camera_device_init(&icd->dev, pdata);
-		}
-	}
+	list_for_each_entry(icd, &devices, list)
+		if (icd->iface == ici->nr && to_soc_camera_control(icd))
+			soc_camera_remove(icd);
 
 	mutex_unlock(&list_lock);
 
@@ -1448,11 +1361,6 @@
 	return 0;
 }
 
-static void soc_camera_device_unregister(struct soc_camera_device *icd)
-{
-	list_del(&icd->list);
-}
-
 static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
 	.vidioc_querycap	 = soc_camera_querycap,
 	.vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,
@@ -1487,7 +1395,7 @@
 
 static int video_dev_create(struct soc_camera_device *icd)
 {
-	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct video_device *vdev = video_device_alloc();
 
 	if (!vdev)
@@ -1495,12 +1403,13 @@
 
 	strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
 
-	vdev->parent		= &icd->dev;
+	vdev->parent		= icd->pdev;
 	vdev->current_norm	= V4L2_STD_UNKNOWN;
 	vdev->fops		= &soc_camera_fops;
 	vdev->ioctl_ops		= &soc_camera_ioctl_ops;
 	vdev->release		= video_device_release;
 	vdev->tvnorms		= V4L2_STD_UNKNOWN;
+	vdev->lock		= &icd->video_lock;
 
 	icd->vdev = vdev;
 
@@ -1515,7 +1424,7 @@
 	const struct device_type *type = icd->vdev->dev.type;
 	int ret;
 
-	if (!icd->dev.parent)
+	if (!icd->parent)
 		return -ENODEV;
 
 	if (!icd->ops ||
@@ -1525,7 +1434,7 @@
 
 	ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
 	if (ret < 0) {
-		dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
+		dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
 		return ret;
 	}
 
@@ -1549,6 +1458,7 @@
 		return -ENOMEM;
 
 	icd->iface = icl->bus_id;
+	icd->link = icl;
 	icd->pdev = &pdev->dev;
 	platform_set_drvdata(pdev, icd);
 
@@ -1556,8 +1466,6 @@
 	if (ret < 0)
 		goto escdevreg;
 
-	soc_camera_device_init(&icd->dev, icl);
-
 	icd->user_width		= DEFAULT_WIDTH;
 	icd->user_height	= DEFAULT_HEIGHT;
 
@@ -1581,7 +1489,7 @@
 	if (!icd)
 		return -EINVAL;
 
-	soc_camera_device_unregister(icd);
+	list_del(&icd->list);
 
 	kfree(icd);
 
@@ -1598,31 +1506,12 @@
 
 static int __init soc_camera_init(void)
 {
-	int ret = bus_register(&soc_camera_bus_type);
-	if (ret)
-		return ret;
-	ret = driver_register(&ic_drv);
-	if (ret)
-		goto edrvr;
-
-	ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
-	if (ret)
-		goto epdr;
-
-	return 0;
-
-epdr:
-	driver_unregister(&ic_drv);
-edrvr:
-	bus_unregister(&soc_camera_bus_type);
-	return ret;
+	return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
 }
 
 static void __exit soc_camera_exit(void)
 {
 	platform_driver_unregister(&soc_camera_pdrv);
-	driver_unregister(&ic_drv);
-	bus_unregister(&soc_camera_bus_type);
 }
 
 module_init(soc_camera_init);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index bf406e8..8069cd6 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -146,7 +146,7 @@
 	if (!p)
 		return -EINVAL;
 
-	if (!p->dev) {
+	if (!p->icd) {
 		dev_err(&pdev->dev,
 			"Platform has not set soc_camera_device pointer!\n");
 		return -EINVAL;
@@ -156,16 +156,16 @@
 	if (!priv)
 		return -ENOMEM;
 
-	icd = to_soc_camera_dev(p->dev);
+	icd = p->icd;
 
 	/* soc-camera convention: control's drvdata points to the subdev */
 	platform_set_drvdata(pdev, &priv->subdev);
 	/* Set the control device reference */
-	dev_set_drvdata(&icd->dev, &pdev->dev);
+	icd->control = &pdev->dev;
 
 	icd->ops = &soc_camera_platform_ops;
 
-	ici = to_soc_camera_host(icd->dev.parent);
+	ici = to_soc_camera_host(icd->parent);
 
 	v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
 	v4l2_set_subdevdata(&priv->subdev, p);
@@ -188,7 +188,7 @@
 {
 	struct soc_camera_platform_priv *priv = get_priv(pdev);
 	struct soc_camera_platform_info *p = pdev->dev.platform_data;
-	struct soc_camera_device *icd = to_soc_camera_dev(p->dev);
+	struct soc_camera_device *icd = p->icd;
 
 	v4l2_device_unregister_subdev(&priv->subdev);
 	icd->ops = NULL;
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
index c901721..8afb0e8 100644
--- a/drivers/media/video/sr030pc30.c
+++ b/drivers/media/video/sr030pc30.c
@@ -726,8 +726,10 @@
 	const struct sr030pc30_platform_data *pdata = info->pdata;
 	int ret;
 
-	if (WARN(pdata == NULL, "No platform data!\n"))
-		return -ENOMEM;
+	if (pdata == NULL) {
+		WARN(1, "No platform data!\n");
+		return -EINVAL;
+	}
 
 	/*
 	 * Put sensor into power sleep mode before switching off
@@ -746,6 +748,7 @@
 	if (on) {
 		ret = sr030pc30_base_config(sd);
 	} else {
+		ret = 0;
 		info->curr_win = NULL;
 		info->curr_fmt = NULL;
 	}
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 3941f95..bd21854 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -49,10 +49,11 @@
 static int loudness; /* disable loudness by default */
 static int debug;	 /* insmod parameter */
 module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0).");
 module_param(loudness, int, S_IRUGO);
-MODULE_PARM_DESC(maxvol,"Set maximium volume to +20db (0), default is 0db(1)");
+MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0).");
 module_param(maxvol, int, S_IRUGO | S_IWUSR);
-
+MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0).");
 
 
 /* Structure of address and subaddresses for the tda7432 */
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
index fc611eb..84cd1b6 100644
--- a/drivers/media/video/timblogiw.c
+++ b/drivers/media/video/timblogiw.c
@@ -20,7 +20,6 @@
  * Timberdale FPGA LogiWin Video In
  */
 
-#include <linux/version.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dmaengine.h>
diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h
index 46066bd..56564e6 100644
--- a/drivers/media/video/tlg2300/pd-common.h
+++ b/drivers/media/video/tlg2300/pd-common.h
@@ -1,7 +1,6 @@
 #ifndef PD_COMMON_H
 #define PD_COMMON_H
 
-#include <linux/version.h>
 #include <linux/fs.h>
 #include <linux/wait.h>
 #include <linux/list.h>
diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c
index edd78f8..d0da11a 100644
--- a/drivers/media/video/tlg2300/pd-dvb.c
+++ b/drivers/media/video/tlg2300/pd-dvb.c
@@ -7,7 +7,7 @@
 
 #include "vendorcmds.h"
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
 
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c
index 99c81a9..129f135 100644
--- a/drivers/media/video/tlg2300/pd-main.c
+++ b/drivers/media/video/tlg2300/pd-main.c
@@ -531,3 +531,4 @@
 MODULE_AUTHOR("Telegent Systems");
 MODULE_DESCRIPTION("For tlg2300-based USB device ");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c
index fae84c2..4fad1df 100644
--- a/drivers/media/video/tlg2300/pd-radio.c
+++ b/drivers/media/video/tlg2300/pd-radio.c
@@ -6,7 +6,6 @@
 #include <linux/usb.h>
 #include <linux/i2c.h>
 #include <media/v4l2-dev.h>
-#include <linux/version.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <media/v4l2-ioctl.h>
@@ -149,7 +148,6 @@
 	strlcpy(v->driver, "tele-radio", sizeof(v->driver));
 	strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
 	usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
-	v->version = KERNEL_VERSION(0, 0, 1);
 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 	return 0;
 }
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index a03945a..11cc980 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -39,6 +39,7 @@
 #include "tda9887.h"
 #include "xc5000.h"
 #include "tda18271.h"
+#include "xc4000.h"
 
 #define UNSET (-1U)
 
@@ -391,6 +392,23 @@
 		tune_now = 0;
 		break;
 	}
+	case TUNER_XC4000:
+	{
+		struct xc4000_config xc4000_cfg = {
+			.i2c_address	  = t->i2c->addr,
+			/* FIXME: the correct parameters will be set */
+			/* only when the digital dvb_attach() occurs */
+			.default_pm	  = 0,
+			.dvb_amplitude	  = 0,
+			.set_smoothedcvbs = 0,
+			.if_khz		  = 0
+		};
+		if (!dvb_attach(xc4000_attach,
+				&t->fe, t->i2c->adapter, &xc4000_cfg))
+			goto attach_failed;
+		tune_now = 0;
+		break;
+	}
 	default:
 		if (!dvb_attach(simple_tuner_attach, &t->fe,
 				t->i2c->adapter, t->i2c->addr, t->type))
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 0347bbe..742482e 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -552,16 +552,6 @@
 	return ret;
 }
 
-static int tw9910_enum_input(struct soc_camera_device *icd,
-			     struct v4l2_input *inp)
-{
-	inp->type = V4L2_INPUT_TYPE_TUNER;
-	inp->std  = V4L2_STD_UNKNOWN;
-	strcpy(inp->name, "Video");
-
-	return 0;
-}
-
 static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
 			       struct v4l2_dbg_chip_ident *id)
 {
@@ -846,13 +836,9 @@
 	struct tw9910_priv *priv = to_tw9910(client);
 	s32 id;
 
-	/*
-	 * We must have a parent by now. And it cannot be a wrong one.
-	 * So this entire test is completely redundant.
-	 */
-	if (!icd->dev.parent ||
-	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
-		return -ENODEV;
+	/* We must have a parent by now. And it cannot be a wrong one. */
+	BUG_ON(!icd->parent ||
+	       to_soc_camera_host(icd->parent)->nr != icd->iface);
 
 	/*
 	 * tw9910 only use 8 or 16 bit bus width
@@ -891,7 +877,6 @@
 static struct soc_camera_ops tw9910_ops = {
 	.set_bus_param		= tw9910_set_bus_param,
 	.query_bus_param	= tw9910_query_bus_param,
-	.enum_input		= tw9910_enum_input,
 };
 
 static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index ea8ea8a..5a74f5e 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -45,7 +45,6 @@
  *
  */
 
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/timer.h>
@@ -77,15 +76,7 @@
 #define DRIVER_ALIAS "USBVision"
 #define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
 #define DRIVER_LICENSE "GPL"
-#define USBVISION_DRIVER_VERSION_MAJOR 0
-#define USBVISION_DRIVER_VERSION_MINOR 9
-#define USBVISION_DRIVER_VERSION_PATCHLEVEL 10
-#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,\
-USBVISION_DRIVER_VERSION_MINOR,\
-USBVISION_DRIVER_VERSION_PATCHLEVEL)
-#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) \
-"." __stringify(USBVISION_DRIVER_VERSION_MINOR) \
-"." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL)
+#define USBVISION_VERSION_STRING "0.9.11"
 
 #define	ENABLE_HEXDUMP	0	/* Enable if you need it */
 
@@ -516,7 +507,6 @@
 		usbvision_device_data[usbvision->dev_model].model_string,
 		sizeof(vc->card));
 	usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
-	vc->version = USBVISION_DRIVER_VERSION;
 	vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_AUDIO |
 		V4L2_CAP_READWRITE |
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index a4db26f..10c2364 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -20,7 +20,7 @@
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "uvcvideo.h"
 
@@ -1664,8 +1664,8 @@
 		return -EINVAL;
 	}
 
-	/* Search for the matching (GUID/CS) control in the given device */
-	list_for_each_entry(entity, &dev->entities, list) {
+	/* Search for the matching (GUID/CS) control on the current chain */
+	list_for_each_entry(entity, &chain->entities, chain) {
 		unsigned int i;
 
 		if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index b6eae48..d29f9c2 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -31,6 +31,7 @@
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
+#include <linux/version.h>
 #include <asm/atomic.h>
 #include <asm/unaligned.h>
 
@@ -1857,7 +1858,7 @@
 			sizeof(dev->mdev.serial));
 	strcpy(dev->mdev.bus_info, udev->devpath);
 	dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
-	dev->mdev.driver_version = DRIVER_VERSION_NUMBER;
+	dev->mdev.driver_version = LINUX_VERSION_CODE;
 	if (media_device_register(&dev->mdev) < 0)
 		goto error;
 
@@ -2130,6 +2131,15 @@
 	  .bInterfaceProtocol	= 0,
 	  .driver_info 		= UVC_QUIRK_PROBE_MINMAX
 				| UVC_QUIRK_BUILTIN_ISIGHT },
+	/* Foxlink ("HP Webcam" on HP Mini 5103) */
+	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
+				| USB_DEVICE_ID_MATCH_INT_INFO,
+	  .idVendor		= 0x05c8,
+	  .idProduct		= 0x0403,
+	  .bInterfaceClass	= USB_CLASS_VIDEO,
+	  .bInterfaceSubClass	= 1,
+	  .bInterfaceProtocol	= 0,
+	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
 	/* Genesys Logic USB 2.0 PC Camera */
 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
 				| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index f90ce9f..677691c 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -19,7 +19,7 @@
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "uvcvideo.h"
 
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 543a803..ea71d5f 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -21,7 +21,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
@@ -83,7 +83,7 @@
 	default:
 		uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
 			  "%u.\n", xmap->v4l2_type);
-		ret = -EINVAL;
+		ret = -ENOTTY;
 		goto done;
 	}
 
@@ -571,7 +571,7 @@
 		strlcpy(cap->card, vdev->name, sizeof cap->card);
 		usb_make_path(stream->dev->udev,
 			      cap->bus_info, sizeof(cap->bus_info));
-		cap->version = DRIVER_VERSION_NUMBER;
+		cap->version = LINUX_VERSION_CODE;
 		if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
 			cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
 					  | V4L2_CAP_STREAMING;
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 4999479..8244167 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -19,7 +19,7 @@
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/unaligned.h>
 
 #include <media/v4l2-common.h>
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 20107fd..df32a43 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -183,8 +183,7 @@
  * Driver specific constants.
  */
 
-#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(1, 1, 0)
-#define DRIVER_VERSION		"v1.1.0"
+#define DRIVER_VERSION		"1.1.1"
 
 /* Number of isochronous URBs. */
 #define UVC_URBS		5
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 06b9f9f..5c6100f 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -105,6 +105,9 @@
 		    menu_items[ctrl->value][0] == '\0')
 			return -EINVAL;
 	}
+	if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+			(ctrl->value & ~qctrl->maximum))
+		return -ERANGE;
 	return 0;
 }
 EXPORT_SYMBOL(v4l2_ctrl_check);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 7c26947..61979b7 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -662,6 +662,32 @@
 	return 0;
 }
 
+struct v4l2_event32 {
+	__u32				type;
+	union {
+		__u8			data[64];
+	} u;
+	__u32				pending;
+	__u32				sequence;
+	struct compat_timespec		timestamp;
+	__u32				id;
+	__u32				reserved[8];
+};
+
+static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+{
+	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+		put_user(kp->type, &up->type) ||
+		copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
+		put_user(kp->pending, &up->pending) ||
+		put_user(kp->sequence, &up->sequence) ||
+		put_compat_timespec(&kp->timestamp, &up->timestamp) ||
+		put_user(kp->id, &up->id) ||
+		copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+			return -EFAULT;
+	return 0;
+}
+
 #define VIDIOC_G_FMT32		_IOWR('V',  4, struct v4l2_format32)
 #define VIDIOC_S_FMT32		_IOWR('V',  5, struct v4l2_format32)
 #define VIDIOC_QUERYBUF32	_IOWR('V',  9, struct v4l2_buffer32)
@@ -675,6 +701,7 @@
 #define VIDIOC_G_EXT_CTRLS32    _IOWR('V', 71, struct v4l2_ext_controls32)
 #define VIDIOC_S_EXT_CTRLS32    _IOWR('V', 72, struct v4l2_ext_controls32)
 #define VIDIOC_TRY_EXT_CTRLS32  _IOWR('V', 73, struct v4l2_ext_controls32)
+#define	VIDIOC_DQEVENT32	_IOR ('V', 89, struct v4l2_event32)
 
 #define VIDIOC_OVERLAY32	_IOW ('V', 14, s32)
 #define VIDIOC_STREAMON32	_IOW ('V', 18, s32)
@@ -693,6 +720,7 @@
 		struct v4l2_input v2i;
 		struct v4l2_standard v2s;
 		struct v4l2_ext_controls v2ecs;
+		struct v4l2_event v2ev;
 		unsigned long vx;
 		int vi;
 	} karg;
@@ -715,6 +743,7 @@
 	case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
 	case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
 	case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+	case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
 	case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
 	case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
 	case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
@@ -778,6 +807,9 @@
 		err = get_v4l2_ext_controls32(&karg.v2ecs, up);
 		compatible_arg = 0;
 		break;
+	case VIDIOC_DQEVENT:
+		compatible_arg = 0;
+		break;
 	}
 	if (err)
 		return err;
@@ -818,6 +850,10 @@
 		err = put_v4l2_framebuffer32(&karg.v2fb, up);
 		break;
 
+	case VIDIOC_DQEVENT:
+		err = put_v4l2_event32(&karg.v2ev, up);
+		break;
+
 	case VIDIOC_G_FMT:
 	case VIDIOC_S_FMT:
 	case VIDIOC_TRY_FMT:
@@ -920,6 +956,7 @@
 	case VIDIOC_S_DV_TIMINGS:
 	case VIDIOC_G_DV_TIMINGS:
 	case VIDIOC_DQEVENT:
+	case VIDIOC_DQEVENT32:
 	case VIDIOC_SUBSCRIBE_EVENT:
 	case VIDIOC_UNSUBSCRIBE_EVENT:
 		ret = do_video_ioctl(file, cmd, arg);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 2412f08..06b6014 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -23,17 +23,39 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-dev.h>
 
+#define has_op(master, op) \
+	(master->ops && master->ops->op)
+#define call_op(master, op) \
+	(has_op(master, op) ? master->ops->op(master) : 0)
+
 /* Internal temporary helper struct, one for each v4l2_ext_control */
-struct ctrl_helper {
+struct v4l2_ctrl_helper {
+	/* Pointer to the control reference of the master control */
+	struct v4l2_ctrl_ref *mref;
 	/* The control corresponding to the v4l2_ext_control ID field. */
 	struct v4l2_ctrl *ctrl;
-	/* Used internally to mark whether this control was already
-	   processed. */
-	bool handled;
+	/* v4l2_ext_control index of the next control belonging to the
+	   same cluster, or 0 if there isn't any. */
+	u32 next;
 };
 
+/* Small helper function to determine if the autocluster is set to manual
+   mode. In that case the is_volatile flag should be ignored. */
+static bool is_cur_manual(const struct v4l2_ctrl *master)
+{
+	return master->is_auto && master->cur.val == master->manual_mode_value;
+}
+
+/* Same as above, but this checks the against the new value instead of the
+   current value. */
+static bool is_new_manual(const struct v4l2_ctrl *master)
+{
+	return master->is_auto && master->val == master->manual_mode_value;
+}
+
 /* Returns NULL or a character pointer array containing the menu for
    the given control ID. The pointer array ends with a NULL pointer.
    An empty string signifies a menu entry that is invalid. This allows
@@ -181,7 +203,7 @@
 	};
 	static const char * const mpeg_stream_vbi_fmt[] = {
 		"No VBI",
-		"Private packet, IVTV format",
+		"Private Packet, IVTV Format",
 		NULL
 	};
 	static const char * const camera_power_line_frequency[] = {
@@ -204,18 +226,130 @@
 		"Negative",
 		"Emboss",
 		"Sketch",
-		"Sky blue",
-		"Grass green",
-		"Skin whiten",
+		"Sky Blue",
+		"Grass Green",
+		"Skin Whiten",
 		"Vivid",
 		NULL
 	};
 	static const char * const tune_preemphasis[] = {
-		"No preemphasis",
+		"No Preemphasis",
 		"50 useconds",
 		"75 useconds",
 		NULL,
 	};
+	static const char * const header_mode[] = {
+		"Separate Buffer",
+		"Joined With 1st Frame",
+		NULL,
+	};
+	static const char * const multi_slice[] = {
+		"Single",
+		"Max Macroblocks",
+		"Max Bytes",
+		NULL,
+	};
+	static const char * const entropy_mode[] = {
+		"CAVLC",
+		"CABAC",
+		NULL,
+	};
+	static const char * const mpeg_h264_level[] = {
+		"1",
+		"1b",
+		"1.1",
+		"1.2",
+		"1.3",
+		"2",
+		"2.1",
+		"2.2",
+		"3",
+		"3.1",
+		"3.2",
+		"4",
+		"4.1",
+		"4.2",
+		"5",
+		"5.1",
+		NULL,
+	};
+	static const char * const h264_loop_filter[] = {
+		"Enabled",
+		"Disabled",
+		"Disabled at Slice Boundary",
+		NULL,
+	};
+	static const char * const h264_profile[] = {
+		"Baseline",
+		"Constrained Baseline",
+		"Main",
+		"Extended",
+		"High",
+		"High 10",
+		"High 422",
+		"High 444 Predictive",
+		"High 10 Intra",
+		"High 422 Intra",
+		"High 444 Intra",
+		"CAVLC 444 Intra",
+		"Scalable Baseline",
+		"Scalable High",
+		"Scalable High Intra",
+		"Multiview High",
+		NULL,
+	};
+	static const char * const vui_sar_idc[] = {
+		"Unspecified",
+		"1:1",
+		"12:11",
+		"10:11",
+		"16:11",
+		"40:33",
+		"24:11",
+		"20:11",
+		"32:11",
+		"80:33",
+		"18:11",
+		"15:11",
+		"64:33",
+		"160:99",
+		"4:3",
+		"3:2",
+		"2:1",
+		"Extended SAR",
+		NULL,
+	};
+	static const char * const mpeg_mpeg4_level[] = {
+		"0",
+		"0b",
+		"1",
+		"2",
+		"3",
+		"3b",
+		"4",
+		"5",
+		NULL,
+	};
+	static const char * const mpeg4_profile[] = {
+		"Simple",
+		"Adcanved Simple",
+		"Core",
+		"Simple Scalable",
+		"Advanced Coding Efficency",
+		NULL,
+	};
+
+	static const char * const flash_led_mode[] = {
+		"Off",
+		"Flash",
+		"Torch",
+		NULL,
+	};
+	static const char * const flash_strobe_source[] = {
+		"Software",
+		"External",
+		NULL,
+	};
 
 	switch (id) {
 	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -256,6 +390,28 @@
 		return colorfx;
 	case V4L2_CID_TUNE_PREEMPHASIS:
 		return tune_preemphasis;
+	case V4L2_CID_FLASH_LED_MODE:
+		return flash_led_mode;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		return flash_strobe_source;
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		return header_mode;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+		return multi_slice;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		return entropy_mode;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		return mpeg_h264_level;
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+		return h264_loop_filter;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		return h264_profile;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+		return vui_sar_idc;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+		return mpeg_mpeg4_level;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+		return mpeg4_profile;
 	default:
 		return NULL;
 	}
@@ -307,6 +463,8 @@
 	case V4L2_CID_CHROMA_GAIN:		return "Chroma Gain";
 	case V4L2_CID_ILLUMINATORS_1:		return "Illuminator 1";
 	case V4L2_CID_ILLUMINATORS_2:		return "Illuminator 2";
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:	return "Minimum Number of Capture Buffers";
+	case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:	return "Minimum Number of Output Buffers";
 
 	/* MPEG controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -343,6 +501,48 @@
 	case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
 	case V4L2_CID_MPEG_VIDEO_MUTE:		return "Video Mute";
 	case V4L2_CID_MPEG_VIDEO_MUTE_YUV:	return "Video Mute YUV";
+	case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:	return "Decoder Slice Interface";
+	case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:	return "MPEG4 Loop Filter Enable";
+	case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:	return "The Number of Intra Refresh MBs";
+	case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:		return "Frame Level Rate Control Enable";
+	case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:			return "H264 MB Level Rate Control";
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:			return "Sequence Header Mode";
+	case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC:			return "The Max Number of Reference Picture";
+	case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:		return "H263 I-Frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:		return "H263 P frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:		return "H263 B frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:			return "H263 Minimum QP Value";
+	case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:			return "H263 Maximum QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:		return "H264 I-Frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:		return "H264 P frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:		return "H264 B frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:			return "H264 Maximum QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:			return "H264 Minimum QP Value";
+	case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:		return "H264 8x8 Transform Enable";
+	case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:			return "H264 CPB Buffer Size";
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:		return "H264 Entorpy Mode";
+	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:			return "H264 I Period";
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:			return "H264 Level";
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:	return "H264 Loop Filter Alpha Offset";
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:		return "H264 Loop Filter Beta Offset";
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:		return "H264 Loop Filter Mode";
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:			return "H264 Profile";
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:	return "Vertical Size of SAR";
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:	return "Horizontal Size of SAR";
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:		return "Aspect Ratio VUI Enable";
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:		return "VUI Aspect Ratio IDC";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:		return "MPEG4 I-Frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:		return "MPEG4 P frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:		return "MPEG4 B frame QP Value";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:			return "MPEG4 Minimum QP Value";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:			return "MPEG4 Maximum QP Value";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:			return "MPEG4 Level";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:			return "MPEG4 Profile";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:			return "Quarter Pixel Search Enable";
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:		return "The Maximum Bytes Per Slice";
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:		return "The Number of MB in a Slice";
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:		return "The Slice Partitioning Method";
+	case V4L2_CID_MPEG_VIDEO_VBV_SIZE:			return "VBV Buffer Size";
 
 	/* CAMERA controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -389,6 +589,21 @@
 	case V4L2_CID_TUNE_POWER_LEVEL:		return "Tune Power Level";
 	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:	return "Tune Antenna Capacitor";
 
+	/* Flash controls */
+	case V4L2_CID_FLASH_CLASS:		return "Flash controls";
+	case V4L2_CID_FLASH_LED_MODE:		return "LED mode";
+	case V4L2_CID_FLASH_STROBE_SOURCE:	return "Strobe source";
+	case V4L2_CID_FLASH_STROBE:		return "Strobe";
+	case V4L2_CID_FLASH_STROBE_STOP:	return "Stop strobe";
+	case V4L2_CID_FLASH_STROBE_STATUS:	return "Strobe status";
+	case V4L2_CID_FLASH_TIMEOUT:		return "Strobe timeout";
+	case V4L2_CID_FLASH_INTENSITY:		return "Intensity, flash mode";
+	case V4L2_CID_FLASH_TORCH_INTENSITY:	return "Intensity, torch mode";
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, indicator";
+	case V4L2_CID_FLASH_FAULT:		return "Faults";
+	case V4L2_CID_FLASH_CHARGE:		return "Charge";
+	case V4L2_CID_FLASH_READY:		return "Ready to strobe";
+
 	default:
 		return NULL;
 	}
@@ -423,12 +638,24 @@
 	case V4L2_CID_PILOT_TONE_ENABLED:
 	case V4L2_CID_ILLUMINATORS_1:
 	case V4L2_CID_ILLUMINATORS_2:
+	case V4L2_CID_FLASH_STROBE_STATUS:
+	case V4L2_CID_FLASH_CHARGE:
+	case V4L2_CID_FLASH_READY:
+	case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+	case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+	case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+	case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+	case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
 		*type = V4L2_CTRL_TYPE_BOOLEAN;
 		*min = 0;
 		*max = *step = 1;
 		break;
 	case V4L2_CID_PAN_RESET:
 	case V4L2_CID_TILT_RESET:
+	case V4L2_CID_FLASH_STROBE:
+	case V4L2_CID_FLASH_STROBE_STOP:
 		*type = V4L2_CTRL_TYPE_BUTTON;
 		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
 		*min = *max = *step = *def = 0;
@@ -452,6 +679,17 @@
 	case V4L2_CID_EXPOSURE_AUTO:
 	case V4L2_CID_COLORFX:
 	case V4L2_CID_TUNE_PREEMPHASIS:
+	case V4L2_CID_FLASH_LED_MODE:
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
 		*type = V4L2_CTRL_TYPE_MENU;
 		break;
 	case V4L2_CID_RDS_TX_PS_NAME:
@@ -462,6 +700,7 @@
 	case V4L2_CID_CAMERA_CLASS:
 	case V4L2_CID_MPEG_CLASS:
 	case V4L2_CID_FM_TX_CLASS:
+	case V4L2_CID_FLASH_CLASS:
 		*type = V4L2_CTRL_TYPE_CTRL_CLASS;
 		/* You can neither read not write these */
 		*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -474,6 +713,14 @@
 		/* Max is calculated as RGB888 that is 2^24 */
 		*max = 0xFFFFFF;
 		break;
+	case V4L2_CID_FLASH_FAULT:
+		*type = V4L2_CTRL_TYPE_BITMASK;
+		break;
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+	case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+		*type = V4L2_CTRL_TYPE_INTEGER;
+		*flags |= V4L2_CTRL_FLAG_READ_ONLY;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -519,6 +766,10 @@
 	case V4L2_CID_ZOOM_RELATIVE:
 		*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
 		break;
+	case V4L2_CID_FLASH_STROBE_STATUS:
+	case V4L2_CID_FLASH_READY:
+		*flags |= V4L2_CTRL_FLAG_READ_ONLY;
+		break;
 	}
 }
 EXPORT_SYMBOL(v4l2_ctrl_fill);
@@ -537,6 +788,42 @@
 	}
 }
 
+static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
+{
+	memset(ev->reserved, 0, sizeof(ev->reserved));
+	ev->type = V4L2_EVENT_CTRL;
+	ev->id = ctrl->id;
+	ev->u.ctrl.changes = changes;
+	ev->u.ctrl.type = ctrl->type;
+	ev->u.ctrl.flags = ctrl->flags;
+	if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+		ev->u.ctrl.value64 = 0;
+	else
+		ev->u.ctrl.value64 = ctrl->cur.val64;
+	ev->u.ctrl.minimum = ctrl->minimum;
+	ev->u.ctrl.maximum = ctrl->maximum;
+	if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+		ev->u.ctrl.step = 1;
+	else
+		ev->u.ctrl.step = ctrl->step;
+	ev->u.ctrl.default_value = ctrl->default_value;
+}
+
+static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
+{
+	struct v4l2_event ev;
+	struct v4l2_subscribed_event *sev;
+
+	if (list_empty(&ctrl->ev_subs))
+		return;
+	fill_event(&ev, ctrl, changes);
+
+	list_for_each_entry(sev, &ctrl->ev_subs, node)
+		if (sev->fh && (sev->fh != fh ||
+				(sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK)))
+			v4l2_event_queue_fh(sev->fh, &ev);
+}
+
 /* Helper function: copy the current control value back to the caller */
 static int cur_to_user(struct v4l2_ext_control *c,
 		       struct v4l2_ctrl *ctrl)
@@ -624,22 +911,45 @@
 }
 
 /* Copy the new value to the current value. */
-static void new_to_cur(struct v4l2_ctrl *ctrl)
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+						bool update_inactive)
 {
+	bool changed = false;
+
 	if (ctrl == NULL)
 		return;
 	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_BUTTON:
+		changed = true;
+		break;
 	case V4L2_CTRL_TYPE_STRING:
 		/* strings are always 0-terminated */
+		changed = strcmp(ctrl->string, ctrl->cur.string);
 		strcpy(ctrl->cur.string, ctrl->string);
 		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
+		changed = ctrl->val64 != ctrl->cur.val64;
 		ctrl->cur.val64 = ctrl->val64;
 		break;
 	default:
+		changed = ctrl->val != ctrl->cur.val;
 		ctrl->cur.val = ctrl->val;
 		break;
 	}
+	if (update_inactive) {
+		ctrl->flags &= ~V4L2_CTRL_FLAG_INACTIVE;
+		if (!is_cur_manual(ctrl->cluster[0]))
+			ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+	}
+	if (changed || update_inactive) {
+		/* If a control was changed that was not one of the controls
+		   modified by the application, then send the event to all. */
+		if (!ctrl->is_new)
+			fh = NULL;
+		send_event(fh, ctrl,
+			(changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
+			(update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+	}
 }
 
 /* Copy the current value to the new value */
@@ -692,13 +1002,11 @@
 	return diff;
 }
 
-/* Validate a new control */
-static int validate_new(struct v4l2_ctrl *ctrl)
+/* Validate integer-type control */
+static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
 {
-	s32 val = ctrl->val;
-	char *s = ctrl->string;
+	s32 val = *pval;
 	u32 offset;
-	size_t len;
 
 	switch (ctrl->type) {
 	case V4L2_CTRL_TYPE_INTEGER:
@@ -711,11 +1019,11 @@
 		offset = val - ctrl->minimum;
 		offset = ctrl->step * (offset / ctrl->step);
 		val = ctrl->minimum + offset;
-		ctrl->val = val;
+		*pval = val;
 		return 0;
 
 	case V4L2_CTRL_TYPE_BOOLEAN:
-		ctrl->val = !!ctrl->val;
+		*pval = !!val;
 		return 0;
 
 	case V4L2_CTRL_TYPE_MENU:
@@ -726,11 +1034,35 @@
 			return -EINVAL;
 		return 0;
 
+	case V4L2_CTRL_TYPE_BITMASK:
+		*pval &= ctrl->maximum;
+		return 0;
+
 	case V4L2_CTRL_TYPE_BUTTON:
 	case V4L2_CTRL_TYPE_CTRL_CLASS:
-		ctrl->val64 = 0;
+		*pval = 0;
 		return 0;
 
+	default:
+		return -EINVAL;
+	}
+}
+
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
+{
+	char *s = c->string;
+	size_t len;
+
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_INTEGER:
+	case V4L2_CTRL_TYPE_BOOLEAN:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_BITMASK:
+	case V4L2_CTRL_TYPE_BUTTON:
+	case V4L2_CTRL_TYPE_CTRL_CLASS:
+		return validate_new_int(ctrl, &c->value);
+
 	case V4L2_CTRL_TYPE_INTEGER64:
 		return 0;
 
@@ -780,6 +1112,7 @@
 {
 	struct v4l2_ctrl_ref *ref, *next_ref;
 	struct v4l2_ctrl *ctrl, *next_ctrl;
+	struct v4l2_subscribed_event *sev, *next_sev;
 
 	if (hdl == NULL || hdl->buckets == NULL)
 		return;
@@ -793,6 +1126,8 @@
 	/* Free all controls owned by the handler */
 	list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
 		list_del(&ctrl->node);
+		list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+			list_del(&sev->node);
 		kfree(ctrl);
 	}
 	kfree(hdl->buckets);
@@ -962,13 +1297,17 @@
 
 	/* Sanity checks */
 	if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
-	    max < min ||
 	    (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+	    (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
 	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
 	    (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+	if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+		handler_set_err(hdl, -ERANGE);
+		return NULL;
+	}
 	if ((type == V4L2_CTRL_TYPE_INTEGER ||
 	     type == V4L2_CTRL_TYPE_MENU ||
 	     type == V4L2_CTRL_TYPE_BOOLEAN) &&
@@ -976,6 +1315,10 @@
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+	if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+		handler_set_err(hdl, -ERANGE);
+		return NULL;
+	}
 
 	if (type == V4L2_CTRL_TYPE_BUTTON)
 		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -991,6 +1334,7 @@
 	}
 
 	INIT_LIST_HEAD(&ctrl->node);
+	INIT_LIST_HEAD(&ctrl->ev_subs);
 	ctrl->handler = hdl;
 	ctrl->ops = ops;
 	ctrl->id = id;
@@ -1132,6 +1476,9 @@
 		/* Skip handler-private controls. */
 		if (ctrl->is_private)
 			continue;
+		/* And control classes */
+		if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+			continue;
 		ret = handler_new_ref(hdl, ctrl);
 		if (ret)
 			break;
@@ -1147,7 +1494,7 @@
 	int i;
 
 	/* The first control is the master control and it must not be NULL */
-	BUG_ON(controls[0] == NULL);
+	BUG_ON(ncontrols == 0 || controls[0] == NULL);
 
 	for (i = 0; i < ncontrols; i++) {
 		if (controls[i]) {
@@ -1158,18 +1505,47 @@
 }
 EXPORT_SYMBOL(v4l2_ctrl_cluster);
 
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+			    u8 manual_val, bool set_volatile)
+{
+	struct v4l2_ctrl *master = controls[0];
+	u32 flag;
+	int i;
+
+	v4l2_ctrl_cluster(ncontrols, controls);
+	WARN_ON(ncontrols <= 1);
+	WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+	master->is_auto = true;
+	master->manual_mode_value = manual_val;
+	master->flags |= V4L2_CTRL_FLAG_UPDATE;
+	flag = is_cur_manual(master) ? 0 : V4L2_CTRL_FLAG_INACTIVE;
+
+	for (i = 1; i < ncontrols; i++)
+		if (controls[i]) {
+			controls[i]->is_volatile = set_volatile;
+			controls[i]->flags |= flag;
+		}
+}
+EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
+
 /* Activate/deactivate a control. */
 void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
 {
+	/* invert since the actual flag is called 'inactive' */
+	bool inactive = !active;
+	bool old;
+
 	if (ctrl == NULL)
 		return;
 
-	if (!active)
+	if (inactive)
 		/* set V4L2_CTRL_FLAG_INACTIVE */
-		set_bit(4, &ctrl->flags);
+		old = test_and_set_bit(4, &ctrl->flags);
 	else
 		/* clear V4L2_CTRL_FLAG_INACTIVE */
-		clear_bit(4, &ctrl->flags);
+		old = test_and_clear_bit(4, &ctrl->flags);
+	if (old != inactive)
+		send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
 }
 EXPORT_SYMBOL(v4l2_ctrl_activate);
 
@@ -1181,15 +1557,21 @@
    these controls. */
 void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
 {
+	bool old;
+
 	if (ctrl == NULL)
 		return;
 
+	v4l2_ctrl_lock(ctrl);
 	if (grabbed)
 		/* set V4L2_CTRL_FLAG_GRABBED */
-		set_bit(1, &ctrl->flags);
+		old = test_and_set_bit(1, &ctrl->flags);
 	else
 		/* clear V4L2_CTRL_FLAG_GRABBED */
-		clear_bit(1, &ctrl->flags);
+		old = test_and_clear_bit(1, &ctrl->flags);
+	if (old != grabbed)
+		send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+	v4l2_ctrl_unlock(ctrl);
 }
 EXPORT_SYMBOL(v4l2_ctrl_grab);
 
@@ -1217,6 +1599,9 @@
 	case V4L2_CTRL_TYPE_MENU:
 		printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
 		break;
+	case V4L2_CTRL_TYPE_BITMASK:
+		printk(KERN_CONT "0x%08x", ctrl->cur.val);
+		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
 		printk(KERN_CONT "%lld", ctrl->cur.val64);
 		break;
@@ -1277,26 +1662,21 @@
 		int i;
 
 		/* Skip if this control was already handled by a cluster. */
-		if (ctrl->done)
+		/* Skip button controls and read-only controls. */
+		if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+		    (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
 			continue;
 
 		for (i = 0; i < master->ncontrols; i++) {
 			if (master->cluster[i]) {
 				cur_to_new(master->cluster[i]);
 				master->cluster[i]->is_new = 1;
+				master->cluster[i]->done = true;
 			}
 		}
-
-		/* Skip button controls and read-only controls. */
-		if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
-		    (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
-			continue;
-		ret = master->ops->s_ctrl(master);
+		ret = call_op(master, s_ctrl);
 		if (ret)
 			break;
-		for (i = 0; i < master->ncontrols; i++)
-			if (master->cluster[i])
-				master->cluster[i]->done = true;
 	}
 	mutex_unlock(&hdl->lock);
 	return ret;
@@ -1447,18 +1827,19 @@
    Find the controls in the control array and do some basic checks. */
 static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 			     struct v4l2_ext_controls *cs,
-			     struct ctrl_helper *helpers,
-			     bool try)
+			     struct v4l2_ctrl_helper *helpers)
 {
+	struct v4l2_ctrl_helper *h;
+	bool have_clusters = false;
 	u32 i;
 
-	for (i = 0; i < cs->count; i++) {
+	for (i = 0, h = helpers; i < cs->count; i++, h++) {
 		struct v4l2_ext_control *c = &cs->controls[i];
+		struct v4l2_ctrl_ref *ref;
 		struct v4l2_ctrl *ctrl;
 		u32 id = c->id & V4L2_CTRL_ID_MASK;
 
-		if (try)
-			cs->error_idx = i;
+		cs->error_idx = i;
 
 		if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
 			return -EINVAL;
@@ -1467,55 +1848,61 @@
 		   extended controls */
 		if (id >= V4L2_CID_PRIVATE_BASE)
 			return -EINVAL;
-		ctrl = v4l2_ctrl_find(hdl, id);
-		if (ctrl == NULL)
+		ref = find_ref_lock(hdl, id);
+		if (ref == NULL)
 			return -EINVAL;
+		ctrl = ref->ctrl;
 		if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
 			return -EINVAL;
 
-		helpers[i].ctrl = ctrl;
-		helpers[i].handled = false;
+		if (ctrl->cluster[0]->ncontrols > 1)
+			have_clusters = true;
+		if (ctrl->cluster[0] != ctrl)
+			ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+		/* Store the ref to the master control of the cluster */
+		h->mref = ref;
+		h->ctrl = ctrl;
+		/* Initially set next to 0, meaning that there is no other
+		   control in this helper array belonging to the same
+		   cluster */
+		h->next = 0;
 	}
+
+	/* We are done if there were no controls that belong to a multi-
+	   control cluster. */
+	if (!have_clusters)
+		return 0;
+
+	/* The code below figures out in O(n) time which controls in the list
+	   belong to the same cluster. */
+
+	/* This has to be done with the handler lock taken. */
+	mutex_lock(&hdl->lock);
+
+	/* First zero the helper field in the master control references */
+	for (i = 0; i < cs->count; i++)
+		helpers[i].mref->helper = 0;
+	for (i = 0, h = helpers; i < cs->count; i++, h++) {
+		struct v4l2_ctrl_ref *mref = h->mref;
+
+		/* If the mref->helper is set, then it points to an earlier
+		   helper that belongs to the same cluster. */
+		if (mref->helper) {
+			/* Set the next field of mref->helper to the current
+			   index: this means that that earlier helper now
+			   points to the next helper in the same cluster. */
+			mref->helper->next = i;
+			/* mref should be set only for the first helper in the
+			   cluster, clear the others. */
+			h->mref = NULL;
+		}
+		/* Point the mref helper to the current helper struct. */
+		mref->helper = h;
+	}
+	mutex_unlock(&hdl->lock);
 	return 0;
 }
 
-typedef int (*cluster_func)(struct v4l2_ext_control *c,
-			    struct v4l2_ctrl *ctrl);
-
-/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
-   and call the provided function. */
-static int cluster_walk(unsigned from,
-			struct v4l2_ext_controls *cs,
-			struct ctrl_helper *helpers,
-			cluster_func f)
-{
-	struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
-	int ret = 0;
-	int i;
-
-	/* Find any controls from the same cluster and call the function */
-	for (i = from; !ret && i < cs->count; i++) {
-		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
-
-		if (!helpers[i].handled && ctrl->cluster == cluster)
-			ret = f(&cs->controls[i], ctrl);
-	}
-	return ret;
-}
-
-static void cluster_done(unsigned from,
-			 struct v4l2_ext_controls *cs,
-			 struct ctrl_helper *helpers)
-{
-	struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
-	int i;
-
-	/* Find any controls from the same cluster and mark them as handled */
-	for (i = from; i < cs->count; i++)
-		if (helpers[i].ctrl->cluster == cluster)
-			helpers[i].handled = true;
-}
-
 /* Handles the corner case where cs->count == 0. It checks whether the
    specified control class exists. If that class ID is 0, then it checks
    whether there are any controls at all. */
@@ -1531,10 +1918,10 @@
 /* Get extended controls. Allocates the helpers array if needed. */
 int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
 {
-	struct ctrl_helper helper[4];
-	struct ctrl_helper *helpers = helper;
+	struct v4l2_ctrl_helper helper[4];
+	struct v4l2_ctrl_helper *helpers = helper;
 	int ret;
-	int i;
+	int i, j;
 
 	cs->error_idx = cs->count;
 	cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
@@ -1551,30 +1938,46 @@
 			return -ENOMEM;
 	}
 
-	ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+	ret = prepare_ext_ctrls(hdl, cs, helpers);
+	cs->error_idx = cs->count;
 
 	for (i = 0; !ret && i < cs->count; i++)
 		if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
 			ret = -EACCES;
 
 	for (i = 0; !ret && i < cs->count; i++) {
-		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
-		struct v4l2_ctrl *master = ctrl->cluster[0];
+		int (*ctrl_to_user)(struct v4l2_ext_control *c,
+				    struct v4l2_ctrl *ctrl) = cur_to_user;
+		struct v4l2_ctrl *master;
 
-		if (helpers[i].handled)
+		if (helpers[i].mref == NULL)
 			continue;
 
+		master = helpers[i].mref->ctrl;
 		cs->error_idx = i;
 
 		v4l2_ctrl_lock(master);
-		/* g_volatile_ctrl will update the current control values */
-		if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
-			ret = master->ops->g_volatile_ctrl(master);
-		/* If OK, then copy the current control values to the caller */
-		if (!ret)
-			ret = cluster_walk(i, cs, helpers, cur_to_user);
+
+		/* g_volatile_ctrl will update the new control values */
+		if (has_op(master, g_volatile_ctrl) && !is_cur_manual(master)) {
+			for (j = 0; j < master->ncontrols; j++)
+				cur_to_new(master->cluster[j]);
+			ret = call_op(master, g_volatile_ctrl);
+			ctrl_to_user = new_to_user;
+		}
+		/* If OK, then copy the current (for non-volatile controls)
+		   or the new (for volatile controls) control values to the
+		   caller */
+		if (!ret) {
+			u32 idx = i;
+
+			do {
+				ret = ctrl_to_user(cs->controls + idx,
+						   helpers[idx].ctrl);
+				idx = helpers[idx].next;
+			} while (!ret && idx);
+		}
 		v4l2_ctrl_unlock(master);
-		cluster_done(i, cs, helpers);
 	}
 
 	if (cs->count > ARRAY_SIZE(helper))
@@ -1594,15 +1997,21 @@
 {
 	struct v4l2_ctrl *master = ctrl->cluster[0];
 	int ret = 0;
+	int i;
 
 	if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
 		return -EACCES;
 
 	v4l2_ctrl_lock(master);
 	/* g_volatile_ctrl will update the current control values */
-	if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
-		ret = master->ops->g_volatile_ctrl(master);
-	*val = ctrl->cur.val;
+	if (ctrl->is_volatile && !is_cur_manual(master)) {
+		for (i = 0; i < master->ncontrols; i++)
+			cur_to_new(master->cluster[i]);
+		ret = call_op(master, g_volatile_ctrl);
+		*val = ctrl->val;
+	} else {
+		*val = ctrl->cur.val;
+	}
 	v4l2_ctrl_unlock(master);
 	return ret;
 }
@@ -1638,72 +2047,61 @@
 /* Core function that calls try/s_ctrl and ensures that the new value is
    copied to the current value on a set.
    Must be called with ctrl->handler->lock held. */
-static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+static int try_or_set_cluster(struct v4l2_fh *fh,
+			      struct v4l2_ctrl *master, bool set)
 {
-	bool try = !set;
-	int ret = 0;
+	bool update_flag;
+	int ret;
 	int i;
 
 	/* Go through the cluster and either validate the new value or
 	   (if no new value was set), copy the current value to the new
 	   value, ensuring a consistent view for the control ops when
 	   called. */
-	for (i = 0; !ret && i < master->ncontrols; i++) {
+	for (i = 0; i < master->ncontrols; i++) {
 		struct v4l2_ctrl *ctrl = master->cluster[i];
 
 		if (ctrl == NULL)
 			continue;
 
-		if (ctrl->is_new) {
-			/* Double check this: it may have changed since the
-			   last check in try_or_set_ext_ctrls(). */
-			if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
-				return -EBUSY;
-
-			/* Validate if required */
-			if (!set)
-				ret = validate_new(ctrl);
+		if (!ctrl->is_new) {
+			cur_to_new(ctrl);
 			continue;
 		}
-		/* No new value was set, so copy the current and force
-		   a call to try_ctrl later, since the values for the cluster
-		   may now have changed and the end result might be invalid. */
-		try = true;
-		cur_to_new(ctrl);
+		/* Check again: it may have changed since the
+		   previous check in try_or_set_ext_ctrls(). */
+		if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+			return -EBUSY;
 	}
 
-	/* For larger clusters you have to call try_ctrl again to
-	   verify that the controls are still valid after the
-	   'cur_to_new' above. */
-	if (!ret && master->ops->try_ctrl && try)
-		ret = master->ops->try_ctrl(master);
+	ret = call_op(master, try_ctrl);
 
 	/* Don't set if there is no change */
-	if (!ret && set && cluster_changed(master)) {
-		ret = master->ops->s_ctrl(master);
-		/* If OK, then make the new values permanent. */
-		if (!ret)
-			for (i = 0; i < master->ncontrols; i++)
-				new_to_cur(master->cluster[i]);
-	}
-	return ret;
+	if (ret || !set || !cluster_changed(master))
+		return ret;
+	ret = call_op(master, s_ctrl);
+	if (ret)
+		return ret;
+
+	/* If OK, then make the new values permanent. */
+	update_flag = is_cur_manual(master) != is_new_manual(master);
+	for (i = 0; i < master->ncontrols; i++)
+		new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+	return 0;
 }
 
-/* Try or set controls. */
-static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
-				struct v4l2_ext_controls *cs,
-				struct ctrl_helper *helpers,
-				bool set)
+/* Validate controls. */
+static int validate_ctrls(struct v4l2_ext_controls *cs,
+			  struct v4l2_ctrl_helper *helpers, bool set)
 {
-	unsigned i, j;
+	unsigned i;
 	int ret = 0;
 
 	cs->error_idx = cs->count;
 	for (i = 0; i < cs->count; i++) {
 		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
 
-		if (!set)
-			cs->error_idx = i;
+		cs->error_idx = i;
 
 		if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
 			return -EACCES;
@@ -1715,50 +2113,22 @@
 		   best-effort to avoid that. */
 		if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
 			return -EBUSY;
+		ret = validate_new(ctrl, &cs->controls[i]);
+		if (ret)
+			return ret;
 	}
-
-	for (i = 0; !ret && i < cs->count; i++) {
-		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
-		struct v4l2_ctrl *master = ctrl->cluster[0];
-
-		cs->error_idx = i;
-
-		if (helpers[i].handled)
-			continue;
-
-		v4l2_ctrl_lock(ctrl);
-
-		/* Reset the 'is_new' flags of the cluster */
-		for (j = 0; j < master->ncontrols; j++)
-			if (master->cluster[j])
-				master->cluster[j]->is_new = 0;
-
-		/* Copy the new caller-supplied control values.
-		   user_to_new() sets 'is_new' to 1. */
-		ret = cluster_walk(i, cs, helpers, user_to_new);
-
-		if (!ret)
-			ret = try_or_set_control_cluster(master, set);
-
-		/* Copy the new values back to userspace. */
-		if (!ret)
-			ret = cluster_walk(i, cs, helpers, new_to_user);
-
-		v4l2_ctrl_unlock(ctrl);
-		cluster_done(i, cs, helpers);
-	}
-	return ret;
+	return 0;
 }
 
 /* Try or try-and-set controls */
-static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 			     struct v4l2_ext_controls *cs,
 			     bool set)
 {
-	struct ctrl_helper helper[4];
-	struct ctrl_helper *helpers = helper;
+	struct v4l2_ctrl_helper helper[4];
+	struct v4l2_ctrl_helper *helpers = helper;
+	unsigned i, j;
 	int ret;
-	int i;
 
 	cs->error_idx = cs->count;
 	cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
@@ -1774,25 +2144,49 @@
 		if (!helpers)
 			return -ENOMEM;
 	}
-	ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
-	if (ret)
-		goto free;
-
-	/* First 'try' all controls and abort on error */
-	ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
-	/* If this is a 'set' operation and the initial 'try' failed,
-	   then set error_idx to count to tell the application that no
-	   controls changed value yet. */
-	if (set)
+	ret = prepare_ext_ctrls(hdl, cs, helpers);
+	if (!ret)
+		ret = validate_ctrls(cs, helpers, set);
+	if (ret && set)
 		cs->error_idx = cs->count;
-	if (!ret && set) {
-		/* Reset 'handled' state */
-		for (i = 0; i < cs->count; i++)
-			helpers[i].handled = false;
-		ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+	for (i = 0; !ret && i < cs->count; i++) {
+		struct v4l2_ctrl *master;
+		u32 idx = i;
+
+		if (helpers[i].mref == NULL)
+			continue;
+
+		cs->error_idx = i;
+		master = helpers[i].mref->ctrl;
+		v4l2_ctrl_lock(master);
+
+		/* Reset the 'is_new' flags of the cluster */
+		for (j = 0; j < master->ncontrols; j++)
+			if (master->cluster[j])
+				master->cluster[j]->is_new = 0;
+
+		/* Copy the new caller-supplied control values.
+		   user_to_new() sets 'is_new' to 1. */
+		do {
+			ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+			idx = helpers[idx].next;
+		} while (!ret && idx);
+
+		if (!ret)
+			ret = try_or_set_cluster(fh, master, set);
+
+		/* Copy the new values back to userspace. */
+		if (!ret) {
+			idx = i;
+			do {
+				ret = new_to_user(cs->controls + idx,
+						helpers[idx].ctrl);
+				idx = helpers[idx].next;
+			} while (!ret && idx);
+		}
+		v4l2_ctrl_unlock(master);
 	}
 
-free:
 	if (cs->count > ARRAY_SIZE(helper))
 		kfree(helpers);
 	return ret;
@@ -1800,37 +2194,39 @@
 
 int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(hdl, cs, false);
+	return try_set_ext_ctrls(NULL, hdl, cs, false);
 }
 EXPORT_SYMBOL(v4l2_try_ext_ctrls);
 
-int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+					struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(hdl, cs, true);
+	return try_set_ext_ctrls(fh, hdl, cs, true);
 }
 EXPORT_SYMBOL(v4l2_s_ext_ctrls);
 
 int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+	return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
 }
 EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
 
 int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+	return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
 }
 EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
 
 /* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
 {
 	struct v4l2_ctrl *master = ctrl->cluster[0];
 	int ret;
 	int i;
 
-	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
-		return -EACCES;
+	ret = validate_new_int(ctrl, val);
+	if (ret)
+		return ret;
 
 	v4l2_ctrl_lock(ctrl);
 
@@ -1841,28 +2237,30 @@
 
 	ctrl->val = *val;
 	ctrl->is_new = 1;
-	ret = try_or_set_control_cluster(master, false);
-	if (!ret)
-		ret = try_or_set_control_cluster(master, true);
+	ret = try_or_set_cluster(fh, master, true);
 	*val = ctrl->cur.val;
 	v4l2_ctrl_unlock(ctrl);
 	return ret;
 }
 
-int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+					struct v4l2_control *control)
 {
 	struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
 
 	if (ctrl == NULL || !type_is_int(ctrl))
 		return -EINVAL;
 
-	return set_ctrl(ctrl, &control->value);
+	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+		return -EACCES;
+
+	return set_ctrl(fh, ctrl, &control->value);
 }
 EXPORT_SYMBOL(v4l2_s_ctrl);
 
 int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
 {
-	return v4l2_s_ctrl(sd->ctrl_handler, control);
+	return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
 }
 EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
 
@@ -1870,6 +2268,34 @@
 {
 	/* It's a driver bug if this happens. */
 	WARN_ON(!type_is_int(ctrl));
-	return set_ctrl(ctrl, &val);
+	return set_ctrl(NULL, ctrl, &val);
 }
 EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+				struct v4l2_subscribed_event *sev)
+{
+	v4l2_ctrl_lock(ctrl);
+	list_add_tail(&sev->node, &ctrl->ev_subs);
+	if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+	    (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+		struct v4l2_event ev;
+		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+
+		if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
+			changes |= V4L2_EVENT_CTRL_CH_VALUE;
+		fill_event(&ev, ctrl, changes);
+		v4l2_event_queue_fh(sev->fh, &ev);
+	}
+	v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_event);
+
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+				struct v4l2_subscribed_event *sev)
+{
+	v4l2_ctrl_lock(ctrl);
+	list_del(&sev->node);
+	v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_del_event);
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index 4aae501..c72856c 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -209,6 +209,7 @@
 		vdev->v4l2_dev = v4l2_dev;
 		vdev->fops = &v4l2_subdev_fops;
 		vdev->release = video_device_release_empty;
+		vdev->ctrl_handler = sd->ctrl_handler;
 		err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
 					      sd->owner);
 		if (err < 0)
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index 69fd343..53b190c 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -25,100 +25,39 @@
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
 
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-int v4l2_event_init(struct v4l2_fh *fh)
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
 {
-	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
-	if (fh->events == NULL)
-		return -ENOMEM;
-
-	init_waitqueue_head(&fh->events->wait);
-
-	INIT_LIST_HEAD(&fh->events->free);
-	INIT_LIST_HEAD(&fh->events->available);
-	INIT_LIST_HEAD(&fh->events->subscribed);
-
-	fh->events->sequence = -1;
-
-	return 0;
+	idx += sev->first;
+	return idx >= sev->elems ? idx - sev->elems : idx;
 }
-EXPORT_SYMBOL_GPL(v4l2_event_init);
-
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
-{
-	struct v4l2_events *events = fh->events;
-	unsigned long flags;
-
-	if (!events) {
-		WARN_ON(1);
-		return -ENOMEM;
-	}
-
-	while (events->nallocated < n) {
-		struct v4l2_kevent *kev;
-
-		kev = kzalloc(sizeof(*kev), GFP_KERNEL);
-		if (kev == NULL)
-			return -ENOMEM;
-
-		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-		list_add_tail(&kev->list, &events->free);
-		events->nallocated++;
-		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_alloc);
-
-#define list_kfree(list, type, member)				\
-	while (!list_empty(list)) {				\
-		type *hi;					\
-		hi = list_first_entry(list, type, member);	\
-		list_del(&hi->member);				\
-		kfree(hi);					\
-	}
-
-void v4l2_event_free(struct v4l2_fh *fh)
-{
-	struct v4l2_events *events = fh->events;
-
-	if (!events)
-		return;
-
-	list_kfree(&events->free, struct v4l2_kevent, list);
-	list_kfree(&events->available, struct v4l2_kevent, list);
-	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
-
-	kfree(events);
-	fh->events = NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_free);
 
 static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
 {
-	struct v4l2_events *events = fh->events;
 	struct v4l2_kevent *kev;
 	unsigned long flags;
 
 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
-	if (list_empty(&events->available)) {
+	if (list_empty(&fh->available)) {
 		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 		return -ENOENT;
 	}
 
-	WARN_ON(events->navailable == 0);
+	WARN_ON(fh->navailable == 0);
 
-	kev = list_first_entry(&events->available, struct v4l2_kevent, list);
-	list_move(&kev->list, &events->free);
-	events->navailable--;
+	kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
+	list_del(&kev->list);
+	fh->navailable--;
 
-	kev->event.pending = events->navailable;
+	kev->event.pending = fh->navailable;
 	*event = kev->event;
+	kev->sev->first = sev_pos(kev->sev, 1);
+	kev->sev->in_use--;
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
@@ -128,7 +67,6 @@
 int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
 		       int nonblocking)
 {
-	struct v4l2_events *events = fh->events;
 	int ret;
 
 	if (nonblocking)
@@ -139,8 +77,8 @@
 		mutex_unlock(fh->vdev->lock);
 
 	do {
-		ret = wait_event_interruptible(events->wait,
-					       events->navailable != 0);
+		ret = wait_event_interruptible(fh->wait,
+					       fh->navailable != 0);
 		if (ret < 0)
 			break;
 
@@ -154,23 +92,72 @@
 }
 EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
 
-/* Caller must hold fh->event->lock! */
+/* Caller must hold fh->vdev->fh_lock! */
 static struct v4l2_subscribed_event *v4l2_event_subscribed(
-	struct v4l2_fh *fh, u32 type)
+		struct v4l2_fh *fh, u32 type, u32 id)
 {
-	struct v4l2_events *events = fh->events;
 	struct v4l2_subscribed_event *sev;
 
 	assert_spin_locked(&fh->vdev->fh_lock);
 
-	list_for_each_entry(sev, &events->subscribed, list) {
-		if (sev->type == type)
+	list_for_each_entry(sev, &fh->subscribed, list)
+		if (sev->type == type && sev->id == id)
 			return sev;
-	}
 
 	return NULL;
 }
 
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+		const struct timespec *ts)
+{
+	struct v4l2_subscribed_event *sev;
+	struct v4l2_kevent *kev;
+	bool copy_payload = true;
+
+	/* Are we subscribed? */
+	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+	if (sev == NULL)
+		return;
+
+	/* Increase event sequence number on fh. */
+	fh->sequence++;
+
+	/* Do we have any free events? */
+	if (sev->in_use == sev->elems) {
+		/* no, remove the oldest one */
+		kev = sev->events + sev_pos(sev, 0);
+		list_del(&kev->list);
+		sev->in_use--;
+		sev->first = sev_pos(sev, 1);
+		fh->navailable--;
+		if (sev->elems == 1) {
+			if (sev->replace) {
+				sev->replace(&kev->event, ev);
+				copy_payload = false;
+			}
+		} else if (sev->merge) {
+			struct v4l2_kevent *second_oldest =
+				sev->events + sev_pos(sev, 0);
+			sev->merge(&kev->event, &second_oldest->event);
+		}
+	}
+
+	/* Take one and fill it. */
+	kev = sev->events + sev_pos(sev, sev->in_use);
+	kev->event.type = ev->type;
+	if (copy_payload)
+		kev->event.u = ev->u;
+	kev->event.id = ev->id;
+	kev->event.timestamp = *ts;
+	kev->event.sequence = fh->sequence;
+	sev->in_use++;
+	list_add_tail(&kev->list, &fh->available);
+
+	fh->navailable++;
+
+	wake_up_all(&fh->wait);
+}
+
 void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
 {
 	struct v4l2_fh *fh;
@@ -181,81 +168,95 @@
 
 	spin_lock_irqsave(&vdev->fh_lock, flags);
 
-	list_for_each_entry(fh, &vdev->fh_list, list) {
-		struct v4l2_events *events = fh->events;
-		struct v4l2_kevent *kev;
-
-		/* Are we subscribed? */
-		if (!v4l2_event_subscribed(fh, ev->type))
-			continue;
-
-		/* Increase event sequence number on fh. */
-		events->sequence++;
-
-		/* Do we have any free events? */
-		if (list_empty(&events->free))
-			continue;
-
-		/* Take one and fill it. */
-		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
-		kev->event.type = ev->type;
-		kev->event.u = ev->u;
-		kev->event.timestamp = timestamp;
-		kev->event.sequence = events->sequence;
-		list_move_tail(&kev->list, &events->available);
-
-		events->navailable++;
-
-		wake_up_all(&events->wait);
-	}
+	list_for_each_entry(fh, &vdev->fh_list, list)
+		__v4l2_event_queue_fh(fh, ev, &timestamp);
 
 	spin_unlock_irqrestore(&vdev->fh_lock, flags);
 }
 EXPORT_SYMBOL_GPL(v4l2_event_queue);
 
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+	unsigned long flags;
+	struct timespec timestamp;
+
+	ktime_get_ts(&timestamp);
+
+	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+	__v4l2_event_queue_fh(fh, ev, &timestamp);
+	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
+
 int v4l2_event_pending(struct v4l2_fh *fh)
 {
-	return fh->events->navailable;
+	return fh->navailable;
 }
 EXPORT_SYMBOL_GPL(v4l2_event_pending);
 
-int v4l2_event_subscribe(struct v4l2_fh *fh,
-			 struct v4l2_event_subscription *sub)
+static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new)
 {
-	struct v4l2_events *events = fh->events;
-	struct v4l2_subscribed_event *sev;
-	unsigned long flags;
+	u32 old_changes = old->u.ctrl.changes;
 
-	if (fh->events == NULL) {
-		WARN_ON(1);
-		return -ENOMEM;
+	old->u.ctrl = new->u.ctrl;
+	old->u.ctrl.changes |= old_changes;
+}
+
+static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
+{
+	new->u.ctrl.changes |= old->u.ctrl.changes;
+}
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+			 struct v4l2_event_subscription *sub, unsigned elems)
+{
+	struct v4l2_subscribed_event *sev, *found_ev;
+	struct v4l2_ctrl *ctrl = NULL;
+	unsigned long flags;
+	unsigned i;
+
+	if (elems < 1)
+		elems = 1;
+	if (sub->type == V4L2_EVENT_CTRL) {
+		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
+		if (ctrl == NULL)
+			return -EINVAL;
 	}
 
-	sev = kmalloc(sizeof(*sev), GFP_KERNEL);
+	sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
 	if (!sev)
 		return -ENOMEM;
-
-	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
-	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
-		INIT_LIST_HEAD(&sev->list);
-		sev->type = sub->type;
-
-		list_add(&sev->list, &events->subscribed);
-		sev = NULL;
+	for (i = 0; i < elems; i++)
+		sev->events[i].sev = sev;
+	sev->type = sub->type;
+	sev->id = sub->id;
+	sev->flags = sub->flags;
+	sev->fh = fh;
+	sev->elems = elems;
+	if (ctrl) {
+		sev->replace = ctrls_replace;
+		sev->merge = ctrls_merge;
 	}
 
+	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+	if (!found_ev)
+		list_add(&sev->list, &fh->subscribed);
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
-	kfree(sev);
+	/* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
+	if (found_ev)
+		kfree(sev);
+	else if (ctrl)
+		v4l2_ctrl_add_event(ctrl, sev);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
 
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
 {
-	struct v4l2_events *events = fh->events;
+	struct v4l2_event_subscription sub;
 	struct v4l2_subscribed_event *sev;
 	unsigned long flags;
 
@@ -263,15 +264,18 @@
 		sev = NULL;
 
 		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-		if (!list_empty(&events->subscribed)) {
-			sev = list_first_entry(&events->subscribed,
-				       struct v4l2_subscribed_event, list);
-			list_del(&sev->list);
+		if (!list_empty(&fh->subscribed)) {
+			sev = list_first_entry(&fh->subscribed,
+					struct v4l2_subscribed_event, list);
+			sub.type = sev->type;
+			sub.id = sev->id;
 		}
 		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-		kfree(sev);
+		if (sev)
+			v4l2_event_unsubscribe(fh, &sub);
 	} while (sev);
 }
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
 
 int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 			   struct v4l2_event_subscription *sub)
@@ -286,11 +290,19 @@
 
 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
-	sev = v4l2_event_subscribed(fh, sub->type);
-	if (sev != NULL)
+	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
+	if (sev != NULL) {
 		list_del(&sev->list);
+		sev->fh = NULL;
+	}
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+	if (sev && sev->type == V4L2_EVENT_CTRL) {
+		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
+
+		if (ctrl)
+			v4l2_ctrl_del_event(ctrl, sev);
+	}
 
 	kfree(sev);
 
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
index 717f71e..122822d 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/video/v4l2-fh.c
@@ -29,23 +29,18 @@
 #include <media/v4l2-event.h>
 #include <media/v4l2-ioctl.h>
 
-int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
 {
 	fh->vdev = vdev;
+	/* Inherit from video_device. May be overridden by the driver. */
+	fh->ctrl_handler = vdev->ctrl_handler;
 	INIT_LIST_HEAD(&fh->list);
 	set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
 	fh->prio = V4L2_PRIORITY_UNSET;
-
-	/*
-	 * fh->events only needs to be initialized if the driver
-	 * supports the VIDIOC_SUBSCRIBE_EVENT ioctl.
-	 */
-	if (vdev->ioctl_ops && vdev->ioctl_ops->vidioc_subscribe_event)
-		return v4l2_event_init(fh);
-
-	fh->events = NULL;
-
-	return 0;
+	init_waitqueue_head(&fh->wait);
+	INIT_LIST_HEAD(&fh->available);
+	INIT_LIST_HEAD(&fh->subscribed);
+	fh->sequence = -1;
 }
 EXPORT_SYMBOL_GPL(v4l2_fh_init);
 
@@ -91,10 +86,8 @@
 {
 	if (fh->vdev == NULL)
 		return;
-
+	v4l2_event_unsubscribe_all(fh);
 	fh->vdev = NULL;
-
-	v4l2_event_free(fh);
 }
 EXPORT_SYMBOL_GPL(v4l2_fh_exit);
 
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 69e8c6f..002ce13 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/version.h>
 
 #include <linux/videodev2.h>
 
@@ -542,12 +543,12 @@
 	struct v4l2_fh *vfh = NULL;
 	struct v4l2_format f_copy;
 	int use_fh_prio = 0;
-	long ret = -EINVAL;
+	long ret = -ENOTTY;
 
 	if (ops == NULL) {
 		printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
 				vfd->name);
-		return -EINVAL;
+		return ret;
 	}
 
 	if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
@@ -605,6 +606,7 @@
 		if (!ops->vidioc_querycap)
 			break;
 
+		cap->version = LINUX_VERSION_CODE;
 		ret = ops->vidioc_querycap(file, fh, cap);
 		if (!ret)
 			dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
@@ -1418,7 +1420,9 @@
 	{
 		struct v4l2_queryctrl *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_queryctrl(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_queryctrl(vfd->ctrl_handler, p);
 		else if (ops->vidioc_queryctrl)
 			ret = ops->vidioc_queryctrl(file, fh, p);
@@ -1438,7 +1442,9 @@
 	{
 		struct v4l2_control *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
 		else if (ops->vidioc_g_ctrl)
 			ret = ops->vidioc_g_ctrl(file, fh, p);
@@ -1470,14 +1476,18 @@
 		struct v4l2_ext_controls ctrls;
 		struct v4l2_ext_control ctrl;
 
-		if (!vfd->ctrl_handler &&
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
 			!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
 			break;
 
 		dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 
+		if (vfh && vfh->ctrl_handler) {
+			ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+			break;
+		}
 		if (vfd->ctrl_handler) {
-			ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+			ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
 			break;
 		}
 		if (ops->vidioc_s_ctrl) {
@@ -1501,7 +1511,9 @@
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
 		else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
 			ret = ops->vidioc_g_ext_ctrls(file, fh, p);
@@ -1515,11 +1527,14 @@
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+				!ops->vidioc_s_ext_ctrls)
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
-		if (vfd->ctrl_handler)
-			ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
+			ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
 		else if (check_ext_ctrls(p, 0))
 			ret = ops->vidioc_s_ext_ctrls(file, fh, p);
 		break;
@@ -1529,10 +1544,13 @@
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+				!ops->vidioc_try_ext_ctrls)
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
 		else if (check_ext_ctrls(p, 0))
 			ret = ops->vidioc_try_ext_ctrls(file, fh, p);
@@ -1542,7 +1560,9 @@
 	{
 		struct v4l2_querymenu *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_querymenu(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_querymenu(vfd->ctrl_handler, p);
 		else if (ops->vidioc_querymenu)
 			ret = ops->vidioc_querymenu(file, fh, p);
@@ -2276,7 +2296,7 @@
 				break;
 			}
 			*user_ptr = (void __user *)buf->m.planes;
-			*kernel_ptr = (void **)&buf->m.planes;
+			*kernel_ptr = (void *)&buf->m.planes;
 			*array_size = sizeof(struct v4l2_plane) * buf->length;
 			ret = 1;
 		}
@@ -2290,7 +2310,7 @@
 
 		if (ctrls->count != 0) {
 			*user_ptr = (void __user *)ctrls->controls;
-			*kernel_ptr = (void **)&ctrls->controls;
+			*kernel_ptr = (void *)&ctrls->controls;
 			*array_size = sizeof(struct v4l2_ext_control)
 				    * ctrls->count;
 			ret = 1;
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index 812729e..b7967c9 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -75,20 +75,7 @@
 		return ret;
 	}
 
-	ret = v4l2_fh_init(&subdev_fh->vfh, vdev);
-	if (ret)
-		goto err;
-
-	if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) {
-		ret = v4l2_event_init(&subdev_fh->vfh);
-		if (ret)
-			goto err;
-
-		ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents);
-		if (ret)
-			goto err;
-	}
-
+	v4l2_fh_init(&subdev_fh->vfh, vdev);
 	v4l2_fh_add(&subdev_fh->vfh);
 	file->private_data = &subdev_fh->vfh;
 #if defined(CONFIG_MEDIA_CONTROLLER)
@@ -155,25 +142,25 @@
 
 	switch (cmd) {
 	case VIDIOC_QUERYCTRL:
-		return v4l2_queryctrl(sd->ctrl_handler, arg);
+		return v4l2_queryctrl(vfh->ctrl_handler, arg);
 
 	case VIDIOC_QUERYMENU:
-		return v4l2_querymenu(sd->ctrl_handler, arg);
+		return v4l2_querymenu(vfh->ctrl_handler, arg);
 
 	case VIDIOC_G_CTRL:
-		return v4l2_g_ctrl(sd->ctrl_handler, arg);
+		return v4l2_g_ctrl(vfh->ctrl_handler, arg);
 
 	case VIDIOC_S_CTRL:
-		return v4l2_s_ctrl(sd->ctrl_handler, arg);
+		return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
 
 	case VIDIOC_G_EXT_CTRLS:
-		return v4l2_g_ext_ctrls(sd->ctrl_handler, arg);
+		return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
 
 	case VIDIOC_S_EXT_CTRLS:
-		return v4l2_s_ext_ctrls(sd->ctrl_handler, arg);
+		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
 
 	case VIDIOC_TRY_EXT_CTRLS:
-		return v4l2_try_ext_ctrls(sd->ctrl_handler, arg);
+		return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
 
 	case VIDIOC_DQEVENT:
 		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
@@ -297,7 +284,7 @@
 	if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
 		return POLLERR;
 
-	poll_wait(file, &fh->events->wait, wait);
+	poll_wait(file, &fh->wait, wait);
 
 	if (v4l2_event_pending(fh))
 		return POLLPRI;
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index ddb8f4b..f300dea 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -108,8 +108,9 @@
 	if (PageHighMem(pages[0]))
 		/* DMA to highmem pages might not work */
 		goto highmem;
-	sg_set_page(&sglist[0], pages[0], PAGE_SIZE - offset, offset);
-	size -= PAGE_SIZE - offset;
+	sg_set_page(&sglist[0], pages[0],
+			min_t(size_t, PAGE_SIZE - offset, size), offset);
+	size -= min_t(size_t, PAGE_SIZE - offset, size);
 	for (i = 1; i < nr_pages; i++) {
 		if (NULL == pages[i])
 			goto nopage;
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
index 10a20d9..065f468 100644
--- a/drivers/media/video/videobuf2-dma-sg.c
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -48,12 +48,10 @@
 	buf->sg_desc.size = size;
 	buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
-	buf->sg_desc.sglist = vmalloc(buf->sg_desc.num_pages *
+	buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
 				      sizeof(*buf->sg_desc.sglist));
 	if (!buf->sg_desc.sglist)
 		goto fail_sglist_alloc;
-	memset(buf->sg_desc.sglist, 0, buf->sg_desc.num_pages *
-	       sizeof(*buf->sg_desc.sglist));
 	sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
 
 	buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
@@ -136,13 +134,11 @@
 	last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
 	buf->sg_desc.num_pages = last - first + 1;
 
-	buf->sg_desc.sglist = vmalloc(
+	buf->sg_desc.sglist = vzalloc(
 		buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
 	if (!buf->sg_desc.sglist)
 		goto userptr_fail_sglist_alloc;
 
-	memset(buf->sg_desc.sglist, 0,
-		buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
 	sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
 
 	buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
index b03c3ae..569eeb3 100644
--- a/drivers/media/video/videobuf2-memops.c
+++ b/drivers/media/video/videobuf2-memops.c
@@ -176,7 +176,7 @@
 
 	vma->vm_ops->open(vma);
 
-	printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+	pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
 			__func__, paddr, vma->vm_start, size);
 
 	return 0;
@@ -194,7 +194,7 @@
 {
 	struct vb2_vmarea_handler *h = vma->vm_private_data;
 
-	printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+	pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
 	       __func__, h, atomic_read(h->refcount), vma->vm_start,
 	       vma->vm_end);
 
@@ -212,7 +212,7 @@
 {
 	struct vb2_vmarea_handler *h = vma->vm_private_data;
 
-	printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+	pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
 	       __func__, h, atomic_read(h->refcount), vma->vm_start,
 	       vma->vm_end);
 
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index d63e9d9..52a0a37 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -36,7 +36,6 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/time.h>
-#include <linux/version.h>
 #include <linux/kmod.h>
 
 #include <linux/i2c.h>
@@ -61,8 +60,7 @@
 // #define VINO_DEBUG
 // #define VINO_DEBUG_INT
 
-#define VINO_MODULE_VERSION "0.0.6"
-#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 6)
+#define VINO_MODULE_VERSION "0.0.7"
 
 MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
 MODULE_VERSION(VINO_MODULE_VERSION);
@@ -2934,7 +2932,6 @@
 	strcpy(cap->driver, vino_driver_name);
 	strcpy(cap->card, vino_driver_description);
 	strcpy(cap->bus_info, vino_bus_name);
-	cap->version = VINO_VERSION_CODE;
 	cap->capabilities =
 		V4L2_CAP_VIDEO_CAPTURE |
 		V4L2_CAP_STREAMING;
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 2238a61..a848bd2 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -22,7 +22,6 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/font.h>
-#include <linux/version.h>
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
 #include <linux/kthread.h>
@@ -32,6 +31,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-common.h>
 
 #define VIVI_MODULE_NAME "vivi"
@@ -44,15 +44,12 @@
 #define MAX_WIDTH 1920
 #define MAX_HEIGHT 1200
 
-#define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 8
-#define VIVI_RELEASE 0
-#define VIVI_VERSION \
-	KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
+#define VIVI_VERSION "0.8.1"
 
 MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
 MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
 MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(VIVI_VERSION);
 
 static unsigned video_nr = -1;
 module_param(video_nr, uint, 0644);
@@ -167,6 +164,11 @@
 	struct v4l2_ctrl	   *contrast;
 	struct v4l2_ctrl	   *saturation;
 	struct v4l2_ctrl	   *hue;
+	struct {
+		/* autogain/gain cluster */
+		struct v4l2_ctrl	   *autogain;
+		struct v4l2_ctrl	   *gain;
+	};
 	struct v4l2_ctrl	   *volume;
 	struct v4l2_ctrl	   *button;
 	struct v4l2_ctrl	   *boolean;
@@ -174,6 +176,7 @@
 	struct v4l2_ctrl	   *int64;
 	struct v4l2_ctrl	   *menu;
 	struct v4l2_ctrl	   *string;
+	struct v4l2_ctrl	   *bitmask;
 
 	spinlock_t                 slock;
 	struct mutex		   mutex;
@@ -457,6 +460,7 @@
 	unsigned ms;
 	char str[100];
 	int h, line = 1;
+	s32 gain;
 
 	if (!vbuf)
 		return;
@@ -479,6 +483,7 @@
 			dev->width, dev->height, dev->input);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
 
+	gain = v4l2_ctrl_g_ctrl(dev->gain);
 	mutex_lock(&dev->ctrl_handler.lock);
 	snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
 			dev->brightness->cur.val,
@@ -486,11 +491,13 @@
 			dev->saturation->cur.val,
 			dev->hue->cur.val);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
-	snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
+	snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ",
+			dev->autogain->cur.val, gain, dev->volume->cur.val);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
-	snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
+	snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
 			dev->int32->cur.val,
-			dev->int64->cur.val64);
+			dev->int64->cur.val64,
+			dev->bitmask->cur.val);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
 	snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
 			dev->boolean->cur.val,
@@ -524,11 +531,13 @@
 	spin_lock_irqsave(&dev->slock, flags);
 	if (list_empty(&dma_q->active)) {
 		dprintk(dev, 1, "No active queue to serve\n");
-		goto unlock;
+		spin_unlock_irqrestore(&dev->slock, flags);
+		return;
 	}
 
 	buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
 	list_del(&buf->list);
+	spin_unlock_irqrestore(&dev->slock, flags);
 
 	do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
 
@@ -538,8 +547,6 @@
 
 	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
 	dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-unlock:
-	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
 #define frames_to_ms(frames)					\
@@ -812,7 +819,6 @@
 	strcpy(cap->driver, "vivi");
 	strcpy(cap->card, "vivi");
 	strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
-	cap->version = VIVI_VERSION;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
 			    V4L2_CAP_READWRITE;
 	return 0;
@@ -975,14 +981,37 @@
 	if (i >= NUM_INPUTS)
 		return -EINVAL;
 
+	if (i == dev->input)
+		return 0;
+
 	dev->input = i;
 	precalculate_bars(dev);
 	precalculate_line(dev);
 	return 0;
 }
 
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+				struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL:
+		return v4l2_event_subscribe(fh, sub, 0);
+	default:
+		return -EINVAL;
+	}
+}
+
 /* --- controls ---------------------------------------------- */
 
+static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+	if (ctrl == dev->autogain)
+		dev->gain->val = jiffies & 0xff;
+	return 0;
+}
+
 static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
@@ -1010,10 +1039,17 @@
 vivi_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct vivi_dev *dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
 	struct vb2_queue *q = &dev->vb_vidq;
+	unsigned int res;
 
 	dprintk(dev, 1, "%s\n", __func__);
-	return vb2_poll(q, file, wait);
+	res = vb2_poll(q, file, wait);
+	if (v4l2_event_pending(fh))
+		res |= POLLPRI;
+	else
+		poll_wait(file, &fh->wait, wait);
+	return res;
 }
 
 static int vivi_close(struct file *file)
@@ -1045,6 +1081,7 @@
 }
 
 static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+	.g_volatile_ctrl = vivi_g_volatile_ctrl,
 	.s_ctrl = vivi_s_ctrl,
 };
 
@@ -1117,9 +1154,20 @@
 	.step = 1,
 };
 
+static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
+	.ops = &vivi_ctrl_ops,
+	.id = VIVI_CID_CUSTOM_BASE + 6,
+	.name = "Bitmask",
+	.type = V4L2_CTRL_TYPE_BITMASK,
+	.def = 0x80002000,
+	.min = 0,
+	.max = 0x80402010,
+	.step = 0,
+};
+
 static const struct v4l2_file_operations vivi_fops = {
 	.owner		= THIS_MODULE,
-	.open		= v4l2_fh_open,
+	.open           = v4l2_fh_open,
 	.release        = vivi_close,
 	.read           = vivi_read,
 	.poll		= vivi_poll,
@@ -1143,6 +1191,8 @@
 	.vidioc_s_input       = vidioc_s_input,
 	.vidioc_streamon      = vidioc_streamon,
 	.vidioc_streamoff     = vidioc_streamoff,
+	.vidioc_subscribe_event = vidioc_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
 static struct video_device vivi_template = {
@@ -1213,16 +1263,22 @@
 			V4L2_CID_SATURATION, 0, 255, 1, 127);
 	dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
 			V4L2_CID_HUE, -128, 127, 1, 0);
+	dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+			V4L2_CID_GAIN, 0, 255, 1, 100);
 	dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
 	dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
 	dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
 	dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
 	dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
 	dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+	dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
 	if (hdl->error) {
 		ret = hdl->error;
 		goto unreg_dev;
 	}
+	v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
 	dev->v4l2_dev.ctrl_handler = hdl;
 
 	/* initialize locks */
@@ -1325,9 +1381,8 @@
 	}
 
 	printk(KERN_INFO "Video Technology Magazine Virtual Video "
-			"Capture Board ver %u.%u.%u successfully loaded.\n",
-			(VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
-			VIVI_VERSION & 0xFF);
+			"Capture Board ver %s successfully loaded.\n",
+			VIVI_VERSION);
 
 	/* n_devs will reflect the actual number of allocated devices */
 	n_devs = i;
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index fa35639..453dbbd 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -57,7 +57,6 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/slab.h>
 #include <media/v4l2-common.h>
@@ -127,7 +126,7 @@
 MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
 MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
 MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0.33.1");
 
 #ifdef MODULE
 static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
@@ -568,7 +567,6 @@
 	strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
 	strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
 	strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
-	vcap->version = KERNEL_VERSION(0, 33, 0);
 	vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
 	return 0;
 }
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
index f3f6400..d7166af 100644
--- a/drivers/media/video/zoran/zoran.h
+++ b/drivers/media/video/zoran/zoran.h
@@ -41,10 +41,6 @@
 };
 
 
-#define MAJOR_VERSION 0		/* driver major version */
-#define MINOR_VERSION 10	/* driver minor version */
-#define RELEASE_VERSION 0	/* release version */
-
 #define ZORAN_NAME    "ZORAN"	/* name of the device */
 
 #define ZR_DEVNAME(zr) ((zr)->name)
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
index 79b04ac..c3602d6 100644
--- a/drivers/media/video/zoran/zoran_card.c
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -123,9 +123,12 @@
 module_param_named(debug, zr36067_debug, int, 0644);
 MODULE_PARM_DESC(debug, "Debug level (0-5)");
 
+#define ZORAN_VERSION "0.10.1"
+
 MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
 MODULE_AUTHOR("Serguei Miridonov");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(ZORAN_VERSION);
 
 #define ZR_DEVICE(subven, subdev, data)	{ \
 	.vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
@@ -1459,8 +1462,8 @@
 {
 	int res;
 
-	printk(KERN_INFO "Zoran MJPEG board driver version %d.%d.%d\n",
-	       MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
+	printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
+	       ZORAN_VERSION);
 
 	/* check the parameters we have been given, adjust if necessary */
 	if (v4l_nbufs < 2)
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 2771d81..d4d05d2 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -44,7 +44,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -1538,8 +1537,6 @@
 	strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
 	snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
 		 pci_name(zr->pci_dev));
-	cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION,
-			   RELEASE_VERSION);
 	cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
 			    V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
 	return 0;
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 7dfb01e..c492846 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -29,7 +29,6 @@
 
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/vmalloc.h>
@@ -42,8 +41,7 @@
 
 
 /* Version Information */
-#define DRIVER_VERSION "v0.73"
-#define ZR364XX_VERSION_CODE KERNEL_VERSION(0, 7, 3)
+#define DRIVER_VERSION "0.7.4"
 #define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
 #define DRIVER_DESC "Zoran 364xx"
 
@@ -744,7 +742,6 @@
 	strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
 	strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
 		sizeof(cap->bus_info));
-	cap->version = ZR364XX_VERSION_CODE;
 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
 			    V4L2_CAP_READWRITE |
 			    V4L2_CAP_STREAMING;
@@ -1721,3 +1718,4 @@
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index a1d4ee6..ce61a57 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -827,7 +827,7 @@
 				 * DID_SOFT_ERROR is set.
 				 */
 				if (ioc->bus_type == SPI) {
-					if (pScsiReq->CDB[0] == READ_6  ||
+					if ((pScsiReq->CDB[0] == READ_6  && ((pScsiReq->CDB[1] & 0x02) == 0)) ||
 					    pScsiReq->CDB[0] == READ_10 ||
 					    pScsiReq->CDB[0] == READ_12 ||
 					    pScsiReq->CDB[0] == READ_16 ||
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index 74fbe56..c8ed7b6 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -59,7 +59,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6ca938a..37b83eb 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -218,7 +218,7 @@
 	  and load scripts controlling which resources are switched off/on
 	  or reset when a sleep, wakeup or warm reset event occurs.
 
-config TWL4030_CODEC
+config MFD_TWL4030_AUDIO
 	bool
 	depends on TWL4030_CORE
 	select MFD_CORE
@@ -233,6 +233,12 @@
 	  Say yes here if you want support for TWL6030 PWM.
 	  This is used to control charging LED brightness.
 
+config TWL6040_CORE
+	bool
+	depends on TWL4030_CORE && GENERIC_HARDIRQS
+	select MFD_CORE
+	default n
+
 config MFD_STMPE
 	bool "Support STMicroelectronics STMPE"
 	depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d7d47d2..22a280f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -40,8 +40,9 @@
 obj-$(CONFIG_TWL4030_CORE)	+= twl-core.o twl4030-irq.o twl6030-irq.o
 obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
-obj-$(CONFIG_TWL4030_CODEC)	+= twl4030-codec.o
+obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
 obj-$(CONFIG_TWL6030_PWM)	+= twl6030-pwm.o
+obj-$(CONFIG_TWL6040_CORE)	+= twl6040-core.o twl6040-irq.o
 
 obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
 
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 69272e4..696879e 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -287,12 +287,8 @@
 static __devinitdata struct timb_radio_platform_data
 	timberdale_radio_platform_data = {
 	.i2c_adapter = 0,
-	.tuner = {
-		.info = &timberdale_tef6868_i2c_board_info
-	},
-	.dsp = {
-		.info = &timberdale_saa7706_i2c_board_info
-	}
+	.tuner = &timberdale_tef6868_i2c_board_info,
+	.dsp = &timberdale_saa7706_i2c_board_info
 };
 
 static const __devinitconst struct resource timberdale_video_resources[] = {
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index b8f2a4e..a2eddc7 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -110,7 +110,7 @@
 #endif
 
 #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
-	defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE)
+	defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
 #define twl_has_codec()	true
 #else
 #define twl_has_codec()	false
@@ -815,20 +815,19 @@
 			return PTR_ERR(child);
 	}
 
-	if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
+	if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
 		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
 		child = add_child(sub_chip_id, "twl4030-audio",
-				pdata->codec, sizeof(*pdata->codec),
+				pdata->audio, sizeof(*pdata->audio),
 				false, 0, 0);
 		if (IS_ERR(child))
 			return PTR_ERR(child);
 	}
 
-	/* Phoenix codec driver is probed directly atm */
-	if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
+	if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
 		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
-		child = add_child(sub_chip_id, "twl6040-codec",
-				pdata->codec, sizeof(*pdata->codec),
+		child = add_child(sub_chip_id, "twl6040",
+				pdata->audio, sizeof(*pdata->audio),
 				false, 0, 0);
 		if (IS_ERR(child))
 			return PTR_ERR(child);
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
new file mode 100644
index 0000000..ae51ab5
--- /dev/null
+++ b/drivers/mfd/twl4030-audio.c
@@ -0,0 +1,277 @@
+/*
+ * MFD driver for twl4030 audio submodule, which contains an audio codec, and
+ * the vibra control.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright:   (C) 2009 Nokia 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-audio.h>
+
+#define TWL4030_AUDIO_CELLS	2
+
+static struct platform_device *twl4030_audio_dev;
+
+struct twl4030_audio_resource {
+	int request_count;
+	u8 reg;
+	u8 mask;
+};
+
+struct twl4030_audio {
+	unsigned int audio_mclk;
+	struct mutex mutex;
+	struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX];
+	struct mfd_cell cells[TWL4030_AUDIO_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	u8 val;
+
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			audio->resource[id].reg);
+
+	if (enable)
+		val |= audio->resource[id].mask;
+	else
+		val &= ~audio->resource[id].mask;
+
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, audio->resource[id].reg);
+
+	return val;
+}
+
+static inline int twl4030_audio_get_resource(enum twl4030_audio_res id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	u8 val;
+
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			audio->resource[id].reg);
+
+	return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_enable_resource(enum twl4030_audio_res id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	int val;
+
+	if (id >= TWL4030_AUDIO_RES_MAX) {
+		dev_err(&twl4030_audio_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&audio->mutex);
+	if (!audio->resource[id].request_count)
+		/* Resource was disabled, enable it */
+		val = twl4030_audio_set_resource(id, 1);
+	else
+		val = twl4030_audio_get_resource(id);
+
+	audio->resource[id].request_count++;
+	mutex_unlock(&audio->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_disable_resource(unsigned id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	int val;
+
+	if (id >= TWL4030_AUDIO_RES_MAX) {
+		dev_err(&twl4030_audio_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&audio->mutex);
+	if (!audio->resource[id].request_count) {
+		dev_err(&twl4030_audio_dev->dev,
+			"Resource has been disabled already (%u)\n", id);
+		mutex_unlock(&audio->mutex);
+		return -EPERM;
+	}
+	audio->resource[id].request_count--;
+
+	if (!audio->resource[id].request_count)
+		/* Resource can be disabled now */
+		val = twl4030_audio_set_resource(id, 0);
+	else
+		val = twl4030_audio_get_resource(id);
+
+	mutex_unlock(&audio->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
+
+unsigned int twl4030_audio_get_mclk(void)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+
+	return audio->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
+
+static int __devinit twl4030_audio_probe(struct platform_device *pdev)
+{
+	struct twl4030_audio *audio;
+	struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+	struct mfd_cell *cell = NULL;
+	int ret, childs = 0;
+	u8 val;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data is missing\n");
+		return -EINVAL;
+	}
+
+	/* Configure APLL_INFREQ and disable APLL if enabled */
+	val = 0;
+	switch (pdata->audio_mclk) {
+	case 19200000:
+		val |= TWL4030_APLL_INFREQ_19200KHZ;
+		break;
+	case 26000000:
+		val |= TWL4030_APLL_INFREQ_26000KHZ;
+		break;
+	case 38400000:
+		val |= TWL4030_APLL_INFREQ_38400KHZ;
+		break;
+	default:
+		dev_err(&pdev->dev, "Invalid audio_mclk\n");
+		return -EINVAL;
+	}
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, TWL4030_REG_APLL_CTL);
+
+	audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL);
+	if (!audio)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, audio);
+
+	twl4030_audio_dev = pdev;
+	mutex_init(&audio->mutex);
+	audio->audio_mclk = pdata->audio_mclk;
+
+	/* Codec power */
+	audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+	audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ;
+
+	/* PLL */
+	audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+	audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN;
+
+	if (pdata->codec) {
+		cell = &audio->cells[childs];
+		cell->name = "twl4030-codec";
+		cell->platform_data = pdata->codec;
+		cell->pdata_size = sizeof(*pdata->codec);
+		childs++;
+	}
+	if (pdata->vibra) {
+		cell = &audio->cells[childs];
+		cell->name = "twl4030-vibra";
+		cell->platform_data = pdata->vibra;
+		cell->pdata_size = sizeof(*pdata->vibra);
+		childs++;
+	}
+
+	if (childs)
+		ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
+				      childs, NULL, 0);
+	else {
+		dev_err(&pdev->dev, "No platform data found for childs\n");
+		ret = -ENODEV;
+	}
+
+	if (!ret)
+		return 0;
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(audio);
+	twl4030_audio_dev = NULL;
+	return ret;
+}
+
+static int __devexit twl4030_audio_remove(struct platform_device *pdev)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(audio);
+	twl4030_audio_dev = NULL;
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:twl4030-audio");
+
+static struct platform_driver twl4030_audio_driver = {
+	.probe		= twl4030_audio_probe,
+	.remove		= __devexit_p(twl4030_audio_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "twl4030-audio",
+	},
+};
+
+static int __devinit twl4030_audio_init(void)
+{
+	return platform_driver_register(&twl4030_audio_driver);
+}
+module_init(twl4030_audio_init);
+
+static void __devexit twl4030_audio_exit(void)
+{
+	platform_driver_unregister(&twl4030_audio_driver);
+}
+module_exit(twl4030_audio_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
deleted file mode 100644
index 2bf4136..0000000
--- a/drivers/mfd/twl4030-codec.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * MFD driver for twl4030 codec submodule
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Copyright:   (C) 2009 Nokia 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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/twl4030-codec.h>
-
-#define TWL4030_CODEC_CELLS	2
-
-static struct platform_device *twl4030_codec_dev;
-
-struct twl4030_codec_resource {
-	int request_count;
-	u8 reg;
-	u8 mask;
-};
-
-struct twl4030_codec {
-	unsigned int audio_mclk;
-	struct mutex mutex;
-	struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
-	struct mfd_cell cells[TWL4030_CODEC_CELLS];
-};
-
-/*
- * Modify the resource, the function returns the content of the register
- * after the modification.
- */
-static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-	u8 val;
-
-	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
-			codec->resource[id].reg);
-
-	if (enable)
-		val |= codec->resource[id].mask;
-	else
-		val &= ~codec->resource[id].mask;
-
-	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					val, codec->resource[id].reg);
-
-	return val;
-}
-
-static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-	u8 val;
-
-	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
-			codec->resource[id].reg);
-
-	return val;
-}
-
-/*
- * Enable the resource.
- * The function returns with error or the content of the register
- */
-int twl4030_codec_enable_resource(enum twl4030_codec_res id)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-	int val;
-
-	if (id >= TWL4030_CODEC_RES_MAX) {
-		dev_err(&twl4030_codec_dev->dev,
-				"Invalid resource ID (%u)\n", id);
-		return -EINVAL;
-	}
-
-	mutex_lock(&codec->mutex);
-	if (!codec->resource[id].request_count)
-		/* Resource was disabled, enable it */
-		val = twl4030_codec_set_resource(id, 1);
-	else
-		val = twl4030_codec_get_resource(id);
-
-	codec->resource[id].request_count++;
-	mutex_unlock(&codec->mutex);
-
-	return val;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
-
-/*
- * Disable the resource.
- * The function returns with error or the content of the register
- */
-int twl4030_codec_disable_resource(unsigned id)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-	int val;
-
-	if (id >= TWL4030_CODEC_RES_MAX) {
-		dev_err(&twl4030_codec_dev->dev,
-				"Invalid resource ID (%u)\n", id);
-		return -EINVAL;
-	}
-
-	mutex_lock(&codec->mutex);
-	if (!codec->resource[id].request_count) {
-		dev_err(&twl4030_codec_dev->dev,
-			"Resource has been disabled already (%u)\n", id);
-		mutex_unlock(&codec->mutex);
-		return -EPERM;
-	}
-	codec->resource[id].request_count--;
-
-	if (!codec->resource[id].request_count)
-		/* Resource can be disabled now */
-		val = twl4030_codec_set_resource(id, 0);
-	else
-		val = twl4030_codec_get_resource(id);
-
-	mutex_unlock(&codec->mutex);
-
-	return val;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
-
-unsigned int twl4030_codec_get_mclk(void)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
-
-	return codec->audio_mclk;
-}
-EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
-
-static int __devinit twl4030_codec_probe(struct platform_device *pdev)
-{
-	struct twl4030_codec *codec;
-	struct twl4030_codec_data *pdata = pdev->dev.platform_data;
-	struct mfd_cell *cell = NULL;
-	int ret, childs = 0;
-	u8 val;
-
-	if (!pdata) {
-		dev_err(&pdev->dev, "Platform data is missing\n");
-		return -EINVAL;
-	}
-
-	/* Configure APLL_INFREQ and disable APLL if enabled */
-	val = 0;
-	switch (pdata->audio_mclk) {
-	case 19200000:
-		val |= TWL4030_APLL_INFREQ_19200KHZ;
-		break;
-	case 26000000:
-		val |= TWL4030_APLL_INFREQ_26000KHZ;
-		break;
-	case 38400000:
-		val |= TWL4030_APLL_INFREQ_38400KHZ;
-		break;
-	default:
-		dev_err(&pdev->dev, "Invalid audio_mclk\n");
-		return -EINVAL;
-	}
-	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					val, TWL4030_REG_APLL_CTL);
-
-	codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
-	if (!codec)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, codec);
-
-	twl4030_codec_dev = pdev;
-	mutex_init(&codec->mutex);
-	codec->audio_mclk = pdata->audio_mclk;
-
-	/* Codec power */
-	codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
-	codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
-
-	/* PLL */
-	codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
-	codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
-
-	if (pdata->audio) {
-		cell = &codec->cells[childs];
-		cell->name = "twl4030-codec";
-		cell->platform_data = pdata->audio;
-		cell->pdata_size = sizeof(*pdata->audio);
-		childs++;
-	}
-	if (pdata->vibra) {
-		cell = &codec->cells[childs];
-		cell->name = "twl4030-vibra";
-		cell->platform_data = pdata->vibra;
-		cell->pdata_size = sizeof(*pdata->vibra);
-		childs++;
-	}
-
-	if (childs)
-		ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
-				      childs, NULL, 0);
-	else {
-		dev_err(&pdev->dev, "No platform data found for childs\n");
-		ret = -ENODEV;
-	}
-
-	if (!ret)
-		return 0;
-
-	platform_set_drvdata(pdev, NULL);
-	kfree(codec);
-	twl4030_codec_dev = NULL;
-	return ret;
-}
-
-static int __devexit twl4030_codec_remove(struct platform_device *pdev)
-{
-	struct twl4030_codec *codec = platform_get_drvdata(pdev);
-
-	mfd_remove_devices(&pdev->dev);
-	platform_set_drvdata(pdev, NULL);
-	kfree(codec);
-	twl4030_codec_dev = NULL;
-
-	return 0;
-}
-
-MODULE_ALIAS("platform:twl4030-audio");
-
-static struct platform_driver twl4030_codec_driver = {
-	.probe		= twl4030_codec_probe,
-	.remove		= __devexit_p(twl4030_codec_remove),
-	.driver		= {
-		.owner	= THIS_MODULE,
-		.name	= "twl4030-audio",
-	},
-};
-
-static int __devinit twl4030_codec_init(void)
-{
-	return platform_driver_register(&twl4030_codec_driver);
-}
-module_init(twl4030_codec_init);
-
-static void __devexit twl4030_codec_exit(void)
-{
-	platform_driver_unregister(&twl4030_codec_driver);
-}
-module_exit(twl4030_codec_exit);
-
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c
new file mode 100644
index 0000000..24d436c
--- /dev/null
+++ b/drivers/mfd/twl6040-core.c
@@ -0,0 +1,620 @@
+/*
+ * MFD driver for TWL6040 audio device
+ *
+ * Authors:	Misael Lopez Cruz <misael.lopez@ti.com>
+ *		Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ *		Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright:	(C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+static struct platform_device *twl6040_dev;
+
+int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
+{
+	int ret;
+	u8 val = 0;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+	if (ret < 0) {
+		mutex_unlock(&twl6040->io_mutex);
+		return ret;
+	}
+	mutex_unlock(&twl6040->io_mutex);
+
+	return val;
+}
+EXPORT_SYMBOL(twl6040_reg_read);
+
+int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
+{
+	int ret;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+	mutex_unlock(&twl6040->io_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_reg_write);
+
+int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+	int ret;
+	u8 val;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+	if (ret)
+		goto out;
+
+	val |= mask;
+	ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+out:
+	mutex_unlock(&twl6040->io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_set_bits);
+
+int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+	int ret;
+	u8 val;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
+	if (ret)
+		goto out;
+
+	val &= ~mask;
+	ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
+out:
+	mutex_unlock(&twl6040->io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_clear_bits);
+
+/* twl6040 codec manual power-up sequence */
+static int twl6040_power_up(struct twl6040 *twl6040)
+{
+	u8 ldoctl, ncpctl, lppllctl;
+	int ret;
+
+	/* enable high-side LDO, reference system and internal oscillator */
+	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		return ret;
+	usleep_range(10000, 10500);
+
+	/* enable negative charge pump */
+	ncpctl = TWL6040_NCPENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+	if (ret)
+		goto ncp_err;
+	usleep_range(1000, 1500);
+
+	/* enable low-side LDO */
+	ldoctl |= TWL6040_LSLDOENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		goto lsldo_err;
+	usleep_range(1000, 1500);
+
+	/* enable low-power PLL */
+	lppllctl = TWL6040_LPLLENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+	if (ret)
+		goto lppll_err;
+	usleep_range(5000, 5500);
+
+	/* disable internal oscillator */
+	ldoctl &= ~TWL6040_OSCENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		goto osc_err;
+
+	return 0;
+
+osc_err:
+	lppllctl &= ~TWL6040_LPLLENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+lppll_err:
+	ldoctl &= ~TWL6040_LSLDOENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+lsldo_err:
+	ncpctl &= ~TWL6040_NCPENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+ncp_err:
+	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+	return ret;
+}
+
+/* twl6040 manual power-down sequence */
+static void twl6040_power_down(struct twl6040 *twl6040)
+{
+	u8 ncpctl, ldoctl, lppllctl;
+
+	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
+	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
+	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+	/* enable internal oscillator */
+	ldoctl |= TWL6040_OSCENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	usleep_range(1000, 1500);
+
+	/* disable low-power PLL */
+	lppllctl &= ~TWL6040_LPLLENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+
+	/* disable low-side LDO */
+	ldoctl &= ~TWL6040_LSLDOENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+	/* disable negative charge pump */
+	ncpctl &= ~TWL6040_NCPENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+
+	/* disable high-side LDO, reference system and internal oscillator */
+	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+}
+
+static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+{
+	struct twl6040 *twl6040 = data;
+	u8 intid, status;
+
+	intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	if (intid & TWL6040_READYINT)
+		complete(&twl6040->ready);
+
+	if (intid & TWL6040_THINT) {
+		status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+		if (status & TWL6040_TSHUTDET) {
+			dev_warn(&twl6040_dev->dev,
+				 "Thermal shutdown, powering-off");
+			twl6040_power(twl6040, 0);
+		} else {
+			dev_warn(&twl6040_dev->dev,
+				 "Leaving thermal shutdown, powering-on");
+			twl6040_power(twl6040, 1);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int twl6040_power_up_completion(struct twl6040 *twl6040,
+				       int naudint)
+{
+	int time_left;
+	u8 intid;
+
+	time_left = wait_for_completion_timeout(&twl6040->ready,
+						msecs_to_jiffies(144));
+	if (!time_left) {
+		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+		if (!(intid & TWL6040_READYINT)) {
+			dev_err(&twl6040_dev->dev,
+				"timeout waiting for READYINT\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+int twl6040_power(struct twl6040 *twl6040, int on)
+{
+	int audpwron = twl6040->audpwron;
+	int naudint = twl6040->irq;
+	int ret = 0;
+
+	mutex_lock(&twl6040->mutex);
+
+	if (on) {
+		/* already powered-up */
+		if (twl6040->power_count++)
+			goto out;
+
+		if (gpio_is_valid(audpwron)) {
+			/* use AUDPWRON line */
+			gpio_set_value(audpwron, 1);
+			/* wait for power-up completion */
+			ret = twl6040_power_up_completion(twl6040, naudint);
+			if (ret) {
+				dev_err(&twl6040_dev->dev,
+					"automatic power-down failed\n");
+				twl6040->power_count = 0;
+				goto out;
+			}
+		} else {
+			/* use manual power-up sequence */
+			ret = twl6040_power_up(twl6040);
+			if (ret) {
+				dev_err(&twl6040_dev->dev,
+					"manual power-up failed\n");
+				twl6040->power_count = 0;
+				goto out;
+			}
+		}
+		/* Default PLL configuration after power up */
+		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
+		twl6040->sysclk = 19200000;
+	} else {
+		/* already powered-down */
+		if (!twl6040->power_count) {
+			dev_err(&twl6040_dev->dev,
+				"device is already powered-off\n");
+			ret = -EPERM;
+			goto out;
+		}
+
+		if (--twl6040->power_count)
+			goto out;
+
+		if (gpio_is_valid(audpwron)) {
+			/* use AUDPWRON line */
+			gpio_set_value(audpwron, 0);
+
+			/* power-down sequence latency */
+			usleep_range(500, 700);
+		} else {
+			/* use manual power-down sequence */
+			twl6040_power_down(twl6040);
+		}
+		twl6040->sysclk = 0;
+	}
+
+out:
+	mutex_unlock(&twl6040->mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_power);
+
+int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
+		    unsigned int freq_in, unsigned int freq_out)
+{
+	u8 hppllctl, lppllctl;
+	int ret = 0;
+
+	mutex_lock(&twl6040->mutex);
+
+	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
+	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+	switch (pll_id) {
+	case TWL6040_SYSCLK_SEL_LPPLL:
+		/* low-power PLL divider */
+		switch (freq_out) {
+		case 17640000:
+			lppllctl |= TWL6040_LPLLFIN;
+			break;
+		case 19200000:
+			lppllctl &= ~TWL6040_LPLLFIN;
+			break;
+		default:
+			dev_err(&twl6040_dev->dev,
+				"freq_out %d not supported\n", freq_out);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+
+		switch (freq_in) {
+		case 32768:
+			lppllctl |= TWL6040_LPLLENA;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+			mdelay(5);
+			lppllctl &= ~TWL6040_HPLLSEL;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+			hppllctl &= ~TWL6040_HPLLENA;
+			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
+					  hppllctl);
+			break;
+		default:
+			dev_err(&twl6040_dev->dev,
+				"freq_in %d not supported\n", freq_in);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+		break;
+	case TWL6040_SYSCLK_SEL_HPPLL:
+		/* high-performance PLL can provide only 19.2 MHz */
+		if (freq_out != 19200000) {
+			dev_err(&twl6040_dev->dev,
+				"freq_out %d not supported\n", freq_out);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+
+		hppllctl &= ~TWL6040_MCLK_MSK;
+
+		switch (freq_in) {
+		case 12000000:
+			/* PLL enabled, active mode */
+			hppllctl |= TWL6040_MCLK_12000KHZ |
+				    TWL6040_HPLLENA;
+			break;
+		case 19200000:
+			/*
+			 * PLL disabled
+			 * (enable PLL if MCLK jitter quality
+			 *  doesn't meet specification)
+			 */
+			hppllctl |= TWL6040_MCLK_19200KHZ;
+			break;
+		case 26000000:
+			/* PLL enabled, active mode */
+			hppllctl |= TWL6040_MCLK_26000KHZ |
+				    TWL6040_HPLLENA;
+			break;
+		case 38400000:
+			/* PLL enabled, active mode */
+			hppllctl |= TWL6040_MCLK_38400KHZ |
+				    TWL6040_HPLLENA;
+			break;
+		default:
+			dev_err(&twl6040_dev->dev,
+				"freq_in %d not supported\n", freq_in);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+
+		/* enable clock slicer to ensure input waveform is square */
+		hppllctl |= TWL6040_HPLLSQRENA;
+
+		twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl);
+		usleep_range(500, 700);
+		lppllctl |= TWL6040_HPLLSEL;
+		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+		lppllctl &= ~TWL6040_LPLLENA;
+		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+		break;
+	default:
+		dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id);
+		ret = -EINVAL;
+		goto pll_out;
+	}
+
+	twl6040->sysclk = freq_out;
+	twl6040->pll = pll_id;
+
+pll_out:
+	mutex_unlock(&twl6040->mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_set_pll);
+
+int twl6040_get_pll(struct twl6040 *twl6040)
+{
+	if (twl6040->power_count)
+		return twl6040->pll;
+	else
+		return -ENODEV;
+}
+EXPORT_SYMBOL(twl6040_get_pll);
+
+unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
+{
+	return twl6040->sysclk;
+}
+EXPORT_SYMBOL(twl6040_get_sysclk);
+
+static struct resource twl6040_vibra_rsrc[] = {
+	{
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource twl6040_codec_rsrc[] = {
+	{
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static int __devinit twl6040_probe(struct platform_device *pdev)
+{
+	struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+	struct twl6040 *twl6040;
+	struct mfd_cell *cell = NULL;
+	int ret, children = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data is missing\n");
+		return -EINVAL;
+	}
+
+	/* In order to operate correctly we need valid interrupt config */
+	if (!pdata->naudint_irq || !pdata->irq_base) {
+		dev_err(&pdev->dev, "Invalid IRQ configuration\n");
+		return -EINVAL;
+	}
+
+	twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL);
+	if (!twl6040)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, twl6040);
+
+	twl6040_dev = pdev;
+	twl6040->dev = &pdev->dev;
+	twl6040->audpwron = pdata->audpwron_gpio;
+	twl6040->irq = pdata->naudint_irq;
+	twl6040->irq_base = pdata->irq_base;
+
+	mutex_init(&twl6040->mutex);
+	mutex_init(&twl6040->io_mutex);
+	init_completion(&twl6040->ready);
+
+	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+
+	if (gpio_is_valid(twl6040->audpwron)) {
+		ret = gpio_request(twl6040->audpwron, "audpwron");
+		if (ret)
+			goto gpio1_err;
+
+		ret = gpio_direction_output(twl6040->audpwron, 0);
+		if (ret)
+			goto gpio2_err;
+	}
+
+	/* ERRATA: Automatic power-up is not possible in ES1.0 */
+	if (twl6040->rev == TWL6040_REV_ES1_0)
+		twl6040->audpwron = -EINVAL;
+
+	/* codec interrupt */
+	ret = twl6040_irq_init(twl6040);
+	if (ret)
+		goto gpio2_err;
+
+	ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
+				   NULL, twl6040_naudint_handler, 0,
+				   "twl6040_irq_ready", twl6040);
+	if (ret) {
+		dev_err(twl6040->dev, "READY IRQ request failed: %d\n",
+			ret);
+		goto irq_err;
+	}
+
+	/* dual-access registers controlled by I2C only */
+	twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
+
+	if (pdata->codec) {
+		int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
+
+		cell = &twl6040->cells[children];
+		cell->name = "twl6040-codec";
+		twl6040_codec_rsrc[0].start = irq;
+		twl6040_codec_rsrc[0].end = irq;
+		cell->resources = twl6040_codec_rsrc;
+		cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
+		cell->platform_data = pdata->codec;
+		cell->pdata_size = sizeof(*pdata->codec);
+		children++;
+	}
+
+	if (pdata->vibra) {
+		int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
+
+		cell = &twl6040->cells[children];
+		cell->name = "twl6040-vibra";
+		twl6040_vibra_rsrc[0].start = irq;
+		twl6040_vibra_rsrc[0].end = irq;
+		cell->resources = twl6040_vibra_rsrc;
+		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
+
+		cell->platform_data = pdata->vibra;
+		cell->pdata_size = sizeof(*pdata->vibra);
+		children++;
+	}
+
+	if (children) {
+		ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells,
+				      children, NULL, 0);
+		if (ret)
+			goto mfd_err;
+	} else {
+		dev_err(&pdev->dev, "No platform data found for children\n");
+		ret = -ENODEV;
+		goto mfd_err;
+	}
+
+	return 0;
+
+mfd_err:
+	free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+irq_err:
+	twl6040_irq_exit(twl6040);
+gpio2_err:
+	if (gpio_is_valid(twl6040->audpwron))
+		gpio_free(twl6040->audpwron);
+gpio1_err:
+	platform_set_drvdata(pdev, NULL);
+	kfree(twl6040);
+	twl6040_dev = NULL;
+	return ret;
+}
+
+static int __devexit twl6040_remove(struct platform_device *pdev)
+{
+	struct twl6040 *twl6040 = platform_get_drvdata(pdev);
+
+	if (twl6040->power_count)
+		twl6040_power(twl6040, 0);
+
+	if (gpio_is_valid(twl6040->audpwron))
+		gpio_free(twl6040->audpwron);
+
+	free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+	twl6040_irq_exit(twl6040);
+
+	mfd_remove_devices(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(twl6040);
+	twl6040_dev = NULL;
+
+	return 0;
+}
+
+static struct platform_driver twl6040_driver = {
+	.probe		= twl6040_probe,
+	.remove		= __devexit_p(twl6040_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "twl6040",
+	},
+};
+
+static int __devinit twl6040_init(void)
+{
+	return platform_driver_register(&twl6040_driver);
+}
+module_init(twl6040_init);
+
+static void __devexit twl6040_exit(void)
+{
+	platform_driver_unregister(&twl6040_driver);
+}
+
+module_exit(twl6040_exit);
+
+MODULE_DESCRIPTION("TWL6040 MFD");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl6040");
diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c
new file mode 100644
index 0000000..b3f8dda
--- /dev/null
+++ b/drivers/mfd/twl6040-irq.c
@@ -0,0 +1,191 @@
+/*
+ * Interrupt controller support for TWL6040
+ *
+ * Author:     Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright:   (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+struct twl6040_irq_data {
+	int mask;
+	int status;
+};
+
+static struct twl6040_irq_data twl6040_irqs[] = {
+	{
+		.mask = TWL6040_THMSK,
+		.status = TWL6040_THINT,
+	},
+	{
+		.mask = TWL6040_PLUGMSK,
+		.status = TWL6040_PLUGINT | TWL6040_UNPLUGINT,
+	},
+	{
+		.mask = TWL6040_HOOKMSK,
+		.status = TWL6040_HOOKINT,
+	},
+	{
+		.mask = TWL6040_HFMSK,
+		.status = TWL6040_HFINT,
+	},
+	{
+		.mask = TWL6040_VIBMSK,
+		.status = TWL6040_VIBINT,
+	},
+	{
+		.mask = TWL6040_READYMSK,
+		.status = TWL6040_READYINT,
+	},
+};
+
+static inline
+struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040,
+					    int irq)
+{
+	return &twl6040_irqs[irq - twl6040->irq_base];
+}
+
+static void twl6040_irq_lock(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_sync_unlock(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+	/* write back to hardware any change in irq mask */
+	if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) {
+		twl6040->irq_masks_cache = twl6040->irq_masks_cur;
+		twl6040_reg_write(twl6040, TWL6040_REG_INTMR,
+				  twl6040->irq_masks_cur);
+	}
+
+	mutex_unlock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_enable(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+	struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+							       data->irq);
+
+	twl6040->irq_masks_cur &= ~irq_data->mask;
+}
+
+static void twl6040_irq_disable(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+	struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+							       data->irq);
+
+	twl6040->irq_masks_cur |= irq_data->mask;
+}
+
+static struct irq_chip twl6040_irq_chip = {
+	.name			= "twl6040",
+	.irq_bus_lock		= twl6040_irq_lock,
+	.irq_bus_sync_unlock	= twl6040_irq_sync_unlock,
+	.irq_enable		= twl6040_irq_enable,
+	.irq_disable		= twl6040_irq_disable,
+};
+
+static irqreturn_t twl6040_irq_thread(int irq, void *data)
+{
+	struct twl6040 *twl6040 = data;
+	u8 intid;
+	int i;
+
+	intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	/* apply masking and report (backwards to handle READYINT first) */
+	for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) {
+		if (twl6040->irq_masks_cur & twl6040_irqs[i].mask)
+			intid &= ~twl6040_irqs[i].status;
+		if (intid & twl6040_irqs[i].status)
+			handle_nested_irq(twl6040->irq_base + i);
+	}
+
+	/* ack unmasked irqs */
+	twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid);
+
+	return IRQ_HANDLED;
+}
+
+int twl6040_irq_init(struct twl6040 *twl6040)
+{
+	int cur_irq, ret;
+	u8 val;
+
+	mutex_init(&twl6040->irq_mutex);
+
+	/* mask the individual interrupt sources */
+	twl6040->irq_masks_cur = TWL6040_ALLINT_MSK;
+	twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
+	twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
+
+	/* Register them with genirq */
+	for (cur_irq = twl6040->irq_base;
+	     cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs);
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, twl6040);
+		irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip,
+					 handle_level_irq);
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread,
+				   IRQF_ONESHOT, "twl6040", twl6040);
+	if (ret) {
+		dev_err(twl6040->dev, "failed to request IRQ %d: %d\n",
+			twl6040->irq, ret);
+		return ret;
+	}
+
+	/* reset interrupts */
+	val = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	/* interrupts cleared on write */
+	twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE);
+
+	return 0;
+}
+EXPORT_SYMBOL(twl6040_irq_init);
+
+void twl6040_irq_exit(struct twl6040 *twl6040)
+{
+	free_irq(twl6040->irq, twl6040);
+}
+EXPORT_SYMBOL(twl6040_irq_exit);
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index b05db55..21b28fc 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -26,7 +26,7 @@
 #include <linux/sched.h>
 #include <linux/mutex.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 
 #define PHANTOM_VERSION		"n0.9.8"
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 6df5a55..053d36c 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -45,7 +45,7 @@
 
 MODULE_AUTHOR("VMware, Inc.");
 MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver");
-MODULE_VERSION("1.2.1.2-k");
+MODULE_VERSION("1.2.1.3-k");
 MODULE_ALIAS("dmi:*:svnVMware*:*");
 MODULE_ALIAS("vmware_vmmemctl");
 MODULE_LICENSE("GPL");
@@ -215,7 +215,6 @@
 };
 
 static struct vmballoon balloon;
-static struct workqueue_struct *vmballoon_wq;
 
 /*
  * Send "start" command to the host, communicating supported version
@@ -674,7 +673,12 @@
 			vmballoon_deflate(b);
 	}
 
-	queue_delayed_work(vmballoon_wq, dwork, round_jiffies_relative(HZ));
+	/*
+	 * We are using a freezable workqueue so that balloon operations are
+	 * stopped while the system transitions to/from sleep/hibernation.
+	 */
+	queue_delayed_work(system_freezable_wq,
+			   dwork, round_jiffies_relative(HZ));
 }
 
 /*
@@ -785,12 +789,6 @@
 	if (x86_hyper != &x86_hyper_vmware)
 		return -ENODEV;
 
-	vmballoon_wq = create_freezable_workqueue("vmmemctl");
-	if (!vmballoon_wq) {
-		pr_err("failed to create workqueue\n");
-		return -ENOMEM;
-	}
-
 	INIT_LIST_HEAD(&balloon.pages);
 	INIT_LIST_HEAD(&balloon.refused_pages);
 
@@ -805,34 +803,27 @@
 	 */
 	if (!vmballoon_send_start(&balloon)) {
 		pr_err("failed to send start command to the host\n");
-		error = -EIO;
-		goto fail;
+		return -EIO;
 	}
 
 	if (!vmballoon_send_guest_id(&balloon)) {
 		pr_err("failed to send guest ID to the host\n");
-		error = -EIO;
-		goto fail;
+		return -EIO;
 	}
 
 	error = vmballoon_debugfs_init(&balloon);
 	if (error)
-		goto fail;
+		return error;
 
-	queue_delayed_work(vmballoon_wq, &balloon.dwork, 0);
+	queue_delayed_work(system_freezable_wq, &balloon.dwork, 0);
 
 	return 0;
-
-fail:
-	destroy_workqueue(vmballoon_wq);
-	return error;
 }
 module_init(vmballoon_init);
 
 static void __exit vmballoon_exit(void)
 {
 	cancel_delayed_work_sync(&balloon.dwork);
-	destroy_workqueue(vmballoon_wq);
 
 	vmballoon_debugfs_exit(&balloon);
 
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 710b706..9ebfb4b 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -20,7 +20,9 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sdio.h>
-#include <mach/hardware.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <mach/esdhc.h>
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
@@ -29,7 +31,6 @@
 #define SDHCI_VENDOR_SPEC		0xC0
 #define  SDHCI_VENDOR_SPEC_SDIO_QUIRK	0x00000002
 
-#define ESDHC_FLAG_GPIO_FOR_CD		(1 << 0)
 /*
  * The CMDTYPE of the CMD register (offset 0xE) should be set to
  * "11" when the STOP CMD12 is issued on imx53 to abort one
@@ -43,11 +44,68 @@
  */
 #define ESDHC_FLAG_MULTIBLK_NO_INT	(1 << 1)
 
+enum imx_esdhc_type {
+	IMX25_ESDHC,
+	IMX35_ESDHC,
+	IMX51_ESDHC,
+	IMX53_ESDHC,
+};
+
 struct pltfm_imx_data {
 	int flags;
 	u32 scratchpad;
+	enum imx_esdhc_type devtype;
+	struct esdhc_platform_data boarddata;
 };
 
+static struct platform_device_id imx_esdhc_devtype[] = {
+	{
+		.name = "sdhci-esdhc-imx25",
+		.driver_data = IMX25_ESDHC,
+	}, {
+		.name = "sdhci-esdhc-imx35",
+		.driver_data = IMX35_ESDHC,
+	}, {
+		.name = "sdhci-esdhc-imx51",
+		.driver_data = IMX51_ESDHC,
+	}, {
+		.name = "sdhci-esdhc-imx53",
+		.driver_data = IMX53_ESDHC,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
+
+static const struct of_device_id imx_esdhc_dt_ids[] = {
+	{ .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
+	{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
+	{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
+	{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
+
+static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
+{
+	return data->devtype == IMX25_ESDHC;
+}
+
+static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
+{
+	return data->devtype == IMX35_ESDHC;
+}
+
+static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
+{
+	return data->devtype == IMX51_ESDHC;
+}
+
+static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
+{
+	return data->devtype == IMX53_ESDHC;
+}
+
 static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
 {
 	void __iomem *base = host->ioaddr + (reg & ~0x3);
@@ -60,17 +118,14 @@
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct pltfm_imx_data *imx_data = pltfm_host->priv;
+	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 
-	/* fake CARD_PRESENT flag on mx25/35 */
+	/* fake CARD_PRESENT flag */
 	u32 val = readl(host->ioaddr + reg);
 
 	if (unlikely((reg == SDHCI_PRESENT_STATE)
-			&& (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD))) {
-		struct esdhc_platform_data *boarddata =
-				host->mmc->parent->platform_data;
-
-		if (boarddata && gpio_is_valid(boarddata->cd_gpio)
-				&& gpio_get_value(boarddata->cd_gpio))
+			&& gpio_is_valid(boarddata->cd_gpio))) {
+		if (gpio_get_value(boarddata->cd_gpio))
 			/* no card, if a valid gpio says so... */
 			val &= ~SDHCI_CARD_PRESENT;
 		else
@@ -85,12 +140,12 @@
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct pltfm_imx_data *imx_data = pltfm_host->priv;
+	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 
 	if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
-			&& (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD)))
+			&& (boarddata->cd_type == ESDHC_CD_GPIO)))
 		/*
 		 * these interrupts won't work with a custom card_detect gpio
-		 * (only applied to mx25/35)
 		 */
 		val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
 
@@ -173,6 +228,17 @@
 		return;
 	}
 	esdhc_clrset_le(host, 0xff, val, reg);
+
+	/*
+	 * The esdhc has a design violation to SDHC spec which tells
+	 * that software reset should not affect card detection circuit.
+	 * But esdhc clears its SYSCTL register bits [0..2] during the
+	 * software reset.  This will stop those clocks that card detection
+	 * circuit relies on.  To work around it, we turn the clocks on back
+	 * to keep card detection circuit functional.
+	 */
+	if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1))
+		esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
 }
 
 static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
@@ -189,6 +255,26 @@
 	return clk_get_rate(pltfm_host->clk) / 256 / 16;
 }
 
+static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_imx_data *imx_data = pltfm_host->priv;
+	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+	switch (boarddata->wp_type) {
+	case ESDHC_WP_GPIO:
+		if (gpio_is_valid(boarddata->wp_gpio))
+			return gpio_get_value(boarddata->wp_gpio);
+	case ESDHC_WP_CONTROLLER:
+		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+			       SDHCI_WRITE_PROTECT);
+	case ESDHC_WP_NONE:
+		break;
+	}
+
+	return -ENOSYS;
+}
+
 static struct sdhci_ops sdhci_esdhc_ops = {
 	.read_l = esdhc_readl_le,
 	.read_w = esdhc_readw_le,
@@ -198,6 +284,7 @@
 	.set_clock = esdhc_set_clock,
 	.get_max_clock = esdhc_pltfm_get_max_clock,
 	.get_min_clock = esdhc_pltfm_get_min_clock,
+	.get_ro = esdhc_pltfm_get_ro,
 };
 
 static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -207,17 +294,6 @@
 	.ops = &sdhci_esdhc_ops,
 };
 
-static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
-{
-	struct esdhc_platform_data *boarddata =
-			host->mmc->parent->platform_data;
-
-	if (boarddata && gpio_is_valid(boarddata->wp_gpio))
-		return gpio_get_value(boarddata->wp_gpio);
-	else
-		return -ENOSYS;
-}
-
 static irqreturn_t cd_irq(int irq, void *data)
 {
 	struct sdhci_host *sdhost = (struct sdhci_host *)data;
@@ -226,8 +302,48 @@
 	return IRQ_HANDLED;
 };
 
+#ifdef CONFIG_OF
+static int __devinit
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+			 struct esdhc_platform_data *boarddata)
+{
+	struct device_node *np = pdev->dev.of_node;
+
+	if (!np)
+		return -ENODEV;
+
+	if (of_get_property(np, "fsl,card-wired", NULL))
+		boarddata->cd_type = ESDHC_CD_PERMANENT;
+
+	if (of_get_property(np, "fsl,cd-controller", NULL))
+		boarddata->cd_type = ESDHC_CD_CONTROLLER;
+
+	if (of_get_property(np, "fsl,wp-controller", NULL))
+		boarddata->wp_type = ESDHC_WP_CONTROLLER;
+
+	boarddata->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+	if (gpio_is_valid(boarddata->cd_gpio))
+		boarddata->cd_type = ESDHC_CD_GPIO;
+
+	boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+	if (gpio_is_valid(boarddata->wp_gpio))
+		boarddata->wp_type = ESDHC_WP_GPIO;
+
+	return 0;
+}
+#else
+static inline int
+sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+			 struct esdhc_platform_data *boarddata)
+{
+	return -ENODEV;
+}
+#endif
+
 static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
 {
+	const struct of_device_id *of_id =
+			of_match_device(imx_esdhc_dt_ids, &pdev->dev);
 	struct sdhci_pltfm_host *pltfm_host;
 	struct sdhci_host *host;
 	struct esdhc_platform_data *boarddata;
@@ -242,8 +358,14 @@
 	pltfm_host = sdhci_priv(host);
 
 	imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
-	if (!imx_data)
-		return -ENOMEM;
+	if (!imx_data) {
+		err = -ENOMEM;
+		goto err_imx_data;
+	}
+
+	if (of_id)
+		pdev->id_entry = of_id->data;
+	imx_data->devtype = pdev->id_entry->driver_data;
 	pltfm_host->priv = imx_data;
 
 	clk = clk_get(mmc_dev(host->mmc), NULL);
@@ -255,50 +377,72 @@
 	clk_enable(clk);
 	pltfm_host->clk = clk;
 
-	if (!cpu_is_mx25())
+	if (!is_imx25_esdhc(imx_data))
 		host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
 
-	if (cpu_is_mx25() || cpu_is_mx35()) {
+	if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
 		/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
 		host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
-		/* write_protect can't be routed to controller, use gpio */
-		sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
-	}
 
-	if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
+	if (is_imx53_esdhc(imx_data))
 		imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
 
-	boarddata = host->mmc->parent->platform_data;
-	if (boarddata) {
+	boarddata = &imx_data->boarddata;
+	if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
+		if (!host->mmc->parent->platform_data) {
+			dev_err(mmc_dev(host->mmc), "no board data!\n");
+			err = -EINVAL;
+			goto no_board_data;
+		}
+		imx_data->boarddata = *((struct esdhc_platform_data *)
+					host->mmc->parent->platform_data);
+	}
+
+	/* write_protect */
+	if (boarddata->wp_type == ESDHC_WP_GPIO) {
 		err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
 		if (err) {
 			dev_warn(mmc_dev(host->mmc),
-				"no write-protect pin available!\n");
-			boarddata->wp_gpio = err;
+				 "no write-protect pin available!\n");
+			boarddata->wp_gpio = -EINVAL;
 		}
+	} else {
+		boarddata->wp_gpio = -EINVAL;
+	}
 
+	/* card_detect */
+	if (boarddata->cd_type != ESDHC_CD_GPIO)
+		boarddata->cd_gpio = -EINVAL;
+
+	switch (boarddata->cd_type) {
+	case ESDHC_CD_GPIO:
 		err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
 		if (err) {
-			dev_warn(mmc_dev(host->mmc),
+			dev_err(mmc_dev(host->mmc),
 				"no card-detect pin available!\n");
 			goto no_card_detect_pin;
 		}
 
-		/* i.MX5x has issues to be researched */
-		if (!cpu_is_mx25() && !cpu_is_mx35())
-			goto not_supported;
-
 		err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
 				 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
 				 mmc_hostname(host->mmc), host);
 		if (err) {
-			dev_warn(mmc_dev(host->mmc), "request irq error\n");
+			dev_err(mmc_dev(host->mmc), "request irq error\n");
 			goto no_card_detect_irq;
 		}
+		/* fall through */
 
-		imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD;
-		/* Now we have a working card_detect again */
+	case ESDHC_CD_CONTROLLER:
+		/* we have a working card_detect back */
 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+		break;
+
+	case ESDHC_CD_PERMANENT:
+		host->mmc->caps = MMC_CAP_NONREMOVABLE;
+		break;
+
+	case ESDHC_CD_NONE:
+		break;
 	}
 
 	err = sdhci_add_host(host);
@@ -307,16 +451,21 @@
 
 	return 0;
 
- no_card_detect_irq:
-	gpio_free(boarddata->cd_gpio);
- no_card_detect_pin:
-	boarddata->cd_gpio = err;
- not_supported:
-	kfree(imx_data);
- err_add_host:
+err_add_host:
+	if (gpio_is_valid(boarddata->cd_gpio))
+		free_irq(gpio_to_irq(boarddata->cd_gpio), host);
+no_card_detect_irq:
+	if (gpio_is_valid(boarddata->cd_gpio))
+		gpio_free(boarddata->cd_gpio);
+	if (gpio_is_valid(boarddata->wp_gpio))
+		gpio_free(boarddata->wp_gpio);
+no_card_detect_pin:
+no_board_data:
 	clk_disable(pltfm_host->clk);
 	clk_put(pltfm_host->clk);
- err_clk_get:
+err_clk_get:
+	kfree(imx_data);
+err_imx_data:
 	sdhci_pltfm_free(pdev);
 	return err;
 }
@@ -325,20 +474,18 @@
 {
 	struct sdhci_host *host = platform_get_drvdata(pdev);
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-	struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;
 	struct pltfm_imx_data *imx_data = pltfm_host->priv;
+	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
 
 	sdhci_remove_host(host, dead);
 
-	if (boarddata && gpio_is_valid(boarddata->wp_gpio))
+	if (gpio_is_valid(boarddata->wp_gpio))
 		gpio_free(boarddata->wp_gpio);
 
-	if (boarddata && gpio_is_valid(boarddata->cd_gpio)) {
+	if (gpio_is_valid(boarddata->cd_gpio)) {
+		free_irq(gpio_to_irq(boarddata->cd_gpio), host);
 		gpio_free(boarddata->cd_gpio);
-
-		if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION))
-			free_irq(gpio_to_irq(boarddata->cd_gpio), host);
 	}
 
 	clk_disable(pltfm_host->clk);
@@ -354,7 +501,9 @@
 	.driver		= {
 		.name	= "sdhci-esdhc-imx",
 		.owner	= THIS_MODULE,
+		.of_match_table = imx_esdhc_dt_ids,
 	},
+	.id_table	= imx_esdhc_devtype,
 	.probe		= sdhci_esdhc_imx_probe,
 	.remove		= __devexit_p(sdhci_esdhc_imx_remove),
 #ifdef CONFIG_PM
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 71c0ce1..6414efe 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -85,6 +85,7 @@
 {
 	struct sdhci_host *host;
 	struct sdhci_pltfm_host *pltfm_host;
+	struct device_node *np = pdev->dev.of_node;
 	struct resource *iomem;
 	int ret;
 
@@ -98,7 +99,7 @@
 		dev_err(&pdev->dev, "Invalid iomem size!\n");
 
 	/* Some PCI-based MFD need the parent here */
-	if (pdev->dev.parent != &platform_bus)
+	if (pdev->dev.parent != &platform_bus && !np)
 		host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
 	else
 		host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 087d880..eeaf643 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -21,6 +21,7 @@
 #include <linux/mutex.h>
 #include <linux/pagemap.h>
 #include <linux/spinlock.h>
+#include <linux/scatterlist.h>
 
 /* Definitions for values the CTRL_SDIO_STATUS register can take. */
 #define TMIO_SDIO_STAT_IOIRQ	0x0001
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b7622c3..e1eca2a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -282,6 +282,7 @@
 obj-$(CONFIG_USB_USBNET)        += usb/
 obj-$(CONFIG_USB_ZD1201)        += usb/
 obj-$(CONFIG_USB_IPHETH)        += usb/
+obj-$(CONFIG_USB_CDC_PHONET)   += usb/
 
 obj-$(CONFIG_WLAN) += wireless/
 obj-$(CONFIG_NET_TULIP) += tulip/
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index 536038b..31798f5 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -1502,13 +1502,13 @@
 	 * firmware to wipe the ring without re-initializing it.
 	 */
 	if (!test_and_set_bit(0, &ap->std_refill_busy))
-		ace_load_std_rx_ring(ap, RX_RING_SIZE);
+		ace_load_std_rx_ring(dev, RX_RING_SIZE);
 	else
 		printk(KERN_ERR "%s: Someone is busy refilling the RX ring\n",
 		       ap->name);
 	if (ap->version >= 2) {
 		if (!test_and_set_bit(0, &ap->mini_refill_busy))
-			ace_load_mini_rx_ring(ap, RX_MINI_SIZE);
+			ace_load_mini_rx_ring(dev, RX_MINI_SIZE);
 		else
 			printk(KERN_ERR "%s: Someone is busy refilling "
 			       "the RX mini ring\n", ap->name);
@@ -1584,9 +1584,10 @@
 }
 
 
-static void ace_tasklet(unsigned long dev)
+static void ace_tasklet(unsigned long arg)
 {
-	struct ace_private *ap = netdev_priv((struct net_device *)dev);
+	struct net_device *dev = (struct net_device *) arg;
+	struct ace_private *ap = netdev_priv(dev);
 	int cur_size;
 
 	cur_size = atomic_read(&ap->cur_rx_bufs);
@@ -1595,7 +1596,7 @@
 #ifdef DEBUG
 		printk("refilling buffers (current %i)\n", cur_size);
 #endif
-		ace_load_std_rx_ring(ap, RX_RING_SIZE - cur_size);
+		ace_load_std_rx_ring(dev, RX_RING_SIZE - cur_size);
 	}
 
 	if (ap->version >= 2) {
@@ -1606,7 +1607,7 @@
 			printk("refilling mini buffers (current %i)\n",
 			       cur_size);
 #endif
-			ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
+			ace_load_mini_rx_ring(dev, RX_MINI_SIZE - cur_size);
 		}
 	}
 
@@ -1616,7 +1617,7 @@
 #ifdef DEBUG
 		printk("refilling jumbo buffers (current %i)\n", cur_size);
 #endif
-		ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
+		ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE - cur_size);
 	}
 	ap->tasklet_pending = 0;
 }
@@ -1642,8 +1643,9 @@
  * done only before the device is enabled, thus no interrupts are
  * generated and by the interrupt handler/tasklet handler.
  */
-static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs)
 {
+	struct ace_private *ap = netdev_priv(dev);
 	struct ace_regs __iomem *regs = ap->regs;
 	short i, idx;
 
@@ -1657,11 +1659,10 @@
 		struct rx_desc *rd;
 		dma_addr_t mapping;
 
-		skb = dev_alloc_skb(ACE_STD_BUFSIZE + NET_IP_ALIGN);
+		skb = netdev_alloc_skb_ip_align(dev, ACE_STD_BUFSIZE);
 		if (!skb)
 			break;
 
-		skb_reserve(skb, NET_IP_ALIGN);
 		mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
 				       offset_in_page(skb->data),
 				       ACE_STD_BUFSIZE,
@@ -1705,8 +1706,9 @@
 }
 
 
-static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs)
 {
+	struct ace_private *ap = netdev_priv(dev);
 	struct ace_regs __iomem *regs = ap->regs;
 	short i, idx;
 
@@ -1718,11 +1720,10 @@
 		struct rx_desc *rd;
 		dma_addr_t mapping;
 
-		skb = dev_alloc_skb(ACE_MINI_BUFSIZE + NET_IP_ALIGN);
+		skb = netdev_alloc_skb_ip_align(dev, ACE_MINI_BUFSIZE);
 		if (!skb)
 			break;
 
-		skb_reserve(skb, NET_IP_ALIGN);
 		mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
 				       offset_in_page(skb->data),
 				       ACE_MINI_BUFSIZE,
@@ -1762,8 +1763,9 @@
  * Load the jumbo rx ring, this may happen at any time if the MTU
  * is changed to a value > 1500.
  */
-static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs)
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs)
 {
+	struct ace_private *ap = netdev_priv(dev);
 	struct ace_regs __iomem *regs = ap->regs;
 	short i, idx;
 
@@ -1774,11 +1776,10 @@
 		struct rx_desc *rd;
 		dma_addr_t mapping;
 
-		skb = dev_alloc_skb(ACE_JUMBO_BUFSIZE + NET_IP_ALIGN);
+		skb = netdev_alloc_skb_ip_align(dev, ACE_JUMBO_BUFSIZE);
 		if (!skb)
 			break;
 
-		skb_reserve(skb, NET_IP_ALIGN);
 		mapping = pci_map_page(ap->pdev, virt_to_page(skb->data),
 				       offset_in_page(skb->data),
 				       ACE_JUMBO_BUFSIZE,
@@ -2196,7 +2197,7 @@
 #ifdef DEBUG
 				printk("low on std buffers %i\n", cur_size);
 #endif
-				ace_load_std_rx_ring(ap,
+				ace_load_std_rx_ring(dev,
 						     RX_RING_SIZE - cur_size);
 			} else
 				run_tasklet = 1;
@@ -2212,7 +2213,8 @@
 					printk("low on mini buffers %i\n",
 					       cur_size);
 #endif
-					ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
+					ace_load_mini_rx_ring(dev,
+							      RX_MINI_SIZE - cur_size);
 				} else
 					run_tasklet = 1;
 			}
@@ -2228,7 +2230,8 @@
 					printk("low on jumbo buffers %i\n",
 					       cur_size);
 #endif
-					ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
+					ace_load_jumbo_rx_ring(dev,
+							       RX_JUMBO_SIZE - cur_size);
 				} else
 					run_tasklet = 1;
 			}
@@ -2267,7 +2270,7 @@
 
 	if (ap->jumbo &&
 	    !test_and_set_bit(0, &ap->jumbo_refill_busy))
-		ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE);
+		ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
 
 	if (dev->flags & IFF_PROMISC) {
 		cmd.evt = C_SET_PROMISC_MODE;
@@ -2575,7 +2578,7 @@
 			       "support\n", dev->name);
 			ap->jumbo = 1;
 			if (!test_and_set_bit(0, &ap->jumbo_refill_busy))
-				ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE);
+				ace_load_jumbo_rx_ring(dev, RX_JUMBO_SIZE);
 			ace_set_rxtx_parms(dev, 1);
 		}
 	} else {
diff --git a/drivers/net/acenic.h b/drivers/net/acenic.h
index f67dc9b..51c486c 100644
--- a/drivers/net/acenic.h
+++ b/drivers/net/acenic.h
@@ -766,9 +766,9 @@
  * Prototypes
  */
 static int ace_init(struct net_device *dev);
-static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs);
-static void ace_load_mini_rx_ring(struct ace_private *ap, int nr_bufs);
-static void ace_load_jumbo_rx_ring(struct ace_private *ap, int nr_bufs);
+static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs);
+static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs);
 static irqreturn_t ace_interrupt(int irq, void *dev_id);
 static int ace_load_firmware(struct net_device *dev);
 static int ace_open(struct net_device *dev);
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 6f0e940..97e6954 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -44,7 +44,7 @@
  * SMP torture testing
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 #include <linux/compiler.h>
diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c
index e0f87cf..d4f7dda 100644
--- a/drivers/net/atlx/atl2.c
+++ b/drivers/net/atlx/atl2.c
@@ -20,7 +20,7 @@
  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/crc32.h>
 #include <linux/dma-mapping.h>
 #include <linux/etherdevice.h>
diff --git a/drivers/net/atlx/atl2.h b/drivers/net/atlx/atl2.h
index 78344dd..bf9016e 100644
--- a/drivers/net/atlx/atl2.h
+++ b/drivers/net/atlx/atl2.h
@@ -25,7 +25,7 @@
 #ifndef _ATL2_H_
 #define _ATL2_H_
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/netdevice.h>
 
 #ifndef _ATL2_HW_H_
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 02842d0..38a83ac 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1557,8 +1557,10 @@
 
 			if (slave_dev->type != ARPHRD_ETHER)
 				bond_setup_by_slave(bond_dev, slave_dev);
-			else
+			else {
 				ether_setup(bond_dev);
+				bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+			}
 
 			netdev_bonding_change(bond_dev,
 					      NETDEV_POST_TYPE_CHANGE);
@@ -4330,7 +4332,7 @@
 	bond_dev->tx_queue_len = 0;
 	bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
 	bond_dev->priv_flags |= IFF_BONDING;
-	bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+	bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 
 	/* At first, we block adding VLANs. That's the only way to
 	 * prevent problems that occur when adding VLANs over an
@@ -4691,7 +4693,7 @@
 		/* miimon and arp_interval not set, we need one so things
 		 * work as expected, see bonding.txt for details
 		 */
-		pr_warning("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
+		pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
 	}
 
 	if (primary && !USES_PRIMARY(bond_mode)) {
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index b60835f..2dfb4bf 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -1025,6 +1025,7 @@
 	int i;
 	struct slave *slave;
 	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ];
 
 	if (!rtnl_trylock())
 		return restart_syscall();
@@ -1035,32 +1036,33 @@
 	if (!USES_PRIMARY(bond->params.mode)) {
 		pr_info("%s: Unable to set primary slave; %s is in mode %d\n",
 			bond->dev->name, bond->dev->name, bond->params.mode);
-	} else {
-		bond_for_each_slave(bond, slave, i) {
-			if (strnicmp
-			    (slave->dev->name, buf,
-			     strlen(slave->dev->name)) == 0) {
-				pr_info("%s: Setting %s as primary slave.\n",
-					bond->dev->name, slave->dev->name);
-				bond->primary_slave = slave;
-				strcpy(bond->params.primary, slave->dev->name);
-				bond_select_active_slave(bond);
-				goto out;
-			}
-		}
+		goto out;
+	}
 
-		/* if we got here, then we didn't match the name of any slave */
+	sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
 
-		if (strlen(buf) == 0 || buf[0] == '\n') {
-			pr_info("%s: Setting primary slave to None.\n",
-				bond->dev->name);
-			bond->primary_slave = NULL;
-				bond_select_active_slave(bond);
-		} else {
-			pr_info("%s: Unable to set %.*s as primary slave as it is not a slave.\n",
-				bond->dev->name, (int)strlen(buf) - 1, buf);
+	/* check to see if we are clearing primary */
+	if (!strlen(ifname) || buf[0] == '\n') {
+		pr_info("%s: Setting primary slave to None.\n",
+			bond->dev->name);
+		bond->primary_slave = NULL;
+		bond_select_active_slave(bond);
+		goto out;
+	}
+
+	bond_for_each_slave(bond, slave, i) {
+		if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+			pr_info("%s: Setting %s as primary slave.\n",
+				bond->dev->name, slave->dev->name);
+			bond->primary_slave = slave;
+			strcpy(bond->params.primary, slave->dev->name);
+			bond_select_active_slave(bond);
+			goto out;
 		}
 	}
+
+	pr_info("%s: Unable to set %.*s as primary slave.\n",
+		bond->dev->name, (int)strlen(buf) - 1, buf);
 out:
 	write_unlock_bh(&bond->curr_slave_lock);
 	read_unlock(&bond->lock);
@@ -1195,6 +1197,7 @@
 	struct slave *old_active = NULL;
 	struct slave *new_active = NULL;
 	struct bonding *bond = to_bond(d);
+	char ifname[IFNAMSIZ];
 
 	if (!rtnl_trylock())
 		return restart_syscall();
@@ -1203,56 +1206,62 @@
 	read_lock(&bond->lock);
 	write_lock_bh(&bond->curr_slave_lock);
 
-	if (!USES_PRIMARY(bond->params.mode))
+	if (!USES_PRIMARY(bond->params.mode)) {
 		pr_info("%s: Unable to change active slave; %s is in mode %d\n",
 			bond->dev->name, bond->dev->name, bond->params.mode);
-	else {
-		bond_for_each_slave(bond, slave, i) {
-			if (strnicmp
-			    (slave->dev->name, buf,
-			     strlen(slave->dev->name)) == 0) {
-        			old_active = bond->curr_active_slave;
-        			new_active = slave;
-        			if (new_active == old_active) {
-					/* do nothing */
-					pr_info("%s: %s is already the current active slave.\n",
+		goto out;
+	}
+
+	sscanf(buf, "%16s", ifname); /* IFNAMSIZ */
+
+	/* check to see if we are clearing active */
+	if (!strlen(ifname) || buf[0] == '\n') {
+		pr_info("%s: Clearing current active slave.\n",
+			bond->dev->name);
+		bond->curr_active_slave = NULL;
+		bond_select_active_slave(bond);
+		goto out;
+	}
+
+	bond_for_each_slave(bond, slave, i) {
+		if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+			old_active = bond->curr_active_slave;
+			new_active = slave;
+			if (new_active == old_active) {
+				/* do nothing */
+				pr_info("%s: %s is already the current"
+					" active slave.\n",
+					bond->dev->name,
+					slave->dev->name);
+				goto out;
+			}
+			else {
+				if ((new_active) &&
+				    (old_active) &&
+				    (new_active->link == BOND_LINK_UP) &&
+				    IS_UP(new_active->dev)) {
+					pr_info("%s: Setting %s as active"
+						" slave.\n",
 						bond->dev->name,
 						slave->dev->name);
-					goto out;
+					bond_change_active_slave(bond,
+								 new_active);
 				}
 				else {
-        				if ((new_active) &&
-            				    (old_active) &&
-				            (new_active->link == BOND_LINK_UP) &&
-				            IS_UP(new_active->dev)) {
-						pr_info("%s: Setting %s as active slave.\n",
-							bond->dev->name,
-							slave->dev->name);
-							bond_change_active_slave(bond, new_active);
-        				}
-					else {
-						pr_info("%s: Could not set %s as active slave; either %s is down or the link is down.\n",
-							bond->dev->name,
-							slave->dev->name,
-							slave->dev->name);
-					}
-					goto out;
+					pr_info("%s: Could not set %s as"
+						" active slave; either %s is"
+						" down or the link is down.\n",
+						bond->dev->name,
+						slave->dev->name,
+						slave->dev->name);
 				}
+				goto out;
 			}
 		}
-
-		/* if we got here, then we didn't match the name of any slave */
-
-		if (strlen(buf) == 0 || buf[0] == '\n') {
-			pr_info("%s: Setting active slave to None.\n",
-				bond->dev->name);
-			bond->primary_slave = NULL;
-			bond_select_active_slave(bond);
-		} else {
-			pr_info("%s: Unable to set %.*s as active slave as it is not a slave.\n",
-				bond->dev->name, (int)strlen(buf) - 1, buf);
-		}
 	}
+
+	pr_info("%s: Unable to set %.*s as active slave.\n",
+		bond->dev->name, (int)strlen(buf) - 1, buf);
  out:
 	write_unlock_bh(&bond->curr_slave_lock);
 	read_unlock(&bond->lock);
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index b414f5a..646c86b 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -98,7 +98,7 @@
 
 #include <net/checksum.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index 086ce04..e0638cb 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -40,7 +40,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/clk.h>
 #include <linux/gpio.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 MODULE_AUTHOR("Eugene Konev <ejka@imfi.kspu.ru>");
 MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 32636a1..805076c 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -34,7 +34,7 @@
 #include <linux/slab.h>
 #include <net/neighbour.h>
 #include <linux/notifier.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/proc_fs.h>
 #include <linux/if_vlan.h>
 #include <net/netevent.h>
diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h
index fd3eb07..7a12d52 100644
--- a/drivers/net/cxgb3/l2t.h
+++ b/drivers/net/cxgb3/l2t.h
@@ -34,7 +34,7 @@
 
 #include <linux/spinlock.h>
 #include "t3cdev.h"
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 enum {
 	L2T_STATE_VALID,	/* entry is up to date */
diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/cxgb3/t3cdev.h
index be55e9a..705713b 100644
--- a/drivers/net/cxgb3/t3cdev.h
+++ b/drivers/net/cxgb3/t3cdev.h
@@ -33,7 +33,7 @@
 #define _T3CDEV_H_
 
 #include <linux/list.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/netdevice.h>
 #include <linux/proc_fs.h>
 #include <linux/skbuff.h>
diff --git a/drivers/net/cxgb4/cxgb4_uld.h b/drivers/net/cxgb4/cxgb4_uld.h
index 1b48c01..b1d39b8 100644
--- a/drivers/net/cxgb4/cxgb4_uld.h
+++ b/drivers/net/cxgb4/cxgb4_uld.h
@@ -38,7 +38,7 @@
 #include <linux/cache.h>
 #include <linux/spinlock.h>
 #include <linux/skbuff.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /* CPL message priority levels */
 enum {
diff --git a/drivers/net/cxgb4/l2t.h b/drivers/net/cxgb4/l2t.h
index 7bd8f42..02b31d0 100644
--- a/drivers/net/cxgb4/l2t.h
+++ b/drivers/net/cxgb4/l2t.h
@@ -37,7 +37,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/if_ether.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct adapter;
 struct l2t_data;
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 5b631fe..e8266cc 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -44,6 +44,10 @@
 #include <linux/platform_device.h>
 #include <linux/phy.h>
 #include <linux/fec.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
 
 #include <asm/cacheflush.h>
 
@@ -66,17 +70,42 @@
 #define FEC_QUIRK_ENET_MAC		(1 << 0)
 /* Controller needs driver to swap frame */
 #define FEC_QUIRK_SWAP_FRAME		(1 << 1)
+/* Controller uses gasket */
+#define FEC_QUIRK_USE_GASKET		(1 << 2)
 
 static struct platform_device_id fec_devtype[] = {
 	{
+		/* keep it for coldfire */
 		.name = DRIVER_NAME,
 		.driver_data = 0,
 	}, {
+		.name = "imx25-fec",
+		.driver_data = FEC_QUIRK_USE_GASKET,
+	}, {
+		.name = "imx27-fec",
+		.driver_data = 0,
+	}, {
 		.name = "imx28-fec",
 		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
-	},
-	{ }
+	}, {
+		/* sentinel */
+	}
 };
+MODULE_DEVICE_TABLE(platform, fec_devtype);
+
+enum imx_fec_type {
+	IMX25_FEC = 1, 	/* runs on i.mx25/50/53 */
+	IMX27_FEC,	/* runs on i.mx27/35/51 */
+	IMX28_FEC,
+};
+
+static const struct of_device_id fec_dt_ids[] = {
+	{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
+	{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
+	{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fec_dt_ids);
 
 static unsigned char macaddr[ETH_ALEN];
 module_param_array(macaddr, byte, NULL, 0);
@@ -427,7 +456,7 @@
 
 	} else {
 #ifdef FEC_MIIGSK_ENR
-		if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
+		if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
 			/* disable the gasket and wait */
 			writel(0, fep->hwp + FEC_MIIGSK_ENR);
 			while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
@@ -436,8 +465,11 @@
 			/*
 			 * configure the gasket:
 			 *   RMII, 50 MHz, no loopback, no echo
+			 *   MII, 25 MHz, no loopback, no echo
 			 */
-			writel(1, fep->hwp + FEC_MIIGSK_CFGR);
+			writel((fep->phy_interface == PHY_INTERFACE_MODE_RMII) ?
+					1 : 0, fep->hwp + FEC_MIIGSK_CFGR);
+
 
 			/* re-enable the gasket */
 			writel(2, fep->hwp + FEC_MIIGSK_ENR);
@@ -734,8 +766,22 @@
 	 */
 	iap = macaddr;
 
+#ifdef CONFIG_OF
 	/*
-	 * 2) from flash or fuse (via platform data)
+	 * 2) from device tree data
+	 */
+	if (!is_valid_ether_addr(iap)) {
+		struct device_node *np = fep->pdev->dev.of_node;
+		if (np) {
+			const char *mac = of_get_mac_address(np);
+			if (mac)
+				iap = (unsigned char *) mac;
+		}
+	}
+#endif
+
+	/*
+	 * 3) from flash or fuse (via platform data)
 	 */
 	if (!is_valid_ether_addr(iap)) {
 #ifdef CONFIG_M5272
@@ -748,7 +794,7 @@
 	}
 
 	/*
-	 * 3) FEC mac registers set by bootloader
+	 * 4) FEC mac registers set by bootloader
 	 */
 	if (!is_valid_ether_addr(iap)) {
 		*((unsigned long *) &tmpaddr[0]) =
@@ -1354,6 +1400,52 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static int __devinit fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+
+	if (np)
+		return of_get_phy_mode(np);
+
+	return -ENODEV;
+}
+
+static int __devinit fec_reset_phy(struct platform_device *pdev)
+{
+	int err, phy_reset;
+	struct device_node *np = pdev->dev.of_node;
+
+	if (!np)
+		return -ENODEV;
+
+	phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
+	err = gpio_request_one(phy_reset, GPIOF_OUT_INIT_LOW, "phy-reset");
+	if (err) {
+		pr_warn("FEC: failed to get gpio phy-reset: %d\n", err);
+		return err;
+	}
+	msleep(1);
+	gpio_set_value(phy_reset, 1);
+
+	return 0;
+}
+#else /* CONFIG_OF */
+static inline int fec_get_phy_mode_dt(struct platform_device *pdev)
+{
+	return -ENODEV;
+}
+
+static inline int fec_reset_phy(struct platform_device *pdev)
+{
+	/*
+	 * In case of platform probe, the reset has been done
+	 * by machine code.
+	 */
+	return 0;
+}
+#endif /* CONFIG_OF */
+
 static int __devinit
 fec_probe(struct platform_device *pdev)
 {
@@ -1362,6 +1454,11 @@
 	struct net_device *ndev;
 	int i, irq, ret = 0;
 	struct resource *r;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_device(fec_dt_ids, &pdev->dev);
+	if (of_id)
+		pdev->id_entry = of_id->data;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r)
@@ -1393,9 +1490,18 @@
 
 	platform_set_drvdata(pdev, ndev);
 
-	pdata = pdev->dev.platform_data;
-	if (pdata)
-		fep->phy_interface = pdata->phy;
+	ret = fec_get_phy_mode_dt(pdev);
+	if (ret < 0) {
+		pdata = pdev->dev.platform_data;
+		if (pdata)
+			fep->phy_interface = pdata->phy;
+		else
+			fep->phy_interface = PHY_INTERFACE_MODE_MII;
+	} else {
+		fep->phy_interface = ret;
+	}
+
+	fec_reset_phy(pdev);
 
 	/* This device has up to three irqs on some platforms */
 	for (i = 0; i < 3; i++) {
@@ -1530,6 +1636,7 @@
 #ifdef CONFIG_PM
 		.pm	= &fec_pm_ops,
 #endif
+		.of_match_table = fec_dt_ids,
 	},
 	.id_table = fec_devtype,
 	.probe	= fec_probe,
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index e64cd9c..e55df30 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -2764,7 +2764,14 @@
 			prefetch(skb->data);
 
 			vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
-			if (vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
+
+			/*
+			 * There's need to check for NETIF_F_HW_VLAN_RX here.
+			 * Even if vlan rx accel is disabled,
+			 * NV_RX3_VLAN_TAG_PRESENT is pseudo randomly set.
+			 */
+			if (dev->features & NETIF_F_HW_VLAN_RX &&
+			    vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
 				u16 vid = vlanflags & NV_RX3_VLAN_TAG_MASK;
 
 				__vlan_hwaccel_put_tag(skb, vid);
@@ -5331,15 +5338,16 @@
 		np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
 		dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG |
 			NETIF_F_TSO | NETIF_F_RXCSUM;
-		dev->features |= dev->hw_features;
 	}
 
 	np->vlanctl_bits = 0;
 	if (id->driver_data & DEV_HAS_VLAN) {
 		np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
-		dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
+		dev->hw_features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
 	}
 
+	dev->features |= dev->hw_features;
+
 	np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
 	if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
 	    (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
@@ -5607,6 +5615,8 @@
 		goto out_error;
 	}
 
+	nv_vlan_mode(dev, dev->features);
+
 	netif_carrier_off(dev);
 
 	dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 835cd25..2659daa 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -388,12 +388,8 @@
 	if (priv->hwts_rx_en)
 		rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE;
 
-	/* keep vlan related bits if it's enabled */
-	if (ndev->features & NETIF_F_HW_VLAN_TX)
-		rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
-
 	if (ndev->features & NETIF_F_HW_VLAN_RX)
-		tctrl |= TCTRL_VLINS;
+		rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
 
 	/* Init rctrl based on our settings */
 	gfar_write(&regs->rctrl, rctrl);
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 0d28378..2a5a34d 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -36,7 +36,7 @@
 #include <linux/tcp.h>
 #include <linux/semaphore.h>
 #include <linux/compat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define SIXPACK_VERSION    "Revision: 0.3.0"
 
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 52b1425..ce555d9 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -36,7 +36,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
 #include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/irq.h>
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 725399e..70cb7d8 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -39,6 +39,7 @@
 #include <linux/bitops.h>
 #include <linux/workqueue.h>
 #include <linux/of.h>
+#include <linux/of_net.h>
 #include <linux/slab.h>
 
 #include <asm/processor.h>
@@ -2506,18 +2507,6 @@
 {
 	struct device_node *np = dev->ofdev->dev.of_node;
 	const void *p;
-	unsigned int plen;
-	const char *pm, *phy_modes[] = {
-		[PHY_MODE_NA] = "",
-		[PHY_MODE_MII] = "mii",
-		[PHY_MODE_RMII] = "rmii",
-		[PHY_MODE_SMII] = "smii",
-		[PHY_MODE_RGMII] = "rgmii",
-		[PHY_MODE_TBI] = "tbi",
-		[PHY_MODE_GMII] = "gmii",
-		[PHY_MODE_RTBI] = "rtbi",
-		[PHY_MODE_SGMII] = "sgmii",
-	};
 
 	/* Read config from device-tree */
 	if (emac_read_uint_prop(np, "mal-device", &dev->mal_ph, 1))
@@ -2566,23 +2555,9 @@
 		dev->mal_burst_size = 256;
 
 	/* PHY mode needs some decoding */
-	dev->phy_mode = PHY_MODE_NA;
-	pm = of_get_property(np, "phy-mode", &plen);
-	if (pm != NULL) {
-		int i;
-		for (i = 0; i < ARRAY_SIZE(phy_modes); i++)
-			if (!strcasecmp(pm, phy_modes[i])) {
-				dev->phy_mode = i;
-				break;
-			}
-	}
-
-	/* Backward compat with non-final DT */
-	if (dev->phy_mode == PHY_MODE_NA && pm != NULL && plen == 4) {
-		u32 nmode = *(const u32 *)pm;
-		if (nmode > PHY_MODE_NA && nmode <= PHY_MODE_SGMII)
-			dev->phy_mode = nmode;
-	}
+	dev->phy_mode = of_get_phy_mode(np);
+	if (dev->phy_mode < 0)
+		dev->phy_mode = PHY_MODE_NA;
 
 	/* Check EMAC version */
 	if (of_device_is_compatible(np, "ibm,emac4sync")) {
diff --git a/drivers/net/ibm_newemac/emac.h b/drivers/net/ibm_newemac/emac.h
index 8a61b59..1568278 100644
--- a/drivers/net/ibm_newemac/emac.h
+++ b/drivers/net/ibm_newemac/emac.h
@@ -26,6 +26,7 @@
 #define __IBM_NEWEMAC_H
 
 #include <linux/types.h>
+#include <linux/phy.h>
 
 /* EMAC registers 			Write Access rules */
 struct emac_regs {
@@ -106,15 +107,15 @@
 /*
  * PHY mode settings (EMAC <-> ZMII/RGMII bridge <-> PHY)
  */
-#define PHY_MODE_NA	0
-#define PHY_MODE_MII	1
-#define PHY_MODE_RMII	2
-#define PHY_MODE_SMII	3
-#define PHY_MODE_RGMII	4
-#define PHY_MODE_TBI	5
-#define PHY_MODE_GMII	6
-#define PHY_MODE_RTBI	7
-#define PHY_MODE_SGMII	8
+#define PHY_MODE_NA	PHY_INTERFACE_MODE_NA
+#define PHY_MODE_MII	PHY_INTERFACE_MODE_MII
+#define PHY_MODE_RMII	PHY_INTERFACE_MODE_RMII
+#define PHY_MODE_SMII	PHY_INTERFACE_MODE_SMII
+#define PHY_MODE_RGMII	PHY_INTERFACE_MODE_RGMII
+#define PHY_MODE_TBI	PHY_INTERFACE_MODE_TBI
+#define PHY_MODE_GMII	PHY_INTERFACE_MODE_GMII
+#define PHY_MODE_RTBI	PHY_INTERFACE_MODE_RTBI
+#define PHY_MODE_SGMII	PHY_INTERFACE_MODE_SGMII
 
 /* EMACx_MR0 */
 #define EMAC_MR0_RXI			0x80000000
diff --git a/drivers/net/ibm_newemac/phy.c b/drivers/net/ibm_newemac/phy.c
index ac9d964..ab4e596 100644
--- a/drivers/net/ibm_newemac/phy.c
+++ b/drivers/net/ibm_newemac/phy.c
@@ -28,12 +28,15 @@
 #include "emac.h"
 #include "phy.h"
 
-static inline int phy_read(struct mii_phy *phy, int reg)
+#define phy_read _phy_read
+#define phy_write _phy_write
+
+static inline int _phy_read(struct mii_phy *phy, int reg)
 {
 	return phy->mdio_read(phy->dev, phy->address, reg);
 }
 
-static inline void phy_write(struct mii_phy *phy, int reg, int val)
+static inline void _phy_write(struct mii_phy *phy, int reg, int val)
 {
 	phy->mdio_write(phy->dev, phy->address, reg, val);
 }
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 838c5b6..ba99af0 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -43,7 +43,7 @@
 #include <linux/ipv6.h>
 #include <linux/slab.h>
 #include <asm/hvcall.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/vio.h>
 #include <asm/iommu.h>
 #include <asm/firmware.h>
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 6e82dd3..46b5f5f 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -183,7 +183,7 @@
 
 	dev->flags |= IFF_NOARP;
 	dev->flags &= ~IFF_MULTICAST;
-	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	random_ether_addr(dev->dev_addr);
 }
 
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index ba631fc..05172c3 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -572,7 +572,7 @@
 {
 	ether_setup(dev);
 
-	dev->priv_flags	       &= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags	       &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	dev->netdev_ops		= &macvlan_netdev_ops;
 	dev->destructor		= free_netdev;
 	dev->header_ops		= &macvlan_hard_header_ops,
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index a475957..3cbda08 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -33,7 +33,7 @@
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 4609bc0..10e5d98 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -48,7 +48,7 @@
 #include <linux/slab.h>
 #include <asm/unaligned.h>
 #include <net/slhc_vj.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/nsproxy.h>
 #include <net/net_namespace.h>
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 8035765..dc3fbf6 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -190,6 +190,7 @@
 
 /* minimum number of free TX descriptors required to wake up TX process */
 #define TG3_TX_WAKEUP_THRESH(tnapi)		((tnapi)->tx_pending / 4)
+#define TG3_TX_BD_DMA_MAX		4096
 
 #define TG3_RAW_IP_ALIGN 2
 
@@ -4824,7 +4825,7 @@
 	txq = netdev_get_tx_queue(tp->dev, index);
 
 	while (sw_idx != hw_idx) {
-		struct ring_info *ri = &tnapi->tx_buffers[sw_idx];
+		struct tg3_tx_ring_info *ri = &tnapi->tx_buffers[sw_idx];
 		struct sk_buff *skb = ri->skb;
 		int i, tx_bug = 0;
 
@@ -4840,6 +4841,12 @@
 
 		ri->skb = NULL;
 
+		while (ri->fragmented) {
+			ri->fragmented = false;
+			sw_idx = NEXT_TX(sw_idx);
+			ri = &tnapi->tx_buffers[sw_idx];
+		}
+
 		sw_idx = NEXT_TX(sw_idx);
 
 		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
@@ -4851,6 +4858,13 @@
 				       dma_unmap_addr(ri, mapping),
 				       skb_shinfo(skb)->frags[i].size,
 				       PCI_DMA_TODEVICE);
+
+			while (ri->fragmented) {
+				ri->fragmented = false;
+				sw_idx = NEXT_TX(sw_idx);
+				ri = &tnapi->tx_buffers[sw_idx];
+			}
+
 			sw_idx = NEXT_TX(sw_idx);
 		}
 
@@ -5901,40 +5915,100 @@
 #endif
 }
 
-static void tg3_set_txd(struct tg3_napi *tnapi, int entry,
-			dma_addr_t mapping, int len, u32 flags,
-			u32 mss_and_is_end)
+static inline void tg3_tx_set_bd(struct tg3_tx_buffer_desc *txbd,
+				 dma_addr_t mapping, u32 len, u32 flags,
+				 u32 mss, u32 vlan)
 {
-	struct tg3_tx_buffer_desc *txd = &tnapi->tx_ring[entry];
-	int is_end = (mss_and_is_end & 0x1);
-	u32 mss = (mss_and_is_end >> 1);
-	u32 vlan_tag = 0;
-
-	if (is_end)
-		flags |= TXD_FLAG_END;
-	if (flags & TXD_FLAG_VLAN) {
-		vlan_tag = flags >> 16;
-		flags &= 0xffff;
-	}
-	vlan_tag |= (mss << TXD_MSS_SHIFT);
-
-	txd->addr_hi = ((u64) mapping >> 32);
-	txd->addr_lo = ((u64) mapping & 0xffffffff);
-	txd->len_flags = (len << TXD_LEN_SHIFT) | flags;
-	txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT;
+	txbd->addr_hi = ((u64) mapping >> 32);
+	txbd->addr_lo = ((u64) mapping & 0xffffffff);
+	txbd->len_flags = (len << TXD_LEN_SHIFT) | (flags & 0x0000ffff);
+	txbd->vlan_tag = (mss << TXD_MSS_SHIFT) | (vlan << TXD_VLAN_TAG_SHIFT);
 }
 
-static void tg3_skb_error_unmap(struct tg3_napi *tnapi,
-				struct sk_buff *skb, int last)
+static bool tg3_tx_frag_set(struct tg3_napi *tnapi, u32 *entry, u32 *budget,
+			    dma_addr_t map, u32 len, u32 flags,
+			    u32 mss, u32 vlan)
+{
+	struct tg3 *tp = tnapi->tp;
+	bool hwbug = false;
+
+	if (tg3_flag(tp, SHORT_DMA_BUG) && len <= 8)
+		hwbug = 1;
+
+	if (tg3_4g_overflow_test(map, len))
+		hwbug = 1;
+
+	if (tg3_40bit_overflow_test(tp, map, len))
+		hwbug = 1;
+
+	if (tg3_flag(tp, 4K_FIFO_LIMIT)) {
+		u32 tmp_flag = flags & ~TXD_FLAG_END;
+		while (len > TG3_TX_BD_DMA_MAX) {
+			u32 frag_len = TG3_TX_BD_DMA_MAX;
+			len -= TG3_TX_BD_DMA_MAX;
+
+			if (len) {
+				tnapi->tx_buffers[*entry].fragmented = true;
+				/* Avoid the 8byte DMA problem */
+				if (len <= 8) {
+					len += TG3_TX_BD_DMA_MAX / 2;
+					frag_len = TG3_TX_BD_DMA_MAX / 2;
+				}
+			} else
+				tmp_flag = flags;
+
+			if (*budget) {
+				tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+					      frag_len, tmp_flag, mss, vlan);
+				(*budget)--;
+				*entry = NEXT_TX(*entry);
+			} else {
+				hwbug = 1;
+				break;
+			}
+
+			map += frag_len;
+		}
+
+		if (len) {
+			if (*budget) {
+				tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+					      len, flags, mss, vlan);
+				(*budget)--;
+				*entry = NEXT_TX(*entry);
+			} else {
+				hwbug = 1;
+			}
+		}
+	} else {
+		tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+			      len, flags, mss, vlan);
+		*entry = NEXT_TX(*entry);
+	}
+
+	return hwbug;
+}
+
+static void tg3_tx_skb_unmap(struct tg3_napi *tnapi, u32 entry, int last)
 {
 	int i;
-	u32 entry = tnapi->tx_prod;
-	struct ring_info *txb = &tnapi->tx_buffers[entry];
+	struct sk_buff *skb;
+	struct tg3_tx_ring_info *txb = &tnapi->tx_buffers[entry];
+
+	skb = txb->skb;
+	txb->skb = NULL;
 
 	pci_unmap_single(tnapi->tp->pdev,
 			 dma_unmap_addr(txb, mapping),
 			 skb_headlen(skb),
 			 PCI_DMA_TODEVICE);
+
+	while (txb->fragmented) {
+		txb->fragmented = false;
+		entry = NEXT_TX(entry);
+		txb = &tnapi->tx_buffers[entry];
+	}
+
 	for (i = 0; i < last; i++) {
 		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 
@@ -5944,18 +6018,24 @@
 		pci_unmap_page(tnapi->tp->pdev,
 			       dma_unmap_addr(txb, mapping),
 			       frag->size, PCI_DMA_TODEVICE);
+
+		while (txb->fragmented) {
+			txb->fragmented = false;
+			entry = NEXT_TX(entry);
+			txb = &tnapi->tx_buffers[entry];
+		}
 	}
 }
 
 /* Workaround 4GB and 40-bit hardware DMA bugs. */
 static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
 				       struct sk_buff *skb,
-				       u32 base_flags, u32 mss)
+				       u32 *entry, u32 *budget,
+				       u32 base_flags, u32 mss, u32 vlan)
 {
 	struct tg3 *tp = tnapi->tp;
 	struct sk_buff *new_skb;
 	dma_addr_t new_addr = 0;
-	u32 entry = tnapi->tx_prod;
 	int ret = 0;
 
 	if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701)
@@ -5976,24 +6056,22 @@
 					  PCI_DMA_TODEVICE);
 		/* Make sure the mapping succeeded */
 		if (pci_dma_mapping_error(tp->pdev, new_addr)) {
-			ret = -1;
 			dev_kfree_skb(new_skb);
-
-		/* Make sure new skb does not cross any 4G boundaries.
-		 * Drop the packet if it does.
-		 */
-		} else if (tg3_4g_overflow_test(new_addr, new_skb->len)) {
-			pci_unmap_single(tp->pdev, new_addr, new_skb->len,
-					 PCI_DMA_TODEVICE);
 			ret = -1;
-			dev_kfree_skb(new_skb);
 		} else {
-			tnapi->tx_buffers[entry].skb = new_skb;
-			dma_unmap_addr_set(&tnapi->tx_buffers[entry],
+			base_flags |= TXD_FLAG_END;
+
+			tnapi->tx_buffers[*entry].skb = new_skb;
+			dma_unmap_addr_set(&tnapi->tx_buffers[*entry],
 					   mapping, new_addr);
 
-			tg3_set_txd(tnapi, entry, new_addr, new_skb->len,
-				    base_flags, 1 | (mss << 1));
+			if (tg3_tx_frag_set(tnapi, entry, budget, new_addr,
+					    new_skb->len, base_flags,
+					    mss, vlan)) {
+				tg3_tx_skb_unmap(tnapi, *entry, 0);
+				dev_kfree_skb(new_skb);
+				ret = -1;
+			}
 		}
 	}
 
@@ -6051,7 +6129,8 @@
 static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct tg3 *tp = netdev_priv(dev);
-	u32 len, entry, base_flags, mss;
+	u32 len, entry, base_flags, mss, vlan = 0;
+	u32 budget;
 	int i = -1, would_hit_hwbug;
 	dma_addr_t mapping;
 	struct tg3_napi *tnapi;
@@ -6063,12 +6142,14 @@
 	if (tg3_flag(tp, ENABLE_TSS))
 		tnapi++;
 
+	budget = tg3_tx_avail(tnapi);
+
 	/* We are running in BH disabled context with netif_tx_lock
 	 * and TX reclaim runs via tp->napi.poll inside of a software
 	 * interrupt.  Furthermore, IRQ processing runs lockless so we have
 	 * no IRQ context deadlocks to worry about either.  Rejoice!
 	 */
-	if (unlikely(tg3_tx_avail(tnapi) <= (skb_shinfo(skb)->nr_frags + 1))) {
+	if (unlikely(budget <= (skb_shinfo(skb)->nr_frags + 1))) {
 		if (!netif_tx_queue_stopped(txq)) {
 			netif_tx_stop_queue(txq);
 
@@ -6153,9 +6234,12 @@
 		}
 	}
 
-	if (vlan_tx_tag_present(skb))
-		base_flags |= (TXD_FLAG_VLAN |
-			       (vlan_tx_tag_get(skb) << 16));
+#ifdef BCM_KERNEL_SUPPORTS_8021Q
+	if (vlan_tx_tag_present(skb)) {
+		base_flags |= TXD_FLAG_VLAN;
+		vlan = vlan_tx_tag_get(skb);
+	}
+#endif
 
 	if (tg3_flag(tp, USE_JUMBO_BDFLAG) &&
 	    !mss && skb->len > VLAN_ETH_FRAME_LEN)
@@ -6174,25 +6258,23 @@
 
 	would_hit_hwbug = 0;
 
-	if (tg3_flag(tp, SHORT_DMA_BUG) && len <= 8)
-		would_hit_hwbug = 1;
-
-	if (tg3_4g_overflow_test(mapping, len))
-		would_hit_hwbug = 1;
-
-	if (tg3_40bit_overflow_test(tp, mapping, len))
-		would_hit_hwbug = 1;
-
 	if (tg3_flag(tp, 5701_DMA_BUG))
 		would_hit_hwbug = 1;
 
-	tg3_set_txd(tnapi, entry, mapping, len, base_flags,
-		    (skb_shinfo(skb)->nr_frags == 0) | (mss << 1));
-
-	entry = NEXT_TX(entry);
+	if (tg3_tx_frag_set(tnapi, &entry, &budget, mapping, len, base_flags |
+			  ((skb_shinfo(skb)->nr_frags == 0) ? TXD_FLAG_END : 0),
+			    mss, vlan))
+		would_hit_hwbug = 1;
 
 	/* Now loop through additional data fragments, and queue them. */
 	if (skb_shinfo(skb)->nr_frags > 0) {
+		u32 tmp_mss = mss;
+
+		if (!tg3_flag(tp, HW_TSO_1) &&
+		    !tg3_flag(tp, HW_TSO_2) &&
+		    !tg3_flag(tp, HW_TSO_3))
+			tmp_mss = 0;
+
 		last = skb_shinfo(skb)->nr_frags - 1;
 		for (i = 0; i <= last; i++) {
 			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
@@ -6209,39 +6291,25 @@
 			if (pci_dma_mapping_error(tp->pdev, mapping))
 				goto dma_error;
 
-			if (tg3_flag(tp, SHORT_DMA_BUG) &&
-			    len <= 8)
+			if (tg3_tx_frag_set(tnapi, &entry, &budget, mapping,
+					    len, base_flags |
+					    ((i == last) ? TXD_FLAG_END : 0),
+					    tmp_mss, vlan))
 				would_hit_hwbug = 1;
-
-			if (tg3_4g_overflow_test(mapping, len))
-				would_hit_hwbug = 1;
-
-			if (tg3_40bit_overflow_test(tp, mapping, len))
-				would_hit_hwbug = 1;
-
-			if (tg3_flag(tp, HW_TSO_1) ||
-			    tg3_flag(tp, HW_TSO_2) ||
-			    tg3_flag(tp, HW_TSO_3))
-				tg3_set_txd(tnapi, entry, mapping, len,
-					    base_flags, (i == last)|(mss << 1));
-			else
-				tg3_set_txd(tnapi, entry, mapping, len,
-					    base_flags, (i == last));
-
-			entry = NEXT_TX(entry);
 		}
 	}
 
 	if (would_hit_hwbug) {
-		tg3_skb_error_unmap(tnapi, skb, i);
+		tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
 
 		/* If the workaround fails due to memory/mapping
 		 * failure, silently drop this packet.
 		 */
-		if (tigon3_dma_hwbug_workaround(tnapi, skb, base_flags, mss))
+		entry = tnapi->tx_prod;
+		budget = tg3_tx_avail(tnapi);
+		if (tigon3_dma_hwbug_workaround(tnapi, skb, &entry, &budget,
+						base_flags, mss, vlan))
 			goto out_unlock;
-
-		entry = NEXT_TX(tnapi->tx_prod);
 	}
 
 	skb_tx_timestamp(skb);
@@ -6269,7 +6337,7 @@
 	return NETDEV_TX_OK;
 
 dma_error:
-	tg3_skb_error_unmap(tnapi, skb, i);
+	tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
 	dev_kfree_skb(skb);
 	tnapi->tx_buffers[tnapi->tx_prod].skb = NULL;
 	return NETDEV_TX_OK;
@@ -6602,35 +6670,13 @@
 		if (!tnapi->tx_buffers)
 			continue;
 
-		for (i = 0; i < TG3_TX_RING_SIZE; ) {
-			struct ring_info *txp;
-			struct sk_buff *skb;
-			unsigned int k;
+		for (i = 0; i < TG3_TX_RING_SIZE; i++) {
+			struct sk_buff *skb = tnapi->tx_buffers[i].skb;
 
-			txp = &tnapi->tx_buffers[i];
-			skb = txp->skb;
-
-			if (skb == NULL) {
-				i++;
+			if (!skb)
 				continue;
-			}
 
-			pci_unmap_single(tp->pdev,
-					 dma_unmap_addr(txp, mapping),
-					 skb_headlen(skb),
-					 PCI_DMA_TODEVICE);
-			txp->skb = NULL;
-
-			i++;
-
-			for (k = 0; k < skb_shinfo(skb)->nr_frags; k++) {
-				txp = &tnapi->tx_buffers[i & (TG3_TX_RING_SIZE - 1)];
-				pci_unmap_page(tp->pdev,
-					       dma_unmap_addr(txp, mapping),
-					       skb_shinfo(skb)->frags[k].size,
-					       PCI_DMA_TODEVICE);
-				i++;
-			}
+			tg3_tx_skb_unmap(tnapi, i, skb_shinfo(skb)->nr_frags);
 
 			dev_kfree_skb_any(skb);
 		}
@@ -6762,9 +6808,9 @@
 		 */
 		if ((!i && !tg3_flag(tp, ENABLE_TSS)) ||
 		    (i && tg3_flag(tp, ENABLE_TSS))) {
-			tnapi->tx_buffers = kzalloc(sizeof(struct ring_info) *
-						    TG3_TX_RING_SIZE,
-						    GFP_KERNEL);
+			tnapi->tx_buffers = kzalloc(
+					       sizeof(struct tg3_tx_ring_info) *
+					       TG3_TX_RING_SIZE, GFP_KERNEL);
 			if (!tnapi->tx_buffers)
 				goto err_out;
 
@@ -8360,7 +8406,7 @@
 	/* Program the jumbo buffer descriptor ring control
 	 * blocks on those devices that have them.
 	 */
-	if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+	if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0 ||
 	    (tg3_flag(tp, JUMBO_CAPABLE) && !tg3_flag(tp, 5780_CLASS))) {
 
 		if (tg3_flag(tp, JUMBO_RING_ENABLE)) {
@@ -11204,6 +11250,7 @@
 {
 	u32 mac_mode, rx_start_idx, rx_idx, tx_idx, opaque_key;
 	u32 base_flags = 0, mss = 0, desc_idx, coal_now, data_off, val;
+	u32 budget;
 	struct sk_buff *skb, *rx_skb;
 	u8 *tx_data;
 	dma_addr_t map;
@@ -11363,6 +11410,10 @@
 		return -EIO;
 	}
 
+	val = tnapi->tx_prod;
+	tnapi->tx_buffers[val].skb = skb;
+	dma_unmap_addr_set(&tnapi->tx_buffers[val], mapping, map);
+
 	tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
 	       rnapi->coal_now);
 
@@ -11370,8 +11421,13 @@
 
 	rx_start_idx = rnapi->hw_status->idx[0].rx_producer;
 
-	tg3_set_txd(tnapi, tnapi->tx_prod, map, tx_len,
-		    base_flags, (mss << 1) | 1);
+	budget = tg3_tx_avail(tnapi);
+	if (tg3_tx_frag_set(tnapi, &val, &budget, map, tx_len,
+			    base_flags | TXD_FLAG_END, mss, 0)) {
+		tnapi->tx_buffers[val].skb = NULL;
+		dev_kfree_skb(skb);
+		return -EIO;
+	}
 
 	tnapi->tx_prod++;
 
@@ -11394,7 +11450,7 @@
 			break;
 	}
 
-	pci_unmap_single(tp->pdev, map, tx_len, PCI_DMA_TODEVICE);
+	tg3_tx_skb_unmap(tnapi, tnapi->tx_prod - 1, 0);
 	dev_kfree_skb(skb);
 
 	if (tx_idx != tnapi->tx_prod)
@@ -13817,7 +13873,7 @@
 		tg3_flag_set(tp, 5705_PLUS);
 
 	/* Determine TSO capabilities */
-	if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+	if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0)
 		; /* Do nothing. HW bug. */
 	else if (tg3_flag(tp, 57765_PLUS))
 		tg3_flag_set(tp, HW_TSO_3);
@@ -13880,11 +13936,14 @@
 	if (tg3_flag(tp, 5755_PLUS))
 		tg3_flag_set(tp, SHORT_DMA_BUG);
 
+	if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+		tg3_flag_set(tp, 4K_FIFO_LIMIT);
+
 	if (tg3_flag(tp, 5717_PLUS))
 		tg3_flag_set(tp, LRG_PROD_RING_CAP);
 
 	if (tg3_flag(tp, 57765_PLUS) &&
-	    GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5719)
+	    tp->pci_chip_rev_id != CHIPREV_ID_5719_A0)
 		tg3_flag_set(tp, USE_JUMBO_BDFLAG);
 
 	if (!tg3_flag(tp, 5705_PLUS) ||
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 691539b..2ea456d 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -2652,6 +2652,12 @@
 	DEFINE_DMA_UNMAP_ADDR(mapping);
 };
 
+struct tg3_tx_ring_info {
+	struct sk_buff			*skb;
+	DEFINE_DMA_UNMAP_ADDR(mapping);
+	bool				fragmented;
+};
+
 struct tg3_link_config {
 	/* Describes what we're trying to get. */
 	u32				advertising;
@@ -2816,7 +2822,7 @@
 	u32				last_tx_cons;
 	u32				prodmbox;
 	struct tg3_tx_buffer_desc	*tx_ring;
-	struct ring_info		*tx_buffers;
+	struct tg3_tx_ring_info		*tx_buffers;
 
 	dma_addr_t			status_mapping;
 	dma_addr_t			rx_rcb_mapping;
@@ -2899,6 +2905,7 @@
 	TG3_FLAG_57765_PLUS,
 	TG3_FLAG_APE_HAS_NCSI,
 	TG3_FLAG_5717_PLUS,
+	TG3_FLAG_4K_FIFO_LIMIT,
 
 	/* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */
 	TG3_FLAG_NUMBER_OF_FLAGS,	/* Last entry in enum TG3_FLAGS */
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 9a6b382..71f3d1a 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -528,6 +528,7 @@
 		dev->netdev_ops = &tap_netdev_ops;
 		/* Ethernet TAP Device */
 		ether_setup(dev);
+		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 		random_ether_addr(dev->dev_addr);
 
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index 5250288..c5c4b4d 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -314,12 +314,11 @@
 	skb_pull(skb, 4);
 
 	while (skb->len > 0) {
-		if ((short)(header & 0x0000ffff) !=
-		    ~((short)((header & 0xffff0000) >> 16))) {
+		if ((header & 0x07ff) != ((~header >> 16) & 0x07ff))
 			netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
-		}
+
 		/* get the packet length */
-		size = (u16) (header & 0x0000ffff);
+		size = (u16) (header & 0x000007ff);
 
 		if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
 			u8 alignment = (unsigned long)skb->data & 0x3;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 7f78db7..5b23767 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -263,6 +263,8 @@
 {
 	ether_setup(dev);
 
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+
 	dev->netdev_ops = &veth_netdev_ops;
 	dev->ethtool_ops = &veth_ethtool_ops;
 	dev->features |= NETIF_F_LLTX;
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index b25c922..eb20281 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -1074,9 +1074,10 @@
 
 	used = pvc_is_used(pvc);
 
-	if (type == ARPHRD_ETHER)
+	if (type == ARPHRD_ETHER) {
 		dev = alloc_netdev(0, "pvceth%d", ether_setup);
-	else
+		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+	} else
 		dev = alloc_netdev(0, "pvc%d", pvc_setup);
 
 	if (!dev) {
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 5eacc65..c421a61 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -155,7 +155,7 @@
 #include <linux/netdevice.h>
 #include <linux/completion.h>
 #include <linux/rwsem.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <net/wimax.h>
 #include <linux/wimax/i2400m.h>
 #include <asm/byteorder.h>
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 55cf71f..e1b3e3c 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -2823,6 +2823,7 @@
 	dev->wireless_data = &ai->wireless_data;
 	dev->irq = irq;
 	dev->base_addr = port;
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 	SET_NETDEV_DEV(dev, dmdev);
 
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index d2293dc..3cab843 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -28,7 +28,7 @@
 
 config B43_BCMA
 	bool "Support for BCMA bus"
-	depends on B43 && BCMA && BROKEN
+	depends on B43 && BCMA
 	default y
 
 config B43_SSB
diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/b43/bus.c
index 64c3f65..05f6c7b 100644
--- a/drivers/net/wireless/b43/bus.c
+++ b/drivers/net/wireless/b43/bus.c
@@ -244,10 +244,12 @@
 #ifdef CONFIG_B43_BCMA
 	case B43_BUS_BCMA:
 		bcma_set_drvdata(dev->bdev, wldev);
+		break;
 #endif
 #ifdef CONFIG_B43_SSB
 	case B43_BUS_SSB:
 		ssb_set_drvdata(dev->sdev, wldev);
+		break;
 #endif
 	}
 }
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 032d466..26f1ab8 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -5350,6 +5350,7 @@
 {
 	struct b43_wl *wl = ssb_get_devtypedata(sdev);
 	struct b43_wldev *wldev = ssb_get_drvdata(sdev);
+	struct b43_bus_dev *dev = wldev->dev;
 
 	/* We must cancel any work here before unregistering from ieee80211,
 	 * as the ieee80211 unreg will destroy the workqueue. */
@@ -5365,14 +5366,14 @@
 		ieee80211_unregister_hw(wl->hw);
 	}
 
-	b43_one_core_detach(wldev->dev);
+	b43_one_core_detach(dev);
 
 	if (list_empty(&wl->devlist)) {
 		b43_leds_unregister(wl);
 		/* Last core on the chip unregistered.
 		 * We can destroy common struct b43_wl.
 		 */
-		b43_wireless_exit(wldev->dev, wl);
+		b43_wireless_exit(dev, wl);
 	}
 }
 
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 17a130d..a610a35 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -8,7 +8,7 @@
 #include <linux/stringify.h>
 #include <linux/netdevice.h>
 #include <linux/pci.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/io.h>
 
 #include <linux/ssb/ssb.h>
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h
index f89c342..686941c 100644
--- a/drivers/net/wireless/b43legacy/dma.h
+++ b/drivers/net/wireless/b43legacy/dma.h
@@ -5,7 +5,7 @@
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/linkage.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "b43legacy.h"
 
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index d508482..89a116f 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -855,6 +855,7 @@
 
 	iface = netdev_priv(dev);
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 	/* kernel callbacks */
 	if (iface) {
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index 0372315..c77e054 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -1596,7 +1596,7 @@
 	usb_free_urb(dev->out_urb);
 	kfree(dev);
 
-	nfc_dev_info(&dev->interface->dev, "NXP PN533 NFC device disconnected");
+	nfc_dev_info(&interface->dev, "NXP PN533 NFC device disconnected");
 }
 
 static struct usb_driver pn533_driver = {
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
index 86f334a..bb18471 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -8,6 +8,51 @@
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
 #include <linux/of_net.h>
+#include <linux/phy.h>
+
+/**
+ * It maps 'enum phy_interface_t' found in include/linux/phy.h
+ * into the device tree binding of 'phy-mode', so that Ethernet
+ * device driver can get phy interface from device tree.
+ */
+static const char *phy_modes[] = {
+	[PHY_INTERFACE_MODE_NA]		= "",
+	[PHY_INTERFACE_MODE_MII]	= "mii",
+	[PHY_INTERFACE_MODE_GMII]	= "gmii",
+	[PHY_INTERFACE_MODE_SGMII]	= "sgmii",
+	[PHY_INTERFACE_MODE_TBI]	= "tbi",
+	[PHY_INTERFACE_MODE_RMII]	= "rmii",
+	[PHY_INTERFACE_MODE_RGMII]	= "rgmii",
+	[PHY_INTERFACE_MODE_RGMII_ID]	= "rgmii-id",
+	[PHY_INTERFACE_MODE_RGMII_RXID]	= "rgmii-rxid",
+	[PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid",
+	[PHY_INTERFACE_MODE_RTBI]	= "rtbi",
+	[PHY_INTERFACE_MODE_SMII]	= "smii",
+};
+
+/**
+ * of_get_phy_mode - Get phy mode for given device_node
+ * @np:	Pointer to the given device_node
+ *
+ * The function gets phy interface string from property 'phy-mode',
+ * and return its index in phy_modes table, or errno in error case.
+ */
+const int of_get_phy_mode(struct device_node *np)
+{
+	const char *pm;
+	int err, i;
+
+	err = of_property_read_string(np, "phy-mode", &pm);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ARRAY_SIZE(phy_modes); i++)
+		if (!strcasecmp(pm, phy_modes[i]))
+			return i;
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(of_get_phy_mode);
 
 /**
  * Search the device tree for the best MAC address to use.  'mac-address' is
diff --git a/drivers/oprofile/oprofile_stats.h b/drivers/oprofile/oprofile_stats.h
index 0b54e46..38b6fc0 100644
--- a/drivers/oprofile/oprofile_stats.h
+++ b/drivers/oprofile/oprofile_stats.h
@@ -10,7 +10,7 @@
 #ifndef OPROFILE_STATS_H
 #define OPROFILE_STATS_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct oprofile_stat_struct {
 	atomic_t sample_lost_no_mm;
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index 8f3faf3..095f29e 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -408,7 +408,7 @@
 }
 EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
 
-static int is_ejectable(acpi_handle handle)
+static int pcihp_is_ejectable(acpi_handle handle)
 {
 	acpi_status status;
 	acpi_handle tmp;
@@ -442,7 +442,7 @@
 		return 0;
 	if (bridge_handle != parent_handle)
 		return 0;
-	return is_ejectable(handle);
+	return pcihp_is_ejectable(handle);
 }
 EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable);
 
@@ -450,7 +450,7 @@
 check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
 	int *found = (int *)context;
-	if (is_ejectable(handle)) {
+	if (pcihp_is_ejectable(handle)) {
 		*found = 1;
 		return AE_CTRL_TERMINATE;
 	}
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index d703e73..3fadf2f 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -32,7 +32,7 @@
 #include <linux/pci_hotplug.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
 #include "cpci_hotplug.h"
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 4952c3b..f1ce99c 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -840,8 +840,9 @@
 	/* Need to read VID early b/c it's used to differentiate CPQ and INTC
 	 * discovery
 	 */
-	rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
-	if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) {
+	vendor_id = pdev->vendor;
+	if ((vendor_id != PCI_VENDOR_ID_COMPAQ) &&
+	    (vendor_id != PCI_VENDOR_ID_INTEL)) {
 		err(msg_HPC_non_compaq_or_intel);
 		rc = -ENODEV;
 		goto err_disable_device;
@@ -868,11 +869,7 @@
 	/* TODO: This code can be made to support non-Compaq or Intel
 	 * subsystem IDs
 	 */
-	rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid);
-	if (rc) {
-		err("%s : pci_read_config_word failed\n", __func__);
-		goto err_disable_device;
-	}
+	subsystem_vid = pdev->subsystem_vendor;
 	dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
 	if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
 		err(msg_HPC_non_compaq_or_intel);
@@ -887,11 +884,7 @@
 		goto err_disable_device;
 	}
 
-	rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid);
-	if (rc) {
-		err("%s : pci_read_config_word failed\n", __func__);
-		goto err_free_ctrl;
-	}
+	subsystem_deviceid = pdev->subsystem_device;
 
 	info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
 
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 085dbb5..1e9c9aa 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -213,6 +213,9 @@
 		goto err_exit;
 	}
 
+	/* Wait for 1 second after checking link training status */
+	msleep(1000);
+
 	/* Check for a power fault */
 	if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
 		ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 50a23da..96dc473 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -275,16 +275,9 @@
          * hot-plug capable downstream port. But old controller might
          * not implement it. In this case, we wait for 1000 ms.
          */
-        if (ctrl->link_active_reporting){
-                /* Wait for Data Link Layer Link Active bit to be set */
+        if (ctrl->link_active_reporting)
                 pcie_wait_link_active(ctrl);
-                /*
-                 * We must wait for 100 ms after the Data Link Layer
-                 * Link Active bit reads 1b before initiating a
-                 * configuration access to the hot added device.
-                 */
-                msleep(100);
-        } else
+        else
                 msleep(1000);
 
 	retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status);
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index 77cb2a1..81525ae 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -55,7 +55,7 @@
 	SMBIOS_ATTR_INSTANCE_SHOW,
 };
 
-static mode_t
+static size_t
 find_smbios_instance_string(struct pci_dev *pdev, char *buf,
 			    enum smbios_attr_enum attribute)
 {
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 692671b..08a95b3 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1905,7 +1905,7 @@
 {
 	int pos;
 	u32 cap;
-	u16 ctrl;
+	u16 flags, ctrl;
 	struct pci_dev *bridge;
 
 	if (!pci_is_pcie(dev) || dev->devfn)
@@ -1923,6 +1923,11 @@
 	if (!pos)
 		return;
 
+	/* ARI is a PCIe v2 feature */
+	pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags);
+	if ((flags & PCI_EXP_FLAGS_VERS) < 2)
+		return;
+
 	pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap);
 	if (!(cap & PCI_EXP_DEVCAP2_ARI))
 		return;
@@ -3186,7 +3191,7 @@
  * @rq: maximum memory read count in bytes
  *    valid values are 128, 256, 512, 1024, 2048, 4096
  *
- * If possible sets maximum read byte count
+ * If possible sets maximum memory read request in bytes
  */
 int pcie_set_readrq(struct pci_dev *dev, int rq)
 {
@@ -3209,7 +3214,7 @@
 	if ((ctl & PCI_EXP_DEVCTL_READRQ) != v) {
 		ctl &= ~PCI_EXP_DEVCTL_READRQ;
 		ctl |= v;
-		err = pci_write_config_dword(dev, cap + PCI_EXP_DEVCTL, ctl);
+		err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
 	}
 
 out:
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 43421fb..9674e9f 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -24,6 +24,7 @@
 #include <linux/suspend.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/kfifo.h>
 #include "aerdrv.h"
 
 static int forceload;
@@ -445,8 +446,7 @@
 	return drv;
 }
 
-static pci_ers_result_t reset_link(struct pcie_device *aerdev,
-		struct pci_dev *dev)
+static pci_ers_result_t reset_link(struct pci_dev *dev)
 {
 	struct pci_dev *udev;
 	pci_ers_result_t status;
@@ -486,7 +486,6 @@
 
 /**
  * do_recovery - handle nonfatal/fatal error recovery process
- * @aerdev: pointer to a pcie_device data structure of root port
  * @dev: pointer to a pci_dev data structure of agent detecting an error
  * @severity: error severity type
  *
@@ -494,8 +493,7 @@
  * error detected message to all downstream drivers within a hierarchy in
  * question and return the returned code.
  */
-static void do_recovery(struct pcie_device *aerdev, struct pci_dev *dev,
-		int severity)
+static void do_recovery(struct pci_dev *dev, int severity)
 {
 	pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
 	enum pci_channel_state state;
@@ -511,7 +509,7 @@
 			report_error_detected);
 
 	if (severity == AER_FATAL) {
-		result = reset_link(aerdev, dev);
+		result = reset_link(dev);
 		if (result != PCI_ERS_RESULT_RECOVERED)
 			goto failed;
 	}
@@ -576,9 +574,73 @@
 			pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
 					info->status);
 	} else
-		do_recovery(aerdev, dev, info->severity);
+		do_recovery(dev, info->severity);
 }
 
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+static void aer_recover_work_func(struct work_struct *work);
+
+#define AER_RECOVER_RING_ORDER		4
+#define AER_RECOVER_RING_SIZE		(1 << AER_RECOVER_RING_ORDER)
+
+struct aer_recover_entry
+{
+	u8	bus;
+	u8	devfn;
+	u16	domain;
+	int	severity;
+};
+
+static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
+		    AER_RECOVER_RING_SIZE);
+/*
+ * Mutual exclusion for writers of aer_recover_ring, reader side don't
+ * need lock, because there is only one reader and lock is not needed
+ * between reader and writer.
+ */
+static DEFINE_SPINLOCK(aer_recover_ring_lock);
+static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
+
+void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
+		       int severity)
+{
+	unsigned long flags;
+	struct aer_recover_entry entry = {
+		.bus		= bus,
+		.devfn		= devfn,
+		.domain		= domain,
+		.severity	= severity,
+	};
+
+	spin_lock_irqsave(&aer_recover_ring_lock, flags);
+	if (kfifo_put(&aer_recover_ring, &entry))
+		schedule_work(&aer_recover_work);
+	else
+		pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
+		       domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+	spin_unlock_irqrestore(&aer_recover_ring_lock, flags);
+}
+EXPORT_SYMBOL_GPL(aer_recover_queue);
+
+static void aer_recover_work_func(struct work_struct *work)
+{
+	struct aer_recover_entry entry;
+	struct pci_dev *pdev;
+
+	while (kfifo_get(&aer_recover_ring, &entry)) {
+		pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
+						   entry.devfn);
+		if (!pdev) {
+			pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n",
+			       entry.domain, entry.bus,
+			       PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
+			continue;
+		}
+		do_recovery(pdev, entry.severity);
+	}
+}
+#endif
+
 /**
  * get_device_error_info - read error status from dev and store it to info
  * @dev: pointer to the device expected to have a error record
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
index b07a42e..3ea5173 100644
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -204,7 +204,7 @@
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
-static int cper_severity_to_aer(int cper_severity)
+int cper_severity_to_aer(int cper_severity)
 {
 	switch (cper_severity) {
 	case CPER_SEV_RECOVERABLE:
@@ -215,6 +215,7 @@
 		return AER_CORRECTABLE;
 	}
 }
+EXPORT_SYMBOL_GPL(cper_severity_to_aer);
 
 void cper_print_aer(const char *prefix, int cper_severity,
 		    struct aer_capability_regs *aer)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9ab492f..795c902 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -68,21 +68,6 @@
 }
 postcore_initcall(pcibus_class_init);
 
-/*
- * Translate the low bits of the PCI base
- * to the resource type
- */
-static inline unsigned int pci_calc_resource_flags(unsigned int flags)
-{
-	if (flags & PCI_BASE_ADDRESS_SPACE_IO)
-		return IORESOURCE_IO;
-
-	if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
-		return IORESOURCE_MEM | IORESOURCE_PREFETCH;
-
-	return IORESOURCE_MEM;
-}
-
 static u64 pci_size(u64 base, u64 maxbase, u64 mask)
 {
 	u64 size = mask & maxbase;	/* Find the significant bits */
@@ -101,18 +86,39 @@
 	return size;
 }
 
-static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar)
+static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
 {
+	u32 mem_type;
+	unsigned long flags;
+
 	if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
-		res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
-		return pci_bar_io;
+		flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
+		flags |= IORESOURCE_IO;
+		return flags;
 	}
 
-	res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
+	flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
+	flags |= IORESOURCE_MEM;
+	if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
+		flags |= IORESOURCE_PREFETCH;
 
-	if (res->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
-		return pci_bar_mem64;
-	return pci_bar_mem32;
+	mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+	switch (mem_type) {
+	case PCI_BASE_ADDRESS_MEM_TYPE_32:
+		break;
+	case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+		dev_info(&dev->dev, "1M mem BAR treated as 32-bit BAR\n");
+		break;
+	case PCI_BASE_ADDRESS_MEM_TYPE_64:
+		flags |= IORESOURCE_MEM_64;
+		break;
+	default:
+		dev_warn(&dev->dev,
+			 "mem unknown type %x treated as 32-bit BAR\n",
+			 mem_type);
+		break;
+	}
+	return flags;
 }
 
 /**
@@ -165,9 +171,9 @@
 		l = 0;
 
 	if (type == pci_bar_unknown) {
-		type = decode_bar(res, l);
-		res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
-		if (type == pci_bar_io) {
+		res->flags = decode_bar(dev, l);
+		res->flags |= IORESOURCE_SIZEALIGN;
+		if (res->flags & IORESOURCE_IO) {
 			l &= PCI_BASE_ADDRESS_IO_MASK;
 			mask = PCI_BASE_ADDRESS_IO_MASK & (u32) IO_SPACE_LIMIT;
 		} else {
@@ -180,7 +186,7 @@
 		mask = (u32)PCI_ROM_ADDRESS_MASK;
 	}
 
-	if (type == pci_bar_mem64) {
+	if (res->flags & IORESOURCE_MEM_64) {
 		u64 l64 = l;
 		u64 sz64 = sz;
 		u64 mask64 = mask | (u64)~0 << 32;
@@ -204,7 +210,6 @@
 			goto fail;
 		}
 
-		res->flags |= IORESOURCE_MEM_64;
 		if ((sizeof(resource_size_t) < 8) && l) {
 			/* Address above 32-bit boundary; disable the BAR */
 			pci_write_config_dword(dev, pos, 0);
@@ -230,7 +235,7 @@
 	}
 
  out:
-	return (type == pci_bar_mem64) ? 1 : 0;
+	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
  fail:
 	res->flags = 0;
 	goto out;
@@ -284,10 +289,6 @@
 		if (!res->end)
 			res->end = limit + 0xfff;
 		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
-	} else {
-		dev_printk(KERN_DEBUG, &dev->dev,
-			 "  bridge window [io  %#06lx-%#06lx] (disabled)\n",
-				 base, limit);
 	}
 }
 
@@ -308,10 +309,6 @@
 		res->start = base;
 		res->end = limit + 0xfffff;
 		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
-	} else {
-		dev_printk(KERN_DEBUG, &dev->dev,
-			"  bridge window [mem %#010lx-%#010lx] (disabled)\n",
-					 base, limit + 0xfffff);
 	}
 }
 
@@ -359,10 +356,6 @@
 		res->start = base;
 		res->end = limit + 0xfffff;
 		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
-	} else {
-		dev_printk(KERN_DEBUG, &dev->dev,
-		     "  bridge window [mem %#010lx-%#010lx pref] (disabled)\n",
-					 base, limit + 0xfffff);
 	}
 }
 
@@ -725,12 +718,14 @@
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
 		/* Prevent assigning a bus number that already exists.
-		 * This can happen when a bridge is hot-plugged */
-		if (pci_find_bus(pci_domain_nr(bus), max+1))
-			goto out;
-		child = pci_add_new_bus(bus, dev, ++max);
-		if (!child)
-			goto out;
+		 * This can happen when a bridge is hot-plugged, so in
+		 * this case we only re-scan this bus. */
+		child = pci_find_bus(pci_domain_nr(bus), max+1);
+		if (!child) {
+			child = pci_add_new_bus(bus, dev, ++max);
+			if (!child)
+				goto out;
+		}
 		buses = (buses & 0xff000000)
 		      | ((unsigned int)(child->primary)     <<  0)
 		      | ((unsigned int)(child->secondary)   <<  8)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 9995842..8a1d3c7 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -336,7 +336,6 @@
 		/* Clear upper 16 bits of I/O base/limit. */
 		io_upper16 = 0;
 		l = 0x00f0;
-		dev_info(&bridge->dev, "  bridge window [io  disabled]\n");
 	}
 	/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
 	pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
@@ -362,7 +361,6 @@
 		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 	} else {
 		l = 0x0000fff0;
-		dev_info(&bridge->dev, "  bridge window [mem disabled]\n");
 	}
 	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
 }
@@ -393,7 +391,6 @@
 		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 	} else {
 		l = 0x0000fff0;
-		dev_info(&bridge->dev, "  bridge window [mem pref disabled]\n");
 	}
 	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
 
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index eec9738..eb219a1 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -21,7 +21,7 @@
 static void __init
 pdev_fixup_irq(struct pci_dev *dev,
 	       u8 (*swizzle)(struct pci_dev *, u8 *),
-	       int (*map_irq)(struct pci_dev *, u8, u8))
+	       int (*map_irq)(const struct pci_dev *, u8, u8))
 {
 	u8 pin, slot;
 	int irq = 0;
@@ -56,7 +56,7 @@
 
 void __init
 pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
-	       int (*map_irq)(struct pci_dev *, u8, u8))
+	       int (*map_irq)(const struct pci_dev *, u8, u8))
 {
 	struct pci_dev *dev = NULL;
 	for_each_pci_dev(dev)
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index bc0e6ee..319f359 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -74,8 +74,7 @@
 			resno, new, check);
 	}
 
-	if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
-	    (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+	if (res->flags & IORESOURCE_MEM_64) {
 		new = region.start >> 16 >> 16;
 		pci_write_config_dword(dev, reg + 4, new);
 		pci_read_config_dword(dev, reg + 4, &check);
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 492b7d8..6fa215a 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -16,7 +16,7 @@
 #include <xen/interface/io/pciif.h>
 #include <asm/xen/pci.h>
 #include <linux/interrupt.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
 #include <linux/time.h>
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index df68618..3195dbd 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -636,6 +636,29 @@
 }
 EXPORT_SYMBOL_GPL(rtc_irq_unregister);
 
+static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
+{
+	/*
+	 * We unconditionally cancel the timer here, because otherwise
+	 * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
+	 * when we manage to start the timer before the callback
+	 * returns HRTIMER_RESTART.
+	 *
+	 * We cannot use hrtimer_cancel() here as a running callback
+	 * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
+	 * would spin forever.
+	 */
+	if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
+		return -1;
+
+	if (enabled) {
+		ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
+
+		hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
+	}
+	return 0;
+}
+
 /**
  * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
  * @rtc: the rtc device
@@ -651,21 +674,21 @@
 	int err = 0;
 	unsigned long flags;
 
+retry:
 	spin_lock_irqsave(&rtc->irq_task_lock, flags);
 	if (rtc->irq_task != NULL && task == NULL)
 		err = -EBUSY;
 	if (rtc->irq_task != task)
 		err = -EACCES;
-
-	if (enabled) {
-		ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
-		hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
-	} else {
-		hrtimer_cancel(&rtc->pie_timer);
+	if (!err) {
+		if (rtc_update_hrtimer(rtc, enabled) < 0) {
+			spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+			cpu_relax();
+			goto retry;
+		}
+		rtc->pie_enabled = enabled;
 	}
-	rtc->pie_enabled = enabled;
 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
-
 	return err;
 }
 EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -685,22 +708,20 @@
 	int err = 0;
 	unsigned long flags;
 
-	if (freq <= 0)
+	if (freq <= 0 || freq > 5000)
 		return -EINVAL;
-
+retry:
 	spin_lock_irqsave(&rtc->irq_task_lock, flags);
 	if (rtc->irq_task != NULL && task == NULL)
 		err = -EBUSY;
 	if (rtc->irq_task != task)
 		err = -EACCES;
-	if (err == 0) {
+	if (!err) {
 		rtc->irq_freq = freq;
-		if (rtc->pie_enabled) {
-			ktime_t period;
-			hrtimer_cancel(&rtc->pie_timer);
-			period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
-			hrtimer_start(&rtc->pie_timer, period,
-					HRTIMER_MODE_REL);
+		if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
+			spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+			cpu_relax();
+			goto retry;
 		}
 	}
 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 77f778b..16c5208 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -21,7 +21,7 @@
 #include <linux/slab.h>
 
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/ebcdic.h>
 
 #include "dasd_int.h"
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 05909a7..a90a02c 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -13,7 +13,7 @@
 #include <linux/smp.h>
 #include <linux/init.h>
 #include <linux/reboot.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/ptrace.h>
 #include <asm/sigp.h>
 #include <asm/smp.h>
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index c837d74..524d988 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -21,7 +21,7 @@
 #include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/cpcmd.h>
 #include <asm/debug.h>
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 7e297c7..0b7245c 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -2,7 +2,7 @@
 #define S390_DEVICE_H
 
 #include <asm/ccwdev.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/wait.h>
 #include <linux/notifier.h>
 #include "io_sch.h"
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 570d4da..e58169c 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -15,7 +15,7 @@
 #include <linux/delay.h>
 #include <linux/gfp.h>
 #include <linux/kernel_stat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/debug.h>
 #include <asm/qdio.h>
 
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 68be6e1..2a1d4df 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -9,7 +9,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/kernel_stat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/debug.h>
 #include <asm/qdio.h>
 #include <asm/airq.h>
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index f8134a4..b77ae51 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -41,7 +41,7 @@
 #include <linux/mutex.h>
 #include <asm/reset.h>
 #include <asm/airq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 #include <asm/isc.h>
 #include <linux/hrtimer.h>
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 8e65447f..88ad33e 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -36,7 +36,7 @@
 #include <linux/seq_file.h>
 #include <linux/compat.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <linux/hw_random.h>
 
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 2176d00..da171b5 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -30,7 +30,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/err.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "ap_bus.h"
diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c
index 44253fd..eb313c3 100644
--- a/drivers/s390/crypto/zcrypt_mono.c
+++ b/drivers/s390/crypto/zcrypt_mono.c
@@ -32,7 +32,7 @@
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
 #include <linux/compat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "ap_bus.h"
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 1afb69c..d84816f 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -30,7 +30,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/err.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "ap_bus.h"
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index aa4c050..bdbdbe1 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -30,7 +30,7 @@
 #include <linux/init.h>
 #include <linux/gfp.h>
 #include <linux/err.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "ap_bus.h"
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 4f85eb7..dd47378 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -31,7 +31,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 #include "ap_bus.h"
diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h
index 1e8b235..a4510cf 100644
--- a/drivers/s390/net/fsm.h
+++ b/drivers/s390/net/fsm.h
@@ -8,7 +8,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /**
  * Define this to get debugging messages.
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 2a4991d..7cac873 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -13,7 +13,7 @@
 #include <linux/slab.h>
 #include <scsi/fc/fc_fcp.h>
 #include <scsi/scsi_eh.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "zfcp_ext.h"
 #include "zfcp_dbf.h"
 #include "zfcp_fc.h"
diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c
index 740da446..965a1fc 100644
--- a/drivers/sbus/char/display7seg.c
+++ b/drivers/sbus/char/display7seg.c
@@ -16,7 +16,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>		/* put_/get_user			*/
 #include <asm/io.h>
 
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 081c171..5ce5170 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -397,7 +397,7 @@
 };
 
 struct be_cmd_bhs {
-	struct iscsi_cmd iscsi_hdr;
+	struct iscsi_scsi_req iscsi_hdr;
 	unsigned char pad1[16];
 	struct pdu_data_out iscsi_data_pdu;
 	unsigned char pad2[BE_SENSE_INFO_SIZE -
@@ -428,7 +428,7 @@
 };
 
 struct be_status_bhs {
-	struct iscsi_cmd iscsi_hdr;
+	struct iscsi_scsi_req iscsi_hdr;
 	unsigned char pad1[16];
 	/**
 	 * The plus 2 below is to hold the sense info length that gets
diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h
index 3b0af11..a796de9 100644
--- a/drivers/scsi/bfa/bfa.h
+++ b/drivers/scsi/bfa/bfa.h
@@ -27,6 +27,7 @@
 struct bfa_s;
 
 typedef void (*bfa_isr_func_t) (struct bfa_s *bfa, struct bfi_msg_s *m);
+typedef void (*bfa_cb_cbfn_status_t) (void *cbarg, bfa_status_t status);
 
 /*
  * Interrupt message handlers
@@ -121,6 +122,7 @@
 #define bfa_cb_queue(__bfa, __hcb_qe, __cbfn, __cbarg) do {	\
 		(__hcb_qe)->cbfn  = (__cbfn);      \
 		(__hcb_qe)->cbarg = (__cbarg);      \
+		(__hcb_qe)->pre_rmv = BFA_FALSE;		\
 		list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q);      \
 	} while (0)
 
@@ -135,6 +137,11 @@
 		}							\
 	} while (0)
 
+#define bfa_cb_queue_status(__bfa, __hcb_qe, __status) do {		\
+		(__hcb_qe)->fw_status = (__status);			\
+		list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q);	\
+} while (0)
+
 #define bfa_cb_queue_done(__hcb_qe) do {	\
 		(__hcb_qe)->once = BFA_FALSE;	\
 	} while (0)
@@ -177,7 +184,7 @@
 struct bfa_hwif_s {
 	void (*hw_reginit)(struct bfa_s *bfa);
 	void (*hw_reqq_ack)(struct bfa_s *bfa, int reqq);
-	void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq);
+	void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq, u32 ci);
 	void (*hw_msix_init)(struct bfa_s *bfa, int nvecs);
 	void (*hw_msix_ctrl_install)(struct bfa_s *bfa);
 	void (*hw_msix_queue_install)(struct bfa_s *bfa);
@@ -268,10 +275,8 @@
 	((__bfa)->iocfc.hwif.hw_msix_queue_install(__bfa))
 #define bfa_msix_uninstall(__bfa)					\
 	((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa))
-#define bfa_isr_rspq_ack(__bfa, __queue) do {				\
-	if ((__bfa)->iocfc.hwif.hw_rspq_ack)				\
-		(__bfa)->iocfc.hwif.hw_rspq_ack(__bfa, __queue);	\
-} while (0)
+#define bfa_isr_rspq_ack(__bfa, __queue, __ci)				\
+	((__bfa)->iocfc.hwif.hw_rspq_ack(__bfa, __queue, __ci))
 #define bfa_isr_reqq_ack(__bfa, __queue) do {				\
 	if ((__bfa)->iocfc.hwif.hw_reqq_ack)				\
 		(__bfa)->iocfc.hwif.hw_reqq_ack(__bfa, __queue);	\
@@ -311,7 +316,7 @@
 void bfa_msix_lpu_err(struct bfa_s *bfa, int vec);
 
 void bfa_hwcb_reginit(struct bfa_s *bfa);
-void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq);
+void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
 void bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs);
 void bfa_hwcb_msix_ctrl_install(struct bfa_s *bfa);
 void bfa_hwcb_msix_queue_install(struct bfa_s *bfa);
@@ -324,7 +329,8 @@
 void bfa_hwct_reginit(struct bfa_s *bfa);
 void bfa_hwct2_reginit(struct bfa_s *bfa);
 void bfa_hwct_reqq_ack(struct bfa_s *bfa, int rspq);
-void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq);
+void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
+void bfa_hwct2_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
 void bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs);
 void bfa_hwct_msix_ctrl_install(struct bfa_s *bfa);
 void bfa_hwct_msix_queue_install(struct bfa_s *bfa);
@@ -376,6 +382,22 @@
 #define bfa_get_fw_clock_res(__bfa)		\
 	((__bfa)->iocfc.cfgrsp->fwcfg.fw_tick_res)
 
+/*
+ * lun mask macros return NULL when min cfg is enabled and there is
+ * no memory allocated for lunmask.
+ */
+#define bfa_get_lun_mask(__bfa)					\
+	((&(__bfa)->modules.dconf_mod)->min_cfg) ? NULL :	\
+	 (&(BFA_DCONF_MOD(__bfa)->dconf->lun_mask))
+
+#define bfa_get_lun_mask_list(_bfa)				\
+	((&(_bfa)->modules.dconf_mod)->min_cfg) ? NULL :	\
+	 (bfa_get_lun_mask(_bfa)->lun_list)
+
+#define bfa_get_lun_mask_status(_bfa)				\
+	(((&(_bfa)->modules.dconf_mod)->min_cfg)		\
+	 ? BFA_LUNMASK_MINCFG : ((bfa_get_lun_mask(_bfa))->status))
+
 void bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids);
 void bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg);
 void bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg);
@@ -406,7 +428,22 @@
 
 void bfa_iocfc_enable(struct bfa_s *bfa);
 void bfa_iocfc_disable(struct bfa_s *bfa);
+void bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status);
 #define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout)		\
 	bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout)
 
+struct bfa_cb_pending_q_s {
+	struct bfa_cb_qe_s	hcb_qe;
+	void			*data;  /* Driver buffer */
+};
+
+/* Common macros to operate on pending stats/attr apis */
+#define bfa_pending_q_init(__qe, __cbfn, __cbarg, __data) do {	\
+	bfa_q_qe_init(&((__qe)->hcb_qe.qe));			\
+	(__qe)->hcb_qe.cbfn = (__cbfn);				\
+	(__qe)->hcb_qe.cbarg = (__cbarg);			\
+	(__qe)->hcb_qe.pre_rmv = BFA_TRUE;			\
+	(__qe)->data = (__data);				\
+} while (0)
+
 #endif /* __BFA_H__ */
diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c
index c38e589..4bd546b 100644
--- a/drivers/scsi/bfa/bfa_core.c
+++ b/drivers/scsi/bfa/bfa_core.c
@@ -33,6 +33,7 @@
 	&hal_mod_uf,
 	&hal_mod_rport,
 	&hal_mod_fcp,
+	&hal_mod_dconf,
 	NULL
 };
 
@@ -237,8 +238,6 @@
 	u32	pi, ci;
 	struct list_head *waitq;
 
-	bfa_isr_rspq_ack(bfa, qid);
-
 	ci = bfa_rspq_ci(bfa, qid);
 	pi = bfa_rspq_pi(bfa, qid);
 
@@ -251,11 +250,9 @@
 	}
 
 	/*
-	 * update CI
+	 * acknowledge RME completions and update CI
 	 */
-	bfa_rspq_ci(bfa, qid) = pi;
-	writel(pi, bfa->iocfc.bfa_regs.rme_q_ci[qid]);
-	mmiowb();
+	bfa_isr_rspq_ack(bfa, qid, ci);
 
 	/*
 	 * Resume any pending requests in the corresponding reqq.
@@ -325,23 +322,19 @@
 	int queue;
 
 	intr = readl(bfa->iocfc.bfa_regs.intr_status);
-	if (!intr)
-		return BFA_FALSE;
 
 	qintr = intr & (__HFN_INT_RME_MASK | __HFN_INT_CPE_MASK);
 	if (qintr)
 		writel(qintr, bfa->iocfc.bfa_regs.intr_status);
 
 	/*
-	 * RME completion queue interrupt
+	 * Unconditional RME completion queue interrupt
 	 */
-	qintr = intr & __HFN_INT_RME_MASK;
-	if (qintr && bfa->queue_process) {
+	if (bfa->queue_process) {
 		for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
 			bfa_isr_rspq(bfa, queue);
 	}
 
-	intr &= ~qintr;
 	if (!intr)
 		return BFA_TRUE;
 
@@ -432,7 +425,8 @@
 				   __HFN_INT_MBOX_LPU1_CT2);
 		intr    &= __HFN_INT_ERR_MASK_CT2;
 	} else {
-		halt_isr = intr & __HFN_INT_LL_HALT;
+		halt_isr = bfa_asic_id_ct(bfa->ioc.pcidev.device_id) ?
+					  (intr & __HFN_INT_LL_HALT) : 0;
 		pss_isr  = intr & __HFN_INT_ERR_PSS;
 		lpu_isr  = intr & (__HFN_INT_MBOX_LPU0 | __HFN_INT_MBOX_LPU1);
 		intr    &= __HFN_INT_ERR_MASK;
@@ -578,7 +572,7 @@
 	} else {
 		iocfc->hwif.hw_reginit = bfa_hwcb_reginit;
 		iocfc->hwif.hw_reqq_ack = NULL;
-		iocfc->hwif.hw_rspq_ack = NULL;
+		iocfc->hwif.hw_rspq_ack = bfa_hwcb_rspq_ack;
 		iocfc->hwif.hw_msix_init = bfa_hwcb_msix_init;
 		iocfc->hwif.hw_msix_ctrl_install = bfa_hwcb_msix_ctrl_install;
 		iocfc->hwif.hw_msix_queue_install = bfa_hwcb_msix_queue_install;
@@ -595,7 +589,7 @@
 	if (bfa_asic_id_ct2(bfa_ioc_devid(&bfa->ioc))) {
 		iocfc->hwif.hw_reginit = bfa_hwct2_reginit;
 		iocfc->hwif.hw_isr_mode_set = NULL;
-		iocfc->hwif.hw_rspq_ack = NULL;
+		iocfc->hwif.hw_rspq_ack = bfa_hwct2_rspq_ack;
 	}
 
 	iocfc->hwif.hw_reginit(bfa);
@@ -685,7 +679,7 @@
 
 	bfa->queue_process = BFA_TRUE;
 	for (i = 0; i < BFI_IOC_MAX_CQS; i++)
-		bfa_isr_rspq_ack(bfa, i);
+		bfa_isr_rspq_ack(bfa, i, bfa_rspq_ci(bfa, i));
 
 	for (i = 0; hal_mods[i]; i++)
 		hal_mods[i]->start(bfa);
@@ -709,7 +703,7 @@
 	struct bfa_s	*bfa = bfa_arg;
 
 	if (complete) {
-		if (bfa->iocfc.cfgdone)
+		if (bfa->iocfc.cfgdone && BFA_DCONF_MOD(bfa)->flashdone)
 			bfa_cb_init(bfa->bfad, BFA_STATUS_OK);
 		else
 			bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED);
@@ -822,9 +816,11 @@
 	 */
 	bfa_fcport_init(bfa);
 
-	if (iocfc->action == BFA_IOCFC_ACT_INIT)
-		bfa_cb_queue(bfa, &iocfc->init_hcb_qe, bfa_iocfc_init_cb, bfa);
-	else {
+	if (iocfc->action == BFA_IOCFC_ACT_INIT) {
+		if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
+			bfa_cb_queue(bfa, &iocfc->init_hcb_qe,
+				bfa_iocfc_init_cb, bfa);
+	} else {
 		if (bfa->iocfc.action == BFA_IOCFC_ACT_ENABLE)
 			bfa_cb_queue(bfa, &bfa->iocfc.en_hcb_qe,
 					bfa_iocfc_enable_cb, bfa);
@@ -1045,6 +1041,7 @@
 	}
 
 	bfa_iocfc_send_cfg(bfa);
+	bfa_dconf_modinit(bfa);
 }
 
 /*
@@ -1207,7 +1204,9 @@
 	bfa->iocfc.action = BFA_IOCFC_ACT_STOP;
 
 	bfa->queue_process = BFA_FALSE;
-	bfa_ioc_disable(&bfa->ioc);
+	bfa_dconf_modexit(bfa);
+	if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
+		bfa_ioc_disable(&bfa->ioc);
 }
 
 void
@@ -1540,10 +1539,17 @@
 	struct list_head		*qe;
 	struct list_head		*qen;
 	struct bfa_cb_qe_s	*hcb_qe;
+	bfa_cb_cbfn_status_t	cbfn;
 
 	list_for_each_safe(qe, qen, comp_q) {
 		hcb_qe = (struct bfa_cb_qe_s *) qe;
-		hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE);
+		if (hcb_qe->pre_rmv) {
+			/* qe is invalid after return, dequeue before cbfn() */
+			list_del(qe);
+			cbfn = (bfa_cb_cbfn_status_t)(hcb_qe->cbfn);
+			cbfn(hcb_qe->cbarg, hcb_qe->fw_status);
+		} else
+			hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE);
 	}
 }
 
@@ -1556,10 +1562,20 @@
 	while (!list_empty(comp_q)) {
 		bfa_q_deq(comp_q, &qe);
 		hcb_qe = (struct bfa_cb_qe_s *) qe;
+		WARN_ON(hcb_qe->pre_rmv);
 		hcb_qe->cbfn(hcb_qe->cbarg, BFA_FALSE);
 	}
 }
 
+void
+bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status)
+{
+	if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) {
+		if (bfa->iocfc.cfgdone == BFA_TRUE)
+			bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
+				bfa_iocfc_init_cb, bfa);
+	}
+}
 
 /*
  * Return the list of PCI vendor/device id lists supported by this
diff --git a/drivers/scsi/bfa/bfa_defs.h b/drivers/scsi/bfa/bfa_defs.h
index ed8d31b..7b3d235 100644
--- a/drivers/scsi/bfa/bfa_defs.h
+++ b/drivers/scsi/bfa/bfa_defs.h
@@ -144,6 +144,7 @@
 	BFA_STATUS_INVLD_DFSZ	= 24,	/*  Invalid Max data field size */
 	BFA_STATUS_CMD_NOTSUPP  = 26,   /*  Command/API not supported */
 	BFA_STATUS_FABRIC_RJT	= 29,	/*  Reject from attached fabric */
+	BFA_STATUS_UNKNOWN_VWWN = 30,	/*  VPORT PWWN not found */
 	BFA_STATUS_PORT_OFFLINE = 34,	/*  Port is not online */
 	BFA_STATUS_VPORT_WWN_BP	= 46,	/*  WWN is same as base port's WWN */
 	BFA_STATUS_PORT_NOT_DISABLED = 47, /* Port not disabled disable port */
@@ -164,6 +165,8 @@
 	BFA_STATUS_INVALID_MAC  = 134, /*  Invalid MAC address */
 	BFA_STATUS_PBC		= 154, /*  Operation not allowed for pre-boot
 					*  configuration */
+	BFA_STATUS_BAD_FWCFG = 156,	/* Bad firmware configuration */
+	BFA_STATUS_INVALID_VENDOR = 158, /* Invalid switch vendor */
 	BFA_STATUS_SFP_NOT_READY = 159,	/* SFP info is not ready. Retry */
 	BFA_STATUS_TRUNK_ENABLED = 164, /* Trunk is already enabled on
 					 * this adapter */
@@ -172,11 +175,15 @@
 	BFA_STATUS_IOPROFILE_OFF = 175, /* IO profile OFF */
 	BFA_STATUS_PHY_NOT_PRESENT = 183, /* PHY module not present */
 	BFA_STATUS_FEATURE_NOT_SUPPORTED = 192,	/* Feature not supported */
+	BFA_STATUS_ENTRY_EXISTS = 193,	/* Entry already exists */
+	BFA_STATUS_ENTRY_NOT_EXISTS = 194, /* Entry does not exist */
+	BFA_STATUS_NO_CHANGE = 195,	/* Feature already in that state */
 	BFA_STATUS_FAA_ENABLED = 197,	/* FAA is already enabled */
 	BFA_STATUS_FAA_DISABLED = 198,	/* FAA is already disabled */
 	BFA_STATUS_FAA_ACQUIRED = 199,	/* FAA is already acquired */
 	BFA_STATUS_FAA_ACQ_ADDR = 200,	/* Acquiring addr */
 	BFA_STATUS_ERROR_TRUNK_ENABLED = 203,	/* Trunk enabled on adapter */
+	BFA_STATUS_MAX_ENTRY_REACHED = 212,	/* MAX entry reached */
 	BFA_STATUS_MAX_VAL		/* Unknown error code */
 };
 #define bfa_status_t enum bfa_status
@@ -359,6 +366,139 @@
 };
 
 /*
+ *			AEN related definitions
+ */
+enum bfa_aen_category {
+	BFA_AEN_CAT_ADAPTER	= 1,
+	BFA_AEN_CAT_PORT	= 2,
+	BFA_AEN_CAT_LPORT	= 3,
+	BFA_AEN_CAT_RPORT	= 4,
+	BFA_AEN_CAT_ITNIM	= 5,
+	BFA_AEN_CAT_AUDIT	= 8,
+	BFA_AEN_CAT_IOC		= 9,
+};
+
+/* BFA adapter level events */
+enum bfa_adapter_aen_event {
+	BFA_ADAPTER_AEN_ADD	= 1,	/* New Adapter found event */
+	BFA_ADAPTER_AEN_REMOVE	= 2,	/* Adapter removed event */
+};
+
+struct bfa_adapter_aen_data_s {
+	char	serial_num[BFA_ADAPTER_SERIAL_NUM_LEN];
+	u32	nports; /* Number of NPorts */
+	wwn_t	pwwn;   /* WWN of one of its physical port */
+};
+
+/* BFA physical port Level events */
+enum bfa_port_aen_event {
+	BFA_PORT_AEN_ONLINE	= 1,    /* Physical Port online event */
+	BFA_PORT_AEN_OFFLINE	= 2,    /* Physical Port offline event */
+	BFA_PORT_AEN_RLIR	= 3,    /* RLIR event, not supported */
+	BFA_PORT_AEN_SFP_INSERT	= 4,    /* SFP inserted event */
+	BFA_PORT_AEN_SFP_REMOVE	= 5,    /* SFP removed event */
+	BFA_PORT_AEN_SFP_POM	= 6,    /* SFP POM event */
+	BFA_PORT_AEN_ENABLE	= 7,    /* Physical Port enable event */
+	BFA_PORT_AEN_DISABLE	= 8,    /* Physical Port disable event */
+	BFA_PORT_AEN_AUTH_ON	= 9,    /* Physical Port auth success event */
+	BFA_PORT_AEN_AUTH_OFF	= 10,   /* Physical Port auth fail event */
+	BFA_PORT_AEN_DISCONNECT	= 11,   /* Physical Port disconnect event */
+	BFA_PORT_AEN_QOS_NEG	= 12,   /* Base Port QOS negotiation event */
+	BFA_PORT_AEN_FABRIC_NAME_CHANGE	= 13, /* Fabric Name/WWN change */
+	BFA_PORT_AEN_SFP_ACCESS_ERROR	= 14, /* SFP read error event */
+	BFA_PORT_AEN_SFP_UNSUPPORT	= 15, /* Unsupported SFP event */
+};
+
+enum bfa_port_aen_sfp_pom {
+	BFA_PORT_AEN_SFP_POM_GREEN = 1, /* Normal */
+	BFA_PORT_AEN_SFP_POM_AMBER = 2, /* Warning */
+	BFA_PORT_AEN_SFP_POM_RED   = 3, /* Critical */
+	BFA_PORT_AEN_SFP_POM_MAX   = BFA_PORT_AEN_SFP_POM_RED
+};
+
+struct bfa_port_aen_data_s {
+	wwn_t		pwwn;		/* WWN of the physical port */
+	wwn_t		fwwn;		/* WWN of the fabric port */
+	u32		phy_port_num;	/* For SFP related events */
+	u16		ioc_type;
+	u16		level;		/* Only transitions will be informed */
+	mac_t		mac;		/* MAC address of the ethernet port */
+	u16		rsvd;
+};
+
+/* BFA AEN logical port events */
+enum bfa_lport_aen_event {
+	BFA_LPORT_AEN_NEW	= 1,		/* LPort created event */
+	BFA_LPORT_AEN_DELETE	= 2,		/* LPort deleted event */
+	BFA_LPORT_AEN_ONLINE	= 3,		/* LPort online event */
+	BFA_LPORT_AEN_OFFLINE	= 4,		/* LPort offline event */
+	BFA_LPORT_AEN_DISCONNECT = 5,		/* LPort disconnect event */
+	BFA_LPORT_AEN_NEW_PROP	= 6,		/* VPort created event */
+	BFA_LPORT_AEN_DELETE_PROP = 7,		/* VPort deleted event */
+	BFA_LPORT_AEN_NEW_STANDARD = 8,		/* VPort created event */
+	BFA_LPORT_AEN_DELETE_STANDARD = 9,	/* VPort deleted event */
+	BFA_LPORT_AEN_NPIV_DUP_WWN = 10,	/* VPort with duplicate WWN */
+	BFA_LPORT_AEN_NPIV_FABRIC_MAX = 11,	/* Max NPIV in fabric/fport */
+	BFA_LPORT_AEN_NPIV_UNKNOWN = 12,	/* Unknown NPIV Error code */
+};
+
+struct bfa_lport_aen_data_s {
+	u16	vf_id;	/* vf_id of this logical port */
+	u16	roles;	/* Logical port mode,IM/TM/IP etc */
+	u32	rsvd;
+	wwn_t	ppwwn;	/* WWN of its physical port */
+	wwn_t	lpwwn;	/* WWN of this logical port */
+};
+
+/* BFA ITNIM events */
+enum bfa_itnim_aen_event {
+	BFA_ITNIM_AEN_ONLINE	 = 1,	/* Target online */
+	BFA_ITNIM_AEN_OFFLINE	 = 2,	/* Target offline */
+	BFA_ITNIM_AEN_DISCONNECT = 3,	/* Target disconnected */
+};
+
+struct bfa_itnim_aen_data_s {
+	u16		vf_id;		/* vf_id of the IT nexus */
+	u16		rsvd[3];
+	wwn_t		ppwwn;		/* WWN of its physical port */
+	wwn_t		lpwwn;		/* WWN of logical port */
+	wwn_t		rpwwn;		/* WWN of remote(target) port */
+};
+
+/* BFA audit events */
+enum bfa_audit_aen_event {
+	BFA_AUDIT_AEN_AUTH_ENABLE	= 1,
+	BFA_AUDIT_AEN_AUTH_DISABLE	= 2,
+	BFA_AUDIT_AEN_FLASH_ERASE	= 3,
+	BFA_AUDIT_AEN_FLASH_UPDATE	= 4,
+};
+
+struct bfa_audit_aen_data_s {
+	wwn_t	pwwn;
+	int	partition_inst;
+	int	partition_type;
+};
+
+/* BFA IOC level events */
+enum bfa_ioc_aen_event {
+	BFA_IOC_AEN_HBGOOD  = 1,	/* Heart Beat restore event	*/
+	BFA_IOC_AEN_HBFAIL  = 2,	/* Heart Beat failure event	*/
+	BFA_IOC_AEN_ENABLE  = 3,	/* IOC enabled event		*/
+	BFA_IOC_AEN_DISABLE = 4,	/* IOC disabled event		*/
+	BFA_IOC_AEN_FWMISMATCH  = 5,	/* IOC firmware mismatch	*/
+	BFA_IOC_AEN_FWCFG_ERROR = 6,	/* IOC firmware config error	*/
+	BFA_IOC_AEN_INVALID_VENDOR = 7,
+	BFA_IOC_AEN_INVALID_NWWN = 8,	/* Zero NWWN			*/
+	BFA_IOC_AEN_INVALID_PWWN = 9	/* Zero PWWN			*/
+};
+
+struct bfa_ioc_aen_data_s {
+	wwn_t	pwwn;
+	u16	ioc_type;
+	mac_t	mac;
+};
+
+/*
  * ---------------------- mfg definitions ------------
  */
 
@@ -520,6 +660,20 @@
 /*
  * BOOT boot configuraton
  */
+struct bfa_boot_cfg_s {
+	u8		version;
+	u8		rsvd1;
+	u16		chksum;
+	u8		enable;		/* enable/disable SAN boot */
+	u8		speed;          /* boot speed settings */
+	u8		topology;       /* boot topology setting */
+	u8		bootopt;        /* bfa_boot_bootopt_t */
+	u32		nbluns;         /* number of boot luns */
+	u32		rsvd2;
+	struct bfa_boot_bootlun_s blun[BFA_BOOT_BOOTLUN_MAX];
+	struct bfa_boot_bootlun_s blun_disc[BFA_BOOT_BOOTLUN_MAX];
+};
+
 struct bfa_boot_pbc_s {
 	u8              enable;         /*  enable/disable SAN boot */
 	u8              speed;          /*  boot speed settings */
@@ -529,6 +683,15 @@
 	struct bfa_boot_bootlun_s pblun[BFA_PREBOOT_BOOTLUN_MAX];
 };
 
+struct bfa_ethboot_cfg_s {
+	u8		version;
+	u8		rsvd1;
+	u16		chksum;
+	u8		enable;	/* enable/disable Eth/PXE boot */
+	u8		rsvd2;
+	u16		vlan;
+};
+
 /*
  * ASIC block configuration related structures
  */
@@ -587,6 +750,14 @@
  */
 #define SFP_DIAGMON_SIZE	10 /* num bytes of diag monitor data */
 
+/* SFP state change notification event */
+#define BFA_SFP_SCN_REMOVED	0
+#define BFA_SFP_SCN_INSERTED	1
+#define BFA_SFP_SCN_POM		2
+#define BFA_SFP_SCN_FAILED	3
+#define BFA_SFP_SCN_UNSUPPORT	4
+#define BFA_SFP_SCN_VALID	5
+
 enum bfa_defs_sfp_media_e {
 	BFA_SFP_MEDIA_UNKNOWN	= 0x00,
 	BFA_SFP_MEDIA_CU	= 0x01,
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index 0b97525..863c6ba 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -268,6 +268,7 @@
     u32    error_resets;       /*  error resets initiated by upsm      */
     u32    sync_lost;          /*  Sync loss count                     */
     u32    sig_lost;           /*  Signal loss count                   */
+	u32	asn8g_attempts;	/* SNSM HWSM at 8Gbps attempts */
 };
 
 struct bfa_fw_port_physm_stats_s {
@@ -468,6 +469,7 @@
  * QoS states
  */
 enum bfa_qos_state {
+	BFA_QOS_DISABLED = 0,		/* QoS is disabled */
 	BFA_QOS_ONLINE = 1,		/*  QoS is online */
 	BFA_QOS_OFFLINE = 2,		/*  QoS is offline */
 };
@@ -670,6 +672,12 @@
 	u32	tm_iocdowns;		/*  TM cleaned-up due to IOC down   */
 	u32	tm_cleanups;		/*  TM cleanup requests	*/
 	u32	tm_cleanup_comps;	/*  TM cleanup completions	*/
+	u32	lm_lun_across_sg;	/*  LM lun is across sg data buf */
+	u32	lm_lun_not_sup;		/*  LM lun not supported */
+	u32	lm_rpl_data_changed;	/*  LM report-lun data changed */
+	u32	lm_wire_residue_changed; /* LM report-lun rsp residue changed */
+	u32	lm_small_buf_addresidue; /* LM buf smaller than reported cnt */
+	u32	lm_lun_not_rdy;		/* LM lun not ready */
 };
 
 /* Modify char* port_stt[] in bfal_port.c if a new state was added */
@@ -785,8 +793,51 @@
 	CEE_ISCSI_PRI_PFC_OFF			= 42,
 	CEE_ISCSI_PRI_OVERLAP_FCOE_PRI		= 43
 };
+
+#define MAX_LUN_MASK_CFG 16
+
+/*
+ * Initially flash content may be fff. On making LUN mask enable and disable
+ * state chnage.  when report lun command is being processed it goes from
+ * BFA_LUN_MASK_ACTIVE to BFA_LUN_MASK_FETCH and comes back to
+ * BFA_LUN_MASK_ACTIVE.
+ */
+enum bfa_ioim_lun_mask_state_s {
+	BFA_IOIM_LUN_MASK_INACTIVE = 0,
+	BFA_IOIM_LUN_MASK_ACTIVE = 1,
+	BFA_IOIM_LUN_MASK_FETCHED = 2,
+};
+
+enum bfa_lunmask_state_s {
+	BFA_LUNMASK_DISABLED = 0x00,
+	BFA_LUNMASK_ENABLED = 0x01,
+	BFA_LUNMASK_MINCFG = 0x02,
+	BFA_LUNMASK_UNINITIALIZED = 0xff,
+};
+
 #pragma pack(1)
 /*
+ * LUN mask configuration
+ */
+struct bfa_lun_mask_s {
+	wwn_t		lp_wwn;
+	wwn_t		rp_wwn;
+	struct scsi_lun	lun;
+	u8		ua;
+	u8		rsvd[3];
+	u16		rp_tag;
+	u8		lp_tag;
+	u8		state;
+};
+
+#define MAX_LUN_MASK_CFG 16
+struct bfa_lunmask_cfg_s {
+	u32	status;
+	u32	rsvd;
+	struct bfa_lun_mask_s	lun_list[MAX_LUN_MASK_CFG];
+};
+
+/*
  *      Physical port configuration
  */
 struct bfa_port_cfg_s {
@@ -1228,4 +1279,52 @@
 
 #pragma pack()
 
+/*
+ *			AEN related definitions
+ */
+#define BFAD_NL_VENDOR_ID (((u64)0x01 << SCSI_NL_VID_TYPE_SHIFT) \
+			   | BFA_PCI_VENDOR_ID_BROCADE)
+
+/* BFA remote port events */
+enum bfa_rport_aen_event {
+	BFA_RPORT_AEN_ONLINE     = 1,   /* RPort online event */
+	BFA_RPORT_AEN_OFFLINE    = 2,   /* RPort offline event */
+	BFA_RPORT_AEN_DISCONNECT = 3,   /* RPort disconnect event */
+	BFA_RPORT_AEN_QOS_PRIO   = 4,   /* QOS priority change event */
+	BFA_RPORT_AEN_QOS_FLOWID = 5,   /* QOS flow Id change event */
+};
+
+struct bfa_rport_aen_data_s {
+	u16             vf_id;  /* vf_id of this logical port */
+	u16             rsvd[3];
+	wwn_t           ppwwn;  /* WWN of its physical port */
+	wwn_t           lpwwn;  /* WWN of this logical port */
+	wwn_t           rpwwn;  /* WWN of this remote port */
+	union {
+		struct bfa_rport_qos_attr_s qos;
+	} priv;
+};
+
+union bfa_aen_data_u {
+	struct bfa_adapter_aen_data_s	adapter;
+	struct bfa_port_aen_data_s	port;
+	struct bfa_lport_aen_data_s	lport;
+	struct bfa_rport_aen_data_s	rport;
+	struct bfa_itnim_aen_data_s	itnim;
+	struct bfa_audit_aen_data_s	audit;
+	struct bfa_ioc_aen_data_s	ioc;
+};
+
+#define BFA_AEN_MAX_ENTRY	512
+
+struct bfa_aen_entry_s {
+	struct list_head	qe;
+	enum bfa_aen_category   aen_category;
+	u32                     aen_type;
+	union bfa_aen_data_u    aen_data;
+	struct timeval          aen_tv;
+	u32                     seq_num;
+	u32                     bfad_num;
+};
+
 #endif /* __BFA_DEFS_SVC_H__ */
diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h
index 8d0b88f..50b6a1c 100644
--- a/drivers/scsi/bfa/bfa_fc.h
+++ b/drivers/scsi/bfa/bfa_fc.h
@@ -56,6 +56,161 @@
 
 #define SCSI_MAX_ALLOC_LEN      0xFF    /* maximum allocarion length */
 
+#define SCSI_SENSE_CUR_ERR	0x70
+#define SCSI_SENSE_DEF_ERR	0x71
+
+/*
+ * SCSI additional sense codes
+ */
+#define SCSI_ASC_LUN_NOT_READY		0x04
+#define SCSI_ASC_LUN_NOT_SUPPORTED	0x25
+#define SCSI_ASC_TOCC			0x3F
+
+/*
+ * SCSI additional sense code qualifiers
+ */
+#define SCSI_ASCQ_MAN_INTR_REQ		0x03	/* manual intervention req */
+#define SCSI_ASCQ_RL_DATA_CHANGED	0x0E	/* report luns data changed */
+
+/*
+ * Methods of reporting informational exceptions
+ */
+#define SCSI_MP_IEC_UNIT_ATTN		0x2	/* generate unit attention */
+
+struct scsi_report_luns_data_s {
+	u32		lun_list_length;	/* length of LUN list length */
+	u32		reserved;
+	struct scsi_lun	lun[1];			/* first LUN in lun list */
+};
+
+struct scsi_inquiry_vendor_s {
+	u8	vendor_id[8];
+};
+
+struct scsi_inquiry_prodid_s {
+	u8	product_id[16];
+};
+
+struct scsi_inquiry_prodrev_s {
+	u8	product_rev[4];
+};
+
+struct scsi_inquiry_data_s {
+#ifdef __BIG_ENDIAN
+	u8		peripheral_qual:3;	/* peripheral qualifier */
+	u8		device_type:5;		/* peripheral device type */
+	u8		rmb:1;			/* removable medium bit */
+	u8		device_type_mod:7;	/* device type modifier */
+	u8		version;
+	u8		aenc:1;		/* async evt notification capability */
+	u8		trm_iop:1;	/* terminate I/O process */
+	u8		norm_aca:1;	/* normal ACA supported */
+	u8		hi_support:1;	/* SCSI-3: supports REPORT LUNS */
+	u8		rsp_data_format:4;
+	u8		additional_len;
+	u8		sccs:1;
+	u8		reserved1:7;
+	u8		reserved2:1;
+	u8		enc_serv:1;	/* enclosure service component */
+	u8		reserved3:1;
+	u8		multi_port:1;	/* multi-port device */
+	u8		m_chngr:1;	/* device in medium transport element */
+	u8		ack_req_q:1;	/* SIP specific bit */
+	u8		addr32:1;	/* SIP specific bit */
+	u8		addr16:1;	/* SIP specific bit */
+	u8		rel_adr:1;	/* relative address */
+	u8		w_bus32:1;
+	u8		w_bus16:1;
+	u8		synchronous:1;
+	u8		linked_commands:1;
+	u8		trans_dis:1;
+	u8		cmd_queue:1;	/* command queueing supported */
+	u8		soft_reset:1;	/* soft reset alternative (VS) */
+#else
+	u8		device_type:5;	/* peripheral device type */
+	u8		peripheral_qual:3; /* peripheral qualifier */
+	u8		device_type_mod:7; /* device type modifier */
+	u8		rmb:1;		/* removable medium bit */
+	u8		version;
+	u8		rsp_data_format:4;
+	u8		hi_support:1;	/* SCSI-3: supports REPORT LUNS */
+	u8		norm_aca:1;	/* normal ACA supported */
+	u8		terminate_iop:1;/* terminate I/O process */
+	u8		aenc:1;		/* async evt notification capability */
+	u8		additional_len;
+	u8		reserved1:7;
+	u8		sccs:1;
+	u8		addr16:1;	/* SIP specific bit */
+	u8		addr32:1;	/* SIP specific bit */
+	u8		ack_req_q:1;	/* SIP specific bit */
+	u8		m_chngr:1;	/* device in medium transport element */
+	u8		multi_port:1;	/* multi-port device */
+	u8		reserved3:1;	/* TBD - Vendor Specific */
+	u8		enc_serv:1;	/* enclosure service component */
+	u8		reserved2:1;
+	u8		soft_seset:1;	/* soft reset alternative (VS) */
+	u8		cmd_queue:1;	/* command queueing supported */
+	u8		trans_dis:1;
+	u8		linked_commands:1;
+	u8		synchronous:1;
+	u8		w_bus16:1;
+	u8		w_bus32:1;
+	u8		rel_adr:1;	/* relative address */
+#endif
+	struct scsi_inquiry_vendor_s	vendor_id;
+	struct scsi_inquiry_prodid_s	product_id;
+	struct scsi_inquiry_prodrev_s	product_rev;
+	u8		vendor_specific[20];
+	u8		reserved4[40];
+};
+
+/*
+ *	SCSI sense data format
+ */
+struct scsi_sense_s {
+#ifdef __BIG_ENDIAN
+	u8		valid:1;
+	u8		rsp_code:7;
+#else
+	u8		rsp_code:7;
+	u8		valid:1;
+#endif
+	u8		seg_num;
+#ifdef __BIG_ENDIAN
+	u8		file_mark:1;
+	u8		eom:1;		/* end of media */
+	u8		ili:1;		/* incorrect length indicator */
+	u8		reserved:1;
+	u8		sense_key:4;
+#else
+	u8		sense_key:4;
+	u8		reserved:1;
+	u8		ili:1;		/* incorrect length indicator */
+	u8		eom:1;		/* end of media */
+	u8		file_mark:1;
+#endif
+	u8		information[4];	/* device-type or cmd specific info */
+	u8		add_sense_length; /* additional sense length */
+	u8		command_info[4];/* command specific information */
+	u8		asc;		/* additional sense code */
+	u8		ascq;		/* additional sense code qualifier */
+	u8		fru_code;	/* field replaceable unit code */
+#ifdef __BIG_ENDIAN
+	u8		sksv:1;		/* sense key specific valid */
+	u8		c_d:1;		/* command/data bit */
+	u8		res1:2;
+	u8		bpv:1;		/* bit pointer valid */
+	u8		bpointer:3;	/* bit pointer */
+#else
+	u8		bpointer:3;	/* bit pointer */
+	u8		bpv:1;		/* bit pointer valid */
+	u8		res1:2;
+	u8		c_d:1;		/* command/data bit */
+	u8		sksv:1;		/* sense key specific valid */
+#endif
+	u8		fpointer[2];	/* field pointer */
+};
+
 /*
  * Fibre Channel Header Structure (FCHS) definition
  */
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index a4e7951..e07bd47 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -24,6 +24,9 @@
  *  BFA ITNIM Related definitions
  */
 static void bfa_itnim_update_del_itn_stats(struct bfa_itnim_s *itnim);
+static bfa_boolean_t bfa_ioim_lm_proc_rpl_data(struct bfa_ioim_s *ioim);
+static bfa_boolean_t bfa_ioim_lm_proc_inq_data(struct bfa_ioim_s *ioim);
+static void bfa_ioim_lm_init(struct bfa_s *bfa);
 
 #define BFA_ITNIM_FROM_TAG(_fcpim, _tag)                                \
 	(((_fcpim)->itnim_arr + ((_tag) & ((_fcpim)->num_itnims - 1))))
@@ -57,6 +60,14 @@
 	}								\
 } while (0)
 
+#define bfa_ioim_rp_wwn(__ioim)						\
+	(((struct bfa_fcs_rport_s *)					\
+	 (__ioim)->itnim->rport->rport_drv)->pwwn)
+
+#define bfa_ioim_lp_wwn(__ioim)						\
+	((BFA_LPS_FROM_TAG(BFA_LPS_MOD((__ioim)->bfa),			\
+	(__ioim)->itnim->rport->rport_info.lp_tag))->pwwn)		\
+
 #define bfa_itnim_sler_cb(__itnim) do {					\
 	if ((__itnim)->bfa->fcs)					\
 		bfa_cb_itnim_sler((__itnim)->ditn);      \
@@ -66,6 +77,18 @@
 	}								\
 } while (0)
 
+enum bfa_ioim_lm_status {
+	BFA_IOIM_LM_PRESENT = 1,
+	BFA_IOIM_LM_LUN_NOT_SUP = 2,
+	BFA_IOIM_LM_RPL_DATA_CHANGED = 3,
+	BFA_IOIM_LM_LUN_NOT_RDY = 4,
+};
+
+enum bfa_ioim_lm_ua_status {
+	BFA_IOIM_LM_UA_RESET = 0,
+	BFA_IOIM_LM_UA_SET = 1,
+};
+
 /*
  *  itnim state machine event
  */
@@ -122,6 +145,9 @@
 	BFA_IOIM_SM_TMDONE	= 16,	/*  IO cleanup from tskim */
 	BFA_IOIM_SM_HWFAIL	= 17,	/*  IOC h/w failure event */
 	BFA_IOIM_SM_IOTOV	= 18,	/*  ITN offline TOV */
+	BFA_IOIM_SM_LM_LUN_NOT_SUP = 19,/*  lunmask lun not supported */
+	BFA_IOIM_SM_LM_RPL_DC = 20,	/*  lunmask report-lun data changed */
+	BFA_IOIM_SM_LM_LUN_NOT_RDY = 21,/*  lunmask lun not ready */
 };
 
 
@@ -219,6 +245,9 @@
 static void __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete);
 static void __bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete);
 static bfa_boolean_t    bfa_ioim_is_abortable(struct bfa_ioim_s *ioim);
+static void __bfa_cb_ioim_lm_lun_not_sup(void *cbarg, bfa_boolean_t complete);
+static void __bfa_cb_ioim_lm_rpl_dc(void *cbarg, bfa_boolean_t complete);
+static void __bfa_cb_ioim_lm_lun_not_rdy(void *cbarg, bfa_boolean_t complete);
 
 /*
  * forward declaration of BFA IO state machine
@@ -416,6 +445,12 @@
 	bfa_fcpim_add_iostats(lstats, rstats, output_reqs);
 	bfa_fcpim_add_iostats(lstats, rstats, rd_throughput);
 	bfa_fcpim_add_iostats(lstats, rstats, wr_throughput);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_lun_across_sg);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_lun_not_sup);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_rpl_data_changed);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_wire_residue_changed);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_small_buf_addresidue);
+	bfa_fcpim_add_iostats(lstats, rstats, lm_lun_not_rdy);
 }
 
 bfa_status_t
@@ -437,6 +472,59 @@
 	return BFA_STATUS_OK;
 }
 
+void
+bfa_ioim_profile_comp(struct bfa_ioim_s *ioim)
+{
+	struct bfa_itnim_latency_s *io_lat =
+			&(ioim->itnim->ioprofile.io_latency);
+	u32 val, idx;
+
+	val = (u32)(jiffies - ioim->start_time);
+	idx = bfa_ioim_get_index(scsi_bufflen((struct scsi_cmnd *)ioim->dio));
+	bfa_itnim_ioprofile_update(ioim->itnim, idx);
+
+	io_lat->count[idx]++;
+	io_lat->min[idx] = (io_lat->min[idx] < val) ? io_lat->min[idx] : val;
+	io_lat->max[idx] = (io_lat->max[idx] > val) ? io_lat->max[idx] : val;
+	io_lat->avg[idx] += val;
+}
+
+void
+bfa_ioim_profile_start(struct bfa_ioim_s *ioim)
+{
+	ioim->start_time = jiffies;
+}
+
+bfa_status_t
+bfa_fcpim_profile_on(struct bfa_s *bfa, u32 time)
+{
+	struct bfa_itnim_s *itnim;
+	struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+	struct list_head *qe, *qen;
+
+	/* accumulate IO stats from itnim */
+	list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+		itnim = (struct bfa_itnim_s *) qe;
+		bfa_itnim_clear_stats(itnim);
+	}
+	fcpim->io_profile = BFA_TRUE;
+	fcpim->io_profile_start_time = time;
+	fcpim->profile_comp = bfa_ioim_profile_comp;
+	fcpim->profile_start = bfa_ioim_profile_start;
+	return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_fcpim_profile_off(struct bfa_s *bfa)
+{
+	struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+	fcpim->io_profile = BFA_FALSE;
+	fcpim->io_profile_start_time = 0;
+	fcpim->profile_comp = NULL;
+	fcpim->profile_start = NULL;
+	return BFA_STATUS_OK;
+}
+
 u16
 bfa_fcpim_qdepth_get(struct bfa_s *bfa)
 {
@@ -1401,6 +1489,26 @@
 		 bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable));
 }
 
+#define bfa_io_lat_clock_res_div	HZ
+#define bfa_io_lat_clock_res_mul	1000
+bfa_status_t
+bfa_itnim_get_ioprofile(struct bfa_itnim_s *itnim,
+			struct bfa_itnim_ioprofile_s *ioprofile)
+{
+	struct bfa_fcpim_s *fcpim = BFA_FCPIM(itnim->bfa);
+	if (!fcpim->io_profile)
+		return BFA_STATUS_IOPROFILE_OFF;
+
+	itnim->ioprofile.index = BFA_IOBUCKET_MAX;
+	itnim->ioprofile.io_profile_start_time =
+				bfa_io_profile_start_time(itnim->bfa);
+	itnim->ioprofile.clock_res_mul = bfa_io_lat_clock_res_mul;
+	itnim->ioprofile.clock_res_div = bfa_io_lat_clock_res_div;
+	*ioprofile = itnim->ioprofile;
+
+	return BFA_STATUS_OK;
+}
+
 void
 bfa_itnim_clear_stats(struct bfa_itnim_s *itnim)
 {
@@ -1469,7 +1577,28 @@
 		bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
 		WARN_ON(!bfa_q_is_on_q(&ioim->itnim->pending_q, ioim));
 		bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
-				__bfa_cb_ioim_abort, ioim);
+			__bfa_cb_ioim_abort, ioim);
+		break;
+
+	case BFA_IOIM_SM_LM_LUN_NOT_SUP:
+		bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
+		bfa_ioim_move_to_comp_q(ioim);
+		bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
+			__bfa_cb_ioim_lm_lun_not_sup, ioim);
+		break;
+
+	case BFA_IOIM_SM_LM_RPL_DC:
+		bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
+		bfa_ioim_move_to_comp_q(ioim);
+		bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
+				__bfa_cb_ioim_lm_rpl_dc, ioim);
+		break;
+
+	case BFA_IOIM_SM_LM_LUN_NOT_RDY:
+		bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
+		bfa_ioim_move_to_comp_q(ioim);
+		bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
+			__bfa_cb_ioim_lm_lun_not_rdy, ioim);
 		break;
 
 	default:
@@ -2009,6 +2138,264 @@
 	}
 }
 
+/*
+ * This is called from bfa_fcpim_start after the bfa_init() with flash read
+ * is complete by driver. now invalidate the stale content of lun mask
+ * like unit attention, rp tag and lp tag.
+ */
+static void
+bfa_ioim_lm_init(struct bfa_s *bfa)
+{
+	struct bfa_lun_mask_s *lunm_list;
+	int	i;
+
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return;
+
+	lunm_list = bfa_get_lun_mask_list(bfa);
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		lunm_list[i].ua = BFA_IOIM_LM_UA_RESET;
+		lunm_list[i].lp_tag = BFA_LP_TAG_INVALID;
+		lunm_list[i].rp_tag = BFA_RPORT_TAG_INVALID;
+	}
+}
+
+/*
+ * Validate LUN for LUN masking
+ */
+static enum bfa_ioim_lm_status
+bfa_ioim_lm_check(struct bfa_ioim_s *ioim, struct bfa_lps_s *lps,
+		struct bfa_rport_s *rp, struct scsi_lun lun)
+{
+	u8 i;
+	struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa);
+	struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio;
+	struct scsi_cdb_s *cdb = (struct scsi_cdb_s *)cmnd->cmnd;
+
+	if ((cdb->scsi_cdb[0] == REPORT_LUNS) &&
+	    (scsilun_to_int((struct scsi_lun *)&lun) == 0)) {
+		ioim->proc_rsp_data = bfa_ioim_lm_proc_rpl_data;
+		return BFA_IOIM_LM_PRESENT;
+	}
+
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+
+		if (lun_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+			continue;
+
+		if ((scsilun_to_int((struct scsi_lun *)&lun_list[i].lun) ==
+		    scsilun_to_int((struct scsi_lun *)&lun))
+		    && (rp->rport_tag == lun_list[i].rp_tag)
+		    && ((u8)ioim->itnim->rport->rport_info.lp_tag ==
+						lun_list[i].lp_tag)) {
+			bfa_trc(ioim->bfa, lun_list[i].rp_tag);
+			bfa_trc(ioim->bfa, lun_list[i].lp_tag);
+			bfa_trc(ioim->bfa, scsilun_to_int(
+				(struct scsi_lun *)&lun_list[i].lun));
+
+			if ((lun_list[i].ua == BFA_IOIM_LM_UA_SET) &&
+			    ((cdb->scsi_cdb[0] != INQUIRY) ||
+			    (cdb->scsi_cdb[0] != REPORT_LUNS))) {
+				lun_list[i].ua = BFA_IOIM_LM_UA_RESET;
+				return BFA_IOIM_LM_RPL_DATA_CHANGED;
+			}
+
+			if (cdb->scsi_cdb[0] == REPORT_LUNS)
+				ioim->proc_rsp_data = bfa_ioim_lm_proc_rpl_data;
+
+			return BFA_IOIM_LM_PRESENT;
+		}
+	}
+
+	if ((cdb->scsi_cdb[0] == INQUIRY) &&
+	    (scsilun_to_int((struct scsi_lun *)&lun) == 0)) {
+		ioim->proc_rsp_data = bfa_ioim_lm_proc_inq_data;
+		return BFA_IOIM_LM_PRESENT;
+	}
+
+	if (cdb->scsi_cdb[0] == TEST_UNIT_READY)
+		return BFA_IOIM_LM_LUN_NOT_RDY;
+
+	return BFA_IOIM_LM_LUN_NOT_SUP;
+}
+
+static bfa_boolean_t
+bfa_ioim_lm_proc_rsp_data_dummy(struct bfa_ioim_s *ioim)
+{
+	return BFA_TRUE;
+}
+
+static void
+bfa_ioim_lm_fetch_lun(struct bfa_ioim_s *ioim, u8 *rl_data, int offset,
+		int buf_lun_cnt)
+{
+	struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa);
+	struct scsi_lun *lun_data = (struct scsi_lun *)(rl_data + offset);
+	struct scsi_lun lun;
+	int i, j;
+
+	bfa_trc(ioim->bfa, buf_lun_cnt);
+	for (j = 0; j < buf_lun_cnt; j++) {
+		lun = *((struct scsi_lun *)(lun_data + j));
+		for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+			if (lun_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+				continue;
+			if ((lun_list[i].rp_wwn == bfa_ioim_rp_wwn(ioim)) &&
+			    (lun_list[i].lp_wwn == bfa_ioim_lp_wwn(ioim)) &&
+			    (scsilun_to_int((struct scsi_lun *)&lun_list[i].lun)
+				== scsilun_to_int((struct scsi_lun *)&lun))) {
+				lun_list[i].state = BFA_IOIM_LUN_MASK_FETCHED;
+				break;
+			}
+		} /* next lun in mask DB */
+	} /* next lun in buf */
+}
+
+static int
+bfa_ioim_lm_update_lun_sg(struct bfa_ioim_s *ioim, u32 *pgdlen,
+		struct scsi_report_luns_data_s *rl)
+{
+	struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio;
+	struct scatterlist *sg = scsi_sglist(cmnd);
+	struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa);
+	struct scsi_lun *prev_rl_data = NULL, *base_rl_data;
+	int i, j, sgeid, lun_fetched_cnt = 0, prev_sg_len = 0, base_count;
+	int lun_across_sg_bytes, bytes_from_next_buf;
+	u64	last_lun, temp_last_lun;
+
+	/* fetch luns from the first sg element */
+	bfa_ioim_lm_fetch_lun(ioim, (u8 *)(rl->lun), 0,
+			(sg_dma_len(sg) / sizeof(struct scsi_lun)) - 1);
+
+	/* fetch luns from multiple sg elements */
+	scsi_for_each_sg(cmnd, sg, scsi_sg_count(cmnd), sgeid) {
+		if (sgeid == 0) {
+			prev_sg_len = sg_dma_len(sg);
+			prev_rl_data = (struct scsi_lun *)
+					phys_to_virt(sg_dma_address(sg));
+			continue;
+		}
+
+		/* if the buf is having more data */
+		lun_across_sg_bytes = prev_sg_len % sizeof(struct scsi_lun);
+		if (lun_across_sg_bytes) {
+			bfa_trc(ioim->bfa, lun_across_sg_bytes);
+			bfa_stats(ioim->itnim, lm_lun_across_sg);
+			bytes_from_next_buf = sizeof(struct scsi_lun) -
+					      lun_across_sg_bytes;
+
+			/* from next buf take higher bytes */
+			temp_last_lun = *((u64 *)
+					  phys_to_virt(sg_dma_address(sg)));
+			last_lun |= temp_last_lun >>
+				    (lun_across_sg_bytes * BITS_PER_BYTE);
+
+			/* from prev buf take higher bytes */
+			temp_last_lun = *((u64 *)(prev_rl_data +
+					  (prev_sg_len - lun_across_sg_bytes)));
+			temp_last_lun >>= bytes_from_next_buf * BITS_PER_BYTE;
+			last_lun = last_lun | (temp_last_lun <<
+				   (bytes_from_next_buf * BITS_PER_BYTE));
+
+			bfa_ioim_lm_fetch_lun(ioim, (u8 *)&last_lun, 0, 1);
+		} else
+			bytes_from_next_buf = 0;
+
+		*pgdlen += sg_dma_len(sg);
+		prev_sg_len = sg_dma_len(sg);
+		prev_rl_data = (struct scsi_lun *)
+				phys_to_virt(sg_dma_address(sg));
+		bfa_ioim_lm_fetch_lun(ioim, (u8 *)prev_rl_data,
+				bytes_from_next_buf,
+				sg_dma_len(sg) / sizeof(struct scsi_lun));
+	}
+
+	/* update the report luns data - based on fetched luns */
+	sg = scsi_sglist(cmnd);
+	base_rl_data = (struct scsi_lun *)rl->lun;
+	base_count = (sg_dma_len(sg) / sizeof(struct scsi_lun)) - 1;
+	for (i = 0, j = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if (lun_list[i].state == BFA_IOIM_LUN_MASK_FETCHED) {
+			base_rl_data[j] = lun_list[i].lun;
+			lun_list[i].state = BFA_IOIM_LUN_MASK_ACTIVE;
+			j++;
+			lun_fetched_cnt++;
+		}
+
+		if (j > base_count) {
+			j = 0;
+			sg = sg_next(sg);
+			base_rl_data = (struct scsi_lun *)
+					phys_to_virt(sg_dma_address(sg));
+			base_count = sg_dma_len(sg) / sizeof(struct scsi_lun);
+		}
+	}
+
+	bfa_trc(ioim->bfa, lun_fetched_cnt);
+	return lun_fetched_cnt;
+}
+
+static bfa_boolean_t
+bfa_ioim_lm_proc_inq_data(struct bfa_ioim_s *ioim)
+{
+	struct scsi_inquiry_data_s *inq;
+	struct scatterlist *sg = scsi_sglist((struct scsi_cmnd *)ioim->dio);
+
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
+	inq = (struct scsi_inquiry_data_s *)phys_to_virt(sg_dma_address(sg));
+
+	bfa_trc(ioim->bfa, inq->device_type);
+	inq->peripheral_qual = SCSI_INQ_PQ_NOT_CON;
+	return 0;
+}
+
+static bfa_boolean_t
+bfa_ioim_lm_proc_rpl_data(struct bfa_ioim_s *ioim)
+{
+	struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio;
+	struct scatterlist *sg = scsi_sglist(cmnd);
+	struct bfi_ioim_rsp_s *m;
+	struct scsi_report_luns_data_s *rl = NULL;
+	int lun_count = 0, lun_fetched_cnt = 0;
+	u32 residue, pgdlen = 0;
+
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
+	if (bfa_get_lun_mask_status(ioim->bfa) != BFA_LUNMASK_ENABLED)
+		return BFA_TRUE;
+
+	m = (struct bfi_ioim_rsp_s *) &ioim->iosp->comp_rspmsg;
+	if (m->scsi_status == SCSI_STATUS_CHECK_CONDITION)
+		return BFA_TRUE;
+
+	pgdlen = sg_dma_len(sg);
+	bfa_trc(ioim->bfa, pgdlen);
+	rl = (struct scsi_report_luns_data_s *)phys_to_virt(sg_dma_address(sg));
+	lun_count = cpu_to_be32(rl->lun_list_length) / sizeof(struct scsi_lun);
+	lun_fetched_cnt = bfa_ioim_lm_update_lun_sg(ioim, &pgdlen, rl);
+
+	if (lun_count == lun_fetched_cnt)
+		return BFA_TRUE;
+
+	bfa_trc(ioim->bfa, lun_count);
+	bfa_trc(ioim->bfa, lun_fetched_cnt);
+	bfa_trc(ioim->bfa, be32_to_cpu(rl->lun_list_length));
+
+	if (be32_to_cpu(rl->lun_list_length) <= pgdlen)
+		rl->lun_list_length = be32_to_cpu(lun_fetched_cnt) *
+				      sizeof(struct scsi_lun);
+	else
+		bfa_stats(ioim->itnim, lm_small_buf_addresidue);
+
+	bfa_trc(ioim->bfa, be32_to_cpu(rl->lun_list_length));
+	bfa_trc(ioim->bfa, be32_to_cpu(m->residue));
+
+	residue = be32_to_cpu(m->residue);
+	residue += (lun_count - lun_fetched_cnt) * sizeof(struct scsi_lun);
+	bfa_stats(ioim->itnim, lm_wire_residue_changed);
+	m->residue = be32_to_cpu(residue);
+	bfa_trc(ioim->bfa, ioim->nsges);
+	return BFA_FALSE;
+}
 
 static void
 __bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete)
@@ -2068,6 +2455,299 @@
 }
 
 static void
+__bfa_cb_ioim_lm_lun_not_sup(void *cbarg, bfa_boolean_t complete)
+{
+	struct bfa_ioim_s *ioim = cbarg;
+	int sns_len = 0xD;
+	u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio);
+	struct scsi_sense_s *snsinfo;
+
+	if (!complete) {
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB);
+		return;
+	}
+
+	snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG(
+					ioim->fcpim->fcp, ioim->iotag);
+	snsinfo->rsp_code = SCSI_SENSE_CUR_ERR;
+	snsinfo->add_sense_length = 0xa;
+	snsinfo->asc = SCSI_ASC_LUN_NOT_SUPPORTED;
+	snsinfo->sense_key = ILLEGAL_REQUEST;
+	bfa_trc(ioim->bfa, residue);
+	bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK,
+			SCSI_STATUS_CHECK_CONDITION, sns_len,
+			(u8 *)snsinfo, residue);
+}
+
+static void
+__bfa_cb_ioim_lm_rpl_dc(void *cbarg, bfa_boolean_t complete)
+{
+	struct bfa_ioim_s *ioim = cbarg;
+	int sns_len = 0xD;
+	u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio);
+	struct scsi_sense_s *snsinfo;
+
+	if (!complete) {
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB);
+		return;
+	}
+
+	snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG(ioim->fcpim->fcp,
+						       ioim->iotag);
+	snsinfo->rsp_code = SCSI_SENSE_CUR_ERR;
+	snsinfo->sense_key = SCSI_MP_IEC_UNIT_ATTN;
+	snsinfo->asc = SCSI_ASC_TOCC;
+	snsinfo->add_sense_length = 0x6;
+	snsinfo->ascq = SCSI_ASCQ_RL_DATA_CHANGED;
+	bfa_trc(ioim->bfa, residue);
+	bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK,
+			SCSI_STATUS_CHECK_CONDITION, sns_len,
+			(u8 *)snsinfo, residue);
+}
+
+static void
+__bfa_cb_ioim_lm_lun_not_rdy(void *cbarg, bfa_boolean_t complete)
+{
+	struct bfa_ioim_s *ioim = cbarg;
+	int sns_len = 0xD;
+	u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio);
+	struct scsi_sense_s *snsinfo;
+
+	if (!complete) {
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB);
+		return;
+	}
+
+	snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG(
+					ioim->fcpim->fcp, ioim->iotag);
+	snsinfo->rsp_code = SCSI_SENSE_CUR_ERR;
+	snsinfo->add_sense_length = 0xa;
+	snsinfo->sense_key = NOT_READY;
+	snsinfo->asc = SCSI_ASC_LUN_NOT_READY;
+	snsinfo->ascq = SCSI_ASCQ_MAN_INTR_REQ;
+	bfa_trc(ioim->bfa, residue);
+	bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK,
+			SCSI_STATUS_CHECK_CONDITION, sns_len,
+			(u8 *)snsinfo, residue);
+}
+
+void
+bfa_fcpim_lunmask_rp_update(struct bfa_s *bfa, wwn_t lp_wwn, wwn_t rp_wwn,
+			u16 rp_tag, u8 lp_tag)
+{
+	struct bfa_lun_mask_s *lun_list;
+	u8	i;
+
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return;
+
+	lun_list = bfa_get_lun_mask_list(bfa);
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if (lun_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE) {
+			if ((lun_list[i].lp_wwn == lp_wwn) &&
+			    (lun_list[i].rp_wwn == rp_wwn)) {
+				lun_list[i].rp_tag = rp_tag;
+				lun_list[i].lp_tag = lp_tag;
+			}
+		}
+	}
+}
+
+/*
+ * set UA for all active luns in LM DB
+ */
+static void
+bfa_ioim_lm_set_ua(struct bfa_s *bfa)
+{
+	struct bfa_lun_mask_s	*lunm_list;
+	int	i;
+
+	lunm_list = bfa_get_lun_mask_list(bfa);
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if (lunm_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+			continue;
+		lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+	}
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_update(struct bfa_s *bfa, u32 update)
+{
+	struct bfa_lunmask_cfg_s	*lun_mask;
+
+	bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return BFA_STATUS_FAILED;
+
+	if (bfa_get_lun_mask_status(bfa) == update)
+		return BFA_STATUS_NO_CHANGE;
+
+	lun_mask = bfa_get_lun_mask(bfa);
+	lun_mask->status = update;
+
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_ENABLED)
+		bfa_ioim_lm_set_ua(bfa);
+
+	return  bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_clear(struct bfa_s *bfa)
+{
+	int i;
+	struct bfa_lun_mask_s	*lunm_list;
+
+	bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return BFA_STATUS_FAILED;
+
+	lunm_list = bfa_get_lun_mask_list(bfa);
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if (lunm_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE) {
+			if (lunm_list[i].rp_tag != BFA_RPORT_TAG_INVALID)
+				bfa_rport_unset_lunmask(bfa,
+				  BFA_RPORT_FROM_TAG(bfa, lunm_list[i].rp_tag));
+		}
+	}
+
+	memset(lunm_list, 0, sizeof(struct bfa_lun_mask_s) * MAX_LUN_MASK_CFG);
+	return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_query(struct bfa_s *bfa, void *buf)
+{
+	struct bfa_lunmask_cfg_s *lun_mask;
+
+	bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return BFA_STATUS_FAILED;
+
+	lun_mask = bfa_get_lun_mask(bfa);
+	memcpy(buf, lun_mask, sizeof(struct bfa_lunmask_cfg_s));
+	return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn,
+		      wwn_t rpwwn, struct scsi_lun lun)
+{
+	struct bfa_lun_mask_s *lunm_list;
+	struct bfa_rport_s *rp = NULL;
+	int i, free_index = MAX_LUN_MASK_CFG + 1;
+	struct bfa_fcs_lport_s *port = NULL;
+	struct bfa_fcs_rport_s *rp_fcs;
+
+	bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return BFA_STATUS_FAILED;
+
+	port = bfa_fcs_lookup_port(&((struct bfad_s *)bfa->bfad)->bfa_fcs,
+				   vf_id, *pwwn);
+	if (port) {
+		*pwwn = port->port_cfg.pwwn;
+		rp_fcs = bfa_fcs_lport_get_rport_by_pwwn(port, rpwwn);
+		rp = rp_fcs->bfa_rport;
+	}
+
+	lunm_list = bfa_get_lun_mask_list(bfa);
+	/* if entry exists */
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if (lunm_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+			free_index = i;
+		if ((lunm_list[i].lp_wwn == *pwwn) &&
+		    (lunm_list[i].rp_wwn == rpwwn) &&
+		    (scsilun_to_int((struct scsi_lun *)&lunm_list[i].lun) ==
+		     scsilun_to_int((struct scsi_lun *)&lun)))
+			return  BFA_STATUS_ENTRY_EXISTS;
+	}
+
+	if (free_index > MAX_LUN_MASK_CFG)
+		return BFA_STATUS_MAX_ENTRY_REACHED;
+
+	if (rp) {
+		lunm_list[free_index].lp_tag = bfa_lps_get_tag_from_pid(bfa,
+						   rp->rport_info.local_pid);
+		lunm_list[free_index].rp_tag = rp->rport_tag;
+	} else {
+		lunm_list[free_index].lp_tag = BFA_LP_TAG_INVALID;
+		lunm_list[free_index].rp_tag = BFA_RPORT_TAG_INVALID;
+	}
+
+	lunm_list[free_index].lp_wwn = *pwwn;
+	lunm_list[free_index].rp_wwn = rpwwn;
+	lunm_list[free_index].lun = lun;
+	lunm_list[free_index].state = BFA_IOIM_LUN_MASK_ACTIVE;
+
+	/* set for all luns in this rp */
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if ((lunm_list[i].lp_wwn == *pwwn) &&
+		    (lunm_list[i].rp_wwn == rpwwn))
+			lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+	}
+
+	return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn,
+			 wwn_t rpwwn, struct scsi_lun lun)
+{
+	struct bfa_lun_mask_s	*lunm_list;
+	struct bfa_rport_s	*rp = NULL;
+	struct bfa_fcs_lport_s *port = NULL;
+	struct bfa_fcs_rport_s *rp_fcs;
+	int	i;
+
+	/* in min cfg lunm_list could be NULL but  no commands should run. */
+	if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+		return BFA_STATUS_FAILED;
+
+	bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+	bfa_trc(bfa, *pwwn);
+	bfa_trc(bfa, rpwwn);
+	bfa_trc(bfa, scsilun_to_int((struct scsi_lun *)&lun));
+
+	if (*pwwn == 0) {
+		port = bfa_fcs_lookup_port(
+				&((struct bfad_s *)bfa->bfad)->bfa_fcs,
+				vf_id, *pwwn);
+		if (port) {
+			*pwwn = port->port_cfg.pwwn;
+			rp_fcs = bfa_fcs_lport_get_rport_by_pwwn(port, rpwwn);
+			rp = rp_fcs->bfa_rport;
+		}
+	}
+
+	lunm_list = bfa_get_lun_mask_list(bfa);
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if ((lunm_list[i].lp_wwn == *pwwn) &&
+		    (lunm_list[i].rp_wwn == rpwwn) &&
+		    (scsilun_to_int((struct scsi_lun *)&lunm_list[i].lun) ==
+		     scsilun_to_int((struct scsi_lun *)&lun))) {
+			lunm_list[i].lp_wwn = 0;
+			lunm_list[i].rp_wwn = 0;
+			int_to_scsilun(0, &lunm_list[i].lun);
+			lunm_list[i].state = BFA_IOIM_LUN_MASK_INACTIVE;
+			if (lunm_list[i].rp_tag != BFA_RPORT_TAG_INVALID) {
+				lunm_list[i].rp_tag = BFA_RPORT_TAG_INVALID;
+				lunm_list[i].lp_tag = BFA_LP_TAG_INVALID;
+			}
+			return bfa_dconf_update(bfa);
+		}
+	}
+
+	/* set for all luns in this rp */
+	for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+		if ((lunm_list[i].lp_wwn == *pwwn) &&
+		    (lunm_list[i].rp_wwn == rpwwn))
+			lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+	}
+
+	return BFA_STATUS_ENTRY_NOT_EXISTS;
+}
+
+static void
 __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete)
 {
 	struct bfa_ioim_s *ioim = cbarg;
@@ -2077,6 +2757,7 @@
 		return;
 	}
 
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 	bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_ABORTED,
 			  0, 0, NULL, 0);
 }
@@ -2092,6 +2773,7 @@
 		return;
 	}
 
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 	bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_PATHTOV,
 			  0, 0, NULL, 0);
 }
@@ -2106,6 +2788,7 @@
 		return;
 	}
 
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 	bfa_cb_ioim_abort(ioim->bfa->bfad, ioim->dio);
 }
 
@@ -2449,6 +3132,7 @@
 		ioim->bfa     = fcpim->bfa;
 		ioim->fcpim   = fcpim;
 		ioim->iosp    = iosp;
+		ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 		INIT_LIST_HEAD(&ioim->sgpg_q);
 		bfa_reqq_winit(&ioim->iosp->reqq_wait,
 				   bfa_ioim_qresume, ioim);
@@ -2486,6 +3170,7 @@
 			evt = BFA_IOIM_SM_DONE;
 		else
 			evt = BFA_IOIM_SM_COMP;
+		ioim->proc_rsp_data(ioim);
 		break;
 
 	case BFI_IOIM_STS_TIMEDOUT:
@@ -2521,6 +3206,7 @@
 		if (rsp->abort_tag != ioim->abort_tag) {
 			bfa_trc(ioim->bfa, rsp->abort_tag);
 			bfa_trc(ioim->bfa, ioim->abort_tag);
+			ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 			return;
 		}
 
@@ -2539,6 +3225,7 @@
 		WARN_ON(1);
 	}
 
+	ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy;
 	bfa_sm_send_event(ioim, evt);
 }
 
@@ -2556,7 +3243,16 @@
 	WARN_ON(BFA_IOIM_TAG_2_ID(ioim->iotag) != iotag);
 
 	bfa_ioim_cb_profile_comp(fcpim, ioim);
-	bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD);
+
+	if (bfa_get_lun_mask_status(bfa) != BFA_LUNMASK_ENABLED)  {
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD);
+		return;
+	}
+
+	if (ioim->proc_rsp_data(ioim) == BFA_TRUE)
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD);
+	else
+		bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP);
 }
 
 /*
@@ -2668,6 +3364,35 @@
 void
 bfa_ioim_start(struct bfa_ioim_s *ioim)
 {
+	struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio;
+	struct bfa_lps_s	*lps;
+	enum bfa_ioim_lm_status status;
+	struct scsi_lun scsilun;
+
+	if (bfa_get_lun_mask_status(ioim->bfa) == BFA_LUNMASK_ENABLED) {
+		lps = BFA_IOIM_TO_LPS(ioim);
+		int_to_scsilun(cmnd->device->lun, &scsilun);
+		status = bfa_ioim_lm_check(ioim, lps,
+				ioim->itnim->rport, scsilun);
+		if (status == BFA_IOIM_LM_LUN_NOT_RDY) {
+			bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_LUN_NOT_RDY);
+			bfa_stats(ioim->itnim, lm_lun_not_rdy);
+			return;
+		}
+
+		if (status == BFA_IOIM_LM_LUN_NOT_SUP) {
+			bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_LUN_NOT_SUP);
+			bfa_stats(ioim->itnim, lm_lun_not_sup);
+			return;
+		}
+
+		if (status == BFA_IOIM_LM_RPL_DATA_CHANGED) {
+			bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_RPL_DC);
+			bfa_stats(ioim->itnim, lm_rpl_data_changed);
+			return;
+		}
+	}
+
 	bfa_ioim_cb_profile_start(ioim->fcpim, ioim);
 
 	/*
@@ -3411,6 +4136,13 @@
 static void
 bfa_fcp_start(struct bfa_s *bfa)
 {
+	struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+
+	/*
+	 * bfa_init() with flash read is complete. now invalidate the stale
+	 * content of lun mask like unit attention, rp tag and lp tag.
+	 */
+	bfa_ioim_lm_init(fcp->bfa);
 }
 
 static void
diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h
index 57b695a..1080bcb 100644
--- a/drivers/scsi/bfa/bfa_fcpim.h
+++ b/drivers/scsi/bfa/bfa_fcpim.h
@@ -79,14 +79,22 @@
 	if (n >= (1UL)<<22)
 		return BFA_IOBUCKET_MAX - 1;
 	n >>= 8;
-	if (n >= (1UL)<<16)
-		n >>= 16; pos += 16;
-	if (n >= 1 << 8)
-		n >>= 8; pos += 8;
-	if (n >= 1 << 4)
-		n >>= 4; pos += 4;
-	if (n >= 1 << 2)
-		n >>= 2; pos += 2;
+	if (n >= (1UL)<<16) {
+		n >>= 16;
+		pos += 16;
+	}
+	if (n >= 1 << 8) {
+		n >>= 8;
+		pos += 8;
+	}
+	if (n >= 1 << 4) {
+		n >>= 4;
+		pos += 4;
+	}
+	if (n >= 1 << 2) {
+		n >>= 2;
+		pos += 2;
+	}
 	if (n >= 1 << 1)
 		pos += 1;
 
@@ -102,6 +110,7 @@
 struct bfad_tskim_s;
 
 typedef void    (*bfa_fcpim_profile_t) (struct bfa_ioim_s *ioim);
+typedef bfa_boolean_t (*bfa_ioim_lm_proc_rsp_data_t) (struct bfa_ioim_s *ioim);
 
 struct bfa_fcpim_s {
 	struct bfa_s		*bfa;
@@ -115,7 +124,7 @@
 	u32			path_tov;
 	u16			q_depth;
 	u8			reqq;		/*  Request queue to be used */
-	u8			rsvd;
+	u8			lun_masking_pending;
 	struct list_head	itnim_q;	/*  queue of active itnim */
 	struct list_head	ioim_resfree_q; /*  IOs waiting for f/w */
 	struct list_head	ioim_comp_q;	/*  IO global comp Q	*/
@@ -170,7 +179,9 @@
 	bfa_cb_cbfn_t		io_cbfn;	/*  IO completion handler */
 	struct bfa_ioim_sp_s	*iosp;		/*  slow-path IO handling */
 	u8			reqq;		/*  Request queue for I/O */
+	u8			mode;		/*  IO is passthrough or not */
 	u64			start_time;	/*  IO's Profile start val */
+	bfa_ioim_lm_proc_rsp_data_t proc_rsp_data; /* RSP data adjust */
 };
 
 struct bfa_ioim_sp_s {
@@ -250,6 +261,10 @@
 	(__ioim)->iotag |= k << BFA_IOIM_RETRY_TAG_OFFSET;		\
 } while (0)
 
+#define BFA_IOIM_TO_LPS(__ioim)		\
+	BFA_LPS_FROM_TAG(BFA_LPS_MOD(__ioim->bfa),	\
+		__ioim->itnim->rport->rport_info.lp_tag)
+
 static inline bfa_boolean_t
 bfa_ioim_maxretry_reached(struct bfa_ioim_s *ioim)
 {
@@ -297,6 +312,8 @@
 			struct bfa_itnim_iostats_s *stats, u8 lp_tag);
 void bfa_fcpim_add_stats(struct bfa_itnim_iostats_s *fcpim_stats,
 			struct bfa_itnim_iostats_s *itnim_stats);
+bfa_status_t bfa_fcpim_profile_on(struct bfa_s *bfa, u32 time);
+bfa_status_t bfa_fcpim_profile_off(struct bfa_s *bfa);
 
 #define bfa_fcpim_ioredirect_enabled(__bfa)				\
 	(((struct bfa_fcpim_s *)(BFA_FCPIM(__bfa)))->ioredirect)
@@ -397,4 +414,14 @@
 void bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk,
 			enum bfi_tskim_status tsk_status);
 
+void	bfa_fcpim_lunmask_rp_update(struct bfa_s *bfa, wwn_t lp_wwn,
+			wwn_t rp_wwn, u16 rp_tag, u8 lp_tag);
+bfa_status_t	bfa_fcpim_lunmask_update(struct bfa_s *bfa, u32 on_off);
+bfa_status_t	bfa_fcpim_lunmask_query(struct bfa_s *bfa, void *buf);
+bfa_status_t	bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id,
+				wwn_t *pwwn, wwn_t rpwwn, struct scsi_lun lun);
+bfa_status_t	bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id,
+				wwn_t *pwwn, wwn_t rpwwn, struct scsi_lun lun);
+bfa_status_t	bfa_fcpim_lunmask_clear(struct bfa_s *bfa);
+
 #endif /* __BFA_FCPIM_H__ */
diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c
index a9b22bc..eaac57e 100644
--- a/drivers/scsi/bfa/bfa_fcs.c
+++ b/drivers/scsi/bfa/bfa_fcs.c
@@ -20,6 +20,7 @@
  */
 
 #include "bfad_drv.h"
+#include "bfad_im.h"
 #include "bfa_fcs.h"
 #include "bfa_fcbuild.h"
 
@@ -1327,6 +1328,29 @@
 	bfa_trc(fabric->fcs, status);
 }
 
+
+/*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_fabric_aen_post(struct bfa_fcs_lport_s *port,
+			enum bfa_port_aen_event event)
+{
+	struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.port.pwwn = bfa_fcs_lport_get_pwwn(port);
+	aen_entry->aen_data.port.fwwn = bfa_fcs_lport_get_fabric_name(port);
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+				  BFA_AEN_CAT_PORT, event);
+}
+
 /*
  *
  * @param[in] fabric - fabric
@@ -1358,6 +1382,8 @@
 		BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
 			"Base port WWN = %s Fabric WWN = %s\n",
 			pwwn_ptr, fwwn_ptr);
+		bfa_fcs_fabric_aen_post(&fabric->bport,
+				BFA_PORT_AEN_FABRIC_NAME_CHANGE);
 	}
 }
 
diff --git a/drivers/scsi/bfa/bfa_fcs.h b/drivers/scsi/bfa/bfa_fcs.h
index a5f1faf..e75e07d 100644
--- a/drivers/scsi/bfa/bfa_fcs.h
+++ b/drivers/scsi/bfa/bfa_fcs.h
@@ -675,6 +675,7 @@
 	struct bfa_fcs_fabric_s fabric; /*  base fabric state machine */
 	struct bfa_fcs_stats_s	stats;	/*  FCS statistics */
 	struct bfa_wc_s		wc;	/*  waiting counter */
+	int			fcs_aen_seq;
 };
 
 /*
diff --git a/drivers/scsi/bfa/bfa_fcs_fcpim.c b/drivers/scsi/bfa/bfa_fcs_fcpim.c
index 29b4108..9272840 100644
--- a/drivers/scsi/bfa/bfa_fcs_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcs_fcpim.c
@@ -37,6 +37,8 @@
 			 struct bfa_fcxp_s *fcxp, void *cbarg,
 			    bfa_status_t req_status, u32 rsp_len,
 			    u32 resid_len, struct fchs_s *rsp_fchs);
+static void	bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
+			enum bfa_itnim_aen_event event);
 
 /*
  *  fcs_itnim_sm FCS itnim state machine events
@@ -269,6 +271,7 @@
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"Target (WWN = %s) is online for initiator (WWN = %s)\n",
 		rpwwn_buf, lpwwn_buf);
+		bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE);
 		break;
 
 	case BFA_FCS_ITNIM_SM_OFFLINE:
@@ -305,14 +308,17 @@
 		bfa_itnim_offline(itnim->bfa_itnim);
 		wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
 		wwn2str(rpwwn_buf, itnim->rport->pwwn);
-		if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE)
+		if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE) {
 			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 			"Target (WWN = %s) connectivity lost for "
 			"initiator (WWN = %s)\n", rpwwn_buf, lpwwn_buf);
-		else
+			bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
+		} else {
 			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Target (WWN = %s) offlined by initiator (WWN = %s)\n",
 			rpwwn_buf, lpwwn_buf);
+			bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
+		}
 		break;
 
 	case BFA_FCS_ITNIM_SM_DELETE:
@@ -382,6 +388,33 @@
 }
 
 static void
+bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
+			enum bfa_itnim_aen_event event)
+{
+	struct bfa_fcs_rport_s *rport = itnim->rport;
+	struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
+	struct bfa_aen_entry_s	*aen_entry;
+
+	/* Don't post events for well known addresses */
+	if (BFA_FCS_PID_IS_WKA(rport->pid))
+		return;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id;
+	aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn(
+					bfa_fcs_get_base_port(itnim->fcs));
+	aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
+	aen_entry->aen_data.itnim.rpwwn = rport->pwwn;
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
+				  BFA_AEN_CAT_ITNIM, event);
+}
+
+static void
 bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced)
 {
 	struct bfa_fcs_itnim_s *itnim = itnim_cbarg;
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index f8251a9..d4f951f 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -16,6 +16,7 @@
  */
 
 #include "bfad_drv.h"
+#include "bfad_im.h"
 #include "bfa_fcs.h"
 #include "bfa_fcbuild.h"
 #include "bfa_fc.h"
@@ -300,6 +301,31 @@
  */
 
 /*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_lport_aen_post(struct bfa_fcs_lport_s *port,
+			enum bfa_lport_aen_event event)
+{
+	struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.lport.vf_id = port->fabric->vf_id;
+	aen_entry->aen_data.lport.roles = port->port_cfg.roles;
+	aen_entry->aen_data.lport.ppwwn = bfa_fcs_lport_get_pwwn(
+					bfa_fcs_get_base_port(port->fcs));
+	aen_entry->aen_data.lport.lpwwn = bfa_fcs_lport_get_pwwn(port);
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+				  BFA_AEN_CAT_LPORT, event);
+}
+
+/*
  * Send a LS reject
  */
 static void
@@ -593,6 +619,7 @@
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"Logical port online: WWN = %s Role = %s\n",
 		lpwwn_buf, "Initiator");
+	bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_ONLINE);
 
 	bfad->bfad_flags |= BFAD_PORT_ONLINE;
 }
@@ -611,14 +638,17 @@
 
 	wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
 	if (bfa_sm_cmp_state(port->fabric,
-			bfa_fcs_fabric_sm_online) == BFA_TRUE)
+			bfa_fcs_fabric_sm_online) == BFA_TRUE) {
 		BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 		"Logical port lost fabric connectivity: WWN = %s Role = %s\n",
 		lpwwn_buf, "Initiator");
-	else
+		bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_DISCONNECT);
+	} else {
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"Logical port taken offline: WWN = %s Role = %s\n",
 		lpwwn_buf, "Initiator");
+		bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_OFFLINE);
+	}
 
 	list_for_each_safe(qe, qen, &port->rport_q) {
 		rport = (struct bfa_fcs_rport_s *) qe;
@@ -676,6 +706,7 @@
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"Logical port deleted: WWN = %s Role = %s\n",
 		lpwwn_buf, "Initiator");
+	bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_DELETE);
 
 	/* Base port will be deleted by the OS driver */
 	if (port->vport) {
@@ -973,6 +1004,7 @@
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"New logical port created: WWN = %s Role = %s\n",
 		lpwwn_buf, "Initiator");
+	bfa_fcs_lport_aen_post(lport, BFA_LPORT_AEN_NEW);
 
 	bfa_sm_set_state(lport, bfa_fcs_lport_sm_uninit);
 	bfa_sm_send_event(lport, BFA_FCS_PORT_SM_CREATE);
@@ -5559,6 +5591,31 @@
  *  fcs_vport_private FCS virtual port private functions
  */
 /*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_vport_aen_post(struct bfa_fcs_lport_s *port,
+		       enum bfa_lport_aen_event event)
+{
+	struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.lport.vf_id = port->fabric->vf_id;
+	aen_entry->aen_data.lport.roles = port->port_cfg.roles;
+	aen_entry->aen_data.lport.ppwwn = bfa_fcs_lport_get_pwwn(
+					bfa_fcs_get_base_port(port->fcs));
+	aen_entry->aen_data.lport.lpwwn = bfa_fcs_lport_get_pwwn(port);
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+				  BFA_AEN_CAT_LPORT, event);
+}
+
+/*
  * This routine will be called to send a FDISC command.
  */
 static void
@@ -5585,8 +5642,11 @@
 	case FC_LS_RJT_EXP_INVALID_NPORT_ID: /* by Cisco */
 		if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
 			bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
-		else
+		else {
+			bfa_fcs_vport_aen_post(&vport->lport,
+					BFA_LPORT_AEN_NPIV_DUP_WWN);
 			bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_DUP_WWN);
+		}
 		break;
 
 	case FC_LS_RJT_EXP_INSUFF_RES:
@@ -5596,11 +5656,17 @@
 		 */
 		if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
 			bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
-		else
+		else {
+			bfa_fcs_vport_aen_post(&vport->lport,
+					BFA_LPORT_AEN_NPIV_FABRIC_MAX);
 			bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED);
+		}
 		break;
 
 	default:
+		if (vport->fdisc_retries == 0)
+			bfa_fcs_vport_aen_post(&vport->lport,
+					BFA_LPORT_AEN_NPIV_UNKNOWN);
 		bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
 	}
 }
diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c
index 2c51445..52628d5 100644
--- a/drivers/scsi/bfa/bfa_fcs_rport.c
+++ b/drivers/scsi/bfa/bfa_fcs_rport.c
@@ -20,6 +20,7 @@
  */
 
 #include "bfad_drv.h"
+#include "bfad_im.h"
 #include "bfa_fcs.h"
 #include "bfa_fcbuild.h"
 
@@ -2041,6 +2042,35 @@
 }
 
 static void
+bfa_fcs_rport_aen_post(struct bfa_fcs_rport_s *rport,
+			enum bfa_rport_aen_event event,
+			struct bfa_rport_aen_data_s *data)
+{
+	struct bfa_fcs_lport_s *port = rport->port;
+	struct bfad_s *bfad = (struct bfad_s *)port->fcs->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	if (event == BFA_RPORT_AEN_QOS_PRIO)
+		aen_entry->aen_data.rport.priv.qos = data->priv.qos;
+	else if (event == BFA_RPORT_AEN_QOS_FLOWID)
+		aen_entry->aen_data.rport.priv.qos = data->priv.qos;
+
+	aen_entry->aen_data.rport.vf_id = rport->port->fabric->vf_id;
+	aen_entry->aen_data.rport.ppwwn = bfa_fcs_lport_get_pwwn(
+					bfa_fcs_get_base_port(rport->fcs));
+	aen_entry->aen_data.rport.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
+	aen_entry->aen_data.rport.rpwwn = rport->pwwn;
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
+				  BFA_AEN_CAT_RPORT, event);
+}
+
+static void
 bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport)
 {
 	struct bfa_fcs_lport_s *port = rport->port;
@@ -2063,10 +2093,12 @@
 
 	wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
 	wwn2str(rpwwn_buf, rport->pwwn);
-	if (!BFA_FCS_PID_IS_WKA(rport->pid))
+	if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 		"Remote port (WWN = %s) online for logical port (WWN = %s)\n",
 		rpwwn_buf, lpwwn_buf);
+		bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_ONLINE, NULL);
+	}
 }
 
 static void
@@ -2083,16 +2115,21 @@
 	wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
 	wwn2str(rpwwn_buf, rport->pwwn);
 	if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
-		if (bfa_fcs_lport_is_online(rport->port) == BFA_TRUE)
+		if (bfa_fcs_lport_is_online(rport->port) == BFA_TRUE) {
 			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 				"Remote port (WWN = %s) connectivity lost for "
 				"logical port (WWN = %s)\n",
 				rpwwn_buf, lpwwn_buf);
-		else
+			bfa_fcs_rport_aen_post(rport,
+				BFA_RPORT_AEN_DISCONNECT, NULL);
+		} else {
 			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 				"Remote port (WWN = %s) offlined by "
 				"logical port (WWN = %s)\n",
 				rpwwn_buf, lpwwn_buf);
+			bfa_fcs_rport_aen_post(rport,
+				BFA_RPORT_AEN_OFFLINE, NULL);
+		}
 	}
 
 	if (bfa_fcs_lport_is_initiator(port)) {
@@ -2366,8 +2403,11 @@
 		struct bfa_rport_qos_attr_s new_qos_attr)
 {
 	struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *) cbarg;
+	struct bfa_rport_aen_data_s aen_data;
 
 	bfa_trc(rport->fcs, rport->pwwn);
+	aen_data.priv.qos = new_qos_attr;
+	bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_FLOWID, &aen_data);
 }
 
 /*
@@ -2390,8 +2430,11 @@
 		struct bfa_rport_qos_attr_s new_qos_attr)
 {
 	struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *) cbarg;
+	struct bfa_rport_aen_data_s aen_data;
 
 	bfa_trc(rport->fcs, rport->pwwn);
+	aen_data.priv.qos = new_qos_attr;
+	bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_PRIO, &aen_data);
 }
 
 /*
diff --git a/drivers/scsi/bfa/bfa_hw_cb.c b/drivers/scsi/bfa/bfa_hw_cb.c
index e7ffd82..ea24d4c 100644
--- a/drivers/scsi/bfa/bfa_hw_cb.c
+++ b/drivers/scsi/bfa/bfa_hw_cb.c
@@ -42,11 +42,36 @@
 			bfa->iocfc.bfa_regs.intr_status);
 }
 
+/*
+ * Actions to respond RME Interrupt for Crossbow ASIC:
+ * - Write 1 to Interrupt Status register
+ *              INTX - done in bfa_intx()
+ *              MSIX - done in bfa_hwcb_rspq_ack_msix()
+ * - Update CI (only if new CI)
+ */
 static void
-bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq)
+bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq, u32 ci)
 {
 	writel(__HFN_INT_RME_Q0 << RME_Q_NUM(bfa_ioc_pcifn(&bfa->ioc), rspq),
-			bfa->iocfc.bfa_regs.intr_status);
+		bfa->iocfc.bfa_regs.intr_status);
+
+	if (bfa_rspq_ci(bfa, rspq) == ci)
+		return;
+
+	bfa_rspq_ci(bfa, rspq) = ci;
+	writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
+	mmiowb();
+}
+
+void
+bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
+{
+	if (bfa_rspq_ci(bfa, rspq) == ci)
+		return;
+
+	bfa_rspq_ci(bfa, rspq) = ci;
+	writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
+	mmiowb();
 }
 
 void
@@ -149,8 +174,13 @@
 void
 bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix)
 {
-	bfa->iocfc.hwif.hw_reqq_ack = bfa_hwcb_reqq_ack_msix;
-	bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix;
+	if (msix) {
+		bfa->iocfc.hwif.hw_reqq_ack = bfa_hwcb_reqq_ack_msix;
+		bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix;
+	} else {
+		bfa->iocfc.hwif.hw_reqq_ack = NULL;
+		bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack;
+	}
 }
 
 void
diff --git a/drivers/scsi/bfa/bfa_hw_ct.c b/drivers/scsi/bfa/bfa_hw_ct.c
index 989bbce..637527f 100644
--- a/drivers/scsi/bfa/bfa_hw_ct.c
+++ b/drivers/scsi/bfa/bfa_hw_ct.c
@@ -64,13 +64,36 @@
 	writel(r32, bfa->iocfc.bfa_regs.cpe_q_ctrl[reqq]);
 }
 
+/*
+ * Actions to respond RME Interrupt for Catapult ASIC:
+ * - Write 1 to Interrupt Status register (INTx only - done in bfa_intx())
+ * - Acknowledge by writing to RME Queue Control register
+ * - Update CI
+ */
 void
-bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq)
+bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
 {
 	u32	r32;
 
 	r32 = readl(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]);
 	writel(r32, bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]);
+
+	bfa_rspq_ci(bfa, rspq) = ci;
+	writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
+	mmiowb();
+}
+
+/*
+ * Actions to respond RME Interrupt for Catapult2 ASIC:
+ * - Write 1 to Interrupt Status register (INTx only - done in bfa_intx())
+ * - Update CI
+ */
+void
+bfa_hwct2_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
+{
+	bfa_rspq_ci(bfa, rspq) = ci;
+	writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
+	mmiowb();
 }
 
 void
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index d6c2bf3..1ac5aec 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -16,6 +16,7 @@
  */
 
 #include "bfad_drv.h"
+#include "bfad_im.h"
 #include "bfa_ioc.h"
 #include "bfi_reg.h"
 #include "bfa_defs.h"
@@ -458,6 +459,7 @@
 	ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
 	bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC enabled\n");
+	bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE);
 }
 
 static void
@@ -502,6 +504,7 @@
 	struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
 	bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_DISABLE);
 	BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC disabled\n");
+	bfa_ioc_aen_post(ioc, BFA_IOC_AEN_DISABLE);
 }
 
 /*
@@ -1966,6 +1969,7 @@
 
 	BFA_LOG(KERN_CRIT, bfad, bfa_log_level,
 		"Heart Beat of IOC has failed\n");
+	bfa_ioc_aen_post(ioc, BFA_IOC_AEN_HBFAIL);
 
 }
 
@@ -1980,6 +1984,7 @@
 	BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
 		"Running firmware version is incompatible "
 		"with the driver version\n");
+	bfa_ioc_aen_post(ioc, BFA_IOC_AEN_FWMISMATCH);
 }
 
 bfa_status_t
@@ -2679,6 +2684,43 @@
 }
 
 /*
+ * Send AEN notification
+ */
+void
+bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event)
+{
+	struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
+	struct bfa_aen_entry_s	*aen_entry;
+	enum bfa_ioc_type_e ioc_type;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	ioc_type = bfa_ioc_get_type(ioc);
+	switch (ioc_type) {
+	case BFA_IOC_TYPE_FC:
+		aen_entry->aen_data.ioc.pwwn = ioc->attr->pwwn;
+		break;
+	case BFA_IOC_TYPE_FCoE:
+		aen_entry->aen_data.ioc.pwwn = ioc->attr->pwwn;
+		aen_entry->aen_data.ioc.mac = bfa_ioc_get_mac(ioc);
+		break;
+	case BFA_IOC_TYPE_LL:
+		aen_entry->aen_data.ioc.mac = bfa_ioc_get_mac(ioc);
+		break;
+	default:
+		WARN_ON(ioc_type != BFA_IOC_TYPE_FC);
+		break;
+	}
+
+	/* Send the AEN notification */
+	aen_entry->aen_data.ioc.ioc_type = ioc_type;
+	bfad_im_post_vendor_event(aen_entry, bfad, ++ioc->ioc_aen_seq,
+				  BFA_AEN_CAT_IOC, event);
+}
+
+/*
  * Retrieve saved firmware trace from a prior IOC failure.
  */
 bfa_status_t
@@ -2879,6 +2921,10 @@
 {
 	if (bfa_ioc_get_type(ioc) == BFA_IOC_TYPE_LL)
 		return;
+	if (ioc->attr->nwwn == 0)
+		bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_NWWN);
+	if (ioc->attr->pwwn == 0)
+		bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_PWWN);
 }
 
 /*
@@ -3443,6 +3489,54 @@
 }
 
 /*
+ * SFP's State Change Notification post to AEN
+ */
+static void
+bfa_sfp_scn_aen_post(struct bfa_sfp_s *sfp, struct bfi_sfp_scn_s *rsp)
+{
+	struct bfad_s *bfad = (struct bfad_s *)sfp->ioc->bfa->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+	enum bfa_port_aen_event aen_evt = 0;
+
+	bfa_trc(sfp, (((u64)rsp->pomlvl) << 16) | (((u64)rsp->sfpid) << 8) |
+		      ((u64)rsp->event));
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.port.ioc_type = bfa_ioc_get_type(sfp->ioc);
+	aen_entry->aen_data.port.pwwn = sfp->ioc->attr->pwwn;
+	aen_entry->aen_data.port.mac = bfa_ioc_get_mac(sfp->ioc);
+
+	switch (rsp->event) {
+	case BFA_SFP_SCN_INSERTED:
+		aen_evt = BFA_PORT_AEN_SFP_INSERT;
+		break;
+	case BFA_SFP_SCN_REMOVED:
+		aen_evt = BFA_PORT_AEN_SFP_REMOVE;
+		break;
+	case BFA_SFP_SCN_FAILED:
+		aen_evt = BFA_PORT_AEN_SFP_ACCESS_ERROR;
+		break;
+	case BFA_SFP_SCN_UNSUPPORT:
+		aen_evt = BFA_PORT_AEN_SFP_UNSUPPORT;
+		break;
+	case BFA_SFP_SCN_POM:
+		aen_evt = BFA_PORT_AEN_SFP_POM;
+		aen_entry->aen_data.port.level = rsp->pomlvl;
+		break;
+	default:
+		bfa_trc(sfp, rsp->event);
+		WARN_ON(1);
+	}
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++sfp->ioc->ioc_aen_seq,
+				  BFA_AEN_CAT_PORT, aen_evt);
+}
+
+/*
  *	SFP get data send
  */
 static void
@@ -3482,6 +3576,50 @@
 }
 
 /*
+ *	SFP scn handler
+ */
+static void
+bfa_sfp_scn(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg)
+{
+	struct bfi_sfp_scn_s *rsp = (struct bfi_sfp_scn_s *) msg;
+
+	switch (rsp->event) {
+	case BFA_SFP_SCN_INSERTED:
+		sfp->state = BFA_SFP_STATE_INSERTED;
+		sfp->data_valid = 0;
+		bfa_sfp_scn_aen_post(sfp, rsp);
+		break;
+	case BFA_SFP_SCN_REMOVED:
+		sfp->state = BFA_SFP_STATE_REMOVED;
+		sfp->data_valid = 0;
+		bfa_sfp_scn_aen_post(sfp, rsp);
+		 break;
+	case BFA_SFP_SCN_FAILED:
+		sfp->state = BFA_SFP_STATE_FAILED;
+		sfp->data_valid = 0;
+		bfa_sfp_scn_aen_post(sfp, rsp);
+		break;
+	case BFA_SFP_SCN_UNSUPPORT:
+		sfp->state = BFA_SFP_STATE_UNSUPPORT;
+		bfa_sfp_scn_aen_post(sfp, rsp);
+		if (!sfp->lock)
+			bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+		break;
+	case BFA_SFP_SCN_POM:
+		bfa_sfp_scn_aen_post(sfp, rsp);
+		break;
+	case BFA_SFP_SCN_VALID:
+		sfp->state = BFA_SFP_STATE_VALID;
+		if (!sfp->lock)
+			bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+		break;
+	default:
+		bfa_trc(sfp, rsp->event);
+		WARN_ON(1);
+	}
+}
+
+/*
  * SFP show complete
  */
 static void
@@ -3645,7 +3783,7 @@
 		break;
 
 	case BFI_SFP_I2H_SCN:
-		bfa_trc(sfp, msg->mh.msg_id);
+		bfa_sfp_scn(sfp, msg);
 		break;
 
 	default:
@@ -3838,6 +3976,26 @@
 	BFA_ROUNDUP(0x010000 + sizeof(struct bfa_mfg_block_s), BFA_FLASH_SEG_SZ)
 
 static void
+bfa_flash_aen_audit_post(struct bfa_ioc_s *ioc, enum bfa_audit_aen_event event,
+			int inst, int type)
+{
+	struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.audit.pwwn = ioc->attr->pwwn;
+	aen_entry->aen_data.audit.partition_inst = inst;
+	aen_entry->aen_data.audit.partition_type = type;
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++ioc->ioc_aen_seq,
+				  BFA_AEN_CAT_AUDIT, event);
+}
+
+static void
 bfa_flash_cb(struct bfa_flash_s *flash)
 {
 	flash->op_busy = 0;
@@ -3978,6 +4136,7 @@
 		struct bfi_flash_erase_rsp_s *erase;
 		struct bfi_flash_write_rsp_s *write;
 		struct bfi_flash_read_rsp_s *read;
+		struct bfi_flash_event_s *event;
 		struct bfi_mbmsg_s   *msg;
 	} m;
 
@@ -4061,8 +4220,19 @@
 		}
 		break;
 	case BFI_FLASH_I2H_BOOT_VER_RSP:
+		break;
 	case BFI_FLASH_I2H_EVENT:
-		bfa_trc(flash, msg->mh.msg_id);
+		status = be32_to_cpu(m.event->status);
+		bfa_trc(flash, status);
+		if (status == BFA_STATUS_BAD_FWCFG)
+			bfa_ioc_aen_post(flash->ioc, BFA_IOC_AEN_FWCFG_ERROR);
+		else if (status == BFA_STATUS_INVALID_VENDOR) {
+			u32 param;
+			param = be32_to_cpu(m.event->param);
+			bfa_trc(flash, param);
+			bfa_ioc_aen_post(flash->ioc,
+				BFA_IOC_AEN_INVALID_VENDOR);
+		}
 		break;
 
 	default:
@@ -4204,6 +4374,8 @@
 	flash->instance = instance;
 
 	bfa_flash_erase_send(flash);
+	bfa_flash_aen_audit_post(flash->ioc, BFA_AUDIT_AEN_FLASH_ERASE,
+				instance, type);
 	return BFA_STATUS_OK;
 }
 
@@ -5416,3 +5588,396 @@
 		WARN_ON(1);
 	}
 }
+
+/*
+ *	DCONF module specific
+ */
+
+BFA_MODULE(dconf);
+
+/*
+ * DCONF state machine events
+ */
+enum bfa_dconf_event {
+	BFA_DCONF_SM_INIT		= 1,	/* dconf Init */
+	BFA_DCONF_SM_FLASH_COMP		= 2,	/* read/write to flash */
+	BFA_DCONF_SM_WR			= 3,	/* binding change, map */
+	BFA_DCONF_SM_TIMEOUT		= 4,	/* Start timer */
+	BFA_DCONF_SM_EXIT		= 5,	/* exit dconf module */
+	BFA_DCONF_SM_IOCDISABLE		= 6,	/* IOC disable event */
+};
+
+/* forward declaration of DCONF state machine */
+static void bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+static void bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+				enum bfa_dconf_event event);
+
+static void bfa_dconf_cbfn(void *dconf, bfa_status_t status);
+static void bfa_dconf_timer(void *cbarg);
+static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
+static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
+
+/*
+ * Begining state of dconf module. Waiting for an event to start.
+ */
+static void
+bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+	bfa_status_t bfa_status;
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_INIT:
+		if (dconf->min_cfg) {
+			bfa_trc(dconf->bfa, dconf->min_cfg);
+			return;
+		}
+		bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read);
+		dconf->flashdone = BFA_FALSE;
+		bfa_trc(dconf->bfa, dconf->flashdone);
+		bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa),
+					BFA_FLASH_PART_DRV, dconf->instance,
+					dconf->dconf,
+					sizeof(struct bfa_dconf_s), 0,
+					bfa_dconf_init_cb, dconf->bfa);
+		if (bfa_status != BFA_STATUS_OK) {
+			bfa_dconf_init_cb(dconf->bfa, BFA_STATUS_FAILED);
+			bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+			return;
+		}
+		break;
+	case BFA_DCONF_SM_EXIT:
+		dconf->flashdone = BFA_TRUE;
+	case BFA_DCONF_SM_IOCDISABLE:
+	case BFA_DCONF_SM_WR:
+	case BFA_DCONF_SM_FLASH_COMP:
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+/*
+ * Read flash for dconf entries and make a call back to the driver once done.
+ */
+static void
+bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+			enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_FLASH_COMP:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+		break;
+	case BFA_DCONF_SM_TIMEOUT:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+		break;
+	case BFA_DCONF_SM_EXIT:
+		dconf->flashdone = BFA_TRUE;
+		bfa_trc(dconf->bfa, dconf->flashdone);
+	case BFA_DCONF_SM_IOCDISABLE:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+/*
+ * DCONF Module is in ready state. Has completed the initialization.
+ */
+static void
+bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_WR:
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+		break;
+	case BFA_DCONF_SM_EXIT:
+		dconf->flashdone = BFA_TRUE;
+		bfa_trc(dconf->bfa, dconf->flashdone);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+		break;
+	case BFA_DCONF_SM_INIT:
+	case BFA_DCONF_SM_IOCDISABLE:
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+/*
+ * entries are dirty, write back to the flash.
+ */
+
+static void
+bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_TIMEOUT:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_sync);
+		bfa_dconf_flash_write(dconf);
+		break;
+	case BFA_DCONF_SM_WR:
+		bfa_timer_stop(&dconf->timer);
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		break;
+	case BFA_DCONF_SM_EXIT:
+		bfa_timer_stop(&dconf->timer);
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+		bfa_dconf_flash_write(dconf);
+		break;
+	case BFA_DCONF_SM_FLASH_COMP:
+		break;
+	case BFA_DCONF_SM_IOCDISABLE:
+		bfa_timer_stop(&dconf->timer);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+/*
+ * Sync the dconf entries to the flash.
+ */
+static void
+bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+			enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_IOCDISABLE:
+	case BFA_DCONF_SM_FLASH_COMP:
+		bfa_timer_stop(&dconf->timer);
+	case BFA_DCONF_SM_TIMEOUT:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+		dconf->flashdone = BFA_TRUE;
+		bfa_trc(dconf->bfa, dconf->flashdone);
+		bfa_ioc_disable(&dconf->bfa->ioc);
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+static void
+bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_FLASH_COMP:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+		break;
+	case BFA_DCONF_SM_WR:
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+		break;
+	case BFA_DCONF_SM_EXIT:
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+		break;
+	case BFA_DCONF_SM_IOCDISABLE:
+		bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+static void
+bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+			enum bfa_dconf_event event)
+{
+	bfa_trc(dconf->bfa, event);
+
+	switch (event) {
+	case BFA_DCONF_SM_INIT:
+		bfa_timer_start(dconf->bfa, &dconf->timer,
+			bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+		bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+		break;
+	case BFA_DCONF_SM_EXIT:
+		dconf->flashdone = BFA_TRUE;
+		bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+		break;
+	case BFA_DCONF_SM_IOCDISABLE:
+		break;
+	default:
+		bfa_sm_fault(dconf->bfa, event);
+	}
+}
+
+/*
+ * Compute and return memory needed by DRV_CFG module.
+ */
+static void
+bfa_dconf_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
+		  struct bfa_s *bfa)
+{
+	struct bfa_mem_kva_s *dconf_kva = BFA_MEM_DCONF_KVA(bfa);
+
+	if (cfg->drvcfg.min_cfg)
+		bfa_mem_kva_setup(meminfo, dconf_kva,
+				sizeof(struct bfa_dconf_hdr_s));
+	else
+		bfa_mem_kva_setup(meminfo, dconf_kva,
+				sizeof(struct bfa_dconf_s));
+}
+
+static void
+bfa_dconf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
+		struct bfa_pcidev_s *pcidev)
+{
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+	dconf->bfad = bfad;
+	dconf->bfa = bfa;
+	dconf->instance = bfa->ioc.port_id;
+	bfa_trc(bfa, dconf->instance);
+
+	dconf->dconf = (struct bfa_dconf_s *) bfa_mem_kva_curp(dconf);
+	if (cfg->drvcfg.min_cfg) {
+		bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_hdr_s);
+		dconf->min_cfg = BFA_TRUE;
+		/*
+		 * Set the flashdone flag to TRUE explicitly as no flash
+		 * write will happen in min_cfg mode.
+		 */
+		dconf->flashdone = BFA_TRUE;
+	} else {
+		dconf->min_cfg = BFA_FALSE;
+		bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_s);
+	}
+
+	bfa_dconf_read_data_valid(bfa) = BFA_FALSE;
+	bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+}
+
+static void
+bfa_dconf_init_cb(void *arg, bfa_status_t status)
+{
+	struct bfa_s *bfa = arg;
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+	dconf->flashdone = BFA_TRUE;
+	bfa_trc(bfa, dconf->flashdone);
+	bfa_iocfc_cb_dconf_modinit(bfa, status);
+	if (status == BFA_STATUS_OK) {
+		bfa_dconf_read_data_valid(bfa) = BFA_TRUE;
+		if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE)
+			dconf->dconf->hdr.signature = BFI_DCONF_SIGNATURE;
+		if (dconf->dconf->hdr.version != BFI_DCONF_VERSION)
+			dconf->dconf->hdr.version = BFI_DCONF_VERSION;
+	}
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+}
+
+void
+bfa_dconf_modinit(struct bfa_s *bfa)
+{
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_INIT);
+}
+static void
+bfa_dconf_start(struct bfa_s *bfa)
+{
+}
+
+static void
+bfa_dconf_stop(struct bfa_s *bfa)
+{
+}
+
+static void bfa_dconf_timer(void *cbarg)
+{
+	struct bfa_dconf_mod_s *dconf = cbarg;
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_TIMEOUT);
+}
+static void
+bfa_dconf_iocdisable(struct bfa_s *bfa)
+{
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_IOCDISABLE);
+}
+
+static void
+bfa_dconf_detach(struct bfa_s *bfa)
+{
+}
+
+static bfa_status_t
+bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf)
+{
+	bfa_status_t bfa_status;
+	bfa_trc(dconf->bfa, 0);
+
+	bfa_status = bfa_flash_update_part(BFA_FLASH(dconf->bfa),
+				BFA_FLASH_PART_DRV, dconf->instance,
+				dconf->dconf,  sizeof(struct bfa_dconf_s), 0,
+				bfa_dconf_cbfn, dconf);
+	if (bfa_status != BFA_STATUS_OK)
+		WARN_ON(bfa_status);
+	bfa_trc(dconf->bfa, bfa_status);
+
+	return bfa_status;
+}
+
+bfa_status_t
+bfa_dconf_update(struct bfa_s *bfa)
+{
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+	bfa_trc(dconf->bfa, 0);
+	if (bfa_sm_cmp_state(dconf, bfa_dconf_sm_iocdown_dirty))
+		return BFA_STATUS_FAILED;
+
+	if (dconf->min_cfg) {
+		bfa_trc(dconf->bfa, dconf->min_cfg);
+		return BFA_STATUS_FAILED;
+	}
+
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_WR);
+	return BFA_STATUS_OK;
+}
+
+static void
+bfa_dconf_cbfn(void *arg, bfa_status_t status)
+{
+	struct bfa_dconf_mod_s *dconf = arg;
+	WARN_ON(status);
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+}
+
+void
+bfa_dconf_modexit(struct bfa_s *bfa)
+{
+	struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+	BFA_DCONF_MOD(bfa)->flashdone = BFA_FALSE;
+	bfa_trc(bfa, BFA_DCONF_MOD(bfa)->flashdone);
+	bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
+}
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index c5ecd2e..546d46b 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -327,6 +327,7 @@
 	enum bfa_mode_s		port_mode;
 	u8			ad_cap_bm;	/* adapter cap bit mask */
 	u8			port_mode_cfg;	/* config port mode */
+	int			ioc_aen_seq;
 };
 
 struct bfa_ioc_hwif_s {
@@ -366,6 +367,8 @@
 	struct list_head	qe;
 	bfa_cb_cbfn_t	cbfn;
 	bfa_boolean_t	once;
+	bfa_boolean_t	pre_rmv;	/* set for stack based qe(s) */
+	bfa_status_t	fw_status;	/* to access fw status in comp proc */
 	void		*cbarg;
 };
 
@@ -658,7 +661,6 @@
 	struct bfa_ioc_notify_s	ioc_notify; /* ioc event notify */
 	struct bfa_mem_dma_s	phy_dma;
 };
-
 #define BFA_PHY(__bfa)	(&(__bfa)->modules.phy)
 #define BFA_MEM_PHY_DMA(__bfa)	(&(BFA_PHY(__bfa)->phy_dma))
 
@@ -684,6 +686,49 @@
 void bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg);
 
 /*
+ * Driver Config( dconf) specific
+ */
+#define BFI_DCONF_SIGNATURE	0xabcdabcd
+#define BFI_DCONF_VERSION	1
+
+#pragma pack(1)
+struct bfa_dconf_hdr_s {
+	u32	signature;
+	u32	version;
+};
+
+struct bfa_dconf_s {
+	struct bfa_dconf_hdr_s		hdr;
+	struct bfa_lunmask_cfg_s	lun_mask;
+};
+#pragma pack()
+
+struct bfa_dconf_mod_s {
+	bfa_sm_t		sm;
+	u8			instance;
+	bfa_boolean_t		flashdone;
+	bfa_boolean_t		read_data_valid;
+	bfa_boolean_t		min_cfg;
+	struct bfa_timer_s	timer;
+	struct bfa_s		*bfa;
+	void			*bfad;
+	void			*trcmod;
+	struct bfa_dconf_s	*dconf;
+	struct bfa_mem_kva_s	kva_seg;
+};
+
+#define BFA_DCONF_MOD(__bfa)	\
+	(&(__bfa)->modules.dconf_mod)
+#define BFA_MEM_DCONF_KVA(__bfa)	(&(BFA_DCONF_MOD(__bfa)->kva_seg))
+#define bfa_dconf_read_data_valid(__bfa)	\
+	(BFA_DCONF_MOD(__bfa)->read_data_valid)
+#define BFA_DCONF_UPDATE_TOV	5000	/* memtest timeout in msec */
+
+void	bfa_dconf_modinit(struct bfa_s *bfa);
+void	bfa_dconf_modexit(struct bfa_s *bfa);
+bfa_status_t	bfa_dconf_update(struct bfa_s *bfa);
+
+/*
  *	IOC specfic macros
  */
 #define bfa_ioc_pcifn(__ioc)		((__ioc)->pcidev.pci_func)
@@ -803,6 +848,7 @@
 			struct bfi_ioc_image_hdr_s *fwhdr);
 bfa_boolean_t bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc,
 			struct bfi_ioc_image_hdr_s *fwhdr);
+void bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event);
 bfa_status_t bfa_ioc_fw_stats_get(struct bfa_ioc_s *ioc, void *stats);
 bfa_status_t bfa_ioc_fw_stats_clear(struct bfa_ioc_s *ioc);
 
diff --git a/drivers/scsi/bfa/bfa_modules.h b/drivers/scsi/bfa/bfa_modules.h
index 1c6efd4..2d36e48 100644
--- a/drivers/scsi/bfa/bfa_modules.h
+++ b/drivers/scsi/bfa/bfa_modules.h
@@ -44,6 +44,7 @@
 	struct bfa_flash_s	flash;		/*  flash module */
 	struct bfa_diag_s	diag_mod;	/*  diagnostics module	*/
 	struct bfa_phy_s	phy;		/*  phy module		*/
+	struct bfa_dconf_mod_s	dconf_mod;	/*  DCONF common module	*/
 };
 
 /*
@@ -119,6 +120,7 @@
 	struct list_head	reqq_waitq[BFI_IOC_MAX_CQS];
 	bfa_boolean_t		fcs;		/*  FCS is attached to BFA */
 	struct bfa_msix_s	msix;
+	int			bfa_aen_seq;
 };
 
 extern bfa_boolean_t bfa_auto_recover;
@@ -130,5 +132,6 @@
 extern struct bfa_module_s hal_mod_uf;
 extern struct bfa_module_s hal_mod_rport;
 extern struct bfa_module_s hal_mod_fcp;
+extern struct bfa_module_s hal_mod_dconf;
 
 #endif /* __BFA_MODULES_H__ */
diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c
index 21caaef..aa8a0ea 100644
--- a/drivers/scsi/bfa/bfa_svc.c
+++ b/drivers/scsi/bfa/bfa_svc.c
@@ -16,6 +16,7 @@
  */
 
 #include "bfad_drv.h"
+#include "bfad_im.h"
 #include "bfa_plog.h"
 #include "bfa_cs.h"
 #include "bfa_modules.h"
@@ -2007,6 +2008,24 @@
 	}
 }
 
+static void
+bfa_fcport_aen_post(struct bfa_fcport_s *fcport, enum bfa_port_aen_event event)
+{
+	struct bfad_s *bfad = (struct bfad_s *)fcport->bfa->bfad;
+	struct bfa_aen_entry_s  *aen_entry;
+
+	bfad_get_aen_entry(bfad, aen_entry);
+	if (!aen_entry)
+		return;
+
+	aen_entry->aen_data.port.ioc_type = bfa_get_type(fcport->bfa);
+	aen_entry->aen_data.port.pwwn = fcport->pwwn;
+
+	/* Send the AEN notification */
+	bfad_im_post_vendor_event(aen_entry, bfad, ++fcport->bfa->bfa_aen_seq,
+				  BFA_AEN_CAT_PORT, event);
+}
+
 /*
  * FC PORT state machine functions
  */
@@ -2095,6 +2114,7 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port disabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
 		break;
 
 	case BFA_FCPORT_SM_LINKUP:
@@ -2155,6 +2175,7 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port disabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
 		break;
 
 	case BFA_FCPORT_SM_STOP:
@@ -2208,6 +2229,12 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port online: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ONLINE);
+
+		/* If QoS is enabled and it is not online, send AEN */
+		if (fcport->cfg.qos_enabled &&
+		    fcport->qos_attr.state != BFA_QOS_ONLINE)
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_QOS_NEG);
 		break;
 
 	case BFA_FCPORT_SM_LINKDOWN:
@@ -2234,6 +2261,7 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port disabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
 		break;
 
 	case BFA_FCPORT_SM_STOP:
@@ -2279,8 +2307,10 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port offline: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port disabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
 		break;
 
 	case BFA_FCPORT_SM_LINKDOWN:
@@ -2290,26 +2320,32 @@
 		bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL,
 				BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown");
 		wwn2str(pwwn_buf, fcport->pwwn);
-		if (BFA_PORT_IS_DISABLED(fcport->bfa))
+		if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
 			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 				"Base port offline: WWN = %s\n", pwwn_buf);
-		else
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+		} else {
 			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 				"Base port (WWN = %s) "
 				"lost fabric connectivity\n", pwwn_buf);
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+		}
 		break;
 
 	case BFA_FCPORT_SM_STOP:
 		bfa_sm_set_state(fcport, bfa_fcport_sm_stopped);
 		bfa_fcport_reset_linkinfo(fcport);
 		wwn2str(pwwn_buf, fcport->pwwn);
-		if (BFA_PORT_IS_DISABLED(fcport->bfa))
+		if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
 			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 				"Base port offline: WWN = %s\n", pwwn_buf);
-		else
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+		} else {
 			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 				"Base port (WWN = %s) "
 				"lost fabric connectivity\n", pwwn_buf);
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+		}
 		break;
 
 	case BFA_FCPORT_SM_HWFAIL:
@@ -2317,13 +2353,16 @@
 		bfa_fcport_reset_linkinfo(fcport);
 		bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE);
 		wwn2str(pwwn_buf, fcport->pwwn);
-		if (BFA_PORT_IS_DISABLED(fcport->bfa))
+		if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
 			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 				"Base port offline: WWN = %s\n", pwwn_buf);
-		else
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+		} else {
 			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
 				"Base port (WWN = %s) "
 				"lost fabric connectivity\n", pwwn_buf);
+			bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+		}
 		break;
 
 	default:
@@ -2454,6 +2493,7 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port enabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ENABLE);
 		break;
 
 	case BFA_FCPORT_SM_STOP:
@@ -2508,6 +2548,7 @@
 		wwn2str(pwwn_buf, fcport->pwwn);
 		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
 			"Base port enabled: WWN = %s\n", pwwn_buf);
+		bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ENABLE);
 		break;
 
 	case BFA_FCPORT_SM_DISABLE:
@@ -2874,6 +2915,9 @@
 
 	port_cfg->trl_def_speed = BFA_PORT_SPEED_1GBPS;
 
+	INIT_LIST_HEAD(&fcport->stats_pending_q);
+	INIT_LIST_HEAD(&fcport->statsclr_pending_q);
+
 	bfa_reqq_winit(&fcport->reqq_wait, bfa_fcport_qresume, fcport);
 }
 
@@ -3102,30 +3146,38 @@
 static void
 __bfa_cb_fcport_stats_get(void *cbarg, bfa_boolean_t complete)
 {
-	struct bfa_fcport_s *fcport = cbarg;
+	struct bfa_fcport_s *fcport = (struct bfa_fcport_s *)cbarg;
+	struct bfa_cb_pending_q_s *cb;
+	struct list_head *qe, *qen;
+	union bfa_fcport_stats_u *ret;
 
 	if (complete) {
-		if (fcport->stats_status == BFA_STATUS_OK) {
-			struct timeval tv;
+		struct timeval tv;
+		if (fcport->stats_status == BFA_STATUS_OK)
+			do_gettimeofday(&tv);
 
-			/* Swap FC QoS or FCoE stats */
-			if (bfa_ioc_get_fcmode(&fcport->bfa->ioc)) {
-				bfa_fcport_qos_stats_swap(
-					&fcport->stats_ret->fcqos,
-					&fcport->stats->fcqos);
-			} else {
-				bfa_fcport_fcoe_stats_swap(
-					&fcport->stats_ret->fcoe,
-					&fcport->stats->fcoe);
-
-				do_gettimeofday(&tv);
-				fcport->stats_ret->fcoe.secs_reset =
+		list_for_each_safe(qe, qen, &fcport->stats_pending_q) {
+			bfa_q_deq(&fcport->stats_pending_q, &qe);
+			cb = (struct bfa_cb_pending_q_s *)qe;
+			if (fcport->stats_status == BFA_STATUS_OK) {
+				ret = (union bfa_fcport_stats_u *)cb->data;
+				/* Swap FC QoS or FCoE stats */
+				if (bfa_ioc_get_fcmode(&fcport->bfa->ioc))
+					bfa_fcport_qos_stats_swap(&ret->fcqos,
+							&fcport->stats->fcqos);
+				else {
+					bfa_fcport_fcoe_stats_swap(&ret->fcoe,
+							&fcport->stats->fcoe);
+					ret->fcoe.secs_reset =
 					tv.tv_sec - fcport->stats_reset_time;
+				}
 			}
+			bfa_cb_queue_status(fcport->bfa, &cb->hcb_qe,
+					fcport->stats_status);
 		}
-		fcport->stats_cbfn(fcport->stats_cbarg, fcport->stats_status);
+		fcport->stats_status = BFA_STATUS_OK;
 	} else {
-		fcport->stats_busy = BFA_FALSE;
+		INIT_LIST_HEAD(&fcport->stats_pending_q);
 		fcport->stats_status = BFA_STATUS_OK;
 	}
 }
@@ -3143,8 +3195,7 @@
 	}
 
 	fcport->stats_status = BFA_STATUS_ETIMER;
-	bfa_cb_queue(fcport->bfa, &fcport->hcb_qe, __bfa_cb_fcport_stats_get,
-		fcport);
+	__bfa_cb_fcport_stats_get(fcport, BFA_TRUE);
 }
 
 static void
@@ -3174,7 +3225,9 @@
 static void
 __bfa_cb_fcport_stats_clr(void *cbarg, bfa_boolean_t complete)
 {
-	struct bfa_fcport_s *fcport = cbarg;
+	struct bfa_fcport_s *fcport = (struct bfa_fcport_s *) cbarg;
+	struct bfa_cb_pending_q_s *cb;
+	struct list_head *qe, *qen;
 
 	if (complete) {
 		struct timeval tv;
@@ -3184,10 +3237,15 @@
 		 */
 		do_gettimeofday(&tv);
 		fcport->stats_reset_time = tv.tv_sec;
-
-		fcport->stats_cbfn(fcport->stats_cbarg, fcport->stats_status);
+		list_for_each_safe(qe, qen, &fcport->statsclr_pending_q) {
+			bfa_q_deq(&fcport->statsclr_pending_q, &qe);
+			cb = (struct bfa_cb_pending_q_s *)qe;
+			bfa_cb_queue_status(fcport->bfa, &cb->hcb_qe,
+						fcport->stats_status);
+		}
+		fcport->stats_status = BFA_STATUS_OK;
 	} else {
-		fcport->stats_busy = BFA_FALSE;
+		INIT_LIST_HEAD(&fcport->statsclr_pending_q);
 		fcport->stats_status = BFA_STATUS_OK;
 	}
 }
@@ -3205,8 +3263,7 @@
 	}
 
 	fcport->stats_status = BFA_STATUS_ETIMER;
-	bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
-			__bfa_cb_fcport_stats_clr, fcport);
+	__bfa_cb_fcport_stats_clr(fcport, BFA_TRUE);
 }
 
 static void
@@ -3402,6 +3459,11 @@
 				fcport->use_flash_cfg = BFA_FALSE;
 			}
 
+			if (fcport->cfg.qos_enabled)
+				fcport->qos_attr.state = BFA_QOS_OFFLINE;
+			else
+				fcport->qos_attr.state = BFA_QOS_DISABLED;
+
 			bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP);
 		}
 		break;
@@ -3426,28 +3488,26 @@
 		/*
 		 * check for timer pop before processing the rsp
 		 */
-		if (fcport->stats_busy == BFA_FALSE ||
-		    fcport->stats_status == BFA_STATUS_ETIMER)
+		if (list_empty(&fcport->stats_pending_q) ||
+		    (fcport->stats_status == BFA_STATUS_ETIMER))
 			break;
 
 		bfa_timer_stop(&fcport->timer);
 		fcport->stats_status = i2hmsg.pstatsget_rsp->status;
-		bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
-				__bfa_cb_fcport_stats_get, fcport);
+		__bfa_cb_fcport_stats_get(fcport, BFA_TRUE);
 		break;
 
 	case BFI_FCPORT_I2H_STATS_CLEAR_RSP:
 		/*
 		 * check for timer pop before processing the rsp
 		 */
-		if (fcport->stats_busy == BFA_FALSE ||
-		    fcport->stats_status == BFA_STATUS_ETIMER)
+		if (list_empty(&fcport->statsclr_pending_q) ||
+		    (fcport->stats_status == BFA_STATUS_ETIMER))
 			break;
 
 		bfa_timer_stop(&fcport->timer);
 		fcport->stats_status = BFA_STATUS_OK;
-		bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
-				__bfa_cb_fcport_stats_clr, fcport);
+		__bfa_cb_fcport_stats_clr(fcport, BFA_TRUE);
 		break;
 
 	case BFI_FCPORT_I2H_ENABLE_AEN:
@@ -3779,25 +3839,25 @@
  * Fetch port statistics (FCQoS or FCoE).
  */
 bfa_status_t
-bfa_fcport_get_stats(struct bfa_s *bfa, union bfa_fcport_stats_u *stats,
-	bfa_cb_port_t cbfn, void *cbarg)
+bfa_fcport_get_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb)
 {
 	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
 
-	if (fcport->stats_busy) {
-		bfa_trc(bfa, fcport->stats_busy);
+	if (bfa_ioc_is_disabled(&bfa->ioc))
+		return BFA_STATUS_IOC_DISABLED;
+
+	if (!list_empty(&fcport->statsclr_pending_q))
 		return BFA_STATUS_DEVBUSY;
-	}
 
-	fcport->stats_busy  = BFA_TRUE;
-	fcport->stats_ret   = stats;
-	fcport->stats_cbfn  = cbfn;
-	fcport->stats_cbarg = cbarg;
+	if (list_empty(&fcport->stats_pending_q)) {
+		list_add_tail(&cb->hcb_qe.qe, &fcport->stats_pending_q);
+		bfa_fcport_send_stats_get(fcport);
+		bfa_timer_start(bfa, &fcport->timer,
+				bfa_fcport_stats_get_timeout,
+				fcport, BFA_FCPORT_STATS_TOV);
+	} else
+		list_add_tail(&cb->hcb_qe.qe, &fcport->stats_pending_q);
 
-	bfa_fcport_send_stats_get(fcport);
-
-	bfa_timer_start(bfa, &fcport->timer, bfa_fcport_stats_get_timeout,
-			fcport, BFA_FCPORT_STATS_TOV);
 	return BFA_STATUS_OK;
 }
 
@@ -3805,27 +3865,25 @@
  * Reset port statistics (FCQoS or FCoE).
  */
 bfa_status_t
-bfa_fcport_clear_stats(struct bfa_s *bfa, bfa_cb_port_t cbfn, void *cbarg)
+bfa_fcport_clear_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb)
 {
 	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
 
-	if (fcport->stats_busy) {
-		bfa_trc(bfa, fcport->stats_busy);
+	if (!list_empty(&fcport->stats_pending_q))
 		return BFA_STATUS_DEVBUSY;
-	}
 
-	fcport->stats_busy  = BFA_TRUE;
-	fcport->stats_cbfn  = cbfn;
-	fcport->stats_cbarg = cbarg;
+	if (list_empty(&fcport->statsclr_pending_q)) {
+		list_add_tail(&cb->hcb_qe.qe, &fcport->statsclr_pending_q);
+		bfa_fcport_send_stats_clear(fcport);
+		bfa_timer_start(bfa, &fcport->timer,
+				bfa_fcport_stats_clr_timeout,
+				fcport, BFA_FCPORT_STATS_TOV);
+	} else
+		list_add_tail(&cb->hcb_qe.qe, &fcport->statsclr_pending_q);
 
-	bfa_fcport_send_stats_clear(fcport);
-
-	bfa_timer_start(bfa, &fcport->timer, bfa_fcport_stats_clr_timeout,
-			fcport, BFA_FCPORT_STATS_TOV);
 	return BFA_STATUS_OK;
 }
 
-
 /*
  * Fetch port attributes.
  */
@@ -4619,6 +4677,7 @@
 		rp = BFA_RPORT_FROM_TAG(bfa, msg.create_rsp->bfa_handle);
 		rp->fw_handle = msg.create_rsp->fw_handle;
 		rp->qos_attr = msg.create_rsp->qos_attr;
+		bfa_rport_set_lunmask(bfa, rp);
 		WARN_ON(msg.create_rsp->status != BFA_STATUS_OK);
 		bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP);
 		break;
@@ -4626,6 +4685,7 @@
 	case BFI_RPORT_I2H_DELETE_RSP:
 		rp = BFA_RPORT_FROM_TAG(bfa, msg.delete_rsp->bfa_handle);
 		WARN_ON(msg.delete_rsp->status != BFA_STATUS_OK);
+		bfa_rport_unset_lunmask(bfa, rp);
 		bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP);
 		break;
 
@@ -4706,6 +4766,37 @@
 	bfa_sm_send_event(rport, BFA_RPORT_SM_SET_SPEED);
 }
 
+/* Set Rport LUN Mask */
+void
+bfa_rport_set_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp)
+{
+	struct bfa_lps_mod_s	*lps_mod = BFA_LPS_MOD(bfa);
+	wwn_t	lp_wwn, rp_wwn;
+	u8 lp_tag = (u8)rp->rport_info.lp_tag;
+
+	rp_wwn = ((struct bfa_fcs_rport_s *)rp->rport_drv)->pwwn;
+	lp_wwn = (BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag))->pwwn;
+
+	BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag)->lun_mask =
+					rp->lun_mask = BFA_TRUE;
+	bfa_fcpim_lunmask_rp_update(bfa, lp_wwn, rp_wwn, rp->rport_tag, lp_tag);
+}
+
+/* Unset Rport LUN mask */
+void
+bfa_rport_unset_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp)
+{
+	struct bfa_lps_mod_s	*lps_mod = BFA_LPS_MOD(bfa);
+	wwn_t	lp_wwn, rp_wwn;
+
+	rp_wwn = ((struct bfa_fcs_rport_s *)rp->rport_drv)->pwwn;
+	lp_wwn = (BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag))->pwwn;
+
+	BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag)->lun_mask =
+				rp->lun_mask = BFA_FALSE;
+	bfa_fcpim_lunmask_rp_update(bfa, lp_wwn, rp_wwn,
+			BFA_RPORT_TAG_INVALID, BFA_LP_TAG_INVALID);
+}
 
 /*
  * SGPG related functions
@@ -5517,11 +5608,29 @@
 		return BFA_STATUS_PORT_NOT_DISABLED;
 	}
 
-	/* Check if the speed is supported */
-	bfa_fcport_get_attr(bfa, &attr);
-	bfa_trc(fcdiag, attr.speed_supported);
-	if (speed > attr.speed_supported)
-		return BFA_STATUS_UNSUPP_SPEED;
+	/*
+	 * Check if input speed is supported by the port mode
+	 */
+	if (bfa_ioc_get_type(&bfa->ioc) == BFA_IOC_TYPE_FC) {
+		if (!(speed == BFA_PORT_SPEED_1GBPS ||
+		      speed == BFA_PORT_SPEED_2GBPS ||
+		      speed == BFA_PORT_SPEED_4GBPS ||
+		      speed == BFA_PORT_SPEED_8GBPS ||
+		      speed == BFA_PORT_SPEED_16GBPS ||
+		      speed == BFA_PORT_SPEED_AUTO)) {
+			bfa_trc(fcdiag, speed);
+			return BFA_STATUS_UNSUPP_SPEED;
+		}
+		bfa_fcport_get_attr(bfa, &attr);
+		bfa_trc(fcdiag, attr.speed_supported);
+		if (speed > attr.speed_supported)
+			return BFA_STATUS_UNSUPP_SPEED;
+	} else {
+		if (speed != BFA_PORT_SPEED_10GBPS) {
+			bfa_trc(fcdiag, speed);
+			return BFA_STATUS_UNSUPP_SPEED;
+		}
+	}
 
 	/* For Mezz card, port speed entered needs to be checked */
 	if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type)) {
diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h
index fbe513a..95adb86 100644
--- a/drivers/scsi/bfa/bfa_svc.h
+++ b/drivers/scsi/bfa/bfa_svc.h
@@ -297,6 +297,7 @@
 	void		*rport_drv;	/*  fcs/driver rport object	    */
 	u16	fw_handle;	/*  firmware rport handle	    */
 	u16	rport_tag;	/*  BFA rport tag		    */
+	u8	lun_mask;	/*  LUN mask flag		    */
 	struct bfa_rport_info_s rport_info; /*  rport info from fcs/driver */
 	struct bfa_reqq_wait_s reqq_wait; /*  to wait for room in reqq     */
 	struct bfa_cb_qe_s hcb_qe;	/*  BFA callback qelem		    */
@@ -404,6 +405,7 @@
 	u8		bb_scn;		/*  local BB_SCN		*/
 	u8		lsrjt_rsn;	/*  LSRJT reason		*/
 	u8		lsrjt_expl;	/*  LSRJT explanation		*/
+	u8		lun_mask;	/*  LUN mask flag		*/
 	wwn_t		pwwn;		/*  port wwn of lport		*/
 	wwn_t		nwwn;		/*  node wwn of lport		*/
 	wwn_t		pr_pwwn;	/*  port wwn of lport peer	*/
@@ -441,7 +443,6 @@
  */
 
 #define BFA_FCPORT(_bfa)	(&((_bfa)->modules.port))
-typedef void (*bfa_cb_port_t) (void *cbarg, enum bfa_status status);
 
 /*
  * Link notification data structure
@@ -495,13 +496,11 @@
 	u8			*stats_kva;
 	u64		stats_pa;
 	union bfa_fcport_stats_u *stats;
-	union bfa_fcport_stats_u *stats_ret; /*  driver stats location */
 	bfa_status_t		stats_status; /*  stats/statsclr status */
-	bfa_boolean_t		stats_busy; /*  outstanding stats/statsclr */
+	struct list_head	stats_pending_q;
+	struct list_head	statsclr_pending_q;
 	bfa_boolean_t		stats_qfull;
 	u32		stats_reset_time; /*  stats reset time stamp */
-	bfa_cb_port_t		stats_cbfn; /*  driver callback function */
-	void			*stats_cbarg; /* *!< user callback arg */
 	bfa_boolean_t		diag_busy; /*  diag busy status */
 	bfa_boolean_t		beacon; /*  port beacon status */
 	bfa_boolean_t		link_e2e_beacon; /*  link beacon status */
@@ -552,10 +551,9 @@
 			bfa_boolean_t link_e2e_beacon);
 bfa_boolean_t	bfa_fcport_is_linkup(struct bfa_s *bfa);
 bfa_status_t bfa_fcport_get_stats(struct bfa_s *bfa,
-				  union bfa_fcport_stats_u *stats,
-				  bfa_cb_port_t cbfn, void *cbarg);
-bfa_status_t bfa_fcport_clear_stats(struct bfa_s *bfa, bfa_cb_port_t cbfn,
-				    void *cbarg);
+			struct bfa_cb_pending_q_s *cb);
+bfa_status_t bfa_fcport_clear_stats(struct bfa_s *bfa,
+			struct bfa_cb_pending_q_s *cb);
 bfa_boolean_t bfa_fcport_is_qos_enabled(struct bfa_s *bfa);
 bfa_boolean_t bfa_fcport_is_trunk_enabled(struct bfa_s *bfa);
 bfa_status_t bfa_fcport_is_pbcdisabled(struct bfa_s *bfa);
@@ -578,6 +576,19 @@
 			       struct bfa_rport_qos_attr_s new_qos_attr);
 
 /*
+ *	Rport LUN masking related
+ */
+#define BFA_RPORT_TAG_INVALID	0xffff
+#define BFA_LP_TAG_INVALID	0xff
+void	bfa_rport_set_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp);
+void	bfa_rport_unset_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp);
+bfa_boolean_t	bfa_rport_lunmask_active(struct bfa_rport_s *rp);
+wwn_t	bfa_rport_get_pwwn(struct bfa_s *bfa, struct bfa_rport_s *rp);
+struct bfa_rport_s *bfa_rport_get_by_wwn(struct bfa_s *bfa, u16 vf_id,
+					 wwn_t *lpwwn, wwn_t rpwwn);
+void *bfa_cb_get_rp_by_wwn(void *arg, u16 vf_id, wwn_t *lpwwn, wwn_t rpwwn);
+
+/*
  * bfa fcxp API functions
  */
 struct bfa_fcxp_s *bfa_fcxp_alloc(void *bfad_fcxp, struct bfa_s *bfa,
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index beb30a7..66fb725 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -1348,7 +1348,7 @@
 bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
 {
 	struct bfad_s	*bfad;
-	int		error = -ENODEV, retval;
+	int		error = -ENODEV, retval, i;
 
 	/* For single port cards - only claim function 0 */
 	if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P) &&
@@ -1372,6 +1372,12 @@
 	bfa_trc_init(bfad->trcmod);
 	bfa_trc(bfad, bfad_inst);
 
+	/* AEN INIT */
+	INIT_LIST_HEAD(&bfad->free_aen_q);
+	INIT_LIST_HEAD(&bfad->active_aen_q);
+	for (i = 0; i < BFA_AEN_MAX_ENTRY; i++)
+		list_add_tail(&bfad->aen_list[i].qe, &bfad->free_aen_q);
+
 	if (!(bfad_load_fwimg(pdev))) {
 		kfree(bfad->trcmod);
 		goto out_alloc_trace_failure;
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 89f863e..06fc00c 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -56,7 +56,7 @@
 	spin_lock_irqsave(&bfad->bfad_lock, flags);
 	if (bfad->disable_active) {
 		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-		return EBUSY;
+		return -EBUSY;
 	}
 
 	bfad->disable_active = BFA_TRUE;
@@ -90,6 +90,7 @@
 	bfa_get_adapter_serial_num(&bfad->bfa, iocmd->serialnum);
 	iocmd->factorynwwn = pattr.factorynwwn;
 	iocmd->factorypwwn = pattr.factorypwwn;
+	iocmd->bfad_num = bfad->inst_no;
 	im_port = bfad->pport.im_port;
 	iocmd->host = im_port->shost->host_no;
 	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
@@ -178,6 +179,38 @@
 }
 
 int
+bfad_iocmd_ioc_reset_stats(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	unsigned long	flags;
+
+	if (v_cmd == IOCMD_IOC_RESET_STATS) {
+		bfa_ioc_clear_stats(&bfad->bfa);
+		iocmd->status = BFA_STATUS_OK;
+	} else if (v_cmd == IOCMD_IOC_RESET_FWSTATS) {
+		spin_lock_irqsave(&bfad->bfad_lock, flags);
+		iocmd->status = bfa_ioc_fw_stats_clear(&bfad->bfa.ioc);
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	}
+
+	return 0;
+}
+
+int
+bfad_iocmd_ioc_set_name(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_ioc_name_s *iocmd = (struct bfa_bsg_ioc_name_s *) cmd;
+
+	if (v_cmd == IOCMD_IOC_SET_ADAPTER_NAME)
+		strcpy(bfad->adapter_name, iocmd->name);
+	else if (v_cmd == IOCMD_IOC_SET_PORT_NAME)
+		strcpy(bfad->port_name, iocmd->name);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
 bfad_iocmd_iocfc_get_attr(struct bfad_s *bfad, void *cmd)
 {
 	struct bfa_bsg_iocfc_attr_s *iocmd = (struct bfa_bsg_iocfc_attr_s *)cmd;
@@ -306,6 +339,81 @@
 	return 0;
 }
 
+int
+bfad_iocmd_port_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+
+	init_completion(&fcomp.comp);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_port_clear_stats(&bfad->bfa.modules.port,
+					bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK) {
+		bfa_trc(bfad, iocmd->status);
+		return 0;
+	}
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+	return 0;
+}
+
+int
+bfad_iocmd_set_port_cfg(struct bfad_s *bfad, void *iocmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_port_cfg_s *cmd = (struct bfa_bsg_port_cfg_s *)iocmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (v_cmd == IOCMD_PORT_CFG_TOPO)
+		cmd->status = bfa_fcport_cfg_topology(&bfad->bfa, cmd->param);
+	else if (v_cmd == IOCMD_PORT_CFG_SPEED)
+		cmd->status = bfa_fcport_cfg_speed(&bfad->bfa, cmd->param);
+	else if (v_cmd == IOCMD_PORT_CFG_ALPA)
+		cmd->status = bfa_fcport_cfg_hardalpa(&bfad->bfa, cmd->param);
+	else if (v_cmd == IOCMD_PORT_CLR_ALPA)
+		cmd->status = bfa_fcport_clr_hardalpa(&bfad->bfa);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+int
+bfad_iocmd_port_cfg_maxfrsize(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_port_cfg_maxfrsize_s *iocmd =
+				(struct bfa_bsg_port_cfg_maxfrsize_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_fcport_cfg_maxfrsize(&bfad->bfa, iocmd->maxfrsize);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+int
+bfad_iocmd_port_cfg_bbsc(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (bfa_ioc_get_type(&bfad->bfa.ioc) == BFA_IOC_TYPE_FC) {
+		if (v_cmd == IOCMD_PORT_BBSC_ENABLE)
+			fcport->cfg.bb_scn_state = BFA_TRUE;
+		else if (v_cmd == IOCMD_PORT_BBSC_DISABLE)
+			fcport->cfg.bb_scn_state = BFA_FALSE;
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
 static int
 bfad_iocmd_lport_get_attr(struct bfad_s *bfad, void *cmd)
 {
@@ -354,6 +462,40 @@
 }
 
 int
+bfad_iocmd_lport_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_fcs_lport_s *fcs_port;
+	struct bfa_bsg_reset_stats_s *iocmd =
+			(struct bfa_bsg_reset_stats_s *)cmd;
+	struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+	struct list_head *qe, *qen;
+	struct bfa_itnim_s *itnim;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->vpwwn);
+	if (fcs_port == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+		goto out;
+	}
+
+	bfa_fcs_lport_clear_stats(fcs_port);
+	/* clear IO stats from all active itnims */
+	list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+		itnim = (struct bfa_itnim_s *) qe;
+		if (itnim->rport->rport_info.lp_tag != fcs_port->lp_tag)
+			continue;
+		bfa_itnim_clear_stats(itnim);
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
 bfad_iocmd_lport_get_iostats(struct bfad_s *bfad, void *cmd)
 {
 	struct bfa_fcs_lport_s *fcs_port;
@@ -389,7 +531,7 @@
 	void	*iocmd_bufptr;
 
 	if (iocmd->nrports == 0)
-		return EINVAL;
+		return -EINVAL;
 
 	if (bfad_chk_iocmd_sz(payload_len,
 			sizeof(struct bfa_bsg_lport_get_rports_s),
@@ -539,6 +681,152 @@
 	return 0;
 }
 
+int
+bfad_iocmd_rport_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_rport_reset_stats_s *iocmd =
+				(struct bfa_bsg_rport_reset_stats_s *)cmd;
+	struct bfa_fcs_lport_s *fcs_port;
+	struct bfa_fcs_rport_s *fcs_rport;
+	struct bfa_rport_s *rport;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->pwwn);
+	if (fcs_port == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+		goto out;
+	}
+
+	fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+	if (fcs_rport == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+		goto out;
+	}
+
+	memset((char *)&fcs_rport->stats, 0, sizeof(struct bfa_rport_stats_s));
+	rport = bfa_fcs_rport_get_halrport(fcs_rport);
+	memset(&rport->stats, 0, sizeof(rport->stats));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_rport_set_speed(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_rport_set_speed_s *iocmd =
+				(struct bfa_bsg_rport_set_speed_s *)cmd;
+	struct bfa_fcs_lport_s *fcs_port;
+	struct bfa_fcs_rport_s *fcs_rport;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->pwwn);
+	if (fcs_port == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+		goto out;
+	}
+
+	fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+	if (fcs_rport == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+		goto out;
+	}
+
+	fcs_rport->rpf.assigned_speed  = iocmd->speed;
+	/* Set this speed in f/w only if the RPSC speed is not available */
+	if (fcs_rport->rpf.rpsc_speed == BFA_PORT_SPEED_UNKNOWN)
+		bfa_rport_speed(fcs_rport->bfa_rport, iocmd->speed);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_vport_get_attr(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_fcs_vport_s *fcs_vport;
+	struct bfa_bsg_vport_attr_s *iocmd = (struct bfa_bsg_vport_attr_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->vpwwn);
+	if (fcs_vport == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+		goto out;
+	}
+
+	bfa_fcs_vport_get_attr(fcs_vport, &iocmd->vport_attr);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_vport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_fcs_vport_s *fcs_vport;
+	struct bfa_bsg_vport_stats_s *iocmd =
+				(struct bfa_bsg_vport_stats_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->vpwwn);
+	if (fcs_vport == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+		goto out;
+	}
+
+	memcpy((void *)&iocmd->vport_stats, (void *)&fcs_vport->vport_stats,
+		sizeof(struct bfa_vport_stats_s));
+	memcpy((void *)&iocmd->vport_stats.port_stats,
+	       (void *)&fcs_vport->lport.stats,
+		sizeof(struct bfa_lport_stats_s));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_vport_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_fcs_vport_s *fcs_vport;
+	struct bfa_bsg_reset_stats_s *iocmd =
+				(struct bfa_bsg_reset_stats_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->vpwwn);
+	if (fcs_vport == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+		goto out;
+	}
+
+	memset(&fcs_vport->vport_stats, 0, sizeof(struct bfa_vport_stats_s));
+	memset(&fcs_vport->lport.stats, 0, sizeof(struct bfa_lport_stats_s));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
 static int
 bfad_iocmd_fabric_get_lports(struct bfad_s *bfad, void *cmd,
 			unsigned int payload_len)
@@ -582,6 +870,66 @@
 }
 
 int
+bfad_iocmd_ratelim(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+	if (cmd == IOCMD_RATELIM_ENABLE)
+		fcport->cfg.ratelimit = BFA_TRUE;
+	else if (cmd == IOCMD_RATELIM_DISABLE)
+		fcport->cfg.ratelimit = BFA_FALSE;
+
+	if (fcport->cfg.trl_def_speed == BFA_PORT_SPEED_UNKNOWN)
+		fcport->cfg.trl_def_speed = BFA_PORT_SPEED_1GBPS;
+
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+
+	return 0;
+}
+
+int
+bfad_iocmd_ratelim_speed(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+	struct bfa_bsg_trl_speed_s *iocmd = (struct bfa_bsg_trl_speed_s *)pcmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+	/* Auto and speeds greater than the supported speed, are invalid */
+	if ((iocmd->speed == BFA_PORT_SPEED_AUTO) ||
+	    (iocmd->speed > fcport->speed_sup)) {
+		iocmd->status = BFA_STATUS_UNSUPP_SPEED;
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		return 0;
+	}
+
+	fcport->cfg.trl_def_speed = iocmd->speed;
+	iocmd->status = BFA_STATUS_OK;
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+int
+bfad_iocmd_cfg_fcpim(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_fcpim_s *iocmd = (struct bfa_bsg_fcpim_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	bfa_fcpim_path_tov_set(&bfad->bfa, iocmd->param);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
 bfad_iocmd_fcpim_get_modstats(struct bfad_s *bfad, void *cmd)
 {
 	struct bfa_bsg_fcpim_modstats_s *iocmd =
@@ -604,6 +952,28 @@
 }
 
 int
+bfad_iocmd_fcpim_clr_modstats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_fcpim_modstatsclr_s *iocmd =
+				(struct bfa_bsg_fcpim_modstatsclr_s *)cmd;
+	struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+	struct list_head *qe, *qen;
+	struct bfa_itnim_s *itnim;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+		itnim = (struct bfa_itnim_s *) qe;
+		bfa_itnim_clear_stats(itnim);
+	}
+	memset(&fcpim->del_itn_stats, 0,
+		sizeof(struct bfa_fcpim_del_itn_stats_s));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
 bfad_iocmd_fcpim_get_del_itn_stats(struct bfad_s *bfad, void *cmd)
 {
 	struct bfa_bsg_fcpim_del_itn_stats_s *iocmd =
@@ -670,6 +1040,35 @@
 }
 
 static int
+bfad_iocmd_itnim_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_rport_reset_stats_s *iocmd =
+			(struct bfa_bsg_rport_reset_stats_s *)cmd;
+	struct bfa_fcs_lport_s	*fcs_port;
+	struct bfa_fcs_itnim_s	*itnim;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->pwwn);
+	if (!fcs_port)
+		iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+	else {
+		itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+		if (itnim == NULL)
+			iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+		else {
+			iocmd->status = BFA_STATUS_OK;
+			bfa_fcs_itnim_stats_clear(fcs_port, iocmd->rpwwn);
+			bfa_itnim_clear_stats(bfa_fcs_itnim_get_halitn(itnim));
+		}
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+static int
 bfad_iocmd_itnim_get_itnstats(struct bfad_s *bfad, void *cmd)
 {
 	struct bfa_bsg_itnim_itnstats_s *iocmd =
@@ -1511,11 +1910,545 @@
 	return 0;
 }
 
+#define BFA_DEBUG_FW_CORE_CHUNK_SZ	0x4000U /* 16K chunks for FW dump */
+int
+bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
+			unsigned int payload_len)
+{
+	struct bfa_bsg_debug_s *iocmd = (struct bfa_bsg_debug_s *)cmd;
+	void	*iocmd_bufptr;
+	unsigned long	flags;
+
+	if (bfad_chk_iocmd_sz(payload_len, sizeof(struct bfa_bsg_debug_s),
+			BFA_DEBUG_FW_CORE_CHUNK_SZ) != BFA_STATUS_OK) {
+		iocmd->status = BFA_STATUS_VERSION_FAIL;
+		return 0;
+	}
+
+	if (iocmd->bufsz < BFA_DEBUG_FW_CORE_CHUNK_SZ ||
+			!IS_ALIGNED(iocmd->bufsz, sizeof(u16)) ||
+			!IS_ALIGNED(iocmd->offset, sizeof(u32))) {
+		bfa_trc(bfad, BFA_DEBUG_FW_CORE_CHUNK_SZ);
+		iocmd->status = BFA_STATUS_EINVAL;
+		goto out;
+	}
+
+	iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_debug_s);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_ioc_debug_fwcore(&bfad->bfa.ioc, iocmd_bufptr,
+				(u32 *)&iocmd->offset, &iocmd->bufsz);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_debug_ctl(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	unsigned long	flags;
+
+	if (v_cmd == IOCMD_DEBUG_FW_STATE_CLR) {
+		spin_lock_irqsave(&bfad->bfad_lock, flags);
+		bfad->bfa.ioc.dbg_fwsave_once = BFA_TRUE;
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	} else if (v_cmd == IOCMD_DEBUG_PORTLOG_CLR)
+		bfad->plog_buf.head = bfad->plog_buf.tail = 0;
+	else if (v_cmd == IOCMD_DEBUG_START_DTRC)
+		bfa_trc_init(bfad->trcmod);
+	else if (v_cmd == IOCMD_DEBUG_STOP_DTRC)
+		bfa_trc_stop(bfad->trcmod);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_porglog_ctl(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_portlogctl_s *iocmd = (struct bfa_bsg_portlogctl_s *)cmd;
+
+	if (iocmd->ctl == BFA_TRUE)
+		bfad->plog_buf.plog_enabled = 1;
+	else
+		bfad->plog_buf.plog_enabled = 0;
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_fcpim_cfg_profile(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_fcpim_profile_s *iocmd =
+				(struct bfa_bsg_fcpim_profile_s *)cmd;
+	struct timeval  tv;
+	unsigned long	flags;
+
+	do_gettimeofday(&tv);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (v_cmd == IOCMD_FCPIM_PROFILE_ON)
+		iocmd->status = bfa_fcpim_profile_on(&bfad->bfa, tv.tv_sec);
+	else if (v_cmd == IOCMD_FCPIM_PROFILE_OFF)
+		iocmd->status = bfa_fcpim_profile_off(&bfad->bfa);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+static int
+bfad_iocmd_itnim_get_ioprofile(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_itnim_ioprofile_s *iocmd =
+				(struct bfa_bsg_itnim_ioprofile_s *)cmd;
+	struct bfa_fcs_lport_s *fcs_port;
+	struct bfa_fcs_itnim_s *itnim;
+	unsigned long   flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+				iocmd->vf_id, iocmd->lpwwn);
+	if (!fcs_port)
+		iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+	else {
+		itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+		if (itnim == NULL)
+			iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+		else
+			iocmd->status = bfa_itnim_get_ioprofile(
+						bfa_fcs_itnim_get_halitn(itnim),
+						&iocmd->ioprofile);
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	return 0;
+}
+
+int
+bfad_iocmd_fcport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_fcport_stats_s *iocmd =
+				(struct bfa_bsg_fcport_stats_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+	struct bfa_cb_pending_q_s cb_qe;
+
+	init_completion(&fcomp.comp);
+	bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp,
+			   &fcomp, &iocmd->stats);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK) {
+		bfa_trc(bfad, iocmd->status);
+		goto out;
+	}
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_fcport_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+	struct bfa_cb_pending_q_s cb_qe;
+
+	init_completion(&fcomp.comp);
+	bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp, &fcomp, NULL);
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK) {
+		bfa_trc(bfad, iocmd->status);
+		goto out;
+	}
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_boot_cfg(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_boot_s *iocmd = (struct bfa_bsg_boot_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+
+	init_completion(&fcomp.comp);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa),
+			BFA_FLASH_PART_BOOT, PCI_FUNC(bfad->pcidev->devfn),
+			&iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0,
+			bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_boot_query(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_boot_s *iocmd = (struct bfa_bsg_boot_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+
+	init_completion(&fcomp.comp);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa),
+			BFA_FLASH_PART_BOOT, PCI_FUNC(bfad->pcidev->devfn),
+			&iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0,
+			bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_preboot_query(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_preboot_s *iocmd = (struct bfa_bsg_preboot_s *)cmd;
+	struct bfi_iocfc_cfgrsp_s *cfgrsp = bfad->bfa.iocfc.cfgrsp;
+	struct bfa_boot_pbc_s *pbcfg = &iocmd->cfg;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	pbcfg->enable = cfgrsp->pbc_cfg.boot_enabled;
+	pbcfg->nbluns = cfgrsp->pbc_cfg.nbluns;
+	pbcfg->speed = cfgrsp->pbc_cfg.port_speed;
+	memcpy(pbcfg->pblun, cfgrsp->pbc_cfg.blun, sizeof(pbcfg->pblun));
+	iocmd->status = BFA_STATUS_OK;
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	return 0;
+}
+
+int
+bfad_iocmd_ethboot_cfg(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_ethboot_s *iocmd = (struct bfa_bsg_ethboot_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+
+	init_completion(&fcomp.comp);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa),
+				BFA_FLASH_PART_PXECFG,
+				bfad->bfa.ioc.port_id, &iocmd->cfg,
+				sizeof(struct bfa_ethboot_cfg_s), 0,
+				bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_ethboot_query(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_ethboot_s *iocmd = (struct bfa_bsg_ethboot_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+
+	init_completion(&fcomp.comp);
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa),
+				BFA_FLASH_PART_PXECFG,
+				bfad->bfa.ioc.port_id, &iocmd->cfg,
+				sizeof(struct bfa_ethboot_cfg_s), 0,
+				bfad_hcb_comp, &fcomp);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK)
+		goto out;
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_cfg_trunk(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	struct bfa_fcport_trunk_s *trunk = &fcport->trunk;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+	if (v_cmd == IOCMD_TRUNK_ENABLE) {
+		trunk->attr.state = BFA_TRUNK_OFFLINE;
+		bfa_fcport_disable(&bfad->bfa);
+		fcport->cfg.trunked = BFA_TRUE;
+	} else if (v_cmd == IOCMD_TRUNK_DISABLE) {
+		trunk->attr.state = BFA_TRUNK_DISABLED;
+		bfa_fcport_disable(&bfad->bfa);
+		fcport->cfg.trunked = BFA_FALSE;
+	}
+
+	if (!bfa_fcport_is_disabled(&bfad->bfa))
+		bfa_fcport_enable(&bfad->bfa);
+
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_trunk_get_attr(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_trunk_attr_s *iocmd = (struct bfa_bsg_trunk_attr_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	struct bfa_fcport_trunk_s *trunk = &fcport->trunk;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	memcpy((void *)&iocmd->attr, (void *)&trunk->attr,
+		sizeof(struct bfa_trunk_attr_s));
+	iocmd->attr.port_id = bfa_lps_get_base_pid(&bfad->bfa);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_qos(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (bfa_ioc_get_type(&bfad->bfa.ioc) == BFA_IOC_TYPE_FC) {
+		if (v_cmd == IOCMD_QOS_ENABLE)
+			fcport->cfg.qos_enabled = BFA_TRUE;
+		else if (v_cmd == IOCMD_QOS_DISABLE)
+			fcport->cfg.qos_enabled = BFA_FALSE;
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_qos_get_attr(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_qos_attr_s *iocmd = (struct bfa_bsg_qos_attr_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->attr.state = fcport->qos_attr.state;
+	iocmd->attr.total_bb_cr = be32_to_cpu(fcport->qos_attr.total_bb_cr);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_qos_get_vc_attr(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_qos_vc_attr_s *iocmd =
+				(struct bfa_bsg_qos_vc_attr_s *)cmd;
+	struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+	struct bfa_qos_vc_attr_s *bfa_vc_attr = &fcport->qos_vc_attr;
+	unsigned long	flags;
+	u32	i = 0;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->attr.total_vc_count = be16_to_cpu(bfa_vc_attr->total_vc_count);
+	iocmd->attr.shared_credit  = be16_to_cpu(bfa_vc_attr->shared_credit);
+	iocmd->attr.elp_opmode_flags  =
+				be32_to_cpu(bfa_vc_attr->elp_opmode_flags);
+
+	/* Individual VC info */
+	while (i < iocmd->attr.total_vc_count) {
+		iocmd->attr.vc_info[i].vc_credit =
+				bfa_vc_attr->vc_info[i].vc_credit;
+		iocmd->attr.vc_info[i].borrow_credit =
+				bfa_vc_attr->vc_info[i].borrow_credit;
+		iocmd->attr.vc_info[i].priority =
+				bfa_vc_attr->vc_info[i].priority;
+		i++;
+	}
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+	iocmd->status = BFA_STATUS_OK;
+	return 0;
+}
+
+int
+bfad_iocmd_qos_get_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_fcport_stats_s *iocmd =
+				(struct bfa_bsg_fcport_stats_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+	struct bfa_cb_pending_q_s cb_qe;
+
+	init_completion(&fcomp.comp);
+	bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp,
+			   &fcomp, &iocmd->stats);
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc));
+	iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK) {
+		bfa_trc(bfad, iocmd->status);
+		goto out;
+	}
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_qos_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+	struct bfad_hal_comp fcomp;
+	unsigned long	flags;
+	struct bfa_cb_pending_q_s cb_qe;
+
+	init_completion(&fcomp.comp);
+	bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp,
+			   &fcomp, NULL);
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc));
+	iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	if (iocmd->status != BFA_STATUS_OK) {
+		bfa_trc(bfad, iocmd->status);
+		goto out;
+	}
+	wait_for_completion(&fcomp.comp);
+	iocmd->status = fcomp.status;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_vf_get_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_vf_stats_s *iocmd =
+			(struct bfa_bsg_vf_stats_s *)cmd;
+	struct bfa_fcs_fabric_s	*fcs_vf;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_vf = bfa_fcs_vf_lookup(&bfad->bfa_fcs, iocmd->vf_id);
+	if (fcs_vf == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_VFID;
+		goto out;
+	}
+	memcpy((void *)&iocmd->stats, (void *)&fcs_vf->stats,
+		sizeof(struct bfa_vf_stats_s));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_vf_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_vf_reset_stats_s *iocmd =
+			(struct bfa_bsg_vf_reset_stats_s *)cmd;
+	struct bfa_fcs_fabric_s	*fcs_vf;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	fcs_vf = bfa_fcs_vf_lookup(&bfad->bfa_fcs, iocmd->vf_id);
+	if (fcs_vf == NULL) {
+		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+		iocmd->status = BFA_STATUS_UNKNOWN_VFID;
+		goto out;
+	}
+	memset((void *)&fcs_vf->stats, 0, sizeof(struct bfa_vf_stats_s));
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	iocmd->status = BFA_STATUS_OK;
+out:
+	return 0;
+}
+
+int
+bfad_iocmd_lunmask(struct bfad_s *bfad, void *pcmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (v_cmd == IOCMD_FCPIM_LUNMASK_ENABLE)
+		iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_TRUE);
+	else if (v_cmd == IOCMD_FCPIM_LUNMASK_DISABLE)
+		iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_FALSE);
+	else if (v_cmd == IOCMD_FCPIM_LUNMASK_CLEAR)
+		iocmd->status = bfa_fcpim_lunmask_clear(&bfad->bfa);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	return 0;
+}
+
+int
+bfad_iocmd_fcpim_lunmask_query(struct bfad_s *bfad, void *cmd)
+{
+	struct bfa_bsg_fcpim_lunmask_query_s *iocmd =
+			(struct bfa_bsg_fcpim_lunmask_query_s *)cmd;
+	struct bfa_lunmask_cfg_s *lun_mask = &iocmd->lun_mask;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	iocmd->status = bfa_fcpim_lunmask_query(&bfad->bfa, lun_mask);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	return 0;
+}
+
+int
+bfad_iocmd_fcpim_cfg_lunmask(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+	struct bfa_bsg_fcpim_lunmask_s *iocmd =
+				(struct bfa_bsg_fcpim_lunmask_s *)cmd;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&bfad->bfad_lock, flags);
+	if (v_cmd == IOCMD_FCPIM_LUNMASK_ADD)
+		iocmd->status = bfa_fcpim_lunmask_add(&bfad->bfa, iocmd->vf_id,
+					&iocmd->pwwn, iocmd->rpwwn, iocmd->lun);
+	else if (v_cmd == IOCMD_FCPIM_LUNMASK_DELETE)
+		iocmd->status = bfa_fcpim_lunmask_delete(&bfad->bfa,
+					iocmd->vf_id, &iocmd->pwwn,
+					iocmd->rpwwn, iocmd->lun);
+	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+	return 0;
+}
+
 static int
 bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd,
 		unsigned int payload_len)
 {
-	int rc = EINVAL;
+	int rc = -EINVAL;
 
 	switch (cmd) {
 	case IOCMD_IOC_ENABLE:
@@ -1536,6 +2469,14 @@
 	case IOCMD_IOC_GET_FWSTATS:
 		rc = bfad_iocmd_ioc_get_fwstats(bfad, iocmd, payload_len);
 		break;
+	case IOCMD_IOC_RESET_STATS:
+	case IOCMD_IOC_RESET_FWSTATS:
+		rc = bfad_iocmd_ioc_reset_stats(bfad, iocmd, cmd);
+		break;
+	case IOCMD_IOC_SET_ADAPTER_NAME:
+	case IOCMD_IOC_SET_PORT_NAME:
+		rc = bfad_iocmd_ioc_set_name(bfad, iocmd, cmd);
+		break;
 	case IOCMD_IOCFC_GET_ATTR:
 		rc = bfad_iocmd_iocfc_get_attr(bfad, iocmd);
 		break;
@@ -1554,12 +2495,31 @@
 	case IOCMD_PORT_GET_STATS:
 		rc = bfad_iocmd_port_get_stats(bfad, iocmd, payload_len);
 		break;
+	case IOCMD_PORT_RESET_STATS:
+		rc = bfad_iocmd_port_reset_stats(bfad, iocmd);
+		break;
+	case IOCMD_PORT_CFG_TOPO:
+	case IOCMD_PORT_CFG_SPEED:
+	case IOCMD_PORT_CFG_ALPA:
+	case IOCMD_PORT_CLR_ALPA:
+		rc = bfad_iocmd_set_port_cfg(bfad, iocmd, cmd);
+		break;
+	case IOCMD_PORT_CFG_MAXFRSZ:
+		rc = bfad_iocmd_port_cfg_maxfrsize(bfad, iocmd);
+		break;
+	case IOCMD_PORT_BBSC_ENABLE:
+	case IOCMD_PORT_BBSC_DISABLE:
+		rc = bfad_iocmd_port_cfg_bbsc(bfad, iocmd, cmd);
+		break;
 	case IOCMD_LPORT_GET_ATTR:
 		rc = bfad_iocmd_lport_get_attr(bfad, iocmd);
 		break;
 	case IOCMD_LPORT_GET_STATS:
 		rc = bfad_iocmd_lport_get_stats(bfad, iocmd);
 		break;
+	case IOCMD_LPORT_RESET_STATS:
+		rc = bfad_iocmd_lport_reset_stats(bfad, iocmd);
+		break;
 	case IOCMD_LPORT_GET_IOSTATS:
 		rc = bfad_iocmd_lport_get_iostats(bfad, iocmd);
 		break;
@@ -1575,12 +2535,40 @@
 	case IOCMD_RPORT_GET_STATS:
 		rc = bfad_iocmd_rport_get_stats(bfad, iocmd);
 		break;
+	case IOCMD_RPORT_RESET_STATS:
+		rc = bfad_iocmd_rport_clr_stats(bfad, iocmd);
+		break;
+	case IOCMD_RPORT_SET_SPEED:
+		rc = bfad_iocmd_rport_set_speed(bfad, iocmd);
+		break;
+	case IOCMD_VPORT_GET_ATTR:
+		rc = bfad_iocmd_vport_get_attr(bfad, iocmd);
+		break;
+	case IOCMD_VPORT_GET_STATS:
+		rc = bfad_iocmd_vport_get_stats(bfad, iocmd);
+		break;
+	case IOCMD_VPORT_RESET_STATS:
+		rc = bfad_iocmd_vport_clr_stats(bfad, iocmd);
+		break;
 	case IOCMD_FABRIC_GET_LPORTS:
 		rc = bfad_iocmd_fabric_get_lports(bfad, iocmd, payload_len);
 		break;
+	case IOCMD_RATELIM_ENABLE:
+	case IOCMD_RATELIM_DISABLE:
+		rc = bfad_iocmd_ratelim(bfad, cmd, iocmd);
+		break;
+	case IOCMD_RATELIM_DEF_SPEED:
+		rc = bfad_iocmd_ratelim_speed(bfad, cmd, iocmd);
+		break;
+	case IOCMD_FCPIM_FAILOVER:
+		rc = bfad_iocmd_cfg_fcpim(bfad, iocmd);
+		break;
 	case IOCMD_FCPIM_MODSTATS:
 		rc = bfad_iocmd_fcpim_get_modstats(bfad, iocmd);
 		break;
+	case IOCMD_FCPIM_MODSTATSCLR:
+		rc = bfad_iocmd_fcpim_clr_modstats(bfad, iocmd);
+		break;
 	case IOCMD_FCPIM_DEL_ITN_STATS:
 		rc = bfad_iocmd_fcpim_get_del_itn_stats(bfad, iocmd);
 		break;
@@ -1590,6 +2578,9 @@
 	case IOCMD_ITNIM_GET_IOSTATS:
 		rc = bfad_iocmd_itnim_get_iostats(bfad, iocmd);
 		break;
+	case IOCMD_ITNIM_RESET_STATS:
+		rc = bfad_iocmd_itnim_reset_stats(bfad, iocmd);
+		break;
 	case IOCMD_ITNIM_GET_ITNSTATS:
 		rc = bfad_iocmd_itnim_get_itnstats(bfad, iocmd);
 		break;
@@ -1702,11 +2693,92 @@
 	case IOCMD_DEBUG_PORTLOG:
 		rc = bfad_iocmd_porglog_get(bfad, iocmd);
 		break;
+	case IOCMD_DEBUG_FW_CORE:
+		rc = bfad_iocmd_debug_fw_core(bfad, iocmd, payload_len);
+		break;
+	case IOCMD_DEBUG_FW_STATE_CLR:
+	case IOCMD_DEBUG_PORTLOG_CLR:
+	case IOCMD_DEBUG_START_DTRC:
+	case IOCMD_DEBUG_STOP_DTRC:
+		rc = bfad_iocmd_debug_ctl(bfad, iocmd, cmd);
+		break;
+	case IOCMD_DEBUG_PORTLOG_CTL:
+		rc = bfad_iocmd_porglog_ctl(bfad, iocmd);
+		break;
+	case IOCMD_FCPIM_PROFILE_ON:
+	case IOCMD_FCPIM_PROFILE_OFF:
+		rc = bfad_iocmd_fcpim_cfg_profile(bfad, iocmd, cmd);
+		break;
+	case IOCMD_ITNIM_GET_IOPROFILE:
+		rc = bfad_iocmd_itnim_get_ioprofile(bfad, iocmd);
+		break;
+	case IOCMD_FCPORT_GET_STATS:
+		rc = bfad_iocmd_fcport_get_stats(bfad, iocmd);
+		break;
+	case IOCMD_FCPORT_RESET_STATS:
+		rc = bfad_iocmd_fcport_reset_stats(bfad, iocmd);
+		break;
+	case IOCMD_BOOT_CFG:
+		rc = bfad_iocmd_boot_cfg(bfad, iocmd);
+		break;
+	case IOCMD_BOOT_QUERY:
+		rc = bfad_iocmd_boot_query(bfad, iocmd);
+		break;
+	case IOCMD_PREBOOT_QUERY:
+		rc = bfad_iocmd_preboot_query(bfad, iocmd);
+		break;
+	case IOCMD_ETHBOOT_CFG:
+		rc = bfad_iocmd_ethboot_cfg(bfad, iocmd);
+		break;
+	case IOCMD_ETHBOOT_QUERY:
+		rc = bfad_iocmd_ethboot_query(bfad, iocmd);
+		break;
+	case IOCMD_TRUNK_ENABLE:
+	case IOCMD_TRUNK_DISABLE:
+		rc = bfad_iocmd_cfg_trunk(bfad, iocmd, cmd);
+		break;
+	case IOCMD_TRUNK_GET_ATTR:
+		rc = bfad_iocmd_trunk_get_attr(bfad, iocmd);
+		break;
+	case IOCMD_QOS_ENABLE:
+	case IOCMD_QOS_DISABLE:
+		rc = bfad_iocmd_qos(bfad, iocmd, cmd);
+		break;
+	case IOCMD_QOS_GET_ATTR:
+		rc = bfad_iocmd_qos_get_attr(bfad, iocmd);
+		break;
+	case IOCMD_QOS_GET_VC_ATTR:
+		rc = bfad_iocmd_qos_get_vc_attr(bfad, iocmd);
+		break;
+	case IOCMD_QOS_GET_STATS:
+		rc = bfad_iocmd_qos_get_stats(bfad, iocmd);
+		break;
+	case IOCMD_QOS_RESET_STATS:
+		rc = bfad_iocmd_qos_reset_stats(bfad, iocmd);
+		break;
+	case IOCMD_VF_GET_STATS:
+		rc = bfad_iocmd_vf_get_stats(bfad, iocmd);
+		break;
+	case IOCMD_VF_RESET_STATS:
+		rc = bfad_iocmd_vf_clr_stats(bfad, iocmd);
+		break;
+	case IOCMD_FCPIM_LUNMASK_ENABLE:
+	case IOCMD_FCPIM_LUNMASK_DISABLE:
+	case IOCMD_FCPIM_LUNMASK_CLEAR:
+		rc = bfad_iocmd_lunmask(bfad, iocmd, cmd);
+		break;
+	case IOCMD_FCPIM_LUNMASK_QUERY:
+		rc = bfad_iocmd_fcpim_lunmask_query(bfad, iocmd);
+		break;
+	case IOCMD_FCPIM_LUNMASK_ADD:
+	case IOCMD_FCPIM_LUNMASK_DELETE:
+		rc = bfad_iocmd_fcpim_cfg_lunmask(bfad, iocmd, cmd);
+		break;
 	default:
-		rc = EINVAL;
+		rc = -EINVAL;
 		break;
 	}
-	return -rc;
+	return rc;
 }
 
 static int
diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h
index 99b0e8a..e859adb 100644
--- a/drivers/scsi/bfa/bfad_bsg.h
+++ b/drivers/scsi/bfa/bfad_bsg.h
@@ -30,24 +30,48 @@
 	IOCMD_IOC_GET_INFO,
 	IOCMD_IOC_GET_STATS,
 	IOCMD_IOC_GET_FWSTATS,
+	IOCMD_IOC_RESET_STATS,
+	IOCMD_IOC_RESET_FWSTATS,
+	IOCMD_IOC_SET_ADAPTER_NAME,
+	IOCMD_IOC_SET_PORT_NAME,
 	IOCMD_IOCFC_GET_ATTR,
 	IOCMD_IOCFC_SET_INTR,
 	IOCMD_PORT_ENABLE,
 	IOCMD_PORT_DISABLE,
 	IOCMD_PORT_GET_ATTR,
 	IOCMD_PORT_GET_STATS,
+	IOCMD_PORT_RESET_STATS,
+	IOCMD_PORT_CFG_TOPO,
+	IOCMD_PORT_CFG_SPEED,
+	IOCMD_PORT_CFG_ALPA,
+	IOCMD_PORT_CFG_MAXFRSZ,
+	IOCMD_PORT_CLR_ALPA,
+	IOCMD_PORT_BBSC_ENABLE,
+	IOCMD_PORT_BBSC_DISABLE,
 	IOCMD_LPORT_GET_ATTR,
 	IOCMD_LPORT_GET_RPORTS,
 	IOCMD_LPORT_GET_STATS,
+	IOCMD_LPORT_RESET_STATS,
 	IOCMD_LPORT_GET_IOSTATS,
 	IOCMD_RPORT_GET_ATTR,
 	IOCMD_RPORT_GET_ADDR,
 	IOCMD_RPORT_GET_STATS,
+	IOCMD_RPORT_RESET_STATS,
+	IOCMD_RPORT_SET_SPEED,
+	IOCMD_VPORT_GET_ATTR,
+	IOCMD_VPORT_GET_STATS,
+	IOCMD_VPORT_RESET_STATS,
 	IOCMD_FABRIC_GET_LPORTS,
+	IOCMD_RATELIM_ENABLE,
+	IOCMD_RATELIM_DISABLE,
+	IOCMD_RATELIM_DEF_SPEED,
+	IOCMD_FCPIM_FAILOVER,
 	IOCMD_FCPIM_MODSTATS,
+	IOCMD_FCPIM_MODSTATSCLR,
 	IOCMD_FCPIM_DEL_ITN_STATS,
 	IOCMD_ITNIM_GET_ATTR,
 	IOCMD_ITNIM_GET_IOSTATS,
+	IOCMD_ITNIM_RESET_STATS,
 	IOCMD_ITNIM_GET_ITNSTATS,
 	IOCMD_IOC_PCIFN_CFG,
 	IOCMD_FCPORT_ENABLE,
@@ -86,6 +110,39 @@
 	IOCMD_PHY_READ_FW,
 	IOCMD_VHBA_QUERY,
 	IOCMD_DEBUG_PORTLOG,
+	IOCMD_DEBUG_FW_CORE,
+	IOCMD_DEBUG_FW_STATE_CLR,
+	IOCMD_DEBUG_PORTLOG_CLR,
+	IOCMD_DEBUG_START_DTRC,
+	IOCMD_DEBUG_STOP_DTRC,
+	IOCMD_DEBUG_PORTLOG_CTL,
+	IOCMD_FCPIM_PROFILE_ON,
+	IOCMD_FCPIM_PROFILE_OFF,
+	IOCMD_ITNIM_GET_IOPROFILE,
+	IOCMD_FCPORT_GET_STATS,
+	IOCMD_FCPORT_RESET_STATS,
+	IOCMD_BOOT_CFG,
+	IOCMD_BOOT_QUERY,
+	IOCMD_PREBOOT_QUERY,
+	IOCMD_ETHBOOT_CFG,
+	IOCMD_ETHBOOT_QUERY,
+	IOCMD_TRUNK_ENABLE,
+	IOCMD_TRUNK_DISABLE,
+	IOCMD_TRUNK_GET_ATTR,
+	IOCMD_QOS_ENABLE,
+	IOCMD_QOS_DISABLE,
+	IOCMD_QOS_GET_ATTR,
+	IOCMD_QOS_GET_VC_ATTR,
+	IOCMD_QOS_GET_STATS,
+	IOCMD_QOS_RESET_STATS,
+	IOCMD_VF_GET_STATS,
+	IOCMD_VF_RESET_STATS,
+	IOCMD_FCPIM_LUNMASK_ENABLE,
+	IOCMD_FCPIM_LUNMASK_DISABLE,
+	IOCMD_FCPIM_LUNMASK_CLEAR,
+	IOCMD_FCPIM_LUNMASK_QUERY,
+	IOCMD_FCPIM_LUNMASK_ADD,
+	IOCMD_FCPIM_LUNMASK_DELETE,
 };
 
 struct bfa_bsg_gen_s {
@@ -94,6 +151,43 @@
 	u16		rsvd;
 };
 
+struct bfa_bsg_portlogctl_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	bfa_boolean_t	ctl;
+	int		inst_no;
+};
+
+struct bfa_bsg_fcpim_profile_s {
+	bfa_status_t    status;
+	u16		bfad_num;
+	u16		rsvd;
+};
+
+struct bfa_bsg_itnim_ioprofile_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		lpwwn;
+	wwn_t		rpwwn;
+	struct bfa_itnim_ioprofile_s ioprofile;
+};
+
+struct bfa_bsg_fcport_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	union bfa_fcport_stats_u stats;
+};
+
+struct bfa_bsg_ioc_name_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	char		name[BFA_ADAPTER_SYM_NAME_LEN];
+};
+
 struct bfa_bsg_ioc_info_s {
 	bfa_status_t	status;
 	u16		bfad_num;
@@ -164,6 +258,20 @@
 	struct bfa_port_attr_s	attr;
 };
 
+struct bfa_bsg_port_cfg_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	u32		param;
+	u32		rsvd1;
+};
+
+struct bfa_bsg_port_cfg_maxfrsize_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		maxfrsize;
+};
+
 struct bfa_bsg_port_stats_s {
 	bfa_status_t	status;
 	u16		bfad_num;
@@ -237,6 +345,47 @@
 	u32		lun;
 };
 
+struct bfa_bsg_rport_reset_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		pwwn;
+	wwn_t		rpwwn;
+};
+
+struct bfa_bsg_rport_set_speed_s {
+	bfa_status_t		status;
+	u16			bfad_num;
+	u16			vf_id;
+	enum bfa_port_speed	speed;
+	u32			rsvd;
+	wwn_t			pwwn;
+	wwn_t			rpwwn;
+};
+
+struct bfa_bsg_vport_attr_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		vpwwn;
+	struct bfa_vport_attr_s vport_attr;
+};
+
+struct bfa_bsg_vport_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		vpwwn;
+	struct bfa_vport_stats_s vport_stats;
+};
+
+struct bfa_bsg_reset_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		vpwwn;
+};
+
 struct bfa_bsg_fabric_get_lports_s {
 	bfa_status_t	status;
 	u16		bfad_num;
@@ -246,6 +395,19 @@
 	u32		rsvd;
 };
 
+struct bfa_bsg_trl_speed_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	enum bfa_port_speed speed;
+};
+
+struct bfa_bsg_fcpim_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		param;
+};
+
 struct bfa_bsg_fcpim_modstats_s {
 	bfa_status_t	status;
 	u16		bfad_num;
@@ -258,6 +420,11 @@
 	struct bfa_fcpim_del_itn_stats_s modstats;
 };
 
+struct bfa_bsg_fcpim_modstatsclr_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+};
+
 struct bfa_bsg_itnim_attr_s {
 	bfa_status_t	status;
 	u16		bfad_num;
@@ -485,6 +652,76 @@
 	struct bfa_vhba_attr_s	attr;
 };
 
+struct bfa_bsg_boot_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct bfa_boot_cfg_s	cfg;
+};
+
+struct bfa_bsg_preboot_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct bfa_boot_pbc_s	cfg;
+};
+
+struct bfa_bsg_ethboot_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct  bfa_ethboot_cfg_s  cfg;
+};
+
+struct bfa_bsg_trunk_attr_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct bfa_trunk_attr_s attr;
+};
+
+struct bfa_bsg_qos_attr_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct bfa_qos_attr_s	attr;
+};
+
+struct bfa_bsg_qos_vc_attr_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		rsvd;
+	struct bfa_qos_vc_attr_s attr;
+};
+
+struct bfa_bsg_vf_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	struct bfa_vf_stats_s	stats;
+};
+
+struct bfa_bsg_vf_reset_stats_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+};
+
+struct bfa_bsg_fcpim_lunmask_query_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	struct bfa_lunmask_cfg_s lun_mask;
+};
+
+struct bfa_bsg_fcpim_lunmask_s {
+	bfa_status_t	status;
+	u16		bfad_num;
+	u16		vf_id;
+	wwn_t		pwwn;
+	wwn_t		rpwwn;
+	struct scsi_lun	lun;
+};
+
 struct bfa_bsg_fcpt_s {
 	bfa_status_t    status;
 	u16		vf_id;
diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h
index 48661a2..bda999a 100644
--- a/drivers/scsi/bfa/bfad_drv.h
+++ b/drivers/scsi/bfa/bfad_drv.h
@@ -56,7 +56,7 @@
 #ifdef BFA_DRIVER_VERSION
 #define BFAD_DRIVER_VERSION    BFA_DRIVER_VERSION
 #else
-#define BFAD_DRIVER_VERSION    "3.0.2.1"
+#define BFAD_DRIVER_VERSION    "3.0.2.2"
 #endif
 
 #define BFAD_PROTO_NAME FCPI_NAME
@@ -224,6 +224,10 @@
 	char *regdata;
 	u32 reglen;
 	struct dentry *bfad_dentry_files[5];
+	struct list_head	free_aen_q;
+	struct list_head	active_aen_q;
+	struct bfa_aen_entry_s	aen_list[BFA_AEN_MAX_ENTRY];
+	spinlock_t		bfad_aen_spinlock;
 };
 
 /* BFAD state machine events */
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index f2bf812..0131238 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -656,6 +656,31 @@
 	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
 }
 
+static void bfad_aen_im_notify_handler(struct work_struct *work)
+{
+	struct bfad_im_s *im =
+		container_of(work, struct bfad_im_s, aen_im_notify_work);
+	struct bfa_aen_entry_s *aen_entry;
+	struct bfad_s *bfad = im->bfad;
+	struct Scsi_Host *shost = bfad->pport.im_port->shost;
+	void *event_data;
+	unsigned long flags;
+
+	while (!list_empty(&bfad->active_aen_q)) {
+		spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags);
+		bfa_q_deq(&bfad->active_aen_q, &aen_entry);
+		spin_unlock_irqrestore(&bfad->bfad_aen_spinlock, flags);
+		event_data = (char *)aen_entry + sizeof(struct list_head);
+		fc_host_post_vendor_event(shost, fc_get_event_number(),
+				sizeof(struct bfa_aen_entry_s) -
+				sizeof(struct list_head),
+				(char *)event_data, BFAD_NL_VENDOR_ID);
+		spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags);
+		list_add_tail(&aen_entry->qe, &bfad->free_aen_q);
+		spin_unlock_irqrestore(&bfad->bfad_aen_spinlock, flags);
+	}
+}
+
 bfa_status_t
 bfad_im_probe(struct bfad_s *bfad)
 {
@@ -676,6 +701,7 @@
 		rc = BFA_STATUS_FAILED;
 	}
 
+	INIT_WORK(&im->aen_im_notify_work, bfad_aen_im_notify_handler);
 ext:
 	return rc;
 }
diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h
index 4fe34d5..004b6cf 100644
--- a/drivers/scsi/bfa/bfad_im.h
+++ b/drivers/scsi/bfa/bfad_im.h
@@ -115,8 +115,30 @@
 	struct bfad_s         *bfad;
 	struct workqueue_struct *drv_workq;
 	char            drv_workq_name[KOBJ_NAME_LEN];
+	struct work_struct	aen_im_notify_work;
 };
 
+#define bfad_get_aen_entry(_drv, _entry) do {				\
+	unsigned long	_flags;						\
+	spin_lock_irqsave(&(_drv)->bfad_aen_spinlock, _flags);		\
+	bfa_q_deq(&(_drv)->free_aen_q, &(_entry));			\
+	if (_entry)							\
+		list_add_tail(&(_entry)->qe, &(_drv)->active_aen_q);	\
+	spin_unlock_irqrestore(&(_drv)->bfad_aen_spinlock, _flags);	\
+} while (0)
+
+/* post fc_host vendor event */
+#define bfad_im_post_vendor_event(_entry, _drv, _cnt, _cat, _evt) do {	      \
+	do_gettimeofday(&(_entry)->aen_tv);				      \
+	(_entry)->bfad_num = (_drv)->inst_no;				      \
+	(_entry)->seq_num = (_cnt);					      \
+	(_entry)->aen_category = (_cat);				      \
+	(_entry)->aen_type = (_evt);					      \
+	if ((_drv)->bfad_flags & BFAD_FC4_PROBE_DONE)			      \
+		queue_work((_drv)->im->drv_workq,			      \
+			   &(_drv)->im->aen_im_notify_work);		      \
+} while (0)
+
 struct Scsi_Host *bfad_scsi_host_alloc(struct bfad_im_port_s *im_port,
 				struct bfad_s *);
 bfa_status_t bfad_thread_workq(struct bfad_s *bfad);
diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h
index 1e258d5..b2ba0b2 100644
--- a/drivers/scsi/bfa/bfi.h
+++ b/drivers/scsi/bfa/bfi.h
@@ -784,6 +784,17 @@
 };
 
 /*
+ *	SFP state change notification
+ */
+struct bfi_sfp_scn_s {
+	struct bfi_mhdr_s mhr;	/* host msg header        */
+	u8	event;
+	u8	sfpid;
+	u8	pomlvl;	/* pom level: normal/warning/alarm */
+	u8	is_elb;	/* e-loopback */
+};
+
+/*
  *	SFP state
  */
 enum bfa_sfp_stat_e {
@@ -926,6 +937,15 @@
 };
 
 /*
+ * Flash event notification
+ */
+struct bfi_flash_event_s {
+	struct bfi_mhdr_s	mh;	/* Common msg header */
+	bfa_status_t		status;
+	u32			param;
+};
+
+/*
  *----------------------------------------------------------------------
  *				DIAG
  *----------------------------------------------------------------------
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index d924236..42228ca 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -2,7 +2,7 @@
 #define _BNX2FC_H_
 /* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -62,7 +62,7 @@
 #include "bnx2fc_constants.h"
 
 #define BNX2FC_NAME		"bnx2fc"
-#define BNX2FC_VERSION		"1.0.3"
+#define BNX2FC_VERSION		"1.0.4"
 
 #define PFX			"bnx2fc: "
 
@@ -141,6 +141,10 @@
 
 #define BNX2FC_RNID_HBA			0x7
 
+#define SRR_RETRY_COUNT			5
+#define REC_RETRY_COUNT			1
+#define BNX2FC_NUM_ERR_BITS		63
+
 /* bnx2fc driver uses only one instance of fcoe_percpu_s */
 extern struct fcoe_percpu_s bnx2fc_global;
 
@@ -153,18 +157,13 @@
 };
 
 struct bnx2fc_hba {
-	struct list_head link;
+	struct list_head list;
 	struct cnic_dev *cnic;
 	struct pci_dev *pcidev;
-	struct net_device *netdev;
 	struct net_device *phys_dev;
 	unsigned long reg_with_cnic;
 		#define BNX2FC_CNIC_REGISTERED           1
-	struct packet_type fcoe_packet_type;
-	struct packet_type fip_packet_type;
 	struct bnx2fc_cmd_mgr *cmd_mgr;
-	struct workqueue_struct *timer_work_queue;
-	struct kref kref;
 	spinlock_t hba_lock;
 	struct mutex hba_mutex;
 	unsigned long adapter_state;
@@ -172,15 +171,9 @@
 		#define ADAPTER_STATE_GOING_DOWN	1
 		#define ADAPTER_STATE_LINK_DOWN		2
 		#define ADAPTER_STATE_READY		3
-	u32 flags;
-	unsigned long init_done;
-		#define BNX2FC_FW_INIT_DONE		0
-		#define BNX2FC_CTLR_INIT_DONE		1
-		#define BNX2FC_CREATE_DONE		2
-	struct fcoe_ctlr ctlr;
-	struct list_head vports;
-	u8 vlan_enabled;
-	int vlan_id;
+	unsigned long flags;
+		#define BNX2FC_FLAG_FW_INIT_DONE	0
+		#define BNX2FC_FLAG_DESTROY_CMPL	1
 	u32 next_conn_id;
 	struct fcoe_task_ctx_entry **task_ctx;
 	dma_addr_t *task_ctx_dma;
@@ -199,38 +192,41 @@
 	char *dummy_buffer;
 	dma_addr_t dummy_buf_dma;
 
+	/* Active list of offloaded sessions */
+	struct bnx2fc_rport **tgt_ofld_list;
+
+	/* statistics */
 	struct fcoe_statistics_params *stats_buffer;
 	dma_addr_t stats_buf_dma;
-
-	/*
-	 * PCI related info.
-	 */
-	u16 pci_did;
-	u16 pci_vid;
-	u16 pci_sdid;
-	u16 pci_svid;
-	u16 pci_func;
-	u16 pci_devno;
-
-	struct task_struct *l2_thread;
-
-	/* linkdown handling */
-	wait_queue_head_t shutdown_wait;
-	int wait_for_link_down;
+	struct completion stat_req_done;
 
 	/*destroy handling */
 	struct timer_list destroy_timer;
 	wait_queue_head_t destroy_wait;
 
-	/* Active list of offloaded sessions */
-	struct bnx2fc_rport *tgt_ofld_list[BNX2FC_NUM_MAX_SESS];
+	/* linkdown handling */
+	wait_queue_head_t shutdown_wait;
+	int wait_for_link_down;
 	int num_ofld_sess;
-
-	/* statistics */
-	struct completion stat_req_done;
+	struct list_head vports;
 };
 
-#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr)
+struct bnx2fc_interface {
+	struct list_head list;
+	unsigned long if_flags;
+		#define BNX2FC_CTLR_INIT_DONE		0
+	struct bnx2fc_hba *hba;
+	struct net_device *netdev;
+	struct packet_type fcoe_packet_type;
+	struct packet_type fip_packet_type;
+	struct workqueue_struct *timer_work_queue;
+	struct kref kref;
+	struct fcoe_ctlr ctlr;
+	u8 vlan_enabled;
+	int vlan_id;
+};
+
+#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_interface, ctlr)
 
 struct bnx2fc_lport {
 	struct list_head list;
@@ -252,9 +248,11 @@
 	struct fc_rport_priv *rdata;
 	void __iomem *ctx_base;
 #define DPM_TRIGER_TYPE		0x40
+	u32 io_timeout;
 	u32 fcoe_conn_id;
 	u32 context_id;
 	u32 sid;
+	int dev_type;
 
 	unsigned long flags;
 #define BNX2FC_FLAG_SESSION_READY	0x1
@@ -262,10 +260,9 @@
 #define BNX2FC_FLAG_DISABLED		0x3
 #define BNX2FC_FLAG_DESTROYED		0x4
 #define BNX2FC_FLAG_OFLD_REQ_CMPL	0x5
-#define BNX2FC_FLAG_DESTROY_CMPL	0x6
-#define BNX2FC_FLAG_CTX_ALLOC_FAILURE	0x7
-#define BNX2FC_FLAG_UPLD_REQ_COMPL	0x8
-#define BNX2FC_FLAG_EXPL_LOGO		0x9
+#define BNX2FC_FLAG_CTX_ALLOC_FAILURE	0x6
+#define BNX2FC_FLAG_UPLD_REQ_COMPL	0x7
+#define BNX2FC_FLAG_EXPL_LOGO		0x8
 
 	u8 src_addr[ETH_ALEN];
 	u32 max_sqes;
@@ -327,12 +324,9 @@
 	spinlock_t cq_lock;
 	atomic_t num_active_ios;
 	u32 flush_in_prog;
-	unsigned long work_time_slice;
 	unsigned long timestamp;
 	struct list_head free_task_list;
 	struct bnx2fc_cmd *pending_queue[BNX2FC_SQ_WQES_MAX+1];
-	atomic_t pi;
-	atomic_t ci;
 	struct list_head active_cmd_queue;
 	struct list_head els_queue;
 	struct list_head io_retire_queue;
@@ -367,6 +361,8 @@
 	struct bnx2fc_cmd *aborted_io_req;
 	struct bnx2fc_cmd *io_req;
 	u16 l2_oxid;
+	u32 offset;
+	enum fc_rctl r_ctl;
 };
 
 /* bnx2fc command structure */
@@ -380,6 +376,7 @@
 #define BNX2FC_ABTS			3
 #define BNX2FC_ELS			4
 #define BNX2FC_CLEANUP			5
+#define BNX2FC_SEQ_CLEANUP		6
 	u8 io_req_flags;
 	struct kref refcount;
 	struct fcoe_port *port;
@@ -393,6 +390,7 @@
 	struct completion tm_done;
 	int wait_for_comp;
 	u16 xid;
+	struct fcoe_err_report_entry err_entry;
 	struct fcoe_task_ctx_entry *task;
 	struct io_bdt *bd_tbl;
 	struct fcp_rsp *rsp;
@@ -409,6 +407,12 @@
 #define BNX2FC_FLAG_IO_COMPL		0x9
 #define BNX2FC_FLAG_ELS_DONE		0xa
 #define BNX2FC_FLAG_ELS_TIMEOUT		0xb
+#define BNX2FC_FLAG_CMD_LOST		0xc
+#define BNX2FC_FLAG_SRR_SENT		0xd
+	u8 rec_retry;
+	u8 srr_retry;
+	u32 srr_offset;
+	u8 srr_rctl;
 	u32 fcp_resid;
 	u32 fcp_rsp_len;
 	u32 fcp_sns_len;
@@ -439,6 +443,7 @@
 
 
 
+struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt);
 struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type);
 void bnx2fc_cmd_release(struct kref *ref);
 int bnx2fc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd);
@@ -476,6 +481,10 @@
 void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
 			      struct fcoe_task_ctx_entry *task,
 			      u16 orig_xid);
+void bnx2fc_init_seq_cleanup_task(struct bnx2fc_cmd *seq_clnup_req,
+				  struct fcoe_task_ctx_entry *task,
+				  struct bnx2fc_cmd *orig_io_req,
+				  u32 offset);
 void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
 			 struct fcoe_task_ctx_entry *task);
 void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
@@ -525,5 +534,13 @@
 				   unsigned char *buf,
 				   u32 frame_len, u16 l2_oxid);
 int bnx2fc_send_stat_req(struct bnx2fc_hba *hba);
+int bnx2fc_post_io_req(struct bnx2fc_rport *tgt, struct bnx2fc_cmd *io_req);
+int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req);
+int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl);
+void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnup_req,
+				      struct fcoe_task_ctx_entry *task,
+				      u8 rx_state);
+int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset,
+				enum fc_rctl r_ctl);
 
 #endif
diff --git a/drivers/scsi/bnx2fc/bnx2fc_debug.h b/drivers/scsi/bnx2fc/bnx2fc_debug.h
index 7f6aff6..3416d9a 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_debug.h
+++ b/drivers/scsi/bnx2fc/bnx2fc_debug.h
@@ -21,21 +21,21 @@
 
 #define BNX2FC_ELS_DBG(fmt, arg...)					\
 	BNX2FC_CHK_LOGGING(LOG_ELS,					\
-			   printk(KERN_ALERT PFX fmt, ##arg))
+			   printk(KERN_INFO PFX fmt, ##arg))
 
 #define BNX2FC_MISC_DBG(fmt, arg...)					\
 	BNX2FC_CHK_LOGGING(LOG_MISC,					\
-			   printk(KERN_ALERT PFX fmt, ##arg))
+			   printk(KERN_INFO PFX fmt, ##arg))
 
 #define BNX2FC_IO_DBG(io_req, fmt, arg...)				\
 	do {								\
 		if (!io_req || !io_req->port || !io_req->port->lport ||	\
 		    !io_req->port->lport->host)				\
 			BNX2FC_CHK_LOGGING(LOG_IO,			\
-			   printk(KERN_ALERT PFX "NULL " fmt, ##arg));	\
+			   printk(KERN_INFO PFX "NULL " fmt, ##arg));	\
 		else							\
 			BNX2FC_CHK_LOGGING(LOG_IO,			\
-			   shost_printk(KERN_ALERT,			\
+			   shost_printk(KERN_INFO,			\
 				   (io_req)->port->lport->host,		\
 				   PFX "xid:0x%x " fmt,			\
 				   (io_req)->xid, ##arg));		\
@@ -46,10 +46,10 @@
 		if (!tgt || !tgt->port || !tgt->port->lport ||		\
 		    !tgt->port->lport->host || !tgt->rport)		\
 			BNX2FC_CHK_LOGGING(LOG_TGT,			\
-			   printk(KERN_ALERT PFX "NULL " fmt, ##arg));	\
+			   printk(KERN_INFO PFX "NULL " fmt, ##arg));	\
 		else							\
 			BNX2FC_CHK_LOGGING(LOG_TGT,			\
-			   shost_printk(KERN_ALERT,			\
+			   shost_printk(KERN_INFO,			\
 				   (tgt)->port->lport->host,		\
 				   PFX "port:%x " fmt,			\
 				   (tgt)->rport->port_id, ##arg));	\
@@ -60,10 +60,10 @@
 	do {								\
 		if (!lport || !lport->host)				\
 			BNX2FC_CHK_LOGGING(LOG_HBA,			\
-			   printk(KERN_ALERT PFX "NULL " fmt, ##arg));	\
+			   printk(KERN_INFO PFX "NULL " fmt, ##arg));	\
 		else							\
 			BNX2FC_CHK_LOGGING(LOG_HBA,			\
-			   shost_printk(KERN_ALERT, lport->host,	\
+			   shost_printk(KERN_INFO, lport->host,	\
 				   PFX fmt, ##arg));			\
 	} while (0)
 
diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c
index 7e89143..d66dcbd 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_els.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_els.c
@@ -3,7 +3,7 @@
  * This file contains helper routines that handle ELS requests
  * and responses.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -253,13 +253,417 @@
 	return rc;
 }
 
+void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg)
+{
+	struct bnx2fc_mp_req *mp_req;
+	struct fc_frame_header *fc_hdr, *fh;
+	struct bnx2fc_cmd *srr_req;
+	struct bnx2fc_cmd *orig_io_req;
+	struct fc_frame *fp;
+	unsigned char *buf;
+	void *resp_buf;
+	u32 resp_len, hdr_len;
+	u8 opcode;
+	int rc = 0;
+
+	orig_io_req = cb_arg->aborted_io_req;
+	srr_req = cb_arg->io_req;
+	if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) {
+		BNX2FC_IO_DBG(srr_req, "srr_compl: xid - 0x%x completed",
+			orig_io_req->xid);
+		goto srr_compl_done;
+	}
+	if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) {
+		BNX2FC_IO_DBG(srr_req, "rec abts in prog "
+		       "orig_io - 0x%x\n",
+			orig_io_req->xid);
+		goto srr_compl_done;
+	}
+	if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) {
+		/* SRR timedout */
+		BNX2FC_IO_DBG(srr_req, "srr timed out, abort "
+		       "orig_io - 0x%x\n",
+			orig_io_req->xid);
+		rc = bnx2fc_initiate_abts(srr_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+				"failed. issue cleanup\n");
+			bnx2fc_initiate_cleanup(srr_req);
+		}
+		orig_io_req->srr_retry++;
+		if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) {
+			struct bnx2fc_rport *tgt = orig_io_req->tgt;
+			spin_unlock_bh(&tgt->tgt_lock);
+			rc = bnx2fc_send_srr(orig_io_req,
+					     orig_io_req->srr_offset,
+					     orig_io_req->srr_rctl);
+			spin_lock_bh(&tgt->tgt_lock);
+			if (!rc)
+				goto srr_compl_done;
+		}
+
+		rc = bnx2fc_initiate_abts(orig_io_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+				"failed xid = 0x%x. issue cleanup\n",
+				orig_io_req->xid);
+			bnx2fc_initiate_cleanup(orig_io_req);
+		}
+		goto srr_compl_done;
+	}
+	mp_req = &(srr_req->mp_req);
+	fc_hdr = &(mp_req->resp_fc_hdr);
+	resp_len = mp_req->resp_len;
+	resp_buf = mp_req->resp_buf;
+
+	hdr_len = sizeof(*fc_hdr);
+	buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+	if (!buf) {
+		printk(KERN_ERR PFX "srr buf: mem alloc failure\n");
+		goto srr_compl_done;
+	}
+	memcpy(buf, fc_hdr, hdr_len);
+	memcpy(buf + hdr_len, resp_buf, resp_len);
+
+	fp = fc_frame_alloc(NULL, resp_len);
+	if (!fp) {
+		printk(KERN_ERR PFX "fc_frame_alloc failure\n");
+		goto free_buf;
+	}
+
+	fh = (struct fc_frame_header *) fc_frame_header_get(fp);
+	/* Copy FC Frame header and payload into the frame */
+	memcpy(fh, buf, hdr_len + resp_len);
+
+	opcode = fc_frame_payload_op(fp);
+	switch (opcode) {
+	case ELS_LS_ACC:
+		BNX2FC_IO_DBG(srr_req, "SRR success\n");
+		break;
+	case ELS_LS_RJT:
+		BNX2FC_IO_DBG(srr_req, "SRR rejected\n");
+		rc = bnx2fc_initiate_abts(orig_io_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+				"failed xid = 0x%x. issue cleanup\n",
+				orig_io_req->xid);
+			bnx2fc_initiate_cleanup(orig_io_req);
+		}
+		break;
+	default:
+		BNX2FC_IO_DBG(srr_req, "srr compl - invalid opcode = %d\n",
+			opcode);
+		break;
+	}
+	fc_frame_free(fp);
+free_buf:
+	kfree(buf);
+srr_compl_done:
+	kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+}
+
+void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg)
+{
+	struct bnx2fc_cmd *orig_io_req, *new_io_req;
+	struct bnx2fc_cmd *rec_req;
+	struct bnx2fc_mp_req *mp_req;
+	struct fc_frame_header *fc_hdr, *fh;
+	struct fc_els_ls_rjt *rjt;
+	struct fc_els_rec_acc *acc;
+	struct bnx2fc_rport *tgt;
+	struct fcoe_err_report_entry *err_entry;
+	struct scsi_cmnd *sc_cmd;
+	enum fc_rctl r_ctl;
+	unsigned char *buf;
+	void *resp_buf;
+	struct fc_frame *fp;
+	u8 opcode;
+	u32 offset;
+	u32 e_stat;
+	u32 resp_len, hdr_len;
+	int rc = 0;
+	bool send_seq_clnp = false;
+	bool abort_io = false;
+
+	BNX2FC_MISC_DBG("Entered rec_compl callback\n");
+	rec_req = cb_arg->io_req;
+	orig_io_req = cb_arg->aborted_io_req;
+	BNX2FC_IO_DBG(rec_req, "rec_compl: orig xid = 0x%x", orig_io_req->xid);
+	tgt = orig_io_req->tgt;
+
+	if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) {
+		BNX2FC_IO_DBG(rec_req, "completed"
+		       "orig_io - 0x%x\n",
+			orig_io_req->xid);
+		goto rec_compl_done;
+	}
+	if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) {
+		BNX2FC_IO_DBG(rec_req, "abts in prog "
+		       "orig_io - 0x%x\n",
+			orig_io_req->xid);
+		goto rec_compl_done;
+	}
+	/* Handle REC timeout case */
+	if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rec_req->req_flags)) {
+		BNX2FC_IO_DBG(rec_req, "timed out, abort "
+		       "orig_io - 0x%x\n",
+			orig_io_req->xid);
+		/* els req is timed out. send abts for els */
+		rc = bnx2fc_initiate_abts(rec_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+				"failed. issue cleanup\n");
+			bnx2fc_initiate_cleanup(rec_req);
+		}
+		orig_io_req->rec_retry++;
+		/* REC timedout. send ABTS to the orig IO req */
+		if (orig_io_req->rec_retry <= REC_RETRY_COUNT) {
+			spin_unlock_bh(&tgt->tgt_lock);
+			rc = bnx2fc_send_rec(orig_io_req);
+			spin_lock_bh(&tgt->tgt_lock);
+			if (!rc)
+				goto rec_compl_done;
+		}
+		rc = bnx2fc_initiate_abts(orig_io_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+				"failed xid = 0x%x. issue cleanup\n",
+				orig_io_req->xid);
+			bnx2fc_initiate_cleanup(orig_io_req);
+		}
+		goto rec_compl_done;
+	}
+	mp_req = &(rec_req->mp_req);
+	fc_hdr = &(mp_req->resp_fc_hdr);
+	resp_len = mp_req->resp_len;
+	acc = resp_buf = mp_req->resp_buf;
+
+	hdr_len = sizeof(*fc_hdr);
+
+	buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+	if (!buf) {
+		printk(KERN_ERR PFX "rec buf: mem alloc failure\n");
+		goto rec_compl_done;
+	}
+	memcpy(buf, fc_hdr, hdr_len);
+	memcpy(buf + hdr_len, resp_buf, resp_len);
+
+	fp = fc_frame_alloc(NULL, resp_len);
+	if (!fp) {
+		printk(KERN_ERR PFX "fc_frame_alloc failure\n");
+		goto free_buf;
+	}
+
+	fh = (struct fc_frame_header *) fc_frame_header_get(fp);
+	/* Copy FC Frame header and payload into the frame */
+	memcpy(fh, buf, hdr_len + resp_len);
+
+	opcode = fc_frame_payload_op(fp);
+	if (opcode == ELS_LS_RJT) {
+		BNX2FC_IO_DBG(rec_req, "opcode is RJT\n");
+		rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+		if ((rjt->er_reason == ELS_RJT_LOGIC ||
+		    rjt->er_reason == ELS_RJT_UNAB) &&
+		    rjt->er_explan == ELS_EXPL_OXID_RXID) {
+			BNX2FC_IO_DBG(rec_req, "handle CMD LOST case\n");
+			new_io_req = bnx2fc_cmd_alloc(tgt);
+			if (!new_io_req)
+				goto abort_io;
+			new_io_req->sc_cmd = orig_io_req->sc_cmd;
+			/* cleanup orig_io_req that is with the FW */
+			set_bit(BNX2FC_FLAG_CMD_LOST,
+				&orig_io_req->req_flags);
+			bnx2fc_initiate_cleanup(orig_io_req);
+			/* Post a new IO req with the same sc_cmd */
+			BNX2FC_IO_DBG(rec_req, "Post IO request again\n");
+			spin_unlock_bh(&tgt->tgt_lock);
+			rc = bnx2fc_post_io_req(tgt, new_io_req);
+			spin_lock_bh(&tgt->tgt_lock);
+			if (!rc)
+				goto free_frame;
+			BNX2FC_IO_DBG(rec_req, "REC: io post err\n");
+		}
+abort_io:
+		rc = bnx2fc_initiate_abts(orig_io_req);
+		if (rc != SUCCESS) {
+			BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+				"failed. issue cleanup\n");
+			bnx2fc_initiate_cleanup(orig_io_req);
+		}
+	} else if (opcode == ELS_LS_ACC) {
+		/* REVISIT: Check if the exchange is already aborted */
+		offset = ntohl(acc->reca_fc4value);
+		e_stat = ntohl(acc->reca_e_stat);
+		if (e_stat & ESB_ST_SEQ_INIT)  {
+			BNX2FC_IO_DBG(rec_req, "target has the seq init\n");
+			goto free_frame;
+		}
+		BNX2FC_IO_DBG(rec_req, "e_stat = 0x%x, offset = 0x%x\n",
+			e_stat, offset);
+		/* Seq initiative is with us */
+		err_entry = (struct fcoe_err_report_entry *)
+			     &orig_io_req->err_entry;
+		sc_cmd = orig_io_req->sc_cmd;
+		if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+			/* SCSI WRITE command */
+			if (offset == orig_io_req->data_xfer_len) {
+				BNX2FC_IO_DBG(rec_req, "WRITE - resp lost\n");
+				/* FCP_RSP lost */
+				r_ctl = FC_RCTL_DD_CMD_STATUS;
+				offset = 0;
+			} else  {
+				/* start transmitting from offset */
+				BNX2FC_IO_DBG(rec_req, "XFER_RDY/DATA lost\n");
+				send_seq_clnp = true;
+				r_ctl = FC_RCTL_DD_DATA_DESC;
+				if (bnx2fc_initiate_seq_cleanup(orig_io_req,
+								offset, r_ctl))
+					abort_io = true;
+				/* XFER_RDY */
+			}
+		} else {
+			/* SCSI READ command */
+			if (err_entry->data.rx_buf_off ==
+					orig_io_req->data_xfer_len) {
+				/* FCP_RSP lost */
+				BNX2FC_IO_DBG(rec_req, "READ - resp lost\n");
+				r_ctl = FC_RCTL_DD_CMD_STATUS;
+				offset = 0;
+			} else  {
+				/* request retransmission from this offset */
+				send_seq_clnp = true;
+				offset = err_entry->data.rx_buf_off;
+				BNX2FC_IO_DBG(rec_req, "RD DATA lost\n");
+				/* FCP_DATA lost */
+				r_ctl = FC_RCTL_DD_SOL_DATA;
+				if (bnx2fc_initiate_seq_cleanup(orig_io_req,
+								offset, r_ctl))
+					abort_io = true;
+			}
+		}
+		if (abort_io) {
+			rc = bnx2fc_initiate_abts(orig_io_req);
+			if (rc != SUCCESS) {
+				BNX2FC_IO_DBG(rec_req, "rec_compl:initiate_abts"
+					      " failed. issue cleanup\n");
+				bnx2fc_initiate_cleanup(orig_io_req);
+			}
+		} else if (!send_seq_clnp) {
+			BNX2FC_IO_DBG(rec_req, "Send SRR - FCP_RSP\n");
+			spin_unlock_bh(&tgt->tgt_lock);
+			rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl);
+			spin_lock_bh(&tgt->tgt_lock);
+
+			if (rc) {
+				BNX2FC_IO_DBG(rec_req, "Unable to send SRR"
+					" IO will abort\n");
+			}
+		}
+	}
+free_frame:
+	fc_frame_free(fp);
+free_buf:
+	kfree(buf);
+rec_compl_done:
+	kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+	kfree(cb_arg);
+}
+
+int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req)
+{
+	struct fc_els_rec rec;
+	struct bnx2fc_rport *tgt = orig_io_req->tgt;
+	struct fc_lport *lport = tgt->rdata->local_port;
+	struct bnx2fc_els_cb_arg *cb_arg = NULL;
+	u32 sid = tgt->sid;
+	u32 r_a_tov = lport->r_a_tov;
+	int rc;
+
+	BNX2FC_IO_DBG(orig_io_req, "Sending REC\n");
+	memset(&rec, 0, sizeof(rec));
+
+	cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+	if (!cb_arg) {
+		printk(KERN_ERR PFX "Unable to allocate cb_arg for REC\n");
+		rc = -ENOMEM;
+		goto rec_err;
+	}
+	kref_get(&orig_io_req->refcount);
+
+	cb_arg->aborted_io_req = orig_io_req;
+
+	rec.rec_cmd = ELS_REC;
+	hton24(rec.rec_s_id, sid);
+	rec.rec_ox_id = htons(orig_io_req->xid);
+	rec.rec_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id);
+
+	rc = bnx2fc_initiate_els(tgt, ELS_REC, &rec, sizeof(rec),
+				 bnx2fc_rec_compl, cb_arg,
+				 r_a_tov);
+rec_err:
+	if (rc) {
+		BNX2FC_IO_DBG(orig_io_req, "REC failed - release\n");
+		spin_lock_bh(&tgt->tgt_lock);
+		kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+		spin_unlock_bh(&tgt->tgt_lock);
+		kfree(cb_arg);
+	}
+	return rc;
+}
+
+int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl)
+{
+	struct fcp_srr srr;
+	struct bnx2fc_rport *tgt = orig_io_req->tgt;
+	struct fc_lport *lport = tgt->rdata->local_port;
+	struct bnx2fc_els_cb_arg *cb_arg = NULL;
+	u32 r_a_tov = lport->r_a_tov;
+	int rc;
+
+	BNX2FC_IO_DBG(orig_io_req, "Sending SRR\n");
+	memset(&srr, 0, sizeof(srr));
+
+	cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+	if (!cb_arg) {
+		printk(KERN_ERR PFX "Unable to allocate cb_arg for SRR\n");
+		rc = -ENOMEM;
+		goto srr_err;
+	}
+	kref_get(&orig_io_req->refcount);
+
+	cb_arg->aborted_io_req = orig_io_req;
+
+	srr.srr_op = ELS_SRR;
+	srr.srr_ox_id = htons(orig_io_req->xid);
+	srr.srr_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id);
+	srr.srr_rel_off = htonl(offset);
+	srr.srr_r_ctl = r_ctl;
+	orig_io_req->srr_offset = offset;
+	orig_io_req->srr_rctl = r_ctl;
+
+	rc = bnx2fc_initiate_els(tgt, ELS_SRR, &srr, sizeof(srr),
+				 bnx2fc_srr_compl, cb_arg,
+				 r_a_tov);
+srr_err:
+	if (rc) {
+		BNX2FC_IO_DBG(orig_io_req, "SRR failed - release\n");
+		spin_lock_bh(&tgt->tgt_lock);
+		kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+		spin_unlock_bh(&tgt->tgt_lock);
+		kfree(cb_arg);
+	} else
+		set_bit(BNX2FC_FLAG_SRR_SENT, &orig_io_req->req_flags);
+
+	return rc;
+}
+
 static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
 			void *data, u32 data_len,
 			void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
 			struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec)
 {
 	struct fcoe_port *port = tgt->port;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
 	struct fc_rport *rport = tgt->rport;
 	struct fc_lport *lport = port->lport;
 	struct bnx2fc_cmd *els_req;
@@ -274,12 +678,12 @@
 
 	rc = fc_remote_port_chkready(rport);
 	if (rc) {
-		printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op);
+		printk(KERN_ERR PFX "els 0x%x: rport not ready\n", op);
 		rc = -EINVAL;
 		goto els_err;
 	}
 	if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
-		printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op);
+		printk(KERN_ERR PFX "els 0x%x: link is not ready\n", op);
 		rc = -EINVAL;
 		goto els_err;
 	}
@@ -305,7 +709,7 @@
 	mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req);
 	rc = bnx2fc_init_mp_req(els_req);
 	if (rc == FAILED) {
-		printk(KERN_ALERT PFX "ELS MP request init failed\n");
+		printk(KERN_ERR PFX "ELS MP request init failed\n");
 		spin_lock_bh(&tgt->tgt_lock);
 		kref_put(&els_req->refcount, bnx2fc_cmd_release);
 		spin_unlock_bh(&tgt->tgt_lock);
@@ -324,7 +728,7 @@
 	if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) {
 		memcpy(mp_req->req_buf, data, data_len);
 	} else {
-		printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op);
+		printk(KERN_ERR PFX "Invalid ELS op 0x%x\n", op);
 		els_req->cb_func = NULL;
 		els_req->cb_arg = NULL;
 		spin_lock_bh(&tgt->tgt_lock);
@@ -342,9 +746,14 @@
 	did = tgt->rport->port_id;
 	sid = tgt->sid;
 
-	__fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
-			   FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ |
-			   FC_FC_SEQ_INIT, 0);
+	if (op == ELS_SRR)
+		__fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS4_REQ, did, sid,
+				   FC_TYPE_FCP, FC_FC_FIRST_SEQ |
+				   FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+	else
+		__fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
+				   FC_TYPE_ELS, FC_FC_FIRST_SEQ |
+				   FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
 	/* Obtain exchange id */
 	xid = els_req->xid;
@@ -352,7 +761,8 @@
 	index = xid % BNX2FC_TASKS_PER_PAGE;
 
 	/* Initialize task context for this IO request */
-	task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+	task_page = (struct fcoe_task_ctx_entry *)
+			interface->hba->task_ctx[task_idx];
 	task = &(task_page[index]);
 	bnx2fc_init_mp_task(els_req, task);
 
@@ -496,8 +906,8 @@
 				      void *arg, u32 timeout)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
-	struct fcoe_ctlr *fip = &hba->ctlr;
+	struct bnx2fc_interface *interface = port->priv;
+	struct fcoe_ctlr *fip = &interface->ctlr;
 	struct fc_frame_header *fh = fc_frame_header_get(fp);
 
 	switch (op) {
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index a97aff3..7cb2cd4 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -3,7 +3,7 @@
  * cnic modules to create FCoE instances, send/receive non-offloaded
  * FIP/FCoE packets, listen to link events etc.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,13 +15,14 @@
 #include "bnx2fc.h"
 
 static struct list_head adapter_list;
+static struct list_head if_list;
 static u32 adapter_count;
 static DEFINE_MUTEX(bnx2fc_dev_lock);
 DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
 
 #define DRV_MODULE_NAME		"bnx2fc"
 #define DRV_MODULE_VERSION	BNX2FC_VERSION
-#define DRV_MODULE_RELDATE	"Jun 10, 2011"
+#define DRV_MODULE_RELDATE	"Jun 23, 2011"
 
 
 static char version[] __devinitdata =
@@ -61,7 +62,7 @@
 
 static void bnx2fc_recv_frame(struct sk_buff *skb);
 
-static void bnx2fc_start_disc(struct bnx2fc_hba *hba);
+static void bnx2fc_start_disc(struct bnx2fc_interface *interface);
 static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev);
 static int bnx2fc_net_config(struct fc_lport *lp);
 static int bnx2fc_lport_config(struct fc_lport *lport);
@@ -70,18 +71,20 @@
 static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba);
 static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba);
 static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba);
-static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
+static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
 				  struct device *parent, int npiv);
 static void bnx2fc_destroy_work(struct work_struct *work);
 
 static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev);
+static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
+							*phys_dev);
 static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic);
 
 static int bnx2fc_fw_init(struct bnx2fc_hba *hba);
 static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba);
 
 static void bnx2fc_port_shutdown(struct fc_lport *lport);
-static void bnx2fc_stop(struct bnx2fc_hba *hba);
+static void bnx2fc_stop(struct bnx2fc_interface *interface);
 static int __init bnx2fc_mod_init(void);
 static void __exit bnx2fc_mod_exit(void);
 
@@ -142,7 +145,8 @@
 static void bnx2fc_cleanup(struct fc_lport *lport)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct bnx2fc_rport *tgt;
 	int i;
 
@@ -219,7 +223,8 @@
 	struct fcoe_crc_eof	*cp;
 	struct sk_buff		*skb;
 	struct fc_frame_header	*fh;
-	struct bnx2fc_hba	*hba;
+	struct bnx2fc_interface	*interface;
+	struct bnx2fc_hba *hba;
 	struct fcoe_port	*port;
 	struct fcoe_hdr		*hp;
 	struct bnx2fc_rport	*tgt;
@@ -230,7 +235,8 @@
 	int			wlen, rc = 0;
 
 	port = (struct fcoe_port *)lport_priv(lport);
-	hba = port->priv;
+	interface = port->priv;
+	hba = interface->hba;
 
 	fh = fc_frame_header_get(fp);
 
@@ -242,12 +248,12 @@
 	}
 
 	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-		if (!hba->ctlr.sel_fcf) {
+		if (!interface->ctlr.sel_fcf) {
 			BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n");
 			kfree_skb(skb);
 			return -EINVAL;
 		}
-		if (fcoe_ctlr_els_send(&hba->ctlr, lport, skb))
+		if (fcoe_ctlr_els_send(&interface->ctlr, lport, skb))
 			return 0;
 	}
 
@@ -316,19 +322,19 @@
 	skb_reset_network_header(skb);
 	skb->mac_len = elen;
 	skb->protocol = htons(ETH_P_FCOE);
-	skb->dev = hba->netdev;
+	skb->dev = interface->netdev;
 
 	/* fill up mac and fcoe headers */
 	eh = eth_hdr(skb);
 	eh->h_proto = htons(ETH_P_FCOE);
-	if (hba->ctlr.map_dest)
+	if (interface->ctlr.map_dest)
 		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
 	else
 		/* insert GW address */
-		memcpy(eh->h_dest, hba->ctlr.dest_addr, ETH_ALEN);
+		memcpy(eh->h_dest, interface->ctlr.dest_addr, ETH_ALEN);
 
-	if (unlikely(hba->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-		memcpy(eh->h_source, hba->ctlr.ctl_src_addr, ETH_ALEN);
+	if (unlikely(interface->ctlr.flogi_oxid != FC_XID_UNKNOWN))
+		memcpy(eh->h_source, interface->ctlr.ctl_src_addr, ETH_ALEN);
 	else
 		memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
@@ -377,22 +383,23 @@
 		struct packet_type *ptype, struct net_device *olddev)
 {
 	struct fc_lport *lport;
-	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct fc_frame_header *fh;
 	struct fcoe_rcv_info *fr;
 	struct fcoe_percpu_s *bg;
 	unsigned short oxid;
 
-	hba = container_of(ptype, struct bnx2fc_hba, fcoe_packet_type);
-	lport = hba->ctlr.lp;
+	interface = container_of(ptype, struct bnx2fc_interface,
+				 fcoe_packet_type);
+	lport = interface->ctlr.lp;
 
 	if (unlikely(lport == NULL)) {
-		printk(KERN_ALERT PFX "bnx2fc_rcv: lport is NULL\n");
+		printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n");
 		goto err;
 	}
 
 	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-		printk(KERN_ALERT PFX "bnx2fc_rcv: Wrong FC type frame\n");
+		printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
 		goto err;
 	}
 
@@ -411,7 +418,6 @@
 
 	fr = fcoe_dev_from_skb(skb);
 	fr->fr_dev = lport;
-	fr->ptype = ptype;
 
 	bg = &bnx2fc_global;
 	spin_lock_bh(&bg->fcoe_rx_list.lock);
@@ -469,7 +475,7 @@
 	fr = fcoe_dev_from_skb(skb);
 	lport = fr->fr_dev;
 	if (unlikely(lport == NULL)) {
-		printk(KERN_ALERT PFX "Invalid lport struct\n");
+		printk(KERN_ERR PFX "Invalid lport struct\n");
 		kfree_skb(skb);
 		return;
 	}
@@ -594,7 +600,8 @@
 	struct fc_host_statistics *bnx2fc_stats;
 	struct fc_lport *lport = shost_priv(shost);
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct fcoe_statistics_params *fw_stats;
 	int rc = 0;
 
@@ -631,7 +638,7 @@
 static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
 	struct Scsi_Host *shost = lport->host;
 	int rc = 0;
 
@@ -654,7 +661,7 @@
 		fc_host_max_npiv_vports(lport->host) = USHRT_MAX;
 	sprintf(fc_host_symbolic_name(lport->host), "%s v%s over %s",
 		BNX2FC_NAME, BNX2FC_VERSION,
-		hba->netdev->name);
+		interface->netdev->name);
 
 	return 0;
 }
@@ -662,8 +669,8 @@
 static void bnx2fc_link_speed_update(struct fc_lport *lport)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
-	struct net_device *netdev = hba->netdev;
+	struct bnx2fc_interface *interface = port->priv;
+	struct net_device *netdev = interface->netdev;
 	struct ethtool_cmd ecmd;
 
 	if (!dev_ethtool_get_settings(netdev, &ecmd)) {
@@ -691,7 +698,8 @@
 static int bnx2fc_link_ok(struct fc_lport *lport)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct net_device *dev = hba->phys_dev;
 	int rc = 0;
 
@@ -713,7 +721,7 @@
  */
 void bnx2fc_get_link_state(struct bnx2fc_hba *hba)
 {
-	if (test_bit(__LINK_STATE_NOCARRIER, &hba->netdev->state))
+	if (test_bit(__LINK_STATE_NOCARRIER, &hba->phys_dev->state))
 		set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
 	else
 		clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
@@ -722,11 +730,13 @@
 static int bnx2fc_net_config(struct fc_lport *lport)
 {
 	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct fcoe_port *port;
 	u64 wwnn, wwpn;
 
 	port = lport_priv(lport);
-	hba = port->priv;
+	interface = port->priv;
+	hba = interface->hba;
 
 	/* require support for get_pauseparam ethtool op. */
 	if (!hba->phys_dev->ethtool_ops ||
@@ -743,11 +753,11 @@
 	bnx2fc_link_speed_update(lport);
 
 	if (!lport->vport) {
-		wwnn = fcoe_wwn_from_mac(hba->ctlr.ctl_src_addr, 1, 0);
+		wwnn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr, 1, 0);
 		BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn);
 		fc_set_wwnn(lport, wwnn);
 
-		wwpn = fcoe_wwn_from_mac(hba->ctlr.ctl_src_addr, 2, 0);
+		wwpn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr, 2, 0);
 		BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn);
 		fc_set_wwpn(lport, wwpn);
 	}
@@ -759,9 +769,9 @@
 {
 	struct bnx2fc_hba *hba = (struct bnx2fc_hba *)data;
 
-	BNX2FC_HBA_DBG(hba->ctlr.lp, "ERROR:bnx2fc_destroy_timer - "
+	BNX2FC_MISC_DBG("ERROR:bnx2fc_destroy_timer - "
 		   "Destroy compl not received!!\n");
-	hba->flags |= BNX2FC_FLAG_DESTROY_CMPL;
+	set_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
 	wake_up_interruptible(&hba->destroy_wait);
 }
 
@@ -779,54 +789,35 @@
 				     u16 vlan_id)
 {
 	struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context;
-	struct fc_lport *lport = hba->ctlr.lp;
+	struct fc_lport *lport;
 	struct fc_lport *vport;
+	struct bnx2fc_interface *interface;
+	int wait_for_upload = 0;
 	u32 link_possible = 1;
 
 	/* Ignore vlans for now */
 	if (vlan_id != 0)
 		return;
 
-	if (!test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
-		BNX2FC_MISC_DBG("driver not ready. event=%s %ld\n",
-			   hba->netdev->name, event);
-		return;
-	}
-
-	/*
-	 * ASSUMPTION:
-	 * indicate_netevent cannot be called from cnic unless bnx2fc
-	 * does register_device
-	 */
-	BUG_ON(!lport);
-
-	BNX2FC_HBA_DBG(lport, "enter netevent handler - event=%s %ld\n",
-				hba->netdev->name, event);
-
 	switch (event) {
 	case NETDEV_UP:
-		BNX2FC_HBA_DBG(lport, "Port up, adapter_state = %ld\n",
-			hba->adapter_state);
 		if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
 			printk(KERN_ERR "indicate_netevent: "\
-					"adapter is not UP!!\n");
+					"hba is not UP!!\n");
 		break;
 
 	case NETDEV_DOWN:
-		BNX2FC_HBA_DBG(lport, "Port down\n");
 		clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
 		clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
 		link_possible = 0;
 		break;
 
 	case NETDEV_GOING_DOWN:
-		BNX2FC_HBA_DBG(lport, "Port going down\n");
 		set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
 		link_possible = 0;
 		break;
 
 	case NETDEV_CHANGE:
-		BNX2FC_HBA_DBG(lport, "NETDEV_CHANGE\n");
 		break;
 
 	default:
@@ -834,15 +825,22 @@
 		return;
 	}
 
-	bnx2fc_link_speed_update(lport);
+	mutex_lock(&bnx2fc_dev_lock);
+	list_for_each_entry(interface, &if_list, list) {
 
-	if (link_possible && !bnx2fc_link_ok(lport)) {
-		printk(KERN_ERR "indicate_netevent: call ctlr_link_up\n");
-		fcoe_ctlr_link_up(&hba->ctlr);
-	} else {
-		printk(KERN_ERR "indicate_netevent: call ctlr_link_down\n");
-		if (fcoe_ctlr_link_down(&hba->ctlr)) {
-			clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+		if (interface->hba != hba)
+			continue;
+
+		lport = interface->ctlr.lp;
+		BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n",
+				interface->netdev->name, event);
+
+		bnx2fc_link_speed_update(lport);
+
+		if (link_possible && !bnx2fc_link_ok(lport)) {
+			printk(KERN_ERR "indicate_netevent: ctlr_link_up\n");
+			fcoe_ctlr_link_up(&interface->ctlr);
+		} else if (fcoe_ctlr_link_down(&interface->ctlr)) {
 			mutex_lock(&lport->lp_mutex);
 			list_for_each_entry(vport, &lport->vports, list)
 				fc_host_port_type(vport->host) =
@@ -853,25 +851,27 @@
 				    get_cpu())->LinkFailureCount++;
 			put_cpu();
 			fcoe_clean_pending_queue(lport);
-
-			init_waitqueue_head(&hba->shutdown_wait);
-			BNX2FC_HBA_DBG(lport, "indicate_netevent "
-					     "num_ofld_sess = %d\n",
-				   hba->num_ofld_sess);
-			hba->wait_for_link_down = 1;
-			BNX2FC_HBA_DBG(lport, "waiting for uploads to "
-					     "compl proc = %s\n",
-				   current->comm);
-			wait_event_interruptible(hba->shutdown_wait,
-						 (hba->num_ofld_sess == 0));
-			BNX2FC_HBA_DBG(lport, "wakeup - num_ofld_sess = %d\n",
-				hba->num_ofld_sess);
-			hba->wait_for_link_down = 0;
-
-			if (signal_pending(current))
-				flush_signals(current);
+			wait_for_upload = 1;
 		}
 	}
+	mutex_unlock(&bnx2fc_dev_lock);
+
+	if (wait_for_upload) {
+		clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+		init_waitqueue_head(&hba->shutdown_wait);
+		BNX2FC_MISC_DBG("indicate_netevent "
+				"num_ofld_sess = %d\n",
+				hba->num_ofld_sess);
+		hba->wait_for_link_down = 1;
+		wait_event_interruptible(hba->shutdown_wait,
+					 (hba->num_ofld_sess == 0));
+		BNX2FC_MISC_DBG("wakeup - num_ofld_sess = %d\n",
+				hba->num_ofld_sess);
+		hba->wait_for_link_down = 0;
+
+		if (signal_pending(current))
+			flush_signals(current);
+	}
 }
 
 static int bnx2fc_libfc_config(struct fc_lport *lport)
@@ -889,23 +889,12 @@
 
 static int bnx2fc_em_config(struct fc_lport *lport)
 {
-	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
-
 	if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_MIN_XID,
 				FCOE_MAX_XID, NULL)) {
 		printk(KERN_ERR PFX "em_config:fc_exch_mgr_alloc failed\n");
 		return -ENOMEM;
 	}
 
-	hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba, BNX2FC_MIN_XID,
-					    BNX2FC_MAX_XID);
-
-	if (!hba->cmd_mgr) {
-		printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n");
-		fc_exch_mgr_free(lport);
-		return -ENOMEM;
-	}
 	return 0;
 }
 
@@ -918,11 +907,8 @@
 	lport->e_d_tov = 2 * 1000;
 	lport->r_a_tov = 10 * 1000;
 
-	/* REVISIT: enable when supporting tape devices
 	lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
 				FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-	*/
-	lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS);
 	lport->does_npiv = 1;
 
 	memset(&lport->rnid_gen, 0, sizeof(struct fc_els_rnid_gen));
@@ -952,9 +938,10 @@
 			   struct packet_type *ptype,
 			   struct net_device *orig_dev)
 {
-	struct bnx2fc_hba *hba;
-	hba = container_of(ptype, struct bnx2fc_hba, fip_packet_type);
-	fcoe_ctlr_recv(&hba->ctlr, skb);
+	struct bnx2fc_interface *interface;
+	interface = container_of(ptype, struct bnx2fc_interface,
+				 fip_packet_type);
+	fcoe_ctlr_recv(&interface->ctlr, skb);
 	return 0;
 }
 
@@ -1005,17 +992,17 @@
 	struct Scsi_Host *shost = vport_to_shost(vport);
 	struct fc_lport *n_port = shost_priv(shost);
 	struct fcoe_port *port = lport_priv(n_port);
-	struct bnx2fc_hba *hba = port->priv;
-	struct net_device *netdev = hba->netdev;
+	struct bnx2fc_interface *interface = port->priv;
+	struct net_device *netdev = interface->netdev;
 	struct fc_lport *vn_port;
 
-	if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+	if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
 		printk(KERN_ERR PFX "vn ports cannot be created on"
-			"this hba\n");
+			"this interface\n");
 		return -EIO;
 	}
 	mutex_lock(&bnx2fc_dev_lock);
-	vn_port = bnx2fc_if_create(hba, &vport->dev, 1);
+	vn_port = bnx2fc_if_create(interface, &vport->dev, 1);
 	mutex_unlock(&bnx2fc_dev_lock);
 
 	if (IS_ERR(vn_port)) {
@@ -1065,10 +1052,10 @@
 }
 
 
-static int bnx2fc_netdev_setup(struct bnx2fc_hba *hba)
+static int bnx2fc_netdev_setup(struct bnx2fc_interface *interface)
 {
-	struct net_device *netdev = hba->netdev;
-	struct net_device *physdev = hba->phys_dev;
+	struct net_device *netdev = interface->netdev;
+	struct net_device *physdev = interface->hba->phys_dev;
 	struct netdev_hw_addr *ha;
 	int sel_san_mac = 0;
 
@@ -1083,7 +1070,8 @@
 
 		if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
 		    (is_valid_ether_addr(ha->addr))) {
-			memcpy(hba->ctlr.ctl_src_addr, ha->addr, ETH_ALEN);
+			memcpy(interface->ctlr.ctl_src_addr, ha->addr,
+			       ETH_ALEN);
 			sel_san_mac = 1;
 			BNX2FC_MISC_DBG("Found SAN MAC\n");
 		}
@@ -1093,15 +1081,15 @@
 	if (!sel_san_mac)
 		return -ENODEV;
 
-	hba->fip_packet_type.func = bnx2fc_fip_recv;
-	hba->fip_packet_type.type = htons(ETH_P_FIP);
-	hba->fip_packet_type.dev = netdev;
-	dev_add_pack(&hba->fip_packet_type);
+	interface->fip_packet_type.func = bnx2fc_fip_recv;
+	interface->fip_packet_type.type = htons(ETH_P_FIP);
+	interface->fip_packet_type.dev = netdev;
+	dev_add_pack(&interface->fip_packet_type);
 
-	hba->fcoe_packet_type.func = bnx2fc_rcv;
-	hba->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	hba->fcoe_packet_type.dev = netdev;
-	dev_add_pack(&hba->fcoe_packet_type);
+	interface->fcoe_packet_type.func = bnx2fc_rcv;
+	interface->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	interface->fcoe_packet_type.dev = netdev;
+	dev_add_pack(&interface->fcoe_packet_type);
 
 	return 0;
 }
@@ -1137,53 +1125,54 @@
 
 static void bnx2fc_interface_release(struct kref *kref)
 {
-	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct net_device *netdev;
-	struct net_device *phys_dev;
 
-	hba = container_of(kref, struct bnx2fc_hba, kref);
+	interface = container_of(kref, struct bnx2fc_interface, kref);
 	BNX2FC_MISC_DBG("Interface is being released\n");
 
-	netdev = hba->netdev;
-	phys_dev = hba->phys_dev;
+	netdev = interface->netdev;
 
 	/* tear-down FIP controller */
-	if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done))
-		fcoe_ctlr_destroy(&hba->ctlr);
+	if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags))
+		fcoe_ctlr_destroy(&interface->ctlr);
 
+	kfree(interface);
+
+	dev_put(netdev);
+	module_put(THIS_MODULE);
+}
+
+static inline void bnx2fc_interface_get(struct bnx2fc_interface *interface)
+{
+	kref_get(&interface->kref);
+}
+
+static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface)
+{
+	kref_put(&interface->kref, bnx2fc_interface_release);
+}
+static void bnx2fc_hba_destroy(struct bnx2fc_hba *hba)
+{
 	/* Free the command manager */
 	if (hba->cmd_mgr) {
 		bnx2fc_cmd_mgr_free(hba->cmd_mgr);
 		hba->cmd_mgr = NULL;
 	}
-	dev_put(netdev);
-	module_put(THIS_MODULE);
-}
-
-static inline void bnx2fc_interface_get(struct bnx2fc_hba *hba)
-{
-	kref_get(&hba->kref);
-}
-
-static inline void bnx2fc_interface_put(struct bnx2fc_hba *hba)
-{
-	kref_put(&hba->kref, bnx2fc_interface_release);
-}
-static void bnx2fc_interface_destroy(struct bnx2fc_hba *hba)
-{
+	kfree(hba->tgt_ofld_list);
 	bnx2fc_unbind_pcidev(hba);
 	kfree(hba);
 }
 
 /**
- * bnx2fc_interface_create - create a new fcoe instance
+ * bnx2fc_hba_create - create a new bnx2fc hba
  *
  * @cnic:	pointer to cnic device
  *
- * Creates a new FCoE instance on the given device which include allocating
- *	hba structure, scsi_host and lport structures.
+ * Creates a new FCoE hba on the given device.
+ *
  */
-static struct bnx2fc_hba *bnx2fc_interface_create(struct cnic_dev *cnic)
+static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic)
 {
 	struct bnx2fc_hba *hba;
 	int rc;
@@ -1198,65 +1187,83 @@
 
 	hba->cnic = cnic;
 	rc = bnx2fc_bind_pcidev(hba);
-	if (rc)
+	if (rc) {
+		printk(KERN_ERR PFX "create_adapter:  bind error\n");
 		goto bind_err;
+	}
 	hba->phys_dev = cnic->netdev;
-	/* will get overwritten after we do vlan discovery */
-	hba->netdev = hba->phys_dev;
+	hba->next_conn_id = 0;
+
+	hba->tgt_ofld_list =
+		kzalloc(sizeof(struct bnx2fc_rport *) * BNX2FC_NUM_MAX_SESS,
+			GFP_KERNEL);
+	if (!hba->tgt_ofld_list) {
+		printk(KERN_ERR PFX "Unable to allocate tgt offload list\n");
+		goto tgtofld_err;
+	}
+
+	hba->num_ofld_sess = 0;
+
+	hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba, BNX2FC_MIN_XID,
+						BNX2FC_MAX_XID);
+	if (!hba->cmd_mgr) {
+		printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n");
+		goto cmgr_err;
+	}
 
 	init_waitqueue_head(&hba->shutdown_wait);
 	init_waitqueue_head(&hba->destroy_wait);
+	INIT_LIST_HEAD(&hba->vports);
 
 	return hba;
+
+cmgr_err:
+	kfree(hba->tgt_ofld_list);
+tgtofld_err:
+	bnx2fc_unbind_pcidev(hba);
 bind_err:
-	printk(KERN_ERR PFX "create_interface: bind error\n");
 	kfree(hba);
 	return NULL;
 }
 
-static int bnx2fc_interface_setup(struct bnx2fc_hba *hba,
-				  enum fip_state fip_mode)
+struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
+				      struct net_device *netdev,
+				      enum fip_state fip_mode)
 {
+	struct bnx2fc_interface *interface;
 	int rc = 0;
-	struct net_device *netdev = hba->netdev;
-	struct fcoe_ctlr *fip = &hba->ctlr;
 
+	interface = kzalloc(sizeof(*interface), GFP_KERNEL);
+	if (!interface) {
+		printk(KERN_ERR PFX "Unable to allocate interface structure\n");
+		return NULL;
+	}
 	dev_hold(netdev);
-	kref_init(&hba->kref);
-
-	hba->flags = 0;
+	kref_init(&interface->kref);
+	interface->hba = hba;
+	interface->netdev = netdev;
 
 	/* Initialize FIP */
-	memset(fip, 0, sizeof(*fip));
-	fcoe_ctlr_init(fip, fip_mode);
-	hba->ctlr.send = bnx2fc_fip_send;
-	hba->ctlr.update_mac = bnx2fc_update_src_mac;
-	hba->ctlr.get_src_addr = bnx2fc_get_src_mac;
-	set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done);
+	fcoe_ctlr_init(&interface->ctlr, fip_mode);
+	interface->ctlr.send = bnx2fc_fip_send;
+	interface->ctlr.update_mac = bnx2fc_update_src_mac;
+	interface->ctlr.get_src_addr = bnx2fc_get_src_mac;
+	set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags);
 
-	INIT_LIST_HEAD(&hba->vports);
-	rc = bnx2fc_netdev_setup(hba);
-	if (rc)
-		goto setup_err;
+	rc = bnx2fc_netdev_setup(interface);
+	if (!rc)
+		return interface;
 
-	hba->next_conn_id = 0;
-
-	memset(hba->tgt_ofld_list, 0, sizeof(hba->tgt_ofld_list));
-	hba->num_ofld_sess = 0;
-
-	return 0;
-
-setup_err:
-	fcoe_ctlr_destroy(&hba->ctlr);
+	fcoe_ctlr_destroy(&interface->ctlr);
 	dev_put(netdev);
-	bnx2fc_interface_put(hba);
-	return rc;
+	kfree(interface);
+	return NULL;
 }
 
 /**
  * bnx2fc_if_create - Create FCoE instance on a given interface
  *
- * @hba:	FCoE interface to create a local port on
+ * @interface:	FCoE interface to create a local port on
  * @parent:	Device pointer to be the parent in sysfs for the SCSI host
  * @npiv:	Indicates if the port is vport or not
  *
@@ -1264,7 +1271,7 @@
  *
  * Returns:	Allocated fc_lport or an error pointer
  */
-static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
+static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
 				  struct device *parent, int npiv)
 {
 	struct fc_lport		*lport, *n_port;
@@ -1272,11 +1279,12 @@
 	struct Scsi_Host	*shost;
 	struct fc_vport		*vport = dev_to_vport(parent);
 	struct bnx2fc_lport	*blport;
+	struct bnx2fc_hba	*hba;
 	int			rc = 0;
 
 	blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
 	if (!blport) {
-		BNX2FC_HBA_DBG(hba->ctlr.lp, "Unable to alloc bnx2fc_lport\n");
+		BNX2FC_HBA_DBG(interface->ctlr.lp, "Unable to alloc blport\n");
 		return NULL;
 	}
 
@@ -1293,7 +1301,7 @@
 	shost = lport->host;
 	port = lport_priv(lport);
 	port->lport = lport;
-	port->priv = hba;
+	port->priv = interface;
 	INIT_WORK(&port->destroy_work, bnx2fc_destroy_work);
 
 	/* Configure fcoe_port */
@@ -1317,7 +1325,7 @@
 	rc = bnx2fc_shost_config(lport, parent);
 	if (rc) {
 		printk(KERN_ERR PFX "Couldnt configure shost for %s\n",
-			hba->netdev->name);
+			interface->netdev->name);
 		goto lp_config_err;
 	}
 
@@ -1343,8 +1351,9 @@
 		goto shost_err;
 	}
 
-	bnx2fc_interface_get(hba);
+	bnx2fc_interface_get(interface);
 
+	hba = interface->hba;
 	spin_lock_bh(&hba->hba_lock);
 	blport->lport = lport;
 	list_add_tail(&blport->list, &hba->vports);
@@ -1361,21 +1370,19 @@
 	return NULL;
 }
 
-static void bnx2fc_netdev_cleanup(struct bnx2fc_hba *hba)
+static void bnx2fc_netdev_cleanup(struct bnx2fc_interface *interface)
 {
 	/* Dont listen for Ethernet packets anymore */
-	__dev_remove_pack(&hba->fcoe_packet_type);
-	__dev_remove_pack(&hba->fip_packet_type);
+	__dev_remove_pack(&interface->fcoe_packet_type);
+	__dev_remove_pack(&interface->fip_packet_type);
 	synchronize_net();
 }
 
-static void bnx2fc_if_destroy(struct fc_lport *lport)
+static void bnx2fc_if_destroy(struct fc_lport *lport, struct bnx2fc_hba *hba)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
 	struct bnx2fc_lport *blport, *tmp;
 
-	BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n");
 	/* Stop the transmit retry timer */
 	del_timer_sync(&port->timer);
 
@@ -1409,8 +1416,6 @@
 
 	/* Release Scsi_Host */
 	scsi_host_put(lport->host);
-
-	bnx2fc_interface_put(hba);
 }
 
 /**
@@ -1425,46 +1430,31 @@
  */
 static int bnx2fc_destroy(struct net_device *netdev)
 {
-	struct bnx2fc_hba *hba = NULL;
-	struct net_device *phys_dev;
+	struct bnx2fc_interface *interface = NULL;
+	struct bnx2fc_hba *hba;
+	struct fc_lport *lport;
 	int rc = 0;
 
 	rtnl_lock();
-
 	mutex_lock(&bnx2fc_dev_lock);
-	/* obtain physical netdev */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		phys_dev = vlan_dev_real_dev(netdev);
-	else {
-		printk(KERN_ERR PFX "Not a vlan device\n");
+
+	interface = bnx2fc_interface_lookup(netdev);
+	if (!interface || !interface->ctlr.lp) {
 		rc = -ENODEV;
+		printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n");
 		goto netdev_err;
 	}
 
-	hba = bnx2fc_hba_lookup(phys_dev);
-	if (!hba || !hba->ctlr.lp) {
-		rc = -ENODEV;
-		printk(KERN_ERR PFX "bnx2fc_destroy: hba or lport not found\n");
-		goto netdev_err;
-	}
+	hba = interface->hba;
 
-	if (!test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
-		printk(KERN_ERR PFX "bnx2fc_destroy: Create not called\n");
-		goto netdev_err;
-	}
+	bnx2fc_netdev_cleanup(interface);
+	lport = interface->ctlr.lp;
+	bnx2fc_stop(interface);
+	list_del(&interface->list);
+	destroy_workqueue(interface->timer_work_queue);
+	bnx2fc_interface_put(interface);
+	bnx2fc_if_destroy(lport, hba);
 
-	bnx2fc_netdev_cleanup(hba);
-
-	bnx2fc_stop(hba);
-
-	bnx2fc_if_destroy(hba->ctlr.lp);
-
-	destroy_workqueue(hba->timer_work_queue);
-
-	if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done))
-		bnx2fc_fw_destroy(hba);
-
-	clear_bit(BNX2FC_CREATE_DONE, &hba->init_done);
 netdev_err:
 	mutex_unlock(&bnx2fc_dev_lock);
 	rtnl_unlock();
@@ -1475,16 +1465,20 @@
 {
 	struct fcoe_port *port;
 	struct fc_lport *lport;
+	struct bnx2fc_interface *interface;
+	struct bnx2fc_hba *hba;
 
 	port = container_of(work, struct fcoe_port, destroy_work);
 	lport = port->lport;
+	interface = port->priv;
+	hba = interface->hba;
 
 	BNX2FC_HBA_DBG(lport, "Entered bnx2fc_destroy_work\n");
 
 	bnx2fc_port_shutdown(lport);
 	rtnl_lock();
 	mutex_lock(&bnx2fc_dev_lock);
-	bnx2fc_if_destroy(lport);
+	bnx2fc_if_destroy(lport, hba);
 	mutex_unlock(&bnx2fc_dev_lock);
 	rtnl_unlock();
 }
@@ -1556,28 +1550,27 @@
 static void bnx2fc_ulp_start(void *handle)
 {
 	struct bnx2fc_hba *hba = handle;
-	struct fc_lport *lport = hba->ctlr.lp;
+	struct bnx2fc_interface *interface;
+	struct fc_lport *lport;
 
-	BNX2FC_MISC_DBG("Entered %s\n", __func__);
 	mutex_lock(&bnx2fc_dev_lock);
 
-	if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done))
-		goto start_disc;
-
-	if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done))
+	if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
 		bnx2fc_fw_init(hba);
 
-start_disc:
-	mutex_unlock(&bnx2fc_dev_lock);
-
 	BNX2FC_MISC_DBG("bnx2fc started.\n");
 
-	/* Kick off Fabric discovery*/
-	if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
-		printk(KERN_ERR PFX "ulp_init: start discovery\n");
-		lport->tt.frame_send = bnx2fc_xmit;
-		bnx2fc_start_disc(hba);
+	list_for_each_entry(interface, &if_list, list) {
+		if (interface->hba == hba) {
+			lport = interface->ctlr.lp;
+			/* Kick off Fabric discovery*/
+			printk(KERN_ERR PFX "ulp_init: start discovery\n");
+			lport->tt.frame_send = bnx2fc_xmit;
+			bnx2fc_start_disc(interface);
+		}
 	}
+
+	mutex_unlock(&bnx2fc_dev_lock);
 }
 
 static void bnx2fc_port_shutdown(struct fc_lport *lport)
@@ -1587,37 +1580,25 @@
 	fc_lport_destroy(lport);
 }
 
-static void bnx2fc_stop(struct bnx2fc_hba *hba)
+static void bnx2fc_stop(struct bnx2fc_interface *interface)
 {
 	struct fc_lport *lport;
 	struct fc_lport *vport;
 
-	BNX2FC_MISC_DBG("ENTERED %s - init_done = %ld\n", __func__,
-		   hba->init_done);
-	if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done) &&
-	    test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
-		lport = hba->ctlr.lp;
-		bnx2fc_port_shutdown(lport);
-		BNX2FC_HBA_DBG(lport, "bnx2fc_stop: waiting for %d "
-				"offloaded sessions\n",
-				hba->num_ofld_sess);
-		wait_event_interruptible(hba->shutdown_wait,
-					 (hba->num_ofld_sess == 0));
-		mutex_lock(&lport->lp_mutex);
-		list_for_each_entry(vport, &lport->vports, list)
-			fc_host_port_type(vport->host) = FC_PORTTYPE_UNKNOWN;
-		mutex_unlock(&lport->lp_mutex);
-		fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
-		fcoe_ctlr_link_down(&hba->ctlr);
-		fcoe_clean_pending_queue(lport);
+	if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags))
+		return;
 
-		mutex_lock(&hba->hba_mutex);
-		clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
-		clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+	lport = interface->ctlr.lp;
+	bnx2fc_port_shutdown(lport);
 
-		clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
-		mutex_unlock(&hba->hba_mutex);
-	}
+	mutex_lock(&lport->lp_mutex);
+	list_for_each_entry(vport, &lport->vports, list)
+		fc_host_port_type(vport->host) =
+					FC_PORTTYPE_UNKNOWN;
+	mutex_unlock(&lport->lp_mutex);
+	fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
+	fcoe_ctlr_link_down(&interface->ctlr);
+	fcoe_clean_pending_queue(lport);
 }
 
 static int bnx2fc_fw_init(struct bnx2fc_hba *hba)
@@ -1656,8 +1637,7 @@
 	}
 
 
-	/* Mark HBA to indicate that the FW INIT is done */
-	set_bit(BNX2FC_FW_INIT_DONE, &hba->init_done);
+	set_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags);
 	return 0;
 
 err_unbind:
@@ -1668,7 +1648,7 @@
 
 static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba)
 {
-	if (test_and_clear_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+	if (test_and_clear_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags)) {
 		if (bnx2fc_send_fw_fcoe_destroy_msg(hba) == 0) {
 			init_timer(&hba->destroy_timer);
 			hba->destroy_timer.expires = BNX2FC_FW_TIMEOUT +
@@ -1677,8 +1657,8 @@
 			hba->destroy_timer.data = (unsigned long)hba;
 			add_timer(&hba->destroy_timer);
 			wait_event_interruptible(hba->destroy_wait,
-						 (hba->flags &
-						  BNX2FC_FLAG_DESTROY_CMPL));
+					test_bit(BNX2FC_FLAG_DESTROY_CMPL,
+						 &hba->flags));
 			/* This should never happen */
 			if (signal_pending(current))
 				flush_signals(current);
@@ -1699,40 +1679,57 @@
  */
 static void bnx2fc_ulp_stop(void *handle)
 {
-	struct bnx2fc_hba *hba = (struct bnx2fc_hba *)handle;
+	struct bnx2fc_hba *hba = handle;
+	struct bnx2fc_interface *interface;
 
 	printk(KERN_ERR "ULP_STOP\n");
 
 	mutex_lock(&bnx2fc_dev_lock);
-	bnx2fc_stop(hba);
+	if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
+		goto exit;
+	list_for_each_entry(interface, &if_list, list) {
+		if (interface->hba == hba)
+			bnx2fc_stop(interface);
+	}
+	BUG_ON(hba->num_ofld_sess != 0);
+
+	mutex_lock(&hba->hba_mutex);
+	clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+	clear_bit(ADAPTER_STATE_GOING_DOWN,
+		  &hba->adapter_state);
+
+	clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+	mutex_unlock(&hba->hba_mutex);
+
 	bnx2fc_fw_destroy(hba);
+exit:
 	mutex_unlock(&bnx2fc_dev_lock);
 }
 
-static void bnx2fc_start_disc(struct bnx2fc_hba *hba)
+static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
 {
 	struct fc_lport *lport;
 	int wait_cnt = 0;
 
 	BNX2FC_MISC_DBG("Entered %s\n", __func__);
 	/* Kick off FIP/FLOGI */
-	if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+	if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
 		printk(KERN_ERR PFX "Init not done yet\n");
 		return;
 	}
 
-	lport = hba->ctlr.lp;
+	lport = interface->ctlr.lp;
 	BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n");
 
 	if (!bnx2fc_link_ok(lport)) {
 		BNX2FC_HBA_DBG(lport, "ctlr_link_up\n");
-		fcoe_ctlr_link_up(&hba->ctlr);
+		fcoe_ctlr_link_up(&interface->ctlr);
 		fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
-		set_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+		set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
 	}
 
 	/* wait for the FCF to be selected before issuing FLOGI */
-	while (!hba->ctlr.sel_fcf) {
+	while (!interface->ctlr.sel_fcf) {
 		msleep(250);
 		/* give up after 3 secs */
 		if (++wait_cnt > 12)
@@ -1758,15 +1755,15 @@
 
 	BNX2FC_MISC_DBG("Entered %s\n", __func__);
 	/* bnx2fc works only when bnx2x is loaded */
-	if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
+	if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags) ||
+	    (dev->max_fcoe_conn == 0)) {
 		printk(KERN_ERR PFX "bnx2fc FCoE not supported on %s,"
-				    " flags: %lx\n",
-			dev->netdev->name, dev->flags);
+				    " flags: %lx fcoe_conn: %d\n",
+			dev->netdev->name, dev->flags, dev->max_fcoe_conn);
 		return;
 	}
 
-	/* Configure FCoE interface */
-	hba = bnx2fc_interface_create(dev);
+	hba = bnx2fc_hba_create(dev);
 	if (!hba) {
 		printk(KERN_ERR PFX "hba initialization failed\n");
 		return;
@@ -1774,7 +1771,7 @@
 
 	/* Add HBA to the adapter list */
 	mutex_lock(&bnx2fc_dev_lock);
-	list_add_tail(&hba->link, &adapter_list);
+	list_add_tail(&hba->list, &adapter_list);
 	adapter_count++;
 	mutex_unlock(&bnx2fc_dev_lock);
 
@@ -1782,7 +1779,7 @@
 	rc = dev->register_device(dev, CNIC_ULP_FCOE,
 						(void *) hba);
 	if (rc)
-		printk(KERN_ALERT PFX "register_device failed, rc = %d\n", rc);
+		printk(KERN_ERR PFX "register_device failed, rc = %d\n", rc);
 	else
 		set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic);
 }
@@ -1790,52 +1787,21 @@
 
 static int bnx2fc_disable(struct net_device *netdev)
 {
-	struct bnx2fc_hba *hba;
-	struct net_device *phys_dev;
-	struct ethtool_drvinfo drvinfo;
+	struct bnx2fc_interface *interface;
 	int rc = 0;
 
 	rtnl_lock();
-
 	mutex_lock(&bnx2fc_dev_lock);
 
-	/* obtain physical netdev */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		phys_dev = vlan_dev_real_dev(netdev);
-	else {
-		printk(KERN_ERR PFX "Not a vlan device\n");
+	interface = bnx2fc_interface_lookup(netdev);
+	if (!interface || !interface->ctlr.lp) {
 		rc = -ENODEV;
-		goto nodev;
-	}
-
-	/* verify if the physical device is a netxtreme2 device */
-	if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
-		memset(&drvinfo, 0, sizeof(drvinfo));
-		phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
-		if (strcmp(drvinfo.driver, "bnx2x")) {
-			printk(KERN_ERR PFX "Not a netxtreme2 device\n");
-			rc = -ENODEV;
-			goto nodev;
-		}
+		printk(KERN_ERR PFX "bnx2fc_disable: interface or lport not found\n");
 	} else {
-		printk(KERN_ERR PFX "unable to obtain drv_info\n");
-		rc = -ENODEV;
-		goto nodev;
+		fcoe_ctlr_link_down(&interface->ctlr);
+		fcoe_clean_pending_queue(interface->ctlr.lp);
 	}
 
-	printk(KERN_ERR PFX "phys_dev is netxtreme2 device\n");
-
-	/* obtain hba and initialize rest of the structure */
-	hba = bnx2fc_hba_lookup(phys_dev);
-	if (!hba || !hba->ctlr.lp) {
-		rc = -ENODEV;
-		printk(KERN_ERR PFX "bnx2fc_disable: hba or lport not found\n");
-	} else {
-		fcoe_ctlr_link_down(&hba->ctlr);
-		fcoe_clean_pending_queue(hba->ctlr.lp);
-	}
-
-nodev:
 	mutex_unlock(&bnx2fc_dev_lock);
 	rtnl_unlock();
 	return rc;
@@ -1844,48 +1810,19 @@
 
 static int bnx2fc_enable(struct net_device *netdev)
 {
-	struct bnx2fc_hba *hba;
-	struct net_device *phys_dev;
-	struct ethtool_drvinfo drvinfo;
+	struct bnx2fc_interface *interface;
 	int rc = 0;
 
 	rtnl_lock();
-
-	BNX2FC_MISC_DBG("Entered %s\n", __func__);
 	mutex_lock(&bnx2fc_dev_lock);
 
-	/* obtain physical netdev */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		phys_dev = vlan_dev_real_dev(netdev);
-	else {
-		printk(KERN_ERR PFX "Not a vlan device\n");
+	interface = bnx2fc_interface_lookup(netdev);
+	if (!interface || !interface->ctlr.lp) {
 		rc = -ENODEV;
-		goto nodev;
-	}
-	/* verify if the physical device is a netxtreme2 device */
-	if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
-		memset(&drvinfo, 0, sizeof(drvinfo));
-		phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
-		if (strcmp(drvinfo.driver, "bnx2x")) {
-			printk(KERN_ERR PFX "Not a netxtreme2 device\n");
-			rc = -ENODEV;
-			goto nodev;
-		}
-	} else {
-		printk(KERN_ERR PFX "unable to obtain drv_info\n");
-		rc = -ENODEV;
-		goto nodev;
-	}
+		printk(KERN_ERR PFX "bnx2fc_enable: interface or lport not found\n");
+	} else if (!bnx2fc_link_ok(interface->ctlr.lp))
+		fcoe_ctlr_link_up(&interface->ctlr);
 
-	/* obtain hba and initialize rest of the structure */
-	hba = bnx2fc_hba_lookup(phys_dev);
-	if (!hba || !hba->ctlr.lp) {
-		rc = -ENODEV;
-		printk(KERN_ERR PFX "bnx2fc_enable: hba or lport not found\n");
-	} else if (!bnx2fc_link_ok(hba->ctlr.lp))
-		fcoe_ctlr_link_up(&hba->ctlr);
-
-nodev:
 	mutex_unlock(&bnx2fc_dev_lock);
 	rtnl_unlock();
 	return rc;
@@ -1903,6 +1840,7 @@
  */
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
 {
+	struct bnx2fc_interface *interface;
 	struct bnx2fc_hba *hba;
 	struct net_device *phys_dev;
 	struct fc_lport *lport;
@@ -1938,7 +1876,7 @@
 	if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
 		memset(&drvinfo, 0, sizeof(drvinfo));
 		phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
-		if (strcmp(drvinfo.driver, "bnx2x")) {
+		if (strncmp(drvinfo.driver, "bnx2x", strlen("bnx2x"))) {
 			printk(KERN_ERR PFX "Not a netxtreme2 device\n");
 			rc = -EINVAL;
 			goto netdev_err;
@@ -1949,7 +1887,7 @@
 		goto netdev_err;
 	}
 
-	/* obtain hba and initialize rest of the structure */
+	/* obtain interface and initialize rest of the structure */
 	hba = bnx2fc_hba_lookup(phys_dev);
 	if (!hba) {
 		rc = -ENODEV;
@@ -1957,67 +1895,61 @@
 		goto netdev_err;
 	}
 
-	if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
-		rc = bnx2fc_fw_init(hba);
-		if (rc)
-			goto netdev_err;
-	}
-
-	if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
+	if (bnx2fc_interface_lookup(netdev)) {
 		rc = -EEXIST;
 		goto netdev_err;
 	}
 
-	/* update netdev with vlan netdev */
-	hba->netdev = netdev;
-	hba->vlan_id = vlan_id;
-	hba->vlan_enabled = 1;
-
-	rc = bnx2fc_interface_setup(hba, fip_mode);
-	if (rc) {
-		printk(KERN_ERR PFX "bnx2fc_interface_setup failed\n");
+	interface = bnx2fc_interface_create(hba, netdev, fip_mode);
+	if (!interface) {
+		printk(KERN_ERR PFX "bnx2fc_interface_create failed\n");
 		goto ifput_err;
 	}
 
-	hba->timer_work_queue =
+	interface->vlan_id = vlan_id;
+	interface->vlan_enabled = 1;
+
+	interface->timer_work_queue =
 			create_singlethread_workqueue("bnx2fc_timer_wq");
-	if (!hba->timer_work_queue) {
+	if (!interface->timer_work_queue) {
 		printk(KERN_ERR PFX "ulp_init could not create timer_wq\n");
 		rc = -EINVAL;
 		goto ifput_err;
 	}
 
-	lport = bnx2fc_if_create(hba, &hba->pcidev->dev, 0);
+	lport = bnx2fc_if_create(interface, &interface->hba->pcidev->dev, 0);
 	if (!lport) {
 		printk(KERN_ERR PFX "Failed to create interface (%s)\n",
 			netdev->name);
-		bnx2fc_netdev_cleanup(hba);
+		bnx2fc_netdev_cleanup(interface);
 		rc = -EINVAL;
 		goto if_create_err;
 	}
 
+	/* Add interface to if_list */
+	list_add_tail(&interface->list, &if_list);
+
 	lport->boot_time = jiffies;
 
 	/* Make this master N_port */
-	hba->ctlr.lp = lport;
+	interface->ctlr.lp = lport;
 
-	set_bit(BNX2FC_CREATE_DONE, &hba->init_done);
-	printk(KERN_ERR PFX "create: START DISC\n");
-	bnx2fc_start_disc(hba);
+	BNX2FC_HBA_DBG(lport, "create: START DISC\n");
+	bnx2fc_start_disc(interface);
 	/*
 	 * Release from kref_init in bnx2fc_interface_setup, on success
 	 * lport should be holding a reference taken in bnx2fc_if_create
 	 */
-	bnx2fc_interface_put(hba);
+	bnx2fc_interface_put(interface);
 	/* put netdev that was held while calling dev_get_by_name */
 	mutex_unlock(&bnx2fc_dev_lock);
 	rtnl_unlock();
 	return 0;
 
 if_create_err:
-	destroy_workqueue(hba->timer_work_queue);
+	destroy_workqueue(interface->timer_work_queue);
 ifput_err:
-	bnx2fc_interface_put(hba);
+	bnx2fc_interface_put(interface);
 netdev_err:
 	module_put(THIS_MODULE);
 mod_err:
@@ -2027,7 +1959,7 @@
 }
 
 /**
- * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc adapter instance
+ * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance
  *
  * @cnic:	Pointer to cnic device instance
  *
@@ -2047,19 +1979,30 @@
 	return NULL;
 }
 
-static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev)
+static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
+							*netdev)
 {
-	struct list_head *list;
-	struct list_head *temp;
+	struct bnx2fc_interface *interface;
+
+	/* Called with bnx2fc_dev_lock held */
+	list_for_each_entry(interface, &if_list, list) {
+		if (interface->netdev == netdev)
+			return interface;
+	}
+	return NULL;
+}
+
+static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device
+						      *phys_dev)
+{
 	struct bnx2fc_hba *hba;
 
 	/* Called with bnx2fc_dev_lock held */
-	list_for_each_safe(list, temp, &adapter_list) {
-		hba = (struct bnx2fc_hba *)list;
+	list_for_each_entry(hba, &adapter_list, list) {
 		if (hba->phys_dev == phys_dev)
 			return hba;
 	}
-	printk(KERN_ERR PFX "hba_lookup: hba NULL\n");
+	printk(KERN_ERR PFX "adapter_lookup: hba NULL\n");
 	return NULL;
 }
 
@@ -2071,6 +2014,8 @@
 static void bnx2fc_ulp_exit(struct cnic_dev *dev)
 {
 	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface, *tmp;
+	struct fc_lport *lport;
 
 	BNX2FC_MISC_DBG("Entered bnx2fc_ulp_exit\n");
 
@@ -2089,13 +2034,20 @@
 		return;
 	}
 
-	list_del_init(&hba->link);
+	list_del_init(&hba->list);
 	adapter_count--;
 
-	if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
+	list_for_each_entry_safe(interface, tmp, &if_list, list) {
 		/* destroy not called yet, move to quiesced list */
-		bnx2fc_netdev_cleanup(hba);
-		bnx2fc_if_destroy(hba->ctlr.lp);
+		if (interface->hba == hba) {
+			bnx2fc_netdev_cleanup(interface);
+			bnx2fc_stop(interface);
+
+			list_del(&interface->list);
+			lport = interface->ctlr.lp;
+			bnx2fc_interface_put(interface);
+			bnx2fc_if_destroy(lport, hba);
+		}
 	}
 	mutex_unlock(&bnx2fc_dev_lock);
 
@@ -2103,7 +2055,7 @@
 	/* unregister cnic device */
 	if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic))
 		hba->cnic->unregister_device(hba->cnic, CNIC_ULP_FCOE);
-	bnx2fc_interface_destroy(hba);
+	bnx2fc_hba_destroy(hba);
 }
 
 /**
@@ -2259,6 +2211,7 @@
 	}
 
 	INIT_LIST_HEAD(&adapter_list);
+	INIT_LIST_HEAD(&if_list);
 	mutex_init(&bnx2fc_dev_lock);
 	adapter_count = 0;
 
@@ -2336,16 +2289,17 @@
 	mutex_unlock(&bnx2fc_dev_lock);
 
 	/* Unregister with cnic */
-	list_for_each_entry_safe(hba, next, &to_be_deleted, link) {
-		list_del_init(&hba->link);
-		printk(KERN_ERR PFX "MOD_EXIT:destroy hba = 0x%p, kref = %d\n",
-			hba, atomic_read(&hba->kref.refcount));
+	list_for_each_entry_safe(hba, next, &to_be_deleted, list) {
+		list_del_init(&hba->list);
+		printk(KERN_ERR PFX "MOD_EXIT:destroy hba = 0x%p\n",
+		       hba);
 		bnx2fc_ulp_stop(hba);
 		/* unregister cnic device */
 		if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED,
 				       &hba->reg_with_cnic))
-			hba->cnic->unregister_device(hba->cnic, CNIC_ULP_FCOE);
-		bnx2fc_interface_destroy(hba);
+			hba->cnic->unregister_device(hba->cnic,
+							 CNIC_ULP_FCOE);
+		bnx2fc_hba_destroy(hba);
 	}
 	cnic_unregister_driver(CNIC_ULP_FCOE);
 
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index 09bdd9b..72cfb14 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -2,7 +2,7 @@
  * This file contains the code that low level functions that interact
  * with 57712 FCoE firmware.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
 						struct fcoe_kcqe *ofld_kcqe);
 static void bnx2fc_init_failure(struct bnx2fc_hba *hba, u32 err_code);
 static void bnx2fc_process_conn_destroy_cmpl(struct bnx2fc_hba *hba,
-					struct fcoe_kcqe *conn_destroy);
+					struct fcoe_kcqe *destroy_kcqe);
 
 int bnx2fc_send_stat_req(struct bnx2fc_hba *hba)
 {
@@ -67,7 +67,7 @@
 	int rc = 0;
 
 	if (!hba->cnic) {
-		printk(KERN_ALERT PFX "hba->cnic NULL during fcoe fw init\n");
+		printk(KERN_ERR PFX "hba->cnic NULL during fcoe fw init\n");
 		return -ENODEV;
 	}
 
@@ -103,6 +103,7 @@
 	fcoe_init2.hsi_major_version = FCOE_HSI_MAJOR_VERSION;
 	fcoe_init2.hsi_minor_version = FCOE_HSI_MINOR_VERSION;
 
+
 	fcoe_init2.hash_tbl_pbl_addr_lo = (u32) hba->hash_tbl_pbl_dma;
 	fcoe_init2.hash_tbl_pbl_addr_hi = (u32)
 					   ((u64) hba->hash_tbl_pbl_dma >> 32);
@@ -165,7 +166,8 @@
 					struct bnx2fc_rport *tgt)
 {
 	struct fc_lport *lport = port->lport;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct kwqe *kwqe_arr[4];
 	struct fcoe_kwqe_conn_offload1 ofld_req1;
 	struct fcoe_kwqe_conn_offload2 ofld_req2;
@@ -227,7 +229,7 @@
 	ofld_req3.hdr.flags =
 		(FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
 
-	ofld_req3.vlan_tag = hba->vlan_id <<
+	ofld_req3.vlan_tag = interface->vlan_id <<
 				FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT;
 	ofld_req3.vlan_tag |= 3 << FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT;
 
@@ -277,8 +279,20 @@
 	ofld_req3.flags |= (((rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) <<
 			     FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT);
 
+	/*
+	 * Info from PRLI response, this info is used for sequence level error
+	 * recovery support
+	 */
+	if (tgt->dev_type == TYPE_TAPE) {
+		ofld_req3.flags |= 1 <<
+				    FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT;
+		ofld_req3.flags |= (((rdata->flags & FC_RP_FLAGS_REC_SUPPORTED)
+				    ? 1 : 0) <<
+				    FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT);
+	}
+
 	/* vlan flag */
-	ofld_req3.flags |= (hba->vlan_enabled <<
+	ofld_req3.flags |= (interface->vlan_enabled <<
 			    FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT);
 
 	/* C2_VALID and ACK flags are not set as they are not suppported */
@@ -300,12 +314,13 @@
 	ofld_req4.src_mac_addr_mid[1] =  port->data_src_addr[2];
 	ofld_req4.src_mac_addr_hi[0] =  port->data_src_addr[1];
 	ofld_req4.src_mac_addr_hi[1] =  port->data_src_addr[0];
-	ofld_req4.dst_mac_addr_lo[0] =  hba->ctlr.dest_addr[5];/* fcf mac */
-	ofld_req4.dst_mac_addr_lo[1] =  hba->ctlr.dest_addr[4];
-	ofld_req4.dst_mac_addr_mid[0] =  hba->ctlr.dest_addr[3];
-	ofld_req4.dst_mac_addr_mid[1] =  hba->ctlr.dest_addr[2];
-	ofld_req4.dst_mac_addr_hi[0] =  hba->ctlr.dest_addr[1];
-	ofld_req4.dst_mac_addr_hi[1] =  hba->ctlr.dest_addr[0];
+	ofld_req4.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
+							/* fcf mac */
+	ofld_req4.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
+	ofld_req4.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
+	ofld_req4.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
+	ofld_req4.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
+	ofld_req4.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
 
 	ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma;
 	ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32);
@@ -335,7 +350,8 @@
 					struct bnx2fc_rport *tgt)
 {
 	struct kwqe *kwqe_arr[2];
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct fcoe_kwqe_conn_enable_disable enbl_req;
 	struct fc_lport *lport = port->lport;
 	struct fc_rport *rport = tgt->rport;
@@ -358,12 +374,12 @@
 	enbl_req.src_mac_addr_hi[1] =  port->data_src_addr[0];
 	memcpy(tgt->src_addr, port->data_src_addr, ETH_ALEN);
 
-	enbl_req.dst_mac_addr_lo[0] =  hba->ctlr.dest_addr[5];/* fcf mac */
-	enbl_req.dst_mac_addr_lo[1] =  hba->ctlr.dest_addr[4];
-	enbl_req.dst_mac_addr_mid[0] =  hba->ctlr.dest_addr[3];
-	enbl_req.dst_mac_addr_mid[1] =  hba->ctlr.dest_addr[2];
-	enbl_req.dst_mac_addr_hi[0] =  hba->ctlr.dest_addr[1];
-	enbl_req.dst_mac_addr_hi[1] =  hba->ctlr.dest_addr[0];
+	enbl_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
+	enbl_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
+	enbl_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
+	enbl_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
+	enbl_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
+	enbl_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
 
 	port_id = fc_host_port_id(lport->host);
 	if (port_id != tgt->sid) {
@@ -379,10 +395,10 @@
 	enbl_req.d_id[0] = (port_id & 0x000000FF);
 	enbl_req.d_id[1] = (port_id & 0x0000FF00) >> 8;
 	enbl_req.d_id[2] = (port_id & 0x00FF0000) >> 16;
-	enbl_req.vlan_tag = hba->vlan_id <<
+	enbl_req.vlan_tag = interface->vlan_id <<
 				FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT;
 	enbl_req.vlan_tag |= 3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT;
-	enbl_req.vlan_flag = hba->vlan_enabled;
+	enbl_req.vlan_flag = interface->vlan_enabled;
 	enbl_req.context_id = tgt->context_id;
 	enbl_req.conn_id = tgt->fcoe_conn_id;
 
@@ -402,7 +418,8 @@
 int bnx2fc_send_session_disable_req(struct fcoe_port *port,
 				    struct bnx2fc_rport *tgt)
 {
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct fcoe_kwqe_conn_enable_disable disable_req;
 	struct kwqe *kwqe_arr[2];
 	struct fc_rport *rport = tgt->rport;
@@ -423,12 +440,12 @@
 	disable_req.src_mac_addr_hi[0] =  tgt->src_addr[1];
 	disable_req.src_mac_addr_hi[1] =  tgt->src_addr[0];
 
-	disable_req.dst_mac_addr_lo[0] =  hba->ctlr.dest_addr[5];/* fcf mac */
-	disable_req.dst_mac_addr_lo[1] =  hba->ctlr.dest_addr[4];
-	disable_req.dst_mac_addr_mid[0] =  hba->ctlr.dest_addr[3];
-	disable_req.dst_mac_addr_mid[1] =  hba->ctlr.dest_addr[2];
-	disable_req.dst_mac_addr_hi[0] =  hba->ctlr.dest_addr[1];
-	disable_req.dst_mac_addr_hi[1] =  hba->ctlr.dest_addr[0];
+	disable_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
+	disable_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
+	disable_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
+	disable_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
+	disable_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
+	disable_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
 
 	port_id = tgt->sid;
 	disable_req.s_id[0] = (port_id & 0x000000FF);
@@ -442,11 +459,11 @@
 	disable_req.d_id[2] = (port_id & 0x00FF0000) >> 16;
 	disable_req.context_id = tgt->context_id;
 	disable_req.conn_id = tgt->fcoe_conn_id;
-	disable_req.vlan_tag = hba->vlan_id <<
+	disable_req.vlan_tag = interface->vlan_id <<
 				FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT;
 	disable_req.vlan_tag |=
 			3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT;
-	disable_req.vlan_flag = hba->vlan_enabled;
+	disable_req.vlan_flag = interface->vlan_enabled;
 
 	kwqe_arr[0] = (struct kwqe *) &disable_req;
 
@@ -525,7 +542,7 @@
 {
 	struct fcoe_port *port = tgt->port;
 	struct fc_lport *lport = port->lport;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
 	struct bnx2fc_unsol_els *unsol_els;
 	struct fc_frame_header *fh;
 	struct fc_frame *fp;
@@ -586,7 +603,7 @@
 		fr_eof(fp) = FC_EOF_T;
 		fr_crc(fp) = cpu_to_le32(~crc);
 		unsol_els->lport = lport;
-		unsol_els->hba = hba;
+		unsol_els->hba = interface->hba;
 		unsol_els->fp = fp;
 		INIT_WORK(&unsol_els->unsol_els_work, bnx2fc_unsol_els_work);
 		queue_work(bnx2fc_wq, &unsol_els->unsol_els_work);
@@ -608,9 +625,12 @@
 	u32 frame_len, len;
 	struct bnx2fc_cmd *io_req = NULL;
 	struct fcoe_task_ctx_entry *task, *task_page;
-	struct bnx2fc_hba *hba = tgt->port->priv;
+	struct bnx2fc_interface *interface = tgt->port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	int task_idx, index;
 	int rc = 0;
+	u64 err_warn_bit_map;
+	u8 err_warn = 0xff;
 
 
 	BNX2FC_TGT_DBG(tgt, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe);
@@ -673,39 +693,43 @@
 		BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n",
 			err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
 
-		bnx2fc_return_rqe(tgt, 1);
 
 		if (xid > BNX2FC_MAX_XID) {
 			BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n",
 				   xid);
-			spin_unlock_bh(&tgt->tgt_lock);
-			break;
+			goto ret_err_rqe;
 		}
 
 		task_idx = xid / BNX2FC_TASKS_PER_PAGE;
 		index = xid % BNX2FC_TASKS_PER_PAGE;
 		task_page = (struct fcoe_task_ctx_entry *)
-						hba->task_ctx[task_idx];
+					hba->task_ctx[task_idx];
 		task = &(task_page[index]);
 
 		io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
-		if (!io_req) {
-			spin_unlock_bh(&tgt->tgt_lock);
-			break;
-		}
+		if (!io_req)
+			goto ret_err_rqe;
 
 		if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
 			printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
-			spin_unlock_bh(&tgt->tgt_lock);
-			break;
+			goto ret_err_rqe;
 		}
 
 		if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP,
 				       &io_req->req_flags)) {
 			BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in "
 					    "progress.. ignore unsol err\n");
-			spin_unlock_bh(&tgt->tgt_lock);
-			break;
+			goto ret_err_rqe;
+		}
+
+		err_warn_bit_map = (u64)
+			((u64)err_entry->data.err_warn_bitmap_hi << 32) |
+			(u64)err_entry->data.err_warn_bitmap_lo;
+		for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
+			if (err_warn_bit_map & (u64)((u64)1 << i)) {
+				err_warn = i;
+				break;
+			}
 		}
 
 		/*
@@ -715,26 +739,61 @@
 		 * logging out the target, when the ABTS eventually
 		 * times out.
 		 */
-		if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
-				      &io_req->req_flags)) {
-			/*
-			 * Cancel the timeout_work, as we received IO
-			 * completion with FW error.
-			 */
-			if (cancel_delayed_work(&io_req->timeout_work))
-				kref_put(&io_req->refcount,
-					 bnx2fc_cmd_release); /* timer hold */
-
-			rc = bnx2fc_initiate_abts(io_req);
-			if (rc != SUCCESS) {
-				BNX2FC_IO_DBG(io_req, "err_warn: initiate_abts "
-					"failed. issue cleanup\n");
-				rc = bnx2fc_initiate_cleanup(io_req);
-				BUG_ON(rc);
-			}
-		} else
+		if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
 			printk(KERN_ERR PFX "err_warn: io_req (0x%x) already "
 					    "in ABTS processing\n", xid);
+			goto ret_err_rqe;
+		}
+		BNX2FC_TGT_DBG(tgt, "err = 0x%x\n", err_warn);
+		if (tgt->dev_type != TYPE_TAPE)
+			goto skip_rec;
+		switch (err_warn) {
+		case FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION:
+		case FCOE_ERROR_CODE_DATA_OOO_RO:
+		case FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT:
+		case FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET:
+		case FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ:
+		case FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET:
+			BNX2FC_TGT_DBG(tgt, "REC TOV popped for xid - 0x%x\n",
+				   xid);
+			memset(&io_req->err_entry, 0,
+			       sizeof(struct fcoe_err_report_entry));
+			memcpy(&io_req->err_entry, err_entry,
+			       sizeof(struct fcoe_err_report_entry));
+			if (!test_bit(BNX2FC_FLAG_SRR_SENT,
+				      &io_req->req_flags)) {
+				spin_unlock_bh(&tgt->tgt_lock);
+				rc = bnx2fc_send_rec(io_req);
+				spin_lock_bh(&tgt->tgt_lock);
+
+				if (rc)
+					goto skip_rec;
+			} else
+				printk(KERN_ERR PFX "SRR in progress\n");
+			goto ret_err_rqe;
+			break;
+		default:
+			break;
+		}
+
+skip_rec:
+		set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags);
+		/*
+		 * Cancel the timeout_work, as we received IO
+		 * completion with FW error.
+		 */
+		if (cancel_delayed_work(&io_req->timeout_work))
+			kref_put(&io_req->refcount, bnx2fc_cmd_release);
+
+		rc = bnx2fc_initiate_abts(io_req);
+		if (rc != SUCCESS) {
+			printk(KERN_ERR PFX "err_warn: initiate_abts "
+				"failed xid = 0x%x. issue cleanup\n",
+				io_req->xid);
+			bnx2fc_initiate_cleanup(io_req);
+		}
+ret_err_rqe:
+		bnx2fc_return_rqe(tgt, 1);
 		spin_unlock_bh(&tgt->tgt_lock);
 		break;
 
@@ -755,6 +814,47 @@
 		BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x",
 			err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
 
+		if (xid > BNX2FC_MAX_XID) {
+			BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid);
+			goto ret_warn_rqe;
+		}
+
+		err_warn_bit_map = (u64)
+			((u64)err_entry->data.err_warn_bitmap_hi << 32) |
+			(u64)err_entry->data.err_warn_bitmap_lo;
+		for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
+			if (err_warn_bit_map & (u64) (1 << i)) {
+				err_warn = i;
+				break;
+			}
+		}
+		BNX2FC_TGT_DBG(tgt, "warn = 0x%x\n", err_warn);
+
+		task_idx = xid / BNX2FC_TASKS_PER_PAGE;
+		index = xid % BNX2FC_TASKS_PER_PAGE;
+		task_page = (struct fcoe_task_ctx_entry *)
+			     interface->hba->task_ctx[task_idx];
+		task = &(task_page[index]);
+		io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
+		if (!io_req)
+			goto ret_warn_rqe;
+
+		if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
+			printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
+			goto ret_warn_rqe;
+		}
+
+		memset(&io_req->err_entry, 0,
+		       sizeof(struct fcoe_err_report_entry));
+		memcpy(&io_req->err_entry, err_entry,
+		       sizeof(struct fcoe_err_report_entry));
+
+		if (err_warn == FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION)
+			/* REC_TOV is not a warning code */
+			BUG_ON(1);
+		else
+			BNX2FC_TGT_DBG(tgt, "Unsolicited warning\n");
+ret_warn_rqe:
 		bnx2fc_return_rqe(tgt, 1);
 		spin_unlock_bh(&tgt->tgt_lock);
 		break;
@@ -770,7 +870,8 @@
 	struct fcoe_task_ctx_entry *task;
 	struct fcoe_task_ctx_entry *task_page;
 	struct fcoe_port *port = tgt->port;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct bnx2fc_cmd *io_req;
 	int task_idx, index;
 	u16 xid;
@@ -781,7 +882,7 @@
 	spin_lock_bh(&tgt->tgt_lock);
 	xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID;
 	if (xid >= BNX2FC_MAX_TASKS) {
-		printk(KERN_ALERT PFX "ERROR:xid out of range\n");
+		printk(KERN_ERR PFX "ERROR:xid out of range\n");
 		spin_unlock_bh(&tgt->tgt_lock);
 		return;
 	}
@@ -861,6 +962,13 @@
 		kref_put(&io_req->refcount, bnx2fc_cmd_release);
 		break;
 
+	case BNX2FC_SEQ_CLEANUP:
+		BNX2FC_IO_DBG(io_req, "cq_compl(0x%x) - seq cleanup resp\n",
+			      io_req->xid);
+		bnx2fc_process_seq_cleanup_compl(io_req, task, rx_state);
+		kref_put(&io_req->refcount, bnx2fc_cmd_release);
+		break;
+
 	default:
 		printk(KERN_ERR PFX "Invalid cmd_type %d\n", cmd_type);
 		break;
@@ -962,8 +1070,10 @@
 				1 - tgt->cq_curr_toggle_bit;
 		}
 	}
-	bnx2fc_arm_cq(tgt);
-	atomic_add(num_free_sqes, &tgt->free_sqes);
+	if (num_free_sqes) {
+		bnx2fc_arm_cq(tgt);
+		atomic_add(num_free_sqes, &tgt->free_sqes);
+	}
 	spin_unlock_bh(&tgt->cq_lock);
 	return 0;
 }
@@ -983,7 +1093,7 @@
 	struct bnx2fc_rport *tgt = hba->tgt_ofld_list[conn_id];
 
 	if (!tgt) {
-		printk(KERN_ALERT PFX "conn_id 0x%x not valid\n", conn_id);
+		printk(KERN_ERR PFX "conn_id 0x%x not valid\n", conn_id);
 		return;
 	}
 
@@ -1004,6 +1114,7 @@
 {
 	struct bnx2fc_rport		*tgt;
 	struct fcoe_port		*port;
+	struct bnx2fc_interface		*interface;
 	u32				conn_id;
 	u32				context_id;
 	int				rc;
@@ -1018,8 +1129,9 @@
 	BNX2FC_TGT_DBG(tgt, "Entered ofld compl - context_id = 0x%x\n",
 		ofld_kcqe->fcoe_conn_context_id);
 	port = tgt->port;
-	if (hba != tgt->port->priv) {
-		printk(KERN_ALERT PFX "ERROR:ofld_cmpl: HBA mis-match\n");
+	interface = tgt->port->priv;
+	if (hba != interface->hba) {
+		printk(KERN_ERR PFX "ERROR:ofld_cmpl: HBA mis-match\n");
 		goto ofld_cmpl_err;
 	}
 	/*
@@ -1040,7 +1152,7 @@
 		/* now enable the session */
 		rc = bnx2fc_send_session_enable_req(port, tgt);
 		if (rc) {
-			printk(KERN_ALERT PFX "enable session failed\n");
+			printk(KERN_ERR PFX "enable session failed\n");
 			goto ofld_cmpl_err;
 		}
 	}
@@ -1063,6 +1175,7 @@
 						struct fcoe_kcqe *ofld_kcqe)
 {
 	struct bnx2fc_rport		*tgt;
+	struct bnx2fc_interface		*interface;
 	u32				conn_id;
 	u32				context_id;
 
@@ -1070,7 +1183,7 @@
 	conn_id = ofld_kcqe->fcoe_conn_id;
 	tgt = hba->tgt_ofld_list[conn_id];
 	if (!tgt) {
-		printk(KERN_ALERT PFX "ERROR:enbl_cmpl: No pending ofld req\n");
+		printk(KERN_ERR PFX "ERROR:enbl_cmpl: No pending ofld req\n");
 		return;
 	}
 
@@ -1082,16 +1195,17 @@
 	 * and enable
 	 */
 	if (tgt->context_id != context_id) {
-		printk(KERN_ALERT PFX "context id mis-match\n");
+		printk(KERN_ERR PFX "context id mis-match\n");
 		return;
 	}
-	if (hba != tgt->port->priv) {
-		printk(KERN_ALERT PFX "bnx2fc-enbl_cmpl: HBA mis-match\n");
+	interface = tgt->port->priv;
+	if (hba != interface->hba) {
+		printk(KERN_ERR PFX "bnx2fc-enbl_cmpl: HBA mis-match\n");
 		goto enbl_cmpl_err;
 	}
-	if (ofld_kcqe->completion_status) {
+	if (ofld_kcqe->completion_status)
 		goto enbl_cmpl_err;
-	} else {
+	else {
 		/* enable successful - rport ready for issuing IOs */
 		set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
 		set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
@@ -1114,14 +1228,14 @@
 	conn_id = disable_kcqe->fcoe_conn_id;
 	tgt = hba->tgt_ofld_list[conn_id];
 	if (!tgt) {
-		printk(KERN_ALERT PFX "ERROR: disable_cmpl: No disable req\n");
+		printk(KERN_ERR PFX "ERROR: disable_cmpl: No disable req\n");
 		return;
 	}
 
 	BNX2FC_TGT_DBG(tgt, PFX "disable_cmpl: conn_id %d\n", conn_id);
 
 	if (disable_kcqe->completion_status) {
-		printk(KERN_ALERT PFX "ERROR: Disable failed with cmpl status %d\n",
+		printk(KERN_ERR PFX "Disable failed with cmpl status %d\n",
 			disable_kcqe->completion_status);
 		return;
 	} else {
@@ -1143,14 +1257,14 @@
 	conn_id = destroy_kcqe->fcoe_conn_id;
 	tgt = hba->tgt_ofld_list[conn_id];
 	if (!tgt) {
-		printk(KERN_ALERT PFX "destroy_cmpl: No destroy req\n");
+		printk(KERN_ERR PFX "destroy_cmpl: No destroy req\n");
 		return;
 	}
 
 	BNX2FC_TGT_DBG(tgt, "destroy_cmpl: conn_id %d\n", conn_id);
 
 	if (destroy_kcqe->completion_status) {
-		printk(KERN_ALERT PFX "Destroy conn failed, cmpl status %d\n",
+		printk(KERN_ERR PFX "Destroy conn failed, cmpl status %d\n",
 			destroy_kcqe->completion_status);
 		return;
 	} else {
@@ -1182,6 +1296,7 @@
 		break;
 	case FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION:
 		printk(KERN_ERR PFX "init failure due to HSI mismatch\n");
+		break;
 	default:
 		printk(KERN_ERR PFX "Unknown Error code %d\n", err_code);
 	}
@@ -1240,7 +1355,7 @@
 			} else {
 				printk(KERN_ERR PFX "DESTROY success\n");
 			}
-			hba->flags |= BNX2FC_FLAG_DESTROY_CMPL;
+			set_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
 			wake_up_interruptible(&hba->destroy_wait);
 			break;
 
@@ -1262,7 +1377,7 @@
 		case FCOE_KCQE_OPCODE_FCOE_ERROR:
 			/* fall thru */
 		default:
-			printk(KERN_ALERT PFX "unknown opcode 0x%x\n",
+			printk(KERN_ERR PFX "unknown opcode 0x%x\n",
 								kcqe->op_code);
 		}
 	}
@@ -1305,7 +1420,8 @@
 	struct fcoe_port *port = tgt->port;
 	u32 reg_off;
 	resource_size_t reg_base;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 
 	reg_base = pci_resource_start(hba->pcidev,
 					BNX2X_DOORBELL_PCI_BAR);
@@ -1344,6 +1460,96 @@
 	tgt->conn_db->rq_prod = tgt->rq_prod_idx;
 }
 
+void bnx2fc_init_seq_cleanup_task(struct bnx2fc_cmd *seq_clnp_req,
+				  struct fcoe_task_ctx_entry *task,
+				  struct bnx2fc_cmd *orig_io_req,
+				  u32 offset)
+{
+	struct scsi_cmnd *sc_cmd = orig_io_req->sc_cmd;
+	struct bnx2fc_rport *tgt = seq_clnp_req->tgt;
+	struct bnx2fc_interface *interface = tgt->port->priv;
+	struct fcoe_bd_ctx *bd = orig_io_req->bd_tbl->bd_tbl;
+	struct fcoe_task_ctx_entry *orig_task;
+	struct fcoe_task_ctx_entry *task_page;
+	struct fcoe_ext_mul_sges_ctx *sgl;
+	u8 task_type = FCOE_TASK_TYPE_SEQUENCE_CLEANUP;
+	u8 orig_task_type;
+	u16 orig_xid = orig_io_req->xid;
+	u32 context_id = tgt->context_id;
+	u64 phys_addr = (u64)orig_io_req->bd_tbl->bd_tbl_dma;
+	u32 orig_offset = offset;
+	int bd_count;
+	int orig_task_idx, index;
+	int i;
+
+	memset(task, 0, sizeof(struct fcoe_task_ctx_entry));
+
+	if (sc_cmd->sc_data_direction == DMA_TO_DEVICE)
+		orig_task_type = FCOE_TASK_TYPE_WRITE;
+	else
+		orig_task_type = FCOE_TASK_TYPE_READ;
+
+	/* Tx flags */
+	task->txwr_rxrd.const_ctx.tx_flags =
+				FCOE_TASK_TX_STATE_SEQUENCE_CLEANUP <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT;
+	/* init flags */
+	task->txwr_rxrd.const_ctx.init_flags = task_type <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
+	task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
+	task->rxwr_txrd.const_ctx.init_flags = context_id <<
+				FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+	task->rxwr_txrd.const_ctx.init_flags = context_id <<
+				FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+
+	task->txwr_rxrd.union_ctx.cleanup.ctx.cleaned_task_id = orig_xid;
+
+	task->txwr_rxrd.union_ctx.cleanup.ctx.rolled_tx_seq_cnt = 0;
+	task->txwr_rxrd.union_ctx.cleanup.ctx.rolled_tx_data_offset = offset;
+
+	bd_count = orig_io_req->bd_tbl->bd_valid;
+
+	/* obtain the appropriate bd entry from relative offset */
+	for (i = 0; i < bd_count; i++) {
+		if (offset < bd[i].buf_len)
+			break;
+		offset -= bd[i].buf_len;
+	}
+	phys_addr += (i * sizeof(struct fcoe_bd_ctx));
+
+	if (orig_task_type == FCOE_TASK_TYPE_WRITE) {
+		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
+				(u32)phys_addr;
+		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
+				(u32)((u64)phys_addr >> 32);
+		task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size =
+				bd_count;
+		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_off =
+				offset; /* adjusted offset */
+		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_idx = i;
+	} else {
+		orig_task_idx = orig_xid / BNX2FC_TASKS_PER_PAGE;
+		index = orig_xid % BNX2FC_TASKS_PER_PAGE;
+
+		task_page = (struct fcoe_task_ctx_entry *)
+			     interface->hba->task_ctx[orig_task_idx];
+		orig_task = &(task_page[index]);
+
+		/* Multiple SGEs were used for this IO */
+		sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl;
+		sgl->mul_sgl.cur_sge_addr.lo = (u32)phys_addr;
+		sgl->mul_sgl.cur_sge_addr.hi = (u32)((u64)phys_addr >> 32);
+		sgl->mul_sgl.sgl_size = bd_count;
+		sgl->mul_sgl.cur_sge_off = offset; /*adjusted offset */
+		sgl->mul_sgl.cur_sge_idx = i;
+
+		memset(&task->rxwr_only.rx_seq_ctx, 0,
+		       sizeof(struct fcoe_rx_seq_ctx));
+		task->rxwr_only.rx_seq_ctx.low_exp_ro = orig_offset;
+		task->rxwr_only.rx_seq_ctx.high_exp_ro = orig_offset;
+	}
+}
 void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
 			      struct fcoe_task_ctx_entry *task,
 			      u16 orig_xid)
@@ -1360,7 +1566,12 @@
 				FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
 	task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
-	task->txwr_rxrd.const_ctx.init_flags |=
+	if (tgt->dev_type == TYPE_TAPE)
+		task->txwr_rxrd.const_ctx.init_flags |=
+				FCOE_TASK_DEV_TYPE_TAPE <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+	else
+		task->txwr_rxrd.const_ctx.init_flags |=
 				FCOE_TASK_DEV_TYPE_DISK <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
 	task->txwr_rxrd.union_ctx.cleanup.ctx.cleaned_task_id = orig_xid;
@@ -1420,7 +1631,12 @@
 	/* init flags */
 	task->txwr_rxrd.const_ctx.init_flags = task_type <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
-	task->txwr_rxrd.const_ctx.init_flags |=
+	if (tgt->dev_type == TYPE_TAPE)
+		task->txwr_rxrd.const_ctx.init_flags |=
+				FCOE_TASK_DEV_TYPE_TAPE <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+	else
+		task->txwr_rxrd.const_ctx.init_flags |=
 				FCOE_TASK_DEV_TYPE_DISK <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
 	task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
@@ -1477,6 +1693,7 @@
 	struct bnx2fc_rport *tgt = io_req->tgt;
 	struct fcoe_cached_sge_ctx *cached_sge;
 	struct fcoe_ext_mul_sges_ctx *sgl;
+	int dev_type = tgt->dev_type;
 	u64 *fcp_cmnd;
 	u64 tmp_fcp_cmnd[4];
 	u32 context_id;
@@ -1494,20 +1711,40 @@
 		task_type = FCOE_TASK_TYPE_READ;
 
 	/* Tx only */
+	bd_count = bd_tbl->bd_valid;
 	if (task_type == FCOE_TASK_TYPE_WRITE) {
-		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
-				(u32)bd_tbl->bd_tbl_dma;
-		task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
-				(u32)((u64)bd_tbl->bd_tbl_dma >> 32);
-		task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size =
-				bd_tbl->bd_valid;
+		if ((dev_type == TYPE_DISK) && (bd_count == 1)) {
+			struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl;
+
+			task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.lo =
+					fcoe_bd_tbl->buf_addr_lo;
+			task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.hi =
+					fcoe_bd_tbl->buf_addr_hi;
+			task->txwr_only.sgl_ctx.cached_sge.cur_buf_rem =
+					fcoe_bd_tbl->buf_len;
+
+			task->txwr_rxrd.const_ctx.init_flags |= 1 <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT;
+		} else {
+			task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
+					(u32)bd_tbl->bd_tbl_dma;
+			task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
+					(u32)((u64)bd_tbl->bd_tbl_dma >> 32);
+			task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size =
+					bd_tbl->bd_valid;
+		}
 	}
 
 	/*Tx Write Rx Read */
 	/* Init state to NORMAL */
-	task->txwr_rxrd.const_ctx.init_flags = task_type <<
+	task->txwr_rxrd.const_ctx.init_flags |= task_type <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
-	task->txwr_rxrd.const_ctx.init_flags |=
+	if (dev_type == TYPE_TAPE)
+		task->txwr_rxrd.const_ctx.init_flags |=
+				FCOE_TASK_DEV_TYPE_TAPE <<
+				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+	else
+		task->txwr_rxrd.const_ctx.init_flags |=
 				FCOE_TASK_DEV_TYPE_DISK <<
 				FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
 	task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
@@ -1550,7 +1787,8 @@
 	cached_sge = &task->rxwr_only.union_ctx.read_info.sgl_ctx.cached_sge;
 	sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl;
 	bd_count = bd_tbl->bd_valid;
-	if (task_type == FCOE_TASK_TYPE_READ) {
+	if (task_type == FCOE_TASK_TYPE_READ &&
+	    dev_type == TYPE_DISK) {
 		if (bd_count == 1) {
 
 			struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl;
@@ -1582,6 +1820,11 @@
 					(u32)((u64)bd_tbl->bd_tbl_dma >> 32);
 			sgl->mul_sgl.sgl_size = bd_count;
 		}
+	} else {
+		sgl->mul_sgl.cur_sge_addr.lo = (u32)bd_tbl->bd_tbl_dma;
+		sgl->mul_sgl.cur_sge_addr.hi =
+				(u32)((u64)bd_tbl->bd_tbl_dma >> 32);
+		sgl->mul_sgl.sgl_size = bd_count;
 	}
 }
 
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index 45eba6d..6cc3789 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1,7 +1,7 @@
 /* bnx2fc_io.c: Broadcom NetXtreme II Linux FCoE offload driver.
  * IO manager and SCSI IO processing.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,8 +18,6 @@
 			   int bd_index);
 static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req);
 static void bnx2fc_build_bd_list_from_sg(struct bnx2fc_cmd *io_req);
-static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
-			       struct bnx2fc_cmd *io_req);
 static void bnx2fc_unmap_sg_list(struct bnx2fc_cmd *io_req);
 static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req);
 static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
@@ -29,10 +27,11 @@
 void bnx2fc_cmd_timer_set(struct bnx2fc_cmd *io_req,
 			  unsigned int timer_msec)
 {
-	struct bnx2fc_hba *hba = io_req->port->priv;
+	struct bnx2fc_interface *interface = io_req->port->priv;
 
-	if (queue_delayed_work(hba->timer_work_queue, &io_req->timeout_work,
-				  msecs_to_jiffies(timer_msec)))
+	if (queue_delayed_work(interface->timer_work_queue,
+			       &io_req->timeout_work,
+			       msecs_to_jiffies(timer_msec)))
 		kref_get(&io_req->refcount);
 }
 
@@ -217,6 +216,11 @@
 		return;
 
 	BNX2FC_IO_DBG(io_req, "scsi_done. err_code = 0x%x\n", err_code);
+	if (test_bit(BNX2FC_FLAG_CMD_LOST, &io_req->req_flags)) {
+		/* Do not call scsi done for this IO */
+		return;
+	}
+
 	bnx2fc_unmap_sg_list(io_req);
 	io_req->sc_cmd = NULL;
 	if (!sc_cmd) {
@@ -419,8 +423,8 @@
 struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type)
 {
 	struct fcoe_port *port = tgt->port;
-	struct bnx2fc_hba *hba = port->priv;
-	struct bnx2fc_cmd_mgr *cmd_mgr = hba->cmd_mgr;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_cmd_mgr *cmd_mgr = interface->hba->cmd_mgr;
 	struct bnx2fc_cmd *io_req;
 	struct list_head *listp;
 	struct io_bdt *bd_tbl;
@@ -485,11 +489,12 @@
 	kref_init(&io_req->refcount);
 	return io_req;
 }
-static struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
+
+struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
 {
 	struct fcoe_port *port = tgt->port;
-	struct bnx2fc_hba *hba = port->priv;
-	struct bnx2fc_cmd_mgr *cmd_mgr = hba->cmd_mgr;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_cmd_mgr *cmd_mgr = interface->hba->cmd_mgr;
 	struct bnx2fc_cmd *io_req;
 	struct list_head *listp;
 	struct io_bdt *bd_tbl;
@@ -570,7 +575,8 @@
 static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req)
 {
 	struct bnx2fc_mp_req *mp_req = &(io_req->mp_req);
-	struct bnx2fc_hba *hba = io_req->port->priv;
+	struct bnx2fc_interface *interface = io_req->port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	size_t sz = sizeof(struct fcoe_bd_ctx);
 
 	/* clear tm flags */
@@ -606,7 +612,8 @@
 	struct bnx2fc_mp_req *mp_req;
 	struct fcoe_bd_ctx *mp_req_bd;
 	struct fcoe_bd_ctx *mp_resp_bd;
-	struct bnx2fc_hba *hba = io_req->port->priv;
+	struct bnx2fc_interface *interface = io_req->port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	dma_addr_t addr;
 	size_t sz;
 
@@ -682,7 +689,7 @@
 	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
 	struct fc_rport_libfc_priv *rp = rport->dd_data;
 	struct fcoe_port *port;
-	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct bnx2fc_rport *tgt;
 	struct bnx2fc_cmd *io_req;
 	struct bnx2fc_mp_req *tm_req;
@@ -699,10 +706,10 @@
 
 	lport = shost_priv(host);
 	port = lport_priv(lport);
-	hba = port->priv;
+	interface = port->priv;
 
 	if (rport == NULL) {
-		printk(KERN_ALERT PFX "device_reset: rport is NULL\n");
+		printk(KERN_ERR PFX "device_reset: rport is NULL\n");
 		rc = FAILED;
 		goto tmf_err;
 	}
@@ -745,7 +752,9 @@
 	rc = bnx2fc_init_mp_req(io_req);
 	if (rc == FAILED) {
 		printk(KERN_ERR PFX "Task mgmt MP request init failed\n");
+		spin_lock_bh(&tgt->tgt_lock);
 		kref_put(&io_req->refcount, bnx2fc_cmd_release);
+		spin_unlock_bh(&tgt->tgt_lock);
 		goto tmf_err;
 	}
 
@@ -774,7 +783,8 @@
 	index = xid % BNX2FC_TASKS_PER_PAGE;
 
 	/* Initialize task context for this IO request */
-	task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+	task_page = (struct fcoe_task_ctx_entry *)
+			interface->hba->task_ctx[task_idx];
 	task = &(task_page[index]);
 	bnx2fc_init_mp_task(io_req, task);
 
@@ -806,10 +816,10 @@
 	spin_unlock_bh(&tgt->tgt_lock);
 
 	if (!rc) {
-		printk(KERN_ERR PFX "task mgmt command failed...\n");
+		BNX2FC_TGT_DBG(tgt, "task mgmt command failed...\n");
 		rc = FAILED;
 	} else {
-		printk(KERN_ERR PFX "task mgmt command success...\n");
+		BNX2FC_TGT_DBG(tgt, "task mgmt command success...\n");
 		rc = SUCCESS;
 	}
 tmf_err:
@@ -822,7 +832,7 @@
 	struct bnx2fc_rport *tgt = io_req->tgt;
 	struct fc_rport *rport = tgt->rport;
 	struct fc_rport_priv *rdata = tgt->rdata;
-	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct fcoe_port *port;
 	struct bnx2fc_cmd *abts_io_req;
 	struct fcoe_task_ctx_entry *task;
@@ -839,7 +849,7 @@
 	BNX2FC_IO_DBG(io_req, "Entered bnx2fc_initiate_abts\n");
 
 	port = io_req->port;
-	hba = port->priv;
+	interface = port->priv;
 	lport = port->lport;
 
 	if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
@@ -849,7 +859,7 @@
 	}
 
 	if (rport == NULL) {
-		printk(KERN_ALERT PFX "initiate_abts: rport is NULL\n");
+		printk(KERN_ERR PFX "initiate_abts: rport is NULL\n");
 		rc = FAILED;
 		goto abts_err;
 	}
@@ -896,7 +906,8 @@
 	index = xid % BNX2FC_TASKS_PER_PAGE;
 
 	/* Initialize task context for this IO request */
-	task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+	task_page = (struct fcoe_task_ctx_entry *)
+			interface->hba->task_ctx[task_idx];
 	task = &(task_page[index]);
 	bnx2fc_init_mp_task(abts_io_req, task);
 
@@ -924,11 +935,81 @@
 	return rc;
 }
 
+int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset,
+				enum fc_rctl r_ctl)
+{
+	struct fc_lport *lport;
+	struct bnx2fc_rport *tgt = orig_io_req->tgt;
+	struct bnx2fc_interface *interface;
+	struct fcoe_port *port;
+	struct bnx2fc_cmd *seq_clnp_req;
+	struct fcoe_task_ctx_entry *task;
+	struct fcoe_task_ctx_entry *task_page;
+	struct bnx2fc_els_cb_arg *cb_arg = NULL;
+	int task_idx, index;
+	u16 xid;
+	int rc = 0;
+
+	BNX2FC_IO_DBG(orig_io_req, "bnx2fc_initiate_seq_cleanup xid = 0x%x\n",
+		   orig_io_req->xid);
+	kref_get(&orig_io_req->refcount);
+
+	port = orig_io_req->port;
+	interface = port->priv;
+	lport = port->lport;
+
+	cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+	if (!cb_arg) {
+		printk(KERN_ERR PFX "Unable to alloc cb_arg for seq clnup\n");
+		rc = -ENOMEM;
+		goto cleanup_err;
+	}
+
+	seq_clnp_req = bnx2fc_elstm_alloc(tgt, BNX2FC_SEQ_CLEANUP);
+	if (!seq_clnp_req) {
+		printk(KERN_ERR PFX "cleanup: couldnt allocate cmd\n");
+		rc = -ENOMEM;
+		kfree(cb_arg);
+		goto cleanup_err;
+	}
+	/* Initialize rest of io_req fields */
+	seq_clnp_req->sc_cmd = NULL;
+	seq_clnp_req->port = port;
+	seq_clnp_req->tgt = tgt;
+	seq_clnp_req->data_xfer_len = 0; /* No data transfer for cleanup */
+
+	xid = seq_clnp_req->xid;
+
+	task_idx = xid/BNX2FC_TASKS_PER_PAGE;
+	index = xid % BNX2FC_TASKS_PER_PAGE;
+
+	/* Initialize task context for this IO request */
+	task_page = (struct fcoe_task_ctx_entry *)
+		     interface->hba->task_ctx[task_idx];
+	task = &(task_page[index]);
+	cb_arg->aborted_io_req = orig_io_req;
+	cb_arg->io_req = seq_clnp_req;
+	cb_arg->r_ctl = r_ctl;
+	cb_arg->offset = offset;
+	seq_clnp_req->cb_arg = cb_arg;
+
+	printk(KERN_ERR PFX "call init_seq_cleanup_task\n");
+	bnx2fc_init_seq_cleanup_task(seq_clnp_req, task, orig_io_req, offset);
+
+	/* Obtain free SQ entry */
+	bnx2fc_add_2_sq(tgt, xid);
+
+	/* Ring doorbell */
+	bnx2fc_ring_doorbell(tgt);
+cleanup_err:
+	return rc;
+}
+
 int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req)
 {
 	struct fc_lport *lport;
 	struct bnx2fc_rport *tgt = io_req->tgt;
-	struct bnx2fc_hba *hba;
+	struct bnx2fc_interface *interface;
 	struct fcoe_port *port;
 	struct bnx2fc_cmd *cleanup_io_req;
 	struct fcoe_task_ctx_entry *task;
@@ -941,7 +1022,7 @@
 	BNX2FC_IO_DBG(io_req, "Entered bnx2fc_initiate_cleanup\n");
 
 	port = io_req->port;
-	hba = port->priv;
+	interface = port->priv;
 	lport = port->lport;
 
 	cleanup_io_req = bnx2fc_elstm_alloc(tgt, BNX2FC_CLEANUP);
@@ -963,7 +1044,8 @@
 	index = xid % BNX2FC_TASKS_PER_PAGE;
 
 	/* Initialize task context for this IO request */
-	task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+	task_page = (struct fcoe_task_ctx_entry *)
+			interface->hba->task_ctx[task_idx];
 	task = &(task_page[index]);
 	orig_xid = io_req->xid;
 
@@ -1031,7 +1113,7 @@
 
 	lport = shost_priv(sc_cmd->device->host);
 	if ((lport->state != LPORT_ST_READY) || !(lport->link_up)) {
-		printk(KERN_ALERT PFX "eh_abort: link not ready\n");
+		printk(KERN_ERR PFX "eh_abort: link not ready\n");
 		return rc;
 	}
 
@@ -1062,7 +1144,7 @@
 	 * io_req is no longer in the active_q.
 	 */
 	if (tgt->flush_in_prog) {
-		printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
+		printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
 			"flush in progress\n", io_req->xid);
 		kref_put(&io_req->refcount, bnx2fc_cmd_release);
 		spin_unlock_bh(&tgt->tgt_lock);
@@ -1070,7 +1152,7 @@
 	}
 
 	if (io_req->on_active_queue == 0) {
-		printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
+		printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
 				"not on active_q\n", io_req->xid);
 		/*
 		 * This condition can happen only due to the FW bug,
@@ -1108,7 +1190,7 @@
 		set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
 		rc = bnx2fc_initiate_abts(io_req);
 	} else {
-		printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
+		printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
 				"already in abts processing\n", io_req->xid);
 		kref_put(&io_req->refcount, bnx2fc_cmd_release);
 		spin_unlock_bh(&tgt->tgt_lock);
@@ -1149,6 +1231,42 @@
 	return rc;
 }
 
+void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnp_req,
+				      struct fcoe_task_ctx_entry *task,
+				      u8 rx_state)
+{
+	struct bnx2fc_els_cb_arg *cb_arg = seq_clnp_req->cb_arg;
+	struct bnx2fc_cmd *orig_io_req = cb_arg->aborted_io_req;
+	u32 offset = cb_arg->offset;
+	enum fc_rctl r_ctl = cb_arg->r_ctl;
+	int rc = 0;
+	struct bnx2fc_rport *tgt = orig_io_req->tgt;
+
+	BNX2FC_IO_DBG(orig_io_req, "Entered process_cleanup_compl xid = 0x%x"
+			      "cmd_type = %d\n",
+		   seq_clnp_req->xid, seq_clnp_req->cmd_type);
+
+	if (rx_state == FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP) {
+		printk(KERN_ERR PFX "seq cleanup ignored - xid = 0x%x\n",
+			seq_clnp_req->xid);
+		goto free_cb_arg;
+	}
+	kref_get(&orig_io_req->refcount);
+
+	spin_unlock_bh(&tgt->tgt_lock);
+	rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl);
+	spin_lock_bh(&tgt->tgt_lock);
+
+	if (rc)
+		printk(KERN_ERR PFX "clnup_compl: Unable to send SRR"
+			" IO will abort\n");
+	seq_clnp_req->cb_arg = NULL;
+	kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+free_cb_arg:
+	kfree(cb_arg);
+	return;
+}
+
 void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req,
 				  struct fcoe_task_ctx_entry *task,
 				  u8 num_rq)
@@ -1378,7 +1496,7 @@
 			fc_hdr->fh_r_ctl);
 	}
 	if (!sc_cmd->SCp.ptr) {
-		printk(KERN_ALERT PFX "tm_compl: SCp.ptr is NULL\n");
+		printk(KERN_ERR PFX "tm_compl: SCp.ptr is NULL\n");
 		return;
 	}
 	switch (io_req->fcp_status) {
@@ -1410,7 +1528,7 @@
 		io_req->on_tmf_queue = 0;
 	} else {
 
-		printk(KERN_ALERT PFX "Command not on active_cmd_queue!\n");
+		printk(KERN_ERR PFX "Command not on active_cmd_queue!\n");
 		return;
 	}
 
@@ -1597,7 +1715,7 @@
 
 		if (rq_buff_len > num_rq * BNX2FC_RQ_BUF_SZ) {
 			/* Invalid sense sense length. */
-			printk(KERN_ALERT PFX "invalid sns length %d\n",
+			printk(KERN_ERR PFX "invalid sns length %d\n",
 				rq_buff_len);
 			/* reset rq_buff_len */
 			rq_buff_len =  num_rq * BNX2FC_RQ_BUF_SZ;
@@ -1780,7 +1898,7 @@
 			scsi_set_resid(sc_cmd, io_req->fcp_resid);
 		break;
 	default:
-		printk(KERN_ALERT PFX "scsi_cmd_compl: fcp_status = %d\n",
+		printk(KERN_ERR PFX "scsi_cmd_compl: fcp_status = %d\n",
 			io_req->fcp_status);
 		break;
 	}
@@ -1789,14 +1907,15 @@
 	kref_put(&io_req->refcount, bnx2fc_cmd_release);
 }
 
-static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
+int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
 			       struct bnx2fc_cmd *io_req)
 {
 	struct fcoe_task_ctx_entry *task;
 	struct fcoe_task_ctx_entry *task_page;
 	struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
 	struct fcoe_port *port = tgt->port;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct fc_lport *lport = port->lport;
 	struct fcoe_dev_stats *stats;
 	int task_idx, index;
@@ -1854,7 +1973,8 @@
 	}
 
 	/* Time IO req */
-	bnx2fc_cmd_timer_set(io_req, BNX2FC_IO_TIMEOUT);
+	if (tgt->io_timeout)
+		bnx2fc_cmd_timer_set(io_req, BNX2FC_IO_TIMEOUT);
 	/* Obtain free SQ entry */
 	bnx2fc_add_2_sq(tgt, xid);
 
diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
index 3e892bd..d5311b5 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
@@ -2,7 +2,7 @@
  * Handles operations such as session offload/upload etc, and manages
  * session resources such as connection id and qp resources.
  *
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008 - 2011 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -65,7 +65,8 @@
 {
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_rport *rport = rdata->rport;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	int rval;
 	int i = 0;
 
@@ -237,7 +238,8 @@
 static void bnx2fc_upload_session(struct fcoe_port *port,
 					struct bnx2fc_rport *tgt)
 {
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 
 	BNX2FC_TGT_DBG(tgt, "upload_session: active_ios = %d\n",
 		tgt->num_active_ios.counter);
@@ -316,7 +318,8 @@
 {
 
 	struct fc_rport *rport = rdata->rport;
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct b577xx_doorbell_set_prod *sq_db = &tgt->sq_db;
 	struct b577xx_fcoe_rx_doorbell *rx_db = &tgt->rx_db;
 
@@ -350,6 +353,14 @@
 	tgt->rq_cons_idx = 0;
 	atomic_set(&tgt->num_active_ios, 0);
 
+	if (rdata->flags & FC_RP_FLAGS_RETRY) {
+		tgt->dev_type = TYPE_TAPE;
+		tgt->io_timeout = 0; /* use default ULP timeout */
+	} else {
+		tgt->dev_type = TYPE_DISK;
+		tgt->io_timeout = BNX2FC_IO_TIMEOUT;
+	}
+
 	/* initialize sq doorbell */
 	sq_db->header.header = B577XX_DOORBELL_HDR_DB_TYPE;
 	sq_db->header.header |= B577XX_FCOE_CONNECTION_TYPE <<
@@ -392,7 +403,8 @@
 				enum fc_rport_event event)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct fc_rport *rport = rdata->rport;
 	struct fc_rport_libfc_priv *rp;
 	struct bnx2fc_rport *tgt;
@@ -403,7 +415,7 @@
 	switch (event) {
 	case RPORT_EV_READY:
 		if (!rport) {
-			printk(KERN_ALERT PFX "rport is NULL: ERROR!\n");
+			printk(KERN_ERR PFX "rport is NULL: ERROR!\n");
 			break;
 		}
 
@@ -415,7 +427,7 @@
 			 * We should not come here, as lport will
 			 * take care of fabric login
 			 */
-			printk(KERN_ALERT PFX "%x - rport_event_handler ERROR\n",
+			printk(KERN_ERR PFX "%x - rport_event_handler ERROR\n",
 				rdata->ids.port_id);
 			break;
 		}
@@ -483,7 +495,7 @@
 			break;
 
 		if (!rport) {
-			printk(KERN_ALERT PFX "%x - rport not created Yet!!\n",
+			printk(KERN_INFO PFX "%x - rport not created Yet!!\n",
 				port_id);
 			break;
 		}
@@ -537,7 +549,8 @@
 struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
 					     u32 port_id)
 {
-	struct bnx2fc_hba *hba = port->priv;
+	struct bnx2fc_interface *interface = port->priv;
+	struct bnx2fc_hba *hba = interface->hba;
 	struct bnx2fc_rport *tgt;
 	struct fc_rport_priv *rdata;
 	int i;
@@ -552,7 +565,7 @@
 						"obtained\n");
 					return tgt;
 				} else {
-					printk(KERN_ERR PFX "rport 0x%x "
+					BNX2FC_TGT_DBG(tgt, "rport 0x%x "
 						"is in DELETED state\n",
 						rdata->ids.port_id);
 					return NULL;
@@ -633,7 +646,7 @@
 	tgt->sq = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
 				     &tgt->sq_dma, GFP_KERNEL);
 	if (!tgt->sq) {
-		printk(KERN_ALERT PFX "unable to allocate SQ memory %d\n",
+		printk(KERN_ERR PFX "unable to allocate SQ memory %d\n",
 			tgt->sq_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -646,7 +659,7 @@
 	tgt->cq = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
 				     &tgt->cq_dma, GFP_KERNEL);
 	if (!tgt->cq) {
-		printk(KERN_ALERT PFX "unable to allocate CQ memory %d\n",
+		printk(KERN_ERR PFX "unable to allocate CQ memory %d\n",
 			tgt->cq_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -659,7 +672,7 @@
 	tgt->rq = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
 					&tgt->rq_dma, GFP_KERNEL);
 	if (!tgt->rq) {
-		printk(KERN_ALERT PFX "unable to allocate RQ memory %d\n",
+		printk(KERN_ERR PFX "unable to allocate RQ memory %d\n",
 			tgt->rq_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -671,7 +684,7 @@
 	tgt->rq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
 					 &tgt->rq_pbl_dma, GFP_KERNEL);
 	if (!tgt->rq_pbl) {
-		printk(KERN_ALERT PFX "unable to allocate RQ PBL %d\n",
+		printk(KERN_ERR PFX "unable to allocate RQ PBL %d\n",
 			tgt->rq_pbl_size);
 		goto mem_alloc_failure;
 	}
@@ -697,7 +710,7 @@
 	tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
 					&tgt->xferq_dma, GFP_KERNEL);
 	if (!tgt->xferq) {
-		printk(KERN_ALERT PFX "unable to allocate XFERQ %d\n",
+		printk(KERN_ERR PFX "unable to allocate XFERQ %d\n",
 			tgt->xferq_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -711,7 +724,7 @@
 	tgt->confq = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
 					&tgt->confq_dma, GFP_KERNEL);
 	if (!tgt->confq) {
-		printk(KERN_ALERT PFX "unable to allocate CONFQ %d\n",
+		printk(KERN_ERR PFX "unable to allocate CONFQ %d\n",
 			tgt->confq_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -726,7 +739,7 @@
 					    tgt->confq_pbl_size,
 					    &tgt->confq_pbl_dma, GFP_KERNEL);
 	if (!tgt->confq_pbl) {
-		printk(KERN_ALERT PFX "unable to allocate CONFQ PBL %d\n",
+		printk(KERN_ERR PFX "unable to allocate CONFQ PBL %d\n",
 			tgt->confq_pbl_size);
 		goto mem_alloc_failure;
 	}
@@ -751,7 +764,7 @@
 					  tgt->conn_db_mem_size,
 					  &tgt->conn_db_dma, GFP_KERNEL);
 	if (!tgt->conn_db) {
-		printk(KERN_ALERT PFX "unable to allocate conn_db %d\n",
+		printk(KERN_ERR PFX "unable to allocate conn_db %d\n",
 						tgt->conn_db_mem_size);
 		goto mem_alloc_failure;
 	}
@@ -767,7 +780,7 @@
 				      &tgt->lcq_dma, GFP_KERNEL);
 
 	if (!tgt->lcq) {
-		printk(KERN_ALERT PFX "unable to allocate lcq %d\n",
+		printk(KERN_ERR PFX "unable to allocate lcq %d\n",
 		       tgt->lcq_mem_size);
 		goto mem_alloc_failure;
 	}
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 030a96c..9ae80cd 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -332,11 +332,11 @@
 {
 	struct bnx2i_cmd *bnx2i_cmd;
 	struct bnx2i_login_request *login_wqe;
-	struct iscsi_login *login_hdr;
+	struct iscsi_login_req *login_hdr;
 	u32 dword;
 
 	bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
-	login_hdr = (struct iscsi_login *)task->hdr;
+	login_hdr = (struct iscsi_login_req *)task->hdr;
 	login_wqe = (struct bnx2i_login_request *)
 						bnx2i_conn->ep->qp.sq_prod_qe;
 
@@ -1349,7 +1349,7 @@
 	struct bnx2i_cmd_response *resp_cqe;
 	struct bnx2i_cmd *bnx2i_cmd;
 	struct iscsi_task *task;
-	struct iscsi_cmd_rsp *hdr;
+	struct iscsi_scsi_rsp *hdr;
 	u32 datalen = 0;
 
 	resp_cqe = (struct bnx2i_cmd_response *)cqe;
@@ -1376,7 +1376,7 @@
 	}
 	bnx2i_iscsi_unmap_sg_list(bnx2i_cmd);
 
-	hdr = (struct iscsi_cmd_rsp *)task->hdr;
+	hdr = (struct iscsi_scsi_rsp *)task->hdr;
 	resp_cqe = (struct bnx2i_cmd_response *)cqe;
 	hdr->opcode = resp_cqe->op_code;
 	hdr->max_cmdsn = cpu_to_be32(resp_cqe->max_cmd_sn);
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 5c55a75..cffd4d7 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1213,7 +1213,7 @@
 	struct bnx2i_conn *bnx2i_conn = conn->dd_data;
 	struct scsi_cmnd *sc = task->sc;
 	struct bnx2i_cmd *cmd = task->dd_data;
-	struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
+	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
 
 	if (atomic_read(&bnx2i_conn->ep->num_active_cmds) + 1  >
 	    hba->max_sqes)
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 2e7c136..27c9d65 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -128,25 +128,7 @@
 	u8	reserved[2];
 };
 
-struct rdac_controller {
-	u8			subsys_id[SUBSYS_ID_LEN];
-	u8			slot_id[SLOT_ID_LEN];
-	int			use_ms10;
-	struct kref		kref;
-	struct list_head	node; /* list of all controllers */
-	union			{
-		struct rdac_pg_legacy legacy;
-		struct rdac_pg_expanded expanded;
-	} mode_select;
-	u8	index;
-	u8	array_name[ARRAY_LABEL_LEN];
-	spinlock_t		ms_lock;
-	int			ms_queued;
-	struct work_struct	ms_work;
-	struct scsi_device	*ms_sdev;
-	struct list_head	ms_head;
-};
-
+#define UNIQUE_ID_LEN 16
 struct c8_inquiry {
 	u8	peripheral_info;
 	u8	page_code; /* 0xC8 */
@@ -159,12 +141,31 @@
 	u8	vol_user_label_len;
 	u8	vol_user_label[60];
 	u8	array_uniq_id_len;
-	u8	array_unique_id[16];
+	u8	array_unique_id[UNIQUE_ID_LEN];
 	u8	array_user_label_len;
 	u8	array_user_label[60];
 	u8	lun[8];
 };
 
+struct rdac_controller {
+	u8			array_id[UNIQUE_ID_LEN];
+	int			use_ms10;
+	struct kref		kref;
+	struct list_head	node; /* list of all controllers */
+	union			{
+		struct rdac_pg_legacy legacy;
+		struct rdac_pg_expanded expanded;
+	} mode_select;
+	u8	index;
+	u8	array_name[ARRAY_LABEL_LEN];
+	struct Scsi_Host	*host;
+	spinlock_t		ms_lock;
+	int			ms_queued;
+	struct work_struct	ms_work;
+	struct scsi_device	*ms_sdev;
+	struct list_head	ms_head;
+};
+
 struct c2_inquiry {
 	u8	peripheral_info;
 	u8	page_code;	/* 0xC2 */
@@ -369,16 +370,17 @@
 	kfree(ctlr);
 }
 
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
-						char *array_name)
+static struct rdac_controller *get_controller(int index, char *array_name,
+			u8 *array_id, struct scsi_device *sdev)
 {
 	struct rdac_controller *ctlr, *tmp;
 
 	spin_lock(&list_lock);
 
 	list_for_each_entry(tmp, &ctlr_list, node) {
-		if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
-			  (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
+		if ((memcmp(tmp->array_id, array_id, UNIQUE_ID_LEN) == 0) &&
+			  (tmp->index == index) &&
+			  (tmp->host == sdev->host)) {
 			kref_get(&tmp->kref);
 			spin_unlock(&list_lock);
 			return tmp;
@@ -389,16 +391,11 @@
 		goto done;
 
 	/* initialize fields of controller */
-	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
-	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+	memcpy(ctlr->array_id, array_id, UNIQUE_ID_LEN);
+	ctlr->index = index;
+	ctlr->host = sdev->host;
 	memcpy(ctlr->array_name, array_name, ARRAY_LABEL_LEN);
 
-	/* update the controller index */
-	if (slot_id[1] == 0x31)
-		ctlr->index = 0;
-	else
-		ctlr->index = 1;
-
 	kref_init(&ctlr->kref);
 	ctlr->use_ms10 = -1;
 	ctlr->ms_queued = 0;
@@ -444,7 +441,7 @@
 }
 
 static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h,
-			char *array_name)
+			char *array_name, u8 *array_id)
 {
 	int err, i;
 	struct c8_inquiry *inqp;
@@ -463,6 +460,8 @@
 			*(array_name+i) = inqp->array_user_label[(2*i)+1];
 
 		*(array_name+ARRAY_LABEL_LEN-1) = '\0';
+		memset(array_id, 0, UNIQUE_ID_LEN);
+		memcpy(array_id, inqp->array_unique_id, inqp->array_uniq_id_len);
 	}
 	return err;
 }
@@ -504,16 +503,20 @@
 }
 
 static int initialize_controller(struct scsi_device *sdev,
-				 struct rdac_dh_data *h, char *array_name)
+		struct rdac_dh_data *h, char *array_name, u8 *array_id)
 {
-	int err;
+	int err, index;
 	struct c4_inquiry *inqp;
 
 	err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h);
 	if (err == SCSI_DH_OK) {
 		inqp = &h->inq.c4;
-		h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id,
-					array_name);
+		/* get the controller index */
+		if (inqp->slot_id[1] == 0x31)
+			index = 0;
+		else
+			index = 1;
+		h->ctlr = get_controller(index, array_name, array_id, sdev);
 		if (!h->ctlr)
 			err = SCSI_DH_RES_TEMP_UNAVAIL;
 	}
@@ -835,6 +838,7 @@
 	unsigned long flags;
 	int err;
 	char array_name[ARRAY_LABEL_LEN];
+	char array_id[UNIQUE_ID_LEN];
 
 	scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
 			       + sizeof(*h) , GFP_KERNEL);
@@ -849,11 +853,11 @@
 	h->lun = UNINITIALIZED_LUN;
 	h->state = RDAC_STATE_ACTIVE;
 
-	err = get_lun_info(sdev, h, array_name);
+	err = get_lun_info(sdev, h, array_name, array_id);
 	if (err != SCSI_DH_OK)
 		goto failed;
 
-	err = initialize_controller(sdev, h, array_name);
+	err = initialize_controller(sdev, h, array_name, array_id);
 	if (err != SCSI_DH_OK)
 		goto failed;
 
diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h
index 179ad77..bd9e31e 100644
--- a/drivers/scsi/dpt/dpti_i2o.h
+++ b/drivers/scsi/dpt/dpti_i2o.h
@@ -22,7 +22,7 @@
 #include <linux/i2o-dev.h>
 
 #include <linux/notifier.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 
 /*
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 204fa8d..ba710e3 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -487,6 +487,19 @@
 }
 
 /**
+ * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame
+ * @port: The FCoE port
+ * @skb: The FIP/FCoE packet to be sent
+ */
+static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb)
+{
+	if (port->fcoe_pending_queue.qlen)
+		fcoe_check_wait_queue(port->lport, skb);
+	else if (fcoe_start_io(skb))
+		fcoe_check_wait_queue(port->lport, skb);
+}
+
+/**
  * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
  * @fip: The FCoE controller
  * @skb: The FIP packet to be sent
@@ -494,7 +507,7 @@
 static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
 	skb->dev = fcoe_from_ctlr(fip)->netdev;
-	dev_queue_xmit(skb);
+	fcoe_port_send(lport_priv(fip->lp), skb);
 }
 
 /**
@@ -1257,30 +1270,20 @@
 /**
  * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming
  *			command.
- * @curr_cpu:   CPU which received request
  *
- * This routine selects next CPU based on cpumask.
+ * This routine selects next CPU based on cpumask to distribute
+ * incoming requests in round robin.
  *
- * Returns: int (CPU number). Caller to verify if returned CPU is online or not.
+ * Returns: int CPU number
  */
-static unsigned int fcoe_select_cpu(unsigned int curr_cpu)
+static inline unsigned int fcoe_select_cpu(void)
 {
 	static unsigned int selected_cpu;
 
-	if (num_online_cpus() == 1)
-		return curr_cpu;
-	/*
-	 * Doing following check, to skip "curr_cpu (smp_processor_id)"
-	 * from selection of CPU is intentional. This is to avoid same CPU
-	 * doing post-processing of command. "curr_cpu" to just receive
-	 * incoming request in case where rx_id is UNKNOWN and all other
-	 * CPU to actually process the command(s)
-	 */
-	do {
-		selected_cpu = cpumask_next(selected_cpu, cpu_online_mask);
-		if (selected_cpu >= nr_cpu_ids)
-			selected_cpu = cpumask_first(cpu_online_mask);
-	} while (selected_cpu == curr_cpu);
+	selected_cpu = cpumask_next(selected_cpu, cpu_online_mask);
+	if (selected_cpu >= nr_cpu_ids)
+		selected_cpu = cpumask_first(cpu_online_mask);
+
 	return selected_cpu;
 }
 
@@ -1350,30 +1353,26 @@
 
 	fr = fcoe_dev_from_skb(skb);
 	fr->fr_dev = lport;
-	fr->ptype = ptype;
 
 	/*
 	 * In case the incoming frame's exchange is originated from
 	 * the initiator, then received frame's exchange id is ANDed
 	 * with fc_cpu_mask bits to get the same cpu on which exchange
-	 * was originated, otherwise just use the current cpu.
+	 * was originated, otherwise select cpu using rx exchange id
+	 * or fcoe_select_cpu().
 	 */
 	if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
 		cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
 	else {
-		cpu = smp_processor_id();
-
-		if ((fh->fh_type == FC_TYPE_FCP) &&
-		    (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) {
-			do {
-				cpu = fcoe_select_cpu(cpu);
-			} while (!cpu_online(cpu));
-		} else  if ((fh->fh_type == FC_TYPE_FCP) &&
-			    (ntohs(fh->fh_rx_id) != FC_XID_UNKNOWN)) {
+		if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)
+			cpu = fcoe_select_cpu();
+		else
 			cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask;
-		} else
-			cpu = smp_processor_id();
 	}
+
+	if (cpu >= nr_cpu_ids)
+		goto err;
+
 	fps = &per_cpu(fcoe_percpu, cpu);
 	spin_lock_bh(&fps->fcoe_rx_list.lock);
 	if (unlikely(!fps->thread)) {
@@ -1572,11 +1571,7 @@
 
 	/* send down to lld */
 	fr_dev(fp) = lport;
-	if (port->fcoe_pending_queue.qlen)
-		fcoe_check_wait_queue(lport, skb);
-	else if (fcoe_start_io(skb))
-		fcoe_check_wait_queue(lport, skb);
-
+	fcoe_port_send(port, skb);
 	return 0;
 }
 
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 6bba23a..ec61bdb 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -46,7 +46,7 @@
 #include <linux/cciss_ioctl.h>
 #include <linux/string.h>
 #include <linux/bitmap.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/kthread.h>
 #include "hpsa_cmd.h"
 #include "hpsa.h"
@@ -1219,8 +1219,8 @@
 		dev_warn(&h->pdev->dev, "cp %p reports abort failed\n", cp);
 		break;
 	case CMD_UNSOLICITED_ABORT:
-		cmd->result = DID_RESET << 16;
-		dev_warn(&h->pdev->dev, "cp %p aborted do to an unsolicited "
+		cmd->result = DID_SOFT_ERROR << 16; /* retry the command */
+		dev_warn(&h->pdev->dev, "cp %p aborted due to an unsolicited "
 			"abort\n", cp);
 		break;
 	case CMD_TIMEOUT:
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 6d8dcd4..7f53cea 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -214,7 +214,7 @@
 	dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
 		c->Header.Tag.lower);
 	writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
-	(void) readl(h->vaddr + SA5_REQUEST_PORT_OFFSET);
+	(void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
 	h->commands_outstanding++;
 	if (h->commands_outstanding > h->max_outstanding)
 		h->max_outstanding = h->commands_outstanding;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 888086c..8d63630 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -8778,14 +8778,14 @@
 	if (rc != PCIBIOS_SUCCESSFUL) {
 		dev_err(&pdev->dev, "Failed to save PCI config space\n");
 		rc = -EIO;
-		goto cleanup_nomem;
+		goto out_msi_disable;
 	}
 
 	if ((rc = ipr_save_pcix_cmd_reg(ioa_cfg)))
-		goto cleanup_nomem;
+		goto out_msi_disable;
 
 	if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg)))
-		goto cleanup_nomem;
+		goto out_msi_disable;
 
 	if (ioa_cfg->sis64)
 		ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr64)
@@ -8800,7 +8800,7 @@
 	if (rc < 0) {
 		dev_err(&pdev->dev,
 			"Couldn't allocate enough memory for device driver!\n");
-		goto cleanup_nomem;
+		goto out_msi_disable;
 	}
 
 	/*
@@ -8845,10 +8845,10 @@
 
 cleanup_nolog:
 	ipr_free_mem(ioa_cfg);
-cleanup_nomem:
-	iounmap(ipr_regs);
 out_msi_disable:
 	pci_disable_msi(pdev);
+cleanup_nomem:
+	iounmap(ipr_regs);
 out_release_regions:
 	pci_release_regions(pdev);
 out_scsi_host_put:
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index f5a0665..01ff082 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -802,10 +802,8 @@
 		pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
 		spin_lock_bh(&pool->lock);
 		ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
-		if (ep) {
+		if (ep && ep->xid == xid)
 			fc_exch_hold(ep);
-			WARN_ON(ep->xid != xid);
-		}
 		spin_unlock_bh(&pool->lock);
 	}
 	return ep;
@@ -2465,8 +2463,11 @@
 
 	fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
 	if (!fc_exch_workqueue)
-		return -ENOMEM;
+		goto err;
 	return 0;
+err:
+	kmem_cache_destroy(fc_em_cachep);
+	return -ENOMEM;
 }
 
 /**
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 9cd2149..afb63c8 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -498,7 +498,7 @@
 			stats = per_cpu_ptr(lport->dev_stats, get_cpu());
 			stats->ErrorFrames++;
 			/* per cpu count, not total count, but OK for limit */
-			if (stats->InvalidCRCCount++ < 5)
+			if (stats->InvalidCRCCount++ < FC_MAX_ERROR_CNT)
 				printk(KERN_WARNING "libfc: CRC error on data "
 				       "frame for port (%6.6x)\n",
 				       lport->port_id);
@@ -690,7 +690,7 @@
 }
 
 /**
- * fc_fcp_abts_resp() - Send an ABTS response
+ * fc_fcp_abts_resp() - Receive an ABTS response
  * @fsp: The FCP packet that is being aborted
  * @fp:	 The response frame
  */
@@ -730,7 +730,7 @@
 }
 
 /**
- * fc_fcp_recv() - Reveive an FCP frame
+ * fc_fcp_recv() - Receive an FCP frame
  * @seq: The sequence the frame is on
  * @fp:	 The received frame
  * @arg: The related FCP packet
@@ -1084,6 +1084,7 @@
 	rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
 	if (unlikely(rc)) {
 		spin_lock_irqsave(&si->scsi_queue_lock, flags);
+		fsp->cmd->SCp.ptr = NULL;
 		list_del(&fsp->list);
 		spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
 	}
@@ -1645,12 +1646,10 @@
 	struct fc_seq *seq;
 	struct fcp_srr *srr;
 	struct fc_frame *fp;
-	u8 cdb_op;
 	unsigned int rec_tov;
 
 	rport = fsp->rport;
 	rpriv = rport->dd_data;
-	cdb_op = fsp->cdb_cmd.fc_cdb[0];
 
 	if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||
 	    rpriv->rp_state != RPORT_ST_READY)
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index e008b16..e55ed9c 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1352,7 +1352,6 @@
 		WARN_ON(1);
 		break;
 	case LPORT_ST_READY:
-		WARN_ON(1);
 		break;
 	case LPORT_ST_RESET:
 		break;
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index d7a4120..256a999 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -84,22 +84,6 @@
 					     __func__, ##arg);		\
 	} while (0);
 
-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
-#define SNA32_CHECK 2147483648UL
-
-static int iscsi_sna_lt(u32 n1, u32 n2)
-{
-	return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
-			    (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
-}
-
-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
-static int iscsi_sna_lte(u32 n1, u32 n2)
-{
-	return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
-			    (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
-}
-
 inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
 {
 	struct Scsi_Host *shost = conn->session->host;
@@ -360,7 +344,7 @@
 	struct iscsi_conn *conn = task->conn;
 	struct iscsi_session *session = conn->session;
 	struct scsi_cmnd *sc = task->sc;
-	struct iscsi_cmd *hdr;
+	struct iscsi_scsi_req *hdr;
 	unsigned hdrlength, cmd_len;
 	itt_t itt;
 	int rc;
@@ -374,7 +358,7 @@
 		if (rc)
 			return rc;
 	}
-	hdr = (struct iscsi_cmd *) task->hdr;
+	hdr = (struct iscsi_scsi_req *)task->hdr;
 	itt = hdr->itt;
 	memset(hdr, 0, sizeof(*hdr));
 
@@ -830,7 +814,7 @@
 			       struct iscsi_task *task, char *data,
 			       int datalen)
 {
-	struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
+	struct iscsi_scsi_rsp *rhdr = (struct iscsi_scsi_rsp *)hdr;
 	struct iscsi_session *session = conn->session;
 	struct scsi_cmnd *sc = task->sc;
 
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 874e29d..f84084b 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -849,6 +849,9 @@
 
 	res = sas_discover_expander(child);
 	if (res) {
+		spin_lock_irq(&parent->port->dev_list_lock);
+		list_del(&child->dev_list_node);
+		spin_unlock_irq(&parent->port->dev_list_lock);
 		kfree(child);
 		return NULL;
 	}
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 8ec2c86..c088a36 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -20,6 +20,11 @@
  *******************************************************************/
 
 #include <scsi/scsi_host.h>
+
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
 struct lpfc_sli2_slim;
 
 #define LPFC_PCI_DEV_LP		0x1
@@ -465,9 +470,10 @@
 struct unsol_rcv_ct_ctx {
 	uint32_t ctxt_id;
 	uint32_t SID;
-	uint32_t oxid;
 	uint32_t flags;
 #define UNSOL_VALID	0x00000001
+	uint16_t oxid;
+	uint16_t rxid;
 };
 
 #define LPFC_USER_LINK_SPEED_AUTO	0	/* auto select (default)*/
@@ -674,6 +680,9 @@
 	uint32_t cfg_enable_rrq;
 	uint32_t cfg_topology;
 	uint32_t cfg_link_speed;
+#define LPFC_FCF_FOV 1		/* Fast fcf failover */
+#define LPFC_FCF_PRIORITY 2	/* Priority fcf failover */
+	uint32_t cfg_fcf_failover_policy;
 	uint32_t cfg_cr_delay;
 	uint32_t cfg_cr_count;
 	uint32_t cfg_multi_ring_support;
@@ -845,9 +854,13 @@
 	/* iDiag debugfs sub-directory */
 	struct dentry *idiag_root;
 	struct dentry *idiag_pci_cfg;
+	struct dentry *idiag_bar_acc;
 	struct dentry *idiag_que_info;
 	struct dentry *idiag_que_acc;
 	struct dentry *idiag_drb_acc;
+	struct dentry *idiag_ctl_acc;
+	struct dentry *idiag_mbx_acc;
+	struct dentry *idiag_ext_acc;
 #endif
 
 	/* Used for deferred freeing of ELS data buffers */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 135a53b..2542f1f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -755,6 +755,47 @@
 }
 
 /**
+ * lpfc_sli4_pdev_status_reg_wait - Wait for pdev status register for readyness
+ * @phba: lpfc_hba pointer.
+ *
+ * Description:
+ * SLI4 interface type-2 device to wait on the sliport status register for
+ * the readyness after performing a firmware reset.
+ *
+ * Returns:
+ * zero for success
+ **/
+static int
+lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *phba)
+{
+	struct lpfc_register portstat_reg;
+	int i;
+
+
+	lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+		   &portstat_reg.word0);
+
+	/* wait for the SLI port firmware ready after firmware reset */
+	for (i = 0; i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT; i++) {
+		msleep(10);
+		lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+			   &portstat_reg.word0);
+		if (!bf_get(lpfc_sliport_status_err, &portstat_reg))
+			continue;
+		if (!bf_get(lpfc_sliport_status_rn, &portstat_reg))
+			continue;
+		if (!bf_get(lpfc_sliport_status_rdy, &portstat_reg))
+			continue;
+		break;
+	}
+
+	if (i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT)
+		return 0;
+	else
+		return -EIO;
+}
+
+/**
  * lpfc_sli4_pdev_reg_request - Request physical dev to perform a register acc
  * @phba: lpfc_hba pointer.
  *
@@ -769,6 +810,7 @@
 lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
 {
 	struct completion online_compl;
+	struct pci_dev *pdev = phba->pcidev;
 	uint32_t reg_val;
 	int status = 0;
 	int rc;
@@ -781,6 +823,14 @@
 	     LPFC_SLI_INTF_IF_TYPE_2))
 		return -EPERM;
 
+	if (!pdev->is_physfn)
+		return -EPERM;
+
+	/* Disable SR-IOV virtual functions if enabled */
+	if (phba->cfg_sriov_nr_virtfn) {
+		pci_disable_sriov(pdev);
+		phba->cfg_sriov_nr_virtfn = 0;
+	}
 	status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
 
 	if (status != 0)
@@ -805,7 +855,10 @@
 	readl(phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET);
 
 	/* delay driver action following IF_TYPE_2 reset */
-	msleep(100);
+	rc = lpfc_sli4_pdev_status_reg_wait(phba);
+
+	if (rc)
+		return -EIO;
 
 	init_completion(&online_compl);
 	rc = lpfc_workq_post_event(phba, &status, &online_compl,
@@ -895,6 +948,10 @@
 
 	if (!phba->cfg_enable_hba_reset)
 		return -EACCES;
+
+	lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+		"3050 lpfc_board_mode set to %s\n", buf);
+
 	init_completion(&online_compl);
 
 	if(strncmp(buf, "online", sizeof("online") - 1) == 0) {
@@ -1290,6 +1347,10 @@
 	if (phba->sli_rev == LPFC_SLI_REV4)
 		val = 0;
 
+	lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+		"3051 lpfc_poll changed from %d to %d\n",
+		phba->cfg_poll, val);
+
 	spin_lock_irq(&phba->hbalock);
 
 	old_val = phba->cfg_poll;
@@ -1414,80 +1475,10 @@
 	struct Scsi_Host *shost = class_to_shost(dev);
 	struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
 	struct lpfc_hba *phba = vport->phba;
-	struct pci_dev *pdev = phba->pcidev;
-	union  lpfc_sli4_cfg_shdr *shdr;
-	uint32_t shdr_status, shdr_add_status;
-	LPFC_MBOXQ_t *mboxq;
-	struct lpfc_mbx_get_prof_cfg *get_prof_cfg;
-	struct lpfc_rsrc_desc_pcie *desc;
-	uint32_t max_nr_virtfn;
-	uint32_t desc_count;
-	int length, rc, i;
+	uint16_t max_nr_virtfn;
 
-	if ((phba->sli_rev < LPFC_SLI_REV4) ||
-	    (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
-	     LPFC_SLI_INTF_IF_TYPE_2))
-		return -EPERM;
-
-	if (!pdev->is_physfn)
-		return snprintf(buf, PAGE_SIZE, "%d\n", 0);
-
-	mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
-	if (!mboxq)
-		return -ENOMEM;
-
-	/* get the maximum number of virtfn support by physfn */
-	length = (sizeof(struct lpfc_mbx_get_prof_cfg) -
-		  sizeof(struct lpfc_sli4_cfg_mhdr));
-	lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
-			 LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG,
-			 length, LPFC_SLI4_MBX_EMBED);
-	shdr = (union lpfc_sli4_cfg_shdr *)
-		&mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
-	bf_set(lpfc_mbox_hdr_pf_num, &shdr->request,
-	       phba->sli4_hba.iov.pf_number + 1);
-
-	get_prof_cfg = &mboxq->u.mqe.un.get_prof_cfg;
-	bf_set(lpfc_mbx_get_prof_cfg_prof_tp, &get_prof_cfg->u.request,
-	       LPFC_CFG_TYPE_CURRENT_ACTIVE);
-
-	rc = lpfc_sli_issue_mbox_wait(phba, mboxq,
-				lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG));
-
-	if (rc != MBX_TIMEOUT) {
-		/* check return status */
-		shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
-		shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
-					 &shdr->response);
-		if (shdr_status || shdr_add_status || rc)
-			goto error_out;
-
-	} else
-		goto error_out;
-
-	desc_count = get_prof_cfg->u.response.prof_cfg.rsrc_desc_count;
-
-	for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) {
-		desc = (struct lpfc_rsrc_desc_pcie *)
-			&get_prof_cfg->u.response.prof_cfg.desc[i];
-		if (LPFC_RSRC_DESC_TYPE_PCIE ==
-		    bf_get(lpfc_rsrc_desc_pcie_type, desc)) {
-			max_nr_virtfn = bf_get(lpfc_rsrc_desc_pcie_nr_virtfn,
-					       desc);
-			break;
-		}
-	}
-
-	if (i < LPFC_RSRC_DESC_MAX_NUM) {
-		if (rc != MBX_TIMEOUT)
-			mempool_free(mboxq, phba->mbox_mem_pool);
-		return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
-	}
-
-error_out:
-	if (rc != MBX_TIMEOUT)
-		mempool_free(mboxq, phba->mbox_mem_pool);
-	return -EIO;
+	max_nr_virtfn = lpfc_sli_sriov_nr_virtfn_get(phba);
+	return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
 }
 
 /**
@@ -1605,6 +1596,9 @@
 lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \
 { \
 	if (val >= minval && val <= maxval) {\
+		lpfc_printf_log(phba, KERN_ERR, LOG_INIT, \
+			"3052 lpfc_" #attr " changed from %d to %d\n", \
+			phba->cfg_##attr, val); \
 		phba->cfg_##attr = val;\
 		return 0;\
 	}\
@@ -1762,6 +1756,9 @@
 lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \
 { \
 	if (val >= minval && val <= maxval) {\
+		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \
+			"3053 lpfc_" #attr " changed from %d to %d\n", \
+			vport->cfg_##attr, val); \
 		vport->cfg_##attr = val;\
 		return 0;\
 	}\
@@ -2196,6 +2193,9 @@
 lpfc_param_init(enable_npiv, 1, 0, 1);
 static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL);
 
+LPFC_ATTR_R(fcf_failover_policy, 1, 1, 2,
+	"FCF Fast failover=1 Priority failover=2");
+
 int lpfc_enable_rrq;
 module_param(lpfc_enable_rrq, int, S_IRUGO);
 MODULE_PARM_DESC(lpfc_enable_rrq, "Enable RRQ functionality");
@@ -2678,6 +2678,9 @@
 		if (nolip)
 			return strlen(buf);
 
+		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+			"3054 lpfc_topology changed from %d to %d\n",
+			prev_val, val);
 		err = lpfc_issue_lip(lpfc_shost_from_vport(phba->pport));
 		if (err) {
 			phba->cfg_topology = prev_val;
@@ -3101,6 +3104,10 @@
 	if (sscanf(val_buf, "%i", &val) != 1)
 		return -EINVAL;
 
+	lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+		"3055 lpfc_link_speed changed from %d to %d %s\n",
+		phba->cfg_link_speed, val, nolip ? "(nolip)" : "(lip)");
+
 	if (((val == LPFC_USER_LINK_SPEED_1G) && !(phba->lmt & LMT_1Gb)) ||
 	    ((val == LPFC_USER_LINK_SPEED_2G) && !(phba->lmt & LMT_2Gb)) ||
 	    ((val == LPFC_USER_LINK_SPEED_4G) && !(phba->lmt & LMT_4Gb)) ||
@@ -3678,7 +3685,9 @@
 #	- Default will result in registering capabilities for all profiles.
 #
 */
-unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION;
+unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION |
+			      SHOST_DIX_TYPE0_PROTECTION |
+			      SHOST_DIX_TYPE1_PROTECTION;
 
 module_param(lpfc_prot_mask, uint, S_IRUGO);
 MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
@@ -3769,6 +3778,7 @@
 	&dev_attr_lpfc_fdmi_on,
 	&dev_attr_lpfc_max_luns,
 	&dev_attr_lpfc_enable_npiv,
+	&dev_attr_lpfc_fcf_failover_policy,
 	&dev_attr_lpfc_enable_rrq,
 	&dev_attr_nport_evt_cnt,
 	&dev_attr_board_mode,
@@ -4989,6 +4999,7 @@
 	lpfc_link_speed_init(phba, lpfc_link_speed);
 	lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
 	lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
+	lpfc_fcf_failover_policy_init(phba, lpfc_fcf_failover_policy);
 	lpfc_enable_rrq_init(phba, lpfc_enable_rrq);
 	lpfc_use_msi_init(phba, lpfc_use_msi);
 	lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 7fb0ba4..6760c69 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -42,6 +42,7 @@
 #include "lpfc.h"
 #include "lpfc_logmsg.h"
 #include "lpfc_crtn.h"
+#include "lpfc_debugfs.h"
 #include "lpfc_vport.h"
 #include "lpfc_version.h"
 
@@ -960,8 +961,10 @@
 						    evt_dat->immed_dat].oxid,
 						phba->ct_ctx[
 						    evt_dat->immed_dat].SID);
+			phba->ct_ctx[evt_dat->immed_dat].rxid =
+				piocbq->iocb.ulpContext;
 			phba->ct_ctx[evt_dat->immed_dat].oxid =
-						piocbq->iocb.ulpContext;
+				piocbq->iocb.unsli3.rcvsli3.ox_id;
 			phba->ct_ctx[evt_dat->immed_dat].SID =
 				piocbq->iocb.un.rcvels.remoteID;
 			phba->ct_ctx[evt_dat->immed_dat].flags = UNSOL_VALID;
@@ -1312,7 +1315,8 @@
 			rc = IOCB_ERROR;
 			goto issue_ct_rsp_exit;
 		}
-		icmd->ulpContext = phba->ct_ctx[tag].oxid;
+		icmd->ulpContext = phba->ct_ctx[tag].rxid;
+		icmd->unsli3.rcvsli3.ox_id = phba->ct_ctx[tag].oxid;
 		ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
 		if (!ndlp) {
 			lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
@@ -1337,9 +1341,7 @@
 			goto issue_ct_rsp_exit;
 		}
 
-		icmd->un.ulpWord[3] = ndlp->nlp_rpi;
-		if (phba->sli_rev == LPFC_SLI_REV4)
-			icmd->ulpContext =
+		icmd->un.ulpWord[3] =
 				phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
 
 		/* The exchange is done, mark the entry as invalid */
@@ -1351,8 +1353,8 @@
 
 	/* Xmit CT response on exchange <xid> */
 	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
-			"2722 Xmit CT response on exchange x%x Data: x%x x%x\n",
-			icmd->ulpContext, icmd->ulpIoTag, phba->link_state);
+		"2722 Xmit CT response on exchange x%x Data: x%x x%x x%x\n",
+		icmd->ulpContext, icmd->ulpIoTag, tag, phba->link_state);
 
 	ctiocb->iocb_cmpl = NULL;
 	ctiocb->iocb_flag |= LPFC_IO_LIBDFC;
@@ -1471,13 +1473,12 @@
 /**
  * lpfc_bsg_diag_mode_enter - process preparing into device diag loopback mode
  * @phba: Pointer to HBA context object.
- * @job: LPFC_BSG_VENDOR_DIAG_MODE
  *
  * This function is responsible for preparing driver for diag loopback
  * on device.
  */
 static int
-lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job)
+lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba)
 {
 	struct lpfc_vport **vports;
 	struct Scsi_Host *shost;
@@ -1521,7 +1522,6 @@
 /**
  * lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode
  * @phba: Pointer to HBA context object.
- * @job: LPFC_BSG_VENDOR_DIAG_MODE
  *
  * This function is responsible for driver exit processing of setting up
  * diag loopback mode on device.
@@ -1567,7 +1567,7 @@
 	uint32_t link_flags;
 	uint32_t timeout;
 	LPFC_MBOXQ_t *pmboxq;
-	int mbxstatus;
+	int mbxstatus = MBX_SUCCESS;
 	int i = 0;
 	int rc = 0;
 
@@ -1586,7 +1586,7 @@
 		goto job_error;
 	}
 
-	rc = lpfc_bsg_diag_mode_enter(phba, job);
+	rc = lpfc_bsg_diag_mode_enter(phba);
 	if (rc)
 		goto job_error;
 
@@ -1741,7 +1741,7 @@
 	uint32_t link_flags, timeout, req_len, alloc_len;
 	struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback;
 	LPFC_MBOXQ_t *pmboxq = NULL;
-	int mbxstatus, i, rc = 0;
+	int mbxstatus = MBX_SUCCESS, i, rc = 0;
 
 	/* no data to return just the return code */
 	job->reply->reply_payload_rcv_len = 0;
@@ -1758,7 +1758,7 @@
 		goto job_error;
 	}
 
-	rc = lpfc_bsg_diag_mode_enter(phba, job);
+	rc = lpfc_bsg_diag_mode_enter(phba);
 	if (rc)
 		goto job_error;
 
@@ -1982,7 +1982,7 @@
 		goto job_error;
 	}
 
-	rc = lpfc_bsg_diag_mode_enter(phba, job);
+	rc = lpfc_bsg_diag_mode_enter(phba);
 	if (rc)
 		goto job_error;
 
@@ -3178,6 +3178,11 @@
 				"(x%x/x%x) complete bsg job done, bsize:%d\n",
 				phba->mbox_ext_buf_ctx.nembType,
 				phba->mbox_ext_buf_ctx.mboxType, size);
+		lpfc_idiag_mbxacc_dump_bsg_mbox(phba,
+					phba->mbox_ext_buf_ctx.nembType,
+					phba->mbox_ext_buf_ctx.mboxType,
+					dma_ebuf, sta_pos_addr,
+					phba->mbox_ext_buf_ctx.mbx_dmabuf, 0);
 	} else
 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
 
@@ -3430,6 +3435,10 @@
 				"ext_buf_cnt:%d\n", ext_buf_cnt);
 	}
 
+	/* before dma descriptor setup */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
+					sta_pre_addr, dmabuf, ext_buf_cnt);
+
 	/* reject non-embedded mailbox command with none external buffer */
 	if (ext_buf_cnt == 0) {
 		rc = -EPERM;
@@ -3477,6 +3486,10 @@
 		}
 	}
 
+	/* after dma descriptor setup */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
+					sta_pos_addr, dmabuf, ext_buf_cnt);
+
 	/* construct base driver mbox command */
 	pmb = &pmboxq->u.mb;
 	pmbx = (uint8_t *)dmabuf->virt;
@@ -3511,7 +3524,7 @@
 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
 				"2947 Issued SLI_CONFIG ext-buffer "
 				"maibox command, rc:x%x\n", rc);
-		return 1;
+		return SLI_CONFIG_HANDLED;
 	}
 	lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
 			"2948 Failed to issue SLI_CONFIG ext-buffer "
@@ -3549,7 +3562,7 @@
 	LPFC_MBOXQ_t *pmboxq = NULL;
 	MAILBOX_t *pmb;
 	uint8_t *mbx;
-	int rc = 0, i;
+	int rc = SLI_CONFIG_NOT_HANDLED, i;
 
 	mbox_req =
 	   (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
@@ -3591,12 +3604,20 @@
 				"ext_buf_cnt:%d\n", ext_buf_cnt);
 	}
 
+	/* before dma buffer descriptor setup */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
+					sta_pre_addr, dmabuf, ext_buf_cnt);
+
 	if (ext_buf_cnt == 0)
 		return -EPERM;
 
 	/* for the first external buffer */
 	lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf);
 
+	/* after dma descriptor setup */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
+					sta_pos_addr, dmabuf, ext_buf_cnt);
+
 	/* log for looking forward */
 	for (i = 1; i < ext_buf_cnt; i++) {
 		if (nemb_tp == nemb_mse)
@@ -3660,7 +3681,7 @@
 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
 					"2955 Issued SLI_CONFIG ext-buffer "
 					"maibox command, rc:x%x\n", rc);
-			return 1;
+			return SLI_CONFIG_HANDLED;
 		}
 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
 				"2956 Failed to issue SLI_CONFIG ext-buffer "
@@ -3668,6 +3689,11 @@
 		rc = -EPIPE;
 	}
 
+	/* wait for additoinal external buffers */
+	job->reply->result = 0;
+	job->job_done(job);
+	return SLI_CONFIG_HANDLED;
+
 job_error:
 	if (pmboxq)
 		mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -3840,6 +3866,12 @@
 	dmabuf = list_first_entry(&phba->mbox_ext_buf_ctx.ext_dmabuf_list,
 				  struct lpfc_dmabuf, list);
 	list_del_init(&dmabuf->list);
+
+	/* after dma buffer descriptor setup */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
+					mbox_rd, dma_ebuf, sta_pos_addr,
+					dmabuf, index);
+
 	pbuf = (uint8_t *)dmabuf->virt;
 	job->reply->reply_payload_rcv_len =
 		sg_copy_from_buffer(job->reply_payload.sg_list,
@@ -3922,6 +3954,11 @@
 					dmabuf);
 	list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
 
+	/* after write dma buffer */
+	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
+					mbox_wr, dma_ebuf, sta_pos_addr,
+					dmabuf, index);
+
 	if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) {
 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
 				"2968 SLI_CONFIG ext-buffer wr all %d "
@@ -3959,7 +3996,7 @@
 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
 					"2969 Issued SLI_CONFIG ext-buffer "
 					"maibox command, rc:x%x\n", rc);
-			return 1;
+			return SLI_CONFIG_HANDLED;
 		}
 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
 				"2970 Failed to issue SLI_CONFIG ext-buffer "
@@ -4039,14 +4076,14 @@
 			    struct lpfc_dmabuf *dmabuf)
 {
 	struct dfc_mbox_req *mbox_req;
-	int rc;
+	int rc = SLI_CONFIG_NOT_HANDLED;
 
 	mbox_req =
 	   (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
 
 	/* mbox command with/without single external buffer */
 	if (mbox_req->extMboxTag == 0 && mbox_req->extSeqNum == 0)
-		return SLI_CONFIG_NOT_HANDLED;
+		return rc;
 
 	/* mbox command and first external buffer */
 	if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) {
@@ -4249,7 +4286,7 @@
 		 * mailbox extension size
 		 */
 		if ((transmit_length > receive_length) ||
-			(transmit_length > MAILBOX_EXT_SIZE)) {
+			(transmit_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
 			rc = -ERANGE;
 			goto job_done;
 		}
@@ -4272,7 +4309,7 @@
 		/* receive length cannot be greater than mailbox
 		 * extension size
 		 */
-		if (receive_length > MAILBOX_EXT_SIZE) {
+		if (receive_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
 			rc = -ERANGE;
 			goto job_done;
 		}
@@ -4306,7 +4343,8 @@
 			bde = (struct ulp_bde64 *)&pmb->un.varWords[4];
 
 			/* bde size cannot be greater than mailbox ext size */
-			if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) {
+			if (bde->tus.f.bdeSize >
+			    BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
 				rc = -ERANGE;
 				goto job_done;
 			}
@@ -4332,7 +4370,8 @@
 				 * mailbox extension size
 				 */
 				if ((receive_length == 0) ||
-				    (receive_length > MAILBOX_EXT_SIZE)) {
+				    (receive_length >
+				     BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
 					rc = -ERANGE;
 					goto job_done;
 				}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index fc20c24..a6db6ae 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -235,9 +235,11 @@
 void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *);
 void lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *);
 uint16_t lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *);
+void lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *, uint16_t);
 int lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *, uint16_t);
 void lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *, uint16_t);
 int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *, uint16_t);
+void lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *);
 
 int lpfc_mem_alloc(struct lpfc_hba *, int align);
 void lpfc_mem_free(struct lpfc_hba *);
@@ -371,6 +373,10 @@
 /* SLI4 if_type 2 externs. */
 int lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *);
 int lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *);
+int lpfc_sli4_get_allocated_extnts(struct lpfc_hba *, uint16_t,
+				   uint16_t *, uint16_t *);
+int lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *, uint16_t,
+					  uint16_t *, uint16_t *);
 
 /* externs BlockGuard */
 extern char *_dump_buf_data;
@@ -432,10 +438,16 @@
 int lpfc_send_rrq(struct lpfc_hba *, struct lpfc_node_rrq *);
 int lpfc_set_rrq_active(struct lpfc_hba *, struct lpfc_nodelist *,
 	uint16_t, uint16_t, uint16_t);
+uint16_t lpfc_sli4_xri_inrange(struct lpfc_hba *, uint16_t);
 void lpfc_cleanup_wt_rrqs(struct lpfc_hba *);
 void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *);
 struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t,
 	uint32_t);
+void lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *, enum nemb_type,
+	enum mbox_type, enum dma_type, enum sta_type,
+	struct lpfc_dmabuf *, uint32_t);
+void lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *, MAILBOX_t *);
 int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *);
 /* functions to support SR-IOV */
 int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int);
+uint16_t lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 30b25c5..a0424dd 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -48,6 +48,7 @@
 #include "lpfc_version.h"
 #include "lpfc_compat.h"
 #include "lpfc_debugfs.h"
+#include "lpfc_bsg.h"
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 /*
@@ -135,7 +136,11 @@
 	int i, index, len, enable;
 	uint32_t ms;
 	struct lpfc_debugfs_trc *dtp;
-	char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
+	char *buffer;
+
+	buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return 0;
 
 	enable = lpfc_debugfs_enable;
 	lpfc_debugfs_enable = 0;
@@ -167,6 +172,8 @@
 	}
 
 	lpfc_debugfs_enable = enable;
+	kfree(buffer);
+
 	return len;
 }
 
@@ -195,8 +202,11 @@
 	int i, index, len, enable;
 	uint32_t ms;
 	struct lpfc_debugfs_trc *dtp;
-	char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
+	char *buffer;
 
+	buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return 0;
 
 	enable = lpfc_debugfs_enable;
 	lpfc_debugfs_enable = 0;
@@ -228,6 +238,8 @@
 	}
 
 	lpfc_debugfs_enable = enable;
+	kfree(buffer);
+
 	return len;
 }
 
@@ -378,7 +390,11 @@
 	int len = 0;
 	int i, off;
 	uint32_t *ptr;
-	char buffer[1024];
+	char *buffer;
+
+	buffer = kmalloc(1024, GFP_KERNEL);
+	if (!buffer)
+		return 0;
 
 	off = 0;
 	spin_lock_irq(&phba->hbalock);
@@ -407,6 +423,8 @@
 	}
 
 	spin_unlock_irq(&phba->hbalock);
+	kfree(buffer);
+
 	return len;
 }
 
@@ -1327,8 +1345,8 @@
 		return 0;
 
 	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
-		where = idiag.cmd.data[0];
-		count = idiag.cmd.data[1];
+		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
 	} else
 		return 0;
 
@@ -1373,6 +1391,11 @@
 		len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
 				"%08x ", u32val);
 		offset += sizeof(uint32_t);
+		if (offset >= LPFC_PCI_CFG_SIZE) {
+			len += snprintf(pbuffer+len,
+					LPFC_PCI_CFG_SIZE-len, "\n");
+			break;
+		}
 		index -= sizeof(uint32_t);
 		if (!index)
 			len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
@@ -1385,8 +1408,11 @@
 	}
 
 	/* Set up the offset for next portion of pci cfg read */
-	idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
-	if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
+	if (index == 0) {
+		idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
+		if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
+			idiag.offset.last_rd = 0;
+	} else
 		idiag.offset.last_rd = 0;
 
 	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
@@ -1439,8 +1465,8 @@
 		if (rc != LPFC_PCI_CFG_RD_CMD_ARG)
 			goto error_out;
 		/* Read command from PCI config space, set up command fields */
-		where = idiag.cmd.data[0];
-		count = idiag.cmd.data[1];
+		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
 		if (count == LPFC_PCI_CFG_BROWSE) {
 			if (where % sizeof(uint32_t))
 				goto error_out;
@@ -1475,9 +1501,9 @@
 		if (rc != LPFC_PCI_CFG_WR_CMD_ARG)
 			goto error_out;
 		/* Write command to PCI config space, read-modify-write */
-		where = idiag.cmd.data[0];
-		count = idiag.cmd.data[1];
-		value = idiag.cmd.data[2];
+		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
+		value = idiag.cmd.data[IDIAG_PCICFG_VALUE_INDX];
 		/* Sanity checks */
 		if ((count != sizeof(uint8_t)) &&
 		    (count != sizeof(uint16_t)) &&
@@ -1570,6 +1596,292 @@
 }
 
 /**
+ * lpfc_idiag_baracc_read - idiag debugfs pci bar access read
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba pci bar memory mapped space
+ * according to the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_baracc_read(struct file *file, char __user *buf, size_t nbytes,
+		       loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	int offset_label, offset, offset_run, len = 0, index;
+	int bar_num, acc_range, bar_size;
+	char *pbuffer;
+	void __iomem *mem_mapped_bar;
+	uint32_t if_type;
+	struct pci_dev *pdev;
+	uint32_t u32val;
+
+	pdev = phba->pcidev;
+	if (!pdev)
+		return 0;
+
+	/* This is a user read operation */
+	debug->op = LPFC_IDIAG_OP_RD;
+
+	if (!debug->buffer)
+		debug->buffer = kmalloc(LPFC_PCI_BAR_RD_BUF_SIZE, GFP_KERNEL);
+	if (!debug->buffer)
+		return 0;
+	pbuffer = debug->buffer;
+
+	if (*ppos)
+		return 0;
+
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
+		bar_num   = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
+		offset    = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
+		acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
+		bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
+	} else
+		return 0;
+
+	if (acc_range == 0)
+		return 0;
+
+	if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+		if (bar_num == IDIAG_BARACC_BAR_0)
+			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+		else if (bar_num == IDIAG_BARACC_BAR_1)
+			mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
+		else if (bar_num == IDIAG_BARACC_BAR_2)
+			mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
+		else
+			return 0;
+	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+		if (bar_num == IDIAG_BARACC_BAR_0)
+			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+		else
+			return 0;
+	} else
+		return 0;
+
+	/* Read single PCI bar space register */
+	if (acc_range == SINGLE_WORD) {
+		offset_run = offset;
+		u32val = readl(mem_mapped_bar + offset_run);
+		len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+				"%05x: %08x\n", offset_run, u32val);
+	} else
+		goto baracc_browse;
+
+	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+
+baracc_browse:
+
+	/* Browse all PCI bar space registers */
+	offset_label = idiag.offset.last_rd;
+	offset_run = offset_label;
+
+	/* Read PCI bar memory mapped space */
+	len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+			"%05x: ", offset_label);
+	index = LPFC_PCI_BAR_RD_SIZE;
+	while (index > 0) {
+		u32val = readl(mem_mapped_bar + offset_run);
+		len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+				"%08x ", u32val);
+		offset_run += sizeof(uint32_t);
+		if (acc_range == LPFC_PCI_BAR_BROWSE) {
+			if (offset_run >= bar_size) {
+				len += snprintf(pbuffer+len,
+					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+				break;
+			}
+		} else {
+			if (offset_run >= offset +
+			    (acc_range * sizeof(uint32_t))) {
+				len += snprintf(pbuffer+len,
+					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+				break;
+			}
+		}
+		index -= sizeof(uint32_t);
+		if (!index)
+			len += snprintf(pbuffer+len,
+					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+		else if (!(index % (8 * sizeof(uint32_t)))) {
+			offset_label += (8 * sizeof(uint32_t));
+			len += snprintf(pbuffer+len,
+					LPFC_PCI_BAR_RD_BUF_SIZE-len,
+					"\n%05x: ", offset_label);
+		}
+	}
+
+	/* Set up the offset for next portion of pci bar read */
+	if (index == 0) {
+		idiag.offset.last_rd += LPFC_PCI_BAR_RD_SIZE;
+		if (acc_range == LPFC_PCI_BAR_BROWSE) {
+			if (idiag.offset.last_rd >= bar_size)
+				idiag.offset.last_rd = 0;
+		} else {
+			if (offset_run >= offset +
+			    (acc_range * sizeof(uint32_t)))
+				idiag.offset.last_rd = offset;
+		}
+	} else {
+		if (acc_range == LPFC_PCI_BAR_BROWSE)
+			idiag.offset.last_rd = 0;
+		else
+			idiag.offset.last_rd = offset;
+	}
+
+	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_baracc_write - Syntax check and set up idiag bar access commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and
+ * then perform the syntax check for PCI bar memory mapped space read or
+ * write command accordingly. In the case of PCI bar memory mapped space
+ * read command, it sets up the command in the idiag command struct for
+ * the debugfs read operation. In the case of PCI bar memorpy mapped space
+ * write operation, it executes the write operation into the PCI bar memory
+ * mapped space accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ */
+static ssize_t
+lpfc_idiag_baracc_write(struct file *file, const char __user *buf,
+			size_t nbytes, loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	uint32_t bar_num, bar_size, offset, value, acc_range;
+	struct pci_dev *pdev;
+	void __iomem *mem_mapped_bar;
+	uint32_t if_type;
+	uint32_t u32val;
+	int rc;
+
+	pdev = phba->pcidev;
+	if (!pdev)
+		return -EFAULT;
+
+	/* This is a user write operation */
+	debug->op = LPFC_IDIAG_OP_WR;
+
+	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+	if (rc < 0)
+		return rc;
+
+	if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+	bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
+
+	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+		if ((bar_num != IDIAG_BARACC_BAR_0) &&
+		    (bar_num != IDIAG_BARACC_BAR_1) &&
+		    (bar_num != IDIAG_BARACC_BAR_2))
+			goto error_out;
+	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+		if (bar_num != IDIAG_BARACC_BAR_0)
+			goto error_out;
+	} else
+		goto error_out;
+
+	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+		if (bar_num == IDIAG_BARACC_BAR_0) {
+			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+				LPFC_PCI_IF0_BAR0_SIZE;
+			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+		} else if (bar_num == IDIAG_BARACC_BAR_1) {
+			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+				LPFC_PCI_IF0_BAR1_SIZE;
+			mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
+		} else if (bar_num == IDIAG_BARACC_BAR_2) {
+			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+				LPFC_PCI_IF0_BAR2_SIZE;
+			mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
+		} else
+			goto error_out;
+	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+		if (bar_num == IDIAG_BARACC_BAR_0) {
+			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+				LPFC_PCI_IF2_BAR0_SIZE;
+			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+		} else
+			goto error_out;
+	} else
+		goto error_out;
+
+	offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
+	if (offset % sizeof(uint32_t))
+		goto error_out;
+
+	bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
+		/* Sanity check on PCI config read command line arguments */
+		if (rc != LPFC_PCI_BAR_RD_CMD_ARG)
+			goto error_out;
+		acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
+		if (acc_range == LPFC_PCI_BAR_BROWSE) {
+			if (offset > bar_size - sizeof(uint32_t))
+				goto error_out;
+			/* Starting offset to browse */
+			idiag.offset.last_rd = offset;
+		} else if (acc_range > SINGLE_WORD) {
+			if (offset + acc_range * sizeof(uint32_t) > bar_size)
+				goto error_out;
+			/* Starting offset to browse */
+			idiag.offset.last_rd = offset;
+		} else if (acc_range != SINGLE_WORD)
+			goto error_out;
+	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR ||
+		   idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST ||
+		   idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
+		/* Sanity check on PCI bar write command line arguments */
+		if (rc != LPFC_PCI_BAR_WR_CMD_ARG)
+			goto error_out;
+		/* Write command to PCI bar space, read-modify-write */
+		acc_range = SINGLE_WORD;
+		value = idiag.cmd.data[IDIAG_BARACC_REG_VAL_INDX];
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR) {
+			writel(value, mem_mapped_bar + offset);
+			readl(mem_mapped_bar + offset);
+		}
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST) {
+			u32val = readl(mem_mapped_bar + offset);
+			u32val |= value;
+			writel(u32val, mem_mapped_bar + offset);
+			readl(mem_mapped_bar + offset);
+		}
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
+			u32val = readl(mem_mapped_bar + offset);
+			u32val &= ~value;
+			writel(u32val, mem_mapped_bar + offset);
+			readl(mem_mapped_bar + offset);
+		}
+	} else
+		/* All other opecodes are illegal for now */
+		goto error_out;
+
+	return nbytes;
+error_out:
+	memset(&idiag, 0, sizeof(idiag));
+	return -EINVAL;
+}
+
+/**
  * lpfc_idiag_queinfo_read - idiag debugfs read queue information
  * @file: The file pointer to read from.
  * @buf: The buffer to copy the data to.
@@ -1871,8 +2183,8 @@
 		return 0;
 
 	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
-		index = idiag.cmd.data[2];
-		count = idiag.cmd.data[3];
+		index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
+		count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
 		pque = (struct lpfc_queue *)idiag.ptr_private;
 	} else
 		return 0;
@@ -1944,12 +2256,12 @@
 		return rc;
 
 	/* Get and sanity check on command feilds */
-	quetp  = idiag.cmd.data[0];
-	queid  = idiag.cmd.data[1];
-	index  = idiag.cmd.data[2];
-	count  = idiag.cmd.data[3];
-	offset = idiag.cmd.data[4];
-	value  = idiag.cmd.data[5];
+	quetp  = idiag.cmd.data[IDIAG_QUEACC_QUETP_INDX];
+	queid  = idiag.cmd.data[IDIAG_QUEACC_QUEID_INDX];
+	index  = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
+	count  = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
+	offset = idiag.cmd.data[IDIAG_QUEACC_OFFST_INDX];
+	value  = idiag.cmd.data[IDIAG_QUEACC_VALUE_INDX];
 
 	/* Sanity check on command line arguments */
 	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
@@ -2218,7 +2530,7 @@
 		return 0;
 
 	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD)
-		drb_reg_id = idiag.cmd.data[0];
+		drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
 	else
 		return 0;
 
@@ -2257,7 +2569,7 @@
 {
 	struct lpfc_debug *debug = file->private_data;
 	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
-	uint32_t drb_reg_id, value, reg_val;
+	uint32_t drb_reg_id, value, reg_val = 0;
 	void __iomem *drb_reg;
 	int rc;
 
@@ -2269,8 +2581,8 @@
 		return rc;
 
 	/* Sanity check on command line arguments */
-	drb_reg_id = idiag.cmd.data[0];
-	value = idiag.cmd.data[1];
+	drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
+	value = idiag.cmd.data[IDIAG_DRBACC_VALUE_INDX];
 
 	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
 	    idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
@@ -2330,6 +2642,679 @@
 	return -EINVAL;
 }
 
+/**
+ * lpfc_idiag_ctlacc_read_reg - idiag debugfs read a control registers
+ * @phba: The pointer to hba structure.
+ * @pbuffer: The pointer to the buffer to copy the data to.
+ * @len: The lenght of bytes to copied.
+ * @drbregid: The id to doorbell registers.
+ *
+ * Description:
+ * This routine reads a control register and copies its content to the
+ * user buffer pointed to by @pbuffer.
+ *
+ * Returns:
+ * This function returns the amount of data that was copied into @pbuffer.
+ **/
+static int
+lpfc_idiag_ctlacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
+			   int len, uint32_t ctlregid)
+{
+
+	if (!pbuffer)
+		return 0;
+
+	switch (ctlregid) {
+	case LPFC_CTL_PORT_SEM:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"Port SemReg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PORT_SEM_OFFSET));
+		break;
+	case LPFC_CTL_PORT_STA:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"Port StaReg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PORT_STA_OFFSET));
+		break;
+	case LPFC_CTL_PORT_CTL:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"Port CtlReg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PORT_CTL_OFFSET));
+		break;
+	case LPFC_CTL_PORT_ER1:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"Port Er1Reg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PORT_ER1_OFFSET));
+		break;
+	case LPFC_CTL_PORT_ER2:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"Port Er2Reg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PORT_ER2_OFFSET));
+		break;
+	case LPFC_CTL_PDEV_CTL:
+		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+				"PDev CtlReg:   0x%08x\n",
+				readl(phba->sli4_hba.conf_regs_memmap_p +
+				      LPFC_CTL_PDEV_CTL_OFFSET));
+		break;
+	default:
+		break;
+	}
+	return len;
+}
+
+/**
+ * lpfc_idiag_ctlacc_read - idiag debugfs read port and device control register
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba port and device registers according
+ * to the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_ctlacc_read(struct file *file, char __user *buf, size_t nbytes,
+		       loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	uint32_t ctl_reg_id, i;
+	char *pbuffer;
+	int len = 0;
+
+	/* This is a user read operation */
+	debug->op = LPFC_IDIAG_OP_RD;
+
+	if (!debug->buffer)
+		debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL);
+	if (!debug->buffer)
+		return 0;
+	pbuffer = debug->buffer;
+
+	if (*ppos)
+		return 0;
+
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD)
+		ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
+	else
+		return 0;
+
+	if (ctl_reg_id == LPFC_CTL_ACC_ALL)
+		for (i = 1; i <= LPFC_CTL_MAX; i++)
+			len = lpfc_idiag_ctlacc_read_reg(phba,
+							 pbuffer, len, i);
+	else
+		len = lpfc_idiag_ctlacc_read_reg(phba,
+						 pbuffer, len, ctl_reg_id);
+
+	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_ctlacc_write - Syntax check and set up idiag ctlacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for port and device control register read (dump)
+ * or write (set) command accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_ctlacc_write(struct file *file, const char __user *buf,
+			size_t nbytes, loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	uint32_t ctl_reg_id, value, reg_val = 0;
+	void __iomem *ctl_reg;
+	int rc;
+
+	/* This is a user write operation */
+	debug->op = LPFC_IDIAG_OP_WR;
+
+	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+	if (rc < 0)
+		return rc;
+
+	/* Sanity check on command line arguments */
+	ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
+	value = idiag.cmd.data[IDIAG_CTLACC_VALUE_INDX];
+
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
+	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
+	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+		if (rc != LPFC_CTL_ACC_WR_CMD_ARG)
+			goto error_out;
+		if (ctl_reg_id > LPFC_CTL_MAX)
+			goto error_out;
+	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) {
+		if (rc != LPFC_CTL_ACC_RD_CMD_ARG)
+			goto error_out;
+		if ((ctl_reg_id > LPFC_CTL_MAX) &&
+		    (ctl_reg_id != LPFC_CTL_ACC_ALL))
+			goto error_out;
+	} else
+		goto error_out;
+
+	/* Perform the write access operation */
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
+	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
+	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+		switch (ctl_reg_id) {
+		case LPFC_CTL_PORT_SEM:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PORT_SEM_OFFSET;
+			break;
+		case LPFC_CTL_PORT_STA:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PORT_STA_OFFSET;
+			break;
+		case LPFC_CTL_PORT_CTL:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PORT_CTL_OFFSET;
+			break;
+		case LPFC_CTL_PORT_ER1:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PORT_ER1_OFFSET;
+			break;
+		case LPFC_CTL_PORT_ER2:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PORT_ER2_OFFSET;
+			break;
+		case LPFC_CTL_PDEV_CTL:
+			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+					LPFC_CTL_PDEV_CTL_OFFSET;
+			break;
+		default:
+			goto error_out;
+		}
+
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR)
+			reg_val = value;
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST) {
+			reg_val = readl(ctl_reg);
+			reg_val |= value;
+		}
+		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+			reg_val = readl(ctl_reg);
+			reg_val &= ~value;
+		}
+		writel(reg_val, ctl_reg);
+		readl(ctl_reg); /* flush */
+	}
+	return nbytes;
+
+error_out:
+	/* Clean out command structure on command error out */
+	memset(&idiag, 0, sizeof(idiag));
+	return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_mbxacc_get_setup - idiag debugfs get mailbox access setup
+ * @phba: Pointer to HBA context object.
+ * @pbuffer: Pointer to data buffer.
+ *
+ * Description:
+ * This routine gets the driver mailbox access debugfs setup information.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static int
+lpfc_idiag_mbxacc_get_setup(struct lpfc_hba *phba, char *pbuffer)
+{
+	uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
+	int len = 0;
+
+	mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+	mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+	mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+	mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+			"mbx_dump_map: 0x%08x\n", mbx_dump_map);
+	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+			"mbx_dump_cnt: %04d\n", mbx_dump_cnt);
+	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+			"mbx_word_cnt: %04d\n", mbx_word_cnt);
+	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+			"mbx_mbox_cmd: 0x%02x\n", mbx_mbox_cmd);
+
+	return len;
+}
+
+/**
+ * lpfc_idiag_mbxacc_read - idiag debugfs read on mailbox access
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba driver mailbox access debugfs setup
+ * information.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_mbxacc_read(struct file *file, char __user *buf, size_t nbytes,
+		       loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	char *pbuffer;
+	int len = 0;
+
+	/* This is a user read operation */
+	debug->op = LPFC_IDIAG_OP_RD;
+
+	if (!debug->buffer)
+		debug->buffer = kmalloc(LPFC_MBX_ACC_BUF_SIZE, GFP_KERNEL);
+	if (!debug->buffer)
+		return 0;
+	pbuffer = debug->buffer;
+
+	if (*ppos)
+		return 0;
+
+	if ((idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) &&
+	    (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP))
+		return 0;
+
+	len = lpfc_idiag_mbxacc_get_setup(phba, pbuffer);
+
+	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_mbxacc_write - Syntax check and set up idiag mbxacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for driver mailbox command (dump) and sets up the
+ * necessary states in the idiag command struct accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_mbxacc_write(struct file *file, const char __user *buf,
+			size_t nbytes, loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
+	int rc;
+
+	/* This is a user write operation */
+	debug->op = LPFC_IDIAG_OP_WR;
+
+	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+	if (rc < 0)
+		return rc;
+
+	/* Sanity check on command line arguments */
+	mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+	mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+	mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+	mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_MBXACC_DP) {
+		if (!(mbx_dump_map & LPFC_MBX_DMP_MBX_ALL))
+			goto error_out;
+		if ((mbx_dump_map & ~LPFC_MBX_DMP_MBX_ALL) &&
+		    (mbx_dump_map != LPFC_MBX_DMP_ALL))
+			goto error_out;
+		if (mbx_word_cnt > sizeof(MAILBOX_t))
+			goto error_out;
+	} else if (idiag.cmd.opcode == LPFC_IDIAG_BSG_MBXACC_DP) {
+		if (!(mbx_dump_map & LPFC_BSG_DMP_MBX_ALL))
+			goto error_out;
+		if ((mbx_dump_map & ~LPFC_BSG_DMP_MBX_ALL) &&
+		    (mbx_dump_map != LPFC_MBX_DMP_ALL))
+			goto error_out;
+		if (mbx_word_cnt > (BSG_MBOX_SIZE)/4)
+			goto error_out;
+		if (mbx_mbox_cmd != 0x9b)
+			goto error_out;
+	} else
+		goto error_out;
+
+	if (mbx_word_cnt == 0)
+		goto error_out;
+	if (rc != LPFC_MBX_DMP_ARG)
+		goto error_out;
+	if (mbx_mbox_cmd & ~0xff)
+		goto error_out;
+
+	/* condition for stop mailbox dump */
+	if (mbx_dump_cnt == 0)
+		goto reset_out;
+
+	return nbytes;
+
+reset_out:
+	/* Clean out command structure on command error out */
+	memset(&idiag, 0, sizeof(idiag));
+	return nbytes;
+
+error_out:
+	/* Clean out command structure on command error out */
+	memset(&idiag, 0, sizeof(idiag));
+	return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_extacc_avail_get - get the available extents information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the available extent information.
+ *
+ * Returns:
+ * overall lenth of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_avail_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+	uint16_t ext_cnt, ext_size;
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\nAvailable Extents Information:\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tPort Available VPI extents: ");
+	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VPI,
+				       &ext_cnt, &ext_size);
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tPort Available VFI extents: ");
+	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VFI,
+				       &ext_cnt, &ext_size);
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tPort Available RPI extents: ");
+	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_RPI,
+				       &ext_cnt, &ext_size);
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tPort Available XRI extents: ");
+	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_XRI,
+				       &ext_cnt, &ext_size);
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+	return len;
+}
+
+/**
+ * lpfc_idiag_extacc_alloc_get - get the allocated extents information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the allocated extent information.
+ *
+ * Returns:
+ * overall lenth of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_alloc_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+	uint16_t ext_cnt, ext_size;
+	int rc;
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\nAllocated Extents Information:\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tHost Allocated VPI extents: ");
+	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VPI,
+					    &ext_cnt, &ext_size);
+	if (!rc)
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"Port %d Extent %3d, Size %3d\n",
+				phba->brd_no, ext_cnt, ext_size);
+	else
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"N/A\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tHost Allocated VFI extents: ");
+	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VFI,
+					    &ext_cnt, &ext_size);
+	if (!rc)
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"Port %d Extent %3d, Size %3d\n",
+				phba->brd_no, ext_cnt, ext_size);
+	else
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"N/A\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tHost Allocated RPI extents: ");
+	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_RPI,
+					    &ext_cnt, &ext_size);
+	if (!rc)
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"Port %d Extent %3d, Size %3d\n",
+				phba->brd_no, ext_cnt, ext_size);
+	else
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"N/A\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tHost Allocated XRI extents: ");
+	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_XRI,
+					    &ext_cnt, &ext_size);
+	if (!rc)
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"Port %d Extent %3d, Size %3d\n",
+				phba->brd_no, ext_cnt, ext_size);
+	else
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"N/A\n");
+
+	return len;
+}
+
+/**
+ * lpfc_idiag_extacc_drivr_get - get driver extent information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the driver extent information.
+ *
+ * Returns:
+ * overall lenth of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_drivr_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+	struct lpfc_rsrc_blks *rsrc_blks;
+	int index;
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\nDriver Extents Information:\n");
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tVPI extents:\n");
+	index = 0;
+	list_for_each_entry(rsrc_blks, &phba->lpfc_vpi_blk_list, list) {
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"\t\tBlock %3d: Start %4d, Count %4d\n",
+				index, rsrc_blks->rsrc_start,
+				rsrc_blks->rsrc_size);
+		index++;
+	}
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tVFI extents:\n");
+	index = 0;
+	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_vfi_blk_list,
+			    list) {
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"\t\tBlock %3d: Start %4d, Count %4d\n",
+				index, rsrc_blks->rsrc_start,
+				rsrc_blks->rsrc_size);
+		index++;
+	}
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tRPI extents:\n");
+	index = 0;
+	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_rpi_blk_list,
+			    list) {
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"\t\tBlock %3d: Start %4d, Count %4d\n",
+				index, rsrc_blks->rsrc_start,
+				rsrc_blks->rsrc_size);
+		index++;
+	}
+
+	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+			"\tXRI extents:\n");
+	index = 0;
+	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_xri_blk_list,
+			    list) {
+		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+				"\t\tBlock %3d: Start %4d, Count %4d\n",
+				index, rsrc_blks->rsrc_start,
+				rsrc_blks->rsrc_size);
+		index++;
+	}
+
+	return len;
+}
+
+/**
+ * lpfc_idiag_extacc_write - Syntax check and set up idiag extacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for extent information access commands and sets
+ * up the necessary states in the idiag command struct accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_extacc_write(struct file *file, const char __user *buf,
+			size_t nbytes, loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	uint32_t ext_map;
+	int rc;
+
+	/* This is a user write operation */
+	debug->op = LPFC_IDIAG_OP_WR;
+
+	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+	if (rc < 0)
+		return rc;
+
+	ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
+
+	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
+		goto error_out;
+	if (rc != LPFC_EXT_ACC_CMD_ARG)
+		goto error_out;
+	if (!(ext_map & LPFC_EXT_ACC_ALL))
+		goto error_out;
+
+	return nbytes;
+error_out:
+	/* Clean out command structure on command error out */
+	memset(&idiag, 0, sizeof(idiag));
+	return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_extacc_read - idiag debugfs read access to extent information
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the proper extent information according to
+ * the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_extacc_read(struct file *file, char __user *buf, size_t nbytes,
+		       loff_t *ppos)
+{
+	struct lpfc_debug *debug = file->private_data;
+	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+	char *pbuffer;
+	uint32_t ext_map;
+	int len = 0;
+
+	/* This is a user read operation */
+	debug->op = LPFC_IDIAG_OP_RD;
+
+	if (!debug->buffer)
+		debug->buffer = kmalloc(LPFC_EXT_ACC_BUF_SIZE, GFP_KERNEL);
+	if (!debug->buffer)
+		return 0;
+	pbuffer = debug->buffer;
+	if (*ppos)
+		return 0;
+	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
+		return 0;
+
+	ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
+	if (ext_map & LPFC_EXT_ACC_AVAIL)
+		len = lpfc_idiag_extacc_avail_get(phba, pbuffer, len);
+	if (ext_map & LPFC_EXT_ACC_ALLOC)
+		len = lpfc_idiag_extacc_alloc_get(phba, pbuffer, len);
+	if (ext_map & LPFC_EXT_ACC_DRIVR)
+		len = lpfc_idiag_extacc_drivr_get(phba, pbuffer, len);
+
+	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
 #undef lpfc_debugfs_op_disc_trc
 static const struct file_operations lpfc_debugfs_op_disc_trc = {
 	.owner =        THIS_MODULE,
@@ -2420,6 +3405,16 @@
 	.release =      lpfc_idiag_cmd_release,
 };
 
+#undef lpfc_idiag_op_barAcc
+static const struct file_operations lpfc_idiag_op_barAcc = {
+	.owner =        THIS_MODULE,
+	.open =         lpfc_idiag_open,
+	.llseek =       lpfc_debugfs_lseek,
+	.read =         lpfc_idiag_baracc_read,
+	.write =        lpfc_idiag_baracc_write,
+	.release =      lpfc_idiag_cmd_release,
+};
+
 #undef lpfc_idiag_op_queInfo
 static const struct file_operations lpfc_idiag_op_queInfo = {
 	.owner =        THIS_MODULE,
@@ -2428,7 +3423,7 @@
 	.release =      lpfc_idiag_release,
 };
 
-#undef lpfc_idiag_op_queacc
+#undef lpfc_idiag_op_queAcc
 static const struct file_operations lpfc_idiag_op_queAcc = {
 	.owner =        THIS_MODULE,
 	.open =         lpfc_idiag_open,
@@ -2438,7 +3433,7 @@
 	.release =      lpfc_idiag_cmd_release,
 };
 
-#undef lpfc_idiag_op_drbacc
+#undef lpfc_idiag_op_drbAcc
 static const struct file_operations lpfc_idiag_op_drbAcc = {
 	.owner =        THIS_MODULE,
 	.open =         lpfc_idiag_open,
@@ -2448,8 +3443,234 @@
 	.release =      lpfc_idiag_cmd_release,
 };
 
+#undef lpfc_idiag_op_ctlAcc
+static const struct file_operations lpfc_idiag_op_ctlAcc = {
+	.owner =        THIS_MODULE,
+	.open =         lpfc_idiag_open,
+	.llseek =       lpfc_debugfs_lseek,
+	.read =         lpfc_idiag_ctlacc_read,
+	.write =        lpfc_idiag_ctlacc_write,
+	.release =      lpfc_idiag_cmd_release,
+};
+
+#undef lpfc_idiag_op_mbxAcc
+static const struct file_operations lpfc_idiag_op_mbxAcc = {
+	.owner =        THIS_MODULE,
+	.open =         lpfc_idiag_open,
+	.llseek =       lpfc_debugfs_lseek,
+	.read =         lpfc_idiag_mbxacc_read,
+	.write =        lpfc_idiag_mbxacc_write,
+	.release =      lpfc_idiag_cmd_release,
+};
+
+#undef lpfc_idiag_op_extAcc
+static const struct file_operations lpfc_idiag_op_extAcc = {
+	.owner =        THIS_MODULE,
+	.open =         lpfc_idiag_open,
+	.llseek =       lpfc_debugfs_lseek,
+	.read =         lpfc_idiag_extacc_read,
+	.write =        lpfc_idiag_extacc_write,
+	.release =      lpfc_idiag_cmd_release,
+};
+
 #endif
 
+/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command
+ * @phba: Pointer to HBA context object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
+ *
+ * Description:
+ * This routine dump a bsg pass-through non-embedded mailbox command with
+ * external buffer.
+ **/
+void
+lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp,
+				enum mbox_type mbox_tp, enum dma_type dma_tp,
+				enum sta_type sta_tp,
+				struct lpfc_dmabuf *dmabuf, uint32_t ext_buf)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+	uint32_t *mbx_mbox_cmd, *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt;
+	char line_buf[LPFC_MBX_ACC_LBUF_SZ];
+	int len = 0;
+	uint32_t do_dump = 0;
+	uint32_t *pword;
+	uint32_t i;
+
+	if (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP)
+		return;
+
+	mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+	mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+	mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+	mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+	if (!(*mbx_dump_map & LPFC_MBX_DMP_ALL) ||
+	    (*mbx_dump_cnt == 0) ||
+	    (*mbx_word_cnt == 0))
+		return;
+
+	if (*mbx_mbox_cmd != 0x9B)
+		return;
+
+	if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) {
+		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) {
+			do_dump |= LPFC_BSG_DMP_MBX_RD_MBX;
+			printk(KERN_ERR "\nRead mbox command (x%x), "
+			       "nemb:0x%x, extbuf_cnt:%d:\n",
+			       sta_tp, nemb_tp, ext_buf);
+		}
+	}
+	if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) {
+		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) {
+			do_dump |= LPFC_BSG_DMP_MBX_RD_BUF;
+			printk(KERN_ERR "\nRead mbox buffer (x%x), "
+			       "nemb:0x%x, extbuf_seq:%d:\n",
+			       sta_tp, nemb_tp, ext_buf);
+		}
+	}
+	if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) {
+		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) {
+			do_dump |= LPFC_BSG_DMP_MBX_WR_MBX;
+			printk(KERN_ERR "\nWrite mbox command (x%x), "
+			       "nemb:0x%x, extbuf_cnt:%d:\n",
+			       sta_tp, nemb_tp, ext_buf);
+		}
+	}
+	if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) {
+		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) {
+			do_dump |= LPFC_BSG_DMP_MBX_WR_BUF;
+			printk(KERN_ERR "\nWrite mbox buffer (x%x), "
+			       "nemb:0x%x, extbuf_seq:%d:\n",
+			       sta_tp, nemb_tp, ext_buf);
+		}
+	}
+
+	/* dump buffer content */
+	if (do_dump) {
+		pword = (uint32_t *)dmabuf->virt;
+		for (i = 0; i < *mbx_word_cnt; i++) {
+			if (!(i % 8)) {
+				if (i != 0)
+					printk(KERN_ERR "%s\n", line_buf);
+				len = 0;
+				len += snprintf(line_buf+len,
+						LPFC_MBX_ACC_LBUF_SZ-len,
+						"%03d: ", i);
+			}
+			len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
+					"%08x ", (uint32_t)*pword);
+			pword++;
+		}
+		if ((i - 1) % 8)
+			printk(KERN_ERR "%s\n", line_buf);
+		(*mbx_dump_cnt)--;
+	}
+
+	/* Clean out command structure on reaching dump count */
+	if (*mbx_dump_cnt == 0)
+		memset(&idiag, 0, sizeof(idiag));
+	return;
+#endif
+}
+
+/* lpfc_idiag_mbxacc_dump_issue_mbox - idiag debugfs dump issue mailbox command
+ * @phba: Pointer to HBA context object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
+ *
+ * Description:
+ * This routine dump a pass-through non-embedded mailbox command from issue
+ * mailbox command.
+ **/
+void
+lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+	uint32_t *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt, *mbx_mbox_cmd;
+	char line_buf[LPFC_MBX_ACC_LBUF_SZ];
+	int len = 0;
+	uint32_t *pword;
+	uint8_t *pbyte;
+	uint32_t i, j;
+
+	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP)
+		return;
+
+	mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+	mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+	mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+	mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+	if (!(*mbx_dump_map & LPFC_MBX_DMP_MBX_ALL) ||
+	    (*mbx_dump_cnt == 0) ||
+	    (*mbx_word_cnt == 0))
+		return;
+
+	if ((*mbx_mbox_cmd != LPFC_MBX_ALL_CMD) &&
+	    (*mbx_mbox_cmd != pmbox->mbxCommand))
+		return;
+
+	/* dump buffer content */
+	if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) {
+		printk(KERN_ERR "Mailbox command:0x%x dump by word:\n",
+		       pmbox->mbxCommand);
+		pword = (uint32_t *)pmbox;
+		for (i = 0; i < *mbx_word_cnt; i++) {
+			if (!(i % 8)) {
+				if (i != 0)
+					printk(KERN_ERR "%s\n", line_buf);
+				len = 0;
+				memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
+				len += snprintf(line_buf+len,
+						LPFC_MBX_ACC_LBUF_SZ-len,
+						"%03d: ", i);
+			}
+			len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
+					"%08x ",
+					((uint32_t)*pword) & 0xffffffff);
+			pword++;
+		}
+		if ((i - 1) % 8)
+			printk(KERN_ERR "%s\n", line_buf);
+		printk(KERN_ERR "\n");
+	}
+	if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) {
+		printk(KERN_ERR "Mailbox command:0x%x dump by byte:\n",
+		       pmbox->mbxCommand);
+		pbyte = (uint8_t *)pmbox;
+		for (i = 0; i < *mbx_word_cnt; i++) {
+			if (!(i % 8)) {
+				if (i != 0)
+					printk(KERN_ERR "%s\n", line_buf);
+				len = 0;
+				memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
+				len += snprintf(line_buf+len,
+						LPFC_MBX_ACC_LBUF_SZ-len,
+						"%03d: ", i);
+			}
+			for (j = 0; j < 4; j++) {
+				len += snprintf(line_buf+len,
+						LPFC_MBX_ACC_LBUF_SZ-len,
+						"%02x",
+						((uint8_t)*pbyte) & 0xff);
+				pbyte++;
+			}
+			len += snprintf(line_buf+len,
+					LPFC_MBX_ACC_LBUF_SZ-len, " ");
+		}
+		if ((i - 1) % 8)
+			printk(KERN_ERR "%s\n", line_buf);
+		printk(KERN_ERR "\n");
+	}
+	(*mbx_dump_cnt)--;
+
+	/* Clean out command structure on reaching dump count */
+	if (*mbx_dump_cnt == 0)
+		memset(&idiag, 0, sizeof(idiag));
+	return;
+#endif
+}
+
 /**
  * lpfc_debugfs_initialize - Initialize debugfs for a vport
  * @vport: The vport pointer to initialize.
@@ -2673,7 +3894,7 @@
 				 vport, &lpfc_debugfs_op_nodelist);
 	if (!vport->debug_nodelist) {
 		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
-				 "0409 Can't create debugfs nodelist\n");
+				 "2985 Can't create debugfs nodelist\n");
 		goto debug_failed;
 	}
 
@@ -2710,6 +3931,20 @@
 		idiag.offset.last_rd = 0;
 	}
 
+	/* iDiag PCI BAR access */
+	snprintf(name, sizeof(name), "barAcc");
+	if (!phba->idiag_bar_acc) {
+		phba->idiag_bar_acc =
+			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+				phba->idiag_root, phba, &lpfc_idiag_op_barAcc);
+		if (!phba->idiag_bar_acc) {
+			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+					"3056 Can't create idiag debugfs\n");
+			goto debug_failed;
+		}
+		idiag.offset.last_rd = 0;
+	}
+
 	/* iDiag get PCI function queue information */
 	snprintf(name, sizeof(name), "queInfo");
 	if (!phba->idiag_que_info) {
@@ -2749,6 +3984,50 @@
 		}
 	}
 
+	/* iDiag access PCI function control registers */
+	snprintf(name, sizeof(name), "ctlAcc");
+	if (!phba->idiag_ctl_acc) {
+		phba->idiag_ctl_acc =
+			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+				phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc);
+		if (!phba->idiag_ctl_acc) {
+			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+					 "2981 Can't create idiag debugfs\n");
+			goto debug_failed;
+		}
+	}
+
+	/* iDiag access mbox commands */
+	snprintf(name, sizeof(name), "mbxAcc");
+	if (!phba->idiag_mbx_acc) {
+		phba->idiag_mbx_acc =
+			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+				phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc);
+		if (!phba->idiag_mbx_acc) {
+			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+					"2980 Can't create idiag debugfs\n");
+			goto debug_failed;
+		}
+	}
+
+	/* iDiag extents access commands */
+	if (phba->sli4_hba.extents_in_use) {
+		snprintf(name, sizeof(name), "extAcc");
+		if (!phba->idiag_ext_acc) {
+			phba->idiag_ext_acc =
+				debugfs_create_file(name,
+						    S_IFREG|S_IRUGO|S_IWUSR,
+						    phba->idiag_root, phba,
+						    &lpfc_idiag_op_extAcc);
+			if (!phba->idiag_ext_acc) {
+				lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+						"2986 Cant create "
+						"idiag debugfs\n");
+				goto debug_failed;
+			}
+		}
+	}
+
 debug_failed:
 	return;
 #endif
@@ -2783,7 +4062,6 @@
 		debugfs_remove(vport->debug_nodelist); /* nodelist */
 		vport->debug_nodelist = NULL;
 	}
-
 	if (vport->vport_debugfs_root) {
 		debugfs_remove(vport->vport_debugfs_root); /* vportX */
 		vport->vport_debugfs_root = NULL;
@@ -2827,6 +4105,21 @@
 		 * iDiag release
 		 */
 		if (phba->sli_rev == LPFC_SLI_REV4) {
+			if (phba->idiag_ext_acc) {
+				/* iDiag extAcc */
+				debugfs_remove(phba->idiag_ext_acc);
+				phba->idiag_ext_acc = NULL;
+			}
+			if (phba->idiag_mbx_acc) {
+				/* iDiag mbxAcc */
+				debugfs_remove(phba->idiag_mbx_acc);
+				phba->idiag_mbx_acc = NULL;
+			}
+			if (phba->idiag_ctl_acc) {
+				/* iDiag ctlAcc */
+				debugfs_remove(phba->idiag_ctl_acc);
+				phba->idiag_ctl_acc = NULL;
+			}
 			if (phba->idiag_drb_acc) {
 				/* iDiag drbAcc */
 				debugfs_remove(phba->idiag_drb_acc);
@@ -2842,6 +4135,11 @@
 				debugfs_remove(phba->idiag_que_info);
 				phba->idiag_que_info = NULL;
 			}
+			if (phba->idiag_bar_acc) {
+				/* iDiag barAcc */
+				debugfs_remove(phba->idiag_bar_acc);
+				phba->idiag_bar_acc = NULL;
+			}
 			if (phba->idiag_pci_cfg) {
 				/* iDiag pciCfg */
 				debugfs_remove(phba->idiag_pci_cfg);
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 6525a5e..f83bd94 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -39,14 +39,51 @@
 /* hbqinfo output buffer size */
 #define LPFC_HBQINFO_SIZE 8192
 
+/*
+ * For SLI4 iDiag debugfs diagnostics tool
+ */
+
 /* pciConf */
 #define LPFC_PCI_CFG_BROWSE 0xffff
 #define LPFC_PCI_CFG_RD_CMD_ARG 2
 #define LPFC_PCI_CFG_WR_CMD_ARG 3
 #define LPFC_PCI_CFG_SIZE 4096
-#define LPFC_PCI_CFG_RD_BUF_SIZE (LPFC_PCI_CFG_SIZE/2)
 #define LPFC_PCI_CFG_RD_SIZE (LPFC_PCI_CFG_SIZE/4)
 
+#define IDIAG_PCICFG_WHERE_INDX 0
+#define IDIAG_PCICFG_COUNT_INDX 1
+#define IDIAG_PCICFG_VALUE_INDX 2
+
+/* barAcc */
+#define LPFC_PCI_BAR_BROWSE 0xffff
+#define LPFC_PCI_BAR_RD_CMD_ARG 3
+#define LPFC_PCI_BAR_WR_CMD_ARG 3
+
+#define LPFC_PCI_IF0_BAR0_SIZE (1024 *  16)
+#define LPFC_PCI_IF0_BAR1_SIZE (1024 * 128)
+#define LPFC_PCI_IF0_BAR2_SIZE (1024 * 128)
+#define LPFC_PCI_IF2_BAR0_SIZE (1024 *  32)
+
+#define LPFC_PCI_BAR_RD_BUF_SIZE 4096
+#define LPFC_PCI_BAR_RD_SIZE (LPFC_PCI_BAR_RD_BUF_SIZE/4)
+
+#define LPFC_PCI_IF0_BAR0_RD_SIZE (LPFC_PCI_IF0_BAR0_SIZE/4)
+#define LPFC_PCI_IF0_BAR1_RD_SIZE (LPFC_PCI_IF0_BAR1_SIZE/4)
+#define LPFC_PCI_IF0_BAR2_RD_SIZE (LPFC_PCI_IF0_BAR2_SIZE/4)
+#define LPFC_PCI_IF2_BAR0_RD_SIZE (LPFC_PCI_IF2_BAR0_SIZE/4)
+
+#define IDIAG_BARACC_BAR_NUM_INDX 0
+#define IDIAG_BARACC_OFF_SET_INDX 1
+#define IDIAG_BARACC_ACC_MOD_INDX 2
+#define IDIAG_BARACC_REG_VAL_INDX 2
+#define IDIAG_BARACC_BAR_SZE_INDX 3
+
+#define IDIAG_BARACC_BAR_0 0
+#define IDIAG_BARACC_BAR_1 1
+#define IDIAG_BARACC_BAR_2 2
+
+#define SINGLE_WORD 1
+
 /* queue info */
 #define LPFC_QUE_INFO_GET_BUF_SIZE 4096
 
@@ -63,7 +100,14 @@
 #define LPFC_IDIAG_WQ 4
 #define LPFC_IDIAG_RQ 5
 
-/* doorbell acc */
+#define IDIAG_QUEACC_QUETP_INDX 0
+#define IDIAG_QUEACC_QUEID_INDX 1
+#define IDIAG_QUEACC_INDEX_INDX 2
+#define IDIAG_QUEACC_COUNT_INDX 3
+#define IDIAG_QUEACC_OFFST_INDX 4
+#define IDIAG_QUEACC_VALUE_INDX 5
+
+/* doorbell register acc */
 #define LPFC_DRB_ACC_ALL 0xffff
 #define LPFC_DRB_ACC_RD_CMD_ARG 1
 #define LPFC_DRB_ACC_WR_CMD_ARG 2
@@ -76,6 +120,67 @@
 
 #define LPFC_DRB_MAX  4
 
+#define IDIAG_DRBACC_REGID_INDX 0
+#define IDIAG_DRBACC_VALUE_INDX 1
+
+/* control register acc */
+#define LPFC_CTL_ACC_ALL 0xffff
+#define LPFC_CTL_ACC_RD_CMD_ARG 1
+#define LPFC_CTL_ACC_WR_CMD_ARG 2
+#define LPFC_CTL_ACC_BUF_SIZE 256
+
+#define LPFC_CTL_PORT_SEM  1
+#define LPFC_CTL_PORT_STA  2
+#define LPFC_CTL_PORT_CTL  3
+#define LPFC_CTL_PORT_ER1  4
+#define LPFC_CTL_PORT_ER2  5
+#define LPFC_CTL_PDEV_CTL  6
+
+#define LPFC_CTL_MAX  6
+
+#define IDIAG_CTLACC_REGID_INDX 0
+#define IDIAG_CTLACC_VALUE_INDX 1
+
+/* mailbox access */
+#define LPFC_MBX_DMP_ARG 4
+
+#define LPFC_MBX_ACC_BUF_SIZE 512
+#define LPFC_MBX_ACC_LBUF_SZ 128
+
+#define LPFC_MBX_DMP_MBX_WORD 0x00000001
+#define LPFC_MBX_DMP_MBX_BYTE 0x00000002
+#define LPFC_MBX_DMP_MBX_ALL (LPFC_MBX_DMP_MBX_WORD | LPFC_MBX_DMP_MBX_BYTE)
+
+#define LPFC_BSG_DMP_MBX_RD_MBX 0x00000001
+#define LPFC_BSG_DMP_MBX_RD_BUF 0x00000002
+#define LPFC_BSG_DMP_MBX_WR_MBX 0x00000004
+#define LPFC_BSG_DMP_MBX_WR_BUF 0x00000008
+#define LPFC_BSG_DMP_MBX_ALL (LPFC_BSG_DMP_MBX_RD_MBX | \
+			      LPFC_BSG_DMP_MBX_RD_BUF | \
+			      LPFC_BSG_DMP_MBX_WR_MBX | \
+			      LPFC_BSG_DMP_MBX_WR_BUF)
+
+#define LPFC_MBX_DMP_ALL 0xffff
+#define LPFC_MBX_ALL_CMD 0xff
+
+#define IDIAG_MBXACC_MBCMD_INDX 0
+#define IDIAG_MBXACC_DPMAP_INDX 1
+#define IDIAG_MBXACC_DPCNT_INDX 2
+#define IDIAG_MBXACC_WDCNT_INDX 3
+
+/* extents access */
+#define LPFC_EXT_ACC_CMD_ARG 1
+#define LPFC_EXT_ACC_BUF_SIZE 4096
+
+#define LPFC_EXT_ACC_AVAIL 0x1
+#define LPFC_EXT_ACC_ALLOC 0x2
+#define LPFC_EXT_ACC_DRIVR 0x4
+#define LPFC_EXT_ACC_ALL   (LPFC_EXT_ACC_DRIVR | \
+			    LPFC_EXT_ACC_AVAIL | \
+			    LPFC_EXT_ACC_ALLOC)
+
+#define IDIAG_EXTACC_EXMAP_INDX 0
+
 #define SIZE_U8  sizeof(uint8_t)
 #define SIZE_U16 sizeof(uint16_t)
 #define SIZE_U32 sizeof(uint32_t)
@@ -110,6 +215,11 @@
 #define LPFC_IDIAG_CMD_PCICFG_ST 0x00000003
 #define LPFC_IDIAG_CMD_PCICFG_CL 0x00000004
 
+#define LPFC_IDIAG_CMD_BARACC_RD 0x00000008
+#define LPFC_IDIAG_CMD_BARACC_WR 0x00000009
+#define LPFC_IDIAG_CMD_BARACC_ST 0x0000000a
+#define LPFC_IDIAG_CMD_BARACC_CL 0x0000000b
+
 #define LPFC_IDIAG_CMD_QUEACC_RD 0x00000011
 #define LPFC_IDIAG_CMD_QUEACC_WR 0x00000012
 #define LPFC_IDIAG_CMD_QUEACC_ST 0x00000013
@@ -119,6 +229,17 @@
 #define LPFC_IDIAG_CMD_DRBACC_WR 0x00000022
 #define LPFC_IDIAG_CMD_DRBACC_ST 0x00000023
 #define LPFC_IDIAG_CMD_DRBACC_CL 0x00000024
+
+#define LPFC_IDIAG_CMD_CTLACC_RD 0x00000031
+#define LPFC_IDIAG_CMD_CTLACC_WR 0x00000032
+#define LPFC_IDIAG_CMD_CTLACC_ST 0x00000033
+#define LPFC_IDIAG_CMD_CTLACC_CL 0x00000034
+
+#define LPFC_IDIAG_CMD_MBXACC_DP 0x00000041
+#define LPFC_IDIAG_BSG_MBXACC_DP 0x00000042
+
+#define LPFC_IDIAG_CMD_EXTACC_RD 0x00000051
+
 	uint32_t data[LPFC_IDIAG_CMD_DATA_SIZE];
 };
 
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 32a0845..023da0e 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -647,21 +647,15 @@
 		}
 		lpfc_cleanup_pending_mbox(vport);
 
-		if (phba->sli_rev == LPFC_SLI_REV4)
+		if (phba->sli_rev == LPFC_SLI_REV4) {
 			lpfc_sli4_unreg_all_rpis(vport);
-
-		if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
 			lpfc_mbx_unreg_vpi(vport);
 			spin_lock_irq(shost->host_lock);
 			vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
-			spin_unlock_irq(shost->host_lock);
-		}
-		/*
-		 * If VPI is unreged, driver need to do INIT_VPI
-		 * before re-registering
-		 */
-		if (phba->sli_rev == LPFC_SLI_REV4) {
-			spin_lock_irq(shost->host_lock);
+			/*
+			* If VPI is unreged, driver need to do INIT_VPI
+			* before re-registering
+			*/
 			vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
 			spin_unlock_irq(shost->host_lock);
 		}
@@ -880,6 +874,8 @@
 					phba->fcf.current_rec.fcf_indx,
 					irsp->ulpStatus, irsp->un.ulpWord[4],
 					irsp->ulpTimeout);
+			lpfc_sli4_set_fcf_flogi_fail(phba,
+					phba->fcf.current_rec.fcf_indx);
 			fcf_index = lpfc_sli4_fcf_rr_next_index_get(phba);
 			rc = lpfc_sli4_fcf_rr_next_proc(vport, fcf_index);
 			if (rc)
@@ -1096,11 +1092,14 @@
 			/* Set the fcfi to the fcfi we registered with */
 			elsiocb->iocb.ulpContext = phba->fcf.fcfi;
 		}
-	} else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
-		sp->cmn.request_multiple_Nport = 1;
-		/* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
-		icmd->ulpCt_h = 1;
-		icmd->ulpCt_l = 0;
+	} else {
+		if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+			sp->cmn.request_multiple_Nport = 1;
+			/* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
+			icmd->ulpCt_h = 1;
+			icmd->ulpCt_l = 0;
+		} else
+			sp->cmn.request_multiple_Nport = 0;
 	}
 
 	if (phba->fc_topology != LPFC_TOPOLOGY_LOOP) {
@@ -3656,7 +3655,8 @@
 		}
 
 		icmd = &elsiocb->iocb;
-		icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+		icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+		icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 		pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 		*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
 		pcmd += sizeof(uint32_t);
@@ -3673,7 +3673,8 @@
 			return 1;
 
 		icmd = &elsiocb->iocb;
-		icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+		icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+		icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 		pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 
 		if (mbox)
@@ -3695,7 +3696,8 @@
 			return 1;
 
 		icmd = &elsiocb->iocb;
-		icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+		icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+		icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 		pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 
 		memcpy(pcmd, ((struct lpfc_dmabuf *) oldiocb->context2)->virt,
@@ -3781,7 +3783,8 @@
 
 	icmd = &elsiocb->iocb;
 	oldcmd = &oldiocb->iocb;
-	icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+	icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+	icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 
 	*((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
@@ -3853,7 +3856,8 @@
 
 	icmd = &elsiocb->iocb;
 	oldcmd = &oldiocb->iocb;
-	icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+	icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+	icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 
 	/* Xmit ADISC ACC response tag <ulpIoTag> */
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -3931,7 +3935,9 @@
 
 	icmd = &elsiocb->iocb;
 	oldcmd = &oldiocb->iocb;
-	icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+	icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+	icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
+
 	/* Xmit PRLI ACC response tag <ulpIoTag> */
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
 			 "0131 Xmit PRLI ACC response tag x%x xri x%x, "
@@ -4035,7 +4041,9 @@
 
 	icmd = &elsiocb->iocb;
 	oldcmd = &oldiocb->iocb;
-	icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+	icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+	icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
+
 	/* Xmit RNID ACC response tag <ulpIoTag> */
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
 			 "0132 Xmit RNID ACC response tag x%x xri x%x\n",
@@ -4163,7 +4171,9 @@
 	if (!elsiocb)
 		return 1;
 
-	elsiocb->iocb.ulpContext = oldiocb->iocb.ulpContext;	/* Xri */
+	elsiocb->iocb.ulpContext = oldiocb->iocb.ulpContext;  /* Xri / rx_id */
+	elsiocb->iocb.unsli3.rcvsli3.ox_id = oldiocb->iocb.unsli3.rcvsli3.ox_id;
+
 	/* Xmit ECHO ACC response tag <ulpIoTag> */
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
 			 "2876 Xmit ECHO ACC response tag x%x xri x%x\n",
@@ -5054,13 +5064,15 @@
 	uint8_t *pcmd;
 	struct lpfc_iocbq *elsiocb;
 	struct lpfc_nodelist *ndlp;
-	uint16_t xri;
+	uint16_t oxid;
+	uint16_t rxid;
 	uint32_t cmdsize;
 
 	mb = &pmb->u.mb;
 
 	ndlp = (struct lpfc_nodelist *) pmb->context2;
-	xri = (uint16_t) ((unsigned long)(pmb->context1));
+	rxid = (uint16_t) ((unsigned long)(pmb->context1) & 0xffff);
+	oxid = (uint16_t) (((unsigned long)(pmb->context1) >> 16) & 0xffff);
 	pmb->context1 = NULL;
 	pmb->context2 = NULL;
 
@@ -5082,7 +5094,8 @@
 		return;
 
 	icmd = &elsiocb->iocb;
-	icmd->ulpContext = xri;
+	icmd->ulpContext = rxid;
+	icmd->unsli3.rcvsli3.ox_id = oxid;
 
 	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 	*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
@@ -5137,13 +5150,16 @@
 	uint8_t *pcmd;
 	struct lpfc_iocbq *elsiocb;
 	struct lpfc_nodelist *ndlp;
-	uint16_t xri, status;
+	uint16_t status;
+	uint16_t oxid;
+	uint16_t rxid;
 	uint32_t cmdsize;
 
 	mb = &pmb->u.mb;
 
 	ndlp = (struct lpfc_nodelist *) pmb->context2;
-	xri = (uint16_t) ((unsigned long)(pmb->context1));
+	rxid = (uint16_t) ((unsigned long)(pmb->context1) & 0xffff);
+	oxid = (uint16_t) (((unsigned long)(pmb->context1) >> 16) & 0xffff);
 	pmb->context1 = NULL;
 	pmb->context2 = NULL;
 
@@ -5165,7 +5181,8 @@
 		return;
 
 	icmd = &elsiocb->iocb;
-	icmd->ulpContext = xri;
+	icmd->ulpContext = rxid;
+	icmd->unsli3.rcvsli3.ox_id = oxid;
 
 	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 	*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
@@ -5238,8 +5255,9 @@
 	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
 	if (mbox) {
 		lpfc_read_lnk_stat(phba, mbox);
-		mbox->context1 =
-		    (void *)((unsigned long) cmdiocb->iocb.ulpContext);
+		mbox->context1 = (void *)((unsigned long)
+			((cmdiocb->iocb.unsli3.rcvsli3.ox_id << 16) |
+			cmdiocb->iocb.ulpContext)); /* rx_id */
 		mbox->context2 = lpfc_nlp_get(ndlp);
 		mbox->vport = vport;
 		mbox->mbox_cmpl = lpfc_els_rsp_rls_acc;
@@ -5314,7 +5332,8 @@
 	pcmd += sizeof(uint32_t); /* Skip past command */
 
 	/* use the command's xri in the response */
-	elsiocb->iocb.ulpContext = cmdiocb->iocb.ulpContext;
+	elsiocb->iocb.ulpContext = cmdiocb->iocb.ulpContext;  /* Xri / rx_id */
+	elsiocb->iocb.unsli3.rcvsli3.ox_id = cmdiocb->iocb.unsli3.rcvsli3.ox_id;
 
 	rtv_rsp = (struct RTV_RSP *)pcmd;
 
@@ -5399,8 +5418,9 @@
 		mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
 		if (mbox) {
 			lpfc_read_lnk_stat(phba, mbox);
-			mbox->context1 =
-			    (void *)((unsigned long) cmdiocb->iocb.ulpContext);
+			mbox->context1 = (void *)((unsigned long)
+				((cmdiocb->iocb.unsli3.rcvsli3.ox_id << 16) |
+				cmdiocb->iocb.ulpContext)); /* rx_id */
 			mbox->context2 = lpfc_nlp_get(ndlp);
 			mbox->vport = vport;
 			mbox->mbox_cmpl = lpfc_els_rsp_rps_acc;
@@ -5554,7 +5574,8 @@
 
 	icmd = &elsiocb->iocb;
 	oldcmd = &oldiocb->iocb;
-	icmd->ulpContext = oldcmd->ulpContext;	/* Xri */
+	icmd->ulpContext = oldcmd->ulpContext;	/* Xri / rx_id */
+	icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
 
 	pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
 	*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
@@ -6586,7 +6607,7 @@
 {
 	struct lpfc_vport *vport;
 	unsigned long flags;
-	int i;
+	int i = 0;
 
 	/* The physical ports are always vpi 0 - translate is unnecessary. */
 	if (vpi > 0) {
@@ -6609,7 +6630,7 @@
 
 	spin_lock_irqsave(&phba->hbalock, flags);
 	list_for_each_entry(vport, &phba->port_list, listentry) {
-		if (vport->vpi == vpi) {
+		if (vport->vpi == i) {
 			spin_unlock_irqrestore(&phba->hbalock, flags);
 			return vport;
 		}
@@ -7787,6 +7808,7 @@
 {
 	uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
 	uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
+	uint16_t lxri = 0;
 
 	struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
 	unsigned long iflag = 0;
@@ -7815,7 +7837,12 @@
 		}
 	}
 	spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
-	sglq_entry = __lpfc_get_active_sglq(phba, xri);
+	lxri = lpfc_sli4_xri_inrange(phba, xri);
+	if (lxri == NO_XRI) {
+		spin_unlock_irqrestore(&phba->hbalock, iflag);
+		return;
+	}
+	sglq_entry = __lpfc_get_active_sglq(phba, lxri);
 	if (!sglq_entry || (sglq_entry->sli4_xritag != xri)) {
 		spin_unlock_irqrestore(&phba->hbalock, iflag);
 		return;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 18d0dbf..0b47adf 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1109,6 +1109,28 @@
 	return;
 }
 
+/**
+ * lpfc_sli4_clear_fcf_rr_bmask
+ * @phba pointer to the struct lpfc_hba for this port.
+ * This fucnction resets the round robin bit mask and clears the
+ * fcf priority list. The list deletions are done while holding the
+ * hbalock. The ON_LIST flag and the FLOGI_FAILED flags are cleared
+ * from the lpfc_fcf_pri record.
+ **/
+void
+lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *phba)
+{
+	struct lpfc_fcf_pri *fcf_pri;
+	struct lpfc_fcf_pri *next_fcf_pri;
+	memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask));
+	spin_lock_irq(&phba->hbalock);
+	list_for_each_entry_safe(fcf_pri, next_fcf_pri,
+				&phba->fcf.fcf_pri_list, list) {
+		list_del_init(&fcf_pri->list);
+		fcf_pri->fcf_rec.flag = 0;
+	}
+	spin_unlock_irq(&phba->hbalock);
+}
 static void
 lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
 {
@@ -1130,7 +1152,8 @@
 	spin_unlock_irq(&phba->hbalock);
 
 	/* If there is a pending FCoE event, restart FCF table scan. */
-	if (lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF))
+	if ((!(phba->hba_flag & FCF_RR_INPROG)) &&
+		lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF))
 		goto fail_out;
 
 	/* Mark successful completion of FCF table scan */
@@ -1250,6 +1273,30 @@
 }
 
 /**
+ * lpfc_update_fcf_record - Update driver fcf record
+ * __lpfc_update_fcf_record_pri - update the lpfc_fcf_pri record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: Index for the lpfc_fcf_record.
+ * @new_fcf_record: pointer to hba fcf record.
+ *
+ * This routine updates the driver FCF priority record from the new HBA FCF
+ * record. This routine is called with the host lock held.
+ **/
+static void
+__lpfc_update_fcf_record_pri(struct lpfc_hba *phba, uint16_t fcf_index,
+				 struct fcf_record *new_fcf_record
+				 )
+{
+	struct lpfc_fcf_pri *fcf_pri;
+
+	fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+	fcf_pri->fcf_rec.fcf_index = fcf_index;
+	/* FCF record priority */
+	fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority;
+
+}
+
+/**
  * lpfc_copy_fcf_record - Copy fcf information to lpfc_hba.
  * @fcf: pointer to driver fcf record.
  * @new_fcf_record: pointer to fcf record.
@@ -1332,6 +1379,9 @@
 	fcf_rec->addr_mode = addr_mode;
 	fcf_rec->vlan_id = vlan_id;
 	fcf_rec->flag |= (flag | RECORD_VALID);
+	__lpfc_update_fcf_record_pri(phba,
+		bf_get(lpfc_fcf_record_fcf_index, new_fcf_record),
+				 new_fcf_record);
 }
 
 /**
@@ -1834,6 +1884,8 @@
 		return false;
 	if (!lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record))
 		return false;
+	if (fcf_rec->priority != new_fcf_record->fip_priority)
+		return false;
 	return true;
 }
 
@@ -1897,6 +1949,152 @@
 }
 
 /**
+ * lpfc_sli4_fcf_pri_list_del
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index the index of the fcf record to delete
+ * This routine checks the on list flag of the fcf_index to be deleted.
+ * If it is one the list then it is removed from the list, and the flag
+ * is cleared. This routine grab the hbalock before removing the fcf
+ * record from the list.
+ **/
+static void lpfc_sli4_fcf_pri_list_del(struct lpfc_hba *phba,
+			uint16_t fcf_index)
+{
+	struct lpfc_fcf_pri *new_fcf_pri;
+
+	new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+	lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+		"3058 deleting idx x%x pri x%x flg x%x\n",
+		fcf_index, new_fcf_pri->fcf_rec.priority,
+		 new_fcf_pri->fcf_rec.flag);
+	spin_lock_irq(&phba->hbalock);
+	if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST) {
+		if (phba->fcf.current_rec.priority ==
+				new_fcf_pri->fcf_rec.priority)
+			phba->fcf.eligible_fcf_cnt--;
+		list_del_init(&new_fcf_pri->list);
+		new_fcf_pri->fcf_rec.flag &= ~LPFC_FCF_ON_PRI_LIST;
+	}
+	spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_set_fcf_flogi_fail
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index the index of the fcf record to update
+ * This routine acquires the hbalock and then set the LPFC_FCF_FLOGI_FAILED
+ * flag so the the round robin slection for the particular priority level
+ * will try a different fcf record that does not have this bit set.
+ * If the fcf record is re-read for any reason this flag is cleared brfore
+ * adding it to the priority list.
+ **/
+void
+lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+	struct lpfc_fcf_pri *new_fcf_pri;
+	new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+	spin_lock_irq(&phba->hbalock);
+	new_fcf_pri->fcf_rec.flag |= LPFC_FCF_FLOGI_FAILED;
+	spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_fcf_pri_list_add
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index the index of the fcf record to add
+ * This routine checks the priority of the fcf_index to be added.
+ * If it is a lower priority than the current head of the fcf_pri list
+ * then it is added to the list in the right order.
+ * If it is the same priority as the current head of the list then it
+ * is added to the head of the list and its bit in the rr_bmask is set.
+ * If the fcf_index to be added is of a higher priority than the current
+ * head of the list then the rr_bmask is cleared, its bit is set in the
+ * rr_bmask and it is added to the head of the list.
+ * returns:
+ * 0=success 1=failure
+ **/
+int lpfc_sli4_fcf_pri_list_add(struct lpfc_hba *phba, uint16_t fcf_index,
+	struct fcf_record *new_fcf_record)
+{
+	uint16_t current_fcf_pri;
+	uint16_t last_index;
+	struct lpfc_fcf_pri *fcf_pri;
+	struct lpfc_fcf_pri *next_fcf_pri;
+	struct lpfc_fcf_pri *new_fcf_pri;
+	int ret;
+
+	new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+	lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+		"3059 adding idx x%x pri x%x flg x%x\n",
+		fcf_index, new_fcf_record->fip_priority,
+		 new_fcf_pri->fcf_rec.flag);
+	spin_lock_irq(&phba->hbalock);
+	if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST)
+		list_del_init(&new_fcf_pri->list);
+	new_fcf_pri->fcf_rec.fcf_index = fcf_index;
+	new_fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority;
+	if (list_empty(&phba->fcf.fcf_pri_list)) {
+		list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list);
+		ret = lpfc_sli4_fcf_rr_index_set(phba,
+				new_fcf_pri->fcf_rec.fcf_index);
+		goto out;
+	}
+
+	last_index = find_first_bit(phba->fcf.fcf_rr_bmask,
+				LPFC_SLI4_FCF_TBL_INDX_MAX);
+	if (last_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+		ret = 0; /* Empty rr list */
+		goto out;
+	}
+	current_fcf_pri = phba->fcf.fcf_pri[last_index].fcf_rec.priority;
+	if (new_fcf_pri->fcf_rec.priority <=  current_fcf_pri) {
+		list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list);
+		if (new_fcf_pri->fcf_rec.priority <  current_fcf_pri) {
+			memset(phba->fcf.fcf_rr_bmask, 0,
+				sizeof(*phba->fcf.fcf_rr_bmask));
+			/* fcfs_at_this_priority_level = 1; */
+			phba->fcf.eligible_fcf_cnt = 1;
+		} else
+			/* fcfs_at_this_priority_level++; */
+			phba->fcf.eligible_fcf_cnt++;
+		ret = lpfc_sli4_fcf_rr_index_set(phba,
+				new_fcf_pri->fcf_rec.fcf_index);
+		goto out;
+	}
+
+	list_for_each_entry_safe(fcf_pri, next_fcf_pri,
+				&phba->fcf.fcf_pri_list, list) {
+		if (new_fcf_pri->fcf_rec.priority <=
+				fcf_pri->fcf_rec.priority) {
+			if (fcf_pri->list.prev == &phba->fcf.fcf_pri_list)
+				list_add(&new_fcf_pri->list,
+						&phba->fcf.fcf_pri_list);
+			else
+				list_add(&new_fcf_pri->list,
+					 &((struct lpfc_fcf_pri *)
+					fcf_pri->list.prev)->list);
+			ret = 0;
+			goto out;
+		} else if (fcf_pri->list.next == &phba->fcf.fcf_pri_list
+			|| new_fcf_pri->fcf_rec.priority <
+				next_fcf_pri->fcf_rec.priority) {
+			list_add(&new_fcf_pri->list, &fcf_pri->list);
+			ret = 0;
+			goto out;
+		}
+		if (new_fcf_pri->fcf_rec.priority > fcf_pri->fcf_rec.priority)
+			continue;
+
+	}
+	ret = 1;
+out:
+	/* we use = instead of |= to clear the FLOGI_FAILED flag. */
+	new_fcf_pri->fcf_rec.flag = LPFC_FCF_ON_PRI_LIST;
+	spin_unlock_irq(&phba->hbalock);
+	return ret;
+}
+
+/**
  * lpfc_mbx_cmpl_fcf_scan_read_fcf_rec - fcf scan read_fcf mbox cmpl handler.
  * @phba: pointer to lpfc hba data structure.
  * @mboxq: pointer to mailbox object.
@@ -1958,6 +2156,9 @@
 	 * record for roundrobin FCF failover.
 	 */
 	if (!rc) {
+		lpfc_sli4_fcf_pri_list_del(phba,
+					bf_get(lpfc_fcf_record_fcf_index,
+					       new_fcf_record));
 		lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
 				"2781 FCF (x%x) failed connection "
 				"list check: (x%x/x%x)\n",
@@ -2005,7 +2206,8 @@
 		goto read_next_fcf;
 	} else {
 		fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
-		rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index);
+		rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index,
+							new_fcf_record);
 		if (rc)
 			goto read_next_fcf;
 	}
@@ -2018,7 +2220,8 @@
 	 */
 	spin_lock_irq(&phba->hbalock);
 	if (phba->fcf.fcf_flag & FCF_IN_USE) {
-		if (lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec,
+		if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV &&
+			lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec,
 		    new_fcf_record, vlan_id)) {
 			if (bf_get(lpfc_fcf_record_fcf_index, new_fcf_record) ==
 			    phba->fcf.current_rec.fcf_indx) {
@@ -2232,7 +2435,8 @@
 			    (phba->fcf.fcf_flag & FCF_REDISC_PEND))
 				return;
 
-			if (phba->fcf.fcf_flag & FCF_IN_USE) {
+			if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV &&
+				phba->fcf.fcf_flag & FCF_IN_USE) {
 				/*
 				 * In case the current in-use FCF record no
 				 * longer existed during FCF discovery that
@@ -2247,7 +2451,6 @@
 				spin_lock_irq(&phba->hbalock);
 				phba->fcf.fcf_flag |= FCF_REDISC_FOV;
 				spin_unlock_irq(&phba->hbalock);
-				lpfc_sli4_mbox_cmd_free(phba, mboxq);
 				lpfc_sli4_fcf_scan_read_fcf_rec(phba,
 						LPFC_FCOE_FCF_GET_FIRST);
 				return;
@@ -2424,7 +2627,8 @@
 
 	/* Update the eligible FCF record index bmask */
 	fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
-	rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index);
+
+	rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index, new_fcf_record);
 
 out:
 	lpfc_sli4_mbox_cmd_free(phba, mboxq);
@@ -2645,6 +2849,7 @@
 	vport->vpi_state |= LPFC_VPI_REGISTERED;
 	vport->fc_flag |= FC_VFI_REGISTERED;
 	vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
+	vport->fc_flag &= ~FC_VPORT_NEEDS_INIT_VPI;
 	spin_unlock_irq(shost->host_lock);
 
 	if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
@@ -2893,8 +3098,7 @@
 			goto out;
 		}
 		/* Reset FCF roundrobin bmask for new discovery */
-		memset(phba->fcf.fcf_rr_bmask, 0,
-		       sizeof(*phba->fcf.fcf_rr_bmask));
+		lpfc_sli4_clear_fcf_rr_bmask(phba);
 	}
 
 	return;
@@ -5592,7 +5796,7 @@
 	spin_unlock_irq(&phba->hbalock);
 
 	/* Reset FCF roundrobin bmask for new discovery */
-	memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask));
+	lpfc_sli4_clear_fcf_rr_bmask(phba);
 
 	rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba, LPFC_FCOE_FCF_GET_FIRST);
 
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index ab4c4d6..046edc4 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -3470,11 +3470,16 @@
    or CMD_IOCB_RCV_SEQ64_CX (0xB5) */
 
 struct rcv_sli3 {
-	uint32_t word8Rsvd;
 #ifdef __BIG_ENDIAN_BITFIELD
+	uint16_t ox_id;
+	uint16_t seq_cnt;
+
 	uint16_t vpi;
 	uint16_t word9Rsvd;
 #else  /*  __LITTLE_ENDIAN */
+	uint16_t seq_cnt;
+	uint16_t ox_id;
+
 	uint16_t word9Rsvd;
 	uint16_t vpi;
 #endif
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 11e26a2..7f8003b 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -170,15 +170,8 @@
 #define LPFC_PCI_FUNC3		3
 #define LPFC_PCI_FUNC4		4
 
-/* SLI4 interface type-2 control register offsets */
-#define LPFC_CTL_PORT_SEM_OFFSET	0x400
-#define LPFC_CTL_PORT_STA_OFFSET	0x404
-#define LPFC_CTL_PORT_CTL_OFFSET	0x408
-#define LPFC_CTL_PORT_ER1_OFFSET	0x40C
-#define LPFC_CTL_PORT_ER2_OFFSET	0x410
+/* SLI4 interface type-2 PDEV_CTL register */
 #define LPFC_CTL_PDEV_CTL_OFFSET	0x414
-
-/* Some SLI4 interface type-2 PDEV_CTL register bits */
 #define LPFC_CTL_PDEV_CTL_DRST		0x00000001
 #define LPFC_CTL_PDEV_CTL_FRST		0x00000002
 #define LPFC_CTL_PDEV_CTL_DD		0x00000004
@@ -337,6 +330,7 @@
 #define CQE_CODE_RELEASE_WQE		0x2
 #define CQE_CODE_RECEIVE		0x4
 #define CQE_CODE_XRI_ABORTED		0x5
+#define CQE_CODE_RECEIVE_V1		0x9
 
 /* completion queue entry for wqe completions */
 struct lpfc_wcqe_complete {
@@ -440,7 +434,10 @@
 #define FC_STATUS_RQ_BUF_LEN_EXCEEDED 	0x11 /* payload truncated */
 #define FC_STATUS_INSUFF_BUF_NEED_BUF 	0x12 /* Insufficient buffers */
 #define FC_STATUS_INSUFF_BUF_FRM_DISC 	0x13 /* Frame Discard */
-	uint32_t reserved1;
+	uint32_t word1;
+#define lpfc_rcqe_fcf_id_v1_SHIFT	0
+#define lpfc_rcqe_fcf_id_v1_MASK	0x0000003F
+#define lpfc_rcqe_fcf_id_v1_WORD	word1
 	uint32_t word2;
 #define lpfc_rcqe_length_SHIFT		16
 #define lpfc_rcqe_length_MASK		0x0000FFFF
@@ -451,6 +448,9 @@
 #define lpfc_rcqe_fcf_id_SHIFT		0
 #define lpfc_rcqe_fcf_id_MASK		0x0000003F
 #define lpfc_rcqe_fcf_id_WORD		word2
+#define lpfc_rcqe_rq_id_v1_SHIFT	0
+#define lpfc_rcqe_rq_id_v1_MASK		0x0000FFFF
+#define lpfc_rcqe_rq_id_v1_WORD		word2
 	uint32_t word3;
 #define lpfc_rcqe_valid_SHIFT		lpfc_cqe_valid_SHIFT
 #define lpfc_rcqe_valid_MASK		lpfc_cqe_valid_MASK
@@ -515,7 +515,7 @@
 /* The following BAR0 register sets are defined for if_type 0 and 2 UCNAs. */
 #define LPFC_SLI_INTF			0x0058
 
-#define LPFC_SLIPORT_IF2_SMPHR		0x0400
+#define LPFC_CTL_PORT_SEM_OFFSET	0x400
 #define lpfc_port_smphr_perr_SHIFT	31
 #define lpfc_port_smphr_perr_MASK	0x1
 #define lpfc_port_smphr_perr_WORD	word0
@@ -575,7 +575,7 @@
 #define LPFC_POST_STAGE_PORT_READY			0xC000
 #define LPFC_POST_STAGE_PORT_UE 			0xF000
 
-#define LPFC_SLIPORT_STATUS		0x0404
+#define LPFC_CTL_PORT_STA_OFFSET	0x404
 #define lpfc_sliport_status_err_SHIFT	31
 #define lpfc_sliport_status_err_MASK	0x1
 #define lpfc_sliport_status_err_WORD	word0
@@ -593,7 +593,7 @@
 #define lpfc_sliport_status_rdy_WORD	word0
 #define MAX_IF_TYPE_2_RESETS	1000
 
-#define LPFC_SLIPORT_CNTRL		0x0408
+#define LPFC_CTL_PORT_CTL_OFFSET	0x408
 #define lpfc_sliport_ctrl_end_SHIFT	30
 #define lpfc_sliport_ctrl_end_MASK	0x1
 #define lpfc_sliport_ctrl_end_WORD	word0
@@ -604,8 +604,8 @@
 #define lpfc_sliport_ctrl_ip_WORD	word0
 #define LPFC_SLIPORT_INIT_PORT	1
 
-#define LPFC_SLIPORT_ERR_1		0x040C
-#define LPFC_SLIPORT_ERR_2		0x0410
+#define LPFC_CTL_PORT_ER1_OFFSET	0x40C
+#define LPFC_CTL_PORT_ER2_OFFSET	0x410
 
 /* The following Registers apply to SLI4 if_type 0 UCNAs. They typically
  * reside in BAR 2.
@@ -3198,6 +3198,8 @@
 #define lpfc_grp_hdr_id_MASK		0x000000FF
 #define lpfc_grp_hdr_id_WORD		word2
 	uint8_t rev_name[128];
+	uint8_t date[12];
+	uint8_t revision[32];
 };
 
 #define FCP_COMMAND 0x0
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 148b98d..a3c8200 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -2927,6 +2927,8 @@
 				 sizeof fc_host_symbolic_name(shost));
 
 	fc_host_supported_speeds(shost) = 0;
+	if (phba->lmt & LMT_16Gb)
+		fc_host_supported_speeds(shost) |= FC_PORTSPEED_16GBIT;
 	if (phba->lmt & LMT_10Gb)
 		fc_host_supported_speeds(shost) |= FC_PORTSPEED_10GBIT;
 	if (phba->lmt & LMT_8Gb)
@@ -3632,8 +3634,7 @@
 			lpfc_sli4_fcf_dead_failthrough(phba);
 		} else {
 			/* Reset FCF roundrobin bmask for new discovery */
-			memset(phba->fcf.fcf_rr_bmask, 0,
-			       sizeof(*phba->fcf.fcf_rr_bmask));
+			lpfc_sli4_clear_fcf_rr_bmask(phba);
 			/*
 			 * Handling fast FCF failover to a DEAD FCF event is
 			 * considered equalivant to receiving CVL to all vports.
@@ -3647,7 +3648,7 @@
 			" tag 0x%x\n", acqe_fip->index, acqe_fip->event_tag);
 
 		vport = lpfc_find_vport_by_vpid(phba,
-				acqe_fip->index - phba->vpi_base);
+						acqe_fip->index);
 		ndlp = lpfc_sli4_perform_vport_cvl(vport);
 		if (!ndlp)
 			break;
@@ -3719,8 +3720,7 @@
 				 * Reset FCF roundrobin bmask for new
 				 * discovery.
 				 */
-				memset(phba->fcf.fcf_rr_bmask, 0,
-				       sizeof(*phba->fcf.fcf_rr_bmask));
+				lpfc_sli4_clear_fcf_rr_bmask(phba);
 		}
 		break;
 	default:
@@ -4035,6 +4035,34 @@
 }
 
 /**
+ * lpfc_sli_sriov_nr_virtfn_get - Get the number of sr-iov virtual functions
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function enables the PCI SR-IOV virtual functions to a physical
+ * function. It invokes the PCI SR-IOV api with the @nr_vfn provided to
+ * enable the number of virtual functions to the physical function. As
+ * not all devices support SR-IOV, the return code from the pci_enable_sriov()
+ * API call does not considered as an error condition for most of the device.
+ **/
+uint16_t
+lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *phba)
+{
+	struct pci_dev *pdev = phba->pcidev;
+	uint16_t nr_virtfn;
+	int pos;
+
+	if (!pdev->is_physfn)
+		return 0;
+
+	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+	if (pos == 0)
+		return 0;
+
+	pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF, &nr_virtfn);
+	return nr_virtfn;
+}
+
+/**
  * lpfc_sli_probe_sriov_nr_virtfn - Enable a number of sr-iov virtual functions
  * @phba: pointer to lpfc hba data structure.
  * @nr_vfn: number of virtual functions to be enabled.
@@ -4049,8 +4077,17 @@
 lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn)
 {
 	struct pci_dev *pdev = phba->pcidev;
+	uint16_t max_nr_vfn;
 	int rc;
 
+	max_nr_vfn = lpfc_sli_sriov_nr_virtfn_get(phba);
+	if (nr_vfn > max_nr_vfn) {
+		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+				"3057 Requested vfs (%d) greater than "
+				"supported vfs (%d)", nr_vfn, max_nr_vfn);
+		return -EINVAL;
+	}
+
 	rc = pci_enable_sriov(pdev, nr_vfn);
 	if (rc) {
 		lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -4516,7 +4553,7 @@
 		}
 	}
 
-	return rc;
+	return 0;
 
 out_free_fcp_eq_hdl:
 	kfree(phba->sli4_hba.fcp_eq_hdl);
@@ -4966,17 +5003,14 @@
  * @phba: pointer to lpfc hba data structure.
  *
  * This routine is invoked to post rpi header templates to the
- * HBA consistent with the SLI-4 interface spec.  This routine
+ * port for those SLI4 ports that do not support extents.  This routine
  * posts a PAGE_SIZE memory region to the port to hold up to
- * PAGE_SIZE modulo 64 rpi context headers.
- * No locks are held here because this is an initialization routine
- * called only from probe or lpfc_online when interrupts are not
- * enabled and the driver is reinitializing the device.
+ * PAGE_SIZE modulo 64 rpi context headers.  This is an initialization routine
+ * and should be called only when interrupts are disabled.
  *
  * Return codes
  * 	0 - successful
- * 	-ENOMEM - No available memory
- *      -EIO - The mailbox failed to complete successfully.
+ *	-ERROR - otherwise.
  **/
 int
 lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba)
@@ -5687,17 +5721,22 @@
 		break;
 	case LPFC_SLI_INTF_IF_TYPE_2:
 		phba->sli4_hba.u.if_type2.ERR1regaddr =
-			phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_ERR_1;
+			phba->sli4_hba.conf_regs_memmap_p +
+						LPFC_CTL_PORT_ER1_OFFSET;
 		phba->sli4_hba.u.if_type2.ERR2regaddr =
-			phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_ERR_2;
+			phba->sli4_hba.conf_regs_memmap_p +
+						LPFC_CTL_PORT_ER2_OFFSET;
 		phba->sli4_hba.u.if_type2.CTRLregaddr =
-			phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_CNTRL;
+			phba->sli4_hba.conf_regs_memmap_p +
+						LPFC_CTL_PORT_CTL_OFFSET;
 		phba->sli4_hba.u.if_type2.STATUSregaddr =
-			phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_STATUS;
+			phba->sli4_hba.conf_regs_memmap_p +
+						LPFC_CTL_PORT_STA_OFFSET;
 		phba->sli4_hba.SLIINTFregaddr =
 			phba->sli4_hba.conf_regs_memmap_p + LPFC_SLI_INTF;
 		phba->sli4_hba.PSMPHRregaddr =
-		     phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_IF2_SMPHR;
+			phba->sli4_hba.conf_regs_memmap_p +
+						LPFC_CTL_PORT_SEM_OFFSET;
 		phba->sli4_hba.RQDBregaddr =
 			phba->sli4_hba.conf_regs_memmap_p + LPFC_RQ_DOORBELL;
 		phba->sli4_hba.WQDBregaddr =
@@ -8859,11 +8898,11 @@
 		return -EINVAL;
 	}
 	lpfc_decode_firmware_rev(phba, fwrev, 1);
-	if (strncmp(fwrev, image->rev_name, strnlen(fwrev, 16))) {
+	if (strncmp(fwrev, image->revision, strnlen(image->revision, 16))) {
 		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
 				"3023 Updating Firmware. Current Version:%s "
 				"New Version:%s\n",
-				fwrev, image->rev_name);
+				fwrev, image->revision);
 		for (i = 0; i < LPFC_MBX_WR_CONFIG_MAX_BDE; i++) {
 			dmabuf = kzalloc(sizeof(struct lpfc_dmabuf),
 					 GFP_KERNEL);
@@ -8892,9 +8931,9 @@
 					       fw->size - offset);
 					break;
 				}
-				temp_offset += SLI4_PAGE_SIZE;
 				memcpy(dmabuf->virt, fw->data + temp_offset,
 				       SLI4_PAGE_SIZE);
+				temp_offset += SLI4_PAGE_SIZE;
 			}
 			rc = lpfc_wr_object(phba, &dma_buffer_list,
 				    (fw->size - offset), &offset);
@@ -9005,6 +9044,7 @@
 	}
 
 	INIT_LIST_HEAD(&phba->active_rrq_list);
+	INIT_LIST_HEAD(&phba->fcf.fcf_pri_list);
 
 	/* Set up common device driver resources */
 	error = lpfc_setup_driver_resource_phase2(phba);
@@ -9112,7 +9152,6 @@
 
 	/* Check if there are static vports to be created. */
 	lpfc_create_static_vport(phba);
-
 	return 0;
 
 out_disable_intr:
@@ -9483,6 +9522,13 @@
 	}
 
 	pci_restore_state(pdev);
+
+	/*
+	 * As the new kernel behavior of pci_restore_state() API call clears
+	 * device saved_state flag, need to save the restored state again.
+	 */
+	pci_save_state(pdev);
+
 	if (pdev->is_busmaster)
 		pci_set_master(pdev);
 
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 5567670..83450cc 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -2031,7 +2031,7 @@
 	bf_set(lpfc_init_vfi_vp, init_vfi, 1);
 	bf_set(lpfc_init_vfi_vfi, init_vfi,
 	       vport->phba->sli4_hba.vfi_ids[vport->vfi]);
-	bf_set(lpfc_init_vpi_vpi, init_vfi,
+	bf_set(lpfc_init_vfi_vpi, init_vfi,
 	       vport->phba->vpi_ids[vport->vpi]);
 	bf_set(lpfc_init_vfi_fcfi, init_vfi,
 	       vport->phba->fcf.fcfi);
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 3ccc974..eadd241 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1302,13 +1302,13 @@
 		case SCSI_PROT_NORMAL:
 		default:
 			lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-				"9063 BLKGRD: Bad op/guard:%d/%d combination\n",
-					scsi_get_prot_op(sc), guard_type);
+				"9063 BLKGRD: Bad op/guard:%d/IP combination\n",
+					scsi_get_prot_op(sc));
 			ret = 1;
 			break;
 
 		}
-	} else if (guard_type == SHOST_DIX_GUARD_CRC) {
+	} else {
 		switch (scsi_get_prot_op(sc)) {
 		case SCSI_PROT_READ_STRIP:
 		case SCSI_PROT_WRITE_INSERT:
@@ -1324,17 +1324,18 @@
 
 		case SCSI_PROT_READ_INSERT:
 		case SCSI_PROT_WRITE_STRIP:
+			*txop = BG_OP_IN_CRC_OUT_NODIF;
+			*rxop = BG_OP_IN_NODIF_OUT_CRC;
+			break;
+
 		case SCSI_PROT_NORMAL:
 		default:
 			lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-				"9075 BLKGRD: Bad op/guard:%d/%d combination\n",
-					scsi_get_prot_op(sc), guard_type);
+				"9075 BLKGRD: Bad op/guard:%d/CRC combination\n",
+					scsi_get_prot_op(sc));
 			ret = 1;
 			break;
 		}
-	} else {
-		/* unsupported format */
-		BUG();
 	}
 
 	return ret;
@@ -1352,45 +1353,6 @@
 	return sc->device->sector_size;
 }
 
-/**
- * lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
- * @sc:             in: SCSI command
- * @apptagmask:     out: app tag mask
- * @apptagval:      out: app tag value
- * @reftag:         out: ref tag (reference tag)
- *
- * Description:
- *   Extract DIF parameters from the command if possible.  Otherwise,
- *   use default parameters.
- *
- **/
-static inline void
-lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
-		uint16_t *apptagval, uint32_t *reftag)
-{
-	struct  scsi_dif_tuple *spt;
-	unsigned char op = scsi_get_prot_op(sc);
-	unsigned int protcnt = scsi_prot_sg_count(sc);
-	static int cnt;
-
-	if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
-				op == SCSI_PROT_WRITE_PASS)) {
-
-		cnt++;
-		spt = page_address(sg_page(scsi_prot_sglist(sc))) +
-			scsi_prot_sglist(sc)[0].offset;
-		*apptagmask = 0;
-		*apptagval = 0;
-		*reftag = cpu_to_be32(spt->ref_tag);
-
-	} else {
-		/* SBC defines ref tag to be lower 32bits of LBA */
-		*reftag = (uint32_t) (0xffffffff & scsi_get_lba(sc));
-		*apptagmask = 0;
-		*apptagval = 0;
-	}
-}
-
 /*
  * This function sets up buffer list for protection groups of
  * type LPFC_PG_TYPE_NO_DIF
@@ -1427,9 +1389,8 @@
 	dma_addr_t physaddr;
 	int i = 0, num_bde = 0, status;
 	int datadir = sc->sc_data_direction;
-	unsigned blksize;
 	uint32_t reftag;
-	uint16_t apptagmask, apptagval;
+	unsigned blksize;
 	uint8_t txop, rxop;
 
 	status  = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
@@ -1438,17 +1399,16 @@
 
 	/* extract some info from the scsi command for pde*/
 	blksize = lpfc_cmd_blksize(sc);
-	lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+	reftag = scsi_get_lba(sc) & 0xffffffff;
 
 	/* setup PDE5 with what we have */
 	pde5 = (struct lpfc_pde5 *) bpl;
 	memset(pde5, 0, sizeof(struct lpfc_pde5));
 	bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
-	pde5->reftag = reftag;
 
 	/* Endianness conversion if necessary for PDE5 */
 	pde5->word0 = cpu_to_le32(pde5->word0);
-	pde5->reftag = cpu_to_le32(pde5->reftag);
+	pde5->reftag = cpu_to_le32(reftag);
 
 	/* advance bpl and increment bde count */
 	num_bde++;
@@ -1463,10 +1423,10 @@
 	if (datadir == DMA_FROM_DEVICE) {
 		bf_set(pde6_ce, pde6, 1);
 		bf_set(pde6_re, pde6, 1);
-		bf_set(pde6_ae, pde6, 1);
 	}
 	bf_set(pde6_ai, pde6, 1);
-	bf_set(pde6_apptagval, pde6, apptagval);
+	bf_set(pde6_ae, pde6, 0);
+	bf_set(pde6_apptagval, pde6, 0);
 
 	/* Endianness conversion if necessary for PDE6 */
 	pde6->word0 = cpu_to_le32(pde6->word0);
@@ -1551,7 +1511,6 @@
 	unsigned char pgdone = 0, alldone = 0;
 	unsigned blksize;
 	uint32_t reftag;
-	uint16_t apptagmask, apptagval;
 	uint8_t txop, rxop;
 	int num_bde = 0;
 
@@ -1571,7 +1530,7 @@
 
 	/* extract some info from the scsi command */
 	blksize = lpfc_cmd_blksize(sc);
-	lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+	reftag = scsi_get_lba(sc) & 0xffffffff;
 
 	split_offset = 0;
 	do {
@@ -1579,11 +1538,10 @@
 		pde5 = (struct lpfc_pde5 *) bpl;
 		memset(pde5, 0, sizeof(struct lpfc_pde5));
 		bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
-		pde5->reftag = reftag;
 
 		/* Endianness conversion if necessary for PDE5 */
 		pde5->word0 = cpu_to_le32(pde5->word0);
-		pde5->reftag = cpu_to_le32(pde5->reftag);
+		pde5->reftag = cpu_to_le32(reftag);
 
 		/* advance bpl and increment bde count */
 		num_bde++;
@@ -1597,9 +1555,9 @@
 		bf_set(pde6_oprx, pde6, rxop);
 		bf_set(pde6_ce, pde6, 1);
 		bf_set(pde6_re, pde6, 1);
-		bf_set(pde6_ae, pde6, 1);
 		bf_set(pde6_ai, pde6, 1);
-		bf_set(pde6_apptagval, pde6, apptagval);
+		bf_set(pde6_ae, pde6, 0);
+		bf_set(pde6_apptagval, pde6, 0);
 
 		/* Endianness conversion if necessary for PDE6 */
 		pde6->word0 = cpu_to_le32(pde6->word0);
@@ -1621,8 +1579,8 @@
 		memset(pde7, 0, sizeof(struct lpfc_pde7));
 		bf_set(pde7_type, pde7, LPFC_PDE7_DESCRIPTOR);
 
-		pde7->addrHigh = le32_to_cpu(putPaddrLow(protphysaddr));
-		pde7->addrLow = le32_to_cpu(putPaddrHigh(protphysaddr));
+		pde7->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+		pde7->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
 
 		protgrp_blks = protgroup_len / 8;
 		protgrp_bytes = protgrp_blks * blksize;
@@ -1632,7 +1590,7 @@
 			protgroup_remainder = 0x1000 - (pde7->addrLow & 0xfff);
 			protgroup_offset += protgroup_remainder;
 			protgrp_blks = protgroup_remainder / 8;
-			protgrp_bytes = protgroup_remainder * blksize;
+			protgrp_bytes = protgrp_blks * blksize;
 		} else {
 			protgroup_offset = 0;
 			curr_prot++;
@@ -2006,16 +1964,21 @@
 	if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
 		/*
 		 * setup sense data descriptor 0 per SPC-4 as an information
-		 * field, and put the failing LBA in it
+		 * field, and put the failing LBA in it.
+		 * This code assumes there was also a guard/app/ref tag error
+		 * indication.
 		 */
-		cmd->sense_buffer[8] = 0;     /* Information */
-		cmd->sense_buffer[9] = 0xa;   /* Add. length */
+		cmd->sense_buffer[7] = 0xc;   /* Additional sense length */
+		cmd->sense_buffer[8] = 0;     /* Information descriptor type */
+		cmd->sense_buffer[9] = 0xa;   /* Additional descriptor length */
+		cmd->sense_buffer[10] = 0x80; /* Validity bit */
 		bghm /= cmd->device->sector_size;
 
 		failing_sector = scsi_get_lba(cmd);
 		failing_sector += bghm;
 
-		put_unaligned_be64(failing_sector, &cmd->sense_buffer[10]);
+		/* Descriptor Information */
+		put_unaligned_be64(failing_sector, &cmd->sense_buffer[12]);
 	}
 
 	if (!ret) {
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 98999bb..8b799f0 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -560,7 +560,7 @@
 	rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
 	if (rrq) {
 		rrq->send_rrq = send_rrq;
-		rrq->xritag = phba->sli4_hba.xri_ids[xritag];
+		rrq->xritag = xritag;
 		rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
 		rrq->ndlp = ndlp;
 		rrq->nlp_DID = ndlp->nlp_DID;
@@ -2452,7 +2452,8 @@
 
 		/* search continue save q for same XRI */
 		list_for_each_entry(iocbq, &pring->iocb_continue_saveq, clist) {
-			if (iocbq->iocb.ulpContext == saveq->iocb.ulpContext) {
+			if (iocbq->iocb.unsli3.rcvsli3.ox_id ==
+				saveq->iocb.unsli3.rcvsli3.ox_id) {
 				list_add_tail(&saveq->list, &iocbq->list);
 				found = 1;
 				break;
@@ -3355,6 +3356,7 @@
 							   irspiocbq);
 			break;
 		case CQE_CODE_RECEIVE:
+		case CQE_CODE_RECEIVE_V1:
 			dmabuf = container_of(cq_event, struct hbq_dmabuf,
 					      cq_event);
 			lpfc_sli4_handle_received_buffer(phba, dmabuf);
@@ -4712,10 +4714,15 @@
  * lpfc_sli4_get_avail_extnt_rsrc - Get available resource extent count.
  * @phba: Pointer to HBA context object.
  * @type: The resource extent type.
+ * @extnt_count: buffer to hold port available extent count.
+ * @extnt_size: buffer to hold element count per extent.
  *
- * This function allocates all SLI4 resource identifiers.
+ * This function calls the port and retrievs the number of available
+ * extents and their size for a particular extent type.
+ *
+ * Returns: 0 if successful.  Nonzero otherwise.
  **/
-static int
+int
 lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
 			       uint16_t *extnt_count, uint16_t *extnt_size)
 {
@@ -4892,7 +4899,7 @@
 				     req_len, *emb);
 	if (alloc_len < req_len) {
 		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-			"9000 Allocated DMA memory size (x%x) is "
+			"2982 Allocated DMA memory size (x%x) is "
 			"less than the requested DMA memory "
 			"size (x%x)\n", alloc_len, req_len);
 		return -ENOMEM;
@@ -5506,6 +5513,154 @@
 }
 
 /**
+ * lpfc_sli4_get_allocated_extnts - Get the port's allocated extents.
+ * @phba: Pointer to HBA context object.
+ * @type: The resource extent type.
+ * @extnt_count: buffer to hold port extent count response
+ * @extnt_size: buffer to hold port extent size response.
+ *
+ * This function calls the port to read the host allocated extents
+ * for a particular type.
+ **/
+int
+lpfc_sli4_get_allocated_extnts(struct lpfc_hba *phba, uint16_t type,
+			       uint16_t *extnt_cnt, uint16_t *extnt_size)
+{
+	bool emb;
+	int rc = 0;
+	uint16_t curr_blks = 0;
+	uint32_t req_len, emb_len;
+	uint32_t alloc_len, mbox_tmo;
+	struct list_head *blk_list_head;
+	struct lpfc_rsrc_blks *rsrc_blk;
+	LPFC_MBOXQ_t *mbox;
+	void *virtaddr = NULL;
+	struct lpfc_mbx_nembed_rsrc_extent *n_rsrc;
+	struct lpfc_mbx_alloc_rsrc_extents *rsrc_ext;
+	union  lpfc_sli4_cfg_shdr *shdr;
+
+	switch (type) {
+	case LPFC_RSC_TYPE_FCOE_VPI:
+		blk_list_head = &phba->lpfc_vpi_blk_list;
+		break;
+	case LPFC_RSC_TYPE_FCOE_XRI:
+		blk_list_head = &phba->sli4_hba.lpfc_xri_blk_list;
+		break;
+	case LPFC_RSC_TYPE_FCOE_VFI:
+		blk_list_head = &phba->sli4_hba.lpfc_vfi_blk_list;
+		break;
+	case LPFC_RSC_TYPE_FCOE_RPI:
+		blk_list_head = &phba->sli4_hba.lpfc_rpi_blk_list;
+		break;
+	default:
+		return -EIO;
+	}
+
+	/* Count the number of extents currently allocatd for this type. */
+	list_for_each_entry(rsrc_blk, blk_list_head, list) {
+		if (curr_blks == 0) {
+			/*
+			 * The GET_ALLOCATED mailbox does not return the size,
+			 * just the count.  The size should be just the size
+			 * stored in the current allocated block and all sizes
+			 * for an extent type are the same so set the return
+			 * value now.
+			 */
+			*extnt_size = rsrc_blk->rsrc_size;
+		}
+		curr_blks++;
+	}
+
+	/* Calculate the total requested length of the dma memory. */
+	req_len = curr_blks * sizeof(uint16_t);
+
+	/*
+	 * Calculate the size of an embedded mailbox.  The uint32_t
+	 * accounts for extents-specific word.
+	 */
+	emb_len = sizeof(MAILBOX_t) - sizeof(struct mbox_header) -
+		sizeof(uint32_t);
+
+	/*
+	 * Presume the allocation and response will fit into an embedded
+	 * mailbox.  If not true, reconfigure to a non-embedded mailbox.
+	 */
+	emb = LPFC_SLI4_MBX_EMBED;
+	req_len = emb_len;
+	if (req_len > emb_len) {
+		req_len = curr_blks * sizeof(uint16_t) +
+			sizeof(union lpfc_sli4_cfg_shdr) +
+			sizeof(uint32_t);
+		emb = LPFC_SLI4_MBX_NEMBED;
+	}
+
+	mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+	if (!mbox)
+		return -ENOMEM;
+	memset(mbox, 0, sizeof(LPFC_MBOXQ_t));
+
+	alloc_len = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+				     LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT,
+				     req_len, emb);
+	if (alloc_len < req_len) {
+		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+			"2983 Allocated DMA memory size (x%x) is "
+			"less than the requested DMA memory "
+			"size (x%x)\n", alloc_len, req_len);
+		rc = -ENOMEM;
+		goto err_exit;
+	}
+	rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, curr_blks, type, emb);
+	if (unlikely(rc)) {
+		rc = -EIO;
+		goto err_exit;
+	}
+
+	if (!phba->sli4_hba.intr_enable)
+		rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+	else {
+		mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+		rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+	}
+
+	if (unlikely(rc)) {
+		rc = -EIO;
+		goto err_exit;
+	}
+
+	/*
+	 * Figure out where the response is located.  Then get local pointers
+	 * to the response data.  The port does not guarantee to respond to
+	 * all extents counts request so update the local variable with the
+	 * allocated count from the port.
+	 */
+	if (emb == LPFC_SLI4_MBX_EMBED) {
+		rsrc_ext = &mbox->u.mqe.un.alloc_rsrc_extents;
+		shdr = &rsrc_ext->header.cfg_shdr;
+		*extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, &rsrc_ext->u.rsp);
+	} else {
+		virtaddr = mbox->sge_array->addr[0];
+		n_rsrc = (struct lpfc_mbx_nembed_rsrc_extent *) virtaddr;
+		shdr = &n_rsrc->cfg_shdr;
+		*extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, n_rsrc);
+	}
+
+	if (bf_get(lpfc_mbox_hdr_status, &shdr->response)) {
+		lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+			"2984 Failed to read allocated resources "
+			"for type %d - Status 0x%x Add'l Status 0x%x.\n",
+			type,
+			bf_get(lpfc_mbox_hdr_status, &shdr->response),
+			bf_get(lpfc_mbox_hdr_add_status, &shdr->response));
+		rc = -EIO;
+		goto err_exit;
+	}
+ err_exit:
+	lpfc_sli4_mbox_cmd_free(phba, mbox);
+	return rc;
+}
+
+/**
  * lpfc_sli4_hba_setup - SLI4 device intialization PCI function
  * @phba: Pointer to HBA context object.
  *
@@ -5837,6 +5992,7 @@
 					"Advanced Error Reporting (AER)\n");
 			phba->cfg_aer_support = 0;
 		}
+		rc = 0;
 	}
 
 	if (!(phba->hba_flag & HBA_FCOE_MODE)) {
@@ -6634,6 +6790,9 @@
 	unsigned long iflags;
 	int rc;
 
+	/* dump from issue mailbox command if setup */
+	lpfc_idiag_mbxacc_dump_issue_mbox(phba, &mboxq->u.mb);
+
 	rc = lpfc_mbox_dev_check(phba);
 	if (unlikely(rc)) {
 		lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
@@ -7318,12 +7477,12 @@
 		bf_set(wqe_qosd, &wqe->els_req.wqe_com, 1);
 		bf_set(wqe_lenloc, &wqe->els_req.wqe_com, LPFC_WQE_LENLOC_NONE);
 		bf_set(wqe_ebde_cnt, &wqe->els_req.wqe_com, 0);
-	break;
+		break;
 	case CMD_XMIT_SEQUENCE64_CX:
 		bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com,
 		       iocbq->iocb.un.ulpWord[3]);
 		bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com,
-		       iocbq->iocb.ulpContext);
+		       iocbq->iocb.unsli3.rcvsli3.ox_id);
 		/* The entire sequence is transmitted for this IOCB */
 		xmit_len = total_len;
 		cmnd = CMD_XMIT_SEQUENCE64_CR;
@@ -7341,7 +7500,7 @@
 		bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0);
 		wqe->xmit_sequence.xmit_len = xmit_len;
 		command_type = OTHER_COMMAND;
-	break;
+		break;
 	case CMD_XMIT_BCAST64_CN:
 		/* word3 iocb=iotag32 wqe=seq_payload_len */
 		wqe->xmit_bcast64.seq_payload_len = xmit_len;
@@ -7355,7 +7514,7 @@
 		bf_set(wqe_lenloc, &wqe->xmit_bcast64.wqe_com,
 		       LPFC_WQE_LENLOC_WORD3);
 		bf_set(wqe_ebde_cnt, &wqe->xmit_bcast64.wqe_com, 0);
-	break;
+		break;
 	case CMD_FCP_IWRITE64_CR:
 		command_type = FCP_COMMAND_DATA_OUT;
 		/* word3 iocb=iotag wqe=payload_offset_len */
@@ -7375,7 +7534,7 @@
 		       LPFC_WQE_LENLOC_WORD4);
 		bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
 		bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU);
-	break;
+		break;
 	case CMD_FCP_IREAD64_CR:
 		/* word3 iocb=iotag wqe=payload_offset_len */
 		/* Add the FCP_CMD and FCP_RSP sizes to get the offset */
@@ -7394,7 +7553,7 @@
 		       LPFC_WQE_LENLOC_WORD4);
 		bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
 		bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU);
-	break;
+		break;
 	case CMD_FCP_ICMND64_CR:
 		/* word3 iocb=IO_TAG wqe=reserved */
 		wqe->fcp_icmd.rsrvd3 = 0;
@@ -7407,7 +7566,7 @@
 		bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com,
 		       LPFC_WQE_LENLOC_NONE);
 		bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0);
-	break;
+		break;
 	case CMD_GEN_REQUEST64_CR:
 		/* For this command calculate the xmit length of the
 		 * request bde.
@@ -7442,7 +7601,7 @@
 		bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE);
 		bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0);
 		command_type = OTHER_COMMAND;
-	break;
+		break;
 	case CMD_XMIT_ELS_RSP64_CX:
 		ndlp = (struct lpfc_nodelist *)iocbq->context1;
 		/* words0-2 BDE memcpy */
@@ -7457,7 +7616,7 @@
 		       ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
 		bf_set(wqe_pu, &wqe->xmit_els_rsp.wqe_com, iocbq->iocb.ulpPU);
 		bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
-		       iocbq->iocb.ulpContext);
+		       iocbq->iocb.unsli3.rcvsli3.ox_id);
 		if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l)
 			bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
 			       phba->vpi_ids[iocbq->vport->vpi]);
@@ -7470,7 +7629,7 @@
 		bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp,
 		       phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
 		command_type = OTHER_COMMAND;
-	break;
+		break;
 	case CMD_CLOSE_XRI_CN:
 	case CMD_ABORT_XRI_CN:
 	case CMD_ABORT_XRI_CX:
@@ -7509,7 +7668,7 @@
 		cmnd = CMD_ABORT_XRI_CX;
 		command_type = OTHER_COMMAND;
 		xritag = 0;
-	break;
+		break;
 	case CMD_XMIT_BLS_RSP64_CX:
 		/* As BLS ABTS RSP WQE is very different from other WQEs,
 		 * we re-construct this WQE here based on information in
@@ -7553,7 +7712,7 @@
 			       bf_get(lpfc_rsn_code, &iocbq->iocb.un.bls_rsp));
 		}
 
-	break;
+		break;
 	case CMD_XRI_ABORTED_CX:
 	case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
 	case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */
@@ -7565,7 +7724,7 @@
 				"2014 Invalid command 0x%x\n",
 				iocbq->iocb.ulpCommand);
 		return IOCB_ERROR;
-	break;
+		break;
 	}
 
 	bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag);
@@ -10481,10 +10640,14 @@
 	struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
 	struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
 	struct hbq_dmabuf *dma_buf;
-	uint32_t status;
+	uint32_t status, rq_id;
 	unsigned long iflags;
 
-	if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
+	if (bf_get(lpfc_cqe_code, rcqe) == CQE_CODE_RECEIVE_V1)
+		rq_id = bf_get(lpfc_rcqe_rq_id_v1, rcqe);
+	else
+		rq_id = bf_get(lpfc_rcqe_rq_id, rcqe);
+	if (rq_id != hrq->queue_id)
 		goto out;
 
 	status = bf_get(lpfc_rcqe_status, rcqe);
@@ -10563,6 +10726,7 @@
 				(struct sli4_wcqe_xri_aborted *)&cqevt);
 		break;
 	case CQE_CODE_RECEIVE:
+	case CQE_CODE_RECEIVE_V1:
 		/* Process the RQ event */
 		phba->last_completion_time = jiffies;
 		workposted = lpfc_sli4_sp_handle_rcqe(phba,
@@ -12345,19 +12509,18 @@
 }
 
 /**
- * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port
+ * lpfc_sli4_alloc_xri - Get an available rpi in the device's range
  * @phba: pointer to lpfc hba data structure.
  *
  * This routine is invoked to post rpi header templates to the
- * port for those SLI4 ports that do not support extents.  This routine
- * posts a PAGE_SIZE memory region to the port to hold up to
- * PAGE_SIZE modulo 64 rpi context headers.  This is an initialization routine
- * and should be called only when interrupts are disabled.
+ * HBA consistent with the SLI-4 interface spec.  This routine
+ * posts a SLI4_PAGE_SIZE memory region to the port to hold up to
+ * SLI4_PAGE_SIZE modulo 64 rpi context headers.
  *
- * Return codes
- *	0 - successful
- *	-ERROR - otherwise.
- */
+ * Returns
+ *	A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful
+ *	LPFC_RPI_ALLOC_ERROR if no rpis are available.
+ **/
 uint16_t
 lpfc_sli4_alloc_xri(struct lpfc_hba *phba)
 {
@@ -13406,7 +13569,7 @@
  * This function validates the xri maps to the known range of XRIs allocated an
  * used by the driver.
  **/
-static uint16_t
+uint16_t
 lpfc_sli4_xri_inrange(struct lpfc_hba *phba,
 		      uint16_t xri)
 {
@@ -13643,10 +13806,12 @@
 static struct lpfc_iocbq *
 lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
 {
+	struct hbq_dmabuf *hbq_buf;
 	struct lpfc_dmabuf *d_buf, *n_buf;
 	struct lpfc_iocbq *first_iocbq, *iocbq;
 	struct fc_frame_header *fc_hdr;
 	uint32_t sid;
+	uint32_t len, tot_len;
 	struct ulp_bde64 *pbde;
 
 	fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
@@ -13655,6 +13820,7 @@
 	lpfc_update_rcv_time_stamp(vport);
 	/* get the Remote Port's SID */
 	sid = sli4_sid_from_fc_hdr(fc_hdr);
+	tot_len = 0;
 	/* Get an iocbq struct to fill in. */
 	first_iocbq = lpfc_sli_get_iocbq(vport->phba);
 	if (first_iocbq) {
@@ -13662,9 +13828,12 @@
 		first_iocbq->iocb.unsli3.rcvsli3.acc_len = 0;
 		first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS;
 		first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX;
-		first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id);
-		/* iocbq is prepped for internal consumption.  Logical vpi. */
-		first_iocbq->iocb.unsli3.rcvsli3.vpi = vport->vpi;
+		first_iocbq->iocb.ulpContext = NO_XRI;
+		first_iocbq->iocb.unsli3.rcvsli3.ox_id =
+			be16_to_cpu(fc_hdr->fh_ox_id);
+		/* iocbq is prepped for internal consumption.  Physical vpi. */
+		first_iocbq->iocb.unsli3.rcvsli3.vpi =
+			vport->phba->vpi_ids[vport->vpi];
 		/* put the first buffer into the first IOCBq */
 		first_iocbq->context2 = &seq_dmabuf->dbuf;
 		first_iocbq->context3 = NULL;
@@ -13672,9 +13841,9 @@
 		first_iocbq->iocb.un.cont64[0].tus.f.bdeSize =
 							LPFC_DATA_BUF_SIZE;
 		first_iocbq->iocb.un.rcvels.remoteID = sid;
-		first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-				bf_get(lpfc_rcqe_length,
+		tot_len = bf_get(lpfc_rcqe_length,
 				       &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+		first_iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len;
 	}
 	iocbq = first_iocbq;
 	/*
@@ -13692,9 +13861,13 @@
 			pbde = (struct ulp_bde64 *)
 					&iocbq->iocb.unsli3.sli3Words[4];
 			pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE;
-			first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-				bf_get(lpfc_rcqe_length,
-				       &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+
+			/* We need to get the size out of the right CQE */
+			hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+			len = bf_get(lpfc_rcqe_length,
+				       &hbq_buf->cq_event.cqe.rcqe_cmpl);
+			iocbq->iocb.unsli3.rcvsli3.acc_len += len;
+			tot_len += len;
 		} else {
 			iocbq = lpfc_sli_get_iocbq(vport->phba);
 			if (!iocbq) {
@@ -13712,9 +13885,14 @@
 			iocbq->iocb.ulpBdeCount = 1;
 			iocbq->iocb.un.cont64[0].tus.f.bdeSize =
 							LPFC_DATA_BUF_SIZE;
-			first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-				bf_get(lpfc_rcqe_length,
-				       &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+
+			/* We need to get the size out of the right CQE */
+			hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+			len = bf_get(lpfc_rcqe_length,
+				       &hbq_buf->cq_event.cqe.rcqe_cmpl);
+			tot_len += len;
+			iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len;
+
 			iocbq->iocb.un.rcvels.remoteID = sid;
 			list_add_tail(&iocbq->list, &first_iocbq->list);
 		}
@@ -13787,7 +13965,13 @@
 		lpfc_in_buf_free(phba, &dmabuf->dbuf);
 		return;
 	}
-	fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
+	if ((bf_get(lpfc_cqe_code,
+		    &dmabuf->cq_event.cqe.rcqe_cmpl) == CQE_CODE_RECEIVE_V1))
+		fcfi = bf_get(lpfc_rcqe_fcf_id_v1,
+			      &dmabuf->cq_event.cqe.rcqe_cmpl);
+	else
+		fcfi = bf_get(lpfc_rcqe_fcf_id,
+			      &dmabuf->cq_event.cqe.rcqe_cmpl);
 	vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
 	if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
 		/* throw out the frame */
@@ -14451,6 +14635,92 @@
 }
 
 /**
+ * lpfc_check_next_fcf_pri
+ * phba pointer to the lpfc_hba struct for this port.
+ * This routine is called from the lpfc_sli4_fcf_rr_next_index_get
+ * routine when the rr_bmask is empty. The FCF indecies are put into the
+ * rr_bmask based on their priority level. Starting from the highest priority
+ * to the lowest. The most likely FCF candidate will be in the highest
+ * priority group. When this routine is called it searches the fcf_pri list for
+ * next lowest priority group and repopulates the rr_bmask with only those
+ * fcf_indexes.
+ * returns:
+ * 1=success 0=failure
+ **/
+int
+lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba)
+{
+	uint16_t next_fcf_pri;
+	uint16_t last_index;
+	struct lpfc_fcf_pri *fcf_pri;
+	int rc;
+	int ret = 0;
+
+	last_index = find_first_bit(phba->fcf.fcf_rr_bmask,
+			LPFC_SLI4_FCF_TBL_INDX_MAX);
+	lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+			"3060 Last IDX %d\n", last_index);
+	if (list_empty(&phba->fcf.fcf_pri_list)) {
+		lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+			"3061 Last IDX %d\n", last_index);
+		return 0; /* Empty rr list */
+	}
+	next_fcf_pri = 0;
+	/*
+	 * Clear the rr_bmask and set all of the bits that are at this
+	 * priority.
+	 */
+	memset(phba->fcf.fcf_rr_bmask, 0,
+			sizeof(*phba->fcf.fcf_rr_bmask));
+	spin_lock_irq(&phba->hbalock);
+	list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
+		if (fcf_pri->fcf_rec.flag & LPFC_FCF_FLOGI_FAILED)
+			continue;
+		/*
+		 * the 1st priority that has not FLOGI failed
+		 * will be the highest.
+		 */
+		if (!next_fcf_pri)
+			next_fcf_pri = fcf_pri->fcf_rec.priority;
+		spin_unlock_irq(&phba->hbalock);
+		if (fcf_pri->fcf_rec.priority == next_fcf_pri) {
+			rc = lpfc_sli4_fcf_rr_index_set(phba,
+						fcf_pri->fcf_rec.fcf_index);
+			if (rc)
+				return 0;
+		}
+		spin_lock_irq(&phba->hbalock);
+	}
+	/*
+	 * if next_fcf_pri was not set above and the list is not empty then
+	 * we have failed flogis on all of them. So reset flogi failed
+	 * and start at the begining.
+	 */
+	if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) {
+		list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
+			fcf_pri->fcf_rec.flag &= ~LPFC_FCF_FLOGI_FAILED;
+			/*
+			 * the 1st priority that has not FLOGI failed
+			 * will be the highest.
+			 */
+			if (!next_fcf_pri)
+				next_fcf_pri = fcf_pri->fcf_rec.priority;
+			spin_unlock_irq(&phba->hbalock);
+			if (fcf_pri->fcf_rec.priority == next_fcf_pri) {
+				rc = lpfc_sli4_fcf_rr_index_set(phba,
+						fcf_pri->fcf_rec.fcf_index);
+				if (rc)
+					return 0;
+			}
+			spin_lock_irq(&phba->hbalock);
+		}
+	} else
+		ret = 1;
+	spin_unlock_irq(&phba->hbalock);
+
+	return ret;
+}
+/**
  * lpfc_sli4_fcf_rr_next_index_get - Get next eligible fcf record index
  * @phba: pointer to lpfc hba data structure.
  *
@@ -14466,6 +14736,7 @@
 	uint16_t next_fcf_index;
 
 	/* Search start from next bit of currently registered FCF index */
+next_priority:
 	next_fcf_index = (phba->fcf.current_rec.fcf_indx + 1) %
 					LPFC_SLI4_FCF_TBL_INDX_MAX;
 	next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
@@ -14473,17 +14744,46 @@
 				       next_fcf_index);
 
 	/* Wrap around condition on phba->fcf.fcf_rr_bmask */
-	if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX)
+	if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+		/*
+		 * If we have wrapped then we need to clear the bits that
+		 * have been tested so that we can detect when we should
+		 * change the priority level.
+		 */
 		next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
 					       LPFC_SLI4_FCF_TBL_INDX_MAX, 0);
+	}
+
 
 	/* Check roundrobin failover list empty condition */
-	if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+	if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX ||
+		next_fcf_index == phba->fcf.current_rec.fcf_indx) {
+		/*
+		 * If next fcf index is not found check if there are lower
+		 * Priority level fcf's in the fcf_priority list.
+		 * Set up the rr_bmask with all of the avaiable fcf bits
+		 * at that level and continue the selection process.
+		 */
+		if (lpfc_check_next_fcf_pri_level(phba))
+			goto next_priority;
 		lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
 				"2844 No roundrobin failover FCF available\n");
-		return LPFC_FCOE_FCF_NEXT_NONE;
+		if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX)
+			return LPFC_FCOE_FCF_NEXT_NONE;
+		else {
+			lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
+				"3063 Only FCF available idx %d, flag %x\n",
+				next_fcf_index,
+			phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag);
+			return next_fcf_index;
+		}
 	}
 
+	if (next_fcf_index < LPFC_SLI4_FCF_TBL_INDX_MAX &&
+		phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag &
+		LPFC_FCF_FLOGI_FAILED)
+		goto next_priority;
+
 	lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
 			"2845 Get next roundrobin failover FCF (x%x)\n",
 			next_fcf_index);
@@ -14535,6 +14835,7 @@
 void
 lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index)
 {
+	struct lpfc_fcf_pri *fcf_pri;
 	if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
 		lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
 				"2762 FCF (x%x) reached driver's book "
@@ -14543,6 +14844,14 @@
 		return;
 	}
 	/* Clear the eligible FCF record index bmask */
+	spin_lock_irq(&phba->hbalock);
+	list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
+		if (fcf_pri->fcf_rec.fcf_index == fcf_index) {
+			list_del_init(&fcf_pri->list);
+			break;
+		}
+	}
+	spin_unlock_irq(&phba->hbalock);
 	clear_bit(fcf_index, phba->fcf.fcf_rr_bmask);
 
 	lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 4b17035..19bb87a 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -81,6 +81,8 @@
 	 (fc_hdr)->fh_f_ctl[1] <<  8 | \
 	 (fc_hdr)->fh_f_ctl[2])
 
+#define LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT 12000
+
 enum lpfc_sli4_queue_type {
 	LPFC_EQ,
 	LPFC_GCQ,
@@ -157,6 +159,25 @@
 #define RECORD_VALID	0x02
 };
 
+struct lpfc_fcf_pri_rec {
+	uint16_t fcf_index;
+#define LPFC_FCF_ON_PRI_LIST 0x0001
+#define LPFC_FCF_FLOGI_FAILED 0x0002
+	uint16_t flag;
+	uint32_t priority;
+};
+
+struct lpfc_fcf_pri {
+	struct list_head list;
+	struct lpfc_fcf_pri_rec fcf_rec;
+};
+
+/*
+ * Maximum FCF table index, it is for driver internal book keeping, it
+ * just needs to be no less than the supported HBA's FCF table size.
+ */
+#define LPFC_SLI4_FCF_TBL_INDX_MAX	32
+
 struct lpfc_fcf {
 	uint16_t fcfi;
 	uint32_t fcf_flag;
@@ -176,15 +197,13 @@
 	uint32_t eligible_fcf_cnt;
 	struct lpfc_fcf_rec current_rec;
 	struct lpfc_fcf_rec failover_rec;
+	struct list_head fcf_pri_list;
+	struct lpfc_fcf_pri fcf_pri[LPFC_SLI4_FCF_TBL_INDX_MAX];
+	uint32_t current_fcf_scan_pri;
 	struct timer_list redisc_wait;
 	unsigned long *fcf_rr_bmask; /* Eligible FCF indexes for RR failover */
 };
 
-/*
- * Maximum FCF table index, it is for driver internal book keeping, it
- * just needs to be no less than the supported HBA's FCF table size.
- */
-#define LPFC_SLI4_FCF_TBL_INDX_MAX	32
 
 #define LPFC_REGION23_SIGNATURE "RG23"
 #define LPFC_REGION23_VERSION	1
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index c03921b..c1e0ae9 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.3.23"
+#define LPFC_DRIVER_VERSION "8.3.25"
 #define LPFC_DRIVER_NAME		"lpfc"
 #define LPFC_SP_DRIVER_HANDLER_NAME	"lpfc:sp"
 #define LPFC_FP_DRIVER_HANDLER_NAME	"lpfc:fp"
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 7370c08..3948a00 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -33,9 +33,9 @@
 /*
  * MegaRAID SAS Driver meta data
  */
-#define MEGASAS_VERSION				"00.00.05.38-rc1"
-#define MEGASAS_RELDATE				"May. 11, 2011"
-#define MEGASAS_EXT_VERSION			"Wed. May. 11 17:00:00 PDT 2011"
+#define MEGASAS_VERSION				"00.00.05.40-rc1"
+#define MEGASAS_RELDATE				"Jul. 26, 2011"
+#define MEGASAS_EXT_VERSION			"Tue. Jul. 26 17:00:00 PDT 2011"
 
 /*
  * Device IDs
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 2d8cdce..776d019 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -18,7 +18,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  *  FILE: megaraid_sas_base.c
- *  Version : v00.00.05.38-rc1
+ *  Version : v00.00.05.40-rc1
  *
  *  Authors: LSI Corporation
  *           Sreenivas Bagalkote
@@ -54,6 +54,7 @@
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
 #include "megaraid_sas_fusion.h"
 #include "megaraid_sas.h"
 
@@ -2057,6 +2058,20 @@
 	}
 }
 
+static int megasas_change_queue_depth(struct scsi_device *sdev,
+				      int queue_depth, int reason)
+{
+	if (reason != SCSI_QDEPTH_DEFAULT)
+		return -EOPNOTSUPP;
+
+	if (queue_depth > sdev->host->can_queue)
+		queue_depth = sdev->host->can_queue;
+	scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
+				queue_depth);
+
+	return queue_depth;
+}
+
 /*
  * Scsi host template for megaraid_sas driver
  */
@@ -2074,6 +2089,7 @@
 	.eh_timed_out = megasas_reset_timer,
 	.bios_param = megasas_bios_param,
 	.use_clustering = ENABLE_CLUSTERING,
+	.change_queue_depth = megasas_change_queue_depth,
 };
 
 /**
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index 8fe3a45..5a5af1f 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -288,7 +288,6 @@
 				/* Get dev handle from Pd */
 				*pDevHandle = MR_PdDevHandleGet(pd, map);
 		}
-		retval = FALSE;
 	}
 
 	*pdBlock += stripRef + MR_LdSpanPtrGet(ld, span, map)->startBlk;
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 939f283..6abd2fc 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -4258,6 +4258,7 @@
 	u32 log_info;
 	struct MPT2SAS_DEVICE *sas_device_priv_data;
 	u32 response_code = 0;
+	unsigned long flags;
 
 	mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
 	scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
@@ -4282,6 +4283,9 @@
 	 * the failed direct I/O should be redirected to volume
 	 */
 	if (_scsih_scsi_direct_io_get(ioc, smid)) {
+		spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+		ioc->scsi_lookup[smid - 1].scmd = scmd;
+		spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
 		_scsih_scsi_direct_io_set(ioc, smid, 0);
 		memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
 		mpi_request->DevHandle =
diff --git a/drivers/scsi/mvsas/Kconfig b/drivers/scsi/mvsas/Kconfig
index c82b012..78f7e20 100644
--- a/drivers/scsi/mvsas/Kconfig
+++ b/drivers/scsi/mvsas/Kconfig
@@ -3,7 +3,7 @@
 #
 # Copyright 2007 Red Hat, Inc.
 # Copyright 2008 Marvell. <kewei@marvell.com>
-# Copyright 2009-20011 Marvell. <yuxiangl@marvell.com>
+# Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
 #
 # This file is licensed under GPLv2.
 #
@@ -41,3 +41,10 @@
 	help
 		Compiles the 88SE64XX/88SE94XX driver in debug mode.  In debug mode,
 		the driver prints some messages to the console.
+config SCSI_MVSAS_TASKLET
+	bool "Support for interrupt tasklet"
+	default n
+	depends on SCSI_MVSAS
+	help
+		Compiles the 88SE64xx/88SE94xx driver in interrupt tasklet mode.In this mode,
+		the interrupt will schedule a tasklet.
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c
index 13c9604..8ba4722 100644
--- a/drivers/scsi/mvsas/mv_64xx.c
+++ b/drivers/scsi/mvsas/mv_64xx.c
@@ -33,7 +33,6 @@
 	u32 reg;
 	struct mvs_phy *phy = &mvi->phy[i];
 
-	/* TODO check & save device type */
 	reg = mr32(MVS_GBL_PORT_TYPE);
 	phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
 	if (reg & MODE_SAS_SATA & (1 << i))
@@ -48,7 +47,7 @@
 	u32 tmp;
 
 	tmp = mr32(MVS_PCS);
-	if (mvi->chip->n_phy <= 4)
+	if (mvi->chip->n_phy <= MVS_SOC_PORTS)
 		tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
 	else
 		tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
@@ -58,24 +57,16 @@
 static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi)
 {
 	void __iomem *regs = mvi->regs;
+	int i;
 
 	mvs_phy_hacks(mvi);
 
 	if (!(mvi->flags & MVF_FLAG_SOC)) {
-		/* TEST - for phy decoding error, adjust voltage levels */
-		mw32(MVS_P0_VSR_ADDR + 0, 0x8);
-		mw32(MVS_P0_VSR_DATA + 0, 0x2F0);
-
-		mw32(MVS_P0_VSR_ADDR + 8, 0x8);
-		mw32(MVS_P0_VSR_DATA + 8, 0x2F0);
-
-		mw32(MVS_P0_VSR_ADDR + 16, 0x8);
-		mw32(MVS_P0_VSR_DATA + 16, 0x2F0);
-
-		mw32(MVS_P0_VSR_ADDR + 24, 0x8);
-		mw32(MVS_P0_VSR_DATA + 24, 0x2F0);
+		for (i = 0; i < MVS_SOC_PORTS; i++) {
+			mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE8);
+			mvs_write_port_vsr_data(mvi, i, 0x2F0);
+		}
 	} else {
-		int i;
 		/* disable auto port detection */
 		mw32(MVS_GBL_PORT_TYPE, 0);
 		for (i = 0; i < mvi->chip->n_phy; i++) {
@@ -95,7 +86,7 @@
 	u32 reg, tmp;
 
 	if (!(mvi->flags & MVF_FLAG_SOC)) {
-		if (phy_id < 4)
+		if (phy_id < MVS_SOC_PORTS)
 			pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &reg);
 		else
 			pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &reg);
@@ -104,13 +95,13 @@
 		reg = mr32(MVS_PHY_CTL);
 
 	tmp = reg;
-	if (phy_id < 4)
+	if (phy_id < MVS_SOC_PORTS)
 		tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
 	else
-		tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS;
+		tmp |= (1U << (phy_id - MVS_SOC_PORTS)) << PCTL_LINK_OFFS;
 
 	if (!(mvi->flags & MVF_FLAG_SOC)) {
-		if (phy_id < 4) {
+		if (phy_id < MVS_SOC_PORTS) {
 			pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
 			mdelay(10);
 			pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
@@ -133,9 +124,9 @@
 	tmp &= ~PHYEV_RDY_CH;
 	mvs_write_port_irq_stat(mvi, phy_id, tmp);
 	tmp = mvs_read_phy_ctl(mvi, phy_id);
-	if (hard == 1)
+	if (hard == MVS_HARD_RESET)
 		tmp |= PHY_RST_HARD;
-	else if (hard == 0)
+	else if (hard == MVS_SOFT_RESET)
 		tmp |= PHY_RST;
 	mvs_write_phy_ctl(mvi, phy_id, tmp);
 	if (hard) {
@@ -321,6 +312,11 @@
 	/* init phys */
 	mvs_64xx_phy_hacks(mvi);
 
+	tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
+	tmp &= 0x0000ffff;
+	tmp |= 0x00fa0000;
+	mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
+
 	/* enable auto port detection */
 	mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN);
 
@@ -346,7 +342,7 @@
 
 		mvs_64xx_enable_xmt(mvi, i);
 
-		mvs_64xx_phy_reset(mvi, i, 1);
+		mvs_64xx_phy_reset(mvi, i, MVS_HARD_RESET);
 		msleep(500);
 		mvs_64xx_detect_porttype(mvi, i);
 	}
@@ -377,13 +373,7 @@
 		mvs_update_phyinfo(mvi, i, 1);
 	}
 
-	/* FIXME: update wide port bitmaps */
-
 	/* little endian for open address and command table, etc. */
-	/*
-	 * it seems that ( from the spec ) turning on big-endian won't
-	 * do us any good on big-endian machines, need further confirmation
-	 */
 	cctl = mr32(MVS_CTL);
 	cctl |= CCTL_ENDIAN_CMD;
 	cctl |= CCTL_ENDIAN_DATA;
@@ -394,15 +384,19 @@
 	/* reset CMD queue */
 	tmp = mr32(MVS_PCS);
 	tmp |= PCS_CMD_RST;
+	tmp &= ~PCS_SELF_CLEAR;
 	mw32(MVS_PCS, tmp);
-	/* interrupt coalescing may cause missing HW interrput in some case,
-	 * and the max count is 0x1ff, while our max slot is 0x200,
+	/*
+	 * the max count is 0x1ff, while our max slot is 0x200,
 	 * it will make count 0.
 	 */
 	tmp = 0;
-	mw32(MVS_INT_COAL, tmp);
+	if (MVS_CHIP_SLOT_SZ > 0x1ff)
+		mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
+	else
+		mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
 
-	tmp = 0x100;
+	tmp = 0x10000 | interrupt_coalescing;
 	mw32(MVS_INT_COAL_TMOUT, tmp);
 
 	/* ladies and gentlemen, start your engines */
@@ -477,13 +471,11 @@
 
 	/* clear CMD_CMPLT ASAP */
 	mw32_f(MVS_INT_STAT, CINT_DONE);
-#ifndef MVS_USE_TASKLET
+
 	spin_lock(&mvi->lock);
-#endif
 	mvs_int_full(mvi);
-#ifndef MVS_USE_TASKLET
 	spin_unlock(&mvi->lock);
-#endif
+
 	return IRQ_HANDLED;
 }
 
@@ -630,7 +622,6 @@
 {
 	u32 tmp;
 	struct mvs_phy *phy = &mvi->phy[i];
-	/* workaround for HW phy decoding error on 1.5g disk drive */
 	mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
 	tmp = mvs_read_port_vsr_data(mvi, i);
 	if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
@@ -661,7 +652,7 @@
 		tmp |= lrmax;
 	}
 	mvs_write_phy_ctl(mvi, phy_id, tmp);
-	mvs_64xx_phy_reset(mvi, phy_id, 1);
+	mvs_64xx_phy_reset(mvi, phy_id, MVS_HARD_RESET);
 }
 
 static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
@@ -744,11 +735,13 @@
 	return -1;
 }
 
-#ifndef DISABLE_HOTPLUG_DMA_FIX
-void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
+				int buf_len, int from, void *prd)
 {
 	int i;
 	struct mvs_prd *buf_prd = prd;
+	dma_addr_t buf_dma = mvi->bulk_buffer_dma;
+
 	buf_prd	+= from;
 	for (i = 0; i < MAX_SG_ENTRY - from; i++) {
 		buf_prd->addr = cpu_to_le64(buf_dma);
@@ -756,7 +749,28 @@
 		++buf_prd;
 	}
 }
-#endif
+
+static void mvs_64xx_tune_interrupt(struct mvs_info *mvi, u32 time)
+{
+	void __iomem *regs = mvi->regs;
+	u32 tmp = 0;
+	/*
+	 * the max count is 0x1ff, while our max slot is 0x200,
+	 * it will make count 0.
+	 */
+	if (time == 0) {
+		mw32(MVS_INT_COAL, 0);
+		mw32(MVS_INT_COAL_TMOUT, 0x10000);
+	} else {
+		if (MVS_CHIP_SLOT_SZ > 0x1ff)
+			mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
+		else
+			mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
+
+		tmp = 0x10000 | time;
+		mw32(MVS_INT_COAL_TMOUT, tmp);
+	}
+}
 
 const struct mvs_dispatch mvs_64xx_dispatch = {
 	"mv64xx",
@@ -780,7 +794,6 @@
 	mvs_write_port_irq_stat,
 	mvs_read_port_irq_mask,
 	mvs_write_port_irq_mask,
-	mvs_get_sas_addr,
 	mvs_64xx_command_active,
 	mvs_64xx_clear_srs_irq,
 	mvs_64xx_issue_stop,
@@ -808,8 +821,8 @@
 	mvs_64xx_spi_buildcmd,
 	mvs_64xx_spi_issuecmd,
 	mvs_64xx_spi_waitdataready,
-#ifndef DISABLE_HOTPLUG_DMA_FIX
 	mvs_64xx_fix_dma,
-#endif
+	mvs_64xx_tune_interrupt,
+	NULL,
 };
 
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 78162c3..3501291 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -48,6 +48,216 @@
 	}
 }
 
+void set_phy_tuning(struct mvs_info *mvi, int phy_id,
+			struct phy_tuning phy_tuning)
+{
+	u32 tmp, setting_0 = 0, setting_1 = 0;
+	u8 i;
+
+	/* Remap information for B0 chip:
+	*
+	* R0Ch -> R118h[15:0] (Adapted DFE F3 - F5 coefficient)
+	* R0Dh -> R118h[31:16] (Generation 1 Setting 0)
+	* R0Eh -> R11Ch[15:0]  (Generation 1 Setting 1)
+	* R0Fh -> R11Ch[31:16] (Generation 2 Setting 0)
+	* R10h -> R120h[15:0]  (Generation 2 Setting 1)
+	* R11h -> R120h[31:16] (Generation 3 Setting 0)
+	* R12h -> R124h[15:0]  (Generation 3 Setting 1)
+	* R13h -> R124h[31:16] (Generation 4 Setting 0 (Reserved))
+	*/
+
+	/* A0 has a different set of registers */
+	if (mvi->pdev->revision == VANIR_A0_REV)
+		return;
+
+	for (i = 0; i < 3; i++) {
+		/* loop 3 times, set Gen 1, Gen 2, Gen 3 */
+		switch (i) {
+		case 0:
+			setting_0 = GENERATION_1_SETTING;
+			setting_1 = GENERATION_1_2_SETTING;
+			break;
+		case 1:
+			setting_0 = GENERATION_1_2_SETTING;
+			setting_1 = GENERATION_2_3_SETTING;
+			break;
+		case 2:
+			setting_0 = GENERATION_2_3_SETTING;
+			setting_1 = GENERATION_3_4_SETTING;
+			break;
+		}
+
+		/* Set:
+		*
+		* Transmitter Emphasis Enable
+		* Transmitter Emphasis Amplitude
+		* Transmitter Amplitude
+		*/
+		mvs_write_port_vsr_addr(mvi, phy_id, setting_0);
+		tmp = mvs_read_port_vsr_data(mvi, phy_id);
+		tmp &= ~(0xFBE << 16);
+		tmp |= (((phy_tuning.trans_emp_en << 11) |
+			(phy_tuning.trans_emp_amp << 7) |
+			(phy_tuning.trans_amp << 1)) << 16);
+		mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+		/* Set Transmitter Amplitude Adjust */
+		mvs_write_port_vsr_addr(mvi, phy_id, setting_1);
+		tmp = mvs_read_port_vsr_data(mvi, phy_id);
+		tmp &= ~(0xC000);
+		tmp |= (phy_tuning.trans_amp_adj << 14);
+		mvs_write_port_vsr_data(mvi, phy_id, tmp);
+	}
+}
+
+void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id,
+				struct ffe_control ffe)
+{
+	u32 tmp;
+
+	/* Don't run this if A0/B0 */
+	if ((mvi->pdev->revision == VANIR_A0_REV)
+		|| (mvi->pdev->revision == VANIR_B0_REV))
+		return;
+
+	/* FFE Resistor and Capacitor */
+	/* R10Ch DFE Resolution Control/Squelch and FFE Setting
+	 *
+	 * FFE_FORCE            [7]
+	 * FFE_RES_SEL          [6:4]
+	 * FFE_CAP_SEL          [3:0]
+	 */
+	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_FFE_CONTROL);
+	tmp = mvs_read_port_vsr_data(mvi, phy_id);
+	tmp &= ~0xFF;
+
+	/* Read from HBA_Info_Page */
+	tmp |= ((0x1 << 7) |
+		(ffe.ffe_rss_sel << 4) |
+		(ffe.ffe_cap_sel << 0));
+
+	mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+	/* R064h PHY Mode Register 1
+	 *
+	 * DFE_DIS		18
+	 */
+	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+	tmp = mvs_read_port_vsr_data(mvi, phy_id);
+	tmp &= ~0x40001;
+	/* Hard coding */
+	/* No defines in HBA_Info_Page */
+	tmp |= (0 << 18);
+	mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+	/* R110h DFE F0-F1 Coefficient Control/DFE Update Control
+	 *
+	 * DFE_UPDATE_EN        [11:6]
+	 * DFE_FX_FORCE         [5:0]
+	 */
+	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_DFE_UPDATE_CRTL);
+	tmp = mvs_read_port_vsr_data(mvi, phy_id);
+	tmp &= ~0xFFF;
+	/* Hard coding */
+	/* No defines in HBA_Info_Page */
+	tmp |= ((0x3F << 6) | (0x0 << 0));
+	mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+	/* R1A0h Interface and Digital Reference Clock Control/Reserved_50h
+	 *
+	 * FFE_TRAIN_EN         3
+	 */
+	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+	tmp = mvs_read_port_vsr_data(mvi, phy_id);
+	tmp &= ~0x8;
+	/* Hard coding */
+	/* No defines in HBA_Info_Page */
+	tmp |= (0 << 3);
+	mvs_write_port_vsr_data(mvi, phy_id, tmp);
+}
+
+/*Notice: this function must be called when phy is disabled*/
+void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate)
+{
+	union reg_phy_cfg phy_cfg, phy_cfg_tmp;
+	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+	phy_cfg_tmp.v = mvs_read_port_vsr_data(mvi, phy_id);
+	phy_cfg.v = 0;
+	phy_cfg.u.disable_phy = phy_cfg_tmp.u.disable_phy;
+	phy_cfg.u.sas_support = 1;
+	phy_cfg.u.sata_support = 1;
+	phy_cfg.u.sata_host_mode = 1;
+
+	switch (rate) {
+	case 0x0:
+		/* support 1.5 Gbps */
+		phy_cfg.u.speed_support = 1;
+		phy_cfg.u.snw_3_support = 0;
+		phy_cfg.u.tx_lnk_parity = 1;
+		phy_cfg.u.tx_spt_phs_lnk_rate = 0x30;
+		break;
+	case 0x1:
+
+		/* support 1.5, 3.0 Gbps */
+		phy_cfg.u.speed_support = 3;
+		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3c;
+		phy_cfg.u.tx_lgcl_lnk_rate = 0x08;
+		break;
+	case 0x2:
+	default:
+		/* support 1.5, 3.0, 6.0 Gbps */
+		phy_cfg.u.speed_support = 7;
+		phy_cfg.u.snw_3_support = 1;
+		phy_cfg.u.tx_lnk_parity = 1;
+		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3f;
+		phy_cfg.u.tx_lgcl_lnk_rate = 0x09;
+		break;
+	}
+	mvs_write_port_vsr_data(mvi, phy_id, phy_cfg.v);
+}
+
+static void __devinit
+mvs_94xx_config_reg_from_hba(struct mvs_info *mvi, int phy_id)
+{
+	u32 temp;
+	temp = (u32)(*(u32 *)&mvi->hba_info_param.phy_tuning[phy_id]);
+	if (temp == 0xFFFFFFFFL) {
+		mvi->hba_info_param.phy_tuning[phy_id].trans_emp_amp = 0x6;
+		mvi->hba_info_param.phy_tuning[phy_id].trans_amp = 0x1A;
+		mvi->hba_info_param.phy_tuning[phy_id].trans_amp_adj = 0x3;
+	}
+
+	temp = (u8)(*(u8 *)&mvi->hba_info_param.ffe_ctl[phy_id]);
+	if (temp == 0xFFL) {
+		switch (mvi->pdev->revision) {
+		case VANIR_A0_REV:
+		case VANIR_B0_REV:
+			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0x7;
+			break;
+		case VANIR_C0_REV:
+		case VANIR_C1_REV:
+		case VANIR_C2_REV:
+		default:
+			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0xC;
+			break;
+		}
+	}
+
+	temp = (u8)(*(u8 *)&mvi->hba_info_param.phy_rate[phy_id]);
+	if (temp == 0xFFL)
+		/*set default phy_rate = 6Gbps*/
+		mvi->hba_info_param.phy_rate[phy_id] = 0x2;
+
+	set_phy_tuning(mvi, phy_id,
+		mvi->hba_info_param.phy_tuning[phy_id]);
+	set_phy_ffe_tuning(mvi, phy_id,
+		mvi->hba_info_param.ffe_ctl[phy_id]);
+	set_phy_rate(mvi, phy_id,
+		mvi->hba_info_param.phy_rate[phy_id]);
+}
+
 static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
 {
 	void __iomem *regs = mvi->regs;
@@ -61,7 +271,14 @@
 static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
 {
 	u32 tmp;
-
+	u32 delay = 5000;
+	if (hard == MVS_PHY_TUNE) {
+		mvs_write_port_cfg_addr(mvi, phy_id, PHYR_SATA_CTL);
+		tmp = mvs_read_port_cfg_data(mvi, phy_id);
+		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x20000000);
+		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x100000);
+		return;
+	}
 	tmp = mvs_read_port_irq_stat(mvi, phy_id);
 	tmp &= ~PHYEV_RDY_CH;
 	mvs_write_port_irq_stat(mvi, phy_id, tmp);
@@ -71,12 +288,15 @@
 		mvs_write_phy_ctl(mvi, phy_id, tmp);
 		do {
 			tmp = mvs_read_phy_ctl(mvi, phy_id);
-		} while (tmp & PHY_RST_HARD);
+			udelay(10);
+			delay--;
+		} while ((tmp & PHY_RST_HARD) && delay);
+		if (!delay)
+			mv_dprintk("phy hard reset failed.\n");
 	} else {
-		mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_STAT);
-		tmp = mvs_read_port_vsr_data(mvi, phy_id);
+		tmp = mvs_read_phy_ctl(mvi, phy_id);
 		tmp |= PHY_RST;
-		mvs_write_port_vsr_data(mvi, phy_id, tmp);
+		mvs_write_phy_ctl(mvi, phy_id, tmp);
 	}
 }
 
@@ -90,12 +310,25 @@
 
 static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
 {
-	mvs_write_port_vsr_addr(mvi, phy_id, 0x1B4);
-	mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
-	mvs_write_port_vsr_addr(mvi, phy_id, 0x104);
-	mvs_write_port_vsr_data(mvi, phy_id, 0x00018080);
+	u32 tmp;
+	u8 revision = 0;
+
+	revision = mvi->pdev->revision;
+	if (revision == VANIR_A0_REV) {
+		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+		mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
+	}
+	if (revision == VANIR_B0_REV) {
+		mvs_write_port_vsr_addr(mvi, phy_id, CMD_APP_MEM_CTL);
+		mvs_write_port_vsr_data(mvi, phy_id, 0x08001006);
+		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+		mvs_write_port_vsr_data(mvi, phy_id, 0x0000705f);
+	}
+
 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
-	mvs_write_port_vsr_data(mvi, phy_id, 0x00207fff);
+	tmp = mvs_read_port_vsr_data(mvi, phy_id);
+	tmp |= bit(0);
+	mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
 }
 
 static int __devinit mvs_94xx_init(struct mvs_info *mvi)
@@ -103,7 +336,9 @@
 	void __iomem *regs = mvi->regs;
 	int i;
 	u32 tmp, cctl;
+	u8 revision;
 
+	revision = mvi->pdev->revision;
 	mvs_show_pcie_usage(mvi);
 	if (mvi->flags & MVF_FLAG_SOC) {
 		tmp = mr32(MVS_PHY_CTL);
@@ -133,6 +368,28 @@
 		msleep(100);
 	}
 
+	/* disable Multiplexing, enable phy implemented */
+	mw32(MVS_PORTS_IMP, 0xFF);
+
+	if (revision == VANIR_A0_REV) {
+		mw32(MVS_PA_VSR_ADDR, CMD_CMWK_OOB_DET);
+		mw32(MVS_PA_VSR_PORT, 0x00018080);
+	}
+	mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE2);
+	if (revision == VANIR_A0_REV || revision == VANIR_B0_REV)
+		/* set 6G/3G/1.5G, multiplexing, without SSC */
+		mw32(MVS_PA_VSR_PORT, 0x0084d4fe);
+	else
+		/* set 6G/3G/1.5G, multiplexing, with and without SSC */
+		mw32(MVS_PA_VSR_PORT, 0x0084fffe);
+
+	if (revision == VANIR_B0_REV) {
+		mw32(MVS_PA_VSR_ADDR, CMD_APP_MEM_CTL);
+		mw32(MVS_PA_VSR_PORT, 0x08001006);
+		mw32(MVS_PA_VSR_ADDR, CMD_HOST_RD_DATA);
+		mw32(MVS_PA_VSR_PORT, 0x0000705f);
+	}
+
 	/* reset control */
 	mw32(MVS_PCS, 0);		/* MVS_PCS */
 	mw32(MVS_STP_REG_SET_0, 0);
@@ -141,17 +398,8 @@
 	/* init phys */
 	mvs_phy_hacks(mvi);
 
-	/* disable Multiplexing, enable phy implemented */
-	mw32(MVS_PORTS_IMP, 0xFF);
-
-
-	mw32(MVS_PA_VSR_ADDR, 0x00000104);
-	mw32(MVS_PA_VSR_PORT, 0x00018080);
-	mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE8);
-	mw32(MVS_PA_VSR_PORT, 0x0084ffff);
-
 	/* set LED blink when IO*/
-	mw32(MVS_PA_VSR_ADDR, 0x00000030);
+	mw32(MVS_PA_VSR_ADDR, VSR_PHY_ACT_LED);
 	tmp = mr32(MVS_PA_VSR_PORT);
 	tmp &= 0xFFFF00FF;
 	tmp |= 0x00003300;
@@ -175,12 +423,13 @@
 		mvs_94xx_phy_disable(mvi, i);
 		/* set phy local SAS address */
 		mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
-						(mvi->phy[i].dev_sas_addr));
+						cpu_to_le64(mvi->phy[i].dev_sas_addr));
 
 		mvs_94xx_enable_xmt(mvi, i);
+		mvs_94xx_config_reg_from_hba(mvi, i);
 		mvs_94xx_phy_enable(mvi, i);
 
-		mvs_94xx_phy_reset(mvi, i, 1);
+		mvs_94xx_phy_reset(mvi, i, PHY_RST_HARD);
 		msleep(500);
 		mvs_94xx_detect_porttype(mvi, i);
 	}
@@ -211,16 +460,9 @@
 		mvs_update_phyinfo(mvi, i, 1);
 	}
 
-	/* FIXME: update wide port bitmaps */
-
 	/* little endian for open address and command table, etc. */
-	/*
-	 * it seems that ( from the spec ) turning on big-endian won't
-	 * do us any good on big-endian machines, need further confirmation
-	 */
 	cctl = mr32(MVS_CTL);
 	cctl |= CCTL_ENDIAN_CMD;
-	cctl |= CCTL_ENDIAN_DATA;
 	cctl &= ~CCTL_ENDIAN_OPEN;
 	cctl |= CCTL_ENDIAN_RSP;
 	mw32_f(MVS_CTL, cctl);
@@ -228,15 +470,20 @@
 	/* reset CMD queue */
 	tmp = mr32(MVS_PCS);
 	tmp |= PCS_CMD_RST;
+	tmp &= ~PCS_SELF_CLEAR;
 	mw32(MVS_PCS, tmp);
-	/* interrupt coalescing may cause missing HW interrput in some case,
-	 * and the max count is 0x1ff, while our max slot is 0x200,
+	/*
+	 * the max count is 0x1ff, while our max slot is 0x200,
 	 * it will make count 0.
 	 */
 	tmp = 0;
-	mw32(MVS_INT_COAL, tmp);
+	if (MVS_CHIP_SLOT_SZ > 0x1ff)
+		mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
+	else
+		mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
 
-	tmp = 0x100;
+	/* default interrupt coalescing time is 128us */
+	tmp = 0x10000 | interrupt_coalescing;
 	mw32(MVS_INT_COAL_TMOUT, tmp);
 
 	/* ladies and gentlemen, start your engines */
@@ -249,7 +496,7 @@
 
 	/* enable completion queue interrupt */
 	tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
-		CINT_DMA_PCIE);
+		CINT_DMA_PCIE | CINT_NON_SPEC_NCQ_ERROR);
 	tmp |= CINT_PHY_MASK;
 	mw32(MVS_INT_MASK, tmp);
 
@@ -332,13 +579,10 @@
 	if (((stat & IRQ_SAS_A) && mvi->id == 0) ||
 			((stat & IRQ_SAS_B) && mvi->id == 1)) {
 		mw32_f(MVS_INT_STAT, CINT_DONE);
-	#ifndef MVS_USE_TASKLET
+
 		spin_lock(&mvi->lock);
-	#endif
 		mvs_int_full(mvi);
-	#ifndef MVS_USE_TASKLET
 		spin_unlock(&mvi->lock);
-	#endif
 	}
 	return IRQ_HANDLED;
 }
@@ -346,10 +590,48 @@
 static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
 {
 	u32 tmp;
-	mvs_cw32(mvi, 0x300 + (slot_idx >> 3), 1 << (slot_idx % 32));
-	do {
-		tmp = mvs_cr32(mvi, 0x300 + (slot_idx >> 3));
-	} while (tmp & 1 << (slot_idx % 32));
+	tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
+	if (tmp && 1 << (slot_idx % 32)) {
+		mv_printk("command active %08X,  slot [%x].\n", tmp, slot_idx);
+		mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
+			1 << (slot_idx % 32));
+		do {
+			tmp = mvs_cr32(mvi,
+				MVS_COMMAND_ACTIVE + (slot_idx >> 3));
+		} while (tmp & 1 << (slot_idx % 32));
+	}
+}
+
+void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
+{
+	void __iomem *regs = mvi->regs;
+	u32 tmp;
+
+	if (clear_all) {
+		tmp = mr32(MVS_INT_STAT_SRS_0);
+		if (tmp) {
+			mv_dprintk("check SRS 0 %08X.\n", tmp);
+			mw32(MVS_INT_STAT_SRS_0, tmp);
+		}
+		tmp = mr32(MVS_INT_STAT_SRS_1);
+		if (tmp) {
+			mv_dprintk("check SRS 1 %08X.\n", tmp);
+			mw32(MVS_INT_STAT_SRS_1, tmp);
+		}
+	} else {
+		if (reg_set > 31)
+			tmp = mr32(MVS_INT_STAT_SRS_1);
+		else
+			tmp = mr32(MVS_INT_STAT_SRS_0);
+
+		if (tmp & (1 << (reg_set % 32))) {
+			mv_dprintk("register set 0x%x was stopped.\n", reg_set);
+			if (reg_set > 31)
+				mw32(MVS_INT_STAT_SRS_1, 1 << (reg_set % 32));
+			else
+				mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32));
+		}
+	}
 }
 
 static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
@@ -357,37 +639,56 @@
 {
 	void __iomem *regs = mvi->regs;
 	u32 tmp;
+	mvs_94xx_clear_srs_irq(mvi, 0, 1);
 
-	if (type == PORT_TYPE_SATA) {
-		tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
-		mw32(MVS_INT_STAT_SRS_0, tmp);
-	}
-	mw32(MVS_INT_STAT, CINT_CI_STOP);
+	tmp = mr32(MVS_INT_STAT);
+	mw32(MVS_INT_STAT, tmp | CINT_CI_STOP);
 	tmp = mr32(MVS_PCS) | 0xFF00;
 	mw32(MVS_PCS, tmp);
 }
 
+static void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi)
+{
+	void __iomem *regs = mvi->regs;
+	u32 err_0, err_1;
+	u8 i;
+	struct mvs_device *device;
+
+	err_0 = mr32(MVS_NON_NCQ_ERR_0);
+	err_1 = mr32(MVS_NON_NCQ_ERR_1);
+
+	mv_dprintk("non specific ncq error err_0:%x,err_1:%x.\n",
+			err_0, err_1);
+	for (i = 0; i < 32; i++) {
+		if (err_0 & bit(i)) {
+			device = mvs_find_dev_by_reg_set(mvi, i);
+			if (device)
+				mvs_release_task(mvi, device->sas_device);
+		}
+		if (err_1 & bit(i)) {
+			device = mvs_find_dev_by_reg_set(mvi, i+32);
+			if (device)
+				mvs_release_task(mvi, device->sas_device);
+		}
+	}
+
+	mw32(MVS_NON_NCQ_ERR_0, err_0);
+	mw32(MVS_NON_NCQ_ERR_1, err_1);
+}
+
 static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
 {
 	void __iomem *regs = mvi->regs;
-	u32 tmp;
 	u8 reg_set = *tfs;
 
 	if (*tfs == MVS_ID_NOT_MAPPED)
 		return;
 
 	mvi->sata_reg_set &= ~bit(reg_set);
-	if (reg_set < 32) {
+	if (reg_set < 32)
 		w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
-		tmp = mr32(MVS_INT_STAT_SRS_0) & (u32)mvi->sata_reg_set;
-		if (tmp)
-			mw32(MVS_INT_STAT_SRS_0, tmp);
-	} else {
-		w_reg_set_enable(reg_set, mvi->sata_reg_set);
-		tmp = mr32(MVS_INT_STAT_SRS_1) & mvi->sata_reg_set;
-		if (tmp)
-			mw32(MVS_INT_STAT_SRS_1, tmp);
-	}
+	else
+		w_reg_set_enable(reg_set, (u32)(mvi->sata_reg_set >> 32));
 
 	*tfs = MVS_ID_NOT_MAPPED;
 
@@ -403,7 +704,7 @@
 		return 0;
 
 	i = mv_ffc64(mvi->sata_reg_set);
-	if (i > 32) {
+	if (i >= 32) {
 		mvi->sata_reg_set |= bit(i);
 		w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
 		*tfs = i;
@@ -422,9 +723,12 @@
 	int i;
 	struct scatterlist *sg;
 	struct mvs_prd *buf_prd = prd;
+	struct mvs_prd_imt im_len;
+	*(u32 *)&im_len = 0;
 	for_each_sg(scatter, sg, nr, i) {
 		buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
-		buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+		im_len.len = sg_dma_len(sg);
+		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
 		buf_prd++;
 	}
 }
@@ -433,7 +737,7 @@
 {
 	u32 phy_st;
 	phy_st = mvs_read_phy_ctl(mvi, i);
-	if (phy_st & PHY_READY_MASK)	/* phy ready */
+	if (phy_st & PHY_READY_MASK)
 		return 1;
 	return 0;
 }
@@ -447,7 +751,7 @@
 	for (i = 0; i < 7; i++) {
 		mvs_write_port_cfg_addr(mvi, port_id,
 					CONFIG_ID_FRAME0 + i * 4);
-		id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
 	}
 	memcpy(id, id_frame, 28);
 }
@@ -458,15 +762,13 @@
 	int i;
 	u32 id_frame[7];
 
-	/* mvs_hexdump(28, (u8 *)id_frame, 0); */
 	for (i = 0; i < 7; i++) {
 		mvs_write_port_cfg_addr(mvi, port_id,
 					CONFIG_ATT_ID_FRAME0 + i * 4);
-		id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
 		mv_dprintk("94xx phy %d atta frame %d %x.\n",
 			port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
 	}
-	/* mvs_hexdump(28, (u8 *)id_frame, 0); */
 	memcpy(id, id_frame, 28);
 }
 
@@ -526,7 +828,18 @@
 void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
 			struct sas_phy_linkrates *rates)
 {
-	/* TODO */
+	u32 lrmax = 0;
+	u32 tmp;
+
+	tmp = mvs_read_phy_ctl(mvi, phy_id);
+	lrmax = (rates->maximum_linkrate - SAS_LINK_RATE_1_5_GBPS) << 12;
+
+	if (lrmax) {
+		tmp &= ~(0x3 << 12);
+		tmp |= lrmax;
+	}
+	mvs_write_phy_ctl(mvi, phy_id, tmp);
+	mvs_94xx_phy_reset(mvi, phy_id, PHY_RST_HARD);
 }
 
 static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
@@ -603,27 +916,59 @@
 	return -1;
 }
 
-#ifndef DISABLE_HOTPLUG_DMA_FIX
-void mvs_94xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
+				int buf_len, int from, void *prd)
 {
 	int i;
 	struct mvs_prd *buf_prd = prd;
+	dma_addr_t buf_dma;
+	struct mvs_prd_imt im_len;
+
+	*(u32 *)&im_len = 0;
 	buf_prd += from;
-	for (i = 0; i < MAX_SG_ENTRY - from; i++) {
-		buf_prd->addr = cpu_to_le64(buf_dma);
-		buf_prd->im_len.len = cpu_to_le32(buf_len);
-		++buf_prd;
+
+#define PRD_CHAINED_ENTRY 0x01
+	if ((mvi->pdev->revision == VANIR_A0_REV) ||
+			(mvi->pdev->revision == VANIR_B0_REV))
+		buf_dma = (phy_mask <= 0x08) ?
+				mvi->bulk_buffer_dma : mvi->bulk_buffer_dma1;
+	else
+		return;
+
+	for (i = from; i < MAX_SG_ENTRY; i++, ++buf_prd) {
+		if (i == MAX_SG_ENTRY - 1) {
+			buf_prd->addr = cpu_to_le64(virt_to_phys(buf_prd - 1));
+			im_len.len = 2;
+			im_len.misc_ctl = PRD_CHAINED_ENTRY;
+		} else {
+			buf_prd->addr = cpu_to_le64(buf_dma);
+			im_len.len = buf_len;
+		}
+		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
 	}
 }
-#endif
 
-/*
- * FIXME JEJB: temporary nop clear_srs_irq to make 94xx still work
- * with 64xx fixes
- */
-static void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set,
-				   u8 clear_all)
+static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
 {
+	void __iomem *regs = mvi->regs;
+	u32 tmp = 0;
+	/*
+	 * the max count is 0x1ff, while our max slot is 0x200,
+	 * it will make count 0.
+	 */
+	if (time == 0) {
+		mw32(MVS_INT_COAL, 0);
+		mw32(MVS_INT_COAL_TMOUT, 0x10000);
+	} else {
+		if (MVS_CHIP_SLOT_SZ > 0x1ff)
+			mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
+		else
+			mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
+
+		tmp = 0x10000 | time;
+		mw32(MVS_INT_COAL_TMOUT, tmp);
+	}
+
 }
 
 const struct mvs_dispatch mvs_94xx_dispatch = {
@@ -648,7 +993,6 @@
 	mvs_write_port_irq_stat,
 	mvs_read_port_irq_mask,
 	mvs_write_port_irq_mask,
-	mvs_get_sas_addr,
 	mvs_94xx_command_active,
 	mvs_94xx_clear_srs_irq,
 	mvs_94xx_issue_stop,
@@ -676,8 +1020,8 @@
 	mvs_94xx_spi_buildcmd,
 	mvs_94xx_spi_issuecmd,
 	mvs_94xx_spi_waitdataready,
-#ifndef DISABLE_HOTPLUG_DMA_FIX
 	mvs_94xx_fix_dma,
-#endif
+	mvs_94xx_tune_interrupt,
+	mvs_94xx_non_spec_ncq_error,
 };
 
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
index 8835bef..8f7eb4f 100644
--- a/drivers/scsi/mvsas/mv_94xx.h
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -30,6 +30,14 @@
 
 #define MAX_LINK_RATE		SAS_LINK_RATE_6_0_GBPS
 
+enum VANIR_REVISION_ID {
+	VANIR_A0_REV		= 0xA0,
+	VANIR_B0_REV		= 0x01,
+	VANIR_C0_REV		= 0x02,
+	VANIR_C1_REV		= 0x03,
+	VANIR_C2_REV		= 0xC2,
+};
+
 enum hw_registers {
 	MVS_GBL_CTL		= 0x04,  /* global control */
 	MVS_GBL_INT_STAT	= 0x00,  /* global irq status */
@@ -101,6 +109,7 @@
 	MVS_P4_VSR_DATA 	= 0x254, /* phy4 VSR data */
 	MVS_PA_VSR_ADDR		= 0x290, /* All port VSR addr */
 	MVS_PA_VSR_PORT		= 0x294, /* All port VSR data */
+	MVS_COMMAND_ACTIVE	= 0x300,
 };
 
 enum pci_cfg_registers {
@@ -112,26 +121,29 @@
 
 /*  SAS/SATA Vendor Specific Port Registers */
 enum sas_sata_vsp_regs {
-	VSR_PHY_STAT		= 0x00 * 4, /* Phy Status */
-	VSR_PHY_MODE1		= 0x01 * 4, /* phy tx */
-	VSR_PHY_MODE2		= 0x02 * 4, /* tx scc */
-	VSR_PHY_MODE3		= 0x03 * 4, /* pll */
-	VSR_PHY_MODE4		= 0x04 * 4, /* VCO */
-	VSR_PHY_MODE5		= 0x05 * 4, /* Rx */
-	VSR_PHY_MODE6		= 0x06 * 4, /* CDR */
-	VSR_PHY_MODE7		= 0x07 * 4, /* Impedance */
-	VSR_PHY_MODE8		= 0x08 * 4, /* Voltage */
-	VSR_PHY_MODE9		= 0x09 * 4, /* Test */
-	VSR_PHY_MODE10		= 0x0A * 4, /* Power */
-	VSR_PHY_MODE11		= 0x0B * 4, /* Phy Mode */
-	VSR_PHY_VS0		= 0x0C * 4, /* Vednor Specific 0 */
-	VSR_PHY_VS1		= 0x0D * 4, /* Vednor Specific 1 */
+	VSR_PHY_STAT		= 0x00 * 4, /* Phy Interrupt Status */
+	VSR_PHY_MODE1		= 0x01 * 4, /* phy Interrupt Enable */
+	VSR_PHY_MODE2		= 0x02 * 4, /* Phy Configuration */
+	VSR_PHY_MODE3		= 0x03 * 4, /* Phy Status */
+	VSR_PHY_MODE4		= 0x04 * 4, /* Phy Counter 0 */
+	VSR_PHY_MODE5		= 0x05 * 4, /* Phy Counter 1 */
+	VSR_PHY_MODE6		= 0x06 * 4, /* Event Counter Control */
+	VSR_PHY_MODE7		= 0x07 * 4, /* Event Counter Select */
+	VSR_PHY_MODE8		= 0x08 * 4, /* Event Counter 0 */
+	VSR_PHY_MODE9		= 0x09 * 4, /* Event Counter 1 */
+	VSR_PHY_MODE10		= 0x0A * 4, /* Event Counter 2 */
+	VSR_PHY_MODE11		= 0x0B * 4, /* Event Counter 3 */
+	VSR_PHY_ACT_LED		= 0x0C * 4, /* Activity LED control */
+
+	VSR_PHY_FFE_CONTROL	= 0x10C,
+	VSR_PHY_DFE_UPDATE_CRTL	= 0x110,
+	VSR_REF_CLOCK_CRTL	= 0x1A0,
 };
 
 enum chip_register_bits {
 	PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
-	PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
-	PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (12),
+	PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 12),
+	PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
 	PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
 			(0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
 };
@@ -169,22 +181,75 @@
 	IRQ_PCIE_ERR                   = (1 << 31),
 };
 
+union reg_phy_cfg {
+	u32 v;
+	struct {
+		u32 phy_reset:1;
+		u32 sas_support:1;
+		u32 sata_support:1;
+		u32 sata_host_mode:1;
+		/*
+		 * bit 2: 6Gbps support
+		 * bit 1: 3Gbps support
+		 * bit 0: 1.5Gbps support
+		 */
+		u32 speed_support:3;
+		u32 snw_3_support:1;
+		u32 tx_lnk_parity:1;
+		/*
+		 * bit 5: G1 (1.5Gbps) Without SSC
+		 * bit 4: G1 (1.5Gbps) with SSC
+		 * bit 3: G2 (3.0Gbps) Without SSC
+		 * bit 2: G2 (3.0Gbps) with SSC
+		 * bit 1: G3 (6.0Gbps) without SSC
+		 * bit 0: G3 (6.0Gbps) with SSC
+		 */
+		u32 tx_spt_phs_lnk_rate:6;
+		/* 8h: 1.5Gbps 9h: 3Gbps Ah: 6Gbps */
+		u32 tx_lgcl_lnk_rate:4;
+		u32 tx_ssc_type:1;
+		u32 sata_spin_up_spt:1;
+		u32 sata_spin_up_en:1;
+		u32 bypass_oob:1;
+		u32 disable_phy:1;
+		u32 rsvd:8;
+	} u;
+};
+
 #define MAX_SG_ENTRY		255
 
 struct mvs_prd_imt {
+#ifndef __BIG_ENDIAN
 	__le32			len:22;
 	u8			_r_a:2;
 	u8			misc_ctl:4;
 	u8			inter_sel:4;
+#else
+	u32			inter_sel:4;
+	u32			misc_ctl:4;
+	u32			_r_a:2;
+	u32			len:22;
+#endif
 };
 
 struct mvs_prd {
 	/* 64-bit buffer address */
 	__le64			addr;
 	/* 22-bit length */
-	struct mvs_prd_imt	im_len;
+	__le32			im_len;
 } __attribute__ ((packed));
 
+/*
+ * these registers are accessed through port vendor
+ * specific address/data registers
+ */
+enum sas_sata_phy_regs {
+	GENERATION_1_SETTING		= 0x118,
+	GENERATION_1_2_SETTING		= 0x11C,
+	GENERATION_2_3_SETTING		= 0x120,
+	GENERATION_3_4_SETTING		= 0x124,
+};
+
 #define SPI_CTRL_REG_94XX           	0xc800
 #define SPI_ADDR_REG_94XX            	0xc804
 #define SPI_WR_DATA_REG_94XX         0xc808
diff --git a/drivers/scsi/mvsas/mv_chips.h b/drivers/scsi/mvsas/mv_chips.h
index 1753a6f..bcc4080 100644
--- a/drivers/scsi/mvsas/mv_chips.h
+++ b/drivers/scsi/mvsas/mv_chips.h
@@ -164,7 +164,6 @@
 {
 	u32 tmp;
 
-	/* workaround for SATA R-ERR, to ignore phy glitch */
 	tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
 	tmp &= ~(1 << 9);
 	tmp |= (1 << 10);
@@ -179,23 +178,10 @@
 	tmp |= 0x3fff;
 	mvs_cw32(mvi, CMD_SAS_CTL0, tmp);
 
-	/* workaround for WDTIMEOUT , set to 550 ms */
 	mvs_cw32(mvi, CMD_WD_TIMER, 0x7a0000);
 
 	/* not to halt for different port op during wideport link change */
 	mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d);
-
-	/* workaround for Seagate disk not-found OOB sequence, recv
-	 * COMINIT before sending out COMWAKE */
-	tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
-	tmp &= 0x0000ffff;
-	tmp |= 0x00fa0000;
-	mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
-
-	tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
-	tmp &= 0x1fffffff;
-	tmp |= (2U << 29);	/* 8 ms retry */
-	mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
 }
 
 static inline void mvs_int_sata(struct mvs_info *mvi)
@@ -223,6 +209,9 @@
 			mvs_int_port(mvi, i, tmp);
 	}
 
+	if (stat & CINT_NON_SPEC_NCQ_ERROR)
+		MVS_CHIP_DISP->non_spec_ncq_error(mvi);
+
 	if (stat & CINT_SRS)
 		mvs_int_sata(mvi);
 
diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h
index bc00c94..dec7cad 100644
--- a/drivers/scsi/mvsas/mv_defs.h
+++ b/drivers/scsi/mvsas/mv_defs.h
@@ -43,7 +43,6 @@
 
 /* driver compile-time configuration */
 enum driver_configuration {
-	MVS_SLOTS		= 512,	/* command slots */
 	MVS_TX_RING_SZ		= 1024,	/* TX ring size (12-bit) */
 	MVS_RX_RING_SZ		= 1024, /* RX ring size (12-bit) */
 					/* software requires power-of-2
@@ -56,8 +55,7 @@
 	MVS_SSP_CMD_SZ		= 64,	/* SSP command table buffer size */
 	MVS_ATA_CMD_SZ		= 96,	/* SATA command table buffer size */
 	MVS_OAF_SZ		= 64,	/* Open address frame buffer size */
-	MVS_QUEUE_SIZE	= 32,	/* Support Queue depth */
-	MVS_CAN_QUEUE		= MVS_SLOTS - 2,	/* SCSI Queue depth */
+	MVS_QUEUE_SIZE		= 64,	/* Support Queue depth */
 	MVS_SOC_CAN_QUEUE	= MVS_SOC_SLOTS - 2,
 };
 
@@ -144,6 +142,7 @@
 	CINT_DMA_PCIE		= (1U << 27),	/* DMA to PCIE timeout */
 	CINT_MEM		= (1U << 26),	/* int mem parity err */
 	CINT_I2C_SLAVE		= (1U << 25),	/* slave I2C event */
+	CINT_NON_SPEC_NCQ_ERROR	= (1U << 25),	/* Non specific NCQ error */
 	CINT_SRS		= (1U << 3),	/* SRS event */
 	CINT_CI_STOP		= (1U << 1),	/* cmd issue stopped */
 	CINT_DONE		= (1U << 0),	/* cmd completion */
@@ -161,7 +160,7 @@
 	TXQ_CMD_SSP		= 1,		/* SSP protocol */
 	TXQ_CMD_SMP		= 2,		/* SMP protocol */
 	TXQ_CMD_STP		= 3,		/* STP/SATA protocol */
-	TXQ_CMD_SSP_FREE_LIST	= 4,		/* add to SSP targ free list */
+	TXQ_CMD_SSP_FREE_LIST	= 4,		/* add to SSP target free list */
 	TXQ_CMD_SLOT_RESET	= 7,		/* reset command slot */
 	TXQ_MODE_I		= (1U << 28),	/* mode: 0=target,1=initiator */
 	TXQ_MODE_TARGET 	= 0,
@@ -391,15 +390,15 @@
 };
 
 enum mvs_info_flags {
-	MVF_MSI		= (1U << 0),	/* MSI is enabled */
 	MVF_PHY_PWR_FIX	= (1U << 1),	/* bug workaround */
 	MVF_FLAG_SOC		= (1U << 2),	/* SoC integrated controllers */
 };
 
 enum mvs_event_flags {
-	PHY_PLUG_EVENT	= (3U),
+	PHY_PLUG_EVENT		= (3U),
 	PHY_PLUG_IN		= (1U << 0),	/* phy plug in */
 	PHY_PLUG_OUT		= (1U << 1),	/* phy plug out */
+	EXP_BRCT_CHG		= (1U << 2),	/* broadcast change */
 };
 
 enum mvs_port_type {
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 90b6366..4e9af66 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -34,22 +34,25 @@
 	"\tThe mvsas SAS LLDD supports both modes.\n"
 	"\tDefault: 1 (Direct Mode).\n");
 
+int interrupt_coalescing = 0x80;
+
 static struct scsi_transport_template *mvs_stt;
 struct kmem_cache *mvs_task_list_cache;
 static const struct mvs_chip_info mvs_chips[] = {
-	[chip_6320] =	{ 1, 2, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-	[chip_6440] =	{ 1, 4, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-	[chip_6485] =	{ 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
-	[chip_9180] =	{ 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
-	[chip_9480] =	{ 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
-	[chip_9445] =	{ 1, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
-	[chip_9485] =	{ 2, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
-	[chip_1300] =	{ 1, 4, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-	[chip_1320] =	{ 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
+	[chip_6320] =	{ 1, 2, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+	[chip_6440] =	{ 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+	[chip_6485] =	{ 1, 8, 0x800, 33, 32, 6, 10, &mvs_64xx_dispatch, },
+	[chip_9180] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
+	[chip_9480] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
+	[chip_9445] =	{ 1, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+	[chip_9485] =	{ 2, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+	[chip_1300] =	{ 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+	[chip_1320] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
 };
 
+struct device_attribute *mvst_host_attrs[];
+
 #define SOC_SAS_NUM 2
-#define SG_MX 64
 
 static struct scsi_host_template mvs_sht = {
 	.module			= THIS_MODULE,
@@ -66,7 +69,7 @@
 	.can_queue		= 1,
 	.cmd_per_lun		= 1,
 	.this_id		= -1,
-	.sg_tablesize		= SG_MX,
+	.sg_tablesize		= SG_ALL,
 	.max_sectors		= SCSI_DEFAULT_MAX_SECTORS,
 	.use_clustering		= ENABLE_CLUSTERING,
 	.eh_device_reset_handler = sas_eh_device_reset_handler,
@@ -74,6 +77,7 @@
 	.slave_alloc		= mvs_slave_alloc,
 	.target_destroy		= sas_target_destroy,
 	.ioctl			= sas_ioctl,
+	.shost_attrs		= mvst_host_attrs,
 };
 
 static struct sas_domain_function_template mvs_transport_ops = {
@@ -100,6 +104,7 @@
 	struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
 	phy->mvi = mvi;
+	phy->port = NULL;
 	init_timer(&phy->timer);
 	sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
 	sas_phy->class = SAS;
@@ -128,7 +133,7 @@
 	if (mvi->flags & MVF_FLAG_SOC)
 		slot_nr = MVS_SOC_SLOTS;
 	else
-		slot_nr = MVS_SLOTS;
+		slot_nr = MVS_CHIP_SLOT_SZ;
 
 	if (mvi->dma_pool)
 		pci_pool_destroy(mvi->dma_pool);
@@ -148,25 +153,26 @@
 		dma_free_coherent(mvi->dev,
 				  sizeof(*mvi->slot) * slot_nr,
 				  mvi->slot, mvi->slot_dma);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
+
 	if (mvi->bulk_buffer)
 		dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
 				  mvi->bulk_buffer, mvi->bulk_buffer_dma);
-#endif
+	if (mvi->bulk_buffer1)
+		dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
+				  mvi->bulk_buffer1, mvi->bulk_buffer_dma1);
 
 	MVS_CHIP_DISP->chip_iounmap(mvi);
 	if (mvi->shost)
 		scsi_host_put(mvi->shost);
 	list_for_each_entry(mwq, &mvi->wq_list, entry)
 		cancel_delayed_work(&mwq->work_q);
+	kfree(mvi->tags);
 	kfree(mvi);
 }
 
-#ifdef MVS_USE_TASKLET
-struct tasklet_struct	mv_tasklet;
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
 static void mvs_tasklet(unsigned long opaque)
 {
-	unsigned long flags;
 	u32 stat;
 	u16 core_nr, i = 0;
 
@@ -179,35 +185,49 @@
 	if (unlikely(!mvi))
 		BUG_ON(1);
 
+	stat = MVS_CHIP_DISP->isr_status(mvi, mvi->pdev->irq);
+	if (!stat)
+		goto out;
+
 	for (i = 0; i < core_nr; i++) {
 		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
-		stat = MVS_CHIP_DISP->isr_status(mvi, mvi->irq);
-		if (stat)
-			MVS_CHIP_DISP->isr(mvi, mvi->irq, stat);
+		MVS_CHIP_DISP->isr(mvi, mvi->pdev->irq, stat);
 	}
+out:
+	MVS_CHIP_DISP->interrupt_enable(mvi);
 
 }
 #endif
 
 static irqreturn_t mvs_interrupt(int irq, void *opaque)
 {
-	u32 core_nr, i = 0;
+	u32 core_nr;
 	u32 stat;
 	struct mvs_info *mvi;
 	struct sas_ha_struct *sha = opaque;
+#ifndef CONFIG_SCSI_MVSAS_TASKLET
+	u32 i;
+#endif
 
 	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
 	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
 
 	if (unlikely(!mvi))
 		return IRQ_NONE;
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	MVS_CHIP_DISP->interrupt_disable(mvi);
+#endif
 
 	stat = MVS_CHIP_DISP->isr_status(mvi, irq);
-	if (!stat)
+	if (!stat) {
+	#ifdef CONFIG_SCSI_MVSAS_TASKLET
+		MVS_CHIP_DISP->interrupt_enable(mvi);
+	#endif
 		return IRQ_NONE;
+	}
 
-#ifdef MVS_USE_TASKLET
-	tasklet_schedule(&mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	tasklet_schedule(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
 #else
 	for (i = 0; i < core_nr; i++) {
 		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
@@ -225,7 +245,7 @@
 	if (mvi->flags & MVF_FLAG_SOC)
 		slot_nr = MVS_SOC_SLOTS;
 	else
-		slot_nr = MVS_SLOTS;
+		slot_nr = MVS_CHIP_SLOT_SZ;
 
 	spin_lock_init(&mvi->lock);
 	for (i = 0; i < mvi->chip->n_phy; i++) {
@@ -273,13 +293,18 @@
 		goto err_out;
 	memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr);
 
-#ifndef DISABLE_HOTPLUG_DMA_FIX
 	mvi->bulk_buffer = dma_alloc_coherent(mvi->dev,
 				       TRASH_BUCKET_SIZE,
 				       &mvi->bulk_buffer_dma, GFP_KERNEL);
 	if (!mvi->bulk_buffer)
 		goto err_out;
-#endif
+
+	mvi->bulk_buffer1 = dma_alloc_coherent(mvi->dev,
+				       TRASH_BUCKET_SIZE,
+				       &mvi->bulk_buffer_dma1, GFP_KERNEL);
+	if (!mvi->bulk_buffer1)
+		goto err_out;
+
 	sprintf(pool_name, "%s%d", "mvs_dma_pool", mvi->id);
 	mvi->dma_pool = pci_pool_create(pool_name, mvi->pdev, MVS_SLOT_BUF_SZ, 16, 0);
 	if (!mvi->dma_pool) {
@@ -354,11 +379,12 @@
 				const struct pci_device_id *ent,
 				struct Scsi_Host *shost, unsigned int id)
 {
-	struct mvs_info *mvi;
+	struct mvs_info *mvi = NULL;
 	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
 
-	mvi = kzalloc(sizeof(*mvi) + MVS_SLOTS * sizeof(struct mvs_slot_info),
-			GFP_KERNEL);
+	mvi = kzalloc(sizeof(*mvi) +
+		(1L << mvs_chips[ent->driver_data].slot_width) *
+		sizeof(struct mvs_slot_info), GFP_KERNEL);
 	if (!mvi)
 		return NULL;
 
@@ -367,7 +393,6 @@
 	mvi->chip_id = ent->driver_data;
 	mvi->chip = &mvs_chips[mvi->chip_id];
 	INIT_LIST_HEAD(&mvi->wq_list);
-	mvi->irq = pdev->irq;
 
 	((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi;
 	((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy;
@@ -375,9 +400,10 @@
 	mvi->id = id;
 	mvi->sas = sha;
 	mvi->shost = shost;
-#ifdef MVS_USE_TASKLET
-	tasklet_init(&mv_tasklet, mvs_tasklet, (unsigned long)sha);
-#endif
+
+	mvi->tags = kzalloc(MVS_CHIP_SLOT_SZ>>3, GFP_KERNEL);
+	if (!mvi->tags)
+		goto err_out;
 
 	if (MVS_CHIP_DISP->chip_ioremap(mvi))
 		goto err_out;
@@ -388,7 +414,6 @@
 	return NULL;
 }
 
-/* move to PCI layer or libata core? */
 static int pci_go_64(struct pci_dev *pdev)
 {
 	int rc;
@@ -450,7 +475,7 @@
 	((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
 
 	shost->transportt = mvs_stt;
-	shost->max_id = 128;
+	shost->max_id = MVS_MAX_DEVICES;
 	shost->max_lun = ~0;
 	shost->max_channel = 1;
 	shost->max_cmd_len = 16;
@@ -493,11 +518,12 @@
 	if (mvi->flags & MVF_FLAG_SOC)
 		can_queue = MVS_SOC_CAN_QUEUE;
 	else
-		can_queue = MVS_CAN_QUEUE;
+		can_queue = MVS_CHIP_SLOT_SZ;
 
 	sha->lldd_queue_size = can_queue;
+	shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
 	shost->can_queue = can_queue;
-	mvi->shost->cmd_per_lun = MVS_SLOTS/sha->num_phys;
+	mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
 	sha->core.shost = mvi->shost;
 }
 
@@ -518,6 +544,7 @@
 {
 	unsigned int rc, nhost = 0;
 	struct mvs_info *mvi;
+	struct mvs_prv_info *mpi;
 	irq_handler_t irq_handler = mvs_interrupt;
 	struct Scsi_Host *shost = NULL;
 	const struct mvs_chip_info *chip;
@@ -569,6 +596,9 @@
 			goto err_out_regions;
 		}
 
+		memset(&mvi->hba_info_param, 0xFF,
+			sizeof(struct hba_info_page));
+
 		mvs_init_sas_add(mvi);
 
 		mvi->instance = nhost;
@@ -579,8 +609,9 @@
 		}
 		nhost++;
 	} while (nhost < chip->n_host);
-#ifdef MVS_USE_TASKLET
-	tasklet_init(&mv_tasklet, mvs_tasklet,
+	mpi = (struct mvs_prv_info *)(SHOST_TO_SAS_HA(shost)->lldd_ha);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	tasklet_init(&(mpi->mv_tasklet), mvs_tasklet,
 		     (unsigned long)SHOST_TO_SAS_HA(shost));
 #endif
 
@@ -625,8 +656,8 @@
 	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
 	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
 
-#ifdef MVS_USE_TASKLET
-	tasklet_kill(&mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
 #endif
 
 	pci_set_drvdata(pdev, NULL);
@@ -635,7 +666,7 @@
 	scsi_remove_host(mvi->shost);
 
 	MVS_CHIP_DISP->interrupt_disable(mvi);
-	free_irq(mvi->irq, sha);
+	free_irq(mvi->pdev->irq, sha);
 	for (i = 0; i < core_nr; i++) {
 		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
 		mvs_free(mvi);
@@ -703,6 +734,70 @@
 	.remove		= __devexit_p(mvs_pci_remove),
 };
 
+static ssize_t
+mvs_show_driver_version(struct device *cdev,
+		struct device_attribute *attr,  char *buffer)
+{
+	return snprintf(buffer, PAGE_SIZE, "%s\n", DRV_VERSION);
+}
+
+static DEVICE_ATTR(driver_version,
+			 S_IRUGO,
+			 mvs_show_driver_version,
+			 NULL);
+
+static ssize_t
+mvs_store_interrupt_coalescing(struct device *cdev,
+			struct device_attribute *attr,
+			const char *buffer, size_t size)
+{
+	int val = 0;
+	struct mvs_info *mvi = NULL;
+	struct Scsi_Host *shost = class_to_shost(cdev);
+	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+	u8 i, core_nr;
+	if (buffer == NULL)
+		return size;
+
+	if (sscanf(buffer, "%d", &val) != 1)
+		return -EINVAL;
+
+	if (val >= 0x10000) {
+		mv_dprintk("interrupt coalescing timer %d us is"
+			"too long\n", val);
+		return strlen(buffer);
+	}
+
+	interrupt_coalescing = val;
+
+	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+	if (unlikely(!mvi))
+		return -EINVAL;
+
+	for (i = 0; i < core_nr; i++) {
+		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+		if (MVS_CHIP_DISP->tune_interrupt)
+			MVS_CHIP_DISP->tune_interrupt(mvi,
+				interrupt_coalescing);
+	}
+	mv_dprintk("set interrupt coalescing time to %d us\n",
+		interrupt_coalescing);
+	return strlen(buffer);
+}
+
+static ssize_t mvs_show_interrupt_coalescing(struct device *cdev,
+			struct device_attribute *attr, char *buffer)
+{
+	return snprintf(buffer, PAGE_SIZE, "%d\n", interrupt_coalescing);
+}
+
+static DEVICE_ATTR(interrupt_coalescing,
+			 S_IRUGO|S_IWUSR,
+			 mvs_show_interrupt_coalescing,
+			 mvs_store_interrupt_coalescing);
+
 /* task handler */
 struct task_struct *mvs_th;
 static int __init mvs_init(void)
@@ -739,6 +834,12 @@
 	kmem_cache_destroy(mvs_task_list_cache);
 }
 
+struct device_attribute *mvst_host_attrs[] = {
+	&dev_attr_driver_version,
+	&dev_attr_interrupt_coalescing,
+	NULL,
+};
+
 module_init(mvs_init);
 module_exit(mvs_exit);
 
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 0ef2742..4958fef 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -38,7 +38,7 @@
 
 void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
 {
-	void *bitmap = &mvi->tags;
+	void *bitmap = mvi->tags;
 	clear_bit(tag, bitmap);
 }
 
@@ -49,14 +49,14 @@
 
 void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
 {
-	void *bitmap = &mvi->tags;
+	void *bitmap = mvi->tags;
 	set_bit(tag, bitmap);
 }
 
 inline int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
 {
 	unsigned int index, tag;
-	void *bitmap = &mvi->tags;
+	void *bitmap = mvi->tags;
 
 	index = find_first_zero_bit(bitmap, mvi->tags_num);
 	tag = index;
@@ -74,126 +74,6 @@
 		mvs_tag_clear(mvi, i);
 }
 
-void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
-{
-	u32 i;
-	u32 run;
-	u32 offset;
-
-	offset = 0;
-	while (size) {
-		printk(KERN_DEBUG"%08X : ", baseaddr + offset);
-		if (size >= 16)
-			run = 16;
-		else
-			run = size;
-		size -= run;
-		for (i = 0; i < 16; i++) {
-			if (i < run)
-				printk(KERN_DEBUG"%02X ", (u32)data[i]);
-			else
-				printk(KERN_DEBUG"   ");
-		}
-		printk(KERN_DEBUG": ");
-		for (i = 0; i < run; i++)
-			printk(KERN_DEBUG"%c",
-				isalnum(data[i]) ? data[i] : '.');
-		printk(KERN_DEBUG"\n");
-		data = &data[16];
-		offset += run;
-	}
-	printk(KERN_DEBUG"\n");
-}
-
-#if (_MV_DUMP > 1)
-static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
-				   enum sas_protocol proto)
-{
-	u32 offset;
-	struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
-	offset = slot->cmd_size + MVS_OAF_SZ +
-	    MVS_CHIP_DISP->prd_size() * slot->n_elem;
-	dev_printk(KERN_DEBUG, mvi->dev, "+---->Status buffer[%d] :\n",
-			tag);
-	mvs_hexdump(32, (u8 *) slot->response,
-		    (u32) slot->buf_dma + offset);
-}
-#endif
-
-static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
-				enum sas_protocol proto)
-{
-#if (_MV_DUMP > 1)
-	u32 sz, w_ptr;
-	u64 addr;
-	struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
-	/*Delivery Queue */
-	sz = MVS_CHIP_SLOT_SZ;
-	w_ptr = slot->tx;
-	addr = mvi->tx_dma;
-	dev_printk(KERN_DEBUG, mvi->dev,
-		"Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
-	dev_printk(KERN_DEBUG, mvi->dev,
-		"Delivery Queue Base Address=0x%llX (PA)"
-		"(tx_dma=0x%llX), Entry=%04d\n",
-		addr, (unsigned long long)mvi->tx_dma, w_ptr);
-	mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
-			(u32) mvi->tx_dma + sizeof(u32) * w_ptr);
-	/*Command List */
-	addr = mvi->slot_dma;
-	dev_printk(KERN_DEBUG, mvi->dev,
-		"Command List Base Address=0x%llX (PA)"
-		"(slot_dma=0x%llX), Header=%03d\n",
-		addr, (unsigned long long)slot->buf_dma, tag);
-	dev_printk(KERN_DEBUG, mvi->dev, "Command Header[%03d]:\n", tag);
-	/*mvs_cmd_hdr */
-	mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
-		(u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
-	/*1.command table area */
-	dev_printk(KERN_DEBUG, mvi->dev, "+---->Command Table :\n");
-	mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
-	/*2.open address frame area */
-	dev_printk(KERN_DEBUG, mvi->dev, "+---->Open Address Frame :\n");
-	mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
-				(u32) slot->buf_dma + slot->cmd_size);
-	/*3.status buffer */
-	mvs_hba_sb_dump(mvi, tag, proto);
-	/*4.PRD table */
-	dev_printk(KERN_DEBUG, mvi->dev, "+---->PRD table :\n");
-	mvs_hexdump(MVS_CHIP_DISP->prd_size() * slot->n_elem,
-		(u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
-		(u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
-#endif
-}
-
-static void mvs_hba_cq_dump(struct mvs_info *mvi)
-{
-#if (_MV_DUMP > 2)
-	u64 addr;
-	void __iomem *regs = mvi->regs;
-	u32 entry = mvi->rx_cons + 1;
-	u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
-
-	/*Completion Queue */
-	addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
-	dev_printk(KERN_DEBUG, mvi->dev, "Completion Task = 0x%p\n",
-		   mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
-	dev_printk(KERN_DEBUG, mvi->dev,
-		"Completion List Base Address=0x%llX (PA), "
-		"CQ_Entry=%04d, CQ_WP=0x%08X\n",
-		addr, entry - 1, mvi->rx[0]);
-	mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
-		    mvi->rx_dma + sizeof(u32) * entry);
-#endif
-}
-
-void mvs_get_sas_addr(void *buf, u32 buflen)
-{
-	/*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
-}
-
 struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
 {
 	unsigned long i = 0, j = 0, hi = 0;
@@ -222,7 +102,6 @@
 
 }
 
-/* FIXME */
 int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
 {
 	unsigned long i = 0, j = 0, n = 0, num = 0;
@@ -253,6 +132,20 @@
 	return num;
 }
 
+struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi,
+						u8 reg_set)
+{
+	u32 dev_no;
+	for (dev_no = 0; dev_no < MVS_MAX_DEVICES; dev_no++) {
+		if (mvi->devices[dev_no].taskfileset == MVS_ID_NOT_MAPPED)
+			continue;
+
+		if (mvi->devices[dev_no].taskfileset == reg_set)
+			return &mvi->devices[dev_no];
+	}
+	return NULL;
+}
+
 static inline void mvs_free_reg_set(struct mvs_info *mvi,
 				struct mvs_device *dev)
 {
@@ -283,7 +176,6 @@
 	}
 }
 
-/* FIXME: locking? */
 int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
 			void *funcdata)
 {
@@ -309,12 +201,12 @@
 		tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
 		if (tmp & PHY_RST_HARD)
 			break;
-		MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
+		MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_HARD_RESET);
 		break;
 
 	case PHY_FUNC_LINK_RESET:
 		MVS_CHIP_DISP->phy_enable(mvi, phy_id);
-		MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
+		MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_SOFT_RESET);
 		break;
 
 	case PHY_FUNC_DISABLE:
@@ -406,14 +298,10 @@
 
 	if (ret)
 		return ret;
-	if (dev_is_sata(dev)) {
-		/* may set PIO mode */
-	#if MV_DISABLE_NCQ
-		struct ata_port *ap = dev->sata_dev.ap;
-		struct ata_device *adev = ap->link.device;
-		adev->flags |= ATA_DFLAG_NCQ_OFF;
-		scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
-	#endif
+	if (!dev_is_sata(dev)) {
+		sas_change_queue_depth(sdev,
+			MVS_QUEUE_SIZE,
+			SCSI_QDEPTH_DEFAULT);
 	}
 	return 0;
 }
@@ -424,6 +312,7 @@
 	unsigned short core_nr;
 	struct mvs_info *mvi;
 	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+	struct mvs_prv_info *mvs_prv = sha->lldd_ha;
 
 	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
 
@@ -432,15 +321,17 @@
 		for (i = 0; i < mvi->chip->n_phy; ++i)
 			mvs_bytes_dmaed(mvi, i);
 	}
+	mvs_prv->scan_finished = 1;
 }
 
 int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
-	/* give the phy enabling interrupt event time to come in (1s
-	 * is empirically about all it takes) */
-	if (time < HZ)
+	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+	struct mvs_prv_info *mvs_prv = sha->lldd_ha;
+
+	if (mvs_prv->scan_finished == 0)
 		return 0;
-	/* Wait for discovery to finish */
+
 	scsi_flush_work(shost);
 	return 1;
 }
@@ -461,10 +352,7 @@
 	void *buf_prd;
 	struct mvs_slot_info *slot = &mvi->slot_info[tag];
 	u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#if _MV_DUMP
-	u8 *buf_cmd;
-	void *from;
-#endif
+
 	/*
 	 * DMA-map SMP request, response buffers
 	 */
@@ -496,15 +384,7 @@
 	buf_tmp = slot->buf;
 	buf_tmp_dma = slot->buf_dma;
 
-#if _MV_DUMP
-	buf_cmd = buf_tmp;
-	hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-	buf_tmp += req_len;
-	buf_tmp_dma += req_len;
-	slot->cmd_size = req_len;
-#else
 	hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
-#endif
 
 	/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
 	buf_oaf = buf_tmp;
@@ -553,12 +433,6 @@
 	/* fill in PRD (scatter/gather) table, if any */
 	MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
 
-#if _MV_DUMP
-	/* copy cmd table */
-	from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
-	memcpy(buf_cmd, from + sg_req->offset, req_len);
-	kunmap_atomic(from, KM_IRQ0);
-#endif
 	return 0;
 
 err_out_2:
@@ -616,14 +490,11 @@
 		(mvi_dev->taskfileset << TXQ_SRS_SHIFT);
 	mvi->tx[mvi->tx_prod] = cpu_to_le32(del_q);
 
-#ifndef DISABLE_HOTPLUG_DMA_FIX
 	if (task->data_dir == DMA_FROM_DEVICE)
 		flags = (MVS_CHIP_DISP->prd_count() << MCH_PRD_LEN_SHIFT);
 	else
 		flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#else
-	flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#endif
+
 	if (task->ata_task.use_ncq)
 		flags |= MCH_FPDMA;
 	if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
@@ -631,11 +502,8 @@
 			flags |= MCH_ATAPI;
 	}
 
-	/* FIXME: fill in port multiplier number */
-
 	hdr->flags = cpu_to_le32(flags);
 
-	/* FIXME: the low order order 5 bits for the TAG if enable NCQ */
 	if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag))
 		task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
 	else
@@ -657,9 +525,6 @@
 
 	buf_tmp += MVS_ATA_CMD_SZ;
 	buf_tmp_dma += MVS_ATA_CMD_SZ;
-#if _MV_DUMP
-	slot->cmd_size = MVS_ATA_CMD_SZ;
-#endif
 
 	/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
 	/* used for STP.  unused for SATA? */
@@ -682,9 +547,6 @@
 	buf_tmp_dma += i;
 
 	/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
-	/* FIXME: probably unused, for SATA.  kept here just in case
-	 * we get a STP/SATA error information record
-	 */
 	slot->response = buf_tmp;
 	hdr->status_buf = cpu_to_le64(buf_tmp_dma);
 	if (mvi->flags & MVF_FLAG_SOC)
@@ -715,11 +577,11 @@
 
 	/* fill in PRD (scatter/gather) table, if any */
 	MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
+
 	if (task->data_dir == DMA_FROM_DEVICE)
-		MVS_CHIP_DISP->dma_fix(mvi->bulk_buffer_dma,
+		MVS_CHIP_DISP->dma_fix(mvi, sas_port->phy_mask,
 				TRASH_BUCKET_SIZE, tei->n_elem, buf_prd);
-#endif
+
 	return 0;
 }
 
@@ -761,6 +623,9 @@
 	}
 	if (is_tmf)
 		flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
+	else
+		flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT);
+
 	hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
 	hdr->tags = cpu_to_le32(tag);
 	hdr->data_len = cpu_to_le32(task->total_xfer_len);
@@ -777,9 +642,6 @@
 
 	buf_tmp += MVS_SSP_CMD_SZ;
 	buf_tmp_dma += MVS_SSP_CMD_SZ;
-#if _MV_DUMP
-	slot->cmd_size = MVS_SSP_CMD_SZ;
-#endif
 
 	/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
 	buf_oaf = buf_tmp;
@@ -986,7 +848,6 @@
 	task->task_state_flags |= SAS_TASK_AT_INITIATOR;
 	spin_unlock(&task->task_state_lock);
 
-	mvs_hba_memory_dump(mvi, tag, task->task_proto);
 	mvi_dev->running_req++;
 	++(*pass);
 	mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
@@ -1189,9 +1050,9 @@
 	mvs_slot_free(mvi, slot_idx);
 }
 
-static void mvs_update_wideport(struct mvs_info *mvi, int i)
+static void mvs_update_wideport(struct mvs_info *mvi, int phy_no)
 {
-	struct mvs_phy *phy = &mvi->phy[i];
+	struct mvs_phy *phy = &mvi->phy[phy_no];
 	struct mvs_port *port = phy->port;
 	int j, no;
 
@@ -1246,18 +1107,17 @@
 		return NULL;
 
 	MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
-	s[3] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+	s[3] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
 
 	MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
-	s[2] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+	s[2] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
 
 	MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
-	s[1] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+	s[1] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
 
 	MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
-	s[0] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+	s[0] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
 
-	/* Workaround: take some ATAPI devices for ATA */
 	if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
 		s[1] = 0x00EB1401 | (*((u8 *)&s[1] + 3) & 0x10);
 
@@ -1269,6 +1129,13 @@
 	return irq_status & PHYEV_SIG_FIS;
 }
 
+static void mvs_sig_remove_timer(struct mvs_phy *phy)
+{
+	if (phy->timer.function)
+		del_timer(&phy->timer);
+	phy->timer.function = NULL;
+}
+
 void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
 {
 	struct mvs_phy *phy = &mvi->phy[i];
@@ -1291,6 +1158,7 @@
 		if (phy->phy_type & PORT_TYPE_SATA) {
 			phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
 			if (mvs_is_sig_fis_received(phy->irq_status)) {
+				mvs_sig_remove_timer(phy);
 				phy->phy_attached = 1;
 				phy->att_dev_sas_addr =
 					i + mvi->id * mvi->chip->n_phy;
@@ -1308,7 +1176,6 @@
 						tmp | PHYEV_SIG_FIS);
 				phy->phy_attached = 0;
 				phy->phy_type &= ~PORT_TYPE_SATA;
-				MVS_CHIP_DISP->phy_reset(mvi, i, 0);
 				goto out_done;
 			}
 		}	else if (phy->phy_type & PORT_TYPE_SAS
@@ -1334,9 +1201,9 @@
 		if (MVS_CHIP_DISP->phy_work_around)
 			MVS_CHIP_DISP->phy_work_around(mvi, i);
 	}
-	mv_dprintk("port %d attach dev info is %x\n",
+	mv_dprintk("phy %d attach dev info is %x\n",
 		i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
-	mv_dprintk("port %d attach sas addr is %llx\n",
+	mv_dprintk("phy %d attach sas addr is %llx\n",
 		i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
 out_done:
 	if (get_st)
@@ -1361,10 +1228,10 @@
 	}
 	hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
 	mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
-	if (sas_port->id >= mvi->chip->n_phy)
-		port = &mvi->port[sas_port->id - mvi->chip->n_phy];
+	if (i >= mvi->chip->n_phy)
+		port = &mvi->port[i - mvi->chip->n_phy];
 	else
-		port = &mvi->port[sas_port->id];
+		port = &mvi->port[i];
 	if (lock)
 		spin_lock_irqsave(&mvi->lock, flags);
 	port->port_attached = 1;
@@ -1393,7 +1260,7 @@
 			return;
 	}
 	list_for_each_entry(dev, &port->dev_list, dev_list_node)
-		mvs_do_release_task(phy->mvi, phy_no, NULL);
+		mvs_do_release_task(phy->mvi, phy_no, dev);
 
 }
 
@@ -1457,6 +1324,7 @@
 	mvi_device->dev_status = MVS_DEV_NORMAL;
 	mvi_device->dev_type = dev->dev_type;
 	mvi_device->mvi_info = mvi;
+	mvi_device->sas_device = dev;
 	if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
 		int phy_id;
 		u8 phy_num = parent_dev->ex_dev.num_phys;
@@ -1508,6 +1376,7 @@
 		mv_dprintk("found dev has gone.\n");
 	}
 	dev->lldd_dev = NULL;
+	mvi_dev->sas_device = NULL;
 
 	spin_unlock_irqrestore(&mvi->lock, flags);
 }
@@ -1555,7 +1424,6 @@
 	complete(&task->completion);
 }
 
-/* XXX */
 #define MVS_TASK_TIMEOUT 20
 static int mvs_exec_internal_tmf_task(struct domain_device *dev,
 			void *parameter, u32 para_len, struct mvs_tmf_task *tmf)
@@ -1588,7 +1456,7 @@
 		}
 
 		wait_for_completion(&task->completion);
-		res = -TMF_RESP_FUNC_FAILED;
+		res = TMF_RESP_FUNC_FAILED;
 		/* Even TMF timed out, return direct. */
 		if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
 			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
@@ -1638,11 +1506,10 @@
 				u8 *lun, struct mvs_tmf_task *tmf)
 {
 	struct sas_ssp_task ssp_task;
-	DECLARE_COMPLETION_ONSTACK(completion);
 	if (!(dev->tproto & SAS_PROTOCOL_SSP))
 		return TMF_RESP_FUNC_ESUPP;
 
-	strncpy((u8 *)&ssp_task.LUN, lun, 8);
+	memcpy(ssp_task.LUN, lun, 8);
 
 	return mvs_exec_internal_tmf_task(dev, &ssp_task,
 				sizeof(ssp_task), tmf);
@@ -1666,7 +1533,7 @@
 int mvs_lu_reset(struct domain_device *dev, u8 *lun)
 {
 	unsigned long flags;
-	int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
+	int rc = TMF_RESP_FUNC_FAILED;
 	struct mvs_tmf_task tmf_task;
 	struct mvs_device * mvi_dev = dev->lldd_dev;
 	struct mvs_info *mvi = mvi_dev->mvi_info;
@@ -1675,10 +1542,8 @@
 	mvi_dev->dev_status = MVS_DEV_EH;
 	rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
 	if (rc == TMF_RESP_FUNC_COMPLETE) {
-		num = mvs_find_dev_phyno(dev, phyno);
 		spin_lock_irqsave(&mvi->lock, flags);
-		for (i = 0; i < num; i++)
-			mvs_release_task(mvi, dev);
+		mvs_release_task(mvi, dev);
 		spin_unlock_irqrestore(&mvi->lock, flags);
 	}
 	/* If failed, fall-through I_T_Nexus reset */
@@ -1696,11 +1561,12 @@
 
 	if (mvi_dev->dev_status != MVS_DEV_EH)
 		return TMF_RESP_FUNC_COMPLETE;
+	else
+		mvi_dev->dev_status = MVS_DEV_NORMAL;
 	rc = mvs_debug_I_T_nexus_reset(dev);
 	mv_printk("%s for device[%x]:rc= %d\n",
 		__func__, mvi_dev->device_id, rc);
 
-	/* housekeeper */
 	spin_lock_irqsave(&mvi->lock, flags);
 	mvs_release_task(mvi, dev);
 	spin_unlock_irqrestore(&mvi->lock, flags);
@@ -1739,9 +1605,6 @@
 		case TMF_RESP_FUNC_FAILED:
 		case TMF_RESP_FUNC_COMPLETE:
 			break;
-		default:
-			rc = TMF_RESP_FUNC_COMPLETE;
-			break;
 		}
 	}
 	mv_printk("%s:rc= %d\n", __func__, rc);
@@ -1761,8 +1624,8 @@
 	u32 tag;
 
 	if (!mvi_dev) {
-		mv_printk("%s:%d TMF_RESP_FUNC_FAILED\n", __func__, __LINE__);
-		rc = TMF_RESP_FUNC_FAILED;
+		mv_printk("Device has removed\n");
+		return TMF_RESP_FUNC_FAILED;
 	}
 
 	mvi = mvi_dev->mvi_info;
@@ -1807,25 +1670,17 @@
 
 	} else if (task->task_proto & SAS_PROTOCOL_SATA ||
 		task->task_proto & SAS_PROTOCOL_STP) {
-		/* to do free register_set */
 		if (SATA_DEV == dev->dev_type) {
 			struct mvs_slot_info *slot = task->lldd_task;
-			struct task_status_struct *tstat;
 			u32 slot_idx = (u32)(slot - mvi->slot_info);
-			tstat = &task->task_status;
-			mv_dprintk(KERN_DEBUG "mv_abort_task() mvi=%p task=%p "
+			mv_dprintk("mvs_abort_task() mvi=%p task=%p "
 				   "slot=%p slot_idx=x%x\n",
 				   mvi, task, slot, slot_idx);
-			tstat->stat = SAS_ABORTED_TASK;
-			if (mvi_dev && mvi_dev->running_req)
-				mvi_dev->running_req--;
-			if (sas_protocol_ata(task->task_proto))
-				mvs_free_reg_set(mvi, mvi_dev);
+			mvs_tmf_timedout((unsigned long)task);
 			mvs_slot_task_free(mvi, task, slot, slot_idx);
-			return -1;
+			rc = TMF_RESP_FUNC_COMPLETE;
+			goto out;
 		}
-	} else {
-		/* SMP */
 
 	}
 out:
@@ -1891,12 +1746,63 @@
 	return stat;
 }
 
+void mvs_set_sense(u8 *buffer, int len, int d_sense,
+		int key, int asc, int ascq)
+{
+	memset(buffer, 0, len);
+
+	if (d_sense) {
+		/* Descriptor format */
+		if (len < 4) {
+			mv_printk("Length %d of sense buffer too small to "
+				"fit sense %x:%x:%x", len, key, asc, ascq);
+		}
+
+		buffer[0] = 0x72;		/* Response Code	*/
+		if (len > 1)
+			buffer[1] = key;	/* Sense Key */
+		if (len > 2)
+			buffer[2] = asc;	/* ASC	*/
+		if (len > 3)
+			buffer[3] = ascq;	/* ASCQ	*/
+	} else {
+		if (len < 14) {
+			mv_printk("Length %d of sense buffer too small to "
+				"fit sense %x:%x:%x", len, key, asc, ascq);
+		}
+
+		buffer[0] = 0x70;		/* Response Code	*/
+		if (len > 2)
+			buffer[2] = key;	/* Sense Key */
+		if (len > 7)
+			buffer[7] = 0x0a;	/* Additional Sense Length */
+		if (len > 12)
+			buffer[12] = asc;	/* ASC */
+		if (len > 13)
+			buffer[13] = ascq; /* ASCQ */
+	}
+
+	return;
+}
+
+void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu,
+				u8 key, u8 asc, u8 asc_q)
+{
+	iu->datapres = 2;
+	iu->response_data_len = 0;
+	iu->sense_data_len = 17;
+	iu->status = 02;
+	mvs_set_sense(iu->sense_data, 17, 0,
+			key, asc, asc_q);
+}
+
 static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
 			 u32 slot_idx)
 {
 	struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
 	int stat;
-	u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
+	u32 err_dw0 = le32_to_cpu(*(u32 *)slot->response);
+	u32 err_dw1 = le32_to_cpu(*((u32 *)slot->response + 1));
 	u32 tfs = 0;
 	enum mvs_port_type type = PORT_TYPE_SAS;
 
@@ -1908,8 +1814,19 @@
 	stat = SAM_STAT_CHECK_CONDITION;
 	switch (task->task_proto) {
 	case SAS_PROTOCOL_SSP:
+	{
 		stat = SAS_ABORTED_TASK;
+		if ((err_dw0 & NO_DEST) || err_dw1 & bit(31)) {
+			struct ssp_response_iu *iu = slot->response +
+				sizeof(struct mvs_err_info);
+			mvs_fill_ssp_resp_iu(iu, NOT_READY, 0x04, 01);
+			sas_ssp_task_response(mvi->dev, task, iu);
+			stat = SAM_STAT_CHECK_CONDITION;
+		}
+		if (err_dw1 & bit(31))
+			mv_printk("reuse same slot, retry command.\n");
 		break;
+	}
 	case SAS_PROTOCOL_SMP:
 		stat = SAM_STAT_CHECK_CONDITION;
 		break;
@@ -1918,10 +1835,8 @@
 	case SAS_PROTOCOL_STP:
 	case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
 	{
-		if (err_dw0 == 0x80400002)
-			mv_printk("find reserved error, why?\n");
-
 		task->ata_task.use_ncq = 0;
+		stat = SAS_PROTO_RESPONSE;
 		mvs_sata_done(mvi, task, slot_idx, err_dw0);
 	}
 		break;
@@ -1945,8 +1860,6 @@
 	void *to;
 	enum exec_status sts;
 
-	if (mvi->exp_req)
-		mvi->exp_req--;
 	if (unlikely(!task || !task->lldd_task || !task->dev))
 		return -1;
 
@@ -1954,8 +1867,6 @@
 	dev = task->dev;
 	mvi_dev = dev->lldd_dev;
 
-	mvs_hba_cq_dump(mvi);
-
 	spin_lock(&task->task_state_lock);
 	task->task_state_flags &=
 		~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
@@ -1978,6 +1889,7 @@
 		return -1;
 	}
 
+	/* when no device attaching, go ahead and complete by error handling*/
 	if (unlikely(!mvi_dev || flags)) {
 		if (!mvi_dev)
 			mv_dprintk("port has not device.\n");
@@ -1987,6 +1899,9 @@
 
 	/* error info record present */
 	if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
+		mv_dprintk("port %d slot %d rx_desc %X has error info"
+			"%016llX.\n", slot->port->sas_port.id, slot_idx,
+			 rx_desc, (u64)(*(u64 *)slot->response));
 		tstat->stat = mvs_slot_err(mvi, task, slot_idx);
 		tstat->resp = SAS_TASK_COMPLETE;
 		goto out;
@@ -2048,8 +1963,7 @@
 	spin_unlock(&mvi->lock);
 	if (task->task_done)
 		task->task_done(task);
-	else
-		mv_dprintk("why has not task_done.\n");
+
 	spin_lock(&mvi->lock);
 
 	return sts;
@@ -2092,7 +2006,6 @@
 		      struct domain_device *dev)
 {
 	int i, phyno[WIDE_PORT_MAX_PHY], num;
-	/* housekeeper */
 	num = mvs_find_dev_phyno(dev, phyno);
 	for (i = 0; i < num; i++)
 		mvs_do_release_task(mvi, phyno[i], dev);
@@ -2111,13 +2024,13 @@
 	struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
 	struct mvs_info *mvi = mwq->mvi;
 	unsigned long flags;
+	u32 phy_no = (unsigned long) mwq->data;
+	struct sas_ha_struct *sas_ha = mvi->sas;
+	struct mvs_phy *phy = &mvi->phy[phy_no];
+	struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
 	spin_lock_irqsave(&mvi->lock, flags);
 	if (mwq->handler & PHY_PLUG_EVENT) {
-		u32 phy_no = (unsigned long) mwq->data;
-		struct sas_ha_struct *sas_ha = mvi->sas;
-		struct mvs_phy *phy = &mvi->phy[phy_no];
-		struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
 		if (phy->phy_event & PHY_PLUG_OUT) {
 			u32 tmp;
@@ -2139,6 +2052,11 @@
 				mv_dprintk("phy%d Attached Device\n", phy_no);
 			}
 		}
+	} else if (mwq->handler & EXP_BRCT_CHG) {
+		phy->phy_event &= ~EXP_BRCT_CHG;
+		sas_ha->notify_port_event(sas_phy,
+				PORTE_BROADCAST_RCVD);
+		mv_dprintk("phy%d Got Broadcast Change\n", phy_no);
 	}
 	list_del(&mwq->entry);
 	spin_unlock_irqrestore(&mvi->lock, flags);
@@ -2174,29 +2092,21 @@
 		if (&mvi->phy[phy_no] == phy) {
 			mv_dprintk("Get signature time out, reset phy %d\n",
 				phy_no+mvi->id*mvi->chip->n_phy);
-			MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
+			MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_HARD_RESET);
 		}
 	}
 }
 
-static void mvs_sig_remove_timer(struct mvs_phy *phy)
-{
-	if (phy->timer.function)
-		del_timer(&phy->timer);
-	phy->timer.function = NULL;
-}
-
 void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
 {
 	u32 tmp;
-	struct sas_ha_struct *sas_ha = mvi->sas;
 	struct mvs_phy *phy = &mvi->phy[phy_no];
-	struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
 	phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
-	mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
+	MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
+	mv_dprintk("phy %d ctrl sts=0x%08X.\n", phy_no+mvi->id*mvi->chip->n_phy,
 		MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
-	mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy,
+	mv_dprintk("phy %d irq sts = 0x%08X\n", phy_no+mvi->id*mvi->chip->n_phy,
 		phy->irq_status);
 
 	/*
@@ -2205,11 +2115,12 @@
 	*/
 
 	if (phy->irq_status & PHYEV_DCDR_ERR) {
-		mv_dprintk("port %d STP decoding error.\n",
+		mv_dprintk("phy %d STP decoding error.\n",
 		phy_no + mvi->id*mvi->chip->n_phy);
 	}
 
 	if (phy->irq_status & PHYEV_POOF) {
+		mdelay(500);
 		if (!(phy->phy_event & PHY_PLUG_OUT)) {
 			int dev_sata = phy->phy_type & PORT_TYPE_SATA;
 			int ready;
@@ -2220,17 +2131,13 @@
 				(void *)(unsigned long)phy_no,
 				PHY_PLUG_EVENT);
 			ready = mvs_is_phy_ready(mvi, phy_no);
-			if (!ready)
-				mv_dprintk("phy%d Unplug Notice\n",
-					phy_no +
-					mvi->id * mvi->chip->n_phy);
 			if (ready || dev_sata) {
 				if (MVS_CHIP_DISP->stp_reset)
 					MVS_CHIP_DISP->stp_reset(mvi,
 							phy_no);
 				else
 					MVS_CHIP_DISP->phy_reset(mvi,
-							phy_no, 0);
+							phy_no, MVS_SOFT_RESET);
 				return;
 			}
 		}
@@ -2243,13 +2150,12 @@
 		if (phy->timer.function == NULL) {
 			phy->timer.data = (unsigned long)phy;
 			phy->timer.function = mvs_sig_time_out;
-			phy->timer.expires = jiffies + 10*HZ;
+			phy->timer.expires = jiffies + 5*HZ;
 			add_timer(&phy->timer);
 		}
 	}
 	if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
 		phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
-		mvs_sig_remove_timer(phy);
 		mv_dprintk("notify plug in on phy[%d]\n", phy_no);
 		if (phy->phy_status) {
 			mdelay(10);
@@ -2263,14 +2169,14 @@
 			}
 			mvs_update_phyinfo(mvi, phy_no, 0);
 			if (phy->phy_type & PORT_TYPE_SAS) {
-				MVS_CHIP_DISP->phy_reset(mvi, phy_no, 2);
+				MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_PHY_TUNE);
 				mdelay(10);
 			}
 
 			mvs_bytes_dmaed(mvi, phy_no);
 			/* whether driver is going to handle hot plug */
 			if (phy->phy_event & PHY_PLUG_OUT) {
-				mvs_port_notify_formed(sas_phy, 0);
+				mvs_port_notify_formed(&phy->sas_phy, 0);
 				phy->phy_event &= ~PHY_PLUG_OUT;
 			}
 		} else {
@@ -2278,13 +2184,11 @@
 				phy_no + mvi->id*mvi->chip->n_phy);
 		}
 	} else if (phy->irq_status & PHYEV_BROAD_CH) {
-		mv_dprintk("port %d broadcast change.\n",
+		mv_dprintk("phy %d broadcast change.\n",
 			phy_no + mvi->id*mvi->chip->n_phy);
-		/* exception for Samsung disk drive*/
-		mdelay(1000);
-		sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+		mvs_handle_event(mvi, (void *)(unsigned long)phy_no,
+				EXP_BRCT_CHG);
 	}
-	MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
 }
 
 int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 1367d8b..44d7885 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -48,12 +48,8 @@
 
 #define DRV_NAME		"mvsas"
 #define DRV_VERSION		"0.8.2"
-#define _MV_DUMP		0
 #define MVS_ID_NOT_MAPPED	0x7f
-/* #define DISABLE_HOTPLUG_DMA_FIX */
-// #define MAX_EXP_RUNNING_REQ	2
 #define WIDE_PORT_MAX_PHY		4
-#define	MV_DISABLE_NCQ	0
 #define mv_printk(fmt, arg ...)	\
 	printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
 #ifdef MV_DEBUG
@@ -64,6 +60,7 @@
 #endif
 #define MV_MAX_U32			0xffffffff
 
+extern int interrupt_coalescing;
 extern struct mvs_tgt_initiator mvs_tgt;
 extern struct mvs_info *tgt_mvi;
 extern const struct mvs_dispatch mvs_64xx_dispatch;
@@ -99,6 +96,11 @@
 	MVS_DEV_EH	= 0x1,
 };
 
+enum dev_reset {
+	MVS_SOFT_RESET	= 0,
+	MVS_HARD_RESET	= 1,
+	MVS_PHY_TUNE	= 2,
+};
 
 struct mvs_info;
 
@@ -130,7 +132,6 @@
 	u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
 	void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);
 
-	void (*get_sas_addr)(void *buf, u32 buflen);
 	void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
 	void (*clear_srs_irq)(struct mvs_info *mvi, u8 reg_set, u8 clear_all);
 	void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
@@ -167,9 +168,10 @@
 						);
 	int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
 	int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
-	void (*dma_fix)(dma_addr_t buf_dma, int buf_len, int from, void *prd);
-#endif
+	void (*dma_fix)(struct mvs_info *mvi, u32 phy_mask,
+				int buf_len, int from, void *prd);
+	void (*tune_interrupt)(struct mvs_info *mvi, u32 time);
+	void (*non_spec_ncq_error)(struct mvs_info *mvi);
 
 };
 
@@ -179,9 +181,11 @@
 	u32 		fis_offs;
 	u32 		fis_count;
 	u32 		srs_sz;
+	u32		sg_width;
 	u32 		slot_width;
 	const struct mvs_dispatch *dispatch;
 };
+#define MVS_MAX_SG		(1U << mvi->chip->sg_width)
 #define MVS_CHIP_SLOT_SZ	(1U << mvi->chip->slot_width)
 #define MVS_RX_FISL_SZ		\
 	(mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
@@ -249,6 +253,73 @@
 	u16 reserved;
 };
 
+/* Generate  PHY tunning parameters */
+struct phy_tuning {
+	/* 1 bit,  transmitter emphasis enable	*/
+	u8	trans_emp_en:1;
+	/* 4 bits, transmitter emphasis amplitude */
+	u8	trans_emp_amp:4;
+	/* 3 bits, reserved space */
+	u8	Reserved_2bit_1:3;
+	/* 5 bits, transmitter amplitude */
+	u8	trans_amp:5;
+	/* 2 bits, transmitter amplitude adjust */
+	u8	trans_amp_adj:2;
+	/* 1 bit, reserved space */
+	u8	resv_2bit_2:1;
+	/* 2 bytes, reserved space */
+	u8	reserved[2];
+};
+
+struct ffe_control {
+	/* 4 bits,  FFE Capacitor Select  (value range 0~F)  */
+	u8 ffe_cap_sel:4;
+	/* 3 bits,  FFE Resistor Select (value range 0~7) */
+	u8 ffe_rss_sel:3;
+	/* 1 bit reserve*/
+	u8 reserved:1;
+};
+
+/*
+ * HBA_Info_Page is saved in Flash/NVRAM, total 256 bytes.
+ * The data area is valid only Signature="MRVL".
+ * If any member fills with 0xFF, the member is invalid.
+ */
+struct hba_info_page {
+	/* Dword 0 */
+	/* 4 bytes, structure signature,should be "MRVL" at first initial */
+	u8 signature[4];
+
+	/* Dword 1-13 */
+	u32 reserved1[13];
+
+	/* Dword 14-29 */
+	/* 64 bytes, SAS address for each port */
+	u64 sas_addr[8];
+
+	/* Dword 30-31 */
+	/* 8 bytes for vanir 8 port PHY FFE seeting
+	 * BIT 0~3 : FFE Capacitor select(value range 0~F)
+	 * BIT 4~6 : FFE Resistor select(value range 0~7)
+	 * BIT 7: reserve.
+	 */
+
+	struct ffe_control  ffe_ctl[8];
+	/* Dword 32 -43 */
+	u32 reserved2[12];
+
+	/* Dword 44-45 */
+	/* 8 bytes,  0:  1.5G, 1: 3.0G, should be 0x01 at first initial */
+	u8 phy_rate[8];
+
+	/* Dword 46-53 */
+	/* 32 bytes, PHY tuning parameters for each PHY*/
+	struct phy_tuning   phy_tuning[8];
+
+	/* Dword 54-63 */
+	u32 reserved3[10];
+};	/* total 256 bytes */
+
 struct mvs_slot_info {
 	struct list_head entry;
 	union {
@@ -264,9 +335,6 @@
 	 */
 	void *buf;
 	dma_addr_t buf_dma;
-#if _MV_DUMP
-	u32 cmd_size;
-#endif
 	void *response;
 	struct mvs_port *port;
 	struct mvs_device	*device;
@@ -320,12 +388,10 @@
 	const struct mvs_chip_info *chip;
 
 	int tags_num;
-	DECLARE_BITMAP(tags, MVS_SLOTS);
+	unsigned long *tags;
 	/* further per-slot information */
 	struct mvs_phy phy[MVS_MAX_PHYS];
 	struct mvs_port port[MVS_MAX_PHYS];
-	u32 irq;
-	u32 exp_req;
 	u32 id;
 	u64 sata_reg_set;
 	struct list_head *hba_list;
@@ -337,12 +403,13 @@
 	u32 flashsectSize;
 
 	void *addon;
+	struct hba_info_page hba_info_param;
 	struct mvs_device	devices[MVS_MAX_DEVICES];
-#ifndef DISABLE_HOTPLUG_DMA_FIX
 	void *bulk_buffer;
 	dma_addr_t bulk_buffer_dma;
+	void *bulk_buffer1;
+	dma_addr_t bulk_buffer_dma1;
 #define TRASH_BUCKET_SIZE    	0x20000
-#endif
 	void *dma_pool;
 	struct mvs_slot_info slot_info[0];
 };
@@ -350,8 +417,10 @@
 struct mvs_prv_info{
 	u8 n_host;
 	u8 n_phy;
-	u16 reserve;
+	u8 scan_finished;
+	u8 reserve;
 	struct mvs_info *mvi[2];
+	struct tasklet_struct mv_tasklet;
 };
 
 struct mvs_wq {
@@ -415,6 +484,6 @@
 void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
 void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
 int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
-void mvs_hexdump(u32 size, u8 *data, u32 baseaddr);
+struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set);
 #endif
 
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index aa05e66..b97c8ab 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -54,7 +54,7 @@
 #include <scsi/libsas.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/sas_ata.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "pm8001_defs.h"
 
 #define DRV_NAME		"pm8001"
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index fca6a89..d079f9a 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -3871,6 +3871,9 @@
 			pmcraid_err("couldn't build passthrough ioadls\n");
 			goto out_free_buffer;
 		}
+	} else if (request_size < 0) {
+		rc = -EINVAL;
+		goto out_free_buffer;
 	}
 
 	/* If data is being written into the device, copy the data from user
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 532313e..7836eb0 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -42,8 +42,8 @@
 	int reading;
 
 	if (IS_QLA82XX(ha)) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-			"Firmware dump not supported for ISP82xx\n"));
+		ql_dbg(ql_dbg_user, vha, 0x705b,
+		    "Firmware dump not supported for ISP82xx\n");
 		return count;
 	}
 
@@ -56,7 +56,7 @@
 		if (!ha->fw_dump_reading)
 			break;
 
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0x705d,
 		    "Firmware dump cleared on (%ld).\n", vha->host_no);
 
 		ha->fw_dump_reading = 0;
@@ -66,7 +66,7 @@
 		if (ha->fw_dumped && !ha->fw_dump_reading) {
 			ha->fw_dump_reading = 1;
 
-			qla_printk(KERN_INFO, ha,
+			ql_log(ql_log_info, vha, 0x705e,
 			    "Raw firmware dump ready for read on (%ld).\n",
 			    vha->host_no);
 		}
@@ -148,7 +148,7 @@
 	}
 
 	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x705f,
 		    "HBA not online, failing NVRAM update.\n");
 		return -EAGAIN;
 	}
@@ -158,6 +158,8 @@
 	ha->isp_ops->read_nvram(vha, (uint8_t *)ha->nvram, ha->nvram_base,
 	    count);
 
+	ql_dbg(ql_dbg_user, vha, 0x7060,
+	    "Setting ISP_ABORT_NEEDED\n");
 	/* NVRAM settings take effect immediately. */
 	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 	qla2xxx_wake_dpc(vha);
@@ -255,9 +257,9 @@
 
 		ha->optrom_state = QLA_SWAITING;
 
-		DEBUG2(qla_printk(KERN_INFO, ha,
+		ql_dbg(ql_dbg_user, vha, 0x7061,
 		    "Freeing flash region allocation -- 0x%x bytes.\n",
-		    ha->optrom_region_size));
+		    ha->optrom_region_size);
 
 		vfree(ha->optrom_buffer);
 		ha->optrom_buffer = NULL;
@@ -273,7 +275,7 @@
 		ha->optrom_state = QLA_SREADING;
 		ha->optrom_buffer = vmalloc(ha->optrom_region_size);
 		if (ha->optrom_buffer == NULL) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x7062,
 			    "Unable to allocate memory for optrom retrieval "
 			    "(%x).\n", ha->optrom_region_size);
 
@@ -282,14 +284,14 @@
 		}
 
 		if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
-			qla_printk(KERN_WARNING, ha,
-				"HBA not online, failing NVRAM update.\n");
+			ql_log(ql_log_warn, vha, 0x7063,
+			    "HBA not online, failing NVRAM update.\n");
 			return -EAGAIN;
 		}
 
-		DEBUG2(qla_printk(KERN_INFO, ha,
+		ql_dbg(ql_dbg_user, vha, 0x7064,
 		    "Reading flash region -- 0x%x/0x%x.\n",
-		    ha->optrom_region_start, ha->optrom_region_size));
+		    ha->optrom_region_start, ha->optrom_region_size);
 
 		memset(ha->optrom_buffer, 0, ha->optrom_region_size);
 		ha->isp_ops->read_optrom(vha, ha->optrom_buffer,
@@ -328,7 +330,7 @@
 		else if (IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha))
 			valid = 1;
 		if (!valid) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x7065,
 			    "Invalid start region 0x%x/0x%x.\n", start, size);
 			return -EINVAL;
 		}
@@ -340,17 +342,17 @@
 		ha->optrom_state = QLA_SWRITING;
 		ha->optrom_buffer = vmalloc(ha->optrom_region_size);
 		if (ha->optrom_buffer == NULL) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x7066,
 			    "Unable to allocate memory for optrom update "
-			    "(%x).\n", ha->optrom_region_size);
+			    "(%x)\n", ha->optrom_region_size);
 
 			ha->optrom_state = QLA_SWAITING;
 			return count;
 		}
 
-		DEBUG2(qla_printk(KERN_INFO, ha,
+		ql_dbg(ql_dbg_user, vha, 0x7067,
 		    "Staging flash region write -- 0x%x/0x%x.\n",
-		    ha->optrom_region_start, ha->optrom_region_size));
+		    ha->optrom_region_start, ha->optrom_region_size);
 
 		memset(ha->optrom_buffer, 0, ha->optrom_region_size);
 		break;
@@ -359,14 +361,14 @@
 			break;
 
 		if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x7068,
 			    "HBA not online, failing flash update.\n");
 			return -EAGAIN;
 		}
 
-		DEBUG2(qla_printk(KERN_INFO, ha,
+		ql_dbg(ql_dbg_user, vha, 0x7069,
 		    "Writing flash region -- 0x%x/0x%x.\n",
-		    ha->optrom_region_start, ha->optrom_region_size));
+		    ha->optrom_region_start, ha->optrom_region_size);
 
 		ha->isp_ops->write_optrom(vha, ha->optrom_buffer,
 		    ha->optrom_region_start, ha->optrom_region_size);
@@ -425,7 +427,7 @@
 		return 0;
 
 	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x706a,
 		    "HBA not online, failing VPD update.\n");
 		return -EAGAIN;
 	}
@@ -440,7 +442,7 @@
 
 	tmp_data = vmalloc(256);
 	if (!tmp_data) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x706b,
 		    "Unable to allocate memory for VPD information update.\n");
 		goto done;
 	}
@@ -480,7 +482,7 @@
 	ha->sfp_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
 	    &ha->sfp_data_dma);
 	if (!ha->sfp_data) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x706c,
 		    "Unable to allocate memory for SFP read-data.\n");
 		return 0;
 	}
@@ -499,9 +501,10 @@
 		rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data,
 		    addr, offset, SFP_BLOCK_SIZE, 0);
 		if (rval != QLA_SUCCESS) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x706d,
 			    "Unable to read SFP data (%x/%x/%x).\n", rval,
 			    addr, offset);
+
 			count = 0;
 			break;
 		}
@@ -538,8 +541,8 @@
 	type = simple_strtol(buf, NULL, 10);
 	switch (type) {
 	case 0x2025c:
-		qla_printk(KERN_INFO, ha,
-		    "Issuing ISP reset on (%ld).\n", vha->host_no);
+		ql_log(ql_log_info, vha, 0x706e,
+		    "Issuing ISP reset.\n");
 
 		scsi_block_requests(vha->host);
 		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -551,8 +554,8 @@
 		if (!IS_QLA81XX(ha))
 			break;
 
-		qla_printk(KERN_INFO, ha,
-		    "Issuing MPI reset on (%ld).\n", vha->host_no);
+		ql_log(ql_log_info, vha, 0x706f,
+		    "Issuing MPI reset.\n");
 
 		/* Make sure FC side is not in reset */
 		qla2x00_wait_for_hba_online(vha);
@@ -560,20 +563,19 @@
 		/* Issue MPI reset */
 		scsi_block_requests(vha->host);
 		if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS)
-			qla_printk(KERN_WARNING, ha,
-			    "MPI reset failed on (%ld).\n", vha->host_no);
+			ql_log(ql_log_warn, vha, 0x7070,
+			    "MPI reset failed.\n");
 		scsi_unblock_requests(vha->host);
 		break;
 	case 0x2025e:
 		if (!IS_QLA82XX(ha) || vha != base_vha) {
-			qla_printk(KERN_INFO, ha,
-			    "FCoE ctx reset not supported for host%ld.\n",
-			    vha->host_no);
+			ql_log(ql_log_info, vha, 0x7071,
+			    "FCoE ctx reset no supported.\n");
 			return count;
 		}
 
-		qla_printk(KERN_INFO, ha,
-		    "Issuing FCoE CTX reset on host%ld.\n", vha->host_no);
+		ql_log(ql_log_info, vha, 0x7072,
+		    "Issuing FCoE ctx reset.\n");
 		set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
 		qla2x00_wait_for_fcoe_ctx_reset(vha);
@@ -611,8 +613,8 @@
 		ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
 		    &ha->edc_data_dma);
 		if (!ha->edc_data) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "Unable to allocate memory for EDC write.\n"));
+			ql_log(ql_log_warn, vha, 0x7073,
+			    "Unable to allocate memory for EDC write.\n");
 			return 0;
 		}
 	}
@@ -631,9 +633,9 @@
 	rval = qla2x00_write_sfp(vha, ha->edc_data_dma, ha->edc_data,
 	    dev, adr, len, opt);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "Unable to write EDC (%x) %02x:%02x:%04x:%02x:%02x.\n",
-		    rval, dev, adr, opt, len, buf[8]));
+		ql_log(ql_log_warn, vha, 0x7074,
+		    "Unable to write EDC (%x) %02x:%04x:%02x:%02x\n",
+		    rval, dev, adr, opt, len, buf[8]);
 		return 0;
 	}
 
@@ -669,8 +671,8 @@
 		ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
 		    &ha->edc_data_dma);
 		if (!ha->edc_data) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "Unable to allocate memory for EDC status.\n"));
+			ql_log(ql_log_warn, vha, 0x708c,
+			    "Unable to allocate memory for EDC status.\n");
 			return 0;
 		}
 	}
@@ -688,9 +690,9 @@
 	rval = qla2x00_read_sfp(vha, ha->edc_data_dma, ha->edc_data,
 			dev, adr, len, opt);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "Unable to write EDC status (%x) %02x:%02x:%04x:%02x.\n",
-		    rval, dev, adr, opt, len));
+		ql_log(ql_log_info, vha, 0x7075,
+		    "Unable to write EDC status (%x) %02x:%04x:%02x.\n",
+		    rval, dev, adr, opt, len);
 		return 0;
 	}
 
@@ -749,7 +751,7 @@
 	ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
 	    &ha->xgmac_data_dma, GFP_KERNEL);
 	if (!ha->xgmac_data) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7076,
 		    "Unable to allocate memory for XGMAC read-data.\n");
 		return 0;
 	}
@@ -761,7 +763,7 @@
 	rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma,
 	    XGMAC_DATA_SIZE, &actual_size);
 	if (rval != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7077,
 		    "Unable to read XGMAC data (%x).\n", rval);
 		count = 0;
 	}
@@ -801,7 +803,7 @@
 	ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
 	    &ha->dcbx_tlv_dma, GFP_KERNEL);
 	if (!ha->dcbx_tlv) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7078,
 		    "Unable to allocate memory for DCBX TLV read-data.\n");
 		return 0;
 	}
@@ -813,8 +815,8 @@
 	rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma,
 	    DCBX_TLV_DATA_SIZE);
 	if (rval != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to read DCBX TLV data (%x).\n", rval);
+		ql_log(ql_log_warn, vha, 0x7079,
+		    "Unable to read DCBX TLV (%x).\n", rval);
 		count = 0;
 	}
 
@@ -869,9 +871,13 @@
 		ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
 		    iter->attr);
 		if (ret)
-			qla_printk(KERN_INFO, vha->hw,
-			    "Unable to create sysfs %s binary attribute "
-			    "(%d).\n", iter->name, ret);
+			ql_log(ql_log_warn, vha, 0x00f3,
+			    "Unable to create sysfs %s binary attribute (%d).\n",
+			    iter->name, ret);
+		else
+			ql_dbg(ql_dbg_init, vha, 0x00f4,
+			    "Successfully created sysfs %s binary attribure.\n",
+			    iter->name);
 	}
 }
 
@@ -1126,7 +1132,7 @@
 		return -EPERM;
 
 	if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x707a,
 		    "Abort ISP active -- ignoring beacon request.\n");
 		return -EBUSY;
 	}
@@ -1322,9 +1328,8 @@
 	temp = frac = 0;
 	if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
 	    test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): isp reset in progress.\n",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x707b,
+		    "ISP reset active.\n");
 	else if (!vha->hw->flags.eeh_busy)
 		rval = qla2x00_get_thermal_temp(vha, &temp, &frac);
 	if (rval != QLA_SUCCESS)
@@ -1343,8 +1348,8 @@
 
 	if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
 		test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
-		DEBUG2_3_11(printk("%s(%ld): isp reset in progress.\n",
-			__func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x707c,
+		    "ISP reset active.\n");
 	else if (!vha->hw->flags.eeh_busy)
 		rval = qla2x00_get_firmware_state(vha, state);
 	if (rval != QLA_SUCCESS)
@@ -1645,8 +1650,8 @@
 
 	stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
 	if (stats == NULL) {
-		DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n",
-		    __func__, base_vha->host_no));
+		ql_log(ql_log_warn, vha, 0x707d,
+		    "Failed to allocate memory for stats.\n");
 		goto done;
 	}
 	memset(stats, 0, DMA_POOL_SIZE);
@@ -1746,15 +1751,14 @@
 
 	ret = qla24xx_vport_create_req_sanity_check(fc_vport);
 	if (ret) {
-		DEBUG15(printk("qla24xx_vport_create_req_sanity_check failed, "
-		    "status %x\n", ret));
+		ql_log(ql_log_warn, vha, 0x707e,
+		    "Vport sanity check failed, status %x\n", ret);
 		return (ret);
 	}
 
 	vha = qla24xx_create_vhost(fc_vport);
 	if (vha == NULL) {
-		DEBUG15(printk ("qla24xx_create_vhost failed, vha = %p\n",
-		    vha));
+		ql_log(ql_log_warn, vha, 0x707f, "Vport create host failed.\n");
 		return FC_VPORT_FAILED;
 	}
 	if (disable) {
@@ -1764,8 +1768,8 @@
 		atomic_set(&vha->vp_state, VP_FAILED);
 
 	/* ready to create vport */
-	qla_printk(KERN_INFO, vha->hw, "VP entry id %d assigned.\n",
-							vha->vp_idx);
+	ql_log(ql_log_info, vha, 0x7080,
+	    "VP entry id %d assigned.\n", vha->vp_idx);
 
 	/* initialized vport states */
 	atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1775,8 +1779,8 @@
 	if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
 	    atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
 		/* Don't retry or attempt login of this virtual port */
-		DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n",
-		    base_vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7081,
+		    "Vport loop state is not UP.\n");
 		atomic_set(&vha->loop_state, LOOP_DEAD);
 		if (!disable)
 			fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
@@ -1785,9 +1789,8 @@
 	if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) {
 		if (ha->fw_attributes & BIT_4) {
 			vha->flags.difdix_supported = 1;
-			DEBUG18(qla_printk(KERN_INFO, ha,
-			    "Registering for DIF/DIX type 1 and 3"
-			    " protection.\n"));
+			ql_dbg(ql_dbg_user, vha, 0x7082,
+			    "Registered for DIF/DIX type 1 and 3 protection.\n");
 			scsi_host_set_prot(vha->host,
 			    SHOST_DIF_TYPE1_PROTECTION
 			    | SHOST_DIF_TYPE2_PROTECTION
@@ -1802,8 +1805,8 @@
 
 	if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
 				   &ha->pdev->dev)) {
-		DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
-			vha->host_no, vha->vp_idx));
+		ql_dbg(ql_dbg_user, vha, 0x7083,
+		    "scsi_add_host failure for VP[%d].\n", vha->vp_idx);
 		goto vport_create_failed_2;
 	}
 
@@ -1820,6 +1823,10 @@
 
 	if (ha->flags.cpu_affinity_enabled) {
 		req = ha->req_q_map[1];
+		ql_dbg(ql_dbg_multiq, vha, 0xc000,
+		    "Request queue %p attached with "
+		    "VP[%d], cpu affinity =%d\n",
+		    req, vha->vp_idx, ha->flags.cpu_affinity_enabled);
 		goto vport_queue;
 	} else if (ql2xmaxqueues == 1 || !ha->npiv_info)
 		goto vport_queue;
@@ -1836,13 +1843,16 @@
 		ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
 			qos);
 		if (!ret)
-			qla_printk(KERN_WARNING, ha,
-			"Can't create request queue for vp_idx:%d\n",
-			vha->vp_idx);
+			ql_log(ql_log_warn, vha, 0x7084,
+			    "Can't create request queue for VP[%d]\n",
+			    vha->vp_idx);
 		else {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			"Request Que:%d (QoS: %d) created for vp_idx:%d\n",
-			ret, qos, vha->vp_idx));
+			ql_dbg(ql_dbg_multiq, vha, 0xc001,
+			    "Request Que:%d Q0s: %d) created for VP[%d]\n",
+			    ret, qos, vha->vp_idx);
+			ql_dbg(ql_dbg_user, vha, 0x7085,
+			    "Request Que:%d Q0s: %d) created for VP[%d]\n",
+			    ret, qos, vha->vp_idx);
 			req = ha->req_q_map[ret];
 		}
 	}
@@ -1882,12 +1892,13 @@
 
 	if (vha->timer_active) {
 		qla2x00_vp_stop_timer(vha);
-		DEBUG15(printk(KERN_INFO "scsi(%ld): timer for the vport[%d]"
-		" = %p has stopped\n", vha->host_no, vha->vp_idx, vha));
+		ql_dbg(ql_dbg_user, vha, 0x7086,
+		    "Timer for the VP[%d] has stopped\n", vha->vp_idx);
 	}
 
 	/* No pending activities shall be there on the vha now */
-	DEBUG(msleep(random32()%10));  /* Just to see if something falls on
+	if (ql2xextended_error_logging & ql_dbg_user)
+		msleep(random32()%10);  /* Just to see if something falls on
 					* the net we have placed below */
 
 	BUG_ON(atomic_read(&vha->vref_count));
@@ -1901,12 +1912,12 @@
 
 	if (vha->req->id && !ha->flags.cpu_affinity_enabled) {
 		if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
-			qla_printk(KERN_WARNING, ha,
-				"Queue delete failed.\n");
+			ql_log(ql_log_warn, vha, 0x7087,
+			    "Queue delete failed.\n");
 	}
 
 	scsi_host_put(vha->host);
-	qla_printk(KERN_INFO, ha, "vport %d deleted\n", id);
+	ql_log(ql_log_info, vha, 0x7088, "VP[%d] deleted.\n", id);
 	return 0;
 }
 
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 8c10e2c..07d1767 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -36,7 +36,8 @@
 }
 
 int
-qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag)
+qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha,
+	struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag)
 {
 	int i, ret, num_valid;
 	uint8_t *bcode;
@@ -51,18 +52,17 @@
 
 	if (bcode_val == 0xFFFFFFFF) {
 		/* No FCP Priority config data in flash */
-		DEBUG2(printk(KERN_INFO
-		    "%s: No FCP priority config data.\n",
-		    __func__));
+		ql_dbg(ql_dbg_user, vha, 0x7051,
+		    "No FCP Priority config data.\n");
 		return 0;
 	}
 
 	if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' ||
 			bcode[3] != 'S') {
 		/* Invalid FCP priority data header*/
-		DEBUG2(printk(KERN_ERR
-		    "%s: Invalid FCP Priority data header. bcode=0x%x\n",
-		    __func__, bcode_val));
+		ql_dbg(ql_dbg_user, vha, 0x7052,
+		    "Invalid FCP Priority data header. bcode=0x%x.\n",
+		    bcode_val);
 		return 0;
 	}
 	if (flag != 1)
@@ -77,15 +77,14 @@
 
 	if (num_valid == 0) {
 		/* No valid FCP priority data entries */
-		DEBUG2(printk(KERN_ERR
-		    "%s: No valid FCP Priority data entries.\n",
-		    __func__));
+		ql_dbg(ql_dbg_user, vha, 0x7053,
+		    "No valid FCP Priority data entries.\n");
 		ret = 0;
 	} else {
 		/* FCP priority data is valid */
-		DEBUG2(printk(KERN_INFO
-		    "%s: Valid FCP priority data. num entries = %d\n",
-		    __func__, num_valid));
+		ql_dbg(ql_dbg_user, vha, 0x7054,
+		    "Valid FCP priority data. num entries = %d.\n",
+		    num_valid);
 	}
 
 	return ret;
@@ -182,10 +181,9 @@
 		if (!ha->fcp_prio_cfg) {
 			ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
 			if (!ha->fcp_prio_cfg) {
-				qla_printk(KERN_WARNING, ha,
-					"Unable to allocate memory "
-					"for fcp prio config data (%x).\n",
-					FCP_PRIO_CFG_SIZE);
+				ql_log(ql_log_warn, vha, 0x7050,
+				    "Unable to allocate memory for fcp prio "
+				    "config data (%x).\n", FCP_PRIO_CFG_SIZE);
 				bsg_job->reply->result = (DID_ERROR << 16);
 				ret = -ENOMEM;
 				goto exit_fcp_prio_cfg;
@@ -198,9 +196,9 @@
 			FCP_PRIO_CFG_SIZE);
 
 		/* validate fcp priority data */
-		if (!qla24xx_fcp_prio_cfg_valid(
-			(struct qla_fcp_prio_cfg *)
-			ha->fcp_prio_cfg, 1)) {
+
+		if (!qla24xx_fcp_prio_cfg_valid(vha,
+		    (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) {
 			bsg_job->reply->result = (DID_ERROR << 16);
 			ret = -EINVAL;
 			/* If buffer was invalidatic int
@@ -256,9 +254,8 @@
 
 	/* pass through is supported only for ISP 4Gb or higher */
 	if (!IS_FWI2_CAPABLE(ha)) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "scsi(%ld):ELS passthru not supported for ISP23xx based "
-		    "adapters\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7001,
+		    "ELS passthru not supported for ISP23xx based adapters.\n");
 		rval = -EPERM;
 		goto done;
 	}
@@ -266,11 +263,11 @@
 	/*  Multiple SG's are not supported for ELS requests */
 	if (bsg_job->request_payload.sg_cnt > 1 ||
 		bsg_job->reply_payload.sg_cnt > 1) {
-		DEBUG2(printk(KERN_INFO
-			"multiple SG's are not supported for ELS requests"
-			" [request_sg_cnt: %x reply_sg_cnt: %x]\n",
-			bsg_job->request_payload.sg_cnt,
-			bsg_job->reply_payload.sg_cnt));
+		ql_dbg(ql_dbg_user, vha, 0x7002,
+		    "Multiple SG's are not suppored for ELS requests, "
+		    "request_sg_cnt=%x reply_sg_cnt=%x.\n",
+		    bsg_job->request_payload.sg_cnt,
+		    bsg_job->reply_payload.sg_cnt);
 		rval = -EPERM;
 		goto done;
 	}
@@ -281,9 +278,9 @@
 		 * if not perform fabric login
 		 */
 		if (qla2x00_fabric_login(vha, fcport, &nextlid)) {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			"failed to login port %06X for ELS passthru\n",
-			fcport->d_id.b24));
+			ql_dbg(ql_dbg_user, vha, 0x7003,
+			    "Failed to login port %06X for ELS passthru.\n",
+			    fcport->d_id.b24);
 			rval = -EIO;
 			goto done;
 		}
@@ -314,8 +311,7 @@
 	}
 
 	if (!vha->flags.online) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		"host not online\n"));
+		ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n");
 		rval = -EIO;
 		goto done;
 	}
@@ -337,12 +333,11 @@
 
 	if ((req_sg_cnt !=  bsg_job->request_payload.sg_cnt) ||
 		(rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
-		DEBUG2(printk(KERN_INFO
-			"dma mapping resulted in different sg counts \
-			[request_sg_cnt: %x dma_request_sg_cnt: %x\
-			reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
-			bsg_job->request_payload.sg_cnt, req_sg_cnt,
-			bsg_job->reply_payload.sg_cnt, rsp_sg_cnt));
+		ql_log(ql_log_warn, vha, 0x7008,
+		    "dma mapping resulted in different sg counts, "
+		    "request_sg_cnt: %x dma_request_sg_cnt:%x reply_sg_cnt:%x "
+		    "dma_reply_sg_cnt:%x.\n", bsg_job->request_payload.sg_cnt,
+		    req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt);
 		rval = -EAGAIN;
 		goto done_unmap_sg;
 	}
@@ -363,15 +358,16 @@
 		"bsg_els_rpt" : "bsg_els_hst");
 	els->u.bsg_job = bsg_job;
 
-	DEBUG2(qla_printk(KERN_INFO, ha,
-		"scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x "
-		"portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type,
-		bsg_job->request->rqst_data.h_els.command_code,
-		fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
-		fcport->d_id.b.al_pa));
+	ql_dbg(ql_dbg_user, vha, 0x700a,
+	    "bsg rqst type: %s els type: %x - loop-id=%x "
+	    "portid=%-2x%02x%02x.\n", type,
+	    bsg_job->request->rqst_data.h_els.command_code, fcport->loop_id,
+	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa);
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x700e,
+		    "qla2x00_start_sp failed = %d\n", rval);
 		kfree(sp->ctx);
 		mempool_free(sp, ha->srb_mempool);
 		rval = -EIO;
@@ -411,6 +407,8 @@
 		dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
 			bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
 	if (!req_sg_cnt) {
+		ql_log(ql_log_warn, vha, 0x700f,
+		    "dma_map_sg return %d for request\n", req_sg_cnt);
 		rval = -ENOMEM;
 		goto done;
 	}
@@ -418,24 +416,25 @@
 	rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
 		bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
 	if (!rsp_sg_cnt) {
+		ql_log(ql_log_warn, vha, 0x7010,
+		    "dma_map_sg return %d for reply\n", rsp_sg_cnt);
 		rval = -ENOMEM;
 		goto done;
 	}
 
 	if ((req_sg_cnt !=  bsg_job->request_payload.sg_cnt) ||
 	    (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "[request_sg_cnt: %x dma_request_sg_cnt: %x\
-		    reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
-		    bsg_job->request_payload.sg_cnt, req_sg_cnt,
-		    bsg_job->reply_payload.sg_cnt, rsp_sg_cnt));
+		ql_log(ql_log_warn, vha, 0x7011,
+		    "request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt:%x "
+		    "dma_reply_sg_cnt: %x\n", bsg_job->request_payload.sg_cnt,
+		    req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt);
 		rval = -EAGAIN;
 		goto done_unmap_sg;
 	}
 
 	if (!vha->flags.online) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-			"host not online\n"));
+		ql_log(ql_log_warn, vha, 0x7012,
+		    "Host is not online.\n");
 		rval = -EIO;
 		goto done_unmap_sg;
 	}
@@ -451,8 +450,8 @@
 		loop_id = vha->mgmt_svr_loop_id;
 		break;
 	default:
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "Unknown loop id: %x\n", loop_id));
+		ql_dbg(ql_dbg_user, vha, 0x7013,
+		    "Unknown loop id: %x.\n", loop_id);
 		rval = -EINVAL;
 		goto done_unmap_sg;
 	}
@@ -464,6 +463,8 @@
 	 */
 	fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
 	if (!fcport) {
+		ql_log(ql_log_warn, vha, 0x7014,
+		    "Failed to allocate fcport.\n");
 		rval = -ENOMEM;
 		goto done_unmap_sg;
 	}
@@ -479,6 +480,8 @@
 	/* Alloc SRB structure */
 	sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx));
 	if (!sp) {
+		ql_log(ql_log_warn, vha, 0x7015,
+		    "qla2x00_get_ctx_bsg_sp failed.\n");
 		rval = -ENOMEM;
 		goto done_free_fcport;
 	}
@@ -488,15 +491,17 @@
 	ct->name = "bsg_ct";
 	ct->u.bsg_job = bsg_job;
 
-	DEBUG2(qla_printk(KERN_INFO, ha,
-		"scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x "
-		"portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type,
-		(bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16),
-		fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
-		fcport->d_id.b.al_pa));
+	ql_dbg(ql_dbg_user, vha, 0x7016,
+	    "bsg rqst type: %s else type: %x - "
+	    "loop-id=%x portid=%02x%02x%02x.\n", type,
+	    (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16),
+	    fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa);
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x7017,
+		    "qla2x00_start_sp failed=%d.\n", rval);
 		kfree(sp->ctx);
 		mempool_free(sp, ha->srb_mempool);
 		rval = -EIO;
@@ -535,9 +540,8 @@
 	ha->notify_dcbx_comp = 1;
 	ret = qla81xx_set_port_config(vha, new_config);
 	if (ret != QLA_SUCCESS) {
-		DEBUG2(printk(KERN_ERR
-		    "%s(%lu): Set port config failed\n",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7021,
+		    "set port config failed.\n");
 		ha->notify_dcbx_comp = 0;
 		rval = -EINVAL;
 		goto done_set_internal;
@@ -545,11 +549,11 @@
 
 	/* Wait for DCBX complete event */
 	if (!wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "State change notificaition not received.\n"));
+		ql_dbg(ql_dbg_user, vha, 0x7022,
+		    "State change notification not received.\n");
 	} else
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "State change RECEIVED\n"));
+		ql_dbg(ql_dbg_user, vha, 0x7023,
+		    "State change received.\n");
 
 	ha->notify_dcbx_comp = 0;
 
@@ -581,9 +585,8 @@
 		ha->notify_dcbx_comp = wait;
 		ret = qla81xx_set_port_config(vha, new_config);
 		if (ret != QLA_SUCCESS) {
-			DEBUG2(printk(KERN_ERR
-			    "%s(%lu): Set port config failed\n",
-			     __func__, vha->host_no));
+			ql_log(ql_log_warn, vha, 0x7025,
+			    "Set port config failed.\n");
 			ha->notify_dcbx_comp = 0;
 			rval = -EINVAL;
 			goto done_reset_internal;
@@ -592,14 +595,14 @@
 		/* Wait for DCBX complete event */
 		if (wait && !wait_for_completion_timeout(&ha->dcbx_comp,
 			(20 * HZ))) {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			    "State change notificaition not received.\n"));
+			ql_dbg(ql_dbg_user, vha, 0x7026,
+			    "State change notification not received.\n");
 			ha->notify_dcbx_comp = 0;
 			rval = -EINVAL;
 			goto done_reset_internal;
 		} else
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "State change RECEIVED\n"));
+			ql_dbg(ql_dbg_user, vha, 0x7027,
+			    "State change received.\n");
 
 		ha->notify_dcbx_comp = 0;
 	}
@@ -629,11 +632,13 @@
 
 	if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
 		test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
-		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags))
+		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+		ql_log(ql_log_warn, vha, 0x7018, "Abort active or needed.\n");
 		return -EBUSY;
+	}
 
 	if (!vha->flags.online) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n"));
+		ql_log(ql_log_warn, vha, 0x7019, "Host is not online.\n");
 		return -EIO;
 	}
 
@@ -641,26 +646,31 @@
 		bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt,
 		DMA_TO_DEVICE);
 
-	if (!elreq.req_sg_cnt)
+	if (!elreq.req_sg_cnt) {
+		ql_log(ql_log_warn, vha, 0x701a,
+		    "dma_map_sg returned %d for request.\n", elreq.req_sg_cnt);
 		return -ENOMEM;
+	}
 
 	elreq.rsp_sg_cnt = dma_map_sg(&ha->pdev->dev,
 		bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt,
 		DMA_FROM_DEVICE);
 
 	if (!elreq.rsp_sg_cnt) {
+		ql_log(ql_log_warn, vha, 0x701b,
+		    "dma_map_sg returned %d for reply.\n", elreq.rsp_sg_cnt);
 		rval = -ENOMEM;
 		goto done_unmap_req_sg;
 	}
 
 	if ((elreq.req_sg_cnt !=  bsg_job->request_payload.sg_cnt) ||
 		(elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
-		DEBUG2(printk(KERN_INFO
-			"dma mapping resulted in different sg counts "
-			"[request_sg_cnt: %x dma_request_sg_cnt: %x "
-			"reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n",
-			bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt,
-			bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt));
+		ql_log(ql_log_warn, vha, 0x701c,
+		    "dma mapping resulted in different sg counts, "
+		    "request_sg_cnt: %x dma_request_sg_cnt: %x "
+		    "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n",
+		    bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt,
+		    bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt);
 		rval = -EAGAIN;
 		goto done_unmap_sg;
 	}
@@ -668,8 +678,8 @@
 	req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len,
 		&req_data_dma, GFP_KERNEL);
 	if (!req_data) {
-		DEBUG2(printk(KERN_ERR "%s: dma alloc for req_data "
-			"failed for host=%lu\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x701d,
+		    "dma alloc failed for req_data.\n");
 		rval = -ENOMEM;
 		goto done_unmap_sg;
 	}
@@ -677,8 +687,8 @@
 	rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len,
 		&rsp_data_dma, GFP_KERNEL);
 	if (!rsp_data) {
-		DEBUG2(printk(KERN_ERR "%s: dma alloc for rsp_data "
-			"failed for host=%lu\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7004,
+		    "dma alloc failed for rsp_data.\n");
 		rval = -ENOMEM;
 		goto done_free_dma_req;
 	}
@@ -699,8 +709,8 @@
 	    && req_data_len == MAX_ELS_FRAME_PAYLOAD)) &&
 		elreq.options == EXTERNAL_LOOPBACK) {
 		type = "FC_BSG_HST_VENDOR_ECHO_DIAG";
-		DEBUG2(qla_printk(KERN_INFO, ha,
-			"scsi(%ld) bsg rqst type: %s\n", vha->host_no, type));
+		ql_dbg(ql_dbg_user, vha, 0x701e,
+		    "BSG request type: %s.\n", type);
 		command_sent = INT_DEF_LB_ECHO_CMD;
 		rval = qla2x00_echo_test(vha, &elreq, response);
 	} else {
@@ -708,9 +718,8 @@
 			memset(config, 0, sizeof(config));
 			memset(new_config, 0, sizeof(new_config));
 			if (qla81xx_get_port_config(vha, config)) {
-				DEBUG2(printk(KERN_ERR
-					"%s(%lu): Get port config failed\n",
-					__func__, vha->host_no));
+				ql_log(ql_log_warn, vha, 0x701f,
+				    "Get port config failed.\n");
 				bsg_job->reply->reply_payload_rcv_len = 0;
 				bsg_job->reply->result = (DID_ERROR << 16);
 				rval = -EPERM;
@@ -718,11 +727,13 @@
 			}
 
 			if (elreq.options != EXTERNAL_LOOPBACK) {
-				DEBUG2(qla_printk(KERN_INFO, ha,
-					"Internal: current port config = %x\n",
-					config[0]));
+				ql_dbg(ql_dbg_user, vha, 0x7020,
+				    "Internal: curent port config = %x\n",
+				    config[0]);
 				if (qla81xx_set_internal_loopback(vha, config,
 					new_config)) {
+					ql_log(ql_log_warn, vha, 0x7024,
+					    "Internal loopback failed.\n");
 					bsg_job->reply->reply_payload_rcv_len =
 						0;
 					bsg_job->reply->result =
@@ -746,9 +757,8 @@
 			}
 
 			type = "FC_BSG_HST_VENDOR_LOOPBACK";
-			DEBUG2(qla_printk(KERN_INFO, ha,
-				"scsi(%ld) bsg rqst type: %s\n",
-				vha->host_no, type));
+			ql_dbg(ql_dbg_user, vha, 0x7028,
+			    "BSG request type: %s.\n", type);
 
 			command_sent = INT_DEF_LB_LOOPBACK_CMD;
 			rval = qla2x00_loopback_test(vha, &elreq, response);
@@ -763,17 +773,16 @@
 
 			if (response[0] == MBS_COMMAND_ERROR &&
 					response[1] == MBS_LB_RESET) {
-				DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing "
-					"ISP\n", __func__, vha->host_no));
+				ql_log(ql_log_warn, vha, 0x7029,
+				    "MBX command error, Aborting ISP.\n");
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 				qla2xxx_wake_dpc(vha);
 				qla2x00_wait_for_chip_reset(vha);
 				/* Also reset the MPI */
 				if (qla81xx_restart_mpi_firmware(vha) !=
 				    QLA_SUCCESS) {
-					qla_printk(KERN_INFO, ha,
-					    "MPI reset failed for host%ld.\n",
-					    vha->host_no);
+					ql_log(ql_log_warn, vha, 0x702a,
+					    "MPI reset failed.\n");
 				}
 
 				bsg_job->reply->reply_payload_rcv_len = 0;
@@ -783,17 +792,16 @@
 			}
 		} else {
 			type = "FC_BSG_HST_VENDOR_LOOPBACK";
-			DEBUG2(qla_printk(KERN_INFO, ha,
-				"scsi(%ld) bsg rqst type: %s\n",
-				vha->host_no, type));
+			ql_dbg(ql_dbg_user, vha, 0x702b,
+			    "BSG request type: %s.\n", type);
 			command_sent = INT_DEF_LB_LOOPBACK_CMD;
 			rval = qla2x00_loopback_test(vha, &elreq, response);
 		}
 	}
 
 	if (rval) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-		    "request %s failed\n", vha->host_no, type));
+		ql_log(ql_log_warn, vha, 0x702c,
+		    "Vendor request %s failed.\n", type);
 
 		fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
 		    sizeof(struct fc_bsg_reply);
@@ -805,8 +813,8 @@
 		bsg_job->reply->reply_payload_rcv_len = 0;
 		bsg_job->reply->result = (DID_ERROR << 16);
 	} else {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-			"request %s completed\n", vha->host_no, type));
+		ql_dbg(ql_dbg_user, vha, 0x702d,
+		    "Vendor request %s completed.\n", type);
 
 		bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
 			sizeof(response) + sizeof(uint8_t);
@@ -851,12 +859,13 @@
 
 	if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
 	    test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
-	    test_bit(ISP_ABORT_RETRY, &vha->dpc_flags))
+	    test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+		ql_log(ql_log_warn, vha, 0x702e, "Abort active or needed.\n");
 		return -EBUSY;
+	}
 
 	if (!IS_QLA84XX(ha)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, "
-		   "exiting.\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x702f, "Not 84xx, exiting.\n");
 		return -EINVAL;
 	}
 
@@ -865,14 +874,14 @@
 	rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
 
 	if (rval) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-		    "request 84xx reset failed\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7030,
+		    "Vendor request 84xx reset failed.\n");
 		rval = bsg_job->reply->reply_payload_rcv_len = 0;
 		bsg_job->reply->result = (DID_ERROR << 16);
 
 	} else {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-		    "request 84xx reset completed\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7031,
+		    "Vendor request 84xx reset completed.\n");
 		bsg_job->reply->result = DID_OK;
 	}
 
@@ -902,21 +911,24 @@
 		return -EBUSY;
 
 	if (!IS_QLA84XX(ha)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, "
-			"exiting.\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7032,
+		    "Not 84xx, exiting.\n");
 		return -EINVAL;
 	}
 
 	sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
 		bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
-	if (!sg_cnt)
+	if (!sg_cnt) {
+		ql_log(ql_log_warn, vha, 0x7033,
+		    "dma_map_sg returned %d for request.\n", sg_cnt);
 		return -ENOMEM;
+	}
 
 	if (sg_cnt != bsg_job->request_payload.sg_cnt) {
-		DEBUG2(printk(KERN_INFO
-			"dma mapping resulted in different sg counts "
-			"request_sg_cnt: %x dma_request_sg_cnt: %x ",
-			bsg_job->request_payload.sg_cnt, sg_cnt));
+		ql_log(ql_log_warn, vha, 0x7034,
+		    "DMA mapping resulted in different sg counts, "
+		    "request_sg_cnt: %x dma_request_sg_cnt: %x.\n",
+		    bsg_job->request_payload.sg_cnt, sg_cnt);
 		rval = -EAGAIN;
 		goto done_unmap_sg;
 	}
@@ -925,8 +937,8 @@
 	fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len,
 		&fw_dma, GFP_KERNEL);
 	if (!fw_buf) {
-		DEBUG2(printk(KERN_ERR "%s: dma alloc for fw_buf "
-			"failed for host=%lu\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7035,
+		    "DMA alloc failed for fw_buf.\n");
 		rval = -ENOMEM;
 		goto done_unmap_sg;
 	}
@@ -936,8 +948,8 @@
 
 	mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
 	if (!mn) {
-		DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer "
-			"failed for host=%lu\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7036,
+		    "DMA alloc failed for fw buffer.\n");
 		rval = -ENOMEM;
 		goto done_free_fw_buf;
 	}
@@ -965,15 +977,15 @@
 	rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
 
 	if (rval) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-			"request 84xx updatefw failed\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7037,
+		    "Vendor request 84xx updatefw failed.\n");
 
 		rval = bsg_job->reply->reply_payload_rcv_len = 0;
 		bsg_job->reply->result = (DID_ERROR << 16);
 
 	} else {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-			"request 84xx updatefw completed\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7038,
+		    "Vendor request 84xx updatefw completed.\n");
 
 		bsg_job->reply_len = sizeof(struct fc_bsg_reply);
 		bsg_job->reply->result = DID_OK;
@@ -1009,27 +1021,30 @@
 
 	if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
 		test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
-		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags))
+		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+		ql_log(ql_log_warn, vha, 0x7039,
+		    "Abort active or needed.\n");
 		return -EBUSY;
+	}
 
 	if (!IS_QLA84XX(ha)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, "
-			"exiting.\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x703a,
+		    "Not 84xx, exiting.\n");
 		return -EINVAL;
 	}
 
 	ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request +
 		sizeof(struct fc_bsg_request));
 	if (!ql84_mgmt) {
-		DEBUG2(printk("%s(%ld): mgmt header not provided, exiting.\n",
-			__func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x703b,
+		    "MGMT header not provided, exiting.\n");
 		return -EINVAL;
 	}
 
 	mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
 	if (!mn) {
-		DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer "
-			"failed for host=%lu\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x703c,
+		    "DMA alloc failed for fw buffer.\n");
 		return -ENOMEM;
 	}
 
@@ -1044,6 +1059,8 @@
 			bsg_job->reply_payload.sg_list,
 			bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
 		if (!sg_cnt) {
+			ql_log(ql_log_warn, vha, 0x703d,
+			    "dma_map_sg returned %d for reply.\n", sg_cnt);
 			rval = -ENOMEM;
 			goto exit_mgmt;
 		}
@@ -1051,10 +1068,10 @@
 		dma_direction = DMA_FROM_DEVICE;
 
 		if (sg_cnt != bsg_job->reply_payload.sg_cnt) {
-			DEBUG2(printk(KERN_INFO
-				"dma mapping resulted in different sg counts "
-				"reply_sg_cnt: %x dma_reply_sg_cnt: %x\n",
-				bsg_job->reply_payload.sg_cnt, sg_cnt));
+			ql_log(ql_log_warn, vha, 0x703e,
+			    "DMA mapping resulted in different sg counts, "
+			    "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n",
+			    bsg_job->reply_payload.sg_cnt, sg_cnt);
 			rval = -EAGAIN;
 			goto done_unmap_sg;
 		}
@@ -1064,9 +1081,8 @@
 		mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len,
 		    &mgmt_dma, GFP_KERNEL);
 		if (!mgmt_b) {
-			DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b "
-				"failed for host=%lu\n",
-				__func__, vha->host_no));
+			ql_log(ql_log_warn, vha, 0x703f,
+			    "DMA alloc failed for mgmt_b.\n");
 			rval = -ENOMEM;
 			goto done_unmap_sg;
 		}
@@ -1094,6 +1110,8 @@
 			bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
 
 		if (!sg_cnt) {
+			ql_log(ql_log_warn, vha, 0x7040,
+			    "dma_map_sg returned %d.\n", sg_cnt);
 			rval = -ENOMEM;
 			goto exit_mgmt;
 		}
@@ -1101,10 +1119,10 @@
 		dma_direction = DMA_TO_DEVICE;
 
 		if (sg_cnt != bsg_job->request_payload.sg_cnt) {
-			DEBUG2(printk(KERN_INFO
-				"dma mapping resulted in different sg counts "
-				"request_sg_cnt: %x dma_request_sg_cnt: %x ",
-				bsg_job->request_payload.sg_cnt, sg_cnt));
+			ql_log(ql_log_warn, vha, 0x7041,
+			    "DMA mapping resulted in different sg counts, "
+			    "request_sg_cnt: %x dma_request_sg_cnt: %x.\n",
+			    bsg_job->request_payload.sg_cnt, sg_cnt);
 			rval = -EAGAIN;
 			goto done_unmap_sg;
 		}
@@ -1113,9 +1131,8 @@
 		mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len,
 			&mgmt_dma, GFP_KERNEL);
 		if (!mgmt_b) {
-			DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b "
-				"failed for host=%lu\n",
-				__func__, vha->host_no));
+			ql_log(ql_log_warn, vha, 0x7042,
+			    "DMA alloc failed for mgmt_b.\n");
 			rval = -ENOMEM;
 			goto done_unmap_sg;
 		}
@@ -1156,15 +1173,15 @@
 	rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0);
 
 	if (rval) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-			"request 84xx mgmt failed\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7043,
+		    "Vendor request 84xx mgmt failed.\n");
 
 		rval = bsg_job->reply->reply_payload_rcv_len = 0;
 		bsg_job->reply->result = (DID_ERROR << 16);
 
 	} else {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor "
-			"request 84xx mgmt completed\n", vha->host_no));
+		ql_dbg(ql_dbg_user, vha, 0x7044,
+		    "Vendor request 84xx mgmt completed.\n");
 
 		bsg_job->reply_len = sizeof(struct fc_bsg_reply);
 		bsg_job->reply->result = DID_OK;
@@ -1204,7 +1221,6 @@
 {
 	struct Scsi_Host *host = bsg_job->shost;
 	scsi_qla_host_t *vha = shost_priv(host);
-	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
 	struct qla_port_param *port_param = NULL;
 	fc_port_t *fcport = NULL;
@@ -1215,26 +1231,27 @@
 
 	if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
 		test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
-		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags))
+		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+		ql_log(ql_log_warn, vha, 0x7045, "abort active or needed.\n");
 		return -EBUSY;
+	}
 
 	if (!IS_IIDMA_CAPABLE(vha->hw)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha, "%s(%lu): iiDMA not "
-			"supported\n",  __func__, vha->host_no));
+		ql_log(ql_log_info, vha, 0x7046, "iiDMA not supported.\n");
 		return -EINVAL;
 	}
 
 	port_param = (struct qla_port_param *)((char *)bsg_job->request +
 		sizeof(struct fc_bsg_request));
 	if (!port_param) {
-		DEBUG2(printk("%s(%ld): port_param header not provided, "
-			"exiting.\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7047,
+		    "port_param header not provided.\n");
 		return -EINVAL;
 	}
 
 	if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) {
-		DEBUG2(printk(KERN_ERR "%s(%ld): Invalid destination type\n",
-			__func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7048,
+		    "Invalid destination type.\n");
 		return -EINVAL;
 	}
 
@@ -1249,21 +1266,20 @@
 	}
 
 	if (!fcport) {
-		DEBUG2(printk(KERN_ERR "%s(%ld): Failed to find port\n",
-			__func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x7049,
+		    "Failed to find port.\n");
 		return -EINVAL;
 	}
 
 	if (atomic_read(&fcport->state) != FCS_ONLINE) {
-		DEBUG2(printk(KERN_ERR "%s(%ld): Port not online\n",
-			__func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x704a,
+		    "Port is not online.\n");
 		return -EINVAL;
 	}
 
 	if (fcport->flags & FCF_LOGIN_NEEDED) {
-		DEBUG2(printk(KERN_ERR "%s(%ld): Remote port not logged in, "
-		    "flags = 0x%x\n",
-		    __func__, vha->host_no, fcport->flags));
+		ql_log(ql_log_warn, vha, 0x704b,
+		    "Remote port not logged in flags = 0x%x.\n", fcport->flags);
 		return -EINVAL;
 	}
 
@@ -1275,15 +1291,13 @@
 			&port_param->speed, mb);
 
 	if (rval) {
-		DEBUG16(printk(KERN_ERR "scsi(%ld): iIDMA cmd failed for "
-			"%02x%02x%02x%02x%02x%02x%02x%02x -- "
-			"%04x %x %04x %04x.\n",
-			vha->host_no, fcport->port_name[0],
-			fcport->port_name[1],
-			fcport->port_name[2], fcport->port_name[3],
-			fcport->port_name[4], fcport->port_name[5],
-			fcport->port_name[6], fcport->port_name[7], rval,
-			fcport->fp_speed, mb[0], mb[1]));
+		ql_log(ql_log_warn, vha, 0x704c,
+		    "iIDMA cmd failed for %02x%02x%02x%02x%02x%02x%02x%02x -- "
+		    "%04x %x %04x %04x.\n", fcport->port_name[0],
+		    fcport->port_name[1], fcport->port_name[2],
+		    fcport->port_name[3], fcport->port_name[4],
+		    fcport->port_name[5], fcport->port_name[6],
+		    fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1]);
 		rval = 0;
 		bsg_job->reply->result = (DID_ERROR << 16);
 
@@ -1307,11 +1321,12 @@
 }
 
 static int
-qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, struct qla_hw_data *ha,
+qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
 	uint8_t is_update)
 {
 	uint32_t start = 0;
 	int valid = 0;
+	struct qla_hw_data *ha = vha->hw;
 
 	bsg_job->reply->reply_payload_rcv_len = 0;
 
@@ -1319,14 +1334,20 @@
 		return -EINVAL;
 
 	start = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
-	if (start > ha->optrom_size)
+	if (start > ha->optrom_size) {
+		ql_log(ql_log_warn, vha, 0x7055,
+		    "start %d > optrom_size %d.\n", start, ha->optrom_size);
 		return -EINVAL;
+	}
 
-	if (ha->optrom_state != QLA_SWAITING)
+	if (ha->optrom_state != QLA_SWAITING) {
+		ql_log(ql_log_info, vha, 0x7056,
+		    "optrom_state %d.\n", ha->optrom_state);
 		return -EBUSY;
+	}
 
 	ha->optrom_region_start = start;
-
+	ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update);
 	if (is_update) {
 		if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
 			valid = 1;
@@ -1337,9 +1358,9 @@
 		    IS_QLA8XXX_TYPE(ha))
 			valid = 1;
 		if (!valid) {
-			qla_printk(KERN_WARNING, ha,
-			    "Invalid start region 0x%x/0x%x.\n",
-			    start, bsg_job->request_payload.payload_len);
+			ql_log(ql_log_warn, vha, 0x7058,
+			    "Invalid start region 0x%x/0x%x.\n", start,
+			    bsg_job->request_payload.payload_len);
 			return -EINVAL;
 		}
 
@@ -1358,9 +1379,9 @@
 
 	ha->optrom_buffer = vmalloc(ha->optrom_region_size);
 	if (!ha->optrom_buffer) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7059,
 		    "Read: Unable to allocate memory for optrom retrieval "
-		    "(%x).\n", ha->optrom_region_size);
+		    "(%x)\n", ha->optrom_region_size);
 
 		ha->optrom_state = QLA_SWAITING;
 		return -ENOMEM;
@@ -1378,7 +1399,7 @@
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
 
-	rval = qla2x00_optrom_setup(bsg_job, ha, 0);
+	rval = qla2x00_optrom_setup(bsg_job, vha, 0);
 	if (rval)
 		return rval;
 
@@ -1406,7 +1427,7 @@
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
 
-	rval = qla2x00_optrom_setup(bsg_job, ha, 1);
+	rval = qla2x00_optrom_setup(bsg_job, vha, 1);
 	if (rval)
 		return rval;
 
@@ -1464,6 +1485,23 @@
 qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
 {
 	int ret = -EINVAL;
+	struct fc_rport *rport;
+	fc_port_t *fcport = NULL;
+	struct Scsi_Host *host;
+	scsi_qla_host_t *vha;
+
+	if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
+		rport = bsg_job->rport;
+		fcport = *(fc_port_t **) rport->dd_data;
+		host = rport_to_shost(rport);
+		vha = shost_priv(host);
+	} else {
+		host = bsg_job->shost;
+		vha = shost_priv(host);
+	}
+
+	ql_dbg(ql_dbg_user, vha, 0x7000,
+	    "Entered %s msgcode=%d.\n", __func__, bsg_job->request->msgcode);
 
 	switch (bsg_job->request->msgcode) {
 	case FC_BSG_RPT_ELS:
@@ -1480,7 +1518,7 @@
 	case FC_BSG_HST_DEL_RPORT:
 	case FC_BSG_RPT_CT:
 	default:
-		DEBUG2(printk("qla2xxx: unsupported BSG request\n"));
+		ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
 		break;
 	}
 	return ret;
@@ -1514,17 +1552,15 @@
 					&& (sp_bsg->u.bsg_job == bsg_job)) {
 					spin_unlock_irqrestore(&ha->hardware_lock, flags);
 					if (ha->isp_ops->abort_command(sp)) {
-						DEBUG2(qla_printk(KERN_INFO, ha,
-						    "scsi(%ld): mbx "
-						    "abort_command failed\n",
-						    vha->host_no));
+						ql_log(ql_log_warn, vha, 0x7089,
+						    "mbx abort_command "
+						    "failed.\n");
 						bsg_job->req->errors =
 						bsg_job->reply->result = -EIO;
 					} else {
-						DEBUG2(qla_printk(KERN_INFO, ha,
-						    "scsi(%ld): mbx "
-						    "abort_command success\n",
-						    vha->host_no));
+						ql_dbg(ql_dbg_user, vha, 0x708a,
+						    "mbx abort_command "
+						    "success.\n");
 						bsg_job->req->errors =
 						bsg_job->reply->result = 0;
 					}
@@ -1535,8 +1571,7 @@
 		}
 	}
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
-	DEBUG2(qla_printk(KERN_INFO, ha,
-		"scsi(%ld) SRB not found to abort\n", vha->host_no));
+	ql_log(ql_log_info, vha, 0x708b, "SRB not found to abort.\n");
 	bsg_job->req->errors = bsg_job->reply->result = -ENXIO;
 	return 0;
 
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index c53719a..2155071 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -4,10 +4,36 @@
  *
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
+
+/*
+ * Table for showing the current message id in use for particular level
+ * Change this table for addition of log/debug messages.
+ * -----------------------------------------------------
+ * |             Level            |   Last Value Used  |
+ * -----------------------------------------------------
+ * | Module Init and Probe        |       0x0116       |
+ * | Mailbox commands             |       0x111e       |
+ * | Device Discovery             |       0x2083       |
+ * | Queue Command and IO tracing |       0x302e       |
+ * | DPC Thread                   |       0x401c       |
+ * | Async Events                 |       0x5059       |
+ * | Timer Routines               |       0x600d       |
+ * | User Space Interactions      |       0x709c       |
+ * | Task Management              |       0x8043       |
+ * | AER/EEH                      |       0x900f       |
+ * | Virtual Port                 |       0xa007       |
+ * | ISP82XX Specific             |       0xb027       |
+ * | MultiQ                       |       0xc00b       |
+ * | Misc                         |       0xd00b       |
+ * -----------------------------------------------------
+ */
+
 #include "qla_def.h"
 
 #include <linux/delay.h>
 
+static uint32_t ql_dbg_offset = 0x800;
+
 static inline void
 qla2xxx_prep_dump(struct qla_hw_data *ha, struct qla2xxx_fw_dump *fw_dump)
 {
@@ -383,11 +409,11 @@
 	struct qla_hw_data *ha = vha->hw;
 
 	if (rval != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
-		    "Failed to dump firmware (%x)!!!\n", rval);
+		ql_log(ql_log_warn, vha, 0xd000,
+		    "Failed to dump firmware (%x).\n", rval);
 		ha->fw_dumped = 0;
 	} else {
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0xd001,
 		    "Firmware dump saved to temp buffer (%ld/%p).\n",
 		    vha->host_no, ha->fw_dump);
 		ha->fw_dumped = 1;
@@ -419,15 +445,16 @@
 		spin_lock_irqsave(&ha->hardware_lock, flags);
 
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "No buffer available for dump!!!\n");
+		ql_log(ql_log_warn, vha, 0xd002,
+		    "No buffer available for dump.\n");
 		goto qla2300_fw_dump_failed;
 	}
 
 	if (ha->fw_dumped) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware has been previously dumped (%p) -- ignoring "
-		    "request...\n", ha->fw_dump);
+		ql_log(ql_log_warn, vha, 0xd003,
+		    "Firmware has been previously dumped (%p) "
+		    "-- ignoring request.\n",
+		    ha->fw_dump);
 		goto qla2300_fw_dump_failed;
 	}
 	fw = &ha->fw_dump->isp.isp23;
@@ -582,15 +609,16 @@
 		spin_lock_irqsave(&ha->hardware_lock, flags);
 
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "No buffer available for dump!!!\n");
+		ql_log(ql_log_warn, vha, 0xd004,
+		    "No buffer available for dump.\n");
 		goto qla2100_fw_dump_failed;
 	}
 
 	if (ha->fw_dumped) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware has been previously dumped (%p) -- ignoring "
-		    "request...\n", ha->fw_dump);
+		ql_log(ql_log_warn, vha, 0xd005,
+		    "Firmware has been previously dumped (%p) "
+		    "-- ignoring request.\n",
+		    ha->fw_dump);
 		goto qla2100_fw_dump_failed;
 	}
 	fw = &ha->fw_dump->isp.isp21;
@@ -779,15 +807,16 @@
 		spin_lock_irqsave(&ha->hardware_lock, flags);
 
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "No buffer available for dump!!!\n");
+		ql_log(ql_log_warn, vha, 0xd006,
+		    "No buffer available for dump.\n");
 		goto qla24xx_fw_dump_failed;
 	}
 
 	if (ha->fw_dumped) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware has been previously dumped (%p) -- ignoring "
-		    "request...\n", ha->fw_dump);
+		ql_log(ql_log_warn, vha, 0xd007,
+		    "Firmware has been previously dumped (%p) "
+		    "-- ignoring request.\n",
+		    ha->fw_dump);
 		goto qla24xx_fw_dump_failed;
 	}
 	fw = &ha->fw_dump->isp.isp24;
@@ -1017,15 +1046,16 @@
 		spin_lock_irqsave(&ha->hardware_lock, flags);
 
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "No buffer available for dump!!!\n");
+		ql_log(ql_log_warn, vha, 0xd008,
+		    "No buffer available for dump.\n");
 		goto qla25xx_fw_dump_failed;
 	}
 
 	if (ha->fw_dumped) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware has been previously dumped (%p) -- ignoring "
-		    "request...\n", ha->fw_dump);
+		ql_log(ql_log_warn, vha, 0xd009,
+		    "Firmware has been previously dumped (%p) "
+		    "-- ignoring request.\n",
+		    ha->fw_dump);
 		goto qla25xx_fw_dump_failed;
 	}
 	fw = &ha->fw_dump->isp.isp25;
@@ -1328,15 +1358,16 @@
 		spin_lock_irqsave(&ha->hardware_lock, flags);
 
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "No buffer available for dump!!!\n");
+		ql_log(ql_log_warn, vha, 0xd00a,
+		    "No buffer available for dump.\n");
 		goto qla81xx_fw_dump_failed;
 	}
 
 	if (ha->fw_dumped) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware has been previously dumped (%p) -- ignoring "
-		    "request...\n", ha->fw_dump);
+		ql_log(ql_log_warn, vha, 0xd00b,
+		    "Firmware has been previously dumped (%p) "
+		    "-- ignoring request.\n",
+		    ha->fw_dump);
 		goto qla81xx_fw_dump_failed;
 	}
 	fw = &ha->fw_dump->isp.isp81;
@@ -1619,106 +1650,255 @@
 /****************************************************************************/
 /*                         Driver Debug Functions.                          */
 /****************************************************************************/
+/*
+ * This function is for formatting and logging debug information.
+ * It is to be used when vha is available. It formats the message
+ * and logs it to the messages file.
+ * parameters:
+ * level: The level of the debug messages to be printed.
+ *        If ql2xextended_error_logging value is correctly set,
+ *        this message will appear in the messages file.
+ * vha:   Pointer to the scsi_qla_host_t.
+ * id:    This is a unique identifier for the level. It identifies the
+ *        part of the code from where the message originated.
+ * msg:   The message to be displayed.
+ */
+void
+ql_dbg(uint32_t level, scsi_qla_host_t *vha, int32_t id, char *msg, ...) {
+
+	char pbuf[QL_DBG_BUF_LEN];
+	va_list ap;
+	uint32_t len;
+	struct pci_dev *pdev = NULL;
+
+	memset(pbuf, 0, QL_DBG_BUF_LEN);
+
+	va_start(ap, msg);
+
+	if ((level & ql2xextended_error_logging) == level) {
+		if (vha != NULL) {
+			pdev = vha->hw->pdev;
+			/* <module-name> <pci-name> <msg-id>:<host> Message */
+			sprintf(pbuf, "%s [%s]-%04x:%ld: ", QL_MSGHDR,
+			    dev_name(&(pdev->dev)), id + ql_dbg_offset,
+			    vha->host_no);
+		} else
+			sprintf(pbuf, "%s [%s]-%04x: : ", QL_MSGHDR,
+			    "0000:00:00.0", id + ql_dbg_offset);
+
+		len = strlen(pbuf);
+		vsprintf(pbuf+len, msg, ap);
+		pr_warning("%s", pbuf);
+	}
+
+	va_end(ap);
+
+}
+
+/*
+ * This function is for formatting and logging debug information.
+ * It is to be used when vha is not available and pci is availble,
+ * i.e., before host allocation. It formats the message and logs it
+ * to the messages file.
+ * parameters:
+ * level: The level of the debug messages to be printed.
+ *        If ql2xextended_error_logging value is correctly set,
+ *        this message will appear in the messages file.
+ * pdev:  Pointer to the struct pci_dev.
+ * id:    This is a unique id for the level. It identifies the part
+ *        of the code from where the message originated.
+ * msg:   The message to be displayed.
+ */
+void
+ql_dbg_pci(uint32_t level, struct pci_dev *pdev, int32_t id, char *msg, ...) {
+
+	char pbuf[QL_DBG_BUF_LEN];
+	va_list ap;
+	uint32_t len;
+
+	if (pdev == NULL)
+		return;
+
+	memset(pbuf, 0, QL_DBG_BUF_LEN);
+
+	va_start(ap, msg);
+
+	if ((level & ql2xextended_error_logging) == level) {
+		/* <module-name> <dev-name>:<msg-id> Message */
+		sprintf(pbuf, "%s [%s]-%04x: : ", QL_MSGHDR,
+		    dev_name(&(pdev->dev)), id + ql_dbg_offset);
+
+		len = strlen(pbuf);
+		vsprintf(pbuf+len, msg, ap);
+		pr_warning("%s", pbuf);
+	}
+
+	va_end(ap);
+
+}
+
+/*
+ * This function is for formatting and logging log messages.
+ * It is to be used when vha is available. It formats the message
+ * and logs it to the messages file. All the messages will be logged
+ * irrespective of value of ql2xextended_error_logging.
+ * parameters:
+ * level: The level of the log messages to be printed in the
+ *        messages file.
+ * vha:   Pointer to the scsi_qla_host_t
+ * id:    This is a unique id for the level. It identifies the
+ *        part of the code from where the message originated.
+ * msg:   The message to be displayed.
+ */
+void
+ql_log(uint32_t level, scsi_qla_host_t *vha, int32_t id, char *msg, ...) {
+
+	char pbuf[QL_DBG_BUF_LEN];
+	va_list ap;
+	uint32_t len;
+	struct pci_dev *pdev = NULL;
+
+	memset(pbuf, 0, QL_DBG_BUF_LEN);
+
+	va_start(ap, msg);
+
+	if (level <= ql_errlev) {
+		if (vha != NULL) {
+			pdev = vha->hw->pdev;
+			/* <module-name> <msg-id>:<host> Message */
+			sprintf(pbuf, "%s [%s]-%04x:%ld: ", QL_MSGHDR,
+			    dev_name(&(pdev->dev)), id, vha->host_no);
+		} else
+			sprintf(pbuf, "%s [%s]-%04x: : ", QL_MSGHDR,
+			    "0000:00:00.0", id);
+
+		len = strlen(pbuf);
+			vsprintf(pbuf+len, msg, ap);
+
+		switch (level) {
+		case 0: /* FATAL LOG */
+			pr_crit("%s", pbuf);
+			break;
+		case 1:
+			pr_err("%s", pbuf);
+			break;
+		case 2:
+			pr_warn("%s", pbuf);
+			break;
+		default:
+			pr_info("%s", pbuf);
+			break;
+		}
+	}
+
+	va_end(ap);
+}
+
+/*
+ * This function is for formatting and logging log messages.
+ * It is to be used when vha is not available and pci is availble,
+ * i.e., before host allocation. It formats the message and logs
+ * it to the messages file. All the messages are logged irrespective
+ * of the value of ql2xextended_error_logging.
+ * parameters:
+ * level: The level of the log messages to be printed in the
+ *        messages file.
+ * pdev:  Pointer to the struct pci_dev.
+ * id:    This is a unique id for the level. It identifies the
+ *        part of the code from where the message originated.
+ * msg:   The message to be displayed.
+ */
+void
+ql_log_pci(uint32_t level, struct pci_dev *pdev, int32_t id, char *msg, ...) {
+
+	char pbuf[QL_DBG_BUF_LEN];
+	va_list ap;
+	uint32_t len;
+
+	if (pdev == NULL)
+		return;
+
+	memset(pbuf, 0, QL_DBG_BUF_LEN);
+
+	va_start(ap, msg);
+
+	if (level <= ql_errlev) {
+		/* <module-name> <dev-name>:<msg-id> Message */
+		sprintf(pbuf, "%s [%s]-%04x: : ", QL_MSGHDR,
+		    dev_name(&(pdev->dev)), id);
+
+		len = strlen(pbuf);
+		vsprintf(pbuf+len, msg, ap);
+		switch (level) {
+		case 0: /* FATAL LOG */
+			pr_crit("%s", pbuf);
+			break;
+		case 1:
+			pr_err("%s", pbuf);
+			break;
+		case 2:
+			pr_warn("%s", pbuf);
+			break;
+		default:
+			pr_info("%s", pbuf);
+			break;
+		}
+	}
+
+	va_end(ap);
+}
 
 void
-qla2x00_dump_regs(scsi_qla_host_t *vha)
+ql_dump_regs(uint32_t level, scsi_qla_host_t *vha, int32_t id)
 {
 	int i;
 	struct qla_hw_data *ha = vha->hw;
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 	struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+	struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
 	uint16_t __iomem *mbx_reg;
 
-	mbx_reg = IS_FWI2_CAPABLE(ha) ? &reg24->mailbox0:
-	    MAILBOX_REG(ha, reg, 0);
+	if ((level & ql2xextended_error_logging) == level) {
 
-	printk("Mailbox registers:\n");
-	for (i = 0; i < 6; i++)
-		printk("scsi(%ld): mbox %d 0x%04x \n", vha->host_no, i,
-		    RD_REG_WORD(mbx_reg++));
-}
-
-
-void
-qla2x00_dump_buffer(uint8_t * b, uint32_t size)
-{
-	uint32_t cnt;
-	uint8_t c;
-
-	printk(" 0   1   2   3   4   5   6   7   8   9  "
-	    "Ah  Bh  Ch  Dh  Eh  Fh\n");
-	printk("----------------------------------------"
-	    "----------------------\n");
-
-	for (cnt = 0; cnt < size;) {
-		c = *b++;
-		printk("%02x",(uint32_t) c);
-		cnt++;
-		if (!(cnt % 16))
-			printk("\n");
+		if (IS_QLA82XX(ha))
+			mbx_reg = &reg82->mailbox_in[0];
+		else if (IS_FWI2_CAPABLE(ha))
+			mbx_reg = &reg24->mailbox0;
 		else
-			printk("  ");
+			mbx_reg = MAILBOX_REG(ha, reg, 0);
+
+		ql_dbg(level, vha, id, "Mailbox registers:\n");
+		for (i = 0; i < 6; i++)
+			ql_dbg(level, vha, id,
+			    "mbox[%d] 0x%04x\n", i, RD_REG_WORD(mbx_reg++));
 	}
-	if (cnt % 16)
-		printk("\n");
 }
 
+
 void
-qla2x00_dump_buffer_zipped(uint8_t *b, uint32_t size)
+ql_dump_buffer(uint32_t level, scsi_qla_host_t *vha, int32_t id,
+	uint8_t *b, uint32_t size)
 {
 	uint32_t cnt;
 	uint8_t c;
-	uint8_t  last16[16], cur16[16];
-	uint32_t lc = 0, num_same16 = 0, j;
+	if ((level & ql2xextended_error_logging) == level) {
 
-	printk(KERN_DEBUG " 0   1   2   3   4   5   6   7   8   9  "
-	    "Ah  Bh  Ch  Dh  Eh  Fh\n");
-	printk(KERN_DEBUG "----------------------------------------"
-	    "----------------------\n");
+		ql_dbg(level, vha, id, " 0   1   2   3   4   5   6   7   8   "
+		    "9  Ah  Bh  Ch  Dh  Eh  Fh\n");
+		ql_dbg(level, vha, id, "----------------------------------"
+		    "----------------------------\n");
 
-	for (cnt = 0; cnt < size;) {
-		c = *b++;
-
-		cur16[lc++] = c;
-
-		cnt++;
+		ql_dbg(level, vha, id, "");
+		for (cnt = 0; cnt < size;) {
+			c = *b++;
+			printk("%02x", (uint32_t) c);
+			cnt++;
+			if (!(cnt % 16))
+				printk("\n");
+			else
+				printk("  ");
+		}
 		if (cnt % 16)
-			continue;
-
-		/* We have 16 now */
-		lc = 0;
-		if (num_same16 == 0) {
-			memcpy(last16, cur16, 16);
-			num_same16++;
-			continue;
-		}
-		if (memcmp(cur16, last16, 16) == 0) {
-			num_same16++;
-			continue;
-		}
-		for (j = 0; j < 16; j++)
-			printk(KERN_DEBUG "%02x  ", (uint32_t)last16[j]);
-		printk(KERN_DEBUG "\n");
-
-		if (num_same16 > 1)
-			printk(KERN_DEBUG "> prev pattern repeats (%u)"
-			    "more times\n", num_same16-1);
-		memcpy(last16, cur16, 16);
-		num_same16 = 1;
-	}
-
-	if (num_same16) {
-		for (j = 0; j < 16; j++)
-			printk(KERN_DEBUG "%02x  ", (uint32_t)last16[j]);
-		printk(KERN_DEBUG "\n");
-
-		if (num_same16 > 1)
-			printk(KERN_DEBUG "> prev pattern repeats (%u)"
-			    "more times\n", num_same16-1);
-	}
-	if (lc) {
-		for (j = 0; j < lc; j++)
-			printk(KERN_DEBUG "%02x  ", (uint32_t)cur16[j]);
-		printk(KERN_DEBUG "\n");
+			ql_dbg(level, vha, id, "\n");
 	}
 }
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
index 9304145..98a377b 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.h
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -8,146 +8,6 @@
 #include "qla_def.h"
 
 /*
- * Driver debug definitions.
- */
-/* #define QL_DEBUG_LEVEL_1  */ /* Output register accesses to COM1 */
-/* #define QL_DEBUG_LEVEL_2  */ /* Output error msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_3  */ /* Output function trace msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_4  */ /* Output NVRAM trace msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_5  */ /* Output ring trace msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_6  */ /* Output WATCHDOG timer trace to COM1 */
-/* #define QL_DEBUG_LEVEL_7  */ /* Output RISC load trace msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_8  */ /* Output ring saturation msgs to COM1 */
-/* #define QL_DEBUG_LEVEL_9  */ /* Output IOCTL trace msgs */
-/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */
-/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */
-/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */
-/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */
-/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
-/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */
-/* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */
-/* #define QL_DEBUG_LEVEL_17 */ /* Output EEH trace messages */
-/* #define QL_DEBUG_LEVEL_18 */ /* Output T10 CRC trace messages */
-
-/*
-* Macros use for debugging the driver.
-*/
-
-#define DEBUG(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-
-#if defined(QL_DEBUG_LEVEL_1)
-#define DEBUG1(x)	do {x;} while (0)
-#else
-#define DEBUG1(x)	do {} while (0)
-#endif
-
-#define DEBUG2(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_3(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_3_11(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_9_10(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_11(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_13(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_16(x)	do { if (ql2xextended_error_logging) { x; } } while (0)
-#define DEBUG2_17(x) 	do { if (ql2xextended_error_logging) { x; } } while (0)
-
-#if defined(QL_DEBUG_LEVEL_3)
-#define DEBUG3(x)	do {x;} while (0)
-#define DEBUG3_11(x)	do {x;} while (0)
-#else
-#define DEBUG3(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_4)
-#define DEBUG4(x)	do {x;} while (0)
-#else
-#define DEBUG4(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_5)
-#define DEBUG5(x)          do {x;} while (0)
-#else
-#define DEBUG5(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_7)
-#define DEBUG7(x)          do {x;} while (0)
-#else
-#define DEBUG7(x)	   do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_9)
-#define DEBUG9(x)       do {x;} while (0)
-#define DEBUG9_10(x)    do {x;} while (0)
-#else
-#define DEBUG9(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_10)
-#define DEBUG10(x)      do {x;} while (0)
-#define DEBUG9_10(x)	do {x;} while (0)
-#else
-#define DEBUG10(x)	do {} while (0)
-  #if !defined(DEBUG9_10)
-  #define DEBUG9_10(x)	do {} while (0)
-  #endif
-#endif
-
-#if defined(QL_DEBUG_LEVEL_11)
-#define DEBUG11(x)      do{x;} while(0)
-#if !defined(DEBUG3_11)
-#define DEBUG3_11(x)    do{x;} while(0)
-#endif
-#else
-#define DEBUG11(x)	do{} while(0)
-  #if !defined(QL_DEBUG_LEVEL_3)
-  #define DEBUG3_11(x)	do{} while(0)
-  #endif
-#endif
-
-#if defined(QL_DEBUG_LEVEL_12)
-#define DEBUG12(x)      do {x;} while (0)
-#else
-#define DEBUG12(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_13)
-#define DEBUG13(x)      do {x;} while (0)
-#else
-#define DEBUG13(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_14)
-#define DEBUG14(x)      do {x;} while (0)
-#else
-#define DEBUG14(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_15)
-#define DEBUG15(x)      do {x;} while (0)
-#else
-#define DEBUG15(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_16)
-#define DEBUG16(x)	do {x;} while (0)
-#else
-#define DEBUG16(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_17)
-#define DEBUG17(x)	do {x;} while (0)
-#else
-#define DEBUG17(x)	do {} while (0)
-#endif
-
-#if defined(QL_DEBUG_LEVEL_18)
-#define DEBUG18(x)	do {if (ql2xextended_error_logging) x; } while (0)
-#else
-#define DEBUG18(x)	do {} while (0)
-#endif
-
-
-/*
  * Firmware Dump structure definition
  */
 
@@ -370,3 +230,50 @@
 		struct qla81xx_fw_dump isp81;
 	} isp;
 };
+
+#define QL_MSGHDR "qla2xxx"
+
+#define ql_log_fatal		0 /* display fatal errors */
+#define ql_log_warn		1 /* display critical errors */
+#define ql_log_info		2 /* display all recovered errors */
+#define ql_log_all		3 /* This value is only used by ql_errlev.
+				   * No messages will use this value.
+				   * This should be always highest value
+				   * as compared to other log levels.
+				   */
+
+extern int ql_errlev;
+
+void
+ql_dbg(uint32_t, scsi_qla_host_t *vha, int32_t, char *, ...);
+void
+ql_dbg_pci(uint32_t, struct pci_dev *pdev, int32_t, char *, ...);
+
+void
+ql_log(uint32_t, scsi_qla_host_t *vha, int32_t, char *, ...);
+void
+ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, char *, ...);
+
+/* Debug Levels */
+/* The 0x40000000 is the max value any debug level can have
+ * as ql2xextended_error_logging is of type signed int
+ */
+#define ql_dbg_init	0x40000000 /* Init Debug */
+#define ql_dbg_mbx	0x20000000 /* MBX Debug */
+#define ql_dbg_disc	0x10000000 /* Device Discovery Debug */
+#define ql_dbg_io	0x08000000 /* IO Tracing Debug */
+#define ql_dbg_dpc	0x04000000 /* DPC Thead Debug */
+#define ql_dbg_async	0x02000000 /* Async events Debug */
+#define ql_dbg_timer	0x01000000 /* Timer Debug */
+#define ql_dbg_user	0x00800000 /* User Space Interations Debug */
+#define ql_dbg_taskm	0x00400000 /* Task Management Debug */
+#define ql_dbg_aer	0x00200000 /* AER/EEH Debug */
+#define ql_dbg_multiq	0x00100000 /* MultiQ Debug */
+#define ql_dbg_p3p	0x00080000 /* P3P specific Debug */
+#define ql_dbg_vport	0x00040000 /* Virtual Port Debug */
+#define ql_dbg_buffer	0x00020000 /* For dumping the buffer/regs */
+#define ql_dbg_misc	0x00010000 /* For dumping everything that is not
+				    * not covered by upper categories
+				    */
+
+#define QL_DBG_BUF_LEN	512
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index a5a4e12..0b4c2b7 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -64,7 +64,7 @@
 	/* Pause tracing to flush FCE buffers. */
 	rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd);
 	if (rval)
-		qla_printk(KERN_WARNING, ha,
+		ql_dbg(ql_dbg_user, vha, 0x705c,
 		    "DebugFS: Unable to disable FCE (%d).\n", rval);
 
 	ha->flags.fce_enabled = 0;
@@ -92,7 +92,7 @@
 	rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs,
 	    ha->fce_mb, &ha->fce_bufs);
 	if (rval) {
-		qla_printk(KERN_WARNING, ha,
+		ql_dbg(ql_dbg_user, vha, 0x700d,
 		    "DebugFS: Unable to reinitialize FCE (%d).\n", rval);
 		ha->flags.fce_enabled = 0;
 	}
@@ -125,8 +125,8 @@
 	atomic_set(&qla2x00_dfs_root_count, 0);
 	qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL);
 	if (!qla2x00_dfs_root) {
-		qla_printk(KERN_NOTICE, ha,
-		    "DebugFS: Unable to create root directory.\n");
+		ql_log(ql_log_warn, vha, 0x00f7,
+		    "Unable to create debugfs root directory.\n");
 		goto out;
 	}
 
@@ -137,8 +137,8 @@
 	mutex_init(&ha->fce_mutex);
 	ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root);
 	if (!ha->dfs_dir) {
-		qla_printk(KERN_NOTICE, ha,
-		    "DebugFS: Unable to create ha directory.\n");
+		ql_log(ql_log_warn, vha, 0x00f8,
+		    "Unable to create debugfs ha directory.\n");
 		goto out;
 	}
 
@@ -148,8 +148,8 @@
 	ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha,
 	    &dfs_fce_ops);
 	if (!ha->dfs_fce) {
-		qla_printk(KERN_NOTICE, ha,
-		    "DebugFS: Unable to fce node.\n");
+		ql_log(ql_log_warn, vha, 0x00f9,
+		    "Unable to create debugfs fce node.\n");
 		goto out;
 	}
 out:
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 0b38122..29b1a3e 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -185,7 +185,7 @@
 int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
 						uint16_t, uint16_t, uint8_t);
 extern int qla2x00_start_sp(srb_t *);
-extern uint16_t qla24xx_calc_iocbs(uint16_t);
+extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t);
 extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
 extern int qla24xx_dif_start_scsi(srb_t *);
 
@@ -439,6 +439,9 @@
 extern void qla2x00_dump_regs(scsi_qla_host_t *);
 extern void qla2x00_dump_buffer(uint8_t *, uint32_t);
 extern void qla2x00_dump_buffer_zipped(uint8_t *, uint32_t);
+extern void ql_dump_regs(uint32_t, scsi_qla_host_t *, int32_t);
+extern void ql_dump_buffer(uint32_t, scsi_qla_host_t *, int32_t,
+	uint8_t *, uint32_t);
 
 /*
  * Global Function Prototypes in qla_gs.c source file.
@@ -478,7 +481,8 @@
 extern int qla2x00_echo_test(scsi_qla_host_t *,
 	struct msg_echo_lb *, uint16_t *);
 extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *);
-extern int qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *, uint8_t);
+extern int qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *,
+	struct qla_fcp_prio_cfg *, uint8_t);
 
 /*
  * Global Function Prototypes in qla_dfs.c source file.
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 8cd9066..37937aa 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -121,11 +121,10 @@
 
 	rval = QLA_FUNCTION_FAILED;
 	if (ms_pkt->entry_status != 0) {
-		DEBUG2_3(printk(KERN_WARNING "scsi(%ld): %s failed, error status "
-		    "(%x) on port_id: %02x%02x%02x.\n",
-		    vha->host_no, routine, ms_pkt->entry_status,
-		    vha->d_id.b.domain, vha->d_id.b.area,
-		    vha->d_id.b.al_pa));
+		ql_dbg(ql_dbg_disc, vha, 0x2031,
+		    "%s failed, error status (%x) on port_id: %02x%02x%02x.\n",
+		    routine, ms_pkt->entry_status, vha->d_id.b.domain,
+		    vha->d_id.b.area, vha->d_id.b.al_pa);
 	} else {
 		if (IS_FWI2_CAPABLE(ha))
 			comp_status = le16_to_cpu(
@@ -138,24 +137,24 @@
 		case CS_DATA_OVERRUN:		/* Overrun? */
 			if (ct_rsp->header.response !=
 			    __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
-				DEBUG2_3(printk("scsi(%ld): %s failed, "
-				    "rejected request on port_id: %02x%02x%02x\n",
-				    vha->host_no, routine,
+				ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2077,
+				    "%s failed rejected request on port_id: "
+				    "%02x%02x%02x.\n", routine,
 				    vha->d_id.b.domain, vha->d_id.b.area,
-				    vha->d_id.b.al_pa));
-				DEBUG2_3(qla2x00_dump_buffer(
-				    (uint8_t *)&ct_rsp->header,
-				    sizeof(struct ct_rsp_hdr)));
+				    vha->d_id.b.al_pa);
+				ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha,
+				    0x2078, (uint8_t *)&ct_rsp->header,
+				    sizeof(struct ct_rsp_hdr));
 				rval = QLA_INVALID_COMMAND;
 			} else
 				rval = QLA_SUCCESS;
 			break;
 		default:
-			DEBUG2_3(printk("scsi(%ld): %s failed, completion "
-			    "status (%x) on port_id: %02x%02x%02x.\n",
-			    vha->host_no, routine, comp_status,
+			ql_dbg(ql_dbg_disc, vha, 0x2033,
+			    "%s failed, completion status (%x) on port_id: "
+			    "%02x%02x%02x.\n", routine, comp_status,
 			    vha->d_id.b.domain, vha->d_id.b.area,
-			    vha->d_id.b.al_pa));
+			    vha->d_id.b.al_pa);
 			break;
 		}
 	}
@@ -202,8 +201,8 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2062,
+		    "GA_NXT issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GA_NXT") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
@@ -222,11 +221,10 @@
 		    ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE)
 			fcport->d_id.b.domain = 0xf0;
 
-		DEBUG2_3(printk("scsi(%ld): GA_NXT entry - "
-		    "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+		ql_dbg(ql_dbg_disc, vha, 0x2063,
+		    "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
 		    "pn %02x%02x%02x%02x%02x%02x%02x%02x "
-		    "portid=%02x%02x%02x.\n",
-		    vha->host_no,
+		    "port_id=%02x%02x%02x.\n",
 		    fcport->node_name[0], fcport->node_name[1],
 		    fcport->node_name[2], fcport->node_name[3],
 		    fcport->node_name[4], fcport->node_name[5],
@@ -236,7 +234,7 @@
 		    fcport->port_name[4], fcport->port_name[5],
 		    fcport->port_name[6], fcport->port_name[7],
 		    fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa));
+		    fcport->d_id.b.al_pa);
 	}
 
 	return (rval);
@@ -287,8 +285,8 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2055,
+		    "GID_PT issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GID_PT") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
@@ -364,8 +362,8 @@
 		    sizeof(ms_iocb_entry_t));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed "
-			    "(%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x2056,
+			    "GPN_ID issue IOCB failed (%d).\n", rval);
 		} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
 		    "GPN_ID") != QLA_SUCCESS) {
 			rval = QLA_FUNCTION_FAILED;
@@ -424,8 +422,8 @@
 		    sizeof(ms_iocb_entry_t));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed "
-			    "(%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x2057,
+			    "GNN_ID issue IOCB failed (%d).\n", rval);
 		} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
 		    "GNN_ID") != QLA_SUCCESS) {
 			rval = QLA_FUNCTION_FAILED;
@@ -434,11 +432,10 @@
 			memcpy(list[i].node_name,
 			    ct_rsp->rsp.gnn_id.node_name, WWN_SIZE);
 
-			DEBUG2_3(printk("scsi(%ld): GID_PT entry - "
-			    "nn %02x%02x%02x%02x%02x%02x%02x%02x "
-			    "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+			ql_dbg(ql_dbg_disc, vha, 0x2058,
+			    "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02X%02x "
+			    "pn %02x%02x%02x%02x%02x%02x%02X%02x "
 			    "portid=%02x%02x%02x.\n",
-			    vha->host_no,
 			    list[i].node_name[0], list[i].node_name[1],
 			    list[i].node_name[2], list[i].node_name[3],
 			    list[i].node_name[4], list[i].node_name[5],
@@ -448,7 +445,7 @@
 			    list[i].port_name[4], list[i].port_name[5],
 			    list[i].port_name[6], list[i].port_name[7],
 			    list[i].d_id.b.domain, list[i].d_id.b.area,
-			    list[i].d_id.b.al_pa));
+			    list[i].d_id.b.al_pa);
 		}
 
 		/* Last device exit. */
@@ -499,14 +496,14 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2043,
+		    "RFT_ID issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2044,
+		    "RFT_ID exiting normally.\n");
 	}
 
 	return (rval);
@@ -528,8 +525,8 @@
 	struct ct_sns_rsp	*ct_rsp;
 
 	if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-		DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on "
-		    "ISP2100/ISP2200.\n", vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2046,
+		    "RFF_ID call not supported on ISP2100/ISP2200.\n");
 		return (QLA_SUCCESS);
 	}
 
@@ -556,14 +553,14 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2047,
+		    "RFF_ID issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2048,
+		    "RFF_ID exiting normally.\n");
 	}
 
 	return (rval);
@@ -609,14 +606,14 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x204d,
+		    "RNN_ID issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x204e,
+		    "RNN_ID exiting normally.\n");
 	}
 
 	return (rval);
@@ -647,8 +644,8 @@
 	struct ct_sns_rsp	*ct_rsp;
 
 	if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-		DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on "
-		    "ISP2100/ISP2200.\n", vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2050,
+		    "RSNN_ID call unsupported on ISP2100/ISP2200.\n");
 		return (QLA_SUCCESS);
 	}
 
@@ -682,14 +679,14 @@
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2051,
+		    "RSNN_NN issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2052,
+		    "RSNN_NN exiting normally.\n");
 	}
 
 	return (rval);
@@ -757,13 +754,14 @@
 	    sizeof(struct sns_cmd_pkt));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x205f,
+		    "GA_NXT Send SNS failed (%d).\n", rval);
 	} else if (sns_cmd->p.gan_data[8] != 0x80 ||
 	    sns_cmd->p.gan_data[9] != 0x02) {
-		DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
-		    "ga_nxt_rsp:\n", vha->host_no));
-		DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16));
+		ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207d,
+		    "GA_NXT failed, rejected request ga_nxt_rsp:\n");
+		ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2074,
+		    sns_cmd->p.gan_data, 16);
 		rval = QLA_FUNCTION_FAILED;
 	} else {
 		/* Populate fc_port_t entry. */
@@ -778,11 +776,10 @@
 		    sns_cmd->p.gan_data[16] != NS_NL_PORT_TYPE)
 			fcport->d_id.b.domain = 0xf0;
 
-		DEBUG2_3(printk("scsi(%ld): GA_NXT entry - "
-		    "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+		ql_dbg(ql_dbg_disc, vha, 0x2061,
+		    "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
 		    "pn %02x%02x%02x%02x%02x%02x%02x%02x "
-		    "portid=%02x%02x%02x.\n",
-		    vha->host_no,
+		    "port_id=%02x%02x%02x.\n",
 		    fcport->node_name[0], fcport->node_name[1],
 		    fcport->node_name[2], fcport->node_name[3],
 		    fcport->node_name[4], fcport->node_name[5],
@@ -792,7 +789,7 @@
 		    fcport->port_name[4], fcport->port_name[5],
 		    fcport->port_name[6], fcport->port_name[7],
 		    fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa));
+		    fcport->d_id.b.al_pa);
 	}
 
 	return (rval);
@@ -831,13 +828,14 @@
 	    sizeof(struct sns_cmd_pkt));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x206d,
+		    "GID_PT Send SNS failed (%d).\n", rval);
 	} else if (sns_cmd->p.gid_data[8] != 0x80 ||
 	    sns_cmd->p.gid_data[9] != 0x02) {
-		DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
-		    "gid_rsp:\n", vha->host_no));
-		DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16));
+		ql_dbg(ql_dbg_disc, vha, 0x202f,
+		    "GID_PT failed, rejected request, gid_rsp:\n");
+		ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2081,
+		    sns_cmd->p.gid_data, 16);
 		rval = QLA_FUNCTION_FAILED;
 	} else {
 		/* Set port IDs in switch info list. */
@@ -900,13 +898,14 @@
 		    GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed "
-			    "(%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x2032,
+			    "GPN_ID Send SNS failed (%d).\n", rval);
 		} else if (sns_cmd->p.gpn_data[8] != 0x80 ||
 		    sns_cmd->p.gpn_data[9] != 0x02) {
-			DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
-			    "request, gpn_rsp:\n", vha->host_no));
-			DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16));
+			ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207e,
+			    "GPN_ID failed, rejected request, gpn_rsp:\n");
+			ql_dump_buffer(ql_dbg_disc, vha, 0x207f,
+			    sns_cmd->p.gpn_data, 16);
 			rval = QLA_FUNCTION_FAILED;
 		} else {
 			/* Save portname */
@@ -955,24 +954,24 @@
 		    GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed "
-			    "(%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x203f,
+			    "GNN_ID Send SNS failed (%d).\n", rval);
 		} else if (sns_cmd->p.gnn_data[8] != 0x80 ||
 		    sns_cmd->p.gnn_data[9] != 0x02) {
-			DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
-			    "request, gnn_rsp:\n", vha->host_no));
-			DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16));
+			ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2082,
+			    "GNN_ID failed, rejected request, gnn_rsp:\n");
+			ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207a,
+			    sns_cmd->p.gnn_data, 16);
 			rval = QLA_FUNCTION_FAILED;
 		} else {
 			/* Save nodename */
 			memcpy(list[i].node_name, &sns_cmd->p.gnn_data[16],
 			    WWN_SIZE);
 
-			DEBUG2_3(printk("scsi(%ld): GID_PT entry - "
-			    "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+			ql_dbg(ql_dbg_disc, vha, 0x206e,
+			    "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
 			    "pn %02x%02x%02x%02x%02x%02x%02x%02x "
-			    "portid=%02x%02x%02x.\n",
-			    vha->host_no,
+			    "port_id=%02x%02x%02x.\n",
 			    list[i].node_name[0], list[i].node_name[1],
 			    list[i].node_name[2], list[i].node_name[3],
 			    list[i].node_name[4], list[i].node_name[5],
@@ -982,7 +981,7 @@
 			    list[i].port_name[4], list[i].port_name[5],
 			    list[i].port_name[6], list[i].port_name[7],
 			    list[i].d_id.b.domain, list[i].d_id.b.area,
-			    list[i].d_id.b.al_pa));
+			    list[i].d_id.b.al_pa);
 		}
 
 		/* Last device exit. */
@@ -1025,17 +1024,18 @@
 	    sizeof(struct sns_cmd_pkt));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2060,
+		    "RFT_ID Send SNS failed (%d).\n", rval);
 	} else if (sns_cmd->p.rft_data[8] != 0x80 ||
 	    sns_cmd->p.rft_data[9] != 0x02) {
-		DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, "
-		    "rft_rsp:\n", vha->host_no));
-		DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16));
+		ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2083,
+		    "RFT_ID failed, rejected request rft_rsp:\n");
+		ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2080,
+		    sns_cmd->p.rft_data, 16);
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2073,
+		    "RFT_ID exiting normally.\n");
 	}
 
 	return (rval);
@@ -1081,17 +1081,18 @@
 	    sizeof(struct sns_cmd_pkt));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x204a,
+		    "RNN_ID Send SNS failed (%d).\n", rval);
 	} else if (sns_cmd->p.rnn_data[8] != 0x80 ||
 	    sns_cmd->p.rnn_data[9] != 0x02) {
-		DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, "
-		    "rnn_rsp:\n", vha->host_no));
-		DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16));
+		ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207b,
+		    "RNN_ID failed, rejected request, rnn_rsp:\n");
+		ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207c,
+		    sns_cmd->p.rnn_data, 16);
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x204c,
+		    "RNN_ID exiting normally.\n");
 	}
 
 	return (rval);
@@ -1116,10 +1117,10 @@
 	ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
 	    mb, BIT_1|BIT_0);
 	if (mb[0] != MBS_COMMAND_COMPLETE) {
-		DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: "
-		    "loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n",
-		    __func__, vha->host_no, vha->mgmt_svr_loop_id, mb[0], mb[1],
-		    mb[2], mb[6], mb[7]));
+		ql_dbg(ql_dbg_disc, vha, 0x2024,
+		    "Failed management_server login: loopid=%x mb[0]=%x "
+		    "mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x.\n",
+		    vha->mgmt_svr_loop_id, mb[0], mb[1], mb[2], mb[6], mb[7]);
 		ret = QLA_FUNCTION_FAILED;
 	} else
 		vha->flags.management_server_logged_in = 1;
@@ -1292,11 +1293,12 @@
 	memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE);
 	size += 4 + WWN_SIZE;
 
-	DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n",
-	    __func__, vha->host_no,
-	    eiter->a.node_name[0], eiter->a.node_name[1], eiter->a.node_name[2],
-	    eiter->a.node_name[3], eiter->a.node_name[4], eiter->a.node_name[5],
-	    eiter->a.node_name[6], eiter->a.node_name[7]));
+	ql_dbg(ql_dbg_disc, vha, 0x2025,
+	    "NodeName = %02x%02x%02x%02x%02x%02x%02x%02x.\n",
+	    eiter->a.node_name[0], eiter->a.node_name[1],
+	    eiter->a.node_name[2], eiter->a.node_name[3],
+	    eiter->a.node_name[4], eiter->a.node_name[5],
+	    eiter->a.node_name[6], eiter->a.node_name[7]);
 
 	/* Manufacturer. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1307,8 +1309,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, vha->host_no,
-	    eiter->a.manufacturer));
+	ql_dbg(ql_dbg_disc, vha, 0x2026,
+	    "Manufacturer = %s.\n", eiter->a.manufacturer);
 
 	/* Serial number. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1320,8 +1322,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, vha->host_no,
-	    eiter->a.serial_num));
+	ql_dbg(ql_dbg_disc, vha, 0x2027,
+	    "Serial no. = %s.\n", eiter->a.serial_num);
 
 	/* Model name. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1332,8 +1334,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, vha->host_no,
-	    eiter->a.model));
+	ql_dbg(ql_dbg_disc, vha, 0x2028,
+	    "Model Name = %s.\n", eiter->a.model);
 
 	/* Model description. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1345,8 +1347,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, vha->host_no,
-	    eiter->a.model_desc));
+	ql_dbg(ql_dbg_disc, vha, 0x2029,
+	    "Model Desc = %s.\n", eiter->a.model_desc);
 
 	/* Hardware version. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1357,8 +1359,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, vha->host_no,
-	    eiter->a.hw_version));
+	ql_dbg(ql_dbg_disc, vha, 0x202a,
+	    "Hardware ver = %s.\n", eiter->a.hw_version);
 
 	/* Driver version. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1369,8 +1371,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, vha->host_no,
-	    eiter->a.driver_version));
+	ql_dbg(ql_dbg_disc, vha, 0x202b,
+	    "Driver ver = %s.\n", eiter->a.driver_version);
 
 	/* Option ROM version. */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1381,8 +1383,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, vha->host_no,
-	    eiter->a.orom_version));
+	ql_dbg(ql_dbg_disc, vha , 0x202c,
+	    "Optrom vers = %s.\n", eiter->a.orom_version);
 
 	/* Firmware version */
 	eiter = (struct ct_fdmi_hba_attr *) (entries + size);
@@ -1393,44 +1395,46 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, vha->host_no,
-	    eiter->a.fw_version));
+	ql_dbg(ql_dbg_disc, vha, 0x202d,
+	    "Firmware vers = %s.\n", eiter->a.fw_version);
 
 	/* Update MS request size. */
 	qla2x00_update_ms_fdmi_iocb(vha, size + 16);
 
-	DEBUG13(printk("%s(%ld): RHBA identifier="
-	    "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
-	    vha->host_no, ct_req->req.rhba.hba_identifier[0],
+	ql_dbg(ql_dbg_disc, vha, 0x202e,
+	    "RHBA identifier = "
+	    "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n",
+	    ct_req->req.rhba.hba_identifier[0],
 	    ct_req->req.rhba.hba_identifier[1],
 	    ct_req->req.rhba.hba_identifier[2],
 	    ct_req->req.rhba.hba_identifier[3],
 	    ct_req->req.rhba.hba_identifier[4],
 	    ct_req->req.rhba.hba_identifier[5],
 	    ct_req->req.rhba.hba_identifier[6],
-	    ct_req->req.rhba.hba_identifier[7], size));
-	DEBUG13(qla2x00_dump_buffer(entries, size));
+	    ct_req->req.rhba.hba_identifier[7], size);
+	ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2076,
+	    entries, size);
 
 	/* Execute MS IOCB */
 	rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RHBA issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2030,
+		    "RHBA issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RHBA") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 		if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM &&
 		    ct_rsp->header.explanation_code ==
 		    CT_EXPL_ALREADY_REGISTERED) {
-			DEBUG2_13(printk("%s(%ld): HBA already registered.\n",
-			    __func__, vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x2034,
+			    "HBA already registered.\n");
 			rval = QLA_ALREADY_REGISTERED;
 		}
 	} else {
-		DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2035,
+		    "RHBA exiting normally.\n");
 	}
 
 	return rval;
@@ -1464,26 +1468,26 @@
 	/* Prepare FDMI command arguments -- portname. */
 	memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE);
 
-	DEBUG13(printk("%s(%ld): DHBA portname="
-	    "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, vha->host_no,
+	ql_dbg(ql_dbg_disc, vha, 0x2036,
+	    "DHBA portname = %02x%02x%02x%02x%02x%02x%02x%02x.\n",
 	    ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1],
 	    ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3],
 	    ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5],
-	    ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]));
+	    ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]);
 
 	/* Execute MS IOCB */
 	rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): DHBA issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2037,
+		    "DHBA issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "DHBA") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2038,
+		    "DHBA exiting normally.\n");
 	}
 
 	return rval;
@@ -1534,9 +1538,10 @@
 	eiter->a.fc4_types[2] = 0x01;
 	size += 4 + 32;
 
-	DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__,
-		vha->host_no, eiter->a.fc4_types[2],
-		eiter->a.fc4_types[1]));
+	ql_dbg(ql_dbg_disc, vha, 0x2039,
+	    "FC4_TYPES=%02x %02x.\n",
+	    eiter->a.fc4_types[2],
+	    eiter->a.fc4_types[1]);
 
 	/* Supported speed. */
 	eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1561,8 +1566,8 @@
 		    FDMI_PORT_SPEED_1GB);
 	size += 4 + 4;
 
-	DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, vha->host_no,
-	    eiter->a.sup_speed));
+	ql_dbg(ql_dbg_disc, vha, 0x203a,
+	    "Supported_Speed=%x.\n", eiter->a.sup_speed);
 
 	/* Current speed. */
 	eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1596,8 +1601,8 @@
 	}
 	size += 4 + 4;
 
-	DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, vha->host_no,
-	    eiter->a.cur_speed));
+	ql_dbg(ql_dbg_disc, vha, 0x203b,
+	    "Current_Speed=%x.\n", eiter->a.cur_speed);
 
 	/* Max frame size. */
 	eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1609,8 +1614,8 @@
 	eiter->a.max_frame_size = cpu_to_be32(max_frame_size);
 	size += 4 + 4;
 
-	DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, vha->host_no,
-	    eiter->a.max_frame_size));
+	ql_dbg(ql_dbg_disc, vha, 0x203c,
+	    "Max_Frame_Size=%x.\n", eiter->a.max_frame_size);
 
 	/* OS device name. */
 	eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1621,8 +1626,8 @@
 	eiter->len = cpu_to_be16(4 + alen);
 	size += 4 + alen;
 
-	DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, vha->host_no,
-	    eiter->a.os_dev_name));
+	ql_dbg(ql_dbg_disc, vha, 0x204b,
+	    "OS_Device_Name=%s.\n", eiter->a.os_dev_name);
 
 	/* Hostname. */
 	if (strlen(fc_host_system_hostname(vha->host))) {
@@ -1637,35 +1642,36 @@
 		eiter->len = cpu_to_be16(4 + alen);
 		size += 4 + alen;
 
-		DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__,
-		    vha->host_no, eiter->a.host_name));
+		ql_dbg(ql_dbg_disc, vha, 0x203d,
+		    "HostName=%s.\n", eiter->a.host_name);
 	}
 
 	/* Update MS request size. */
 	qla2x00_update_ms_fdmi_iocb(vha, size + 16);
 
-	DEBUG13(printk("%s(%ld): RPA portname="
-	    "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
-	    vha->host_no, ct_req->req.rpa.port_name[0],
-	    ct_req->req.rpa.port_name[1], ct_req->req.rpa.port_name[2],
-	    ct_req->req.rpa.port_name[3], ct_req->req.rpa.port_name[4],
-	    ct_req->req.rpa.port_name[5], ct_req->req.rpa.port_name[6],
-	    ct_req->req.rpa.port_name[7], size));
-	DEBUG13(qla2x00_dump_buffer(entries, size));
+	ql_dbg(ql_dbg_disc, vha, 0x203e,
+	    "RPA portname= %02x%02x%02x%02x%02X%02x%02x%02x size=%d.\n",
+	    ct_req->req.rpa.port_name[0], ct_req->req.rpa.port_name[1],
+	    ct_req->req.rpa.port_name[2], ct_req->req.rpa.port_name[3],
+	    ct_req->req.rpa.port_name[4], ct_req->req.rpa.port_name[5],
+	    ct_req->req.rpa.port_name[6], ct_req->req.rpa.port_name[7],
+	    size);
+	ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2079,
+	    entries, size);
 
 	/* Execute MS IOCB */
 	rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
 	    sizeof(ms_iocb_entry_t));
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3(printk("scsi(%ld): RPA issue IOCB failed (%d).\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2040,
+		    "RPA issue IOCB failed (%d).\n", rval);
 	} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RPA") !=
 	    QLA_SUCCESS) {
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("scsi(%ld): RPA exiting normally.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2041,
+		    "RPA exiting nornally.\n");
 	}
 
 	return rval;
@@ -1749,8 +1755,8 @@
 		    sizeof(ms_iocb_entry_t));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB "
-			    "failed (%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x2023,
+			    "GFPN_ID issue IOCB failed (%d).\n", rval);
 		} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
 		    "GFPN_ID") != QLA_SUCCESS) {
 			rval = QLA_FUNCTION_FAILED;
@@ -1860,8 +1866,8 @@
 		    sizeof(ms_iocb_entry_t));
 		if (rval != QLA_SUCCESS) {
 			/*EMPTY*/
-			DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB "
-			    "failed (%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x2059,
+			    "GPSC issue IOCB failed (%d).\n", rval);
 		} else if ((rval = qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
 		    "GPSC")) != QLA_SUCCESS) {
 			/* FM command unsupported? */
@@ -1870,9 +1876,9 @@
 				CT_REASON_INVALID_COMMAND_CODE ||
 			     ct_rsp->header.reason_code ==
 				CT_REASON_COMMAND_UNSUPPORTED)) {
-				DEBUG2(printk("scsi(%ld): GPSC command "
-				    "unsupported, disabling query...\n",
-				    vha->host_no));
+				ql_dbg(ql_dbg_disc, vha, 0x205a,
+				    "GPSC command unsupported, disabling "
+				    "query.\n");
 				ha->flags.gpsc_supported = 0;
 				rval = QLA_FUNCTION_FAILED;
 				break;
@@ -1898,9 +1904,10 @@
 				break;
 			}
 
-			DEBUG2_3(printk("scsi(%ld): GPSC ext entry - "
-			    "fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x "
-			    "speed=%04x.\n", vha->host_no,
+			ql_dbg(ql_dbg_disc, vha, 0x205b,
+			    "GPSC ext entry - fpn "
+			    "%02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x "
+			    "speed=%04x.\n",
 			    list[i].fabric_port_name[0],
 			    list[i].fabric_port_name[1],
 			    list[i].fabric_port_name[2],
@@ -1910,7 +1917,7 @@
 			    list[i].fabric_port_name[6],
 			    list[i].fabric_port_name[7],
 			    be16_to_cpu(ct_rsp->rsp.gpsc.speeds),
-			    be16_to_cpu(ct_rsp->rsp.gpsc.speed)));
+			    be16_to_cpu(ct_rsp->rsp.gpsc.speed));
 		}
 
 		/* Last device exit. */
@@ -1968,14 +1975,12 @@
 		   sizeof(ms_iocb_entry_t));
 
 		if (rval != QLA_SUCCESS) {
-			DEBUG2_3(printk(KERN_INFO
-			    "scsi(%ld): GFF_ID issue IOCB failed "
-			    "(%d).\n", vha->host_no, rval));
+			ql_dbg(ql_dbg_disc, vha, 0x205c,
+			    "GFF_ID issue IOCB failed (%d).\n", rval);
 		} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
 			       "GFF_ID") != QLA_SUCCESS) {
-			DEBUG2_3(printk(KERN_INFO
-			    "scsi(%ld): GFF_ID IOCB status had a "
-			    "failure status code\n", vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x205d,
+			    "GFF_ID IOCB status had a failure status code.\n");
 		} else {
 			fcp_scsi_features =
 			   ct_rsp->rsp.gff_id.fc4_features[GFF_FCP_SCSI_OFFSET];
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 920b76b..def6942 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -153,11 +153,10 @@
 	fc_port_t *fcport = sp->fcport;
 	struct srb_ctx *ctx = sp->ctx;
 
-	DEBUG2(printk(KERN_WARNING
-		"scsi(%ld:%x): Async-%s timeout - portid=%02x%02x%02x.\n",
-		fcport->vha->host_no, sp->handle,
-		ctx->name, fcport->d_id.b.domain,
-		fcport->d_id.b.area, fcport->d_id.b.al_pa));
+	ql_dbg(ql_dbg_disc, fcport->vha, 0x2071,
+	    "Async-%s timeout - portid=%02x%02x%02x.\n",
+	    ctx->name, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa);
 
 	fcport->flags &= ~FCF_ASYNC_SENT;
 	if (ctx->type == SRB_LOGIN_CMD) {
@@ -211,11 +210,10 @@
 	if (rval != QLA_SUCCESS)
 		goto done_free_sp;
 
-	DEBUG2(printk(KERN_DEBUG
-	    "scsi(%ld:%x): Async-login - loop-id=%x portid=%02x%02x%02x "
-	    "retries=%d.\n", fcport->vha->host_no, sp->handle, fcport->loop_id,
-	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
-	    fcport->login_retry));
+	ql_dbg(ql_dbg_disc, vha, 0x2072,
+	    "Async-login - loopid=%x portid=%02x%02x%02x retries=%d.\n",
+	    fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa, fcport->login_retry);
 	return rval;
 
 done_free_sp:
@@ -259,10 +257,10 @@
 	if (rval != QLA_SUCCESS)
 		goto done_free_sp;
 
-	DEBUG2(printk(KERN_DEBUG
-	    "scsi(%ld:%x): Async-logout - loop-id=%x portid=%02x%02x%02x.\n",
-	    fcport->vha->host_no, sp->handle, fcport->loop_id,
-	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa));
+	ql_dbg(ql_dbg_disc, vha, 0x2070,
+	    "Async-logout - loop-id=%x portid=%02x%02x%02x.\n",
+	    fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa);
 	return rval;
 
 done_free_sp:
@@ -309,11 +307,10 @@
 	if (rval != QLA_SUCCESS)
 		goto done_free_sp;
 
-	DEBUG2(printk(KERN_DEBUG
-	    "scsi(%ld:%x): Async-adisc - loop-id=%x portid=%02x%02x%02x.\n",
-	    fcport->vha->host_no, sp->handle, fcport->loop_id,
-	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa));
-
+	ql_dbg(ql_dbg_disc, vha, 0x206f,
+	    "Async-adisc - loopid=%x portid=%02x%02x%02x.\n",
+	    fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa);
 	return rval;
 
 done_free_sp:
@@ -362,11 +359,10 @@
 	if (rval != QLA_SUCCESS)
 		goto done_free_sp;
 
-	DEBUG2(printk(KERN_DEBUG
-	    "scsi(%ld:%x): Async-tmf - loop-id=%x portid=%02x%02x%02x.\n",
-	    fcport->vha->host_no, sp->handle, fcport->loop_id,
-	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa));
-
+	ql_dbg(ql_dbg_taskm, vha, 0x802f,
+	    "Async-tmf loop-id=%x portid=%02x%02x%02x.\n",
+	    fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+	    fcport->d_id.b.al_pa);
 	return rval;
 
 done_free_sp:
@@ -471,9 +467,8 @@
 		flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID);
 
 	if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) {
-		DEBUG2_3_11(printk(KERN_WARNING
-			"%s(%ld): TM IOCB failed (%x).\n",
-			__func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_taskm, vha, 0x8030,
+		    "TM IOCB failed (%x).\n", rval);
 	}
 
 	return;
@@ -519,11 +514,12 @@
 	set_bit(0, ha->req_qid_map);
 	set_bit(0, ha->rsp_qid_map);
 
-	qla_printk(KERN_INFO, ha, "Configuring PCI space...\n");
+	ql_log(ql_log_info, vha, 0x0040,
+	    "Configuring PCI space...\n");
 	rval = ha->isp_ops->pci_config(vha);
 	if (rval) {
-		DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n",
-		    vha->host_no));
+		ql_log(ql_log_warn, vha, 0x0044,
+		    "Unable to configure PCI space.\n");
 		return (rval);
 	}
 
@@ -531,20 +527,21 @@
 
 	rval = qla2xxx_get_flash_info(vha);
 	if (rval) {
-		DEBUG2(printk("scsi(%ld): Unable to validate FLASH data.\n",
-		    vha->host_no));
+		ql_log(ql_log_fatal, vha, 0x004f,
+		    "Unable to validate FLASH data.\n");
 		return (rval);
 	}
 
 	ha->isp_ops->get_flash_version(vha, req->ring);
-
-	qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n");
+	ql_log(ql_log_info, vha, 0x0061,
+	    "Configure NVRAM parameters...\n");
 
 	ha->isp_ops->nvram_config(vha);
 
 	if (ha->flags.disable_serdes) {
 		/* Mask HBA via NVRAM settings? */
-		qla_printk(KERN_INFO, ha, "Masking HBA WWPN "
+		ql_log(ql_log_info, vha, 0x0077,
+		    "Masking HBA WWPN "
 		    "%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n",
 		    vha->port_name[0], vha->port_name[1],
 		    vha->port_name[2], vha->port_name[3],
@@ -553,7 +550,8 @@
 		return QLA_FUNCTION_FAILED;
 	}
 
-	qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n");
+	ql_log(ql_log_info, vha, 0x0078,
+	    "Verifying loaded RISC code...\n");
 
 	if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) {
 		rval = ha->isp_ops->chip_diag(vha);
@@ -567,7 +565,7 @@
 	if (IS_QLA84XX(ha)) {
 		ha->cs84xx = qla84xx_get_chip(vha);
 		if (!ha->cs84xx) {
-			qla_printk(KERN_ERR, ha,
+			ql_log(ql_log_warn, vha, 0x00d0,
 			    "Unable to configure ISP84XX.\n");
 			return QLA_FUNCTION_FAILED;
 		}
@@ -579,8 +577,8 @@
 		/* Issue verify 84xx FW IOCB to complete 84xx initialization */
 		rval = qla84xx_init_chip(vha);
 		if (rval != QLA_SUCCESS) {
-			qla_printk(KERN_ERR, ha,
-				"Unable to initialize ISP84XX.\n");
+			ql_log(ql_log_warn, vha, 0x00d4,
+			    "Unable to initialize ISP84XX.\n");
 		qla84xx_put_chip(vha);
 		}
 	}
@@ -797,9 +795,7 @@
 	rval = QLA_FUNCTION_FAILED;
 
 	if (ha->flags.disable_risc_code_load) {
-		DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n",
-		    vha->host_no));
-		qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n");
+		ql_log(ql_log_info, vha, 0x0079, "RISC CODE NOT loaded.\n");
 
 		/* Verify checksum of loaded RISC code. */
 		rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address);
@@ -810,10 +806,9 @@
 		}
 	}
 
-	if (rval) {
-		DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n",
-		    vha->host_no));
-	}
+	if (rval)
+		ql_dbg(ql_dbg_init, vha, 0x007a,
+		    "**** Load RISC code ****.\n");
 
 	return (rval);
 }
@@ -1105,8 +1100,8 @@
 	/* Assume a failed state */
 	rval = QLA_FUNCTION_FAILED;
 
-	DEBUG3(printk("scsi(%ld): Testing device at %lx.\n",
-	    vha->host_no, (u_long)&reg->flash_address));
+	ql_dbg(ql_dbg_init, vha, 0x007b,
+	    "Testing device at %lx.\n", (u_long)&reg->flash_address);
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -1128,8 +1123,8 @@
 	if (!cnt)
 		goto chip_diag_failed;
 
-	DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_init, vha, 0x007c,
+	    "Reset register cleared by chip reset.\n");
 
 	/* Reset RISC processor. */
 	WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
@@ -1150,7 +1145,7 @@
 		goto chip_diag_failed;
 
 	/* Check product ID of chip */
-	DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", vha->host_no));
+	ql_dbg(ql_dbg_init, vha, 0x007d, "Checking product Id of chip.\n");
 
 	mb[1] = RD_MAILBOX_REG(ha, reg, 1);
 	mb[2] = RD_MAILBOX_REG(ha, reg, 2);
@@ -1158,8 +1153,9 @@
 	mb[4] = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 4));
 	if (mb[1] != PROD_ID_1 || (mb[2] != PROD_ID_2 && mb[2] != PROD_ID_2a) ||
 	    mb[3] != PROD_ID_3) {
-		qla_printk(KERN_WARNING, ha,
-		    "Wrong product ID = 0x%x,0x%x,0x%x\n", mb[1], mb[2], mb[3]);
+		ql_log(ql_log_warn, vha, 0x0062,
+		    "Wrong product ID = 0x%x,0x%x,0x%x.\n",
+		    mb[1], mb[2], mb[3]);
 
 		goto chip_diag_failed;
 	}
@@ -1178,8 +1174,7 @@
 	if (IS_QLA2200(ha) &&
 	    RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) {
 		/* Limit firmware transfer size with a 2200A */
-		DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_init, vha, 0x007e, "Found QLA2200A Chip.\n");
 
 		ha->device_type |= DT_ISP2200A;
 		ha->fw_transfer_size = 128;
@@ -1188,24 +1183,20 @@
 	/* Wrap Incoming Mailboxes Test. */
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
-	DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", vha->host_no));
+	ql_dbg(ql_dbg_init, vha, 0x007f, "Checking mailboxes.\n");
 	rval = qla2x00_mbx_reg_test(vha);
-	if (rval) {
-		DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
-		    vha->host_no));
-		qla_printk(KERN_WARNING, ha,
-		    "Failed mailbox send register test\n");
-	}
-	else {
+	if (rval)
+		ql_log(ql_log_warn, vha, 0x0080,
+		    "Failed mailbox send register test.\n");
+	else
 		/* Flag a successful rval */
 		rval = QLA_SUCCESS;
-	}
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
 chip_diag_failed:
 	if (rval)
-		DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED "
-		    "****\n", vha->host_no));
+		ql_log(ql_log_info, vha, 0x0081,
+		    "Chip diagnostics **** FAILED ****.\n");
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
@@ -1232,10 +1223,8 @@
 
 	rval = qla2x00_mbx_reg_test(vha);
 	if (rval) {
-		DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
-		    vha->host_no));
-		qla_printk(KERN_WARNING, ha,
-		    "Failed mailbox send register test\n");
+		ql_log(ql_log_warn, vha, 0x0082,
+		    "Failed mailbox send register test.\n");
 	} else {
 		/* Flag a successful rval */
 		rval = QLA_SUCCESS;
@@ -1257,8 +1246,8 @@
 	struct rsp_que *rsp = ha->rsp_q_map[0];
 
 	if (ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware dump previously allocated.\n");
+		ql_dbg(ql_dbg_init, vha, 0x00bd,
+		    "Firmware dump already allocated.\n");
 		return;
 	}
 
@@ -1288,8 +1277,9 @@
 		tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma,
 		    GFP_KERNEL);
 		if (!tc) {
-			qla_printk(KERN_WARNING, ha, "Unable to allocate "
-			    "(%d KB) for FCE.\n", FCE_SIZE / 1024);
+			ql_log(ql_log_warn, vha, 0x00be,
+			    "Unable to allocate (%d KB) for FCE.\n",
+			    FCE_SIZE / 1024);
 			goto try_eft;
 		}
 
@@ -1297,16 +1287,15 @@
 		rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS,
 		    ha->fce_mb, &ha->fce_bufs);
 		if (rval) {
-			qla_printk(KERN_WARNING, ha, "Unable to initialize "
-			    "FCE (%d).\n", rval);
+			ql_log(ql_log_warn, vha, 0x00bf,
+			    "Unable to initialize FCE (%d).\n", rval);
 			dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc,
 			    tc_dma);
 			ha->flags.fce_enabled = 0;
 			goto try_eft;
 		}
-
-		qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n",
-		    FCE_SIZE / 1024);
+		ql_log(ql_log_info, vha, 0x00c0,
+		    "Allocate (%d KB) for FCE...\n", FCE_SIZE / 1024);
 
 		fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE;
 		ha->flags.fce_enabled = 1;
@@ -1317,23 +1306,23 @@
 		tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
 		    GFP_KERNEL);
 		if (!tc) {
-			qla_printk(KERN_WARNING, ha, "Unable to allocate "
-			    "(%d KB) for EFT.\n", EFT_SIZE / 1024);
+			ql_log(ql_log_warn, vha, 0x00c1,
+			    "Unable to allocate (%d KB) for EFT.\n",
+			    EFT_SIZE / 1024);
 			goto cont_alloc;
 		}
 
 		memset(tc, 0, EFT_SIZE);
 		rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
 		if (rval) {
-			qla_printk(KERN_WARNING, ha, "Unable to initialize "
-			    "EFT (%d).\n", rval);
+			ql_log(ql_log_warn, vha, 0x00c2,
+			    "Unable to initialize EFT (%d).\n", rval);
 			dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
 			    tc_dma);
 			goto cont_alloc;
 		}
-
-		qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n",
-		    EFT_SIZE / 1024);
+		ql_log(ql_log_info, vha, 0x00c3,
+		    "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024);
 
 		eft_size = EFT_SIZE;
 		ha->eft_dma = tc_dma;
@@ -1350,8 +1339,9 @@
 
 	ha->fw_dump = vmalloc(dump_size);
 	if (!ha->fw_dump) {
-		qla_printk(KERN_WARNING, ha, "Unable to allocate (%d KB) for "
-		    "firmware dump!!!\n", dump_size / 1024);
+		ql_log(ql_log_warn, vha, 0x00c4,
+		    "Unable to allocate (%d KB) for firmware dump.\n",
+		    dump_size / 1024);
 
 		if (ha->fce) {
 			dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
@@ -1368,8 +1358,8 @@
 		}
 		return;
 	}
-	qla_printk(KERN_INFO, ha, "Allocated (%d KB) for firmware dump...\n",
-	    dump_size / 1024);
+	ql_log(ql_log_info, vha, 0x00c5,
+	    "Allocated (%d KB) for firmware dump.\n", dump_size / 1024);
 
 	ha->fw_dump_len = dump_size;
 	ha->fw_dump->signature[0] = 'Q';
@@ -1398,23 +1388,21 @@
 	int rval;
 	uint16_t dc;
 	uint32_t dw;
-	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA81XX(vha->hw))
 		return QLA_SUCCESS;
 
 	rval = qla2x00_write_ram_word(vha, 0x7c00, 1);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "Sync-MPI: Unable to acquire semaphore.\n"));
+		ql_log(ql_log_warn, vha, 0x0105,
+		    "Unable to acquire semaphore.\n");
 		goto done;
 	}
 
 	pci_read_config_word(vha->hw->pdev, 0x54, &dc);
 	rval = qla2x00_read_ram_word(vha, 0x7a15, &dw);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "Sync-MPI: Unable to read sync.\n"));
+		ql_log(ql_log_warn, vha, 0x0067, "Unable to read sync.\n");
 		goto done_release;
 	}
 
@@ -1426,15 +1414,14 @@
 	dw |= dc;
 	rval = qla2x00_write_ram_word(vha, 0x7a15, dw);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "Sync-MPI: Unable to gain sync.\n"));
+		ql_log(ql_log_warn, vha, 0x0114, "Unable to gain sync.\n");
 	}
 
 done_release:
 	rval = qla2x00_write_ram_word(vha, 0x7c00, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "Sync-MPI: Unable to release semaphore.\n"));
+		ql_log(ql_log_warn, vha, 0x006d,
+		    "Unable to release semaphore.\n");
 	}
 
 done:
@@ -1479,14 +1466,14 @@
 	/* Load firmware sequences */
 	rval = ha->isp_ops->load_risc(vha, &srisc_address);
 	if (rval == QLA_SUCCESS) {
-		DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC "
-		    "code.\n", vha->host_no));
+		ql_dbg(ql_dbg_init, vha, 0x00c9,
+		    "Verifying Checksum of loaded RISC code.\n");
 
 		rval = qla2x00_verify_checksum(vha, srisc_address);
 		if (rval == QLA_SUCCESS) {
 			/* Start firmware execution. */
-			DEBUG(printk("scsi(%ld): Checksum OK, start "
-			    "firmware.\n", vha->host_no));
+			ql_dbg(ql_dbg_init, vha, 0x00ca,
+			    "Starting firmware.\n");
 
 			rval = qla2x00_execute_fw(vha, srisc_address);
 			/* Retrieve firmware information. */
@@ -1522,9 +1509,9 @@
 				}
 			}
 		} else {
-			DEBUG2(printk(KERN_INFO
-			    "scsi(%ld): ISP Firmware failed checksum.\n",
-			    vha->host_no));
+			ql_log(ql_log_fatal, vha, 0x00cd,
+			    "ISP Firmware failed checksum.\n");
+			goto failed;
 		}
 	}
 
@@ -1549,7 +1536,7 @@
 			ha->flags.fac_supported = 1;
 			ha->fdt_block_size = size << 2;
 		} else {
-			qla_printk(KERN_ERR, ha,
+			ql_log(ql_log_warn, vha, 0x00ce,
 			    "Unsupported FAC firmware (%d.%02d.%02d).\n",
 			    ha->fw_major_version, ha->fw_minor_version,
 			    ha->fw_subminor_version);
@@ -1557,8 +1544,8 @@
 	}
 failed:
 	if (rval) {
-		DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
-		    vha->host_no));
+		ql_log(ql_log_fatal, vha, 0x00cf,
+		    "Setup chip ****FAILED****.\n");
 	}
 
 	return (rval);
@@ -1608,10 +1595,11 @@
 		return;
 
 	/* Serial Link options. */
-	DEBUG3(printk("scsi(%ld): Serial link options:\n",
-	    vha->host_no));
-	DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options,
-	    sizeof(ha->fw_seriallink_options)));
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0115,
+	    "Serial link options.\n");
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0109,
+	    (uint8_t *)&ha->fw_seriallink_options,
+	    sizeof(ha->fw_seriallink_options));
 
 	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
 	if (ha->fw_seriallink_options[3] & BIT_2) {
@@ -1688,7 +1676,7 @@
 	    le16_to_cpu(ha->fw_seriallink_options24[2]),
 	    le16_to_cpu(ha->fw_seriallink_options24[3]));
 	if (rval != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x0104,
 		    "Unable to update Serial Link options (%x).\n", rval);
 	}
 }
@@ -1746,8 +1734,9 @@
 		icb->rid = __constant_cpu_to_le16(rid);
 		if (ha->flags.msix_enabled) {
 			msix = &ha->msix_entries[1];
-			DEBUG2_17(printk(KERN_INFO
-			"Registering vector 0x%x for base que\n", msix->entry));
+			ql_dbg(ql_dbg_init, vha, 0x00fd,
+			    "Registering vector 0x%x for base que.\n",
+			    msix->entry);
 			icb->msix = cpu_to_le16(msix->entry);
 		}
 		/* Use alternate PCI bus number */
@@ -1764,8 +1753,8 @@
 			icb->firmware_options_2 &=
 				__constant_cpu_to_le32(~BIT_22);
 			ha->flags.disable_msix_handshake = 1;
-			qla_printk(KERN_INFO, ha,
-				"MSIX Handshake Disable Mode turned on\n");
+			ql_dbg(ql_dbg_init, vha, 0x00fe,
+			    "MSIX Handshake Disable Mode turned on.\n");
 		} else {
 			icb->firmware_options_2 |=
 				__constant_cpu_to_le32(BIT_22);
@@ -1850,7 +1839,7 @@
 	/* Update any ISP specific firmware options before initialization. */
 	ha->isp_ops->update_fw_options(vha);
 
-	DEBUG(printk("scsi(%ld): Issue init firmware.\n", vha->host_no));
+	ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n");
 
 	if (ha->flags.npiv_supported) {
 		if (ha->operating_mode == LOOP)
@@ -1866,11 +1855,11 @@
 
 	rval = qla2x00_init_firmware(vha, ha->init_cb_size);
 	if (rval) {
-		DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n",
-		    vha->host_no));
+		ql_log(ql_log_fatal, vha, 0x00d2,
+		    "Init Firmware **** FAILED ****.\n");
 	} else {
-		DEBUG3(printk("scsi(%ld): Init firmware -- success.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_init, vha, 0x00d3,
+		    "Init Firmware -- success.\n");
 	}
 
 	return (rval);
@@ -1913,10 +1902,8 @@
 
 	/* Wait for ISP to finish LIP */
 	if (!vha->flags.init_done)
- 		qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n");
-
-	DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n",
-	    vha->host_no));
+		ql_log(ql_log_info, vha, 0x801e,
+		    "Waiting for LIP to complete.\n");
 
 	do {
 		rval = qla2x00_get_firmware_state(vha, state);
@@ -1925,30 +1912,35 @@
 				vha->device_flags &= ~DFLG_NO_CABLE;
 			}
 			if (IS_QLA84XX(ha) && state[0] != FSTATE_READY) {
-				DEBUG16(printk("scsi(%ld): fw_state=%x "
-				    "84xx=%x.\n", vha->host_no, state[0],
-				    state[2]));
+				ql_dbg(ql_dbg_taskm, vha, 0x801f,
+				    "fw_state=%x 84xx=%x.\n", state[0],
+				    state[2]);
 				if ((state[2] & FSTATE_LOGGED_IN) &&
 				     (state[2] & FSTATE_WAITING_FOR_VERIFY)) {
-					DEBUG16(printk("scsi(%ld): Sending "
-					    "verify iocb.\n", vha->host_no));
+					ql_dbg(ql_dbg_taskm, vha, 0x8028,
+					    "Sending verify iocb.\n");
 
 					cs84xx_time = jiffies;
 					rval = qla84xx_init_chip(vha);
-					if (rval != QLA_SUCCESS)
+					if (rval != QLA_SUCCESS) {
+						ql_log(ql_log_warn,
+						    vha, 0x8043,
+						    "Init chip failed.\n");
 						break;
+					}
 
 					/* Add time taken to initialize. */
 					cs84xx_time = jiffies - cs84xx_time;
 					wtime += cs84xx_time;
 					mtime += cs84xx_time;
-					DEBUG16(printk("scsi(%ld): Increasing "
-					    "wait time by %ld. New time %ld\n",
-					    vha->host_no, cs84xx_time, wtime));
+					ql_dbg(ql_dbg_taskm, vha, 0x8042,
+					    "Increasing wait time by %ld. "
+					    "New time %ld.\n", cs84xx_time,
+					    wtime);
 				}
 			} else if (state[0] == FSTATE_READY) {
-				DEBUG(printk("scsi(%ld): F/W Ready - OK \n",
-				    vha->host_no));
+				ql_dbg(ql_dbg_taskm, vha, 0x8037,
+				    "F/W Ready - OK.\n");
 
 				qla2x00_get_retry_cnt(vha, &ha->retry_count,
 				    &ha->login_timeout, &ha->r_a_tov);
@@ -1965,7 +1957,7 @@
 				 * other than Wait for Login.
 				 */
 				if (time_after_eq(jiffies, mtime)) {
-					qla_printk(KERN_INFO, ha,
+					ql_log(ql_log_info, vha, 0x8038,
 					    "Cable is unplugged...\n");
 
 					vha->device_flags |= DFLG_NO_CABLE;
@@ -1985,17 +1977,17 @@
 		/* Delay for a while */
 		msleep(500);
 
-		DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
-		    vha->host_no, state[0], jiffies));
+		ql_dbg(ql_dbg_taskm, vha, 0x8039,
+		    "fw_state=%x curr time=%lx.\n", state[0], jiffies);
 	} while (1);
 
-	DEBUG(printk("scsi(%ld): fw_state=%x (%x, %x, %x, %x) curr time=%lx.\n",
-	    vha->host_no, state[0], state[1], state[2], state[3], state[4],
-	    jiffies));
+	ql_dbg(ql_dbg_taskm, vha, 0x803a,
+	    "fw_state=%x (%x, %x, %x, %x) " "curr time=%lx.\n", state[0],
+	    state[1], state[2], state[3], state[4], jiffies);
 
 	if (rval) {
-		DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
-		    vha->host_no));
+		ql_log(ql_log_warn, vha, 0x803b,
+		    "Firmware ready **** FAILED ****.\n");
 	}
 
 	return (rval);
@@ -2034,19 +2026,19 @@
 		if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) ||
 		    IS_QLA8XXX_TYPE(ha) ||
 		    (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
-			DEBUG2(printk("%s(%ld) Loop is in a transition state\n",
-			    __func__, vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x2008,
+			    "Loop is in a transition state.\n");
 		} else {
-			qla_printk(KERN_WARNING, ha,
-			    "ERROR -- Unable to get host loop ID.\n");
+			ql_log(ql_log_warn, vha, 0x2009,
+			    "Unable to get host loop ID.\n");
 			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 		}
 		return (rval);
 	}
 
 	if (topo == 4) {
-		qla_printk(KERN_INFO, ha,
-			"Cannot get topology - retrying.\n");
+		ql_log(ql_log_info, vha, 0x200a,
+		    "Cannot get topology - retrying.\n");
 		return (QLA_FUNCTION_FAILED);
 	}
 
@@ -2059,31 +2051,27 @@
 
 	switch (topo) {
 	case 0:
-		DEBUG3(printk("scsi(%ld): HBA in NL topology.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x200b, "HBA in NL topology.\n");
 		ha->current_topology = ISP_CFG_NL;
 		strcpy(connect_type, "(Loop)");
 		break;
 
 	case 1:
-		DEBUG3(printk("scsi(%ld): HBA in FL topology.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x200c, "HBA in FL topology.\n");
 		ha->switch_cap = sw_cap;
 		ha->current_topology = ISP_CFG_FL;
 		strcpy(connect_type, "(FL_Port)");
 		break;
 
 	case 2:
-		DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x200d, "HBA in N P2P topology.\n");
 		ha->operating_mode = P2P;
 		ha->current_topology = ISP_CFG_N;
 		strcpy(connect_type, "(N_Port-to-N_Port)");
 		break;
 
 	case 3:
-		DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x200e, "HBA in F P2P topology.\n");
 		ha->switch_cap = sw_cap;
 		ha->operating_mode = P2P;
 		ha->current_topology = ISP_CFG_F;
@@ -2091,9 +2079,8 @@
 		break;
 
 	default:
-		DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. "
-		    "Using NL.\n",
-		    vha->host_no, topo));
+		ql_dbg(ql_dbg_disc, vha, 0x200f,
+		    "HBA in unknown topology %x, using NL.\n", topo);
 		ha->current_topology = ISP_CFG_NL;
 		strcpy(connect_type, "(Loop)");
 		break;
@@ -2106,14 +2093,16 @@
 	vha->d_id.b.al_pa = al_pa;
 
 	if (!vha->flags.init_done)
- 		qla_printk(KERN_INFO, ha,
-		    "Topology - %s, Host Loop address 0x%x\n",
+		ql_log(ql_log_info, vha, 0x2010,
+		    "Topology - %s, Host Loop address 0x%x.\n",
 		    connect_type, vha->loop_id);
 
 	if (rval) {
-		DEBUG2_3(printk("scsi(%ld): FAILED.\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x2011,
+		    "%s FAILED\n", __func__);
 	} else {
-		DEBUG3(printk("scsi(%ld): exiting normally.\n", vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2012,
+		    "%s success\n", __func__);
 	}
 
 	return(rval);
@@ -2227,18 +2216,22 @@
 	for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++)
 		chksum += *ptr++;
 
-	DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
-	DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010f,
+	    "Contents of NVRAM.\n");
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0110,
+	    (uint8_t *)nv, ha->nvram_size);
 
 	/* Bad NVRAM data, set defaults parameters. */
 	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' ||
 	    nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
 		/* Reset NVRAM data. */
-		qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: "
-		    "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0],
-		    nv->nvram_version);
-		qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet "
-		    "invalid -- WWPN) defaults.\n");
+		ql_log(ql_log_warn, vha, 0x0064,
+		    "Inconisistent NVRAM "
+		    "detected: checksum=0x%x id=%c version=0x%x.\n",
+		    chksum, nv->id[0], nv->nvram_version);
+		ql_log(ql_log_warn, vha, 0x0065,
+		    "Falling back to "
+		    "functioning (yet invalid -- WWPN) defaults.\n");
 
 		/*
 		 * Set default initialization control block.
@@ -2382,8 +2375,13 @@
 	/*
 	 * Set host adapter parameters.
 	 */
+
+	/*
+	 * BIT_7 in the host-parameters section allows for modification to
+	 * internal driver logging.
+	 */
 	if (nv->host_p[0] & BIT_7)
-		ql2xextended_error_logging = 1;
+		ql2xextended_error_logging = 0x7fffffff;
 	ha->flags.disable_risc_code_load = ((nv->host_p[0] & BIT_4) ? 1 : 0);
 	/* Always load RISC code on non ISP2[12]00 chips. */
 	if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
@@ -2488,10 +2486,7 @@
 		if (ha->zio_mode != QLA_ZIO_DISABLED) {
 			ha->zio_mode = QLA_ZIO_MODE_6;
 
-			DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer "
-			    "delay (%d us).\n", vha->host_no, ha->zio_mode,
-			    ha->zio_timer * 100));
-			qla_printk(KERN_INFO, ha,
+			ql_log(ql_log_info, vha, 0x0068,
 			    "ZIO mode %d enabled; timer delay (%d us).\n",
 			    ha->zio_mode, ha->zio_timer * 100);
 
@@ -2502,8 +2497,8 @@
 	}
 
 	if (rval) {
-		DEBUG2_3(printk(KERN_WARNING
-		    "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x0069,
+		    "NVRAM configuration failed.\n");
 	}
 	return (rval);
 }
@@ -2574,15 +2569,15 @@
 	if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) {
 		rval = qla2x00_configure_hba(vha);
 		if (rval != QLA_SUCCESS) {
-			DEBUG(printk("scsi(%ld): Unable to configure HBA.\n",
-			    vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x2013,
+			    "Unable to configure HBA.\n");
 			return (rval);
 		}
 	}
 
 	save_flags = flags = vha->dpc_flags;
-	DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n",
-	    vha->host_no, flags));
+	ql_dbg(ql_dbg_disc, vha, 0x2014,
+	    "Configure loop -- dpc flags = 0x%lx.\n", flags);
 
 	/*
 	 * If we have both an RSCN and PORT UPDATE pending then handle them
@@ -2619,15 +2614,21 @@
 	}
 
 	if (test_bit(LOCAL_LOOP_UPDATE, &flags)) {
-		if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+		if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+			ql_dbg(ql_dbg_disc, vha, 0x2015,
+			    "Loop resync needed, failing.\n");
 			rval = QLA_FUNCTION_FAILED;
+		}
 		else
 			rval = qla2x00_configure_local_loop(vha);
 	}
 
 	if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) {
-		if (LOOP_TRANSITION(vha))
+		if (LOOP_TRANSITION(vha)) {
+			ql_dbg(ql_dbg_disc, vha, 0x201e,
+			    "Needs RSCN update and loop transition.\n");
 			rval = QLA_FUNCTION_FAILED;
+		}
 		else
 			rval = qla2x00_configure_fabric(vha);
 	}
@@ -2638,16 +2639,17 @@
 			rval = QLA_FUNCTION_FAILED;
 		} else {
 			atomic_set(&vha->loop_state, LOOP_READY);
-
-			DEBUG(printk("scsi(%ld): LOOP READY\n", vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x2069,
+			    "LOOP READY.\n");
 		}
 	}
 
 	if (rval) {
-		DEBUG2_3(printk("%s(%ld): *** FAILED ***\n",
-		    __func__, vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x206a,
+		    "%s *** FAILED ***.\n", __func__);
 	} else {
-		DEBUG3(printk("%s: exiting normally\n", __func__));
+		ql_dbg(ql_dbg_disc, vha, 0x206b,
+		    "%s: exiting normally.\n", __func__);
 	}
 
 	/* Restore state if a resync event occurred during processing */
@@ -2695,8 +2697,10 @@
 	new_fcport = NULL;
 	entries = MAX_FIBRE_DEVICES;
 
-	DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", vha->host_no));
-	DEBUG3(qla2x00_get_fcal_position_map(vha, NULL));
+	ql_dbg(ql_dbg_disc, vha, 0x2016,
+	    "Getting FCAL position map.\n");
+	if (ql2xextended_error_logging & ql_dbg_disc)
+		qla2x00_get_fcal_position_map(vha, NULL);
 
 	/* Get list of logged in devices. */
 	memset(ha->gid_list, 0, GID_LIST_SIZE);
@@ -2705,14 +2709,17 @@
 	if (rval != QLA_SUCCESS)
 		goto cleanup_allocation;
 
-	DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n",
-	    vha->host_no, entries));
-	DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list,
-	    entries * sizeof(struct gid_list_info)));
+	ql_dbg(ql_dbg_disc, vha, 0x2017,
+	    "Entries in ID list (%d).\n", entries);
+	ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075,
+	    (uint8_t *)ha->gid_list,
+	    entries * sizeof(struct gid_list_info));
 
 	/* Allocate temporary fcport for any new fcports discovered. */
 	new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
 	if (new_fcport == NULL) {
+		ql_log(ql_log_warn, vha, 0x2018,
+		    "Memory allocation failed for fcport.\n");
 		rval = QLA_MEMORY_ALLOC_FAILED;
 		goto cleanup_allocation;
 	}
@@ -2726,9 +2733,9 @@
 		    fcport->port_type != FCT_BROADCAST &&
 		    (fcport->flags & FCF_FABRIC_DEVICE) == 0) {
 
-			DEBUG(printk("scsi(%ld): Marking port lost, "
-			    "loop_id=0x%04x\n",
-			    vha->host_no, fcport->loop_id));
+			ql_dbg(ql_dbg_disc, vha, 0x2019,
+			    "Marking port lost loop_id=0x%04x.\n",
+			    fcport->loop_id);
 
 			qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
 		}
@@ -2769,12 +2776,12 @@
 		new_fcport->vp_idx = vha->vp_idx;
 		rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
 		if (rval2 != QLA_SUCCESS) {
-			DEBUG2(printk("scsi(%ld): Failed to retrieve fcport "
-			    "information -- get_port_database=%x, "
-			    "loop_id=0x%04x\n",
-			    vha->host_no, rval2, new_fcport->loop_id));
-			DEBUG2(printk("scsi(%ld): Scheduling resync...\n",
-			    vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x201a,
+			    "Failed to retrieve fcport information "
+			    "-- get_port_database=%x, loop_id=0x%04x.\n",
+			    rval2, new_fcport->loop_id);
+			ql_dbg(ql_dbg_disc, vha, 0x201b,
+			    "Scheduling resync.\n");
 			set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
 			continue;
 		}
@@ -2810,6 +2817,8 @@
 			fcport = new_fcport;
 			new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
 			if (new_fcport == NULL) {
+				ql_log(ql_log_warn, vha, 0x201c,
+				    "Failed to allocate memory for fcport.\n");
 				rval = QLA_MEMORY_ALLOC_FAILED;
 				goto cleanup_allocation;
 			}
@@ -2828,8 +2837,8 @@
 	kfree(new_fcport);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(printk("scsi(%ld): Configure local loop error exit: "
-		    "rval=%x\n", vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x201d,
+		    "Configure local loop error exit: rval=%x.\n", rval);
 	}
 
 	return (rval);
@@ -2858,27 +2867,27 @@
 	rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed,
 	    mb);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA "
-		    "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n",
-		    vha->host_no, fcport->port_name[0], fcport->port_name[1],
+		ql_dbg(ql_dbg_disc, vha, 0x2004,
+		    "Unable to adjust iIDMA "
+		    "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x "
+		    "%04x.\n", fcport->port_name[0], fcport->port_name[1],
 		    fcport->port_name[2], fcport->port_name[3],
 		    fcport->port_name[4], fcport->port_name[5],
 		    fcport->port_name[6], fcport->port_name[7], rval,
-		    fcport->fp_speed, mb[0], mb[1]));
+		    fcport->fp_speed, mb[0], mb[1]);
 	} else {
 		link_speed = link_speeds[LS_UNKNOWN];
 		if (fcport->fp_speed < 5)
 			link_speed = link_speeds[fcport->fp_speed];
 		else if (fcport->fp_speed == 0x13)
 			link_speed = link_speeds[5];
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "iIDMA adjusted to %s GB/s on "
-		    "%02x%02x%02x%02x%02x%02x%02x%02x.\n",
-		    link_speed, fcport->port_name[0],
-		    fcport->port_name[1], fcport->port_name[2],
-		    fcport->port_name[3], fcport->port_name[4],
-		    fcport->port_name[5], fcport->port_name[6],
-		    fcport->port_name[7]));
+		ql_dbg(ql_dbg_disc, vha, 0x2005,
+		    "iIDMA adjusted to %s GB/s "
+		    "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed,
+		    fcport->port_name[0], fcport->port_name[1],
+		    fcport->port_name[2], fcport->port_name[3],
+		    fcport->port_name[4], fcport->port_name[5],
+		    fcport->port_name[6], fcport->port_name[7]);
 	}
 }
 
@@ -2887,7 +2896,6 @@
 {
 	struct fc_rport_identifiers rport_ids;
 	struct fc_rport *rport;
-	struct qla_hw_data *ha = vha->hw;
 	unsigned long flags;
 
 	qla2x00_rport_del(fcport);
@@ -2899,8 +2907,8 @@
 	rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
 	fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids);
 	if (!rport) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to allocate fc remote port!\n");
+		ql_log(ql_log_warn, vha, 0x2006,
+		    "Unable to allocate fc remote port.\n");
 		return;
 	}
 	spin_lock_irqsave(fcport->vha->host->host_lock, flags);
@@ -2975,8 +2983,8 @@
 		loop_id = SNS_FL_PORT;
 	rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL "
-		    "Port\n", vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x201f,
+		    "MBX_GET_PORT_NAME failed, No FL Port.\n");
 
 		vha->device_flags &= ~SWITCH_FOUND;
 		return (QLA_SUCCESS);
@@ -3003,32 +3011,32 @@
 		ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
 		    0xfc, mb, BIT_1 | BIT_0);
 		if (mb[0] != MBS_COMMAND_COMPLETE) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
-			    "mb[2]=%x mb[6]=%x mb[7]=%x\n", loop_id,
-			    mb[0], mb[1], mb[2], mb[6], mb[7]));
+			ql_dbg(ql_dbg_disc, vha, 0x2042,
+			    "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x "
+			    "mb[6]=%x mb[7]=%x.\n", loop_id, mb[0], mb[1],
+			    mb[2], mb[6], mb[7]);
 			return (QLA_SUCCESS);
 		}
 
 		if (test_and_clear_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags)) {
 			if (qla2x00_rft_id(vha)) {
 				/* EMPTY */
-				DEBUG2(printk("scsi(%ld): Register FC-4 "
-				    "TYPE failed.\n", vha->host_no));
+				ql_dbg(ql_dbg_disc, vha, 0x2045,
+				    "Register FC-4 TYPE failed.\n");
 			}
 			if (qla2x00_rff_id(vha)) {
 				/* EMPTY */
-				DEBUG2(printk("scsi(%ld): Register FC-4 "
-				    "Features failed.\n", vha->host_no));
+				ql_dbg(ql_dbg_disc, vha, 0x2049,
+				    "Register FC-4 Features failed.\n");
 			}
 			if (qla2x00_rnn_id(vha)) {
 				/* EMPTY */
-				DEBUG2(printk("scsi(%ld): Register Node Name "
-				    "failed.\n", vha->host_no));
+				ql_dbg(ql_dbg_disc, vha, 0x204f,
+				    "Register Node Name failed.\n");
 			} else if (qla2x00_rsnn_nn(vha)) {
 				/* EMPTY */
-				DEBUG2(printk("scsi(%ld): Register Symbolic "
-				    "Node Name failed.\n", vha->host_no));
+				ql_dbg(ql_dbg_disc, vha, 0x2053,
+				    "Register Symobilic Node Name failed.\n");
 			}
 		}
 
@@ -3132,8 +3140,8 @@
 	}
 
 	if (rval) {
-		DEBUG2(printk("scsi(%ld): Configure fabric error exit: "
-		    "rval=%d\n", vha->host_no, rval));
+		ql_dbg(ql_dbg_disc, vha, 0x2068,
+		    "Configure fabric error exit rval=%d.\n", rval);
 	}
 
 	return (rval);
@@ -3175,8 +3183,8 @@
 	swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_KERNEL);
 	if (!swl) {
 		/*EMPTY*/
-		DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback "
-		    "on GA_NXT\n", vha->host_no));
+		ql_dbg(ql_dbg_disc, vha, 0x2054,
+		    "GID_PT allocations failed, fallback on GA_NXT.\n");
 	} else {
 		if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) {
 			kfree(swl);
@@ -3201,6 +3209,8 @@
 	/* Allocate temporary fcport for any new fcports discovered. */
 	new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
 	if (new_fcport == NULL) {
+		ql_log(ql_log_warn, vha, 0x205e,
+		    "Failed to allocate memory for fcport.\n");
 		kfree(swl);
 		return (QLA_MEMORY_ALLOC_FAILED);
 	}
@@ -3247,9 +3257,9 @@
 			/* Send GA_NXT to the switch */
 			rval = qla2x00_ga_nxt(vha, new_fcport);
 			if (rval != QLA_SUCCESS) {
-				qla_printk(KERN_WARNING, ha,
-				    "SNS scan failed -- assuming zero-entry "
-				    "result...\n");
+				ql_log(ql_log_warn, vha, 0x2064,
+				    "SNS scan failed -- assuming "
+				    "zero-entry result.\n");
 				list_for_each_entry_safe(fcport, fcptemp,
 				    new_fcports, list) {
 					list_del(&fcport->list);
@@ -3265,9 +3275,11 @@
 			wrap.b24 = new_fcport->d_id.b24;
 			first_dev = 0;
 		} else if (new_fcport->d_id.b24 == wrap.b24) {
-			DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n",
-			    vha->host_no, new_fcport->d_id.b.domain,
-			    new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa));
+			ql_dbg(ql_dbg_disc, vha, 0x2065,
+			    "Device wrap (%02x%02x%02x).\n",
+			    new_fcport->d_id.b.domain,
+			    new_fcport->d_id.b.area,
+			    new_fcport->d_id.b.al_pa);
 			break;
 		}
 
@@ -3372,6 +3384,8 @@
 		nxt_d_id.b24 = new_fcport->d_id.b24;
 		new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
 		if (new_fcport == NULL) {
+			ql_log(ql_log_warn, vha, 0x2066,
+			    "Memory allocation failed for fcport.\n");
 			kfree(swl);
 			return (QLA_MEMORY_ALLOC_FAILED);
 		}
@@ -3501,10 +3515,10 @@
 		d_id.b.area = MSB(LSW(rscn_entry));
 		d_id.b.al_pa = LSB(LSW(rscn_entry));
 
-		DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = "
-		    "[%02x/%02x%02x%02x].\n",
-		    vha->host_no, vha->rscn_out_ptr, format, d_id.b.domain,
-		    d_id.b.area, d_id.b.al_pa));
+		ql_dbg(ql_dbg_disc, vha, 0x2020,
+		    "RSCN queue entry[%d] = [%02x/%02x%02x%02x].\n",
+		    vha->rscn_out_ptr, format, d_id.b.domain, d_id.b.area,
+		    d_id.b.al_pa);
 
 		vha->rscn_out_ptr++;
 		if (vha->rscn_out_ptr == MAX_RSCN_COUNT)
@@ -3520,17 +3534,17 @@
 			if (rscn_entry != vha->rscn_queue[rscn_out_iter])
 				break;
 
-			DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue "
-			    "entry found at [%d].\n", vha->host_no,
-			    rscn_out_iter));
+			ql_dbg(ql_dbg_disc, vha, 0x2021,
+			    "Skipping duplicate RSCN queue entry found at "
+			    "[%d].\n", rscn_out_iter);
 
 			vha->rscn_out_ptr = rscn_out_iter;
 		}
 
 		/* Queue overflow, set switch default case. */
 		if (vha->flags.rscn_queue_overflow) {
-			DEBUG(printk("scsi(%ld): device_resync: rscn "
-			    "overflow.\n", vha->host_no));
+			ql_dbg(ql_dbg_disc, vha, 0x2022,
+			    "device_resync: rscn overflow.\n");
 
 			format = 3;
 			vha->flags.rscn_queue_overflow = 0;
@@ -3659,10 +3673,11 @@
 	tmp_loopid = 0;
 
 	for (;;) {
-		DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x "
- 		    "for port %02x%02x%02x.\n",
-		    vha->host_no, fcport->loop_id, fcport->d_id.b.domain,
-		    fcport->d_id.b.area, fcport->d_id.b.al_pa));
+		ql_dbg(ql_dbg_disc, vha, 0x2000,
+		    "Trying Fabric Login w/loop id 0x%04x for port "
+		    "%02x%02x%02x.\n",
+		    fcport->loop_id, fcport->d_id.b.domain,
+		    fcport->d_id.b.area, fcport->d_id.b.al_pa);
 
 		/* Login fcport on switch. */
 		ha->isp_ops->fabric_login(vha, fcport->loop_id,
@@ -3680,10 +3695,11 @@
 			tmp_loopid = fcport->loop_id;
 			fcport->loop_id = mb[1];
 
-			DEBUG(printk("Fabric Login: port in use - next "
- 			    "loop id=0x%04x, port Id=%02x%02x%02x.\n",
+			ql_dbg(ql_dbg_disc, vha, 0x2001,
+			    "Fabric Login: port in use - next loop "
+			    "id=0x%04x, port id= %02x%02x%02x.\n",
 			    fcport->loop_id, fcport->d_id.b.domain,
-			    fcport->d_id.b.area, fcport->d_id.b.al_pa));
+			    fcport->d_id.b.area, fcport->d_id.b.al_pa);
 
 		} else if (mb[0] == MBS_COMMAND_COMPLETE) {
 			/*
@@ -3744,11 +3760,11 @@
 			/*
 			 * unrecoverable / not handled error
 			 */
-			DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x "
- 			    "loop_id=%x jiffies=%lx.\n",
-			    __func__, vha->host_no, mb[0],
-			    fcport->d_id.b.domain, fcport->d_id.b.area,
-			    fcport->d_id.b.al_pa, fcport->loop_id, jiffies));
+			ql_dbg(ql_dbg_disc, vha, 0x2002,
+			    "Failed=%x port_id=%02x%02x%02x loop_id=%x "
+			    "jiffies=%lx.\n", mb[0], fcport->d_id.b.domain,
+			    fcport->d_id.b.area, fcport->d_id.b.al_pa,
+			    fcport->loop_id, jiffies);
 
 			*next_loopid = fcport->loop_id;
 			ha->isp_ops->fabric_logout(vha, fcport->loop_id,
@@ -3852,7 +3868,8 @@
 		return (QLA_FUNCTION_FAILED);
 
 	if (rval)
-		DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
+		ql_dbg(ql_dbg_disc, vha, 0x206c,
+		    "%s *** FAILED ***.\n", __func__);
 
 	return (rval);
 }
@@ -3929,8 +3946,8 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct scsi_qla_host *vp;
 
-	qla_printk(KERN_INFO, ha,
-			"Performing ISP error recovery - ha= %p.\n", ha);
+	ql_dbg(ql_dbg_p3p, vha, 0xb002,
+	    "Performing ISP error recovery - ha=%p.\n", ha);
 
 	atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
 	if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
@@ -3964,8 +3981,8 @@
 	clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 	ha->qla_stats.total_isp_aborts++;
 
-	qla_printk(KERN_INFO, ha,
-	    "Performing ISP error recovery - ha= %p.\n", ha);
+	ql_log(ql_log_info, vha, 0x00af,
+	    "Performing ISP error recovery - ha=%p.\n", ha);
 
 	/* For ISP82XX, reset_chip is just disabling interrupts.
 	 * Driver waits for the completion of the commands.
@@ -4016,6 +4033,8 @@
 		/* Make sure for ISP 82XX IO DMA is complete */
 		if (IS_QLA82XX(ha)) {
 			qla82xx_chip_reset_cleanup(vha);
+			ql_log(ql_log_info, vha, 0x00b4,
+			    "Done chip reset cleanup.\n");
 
 			/* Done waiting for pending commands.
 			 * Reset the online flag.
@@ -4097,7 +4116,7 @@
 				    ha->fce_dma, ha->fce_bufs, ha->fce_mb,
 				    &ha->fce_bufs);
 				if (rval) {
-					qla_printk(KERN_WARNING, ha,
+					ql_log(ql_log_warn, vha, 0x8033,
 					    "Unable to reinitialize FCE "
 					    "(%d).\n", rval);
 					ha->flags.fce_enabled = 0;
@@ -4109,7 +4128,7 @@
 				rval = qla2x00_enable_eft_trace(vha,
 				    ha->eft_dma, EFT_NUM_BUFFERS);
 				if (rval) {
-					qla_printk(KERN_WARNING, ha,
+					ql_log(ql_log_warn, vha, 0x8034,
 					    "Unable to reinitialize EFT "
 					    "(%d).\n", rval);
 				}
@@ -4118,9 +4137,9 @@
 			vha->flags.online = 1;
 			if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
 				if (ha->isp_abort_cnt == 0) {
- 					qla_printk(KERN_WARNING, ha,
-					    "ISP error recovery failed - "
-					    "board disabled\n");
+					ql_log(ql_log_fatal, vha, 0x8035,
+					    "ISP error recover failed - "
+					    "board disabled.\n");
 					/*
 					 * The next call disables the board
 					 * completely.
@@ -4132,16 +4151,16 @@
 					status = 0;
 				} else { /* schedule another ISP abort */
 					ha->isp_abort_cnt--;
-					DEBUG(printk("qla%ld: ISP abort - "
-					    "retry remaining %d\n",
-					    vha->host_no, ha->isp_abort_cnt));
+					ql_dbg(ql_dbg_taskm, vha, 0x8020,
+					    "ISP abort - retry remaining %d.\n",
+					    ha->isp_abort_cnt);
 					status = 1;
 				}
 			} else {
 				ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
-				DEBUG(printk("qla2x00(%ld): ISP error recovery "
-				    "- retrying (%d) more times\n",
-				    vha->host_no, ha->isp_abort_cnt));
+				ql_dbg(ql_dbg_taskm, vha, 0x8021,
+				    "ISP error recovery - retrying (%d) "
+				    "more times.\n", ha->isp_abort_cnt);
 				set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
 				status = 1;
 			}
@@ -4150,9 +4169,7 @@
 	}
 
 	if (!status) {
-		DEBUG(printk(KERN_INFO
-				"qla2x00_abort_isp(%ld): succeeded.\n",
-				vha->host_no));
+		ql_dbg(ql_dbg_taskm, vha, 0x8022, "%s succeeded.\n", __func__);
 
 		spin_lock_irqsave(&ha->vport_slock, flags);
 		list_for_each_entry(vp, &ha->vp_list, list) {
@@ -4169,8 +4186,7 @@
 		spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 	} else {
-		qla_printk(KERN_INFO, ha,
-			"qla2x00_abort_isp: **** FAILED ****\n");
+		ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n");
 	}
 
 	return(status);
@@ -4211,8 +4227,8 @@
 
 		status = qla2x00_fw_ready(vha);
 		if (!status) {
-			DEBUG(printk("%s(): Start configure loop, "
-			    "status = %d\n", __func__, status));
+			ql_dbg(ql_dbg_taskm, vha, 0x8031,
+			    "Start configure loop status = %d.\n", status);
 
 			/* Issue a marker after FW becomes ready. */
 			qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
@@ -4234,9 +4250,8 @@
 		if ((vha->device_flags & DFLG_NO_CABLE))
 			status = 0;
 
-		DEBUG(printk("%s(): Configure loop done, status = 0x%x\n",
-				__func__,
-				status));
+		ql_dbg(ql_dbg_taskm, vha, 0x8032,
+		    "Configure loop done, status = 0x%x.\n", status);
 	}
 	return (status);
 }
@@ -4256,13 +4271,13 @@
 			rsp->options &= ~BIT_0;
 			ret = qla25xx_init_rsp_que(base_vha, rsp);
 			if (ret != QLA_SUCCESS)
-				DEBUG2_17(printk(KERN_WARNING
-					"%s Rsp que:%d init failed\n", __func__,
-						rsp->id));
+				ql_dbg(ql_dbg_init, base_vha, 0x00ff,
+				    "%s Rsp que: %d init failed.\n",
+				    __func__, rsp->id);
 			else
-				DEBUG2_17(printk(KERN_INFO
-					"%s Rsp que:%d inited\n", __func__,
-						rsp->id));
+				ql_dbg(ql_dbg_init, base_vha, 0x0100,
+				    "%s Rsp que: %d inited.\n",
+				    __func__, rsp->id);
 		}
 	}
 	for (i = 1; i < ha->max_req_queues; i++) {
@@ -4272,13 +4287,13 @@
 			req->options &= ~BIT_0;
 			ret = qla25xx_init_req_que(base_vha, req);
 			if (ret != QLA_SUCCESS)
-				DEBUG2_17(printk(KERN_WARNING
-					"%s Req que:%d init failed\n", __func__,
-						req->id));
+				ql_dbg(ql_dbg_init, base_vha, 0x0101,
+				    "%s Req que: %d init failed.\n",
+				    __func__, req->id);
 			else
-				DEBUG2_17(printk(KERN_WARNING
-					"%s Req que:%d inited\n", __func__,
-						req->id));
+				ql_dbg(ql_dbg_init, base_vha, 0x0102,
+				    "%s Req que: %d inited.\n",
+				    __func__, req->id);
 		}
 	}
 	return ret;
@@ -4397,19 +4412,22 @@
 	for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
 		chksum += le32_to_cpu(*dptr++);
 
-	DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
-	DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x006a,
+	    "Contents of NVRAM\n");
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010d,
+	    (uint8_t *)nv, ha->nvram_size);
 
 	/* Bad NVRAM data, set defaults parameters. */
 	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
 	    || nv->id[3] != ' ' ||
 	    nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
 		/* Reset NVRAM data. */
-		qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: "
-		    "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0],
-		    le16_to_cpu(nv->nvram_version));
-		qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet "
-		    "invalid -- WWPN) defaults.\n");
+		ql_log(ql_log_warn, vha, 0x006b,
+		    "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+		    "version=0x%x.\n", chksum, nv->id[0], nv->nvram_version);
+		ql_log(ql_log_warn, vha, 0x006c,
+		    "Falling back to functioning (yet invalid -- WWPN) "
+		    "defaults.\n");
 
 		/*
 		 * Set default initialization control block.
@@ -4587,10 +4605,7 @@
 	if (ha->zio_mode != QLA_ZIO_DISABLED) {
 		ha->zio_mode = QLA_ZIO_MODE_6;
 
-		DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay "
-		    "(%d us).\n", vha->host_no, ha->zio_mode,
-		    ha->zio_timer * 100));
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0x006f,
 		    "ZIO mode %d enabled; timer delay (%d us).\n",
 		    ha->zio_mode, ha->zio_timer * 100);
 
@@ -4601,8 +4616,8 @@
 	}
 
 	if (rval) {
-		DEBUG2_3(printk(KERN_WARNING
-		    "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x0070,
+		    "NVRAM configuration failed.\n");
 	}
 	return (rval);
 }
@@ -4620,8 +4635,8 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = ha->req_q_map[0];
 
-	qla_printk(KERN_INFO, ha,
-	    "FW: Loading from flash (%x)...\n", faddr);
+	ql_dbg(ql_dbg_init, vha, 0x008b,
+	    "Loading firmware from flash (%x).\n", faddr);
 
 	rval = QLA_SUCCESS;
 
@@ -4637,11 +4652,12 @@
 	    dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
 	    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
 		dcode[3] == 0)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to verify integrity of flash firmware image!\n");
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware data: %08x %08x %08x %08x!\n", dcode[0],
-		    dcode[1], dcode[2], dcode[3]);
+		ql_log(ql_log_fatal, vha, 0x008c,
+		    "Unable to verify the integrity of flash firmware "
+		    "image.\n");
+		ql_log(ql_log_fatal, vha, 0x008d,
+		    "Firmware data: %08x %08x %08x %08x.\n",
+		    dcode[0], dcode[1], dcode[2], dcode[3]);
 
 		return QLA_FUNCTION_FAILED;
 	}
@@ -4660,9 +4676,10 @@
 			if (dlen > risc_size)
 				dlen = risc_size;
 
-			DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
-			    "addr %x, number of dwords 0x%x, offset 0x%x.\n",
-			    vha->host_no, risc_addr, dlen, faddr));
+			ql_dbg(ql_dbg_init, vha, 0x008e,
+			    "Loading risc segment@ risc addr %x "
+			    "number of dwords 0x%x offset 0x%x.\n",
+			    risc_addr, dlen, faddr);
 
 			qla24xx_read_flash_data(vha, dcode, faddr, dlen);
 			for (i = 0; i < dlen; i++)
@@ -4671,12 +4688,9 @@
 			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
 			    dlen);
 			if (rval) {
-				DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
-				    "segment %d of firmware\n", vha->host_no,
-				    fragment));
-				qla_printk(KERN_WARNING, ha,
-				    "[ERROR] Failed to load segment %d of "
-				    "firmware\n", fragment);
+				ql_log(ql_log_fatal, vha, 0x008f,
+				    "Failed to load segment %d of firmware.\n",
+				    fragment);
 				break;
 			}
 
@@ -4709,9 +4723,10 @@
 	/* Load firmware blob. */
 	blob = qla2x00_request_firmware(vha);
 	if (!blob) {
-		qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
-		qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
-		    "from: " QLA_FW_URL ".\n");
+		ql_log(ql_log_info, vha, 0x0083,
+		    "Fimware image unavailable.\n");
+		ql_log(ql_log_info, vha, 0x0084,
+		    "Firmware images can be retrieved from: "QLA_FW_URL ".\n");
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -4724,8 +4739,8 @@
 
 	/* Validate firmware image by checking version. */
 	if (blob->fw->size < 8 * sizeof(uint16_t)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to verify integrity of firmware image (%Zd)!\n",
+		ql_log(ql_log_fatal, vha, 0x0085,
+		    "Unable to verify integrity of firmware image (%Zd).\n",
 		    blob->fw->size);
 		goto fail_fw_integrity;
 	}
@@ -4734,11 +4749,11 @@
 	if ((wcode[0] == 0xffff && wcode[1] == 0xffff && wcode[2] == 0xffff &&
 	    wcode[3] == 0xffff) || (wcode[0] == 0 && wcode[1] == 0 &&
 		wcode[2] == 0 && wcode[3] == 0)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to verify integrity of firmware image!\n");
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware data: %04x %04x %04x %04x!\n", wcode[0],
-		    wcode[1], wcode[2], wcode[3]);
+		ql_log(ql_log_fatal, vha, 0x0086,
+		    "Unable to verify integrity of firmware image.\n");
+		ql_log(ql_log_fatal, vha, 0x0087,
+		    "Firmware data: %04x %04x %04x %04x.\n",
+		    wcode[0], wcode[1], wcode[2], wcode[3]);
 		goto fail_fw_integrity;
 	}
 
@@ -4751,9 +4766,9 @@
 		/* Validate firmware image size. */
 		fwclen += risc_size * sizeof(uint16_t);
 		if (blob->fw->size < fwclen) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_fatal, vha, 0x0088,
 			    "Unable to verify integrity of firmware image "
-			    "(%Zd)!\n", blob->fw->size);
+			    "(%Zd).\n", blob->fw->size);
 			goto fail_fw_integrity;
 		}
 
@@ -4762,10 +4777,9 @@
 			wlen = (uint16_t)(ha->fw_transfer_size >> 1);
 			if (wlen > risc_size)
 				wlen = risc_size;
-
-			DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
-			    "addr %x, number of words 0x%x.\n", vha->host_no,
-			    risc_addr, wlen));
+			ql_dbg(ql_dbg_init, vha, 0x0089,
+			    "Loading risc segment@ risc addr %x number of "
+			    "words 0x%x.\n", risc_addr, wlen);
 
 			for (i = 0; i < wlen; i++)
 				wcode[i] = swab16(fwcode[i]);
@@ -4773,12 +4787,9 @@
 			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
 			    wlen);
 			if (rval) {
-				DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
-				    "segment %d of firmware\n", vha->host_no,
-				    fragment));
-				qla_printk(KERN_WARNING, ha,
-				    "[ERROR] Failed to load segment %d of "
-				    "firmware\n", fragment);
+				ql_log(ql_log_fatal, vha, 0x008a,
+				    "Failed to load segment %d of firmware.\n",
+				    fragment);
 				break;
 			}
 
@@ -4814,15 +4825,17 @@
 	/* Load firmware blob. */
 	blob = qla2x00_request_firmware(vha);
 	if (!blob) {
-		qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
-		qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
-		    "from: " QLA_FW_URL ".\n");
+		ql_log(ql_log_warn, vha, 0x0090,
+		    "Fimware image unavailable.\n");
+		ql_log(ql_log_warn, vha, 0x0091,
+		    "Firmware images can be retrieved from: "
+		    QLA_FW_URL ".\n");
 
 		return QLA_FUNCTION_FAILED;
 	}
 
-	qla_printk(KERN_INFO, ha,
-	    "FW: Loading via request-firmware...\n");
+	ql_log(ql_log_info, vha, 0x0092,
+	    "Loading via request-firmware.\n");
 
 	rval = QLA_SUCCESS;
 
@@ -4834,8 +4847,8 @@
 
 	/* Validate firmware image by checking version. */
 	if (blob->fw->size < 8 * sizeof(uint32_t)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to verify integrity of firmware image (%Zd)!\n",
+		ql_log(ql_log_fatal, vha, 0x0093,
+		    "Unable to verify integrity of firmware image (%Zd).\n",
 		    blob->fw->size);
 		goto fail_fw_integrity;
 	}
@@ -4845,11 +4858,12 @@
 	    dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
 	    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
 		dcode[3] == 0)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to verify integrity of firmware image!\n");
-		qla_printk(KERN_WARNING, ha,
-		    "Firmware data: %08x %08x %08x %08x!\n", dcode[0],
-		    dcode[1], dcode[2], dcode[3]);
+		ql_log(ql_log_fatal, vha, 0x0094,
+		    "Unable to verify integrity of firmware image (%Zd).\n",
+		    blob->fw->size);
+		ql_log(ql_log_fatal, vha, 0x0095,
+		    "Firmware data: %08x %08x %08x %08x.\n",
+		    dcode[0], dcode[1], dcode[2], dcode[3]);
 		goto fail_fw_integrity;
 	}
 
@@ -4861,9 +4875,9 @@
 		/* Validate firmware image size. */
 		fwclen += risc_size * sizeof(uint32_t);
 		if (blob->fw->size < fwclen) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_fatal, vha, 0x0096,
 			    "Unable to verify integrity of firmware image "
-			    "(%Zd)!\n", blob->fw->size);
+			    "(%Zd).\n", blob->fw->size);
 
 			goto fail_fw_integrity;
 		}
@@ -4874,9 +4888,9 @@
 			if (dlen > risc_size)
 				dlen = risc_size;
 
-			DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
-			    "addr %x, number of dwords 0x%x.\n", vha->host_no,
-			    risc_addr, dlen));
+			ql_dbg(ql_dbg_init, vha, 0x0097,
+			    "Loading risc segment@ risc addr %x "
+			    "number of dwords 0x%x.\n", risc_addr, dlen);
 
 			for (i = 0; i < dlen; i++)
 				dcode[i] = swab32(fwcode[i]);
@@ -4884,12 +4898,9 @@
 			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
 			    dlen);
 			if (rval) {
-				DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
-				    "segment %d of firmware\n", vha->host_no,
-				    fragment));
-				qla_printk(KERN_WARNING, ha,
-				    "[ERROR] Failed to load segment %d of "
-				    "firmware\n", fragment);
+				ql_log(ql_log_fatal, vha, 0x0098,
+				    "Failed to load segment %d of firmware.\n",
+				    fragment);
 				break;
 			}
 
@@ -4953,14 +4964,13 @@
 	if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw)
 		return rval;
 
-	qla_printk(KERN_ERR, ha,
-	    "FW: Attempting to fallback to golden firmware...\n");
+	ql_log(ql_log_info, vha, 0x0099,
+	    "Attempting to fallback to golden firmware.\n");
 	rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_gold_fw);
 	if (rval != QLA_SUCCESS)
 		return rval;
 
-	qla_printk(KERN_ERR, ha,
-	    "FW: Please update operational firmware...\n");
+	ql_log(ql_log_info, vha, 0x009a, "Update operational firmware.\n");
 	ha->flags.running_gold_fw = 1;
 
 	return rval;
@@ -4987,8 +4997,8 @@
 			continue;
 		if (qla2x00_setup_chip(vha) != QLA_SUCCESS)
 			continue;
-		qla_printk(KERN_INFO, ha,
-		    "Attempting retry of stop-firmware command...\n");
+		ql_log(ql_log_info, vha, 0x8015,
+		    "Attempting retry of stop-firmware command.\n");
 		ret = qla2x00_stop_firmware(vha);
 	}
 }
@@ -5023,10 +5033,10 @@
 	/* Login to SNS first */
 	ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, BIT_1);
 	if (mb[0] != MBS_COMMAND_COMPLETE) {
-		DEBUG15(qla_printk(KERN_INFO, ha,
-		    "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
-		    "mb[2]=%x mb[6]=%x mb[7]=%x\n", NPH_SNS,
-		    mb[0], mb[1], mb[2], mb[6], mb[7]));
+		ql_dbg(ql_dbg_init, vha, 0x0103,
+		    "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x "
+		    "mb[6]=%x mb[7]=%x.\n",
+		    NPH_SNS, mb[0], mb[1], mb[2], mb[6], mb[7]);
 		return (QLA_FUNCTION_FAILED);
 	}
 
@@ -5146,19 +5156,23 @@
 	for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
 		chksum += le32_to_cpu(*dptr++);
 
-	DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
-	DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111,
+	    "Contents of NVRAM:\n");
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112,
+	    (uint8_t *)nv, ha->nvram_size);
 
 	/* Bad NVRAM data, set defaults parameters. */
 	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
 	    || nv->id[3] != ' ' ||
 	    nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
 		/* Reset NVRAM data. */
-		qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: "
-		    "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0],
+		ql_log(ql_log_info, vha, 0x0073,
+		    "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+		    "version=0x%x.\n", chksum, nv->id[0],
 		    le16_to_cpu(nv->nvram_version));
-		qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet "
-		    "invalid -- WWPN) defaults.\n");
+		ql_log(ql_log_info, vha, 0x0074,
+		    "Falling back to functioning (yet invalid -- WWPN) "
+		    "defaults.\n");
 
 		/*
 		 * Set default initialization control block.
@@ -5350,12 +5364,10 @@
 	if (ha->zio_mode != QLA_ZIO_DISABLED) {
 		ha->zio_mode = QLA_ZIO_MODE_6;
 
-		DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay "
-		    "(%d us).\n", vha->host_no, ha->zio_mode,
-		    ha->zio_timer * 100));
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0x0075,
 		    "ZIO mode %d enabled; timer delay (%d us).\n",
-		    ha->zio_mode, ha->zio_timer * 100);
+		    ha->zio_mode,
+		    ha->zio_timer * 100);
 
 		icb->firmware_options_2 |= cpu_to_le32(
 		    (uint32_t)ha->zio_mode);
@@ -5364,8 +5376,8 @@
 	}
 
 	if (rval) {
-		DEBUG2_3(printk(KERN_WARNING
-		    "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
+		ql_log(ql_log_warn, vha, 0x0076,
+		    "NVRAM configuration failed.\n");
 	}
 	return (rval);
 }
@@ -5388,9 +5400,8 @@
 
 		status = qla2x00_fw_ready(vha);
 		if (!status) {
-			qla_printk(KERN_INFO, ha,
-			"%s(): Start configure loop, "
-			"status = %d\n", __func__, status);
+			ql_log(ql_log_info, vha, 0x803c,
+			    "Start configure loop, status =%d.\n", status);
 
 			/* Issue a marker after FW becomes ready. */
 			qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
@@ -5412,9 +5423,8 @@
 		if ((vha->device_flags & DFLG_NO_CABLE))
 			status = 0;
 
-		qla_printk(KERN_INFO, ha,
-			"%s(): Configure loop done, status = 0x%x\n",
-			__func__, status);
+		ql_log(ql_log_info, vha, 0x803d,
+		    "Configure loop done, status = 0x%x.\n", status);
 	}
 
 	if (!status) {
@@ -5450,9 +5460,9 @@
 			    ha->fce_dma, ha->fce_bufs, ha->fce_mb,
 			    &ha->fce_bufs);
 			if (rval) {
-				qla_printk(KERN_WARNING, ha,
-				    "Unable to reinitialize FCE "
-				    "(%d).\n", rval);
+				ql_log(ql_log_warn, vha, 0x803e,
+				    "Unable to reinitialize FCE (%d).\n",
+				    rval);
 				ha->flags.fce_enabled = 0;
 			}
 		}
@@ -5462,17 +5472,16 @@
 			rval = qla2x00_enable_eft_trace(vha,
 			    ha->eft_dma, EFT_NUM_BUFFERS);
 			if (rval) {
-				qla_printk(KERN_WARNING, ha,
-				    "Unable to reinitialize EFT "
-				    "(%d).\n", rval);
+				ql_log(ql_log_warn, vha, 0x803f,
+				    "Unable to reinitialize EFT (%d).\n",
+				    rval);
 			}
 		}
 	}
 
 	if (!status) {
-		DEBUG(printk(KERN_INFO
-			"qla82xx_restart_isp(%ld): succeeded.\n",
-			vha->host_no));
+		ql_dbg(ql_dbg_taskm, vha, 0x8040,
+		    "qla82xx_restart_isp succeeded.\n");
 
 		spin_lock_irqsave(&ha->vport_slock, flags);
 		list_for_each_entry(vp, &ha->vp_list, list) {
@@ -5489,8 +5498,8 @@
 		spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 	} else {
-		qla_printk(KERN_INFO, ha,
-			"qla82xx_restart_isp: **** FAILED ****\n");
+		ql_log(ql_log_warn, vha, 0x8041,
+		    "qla82xx_restart_isp **** FAILED ****.\n");
 	}
 
 	return status;
@@ -5640,9 +5649,8 @@
 	if (ret == QLA_SUCCESS)
 		fcport->fcp_prio = priority;
 	else
-		DEBUG2(printk(KERN_WARNING
-			"scsi(%ld): Unable to activate fcp priority, "
-			" ret=0x%x\n", vha->host_no, ret));
+		ql_dbg(ql_dbg_user, vha, 0x704f,
+		    "Unable to activate fcp priority, ret=0x%x.\n", ret);
 
 	return  ret;
 }
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 4c8167e..d2e904b 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -94,11 +94,11 @@
 
 	/* Don't print state transitions during initial allocation of fcport */
 	if (old_state && old_state != state) {
-		DEBUG(qla_printk(KERN_WARNING, fcport->vha->hw,
-		    "scsi(%ld): FCPort state transitioned from %s to %s - "
-		    "portid=%02x%02x%02x.\n", fcport->vha->host_no,
+		ql_dbg(ql_dbg_disc, fcport->vha, 0x207d,
+		    "FCPort state transitioned from %s to %s - "
+		    "portid=%02x%02x%02x.\n",
 		    port_state_str[old_state], port_state_str[state],
 		    fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa));
+		    fcport->d_id.b.al_pa);
 	}
 }
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 7bac3cd..49d6906 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -150,7 +150,8 @@
 
 	/* We only support T10 DIF right now */
 	if (guard != SHOST_DIX_GUARD_CRC) {
-		DEBUG2(printk(KERN_ERR "Unsupported guard: %d\n", guard));
+		ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3007,
+		    "Unsupported guard: %d for cmd=%p.\n", guard, sp->cmd);
 		return 0;
 	}
 
@@ -343,9 +344,10 @@
 
 	/* Send marker if required */
 	if (vha->marker_needed != 0) {
-		if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
-							!= QLA_SUCCESS)
+		if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+		    QLA_SUCCESS) {
 			return (QLA_FUNCTION_FAILED);
+		}
 		vha->marker_needed = 0;
 	}
 
@@ -490,8 +492,8 @@
 	mrk24 = NULL;
 	mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, 0);
 	if (mrk == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n",
-		    __func__, base_vha->host_no));
+		ql_log(ql_log_warn, base_vha, 0x3026,
+		    "Failed to allocate Marker IOCB.\n");
 
 		return (QLA_FUNCTION_FAILED);
 	}
@@ -547,9 +549,10 @@
 	device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
 	struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
 
-	DEBUG5(printk("%s(): IOCB data:\n", __func__));
-	DEBUG5(qla2x00_dump_buffer(
-	    (uint8_t *)req->ring_ptr, REQUEST_ENTRY_SIZE));
+	ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x302d,
+	    "IOCB data:\n");
+	ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302e,
+	    (uint8_t *)req->ring_ptr, REQUEST_ENTRY_SIZE);
 
 	/* Adjust ring index. */
 	req->ring_index++;
@@ -604,7 +607,7 @@
  * Returns the number of IOCB entries needed to store @dsds.
  */
 inline uint16_t
-qla24xx_calc_iocbs(uint16_t dsds)
+qla24xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds)
 {
 	uint16_t iocbs;
 
@@ -614,8 +617,6 @@
 		if ((dsds - 1) % 5)
 			iocbs++;
 	}
-	DEBUG3(printk(KERN_DEBUG "%s(): Required PKT(s) = %d\n",
-	    __func__, iocbs));
 	return iocbs;
 }
 
@@ -712,6 +713,7 @@
     unsigned int protcnt)
 {
 	struct sd_dif_tuple *spt;
+	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
 	unsigned char op = scsi_get_prot_op(cmd);
 
 	switch (scsi_get_prot_type(cmd)) {
@@ -768,9 +770,9 @@
 		    op == SCSI_PROT_WRITE_PASS)) {
 			spt = page_address(sg_page(scsi_prot_sglist(cmd))) +
 			    scsi_prot_sglist(cmd)[0].offset;
-			DEBUG18(printk(KERN_DEBUG
-			    "%s(): LBA from user %p, lba = 0x%x\n",
-			    __func__, spt, (int)spt->ref_tag));
+			ql_dbg(ql_dbg_io, vha, 0x3008,
+			    "LBA from user %p, lba = 0x%x for cmd=%p.\n",
+			    spt, (int)spt->ref_tag, cmd);
 			pkt->ref_tag = swab32(spt->ref_tag);
 			pkt->app_tag_mask[0] = 0x0;
 			pkt->app_tag_mask[1] = 0x0;
@@ -789,11 +791,11 @@
 		break;
 	}
 
-	DEBUG18(printk(KERN_DEBUG
-	    "%s(): Setting protection Tags: (BIG) ref tag = 0x%x,"
-	    " app tag = 0x%x, prot SG count %d , cmd lba 0x%x,"
-	    " prot_type=%u\n", __func__, pkt->ref_tag, pkt->app_tag, protcnt,
-	    (int)scsi_get_lba(cmd), scsi_get_prot_type(cmd)));
+	ql_dbg(ql_dbg_io, vha, 0x3009,
+	    "Setting protection Tags: (BIG) ref tag = 0x%x, app tag = 0x%x, "
+	    "prot SG count %d, cmd lba 0x%x, prot_type=%u cmd=%p.\n",
+	    pkt->ref_tag, pkt->app_tag, protcnt, (int)scsi_get_lba(cmd),
+	    scsi_get_prot_type(cmd), cmd);
 }
 
 
@@ -809,6 +811,7 @@
 	uint32_t *cur_dsd = dsd;
 	int	i;
 	uint16_t	used_dsds = tot_dsds;
+	scsi_qla_host_t *vha = shost_priv(sp->cmd->device->host);
 
 	uint8_t		*cp;
 
@@ -853,9 +856,10 @@
 			cur_dsd = (uint32_t *)next_dsd;
 		}
 		sle_dma = sg_dma_address(sg);
-		DEBUG18(printk("%s(): %p, sg entry %d - addr =0x%x 0x%x,"
-		    " len =%d\n", __func__ , cur_dsd, i, LSD(sle_dma),
-		    MSD(sle_dma), sg_dma_len(sg)));
+		ql_dbg(ql_dbg_io, vha, 0x300a,
+		    "sg entry %d - addr=0x%x 0x%x, " "len=%d for cmd=%p.\n",
+		    cur_dsd, i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg),
+		    sp->cmd);
 		*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
 		*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
 		*cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
@@ -863,8 +867,8 @@
 
 		if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
 			cp = page_address(sg_page(sg)) + sg->offset;
-			DEBUG18(printk("%s(): User Data buffer= %p:\n",
-			    __func__ , cp));
+			ql_dbg(ql_dbg_io, vha, 0x300b,
+			    "User data buffer=%p for cmd=%p.\n", cp, sp->cmd);
 		}
 	}
 	/* Null termination */
@@ -888,7 +892,7 @@
 	struct scsi_cmnd *cmd;
 	uint32_t *cur_dsd = dsd;
 	uint16_t	used_dsds = tot_dsds;
-
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	uint8_t		*cp;
 
 
@@ -935,10 +939,11 @@
 		}
 		sle_dma = sg_dma_address(sg);
 		if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
-			DEBUG18(printk(KERN_DEBUG
-			    "%s(): %p, sg entry %d - addr =0x%x"
-			    "0x%x, len =%d\n", __func__ , cur_dsd, i,
-			    LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg)));
+			ql_dbg(ql_dbg_io, vha, 0x3027,
+			    "%s(): %p, sg_entry %d - "
+			    "addr=0x%x0x%x, len=%d.\n",
+			    __func__, cur_dsd, i,
+			    LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg));
 		}
 		*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
 		*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
@@ -946,8 +951,9 @@
 
 		if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
 			cp = page_address(sg_page(sg)) + sg->offset;
-			DEBUG18(printk("%s(): Protection Data buffer = %p:\n",
-			    __func__ , cp));
+			ql_dbg(ql_dbg_io, vha, 0x3028,
+			    "%s(): Protection Data buffer = %p.\n", __func__,
+			    cp);
 		}
 		avail_dsds--;
 	}
@@ -996,21 +1002,15 @@
 	*((uint32_t *)(&cmd_pkt->entry_type)) =
 	    __constant_cpu_to_le32(COMMAND_TYPE_CRC_2);
 
-	/* No data transfer */
-	data_bytes = scsi_bufflen(cmd);
-	if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
-		DEBUG18(printk(KERN_INFO "%s: Zero data bytes or DMA-NONE %d\n",
-		    __func__, data_bytes));
-		cmd_pkt->byte_count = __constant_cpu_to_le32(0);
-		return QLA_SUCCESS;
-	}
-
 	vha = sp->fcport->vha;
 	ha = vha->hw;
 
-	DEBUG18(printk(KERN_DEBUG
-	    "%s(%ld): Executing cmd sp %p, prot_op=%u.\n", __func__,
-	    vha->host_no, sp, scsi_get_prot_op(sp->cmd)));
+	/* No data transfer */
+	data_bytes = scsi_bufflen(cmd);
+	if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+		return QLA_SUCCESS;
+	}
 
 	cmd_pkt->vp_index = sp->fcport->vp_idx;
 
@@ -1056,8 +1056,6 @@
 
 	/* Determine SCSI command length -- align to 4 byte boundary */
 	if (cmd->cmd_len > 16) {
-		DEBUG18(printk(KERN_INFO "%s(): **** SCSI CMD > 16\n",
-		    __func__));
 		additional_fcpcdb_len = cmd->cmd_len - 16;
 		if ((cmd->cmd_len % 4) != 0) {
 			/* SCSI cmd > 16 bytes must be multiple of 4 */
@@ -1108,11 +1106,6 @@
 
 	cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
 
-	DEBUG18(printk(KERN_INFO "%s(%ld): Total SG(s) Entries %d, Data"
-	    "entries %d, data bytes %d, Protection entries %d\n",
-	    __func__, vha->host_no, tot_dsds, (tot_dsds-tot_prot_dsds),
-	    data_bytes, tot_prot_dsds));
-
 	/* Compute dif len and adjust data len to incude protection */
 	total_bytes = data_bytes;
 	dif_bytes = 0;
@@ -1150,14 +1143,7 @@
 	    additional_fcpcdb_len);
 	*fcp_dl = htonl(total_bytes);
 
-	DEBUG18(printk(KERN_INFO "%s(%ld): dif bytes = 0x%x (%d), total bytes"
-	    " = 0x%x (%d), dat block size =0x%x (%d)\n", __func__,
-	    vha->host_no, dif_bytes, dif_bytes, total_bytes, total_bytes,
-	    crc_ctx_pkt->blk_size, crc_ctx_pkt->blk_size));
-
 	if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
-		DEBUG18(printk(KERN_INFO "%s: Zero data bytes or DMA-NONE %d\n",
-		    __func__, data_bytes));
 		cmd_pkt->byte_count = __constant_cpu_to_le32(0);
 		return QLA_SUCCESS;
 	}
@@ -1182,8 +1168,6 @@
 	return QLA_SUCCESS;
 
 crc_queuing_error:
-	DEBUG18(qla_printk(KERN_INFO, ha,
-	    "CMD sent FAILED crc_q error:sp = %p\n", sp));
 	/* Cleanup will be performed by the caller */
 
 	return QLA_FUNCTION_FAILED;
@@ -1225,8 +1209,8 @@
 
 	/* Send marker if required */
 	if (vha->marker_needed != 0) {
-		if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
-							!= QLA_SUCCESS)
+		if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+		    QLA_SUCCESS)
 			return QLA_FUNCTION_FAILED;
 		vha->marker_needed = 0;
 	}
@@ -1243,8 +1227,9 @@
 		if (!req->outstanding_cmds[handle])
 			break;
 	}
-	if (index == MAX_OUTSTANDING_COMMANDS)
+	if (index == MAX_OUTSTANDING_COMMANDS) {
 		goto queuing_error;
+	}
 
 	/* Map the sg table so we have an accurate count of sg entries needed */
 	if (scsi_sg_count(cmd)) {
@@ -1256,8 +1241,7 @@
 		nseg = 0;
 
 	tot_dsds = nseg;
-
-	req_cnt = qla24xx_calc_iocbs(tot_dsds);
+	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 	if (req->cnt < (req_cnt + 2)) {
 		cnt = RD_REG_DWORD_RELAXED(req->req_q_out);
 
@@ -1322,7 +1306,6 @@
 	/* Specify response queue number where completion should happen */
 	cmd_pkt->entry_status = (uint8_t) rsp->id;
 	wmb();
-
 	/* Adjust ring index. */
 	req->ring_index++;
 	if (req->ring_index == req->length) {
@@ -1534,9 +1517,6 @@
 	/* Cleanup will be performed by the caller (queuecommand) */
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
-	DEBUG18(qla_printk(KERN_INFO, ha,
-	    "CMD sent FAILED SCSI prot_op:%02x\n", scsi_get_prot_op(cmd)));
 	return QLA_FUNCTION_FAILED;
 }
 
@@ -1581,8 +1561,11 @@
 		if (!req->outstanding_cmds[handle])
 			break;
 	}
-	if (index == MAX_OUTSTANDING_COMMANDS)
+	if (index == MAX_OUTSTANDING_COMMANDS) {
+		ql_log(ql_log_warn, vha, 0x700b,
+		    "No room on oustanding cmd array.\n");
 		goto queuing_error;
+	}
 
 	/* Prep command array. */
 	req->current_outstanding_cmd = handle;
@@ -1999,8 +1982,11 @@
 	rval = QLA_FUNCTION_FAILED;
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp);
-	if (!pkt)
+	if (!pkt) {
+		ql_log(ql_log_warn, sp->fcport->vha, 0x700c,
+		    "qla2x00_alloc_iocbs failed.\n");
 		goto done;
+	}
 
 	rval = QLA_SUCCESS;
 	switch (ctx->type) {
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index ae8e298..b16b772 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -45,7 +45,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-		    "%s(): NULL response queue pointer\n", __func__);
+		    "%s(): NULL response queue pointer.\n", __func__);
 		return (IRQ_NONE);
 	}
 
@@ -91,9 +91,9 @@
 				qla2x00_async_event(vha, rsp, mb);
 			} else {
 				/*EMPTY*/
-				DEBUG2(printk("scsi(%ld): Unrecognized "
-				    "interrupt type (%d).\n",
-				    vha->host_no, mb[0]));
+				ql_dbg(ql_dbg_async, vha, 0x5025,
+				    "Unrecognized interrupt type (%d).\n",
+				    mb[0]);
 			}
 			/* Release mailbox registers. */
 			WRT_REG_WORD(&reg->semaphore, 0);
@@ -142,7 +142,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-		    "%s(): NULL response queue pointer\n", __func__);
+		    "%s(): NULL response queue pointer.\n", __func__);
 		return (IRQ_NONE);
 	}
 
@@ -160,11 +160,13 @@
 
 			hccr = RD_REG_WORD(&reg->hccr);
 			if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
-				qla_printk(KERN_INFO, ha, "Parity error -- "
-				    "HCCR=%x, Dumping firmware!\n", hccr);
+				ql_log(ql_log_warn, vha, 0x5026,
+				    "Parity error -- HCCR=%x, Dumping "
+				    "firmware.\n", hccr);
 			else
-				qla_printk(KERN_INFO, ha, "RISC paused -- "
-				    "HCCR=%x, Dumping firmware!\n", hccr);
+				ql_log(ql_log_warn, vha, 0x5027,
+				    "RISC paused -- HCCR=%x, Dumping "
+				    "firmware.\n", hccr);
 
 			/*
 			 * Issue a "HARD" reset in order for the RISC
@@ -213,9 +215,8 @@
 			qla2x00_async_event(vha, rsp, mb);
 			break;
 		default:
-			DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
-			    "(%d).\n",
-			    vha->host_no, stat & 0xff));
+			ql_dbg(ql_dbg_async, vha, 0x5028,
+			    "Unrecognized interrupt type (%d).\n", stat & 0xff);
 			break;
 		}
 		WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
@@ -262,11 +263,11 @@
 	}
 
 	if (ha->mcp) {
-		DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
-		    __func__, vha->host_no, ha->mcp->mb[0]));
+		ql_dbg(ql_dbg_async, vha, 0x5000,
+		    "Got mbx completion. cmd=%x.\n", ha->mcp->mb[0]);
 	} else {
-		DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
-		    __func__, vha->host_no));
+		ql_dbg(ql_dbg_async, vha, 0x5001,
+		    "MBX pointer ERROR.\n");
 	}
 }
 
@@ -285,22 +286,24 @@
 	for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++)
 		mb[cnt] = RD_REG_WORD(wptr);
 
-	DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- "
-	    "%04x %04x %04x %04x %04x %04x %04x.\n", vha->host_no,
-	    event[aen & 0xff],
-	    mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6]));
+	ql_dbg(ql_dbg_async, vha, 0x5021,
+	    "Inter-Driver Commucation %s -- "
+	    "%04x %04x %04x %04x %04x %04x %04x.\n",
+	    event[aen & 0xff], mb[0], mb[1], mb[2], mb[3],
+	    mb[4], mb[5], mb[6]);
 
 	/* Acknowledgement needed? [Notify && non-zero timeout]. */
 	timeout = (descr >> 8) & 0xf;
 	if (aen != MBA_IDC_NOTIFY || !timeout)
 		return;
 
-	DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- "
-	    "ACK timeout=%d.\n", vha->host_no, event[aen & 0xff], timeout));
+	ql_dbg(ql_dbg_async, vha, 0x5022,
+	    "Inter-Driver Commucation %s -- ACK timeout=%d.\n",
+	    vha->host_no, event[aen & 0xff], timeout);
 
 	rval = qla2x00_post_idc_ack_work(vha, mb);
 	if (rval != QLA_SUCCESS)
-		qla_printk(KERN_WARNING, vha->hw,
+		ql_log(ql_log_warn, vha, 0x5023,
 		    "IDC failed to post ACK.\n");
 }
 
@@ -393,15 +396,15 @@
 		break;
 
 	case MBA_RESET:			/* Reset */
-		DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n",
-			vha->host_no));
+		ql_dbg(ql_dbg_async, vha, 0x5002,
+		    "Asynchronous RESET.\n");
 
 		set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
 		break;
 
 	case MBA_SYSTEM_ERR:		/* System Error */
 		mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_warn, vha, 0x5003,
 		    "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
 		    "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
 
@@ -409,7 +412,7 @@
 
 		if (IS_FWI2_CAPABLE(ha)) {
 			if (mb[1] == 0 && mb[2] == 0) {
-				qla_printk(KERN_ERR, ha,
+				ql_log(ql_log_fatal, vha, 0x5004,
 				    "Unrecoverable Hardware Error: adapter "
 				    "marked OFFLINE!\n");
 				vha->flags.online = 0;
@@ -422,7 +425,7 @@
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 			}
 		} else if (mb[1] == 0) {
-			qla_printk(KERN_INFO, ha,
+			ql_log(ql_log_fatal, vha, 0x5005,
 			    "Unrecoverable Hardware Error: adapter marked "
 			    "OFFLINE!\n");
 			vha->flags.online = 0;
@@ -431,31 +434,27 @@
 		break;
 
 	case MBA_REQ_TRANSFER_ERR:	/* Request Transfer Error */
-		DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n",
-		    vha->host_no, mb[1]));
-		qla_printk(KERN_WARNING, ha,
-		    "ISP Request Transfer Error (%x).\n", mb[1]);
+		ql_log(ql_log_warn, vha, 0x5006,
+		    "ISP Request Transfer Error (%x).\n",  mb[1]);
 
 		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 		break;
 
 	case MBA_RSP_TRANSFER_ERR:	/* Response Transfer Error */
-		DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n",
-		    vha->host_no));
-		qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n");
+		ql_log(ql_log_warn, vha, 0x5007,
+		    "ISP Response Transfer Error.\n");
 
 		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 		break;
 
 	case MBA_WAKEUP_THRES:		/* Request Queue Wake-up */
-		DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_async, vha, 0x5008,
+		    "Asynchronous WAKEUP_THRES.\n");
 		break;
 
 	case MBA_LIP_OCCURRED:		/* Loop Initialization Procedure */
-		DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", vha->host_no,
-		    mb[1]));
-		qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]);
+		ql_log(ql_log_info, vha, 0x5009,
+		    "LIP occurred (%x).\n", mb[1]);
 
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
 			atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -488,10 +487,8 @@
 			ha->link_data_rate = mb[1];
 		}
 
-		DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n",
-		    vha->host_no, link_speed));
-		qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n",
-		    link_speed);
+		ql_log(ql_log_info, vha, 0x500a,
+		    "LOOP UP detected (%s Gbps).\n", link_speed);
 
 		vha->flags.management_server_logged_in = 0;
 		qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
@@ -500,12 +497,9 @@
 	case MBA_LOOP_DOWN:		/* Loop Down Event */
 		mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
 		mbx = IS_QLA82XX(ha) ? RD_REG_WORD(&reg82->mailbox_out[4]) : mbx;
-		DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
-		    "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3],
-		    mbx));
-		qla_printk(KERN_INFO, ha,
-		    "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3],
-		    mbx);
+		ql_log(ql_log_info, vha, 0x500b,
+		    "LOOP DOWN detected (%x %x %x %x).\n",
+		    mb[1], mb[2], mb[3], mbx);
 
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
 			atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -525,9 +519,7 @@
 		break;
 
 	case MBA_LIP_RESET:		/* LIP reset occurred */
-		DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
-		    vha->host_no, mb[1]));
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0x500c,
 		    "LIP reset occurred (%x).\n", mb[1]);
 
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
@@ -554,14 +546,15 @@
 			break;
 
 		if (IS_QLA8XXX_TYPE(ha)) {
-			DEBUG2(printk("scsi(%ld): DCBX Completed -- %04x %04x "
-			    "%04x\n", vha->host_no, mb[1], mb[2], mb[3]));
+			ql_dbg(ql_dbg_async, vha, 0x500d,
+			    "DCBX Completed -- %04x %04x %04x.\n",
+			    mb[1], mb[2], mb[3]);
 			if (ha->notify_dcbx_comp)
 				complete(&ha->dcbx_comp);
 
 		} else
-			DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE "
-			    "received.\n", vha->host_no));
+			ql_dbg(ql_dbg_async, vha, 0x500e,
+			    "Asynchronous P2P MODE received.\n");
 
 		/*
 		 * Until there's a transition from loop down to loop up, treat
@@ -594,10 +587,7 @@
 		if (IS_QLA2100(ha))
 			break;
 
-		DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection "
-		    "received.\n",
-		    vha->host_no));
-		qla_printk(KERN_INFO, ha,
+		ql_log(ql_log_info, vha, 0x500f,
 		    "Configuration change detected: value=%x.\n", mb[1]);
 
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
@@ -640,11 +630,9 @@
 
 		/* Global event -- port logout or port unavailable. */
 		if (mb[1] == 0xffff && mb[2] == 0x7) {
-			DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
-			    vha->host_no));
-			DEBUG(printk(KERN_INFO
-			    "scsi(%ld): Port unavailable %04x %04x %04x.\n",
-			    vha->host_no, mb[1], mb[2], mb[3]));
+			ql_dbg(ql_dbg_async, vha, 0x5010,
+			    "Port unavailable %04x %04x %04x.\n",
+			    mb[1], mb[2], mb[3]);
 
 			if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
 				atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -674,17 +662,15 @@
 		atomic_set(&vha->loop_down_timer, 0);
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
 		    atomic_read(&vha->loop_state) != LOOP_DEAD) {
-			DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE "
-			    "ignored %04x/%04x/%04x.\n", vha->host_no, mb[1],
-			    mb[2], mb[3]));
+			ql_dbg(ql_dbg_async, vha, 0x5011,
+			    "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
+			    mb[1], mb[2], mb[3]);
 			break;
 		}
 
-		DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
-		    vha->host_no));
-		DEBUG(printk(KERN_INFO
-		    "scsi(%ld): Port database changed %04x %04x %04x.\n",
-		    vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x5012,
+		    "Port database changed %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 
 		/*
 		 * Mark all devices as missing so we will login again.
@@ -707,20 +693,17 @@
 		if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff))
 			break;
 
-		DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
-		    vha->host_no));
-		DEBUG(printk(KERN_INFO
-		    "scsi(%ld): RSCN database changed -- %04x %04x %04x.\n",
-		    vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x5013,
+		    "RSCN database changed -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 
 		rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
 		host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
 				| vha->d_id.b.al_pa;
 		if (rscn_entry == host_pid) {
-			DEBUG(printk(KERN_INFO
-			    "scsi(%ld): Ignoring RSCN update to local host "
-			    "port ID (%06x)\n",
-			    vha->host_no, host_pid));
+			ql_dbg(ql_dbg_async, vha, 0x5014,
+			    "Ignoring RSCN update to local host "
+			    "port ID (%06x).\n", host_pid);
 			break;
 		}
 
@@ -747,8 +730,8 @@
 
 	/* case MBA_RIO_RESPONSE: */
 	case MBA_ZIO_RESPONSE:
-		DEBUG3(printk("scsi(%ld): [R|Z]IO update completion.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_async, vha, 0x5015,
+		    "[R|Z]IO update completion.\n");
 
 		if (IS_FWI2_CAPABLE(ha))
 			qla24xx_process_response_queue(vha, rsp);
@@ -757,61 +740,68 @@
 		break;
 
 	case MBA_DISCARD_RND_FRAME:
-		DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x "
-		    "%04x.\n", vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x5016,
+		    "Discard RND Frame -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 		break;
 
 	case MBA_TRACE_NOTIFICATION:
-		DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n",
-		vha->host_no, mb[1], mb[2]));
+		ql_dbg(ql_dbg_async, vha, 0x5017,
+		    "Trace Notification -- %04x %04x.\n", mb[1], mb[2]);
 		break;
 
 	case MBA_ISP84XX_ALERT:
-		DEBUG2(printk("scsi(%ld): ISP84XX Alert Notification -- "
-		    "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x5018,
+		    "ISP84XX Alert Notification -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 
 		spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
 		switch (mb[1]) {
 		case A84_PANIC_RECOVERY:
-			qla_printk(KERN_INFO, ha, "Alert 84XX: panic recovery "
-			    "%04x %04x\n", mb[2], mb[3]);
+			ql_log(ql_log_info, vha, 0x5019,
+			    "Alert 84XX: panic recovery %04x %04x.\n",
+			    mb[2], mb[3]);
 			break;
 		case A84_OP_LOGIN_COMPLETE:
 			ha->cs84xx->op_fw_version = mb[3] << 16 | mb[2];
-			DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:"
-			    "firmware version %x\n", ha->cs84xx->op_fw_version));
+			ql_log(ql_log_info, vha, 0x501a,
+			    "Alert 84XX: firmware version %x.\n",
+			    ha->cs84xx->op_fw_version);
 			break;
 		case A84_DIAG_LOGIN_COMPLETE:
 			ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
-			DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:"
-			    "diagnostic firmware version %x\n",
-			    ha->cs84xx->diag_fw_version));
+			ql_log(ql_log_info, vha, 0x501b,
+			    "Alert 84XX: diagnostic firmware version %x.\n",
+			    ha->cs84xx->diag_fw_version);
 			break;
 		case A84_GOLD_LOGIN_COMPLETE:
 			ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
 			ha->cs84xx->fw_update = 1;
-			DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX: gold "
-			    "firmware version %x\n",
-			    ha->cs84xx->gold_fw_version));
+			ql_log(ql_log_info, vha, 0x501c,
+			    "Alert 84XX: gold firmware version %x.\n",
+			    ha->cs84xx->gold_fw_version);
 			break;
 		default:
-			qla_printk(KERN_ERR, ha,
-			    "Alert 84xx: Invalid Alert %04x %04x %04x\n",
+			ql_log(ql_log_warn, vha, 0x501d,
+			    "Alert 84xx: Invalid Alert %04x %04x %04x.\n",
 			    mb[1], mb[2], mb[3]);
 		}
 		spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags);
 		break;
 	case MBA_DCBX_START:
-		DEBUG2(printk("scsi(%ld): DCBX Started -- %04x %04x %04x\n",
-		    vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x501e,
+		    "DCBX Started -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 		break;
 	case MBA_DCBX_PARAM_UPDATE:
-		DEBUG2(printk("scsi(%ld): DCBX Parameters Updated -- "
-		    "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x501f,
+		    "DCBX Parameters Updated -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 		break;
 	case MBA_FCF_CONF_ERR:
-		DEBUG2(printk("scsi(%ld): FCF Configuration Error -- "
-		    "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3]));
+		ql_dbg(ql_dbg_async, vha, 0x5020,
+		    "FCF Configuration Error -- %04x %04x %04x.\n",
+		    mb[1], mb[2], mb[3]);
 		break;
 	case MBA_IDC_COMPLETE:
 	case MBA_IDC_NOTIFY:
@@ -838,10 +828,8 @@
 
 	/* Validate handle. */
 	if (index >= MAX_OUTSTANDING_COMMANDS) {
-		DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n",
-		    vha->host_no, index));
-		qla_printk(KERN_WARNING, ha,
-		    "Invalid SCSI completion handle %d.\n", index);
+		ql_log(ql_log_warn, vha, 0x3014,
+		    "Invalid SCSI command index (%x).\n", index);
 
 		if (IS_QLA82XX(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
@@ -859,10 +847,7 @@
 		sp->cmd->result = DID_OK << 16;
 		qla2x00_sp_compl(ha, sp);
 	} else {
-		DEBUG2(printk("scsi(%ld) Req:%d: Invalid ISP SCSI completion"
-			" handle(0x%x)\n", vha->host_no, req->id, index));
-		qla_printk(KERN_WARNING, ha,
-		    "Invalid ISP SCSI completion handle\n");
+		ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n");
 
 		if (IS_QLA82XX(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
@@ -882,8 +867,8 @@
 
 	index = LSW(pkt->handle);
 	if (index >= MAX_OUTSTANDING_COMMANDS) {
-		qla_printk(KERN_WARNING, ha,
-		    "%s: Invalid completion handle (%x).\n", func, index);
+		ql_log(ql_log_warn, vha, 0x5031,
+		    "Invalid command index (%x).\n", index);
 		if (IS_QLA82XX(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
 		else
@@ -892,15 +877,13 @@
 	}
 	sp = req->outstanding_cmds[index];
 	if (!sp) {
-		qla_printk(KERN_WARNING, ha,
-		    "%s: Invalid completion handle (%x) -- timed-out.\n", func,
-		    index);
+		ql_log(ql_log_warn, vha, 0x5032,
+		    "Invalid completion handle (%x) -- timed-out.\n", index);
 		return sp;
 	}
 	if (sp->handle != index) {
-		qla_printk(KERN_WARNING, ha,
-		    "%s: SRB handle (%x) mismatch %x.\n", func, sp->handle,
-		    index);
+		ql_log(ql_log_warn, vha, 0x5033,
+		    "SRB handle (%x) mismatch %x.\n", sp->handle, index);
 		return NULL;
 	}
 
@@ -937,17 +920,17 @@
 	data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 	    QLA_LOGIO_LOGIN_RETRIED : 0;
 	if (mbx->entry_status) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error entry - portid=%02x%02x%02x "
+		ql_dbg(ql_dbg_async, vha, 0x5043,
+		    "Async-%s error entry - portid=%02x%02x%02x "
 		    "entry-status=%x status=%x state-flag=%x "
 		    "status-flags=%x.\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    fcport->d_id.b.domain, fcport->d_id.b.area,
+		    type, fcport->d_id.b.domain, fcport->d_id.b.area,
 		    fcport->d_id.b.al_pa, mbx->entry_status,
 		    le16_to_cpu(mbx->status), le16_to_cpu(mbx->state_flags),
-		    le16_to_cpu(mbx->status_flags)));
+		    le16_to_cpu(mbx->status_flags));
 
-		DEBUG2(qla2x00_dump_buffer((uint8_t *)mbx, sizeof(*mbx)));
+		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5057,
+		    (uint8_t *)mbx, sizeof(*mbx));
 
 		goto logio_done;
 	}
@@ -957,12 +940,10 @@
 	    le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE)
 		status = 0;
 	if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) {
-		DEBUG2(printk(KERN_DEBUG
-		    "scsi(%ld:%x): Async-%s complete - portid=%02x%02x%02x "
-		    "mbx1=%x.\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa, le16_to_cpu(mbx->mb1)));
+		ql_dbg(ql_dbg_async, vha, 0x5045,
+		    "Async-%s complete - portid=%02x%02x%02x mbx1=%x.\n",
+		    type, fcport->d_id.b.domain, fcport->d_id.b.area,
+		    fcport->d_id.b.al_pa, le16_to_cpu(mbx->mb1));
 
 		data[0] = MBS_COMMAND_COMPLETE;
 		if (ctx->type == SRB_LOGIN_CMD) {
@@ -987,14 +968,14 @@
 		break;
 	}
 
-	DEBUG2(printk(KERN_WARNING
-	    "scsi(%ld:%x): Async-%s failed - portid=%02x%02x%02x status=%x "
+	ql_log(ql_log_warn, vha, 0x5046,
+	    "Async-%s failed - portid=%02x%02x%02x status=%x "
 	    "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
-	    fcport->vha->host_no, sp->handle, type, fcport->d_id.b.domain,
+	    type, fcport->d_id.b.domain,
 	    fcport->d_id.b.area, fcport->d_id.b.al_pa, status,
 	    le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1),
 	    le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6),
-	    le16_to_cpu(mbx->mb7)));
+	    le16_to_cpu(mbx->mb7));
 
 logio_done:
 	lio->done(sp);
@@ -1025,9 +1006,8 @@
 		type = "ct pass-through";
 		break;
 	default:
-		qla_printk(KERN_WARNING, ha,
-		    "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp,
-		    sp_bsg->type);
+		ql_log(ql_log_warn, vha, 0x5047,
+		    "Unrecognized SRB: (%p) type=%d.\n", sp, sp_bsg->type);
 		return;
 	}
 
@@ -1045,20 +1025,20 @@
 			bsg_job->reply->reply_payload_rcv_len =
 			    le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len);
 
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			    "scsi(%ld): CT pass-through-%s error "
+			ql_log(ql_log_warn, vha, 0x5048,
+			    "CT pass-through-%s error "
 			    "comp_status-status=0x%x total_byte = 0x%x.\n",
-			    vha->host_no, type, comp_status,
-			    bsg_job->reply->reply_payload_rcv_len));
+			    type, comp_status,
+			    bsg_job->reply->reply_payload_rcv_len);
 		} else {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			    "scsi(%ld): CT pass-through-%s error "
-			    "comp_status-status=0x%x.\n",
-			    vha->host_no, type, comp_status));
+			ql_log(ql_log_warn, vha, 0x5049,
+			    "CT pass-through-%s error "
+			    "comp_status-status=0x%x.\n", type, comp_status);
 			bsg_job->reply->result = DID_ERROR << 16;
 			bsg_job->reply->reply_payload_rcv_len = 0;
 		}
-		DEBUG2(qla2x00_dump_buffer((uint8_t *)pkt, sizeof(*pkt)));
+		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5058,
+		    (uint8_t *)pkt, sizeof(*pkt));
 	} else {
 		bsg_job->reply->result =  DID_OK << 16;
 		bsg_job->reply->reply_payload_rcv_len =
@@ -1110,9 +1090,8 @@
 		type = "ct pass-through";
 		break;
 	default:
-		qla_printk(KERN_WARNING, ha,
-		    "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp,
-		    sp_bsg->type);
+		ql_log(ql_log_warn, vha, 0x503e,
+		    "Unrecognized SRB: (%p) type=%d.\n", sp, sp_bsg->type);
 		return;
 	}
 
@@ -1132,27 +1111,31 @@
 			bsg_job->reply->reply_payload_rcv_len =
 				le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count);
 
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			    "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x "
+			ql_log(ql_log_info, vha, 0x503f,
+			    "ELS-CT pass-through-%s error comp_status-status=0x%x "
 			    "error subcode 1=0x%x error subcode 2=0x%x total_byte = 0x%x.\n",
-				vha->host_no, sp->handle, type, comp_status, fw_status[1], fw_status[2],
-				le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count)));
+			    type, comp_status, fw_status[1], fw_status[2],
+			    le16_to_cpu(((struct els_sts_entry_24xx *)
+				pkt)->total_byte_count));
 			fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
 			memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
 		}
 		else {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			    "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x "
+			ql_log(ql_log_info, vha, 0x5040,
+			    "ELS-CT pass-through-%s error comp_status-status=0x%x "
 			    "error subcode 1=0x%x error subcode 2=0x%x.\n",
-				vha->host_no, sp->handle, type, comp_status,
-				le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1),
-				le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2)));
+			    type, comp_status,
+			    le16_to_cpu(((struct els_sts_entry_24xx *)
+				pkt)->error_subcode_1),
+			    le16_to_cpu(((struct els_sts_entry_24xx *)
+				    pkt)->error_subcode_2));
 			bsg_job->reply->result = DID_ERROR << 16;
 			bsg_job->reply->reply_payload_rcv_len = 0;
 			fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
 			memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
 		}
-		DEBUG2(qla2x00_dump_buffer((uint8_t *)pkt, sizeof(*pkt)));
+		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5056,
+				(uint8_t *)pkt, sizeof(*pkt));
 	}
 	else {
 		bsg_job->reply->result =  DID_OK << 16;
@@ -1201,25 +1184,24 @@
 	data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 		QLA_LOGIO_LOGIN_RETRIED : 0;
 	if (logio->entry_status) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error entry - "
+		ql_log(ql_log_warn, vha, 0x5034,
+		    "Async-%s error entry - "
 		    "portid=%02x%02x%02x entry-status=%x.\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa, logio->entry_status));
-		DEBUG2(qla2x00_dump_buffer((uint8_t *)logio, sizeof(*logio)));
+		    type, fcport->d_id.b.domain, fcport->d_id.b.area,
+		    fcport->d_id.b.al_pa, logio->entry_status);
+		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5059,
+		    (uint8_t *)logio, sizeof(*logio));
 
 		goto logio_done;
 	}
 
 	if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) {
-		DEBUG2(printk(KERN_DEBUG
-		    "scsi(%ld:%x): Async-%s complete - portid=%02x%02x%02x "
+		ql_dbg(ql_dbg_async, vha, 0x5036,
+		    "Async-%s complete - portid=%02x%02x%02x "
 		    "iop0=%x.\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    fcport->d_id.b.domain, fcport->d_id.b.area,
+		    type, fcport->d_id.b.domain, fcport->d_id.b.area,
 		    fcport->d_id.b.al_pa,
-		    le32_to_cpu(logio->io_parameter[0])));
+		    le32_to_cpu(logio->io_parameter[0]));
 
 		data[0] = MBS_COMMAND_COMPLETE;
 		if (ctx->type != SRB_LOGIN_CMD)
@@ -1256,14 +1238,14 @@
 		break;
 	}
 
-	DEBUG2(printk(KERN_WARNING
-	    "scsi(%ld:%x): Async-%s failed - portid=%02x%02x%02x comp=%x "
+	ql_dbg(ql_dbg_async, vha, 0x5037,
+	    "Async-%s failed - portid=%02x%02x%02x comp=%x "
 	    "iop0=%x iop1=%x.\n",
-	    fcport->vha->host_no, sp->handle, type, fcport->d_id.b.domain,
+	    type, fcport->d_id.b.domain,
 	    fcport->d_id.b.area, fcport->d_id.b.al_pa,
 	    le16_to_cpu(logio->comp_status),
 	    le32_to_cpu(logio->io_parameter[0]),
-	    le32_to_cpu(logio->io_parameter[1])));
+	    le32_to_cpu(logio->io_parameter[1]));
 
 logio_done:
 	lio->done(sp);
@@ -1292,38 +1274,34 @@
 	fcport = sp->fcport;
 
 	if (sts->entry_status) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error - entry-status(%x).\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    sts->entry_status));
+		ql_log(ql_log_warn, vha, 0x5038,
+		    "Async-%s error - entry-status(%x).\n",
+		    type, sts->entry_status);
 	} else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error - completion status(%x).\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    sts->comp_status));
+		ql_log(ql_log_warn, vha, 0x5039,
+		    "Async-%s error - completion status(%x).\n",
+		    type, sts->comp_status);
 	} else if (!(le16_to_cpu(sts->scsi_status) &
 	    SS_RESPONSE_INFO_LEN_VALID)) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error - no response info(%x).\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    sts->scsi_status));
+		ql_log(ql_log_warn, vha, 0x503a,
+		    "Async-%s error - no response info(%x).\n",
+		    type, sts->scsi_status);
 	} else if (le32_to_cpu(sts->rsp_data_len) < 4) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error - not enough response(%d).\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    sts->rsp_data_len));
+		ql_log(ql_log_warn, vha, 0x503b,
+		    "Async-%s error - not enough response(%d).\n",
+		    type, sts->rsp_data_len);
 	} else if (sts->data[3]) {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld:%x): Async-%s error - response(%x).\n",
-		    fcport->vha->host_no, sp->handle, type,
-		    sts->data[3]));
+		ql_log(ql_log_warn, vha, 0x503c,
+		    "Async-%s error - response(%x).\n",
+		    type, sts->data[3]);
 	} else {
 		error = 0;
 	}
 
 	if (error) {
 		iocb->u.tmf.data = error;
-		DEBUG2(qla2x00_dump_buffer((uint8_t *)sts, sizeof(*sts)));
+		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055,
+		    (uint8_t *)sts, sizeof(*sts));
 	}
 
 	iocb->done(sp);
@@ -1360,8 +1338,8 @@
 		}
 
 		if (pkt->entry_status != 0) {
-			DEBUG3(printk(KERN_INFO
-			    "scsi(%ld): Process error entry.\n", vha->host_no));
+			ql_log(ql_log_warn, vha, 0x5035,
+			    "Process error entry.\n");
 
 			qla2x00_error_entry(vha, rsp, pkt);
 			((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -1399,10 +1377,10 @@
 			break;
 		default:
 			/* Type Not Supported. */
-			DEBUG4(printk(KERN_WARNING
-			    "scsi(%ld): Received unknown response pkt type %x "
+			ql_log(ql_log_warn, vha, 0x504a,
+			    "Received unknown response pkt type %x "
 			    "entry status=%x.\n",
-			    vha->host_no, pkt->entry_type, pkt->entry_status));
+			    pkt->entry_type, pkt->entry_status);
 			break;
 		}
 		((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -1418,6 +1396,7 @@
 qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
     uint32_t sense_len, struct rsp_que *rsp)
 {
+	struct scsi_qla_host *vha = sp->fcport->vha;
 	struct scsi_cmnd *cp = sp->cmd;
 
 	if (sense_len >= SCSI_SENSE_BUFFERSIZE)
@@ -1435,11 +1414,13 @@
 	if (sp->request_sense_length != 0)
 		rsp->status_srb = sp;
 
-	DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) "
-	    "cmd=%p\n", __func__, sp->fcport->vha->host_no,
-	    cp->device->channel, cp->device->id, cp->device->lun, cp));
+	ql_dbg(ql_dbg_io, vha, 0x301c,
+	    "Check condition Sense data, scsi(%ld:%d:%d:%d) cmd=%p.\n",
+	    sp->fcport->vha->host_no, cp->device->channel, cp->device->id,
+	    cp->device->lun, cp);
 	if (sense_len)
-		DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, sense_len));
+		ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302b,
+		    cp->sense_buffer, sense_len);
 }
 
 struct scsi_dif_tuple {
@@ -1457,6 +1438,7 @@
 static inline void
 qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
 {
+	struct scsi_qla_host *vha = sp->fcport->vha;
 	struct scsi_cmnd *cmd = sp->cmd;
 	struct scsi_dif_tuple	*ep =
 			(struct scsi_dif_tuple *)&sts24->data[20];
@@ -1473,15 +1455,15 @@
 	e_guard = be16_to_cpu(ep->guard);
 	a_guard = be16_to_cpu(ap->guard);
 
-	DEBUG18(printk(KERN_DEBUG
-	    "%s(): iocb(s) %p Returned STATUS\n", __func__, sts24));
+	ql_dbg(ql_dbg_io, vha, 0x3023,
+	    "iocb(s) %p Returned STATUS.\n", sts24);
 
-	DEBUG18(printk(KERN_ERR "DIF ERROR in cmd 0x%x lba 0x%llx act ref"
+	ql_dbg(ql_dbg_io, vha, 0x3024,
+	    "DIF ERROR in cmd 0x%x lba 0x%llx act ref"
 	    " tag=0x%x, exp ref_tag=0x%x, act app tag=0x%x, exp app"
-	    " tag=0x%x, act guard=0x%x, exp guard=0x%x\n",
+	    " tag=0x%x, act guard=0x%x, exp guard=0x%x.\n",
 	    cmd->cmnd[0], (u64)scsi_get_lba(cmd), a_ref_tag, e_ref_tag,
-	    a_app_tag, e_app_tag, a_guard, e_guard));
-
+	    a_app_tag, e_app_tag, a_guard, e_guard);
 
 	/* check guard */
 	if (e_guard != a_guard) {
@@ -1569,9 +1551,8 @@
 		sp = NULL;
 
 	if (sp == NULL) {
-		qla_printk(KERN_WARNING, ha,
-		    "scsi(%ld): Invalid status handle (0x%x).\n", vha->host_no,
-		    sts->handle);
+		ql_log(ql_log_warn, vha, 0x3017,
+		    "Invalid status handle (0x%x).\n", sts->handle);
 
 		if (IS_QLA82XX(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
@@ -1582,9 +1563,9 @@
 	}
 	cp = sp->cmd;
 	if (cp == NULL) {
-		qla_printk(KERN_WARNING, ha,
-		    "scsi(%ld): Command already returned (0x%x/%p).\n",
-		    vha->host_no, sts->handle, sp);
+		ql_log(ql_log_warn, vha, 0x3018,
+		    "Command already returned (0x%x/%p).\n",
+		    sts->handle, sp);
 
 		return;
 	}
@@ -1629,10 +1610,9 @@
 			par_sense_len -= rsp_info_len;
 		}
 		if (rsp_info_len > 3 && rsp_info[3]) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "scsi(%ld:%d:%d): FCP I/O protocol failure "
-			    "(0x%x/0x%x).\n", vha->host_no, cp->device->id,
-			    cp->device->lun, rsp_info_len, rsp_info[3]));
+			ql_log(ql_log_warn, vha, 0x3019,
+			    "FCP I/O protocol failure (0x%x/0x%x).\n",
+			    rsp_info_len, rsp_info[3]);
 
 			cp->result = DID_BUS_BUSY << 16;
 			goto out;
@@ -1661,11 +1641,10 @@
 			if (!lscsi_status &&
 			    ((unsigned)(scsi_bufflen(cp) - resid) <
 			     cp->underflow)) {
-				qla_printk(KERN_INFO, ha,
-				    "scsi(%ld:%d:%d): Mid-layer underflow "
+				ql_log(ql_log_warn, vha, 0x301a,
+				    "Mid-layer underflow "
 				    "detected (0x%x of 0x%x bytes).\n",
-				    vha->host_no, cp->device->id,
-				    cp->device->lun, resid, scsi_bufflen(cp));
+				    resid, scsi_bufflen(cp));
 
 				cp->result = DID_ERROR << 16;
 				break;
@@ -1674,9 +1653,8 @@
 		cp->result = DID_OK << 16 | lscsi_status;
 
 		if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "scsi(%ld:%d:%d) QUEUE FULL detected.\n",
-			    vha->host_no, cp->device->id, cp->device->lun));
+			ql_log(ql_log_warn, vha, 0x301b,
+			    "QUEUE FULL detected.\n");
 			break;
 		}
 		logit = 0;
@@ -1697,11 +1675,10 @@
 		scsi_set_resid(cp, resid);
 		if (scsi_status & SS_RESIDUAL_UNDER) {
 			if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
-				DEBUG2(qla_printk(KERN_INFO, ha,
-				    "scsi(%ld:%d:%d) Dropped frame(s) detected "
-				    "(0x%x of 0x%x bytes).\n", vha->host_no,
-				    cp->device->id, cp->device->lun, resid,
-				    scsi_bufflen(cp)));
+				ql_log(ql_log_warn, vha, 0x301d,
+				    "Dropped frame(s) detected "
+				    "(0x%x of 0x%x bytes).\n",
+				    resid, scsi_bufflen(cp));
 
 				cp->result = DID_ERROR << 16 | lscsi_status;
 				break;
@@ -1710,20 +1687,18 @@
 			if (!lscsi_status &&
 			    ((unsigned)(scsi_bufflen(cp) - resid) <
 			    cp->underflow)) {
-				qla_printk(KERN_INFO, ha,
-				    "scsi(%ld:%d:%d): Mid-layer underflow "
+				ql_log(ql_log_warn, vha, 0x301e,
+				    "Mid-layer underflow "
 				    "detected (0x%x of 0x%x bytes).\n",
-				    vha->host_no, cp->device->id,
-				    cp->device->lun, resid, scsi_bufflen(cp));
+				    resid, scsi_bufflen(cp));
 
 				cp->result = DID_ERROR << 16;
 				break;
 			}
 		} else {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "scsi(%ld:%d:%d) Dropped frame(s) detected (0x%x "
-			    "of 0x%x bytes).\n", vha->host_no, cp->device->id,
-			    cp->device->lun, resid, scsi_bufflen(cp)));
+			ql_log(ql_log_warn, vha, 0x301f,
+			    "Dropped frame(s) detected (0x%x "
+			    "of 0x%x bytes).\n", resid, scsi_bufflen(cp));
 
 			cp->result = DID_ERROR << 16 | lscsi_status;
 			goto check_scsi_status;
@@ -1739,10 +1714,8 @@
 		 */
 		if (lscsi_status != 0) {
 			if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
-				DEBUG2(qla_printk(KERN_INFO, ha,
-				    "scsi(%ld:%d:%d) QUEUE FULL detected.\n",
-				    vha->host_no, cp->device->id,
-				    cp->device->lun));
+				ql_log(ql_log_warn, vha, 0x3020,
+				    "QUEUE FULL detected.\n");
 				logit = 1;
 				break;
 			}
@@ -1781,10 +1754,9 @@
 				break;
 		}
 
-		DEBUG2(qla_printk(KERN_INFO, ha,
-			"scsi(%ld:%d:%d) Port down status: port-state=0x%x\n",
-			vha->host_no, cp->device->id, cp->device->lun,
-			atomic_read(&fcport->state)));
+		ql_dbg(ql_dbg_io, vha, 0x3021,
+		    "Port down status: port-state=0x%x.\n",
+		    atomic_read(&fcport->state));
 
 		if (atomic_read(&fcport->state) == FCS_ONLINE)
 			qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
@@ -1804,15 +1776,13 @@
 
 out:
 	if (logit)
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "scsi(%ld:%d:%d) FCP command status: 0x%x-0x%x (0x%x) "
-		    "portid=%02x%02x%02x oxid=0x%x cdb=%02x%02x%02x len=0x%x "
-		    "rsp_info=0x%x resid=0x%x fw_resid=0x%x\n", vha->host_no,
-		    cp->device->id, cp->device->lun, comp_status, scsi_status,
-		    cp->result, fcport->d_id.b.domain, fcport->d_id.b.area,
-		    fcport->d_id.b.al_pa, ox_id, cp->cmnd[0], cp->cmnd[1],
-		    cp->cmnd[2], scsi_bufflen(cp), rsp_info_len, resid_len,
-		    fw_resid_len));
+		ql_dbg(ql_dbg_io, vha, 0x3022,
+		    "FCP command status: 0x%x-0x%x (0x%x) "
+		    "oxid=0x%x cdb=%02x%02x%02x len=0x%x "
+		    "rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n",
+		    comp_status, scsi_status, cp->result, ox_id, cp->cmnd[0],
+		    cp->cmnd[1], cp->cmnd[2], scsi_bufflen(cp), rsp_info_len,
+		    resid_len, fw_resid_len);
 
 	if (rsp->status_srb == NULL)
 		qla2x00_sp_compl(ha, sp);
@@ -1830,16 +1800,15 @@
 {
 	uint8_t		sense_sz = 0;
 	struct qla_hw_data *ha = rsp->hw;
+	struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
 	srb_t		*sp = rsp->status_srb;
 	struct scsi_cmnd *cp;
 
 	if (sp != NULL && sp->request_sense_length != 0) {
 		cp = sp->cmd;
 		if (cp == NULL) {
-			DEBUG2(printk("%s(): Cmd already returned back to OS "
-			    "sp=%p.\n", __func__, sp));
-			qla_printk(KERN_INFO, ha,
-			    "cmd is NULL: already returned to OS (sp=%p)\n",
+			ql_log(ql_log_warn, vha, 0x3025,
+			    "cmd is NULL: already returned to OS (sp=%p).\n",
 			    sp);
 
 			rsp->status_srb = NULL;
@@ -1856,7 +1825,8 @@
 		if (IS_FWI2_CAPABLE(ha))
 			host_to_fcp_swap(pkt->data, sizeof(pkt->data));
 		memcpy(sp->request_sense_ptr, pkt->data, sense_sz);
-		DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz));
+		ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
+			sp->request_sense_ptr, sense_sz);
 
 		sp->request_sense_ptr += sense_sz;
 		sp->request_sense_length -= sense_sz;
@@ -1882,21 +1852,25 @@
 	uint32_t handle = LSW(pkt->handle);
 	uint16_t que = MSW(pkt->handle);
 	struct req_que *req = ha->req_q_map[que];
-#if defined(QL_DEBUG_LEVEL_2)
+
 	if (pkt->entry_status & RF_INV_E_ORDER)
-		qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
+		ql_dbg(ql_dbg_async, vha, 0x502a,
+		    "Invalid Entry Order.\n");
 	else if (pkt->entry_status & RF_INV_E_COUNT)
-		qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__);
+		ql_dbg(ql_dbg_async, vha, 0x502b,
+		    "Invalid Entry Count.\n");
 	else if (pkt->entry_status & RF_INV_E_PARAM)
-		qla_printk(KERN_ERR, ha,
-		    "%s: Invalid Entry Parameter\n", __func__);
+		ql_dbg(ql_dbg_async, vha, 0x502c,
+		    "Invalid Entry Parameter.\n");
 	else if (pkt->entry_status & RF_INV_E_TYPE)
-		qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__);
+		ql_dbg(ql_dbg_async, vha, 0x502d,
+		    "Invalid Entry Type.\n");
 	else if (pkt->entry_status & RF_BUSY)
-		qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__);
+		ql_dbg(ql_dbg_async, vha, 0x502e,
+		    "Busy.\n");
 	else
-		qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__);
-#endif
+		ql_dbg(ql_dbg_async, vha, 0x502f,
+		    "UNKNOWN flag error.\n");
 
 	/* Validate handle. */
 	if (handle < MAX_OUTSTANDING_COMMANDS)
@@ -1923,10 +1897,8 @@
 	} else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type ==
 		COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7
 		|| pkt->entry_type == COMMAND_TYPE_6) {
-		DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
-			vha->host_no));
-		qla_printk(KERN_WARNING, ha,
-			"Error entry - invalid handle\n");
+		ql_log(ql_log_warn, vha, 0x5030,
+		    "Error entry - invalid handle.\n");
 
 		if (IS_QLA82XX(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
@@ -1960,11 +1932,11 @@
 	}
 
 	if (ha->mcp) {
-		DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
-		    __func__, vha->host_no, ha->mcp->mb[0]));
+		ql_dbg(ql_dbg_async, vha, 0x504d,
+		    "Got mailbox completion. cmd=%x.\n", ha->mcp->mb[0]);
 	} else {
-		DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
-		    __func__, vha->host_no));
+		ql_dbg(ql_dbg_async, vha, 0x504e,
+		    "MBX pointer ERROR.\n");
 	}
 }
 
@@ -1993,8 +1965,8 @@
 		}
 
 		if (pkt->entry_status != 0) {
-			DEBUG3(printk(KERN_INFO
-			    "scsi(%ld): Process error entry.\n", vha->host_no));
+			ql_dbg(ql_dbg_async, vha, 0x5029,
+			    "Process error entry.\n");
 
 			qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
 			((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -2030,10 +2002,10 @@
 			break;
 		default:
 			/* Type Not Supported. */
-			DEBUG4(printk(KERN_WARNING
-			    "scsi(%ld): Received unknown response pkt type %x "
+			ql_dbg(ql_dbg_async, vha, 0x5042,
+			    "Received unknown response pkt type %x "
 			    "entry status=%x.\n",
-			    vha->host_no, pkt->entry_type, pkt->entry_status));
+			    pkt->entry_type, pkt->entry_status);
 			break;
 		}
 		((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -2088,7 +2060,8 @@
 
 next_test:
 	if (RD_REG_DWORD(&reg->iobase_c8) & BIT_3)
-		qla_printk(KERN_INFO, ha, "Additional code -- 0x55AA.\n");
+		ql_log(ql_log_info, vha, 0x504c,
+		    "Additional code -- 0x55AA.\n");
 
 done:
 	WRT_REG_DWORD(&reg->iobase_window, 0x0000);
@@ -2121,7 +2094,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-		    "%s(): NULL response queue pointer\n", __func__);
+		    "%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 
@@ -2142,8 +2115,9 @@
 
 			hccr = RD_REG_DWORD(&reg->hccr);
 
-			qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
-			    "Dumping firmware!\n", hccr);
+			ql_log(ql_log_warn, vha, 0x504b,
+			    "RISC paused -- HCCR=%x, Dumping firmware.\n",
+			    hccr);
 
 			qla2xxx_check_risc_status(vha);
 
@@ -2174,9 +2148,8 @@
 			qla24xx_process_response_queue(vha, rsp);
 			break;
 		default:
-			DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
-			    "(%d).\n",
-			    vha->host_no, stat & 0xff));
+			ql_dbg(ql_dbg_async, vha, 0x504f,
+			    "Unrecognized interrupt type (%d).\n", stat * 0xff);
 			break;
 		}
 		WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -2205,7 +2178,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-		"%s(): NULL response queue pointer\n", __func__);
+		"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 	ha = rsp->hw;
@@ -2235,7 +2208,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-			"%s(): NULL response queue pointer\n", __func__);
+			"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 	ha = rsp->hw;
@@ -2268,8 +2241,8 @@
 
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
-		DEBUG(printk(
-		"%s(): NULL response queue pointer\n", __func__));
+		printk(KERN_INFO
+			"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 	ha = rsp->hw;
@@ -2286,8 +2259,9 @@
 
 			hccr = RD_REG_DWORD(&reg->hccr);
 
-			qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
-			    "Dumping firmware!\n", hccr);
+			ql_log(ql_log_info, vha, 0x5050,
+			    "RISC paused -- HCCR=%x, Dumping firmware.\n",
+			    hccr);
 
 			qla2xxx_check_risc_status(vha);
 
@@ -2318,9 +2292,8 @@
 			qla24xx_process_response_queue(vha, rsp);
 			break;
 		default:
-			DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
-			    "(%d).\n",
-			    vha->host_no, stat & 0xff));
+			ql_dbg(ql_dbg_async, vha, 0x5051,
+			    "Unrecognized interrupt type (%d).\n", stat & 0xff);
 			break;
 		}
 		WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -2358,6 +2331,7 @@
 {
 	int i;
 	struct qla_msix_entry *qentry;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	for (i = 0; i < ha->msix_count; i++) {
 		qentry = &ha->msix_entries[i];
@@ -2368,6 +2342,8 @@
 	kfree(ha->msix_entries);
 	ha->msix_entries = NULL;
 	ha->flags.msix_enabled = 0;
+	ql_dbg(ql_dbg_init, vha, 0x0042,
+	    "Disabled the MSI.\n");
 }
 
 static int
@@ -2377,11 +2353,15 @@
 	int i, ret;
 	struct msix_entry *entries;
 	struct qla_msix_entry *qentry;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
 			GFP_KERNEL);
-	if (!entries)
+	if (!entries) {
+		ql_log(ql_log_warn, vha, 0x00bc,
+		    "Failed to allocate memory for msix_entry.\n");
 		return -ENOMEM;
+	}
 
 	for (i = 0; i < ha->msix_count; i++)
 		entries[i].entry = i;
@@ -2391,16 +2371,18 @@
 		if (ret < MIN_MSIX_COUNT)
 			goto msix_failed;
 
-		qla_printk(KERN_WARNING, ha,
-			"MSI-X: Failed to enable support -- %d/%d\n"
-			" Retry with %d vectors\n", ha->msix_count, ret, ret);
+		ql_log(ql_log_warn, vha, 0x00c6,
+		    "MSI-X: Failed to enable support "
+		    "-- %d/%d\n Retry with %d vectors.\n",
+		    ha->msix_count, ret, ret);
 		ha->msix_count = ret;
 		ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
 		if (ret) {
 msix_failed:
-			qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable"
-				" support, giving up -- %d/%d\n",
-				ha->msix_count, ret);
+			ql_log(ql_log_fatal, vha, 0x00c7,
+			    "MSI-X: Failed to enable support, "
+			    "giving   up -- %d/%d.\n",
+			    ha->msix_count, ret);
 			goto msix_out;
 		}
 		ha->max_rsp_queues = ha->msix_count - 1;
@@ -2408,6 +2390,8 @@
 	ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
 				ha->msix_count, GFP_KERNEL);
 	if (!ha->msix_entries) {
+		ql_log(ql_log_fatal, vha, 0x00c8,
+		    "Failed to allocate memory for ha->msix_entries.\n");
 		ret = -ENOMEM;
 		goto msix_out;
 	}
@@ -2434,9 +2418,9 @@
 				0, msix_entries[i].name, rsp);
 		}
 		if (ret) {
-			qla_printk(KERN_WARNING, ha,
-			"MSI-X: Unable to register handler -- %x/%d.\n",
-			qentry->vector, ret);
+			ql_log(ql_log_fatal, vha, 0x00cb,
+			    "MSI-X: unable to register handler -- %x/%d.\n",
+			    qentry->vector, ret);
 			qla24xx_disable_msix(ha);
 			ha->mqenable = 0;
 			goto msix_out;
@@ -2449,6 +2433,12 @@
 	/* Enable MSI-X vector for response queue update for queue 0 */
 	if (ha->mqiobase &&  (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
 		ha->mqenable = 1;
+	ql_dbg(ql_dbg_multiq, vha, 0xc005,
+	    "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
+	    ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
+	ql_dbg(ql_dbg_init, vha, 0x0055,
+	    "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
+	    ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
 
 msix_out:
 	kfree(entries);
@@ -2460,6 +2450,7 @@
 {
 	int ret;
 	device_reg_t __iomem *reg = ha->iobase;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	/* If possible, enable MSI-X. */
 	if (!IS_QLA2432(ha) && !IS_QLA2532(ha) &&
@@ -2470,30 +2461,30 @@
 		(ha->pdev->subsystem_device == 0x7040 ||
 		ha->pdev->subsystem_device == 0x7041 ||
 		ha->pdev->subsystem_device == 0x1705)) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-			"MSI-X: Unsupported ISP2432 SSVID/SSDID (0x%X,0x%X).\n",
+		ql_log(ql_log_warn, vha, 0x0034,
+		    "MSI-X: Unsupported ISP 2432 SSVID/SSDID (0x%X,0x%X).\n",
 			ha->pdev->subsystem_vendor,
-			ha->pdev->subsystem_device));
+			ha->pdev->subsystem_device);
 		goto skip_msi;
 	}
 
 	if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX ||
 		!QLA_MSIX_FW_MODE_1(ha->fw_attributes))) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		"MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n",
-			ha->pdev->revision, ha->fw_attributes));
+		ql_log(ql_log_warn, vha, 0x0035,
+		    "MSI-X; Unsupported ISP2432 (0x%X, 0x%X).\n",
+		    ha->pdev->revision, ha->fw_attributes);
 		goto skip_msix;
 	}
 
 	ret = qla24xx_enable_msix(ha, rsp);
 	if (!ret) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision,
-		    ha->fw_attributes));
+		ql_dbg(ql_dbg_init, vha, 0x0036,
+		    "MSI-X: Enabled (0x%X, 0x%X).\n",
+		    ha->chip_revision, ha->fw_attributes);
 		goto clear_risc_ints;
 	}
-	qla_printk(KERN_WARNING, ha,
-	    "MSI-X: Falling back-to MSI mode -- %d.\n", ret);
+	ql_log(ql_log_info, vha, 0x0037,
+	    "MSI-X Falling back-to MSI mode -%d.\n", ret);
 skip_msix:
 
 	if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
@@ -2502,18 +2493,19 @@
 
 	ret = pci_enable_msi(ha->pdev);
 	if (!ret) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "MSI: Enabled.\n"));
+		ql_dbg(ql_dbg_init, vha, 0x0038,
+		    "MSI: Enabled.\n");
 		ha->flags.msi_enabled = 1;
 	} else
-		qla_printk(KERN_WARNING, ha,
-		    "MSI-X: Falling back-to INTa mode -- %d.\n", ret);
+		ql_log(ql_log_warn, vha, 0x0039,
+		    "MSI-X; Falling back-to INTa mode -- %d.\n", ret);
 skip_msi:
 
 	ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler,
 	    ha->flags.msi_enabled ? 0 : IRQF_SHARED,
 	    QLA2XXX_DRIVER_NAME, rsp);
 	if (ret) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x003a,
 		    "Failed to reserve interrupt %d already in use.\n",
 		    ha->pdev->irq);
 		goto fail;
@@ -2563,13 +2555,14 @@
 	struct qla_hw_data *ha = rsp->hw;
 	struct qla_init_msix_entry *intr = &msix_entries[2];
 	struct qla_msix_entry *msix = rsp->msix;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	int ret;
 
 	ret = request_irq(msix->vector, intr->handler, 0, intr->name, rsp);
 	if (ret) {
-		qla_printk(KERN_WARNING, ha,
-			"MSI-X: Unable to register handler -- %x/%d.\n",
-			msix->vector, ret);
+		ql_log(ql_log_fatal, vha, 0x00e6,
+		    "MSI-X: Unable to register handler -- %x/%d.\n",
+		    msix->vector, ret);
 		return ret;
 	}
 	msix->have_irq = 1;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index c26f0ac..f7604ea 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -46,14 +46,18 @@
 	struct qla_hw_data *ha = vha->hw;
 	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 
-	if (ha->pdev->error_state > pci_channel_io_frozen)
+	ql_dbg(ql_dbg_mbx, base_vha, 0x1000, "Entered %s.\n", __func__);
+
+	if (ha->pdev->error_state > pci_channel_io_frozen) {
+		ql_log(ql_log_warn, base_vha, 0x1001,
+		    "error_state is greater than pci_channel_io_frozen, "
+		    "exiting.\n");
 		return QLA_FUNCTION_TIMEOUT;
+	}
 
 	if (vha->device_flags & DFLG_DEV_FAILED) {
-		DEBUG2_3_11(qla_printk(KERN_WARNING, ha,
-			"%s(%ld): Device in failed state, "
-			"timeout MBX Exiting.\n",
-			__func__, base_vha->host_no));
+		ql_log(ql_log_warn, base_vha, 0x1002,
+		    "Device in failed state, exiting.\n");
 		return QLA_FUNCTION_TIMEOUT;
 	}
 
@@ -63,17 +67,18 @@
 	rval = QLA_SUCCESS;
 	abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, base_vha->host_no));
 
 	if (ha->flags.pci_channel_io_perm_failure) {
-		DEBUG(printk("%s(%ld): Perm failure on EEH, timeout MBX "
-			     "Exiting.\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, base_vha, 0x1003,
+		    "Perm failure on EEH timeout MBX, exiting.\n");
 		return QLA_FUNCTION_TIMEOUT;
 	}
 
 	if (ha->flags.isp82xx_fw_hung) {
 		/* Setting Link-Down error */
 		mcp->mb[0] = MBS_LINK_DOWN_ERROR;
+		ql_log(ql_log_warn, base_vha, 0x1004,
+		    "FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
 		rval = QLA_FUNCTION_FAILED;
 		goto premature_exit;
 	}
@@ -85,8 +90,8 @@
 	 */
 	if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) {
 		/* Timeout occurred. Return error. */
-		DEBUG2_3_11(printk("%s(%ld): cmd access timeout. "
-		    "Exiting.\n", __func__, base_vha->host_no));
+		ql_log(ql_log_warn, base_vha, 0x1005,
+		    "Cmd access timeout, Exiting.\n");
 		return QLA_FUNCTION_TIMEOUT;
 	}
 
@@ -94,8 +99,8 @@
 	/* Save mailbox command for debug */
 	ha->mcp = mcp;
 
-	DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n",
-	    base_vha->host_no, mcp->mb[0]));
+	ql_dbg(ql_dbg_mbx, base_vha, 0x1006,
+	    "Prepare to issue mbox cmd=0x%x.\n", mcp->mb[0]);
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -123,27 +128,30 @@
 		iptr++;
 	}
 
-#if defined(QL_DEBUG_LEVEL_1)
-	printk("%s(%ld): Loaded MBX registers (displayed in bytes) = \n",
-	    __func__, base_vha->host_no);
-	qla2x00_dump_buffer((uint8_t *)mcp->mb, 16);
-	printk("\n");
-	qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16);
-	printk("\n");
-	qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8);
-	printk("\n");
-	printk("%s(%ld): I/O address = %p.\n", __func__, base_vha->host_no,
-		optr);
-	qla2x00_dump_regs(base_vha);
-#endif
+	ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1111,
+	    "Loaded MBX registers (displayed in bytes) =.\n");
+	ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1112,
+	    (uint8_t *)mcp->mb, 16);
+	ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1113,
+	    ".\n");
+	ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1114,
+	    ((uint8_t *)mcp->mb + 0x10), 16);
+	ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1115,
+	    ".\n");
+	ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1116,
+	    ((uint8_t *)mcp->mb + 0x20), 8);
+	ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1117,
+	    "I/O Address = %p.\n", optr);
+	ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x100e);
 
 	/* Issue set host interrupt command to send cmd out. */
 	ha->flags.mbox_int = 0;
 	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
 	/* Unlock mbx registers and wait for interrupt */
-	DEBUG11(printk("%s(%ld): going to unlock irq & waiting for interrupt. "
-	    "jiffies=%lx.\n", __func__, base_vha->host_no, jiffies));
+	ql_dbg(ql_dbg_mbx, base_vha, 0x100f,
+	    "Going to unlock irq & waiting for interrupts. "
+	    "jiffies=%lx.\n", jiffies);
 
 	/* Wait for mbx cmd completion until timeout */
 
@@ -155,9 +163,8 @@
 				HINT_MBX_INT_PENDING) {
 				spin_unlock_irqrestore(&ha->hardware_lock,
 					flags);
-				DEBUG2_3_11(printk(KERN_INFO
-				    "%s(%ld): Pending Mailbox timeout. "
-				    "Exiting.\n", __func__, base_vha->host_no));
+				ql_dbg(ql_dbg_mbx, base_vha, 0x1010,
+				    "Pending mailbox timeout, exiting.\n");
 				rval = QLA_FUNCTION_TIMEOUT;
 				goto premature_exit;
 			}
@@ -173,17 +180,16 @@
 		clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
 
 	} else {
-		DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__,
-		    base_vha->host_no, command));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x1011,
+		    "Cmd=%x Polling Mode.\n", command);
 
 		if (IS_QLA82XX(ha)) {
 			if (RD_REG_DWORD(&reg->isp82.hint) &
 				HINT_MBX_INT_PENDING) {
 				spin_unlock_irqrestore(&ha->hardware_lock,
 					flags);
-				DEBUG2_3_11(printk(KERN_INFO
-				    "%s(%ld): Pending Mailbox timeout. "
-				    "Exiting.\n", __func__, base_vha->host_no));
+				ql_dbg(ql_dbg_mbx, base_vha, 0x1012,
+				    "Pending mailbox timeout, exiting.\n");
 				rval = QLA_FUNCTION_TIMEOUT;
 				goto premature_exit;
 			}
@@ -207,17 +213,17 @@
 			    command == MBC_LOAD_RISC_RAM_EXTENDED))
 				msleep(10);
 		} /* while */
-		DEBUG17(qla_printk(KERN_WARNING, ha,
-			"Waited %d sec\n",
-			(uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ)));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x1013,
+		    "Waited %d sec.\n",
+		    (uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ));
 	}
 
 	/* Check whether we timed out */
 	if (ha->flags.mbox_int) {
 		uint16_t *iptr2;
 
-		DEBUG3_11(printk("%s(%ld): cmd %x completed.\n", __func__,
-		    base_vha->host_no, command));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x1014,
+		    "Cmd=%x completed.\n", command);
 
 		/* Got interrupt. Clear the flag. */
 		ha->flags.mbox_int = 0;
@@ -229,6 +235,8 @@
 			mcp->mb[0] = MBS_LINK_DOWN_ERROR;
 			ha->mcp = NULL;
 			rval = QLA_FUNCTION_FAILED;
+			ql_log(ql_log_warn, base_vha, 0x1015,
+			    "FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
 			goto premature_exit;
 		}
 
@@ -249,8 +257,6 @@
 		}
 	} else {
 
-#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) || \
-		defined(QL_DEBUG_LEVEL_11)
 		uint16_t mb0;
 		uint32_t ictrl;
 
@@ -261,14 +267,13 @@
 			mb0 = RD_MAILBOX_REG(ha, &reg->isp, 0);
 			ictrl = RD_REG_WORD(&reg->isp.ictrl);
 		}
-		printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n",
-		    __func__, base_vha->host_no, command);
-		printk("%s(%ld): icontrol=%x jiffies=%lx\n", __func__,
-		    base_vha->host_no, ictrl, jiffies);
-		printk("%s(%ld): *** mailbox[0] = 0x%x ***\n", __func__,
-		    base_vha->host_no, mb0);
-		qla2x00_dump_regs(base_vha);
-#endif
+		ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1119,
+		    "MBX Command timeout for cmd %x.\n", command);
+		ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x111a,
+		    "iocontrol=%x jiffies=%lx.\n", ictrl, jiffies);
+		ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x111b,
+		    "mb[0] = 0x%x.\n", mb0);
+		ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1019);
 
 		rval = QLA_FUNCTION_TIMEOUT;
 	}
@@ -279,8 +284,8 @@
 	ha->mcp = NULL;
 
 	if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) {
-		DEBUG11(printk("%s(%ld): checking for additional resp "
-		    "interrupt.\n", __func__, base_vha->host_no));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x101a,
+		    "Checking for additional resp interrupt.\n");
 
 		/* polling mode for non isp_abort commands. */
 		qla2x00_poll(ha->rsp_q_map[0]);
@@ -291,38 +296,32 @@
 		if (!io_lock_on || (mcp->flags & IOCTL_CMD) ||
 		    ha->flags.eeh_busy) {
 			/* not in dpc. schedule it for dpc to take over. */
-			DEBUG(printk("%s(%ld): timeout schedule "
-			"isp_abort_needed.\n", __func__,
-			base_vha->host_no));
-			DEBUG2_3_11(printk("%s(%ld): timeout schedule "
-			"isp_abort_needed.\n", __func__,
-			base_vha->host_no));
+			ql_dbg(ql_dbg_mbx, base_vha, 0x101b,
+			    "Timeout, schedule isp_abort_needed.\n");
 
 			if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
 			    !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
 			    !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
 
-				qla_printk(KERN_WARNING, ha,
-				    "Mailbox command timeout occurred. "
-				    "Scheduling ISP " "abort. eeh_busy: 0x%x\n",
-				    ha->flags.eeh_busy);
+				ql_log(ql_log_info, base_vha, 0x101c,
+				    "Mailbox cmd timeout occured. "
+				    "Scheduling ISP abort eeh_busy=0x%x.\n",
+					ha->flags.eeh_busy);
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 				qla2xxx_wake_dpc(vha);
 			}
 		} else if (!abort_active) {
 			/* call abort directly since we are in the DPC thread */
-			DEBUG(printk("%s(%ld): timeout calling abort_isp\n",
-			    __func__, base_vha->host_no));
-			DEBUG2_3_11(printk("%s(%ld): timeout calling "
-			    "abort_isp\n", __func__, base_vha->host_no));
+			ql_dbg(ql_dbg_mbx, base_vha, 0x101d,
+			    "Timeout, calling abort_isp.\n");
 
 			if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
 			    !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
 			    !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
 
-				qla_printk(KERN_WARNING, ha,
-				    "Mailbox command timeout occurred. "
-				    "Issuing ISP abort.\n");
+				ql_log(ql_log_info, base_vha, 0x101e,
+				    "Mailbox cmd timeout occured. "
+				    "Scheduling ISP abort.\n");
 
 				set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
 				clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -332,11 +331,8 @@
 					    &vha->dpc_flags);
 				}
 				clear_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
-				DEBUG(printk("%s(%ld): finished abort_isp\n",
-				    __func__, vha->host_no));
-				DEBUG2_3_11(printk(
-				    "%s(%ld): finished abort_isp\n",
-				    __func__, vha->host_no));
+				ql_dbg(ql_dbg_mbx, base_vha, 0x101f,
+				    "Finished abort_isp.\n");
 			}
 		}
 	}
@@ -346,12 +342,11 @@
 	complete(&ha->mbx_cmd_comp);
 
 	if (rval) {
-		DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, "
-		    "mbx2=%x, cmd=%x ****\n", __func__, base_vha->host_no,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[2], command));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x1020,
+		    "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, cmd=%x ****.\n",
+		    mcp->mb[0], mcp->mb[1], mcp->mb[2], command);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__,
-		base_vha->host_no));
+		ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -366,7 +361,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1022, "Entered %s.\n", __func__);
 
 	if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
 		mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
@@ -397,10 +392,10 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1023,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1024, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -430,7 +425,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1025, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
 	mcp->out_mb = MBX_0;
@@ -461,15 +456,14 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1026,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
 		if (IS_FWI2_CAPABLE(ha)) {
-			DEBUG11(printk("%s(%ld): done exchanges=%x.\n",
-			    __func__, vha->host_no, mcp->mb[1]));
+			ql_dbg(ql_dbg_mbx, vha, 0x1027,
+			    "Done exchanges=%x.\n", mcp->mb[1]);
 		} else {
-			DEBUG11(printk("%s(%ld): done.\n", __func__,
-			    vha->host_no));
+			ql_dbg(ql_dbg_mbx, vha, 0x1028, "Done %s.\n", __func__);
 		}
 	}
 
@@ -501,7 +495,7 @@
 	mbx_cmd_t	mc;
 	mbx_cmd_t	*mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1029, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
 	mcp->out_mb = MBX_0;
@@ -535,11 +529,10 @@
 failed:
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x102b, "Done %s.\n", __func__);
 	}
 	return rval;
 }
@@ -565,7 +558,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x102c, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
 	mcp->out_mb = MBX_0;
@@ -576,15 +569,14 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x102d, "Failed=%x.\n", rval);
 	} else {
 		fwopts[0] = mcp->mb[0];
 		fwopts[1] = mcp->mb[1];
 		fwopts[2] = mcp->mb[2];
 		fwopts[3] = mcp->mb[3];
 
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x102e, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -612,7 +604,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x102f, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
 	mcp->mb[1] = fwopts[1];
@@ -636,11 +628,11 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed=%x (%x/%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1030,
+		    "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1031, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -668,7 +660,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1032, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
 	mcp->mb[1] = 0xAAAA;
@@ -695,12 +687,10 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1034, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -728,7 +718,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1035, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_VERIFY_CHECKSUM;
 	mcp->out_mb = MBX_0;
@@ -749,11 +739,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__,
-		    vha->host_no, rval, IS_FWI2_CAPABLE(vha->hw) ?
-		    (mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1036,
+		    "Failed=%x chm sum=%x.\n", rval, IS_FWI2_CAPABLE(vha->hw) ?
+		    (mcp->mb[2] << 16) | mcp->mb[1] : mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1037, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -785,6 +775,8 @@
 	mbx_cmd_t	mc;
 	mbx_cmd_t	*mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x1038, "Entered %s.\n", __func__);
+
 	mcp->mb[0] = MBC_IOCB_COMMAND_A64;
 	mcp->mb[1] = 0;
 	mcp->mb[2] = MSW(phys_addr);
@@ -799,14 +791,14 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1039, "Failed=%x.\n", rval);
 	} else {
 		sts_entry_t *sts_entry = (sts_entry_t *) buffer;
 
 		/* Mask reserved bits. */
 		sts_entry->entry_status &=
 		    IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
+		ql_dbg(ql_dbg_mbx, vha, 0x103a, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -847,7 +839,7 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = vha->req;
 
-	DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x103b, "Entered %s.\n", __func__);
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -876,11 +868,9 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("qla2x00_abort_command(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x103d, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -896,10 +886,11 @@
 	struct req_que *req;
 	struct rsp_que *rsp;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
-
 	l = l;
 	vha = fcport->vha;
+
+	ql_dbg(ql_dbg_mbx, vha, 0x103e, "Entered %s.\n", __func__);
+
 	req = vha->hw->req_q_map[0];
 	rsp = req->rsp;
 	mcp->mb[0] = MBC_ABORT_TARGET;
@@ -919,18 +910,17 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x103f, "Failed=%x.\n", rval);
 	}
 
 	/* Issue marker IOCB. */
 	rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0,
 							MK_SYNC_ID);
 	if (rval2 != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
-		    "(%x).\n", __func__, vha->host_no, rval2));
+		ql_dbg(ql_dbg_mbx, vha, 0x1040,
+		    "Failed to issue marker IOCB (%x).\n", rval2);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1041, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -946,9 +936,10 @@
 	struct req_que *req;
 	struct rsp_que *rsp;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
-
 	vha = fcport->vha;
+
+	ql_dbg(ql_dbg_mbx, vha, 0x1042, "Entered %s.\n", __func__);
+
 	req = vha->hw->req_q_map[0];
 	rsp = req->rsp;
 	mcp->mb[0] = MBC_LUN_RESET;
@@ -966,18 +957,17 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1043, "Failed=%x.\n", rval);
 	}
 
 	/* Issue marker IOCB. */
 	rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
 								MK_SYNC_ID_LUN);
 	if (rval2 != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
-		    "(%x).\n", __func__, vha->host_no, rval2));
+		ql_dbg(ql_dbg_mbx, vha, 0x1044,
+		    "Failed to issue marker IOCB (%x).\n", rval2);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1045, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1011,8 +1001,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1046, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
 	mcp->mb[9] = vha->vp_idx;
@@ -1038,11 +1027,9 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1048, "Done %s.\n", __func__);
 
 		if (IS_QLA8XXX_TYPE(vha->hw)) {
 			vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
@@ -1083,8 +1070,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n",
-			vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1049, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_RETRY_COUNT;
 	mcp->out_mb = MBX_0;
@@ -1095,8 +1081,8 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n",
-		    vha->host_no, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x104a,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
 		/* Convert returned data and check our values. */
 		*r_a_tov = mcp->mb[3] / 2;
@@ -1107,8 +1093,8 @@
 			*tov = ratov;
 		}
 
-		DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d "
-		    "ratov=%d.\n", vha->host_no, mcp->mb[3], ratov));
+		ql_dbg(ql_dbg_mbx, vha, 0x104b,
+		    "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov);
 	}
 
 	return rval;
@@ -1139,8 +1125,7 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x104c, "Entered %s.\n", __func__);
 
 	if (IS_QLA82XX(ha) && ql2xdbwr)
 		qla82xx_wr_32(ha, ha->nxdb_wr_ptr,
@@ -1174,13 +1159,11 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x "
-		    "mb0=%x.\n",
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x104d,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x104e, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1213,13 +1196,13 @@
 	dma_addr_t pd_dma;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x104f, "Entered %s.\n", __func__);
 
 	pd24 = NULL;
 	pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
 	if (pd  == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Port Database "
-		    "structure.\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x1050,
+		    "Failed to allocate port database structure.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE));
@@ -1261,12 +1244,10 @@
 		/* Check for logged in state. */
 		if (pd24->current_login_state != PDS_PRLI_COMPLETE &&
 		    pd24->last_login_state != PDS_PRLI_COMPLETE) {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			   "scsi(%ld): Unable to verify login-state (%x/%x) "
-			   " - portid=%02x%02x%02x.\n", vha->host_no,
-			   pd24->current_login_state, pd24->last_login_state,
-			   fcport->d_id.b.domain, fcport->d_id.b.area,
-			   fcport->d_id.b.al_pa));
+			ql_dbg(ql_dbg_mbx, vha, 0x1051,
+			    "Unable to verify login-state (%x/%x) for "
+			    "loop_id %x.\n", pd24->current_login_state,
+			    pd24->last_login_state, fcport->loop_id);
 			rval = QLA_FUNCTION_FAILED;
 			goto gpd_error_out;
 		}
@@ -1290,12 +1271,11 @@
 		/* Check for logged in state. */
 		if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
 		    pd->slave_state != PD_STATE_PORT_LOGGED_IN) {
-			DEBUG2(qla_printk(KERN_WARNING, ha,
-			   "scsi(%ld): Unable to verify login-state (%x/%x) "
-			   " - portid=%02x%02x%02x.\n", vha->host_no,
-			   pd->master_state, pd->slave_state,
-			   fcport->d_id.b.domain, fcport->d_id.b.area,
-			   fcport->d_id.b.al_pa));
+			ql_dbg(ql_dbg_mbx, vha, 0x100a,
+			    "Unable to verify login-state (%x/%x) - "
+			    "portid=%02x%02x%02x.\n", pd->master_state,
+			    pd->slave_state, fcport->d_id.b.domain,
+			    fcport->d_id.b.area, fcport->d_id.b.al_pa);
 			rval = QLA_FUNCTION_FAILED;
 			goto gpd_error_out;
 		}
@@ -1325,10 +1305,11 @@
 	dma_pool_free(ha->s_dma_pool, pd, pd_dma);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1052,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n", rval,
+		    mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1053, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1357,8 +1338,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1054, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
 	mcp->out_mb = MBX_0;
@@ -1381,12 +1361,10 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): "
-		    "failed=%x.\n", vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1056, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1418,8 +1396,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1057, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_PORT_NAME;
 	mcp->mb[9] = vha->vp_idx;
@@ -1439,8 +1416,7 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1058, "Failed=%x.\n", rval);
 	} else {
 		if (name != NULL) {
 			/* This function returns name in big endian. */
@@ -1454,8 +1430,7 @@
 			name[7] = LSB(mcp->mb[7]);
 		}
 
-		DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1059, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1483,7 +1458,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x105a, "Entered %s.\n", __func__);
 
 	if (IS_QLA8XXX_TYPE(vha->hw)) {
 		/* Logout across all FCFs. */
@@ -1517,11 +1492,10 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n",
-		    __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x105c, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1553,12 +1527,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x105d, "Entered %s.\n", __func__);
 
-	DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total "
-		"tov=%d.\n", vha->hw->retry_count, vha->hw->login_timeout,
-		mcp->tov));
+	ql_dbg(ql_dbg_mbx, vha, 0x105e,
+	    "Retry cnt=%d ratov=%d total tov=%d.\n",
+	    vha->hw->retry_count, vha->hw->login_timeout, mcp->tov);
 
 	mcp->mb[0] = MBC_SEND_SNS_COMMAND;
 	mcp->mb[1] = cmd_size;
@@ -1575,13 +1548,12 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
-		    "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
-		DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
-		    "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x105f,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1060, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1600,7 +1572,7 @@
 	struct req_que *req;
 	struct rsp_que *rsp;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1061, "Entered %s.\n", __func__);
 
 	if (ha->flags.cpu_affinity_enabled)
 		req = ha->req_q_map[0];
@@ -1610,8 +1582,8 @@
 
 	lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
 	if (lg == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x1062,
+		    "Failed to allocate login IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1631,21 +1603,21 @@
 	lg->vp_index = vha->vp_idx;
 	rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB "
-		    "(%x).\n", __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1063,
+		    "Failed to issue login IOCB (%x).\n", rval);
 	} else if (lg->entry_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, vha->host_no,
-		    lg->entry_status));
+		ql_dbg(ql_dbg_mbx, vha, 0x1064,
+		    "Failed to complete IOCB -- error status (%x).\n",
+		    lg->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
 		iop[0] = le32_to_cpu(lg->io_parameter[0]);
 		iop[1] = le32_to_cpu(lg->io_parameter[1]);
 
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x)  ioparam=%x/%x.\n", __func__,
-		    vha->host_no, le16_to_cpu(lg->comp_status), iop[0],
-		    iop[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1065,
+		    "Failed to complete IOCB -- completion  status (%x) "
+		    "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status),
+		    iop[0], iop[1]);
 
 		switch (iop[0]) {
 		case LSC_SCODE_PORTID_USED:
@@ -1673,7 +1645,7 @@
 			break;
 		}
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1066, "Done %s.\n", __func__);
 
 		iop[0] = le32_to_cpu(lg->io_parameter[0]);
 
@@ -1728,7 +1700,7 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1067, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
 	mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -1771,13 +1743,12 @@
 			rval = QLA_SUCCESS;
 
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x "
-		    "mb[0]=%x mb[1]=%x mb[2]=%x.\n", vha->host_no, rval,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1068,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1069, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1808,13 +1779,13 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x106a, "Entered %s.\n", __func__);
+
 	if (IS_FWI2_CAPABLE(ha))
 		return qla24xx_login_fabric(vha, fcport->loop_id,
 		    fcport->d_id.b.domain, fcport->d_id.b.area,
 		    fcport->d_id.b.al_pa, mb_ret, opt);
 
-	DEBUG3(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
 	if (HAS_EXTENDED_IDS(ha))
 		mcp->mb[1] = fcport->loop_id;
@@ -1845,15 +1816,12 @@
  		if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006)
  			rval = QLA_SUCCESS;
 
-		DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
-		    "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
-		DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
-		    "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
+		ql_dbg(ql_dbg_mbx, vha, 0x106b,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[6]=%x mb[7]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);
 	} else {
 		/*EMPTY*/
-		DEBUG3(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x106c, "Done %s.\n", __func__);
 	}
 
 	return (rval);
@@ -1870,12 +1838,12 @@
 	struct req_que *req;
 	struct rsp_que *rsp;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x106d, "Entered %s.\n", __func__);
 
 	lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
 	if (lg == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Logout IOCB.\n",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x106e,
+		    "Failed to allocate logout IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1899,22 +1867,22 @@
 
 	rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB "
-		    "(%x).\n", __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x106f,
+		    "Failed to issue logout IOCB (%x).\n", rval);
 	} else if (lg->entry_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, vha->host_no,
-		    lg->entry_status));
+		ql_dbg(ql_dbg_mbx, vha, 0x1070,
+		    "Failed to complete IOCB -- error status (%x).\n",
+		    lg->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
-		DEBUG2_3_11(printk("%s(%ld %d): failed to complete IOCB "
-		    "-- completion status (%x)  ioparam=%x/%x.\n", __func__,
-		    vha->host_no, vha->vp_idx, le16_to_cpu(lg->comp_status),
+		ql_dbg(ql_dbg_mbx, vha, 0x1071,
+		    "Failed to complete IOCB -- completion status (%x) "
+		    "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status),
 		    le32_to_cpu(lg->io_parameter[0]),
-		    le32_to_cpu(lg->io_parameter[1])));
+		    le32_to_cpu(lg->io_parameter[1]));
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1072, "Done %s.\n", __func__);
 	}
 
 	dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1946,8 +1914,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1073, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
 	mcp->out_mb = MBX_1|MBX_0;
@@ -1966,12 +1933,11 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x "
-		    "mbx1=%x.\n", vha->host_no, rval, mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1074,
+		    "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1075, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -1999,8 +1965,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1076, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_LIP_FULL_LOGIN;
 	mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
@@ -2014,12 +1979,10 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1078, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2045,8 +2008,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n",
-	    vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1079, "Entered %s.\n", __func__);
 
 	if (id_list == NULL)
 		return QLA_FUNCTION_FAILED;
@@ -2075,12 +2037,10 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n",
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval);
 	} else {
 		*entries = mcp->mb[1];
-		DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n",
-		    vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x107b, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2108,7 +2068,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x107c, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
 	mcp->out_mb = MBX_0;
@@ -2121,14 +2081,14 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__,
-		    vha->host_no, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x107d,
+		    "Failed mb[0]=%x.\n", mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
-		    "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__,
-		    vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3],
-		    mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11],
-		    mcp->mb[12]));
+		ql_dbg(ql_dbg_mbx, vha, 0x107e,
+		    "Done %s mb1=%x mb2=%x mb3=%x mb6=%x mb7=%x mb10=%x "
+		    "mb11=%x mb12=%x.\n", __func__, mcp->mb[1], mcp->mb[2],
+		    mcp->mb[3], mcp->mb[6], mcp->mb[7], mcp->mb[10],
+		    mcp->mb[11], mcp->mb[12]);
 
 		if (cur_xchg_cnt)
 			*cur_xchg_cnt = mcp->mb[3];
@@ -2147,7 +2107,6 @@
 	return (rval);
 }
 
-#if defined(QL_DEBUG_LEVEL_3)
 /*
  * qla2x00_get_fcal_position_map
  *	Get FCAL (LILP) position map using mailbox command
@@ -2172,10 +2131,12 @@
 	dma_addr_t pmap_dma;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x107f, "Entered %s.\n", __func__);
+
 	pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
 	if (pmap  == NULL) {
-		DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x1080,
+		    "Memory alloc failed.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(pmap, 0, FCAL_MAP_SIZE);
@@ -2193,10 +2154,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval == QLA_SUCCESS) {
-		DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map "
-		    "size (%x)\n", __func__, vha->host_no, mcp->mb[0],
-		    mcp->mb[1], (unsigned)pmap[0]));
-		DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1));
+		ql_dbg(ql_dbg_mbx, vha, 0x1081,
+		    "mb0/mb1=%x/%X FC/AL position map size (%x).\n",
+		    mcp->mb[0], mcp->mb[1], (unsigned)pmap[0]);
+		ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111d,
+		    pmap, pmap[0] + 1);
 
 		if (pos_map)
 			memcpy(pos_map, pmap, FCAL_MAP_SIZE);
@@ -2204,15 +2166,13 @@
 	dma_pool_free(ha->s_dma_pool, pmap, pmap_dma);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1083, "Done %s.\n", __func__);
 	}
 
 	return rval;
 }
-#endif
 
 /*
  * qla2x00_get_link_status
@@ -2237,7 +2197,7 @@
 	uint32_t *siter, *diter, dwords;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1084, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_LINK_STATUS;
 	mcp->mb[2] = MSW(stats_dma);
@@ -2266,11 +2226,12 @@
 
 	if (rval == QLA_SUCCESS) {
 		if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
-			DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
-			    __func__, vha->host_no, mcp->mb[0]));
+			ql_dbg(ql_dbg_mbx, vha, 0x1085,
+			    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 			rval = QLA_FUNCTION_FAILED;
 		} else {
 			/* Copy over data -- firmware data is LE. */
+			ql_dbg(ql_dbg_mbx, vha, 0x1086, "Done %s.\n", __func__);
 			dwords = offsetof(struct link_statistics, unused1) / 4;
 			siter = diter = &stats->link_fail_cnt;
 			while (dwords--)
@@ -2278,8 +2239,7 @@
 		}
 	} else {
 		/* Failed. */
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1087, "Failed=%x.\n", rval);
 	}
 
 	return rval;
@@ -2294,7 +2254,7 @@
 	mbx_cmd_t *mcp = &mc;
 	uint32_t *siter, *diter, dwords;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1088, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
 	mcp->mb[2] = MSW(stats_dma);
@@ -2312,10 +2272,11 @@
 
 	if (rval == QLA_SUCCESS) {
 		if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
-			DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
-			    __func__, vha->host_no, mcp->mb[0]));
+			ql_dbg(ql_dbg_mbx, vha, 0x1089,
+			    "Failed mb[0]=%x.\n", mcp->mb[0]);
 			rval = QLA_FUNCTION_FAILED;
 		} else {
+			ql_dbg(ql_dbg_mbx, vha, 0x108a, "Done %s.\n", __func__);
 			/* Copy over data -- firmware data is LE. */
 			dwords = sizeof(struct link_statistics) / 4;
 			siter = diter = &stats->link_fail_cnt;
@@ -2324,8 +2285,7 @@
 		}
 	} else {
 		/* Failed. */
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x108b, "Failed=%x.\n", rval);
 	}
 
 	return rval;
@@ -2345,7 +2305,7 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = vha->req;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x108c, "Entered %s.\n", __func__);
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -2360,8 +2320,8 @@
 
 	abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
 	if (abt == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Abort IOCB.\n",
-		    __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x108d,
+		    "Failed to allocate abort IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(abt, 0, sizeof(struct abort_entry_24xx));
@@ -2380,20 +2340,20 @@
 
 	rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n",
-		    __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x108e,
+		    "Failed to issue IOCB (%x).\n", rval);
 	} else if (abt->entry_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, vha->host_no,
-		    abt->entry_status));
+		ql_dbg(ql_dbg_mbx, vha, 0x108f,
+		    "Failed to complete IOCB -- error status (%x).\n",
+		    abt->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (abt->nport_handle != __constant_cpu_to_le16(0)) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x).\n", __func__, vha->host_no,
-		    le16_to_cpu(abt->nport_handle)));
+		ql_dbg(ql_dbg_mbx, vha, 0x1090,
+		    "Failed to complete IOCB -- completion status (%x).\n",
+		    le16_to_cpu(abt->nport_handle));
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1091, "Done %s.\n", __func__);
 	}
 
 	dma_pool_free(ha->s_dma_pool, abt, abt_dma);
@@ -2421,19 +2381,20 @@
 	struct req_que *req;
 	struct rsp_que *rsp;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
-
 	vha = fcport->vha;
 	ha = vha->hw;
 	req = vha->req;
+
+	ql_dbg(ql_dbg_mbx, vha, 0x1092, "Entered %s.\n", __func__);
+
 	if (ha->flags.cpu_affinity_enabled)
 		rsp = ha->rsp_q_map[tag + 1];
 	else
 		rsp = req->rsp;
 	tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
 	if (tsk == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
-		    "IOCB.\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x1093,
+		    "Failed to allocate task management IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(tsk, 0, sizeof(struct tsk_mgmt_cmd));
@@ -2457,30 +2418,30 @@
 	sts = &tsk->p.sts;
 	rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
-		    "(%x).\n", __func__, vha->host_no, name, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1094,
+		    "Failed to issue %s reset IOCB (%x).\n", name, rval);
 	} else if (sts->entry_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, vha->host_no,
-		    sts->entry_status));
+		ql_dbg(ql_dbg_mbx, vha, 0x1095,
+		    "Failed to complete IOCB -- error status (%x).\n",
+		    sts->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (sts->comp_status !=
 	    __constant_cpu_to_le16(CS_COMPLETE)) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x).\n", __func__,
-		    vha->host_no, le16_to_cpu(sts->comp_status)));
+		ql_dbg(ql_dbg_mbx, vha, 0x1096,
+		    "Failed to complete IOCB -- completion status (%x).\n",
+		    le16_to_cpu(sts->comp_status));
 		rval = QLA_FUNCTION_FAILED;
 	} else if (le16_to_cpu(sts->scsi_status) &
 	    SS_RESPONSE_INFO_LEN_VALID) {
 		if (le32_to_cpu(sts->rsp_data_len) < 4) {
-			DEBUG2_3_11(printk("%s(%ld): ignoring inconsistent "
-			    "data length -- not enough response info (%d).\n",
-			    __func__, vha->host_no,
-			    le32_to_cpu(sts->rsp_data_len)));
+			ql_dbg(ql_dbg_mbx, vha, 0x1097,
+			    "Ignoring inconsistent data length -- not enough "
+			    "response info (%d).\n",
+			    le32_to_cpu(sts->rsp_data_len));
 		} else if (sts->data[3]) {
-			DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-			    "-- response (%x).\n", __func__,
-			    vha->host_no, sts->data[3]));
+			ql_dbg(ql_dbg_mbx, vha, 0x1098,
+			    "Failed to complete IOCB -- response (%x).\n",
+			    sts->data[3]);
 			rval = QLA_FUNCTION_FAILED;
 		}
 	}
@@ -2489,10 +2450,10 @@
 	rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
 	    type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID);
 	if (rval2 != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
-		    "(%x).\n", __func__, vha->host_no, rval2));
+		ql_dbg(ql_dbg_mbx, vha, 0x1099,
+		    "Failed to issue marker IOCB (%x).\n", rval2);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x109a, "Done %s.\n", __func__);
 	}
 
 	dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
@@ -2533,7 +2494,7 @@
 	if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x109b, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
 	mcp->out_mb = MBX_0;
@@ -2543,10 +2504,9 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x109d, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2566,7 +2526,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x109e, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_SERDES_PARAMS;
 	mcp->mb[1] = BIT_0;
@@ -2581,11 +2541,11 @@
 
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
-		DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x109f,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
 		/*EMPTY*/
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a0, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2601,7 +2561,7 @@
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10a1, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_STOP_FIRMWARE;
 	mcp->out_mb = MBX_0;
@@ -2611,12 +2571,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a2, "Failed=%x.\n", rval);
 		if (mcp->mb[0] == MBS_INVALID_COMMAND)
 			rval = QLA_INVALID_COMMAND;
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a3, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2630,14 +2589,14 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10a4, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_TRACE_CONTROL;
 	mcp->mb[1] = TC_EFT_ENABLE;
 	mcp->mb[2] = LSW(eft_dma);
@@ -2652,10 +2611,11 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a5,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a6, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2668,14 +2628,14 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10a7, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_TRACE_CONTROL;
 	mcp->mb[1] = TC_EFT_DISABLE;
 	mcp->out_mb = MBX_1|MBX_0;
@@ -2684,10 +2644,11 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a8,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10a9, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2701,14 +2662,14 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10aa, "Entered %s.\n", __func__);
+
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_TRACE_CONTROL;
 	mcp->mb[1] = TC_FCE_ENABLE;
 	mcp->mb[2] = LSW(fce_dma);
@@ -2727,10 +2688,11 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ab,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ac, "Done %s.\n", __func__);
 
 		if (mb)
 			memcpy(mb, mcp->mb, 8 * sizeof(*mb));
@@ -2748,14 +2710,14 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10ad, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_TRACE_CONTROL;
 	mcp->mb[1] = TC_FCE_DISABLE;
 	mcp->mb[2] = TC_FCE_DISABLE_TRACE;
@@ -2766,10 +2728,11 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ae,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10af, "Done %s.\n", __func__);
 
 		if (wr)
 			*wr = (uint64_t) mcp->mb[5] << 48 |
@@ -2794,11 +2757,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10b0, "Entered %s.\n", __func__);
+
 	if (!IS_IIDMA_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_PORT_PARAMS;
 	mcp->mb[1] = loop_id;
 	mcp->mb[2] = mcp->mb[3] = 0;
@@ -2817,10 +2780,9 @@
 	}
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b2, "Done %s.\n", __func__);
 		if (port_speed)
 			*port_speed = mcp->mb[3];
 	}
@@ -2836,11 +2798,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10b3, "Entered %s.\n", __func__);
+
 	if (!IS_IIDMA_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_PORT_PARAMS;
 	mcp->mb[1] = loop_id;
 	mcp->mb[2] = BIT_0;
@@ -2863,10 +2825,9 @@
 	}
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b4, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b5, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -2882,33 +2843,36 @@
 	scsi_qla_host_t *vp;
 	unsigned long   flags;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10b6, "Entered %s.\n", __func__);
+
 	if (rptid_entry->entry_status != 0)
 		return;
 
 	if (rptid_entry->format == 0) {
-		DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d,"
-			" number of VPs acquired %d\n", __func__, vha->host_no,
-			MSB(le16_to_cpu(rptid_entry->vp_count)),
-			LSB(le16_to_cpu(rptid_entry->vp_count))));
-		DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__,
-			rptid_entry->port_id[2], rptid_entry->port_id[1],
-			rptid_entry->port_id[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b7,
+		    "Format 0 : Number of VPs setup %d, number of "
+		    "VPs acquired %d.\n",
+		    MSB(le16_to_cpu(rptid_entry->vp_count)),
+		    LSB(le16_to_cpu(rptid_entry->vp_count)));
+		ql_dbg(ql_dbg_mbx, vha, 0x10b8,
+		    "Primary port id %02x%02x%02x.\n",
+		    rptid_entry->port_id[2], rptid_entry->port_id[1],
+		    rptid_entry->port_id[0]);
 	} else if (rptid_entry->format == 1) {
 		vp_idx = LSB(stat);
-		DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled "
-		    "- status %d - "
-		    "with port id %02x%02x%02x\n", __func__, vha->host_no,
-		    vp_idx, MSB(stat),
+		ql_dbg(ql_dbg_mbx, vha, 0x10b9,
+		    "Format 1: VP[%d] enabled - status %d - with "
+		    "port id %02x%02x%02x.\n", vp_idx, MSB(stat),
 		    rptid_entry->port_id[2], rptid_entry->port_id[1],
-		    rptid_entry->port_id[0]));
+		    rptid_entry->port_id[0]);
 
 		vp = vha;
 		if (vp_idx == 0 && (MSB(stat) != 1))
 			goto reg_needed;
 
 		if (MSB(stat) == 1) {
-			DEBUG2(printk("scsi(%ld): Could not acquire ID for "
-			    "VP[%d].\n", vha->host_no, vp_idx));
+			ql_dbg(ql_dbg_mbx, vha, 0x10ba,
+			    "Could not acquire ID for VP[%d].\n", vp_idx);
 			return;
 		}
 
@@ -2963,10 +2927,12 @@
 
 	/* This can be called by the parent */
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10bb, "Entered %s.\n", __func__);
+
 	vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
 	if (!vpmod) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP "
-		    "IOCB.\n", __func__, vha->host_no));
+		ql_log(ql_log_warn, vha, 0x10bc,
+		    "Failed to allocate modify VP IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
@@ -2983,22 +2949,21 @@
 
 	rval = qla2x00_issue_iocb(base_vha, vpmod, vpmod_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB"
-			"(%x).\n", __func__, base_vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
+		    "Failed to issue VP config IOCB (%x).\n", rval);
 	} else if (vpmod->comp_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-			"-- error status (%x).\n", __func__, base_vha->host_no,
-			vpmod->comp_status));
+		ql_dbg(ql_dbg_mbx, vha, 0x10be,
+		    "Failed to complete IOCB -- error status (%x).\n",
+		    vpmod->comp_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x).\n", __func__, base_vha->host_no,
-		    le16_to_cpu(vpmod->comp_status)));
+		ql_dbg(ql_dbg_mbx, vha, 0x10bf,
+		    "Failed to complete IOCB -- completion status (%x).\n",
+		    le16_to_cpu(vpmod->comp_status));
 		rval = QLA_FUNCTION_FAILED;
 	} else {
 		/* EMPTY */
-		DEBUG11(printk("%s(%ld): done.\n", __func__,
-							base_vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10c0, "Done %s.\n", __func__);
 		fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
 	}
 	dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
@@ -3032,17 +2997,16 @@
 	int	vp_index = vha->vp_idx;
 	struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 
-	DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__,
-	    vha->host_no, vp_index));
+	ql_dbg(ql_dbg_mbx, vha, 0x10c1,
+	    "Entered %s enabling index %d.\n", __func__, vp_index);
 
 	if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
 		return QLA_PARAMETER_ERROR;
 
 	vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma);
 	if (!vce) {
-		DEBUG2_3(printk("%s(%ld): "
-		    "failed to allocate VP Control IOCB.\n", __func__,
-		    base_vha->host_no));
+		ql_log(ql_log_warn, vha, 0x10c2,
+		    "Failed to allocate VP control IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 	memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx));
@@ -3063,28 +3027,20 @@
 
 	rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB"
-		    "(%x).\n", __func__, base_vha->host_no, rval));
-		printk("%s(%ld): failed to issue VP control IOCB"
-		    "(%x).\n", __func__, base_vha->host_no, rval);
+		ql_dbg(ql_dbg_mbx, vha, 0x10c3,
+		    "Failed to issue VP control IOCB (%x).\n", rval);
 	} else if (vce->entry_status != 0) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, base_vha->host_no,
-		    vce->entry_status));
-		printk("%s(%ld): failed to complete IOCB "
-		    "-- error status (%x).\n", __func__, base_vha->host_no,
+		ql_dbg(ql_dbg_mbx, vha, 0x10c4,
+		    "Failed to complete IOCB -- error status (%x).\n",
 		    vce->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
-		DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x).\n", __func__, base_vha->host_no,
-		    le16_to_cpu(vce->comp_status)));
-		printk("%s(%ld): failed to complete IOCB "
-		    "-- completion status (%x).\n", __func__, base_vha->host_no,
+		ql_dbg(ql_dbg_mbx, vha, 0x10c5,
+		    "Failed to complet IOCB -- completion status (%x).\n",
 		    le16_to_cpu(vce->comp_status));
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		DEBUG2(printk("%s(%ld): done.\n", __func__, base_vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10c6, "Done %s.\n", __func__);
 	}
 
 	dma_pool_free(ha->s_dma_pool, vce, vce_dma);
@@ -3121,6 +3077,8 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10c7, "Entered %s.\n", __func__);
+
 	/*
 	 * This command is implicitly executed by firmware during login for the
 	 * physical hosts
@@ -3155,7 +3113,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1009, "Entered %s.\n", __func__);
 
 	if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
 		mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
@@ -3186,10 +3144,10 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1008,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1007, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3214,12 +3172,10 @@
 	unsigned long flags;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG16(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10c8, "Entered %s.\n", __func__);
 
 	mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
 	if (mn == NULL) {
-		DEBUG2_3(printk("%s(%ld): failed to allocate Verify ISP84XX "
-		    "IOCB.\n", __func__, vha->host_no));
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
@@ -3237,43 +3193,43 @@
 		mn->p.req.entry_count = 1;
 		mn->p.req.options = cpu_to_le16(options);
 
-		DEBUG16(printk("%s(%ld): Dump of Verify Request.\n", __func__,
-		    vha->host_no));
-		DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
-		    sizeof(*mn)));
+		ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111c,
+		    "Dump of Verify Request.\n");
+		ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111e,
+		    (uint8_t *)mn, sizeof(*mn));
 
 		rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
 		if (rval != QLA_SUCCESS) {
-			DEBUG2_16(printk("%s(%ld): failed to issue Verify "
-			    "IOCB (%x).\n", __func__, vha->host_no, rval));
+			ql_dbg(ql_dbg_mbx, vha, 0x10cb,
+			    "Failed to issue verify IOCB (%x).\n", rval);
 			goto verify_done;
 		}
 
-		DEBUG16(printk("%s(%ld): Dump of Verify Response.\n", __func__,
-		    vha->host_no));
-		DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
-		    sizeof(*mn)));
+		ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1110,
+		    "Dump of Verify Response.\n");
+		ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1118,
+		    (uint8_t *)mn, sizeof(*mn));
 
 		status[0] = le16_to_cpu(mn->p.rsp.comp_status);
 		status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
 		    le16_to_cpu(mn->p.rsp.failure_code) : 0;
-		DEBUG2_16(printk("%s(%ld): cs=%x fc=%x\n", __func__,
-		    vha->host_no, status[0], status[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ce,
+		    "cs=%x fc=%x.\n", status[0], status[1]);
 
 		if (status[0] != CS_COMPLETE) {
 			rval = QLA_FUNCTION_FAILED;
 			if (!(options & VCO_DONT_UPDATE_FW)) {
-				DEBUG2_16(printk("%s(%ld): Firmware update "
-				    "failed. Retrying without update "
-				    "firmware.\n", __func__, vha->host_no));
+				ql_dbg(ql_dbg_mbx, vha, 0x10cf,
+				    "Firmware update failed. Retrying "
+				    "without update firmware.\n");
 				options |= VCO_DONT_UPDATE_FW;
 				options &= ~VCO_FORCE_UPDATE;
 				retry = 1;
 			}
 		} else {
-			DEBUG2_16(printk("%s(%ld): firmware updated to %x.\n",
-			    __func__, vha->host_no,
-			    le32_to_cpu(mn->p.rsp.fw_ver)));
+			ql_dbg(ql_dbg_mbx, vha, 0x10d0,
+			    "Firmware updated to %x.\n",
+			    le32_to_cpu(mn->p.rsp.fw_ver));
 
 			/* NOTE: we only update OP firmware. */
 			spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
@@ -3288,10 +3244,9 @@
 	dma_pool_free(ha->s_dma_pool, mn, mn_dma);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_16(printk("%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10d1, "Failed=%x.\n", rval);
 	} else {
-		DEBUG16(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10d2, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3307,6 +3262,8 @@
 	struct device_reg_25xxmq __iomem *reg;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10d3, "Entered %s.\n", __func__);
+
 	mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
 	mcp->mb[1] = req->options;
 	mcp->mb[2] = MSW(LSD(req->dma));
@@ -3344,9 +3301,13 @@
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	rval = qla2x00_mailbox_command(vha, mcp);
-	if (rval != QLA_SUCCESS)
-		DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x mb0=%x.\n",
-			__func__, vha->host_no, rval, mcp->mb[0]));
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x10d4,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+	} else {
+		ql_dbg(ql_dbg_mbx, vha, 0x10d5, "Done %s.\n", __func__);
+	}
+
 	return rval;
 }
 
@@ -3360,6 +3321,8 @@
 	struct device_reg_25xxmq __iomem *reg;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10d6, "Entered %s.\n", __func__);
+
 	mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
 	mcp->mb[1] = rsp->options;
 	mcp->mb[2] = MSW(LSD(rsp->dma));
@@ -3393,10 +3356,13 @@
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	rval = qla2x00_mailbox_command(vha, mcp);
-	if (rval != QLA_SUCCESS)
-		DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x "
-			"mb0=%x.\n", __func__,
-			vha->host_no, rval, mcp->mb[0]));
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x10d7,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+	} else {
+		ql_dbg(ql_dbg_mbx, vha, 0x10d8, "Done %s.\n", __func__);
+	}
+
 	return rval;
 }
 
@@ -3407,7 +3373,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10d9, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_IDC_ACK;
 	memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
@@ -3418,10 +3384,10 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10da,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10db, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3434,11 +3400,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10dc, "Entered %s.\n", __func__);
+
 	if (!IS_QLA81XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
 	mcp->mb[1] = FAC_OPT_CMD_GET_SECTOR_SIZE;
 	mcp->out_mb = MBX_1|MBX_0;
@@ -3448,10 +3414,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10dd,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10de, "Done %s.\n", __func__);
 		*sector_size = mcp->mb[1];
 	}
 
@@ -3468,7 +3435,7 @@
 	if (!IS_QLA81XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10df, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
 	mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE :
@@ -3480,10 +3447,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e0,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e1, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3499,7 +3467,7 @@
 	if (!IS_QLA81XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10e2, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
 	mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR;
@@ -3514,11 +3482,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
-		    "mb[2]=%x.\n", __func__, vha->host_no, rval, mcp->mb[0],
-		    mcp->mb[1], mcp->mb[2]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e3,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e4, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3531,7 +3499,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10e5, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_RESTART_MPI_FW;
 	mcp->out_mb = MBX_0;
@@ -3541,10 +3509,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x mb[1]=0x%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e6,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e7, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3559,11 +3528,11 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10e8, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	if (len == 1)
 		opt |= BIT_0;
 
@@ -3586,10 +3555,10 @@
 		*sfp = mcp->mb[1];
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10e9,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ea, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3604,11 +3573,11 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10eb, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	if (len == 1)
 		opt |= BIT_0;
 
@@ -3631,10 +3600,10 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ec,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ed, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3648,11 +3617,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10ee, "Entered %s.\n", __func__);
+
 	if (!IS_QLA8XXX_TYPE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_GET_XGMAC_STATS;
 	mcp->mb[2] = MSW(stats_dma);
 	mcp->mb[3] = LSW(stats_dma);
@@ -3666,11 +3635,12 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
-		    "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ef,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f0, "Done %s.\n", __func__);
+
 
 		*actual_size = mcp->mb[2] << 2;
 	}
@@ -3686,11 +3656,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10f1, "Entered %s.\n", __func__);
+
 	if (!IS_QLA8XXX_TYPE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_GET_DCBX_PARAMS;
 	mcp->mb[1] = 0;
 	mcp->mb[2] = MSW(tlv_dma);
@@ -3705,11 +3675,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
-		    "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
-		    mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f2,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f3, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3722,11 +3692,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x10f4, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_READ_RAM_EXTENDED;
 	mcp->mb[1] = LSW(risc_addr);
 	mcp->mb[8] = MSW(risc_addr);
@@ -3736,10 +3706,10 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f5,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f6, "Done %s.\n", __func__);
 		*data = mcp->mb[3] << 16 | mcp->mb[2];
 	}
 
@@ -3755,7 +3725,7 @@
 	mbx_cmd_t *mcp = &mc;
 	uint32_t iter_cnt = 0x1;
 
-	DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10f7, "Entered %s.\n", __func__);
 
 	memset(mcp->mb, 0 , sizeof(mcp->mb));
 	mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK;
@@ -3794,15 +3764,12 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(printk(KERN_WARNING
-			"(%ld): failed=%x mb[0]=0x%x "
-			"mb[1]=0x%x mb[2]=0x%x mb[3]=0x%x mb[18]=0x%x "
-			"mb[19]=0x%x.\n",
-			vha->host_no, rval, mcp->mb[0], mcp->mb[1], mcp->mb[2],
-			mcp->mb[3], mcp->mb[18], mcp->mb[19]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f8,
+		    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[18]=%x "
+		    "mb[19]=%x.\n", rval, mcp->mb[0], mcp->mb[1], mcp->mb[2],
+		    mcp->mb[3], mcp->mb[18], mcp->mb[19]);
 	} else {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld): done.\n", vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10f9, "Done %s.\n", __func__);
 	}
 
 	/* Copy mailbox information */
@@ -3819,7 +3786,7 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10fa, "Entered %s.\n", __func__);
 
 	memset(mcp->mb, 0 , sizeof(mcp->mb));
 	mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
@@ -3858,12 +3825,11 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2(printk(KERN_WARNING
-		    "(%ld): failed=%x mb[0]=0x%x mb[1]=0x%x.\n",
-		    vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+		ql_dbg(ql_dbg_mbx, vha, 0x10fb,
+		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
+		    rval, mcp->mb[0], mcp->mb[1]);
 	} else {
-		DEBUG2(printk(KERN_WARNING
-		    "scsi(%ld): done.\n", vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10fc, "Done %s.\n", __func__);
 	}
 
 	/* Copy mailbox information */
@@ -3872,14 +3838,14 @@
 }
 
 int
-qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic)
+qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
 {
 	int rval;
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG16(printk("%s(%ld): enable_diag=%d entered.\n", __func__,
-		ha->host_no, enable_diagnostic));
+	ql_dbg(ql_dbg_mbx, vha, 0x10fd,
+	    "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic);
 
 	mcp->mb[0] = MBC_ISP84XX_RESET;
 	mcp->mb[1] = enable_diagnostic;
@@ -3887,13 +3853,12 @@
 	mcp->in_mb = MBX_1|MBX_0;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
-	rval = qla2x00_mailbox_command(ha, mcp);
+	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS)
-		DEBUG16(printk("%s(%ld): failed=%x.\n", __func__, ha->host_no,
-			rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval);
 	else
-		DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10ff, "Done %s.\n", __func__);
 
 	return rval;
 }
@@ -3905,11 +3870,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x1100, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED;
 	mcp->mb[1] = LSW(risc_addr);
 	mcp->mb[2] = LSW(data);
@@ -3921,10 +3886,10 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1101,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1102, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3941,8 +3906,7 @@
 
 	rval = QLA_SUCCESS;
 
-	DEBUG11(qla_printk(KERN_INFO, ha,
-	    "%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1103, "Entered %s.\n", __func__);
 
 	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
@@ -3982,11 +3946,10 @@
 		rval = QLA_FUNCTION_FAILED;
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_INFO "%s(%ld): failed=%x mb[0]=%x.\n",
-		    __func__, vha->host_no, rval, mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1104,
+		    "Failed=%x mb[0]=%x.\n", rval, mb[0]);
 	} else {
-		DEBUG11(printk(KERN_INFO
-		    "%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1105, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -3999,12 +3962,11 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x1106, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(qla_printk(KERN_INFO, ha,
-		"%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_DATA_RATE;
 	mcp->mb[1] = 0;
 	mcp->out_mb = MBX_1|MBX_0;
@@ -4013,11 +3975,10 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_INFO "%s(%ld): failed=%x mb[0]=%x.\n",
-		    __func__, vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1107,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(printk(KERN_INFO
-		    "%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x1108, "Done %s.\n", __func__);
 		if (mcp->mb[1] != 0x7)
 			ha->link_data_rate = mcp->mb[1];
 	}
@@ -4033,8 +3994,7 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk(KERN_INFO
-	    "%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1109, "Entered %s.\n", __func__);
 
 	if (!IS_QLA81XX(ha))
 		return QLA_FUNCTION_FAILED;
@@ -4047,15 +4007,13 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x110a,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
 		/* Copy all bits to preserve original value */
 		memcpy(mb, &mcp->mb[1], sizeof(uint16_t) * 4);
 
-		DEBUG11(printk(KERN_INFO
-		    "%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x110b, "Done %s.\n", __func__);
 	}
 	return rval;
 }
@@ -4067,8 +4025,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	DEBUG11(printk(KERN_INFO
-	    "%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x110c, "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_SET_PORT_CONFIG;
 	/* Copy all bits to preserve original setting */
@@ -4080,12 +4037,10 @@
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): failed=%x (%x).\n", __func__,
-		    vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x110d,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else
-		DEBUG11(printk(KERN_INFO
-		    "%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x110e, "Done %s.\n", __func__);
 
 	return rval;
 }
@@ -4100,12 +4055,11 @@
 	mbx_cmd_t *mcp = &mc;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x110f, "Entered %s.\n", __func__);
+
 	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(printk(KERN_INFO
-	    "%s(%ld): entered.\n", __func__, vha->host_no));
-
 	mcp->mb[0] = MBC_PORT_PARAMS;
 	mcp->mb[1] = loop_id;
 	if (ha->flags.fcp_prio_enabled)
@@ -4127,12 +4081,9 @@
 	}
 
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): failed=%x.\n", __func__,
-		    vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval);
 	} else {
-		DEBUG11(printk(KERN_INFO
-		    "%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x10cc, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -4145,13 +4096,12 @@
 	uint8_t byte;
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG11(printk(KERN_INFO "%s(%ld): entered.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x10ca, "Entered %s.\n", __func__);
 
 	/* Integer part */
 	rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x10c9, "Failed=%x.\n", rval);
 		ha->flags.thermal_supported = 0;
 		goto fail;
 	}
@@ -4160,14 +4110,13 @@
 	/* Fraction part */
 	rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(printk(KERN_WARNING
-		    "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval));
+		ql_dbg(ql_dbg_mbx, vha, 0x1019, "Failed=%x.\n", rval);
 		ha->flags.thermal_supported = 0;
 		goto fail;
 	}
 	*frac = (byte >> 6) * 25;
 
-	DEBUG11(printk(KERN_INFO "%s(%ld): done.\n", __func__, vha->host_no));
+	ql_dbg(ql_dbg_mbx, vha, 0x1018, "Done %s.\n", __func__);
 fail:
 	return rval;
 }
@@ -4180,12 +4129,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x1017, "Entered %s.\n", __func__);
+
 	if (!IS_FWI2_CAPABLE(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(qla_printk(KERN_INFO, ha,
-		"%s(%ld): entered.\n", __func__, vha->host_no));
-
 	memset(mcp, 0, sizeof(mbx_cmd_t));
 	mcp->mb[0] = MBC_TOGGLE_INTERRUPT;
 	mcp->mb[1] = 1;
@@ -4197,12 +4145,10 @@
 
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(qla_printk(KERN_WARNING, ha,
-			"%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-			vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x1016,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(qla_printk(KERN_INFO, ha,
-			"%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x100e, "Done %s.\n", __func__);
 	}
 
 	return rval;
@@ -4216,12 +4162,11 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
+	ql_dbg(ql_dbg_mbx, vha, 0x100d, "Entered %s.\n", __func__);
+
 	if (!IS_QLA82XX(ha))
 		return QLA_FUNCTION_FAILED;
 
-	DEBUG11(qla_printk(KERN_INFO, ha,
-		"%s(%ld): entered.\n", __func__, vha->host_no));
-
 	memset(mcp, 0, sizeof(mbx_cmd_t));
 	mcp->mb[0] = MBC_TOGGLE_INTERRUPT;
 	mcp->mb[1] = 0;
@@ -4233,12 +4178,10 @@
 
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		DEBUG2_3_11(qla_printk(KERN_WARNING, ha,
-			"%s(%ld): failed=%x mb[0]=%x.\n", __func__,
-			vha->host_no, rval, mcp->mb[0]));
+		ql_dbg(ql_dbg_mbx, vha, 0x100c,
+		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		DEBUG11(qla_printk(KERN_INFO, ha,
-			"%s(%ld): done.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_mbx, vha, 0x100b, "Done %s.\n", __func__);
 	}
 
 	return rval;
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 5e34391..c706ed3 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -36,8 +36,9 @@
 	mutex_lock(&ha->vport_lock);
 	vp_id = find_first_zero_bit(ha->vp_idx_map, ha->max_npiv_vports + 1);
 	if (vp_id > ha->max_npiv_vports) {
-		DEBUG15(printk ("vp_id %d is bigger than max-supported %d.\n",
-		    vp_id, ha->max_npiv_vports));
+		ql_dbg(ql_dbg_vport, vha, 0xa000,
+		    "vp_id %d is bigger than max-supported %d.\n",
+		    vp_id, ha->max_npiv_vports);
 		mutex_unlock(&ha->vport_lock);
 		return vp_id;
 	}
@@ -131,9 +132,9 @@
 	fc_port_t *fcport;
 
 	list_for_each_entry(fcport, &vha->vp_fcports, list) {
-		DEBUG15(printk("scsi(%ld): Marking port dead, "
-		    "loop_id=0x%04x :%x\n",
-		    vha->host_no, fcport->loop_id, fcport->vp_idx));
+		ql_dbg(ql_dbg_vport, vha, 0xa001,
+		    "Marking port dead, loop_id=0x%04x : %x.\n",
+		    fcport->loop_id, fcport->vp_idx);
 
 		qla2x00_mark_device_lost(vha, fcport, 0, 0);
 		qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
@@ -187,13 +188,13 @@
 		goto enable_failed;
 	}
 
-	DEBUG15(qla_printk(KERN_INFO, ha,
-	    "Virtual port with id: %d - Enabled\n", vha->vp_idx));
+	ql_dbg(ql_dbg_taskm, vha, 0x801a,
+	    "Virtual port with id: %d - Enabled.\n", vha->vp_idx);
 	return 0;
 
 enable_failed:
-	DEBUG15(qla_printk(KERN_INFO, ha,
-	    "Virtual port with id: %d - Disabled\n", vha->vp_idx));
+	ql_dbg(ql_dbg_taskm, vha, 0x801b,
+	    "Virtual port with id: %d - Disabled.\n", vha->vp_idx);
 	return 1;
 }
 
@@ -205,12 +206,12 @@
 
 	fc_vport = vha->fc_vport;
 
-	DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n",
-	    vha->host_no, __func__));
+	ql_dbg(ql_dbg_vport, vha, 0xa002,
+	    "%s: change request #3.\n", __func__);
 	ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx);
 	if (ret != QLA_SUCCESS) {
-		DEBUG15(qla_printk(KERN_ERR, vha->hw, "Failed to enable "
-		    "receiving of RSCN requests: 0x%x\n", ret));
+		ql_dbg(ql_dbg_vport, vha, 0xa003, "Failed to enable "
+		    "receiving of RSCN requests: 0x%x.\n", ret);
 		return;
 	} else {
 		/* Corresponds to SCR enabled */
@@ -248,9 +249,9 @@
 			case MBA_CHG_IN_CONNECTION:
 			case MBA_PORT_UPDATE:
 			case MBA_RSCN_UPDATE:
-				DEBUG15(printk("scsi(%ld)%s: Async_event for"
-				" VP[%d], mb = 0x%x, vha=%p\n",
-				vha->host_no, __func__, i, *mb, vha));
+				ql_dbg(ql_dbg_async, vha, 0x5024,
+				    "Async_event for VP[%d], mb=0x%x vha=%p.\n",
+				    i, *mb, vha);
 				qla2x00_async_event(vha, rsp, mb);
 				break;
 			}
@@ -286,37 +287,49 @@
 	if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
 		qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
 
-	DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n",
-	    vha->host_no, vha->vp_idx));
+	ql_dbg(ql_dbg_taskm, vha, 0x801d,
+	    "Scheduling enable of Vport %d.\n", vha->vp_idx);
 	return qla24xx_enable_vp(vha);
 }
 
 static int
 qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
 {
+	ql_dbg(ql_dbg_dpc, vha, 0x4012,
+	    "Entering %s.\n", __func__);
+	ql_dbg(ql_dbg_dpc, vha, 0x4013,
+	    "vp_flags: 0x%lx.\n", vha->vp_flags);
+
 	qla2x00_do_work(vha);
 
 	if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
 		/* VP acquired. complete port configuration */
+		ql_dbg(ql_dbg_dpc, vha, 0x4014,
+		    "Configure VP scheduled.\n");
 		qla24xx_configure_vp(vha);
+		ql_dbg(ql_dbg_dpc, vha, 0x4015,
+		    "Configure VP end.\n");
 		return 0;
 	}
 
 	if (test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) {
+		ql_dbg(ql_dbg_dpc, vha, 0x4016,
+		    "FCPort update scheduled.\n");
 		qla2x00_update_fcports(vha);
 		clear_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+		ql_dbg(ql_dbg_dpc, vha, 0x4017,
+		    "FCPort update end.\n");
 	}
 
 	if ((test_and_clear_bit(RELOGIN_NEEDED, &vha->dpc_flags)) &&
 		!test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) &&
 		atomic_read(&vha->loop_state) != LOOP_DOWN) {
 
-		DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
-						vha->host_no));
+		ql_dbg(ql_dbg_dpc, vha, 0x4018,
+		    "Relogin needed scheduled.\n");
 		qla2x00_relogin(vha);
-
-		DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
-							vha->host_no));
+		ql_dbg(ql_dbg_dpc, vha, 0x4019,
+		    "Relogin needed end.\n");
 	}
 
 	if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) &&
@@ -326,11 +339,17 @@
 
 	if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
 		if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
+			ql_dbg(ql_dbg_dpc, vha, 0x401a,
+			    "Loop resync scheduled.\n");
 			qla2x00_loop_resync(vha);
 			clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
+			ql_dbg(ql_dbg_dpc, vha, 0x401b,
+			    "Loop resync end.\n");
 		}
 	}
 
+	ql_dbg(ql_dbg_dpc, vha, 0x401c,
+	    "Exiting %s.\n", __func__);
 	return 0;
 }
 
@@ -396,9 +415,10 @@
 
 	/* Check up max-npiv-supports */
 	if (ha->num_vhosts > ha->max_npiv_vports) {
-		DEBUG15(printk("scsi(%ld): num_vhosts %ud is bigger than "
-		    "max_npv_vports %ud.\n", base_vha->host_no,
-		    ha->num_vhosts, ha->max_npiv_vports));
+		ql_dbg(ql_dbg_vport, vha, 0xa004,
+		    "num_vhosts %ud is bigger "
+		    "than max_npiv_vports %ud.\n",
+		    ha->num_vhosts, ha->max_npiv_vports);
 		return VPCERR_UNSUPPORTED;
 	}
 	return 0;
@@ -415,7 +435,8 @@
 
 	vha = qla2x00_create_host(sht, ha);
 	if (!vha) {
-		DEBUG(printk("qla2xxx: scsi_host_alloc() failed for vport\n"));
+		ql_log(ql_log_warn, vha, 0xa005,
+		    "scsi_host_alloc() failed for vport.\n");
 		return(NULL);
 	}
 
@@ -429,8 +450,8 @@
 	vha->device_flags = 0;
 	vha->vp_idx = qla24xx_allocate_vp_id(vha);
 	if (vha->vp_idx > ha->max_npiv_vports) {
-		DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n",
-			vha->host_no));
+		ql_dbg(ql_dbg_vport, vha, 0xa006,
+		    "Couldn't allocate vp_id.\n");
 		goto create_vhost_failed;
 	}
 	vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
@@ -461,8 +482,9 @@
 	host->max_id = MAX_TARGETS_2200;
 	host->transportt = qla2xxx_transport_vport_template;
 
-	DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n",
-	    vha->host_no, vha));
+	ql_dbg(ql_dbg_vport, vha, 0xa007,
+	    "Detect vport hba %ld at address = %p.\n",
+	    vha->host_no, vha);
 
 	vha->flags.init_done = 1;
 
@@ -567,9 +589,9 @@
 		if (req) {
 			ret = qla25xx_delete_req_que(vha, req);
 			if (ret != QLA_SUCCESS) {
-				qla_printk(KERN_WARNING, ha,
-				"Couldn't delete req que %d\n",
-				req->id);
+				ql_log(ql_log_warn, vha, 0x00ea,
+				    "Couldn't delete req que %d.\n",
+				    req->id);
 				return ret;
 			}
 		}
@@ -581,9 +603,9 @@
 		if (rsp) {
 			ret = qla25xx_delete_rsp_que(vha, rsp);
 			if (ret != QLA_SUCCESS) {
-				qla_printk(KERN_WARNING, ha,
-				"Couldn't delete rsp que %d\n",
-				rsp->id);
+				ql_log(ql_log_warn, vha, 0x00eb,
+				    "Couldn't delete rsp que %d.\n",
+				    rsp->id);
 				return ret;
 			}
 		}
@@ -604,8 +626,8 @@
 
 	req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
 	if (req == NULL) {
-		qla_printk(KERN_WARNING, ha, "could not allocate memory"
-			"for request que\n");
+		ql_log(ql_log_fatal, base_vha, 0x00d9,
+		    "Failed to allocate memory for request queue.\n");
 		goto failed;
 	}
 
@@ -614,8 +636,8 @@
 			(req->length + 1) * sizeof(request_t),
 			&req->dma, GFP_KERNEL);
 	if (req->ring == NULL) {
-		qla_printk(KERN_WARNING, ha,
-		"Memory Allocation failed - request_ring\n");
+		ql_log(ql_log_fatal, base_vha, 0x00da,
+		    "Failed to allocte memory for request_ring.\n");
 		goto que_failed;
 	}
 
@@ -623,8 +645,8 @@
 	que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues);
 	if (que_id >= ha->max_req_queues) {
 		mutex_unlock(&ha->vport_lock);
-		qla_printk(KERN_INFO, ha, "No resources to create "
-			 "additional request queue\n");
+		ql_log(ql_log_warn, base_vha, 0x00db,
+		    "No resources to create additional request queue.\n");
 		goto que_failed;
 	}
 	set_bit(que_id, ha->req_qid_map);
@@ -633,6 +655,12 @@
 	req->vp_idx = vp_idx;
 	req->qos = qos;
 
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc002,
+	    "queue_id=%d rid=%d vp_idx=%d qos=%d.\n",
+	    que_id, req->rid, req->vp_idx, req->qos);
+	ql_dbg(ql_dbg_init, base_vha, 0x00dc,
+	    "queue_id=%d rid=%d vp_idx=%d qos=%d.\n",
+	    que_id, req->rid, req->vp_idx, req->qos);
 	if (rsp_que < 0)
 		req->rsp = NULL;
 	else
@@ -645,6 +673,10 @@
 		options |= BIT_5;
 	req->options = options;
 
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc003,
+	    "options=0x%x.\n", req->options);
+	ql_dbg(ql_dbg_init, base_vha, 0x00dd,
+	    "options=0x%x.\n", req->options);
 	for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
 		req->outstanding_cmds[cnt] = NULL;
 	req->current_outstanding_cmd = 1;
@@ -656,10 +688,21 @@
 	reg = ISP_QUE_REG(ha, que_id);
 	req->max_q_depth = ha->req_q_map[0]->max_q_depth;
 	mutex_unlock(&ha->vport_lock);
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc004,
+	    "ring_ptr=%p ring_index=%d, "
+	    "cnt=%d id=%d max_q_depth=%d.\n",
+	    req->ring_ptr, req->ring_index,
+	    req->cnt, req->id, req->max_q_depth);
+	ql_dbg(ql_dbg_init, base_vha, 0x00de,
+	    "ring_ptr=%p ring_index=%d, "
+	    "cnt=%d id=%d max_q_depth=%d.\n",
+	    req->ring_ptr, req->ring_index, req->cnt,
+	    req->id, req->max_q_depth);
 
 	ret = qla25xx_init_req_que(base_vha, req);
 	if (ret != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+		ql_log(ql_log_fatal, base_vha, 0x00df,
+		    "%s failed.\n", __func__);
 		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->req_qid_map);
 		mutex_unlock(&ha->vport_lock);
@@ -700,8 +743,8 @@
 
 	rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
 	if (rsp == NULL) {
-		qla_printk(KERN_WARNING, ha, "could not allocate memory for"
-				" response que\n");
+		ql_log(ql_log_warn, base_vha, 0x0066,
+		    "Failed to allocate memory for response queue.\n");
 		goto failed;
 	}
 
@@ -710,8 +753,8 @@
 			(rsp->length + 1) * sizeof(response_t),
 			&rsp->dma, GFP_KERNEL);
 	if (rsp->ring == NULL) {
-		qla_printk(KERN_WARNING, ha,
-		"Memory Allocation failed - response_ring\n");
+		ql_log(ql_log_warn, base_vha, 0x00e1,
+		    "Failed to allocate memory for response ring.\n");
 		goto que_failed;
 	}
 
@@ -719,8 +762,8 @@
 	que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
 	if (que_id >= ha->max_rsp_queues) {
 		mutex_unlock(&ha->vport_lock);
-		qla_printk(KERN_INFO, ha, "No resources to create "
-			 "additional response queue\n");
+		ql_log(ql_log_warn, base_vha, 0x00e2,
+		    "No resources to create additional request queue.\n");
 		goto que_failed;
 	}
 	set_bit(que_id, ha->rsp_qid_map);
@@ -728,12 +771,16 @@
 	if (ha->flags.msix_enabled)
 		rsp->msix = &ha->msix_entries[que_id + 1];
 	else
-		qla_printk(KERN_WARNING, ha, "msix not enabled\n");
+		ql_log(ql_log_warn, base_vha, 0x00e3,
+		    "MSIX not enalbled.\n");
 
 	ha->rsp_q_map[que_id] = rsp;
 	rsp->rid = rid;
 	rsp->vp_idx = vp_idx;
 	rsp->hw = ha;
+	ql_dbg(ql_dbg_init, base_vha, 0x00e4,
+	    "queue_id=%d rid=%d vp_idx=%d hw=%p.\n",
+	    que_id, rsp->rid, rsp->vp_idx, rsp->hw);
 	/* Use alternate PCI bus number */
 	if (MSB(rsp->rid))
 		options |= BIT_4;
@@ -750,6 +797,14 @@
 	rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
 	rsp->rsp_q_out = &reg->isp25mq.rsp_q_out;
 	mutex_unlock(&ha->vport_lock);
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc00b,
+	    "options=%x id=%d rsp_q_in=%p rsp_q_out=%p",
+	    rsp->options, rsp->id, rsp->rsp_q_in,
+	    rsp->rsp_q_out);
+	ql_dbg(ql_dbg_init, base_vha, 0x00e5,
+	    "options=%x id=%d rsp_q_in=%p rsp_q_out=%p",
+	    rsp->options, rsp->id, rsp->rsp_q_in,
+	    rsp->rsp_q_out);
 
 	ret = qla25xx_request_irq(rsp);
 	if (ret)
@@ -757,7 +812,8 @@
 
 	ret = qla25xx_init_rsp_que(base_vha, rsp);
 	if (ret != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+		ql_log(ql_log_fatal, base_vha, 0x00e7,
+		    "%s failed.\n", __func__);
 		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->rsp_qid_map);
 		mutex_unlock(&ha->vport_lock);
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index e1138bc..5cbf33a 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -348,6 +348,7 @@
 qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off)
 {
 	u32 win_read;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	ha->crb_win = CRB_HI(*off);
 	writel(ha->crb_win,
@@ -358,9 +359,10 @@
 	 */
 	win_read = RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
 	if (win_read != ha->crb_win) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "%s: Written crbwin (0x%x) != Read crbwin (0x%x), "
-		    "off=0x%lx\n", __func__, ha->crb_win, win_read, *off));
+		ql_dbg(ql_dbg_p3p, vha, 0xb000,
+		    "%s: Written crbwin (0x%x) "
+		    "!= Read crbwin (0x%x), off=0x%lx.\n",
+		    ha->crb_win, win_read, *off);
 	}
 	*off = (*off & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase;
 }
@@ -368,6 +370,7 @@
 static inline unsigned long
 qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	/* See if we are currently pointing to the region we want to use next */
 	if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_DDR_NET)) {
 		/* No need to change window. PCIX and PCIEregs are in both
@@ -398,9 +401,10 @@
 			return off;
 	}
 	/* strange address given */
-	qla_printk(KERN_WARNING, ha,
-		"%s: Warning: unm_nic_pci_set_crbwindow called with"
-		" an unknown address(%llx)\n", QLA2XXX_DRIVER_NAME, off);
+	ql_dbg(ql_dbg_p3p, vha, 0xb001,
+	    "%x: Warning: unm_nic_pci_set_crbwindow "
+	    "called with an unknown address(%llx).\n",
+	    QLA2XXX_DRIVER_NAME, off);
 	return off;
 }
 
@@ -563,6 +567,7 @@
 {
 	int window;
 	u32 win_read;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET,
 		QLA82XX_ADDR_DDR_NET_MAX)) {
@@ -574,8 +579,8 @@
 		win_read = qla82xx_rd_32(ha,
 			ha->mn_win_crb | QLA82XX_PCI_CRBSPACE);
 		if ((win_read << 17) != window) {
-			qla_printk(KERN_WARNING, ha,
-			    "%s: Written MNwin (0x%x) != Read MNwin (0x%x)\n",
+			ql_dbg(ql_dbg_p3p, vha, 0xb003,
+			    "%s: Written MNwin (0x%x) != Read MNwin (0x%x).\n",
 			    __func__, window, win_read);
 		}
 		addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_DDR_NET;
@@ -583,7 +588,7 @@
 		QLA82XX_ADDR_OCM0_MAX)) {
 		unsigned int temp1;
 		if ((addr & 0x00ff800) == 0xff800) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0xb004,
 			    "%s: QM access not handled.\n", __func__);
 			addr = -1UL;
 		}
@@ -596,8 +601,8 @@
 		temp1 = ((window & 0x1FF) << 7) |
 		    ((window & 0x0FFFE0000) >> 17);
 		if (win_read != temp1) {
-			qla_printk(KERN_WARNING, ha,
-			    "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x)\n",
+			ql_log(ql_log_warn, vha, 0xb005,
+			    "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x).\n",
 			    __func__, temp1, win_read);
 		}
 		addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_OCM0_2M;
@@ -612,8 +617,8 @@
 		win_read = qla82xx_rd_32(ha,
 			ha->ms_win_crb | QLA82XX_PCI_CRBSPACE);
 		if (win_read != window) {
-			qla_printk(KERN_WARNING, ha,
-			    "%s: Written MSwin (0x%x) != Read MSwin (0x%x)\n",
+			ql_log(ql_log_warn, vha, 0xb006,
+			    "%s: Written MSwin (0x%x) != Read MSwin (0x%x).\n",
 			    __func__, window, win_read);
 		}
 		addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_QDR_NET;
@@ -624,9 +629,9 @@
 		 */
 		if ((qla82xx_pci_set_window_warning_count++ < 8) ||
 		    (qla82xx_pci_set_window_warning_count%64 == 0)) {
-			qla_printk(KERN_WARNING, ha,
-			    "%s: Warning:%s Unknown address range!\n", __func__,
-			    QLA2XXX_DRIVER_NAME);
+			ql_log(ql_log_warn, vha, 0xb007,
+			    "%s: Warning:%s Unknown address range!.\n",
+			    __func__, QLA2XXX_DRIVER_NAME);
 		}
 		addr = -1UL;
 	}
@@ -671,6 +676,7 @@
 	uint8_t         *mem_ptr = NULL;
 	unsigned long   mem_base;
 	unsigned long   mem_page;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	write_lock_irqsave(&ha->hw_lock, flags);
 
@@ -682,9 +688,10 @@
 	if ((start == -1UL) ||
 		(qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) {
 		write_unlock_irqrestore(&ha->hw_lock, flags);
-		qla_printk(KERN_ERR, ha,
-			"%s out of bound pci memory access. "
-			"offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off);
+		ql_log(ql_log_fatal, vha, 0xb008,
+		    "%s out of bound pci memory "
+		    "access, offset is 0x%llx.\n",
+		    QLA2XXX_DRIVER_NAME, off);
 		return -1;
 	}
 
@@ -741,6 +748,7 @@
 	uint8_t         *mem_ptr = NULL;
 	unsigned long   mem_base;
 	unsigned long   mem_page;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	write_lock_irqsave(&ha->hw_lock, flags);
 
@@ -752,9 +760,10 @@
 	if ((start == -1UL) ||
 		(qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) {
 		write_unlock_irqrestore(&ha->hw_lock, flags);
-		qla_printk(KERN_ERR, ha,
-			"%s out of bound pci memory access. "
-			"offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off);
+		ql_log(ql_log_fatal, vha, 0xb009,
+		    "%s out of bount memory "
+		    "access, offset is 0x%llx.\n",
+		    QLA2XXX_DRIVER_NAME, off);
 		return -1;
 	}
 
@@ -855,15 +864,16 @@
 {
 	long timeout = 0;
 	long done = 0 ;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	while (done == 0) {
 		done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS);
 		done &= 4;
 		timeout++;
 		if (timeout >= rom_max_timeout) {
-			DEBUG(qla_printk(KERN_INFO, ha,
-				"%s: Timeout reached waiting for rom busy",
-				QLA2XXX_DRIVER_NAME));
+			ql_dbg(ql_dbg_p3p, vha, 0xb00a,
+			    "%s: Timeout reached waiting for rom busy.\n",
+			    QLA2XXX_DRIVER_NAME);
 			return -1;
 		}
 	}
@@ -875,15 +885,16 @@
 {
 	long timeout = 0;
 	long done = 0 ;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	while (done == 0) {
 		done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS);
 		done &= 2;
 		timeout++;
 		if (timeout >= rom_max_timeout) {
-			DEBUG(qla_printk(KERN_INFO, ha,
-				"%s: Timeout reached  waiting for rom done",
-				QLA2XXX_DRIVER_NAME));
+			ql_dbg(ql_dbg_p3p, vha, 0xb00b,
+			    "%s: Timeout reached waiting for rom done.\n",
+			    QLA2XXX_DRIVER_NAME);
 			return -1;
 		}
 	}
@@ -893,15 +904,16 @@
 static int
 qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0xb);
 	qla82xx_wait_rom_busy(ha);
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-			"%s: Error waiting for rom done\n",
-			QLA2XXX_DRIVER_NAME);
+		ql_log(ql_log_fatal, vha, 0x00ba,
+		    "Error waiting for rom done.\n");
 		return -1;
 	}
 	/* Reset abyte_cnt and dummy_byte_cnt */
@@ -917,6 +929,7 @@
 qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
 {
 	int ret, loops = 0;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) {
 		udelay(100);
@@ -924,9 +937,8 @@
 		loops++;
 	}
 	if (loops >= 50000) {
-		qla_printk(KERN_INFO, ha,
-			"%s: qla82xx_rom_lock failed\n",
-			QLA2XXX_DRIVER_NAME);
+		ql_log(ql_log_fatal, vha, 0x00b9,
+		    "Failed to aquire SEM2 lock.\n");
 		return -1;
 	}
 	ret = qla82xx_do_rom_fast_read(ha, addr, valp);
@@ -937,11 +949,12 @@
 static int
 qla82xx_read_status_reg(struct qla_hw_data *ha, uint32_t *val)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_RDSR);
 	qla82xx_wait_rom_busy(ha);
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Error waiting for rom done\n");
+		ql_log(ql_log_warn, vha, 0xb00c,
+		    "Error waiting for rom done.\n");
 		return -1;
 	}
 	*val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA);
@@ -955,6 +968,7 @@
 	uint32_t done = 1 ;
 	uint32_t val;
 	int ret = 0;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0);
 	while ((done != 0) && (ret == 0)) {
@@ -964,8 +978,8 @@
 		udelay(10);
 		cond_resched();
 		if (timeout >= 50000) {
-			qla_printk(KERN_WARNING, ha,
-			    "Timeout reached  waiting for write finish");
+			ql_log(ql_log_warn, vha, 0xb00d,
+			    "Timeout reached waiting for write finish.\n");
 			return -1;
 		}
 	}
@@ -992,13 +1006,14 @@
 static int
 qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	if (qla82xx_flash_set_write_enable(ha))
 		return -1;
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, val);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0x1);
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Error waiting for rom done\n");
+		ql_log(ql_log_warn, vha, 0xb00e,
+		    "Error waiting for rom done.\n");
 		return -1;
 	}
 	return qla82xx_flash_wait_write_finish(ha);
@@ -1007,10 +1022,11 @@
 static int
 qla82xx_write_disable_flash(struct qla_hw_data *ha)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WRDI);
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Error waiting for rom done\n");
+		ql_log(ql_log_warn, vha, 0xb00f,
+		    "Error waiting for rom done.\n");
 		return -1;
 	}
 	return 0;
@@ -1020,13 +1036,16 @@
 ql82xx_rom_lock_d(struct qla_hw_data *ha)
 {
 	int loops = 0;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
 	while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) {
 		udelay(100);
 		cond_resched();
 		loops++;
 	}
 	if (loops >= 50000) {
-		qla_printk(KERN_WARNING, ha, "ROM lock failed\n");
+		ql_log(ql_log_warn, vha, 0xb010,
+		    "ROM lock failed.\n");
 		return -1;
 	}
 	return 0;;
@@ -1037,10 +1056,12 @@
 	uint32_t data)
 {
 	int ret = 0;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	ret = ql82xx_rom_lock_d(ha);
 	if (ret < 0) {
-		qla_printk(KERN_WARNING, ha, "ROM Lock failed\n");
+		ql_log(ql_log_warn, vha, 0xb011,
+		    "ROM lock failed.\n");
 		return ret;
 	}
 
@@ -1053,8 +1074,8 @@
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_PP);
 	qla82xx_wait_rom_busy(ha);
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-			"Error waiting for rom done\n");
+		ql_log(ql_log_warn, vha, 0xb012,
+		    "Error waiting for rom done.\n");
 		ret = -1;
 		goto done_write;
 	}
@@ -1159,8 +1180,8 @@
 	 */
 	if (qla82xx_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafeUL ||
 	    qla82xx_rom_fast_read(ha, 4, &n) != 0) {
-		qla_printk(KERN_WARNING, ha,
-		    "[ERROR] Reading crb_init area: n: %08x\n", n);
+		ql_log(ql_log_fatal, vha, 0x006e,
+		    "Error Reading crb_init area: n: %08x.\n", n);
 		return -1;
 	}
 
@@ -1172,20 +1193,18 @@
 
 	/* number of addr/value pair should not exceed 1024 enteries */
 	if (n  >= 1024) {
-		qla_printk(KERN_WARNING, ha,
-		    "%s: %s:n=0x%x [ERROR] Card flash not initialized.\n",
-		    QLA2XXX_DRIVER_NAME, __func__, n);
+		ql_log(ql_log_fatal, vha, 0x0071,
+		    "Card flash not initialized:n=0x%x.\n", n);
 		return -1;
 	}
 
-	qla_printk(KERN_INFO, ha,
-	    "%s: %d CRB init values found in ROM.\n", QLA2XXX_DRIVER_NAME, n);
+	ql_log(ql_log_info, vha, 0x0072,
+	    "%d CRB init values found in ROM.\n", n);
 
 	buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL);
 	if (buf == NULL) {
-		qla_printk(KERN_WARNING, ha,
-		    "%s: [ERROR] Unable to malloc memory.\n",
-		    QLA2XXX_DRIVER_NAME);
+		ql_log(ql_log_fatal, vha, 0x010c,
+		    "Unable to allocate memory.\n");
 		return -1;
 	}
 
@@ -1236,9 +1255,8 @@
 			continue;
 
 		if (off == ADDR_ERROR) {
-			qla_printk(KERN_WARNING, ha,
-			    "%s: [ERROR] Unknown addr: 0x%08lx\n",
-			    QLA2XXX_DRIVER_NAME, buf[i].addr);
+			ql_log(ql_log_fatal, vha, 0x0116,
+			    "Unknow addr: 0x%08lx.\n", buf[i].addr);
 			continue;
 		}
 
@@ -1370,7 +1388,7 @@
 		if (j >= MAX_CTL_CHECK) {
 			if (printk_ratelimit())
 				dev_err(&ha->pdev->dev,
-				    "failed to write through agent\n");
+				    "failed to write through agent.\n");
 			ret = -1;
 			break;
 		}
@@ -1460,7 +1478,7 @@
 		if (j >= MAX_CTL_CHECK) {
 			if (printk_ratelimit())
 				dev_err(&ha->pdev->dev,
-				    "failed to read through agent\n");
+				    "failed to read through agent.\n");
 			break;
 		}
 
@@ -1633,17 +1651,15 @@
 	uint32_t len = 0;
 
 	if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) {
-		qla_printk(KERN_WARNING, ha,
-			"Failed to reserve selected regions (%s)\n",
-			pci_name(ha->pdev));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x000c,
+		    "Failed to reserver selected regions.\n");
 		goto iospace_error_exit;
 	}
 
 	/* Use MMIO operations for all accesses. */
 	if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) {
-		qla_printk(KERN_ERR, ha,
-			"region #0 not an MMIO resource (%s), aborting\n",
-			pci_name(ha->pdev));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x000d,
+		    "Region #0 not an MMIO resource, aborting.\n");
 		goto iospace_error_exit;
 	}
 
@@ -1651,9 +1667,8 @@
 	ha->nx_pcibase =
 	    (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len);
 	if (!ha->nx_pcibase) {
-		qla_printk(KERN_ERR, ha,
-		    "cannot remap pcibase MMIO (%s), aborting\n",
-		    pci_name(ha->pdev));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x000e,
+		    "Cannot remap pcibase MMIO, aborting.\n");
 		pci_release_regions(ha->pdev);
 		goto iospace_error_exit;
 	}
@@ -1667,9 +1682,8 @@
 		    (unsigned long)ioremap((pci_resource_start(ha->pdev, 4) +
 		    (ha->pdev->devfn << 12)), 4);
 		if (!ha->nxdb_wr_ptr) {
-			qla_printk(KERN_ERR, ha,
-			    "cannot remap MMIO (%s), aborting\n",
-			    pci_name(ha->pdev));
+			ql_log_pci(ql_log_fatal, ha->pdev, 0x000f,
+			    "Cannot remap MMIO, aborting.\n");
 			pci_release_regions(ha->pdev);
 			goto iospace_error_exit;
 		}
@@ -1687,6 +1701,16 @@
 
 	ha->max_req_queues = ha->max_rsp_queues = 1;
 	ha->msix_count = ha->max_rsp_queues + 1;
+	ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc006,
+	    "nx_pci_base=%p iobase=%p "
+	    "max_req_queues=%d msix_count=%d.\n",
+	    ha->nx_pcibase, ha->iobase,
+	    ha->max_req_queues, ha->msix_count);
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0010,
+	    "nx_pci_base=%p iobase=%p "
+	    "max_req_queues=%d msix_count=%d.\n",
+	    ha->nx_pcibase, ha->iobase,
+	    ha->max_req_queues, ha->msix_count);
 	return 0;
 
 iospace_error_exit:
@@ -1712,6 +1736,9 @@
 	pci_set_master(ha->pdev);
 	ret = pci_set_mwi(ha->pdev);
 	ha->chip_revision = ha->pdev->revision;
+	ql_dbg(ql_dbg_init, vha, 0x0043,
+	    "Chip revision:%ld.\n",
+	    ha->chip_revision);
 	return 0;
 }
 
@@ -1877,6 +1904,7 @@
 {
 	u32 val = 0;
 	int retries = 60;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	do {
 		read_lock(&ha->hw_lock);
@@ -1892,15 +1920,15 @@
 		default:
 			break;
 		}
-		qla_printk(KERN_WARNING, ha,
-			"CRB_CMDPEG_STATE: 0x%x and retries: 0x%x\n",
-			val, retries);
+		ql_log(ql_log_info, vha, 0x00a8,
+		    "CRB_CMDPEG_STATE: 0x%x and retries:0x%x.\n",
+		    val, retries);
 
 		msleep(500);
 
 	} while (--retries);
 
-	qla_printk(KERN_INFO, ha,
+	ql_log(ql_log_fatal, vha, 0x00a9,
 	    "Cmd Peg initialization failed: 0x%x.\n", val);
 
 	val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_PEGTUNE_DONE);
@@ -1915,6 +1943,7 @@
 {
 	u32 val = 0;
 	int retries = 60;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	do {
 		read_lock(&ha->hw_lock);
@@ -1930,17 +1959,16 @@
 		default:
 			break;
 		}
-
-		qla_printk(KERN_WARNING, ha,
-			"CRB_RCVPEG_STATE: 0x%x and retries: 0x%x\n",
-			val, retries);
+		ql_log(ql_log_info, vha, 0x00ab,
+		    "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x.\n",
+		    val, retries);
 
 		msleep(500);
 
 	} while (--retries);
 
-	qla_printk(KERN_INFO, ha,
-		"Rcv Peg initialization failed: 0x%x.\n", val);
+	ql_log(ql_log_fatal, vha, 0x00ac,
+	    "Rcv Peg initializatin failed: 0x%x.\n", val);
 	read_lock(&ha->hw_lock);
 	qla82xx_wr_32(ha, CRB_RCVPEG_STATE, PHAN_INITIALIZE_FAILED);
 	read_unlock(&ha->hw_lock);
@@ -1989,13 +2017,11 @@
 	}
 
 	if (ha->mcp) {
-		DEBUG3_11(printk(KERN_INFO "%s(%ld): "
-			"Got mailbox completion. cmd=%x.\n",
-			__func__, vha->host_no, ha->mcp->mb[0]));
+		ql_dbg(ql_dbg_async, vha, 0x5052,
+		    "Got mailbox completion. cmd=%x.\n", ha->mcp->mb[0]);
 	} else {
-		qla_printk(KERN_INFO, ha,
-			"%s(%ld): MBX pointer ERROR!\n",
-			__func__, vha->host_no);
+		ql_dbg(ql_dbg_async, vha, 0x5053,
+		    "MBX pointer ERROR.\n");
 	}
 }
 
@@ -2019,13 +2045,13 @@
 	int status = 0, status1 = 0;
 	unsigned long	flags;
 	unsigned long	iter;
-	uint32_t	stat;
+	uint32_t	stat = 0;
 	uint16_t	mb[4];
 
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-			"%s(): NULL response queue pointer\n", __func__);
+			"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 	ha = rsp->hw;
@@ -2075,9 +2101,9 @@
 				qla24xx_process_response_queue(vha, rsp);
 				break;
 			default:
-				DEBUG2(printk("scsi(%ld): "
-					" Unrecognized interrupt type (%d).\n",
-					vha->host_no, stat & 0xff));
+				ql_dbg(ql_dbg_async, vha, 0x5054,
+				    "Unrecognized interrupt type (%d).\n",
+				    stat & 0xff);
 				break;
 			}
 		}
@@ -2089,8 +2115,8 @@
 
 #ifdef QL_DEBUG_LEVEL_17
 	if (!irq && ha->flags.eeh_busy)
-		qla_printk(KERN_WARNING, ha,
-		    "isr: status %x, cmd_flags %lx, mbox_int %x, stat %x\n",
+		ql_log(ql_log_warn, vha, 0x503d,
+		    "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n",
 		    status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat);
 #endif
 
@@ -2111,13 +2137,13 @@
 	struct device_reg_82xx __iomem *reg;
 	int status = 0;
 	unsigned long flags;
-	uint32_t stat;
+	uint32_t stat = 0;
 	uint16_t mb[4];
 
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-			"%s(): NULL response queue pointer\n", __func__);
+			"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 	ha = rsp->hw;
@@ -2149,9 +2175,9 @@
 				qla24xx_process_response_queue(vha, rsp);
 				break;
 			default:
-				DEBUG2(printk("scsi(%ld): "
-					" Unrecognized interrupt type (%d).\n",
-					vha->host_no, stat & 0xff));
+				ql_dbg(ql_dbg_async, vha, 0x5041,
+				    "Unrecognized interrupt type (%d).\n",
+				    stat & 0xff);
 				break;
 			}
 		}
@@ -2162,9 +2188,9 @@
 
 #ifdef QL_DEBUG_LEVEL_17
 	if (!irq && ha->flags.eeh_busy)
-		qla_printk(KERN_WARNING, ha,
-			"isr: status %x, cmd_flags %lx, mbox_int %x, stat %x\n",
-			status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat);
+		ql_log(ql_log_warn, vha, 0x5044,
+		    "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n",
+		    status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat);
 #endif
 
 	if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
@@ -2186,7 +2212,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-			"%s(): NULL response queue pointer\n", __func__);
+			"%s(): NULL response queue pointer.\n", __func__);
 		return IRQ_NONE;
 	}
 
@@ -2215,7 +2241,7 @@
 	rsp = (struct rsp_que *) dev_id;
 	if (!rsp) {
 		printk(KERN_INFO
-			"%s(): NULL response queue pointer\n", __func__);
+			"%s(): NULL response queue pointer.\n", __func__);
 		return;
 	}
 	ha = rsp->hw;
@@ -2245,9 +2271,9 @@
 			qla24xx_process_response_queue(vha, rsp);
 			break;
 		default:
-			DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
-				"(%d).\n",
-				vha->host_no, stat & 0xff));
+			ql_dbg(ql_dbg_p3p, vha, 0xb013,
+			    "Unrecognized interrupt type (%d).\n",
+			    stat * 0xff);
 			break;
 		}
 	}
@@ -2347,9 +2373,8 @@
 		drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
 	}
 	drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
-	qla_printk(KERN_INFO, ha,
-		"%s(%ld):drv_state = 0x%x\n",
-		__func__, vha->host_no, drv_state);
+	ql_log(ql_log_info, vha, 0x00bb,
+	    "drv_state = 0x%x.\n", drv_state);
 	qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
 }
 
@@ -2392,8 +2417,8 @@
 	struct qla_hw_data *ha = vha->hw;
 
 	if (qla82xx_pinit_from_rom(vha) != QLA_SUCCESS) {
-		qla_printk(KERN_ERR, ha,
-			"%s: Error during CRB Initialization\n", __func__);
+		ql_log(ql_log_fatal, vha, 0x009f,
+		    "Error during CRB initialization.\n");
 		return QLA_FUNCTION_FAILED;
 	}
 	udelay(500);
@@ -2411,27 +2436,27 @@
 	if (ql2xfwloadbin == 2)
 		goto try_blob_fw;
 
-	qla_printk(KERN_INFO, ha,
-		"Attempting to load firmware from flash\n");
+	ql_log(ql_log_info, vha, 0x00a0,
+	    "Attempting to load firmware from flash.\n");
 
 	if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) {
-		qla_printk(KERN_ERR, ha,
-		    "Firmware loaded successfully from flash\n");
+		ql_log(ql_log_info, vha, 0x00a1,
+		    "Firmware loaded successully from flash.\n");
 		return QLA_SUCCESS;
 	} else {
-		qla_printk(KERN_ERR, ha,
-		    "Firmware load from flash failed\n");
+		ql_log(ql_log_warn, vha, 0x0108,
+		    "Firmware load from flash failed.\n");
 	}
 
 try_blob_fw:
-	qla_printk(KERN_INFO, ha,
-	    "Attempting to load firmware from blob\n");
+	ql_log(ql_log_info, vha, 0x00a2,
+	    "Attempting to load firmware from blob.\n");
 
 	/* Load firmware blob. */
 	blob = ha->hablob = qla2x00_request_firmware(vha);
 	if (!blob) {
-		qla_printk(KERN_ERR, ha,
-			"Firmware image not present.\n");
+		ql_log(ql_log_fatal, vha, 0x00a3,
+		    "Firmware image not preset.\n");
 		goto fw_load_failed;
 	}
 
@@ -2441,20 +2466,19 @@
 		/* Fallback to URI format */
 		if (qla82xx_validate_firmware_blob(vha,
 			QLA82XX_UNIFIED_ROMIMAGE)) {
-			qla_printk(KERN_ERR, ha,
-				"No valid firmware image found!!!");
+			ql_log(ql_log_fatal, vha, 0x00a4,
+			    "No valid firmware image found.\n");
 			return QLA_FUNCTION_FAILED;
 		}
 	}
 
 	if (qla82xx_fw_load_from_blob(ha) == QLA_SUCCESS) {
-		qla_printk(KERN_ERR, ha,
-			"%s: Firmware loaded successfully "
-			" from binary blob\n", __func__);
+		ql_log(ql_log_info, vha, 0x00a5,
+		    "Firmware loaded successfully from binary blob.\n");
 		return QLA_SUCCESS;
 	} else {
-		qla_printk(KERN_ERR, ha,
-		    "Firmware load failed from binary blob\n");
+		ql_log(ql_log_fatal, vha, 0x00a6,
+		    "Firmware load failed for binary blob.\n");
 		blob->fw = NULL;
 		blob = NULL;
 		goto fw_load_failed;
@@ -2486,15 +2510,15 @@
 	qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS2, 0);
 
 	if (qla82xx_load_fw(vha) != QLA_SUCCESS) {
-		qla_printk(KERN_INFO, ha,
-			"%s: Error trying to start fw!\n", __func__);
+		ql_log(ql_log_fatal, vha, 0x00a7,
+		    "Error trying to start fw.\n");
 		return QLA_FUNCTION_FAILED;
 	}
 
 	/* Handshake with the card before we register the devices. */
 	if (qla82xx_check_cmdpeg_state(ha) != QLA_SUCCESS) {
-		qla_printk(KERN_INFO, ha,
-			"%s: Error during card handshake!\n", __func__);
+		ql_log(ql_log_fatal, vha, 0x00aa,
+		    "Error during card handshake.\n");
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -2663,8 +2687,11 @@
 	/* Send marker if required */
 	if (vha->marker_needed != 0) {
 		if (qla2x00_marker(vha, req,
-			rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS)
+			rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x300c,
+			    "qla2x00_marker failed for cmd=%p.\n", cmd);
 			return QLA_FUNCTION_FAILED;
+		}
 		vha->marker_needed = 0;
 	}
 
@@ -2701,8 +2728,13 @@
 		uint16_t i;
 
 		more_dsd_lists = qla82xx_calc_dsd_lists(tot_dsds);
-		if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN)
+		if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) {
+			ql_dbg(ql_dbg_io, vha, 0x300d,
+			    "Num of DSD list %d is than %d for cmd=%p.\n",
+			    more_dsd_lists + ha->gbl_dsd_inuse, NUM_DSD_CHAIN,
+			    cmd);
 			goto queuing_error;
+		}
 
 		if (more_dsd_lists <= ha->gbl_dsd_avail)
 			goto sufficient_dsds;
@@ -2711,13 +2743,20 @@
 
 		for (i = 0; i < more_dsd_lists; i++) {
 			dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
-			if (!dsd_ptr)
+			if (!dsd_ptr) {
+				ql_log(ql_log_fatal, vha, 0x300e,
+				    "Failed to allocate memory for dsd_dma "
+				    "for cmd=%p.\n", cmd);
 				goto queuing_error;
+			}
 
 			dsd_ptr->dsd_addr = dma_pool_alloc(ha->dl_dma_pool,
 				GFP_ATOMIC, &dsd_ptr->dsd_list_dma);
 			if (!dsd_ptr->dsd_addr) {
 				kfree(dsd_ptr);
+				ql_log(ql_log_fatal, vha, 0x300f,
+				    "Failed to allocate memory for dsd_addr "
+				    "for cmd=%p.\n", cmd);
 				goto queuing_error;
 			}
 			list_add_tail(&dsd_ptr->list, &ha->gbl_dsd_list);
@@ -2742,17 +2781,16 @@
 
 		ctx = sp->ctx = mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
 		if (!sp->ctx) {
-			DEBUG(printk(KERN_INFO
-				"%s(%ld): failed to allocate"
-				" ctx.\n", __func__, vha->host_no));
+			ql_log(ql_log_fatal, vha, 0x3010,
+			    "Failed to allocate ctx for cmd=%p.\n", cmd);
 			goto queuing_error;
 		}
 		memset(ctx, 0, sizeof(struct ct6_dsd));
 		ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool,
 			GFP_ATOMIC, &ctx->fcp_cmnd_dma);
 		if (!ctx->fcp_cmnd) {
-			DEBUG2_3(printk("%s(%ld): failed to allocate"
-				" fcp_cmnd.\n", __func__, vha->host_no));
+			ql_log(ql_log_fatal, vha, 0x3011,
+			    "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
 			goto queuing_error_fcp_cmnd;
 		}
 
@@ -2766,6 +2804,9 @@
 				/* SCSI command bigger than 16 bytes must be
 				 * multiple of 4
 				 */
+				ql_log(ql_log_warn, vha, 0x3012,
+				    "scsi cmd len %d not multiple of 4 "
+				    "for cmd=%p.\n", cmd->cmd_len, cmd);
 				goto queuing_error_fcp_cmnd;
 			}
 			ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4;
@@ -2845,7 +2886,7 @@
 		cmd_pkt->entry_status = (uint8_t) rsp->id;
 	} else {
 		struct cmd_type_7 *cmd_pkt;
-		req_cnt = qla24xx_calc_iocbs(tot_dsds);
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 		if (req->cnt < (req_cnt + 2)) {
 			cnt = (uint16_t)RD_REG_DWORD_RELAXED(
 			    &reg->req_q_out[0]);
@@ -2979,8 +3020,8 @@
 	/* Dword reads to flash. */
 	for (i = 0; i < length/4; i++, faddr += 4) {
 		if (qla82xx_rom_fast_read(ha, faddr, &val)) {
-			qla_printk(KERN_WARNING, ha,
-			    "Do ROM fast read failed\n");
+			ql_log(ql_log_warn, vha, 0x0106,
+			    "Do ROM fast read failed.\n");
 			goto done_read;
 		}
 		dwptr[i] = __constant_cpu_to_le32(val);
@@ -2994,10 +3035,12 @@
 {
 	int ret;
 	uint32_t val;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	ret = ql82xx_rom_lock_d(ha);
 	if (ret < 0) {
-		qla_printk(KERN_WARNING, ha, "ROM Lock failed\n");
+		ql_log(ql_log_warn, vha, 0xb014,
+		    "ROM Lock failed.\n");
 		return ret;
 	}
 
@@ -3013,7 +3056,8 @@
 	}
 
 	if (qla82xx_write_disable_flash(ha) != 0)
-		qla_printk(KERN_WARNING, ha, "Write disable failed\n");
+		ql_log(ql_log_warn, vha, 0xb015,
+		    "Write disable failed.\n");
 
 done_unprotect:
 	qla82xx_rom_unlock(ha);
@@ -3025,10 +3069,12 @@
 {
 	int ret;
 	uint32_t val;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	ret = ql82xx_rom_lock_d(ha);
 	if (ret < 0) {
-		qla_printk(KERN_WARNING, ha, "ROM Lock failed\n");
+		ql_log(ql_log_warn, vha, 0xb016,
+		    "ROM Lock failed.\n");
 		return ret;
 	}
 
@@ -3040,10 +3086,12 @@
 	/* LOCK all sectors */
 	ret = qla82xx_write_status_reg(ha, val);
 	if (ret < 0)
-		qla_printk(KERN_WARNING, ha, "Write status register failed\n");
+		ql_log(ql_log_warn, vha, 0xb017,
+		    "Write status register failed.\n");
 
 	if (qla82xx_write_disable_flash(ha) != 0)
-		qla_printk(KERN_WARNING, ha, "Write disable failed\n");
+		ql_log(ql_log_warn, vha, 0xb018,
+		    "Write disable failed.\n");
 done_protect:
 	qla82xx_rom_unlock(ha);
 	return ret;
@@ -3053,10 +3101,12 @@
 qla82xx_erase_sector(struct qla_hw_data *ha, int addr)
 {
 	int ret = 0;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	ret = ql82xx_rom_lock_d(ha);
 	if (ret < 0) {
-		qla_printk(KERN_WARNING, ha, "ROM Lock failed\n");
+		ql_log(ql_log_warn, vha, 0xb019,
+		    "ROM Lock failed.\n");
 		return ret;
 	}
 
@@ -3066,8 +3116,8 @@
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_SE);
 
 	if (qla82xx_wait_rom_done(ha)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Error waiting for rom done\n");
+		ql_log(ql_log_warn, vha, 0xb01a,
+		    "Error waiting for rom done.\n");
 		ret = -1;
 		goto done;
 	}
@@ -3110,10 +3160,10 @@
 		optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
 		    &optrom_dma, GFP_KERNEL);
 		if (!optrom) {
-			qla_printk(KERN_DEBUG, ha,
-				"Unable to allocate memory for optrom "
-				"burst write (%x KB).\n",
-				OPTROM_BURST_SIZE / 1024);
+			ql_log(ql_log_warn, vha, 0xb01b,
+			    "Unable to allocate memory "
+			    "for optron burst write (%x KB).\n",
+			    OPTROM_BURST_SIZE / 1024);
 		}
 	}
 
@@ -3122,8 +3172,8 @@
 
 	ret = qla82xx_unprotect_flash(ha);
 	if (ret) {
-		qla_printk(KERN_WARNING, ha,
-			"Unable to unprotect flash for update.\n");
+		ql_log(ql_log_warn, vha, 0xb01c,
+		    "Unable to unprotect flash for update.\n");
 		goto write_done;
 	}
 
@@ -3133,9 +3183,9 @@
 
 			ret = qla82xx_erase_sector(ha, faddr);
 			if (ret) {
-				DEBUG9(qla_printk(KERN_ERR, ha,
-				    "Unable to erase sector: "
-				    "address=%x.\n", faddr));
+				ql_log(ql_log_warn, vha, 0xb01d,
+				    "Unable to erase sector: address=%x.\n",
+				    faddr);
 				break;
 			}
 		}
@@ -3149,12 +3199,12 @@
 			    (ha->flash_data_off | faddr),
 			    OPTROM_BURST_DWORDS);
 			if (ret != QLA_SUCCESS) {
-				qla_printk(KERN_WARNING, ha,
+				ql_log(ql_log_warn, vha, 0xb01e,
 				    "Unable to burst-write optrom segment "
 				    "(%x/%x/%llx).\n", ret,
 				    (ha->flash_data_off | faddr),
 				    (unsigned long long)optrom_dma);
-				qla_printk(KERN_WARNING, ha,
+				ql_log(ql_log_warn, vha, 0xb01f,
 				    "Reverting to slow-write.\n");
 
 				dma_free_coherent(&ha->pdev->dev,
@@ -3171,16 +3221,16 @@
 		ret = qla82xx_write_flash_dword(ha, faddr,
 		    cpu_to_le32(*dwptr));
 		if (ret) {
-			DEBUG9(printk(KERN_DEBUG "%s(%ld) Unable to program"
-			    "flash address=%x data=%x.\n", __func__,
-			    ha->host_no, faddr, *dwptr));
+			ql_dbg(ql_dbg_p3p, vha, 0xb020,
+			    "Unable to program flash address=%x data=%x.\n",
+			    faddr, *dwptr);
 			break;
 		}
 	}
 
 	ret = qla82xx_protect_flash(ha);
 	if (ret)
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0xb021,
 		    "Unable to protect flash after update.\n");
 write_done:
 	if (optrom)
@@ -3244,9 +3294,12 @@
 
 void qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
 	if (qla82xx_rom_lock(ha))
 		/* Someone else is holding the lock. */
-		qla_printk(KERN_INFO, ha, "Resetting rom_lock\n");
+		ql_log(ql_log_info, vha, 0xb022,
+		    "Resetting rom_lock.\n");
 
 	/*
 	 * Either we got the lock, or someone
@@ -3313,7 +3366,8 @@
 
 dev_initialize:
 	/* set to DEV_INITIALIZING */
-	qla_printk(KERN_INFO, ha, "HW State: INITIALIZING\n");
+	ql_log(ql_log_info, vha, 0x009e,
+	    "HW State: INITIALIZING.\n");
 	qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING);
 
 	/* Driver that sets device state to initializating sets IDC version */
@@ -3324,14 +3378,16 @@
 	qla82xx_idc_lock(ha);
 
 	if (rval != QLA_SUCCESS) {
-		qla_printk(KERN_INFO, ha, "HW State: FAILED\n");
+		ql_log(ql_log_fatal, vha, 0x00ad,
+		    "HW State: FAILED.\n");
 		qla82xx_clear_drv_active(ha);
 		qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED);
 		return rval;
 	}
 
 dev_ready:
-	qla_printk(KERN_INFO, ha, "HW State: READY\n");
+	ql_log(ql_log_info, vha, 0x00ae,
+	    "HW State: READY.\n");
 	qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY);
 
 	return QLA_SUCCESS;
@@ -3376,15 +3432,15 @@
 			/* quiescence timeout, other functions didn't ack
 			 * changing the state to DEV_READY
 			 */
-			qla_printk(KERN_INFO, ha,
-			    "%s: QUIESCENT TIMEOUT\n", QLA2XXX_DRIVER_NAME);
-			qla_printk(KERN_INFO, ha,
-			    "DRV_ACTIVE:%d DRV_STATE:%d\n", drv_active,
-			    drv_state);
+			ql_log(ql_log_info, vha, 0xb023,
+			    "%s : QUIESCENT TIMEOUT.\n", QLA2XXX_DRIVER_NAME);
+			ql_log(ql_log_info, vha, 0xb024,
+			    "DRV_ACTIVE:%d DRV_STATE:%d.\n",
+			    drv_active, drv_state);
 			qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
-						QLA82XX_DEV_READY);
-			qla_printk(KERN_INFO, ha,
-			    "HW State: DEV_READY\n");
+			    QLA82XX_DEV_READY);
+			ql_log(ql_log_info, vha, 0xb025,
+			    "HW State: DEV_READY.\n");
 			qla82xx_idc_unlock(ha);
 			qla2x00_perform_loop_resync(vha);
 			qla82xx_idc_lock(ha);
@@ -3404,7 +3460,8 @@
 	dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
 	/* everyone acked so set the state to DEV_QUIESCENCE */
 	if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) {
-		qla_printk(KERN_INFO, ha, "HW State: DEV_QUIESCENT\n");
+		ql_log(ql_log_info, vha, 0xb026,
+		    "HW State: DEV_QUIESCENT.\n");
 		qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT);
 	}
 }
@@ -3441,7 +3498,8 @@
 	struct qla_hw_data *ha = vha->hw;
 
 	/* Disable the board */
-	qla_printk(KERN_INFO, ha, "Disabling the board\n");
+	ql_log(ql_log_fatal, vha, 0x00b8,
+	    "Disabling the board.\n");
 
 	qla82xx_idc_lock(ha);
 	qla82xx_clear_drv_active(ha);
@@ -3492,8 +3550,8 @@
 
 	while (drv_state != drv_active) {
 		if (time_after_eq(jiffies, reset_timeout)) {
-			qla_printk(KERN_INFO, ha,
-				"%s: RESET TIMEOUT!\n", QLA2XXX_DRIVER_NAME);
+			ql_log(ql_log_warn, vha, 0x00b5,
+			    "Reset timeout.\n");
 			break;
 		}
 		qla82xx_idc_unlock(ha);
@@ -3504,12 +3562,15 @@
 	}
 
 	dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-	qla_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state,
-		dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+	ql_log(ql_log_info, vha, 0x00b6,
+	    "Device state is 0x%x = %s.\n",
+	    dev_state,
+	    dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
 
 	/* Force to DEV_COLD unless someone else is starting a reset */
 	if (dev_state != QLA82XX_DEV_INITIALIZING) {
-		qla_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n");
+		ql_log(ql_log_info, vha, 0x00b7,
+		    "HW State: COLD/RE-INIT.\n");
 		qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
 	}
 }
@@ -3523,8 +3584,12 @@
 	fw_heartbeat_counter = qla82xx_rd_32(vha->hw,
 		QLA82XX_PEG_ALIVE_COUNTER);
 	/* all 0xff, assume AER/EEH in progress, ignore */
-	if (fw_heartbeat_counter == 0xffffffff)
+	if (fw_heartbeat_counter == 0xffffffff) {
+		ql_dbg(ql_dbg_timer, vha, 0x6003,
+		    "FW heartbeat counter is 0xffffffff, "
+		    "returning status=%d.\n", status);
 		return status;
+	}
 	if (vha->fw_heartbeat_counter == fw_heartbeat_counter) {
 		vha->seconds_since_last_heartbeat++;
 		/* FW not alive after 2 seconds */
@@ -3535,6 +3600,9 @@
 	} else
 		vha->seconds_since_last_heartbeat = 0;
 	vha->fw_heartbeat_counter = fw_heartbeat_counter;
+	if (status)
+		ql_dbg(ql_dbg_timer, vha, 0x6004,
+		    "Returning status=%d.\n", status);
 	return status;
 }
 
@@ -3565,8 +3633,10 @@
 
 	dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
 	old_dev_state = dev_state;
-	qla_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state,
-		dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+	ql_log(ql_log_info, vha, 0x009b,
+	    "Device state is 0x%x = %s.\n",
+	    dev_state,
+	    dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
 
 	/* wait for 30 seconds for device to go ready */
 	dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
@@ -3574,9 +3644,8 @@
 	while (1) {
 
 		if (time_after_eq(jiffies, dev_init_timeout)) {
-			DEBUG(qla_printk(KERN_INFO, ha,
-				"%s: device init failed!\n",
-				QLA2XXX_DRIVER_NAME));
+			ql_log(ql_log_fatal, vha, 0x009c,
+			    "Device init failed.\n");
 			rval = QLA_FUNCTION_FAILED;
 			break;
 		}
@@ -3586,10 +3655,11 @@
 			old_dev_state = dev_state;
 		}
 		if (loopcount < 5) {
-			qla_printk(KERN_INFO, ha,
-			    "2:Device state is 0x%x = %s\n", dev_state,
-			    dev_state < MAX_STATES ?
-			    qdev_state[dev_state] : "Unknown");
+			ql_log(ql_log_info, vha, 0x009d,
+			    "Device state is 0x%x = %s.\n",
+			    dev_state,
+			    dev_state < MAX_STATES ? qdev_state[dev_state] :
+			    "Unknown");
 		}
 
 		switch (dev_state) {
@@ -3656,29 +3726,26 @@
 		dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
 		if (dev_state == QLA82XX_DEV_NEED_RESET &&
 		    !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
-			qla_printk(KERN_WARNING, ha,
-			    "scsi(%ld) %s: Adapter reset needed!\n",
-				vha->host_no, __func__);
+			ql_log(ql_log_warn, vha, 0x6001,
+			    "Adapter reset needed.\n");
 			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 			qla2xxx_wake_dpc(vha);
 		} else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
 			!test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
-			DEBUG(qla_printk(KERN_INFO, ha,
-				"scsi(%ld) %s - detected quiescence needed\n",
-				vha->host_no, __func__));
+			ql_log(ql_log_warn, vha, 0x6002,
+			    "Quiescent needed.\n");
 			set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
 			qla2xxx_wake_dpc(vha);
 		} else {
 			if (qla82xx_check_fw_alive(vha)) {
 				halt_status = qla82xx_rd_32(ha,
 				    QLA82XX_PEG_HALT_STATUS1);
-				qla_printk(KERN_INFO, ha,
-				    "scsi(%ld): %s, Dumping hw/fw registers:\n "
-				    " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n "
-				    " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n "
-				    " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n "
-				    " PEG_NET_4_PC: 0x%x\n",
-				    vha->host_no, __func__, halt_status,
+				ql_dbg(ql_dbg_timer, vha, 0x6005,
+				    "dumping hw/fw registers:.\n "
+				    " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,.\n "
+				    " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,.\n "
+				    " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,.\n "
+				    " PEG_NET_4_PC: 0x%x.\n", halt_status,
 				    qla82xx_rd_32(ha, QLA82XX_PEG_HALT_STATUS2),
 				    qla82xx_rd_32(ha,
 					    QLA82XX_CRB_PEG_NET_0 + 0x3c),
@@ -3694,9 +3761,8 @@
 					set_bit(ISP_UNRECOVERABLE,
 					    &vha->dpc_flags);
 				} else {
-					qla_printk(KERN_INFO, ha,
-					    "scsi(%ld): %s - detect abort needed\n",
-					    vha->host_no, __func__);
+					ql_log(ql_log_info, vha, 0x6006,
+					    "Detect abort  needed.\n");
 					set_bit(ISP_ABORT_NEEDED,
 					    &vha->dpc_flags);
 				}
@@ -3704,10 +3770,10 @@
 				ha->flags.isp82xx_fw_hung = 1;
 				if (ha->flags.mbox_busy) {
 					ha->flags.mbox_int = 1;
-					DEBUG2(qla_printk(KERN_ERR, ha,
-					    "scsi(%ld) Due to fw hung, doing "
+					ql_log(ql_log_warn, vha, 0x6007,
+					    "Due to FW hung, doing "
 					    "premature completion of mbx "
-					    "command\n", vha->host_no));
+					    "command.\n");
 					if (test_bit(MBX_INTR_WAIT,
 					    &ha->mbx_cmd_flags))
 						complete(&ha->mbx_intr_comp);
@@ -3742,9 +3808,8 @@
 	uint32_t dev_state;
 
 	if (vha->device_flags & DFLG_DEV_FAILED) {
-		qla_printk(KERN_WARNING, ha,
-			"%s(%ld): Device in failed state, "
-			"Exiting.\n", __func__, vha->host_no);
+		ql_log(ql_log_warn, vha, 0x8024,
+		    "Device in failed state, exiting.\n");
 		return QLA_SUCCESS;
 	}
 	ha->flags.isp82xx_reset_hdlr_active = 1;
@@ -3752,13 +3817,14 @@
 	qla82xx_idc_lock(ha);
 	dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
 	if (dev_state == QLA82XX_DEV_READY) {
-		qla_printk(KERN_INFO, ha, "HW State: NEED RESET\n");
+		ql_log(ql_log_info, vha, 0x8025,
+		    "HW State: NEED RESET.\n");
 		qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
 			QLA82XX_DEV_NEED_RESET);
 	} else
-		qla_printk(KERN_INFO, ha, "HW State: %s\n",
-			dev_state < MAX_STATES ?
-			qdev_state[dev_state] : "Unknown");
+		ql_log(ql_log_info, vha, 0x8026,
+		    "Hw State: %s.\n", dev_state < MAX_STATES ?
+		    qdev_state[dev_state] : "Unknown");
 	qla82xx_idc_unlock(ha);
 
 	rval = qla82xx_device_state_handler(vha);
@@ -3777,9 +3843,9 @@
 		vha->flags.online = 1;
 		if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
 			if (ha->isp_abort_cnt == 0) {
-				qla_printk(KERN_WARNING, ha,
-				    "ISP error recovery failed - "
-				    "board disabled\n");
+				ql_log(ql_log_warn, vha, 0x8027,
+				    "ISP error recover failed - board "
+				    "disabled.\n");
 				/*
 				 * The next call disables the board
 				 * completely.
@@ -3791,16 +3857,16 @@
 				rval = QLA_SUCCESS;
 			} else { /* schedule another ISP abort */
 				ha->isp_abort_cnt--;
-				DEBUG(qla_printk(KERN_INFO, ha,
-				    "qla%ld: ISP abort - retry remaining %d\n",
-				    vha->host_no, ha->isp_abort_cnt));
+				ql_log(ql_log_warn, vha, 0x8036,
+				    "ISP abort - retry remaining %d.\n",
+				    ha->isp_abort_cnt);
 				rval = QLA_FUNCTION_FAILED;
 			}
 		} else {
 			ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
-			DEBUG(qla_printk(KERN_INFO, ha,
-			    "(%ld): ISP error recovery - retrying (%d) "
-			    "more times\n", vha->host_no, ha->isp_abort_cnt));
+			ql_dbg(ql_dbg_taskm, vha, 0x8029,
+			    "ISP error recovery - retrying (%d) more times.\n",
+			    ha->isp_abort_cnt);
 			set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
 			rval = QLA_FUNCTION_FAILED;
 		}
@@ -3872,8 +3938,8 @@
 			break;
 		}
 	}
-	DEBUG2(printk(KERN_INFO
-	    "%s status=%d\n", __func__, status));
+	ql_dbg(ql_dbg_p3p, vha, 0xb027,
+	    "%s status=%d.\n", status);
 
 	return status;
 }
@@ -3902,6 +3968,9 @@
 			}
 		}
 	}
+	ql_dbg(ql_dbg_init, vha, 0x00b0,
+	    "Entered %s fw_hung=%d.\n",
+	    __func__, ha->flags.isp82xx_fw_hung);
 
 	/* Abort all commands gracefully if fw NOT hung */
 	if (!ha->flags.isp82xx_fw_hung) {
@@ -3922,13 +3991,13 @@
 						spin_unlock_irqrestore(
 						    &ha->hardware_lock, flags);
 						if (ha->isp_ops->abort_command(sp)) {
-							qla_printk(KERN_INFO, ha,
-							    "scsi(%ld): mbx abort command failed in %s\n",
-							    vha->host_no, __func__);
+							ql_log(ql_log_info, vha,
+							    0x00b1,
+							    "mbx abort failed.\n");
 						} else {
-							qla_printk(KERN_INFO, ha,
-							    "scsi(%ld): mbx abort command success in %s\n",
-							    vha->host_no, __func__);
+							ql_log(ql_log_info, vha,
+							    0x00b2,
+							    "mbx abort success.\n");
 						}
 						spin_lock_irqsave(&ha->hardware_lock, flags);
 					}
@@ -3940,8 +4009,9 @@
 		/* Wait for pending cmds (physical and virtual) to complete */
 		if (!qla2x00_eh_wait_for_pending_commands(vha, 0, 0,
 		    WAIT_HOST) == QLA_SUCCESS) {
-			DEBUG2(qla_printk(KERN_INFO, ha,
-			    "Done wait for pending commands\n"));
+			ql_dbg(ql_dbg_init, vha, 0x00b3,
+			    "Done wait for "
+			    "pending commands.\n");
 		}
 	}
 }
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index f461925..e02df27 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -35,6 +35,10 @@
  * CT6 CTX allocation cache
  */
 static struct kmem_cache *ctx_cachep;
+/*
+ * error level for logging
+ */
+int ql_errlev = ql_log_all;
 
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO);
@@ -69,8 +73,17 @@
 int ql2xextended_error_logging;
 module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(ql2xextended_error_logging,
-		"Option to enable extended error logging, "
-		"Default is 0 - no logging. 1 - log errors.");
+		"Option to enable extended error logging,\n"
+		"\t\tDefault is 0 - no logging.  0x40000000 - Module Init & Probe.\n"
+		"\t\t0x20000000 - Mailbox Cmnds. 0x10000000 - Device Discovery.\n"
+		"\t\t0x08000000 - IO tracing.    0x04000000 - DPC Thread.\n"
+		"\t\t0x02000000 - Async events.  0x01000000 - Timer routines.\n"
+		"\t\t0x00800000 - User space.    0x00400000 - Task Management.\n"
+		"\t\t0x00200000 - AER/EEH.       0x00100000 - Multi Q.\n"
+		"\t\t0x00080000 - P3P Specific.  0x00040000 - Virtual Port.\n"
+		"\t\t0x00020000 - Buffer Dump.   0x00010000 - Misc.\n"
+		"\t\t0x7fffffff - For enabling all logs, can be too many logs.\n"
+		"\t\tDo LOGICAL OR of the value to enable more than one level");
 
 int ql2xshiftctondsd = 6;
 module_param(ql2xshiftctondsd, int, S_IRUGO);
@@ -128,8 +141,8 @@
 int ql2xfwloadbin;
 module_param(ql2xfwloadbin, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xfwloadbin,
-		"Option to specify location from which to load ISP firmware:\n"
-		" 2 -- load firmware via the request_firmware() (hotplug)\n"
+		"Option to specify location from which to load ISP firmware:.\n"
+		" 2 -- load firmware via the request_firmware() (hotplug).\n"
 		"      interface.\n"
 		" 1 -- load firmware from flash.\n"
 		" 0 -- use default semantics.\n");
@@ -143,7 +156,7 @@
 int ql2xdbwr = 1;
 module_param(ql2xdbwr, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xdbwr,
-	"Option to specify scheme for request queue posting\n"
+	"Option to specify scheme for request queue posting.\n"
 	" 0 -- Regular doorbell.\n"
 	" 1 -- CAMRAM doorbell (faster).\n");
 
@@ -168,7 +181,7 @@
 int ql2xdontresethba;
 module_param(ql2xdontresethba, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xdontresethba,
-	"Option to specify reset behaviour\n"
+	"Option to specify reset behaviour.\n"
 	" 0 (Default) -- Reset on failure.\n"
 	" 1 -- Do not reset on failure.\n");
 
@@ -247,8 +260,11 @@
 qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
 {
 	/* Currently used for 82XX only. */
-	if (vha->device_flags & DFLG_DEV_FAILED)
+	if (vha->device_flags & DFLG_DEV_FAILED) {
+		ql_dbg(ql_dbg_timer, vha, 0x600d,
+		    "Device in a failed state, returning.\n");
 		return;
+	}
 
 	mod_timer(&vha->timer, jiffies + interval * HZ);
 }
@@ -273,19 +289,20 @@
 /* -------------------------------------------------------------------------- */
 static int qla2x00_alloc_queues(struct qla_hw_data *ha)
 {
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 	ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
 				GFP_KERNEL);
 	if (!ha->req_q_map) {
-		qla_printk(KERN_WARNING, ha,
-			"Unable to allocate memory for request queue ptrs\n");
+		ql_log(ql_log_fatal, vha, 0x003b,
+		    "Unable to allocate memory for request queue ptrs.\n");
 		goto fail_req_map;
 	}
 
 	ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
 				GFP_KERNEL);
 	if (!ha->rsp_q_map) {
-		qla_printk(KERN_WARNING, ha,
-			"Unable to allocate memory for response queue ptrs\n");
+		ql_log(ql_log_fatal, vha, 0x003c,
+		    "Unable to allocate memory for response queue ptrs.\n");
 		goto fail_rsp_map;
 	}
 	set_bit(0, ha->rsp_qid_map);
@@ -349,8 +366,8 @@
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!(ha->fw_attributes & BIT_6)) {
-		qla_printk(KERN_INFO, ha,
-			"Firmware is not multi-queue capable\n");
+		ql_log(ql_log_warn, vha, 0x00d8,
+		    "Firmware is not multi-queue capable.\n");
 		goto fail;
 	}
 	if (ql2xmultique_tag) {
@@ -359,8 +376,8 @@
 		req = qla25xx_create_req_que(ha, options, 0, 0, -1,
 			QLA_DEFAULT_QUE_QOS);
 		if (!req) {
-			qla_printk(KERN_WARNING, ha,
-				"Can't create request queue\n");
+			ql_log(ql_log_warn, vha, 0x00e0,
+			    "Failed to create request queue.\n");
 			goto fail;
 		}
 		ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 1);
@@ -369,17 +386,20 @@
 		for (ques = 1; ques < ha->max_rsp_queues; ques++) {
 			ret = qla25xx_create_rsp_que(ha, options, 0, 0, req);
 			if (!ret) {
-				qla_printk(KERN_WARNING, ha,
-					"Response Queue create failed\n");
+				ql_log(ql_log_warn, vha, 0x00e8,
+				    "Failed to create response queue.\n");
 				goto fail2;
 			}
 		}
 		ha->flags.cpu_affinity_enabled = 1;
-
-		DEBUG2(qla_printk(KERN_INFO, ha,
-			"CPU affinity mode enabled, no. of response"
-			" queues:%d, no. of request queues:%d\n",
-			ha->max_rsp_queues, ha->max_req_queues));
+		ql_dbg(ql_dbg_multiq, vha, 0xc007,
+		    "CPU affinity mode enalbed, "
+		    "no. of response queues:%d no. of request queues:%d.\n",
+		    ha->max_rsp_queues, ha->max_req_queues);
+		ql_dbg(ql_dbg_init, vha, 0x00e9,
+		    "CPU affinity mode enalbed, "
+		    "no. of response queues:%d no. of request queues:%d.\n",
+		    ha->max_rsp_queues, ha->max_req_queues);
 	}
 	return 0;
 fail2:
@@ -526,8 +546,11 @@
 	struct qla_hw_data *ha = vha->hw;
 
 	sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
-	if (!sp)
+	if (!sp) {
+		ql_log(ql_log_warn, vha, 0x3006,
+		    "Memory allocation failed for sp.\n");
 		return sp;
+	}
 
 	atomic_set(&sp->ref_count, 1);
 	sp->fcport = fcport;
@@ -551,30 +574,43 @@
 	int rval;
 
 	if (ha->flags.eeh_busy) {
-		if (ha->flags.pci_channel_io_perm_failure)
+		if (ha->flags.pci_channel_io_perm_failure) {
+			ql_dbg(ql_dbg_io, vha, 0x3001,
+			    "PCI Channel IO permanent failure, exiting "
+			    "cmd=%p.\n", cmd);
 			cmd->result = DID_NO_CONNECT << 16;
-		else
+		} else {
+			ql_dbg(ql_dbg_io, vha, 0x3002,
+			    "EEH_Busy, Requeuing the cmd=%p.\n", cmd);
 			cmd->result = DID_REQUEUE << 16;
+		}
 		goto qc24_fail_command;
 	}
 
 	rval = fc_remote_port_chkready(rport);
 	if (rval) {
 		cmd->result = rval;
+		ql_dbg(ql_dbg_io, vha, 0x3003,
+		    "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",
+		    cmd, rval);
 		goto qc24_fail_command;
 	}
 
 	if (!vha->flags.difdix_supported &&
 		scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
-			DEBUG2(qla_printk(KERN_ERR, ha,
-			    "DIF Cap Not Reg, fail DIF capable cmd's:%x\n",
-			    cmd->cmnd[0]));
+			ql_dbg(ql_dbg_io, vha, 0x3004,
+			    "DIF Cap not reg, fail DIF capable cmd's:%p.\n",
+			    cmd);
 			cmd->result = DID_NO_CONNECT << 16;
 			goto qc24_fail_command;
 	}
 	if (atomic_read(&fcport->state) != FCS_ONLINE) {
 		if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
 			atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
+			ql_dbg(ql_dbg_io, vha, 0x3005,
+			    "Returning DNC, fcport_state=%d loop_state=%d.\n",
+			    atomic_read(&fcport->state),
+			    atomic_read(&base_vha->loop_state));
 			cmd->result = DID_NO_CONNECT << 16;
 			goto qc24_fail_command;
 		}
@@ -586,8 +622,11 @@
 		goto qc24_host_busy;
 
 	rval = ha->isp_ops->start_scsi(sp);
-	if (rval != QLA_SUCCESS)
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_io, vha, 0x3013,
+		    "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
 		goto qc24_host_busy_free_sp;
+	}
 
 	return 0;
 
@@ -630,7 +669,8 @@
 	int ret = QLA_SUCCESS;
 
 	if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) {
-		DEBUG17(qla_printk(KERN_WARNING, ha, "return:eh_wait\n"));
+		ql_dbg(ql_dbg_taskm, vha, 0x8005,
+		    "Return:eh_wait.\n");
 		return ret;
 	}
 
@@ -723,7 +763,8 @@
 	else
 		return_status = QLA_FUNCTION_FAILED;
 
-	DEBUG2(printk("%s return_status=%d\n", __func__, return_status));
+	ql_dbg(ql_dbg_taskm, vha, 0x8019,
+	    "%s return status=%d.\n", __func__, return_status);
 
 	return return_status;
 }
@@ -831,10 +872,14 @@
 	int wait = 0;
 	struct qla_hw_data *ha = vha->hw;
 
+	ql_dbg(ql_dbg_taskm, vha, 0x8000,
+	    "Entered %s for cmd=%p.\n", __func__, cmd);
 	if (!CMD_SP(cmd))
 		return SUCCESS;
 
 	ret = fc_block_scsi_eh(cmd);
+	ql_dbg(ql_dbg_taskm, vha, 0x8001,
+	    "Return value of fc_block_scsi_eh=%d.\n", ret);
 	if (ret != 0)
 		return ret;
 	ret = SUCCESS;
@@ -849,20 +894,19 @@
 		return SUCCESS;
 	}
 
-	DEBUG2(printk("%s(%ld): aborting sp %p from RISC.",
-	    __func__, vha->host_no, sp));
+	ql_dbg(ql_dbg_taskm, vha, 0x8002,
+	    "Aborting sp=%p cmd=%p from RISC ", sp, cmd);
 
 	/* Get a reference to the sp and drop the lock.*/
 	sp_get(sp);
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 	if (ha->isp_ops->abort_command(sp)) {
-		DEBUG2(printk("%s(%ld): abort_command "
-		"mbx failed.\n", __func__, vha->host_no));
-		ret = FAILED;
+		ql_dbg(ql_dbg_taskm, vha, 0x8003,
+		    "Abort command mbx failed for cmd=%p.\n", cmd);
 	} else {
-		DEBUG3(printk("%s(%ld): abort_command "
-		"mbx success.\n", __func__, vha->host_no));
+		ql_dbg(ql_dbg_taskm, vha, 0x8004,
+		    "Abort command mbx success.\n");
 		wait = 1;
 	}
 	qla2x00_sp_compl(ha, sp);
@@ -870,16 +914,14 @@
 	/* Wait for the command to be returned. */
 	if (wait) {
 		if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
-			qla_printk(KERN_ERR, ha,
-			    "scsi(%ld:%d:%d): Abort handler timed out -- %x.\n",
-			    vha->host_no, id, lun, ret);
+			ql_log(ql_log_warn, vha, 0x8006,
+			    "Abort handler timed out for cmd=%p.\n", cmd);
 			ret = FAILED;
 		}
 	}
 
-	qla_printk(KERN_INFO, ha,
-	    "scsi(%ld:%d:%d): Abort command issued -- %d %x.\n",
-	    vha->host_no, id, lun, wait, ret);
+	ql_log(ql_log_info, vha, 0x801c,
+	    "Abort command issued --  %d %x.\n", wait, ret);
 
 	return ret;
 }
@@ -947,40 +989,59 @@
 	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
 	int err;
 
-	if (!fcport)
+	if (!fcport) {
+		ql_log(ql_log_warn, vha, 0x8007,
+		    "fcport is NULL.\n");
 		return FAILED;
+	}
 
 	err = fc_block_scsi_eh(cmd);
+	ql_dbg(ql_dbg_taskm, vha, 0x8008,
+	    "fc_block_scsi_eh ret=%d.\n", err);
 	if (err != 0)
 		return err;
 
-	qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
-	    vha->host_no, cmd->device->id, cmd->device->lun, name);
+	ql_log(ql_log_info, vha, 0x8009,
+	    "%s RESET ISSUED for id %d lun %d cmd=%p.\n", name,
+	    cmd->device->id, cmd->device->lun, cmd);
 
 	err = 0;
-	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
+	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800a,
+		    "Wait for hba online failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
+	}
 	err = 1;
-	if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
+	if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800b,
+		    "Wait for loop ready failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
+	}
 	err = 2;
 	if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1)
-		!= QLA_SUCCESS)
+		!= QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800c,
+		    "do_reset failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
+	}
 	err = 3;
 	if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
-	    cmd->device->lun, type) != QLA_SUCCESS)
+	    cmd->device->lun, type) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800d,
+		    "wait for peding cmds failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
+	}
 
-	qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
-	    vha->host_no, cmd->device->id, cmd->device->lun, name);
+	ql_log(ql_log_info, vha, 0x800e,
+	    "%s RESET SUCCEEDED for id %d lun %d cmd=%p.\n", name,
+	    cmd->device->id, cmd->device->lun, cmd);
 
 	return SUCCESS;
 
 eh_reset_failed:
-	qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n"
-	    , vha->host_no, cmd->device->id, cmd->device->lun, name,
-	    reset_errors[err]);
+	ql_log(ql_log_info, vha, 0x800f,
+	    "%s RESET FAILED: %s for id %d lun %d cmd=%p.\n", name,
+	    reset_errors[err], cmd->device->id, cmd->device->lun);
 	return FAILED;
 }
 
@@ -1030,19 +1091,25 @@
 	id = cmd->device->id;
 	lun = cmd->device->lun;
 
-	if (!fcport)
+	if (!fcport) {
+		ql_log(ql_log_warn, vha, 0x8010,
+		    "fcport is NULL.\n");
 		return ret;
+	}
 
 	ret = fc_block_scsi_eh(cmd);
+	ql_dbg(ql_dbg_taskm, vha, 0x8011,
+	    "fc_block_scsi_eh ret=%d.\n", ret);
 	if (ret != 0)
 		return ret;
 	ret = FAILED;
 
-	qla_printk(KERN_INFO, vha->hw,
-	    "scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun);
+	ql_log(ql_log_info, vha, 0x8012,
+	    "BUS RESET ISSUED for id %d lun %d.\n", id, lun);
 
 	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
-		DEBUG2(printk("%s failed:board disabled\n",__func__));
+		ql_log(ql_log_fatal, vha, 0x8013,
+		    "Wait for hba online failed board disabled.\n");
 		goto eh_bus_reset_done;
 	}
 
@@ -1055,12 +1122,15 @@
 
 	/* Flush outstanding commands. */
 	if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) !=
-	    QLA_SUCCESS)
+	    QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x8014,
+		    "Wait for pending commands failed.\n");
 		ret = FAILED;
+	}
 
 eh_bus_reset_done:
-	qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__,
-	    (ret == FAILED) ? "failed" : "succeeded");
+	ql_log(ql_log_warn, vha, 0x802b,
+	    "BUS RESET %s.\n", (ret == FAILED) ? "FAILED" : "SUCCEDED");
 
 	return ret;
 }
@@ -1093,16 +1163,21 @@
 	id = cmd->device->id;
 	lun = cmd->device->lun;
 
-	if (!fcport)
+	if (!fcport) {
+		ql_log(ql_log_warn, vha, 0x8016,
+		    "fcport is NULL.\n");
 		return ret;
+	}
 
 	ret = fc_block_scsi_eh(cmd);
+	ql_dbg(ql_dbg_taskm, vha, 0x8017,
+	    "fc_block_scsi_eh ret=%d.\n", ret);
 	if (ret != 0)
 		return ret;
 	ret = FAILED;
 
-	qla_printk(KERN_INFO, ha,
-	    "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun);
+	ql_log(ql_log_info, vha, 0x8018,
+	    "ADAPTER RESET ISSUED for id %d lun %d.\n", id, lun);
 
 	if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS)
 		goto eh_host_reset_lock;
@@ -1137,8 +1212,11 @@
 			/* failed. schedule dpc to try */
 			set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
 
-			if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
+			if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+				ql_log(ql_log_warn, vha, 0x802a,
+				    "wait for hba online failed.\n");
 				goto eh_host_reset_lock;
+			}
 		}
 		clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
 	}
@@ -1149,7 +1227,7 @@
 		ret = SUCCESS;
 
 eh_host_reset_lock:
-	qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+	qla_printk(KERN_INFO, ha, "%s: reset %s.\n", __func__,
 	    (ret == FAILED) ? "failed" : "succeeded");
 
 	return ret;
@@ -1179,9 +1257,9 @@
 
 			ret = ha->isp_ops->target_reset(fcport, 0, 0);
 			if (ret != QLA_SUCCESS) {
-				DEBUG2_3(printk("%s(%ld): bus_reset failed: "
-				    "target_reset=%d d_id=%x.\n", __func__,
-				    vha->host_no, ret, fcport->d_id.b24));
+				ql_dbg(ql_dbg_taskm, vha, 0x802c,
+				    "Bus Reset failed: Target Reset=%d "
+				    "d_id=%x.\n", ret, fcport->d_id.b24);
 			}
 		}
 	}
@@ -1189,9 +1267,8 @@
 	if (ha->flags.enable_lip_full_login && !IS_QLA8XXX_TYPE(ha)) {
 		ret = qla2x00_full_login_lip(vha);
 		if (ret != QLA_SUCCESS) {
-			DEBUG2_3(printk("%s(%ld): failed: "
-			    "full_login_lip=%d.\n", __func__, vha->host_no,
-			    ret));
+			ql_dbg(ql_dbg_taskm, vha, 0x802d,
+			    "full_login_lip=%d.\n", ret);
 		}
 		atomic_set(&vha->loop_state, LOOP_DOWN);
 		atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
@@ -1202,8 +1279,8 @@
 	if (ha->flags.enable_lip_reset) {
 		ret = qla2x00_lip_reset(vha);
 		if (ret != QLA_SUCCESS) {
-			DEBUG2_3(printk("%s(%ld): failed: "
-			    "lip_reset=%d.\n", __func__, vha->host_no, ret));
+			ql_dbg(ql_dbg_taskm, vha, 0x802e,
+			    "lip_reset failed (%d).\n", ret);
 		} else
 			qla2x00_wait_for_loop_ready(vha);
 	}
@@ -1302,17 +1379,17 @@
 	if (!scsi_track_queue_full(sdev, qdepth))
 		return;
 
-	DEBUG2(qla_printk(KERN_INFO, fcport->vha->hw,
-		"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
-		fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
-		sdev->queue_depth));
+	ql_dbg(ql_dbg_io, fcport->vha, 0x3029,
+	    "Queue depth adjusted-down "
+	    "to %d for scsi(%ld:%d:%d:%d).\n",
+	    sdev->queue_depth, fcport->vha->host_no,
+	    sdev->channel, sdev->id, sdev->lun);
 }
 
 static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
 {
 	fc_port_t *fcport = sdev->hostdata;
 	struct scsi_qla_host *vha = fcport->vha;
-	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = NULL;
 
 	req = vha->req;
@@ -1327,10 +1404,11 @@
 	else
 		scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
 
-	DEBUG2(qla_printk(KERN_INFO, ha,
-	       "scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
-	       fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
-	       sdev->queue_depth));
+	ql_dbg(ql_dbg_io, vha, 0x302a,
+	    "Queue depth adjusted-up to %d for "
+	    "scsi(%ld:%d:%d:%d).\n",
+	    sdev->queue_depth, fcport->vha->host_no,
+	    sdev->channel, sdev->id, sdev->lun);
 }
 
 static int
@@ -1776,6 +1854,9 @@
 		ha->flags.port0 = 1;
 	else
 		ha->flags.port0 = 0;
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b,
+	    "device_type=0x%x port=%d fw_srisc_address=%p.\n",
+	    ha->device_type, ha->flags.port0, ha->fw_srisc_address);
 }
 
 static int
@@ -1790,10 +1871,9 @@
 
 	if (pci_request_selected_regions(ha->pdev, ha->bars,
 	    QLA2XXX_DRIVER_NAME)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Failed to reserve PIO/MMIO regions (%s)\n",
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0011,
+		    "Failed to reserve PIO/MMIO regions (%s), aborting.\n",
 		    pci_name(ha->pdev));
-
 		goto iospace_error_exit;
 	}
 	if (!(ha->bars & 1))
@@ -1803,39 +1883,42 @@
 	pio = pci_resource_start(ha->pdev, 0);
 	if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) {
 		if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
-			qla_printk(KERN_WARNING, ha,
-			    "Invalid PCI I/O region size (%s)...\n",
-				pci_name(ha->pdev));
+			ql_log_pci(ql_log_warn, ha->pdev, 0x0012,
+			    "Invalid pci I/O region size (%s).\n",
+			    pci_name(ha->pdev));
 			pio = 0;
 		}
 	} else {
-		qla_printk(KERN_WARNING, ha,
-		    "region #0 not a PIO resource (%s)...\n",
+		ql_log_pci(ql_log_warn, ha->pdev, 0x0013,
+		    "Region #0 no a PIO resource (%s).\n",
 		    pci_name(ha->pdev));
 		pio = 0;
 	}
 	ha->pio_address = pio;
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0014,
+	    "PIO address=%p.\n",
+	    ha->pio_address);
 
 skip_pio:
 	/* Use MMIO operations for all accesses. */
 	if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) {
-		qla_printk(KERN_ERR, ha,
-		    "region #1 not an MMIO resource (%s), aborting\n",
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0015,
+		    "Region #1 not an MMIO resource (%s), aborting.\n",
 		    pci_name(ha->pdev));
 		goto iospace_error_exit;
 	}
 	if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) {
-		qla_printk(KERN_ERR, ha,
-		    "Invalid PCI mem region size (%s), aborting\n",
-			pci_name(ha->pdev));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0016,
+		    "Invalid PCI mem region size (%s), aborting.\n",
+		    pci_name(ha->pdev));
 		goto iospace_error_exit;
 	}
 
 	ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN);
 	if (!ha->iobase) {
-		qla_printk(KERN_ERR, ha,
-		    "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
-
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0017,
+		    "Cannot remap MMIO (%s), aborting.\n",
+		    pci_name(ha->pdev));
 		goto iospace_error_exit;
 	}
 
@@ -1849,6 +1932,8 @@
 	ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
 			pci_resource_len(ha->pdev, 3));
 	if (ha->mqiobase) {
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0018,
+		    "MQIO Base=%p.\n", ha->mqiobase);
 		/* Read MSIX vector size of the board */
 		pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
 		ha->msix_count = msix;
@@ -1861,17 +1946,24 @@
 			ha->max_req_queues = 2;
 		} else if (ql2xmaxqueues > 1) {
 			ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
-						QLA_MQ_SIZE : ql2xmaxqueues;
-			DEBUG2(qla_printk(KERN_INFO, ha, "QoS mode set, max no"
-			" of request queues:%d\n", ha->max_req_queues));
+			    QLA_MQ_SIZE : ql2xmaxqueues;
+			ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc008,
+			    "QoS mode set, max no of request queues:%d.\n",
+			    ha->max_req_queues);
+			ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0019,
+			    "QoS mode set, max no of request queues:%d.\n",
+			    ha->max_req_queues);
 		}
-		qla_printk(KERN_INFO, ha,
-			"MSI-X vector count: %d\n", msix);
+		ql_log_pci(ql_log_info, ha->pdev, 0x001a,
+		    "MSI-X vector count: %d.\n", msix);
 	} else
-		qla_printk(KERN_INFO, ha, "BAR 3 not enabled\n");
+		ql_log_pci(ql_log_info, ha->pdev, 0x001b,
+		    "BAR 3 not enabled.\n");
 
 mqiobase_exit:
 	ha->msix_count = ha->max_rsp_queues + 1;
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001c,
+	    "MSIX Count:%d.\n", ha->msix_count);
 	return (0);
 
 iospace_error_exit:
@@ -1935,7 +2027,11 @@
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021) {
 		bars = pci_select_bars(pdev, IORESOURCE_MEM);
 		mem_only = 1;
+		ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
+		    "Mem only adapter.\n");
 	}
+	ql_dbg_pci(ql_dbg_init, pdev, 0x0008,
+	    "Bars=%d.\n", bars);
 
 	if (mem_only) {
 		if (pci_enable_device_mem(pdev))
@@ -1950,9 +2046,12 @@
 
 	ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
 	if (!ha) {
-		DEBUG(printk("Unable to allocate memory for ha\n"));
+		ql_log_pci(ql_log_fatal, pdev, 0x0009,
+		    "Unable to allocate memory for ha.\n");
 		goto probe_out;
 	}
+	ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
+	    "Memory allocated for ha=%p.\n", ha);
 	ha->pdev = pdev;
 
 	/* Clear our data area */
@@ -1974,10 +2073,9 @@
 	if (ret)
 		goto probe_hw_failed;
 
-	qla_printk(KERN_INFO, ha,
-	    "Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
-	    ha->iobase);
-
+	ql_log_pci(ql_log_info, pdev, 0x001d,
+	    "Found an ISP%04X irq %d iobase 0x%p.\n",
+	    pdev->device, pdev->irq, ha->iobase);
 	ha->prev_topology = 0;
 	ha->init_cb_size = sizeof(init_cb_t);
 	ha->link_data_rate = PORT_SPEED_UNKNOWN;
@@ -2078,7 +2176,18 @@
 		ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
 		ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
 	}
-
+	ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
+	    "mbx_count=%d, req_length=%d, "
+	    "rsp_length=%d, max_loop_id=%d, init_cb_size=%d, "
+	    "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, .\n",
+	    ha->mbx_count, req_length, rsp_length, ha->max_loop_id,
+	    ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size,
+	    ha->nvram_npiv_size);
+	ql_dbg_pci(ql_dbg_init, pdev, 0x001f,
+	    "isp_ops=%p, flash_conf_off=%d, "
+	    "flash_data_off=%d, nvram_conf_off=%d, nvram_data_off=%d.\n",
+	    ha->isp_ops, ha->flash_conf_off, ha->flash_data_off,
+	    ha->nvram_conf_off, ha->nvram_data_off);
 	mutex_init(&ha->vport_lock);
 	init_completion(&ha->mbx_cmd_comp);
 	complete(&ha->mbx_cmd_comp);
@@ -2088,10 +2197,14 @@
 	set_bit(0, (unsigned long *) ha->vp_idx_map);
 
 	qla2x00_config_dma_addressing(ha);
+	ql_dbg_pci(ql_dbg_init, pdev, 0x0020,
+	    "64 Bit addressing is %s.\n",
+	    ha->flags.enable_64bit_addressing ? "enable" :
+	    "disable");
 	ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
 	if (!ret) {
-		qla_printk(KERN_WARNING, ha,
-		    "[ERROR] Failed to allocate memory for adapter\n");
+		ql_log_pci(ql_log_fatal, pdev, 0x0031,
+		    "Failed to allocate memory for adapter, aborting.\n");
 
 		goto probe_hw_failed;
 	}
@@ -2103,9 +2216,6 @@
 
 	base_vha = qla2x00_create_host(sht, ha);
 	if (!base_vha) {
-		qla_printk(KERN_WARNING, ha,
-		    "[ERROR] Failed to allocate memory for scsi_host\n");
-
 		ret = -ENOMEM;
 		qla2x00_mem_free(ha);
 		qla2x00_free_req_que(ha, req);
@@ -2132,7 +2242,11 @@
 		if (!IS_QLA82XX(ha))
 			host->sg_tablesize = QLA_SG_ALL;
 	}
-
+	ql_dbg(ql_dbg_init, base_vha, 0x0032,
+	    "can_queue=%d, req=%p, "
+	    "mgmt_svr_loop_id=%d, sg_tablesize=%d.\n",
+	    host->can_queue, base_vha->req,
+	    base_vha->mgmt_svr_loop_id, host->sg_tablesize);
 	host->max_id = max_id;
 	host->this_id = 255;
 	host->cmd_per_lun = 3;
@@ -2146,6 +2260,14 @@
 	host->transportt = qla2xxx_transport_template;
 	sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC);
 
+	ql_dbg(ql_dbg_init, base_vha, 0x0033,
+	    "max_id=%d this_id=%d "
+	    "cmd_per_len=%d unique_id=%d max_cmd_len=%d max_channel=%d "
+	    "max_lun=%d transportt=%p, vendor_id=%d.\n", host->max_id,
+	    host->this_id, host->cmd_per_lun, host->unique_id,
+	    host->max_cmd_len, host->max_channel, host->max_lun,
+	    host->transportt, sht->vendor_id);
+
 	/* Set up the irqs */
 	ret = qla2x00_request_irqs(ha, rsp);
 	if (ret)
@@ -2156,9 +2278,8 @@
 	/* Alloc arrays of request and response ring ptrs */
 que_init:
 	if (!qla2x00_alloc_queues(ha)) {
-		qla_printk(KERN_WARNING, ha,
-		"[ERROR] Failed to allocate memory for queue"
-		" pointers\n");
+		ql_log(ql_log_fatal, base_vha, 0x003d,
+		    "Failed to allocate memory for queue pointers.. aborting.\n");
 		goto probe_init_failed;
 	}
 
@@ -2186,20 +2307,33 @@
 		rsp->rsp_q_out = &ha->iobase->isp82.rsp_q_out[0];
 	}
 
-	if (qla2x00_initialize_adapter(base_vha)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Failed to initialize adapter\n");
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc009,
+	    "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
+	    ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
+	ql_dbg(ql_dbg_multiq, base_vha, 0xc00a,
+	    "req->req_q_in=%p req->req_q_out=%p "
+	    "rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
+	    req->req_q_in, req->req_q_out,
+	    rsp->rsp_q_in, rsp->rsp_q_out);
+	ql_dbg(ql_dbg_init, base_vha, 0x003e,
+	    "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
+	    ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
+	ql_dbg(ql_dbg_init, base_vha, 0x003f,
+	    "req->req_q_in=%p req->req_q_out=%p rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
+	    req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);
 
-		DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
-		    "Adapter flags %x.\n",
-		    base_vha->host_no, base_vha->device_flags));
+	if (qla2x00_initialize_adapter(base_vha)) {
+		ql_log(ql_log_fatal, base_vha, 0x00d6,
+		    "Failed to initialize adapter - Adapter flags %x.\n",
+		    base_vha->device_flags);
 
 		if (IS_QLA82XX(ha)) {
 			qla82xx_idc_lock(ha);
 			qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
 				QLA82XX_DEV_FAILED);
 			qla82xx_idc_unlock(ha);
-			qla_printk(KERN_INFO, ha, "HW State: FAILED\n");
+			ql_log(ql_log_fatal, base_vha, 0x00d7,
+			    "HW State: FAILED.\n");
 		}
 
 		ret = -ENODEV;
@@ -2208,9 +2342,8 @@
 
 	if (ha->mqenable) {
 		if (qla25xx_setup_mode(base_vha)) {
-			qla_printk(KERN_WARNING, ha,
-				"Can't create queues, falling back to single"
-				" queue mode\n");
+			ql_log(ql_log_warn, base_vha, 0x00ec,
+			    "Failed to create queues, falling back to single queue mode.\n");
 			goto que_init;
 		}
 	}
@@ -2222,13 +2355,15 @@
 	 * Startup the kernel thread for this host adapter
 	 */
 	ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
-			"%s_dpc", base_vha->host_str);
+	    "%s_dpc", base_vha->host_str);
 	if (IS_ERR(ha->dpc_thread)) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to start DPC thread!\n");
+		ql_log(ql_log_fatal, base_vha, 0x00ed,
+		    "Failed to start DPC thread.\n");
 		ret = PTR_ERR(ha->dpc_thread);
 		goto probe_failed;
 	}
+	ql_dbg(ql_dbg_init, base_vha, 0x00ee,
+	    "DPC thread started successfully.\n");
 
 skip_dpc:
 	list_add_tail(&base_vha->list, &ha->vp_list);
@@ -2236,16 +2371,18 @@
 
 	/* Initialized the timer */
 	qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
-
-	DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
-	    base_vha->host_no, ha));
+	ql_dbg(ql_dbg_init, base_vha, 0x00ef,
+	    "Started qla2x00_timer with "
+	    "interval=%d.\n", WATCH_INTERVAL);
+	ql_dbg(ql_dbg_init, base_vha, 0x00f0,
+	    "Detected hba at address=%p.\n",
+	    ha);
 
 	if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) {
 		if (ha->fw_attributes & BIT_4) {
 			base_vha->flags.difdix_supported = 1;
-			DEBUG18(qla_printk(KERN_INFO, ha,
-			    "Registering for DIF/DIX type 1 and 3"
-			    " protection.\n"));
+			ql_dbg(ql_dbg_init, base_vha, 0x00f1,
+			    "Registering for DIF/DIX type 1 and 3 protection.\n");
 			scsi_host_set_prot(host,
 			    SHOST_DIF_TYPE1_PROTECTION
 			    | SHOST_DIF_TYPE2_PROTECTION
@@ -2267,6 +2404,9 @@
 	base_vha->flags.init_done = 1;
 	base_vha->flags.online = 1;
 
+	ql_dbg(ql_dbg_init, base_vha, 0x00f2,
+	    "Init done and hba is online.\n");
+
 	scsi_scan_host(host);
 
 	qla2x00_alloc_sysfs_attr(base_vha);
@@ -2275,14 +2415,17 @@
 
 	qla2x00_dfs_setup(base_vha);
 
-	qla_printk(KERN_INFO, ha, "\n"
-	    " QLogic Fibre Channel HBA Driver: %s\n"
-	    "  QLogic %s - %s\n"
-	    "  ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n",
-	    qla2x00_version_str, ha->model_number,
-	    ha->model_desc ? ha->model_desc : "", pdev->device,
-	    ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev),
-	    ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no,
+	ql_log(ql_log_info, base_vha, 0x00fa,
+	    "QLogic Fibre Channed HBA Driver: %s.\n",
+	    qla2x00_version_str);
+	ql_log(ql_log_info, base_vha, 0x00fb,
+	    "QLogic %s - %s.\n",
+	    ha->model_number, ha->model_desc ? ha->model_desc : "");
+	ql_log(ql_log_info, base_vha, 0x00fc,
+	    "ISP%04X: %s @ %s hdma%c host#=%ld fw=%s.\n",
+	    pdev->device, ha->isp_ops->pci_info_str(base_vha, pci_info),
+	    pci_name(pdev), ha->flags.enable_64bit_addressing ? '+' : '-',
+	    base_vha->host_no,
 	    ha->isp_ops->fw_version_str(base_vha, fw_str));
 
 	return 0;
@@ -2580,20 +2723,15 @@
 		fcport->login_retry = vha->hw->login_retry_count;
 		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 
-		DEBUG(printk("scsi(%ld): Port login retry: "
+		ql_dbg(ql_dbg_disc, vha, 0x2067,
+		    "Port login retry "
 		    "%02x%02x%02x%02x%02x%02x%02x%02x, "
-		    "id = 0x%04x retry cnt=%d\n",
-		    vha->host_no,
-		    fcport->port_name[0],
-		    fcport->port_name[1],
-		    fcport->port_name[2],
-		    fcport->port_name[3],
-		    fcport->port_name[4],
-		    fcport->port_name[5],
-		    fcport->port_name[6],
-		    fcport->port_name[7],
-		    fcport->loop_id,
-		    fcport->login_retry));
+		    "id = 0x%04x retry cnt=%d.\n",
+		    fcport->port_name[0], fcport->port_name[1],
+		    fcport->port_name[2], fcport->port_name[3],
+		    fcport->port_name[4], fcport->port_name[5],
+		    fcport->port_name[6], fcport->port_name[7],
+		    fcport->loop_id, fcport->login_retry);
 	}
 }
 
@@ -2676,6 +2814,9 @@
 			ctx_cachep);
 		if (!ha->ctx_mempool)
 			goto fail_free_srb_mempool;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0021,
+		    "ctx_cachep=%p ctx_mempool=%p.\n",
+		    ctx_cachep, ha->ctx_mempool);
 	}
 
 	/* Get memory for cached NVRAM */
@@ -2690,22 +2831,29 @@
 	if (!ha->s_dma_pool)
 		goto fail_free_nvram;
 
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0022,
+	    "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
+	    ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
+
 	if (IS_QLA82XX(ha) || ql2xenabledif) {
 		ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 			DSD_LIST_DMA_POOL_SIZE, 8, 0);
 		if (!ha->dl_dma_pool) {
-			qla_printk(KERN_WARNING, ha,
-			    "Memory Allocation failed - dl_dma_pool\n");
+			ql_log_pci(ql_log_fatal, ha->pdev, 0x0023,
+			    "Failed to allocate memory for dl_dma_pool.\n");
 			goto fail_s_dma_pool;
 		}
 
 		ha->fcp_cmnd_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 			FCP_CMND_DMA_POOL_SIZE, 8, 0);
 		if (!ha->fcp_cmnd_dma_pool) {
-			qla_printk(KERN_WARNING, ha,
-			    "Memory Allocation failed - fcp_cmnd_dma_pool\n");
+			ql_log_pci(ql_log_fatal, ha->pdev, 0x0024,
+			    "Failed to allocate memory for fcp_cmnd_dma_pool.\n");
 			goto fail_dl_dma_pool;
 		}
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025,
+		    "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n",
+		    ha->dl_dma_pool, ha->fcp_cmnd_dma_pool);
 	}
 
 	/* Allocate memory for SNS commands */
@@ -2715,6 +2863,8 @@
 		sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
 		if (!ha->sns_cmd)
 			goto fail_dma_pool;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0026,
+		    "sns_cmd.\n", ha->sns_cmd);
 	} else {
 	/* Get consistent memory allocated for MS IOCB */
 		ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
@@ -2726,12 +2876,16 @@
 			sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
 		if (!ha->ct_sns)
 			goto fail_free_ms_iocb;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0027,
+		    "ms_iocb=%p ct_sns=%p.\n",
+		    ha->ms_iocb, ha->ct_sns);
 	}
 
 	/* Allocate memory for request ring */
 	*req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
 	if (!*req) {
-		DEBUG(printk("Unable to allocate memory for req\n"));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0028,
+		    "Failed to allocate memory for req.\n");
 		goto fail_req;
 	}
 	(*req)->length = req_len;
@@ -2739,14 +2893,15 @@
 		((*req)->length + 1) * sizeof(request_t),
 		&(*req)->dma, GFP_KERNEL);
 	if (!(*req)->ring) {
-		DEBUG(printk("Unable to allocate memory for req_ring\n"));
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0029,
+		    "Failed to allocate memory for req_ring.\n");
 		goto fail_req_ring;
 	}
 	/* Allocate memory for response ring */
 	*rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
 	if (!*rsp) {
-		qla_printk(KERN_WARNING, ha,
-			"Unable to allocate memory for rsp\n");
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x002a,
+		    "Failed to allocate memory for rsp.\n");
 		goto fail_rsp;
 	}
 	(*rsp)->hw = ha;
@@ -2755,19 +2910,24 @@
 		((*rsp)->length + 1) * sizeof(response_t),
 		&(*rsp)->dma, GFP_KERNEL);
 	if (!(*rsp)->ring) {
-		qla_printk(KERN_WARNING, ha,
-			"Unable to allocate memory for rsp_ring\n");
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x002b,
+		    "Failed to allocate memory for rsp_ring.\n");
 		goto fail_rsp_ring;
 	}
 	(*req)->rsp = *rsp;
 	(*rsp)->req = *req;
+	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c,
+	    "req=%p req->length=%d req->ring=%p rsp=%p "
+	    "rsp->length=%d rsp->ring=%p.\n",
+	    *req, (*req)->length, (*req)->ring, *rsp, (*rsp)->length,
+	    (*rsp)->ring);
 	/* Allocate memory for NVRAM data for vports */
 	if (ha->nvram_npiv_size) {
 		ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
-					ha->nvram_npiv_size, GFP_KERNEL);
+		    ha->nvram_npiv_size, GFP_KERNEL);
 		if (!ha->npiv_info) {
-			qla_printk(KERN_WARNING, ha,
-				"Unable to allocate memory for npiv info\n");
+			ql_log_pci(ql_log_fatal, ha->pdev, 0x002d,
+			    "Failed to allocate memory for npiv_info.\n");
 			goto fail_npiv_info;
 		}
 	} else
@@ -2779,6 +2939,8 @@
 		    &ha->ex_init_cb_dma);
 		if (!ha->ex_init_cb)
 			goto fail_ex_init_cb;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002e,
+		    "ex_init_cb=%p.\n", ha->ex_init_cb);
 	}
 
 	INIT_LIST_HEAD(&ha->gbl_dsd_list);
@@ -2789,6 +2951,8 @@
 			&ha->async_pd_dma);
 		if (!ha->async_pd)
 			goto fail_async_pd;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002f,
+		    "async_pd=%p.\n", ha->async_pd);
 	}
 
 	INIT_LIST_HEAD(&ha->vp_list);
@@ -2854,7 +3018,8 @@
 	ha->init_cb = NULL;
 	ha->init_cb_dma = 0;
 fail:
-	DEBUG(printk("%s: Memory allocation failure\n", __func__));
+	ql_log(ql_log_fatal, NULL, 0x0030,
+	    "Memory allocation failure.\n");
 	return -ENOMEM;
 }
 
@@ -3003,8 +3168,8 @@
 
 	host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
 	if (host == NULL) {
-		printk(KERN_WARNING
-		"qla2xxx: Couldn't allocate host from scsi layer!\n");
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0107,
+		    "Failed to allocate host from the scsi layer, aborting.\n");
 		goto fail;
 	}
 
@@ -3023,6 +3188,11 @@
 	spin_lock_init(&vha->work_lock);
 
 	sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
+	ql_dbg(ql_dbg_init, vha, 0x0041,
+	    "Allocated the host=%p hw=%p vha=%p dev_name=%s",
+	    vha->host, vha->hw, vha,
+	    dev_name(&(ha->pdev->dev)));
+
 	return vha;
 
 fail:
@@ -3264,18 +3434,18 @@
 			if (status == QLA_SUCCESS) {
 				fcport->old_loop_id = fcport->loop_id;
 
-				DEBUG(printk("scsi(%ld): port login OK: logged "
-				"in ID 0x%x\n", vha->host_no, fcport->loop_id));
+				ql_dbg(ql_dbg_disc, vha, 0x2003,
+				    "Port login OK: logged in ID 0x%x.\n",
+				    fcport->loop_id);
 
 				qla2x00_update_fcport(vha, fcport);
 
 			} else if (status == 1) {
 				set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 				/* retry the login again */
-				DEBUG(printk("scsi(%ld): Retrying"
-				" %d login again loop_id 0x%x\n",
-				vha->host_no, fcport->login_retry,
-						fcport->loop_id));
+				ql_dbg(ql_dbg_disc, vha, 0x2007,
+				    "Retrying %d login again loop_id 0x%x.\n",
+				    fcport->login_retry, fcport->loop_id);
 			} else {
 				fcport->login_retry = 0;
 			}
@@ -3315,26 +3485,27 @@
 
 	set_current_state(TASK_INTERRUPTIBLE);
 	while (!kthread_should_stop()) {
-		DEBUG3(printk("qla2x00: DPC handler sleeping\n"));
+		ql_dbg(ql_dbg_dpc, base_vha, 0x4000,
+		    "DPC handler sleeping.\n");
 
 		schedule();
 		__set_current_state(TASK_RUNNING);
 
-		DEBUG3(printk("qla2x00: DPC handler waking up\n"));
+		ql_dbg(ql_dbg_dpc, base_vha, 0x4001,
+		    "DPC handler waking up.\n");
+		ql_dbg(ql_dbg_dpc, base_vha, 0x4002,
+		    "dpc_flags=0x%lx.\n", base_vha->dpc_flags);
 
 		/* Initialization not yet finished. Don't do anything yet. */
 		if (!base_vha->flags.init_done)
 			continue;
 
 		if (ha->flags.eeh_busy) {
-			DEBUG17(qla_printk(KERN_WARNING, ha,
-			    "qla2x00_do_dpc: dpc_flags: %lx\n",
-			    base_vha->dpc_flags));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x4003,
+			    "eeh_busy=%d.\n", ha->flags.eeh_busy);
 			continue;
 		}
 
-		DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no));
-
 		ha->dpc_active = 1;
 
 		if (ha->flags.mbox_busy) {
@@ -3351,8 +3522,8 @@
 				qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
 					QLA82XX_DEV_FAILED);
 				qla82xx_idc_unlock(ha);
-				qla_printk(KERN_INFO, ha,
-					"HW State: FAILED\n");
+				ql_log(ql_log_info, base_vha, 0x4004,
+				    "HW State: FAILED.\n");
 				qla82xx_device_state_handler(base_vha);
 				continue;
 			}
@@ -3360,10 +3531,8 @@
 			if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED,
 				&base_vha->dpc_flags)) {
 
-				DEBUG(printk(KERN_INFO
-					"scsi(%ld): dpc: sched "
-					"qla82xx_fcoe_ctx_reset ha = %p\n",
-					base_vha->host_no, ha));
+				ql_dbg(ql_dbg_dpc, base_vha, 0x4005,
+				    "FCoE context reset scheduled.\n");
 				if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
 					&base_vha->dpc_flags))) {
 					if (qla82xx_fcoe_ctx_reset(base_vha)) {
@@ -3377,18 +3546,16 @@
 						&base_vha->dpc_flags);
 				}
 
-				DEBUG(printk("scsi(%ld): dpc:"
-					" qla82xx_fcoe_ctx_reset end\n",
-					base_vha->host_no));
+				ql_dbg(ql_dbg_dpc, base_vha, 0x4006,
+				    "FCoE context reset end.\n");
 			}
 		}
 
 		if (test_and_clear_bit(ISP_ABORT_NEEDED,
 						&base_vha->dpc_flags)) {
 
-			DEBUG(printk("scsi(%ld): dpc: sched "
-			    "qla2x00_abort_isp ha = %p\n",
-			    base_vha->host_no, ha));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x4007,
+			    "ISP abort scheduled.\n");
 			if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
 			    &base_vha->dpc_flags))) {
 
@@ -3401,8 +3568,8 @@
 						&base_vha->dpc_flags);
 			}
 
-			DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
-			    base_vha->host_no));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x4008,
+			    "ISP abort end.\n");
 		}
 
 		if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
@@ -3411,9 +3578,8 @@
 		}
 
 		if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) {
-			DEBUG(printk(KERN_INFO "scsi(%ld): dpc: sched "
-			    "qla2x00_quiesce_needed ha = %p\n",
-			    base_vha->host_no, ha));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x4009,
+			    "Quiescence mode scheduled.\n");
 			qla82xx_device_state_handler(base_vha);
 			clear_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags);
 			if (!ha->flags.quiesce_owner) {
@@ -3423,17 +3589,20 @@
 				qla82xx_clear_qsnt_ready(base_vha);
 				qla82xx_idc_unlock(ha);
 			}
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400a,
+			    "Quiescence mode end.\n");
 		}
 
 		if (test_and_clear_bit(RESET_MARKER_NEEDED,
 							&base_vha->dpc_flags) &&
 		    (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
 
-			DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
-			    base_vha->host_no));
-
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400b,
+			    "Reset marker scheduled.\n");
 			qla2x00_rst_aen(base_vha);
 			clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400c,
+			    "Reset marker end.\n");
 		}
 
 		/* Retry each device up to login retry count */
@@ -3442,19 +3611,18 @@
 		    !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
 		    atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
 
-			DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
-					base_vha->host_no));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400d,
+			    "Relogin scheduled.\n");
 			qla2x00_relogin(base_vha);
-
-			DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
-			    base_vha->host_no));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400e,
+			    "Relogin end.\n");
 		}
 
 		if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
 							&base_vha->dpc_flags)) {
 
-			DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
-				base_vha->host_no));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x400f,
+			    "Loop resync scheduled.\n");
 
 			if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
 			    &base_vha->dpc_flags))) {
@@ -3465,8 +3633,8 @@
 						&base_vha->dpc_flags);
 			}
 
-			DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
-			    base_vha->host_no));
+			ql_dbg(ql_dbg_dpc, base_vha, 0x4010,
+			    "Loop resync end.\n");
 		}
 
 		if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
@@ -3489,7 +3657,8 @@
 	} /* End of while(1) */
 	__set_current_state(TASK_RUNNING);
 
-	DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no));
+	ql_dbg(ql_dbg_dpc, base_vha, 0x4011,
+	    "DPC handler exiting.\n");
 
 	/*
 	 * Make sure that nobody tries to wake us up again.
@@ -3596,9 +3765,11 @@
 qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp)
 {
 	if (atomic_read(&sp->ref_count) == 0) {
-		DEBUG2(qla_printk(KERN_WARNING, ha,
-		    "SP reference-count to ZERO -- sp=%p\n", sp));
-		DEBUG2(BUG());
+		ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015,
+		    "SP reference-count to ZERO -- sp=%p cmd=%p.\n",
+		    sp, sp->cmd);
+		if (ql2xextended_error_logging & ql_dbg_io)
+			BUG();
 		return;
 	}
 	if (!atomic_dec_and_test(&sp->ref_count))
@@ -3626,6 +3797,9 @@
 	struct req_que *req;
 
 	if (ha->flags.eeh_busy) {
+		ql_dbg(ql_dbg_timer, vha, 0x6000,
+		    "EEH = %d, restarting timer.\n",
+		    ha->flags.eeh_busy);
 		qla2x00_restart_timer(vha, WATCH_INTERVAL);
 		return;
 	}
@@ -3650,9 +3824,8 @@
 		if (atomic_read(&vha->loop_down_timer) ==
 		    vha->loop_down_abort_time) {
 
-			DEBUG(printk("scsi(%ld): Loop Down - aborting the "
-			    "queues before time expire\n",
-			    vha->host_no));
+			ql_log(ql_log_info, vha, 0x6008,
+			    "Loop down - aborting the queues before time expires.\n");
 
 			if (!IS_QLA2100(ha) && vha->link_down_timeout)
 				atomic_set(&vha->loop_state, LOOP_DEAD);
@@ -3697,10 +3870,7 @@
 		/* if the loop has been down for 4 minutes, reinit adapter */
 		if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
 			if (!(vha->device_flags & DFLG_NO_CABLE)) {
-				DEBUG(printk("scsi(%ld): Loop down - "
-				    "aborting ISP.\n",
-				    vha->host_no));
-				qla_printk(KERN_WARNING, ha,
+				ql_log(ql_log_warn, vha, 0x6009,
 				    "Loop down - aborting ISP.\n");
 
 				if (IS_QLA82XX(ha))
@@ -3711,9 +3881,9 @@
 						&vha->dpc_flags);
 			}
 		}
-		DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
-		    vha->host_no,
-		    atomic_read(&vha->loop_down_timer)));
+		ql_dbg(ql_dbg_timer, vha, 0x600a,
+		    "Loop down - seconds remaining %d.\n",
+		    atomic_read(&vha->loop_down_timer));
 	}
 
 	/* Check if beacon LED needs to be blinked for physical host only */
@@ -3736,8 +3906,27 @@
 	    test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) ||
 	    test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) ||
 	    test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
-	    test_bit(RELOGIN_NEEDED, &vha->dpc_flags)))
+	    test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) {
+		ql_dbg(ql_dbg_timer, vha, 0x600b,
+		    "isp_abort_needed=%d loop_resync_needed=%d "
+		    "fcport_update_needed=%d start_dpc=%d "
+		    "reset_marker_needed=%d",
+		    test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags),
+		    test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags),
+		    test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags),
+		    start_dpc,
+		    test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags));
+		ql_dbg(ql_dbg_timer, vha, 0x600c,
+		    "beacon_blink_needed=%d isp_unrecoverable=%d "
+		    "fcoe_ctx_reset_needed=%d vp_dpc_needed=%d "
+		    "relogin_needed=%d.\n",
+		    test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags),
+		    test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags),
+		    test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags),
+		    test_bit(VP_DPC_NEEDED, &vha->dpc_flags),
+		    test_bit(RELOGIN_NEEDED, &vha->dpc_flags));
 		qla2xxx_wake_dpc(vha);
+	}
 
 	qla2x00_restart_timer(vha, WATCH_INTERVAL);
 }
@@ -3806,8 +3995,8 @@
 		goto out;
 
 	if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
-		DEBUG2(printk("scsi(%ld): Failed to load firmware image "
-		    "(%s).\n", vha->host_no, blob->name));
+		ql_log(ql_log_warn, vha, 0x0063,
+		    "Failed to load firmware image (%s).\n", blob->name);
 		blob->fw = NULL;
 		blob = NULL;
 		goto out;
@@ -3836,8 +4025,8 @@
 	scsi_qla_host_t *vha = pci_get_drvdata(pdev);
 	struct qla_hw_data *ha = vha->hw;
 
-	DEBUG2(qla_printk(KERN_WARNING, ha, "error_detected:state %x\n",
-	    state));
+	ql_dbg(ql_dbg_aer, vha, 0x9000,
+	    "PCI error detected, state %x.\n", state);
 
 	switch (state) {
 	case pci_channel_io_normal:
@@ -3850,9 +4039,9 @@
 			ha->flags.isp82xx_fw_hung = 1;
 			if (ha->flags.mbox_busy) {
 				ha->flags.mbox_int = 1;
-				DEBUG2(qla_printk(KERN_ERR, ha,
-					"Due to pci channel io frozen, doing premature "
-					"completion of mbx command\n"));
+				ql_dbg(ql_dbg_aer, vha, 0x9001,
+				    "Due to pci channel io frozen, doing premature "
+				    "completion of mbx command.\n");
 				complete(&ha->mbx_intr_comp);
 			}
 		}
@@ -3900,8 +4089,8 @@
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	if (risc_paused) {
-		qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, "
-		    "Dumping firmware!\n");
+		ql_log(ql_log_info, base_vha, 0x9003,
+		    "RISC paused -- mmio_enabled, Dumping firmware.\n");
 		ha->isp_ops->fw_dump(base_vha, 0);
 
 		return PCI_ERS_RESULT_NEED_RESET;
@@ -3917,8 +4106,8 @@
 	int fn;
 	struct pci_dev *other_pdev = NULL;
 
-	DEBUG17(qla_printk(KERN_INFO, ha,
-	    "scsi(%ld): In qla82xx_error_recovery\n", base_vha->host_no));
+	ql_dbg(ql_dbg_aer, base_vha, 0x9006,
+	    "Entered %s.\n", __func__);
 
 	set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
 
@@ -3932,8 +4121,8 @@
 	fn = PCI_FUNC(ha->pdev->devfn);
 	while (fn > 0) {
 		fn--;
-		DEBUG17(qla_printk(KERN_INFO, ha,
-		    "Finding pci device at function = 0x%x\n", fn));
+		ql_dbg(ql_dbg_aer, base_vha, 0x9007,
+		    "Finding pci device at function = 0x%x.\n", fn);
 		other_pdev =
 		    pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
 		    ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
@@ -3942,9 +4131,9 @@
 		if (!other_pdev)
 			continue;
 		if (atomic_read(&other_pdev->enable_cnt)) {
-			DEBUG17(qla_printk(KERN_INFO, ha,
-			    "Found PCI func available and enabled at 0x%x\n",
-			    fn));
+			ql_dbg(ql_dbg_aer, base_vha, 0x9008,
+			    "Found PCI func available and enable at 0x%x.\n",
+			    fn);
 			pci_dev_put(other_pdev);
 			break;
 		}
@@ -3953,8 +4142,9 @@
 
 	if (!fn) {
 		/* Reset owner */
-		DEBUG17(qla_printk(KERN_INFO, ha,
-		    "This devfn is reset owner = 0x%x\n", ha->pdev->devfn));
+		ql_dbg(ql_dbg_aer, base_vha, 0x9009,
+		    "This devfn is reset owner = 0x%x.\n",
+		    ha->pdev->devfn);
 		qla82xx_idc_lock(ha);
 
 		qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
@@ -3964,8 +4154,8 @@
 		    QLA82XX_IDC_VERSION);
 
 		drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
-		DEBUG17(qla_printk(KERN_INFO, ha,
-		    "drv_active = 0x%x\n", drv_active));
+		ql_dbg(ql_dbg_aer, base_vha, 0x900a,
+		    "drv_active = 0x%x.\n", drv_active);
 
 		qla82xx_idc_unlock(ha);
 		/* Reset if device is not already reset
@@ -3978,12 +4168,14 @@
 		qla82xx_idc_lock(ha);
 
 		if (rval != QLA_SUCCESS) {
-			qla_printk(KERN_INFO, ha, "HW State: FAILED\n");
+			ql_log(ql_log_info, base_vha, 0x900b,
+			    "HW State: FAILED.\n");
 			qla82xx_clear_drv_active(ha);
 			qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
 			    QLA82XX_DEV_FAILED);
 		} else {
-			qla_printk(KERN_INFO, ha, "HW State: READY\n");
+			ql_log(ql_log_info, base_vha, 0x900c,
+			    "HW State: READY.\n");
 			qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
 			    QLA82XX_DEV_READY);
 			qla82xx_idc_unlock(ha);
@@ -3996,8 +4188,9 @@
 		}
 		qla82xx_idc_unlock(ha);
 	} else {
-		DEBUG17(qla_printk(KERN_INFO, ha,
-		    "This devfn is not reset owner = 0x%x\n", ha->pdev->devfn));
+		ql_dbg(ql_dbg_aer, base_vha, 0x900d,
+		    "This devfn is not reset owner = 0x%x.\n",
+		    ha->pdev->devfn);
 		if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
 		    QLA82XX_DEV_READY)) {
 			ha->flags.isp82xx_fw_hung = 0;
@@ -4021,7 +4214,8 @@
 	struct rsp_que *rsp;
 	int rc, retries = 10;
 
-	DEBUG17(qla_printk(KERN_WARNING, ha, "slot_reset\n"));
+	ql_dbg(ql_dbg_aer, base_vha, 0x9004,
+	    "Slot Reset.\n");
 
 	/* Workaround: qla2xxx driver which access hardware earlier
 	 * needs error state to be pci_channel_io_online.
@@ -4042,7 +4236,7 @@
 		rc = pci_enable_device(pdev);
 
 	if (rc) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, base_vha, 0x9005,
 		    "Can't re-enable PCI device after reset.\n");
 		goto exit_slot_reset;
 	}
@@ -4072,8 +4266,8 @@
 
 
 exit_slot_reset:
-	DEBUG17(qla_printk(KERN_WARNING, ha,
-	    "slot_reset-return:ret=%x\n", ret));
+	ql_dbg(ql_dbg_aer, base_vha, 0x900e,
+	    "slot_reset return %x.\n", ret);
 
 	return ret;
 }
@@ -4085,13 +4279,13 @@
 	struct qla_hw_data *ha = base_vha->hw;
 	int ret;
 
-	DEBUG17(qla_printk(KERN_WARNING, ha, "pci_resume\n"));
+	ql_dbg(ql_dbg_aer, base_vha, 0x900f,
+	    "pci_resume.\n");
 
 	ret = qla2x00_wait_for_hba_online(base_vha);
 	if (ret != QLA_SUCCESS) {
-		qla_printk(KERN_ERR, ha,
-		    "the device failed to resume I/O "
-		    "from slot/link_reset");
+		ql_log(ql_log_fatal, base_vha, 0x9002,
+		    "The device failed to resume I/O from slot/link_reset.\n");
 	}
 
 	pci_cleanup_aer_uncorrect_error_status(pdev);
@@ -4155,8 +4349,8 @@
 	srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
 	    SLAB_HWCACHE_ALIGN, NULL);
 	if (srb_cachep == NULL) {
-		printk(KERN_ERR
-		    "qla2xxx: Unable to allocate SRB cache...Failing load!\n");
+		ql_log(ql_log_fatal, NULL, 0x0001,
+		    "Unable to allocate SRB cache...Failing load!.\n");
 		return -ENOMEM;
 	}
 
@@ -4169,13 +4363,15 @@
 	    fc_attach_transport(&qla2xxx_transport_functions);
 	if (!qla2xxx_transport_template) {
 		kmem_cache_destroy(srb_cachep);
+		ql_log(ql_log_fatal, NULL, 0x0002,
+		    "fc_attach_transport failed...Failing load!.\n");
 		return -ENODEV;
 	}
 
 	apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops);
 	if (apidev_major < 0) {
-		printk(KERN_WARNING "qla2xxx: Unable to register char device "
-		    "%s\n", QLA2XXX_APIDEV);
+		ql_log(ql_log_fatal, NULL, 0x0003,
+		    "Unable to register char device %s.\n", QLA2XXX_APIDEV);
 	}
 
 	qla2xxx_transport_vport_template =
@@ -4183,16 +4379,21 @@
 	if (!qla2xxx_transport_vport_template) {
 		kmem_cache_destroy(srb_cachep);
 		fc_release_transport(qla2xxx_transport_template);
+		ql_log(ql_log_fatal, NULL, 0x0004,
+		    "fc_attach_transport vport failed...Failing load!.\n");
 		return -ENODEV;
 	}
-
-	printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n",
+	ql_log(ql_log_info, NULL, 0x0005,
+	    "QLogic Fibre Channel HBA Driver: %s.\n",
 	    qla2x00_version_str);
 	ret = pci_register_driver(&qla2xxx_pci_driver);
 	if (ret) {
 		kmem_cache_destroy(srb_cachep);
 		fc_release_transport(qla2xxx_transport_template);
 		fc_release_transport(qla2xxx_transport_vport_template);
+		ql_log(ql_log_fatal, NULL, 0x0006,
+		    "pci_register_driver failed...ret=%d Failing load!.\n",
+		    ret);
 	}
 	return ret;
 }
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 6936476..eff1356 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -189,6 +189,7 @@
 	uint16_t word;
 	uint32_t nv_cmd, wait_cnt;
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	qla2x00_nv_write(ha, NVR_DATA_OUT);
 	qla2x00_nv_write(ha, 0);
@@ -220,8 +221,8 @@
 	wait_cnt = NVR_WAIT_CNT;
 	do {
 		if (!--wait_cnt) {
-			DEBUG9_10(qla_printk(KERN_WARNING, ha,
-			    "NVRAM didn't go ready...\n"));
+			ql_dbg(ql_dbg_user, vha, 0x708d,
+			    "NVRAM didn't go ready...\n");
 			break;
 		}
 		NVRAM_DELAY();
@@ -308,6 +309,7 @@
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 	uint32_t word, wait_cnt;
 	uint16_t wprot, wprot_old;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	/* Clear NVRAM write protection. */
 	ret = QLA_FUNCTION_FAILED;
@@ -350,8 +352,8 @@
 		wait_cnt = NVR_WAIT_CNT;
 		do {
 			if (!--wait_cnt) {
-				DEBUG9_10(qla_printk(KERN_WARNING, ha,
-				    "NVRAM didn't go ready...\n"));
+				ql_dbg(ql_dbg_user, vha, 0x708e,
+				    "NVRAM didn't go ready...\n");
 				break;
 			}
 			NVRAM_DELAY();
@@ -371,6 +373,7 @@
 {
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 	uint32_t word, wait_cnt;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	if (stat != QLA_SUCCESS)
 		return;
@@ -409,8 +412,8 @@
 	wait_cnt = NVR_WAIT_CNT;
 	do {
 		if (!--wait_cnt) {
-			DEBUG9_10(qla_printk(KERN_WARNING, ha,
-			    "NVRAM didn't go ready...\n"));
+			ql_dbg(ql_dbg_user, vha, 0x708f,
+			    "NVRAM didn't go ready...\n");
 			break;
 		}
 		NVRAM_DELAY();
@@ -607,9 +610,10 @@
 	for (chksum = 0; cnt; cnt--)
 		chksum += le16_to_cpu(*wptr++);
 	if (chksum) {
-		qla_printk(KERN_ERR, ha,
+		ql_log(ql_log_fatal, vha, 0x0045,
 		    "Inconsistent FLTL detected: checksum=0x%x.\n", chksum);
-		qla2x00_dump_buffer(buf, sizeof(struct qla_flt_location));
+		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e,
+		    buf, sizeof(struct qla_flt_location));
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -618,7 +622,9 @@
 	*start = (le16_to_cpu(fltl->start_hi) << 16 |
 	    le16_to_cpu(fltl->start_lo)) >> 2;
 end:
-	DEBUG2(qla_printk(KERN_DEBUG, ha, "FLTL[%s] = 0x%x.\n", loc, *start));
+	ql_dbg(ql_dbg_init, vha, 0x0046,
+	    "FLTL[%s] = 0x%x.\n",
+	    loc, *start);
 	return QLA_SUCCESS;
 }
 
@@ -685,10 +691,10 @@
 	if (*wptr == __constant_cpu_to_le16(0xffff))
 		goto no_flash_data;
 	if (flt->version != __constant_cpu_to_le16(1)) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported FLT detected: "
-		    "version=0x%x length=0x%x checksum=0x%x.\n",
+		ql_log(ql_log_warn, vha, 0x0047,
+		    "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
 		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
-		    le16_to_cpu(flt->checksum)));
+		    le16_to_cpu(flt->checksum));
 		goto no_flash_data;
 	}
 
@@ -696,10 +702,10 @@
 	for (chksum = 0; cnt; cnt--)
 		chksum += le16_to_cpu(*wptr++);
 	if (chksum) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FLT detected: "
-		    "version=0x%x length=0x%x checksum=0x%x.\n",
+		ql_log(ql_log_fatal, vha, 0x0048,
+		    "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
 		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
-		    chksum));
+		    le16_to_cpu(flt->checksum));
 		goto no_flash_data;
 	}
 
@@ -708,10 +714,11 @@
 	for ( ; cnt; cnt--, region++) {
 		/* Store addresses as DWORD offsets. */
 		start = le32_to_cpu(region->start) >> 2;
-
-		DEBUG3(qla_printk(KERN_DEBUG, ha, "FLT[%02x]: start=0x%x "
-		    "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), start,
-		    le32_to_cpu(region->end) >> 2, le32_to_cpu(region->size)));
+		ql_dbg(ql_dbg_init, vha, 0x0049,
+		    "FLT[%02x]: start=0x%x "
+		    "end=0x%x size=0x%x.\n", le32_to_cpu(region->code),
+		    start, le32_to_cpu(region->end) >> 2,
+		    le32_to_cpu(region->size));
 
 		switch (le32_to_cpu(region->code) & 0xff) {
 		case FLT_REG_FW:
@@ -796,12 +803,16 @@
 	ha->flt_region_npiv_conf = ha->flags.port0 ?
 	    def_npiv_conf0[def] : def_npiv_conf1[def];
 done:
-	DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x "
-	    "vpd_nvram=0x%x vpd=0x%x nvram=0x%x fdt=0x%x flt=0x%x "
-	    "npiv=0x%x. fcp_prio_cfg=0x%x\n", loc, ha->flt_region_boot,
-	    ha->flt_region_fw, ha->flt_region_vpd_nvram, ha->flt_region_vpd,
-	    ha->flt_region_nvram, ha->flt_region_fdt, ha->flt_region_flt,
-	    ha->flt_region_npiv_conf, ha->flt_region_fcp_prio));
+	ql_dbg(ql_dbg_init, vha, 0x004a,
+	    "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x.\n",
+	    loc, ha->flt_region_boot,
+	    ha->flt_region_fw, ha->flt_region_vpd_nvram,
+	    ha->flt_region_vpd);
+	ql_dbg(ql_dbg_init, vha, 0x004b,
+	    "nvram=0x%x fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
+	    ha->flt_region_nvram,
+	    ha->flt_region_fdt, ha->flt_region_flt,
+	    ha->flt_region_npiv_conf, ha->flt_region_fcp_prio);
 }
 
 static void
@@ -833,10 +844,12 @@
 	    cnt++)
 		chksum += le16_to_cpu(*wptr++);
 	if (chksum) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FDT detected: "
-		    "checksum=0x%x id=%c version=0x%x.\n", chksum, fdt->sig[0],
-		    le16_to_cpu(fdt->version)));
-		DEBUG9(qla2x00_dump_buffer((uint8_t *)fdt, sizeof(*fdt)));
+		ql_dbg(ql_dbg_init, vha, 0x004c,
+		    "Inconsistent FDT detected:"
+		    " checksum=0x%x id=%c version0x%x.\n", chksum,
+		    fdt->sig[0], le16_to_cpu(fdt->version));
+		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
+		    (uint8_t *)fdt, sizeof(*fdt));
 		goto no_flash_data;
 	}
 
@@ -890,11 +903,12 @@
 		break;
 	}
 done:
-	DEBUG2(qla_printk(KERN_DEBUG, ha, "FDT[%s]: (0x%x/0x%x) erase=0x%x "
-	    "pro=%x upro=%x wrtd=0x%x blk=0x%x.\n", loc, mid, fid,
+	ql_dbg(ql_dbg_init, vha, 0x004d,
+	    "FDT[%x]: (0x%x/0x%x) erase=0x%x "
+	    "pr=%x upro=%x wrtd=0x%x blk=0x%x.\n", loc, mid, fid,
 	    ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd,
-	    ha->fdt_unprotect_sec_cmd, ha->fdt_wrt_disable,
-	    ha->fdt_block_size));
+	    ha->fdt_wrt_disable, ha->fdt_block_size);
+
 }
 
 static void
@@ -919,6 +933,10 @@
 		ha->nx_dev_init_timeout = le32_to_cpu(*wptr++);
 		ha->nx_reset_timeout = le32_to_cpu(*wptr);
 	}
+	ql_dbg(ql_dbg_init, vha, 0x004e,
+	    "nx_dev_init_timeout=%d "
+	    "nx_reset_timeout=%d.\n", ha->nx_dev_init_timeout,
+	    ha->nx_reset_timeout);
 	return;
 }
 
@@ -963,17 +981,18 @@
 	if (hdr.version == __constant_cpu_to_le16(0xffff))
 		return;
 	if (hdr.version != __constant_cpu_to_le16(1)) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported NPIV-Config "
+		ql_dbg(ql_dbg_user, vha, 0x7090,
+		    "Unsupported NPIV-Config "
 		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
 		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
-		    le16_to_cpu(hdr.checksum)));
+		    le16_to_cpu(hdr.checksum));
 		return;
 	}
 
 	data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL);
 	if (!data) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "NPIV-Config: Unable to "
-		    "allocate memory.\n"));
+		ql_log(ql_log_warn, vha, 0x7091,
+		    "Unable to allocate memory for data.\n");
 		return;
 	}
 
@@ -985,10 +1004,11 @@
 	for (wptr = data, chksum = 0; cnt; cnt--)
 		chksum += le16_to_cpu(*wptr++);
 	if (chksum) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent NPIV-Config "
+		ql_dbg(ql_dbg_user, vha, 0x7092,
+		    "Inconsistent NPIV-Config "
 		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
 		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
-		    chksum));
+		    le16_to_cpu(hdr.checksum));
 		goto done;
 	}
 
@@ -1014,21 +1034,22 @@
 		vid.port_name = wwn_to_u64(entry->port_name);
 		vid.node_name = wwn_to_u64(entry->node_name);
 
-		DEBUG2(qla_printk(KERN_INFO, ha, "NPIV[%02x]: wwpn=%llx "
-			"wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
-			(unsigned long long)vid.port_name,
-			(unsigned long long)vid.node_name,
-			le16_to_cpu(entry->vf_id),
-			entry->q_qos, entry->f_qos));
+		ql_dbg(ql_dbg_user, vha, 0x7093,
+		    "NPIV[%02x]: wwpn=%llx "
+		    "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
+		    (unsigned long long)vid.port_name,
+		    (unsigned long long)vid.node_name,
+		    le16_to_cpu(entry->vf_id),
+		    entry->q_qos, entry->f_qos);
 
 		if (i < QLA_PRECONFIG_VPORTS) {
 			vport = fc_vport_create(vha->host, 0, &vid);
 			if (!vport)
-				qla_printk(KERN_INFO, ha,
-				"NPIV-Config: Failed to create vport [%02x]: "
-				"wwpn=%llx wwnn=%llx.\n", cnt,
-				(unsigned long long)vid.port_name,
-				(unsigned long long)vid.node_name);
+				ql_log(ql_log_warn, vha, 0x7094,
+				    "NPIV-Config Failed to create vport [%02x]: "
+				    "wwpn=%llx wwnn=%llx.\n", cnt,
+				    (unsigned long long)vid.port_name,
+				    (unsigned long long)vid.node_name);
 		}
 	}
 done:
@@ -1127,9 +1148,10 @@
 		optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
 		    &optrom_dma, GFP_KERNEL);
 		if (!optrom) {
-			qla_printk(KERN_DEBUG, ha,
-			    "Unable to allocate memory for optrom burst write "
-			    "(%x KB).\n", OPTROM_BURST_SIZE / 1024);
+			ql_log(ql_log_warn, vha, 0x7095,
+			    "Unable to allocate "
+			    "memory for optrom burst write (%x KB).\n",
+			    OPTROM_BURST_SIZE / 1024);
 		}
 	}
 
@@ -1138,7 +1160,7 @@
 
 	ret = qla24xx_unprotect_flash(vha);
 	if (ret != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7096,
 		    "Unable to unprotect flash for update.\n");
 		goto done;
 	}
@@ -1156,9 +1178,9 @@
 				    0xff0000) | ((fdata >> 16) & 0xff));
 			ret = qla24xx_erase_sector(vha, fdata);
 			if (ret != QLA_SUCCESS) {
-				DEBUG9(qla_printk(KERN_WARNING, ha,
-				    "Unable to erase sector: address=%x.\n",
-				    faddr));
+				ql_dbg(ql_dbg_user, vha, 0x7007,
+				    "Unable to erase erase sector: address=%x.\n",
+				    faddr);
 				break;
 			}
 		}
@@ -1172,12 +1194,12 @@
 			    flash_data_addr(ha, faddr),
 			    OPTROM_BURST_DWORDS);
 			if (ret != QLA_SUCCESS) {
-				qla_printk(KERN_WARNING, ha,
+				ql_log(ql_log_warn, vha, 0x7097,
 				    "Unable to burst-write optrom segment "
 				    "(%x/%x/%llx).\n", ret,
 				    flash_data_addr(ha, faddr),
 				    (unsigned long long)optrom_dma);
-				qla_printk(KERN_WARNING, ha,
+				ql_log(ql_log_warn, vha, 0x7098,
 				    "Reverting to slow-write.\n");
 
 				dma_free_coherent(&ha->pdev->dev,
@@ -1194,9 +1216,9 @@
 		ret = qla24xx_write_flash_dword(ha,
 		    flash_data_addr(ha, faddr), cpu_to_le32(*dwptr));
 		if (ret != QLA_SUCCESS) {
-			DEBUG9(printk("%s(%ld) Unable to program flash "
-			    "address=%x data=%x.\n", __func__,
-			    vha->host_no, faddr, *dwptr));
+			ql_dbg(ql_dbg_user, vha, 0x7006,
+			    "Unable to program flash address=%x data=%x.\n",
+			    faddr, *dwptr);
 			break;
 		}
 
@@ -1211,7 +1233,7 @@
 
 	ret = qla24xx_protect_flash(vha);
 	if (ret != QLA_SUCCESS)
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x7099,
 		    "Unable to protect flash after update.\n");
 done:
 	if (optrom)
@@ -1324,9 +1346,9 @@
 		ret = qla24xx_write_flash_dword(ha,
 		    nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr));
 		if (ret != QLA_SUCCESS) {
-			DEBUG9(qla_printk(KERN_WARNING, ha,
+			ql_dbg(ql_dbg_user, vha, 0x709a,
 			    "Unable to program nvram address=%x data=%x.\n",
-			    naddr, *dwptr));
+			    naddr, *dwptr);
 			break;
 		}
 	}
@@ -1476,7 +1498,7 @@
 	ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
 
 	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x709b,
 		    "Unable to update fw options (beacon on).\n");
 		return QLA_FUNCTION_FAILED;
 	}
@@ -1541,7 +1563,7 @@
 
 	rval = qla2x00_set_fw_options(vha, ha->fw_options);
 	if (rval != QLA_SUCCESS)
-		qla_printk(KERN_WARNING, ha,
+		ql_log(ql_log_warn, vha, 0x709c,
 		    "Unable to update fw options (beacon off).\n");
 	return rval;
 }
@@ -1616,7 +1638,7 @@
 
 		if (qla2x00_get_fw_options(vha, ha->fw_options) !=
 		    QLA_SUCCESS) {
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x7009,
 			    "Unable to update fw options (beacon on).\n");
 			return QLA_FUNCTION_FAILED;
 		}
@@ -1670,14 +1692,14 @@
 	ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
 
 	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to update fw options (beacon off).\n");
+		ql_log(ql_log_warn, vha, 0x704d,
+		    "Unable to update fw options (beacon on).\n");
 		return QLA_FUNCTION_FAILED;
 	}
 
 	if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
-		qla_printk(KERN_WARNING, ha,
-		    "Unable to get fw options (beacon off).\n");
+		ql_log(ql_log_warn, vha, 0x704e,
+		    "Unable to update fw options (beacon on).\n");
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -2389,10 +2411,9 @@
 	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
 	    &optrom_dma, GFP_KERNEL);
 	if (!optrom) {
-		qla_printk(KERN_DEBUG, ha,
-		    "Unable to allocate memory for optrom burst read "
-		    "(%x KB).\n", OPTROM_BURST_SIZE / 1024);
-
+		ql_log(ql_log_warn, vha, 0x00cc,
+		    "Unable to allocate memory for optrom burst read (%x KB).\n",
+		    OPTROM_BURST_SIZE / 1024);
 		goto slow_read;
 	}
 
@@ -2407,12 +2428,11 @@
 		rval = qla2x00_dump_ram(vha, optrom_dma,
 		    flash_data_addr(ha, faddr), burst);
 		if (rval) {
-			qla_printk(KERN_WARNING, ha,
-			    "Unable to burst-read optrom segment "
-			    "(%x/%x/%llx).\n", rval,
-			    flash_data_addr(ha, faddr),
+			ql_log(ql_log_warn, vha, 0x00f5,
+			    "Unable to burst-read optrom segment (%x/%x/%llx).\n",
+			    rval, flash_data_addr(ha, faddr),
 			    (unsigned long long)optrom_dma);
-			qla_printk(KERN_WARNING, ha,
+			ql_log(ql_log_warn, vha, 0x00f6,
 			    "Reverting to slow-read.\n");
 
 			dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
@@ -2556,8 +2576,8 @@
 		if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
 		    qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
 			/* No signature */
-			DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
-			    "signature.\n"));
+			ql_log(ql_log_fatal, vha, 0x0050,
+			    "No matching ROM signature.\n");
 			ret = QLA_FUNCTION_FAILED;
 			break;
 		}
@@ -2573,8 +2593,8 @@
 		    qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
 		    qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
 			/* Incorrect header. */
-			DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
-			    "found pcir_adr=%x.\n", pcids));
+			ql_log(ql_log_fatal, vha, 0x0051,
+			    "PCI data struct not found pcir_adr=%x.\n", pcids);
 			ret = QLA_FUNCTION_FAILED;
 			break;
 		}
@@ -2588,8 +2608,9 @@
 			    qla2x00_read_flash_byte(ha, pcids + 0x12);
 			ha->bios_revision[1] =
 			    qla2x00_read_flash_byte(ha, pcids + 0x13);
-			DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
-			    ha->bios_revision[1], ha->bios_revision[0]));
+			ql_dbg(ql_dbg_init, vha, 0x0052,
+			    "Read BIOS %d.%d.\n",
+			    ha->bios_revision[1], ha->bios_revision[0]);
 			break;
 		case ROM_CODE_TYPE_FCODE:
 			/* Open Firmware standard for PCI (FCode). */
@@ -2602,12 +2623,14 @@
 			    qla2x00_read_flash_byte(ha, pcids + 0x12);
 			ha->efi_revision[1] =
 			    qla2x00_read_flash_byte(ha, pcids + 0x13);
-			DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
-			    ha->efi_revision[1], ha->efi_revision[0]));
+			ql_dbg(ql_dbg_init, vha, 0x0053,
+			    "Read EFI %d.%d.\n",
+			    ha->efi_revision[1], ha->efi_revision[0]);
 			break;
 		default:
-			DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
-			    "type %x at pcids %x.\n", code_type, pcids));
+			ql_log(ql_log_warn, vha, 0x0054,
+			    "Unrecognized code type %x at pcids %x.\n",
+			    code_type, pcids);
 			break;
 		}
 
@@ -2627,21 +2650,28 @@
 
 		qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
 		    8);
-		DEBUG3(qla_printk(KERN_DEBUG, ha, "dumping fw ver from "
-		    "flash:\n"));
-		DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8));
+		ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010a,
+		    "Dumping fw "
+		    "ver from flash:.\n");
+		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010b,
+		    (uint8_t *)dbyte, 8);
 
 		if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
 		    dcode[2] == 0xffff && dcode[3] == 0xffff) ||
 		    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
 		    dcode[3] == 0)) {
-			DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
-			    "revision at %x.\n", ha->flt_region_fw * 4));
+			ql_log(ql_log_warn, vha, 0x0057,
+			    "Unrecognized fw revision at %x.\n",
+			    ha->flt_region_fw * 4);
 		} else {
 			/* values are in big endian */
 			ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
 			ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3];
 			ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5];
+			ql_dbg(ql_dbg_init, vha, 0x0058,
+			    "FW Version: "
+			    "%d.%d.%d.\n", ha->fw_revision[0],
+			    ha->fw_revision[1], ha->fw_revision[2]);
 		}
 	}
 
@@ -2683,8 +2713,8 @@
 		bcode = mbuf + (pcihdr % 4);
 		if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) {
 			/* No signature */
-			DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
-			    "signature.\n"));
+			ql_log(ql_log_fatal, vha, 0x0059,
+			    "No matching ROM signature.\n");
 			ret = QLA_FUNCTION_FAILED;
 			break;
 		}
@@ -2699,8 +2729,8 @@
 		if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
 		    bcode[0x2] != 'I' || bcode[0x3] != 'R') {
 			/* Incorrect header. */
-			DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
-			    "found pcir_adr=%x.\n", pcids));
+			ql_log(ql_log_fatal, vha, 0x005a,
+			    "PCI data struct not found pcir_adr=%x.\n", pcids);
 			ret = QLA_FUNCTION_FAILED;
 			break;
 		}
@@ -2712,26 +2742,30 @@
 			/* Intel x86, PC-AT compatible. */
 			ha->bios_revision[0] = bcode[0x12];
 			ha->bios_revision[1] = bcode[0x13];
-			DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
-			    ha->bios_revision[1], ha->bios_revision[0]));
+			ql_dbg(ql_dbg_init, vha, 0x005b,
+			    "Read BIOS %d.%d.\n",
+			    ha->bios_revision[1], ha->bios_revision[0]);
 			break;
 		case ROM_CODE_TYPE_FCODE:
 			/* Open Firmware standard for PCI (FCode). */
 			ha->fcode_revision[0] = bcode[0x12];
 			ha->fcode_revision[1] = bcode[0x13];
-			DEBUG3(qla_printk(KERN_DEBUG, ha, "read FCODE %d.%d.\n",
-			    ha->fcode_revision[1], ha->fcode_revision[0]));
+			ql_dbg(ql_dbg_init, vha, 0x005c,
+			    "Read FCODE %d.%d.\n",
+			    ha->fcode_revision[1], ha->fcode_revision[0]);
 			break;
 		case ROM_CODE_TYPE_EFI:
 			/* Extensible Firmware Interface (EFI). */
 			ha->efi_revision[0] = bcode[0x12];
 			ha->efi_revision[1] = bcode[0x13];
-			DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
-			    ha->efi_revision[1], ha->efi_revision[0]));
+			ql_dbg(ql_dbg_init, vha, 0x005d,
+			    "Read EFI %d.%d.\n",
+			    ha->efi_revision[1], ha->efi_revision[0]);
 			break;
 		default:
-			DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
-			    "type %x at pcids %x.\n", code_type, pcids));
+			ql_log(ql_log_warn, vha, 0x005e,
+			    "Unrecognized code type %x at pcids %x.\n",
+			    code_type, pcids);
 			break;
 		}
 
@@ -2753,13 +2787,18 @@
 	    dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
 	    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
 	    dcode[3] == 0)) {
-		DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
-		    "revision at %x.\n", ha->flt_region_fw * 4));
+		ql_log(ql_log_warn, vha, 0x005f,
+		    "Unrecognized fw revision at %x.\n",
+		    ha->flt_region_fw * 4);
 	} else {
 		ha->fw_revision[0] = dcode[0];
 		ha->fw_revision[1] = dcode[1];
 		ha->fw_revision[2] = dcode[2];
 		ha->fw_revision[3] = dcode[3];
+		ql_dbg(ql_dbg_init, vha, 0x0060,
+		    "Firmware revision %d.%d.%d.%d.\n",
+		    ha->fw_revision[0], ha->fw_revision[1],
+		    ha->fw_revision[2], ha->fw_revision[3]);
 	}
 
 	/* Check for golden firmware and get version if available */
@@ -2775,9 +2814,9 @@
 
 	if (dcode[4] == 0xFFFFFFFF && dcode[5] == 0xFFFFFFFF &&
 	    dcode[6] == 0xFFFFFFFF && dcode[7] == 0xFFFFFFFF) {
-		DEBUG2(qla_printk(KERN_INFO, ha,
-		    "%s(%ld): Unrecognized golden fw at 0x%x.\n",
-		    __func__, vha->host_no, ha->flt_region_gold_fw * 4));
+		ql_log(ql_log_warn, vha, 0x0056,
+		    "Unrecognized golden fw at 0x%x.\n",
+		    ha->flt_region_gold_fw * 4);
 		return ret;
 	}
 
@@ -2843,9 +2882,9 @@
 	if (!ha->fcp_prio_cfg) {
 		ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
 		if (!ha->fcp_prio_cfg) {
-			qla_printk(KERN_WARNING, ha,
-			"Unable to allocate memory for fcp priority data "
-					"(%x).\n", FCP_PRIO_CFG_SIZE);
+			ql_log(ql_log_warn, vha, 0x00d5,
+			    "Unable to allocate memory for fcp priorty data (%x).\n",
+			    FCP_PRIO_CFG_SIZE);
 			return QLA_FUNCTION_FAILED;
 		}
 	}
@@ -2857,7 +2896,7 @@
 	ha->isp_ops->read_optrom(vha, (uint8_t *)ha->fcp_prio_cfg,
 			fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE);
 
-	if (!qla24xx_fcp_prio_cfg_valid(ha->fcp_prio_cfg, 0))
+	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0))
 		goto fail;
 
 	/* read remaining FCP CMD config data from flash */
@@ -2869,7 +2908,7 @@
 			fcp_prio_addr << 2, (len < max_len ? len : max_len));
 
 	/* revalidate the entire FCP priority config data, including entries */
-	if (!qla24xx_fcp_prio_cfg_valid(ha->fcp_prio_cfg, 1))
+	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1))
 		goto fail;
 
 	ha->flags.fcp_prio_enabled = 1;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 28d9c9d..fc3f168 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -137,6 +137,7 @@
 		host->host_blocked = host->max_host_blocked;
 		break;
 	case SCSI_MLQUEUE_DEVICE_BUSY:
+	case SCSI_MLQUEUE_EH_RETRY:
 		device->device_blocked = device->max_device_blocked;
 		break;
 	case SCSI_MLQUEUE_TARGET_BUSY:
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
index 8a172d4..5fbeadd 100644
--- a/drivers/scsi/scsi_transport_spi.c
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -597,6 +597,28 @@
 		   show_spi_host_signalling,
 		   store_spi_host_signalling);
 
+static ssize_t show_spi_host_width(struct device *cdev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct Scsi_Host *shost = transport_class_to_shost(cdev);
+
+	return sprintf(buf, "%s\n", shost->max_id == 16 ? "wide" : "narrow");
+}
+static DEVICE_ATTR(host_width, S_IRUGO,
+		   show_spi_host_width, NULL);
+
+static ssize_t show_spi_host_hba_id(struct device *cdev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct Scsi_Host *shost = transport_class_to_shost(cdev);
+
+	return sprintf(buf, "%d\n", shost->this_id);
+}
+static DEVICE_ATTR(hba_id, S_IRUGO,
+		   show_spi_host_hba_id, NULL);
+
 #define DV_SET(x, y)			\
 	if(i->f->set_##x)		\
 		i->f->set_##x(sdev->sdev_target, y)
@@ -1380,6 +1402,8 @@
 
 static struct attribute *host_attributes[] = {
 	&dev_attr_signalling.attr,
+	&dev_attr_host_width.attr,
+	&dev_attr_hba_id.attr,
 	NULL
 };
 
diff --git a/drivers/staging/ath6kl/os/linux/ar6000_drv.c b/drivers/staging/ath6kl/os/linux/ar6000_drv.c
index 499b7a9..32ee39a 100644
--- a/drivers/staging/ath6kl/os/linux/ar6000_drv.c
+++ b/drivers/staging/ath6kl/os/linux/ar6000_drv.c
@@ -6205,6 +6205,7 @@
     
     ether_setup(dev);
     init_netdev(dev, ap_ifname);
+    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
     if (register_netdev(dev)) {
         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ar6000_create_ap_interface: register_netdev failed\n"));
diff --git a/drivers/staging/brcm80211/brcmsmac/mac80211_if.h b/drivers/staging/brcm80211/brcmsmac/mac80211_if.h
index 5711e7c..40e3d37 100644
--- a/drivers/staging/brcm80211/brcmsmac/mac80211_if.h
+++ b/drivers/staging/brcm80211/brcmsmac/mac80211_if.h
@@ -24,8 +24,6 @@
 #define BRCMS_SET_SHORTSLOT_OVERRIDE		146
 
 
-#include <linux/interrupt.h>
-
 /* BMAC Note: High-only driver is no longer working in softirq context as it needs to block and
  * sleep so perimeter lock has to be a semaphore instead of spinlock. This requires timers to be
  * submitted to workqueue instead of being on kernel timer
diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/cxd2099/Kconfig
index 9d638c3..b48aefd 100644
--- a/drivers/staging/cxd2099/Kconfig
+++ b/drivers/staging/cxd2099/Kconfig
@@ -1,9 +1,10 @@
 config DVB_CXD2099
-        tristate "CXD2099AR Common Interface driver"
-        depends on DVB_CORE && PCI && I2C && DVB_NGENE
-        ---help---
-          Support for the CI module found on cineS2 DVB-S2, supported by
-	  the Micronas PCIe device driver (ngene).
+	tristate "CXD2099AR Common Interface driver"
+	depends on DVB_CORE && PCI && I2C
+	---help---
+	  Support for the CI module found on cards based on
+	  - Micronas ngene PCIe bridge: cineS2 etc.
+	  - Digital Devices PCIe bridge: Octopus series
 
 	  For now, data is passed through '/dev/dvb/adapterX/sec0':
 	    - Encrypted data must be written to 'sec0'.
diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/cxd2099/cxd2099.c
index 55b1c4a..1c04185 100644
--- a/drivers/staging/cxd2099/cxd2099.c
+++ b/drivers/staging/cxd2099/cxd2099.c
@@ -1,7 +1,7 @@
 /*
  * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
  *
- * Copyright (C) 2010 DigitalDevices UG
+ * Copyright (C) 2010-2011 Digital Devices GmbH
  *
  *
  * This program is free software; you can redistribute it and/or
@@ -41,13 +41,13 @@
 	struct dvb_ca_en50221 en;
 
 	struct i2c_adapter *i2c;
-	u8     adr;
+	struct cxd2099_cfg cfg;
+
 	u8     regs[0x23];
 	u8     lastaddress;
 	u8     clk_reg_f;
 	u8     clk_reg_b;
 	int    mode;
-	u32    bitrate;
 	int    ready;
 	int    dr;
 	int    slot_stat;
@@ -89,9 +89,9 @@
 			u8 reg, u8 *val)
 {
 	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
-				   .buf = &reg, .len = 1 },
+				   .buf = &reg, .len = 1},
 				  {.addr = adr, .flags = I2C_M_RD,
-				   .buf = val, .len = 1 } };
+				   .buf = val, .len = 1} };
 
 	if (i2c_transfer(adapter, msgs, 2) != 2) {
 		printk(KERN_ERR "error in i2c_read_reg\n");
@@ -104,9 +104,9 @@
 		    u8 reg, u8 *data, u8 n)
 {
 	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
-				   .buf = &reg, .len = 1 },
-				  {.addr = adr, .flags = I2C_M_RD,
-				   .buf = data, .len = n } };
+				 .buf = &reg, .len = 1},
+				{.addr = adr, .flags = I2C_M_RD,
+				 .buf = data, .len = n} };
 
 	if (i2c_transfer(adapter, msgs, 2) != 2) {
 		printk(KERN_ERR "error in i2c_read\n");
@@ -119,10 +119,10 @@
 {
 	int status;
 
-	status = i2c_write_reg(ci->i2c, ci->adr, 0, adr);
+	status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
 	if (!status) {
 		ci->lastaddress = adr;
-		status = i2c_read(ci->i2c, ci->adr, 1, data, n);
+		status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n);
 	}
 	return status;
 }
@@ -136,24 +136,24 @@
 static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
 {
 	int status;
-	u8 addr[3] = { 2, address&0xff, address>>8 };
+	u8 addr[3] = {2, address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->adr, addr, 3);
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
 	if (!status)
-		status = i2c_read(ci->i2c, ci->adr, 3, data, n);
+		status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
 	return status;
 }
 
 static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
 {
 	int status;
-	u8 addr[3] = { 2, address&0xff, address>>8 };
+	u8 addr[3] = {2, address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->adr, addr, 3);
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
 	if (!status) {
 		u8 buf[256] = {3};
 		memcpy(buf+1, data, n);
-		status = i2c_write(ci->i2c, ci->adr, buf, n+1);
+		status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1);
 	}
 	return status;
 }
@@ -161,39 +161,64 @@
 static int read_io(struct cxd *ci, u16 address, u8 *val)
 {
 	int status;
-	u8 addr[3] = { 2, address&0xff, address>>8 };
+	u8 addr[3] = {2, address & 0xff, address >> 8};
 
-	status = i2c_write(ci->i2c, ci->adr, addr, 3);
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
 	if (!status)
-		status = i2c_read(ci->i2c, ci->adr, 3, val, 1);
+		status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
 	return status;
 }
 
 static int write_io(struct cxd *ci, u16 address, u8 val)
 {
 	int status;
-	u8 addr[3] = { 2, address&0xff, address>>8 };
-	u8 buf[2] = { 3, val };
+	u8 addr[3] = {2, address & 0xff, address >> 8};
+	u8 buf[2] = {3, val};
 
-	status = i2c_write(ci->i2c, ci->adr, addr, 3);
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
 	if (!status)
-		status = i2c_write(ci->i2c, ci->adr, buf, 2);
-
+		status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
 	return status;
 }
 
+#if 0
+static int read_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+	int status;
+	u8 addr[3] = { 2, 0, 0 };
+
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	if (!status)
+		status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+	return 0;
+}
+
+static int write_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+	int status;
+	u8 addr[3] = {2, 0, 0};
+
+	status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+	if (!status) {
+		u8 buf[256] = {3};
+		memcpy(buf+1, data, n);
+		status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+	}
+	return 0;
+}
+#endif
 
 static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
 {
 	int status;
 
-	status = i2c_write_reg(ci->i2c, ci->adr, 0, reg);
+	status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
 	if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
-		status = i2c_read_reg(ci->i2c, ci->adr, 1, &ci->regs[reg]);
-	ci->regs[reg] = (ci->regs[reg]&(~mask))|val;
+		status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
+	ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
 	if (!status) {
 		ci->lastaddress = reg;
-		status = i2c_write_reg(ci->i2c, ci->adr, 1, ci->regs[reg]);
+		status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
 	}
 	if (reg == 0x20)
 		ci->regs[reg] &= 0x7f;
@@ -211,11 +236,11 @@
 	int status;
 	u8 buf[256] = {1};
 
-	status = i2c_write_reg(ci->i2c, ci->adr, 0, adr);
+	status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
 	if (!status) {
 		ci->lastaddress = adr;
-		memcpy(buf+1, data, n);
-		status = i2c_write(ci->i2c, ci->adr, buf, n+1);
+		memcpy(buf + 1, data, n);
+		status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
 	}
 	return status;
 }
@@ -249,12 +274,16 @@
 		write_regm(ci, 0x20, 0x80, 0x80);
 		break;
 	case 0x01:
+#ifdef BUFFER_MODE
+		if (!ci->en.read_data)
+			return;
 		printk(KERN_INFO "enable cam buffer mode\n");
 		/* write_reg(ci, 0x0d, 0x00); */
 		/* write_reg(ci, 0x0e, 0x01); */
 		write_regm(ci, 0x08, 0x40, 0x40);
 		/* read_reg(ci, 0x12, &dummy); */
 		write_regm(ci, 0x08, 0x80, 0x80);
+#endif
 		break;
 	default:
 		break;
@@ -264,8 +293,6 @@
 
 
 
-#define CHK_ERROR(s) if ((status = s)) break
-
 static int init(struct cxd *ci)
 {
 	int status;
@@ -273,63 +300,160 @@
 	mutex_lock(&ci->lock);
 	ci->mode = -1;
 	do {
-		CHK_ERROR(write_reg(ci, 0x00, 0x00));
-		CHK_ERROR(write_reg(ci, 0x01, 0x00));
-		CHK_ERROR(write_reg(ci, 0x02, 0x10));
-		CHK_ERROR(write_reg(ci, 0x03, 0x00));
-		CHK_ERROR(write_reg(ci, 0x05, 0xFF));
-		CHK_ERROR(write_reg(ci, 0x06, 0x1F));
-		CHK_ERROR(write_reg(ci, 0x07, 0x1F));
-		CHK_ERROR(write_reg(ci, 0x08, 0x28));
-		CHK_ERROR(write_reg(ci, 0x14, 0x20));
+		status = write_reg(ci, 0x00, 0x00);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x01, 0x00);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x02, 0x10);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x03, 0x00);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x05, 0xFF);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x06, 0x1F);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x07, 0x1F);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x08, 0x28);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x14, 0x20);
+		if (status < 0)
+			break;
 
-		CHK_ERROR(write_reg(ci, 0x09, 0x4D)); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
-		CHK_ERROR(write_reg(ci, 0x0A, 0xA7)); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */
-
-		/* Sync detector */
-		CHK_ERROR(write_reg(ci, 0x0B, 0x33));
-		CHK_ERROR(write_reg(ci, 0x0C, 0x33));
-
-		CHK_ERROR(write_regm(ci, 0x14, 0x00, 0x0F));
-		CHK_ERROR(write_reg(ci, 0x15, ci->clk_reg_b));
-		CHK_ERROR(write_regm(ci, 0x16, 0x00, 0x0F));
-		CHK_ERROR(write_reg(ci, 0x17, ci->clk_reg_f));
-
-		CHK_ERROR(write_reg(ci, 0x20, 0x28)); /* Integer Divider, Falling Edge, Internal Sync, */
-		CHK_ERROR(write_reg(ci, 0x21, 0x00)); /* MCLKI = TICLK/8 */
-		CHK_ERROR(write_reg(ci, 0x22, 0x07)); /* MCLKI = TICLK/8 */
-
-
-		CHK_ERROR(write_regm(ci, 0x20, 0x80, 0x80)); /* Reset CAM state machine */
-
-		CHK_ERROR(write_regm(ci, 0x03, 0x02, 02));  /* Enable IREQA Interrupt */
-		CHK_ERROR(write_reg(ci, 0x01, 0x04));  /* Enable CD Interrupt */
-		CHK_ERROR(write_reg(ci, 0x00, 0x31));  /* Enable TS1,Hot Swap,Slot A */
-		CHK_ERROR(write_regm(ci, 0x09, 0x08, 0x08));  /* Put TS in bypass */
-		ci->cammode = -1;
-#ifdef BUFFER_MODE
-		cam_mode(ci, 0);
+#if 0
+		status = write_reg(ci, 0x09, 0x4D); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
+		if (status < 0)
+			break;
 #endif
+		status = write_reg(ci, 0x0A, 0xA7); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */
+		if (status < 0)
+			break;
+
+		status = write_reg(ci, 0x0B, 0x33);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x0C, 0x33);
+		if (status < 0)
+			break;
+
+		status = write_regm(ci, 0x14, 0x00, 0x0F);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x15, ci->clk_reg_b);
+		if (status < 0)
+			break;
+		status = write_regm(ci, 0x16, 0x00, 0x0F);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x17, ci->clk_reg_f);
+		if (status < 0)
+			break;
+
+		if (ci->cfg.clock_mode) {
+			if (ci->cfg.polarity) {
+				status = write_reg(ci, 0x09, 0x6f);
+				if (status < 0)
+					break;
+			} else {
+				status = write_reg(ci, 0x09, 0x6d);
+				if (status < 0)
+					break;
+			}
+			status = write_reg(ci, 0x20, 0x68);
+			if (status < 0)
+				break;
+			status = write_reg(ci, 0x21, 0x00);
+			if (status < 0)
+				break;
+			status = write_reg(ci, 0x22, 0x02);
+			if (status < 0)
+				break;
+		} else {
+			if (ci->cfg.polarity) {
+				status = write_reg(ci, 0x09, 0x4f);
+				if (status < 0)
+					break;
+			} else {
+				status = write_reg(ci, 0x09, 0x4d);
+				if (status < 0)
+					break;
+			}
+
+			status = write_reg(ci, 0x20, 0x28);
+			if (status < 0)
+				break;
+			status = write_reg(ci, 0x21, 0x00);
+			if (status < 0)
+				break;
+			status = write_reg(ci, 0x22, 0x07);
+			if (status < 0)
+				break;
+		}
+
+		status = write_regm(ci, 0x20, 0x80, 0x80);
+		if (status < 0)
+			break;
+		status = write_regm(ci, 0x03, 0x02, 0x02);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x01, 0x04);
+		if (status < 0)
+			break;
+		status = write_reg(ci, 0x00, 0x31);
+		if (status < 0)
+			break;
+
+		/* Put TS in bypass */
+		status = write_regm(ci, 0x09, 0x08, 0x08);
+		if (status < 0)
+			break;
+		ci->cammode = -1;
+		cam_mode(ci, 0);
 	} while (0);
 	mutex_unlock(&ci->lock);
 
 	return 0;
 }
 
-
 static int read_attribute_mem(struct dvb_ca_en50221 *ca,
 			      int slot, int address)
 {
 	struct cxd *ci = ca->data;
+#if 0
+	if (ci->amem_read) {
+		if (address <= 0 || address > 1024)
+			return -EIO;
+		return ci->amem[address];
+	}
+
+	mutex_lock(&ci->lock);
+	write_regm(ci, 0x06, 0x00, 0x05);
+	read_pccard(ci, 0, &ci->amem[0], 128);
+	read_pccard(ci, 128, &ci->amem[0], 128);
+	read_pccard(ci, 256, &ci->amem[0], 128);
+	read_pccard(ci, 384, &ci->amem[0], 128);
+	write_regm(ci, 0x06, 0x05, 0x05);
+	mutex_unlock(&ci->lock);
+	return ci->amem[address];
+#else
 	u8 val;
 	mutex_lock(&ci->lock);
 	set_mode(ci, 1);
 	read_pccard(ci, address, &val, 1);
 	mutex_unlock(&ci->lock);
+	/* printk(KERN_INFO "%02x:%02x\n", address,val); */
 	return val;
+#endif
 }
 
-
 static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
 			       int address, u8 value)
 {
@@ -372,6 +496,15 @@
 	struct cxd *ci = ca->data;
 
 	mutex_lock(&ci->lock);
+#if 0
+	write_reg(ci, 0x00, 0x21);
+	write_reg(ci, 0x06, 0x1F);
+	write_reg(ci, 0x00, 0x31);
+#else
+#if 0
+	write_reg(ci, 0x06, 0x1F);
+	write_reg(ci, 0x06, 0x2F);
+#else
 	cam_mode(ci, 0);
 	write_reg(ci, 0x00, 0x21);
 	write_reg(ci, 0x06, 0x1F);
@@ -379,13 +512,25 @@
 	write_regm(ci, 0x20, 0x80, 0x80);
 	write_reg(ci, 0x03, 0x02);
 	ci->ready = 0;
+#endif
+#endif
 	ci->mode = -1;
 	{
 		int i;
+#if 0
+		u8 val;
+#endif
 		for (i = 0; i < 100; i++) {
 			msleep(10);
+#if 0
+			read_reg(ci, 0x06, &val);
+			printk(KERN_INFO "%d:%02x\n", i, val);
+			if (!(val&0x10))
+				break;
+#else
 			if (ci->ready)
 				break;
+#endif
 		}
 	}
 	mutex_unlock(&ci->lock);
@@ -399,12 +544,12 @@
 
 	printk(KERN_INFO "slot_shutdown\n");
 	mutex_lock(&ci->lock);
-	/* write_regm(ci, 0x09, 0x08, 0x08); */
-	write_regm(ci, 0x20, 0x80, 0x80);
-	write_regm(ci, 0x06, 0x07, 0x07);
+	write_regm(ci, 0x09, 0x08, 0x08);
+	write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
+	write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
 	ci->mode = -1;
 	mutex_unlock(&ci->lock);
-	return 0; /* shutdown(ci); */
+	return 0;
 }
 
 static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
@@ -459,7 +604,6 @@
 		if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
 			ci->ready = 1;
 			ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
-			printk(KERN_INFO "READY\n");
 		}
 	}
 	return 0;
@@ -510,7 +654,7 @@
 	struct cxd *ci = ca->data;
 
 	mutex_lock(&ci->lock);
-	printk(KERN_INFO "write_data %d\n", ecount);
+	printk(kern_INFO "write_data %d\n", ecount);
 	write_reg(ci, 0x0d, ecount>>8);
 	write_reg(ci, 0x0e, ecount&0xff);
 	write_block(ci, 0x11, ebuf, ecount);
@@ -535,15 +679,15 @@
 
 };
 
-struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv,
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+				      void *priv,
 				      struct i2c_adapter *i2c)
 {
 	struct cxd *ci = 0;
-	u32 bitrate = 62000000;
 	u8 val;
 
-	if (i2c_read_reg(i2c, adr, 0, &val) < 0) {
-		printk(KERN_ERR "No CXD2099 detected at %02x\n", adr);
+	if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
+		printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr);
 		return 0;
 	}
 
@@ -553,21 +697,20 @@
 	memset(ci, 0, sizeof(*ci));
 
 	mutex_init(&ci->lock);
+	memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg));
 	ci->i2c = i2c;
-	ci->adr = adr;
 	ci->lastaddress = 0xff;
 	ci->clk_reg_b = 0x4a;
 	ci->clk_reg_f = 0x1b;
-	ci->bitrate = bitrate;
 
 	memcpy(&ci->en, &en_templ, sizeof(en_templ));
 	ci->en.data = ci;
 	init(ci);
-	printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->adr);
+	printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr);
 	return &ci->en;
 }
 EXPORT_SYMBOL(cxd2099_attach);
 
 MODULE_DESCRIPTION("cxd2099");
-MODULE_AUTHOR("Ralph Metzler <rjkm@metzlerbros.de>");
+MODULE_AUTHOR("Ralph Metzler");
 MODULE_LICENSE("GPL");
diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h
index bed54ff..19c588a 100644
--- a/drivers/staging/cxd2099/cxd2099.h
+++ b/drivers/staging/cxd2099/cxd2099.h
@@ -1,7 +1,7 @@
 /*
  * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
  *
- * Copyright (C) 2010 DigitalDevices UG
+ * Copyright (C) 2010-2011 Digital Devices GmbH
  *
  *
  * This program is free software; you can redistribute it and/or
@@ -27,11 +27,21 @@
 
 #include <dvb_ca_en50221.h>
 
+struct cxd2099_cfg {
+	u32 bitrate;
+	u8  adr;
+	u8  polarity:1;
+	u8  clock_mode:1;
+};
+
 #if defined(CONFIG_DVB_CXD2099) || \
-        (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
-struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c);
+	(defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+				      void *priv, struct i2c_adapter *i2c);
 #else
-static inline struct dvb_ca_en50221 *cxd2099_attach(u8 adr, void *priv, struct i2c_adapter *i2c)
+
+static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+					void *priv, struct i2c_adapter *i2c)
 {
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
diff --git a/drivers/staging/gma500/mrst_hdmi.c b/drivers/staging/gma500/mrst_hdmi.c
index d6a5179..e66607e 100644
--- a/drivers/staging/gma500/mrst_hdmi.c
+++ b/drivers/staging/gma500/mrst_hdmi.c
@@ -129,7 +129,7 @@
 {
 	/* FIXME: Can we do this as a sleep ? */
 	/* Wait for 20ms, i.e. one cycle at 50hz. */
-	udelay(20000);
+	mdelay(20);
 }
 
 static void scu_busy_loop(void *scu_base)
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 0f22f0f..1a7c19a 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -42,7 +42,7 @@
 #include <net/xfrm.h>
 #endif /* CONFIG_XFRM */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <asm/octeon/octeon.h>
 
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 6227571..b445cd6 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -38,7 +38,7 @@
 #include <net/xfrm.h>
 #endif /* CONFIG_XFRM */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <asm/octeon/octeon.h>
 
diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/solo6x10/solo6x10.h
index fd59b09..17c06bd 100644
--- a/drivers/staging/solo6x10/solo6x10.h
+++ b/drivers/staging/solo6x10/solo6x10.h
@@ -29,7 +29,7 @@
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-dev.h>
 #include <media/videobuf-core.h>
diff --git a/drivers/staging/tidspbridge/include/dspbridge/host_os.h b/drivers/staging/tidspbridge/include/dspbridge/host_os.h
index 1a38896..a2f31c6 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/host_os.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/host_os.h
@@ -18,7 +18,7 @@
 #define _HOST_OS_H_
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/semaphore.h>
 #include <linux/uaccess.h>
 #include <linux/irq.h>
diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c
index ddfd7c3..bd5fa89 100644
--- a/drivers/staging/tm6000/tm6000-alsa.c
+++ b/drivers/staging/tm6000/tm6000-alsa.c
@@ -84,7 +84,6 @@
 
 	tm6000_set_audio_bitrate(core, 48000);
 
-
 	return 0;
 }
 
@@ -123,6 +122,7 @@
 	if (substream->runtime->dma_area) {
 		if (substream->runtime->dma_bytes > size)
 			return 0;
+
 		dsp_buffer_free(substream);
 	}
 
@@ -152,9 +152,9 @@
 		SNDRV_PCM_INFO_MMAP_VALID,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 
-	.rates =		SNDRV_PCM_RATE_CONTINUOUS,
-	.rate_min =		48000,
-	.rate_max =		48000,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 48000,
+	.rate_max = 48000,
 	.channels_min = 2,
 	.channels_max = 2,
 	.period_bytes_min = 64,
@@ -254,9 +254,7 @@
 		memcpy(runtime->dma_area + buf_pos * stride, buf,
 			length * stride);
 
-#ifndef NO_PCM_LOCK
 	snd_pcm_stream_lock(substream);
-#endif
 
 	chip->buf_pos += length;
 	if (chip->buf_pos >= runtime->buffer_size)
@@ -268,9 +266,7 @@
 		period_elapsed = 1;
 	}
 
-#ifndef NO_PCM_LOCK
 	snd_pcm_stream_unlock(substream);
-#endif
 
 	if (period_elapsed)
 		snd_pcm_period_elapsed(substream);
diff --git a/drivers/staging/winbond/mds_s.h b/drivers/staging/winbond/mds_s.h
index eeedf01..07d835b 100644
--- a/drivers/staging/winbond/mds_s.h
+++ b/drivers/staging/winbond/mds_s.h
@@ -3,7 +3,7 @@
 
 #include <linux/timer.h>
 #include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "localpara.h"
 #include "mac_structures.h"
diff --git a/drivers/staging/winbond/wb35reg_s.h b/drivers/staging/winbond/wb35reg_s.h
index eb274ff..dc79faa 100644
--- a/drivers/staging/winbond/wb35reg_s.h
+++ b/drivers/staging/winbond/wb35reg_s.h
@@ -3,7 +3,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct hw_data;
 
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 5cb0f0ef6..b28794b 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -31,5 +31,6 @@
 
 source "drivers/target/loopback/Kconfig"
 source "drivers/target/tcm_fc/Kconfig"
+source "drivers/target/iscsi/Kconfig"
 
 endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 21df808..1060c7b 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -24,5 +24,5 @@
 
 # Fabric modules
 obj-$(CONFIG_LOOPBACK_TARGET)	+= loopback/
-
 obj-$(CONFIG_TCM_FC)		+= tcm_fc/
+obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
diff --git a/drivers/target/iscsi/Kconfig b/drivers/target/iscsi/Kconfig
new file mode 100644
index 0000000..564ff4e
--- /dev/null
+++ b/drivers/target/iscsi/Kconfig
@@ -0,0 +1,8 @@
+config ISCSI_TARGET
+	tristate "Linux-iSCSI.org iSCSI Target Mode Stack"
+	select CRYPTO
+	select CRYPTO_CRC32C
+	select CRYPTO_CRC32C_INTEL if X86
+	help
+	Say M here to enable the ConfigFS enabled Linux-iSCSI.org iSCSI
+	Target Mode Stack.
diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile
new file mode 100644
index 0000000..5b9a2cf
--- /dev/null
+++ b/drivers/target/iscsi/Makefile
@@ -0,0 +1,20 @@
+iscsi_target_mod-y +=		iscsi_target_parameters.o \
+				iscsi_target_seq_pdu_list.o \
+				iscsi_target_tq.o \
+				iscsi_target_auth.o \
+				iscsi_target_datain_values.o \
+				iscsi_target_device.o \
+				iscsi_target_erl0.o \
+				iscsi_target_erl1.o \
+				iscsi_target_erl2.o \
+				iscsi_target_login.o \
+				iscsi_target_nego.o \
+				iscsi_target_nodeattrib.o \
+				iscsi_target_tmr.o \
+				iscsi_target_tpg.o \
+				iscsi_target_util.o \
+				iscsi_target.o \
+				iscsi_target_configfs.o \
+				iscsi_target_stat.o
+
+obj-$(CONFIG_ISCSI_TARGET)	+= iscsi_target_mod.o
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
new file mode 100644
index 0000000..14c81c4
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -0,0 +1,4559 @@
+/*******************************************************************************
+ * This file contains main functions related to the iSCSI Target Core Driver.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/string.h>
+#include <linux/kthread.h>
+#include <linux/crypto.h>
+#include <linux/completion.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi_device.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_tmr.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_configfs.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_tmr.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_stat.h"
+
+static LIST_HEAD(g_tiqn_list);
+static LIST_HEAD(g_np_list);
+static DEFINE_SPINLOCK(tiqn_lock);
+static DEFINE_SPINLOCK(np_lock);
+
+static struct idr tiqn_idr;
+struct idr sess_idr;
+struct mutex auth_id_lock;
+spinlock_t sess_idr_lock;
+
+struct iscsit_global *iscsit_global;
+
+struct kmem_cache *lio_cmd_cache;
+struct kmem_cache *lio_qr_cache;
+struct kmem_cache *lio_dr_cache;
+struct kmem_cache *lio_ooo_cache;
+struct kmem_cache *lio_r2t_cache;
+
+static int iscsit_handle_immediate_data(struct iscsi_cmd *,
+			unsigned char *buf, u32);
+static int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *);
+
+struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf)
+{
+	struct iscsi_tiqn *tiqn = NULL;
+
+	spin_lock(&tiqn_lock);
+	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
+		if (!strcmp(tiqn->tiqn, buf)) {
+
+			spin_lock(&tiqn->tiqn_state_lock);
+			if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
+				tiqn->tiqn_access_count++;
+				spin_unlock(&tiqn->tiqn_state_lock);
+				spin_unlock(&tiqn_lock);
+				return tiqn;
+			}
+			spin_unlock(&tiqn->tiqn_state_lock);
+		}
+	}
+	spin_unlock(&tiqn_lock);
+
+	return NULL;
+}
+
+static int iscsit_set_tiqn_shutdown(struct iscsi_tiqn *tiqn)
+{
+	spin_lock(&tiqn->tiqn_state_lock);
+	if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
+		tiqn->tiqn_state = TIQN_STATE_SHUTDOWN;
+		spin_unlock(&tiqn->tiqn_state_lock);
+		return 0;
+	}
+	spin_unlock(&tiqn->tiqn_state_lock);
+
+	return -1;
+}
+
+void iscsit_put_tiqn_for_login(struct iscsi_tiqn *tiqn)
+{
+	spin_lock(&tiqn->tiqn_state_lock);
+	tiqn->tiqn_access_count--;
+	spin_unlock(&tiqn->tiqn_state_lock);
+}
+
+/*
+ * Note that IQN formatting is expected to be done in userspace, and
+ * no explict IQN format checks are done here.
+ */
+struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf)
+{
+	struct iscsi_tiqn *tiqn = NULL;
+	int ret;
+
+	if (strlen(buf) > ISCSI_IQN_LEN) {
+		pr_err("Target IQN exceeds %d bytes\n",
+				ISCSI_IQN_LEN);
+		return ERR_PTR(-EINVAL);
+	}
+
+	tiqn = kzalloc(sizeof(struct iscsi_tiqn), GFP_KERNEL);
+	if (!tiqn) {
+		pr_err("Unable to allocate struct iscsi_tiqn\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sprintf(tiqn->tiqn, "%s", buf);
+	INIT_LIST_HEAD(&tiqn->tiqn_list);
+	INIT_LIST_HEAD(&tiqn->tiqn_tpg_list);
+	spin_lock_init(&tiqn->tiqn_state_lock);
+	spin_lock_init(&tiqn->tiqn_tpg_lock);
+	spin_lock_init(&tiqn->sess_err_stats.lock);
+	spin_lock_init(&tiqn->login_stats.lock);
+	spin_lock_init(&tiqn->logout_stats.lock);
+
+	if (!idr_pre_get(&tiqn_idr, GFP_KERNEL)) {
+		pr_err("idr_pre_get() for tiqn_idr failed\n");
+		kfree(tiqn);
+		return ERR_PTR(-ENOMEM);
+	}
+	tiqn->tiqn_state = TIQN_STATE_ACTIVE;
+
+	spin_lock(&tiqn_lock);
+	ret = idr_get_new(&tiqn_idr, NULL, &tiqn->tiqn_index);
+	if (ret < 0) {
+		pr_err("idr_get_new() failed for tiqn->tiqn_index\n");
+		spin_unlock(&tiqn_lock);
+		kfree(tiqn);
+		return ERR_PTR(ret);
+	}
+	list_add_tail(&tiqn->tiqn_list, &g_tiqn_list);
+	spin_unlock(&tiqn_lock);
+
+	pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn);
+
+	return tiqn;
+
+}
+
+static void iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn)
+{
+	/*
+	 * Wait for accesses to said struct iscsi_tiqn to end.
+	 */
+	spin_lock(&tiqn->tiqn_state_lock);
+	while (tiqn->tiqn_access_count != 0) {
+		spin_unlock(&tiqn->tiqn_state_lock);
+		msleep(10);
+		spin_lock(&tiqn->tiqn_state_lock);
+	}
+	spin_unlock(&tiqn->tiqn_state_lock);
+}
+
+void iscsit_del_tiqn(struct iscsi_tiqn *tiqn)
+{
+	/*
+	 * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN
+	 * while holding tiqn->tiqn_state_lock.  This means that all subsequent
+	 * attempts to access this struct iscsi_tiqn will fail from both transport
+	 * fabric and control code paths.
+	 */
+	if (iscsit_set_tiqn_shutdown(tiqn) < 0) {
+		pr_err("iscsit_set_tiqn_shutdown() failed\n");
+		return;
+	}
+
+	iscsit_wait_for_tiqn(tiqn);
+
+	spin_lock(&tiqn_lock);
+	list_del(&tiqn->tiqn_list);
+	idr_remove(&tiqn_idr, tiqn->tiqn_index);
+	spin_unlock(&tiqn_lock);
+
+	pr_debug("CORE[0] - Deleted iSCSI Target IQN: %s\n",
+			tiqn->tiqn);
+	kfree(tiqn);
+}
+
+int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
+{
+	int ret;
+	/*
+	 * Determine if the network portal is accepting storage traffic.
+	 */
+	spin_lock_bh(&np->np_thread_lock);
+	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+		spin_unlock_bh(&np->np_thread_lock);
+		return -1;
+	}
+	if (np->np_login_tpg) {
+		pr_err("np->np_login_tpg() is not NULL!\n");
+		spin_unlock_bh(&np->np_thread_lock);
+		return -1;
+	}
+	spin_unlock_bh(&np->np_thread_lock);
+	/*
+	 * Determine if the portal group is accepting storage traffic.
+	 */
+	spin_lock_bh(&tpg->tpg_state_lock);
+	if (tpg->tpg_state != TPG_STATE_ACTIVE) {
+		spin_unlock_bh(&tpg->tpg_state_lock);
+		return -1;
+	}
+	spin_unlock_bh(&tpg->tpg_state_lock);
+
+	/*
+	 * Here we serialize access across the TIQN+TPG Tuple.
+	 */
+	ret = mutex_lock_interruptible(&tpg->np_login_lock);
+	if ((ret != 0) || signal_pending(current))
+		return -1;
+
+	spin_lock_bh(&np->np_thread_lock);
+	np->np_login_tpg = tpg;
+	spin_unlock_bh(&np->np_thread_lock);
+
+	return 0;
+}
+
+int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
+{
+	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+	spin_lock_bh(&np->np_thread_lock);
+	np->np_login_tpg = NULL;
+	spin_unlock_bh(&np->np_thread_lock);
+
+	mutex_unlock(&tpg->np_login_lock);
+
+	if (tiqn)
+		iscsit_put_tiqn_for_login(tiqn);
+
+	return 0;
+}
+
+static struct iscsi_np *iscsit_get_np(
+	struct __kernel_sockaddr_storage *sockaddr,
+	int network_transport)
+{
+	struct sockaddr_in *sock_in, *sock_in_e;
+	struct sockaddr_in6 *sock_in6, *sock_in6_e;
+	struct iscsi_np *np;
+	int ip_match = 0;
+	u16 port;
+
+	spin_lock_bh(&np_lock);
+	list_for_each_entry(np, &g_np_list, np_list) {
+		spin_lock(&np->np_thread_lock);
+		if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+			spin_unlock(&np->np_thread_lock);
+			continue;
+		}
+
+		if (sockaddr->ss_family == AF_INET6) {
+			sock_in6 = (struct sockaddr_in6 *)sockaddr;
+			sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr;
+
+			if (!memcmp((void *)&sock_in6->sin6_addr.in6_u,
+				    (void *)&sock_in6_e->sin6_addr.in6_u,
+				    sizeof(struct in6_addr)))
+				ip_match = 1;
+
+			port = ntohs(sock_in6->sin6_port);
+		} else {
+			sock_in = (struct sockaddr_in *)sockaddr;
+			sock_in_e = (struct sockaddr_in *)&np->np_sockaddr;
+
+			if (sock_in->sin_addr.s_addr ==
+			    sock_in_e->sin_addr.s_addr)
+				ip_match = 1;
+
+			port = ntohs(sock_in->sin_port);
+		}
+
+		if ((ip_match == 1) && (np->np_port == port) &&
+		    (np->np_network_transport == network_transport)) {
+			/*
+			 * Increment the np_exports reference count now to
+			 * prevent iscsit_del_np() below from being called
+			 * while iscsi_tpg_add_network_portal() is called.
+			 */
+			np->np_exports++;
+			spin_unlock(&np->np_thread_lock);
+			spin_unlock_bh(&np_lock);
+			return np;
+		}
+		spin_unlock(&np->np_thread_lock);
+	}
+	spin_unlock_bh(&np_lock);
+
+	return NULL;
+}
+
+struct iscsi_np *iscsit_add_np(
+	struct __kernel_sockaddr_storage *sockaddr,
+	char *ip_str,
+	int network_transport)
+{
+	struct sockaddr_in *sock_in;
+	struct sockaddr_in6 *sock_in6;
+	struct iscsi_np *np;
+	int ret;
+	/*
+	 * Locate the existing struct iscsi_np if already active..
+	 */
+	np = iscsit_get_np(sockaddr, network_transport);
+	if (np)
+		return np;
+
+	np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL);
+	if (!np) {
+		pr_err("Unable to allocate memory for struct iscsi_np\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	np->np_flags |= NPF_IP_NETWORK;
+	if (sockaddr->ss_family == AF_INET6) {
+		sock_in6 = (struct sockaddr_in6 *)sockaddr;
+		snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str);
+		np->np_port = ntohs(sock_in6->sin6_port);
+	} else {
+		sock_in = (struct sockaddr_in *)sockaddr;
+		sprintf(np->np_ip, "%s", ip_str);
+		np->np_port = ntohs(sock_in->sin_port);
+	}
+
+	np->np_network_transport = network_transport;
+	spin_lock_init(&np->np_thread_lock);
+	init_completion(&np->np_restart_comp);
+	INIT_LIST_HEAD(&np->np_list);
+
+	ret = iscsi_target_setup_login_socket(np, sockaddr);
+	if (ret != 0) {
+		kfree(np);
+		return ERR_PTR(ret);
+	}
+
+	np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np");
+	if (IS_ERR(np->np_thread)) {
+		pr_err("Unable to create kthread: iscsi_np\n");
+		ret = PTR_ERR(np->np_thread);
+		kfree(np);
+		return ERR_PTR(ret);
+	}
+	/*
+	 * Increment the np_exports reference count now to prevent
+	 * iscsit_del_np() below from being run while a new call to
+	 * iscsi_tpg_add_network_portal() for a matching iscsi_np is
+	 * active.  We don't need to hold np->np_thread_lock at this
+	 * point because iscsi_np has not been added to g_np_list yet.
+	 */
+	np->np_exports = 1;
+
+	spin_lock_bh(&np_lock);
+	list_add_tail(&np->np_list, &g_np_list);
+	spin_unlock_bh(&np_lock);
+
+	pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n",
+		np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ?
+		"TCP" : "SCTP");
+
+	return np;
+}
+
+int iscsit_reset_np_thread(
+	struct iscsi_np *np,
+	struct iscsi_tpg_np *tpg_np,
+	struct iscsi_portal_group *tpg)
+{
+	spin_lock_bh(&np->np_thread_lock);
+	if (tpg && tpg_np) {
+		/*
+		 * The reset operation need only be performed when the
+		 * passed struct iscsi_portal_group has a login in progress
+		 * to one of the network portals.
+		 */
+		if (tpg_np->tpg_np->np_login_tpg != tpg) {
+			spin_unlock_bh(&np->np_thread_lock);
+			return 0;
+		}
+	}
+	if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) {
+		spin_unlock_bh(&np->np_thread_lock);
+		return 0;
+	}
+	np->np_thread_state = ISCSI_NP_THREAD_RESET;
+
+	if (np->np_thread) {
+		spin_unlock_bh(&np->np_thread_lock);
+		send_sig(SIGINT, np->np_thread, 1);
+		wait_for_completion(&np->np_restart_comp);
+		spin_lock_bh(&np->np_thread_lock);
+	}
+	spin_unlock_bh(&np->np_thread_lock);
+
+	return 0;
+}
+
+int iscsit_del_np_comm(struct iscsi_np *np)
+{
+	if (!np->np_socket)
+		return 0;
+
+	/*
+	 * Some network transports allocate their own struct sock->file,
+	 * see  if we need to free any additional allocated resources.
+	 */
+	if (np->np_flags & NPF_SCTP_STRUCT_FILE) {
+		kfree(np->np_socket->file);
+		np->np_socket->file = NULL;
+	}
+
+	sock_release(np->np_socket);
+	return 0;
+}
+
+int iscsit_del_np(struct iscsi_np *np)
+{
+	spin_lock_bh(&np->np_thread_lock);
+	np->np_exports--;
+	if (np->np_exports) {
+		spin_unlock_bh(&np->np_thread_lock);
+		return 0;
+	}
+	np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN;
+	spin_unlock_bh(&np->np_thread_lock);
+
+	if (np->np_thread) {
+		/*
+		 * We need to send the signal to wakeup Linux/Net
+		 * which may be sleeping in sock_accept()..
+		 */
+		send_sig(SIGINT, np->np_thread, 1);
+		kthread_stop(np->np_thread);
+	}
+	iscsit_del_np_comm(np);
+
+	spin_lock_bh(&np_lock);
+	list_del(&np->np_list);
+	spin_unlock_bh(&np_lock);
+
+	pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n",
+		np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ?
+		"TCP" : "SCTP");
+
+	kfree(np);
+	return 0;
+}
+
+static int __init iscsi_target_init_module(void)
+{
+	int ret = 0;
+
+	pr_debug("iSCSI-Target "ISCSIT_VERSION"\n");
+
+	iscsit_global = kzalloc(sizeof(struct iscsit_global), GFP_KERNEL);
+	if (!iscsit_global) {
+		pr_err("Unable to allocate memory for iscsit_global\n");
+		return -1;
+	}
+	mutex_init(&auth_id_lock);
+	spin_lock_init(&sess_idr_lock);
+	idr_init(&tiqn_idr);
+	idr_init(&sess_idr);
+
+	ret = iscsi_target_register_configfs();
+	if (ret < 0)
+		goto out;
+
+	ret = iscsi_thread_set_init();
+	if (ret < 0)
+		goto configfs_out;
+
+	if (iscsi_allocate_thread_sets(TARGET_THREAD_SET_COUNT) !=
+			TARGET_THREAD_SET_COUNT) {
+		pr_err("iscsi_allocate_thread_sets() returned"
+			" unexpected value!\n");
+		goto ts_out1;
+	}
+
+	lio_cmd_cache = kmem_cache_create("lio_cmd_cache",
+			sizeof(struct iscsi_cmd), __alignof__(struct iscsi_cmd),
+			0, NULL);
+	if (!lio_cmd_cache) {
+		pr_err("Unable to kmem_cache_create() for"
+				" lio_cmd_cache\n");
+		goto ts_out2;
+	}
+
+	lio_qr_cache = kmem_cache_create("lio_qr_cache",
+			sizeof(struct iscsi_queue_req),
+			__alignof__(struct iscsi_queue_req), 0, NULL);
+	if (!lio_qr_cache) {
+		pr_err("nable to kmem_cache_create() for"
+				" lio_qr_cache\n");
+		goto cmd_out;
+	}
+
+	lio_dr_cache = kmem_cache_create("lio_dr_cache",
+			sizeof(struct iscsi_datain_req),
+			__alignof__(struct iscsi_datain_req), 0, NULL);
+	if (!lio_dr_cache) {
+		pr_err("Unable to kmem_cache_create() for"
+				" lio_dr_cache\n");
+		goto qr_out;
+	}
+
+	lio_ooo_cache = kmem_cache_create("lio_ooo_cache",
+			sizeof(struct iscsi_ooo_cmdsn),
+			__alignof__(struct iscsi_ooo_cmdsn), 0, NULL);
+	if (!lio_ooo_cache) {
+		pr_err("Unable to kmem_cache_create() for"
+				" lio_ooo_cache\n");
+		goto dr_out;
+	}
+
+	lio_r2t_cache = kmem_cache_create("lio_r2t_cache",
+			sizeof(struct iscsi_r2t), __alignof__(struct iscsi_r2t),
+			0, NULL);
+	if (!lio_r2t_cache) {
+		pr_err("Unable to kmem_cache_create() for"
+				" lio_r2t_cache\n");
+		goto ooo_out;
+	}
+
+	if (iscsit_load_discovery_tpg() < 0)
+		goto r2t_out;
+
+	return ret;
+r2t_out:
+	kmem_cache_destroy(lio_r2t_cache);
+ooo_out:
+	kmem_cache_destroy(lio_ooo_cache);
+dr_out:
+	kmem_cache_destroy(lio_dr_cache);
+qr_out:
+	kmem_cache_destroy(lio_qr_cache);
+cmd_out:
+	kmem_cache_destroy(lio_cmd_cache);
+ts_out2:
+	iscsi_deallocate_thread_sets();
+ts_out1:
+	iscsi_thread_set_free();
+configfs_out:
+	iscsi_target_deregister_configfs();
+out:
+	kfree(iscsit_global);
+	return -ENOMEM;
+}
+
+static void __exit iscsi_target_cleanup_module(void)
+{
+	iscsi_deallocate_thread_sets();
+	iscsi_thread_set_free();
+	iscsit_release_discovery_tpg();
+	kmem_cache_destroy(lio_cmd_cache);
+	kmem_cache_destroy(lio_qr_cache);
+	kmem_cache_destroy(lio_dr_cache);
+	kmem_cache_destroy(lio_ooo_cache);
+	kmem_cache_destroy(lio_r2t_cache);
+
+	iscsi_target_deregister_configfs();
+
+	kfree(iscsit_global);
+}
+
+int iscsit_add_reject(
+	u8 reason,
+	int fail_conn,
+	unsigned char *buf,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_cmd *cmd;
+	struct iscsi_reject *hdr;
+	int ret;
+
+	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+	if (!cmd)
+		return -1;
+
+	cmd->iscsi_opcode = ISCSI_OP_REJECT;
+	if (fail_conn)
+		cmd->cmd_flags |= ICF_REJECT_FAIL_CONN;
+
+	hdr	= (struct iscsi_reject *) cmd->pdu;
+	hdr->reason = reason;
+
+	cmd->buf_ptr = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
+	if (!cmd->buf_ptr) {
+		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
+		iscsit_release_cmd(cmd);
+		return -1;
+	}
+	memcpy(cmd->buf_ptr, buf, ISCSI_HDR_LEN);
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	cmd->i_state = ISTATE_SEND_REJECT;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	ret = wait_for_completion_interruptible(&cmd->reject_comp);
+	if (ret != 0)
+		return -1;
+
+	return (!fail_conn) ? 0 : -1;
+}
+
+int iscsit_add_reject_from_cmd(
+	u8 reason,
+	int fail_conn,
+	int add_to_conn,
+	unsigned char *buf,
+	struct iscsi_cmd *cmd)
+{
+	struct iscsi_conn *conn;
+	struct iscsi_reject *hdr;
+	int ret;
+
+	if (!cmd->conn) {
+		pr_err("cmd->conn is NULL for ITT: 0x%08x\n",
+				cmd->init_task_tag);
+		return -1;
+	}
+	conn = cmd->conn;
+
+	cmd->iscsi_opcode = ISCSI_OP_REJECT;
+	if (fail_conn)
+		cmd->cmd_flags |= ICF_REJECT_FAIL_CONN;
+
+	hdr	= (struct iscsi_reject *) cmd->pdu;
+	hdr->reason = reason;
+
+	cmd->buf_ptr = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
+	if (!cmd->buf_ptr) {
+		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
+		iscsit_release_cmd(cmd);
+		return -1;
+	}
+	memcpy(cmd->buf_ptr, buf, ISCSI_HDR_LEN);
+
+	if (add_to_conn) {
+		spin_lock_bh(&conn->cmd_lock);
+		list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+		spin_unlock_bh(&conn->cmd_lock);
+	}
+
+	cmd->i_state = ISTATE_SEND_REJECT;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	ret = wait_for_completion_interruptible(&cmd->reject_comp);
+	if (ret != 0)
+		return -1;
+
+	return (!fail_conn) ? 0 : -1;
+}
+
+/*
+ * Map some portion of the allocated scatterlist to an iovec, suitable for
+ * kernel sockets to copy data in/out. This handles both pages and slab-allocated
+ * buffers, since we have been tricky and mapped t_mem_sg to the buffer in
+ * either case (see iscsit_alloc_buffs)
+ */
+static int iscsit_map_iovec(
+	struct iscsi_cmd *cmd,
+	struct kvec *iov,
+	u32 data_offset,
+	u32 data_length)
+{
+	u32 i = 0;
+	struct scatterlist *sg;
+	unsigned int page_off;
+
+	/*
+	 * We have a private mapping of the allocated pages in t_mem_sg.
+	 * At this point, we also know each contains a page.
+	 */
+	sg = &cmd->t_mem_sg[data_offset / PAGE_SIZE];
+	page_off = (data_offset % PAGE_SIZE);
+
+	cmd->first_data_sg = sg;
+	cmd->first_data_sg_off = page_off;
+
+	while (data_length) {
+		u32 cur_len = min_t(u32, data_length, sg->length - page_off);
+
+		iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off;
+		iov[i].iov_len = cur_len;
+
+		data_length -= cur_len;
+		page_off = 0;
+		sg = sg_next(sg);
+		i++;
+	}
+
+	cmd->kmapped_nents = i;
+
+	return i;
+}
+
+static void iscsit_unmap_iovec(struct iscsi_cmd *cmd)
+{
+	u32 i;
+	struct scatterlist *sg;
+
+	sg = cmd->first_data_sg;
+
+	for (i = 0; i < cmd->kmapped_nents; i++)
+		kunmap(sg_page(&sg[i]));
+}
+
+static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)
+{
+	struct iscsi_cmd *cmd;
+
+	conn->exp_statsn = exp_statsn;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+		spin_lock(&cmd->istate_lock);
+		if ((cmd->i_state == ISTATE_SENT_STATUS) &&
+		    (cmd->stat_sn < exp_statsn)) {
+			cmd->i_state = ISTATE_REMOVE;
+			spin_unlock(&cmd->istate_lock);
+			iscsit_add_cmd_to_immediate_queue(cmd, conn,
+						cmd->i_state);
+			continue;
+		}
+		spin_unlock(&cmd->istate_lock);
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+}
+
+static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd)
+{
+	u32 iov_count = (cmd->se_cmd.t_data_nents == 0) ? 1 :
+				cmd->se_cmd.t_data_nents;
+
+	iov_count += TRANSPORT_IOV_DATA_BUFFER;
+
+	cmd->iov_data = kzalloc(iov_count * sizeof(struct kvec), GFP_KERNEL);
+	if (!cmd->iov_data) {
+		pr_err("Unable to allocate cmd->iov_data\n");
+		return -ENOMEM;
+	}
+
+	cmd->orig_iov_data_count = iov_count;
+	return 0;
+}
+
+static int iscsit_alloc_buffs(struct iscsi_cmd *cmd)
+{
+	struct scatterlist *sgl;
+	u32 length = cmd->se_cmd.data_length;
+	int nents = DIV_ROUND_UP(length, PAGE_SIZE);
+	int i = 0, ret;
+	/*
+	 * If no SCSI payload is present, allocate the default iovecs used for
+	 * iSCSI PDU Header
+	 */
+	if (!length)
+		return iscsit_allocate_iovecs(cmd);
+
+	sgl = kzalloc(sizeof(*sgl) * nents, GFP_KERNEL);
+	if (!sgl)
+		return -ENOMEM;
+
+	sg_init_table(sgl, nents);
+
+	while (length) {
+		int buf_size = min_t(int, length, PAGE_SIZE);
+		struct page *page;
+
+		page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!page)
+			goto page_alloc_failed;
+
+		sg_set_page(&sgl[i], page, buf_size, 0);
+
+		length -= buf_size;
+		i++;
+	}
+
+	cmd->t_mem_sg = sgl;
+	cmd->t_mem_sg_nents = nents;
+
+	/* BIDI ops not supported */
+
+	/* Tell the core about our preallocated memory */
+	transport_generic_map_mem_to_cmd(&cmd->se_cmd, sgl, nents, NULL, 0);
+	/*
+	 * Allocate iovecs for SCSI payload after transport_generic_map_mem_to_cmd
+	 * so that cmd->se_cmd.t_tasks_se_num has been set.
+	 */
+        ret = iscsit_allocate_iovecs(cmd);
+        if (ret < 0)
+		goto page_alloc_failed;
+
+	return 0;
+
+page_alloc_failed:
+	while (i >= 0) {
+		__free_page(sg_page(&sgl[i]));
+		i--;
+	}
+	kfree(cmd->t_mem_sg);
+	cmd->t_mem_sg = NULL;
+	return -ENOMEM;
+}
+
+static int iscsit_handle_scsi_cmd(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	int	data_direction, cmdsn_ret = 0, immed_ret, ret, transport_ret;
+	int	dump_immediate_data = 0, send_check_condition = 0, payload_length;
+	struct iscsi_cmd	*cmd = NULL;
+	struct iscsi_scsi_req *hdr;
+
+	spin_lock_bh(&conn->sess->session_stats_lock);
+	conn->sess->cmd_pdus++;
+	if (conn->sess->se_sess->se_node_acl) {
+		spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
+		conn->sess->se_sess->se_node_acl->num_cmds++;
+		spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
+	}
+	spin_unlock_bh(&conn->sess->session_stats_lock);
+
+	hdr			= (struct iscsi_scsi_req *) buf;
+	payload_length		= ntoh24(hdr->dlength);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->data_length	= be32_to_cpu(hdr->data_length);
+	hdr->cmdsn		= be32_to_cpu(hdr->cmdsn);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+
+	/* FIXME; Add checks for AdditionalHeaderSegment */
+
+	if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) &&
+	    !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
+		pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"
+				" not set. Bad iSCSI Initiator.\n");
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+				buf, conn);
+	}
+
+	if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
+	     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) {
+		/*
+		 * Vmware ESX v3.0 uses a modified Cisco Initiator (v3.4.2)
+		 * that adds support for RESERVE/RELEASE.  There is a bug
+		 * add with this new functionality that sets R/W bits when
+		 * neither CDB carries any READ or WRITE datapayloads.
+		 */
+		if ((hdr->cdb[0] == 0x16) || (hdr->cdb[0] == 0x17)) {
+			hdr->flags &= ~ISCSI_FLAG_CMD_READ;
+			hdr->flags &= ~ISCSI_FLAG_CMD_WRITE;
+			goto done;
+		}
+
+		pr_err("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"
+			" set when Expected Data Transfer Length is 0 for"
+			" CDB: 0x%02x. Bad iSCSI Initiator.\n", hdr->cdb[0]);
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+				buf, conn);
+	}
+done:
+
+	if (!(hdr->flags & ISCSI_FLAG_CMD_READ) &&
+	    !(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) {
+		pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"
+			" MUST be set if Expected Data Transfer Length is not 0."
+			" Bad iSCSI Initiator\n");
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+				buf, conn);
+	}
+
+	if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
+	    (hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
+		pr_err("Bidirectional operations not supported!\n");
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+				buf, conn);
+	}
+
+	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+		pr_err("Illegally set Immediate Bit in iSCSI Initiator"
+				" Scsi Command PDU.\n");
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+				buf, conn);
+	}
+
+	if (payload_length && !conn->sess->sess_ops->ImmediateData) {
+		pr_err("ImmediateData=No but DataSegmentLength=%u,"
+			" protocol error.\n", payload_length);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+				buf, conn);
+	}
+
+	if ((hdr->data_length == payload_length) &&
+	    (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
+		pr_err("Expected Data Transfer Length and Length of"
+			" Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"
+			" bit is not set protocol error\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+				buf, conn);
+	}
+
+	if (payload_length > hdr->data_length) {
+		pr_err("DataSegmentLength: %u is greater than"
+			" EDTL: %u, protocol error.\n", payload_length,
+				hdr->data_length);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+				buf, conn);
+	}
+
+	if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) {
+		pr_err("DataSegmentLength: %u is greater than"
+			" MaxRecvDataSegmentLength: %u, protocol error.\n",
+			payload_length, conn->conn_ops->MaxRecvDataSegmentLength);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+				buf, conn);
+	}
+
+	if (payload_length > conn->sess->sess_ops->FirstBurstLength) {
+		pr_err("DataSegmentLength: %u is greater than"
+			" FirstBurstLength: %u, protocol error.\n",
+			payload_length, conn->sess->sess_ops->FirstBurstLength);
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1,
+					buf, conn);
+	}
+
+	data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
+			 (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :
+			  DMA_NONE;
+
+	cmd = iscsit_allocate_se_cmd(conn, hdr->data_length, data_direction,
+				(hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK));
+	if (!cmd)
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1,
+					buf, conn);
+
+	pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x,"
+		" ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt,
+		hdr->cmdsn, hdr->data_length, payload_length, conn->cid);
+
+	cmd->iscsi_opcode	= ISCSI_OP_SCSI_CMD;
+	cmd->i_state		= ISTATE_NEW_CMD;
+	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+	cmd->immediate_data	= (payload_length) ? 1 : 0;
+	cmd->unsolicited_data	= ((!(hdr->flags & ISCSI_FLAG_CMD_FINAL) &&
+				     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) ? 1 : 0);
+	if (cmd->unsolicited_data)
+		cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA;
+
+	conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+	if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+		spin_lock_bh(&conn->sess->ttt_lock);
+		cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+		if (cmd->targ_xfer_tag == 0xFFFFFFFF)
+			cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+		spin_unlock_bh(&conn->sess->ttt_lock);
+	} else if (hdr->flags & ISCSI_FLAG_CMD_WRITE)
+		cmd->targ_xfer_tag = 0xFFFFFFFF;
+	cmd->cmd_sn		= hdr->cmdsn;
+	cmd->exp_stat_sn	= hdr->exp_statsn;
+	cmd->first_burst_len	= payload_length;
+
+	if (cmd->data_direction == DMA_FROM_DEVICE) {
+		struct iscsi_datain_req *dr;
+
+		dr = iscsit_allocate_datain_req();
+		if (!dr)
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+					1, 1, buf, cmd);
+
+		iscsit_attach_datain_req(cmd, dr);
+	}
+
+	/*
+	 * The CDB is going to an se_device_t.
+	 */
+	ret = iscsit_get_lun_for_cmd(cmd, hdr->cdb,
+				get_unaligned_le64(&hdr->lun));
+	if (ret < 0) {
+		if (cmd->se_cmd.scsi_sense_reason == TCM_NON_EXISTENT_LUN) {
+			pr_debug("Responding to non-acl'ed,"
+				" non-existent or non-exported iSCSI LUN:"
+				" 0x%016Lx\n", get_unaligned_le64(&hdr->lun));
+		}
+		if (ret == PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES)
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+					1, 1, buf, cmd);
+
+		send_check_condition = 1;
+		goto attach_cmd;
+	}
+	/*
+	 * The Initiator Node has access to the LUN (the addressing method
+	 * is handled inside of iscsit_get_lun_for_cmd()).  Now it's time to
+	 * allocate 1->N transport tasks (depending on sector count and
+	 * maximum request size the physical HBA(s) can handle.
+	 */
+	transport_ret = transport_generic_allocate_tasks(&cmd->se_cmd, hdr->cdb);
+	if (transport_ret == -ENOMEM) {
+		return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+				1, 1, buf, cmd);
+	} else if (transport_ret == -EINVAL) {
+		/*
+		 * Unsupported SAM Opcode.  CHECK_CONDITION will be sent
+		 * in iscsit_execute_cmd() during the CmdSN OOO Execution
+		 * Mechinism.
+		 */
+		send_check_condition = 1;
+	} else {
+		if (iscsit_decide_list_to_build(cmd, payload_length) < 0)
+			return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+				1, 1, buf, cmd);
+	}
+
+attach_cmd:
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+	/*
+	 * Check if we need to delay processing because of ALUA
+	 * Active/NonOptimized primary access state..
+	 */
+	core_alua_check_nonop_delay(&cmd->se_cmd);
+	/*
+	 * Allocate and setup SGL used with transport_generic_map_mem_to_cmd().
+	 * also call iscsit_allocate_iovecs()
+	 */
+	ret = iscsit_alloc_buffs(cmd);
+	if (ret < 0)
+		return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+				1, 1, buf, cmd);
+	/*
+	 * Check the CmdSN against ExpCmdSN/MaxCmdSN here if
+	 * the Immediate Bit is not set, and no Immediate
+	 * Data is attached.
+	 *
+	 * A PDU/CmdSN carrying Immediate Data can only
+	 * be processed after the DataCRC has passed.
+	 * If the DataCRC fails, the CmdSN MUST NOT
+	 * be acknowledged. (See below)
+	 */
+	if (!cmd->immediate_data) {
+		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+			return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_PROTOCOL_ERROR,
+				1, 0, buf, cmd);
+	}
+
+	iscsit_ack_from_expstatsn(conn, hdr->exp_statsn);
+
+	/*
+	 * If no Immediate Data is attached, it's OK to return now.
+	 */
+	if (!cmd->immediate_data) {
+		if (send_check_condition)
+			return 0;
+
+		if (cmd->unsolicited_data) {
+			iscsit_set_dataout_sequence_values(cmd);
+
+			spin_lock_bh(&cmd->dataout_timeout_lock);
+			iscsit_start_dataout_timer(cmd, cmd->conn);
+			spin_unlock_bh(&cmd->dataout_timeout_lock);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * Early CHECK_CONDITIONs never make it to the transport processing
+	 * thread.  They are processed in CmdSN order by
+	 * iscsit_check_received_cmdsn() below.
+	 */
+	if (send_check_condition) {
+		immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
+		dump_immediate_data = 1;
+		goto after_immediate_data;
+	}
+	/*
+	 * Call directly into transport_generic_new_cmd() to perform
+	 * the backend memory allocation.
+	 */
+	ret = transport_generic_new_cmd(&cmd->se_cmd);
+	if ((ret < 0) || (cmd->se_cmd.se_cmd_flags & SCF_SE_CMD_FAILED)) {
+		immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
+		dump_immediate_data = 1;
+		goto after_immediate_data;
+	}
+
+	immed_ret = iscsit_handle_immediate_data(cmd, buf, payload_length);
+after_immediate_data:
+	if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) {
+		/*
+		 * A PDU/CmdSN carrying Immediate Data passed
+		 * DataCRC, check against ExpCmdSN/MaxCmdSN if
+		 * Immediate Bit is not set.
+		 */
+		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		/*
+		 * Special case for Unsupported SAM WRITE Opcodes
+		 * and ImmediateData=Yes.
+		 */
+		if (dump_immediate_data) {
+			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+				return -1;
+		} else if (cmd->unsolicited_data) {
+			iscsit_set_dataout_sequence_values(cmd);
+
+			spin_lock_bh(&cmd->dataout_timeout_lock);
+			iscsit_start_dataout_timer(cmd, cmd->conn);
+			spin_unlock_bh(&cmd->dataout_timeout_lock);
+		}
+
+		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+			return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_PROTOCOL_ERROR,
+				1, 0, buf, cmd);
+
+	} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
+		/*
+		 * Immediate Data failed DataCRC and ERL>=1,
+		 * silently drop this PDU and let the initiator
+		 * plug the CmdSN gap.
+		 *
+		 * FIXME: Send Unsolicited NOPIN with reserved
+		 * TTT here to help the initiator figure out
+		 * the missing CmdSN, although they should be
+		 * intelligent enough to determine the missing
+		 * CmdSN and issue a retry to plug the sequence.
+		 */
+		cmd->i_state = ISTATE_REMOVE;
+		iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state);
+	} else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */
+		return -1;
+
+	return 0;
+}
+
+static u32 iscsit_do_crypto_hash_sg(
+	struct hash_desc *hash,
+	struct iscsi_cmd *cmd,
+	u32 data_offset,
+	u32 data_length,
+	u32 padding,
+	u8 *pad_bytes)
+{
+	u32 data_crc;
+	u32 i;
+	struct scatterlist *sg;
+	unsigned int page_off;
+
+	crypto_hash_init(hash);
+
+	sg = cmd->first_data_sg;
+	page_off = cmd->first_data_sg_off;
+
+	i = 0;
+	while (data_length) {
+		u32 cur_len = min_t(u32, data_length, (sg[i].length - page_off));
+
+		crypto_hash_update(hash, &sg[i], cur_len);
+
+		data_length -= cur_len;
+		page_off = 0;
+		i++;
+	}
+
+	if (padding) {
+		struct scatterlist pad_sg;
+
+		sg_init_one(&pad_sg, pad_bytes, padding);
+		crypto_hash_update(hash, &pad_sg, padding);
+	}
+	crypto_hash_final(hash, (u8 *) &data_crc);
+
+	return data_crc;
+}
+
+static void iscsit_do_crypto_hash_buf(
+	struct hash_desc *hash,
+	unsigned char *buf,
+	u32 payload_length,
+	u32 padding,
+	u8 *pad_bytes,
+	u8 *data_crc)
+{
+	struct scatterlist sg;
+
+	crypto_hash_init(hash);
+
+	sg_init_one(&sg, (u8 *)buf, payload_length);
+	crypto_hash_update(hash, &sg, payload_length);
+
+	if (padding) {
+		sg_init_one(&sg, pad_bytes, padding);
+		crypto_hash_update(hash, &sg, padding);
+	}
+	crypto_hash_final(hash, data_crc);
+}
+
+static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)
+{
+	int iov_ret, ooo_cmdsn = 0, ret;
+	u8 data_crc_failed = 0;
+	u32 checksum, iov_count = 0, padding = 0, rx_got = 0;
+	u32 rx_size = 0, payload_length;
+	struct iscsi_cmd *cmd = NULL;
+	struct se_cmd *se_cmd;
+	struct iscsi_data *hdr;
+	struct kvec *iov;
+	unsigned long flags;
+
+	hdr			= (struct iscsi_data *) buf;
+	payload_length		= ntoh24(hdr->dlength);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->ttt		= be32_to_cpu(hdr->ttt);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+	hdr->datasn		= be32_to_cpu(hdr->datasn);
+	hdr->offset		= be32_to_cpu(hdr->offset);
+
+	if (!payload_length) {
+		pr_err("DataOUT payload is ZERO, protocol error.\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	/* iSCSI write */
+	spin_lock_bh(&conn->sess->session_stats_lock);
+	conn->sess->rx_data_octets += payload_length;
+	if (conn->sess->se_sess->se_node_acl) {
+		spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
+		conn->sess->se_sess->se_node_acl->write_bytes += payload_length;
+		spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
+	}
+	spin_unlock_bh(&conn->sess->session_stats_lock);
+
+	if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) {
+		pr_err("DataSegmentLength: %u is greater than"
+			" MaxRecvDataSegmentLength: %u\n", payload_length,
+			conn->conn_ops->MaxRecvDataSegmentLength);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt,
+			payload_length);
+	if (!cmd)
+		return 0;
+
+	pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x,"
+		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
+		hdr->itt, hdr->ttt, hdr->datasn, hdr->offset,
+		payload_length, conn->cid);
+
+	if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+		pr_err("Command ITT: 0x%08x received DataOUT after"
+			" last DataOUT received, dumping payload\n",
+			cmd->init_task_tag);
+		return iscsit_dump_data_payload(conn, payload_length, 1);
+	}
+
+	if (cmd->data_direction != DMA_TO_DEVICE) {
+		pr_err("Command ITT: 0x%08x received DataOUT for a"
+			" NON-WRITE command.\n", cmd->init_task_tag);
+		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
+				1, 0, buf, cmd);
+	}
+	se_cmd = &cmd->se_cmd;
+	iscsit_mod_dataout_timer(cmd);
+
+	if ((hdr->offset + payload_length) > cmd->data_length) {
+		pr_err("DataOut Offset: %u, Length %u greater than"
+			" iSCSI Command EDTL %u, protocol error.\n",
+			hdr->offset, payload_length, cmd->data_length);
+		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
+				1, 0, buf, cmd);
+	}
+
+	if (cmd->unsolicited_data) {
+		int dump_unsolicited_data = 0;
+
+		if (conn->sess->sess_ops->InitialR2T) {
+			pr_err("Received unexpected unsolicited data"
+				" while InitialR2T=Yes, protocol error.\n");
+			transport_send_check_condition_and_sense(&cmd->se_cmd,
+					TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
+			return -1;
+		}
+		/*
+		 * Special case for dealing with Unsolicited DataOUT
+		 * and Unsupported SAM WRITE Opcodes and SE resource allocation
+		 * failures;
+		 */
+
+		/* Something's amiss if we're not in WRITE_PENDING state... */
+		spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+		WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING);
+		spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+		spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+		if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) ||
+		     (se_cmd->se_cmd_flags & SCF_SE_CMD_FAILED))
+			dump_unsolicited_data = 1;
+		spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+		if (dump_unsolicited_data) {
+			/*
+			 * Check if a delayed TASK_ABORTED status needs to
+			 * be sent now if the ISCSI_FLAG_CMD_FINAL has been
+			 * received with the unsolicitied data out.
+			 */
+			if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
+				iscsit_stop_dataout_timer(cmd);
+
+			transport_check_aborted_status(se_cmd,
+					(hdr->flags & ISCSI_FLAG_CMD_FINAL));
+			return iscsit_dump_data_payload(conn, payload_length, 1);
+		}
+	} else {
+		/*
+		 * For the normal solicited data path:
+		 *
+		 * Check for a delayed TASK_ABORTED status and dump any
+		 * incoming data out payload if one exists.  Also, when the
+		 * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current
+		 * data out sequence, we decrement outstanding_r2ts.  Once
+		 * outstanding_r2ts reaches zero, go ahead and send the delayed
+		 * TASK_ABORTED status.
+		 */
+		if (atomic_read(&se_cmd->t_transport_aborted) != 0) {
+			if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
+				if (--cmd->outstanding_r2ts < 1) {
+					iscsit_stop_dataout_timer(cmd);
+					transport_check_aborted_status(
+							se_cmd, 1);
+				}
+
+			return iscsit_dump_data_payload(conn, payload_length, 1);
+		}
+	}
+	/*
+	 * Preform DataSN, DataSequenceInOrder, DataPDUInOrder, and
+	 * within-command recovery checks before receiving the payload.
+	 */
+	ret = iscsit_check_pre_dataout(cmd, buf);
+	if (ret == DATAOUT_WITHIN_COMMAND_RECOVERY)
+		return 0;
+	else if (ret == DATAOUT_CANNOT_RECOVER)
+		return -1;
+
+	rx_size += payload_length;
+	iov = &cmd->iov_data[0];
+
+	iov_ret = iscsit_map_iovec(cmd, iov, hdr->offset, payload_length);
+	if (iov_ret < 0)
+		return -1;
+
+	iov_count += iov_ret;
+
+	padding = ((-payload_length) & 3);
+	if (padding != 0) {
+		iov[iov_count].iov_base	= cmd->pad_bytes;
+		iov[iov_count++].iov_len = padding;
+		rx_size += padding;
+		pr_debug("Receiving %u padding bytes.\n", padding);
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		iov[iov_count].iov_base = &checksum;
+		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+		rx_size += ISCSI_CRC_LEN;
+	}
+
+	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
+
+	iscsit_unmap_iovec(cmd);
+
+	if (rx_got != rx_size)
+		return -1;
+
+	if (conn->conn_ops->DataDigest) {
+		u32 data_crc;
+
+		data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd,
+						    hdr->offset, payload_length, padding,
+						    cmd->pad_bytes);
+
+		if (checksum != data_crc) {
+			pr_err("ITT: 0x%08x, Offset: %u, Length: %u,"
+				" DataSN: 0x%08x, CRC32C DataDigest 0x%08x"
+				" does not match computed 0x%08x\n",
+				hdr->itt, hdr->offset, payload_length,
+				hdr->datasn, checksum, data_crc);
+			data_crc_failed = 1;
+		} else {
+			pr_debug("Got CRC32C DataDigest 0x%08x for"
+				" %u bytes of Data Out\n", checksum,
+				payload_length);
+		}
+	}
+	/*
+	 * Increment post receive data and CRC values or perform
+	 * within-command recovery.
+	 */
+	ret = iscsit_check_post_dataout(cmd, buf, data_crc_failed);
+	if ((ret == DATAOUT_NORMAL) || (ret == DATAOUT_WITHIN_COMMAND_RECOVERY))
+		return 0;
+	else if (ret == DATAOUT_SEND_R2T) {
+		iscsit_set_dataout_sequence_values(cmd);
+		iscsit_build_r2ts_for_cmd(cmd, conn, 0);
+	} else if (ret == DATAOUT_SEND_TO_TRANSPORT) {
+		/*
+		 * Handle extra special case for out of order
+		 * Unsolicited Data Out.
+		 */
+		spin_lock_bh(&cmd->istate_lock);
+		ooo_cmdsn = (cmd->cmd_flags & ICF_OOO_CMDSN);
+		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
+		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
+		spin_unlock_bh(&cmd->istate_lock);
+
+		iscsit_stop_dataout_timer(cmd);
+		return (!ooo_cmdsn) ? transport_generic_handle_data(
+					&cmd->se_cmd) : 0;
+	} else /* DATAOUT_CANNOT_RECOVER */
+		return -1;
+
+	return 0;
+}
+
+static int iscsit_handle_nop_out(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	unsigned char *ping_data = NULL;
+	int cmdsn_ret, niov = 0, ret = 0, rx_got, rx_size;
+	u32 checksum, data_crc, padding = 0, payload_length;
+	u64 lun;
+	struct iscsi_cmd *cmd = NULL;
+	struct kvec *iov = NULL;
+	struct iscsi_nopout *hdr;
+
+	hdr			= (struct iscsi_nopout *) buf;
+	payload_length		= ntoh24(hdr->dlength);
+	lun			= get_unaligned_le64(&hdr->lun);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->ttt		= be32_to_cpu(hdr->ttt);
+	hdr->cmdsn		= be32_to_cpu(hdr->cmdsn);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+
+	if ((hdr->itt == 0xFFFFFFFF) && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+		pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
+			" not set, protocol error.\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) {
+		pr_err("NOPOUT Ping Data DataSegmentLength: %u is"
+			" greater than MaxRecvDataSegmentLength: %u, protocol"
+			" error.\n", payload_length,
+			conn->conn_ops->MaxRecvDataSegmentLength);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%09x,"
+		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
+		(hdr->itt == 0xFFFFFFFF) ? "Response" : "Request",
+		hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn,
+		payload_length);
+	/*
+	 * This is not a response to a Unsolicited NopIN, which means
+	 * it can either be a NOPOUT ping request (with a valid ITT),
+	 * or a NOPOUT not requesting a NOPIN (with a reserved ITT).
+	 * Either way, make sure we allocate an struct iscsi_cmd, as both
+	 * can contain ping data.
+	 */
+	if (hdr->ttt == 0xFFFFFFFF) {
+		cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+		if (!cmd)
+			return iscsit_add_reject(
+					ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+					1, buf, conn);
+
+		cmd->iscsi_opcode	= ISCSI_OP_NOOP_OUT;
+		cmd->i_state		= ISTATE_SEND_NOPIN;
+		cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
+						1 : 0);
+		conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+		cmd->targ_xfer_tag	= 0xFFFFFFFF;
+		cmd->cmd_sn		= hdr->cmdsn;
+		cmd->exp_stat_sn	= hdr->exp_statsn;
+		cmd->data_direction	= DMA_NONE;
+	}
+
+	if (payload_length && (hdr->ttt == 0xFFFFFFFF)) {
+		rx_size = payload_length;
+		ping_data = kzalloc(payload_length + 1, GFP_KERNEL);
+		if (!ping_data) {
+			pr_err("Unable to allocate memory for"
+				" NOPOUT ping data.\n");
+			ret = -1;
+			goto out;
+		}
+
+		iov = &cmd->iov_misc[0];
+		iov[niov].iov_base	= ping_data;
+		iov[niov++].iov_len	= payload_length;
+
+		padding = ((-payload_length) & 3);
+		if (padding != 0) {
+			pr_debug("Receiving %u additional bytes"
+				" for padding.\n", padding);
+			iov[niov].iov_base	= &cmd->pad_bytes;
+			iov[niov++].iov_len	= padding;
+			rx_size += padding;
+		}
+		if (conn->conn_ops->DataDigest) {
+			iov[niov].iov_base	= &checksum;
+			iov[niov++].iov_len	= ISCSI_CRC_LEN;
+			rx_size += ISCSI_CRC_LEN;
+		}
+
+		rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size);
+		if (rx_got != rx_size) {
+			ret = -1;
+			goto out;
+		}
+
+		if (conn->conn_ops->DataDigest) {
+			iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+					ping_data, payload_length,
+					padding, cmd->pad_bytes,
+					(u8 *)&data_crc);
+
+			if (checksum != data_crc) {
+				pr_err("Ping data CRC32C DataDigest"
+				" 0x%08x does not match computed 0x%08x\n",
+					checksum, data_crc);
+				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+					pr_err("Unable to recover from"
+					" NOPOUT Ping DataCRC failure while in"
+						" ERL=0.\n");
+					ret = -1;
+					goto out;
+				} else {
+					/*
+					 * Silently drop this PDU and let the
+					 * initiator plug the CmdSN gap.
+					 */
+					pr_debug("Dropping NOPOUT"
+					" Command CmdSN: 0x%08x due to"
+					" DataCRC error.\n", hdr->cmdsn);
+					ret = 0;
+					goto out;
+				}
+			} else {
+				pr_debug("Got CRC32C DataDigest"
+				" 0x%08x for %u bytes of ping data.\n",
+					checksum, payload_length);
+			}
+		}
+
+		ping_data[payload_length] = '\0';
+		/*
+		 * Attach ping data to struct iscsi_cmd->buf_ptr.
+		 */
+		cmd->buf_ptr = (void *)ping_data;
+		cmd->buf_ptr_size = payload_length;
+
+		pr_debug("Got %u bytes of NOPOUT ping"
+			" data.\n", payload_length);
+		pr_debug("Ping Data: \"%s\"\n", ping_data);
+	}
+
+	if (hdr->itt != 0xFFFFFFFF) {
+		if (!cmd) {
+			pr_err("Checking CmdSN for NOPOUT,"
+				" but cmd is NULL!\n");
+			return -1;
+		}
+		/*
+		 * Initiator is expecting a NopIN ping reply,
+		 */
+		spin_lock_bh(&conn->cmd_lock);
+		list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+		spin_unlock_bh(&conn->cmd_lock);
+
+		iscsit_ack_from_expstatsn(conn, hdr->exp_statsn);
+
+		if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+			iscsit_add_cmd_to_response_queue(cmd, conn,
+					cmd->i_state);
+			return 0;
+		}
+
+		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+			ret = 0;
+			goto ping_out;
+		}
+		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_PROTOCOL_ERROR,
+					1, 0, buf, cmd);
+
+		return 0;
+	}
+
+	if (hdr->ttt != 0xFFFFFFFF) {
+		/*
+		 * This was a response to a unsolicited NOPIN ping.
+		 */
+		cmd = iscsit_find_cmd_from_ttt(conn, hdr->ttt);
+		if (!cmd)
+			return -1;
+
+		iscsit_stop_nopin_response_timer(conn);
+
+		cmd->i_state = ISTATE_REMOVE;
+		iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state);
+		iscsit_start_nopin_timer(conn);
+	} else {
+		/*
+		 * Initiator is not expecting a NOPIN is response.
+		 * Just ignore for now.
+		 *
+		 * iSCSI v19-91 10.18
+		 * "A NOP-OUT may also be used to confirm a changed
+		 *  ExpStatSN if another PDU will not be available
+		 *  for a long time."
+		 */
+		ret = 0;
+		goto out;
+	}
+
+	return 0;
+out:
+	if (cmd)
+		iscsit_release_cmd(cmd);
+ping_out:
+	kfree(ping_data);
+	return ret;
+}
+
+static int iscsit_handle_task_mgt_cmd(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	struct iscsi_cmd *cmd;
+	struct se_tmr_req *se_tmr;
+	struct iscsi_tmr_req *tmr_req;
+	struct iscsi_tm *hdr;
+	u32 payload_length;
+	int out_of_order_cmdsn = 0;
+	int ret;
+	u8 function;
+
+	hdr			= (struct iscsi_tm *) buf;
+	payload_length		= ntoh24(hdr->dlength);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->rtt		= be32_to_cpu(hdr->rtt);
+	hdr->cmdsn		= be32_to_cpu(hdr->cmdsn);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+	hdr->refcmdsn		= be32_to_cpu(hdr->refcmdsn);
+	hdr->exp_datasn		= be32_to_cpu(hdr->exp_datasn);
+	hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
+	function = hdr->flags;
+
+	pr_debug("Got Task Management Request ITT: 0x%08x, CmdSN:"
+		" 0x%08x, Function: 0x%02x, RefTaskTag: 0x%08x, RefCmdSN:"
+		" 0x%08x, CID: %hu\n", hdr->itt, hdr->cmdsn, function,
+		hdr->rtt, hdr->refcmdsn, conn->cid);
+
+	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
+	    ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
+	     (hdr->rtt != ISCSI_RESERVED_TAG))) {
+		pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n");
+		hdr->rtt = ISCSI_RESERVED_TAG;
+	}
+
+	if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) &&
+			!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+		pr_err("Task Management Request TASK_REASSIGN not"
+			" issued as immediate command, bad iSCSI Initiator"
+				"implementation\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
+	    (hdr->refcmdsn != ISCSI_RESERVED_TAG))
+		hdr->refcmdsn = ISCSI_RESERVED_TAG;
+
+	cmd = iscsit_allocate_se_cmd_for_tmr(conn, function);
+	if (!cmd)
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+					1, buf, conn);
+
+	cmd->iscsi_opcode	= ISCSI_OP_SCSI_TMFUNC;
+	cmd->i_state		= ISTATE_SEND_TASKMGTRSP;
+	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+	cmd->init_task_tag	= hdr->itt;
+	cmd->targ_xfer_tag	= 0xFFFFFFFF;
+	cmd->cmd_sn		= hdr->cmdsn;
+	cmd->exp_stat_sn	= hdr->exp_statsn;
+	se_tmr			= cmd->se_cmd.se_tmr_req;
+	tmr_req			= cmd->tmr_req;
+	/*
+	 * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
+	 */
+	if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
+		ret = iscsit_get_lun_for_tmr(cmd,
+				get_unaligned_le64(&hdr->lun));
+		if (ret < 0) {
+			cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
+			se_tmr->response = ISCSI_TMF_RSP_NO_LUN;
+			goto attach;
+		}
+	}
+
+	switch (function) {
+	case ISCSI_TM_FUNC_ABORT_TASK:
+		se_tmr->response = iscsit_tmr_abort_task(cmd, buf);
+		if (se_tmr->response != ISCSI_TMF_RSP_COMPLETE) {
+			cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
+			goto attach;
+		}
+		break;
+	case ISCSI_TM_FUNC_ABORT_TASK_SET:
+	case ISCSI_TM_FUNC_CLEAR_ACA:
+	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
+	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+		break;
+	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+		if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) {
+			cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
+			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
+			goto attach;
+		}
+		break;
+	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+		if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) {
+			cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
+			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
+			goto attach;
+		}
+		break;
+	case ISCSI_TM_FUNC_TASK_REASSIGN:
+		se_tmr->response = iscsit_tmr_task_reassign(cmd, buf);
+		/*
+		 * Perform sanity checks on the ExpDataSN only if the
+		 * TASK_REASSIGN was successful.
+		 */
+		if (se_tmr->response != ISCSI_TMF_RSP_COMPLETE)
+			break;
+
+		if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0)
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_BOOKMARK_INVALID, 1, 1,
+					buf, cmd);
+		break;
+	default:
+		pr_err("Unknown TMR function: 0x%02x, protocol"
+			" error.\n", function);
+		cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
+		se_tmr->response = ISCSI_TMF_RSP_NOT_SUPPORTED;
+		goto attach;
+	}
+
+	if ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
+	    (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
+		se_tmr->call_transport = 1;
+attach:
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+		int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP)
+			out_of_order_cmdsn = 1;
+		else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+			return 0;
+		} else { /* (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) */
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_PROTOCOL_ERROR,
+					1, 0, buf, cmd);
+		}
+	}
+	iscsit_ack_from_expstatsn(conn, hdr->exp_statsn);
+
+	if (out_of_order_cmdsn)
+		return 0;
+	/*
+	 * Found the referenced task, send to transport for processing.
+	 */
+	if (se_tmr->call_transport)
+		return transport_generic_handle_tmr(&cmd->se_cmd);
+
+	/*
+	 * Could not find the referenced LUN, task, or Task Management
+	 * command not authorized or supported.  Change state and
+	 * let the tx_thread send the response.
+	 *
+	 * For connection recovery, this is also the default action for
+	 * TMR TASK_REASSIGN.
+	 */
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+	return 0;
+}
+
+/* #warning FIXME: Support Text Command parameters besides SendTargets */
+static int iscsit_handle_text_cmd(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	char *text_ptr, *text_in;
+	int cmdsn_ret, niov = 0, rx_got, rx_size;
+	u32 checksum = 0, data_crc = 0, payload_length;
+	u32 padding = 0, text_length = 0;
+	struct iscsi_cmd *cmd;
+	struct kvec iov[3];
+	struct iscsi_text *hdr;
+
+	hdr			= (struct iscsi_text *) buf;
+	payload_length		= ntoh24(hdr->dlength);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->ttt		= be32_to_cpu(hdr->ttt);
+	hdr->cmdsn		= be32_to_cpu(hdr->cmdsn);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+
+	if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) {
+		pr_err("Unable to accept text parameter length: %u"
+			"greater than MaxRecvDataSegmentLength %u.\n",
+		       payload_length, conn->conn_ops->MaxRecvDataSegmentLength);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
+		" ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
+		hdr->exp_statsn, payload_length);
+
+	rx_size = text_length = payload_length;
+	if (text_length) {
+		text_in = kzalloc(text_length, GFP_KERNEL);
+		if (!text_in) {
+			pr_err("Unable to allocate memory for"
+				" incoming text parameters\n");
+			return -1;
+		}
+
+		memset(iov, 0, 3 * sizeof(struct kvec));
+		iov[niov].iov_base	= text_in;
+		iov[niov++].iov_len	= text_length;
+
+		padding = ((-payload_length) & 3);
+		if (padding != 0) {
+			iov[niov].iov_base = cmd->pad_bytes;
+			iov[niov++].iov_len  = padding;
+			rx_size += padding;
+			pr_debug("Receiving %u additional bytes"
+					" for padding.\n", padding);
+		}
+		if (conn->conn_ops->DataDigest) {
+			iov[niov].iov_base	= &checksum;
+			iov[niov++].iov_len	= ISCSI_CRC_LEN;
+			rx_size += ISCSI_CRC_LEN;
+		}
+
+		rx_got = rx_data(conn, &iov[0], niov, rx_size);
+		if (rx_got != rx_size) {
+			kfree(text_in);
+			return -1;
+		}
+
+		if (conn->conn_ops->DataDigest) {
+			iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+					text_in, text_length,
+					padding, cmd->pad_bytes,
+					(u8 *)&data_crc);
+
+			if (checksum != data_crc) {
+				pr_err("Text data CRC32C DataDigest"
+					" 0x%08x does not match computed"
+					" 0x%08x\n", checksum, data_crc);
+				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+					pr_err("Unable to recover from"
+					" Text Data digest failure while in"
+						" ERL=0.\n");
+					kfree(text_in);
+					return -1;
+				} else {
+					/*
+					 * Silently drop this PDU and let the
+					 * initiator plug the CmdSN gap.
+					 */
+					pr_debug("Dropping Text"
+					" Command CmdSN: 0x%08x due to"
+					" DataCRC error.\n", hdr->cmdsn);
+					kfree(text_in);
+					return 0;
+				}
+			} else {
+				pr_debug("Got CRC32C DataDigest"
+					" 0x%08x for %u bytes of text data.\n",
+						checksum, text_length);
+			}
+		}
+		text_in[text_length - 1] = '\0';
+		pr_debug("Successfully read %d bytes of text"
+				" data.\n", text_length);
+
+		if (strncmp("SendTargets", text_in, 11) != 0) {
+			pr_err("Received Text Data that is not"
+				" SendTargets, cannot continue.\n");
+			kfree(text_in);
+			return -1;
+		}
+		text_ptr = strchr(text_in, '=');
+		if (!text_ptr) {
+			pr_err("No \"=\" separator found in Text Data,"
+				"  cannot continue.\n");
+			kfree(text_in);
+			return -1;
+		}
+		if (strncmp("=All", text_ptr, 4) != 0) {
+			pr_err("Unable to locate All value for"
+				" SendTargets key,  cannot continue.\n");
+			kfree(text_in);
+			return -1;
+		}
+/*#warning Support SendTargets=(iSCSI Target Name/Nothing) values. */
+		kfree(text_in);
+	}
+
+	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+	if (!cmd)
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+					1, buf, conn);
+
+	cmd->iscsi_opcode	= ISCSI_OP_TEXT;
+	cmd->i_state		= ISTATE_SEND_TEXTRSP;
+	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+	conn->sess->init_task_tag = cmd->init_task_tag	= hdr->itt;
+	cmd->targ_xfer_tag	= 0xFFFFFFFF;
+	cmd->cmd_sn		= hdr->cmdsn;
+	cmd->exp_stat_sn	= hdr->exp_statsn;
+	cmd->data_direction	= DMA_NONE;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	iscsit_ack_from_expstatsn(conn, hdr->exp_statsn);
+
+	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_PROTOCOL_ERROR,
+					1, 0, buf, cmd);
+
+		return 0;
+	}
+
+	return iscsit_execute_cmd(cmd, 0);
+}
+
+int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+	struct iscsi_conn *conn_p;
+	struct iscsi_session *sess = conn->sess;
+
+	pr_debug("Received logout request CLOSESESSION on CID: %hu"
+		" for SID: %u.\n", conn->cid, conn->sess->sid);
+
+	atomic_set(&sess->session_logout, 1);
+	atomic_set(&conn->conn_logout_remove, 1);
+	conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_SESSION;
+
+	iscsit_inc_conn_usage_count(conn);
+	iscsit_inc_session_usage_count(sess);
+
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(conn_p, &sess->sess_conn_list, conn_list) {
+		if (conn_p->conn_state != TARG_CONN_STATE_LOGGED_IN)
+			continue;
+
+		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+		conn_p->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	return 0;
+}
+
+int iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+	struct iscsi_conn *l_conn;
+	struct iscsi_session *sess = conn->sess;
+
+	pr_debug("Received logout request CLOSECONNECTION for CID:"
+		" %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
+
+	/*
+	 * A Logout Request with a CLOSECONNECTION reason code for a CID
+	 * can arrive on a connection with a differing CID.
+	 */
+	if (conn->cid == cmd->logout_cid) {
+		spin_lock_bh(&conn->state_lock);
+		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+		conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+
+		atomic_set(&conn->conn_logout_remove, 1);
+		conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_CONNECTION;
+		iscsit_inc_conn_usage_count(conn);
+
+		spin_unlock_bh(&conn->state_lock);
+	} else {
+		/*
+		 * Handle all different cid CLOSECONNECTION requests in
+		 * iscsit_logout_post_handler_diffcid() as to give enough
+		 * time for any non immediate command's CmdSN to be
+		 * acknowledged on the connection in question.
+		 *
+		 * Here we simply make sure the CID is still around.
+		 */
+		l_conn = iscsit_get_conn_from_cid(sess,
+				cmd->logout_cid);
+		if (!l_conn) {
+			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
+			iscsit_add_cmd_to_response_queue(cmd, conn,
+					cmd->i_state);
+			return 0;
+		}
+
+		iscsit_dec_conn_usage_count(l_conn);
+	}
+
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	return 0;
+}
+
+int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+
+	pr_debug("Received explicit REMOVECONNFORRECOVERY logout for"
+		" CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
+
+	if (sess->sess_ops->ErrorRecoveryLevel != 2) {
+		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
+			" while ERL!=2.\n");
+		cmd->logout_response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED;
+		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+		return 0;
+	}
+
+	if (conn->cid == cmd->logout_cid) {
+		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
+			" with CID: %hu on CID: %hu, implementation error.\n",
+				cmd->logout_cid, conn->cid);
+		cmd->logout_response = ISCSI_LOGOUT_CLEANUP_FAILED;
+		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+		return 0;
+	}
+
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	return 0;
+}
+
+static int iscsit_handle_logout_cmd(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	int cmdsn_ret, logout_remove = 0;
+	u8 reason_code = 0;
+	struct iscsi_cmd *cmd;
+	struct iscsi_logout *hdr;
+	struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn);
+
+	hdr			= (struct iscsi_logout *) buf;
+	reason_code		= (hdr->flags & 0x7f);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->cid		= be16_to_cpu(hdr->cid);
+	hdr->cmdsn		= be32_to_cpu(hdr->cmdsn);
+	hdr->exp_statsn	= be32_to_cpu(hdr->exp_statsn);
+
+	if (tiqn) {
+		spin_lock(&tiqn->logout_stats.lock);
+		if (reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION)
+			tiqn->logout_stats.normal_logouts++;
+		else
+			tiqn->logout_stats.abnormal_logouts++;
+		spin_unlock(&tiqn->logout_stats.lock);
+	}
+
+	pr_debug("Got Logout Request ITT: 0x%08x CmdSN: 0x%08x"
+		" ExpStatSN: 0x%08x Reason: 0x%02x CID: %hu on CID: %hu\n",
+		hdr->itt, hdr->cmdsn, hdr->exp_statsn, reason_code,
+		hdr->cid, conn->cid);
+
+	if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) {
+		pr_err("Received logout request on connection that"
+			" is not in logged in state, ignoring request.\n");
+		return 0;
+	}
+
+	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+	if (!cmd)
+		return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1,
+					buf, conn);
+
+	cmd->iscsi_opcode       = ISCSI_OP_LOGOUT;
+	cmd->i_state            = ISTATE_SEND_LOGOUTRSP;
+	cmd->immediate_cmd      = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+	conn->sess->init_task_tag = cmd->init_task_tag  = hdr->itt;
+	cmd->targ_xfer_tag      = 0xFFFFFFFF;
+	cmd->cmd_sn             = hdr->cmdsn;
+	cmd->exp_stat_sn        = hdr->exp_statsn;
+	cmd->logout_cid         = hdr->cid;
+	cmd->logout_reason      = reason_code;
+	cmd->data_direction     = DMA_NONE;
+
+	/*
+	 * We need to sleep in these cases (by returning 1) until the Logout
+	 * Response gets sent in the tx thread.
+	 */
+	if ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) ||
+	   ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) &&
+	    (hdr->cid == conn->cid)))
+		logout_remove = 1;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	if (reason_code != ISCSI_LOGOUT_REASON_RECOVERY)
+		iscsit_ack_from_expstatsn(conn, hdr->exp_statsn);
+
+	/*
+	 * Immediate commands are executed, well, immediately.
+	 * Non-Immediate Logout Commands are executed in CmdSN order.
+	 */
+	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+		int ret = iscsit_execute_cmd(cmd, 0);
+
+		if (ret < 0)
+			return ret;
+	} else {
+		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+			logout_remove = 0;
+		} else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
+			return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_PROTOCOL_ERROR,
+				1, 0, buf, cmd);
+		}
+	}
+
+	return logout_remove;
+}
+
+static int iscsit_handle_snack(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	u32 unpacked_lun;
+	u64 lun;
+	struct iscsi_snack *hdr;
+
+	hdr			= (struct iscsi_snack *) buf;
+	hdr->flags		&= ~ISCSI_FLAG_CMD_FINAL;
+	lun			= get_unaligned_le64(&hdr->lun);
+	unpacked_lun		= scsilun_to_int((struct scsi_lun *)&lun);
+	hdr->itt		= be32_to_cpu(hdr->itt);
+	hdr->ttt		= be32_to_cpu(hdr->ttt);
+	hdr->exp_statsn		= be32_to_cpu(hdr->exp_statsn);
+	hdr->begrun		= be32_to_cpu(hdr->begrun);
+	hdr->runlength		= be32_to_cpu(hdr->runlength);
+
+	pr_debug("Got ISCSI_INIT_SNACK, ITT: 0x%08x, ExpStatSN:"
+		" 0x%08x, Type: 0x%02x, BegRun: 0x%08x, RunLength: 0x%08x,"
+		" CID: %hu\n", hdr->itt, hdr->exp_statsn, hdr->flags,
+			hdr->begrun, hdr->runlength, conn->cid);
+
+	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+		pr_err("Initiator sent SNACK request while in"
+			" ErrorRecoveryLevel=0.\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+	/*
+	 * SNACK_DATA and SNACK_R2T are both 0,  so check which function to
+	 * call from inside iscsi_send_recovery_datain_or_r2t().
+	 */
+	switch (hdr->flags & ISCSI_FLAG_SNACK_TYPE_MASK) {
+	case 0:
+		return iscsit_handle_recovery_datain_or_r2t(conn, buf,
+			hdr->itt, hdr->ttt, hdr->begrun, hdr->runlength);
+		return 0;
+	case ISCSI_FLAG_SNACK_TYPE_STATUS:
+		return iscsit_handle_status_snack(conn, hdr->itt, hdr->ttt,
+			hdr->begrun, hdr->runlength);
+	case ISCSI_FLAG_SNACK_TYPE_DATA_ACK:
+		return iscsit_handle_data_ack(conn, hdr->ttt, hdr->begrun,
+			hdr->runlength);
+	case ISCSI_FLAG_SNACK_TYPE_RDATA:
+		/* FIXME: Support R-Data SNACK */
+		pr_err("R-Data SNACK Not Supported.\n");
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	default:
+		pr_err("Unknown SNACK type 0x%02x, protocol"
+			" error.\n", hdr->flags & 0x0f);
+		return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buf, conn);
+	}
+
+	return 0;
+}
+
+static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn)
+{
+	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
+	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
+		wait_for_completion_interruptible_timeout(
+					&conn->rx_half_close_comp,
+					ISCSI_RX_THREAD_TCP_TIMEOUT * HZ);
+	}
+}
+
+static int iscsit_handle_immediate_data(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf,
+	u32 length)
+{
+	int iov_ret, rx_got = 0, rx_size = 0;
+	u32 checksum, iov_count = 0, padding = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct kvec *iov;
+
+	iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done, length);
+	if (iov_ret < 0)
+		return IMMEDIATE_DATA_CANNOT_RECOVER;
+
+	rx_size = length;
+	iov_count = iov_ret;
+	iov = &cmd->iov_data[0];
+
+	padding = ((-length) & 3);
+	if (padding != 0) {
+		iov[iov_count].iov_base	= cmd->pad_bytes;
+		iov[iov_count++].iov_len = padding;
+		rx_size += padding;
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		iov[iov_count].iov_base		= &checksum;
+		iov[iov_count++].iov_len	= ISCSI_CRC_LEN;
+		rx_size += ISCSI_CRC_LEN;
+	}
+
+	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
+
+	iscsit_unmap_iovec(cmd);
+
+	if (rx_got != rx_size) {
+		iscsit_rx_thread_wait_for_tcp(conn);
+		return IMMEDIATE_DATA_CANNOT_RECOVER;
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		u32 data_crc;
+
+		data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd,
+						    cmd->write_data_done, length, padding,
+						    cmd->pad_bytes);
+
+		if (checksum != data_crc) {
+			pr_err("ImmediateData CRC32C DataDigest 0x%08x"
+				" does not match computed 0x%08x\n", checksum,
+				data_crc);
+
+			if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+				pr_err("Unable to recover from"
+					" Immediate Data digest failure while"
+					" in ERL=0.\n");
+				iscsit_add_reject_from_cmd(
+						ISCSI_REASON_DATA_DIGEST_ERROR,
+						1, 0, buf, cmd);
+				return IMMEDIATE_DATA_CANNOT_RECOVER;
+			} else {
+				iscsit_add_reject_from_cmd(
+						ISCSI_REASON_DATA_DIGEST_ERROR,
+						0, 0, buf, cmd);
+				return IMMEDIATE_DATA_ERL1_CRC_FAILURE;
+			}
+		} else {
+			pr_debug("Got CRC32C DataDigest 0x%08x for"
+				" %u bytes of Immediate Data\n", checksum,
+				length);
+		}
+	}
+
+	cmd->write_data_done += length;
+
+	if (cmd->write_data_done == cmd->data_length) {
+		spin_lock_bh(&cmd->istate_lock);
+		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
+		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
+		spin_unlock_bh(&cmd->istate_lock);
+	}
+
+	return IMMEDIATE_DATA_NORMAL_OPERATION;
+}
+
+/*
+ *	Called with sess->conn_lock held.
+ */
+/* #warning iscsi_build_conn_drop_async_message() only sends out on connections
+	with active network interface */
+static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
+{
+	struct iscsi_cmd *cmd;
+	struct iscsi_conn *conn_p;
+
+	/*
+	 * Only send a Asynchronous Message on connections whos network
+	 * interface is still functional.
+	 */
+	list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) {
+		if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) {
+			iscsit_inc_conn_usage_count(conn_p);
+			break;
+		}
+	}
+
+	if (!conn_p)
+		return;
+
+	cmd = iscsit_allocate_cmd(conn_p, GFP_KERNEL);
+	if (!cmd) {
+		iscsit_dec_conn_usage_count(conn_p);
+		return;
+	}
+
+	cmd->logout_cid = conn->cid;
+	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
+	cmd->i_state = ISTATE_SEND_ASYNCMSG;
+
+	spin_lock_bh(&conn_p->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn_p->conn_cmd_list);
+	spin_unlock_bh(&conn_p->cmd_lock);
+
+	iscsit_add_cmd_to_response_queue(cmd, conn_p, cmd->i_state);
+	iscsit_dec_conn_usage_count(conn_p);
+}
+
+static int iscsit_send_conn_drop_async_message(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_async *hdr;
+
+	cmd->tx_size = ISCSI_HDR_LEN;
+	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
+
+	hdr			= (struct iscsi_async *) cmd->pdu;
+	hdr->opcode		= ISCSI_OP_ASYNC_EVENT;
+	hdr->flags		= ISCSI_FLAG_CMD_FINAL;
+	cmd->init_task_tag	= 0xFFFFFFFF;
+	cmd->targ_xfer_tag	= 0xFFFFFFFF;
+	put_unaligned_be64(0xFFFFFFFFFFFFFFFFULL, &hdr->rsvd4[0]);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+	hdr->async_event	= ISCSI_ASYNC_MSG_DROPPING_CONNECTION;
+	hdr->param1		= cpu_to_be16(cmd->logout_cid);
+	hdr->param2		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait);
+	hdr->param3		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Retain);
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		cmd->tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32C HeaderDigest to"
+			" Async Message 0x%08x\n", *header_digest);
+	}
+
+	cmd->iov_misc[0].iov_base	= cmd->pdu;
+	cmd->iov_misc[0].iov_len	= cmd->tx_size;
+	cmd->iov_misc_count		= 1;
+
+	pr_debug("Sending Connection Dropped Async Message StatSN:"
+		" 0x%08x, for CID: %hu on CID: %hu\n", cmd->stat_sn,
+			cmd->logout_cid, conn->cid);
+	return 0;
+}
+
+static int iscsit_send_data_in(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	int *eodr)
+{
+	int iov_ret = 0, set_statsn = 0;
+	u32 iov_count = 0, tx_size = 0;
+	struct iscsi_datain datain;
+	struct iscsi_datain_req *dr;
+	struct iscsi_data_rsp *hdr;
+	struct kvec *iov;
+
+	memset(&datain, 0, sizeof(struct iscsi_datain));
+	dr = iscsit_get_datain_values(cmd, &datain);
+	if (!dr) {
+		pr_err("iscsit_get_datain_values failed for ITT: 0x%08x\n",
+				cmd->init_task_tag);
+		return -1;
+	}
+
+	/*
+	 * Be paranoid and double check the logic for now.
+	 */
+	if ((datain.offset + datain.length) > cmd->data_length) {
+		pr_err("Command ITT: 0x%08x, datain.offset: %u and"
+			" datain.length: %u exceeds cmd->data_length: %u\n",
+			cmd->init_task_tag, datain.offset, datain.length,
+				cmd->data_length);
+		return -1;
+	}
+
+	spin_lock_bh(&conn->sess->session_stats_lock);
+	conn->sess->tx_data_octets += datain.length;
+	if (conn->sess->se_sess->se_node_acl) {
+		spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock);
+		conn->sess->se_sess->se_node_acl->read_bytes += datain.length;
+		spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock);
+	}
+	spin_unlock_bh(&conn->sess->session_stats_lock);
+	/*
+	 * Special case for successfully execution w/ both DATAIN
+	 * and Sense Data.
+	 */
+	if ((datain.flags & ISCSI_FLAG_DATA_STATUS) &&
+	    (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
+		datain.flags &= ~ISCSI_FLAG_DATA_STATUS;
+	else {
+		if ((dr->dr_complete == DATAIN_COMPLETE_NORMAL) ||
+		    (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) {
+			iscsit_increment_maxcmdsn(cmd, conn->sess);
+			cmd->stat_sn = conn->stat_sn++;
+			set_statsn = 1;
+		} else if (dr->dr_complete ==
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY)
+			set_statsn = 1;
+	}
+
+	hdr	= (struct iscsi_data_rsp *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_SCSI_DATA_IN;
+	hdr->flags		= datain.flags;
+	if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+		if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
+			hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW;
+			hdr->residual_count = cpu_to_be32(cmd->residual_count);
+		} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
+			hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW;
+			hdr->residual_count = cpu_to_be32(cmd->residual_count);
+		}
+	}
+	hton24(hdr->dlength, datain.length);
+	if (hdr->flags & ISCSI_FLAG_DATA_ACK)
+		int_to_scsilun(cmd->se_cmd.orig_fe_lun,
+				(struct scsi_lun *)&hdr->lun);
+	else
+		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
+
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	hdr->ttt		= (hdr->flags & ISCSI_FLAG_DATA_ACK) ?
+				   cpu_to_be32(cmd->targ_xfer_tag) :
+				   0xFFFFFFFF;
+	hdr->statsn		= (set_statsn) ? cpu_to_be32(cmd->stat_sn) :
+						0xFFFFFFFF;
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+	hdr->datasn		= cpu_to_be32(datain.data_sn);
+	hdr->offset		= cpu_to_be32(datain.offset);
+
+	iov = &cmd->iov_data[0];
+	iov[iov_count].iov_base	= cmd->pdu;
+	iov[iov_count++].iov_len	= ISCSI_HDR_LEN;
+	tx_size += ISCSI_HDR_LEN;
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+
+		pr_debug("Attaching CRC32 HeaderDigest"
+			" for DataIN PDU 0x%08x\n", *header_digest);
+	}
+
+	iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1], datain.offset, datain.length);
+	if (iov_ret < 0)
+		return -1;
+
+	iov_count += iov_ret;
+	tx_size += datain.length;
+
+	cmd->padding = ((-datain.length) & 3);
+	if (cmd->padding) {
+		iov[iov_count].iov_base		= cmd->pad_bytes;
+		iov[iov_count++].iov_len	= cmd->padding;
+		tx_size += cmd->padding;
+
+		pr_debug("Attaching %u padding bytes\n",
+				cmd->padding);
+	}
+	if (conn->conn_ops->DataDigest) {
+		cmd->data_crc = iscsit_do_crypto_hash_sg(&conn->conn_tx_hash, cmd,
+			 datain.offset, datain.length, cmd->padding, cmd->pad_bytes);
+
+		iov[iov_count].iov_base	= &cmd->data_crc;
+		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+
+		pr_debug("Attached CRC32C DataDigest %d bytes, crc"
+			" 0x%08x\n", datain.length+cmd->padding, cmd->data_crc);
+	}
+
+	cmd->iov_data_count = iov_count;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x,"
+		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
+		cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn),
+		ntohl(hdr->offset), datain.length, conn->cid);
+
+	if (dr->dr_complete) {
+		*eodr = (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ?
+				2 : 1;
+		iscsit_free_datain_req(cmd, dr);
+	}
+
+	return 0;
+}
+
+static int iscsit_send_logout_response(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	int niov = 0, tx_size;
+	struct iscsi_conn *logout_conn = NULL;
+	struct iscsi_conn_recovery *cr = NULL;
+	struct iscsi_session *sess = conn->sess;
+	struct kvec *iov;
+	struct iscsi_logout_rsp *hdr;
+	/*
+	 * The actual shutting down of Sessions and/or Connections
+	 * for CLOSESESSION and CLOSECONNECTION Logout Requests
+	 * is done in scsi_logout_post_handler().
+	 */
+	switch (cmd->logout_reason) {
+	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+		pr_debug("iSCSI session logout successful, setting"
+			" logout response to ISCSI_LOGOUT_SUCCESS.\n");
+		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+		break;
+	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+		if (cmd->logout_response == ISCSI_LOGOUT_CID_NOT_FOUND)
+			break;
+		/*
+		 * For CLOSECONNECTION logout requests carrying
+		 * a matching logout CID -> local CID, the reference
+		 * for the local CID will have been incremented in
+		 * iscsi_logout_closeconnection().
+		 *
+		 * For CLOSECONNECTION logout requests carrying
+		 * a different CID than the connection it arrived
+		 * on, the connection responding to cmd->logout_cid
+		 * is stopped in iscsit_logout_post_handler_diffcid().
+		 */
+
+		pr_debug("iSCSI CID: %hu logout on CID: %hu"
+			" successful.\n", cmd->logout_cid, conn->cid);
+		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+		break;
+	case ISCSI_LOGOUT_REASON_RECOVERY:
+		if ((cmd->logout_response == ISCSI_LOGOUT_RECOVERY_UNSUPPORTED) ||
+		    (cmd->logout_response == ISCSI_LOGOUT_CLEANUP_FAILED))
+			break;
+		/*
+		 * If the connection is still active from our point of view
+		 * force connection recovery to occur.
+		 */
+		logout_conn = iscsit_get_conn_from_cid_rcfr(sess,
+				cmd->logout_cid);
+		if ((logout_conn)) {
+			iscsit_connection_reinstatement_rcfr(logout_conn);
+			iscsit_dec_conn_usage_count(logout_conn);
+		}
+
+		cr = iscsit_get_inactive_connection_recovery_entry(
+				conn->sess, cmd->logout_cid);
+		if (!cr) {
+			pr_err("Unable to locate CID: %hu for"
+			" REMOVECONNFORRECOVERY Logout Request.\n",
+				cmd->logout_cid);
+			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
+			break;
+		}
+
+		iscsit_discard_cr_cmds_by_expstatsn(cr, cmd->exp_stat_sn);
+
+		pr_debug("iSCSI REMOVECONNFORRECOVERY logout"
+			" for recovery for CID: %hu on CID: %hu successful.\n",
+				cmd->logout_cid, conn->cid);
+		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+		break;
+	default:
+		pr_err("Unknown cmd->logout_reason: 0x%02x\n",
+				cmd->logout_reason);
+		return -1;
+	}
+
+	tx_size = ISCSI_HDR_LEN;
+	hdr			= (struct iscsi_logout_rsp *)cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_LOGOUT_RSP;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	hdr->response		= cmd->logout_response;
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+
+	iscsit_increment_maxcmdsn(cmd, conn->sess);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	iov = &cmd->iov_misc[0];
+	iov[niov].iov_base	= cmd->pdu;
+	iov[niov++].iov_len	= ISCSI_HDR_LEN;
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32C HeaderDigest to"
+			" Logout Response 0x%08x\n", *header_digest);
+	}
+	cmd->iov_misc_count = niov;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Sending Logout Response ITT: 0x%08x StatSN:"
+		" 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n",
+		cmd->init_task_tag, cmd->stat_sn, hdr->response,
+		cmd->logout_cid, conn->cid);
+
+	return 0;
+}
+
+/*
+ *	Unsolicited NOPIN, either requesting a response or not.
+ */
+static int iscsit_send_unsolicited_nopin(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	int want_response)
+{
+	int tx_size = ISCSI_HDR_LEN;
+	struct iscsi_nopin *hdr;
+
+	hdr			= (struct iscsi_nopin *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_NOOP_IN;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
+	cmd->stat_sn		= conn->stat_sn;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32C HeaderDigest to"
+			" NopIN 0x%08x\n", *header_digest);
+	}
+
+	cmd->iov_misc[0].iov_base	= cmd->pdu;
+	cmd->iov_misc[0].iov_len	= tx_size;
+	cmd->iov_misc_count	= 1;
+	cmd->tx_size		= tx_size;
+
+	pr_debug("Sending Unsolicited NOPIN TTT: 0x%08x StatSN:"
+		" 0x%08x CID: %hu\n", hdr->ttt, cmd->stat_sn, conn->cid);
+
+	return 0;
+}
+
+static int iscsit_send_nopin_response(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	int niov = 0, tx_size;
+	u32 padding = 0;
+	struct kvec *iov;
+	struct iscsi_nopin *hdr;
+
+	tx_size = ISCSI_HDR_LEN;
+	hdr			= (struct iscsi_nopin *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_NOOP_IN;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	hton24(hdr->dlength, cmd->buf_ptr_size);
+	put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+
+	iscsit_increment_maxcmdsn(cmd, conn->sess);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	iov = &cmd->iov_misc[0];
+	iov[niov].iov_base	= cmd->pdu;
+	iov[niov++].iov_len	= ISCSI_HDR_LEN;
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32C HeaderDigest"
+			" to NopIn 0x%08x\n", *header_digest);
+	}
+
+	/*
+	 * NOPOUT Ping Data is attached to struct iscsi_cmd->buf_ptr.
+	 * NOPOUT DataSegmentLength is at struct iscsi_cmd->buf_ptr_size.
+	 */
+	if (cmd->buf_ptr_size) {
+		iov[niov].iov_base	= cmd->buf_ptr;
+		iov[niov++].iov_len	= cmd->buf_ptr_size;
+		tx_size += cmd->buf_ptr_size;
+
+		pr_debug("Echoing back %u bytes of ping"
+			" data.\n", cmd->buf_ptr_size);
+
+		padding = ((-cmd->buf_ptr_size) & 3);
+		if (padding != 0) {
+			iov[niov].iov_base = &cmd->pad_bytes;
+			iov[niov++].iov_len = padding;
+			tx_size += padding;
+			pr_debug("Attaching %u additional"
+				" padding bytes.\n", padding);
+		}
+		if (conn->conn_ops->DataDigest) {
+			iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				cmd->buf_ptr, cmd->buf_ptr_size,
+				padding, (u8 *)&cmd->pad_bytes,
+				(u8 *)&cmd->data_crc);
+
+			iov[niov].iov_base = &cmd->data_crc;
+			iov[niov++].iov_len = ISCSI_CRC_LEN;
+			tx_size += ISCSI_CRC_LEN;
+			pr_debug("Attached DataDigest for %u"
+				" bytes of ping data, CRC 0x%08x\n",
+				cmd->buf_ptr_size, cmd->data_crc);
+		}
+	}
+
+	cmd->iov_misc_count = niov;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Sending NOPIN Response ITT: 0x%08x, TTT:"
+		" 0x%08x, StatSN: 0x%08x, Length %u\n", cmd->init_task_tag,
+		cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size);
+
+	return 0;
+}
+
+int iscsit_send_r2t(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	int tx_size = 0;
+	struct iscsi_r2t *r2t;
+	struct iscsi_r2t_rsp *hdr;
+
+	r2t = iscsit_get_r2t_from_list(cmd);
+	if (!r2t)
+		return -1;
+
+	hdr			= (struct iscsi_r2t_rsp *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_R2T;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	int_to_scsilun(cmd->se_cmd.orig_fe_lun,
+			(struct scsi_lun *)&hdr->lun);
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	spin_lock_bh(&conn->sess->ttt_lock);
+	r2t->targ_xfer_tag	= conn->sess->targ_xfer_tag++;
+	if (r2t->targ_xfer_tag == 0xFFFFFFFF)
+		r2t->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+	spin_unlock_bh(&conn->sess->ttt_lock);
+	hdr->ttt		= cpu_to_be32(r2t->targ_xfer_tag);
+	hdr->statsn		= cpu_to_be32(conn->stat_sn);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+	hdr->r2tsn		= cpu_to_be32(r2t->r2t_sn);
+	hdr->data_offset	= cpu_to_be32(r2t->offset);
+	hdr->data_length	= cpu_to_be32(r2t->xfer_len);
+
+	cmd->iov_misc[0].iov_base	= cmd->pdu;
+	cmd->iov_misc[0].iov_len	= ISCSI_HDR_LEN;
+	tx_size += ISCSI_HDR_LEN;
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 HeaderDigest for R2T"
+			" PDU 0x%08x\n", *header_digest);
+	}
+
+	pr_debug("Built %sR2T, ITT: 0x%08x, TTT: 0x%08x, StatSN:"
+		" 0x%08x, R2TSN: 0x%08x, Offset: %u, DDTL: %u, CID: %hu\n",
+		(!r2t->recovery_r2t) ? "" : "Recovery ", cmd->init_task_tag,
+		r2t->targ_xfer_tag, ntohl(hdr->statsn), r2t->r2t_sn,
+			r2t->offset, r2t->xfer_len, conn->cid);
+
+	cmd->iov_misc_count = 1;
+	cmd->tx_size = tx_size;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	r2t->sent_r2t = 1;
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return 0;
+}
+
+/*
+ *	type 0: Normal Operation.
+ *	type 1: Called from Storage Transport.
+ *	type 2: Called from iscsi_task_reassign_complete_write() for
+ *	        connection recovery.
+ */
+int iscsit_build_r2ts_for_cmd(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	int type)
+{
+	int first_r2t = 1;
+	u32 offset = 0, xfer_len = 0;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	if (cmd->cmd_flags & ICF_SENT_LAST_R2T) {
+		spin_unlock_bh(&cmd->r2t_lock);
+		return 0;
+	}
+
+	if (conn->sess->sess_ops->DataSequenceInOrder && (type != 2))
+		if (cmd->r2t_offset < cmd->write_data_done)
+			cmd->r2t_offset = cmd->write_data_done;
+
+	while (cmd->outstanding_r2ts < conn->sess->sess_ops->MaxOutstandingR2T) {
+		if (conn->sess->sess_ops->DataSequenceInOrder) {
+			offset = cmd->r2t_offset;
+
+			if (first_r2t && (type == 2)) {
+				xfer_len = ((offset +
+					     (conn->sess->sess_ops->MaxBurstLength -
+					     cmd->next_burst_len) >
+					     cmd->data_length) ?
+					    (cmd->data_length - offset) :
+					    (conn->sess->sess_ops->MaxBurstLength -
+					     cmd->next_burst_len));
+			} else {
+				xfer_len = ((offset +
+					     conn->sess->sess_ops->MaxBurstLength) >
+					     cmd->data_length) ?
+					     (cmd->data_length - offset) :
+					     conn->sess->sess_ops->MaxBurstLength;
+			}
+			cmd->r2t_offset += xfer_len;
+
+			if (cmd->r2t_offset == cmd->data_length)
+				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
+		} else {
+			struct iscsi_seq *seq;
+
+			seq = iscsit_get_seq_holder_for_r2t(cmd);
+			if (!seq) {
+				spin_unlock_bh(&cmd->r2t_lock);
+				return -1;
+			}
+
+			offset = seq->offset;
+			xfer_len = seq->xfer_len;
+
+			if (cmd->seq_send_order == cmd->seq_count)
+				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
+		}
+		cmd->outstanding_r2ts++;
+		first_r2t = 0;
+
+		if (iscsit_add_r2t_to_list(cmd, offset, xfer_len, 0, 0) < 0) {
+			spin_unlock_bh(&cmd->r2t_lock);
+			return -1;
+		}
+
+		if (cmd->cmd_flags & ICF_SENT_LAST_R2T)
+			break;
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return 0;
+}
+
+static int iscsit_send_status(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	u8 iov_count = 0, recovery;
+	u32 padding = 0, tx_size = 0;
+	struct iscsi_scsi_rsp *hdr;
+	struct kvec *iov;
+
+	recovery = (cmd->i_state != ISTATE_SEND_STATUS);
+	if (!recovery)
+		cmd->stat_sn = conn->stat_sn++;
+
+	spin_lock_bh(&conn->sess->session_stats_lock);
+	conn->sess->rsp_pdus++;
+	spin_unlock_bh(&conn->sess->session_stats_lock);
+
+	hdr			= (struct iscsi_scsi_rsp *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_SCSI_CMD_RSP;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
+		hdr->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+		hdr->residual_count = cpu_to_be32(cmd->residual_count);
+	} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
+		hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+		hdr->residual_count = cpu_to_be32(cmd->residual_count);
+	}
+	hdr->response		= cmd->iscsi_response;
+	hdr->cmd_status		= cmd->se_cmd.scsi_status;
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+
+	iscsit_increment_maxcmdsn(cmd, conn->sess);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	iov = &cmd->iov_misc[0];
+	iov[iov_count].iov_base	= cmd->pdu;
+	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+	tx_size += ISCSI_HDR_LEN;
+
+	/*
+	 * Attach SENSE DATA payload to iSCSI Response PDU
+	 */
+	if (cmd->se_cmd.sense_buffer &&
+	   ((cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+	    (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
+		padding		= -(cmd->se_cmd.scsi_sense_length) & 3;
+		hton24(hdr->dlength, cmd->se_cmd.scsi_sense_length);
+		iov[iov_count].iov_base	= cmd->se_cmd.sense_buffer;
+		iov[iov_count++].iov_len =
+				(cmd->se_cmd.scsi_sense_length + padding);
+		tx_size += cmd->se_cmd.scsi_sense_length;
+
+		if (padding) {
+			memset(cmd->se_cmd.sense_buffer +
+				cmd->se_cmd.scsi_sense_length, 0, padding);
+			tx_size += padding;
+			pr_debug("Adding %u bytes of padding to"
+				" SENSE.\n", padding);
+		}
+
+		if (conn->conn_ops->DataDigest) {
+			iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				cmd->se_cmd.sense_buffer,
+				(cmd->se_cmd.scsi_sense_length + padding),
+				0, NULL, (u8 *)&cmd->data_crc);
+
+			iov[iov_count].iov_base    = &cmd->data_crc;
+			iov[iov_count++].iov_len     = ISCSI_CRC_LEN;
+			tx_size += ISCSI_CRC_LEN;
+
+			pr_debug("Attaching CRC32 DataDigest for"
+				" SENSE, %u bytes CRC 0x%08x\n",
+				(cmd->se_cmd.scsi_sense_length + padding),
+				cmd->data_crc);
+		}
+
+		pr_debug("Attaching SENSE DATA: %u bytes to iSCSI"
+				" Response PDU\n",
+				cmd->se_cmd.scsi_sense_length);
+	}
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 HeaderDigest for Response"
+				" PDU 0x%08x\n", *header_digest);
+	}
+
+	cmd->iov_misc_count = iov_count;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Built %sSCSI Response, ITT: 0x%08x, StatSN: 0x%08x,"
+		" Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n",
+		(!recovery) ? "" : "Recovery ", cmd->init_task_tag,
+		cmd->stat_sn, 0x00, cmd->se_cmd.scsi_status, conn->cid);
+
+	return 0;
+}
+
+static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr)
+{
+	switch (se_tmr->response) {
+	case TMR_FUNCTION_COMPLETE:
+		return ISCSI_TMF_RSP_COMPLETE;
+	case TMR_TASK_DOES_NOT_EXIST:
+		return ISCSI_TMF_RSP_NO_TASK;
+	case TMR_LUN_DOES_NOT_EXIST:
+		return ISCSI_TMF_RSP_NO_LUN;
+	case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+		return ISCSI_TMF_RSP_NOT_SUPPORTED;
+	case TMR_FUNCTION_AUTHORIZATION_FAILED:
+		return ISCSI_TMF_RSP_AUTH_FAILED;
+	case TMR_FUNCTION_REJECTED:
+	default:
+		return ISCSI_TMF_RSP_REJECTED;
+	}
+}
+
+static int iscsit_send_task_mgt_rsp(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+	struct iscsi_tm_rsp *hdr;
+	u32 tx_size = 0;
+
+	hdr			= (struct iscsi_tm_rsp *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_SCSI_TMFUNC_RSP;
+	hdr->response		= iscsit_convert_tcm_tmr_rsp(se_tmr);
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+
+	iscsit_increment_maxcmdsn(cmd, conn->sess);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	cmd->iov_misc[0].iov_base	= cmd->pdu;
+	cmd->iov_misc[0].iov_len	= ISCSI_HDR_LEN;
+	tx_size += ISCSI_HDR_LEN;
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 HeaderDigest for Task"
+			" Mgmt Response PDU 0x%08x\n", *header_digest);
+	}
+
+	cmd->iov_misc_count = 1;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Built Task Management Response ITT: 0x%08x,"
+		" StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n",
+		cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid);
+
+	return 0;
+}
+
+static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
+{
+	char *payload = NULL;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tiqn *tiqn;
+	struct iscsi_tpg_np *tpg_np;
+	int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
+	unsigned char buf[256];
+
+	buffer_len = (conn->conn_ops->MaxRecvDataSegmentLength > 32768) ?
+			32768 : conn->conn_ops->MaxRecvDataSegmentLength;
+
+	memset(buf, 0, 256);
+
+	payload = kzalloc(buffer_len, GFP_KERNEL);
+	if (!payload) {
+		pr_err("Unable to allocate memory for sendtargets"
+				" response.\n");
+		return -ENOMEM;
+	}
+
+	spin_lock(&tiqn_lock);
+	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
+		len = sprintf(buf, "TargetName=%s", tiqn->tiqn);
+		len += 1;
+
+		if ((len + payload_len) > buffer_len) {
+			spin_unlock(&tiqn->tiqn_tpg_lock);
+			end_of_buf = 1;
+			goto eob;
+		}
+		memcpy((void *)payload + payload_len, buf, len);
+		payload_len += len;
+
+		spin_lock(&tiqn->tiqn_tpg_lock);
+		list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
+
+			spin_lock(&tpg->tpg_state_lock);
+			if ((tpg->tpg_state == TPG_STATE_FREE) ||
+			    (tpg->tpg_state == TPG_STATE_INACTIVE)) {
+				spin_unlock(&tpg->tpg_state_lock);
+				continue;
+			}
+			spin_unlock(&tpg->tpg_state_lock);
+
+			spin_lock(&tpg->tpg_np_lock);
+			list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
+						tpg_np_list) {
+				len = sprintf(buf, "TargetAddress="
+					"%s%s%s:%hu,%hu",
+					(tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ?
+					"[" : "", tpg_np->tpg_np->np_ip,
+					(tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ?
+					"]" : "", tpg_np->tpg_np->np_port,
+					tpg->tpgt);
+				len += 1;
+
+				if ((len + payload_len) > buffer_len) {
+					spin_unlock(&tpg->tpg_np_lock);
+					spin_unlock(&tiqn->tiqn_tpg_lock);
+					end_of_buf = 1;
+					goto eob;
+				}
+				memcpy((void *)payload + payload_len, buf, len);
+				payload_len += len;
+			}
+			spin_unlock(&tpg->tpg_np_lock);
+		}
+		spin_unlock(&tiqn->tiqn_tpg_lock);
+eob:
+		if (end_of_buf)
+			break;
+	}
+	spin_unlock(&tiqn_lock);
+
+	cmd->buf_ptr = payload;
+
+	return payload_len;
+}
+
+/*
+ *	FIXME: Add support for F_BIT and C_BIT when the length is longer than
+ *	MaxRecvDataSegmentLength.
+ */
+static int iscsit_send_text_rsp(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_text_rsp *hdr;
+	struct kvec *iov;
+	u32 padding = 0, tx_size = 0;
+	int text_length, iov_count = 0;
+
+	text_length = iscsit_build_sendtargets_response(cmd);
+	if (text_length < 0)
+		return text_length;
+
+	padding = ((-text_length) & 3);
+	if (padding != 0) {
+		memset(cmd->buf_ptr + text_length, 0, padding);
+		pr_debug("Attaching %u additional bytes for"
+			" padding.\n", padding);
+	}
+
+	hdr			= (struct iscsi_text_rsp *) cmd->pdu;
+	memset(hdr, 0, ISCSI_HDR_LEN);
+	hdr->opcode		= ISCSI_OP_TEXT_RSP;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	hton24(hdr->dlength, text_length);
+	hdr->itt		= cpu_to_be32(cmd->init_task_tag);
+	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+
+	iscsit_increment_maxcmdsn(cmd, conn->sess);
+	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	iov = &cmd->iov_misc[0];
+
+	iov[iov_count].iov_base = cmd->pdu;
+	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+	iov[iov_count].iov_base	= cmd->buf_ptr;
+	iov[iov_count++].iov_len = text_length + padding;
+
+	tx_size += (ISCSI_HDR_LEN + text_length + padding);
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 HeaderDigest for"
+			" Text Response PDU 0x%08x\n", *header_digest);
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				cmd->buf_ptr, (text_length + padding),
+				0, NULL, (u8 *)&cmd->data_crc);
+
+		iov[iov_count].iov_base	= &cmd->data_crc;
+		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+		tx_size	+= ISCSI_CRC_LEN;
+
+		pr_debug("Attaching DataDigest for %u bytes of text"
+			" data, CRC 0x%08x\n", (text_length + padding),
+			cmd->data_crc);
+	}
+
+	cmd->iov_misc_count = iov_count;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Built Text Response: ITT: 0x%08x, StatSN: 0x%08x,"
+		" Length: %u, CID: %hu\n", cmd->init_task_tag, cmd->stat_sn,
+			text_length, conn->cid);
+	return 0;
+}
+
+static int iscsit_send_reject(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	u32 iov_count = 0, tx_size = 0;
+	struct iscsi_reject *hdr;
+	struct kvec *iov;
+
+	hdr			= (struct iscsi_reject *) cmd->pdu;
+	hdr->opcode		= ISCSI_OP_REJECT;
+	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
+	hton24(hdr->dlength, ISCSI_HDR_LEN);
+	cmd->stat_sn		= conn->stat_sn++;
+	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
+	hdr->exp_cmdsn	= cpu_to_be32(conn->sess->exp_cmd_sn);
+	hdr->max_cmdsn	= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	iov = &cmd->iov_misc[0];
+
+	iov[iov_count].iov_base = cmd->pdu;
+	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+	iov[iov_count].iov_base = cmd->buf_ptr;
+	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+
+	tx_size = (ISCSI_HDR_LEN + ISCSI_HDR_LEN);
+
+	if (conn->conn_ops->HeaderDigest) {
+		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)hdr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)header_digest);
+
+		iov[0].iov_len += ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 HeaderDigest for"
+			" REJECT PDU 0x%08x\n", *header_digest);
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+				(unsigned char *)cmd->buf_ptr, ISCSI_HDR_LEN,
+				0, NULL, (u8 *)&cmd->data_crc);
+
+		iov[iov_count].iov_base = &cmd->data_crc;
+		iov[iov_count++].iov_len  = ISCSI_CRC_LEN;
+		tx_size += ISCSI_CRC_LEN;
+		pr_debug("Attaching CRC32 DataDigest for REJECT"
+				" PDU 0x%08x\n", cmd->data_crc);
+	}
+
+	cmd->iov_misc_count = iov_count;
+	cmd->tx_size = tx_size;
+
+	pr_debug("Built Reject PDU StatSN: 0x%08x, Reason: 0x%02x,"
+		" CID: %hu\n", ntohl(hdr->statsn), hdr->reason, conn->cid);
+
+	return 0;
+}
+
+static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn)
+{
+	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
+	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
+		wait_for_completion_interruptible_timeout(
+					&conn->tx_half_close_comp,
+					ISCSI_TX_THREAD_TCP_TIMEOUT * HZ);
+	}
+}
+
+#ifdef CONFIG_SMP
+
+void iscsit_thread_get_cpumask(struct iscsi_conn *conn)
+{
+	struct iscsi_thread_set *ts = conn->thread_set;
+	int ord, cpu;
+	/*
+	 * thread_id is assigned from iscsit_global->ts_bitmap from
+	 * within iscsi_thread_set.c:iscsi_allocate_thread_sets()
+	 *
+	 * Here we use thread_id to determine which CPU that this
+	 * iSCSI connection's iscsi_thread_set will be scheduled to
+	 * execute upon.
+	 */
+	ord = ts->thread_id % cpumask_weight(cpu_online_mask);
+#if 0
+	pr_debug(">>>>>>>>>>>>>>>>>>>> Generated ord: %d from"
+			" thread_id: %d\n", ord, ts->thread_id);
+#endif
+	for_each_online_cpu(cpu) {
+		if (ord-- == 0) {
+			cpumask_set_cpu(cpu, conn->conn_cpumask);
+			return;
+		}
+	}
+	/*
+	 * This should never be reached..
+	 */
+	dump_stack();
+	cpumask_setall(conn->conn_cpumask);
+}
+
+static inline void iscsit_thread_check_cpumask(
+	struct iscsi_conn *conn,
+	struct task_struct *p,
+	int mode)
+{
+	char buf[128];
+	/*
+	 * mode == 1 signals iscsi_target_tx_thread() usage.
+	 * mode == 0 signals iscsi_target_rx_thread() usage.
+	 */
+	if (mode == 1) {
+		if (!conn->conn_tx_reset_cpumask)
+			return;
+		conn->conn_tx_reset_cpumask = 0;
+	} else {
+		if (!conn->conn_rx_reset_cpumask)
+			return;
+		conn->conn_rx_reset_cpumask = 0;
+	}
+	/*
+	 * Update the CPU mask for this single kthread so that
+	 * both TX and RX kthreads are scheduled to run on the
+	 * same CPU.
+	 */
+	memset(buf, 0, 128);
+	cpumask_scnprintf(buf, 128, conn->conn_cpumask);
+#if 0
+	pr_debug(">>>>>>>>>>>>>> Calling set_cpus_allowed_ptr():"
+			" %s for %s\n", buf, p->comm);
+#endif
+	set_cpus_allowed_ptr(p, conn->conn_cpumask);
+}
+
+#else
+#define iscsit_thread_get_cpumask(X) ({})
+#define iscsit_thread_check_cpumask(X, Y, Z) ({})
+#endif /* CONFIG_SMP */
+
+int iscsi_target_tx_thread(void *arg)
+{
+	u8 state;
+	int eodr = 0;
+	int ret = 0;
+	int sent_status = 0;
+	int use_misc = 0;
+	int map_sg = 0;
+	struct iscsi_cmd *cmd = NULL;
+	struct iscsi_conn *conn;
+	struct iscsi_queue_req *qr = NULL;
+	struct se_cmd *se_cmd;
+	struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg;
+	/*
+	 * Allow ourselves to be interrupted by SIGINT so that a
+	 * connection recovery / failure event can be triggered externally.
+	 */
+	allow_signal(SIGINT);
+
+restart:
+	conn = iscsi_tx_thread_pre_handler(ts);
+	if (!conn)
+		goto out;
+
+	eodr = map_sg = ret = sent_status = use_misc = 0;
+
+	while (!kthread_should_stop()) {
+		/*
+		 * Ensure that both TX and RX per connection kthreads
+		 * are scheduled to run on the same CPU.
+		 */
+		iscsit_thread_check_cpumask(conn, current, 1);
+
+		schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+
+		if ((ts->status == ISCSI_THREAD_SET_RESET) ||
+		     signal_pending(current))
+			goto transport_err;
+
+get_immediate:
+		qr = iscsit_get_cmd_from_immediate_queue(conn);
+		if (qr) {
+			atomic_set(&conn->check_immediate_queue, 0);
+			cmd = qr->cmd;
+			state = qr->state;
+			kmem_cache_free(lio_qr_cache, qr);
+
+			spin_lock_bh(&cmd->istate_lock);
+			switch (state) {
+			case ISTATE_SEND_R2T:
+				spin_unlock_bh(&cmd->istate_lock);
+				ret = iscsit_send_r2t(cmd, conn);
+				break;
+			case ISTATE_REMOVE:
+				spin_unlock_bh(&cmd->istate_lock);
+
+				if (cmd->data_direction == DMA_TO_DEVICE)
+					iscsit_stop_dataout_timer(cmd);
+
+				spin_lock_bh(&conn->cmd_lock);
+				list_del(&cmd->i_list);
+				spin_unlock_bh(&conn->cmd_lock);
+				/*
+				 * Determine if a struct se_cmd is assoicated with
+				 * this struct iscsi_cmd.
+				 */
+				if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) &&
+				    !(cmd->tmr_req))
+					iscsit_release_cmd(cmd);
+				else
+					transport_generic_free_cmd(&cmd->se_cmd,
+								1, 0);
+				goto get_immediate;
+			case ISTATE_SEND_NOPIN_WANT_RESPONSE:
+				spin_unlock_bh(&cmd->istate_lock);
+				iscsit_mod_nopin_response_timer(conn);
+				ret = iscsit_send_unsolicited_nopin(cmd,
+						conn, 1);
+				break;
+			case ISTATE_SEND_NOPIN_NO_RESPONSE:
+				spin_unlock_bh(&cmd->istate_lock);
+				ret = iscsit_send_unsolicited_nopin(cmd,
+						conn, 0);
+				break;
+			default:
+				pr_err("Unknown Opcode: 0x%02x ITT:"
+				" 0x%08x, i_state: %d on CID: %hu\n",
+				cmd->iscsi_opcode, cmd->init_task_tag, state,
+				conn->cid);
+				spin_unlock_bh(&cmd->istate_lock);
+				goto transport_err;
+			}
+			if (ret < 0) {
+				conn->tx_immediate_queue = 0;
+				goto transport_err;
+			}
+
+			if (iscsit_send_tx_data(cmd, conn, 1) < 0) {
+				conn->tx_immediate_queue = 0;
+				iscsit_tx_thread_wait_for_tcp(conn);
+				goto transport_err;
+			}
+
+			spin_lock_bh(&cmd->istate_lock);
+			switch (state) {
+			case ISTATE_SEND_R2T:
+				spin_unlock_bh(&cmd->istate_lock);
+				spin_lock_bh(&cmd->dataout_timeout_lock);
+				iscsit_start_dataout_timer(cmd, conn);
+				spin_unlock_bh(&cmd->dataout_timeout_lock);
+				break;
+			case ISTATE_SEND_NOPIN_WANT_RESPONSE:
+				cmd->i_state = ISTATE_SENT_NOPIN_WANT_RESPONSE;
+				spin_unlock_bh(&cmd->istate_lock);
+				break;
+			case ISTATE_SEND_NOPIN_NO_RESPONSE:
+				cmd->i_state = ISTATE_SENT_STATUS;
+				spin_unlock_bh(&cmd->istate_lock);
+				break;
+			default:
+				pr_err("Unknown Opcode: 0x%02x ITT:"
+					" 0x%08x, i_state: %d on CID: %hu\n",
+					cmd->iscsi_opcode, cmd->init_task_tag,
+					state, conn->cid);
+				spin_unlock_bh(&cmd->istate_lock);
+				goto transport_err;
+			}
+			goto get_immediate;
+		} else
+			conn->tx_immediate_queue = 0;
+
+get_response:
+		qr = iscsit_get_cmd_from_response_queue(conn);
+		if (qr) {
+			cmd = qr->cmd;
+			state = qr->state;
+			kmem_cache_free(lio_qr_cache, qr);
+
+			spin_lock_bh(&cmd->istate_lock);
+check_rsp_state:
+			switch (state) {
+			case ISTATE_SEND_DATAIN:
+				spin_unlock_bh(&cmd->istate_lock);
+				ret = iscsit_send_data_in(cmd, conn,
+							  &eodr);
+				map_sg = 1;
+				break;
+			case ISTATE_SEND_STATUS:
+			case ISTATE_SEND_STATUS_RECOVERY:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_status(cmd, conn);
+				break;
+			case ISTATE_SEND_LOGOUTRSP:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_logout_response(cmd, conn);
+				break;
+			case ISTATE_SEND_ASYNCMSG:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_conn_drop_async_message(
+						cmd, conn);
+				break;
+			case ISTATE_SEND_NOPIN:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_nopin_response(cmd, conn);
+				break;
+			case ISTATE_SEND_REJECT:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_reject(cmd, conn);
+				break;
+			case ISTATE_SEND_TASKMGTRSP:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_task_mgt_rsp(cmd, conn);
+				if (ret != 0)
+					break;
+				ret = iscsit_tmr_post_handler(cmd, conn);
+				if (ret != 0)
+					iscsit_fall_back_to_erl0(conn->sess);
+				break;
+			case ISTATE_SEND_TEXTRSP:
+				spin_unlock_bh(&cmd->istate_lock);
+				use_misc = 1;
+				ret = iscsit_send_text_rsp(cmd, conn);
+				break;
+			default:
+				pr_err("Unknown Opcode: 0x%02x ITT:"
+					" 0x%08x, i_state: %d on CID: %hu\n",
+					cmd->iscsi_opcode, cmd->init_task_tag,
+					state, conn->cid);
+				spin_unlock_bh(&cmd->istate_lock);
+				goto transport_err;
+			}
+			if (ret < 0) {
+				conn->tx_response_queue = 0;
+				goto transport_err;
+			}
+
+			se_cmd = &cmd->se_cmd;
+
+			if (map_sg && !conn->conn_ops->IFMarker) {
+				if (iscsit_fe_sendpage_sg(cmd, conn) < 0) {
+					conn->tx_response_queue = 0;
+					iscsit_tx_thread_wait_for_tcp(conn);
+					iscsit_unmap_iovec(cmd);
+					goto transport_err;
+				}
+			} else {
+				if (iscsit_send_tx_data(cmd, conn, use_misc) < 0) {
+					conn->tx_response_queue = 0;
+					iscsit_tx_thread_wait_for_tcp(conn);
+					iscsit_unmap_iovec(cmd);
+					goto transport_err;
+				}
+			}
+			map_sg = 0;
+			iscsit_unmap_iovec(cmd);
+
+			spin_lock_bh(&cmd->istate_lock);
+			switch (state) {
+			case ISTATE_SEND_DATAIN:
+				if (!eodr)
+					goto check_rsp_state;
+
+				if (eodr == 1) {
+					cmd->i_state = ISTATE_SENT_LAST_DATAIN;
+					sent_status = 1;
+					eodr = use_misc = 0;
+				} else if (eodr == 2) {
+					cmd->i_state = state =
+							ISTATE_SEND_STATUS;
+					sent_status = 0;
+					eodr = use_misc = 0;
+					goto check_rsp_state;
+				}
+				break;
+			case ISTATE_SEND_STATUS:
+				use_misc = 0;
+				sent_status = 1;
+				break;
+			case ISTATE_SEND_ASYNCMSG:
+			case ISTATE_SEND_NOPIN:
+			case ISTATE_SEND_STATUS_RECOVERY:
+			case ISTATE_SEND_TEXTRSP:
+				use_misc = 0;
+				sent_status = 1;
+				break;
+			case ISTATE_SEND_REJECT:
+				use_misc = 0;
+				if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) {
+					cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN;
+					spin_unlock_bh(&cmd->istate_lock);
+					complete(&cmd->reject_comp);
+					goto transport_err;
+				}
+				complete(&cmd->reject_comp);
+				break;
+			case ISTATE_SEND_TASKMGTRSP:
+				use_misc = 0;
+				sent_status = 1;
+				break;
+			case ISTATE_SEND_LOGOUTRSP:
+				spin_unlock_bh(&cmd->istate_lock);
+				if (!iscsit_logout_post_handler(cmd, conn))
+					goto restart;
+				spin_lock_bh(&cmd->istate_lock);
+				use_misc = 0;
+				sent_status = 1;
+				break;
+			default:
+				pr_err("Unknown Opcode: 0x%02x ITT:"
+					" 0x%08x, i_state: %d on CID: %hu\n",
+					cmd->iscsi_opcode, cmd->init_task_tag,
+					cmd->i_state, conn->cid);
+				spin_unlock_bh(&cmd->istate_lock);
+				goto transport_err;
+			}
+
+			if (sent_status) {
+				cmd->i_state = ISTATE_SENT_STATUS;
+				sent_status = 0;
+			}
+			spin_unlock_bh(&cmd->istate_lock);
+
+			if (atomic_read(&conn->check_immediate_queue))
+				goto get_immediate;
+
+			goto get_response;
+		} else
+			conn->tx_response_queue = 0;
+	}
+
+transport_err:
+	iscsit_take_action_for_connection_exit(conn);
+	goto restart;
+out:
+	return 0;
+}
+
+int iscsi_target_rx_thread(void *arg)
+{
+	int ret;
+	u8 buffer[ISCSI_HDR_LEN], opcode;
+	u32 checksum = 0, digest = 0;
+	struct iscsi_conn *conn = NULL;
+	struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg;
+	struct kvec iov;
+	/*
+	 * Allow ourselves to be interrupted by SIGINT so that a
+	 * connection recovery / failure event can be triggered externally.
+	 */
+	allow_signal(SIGINT);
+
+restart:
+	conn = iscsi_rx_thread_pre_handler(ts);
+	if (!conn)
+		goto out;
+
+	while (!kthread_should_stop()) {
+		/*
+		 * Ensure that both TX and RX per connection kthreads
+		 * are scheduled to run on the same CPU.
+		 */
+		iscsit_thread_check_cpumask(conn, current, 0);
+
+		memset(buffer, 0, ISCSI_HDR_LEN);
+		memset(&iov, 0, sizeof(struct kvec));
+
+		iov.iov_base	= buffer;
+		iov.iov_len	= ISCSI_HDR_LEN;
+
+		ret = rx_data(conn, &iov, 1, ISCSI_HDR_LEN);
+		if (ret != ISCSI_HDR_LEN) {
+			iscsit_rx_thread_wait_for_tcp(conn);
+			goto transport_err;
+		}
+
+		/*
+		 * Set conn->bad_hdr for use with REJECT PDUs.
+		 */
+		memcpy(&conn->bad_hdr, &buffer, ISCSI_HDR_LEN);
+
+		if (conn->conn_ops->HeaderDigest) {
+			iov.iov_base	= &digest;
+			iov.iov_len	= ISCSI_CRC_LEN;
+
+			ret = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
+			if (ret != ISCSI_CRC_LEN) {
+				iscsit_rx_thread_wait_for_tcp(conn);
+				goto transport_err;
+			}
+
+			iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+					buffer, ISCSI_HDR_LEN,
+					0, NULL, (u8 *)&checksum);
+
+			if (digest != checksum) {
+				pr_err("HeaderDigest CRC32C failed,"
+					" received 0x%08x, computed 0x%08x\n",
+					digest, checksum);
+				/*
+				 * Set the PDU to 0xff so it will intentionally
+				 * hit default in the switch below.
+				 */
+				memset(buffer, 0xff, ISCSI_HDR_LEN);
+				spin_lock_bh(&conn->sess->session_stats_lock);
+				conn->sess->conn_digest_errors++;
+				spin_unlock_bh(&conn->sess->session_stats_lock);
+			} else {
+				pr_debug("Got HeaderDigest CRC32C"
+						" 0x%08x\n", checksum);
+			}
+		}
+
+		if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT)
+			goto transport_err;
+
+		opcode = buffer[0] & ISCSI_OPCODE_MASK;
+
+		if (conn->sess->sess_ops->SessionType &&
+		   ((!(opcode & ISCSI_OP_TEXT)) ||
+		    (!(opcode & ISCSI_OP_LOGOUT)))) {
+			pr_err("Received illegal iSCSI Opcode: 0x%02x"
+			" while in Discovery Session, rejecting.\n", opcode);
+			iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
+					buffer, conn);
+			goto transport_err;
+		}
+
+		switch (opcode) {
+		case ISCSI_OP_SCSI_CMD:
+			if (iscsit_handle_scsi_cmd(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_SCSI_DATA_OUT:
+			if (iscsit_handle_data_out(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_NOOP_OUT:
+			if (iscsit_handle_nop_out(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_SCSI_TMFUNC:
+			if (iscsit_handle_task_mgt_cmd(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_TEXT:
+			if (iscsit_handle_text_cmd(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_LOGOUT:
+			ret = iscsit_handle_logout_cmd(conn, buffer);
+			if (ret > 0) {
+				wait_for_completion_timeout(&conn->conn_logout_comp,
+						SECONDS_FOR_LOGOUT_COMP * HZ);
+				goto transport_err;
+			} else if (ret < 0)
+				goto transport_err;
+			break;
+		case ISCSI_OP_SNACK:
+			if (iscsit_handle_snack(conn, buffer) < 0)
+				goto transport_err;
+			break;
+		default:
+			pr_err("Got unknown iSCSI OpCode: 0x%02x\n",
+					opcode);
+			if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+				pr_err("Cannot recover from unknown"
+				" opcode while ERL=0, closing iSCSI connection"
+				".\n");
+				goto transport_err;
+			}
+			if (!conn->conn_ops->OFMarker) {
+				pr_err("Unable to recover from unknown"
+				" opcode while OFMarker=No, closing iSCSI"
+					" connection.\n");
+				goto transport_err;
+			}
+			if (iscsit_recover_from_unknown_opcode(conn) < 0) {
+				pr_err("Unable to recover from unknown"
+					" opcode, closing iSCSI connection.\n");
+				goto transport_err;
+			}
+			break;
+		}
+	}
+
+transport_err:
+	if (!signal_pending(current))
+		atomic_set(&conn->transport_failed, 1);
+	iscsit_take_action_for_connection_exit(conn);
+	goto restart;
+out:
+	return 0;
+}
+
+static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
+{
+	struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL;
+	struct iscsi_session *sess = conn->sess;
+	struct se_cmd *se_cmd;
+	/*
+	 * We expect this function to only ever be called from either RX or TX
+	 * thread context via iscsit_close_connection() once the other context
+	 * has been reset -> returned sleeping pre-handler state.
+	 */
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) {
+		if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD)) {
+
+			list_del(&cmd->i_list);
+			spin_unlock_bh(&conn->cmd_lock);
+			iscsit_increment_maxcmdsn(cmd, sess);
+			se_cmd = &cmd->se_cmd;
+			/*
+			 * Special cases for active iSCSI TMR, and
+			 * transport_lookup_cmd_lun() failing from
+			 * iscsit_get_lun_for_cmd() in iscsit_handle_scsi_cmd().
+			 */
+			if (cmd->tmr_req && se_cmd->transport_wait_for_tasks)
+				se_cmd->transport_wait_for_tasks(se_cmd, 1, 1);
+			else if (cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD)
+				transport_release_cmd(se_cmd);
+			else
+				iscsit_release_cmd(cmd);
+
+			spin_lock_bh(&conn->cmd_lock);
+			continue;
+		}
+		list_del(&cmd->i_list);
+		spin_unlock_bh(&conn->cmd_lock);
+
+		iscsit_increment_maxcmdsn(cmd, sess);
+		se_cmd = &cmd->se_cmd;
+
+		if (se_cmd->transport_wait_for_tasks)
+			se_cmd->transport_wait_for_tasks(se_cmd, 1, 1);
+
+		spin_lock_bh(&conn->cmd_lock);
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+}
+
+static void iscsit_stop_timers_for_cmds(
+	struct iscsi_conn *conn)
+{
+	struct iscsi_cmd *cmd;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+		if (cmd->data_direction == DMA_TO_DEVICE)
+			iscsit_stop_dataout_timer(cmd);
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+}
+
+int iscsit_close_connection(
+	struct iscsi_conn *conn)
+{
+	int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT);
+	struct iscsi_session	*sess = conn->sess;
+
+	pr_debug("Closing iSCSI connection CID %hu on SID:"
+		" %u\n", conn->cid, sess->sid);
+	/*
+	 * Always up conn_logout_comp just in case the RX Thread is sleeping
+	 * and the logout response never got sent because the connection
+	 * failed.
+	 */
+	complete(&conn->conn_logout_comp);
+
+	iscsi_release_thread_set(conn);
+
+	iscsit_stop_timers_for_cmds(conn);
+	iscsit_stop_nopin_response_timer(conn);
+	iscsit_stop_nopin_timer(conn);
+	iscsit_free_queue_reqs_for_conn(conn);
+
+	/*
+	 * During Connection recovery drop unacknowledged out of order
+	 * commands for this connection, and prepare the other commands
+	 * for realligence.
+	 *
+	 * During normal operation clear the out of order commands (but
+	 * do not free the struct iscsi_ooo_cmdsn's) and release all
+	 * struct iscsi_cmds.
+	 */
+	if (atomic_read(&conn->connection_recovery)) {
+		iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn);
+		iscsit_prepare_cmds_for_realligance(conn);
+	} else {
+		iscsit_clear_ooo_cmdsns_for_conn(conn);
+		iscsit_release_commands_from_conn(conn);
+	}
+
+	/*
+	 * Handle decrementing session or connection usage count if
+	 * a logout response was not able to be sent because the
+	 * connection failed.  Fall back to Session Recovery here.
+	 */
+	if (atomic_read(&conn->conn_logout_remove)) {
+		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
+			iscsit_dec_conn_usage_count(conn);
+			iscsit_dec_session_usage_count(sess);
+		}
+		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION)
+			iscsit_dec_conn_usage_count(conn);
+
+		atomic_set(&conn->conn_logout_remove, 0);
+		atomic_set(&sess->session_reinstatement, 0);
+		atomic_set(&sess->session_fall_back_to_erl0, 1);
+	}
+
+	spin_lock_bh(&sess->conn_lock);
+	list_del(&conn->conn_list);
+
+	/*
+	 * Attempt to let the Initiator know this connection failed by
+	 * sending an Connection Dropped Async Message on another
+	 * active connection.
+	 */
+	if (atomic_read(&conn->connection_recovery))
+		iscsit_build_conn_drop_async_message(conn);
+
+	spin_unlock_bh(&sess->conn_lock);
+
+	/*
+	 * If connection reinstatement is being performed on this connection,
+	 * up the connection reinstatement semaphore that is being blocked on
+	 * in iscsit_cause_connection_reinstatement().
+	 */
+	spin_lock_bh(&conn->state_lock);
+	if (atomic_read(&conn->sleep_on_conn_wait_comp)) {
+		spin_unlock_bh(&conn->state_lock);
+		complete(&conn->conn_wait_comp);
+		wait_for_completion(&conn->conn_post_wait_comp);
+		spin_lock_bh(&conn->state_lock);
+	}
+
+	/*
+	 * If connection reinstatement is being performed on this connection
+	 * by receiving a REMOVECONNFORRECOVERY logout request, up the
+	 * connection wait rcfr semaphore that is being blocked on
+	 * an iscsit_connection_reinstatement_rcfr().
+	 */
+	if (atomic_read(&conn->connection_wait_rcfr)) {
+		spin_unlock_bh(&conn->state_lock);
+		complete(&conn->conn_wait_rcfr_comp);
+		wait_for_completion(&conn->conn_post_wait_comp);
+		spin_lock_bh(&conn->state_lock);
+	}
+	atomic_set(&conn->connection_reinstatement, 1);
+	spin_unlock_bh(&conn->state_lock);
+
+	/*
+	 * If any other processes are accessing this connection pointer we
+	 * must wait until they have completed.
+	 */
+	iscsit_check_conn_usage_count(conn);
+
+	if (conn->conn_rx_hash.tfm)
+		crypto_free_hash(conn->conn_rx_hash.tfm);
+	if (conn->conn_tx_hash.tfm)
+		crypto_free_hash(conn->conn_tx_hash.tfm);
+
+	if (conn->conn_cpumask)
+		free_cpumask_var(conn->conn_cpumask);
+
+	kfree(conn->conn_ops);
+	conn->conn_ops = NULL;
+
+	if (conn->sock) {
+		if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) {
+			kfree(conn->sock->file);
+			conn->sock->file = NULL;
+		}
+		sock_release(conn->sock);
+	}
+	conn->thread_set = NULL;
+
+	pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
+	conn->conn_state = TARG_CONN_STATE_FREE;
+	kfree(conn);
+
+	spin_lock_bh(&sess->conn_lock);
+	atomic_dec(&sess->nconn);
+	pr_debug("Decremented iSCSI connection count to %hu from node:"
+		" %s\n", atomic_read(&sess->nconn),
+		sess->sess_ops->InitiatorName);
+	/*
+	 * Make sure that if one connection fails in an non ERL=2 iSCSI
+	 * Session that they all fail.
+	 */
+	if ((sess->sess_ops->ErrorRecoveryLevel != 2) && !conn_logout &&
+	     !atomic_read(&sess->session_logout))
+		atomic_set(&sess->session_fall_back_to_erl0, 1);
+
+	/*
+	 * If this was not the last connection in the session, and we are
+	 * performing session reinstatement or falling back to ERL=0, call
+	 * iscsit_stop_session() without sleeping to shutdown the other
+	 * active connections.
+	 */
+	if (atomic_read(&sess->nconn)) {
+		if (!atomic_read(&sess->session_reinstatement) &&
+		    !atomic_read(&sess->session_fall_back_to_erl0)) {
+			spin_unlock_bh(&sess->conn_lock);
+			return 0;
+		}
+		if (!atomic_read(&sess->session_stop_active)) {
+			atomic_set(&sess->session_stop_active, 1);
+			spin_unlock_bh(&sess->conn_lock);
+			iscsit_stop_session(sess, 0, 0);
+			return 0;
+		}
+		spin_unlock_bh(&sess->conn_lock);
+		return 0;
+	}
+
+	/*
+	 * If this was the last connection in the session and one of the
+	 * following is occurring:
+	 *
+	 * Session Reinstatement is not being performed, and are falling back
+	 * to ERL=0 call iscsit_close_session().
+	 *
+	 * Session Logout was requested.  iscsit_close_session() will be called
+	 * elsewhere.
+	 *
+	 * Session Continuation is not being performed, start the Time2Retain
+	 * handler and check if sleep_on_sess_wait_sem is active.
+	 */
+	if (!atomic_read(&sess->session_reinstatement) &&
+	     atomic_read(&sess->session_fall_back_to_erl0)) {
+		spin_unlock_bh(&sess->conn_lock);
+		iscsit_close_session(sess);
+
+		return 0;
+	} else if (atomic_read(&sess->session_logout)) {
+		pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
+		sess->session_state = TARG_SESS_STATE_FREE;
+		spin_unlock_bh(&sess->conn_lock);
+
+		if (atomic_read(&sess->sleep_on_sess_wait_comp))
+			complete(&sess->session_wait_comp);
+
+		return 0;
+	} else {
+		pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
+		sess->session_state = TARG_SESS_STATE_FAILED;
+
+		if (!atomic_read(&sess->session_continuation)) {
+			spin_unlock_bh(&sess->conn_lock);
+			iscsit_start_time2retain_handler(sess);
+		} else
+			spin_unlock_bh(&sess->conn_lock);
+
+		if (atomic_read(&sess->sleep_on_sess_wait_comp))
+			complete(&sess->session_wait_comp);
+
+		return 0;
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	return 0;
+}
+
+int iscsit_close_session(struct iscsi_session *sess)
+{
+	struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+	if (atomic_read(&sess->nconn)) {
+		pr_err("%d connection(s) still exist for iSCSI session"
+			" to %s\n", atomic_read(&sess->nconn),
+			sess->sess_ops->InitiatorName);
+		BUG();
+	}
+
+	spin_lock_bh(&se_tpg->session_lock);
+	atomic_set(&sess->session_logout, 1);
+	atomic_set(&sess->session_reinstatement, 1);
+	iscsit_stop_time2retain_timer(sess);
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	/*
+	 * transport_deregister_session_configfs() will clear the
+	 * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context
+	 * can be setting it again with __transport_register_session() in
+	 * iscsi_post_login_handler() again after the iscsit_stop_session()
+	 * completes in iscsi_np context.
+	 */
+	transport_deregister_session_configfs(sess->se_sess);
+
+	/*
+	 * If any other processes are accessing this session pointer we must
+	 * wait until they have completed.  If we are in an interrupt (the
+	 * time2retain handler) and contain and active session usage count we
+	 * restart the timer and exit.
+	 */
+	if (!in_interrupt()) {
+		if (iscsit_check_session_usage_count(sess) == 1)
+			iscsit_stop_session(sess, 1, 1);
+	} else {
+		if (iscsit_check_session_usage_count(sess) == 2) {
+			atomic_set(&sess->session_logout, 0);
+			iscsit_start_time2retain_handler(sess);
+			return 0;
+		}
+	}
+
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->sess_ops->ErrorRecoveryLevel == 2)
+		iscsit_free_connection_recovery_entires(sess);
+
+	iscsit_free_all_ooo_cmdsns(sess);
+
+	spin_lock_bh(&se_tpg->session_lock);
+	pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
+	sess->session_state = TARG_SESS_STATE_FREE;
+	pr_debug("Released iSCSI session from node: %s\n",
+			sess->sess_ops->InitiatorName);
+	tpg->nsessions--;
+	if (tpg->tpg_tiqn)
+		tpg->tpg_tiqn->tiqn_nsessions--;
+
+	pr_debug("Decremented number of active iSCSI Sessions on"
+		" iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions);
+
+	spin_lock(&sess_idr_lock);
+	idr_remove(&sess_idr, sess->session_index);
+	spin_unlock(&sess_idr_lock);
+
+	kfree(sess->sess_ops);
+	sess->sess_ops = NULL;
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	kfree(sess);
+	return 0;
+}
+
+static void iscsit_logout_post_handler_closesession(
+	struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+
+	iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD);
+	iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD);
+
+	atomic_set(&conn->conn_logout_remove, 0);
+	complete(&conn->conn_logout_comp);
+
+	iscsit_dec_conn_usage_count(conn);
+	iscsit_stop_session(sess, 1, 1);
+	iscsit_dec_session_usage_count(sess);
+	iscsit_close_session(sess);
+}
+
+static void iscsit_logout_post_handler_samecid(
+	struct iscsi_conn *conn)
+{
+	iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD);
+	iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD);
+
+	atomic_set(&conn->conn_logout_remove, 0);
+	complete(&conn->conn_logout_comp);
+
+	iscsit_cause_connection_reinstatement(conn, 1);
+	iscsit_dec_conn_usage_count(conn);
+}
+
+static void iscsit_logout_post_handler_diffcid(
+	struct iscsi_conn *conn,
+	u16 cid)
+{
+	struct iscsi_conn *l_conn;
+	struct iscsi_session *sess = conn->sess;
+
+	if (!sess)
+		return;
+
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(l_conn, &sess->sess_conn_list, conn_list) {
+		if (l_conn->cid == cid) {
+			iscsit_inc_conn_usage_count(l_conn);
+			break;
+		}
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	if (!l_conn)
+		return;
+
+	if (l_conn->sock)
+		l_conn->sock->ops->shutdown(l_conn->sock, RCV_SHUTDOWN);
+
+	spin_lock_bh(&l_conn->state_lock);
+	pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+	l_conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+	spin_unlock_bh(&l_conn->state_lock);
+
+	iscsit_cause_connection_reinstatement(l_conn, 1);
+	iscsit_dec_conn_usage_count(l_conn);
+}
+
+/*
+ *	Return of 0 causes the TX thread to restart.
+ */
+static int iscsit_logout_post_handler(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	int ret = 0;
+
+	switch (cmd->logout_reason) {
+	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+		switch (cmd->logout_response) {
+		case ISCSI_LOGOUT_SUCCESS:
+		case ISCSI_LOGOUT_CLEANUP_FAILED:
+		default:
+			iscsit_logout_post_handler_closesession(conn);
+			break;
+		}
+		ret = 0;
+		break;
+	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+		if (conn->cid == cmd->logout_cid) {
+			switch (cmd->logout_response) {
+			case ISCSI_LOGOUT_SUCCESS:
+			case ISCSI_LOGOUT_CLEANUP_FAILED:
+			default:
+				iscsit_logout_post_handler_samecid(conn);
+				break;
+			}
+			ret = 0;
+		} else {
+			switch (cmd->logout_response) {
+			case ISCSI_LOGOUT_SUCCESS:
+				iscsit_logout_post_handler_diffcid(conn,
+					cmd->logout_cid);
+				break;
+			case ISCSI_LOGOUT_CID_NOT_FOUND:
+			case ISCSI_LOGOUT_CLEANUP_FAILED:
+			default:
+				break;
+			}
+			ret = 1;
+		}
+		break;
+	case ISCSI_LOGOUT_REASON_RECOVERY:
+		switch (cmd->logout_response) {
+		case ISCSI_LOGOUT_SUCCESS:
+		case ISCSI_LOGOUT_CID_NOT_FOUND:
+		case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED:
+		case ISCSI_LOGOUT_CLEANUP_FAILED:
+		default:
+			break;
+		}
+		ret = 1;
+		break;
+	default:
+		break;
+
+	}
+	return ret;
+}
+
+void iscsit_fail_session(struct iscsi_session *sess)
+{
+	struct iscsi_conn *conn;
+
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+		pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
+		conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
+	sess->session_state = TARG_SESS_STATE_FAILED;
+}
+
+int iscsit_free_session(struct iscsi_session *sess)
+{
+	u16 conn_count = atomic_read(&sess->nconn);
+	struct iscsi_conn *conn, *conn_tmp = NULL;
+	int is_last;
+
+	spin_lock_bh(&sess->conn_lock);
+	atomic_set(&sess->sleep_on_sess_wait_comp, 1);
+
+	list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
+			conn_list) {
+		if (conn_count == 0)
+			break;
+
+		if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
+			is_last = 1;
+		} else {
+			iscsit_inc_conn_usage_count(conn_tmp);
+			is_last = 0;
+		}
+		iscsit_inc_conn_usage_count(conn);
+
+		spin_unlock_bh(&sess->conn_lock);
+		iscsit_cause_connection_reinstatement(conn, 1);
+		spin_lock_bh(&sess->conn_lock);
+
+		iscsit_dec_conn_usage_count(conn);
+		if (is_last == 0)
+			iscsit_dec_conn_usage_count(conn_tmp);
+
+		conn_count--;
+	}
+
+	if (atomic_read(&sess->nconn)) {
+		spin_unlock_bh(&sess->conn_lock);
+		wait_for_completion(&sess->session_wait_comp);
+	} else
+		spin_unlock_bh(&sess->conn_lock);
+
+	iscsit_close_session(sess);
+	return 0;
+}
+
+void iscsit_stop_session(
+	struct iscsi_session *sess,
+	int session_sleep,
+	int connection_sleep)
+{
+	u16 conn_count = atomic_read(&sess->nconn);
+	struct iscsi_conn *conn, *conn_tmp = NULL;
+	int is_last;
+
+	spin_lock_bh(&sess->conn_lock);
+	if (session_sleep)
+		atomic_set(&sess->sleep_on_sess_wait_comp, 1);
+
+	if (connection_sleep) {
+		list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
+				conn_list) {
+			if (conn_count == 0)
+				break;
+
+			if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
+				is_last = 1;
+			} else {
+				iscsit_inc_conn_usage_count(conn_tmp);
+				is_last = 0;
+			}
+			iscsit_inc_conn_usage_count(conn);
+
+			spin_unlock_bh(&sess->conn_lock);
+			iscsit_cause_connection_reinstatement(conn, 1);
+			spin_lock_bh(&sess->conn_lock);
+
+			iscsit_dec_conn_usage_count(conn);
+			if (is_last == 0)
+				iscsit_dec_conn_usage_count(conn_tmp);
+			conn_count--;
+		}
+	} else {
+		list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
+			iscsit_cause_connection_reinstatement(conn, 0);
+	}
+
+	if (session_sleep && atomic_read(&sess->nconn)) {
+		spin_unlock_bh(&sess->conn_lock);
+		wait_for_completion(&sess->session_wait_comp);
+	} else
+		spin_unlock_bh(&sess->conn_lock);
+}
+
+int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
+{
+	struct iscsi_session *sess;
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+	struct se_session *se_sess, *se_sess_tmp;
+	int session_count = 0;
+
+	spin_lock_bh(&se_tpg->session_lock);
+	if (tpg->nsessions && !force) {
+		spin_unlock_bh(&se_tpg->session_lock);
+		return -1;
+	}
+
+	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+			sess_list) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+
+		spin_lock(&sess->conn_lock);
+		if (atomic_read(&sess->session_fall_back_to_erl0) ||
+		    atomic_read(&sess->session_logout) ||
+		    (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+			spin_unlock(&sess->conn_lock);
+			continue;
+		}
+		atomic_set(&sess->session_reinstatement, 1);
+		spin_unlock(&sess->conn_lock);
+		spin_unlock_bh(&se_tpg->session_lock);
+
+		iscsit_free_session(sess);
+		spin_lock_bh(&se_tpg->session_lock);
+
+		session_count++;
+	}
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	pr_debug("Released %d iSCSI Session(s) from Target Portal"
+			" Group: %hu\n", session_count, tpg->tpgt);
+	return 0;
+}
+
+MODULE_DESCRIPTION("iSCSI-Target Driver for mainline target infrastructure");
+MODULE_VERSION("4.1.x");
+MODULE_AUTHOR("nab@Linux-iSCSI.org");
+MODULE_LICENSE("GPL");
+
+module_init(iscsi_target_init_module);
+module_exit(iscsi_target_cleanup_module);
diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h
new file mode 100644
index 0000000..5db2dde
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target.h
@@ -0,0 +1,42 @@
+#ifndef ISCSI_TARGET_H
+#define ISCSI_TARGET_H
+
+extern struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *);
+extern struct iscsi_tiqn *iscsit_get_tiqn(unsigned char *, int);
+extern void iscsit_put_tiqn_for_login(struct iscsi_tiqn *);
+extern struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *);
+extern void iscsit_del_tiqn(struct iscsi_tiqn *);
+extern int iscsit_access_np(struct iscsi_np *, struct iscsi_portal_group *);
+extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *);
+extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *,
+				char *, int);
+extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
+				struct iscsi_portal_group *);
+extern int iscsit_del_np(struct iscsi_np *);
+extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *);
+extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8);
+extern int iscsit_send_r2t(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_build_r2ts_for_cmd(struct iscsi_cmd *, struct iscsi_conn *, int);
+extern void iscsit_thread_get_cpumask(struct iscsi_conn *);
+extern int iscsi_target_tx_thread(void *);
+extern int iscsi_target_rx_thread(void *);
+extern int iscsit_close_connection(struct iscsi_conn *);
+extern int iscsit_close_session(struct iscsi_session *);
+extern void iscsit_fail_session(struct iscsi_session *);
+extern int iscsit_free_session(struct iscsi_session *);
+extern void iscsit_stop_session(struct iscsi_session *, int, int);
+extern int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *, int);
+
+extern struct iscsit_global *iscsit_global;
+extern struct target_fabric_configfs *lio_target_fabric_configfs;
+
+extern struct kmem_cache *lio_dr_cache;
+extern struct kmem_cache *lio_ooo_cache;
+extern struct kmem_cache *lio_cmd_cache;
+extern struct kmem_cache *lio_qr_cache;
+extern struct kmem_cache *lio_r2t_cache;
+
+#endif   /*** ISCSI_TARGET_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c
new file mode 100644
index 0000000..11fd743
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_auth.c
@@ -0,0 +1,490 @@
+/*******************************************************************************
+ * This file houses the main functions for the iSCSI CHAP support
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_auth.h"
+
+static unsigned char chap_asciihex_to_binaryhex(unsigned char val[2])
+{
+	unsigned char result = 0;
+	/*
+	 * MSB
+	 */
+	if ((val[0] >= 'a') && (val[0] <= 'f'))
+		result = ((val[0] - 'a' + 10) & 0xf) << 4;
+	else
+		if ((val[0] >= 'A') && (val[0] <= 'F'))
+			result = ((val[0] - 'A' + 10) & 0xf) << 4;
+		else /* digit */
+			result = ((val[0] - '0') & 0xf) << 4;
+	/*
+	 * LSB
+	 */
+	if ((val[1] >= 'a') && (val[1] <= 'f'))
+		result |= ((val[1] - 'a' + 10) & 0xf);
+	else
+		if ((val[1] >= 'A') && (val[1] <= 'F'))
+			result |= ((val[1] - 'A' + 10) & 0xf);
+		else /* digit */
+			result |= ((val[1] - '0') & 0xf);
+
+	return result;
+}
+
+static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len)
+{
+	int i, j = 0;
+
+	for (i = 0; i < len; i += 2) {
+		dst[j++] = (unsigned char) chap_asciihex_to_binaryhex(&src[i]);
+	}
+
+	dst[j] = '\0';
+	return j;
+}
+
+static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len)
+{
+	int i;
+
+	for (i = 0; i < src_len; i++) {
+		sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff);
+	}
+}
+
+static void chap_set_random(char *data, int length)
+{
+	long r;
+	unsigned n;
+
+	while (length > 0) {
+		get_random_bytes(&r, sizeof(long));
+		r = r ^ (r >> 8);
+		r = r ^ (r >> 4);
+		n = r & 0x7;
+
+		get_random_bytes(&r, sizeof(long));
+		r = r ^ (r >> 8);
+		r = r ^ (r >> 5);
+		n = (n << 3) | (r & 0x7);
+
+		get_random_bytes(&r, sizeof(long));
+		r = r ^ (r >> 8);
+		r = r ^ (r >> 5);
+		n = (n << 2) | (r & 0x3);
+
+		*data++ = n;
+		length--;
+	}
+}
+
+static void chap_gen_challenge(
+	struct iscsi_conn *conn,
+	int caller,
+	char *c_str,
+	unsigned int *c_len)
+{
+	unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1];
+	struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol;
+
+	memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
+
+	chap_set_random(chap->challenge, CHAP_CHALLENGE_LENGTH);
+	chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,
+				CHAP_CHALLENGE_LENGTH);
+	/*
+	 * Set CHAP_C, and copy the generated challenge into c_str.
+	 */
+	*c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);
+	*c_len += 1;
+
+	pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",
+			challenge_asciihex);
+}
+
+
+static struct iscsi_chap *chap_server_open(
+	struct iscsi_conn *conn,
+	struct iscsi_node_auth *auth,
+	const char *a_str,
+	char *aic_str,
+	unsigned int *aic_len)
+{
+	struct iscsi_chap *chap;
+
+	if (!(auth->naf_flags & NAF_USERID_SET) ||
+	    !(auth->naf_flags & NAF_PASSWORD_SET)) {
+		pr_err("CHAP user or password not set for"
+				" Initiator ACL\n");
+		return NULL;
+	}
+
+	conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);
+	if (!conn->auth_protocol)
+		return NULL;
+
+	chap = (struct iscsi_chap *) conn->auth_protocol;
+	/*
+	 * We only support MD5 MDA presently.
+	 */
+	if (strncmp(a_str, "CHAP_A=5", 8)) {
+		pr_err("CHAP_A is not MD5.\n");
+		return NULL;
+	}
+	pr_debug("[server] Got CHAP_A=5\n");
+	/*
+	 * Send back CHAP_A set to MD5.
+	 */
+	*aic_len = sprintf(aic_str, "CHAP_A=5");
+	*aic_len += 1;
+	chap->digest_type = CHAP_DIGEST_MD5;
+	pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+	/*
+	 * Set Identifier.
+	 */
+	chap->id = ISCSI_TPG_C(conn)->tpg_chap_id++;
+	*aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
+	*aic_len += 1;
+	pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
+	/*
+	 * Generate Challenge.
+	 */
+	chap_gen_challenge(conn, 1, aic_str, aic_len);
+
+	return chap;
+}
+
+static void chap_close(struct iscsi_conn *conn)
+{
+	kfree(conn->auth_protocol);
+	conn->auth_protocol = NULL;
+}
+
+static int chap_server_compute_md5(
+	struct iscsi_conn *conn,
+	struct iscsi_node_auth *auth,
+	char *nr_in_ptr,
+	char *nr_out_ptr,
+	unsigned int *nr_out_len)
+{
+	char *endptr;
+	unsigned char id, digest[MD5_SIGNATURE_SIZE];
+	unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2];
+	unsigned char identifier[10], *challenge = NULL;
+	unsigned char *challenge_binhex = NULL;
+	unsigned char client_digest[MD5_SIGNATURE_SIZE];
+	unsigned char server_digest[MD5_SIGNATURE_SIZE];
+	unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
+	struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol;
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+	int auth_ret = -1, ret, challenge_len;
+
+	memset(identifier, 0, 10);
+	memset(chap_n, 0, MAX_CHAP_N_SIZE);
+	memset(chap_r, 0, MAX_RESPONSE_LENGTH);
+	memset(digest, 0, MD5_SIGNATURE_SIZE);
+	memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2);
+	memset(client_digest, 0, MD5_SIGNATURE_SIZE);
+	memset(server_digest, 0, MD5_SIGNATURE_SIZE);
+
+	challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
+	if (!challenge) {
+		pr_err("Unable to allocate challenge buffer\n");
+		goto out;
+	}
+
+	challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
+	if (!challenge_binhex) {
+		pr_err("Unable to allocate challenge_binhex buffer\n");
+		goto out;
+	}
+	/*
+	 * Extract CHAP_N.
+	 */
+	if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,
+				&type) < 0) {
+		pr_err("Could not find CHAP_N.\n");
+		goto out;
+	}
+	if (type == HEX) {
+		pr_err("Could not find CHAP_N.\n");
+		goto out;
+	}
+
+	if (memcmp(chap_n, auth->userid, strlen(auth->userid)) != 0) {
+		pr_err("CHAP_N values do not match!\n");
+		goto out;
+	}
+	pr_debug("[server] Got CHAP_N=%s\n", chap_n);
+	/*
+	 * Extract CHAP_R.
+	 */
+	if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,
+				&type) < 0) {
+		pr_err("Could not find CHAP_R.\n");
+		goto out;
+	}
+	if (type != HEX) {
+		pr_err("Could not find CHAP_R.\n");
+		goto out;
+	}
+
+	pr_debug("[server] Got CHAP_R=%s\n", chap_r);
+	chap_string_to_hex(client_digest, chap_r, strlen(chap_r));
+
+	tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		pr_err("Unable to allocate struct crypto_hash\n");
+		goto out;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	ret = crypto_hash_init(&desc);
+	if (ret < 0) {
+		pr_err("crypto_hash_init() failed\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	sg_init_one(&sg, (void *)&chap->id, 1);
+	ret = crypto_hash_update(&desc, &sg, 1);
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for id\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	sg_init_one(&sg, (void *)&auth->password, strlen(auth->password));
+	ret = crypto_hash_update(&desc, &sg, strlen(auth->password));
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for password\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	sg_init_one(&sg, (void *)chap->challenge, CHAP_CHALLENGE_LENGTH);
+	ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH);
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for challenge\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	ret = crypto_hash_final(&desc, server_digest);
+	if (ret < 0) {
+		pr_err("crypto_hash_final() failed for server digest\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+	crypto_free_hash(tfm);
+
+	chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE);
+	pr_debug("[server] MD5 Server Digest: %s\n", response);
+
+	if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) {
+		pr_debug("[server] MD5 Digests do not match!\n\n");
+		goto out;
+	} else
+		pr_debug("[server] MD5 Digests match, CHAP connetication"
+				" successful.\n\n");
+	/*
+	 * One way authentication has succeeded, return now if mutual
+	 * authentication is not enabled.
+	 */
+	if (!auth->authenticate_target) {
+		kfree(challenge);
+		kfree(challenge_binhex);
+		return 0;
+	}
+	/*
+	 * Get CHAP_I.
+	 */
+	if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) {
+		pr_err("Could not find CHAP_I.\n");
+		goto out;
+	}
+
+	if (type == HEX)
+		id = (unsigned char)simple_strtoul((char *)&identifier[2],
+					&endptr, 0);
+	else
+		id = (unsigned char)simple_strtoul(identifier, &endptr, 0);
+	/*
+	 * RFC 1994 says Identifier is no more than octet (8 bits).
+	 */
+	pr_debug("[server] Got CHAP_I=%d\n", id);
+	/*
+	 * Get CHAP_C.
+	 */
+	if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,
+			challenge, &type) < 0) {
+		pr_err("Could not find CHAP_C.\n");
+		goto out;
+	}
+
+	if (type != HEX) {
+		pr_err("Could not find CHAP_C.\n");
+		goto out;
+	}
+	pr_debug("[server] Got CHAP_C=%s\n", challenge);
+	challenge_len = chap_string_to_hex(challenge_binhex, challenge,
+				strlen(challenge));
+	if (!challenge_len) {
+		pr_err("Unable to convert incoming challenge\n");
+		goto out;
+	}
+	/*
+	 * Generate CHAP_N and CHAP_R for mutual authentication.
+	 */
+	tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		pr_err("Unable to allocate struct crypto_hash\n");
+		goto out;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	ret = crypto_hash_init(&desc);
+	if (ret < 0) {
+		pr_err("crypto_hash_init() failed\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	sg_init_one(&sg, (void *)&id, 1);
+	ret = crypto_hash_update(&desc, &sg, 1);
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for id\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	sg_init_one(&sg, (void *)auth->password_mutual,
+				strlen(auth->password_mutual));
+	ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual));
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for"
+				" password_mutual\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+	/*
+	 * Convert received challenge to binary hex.
+	 */
+	sg_init_one(&sg, (void *)challenge_binhex, challenge_len);
+	ret = crypto_hash_update(&desc, &sg, challenge_len);
+	if (ret < 0) {
+		pr_err("crypto_hash_update() failed for ma challenge\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+
+	ret = crypto_hash_final(&desc, digest);
+	if (ret < 0) {
+		pr_err("crypto_hash_final() failed for ma digest\n");
+		crypto_free_hash(tfm);
+		goto out;
+	}
+	crypto_free_hash(tfm);
+	/*
+	 * Generate CHAP_N and CHAP_R.
+	 */
+	*nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);
+	*nr_out_len += 1;
+	pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);
+	/*
+	 * Convert response from binary hex to ascii hext.
+	 */
+	chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE);
+	*nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",
+			response);
+	*nr_out_len += 1;
+	pr_debug("[server] Sending CHAP_R=0x%s\n", response);
+	auth_ret = 0;
+out:
+	kfree(challenge);
+	kfree(challenge_binhex);
+	return auth_ret;
+}
+
+static int chap_got_response(
+	struct iscsi_conn *conn,
+	struct iscsi_node_auth *auth,
+	char *nr_in_ptr,
+	char *nr_out_ptr,
+	unsigned int *nr_out_len)
+{
+	struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol;
+
+	switch (chap->digest_type) {
+	case CHAP_DIGEST_MD5:
+		if (chap_server_compute_md5(conn, auth, nr_in_ptr,
+				nr_out_ptr, nr_out_len) < 0)
+			return -1;
+		return 0;
+	default:
+		pr_err("Unknown CHAP digest type %d!\n",
+				chap->digest_type);
+		return -1;
+	}
+}
+
+u32 chap_main_loop(
+	struct iscsi_conn *conn,
+	struct iscsi_node_auth *auth,
+	char *in_text,
+	char *out_text,
+	int *in_len,
+	int *out_len)
+{
+	struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol;
+
+	if (!chap) {
+		chap = chap_server_open(conn, auth, in_text, out_text, out_len);
+		if (!chap)
+			return 2;
+		chap->chap_state = CHAP_STAGE_SERVER_AIC;
+		return 0;
+	} else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {
+		convert_null_to_semi(in_text, *in_len);
+		if (chap_got_response(conn, auth, in_text, out_text,
+				out_len) < 0) {
+			chap_close(conn);
+			return 2;
+		}
+		if (auth->authenticate_target)
+			chap->chap_state = CHAP_STAGE_SERVER_NR;
+		else
+			*out_len = 0;
+		chap_close(conn);
+		return 1;
+	}
+
+	return 2;
+}
diff --git a/drivers/target/iscsi/iscsi_target_auth.h b/drivers/target/iscsi/iscsi_target_auth.h
new file mode 100644
index 0000000..2f463c0
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_auth.h
@@ -0,0 +1,31 @@
+#ifndef _ISCSI_CHAP_H_
+#define _ISCSI_CHAP_H_
+
+#define CHAP_DIGEST_MD5		5
+#define CHAP_DIGEST_SHA		6
+
+#define CHAP_CHALLENGE_LENGTH	16
+#define CHAP_CHALLENGE_STR_LEN	4096
+#define MAX_RESPONSE_LENGTH	64	/* sufficient for MD5 */
+#define	MAX_CHAP_N_SIZE		512
+
+#define MD5_SIGNATURE_SIZE	16	/* 16 bytes in a MD5 message digest */
+
+#define CHAP_STAGE_CLIENT_A	1
+#define CHAP_STAGE_SERVER_AIC	2
+#define CHAP_STAGE_CLIENT_NR	3
+#define CHAP_STAGE_CLIENT_NRIC	4
+#define CHAP_STAGE_SERVER_NR	5
+
+extern u32 chap_main_loop(struct iscsi_conn *, struct iscsi_node_auth *, char *, char *,
+				int *, int *);
+
+struct iscsi_chap {
+	unsigned char	digest_type;
+	unsigned char	id;
+	unsigned char	challenge[CHAP_CHALLENGE_LENGTH];
+	unsigned int	authenticate_target;
+	unsigned int	chap_state;
+} ____cacheline_aligned;
+
+#endif   /*** _ISCSI_CHAP_H_ ***/
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
new file mode 100644
index 0000000..32bb92c
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -0,0 +1,1882 @@
+/*******************************************************************************
+ * This file contains the configfs implementation for iSCSI Target mode
+ * from the LIO-Target Project.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+#include <linux/configfs.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_fabric_lib.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_nodeattrib.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_stat.h"
+#include "iscsi_target_configfs.h"
+
+struct target_fabric_configfs *lio_target_fabric_configfs;
+
+struct lio_target_configfs_attribute {
+	struct configfs_attribute attr;
+	ssize_t (*show)(void *, char *);
+	ssize_t (*store)(void *, const char *, size_t);
+};
+
+struct iscsi_portal_group *lio_get_tpg_from_tpg_item(
+	struct config_item *item,
+	struct iscsi_tiqn **tiqn_out)
+{
+	struct se_portal_group *se_tpg = container_of(to_config_group(item),
+					struct se_portal_group, tpg_group);
+	struct iscsi_portal_group *tpg =
+			(struct iscsi_portal_group *)se_tpg->se_tpg_fabric_ptr;
+	int ret;
+
+	if (!tpg) {
+		pr_err("Unable to locate struct iscsi_portal_group "
+			"pointer\n");
+		return NULL;
+	}
+	ret = iscsit_get_tpg(tpg);
+	if (ret < 0)
+		return NULL;
+
+	*tiqn_out = tpg->tpg_tiqn;
+	return tpg;
+}
+
+/* Start items for lio_target_portal_cit */
+
+static ssize_t lio_target_np_show_sctp(
+	struct se_tpg_np *se_tpg_np,
+	char *page)
+{
+	struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+				struct iscsi_tpg_np, se_tpg_np);
+	struct iscsi_tpg_np *tpg_np_sctp;
+	ssize_t rb;
+
+	tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP);
+	if (tpg_np_sctp)
+		rb = sprintf(page, "1\n");
+	else
+		rb = sprintf(page, "0\n");
+
+	return rb;
+}
+
+static ssize_t lio_target_np_store_sctp(
+	struct se_tpg_np *se_tpg_np,
+	const char *page,
+	size_t count)
+{
+	struct iscsi_np *np;
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+				struct iscsi_tpg_np, se_tpg_np);
+	struct iscsi_tpg_np *tpg_np_sctp = NULL;
+	char *endptr;
+	u32 op;
+	int ret;
+
+	op = simple_strtoul(page, &endptr, 0);
+	if ((op != 1) && (op != 0)) {
+		pr_err("Illegal value for tpg_enable: %u\n", op);
+		return -EINVAL;
+	}
+	np = tpg_np->tpg_np;
+	if (!np) {
+		pr_err("Unable to locate struct iscsi_np from"
+				" struct iscsi_tpg_np\n");
+		return -EINVAL;
+	}
+
+	tpg = tpg_np->tpg;
+	if (iscsit_get_tpg(tpg) < 0)
+		return -EINVAL;
+
+	if (op) {
+		/*
+		 * Use existing np->np_sockaddr for SCTP network portal reference
+		 */
+		tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
+					np->np_ip, tpg_np, ISCSI_SCTP_TCP);
+		if (!tpg_np_sctp || IS_ERR(tpg_np_sctp))
+			goto out;
+	} else {
+		tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP);
+		if (!tpg_np_sctp)
+			goto out;
+
+		ret = iscsit_tpg_del_network_portal(tpg, tpg_np_sctp);
+		if (ret < 0)
+			goto out;
+	}
+
+	iscsit_put_tpg(tpg);
+	return count;
+out:
+	iscsit_put_tpg(tpg);
+	return -EINVAL;
+}
+
+TF_NP_BASE_ATTR(lio_target, sctp, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_portal_attrs[] = {
+	&lio_target_np_sctp.attr,
+	NULL,
+};
+
+/* Stop items for lio_target_portal_cit */
+
+/* Start items for lio_target_np_cit */
+
+#define MAX_PORTAL_LEN		256
+
+struct se_tpg_np *lio_target_call_addnptotpg(
+	struct se_portal_group *se_tpg,
+	struct config_group *group,
+	const char *name)
+{
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tpg_np *tpg_np;
+	char *str, *str2, *ip_str, *port_str;
+	struct __kernel_sockaddr_storage sockaddr;
+	struct sockaddr_in *sock_in;
+	struct sockaddr_in6 *sock_in6;
+	unsigned long port;
+	int ret;
+	char buf[MAX_PORTAL_LEN + 1];
+
+	if (strlen(name) > MAX_PORTAL_LEN) {
+		pr_err("strlen(name): %d exceeds MAX_PORTAL_LEN: %d\n",
+			(int)strlen(name), MAX_PORTAL_LEN);
+		return ERR_PTR(-EOVERFLOW);
+	}
+	memset(buf, 0, MAX_PORTAL_LEN + 1);
+	snprintf(buf, MAX_PORTAL_LEN, "%s", name);
+
+	memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage));
+
+	str = strstr(buf, "[");
+	if (str) {
+		const char *end;
+
+		str2 = strstr(str, "]");
+		if (!str2) {
+			pr_err("Unable to locate trailing \"]\""
+				" in IPv6 iSCSI network portal address\n");
+			return ERR_PTR(-EINVAL);
+		}
+		str++; /* Skip over leading "[" */
+		*str2 = '\0'; /* Terminate the IPv6 address */
+		str2++; /* Skip over the "]" */
+		port_str = strstr(str2, ":");
+		if (!port_str) {
+			pr_err("Unable to locate \":port\""
+				" in IPv6 iSCSI network portal address\n");
+			return ERR_PTR(-EINVAL);
+		}
+		*port_str = '\0'; /* Terminate string for IP */
+		port_str++; /* Skip over ":" */
+
+		ret = strict_strtoul(port_str, 0, &port);
+		if (ret < 0) {
+			pr_err("strict_strtoul() failed for port_str: %d\n", ret);
+			return ERR_PTR(ret);
+		}
+		sock_in6 = (struct sockaddr_in6 *)&sockaddr;
+		sock_in6->sin6_family = AF_INET6;
+		sock_in6->sin6_port = htons((unsigned short)port);
+		ret = in6_pton(str, IPV6_ADDRESS_SPACE,
+				(void *)&sock_in6->sin6_addr.in6_u, -1, &end);
+		if (ret <= 0) {
+			pr_err("in6_pton returned: %d\n", ret);
+			return ERR_PTR(-EINVAL);
+		}
+	} else {
+		str = ip_str = &buf[0];
+		port_str = strstr(ip_str, ":");
+		if (!port_str) {
+			pr_err("Unable to locate \":port\""
+				" in IPv4 iSCSI network portal address\n");
+			return ERR_PTR(-EINVAL);
+		}
+		*port_str = '\0'; /* Terminate string for IP */
+		port_str++; /* Skip over ":" */
+
+		ret = strict_strtoul(port_str, 0, &port);
+		if (ret < 0) {
+			pr_err("strict_strtoul() failed for port_str: %d\n", ret);
+			return ERR_PTR(ret);
+		}
+		sock_in = (struct sockaddr_in *)&sockaddr;
+		sock_in->sin_family = AF_INET;
+		sock_in->sin_port = htons((unsigned short)port);
+		sock_in->sin_addr.s_addr = in_aton(ip_str);
+	}
+	tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg);
+	ret = iscsit_get_tpg(tpg);
+	if (ret < 0)
+		return ERR_PTR(-EINVAL);
+
+	pr_debug("LIO_Target_ConfigFS: REGISTER -> %s TPGT: %hu"
+		" PORTAL: %s\n",
+		config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
+		tpg->tpgt, name);
+	/*
+	 * Assume ISCSI_TCP by default.  Other network portals for other
+	 * iSCSI fabrics:
+	 *
+	 * Traditional iSCSI over SCTP (initial support)
+	 * iSER/TCP (TODO, hardware available)
+	 * iSER/SCTP (TODO, software emulation with osc-iwarp)
+	 * iSER/IB (TODO, hardware available)
+	 *
+	 * can be enabled with atributes under
+	 * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/
+	 *
+	 */
+	tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL,
+				ISCSI_TCP);
+	if (IS_ERR(tpg_np)) {
+		iscsit_put_tpg(tpg);
+		return ERR_PTR(PTR_ERR(tpg_np));
+	}
+	pr_debug("LIO_Target_ConfigFS: addnptotpg done!\n");
+
+	iscsit_put_tpg(tpg);
+	return &tpg_np->se_tpg_np;
+}
+
+static void lio_target_call_delnpfromtpg(
+	struct se_tpg_np *se_tpg_np)
+{
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tpg_np *tpg_np;
+	struct se_portal_group *se_tpg;
+	int ret;
+
+	tpg_np = container_of(se_tpg_np, struct iscsi_tpg_np, se_tpg_np);
+	tpg = tpg_np->tpg;
+	ret = iscsit_get_tpg(tpg);
+	if (ret < 0)
+		return;
+
+	se_tpg = &tpg->tpg_se_tpg;
+	pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu"
+		" PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
+		tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port);
+
+	ret = iscsit_tpg_del_network_portal(tpg, tpg_np);
+	if (ret < 0)
+		goto out;
+
+	pr_debug("LIO_Target_ConfigFS: delnpfromtpg done!\n");
+out:
+	iscsit_put_tpg(tpg);
+}
+
+/* End items for lio_target_np_cit */
+
+/* Start items for lio_target_nacl_attrib_cit */
+
+#define DEF_NACL_ATTRIB(name)						\
+static ssize_t iscsi_nacl_attrib_show_##name(				\
+	struct se_node_acl *se_nacl,					\
+	char *page)							\
+{									\
+	struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
+					se_node_acl);			\
+									\
+	return sprintf(page, "%u\n", ISCSI_NODE_ATTRIB(nacl)->name);	\
+}									\
+									\
+static ssize_t iscsi_nacl_attrib_store_##name(				\
+	struct se_node_acl *se_nacl,					\
+	const char *page,						\
+	size_t count)							\
+{									\
+	struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
+					se_node_acl);			\
+	char *endptr;							\
+	u32 val;							\
+	int ret;							\
+									\
+	val = simple_strtoul(page, &endptr, 0);				\
+	ret = iscsit_na_##name(nacl, val);				\
+	if (ret < 0)							\
+		return ret;						\
+									\
+	return count;							\
+}
+
+#define NACL_ATTR(_name, _mode) TF_NACL_ATTRIB_ATTR(iscsi, _name, _mode);
+/*
+ * Define iscsi_node_attrib_s_dataout_timeout
+ */
+DEF_NACL_ATTRIB(dataout_timeout);
+NACL_ATTR(dataout_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_dataout_timeout_retries
+ */
+DEF_NACL_ATTRIB(dataout_timeout_retries);
+NACL_ATTR(dataout_timeout_retries, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_default_erl
+ */
+DEF_NACL_ATTRIB(default_erl);
+NACL_ATTR(default_erl, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_nopin_timeout
+ */
+DEF_NACL_ATTRIB(nopin_timeout);
+NACL_ATTR(nopin_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_nopin_response_timeout
+ */
+DEF_NACL_ATTRIB(nopin_response_timeout);
+NACL_ATTR(nopin_response_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_datain_pdu_offsets
+ */
+DEF_NACL_ATTRIB(random_datain_pdu_offsets);
+NACL_ATTR(random_datain_pdu_offsets, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_datain_seq_offsets
+ */
+DEF_NACL_ATTRIB(random_datain_seq_offsets);
+NACL_ATTR(random_datain_seq_offsets, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_r2t_offsets
+ */
+DEF_NACL_ATTRIB(random_r2t_offsets);
+NACL_ATTR(random_r2t_offsets, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_nacl_attrib_attrs[] = {
+	&iscsi_nacl_attrib_dataout_timeout.attr,
+	&iscsi_nacl_attrib_dataout_timeout_retries.attr,
+	&iscsi_nacl_attrib_default_erl.attr,
+	&iscsi_nacl_attrib_nopin_timeout.attr,
+	&iscsi_nacl_attrib_nopin_response_timeout.attr,
+	&iscsi_nacl_attrib_random_datain_pdu_offsets.attr,
+	&iscsi_nacl_attrib_random_datain_seq_offsets.attr,
+	&iscsi_nacl_attrib_random_r2t_offsets.attr,
+	NULL,
+};
+
+/* End items for lio_target_nacl_attrib_cit */
+
+/* Start items for lio_target_nacl_auth_cit */
+
+#define __DEF_NACL_AUTH_STR(prefix, name, flags)			\
+static ssize_t __iscsi_##prefix##_show_##name(				\
+	struct iscsi_node_acl *nacl,					\
+	char *page)							\
+{									\
+	struct iscsi_node_auth *auth = &nacl->node_auth;		\
+									\
+	if (!capable(CAP_SYS_ADMIN))					\
+		return -EPERM;						\
+	return snprintf(page, PAGE_SIZE, "%s\n", auth->name);		\
+}									\
+									\
+static ssize_t __iscsi_##prefix##_store_##name(				\
+	struct iscsi_node_acl *nacl,					\
+	const char *page,						\
+	size_t count)							\
+{									\
+	struct iscsi_node_auth *auth = &nacl->node_auth;		\
+									\
+	if (!capable(CAP_SYS_ADMIN))					\
+		return -EPERM;						\
+									\
+	snprintf(auth->name, PAGE_SIZE, "%s", page);			\
+	if (!strncmp("NULL", auth->name, 4))				\
+		auth->naf_flags &= ~flags;				\
+	else								\
+		auth->naf_flags |= flags;				\
+									\
+	if ((auth->naf_flags & NAF_USERID_IN_SET) &&			\
+	    (auth->naf_flags & NAF_PASSWORD_IN_SET))			\
+		auth->authenticate_target = 1;				\
+	else								\
+		auth->authenticate_target = 0;				\
+									\
+	return count;							\
+}
+
+#define __DEF_NACL_AUTH_INT(prefix, name)				\
+static ssize_t __iscsi_##prefix##_show_##name(				\
+	struct iscsi_node_acl *nacl,					\
+	char *page)							\
+{									\
+	struct iscsi_node_auth *auth = &nacl->node_auth;		\
+									\
+	if (!capable(CAP_SYS_ADMIN))					\
+		return -EPERM;						\
+									\
+	return snprintf(page, PAGE_SIZE, "%d\n", auth->name);		\
+}
+
+#define DEF_NACL_AUTH_STR(name, flags)					\
+	__DEF_NACL_AUTH_STR(nacl_auth, name, flags)			\
+static ssize_t iscsi_nacl_auth_show_##name(				\
+	struct se_node_acl *nacl,					\
+	char *page)							\
+{									\
+	return __iscsi_nacl_auth_show_##name(container_of(nacl,		\
+			struct iscsi_node_acl, se_node_acl), page);		\
+}									\
+static ssize_t iscsi_nacl_auth_store_##name(				\
+	struct se_node_acl *nacl,					\
+	const char *page,						\
+	size_t count)							\
+{									\
+	return __iscsi_nacl_auth_store_##name(container_of(nacl,	\
+			struct iscsi_node_acl, se_node_acl), page, count);	\
+}
+
+#define DEF_NACL_AUTH_INT(name)						\
+	__DEF_NACL_AUTH_INT(nacl_auth, name)				\
+static ssize_t iscsi_nacl_auth_show_##name(				\
+	struct se_node_acl *nacl,					\
+	char *page)							\
+{									\
+	return __iscsi_nacl_auth_show_##name(container_of(nacl,		\
+			struct iscsi_node_acl, se_node_acl), page);		\
+}
+
+#define AUTH_ATTR(_name, _mode)	TF_NACL_AUTH_ATTR(iscsi, _name, _mode);
+#define AUTH_ATTR_RO(_name) TF_NACL_AUTH_ATTR_RO(iscsi, _name);
+
+/*
+ * One-way authentication userid
+ */
+DEF_NACL_AUTH_STR(userid, NAF_USERID_SET);
+AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * One-way authentication password
+ */
+DEF_NACL_AUTH_STR(password, NAF_PASSWORD_SET);
+AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * Enforce mutual authentication
+ */
+DEF_NACL_AUTH_INT(authenticate_target);
+AUTH_ATTR_RO(authenticate_target);
+/*
+ * Mutual authentication userid
+ */
+DEF_NACL_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * Mutual authentication password
+ */
+DEF_NACL_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_nacl_auth_attrs[] = {
+	&iscsi_nacl_auth_userid.attr,
+	&iscsi_nacl_auth_password.attr,
+	&iscsi_nacl_auth_authenticate_target.attr,
+	&iscsi_nacl_auth_userid_mutual.attr,
+	&iscsi_nacl_auth_password_mutual.attr,
+	NULL,
+};
+
+/* End items for lio_target_nacl_auth_cit */
+
+/* Start items for lio_target_nacl_param_cit */
+
+#define DEF_NACL_PARAM(name)						\
+static ssize_t iscsi_nacl_param_show_##name(				\
+	struct se_node_acl *se_nacl,					\
+	char *page)							\
+{									\
+	struct iscsi_session *sess;					\
+	struct se_session *se_sess;					\
+	ssize_t rb;							\
+									\
+	spin_lock_bh(&se_nacl->nacl_sess_lock);				\
+	se_sess = se_nacl->nacl_sess;					\
+	if (!se_sess) {							\
+		rb = snprintf(page, PAGE_SIZE,				\
+			"No Active iSCSI Session\n");			\
+	} else {							\
+		sess = se_sess->fabric_sess_ptr;			\
+		rb = snprintf(page, PAGE_SIZE, "%u\n",			\
+			(u32)sess->sess_ops->name);			\
+	}								\
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);			\
+									\
+	return rb;							\
+}
+
+#define NACL_PARAM_ATTR(_name) TF_NACL_PARAM_ATTR_RO(iscsi, _name);
+
+DEF_NACL_PARAM(MaxConnections);
+NACL_PARAM_ATTR(MaxConnections);
+
+DEF_NACL_PARAM(InitialR2T);
+NACL_PARAM_ATTR(InitialR2T);
+
+DEF_NACL_PARAM(ImmediateData);
+NACL_PARAM_ATTR(ImmediateData);
+
+DEF_NACL_PARAM(MaxBurstLength);
+NACL_PARAM_ATTR(MaxBurstLength);
+
+DEF_NACL_PARAM(FirstBurstLength);
+NACL_PARAM_ATTR(FirstBurstLength);
+
+DEF_NACL_PARAM(DefaultTime2Wait);
+NACL_PARAM_ATTR(DefaultTime2Wait);
+
+DEF_NACL_PARAM(DefaultTime2Retain);
+NACL_PARAM_ATTR(DefaultTime2Retain);
+
+DEF_NACL_PARAM(MaxOutstandingR2T);
+NACL_PARAM_ATTR(MaxOutstandingR2T);
+
+DEF_NACL_PARAM(DataPDUInOrder);
+NACL_PARAM_ATTR(DataPDUInOrder);
+
+DEF_NACL_PARAM(DataSequenceInOrder);
+NACL_PARAM_ATTR(DataSequenceInOrder);
+
+DEF_NACL_PARAM(ErrorRecoveryLevel);
+NACL_PARAM_ATTR(ErrorRecoveryLevel);
+
+static struct configfs_attribute *lio_target_nacl_param_attrs[] = {
+	&iscsi_nacl_param_MaxConnections.attr,
+	&iscsi_nacl_param_InitialR2T.attr,
+	&iscsi_nacl_param_ImmediateData.attr,
+	&iscsi_nacl_param_MaxBurstLength.attr,
+	&iscsi_nacl_param_FirstBurstLength.attr,
+	&iscsi_nacl_param_DefaultTime2Wait.attr,
+	&iscsi_nacl_param_DefaultTime2Retain.attr,
+	&iscsi_nacl_param_MaxOutstandingR2T.attr,
+	&iscsi_nacl_param_DataPDUInOrder.attr,
+	&iscsi_nacl_param_DataSequenceInOrder.attr,
+	&iscsi_nacl_param_ErrorRecoveryLevel.attr,
+	NULL,
+};
+
+/* End items for lio_target_nacl_param_cit */
+
+/* Start items for lio_target_acl_cit */
+
+static ssize_t lio_target_nacl_show_info(
+	struct se_node_acl *se_nacl,
+	char *page)
+{
+	struct iscsi_session *sess;
+	struct iscsi_conn *conn;
+	struct se_session *se_sess;
+	ssize_t rb = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (!se_sess) {
+		rb += sprintf(page+rb, "No active iSCSI Session for Initiator"
+			" Endpoint: %s\n", se_nacl->initiatorname);
+	} else {
+		sess = se_sess->fabric_sess_ptr;
+
+		if (sess->sess_ops->InitiatorName)
+			rb += sprintf(page+rb, "InitiatorName: %s\n",
+				sess->sess_ops->InitiatorName);
+		if (sess->sess_ops->InitiatorAlias)
+			rb += sprintf(page+rb, "InitiatorAlias: %s\n",
+				sess->sess_ops->InitiatorAlias);
+
+		rb += sprintf(page+rb, "LIO Session ID: %u   "
+			"ISID: 0x%02x %02x %02x %02x %02x %02x  "
+			"TSIH: %hu  ", sess->sid,
+			sess->isid[0], sess->isid[1], sess->isid[2],
+			sess->isid[3], sess->isid[4], sess->isid[5],
+			sess->tsih);
+		rb += sprintf(page+rb, "SessionType: %s\n",
+				(sess->sess_ops->SessionType) ?
+				"Discovery" : "Normal");
+		rb += sprintf(page+rb, "Session State: ");
+		switch (sess->session_state) {
+		case TARG_SESS_STATE_FREE:
+			rb += sprintf(page+rb, "TARG_SESS_FREE\n");
+			break;
+		case TARG_SESS_STATE_ACTIVE:
+			rb += sprintf(page+rb, "TARG_SESS_STATE_ACTIVE\n");
+			break;
+		case TARG_SESS_STATE_LOGGED_IN:
+			rb += sprintf(page+rb, "TARG_SESS_STATE_LOGGED_IN\n");
+			break;
+		case TARG_SESS_STATE_FAILED:
+			rb += sprintf(page+rb, "TARG_SESS_STATE_FAILED\n");
+			break;
+		case TARG_SESS_STATE_IN_CONTINUE:
+			rb += sprintf(page+rb, "TARG_SESS_STATE_IN_CONTINUE\n");
+			break;
+		default:
+			rb += sprintf(page+rb, "ERROR: Unknown Session"
+					" State!\n");
+			break;
+		}
+
+		rb += sprintf(page+rb, "---------------------[iSCSI Session"
+				" Values]-----------------------\n");
+		rb += sprintf(page+rb, "  CmdSN/WR  :  CmdSN/WC  :  ExpCmdSN"
+				"  :  MaxCmdSN  :     ITT    :     TTT\n");
+		rb += sprintf(page+rb, " 0x%08x   0x%08x   0x%08x   0x%08x"
+				"   0x%08x   0x%08x\n",
+			sess->cmdsn_window,
+			(sess->max_cmd_sn - sess->exp_cmd_sn) + 1,
+			sess->exp_cmd_sn, sess->max_cmd_sn,
+			sess->init_task_tag, sess->targ_xfer_tag);
+		rb += sprintf(page+rb, "----------------------[iSCSI"
+				" Connections]-------------------------\n");
+
+		spin_lock(&sess->conn_lock);
+		list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+			rb += sprintf(page+rb, "CID: %hu  Connection"
+					" State: ", conn->cid);
+			switch (conn->conn_state) {
+			case TARG_CONN_STATE_FREE:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_FREE\n");
+				break;
+			case TARG_CONN_STATE_XPT_UP:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_XPT_UP\n");
+				break;
+			case TARG_CONN_STATE_IN_LOGIN:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_IN_LOGIN\n");
+				break;
+			case TARG_CONN_STATE_LOGGED_IN:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_LOGGED_IN\n");
+				break;
+			case TARG_CONN_STATE_IN_LOGOUT:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_IN_LOGOUT\n");
+				break;
+			case TARG_CONN_STATE_LOGOUT_REQUESTED:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_LOGOUT_REQUESTED\n");
+				break;
+			case TARG_CONN_STATE_CLEANUP_WAIT:
+				rb += sprintf(page+rb,
+					"TARG_CONN_STATE_CLEANUP_WAIT\n");
+				break;
+			default:
+				rb += sprintf(page+rb,
+					"ERROR: Unknown Connection State!\n");
+				break;
+			}
+
+			rb += sprintf(page+rb, "   Address %s %s", conn->login_ip,
+				(conn->network_transport == ISCSI_TCP) ?
+				"TCP" : "SCTP");
+			rb += sprintf(page+rb, "  StatSN: 0x%08x\n",
+				conn->stat_sn);
+		}
+		spin_unlock(&sess->conn_lock);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return rb;
+}
+
+TF_NACL_BASE_ATTR_RO(lio_target, info);
+
+static ssize_t lio_target_nacl_show_cmdsn_depth(
+	struct se_node_acl *se_nacl,
+	char *page)
+{
+	return sprintf(page, "%u\n", se_nacl->queue_depth);
+}
+
+static ssize_t lio_target_nacl_store_cmdsn_depth(
+	struct se_node_acl *se_nacl,
+	const char *page,
+	size_t count)
+{
+	struct se_portal_group *se_tpg = se_nacl->se_tpg;
+	struct iscsi_portal_group *tpg = container_of(se_tpg,
+			struct iscsi_portal_group, tpg_se_tpg);
+	struct config_item *acl_ci, *tpg_ci, *wwn_ci;
+	char *endptr;
+	u32 cmdsn_depth = 0;
+	int ret;
+
+	cmdsn_depth = simple_strtoul(page, &endptr, 0);
+	if (cmdsn_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) {
+		pr_err("Passed cmdsn_depth: %u exceeds"
+			" TA_DEFAULT_CMDSN_DEPTH_MAX: %u\n", cmdsn_depth,
+			TA_DEFAULT_CMDSN_DEPTH_MAX);
+		return -EINVAL;
+	}
+	acl_ci = &se_nacl->acl_group.cg_item;
+	if (!acl_ci) {
+		pr_err("Unable to locatel acl_ci\n");
+		return -EINVAL;
+	}
+	tpg_ci = &acl_ci->ci_parent->ci_group->cg_item;
+	if (!tpg_ci) {
+		pr_err("Unable to locate tpg_ci\n");
+		return -EINVAL;
+	}
+	wwn_ci = &tpg_ci->ci_group->cg_item;
+	if (!wwn_ci) {
+		pr_err("Unable to locate config_item wwn_ci\n");
+		return -EINVAL;
+	}
+
+	if (iscsit_get_tpg(tpg) < 0)
+		return -EINVAL;
+	/*
+	 * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1
+	 */
+	ret = iscsit_tpg_set_initiator_node_queue_depth(tpg,
+				config_item_name(acl_ci), cmdsn_depth, 1);
+
+	pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for"
+		"InitiatorName: %s\n", config_item_name(wwn_ci),
+		config_item_name(tpg_ci), cmdsn_depth,
+		config_item_name(acl_ci));
+
+	iscsit_put_tpg(tpg);
+	return (!ret) ? count : (ssize_t)ret;
+}
+
+TF_NACL_BASE_ATTR(lio_target, cmdsn_depth, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_initiator_attrs[] = {
+	&lio_target_nacl_info.attr,
+	&lio_target_nacl_cmdsn_depth.attr,
+	NULL,
+};
+
+static struct se_node_acl *lio_tpg_alloc_fabric_acl(
+	struct se_portal_group *se_tpg)
+{
+	struct iscsi_node_acl *acl;
+
+	acl = kzalloc(sizeof(struct iscsi_node_acl), GFP_KERNEL);
+	if (!acl) {
+		pr_err("Unable to allocate memory for struct iscsi_node_acl\n");
+		return NULL;
+	}
+
+	return &acl->se_node_acl;
+}
+
+static struct se_node_acl *lio_target_make_nodeacl(
+	struct se_portal_group *se_tpg,
+	struct config_group *group,
+	const char *name)
+{
+	struct config_group *stats_cg;
+	struct iscsi_node_acl *acl;
+	struct se_node_acl *se_nacl_new, *se_nacl;
+	struct iscsi_portal_group *tpg = container_of(se_tpg,
+			struct iscsi_portal_group, tpg_se_tpg);
+	u32 cmdsn_depth;
+
+	se_nacl_new = lio_tpg_alloc_fabric_acl(se_tpg);
+	if (!se_nacl_new)
+		return ERR_PTR(-ENOMEM);
+
+	acl = container_of(se_nacl_new, struct iscsi_node_acl,
+				se_node_acl);
+
+	cmdsn_depth = ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth;
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NdoeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+				name, cmdsn_depth);
+	if (IS_ERR(se_nacl))
+		return se_nacl;
+
+	stats_cg = &acl->se_node_acl.acl_fabric_stat_group;
+
+	stats_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+				GFP_KERNEL);
+	if (!stats_cg->default_groups) {
+		pr_err("Unable to allocate memory for"
+				" stats_cg->default_groups\n");
+		core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1);
+		kfree(acl);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	stats_cg->default_groups[0] = &NODE_STAT_GRPS(acl)->iscsi_sess_stats_group;
+	stats_cg->default_groups[1] = NULL;
+	config_group_init_type_name(&NODE_STAT_GRPS(acl)->iscsi_sess_stats_group,
+			"iscsi_sess_stats", &iscsi_stat_sess_cit);
+
+	return se_nacl;
+}
+
+static void lio_target_drop_nodeacl(
+	struct se_node_acl *se_nacl)
+{
+	struct se_portal_group *se_tpg = se_nacl->se_tpg;
+	struct iscsi_node_acl *acl = container_of(se_nacl,
+			struct iscsi_node_acl, se_node_acl);
+	struct config_item *df_item;
+	struct config_group *stats_cg;
+	int i;
+
+	stats_cg = &acl->se_node_acl.acl_fabric_stat_group;
+	for (i = 0; stats_cg->default_groups[i]; i++) {
+		df_item = &stats_cg->default_groups[i]->cg_item;
+		stats_cg->default_groups[i] = NULL;
+		config_item_put(df_item);
+	}
+	kfree(stats_cg->default_groups);
+
+	core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1);
+	kfree(acl);
+}
+
+/* End items for lio_target_acl_cit */
+
+/* Start items for lio_target_tpg_attrib_cit */
+
+#define DEF_TPG_ATTRIB(name)						\
+									\
+static ssize_t iscsi_tpg_attrib_show_##name(				\
+	struct se_portal_group *se_tpg,				\
+	char *page)							\
+{									\
+	struct iscsi_portal_group *tpg = container_of(se_tpg,		\
+			struct iscsi_portal_group, tpg_se_tpg);	\
+	ssize_t rb;							\
+									\
+	if (iscsit_get_tpg(tpg) < 0)					\
+		return -EINVAL;						\
+									\
+	rb = sprintf(page, "%u\n", ISCSI_TPG_ATTRIB(tpg)->name);	\
+	iscsit_put_tpg(tpg);						\
+	return rb;							\
+}									\
+									\
+static ssize_t iscsi_tpg_attrib_store_##name(				\
+	struct se_portal_group *se_tpg,				\
+	const char *page,						\
+	size_t count)							\
+{									\
+	struct iscsi_portal_group *tpg = container_of(se_tpg,		\
+			struct iscsi_portal_group, tpg_se_tpg);	\
+	char *endptr;							\
+	u32 val;							\
+	int ret;							\
+									\
+	if (iscsit_get_tpg(tpg) < 0)					\
+		return -EINVAL;						\
+									\
+	val = simple_strtoul(page, &endptr, 0);				\
+	ret = iscsit_ta_##name(tpg, val);				\
+	if (ret < 0)							\
+		goto out;						\
+									\
+	iscsit_put_tpg(tpg);						\
+	return count;							\
+out:									\
+	iscsit_put_tpg(tpg);						\
+	return ret;							\
+}
+
+#define TPG_ATTR(_name, _mode) TF_TPG_ATTRIB_ATTR(iscsi, _name, _mode);
+
+/*
+ * Define iscsi_tpg_attrib_s_authentication
+ */
+DEF_TPG_ATTRIB(authentication);
+TPG_ATTR(authentication, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_login_timeout
+ */
+DEF_TPG_ATTRIB(login_timeout);
+TPG_ATTR(login_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_netif_timeout
+ */
+DEF_TPG_ATTRIB(netif_timeout);
+TPG_ATTR(netif_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_generate_node_acls
+ */
+DEF_TPG_ATTRIB(generate_node_acls);
+TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_default_cmdsn_depth
+ */
+DEF_TPG_ATTRIB(default_cmdsn_depth);
+TPG_ATTR(default_cmdsn_depth, S_IRUGO | S_IWUSR);
+/*
+ Define iscsi_tpg_attrib_s_cache_dynamic_acls
+ */
+DEF_TPG_ATTRIB(cache_dynamic_acls);
+TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_demo_mode_write_protect
+ */
+DEF_TPG_ATTRIB(demo_mode_write_protect);
+TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_prod_mode_write_protect
+ */
+DEF_TPG_ATTRIB(prod_mode_write_protect);
+TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
+	&iscsi_tpg_attrib_authentication.attr,
+	&iscsi_tpg_attrib_login_timeout.attr,
+	&iscsi_tpg_attrib_netif_timeout.attr,
+	&iscsi_tpg_attrib_generate_node_acls.attr,
+	&iscsi_tpg_attrib_default_cmdsn_depth.attr,
+	&iscsi_tpg_attrib_cache_dynamic_acls.attr,
+	&iscsi_tpg_attrib_demo_mode_write_protect.attr,
+	&iscsi_tpg_attrib_prod_mode_write_protect.attr,
+	NULL,
+};
+
+/* End items for lio_target_tpg_attrib_cit */
+
+/* Start items for lio_target_tpg_param_cit */
+
+#define DEF_TPG_PARAM(name)						\
+static ssize_t iscsi_tpg_param_show_##name(				\
+	struct se_portal_group *se_tpg,					\
+	char *page)							\
+{									\
+	struct iscsi_portal_group *tpg = container_of(se_tpg,		\
+			struct iscsi_portal_group, tpg_se_tpg);		\
+	struct iscsi_param *param;					\
+	ssize_t rb;							\
+									\
+	if (iscsit_get_tpg(tpg) < 0)					\
+		return -EINVAL;						\
+									\
+	param = iscsi_find_param_from_key(__stringify(name),		\
+				tpg->param_list);			\
+	if (!param) {							\
+		iscsit_put_tpg(tpg);					\
+		return -EINVAL;						\
+	}								\
+	rb = snprintf(page, PAGE_SIZE, "%s\n", param->value);		\
+									\
+	iscsit_put_tpg(tpg);						\
+	return rb;							\
+}									\
+static ssize_t iscsi_tpg_param_store_##name(				\
+	struct se_portal_group *se_tpg,				\
+	const char *page,						\
+	size_t count)							\
+{									\
+	struct iscsi_portal_group *tpg = container_of(se_tpg,		\
+			struct iscsi_portal_group, tpg_se_tpg);		\
+	char *buf;							\
+	int ret;							\
+									\
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);				\
+	if (!buf)							\
+		return -ENOMEM;						\
+	snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page);	\
+	buf[strlen(buf)-1] = '\0'; /* Kill newline */			\
+									\
+	if (iscsit_get_tpg(tpg) < 0) {					\
+		kfree(buf);						\
+		return -EINVAL;						\
+	}								\
+									\
+	ret = iscsi_change_param_value(buf, tpg->param_list, 1);	\
+	if (ret < 0)							\
+		goto out;						\
+									\
+	kfree(buf);							\
+	iscsit_put_tpg(tpg);						\
+	return count;							\
+out:									\
+	kfree(buf);							\
+	iscsit_put_tpg(tpg);						\
+	return -EINVAL;						\
+}
+
+#define TPG_PARAM_ATTR(_name, _mode) TF_TPG_PARAM_ATTR(iscsi, _name, _mode);
+
+DEF_TPG_PARAM(AuthMethod);
+TPG_PARAM_ATTR(AuthMethod, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(HeaderDigest);
+TPG_PARAM_ATTR(HeaderDigest, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataDigest);
+TPG_PARAM_ATTR(DataDigest, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxConnections);
+TPG_PARAM_ATTR(MaxConnections, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(TargetAlias);
+TPG_PARAM_ATTR(TargetAlias, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(InitialR2T);
+TPG_PARAM_ATTR(InitialR2T, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(ImmediateData);
+TPG_PARAM_ATTR(ImmediateData, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxRecvDataSegmentLength);
+TPG_PARAM_ATTR(MaxRecvDataSegmentLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxBurstLength);
+TPG_PARAM_ATTR(MaxBurstLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(FirstBurstLength);
+TPG_PARAM_ATTR(FirstBurstLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DefaultTime2Wait);
+TPG_PARAM_ATTR(DefaultTime2Wait, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DefaultTime2Retain);
+TPG_PARAM_ATTR(DefaultTime2Retain, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxOutstandingR2T);
+TPG_PARAM_ATTR(MaxOutstandingR2T, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataPDUInOrder);
+TPG_PARAM_ATTR(DataPDUInOrder, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataSequenceInOrder);
+TPG_PARAM_ATTR(DataSequenceInOrder, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(ErrorRecoveryLevel);
+TPG_PARAM_ATTR(ErrorRecoveryLevel, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(IFMarker);
+TPG_PARAM_ATTR(IFMarker, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(OFMarker);
+TPG_PARAM_ATTR(OFMarker, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(IFMarkInt);
+TPG_PARAM_ATTR(IFMarkInt, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(OFMarkInt);
+TPG_PARAM_ATTR(OFMarkInt, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_param_attrs[] = {
+	&iscsi_tpg_param_AuthMethod.attr,
+	&iscsi_tpg_param_HeaderDigest.attr,
+	&iscsi_tpg_param_DataDigest.attr,
+	&iscsi_tpg_param_MaxConnections.attr,
+	&iscsi_tpg_param_TargetAlias.attr,
+	&iscsi_tpg_param_InitialR2T.attr,
+	&iscsi_tpg_param_ImmediateData.attr,
+	&iscsi_tpg_param_MaxRecvDataSegmentLength.attr,
+	&iscsi_tpg_param_MaxBurstLength.attr,
+	&iscsi_tpg_param_FirstBurstLength.attr,
+	&iscsi_tpg_param_DefaultTime2Wait.attr,
+	&iscsi_tpg_param_DefaultTime2Retain.attr,
+	&iscsi_tpg_param_MaxOutstandingR2T.attr,
+	&iscsi_tpg_param_DataPDUInOrder.attr,
+	&iscsi_tpg_param_DataSequenceInOrder.attr,
+	&iscsi_tpg_param_ErrorRecoveryLevel.attr,
+	&iscsi_tpg_param_IFMarker.attr,
+	&iscsi_tpg_param_OFMarker.attr,
+	&iscsi_tpg_param_IFMarkInt.attr,
+	&iscsi_tpg_param_OFMarkInt.attr,
+	NULL,
+};
+
+/* End items for lio_target_tpg_param_cit */
+
+/* Start items for lio_target_tpg_cit */
+
+static ssize_t lio_target_tpg_show_enable(
+	struct se_portal_group *se_tpg,
+	char *page)
+{
+	struct iscsi_portal_group *tpg = container_of(se_tpg,
+			struct iscsi_portal_group, tpg_se_tpg);
+	ssize_t len;
+
+	spin_lock(&tpg->tpg_state_lock);
+	len = sprintf(page, "%d\n",
+			(tpg->tpg_state == TPG_STATE_ACTIVE) ? 1 : 0);
+	spin_unlock(&tpg->tpg_state_lock);
+
+	return len;
+}
+
+static ssize_t lio_target_tpg_store_enable(
+	struct se_portal_group *se_tpg,
+	const char *page,
+	size_t count)
+{
+	struct iscsi_portal_group *tpg = container_of(se_tpg,
+			struct iscsi_portal_group, tpg_se_tpg);
+	char *endptr;
+	u32 op;
+	int ret = 0;
+
+	op = simple_strtoul(page, &endptr, 0);
+	if ((op != 1) && (op != 0)) {
+		pr_err("Illegal value for tpg_enable: %u\n", op);
+		return -EINVAL;
+	}
+
+	ret = iscsit_get_tpg(tpg);
+	if (ret < 0)
+		return -EINVAL;
+
+	if (op) {
+		ret = iscsit_tpg_enable_portal_group(tpg);
+		if (ret < 0)
+			goto out;
+	} else {
+		/*
+		 * iscsit_tpg_disable_portal_group() assumes force=1
+		 */
+		ret = iscsit_tpg_disable_portal_group(tpg, 1);
+		if (ret < 0)
+			goto out;
+	}
+
+	iscsit_put_tpg(tpg);
+	return count;
+out:
+	iscsit_put_tpg(tpg);
+	return -EINVAL;
+}
+
+TF_TPG_BASE_ATTR(lio_target, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_attrs[] = {
+	&lio_target_tpg_enable.attr,
+	NULL,
+};
+
+/* End items for lio_target_tpg_cit */
+
+/* Start items for lio_target_tiqn_cit */
+
+struct se_portal_group *lio_target_tiqn_addtpg(
+	struct se_wwn *wwn,
+	struct config_group *group,
+	const char *name)
+{
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tiqn *tiqn;
+	char *tpgt_str, *end_ptr;
+	int ret = 0;
+	unsigned short int tpgt;
+
+	tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn);
+	/*
+	 * Only tpgt_# directory groups can be created below
+	 * target/iscsi/iqn.superturodiskarry/
+	*/
+	tpgt_str = strstr(name, "tpgt_");
+	if (!tpgt_str) {
+		pr_err("Unable to locate \"tpgt_#\" directory"
+				" group\n");
+		return NULL;
+	}
+	tpgt_str += 5; /* Skip ahead of "tpgt_" */
+	tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0);
+
+	tpg = iscsit_alloc_portal_group(tiqn, tpgt);
+	if (!tpg)
+		return NULL;
+
+	ret = core_tpg_register(
+			&lio_target_fabric_configfs->tf_ops,
+			wwn, &tpg->tpg_se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0)
+		return NULL;
+
+	ret = iscsit_tpg_add_portal_group(tiqn, tpg);
+	if (ret != 0)
+		goto out;
+
+	pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn);
+	pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated TPG: %s\n",
+			name);
+	return &tpg->tpg_se_tpg;
+out:
+	core_tpg_deregister(&tpg->tpg_se_tpg);
+	kfree(tpg);
+	return NULL;
+}
+
+void lio_target_tiqn_deltpg(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tiqn *tiqn;
+
+	tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg);
+	tiqn = tpg->tpg_tiqn;
+	/*
+	 * iscsit_tpg_del_portal_group() assumes force=1
+	 */
+	pr_debug("LIO_Target_ConfigFS: DEREGISTER -> Releasing TPG\n");
+	iscsit_tpg_del_portal_group(tiqn, tpg, 1);
+}
+
+/* End items for lio_target_tiqn_cit */
+
+/* Start LIO-Target TIQN struct contig_item lio_target_cit */
+
+static ssize_t lio_target_wwn_show_attr_lio_version(
+	struct target_fabric_configfs *tf,
+	char *page)
+{
+	return sprintf(page, "RisingTide Systems Linux-iSCSI Target "ISCSIT_VERSION"\n");
+}
+
+TF_WWN_ATTR_RO(lio_target, lio_version);
+
+static struct configfs_attribute *lio_target_wwn_attrs[] = {
+	&lio_target_wwn_lio_version.attr,
+	NULL,
+};
+
+struct se_wwn *lio_target_call_coreaddtiqn(
+	struct target_fabric_configfs *tf,
+	struct config_group *group,
+	const char *name)
+{
+	struct config_group *stats_cg;
+	struct iscsi_tiqn *tiqn;
+
+	tiqn = iscsit_add_tiqn((unsigned char *)name);
+	if (IS_ERR(tiqn))
+		return ERR_PTR(PTR_ERR(tiqn));
+	/*
+	 * Setup struct iscsi_wwn_stat_grps for se_wwn->fabric_stat_group.
+	 */
+	stats_cg = &tiqn->tiqn_wwn.fabric_stat_group;
+
+	stats_cg->default_groups = kzalloc(sizeof(struct config_group) * 6,
+				GFP_KERNEL);
+	if (!stats_cg->default_groups) {
+		pr_err("Unable to allocate memory for"
+				" stats_cg->default_groups\n");
+		iscsit_del_tiqn(tiqn);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	stats_cg->default_groups[0] = &WWN_STAT_GRPS(tiqn)->iscsi_instance_group;
+	stats_cg->default_groups[1] = &WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group;
+	stats_cg->default_groups[2] = &WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group;
+	stats_cg->default_groups[3] = &WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group;
+	stats_cg->default_groups[4] = &WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group;
+	stats_cg->default_groups[5] = NULL;
+	config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_instance_group,
+			"iscsi_instance", &iscsi_stat_instance_cit);
+	config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group,
+			"iscsi_sess_err", &iscsi_stat_sess_err_cit);
+	config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group,
+			"iscsi_tgt_attr", &iscsi_stat_tgt_attr_cit);
+	config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group,
+			"iscsi_login_stats", &iscsi_stat_login_cit);
+	config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group,
+			"iscsi_logout_stats", &iscsi_stat_logout_cit);
+
+	pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn);
+	pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated Node:"
+			" %s\n", name);
+	return &tiqn->tiqn_wwn;
+}
+
+void lio_target_call_coredeltiqn(
+	struct se_wwn *wwn)
+{
+	struct iscsi_tiqn *tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn);
+	struct config_item *df_item;
+	struct config_group *stats_cg;
+	int i;
+
+	stats_cg = &tiqn->tiqn_wwn.fabric_stat_group;
+	for (i = 0; stats_cg->default_groups[i]; i++) {
+		df_item = &stats_cg->default_groups[i]->cg_item;
+		stats_cg->default_groups[i] = NULL;
+		config_item_put(df_item);
+	}
+	kfree(stats_cg->default_groups);
+
+	pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s\n",
+			tiqn->tiqn);
+	iscsit_del_tiqn(tiqn);
+}
+
+/* End LIO-Target TIQN struct contig_lio_target_cit */
+
+/* Start lio_target_discovery_auth_cit */
+
+#define DEF_DISC_AUTH_STR(name, flags)					\
+	__DEF_NACL_AUTH_STR(disc, name, flags)				\
+static ssize_t iscsi_disc_show_##name(					\
+	struct target_fabric_configfs *tf,				\
+	char *page)							\
+{									\
+	return __iscsi_disc_show_##name(&iscsit_global->discovery_acl,	\
+		page);							\
+}									\
+static ssize_t iscsi_disc_store_##name(					\
+	struct target_fabric_configfs *tf,				\
+	const char *page,						\
+	size_t count)							\
+{									\
+	return __iscsi_disc_store_##name(&iscsit_global->discovery_acl,	\
+		page, count);						\
+}
+
+#define DEF_DISC_AUTH_INT(name)						\
+	__DEF_NACL_AUTH_INT(disc, name)					\
+static ssize_t iscsi_disc_show_##name(					\
+	struct target_fabric_configfs *tf,				\
+	char *page)							\
+{									\
+	return __iscsi_disc_show_##name(&iscsit_global->discovery_acl,	\
+			page);						\
+}
+
+#define DISC_AUTH_ATTR(_name, _mode) TF_DISC_ATTR(iscsi, _name, _mode)
+#define DISC_AUTH_ATTR_RO(_name) TF_DISC_ATTR_RO(iscsi, _name)
+
+/*
+ * One-way authentication userid
+ */
+DEF_DISC_AUTH_STR(userid, NAF_USERID_SET);
+DISC_AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * One-way authentication password
+ */
+DEF_DISC_AUTH_STR(password, NAF_PASSWORD_SET);
+DISC_AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * Enforce mutual authentication
+ */
+DEF_DISC_AUTH_INT(authenticate_target);
+DISC_AUTH_ATTR_RO(authenticate_target);
+/*
+ * Mutual authentication userid
+ */
+DEF_DISC_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+DISC_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * Mutual authentication password
+ */
+DEF_DISC_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+DISC_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+/*
+ * enforce_discovery_auth
+ */
+static ssize_t iscsi_disc_show_enforce_discovery_auth(
+	struct target_fabric_configfs *tf,
+	char *page)
+{
+	struct iscsi_node_auth *discovery_auth = &iscsit_global->discovery_acl.node_auth;
+
+	return sprintf(page, "%d\n", discovery_auth->enforce_discovery_auth);
+}
+
+static ssize_t iscsi_disc_store_enforce_discovery_auth(
+	struct target_fabric_configfs *tf,
+	const char *page,
+	size_t count)
+{
+	struct iscsi_param *param;
+	struct iscsi_portal_group *discovery_tpg = iscsit_global->discovery_tpg;
+	char *endptr;
+	u32 op;
+
+	op = simple_strtoul(page, &endptr, 0);
+	if ((op != 1) && (op != 0)) {
+		pr_err("Illegal value for enforce_discovery_auth:"
+				" %u\n", op);
+		return -EINVAL;
+	}
+
+	if (!discovery_tpg) {
+		pr_err("iscsit_global->discovery_tpg is NULL\n");
+		return -EINVAL;
+	}
+
+	param = iscsi_find_param_from_key(AUTHMETHOD,
+				discovery_tpg->param_list);
+	if (!param)
+		return -EINVAL;
+
+	if (op) {
+		/*
+		 * Reset the AuthMethod key to CHAP.
+		 */
+		if (iscsi_update_param_value(param, CHAP) < 0)
+			return -EINVAL;
+
+		discovery_tpg->tpg_attrib.authentication = 1;
+		iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 1;
+		pr_debug("LIO-CORE[0] Successfully enabled"
+			" authentication enforcement for iSCSI"
+			" Discovery TPG\n");
+	} else {
+		/*
+		 * Reset the AuthMethod key to CHAP,None
+		 */
+		if (iscsi_update_param_value(param, "CHAP,None") < 0)
+			return -EINVAL;
+
+		discovery_tpg->tpg_attrib.authentication = 0;
+		iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 0;
+		pr_debug("LIO-CORE[0] Successfully disabled"
+			" authentication enforcement for iSCSI"
+			" Discovery TPG\n");
+	}
+
+	return count;
+}
+
+DISC_AUTH_ATTR(enforce_discovery_auth, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_discovery_auth_attrs[] = {
+	&iscsi_disc_userid.attr,
+	&iscsi_disc_password.attr,
+	&iscsi_disc_authenticate_target.attr,
+	&iscsi_disc_userid_mutual.attr,
+	&iscsi_disc_password_mutual.attr,
+	&iscsi_disc_enforce_discovery_auth.attr,
+	NULL,
+};
+
+/* End lio_target_discovery_auth_cit */
+
+/* Start functions for target_core_fabric_ops */
+
+static char *iscsi_get_fabric_name(void)
+{
+	return "iSCSI";
+}
+
+static u32 iscsi_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	return cmd->init_task_tag;
+}
+
+static int iscsi_get_cmd_state(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	return cmd->i_state;
+}
+
+static int iscsi_is_state_remove(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	return (cmd->i_state == ISTATE_REMOVE);
+}
+
+static int lio_sess_logged_in(struct se_session *se_sess)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+	int ret;
+	/*
+	 * Called with spin_lock_bh(&tpg_lock); and
+	 * spin_lock(&se_tpg->session_lock); held.
+	 */
+	spin_lock(&sess->conn_lock);
+	ret = (sess->session_state != TARG_SESS_STATE_LOGGED_IN);
+	spin_unlock(&sess->conn_lock);
+
+	return ret;
+}
+
+static u32 lio_sess_get_index(struct se_session *se_sess)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+	return sess->session_index;
+}
+
+static u32 lio_sess_get_initiator_sid(
+	struct se_session *se_sess,
+	unsigned char *buf,
+	u32 size)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+	/*
+	 * iSCSI Initiator Session Identifier from RFC-3720.
+	 */
+	return snprintf(buf, size, "%02x%02x%02x%02x%02x%02x",
+		sess->isid[0], sess->isid[1], sess->isid[2],
+		sess->isid[3], sess->isid[4], sess->isid[5]);
+}
+
+static int lio_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	cmd->i_state = ISTATE_SEND_DATAIN;
+	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+	return 0;
+}
+
+static int lio_write_pending(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	if (!cmd->immediate_data && !cmd->unsolicited_data)
+		return iscsit_build_r2ts_for_cmd(cmd, cmd->conn, 1);
+
+	return 0;
+}
+
+static int lio_write_pending_status(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+	int ret;
+
+	spin_lock_bh(&cmd->istate_lock);
+	ret = !(cmd->cmd_flags & ICF_GOT_LAST_DATAOUT);
+	spin_unlock_bh(&cmd->istate_lock);
+
+	return ret;
+}
+
+static int lio_queue_status(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	cmd->i_state = ISTATE_SEND_STATUS;
+	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+	return 0;
+}
+
+static u16 lio_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	unsigned char *buffer = se_cmd->sense_buffer;
+	/*
+	 * From RFC-3720 10.4.7.  Data Segment - Sense and Response Data Segment
+	 * 16-bit SenseLength.
+	 */
+	buffer[0] = ((sense_length >> 8) & 0xff);
+	buffer[1] = (sense_length & 0xff);
+	/*
+	 * Return two byte offset into allocated sense_buffer.
+	 */
+	return 2;
+}
+
+static u16 lio_get_fabric_sense_len(void)
+{
+	/*
+	 * Return two byte offset into allocated sense_buffer.
+	 */
+	return 2;
+}
+
+static int lio_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	cmd->i_state = ISTATE_SEND_TASKMGTRSP;
+	iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+	return 0;
+}
+
+static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return &tpg->tpg_tiqn->tiqn[0];
+}
+
+static u16 lio_tpg_get_tag(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return tpg->tpgt;
+}
+
+static u32 lio_tpg_get_default_depth(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth;
+}
+
+static int lio_tpg_check_demo_mode(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return ISCSI_TPG_ATTRIB(tpg)->generate_node_acls;
+}
+
+static int lio_tpg_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return ISCSI_TPG_ATTRIB(tpg)->cache_dynamic_acls;
+}
+
+static int lio_tpg_check_demo_mode_write_protect(
+	struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return ISCSI_TPG_ATTRIB(tpg)->demo_mode_write_protect;
+}
+
+static int lio_tpg_check_prod_mode_write_protect(
+	struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return ISCSI_TPG_ATTRIB(tpg)->prod_mode_write_protect;
+}
+
+static void lio_tpg_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_acl)
+{
+	struct iscsi_node_acl *acl = container_of(se_acl,
+				struct iscsi_node_acl, se_node_acl);
+	kfree(acl);
+}
+
+/*
+ * Called with spin_lock_bh(struct se_portal_group->session_lock) held..
+ *
+ * Also, this function calls iscsit_inc_session_usage_count() on the
+ * struct iscsi_session in question.
+ */
+static int lio_tpg_shutdown_session(struct se_session *se_sess)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+	spin_lock(&sess->conn_lock);
+	if (atomic_read(&sess->session_fall_back_to_erl0) ||
+	    atomic_read(&sess->session_logout) ||
+	    (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+		spin_unlock(&sess->conn_lock);
+		return 0;
+	}
+	atomic_set(&sess->session_reinstatement, 1);
+	spin_unlock(&sess->conn_lock);
+
+	iscsit_inc_session_usage_count(sess);
+	iscsit_stop_time2retain_timer(sess);
+
+	return 1;
+}
+
+/*
+ * Calls iscsit_dec_session_usage_count() as inverse of
+ * lio_tpg_shutdown_session()
+ */
+static void lio_tpg_close_session(struct se_session *se_sess)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+	/*
+	 * If the iSCSI Session for the iSCSI Initiator Node exists,
+	 * forcefully shutdown the iSCSI NEXUS.
+	 */
+	iscsit_stop_session(sess, 1, 1);
+	iscsit_dec_session_usage_count(sess);
+	iscsit_close_session(sess);
+}
+
+static void lio_tpg_stop_session(
+	struct se_session *se_sess,
+	int sess_sleep,
+	int conn_sleep)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+	iscsit_stop_session(sess, sess_sleep, conn_sleep);
+}
+
+static void lio_tpg_fall_back_to_erl0(struct se_session *se_sess)
+{
+	struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+	iscsit_fall_back_to_erl0(sess);
+}
+
+static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+	return tpg->tpg_tiqn->tiqn_index;
+}
+
+static void lio_set_default_node_attributes(struct se_node_acl *se_acl)
+{
+	struct iscsi_node_acl *acl = container_of(se_acl, struct iscsi_node_acl,
+				se_node_acl);
+
+	ISCSI_NODE_ATTRIB(acl)->nacl = acl;
+	iscsit_set_default_node_attribues(acl);
+}
+
+static void lio_release_cmd(struct se_cmd *se_cmd)
+{
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	iscsit_release_cmd(cmd);
+}
+
+/* End functions for target_core_fabric_ops */
+
+int iscsi_target_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	lio_target_fabric_configfs = NULL;
+	fabric = target_fabric_configfs_init(THIS_MODULE, "iscsi");
+	if (IS_ERR(fabric)) {
+		pr_err("target_fabric_configfs_init() for"
+				" LIO-Target failed!\n");
+		return PTR_ERR(fabric);
+	}
+	/*
+	 * Setup the fabric API of function pointers used by target_core_mod..
+	 */
+	fabric->tf_ops.get_fabric_name = &iscsi_get_fabric_name;
+	fabric->tf_ops.get_fabric_proto_ident = &iscsi_get_fabric_proto_ident;
+	fabric->tf_ops.tpg_get_wwn = &lio_tpg_get_endpoint_wwn;
+	fabric->tf_ops.tpg_get_tag = &lio_tpg_get_tag;
+	fabric->tf_ops.tpg_get_default_depth = &lio_tpg_get_default_depth;
+	fabric->tf_ops.tpg_get_pr_transport_id = &iscsi_get_pr_transport_id;
+	fabric->tf_ops.tpg_get_pr_transport_id_len =
+				&iscsi_get_pr_transport_id_len;
+	fabric->tf_ops.tpg_parse_pr_out_transport_id =
+				&iscsi_parse_pr_out_transport_id;
+	fabric->tf_ops.tpg_check_demo_mode = &lio_tpg_check_demo_mode;
+	fabric->tf_ops.tpg_check_demo_mode_cache =
+				&lio_tpg_check_demo_mode_cache;
+	fabric->tf_ops.tpg_check_demo_mode_write_protect =
+				&lio_tpg_check_demo_mode_write_protect;
+	fabric->tf_ops.tpg_check_prod_mode_write_protect =
+				&lio_tpg_check_prod_mode_write_protect;
+	fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl;
+	fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl;
+	fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index;
+	fabric->tf_ops.release_cmd = &lio_release_cmd;
+	fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;
+	fabric->tf_ops.close_session = &lio_tpg_close_session;
+	fabric->tf_ops.stop_session = &lio_tpg_stop_session;
+	fabric->tf_ops.fall_back_to_erl0 = &lio_tpg_fall_back_to_erl0;
+	fabric->tf_ops.sess_logged_in = &lio_sess_logged_in;
+	fabric->tf_ops.sess_get_index = &lio_sess_get_index;
+	fabric->tf_ops.sess_get_initiator_sid = &lio_sess_get_initiator_sid;
+	fabric->tf_ops.write_pending = &lio_write_pending;
+	fabric->tf_ops.write_pending_status = &lio_write_pending_status;
+	fabric->tf_ops.set_default_node_attributes =
+				&lio_set_default_node_attributes;
+	fabric->tf_ops.get_task_tag = &iscsi_get_task_tag;
+	fabric->tf_ops.get_cmd_state = &iscsi_get_cmd_state;
+	fabric->tf_ops.queue_data_in = &lio_queue_data_in;
+	fabric->tf_ops.queue_status = &lio_queue_status;
+	fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp;
+	fabric->tf_ops.set_fabric_sense_len = &lio_set_fabric_sense_len;
+	fabric->tf_ops.get_fabric_sense_len = &lio_get_fabric_sense_len;
+	fabric->tf_ops.is_state_remove = &iscsi_is_state_remove;
+	/*
+	 * Setup function pointers for generic logic in target_core_fabric_configfs.c
+	 */
+	fabric->tf_ops.fabric_make_wwn = &lio_target_call_coreaddtiqn;
+	fabric->tf_ops.fabric_drop_wwn = &lio_target_call_coredeltiqn;
+	fabric->tf_ops.fabric_make_tpg = &lio_target_tiqn_addtpg;
+	fabric->tf_ops.fabric_drop_tpg = &lio_target_tiqn_deltpg;
+	fabric->tf_ops.fabric_post_link	= NULL;
+	fabric->tf_ops.fabric_pre_unlink = NULL;
+	fabric->tf_ops.fabric_make_np = &lio_target_call_addnptotpg;
+	fabric->tf_ops.fabric_drop_np = &lio_target_call_delnpfromtpg;
+	fabric->tf_ops.fabric_make_nodeacl = &lio_target_make_nodeacl;
+	fabric->tf_ops.fabric_drop_nodeacl = &lio_target_drop_nodeacl;
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 * sturct config_item_type's
+	 */
+	TF_CIT_TMPL(fabric)->tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs;
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs;
+
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		pr_err("target_fabric_configfs_register() for"
+				" LIO-Target failed!\n");
+		target_fabric_configfs_free(fabric);
+		return ret;
+	}
+
+	lio_target_fabric_configfs = fabric;
+	pr_debug("LIO_TARGET[0] - Set fabric ->"
+			" lio_target_fabric_configfs\n");
+	return 0;
+}
+
+
+void iscsi_target_deregister_configfs(void)
+{
+	if (!lio_target_fabric_configfs)
+		return;
+	/*
+	 * Shutdown discovery sessions and disable discovery TPG
+	 */
+	if (iscsit_global->discovery_tpg)
+		iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
+
+	target_fabric_configfs_deregister(lio_target_fabric_configfs);
+	lio_target_fabric_configfs = NULL;
+	pr_debug("LIO_TARGET[0] - Cleared"
+				" lio_target_fabric_configfs\n");
+}
diff --git a/drivers/target/iscsi/iscsi_target_configfs.h b/drivers/target/iscsi/iscsi_target_configfs.h
new file mode 100644
index 0000000..8cd5a63
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_configfs.h
@@ -0,0 +1,7 @@
+#ifndef ISCSI_TARGET_CONFIGFS_H
+#define ISCSI_TARGET_CONFIGFS_H
+
+extern int iscsi_target_register_configfs(void);
+extern void iscsi_target_deregister_configfs(void);
+
+#endif /* ISCSI_TARGET_CONFIGFS_H */
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
new file mode 100644
index 0000000..470ed55
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -0,0 +1,859 @@
+#ifndef ISCSI_TARGET_CORE_H
+#define ISCSI_TARGET_CORE_H
+
+#include <linux/in.h>
+#include <linux/configfs.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+
+#define ISCSIT_VERSION			"v4.1.0-rc1"
+#define ISCSI_MAX_DATASN_MISSING_COUNT	16
+#define ISCSI_TX_THREAD_TCP_TIMEOUT	2
+#define ISCSI_RX_THREAD_TCP_TIMEOUT	2
+#define SECONDS_FOR_ASYNC_LOGOUT	10
+#define SECONDS_FOR_ASYNC_TEXT		10
+#define SECONDS_FOR_LOGOUT_COMP		15
+#define WHITE_SPACE			" \t\v\f\n\r"
+
+/* struct iscsi_node_attrib sanity values */
+#define NA_DATAOUT_TIMEOUT		3
+#define NA_DATAOUT_TIMEOUT_MAX		60
+#define NA_DATAOUT_TIMEOUT_MIX		2
+#define NA_DATAOUT_TIMEOUT_RETRIES	5
+#define NA_DATAOUT_TIMEOUT_RETRIES_MAX	15
+#define NA_DATAOUT_TIMEOUT_RETRIES_MIN	1
+#define NA_NOPIN_TIMEOUT		5
+#define NA_NOPIN_TIMEOUT_MAX		60
+#define NA_NOPIN_TIMEOUT_MIN		3
+#define NA_NOPIN_RESPONSE_TIMEOUT	5
+#define NA_NOPIN_RESPONSE_TIMEOUT_MAX	60
+#define NA_NOPIN_RESPONSE_TIMEOUT_MIN	3
+#define NA_RANDOM_DATAIN_PDU_OFFSETS	0
+#define NA_RANDOM_DATAIN_SEQ_OFFSETS	0
+#define NA_RANDOM_R2T_OFFSETS		0
+#define NA_DEFAULT_ERL			0
+#define NA_DEFAULT_ERL_MAX		2
+#define NA_DEFAULT_ERL_MIN		0
+
+/* struct iscsi_tpg_attrib sanity values */
+#define TA_AUTHENTICATION		1
+#define TA_LOGIN_TIMEOUT		15
+#define TA_LOGIN_TIMEOUT_MAX		30
+#define TA_LOGIN_TIMEOUT_MIN		5
+#define TA_NETIF_TIMEOUT		2
+#define TA_NETIF_TIMEOUT_MAX		15
+#define TA_NETIF_TIMEOUT_MIN		2
+#define TA_GENERATE_NODE_ACLS		0
+#define TA_DEFAULT_CMDSN_DEPTH		16
+#define TA_DEFAULT_CMDSN_DEPTH_MAX	512
+#define TA_DEFAULT_CMDSN_DEPTH_MIN	1
+#define TA_CACHE_DYNAMIC_ACLS		0
+/* Enabled by default in demo mode (generic_node_acls=1) */
+#define TA_DEMO_MODE_WRITE_PROTECT	1
+/* Disabled by default in production mode w/ explict ACLs */
+#define TA_PROD_MODE_WRITE_PROTECT	0
+#define TA_CACHE_CORE_NPS		0
+
+enum tpg_np_network_transport_table {
+	ISCSI_TCP				= 0,
+	ISCSI_SCTP_TCP				= 1,
+	ISCSI_SCTP_UDP				= 2,
+	ISCSI_IWARP_TCP				= 3,
+	ISCSI_IWARP_SCTP			= 4,
+	ISCSI_INFINIBAND			= 5,
+};
+
+/* RFC-3720 7.1.4  Standard Connection State Diagram for a Target */
+enum target_conn_state_table {
+	TARG_CONN_STATE_FREE			= 0x1,
+	TARG_CONN_STATE_XPT_UP			= 0x3,
+	TARG_CONN_STATE_IN_LOGIN		= 0x4,
+	TARG_CONN_STATE_LOGGED_IN		= 0x5,
+	TARG_CONN_STATE_IN_LOGOUT		= 0x6,
+	TARG_CONN_STATE_LOGOUT_REQUESTED	= 0x7,
+	TARG_CONN_STATE_CLEANUP_WAIT		= 0x8,
+};
+
+/* RFC-3720 7.3.2  Session State Diagram for a Target */
+enum target_sess_state_table {
+	TARG_SESS_STATE_FREE			= 0x1,
+	TARG_SESS_STATE_ACTIVE			= 0x2,
+	TARG_SESS_STATE_LOGGED_IN		= 0x3,
+	TARG_SESS_STATE_FAILED			= 0x4,
+	TARG_SESS_STATE_IN_CONTINUE		= 0x5,
+};
+
+/* struct iscsi_data_count->type */
+enum data_count_type {
+	ISCSI_RX_DATA	= 1,
+	ISCSI_TX_DATA	= 2,
+};
+
+/* struct iscsi_datain_req->dr_complete */
+enum datain_req_comp_table {
+	DATAIN_COMPLETE_NORMAL			= 1,
+	DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY = 2,
+	DATAIN_COMPLETE_CONNECTION_RECOVERY	= 3,
+};
+
+/* struct iscsi_datain_req->recovery */
+enum datain_req_rec_table {
+	DATAIN_WITHIN_COMMAND_RECOVERY		= 1,
+	DATAIN_CONNECTION_RECOVERY		= 2,
+};
+
+/* struct iscsi_portal_group->state */
+enum tpg_state_table {
+	TPG_STATE_FREE				= 0,
+	TPG_STATE_ACTIVE			= 1,
+	TPG_STATE_INACTIVE			= 2,
+	TPG_STATE_COLD_RESET			= 3,
+};
+
+/* struct iscsi_tiqn->tiqn_state */
+enum tiqn_state_table {
+	TIQN_STATE_ACTIVE			= 1,
+	TIQN_STATE_SHUTDOWN			= 2,
+};
+
+/* struct iscsi_cmd->cmd_flags */
+enum cmd_flags_table {
+	ICF_GOT_LAST_DATAOUT			= 0x00000001,
+	ICF_GOT_DATACK_SNACK			= 0x00000002,
+	ICF_NON_IMMEDIATE_UNSOLICITED_DATA	= 0x00000004,
+	ICF_SENT_LAST_R2T			= 0x00000008,
+	ICF_WITHIN_COMMAND_RECOVERY		= 0x00000010,
+	ICF_CONTIG_MEMORY			= 0x00000020,
+	ICF_ATTACHED_TO_RQUEUE			= 0x00000040,
+	ICF_OOO_CMDSN				= 0x00000080,
+	ICF_REJECT_FAIL_CONN			= 0x00000100,
+};
+
+/* struct iscsi_cmd->i_state */
+enum cmd_i_state_table {
+	ISTATE_NO_STATE			= 0,
+	ISTATE_NEW_CMD			= 1,
+	ISTATE_DEFERRED_CMD		= 2,
+	ISTATE_UNSOLICITED_DATA		= 3,
+	ISTATE_RECEIVE_DATAOUT		= 4,
+	ISTATE_RECEIVE_DATAOUT_RECOVERY	= 5,
+	ISTATE_RECEIVED_LAST_DATAOUT	= 6,
+	ISTATE_WITHIN_DATAOUT_RECOVERY	= 7,
+	ISTATE_IN_CONNECTION_RECOVERY	= 8,
+	ISTATE_RECEIVED_TASKMGT		= 9,
+	ISTATE_SEND_ASYNCMSG		= 10,
+	ISTATE_SENT_ASYNCMSG		= 11,
+	ISTATE_SEND_DATAIN		= 12,
+	ISTATE_SEND_LAST_DATAIN		= 13,
+	ISTATE_SENT_LAST_DATAIN		= 14,
+	ISTATE_SEND_LOGOUTRSP		= 15,
+	ISTATE_SENT_LOGOUTRSP		= 16,
+	ISTATE_SEND_NOPIN		= 17,
+	ISTATE_SENT_NOPIN		= 18,
+	ISTATE_SEND_REJECT		= 19,
+	ISTATE_SENT_REJECT		= 20,
+	ISTATE_SEND_R2T			= 21,
+	ISTATE_SENT_R2T			= 22,
+	ISTATE_SEND_R2T_RECOVERY	= 23,
+	ISTATE_SENT_R2T_RECOVERY	= 24,
+	ISTATE_SEND_LAST_R2T		= 25,
+	ISTATE_SENT_LAST_R2T		= 26,
+	ISTATE_SEND_LAST_R2T_RECOVERY	= 27,
+	ISTATE_SENT_LAST_R2T_RECOVERY	= 28,
+	ISTATE_SEND_STATUS		= 29,
+	ISTATE_SEND_STATUS_BROKEN_PC	= 30,
+	ISTATE_SENT_STATUS		= 31,
+	ISTATE_SEND_STATUS_RECOVERY	= 32,
+	ISTATE_SENT_STATUS_RECOVERY	= 33,
+	ISTATE_SEND_TASKMGTRSP		= 34,
+	ISTATE_SENT_TASKMGTRSP		= 35,
+	ISTATE_SEND_TEXTRSP		= 36,
+	ISTATE_SENT_TEXTRSP		= 37,
+	ISTATE_SEND_NOPIN_WANT_RESPONSE	= 38,
+	ISTATE_SENT_NOPIN_WANT_RESPONSE	= 39,
+	ISTATE_SEND_NOPIN_NO_RESPONSE	= 40,
+	ISTATE_REMOVE			= 41,
+	ISTATE_FREE			= 42,
+};
+
+/* Used for iscsi_recover_cmdsn() return values */
+enum recover_cmdsn_ret_table {
+	CMDSN_ERROR_CANNOT_RECOVER	= -1,
+	CMDSN_NORMAL_OPERATION		= 0,
+	CMDSN_LOWER_THAN_EXP		= 1,
+	CMDSN_HIGHER_THAN_EXP		= 2,
+};
+
+/* Used for iscsi_handle_immediate_data() return values */
+enum immedate_data_ret_table {
+	IMMEDIATE_DATA_CANNOT_RECOVER	= -1,
+	IMMEDIATE_DATA_NORMAL_OPERATION = 0,
+	IMMEDIATE_DATA_ERL1_CRC_FAILURE = 1,
+};
+
+/* Used for iscsi_decide_dataout_action() return values */
+enum dataout_action_ret_table {
+	DATAOUT_CANNOT_RECOVER		= -1,
+	DATAOUT_NORMAL			= 0,
+	DATAOUT_SEND_R2T		= 1,
+	DATAOUT_SEND_TO_TRANSPORT	= 2,
+	DATAOUT_WITHIN_COMMAND_RECOVERY = 3,
+};
+
+/* Used for struct iscsi_node_auth->naf_flags */
+enum naf_flags_table {
+	NAF_USERID_SET			= 0x01,
+	NAF_PASSWORD_SET		= 0x02,
+	NAF_USERID_IN_SET		= 0x04,
+	NAF_PASSWORD_IN_SET		= 0x08,
+};
+
+/* Used by various struct timer_list to manage iSCSI specific state */
+enum iscsi_timer_flags_table {
+	ISCSI_TF_RUNNING		= 0x01,
+	ISCSI_TF_STOP			= 0x02,
+	ISCSI_TF_EXPIRED		= 0x04,
+};
+
+/* Used for struct iscsi_np->np_flags */
+enum np_flags_table {
+	NPF_IP_NETWORK		= 0x00,
+	NPF_SCTP_STRUCT_FILE	= 0x01 /* Bugfix */
+};
+
+/* Used for struct iscsi_np->np_thread_state */
+enum np_thread_state_table {
+	ISCSI_NP_THREAD_ACTIVE		= 1,
+	ISCSI_NP_THREAD_INACTIVE	= 2,
+	ISCSI_NP_THREAD_RESET		= 3,
+	ISCSI_NP_THREAD_SHUTDOWN	= 4,
+	ISCSI_NP_THREAD_EXIT		= 5,
+};
+
+struct iscsi_conn_ops {
+	u8	HeaderDigest;			/* [0,1] == [None,CRC32C] */
+	u8	DataDigest;			/* [0,1] == [None,CRC32C] */
+	u32	MaxRecvDataSegmentLength;	/* [512..2**24-1] */
+	u8	OFMarker;			/* [0,1] == [No,Yes] */
+	u8	IFMarker;			/* [0,1] == [No,Yes] */
+	u32	OFMarkInt;			/* [1..65535] */
+	u32	IFMarkInt;			/* [1..65535] */
+};
+
+struct iscsi_sess_ops {
+	char	InitiatorName[224];
+	char	InitiatorAlias[256];
+	char	TargetName[224];
+	char	TargetAlias[256];
+	char	TargetAddress[256];
+	u16	TargetPortalGroupTag;		/* [0..65535] */
+	u16	MaxConnections;			/* [1..65535] */
+	u8	InitialR2T;			/* [0,1] == [No,Yes] */
+	u8	ImmediateData;			/* [0,1] == [No,Yes] */
+	u32	MaxBurstLength;			/* [512..2**24-1] */
+	u32	FirstBurstLength;		/* [512..2**24-1] */
+	u16	DefaultTime2Wait;		/* [0..3600] */
+	u16	DefaultTime2Retain;		/* [0..3600] */
+	u16	MaxOutstandingR2T;		/* [1..65535] */
+	u8	DataPDUInOrder;			/* [0,1] == [No,Yes] */
+	u8	DataSequenceInOrder;		/* [0,1] == [No,Yes] */
+	u8	ErrorRecoveryLevel;		/* [0..2] */
+	u8	SessionType;			/* [0,1] == [Normal,Discovery]*/
+};
+
+struct iscsi_queue_req {
+	int			state;
+	struct iscsi_cmd	*cmd;
+	struct list_head	qr_list;
+};
+
+struct iscsi_data_count {
+	int			data_length;
+	int			sync_and_steering;
+	enum data_count_type	type;
+	u32			iov_count;
+	u32			ss_iov_count;
+	u32			ss_marker_count;
+	struct kvec		*iov;
+};
+
+struct iscsi_param_list {
+	struct list_head	param_list;
+	struct list_head	extra_response_list;
+};
+
+struct iscsi_datain_req {
+	enum datain_req_comp_table dr_complete;
+	int			generate_recovery_values;
+	enum datain_req_rec_table recovery;
+	u32			begrun;
+	u32			runlength;
+	u32			data_length;
+	u32			data_offset;
+	u32			data_offset_end;
+	u32			data_sn;
+	u32			next_burst_len;
+	u32			read_data_done;
+	u32			seq_send_order;
+	struct list_head	dr_list;
+} ____cacheline_aligned;
+
+struct iscsi_ooo_cmdsn {
+	u16			cid;
+	u32			batch_count;
+	u32			cmdsn;
+	u32			exp_cmdsn;
+	struct iscsi_cmd	*cmd;
+	struct list_head	ooo_list;
+} ____cacheline_aligned;
+
+struct iscsi_datain {
+	u8			flags;
+	u32			data_sn;
+	u32			length;
+	u32			offset;
+} ____cacheline_aligned;
+
+struct iscsi_r2t {
+	int			seq_complete;
+	int			recovery_r2t;
+	int			sent_r2t;
+	u32			r2t_sn;
+	u32			offset;
+	u32			targ_xfer_tag;
+	u32			xfer_len;
+	struct list_head	r2t_list;
+} ____cacheline_aligned;
+
+struct iscsi_cmd {
+	enum iscsi_timer_flags_table dataout_timer_flags;
+	/* DataOUT timeout retries */
+	u8			dataout_timeout_retries;
+	/* Within command recovery count */
+	u8			error_recovery_count;
+	/* iSCSI dependent state for out or order CmdSNs */
+	enum cmd_i_state_table	deferred_i_state;
+	/* iSCSI dependent state */
+	enum cmd_i_state_table	i_state;
+	/* Command is an immediate command (ISCSI_OP_IMMEDIATE set) */
+	u8			immediate_cmd;
+	/* Immediate data present */
+	u8			immediate_data;
+	/* iSCSI Opcode */
+	u8			iscsi_opcode;
+	/* iSCSI Response Code */
+	u8			iscsi_response;
+	/* Logout reason when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */
+	u8			logout_reason;
+	/* Logout response code when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */
+	u8			logout_response;
+	/* MaxCmdSN has been incremented */
+	u8			maxcmdsn_inc;
+	/* Immediate Unsolicited Dataout */
+	u8			unsolicited_data;
+	/* CID contained in logout PDU when opcode == ISCSI_INIT_LOGOUT_CMND */
+	u16			logout_cid;
+	/* Command flags */
+	enum cmd_flags_table	cmd_flags;
+	/* Initiator Task Tag assigned from Initiator */
+	u32			init_task_tag;
+	/* Target Transfer Tag assigned from Target */
+	u32			targ_xfer_tag;
+	/* CmdSN assigned from Initiator */
+	u32			cmd_sn;
+	/* ExpStatSN assigned from Initiator */
+	u32			exp_stat_sn;
+	/* StatSN assigned to this ITT */
+	u32			stat_sn;
+	/* DataSN Counter */
+	u32			data_sn;
+	/* R2TSN Counter */
+	u32			r2t_sn;
+	/* Last DataSN acknowledged via DataAck SNACK */
+	u32			acked_data_sn;
+	/* Used for echoing NOPOUT ping data */
+	u32			buf_ptr_size;
+	/* Used to store DataDigest */
+	u32			data_crc;
+	/* Total size in bytes associated with command */
+	u32			data_length;
+	/* Counter for MaxOutstandingR2T */
+	u32			outstanding_r2ts;
+	/* Next R2T Offset when DataSequenceInOrder=Yes */
+	u32			r2t_offset;
+	/* Iovec current and orig count for iscsi_cmd->iov_data */
+	u32			iov_data_count;
+	u32			orig_iov_data_count;
+	/* Number of miscellaneous iovecs used for IP stack calls */
+	u32			iov_misc_count;
+	/* Number of struct iscsi_pdu in struct iscsi_cmd->pdu_list */
+	u32			pdu_count;
+	/* Next struct iscsi_pdu to send in struct iscsi_cmd->pdu_list */
+	u32			pdu_send_order;
+	/* Current struct iscsi_pdu in struct iscsi_cmd->pdu_list */
+	u32			pdu_start;
+	u32			residual_count;
+	/* Next struct iscsi_seq to send in struct iscsi_cmd->seq_list */
+	u32			seq_send_order;
+	/* Number of struct iscsi_seq in struct iscsi_cmd->seq_list */
+	u32			seq_count;
+	/* Current struct iscsi_seq in struct iscsi_cmd->seq_list */
+	u32			seq_no;
+	/* Lowest offset in current DataOUT sequence */
+	u32			seq_start_offset;
+	/* Highest offset in current DataOUT sequence */
+	u32			seq_end_offset;
+	/* Total size in bytes received so far of READ data */
+	u32			read_data_done;
+	/* Total size in bytes received so far of WRITE data */
+	u32			write_data_done;
+	/* Counter for FirstBurstLength key */
+	u32			first_burst_len;
+	/* Counter for MaxBurstLength key */
+	u32			next_burst_len;
+	/* Transfer size used for IP stack calls */
+	u32			tx_size;
+	/* Buffer used for various purposes */
+	void			*buf_ptr;
+	/* See include/linux/dma-mapping.h */
+	enum dma_data_direction	data_direction;
+	/* iSCSI PDU Header + CRC */
+	unsigned char		pdu[ISCSI_HDR_LEN + ISCSI_CRC_LEN];
+	/* Number of times struct iscsi_cmd is present in immediate queue */
+	atomic_t		immed_queue_count;
+	atomic_t		response_queue_count;
+	atomic_t		transport_sent;
+	spinlock_t		datain_lock;
+	spinlock_t		dataout_timeout_lock;
+	/* spinlock for protecting struct iscsi_cmd->i_state */
+	spinlock_t		istate_lock;
+	/* spinlock for adding within command recovery entries */
+	spinlock_t		error_lock;
+	/* spinlock for adding R2Ts */
+	spinlock_t		r2t_lock;
+	/* DataIN List */
+	struct list_head	datain_list;
+	/* R2T List */
+	struct list_head	cmd_r2t_list;
+	struct completion	reject_comp;
+	/* Timer for DataOUT */
+	struct timer_list	dataout_timer;
+	/* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */
+	struct kvec		*iov_data;
+	/* Iovecs for miscellaneous purposes */
+#define ISCSI_MISC_IOVECS			5
+	struct kvec		iov_misc[ISCSI_MISC_IOVECS];
+	/* Array of struct iscsi_pdu used for DataPDUInOrder=No */
+	struct iscsi_pdu	*pdu_list;
+	/* Current struct iscsi_pdu used for DataPDUInOrder=No */
+	struct iscsi_pdu	*pdu_ptr;
+	/* Array of struct iscsi_seq used for DataSequenceInOrder=No */
+	struct iscsi_seq	*seq_list;
+	/* Current struct iscsi_seq used for DataSequenceInOrder=No */
+	struct iscsi_seq	*seq_ptr;
+	/* TMR Request when iscsi_opcode == ISCSI_OP_SCSI_TMFUNC */
+	struct iscsi_tmr_req	*tmr_req;
+	/* Connection this command is alligient to */
+	struct iscsi_conn	*conn;
+	/* Pointer to connection recovery entry */
+	struct iscsi_conn_recovery *cr;
+	/* Session the command is part of,  used for connection recovery */
+	struct iscsi_session	*sess;
+	/* list_head for connection list */
+	struct list_head	i_list;
+	/* The TCM I/O descriptor that is accessed via container_of() */
+	struct se_cmd		se_cmd;
+	/* Sense buffer that will be mapped into outgoing status */
+#define ISCSI_SENSE_BUFFER_LEN          (TRANSPORT_SENSE_BUFFER + 2)
+	unsigned char		sense_buffer[ISCSI_SENSE_BUFFER_LEN];
+
+	struct scatterlist	*t_mem_sg;
+	u32			t_mem_sg_nents;
+
+	u32			padding;
+	u8			pad_bytes[4];
+
+	struct scatterlist	*first_data_sg;
+	u32			first_data_sg_off;
+	u32			kmapped_nents;
+
+}  ____cacheline_aligned;
+
+struct iscsi_tmr_req {
+	bool			task_reassign:1;
+	u32			ref_cmd_sn;
+	u32			exp_data_sn;
+	struct iscsi_conn_recovery *conn_recovery;
+	struct se_tmr_req	*se_tmr_req;
+};
+
+struct iscsi_conn {
+	/* Authentication Successful for this connection */
+	u8			auth_complete;
+	/* State connection is currently in */
+	u8			conn_state;
+	u8			conn_logout_reason;
+	u8			network_transport;
+	enum iscsi_timer_flags_table nopin_timer_flags;
+	enum iscsi_timer_flags_table nopin_response_timer_flags;
+	u8			tx_immediate_queue;
+	u8			tx_response_queue;
+	/* Used to know what thread encountered a transport failure */
+	u8			which_thread;
+	/* connection id assigned by the Initiator */
+	u16			cid;
+	/* Remote TCP Port */
+	u16			login_port;
+	int			net_size;
+	u32			auth_id;
+#define CONNFLAG_SCTP_STRUCT_FILE			0x01
+	u32			conn_flags;
+	/* Used for iscsi_tx_login_rsp() */
+	u32			login_itt;
+	u32			exp_statsn;
+	/* Per connection status sequence number */
+	u32			stat_sn;
+	/* IFMarkInt's Current Value */
+	u32			if_marker;
+	/* OFMarkInt's Current Value */
+	u32			of_marker;
+	/* Used for calculating OFMarker offset to next PDU */
+	u32			of_marker_offset;
+	/* Complete Bad PDU for sending reject */
+	unsigned char		bad_hdr[ISCSI_HDR_LEN];
+#define IPV6_ADDRESS_SPACE				48
+	unsigned char		login_ip[IPV6_ADDRESS_SPACE];
+	int			conn_usage_count;
+	int			conn_waiting_on_uc;
+	atomic_t		check_immediate_queue;
+	atomic_t		conn_logout_remove;
+	atomic_t		connection_exit;
+	atomic_t		connection_recovery;
+	atomic_t		connection_reinstatement;
+	atomic_t		connection_wait;
+	atomic_t		connection_wait_rcfr;
+	atomic_t		sleep_on_conn_wait_comp;
+	atomic_t		transport_failed;
+	struct completion	conn_post_wait_comp;
+	struct completion	conn_wait_comp;
+	struct completion	conn_wait_rcfr_comp;
+	struct completion	conn_waiting_on_uc_comp;
+	struct completion	conn_logout_comp;
+	struct completion	tx_half_close_comp;
+	struct completion	rx_half_close_comp;
+	/* socket used by this connection */
+	struct socket		*sock;
+	struct timer_list	nopin_timer;
+	struct timer_list	nopin_response_timer;
+	struct timer_list	transport_timer;
+	/* Spinlock used for add/deleting cmd's from conn_cmd_list */
+	spinlock_t		cmd_lock;
+	spinlock_t		conn_usage_lock;
+	spinlock_t		immed_queue_lock;
+	spinlock_t		nopin_timer_lock;
+	spinlock_t		response_queue_lock;
+	spinlock_t		state_lock;
+	/* libcrypto RX and TX contexts for crc32c */
+	struct hash_desc	conn_rx_hash;
+	struct hash_desc	conn_tx_hash;
+	/* Used for scheduling TX and RX connection kthreads */
+	cpumask_var_t		conn_cpumask;
+	int			conn_rx_reset_cpumask:1;
+	int			conn_tx_reset_cpumask:1;
+	/* list_head of struct iscsi_cmd for this connection */
+	struct list_head	conn_cmd_list;
+	struct list_head	immed_queue_list;
+	struct list_head	response_queue_list;
+	struct iscsi_conn_ops	*conn_ops;
+	struct iscsi_param_list	*param_list;
+	/* Used for per connection auth state machine */
+	void			*auth_protocol;
+	struct iscsi_login_thread_s *login_thread;
+	struct iscsi_portal_group *tpg;
+	/* Pointer to parent session */
+	struct iscsi_session	*sess;
+	/* Pointer to thread_set in use for this conn's threads */
+	struct iscsi_thread_set	*thread_set;
+	/* list_head for session connection list */
+	struct list_head	conn_list;
+} ____cacheline_aligned;
+
+struct iscsi_conn_recovery {
+	u16			cid;
+	u32			cmd_count;
+	u32			maxrecvdatasegmentlength;
+	int			ready_for_reallegiance;
+	struct list_head	conn_recovery_cmd_list;
+	spinlock_t		conn_recovery_cmd_lock;
+	struct timer_list	time2retain_timer;
+	struct iscsi_session	*sess;
+	struct list_head	cr_list;
+}  ____cacheline_aligned;
+
+struct iscsi_session {
+	u8			initiator_vendor;
+	u8			isid[6];
+	enum iscsi_timer_flags_table time2retain_timer_flags;
+	u8			version_active;
+	u16			cid_called;
+	u16			conn_recovery_count;
+	u16			tsih;
+	/* state session is currently in */
+	u32			session_state;
+	/* session wide counter: initiator assigned task tag */
+	u32			init_task_tag;
+	/* session wide counter: target assigned task tag */
+	u32			targ_xfer_tag;
+	u32			cmdsn_window;
+
+	/* protects cmdsn values */
+	struct mutex		cmdsn_mutex;
+	/* session wide counter: expected command sequence number */
+	u32			exp_cmd_sn;
+	/* session wide counter: maximum allowed command sequence number */
+	u32			max_cmd_sn;
+	struct list_head	sess_ooo_cmdsn_list;
+
+	/* LIO specific session ID */
+	u32			sid;
+	char			auth_type[8];
+	/* unique within the target */
+	int			session_index;
+	/* Used for session reference counting */
+	int			session_usage_count;
+	int			session_waiting_on_uc;
+	u32			cmd_pdus;
+	u32			rsp_pdus;
+	u64			tx_data_octets;
+	u64			rx_data_octets;
+	u32			conn_digest_errors;
+	u32			conn_timeout_errors;
+	u64			creation_time;
+	spinlock_t		session_stats_lock;
+	/* Number of active connections */
+	atomic_t		nconn;
+	atomic_t		session_continuation;
+	atomic_t		session_fall_back_to_erl0;
+	atomic_t		session_logout;
+	atomic_t		session_reinstatement;
+	atomic_t		session_stop_active;
+	atomic_t		sleep_on_sess_wait_comp;
+	atomic_t		transport_wait_cmds;
+	/* connection list */
+	struct list_head	sess_conn_list;
+	struct list_head	cr_active_list;
+	struct list_head	cr_inactive_list;
+	spinlock_t		conn_lock;
+	spinlock_t		cr_a_lock;
+	spinlock_t		cr_i_lock;
+	spinlock_t		session_usage_lock;
+	spinlock_t		ttt_lock;
+	struct completion	async_msg_comp;
+	struct completion	reinstatement_comp;
+	struct completion	session_wait_comp;
+	struct completion	session_waiting_on_uc_comp;
+	struct timer_list	time2retain_timer;
+	struct iscsi_sess_ops	*sess_ops;
+	struct se_session	*se_sess;
+	struct iscsi_portal_group *tpg;
+} ____cacheline_aligned;
+
+struct iscsi_login {
+	u8 auth_complete;
+	u8 checked_for_existing;
+	u8 current_stage;
+	u8 leading_connection;
+	u8 first_request;
+	u8 version_min;
+	u8 version_max;
+	char isid[6];
+	u32 cmd_sn;
+	u32 init_task_tag;
+	u32 initial_exp_statsn;
+	u32 rsp_length;
+	u16 cid;
+	u16 tsih;
+	char *req;
+	char *rsp;
+	char *req_buf;
+	char *rsp_buf;
+} ____cacheline_aligned;
+
+struct iscsi_node_attrib {
+	u32			dataout_timeout;
+	u32			dataout_timeout_retries;
+	u32			default_erl;
+	u32			nopin_timeout;
+	u32			nopin_response_timeout;
+	u32			random_datain_pdu_offsets;
+	u32			random_datain_seq_offsets;
+	u32			random_r2t_offsets;
+	u32			tmr_cold_reset;
+	u32			tmr_warm_reset;
+	struct iscsi_node_acl *nacl;
+};
+
+struct se_dev_entry_s;
+
+struct iscsi_node_auth {
+	enum naf_flags_table	naf_flags;
+	int			authenticate_target;
+	/* Used for iscsit_global->discovery_auth,
+	 * set to zero (auth disabled) by default */
+	int			enforce_discovery_auth;
+#define MAX_USER_LEN				256
+#define MAX_PASS_LEN				256
+	char			userid[MAX_USER_LEN];
+	char			password[MAX_PASS_LEN];
+	char			userid_mutual[MAX_USER_LEN];
+	char			password_mutual[MAX_PASS_LEN];
+};
+
+#include "iscsi_target_stat.h"
+
+struct iscsi_node_stat_grps {
+	struct config_group	iscsi_sess_stats_group;
+	struct config_group	iscsi_conn_stats_group;
+};
+
+struct iscsi_node_acl {
+	struct iscsi_node_attrib node_attrib;
+	struct iscsi_node_auth	node_auth;
+	struct iscsi_node_stat_grps node_stat_grps;
+	struct se_node_acl	se_node_acl;
+};
+
+#define NODE_STAT_GRPS(nacl)	(&(nacl)->node_stat_grps)
+
+#define ISCSI_NODE_ATTRIB(t)	(&(t)->node_attrib)
+#define ISCSI_NODE_AUTH(t)	(&(t)->node_auth)
+
+struct iscsi_tpg_attrib {
+	u32			authentication;
+	u32			login_timeout;
+	u32			netif_timeout;
+	u32			generate_node_acls;
+	u32			cache_dynamic_acls;
+	u32			default_cmdsn_depth;
+	u32			demo_mode_write_protect;
+	u32			prod_mode_write_protect;
+	struct iscsi_portal_group *tpg;
+};
+
+struct iscsi_np {
+	int			np_network_transport;
+	int			np_ip_proto;
+	int			np_sock_type;
+	enum np_thread_state_table np_thread_state;
+	enum iscsi_timer_flags_table np_login_timer_flags;
+	u32			np_exports;
+	enum np_flags_table	np_flags;
+	unsigned char		np_ip[IPV6_ADDRESS_SPACE];
+	u16			np_port;
+	spinlock_t		np_thread_lock;
+	struct completion	np_restart_comp;
+	struct socket		*np_socket;
+	struct __kernel_sockaddr_storage np_sockaddr;
+	struct task_struct	*np_thread;
+	struct timer_list	np_login_timer;
+	struct iscsi_portal_group *np_login_tpg;
+	struct list_head	np_list;
+} ____cacheline_aligned;
+
+struct iscsi_tpg_np {
+	struct iscsi_np		*tpg_np;
+	struct iscsi_portal_group *tpg;
+	struct iscsi_tpg_np	*tpg_np_parent;
+	struct list_head	tpg_np_list;
+	struct list_head	tpg_np_child_list;
+	struct list_head	tpg_np_parent_list;
+	struct se_tpg_np	se_tpg_np;
+	spinlock_t		tpg_np_parent_lock;
+};
+
+struct iscsi_portal_group {
+	unsigned char		tpg_chap_id;
+	/* TPG State */
+	enum tpg_state_table	tpg_state;
+	/* Target Portal Group Tag */
+	u16			tpgt;
+	/* Id assigned to target sessions */
+	u16			ntsih;
+	/* Number of active sessions */
+	u32			nsessions;
+	/* Number of Network Portals available for this TPG */
+	u32			num_tpg_nps;
+	/* Per TPG LIO specific session ID. */
+	u32			sid;
+	/* Spinlock for adding/removing Network Portals */
+	spinlock_t		tpg_np_lock;
+	spinlock_t		tpg_state_lock;
+	struct se_portal_group tpg_se_tpg;
+	struct mutex		tpg_access_lock;
+	struct mutex		np_login_lock;
+	struct iscsi_tpg_attrib	tpg_attrib;
+	/* Pointer to default list of iSCSI parameters for TPG */
+	struct iscsi_param_list	*param_list;
+	struct iscsi_tiqn	*tpg_tiqn;
+	struct list_head	tpg_gnp_list;
+	struct list_head	tpg_list;
+} ____cacheline_aligned;
+
+#define ISCSI_TPG_C(c)		((struct iscsi_portal_group *)(c)->tpg)
+#define ISCSI_TPG_LUN(c, l)  ((iscsi_tpg_list_t *)(c)->tpg->tpg_lun_list_t[l])
+#define ISCSI_TPG_S(s)		((struct iscsi_portal_group *)(s)->tpg)
+#define ISCSI_TPG_ATTRIB(t)	(&(t)->tpg_attrib)
+#define SE_TPG(tpg)		(&(tpg)->tpg_se_tpg)
+
+struct iscsi_wwn_stat_grps {
+	struct config_group	iscsi_stat_group;
+	struct config_group	iscsi_instance_group;
+	struct config_group	iscsi_sess_err_group;
+	struct config_group	iscsi_tgt_attr_group;
+	struct config_group	iscsi_login_stats_group;
+	struct config_group	iscsi_logout_stats_group;
+};
+
+struct iscsi_tiqn {
+#define ISCSI_IQN_LEN				224
+	unsigned char		tiqn[ISCSI_IQN_LEN];
+	enum tiqn_state_table	tiqn_state;
+	int			tiqn_access_count;
+	u32			tiqn_active_tpgs;
+	u32			tiqn_ntpgs;
+	u32			tiqn_num_tpg_nps;
+	u32			tiqn_nsessions;
+	struct list_head	tiqn_list;
+	struct list_head	tiqn_tpg_list;
+	spinlock_t		tiqn_state_lock;
+	spinlock_t		tiqn_tpg_lock;
+	struct se_wwn		tiqn_wwn;
+	struct iscsi_wwn_stat_grps tiqn_stat_grps;
+	int			tiqn_index;
+	struct iscsi_sess_err_stats  sess_err_stats;
+	struct iscsi_login_stats     login_stats;
+	struct iscsi_logout_stats    logout_stats;
+} ____cacheline_aligned;
+
+#define WWN_STAT_GRPS(tiqn)	(&(tiqn)->tiqn_stat_grps)
+
+struct iscsit_global {
+	/* In core shutdown */
+	u32			in_shutdown;
+	u32			active_ts;
+	/* Unique identifier used for the authentication daemon */
+	u32			auth_id;
+	u32			inactive_ts;
+	/* Thread Set bitmap count */
+	int			ts_bitmap_count;
+	/* Thread Set bitmap pointer */
+	unsigned long		*ts_bitmap;
+	/* Used for iSCSI discovery session authentication */
+	struct iscsi_node_acl	discovery_acl;
+	struct iscsi_portal_group	*discovery_tpg;
+};
+
+#endif /* ISCSI_TARGET_CORE_H */
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.c b/drivers/target/iscsi/iscsi_target_datain_values.c
new file mode 100644
index 0000000..8c04951
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_datain_values.c
@@ -0,0 +1,531 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target DataIN value generation functions.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_datain_values.h"
+
+struct iscsi_datain_req *iscsit_allocate_datain_req(void)
+{
+	struct iscsi_datain_req *dr;
+
+	dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);
+	if (!dr) {
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_datain_req\n");
+		return NULL;
+	}
+	INIT_LIST_HEAD(&dr->dr_list);
+
+	return dr;
+}
+
+void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
+{
+	spin_lock(&cmd->datain_lock);
+	list_add_tail(&dr->dr_list, &cmd->datain_list);
+	spin_unlock(&cmd->datain_lock);
+}
+
+void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
+{
+	spin_lock(&cmd->datain_lock);
+	list_del(&dr->dr_list);
+	spin_unlock(&cmd->datain_lock);
+
+	kmem_cache_free(lio_dr_cache, dr);
+}
+
+void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd)
+{
+	struct iscsi_datain_req *dr, *dr_tmp;
+
+	spin_lock(&cmd->datain_lock);
+	list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, dr_list) {
+		list_del(&dr->dr_list);
+		kmem_cache_free(lio_dr_cache, dr);
+	}
+	spin_unlock(&cmd->datain_lock);
+}
+
+struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
+{
+	struct iscsi_datain_req *dr;
+
+	if (list_empty(&cmd->datain_list)) {
+		pr_err("cmd->datain_list is empty for ITT:"
+			" 0x%08x\n", cmd->init_task_tag);
+		return NULL;
+	}
+	list_for_each_entry(dr, &cmd->datain_list, dr_list)
+		break;
+
+	return dr;
+}
+
+/*
+ *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain *datain)
+{
+	u32 next_burst_len, read_data_done, read_data_left;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+
+	dr = iscsit_get_datain_req(cmd);
+	if (!dr)
+		return NULL;
+
+	if (dr->recovery && dr->generate_recovery_values) {
+		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+					cmd, dr) < 0)
+			return NULL;
+
+		dr->generate_recovery_values = 0;
+	}
+
+	next_burst_len = (!dr->recovery) ?
+			cmd->next_burst_len : dr->next_burst_len;
+	read_data_done = (!dr->recovery) ?
+			cmd->read_data_done : dr->read_data_done;
+
+	read_data_left = (cmd->data_length - read_data_done);
+	if (!read_data_left) {
+		pr_err("ITT: 0x%08x read_data_left is zero!\n",
+				cmd->init_task_tag);
+		return NULL;
+	}
+
+	if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
+	    (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
+	     next_burst_len))) {
+		datain->length = read_data_left;
+
+		datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
+		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+			datain->flags |= ISCSI_FLAG_DATA_ACK;
+	} else {
+		if ((next_burst_len +
+		     conn->conn_ops->MaxRecvDataSegmentLength) <
+		     conn->sess->sess_ops->MaxBurstLength) {
+			datain->length =
+				conn->conn_ops->MaxRecvDataSegmentLength;
+			next_burst_len += datain->length;
+		} else {
+			datain->length = (conn->sess->sess_ops->MaxBurstLength -
+					  next_burst_len);
+			next_burst_len = 0;
+
+			datain->flags |= ISCSI_FLAG_CMD_FINAL;
+			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+				datain->flags |= ISCSI_FLAG_DATA_ACK;
+		}
+	}
+
+	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+	datain->offset = read_data_done;
+
+	if (!dr->recovery) {
+		cmd->next_burst_len = next_burst_len;
+		cmd->read_data_done += datain->length;
+	} else {
+		dr->next_burst_len = next_burst_len;
+		dr->read_data_done += datain->length;
+	}
+
+	if (!dr->recovery) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+		return dr;
+	}
+
+	if (!dr->runlength) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	} else {
+		if ((dr->begrun + dr->runlength) == dr->data_sn) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	}
+
+	return dr;
+}
+
+/*
+ *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain *datain)
+{
+	u32 offset, read_data_done, read_data_left, seq_send_order;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+	struct iscsi_seq *seq;
+
+	dr = iscsit_get_datain_req(cmd);
+	if (!dr)
+		return NULL;
+
+	if (dr->recovery && dr->generate_recovery_values) {
+		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
+					cmd, dr) < 0)
+			return NULL;
+
+		dr->generate_recovery_values = 0;
+	}
+
+	read_data_done = (!dr->recovery) ?
+			cmd->read_data_done : dr->read_data_done;
+	seq_send_order = (!dr->recovery) ?
+			cmd->seq_send_order : dr->seq_send_order;
+
+	read_data_left = (cmd->data_length - read_data_done);
+	if (!read_data_left) {
+		pr_err("ITT: 0x%08x read_data_left is zero!\n",
+				cmd->init_task_tag);
+		return NULL;
+	}
+
+	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
+	if (!seq)
+		return NULL;
+
+	seq->sent = 1;
+
+	if (!dr->recovery && !seq->next_burst_len)
+		seq->first_datasn = cmd->data_sn;
+
+	offset = (seq->offset + seq->next_burst_len);
+
+	if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
+	     cmd->data_length) {
+		datain->length = (cmd->data_length - offset);
+		datain->offset = offset;
+
+		datain->flags |= ISCSI_FLAG_CMD_FINAL;
+		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+			datain->flags |= ISCSI_FLAG_DATA_ACK;
+
+		seq->next_burst_len = 0;
+		seq_send_order++;
+	} else {
+		if ((seq->next_burst_len +
+		     conn->conn_ops->MaxRecvDataSegmentLength) <
+		     conn->sess->sess_ops->MaxBurstLength) {
+			datain->length =
+				conn->conn_ops->MaxRecvDataSegmentLength;
+			datain->offset = (seq->offset + seq->next_burst_len);
+
+			seq->next_burst_len += datain->length;
+		} else {
+			datain->length = (conn->sess->sess_ops->MaxBurstLength -
+					  seq->next_burst_len);
+			datain->offset = (seq->offset + seq->next_burst_len);
+
+			datain->flags |= ISCSI_FLAG_CMD_FINAL;
+			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+				datain->flags |= ISCSI_FLAG_DATA_ACK;
+
+			seq->next_burst_len = 0;
+			seq_send_order++;
+		}
+	}
+
+	if ((read_data_done + datain->length) == cmd->data_length)
+		datain->flags |= ISCSI_FLAG_DATA_STATUS;
+
+	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+	if (!dr->recovery) {
+		cmd->seq_send_order = seq_send_order;
+		cmd->read_data_done += datain->length;
+	} else {
+		dr->seq_send_order = seq_send_order;
+		dr->read_data_done += datain->length;
+	}
+
+	if (!dr->recovery) {
+		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
+			seq->last_datasn = datain->data_sn;
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+		return dr;
+	}
+
+	if (!dr->runlength) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	} else {
+		if ((dr->begrun + dr->runlength) == dr->data_sn) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	}
+
+	return dr;
+}
+
+/*
+ *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain *datain)
+{
+	u32 next_burst_len, read_data_done, read_data_left;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+	struct iscsi_pdu *pdu;
+
+	dr = iscsit_get_datain_req(cmd);
+	if (!dr)
+		return NULL;
+
+	if (dr->recovery && dr->generate_recovery_values) {
+		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+					cmd, dr) < 0)
+			return NULL;
+
+		dr->generate_recovery_values = 0;
+	}
+
+	next_burst_len = (!dr->recovery) ?
+			cmd->next_burst_len : dr->next_burst_len;
+	read_data_done = (!dr->recovery) ?
+			cmd->read_data_done : dr->read_data_done;
+
+	read_data_left = (cmd->data_length - read_data_done);
+	if (!read_data_left) {
+		pr_err("ITT: 0x%08x read_data_left is zero!\n",
+				cmd->init_task_tag);
+		return dr;
+	}
+
+	pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
+	if (!pdu)
+		return dr;
+
+	if ((read_data_done + pdu->length) == cmd->data_length) {
+		pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
+		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+			pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+		next_burst_len = 0;
+	} else {
+		if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
+		     conn->sess->sess_ops->MaxBurstLength)
+			next_burst_len += pdu->length;
+		else {
+			pdu->flags |= ISCSI_FLAG_CMD_FINAL;
+			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+				pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+			next_burst_len = 0;
+		}
+	}
+
+	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+	if (!dr->recovery) {
+		cmd->next_burst_len = next_burst_len;
+		cmd->read_data_done += pdu->length;
+	} else {
+		dr->next_burst_len = next_burst_len;
+		dr->read_data_done += pdu->length;
+	}
+
+	datain->flags = pdu->flags;
+	datain->length = pdu->length;
+	datain->offset = pdu->offset;
+	datain->data_sn = pdu->data_sn;
+
+	if (!dr->recovery) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+		return dr;
+	}
+
+	if (!dr->runlength) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	} else {
+		if ((dr->begrun + dr->runlength) == dr->data_sn) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	}
+
+	return dr;
+}
+
+/*
+ *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain *datain)
+{
+	u32 read_data_done, read_data_left, seq_send_order;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+	struct iscsi_pdu *pdu;
+	struct iscsi_seq *seq = NULL;
+
+	dr = iscsit_get_datain_req(cmd);
+	if (!dr)
+		return NULL;
+
+	if (dr->recovery && dr->generate_recovery_values) {
+		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
+					cmd, dr) < 0)
+			return NULL;
+
+		dr->generate_recovery_values = 0;
+	}
+
+	read_data_done = (!dr->recovery) ?
+			cmd->read_data_done : dr->read_data_done;
+	seq_send_order = (!dr->recovery) ?
+			cmd->seq_send_order : dr->seq_send_order;
+
+	read_data_left = (cmd->data_length - read_data_done);
+	if (!read_data_left) {
+		pr_err("ITT: 0x%08x read_data_left is zero!\n",
+				cmd->init_task_tag);
+		return NULL;
+	}
+
+	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
+	if (!seq)
+		return NULL;
+
+	seq->sent = 1;
+
+	if (!dr->recovery && !seq->next_burst_len)
+		seq->first_datasn = cmd->data_sn;
+
+	pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
+	if (!pdu)
+		return NULL;
+
+	if (seq->pdu_send_order == seq->pdu_count) {
+		pdu->flags |= ISCSI_FLAG_CMD_FINAL;
+		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+			pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+		seq->next_burst_len = 0;
+		seq_send_order++;
+	} else
+		seq->next_burst_len += pdu->length;
+
+	if ((read_data_done + pdu->length) == cmd->data_length)
+		pdu->flags |= ISCSI_FLAG_DATA_STATUS;
+
+	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+	if (!dr->recovery) {
+		cmd->seq_send_order = seq_send_order;
+		cmd->read_data_done += pdu->length;
+	} else {
+		dr->seq_send_order = seq_send_order;
+		dr->read_data_done += pdu->length;
+	}
+
+	datain->flags = pdu->flags;
+	datain->length = pdu->length;
+	datain->offset = pdu->offset;
+	datain->data_sn = pdu->data_sn;
+
+	if (!dr->recovery) {
+		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
+			seq->last_datasn = datain->data_sn;
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+		return dr;
+	}
+
+	if (!dr->runlength) {
+		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	} else {
+		if ((dr->begrun + dr->runlength) == dr->data_sn) {
+			dr->dr_complete =
+			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+				DATAIN_COMPLETE_CONNECTION_RECOVERY;
+		}
+	}
+
+	return dr;
+}
+
+struct iscsi_datain_req *iscsit_get_datain_values(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain *datain)
+{
+	struct iscsi_conn *conn = cmd->conn;
+
+	if (conn->sess->sess_ops->DataSequenceInOrder &&
+	    conn->sess->sess_ops->DataPDUInOrder)
+		return iscsit_set_datain_values_yes_and_yes(cmd, datain);
+	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
+		  conn->sess->sess_ops->DataPDUInOrder)
+		return iscsit_set_datain_values_no_and_yes(cmd, datain);
+	else if (conn->sess->sess_ops->DataSequenceInOrder &&
+		 !conn->sess->sess_ops->DataPDUInOrder)
+		return iscsit_set_datain_values_yes_and_no(cmd, datain);
+	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
+		   !conn->sess->sess_ops->DataPDUInOrder)
+		return iscsit_set_datain_values_no_and_no(cmd, datain);
+
+	return NULL;
+}
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.h b/drivers/target/iscsi/iscsi_target_datain_values.h
new file mode 100644
index 0000000..646429a
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_datain_values.h
@@ -0,0 +1,12 @@
+#ifndef ISCSI_TARGET_DATAIN_VALUES_H
+#define ISCSI_TARGET_DATAIN_VALUES_H
+
+extern struct iscsi_datain_req *iscsit_allocate_datain_req(void);
+extern void iscsit_attach_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *);
+extern void iscsit_free_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *);
+extern void iscsit_free_all_datain_reqs(struct iscsi_cmd *);
+extern struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *);
+extern struct iscsi_datain_req *iscsit_get_datain_values(struct iscsi_cmd *,
+			struct iscsi_datain *);
+
+#endif   /*** ISCSI_TARGET_DATAIN_VALUES_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c
new file mode 100644
index 0000000..a19fa5e
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_device.c
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * This file contains the iSCSI Virtual Device and Disk Transport
+ * agnostic related functions.
+ *
+ \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/scsi_device.h>
+#include <target/target_core_base.h>
+#include <target/target_core_device.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+
+int iscsit_get_lun_for_tmr(
+	struct iscsi_cmd *cmd,
+	u64 lun)
+{
+	u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+	return transport_lookup_tmr_lun(&cmd->se_cmd, unpacked_lun);
+}
+
+int iscsit_get_lun_for_cmd(
+	struct iscsi_cmd *cmd,
+	unsigned char *cdb,
+	u64 lun)
+{
+	u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+	return transport_lookup_cmd_lun(&cmd->se_cmd, unpacked_lun);
+}
+
+void iscsit_determine_maxcmdsn(struct iscsi_session *sess)
+{
+	struct se_node_acl *se_nacl;
+
+	/*
+	 * This is a discovery session, the single queue slot was already
+	 * assigned in iscsi_login_zero_tsih().  Since only Logout and
+	 * Text Opcodes are allowed during discovery we do not have to worry
+	 * about the HBA's queue depth here.
+	 */
+	if (sess->sess_ops->SessionType)
+		return;
+
+	se_nacl = sess->se_sess->se_node_acl;
+
+	/*
+	 * This is a normal session, set the Session's CmdSN window to the
+	 * struct se_node_acl->queue_depth.  The value in struct se_node_acl->queue_depth
+	 * has already been validated as a legal value in
+	 * core_set_queue_depth_for_node().
+	 */
+	sess->cmdsn_window = se_nacl->queue_depth;
+	sess->max_cmd_sn = (sess->max_cmd_sn + se_nacl->queue_depth) - 1;
+}
+
+void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess)
+{
+	if (cmd->immediate_cmd || cmd->maxcmdsn_inc)
+		return;
+
+	cmd->maxcmdsn_inc = 1;
+
+	mutex_lock(&sess->cmdsn_mutex);
+	sess->max_cmd_sn += 1;
+	pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
+	mutex_unlock(&sess->cmdsn_mutex);
+}
diff --git a/drivers/target/iscsi/iscsi_target_device.h b/drivers/target/iscsi/iscsi_target_device.h
new file mode 100644
index 0000000..bef1cad
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_device.h
@@ -0,0 +1,9 @@
+#ifndef ISCSI_TARGET_DEVICE_H
+#define ISCSI_TARGET_DEVICE_H
+
+extern int iscsit_get_lun_for_tmr(struct iscsi_cmd *, u64);
+extern int iscsit_get_lun_for_cmd(struct iscsi_cmd *, unsigned char *, u64);
+extern void iscsit_determine_maxcmdsn(struct iscsi_session *);
+extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
+
+#endif /* ISCSI_TARGET_DEVICE_H */
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
new file mode 100644
index 0000000..b7ffc3c
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -0,0 +1,1004 @@
+/******************************************************************************
+ * This file contains error recovery level zero functions used by
+ * the iSCSI Target driver.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+/*
+ *	Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence()
+ *	checks against to determine a PDU's Offset+Length is within the current
+ *	DataOUT Sequence.  Used for DataSequenceInOrder=Yes only.
+ */
+void iscsit_set_dataout_sequence_values(
+	struct iscsi_cmd *cmd)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	/*
+	 * Still set seq_start_offset and seq_end_offset for Unsolicited
+	 * DataOUT, even if DataSequenceInOrder=No.
+	 */
+	if (cmd->unsolicited_data) {
+		cmd->seq_start_offset = cmd->write_data_done;
+		cmd->seq_end_offset = (cmd->write_data_done +
+			(cmd->data_length >
+			 conn->sess->sess_ops->FirstBurstLength) ?
+			conn->sess->sess_ops->FirstBurstLength : cmd->data_length);
+		return;
+	}
+
+	if (!conn->sess->sess_ops->DataSequenceInOrder)
+		return;
+
+	if (!cmd->seq_start_offset && !cmd->seq_end_offset) {
+		cmd->seq_start_offset = cmd->write_data_done;
+		cmd->seq_end_offset = (cmd->data_length >
+			conn->sess->sess_ops->MaxBurstLength) ?
+			(cmd->write_data_done +
+			conn->sess->sess_ops->MaxBurstLength) : cmd->data_length;
+	} else {
+		cmd->seq_start_offset = cmd->seq_end_offset;
+		cmd->seq_end_offset = ((cmd->seq_end_offset +
+			conn->sess->sess_ops->MaxBurstLength) >=
+			cmd->data_length) ? cmd->data_length :
+			(cmd->seq_end_offset +
+			 conn->sess->sess_ops->MaxBurstLength);
+	}
+}
+
+static int iscsit_dataout_within_command_recovery_check(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	/*
+	 * We do the within-command recovery checks here as it is
+	 * the first function called in iscsi_check_pre_dataout().
+	 * Basically, if we are in within-command recovery and
+	 * the PDU does not contain the offset the sequence needs,
+	 * dump the payload.
+	 *
+	 * This only applies to DataPDUInOrder=Yes, for
+	 * DataPDUInOrder=No we only re-request the failed PDU
+	 * and check that all PDUs in a sequence are received
+	 * upon end of sequence.
+	 */
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) &&
+		    (cmd->write_data_done != hdr->offset))
+			goto dump;
+
+		cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY;
+	} else {
+		struct iscsi_seq *seq;
+
+		seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length);
+		if (!seq)
+			return DATAOUT_CANNOT_RECOVER;
+		/*
+		 * Set the struct iscsi_seq pointer to reuse later.
+		 */
+		cmd->seq_ptr = seq;
+
+		if (conn->sess->sess_ops->DataPDUInOrder) {
+			if ((seq->status ==
+			     DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) &&
+			   ((seq->offset != hdr->offset) ||
+			    (seq->data_sn != hdr->datasn)))
+				goto dump;
+		} else {
+			if ((seq->status ==
+			     DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) &&
+			    (seq->data_sn != hdr->datasn))
+				goto dump;
+		}
+
+		if (seq->status == DATAOUT_SEQUENCE_COMPLETE)
+			goto dump;
+
+		if (seq->status != DATAOUT_SEQUENCE_COMPLETE)
+			seq->status = 0;
+	}
+
+	return DATAOUT_NORMAL;
+
+dump:
+	pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:"
+		" 0x%08x\n", hdr->offset, payload_length, hdr->datasn);
+	return iscsit_dump_data_payload(conn, payload_length, 1);
+}
+
+static int iscsit_dataout_check_unsolicited_sequence(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	u32 first_burst_len;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+
+	if ((hdr->offset < cmd->seq_start_offset) ||
+	   ((hdr->offset + payload_length) > cmd->seq_end_offset)) {
+		pr_err("Command ITT: 0x%08x with Offset: %u,"
+		" Length: %u outside of Unsolicited Sequence %u:%u while"
+		" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
+		hdr->offset, payload_length, cmd->seq_start_offset,
+			cmd->seq_end_offset);
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+	first_burst_len = (cmd->first_burst_len + payload_length);
+
+	if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) {
+		pr_err("Total %u bytes exceeds FirstBurstLength: %u"
+			" for this Unsolicited DataOut Burst.\n",
+			first_burst_len, conn->sess->sess_ops->FirstBurstLength);
+		transport_send_check_condition_and_sense(&cmd->se_cmd,
+				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+	/*
+	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
+	 * checks for the current Unsolicited DataOUT Sequence.
+	 */
+	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
+		/*
+		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
+		 * sequence checks are handled in
+		 * iscsit_dataout_datapduinorder_no_fbit().
+		 */
+		if (!conn->sess->sess_ops->DataPDUInOrder)
+			goto out;
+
+		if ((first_burst_len != cmd->data_length) &&
+		    (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) {
+			pr_err("Unsolicited non-immediate data"
+			" received %u does not equal FirstBurstLength: %u, and"
+			" does not equal ExpXferLen %u.\n", first_burst_len,
+				conn->sess->sess_ops->FirstBurstLength,
+				cmd->data_length);
+			transport_send_check_condition_and_sense(&cmd->se_cmd,
+					TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+			return DATAOUT_CANNOT_RECOVER;
+		}
+	} else {
+		if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) {
+			pr_err("Command ITT: 0x%08x reached"
+			" FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
+				" error.\n", cmd->init_task_tag,
+				conn->sess->sess_ops->FirstBurstLength);
+			return DATAOUT_CANNOT_RECOVER;
+		}
+		if (first_burst_len == cmd->data_length) {
+			pr_err("Command ITT: 0x%08x reached"
+			" ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
+			" error.\n", cmd->init_task_tag, cmd->data_length);
+			return DATAOUT_CANNOT_RECOVER;
+		}
+	}
+
+out:
+	return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_check_sequence(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	u32 next_burst_len;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_seq *seq = NULL;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	/*
+	 * For DataSequenceInOrder=Yes: Check that the offset and offset+length
+	 * is within range as defined by iscsi_set_dataout_sequence_values().
+	 *
+	 * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for
+	 * offset+length tuple.
+	 */
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		/*
+		 * Due to possibility of recovery DataOUT sent by the initiator
+		 * fullfilling an Recovery R2T, it's best to just dump the
+		 * payload here, instead of erroring out.
+		 */
+		if ((hdr->offset < cmd->seq_start_offset) ||
+		   ((hdr->offset + payload_length) > cmd->seq_end_offset)) {
+			pr_err("Command ITT: 0x%08x with Offset: %u,"
+			" Length: %u outside of Sequence %u:%u while"
+			" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
+			hdr->offset, payload_length, cmd->seq_start_offset,
+				cmd->seq_end_offset);
+
+			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+				return DATAOUT_CANNOT_RECOVER;
+			return DATAOUT_WITHIN_COMMAND_RECOVERY;
+		}
+
+		next_burst_len = (cmd->next_burst_len + payload_length);
+	} else {
+		seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length);
+		if (!seq)
+			return DATAOUT_CANNOT_RECOVER;
+		/*
+		 * Set the struct iscsi_seq pointer to reuse later.
+		 */
+		cmd->seq_ptr = seq;
+
+		if (seq->status == DATAOUT_SEQUENCE_COMPLETE) {
+			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+				return DATAOUT_CANNOT_RECOVER;
+			return DATAOUT_WITHIN_COMMAND_RECOVERY;
+		}
+
+		next_burst_len = (seq->next_burst_len + payload_length);
+	}
+
+	if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) {
+		pr_err("Command ITT: 0x%08x, NextBurstLength: %u and"
+			" Length: %u exceeds MaxBurstLength: %u. protocol"
+			" error.\n", cmd->init_task_tag,
+			(next_burst_len - payload_length),
+			payload_length, conn->sess->sess_ops->MaxBurstLength);
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+	/*
+	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
+	 * checks for the current DataOUT Sequence.
+	 */
+	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
+		/*
+		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
+		 * sequence checks are handled in
+		 * iscsit_dataout_datapduinorder_no_fbit().
+		 */
+		if (!conn->sess->sess_ops->DataPDUInOrder)
+			goto out;
+
+		if (conn->sess->sess_ops->DataSequenceInOrder) {
+			if ((next_burst_len <
+			     conn->sess->sess_ops->MaxBurstLength) &&
+			   ((cmd->write_data_done + payload_length) <
+			     cmd->data_length)) {
+				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
+				" before end of DataOUT sequence, protocol"
+				" error.\n", cmd->init_task_tag);
+				return DATAOUT_CANNOT_RECOVER;
+			}
+		} else {
+			if (next_burst_len < seq->xfer_len) {
+				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
+				" before end of DataOUT sequence, protocol"
+				" error.\n", cmd->init_task_tag);
+				return DATAOUT_CANNOT_RECOVER;
+			}
+		}
+	} else {
+		if (conn->sess->sess_ops->DataSequenceInOrder) {
+			if (next_burst_len ==
+					conn->sess->sess_ops->MaxBurstLength) {
+				pr_err("Command ITT: 0x%08x reached"
+				" MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is"
+				" not set, protocol error.", cmd->init_task_tag,
+					conn->sess->sess_ops->MaxBurstLength);
+				return DATAOUT_CANNOT_RECOVER;
+			}
+			if ((cmd->write_data_done + payload_length) ==
+					cmd->data_length) {
+				pr_err("Command ITT: 0x%08x reached"
+				" last DataOUT PDU in sequence but ISCSI_FLAG_"
+				"CMD_FINAL is not set, protocol error.\n",
+					cmd->init_task_tag);
+				return DATAOUT_CANNOT_RECOVER;
+			}
+		} else {
+			if (next_burst_len == seq->xfer_len) {
+				pr_err("Command ITT: 0x%08x reached"
+				" last DataOUT PDU in sequence but ISCSI_FLAG_"
+				"CMD_FINAL is not set, protocol error.\n",
+					cmd->init_task_tag);
+				return DATAOUT_CANNOT_RECOVER;
+			}
+		}
+	}
+
+out:
+	return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_check_datasn(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	int dump = 0, recovery = 0;
+	u32 data_sn = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	/*
+	 * Considering the target has no method of re-requesting DataOUT
+	 * by DataSN, if we receieve a greater DataSN than expected we
+	 * assume the functions for DataPDUInOrder=[Yes,No] below will
+	 * handle it.
+	 *
+	 * If the DataSN is less than expected, dump the payload.
+	 */
+	if (conn->sess->sess_ops->DataSequenceInOrder)
+		data_sn = cmd->data_sn;
+	else {
+		struct iscsi_seq *seq = cmd->seq_ptr;
+		data_sn = seq->data_sn;
+	}
+
+	if (hdr->datasn > data_sn) {
+		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
+			" higher than expected 0x%08x.\n", cmd->init_task_tag,
+				hdr->datasn, data_sn);
+		recovery = 1;
+		goto recover;
+	} else if (hdr->datasn < data_sn) {
+		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
+			" lower than expected 0x%08x, discarding payload.\n",
+			cmd->init_task_tag, hdr->datasn, data_sn);
+		dump = 1;
+		goto dump;
+	}
+
+	return DATAOUT_NORMAL;
+
+recover:
+	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+		pr_err("Unable to perform within-command recovery"
+				" while ERL=0.\n");
+		return DATAOUT_CANNOT_RECOVER;
+	}
+dump:
+	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+		return DATAOUT_CANNOT_RECOVER;
+
+	return (recovery || dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY :
+				DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_pre_datapduinorder_yes(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	int dump = 0, recovery = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	/*
+	 * For DataSequenceInOrder=Yes: If the offset is greater than the global
+	 * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has
+	 * occured and fail the connection.
+	 *
+	 * For DataSequenceInOrder=No: If the offset is greater than the per
+	 * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
+	 * error has occured and fail the connection.
+	 */
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		if (hdr->offset != cmd->write_data_done) {
+			pr_err("Command ITT: 0x%08x, received offset"
+			" %u different than expected %u.\n", cmd->init_task_tag,
+				hdr->offset, cmd->write_data_done);
+			recovery = 1;
+			goto recover;
+		}
+	} else {
+		struct iscsi_seq *seq = cmd->seq_ptr;
+
+		if (hdr->offset > seq->offset) {
+			pr_err("Command ITT: 0x%08x, received offset"
+			" %u greater than expected %u.\n", cmd->init_task_tag,
+				hdr->offset, seq->offset);
+			recovery = 1;
+			goto recover;
+		} else if (hdr->offset < seq->offset) {
+			pr_err("Command ITT: 0x%08x, received offset"
+			" %u less than expected %u, discarding payload.\n",
+				cmd->init_task_tag, hdr->offset, seq->offset);
+			dump = 1;
+			goto dump;
+		}
+	}
+
+	return DATAOUT_NORMAL;
+
+recover:
+	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+		pr_err("Unable to perform within-command recovery"
+				" while ERL=0.\n");
+		return DATAOUT_CANNOT_RECOVER;
+	}
+dump:
+	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+		return DATAOUT_CANNOT_RECOVER;
+
+	return (recovery) ? iscsit_recover_dataout_sequence(cmd,
+		hdr->offset, payload_length) :
+	       (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_pre_datapduinorder_no(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	struct iscsi_pdu *pdu;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	pdu = iscsit_get_pdu_holder(cmd, hdr->offset, payload_length);
+	if (!pdu)
+		return DATAOUT_CANNOT_RECOVER;
+
+	cmd->pdu_ptr = pdu;
+
+	switch (pdu->status) {
+	case ISCSI_PDU_NOT_RECEIVED:
+	case ISCSI_PDU_CRC_FAILED:
+	case ISCSI_PDU_TIMED_OUT:
+		break;
+	case ISCSI_PDU_RECEIVED_OK:
+		pr_err("Command ITT: 0x%08x received already gotten"
+			" Offset: %u, Length: %u\n", cmd->init_task_tag,
+				hdr->offset, payload_length);
+		return iscsit_dump_data_payload(cmd->conn, payload_length, 1);
+	default:
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+	return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length)
+{
+	struct iscsi_r2t *r2t;
+
+	if (cmd->unsolicited_data)
+		return 0;
+
+	r2t = iscsit_get_r2t_for_eos(cmd, offset, length);
+	if (!r2t)
+		return -1;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	r2t->seq_complete = 1;
+	cmd->outstanding_r2ts--;
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return 0;
+}
+
+static int iscsit_dataout_update_datapduinorder_no(
+	struct iscsi_cmd *cmd,
+	u32 data_sn,
+	int f_bit)
+{
+	int ret = 0;
+	struct iscsi_pdu *pdu = cmd->pdu_ptr;
+
+	pdu->data_sn = data_sn;
+
+	switch (pdu->status) {
+	case ISCSI_PDU_NOT_RECEIVED:
+		pdu->status = ISCSI_PDU_RECEIVED_OK;
+		break;
+	case ISCSI_PDU_CRC_FAILED:
+		pdu->status = ISCSI_PDU_RECEIVED_OK;
+		break;
+	case ISCSI_PDU_TIMED_OUT:
+		pdu->status = ISCSI_PDU_RECEIVED_OK;
+		break;
+	default:
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+	if (f_bit) {
+		ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu);
+		if (ret == DATAOUT_CANNOT_RECOVER)
+			return ret;
+	}
+
+	return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_post_crc_passed(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	int ret, send_r2t = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_seq *seq = NULL;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	if (cmd->unsolicited_data) {
+		if ((cmd->first_burst_len + payload_length) ==
+		     conn->sess->sess_ops->FirstBurstLength) {
+			if (iscsit_dataout_update_r2t(cmd, hdr->offset,
+					payload_length) < 0)
+				return DATAOUT_CANNOT_RECOVER;
+			send_r2t = 1;
+		}
+
+		if (!conn->sess->sess_ops->DataPDUInOrder) {
+			ret = iscsit_dataout_update_datapduinorder_no(cmd,
+				hdr->datasn, (hdr->flags & ISCSI_FLAG_CMD_FINAL));
+			if (ret == DATAOUT_CANNOT_RECOVER)
+				return ret;
+		}
+
+		cmd->first_burst_len += payload_length;
+
+		if (conn->sess->sess_ops->DataSequenceInOrder)
+			cmd->data_sn++;
+		else {
+			seq = cmd->seq_ptr;
+			seq->data_sn++;
+			seq->offset += payload_length;
+		}
+
+		if (send_r2t) {
+			if (seq)
+				seq->status = DATAOUT_SEQUENCE_COMPLETE;
+			cmd->first_burst_len = 0;
+			cmd->unsolicited_data = 0;
+		}
+	} else {
+		if (conn->sess->sess_ops->DataSequenceInOrder) {
+			if ((cmd->next_burst_len + payload_length) ==
+			     conn->sess->sess_ops->MaxBurstLength) {
+				if (iscsit_dataout_update_r2t(cmd, hdr->offset,
+						payload_length) < 0)
+					return DATAOUT_CANNOT_RECOVER;
+				send_r2t = 1;
+			}
+
+			if (!conn->sess->sess_ops->DataPDUInOrder) {
+				ret = iscsit_dataout_update_datapduinorder_no(
+						cmd, hdr->datasn,
+						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
+				if (ret == DATAOUT_CANNOT_RECOVER)
+					return ret;
+			}
+
+			cmd->next_burst_len += payload_length;
+			cmd->data_sn++;
+
+			if (send_r2t)
+				cmd->next_burst_len = 0;
+		} else {
+			seq = cmd->seq_ptr;
+
+			if ((seq->next_burst_len + payload_length) ==
+			     seq->xfer_len) {
+				if (iscsit_dataout_update_r2t(cmd, hdr->offset,
+						payload_length) < 0)
+					return DATAOUT_CANNOT_RECOVER;
+				send_r2t = 1;
+			}
+
+			if (!conn->sess->sess_ops->DataPDUInOrder) {
+				ret = iscsit_dataout_update_datapduinorder_no(
+						cmd, hdr->datasn,
+						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
+				if (ret == DATAOUT_CANNOT_RECOVER)
+					return ret;
+			}
+
+			seq->data_sn++;
+			seq->offset += payload_length;
+			seq->next_burst_len += payload_length;
+
+			if (send_r2t) {
+				seq->next_burst_len = 0;
+				seq->status = DATAOUT_SEQUENCE_COMPLETE;
+			}
+		}
+	}
+
+	if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder)
+		cmd->data_sn = 0;
+
+	cmd->write_data_done += payload_length;
+
+	return (cmd->write_data_done == cmd->data_length) ?
+		DATAOUT_SEND_TO_TRANSPORT : (send_r2t) ?
+		DATAOUT_SEND_R2T : DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_post_crc_failed(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *pdu;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	if (conn->sess->sess_ops->DataPDUInOrder)
+		goto recover;
+	/*
+	 * The rest of this function is only called when DataPDUInOrder=No.
+	 */
+	pdu = cmd->pdu_ptr;
+
+	switch (pdu->status) {
+	case ISCSI_PDU_NOT_RECEIVED:
+		pdu->status = ISCSI_PDU_CRC_FAILED;
+		break;
+	case ISCSI_PDU_CRC_FAILED:
+		break;
+	case ISCSI_PDU_TIMED_OUT:
+		pdu->status = ISCSI_PDU_CRC_FAILED;
+		break;
+	default:
+		return DATAOUT_CANNOT_RECOVER;
+	}
+
+recover:
+	return iscsit_recover_dataout_sequence(cmd, hdr->offset, payload_length);
+}
+
+/*
+ *	Called from iscsit_handle_data_out() before DataOUT Payload is received
+ *	and CRC computed.
+ */
+extern int iscsit_check_pre_dataout(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	int ret;
+	struct iscsi_conn *conn = cmd->conn;
+
+	ret = iscsit_dataout_within_command_recovery_check(cmd, buf);
+	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+	    (ret == DATAOUT_CANNOT_RECOVER))
+		return ret;
+
+	ret = iscsit_dataout_check_datasn(cmd, buf);
+	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+	    (ret == DATAOUT_CANNOT_RECOVER))
+		return ret;
+
+	if (cmd->unsolicited_data) {
+		ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf);
+		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+		    (ret == DATAOUT_CANNOT_RECOVER))
+			return ret;
+	} else {
+		ret = iscsit_dataout_check_sequence(cmd, buf);
+		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+		    (ret == DATAOUT_CANNOT_RECOVER))
+			return ret;
+	}
+
+	return (conn->sess->sess_ops->DataPDUInOrder) ?
+		iscsit_dataout_pre_datapduinorder_yes(cmd, buf) :
+		iscsit_dataout_pre_datapduinorder_no(cmd, buf);
+}
+
+/*
+ *	Called from iscsit_handle_data_out() after DataOUT Payload is received
+ *	and CRC computed.
+ */
+int iscsit_check_post_dataout(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf,
+	u8 data_crc_failed)
+{
+	struct iscsi_conn *conn = cmd->conn;
+
+	cmd->dataout_timeout_retries = 0;
+
+	if (!data_crc_failed)
+		return iscsit_dataout_post_crc_passed(cmd, buf);
+	else {
+		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+			pr_err("Unable to recover from DataOUT CRC"
+				" failure while ERL=0, closing session.\n");
+			iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR,
+					1, 0, buf, cmd);
+			return DATAOUT_CANNOT_RECOVER;
+		}
+
+		iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR,
+				0, 0, buf, cmd);
+		return iscsit_dataout_post_crc_failed(cmd, buf);
+	}
+}
+
+static void iscsit_handle_time2retain_timeout(unsigned long data)
+{
+	struct iscsi_session *sess = (struct iscsi_session *) data;
+	struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+	spin_lock_bh(&se_tpg->session_lock);
+	if (sess->time2retain_timer_flags & ISCSI_TF_STOP) {
+		spin_unlock_bh(&se_tpg->session_lock);
+		return;
+	}
+	if (atomic_read(&sess->session_reinstatement)) {
+		pr_err("Exiting Time2Retain handler because"
+				" session_reinstatement=1\n");
+		spin_unlock_bh(&se_tpg->session_lock);
+		return;
+	}
+	sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED;
+
+	pr_err("Time2Retain timer expired for SID: %u, cleaning up"
+			" iSCSI session.\n", sess->sid);
+	{
+	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+	if (tiqn) {
+		spin_lock(&tiqn->sess_err_stats.lock);
+		strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name,
+			(void *)sess->sess_ops->InitiatorName);
+		tiqn->sess_err_stats.last_sess_failure_type =
+				ISCSI_SESS_ERR_CXN_TIMEOUT;
+		tiqn->sess_err_stats.cxn_timeout_errors++;
+		sess->conn_timeout_errors++;
+		spin_unlock(&tiqn->sess_err_stats.lock);
+	}
+	}
+
+	spin_unlock_bh(&se_tpg->session_lock);
+	iscsit_close_session(sess);
+}
+
+extern void iscsit_start_time2retain_handler(struct iscsi_session *sess)
+{
+	int tpg_active;
+	/*
+	 * Only start Time2Retain timer when the assoicated TPG is still in
+	 * an ACTIVE (eg: not disabled or shutdown) state.
+	 */
+	spin_lock(&ISCSI_TPG_S(sess)->tpg_state_lock);
+	tpg_active = (ISCSI_TPG_S(sess)->tpg_state == TPG_STATE_ACTIVE);
+	spin_unlock(&ISCSI_TPG_S(sess)->tpg_state_lock);
+
+	if (!tpg_active)
+		return;
+
+	if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING)
+		return;
+
+	pr_debug("Starting Time2Retain timer for %u seconds on"
+		" SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid);
+
+	init_timer(&sess->time2retain_timer);
+	sess->time2retain_timer.expires =
+		(get_jiffies_64() + sess->sess_ops->DefaultTime2Retain * HZ);
+	sess->time2retain_timer.data = (unsigned long)sess;
+	sess->time2retain_timer.function = iscsit_handle_time2retain_timeout;
+	sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
+	sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&sess->time2retain_timer);
+}
+
+/*
+ *	Called with spin_lock_bh(&struct se_portal_group->session_lock) held
+ */
+extern int iscsit_stop_time2retain_timer(struct iscsi_session *sess)
+{
+	struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+	if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
+		return -1;
+
+	if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING))
+		return 0;
+
+	sess->time2retain_timer_flags |= ISCSI_TF_STOP;
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	del_timer_sync(&sess->time2retain_timer);
+
+	spin_lock_bh(&se_tpg->session_lock);
+	sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
+	pr_debug("Stopped Time2Retain Timer for SID: %u\n",
+			sess->sid);
+	return 0;
+}
+
+void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->state_lock);
+	if (atomic_read(&conn->connection_exit)) {
+		spin_unlock_bh(&conn->state_lock);
+		goto sleep;
+	}
+
+	if (atomic_read(&conn->transport_failed)) {
+		spin_unlock_bh(&conn->state_lock);
+		goto sleep;
+	}
+	spin_unlock_bh(&conn->state_lock);
+
+	iscsi_thread_set_force_reinstatement(conn);
+
+sleep:
+	wait_for_completion(&conn->conn_wait_rcfr_comp);
+	complete(&conn->conn_post_wait_comp);
+}
+
+void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep)
+{
+	spin_lock_bh(&conn->state_lock);
+	if (atomic_read(&conn->connection_exit)) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	if (atomic_read(&conn->transport_failed)) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	if (atomic_read(&conn->connection_reinstatement)) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	if (iscsi_thread_set_force_reinstatement(conn) < 0) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	atomic_set(&conn->connection_reinstatement, 1);
+	if (!sleep) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	atomic_set(&conn->sleep_on_conn_wait_comp, 1);
+	spin_unlock_bh(&conn->state_lock);
+
+	wait_for_completion(&conn->conn_wait_comp);
+	complete(&conn->conn_post_wait_comp);
+}
+
+void iscsit_fall_back_to_erl0(struct iscsi_session *sess)
+{
+	pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:"
+			" %u\n", sess->sid);
+
+	atomic_set(&sess->session_fall_back_to_erl0, 1);
+}
+
+static void iscsit_handle_connection_cleanup(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+
+	if ((sess->sess_ops->ErrorRecoveryLevel == 2) &&
+	    !atomic_read(&sess->session_reinstatement) &&
+	    !atomic_read(&sess->session_fall_back_to_erl0))
+		iscsit_connection_recovery_transport_reset(conn);
+	else {
+		pr_debug("Performing cleanup for failed iSCSI"
+			" Connection ID: %hu from %s\n", conn->cid,
+			sess->sess_ops->InitiatorName);
+		iscsit_close_connection(conn);
+	}
+}
+
+extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->state_lock);
+	if (atomic_read(&conn->connection_exit)) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+	atomic_set(&conn->connection_exit, 1);
+
+	if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
+		spin_unlock_bh(&conn->state_lock);
+		iscsit_close_connection(conn);
+		return;
+	}
+
+	if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) {
+		spin_unlock_bh(&conn->state_lock);
+		return;
+	}
+
+	pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
+	conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
+	spin_unlock_bh(&conn->state_lock);
+
+	iscsit_handle_connection_cleanup(conn);
+}
+
+/*
+ *	This is the simple function that makes the magic of
+ *	sync and steering happen in the follow paradoxical order:
+ *
+ *	0) Receive conn->of_marker (bytes left until next OFMarker)
+ *	   bytes into an offload buffer.  When we pass the exact number
+ *	   of bytes in conn->of_marker, iscsit_dump_data_payload() and hence
+ *	   rx_data() will automatically receive the identical u32 marker
+ *	   values and store it in conn->of_marker_offset;
+ *	1) Now conn->of_marker_offset will contain the offset to the start
+ *	   of the next iSCSI PDU.  Dump these remaining bytes into another
+ *	   offload buffer.
+ *	2) We are done!
+ *	   Next byte in the TCP stream will contain the next iSCSI PDU!
+ *	   Cool Huh?!
+ */
+int iscsit_recover_from_unknown_opcode(struct iscsi_conn *conn)
+{
+	/*
+	 * Make sure the remaining bytes to next maker is a sane value.
+	 */
+	if (conn->of_marker > (conn->conn_ops->OFMarkInt * 4)) {
+		pr_err("Remaining bytes to OFMarker: %u exceeds"
+			" OFMarkInt bytes: %u.\n", conn->of_marker,
+				conn->conn_ops->OFMarkInt * 4);
+		return -1;
+	}
+
+	pr_debug("Advancing %u bytes in TCP stream to get to the"
+			" next OFMarker.\n", conn->of_marker);
+
+	if (iscsit_dump_data_payload(conn, conn->of_marker, 0) < 0)
+		return -1;
+
+	/*
+	 * Make sure the offset marker we retrived is a valid value.
+	 */
+	if (conn->of_marker_offset > (ISCSI_HDR_LEN + (ISCSI_CRC_LEN * 2) +
+	    conn->conn_ops->MaxRecvDataSegmentLength)) {
+		pr_err("OfMarker offset value: %u exceeds limit.\n",
+			conn->of_marker_offset);
+		return -1;
+	}
+
+	pr_debug("Discarding %u bytes of TCP stream to get to the"
+			" next iSCSI Opcode.\n", conn->of_marker_offset);
+
+	if (iscsit_dump_data_payload(conn, conn->of_marker_offset, 0) < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_erl0.h b/drivers/target/iscsi/iscsi_target_erl0.h
new file mode 100644
index 0000000..21acc9a
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl0.h
@@ -0,0 +1,15 @@
+#ifndef ISCSI_TARGET_ERL0_H
+#define ISCSI_TARGET_ERL0_H
+
+extern void iscsit_set_dataout_sequence_values(struct iscsi_cmd *);
+extern int iscsit_check_pre_dataout(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_check_post_dataout(struct iscsi_cmd *, unsigned char *, u8);
+extern void iscsit_start_time2retain_handler(struct iscsi_session *);
+extern int iscsit_stop_time2retain_timer(struct iscsi_session *);
+extern void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *);
+extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int);
+extern void iscsit_fall_back_to_erl0(struct iscsi_session *);
+extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *);
+extern int iscsit_recover_from_unknown_opcode(struct iscsi_conn *);
+
+#endif   /*** ISCSI_TARGET_ERL0_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c
new file mode 100644
index 0000000..9806507
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl1.c
@@ -0,0 +1,1299 @@
+/*******************************************************************************
+ * This file contains error recovery level one used by the iSCSI Target driver.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target.h"
+
+#define OFFLOAD_BUF_SIZE	32768
+
+/*
+ *	Used to dump excess datain payload for certain error recovery
+ *	situations.  Receive in OFFLOAD_BUF_SIZE max of datain per rx_data().
+ *
+ *	dump_padding_digest denotes if padding and data digests need
+ *	to be dumped.
+ */
+int iscsit_dump_data_payload(
+	struct iscsi_conn *conn,
+	u32 buf_len,
+	int dump_padding_digest)
+{
+	char *buf, pad_bytes[4];
+	int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got;
+	u32 length, padding, offset = 0, size;
+	struct kvec iov;
+
+	length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len;
+
+	buf = kzalloc(length, GFP_ATOMIC);
+	if (!buf) {
+		pr_err("Unable to allocate %u bytes for offload"
+				" buffer.\n", length);
+		return -1;
+	}
+	memset(&iov, 0, sizeof(struct kvec));
+
+	while (offset < buf_len) {
+		size = ((offset + length) > buf_len) ?
+			(buf_len - offset) : length;
+
+		iov.iov_len = size;
+		iov.iov_base = buf;
+
+		rx_got = rx_data(conn, &iov, 1, size);
+		if (rx_got != size) {
+			ret = DATAOUT_CANNOT_RECOVER;
+			goto out;
+		}
+
+		offset += size;
+	}
+
+	if (!dump_padding_digest)
+		goto out;
+
+	padding = ((-buf_len) & 3);
+	if (padding != 0) {
+		iov.iov_len = padding;
+		iov.iov_base = pad_bytes;
+
+		rx_got = rx_data(conn, &iov, 1, padding);
+		if (rx_got != padding) {
+			ret = DATAOUT_CANNOT_RECOVER;
+			goto out;
+		}
+	}
+
+	if (conn->conn_ops->DataDigest) {
+		u32 data_crc;
+
+		iov.iov_len = ISCSI_CRC_LEN;
+		iov.iov_base = &data_crc;
+
+		rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
+		if (rx_got != ISCSI_CRC_LEN) {
+			ret = DATAOUT_CANNOT_RECOVER;
+			goto out;
+		}
+	}
+
+out:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ *	Used for retransmitting R2Ts from a R2T SNACK request.
+ */
+static int iscsit_send_recovery_r2t_for_snack(
+	struct iscsi_cmd *cmd,
+	struct iscsi_r2t *r2t)
+{
+	/*
+	 * If the struct iscsi_r2t has not been sent yet, we can safely
+	 * ignore retransmission
+	 * of the R2TSN in question.
+	 */
+	spin_lock_bh(&cmd->r2t_lock);
+	if (!r2t->sent_r2t) {
+		spin_unlock_bh(&cmd->r2t_lock);
+		return 0;
+	}
+	r2t->sent_r2t = 0;
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T);
+
+	return 0;
+}
+
+static int iscsit_handle_r2t_snack(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf,
+	u32 begrun,
+	u32 runlength)
+{
+	u32 last_r2tsn;
+	struct iscsi_r2t *r2t;
+
+	/*
+	 * Make sure the initiator is not requesting retransmission
+	 * of R2TSNs already acknowledged by a TMR TASK_REASSIGN.
+	 */
+	if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+	    (begrun <= cmd->acked_data_sn)) {
+		pr_err("ITT: 0x%08x, R2T SNACK requesting"
+			" retransmission of R2TSN: 0x%08x to 0x%08x but already"
+			" acked to  R2TSN: 0x%08x by TMR TASK_REASSIGN,"
+			" protocol error.\n", cmd->init_task_tag, begrun,
+			(begrun + runlength), cmd->acked_data_sn);
+
+			return iscsit_add_reject_from_cmd(
+					ISCSI_REASON_PROTOCOL_ERROR,
+					1, 0, buf, cmd);
+	}
+
+	if (runlength) {
+		if ((begrun + runlength) > cmd->r2t_sn) {
+			pr_err("Command ITT: 0x%08x received R2T SNACK"
+			" with BegRun: 0x%08x, RunLength: 0x%08x, exceeds"
+			" current R2TSN: 0x%08x, protocol error.\n",
+			cmd->init_task_tag, begrun, runlength, cmd->r2t_sn);
+			return iscsit_add_reject_from_cmd(
+				ISCSI_REASON_BOOKMARK_INVALID, 1, 0, buf, cmd);
+		}
+		last_r2tsn = (begrun + runlength);
+	} else
+		last_r2tsn = cmd->r2t_sn;
+
+	while (begrun < last_r2tsn) {
+		r2t = iscsit_get_holder_for_r2tsn(cmd, begrun);
+		if (!r2t)
+			return -1;
+		if (iscsit_send_recovery_r2t_for_snack(cmd, r2t) < 0)
+			return -1;
+
+		begrun++;
+	}
+
+	return 0;
+}
+
+/*
+ *	Generates Offsets and NextBurstLength based on Begrun and Runlength
+ *	carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN.
+ *
+ *	For DataSequenceInOrder=Yes and DataPDUInOrder=[Yes,No] only.
+ *
+ *	FIXME: How is this handled for a RData SNACK?
+ */
+int iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain_req *dr)
+{
+	u32 data_sn = 0, data_sn_count = 0;
+	u32 pdu_start = 0, seq_no = 0;
+	u32 begrun = dr->begrun;
+	struct iscsi_conn *conn = cmd->conn;
+
+	while (begrun > data_sn++) {
+		data_sn_count++;
+		if ((dr->next_burst_len +
+		     conn->conn_ops->MaxRecvDataSegmentLength) <
+		     conn->sess->sess_ops->MaxBurstLength) {
+			dr->read_data_done +=
+				conn->conn_ops->MaxRecvDataSegmentLength;
+			dr->next_burst_len +=
+				conn->conn_ops->MaxRecvDataSegmentLength;
+		} else {
+			dr->read_data_done +=
+				(conn->sess->sess_ops->MaxBurstLength -
+				 dr->next_burst_len);
+			dr->next_burst_len = 0;
+			pdu_start += data_sn_count;
+			data_sn_count = 0;
+			seq_no++;
+		}
+	}
+
+	if (!conn->sess->sess_ops->DataPDUInOrder) {
+		cmd->seq_no = seq_no;
+		cmd->pdu_start = pdu_start;
+		cmd->pdu_send_order = data_sn_count;
+	}
+
+	return 0;
+}
+
+/*
+ *	Generates Offsets and NextBurstLength based on Begrun and Runlength
+ *	carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN.
+ *
+ *	For DataSequenceInOrder=No and DataPDUInOrder=[Yes,No] only.
+ *
+ *	FIXME: How is this handled for a RData SNACK?
+ */
+int iscsit_create_recovery_datain_values_datasequenceinorder_no(
+	struct iscsi_cmd *cmd,
+	struct iscsi_datain_req *dr)
+{
+	int found_seq = 0, i;
+	u32 data_sn, read_data_done = 0, seq_send_order = 0;
+	u32 begrun = dr->begrun;
+	u32 runlength = dr->runlength;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_seq *first_seq = NULL, *seq = NULL;
+
+	if (!cmd->seq_list) {
+		pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+		return -1;
+	}
+
+	/*
+	 * Calculate read_data_done for all sequences containing a
+	 * first_datasn and last_datasn less than the BegRun.
+	 *
+	 * Locate the struct iscsi_seq the BegRun lies within and calculate
+	 * NextBurstLenghth up to the DataSN based on MaxRecvDataSegmentLength.
+	 *
+	 * Also use struct iscsi_seq->seq_send_order to determine where to start.
+	 */
+	for (i = 0; i < cmd->seq_count; i++) {
+		seq = &cmd->seq_list[i];
+
+		if (!seq->seq_send_order)
+			first_seq = seq;
+
+		/*
+		 * No data has been transferred for this DataIN sequence, so the
+		 * seq->first_datasn and seq->last_datasn have not been set.
+		 */
+		if (!seq->sent) {
+#if 0
+			pr_err("Ignoring non-sent sequence 0x%08x ->"
+				" 0x%08x\n\n", seq->first_datasn,
+				seq->last_datasn);
+#endif
+			continue;
+		}
+
+		/*
+		 * This DataIN sequence is precedes the received BegRun, add the
+		 * total xfer_len of the sequence to read_data_done and reset
+		 * seq->pdu_send_order.
+		 */
+		if ((seq->first_datasn < begrun) &&
+				(seq->last_datasn < begrun)) {
+#if 0
+			pr_err("Pre BegRun sequence 0x%08x ->"
+				" 0x%08x\n", seq->first_datasn,
+				seq->last_datasn);
+#endif
+			read_data_done += cmd->seq_list[i].xfer_len;
+			seq->next_burst_len = seq->pdu_send_order = 0;
+			continue;
+		}
+
+		/*
+		 * The BegRun lies within this DataIN sequence.
+		 */
+		if ((seq->first_datasn <= begrun) &&
+				(seq->last_datasn >= begrun)) {
+#if 0
+			pr_err("Found sequence begrun: 0x%08x in"
+				" 0x%08x -> 0x%08x\n", begrun,
+				seq->first_datasn, seq->last_datasn);
+#endif
+			seq_send_order = seq->seq_send_order;
+			data_sn = seq->first_datasn;
+			seq->next_burst_len = seq->pdu_send_order = 0;
+			found_seq = 1;
+
+			/*
+			 * For DataPDUInOrder=Yes, while the first DataSN of
+			 * the sequence is less than the received BegRun, add
+			 * the MaxRecvDataSegmentLength to read_data_done and
+			 * to the sequence's next_burst_len;
+			 *
+			 * For DataPDUInOrder=No, while the first DataSN of the
+			 * sequence is less than the received BegRun, find the
+			 * struct iscsi_pdu of the DataSN in question and add the
+			 * MaxRecvDataSegmentLength to read_data_done and to the
+			 * sequence's next_burst_len;
+			 */
+			if (conn->sess->sess_ops->DataPDUInOrder) {
+				while (data_sn < begrun) {
+					seq->pdu_send_order++;
+					read_data_done +=
+						conn->conn_ops->MaxRecvDataSegmentLength;
+					seq->next_burst_len +=
+						conn->conn_ops->MaxRecvDataSegmentLength;
+					data_sn++;
+				}
+			} else {
+				int j;
+				struct iscsi_pdu *pdu;
+
+				while (data_sn < begrun) {
+					seq->pdu_send_order++;
+
+					for (j = 0; j < seq->pdu_count; j++) {
+						pdu = &cmd->pdu_list[
+							seq->pdu_start + j];
+						if (pdu->data_sn == data_sn) {
+							read_data_done +=
+								pdu->length;
+							seq->next_burst_len +=
+								pdu->length;
+						}
+					}
+					data_sn++;
+				}
+			}
+			continue;
+		}
+
+		/*
+		 * This DataIN sequence is larger than the received BegRun,
+		 * reset seq->pdu_send_order and continue.
+		 */
+		if ((seq->first_datasn > begrun) ||
+				(seq->last_datasn > begrun)) {
+#if 0
+			pr_err("Post BegRun sequence 0x%08x -> 0x%08x\n",
+					seq->first_datasn, seq->last_datasn);
+#endif
+			seq->next_burst_len = seq->pdu_send_order = 0;
+			continue;
+		}
+	}
+
+	if (!found_seq) {
+		if (!begrun) {
+			if (!first_seq) {
+				pr_err("ITT: 0x%08x, Begrun: 0x%08x"
+					" but first_seq is NULL\n",
+					cmd->init_task_tag, begrun);
+				return -1;
+			}
+			seq_send_order = first_seq->seq_send_order;
+			seq->next_burst_len = seq->pdu_send_order = 0;
+			goto done;
+		}
+
+		pr_err("Unable to locate struct iscsi_seq for ITT: 0x%08x,"
+			" BegRun: 0x%08x, RunLength: 0x%08x while"
+			" DataSequenceInOrder=No and DataPDUInOrder=%s.\n",
+				cmd->init_task_tag, begrun, runlength,
+			(conn->sess->sess_ops->DataPDUInOrder) ? "Yes" : "No");
+		return -1;
+	}
+
+done:
+	dr->read_data_done = read_data_done;
+	dr->seq_send_order = seq_send_order;
+
+	return 0;
+}
+
+static int iscsit_handle_recovery_datain(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf,
+	u32 begrun,
+	u32 runlength)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+
+	if (!atomic_read(&se_cmd->t_transport_complete)) {
+		pr_err("Ignoring ITT: 0x%08x Data SNACK\n",
+				cmd->init_task_tag);
+		return 0;
+	}
+
+	/*
+	 * Make sure the initiator is not requesting retransmission
+	 * of DataSNs already acknowledged by a Data ACK SNACK.
+	 */
+	if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+	    (begrun <= cmd->acked_data_sn)) {
+		pr_err("ITT: 0x%08x, Data SNACK requesting"
+			" retransmission of DataSN: 0x%08x to 0x%08x but"
+			" already acked to DataSN: 0x%08x by Data ACK SNACK,"
+			" protocol error.\n", cmd->init_task_tag, begrun,
+			(begrun + runlength), cmd->acked_data_sn);
+
+		return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
+				1, 0, buf, cmd);
+	}
+
+	/*
+	 * Make sure BegRun and RunLength in the Data SNACK are sane.
+	 * Note: (cmd->data_sn - 1) will carry the maximum DataSN sent.
+	 */
+	if ((begrun + runlength) > (cmd->data_sn - 1)) {
+		pr_err("Initiator requesting BegRun: 0x%08x, RunLength"
+			": 0x%08x greater than maximum DataSN: 0x%08x.\n",
+				begrun, runlength, (cmd->data_sn - 1));
+		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
+				1, 0, buf, cmd);
+	}
+
+	dr = iscsit_allocate_datain_req();
+	if (!dr)
+		return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+				1, 0, buf, cmd);
+
+	dr->data_sn = dr->begrun = begrun;
+	dr->runlength = runlength;
+	dr->generate_recovery_values = 1;
+	dr->recovery = DATAIN_WITHIN_COMMAND_RECOVERY;
+
+	iscsit_attach_datain_req(cmd, dr);
+
+	cmd->i_state = ISTATE_SEND_DATAIN;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+	return 0;
+}
+
+int iscsit_handle_recovery_datain_or_r2t(
+	struct iscsi_conn *conn,
+	unsigned char *buf,
+	u32 init_task_tag,
+	u32 targ_xfer_tag,
+	u32 begrun,
+	u32 runlength)
+{
+	struct iscsi_cmd *cmd;
+
+	cmd = iscsit_find_cmd_from_itt(conn, init_task_tag);
+	if (!cmd)
+		return 0;
+
+	/*
+	 * FIXME: This will not work for bidi commands.
+	 */
+	switch (cmd->data_direction) {
+	case DMA_TO_DEVICE:
+		return iscsit_handle_r2t_snack(cmd, buf, begrun, runlength);
+	case DMA_FROM_DEVICE:
+		return iscsit_handle_recovery_datain(cmd, buf, begrun,
+				runlength);
+	default:
+		pr_err("Unknown cmd->data_direction: 0x%02x\n",
+				cmd->data_direction);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* #warning FIXME: Status SNACK needs to be dependent on OPCODE!!! */
+int iscsit_handle_status_snack(
+	struct iscsi_conn *conn,
+	u32 init_task_tag,
+	u32 targ_xfer_tag,
+	u32 begrun,
+	u32 runlength)
+{
+	struct iscsi_cmd *cmd = NULL;
+	u32 last_statsn;
+	int found_cmd;
+
+	if (conn->exp_statsn > begrun) {
+		pr_err("Got Status SNACK Begrun: 0x%08x, RunLength:"
+			" 0x%08x but already got ExpStatSN: 0x%08x on CID:"
+			" %hu.\n", begrun, runlength, conn->exp_statsn,
+			conn->cid);
+		return 0;
+	}
+
+	last_statsn = (!runlength) ? conn->stat_sn : (begrun + runlength);
+
+	while (begrun < last_statsn) {
+		found_cmd = 0;
+
+		spin_lock_bh(&conn->cmd_lock);
+		list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+			if (cmd->stat_sn == begrun) {
+				found_cmd = 1;
+				break;
+			}
+		}
+		spin_unlock_bh(&conn->cmd_lock);
+
+		if (!found_cmd) {
+			pr_err("Unable to find StatSN: 0x%08x for"
+				" a Status SNACK, assuming this was a"
+				" protactic SNACK for an untransmitted"
+				" StatSN, ignoring.\n", begrun);
+			begrun++;
+			continue;
+		}
+
+		spin_lock_bh(&cmd->istate_lock);
+		if (cmd->i_state == ISTATE_SEND_DATAIN) {
+			spin_unlock_bh(&cmd->istate_lock);
+			pr_err("Ignoring Status SNACK for BegRun:"
+				" 0x%08x, RunLength: 0x%08x, assuming this was"
+				" a protactic SNACK for an untransmitted"
+				" StatSN\n", begrun, runlength);
+			begrun++;
+			continue;
+		}
+		spin_unlock_bh(&cmd->istate_lock);
+
+		cmd->i_state = ISTATE_SEND_STATUS_RECOVERY;
+		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+		begrun++;
+	}
+
+	return 0;
+}
+
+int iscsit_handle_data_ack(
+	struct iscsi_conn *conn,
+	u32 targ_xfer_tag,
+	u32 begrun,
+	u32 runlength)
+{
+	struct iscsi_cmd *cmd = NULL;
+
+	cmd = iscsit_find_cmd_from_ttt(conn, targ_xfer_tag);
+	if (!cmd) {
+		pr_err("Data ACK SNACK for TTT: 0x%08x is"
+			" invalid.\n", targ_xfer_tag);
+		return -1;
+	}
+
+	if (begrun <= cmd->acked_data_sn) {
+		pr_err("ITT: 0x%08x Data ACK SNACK BegRUN: 0x%08x is"
+			" less than the already acked DataSN: 0x%08x.\n",
+			cmd->init_task_tag, begrun, cmd->acked_data_sn);
+		return -1;
+	}
+
+	/*
+	 * For Data ACK SNACK, BegRun is the next expected DataSN.
+	 * (see iSCSI v19: 10.16.6)
+	 */
+	cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+	cmd->acked_data_sn = (begrun - 1);
+
+	pr_debug("Received Data ACK SNACK for ITT: 0x%08x,"
+		" updated acked DataSN to 0x%08x.\n",
+			cmd->init_task_tag, cmd->acked_data_sn);
+
+	return 0;
+}
+
+static int iscsit_send_recovery_r2t(
+	struct iscsi_cmd *cmd,
+	u32 offset,
+	u32 xfer_len)
+{
+	int ret;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	ret = iscsit_add_r2t_to_list(cmd, offset, xfer_len, 1, 0);
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return ret;
+}
+
+int iscsit_dataout_datapduinorder_no_fbit(
+	struct iscsi_cmd *cmd,
+	struct iscsi_pdu *pdu)
+{
+	int i, send_recovery_r2t = 0, recovery = 0;
+	u32 length = 0, offset = 0, pdu_count = 0, xfer_len = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *first_pdu = NULL;
+
+	/*
+	 * Get an struct iscsi_pdu pointer to the first PDU, and total PDU count
+	 * of the DataOUT sequence.
+	 */
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		for (i = 0; i < cmd->pdu_count; i++) {
+			if (cmd->pdu_list[i].seq_no == pdu->seq_no) {
+				if (!first_pdu)
+					first_pdu = &cmd->pdu_list[i];
+				 xfer_len += cmd->pdu_list[i].length;
+				 pdu_count++;
+			} else if (pdu_count)
+				break;
+		}
+	} else {
+		struct iscsi_seq *seq = cmd->seq_ptr;
+
+		first_pdu = &cmd->pdu_list[seq->pdu_start];
+		pdu_count = seq->pdu_count;
+	}
+
+	if (!first_pdu || !pdu_count)
+		return DATAOUT_CANNOT_RECOVER;
+
+	/*
+	 * Loop through the ending DataOUT Sequence checking each struct iscsi_pdu.
+	 * The following ugly logic does batching of not received PDUs.
+	 */
+	for (i = 0; i < pdu_count; i++) {
+		if (first_pdu[i].status == ISCSI_PDU_RECEIVED_OK) {
+			if (!send_recovery_r2t)
+				continue;
+
+			if (iscsit_send_recovery_r2t(cmd, offset, length) < 0)
+				return DATAOUT_CANNOT_RECOVER;
+
+			send_recovery_r2t = length = offset = 0;
+			continue;
+		}
+		/*
+		 * Set recovery = 1 for any missing, CRC failed, or timed
+		 * out PDUs to let the DataOUT logic know that this sequence
+		 * has not been completed yet.
+		 *
+		 * Also, only send a Recovery R2T for ISCSI_PDU_NOT_RECEIVED.
+		 * We assume if the PDU either failed CRC or timed out
+		 * that a Recovery R2T has already been sent.
+		 */
+		recovery = 1;
+
+		if (first_pdu[i].status != ISCSI_PDU_NOT_RECEIVED)
+			continue;
+
+		if (!offset)
+			offset = first_pdu[i].offset;
+		length += first_pdu[i].length;
+
+		send_recovery_r2t = 1;
+	}
+
+	if (send_recovery_r2t)
+		if (iscsit_send_recovery_r2t(cmd, offset, length) < 0)
+			return DATAOUT_CANNOT_RECOVER;
+
+	return (!recovery) ? DATAOUT_NORMAL : DATAOUT_WITHIN_COMMAND_RECOVERY;
+}
+
+static int iscsit_recalculate_dataout_values(
+	struct iscsi_cmd *cmd,
+	u32 pdu_offset,
+	u32 pdu_length,
+	u32 *r2t_offset,
+	u32 *r2t_length)
+{
+	int i;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *pdu = NULL;
+
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		cmd->data_sn = 0;
+
+		if (conn->sess->sess_ops->DataPDUInOrder) {
+			*r2t_offset = cmd->write_data_done;
+			*r2t_length = (cmd->seq_end_offset -
+					cmd->write_data_done);
+			return 0;
+		}
+
+		*r2t_offset = cmd->seq_start_offset;
+		*r2t_length = (cmd->seq_end_offset - cmd->seq_start_offset);
+
+		for (i = 0; i < cmd->pdu_count; i++) {
+			pdu = &cmd->pdu_list[i];
+
+			if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+				continue;
+
+			if ((pdu->offset >= cmd->seq_start_offset) &&
+			   ((pdu->offset + pdu->length) <=
+			     cmd->seq_end_offset)) {
+				if (!cmd->unsolicited_data)
+					cmd->next_burst_len -= pdu->length;
+				else
+					cmd->first_burst_len -= pdu->length;
+
+				cmd->write_data_done -= pdu->length;
+				pdu->status = ISCSI_PDU_NOT_RECEIVED;
+			}
+		}
+	} else {
+		struct iscsi_seq *seq = NULL;
+
+		seq = iscsit_get_seq_holder(cmd, pdu_offset, pdu_length);
+		if (!seq)
+			return -1;
+
+		*r2t_offset = seq->orig_offset;
+		*r2t_length = seq->xfer_len;
+
+		cmd->write_data_done -= (seq->offset - seq->orig_offset);
+		if (cmd->immediate_data)
+			cmd->first_burst_len = cmd->write_data_done;
+
+		seq->data_sn = 0;
+		seq->offset = seq->orig_offset;
+		seq->next_burst_len = 0;
+		seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+		if (conn->sess->sess_ops->DataPDUInOrder)
+			return 0;
+
+		for (i = 0; i < seq->pdu_count; i++) {
+			pdu = &cmd->pdu_list[i+seq->pdu_start];
+
+			if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+				continue;
+
+			pdu->status = ISCSI_PDU_NOT_RECEIVED;
+		}
+	}
+
+	return 0;
+}
+
+int iscsit_recover_dataout_sequence(
+	struct iscsi_cmd *cmd,
+	u32 pdu_offset,
+	u32 pdu_length)
+{
+	u32 r2t_length = 0, r2t_offset = 0;
+
+	spin_lock_bh(&cmd->istate_lock);
+	cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY;
+	spin_unlock_bh(&cmd->istate_lock);
+
+	if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length,
+			&r2t_offset, &r2t_length) < 0)
+		return DATAOUT_CANNOT_RECOVER;
+
+	iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length);
+
+	return DATAOUT_WITHIN_COMMAND_RECOVERY;
+}
+
+static struct iscsi_ooo_cmdsn *iscsit_allocate_ooo_cmdsn(void)
+{
+	struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL;
+
+	ooo_cmdsn = kmem_cache_zalloc(lio_ooo_cache, GFP_ATOMIC);
+	if (!ooo_cmdsn) {
+		pr_err("Unable to allocate memory for"
+			" struct iscsi_ooo_cmdsn.\n");
+		return NULL;
+	}
+	INIT_LIST_HEAD(&ooo_cmdsn->ooo_list);
+
+	return ooo_cmdsn;
+}
+
+/*
+ *	Called with sess->cmdsn_mutex held.
+ */
+static int iscsit_attach_ooo_cmdsn(
+	struct iscsi_session *sess,
+	struct iscsi_ooo_cmdsn *ooo_cmdsn)
+{
+	struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp;
+	/*
+	 * We attach the struct iscsi_ooo_cmdsn entry to the out of order
+	 * list in increasing CmdSN order.
+	 * This allows iscsi_execute_ooo_cmdsns() to detect any
+	 * additional CmdSN holes while performing delayed execution.
+	 */
+	if (list_empty(&sess->sess_ooo_cmdsn_list))
+		list_add_tail(&ooo_cmdsn->ooo_list,
+				&sess->sess_ooo_cmdsn_list);
+	else {
+		ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev,
+				typeof(*ooo_tail), ooo_list);
+		/*
+		 * CmdSN is greater than the tail of the list.
+		 */
+		if (ooo_tail->cmdsn < ooo_cmdsn->cmdsn)
+			list_add_tail(&ooo_cmdsn->ooo_list,
+					&sess->sess_ooo_cmdsn_list);
+		else {
+			/*
+			 * CmdSN is either lower than the head,  or somewhere
+			 * in the middle.
+			 */
+			list_for_each_entry(ooo_tmp, &sess->sess_ooo_cmdsn_list,
+						ooo_list) {
+				while (ooo_tmp->cmdsn < ooo_cmdsn->cmdsn)
+					continue;
+
+				list_add(&ooo_cmdsn->ooo_list,
+					&ooo_tmp->ooo_list);
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ *	Removes an struct iscsi_ooo_cmdsn from a session's list,
+ *	called with struct iscsi_session->cmdsn_mutex held.
+ */
+void iscsit_remove_ooo_cmdsn(
+	struct iscsi_session *sess,
+	struct iscsi_ooo_cmdsn *ooo_cmdsn)
+{
+	list_del(&ooo_cmdsn->ooo_list);
+	kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+}
+
+void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
+{
+	struct iscsi_ooo_cmdsn *ooo_cmdsn;
+	struct iscsi_session *sess = conn->sess;
+
+	mutex_lock(&sess->cmdsn_mutex);
+	list_for_each_entry(ooo_cmdsn, &sess->sess_ooo_cmdsn_list, ooo_list) {
+		if (ooo_cmdsn->cid != conn->cid)
+			continue;
+
+		ooo_cmdsn->cmd = NULL;
+	}
+	mutex_unlock(&sess->cmdsn_mutex);
+}
+
+/*
+ *	Called with sess->cmdsn_mutex held.
+ */
+int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess)
+{
+	int ooo_count = 0;
+	struct iscsi_cmd *cmd = NULL;
+	struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+
+	list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+				&sess->sess_ooo_cmdsn_list, ooo_list) {
+		if (ooo_cmdsn->cmdsn != sess->exp_cmd_sn)
+			continue;
+
+		if (!ooo_cmdsn->cmd) {
+			sess->exp_cmd_sn++;
+			iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+			continue;
+		}
+
+		cmd = ooo_cmdsn->cmd;
+		cmd->i_state = cmd->deferred_i_state;
+		ooo_count++;
+		sess->exp_cmd_sn++;
+		pr_debug("Executing out of order CmdSN: 0x%08x,"
+			" incremented ExpCmdSN to 0x%08x.\n",
+			cmd->cmd_sn, sess->exp_cmd_sn);
+
+		iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+
+		if (iscsit_execute_cmd(cmd, 1) < 0)
+			return -1;
+
+		continue;
+	}
+
+	return ooo_count;
+}
+
+/*
+ *	Called either:
+ *
+ *	1. With sess->cmdsn_mutex held from iscsi_execute_ooo_cmdsns()
+ *	or iscsi_check_received_cmdsn().
+ *	2. With no locks held directly from iscsi_handle_XXX_pdu() functions
+ *	for immediate commands.
+ */
+int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
+{
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	int lr = 0;
+
+	spin_lock_bh(&cmd->istate_lock);
+	if (ooo)
+		cmd->cmd_flags &= ~ICF_OOO_CMDSN;
+
+	switch (cmd->iscsi_opcode) {
+	case ISCSI_OP_SCSI_CMD:
+		/*
+		 * Go ahead and send the CHECK_CONDITION status for
+		 * any SCSI CDB exceptions that may have occurred, also
+		 * handle the SCF_SCSI_RESERVATION_CONFLICT case here as well.
+		 */
+		if (se_cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION) {
+			if (se_cmd->se_cmd_flags &
+					SCF_SCSI_RESERVATION_CONFLICT) {
+				cmd->i_state = ISTATE_SEND_STATUS;
+				spin_unlock_bh(&cmd->istate_lock);
+				iscsit_add_cmd_to_response_queue(cmd, cmd->conn,
+						cmd->i_state);
+				return 0;
+			}
+			spin_unlock_bh(&cmd->istate_lock);
+			/*
+			 * Determine if delayed TASK_ABORTED status for WRITEs
+			 * should be sent now if no unsolicited data out
+			 * payloads are expected, or if the delayed status
+			 * should be sent after unsolicited data out with
+			 * ISCSI_FLAG_CMD_FINAL set in iscsi_handle_data_out()
+			 */
+			if (transport_check_aborted_status(se_cmd,
+					(cmd->unsolicited_data == 0)) != 0)
+				return 0;
+			/*
+			 * Otherwise send CHECK_CONDITION and sense for
+			 * exception
+			 */
+			return transport_send_check_condition_and_sense(se_cmd,
+					se_cmd->scsi_sense_reason, 0);
+		}
+		/*
+		 * Special case for delayed CmdSN with Immediate
+		 * Data and/or Unsolicited Data Out attached.
+		 */
+		if (cmd->immediate_data) {
+			if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+				spin_unlock_bh(&cmd->istate_lock);
+				return transport_generic_handle_data(
+						&cmd->se_cmd);
+			}
+			spin_unlock_bh(&cmd->istate_lock);
+
+			if (!(cmd->cmd_flags &
+					ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) {
+				/*
+				 * Send the delayed TASK_ABORTED status for
+				 * WRITEs if no more unsolicitied data is
+				 * expected.
+				 */
+				if (transport_check_aborted_status(se_cmd, 1)
+						!= 0)
+					return 0;
+
+				iscsit_set_dataout_sequence_values(cmd);
+				iscsit_build_r2ts_for_cmd(cmd, cmd->conn, 0);
+			}
+			return 0;
+		}
+		/*
+		 * The default handler.
+		 */
+		spin_unlock_bh(&cmd->istate_lock);
+
+		if ((cmd->data_direction == DMA_TO_DEVICE) &&
+		    !(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) {
+			/*
+			 * Send the delayed TASK_ABORTED status for WRITEs if
+			 * no more nsolicitied data is expected.
+			 */
+			if (transport_check_aborted_status(se_cmd, 1) != 0)
+				return 0;
+
+			iscsit_set_dataout_sequence_values(cmd);
+			spin_lock_bh(&cmd->dataout_timeout_lock);
+			iscsit_start_dataout_timer(cmd, cmd->conn);
+			spin_unlock_bh(&cmd->dataout_timeout_lock);
+		}
+		return transport_handle_cdb_direct(&cmd->se_cmd);
+
+	case ISCSI_OP_NOOP_OUT:
+	case ISCSI_OP_TEXT:
+		spin_unlock_bh(&cmd->istate_lock);
+		iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+		break;
+	case ISCSI_OP_SCSI_TMFUNC:
+		if (se_cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION) {
+			spin_unlock_bh(&cmd->istate_lock);
+			iscsit_add_cmd_to_response_queue(cmd, cmd->conn,
+					cmd->i_state);
+			return 0;
+		}
+		spin_unlock_bh(&cmd->istate_lock);
+
+		return transport_generic_handle_tmr(&cmd->se_cmd);
+	case ISCSI_OP_LOGOUT:
+		spin_unlock_bh(&cmd->istate_lock);
+		switch (cmd->logout_reason) {
+		case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+			lr = iscsit_logout_closesession(cmd, cmd->conn);
+			break;
+		case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+			lr = iscsit_logout_closeconnection(cmd, cmd->conn);
+			break;
+		case ISCSI_LOGOUT_REASON_RECOVERY:
+			lr = iscsit_logout_removeconnforrecovery(cmd, cmd->conn);
+			break;
+		default:
+			pr_err("Unknown iSCSI Logout Request Code:"
+				" 0x%02x\n", cmd->logout_reason);
+			return -1;
+		}
+
+		return lr;
+	default:
+		spin_unlock_bh(&cmd->istate_lock);
+		pr_err("Cannot perform out of order execution for"
+		" unknown iSCSI Opcode: 0x%02x\n", cmd->iscsi_opcode);
+		return -1;
+	}
+
+	return 0;
+}
+
+void iscsit_free_all_ooo_cmdsns(struct iscsi_session *sess)
+{
+	struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+
+	mutex_lock(&sess->cmdsn_mutex);
+	list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+			&sess->sess_ooo_cmdsn_list, ooo_list) {
+
+		list_del(&ooo_cmdsn->ooo_list);
+		kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+	}
+	mutex_unlock(&sess->cmdsn_mutex);
+}
+
+int iscsit_handle_ooo_cmdsn(
+	struct iscsi_session *sess,
+	struct iscsi_cmd *cmd,
+	u32 cmdsn)
+{
+	int batch = 0;
+	struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL, *ooo_tail = NULL;
+
+	cmd->deferred_i_state		= cmd->i_state;
+	cmd->i_state			= ISTATE_DEFERRED_CMD;
+	cmd->cmd_flags			|= ICF_OOO_CMDSN;
+
+	if (list_empty(&sess->sess_ooo_cmdsn_list))
+		batch = 1;
+	else {
+		ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev,
+				typeof(*ooo_tail), ooo_list);
+		if (ooo_tail->cmdsn != (cmdsn - 1))
+			batch = 1;
+	}
+
+	ooo_cmdsn = iscsit_allocate_ooo_cmdsn();
+	if (!ooo_cmdsn)
+		return CMDSN_ERROR_CANNOT_RECOVER;
+
+	ooo_cmdsn->cmd			= cmd;
+	ooo_cmdsn->batch_count		= (batch) ?
+					  (cmdsn - sess->exp_cmd_sn) : 1;
+	ooo_cmdsn->cid			= cmd->conn->cid;
+	ooo_cmdsn->exp_cmdsn		= sess->exp_cmd_sn;
+	ooo_cmdsn->cmdsn		= cmdsn;
+
+	if (iscsit_attach_ooo_cmdsn(sess, ooo_cmdsn) < 0) {
+		kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+		return CMDSN_ERROR_CANNOT_RECOVER;
+	}
+
+	return CMDSN_HIGHER_THAN_EXP;
+}
+
+static int iscsit_set_dataout_timeout_values(
+	struct iscsi_cmd *cmd,
+	u32 *offset,
+	u32 *length)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_r2t *r2t;
+
+	if (cmd->unsolicited_data) {
+		*offset = 0;
+		*length = (conn->sess->sess_ops->FirstBurstLength >
+			   cmd->data_length) ?
+			   cmd->data_length :
+			   conn->sess->sess_ops->FirstBurstLength;
+		return 0;
+	}
+
+	spin_lock_bh(&cmd->r2t_lock);
+	if (list_empty(&cmd->cmd_r2t_list)) {
+		pr_err("cmd->cmd_r2t_list is empty!\n");
+		spin_unlock_bh(&cmd->r2t_lock);
+		return -1;
+	}
+
+	list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+		if (r2t->sent_r2t && !r2t->recovery_r2t && !r2t->seq_complete) {
+			*offset = r2t->offset;
+			*length = r2t->xfer_len;
+			spin_unlock_bh(&cmd->r2t_lock);
+			return 0;
+		}
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	pr_err("Unable to locate any incomplete DataOUT"
+		" sequences for ITT: 0x%08x.\n", cmd->init_task_tag);
+
+	return -1;
+}
+
+/*
+ *	NOTE: Called from interrupt (timer) context.
+ */
+static void iscsit_handle_dataout_timeout(unsigned long data)
+{
+	u32 pdu_length = 0, pdu_offset = 0;
+	u32 r2t_length = 0, r2t_offset = 0;
+	struct iscsi_cmd *cmd = (struct iscsi_cmd *) data;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_session *sess = NULL;
+	struct iscsi_node_attrib *na;
+
+	iscsit_inc_conn_usage_count(conn);
+
+	spin_lock_bh(&cmd->dataout_timeout_lock);
+	if (cmd->dataout_timer_flags & ISCSI_TF_STOP) {
+		spin_unlock_bh(&cmd->dataout_timeout_lock);
+		iscsit_dec_conn_usage_count(conn);
+		return;
+	}
+	cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING;
+	sess = conn->sess;
+	na = iscsit_tpg_get_node_attrib(sess);
+
+	if (!sess->sess_ops->ErrorRecoveryLevel) {
+		pr_debug("Unable to recover from DataOut timeout while"
+			" in ERL=0.\n");
+		goto failure;
+	}
+
+	if (++cmd->dataout_timeout_retries == na->dataout_timeout_retries) {
+		pr_debug("Command ITT: 0x%08x exceeded max retries"
+			" for DataOUT timeout %u, closing iSCSI connection.\n",
+			cmd->init_task_tag, na->dataout_timeout_retries);
+		goto failure;
+	}
+
+	cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY;
+
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		if (conn->sess->sess_ops->DataPDUInOrder) {
+			pdu_offset = cmd->write_data_done;
+			if ((pdu_offset + (conn->sess->sess_ops->MaxBurstLength -
+			     cmd->next_burst_len)) > cmd->data_length)
+				pdu_length = (cmd->data_length -
+					cmd->write_data_done);
+			else
+				pdu_length = (conn->sess->sess_ops->MaxBurstLength -
+						cmd->next_burst_len);
+		} else {
+			pdu_offset = cmd->seq_start_offset;
+			pdu_length = (cmd->seq_end_offset -
+				cmd->seq_start_offset);
+		}
+	} else {
+		if (iscsit_set_dataout_timeout_values(cmd, &pdu_offset,
+				&pdu_length) < 0)
+			goto failure;
+	}
+
+	if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length,
+			&r2t_offset, &r2t_length) < 0)
+		goto failure;
+
+	pr_debug("Command ITT: 0x%08x timed out waiting for"
+		" completion of %sDataOUT Sequence Offset: %u, Length: %u\n",
+		cmd->init_task_tag, (cmd->unsolicited_data) ? "Unsolicited " :
+		"", r2t_offset, r2t_length);
+
+	if (iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length) < 0)
+		goto failure;
+
+	iscsit_start_dataout_timer(cmd, conn);
+	spin_unlock_bh(&cmd->dataout_timeout_lock);
+	iscsit_dec_conn_usage_count(conn);
+
+	return;
+
+failure:
+	spin_unlock_bh(&cmd->dataout_timeout_lock);
+	iscsit_cause_connection_reinstatement(conn, 0);
+	iscsit_dec_conn_usage_count(conn);
+}
+
+void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess);
+
+	spin_lock_bh(&cmd->dataout_timeout_lock);
+	if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&cmd->dataout_timeout_lock);
+		return;
+	}
+
+	mod_timer(&cmd->dataout_timer,
+		(get_jiffies_64() + na->dataout_timeout * HZ));
+	pr_debug("Updated DataOUT timer for ITT: 0x%08x",
+			cmd->init_task_tag);
+	spin_unlock_bh(&cmd->dataout_timeout_lock);
+}
+
+/*
+ *	Called with cmd->dataout_timeout_lock held.
+ */
+void iscsit_start_dataout_timer(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess);
+
+	if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING)
+		return;
+
+	pr_debug("Starting DataOUT timer for ITT: 0x%08x on"
+		" CID: %hu.\n", cmd->init_task_tag, conn->cid);
+
+	init_timer(&cmd->dataout_timer);
+	cmd->dataout_timer.expires = (get_jiffies_64() + na->dataout_timeout * HZ);
+	cmd->dataout_timer.data = (unsigned long)cmd;
+	cmd->dataout_timer.function = iscsit_handle_dataout_timeout;
+	cmd->dataout_timer_flags &= ~ISCSI_TF_STOP;
+	cmd->dataout_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&cmd->dataout_timer);
+}
+
+void iscsit_stop_dataout_timer(struct iscsi_cmd *cmd)
+{
+	spin_lock_bh(&cmd->dataout_timeout_lock);
+	if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&cmd->dataout_timeout_lock);
+		return;
+	}
+	cmd->dataout_timer_flags |= ISCSI_TF_STOP;
+	spin_unlock_bh(&cmd->dataout_timeout_lock);
+
+	del_timer_sync(&cmd->dataout_timer);
+
+	spin_lock_bh(&cmd->dataout_timeout_lock);
+	cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING;
+	pr_debug("Stopped DataOUT Timer for ITT: 0x%08x\n",
+			cmd->init_task_tag);
+	spin_unlock_bh(&cmd->dataout_timeout_lock);
+}
diff --git a/drivers/target/iscsi/iscsi_target_erl1.h b/drivers/target/iscsi/iscsi_target_erl1.h
new file mode 100644
index 0000000..85e67e2
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl1.h
@@ -0,0 +1,26 @@
+#ifndef ISCSI_TARGET_ERL1_H
+#define ISCSI_TARGET_ERL1_H
+
+extern int iscsit_dump_data_payload(struct iscsi_conn *, u32, int);
+extern int iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+			struct iscsi_cmd *, struct iscsi_datain_req *);
+extern int iscsit_create_recovery_datain_values_datasequenceinorder_no(
+			struct iscsi_cmd *, struct iscsi_datain_req *);
+extern int iscsit_handle_recovery_datain_or_r2t(struct iscsi_conn *, unsigned char *,
+			u32, u32, u32, u32);
+extern int iscsit_handle_status_snack(struct iscsi_conn *, u32, u32,
+			u32, u32);
+extern int iscsit_handle_data_ack(struct iscsi_conn *, u32, u32, u32);
+extern int iscsit_dataout_datapduinorder_no_fbit(struct iscsi_cmd *, struct iscsi_pdu *);
+extern int iscsit_recover_dataout_sequence(struct iscsi_cmd *, u32, u32);
+extern void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *);
+extern void iscsit_free_all_ooo_cmdsns(struct iscsi_session *);
+extern int iscsit_execute_ooo_cmdsns(struct iscsi_session *);
+extern int iscsit_execute_cmd(struct iscsi_cmd *, int);
+extern int iscsit_handle_ooo_cmdsn(struct iscsi_session *, struct iscsi_cmd *, u32);
+extern void iscsit_remove_ooo_cmdsn(struct iscsi_session *, struct iscsi_ooo_cmdsn *);
+extern void iscsit_mod_dataout_timer(struct iscsi_cmd *);
+extern void iscsit_start_dataout_timer(struct iscsi_cmd *, struct iscsi_conn *);
+extern void iscsit_stop_dataout_timer(struct iscsi_cmd *);
+
+#endif /* ISCSI_TARGET_ERL1_H */
diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c
new file mode 100644
index 0000000..91a4d17
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl2.c
@@ -0,0 +1,474 @@
+/*******************************************************************************
+ * This file contains error recovery level two functions used by
+ * the iSCSI Target driver.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target.h"
+
+/*
+ *	FIXME: Does RData SNACK apply here as well?
+ */
+void iscsit_create_conn_recovery_datain_values(
+	struct iscsi_cmd *cmd,
+	u32 exp_data_sn)
+{
+	u32 data_sn = 0;
+	struct iscsi_conn *conn = cmd->conn;
+
+	cmd->next_burst_len = 0;
+	cmd->read_data_done = 0;
+
+	while (exp_data_sn > data_sn) {
+		if ((cmd->next_burst_len +
+		     conn->conn_ops->MaxRecvDataSegmentLength) <
+		     conn->sess->sess_ops->MaxBurstLength) {
+			cmd->read_data_done +=
+			       conn->conn_ops->MaxRecvDataSegmentLength;
+			cmd->next_burst_len +=
+			       conn->conn_ops->MaxRecvDataSegmentLength;
+		} else {
+			cmd->read_data_done +=
+				(conn->sess->sess_ops->MaxBurstLength -
+				cmd->next_burst_len);
+			cmd->next_burst_len = 0;
+		}
+		data_sn++;
+	}
+}
+
+void iscsit_create_conn_recovery_dataout_values(
+	struct iscsi_cmd *cmd)
+{
+	u32 write_data_done = 0;
+	struct iscsi_conn *conn = cmd->conn;
+
+	cmd->data_sn = 0;
+	cmd->next_burst_len = 0;
+
+	while (cmd->write_data_done > write_data_done) {
+		if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <=
+		     cmd->write_data_done)
+			write_data_done += conn->sess->sess_ops->MaxBurstLength;
+		else
+			break;
+	}
+
+	cmd->write_data_done = write_data_done;
+}
+
+static int iscsit_attach_active_connection_recovery_entry(
+	struct iscsi_session *sess,
+	struct iscsi_conn_recovery *cr)
+{
+	spin_lock(&sess->cr_a_lock);
+	list_add_tail(&cr->cr_list, &sess->cr_active_list);
+	spin_unlock(&sess->cr_a_lock);
+
+	return 0;
+}
+
+static int iscsit_attach_inactive_connection_recovery_entry(
+	struct iscsi_session *sess,
+	struct iscsi_conn_recovery *cr)
+{
+	spin_lock(&sess->cr_i_lock);
+	list_add_tail(&cr->cr_list, &sess->cr_inactive_list);
+
+	sess->conn_recovery_count++;
+	pr_debug("Incremented connection recovery count to %u for"
+		" SID: %u\n", sess->conn_recovery_count, sess->sid);
+	spin_unlock(&sess->cr_i_lock);
+
+	return 0;
+}
+
+struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
+	struct iscsi_session *sess,
+	u16 cid)
+{
+	struct iscsi_conn_recovery *cr;
+
+	spin_lock(&sess->cr_i_lock);
+	list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
+		if (cr->cid == cid) {
+			spin_unlock(&sess->cr_i_lock);
+			return cr;
+		}
+	}
+	spin_unlock(&sess->cr_i_lock);
+
+	return NULL;
+}
+
+void iscsit_free_connection_recovery_entires(struct iscsi_session *sess)
+{
+	struct iscsi_cmd *cmd, *cmd_tmp;
+	struct iscsi_conn_recovery *cr, *cr_tmp;
+
+	spin_lock(&sess->cr_a_lock);
+	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) {
+		list_del(&cr->cr_list);
+		spin_unlock(&sess->cr_a_lock);
+
+		spin_lock(&cr->conn_recovery_cmd_lock);
+		list_for_each_entry_safe(cmd, cmd_tmp,
+				&cr->conn_recovery_cmd_list, i_list) {
+
+			list_del(&cmd->i_list);
+			cmd->conn = NULL;
+			spin_unlock(&cr->conn_recovery_cmd_lock);
+			if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+			    !(cmd->se_cmd.transport_wait_for_tasks))
+				iscsit_release_cmd(cmd);
+			else
+				cmd->se_cmd.transport_wait_for_tasks(
+						&cmd->se_cmd, 1, 1);
+			spin_lock(&cr->conn_recovery_cmd_lock);
+		}
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+		spin_lock(&sess->cr_a_lock);
+
+		kfree(cr);
+	}
+	spin_unlock(&sess->cr_a_lock);
+
+	spin_lock(&sess->cr_i_lock);
+	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) {
+		list_del(&cr->cr_list);
+		spin_unlock(&sess->cr_i_lock);
+
+		spin_lock(&cr->conn_recovery_cmd_lock);
+		list_for_each_entry_safe(cmd, cmd_tmp,
+				&cr->conn_recovery_cmd_list, i_list) {
+
+			list_del(&cmd->i_list);
+			cmd->conn = NULL;
+			spin_unlock(&cr->conn_recovery_cmd_lock);
+			if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+			    !(cmd->se_cmd.transport_wait_for_tasks))
+				iscsit_release_cmd(cmd);
+			else
+				cmd->se_cmd.transport_wait_for_tasks(
+						&cmd->se_cmd, 1, 1);
+			spin_lock(&cr->conn_recovery_cmd_lock);
+		}
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+		spin_lock(&sess->cr_i_lock);
+
+		kfree(cr);
+	}
+	spin_unlock(&sess->cr_i_lock);
+}
+
+int iscsit_remove_active_connection_recovery_entry(
+	struct iscsi_conn_recovery *cr,
+	struct iscsi_session *sess)
+{
+	spin_lock(&sess->cr_a_lock);
+	list_del(&cr->cr_list);
+
+	sess->conn_recovery_count--;
+	pr_debug("Decremented connection recovery count to %u for"
+		" SID: %u\n", sess->conn_recovery_count, sess->sid);
+	spin_unlock(&sess->cr_a_lock);
+
+	kfree(cr);
+
+	return 0;
+}
+
+int iscsit_remove_inactive_connection_recovery_entry(
+	struct iscsi_conn_recovery *cr,
+	struct iscsi_session *sess)
+{
+	spin_lock(&sess->cr_i_lock);
+	list_del(&cr->cr_list);
+	spin_unlock(&sess->cr_i_lock);
+
+	return 0;
+}
+
+/*
+ *	Called with cr->conn_recovery_cmd_lock help.
+ */
+int iscsit_remove_cmd_from_connection_recovery(
+	struct iscsi_cmd *cmd,
+	struct iscsi_session *sess)
+{
+	struct iscsi_conn_recovery *cr;
+
+	if (!cmd->cr) {
+		pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+			" is NULL!\n", cmd->init_task_tag);
+		BUG();
+	}
+	cr = cmd->cr;
+
+	list_del(&cmd->i_list);
+	return --cr->cmd_count;
+}
+
+void iscsit_discard_cr_cmds_by_expstatsn(
+	struct iscsi_conn_recovery *cr,
+	u32 exp_statsn)
+{
+	u32 dropped_count = 0;
+	struct iscsi_cmd *cmd, *cmd_tmp;
+	struct iscsi_session *sess = cr->sess;
+
+	spin_lock(&cr->conn_recovery_cmd_lock);
+	list_for_each_entry_safe(cmd, cmd_tmp,
+			&cr->conn_recovery_cmd_list, i_list) {
+
+		if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
+		     (cmd->deferred_i_state != ISTATE_REMOVE)) ||
+		     (cmd->stat_sn >= exp_statsn)) {
+			continue;
+		}
+
+		dropped_count++;
+		pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
+			" 0x%08x, CID: %hu.\n", cmd->init_task_tag,
+				cmd->stat_sn, cr->cid);
+
+		iscsit_remove_cmd_from_connection_recovery(cmd, sess);
+
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+		if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+		    !(cmd->se_cmd.transport_wait_for_tasks))
+			iscsit_release_cmd(cmd);
+		else
+			cmd->se_cmd.transport_wait_for_tasks(
+					&cmd->se_cmd, 1, 0);
+		spin_lock(&cr->conn_recovery_cmd_lock);
+	}
+	spin_unlock(&cr->conn_recovery_cmd_lock);
+
+	pr_debug("Dropped %u total acknowledged commands on"
+		" CID: %hu less than old ExpStatSN: 0x%08x\n",
+			dropped_count, cr->cid, exp_statsn);
+
+	if (!cr->cmd_count) {
+		pr_debug("No commands to be reassigned for failed"
+			" connection CID: %hu on SID: %u\n",
+			cr->cid, sess->sid);
+		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
+		iscsit_attach_active_connection_recovery_entry(sess, cr);
+		pr_debug("iSCSI connection recovery successful for CID:"
+			" %hu on SID: %u\n", cr->cid, sess->sid);
+		iscsit_remove_active_connection_recovery_entry(cr, sess);
+	} else {
+		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
+		iscsit_attach_active_connection_recovery_entry(sess, cr);
+	}
+}
+
+int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
+{
+	u32 dropped_count = 0;
+	struct iscsi_cmd *cmd, *cmd_tmp;
+	struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+	struct iscsi_session *sess = conn->sess;
+
+	mutex_lock(&sess->cmdsn_mutex);
+	list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+			&sess->sess_ooo_cmdsn_list, ooo_list) {
+
+		if (ooo_cmdsn->cid != conn->cid)
+			continue;
+
+		dropped_count++;
+		pr_debug("Dropping unacknowledged CmdSN:"
+		" 0x%08x during connection recovery on CID: %hu\n",
+			ooo_cmdsn->cmdsn, conn->cid);
+		iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+	}
+	mutex_unlock(&sess->cmdsn_mutex);
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) {
+		if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
+			continue;
+
+		list_del(&cmd->i_list);
+
+		spin_unlock_bh(&conn->cmd_lock);
+		if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+		    !(cmd->se_cmd.transport_wait_for_tasks))
+			iscsit_release_cmd(cmd);
+		else
+			cmd->se_cmd.transport_wait_for_tasks(
+					&cmd->se_cmd, 1, 1);
+		spin_lock_bh(&conn->cmd_lock);
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+
+	pr_debug("Dropped %u total unacknowledged commands on CID:"
+		" %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
+				sess->exp_cmd_sn);
+	return 0;
+}
+
+int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn)
+{
+	u32 cmd_count = 0;
+	struct iscsi_cmd *cmd, *cmd_tmp;
+	struct iscsi_conn_recovery *cr;
+
+	/*
+	 * Allocate an struct iscsi_conn_recovery for this connection.
+	 * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
+	 * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
+	 * connection's command list for connection recovery.
+	 */
+	cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
+	if (!cr) {
+		pr_err("Unable to allocate memory for"
+			" struct iscsi_conn_recovery.\n");
+		return -1;
+	}
+	INIT_LIST_HEAD(&cr->cr_list);
+	INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
+	spin_lock_init(&cr->conn_recovery_cmd_lock);
+	/*
+	 * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
+	 * ISCSI_OP_NOOP_OUT opcodes.  For all other opcodes call
+	 * list_del(&cmd->i_list); to release the command to the
+	 * session pool and remove it from the connection's list.
+	 *
+	 * Also stop the DataOUT timer, which will be restarted after
+	 * sending the TMR response.
+	 */
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) {
+
+		if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
+		    (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
+			pr_debug("Not performing realligence on"
+				" Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
+				" CID: %hu\n", cmd->iscsi_opcode,
+				cmd->init_task_tag, cmd->cmd_sn, conn->cid);
+
+			list_del(&cmd->i_list);
+			spin_unlock_bh(&conn->cmd_lock);
+
+			if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+			    !(cmd->se_cmd.transport_wait_for_tasks))
+				iscsit_release_cmd(cmd);
+			else
+				cmd->se_cmd.transport_wait_for_tasks(
+						&cmd->se_cmd, 1, 0);
+			spin_lock_bh(&conn->cmd_lock);
+			continue;
+		}
+
+		/*
+		 * Special case where commands greater than or equal to
+		 * the session's ExpCmdSN are attached to the connection
+		 * list but not to the out of order CmdSN list.  The one
+		 * obvious case is when a command with immediate data
+		 * attached must only check the CmdSN against ExpCmdSN
+		 * after the data is received.  The special case below
+		 * is when the connection fails before data is received,
+		 * but also may apply to other PDUs, so it has been
+		 * made generic here.
+		 */
+		if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
+		     (cmd->cmd_sn >= conn->sess->exp_cmd_sn)) {
+			list_del(&cmd->i_list);
+			spin_unlock_bh(&conn->cmd_lock);
+
+			if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
+			    !(cmd->se_cmd.transport_wait_for_tasks))
+				iscsit_release_cmd(cmd);
+			else
+				cmd->se_cmd.transport_wait_for_tasks(
+						&cmd->se_cmd, 1, 1);
+			spin_lock_bh(&conn->cmd_lock);
+			continue;
+		}
+
+		cmd_count++;
+		pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
+			" CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
+			" realligence.\n", cmd->iscsi_opcode,
+			cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
+			conn->cid);
+
+		cmd->deferred_i_state = cmd->i_state;
+		cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY;
+
+		if (cmd->data_direction == DMA_TO_DEVICE)
+			iscsit_stop_dataout_timer(cmd);
+
+		cmd->sess = conn->sess;
+
+		list_del(&cmd->i_list);
+		spin_unlock_bh(&conn->cmd_lock);
+
+		iscsit_free_all_datain_reqs(cmd);
+
+		if ((cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) &&
+		     cmd->se_cmd.transport_wait_for_tasks)
+			cmd->se_cmd.transport_wait_for_tasks(&cmd->se_cmd,
+					0, 0);
+		/*
+		 * Add the struct iscsi_cmd to the connection recovery cmd list
+		 */
+		spin_lock(&cr->conn_recovery_cmd_lock);
+		list_add_tail(&cmd->i_list, &cr->conn_recovery_cmd_list);
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+
+		spin_lock_bh(&conn->cmd_lock);
+		cmd->cr = cr;
+		cmd->conn = NULL;
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+	/*
+	 * Fill in the various values in the preallocated struct iscsi_conn_recovery.
+	 */
+	cr->cid = conn->cid;
+	cr->cmd_count = cmd_count;
+	cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
+	cr->sess = conn->sess;
+
+	iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
+
+	return 0;
+}
+
+int iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn)
+{
+	atomic_set(&conn->connection_recovery, 1);
+
+	if (iscsit_close_connection(conn) < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_erl2.h b/drivers/target/iscsi/iscsi_target_erl2.h
new file mode 100644
index 0000000..22f8d24
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl2.h
@@ -0,0 +1,18 @@
+#ifndef ISCSI_TARGET_ERL2_H
+#define ISCSI_TARGET_ERL2_H
+
+extern void iscsit_create_conn_recovery_datain_values(struct iscsi_cmd *, u32);
+extern void iscsit_create_conn_recovery_dataout_values(struct iscsi_cmd *);
+extern struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
+			struct iscsi_session *, u16);
+extern void iscsit_free_connection_recovery_entires(struct iscsi_session *);
+extern int iscsit_remove_active_connection_recovery_entry(
+			struct iscsi_conn_recovery *, struct iscsi_session *);
+extern int iscsit_remove_cmd_from_connection_recovery(struct iscsi_cmd *,
+			struct iscsi_session *);
+extern void iscsit_discard_cr_cmds_by_expstatsn(struct iscsi_conn_recovery *, u32);
+extern int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *);
+extern int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *);
+extern int iscsit_connection_recovery_transport_reset(struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_ERL2_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
new file mode 100644
index 0000000..bcaf82f
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -0,0 +1,1232 @@
+/*******************************************************************************
+ * This file contains the login functions used by the iSCSI Target driver.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/string.h>
+#include <linux/kthread.h>
+#include <linux/crypto.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_stat.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_parameters.h"
+
+extern struct idr sess_idr;
+extern struct mutex auth_id_lock;
+extern spinlock_t sess_idr_lock;
+
+static int iscsi_login_init_conn(struct iscsi_conn *conn)
+{
+	INIT_LIST_HEAD(&conn->conn_list);
+	INIT_LIST_HEAD(&conn->conn_cmd_list);
+	INIT_LIST_HEAD(&conn->immed_queue_list);
+	INIT_LIST_HEAD(&conn->response_queue_list);
+	init_completion(&conn->conn_post_wait_comp);
+	init_completion(&conn->conn_wait_comp);
+	init_completion(&conn->conn_wait_rcfr_comp);
+	init_completion(&conn->conn_waiting_on_uc_comp);
+	init_completion(&conn->conn_logout_comp);
+	init_completion(&conn->rx_half_close_comp);
+	init_completion(&conn->tx_half_close_comp);
+	spin_lock_init(&conn->cmd_lock);
+	spin_lock_init(&conn->conn_usage_lock);
+	spin_lock_init(&conn->immed_queue_lock);
+	spin_lock_init(&conn->nopin_timer_lock);
+	spin_lock_init(&conn->response_queue_lock);
+	spin_lock_init(&conn->state_lock);
+
+	if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) {
+		pr_err("Unable to allocate conn->conn_cpumask\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Used by iscsi_target_nego.c:iscsi_target_locate_portal() to setup
+ * per struct iscsi_conn libcrypto contexts for crc32c and crc32-intel
+ */
+int iscsi_login_setup_crypto(struct iscsi_conn *conn)
+{
+	/*
+	 * Setup slicing by CRC32C algorithm for RX and TX libcrypto contexts
+	 * which will default to crc32c_intel.ko for cpu_has_xmm4_2, or fallback
+	 * to software 1x8 byte slicing from crc32c.ko
+	 */
+	conn->conn_rx_hash.flags = 0;
+	conn->conn_rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+						CRYPTO_ALG_ASYNC);
+	if (IS_ERR(conn->conn_rx_hash.tfm)) {
+		pr_err("crypto_alloc_hash() failed for conn_rx_tfm\n");
+		return -ENOMEM;
+	}
+
+	conn->conn_tx_hash.flags = 0;
+	conn->conn_tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+						CRYPTO_ALG_ASYNC);
+	if (IS_ERR(conn->conn_tx_hash.tfm)) {
+		pr_err("crypto_alloc_hash() failed for conn_tx_tfm\n");
+		crypto_free_hash(conn->conn_rx_hash.tfm);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int iscsi_login_check_initiator_version(
+	struct iscsi_conn *conn,
+	u8 version_max,
+	u8 version_min)
+{
+	if ((version_max != 0x00) || (version_min != 0x00)) {
+		pr_err("Unsupported iSCSI IETF Pre-RFC Revision,"
+			" version Min/Max 0x%02x/0x%02x, rejecting login.\n",
+			version_min, version_max);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_NO_VERSION);
+		return -1;
+	}
+
+	return 0;
+}
+
+int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
+{
+	int sessiontype;
+	struct iscsi_param *initiatorname_param = NULL, *sessiontype_param = NULL;
+	struct iscsi_portal_group *tpg = conn->tpg;
+	struct iscsi_session *sess = NULL, *sess_p = NULL;
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+	struct se_session *se_sess, *se_sess_tmp;
+
+	initiatorname_param = iscsi_find_param_from_key(
+			INITIATORNAME, conn->param_list);
+	if (!initiatorname_param)
+		return -1;
+
+	sessiontype_param = iscsi_find_param_from_key(
+			SESSIONTYPE, conn->param_list);
+	if (!sessiontype_param)
+		return -1;
+
+	sessiontype = (strncmp(sessiontype_param->value, NORMAL, 6)) ? 1 : 0;
+
+	spin_lock_bh(&se_tpg->session_lock);
+	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+			sess_list) {
+
+		sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		spin_lock(&sess_p->conn_lock);
+		if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
+		    atomic_read(&sess_p->session_logout) ||
+		    (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+			spin_unlock(&sess_p->conn_lock);
+			continue;
+		}
+		if (!memcmp((void *)sess_p->isid, (void *)conn->sess->isid, 6) &&
+		   (!strcmp((void *)sess_p->sess_ops->InitiatorName,
+			    (void *)initiatorname_param->value) &&
+		   (sess_p->sess_ops->SessionType == sessiontype))) {
+			atomic_set(&sess_p->session_reinstatement, 1);
+			spin_unlock(&sess_p->conn_lock);
+			iscsit_inc_session_usage_count(sess_p);
+			iscsit_stop_time2retain_timer(sess_p);
+			sess = sess_p;
+			break;
+		}
+		spin_unlock(&sess_p->conn_lock);
+	}
+	spin_unlock_bh(&se_tpg->session_lock);
+	/*
+	 * If the Time2Retain handler has expired, the session is already gone.
+	 */
+	if (!sess)
+		return 0;
+
+	pr_debug("%s iSCSI Session SID %u is still active for %s,"
+		" preforming session reinstatement.\n", (sessiontype) ?
+		"Discovery" : "Normal", sess->sid,
+		sess->sess_ops->InitiatorName);
+
+	spin_lock_bh(&sess->conn_lock);
+	if (sess->session_state == TARG_SESS_STATE_FAILED) {
+		spin_unlock_bh(&sess->conn_lock);
+		iscsit_dec_session_usage_count(sess);
+		return iscsit_close_session(sess);
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	iscsit_stop_session(sess, 1, 1);
+	iscsit_dec_session_usage_count(sess);
+
+	return iscsit_close_session(sess);
+}
+
+static void iscsi_login_set_conn_values(
+	struct iscsi_session *sess,
+	struct iscsi_conn *conn,
+	u16 cid)
+{
+	conn->sess		= sess;
+	conn->cid		= cid;
+	/*
+	 * Generate a random Status sequence number (statsn) for the new
+	 * iSCSI connection.
+	 */
+	get_random_bytes(&conn->stat_sn, sizeof(u32));
+
+	mutex_lock(&auth_id_lock);
+	conn->auth_id		= iscsit_global->auth_id++;
+	mutex_unlock(&auth_id_lock);
+}
+
+/*
+ *	This is the leading connection of a new session,
+ *	or session reinstatement.
+ */
+static int iscsi_login_zero_tsih_s1(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	struct iscsi_session *sess = NULL;
+	struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+
+	sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);
+	if (!sess) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		pr_err("Could not allocate memory for session\n");
+		return -1;
+	}
+
+	iscsi_login_set_conn_values(sess, conn, pdu->cid);
+	sess->init_task_tag	= pdu->itt;
+	memcpy((void *)&sess->isid, (void *)pdu->isid, 6);
+	sess->exp_cmd_sn	= pdu->cmdsn;
+	INIT_LIST_HEAD(&sess->sess_conn_list);
+	INIT_LIST_HEAD(&sess->sess_ooo_cmdsn_list);
+	INIT_LIST_HEAD(&sess->cr_active_list);
+	INIT_LIST_HEAD(&sess->cr_inactive_list);
+	init_completion(&sess->async_msg_comp);
+	init_completion(&sess->reinstatement_comp);
+	init_completion(&sess->session_wait_comp);
+	init_completion(&sess->session_waiting_on_uc_comp);
+	mutex_init(&sess->cmdsn_mutex);
+	spin_lock_init(&sess->conn_lock);
+	spin_lock_init(&sess->cr_a_lock);
+	spin_lock_init(&sess->cr_i_lock);
+	spin_lock_init(&sess->session_usage_lock);
+	spin_lock_init(&sess->ttt_lock);
+
+	if (!idr_pre_get(&sess_idr, GFP_KERNEL)) {
+		pr_err("idr_pre_get() for sess_idr failed\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+	spin_lock(&sess_idr_lock);
+	idr_get_new(&sess_idr, NULL, &sess->session_index);
+	spin_unlock(&sess_idr_lock);
+
+	sess->creation_time = get_jiffies_64();
+	spin_lock_init(&sess->session_stats_lock);
+	/*
+	 * The FFP CmdSN window values will be allocated from the TPG's
+	 * Initiator Node's ACL once the login has been successfully completed.
+	 */
+	sess->max_cmd_sn	= pdu->cmdsn;
+
+	sess->sess_ops = kzalloc(sizeof(struct iscsi_sess_ops), GFP_KERNEL);
+	if (!sess->sess_ops) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_sess_ops.\n");
+		return -1;
+	}
+
+	sess->se_sess = transport_init_session();
+	if (!sess->se_sess) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int iscsi_login_zero_tsih_s2(
+	struct iscsi_conn *conn)
+{
+	struct iscsi_node_attrib *na;
+	struct iscsi_session *sess = conn->sess;
+	unsigned char buf[32];
+
+	sess->tpg = conn->tpg;
+
+	/*
+	 * Assign a new TPG Session Handle.  Note this is protected with
+	 * struct iscsi_portal_group->np_login_sem from iscsit_access_np().
+	 */
+	sess->tsih = ++ISCSI_TPG_S(sess)->ntsih;
+	if (!sess->tsih)
+		sess->tsih = ++ISCSI_TPG_S(sess)->ntsih;
+
+	/*
+	 * Create the default params from user defined values..
+	 */
+	if (iscsi_copy_param_list(&conn->param_list,
+				ISCSI_TPG_C(conn)->param_list, 1) < 0) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	iscsi_set_keys_to_negotiate(0, conn->param_list);
+
+	if (sess->sess_ops->SessionType)
+		return iscsi_set_keys_irrelevant_for_discovery(
+				conn->param_list);
+
+	na = iscsit_tpg_get_node_attrib(sess);
+
+	/*
+	 * Need to send TargetPortalGroupTag back in first login response
+	 * on any iSCSI connection where the Initiator provides TargetName.
+	 * See 5.3.1.  Login Phase Start
+	 *
+	 * In our case, we have already located the struct iscsi_tiqn at this point.
+	 */
+	memset(buf, 0, 32);
+	sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt);
+	if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	/*
+	 * Workaround for Initiators that have broken connection recovery logic.
+	 *
+	 * "We would really like to get rid of this." Linux-iSCSI.org team
+	 */
+	memset(buf, 0, 32);
+	sprintf(buf, "ErrorRecoveryLevel=%d", na->default_erl);
+	if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Remove PSTATE_NEGOTIATE for the four FIM related keys.
+ * The Initiator node will be able to enable FIM by proposing them itself.
+ */
+int iscsi_login_disable_FIM_keys(
+	struct iscsi_param_list *param_list,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_param *param;
+
+	param = iscsi_find_param_from_key("OFMarker", param_list);
+	if (!param) {
+		pr_err("iscsi_find_param_from_key() for"
+				" OFMarker failed\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+	param->state &= ~PSTATE_NEGOTIATE;
+
+	param = iscsi_find_param_from_key("OFMarkInt", param_list);
+	if (!param) {
+		pr_err("iscsi_find_param_from_key() for"
+				" IFMarker failed\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+	param->state &= ~PSTATE_NEGOTIATE;
+
+	param = iscsi_find_param_from_key("IFMarker", param_list);
+	if (!param) {
+		pr_err("iscsi_find_param_from_key() for"
+				" IFMarker failed\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+	param->state &= ~PSTATE_NEGOTIATE;
+
+	param = iscsi_find_param_from_key("IFMarkInt", param_list);
+	if (!param) {
+		pr_err("iscsi_find_param_from_key() for"
+				" IFMarker failed\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+	param->state &= ~PSTATE_NEGOTIATE;
+
+	return 0;
+}
+
+static int iscsi_login_non_zero_tsih_s1(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+
+	iscsi_login_set_conn_values(NULL, conn, pdu->cid);
+	return 0;
+}
+
+/*
+ *	Add a new connection to an existing session.
+ */
+static int iscsi_login_non_zero_tsih_s2(
+	struct iscsi_conn *conn,
+	unsigned char *buf)
+{
+	struct iscsi_portal_group *tpg = conn->tpg;
+	struct iscsi_session *sess = NULL, *sess_p = NULL;
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+	struct se_session *se_sess, *se_sess_tmp;
+	struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+
+	spin_lock_bh(&se_tpg->session_lock);
+	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+			sess_list) {
+
+		sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
+		    atomic_read(&sess_p->session_logout) ||
+		   (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED))
+			continue;
+		if (!memcmp((const void *)sess_p->isid,
+		     (const void *)pdu->isid, 6) &&
+		     (sess_p->tsih == pdu->tsih)) {
+			iscsit_inc_session_usage_count(sess_p);
+			iscsit_stop_time2retain_timer(sess_p);
+			sess = sess_p;
+			break;
+		}
+	}
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	/*
+	 * If the Time2Retain handler has expired, the session is already gone.
+	 */
+	if (!sess) {
+		pr_err("Initiator attempting to add a connection to"
+			" a non-existent session, rejecting iSCSI Login.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_NO_SESSION);
+		return -1;
+	}
+
+	/*
+	 * Stop the Time2Retain timer if this is a failed session, we restart
+	 * the timer if the login is not successful.
+	 */
+	spin_lock_bh(&sess->conn_lock);
+	if (sess->session_state == TARG_SESS_STATE_FAILED)
+		atomic_set(&sess->session_continuation, 1);
+	spin_unlock_bh(&sess->conn_lock);
+
+	iscsi_login_set_conn_values(sess, conn, pdu->cid);
+
+	if (iscsi_copy_param_list(&conn->param_list,
+			ISCSI_TPG_C(conn)->param_list, 0) < 0) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	iscsi_set_keys_to_negotiate(0, conn->param_list);
+	/*
+	 * Need to send TargetPortalGroupTag back in first login response
+	 * on any iSCSI connection where the Initiator provides TargetName.
+	 * See 5.3.1.  Login Phase Start
+	 *
+	 * In our case, we have already located the struct iscsi_tiqn at this point.
+	 */
+	memset(buf, 0, 32);
+	sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt);
+	if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		return -1;
+	}
+
+	return iscsi_login_disable_FIM_keys(conn->param_list, conn);
+}
+
+int iscsi_login_post_auth_non_zero_tsih(
+	struct iscsi_conn *conn,
+	u16 cid,
+	u32 exp_statsn)
+{
+	struct iscsi_conn *conn_ptr = NULL;
+	struct iscsi_conn_recovery *cr = NULL;
+	struct iscsi_session *sess = conn->sess;
+
+	/*
+	 * By following item 5 in the login table,  if we have found
+	 * an existing ISID and a valid/existing TSIH and an existing
+	 * CID we do connection reinstatement.  Currently we dont not
+	 * support it so we send back an non-zero status class to the
+	 * initiator and release the new connection.
+	 */
+	conn_ptr = iscsit_get_conn_from_cid_rcfr(sess, cid);
+	if ((conn_ptr)) {
+		pr_err("Connection exists with CID %hu for %s,"
+			" performing connection reinstatement.\n",
+			conn_ptr->cid, sess->sess_ops->InitiatorName);
+
+		iscsit_connection_reinstatement_rcfr(conn_ptr);
+		iscsit_dec_conn_usage_count(conn_ptr);
+	}
+
+	/*
+	 * Check for any connection recovery entires containing CID.
+	 * We use the original ExpStatSN sent in the first login request
+	 * to acknowledge commands for the failed connection.
+	 *
+	 * Also note that an explict logout may have already been sent,
+	 * but the response may not be sent due to additional connection
+	 * loss.
+	 */
+	if (sess->sess_ops->ErrorRecoveryLevel == 2) {
+		cr = iscsit_get_inactive_connection_recovery_entry(
+				sess, cid);
+		if ((cr)) {
+			pr_debug("Performing implicit logout"
+				" for connection recovery on CID: %hu\n",
+					conn->cid);
+			iscsit_discard_cr_cmds_by_expstatsn(cr, exp_statsn);
+		}
+	}
+
+	/*
+	 * Else we follow item 4 from the login table in that we have
+	 * found an existing ISID and a valid/existing TSIH and a new
+	 * CID we go ahead and continue to add a new connection to the
+	 * session.
+	 */
+	pr_debug("Adding CID %hu to existing session for %s.\n",
+			cid, sess->sess_ops->InitiatorName);
+
+	if ((atomic_read(&sess->nconn) + 1) > sess->sess_ops->MaxConnections) {
+		pr_err("Adding additional connection to this session"
+			" would exceed MaxConnections %d, login failed.\n",
+				sess->sess_ops->MaxConnections);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_ISID_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void iscsi_post_login_start_timers(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+
+	if (!sess->sess_ops->SessionType)
+		iscsit_start_nopin_timer(conn);
+}
+
+static int iscsi_post_login_handler(
+	struct iscsi_np *np,
+	struct iscsi_conn *conn,
+	u8 zero_tsih)
+{
+	int stop_timer = 0;
+	struct iscsi_session *sess = conn->sess;
+	struct se_session *se_sess = sess->se_sess;
+	struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess);
+	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+	struct iscsi_thread_set *ts;
+
+	iscsit_inc_conn_usage_count(conn);
+
+	iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_SUCCESS,
+			ISCSI_LOGIN_STATUS_ACCEPT);
+
+	pr_debug("Moving to TARG_CONN_STATE_LOGGED_IN.\n");
+	conn->conn_state = TARG_CONN_STATE_LOGGED_IN;
+
+	iscsi_set_connection_parameters(conn->conn_ops, conn->param_list);
+	iscsit_set_sync_and_steering_values(conn);
+	/*
+	 * SCSI Initiator -> SCSI Target Port Mapping
+	 */
+	ts = iscsi_get_thread_set();
+	if (!zero_tsih) {
+		iscsi_set_session_parameters(sess->sess_ops,
+				conn->param_list, 0);
+		iscsi_release_param_list(conn->param_list);
+		conn->param_list = NULL;
+
+		spin_lock_bh(&sess->conn_lock);
+		atomic_set(&sess->session_continuation, 0);
+		if (sess->session_state == TARG_SESS_STATE_FAILED) {
+			pr_debug("Moving to"
+					" TARG_SESS_STATE_LOGGED_IN.\n");
+			sess->session_state = TARG_SESS_STATE_LOGGED_IN;
+			stop_timer = 1;
+		}
+
+		pr_debug("iSCSI Login successful on CID: %hu from %s to"
+			" %s:%hu,%hu\n", conn->cid, conn->login_ip, np->np_ip,
+				np->np_port, tpg->tpgt);
+
+		list_add_tail(&conn->conn_list, &sess->sess_conn_list);
+		atomic_inc(&sess->nconn);
+		pr_debug("Incremented iSCSI Connection count to %hu"
+			" from node: %s\n", atomic_read(&sess->nconn),
+			sess->sess_ops->InitiatorName);
+		spin_unlock_bh(&sess->conn_lock);
+
+		iscsi_post_login_start_timers(conn);
+		iscsi_activate_thread_set(conn, ts);
+		/*
+		 * Determine CPU mask to ensure connection's RX and TX kthreads
+		 * are scheduled on the same CPU.
+		 */
+		iscsit_thread_get_cpumask(conn);
+		conn->conn_rx_reset_cpumask = 1;
+		conn->conn_tx_reset_cpumask = 1;
+
+		iscsit_dec_conn_usage_count(conn);
+		if (stop_timer) {
+			spin_lock_bh(&se_tpg->session_lock);
+			iscsit_stop_time2retain_timer(sess);
+			spin_unlock_bh(&se_tpg->session_lock);
+		}
+		iscsit_dec_session_usage_count(sess);
+		return 0;
+	}
+
+	iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1);
+	iscsi_release_param_list(conn->param_list);
+	conn->param_list = NULL;
+
+	iscsit_determine_maxcmdsn(sess);
+
+	spin_lock_bh(&se_tpg->session_lock);
+	__transport_register_session(&sess->tpg->tpg_se_tpg,
+			se_sess->se_node_acl, se_sess, (void *)sess);
+	pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n");
+	sess->session_state = TARG_SESS_STATE_LOGGED_IN;
+
+	pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n",
+		conn->cid, conn->login_ip, np->np_ip, np->np_port, tpg->tpgt);
+
+	spin_lock_bh(&sess->conn_lock);
+	list_add_tail(&conn->conn_list, &sess->sess_conn_list);
+	atomic_inc(&sess->nconn);
+	pr_debug("Incremented iSCSI Connection count to %hu from node:"
+		" %s\n", atomic_read(&sess->nconn),
+		sess->sess_ops->InitiatorName);
+	spin_unlock_bh(&sess->conn_lock);
+
+	sess->sid = tpg->sid++;
+	if (!sess->sid)
+		sess->sid = tpg->sid++;
+	pr_debug("Established iSCSI session from node: %s\n",
+			sess->sess_ops->InitiatorName);
+
+	tpg->nsessions++;
+	if (tpg->tpg_tiqn)
+		tpg->tpg_tiqn->tiqn_nsessions++;
+
+	pr_debug("Incremented number of active iSCSI sessions to %u on"
+		" iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt);
+	spin_unlock_bh(&se_tpg->session_lock);
+
+	iscsi_post_login_start_timers(conn);
+	iscsi_activate_thread_set(conn, ts);
+	/*
+	 * Determine CPU mask to ensure connection's RX and TX kthreads
+	 * are scheduled on the same CPU.
+	 */
+	iscsit_thread_get_cpumask(conn);
+	conn->conn_rx_reset_cpumask = 1;
+	conn->conn_tx_reset_cpumask = 1;
+
+	iscsit_dec_conn_usage_count(conn);
+
+	return 0;
+}
+
+static void iscsi_handle_login_thread_timeout(unsigned long data)
+{
+	struct iscsi_np *np = (struct iscsi_np *) data;
+
+	spin_lock_bh(&np->np_thread_lock);
+	pr_err("iSCSI Login timeout on Network Portal %s:%hu\n",
+			np->np_ip, np->np_port);
+
+	if (np->np_login_timer_flags & ISCSI_TF_STOP) {
+		spin_unlock_bh(&np->np_thread_lock);
+		return;
+	}
+
+	if (np->np_thread)
+		send_sig(SIGINT, np->np_thread, 1);
+
+	np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&np->np_thread_lock);
+}
+
+static void iscsi_start_login_thread_timer(struct iscsi_np *np)
+{
+	/*
+	 * This used the TA_LOGIN_TIMEOUT constant because at this
+	 * point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout
+	 */
+	spin_lock_bh(&np->np_thread_lock);
+	init_timer(&np->np_login_timer);
+	np->np_login_timer.expires = (get_jiffies_64() + TA_LOGIN_TIMEOUT * HZ);
+	np->np_login_timer.data = (unsigned long)np;
+	np->np_login_timer.function = iscsi_handle_login_thread_timeout;
+	np->np_login_timer_flags &= ~ISCSI_TF_STOP;
+	np->np_login_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&np->np_login_timer);
+
+	pr_debug("Added timeout timer to iSCSI login request for"
+			" %u seconds.\n", TA_LOGIN_TIMEOUT);
+	spin_unlock_bh(&np->np_thread_lock);
+}
+
+static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
+{
+	spin_lock_bh(&np->np_thread_lock);
+	if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&np->np_thread_lock);
+		return;
+	}
+	np->np_login_timer_flags |= ISCSI_TF_STOP;
+	spin_unlock_bh(&np->np_thread_lock);
+
+	del_timer_sync(&np->np_login_timer);
+
+	spin_lock_bh(&np->np_thread_lock);
+	np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&np->np_thread_lock);
+}
+
+int iscsi_target_setup_login_socket(
+	struct iscsi_np *np,
+	struct __kernel_sockaddr_storage *sockaddr)
+{
+	struct socket *sock;
+	int backlog = 5, ret, opt = 0, len;
+
+	switch (np->np_network_transport) {
+	case ISCSI_TCP:
+		np->np_ip_proto = IPPROTO_TCP;
+		np->np_sock_type = SOCK_STREAM;
+		break;
+	case ISCSI_SCTP_TCP:
+		np->np_ip_proto = IPPROTO_SCTP;
+		np->np_sock_type = SOCK_STREAM;
+		break;
+	case ISCSI_SCTP_UDP:
+		np->np_ip_proto = IPPROTO_SCTP;
+		np->np_sock_type = SOCK_SEQPACKET;
+		break;
+	case ISCSI_IWARP_TCP:
+	case ISCSI_IWARP_SCTP:
+	case ISCSI_INFINIBAND:
+	default:
+		pr_err("Unsupported network_transport: %d\n",
+				np->np_network_transport);
+		return -EINVAL;
+	}
+
+	ret = sock_create(sockaddr->ss_family, np->np_sock_type,
+			np->np_ip_proto, &sock);
+	if (ret < 0) {
+		pr_err("sock_create() failed.\n");
+		return ret;
+	}
+	np->np_socket = sock;
+	/*
+	 * The SCTP stack needs struct socket->file.
+	 */
+	if ((np->np_network_transport == ISCSI_SCTP_TCP) ||
+	    (np->np_network_transport == ISCSI_SCTP_UDP)) {
+		if (!sock->file) {
+			sock->file = kzalloc(sizeof(struct file), GFP_KERNEL);
+			if (!sock->file) {
+				pr_err("Unable to allocate struct"
+						" file for SCTP\n");
+				ret = -ENOMEM;
+				goto fail;
+			}
+			np->np_flags |= NPF_SCTP_STRUCT_FILE;
+		}
+	}
+	/*
+	 * Setup the np->np_sockaddr from the passed sockaddr setup
+	 * in iscsi_target_configfs.c code..
+	 */
+	memcpy((void *)&np->np_sockaddr, (void *)sockaddr,
+			sizeof(struct __kernel_sockaddr_storage));
+
+	if (sockaddr->ss_family == AF_INET6)
+		len = sizeof(struct sockaddr_in6);
+	else
+		len = sizeof(struct sockaddr_in);
+	/*
+	 * Set SO_REUSEADDR, and disable Nagel Algorithm with TCP_NODELAY.
+	 */
+	opt = 1;
+	if (np->np_network_transport == ISCSI_TCP) {
+		ret = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+				(char *)&opt, sizeof(opt));
+		if (ret < 0) {
+			pr_err("kernel_setsockopt() for TCP_NODELAY"
+				" failed: %d\n", ret);
+			goto fail;
+		}
+	}
+
+	ret = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+			(char *)&opt, sizeof(opt));
+	if (ret < 0) {
+		pr_err("kernel_setsockopt() for SO_REUSEADDR"
+			" failed\n");
+		goto fail;
+	}
+
+	ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len);
+	if (ret < 0) {
+		pr_err("kernel_bind() failed: %d\n", ret);
+		goto fail;
+	}
+
+	ret = kernel_listen(sock, backlog);
+	if (ret != 0) {
+		pr_err("kernel_listen() failed: %d\n", ret);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	np->np_socket = NULL;
+	if (sock) {
+		if (np->np_flags & NPF_SCTP_STRUCT_FILE) {
+			kfree(sock->file);
+			sock->file = NULL;
+		}
+
+		sock_release(sock);
+	}
+	return ret;
+}
+
+static int __iscsi_target_login_thread(struct iscsi_np *np)
+{
+	u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0;
+	int err, ret = 0, ip_proto, sock_type, set_sctp_conn_flag, stop;
+	struct iscsi_conn *conn = NULL;
+	struct iscsi_login *login;
+	struct iscsi_portal_group *tpg = NULL;
+	struct socket *new_sock, *sock;
+	struct kvec iov;
+	struct iscsi_login_req *pdu;
+	struct sockaddr_in sock_in;
+	struct sockaddr_in6 sock_in6;
+
+	flush_signals(current);
+	set_sctp_conn_flag = 0;
+	sock = np->np_socket;
+	ip_proto = np->np_ip_proto;
+	sock_type = np->np_sock_type;
+
+	spin_lock_bh(&np->np_thread_lock);
+	if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+		complete(&np->np_restart_comp);
+	} else {
+		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+	}
+	spin_unlock_bh(&np->np_thread_lock);
+
+	if (kernel_accept(sock, &new_sock, 0) < 0) {
+		spin_lock_bh(&np->np_thread_lock);
+		if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+			spin_unlock_bh(&np->np_thread_lock);
+			complete(&np->np_restart_comp);
+			/* Get another socket */
+			return 1;
+		}
+		spin_unlock_bh(&np->np_thread_lock);
+		goto out;
+	}
+	/*
+	 * The SCTP stack needs struct socket->file.
+	 */
+	if ((np->np_network_transport == ISCSI_SCTP_TCP) ||
+	    (np->np_network_transport == ISCSI_SCTP_UDP)) {
+		if (!new_sock->file) {
+			new_sock->file = kzalloc(
+					sizeof(struct file), GFP_KERNEL);
+			if (!new_sock->file) {
+				pr_err("Unable to allocate struct"
+						" file for SCTP\n");
+				sock_release(new_sock);
+				/* Get another socket */
+				return 1;
+			}
+			set_sctp_conn_flag = 1;
+		}
+	}
+
+	iscsi_start_login_thread_timer(np);
+
+	conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);
+	if (!conn) {
+		pr_err("Could not allocate memory for"
+			" new connection\n");
+		if (set_sctp_conn_flag) {
+			kfree(new_sock->file);
+			new_sock->file = NULL;
+		}
+		sock_release(new_sock);
+		/* Get another socket */
+		return 1;
+	}
+
+	pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
+	conn->conn_state = TARG_CONN_STATE_FREE;
+	conn->sock = new_sock;
+
+	if (set_sctp_conn_flag)
+		conn->conn_flags |= CONNFLAG_SCTP_STRUCT_FILE;
+
+	pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
+	conn->conn_state = TARG_CONN_STATE_XPT_UP;
+
+	/*
+	 * Allocate conn->conn_ops early as a failure calling
+	 * iscsit_tx_login_rsp() below will call tx_data().
+	 */
+	conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL);
+	if (!conn->conn_ops) {
+		pr_err("Unable to allocate memory for"
+			" struct iscsi_conn_ops.\n");
+		goto new_sess_out;
+	}
+	/*
+	 * Perform the remaining iSCSI connection initialization items..
+	 */
+	if (iscsi_login_init_conn(conn) < 0)
+		goto new_sess_out;
+
+	memset(buffer, 0, ISCSI_HDR_LEN);
+	memset(&iov, 0, sizeof(struct kvec));
+	iov.iov_base	= buffer;
+	iov.iov_len	= ISCSI_HDR_LEN;
+
+	if (rx_data(conn, &iov, 1, ISCSI_HDR_LEN) <= 0) {
+		pr_err("rx_data() returned an error.\n");
+		goto new_sess_out;
+	}
+
+	iscsi_opcode = (buffer[0] & ISCSI_OPCODE_MASK);
+	if (!(iscsi_opcode & ISCSI_OP_LOGIN)) {
+		pr_err("First opcode is not login request,"
+			" failing login request.\n");
+		goto new_sess_out;
+	}
+
+	pdu			= (struct iscsi_login_req *) buffer;
+	pdu->cid		= be16_to_cpu(pdu->cid);
+	pdu->tsih		= be16_to_cpu(pdu->tsih);
+	pdu->itt		= be32_to_cpu(pdu->itt);
+	pdu->cmdsn		= be32_to_cpu(pdu->cmdsn);
+	pdu->exp_statsn		= be32_to_cpu(pdu->exp_statsn);
+	/*
+	 * Used by iscsit_tx_login_rsp() for Login Resonses PDUs
+	 * when Status-Class != 0.
+	*/
+	conn->login_itt		= pdu->itt;
+
+	spin_lock_bh(&np->np_thread_lock);
+	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+		spin_unlock_bh(&np->np_thread_lock);
+		pr_err("iSCSI Network Portal on %s:%hu currently not"
+			" active.\n", np->np_ip, np->np_port);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+		goto new_sess_out;
+	}
+	spin_unlock_bh(&np->np_thread_lock);
+
+	if (np->np_sockaddr.ss_family == AF_INET6) {
+		memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
+
+		if (conn->sock->ops->getname(conn->sock,
+				(struct sockaddr *)&sock_in6, &err, 1) < 0) {
+			pr_err("sock_ops->getname() failed.\n");
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+					ISCSI_LOGIN_STATUS_TARGET_ERROR);
+			goto new_sess_out;
+		}
+#if 0
+		if (!iscsi_ntop6((const unsigned char *)
+				&sock_in6.sin6_addr.in6_u,
+				(char *)&conn->ipv6_login_ip[0],
+				IPV6_ADDRESS_SPACE)) {
+			pr_err("iscsi_ntop6() failed\n");
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+					ISCSI_LOGIN_STATUS_TARGET_ERROR);
+			goto new_sess_out;
+		}
+#else
+		pr_debug("Skipping iscsi_ntop6()\n");
+#endif
+	} else {
+		memset(&sock_in, 0, sizeof(struct sockaddr_in));
+
+		if (conn->sock->ops->getname(conn->sock,
+				(struct sockaddr *)&sock_in, &err, 1) < 0) {
+			pr_err("sock_ops->getname() failed.\n");
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+					ISCSI_LOGIN_STATUS_TARGET_ERROR);
+			goto new_sess_out;
+		}
+		sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr);
+		conn->login_port = ntohs(sock_in.sin_port);
+	}
+
+	conn->network_transport = np->np_network_transport;
+
+	pr_debug("Received iSCSI login request from %s on %s Network"
+			" Portal %s:%hu\n", conn->login_ip,
+		(conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP",
+			np->np_ip, np->np_port);
+
+	pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
+	conn->conn_state	= TARG_CONN_STATE_IN_LOGIN;
+
+	if (iscsi_login_check_initiator_version(conn, pdu->max_version,
+			pdu->min_version) < 0)
+		goto new_sess_out;
+
+	zero_tsih = (pdu->tsih == 0x0000);
+	if ((zero_tsih)) {
+		/*
+		 * This is the leading connection of a new session.
+		 * We wait until after authentication to check for
+		 * session reinstatement.
+		 */
+		if (iscsi_login_zero_tsih_s1(conn, buffer) < 0)
+			goto new_sess_out;
+	} else {
+		/*
+		 * Add a new connection to an existing session.
+		 * We check for a non-existant session in
+		 * iscsi_login_non_zero_tsih_s2() below based
+		 * on ISID/TSIH, but wait until after authentication
+		 * to check for connection reinstatement, etc.
+		 */
+		if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)
+			goto new_sess_out;
+	}
+
+	/*
+	 * This will process the first login request, and call
+	 * iscsi_target_locate_portal(), and return a valid struct iscsi_login.
+	 */
+	login = iscsi_target_init_negotiation(np, conn, buffer);
+	if (!login) {
+		tpg = conn->tpg;
+		goto new_sess_out;
+	}
+
+	tpg = conn->tpg;
+	if (!tpg) {
+		pr_err("Unable to locate struct iscsi_conn->tpg\n");
+		goto new_sess_out;
+	}
+
+	if (zero_tsih) {
+		if (iscsi_login_zero_tsih_s2(conn) < 0) {
+			iscsi_target_nego_release(login, conn);
+			goto new_sess_out;
+		}
+	} else {
+		if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0) {
+			iscsi_target_nego_release(login, conn);
+			goto old_sess_out;
+		}
+	}
+
+	if (iscsi_target_start_negotiation(login, conn) < 0)
+		goto new_sess_out;
+
+	if (!conn->sess) {
+		pr_err("struct iscsi_conn session pointer is NULL!\n");
+		goto new_sess_out;
+	}
+
+	iscsi_stop_login_thread_timer(np);
+
+	if (signal_pending(current))
+		goto new_sess_out;
+
+	ret = iscsi_post_login_handler(np, conn, zero_tsih);
+
+	if (ret < 0)
+		goto new_sess_out;
+
+	iscsit_deaccess_np(np, tpg);
+	tpg = NULL;
+	/* Get another socket */
+	return 1;
+
+new_sess_out:
+	pr_err("iSCSI Login negotiation failed.\n");
+	iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				  ISCSI_LOGIN_STATUS_INIT_ERR);
+	if (!zero_tsih || !conn->sess)
+		goto old_sess_out;
+	if (conn->sess->se_sess)
+		transport_free_session(conn->sess->se_sess);
+	if (conn->sess->session_index != 0) {
+		spin_lock_bh(&sess_idr_lock);
+		idr_remove(&sess_idr, conn->sess->session_index);
+		spin_unlock_bh(&sess_idr_lock);
+	}
+	if (conn->sess->sess_ops)
+		kfree(conn->sess->sess_ops);
+	if (conn->sess)
+		kfree(conn->sess);
+old_sess_out:
+	iscsi_stop_login_thread_timer(np);
+	/*
+	 * If login negotiation fails check if the Time2Retain timer
+	 * needs to be restarted.
+	 */
+	if (!zero_tsih && conn->sess) {
+		spin_lock_bh(&conn->sess->conn_lock);
+		if (conn->sess->session_state == TARG_SESS_STATE_FAILED) {
+			struct se_portal_group *se_tpg =
+					&ISCSI_TPG_C(conn)->tpg_se_tpg;
+
+			atomic_set(&conn->sess->session_continuation, 0);
+			spin_unlock_bh(&conn->sess->conn_lock);
+			spin_lock_bh(&se_tpg->session_lock);
+			iscsit_start_time2retain_handler(conn->sess);
+			spin_unlock_bh(&se_tpg->session_lock);
+		} else
+			spin_unlock_bh(&conn->sess->conn_lock);
+		iscsit_dec_session_usage_count(conn->sess);
+	}
+
+	if (!IS_ERR(conn->conn_rx_hash.tfm))
+		crypto_free_hash(conn->conn_rx_hash.tfm);
+	if (!IS_ERR(conn->conn_tx_hash.tfm))
+		crypto_free_hash(conn->conn_tx_hash.tfm);
+
+	if (conn->conn_cpumask)
+		free_cpumask_var(conn->conn_cpumask);
+
+	kfree(conn->conn_ops);
+
+	if (conn->param_list) {
+		iscsi_release_param_list(conn->param_list);
+		conn->param_list = NULL;
+	}
+	if (conn->sock) {
+		if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) {
+			kfree(conn->sock->file);
+			conn->sock->file = NULL;
+		}
+		sock_release(conn->sock);
+	}
+	kfree(conn);
+
+	if (tpg) {
+		iscsit_deaccess_np(np, tpg);
+		tpg = NULL;
+	}
+
+out:
+	stop = kthread_should_stop();
+	if (!stop && signal_pending(current)) {
+		spin_lock_bh(&np->np_thread_lock);
+		stop = (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN);
+		spin_unlock_bh(&np->np_thread_lock);
+	}
+	/* Wait for another socket.. */
+	if (!stop)
+		return 1;
+
+	iscsi_stop_login_thread_timer(np);
+	spin_lock_bh(&np->np_thread_lock);
+	np->np_thread_state = ISCSI_NP_THREAD_EXIT;
+	spin_unlock_bh(&np->np_thread_lock);
+	return 0;
+}
+
+int iscsi_target_login_thread(void *arg)
+{
+	struct iscsi_np *np = (struct iscsi_np *)arg;
+	int ret;
+
+	allow_signal(SIGINT);
+
+	while (!kthread_should_stop()) {
+		ret = __iscsi_target_login_thread(np);
+		/*
+		 * We break and exit here unless another sock_accept() call
+		 * is expected.
+		 */
+		if (ret != 1)
+			break;
+	}
+
+	return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_login.h b/drivers/target/iscsi/iscsi_target_login.h
new file mode 100644
index 0000000..091dcae
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_login.h
@@ -0,0 +1,12 @@
+#ifndef ISCSI_TARGET_LOGIN_H
+#define ISCSI_TARGET_LOGIN_H
+
+extern int iscsi_login_setup_crypto(struct iscsi_conn *);
+extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *);
+extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32);
+extern int iscsi_target_setup_login_socket(struct iscsi_np *,
+				struct __kernel_sockaddr_storage *);
+extern int iscsi_target_login_thread(void *);
+extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *);
+
+#endif   /*** ISCSI_TARGET_LOGIN_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
new file mode 100644
index 0000000..713a4d2
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -0,0 +1,1067 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI Parameter negotiation.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/ctype.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_tpg.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_auth.h"
+
+#define MAX_LOGIN_PDUS  7
+#define TEXT_LEN	4096
+
+void convert_null_to_semi(char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		if (buf[i] == '\0')
+			buf[i] = ';';
+}
+
+int strlen_semi(char *buf)
+{
+	int i = 0;
+
+	while (buf[i] != '\0') {
+		if (buf[i] == ';')
+			return i;
+		i++;
+	}
+
+	return -1;
+}
+
+int extract_param(
+	const char *in_buf,
+	const char *pattern,
+	unsigned int max_length,
+	char *out_buf,
+	unsigned char *type)
+{
+	char *ptr;
+	int len;
+
+	if (!in_buf || !pattern || !out_buf || !type)
+		return -1;
+
+	ptr = strstr(in_buf, pattern);
+	if (!ptr)
+		return -1;
+
+	ptr = strstr(ptr, "=");
+	if (!ptr)
+		return -1;
+
+	ptr += 1;
+	if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) {
+		ptr += 2; /* skip 0x */
+		*type = HEX;
+	} else
+		*type = DECIMAL;
+
+	len = strlen_semi(ptr);
+	if (len < 0)
+		return -1;
+
+	if (len > max_length) {
+		pr_err("Length of input: %d exeeds max_length:"
+			" %d\n", len, max_length);
+		return -1;
+	}
+	memcpy(out_buf, ptr, len);
+	out_buf[len] = '\0';
+
+	return 0;
+}
+
+static u32 iscsi_handle_authentication(
+	struct iscsi_conn *conn,
+	char *in_buf,
+	char *out_buf,
+	int in_length,
+	int *out_length,
+	unsigned char *authtype)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_auth *auth;
+	struct iscsi_node_acl *iscsi_nacl;
+	struct se_node_acl *se_nacl;
+
+	if (!sess->sess_ops->SessionType) {
+		/*
+		 * For SessionType=Normal
+		 */
+		se_nacl = conn->sess->se_sess->se_node_acl;
+		if (!se_nacl) {
+			pr_err("Unable to locate struct se_node_acl for"
+					" CHAP auth\n");
+			return -1;
+		}
+		iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
+				se_node_acl);
+		if (!iscsi_nacl) {
+			pr_err("Unable to locate struct iscsi_node_acl for"
+					" CHAP auth\n");
+			return -1;
+		}
+
+		auth = ISCSI_NODE_AUTH(iscsi_nacl);
+	} else {
+		/*
+		 * For SessionType=Discovery
+		 */
+		auth = &iscsit_global->discovery_acl.node_auth;
+	}
+
+	if (strstr("CHAP", authtype))
+		strcpy(conn->sess->auth_type, "CHAP");
+	else
+		strcpy(conn->sess->auth_type, NONE);
+
+	if (strstr("None", authtype))
+		return 1;
+#ifdef CANSRP
+	else if (strstr("SRP", authtype))
+		return srp_main_loop(conn, auth, in_buf, out_buf,
+				&in_length, out_length);
+#endif
+	else if (strstr("CHAP", authtype))
+		return chap_main_loop(conn, auth, in_buf, out_buf,
+				&in_length, out_length);
+	else if (strstr("SPKM1", authtype))
+		return 2;
+	else if (strstr("SPKM2", authtype))
+		return 2;
+	else if (strstr("KRB5", authtype))
+		return 2;
+	else
+		return 2;
+}
+
+static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn)
+{
+	kfree(conn->auth_protocol);
+}
+
+static int iscsi_target_check_login_request(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	int req_csg, req_nsg, rsp_csg, rsp_nsg;
+	u32 payload_length;
+	struct iscsi_login_req *login_req;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+	payload_length = ntoh24(login_req->dlength);
+
+	switch (login_req->opcode & ISCSI_OPCODE_MASK) {
+	case ISCSI_OP_LOGIN:
+		break;
+	default:
+		pr_err("Received unknown opcode 0x%02x.\n",
+				login_req->opcode & ISCSI_OPCODE_MASK);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if ((login_req->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
+	    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+		pr_err("Login request has both ISCSI_FLAG_LOGIN_CONTINUE"
+			" and ISCSI_FLAG_LOGIN_TRANSIT set, protocol error.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
+	rsp_csg = (login_rsp->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
+	req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK);
+	rsp_nsg = (login_rsp->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK);
+
+	if (req_csg != login->current_stage) {
+		pr_err("Initiator unexpectedly changed login stage"
+			" from %d to %d, login failed.\n", login->current_stage,
+			req_csg);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if ((req_nsg == 2) || (req_csg >= 2) ||
+	   ((login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
+	    (req_nsg <= req_csg))) {
+		pr_err("Illegal login_req->flags Combination, CSG: %d,"
+			" NSG: %d, ISCSI_FLAG_LOGIN_TRANSIT: %d.\n", req_csg,
+			req_nsg, (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT));
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if ((login_req->max_version != login->version_max) ||
+	    (login_req->min_version != login->version_min)) {
+		pr_err("Login request changed Version Max/Nin"
+			" unexpectedly to 0x%02x/0x%02x, protocol error\n",
+			login_req->max_version, login_req->min_version);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if (memcmp(login_req->isid, login->isid, 6) != 0) {
+		pr_err("Login request changed ISID unexpectedly,"
+				" protocol error.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if (login_req->itt != login->init_task_tag) {
+		pr_err("Login request changed ITT unexpectedly to"
+			" 0x%08x, protocol error.\n", login_req->itt);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_INIT_ERR);
+		return -1;
+	}
+
+	if (payload_length > MAX_KEY_VALUE_PAIRS) {
+		pr_err("Login request payload exceeds default"
+			" MaxRecvDataSegmentLength: %u, protocol error.\n",
+				MAX_KEY_VALUE_PAIRS);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int iscsi_target_check_first_request(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	struct iscsi_param *param = NULL;
+	struct se_node_acl *se_nacl;
+
+	login->first_request = 0;
+
+	list_for_each_entry(param, &conn->param_list->param_list, p_list) {
+		if (!strncmp(param->name, SESSIONTYPE, 11)) {
+			if (!IS_PSTATE_ACCEPTOR(param)) {
+				pr_err("SessionType key not received"
+					" in first login request.\n");
+				iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+					ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+				return -1;
+			}
+			if (!strncmp(param->value, DISCOVERY, 9))
+				return 0;
+		}
+
+		if (!strncmp(param->name, INITIATORNAME, 13)) {
+			if (!IS_PSTATE_ACCEPTOR(param)) {
+				if (!login->leading_connection)
+					continue;
+
+				pr_err("InitiatorName key not received"
+					" in first login request.\n");
+				iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+					ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+				return -1;
+			}
+
+			/*
+			 * For non-leading connections, double check that the
+			 * received InitiatorName matches the existing session's
+			 * struct iscsi_node_acl.
+			 */
+			if (!login->leading_connection) {
+				se_nacl = conn->sess->se_sess->se_node_acl;
+				if (!se_nacl) {
+					pr_err("Unable to locate"
+						" struct se_node_acl\n");
+					iscsit_tx_login_rsp(conn,
+							ISCSI_STATUS_CLS_INITIATOR_ERR,
+							ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
+					return -1;
+				}
+
+				if (strcmp(param->value,
+						se_nacl->initiatorname)) {
+					pr_err("Incorrect"
+						" InitiatorName: %s for this"
+						" iSCSI Initiator Node.\n",
+						param->value);
+					iscsit_tx_login_rsp(conn,
+							ISCSI_STATUS_CLS_INITIATOR_ERR,
+							ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
+					return -1;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+	u32 padding = 0;
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+
+	login_rsp->opcode		= ISCSI_OP_LOGIN_RSP;
+	hton24(login_rsp->dlength, login->rsp_length);
+	memcpy(login_rsp->isid, login->isid, 6);
+	login_rsp->tsih			= cpu_to_be16(login->tsih);
+	login_rsp->itt			= cpu_to_be32(login->init_task_tag);
+	login_rsp->statsn		= cpu_to_be32(conn->stat_sn++);
+	login_rsp->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
+	login_rsp->max_cmdsn		= cpu_to_be32(conn->sess->max_cmd_sn);
+
+	pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x,"
+		" ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:"
+		" %u\n", login_rsp->flags, ntohl(login_rsp->itt),
+		ntohl(login_rsp->exp_cmdsn), ntohl(login_rsp->max_cmdsn),
+		ntohl(login_rsp->statsn), login->rsp_length);
+
+	padding = ((-login->rsp_length) & 3);
+
+	if (iscsi_login_tx_data(
+			conn,
+			login->rsp,
+			login->rsp_buf,
+			login->rsp_length + padding) < 0)
+		return -1;
+
+	login->rsp_length		= 0;
+	login_rsp->tsih			= be16_to_cpu(login_rsp->tsih);
+	login_rsp->itt			= be32_to_cpu(login_rsp->itt);
+	login_rsp->statsn		= be32_to_cpu(login_rsp->statsn);
+	mutex_lock(&sess->cmdsn_mutex);
+	login_rsp->exp_cmdsn		= be32_to_cpu(sess->exp_cmd_sn);
+	login_rsp->max_cmdsn		= be32_to_cpu(sess->max_cmd_sn);
+	mutex_unlock(&sess->cmdsn_mutex);
+
+	return 0;
+}
+
+static int iscsi_target_do_rx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+	u32 padding = 0, payload_length;
+	struct iscsi_login_req *login_req;
+
+	if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0)
+		return -1;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	payload_length			= ntoh24(login_req->dlength);
+	login_req->tsih			= be16_to_cpu(login_req->tsih);
+	login_req->itt			= be32_to_cpu(login_req->itt);
+	login_req->cid			= be16_to_cpu(login_req->cid);
+	login_req->cmdsn		= be32_to_cpu(login_req->cmdsn);
+	login_req->exp_statsn		= be32_to_cpu(login_req->exp_statsn);
+
+	pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
+		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n",
+		 login_req->flags, login_req->itt, login_req->cmdsn,
+		 login_req->exp_statsn, login_req->cid, payload_length);
+
+	if (iscsi_target_check_login_request(conn, login) < 0)
+		return -1;
+
+	padding = ((-payload_length) & 3);
+	memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS);
+
+	if (iscsi_login_rx_data(
+			conn,
+			login->req_buf,
+			payload_length + padding) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+	if (iscsi_target_do_tx_login_io(conn, login) < 0)
+		return -1;
+
+	if (iscsi_target_do_rx_login_io(conn, login) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int iscsi_target_get_initial_payload(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	u32 padding = 0, payload_length;
+	struct iscsi_login_req *login_req;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	payload_length = ntoh24(login_req->dlength);
+
+	pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
+		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
+		login_req->flags, login_req->itt, login_req->cmdsn,
+		login_req->exp_statsn, payload_length);
+
+	if (iscsi_target_check_login_request(conn, login) < 0)
+		return -1;
+
+	padding = ((-payload_length) & 3);
+
+	if (iscsi_login_rx_data(
+			conn,
+			login->req_buf,
+			payload_length + padding) < 0)
+		return -1;
+
+	return 0;
+}
+
+/*
+ *	NOTE: We check for existing sessions or connections AFTER the initiator
+ *	has been successfully authenticated in order to protect against faked
+ *	ISID/TSIH combinations.
+ */
+static int iscsi_target_check_for_existing_instances(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	if (login->checked_for_existing)
+		return 0;
+
+	login->checked_for_existing = 1;
+
+	if (!login->tsih)
+		return iscsi_check_for_session_reinstatement(conn);
+	else
+		return iscsi_login_post_auth_non_zero_tsih(conn, login->cid,
+				login->initial_exp_statsn);
+}
+
+static int iscsi_target_do_authentication(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	int authret;
+	u32 payload_length;
+	struct iscsi_param *param;
+	struct iscsi_login_req *login_req;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+	payload_length = ntoh24(login_req->dlength);
+
+	param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
+	if (!param)
+		return -1;
+
+	authret = iscsi_handle_authentication(
+			conn,
+			login->req_buf,
+			login->rsp_buf,
+			payload_length,
+			&login->rsp_length,
+			param->value);
+	switch (authret) {
+	case 0:
+		pr_debug("Received OK response"
+		" from LIO Authentication, continuing.\n");
+		break;
+	case 1:
+		pr_debug("iSCSI security negotiation"
+			" completed sucessfully.\n");
+		login->auth_complete = 1;
+		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
+		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+			login_rsp->flags |= (ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
+					     ISCSI_FLAG_LOGIN_TRANSIT);
+			login->current_stage = 1;
+		}
+		return iscsi_target_check_for_existing_instances(
+				conn, login);
+	case 2:
+		pr_err("Security negotiation"
+			" failed.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_AUTH_FAILED);
+		return -1;
+	default:
+		pr_err("Received unknown error %d from LIO"
+				" Authentication\n", authret);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_TARGET_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int iscsi_target_handle_csg_zero(
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	int ret;
+	u32 payload_length;
+	struct iscsi_param *param;
+	struct iscsi_login_req *login_req;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+	payload_length = ntoh24(login_req->dlength);
+
+	param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
+	if (!param)
+		return -1;
+
+	ret = iscsi_decode_text_input(
+			PHASE_SECURITY|PHASE_DECLARATIVE,
+			SENDER_INITIATOR|SENDER_RECEIVER,
+			login->req_buf,
+			payload_length,
+			conn->param_list);
+	if (ret < 0)
+		return -1;
+
+	if (ret > 0) {
+		if (login->auth_complete) {
+			pr_err("Initiator has already been"
+				" successfully authenticated, but is still"
+				" sending %s keys.\n", param->value);
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+					ISCSI_LOGIN_STATUS_INIT_ERR);
+			return -1;
+		}
+
+		goto do_auth;
+	}
+
+	if (login->first_request)
+		if (iscsi_target_check_first_request(conn, login) < 0)
+			return -1;
+
+	ret = iscsi_encode_text_output(
+			PHASE_SECURITY|PHASE_DECLARATIVE,
+			SENDER_TARGET,
+			login->rsp_buf,
+			&login->rsp_length,
+			conn->param_list);
+	if (ret < 0)
+		return -1;
+
+	if (!iscsi_check_negotiated_keys(conn->param_list)) {
+		if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication &&
+		    !strncmp(param->value, NONE, 4)) {
+			pr_err("Initiator sent AuthMethod=None but"
+				" Target is enforcing iSCSI Authentication,"
+					" login failed.\n");
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+					ISCSI_LOGIN_STATUS_AUTH_FAILED);
+			return -1;
+		}
+
+		if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication &&
+		    !login->auth_complete)
+			return 0;
+
+		if (strncmp(param->value, NONE, 4) && !login->auth_complete)
+			return 0;
+
+		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
+		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+			login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
+					    ISCSI_FLAG_LOGIN_TRANSIT;
+			login->current_stage = 1;
+		}
+	}
+
+	return 0;
+do_auth:
+	return iscsi_target_do_authentication(conn, login);
+}
+
+static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+	int ret;
+	u32 payload_length;
+	struct iscsi_login_req *login_req;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+	payload_length = ntoh24(login_req->dlength);
+
+	ret = iscsi_decode_text_input(
+			PHASE_OPERATIONAL|PHASE_DECLARATIVE,
+			SENDER_INITIATOR|SENDER_RECEIVER,
+			login->req_buf,
+			payload_length,
+			conn->param_list);
+	if (ret < 0)
+		return -1;
+
+	if (login->first_request)
+		if (iscsi_target_check_first_request(conn, login) < 0)
+			return -1;
+
+	if (iscsi_target_check_for_existing_instances(conn, login) < 0)
+		return -1;
+
+	ret = iscsi_encode_text_output(
+			PHASE_OPERATIONAL|PHASE_DECLARATIVE,
+			SENDER_TARGET,
+			login->rsp_buf,
+			&login->rsp_length,
+			conn->param_list);
+	if (ret < 0)
+		return -1;
+
+	if (!login->auth_complete &&
+	     ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication) {
+		pr_err("Initiator is requesting CSG: 1, has not been"
+			 " successfully authenticated, and the Target is"
+			" enforcing iSCSI Authentication, login failed.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_AUTH_FAILED);
+		return -1;
+	}
+
+	if (!iscsi_check_negotiated_keys(conn->param_list))
+		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) &&
+		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT))
+			login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE3 |
+					    ISCSI_FLAG_LOGIN_TRANSIT;
+
+	return 0;
+}
+
+static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+	int pdu_count = 0;
+	struct iscsi_login_req *login_req;
+	struct iscsi_login_rsp *login_rsp;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_login_rsp *) login->rsp;
+
+	while (1) {
+		if (++pdu_count > MAX_LOGIN_PDUS) {
+			pr_err("MAX_LOGIN_PDUS count reached.\n");
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+					ISCSI_LOGIN_STATUS_TARGET_ERROR);
+			return -1;
+		}
+
+		switch ((login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) {
+		case 0:
+			login_rsp->flags |= (0 & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK);
+			if (iscsi_target_handle_csg_zero(conn, login) < 0)
+				return -1;
+			break;
+		case 1:
+			login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1;
+			if (iscsi_target_handle_csg_one(conn, login) < 0)
+				return -1;
+			if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+				login->tsih = conn->sess->tsih;
+				if (iscsi_target_do_tx_login_io(conn,
+						login) < 0)
+					return -1;
+				return 0;
+			}
+			break;
+		default:
+			pr_err("Illegal CSG: %d received from"
+				" Initiator, protocol error.\n",
+				(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK)
+				>> 2);
+			break;
+		}
+
+		if (iscsi_target_do_login_io(conn, login) < 0)
+			return -1;
+
+		if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT;
+			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
+		}
+	}
+
+	return 0;
+}
+
+static void iscsi_initiatorname_tolower(
+	char *param_buf)
+{
+	char *c;
+	u32 iqn_size = strlen(param_buf), i;
+
+	for (i = 0; i < iqn_size; i++) {
+		c = (char *)&param_buf[i];
+		if (!isupper(*c))
+			continue;
+
+		*c = tolower(*c);
+	}
+}
+
+/*
+ * Processes the first Login Request..
+ */
+static int iscsi_target_locate_portal(
+	struct iscsi_np *np,
+	struct iscsi_conn *conn,
+	struct iscsi_login *login)
+{
+	char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL;
+	char *tmpbuf, *start = NULL, *end = NULL, *key, *value;
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_tiqn *tiqn;
+	struct iscsi_login_req *login_req;
+	struct iscsi_targ_login_rsp *login_rsp;
+	u32 payload_length;
+	int sessiontype = 0, ret = 0;
+
+	login_req = (struct iscsi_login_req *) login->req;
+	login_rsp = (struct iscsi_targ_login_rsp *) login->rsp;
+	payload_length = ntoh24(login_req->dlength);
+
+	login->first_request	= 1;
+	login->leading_connection = (!login_req->tsih) ? 1 : 0;
+	login->current_stage	=
+		(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
+	login->version_min	= login_req->min_version;
+	login->version_max	= login_req->max_version;
+	memcpy(login->isid, login_req->isid, 6);
+	login->cmd_sn		= login_req->cmdsn;
+	login->init_task_tag	= login_req->itt;
+	login->initial_exp_statsn = login_req->exp_statsn;
+	login->cid		= login_req->cid;
+	login->tsih		= login_req->tsih;
+
+	if (iscsi_target_get_initial_payload(conn, login) < 0)
+		return -1;
+
+	tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);
+	if (!tmpbuf) {
+		pr_err("Unable to allocate memory for tmpbuf.\n");
+		return -1;
+	}
+
+	memcpy(tmpbuf, login->req_buf, payload_length);
+	tmpbuf[payload_length] = '\0';
+	start = tmpbuf;
+	end = (start + payload_length);
+
+	/*
+	 * Locate the initial keys expected from the Initiator node in
+	 * the first login request in order to progress with the login phase.
+	 */
+	while (start < end) {
+		if (iscsi_extract_key_value(start, &key, &value) < 0) {
+			ret = -1;
+			goto out;
+		}
+
+		if (!strncmp(key, "InitiatorName", 13))
+			i_buf = value;
+		else if (!strncmp(key, "SessionType", 11))
+			s_buf = value;
+		else if (!strncmp(key, "TargetName", 10))
+			t_buf = value;
+
+		start += strlen(key) + strlen(value) + 2;
+	}
+
+	/*
+	 * See 5.3.  Login Phase.
+	 */
+	if (!i_buf) {
+		pr_err("InitiatorName key not received"
+			" in first login request.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+		ret = -1;
+		goto out;
+	}
+	/*
+	 * Convert the incoming InitiatorName to lowercase following
+	 * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs
+	 * are NOT case sensitive.
+	 */
+	iscsi_initiatorname_tolower(i_buf);
+
+	if (!s_buf) {
+		if (!login->leading_connection)
+			goto get_target;
+
+		pr_err("SessionType key not received"
+			" in first login request.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+		ret = -1;
+		goto out;
+	}
+
+	/*
+	 * Use default portal group for discovery sessions.
+	 */
+	sessiontype = strncmp(s_buf, DISCOVERY, 9);
+	if (!sessiontype) {
+		conn->tpg = iscsit_global->discovery_tpg;
+		if (!login->leading_connection)
+			goto get_target;
+
+		sess->sess_ops->SessionType = 1;
+		/*
+		 * Setup crc32c modules from libcrypto
+		 */
+		if (iscsi_login_setup_crypto(conn) < 0) {
+			pr_err("iscsi_login_setup_crypto() failed\n");
+			ret = -1;
+			goto out;
+		}
+		/*
+		 * Serialize access across the discovery struct iscsi_portal_group to
+		 * process login attempt.
+		 */
+		if (iscsit_access_np(np, conn->tpg) < 0) {
+			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+			ret = -1;
+			goto out;
+		}
+		ret = 0;
+		goto out;
+	}
+
+get_target:
+	if (!t_buf) {
+		pr_err("TargetName key not received"
+			" in first login request while"
+			" SessionType=Normal.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+		ret = -1;
+		goto out;
+	}
+
+	/*
+	 * Locate Target IQN from Storage Node.
+	 */
+	tiqn = iscsit_get_tiqn_for_login(t_buf);
+	if (!tiqn) {
+		pr_err("Unable to locate Target IQN: %s in"
+			" Storage Node\n", t_buf);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+		ret = -1;
+		goto out;
+	}
+	pr_debug("Located Storage Object: %s\n", tiqn->tiqn);
+
+	/*
+	 * Locate Target Portal Group from Storage Node.
+	 */
+	conn->tpg = iscsit_get_tpg_from_np(tiqn, np);
+	if (!conn->tpg) {
+		pr_err("Unable to locate Target Portal Group"
+				" on %s\n", tiqn->tiqn);
+		iscsit_put_tiqn_for_login(tiqn);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+		ret = -1;
+		goto out;
+	}
+	pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
+	/*
+	 * Setup crc32c modules from libcrypto
+	 */
+	if (iscsi_login_setup_crypto(conn) < 0) {
+		pr_err("iscsi_login_setup_crypto() failed\n");
+		ret = -1;
+		goto out;
+	}
+	/*
+	 * Serialize access across the struct iscsi_portal_group to
+	 * process login attempt.
+	 */
+	if (iscsit_access_np(np, conn->tpg) < 0) {
+		iscsit_put_tiqn_for_login(tiqn);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+		ret = -1;
+		conn->tpg = NULL;
+		goto out;
+	}
+
+	/*
+	 * conn->sess->node_acl will be set when the referenced
+	 * struct iscsi_session is located from received ISID+TSIH in
+	 * iscsi_login_non_zero_tsih_s2().
+	 */
+	if (!login->leading_connection) {
+		ret = 0;
+		goto out;
+	}
+
+	/*
+	 * This value is required in iscsi_login_zero_tsih_s2()
+	 */
+	sess->sess_ops->SessionType = 0;
+
+	/*
+	 * Locate incoming Initiator IQN reference from Storage Node.
+	 */
+	sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+			&conn->tpg->tpg_se_tpg, i_buf);
+	if (!sess->se_sess->se_node_acl) {
+		pr_err("iSCSI Initiator Node: %s is not authorized to"
+			" access iSCSI target portal group: %hu.\n",
+				i_buf, conn->tpg->tpgt);
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+				ISCSI_LOGIN_STATUS_TGT_FORBIDDEN);
+		ret = -1;
+		goto out;
+	}
+
+	ret = 0;
+out:
+	kfree(tmpbuf);
+	return ret;
+}
+
+struct iscsi_login *iscsi_target_init_negotiation(
+	struct iscsi_np *np,
+	struct iscsi_conn *conn,
+	char *login_pdu)
+{
+	struct iscsi_login *login;
+
+	login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL);
+	if (!login) {
+		pr_err("Unable to allocate memory for struct iscsi_login.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		goto out;
+	}
+
+	login->req = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
+	if (!login->req) {
+		pr_err("Unable to allocate memory for Login Request.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		goto out;
+	}
+	memcpy(login->req, login_pdu, ISCSI_HDR_LEN);
+
+	login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
+	if (!login->req_buf) {
+		pr_err("Unable to allocate memory for response buffer.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		goto out;
+	}
+	/*
+	 * SessionType: Discovery
+	 *
+	 *	Locates Default Portal
+	 *
+	 * SessionType: Normal
+	 *
+	 *	Locates Target Portal from NP -> Target IQN
+	 */
+	if (iscsi_target_locate_portal(np, conn, login) < 0) {
+		pr_err("iSCSI Login negotiation failed.\n");
+		goto out;
+	}
+
+	return login;
+out:
+	kfree(login->req);
+	kfree(login->req_buf);
+	kfree(login);
+
+	return NULL;
+}
+
+int iscsi_target_start_negotiation(
+	struct iscsi_login *login,
+	struct iscsi_conn *conn)
+{
+	int ret = -1;
+
+	login->rsp = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
+	if (!login->rsp) {
+		pr_err("Unable to allocate memory for"
+				" Login Response.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		ret = -1;
+		goto out;
+	}
+
+	login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
+	if (!login->rsp_buf) {
+		pr_err("Unable to allocate memory for"
+			" request buffer.\n");
+		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+				ISCSI_LOGIN_STATUS_NO_RESOURCES);
+		ret = -1;
+		goto out;
+	}
+
+	ret = iscsi_target_do_login(conn, login);
+out:
+	if (ret != 0)
+		iscsi_remove_failed_auth_entry(conn);
+
+	iscsi_target_nego_release(login, conn);
+	return ret;
+}
+
+void iscsi_target_nego_release(
+	struct iscsi_login *login,
+	struct iscsi_conn *conn)
+{
+	kfree(login->req);
+	kfree(login->rsp);
+	kfree(login->req_buf);
+	kfree(login->rsp_buf);
+	kfree(login);
+}
diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h
new file mode 100644
index 0000000..92e133a
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nego.h
@@ -0,0 +1,17 @@
+#ifndef ISCSI_TARGET_NEGO_H
+#define ISCSI_TARGET_NEGO_H
+
+#define DECIMAL         0
+#define HEX             1
+
+extern void convert_null_to_semi(char *, int);
+extern int extract_param(const char *, const char *, unsigned int, char *,
+		unsigned char *);
+extern struct iscsi_login *iscsi_target_init_negotiation(
+		struct iscsi_np *, struct iscsi_conn *, char *);
+extern int iscsi_target_start_negotiation(
+		struct iscsi_login *, struct iscsi_conn *);
+extern void iscsi_target_nego_release(
+		struct iscsi_login *, struct iscsi_conn *);
+
+#endif /* ISCSI_TARGET_NEGO_H */
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c
new file mode 100644
index 0000000..aeafbe0
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * This file contains the main functions related to Initiator Node Attributes.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_nodeattrib.h"
+
+static inline char *iscsit_na_get_initiatorname(
+	struct iscsi_node_acl *nacl)
+{
+	struct se_node_acl *se_nacl = &nacl->se_node_acl;
+
+	return &se_nacl->initiatorname[0];
+}
+
+void iscsit_set_default_node_attribues(
+	struct iscsi_node_acl *acl)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	a->dataout_timeout = NA_DATAOUT_TIMEOUT;
+	a->dataout_timeout_retries = NA_DATAOUT_TIMEOUT_RETRIES;
+	a->nopin_timeout = NA_NOPIN_TIMEOUT;
+	a->nopin_response_timeout = NA_NOPIN_RESPONSE_TIMEOUT;
+	a->random_datain_pdu_offsets = NA_RANDOM_DATAIN_PDU_OFFSETS;
+	a->random_datain_seq_offsets = NA_RANDOM_DATAIN_SEQ_OFFSETS;
+	a->random_r2t_offsets = NA_RANDOM_R2T_OFFSETS;
+	a->default_erl = NA_DEFAULT_ERL;
+}
+
+extern int iscsit_na_dataout_timeout(
+	struct iscsi_node_acl *acl,
+	u32 dataout_timeout)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (dataout_timeout > NA_DATAOUT_TIMEOUT_MAX) {
+		pr_err("Requested DataOut Timeout %u larger than"
+			" maximum %u\n", dataout_timeout,
+			NA_DATAOUT_TIMEOUT_MAX);
+		return -EINVAL;
+	} else if (dataout_timeout < NA_DATAOUT_TIMEOUT_MIX) {
+		pr_err("Requested DataOut Timeout %u smaller than"
+			" minimum %u\n", dataout_timeout,
+			NA_DATAOUT_TIMEOUT_MIX);
+		return -EINVAL;
+	}
+
+	a->dataout_timeout = dataout_timeout;
+	pr_debug("Set DataOut Timeout to %u for Initiator Node"
+		" %s\n", a->dataout_timeout, iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_dataout_timeout_retries(
+	struct iscsi_node_acl *acl,
+	u32 dataout_timeout_retries)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (dataout_timeout_retries > NA_DATAOUT_TIMEOUT_RETRIES_MAX) {
+		pr_err("Requested DataOut Timeout Retries %u larger"
+			" than maximum %u", dataout_timeout_retries,
+				NA_DATAOUT_TIMEOUT_RETRIES_MAX);
+		return -EINVAL;
+	} else if (dataout_timeout_retries < NA_DATAOUT_TIMEOUT_RETRIES_MIN) {
+		pr_err("Requested DataOut Timeout Retries %u smaller"
+			" than minimum %u", dataout_timeout_retries,
+				NA_DATAOUT_TIMEOUT_RETRIES_MIN);
+		return -EINVAL;
+	}
+
+	a->dataout_timeout_retries = dataout_timeout_retries;
+	pr_debug("Set DataOut Timeout Retries to %u for"
+		" Initiator Node %s\n", a->dataout_timeout_retries,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_nopin_timeout(
+	struct iscsi_node_acl *acl,
+	u32 nopin_timeout)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+	struct iscsi_session *sess;
+	struct iscsi_conn *conn;
+	struct se_node_acl *se_nacl = &a->nacl->se_node_acl;
+	struct se_session *se_sess;
+	u32 orig_nopin_timeout = a->nopin_timeout;
+
+	if (nopin_timeout > NA_NOPIN_TIMEOUT_MAX) {
+		pr_err("Requested NopIn Timeout %u larger than maximum"
+			" %u\n", nopin_timeout, NA_NOPIN_TIMEOUT_MAX);
+		return -EINVAL;
+	} else if ((nopin_timeout < NA_NOPIN_TIMEOUT_MIN) &&
+		   (nopin_timeout != 0)) {
+		pr_err("Requested NopIn Timeout %u smaller than"
+			" minimum %u and not 0\n", nopin_timeout,
+			NA_NOPIN_TIMEOUT_MIN);
+		return -EINVAL;
+	}
+
+	a->nopin_timeout = nopin_timeout;
+	pr_debug("Set NopIn Timeout to %u for Initiator"
+		" Node %s\n", a->nopin_timeout,
+		iscsit_na_get_initiatorname(acl));
+	/*
+	 * Reenable disabled nopin_timeout timer for all iSCSI connections.
+	 */
+	if (!orig_nopin_timeout) {
+		spin_lock_bh(&se_nacl->nacl_sess_lock);
+		se_sess = se_nacl->nacl_sess;
+		if (se_sess) {
+			sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+
+			spin_lock(&sess->conn_lock);
+			list_for_each_entry(conn, &sess->sess_conn_list,
+					conn_list) {
+				if (conn->conn_state !=
+						TARG_CONN_STATE_LOGGED_IN)
+					continue;
+
+				spin_lock(&conn->nopin_timer_lock);
+				__iscsit_start_nopin_timer(conn);
+				spin_unlock(&conn->nopin_timer_lock);
+			}
+			spin_unlock(&sess->conn_lock);
+		}
+		spin_unlock_bh(&se_nacl->nacl_sess_lock);
+	}
+
+	return 0;
+}
+
+extern int iscsit_na_nopin_response_timeout(
+	struct iscsi_node_acl *acl,
+	u32 nopin_response_timeout)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (nopin_response_timeout > NA_NOPIN_RESPONSE_TIMEOUT_MAX) {
+		pr_err("Requested NopIn Response Timeout %u larger"
+			" than maximum %u\n", nopin_response_timeout,
+				NA_NOPIN_RESPONSE_TIMEOUT_MAX);
+		return -EINVAL;
+	} else if (nopin_response_timeout < NA_NOPIN_RESPONSE_TIMEOUT_MIN) {
+		pr_err("Requested NopIn Response Timeout %u smaller"
+			" than minimum %u\n", nopin_response_timeout,
+				NA_NOPIN_RESPONSE_TIMEOUT_MIN);
+		return -EINVAL;
+	}
+
+	a->nopin_response_timeout = nopin_response_timeout;
+	pr_debug("Set NopIn Response Timeout to %u for"
+		" Initiator Node %s\n", a->nopin_timeout,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_random_datain_pdu_offsets(
+	struct iscsi_node_acl *acl,
+	u32 random_datain_pdu_offsets)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (random_datain_pdu_offsets != 0 && random_datain_pdu_offsets != 1) {
+		pr_err("Requested Random DataIN PDU Offsets: %u not"
+			" 0 or 1\n", random_datain_pdu_offsets);
+		return -EINVAL;
+	}
+
+	a->random_datain_pdu_offsets = random_datain_pdu_offsets;
+	pr_debug("Set Random DataIN PDU Offsets to %u for"
+		" Initiator Node %s\n", a->random_datain_pdu_offsets,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_random_datain_seq_offsets(
+	struct iscsi_node_acl *acl,
+	u32 random_datain_seq_offsets)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (random_datain_seq_offsets != 0 && random_datain_seq_offsets != 1) {
+		pr_err("Requested Random DataIN Sequence Offsets: %u"
+			" not 0 or 1\n", random_datain_seq_offsets);
+		return -EINVAL;
+	}
+
+	a->random_datain_seq_offsets = random_datain_seq_offsets;
+	pr_debug("Set Random DataIN Sequence Offsets to %u for"
+		" Initiator Node %s\n", a->random_datain_seq_offsets,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_random_r2t_offsets(
+	struct iscsi_node_acl *acl,
+	u32 random_r2t_offsets)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (random_r2t_offsets != 0 && random_r2t_offsets != 1) {
+		pr_err("Requested Random R2T Offsets: %u not"
+			" 0 or 1\n", random_r2t_offsets);
+		return -EINVAL;
+	}
+
+	a->random_r2t_offsets = random_r2t_offsets;
+	pr_debug("Set Random R2T Offsets to %u for"
+		" Initiator Node %s\n", a->random_r2t_offsets,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
+
+extern int iscsit_na_default_erl(
+	struct iscsi_node_acl *acl,
+	u32 default_erl)
+{
+	struct iscsi_node_attrib *a = &acl->node_attrib;
+
+	if (default_erl != 0 && default_erl != 1 && default_erl != 2) {
+		pr_err("Requested default ERL: %u not 0, 1, or 2\n",
+				default_erl);
+		return -EINVAL;
+	}
+
+	a->default_erl = default_erl;
+	pr_debug("Set use ERL0 flag to %u for Initiator"
+		" Node %s\n", a->default_erl,
+		iscsit_na_get_initiatorname(acl));
+
+	return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.h b/drivers/target/iscsi/iscsi_target_nodeattrib.h
new file mode 100644
index 0000000..c970b326
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nodeattrib.h
@@ -0,0 +1,14 @@
+#ifndef ISCSI_TARGET_NODEATTRIB_H
+#define ISCSI_TARGET_NODEATTRIB_H
+
+extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *);
+extern int iscsit_na_dataout_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_dataout_timeout_retries(struct iscsi_node_acl *, u32);
+extern int iscsit_na_nopin_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_nopin_response_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_datain_pdu_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_datain_seq_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_r2t_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_default_erl(struct iscsi_node_acl *, u32);
+
+#endif /* ISCSI_TARGET_NODEATTRIB_H */
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
new file mode 100644
index 0000000..252e246
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -0,0 +1,1905 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI Parameter negotiation.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/slab.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_parameters.h"
+
+int iscsi_login_rx_data(
+	struct iscsi_conn *conn,
+	char *buf,
+	int length)
+{
+	int rx_got;
+	struct kvec iov;
+
+	memset(&iov, 0, sizeof(struct kvec));
+	iov.iov_len	= length;
+	iov.iov_base	= buf;
+
+	/*
+	 * Initial Marker-less Interval.
+	 * Add the values regardless of IFMarker/OFMarker, considering
+	 * it may not be negoitated yet.
+	 */
+	conn->of_marker += length;
+
+	rx_got = rx_data(conn, &iov, 1, length);
+	if (rx_got != length) {
+		pr_err("rx_data returned %d, expecting %d.\n",
+				rx_got, length);
+		return -1;
+	}
+
+	return 0 ;
+}
+
+int iscsi_login_tx_data(
+	struct iscsi_conn *conn,
+	char *pdu_buf,
+	char *text_buf,
+	int text_length)
+{
+	int length, tx_sent;
+	struct kvec iov[2];
+
+	length = (ISCSI_HDR_LEN + text_length);
+
+	memset(&iov[0], 0, 2 * sizeof(struct kvec));
+	iov[0].iov_len		= ISCSI_HDR_LEN;
+	iov[0].iov_base		= pdu_buf;
+	iov[1].iov_len		= text_length;
+	iov[1].iov_base		= text_buf;
+
+	/*
+	 * Initial Marker-less Interval.
+	 * Add the values regardless of IFMarker/OFMarker, considering
+	 * it may not be negoitated yet.
+	 */
+	conn->if_marker += length;
+
+	tx_sent = tx_data(conn, &iov[0], 2, length);
+	if (tx_sent != length) {
+		pr_err("tx_data returned %d, expecting %d.\n",
+				tx_sent, length);
+		return -1;
+	}
+
+	return 0;
+}
+
+void iscsi_dump_conn_ops(struct iscsi_conn_ops *conn_ops)
+{
+	pr_debug("HeaderDigest: %s\n", (conn_ops->HeaderDigest) ?
+				"CRC32C" : "None");
+	pr_debug("DataDigest: %s\n", (conn_ops->DataDigest) ?
+				"CRC32C" : "None");
+	pr_debug("MaxRecvDataSegmentLength: %u\n",
+				conn_ops->MaxRecvDataSegmentLength);
+	pr_debug("OFMarker: %s\n", (conn_ops->OFMarker) ? "Yes" : "No");
+	pr_debug("IFMarker: %s\n", (conn_ops->IFMarker) ? "Yes" : "No");
+	if (conn_ops->OFMarker)
+		pr_debug("OFMarkInt: %u\n", conn_ops->OFMarkInt);
+	if (conn_ops->IFMarker)
+		pr_debug("IFMarkInt: %u\n", conn_ops->IFMarkInt);
+}
+
+void iscsi_dump_sess_ops(struct iscsi_sess_ops *sess_ops)
+{
+	pr_debug("InitiatorName: %s\n", sess_ops->InitiatorName);
+	pr_debug("InitiatorAlias: %s\n", sess_ops->InitiatorAlias);
+	pr_debug("TargetName: %s\n", sess_ops->TargetName);
+	pr_debug("TargetAlias: %s\n", sess_ops->TargetAlias);
+	pr_debug("TargetPortalGroupTag: %hu\n",
+			sess_ops->TargetPortalGroupTag);
+	pr_debug("MaxConnections: %hu\n", sess_ops->MaxConnections);
+	pr_debug("InitialR2T: %s\n",
+			(sess_ops->InitialR2T) ? "Yes" : "No");
+	pr_debug("ImmediateData: %s\n", (sess_ops->ImmediateData) ?
+			"Yes" : "No");
+	pr_debug("MaxBurstLength: %u\n", sess_ops->MaxBurstLength);
+	pr_debug("FirstBurstLength: %u\n", sess_ops->FirstBurstLength);
+	pr_debug("DefaultTime2Wait: %hu\n", sess_ops->DefaultTime2Wait);
+	pr_debug("DefaultTime2Retain: %hu\n",
+			sess_ops->DefaultTime2Retain);
+	pr_debug("MaxOutstandingR2T: %hu\n",
+			sess_ops->MaxOutstandingR2T);
+	pr_debug("DataPDUInOrder: %s\n",
+			(sess_ops->DataPDUInOrder) ? "Yes" : "No");
+	pr_debug("DataSequenceInOrder: %s\n",
+			(sess_ops->DataSequenceInOrder) ? "Yes" : "No");
+	pr_debug("ErrorRecoveryLevel: %hu\n",
+			sess_ops->ErrorRecoveryLevel);
+	pr_debug("SessionType: %s\n", (sess_ops->SessionType) ?
+			"Discovery" : "Normal");
+}
+
+void iscsi_print_params(struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+
+	list_for_each_entry(param, &param_list->param_list, p_list)
+		pr_debug("%s: %s\n", param->name, param->value);
+}
+
+static struct iscsi_param *iscsi_set_default_param(struct iscsi_param_list *param_list,
+		char *name, char *value, u8 phase, u8 scope, u8 sender,
+		u16 type_range, u8 use)
+{
+	struct iscsi_param *param = NULL;
+
+	param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL);
+	if (!param) {
+		pr_err("Unable to allocate memory for parameter.\n");
+		goto out;
+	}
+	INIT_LIST_HEAD(&param->p_list);
+
+	param->name = kzalloc(strlen(name) + 1, GFP_KERNEL);
+	if (!param->name) {
+		pr_err("Unable to allocate memory for parameter name.\n");
+		goto out;
+	}
+
+	param->value = kzalloc(strlen(value) + 1, GFP_KERNEL);
+	if (!param->value) {
+		pr_err("Unable to allocate memory for parameter value.\n");
+		goto out;
+	}
+
+	memcpy(param->name, name, strlen(name));
+	param->name[strlen(name)] = '\0';
+	memcpy(param->value, value, strlen(value));
+	param->value[strlen(value)] = '\0';
+	param->phase		= phase;
+	param->scope		= scope;
+	param->sender		= sender;
+	param->use		= use;
+	param->type_range	= type_range;
+
+	switch (param->type_range) {
+	case TYPERANGE_BOOL_AND:
+		param->type = TYPE_BOOL_AND;
+		break;
+	case TYPERANGE_BOOL_OR:
+		param->type = TYPE_BOOL_OR;
+		break;
+	case TYPERANGE_0_TO_2:
+	case TYPERANGE_0_TO_3600:
+	case TYPERANGE_0_TO_32767:
+	case TYPERANGE_0_TO_65535:
+	case TYPERANGE_1_TO_65535:
+	case TYPERANGE_2_TO_3600:
+	case TYPERANGE_512_TO_16777215:
+		param->type = TYPE_NUMBER;
+		break;
+	case TYPERANGE_AUTH:
+	case TYPERANGE_DIGEST:
+		param->type = TYPE_VALUE_LIST | TYPE_STRING;
+		break;
+	case TYPERANGE_MARKINT:
+		param->type = TYPE_NUMBER_RANGE;
+		param->type_range |= TYPERANGE_1_TO_65535;
+		break;
+	case TYPERANGE_ISCSINAME:
+	case TYPERANGE_SESSIONTYPE:
+	case TYPERANGE_TARGETADDRESS:
+	case TYPERANGE_UTF8:
+		param->type = TYPE_STRING;
+		break;
+	default:
+		pr_err("Unknown type_range 0x%02x\n",
+				param->type_range);
+		goto out;
+	}
+	list_add_tail(&param->p_list, &param_list->param_list);
+
+	return param;
+out:
+	if (param) {
+		kfree(param->value);
+		kfree(param->name);
+		kfree(param);
+	}
+
+	return NULL;
+}
+
+/* #warning Add extension keys */
+int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
+{
+	struct iscsi_param *param = NULL;
+	struct iscsi_param_list *pl;
+
+	pl = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL);
+	if (!pl) {
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_param_list.\n");
+		return -1 ;
+	}
+	INIT_LIST_HEAD(&pl->param_list);
+	INIT_LIST_HEAD(&pl->extra_response_list);
+
+	/*
+	 * The format for setting the initial parameter definitions are:
+	 *
+	 * Parameter name:
+	 * Initial value:
+	 * Allowable phase:
+	 * Scope:
+	 * Allowable senders:
+	 * Typerange:
+	 * Use:
+	 */
+	param = iscsi_set_default_param(pl, AUTHMETHOD, INITIAL_AUTHMETHOD,
+			PHASE_SECURITY, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_AUTH, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, HEADERDIGEST, INITIAL_HEADERDIGEST,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_DIGEST, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, DATADIGEST, INITIAL_DATADIGEST,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_DIGEST, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, MAXCONNECTIONS,
+			INITIAL_MAXCONNECTIONS, PHASE_OPERATIONAL,
+			SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_1_TO_65535, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, SENDTARGETS, INITIAL_SENDTARGETS,
+			PHASE_FFP0, SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+			TYPERANGE_UTF8, 0);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, TARGETNAME, INITIAL_TARGETNAME,
+			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_ISCSINAME, USE_ALL);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, INITIATORNAME,
+			INITIAL_INITIATORNAME, PHASE_DECLARATIVE,
+			SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+			TYPERANGE_ISCSINAME, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, TARGETALIAS, INITIAL_TARGETALIAS,
+			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET,
+			TYPERANGE_UTF8, USE_ALL);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, INITIATORALIAS,
+			INITIAL_INITIATORALIAS, PHASE_DECLARATIVE,
+			SCOPE_SESSION_WIDE, SENDER_INITIATOR, TYPERANGE_UTF8,
+			USE_ALL);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, TARGETADDRESS,
+			INITIAL_TARGETADDRESS, PHASE_DECLARATIVE,
+			SCOPE_SESSION_WIDE, SENDER_TARGET,
+			TYPERANGE_TARGETADDRESS, USE_ALL);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, TARGETPORTALGROUPTAG,
+			INITIAL_TARGETPORTALGROUPTAG,
+			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET,
+			TYPERANGE_0_TO_65535, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, INITIALR2T, INITIAL_INITIALR2T,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_BOOL_OR, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, IMMEDIATEDATA,
+			INITIAL_IMMEDIATEDATA, PHASE_OPERATIONAL,
+			SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_AND,
+			USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, MAXRECVDATASEGMENTLENGTH,
+			INITIAL_MAXRECVDATASEGMENTLENGTH,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_512_TO_16777215, USE_ALL);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, MAXBURSTLENGTH,
+			INITIAL_MAXBURSTLENGTH, PHASE_OPERATIONAL,
+			SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_512_TO_16777215, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, FIRSTBURSTLENGTH,
+			INITIAL_FIRSTBURSTLENGTH,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_512_TO_16777215, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, DEFAULTTIME2WAIT,
+			INITIAL_DEFAULTTIME2WAIT,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_0_TO_3600, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, DEFAULTTIME2RETAIN,
+			INITIAL_DEFAULTTIME2RETAIN,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_0_TO_3600, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, MAXOUTSTANDINGR2T,
+			INITIAL_MAXOUTSTANDINGR2T,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_1_TO_65535, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, DATAPDUINORDER,
+			INITIAL_DATAPDUINORDER, PHASE_OPERATIONAL,
+			SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_OR,
+			USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, DATASEQUENCEINORDER,
+			INITIAL_DATASEQUENCEINORDER,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_BOOL_OR, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, ERRORRECOVERYLEVEL,
+			INITIAL_ERRORRECOVERYLEVEL,
+			PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+			TYPERANGE_0_TO_2, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, SESSIONTYPE, INITIAL_SESSIONTYPE,
+			PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+			TYPERANGE_SESSIONTYPE, USE_LEADING_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, IFMARKER, INITIAL_IFMARKER,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_BOOL_AND, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, OFMARKER, INITIAL_OFMARKER,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_BOOL_AND, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, IFMARKINT, INITIAL_IFMARKINT,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_MARKINT, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	param = iscsi_set_default_param(pl, OFMARKINT, INITIAL_OFMARKINT,
+			PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+			TYPERANGE_MARKINT, USE_INITIAL_ONLY);
+	if (!param)
+		goto out;
+
+	*param_list_ptr = pl;
+	return 0;
+out:
+	iscsi_release_param_list(pl);
+	return -1;
+}
+
+int iscsi_set_keys_to_negotiate(
+	int sessiontype,
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		param->state = 0;
+		if (!strcmp(param->name, AUTHMETHOD)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, HEADERDIGEST)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, DATADIGEST)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, MAXCONNECTIONS)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, TARGETNAME)) {
+			continue;
+		} else if (!strcmp(param->name, INITIATORNAME)) {
+			continue;
+		} else if (!strcmp(param->name, TARGETALIAS)) {
+			if (param->value)
+				SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, INITIATORALIAS)) {
+			continue;
+		} else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, INITIALR2T)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, IMMEDIATEDATA)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, MAXBURSTLENGTH)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, DATAPDUINORDER)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, DATASEQUENCEINORDER)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, SESSIONTYPE)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, IFMARKER)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, OFMARKER)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, IFMARKINT)) {
+			SET_PSTATE_NEGOTIATE(param);
+		} else if (!strcmp(param->name, OFMARKINT)) {
+			SET_PSTATE_NEGOTIATE(param);
+		}
+	}
+
+	return 0;
+}
+
+int iscsi_set_keys_irrelevant_for_discovery(
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!strcmp(param->name, MAXCONNECTIONS))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, INITIALR2T))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, IMMEDIATEDATA))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, MAXBURSTLENGTH))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, FIRSTBURSTLENGTH))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, MAXOUTSTANDINGR2T))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, DATAPDUINORDER))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, DATASEQUENCEINORDER))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, ERRORRECOVERYLEVEL))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, DEFAULTTIME2WAIT))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, DEFAULTTIME2RETAIN))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, IFMARKER))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, OFMARKER))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, IFMARKINT))
+			param->state &= ~PSTATE_NEGOTIATE;
+		else if (!strcmp(param->name, OFMARKINT))
+			param->state &= ~PSTATE_NEGOTIATE;
+	}
+
+	return 0;
+}
+
+int iscsi_copy_param_list(
+	struct iscsi_param_list **dst_param_list,
+	struct iscsi_param_list *src_param_list,
+	int leading)
+{
+	struct iscsi_param *new_param = NULL, *param = NULL;
+	struct iscsi_param_list *param_list = NULL;
+
+	param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL);
+	if (!param_list) {
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_param_list.\n");
+		goto err_out;
+	}
+	INIT_LIST_HEAD(&param_list->param_list);
+	INIT_LIST_HEAD(&param_list->extra_response_list);
+
+	list_for_each_entry(param, &src_param_list->param_list, p_list) {
+		if (!leading && (param->scope & SCOPE_SESSION_WIDE)) {
+			if ((strcmp(param->name, "TargetName") != 0) &&
+			    (strcmp(param->name, "InitiatorName") != 0) &&
+			    (strcmp(param->name, "TargetPortalGroupTag") != 0))
+				continue;
+		}
+
+		new_param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL);
+		if (!new_param) {
+			pr_err("Unable to allocate memory for"
+				" struct iscsi_param.\n");
+			goto err_out;
+		}
+
+		new_param->set_param = param->set_param;
+		new_param->phase = param->phase;
+		new_param->scope = param->scope;
+		new_param->sender = param->sender;
+		new_param->type = param->type;
+		new_param->use = param->use;
+		new_param->type_range = param->type_range;
+
+		new_param->name = kzalloc(strlen(param->name) + 1, GFP_KERNEL);
+		if (!new_param->name) {
+			pr_err("Unable to allocate memory for"
+				" parameter name.\n");
+			goto err_out;
+		}
+
+		new_param->value = kzalloc(strlen(param->value) + 1,
+				GFP_KERNEL);
+		if (!new_param->value) {
+			pr_err("Unable to allocate memory for"
+				" parameter value.\n");
+			goto err_out;
+		}
+
+		memcpy(new_param->name, param->name, strlen(param->name));
+		new_param->name[strlen(param->name)] = '\0';
+		memcpy(new_param->value, param->value, strlen(param->value));
+		new_param->value[strlen(param->value)] = '\0';
+
+		list_add_tail(&new_param->p_list, &param_list->param_list);
+	}
+
+	if (!list_empty(&param_list->param_list))
+		*dst_param_list = param_list;
+	else {
+		pr_err("No parameters allocated.\n");
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	iscsi_release_param_list(param_list);
+	return -1;
+}
+
+static void iscsi_release_extra_responses(struct iscsi_param_list *param_list)
+{
+	struct iscsi_extra_response *er, *er_tmp;
+
+	list_for_each_entry_safe(er, er_tmp, &param_list->extra_response_list,
+			er_list) {
+		list_del(&er->er_list);
+		kfree(er);
+	}
+}
+
+void iscsi_release_param_list(struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param, *param_tmp;
+
+	list_for_each_entry_safe(param, param_tmp, &param_list->param_list,
+			p_list) {
+		list_del(&param->p_list);
+
+		kfree(param->name);
+		param->name = NULL;
+		kfree(param->value);
+		param->value = NULL;
+		kfree(param);
+		param = NULL;
+	}
+
+	iscsi_release_extra_responses(param_list);
+
+	kfree(param_list);
+}
+
+struct iscsi_param *iscsi_find_param_from_key(
+	char *key,
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+
+	if (!key || !param_list) {
+		pr_err("Key or parameter list pointer is NULL.\n");
+		return NULL;
+	}
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!strcmp(key, param->name))
+			return param;
+	}
+
+	pr_err("Unable to locate key \"%s\".\n", key);
+	return NULL;
+}
+
+int iscsi_extract_key_value(char *textbuf, char **key, char **value)
+{
+	*value = strchr(textbuf, '=');
+	if (!*value) {
+		pr_err("Unable to locate \"=\" seperator for key,"
+				" ignoring request.\n");
+		return -1;
+	}
+
+	*key = textbuf;
+	**value = '\0';
+	*value = *value + 1;
+
+	return 0;
+}
+
+int iscsi_update_param_value(struct iscsi_param *param, char *value)
+{
+	kfree(param->value);
+
+	param->value = kzalloc(strlen(value) + 1, GFP_KERNEL);
+	if (!param->value) {
+		pr_err("Unable to allocate memory for value.\n");
+		return -1;
+	}
+
+	memcpy(param->value, value, strlen(value));
+	param->value[strlen(value)] = '\0';
+
+	pr_debug("iSCSI Parameter updated to %s=%s\n",
+			param->name, param->value);
+	return 0;
+}
+
+static int iscsi_add_notunderstood_response(
+	char *key,
+	char *value,
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_extra_response *extra_response;
+
+	if (strlen(value) > VALUE_MAXLEN) {
+		pr_err("Value for notunderstood key \"%s\" exceeds %d,"
+			" protocol error.\n", key, VALUE_MAXLEN);
+		return -1;
+	}
+
+	extra_response = kzalloc(sizeof(struct iscsi_extra_response), GFP_KERNEL);
+	if (!extra_response) {
+		pr_err("Unable to allocate memory for"
+			" struct iscsi_extra_response.\n");
+		return -1;
+	}
+	INIT_LIST_HEAD(&extra_response->er_list);
+
+	strncpy(extra_response->key, key, strlen(key) + 1);
+	strncpy(extra_response->value, NOTUNDERSTOOD,
+			strlen(NOTUNDERSTOOD) + 1);
+
+	list_add_tail(&extra_response->er_list,
+			&param_list->extra_response_list);
+	return 0;
+}
+
+static int iscsi_check_for_auth_key(char *key)
+{
+	/*
+	 * RFC 1994
+	 */
+	if (!strcmp(key, "CHAP_A") || !strcmp(key, "CHAP_I") ||
+	    !strcmp(key, "CHAP_C") || !strcmp(key, "CHAP_N") ||
+	    !strcmp(key, "CHAP_R"))
+		return 1;
+
+	/*
+	 * RFC 2945
+	 */
+	if (!strcmp(key, "SRP_U") || !strcmp(key, "SRP_N") ||
+	    !strcmp(key, "SRP_g") || !strcmp(key, "SRP_s") ||
+	    !strcmp(key, "SRP_A") || !strcmp(key, "SRP_B") ||
+	    !strcmp(key, "SRP_M") || !strcmp(key, "SRP_HM"))
+		return 1;
+
+	return 0;
+}
+
+static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param)
+{
+	if (IS_TYPE_BOOL_AND(param)) {
+		if (!strcmp(param->value, NO))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+	} else if (IS_TYPE_BOOL_OR(param)) {
+		if (!strcmp(param->value, YES))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		 /*
+		  * Required for gPXE iSCSI boot client
+		  */
+		if (!strcmp(param->name, IMMEDIATEDATA))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+	} else if (IS_TYPE_NUMBER(param)) {
+		if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		/*
+		 * The GlobalSAN iSCSI Initiator for MacOSX does
+		 * not respond to MaxBurstLength, FirstBurstLength,
+		 * DefaultTime2Wait or DefaultTime2Retain parameter keys.
+		 * So, we set them to 'reply optional' here, and assume the
+		 * the defaults from iscsi_parameters.h if the initiator
+		 * is not RFC compliant and the keys are not negotiated.
+		 */
+		if (!strcmp(param->name, MAXBURSTLENGTH))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		if (!strcmp(param->name, FIRSTBURSTLENGTH))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		if (!strcmp(param->name, DEFAULTTIME2WAIT))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		if (!strcmp(param->name, DEFAULTTIME2RETAIN))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+		/*
+		 * Required for gPXE iSCSI boot client
+		 */
+		if (!strcmp(param->name, MAXCONNECTIONS))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+	} else if (IS_PHASE_DECLARATIVE(param))
+		SET_PSTATE_REPLY_OPTIONAL(param);
+}
+
+static int iscsi_check_boolean_value(struct iscsi_param *param, char *value)
+{
+	if (strcmp(value, YES) && strcmp(value, NO)) {
+		pr_err("Illegal value for \"%s\", must be either"
+			" \"%s\" or \"%s\".\n", param->name, YES, NO);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int iscsi_check_numerical_value(struct iscsi_param *param, char *value_ptr)
+{
+	char *tmpptr;
+	int value = 0;
+
+	value = simple_strtoul(value_ptr, &tmpptr, 0);
+
+/* #warning FIXME: Fix this */
+#if 0
+	if (strspn(endptr, WHITE_SPACE) != strlen(endptr)) {
+		pr_err("Illegal value \"%s\" for \"%s\".\n",
+			value, param->name);
+		return -1;
+	}
+#endif
+	if (IS_TYPERANGE_0_TO_2(param)) {
+		if ((value < 0) || (value > 2)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 0 and 2.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_0_TO_3600(param)) {
+		if ((value < 0) || (value > 3600)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 0 and 3600.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_0_TO_32767(param)) {
+		if ((value < 0) || (value > 32767)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 0 and 32767.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_0_TO_65535(param)) {
+		if ((value < 0) || (value > 65535)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 0 and 65535.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_1_TO_65535(param)) {
+		if ((value < 1) || (value > 65535)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 1 and 65535.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_2_TO_3600(param)) {
+		if ((value < 2) || (value > 3600)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 2 and 3600.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+	if (IS_TYPERANGE_512_TO_16777215(param)) {
+		if ((value < 512) || (value > 16777215)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" between 512 and 16777215.\n", param->name);
+			return -1;
+		}
+		return 0;
+	}
+
+	return 0;
+}
+
+static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *value)
+{
+	char *left_val_ptr = NULL, *right_val_ptr = NULL;
+	char *tilde_ptr = NULL, *tmp_ptr = NULL;
+	u32 left_val, right_val, local_left_val, local_right_val;
+
+	if (strcmp(param->name, IFMARKINT) &&
+	    strcmp(param->name, OFMARKINT)) {
+		pr_err("Only parameters \"%s\" or \"%s\" may contain a"
+		       " numerical range value.\n", IFMARKINT, OFMARKINT);
+		return -1;
+	}
+
+	if (IS_PSTATE_PROPOSER(param))
+		return 0;
+
+	tilde_ptr = strchr(value, '~');
+	if (!tilde_ptr) {
+		pr_err("Unable to locate numerical range indicator"
+			" \"~\" for \"%s\".\n", param->name);
+		return -1;
+	}
+	*tilde_ptr = '\0';
+
+	left_val_ptr = value;
+	right_val_ptr = value + strlen(left_val_ptr) + 1;
+
+	if (iscsi_check_numerical_value(param, left_val_ptr) < 0)
+		return -1;
+	if (iscsi_check_numerical_value(param, right_val_ptr) < 0)
+		return -1;
+
+	left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
+	right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
+	*tilde_ptr = '~';
+
+	if (right_val < left_val) {
+		pr_err("Numerical range for parameter \"%s\" contains"
+			" a right value which is less than the left.\n",
+				param->name);
+		return -1;
+	}
+
+	/*
+	 * For now,  enforce reasonable defaults for [I,O]FMarkInt.
+	 */
+	tilde_ptr = strchr(param->value, '~');
+	if (!tilde_ptr) {
+		pr_err("Unable to locate numerical range indicator"
+			" \"~\" for \"%s\".\n", param->name);
+		return -1;
+	}
+	*tilde_ptr = '\0';
+
+	left_val_ptr = param->value;
+	right_val_ptr = param->value + strlen(left_val_ptr) + 1;
+
+	local_left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
+	local_right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
+	*tilde_ptr = '~';
+
+	if (param->set_param) {
+		if ((left_val < local_left_val) ||
+		    (right_val < local_left_val)) {
+			pr_err("Passed value range \"%u~%u\" is below"
+				" minimum left value \"%u\" for key \"%s\","
+				" rejecting.\n", left_val, right_val,
+				local_left_val, param->name);
+			return -1;
+		}
+	} else {
+		if ((left_val < local_left_val) &&
+		    (right_val < local_left_val)) {
+			pr_err("Received value range \"%u~%u\" is"
+				" below minimum left value \"%u\" for key"
+				" \"%s\", rejecting.\n", left_val, right_val,
+				local_left_val, param->name);
+			SET_PSTATE_REJECT(param);
+			if (iscsi_update_param_value(param, REJECT) < 0)
+				return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int iscsi_check_string_or_list_value(struct iscsi_param *param, char *value)
+{
+	if (IS_PSTATE_PROPOSER(param))
+		return 0;
+
+	if (IS_TYPERANGE_AUTH_PARAM(param)) {
+		if (strcmp(value, KRB5) && strcmp(value, SPKM1) &&
+		    strcmp(value, SPKM2) && strcmp(value, SRP) &&
+		    strcmp(value, CHAP) && strcmp(value, NONE)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" \"%s\", \"%s\", \"%s\", \"%s\", \"%s\""
+				" or \"%s\".\n", param->name, KRB5,
+					SPKM1, SPKM2, SRP, CHAP, NONE);
+			return -1;
+		}
+	}
+	if (IS_TYPERANGE_DIGEST_PARAM(param)) {
+		if (strcmp(value, CRC32C) && strcmp(value, NONE)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" \"%s\" or \"%s\".\n", param->name,
+					CRC32C, NONE);
+			return -1;
+		}
+	}
+	if (IS_TYPERANGE_SESSIONTYPE(param)) {
+		if (strcmp(value, DISCOVERY) && strcmp(value, NORMAL)) {
+			pr_err("Illegal value for \"%s\", must be"
+				" \"%s\" or \"%s\".\n", param->name,
+					DISCOVERY, NORMAL);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ *	This function is used to pick a value range number,  currently just
+ *	returns the lesser of both right values.
+ */
+static char *iscsi_get_value_from_number_range(
+	struct iscsi_param *param,
+	char *value)
+{
+	char *end_ptr, *tilde_ptr1 = NULL, *tilde_ptr2 = NULL;
+	u32 acceptor_right_value, proposer_right_value;
+
+	tilde_ptr1 = strchr(value, '~');
+	if (!tilde_ptr1)
+		return NULL;
+	*tilde_ptr1++ = '\0';
+	proposer_right_value = simple_strtoul(tilde_ptr1, &end_ptr, 0);
+
+	tilde_ptr2 = strchr(param->value, '~');
+	if (!tilde_ptr2)
+		return NULL;
+	*tilde_ptr2++ = '\0';
+	acceptor_right_value = simple_strtoul(tilde_ptr2, &end_ptr, 0);
+
+	return (acceptor_right_value >= proposer_right_value) ?
+		tilde_ptr1 : tilde_ptr2;
+}
+
+static char *iscsi_check_valuelist_for_support(
+	struct iscsi_param *param,
+	char *value)
+{
+	char *tmp1 = NULL, *tmp2 = NULL;
+	char *acceptor_values = NULL, *proposer_values = NULL;
+
+	acceptor_values = param->value;
+	proposer_values = value;
+
+	do {
+		if (!proposer_values)
+			return NULL;
+		tmp1 = strchr(proposer_values, ',');
+		if (tmp1)
+			*tmp1 = '\0';
+		acceptor_values = param->value;
+		do {
+			if (!acceptor_values) {
+				if (tmp1)
+					*tmp1 = ',';
+				return NULL;
+			}
+			tmp2 = strchr(acceptor_values, ',');
+			if (tmp2)
+				*tmp2 = '\0';
+			if (!acceptor_values || !proposer_values) {
+				if (tmp1)
+					*tmp1 = ',';
+				if (tmp2)
+					*tmp2 = ',';
+				return NULL;
+			}
+			if (!strcmp(acceptor_values, proposer_values)) {
+				if (tmp2)
+					*tmp2 = ',';
+				goto out;
+			}
+			if (tmp2)
+				*tmp2++ = ',';
+
+			acceptor_values = tmp2;
+			if (!acceptor_values)
+				break;
+		} while (acceptor_values);
+		if (tmp1)
+			*tmp1++ = ',';
+		proposer_values = tmp1;
+	} while (proposer_values);
+
+out:
+	return proposer_values;
+}
+
+static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value)
+{
+	u8 acceptor_boolean_value = 0, proposer_boolean_value = 0;
+	char *negoitated_value = NULL;
+
+	if (IS_PSTATE_ACCEPTOR(param)) {
+		pr_err("Received key \"%s\" twice, protocol error.\n",
+				param->name);
+		return -1;
+	}
+
+	if (IS_PSTATE_REJECT(param))
+		return 0;
+
+	if (IS_TYPE_BOOL_AND(param)) {
+		if (!strcmp(value, YES))
+			proposer_boolean_value = 1;
+		if (!strcmp(param->value, YES))
+			acceptor_boolean_value = 1;
+		if (acceptor_boolean_value && proposer_boolean_value)
+			do {} while (0);
+		else {
+			if (iscsi_update_param_value(param, NO) < 0)
+				return -1;
+			if (!proposer_boolean_value)
+				SET_PSTATE_REPLY_OPTIONAL(param);
+		}
+	} else if (IS_TYPE_BOOL_OR(param)) {
+		if (!strcmp(value, YES))
+			proposer_boolean_value = 1;
+		if (!strcmp(param->value, YES))
+			acceptor_boolean_value = 1;
+		if (acceptor_boolean_value || proposer_boolean_value) {
+			if (iscsi_update_param_value(param, YES) < 0)
+				return -1;
+			if (proposer_boolean_value)
+				SET_PSTATE_REPLY_OPTIONAL(param);
+		}
+	} else if (IS_TYPE_NUMBER(param)) {
+		char *tmpptr, buf[10];
+		u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0);
+		u32 proposer_value = simple_strtoul(value, &tmpptr, 0);
+
+		memset(buf, 0, 10);
+
+		if (!strcmp(param->name, MAXCONNECTIONS) ||
+		    !strcmp(param->name, MAXBURSTLENGTH) ||
+		    !strcmp(param->name, FIRSTBURSTLENGTH) ||
+		    !strcmp(param->name, MAXOUTSTANDINGR2T) ||
+		    !strcmp(param->name, DEFAULTTIME2RETAIN) ||
+		    !strcmp(param->name, ERRORRECOVERYLEVEL)) {
+			if (proposer_value > acceptor_value) {
+				sprintf(buf, "%u", acceptor_value);
+				if (iscsi_update_param_value(param,
+						&buf[0]) < 0)
+					return -1;
+			} else {
+				if (iscsi_update_param_value(param, value) < 0)
+					return -1;
+			}
+		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+			if (acceptor_value > proposer_value) {
+				sprintf(buf, "%u", acceptor_value);
+				if (iscsi_update_param_value(param,
+						&buf[0]) < 0)
+					return -1;
+			} else {
+				if (iscsi_update_param_value(param, value) < 0)
+					return -1;
+			}
+		} else {
+			if (iscsi_update_param_value(param, value) < 0)
+				return -1;
+		}
+
+		if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
+			SET_PSTATE_REPLY_OPTIONAL(param);
+	} else if (IS_TYPE_NUMBER_RANGE(param)) {
+		negoitated_value = iscsi_get_value_from_number_range(
+					param, value);
+		if (!negoitated_value)
+			return -1;
+		if (iscsi_update_param_value(param, negoitated_value) < 0)
+			return -1;
+	} else if (IS_TYPE_VALUE_LIST(param)) {
+		negoitated_value = iscsi_check_valuelist_for_support(
+					param, value);
+		if (!negoitated_value) {
+			pr_err("Proposer's value list \"%s\" contains"
+				" no valid values from Acceptor's value list"
+				" \"%s\".\n", value, param->value);
+			return -1;
+		}
+		if (iscsi_update_param_value(param, negoitated_value) < 0)
+			return -1;
+	} else if (IS_PHASE_DECLARATIVE(param)) {
+		if (iscsi_update_param_value(param, value) < 0)
+			return -1;
+		SET_PSTATE_REPLY_OPTIONAL(param);
+	}
+
+	return 0;
+}
+
+static int iscsi_check_proposer_state(struct iscsi_param *param, char *value)
+{
+	if (IS_PSTATE_RESPONSE_GOT(param)) {
+		pr_err("Received key \"%s\" twice, protocol error.\n",
+				param->name);
+		return -1;
+	}
+
+	if (IS_TYPE_NUMBER_RANGE(param)) {
+		u32 left_val = 0, right_val = 0, recieved_value = 0;
+		char *left_val_ptr = NULL, *right_val_ptr = NULL;
+		char *tilde_ptr = NULL, *tmp_ptr = NULL;
+
+		if (!strcmp(value, IRRELEVANT) || !strcmp(value, REJECT)) {
+			if (iscsi_update_param_value(param, value) < 0)
+				return -1;
+			return 0;
+		}
+
+		tilde_ptr = strchr(value, '~');
+		if (tilde_ptr) {
+			pr_err("Illegal \"~\" in response for \"%s\".\n",
+					param->name);
+			return -1;
+		}
+		tilde_ptr = strchr(param->value, '~');
+		if (!tilde_ptr) {
+			pr_err("Unable to locate numerical range"
+				" indicator \"~\" for \"%s\".\n", param->name);
+			return -1;
+		}
+		*tilde_ptr = '\0';
+
+		left_val_ptr = param->value;
+		right_val_ptr = param->value + strlen(left_val_ptr) + 1;
+		left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
+		right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
+		recieved_value = simple_strtoul(value, &tmp_ptr, 0);
+
+		*tilde_ptr = '~';
+
+		if ((recieved_value < left_val) ||
+		    (recieved_value > right_val)) {
+			pr_err("Illegal response \"%s=%u\", value must"
+				" be between %u and %u.\n", param->name,
+				recieved_value, left_val, right_val);
+			return -1;
+		}
+	} else if (IS_TYPE_VALUE_LIST(param)) {
+		char *comma_ptr = NULL, *tmp_ptr = NULL;
+
+		comma_ptr = strchr(value, ',');
+		if (comma_ptr) {
+			pr_err("Illegal \",\" in response for \"%s\".\n",
+					param->name);
+			return -1;
+		}
+
+		tmp_ptr = iscsi_check_valuelist_for_support(param, value);
+		if (!tmp_ptr)
+			return -1;
+	}
+
+	if (iscsi_update_param_value(param, value) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int iscsi_check_value(struct iscsi_param *param, char *value)
+{
+	char *comma_ptr = NULL;
+
+	if (!strcmp(value, REJECT)) {
+		if (!strcmp(param->name, IFMARKINT) ||
+		    !strcmp(param->name, OFMARKINT)) {
+			/*
+			 * Reject is not fatal for [I,O]FMarkInt,  and causes
+			 * [I,O]FMarker to be reset to No. (See iSCSI v20 A.3.2)
+			 */
+			SET_PSTATE_REJECT(param);
+			return 0;
+		}
+		pr_err("Received %s=%s\n", param->name, value);
+		return -1;
+	}
+	if (!strcmp(value, IRRELEVANT)) {
+		pr_debug("Received %s=%s\n", param->name, value);
+		SET_PSTATE_IRRELEVANT(param);
+		return 0;
+	}
+	if (!strcmp(value, NOTUNDERSTOOD)) {
+		if (!IS_PSTATE_PROPOSER(param)) {
+			pr_err("Received illegal offer %s=%s\n",
+				param->name, value);
+			return -1;
+		}
+
+/* #warning FIXME: Add check for X-ExtensionKey here */
+		pr_err("Standard iSCSI key \"%s\" cannot be answered"
+			" with \"%s\", protocol error.\n", param->name, value);
+		return -1;
+	}
+
+	do {
+		comma_ptr = NULL;
+		comma_ptr = strchr(value, ',');
+
+		if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) {
+			pr_err("Detected value seperator \",\", but"
+				" key \"%s\" does not allow a value list,"
+				" protocol error.\n", param->name);
+			return -1;
+		}
+		if (comma_ptr)
+			*comma_ptr = '\0';
+
+		if (strlen(value) > VALUE_MAXLEN) {
+			pr_err("Value for key \"%s\" exceeds %d,"
+				" protocol error.\n", param->name,
+				VALUE_MAXLEN);
+			return -1;
+		}
+
+		if (IS_TYPE_BOOL_AND(param) || IS_TYPE_BOOL_OR(param)) {
+			if (iscsi_check_boolean_value(param, value) < 0)
+				return -1;
+		} else if (IS_TYPE_NUMBER(param)) {
+			if (iscsi_check_numerical_value(param, value) < 0)
+				return -1;
+		} else if (IS_TYPE_NUMBER_RANGE(param)) {
+			if (iscsi_check_numerical_range_value(param, value) < 0)
+				return -1;
+		} else if (IS_TYPE_STRING(param) || IS_TYPE_VALUE_LIST(param)) {
+			if (iscsi_check_string_or_list_value(param, value) < 0)
+				return -1;
+		} else {
+			pr_err("Huh? 0x%02x\n", param->type);
+			return -1;
+		}
+
+		if (comma_ptr)
+			*comma_ptr++ = ',';
+
+		value = comma_ptr;
+	} while (value);
+
+	return 0;
+}
+
+static struct iscsi_param *__iscsi_check_key(
+	char *key,
+	int sender,
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+
+	if (strlen(key) > KEY_MAXLEN) {
+		pr_err("Length of key name \"%s\" exceeds %d.\n",
+			key, KEY_MAXLEN);
+		return NULL;
+	}
+
+	param = iscsi_find_param_from_key(key, param_list);
+	if (!param)
+		return NULL;
+
+	if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) {
+		pr_err("Key \"%s\" may not be sent to %s,"
+			" protocol error.\n", param->name,
+			(sender & SENDER_RECEIVER) ? "target" : "initiator");
+		return NULL;
+	}
+
+	if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) {
+		pr_err("Key \"%s\" may not be sent to %s,"
+			" protocol error.\n", param->name,
+			(sender & SENDER_RECEIVER) ? "initiator" : "target");
+		return NULL;
+	}
+
+	return param;
+}
+
+static struct iscsi_param *iscsi_check_key(
+	char *key,
+	int phase,
+	int sender,
+	struct iscsi_param_list *param_list)
+{
+	struct iscsi_param *param;
+	/*
+	 * Key name length must not exceed 63 bytes. (See iSCSI v20 5.1)
+	 */
+	if (strlen(key) > KEY_MAXLEN) {
+		pr_err("Length of key name \"%s\" exceeds %d.\n",
+			key, KEY_MAXLEN);
+		return NULL;
+	}
+
+	param = iscsi_find_param_from_key(key, param_list);
+	if (!param)
+		return NULL;
+
+	if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) {
+		pr_err("Key \"%s\" may not be sent to %s,"
+			" protocol error.\n", param->name,
+			(sender & SENDER_RECEIVER) ? "target" : "initiator");
+		return NULL;
+	}
+	if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) {
+		pr_err("Key \"%s\" may not be sent to %s,"
+				" protocol error.\n", param->name,
+			(sender & SENDER_RECEIVER) ? "initiator" : "target");
+		return NULL;
+	}
+
+	if (IS_PSTATE_ACCEPTOR(param)) {
+		pr_err("Key \"%s\" received twice, protocol error.\n",
+				key);
+		return NULL;
+	}
+
+	if (!phase)
+		return param;
+
+	if (!(param->phase & phase)) {
+		pr_err("Key \"%s\" may not be negotiated during ",
+				param->name);
+		switch (phase) {
+		case PHASE_SECURITY:
+			pr_debug("Security phase.\n");
+			break;
+		case PHASE_OPERATIONAL:
+			pr_debug("Operational phase.\n");
+		default:
+			pr_debug("Unknown phase.\n");
+		}
+		return NULL;
+	}
+
+	return param;
+}
+
+static int iscsi_enforce_integrity_rules(
+	u8 phase,
+	struct iscsi_param_list *param_list)
+{
+	char *tmpptr;
+	u8 DataSequenceInOrder = 0;
+	u8 ErrorRecoveryLevel = 0, SessionType = 0;
+	u8 IFMarker = 0, OFMarker = 0;
+	u8 IFMarkInt_Reject = 0, OFMarkInt_Reject = 0;
+	u32 FirstBurstLength = 0, MaxBurstLength = 0;
+	struct iscsi_param *param = NULL;
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!(param->phase & phase))
+			continue;
+		if (!strcmp(param->name, SESSIONTYPE))
+			if (!strcmp(param->value, NORMAL))
+				SessionType = 1;
+		if (!strcmp(param->name, ERRORRECOVERYLEVEL))
+			ErrorRecoveryLevel = simple_strtoul(param->value,
+					&tmpptr, 0);
+		if (!strcmp(param->name, DATASEQUENCEINORDER))
+			if (!strcmp(param->value, YES))
+				DataSequenceInOrder = 1;
+		if (!strcmp(param->name, MAXBURSTLENGTH))
+			MaxBurstLength = simple_strtoul(param->value,
+					&tmpptr, 0);
+		if (!strcmp(param->name, IFMARKER))
+			if (!strcmp(param->value, YES))
+				IFMarker = 1;
+		if (!strcmp(param->name, OFMARKER))
+			if (!strcmp(param->value, YES))
+				OFMarker = 1;
+		if (!strcmp(param->name, IFMARKINT))
+			if (!strcmp(param->value, REJECT))
+				IFMarkInt_Reject = 1;
+		if (!strcmp(param->name, OFMARKINT))
+			if (!strcmp(param->value, REJECT))
+				OFMarkInt_Reject = 1;
+	}
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!(param->phase & phase))
+			continue;
+		if (!SessionType && (!IS_PSTATE_ACCEPTOR(param) &&
+		     (strcmp(param->name, IFMARKER) &&
+		      strcmp(param->name, OFMARKER) &&
+		      strcmp(param->name, IFMARKINT) &&
+		      strcmp(param->name, OFMARKINT))))
+			continue;
+		if (!strcmp(param->name, MAXOUTSTANDINGR2T) &&
+		    DataSequenceInOrder && (ErrorRecoveryLevel > 0)) {
+			if (strcmp(param->value, "1")) {
+				if (iscsi_update_param_value(param, "1") < 0)
+					return -1;
+				pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+			}
+		}
+		if (!strcmp(param->name, MAXCONNECTIONS) && !SessionType) {
+			if (strcmp(param->value, "1")) {
+				if (iscsi_update_param_value(param, "1") < 0)
+					return -1;
+				pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+			}
+		}
+		if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+			FirstBurstLength = simple_strtoul(param->value,
+					&tmpptr, 0);
+			if (FirstBurstLength > MaxBurstLength) {
+				char tmpbuf[10];
+				memset(tmpbuf, 0, 10);
+				sprintf(tmpbuf, "%u", MaxBurstLength);
+				if (iscsi_update_param_value(param, tmpbuf))
+					return -1;
+				pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+			}
+		}
+		if (!strcmp(param->name, IFMARKER) && IFMarkInt_Reject) {
+			if (iscsi_update_param_value(param, NO) < 0)
+				return -1;
+			IFMarker = 0;
+			pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+		}
+		if (!strcmp(param->name, OFMARKER) && OFMarkInt_Reject) {
+			if (iscsi_update_param_value(param, NO) < 0)
+				return -1;
+			OFMarker = 0;
+			pr_debug("Reset \"%s\" to \"%s\".\n",
+					 param->name, param->value);
+		}
+		if (!strcmp(param->name, IFMARKINT) && !IFMarker) {
+			if (!strcmp(param->value, REJECT))
+				continue;
+			param->state &= ~PSTATE_NEGOTIATE;
+			if (iscsi_update_param_value(param, IRRELEVANT) < 0)
+				return -1;
+			pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+		}
+		if (!strcmp(param->name, OFMARKINT) && !OFMarker) {
+			if (!strcmp(param->value, REJECT))
+				continue;
+			param->state &= ~PSTATE_NEGOTIATE;
+			if (iscsi_update_param_value(param, IRRELEVANT) < 0)
+				return -1;
+			pr_debug("Reset \"%s\" to \"%s\".\n",
+					param->name, param->value);
+		}
+	}
+
+	return 0;
+}
+
+int iscsi_decode_text_input(
+	u8 phase,
+	u8 sender,
+	char *textbuf,
+	u32 length,
+	struct iscsi_param_list *param_list)
+{
+	char *tmpbuf, *start = NULL, *end = NULL;
+
+	tmpbuf = kzalloc(length + 1, GFP_KERNEL);
+	if (!tmpbuf) {
+		pr_err("Unable to allocate memory for tmpbuf.\n");
+		return -1;
+	}
+
+	memcpy(tmpbuf, textbuf, length);
+	tmpbuf[length] = '\0';
+	start = tmpbuf;
+	end = (start + length);
+
+	while (start < end) {
+		char *key, *value;
+		struct iscsi_param *param;
+
+		if (iscsi_extract_key_value(start, &key, &value) < 0) {
+			kfree(tmpbuf);
+			return -1;
+		}
+
+		pr_debug("Got key: %s=%s\n", key, value);
+
+		if (phase & PHASE_SECURITY) {
+			if (iscsi_check_for_auth_key(key) > 0) {
+				char *tmpptr = key + strlen(key);
+				*tmpptr = '=';
+				kfree(tmpbuf);
+				return 1;
+			}
+		}
+
+		param = iscsi_check_key(key, phase, sender, param_list);
+		if (!param) {
+			if (iscsi_add_notunderstood_response(key,
+					value, param_list) < 0) {
+				kfree(tmpbuf);
+				return -1;
+			}
+			start += strlen(key) + strlen(value) + 2;
+			continue;
+		}
+		if (iscsi_check_value(param, value) < 0) {
+			kfree(tmpbuf);
+			return -1;
+		}
+
+		start += strlen(key) + strlen(value) + 2;
+
+		if (IS_PSTATE_PROPOSER(param)) {
+			if (iscsi_check_proposer_state(param, value) < 0) {
+				kfree(tmpbuf);
+				return -1;
+			}
+			SET_PSTATE_RESPONSE_GOT(param);
+		} else {
+			if (iscsi_check_acceptor_state(param, value) < 0) {
+				kfree(tmpbuf);
+				return -1;
+			}
+			SET_PSTATE_ACCEPTOR(param);
+		}
+	}
+
+	kfree(tmpbuf);
+	return 0;
+}
+
+int iscsi_encode_text_output(
+	u8 phase,
+	u8 sender,
+	char *textbuf,
+	u32 *length,
+	struct iscsi_param_list *param_list)
+{
+	char *output_buf = NULL;
+	struct iscsi_extra_response *er;
+	struct iscsi_param *param;
+
+	output_buf = textbuf + *length;
+
+	if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
+		return -1;
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!(param->sender & sender))
+			continue;
+		if (IS_PSTATE_ACCEPTOR(param) &&
+		    !IS_PSTATE_RESPONSE_SENT(param) &&
+		    !IS_PSTATE_REPLY_OPTIONAL(param) &&
+		    (param->phase & phase)) {
+			*length += sprintf(output_buf, "%s=%s",
+				param->name, param->value);
+			*length += 1;
+			output_buf = textbuf + *length;
+			SET_PSTATE_RESPONSE_SENT(param);
+			pr_debug("Sending key: %s=%s\n",
+				param->name, param->value);
+			continue;
+		}
+		if (IS_PSTATE_NEGOTIATE(param) &&
+		    !IS_PSTATE_ACCEPTOR(param) &&
+		    !IS_PSTATE_PROPOSER(param) &&
+		    (param->phase & phase)) {
+			*length += sprintf(output_buf, "%s=%s",
+				param->name, param->value);
+			*length += 1;
+			output_buf = textbuf + *length;
+			SET_PSTATE_PROPOSER(param);
+			iscsi_check_proposer_for_optional_reply(param);
+			pr_debug("Sending key: %s=%s\n",
+				param->name, param->value);
+		}
+	}
+
+	list_for_each_entry(er, &param_list->extra_response_list, er_list) {
+		*length += sprintf(output_buf, "%s=%s", er->key, er->value);
+		*length += 1;
+		output_buf = textbuf + *length;
+		pr_debug("Sending key: %s=%s\n", er->key, er->value);
+	}
+	iscsi_release_extra_responses(param_list);
+
+	return 0;
+}
+
+int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)
+{
+	int ret = 0;
+	struct iscsi_param *param;
+
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (IS_PSTATE_NEGOTIATE(param) &&
+		    IS_PSTATE_PROPOSER(param) &&
+		    !IS_PSTATE_RESPONSE_GOT(param) &&
+		    !IS_PSTATE_REPLY_OPTIONAL(param) &&
+		    !IS_PHASE_DECLARATIVE(param)) {
+			pr_err("No response for proposed key \"%s\".\n",
+					param->name);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+int iscsi_change_param_value(
+	char *keyvalue,
+	struct iscsi_param_list *param_list,
+	int check_key)
+{
+	char *key = NULL, *value = NULL;
+	struct iscsi_param *param;
+	int sender = 0;
+
+	if (iscsi_extract_key_value(keyvalue, &key, &value) < 0)
+		return -1;
+
+	if (!check_key) {
+		param = __iscsi_check_key(keyvalue, sender, param_list);
+		if (!param)
+			return -1;
+	} else {
+		param = iscsi_check_key(keyvalue, 0, sender, param_list);
+		if (!param)
+			return -1;
+
+		param->set_param = 1;
+		if (iscsi_check_value(param, value) < 0) {
+			param->set_param = 0;
+			return -1;
+		}
+		param->set_param = 0;
+	}
+
+	if (iscsi_update_param_value(param, value) < 0)
+		return -1;
+
+	return 0;
+}
+
+void iscsi_set_connection_parameters(
+	struct iscsi_conn_ops *ops,
+	struct iscsi_param_list *param_list)
+{
+	char *tmpptr;
+	struct iscsi_param *param;
+
+	pr_debug("---------------------------------------------------"
+			"---------------\n");
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param))
+			continue;
+		if (!strcmp(param->name, AUTHMETHOD)) {
+			pr_debug("AuthMethod:                   %s\n",
+				param->value);
+		} else if (!strcmp(param->name, HEADERDIGEST)) {
+			ops->HeaderDigest = !strcmp(param->value, CRC32C);
+			pr_debug("HeaderDigest:                 %s\n",
+				param->value);
+		} else if (!strcmp(param->name, DATADIGEST)) {
+			ops->DataDigest = !strcmp(param->value, CRC32C);
+			pr_debug("DataDigest:                   %s\n",
+				param->value);
+		} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
+			ops->MaxRecvDataSegmentLength =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("MaxRecvDataSegmentLength:     %s\n",
+				param->value);
+		} else if (!strcmp(param->name, OFMARKER)) {
+			ops->OFMarker = !strcmp(param->value, YES);
+			pr_debug("OFMarker:                     %s\n",
+				param->value);
+		} else if (!strcmp(param->name, IFMARKER)) {
+			ops->IFMarker = !strcmp(param->value, YES);
+			pr_debug("IFMarker:                     %s\n",
+				param->value);
+		} else if (!strcmp(param->name, OFMARKINT)) {
+			ops->OFMarkInt =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("OFMarkInt:                    %s\n",
+				param->value);
+		} else if (!strcmp(param->name, IFMARKINT)) {
+			ops->IFMarkInt =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("IFMarkInt:                    %s\n",
+				param->value);
+		}
+	}
+	pr_debug("----------------------------------------------------"
+			"--------------\n");
+}
+
+void iscsi_set_session_parameters(
+	struct iscsi_sess_ops *ops,
+	struct iscsi_param_list *param_list,
+	int leading)
+{
+	char *tmpptr;
+	struct iscsi_param *param;
+
+	pr_debug("----------------------------------------------------"
+			"--------------\n");
+	list_for_each_entry(param, &param_list->param_list, p_list) {
+		if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param))
+			continue;
+		if (!strcmp(param->name, INITIATORNAME)) {
+			if (!param->value)
+				continue;
+			if (leading)
+				snprintf(ops->InitiatorName,
+						sizeof(ops->InitiatorName),
+						"%s", param->value);
+			pr_debug("InitiatorName:                %s\n",
+				param->value);
+		} else if (!strcmp(param->name, INITIATORALIAS)) {
+			if (!param->value)
+				continue;
+			snprintf(ops->InitiatorAlias,
+						sizeof(ops->InitiatorAlias),
+						"%s", param->value);
+			pr_debug("InitiatorAlias:               %s\n",
+				param->value);
+		} else if (!strcmp(param->name, TARGETNAME)) {
+			if (!param->value)
+				continue;
+			if (leading)
+				snprintf(ops->TargetName,
+						sizeof(ops->TargetName),
+						"%s", param->value);
+			pr_debug("TargetName:                   %s\n",
+				param->value);
+		} else if (!strcmp(param->name, TARGETALIAS)) {
+			if (!param->value)
+				continue;
+			snprintf(ops->TargetAlias, sizeof(ops->TargetAlias),
+					"%s", param->value);
+			pr_debug("TargetAlias:                  %s\n",
+				param->value);
+		} else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) {
+			ops->TargetPortalGroupTag =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("TargetPortalGroupTag:         %s\n",
+				param->value);
+		} else if (!strcmp(param->name, MAXCONNECTIONS)) {
+			ops->MaxConnections =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("MaxConnections:               %s\n",
+				param->value);
+		} else if (!strcmp(param->name, INITIALR2T)) {
+			ops->InitialR2T = !strcmp(param->value, YES);
+			 pr_debug("InitialR2T:                   %s\n",
+				param->value);
+		} else if (!strcmp(param->name, IMMEDIATEDATA)) {
+			ops->ImmediateData = !strcmp(param->value, YES);
+			pr_debug("ImmediateData:                %s\n",
+				param->value);
+		} else if (!strcmp(param->name, MAXBURSTLENGTH)) {
+			ops->MaxBurstLength =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("MaxBurstLength:               %s\n",
+				param->value);
+		} else if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+			ops->FirstBurstLength =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("FirstBurstLength:             %s\n",
+				param->value);
+		} else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+			ops->DefaultTime2Wait =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("DefaultTime2Wait:             %s\n",
+				param->value);
+		} else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) {
+			ops->DefaultTime2Retain =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("DefaultTime2Retain:           %s\n",
+				param->value);
+		} else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) {
+			ops->MaxOutstandingR2T =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("MaxOutstandingR2T:            %s\n",
+				param->value);
+		} else if (!strcmp(param->name, DATAPDUINORDER)) {
+			ops->DataPDUInOrder = !strcmp(param->value, YES);
+			pr_debug("DataPDUInOrder:               %s\n",
+				param->value);
+		} else if (!strcmp(param->name, DATASEQUENCEINORDER)) {
+			ops->DataSequenceInOrder = !strcmp(param->value, YES);
+			pr_debug("DataSequenceInOrder:          %s\n",
+				param->value);
+		} else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) {
+			ops->ErrorRecoveryLevel =
+				simple_strtoul(param->value, &tmpptr, 0);
+			pr_debug("ErrorRecoveryLevel:           %s\n",
+				param->value);
+		} else if (!strcmp(param->name, SESSIONTYPE)) {
+			ops->SessionType = !strcmp(param->value, DISCOVERY);
+			pr_debug("SessionType:                  %s\n",
+				param->value);
+		}
+	}
+	pr_debug("----------------------------------------------------"
+			"--------------\n");
+
+}
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h
new file mode 100644
index 0000000..6a37fd6
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_parameters.h
@@ -0,0 +1,269 @@
+#ifndef ISCSI_PARAMETERS_H
+#define ISCSI_PARAMETERS_H
+
+struct iscsi_extra_response {
+	char key[64];
+	char value[32];
+	struct list_head er_list;
+} ____cacheline_aligned;
+
+struct iscsi_param {
+	char *name;
+	char *value;
+	u8 set_param;
+	u8 phase;
+	u8 scope;
+	u8 sender;
+	u8 type;
+	u8 use;
+	u16 type_range;
+	u32 state;
+	struct list_head p_list;
+} ____cacheline_aligned;
+
+extern int iscsi_login_rx_data(struct iscsi_conn *, char *, int);
+extern int iscsi_login_tx_data(struct iscsi_conn *, char *, char *, int);
+extern void iscsi_dump_conn_ops(struct iscsi_conn_ops *);
+extern void iscsi_dump_sess_ops(struct iscsi_sess_ops *);
+extern void iscsi_print_params(struct iscsi_param_list *);
+extern int iscsi_create_default_params(struct iscsi_param_list **);
+extern int iscsi_set_keys_to_negotiate(int, struct iscsi_param_list *);
+extern int iscsi_set_keys_irrelevant_for_discovery(struct iscsi_param_list *);
+extern int iscsi_copy_param_list(struct iscsi_param_list **,
+			struct iscsi_param_list *, int);
+extern int iscsi_change_param_value(char *, struct iscsi_param_list *, int);
+extern void iscsi_release_param_list(struct iscsi_param_list *);
+extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_list *);
+extern int iscsi_extract_key_value(char *, char **, char **);
+extern int iscsi_update_param_value(struct iscsi_param *, char *);
+extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_param_list *);
+extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
+			struct iscsi_param_list *);
+extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
+extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,
+			struct iscsi_param_list *);
+extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,
+			struct iscsi_param_list *, int);
+
+#define YES				"Yes"
+#define NO				"No"
+#define ALL				"All"
+#define IRRELEVANT			"Irrelevant"
+#define NONE				"None"
+#define NOTUNDERSTOOD			"NotUnderstood"
+#define REJECT				"Reject"
+
+/*
+ * The Parameter Names.
+ */
+#define AUTHMETHOD			"AuthMethod"
+#define HEADERDIGEST			"HeaderDigest"
+#define DATADIGEST			"DataDigest"
+#define MAXCONNECTIONS			"MaxConnections"
+#define SENDTARGETS			"SendTargets"
+#define TARGETNAME			"TargetName"
+#define INITIATORNAME			"InitiatorName"
+#define TARGETALIAS			"TargetAlias"
+#define INITIATORALIAS			"InitiatorAlias"
+#define TARGETADDRESS			"TargetAddress"
+#define TARGETPORTALGROUPTAG		"TargetPortalGroupTag"
+#define INITIALR2T			"InitialR2T"
+#define IMMEDIATEDATA			"ImmediateData"
+#define MAXRECVDATASEGMENTLENGTH	"MaxRecvDataSegmentLength"
+#define MAXBURSTLENGTH			"MaxBurstLength"
+#define FIRSTBURSTLENGTH		"FirstBurstLength"
+#define DEFAULTTIME2WAIT		"DefaultTime2Wait"
+#define DEFAULTTIME2RETAIN		"DefaultTime2Retain"
+#define MAXOUTSTANDINGR2T		"MaxOutstandingR2T"
+#define DATAPDUINORDER			"DataPDUInOrder"
+#define DATASEQUENCEINORDER		"DataSequenceInOrder"
+#define ERRORRECOVERYLEVEL		"ErrorRecoveryLevel"
+#define SESSIONTYPE			"SessionType"
+#define IFMARKER			"IFMarker"
+#define OFMARKER			"OFMarker"
+#define IFMARKINT			"IFMarkInt"
+#define OFMARKINT			"OFMarkInt"
+#define X_EXTENSIONKEY			"X-com.sbei.version"
+#define X_EXTENSIONKEY_CISCO_NEW	"X-com.cisco.protocol"
+#define X_EXTENSIONKEY_CISCO_OLD	"X-com.cisco.iscsi.draft"
+
+/*
+ * For AuthMethod.
+ */
+#define KRB5				"KRB5"
+#define SPKM1				"SPKM1"
+#define SPKM2				"SPKM2"
+#define SRP				"SRP"
+#define CHAP				"CHAP"
+
+/*
+ * Initial values for Parameter Negotiation.
+ */
+#define INITIAL_AUTHMETHOD			CHAP
+#define INITIAL_HEADERDIGEST			"CRC32C,None"
+#define INITIAL_DATADIGEST			"CRC32C,None"
+#define INITIAL_MAXCONNECTIONS			"1"
+#define INITIAL_SENDTARGETS			ALL
+#define INITIAL_TARGETNAME			"LIO.Target"
+#define INITIAL_INITIATORNAME			"LIO.Initiator"
+#define INITIAL_TARGETALIAS			"LIO Target"
+#define INITIAL_INITIATORALIAS			"LIO Initiator"
+#define INITIAL_TARGETADDRESS			"0.0.0.0:0000,0"
+#define INITIAL_TARGETPORTALGROUPTAG		"1"
+#define INITIAL_INITIALR2T			YES
+#define INITIAL_IMMEDIATEDATA			YES
+#define INITIAL_MAXRECVDATASEGMENTLENGTH	"8192"
+#define INITIAL_MAXBURSTLENGTH			"262144"
+#define INITIAL_FIRSTBURSTLENGTH		"65536"
+#define INITIAL_DEFAULTTIME2WAIT		"2"
+#define INITIAL_DEFAULTTIME2RETAIN		"20"
+#define INITIAL_MAXOUTSTANDINGR2T		"1"
+#define INITIAL_DATAPDUINORDER			YES
+#define INITIAL_DATASEQUENCEINORDER		YES
+#define INITIAL_ERRORRECOVERYLEVEL		"0"
+#define INITIAL_SESSIONTYPE			NORMAL
+#define INITIAL_IFMARKER			NO
+#define INITIAL_OFMARKER			NO
+#define INITIAL_IFMARKINT			"2048~65535"
+#define INITIAL_OFMARKINT			"2048~65535"
+
+/*
+ * For [Header,Data]Digests.
+ */
+#define CRC32C				"CRC32C"
+
+/*
+ * For SessionType.
+ */
+#define DISCOVERY			"Discovery"
+#define NORMAL				"Normal"
+
+/*
+ * struct iscsi_param->use
+ */
+#define USE_LEADING_ONLY		0x01
+#define USE_INITIAL_ONLY		0x02
+#define USE_ALL				0x04
+
+#define IS_USE_LEADING_ONLY(p)		((p)->use & USE_LEADING_ONLY)
+#define IS_USE_INITIAL_ONLY(p)		((p)->use & USE_INITIAL_ONLY)
+#define IS_USE_ALL(p)			((p)->use & USE_ALL)
+
+#define SET_USE_INITIAL_ONLY(p)		((p)->use |= USE_INITIAL_ONLY)
+
+/*
+ * struct iscsi_param->sender
+ */
+#define	SENDER_INITIATOR		0x01
+#define SENDER_TARGET			0x02
+#define SENDER_BOTH			0x03
+/* Used in iscsi_check_key() */
+#define SENDER_RECEIVER			0x04
+
+#define IS_SENDER_INITIATOR(p)		((p)->sender & SENDER_INITIATOR)
+#define IS_SENDER_TARGET(p)		((p)->sender & SENDER_TARGET)
+#define IS_SENDER_BOTH(p)		((p)->sender & SENDER_BOTH)
+
+/*
+ * struct iscsi_param->scope
+ */
+#define SCOPE_CONNECTION_ONLY		0x01
+#define SCOPE_SESSION_WIDE		0x02
+
+#define IS_SCOPE_CONNECTION_ONLY(p)	((p)->scope & SCOPE_CONNECTION_ONLY)
+#define IS_SCOPE_SESSION_WIDE(p)	((p)->scope & SCOPE_SESSION_WIDE)
+
+/*
+ * struct iscsi_param->phase
+ */
+#define PHASE_SECURITY			0x01
+#define PHASE_OPERATIONAL		0x02
+#define PHASE_DECLARATIVE		0x04
+#define PHASE_FFP0			0x08
+
+#define IS_PHASE_SECURITY(p)		((p)->phase & PHASE_SECURITY)
+#define IS_PHASE_OPERATIONAL(p)		((p)->phase & PHASE_OPERATIONAL)
+#define IS_PHASE_DECLARATIVE(p)		((p)->phase & PHASE_DECLARATIVE)
+#define IS_PHASE_FFP0(p)		((p)->phase & PHASE_FFP0)
+
+/*
+ * struct iscsi_param->type
+ */
+#define TYPE_BOOL_AND			0x01
+#define TYPE_BOOL_OR			0x02
+#define TYPE_NUMBER			0x04
+#define TYPE_NUMBER_RANGE		0x08
+#define TYPE_STRING			0x10
+#define TYPE_VALUE_LIST			0x20
+
+#define IS_TYPE_BOOL_AND(p)		((p)->type & TYPE_BOOL_AND)
+#define IS_TYPE_BOOL_OR(p)		((p)->type & TYPE_BOOL_OR)
+#define IS_TYPE_NUMBER(p)		((p)->type & TYPE_NUMBER)
+#define IS_TYPE_NUMBER_RANGE(p)		((p)->type & TYPE_NUMBER_RANGE)
+#define IS_TYPE_STRING(p)		((p)->type & TYPE_STRING)
+#define IS_TYPE_VALUE_LIST(p)		((p)->type & TYPE_VALUE_LIST)
+
+/*
+ * struct iscsi_param->type_range
+ */
+#define TYPERANGE_BOOL_AND		0x0001
+#define TYPERANGE_BOOL_OR		0x0002
+#define TYPERANGE_0_TO_2		0x0004
+#define TYPERANGE_0_TO_3600		0x0008
+#define TYPERANGE_0_TO_32767		0x0010
+#define TYPERANGE_0_TO_65535		0x0020
+#define TYPERANGE_1_TO_65535		0x0040
+#define TYPERANGE_2_TO_3600		0x0080
+#define TYPERANGE_512_TO_16777215	0x0100
+#define TYPERANGE_AUTH			0x0200
+#define TYPERANGE_DIGEST		0x0400
+#define TYPERANGE_ISCSINAME		0x0800
+#define TYPERANGE_MARKINT		0x1000
+#define TYPERANGE_SESSIONTYPE		0x2000
+#define TYPERANGE_TARGETADDRESS		0x4000
+#define TYPERANGE_UTF8			0x8000
+
+#define IS_TYPERANGE_0_TO_2(p)		((p)->type_range & TYPERANGE_0_TO_2)
+#define IS_TYPERANGE_0_TO_3600(p)	((p)->type_range & TYPERANGE_0_TO_3600)
+#define IS_TYPERANGE_0_TO_32767(p)	((p)->type_range & TYPERANGE_0_TO_32767)
+#define IS_TYPERANGE_0_TO_65535(p)	((p)->type_range & TYPERANGE_0_TO_65535)
+#define IS_TYPERANGE_1_TO_65535(p)	((p)->type_range & TYPERANGE_1_TO_65535)
+#define IS_TYPERANGE_2_TO_3600(p)	((p)->type_range & TYPERANGE_2_TO_3600)
+#define IS_TYPERANGE_512_TO_16777215(p)	((p)->type_range & \
+						TYPERANGE_512_TO_16777215)
+#define IS_TYPERANGE_AUTH_PARAM(p)	((p)->type_range & TYPERANGE_AUTH)
+#define IS_TYPERANGE_DIGEST_PARAM(p)	((p)->type_range & TYPERANGE_DIGEST)
+#define IS_TYPERANGE_SESSIONTYPE(p)	((p)->type_range & \
+						TYPERANGE_SESSIONTYPE)
+
+/*
+ * struct iscsi_param->state
+ */
+#define PSTATE_ACCEPTOR			0x01
+#define PSTATE_NEGOTIATE		0x02
+#define PSTATE_PROPOSER			0x04
+#define PSTATE_IRRELEVANT		0x08
+#define PSTATE_REJECT			0x10
+#define PSTATE_REPLY_OPTIONAL		0x20
+#define PSTATE_RESPONSE_GOT		0x40
+#define PSTATE_RESPONSE_SENT		0x80
+
+#define IS_PSTATE_ACCEPTOR(p)		((p)->state & PSTATE_ACCEPTOR)
+#define IS_PSTATE_NEGOTIATE(p)		((p)->state & PSTATE_NEGOTIATE)
+#define IS_PSTATE_PROPOSER(p)		((p)->state & PSTATE_PROPOSER)
+#define IS_PSTATE_IRRELEVANT(p)		((p)->state & PSTATE_IRRELEVANT)
+#define IS_PSTATE_REJECT(p)		((p)->state & PSTATE_REJECT)
+#define IS_PSTATE_REPLY_OPTIONAL(p)	((p)->state & PSTATE_REPLY_OPTIONAL)
+#define IS_PSTATE_RESPONSE_GOT(p)	((p)->state & PSTATE_RESPONSE_GOT)
+#define IS_PSTATE_RESPONSE_SENT(p)	((p)->state & PSTATE_RESPONSE_SENT)
+
+#define SET_PSTATE_ACCEPTOR(p)		((p)->state |= PSTATE_ACCEPTOR)
+#define SET_PSTATE_NEGOTIATE(p)		((p)->state |= PSTATE_NEGOTIATE)
+#define SET_PSTATE_PROPOSER(p)		((p)->state |= PSTATE_PROPOSER)
+#define SET_PSTATE_IRRELEVANT(p)	((p)->state |= PSTATE_IRRELEVANT)
+#define SET_PSTATE_REJECT(p)		((p)->state |= PSTATE_REJECT)
+#define SET_PSTATE_REPLY_OPTIONAL(p)	((p)->state |= PSTATE_REPLY_OPTIONAL)
+#define SET_PSTATE_RESPONSE_GOT(p)	((p)->state |= PSTATE_RESPONSE_GOT)
+#define SET_PSTATE_RESPONSE_SENT(p)	((p)->state |= PSTATE_RESPONSE_SENT)
+
+#endif /* ISCSI_PARAMETERS_H */
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.c b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c
new file mode 100644
index 0000000..fc69408
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c
@@ -0,0 +1,664 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI DataSequenceInOrder=No
+ * and DataPDUInOrder=No.
+ *
+ \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/random.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_seq_pdu_list.h"
+
+#define OFFLOAD_BUF_SIZE	32768
+
+void iscsit_dump_seq_list(struct iscsi_cmd *cmd)
+{
+	int i;
+	struct iscsi_seq *seq;
+
+	pr_debug("Dumping Sequence List for ITT: 0x%08x:\n",
+			cmd->init_task_tag);
+
+	for (i = 0; i < cmd->seq_count; i++) {
+		seq = &cmd->seq_list[i];
+		pr_debug("i: %d, pdu_start: %d, pdu_count: %d,"
+			" offset: %d, xfer_len: %d, seq_send_order: %d,"
+			" seq_no: %d\n", i, seq->pdu_start, seq->pdu_count,
+			seq->offset, seq->xfer_len, seq->seq_send_order,
+			seq->seq_no);
+	}
+}
+
+void iscsit_dump_pdu_list(struct iscsi_cmd *cmd)
+{
+	int i;
+	struct iscsi_pdu *pdu;
+
+	pr_debug("Dumping PDU List for ITT: 0x%08x:\n",
+			cmd->init_task_tag);
+
+	for (i = 0; i < cmd->pdu_count; i++) {
+		pdu = &cmd->pdu_list[i];
+		pr_debug("i: %d, offset: %d, length: %d,"
+			" pdu_send_order: %d, seq_no: %d\n", i, pdu->offset,
+			pdu->length, pdu->pdu_send_order, pdu->seq_no);
+	}
+}
+
+static void iscsit_ordered_seq_lists(
+	struct iscsi_cmd *cmd,
+	u8 type)
+{
+	u32 i, seq_count = 0;
+
+	for (i = 0; i < cmd->seq_count; i++) {
+		if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+			continue;
+		cmd->seq_list[i].seq_send_order = seq_count++;
+	}
+}
+
+static void iscsit_ordered_pdu_lists(
+	struct iscsi_cmd *cmd,
+	u8 type)
+{
+	u32 i, pdu_send_order = 0, seq_no = 0;
+
+	for (i = 0; i < cmd->pdu_count; i++) {
+redo:
+		if (cmd->pdu_list[i].seq_no == seq_no) {
+			cmd->pdu_list[i].pdu_send_order = pdu_send_order++;
+			continue;
+		}
+		seq_no++;
+		pdu_send_order = 0;
+		goto redo;
+	}
+}
+
+/*
+ *	Generate count random values into array.
+ *	Use 0x80000000 to mark generates valued in array[].
+ */
+static void iscsit_create_random_array(u32 *array, u32 count)
+{
+	int i, j, k;
+
+	if (count == 1) {
+		array[0] = 0;
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+redo:
+		get_random_bytes(&j, sizeof(u32));
+		j = (1 + (int) (9999 + 1) - j) % count;
+		for (k = 0; k < i + 1; k++) {
+			j |= 0x80000000;
+			if ((array[k] & 0x80000000) && (array[k] == j))
+				goto redo;
+		}
+		array[i] = j;
+	}
+
+	for (i = 0; i < count; i++)
+		array[i] &= ~0x80000000;
+}
+
+static int iscsit_randomize_pdu_lists(
+	struct iscsi_cmd *cmd,
+	u8 type)
+{
+	int i = 0;
+	u32 *array, pdu_count, seq_count = 0, seq_no = 0, seq_offset = 0;
+
+	for (pdu_count = 0; pdu_count < cmd->pdu_count; pdu_count++) {
+redo:
+		if (cmd->pdu_list[pdu_count].seq_no == seq_no) {
+			seq_count++;
+			continue;
+		}
+		array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
+		if (!array) {
+			pr_err("Unable to allocate memory"
+				" for random array.\n");
+			return -1;
+		}
+		iscsit_create_random_array(array, seq_count);
+
+		for (i = 0; i < seq_count; i++)
+			cmd->pdu_list[seq_offset+i].pdu_send_order = array[i];
+
+		kfree(array);
+
+		seq_offset += seq_count;
+		seq_count = 0;
+		seq_no++;
+		goto redo;
+	}
+
+	if (seq_count) {
+		array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
+		if (!array) {
+			pr_err("Unable to allocate memory for"
+				" random array.\n");
+			return -1;
+		}
+		iscsit_create_random_array(array, seq_count);
+
+		for (i = 0; i < seq_count; i++)
+			cmd->pdu_list[seq_offset+i].pdu_send_order = array[i];
+
+		kfree(array);
+	}
+
+	return 0;
+}
+
+static int iscsit_randomize_seq_lists(
+	struct iscsi_cmd *cmd,
+	u8 type)
+{
+	int i, j = 0;
+	u32 *array, seq_count = cmd->seq_count;
+
+	if ((type == PDULIST_IMMEDIATE) || (type == PDULIST_UNSOLICITED))
+		seq_count--;
+	else if (type == PDULIST_IMMEDIATE_AND_UNSOLICITED)
+		seq_count -= 2;
+
+	if (!seq_count)
+		return 0;
+
+	array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
+	if (!array) {
+		pr_err("Unable to allocate memory for random array.\n");
+		return -1;
+	}
+	iscsit_create_random_array(array, seq_count);
+
+	for (i = 0; i < cmd->seq_count; i++) {
+		if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+			continue;
+		cmd->seq_list[i].seq_send_order = array[j++];
+	}
+
+	kfree(array);
+	return 0;
+}
+
+static void iscsit_determine_counts_for_list(
+	struct iscsi_cmd *cmd,
+	struct iscsi_build_list *bl,
+	u32 *seq_count,
+	u32 *pdu_count)
+{
+	int check_immediate = 0;
+	u32 burstlength = 0, offset = 0;
+	u32 unsolicited_data_length = 0;
+	struct iscsi_conn *conn = cmd->conn;
+
+	if ((bl->type == PDULIST_IMMEDIATE) ||
+	    (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+		check_immediate = 1;
+
+	if ((bl->type == PDULIST_UNSOLICITED) ||
+	    (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+		unsolicited_data_length = (cmd->data_length >
+			conn->sess->sess_ops->FirstBurstLength) ?
+			conn->sess->sess_ops->FirstBurstLength : cmd->data_length;
+
+	while (offset < cmd->data_length) {
+		*pdu_count += 1;
+
+		if (check_immediate) {
+			check_immediate = 0;
+			offset += bl->immediate_data_length;
+			*seq_count += 1;
+			if (unsolicited_data_length)
+				unsolicited_data_length -=
+					bl->immediate_data_length;
+			continue;
+		}
+		if (unsolicited_data_length > 0) {
+			if ((offset + conn->conn_ops->MaxRecvDataSegmentLength)
+					>= cmd->data_length) {
+				unsolicited_data_length -=
+					(cmd->data_length - offset);
+				offset += (cmd->data_length - offset);
+				continue;
+			}
+			if ((offset + conn->conn_ops->MaxRecvDataSegmentLength)
+					>= conn->sess->sess_ops->FirstBurstLength) {
+				unsolicited_data_length -=
+					(conn->sess->sess_ops->FirstBurstLength -
+					offset);
+				offset += (conn->sess->sess_ops->FirstBurstLength -
+					offset);
+				burstlength = 0;
+				*seq_count += 1;
+				continue;
+			}
+
+			offset += conn->conn_ops->MaxRecvDataSegmentLength;
+			unsolicited_data_length -=
+				conn->conn_ops->MaxRecvDataSegmentLength;
+			continue;
+		}
+		if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
+		     cmd->data_length) {
+			offset += (cmd->data_length - offset);
+			continue;
+		}
+		if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >=
+		     conn->sess->sess_ops->MaxBurstLength) {
+			offset += (conn->sess->sess_ops->MaxBurstLength -
+					burstlength);
+			burstlength = 0;
+			*seq_count += 1;
+			continue;
+		}
+
+		burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
+		offset += conn->conn_ops->MaxRecvDataSegmentLength;
+	}
+}
+
+
+/*
+ *	Builds PDU and/or Sequence list,  called while DataSequenceInOrder=No
+ *	and DataPDUInOrder=No.
+ */
+static int iscsit_build_pdu_and_seq_list(
+	struct iscsi_cmd *cmd,
+	struct iscsi_build_list *bl)
+{
+	int check_immediate = 0, datapduinorder, datasequenceinorder;
+	u32 burstlength = 0, offset = 0, i = 0;
+	u32 pdu_count = 0, seq_no = 0, unsolicited_data_length = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *pdu = cmd->pdu_list;
+	struct iscsi_seq *seq = cmd->seq_list;
+
+	datapduinorder = conn->sess->sess_ops->DataPDUInOrder;
+	datasequenceinorder = conn->sess->sess_ops->DataSequenceInOrder;
+
+	if ((bl->type == PDULIST_IMMEDIATE) ||
+	    (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+		check_immediate = 1;
+
+	if ((bl->type == PDULIST_UNSOLICITED) ||
+	    (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+		unsolicited_data_length = (cmd->data_length >
+			conn->sess->sess_ops->FirstBurstLength) ?
+			conn->sess->sess_ops->FirstBurstLength : cmd->data_length;
+
+	while (offset < cmd->data_length) {
+		pdu_count++;
+		if (!datapduinorder) {
+			pdu[i].offset = offset;
+			pdu[i].seq_no = seq_no;
+		}
+		if (!datasequenceinorder && (pdu_count == 1)) {
+			seq[seq_no].pdu_start = i;
+			seq[seq_no].seq_no = seq_no;
+			seq[seq_no].offset = offset;
+			seq[seq_no].orig_offset = offset;
+		}
+
+		if (check_immediate) {
+			check_immediate = 0;
+			if (!datapduinorder) {
+				pdu[i].type = PDUTYPE_IMMEDIATE;
+				pdu[i++].length = bl->immediate_data_length;
+			}
+			if (!datasequenceinorder) {
+				seq[seq_no].type = SEQTYPE_IMMEDIATE;
+				seq[seq_no].pdu_count = 1;
+				seq[seq_no].xfer_len =
+					bl->immediate_data_length;
+			}
+			offset += bl->immediate_data_length;
+			pdu_count = 0;
+			seq_no++;
+			if (unsolicited_data_length)
+				unsolicited_data_length -=
+					bl->immediate_data_length;
+			continue;
+		}
+		if (unsolicited_data_length > 0) {
+			if ((offset +
+			     conn->conn_ops->MaxRecvDataSegmentLength) >=
+			     cmd->data_length) {
+				if (!datapduinorder) {
+					pdu[i].type = PDUTYPE_UNSOLICITED;
+					pdu[i].length =
+						(cmd->data_length - offset);
+				}
+				if (!datasequenceinorder) {
+					seq[seq_no].type = SEQTYPE_UNSOLICITED;
+					seq[seq_no].pdu_count = pdu_count;
+					seq[seq_no].xfer_len = (burstlength +
+						(cmd->data_length - offset));
+				}
+				unsolicited_data_length -=
+						(cmd->data_length - offset);
+				offset += (cmd->data_length - offset);
+				continue;
+			}
+			if ((offset +
+			     conn->conn_ops->MaxRecvDataSegmentLength) >=
+					conn->sess->sess_ops->FirstBurstLength) {
+				if (!datapduinorder) {
+					pdu[i].type = PDUTYPE_UNSOLICITED;
+					pdu[i++].length =
+					   (conn->sess->sess_ops->FirstBurstLength -
+						offset);
+				}
+				if (!datasequenceinorder) {
+					seq[seq_no].type = SEQTYPE_UNSOLICITED;
+					seq[seq_no].pdu_count = pdu_count;
+					seq[seq_no].xfer_len = (burstlength +
+					   (conn->sess->sess_ops->FirstBurstLength -
+						offset));
+				}
+				unsolicited_data_length -=
+					(conn->sess->sess_ops->FirstBurstLength -
+						offset);
+				offset += (conn->sess->sess_ops->FirstBurstLength -
+						offset);
+				burstlength = 0;
+				pdu_count = 0;
+				seq_no++;
+				continue;
+			}
+
+			if (!datapduinorder) {
+				pdu[i].type = PDUTYPE_UNSOLICITED;
+				pdu[i++].length =
+				     conn->conn_ops->MaxRecvDataSegmentLength;
+			}
+			burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
+			offset += conn->conn_ops->MaxRecvDataSegmentLength;
+			unsolicited_data_length -=
+				conn->conn_ops->MaxRecvDataSegmentLength;
+			continue;
+		}
+		if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
+		     cmd->data_length) {
+			if (!datapduinorder) {
+				pdu[i].type = PDUTYPE_NORMAL;
+				pdu[i].length = (cmd->data_length - offset);
+			}
+			if (!datasequenceinorder) {
+				seq[seq_no].type = SEQTYPE_NORMAL;
+				seq[seq_no].pdu_count = pdu_count;
+				seq[seq_no].xfer_len = (burstlength +
+					(cmd->data_length - offset));
+			}
+			offset += (cmd->data_length - offset);
+			continue;
+		}
+		if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >=
+		     conn->sess->sess_ops->MaxBurstLength) {
+			if (!datapduinorder) {
+				pdu[i].type = PDUTYPE_NORMAL;
+				pdu[i++].length =
+					(conn->sess->sess_ops->MaxBurstLength -
+						burstlength);
+			}
+			if (!datasequenceinorder) {
+				seq[seq_no].type = SEQTYPE_NORMAL;
+				seq[seq_no].pdu_count = pdu_count;
+				seq[seq_no].xfer_len = (burstlength +
+					(conn->sess->sess_ops->MaxBurstLength -
+					burstlength));
+			}
+			offset += (conn->sess->sess_ops->MaxBurstLength -
+					burstlength);
+			burstlength = 0;
+			pdu_count = 0;
+			seq_no++;
+			continue;
+		}
+
+		if (!datapduinorder) {
+			pdu[i].type = PDUTYPE_NORMAL;
+			pdu[i++].length =
+				conn->conn_ops->MaxRecvDataSegmentLength;
+		}
+		burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
+		offset += conn->conn_ops->MaxRecvDataSegmentLength;
+	}
+
+	if (!datasequenceinorder) {
+		if (bl->data_direction & ISCSI_PDU_WRITE) {
+			if (bl->randomize & RANDOM_R2T_OFFSETS) {
+				if (iscsit_randomize_seq_lists(cmd, bl->type)
+						< 0)
+					return -1;
+			} else
+				iscsit_ordered_seq_lists(cmd, bl->type);
+		} else if (bl->data_direction & ISCSI_PDU_READ) {
+			if (bl->randomize & RANDOM_DATAIN_SEQ_OFFSETS) {
+				if (iscsit_randomize_seq_lists(cmd, bl->type)
+						< 0)
+					return -1;
+			} else
+				iscsit_ordered_seq_lists(cmd, bl->type);
+		}
+#if 0
+		iscsit_dump_seq_list(cmd);
+#endif
+	}
+	if (!datapduinorder) {
+		if (bl->data_direction & ISCSI_PDU_WRITE) {
+			if (bl->randomize & RANDOM_DATAOUT_PDU_OFFSETS) {
+				if (iscsit_randomize_pdu_lists(cmd, bl->type)
+						< 0)
+					return -1;
+			} else
+				iscsit_ordered_pdu_lists(cmd, bl->type);
+		} else if (bl->data_direction & ISCSI_PDU_READ) {
+			if (bl->randomize & RANDOM_DATAIN_PDU_OFFSETS) {
+				if (iscsit_randomize_pdu_lists(cmd, bl->type)
+						< 0)
+					return -1;
+			} else
+				iscsit_ordered_pdu_lists(cmd, bl->type);
+		}
+#if 0
+		iscsit_dump_pdu_list(cmd);
+#endif
+	}
+
+	return 0;
+}
+
+/*
+ *	Only called while DataSequenceInOrder=No or DataPDUInOrder=No.
+ */
+int iscsit_do_build_list(
+	struct iscsi_cmd *cmd,
+	struct iscsi_build_list *bl)
+{
+	u32 pdu_count = 0, seq_count = 1;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *pdu = NULL;
+	struct iscsi_seq *seq = NULL;
+
+	iscsit_determine_counts_for_list(cmd, bl, &seq_count, &pdu_count);
+
+	if (!conn->sess->sess_ops->DataSequenceInOrder) {
+		seq = kzalloc(seq_count * sizeof(struct iscsi_seq), GFP_ATOMIC);
+		if (!seq) {
+			pr_err("Unable to allocate struct iscsi_seq list\n");
+			return -1;
+		}
+		cmd->seq_list = seq;
+		cmd->seq_count = seq_count;
+	}
+
+	if (!conn->sess->sess_ops->DataPDUInOrder) {
+		pdu = kzalloc(pdu_count * sizeof(struct iscsi_pdu), GFP_ATOMIC);
+		if (!pdu) {
+			pr_err("Unable to allocate struct iscsi_pdu list.\n");
+			kfree(seq);
+			return -1;
+		}
+		cmd->pdu_list = pdu;
+		cmd->pdu_count = pdu_count;
+	}
+
+	return iscsit_build_pdu_and_seq_list(cmd, bl);
+}
+
+struct iscsi_pdu *iscsit_get_pdu_holder(
+	struct iscsi_cmd *cmd,
+	u32 offset,
+	u32 length)
+{
+	u32 i;
+	struct iscsi_pdu *pdu = NULL;
+
+	if (!cmd->pdu_list) {
+		pr_err("struct iscsi_cmd->pdu_list is NULL!\n");
+		return NULL;
+	}
+
+	pdu = &cmd->pdu_list[0];
+
+	for (i = 0; i < cmd->pdu_count; i++)
+		if ((pdu[i].offset == offset) && (pdu[i].length == length))
+			return &pdu[i];
+
+	pr_err("Unable to locate PDU holder for ITT: 0x%08x, Offset:"
+		" %u, Length: %u\n", cmd->init_task_tag, offset, length);
+	return NULL;
+}
+
+struct iscsi_pdu *iscsit_get_pdu_holder_for_seq(
+	struct iscsi_cmd *cmd,
+	struct iscsi_seq *seq)
+{
+	u32 i;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_pdu *pdu = NULL;
+
+	if (!cmd->pdu_list) {
+		pr_err("struct iscsi_cmd->pdu_list is NULL!\n");
+		return NULL;
+	}
+
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+redo:
+		pdu = &cmd->pdu_list[cmd->pdu_start];
+
+		for (i = 0; pdu[i].seq_no != cmd->seq_no; i++) {
+#if 0
+			pr_debug("pdu[i].seq_no: %d, pdu[i].pdu"
+				"_send_order: %d, pdu[i].offset: %d,"
+				" pdu[i].length: %d\n", pdu[i].seq_no,
+				pdu[i].pdu_send_order, pdu[i].offset,
+				pdu[i].length);
+#endif
+			if (pdu[i].pdu_send_order == cmd->pdu_send_order) {
+				cmd->pdu_send_order++;
+				return &pdu[i];
+			}
+		}
+
+		cmd->pdu_start += cmd->pdu_send_order;
+		cmd->pdu_send_order = 0;
+		cmd->seq_no++;
+
+		if (cmd->pdu_start < cmd->pdu_count)
+			goto redo;
+
+		pr_err("Command ITT: 0x%08x unable to locate"
+			" struct iscsi_pdu for cmd->pdu_send_order: %u.\n",
+			cmd->init_task_tag, cmd->pdu_send_order);
+		return NULL;
+	} else {
+		if (!seq) {
+			pr_err("struct iscsi_seq is NULL!\n");
+			return NULL;
+		}
+#if 0
+		pr_debug("seq->pdu_start: %d, seq->pdu_count: %d,"
+			" seq->seq_no: %d\n", seq->pdu_start, seq->pdu_count,
+			seq->seq_no);
+#endif
+		pdu = &cmd->pdu_list[seq->pdu_start];
+
+		if (seq->pdu_send_order == seq->pdu_count) {
+			pr_err("Command ITT: 0x%08x seq->pdu_send"
+				"_order: %u equals seq->pdu_count: %u\n",
+				cmd->init_task_tag, seq->pdu_send_order,
+				seq->pdu_count);
+			return NULL;
+		}
+
+		for (i = 0; i < seq->pdu_count; i++) {
+			if (pdu[i].pdu_send_order == seq->pdu_send_order) {
+				seq->pdu_send_order++;
+				return &pdu[i];
+			}
+		}
+
+		pr_err("Command ITT: 0x%08x unable to locate iscsi"
+			"_pdu_t for seq->pdu_send_order: %u.\n",
+			cmd->init_task_tag, seq->pdu_send_order);
+		return NULL;
+	}
+
+	return NULL;
+}
+
+struct iscsi_seq *iscsit_get_seq_holder(
+	struct iscsi_cmd *cmd,
+	u32 offset,
+	u32 length)
+{
+	u32 i;
+
+	if (!cmd->seq_list) {
+		pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+		return NULL;
+	}
+
+	for (i = 0; i < cmd->seq_count; i++) {
+#if 0
+		pr_debug("seq_list[i].orig_offset: %d, seq_list[i]."
+			"xfer_len: %d, seq_list[i].seq_no %u\n",
+			cmd->seq_list[i].orig_offset, cmd->seq_list[i].xfer_len,
+			cmd->seq_list[i].seq_no);
+#endif
+		if ((cmd->seq_list[i].orig_offset +
+				cmd->seq_list[i].xfer_len) >=
+				(offset + length))
+			return &cmd->seq_list[i];
+	}
+
+	pr_err("Unable to locate Sequence holder for ITT: 0x%08x,"
+		" Offset: %u, Length: %u\n", cmd->init_task_tag, offset,
+		length);
+	return NULL;
+}
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.h b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h
new file mode 100644
index 0000000..0d52a10
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h
@@ -0,0 +1,86 @@
+#ifndef ISCSI_SEQ_AND_PDU_LIST_H
+#define ISCSI_SEQ_AND_PDU_LIST_H
+
+/* struct iscsi_pdu->status */
+#define DATAOUT_PDU_SENT			1
+
+/* struct iscsi_seq->type */
+#define SEQTYPE_IMMEDIATE			1
+#define SEQTYPE_UNSOLICITED			2
+#define SEQTYPE_NORMAL				3
+
+/* struct iscsi_seq->status */
+#define DATAOUT_SEQUENCE_GOT_R2T		1
+#define DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY 2
+#define DATAOUT_SEQUENCE_COMPLETE		3
+
+/* iscsi_determine_counts_for_list() type */
+#define PDULIST_NORMAL				1
+#define PDULIST_IMMEDIATE			2
+#define PDULIST_UNSOLICITED			3
+#define PDULIST_IMMEDIATE_AND_UNSOLICITED	4
+
+/* struct iscsi_pdu->type */
+#define PDUTYPE_IMMEDIATE			1
+#define PDUTYPE_UNSOLICITED			2
+#define PDUTYPE_NORMAL				3
+
+/* struct iscsi_pdu->status */
+#define ISCSI_PDU_NOT_RECEIVED			0
+#define ISCSI_PDU_RECEIVED_OK			1
+#define ISCSI_PDU_CRC_FAILED			2
+#define ISCSI_PDU_TIMED_OUT			3
+
+/* struct iscsi_build_list->randomize */
+#define RANDOM_DATAIN_PDU_OFFSETS		0x01
+#define RANDOM_DATAIN_SEQ_OFFSETS		0x02
+#define RANDOM_DATAOUT_PDU_OFFSETS		0x04
+#define RANDOM_R2T_OFFSETS			0x08
+
+/* struct iscsi_build_list->data_direction */
+#define ISCSI_PDU_READ				0x01
+#define ISCSI_PDU_WRITE				0x02
+
+struct iscsi_build_list {
+	int		data_direction;
+	int		randomize;
+	int		type;
+	int		immediate_data_length;
+};
+
+struct iscsi_pdu {
+	int		status;
+	int		type;
+	u8		flags;
+	u32		data_sn;
+	u32		length;
+	u32		offset;
+	u32		pdu_send_order;
+	u32		seq_no;
+} ____cacheline_aligned;
+
+struct iscsi_seq {
+	int		sent;
+	int		status;
+	int		type;
+	u32		data_sn;
+	u32		first_datasn;
+	u32		last_datasn;
+	u32		next_burst_len;
+	u32		pdu_start;
+	u32		pdu_count;
+	u32		offset;
+	u32		orig_offset;
+	u32		pdu_send_order;
+	u32		r2t_sn;
+	u32		seq_send_order;
+	u32		seq_no;
+	u32		xfer_len;
+} ____cacheline_aligned;
+
+extern int iscsit_do_build_list(struct iscsi_cmd *, struct iscsi_build_list *);
+extern struct iscsi_pdu *iscsit_get_pdu_holder(struct iscsi_cmd *, u32, u32);
+extern struct iscsi_pdu *iscsit_get_pdu_holder_for_seq(struct iscsi_cmd *, struct iscsi_seq *);
+extern struct iscsi_seq *iscsit_get_seq_holder(struct iscsi_cmd *, u32, u32);
+
+#endif /* ISCSI_SEQ_AND_PDU_LIST_H */
diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c
new file mode 100644
index 0000000..bbdbe93
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_stat.c
@@ -0,0 +1,950 @@
+/*******************************************************************************
+ * Modern ConfigFS group context specific iSCSI statistics based on original
+ * iscsi_target_mib.c code
+ *
+ * Copyright (c) 2011 Rising Tide Systems
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/configfs.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/configfs_macros.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_stat.h"
+
+#ifndef INITIAL_JIFFIES
+#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
+#endif
+
+/* Instance Attributes Table */
+#define ISCSI_INST_NUM_NODES		1
+#define ISCSI_INST_DESCR		"Storage Engine Target"
+#define ISCSI_INST_LAST_FAILURE_TYPE	0
+#define ISCSI_DISCONTINUITY_TIME	0
+
+#define ISCSI_NODE_INDEX		1
+
+#define ISPRINT(a)   ((a >= ' ') && (a <= '~'))
+
+/****************************************************************************
+ * iSCSI MIB Tables
+ ****************************************************************************/
+/*
+ * Instance Attributes Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_instance, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_INSTANCE_ATTR(_name, _mode)			\
+static struct iscsi_stat_instance_attribute			\
+			iscsi_stat_instance_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_instance_show_attr_##_name,			\
+	iscsi_stat_instance_store_attr_##_name);
+
+#define ISCSI_STAT_INSTANCE_ATTR_RO(_name)			\
+static struct iscsi_stat_instance_attribute			\
+			iscsi_stat_instance_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_instance_show_attr_##_name);
+
+static ssize_t iscsi_stat_instance_show_attr_inst(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_instance_show_attr_min_ver(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(min_ver);
+
+static ssize_t iscsi_stat_instance_show_attr_max_ver(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(max_ver);
+
+static ssize_t iscsi_stat_instance_show_attr_portals(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_num_tpg_nps);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(portals);
+
+static ssize_t iscsi_stat_instance_show_attr_nodes(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_INST_NUM_NODES);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(nodes);
+
+static ssize_t iscsi_stat_instance_show_attr_sessions(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_nsessions);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(sessions);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_sess(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+	u32 sess_err_count;
+
+	spin_lock_bh(&sess_err->lock);
+	sess_err_count = (sess_err->digest_errors +
+			  sess_err->cxn_timeout_errors +
+			  sess_err->pdu_format_errors);
+	spin_unlock_bh(&sess_err->lock);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", sess_err_count);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_sess);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_type(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n",
+			sess_err->last_sess_failure_type);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_type);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_rem_name(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+	return snprintf(page, PAGE_SIZE, "%s\n",
+			sess_err->last_sess_fail_rem_name[0] ?
+			sess_err->last_sess_fail_rem_name : NONE);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_rem_name);
+
+static ssize_t iscsi_stat_instance_show_attr_disc_time(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DISCONTINUITY_TIME);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(disc_time);
+
+static ssize_t iscsi_stat_instance_show_attr_description(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%s\n", ISCSI_INST_DESCR);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(description);
+
+static ssize_t iscsi_stat_instance_show_attr_vendor(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "RisingTide Systems iSCSI-Target\n");
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(vendor);
+
+static ssize_t iscsi_stat_instance_show_attr_version(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%s\n", ISCSIT_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(version);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_instance, iscsi_wwn_stat_grps,
+		iscsi_instance_group);
+
+static struct configfs_attribute *iscsi_stat_instance_attrs[] = {
+	&iscsi_stat_instance_inst.attr,
+	&iscsi_stat_instance_min_ver.attr,
+	&iscsi_stat_instance_max_ver.attr,
+	&iscsi_stat_instance_portals.attr,
+	&iscsi_stat_instance_nodes.attr,
+	&iscsi_stat_instance_sessions.attr,
+	&iscsi_stat_instance_fail_sess.attr,
+	&iscsi_stat_instance_fail_type.attr,
+	&iscsi_stat_instance_fail_rem_name.attr,
+	&iscsi_stat_instance_disc_time.attr,
+	&iscsi_stat_instance_description.attr,
+	&iscsi_stat_instance_vendor.attr,
+	&iscsi_stat_instance_version.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_instance_item_ops = {
+	.show_attribute		= iscsi_stat_instance_attr_show,
+	.store_attribute	= iscsi_stat_instance_attr_store,
+};
+
+struct config_item_type iscsi_stat_instance_cit = {
+	.ct_item_ops		= &iscsi_stat_instance_item_ops,
+	.ct_attrs		= iscsi_stat_instance_attrs,
+	.ct_owner		= THIS_MODULE,
+};
+
+/*
+ * Instance Session Failure Stats Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_sess_err, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_SESS_ERR_ATTR(_name, _mode)			\
+static struct iscsi_stat_sess_err_attribute			\
+			iscsi_stat_sess_err_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_sess_err_show_attr_##_name,			\
+	iscsi_stat_sess_err_store_attr_##_name);
+
+#define ISCSI_STAT_SESS_ERR_ATTR_RO(_name)			\
+static struct iscsi_stat_sess_err_attribute			\
+			iscsi_stat_sess_err_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_sess_err_show_attr_##_name);
+
+static ssize_t iscsi_stat_sess_err_show_attr_inst(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_sess_err_show_attr_digest_errors(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n", sess_err->digest_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(digest_errors);
+
+static ssize_t iscsi_stat_sess_err_show_attr_cxn_errors(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n", sess_err->cxn_timeout_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(cxn_errors);
+
+static ssize_t iscsi_stat_sess_err_show_attr_format_errors(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n", sess_err->pdu_format_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(format_errors);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_sess_err, iscsi_wwn_stat_grps,
+		iscsi_sess_err_group);
+
+static struct configfs_attribute *iscsi_stat_sess_err_attrs[] = {
+	&iscsi_stat_sess_err_inst.attr,
+	&iscsi_stat_sess_err_digest_errors.attr,
+	&iscsi_stat_sess_err_cxn_errors.attr,
+	&iscsi_stat_sess_err_format_errors.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_sess_err_item_ops = {
+	.show_attribute		= iscsi_stat_sess_err_attr_show,
+	.store_attribute	= iscsi_stat_sess_err_attr_store,
+};
+
+struct config_item_type iscsi_stat_sess_err_cit = {
+	.ct_item_ops		= &iscsi_stat_sess_err_item_ops,
+	.ct_attrs		= iscsi_stat_sess_err_attrs,
+	.ct_owner		= THIS_MODULE,
+};
+
+/*
+ * Target Attributes Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_TGT_ATTR(_name, _mode)			\
+static struct iscsi_stat_tgt_attr_attribute			\
+			iscsi_stat_tgt_attr_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_tgt-attr_show_attr_##_name,			\
+	iscsi_stat_tgt_attr_store_attr_##_name);
+
+#define ISCSI_STAT_TGT_ATTR_RO(_name)				\
+static struct iscsi_stat_tgt_attr_attribute			\
+			iscsi_stat_tgt_attr_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_tgt_attr_show_attr_##_name);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_inst(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_TGT_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_indx(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_TGT_ATTR_RO(indx);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_login_fails(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	u32 fail_count;
+
+	spin_lock(&lstat->lock);
+	fail_count = (lstat->redirects + lstat->authorize_fails +
+			lstat->authenticate_fails + lstat->negotiate_fails +
+			lstat->other_fails);
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", fail_count);
+}
+ISCSI_STAT_TGT_ATTR_RO(login_fails);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_time(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	u32 last_fail_time;
+
+	spin_lock(&lstat->lock);
+	last_fail_time = lstat->last_fail_time ?
+			(u32)(((u32)lstat->last_fail_time -
+				INITIAL_JIFFIES) * 100 / HZ) : 0;
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", last_fail_time);
+}
+ISCSI_STAT_TGT_ATTR_RO(last_fail_time);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_type(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	u32 last_fail_type;
+
+	spin_lock(&lstat->lock);
+	last_fail_type = lstat->last_fail_type;
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", last_fail_type);
+}
+ISCSI_STAT_TGT_ATTR_RO(last_fail_type);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_name(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	unsigned char buf[224];
+
+	spin_lock(&lstat->lock);
+	snprintf(buf, 224, "%s", lstat->last_intr_fail_name[0] ?
+				lstat->last_intr_fail_name : NONE);
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%s\n", buf);
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_name);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr_type(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+			struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	unsigned char buf[8];
+
+	spin_lock(&lstat->lock);
+	snprintf(buf, 8, "%s", (lstat->last_intr_fail_ip_addr != NULL) ?
+				"ipv6" : "ipv4");
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%s\n", buf);
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr_type);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+			struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	unsigned char buf[32];
+
+	spin_lock(&lstat->lock);
+	if (lstat->last_intr_fail_ip_family == AF_INET6)
+		snprintf(buf, 32, "[%s]", lstat->last_intr_fail_ip_addr);
+	else
+		snprintf(buf, 32, "%s", lstat->last_intr_fail_ip_addr);
+	spin_unlock(&lstat->lock);
+
+	return snprintf(page, PAGE_SIZE, "%s\n", buf);
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps,
+		iscsi_tgt_attr_group);
+
+static struct configfs_attribute *iscsi_stat_tgt_attr_attrs[] = {
+	&iscsi_stat_tgt_attr_inst.attr,
+	&iscsi_stat_tgt_attr_indx.attr,
+	&iscsi_stat_tgt_attr_login_fails.attr,
+	&iscsi_stat_tgt_attr_last_fail_time.attr,
+	&iscsi_stat_tgt_attr_last_fail_type.attr,
+	&iscsi_stat_tgt_attr_fail_intr_name.attr,
+	&iscsi_stat_tgt_attr_fail_intr_addr_type.attr,
+	&iscsi_stat_tgt_attr_fail_intr_addr.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_tgt_attr_item_ops = {
+	.show_attribute		= iscsi_stat_tgt_attr_attr_show,
+	.store_attribute	= iscsi_stat_tgt_attr_attr_store,
+};
+
+struct config_item_type iscsi_stat_tgt_attr_cit = {
+	.ct_item_ops		= &iscsi_stat_tgt_attr_item_ops,
+	.ct_attrs		= iscsi_stat_tgt_attr_attrs,
+	.ct_owner		= THIS_MODULE,
+};
+
+/*
+ * Target Login Stats Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_login, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_LOGIN(_name, _mode)				\
+static struct iscsi_stat_login_attribute			\
+			iscsi_stat_login_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_login_show_attr_##_name,			\
+	iscsi_stat_login_store_attr_##_name);
+
+#define ISCSI_STAT_LOGIN_RO(_name)				\
+static struct iscsi_stat_login_attribute			\
+			iscsi_stat_login_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_login_show_attr_##_name);
+
+static ssize_t iscsi_stat_login_show_attr_inst(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_LOGIN_RO(inst);
+
+static ssize_t iscsi_stat_login_show_attr_indx(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_LOGIN_RO(indx);
+
+static ssize_t iscsi_stat_login_show_attr_accepts(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->accepts);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(accepts);
+
+static ssize_t iscsi_stat_login_show_attr_other_fails(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->other_fails);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(other_fails);
+
+static ssize_t iscsi_stat_login_show_attr_redirects(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->redirects);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(redirects);
+
+static ssize_t iscsi_stat_login_show_attr_authorize_fails(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authorize_fails);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(authorize_fails);
+
+static ssize_t iscsi_stat_login_show_attr_authenticate_fails(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authenticate_fails);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(authenticate_fails);
+
+static ssize_t iscsi_stat_login_show_attr_negotiate_fails(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+				struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_login_stats *lstat = &tiqn->login_stats;
+	ssize_t ret;
+
+	spin_lock(&lstat->lock);
+	ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->negotiate_fails);
+	spin_unlock(&lstat->lock);
+
+	return ret;
+}
+ISCSI_STAT_LOGIN_RO(negotiate_fails);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_login, iscsi_wwn_stat_grps,
+		iscsi_login_stats_group);
+
+static struct configfs_attribute *iscsi_stat_login_stats_attrs[] = {
+	&iscsi_stat_login_inst.attr,
+	&iscsi_stat_login_indx.attr,
+	&iscsi_stat_login_accepts.attr,
+	&iscsi_stat_login_other_fails.attr,
+	&iscsi_stat_login_redirects.attr,
+	&iscsi_stat_login_authorize_fails.attr,
+	&iscsi_stat_login_authenticate_fails.attr,
+	&iscsi_stat_login_negotiate_fails.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_login_stats_item_ops = {
+	.show_attribute		= iscsi_stat_login_attr_show,
+	.store_attribute	= iscsi_stat_login_attr_store,
+};
+
+struct config_item_type iscsi_stat_login_cit = {
+	.ct_item_ops		= &iscsi_stat_login_stats_item_ops,
+	.ct_attrs		= iscsi_stat_login_stats_attrs,
+	.ct_owner		= THIS_MODULE,
+};
+
+/*
+ * Target Logout Stats Table
+ */
+
+CONFIGFS_EATTR_STRUCT(iscsi_stat_logout, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_LOGOUT(_name, _mode)				\
+static struct iscsi_stat_logout_attribute			\
+			iscsi_stat_logout_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_logout_show_attr_##_name,			\
+	iscsi_stat_logout_store_attr_##_name);
+
+#define ISCSI_STAT_LOGOUT_RO(_name)				\
+static struct iscsi_stat_logout_attribute			\
+			iscsi_stat_logout_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_logout_show_attr_##_name);
+
+static ssize_t iscsi_stat_logout_show_attr_inst(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+			struct iscsi_tiqn, tiqn_stat_grps);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_LOGOUT_RO(inst);
+
+static ssize_t iscsi_stat_logout_show_attr_indx(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_LOGOUT_RO(indx);
+
+static ssize_t iscsi_stat_logout_show_attr_normal_logouts(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+			struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_logout_stats *lstats = &tiqn->logout_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n", lstats->normal_logouts);
+}
+ISCSI_STAT_LOGOUT_RO(normal_logouts);
+
+static ssize_t iscsi_stat_logout_show_attr_abnormal_logouts(
+	struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+	struct iscsi_tiqn *tiqn = container_of(igrps,
+			struct iscsi_tiqn, tiqn_stat_grps);
+	struct iscsi_logout_stats *lstats = &tiqn->logout_stats;
+
+	return snprintf(page, PAGE_SIZE, "%u\n", lstats->abnormal_logouts);
+}
+ISCSI_STAT_LOGOUT_RO(abnormal_logouts);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_logout, iscsi_wwn_stat_grps,
+		iscsi_logout_stats_group);
+
+static struct configfs_attribute *iscsi_stat_logout_stats_attrs[] = {
+	&iscsi_stat_logout_inst.attr,
+	&iscsi_stat_logout_indx.attr,
+	&iscsi_stat_logout_normal_logouts.attr,
+	&iscsi_stat_logout_abnormal_logouts.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_logout_stats_item_ops = {
+	.show_attribute		= iscsi_stat_logout_attr_show,
+	.store_attribute	= iscsi_stat_logout_attr_store,
+};
+
+struct config_item_type iscsi_stat_logout_cit = {
+	.ct_item_ops		= &iscsi_stat_logout_stats_item_ops,
+	.ct_attrs		= iscsi_stat_logout_stats_attrs,
+	.ct_owner		= THIS_MODULE,
+};
+
+/*
+ * Session Stats Table
+ */
+
+CONFIGFS_EATTR_STRUCT(iscsi_stat_sess, iscsi_node_stat_grps);
+#define ISCSI_STAT_SESS(_name, _mode)				\
+static struct iscsi_stat_sess_attribute				\
+			iscsi_stat_sess_##_name =		\
+	__CONFIGFS_EATTR(_name, _mode,				\
+	iscsi_stat_sess_show_attr_##_name,			\
+	iscsi_stat_sess_store_attr_##_name);
+
+#define ISCSI_STAT_SESS_RO(_name)				\
+static struct iscsi_stat_sess_attribute				\
+			iscsi_stat_sess_##_name =		\
+	__CONFIGFS_EATTR_RO(_name,				\
+	iscsi_stat_sess_show_attr_##_name);
+
+static ssize_t iscsi_stat_sess_show_attr_inst(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_wwn *wwn = acl->se_node_acl.se_tpg->se_tpg_wwn;
+	struct iscsi_tiqn *tiqn = container_of(wwn,
+			struct iscsi_tiqn, tiqn_wwn);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_SESS_RO(inst);
+
+static ssize_t iscsi_stat_sess_show_attr_node(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n",
+				sess->sess_ops->SessionType ? 0 : ISCSI_NODE_INDEX);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(node);
+
+static ssize_t iscsi_stat_sess_show_attr_indx(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n",
+					sess->session_index);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(indx);
+
+static ssize_t iscsi_stat_sess_show_attr_cmd_pdus(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n", sess->cmd_pdus);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(cmd_pdus);
+
+static ssize_t iscsi_stat_sess_show_attr_rsp_pdus(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n", sess->rsp_pdus);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(rsp_pdus);
+
+static ssize_t iscsi_stat_sess_show_attr_txdata_octs(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%llu\n",
+				(unsigned long long)sess->tx_data_octets);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(txdata_octs);
+
+static ssize_t iscsi_stat_sess_show_attr_rxdata_octs(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%llu\n",
+				(unsigned long long)sess->rx_data_octets);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(rxdata_octs);
+
+static ssize_t iscsi_stat_sess_show_attr_conn_digest_errors(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n",
+					sess->conn_digest_errors);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(conn_digest_errors);
+
+static ssize_t iscsi_stat_sess_show_attr_conn_timeout_errors(
+	struct iscsi_node_stat_grps *igrps, char *page)
+{
+	struct iscsi_node_acl *acl = container_of(igrps,
+			struct iscsi_node_acl, node_stat_grps);
+	struct se_node_acl *se_nacl = &acl->se_node_acl;
+	struct iscsi_session *sess;
+	struct se_session *se_sess;
+	ssize_t ret = 0;
+
+	spin_lock_bh(&se_nacl->nacl_sess_lock);
+	se_sess = se_nacl->nacl_sess;
+	if (se_sess) {
+		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+		if (sess)
+			ret = snprintf(page, PAGE_SIZE, "%u\n",
+					sess->conn_timeout_errors);
+	}
+	spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+	return ret;
+}
+ISCSI_STAT_SESS_RO(conn_timeout_errors);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_sess, iscsi_node_stat_grps,
+		iscsi_sess_stats_group);
+
+static struct configfs_attribute *iscsi_stat_sess_stats_attrs[] = {
+	&iscsi_stat_sess_inst.attr,
+	&iscsi_stat_sess_node.attr,
+	&iscsi_stat_sess_indx.attr,
+	&iscsi_stat_sess_cmd_pdus.attr,
+	&iscsi_stat_sess_rsp_pdus.attr,
+	&iscsi_stat_sess_txdata_octs.attr,
+	&iscsi_stat_sess_rxdata_octs.attr,
+	&iscsi_stat_sess_conn_digest_errors.attr,
+	&iscsi_stat_sess_conn_timeout_errors.attr,
+	NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_sess_stats_item_ops = {
+	.show_attribute		= iscsi_stat_sess_attr_show,
+	.store_attribute	= iscsi_stat_sess_attr_store,
+};
+
+struct config_item_type iscsi_stat_sess_cit = {
+	.ct_item_ops		= &iscsi_stat_sess_stats_item_ops,
+	.ct_attrs		= iscsi_stat_sess_stats_attrs,
+	.ct_owner		= THIS_MODULE,
+};
diff --git a/drivers/target/iscsi/iscsi_target_stat.h b/drivers/target/iscsi/iscsi_target_stat.h
new file mode 100644
index 0000000..3ff76b4
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_stat.h
@@ -0,0 +1,64 @@
+#ifndef ISCSI_TARGET_STAT_H
+#define ISCSI_TARGET_STAT_H
+
+/*
+ * For struct iscsi_tiqn->tiqn_wwn default groups
+ */
+extern struct config_item_type iscsi_stat_instance_cit;
+extern struct config_item_type iscsi_stat_sess_err_cit;
+extern struct config_item_type iscsi_stat_tgt_attr_cit;
+extern struct config_item_type iscsi_stat_login_cit;
+extern struct config_item_type iscsi_stat_logout_cit;
+
+/*
+ * For struct iscsi_session->se_sess default groups
+ */
+extern struct config_item_type iscsi_stat_sess_cit;
+
+/* iSCSI session error types */
+#define ISCSI_SESS_ERR_UNKNOWN		0
+#define ISCSI_SESS_ERR_DIGEST		1
+#define ISCSI_SESS_ERR_CXN_TIMEOUT	2
+#define ISCSI_SESS_ERR_PDU_FORMAT	3
+
+/* iSCSI session error stats */
+struct iscsi_sess_err_stats {
+	spinlock_t	lock;
+	u32		digest_errors;
+	u32		cxn_timeout_errors;
+	u32		pdu_format_errors;
+	u32		last_sess_failure_type;
+	char		last_sess_fail_rem_name[224];
+} ____cacheline_aligned;
+
+/* iSCSI login failure types (sub oids) */
+#define ISCSI_LOGIN_FAIL_OTHER		2
+#define ISCSI_LOGIN_FAIL_REDIRECT	3
+#define ISCSI_LOGIN_FAIL_AUTHORIZE	4
+#define ISCSI_LOGIN_FAIL_AUTHENTICATE	5
+#define ISCSI_LOGIN_FAIL_NEGOTIATE	6
+
+/* iSCSI login stats */
+struct iscsi_login_stats {
+	spinlock_t	lock;
+	u32		accepts;
+	u32		other_fails;
+	u32		redirects;
+	u32		authorize_fails;
+	u32		authenticate_fails;
+	u32		negotiate_fails;	/* used for notifications */
+	u64		last_fail_time;		/* time stamp (jiffies) */
+	u32		last_fail_type;
+	int		last_intr_fail_ip_family;
+	unsigned char	last_intr_fail_ip_addr[IPV6_ADDRESS_SPACE];
+	char		last_intr_fail_name[224];
+} ____cacheline_aligned;
+
+/* iSCSI logout stats */
+struct iscsi_logout_stats {
+	spinlock_t	lock;
+	u32		normal_logouts;
+	u32		abnormal_logouts;
+} ____cacheline_aligned;
+
+#endif   /*** ISCSI_TARGET_STAT_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c
new file mode 100644
index 0000000..db1fe1e
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tmr.c
@@ -0,0 +1,849 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target specific Task Management functions.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <asm/unaligned.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_tmr.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+u8 iscsit_tmr_abort_task(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	struct iscsi_cmd *ref_cmd;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+	struct iscsi_tm *hdr = (struct iscsi_tm *) buf;
+
+	ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt);
+	if (!ref_cmd) {
+		pr_err("Unable to locate RefTaskTag: 0x%08x on CID:"
+			" %hu.\n", hdr->rtt, conn->cid);
+		return ((hdr->refcmdsn >= conn->sess->exp_cmd_sn) &&
+			(hdr->refcmdsn <= conn->sess->max_cmd_sn)) ?
+			ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK;
+	}
+	if (ref_cmd->cmd_sn != hdr->refcmdsn) {
+		pr_err("RefCmdSN 0x%08x does not equal"
+			" task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n",
+			hdr->refcmdsn, ref_cmd->cmd_sn);
+		return ISCSI_TMF_RSP_REJECTED;
+	}
+
+	se_tmr->ref_task_tag		= hdr->rtt;
+	se_tmr->ref_cmd			= &ref_cmd->se_cmd;
+	tmr_req->ref_cmd_sn		= hdr->refcmdsn;
+	tmr_req->exp_data_sn		= hdr->exp_datasn;
+
+	return ISCSI_TMF_RSP_COMPLETE;
+}
+
+/*
+ *	Called from iscsit_handle_task_mgt_cmd().
+ */
+int iscsit_tmr_task_warm_reset(
+	struct iscsi_conn *conn,
+	struct iscsi_tmr_req *tmr_req,
+	unsigned char *buf)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+#if 0
+	struct iscsi_init_task_mgt_cmnd *hdr =
+		(struct iscsi_init_task_mgt_cmnd *) buf;
+#endif
+	if (!na->tmr_warm_reset) {
+		pr_err("TMR Opcode TARGET_WARM_RESET authorization"
+			" failed for Initiator Node: %s\n",
+			sess->se_sess->se_node_acl->initiatorname);
+		 return -1;
+	}
+	/*
+	 * Do the real work in transport_generic_do_tmr().
+	 */
+	return 0;
+}
+
+int iscsit_tmr_task_cold_reset(
+	struct iscsi_conn *conn,
+	struct iscsi_tmr_req *tmr_req,
+	unsigned char *buf)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+	if (!na->tmr_cold_reset) {
+		pr_err("TMR Opcode TARGET_COLD_RESET authorization"
+			" failed for Initiator Node: %s\n",
+			sess->se_sess->se_node_acl->initiatorname);
+		return -1;
+	}
+	/*
+	 * Do the real work in transport_generic_do_tmr().
+	 */
+	return 0;
+}
+
+u8 iscsit_tmr_task_reassign(
+	struct iscsi_cmd *cmd,
+	unsigned char *buf)
+{
+	struct iscsi_cmd *ref_cmd = NULL;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_conn_recovery *cr = NULL;
+	struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+	struct iscsi_tm *hdr = (struct iscsi_tm *) buf;
+	int ret;
+
+	pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x,"
+		" RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n",
+		hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid);
+
+	if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) {
+		pr_err("TMR TASK_REASSIGN not supported in ERL<2,"
+				" ignoring request.\n");
+		return ISCSI_TMF_RSP_NOT_SUPPORTED;
+	}
+
+	ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt);
+	if (ret == -2) {
+		pr_err("Command ITT: 0x%08x is still alligent to CID:"
+			" %hu\n", ref_cmd->init_task_tag, cr->cid);
+		return ISCSI_TMF_RSP_TASK_ALLEGIANT;
+	} else if (ret == -1) {
+		pr_err("Unable to locate RefTaskTag: 0x%08x in"
+			" connection recovery command list.\n", hdr->rtt);
+		return ISCSI_TMF_RSP_NO_TASK;
+	}
+	/*
+	 * Temporary check to prevent connection recovery for
+	 * connections with a differing MaxRecvDataSegmentLength.
+	 */
+	if (cr->maxrecvdatasegmentlength !=
+	    conn->conn_ops->MaxRecvDataSegmentLength) {
+		pr_err("Unable to perform connection recovery for"
+			" differing MaxRecvDataSegmentLength, rejecting"
+			" TMR TASK_REASSIGN.\n");
+		return ISCSI_TMF_RSP_REJECTED;
+	}
+
+	se_tmr->ref_task_tag		= hdr->rtt;
+	se_tmr->ref_cmd			= &ref_cmd->se_cmd;
+	se_tmr->ref_task_lun		= get_unaligned_le64(&hdr->lun);
+	tmr_req->ref_cmd_sn		= hdr->refcmdsn;
+	tmr_req->exp_data_sn		= hdr->exp_datasn;
+	tmr_req->conn_recovery		= cr;
+	tmr_req->task_reassign		= 1;
+	/*
+	 * Command can now be reassigned to a new connection.
+	 * The task management response must be sent before the
+	 * reassignment actually happens.  See iscsi_tmr_post_handler().
+	 */
+	return ISCSI_TMF_RSP_COMPLETE;
+}
+
+static void iscsit_task_reassign_remove_cmd(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn_recovery *cr,
+	struct iscsi_session *sess)
+{
+	int ret;
+
+	spin_lock(&cr->conn_recovery_cmd_lock);
+	ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess);
+	spin_unlock(&cr->conn_recovery_cmd_lock);
+	if (!ret) {
+		pr_debug("iSCSI connection recovery successful for CID:"
+			" %hu on SID: %u\n", cr->cid, sess->sid);
+		iscsit_remove_active_connection_recovery_entry(cr, sess);
+	}
+}
+
+static int iscsit_task_reassign_complete_nop_out(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
+	struct se_cmd *se_cmd = se_tmr->ref_cmd;
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+	struct iscsi_conn_recovery *cr;
+
+	if (!cmd->cr) {
+		pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+			" is NULL!\n", cmd->init_task_tag);
+		return -1;
+	}
+	cr = cmd->cr;
+
+	/*
+	 * Reset the StatSN so a new one for this commands new connection
+	 * will be assigned.
+	 * Reset the ExpStatSN as well so we may receive Status SNACKs.
+	 */
+	cmd->stat_sn = cmd->exp_stat_sn = 0;
+
+	iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess);
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	cmd->i_state = ISTATE_SEND_NOPIN;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+	return 0;
+}
+
+static int iscsit_task_reassign_complete_write(
+	struct iscsi_cmd *cmd,
+	struct iscsi_tmr_req *tmr_req)
+{
+	int no_build_r2ts = 0;
+	u32 length = 0, offset = 0;
+	struct iscsi_conn *conn = cmd->conn;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	/*
+	 * The Initiator must not send a R2T SNACK with a Begrun less than
+	 * the TMR TASK_REASSIGN's ExpDataSN.
+	 */
+	if (!tmr_req->exp_data_sn) {
+		cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
+		cmd->acked_data_sn = 0;
+	} else {
+		cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+		cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
+	}
+
+	/*
+	 * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the
+	 * Initiator is expecting.  The Target controls all WRITE operations
+	 * so if we have received all DataOUT we can safety ignore Initiator.
+	 */
+	if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+		if (!atomic_read(&cmd->transport_sent)) {
+			pr_debug("WRITE ITT: 0x%08x: t_state: %d"
+				" never sent to transport\n",
+				cmd->init_task_tag, cmd->se_cmd.t_state);
+			return transport_generic_handle_data(se_cmd);
+		}
+
+		cmd->i_state = ISTATE_SEND_STATUS;
+		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+		return 0;
+	}
+
+	/*
+	 * Special case to deal with DataSequenceInOrder=No and Non-Immeidate
+	 * Unsolicited DataOut.
+	 */
+	if (cmd->unsolicited_data) {
+		cmd->unsolicited_data = 0;
+
+		offset = cmd->next_burst_len = cmd->write_data_done;
+
+		if ((conn->sess->sess_ops->FirstBurstLength - offset) >=
+		     cmd->data_length) {
+			no_build_r2ts = 1;
+			length = (cmd->data_length - offset);
+		} else
+			length = (conn->sess->sess_ops->FirstBurstLength - offset);
+
+		spin_lock_bh(&cmd->r2t_lock);
+		if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) {
+			spin_unlock_bh(&cmd->r2t_lock);
+			return -1;
+		}
+		cmd->outstanding_r2ts++;
+		spin_unlock_bh(&cmd->r2t_lock);
+
+		if (no_build_r2ts)
+			return 0;
+	}
+	/*
+	 * iscsit_build_r2ts_for_cmd() can handle the rest from here.
+	 */
+	return iscsit_build_r2ts_for_cmd(cmd, conn, 2);
+}
+
+static int iscsit_task_reassign_complete_read(
+	struct iscsi_cmd *cmd,
+	struct iscsi_tmr_req *tmr_req)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_datain_req *dr;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	/*
+	 * The Initiator must not send a Data SNACK with a BegRun less than
+	 * the TMR TASK_REASSIGN's ExpDataSN.
+	 */
+	if (!tmr_req->exp_data_sn) {
+		cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
+		cmd->acked_data_sn = 0;
+	} else {
+		cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+		cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
+	}
+
+	if (!atomic_read(&cmd->transport_sent)) {
+		pr_debug("READ ITT: 0x%08x: t_state: %d never sent to"
+			" transport\n", cmd->init_task_tag,
+			cmd->se_cmd.t_state);
+		transport_generic_handle_cdb(se_cmd);
+		return 0;
+	}
+
+	if (!atomic_read(&se_cmd->t_transport_complete)) {
+		pr_err("READ ITT: 0x%08x: t_state: %d, never returned"
+			" from transport\n", cmd->init_task_tag,
+			cmd->se_cmd.t_state);
+		return -1;
+	}
+
+	dr = iscsit_allocate_datain_req();
+	if (!dr)
+		return -1;
+	/*
+	 * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the
+	 * Initiator is expecting.
+	 */
+	dr->data_sn = dr->begrun = tmr_req->exp_data_sn;
+	dr->runlength = 0;
+	dr->generate_recovery_values = 1;
+	dr->recovery = DATAIN_CONNECTION_RECOVERY;
+
+	iscsit_attach_datain_req(cmd, dr);
+
+	cmd->i_state = ISTATE_SEND_DATAIN;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+	return 0;
+}
+
+static int iscsit_task_reassign_complete_none(
+	struct iscsi_cmd *cmd,
+	struct iscsi_tmr_req *tmr_req)
+{
+	struct iscsi_conn *conn = cmd->conn;
+
+	cmd->i_state = ISTATE_SEND_STATUS;
+	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+	return 0;
+}
+
+static int iscsit_task_reassign_complete_scsi_cmnd(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
+	struct se_cmd *se_cmd = se_tmr->ref_cmd;
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+	struct iscsi_conn_recovery *cr;
+
+	if (!cmd->cr) {
+		pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+			" is NULL!\n", cmd->init_task_tag);
+		return -1;
+	}
+	cr = cmd->cr;
+
+	/*
+	 * Reset the StatSN so a new one for this commands new connection
+	 * will be assigned.
+	 * Reset the ExpStatSN as well so we may receive Status SNACKs.
+	 */
+	cmd->stat_sn = cmd->exp_stat_sn = 0;
+
+	iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess);
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	if (se_cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
+		cmd->i_state = ISTATE_SEND_STATUS;
+		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+		return 0;
+	}
+
+	switch (cmd->data_direction) {
+	case DMA_TO_DEVICE:
+		return iscsit_task_reassign_complete_write(cmd, tmr_req);
+	case DMA_FROM_DEVICE:
+		return iscsit_task_reassign_complete_read(cmd, tmr_req);
+	case DMA_NONE:
+		return iscsit_task_reassign_complete_none(cmd, tmr_req);
+	default:
+		pr_err("Unknown cmd->data_direction: 0x%02x\n",
+				cmd->data_direction);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int iscsit_task_reassign_complete(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
+	struct se_cmd *se_cmd;
+	struct iscsi_cmd *cmd;
+	int ret = 0;
+
+	if (!se_tmr->ref_cmd) {
+		pr_err("TMR Request is missing a RefCmd struct iscsi_cmd.\n");
+		return -1;
+	}
+	se_cmd = se_tmr->ref_cmd;
+	cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	cmd->conn = conn;
+
+	switch (cmd->iscsi_opcode) {
+	case ISCSI_OP_NOOP_OUT:
+		ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn);
+		break;
+	case ISCSI_OP_SCSI_CMD:
+		ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn);
+		break;
+	default:
+		 pr_err("Illegal iSCSI Opcode 0x%02x during"
+			" command realligence\n", cmd->iscsi_opcode);
+		return -1;
+	}
+
+	if (ret != 0)
+		return ret;
+
+	pr_debug("Completed connection realligence for Opcode: 0x%02x,"
+		" ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode,
+			cmd->init_task_tag, conn->cid);
+
+	return 0;
+}
+
+/*
+ *	Handles special after-the-fact actions related to TMRs.
+ *	Right now the only one that its really needed for is
+ *	connection recovery releated TASK_REASSIGN.
+ */
+extern int iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+	struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+
+	if (tmr_req->task_reassign &&
+	   (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
+		return iscsit_task_reassign_complete(tmr_req, conn);
+
+	return 0;
+}
+
+/*
+ *	Nothing to do here, but leave it for good measure. :-)
+ */
+int iscsit_task_reassign_prepare_read(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	return 0;
+}
+
+static void iscsit_task_reassign_prepare_unsolicited_dataout(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	int i, j;
+	struct iscsi_pdu *pdu = NULL;
+	struct iscsi_seq *seq = NULL;
+
+	if (conn->sess->sess_ops->DataSequenceInOrder) {
+		cmd->data_sn = 0;
+
+		if (cmd->immediate_data)
+			cmd->r2t_offset += (cmd->first_burst_len -
+				cmd->seq_start_offset);
+
+		if (conn->sess->sess_ops->DataPDUInOrder) {
+			cmd->write_data_done -= (cmd->immediate_data) ?
+						(cmd->first_burst_len -
+						 cmd->seq_start_offset) :
+						 cmd->first_burst_len;
+			cmd->first_burst_len = 0;
+			return;
+		}
+
+		for (i = 0; i < cmd->pdu_count; i++) {
+			pdu = &cmd->pdu_list[i];
+
+			if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+				continue;
+
+			if ((pdu->offset >= cmd->seq_start_offset) &&
+			   ((pdu->offset + pdu->length) <=
+			     cmd->seq_end_offset)) {
+				cmd->first_burst_len -= pdu->length;
+				cmd->write_data_done -= pdu->length;
+				pdu->status = ISCSI_PDU_NOT_RECEIVED;
+			}
+		}
+	} else {
+		for (i = 0; i < cmd->seq_count; i++) {
+			seq = &cmd->seq_list[i];
+
+			if (seq->type != SEQTYPE_UNSOLICITED)
+				continue;
+
+			cmd->write_data_done -=
+					(seq->offset - seq->orig_offset);
+			cmd->first_burst_len = 0;
+			seq->data_sn = 0;
+			seq->offset = seq->orig_offset;
+			seq->next_burst_len = 0;
+			seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+			if (conn->sess->sess_ops->DataPDUInOrder)
+				continue;
+
+			for (j = 0; j < seq->pdu_count; j++) {
+				pdu = &cmd->pdu_list[j+seq->pdu_start];
+
+				if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+					continue;
+
+				pdu->status = ISCSI_PDU_NOT_RECEIVED;
+			}
+		}
+	}
+}
+
+int iscsit_task_reassign_prepare_write(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
+	struct se_cmd *se_cmd = se_tmr->ref_cmd;
+	struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+	struct iscsi_pdu *pdu = NULL;
+	struct iscsi_r2t *r2t = NULL, *r2t_tmp;
+	int first_incomplete_r2t = 1, i = 0;
+
+	/*
+	 * The command was in the process of receiving Unsolicited DataOUT when
+	 * the connection failed.
+	 */
+	if (cmd->unsolicited_data)
+		iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn);
+
+	/*
+	 * The Initiator is requesting R2Ts starting from zero,  skip
+	 * checking acknowledged R2Ts and start checking struct iscsi_r2ts
+	 * greater than zero.
+	 */
+	if (!tmr_req->exp_data_sn)
+		goto drop_unacknowledged_r2ts;
+
+	/*
+	 * We now check that the PDUs in DataOUT sequences below
+	 * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is
+	 * expecting next) have all the DataOUT they require to complete
+	 * the DataOUT sequence.  First scan from R2TSN 0 to TMR
+	 * TASK_REASSIGN ExpDataSN-1.
+	 *
+	 * If we have not received all DataOUT in question,  we must
+	 * make sure to make the appropriate changes to values in
+	 * struct iscsi_cmd (and elsewhere depending on session parameters)
+	 * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write()
+	 * will resend a new R2T for the DataOUT sequences in question.
+	 */
+	spin_lock_bh(&cmd->r2t_lock);
+	if (list_empty(&cmd->cmd_r2t_list)) {
+		spin_unlock_bh(&cmd->r2t_lock);
+		return -1;
+	}
+
+	list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+
+		if (r2t->r2t_sn >= tmr_req->exp_data_sn)
+			continue;
+		/*
+		 * Safely ignore Recovery R2Ts and R2Ts that have completed
+		 * DataOUT sequences.
+		 */
+		if (r2t->seq_complete)
+			continue;
+
+		if (r2t->recovery_r2t)
+			continue;
+
+		/*
+		 *                 DataSequenceInOrder=Yes:
+		 *
+		 * Taking into account the iSCSI implementation requirement of
+		 * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and
+		 * DataSequenceInOrder=Yes, we must take into consideration
+		 * the following:
+		 *
+		 *                  DataSequenceInOrder=No:
+		 *
+		 * Taking into account that the Initiator controls the (possibly
+		 * random) PDU Order in (possibly random) Sequence Order of
+		 * DataOUT the target requests with R2Ts,  we must take into
+		 * consideration the following:
+		 *
+		 *      DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]:
+		 *
+		 * While processing non-complete R2T DataOUT sequence requests
+		 * the Target will re-request only the total sequence length
+		 * minus current received offset.  This is because we must
+		 * assume the initiator will continue sending DataOUT from the
+		 * last PDU before the connection failed.
+		 *
+		 *      DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]:
+		 *
+		 * While processing non-complete R2T DataOUT sequence requests
+		 * the Target will re-request the entire DataOUT sequence if
+		 * any single PDU is missing from the sequence.  This is because
+		 * we have no logical method to determine the next PDU offset,
+		 * and we must assume the Initiator will be sending any random
+		 * PDU offset in the current sequence after TASK_REASSIGN
+		 * has completed.
+		 */
+		if (conn->sess->sess_ops->DataSequenceInOrder) {
+			if (!first_incomplete_r2t) {
+				cmd->r2t_offset -= r2t->xfer_len;
+				goto next;
+			}
+
+			if (conn->sess->sess_ops->DataPDUInOrder) {
+				cmd->data_sn = 0;
+				cmd->r2t_offset -= (r2t->xfer_len -
+					cmd->next_burst_len);
+				first_incomplete_r2t = 0;
+				goto next;
+			}
+
+			cmd->data_sn = 0;
+			cmd->r2t_offset -= r2t->xfer_len;
+
+			for (i = 0; i < cmd->pdu_count; i++) {
+				pdu = &cmd->pdu_list[i];
+
+				if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+					continue;
+
+				if ((pdu->offset >= r2t->offset) &&
+				    (pdu->offset < (r2t->offset +
+						r2t->xfer_len))) {
+					cmd->next_burst_len -= pdu->length;
+					cmd->write_data_done -= pdu->length;
+					pdu->status = ISCSI_PDU_NOT_RECEIVED;
+				}
+			}
+
+			first_incomplete_r2t = 0;
+		} else {
+			struct iscsi_seq *seq;
+
+			seq = iscsit_get_seq_holder(cmd, r2t->offset,
+					r2t->xfer_len);
+			if (!seq) {
+				spin_unlock_bh(&cmd->r2t_lock);
+				return -1;
+			}
+
+			cmd->write_data_done -=
+					(seq->offset - seq->orig_offset);
+			seq->data_sn = 0;
+			seq->offset = seq->orig_offset;
+			seq->next_burst_len = 0;
+			seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+			cmd->seq_send_order--;
+
+			if (conn->sess->sess_ops->DataPDUInOrder)
+				goto next;
+
+			for (i = 0; i < seq->pdu_count; i++) {
+				pdu = &cmd->pdu_list[i+seq->pdu_start];
+
+				if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+					continue;
+
+				pdu->status = ISCSI_PDU_NOT_RECEIVED;
+			}
+		}
+
+next:
+		cmd->outstanding_r2ts--;
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	/*
+	 * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR
+	 * TASK_REASSIGN to the last R2T in the list..  We are also careful
+	 * to check that the Initiator is not requesting R2Ts for DataOUT
+	 * sequences it has already completed.
+	 *
+	 * Free each R2T in question and adjust values in struct iscsi_cmd
+	 * accordingly so iscsit_build_r2ts_for_cmd() do the rest of
+	 * the work after the TMR TASK_REASSIGN Response is sent.
+	 */
+drop_unacknowledged_r2ts:
+
+	cmd->cmd_flags &= ~ICF_SENT_LAST_R2T;
+	cmd->r2t_sn = tmr_req->exp_data_sn;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) {
+		/*
+		 * Skip up to the R2T Sequence number provided by the
+		 * iSCSI TASK_REASSIGN TMR
+		 */
+		if (r2t->r2t_sn < tmr_req->exp_data_sn)
+			continue;
+
+		if (r2t->seq_complete) {
+			pr_err("Initiator is requesting R2Ts from"
+				" R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u,"
+				" Length: %u is already complete."
+				"   BAD INITIATOR ERL=2 IMPLEMENTATION!\n",
+				tmr_req->exp_data_sn, r2t->r2t_sn,
+				r2t->offset, r2t->xfer_len);
+			spin_unlock_bh(&cmd->r2t_lock);
+			return -1;
+		}
+
+		if (r2t->recovery_r2t) {
+			iscsit_free_r2t(r2t, cmd);
+			continue;
+		}
+
+		/*		   DataSequenceInOrder=Yes:
+		 *
+		 * Taking into account the iSCSI implementation requirement of
+		 * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and
+		 * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts
+		 * entire transfer length from the commands R2T offset marker.
+		 *
+		 *		   DataSequenceInOrder=No:
+		 *
+		 * We subtract the difference from struct iscsi_seq between the
+		 * current offset and original offset from cmd->write_data_done
+		 * for account for DataOUT PDUs already received.  Then reset
+		 * the current offset to the original and zero out the current
+		 * burst length,  to make sure we re-request the entire DataOUT
+		 * sequence.
+		 */
+		if (conn->sess->sess_ops->DataSequenceInOrder)
+			cmd->r2t_offset -= r2t->xfer_len;
+		else
+			cmd->seq_send_order--;
+
+		cmd->outstanding_r2ts--;
+		iscsit_free_r2t(r2t, cmd);
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return 0;
+}
+
+/*
+ *	Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for
+ *	a given struct iscsi_cmd.
+ */
+int iscsit_check_task_reassign_expdatasn(
+	struct iscsi_tmr_req *tmr_req,
+	struct iscsi_conn *conn)
+{
+	struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
+	struct se_cmd *se_cmd = se_tmr->ref_cmd;
+	struct iscsi_cmd *ref_cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+	if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD)
+		return 0;
+
+	if (se_cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION)
+		return 0;
+
+	if (ref_cmd->data_direction == DMA_NONE)
+		return 0;
+
+	/*
+	 * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN
+	 * of DataIN the Initiator is expecting.
+	 *
+	 * Also check that the Initiator is not re-requesting DataIN that has
+	 * already been acknowledged with a DataAck SNACK.
+	 */
+	if (ref_cmd->data_direction == DMA_FROM_DEVICE) {
+		if (tmr_req->exp_data_sn > ref_cmd->data_sn) {
+			pr_err("Received ExpDataSN: 0x%08x for READ"
+				" in TMR TASK_REASSIGN greater than command's"
+				" DataSN: 0x%08x.\n", tmr_req->exp_data_sn,
+				ref_cmd->data_sn);
+			return -1;
+		}
+		if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+		    (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) {
+			pr_err("Received ExpDataSN: 0x%08x for READ"
+				" in TMR TASK_REASSIGN for previously"
+				" acknowledged DataIN: 0x%08x,"
+				" protocol error\n", tmr_req->exp_data_sn,
+				ref_cmd->acked_data_sn);
+			return -1;
+		}
+		return iscsit_task_reassign_prepare_read(tmr_req, conn);
+	}
+
+	/*
+	 * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN
+	 * for R2Ts the Initiator is expecting.
+	 *
+	 * Do the magic in iscsit_task_reassign_prepare_write().
+	 */
+	if (ref_cmd->data_direction == DMA_TO_DEVICE) {
+		if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) {
+			pr_err("Received ExpDataSN: 0x%08x for WRITE"
+				" in TMR TASK_REASSIGN greater than command's"
+				" R2TSN: 0x%08x.\n", tmr_req->exp_data_sn,
+					ref_cmd->r2t_sn);
+			return -1;
+		}
+		return iscsit_task_reassign_prepare_write(tmr_req, conn);
+	}
+
+	pr_err("Unknown iSCSI data_direction: 0x%02x\n",
+			ref_cmd->data_direction);
+
+	return -1;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tmr.h b/drivers/target/iscsi/iscsi_target_tmr.h
new file mode 100644
index 0000000..142e992
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tmr.h
@@ -0,0 +1,14 @@
+#ifndef ISCSI_TARGET_TMR_H
+#define ISCSI_TARGET_TMR_H
+
+extern u8 iscsit_tmr_abort_task(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_tmr_task_warm_reset(struct iscsi_conn *, struct iscsi_tmr_req *,
+			unsigned char *);
+extern int iscsit_tmr_task_cold_reset(struct iscsi_conn *, struct iscsi_tmr_req *,
+			unsigned char *);
+extern u8 iscsit_tmr_task_reassign(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_tmr_post_handler(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_check_task_reassign_expdatasn(struct iscsi_tmr_req *,
+			struct iscsi_conn *);
+
+#endif /* ISCSI_TARGET_TMR_H */
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
new file mode 100644
index 0000000..d4cf2cd
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -0,0 +1,759 @@
+/*******************************************************************************
+ * This file contains iSCSI Target Portal Group related functions.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_tpg.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_nodeattrib.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_parameters.h"
+
+struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)
+{
+	struct iscsi_portal_group *tpg;
+
+	tpg = kzalloc(sizeof(struct iscsi_portal_group), GFP_KERNEL);
+	if (!tpg) {
+		pr_err("Unable to allocate struct iscsi_portal_group\n");
+		return NULL;
+	}
+
+	tpg->tpgt = tpgt;
+	tpg->tpg_state = TPG_STATE_FREE;
+	tpg->tpg_tiqn = tiqn;
+	INIT_LIST_HEAD(&tpg->tpg_gnp_list);
+	INIT_LIST_HEAD(&tpg->tpg_list);
+	mutex_init(&tpg->tpg_access_lock);
+	mutex_init(&tpg->np_login_lock);
+	spin_lock_init(&tpg->tpg_state_lock);
+	spin_lock_init(&tpg->tpg_np_lock);
+
+	return tpg;
+}
+
+static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *);
+
+int iscsit_load_discovery_tpg(void)
+{
+	struct iscsi_param *param;
+	struct iscsi_portal_group *tpg;
+	int ret;
+
+	tpg = iscsit_alloc_portal_group(NULL, 1);
+	if (!tpg) {
+		pr_err("Unable to allocate struct iscsi_portal_group\n");
+		return -1;
+	}
+
+	ret = core_tpg_register(
+			&lio_target_fabric_configfs->tf_ops,
+			NULL, &tpg->tpg_se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_DISCOVERY);
+	if (ret < 0) {
+		kfree(tpg);
+		return -1;
+	}
+
+	tpg->sid = 1; /* First Assigned LIO Session ID */
+	iscsit_set_default_tpg_attribs(tpg);
+
+	if (iscsi_create_default_params(&tpg->param_list) < 0)
+		goto out;
+	/*
+	 * By default we disable authentication for discovery sessions,
+	 * this can be changed with:
+	 *
+	 * /sys/kernel/config/target/iscsi/discovery_auth/enforce_discovery_auth
+	 */
+	param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+	if (!param)
+		goto out;
+
+	if (iscsi_update_param_value(param, "CHAP,None") < 0)
+		goto out;
+
+	tpg->tpg_attrib.authentication = 0;
+
+	spin_lock(&tpg->tpg_state_lock);
+	tpg->tpg_state  = TPG_STATE_ACTIVE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	iscsit_global->discovery_tpg = tpg;
+	pr_debug("CORE[0] - Allocated Discovery TPG\n");
+
+	return 0;
+out:
+	if (tpg->sid == 1)
+		core_tpg_deregister(&tpg->tpg_se_tpg);
+	kfree(tpg);
+	return -1;
+}
+
+void iscsit_release_discovery_tpg(void)
+{
+	struct iscsi_portal_group *tpg = iscsit_global->discovery_tpg;
+
+	if (!tpg)
+		return;
+
+	core_tpg_deregister(&tpg->tpg_se_tpg);
+
+	kfree(tpg);
+	iscsit_global->discovery_tpg = NULL;
+}
+
+struct iscsi_portal_group *iscsit_get_tpg_from_np(
+	struct iscsi_tiqn *tiqn,
+	struct iscsi_np *np)
+{
+	struct iscsi_portal_group *tpg = NULL;
+	struct iscsi_tpg_np *tpg_np;
+
+	spin_lock(&tiqn->tiqn_tpg_lock);
+	list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
+
+		spin_lock(&tpg->tpg_state_lock);
+		if (tpg->tpg_state == TPG_STATE_FREE) {
+			spin_unlock(&tpg->tpg_state_lock);
+			continue;
+		}
+		spin_unlock(&tpg->tpg_state_lock);
+
+		spin_lock(&tpg->tpg_np_lock);
+		list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
+			if (tpg_np->tpg_np == np) {
+				spin_unlock(&tpg->tpg_np_lock);
+				spin_unlock(&tiqn->tiqn_tpg_lock);
+				return tpg;
+			}
+		}
+		spin_unlock(&tpg->tpg_np_lock);
+	}
+	spin_unlock(&tiqn->tiqn_tpg_lock);
+
+	return NULL;
+}
+
+int iscsit_get_tpg(
+	struct iscsi_portal_group *tpg)
+{
+	int ret;
+
+	ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
+	return ((ret != 0) || signal_pending(current)) ? -1 : 0;
+}
+
+void iscsit_put_tpg(struct iscsi_portal_group *tpg)
+{
+	mutex_unlock(&tpg->tpg_access_lock);
+}
+
+static void iscsit_clear_tpg_np_login_thread(
+	struct iscsi_tpg_np *tpg_np,
+	struct iscsi_portal_group *tpg)
+{
+	if (!tpg_np->tpg_np) {
+		pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
+		return;
+	}
+
+	iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg);
+}
+
+void iscsit_clear_tpg_np_login_threads(
+	struct iscsi_portal_group *tpg)
+{
+	struct iscsi_tpg_np *tpg_np;
+
+	spin_lock(&tpg->tpg_np_lock);
+	list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
+		if (!tpg_np->tpg_np) {
+			pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
+			continue;
+		}
+		spin_unlock(&tpg->tpg_np_lock);
+		iscsit_clear_tpg_np_login_thread(tpg_np, tpg);
+		spin_lock(&tpg->tpg_np_lock);
+	}
+	spin_unlock(&tpg->tpg_np_lock);
+}
+
+void iscsit_tpg_dump_params(struct iscsi_portal_group *tpg)
+{
+	iscsi_print_params(tpg->param_list);
+}
+
+static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	a->authentication = TA_AUTHENTICATION;
+	a->login_timeout = TA_LOGIN_TIMEOUT;
+	a->netif_timeout = TA_NETIF_TIMEOUT;
+	a->default_cmdsn_depth = TA_DEFAULT_CMDSN_DEPTH;
+	a->generate_node_acls = TA_GENERATE_NODE_ACLS;
+	a->cache_dynamic_acls = TA_CACHE_DYNAMIC_ACLS;
+	a->demo_mode_write_protect = TA_DEMO_MODE_WRITE_PROTECT;
+	a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT;
+}
+
+int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
+{
+	if (tpg->tpg_state != TPG_STATE_FREE) {
+		pr_err("Unable to add iSCSI Target Portal Group: %d"
+			" while not in TPG_STATE_FREE state.\n", tpg->tpgt);
+		return -EEXIST;
+	}
+	iscsit_set_default_tpg_attribs(tpg);
+
+	if (iscsi_create_default_params(&tpg->param_list) < 0)
+		goto err_out;
+
+	ISCSI_TPG_ATTRIB(tpg)->tpg = tpg;
+
+	spin_lock(&tpg->tpg_state_lock);
+	tpg->tpg_state	= TPG_STATE_INACTIVE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	spin_lock(&tiqn->tiqn_tpg_lock);
+	list_add_tail(&tpg->tpg_list, &tiqn->tiqn_tpg_list);
+	tiqn->tiqn_ntpgs++;
+	pr_debug("CORE[%s]_TPG[%hu] - Added iSCSI Target Portal Group\n",
+			tiqn->tiqn, tpg->tpgt);
+	spin_unlock(&tiqn->tiqn_tpg_lock);
+
+	return 0;
+err_out:
+	if (tpg->param_list) {
+		iscsi_release_param_list(tpg->param_list);
+		tpg->param_list = NULL;
+	}
+	kfree(tpg);
+	return -ENOMEM;
+}
+
+int iscsit_tpg_del_portal_group(
+	struct iscsi_tiqn *tiqn,
+	struct iscsi_portal_group *tpg,
+	int force)
+{
+	u8 old_state = tpg->tpg_state;
+
+	spin_lock(&tpg->tpg_state_lock);
+	tpg->tpg_state = TPG_STATE_INACTIVE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
+		pr_err("Unable to delete iSCSI Target Portal Group:"
+			" %hu while active sessions exist, and force=0\n",
+			tpg->tpgt);
+		tpg->tpg_state = old_state;
+		return -EPERM;
+	}
+
+	core_tpg_clear_object_luns(&tpg->tpg_se_tpg);
+
+	if (tpg->param_list) {
+		iscsi_release_param_list(tpg->param_list);
+		tpg->param_list = NULL;
+	}
+
+	core_tpg_deregister(&tpg->tpg_se_tpg);
+
+	spin_lock(&tpg->tpg_state_lock);
+	tpg->tpg_state = TPG_STATE_FREE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	spin_lock(&tiqn->tiqn_tpg_lock);
+	tiqn->tiqn_ntpgs--;
+	list_del(&tpg->tpg_list);
+	spin_unlock(&tiqn->tiqn_tpg_lock);
+
+	pr_debug("CORE[%s]_TPG[%hu] - Deleted iSCSI Target Portal Group\n",
+			tiqn->tiqn, tpg->tpgt);
+
+	kfree(tpg);
+	return 0;
+}
+
+int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *tpg)
+{
+	struct iscsi_param *param;
+	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+	spin_lock(&tpg->tpg_state_lock);
+	if (tpg->tpg_state == TPG_STATE_ACTIVE) {
+		pr_err("iSCSI target portal group: %hu is already"
+			" active, ignoring request.\n", tpg->tpgt);
+		spin_unlock(&tpg->tpg_state_lock);
+		return -EINVAL;
+	}
+	/*
+	 * Make sure that AuthMethod does not contain None as an option
+	 * unless explictly disabled.  Set the default to CHAP if authentication
+	 * is enforced (as per default), and remove the NONE option.
+	 */
+	param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+	if (!param) {
+		spin_unlock(&tpg->tpg_state_lock);
+		return -ENOMEM;
+	}
+
+	if (ISCSI_TPG_ATTRIB(tpg)->authentication) {
+		if (!strcmp(param->value, NONE))
+			if (iscsi_update_param_value(param, CHAP) < 0) {
+				spin_unlock(&tpg->tpg_state_lock);
+				return -ENOMEM;
+			}
+		if (iscsit_ta_authentication(tpg, 1) < 0) {
+			spin_unlock(&tpg->tpg_state_lock);
+			return -ENOMEM;
+		}
+	}
+
+	tpg->tpg_state = TPG_STATE_ACTIVE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	spin_lock(&tiqn->tiqn_tpg_lock);
+	tiqn->tiqn_active_tpgs++;
+	pr_debug("iSCSI_TPG[%hu] - Enabled iSCSI Target Portal Group\n",
+			tpg->tpgt);
+	spin_unlock(&tiqn->tiqn_tpg_lock);
+
+	return 0;
+}
+
+int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *tpg, int force)
+{
+	struct iscsi_tiqn *tiqn;
+	u8 old_state = tpg->tpg_state;
+
+	spin_lock(&tpg->tpg_state_lock);
+	if (tpg->tpg_state == TPG_STATE_INACTIVE) {
+		pr_err("iSCSI Target Portal Group: %hu is already"
+			" inactive, ignoring request.\n", tpg->tpgt);
+		spin_unlock(&tpg->tpg_state_lock);
+		return -EINVAL;
+	}
+	tpg->tpg_state = TPG_STATE_INACTIVE;
+	spin_unlock(&tpg->tpg_state_lock);
+
+	iscsit_clear_tpg_np_login_threads(tpg);
+
+	if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
+		spin_lock(&tpg->tpg_state_lock);
+		tpg->tpg_state = old_state;
+		spin_unlock(&tpg->tpg_state_lock);
+		pr_err("Unable to disable iSCSI Target Portal Group:"
+			" %hu while active sessions exist, and force=0\n",
+			tpg->tpgt);
+		return -EPERM;
+	}
+
+	tiqn = tpg->tpg_tiqn;
+	if (!tiqn || (tpg == iscsit_global->discovery_tpg))
+		return 0;
+
+	spin_lock(&tiqn->tiqn_tpg_lock);
+	tiqn->tiqn_active_tpgs--;
+	pr_debug("iSCSI_TPG[%hu] - Disabled iSCSI Target Portal Group\n",
+			tpg->tpgt);
+	spin_unlock(&tiqn->tiqn_tpg_lock);
+
+	return 0;
+}
+
+struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(
+	struct iscsi_session *sess)
+{
+	struct se_session *se_sess = sess->se_sess;
+	struct se_node_acl *se_nacl = se_sess->se_node_acl;
+	struct iscsi_node_acl *acl = container_of(se_nacl, struct iscsi_node_acl,
+					se_node_acl);
+
+	return &acl->node_attrib;
+}
+
+struct iscsi_tpg_np *iscsit_tpg_locate_child_np(
+	struct iscsi_tpg_np *tpg_np,
+	int network_transport)
+{
+	struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp;
+
+	spin_lock(&tpg_np->tpg_np_parent_lock);
+	list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp,
+			&tpg_np->tpg_np_parent_list, tpg_np_child_list) {
+		if (tpg_np_child->tpg_np->np_network_transport ==
+				network_transport) {
+			spin_unlock(&tpg_np->tpg_np_parent_lock);
+			return tpg_np_child;
+		}
+	}
+	spin_unlock(&tpg_np->tpg_np_parent_lock);
+
+	return NULL;
+}
+
+struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
+	struct iscsi_portal_group *tpg,
+	struct __kernel_sockaddr_storage *sockaddr,
+	char *ip_str,
+	struct iscsi_tpg_np *tpg_np_parent,
+	int network_transport)
+{
+	struct iscsi_np *np;
+	struct iscsi_tpg_np *tpg_np;
+
+	tpg_np = kzalloc(sizeof(struct iscsi_tpg_np), GFP_KERNEL);
+	if (!tpg_np) {
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_tpg_np.\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	np = iscsit_add_np(sockaddr, ip_str, network_transport);
+	if (IS_ERR(np)) {
+		kfree(tpg_np);
+		return ERR_CAST(np);
+	}
+
+	INIT_LIST_HEAD(&tpg_np->tpg_np_list);
+	INIT_LIST_HEAD(&tpg_np->tpg_np_child_list);
+	INIT_LIST_HEAD(&tpg_np->tpg_np_parent_list);
+	spin_lock_init(&tpg_np->tpg_np_parent_lock);
+	tpg_np->tpg_np		= np;
+	tpg_np->tpg		= tpg;
+
+	spin_lock(&tpg->tpg_np_lock);
+	list_add_tail(&tpg_np->tpg_np_list, &tpg->tpg_gnp_list);
+	tpg->num_tpg_nps++;
+	if (tpg->tpg_tiqn)
+		tpg->tpg_tiqn->tiqn_num_tpg_nps++;
+	spin_unlock(&tpg->tpg_np_lock);
+
+	if (tpg_np_parent) {
+		tpg_np->tpg_np_parent = tpg_np_parent;
+		spin_lock(&tpg_np_parent->tpg_np_parent_lock);
+		list_add_tail(&tpg_np->tpg_np_child_list,
+			&tpg_np_parent->tpg_np_parent_list);
+		spin_unlock(&tpg_np_parent->tpg_np_parent_lock);
+	}
+
+	pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n",
+		tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+		(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
+
+	return tpg_np;
+}
+
+static int iscsit_tpg_release_np(
+	struct iscsi_tpg_np *tpg_np,
+	struct iscsi_portal_group *tpg,
+	struct iscsi_np *np)
+{
+	iscsit_clear_tpg_np_login_thread(tpg_np, tpg);
+
+	pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n",
+		tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+		(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
+
+	tpg_np->tpg_np = NULL;
+	tpg_np->tpg = NULL;
+	kfree(tpg_np);
+	/*
+	 * iscsit_del_np() will shutdown struct iscsi_np when last TPG reference is released.
+	 */
+	return iscsit_del_np(np);
+}
+
+int iscsit_tpg_del_network_portal(
+	struct iscsi_portal_group *tpg,
+	struct iscsi_tpg_np *tpg_np)
+{
+	struct iscsi_np *np;
+	struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp;
+	int ret = 0;
+
+	np = tpg_np->tpg_np;
+	if (!np) {
+		pr_err("Unable to locate struct iscsi_np from"
+				" struct iscsi_tpg_np\n");
+		return -EINVAL;
+	}
+
+	if (!tpg_np->tpg_np_parent) {
+		/*
+		 * We are the parent tpg network portal.  Release all of the
+		 * child tpg_np's (eg: the non ISCSI_TCP ones) on our parent
+		 * list first.
+		 */
+		list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp,
+				&tpg_np->tpg_np_parent_list,
+				tpg_np_child_list) {
+			ret = iscsit_tpg_del_network_portal(tpg, tpg_np_child);
+			if (ret < 0)
+				pr_err("iscsit_tpg_del_network_portal()"
+					" failed: %d\n", ret);
+		}
+	} else {
+		/*
+		 * We are not the parent ISCSI_TCP tpg network portal.  Release
+		 * our own network portals from the child list.
+		 */
+		spin_lock(&tpg_np->tpg_np_parent->tpg_np_parent_lock);
+		list_del(&tpg_np->tpg_np_child_list);
+		spin_unlock(&tpg_np->tpg_np_parent->tpg_np_parent_lock);
+	}
+
+	spin_lock(&tpg->tpg_np_lock);
+	list_del(&tpg_np->tpg_np_list);
+	tpg->num_tpg_nps--;
+	if (tpg->tpg_tiqn)
+		tpg->tpg_tiqn->tiqn_num_tpg_nps--;
+	spin_unlock(&tpg->tpg_np_lock);
+
+	return iscsit_tpg_release_np(tpg_np, tpg, np);
+}
+
+int iscsit_tpg_set_initiator_node_queue_depth(
+	struct iscsi_portal_group *tpg,
+	unsigned char *initiatorname,
+	u32 queue_depth,
+	int force)
+{
+	return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg,
+		initiatorname, queue_depth, force);
+}
+
+int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication)
+{
+	unsigned char buf1[256], buf2[256], *none = NULL;
+	int len;
+	struct iscsi_param *param;
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if ((authentication != 1) && (authentication != 0)) {
+		pr_err("Illegal value for authentication parameter:"
+			" %u, ignoring request.\n", authentication);
+		return -1;
+	}
+
+	memset(buf1, 0, sizeof(buf1));
+	memset(buf2, 0, sizeof(buf2));
+
+	param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+	if (!param)
+		return -EINVAL;
+
+	if (authentication) {
+		snprintf(buf1, sizeof(buf1), "%s", param->value);
+		none = strstr(buf1, NONE);
+		if (!none)
+			goto out;
+		if (!strncmp(none + 4, ",", 1)) {
+			if (!strcmp(buf1, none))
+				sprintf(buf2, "%s", none+5);
+			else {
+				none--;
+				*none = '\0';
+				len = sprintf(buf2, "%s", buf1);
+				none += 5;
+				sprintf(buf2 + len, "%s", none);
+			}
+		} else {
+			none--;
+			*none = '\0';
+			sprintf(buf2, "%s", buf1);
+		}
+		if (iscsi_update_param_value(param, buf2) < 0)
+			return -EINVAL;
+	} else {
+		snprintf(buf1, sizeof(buf1), "%s", param->value);
+		none = strstr(buf1, NONE);
+		if ((none))
+			goto out;
+		strncat(buf1, ",", strlen(","));
+		strncat(buf1, NONE, strlen(NONE));
+		if (iscsi_update_param_value(param, buf1) < 0)
+			return -EINVAL;
+	}
+
+out:
+	a->authentication = authentication;
+	pr_debug("%s iSCSI Authentication Methods for TPG: %hu.\n",
+		a->authentication ? "Enforcing" : "Disabling", tpg->tpgt);
+
+	return 0;
+}
+
+int iscsit_ta_login_timeout(
+	struct iscsi_portal_group *tpg,
+	u32 login_timeout)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if (login_timeout > TA_LOGIN_TIMEOUT_MAX) {
+		pr_err("Requested Login Timeout %u larger than maximum"
+			" %u\n", login_timeout, TA_LOGIN_TIMEOUT_MAX);
+		return -EINVAL;
+	} else if (login_timeout < TA_LOGIN_TIMEOUT_MIN) {
+		pr_err("Requested Logout Timeout %u smaller than"
+			" minimum %u\n", login_timeout, TA_LOGIN_TIMEOUT_MIN);
+		return -EINVAL;
+	}
+
+	a->login_timeout = login_timeout;
+	pr_debug("Set Logout Timeout to %u for Target Portal Group"
+		" %hu\n", a->login_timeout, tpg->tpgt);
+
+	return 0;
+}
+
+int iscsit_ta_netif_timeout(
+	struct iscsi_portal_group *tpg,
+	u32 netif_timeout)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if (netif_timeout > TA_NETIF_TIMEOUT_MAX) {
+		pr_err("Requested Network Interface Timeout %u larger"
+			" than maximum %u\n", netif_timeout,
+				TA_NETIF_TIMEOUT_MAX);
+		return -EINVAL;
+	} else if (netif_timeout < TA_NETIF_TIMEOUT_MIN) {
+		pr_err("Requested Network Interface Timeout %u smaller"
+			" than minimum %u\n", netif_timeout,
+				TA_NETIF_TIMEOUT_MIN);
+		return -EINVAL;
+	}
+
+	a->netif_timeout = netif_timeout;
+	pr_debug("Set Network Interface Timeout to %u for"
+		" Target Portal Group %hu\n", a->netif_timeout, tpg->tpgt);
+
+	return 0;
+}
+
+int iscsit_ta_generate_node_acls(
+	struct iscsi_portal_group *tpg,
+	u32 flag)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if ((flag != 0) && (flag != 1)) {
+		pr_err("Illegal value %d\n", flag);
+		return -EINVAL;
+	}
+
+	a->generate_node_acls = flag;
+	pr_debug("iSCSI_TPG[%hu] - Generate Initiator Portal Group ACLs: %s\n",
+		tpg->tpgt, (a->generate_node_acls) ? "Enabled" : "Disabled");
+
+	return 0;
+}
+
+int iscsit_ta_default_cmdsn_depth(
+	struct iscsi_portal_group *tpg,
+	u32 tcq_depth)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if (tcq_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) {
+		pr_err("Requested Default Queue Depth: %u larger"
+			" than maximum %u\n", tcq_depth,
+				TA_DEFAULT_CMDSN_DEPTH_MAX);
+		return -EINVAL;
+	} else if (tcq_depth < TA_DEFAULT_CMDSN_DEPTH_MIN) {
+		pr_err("Requested Default Queue Depth: %u smaller"
+			" than minimum %u\n", tcq_depth,
+				TA_DEFAULT_CMDSN_DEPTH_MIN);
+		return -EINVAL;
+	}
+
+	a->default_cmdsn_depth = tcq_depth;
+	pr_debug("iSCSI_TPG[%hu] - Set Default CmdSN TCQ Depth to %u\n",
+		tpg->tpgt, a->default_cmdsn_depth);
+
+	return 0;
+}
+
+int iscsit_ta_cache_dynamic_acls(
+	struct iscsi_portal_group *tpg,
+	u32 flag)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if ((flag != 0) && (flag != 1)) {
+		pr_err("Illegal value %d\n", flag);
+		return -EINVAL;
+	}
+
+	a->cache_dynamic_acls = flag;
+	pr_debug("iSCSI_TPG[%hu] - Cache Dynamic Initiator Portal Group"
+		" ACLs %s\n", tpg->tpgt, (a->cache_dynamic_acls) ?
+		"Enabled" : "Disabled");
+
+	return 0;
+}
+
+int iscsit_ta_demo_mode_write_protect(
+	struct iscsi_portal_group *tpg,
+	u32 flag)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if ((flag != 0) && (flag != 1)) {
+		pr_err("Illegal value %d\n", flag);
+		return -EINVAL;
+	}
+
+	a->demo_mode_write_protect = flag;
+	pr_debug("iSCSI_TPG[%hu] - Demo Mode Write Protect bit: %s\n",
+		tpg->tpgt, (a->demo_mode_write_protect) ? "ON" : "OFF");
+
+	return 0;
+}
+
+int iscsit_ta_prod_mode_write_protect(
+	struct iscsi_portal_group *tpg,
+	u32 flag)
+{
+	struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+	if ((flag != 0) && (flag != 1)) {
+		pr_err("Illegal value %d\n", flag);
+		return -EINVAL;
+	}
+
+	a->prod_mode_write_protect = flag;
+	pr_debug("iSCSI_TPG[%hu] - Production Mode Write Protect bit:"
+		" %s\n", tpg->tpgt, (a->prod_mode_write_protect) ?
+		"ON" : "OFF");
+
+	return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
new file mode 100644
index 0000000..dda48c1
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -0,0 +1,41 @@
+#ifndef ISCSI_TARGET_TPG_H
+#define ISCSI_TARGET_TPG_H
+
+extern struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *, u16);
+extern int iscsit_load_discovery_tpg(void);
+extern void iscsit_release_discovery_tpg(void);
+extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *,
+			struct iscsi_np *);
+extern int iscsit_get_tpg(struct iscsi_portal_group *);
+extern void iscsit_put_tpg(struct iscsi_portal_group *);
+extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *);
+extern void iscsit_tpg_dump_params(struct iscsi_portal_group *);
+extern int iscsit_tpg_add_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *);
+extern int iscsit_tpg_del_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *,
+			int);
+extern int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *);
+extern int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *, int);
+extern struct iscsi_node_acl *iscsit_tpg_add_initiator_node_acl(
+			struct iscsi_portal_group *, const char *, u32);
+extern void iscsit_tpg_del_initiator_node_acl(struct iscsi_portal_group *,
+			struct se_node_acl *);
+extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session *);
+extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *);
+extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int);
+extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *,
+			struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *,
+			int);
+extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *,
+			struct iscsi_tpg_np *);
+extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *,
+			unsigned char *, u32, int);
+extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_generate_node_acls(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_default_cmdsn_depth(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_cache_dynamic_acls(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32);
+
+#endif /* ISCSI_TARGET_TPG_H */
diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c
new file mode 100644
index 0000000..0baac5b
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tq.c
@@ -0,0 +1,551 @@
+/*******************************************************************************
+ * This file contains the iSCSI Login Thread and Thread Queue functions.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/bitmap.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target.h"
+
+static LIST_HEAD(active_ts_list);
+static LIST_HEAD(inactive_ts_list);
+static DEFINE_SPINLOCK(active_ts_lock);
+static DEFINE_SPINLOCK(inactive_ts_lock);
+static DEFINE_SPINLOCK(ts_bitmap_lock);
+
+static void iscsi_add_ts_to_active_list(struct iscsi_thread_set *ts)
+{
+	spin_lock(&active_ts_lock);
+	list_add_tail(&ts->ts_list, &active_ts_list);
+	iscsit_global->active_ts++;
+	spin_unlock(&active_ts_lock);
+}
+
+extern void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts)
+{
+	spin_lock(&inactive_ts_lock);
+	list_add_tail(&ts->ts_list, &inactive_ts_list);
+	iscsit_global->inactive_ts++;
+	spin_unlock(&inactive_ts_lock);
+}
+
+static void iscsi_del_ts_from_active_list(struct iscsi_thread_set *ts)
+{
+	spin_lock(&active_ts_lock);
+	list_del(&ts->ts_list);
+	iscsit_global->active_ts--;
+	spin_unlock(&active_ts_lock);
+}
+
+static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void)
+{
+	struct iscsi_thread_set *ts;
+
+	spin_lock(&inactive_ts_lock);
+	if (list_empty(&inactive_ts_list)) {
+		spin_unlock(&inactive_ts_lock);
+		return NULL;
+	}
+
+	list_for_each_entry(ts, &inactive_ts_list, ts_list)
+		break;
+
+	list_del(&ts->ts_list);
+	iscsit_global->inactive_ts--;
+	spin_unlock(&inactive_ts_lock);
+
+	return ts;
+}
+
+extern int iscsi_allocate_thread_sets(u32 thread_pair_count)
+{
+	int allocated_thread_pair_count = 0, i, thread_id;
+	struct iscsi_thread_set *ts = NULL;
+
+	for (i = 0; i < thread_pair_count; i++) {
+		ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL);
+		if (!ts) {
+			pr_err("Unable to allocate memory for"
+					" thread set.\n");
+			return allocated_thread_pair_count;
+		}
+		/*
+		 * Locate the next available regision in the thread_set_bitmap
+		 */
+		spin_lock(&ts_bitmap_lock);
+		thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap,
+				iscsit_global->ts_bitmap_count, get_order(1));
+		spin_unlock(&ts_bitmap_lock);
+		if (thread_id < 0) {
+			pr_err("bitmap_find_free_region() failed for"
+				" thread_set_bitmap\n");
+			kfree(ts);
+			return allocated_thread_pair_count;
+		}
+
+		ts->thread_id = thread_id;
+		ts->status = ISCSI_THREAD_SET_FREE;
+		INIT_LIST_HEAD(&ts->ts_list);
+		spin_lock_init(&ts->ts_state_lock);
+		init_completion(&ts->rx_post_start_comp);
+		init_completion(&ts->tx_post_start_comp);
+		init_completion(&ts->rx_restart_comp);
+		init_completion(&ts->tx_restart_comp);
+		init_completion(&ts->rx_start_comp);
+		init_completion(&ts->tx_start_comp);
+
+		ts->create_threads = 1;
+		ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s",
+					ISCSI_TX_THREAD_NAME);
+		if (IS_ERR(ts->tx_thread)) {
+			dump_stack();
+			pr_err("Unable to start iscsi_target_tx_thread\n");
+			break;
+		}
+
+		ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s",
+					ISCSI_RX_THREAD_NAME);
+		if (IS_ERR(ts->rx_thread)) {
+			kthread_stop(ts->tx_thread);
+			pr_err("Unable to start iscsi_target_rx_thread\n");
+			break;
+		}
+		ts->create_threads = 0;
+
+		iscsi_add_ts_to_inactive_list(ts);
+		allocated_thread_pair_count++;
+	}
+
+	pr_debug("Spawned %d thread set(s) (%d total threads).\n",
+		allocated_thread_pair_count, allocated_thread_pair_count * 2);
+	return allocated_thread_pair_count;
+}
+
+extern void iscsi_deallocate_thread_sets(void)
+{
+	u32 released_count = 0;
+	struct iscsi_thread_set *ts = NULL;
+
+	while ((ts = iscsi_get_ts_from_inactive_list())) {
+
+		spin_lock_bh(&ts->ts_state_lock);
+		ts->status = ISCSI_THREAD_SET_DIE;
+		spin_unlock_bh(&ts->ts_state_lock);
+
+		if (ts->rx_thread) {
+			send_sig(SIGINT, ts->rx_thread, 1);
+			kthread_stop(ts->rx_thread);
+		}
+		if (ts->tx_thread) {
+			send_sig(SIGINT, ts->tx_thread, 1);
+			kthread_stop(ts->tx_thread);
+		}
+		/*
+		 * Release this thread_id in the thread_set_bitmap
+		 */
+		spin_lock(&ts_bitmap_lock);
+		bitmap_release_region(iscsit_global->ts_bitmap,
+				ts->thread_id, get_order(1));
+		spin_unlock(&ts_bitmap_lock);
+
+		released_count++;
+		kfree(ts);
+	}
+
+	if (released_count)
+		pr_debug("Stopped %d thread set(s) (%d total threads)."
+			"\n", released_count, released_count * 2);
+}
+
+static void iscsi_deallocate_extra_thread_sets(void)
+{
+	u32 orig_count, released_count = 0;
+	struct iscsi_thread_set *ts = NULL;
+
+	orig_count = TARGET_THREAD_SET_COUNT;
+
+	while ((iscsit_global->inactive_ts + 1) > orig_count) {
+		ts = iscsi_get_ts_from_inactive_list();
+		if (!ts)
+			break;
+
+		spin_lock_bh(&ts->ts_state_lock);
+		ts->status = ISCSI_THREAD_SET_DIE;
+		spin_unlock_bh(&ts->ts_state_lock);
+
+		if (ts->rx_thread) {
+			send_sig(SIGINT, ts->rx_thread, 1);
+			kthread_stop(ts->rx_thread);
+		}
+		if (ts->tx_thread) {
+			send_sig(SIGINT, ts->tx_thread, 1);
+			kthread_stop(ts->tx_thread);
+		}
+		/*
+		 * Release this thread_id in the thread_set_bitmap
+		 */
+		spin_lock(&ts_bitmap_lock);
+		bitmap_release_region(iscsit_global->ts_bitmap,
+				ts->thread_id, get_order(1));
+		spin_unlock(&ts_bitmap_lock);
+
+		released_count++;
+		kfree(ts);
+	}
+
+	if (released_count) {
+		pr_debug("Stopped %d thread set(s) (%d total threads)."
+			"\n", released_count, released_count * 2);
+	}
+}
+
+void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts)
+{
+	iscsi_add_ts_to_active_list(ts);
+
+	spin_lock_bh(&ts->ts_state_lock);
+	conn->thread_set = ts;
+	ts->conn = conn;
+	spin_unlock_bh(&ts->ts_state_lock);
+	/*
+	 * Start up the RX thread and wait on rx_post_start_comp.  The RX
+	 * Thread will then do the same for the TX Thread in
+	 * iscsi_rx_thread_pre_handler().
+	 */
+	complete(&ts->rx_start_comp);
+	wait_for_completion(&ts->rx_post_start_comp);
+}
+
+struct iscsi_thread_set *iscsi_get_thread_set(void)
+{
+	int allocate_ts = 0;
+	struct completion comp;
+	struct iscsi_thread_set *ts = NULL;
+	/*
+	 * If no inactive thread set is available on the first call to
+	 * iscsi_get_ts_from_inactive_list(), sleep for a second and
+	 * try again.  If still none are available after two attempts,
+	 * allocate a set ourselves.
+	 */
+get_set:
+	ts = iscsi_get_ts_from_inactive_list();
+	if (!ts) {
+		if (allocate_ts == 2)
+			iscsi_allocate_thread_sets(1);
+
+		init_completion(&comp);
+		wait_for_completion_timeout(&comp, 1 * HZ);
+
+		allocate_ts++;
+		goto get_set;
+	}
+
+	ts->delay_inactive = 1;
+	ts->signal_sent = 0;
+	ts->thread_count = 2;
+	init_completion(&ts->rx_restart_comp);
+	init_completion(&ts->tx_restart_comp);
+
+	return ts;
+}
+
+void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear)
+{
+	struct iscsi_thread_set *ts = NULL;
+
+	if (!conn->thread_set) {
+		pr_err("struct iscsi_conn->thread_set is NULL\n");
+		return;
+	}
+	ts = conn->thread_set;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	ts->thread_clear &= ~thread_clear;
+
+	if ((thread_clear & ISCSI_CLEAR_RX_THREAD) &&
+	    (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD))
+		complete(&ts->rx_restart_comp);
+	else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) &&
+		 (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD))
+		complete(&ts->tx_restart_comp);
+	spin_unlock_bh(&ts->ts_state_lock);
+}
+
+void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent)
+{
+	struct iscsi_thread_set *ts = NULL;
+
+	if (!conn->thread_set) {
+		pr_err("struct iscsi_conn->thread_set is NULL\n");
+		return;
+	}
+	ts = conn->thread_set;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	ts->signal_sent |= signal_sent;
+	spin_unlock_bh(&ts->ts_state_lock);
+}
+
+int iscsi_release_thread_set(struct iscsi_conn *conn)
+{
+	int thread_called = 0;
+	struct iscsi_thread_set *ts = NULL;
+
+	if (!conn || !conn->thread_set) {
+		pr_err("connection or thread set pointer is NULL\n");
+		BUG();
+	}
+	ts = conn->thread_set;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	ts->status = ISCSI_THREAD_SET_RESET;
+
+	if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME,
+			strlen(ISCSI_RX_THREAD_NAME)))
+		thread_called = ISCSI_RX_THREAD;
+	else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME,
+			strlen(ISCSI_TX_THREAD_NAME)))
+		thread_called = ISCSI_TX_THREAD;
+
+	if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) &&
+	   (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) {
+
+		if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) {
+			send_sig(SIGINT, ts->rx_thread, 1);
+			ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
+		}
+		ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD;
+		spin_unlock_bh(&ts->ts_state_lock);
+		wait_for_completion(&ts->rx_restart_comp);
+		spin_lock_bh(&ts->ts_state_lock);
+		ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD;
+	}
+	if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) &&
+	   (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) {
+
+		if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) {
+			send_sig(SIGINT, ts->tx_thread, 1);
+			ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
+		}
+		ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD;
+		spin_unlock_bh(&ts->ts_state_lock);
+		wait_for_completion(&ts->tx_restart_comp);
+		spin_lock_bh(&ts->ts_state_lock);
+		ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD;
+	}
+
+	ts->conn = NULL;
+	ts->status = ISCSI_THREAD_SET_FREE;
+	spin_unlock_bh(&ts->ts_state_lock);
+
+	return 0;
+}
+
+int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn)
+{
+	struct iscsi_thread_set *ts;
+
+	if (!conn->thread_set)
+		return -1;
+	ts = conn->thread_set;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	if (ts->status != ISCSI_THREAD_SET_ACTIVE) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		return -1;
+	}
+
+	if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) {
+		send_sig(SIGINT, ts->tx_thread, 1);
+		ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
+	}
+	if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) {
+		send_sig(SIGINT, ts->rx_thread, 1);
+		ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
+	}
+	spin_unlock_bh(&ts->ts_state_lock);
+
+	return 0;
+}
+
+static void iscsi_check_to_add_additional_sets(void)
+{
+	int thread_sets_add;
+
+	spin_lock(&inactive_ts_lock);
+	thread_sets_add = iscsit_global->inactive_ts;
+	spin_unlock(&inactive_ts_lock);
+	if (thread_sets_add == 1)
+		iscsi_allocate_thread_sets(1);
+}
+
+static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+	spin_lock_bh(&ts->ts_state_lock);
+	if ((ts->status == ISCSI_THREAD_SET_DIE) || signal_pending(current)) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		return -1;
+	}
+	spin_unlock_bh(&ts->ts_state_lock);
+
+	return 0;
+}
+
+struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+	int ret;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	if (ts->create_threads) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		goto sleep;
+	}
+
+	flush_signals(current);
+
+	if (ts->delay_inactive && (--ts->thread_count == 0)) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		iscsi_del_ts_from_active_list(ts);
+
+		if (!iscsit_global->in_shutdown)
+			iscsi_deallocate_extra_thread_sets();
+
+		iscsi_add_ts_to_inactive_list(ts);
+		spin_lock_bh(&ts->ts_state_lock);
+	}
+
+	if ((ts->status == ISCSI_THREAD_SET_RESET) &&
+	    (ts->thread_clear & ISCSI_CLEAR_RX_THREAD))
+		complete(&ts->rx_restart_comp);
+
+	ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD;
+	spin_unlock_bh(&ts->ts_state_lock);
+sleep:
+	ret = wait_for_completion_interruptible(&ts->rx_start_comp);
+	if (ret != 0)
+		return NULL;
+
+	if (iscsi_signal_thread_pre_handler(ts) < 0)
+		return NULL;
+
+	if (!ts->conn) {
+		pr_err("struct iscsi_thread_set->conn is NULL for"
+			" thread_id: %d, going back to sleep\n", ts->thread_id);
+		goto sleep;
+	}
+	iscsi_check_to_add_additional_sets();
+	/*
+	 * The RX Thread starts up the TX Thread and sleeps.
+	 */
+	ts->thread_clear |= ISCSI_CLEAR_RX_THREAD;
+	complete(&ts->tx_start_comp);
+	wait_for_completion(&ts->tx_post_start_comp);
+
+	return ts->conn;
+}
+
+struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+	int ret;
+
+	spin_lock_bh(&ts->ts_state_lock);
+	if (ts->create_threads) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		goto sleep;
+	}
+
+	flush_signals(current);
+
+	if (ts->delay_inactive && (--ts->thread_count == 0)) {
+		spin_unlock_bh(&ts->ts_state_lock);
+		iscsi_del_ts_from_active_list(ts);
+
+		if (!iscsit_global->in_shutdown)
+			iscsi_deallocate_extra_thread_sets();
+
+		iscsi_add_ts_to_inactive_list(ts);
+		spin_lock_bh(&ts->ts_state_lock);
+	}
+	if ((ts->status == ISCSI_THREAD_SET_RESET) &&
+	    (ts->thread_clear & ISCSI_CLEAR_TX_THREAD))
+		complete(&ts->tx_restart_comp);
+
+	ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD;
+	spin_unlock_bh(&ts->ts_state_lock);
+sleep:
+	ret = wait_for_completion_interruptible(&ts->tx_start_comp);
+	if (ret != 0)
+		return NULL;
+
+	if (iscsi_signal_thread_pre_handler(ts) < 0)
+		return NULL;
+
+	if (!ts->conn) {
+		pr_err("struct iscsi_thread_set->conn is NULL for "
+			" thread_id: %d, going back to sleep\n",
+			ts->thread_id);
+		goto sleep;
+	}
+
+	iscsi_check_to_add_additional_sets();
+	/*
+	 * From the TX thread, up the tx_post_start_comp that the RX Thread is
+	 * sleeping on in iscsi_rx_thread_pre_handler(), then up the
+	 * rx_post_start_comp that iscsi_activate_thread_set() is sleeping on.
+	 */
+	ts->thread_clear |= ISCSI_CLEAR_TX_THREAD;
+	complete(&ts->tx_post_start_comp);
+	complete(&ts->rx_post_start_comp);
+
+	spin_lock_bh(&ts->ts_state_lock);
+	ts->status = ISCSI_THREAD_SET_ACTIVE;
+	spin_unlock_bh(&ts->ts_state_lock);
+
+	return ts->conn;
+}
+
+int iscsi_thread_set_init(void)
+{
+	int size;
+
+	iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS;
+
+	size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long);
+	iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL);
+	if (!iscsit_global->ts_bitmap) {
+		pr_err("Unable to allocate iscsit_global->ts_bitmap\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&active_ts_lock);
+	spin_lock_init(&inactive_ts_lock);
+	spin_lock_init(&ts_bitmap_lock);
+	INIT_LIST_HEAD(&active_ts_list);
+	INIT_LIST_HEAD(&inactive_ts_list);
+
+	return 0;
+}
+
+void iscsi_thread_set_free(void)
+{
+	kfree(iscsit_global->ts_bitmap);
+}
diff --git a/drivers/target/iscsi/iscsi_target_tq.h b/drivers/target/iscsi/iscsi_target_tq.h
new file mode 100644
index 0000000..26e6a95
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tq.h
@@ -0,0 +1,88 @@
+#ifndef ISCSI_THREAD_QUEUE_H
+#define ISCSI_THREAD_QUEUE_H
+
+/*
+ * Defines for thread sets.
+ */
+extern int iscsi_thread_set_force_reinstatement(struct iscsi_conn *);
+extern void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *);
+extern int iscsi_allocate_thread_sets(u32);
+extern void iscsi_deallocate_thread_sets(void);
+extern void iscsi_activate_thread_set(struct iscsi_conn *, struct iscsi_thread_set *);
+extern struct iscsi_thread_set *iscsi_get_thread_set(void);
+extern void iscsi_set_thread_clear(struct iscsi_conn *, u8);
+extern void iscsi_set_thread_set_signal(struct iscsi_conn *, u8);
+extern int iscsi_release_thread_set(struct iscsi_conn *);
+extern struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *);
+extern struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *);
+extern int iscsi_thread_set_init(void);
+extern void iscsi_thread_set_free(void);
+
+extern int iscsi_target_tx_thread(void *);
+extern int iscsi_target_rx_thread(void *);
+
+#define TARGET_THREAD_SET_COUNT			4
+
+#define ISCSI_RX_THREAD                         1
+#define ISCSI_TX_THREAD                         2
+#define ISCSI_RX_THREAD_NAME			"iscsi_trx"
+#define ISCSI_TX_THREAD_NAME			"iscsi_ttx"
+#define ISCSI_BLOCK_RX_THREAD			0x1
+#define ISCSI_BLOCK_TX_THREAD			0x2
+#define ISCSI_CLEAR_RX_THREAD			0x1
+#define ISCSI_CLEAR_TX_THREAD			0x2
+#define ISCSI_SIGNAL_RX_THREAD			0x1
+#define ISCSI_SIGNAL_TX_THREAD			0x2
+
+/* struct iscsi_thread_set->status */
+#define ISCSI_THREAD_SET_FREE			1
+#define ISCSI_THREAD_SET_ACTIVE			2
+#define ISCSI_THREAD_SET_DIE			3
+#define ISCSI_THREAD_SET_RESET			4
+#define ISCSI_THREAD_SET_DEALLOCATE_THREADS	5
+
+/* By default allow a maximum of 32K iSCSI connections */
+#define ISCSI_TS_BITMAP_BITS			32768
+
+struct iscsi_thread_set {
+	/* flags used for blocking and restarting sets */
+	int	blocked_threads;
+	/* flag for creating threads */
+	int	create_threads;
+	/* flag for delaying readding to inactive list */
+	int	delay_inactive;
+	/* status for thread set */
+	int	status;
+	/* which threads have had signals sent */
+	int	signal_sent;
+	/* flag for which threads exited first */
+	int	thread_clear;
+	/* Active threads in the thread set */
+	int	thread_count;
+	/* Unique thread ID */
+	u32	thread_id;
+	/* pointer to connection if set is active */
+	struct iscsi_conn	*conn;
+	/* used for controlling ts state accesses */
+	spinlock_t	ts_state_lock;
+	/* Used for rx side post startup */
+	struct completion	rx_post_start_comp;
+	/* Used for tx side post startup */
+	struct completion	tx_post_start_comp;
+	/* used for restarting thread queue */
+	struct completion	rx_restart_comp;
+	/* used for restarting thread queue */
+	struct completion	tx_restart_comp;
+	/* used for normal unused blocking */
+	struct completion	rx_start_comp;
+	/* used for normal unused blocking */
+	struct completion	tx_start_comp;
+	/* OS descriptor for rx thread */
+	struct task_struct	*rx_thread;
+	/* OS descriptor for tx thread */
+	struct task_struct	*tx_thread;
+	/* struct iscsi_thread_set in list list head*/
+	struct list_head	ts_list;
+};
+
+#endif   /*** ISCSI_THREAD_QUEUE_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
new file mode 100644
index 0000000..a1acb01
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -0,0 +1,1819 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target specific utility functions.
+ *
+ * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_tmr.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_configfs.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+#define PRINT_BUFF(buff, len)					\
+{								\
+	int zzz;						\
+								\
+	pr_debug("%d:\n", __LINE__);				\
+	for (zzz = 0; zzz < len; zzz++) {			\
+		if (zzz % 16 == 0) {				\
+			if (zzz)				\
+				pr_debug("\n");			\
+			pr_debug("%4i: ", zzz);			\
+		}						\
+		pr_debug("%02x ", (unsigned char) (buff)[zzz]);	\
+	}							\
+	if ((len + 1) % 16)					\
+		pr_debug("\n");					\
+}
+
+extern struct list_head g_tiqn_list;
+extern spinlock_t tiqn_lock;
+
+/*
+ *	Called with cmd->r2t_lock held.
+ */
+int iscsit_add_r2t_to_list(
+	struct iscsi_cmd *cmd,
+	u32 offset,
+	u32 xfer_len,
+	int recovery,
+	u32 r2t_sn)
+{
+	struct iscsi_r2t *r2t;
+
+	r2t = kmem_cache_zalloc(lio_r2t_cache, GFP_ATOMIC);
+	if (!r2t) {
+		pr_err("Unable to allocate memory for struct iscsi_r2t.\n");
+		return -1;
+	}
+	INIT_LIST_HEAD(&r2t->r2t_list);
+
+	r2t->recovery_r2t = recovery;
+	r2t->r2t_sn = (!r2t_sn) ? cmd->r2t_sn++ : r2t_sn;
+	r2t->offset = offset;
+	r2t->xfer_len = xfer_len;
+	list_add_tail(&r2t->r2t_list, &cmd->cmd_r2t_list);
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T);
+
+	spin_lock_bh(&cmd->r2t_lock);
+	return 0;
+}
+
+struct iscsi_r2t *iscsit_get_r2t_for_eos(
+	struct iscsi_cmd *cmd,
+	u32 offset,
+	u32 length)
+{
+	struct iscsi_r2t *r2t;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+		if ((r2t->offset <= offset) &&
+		    (r2t->offset + r2t->xfer_len) >= (offset + length)) {
+			spin_unlock_bh(&cmd->r2t_lock);
+			return r2t;
+		}
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	pr_err("Unable to locate R2T for Offset: %u, Length:"
+			" %u\n", offset, length);
+	return NULL;
+}
+
+struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *cmd)
+{
+	struct iscsi_r2t *r2t;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+		if (!r2t->sent_r2t) {
+			spin_unlock_bh(&cmd->r2t_lock);
+			return r2t;
+		}
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	pr_err("Unable to locate next R2T to send for ITT:"
+			" 0x%08x.\n", cmd->init_task_tag);
+	return NULL;
+}
+
+/*
+ *	Called with cmd->r2t_lock held.
+ */
+void iscsit_free_r2t(struct iscsi_r2t *r2t, struct iscsi_cmd *cmd)
+{
+	list_del(&r2t->r2t_list);
+	kmem_cache_free(lio_r2t_cache, r2t);
+}
+
+void iscsit_free_r2ts_from_list(struct iscsi_cmd *cmd)
+{
+	struct iscsi_r2t *r2t, *r2t_tmp;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list)
+		iscsit_free_r2t(r2t, cmd);
+	spin_unlock_bh(&cmd->r2t_lock);
+}
+
+/*
+ * May be called from software interrupt (timer) context for allocating
+ * iSCSI NopINs.
+ */
+struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)
+{
+	struct iscsi_cmd *cmd;
+
+	cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask);
+	if (!cmd) {
+		pr_err("Unable to allocate memory for struct iscsi_cmd.\n");
+		return NULL;
+	}
+
+	cmd->conn	= conn;
+	INIT_LIST_HEAD(&cmd->i_list);
+	INIT_LIST_HEAD(&cmd->datain_list);
+	INIT_LIST_HEAD(&cmd->cmd_r2t_list);
+	init_completion(&cmd->reject_comp);
+	spin_lock_init(&cmd->datain_lock);
+	spin_lock_init(&cmd->dataout_timeout_lock);
+	spin_lock_init(&cmd->istate_lock);
+	spin_lock_init(&cmd->error_lock);
+	spin_lock_init(&cmd->r2t_lock);
+
+	return cmd;
+}
+
+/*
+ * Called from iscsi_handle_scsi_cmd()
+ */
+struct iscsi_cmd *iscsit_allocate_se_cmd(
+	struct iscsi_conn *conn,
+	u32 data_length,
+	int data_direction,
+	int iscsi_task_attr)
+{
+	struct iscsi_cmd *cmd;
+	struct se_cmd *se_cmd;
+	int sam_task_attr;
+
+	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+	if (!cmd)
+		return NULL;
+
+	cmd->data_direction = data_direction;
+	cmd->data_length = data_length;
+	/*
+	 * Figure out the SAM Task Attribute for the incoming SCSI CDB
+	 */
+	if ((iscsi_task_attr == ISCSI_ATTR_UNTAGGED) ||
+	    (iscsi_task_attr == ISCSI_ATTR_SIMPLE))
+		sam_task_attr = MSG_SIMPLE_TAG;
+	else if (iscsi_task_attr == ISCSI_ATTR_ORDERED)
+		sam_task_attr = MSG_ORDERED_TAG;
+	else if (iscsi_task_attr == ISCSI_ATTR_HEAD_OF_QUEUE)
+		sam_task_attr = MSG_HEAD_TAG;
+	else if (iscsi_task_attr == ISCSI_ATTR_ACA)
+		sam_task_attr = MSG_ACA_TAG;
+	else {
+		pr_debug("Unknown iSCSI Task Attribute: 0x%02x, using"
+			" MSG_SIMPLE_TAG\n", iscsi_task_attr);
+		sam_task_attr = MSG_SIMPLE_TAG;
+	}
+
+	se_cmd = &cmd->se_cmd;
+	/*
+	 * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+	 */
+	transport_init_se_cmd(se_cmd, &lio_target_fabric_configfs->tf_ops,
+			conn->sess->se_sess, data_length, data_direction,
+			sam_task_attr, &cmd->sense_buffer[0]);
+	return cmd;
+}
+
+struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(
+	struct iscsi_conn *conn,
+	u8 function)
+{
+	struct iscsi_cmd *cmd;
+	struct se_cmd *se_cmd;
+	u8 tcm_function;
+
+	cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+	if (!cmd)
+		return NULL;
+
+	cmd->data_direction = DMA_NONE;
+
+	cmd->tmr_req = kzalloc(sizeof(struct iscsi_tmr_req), GFP_KERNEL);
+	if (!cmd->tmr_req) {
+		pr_err("Unable to allocate memory for"
+			" Task Management command!\n");
+		return NULL;
+	}
+	/*
+	 * TASK_REASSIGN for ERL=2 / connection stays inside of
+	 * LIO-Target $FABRIC_MOD
+	 */
+	if (function == ISCSI_TM_FUNC_TASK_REASSIGN)
+		return cmd;
+
+	se_cmd = &cmd->se_cmd;
+	/*
+	 * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+	 */
+	transport_init_se_cmd(se_cmd, &lio_target_fabric_configfs->tf_ops,
+				conn->sess->se_sess, 0, DMA_NONE,
+				MSG_SIMPLE_TAG, &cmd->sense_buffer[0]);
+
+	switch (function) {
+	case ISCSI_TM_FUNC_ABORT_TASK:
+		tcm_function = TMR_ABORT_TASK;
+		break;
+	case ISCSI_TM_FUNC_ABORT_TASK_SET:
+		tcm_function = TMR_ABORT_TASK_SET;
+		break;
+	case ISCSI_TM_FUNC_CLEAR_ACA:
+		tcm_function = TMR_CLEAR_ACA;
+		break;
+	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
+		tcm_function = TMR_CLEAR_TASK_SET;
+		break;
+	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+		tcm_function = TMR_LUN_RESET;
+		break;
+	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+		tcm_function = TMR_TARGET_WARM_RESET;
+		break;
+	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+		tcm_function = TMR_TARGET_COLD_RESET;
+		break;
+	default:
+		pr_err("Unknown iSCSI TMR Function:"
+			" 0x%02x\n", function);
+		goto out;
+	}
+
+	se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd,
+				(void *)cmd->tmr_req, tcm_function);
+	if (!se_cmd->se_tmr_req)
+		goto out;
+
+	cmd->tmr_req->se_tmr_req = se_cmd->se_tmr_req;
+
+	return cmd;
+out:
+	iscsit_release_cmd(cmd);
+	if (se_cmd)
+		transport_free_se_cmd(se_cmd);
+	return NULL;
+}
+
+int iscsit_decide_list_to_build(
+	struct iscsi_cmd *cmd,
+	u32 immediate_data_length)
+{
+	struct iscsi_build_list bl;
+	struct iscsi_conn *conn = cmd->conn;
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na;
+
+	if (sess->sess_ops->DataSequenceInOrder &&
+	    sess->sess_ops->DataPDUInOrder)
+		return 0;
+
+	if (cmd->data_direction == DMA_NONE)
+		return 0;
+
+	na = iscsit_tpg_get_node_attrib(sess);
+	memset(&bl, 0, sizeof(struct iscsi_build_list));
+
+	if (cmd->data_direction == DMA_FROM_DEVICE) {
+		bl.data_direction = ISCSI_PDU_READ;
+		bl.type = PDULIST_NORMAL;
+		if (na->random_datain_pdu_offsets)
+			bl.randomize |= RANDOM_DATAIN_PDU_OFFSETS;
+		if (na->random_datain_seq_offsets)
+			bl.randomize |= RANDOM_DATAIN_SEQ_OFFSETS;
+	} else {
+		bl.data_direction = ISCSI_PDU_WRITE;
+		bl.immediate_data_length = immediate_data_length;
+		if (na->random_r2t_offsets)
+			bl.randomize |= RANDOM_R2T_OFFSETS;
+
+		if (!cmd->immediate_data && !cmd->unsolicited_data)
+			bl.type = PDULIST_NORMAL;
+		else if (cmd->immediate_data && !cmd->unsolicited_data)
+			bl.type = PDULIST_IMMEDIATE;
+		else if (!cmd->immediate_data && cmd->unsolicited_data)
+			bl.type = PDULIST_UNSOLICITED;
+		else if (cmd->immediate_data && cmd->unsolicited_data)
+			bl.type = PDULIST_IMMEDIATE_AND_UNSOLICITED;
+	}
+
+	return iscsit_do_build_list(cmd, &bl);
+}
+
+struct iscsi_seq *iscsit_get_seq_holder_for_datain(
+	struct iscsi_cmd *cmd,
+	u32 seq_send_order)
+{
+	u32 i;
+
+	for (i = 0; i < cmd->seq_count; i++)
+		if (cmd->seq_list[i].seq_send_order == seq_send_order)
+			return &cmd->seq_list[i];
+
+	return NULL;
+}
+
+struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *cmd)
+{
+	u32 i;
+
+	if (!cmd->seq_list) {
+		pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+		return NULL;
+	}
+
+	for (i = 0; i < cmd->seq_count; i++) {
+		if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+			continue;
+		if (cmd->seq_list[i].seq_send_order == cmd->seq_send_order) {
+			cmd->seq_send_order++;
+			return &cmd->seq_list[i];
+		}
+	}
+
+	return NULL;
+}
+
+struct iscsi_r2t *iscsit_get_holder_for_r2tsn(
+	struct iscsi_cmd *cmd,
+	u32 r2t_sn)
+{
+	struct iscsi_r2t *r2t;
+
+	spin_lock_bh(&cmd->r2t_lock);
+	list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+		if (r2t->r2t_sn == r2t_sn) {
+			spin_unlock_bh(&cmd->r2t_lock);
+			return r2t;
+		}
+	}
+	spin_unlock_bh(&cmd->r2t_lock);
+
+	return NULL;
+}
+
+static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cmdsn)
+{
+	int ret;
+
+	/*
+	 * This is the proper method of checking received CmdSN against
+	 * ExpCmdSN and MaxCmdSN values, as well as accounting for out
+	 * or order CmdSNs due to multiple connection sessions and/or
+	 * CRC failures.
+	 */
+	if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) {
+		pr_err("Received CmdSN: 0x%08x is greater than"
+		       " MaxCmdSN: 0x%08x, protocol error.\n", cmdsn,
+		       sess->max_cmd_sn);
+		ret = CMDSN_ERROR_CANNOT_RECOVER;
+
+	} else if (cmdsn == sess->exp_cmd_sn) {
+		sess->exp_cmd_sn++;
+		pr_debug("Received CmdSN matches ExpCmdSN,"
+		      " incremented ExpCmdSN to: 0x%08x\n",
+		      sess->exp_cmd_sn);
+		ret = CMDSN_NORMAL_OPERATION;
+
+	} else if (iscsi_sna_gt(cmdsn, sess->exp_cmd_sn)) {
+		pr_debug("Received CmdSN: 0x%08x is greater"
+		      " than ExpCmdSN: 0x%08x, not acknowledging.\n",
+		      cmdsn, sess->exp_cmd_sn);
+		ret = CMDSN_HIGHER_THAN_EXP;
+
+	} else {
+		pr_err("Received CmdSN: 0x%08x is less than"
+		       " ExpCmdSN: 0x%08x, ignoring.\n", cmdsn,
+		       sess->exp_cmd_sn);
+		ret = CMDSN_LOWER_THAN_EXP;
+	}
+
+	return ret;
+}
+
+/*
+ * Commands may be received out of order if MC/S is in use.
+ * Ensure they are executed in CmdSN order.
+ */
+int iscsit_sequence_cmd(
+	struct iscsi_conn *conn,
+	struct iscsi_cmd *cmd,
+	u32 cmdsn)
+{
+	int ret;
+	int cmdsn_ret;
+
+	mutex_lock(&conn->sess->cmdsn_mutex);
+
+	cmdsn_ret = iscsit_check_received_cmdsn(conn->sess, cmdsn);
+	switch (cmdsn_ret) {
+	case CMDSN_NORMAL_OPERATION:
+		ret = iscsit_execute_cmd(cmd, 0);
+		if ((ret >= 0) && !list_empty(&conn->sess->sess_ooo_cmdsn_list))
+			iscsit_execute_ooo_cmdsns(conn->sess);
+		break;
+	case CMDSN_HIGHER_THAN_EXP:
+		ret = iscsit_handle_ooo_cmdsn(conn->sess, cmd, cmdsn);
+		break;
+	case CMDSN_LOWER_THAN_EXP:
+		cmd->i_state = ISTATE_REMOVE;
+		iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state);
+		ret = cmdsn_ret;
+		break;
+	default:
+		ret = cmdsn_ret;
+		break;
+	}
+	mutex_unlock(&conn->sess->cmdsn_mutex);
+
+	return ret;
+}
+
+int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct iscsi_data *hdr = (struct iscsi_data *) buf;
+	u32 payload_length = ntoh24(hdr->dlength);
+
+	if (conn->sess->sess_ops->InitialR2T) {
+		pr_err("Received unexpected unsolicited data"
+			" while InitialR2T=Yes, protocol error.\n");
+		transport_send_check_condition_and_sense(se_cmd,
+				TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
+		return -1;
+	}
+
+	if ((cmd->first_burst_len + payload_length) >
+	     conn->sess->sess_ops->FirstBurstLength) {
+		pr_err("Total %u bytes exceeds FirstBurstLength: %u"
+			" for this Unsolicited DataOut Burst.\n",
+			(cmd->first_burst_len + payload_length),
+				conn->sess->sess_ops->FirstBurstLength);
+		transport_send_check_condition_and_sense(se_cmd,
+				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+		return -1;
+	}
+
+	if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))
+		return 0;
+
+	if (((cmd->first_burst_len + payload_length) != cmd->data_length) &&
+	    ((cmd->first_burst_len + payload_length) !=
+	      conn->sess->sess_ops->FirstBurstLength)) {
+		pr_err("Unsolicited non-immediate data received %u"
+			" does not equal FirstBurstLength: %u, and does"
+			" not equal ExpXferLen %u.\n",
+			(cmd->first_burst_len + payload_length),
+			conn->sess->sess_ops->FirstBurstLength, cmd->data_length);
+		transport_send_check_condition_and_sense(se_cmd,
+				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+		return -1;
+	}
+	return 0;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_itt(
+	struct iscsi_conn *conn,
+	u32 init_task_tag)
+{
+	struct iscsi_cmd *cmd;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+		if (cmd->init_task_tag == init_task_tag) {
+			spin_unlock_bh(&conn->cmd_lock);
+			return cmd;
+		}
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+
+	pr_err("Unable to locate ITT: 0x%08x on CID: %hu",
+			init_task_tag, conn->cid);
+	return NULL;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(
+	struct iscsi_conn *conn,
+	u32 init_task_tag,
+	u32 length)
+{
+	struct iscsi_cmd *cmd;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+		if (cmd->init_task_tag == init_task_tag) {
+			spin_unlock_bh(&conn->cmd_lock);
+			return cmd;
+		}
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+
+	pr_err("Unable to locate ITT: 0x%08x on CID: %hu,"
+			" dumping payload\n", init_task_tag, conn->cid);
+	if (length)
+		iscsit_dump_data_payload(conn, length, 1);
+
+	return NULL;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_ttt(
+	struct iscsi_conn *conn,
+	u32 targ_xfer_tag)
+{
+	struct iscsi_cmd *cmd = NULL;
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) {
+		if (cmd->targ_xfer_tag == targ_xfer_tag) {
+			spin_unlock_bh(&conn->cmd_lock);
+			return cmd;
+		}
+	}
+	spin_unlock_bh(&conn->cmd_lock);
+
+	pr_err("Unable to locate TTT: 0x%08x on CID: %hu\n",
+			targ_xfer_tag, conn->cid);
+	return NULL;
+}
+
+int iscsit_find_cmd_for_recovery(
+	struct iscsi_session *sess,
+	struct iscsi_cmd **cmd_ptr,
+	struct iscsi_conn_recovery **cr_ptr,
+	u32 init_task_tag)
+{
+	struct iscsi_cmd *cmd = NULL;
+	struct iscsi_conn_recovery *cr;
+	/*
+	 * Scan through the inactive connection recovery list's command list.
+	 * If init_task_tag matches the command is still alligent.
+	 */
+	spin_lock(&sess->cr_i_lock);
+	list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
+		spin_lock(&cr->conn_recovery_cmd_lock);
+		list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_list) {
+			if (cmd->init_task_tag == init_task_tag) {
+				spin_unlock(&cr->conn_recovery_cmd_lock);
+				spin_unlock(&sess->cr_i_lock);
+
+				*cr_ptr = cr;
+				*cmd_ptr = cmd;
+				return -2;
+			}
+		}
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+	}
+	spin_unlock(&sess->cr_i_lock);
+	/*
+	 * Scan through the active connection recovery list's command list.
+	 * If init_task_tag matches the command is ready to be reassigned.
+	 */
+	spin_lock(&sess->cr_a_lock);
+	list_for_each_entry(cr, &sess->cr_active_list, cr_list) {
+		spin_lock(&cr->conn_recovery_cmd_lock);
+		list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_list) {
+			if (cmd->init_task_tag == init_task_tag) {
+				spin_unlock(&cr->conn_recovery_cmd_lock);
+				spin_unlock(&sess->cr_a_lock);
+
+				*cr_ptr = cr;
+				*cmd_ptr = cmd;
+				return 0;
+			}
+		}
+		spin_unlock(&cr->conn_recovery_cmd_lock);
+	}
+	spin_unlock(&sess->cr_a_lock);
+
+	return -1;
+}
+
+void iscsit_add_cmd_to_immediate_queue(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	u8 state)
+{
+	struct iscsi_queue_req *qr;
+
+	qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC);
+	if (!qr) {
+		pr_err("Unable to allocate memory for"
+				" struct iscsi_queue_req\n");
+		return;
+	}
+	INIT_LIST_HEAD(&qr->qr_list);
+	qr->cmd = cmd;
+	qr->state = state;
+
+	spin_lock_bh(&conn->immed_queue_lock);
+	list_add_tail(&qr->qr_list, &conn->immed_queue_list);
+	atomic_inc(&cmd->immed_queue_count);
+	atomic_set(&conn->check_immediate_queue, 1);
+	spin_unlock_bh(&conn->immed_queue_lock);
+
+	wake_up_process(conn->thread_set->tx_thread);
+}
+
+struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *conn)
+{
+	struct iscsi_queue_req *qr;
+
+	spin_lock_bh(&conn->immed_queue_lock);
+	if (list_empty(&conn->immed_queue_list)) {
+		spin_unlock_bh(&conn->immed_queue_lock);
+		return NULL;
+	}
+	list_for_each_entry(qr, &conn->immed_queue_list, qr_list)
+		break;
+
+	list_del(&qr->qr_list);
+	if (qr->cmd)
+		atomic_dec(&qr->cmd->immed_queue_count);
+	spin_unlock_bh(&conn->immed_queue_lock);
+
+	return qr;
+}
+
+static void iscsit_remove_cmd_from_immediate_queue(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_queue_req *qr, *qr_tmp;
+
+	spin_lock_bh(&conn->immed_queue_lock);
+	if (!atomic_read(&cmd->immed_queue_count)) {
+		spin_unlock_bh(&conn->immed_queue_lock);
+		return;
+	}
+
+	list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) {
+		if (qr->cmd != cmd)
+			continue;
+
+		atomic_dec(&qr->cmd->immed_queue_count);
+		list_del(&qr->qr_list);
+		kmem_cache_free(lio_qr_cache, qr);
+	}
+	spin_unlock_bh(&conn->immed_queue_lock);
+
+	if (atomic_read(&cmd->immed_queue_count)) {
+		pr_err("ITT: 0x%08x immed_queue_count: %d\n",
+			cmd->init_task_tag,
+			atomic_read(&cmd->immed_queue_count));
+	}
+}
+
+void iscsit_add_cmd_to_response_queue(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	u8 state)
+{
+	struct iscsi_queue_req *qr;
+
+	qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC);
+	if (!qr) {
+		pr_err("Unable to allocate memory for"
+			" struct iscsi_queue_req\n");
+		return;
+	}
+	INIT_LIST_HEAD(&qr->qr_list);
+	qr->cmd = cmd;
+	qr->state = state;
+
+	spin_lock_bh(&conn->response_queue_lock);
+	list_add_tail(&qr->qr_list, &conn->response_queue_list);
+	atomic_inc(&cmd->response_queue_count);
+	spin_unlock_bh(&conn->response_queue_lock);
+
+	wake_up_process(conn->thread_set->tx_thread);
+}
+
+struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *conn)
+{
+	struct iscsi_queue_req *qr;
+
+	spin_lock_bh(&conn->response_queue_lock);
+	if (list_empty(&conn->response_queue_list)) {
+		spin_unlock_bh(&conn->response_queue_lock);
+		return NULL;
+	}
+
+	list_for_each_entry(qr, &conn->response_queue_list, qr_list)
+		break;
+
+	list_del(&qr->qr_list);
+	if (qr->cmd)
+		atomic_dec(&qr->cmd->response_queue_count);
+	spin_unlock_bh(&conn->response_queue_lock);
+
+	return qr;
+}
+
+static void iscsit_remove_cmd_from_response_queue(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct iscsi_queue_req *qr, *qr_tmp;
+
+	spin_lock_bh(&conn->response_queue_lock);
+	if (!atomic_read(&cmd->response_queue_count)) {
+		spin_unlock_bh(&conn->response_queue_lock);
+		return;
+	}
+
+	list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list,
+				qr_list) {
+		if (qr->cmd != cmd)
+			continue;
+
+		atomic_dec(&qr->cmd->response_queue_count);
+		list_del(&qr->qr_list);
+		kmem_cache_free(lio_qr_cache, qr);
+	}
+	spin_unlock_bh(&conn->response_queue_lock);
+
+	if (atomic_read(&cmd->response_queue_count)) {
+		pr_err("ITT: 0x%08x response_queue_count: %d\n",
+			cmd->init_task_tag,
+			atomic_read(&cmd->response_queue_count));
+	}
+}
+
+void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *conn)
+{
+	struct iscsi_queue_req *qr, *qr_tmp;
+
+	spin_lock_bh(&conn->immed_queue_lock);
+	list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) {
+		list_del(&qr->qr_list);
+		if (qr->cmd)
+			atomic_dec(&qr->cmd->immed_queue_count);
+
+		kmem_cache_free(lio_qr_cache, qr);
+	}
+	spin_unlock_bh(&conn->immed_queue_lock);
+
+	spin_lock_bh(&conn->response_queue_lock);
+	list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list,
+			qr_list) {
+		list_del(&qr->qr_list);
+		if (qr->cmd)
+			atomic_dec(&qr->cmd->response_queue_count);
+
+		kmem_cache_free(lio_qr_cache, qr);
+	}
+	spin_unlock_bh(&conn->response_queue_lock);
+}
+
+void iscsit_release_cmd(struct iscsi_cmd *cmd)
+{
+	struct iscsi_conn *conn = cmd->conn;
+	int i;
+
+	iscsit_free_r2ts_from_list(cmd);
+	iscsit_free_all_datain_reqs(cmd);
+
+	kfree(cmd->buf_ptr);
+	kfree(cmd->pdu_list);
+	kfree(cmd->seq_list);
+	kfree(cmd->tmr_req);
+	kfree(cmd->iov_data);
+
+	for (i = 0; i < cmd->t_mem_sg_nents; i++)
+		__free_page(sg_page(&cmd->t_mem_sg[i]));
+
+	kfree(cmd->t_mem_sg);
+
+	if (conn) {
+		iscsit_remove_cmd_from_immediate_queue(cmd, conn);
+		iscsit_remove_cmd_from_response_queue(cmd, conn);
+	}
+
+	kmem_cache_free(lio_cmd_cache, cmd);
+}
+
+int iscsit_check_session_usage_count(struct iscsi_session *sess)
+{
+	spin_lock_bh(&sess->session_usage_lock);
+	if (sess->session_usage_count != 0) {
+		sess->session_waiting_on_uc = 1;
+		spin_unlock_bh(&sess->session_usage_lock);
+		if (in_interrupt())
+			return 2;
+
+		wait_for_completion(&sess->session_waiting_on_uc_comp);
+		return 1;
+	}
+	spin_unlock_bh(&sess->session_usage_lock);
+
+	return 0;
+}
+
+void iscsit_dec_session_usage_count(struct iscsi_session *sess)
+{
+	spin_lock_bh(&sess->session_usage_lock);
+	sess->session_usage_count--;
+
+	if (!sess->session_usage_count && sess->session_waiting_on_uc)
+		complete(&sess->session_waiting_on_uc_comp);
+
+	spin_unlock_bh(&sess->session_usage_lock);
+}
+
+void iscsit_inc_session_usage_count(struct iscsi_session *sess)
+{
+	spin_lock_bh(&sess->session_usage_lock);
+	sess->session_usage_count++;
+	spin_unlock_bh(&sess->session_usage_lock);
+}
+
+/*
+ *	Used before iscsi_do[rx,tx]_data() to determine iov and [rx,tx]_marker
+ *	array counts needed for sync and steering.
+ */
+static int iscsit_determine_sync_and_steering_counts(
+	struct iscsi_conn *conn,
+	struct iscsi_data_count *count)
+{
+	u32 length = count->data_length;
+	u32 marker, markint;
+
+	count->sync_and_steering = 1;
+
+	marker = (count->type == ISCSI_RX_DATA) ?
+			conn->of_marker : conn->if_marker;
+	markint = (count->type == ISCSI_RX_DATA) ?
+			(conn->conn_ops->OFMarkInt * 4) :
+			(conn->conn_ops->IFMarkInt * 4);
+	count->ss_iov_count = count->iov_count;
+
+	while (length > 0) {
+		if (length >= marker) {
+			count->ss_iov_count += 3;
+			count->ss_marker_count += 2;
+
+			length -= marker;
+			marker = markint;
+		} else
+			length = 0;
+	}
+
+	return 0;
+}
+
+/*
+ *	Setup conn->if_marker and conn->of_marker values based upon
+ *	the initial marker-less interval. (see iSCSI v19 A.2)
+ */
+int iscsit_set_sync_and_steering_values(struct iscsi_conn *conn)
+{
+	int login_ifmarker_count = 0, login_ofmarker_count = 0, next_marker = 0;
+	/*
+	 * IFMarkInt and OFMarkInt are negotiated as 32-bit words.
+	 */
+	u32 IFMarkInt = (conn->conn_ops->IFMarkInt * 4);
+	u32 OFMarkInt = (conn->conn_ops->OFMarkInt * 4);
+
+	if (conn->conn_ops->OFMarker) {
+		/*
+		 * Account for the first Login Command received not
+		 * via iscsi_recv_msg().
+		 */
+		conn->of_marker += ISCSI_HDR_LEN;
+		if (conn->of_marker <= OFMarkInt) {
+			conn->of_marker = (OFMarkInt - conn->of_marker);
+		} else {
+			login_ofmarker_count = (conn->of_marker / OFMarkInt);
+			next_marker = (OFMarkInt * (login_ofmarker_count + 1)) +
+					(login_ofmarker_count * MARKER_SIZE);
+			conn->of_marker = (next_marker - conn->of_marker);
+		}
+		conn->of_marker_offset = 0;
+		pr_debug("Setting OFMarker value to %u based on Initial"
+			" Markerless Interval.\n", conn->of_marker);
+	}
+
+	if (conn->conn_ops->IFMarker) {
+		if (conn->if_marker <= IFMarkInt) {
+			conn->if_marker = (IFMarkInt - conn->if_marker);
+		} else {
+			login_ifmarker_count = (conn->if_marker / IFMarkInt);
+			next_marker = (IFMarkInt * (login_ifmarker_count + 1)) +
+					(login_ifmarker_count * MARKER_SIZE);
+			conn->if_marker = (next_marker - conn->if_marker);
+		}
+		pr_debug("Setting IFMarker value to %u based on Initial"
+			" Markerless Interval.\n", conn->if_marker);
+	}
+
+	return 0;
+}
+
+struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *sess, u16 cid)
+{
+	struct iscsi_conn *conn;
+
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+		if ((conn->cid == cid) &&
+		    (conn->conn_state == TARG_CONN_STATE_LOGGED_IN)) {
+			iscsit_inc_conn_usage_count(conn);
+			spin_unlock_bh(&sess->conn_lock);
+			return conn;
+		}
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	return NULL;
+}
+
+struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *sess, u16 cid)
+{
+	struct iscsi_conn *conn;
+
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+		if (conn->cid == cid) {
+			iscsit_inc_conn_usage_count(conn);
+			spin_lock(&conn->state_lock);
+			atomic_set(&conn->connection_wait_rcfr, 1);
+			spin_unlock(&conn->state_lock);
+			spin_unlock_bh(&sess->conn_lock);
+			return conn;
+		}
+	}
+	spin_unlock_bh(&sess->conn_lock);
+
+	return NULL;
+}
+
+void iscsit_check_conn_usage_count(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->conn_usage_lock);
+	if (conn->conn_usage_count != 0) {
+		conn->conn_waiting_on_uc = 1;
+		spin_unlock_bh(&conn->conn_usage_lock);
+
+		wait_for_completion(&conn->conn_waiting_on_uc_comp);
+		return;
+	}
+	spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+void iscsit_dec_conn_usage_count(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->conn_usage_lock);
+	conn->conn_usage_count--;
+
+	if (!conn->conn_usage_count && conn->conn_waiting_on_uc)
+		complete(&conn->conn_waiting_on_uc_comp);
+
+	spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+void iscsit_inc_conn_usage_count(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->conn_usage_lock);
+	conn->conn_usage_count++;
+	spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+static int iscsit_add_nopin(struct iscsi_conn *conn, int want_response)
+{
+	u8 state;
+	struct iscsi_cmd *cmd;
+
+	cmd = iscsit_allocate_cmd(conn, GFP_ATOMIC);
+	if (!cmd)
+		return -1;
+
+	cmd->iscsi_opcode = ISCSI_OP_NOOP_IN;
+	state = (want_response) ? ISTATE_SEND_NOPIN_WANT_RESPONSE :
+				ISTATE_SEND_NOPIN_NO_RESPONSE;
+	cmd->init_task_tag = 0xFFFFFFFF;
+	spin_lock_bh(&conn->sess->ttt_lock);
+	cmd->targ_xfer_tag = (want_response) ? conn->sess->targ_xfer_tag++ :
+			0xFFFFFFFF;
+	if (want_response && (cmd->targ_xfer_tag == 0xFFFFFFFF))
+		cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+	spin_unlock_bh(&conn->sess->ttt_lock);
+
+	spin_lock_bh(&conn->cmd_lock);
+	list_add_tail(&cmd->i_list, &conn->conn_cmd_list);
+	spin_unlock_bh(&conn->cmd_lock);
+
+	if (want_response)
+		iscsit_start_nopin_response_timer(conn);
+	iscsit_add_cmd_to_immediate_queue(cmd, conn, state);
+
+	return 0;
+}
+
+static void iscsit_handle_nopin_response_timeout(unsigned long data)
+{
+	struct iscsi_conn *conn = (struct iscsi_conn *) data;
+
+	iscsit_inc_conn_usage_count(conn);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (conn->nopin_response_timer_flags & ISCSI_TF_STOP) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		iscsit_dec_conn_usage_count(conn);
+		return;
+	}
+
+	pr_debug("Did not receive response to NOPIN on CID: %hu on"
+		" SID: %u, failing connection.\n", conn->cid,
+			conn->sess->sid);
+	conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+
+	{
+	struct iscsi_portal_group *tpg = conn->sess->tpg;
+	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+	if (tiqn) {
+		spin_lock_bh(&tiqn->sess_err_stats.lock);
+		strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name,
+				(void *)conn->sess->sess_ops->InitiatorName);
+		tiqn->sess_err_stats.last_sess_failure_type =
+				ISCSI_SESS_ERR_CXN_TIMEOUT;
+		tiqn->sess_err_stats.cxn_timeout_errors++;
+		conn->sess->conn_timeout_errors++;
+		spin_unlock_bh(&tiqn->sess_err_stats.lock);
+	}
+	}
+
+	iscsit_cause_connection_reinstatement(conn, 0);
+	iscsit_dec_conn_usage_count(conn);
+}
+
+void iscsit_mod_nopin_response_timer(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		return;
+	}
+
+	mod_timer(&conn->nopin_response_timer,
+		(get_jiffies_64() + na->nopin_response_timeout * HZ));
+	spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+/*
+ *	Called with conn->nopin_timer_lock held.
+ */
+void iscsit_start_nopin_response_timer(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (conn->nopin_response_timer_flags & ISCSI_TF_RUNNING) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		return;
+	}
+
+	init_timer(&conn->nopin_response_timer);
+	conn->nopin_response_timer.expires =
+		(get_jiffies_64() + na->nopin_response_timeout * HZ);
+	conn->nopin_response_timer.data = (unsigned long)conn;
+	conn->nopin_response_timer.function = iscsit_handle_nopin_response_timeout;
+	conn->nopin_response_timer_flags &= ~ISCSI_TF_STOP;
+	conn->nopin_response_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&conn->nopin_response_timer);
+
+	pr_debug("Started NOPIN Response Timer on CID: %d to %u"
+		" seconds\n", conn->cid, na->nopin_response_timeout);
+	spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+void iscsit_stop_nopin_response_timer(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		return;
+	}
+	conn->nopin_response_timer_flags |= ISCSI_TF_STOP;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+
+	del_timer_sync(&conn->nopin_response_timer);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+static void iscsit_handle_nopin_timeout(unsigned long data)
+{
+	struct iscsi_conn *conn = (struct iscsi_conn *) data;
+
+	iscsit_inc_conn_usage_count(conn);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (conn->nopin_timer_flags & ISCSI_TF_STOP) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		iscsit_dec_conn_usage_count(conn);
+		return;
+	}
+	conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+
+	iscsit_add_nopin(conn, 1);
+	iscsit_dec_conn_usage_count(conn);
+}
+
+/*
+ * Called with conn->nopin_timer_lock held.
+ */
+void __iscsit_start_nopin_timer(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+	/*
+	* NOPIN timeout is disabled.
+	 */
+	if (!na->nopin_timeout)
+		return;
+
+	if (conn->nopin_timer_flags & ISCSI_TF_RUNNING)
+		return;
+
+	init_timer(&conn->nopin_timer);
+	conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ);
+	conn->nopin_timer.data = (unsigned long)conn;
+	conn->nopin_timer.function = iscsit_handle_nopin_timeout;
+	conn->nopin_timer_flags &= ~ISCSI_TF_STOP;
+	conn->nopin_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&conn->nopin_timer);
+
+	pr_debug("Started NOPIN Timer on CID: %d at %u second"
+		" interval\n", conn->cid, na->nopin_timeout);
+}
+
+void iscsit_start_nopin_timer(struct iscsi_conn *conn)
+{
+	struct iscsi_session *sess = conn->sess;
+	struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+	/*
+	 * NOPIN timeout is disabled..
+	 */
+	if (!na->nopin_timeout)
+		return;
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (conn->nopin_timer_flags & ISCSI_TF_RUNNING) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		return;
+	}
+
+	init_timer(&conn->nopin_timer);
+	conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ);
+	conn->nopin_timer.data = (unsigned long)conn;
+	conn->nopin_timer.function = iscsit_handle_nopin_timeout;
+	conn->nopin_timer_flags &= ~ISCSI_TF_STOP;
+	conn->nopin_timer_flags |= ISCSI_TF_RUNNING;
+	add_timer(&conn->nopin_timer);
+
+	pr_debug("Started NOPIN Timer on CID: %d at %u second"
+			" interval\n", conn->cid, na->nopin_timeout);
+	spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+void iscsit_stop_nopin_timer(struct iscsi_conn *conn)
+{
+	spin_lock_bh(&conn->nopin_timer_lock);
+	if (!(conn->nopin_timer_flags & ISCSI_TF_RUNNING)) {
+		spin_unlock_bh(&conn->nopin_timer_lock);
+		return;
+	}
+	conn->nopin_timer_flags |= ISCSI_TF_STOP;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+
+	del_timer_sync(&conn->nopin_timer);
+
+	spin_lock_bh(&conn->nopin_timer_lock);
+	conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING;
+	spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+int iscsit_send_tx_data(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn,
+	int use_misc)
+{
+	int tx_sent, tx_size;
+	u32 iov_count;
+	struct kvec *iov;
+
+send_data:
+	tx_size = cmd->tx_size;
+
+	if (!use_misc) {
+		iov = &cmd->iov_data[0];
+		iov_count = cmd->iov_data_count;
+	} else {
+		iov = &cmd->iov_misc[0];
+		iov_count = cmd->iov_misc_count;
+	}
+
+	tx_sent = tx_data(conn, &iov[0], iov_count, tx_size);
+	if (tx_size != tx_sent) {
+		if (tx_sent == -EAGAIN) {
+			pr_err("tx_data() returned -EAGAIN\n");
+			goto send_data;
+		} else
+			return -1;
+	}
+	cmd->tx_size = 0;
+
+	return 0;
+}
+
+int iscsit_fe_sendpage_sg(
+	struct iscsi_cmd *cmd,
+	struct iscsi_conn *conn)
+{
+	struct scatterlist *sg = cmd->first_data_sg;
+	struct kvec iov;
+	u32 tx_hdr_size, data_len;
+	u32 offset = cmd->first_data_sg_off;
+	int tx_sent;
+
+send_hdr:
+	tx_hdr_size = ISCSI_HDR_LEN;
+	if (conn->conn_ops->HeaderDigest)
+		tx_hdr_size += ISCSI_CRC_LEN;
+
+	iov.iov_base = cmd->pdu;
+	iov.iov_len = tx_hdr_size;
+
+	tx_sent = tx_data(conn, &iov, 1, tx_hdr_size);
+	if (tx_hdr_size != tx_sent) {
+		if (tx_sent == -EAGAIN) {
+			pr_err("tx_data() returned -EAGAIN\n");
+			goto send_hdr;
+		}
+		return -1;
+	}
+
+	data_len = cmd->tx_size - tx_hdr_size - cmd->padding;
+	if (conn->conn_ops->DataDigest)
+		data_len -= ISCSI_CRC_LEN;
+
+	/*
+	 * Perform sendpage() for each page in the scatterlist
+	 */
+	while (data_len) {
+		u32 space = (sg->length - offset);
+		u32 sub_len = min_t(u32, data_len, space);
+send_pg:
+		tx_sent = conn->sock->ops->sendpage(conn->sock,
+					sg_page(sg), sg->offset + offset, sub_len, 0);
+		if (tx_sent != sub_len) {
+			if (tx_sent == -EAGAIN) {
+				pr_err("tcp_sendpage() returned"
+						" -EAGAIN\n");
+				goto send_pg;
+			}
+
+			pr_err("tcp_sendpage() failure: %d\n",
+					tx_sent);
+			return -1;
+		}
+
+		data_len -= sub_len;
+		offset = 0;
+		sg = sg_next(sg);
+	}
+
+send_padding:
+	if (cmd->padding) {
+		struct kvec *iov_p =
+			&cmd->iov_data[cmd->iov_data_count-1];
+
+		tx_sent = tx_data(conn, iov_p, 1, cmd->padding);
+		if (cmd->padding != tx_sent) {
+			if (tx_sent == -EAGAIN) {
+				pr_err("tx_data() returned -EAGAIN\n");
+				goto send_padding;
+			}
+			return -1;
+		}
+	}
+
+send_datacrc:
+	if (conn->conn_ops->DataDigest) {
+		struct kvec *iov_d =
+			&cmd->iov_data[cmd->iov_data_count];
+
+		tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN);
+		if (ISCSI_CRC_LEN != tx_sent) {
+			if (tx_sent == -EAGAIN) {
+				pr_err("tx_data() returned -EAGAIN\n");
+				goto send_datacrc;
+			}
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ *      This function is used for mainly sending a ISCSI_TARG_LOGIN_RSP PDU
+ *      back to the Initiator when an expection condition occurs with the
+ *      errors set in status_class and status_detail.
+ *
+ *      Parameters:     iSCSI Connection, Status Class, Status Detail.
+ *      Returns:        0 on success, -1 on error.
+ */
+int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_detail)
+{
+	u8 iscsi_hdr[ISCSI_HDR_LEN];
+	int err;
+	struct kvec iov;
+	struct iscsi_login_rsp *hdr;
+
+	iscsit_collect_login_stats(conn, status_class, status_detail);
+
+	memset(&iov, 0, sizeof(struct kvec));
+	memset(&iscsi_hdr, 0x0, ISCSI_HDR_LEN);
+
+	hdr	= (struct iscsi_login_rsp *)&iscsi_hdr;
+	hdr->opcode		= ISCSI_OP_LOGIN_RSP;
+	hdr->status_class	= status_class;
+	hdr->status_detail	= status_detail;
+	hdr->itt		= cpu_to_be32(conn->login_itt);
+
+	iov.iov_base		= &iscsi_hdr;
+	iov.iov_len		= ISCSI_HDR_LEN;
+
+	PRINT_BUFF(iscsi_hdr, ISCSI_HDR_LEN);
+
+	err = tx_data(conn, &iov, 1, ISCSI_HDR_LEN);
+	if (err != ISCSI_HDR_LEN) {
+		pr_err("tx_data returned less than expected\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void iscsit_print_session_params(struct iscsi_session *sess)
+{
+	struct iscsi_conn *conn;
+
+	pr_debug("-----------------------------[Session Params for"
+		" SID: %u]-----------------------------\n", sess->sid);
+	spin_lock_bh(&sess->conn_lock);
+	list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
+		iscsi_dump_conn_ops(conn->conn_ops);
+	spin_unlock_bh(&sess->conn_lock);
+
+	iscsi_dump_sess_ops(sess->sess_ops);
+}
+
+static int iscsit_do_rx_data(
+	struct iscsi_conn *conn,
+	struct iscsi_data_count *count)
+{
+	int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len;
+	u32 rx_marker_val[count->ss_marker_count], rx_marker_iov = 0;
+	struct kvec iov[count->ss_iov_count], *iov_p;
+	struct msghdr msg;
+
+	if (!conn || !conn->sock || !conn->conn_ops)
+		return -1;
+
+	memset(&msg, 0, sizeof(struct msghdr));
+
+	if (count->sync_and_steering) {
+		int size = 0;
+		u32 i, orig_iov_count = 0;
+		u32 orig_iov_len = 0, orig_iov_loc = 0;
+		u32 iov_count = 0, per_iov_bytes = 0;
+		u32 *rx_marker, old_rx_marker = 0;
+		struct kvec *iov_record;
+
+		memset(&rx_marker_val, 0,
+				count->ss_marker_count * sizeof(u32));
+		memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec));
+
+		iov_record = count->iov;
+		orig_iov_count = count->iov_count;
+		rx_marker = &conn->of_marker;
+
+		i = 0;
+		size = data;
+		orig_iov_len = iov_record[orig_iov_loc].iov_len;
+		while (size > 0) {
+			pr_debug("rx_data: #1 orig_iov_len %u,"
+			" orig_iov_loc %u\n", orig_iov_len, orig_iov_loc);
+			pr_debug("rx_data: #2 rx_marker %u, size"
+				" %u\n", *rx_marker, size);
+
+			if (orig_iov_len >= *rx_marker) {
+				iov[iov_count].iov_len = *rx_marker;
+				iov[iov_count++].iov_base =
+					(iov_record[orig_iov_loc].iov_base +
+						per_iov_bytes);
+
+				iov[iov_count].iov_len = (MARKER_SIZE / 2);
+				iov[iov_count++].iov_base =
+					&rx_marker_val[rx_marker_iov++];
+				iov[iov_count].iov_len = (MARKER_SIZE / 2);
+				iov[iov_count++].iov_base =
+					&rx_marker_val[rx_marker_iov++];
+				old_rx_marker = *rx_marker;
+
+				/*
+				 * OFMarkInt is in 32-bit words.
+				 */
+				*rx_marker = (conn->conn_ops->OFMarkInt * 4);
+				size -= old_rx_marker;
+				orig_iov_len -= old_rx_marker;
+				per_iov_bytes += old_rx_marker;
+
+				pr_debug("rx_data: #3 new_rx_marker"
+					" %u, size %u\n", *rx_marker, size);
+			} else {
+				iov[iov_count].iov_len = orig_iov_len;
+				iov[iov_count++].iov_base =
+					(iov_record[orig_iov_loc].iov_base +
+						per_iov_bytes);
+
+				per_iov_bytes = 0;
+				*rx_marker -= orig_iov_len;
+				size -= orig_iov_len;
+
+				if (size)
+					orig_iov_len =
+					iov_record[++orig_iov_loc].iov_len;
+
+				pr_debug("rx_data: #4 new_rx_marker"
+					" %u, size %u\n", *rx_marker, size);
+			}
+		}
+		data += (rx_marker_iov * (MARKER_SIZE / 2));
+
+		iov_p	= &iov[0];
+		iov_len	= iov_count;
+
+		if (iov_count > count->ss_iov_count) {
+			pr_err("iov_count: %d, count->ss_iov_count:"
+				" %d\n", iov_count, count->ss_iov_count);
+			return -1;
+		}
+		if (rx_marker_iov > count->ss_marker_count) {
+			pr_err("rx_marker_iov: %d, count->ss_marker"
+				"_count: %d\n", rx_marker_iov,
+				count->ss_marker_count);
+			return -1;
+		}
+	} else {
+		iov_p = count->iov;
+		iov_len	= count->iov_count;
+	}
+
+	while (total_rx < data) {
+		rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len,
+					(data - total_rx), MSG_WAITALL);
+		if (rx_loop <= 0) {
+			pr_debug("rx_loop: %d total_rx: %d\n",
+				rx_loop, total_rx);
+			return rx_loop;
+		}
+		total_rx += rx_loop;
+		pr_debug("rx_loop: %d, total_rx: %d, data: %d\n",
+				rx_loop, total_rx, data);
+	}
+
+	if (count->sync_and_steering) {
+		int j;
+		for (j = 0; j < rx_marker_iov; j++) {
+			pr_debug("rx_data: #5 j: %d, offset: %d\n",
+				j, rx_marker_val[j]);
+			conn->of_marker_offset = rx_marker_val[j];
+		}
+		total_rx -= (rx_marker_iov * (MARKER_SIZE / 2));
+	}
+
+	return total_rx;
+}
+
+static int iscsit_do_tx_data(
+	struct iscsi_conn *conn,
+	struct iscsi_data_count *count)
+{
+	int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len;
+	u32 tx_marker_val[count->ss_marker_count], tx_marker_iov = 0;
+	struct kvec iov[count->ss_iov_count], *iov_p;
+	struct msghdr msg;
+
+	if (!conn || !conn->sock || !conn->conn_ops)
+		return -1;
+
+	if (data <= 0) {
+		pr_err("Data length is: %d\n", data);
+		return -1;
+	}
+
+	memset(&msg, 0, sizeof(struct msghdr));
+
+	if (count->sync_and_steering) {
+		int size = 0;
+		u32 i, orig_iov_count = 0;
+		u32 orig_iov_len = 0, orig_iov_loc = 0;
+		u32 iov_count = 0, per_iov_bytes = 0;
+		u32 *tx_marker, old_tx_marker = 0;
+		struct kvec *iov_record;
+
+		memset(&tx_marker_val, 0,
+			count->ss_marker_count * sizeof(u32));
+		memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec));
+
+		iov_record = count->iov;
+		orig_iov_count = count->iov_count;
+		tx_marker = &conn->if_marker;
+
+		i = 0;
+		size = data;
+		orig_iov_len = iov_record[orig_iov_loc].iov_len;
+		while (size > 0) {
+			pr_debug("tx_data: #1 orig_iov_len %u,"
+			" orig_iov_loc %u\n", orig_iov_len, orig_iov_loc);
+			pr_debug("tx_data: #2 tx_marker %u, size"
+				" %u\n", *tx_marker, size);
+
+			if (orig_iov_len >= *tx_marker) {
+				iov[iov_count].iov_len = *tx_marker;
+				iov[iov_count++].iov_base =
+					(iov_record[orig_iov_loc].iov_base +
+						per_iov_bytes);
+
+				tx_marker_val[tx_marker_iov] =
+						(size - *tx_marker);
+				iov[iov_count].iov_len = (MARKER_SIZE / 2);
+				iov[iov_count++].iov_base =
+					&tx_marker_val[tx_marker_iov++];
+				iov[iov_count].iov_len = (MARKER_SIZE / 2);
+				iov[iov_count++].iov_base =
+					&tx_marker_val[tx_marker_iov++];
+				old_tx_marker = *tx_marker;
+
+				/*
+				 * IFMarkInt is in 32-bit words.
+				 */
+				*tx_marker = (conn->conn_ops->IFMarkInt * 4);
+				size -= old_tx_marker;
+				orig_iov_len -= old_tx_marker;
+				per_iov_bytes += old_tx_marker;
+
+				pr_debug("tx_data: #3 new_tx_marker"
+					" %u, size %u\n", *tx_marker, size);
+				pr_debug("tx_data: #4 offset %u\n",
+					tx_marker_val[tx_marker_iov-1]);
+			} else {
+				iov[iov_count].iov_len = orig_iov_len;
+				iov[iov_count++].iov_base
+					= (iov_record[orig_iov_loc].iov_base +
+						per_iov_bytes);
+
+				per_iov_bytes = 0;
+				*tx_marker -= orig_iov_len;
+				size -= orig_iov_len;
+
+				if (size)
+					orig_iov_len =
+					iov_record[++orig_iov_loc].iov_len;
+
+				pr_debug("tx_data: #5 new_tx_marker"
+					" %u, size %u\n", *tx_marker, size);
+			}
+		}
+
+		data += (tx_marker_iov * (MARKER_SIZE / 2));
+
+		iov_p = &iov[0];
+		iov_len = iov_count;
+
+		if (iov_count > count->ss_iov_count) {
+			pr_err("iov_count: %d, count->ss_iov_count:"
+				" %d\n", iov_count, count->ss_iov_count);
+			return -1;
+		}
+		if (tx_marker_iov > count->ss_marker_count) {
+			pr_err("tx_marker_iov: %d, count->ss_marker"
+				"_count: %d\n", tx_marker_iov,
+				count->ss_marker_count);
+			return -1;
+		}
+	} else {
+		iov_p = count->iov;
+		iov_len = count->iov_count;
+	}
+
+	while (total_tx < data) {
+		tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
+					(data - total_tx));
+		if (tx_loop <= 0) {
+			pr_debug("tx_loop: %d total_tx %d\n",
+				tx_loop, total_tx);
+			return tx_loop;
+		}
+		total_tx += tx_loop;
+		pr_debug("tx_loop: %d, total_tx: %d, data: %d\n",
+					tx_loop, total_tx, data);
+	}
+
+	if (count->sync_and_steering)
+		total_tx -= (tx_marker_iov * (MARKER_SIZE / 2));
+
+	return total_tx;
+}
+
+int rx_data(
+	struct iscsi_conn *conn,
+	struct kvec *iov,
+	int iov_count,
+	int data)
+{
+	struct iscsi_data_count c;
+
+	if (!conn || !conn->sock || !conn->conn_ops)
+		return -1;
+
+	memset(&c, 0, sizeof(struct iscsi_data_count));
+	c.iov = iov;
+	c.iov_count = iov_count;
+	c.data_length = data;
+	c.type = ISCSI_RX_DATA;
+
+	if (conn->conn_ops->OFMarker &&
+	   (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) {
+		if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0)
+			return -1;
+	}
+
+	return iscsit_do_rx_data(conn, &c);
+}
+
+int tx_data(
+	struct iscsi_conn *conn,
+	struct kvec *iov,
+	int iov_count,
+	int data)
+{
+	struct iscsi_data_count c;
+
+	if (!conn || !conn->sock || !conn->conn_ops)
+		return -1;
+
+	memset(&c, 0, sizeof(struct iscsi_data_count));
+	c.iov = iov;
+	c.iov_count = iov_count;
+	c.data_length = data;
+	c.type = ISCSI_TX_DATA;
+
+	if (conn->conn_ops->IFMarker &&
+	   (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) {
+		if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0)
+			return -1;
+	}
+
+	return iscsit_do_tx_data(conn, &c);
+}
+
+void iscsit_collect_login_stats(
+	struct iscsi_conn *conn,
+	u8 status_class,
+	u8 status_detail)
+{
+	struct iscsi_param *intrname = NULL;
+	struct iscsi_tiqn *tiqn;
+	struct iscsi_login_stats *ls;
+
+	tiqn = iscsit_snmp_get_tiqn(conn);
+	if (!tiqn)
+		return;
+
+	ls = &tiqn->login_stats;
+
+	spin_lock(&ls->lock);
+	if (!strcmp(conn->login_ip, ls->last_intr_fail_ip_addr) &&
+	    ((get_jiffies_64() - ls->last_fail_time) < 10)) {
+		/* We already have the failure info for this login */
+		spin_unlock(&ls->lock);
+		return;
+	}
+
+	if (status_class == ISCSI_STATUS_CLS_SUCCESS)
+		ls->accepts++;
+	else if (status_class == ISCSI_STATUS_CLS_REDIRECT) {
+		ls->redirects++;
+		ls->last_fail_type = ISCSI_LOGIN_FAIL_REDIRECT;
+	} else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR)  &&
+		 (status_detail == ISCSI_LOGIN_STATUS_AUTH_FAILED)) {
+		ls->authenticate_fails++;
+		ls->last_fail_type =  ISCSI_LOGIN_FAIL_AUTHENTICATE;
+	} else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR)  &&
+		 (status_detail == ISCSI_LOGIN_STATUS_TGT_FORBIDDEN)) {
+		ls->authorize_fails++;
+		ls->last_fail_type = ISCSI_LOGIN_FAIL_AUTHORIZE;
+	} else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) &&
+		 (status_detail == ISCSI_LOGIN_STATUS_INIT_ERR)) {
+		ls->negotiate_fails++;
+		ls->last_fail_type = ISCSI_LOGIN_FAIL_NEGOTIATE;
+	} else {
+		ls->other_fails++;
+		ls->last_fail_type = ISCSI_LOGIN_FAIL_OTHER;
+	}
+
+	/* Save initiator name, ip address and time, if it is a failed login */
+	if (status_class != ISCSI_STATUS_CLS_SUCCESS) {
+		if (conn->param_list)
+			intrname = iscsi_find_param_from_key(INITIATORNAME,
+							     conn->param_list);
+		strcpy(ls->last_intr_fail_name,
+		       (intrname ? intrname->value : "Unknown"));
+
+		ls->last_intr_fail_ip_family = conn->sock->sk->sk_family;
+		snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,
+				"%s", conn->login_ip);
+		ls->last_fail_time = get_jiffies_64();
+	}
+
+	spin_unlock(&ls->lock);
+}
+
+struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *conn)
+{
+	struct iscsi_portal_group *tpg;
+
+	if (!conn || !conn->sess)
+		return NULL;
+
+	tpg = conn->sess->tpg;
+	if (!tpg)
+		return NULL;
+
+	if (!tpg->tpg_tiqn)
+		return NULL;
+
+	return tpg->tpg_tiqn;
+}
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h
new file mode 100644
index 0000000..2cd49d6
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_util.h
@@ -0,0 +1,60 @@
+#ifndef ISCSI_TARGET_UTIL_H
+#define ISCSI_TARGET_UTIL_H
+
+#define MARKER_SIZE	8
+
+extern int iscsit_add_r2t_to_list(struct iscsi_cmd *, u32, u32, int, u32);
+extern struct iscsi_r2t *iscsit_get_r2t_for_eos(struct iscsi_cmd *, u32, u32);
+extern struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *);
+extern void iscsit_free_r2t(struct iscsi_r2t *, struct iscsi_cmd *);
+extern void iscsit_free_r2ts_from_list(struct iscsi_cmd *);
+extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t);
+extern struct iscsi_cmd *iscsit_allocate_se_cmd(struct iscsi_conn *, u32, int, int);
+extern struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(struct iscsi_conn *, u8);
+extern int iscsit_decide_list_to_build(struct iscsi_cmd *, u32);
+extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32);
+extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *);
+extern struct iscsi_r2t *iscsit_get_holder_for_r2tsn(struct iscsi_cmd *, u32);
+int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, u32 cmdsn);
+extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *);
+extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, u32);
+extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *,
+			u32, u32);
+extern struct iscsi_cmd *iscsit_find_cmd_from_ttt(struct iscsi_conn *, u32);
+extern int iscsit_find_cmd_for_recovery(struct iscsi_session *, struct iscsi_cmd **,
+			struct iscsi_conn_recovery **, u32);
+extern void iscsit_add_cmd_to_immediate_queue(struct iscsi_cmd *, struct iscsi_conn *, u8);
+extern struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *);
+extern void iscsit_add_cmd_to_response_queue(struct iscsi_cmd *, struct iscsi_conn *, u8);
+extern struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *);
+extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_conn *);
+extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *);
+extern void iscsit_release_cmd(struct iscsi_cmd *);
+extern int iscsit_check_session_usage_count(struct iscsi_session *);
+extern void iscsit_dec_session_usage_count(struct iscsi_session *);
+extern void iscsit_inc_session_usage_count(struct iscsi_session *);
+extern int iscsit_set_sync_and_steering_values(struct iscsi_conn *);
+extern struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *, u16);
+extern struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *, u16);
+extern void iscsit_check_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_dec_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_inc_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_mod_nopin_response_timer(struct iscsi_conn *);
+extern void iscsit_start_nopin_response_timer(struct iscsi_conn *);
+extern void iscsit_stop_nopin_response_timer(struct iscsi_conn *);
+extern void __iscsit_start_nopin_timer(struct iscsi_conn *);
+extern void iscsit_start_nopin_timer(struct iscsi_conn *);
+extern void iscsit_stop_nopin_timer(struct iscsi_conn *);
+extern int iscsit_send_tx_data(struct iscsi_cmd *, struct iscsi_conn *, int);
+extern int iscsit_fe_sendpage_sg(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_tx_login_rsp(struct iscsi_conn *, u8, u8);
+extern void iscsit_print_session_params(struct iscsi_session *);
+extern int iscsit_print_dev_to_proc(char *, char **, off_t, int);
+extern int iscsit_print_sessions_to_proc(char *, char **, off_t, int);
+extern int iscsit_print_tpg_to_proc(char *, char **, off_t, int);
+extern int rx_data(struct iscsi_conn *, struct kvec *, int, int);
+extern int tx_data(struct iscsi_conn *, struct kvec *, int, int);
+extern void iscsit_collect_login_stats(struct iscsi_conn *, u8, u8);
+extern struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_UTIL_H ***/
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 46352d6..c75a01a 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -4052,17 +4052,16 @@
 	struct se_task *task;
 	struct se_device *dev = cmd->se_dev;
 	unsigned long flags;
-	sector_t sectors;
 	int task_count, i, ret;
-	sector_t dev_max_sectors = dev->se_sub_dev->se_dev_attrib.max_sectors;
+	sector_t sectors, dev_max_sectors = dev->se_sub_dev->se_dev_attrib.max_sectors;
 	u32 sector_size = dev->se_sub_dev->se_dev_attrib.block_size;
 	struct scatterlist *sg;
 	struct scatterlist *cmd_sg;
 
 	WARN_ON(cmd->data_length % sector_size);
 	sectors = DIV_ROUND_UP(cmd->data_length, sector_size);
-	task_count = DIV_ROUND_UP(sectors, dev_max_sectors);
-
+	task_count = DIV_ROUND_UP_SECTOR_T(sectors, dev_max_sectors);
+	
 	cmd_sg = sgl;
 	for (i = 0; i < task_count; i++) {
 		unsigned int task_size;
diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c
index 03c285b..3a99776 100644
--- a/drivers/tty/bfin_jtag_comm.c
+++ b/drivers/tty/bfin_jtag_comm.c
@@ -25,7 +25,7 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
 
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index 13043e8..6a1241c 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -83,7 +83,7 @@
 #include <linux/wait.h>
 #include <linux/pci.h>
 #include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/unaligned.h>
 #include <linux/bitops.h>
 #include <linux/spinlock.h>
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index 57421d7..ddc487a 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -48,7 +48,7 @@
 #include <linux/sysrq.h>
 #include <linux/tty.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/bootinfo.h>
 #include <asm/io.h>
 #include <asm/system.h>
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 22fe801..827db76 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -45,10 +45,11 @@
 #include <linux/delay.h>
 #include <linux/rational.h>
 #include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <mach/hardware.h>
 #include <mach/imx-uart.h>
 
 /* Register definitions */
@@ -66,8 +67,9 @@
 #define UBIR  0xa4 /* BRM Incremental Register */
 #define UBMR  0xa8 /* BRM Modulator Register */
 #define UBRC  0xac /* Baud Rate Count Register */
-#define MX2_ONEMS 0xb0 /* One Millisecond register */
-#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */
+#define IMX21_ONEMS 0xb0 /* One Millisecond register */
+#define IMX1_UTS 0xd0 /* UART Test Register on i.mx1 */
+#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 
 /* UART Control Register Bit Fields.*/
 #define  URXD_CHARRDY    (1<<15)
@@ -87,7 +89,7 @@
 #define  UCR1_RTSDEN     (1<<5)	 /* RTS delta interrupt enable */
 #define  UCR1_SNDBRK     (1<<4)	 /* Send break */
 #define  UCR1_TDMAEN     (1<<3)	 /* Transmitter ready DMA enable */
-#define  MX1_UCR1_UARTCLKEN  (1<<2)	 /* UART clock enabled, mx1 only */
+#define  IMX1_UCR1_UARTCLKEN  (1<<2)  /* UART clock enabled, i.mx1 only */
 #define  UCR1_DOZE       (1<<1)	 /* Doze */
 #define  UCR1_UARTEN     (1<<0)	 /* UART enabled */
 #define  UCR2_ESCI     	 (1<<15) /* Escape seq interrupt enable */
@@ -113,9 +115,7 @@
 #define  UCR3_RXDSEN	 (1<<6)  /* Receive status interrupt enable */
 #define  UCR3_AIRINTEN   (1<<5)  /* Async IR wake interrupt enable */
 #define  UCR3_AWAKEN	 (1<<4)  /* Async wake interrupt enable */
-#define  MX1_UCR3_REF25 	 (1<<3)  /* Ref freq 25 MHz, only on mx1 */
-#define  MX1_UCR3_REF30 	 (1<<2)  /* Ref Freq 30 MHz, only on mx1 */
-#define  MX2_UCR3_RXDMUXSEL	 (1<<2)  /* RXD Muxed Input Select, on mx2/mx3 */
+#define  IMX21_UCR3_RXDMUXSEL	 (1<<2)  /* RXD Muxed Input Select */
 #define  UCR3_INVT  	 (1<<1)  /* Inverted Infrared transmission */
 #define  UCR3_BPEN  	 (1<<0)  /* Preset registers enable */
 #define  UCR4_CTSTL_SHF  10      /* CTS trigger level shift */
@@ -181,6 +181,18 @@
 
 #define UART_NR 8
 
+/* i.mx21 type uart runs on all i.mx except i.mx1 */
+enum imx_uart_type {
+	IMX1_UART,
+	IMX21_UART,
+};
+
+/* device type dependent stuff */
+struct imx_uart_data {
+	unsigned uts_reg;
+	enum imx_uart_type devtype;
+};
+
 struct imx_port {
 	struct uart_port	port;
 	struct timer_list	timer;
@@ -192,6 +204,7 @@
 	unsigned int		irda_inv_tx:1;
 	unsigned short		trcv_delay; /* transceiver delay */
 	struct clk		*clk;
+	struct imx_uart_data	*devdata;
 };
 
 #ifdef CONFIG_IRDA
@@ -200,6 +213,52 @@
 #define USE_IRDA(sport)	(0)
 #endif
 
+static struct imx_uart_data imx_uart_devdata[] = {
+	[IMX1_UART] = {
+		.uts_reg = IMX1_UTS,
+		.devtype = IMX1_UART,
+	},
+	[IMX21_UART] = {
+		.uts_reg = IMX21_UTS,
+		.devtype = IMX21_UART,
+	},
+};
+
+static struct platform_device_id imx_uart_devtype[] = {
+	{
+		.name = "imx1-uart",
+		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
+	}, {
+		.name = "imx21-uart",
+		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
+
+static struct of_device_id imx_uart_dt_ids[] = {
+	{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
+	{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
+
+static inline unsigned uts_reg(struct imx_port *sport)
+{
+	return sport->devdata->uts_reg;
+}
+
+static inline int is_imx1_uart(struct imx_port *sport)
+{
+	return sport->devdata->devtype == IMX1_UART;
+}
+
+static inline int is_imx21_uart(struct imx_port *sport)
+{
+	return sport->devdata->devtype == IMX21_UART;
+}
+
 /*
  * Handle any change of modem status signal since we were last called.
  */
@@ -326,7 +385,8 @@
 	struct circ_buf *xmit = &sport->port.state->xmit;
 
 	while (!uart_circ_empty(xmit) &&
-			!(readl(sport->port.membase + UTS) & UTS_TXFULL)) {
+			!(readl(sport->port.membase + uts_reg(sport))
+				& UTS_TXFULL)) {
 		/* send xmit->buf[xmit->tail]
 		 * out the port here */
 		writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
@@ -373,7 +433,7 @@
 		writel(temp, sport->port.membase + UCR4);
 	}
 
-	if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
+	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
 		imx_transmit_buffer(sport);
 }
 
@@ -689,9 +749,9 @@
 		}
 	}
 
-	if (!cpu_is_mx1()) {
+	if (is_imx21_uart(sport)) {
 		temp = readl(sport->port.membase + UCR3);
-		temp |= MX2_UCR3_RXDMUXSEL;
+		temp |= IMX21_UCR3_RXDMUXSEL;
 		writel(temp, sport->port.membase + UCR3);
 	}
 
@@ -923,9 +983,9 @@
 	writel(num, sport->port.membase + UBIR);
 	writel(denom, sport->port.membase + UBMR);
 
-	if (!cpu_is_mx1())
+	if (is_imx21_uart(sport))
 		writel(sport->port.uartclk / div / 1000,
-				sport->port.membase + MX2_ONEMS);
+				sport->port.membase + IMX21_ONEMS);
 
 	writel(old_ucr1, sport->port.membase + UCR1);
 
@@ -1041,7 +1101,7 @@
 {
 	struct imx_port *sport = (struct imx_port *)port;
 
-	while (readl(sport->port.membase + UTS) & UTS_TXFULL)
+	while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)
 		barrier();
 
 	writel(ch, sport->port.membase + URTX0);
@@ -1062,8 +1122,8 @@
 	ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
 	old_ucr2 = readl(sport->port.membase + UCR2);
 
-	if (cpu_is_mx1())
-		ucr1 |= MX1_UCR1_UARTCLKEN;
+	if (is_imx1_uart(sport))
+		ucr1 |= IMX1_UCR1_UARTCLKEN;
 	ucr1 |= UCR1_UARTEN;
 	ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
 
@@ -1222,6 +1282,63 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static int serial_imx_probe_dt(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *of_id =
+			of_match_device(imx_uart_dt_ids, &pdev->dev);
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		pr_err("%s: failed to get alias id, errno %d\n",
+			__func__, ret);
+		return -ENODEV;
+	} else {
+		sport->port.line = ret;
+	}
+
+	if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
+		sport->have_rtscts = 1;
+
+	if (of_get_property(np, "fsl,irda-mode", NULL))
+		sport->use_irda = 1;
+
+	sport->devdata = of_id->data;
+
+	return 0;
+}
+#else
+static inline int serial_imx_probe_dt(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	return -ENODEV;
+}
+#endif
+
+static void serial_imx_probe_pdata(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	struct imxuart_platform_data *pdata = pdev->dev.platform_data;
+
+	sport->port.line = pdev->id;
+	sport->devdata = (struct imx_uart_data	*) pdev->id_entry->driver_data;
+
+	if (!pdata)
+		return;
+
+	if (pdata->flags & IMXUART_HAVE_RTSCTS)
+		sport->have_rtscts = 1;
+
+	if (pdata->flags & IMXUART_IRDA)
+		sport->use_irda = 1;
+}
+
 static int serial_imx_probe(struct platform_device *pdev)
 {
 	struct imx_port *sport;
@@ -1234,6 +1351,10 @@
 	if (!sport)
 		return -ENOMEM;
 
+	ret = serial_imx_probe_dt(sport, pdev);
+	if (ret == -ENODEV)
+		serial_imx_probe_pdata(sport, pdev);
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		ret = -ENODEV;
@@ -1258,7 +1379,6 @@
 	sport->port.fifosize = 32;
 	sport->port.ops = &imx_pops;
 	sport->port.flags = UPF_BOOT_AUTOCONF;
-	sport->port.line = pdev->id;
 	init_timer(&sport->timer);
 	sport->timer.function = imx_timeout;
 	sport->timer.data     = (unsigned long)sport;
@@ -1272,17 +1392,9 @@
 
 	sport->port.uartclk = clk_get_rate(sport->clk);
 
-	imx_ports[pdev->id] = sport;
+	imx_ports[sport->port.line] = sport;
 
 	pdata = pdev->dev.platform_data;
-	if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
-		sport->have_rtscts = 1;
-
-#ifdef CONFIG_IRDA
-	if (pdata && (pdata->flags & IMXUART_IRDA))
-		sport->use_irda = 1;
-#endif
-
 	if (pdata && pdata->init) {
 		ret = pdata->init(pdev);
 		if (ret)
@@ -1340,9 +1452,11 @@
 
 	.suspend	= serial_imx_suspend,
 	.resume		= serial_imx_resume,
+	.id_table	= imx_uart_devtype,
 	.driver		= {
 		.name	= "imx-uart",
 		.owner	= THIS_MODULE,
+		.of_match_table = imx_uart_dt_ids,
 	},
 };
 
diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
index ea2340b..6bc2e3f 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -39,7 +39,7 @@
 #include <linux/tty.h>
 #include <linux/types.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 #include <asm/war.h>
 
diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index 1a7fd3e..0aebd71 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -65,7 +65,7 @@
 #include <linux/tty.h>
 #include <linux/types.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 #include <asm/dec/interrupts.h>
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index 02a0270..a9a4ead 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "u_audio.h"
 
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 8f3eae9..3ea4666 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -29,7 +29,7 @@
 #include <linux/device.h>
 #include <linux/etherdevice.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "u_ether.h"
 #include "rndis.h"
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
index f7395ac..aa0ad34 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/uvc_queue.c
@@ -19,7 +19,7 @@
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "uvc.h"
 
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
index 5e807f0..52f8f9e 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/uvc_v4l2.c
@@ -124,24 +124,12 @@
 	struct video_device *vdev = video_devdata(file);
 	struct uvc_device *uvc = video_get_drvdata(vdev);
 	struct uvc_file_handle *handle;
-	int ret;
 
 	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
 	if (handle == NULL)
 		return -ENOMEM;
 
-	ret = v4l2_fh_init(&handle->vfh, vdev);
-	if (ret < 0)
-		goto error;
-
-	ret = v4l2_event_init(&handle->vfh);
-	if (ret < 0)
-		goto error;
-
-	ret = v4l2_event_alloc(&handle->vfh, 8);
-	if (ret < 0)
-		goto error;
-
+	v4l2_fh_init(&handle->vfh, vdev);
 	v4l2_fh_add(&handle->vfh);
 
 	handle->device = &uvc->video;
@@ -149,10 +137,6 @@
 
 	uvc_function_connect(uvc);
 	return 0;
-
-error:
-	v4l2_fh_exit(&handle->vfh);
-	return ret;
 }
 
 static int
@@ -314,7 +298,7 @@
 		if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
 			return -EINVAL;
 
-		return v4l2_event_subscribe(&handle->vfh, arg);
+		return v4l2_event_subscribe(&handle->vfh, arg, 2);
 	}
 
 	case VIDIOC_UNSUBSCRIBE_EVENT:
@@ -354,7 +338,7 @@
 	struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
 	unsigned int mask = 0;
 
-	poll_wait(file, &handle->vfh.events->wait, wait);
+	poll_wait(file, &handle->vfh.wait, wait);
 	if (v4l2_event_pending(&handle->vfh))
 		mask |= POLLPRI;
 
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index a003796..27e209a 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -131,7 +131,7 @@
 #include <linux/usb.h>
 #include <linux/proc_fs.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/blkdev.h>
 #include "../../scsi/scsi.h"
 #include <scsi/scsi_host.h>
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index 68ab460..ac0d75a 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -29,7 +29,7 @@
 #include <linux/backlight.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define APPLE_VENDOR_ID		0x05AC
 
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 406893e..a34430f 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -16,6 +16,7 @@
  */
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/scatterlist.h>
 #include "./common.h"
 #include "./pipe.h"
 
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index b0a7a9e..1a49ca9 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -34,7 +34,7 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
index ca80171..2acc7f5 100644
--- a/drivers/usb/wusbcore/wa-rpipe.c
+++ b/drivers/usb/wusbcore/wa-rpipe.c
@@ -58,7 +58,7 @@
  *  destination address.
  */
 #include <linux/init.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/bitmap.h>
 #include <linux/slab.h>
 
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index 14c9abf..a801e28 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -11,7 +11,7 @@
 #include <linux/uio.h>
 #include <linux/virtio_config.h>
 #include <linux/virtio_ring.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /* This is for zerocopy, used buffer len is set to 1 when lower device DMA
  * done */
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 019dbd3..b048417 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -24,7 +24,7 @@
 #include <linux/backlight.h>
 #include <linux/gpio.h>
 #include <video/sh_mobile_lcdc.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "sh_mobile_lcdcfb.h"
 #include "sh_mobile_meram.h"
diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h
index 7491abf..43d11ec 100644
--- a/drivers/video/vermilion/vermilion.h
+++ b/drivers/video/vermilion/vermilion.h
@@ -31,7 +31,7 @@
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/mutex.h>
 
 #define VML_DEVICE_GPU 0x5002
diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c
index 1550431..334d1cc 100644
--- a/drivers/w1/masters/matrox_w1.c
+++ b/drivers/w1/masters/matrox_w1.c
@@ -20,7 +20,7 @@
  */
 
 #include <asm/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/io.h>
 
 #include <linux/delay.h>
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 17726a0..402928b 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -86,6 +86,11 @@
 	.fops = &w1_therm_fops,
 };
 
+static struct w1_family w1_therm_family_DS28EA00 = {
+	.fid = W1_THERM_DS28EA00,
+	.fops = &w1_therm_fops,
+};
+
 struct w1_therm_family_converter
 {
 	u8			broken;
@@ -111,6 +116,10 @@
 		.f		= &w1_therm_family_DS18B20,
 		.convert 	= w1_DS18B20_convert_temp
 	},
+	{
+		.f		= &w1_therm_family_DS28EA00,
+		.convert	= w1_DS18B20_convert_temp
+	},
 };
 
 static inline int w1_DS18B20_convert_temp(u8 rom[9])
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 10606c8..6c136c1 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -33,7 +33,7 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "w1.h"
 #include "w1_log.h"
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 97479ae..98a1ac0 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -24,7 +24,7 @@
 
 #include <linux/types.h>
 #include <linux/device.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define W1_FAMILY_DEFAULT	0
 #define W1_FAMILY_SMEM_01	0x01
@@ -38,6 +38,7 @@
 #define W1_EEPROM_DS2431	0x2D
 #define W1_FAMILY_DS2760	0x30
 #define W1_FAMILY_DS2780	0x32
+#define W1_THERM_DS28EA00	0x42
 
 #define MAXNAMELEN		32
 
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 21d816e..f441726 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -28,6 +28,17 @@
 
 if WATCHDOG
 
+config WATCHDOG_CORE
+	bool "WatchDog Timer Driver Core"
+	---help---
+	  Say Y here if you want to use the new watchdog timer driver core.
+	  This driver provides a framework for all watchdog timer drivers
+	  and gives them the /dev/watchdog interface (and later also the
+	  sysfs interface).
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called watchdog.
+
 config WATCHDOG_NOWAYOUT
 	bool "Disable watchdog shutdown on close"
 	help
@@ -186,6 +197,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called sa1100_wdt.
 
+config DW_WATCHDOG
+	tristate "Synopsys DesignWare watchdog"
+	depends on ARM && HAVE_CLK
+	help
+	  Say Y here if to include support for the Synopsys DesignWare
+	  watchdog timer found in many ARM chips.
+	  To compile this driver as a module, choose M here: the
+	  module will be called dw_wdt.
+
 config MPCORE_WATCHDOG
 	tristate "MPcore watchdog"
 	depends on HAVE_ARM_TWD
@@ -321,7 +341,7 @@
 
 config IMX2_WDT
 	tristate "IMX2+ Watchdog"
-	depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5
+	depends on IMX_HAVE_PLATFORM_IMX2_WDT
 	help
 	  This is the driver for the hardware watchdog
 	  on the Freescale IMX2 and later processors.
@@ -879,6 +899,20 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called m54xx_wdt.
 
+# MicroBlaze Architecture
+
+config XILINX_WATCHDOG
+	tristate "Xilinx Watchdog timer"
+	depends on MICROBLAZE
+	---help---
+	  Watchdog driver for the xps_timebase_wdt ip core.
+
+	  IMPORTANT: The xps_timebase_wdt parent must have the property
+	  "clock-frequency" at device tree.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called of_xilinx_wdt.
+
 # MIPS Architecture
 
 config ATH79_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index ed26f70..55bd574 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -2,6 +2,10 @@
 # Makefile for the WatchDog device drivers.
 #
 
+# The WatchDog Timer Driver Core.
+watchdog-objs	+= watchdog_core.o watchdog_dev.o
+obj-$(CONFIG_WATCHDOG_CORE)	+= watchdog.o
+
 # Only one watchdog can succeed. We probe the ISA/PCI/USB based
 # watchdog-cards first, then the architecture specific watchdog
 # drivers and then the architecture independent "softdog" driver.
@@ -37,6 +41,7 @@
 obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
+obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
@@ -109,6 +114,9 @@
 # M68K Architecture
 obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
 
+# MicroBlaze Architecture
+obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o
+
 # MIPS Architecture
 obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
 obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index eac2602..87445b2 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -31,7 +31,7 @@
 #include <linux/bitops.h>
 #include <linux/uaccess.h>
 
-#include <mach/at91_wdt.h>
+#include "at91sam9_wdt.h"
 
 #define DRV_NAME "AT91SAM9 Watchdog"
 
@@ -284,27 +284,8 @@
 	return res;
 }
 
-#ifdef CONFIG_PM
-
-static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
-{
-	return 0;
-}
-
-static int at91wdt_resume(struct platform_device *pdev)
-{
-	return 0;
-}
-
-#else
-#define at91wdt_suspend	NULL
-#define at91wdt_resume	NULL
-#endif
-
 static struct platform_driver at91wdt_driver = {
 	.remove		= __exit_p(at91wdt_remove),
-	.suspend	= at91wdt_suspend,
-	.resume		= at91wdt_resume,
 	.driver		= {
 		.name	= "at91_wdt",
 		.owner	= THIS_MODULE,
diff --git a/arch/arm/mach-at91/include/mach/at91_wdt.h b/drivers/watchdog/at91sam9_wdt.h
similarity index 96%
rename from arch/arm/mach-at91/include/mach/at91_wdt.h
rename to drivers/watchdog/at91sam9_wdt.h
index fecc2e9..757f9ca 100644
--- a/arch/arm/mach-at91/include/mach/at91_wdt.h
+++ b/drivers/watchdog/at91sam9_wdt.h
@@ -1,5 +1,5 @@
 /*
- * arch/arm/mach-at91/include/mach/at91_wdt.h
+ * drivers/watchdog/at91sam9_wdt.h
  *
  * Copyright (C) 2007 Andrew Victor
  * Copyright (C) 2007 Atmel Corporation.
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
new file mode 100644
index 0000000..f10f8c0
--- /dev/null
+++ b/drivers/watchdog/dw_wdt.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2010-2011 Picochip Ltd., Jamie Iles
+ * http://www.picochip.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 file implements a driver for the Synopsys DesignWare watchdog device
+ * in the many ARM subsystems. The watchdog has 16 different timeout periods
+ * and these are a function of the input clock frequency.
+ *
+ * The DesignWare watchdog cannot be stopped once it has been started so we
+ * use a software timer to implement a ping that will keep the watchdog alive.
+ * If we receive an expected close for the watchdog then we keep the timer
+ * running, otherwise the timer is stopped and the watchdog will expire.
+ */
+#define pr_fmt(fmt) "dw_wdt: " fmt
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define WDOG_CONTROL_REG_OFFSET		    0x00
+#define WDOG_CONTROL_REG_WDT_EN_MASK	    0x01
+#define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
+#define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
+#define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
+#define WDOG_COUNTER_RESTART_KICK_VALUE	    0x76
+
+/* The maximum TOP (timeout period) value that can be set in the watchdog. */
+#define DW_WDT_MAX_TOP		15
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define WDT_TIMEOUT		(HZ / 2)
+
+static struct {
+	spinlock_t		lock;
+	void __iomem		*regs;
+	struct clk		*clk;
+	unsigned long		in_use;
+	unsigned long		next_heartbeat;
+	struct timer_list	timer;
+	int			expect_close;
+} dw_wdt;
+
+static inline int dw_wdt_is_enabled(void)
+{
+	return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
+		WDOG_CONTROL_REG_WDT_EN_MASK;
+}
+
+static inline int dw_wdt_top_in_seconds(unsigned top)
+{
+	/*
+	 * There are 16 possible timeout values in 0..15 where the number of
+	 * cycles is 2 ^ (16 + i) and the watchdog counts down.
+	 */
+	return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk);
+}
+
+static int dw_wdt_get_top(void)
+{
+	int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
+
+	return dw_wdt_top_in_seconds(top);
+}
+
+static inline void dw_wdt_set_next_heartbeat(void)
+{
+	dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
+}
+
+static int dw_wdt_set_top(unsigned top_s)
+{
+	int i, top_val = DW_WDT_MAX_TOP;
+
+	/*
+	 * Iterate over the timeout values until we find the closest match. We
+	 * always look for >=.
+	 */
+	for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
+		if (dw_wdt_top_in_seconds(i) >= top_s) {
+			top_val = i;
+			break;
+		}
+
+	/* Set the new value in the watchdog. */
+	writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+
+	dw_wdt_set_next_heartbeat();
+
+	return dw_wdt_top_in_seconds(top_val);
+}
+
+static void dw_wdt_keepalive(void)
+{
+	writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
+	       WDOG_COUNTER_RESTART_REG_OFFSET);
+}
+
+static void dw_wdt_ping(unsigned long data)
+{
+	if (time_before(jiffies, dw_wdt.next_heartbeat) ||
+	    (!nowayout && !dw_wdt.in_use)) {
+		dw_wdt_keepalive();
+		mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
+	} else
+		pr_crit("keepalive missed, machine will reset\n");
+}
+
+static int dw_wdt_open(struct inode *inode, struct file *filp)
+{
+	if (test_and_set_bit(0, &dw_wdt.in_use))
+		return -EBUSY;
+
+	/* Make sure we don't get unloaded. */
+	__module_get(THIS_MODULE);
+
+	spin_lock(&dw_wdt.lock);
+	if (!dw_wdt_is_enabled()) {
+		/*
+		 * The watchdog is not currently enabled. Set the timeout to
+		 * the maximum and then start it.
+		 */
+		dw_wdt_set_top(DW_WDT_MAX_TOP);
+		writel(WDOG_CONTROL_REG_WDT_EN_MASK,
+		       dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
+	}
+
+	dw_wdt_set_next_heartbeat();
+
+	spin_unlock(&dw_wdt.lock);
+
+	return nonseekable_open(inode, filp);
+}
+
+ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len,
+		     loff_t *offset)
+{
+	if (!len)
+		return 0;
+
+	if (!nowayout) {
+		size_t i;
+
+		dw_wdt.expect_close = 0;
+
+		for (i = 0; i < len; ++i) {
+			char c;
+
+			if (get_user(c, buf + i))
+				return -EFAULT;
+
+			if (c == 'V') {
+				dw_wdt.expect_close = 1;
+				break;
+			}
+		}
+	}
+
+	dw_wdt_set_next_heartbeat();
+	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
+
+	return len;
+}
+
+static u32 dw_wdt_time_left(void)
+{
+	return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
+		clk_get_rate(dw_wdt.clk);
+}
+
+static const struct watchdog_info dw_wdt_ident = {
+	.options	= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+			  WDIOF_MAGICCLOSE,
+	.identity	= "Synopsys DesignWare Watchdog",
+};
+
+static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	unsigned long val;
+	int timeout;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident,
+				    sizeof(dw_wdt_ident)) ? -EFAULT : 0;
+
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		return put_user(0, (int *)arg);
+
+	case WDIOC_KEEPALIVE:
+		dw_wdt_set_next_heartbeat();
+		return 0;
+
+	case WDIOC_SETTIMEOUT:
+		if (get_user(val, (int __user *)arg))
+			return -EFAULT;
+		timeout = dw_wdt_set_top(val);
+		return put_user(timeout , (int __user *)arg);
+
+	case WDIOC_GETTIMEOUT:
+		return put_user(dw_wdt_get_top(), (int __user *)arg);
+
+	case WDIOC_GETTIMELEFT:
+		/* Get the time left until expiry. */
+		if (get_user(val, (int __user *)arg))
+			return -EFAULT;
+		return put_user(dw_wdt_time_left(), (int __user *)arg);
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+static int dw_wdt_release(struct inode *inode, struct file *filp)
+{
+	clear_bit(0, &dw_wdt.in_use);
+
+	if (!dw_wdt.expect_close) {
+		del_timer(&dw_wdt.timer);
+
+		if (!nowayout)
+			pr_crit("unexpected close, system will reboot soon\n");
+		else
+			pr_crit("watchdog cannot be disabled, system will reboot soon\n");
+	}
+
+	dw_wdt.expect_close = 0;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int dw_wdt_suspend(struct device *dev)
+{
+	clk_disable(dw_wdt.clk);
+
+	return 0;
+}
+
+static int dw_wdt_resume(struct device *dev)
+{
+	int err = clk_enable(dw_wdt.clk);
+
+	if (err)
+		return err;
+
+	dw_wdt_keepalive();
+
+	return 0;
+}
+
+static const struct dev_pm_ops dw_wdt_pm_ops = {
+	.suspend	= dw_wdt_suspend,
+	.resume		= dw_wdt_resume,
+};
+#endif /* CONFIG_PM */
+
+static const struct file_operations wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.open		= dw_wdt_open,
+	.write		= dw_wdt_write,
+	.unlocked_ioctl	= dw_wdt_ioctl,
+	.release	= dw_wdt_release
+};
+
+static struct miscdevice dw_wdt_miscdev = {
+	.fops		= &wdt_fops,
+	.name		= "watchdog",
+	.minor		= WATCHDOG_MINOR,
+};
+
+static int __devinit dw_wdt_drv_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!mem)
+		return -EINVAL;
+
+	if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
+				     "dw_wdt"))
+		return -ENOMEM;
+
+	dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+	if (!dw_wdt.regs)
+		return -ENOMEM;
+
+	dw_wdt.clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dw_wdt.clk))
+		return PTR_ERR(dw_wdt.clk);
+
+	ret = clk_enable(dw_wdt.clk);
+	if (ret)
+		goto out_put_clk;
+
+	spin_lock_init(&dw_wdt.lock);
+
+	ret = misc_register(&dw_wdt_miscdev);
+	if (ret)
+		goto out_disable_clk;
+
+	dw_wdt_set_next_heartbeat();
+	setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
+	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
+
+	return 0;
+
+out_disable_clk:
+	clk_disable(dw_wdt.clk);
+out_put_clk:
+	clk_put(dw_wdt.clk);
+
+	return ret;
+}
+
+static int __devexit dw_wdt_drv_remove(struct platform_device *pdev)
+{
+	misc_deregister(&dw_wdt_miscdev);
+
+	clk_disable(dw_wdt.clk);
+	clk_put(dw_wdt.clk);
+
+	return 0;
+}
+
+static struct platform_driver dw_wdt_driver = {
+	.probe		= dw_wdt_drv_probe,
+	.remove		= __devexit_p(dw_wdt_drv_remove),
+	.driver		= {
+		.name	= "dw_wdt",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &dw_wdt_pm_ops,
+#endif /* CONFIG_PM */
+	},
+};
+
+static int __init dw_wdt_watchdog_init(void)
+{
+	return platform_driver_register(&dw_wdt_driver);
+}
+module_init(dw_wdt_watchdog_init);
+
+static void __exit dw_wdt_watchdog_exit(void)
+{
+	platform_driver_unregister(&dw_wdt_driver);
+}
+module_exit(dw_wdt_watchdog_exit);
+
+MODULE_AUTHOR("Jamie Iles");
+MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 8cb2685..410fba4 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -36,7 +36,7 @@
 #include <asm/cacheflush.h>
 #endif /* CONFIG_HPWDT_NMI_DECODING */
 
-#define HPWDT_VERSION			"1.2.0"
+#define HPWDT_VERSION			"1.3.0"
 #define SECS_TO_TICKS(secs)		((secs) * 1000 / 128)
 #define TICKS_TO_SECS(ticks)		((ticks) * 128 / 1000)
 #define HPWDT_MAX_TIMER			TICKS_TO_SECS(65535)
@@ -87,6 +87,19 @@
 };
 #define SMBIOS_CRU64_INFORMATION	212
 
+/* type 219 */
+struct smbios_proliant_info {
+	u8 type;
+	u8 byte_length;
+	u16 handle;
+	u32 power_features;
+	u32 omega_features;
+	u32 reserved;
+	u32 misc_features;
+};
+#define SMBIOS_ICRU_INFORMATION		219
+
+
 struct cmn_registers {
 	union {
 		struct {
@@ -132,6 +145,7 @@
 static unsigned int hpwdt_nmi_decoding;
 static unsigned int allow_kdump;
 static unsigned int priority;		/* hpwdt at end of die_notify list */
+static unsigned int is_icru;
 static DEFINE_SPINLOCK(rom_lock);
 static void *cru_rom_addr;
 static struct cmn_registers cmn_regs;
@@ -476,19 +490,22 @@
 		goto out;
 
 	spin_lock_irqsave(&rom_lock, rom_pl);
-	if (!die_nmi_called)
+	if (!die_nmi_called && !is_icru)
 		asminline_call(&cmn_regs, cru_rom_addr);
 	die_nmi_called = 1;
 	spin_unlock_irqrestore(&rom_lock, rom_pl);
-	if (cmn_regs.u1.ral == 0) {
-		printk(KERN_WARNING "hpwdt: An NMI occurred, "
-			"but unable to determine source.\n");
-	} else {
-		if (allow_kdump)
-			hpwdt_stop();
-		panic("An NMI occurred, please see the Integrated "
-			"Management Log for details.\n");
+	if (!is_icru) {
+		if (cmn_regs.u1.ral == 0) {
+			printk(KERN_WARNING "hpwdt: An NMI occurred, "
+				"but unable to determine source.\n");
+		}
 	}
+
+	if (allow_kdump)
+		hpwdt_stop();
+	panic("An NMI occurred, please see the Integrated "
+		"Management Log for details.\n");
+
 out:
 	return NOTIFY_OK;
 }
@@ -659,30 +676,63 @@
 }
 #endif /* CONFIG_X86_LOCAL_APIC */
 
+/*
+ *	dmi_find_icru
+ *
+ *	Routine Description:
+ *	This function checks whether or not we are on an iCRU-based server.
+ *	This check is independent of architecture and needs to be made for
+ *	any ProLiant system.
+ */
+static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy)
+{
+	struct smbios_proliant_info *smbios_proliant_ptr;
+
+	if (dm->type == SMBIOS_ICRU_INFORMATION) {
+		smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
+		if (smbios_proliant_ptr->misc_features & 0x01)
+			is_icru = 1;
+	}
+}
+
 static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
 {
 	int retval;
 
 	/*
-	 * We need to map the ROM to get the CRU service.
-	 * For 32 bit Operating Systems we need to go through the 32 Bit
-	 * BIOS Service Directory
-	 * For 64 bit Operating Systems we get that service through SMBIOS.
+	 * On typical CRU-based systems we need to map that service in
+	 * the BIOS. For 32 bit Operating Systems we need to go through
+	 * the 32 Bit BIOS Service Directory. For 64 bit Operating
+	 * Systems we get that service through SMBIOS.
+	 *
+	 * On systems that support the new iCRU service all we need to
+	 * do is call dmi_walk to get the supported flag value and skip
+	 * the old cru detect code.
 	 */
-	retval = detect_cru_service();
-	if (retval < 0) {
-		dev_warn(&dev->dev,
-			"Unable to detect the %d Bit CRU Service.\n",
-			HPWDT_ARCH);
-		return retval;
-	}
+	dmi_walk(dmi_find_icru, NULL);
+	if (!is_icru) {
 
-	/*
-	 * We know this is the only CRU call we need to make so lets keep as
-	 * few instructions as possible once the NMI comes in.
-	 */
-	cmn_regs.u1.rah = 0x0D;
-	cmn_regs.u1.ral = 0x02;
+		/*
+		* We need to map the ROM to get the CRU service.
+		* For 32 bit Operating Systems we need to go through the 32 Bit
+		* BIOS Service Directory
+		* For 64 bit Operating Systems we get that service through SMBIOS.
+		*/
+		retval = detect_cru_service();
+		if (retval < 0) {
+			dev_warn(&dev->dev,
+				"Unable to detect the %d Bit CRU Service.\n",
+				HPWDT_ARCH);
+			return retval;
+		}
+
+		/*
+		* We know this is the only CRU call we need to make so lets keep as
+		* few instructions as possible once the NMI comes in.
+		*/
+		cmn_regs.u1.rah = 0x0D;
+		cmn_regs.u1.ral = 0x02;
+	}
 
 	/*
 	 * If the priority is set to 1, then we will be put first on the
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 5fd020d..751a591 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -120,72 +120,12 @@
 	TCO_3420,	/* 3420 */
 	TCO_3450,	/* 3450 */
 	TCO_EP80579,	/* EP80579 */
-	TCO_CPT1,	/* Cougar Point */
-	TCO_CPT2,	/* Cougar Point Desktop */
-	TCO_CPT3,	/* Cougar Point Mobile */
-	TCO_CPT4,	/* Cougar Point */
-	TCO_CPT5,	/* Cougar Point */
-	TCO_CPT6,	/* Cougar Point */
-	TCO_CPT7,	/* Cougar Point */
-	TCO_CPT8,	/* Cougar Point */
-	TCO_CPT9,	/* Cougar Point */
-	TCO_CPT10,	/* Cougar Point */
-	TCO_CPT11,	/* Cougar Point */
-	TCO_CPT12,	/* Cougar Point */
-	TCO_CPT13,	/* Cougar Point */
-	TCO_CPT14,	/* Cougar Point */
-	TCO_CPT15,	/* Cougar Point */
-	TCO_CPT16,	/* Cougar Point */
-	TCO_CPT17,	/* Cougar Point */
-	TCO_CPT18,	/* Cougar Point */
-	TCO_CPT19,	/* Cougar Point */
-	TCO_CPT20,	/* Cougar Point */
-	TCO_CPT21,	/* Cougar Point */
-	TCO_CPT22,	/* Cougar Point */
-	TCO_CPT23,	/* Cougar Point */
-	TCO_CPT24,	/* Cougar Point */
-	TCO_CPT25,	/* Cougar Point */
-	TCO_CPT26,	/* Cougar Point */
-	TCO_CPT27,	/* Cougar Point */
-	TCO_CPT28,	/* Cougar Point */
-	TCO_CPT29,	/* Cougar Point */
-	TCO_CPT30,	/* Cougar Point */
-	TCO_CPT31,	/* Cougar Point */
-	TCO_PBG1,	/* Patsburg */
-	TCO_PBG2,	/* Patsburg */
+	TCO_CPT,	/* Cougar Point */
+	TCO_CPTD,	/* Cougar Point Desktop */
+	TCO_CPTM,	/* Cougar Point Mobile */
+	TCO_PBG,	/* Patsburg */
 	TCO_DH89XXCC,	/* DH89xxCC */
-	TCO_PPT0,	/* Panther Point */
-	TCO_PPT1,	/* Panther Point */
-	TCO_PPT2,	/* Panther Point */
-	TCO_PPT3,	/* Panther Point */
-	TCO_PPT4,	/* Panther Point */
-	TCO_PPT5,	/* Panther Point */
-	TCO_PPT6,	/* Panther Point */
-	TCO_PPT7,	/* Panther Point */
-	TCO_PPT8,	/* Panther Point */
-	TCO_PPT9,	/* Panther Point */
-	TCO_PPT10,	/* Panther Point */
-	TCO_PPT11,	/* Panther Point */
-	TCO_PPT12,	/* Panther Point */
-	TCO_PPT13,	/* Panther Point */
-	TCO_PPT14,	/* Panther Point */
-	TCO_PPT15,	/* Panther Point */
-	TCO_PPT16,	/* Panther Point */
-	TCO_PPT17,	/* Panther Point */
-	TCO_PPT18,	/* Panther Point */
-	TCO_PPT19,	/* Panther Point */
-	TCO_PPT20,	/* Panther Point */
-	TCO_PPT21,	/* Panther Point */
-	TCO_PPT22,	/* Panther Point */
-	TCO_PPT23,	/* Panther Point */
-	TCO_PPT24,	/* Panther Point */
-	TCO_PPT25,	/* Panther Point */
-	TCO_PPT26,	/* Panther Point */
-	TCO_PPT27,	/* Panther Point */
-	TCO_PPT28,	/* Panther Point */
-	TCO_PPT29,	/* Panther Point */
-	TCO_PPT30,	/* Panther Point */
-	TCO_PPT31,	/* Panther Point */
+	TCO_PPT,	/* Panther Point */
 };
 
 static struct {
@@ -244,83 +184,14 @@
 	{"3450", 2},
 	{"EP80579", 2},
 	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Cougar Point", 2},
-	{"Patsburg", 2},
+	{"Cougar Point Desktop", 2},
+	{"Cougar Point Mobile", 2},
 	{"Patsburg", 2},
 	{"DH89xxCC", 2},
 	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
-	{"Panther Point", 2},
 	{NULL, 0}
 };
 
-#define ITCO_PCI_DEVICE(dev, data) \
-	.vendor = PCI_VENDOR_ID_INTEL,	\
-	.device = dev,			\
-	.subvendor = PCI_ANY_ID,	\
-	.subdevice = PCI_ANY_ID,	\
-	.class = 0,			\
-	.class_mask = 0,		\
-	.driver_data = data
-
 /*
  * This data only exists for exporting the supported PCI ids
  * via MODULE_DEVICE_TABLE.  We do not actually register a
@@ -328,138 +199,138 @@
  * functions that probably will be registered by other drivers.
  */
 static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0,	TCO_ICH)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0,	TCO_ICH0)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0,	TCO_ICH2)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10,	TCO_ICH2M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0,	TCO_ICH3)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12,	TCO_ICH3M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0,	TCO_ICH4)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12,	TCO_ICH4M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0,		TCO_CICH)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0,	TCO_ICH5)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1,		TCO_6300ESB)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0,		TCO_ICH6)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1,		TCO_ICH6M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2,		TCO_ICH6W)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0,		TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2671,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2672,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2673,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2674,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2675,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2676,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2677,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2678,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x2679,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267a,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267b,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267c,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267d,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267e,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(0x267f,				TCO_631XESB)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0,		TCO_ICH7)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30,		TCO_ICH7DH)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1,		TCO_ICH7M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31,		TCO_ICH7MDH)},
-	{ ITCO_PCI_DEVICE(0x27bc,				TCO_NM10)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0,		TCO_ICH8)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2,		TCO_ICH8DH)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3,		TCO_ICH8DO)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4,		TCO_ICH8M)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1,		TCO_ICH8ME)},
-	{ ITCO_PCI_DEVICE(0x2918,				TCO_ICH9)},
-	{ ITCO_PCI_DEVICE(0x2916,				TCO_ICH9R)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2,		TCO_ICH9DH)},
-	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4,		TCO_ICH9DO)},
-	{ ITCO_PCI_DEVICE(0x2919,				TCO_ICH9M)},
-	{ ITCO_PCI_DEVICE(0x2917,				TCO_ICH9ME)},
-	{ ITCO_PCI_DEVICE(0x3a18,				TCO_ICH10)},
-	{ ITCO_PCI_DEVICE(0x3a16,				TCO_ICH10R)},
-	{ ITCO_PCI_DEVICE(0x3a1a,				TCO_ICH10D)},
-	{ ITCO_PCI_DEVICE(0x3a14,				TCO_ICH10DO)},
-	{ ITCO_PCI_DEVICE(0x3b00,				TCO_PCH)},
-	{ ITCO_PCI_DEVICE(0x3b01,				TCO_PCHM)},
-	{ ITCO_PCI_DEVICE(0x3b02,				TCO_P55)},
-	{ ITCO_PCI_DEVICE(0x3b03,				TCO_PM55)},
-	{ ITCO_PCI_DEVICE(0x3b06,				TCO_H55)},
-	{ ITCO_PCI_DEVICE(0x3b07,				TCO_QM57)},
-	{ ITCO_PCI_DEVICE(0x3b08,				TCO_H57)},
-	{ ITCO_PCI_DEVICE(0x3b09,				TCO_HM55)},
-	{ ITCO_PCI_DEVICE(0x3b0a,				TCO_Q57)},
-	{ ITCO_PCI_DEVICE(0x3b0b,				TCO_HM57)},
-	{ ITCO_PCI_DEVICE(0x3b0d,				TCO_PCHMSFF)},
-	{ ITCO_PCI_DEVICE(0x3b0f,				TCO_QS57)},
-	{ ITCO_PCI_DEVICE(0x3b12,				TCO_3400)},
-	{ ITCO_PCI_DEVICE(0x3b14,				TCO_3420)},
-	{ ITCO_PCI_DEVICE(0x3b16,				TCO_3450)},
-	{ ITCO_PCI_DEVICE(0x5031,				TCO_EP80579)},
-	{ ITCO_PCI_DEVICE(0x1c41,				TCO_CPT1)},
-	{ ITCO_PCI_DEVICE(0x1c42,				TCO_CPT2)},
-	{ ITCO_PCI_DEVICE(0x1c43,				TCO_CPT3)},
-	{ ITCO_PCI_DEVICE(0x1c44,				TCO_CPT4)},
-	{ ITCO_PCI_DEVICE(0x1c45,				TCO_CPT5)},
-	{ ITCO_PCI_DEVICE(0x1c46,				TCO_CPT6)},
-	{ ITCO_PCI_DEVICE(0x1c47,				TCO_CPT7)},
-	{ ITCO_PCI_DEVICE(0x1c48,				TCO_CPT8)},
-	{ ITCO_PCI_DEVICE(0x1c49,				TCO_CPT9)},
-	{ ITCO_PCI_DEVICE(0x1c4a,				TCO_CPT10)},
-	{ ITCO_PCI_DEVICE(0x1c4b,				TCO_CPT11)},
-	{ ITCO_PCI_DEVICE(0x1c4c,				TCO_CPT12)},
-	{ ITCO_PCI_DEVICE(0x1c4d,				TCO_CPT13)},
-	{ ITCO_PCI_DEVICE(0x1c4e,				TCO_CPT14)},
-	{ ITCO_PCI_DEVICE(0x1c4f,				TCO_CPT15)},
-	{ ITCO_PCI_DEVICE(0x1c50,				TCO_CPT16)},
-	{ ITCO_PCI_DEVICE(0x1c51,				TCO_CPT17)},
-	{ ITCO_PCI_DEVICE(0x1c52,				TCO_CPT18)},
-	{ ITCO_PCI_DEVICE(0x1c53,				TCO_CPT19)},
-	{ ITCO_PCI_DEVICE(0x1c54,				TCO_CPT20)},
-	{ ITCO_PCI_DEVICE(0x1c55,				TCO_CPT21)},
-	{ ITCO_PCI_DEVICE(0x1c56,				TCO_CPT22)},
-	{ ITCO_PCI_DEVICE(0x1c57,				TCO_CPT23)},
-	{ ITCO_PCI_DEVICE(0x1c58,				TCO_CPT24)},
-	{ ITCO_PCI_DEVICE(0x1c59,				TCO_CPT25)},
-	{ ITCO_PCI_DEVICE(0x1c5a,				TCO_CPT26)},
-	{ ITCO_PCI_DEVICE(0x1c5b,				TCO_CPT27)},
-	{ ITCO_PCI_DEVICE(0x1c5c,				TCO_CPT28)},
-	{ ITCO_PCI_DEVICE(0x1c5d,				TCO_CPT29)},
-	{ ITCO_PCI_DEVICE(0x1c5e,				TCO_CPT30)},
-	{ ITCO_PCI_DEVICE(0x1c5f,				TCO_CPT31)},
-	{ ITCO_PCI_DEVICE(0x1d40,				TCO_PBG1)},
-	{ ITCO_PCI_DEVICE(0x1d41,				TCO_PBG2)},
-	{ ITCO_PCI_DEVICE(0x2310,				TCO_DH89XXCC)},
-	{ ITCO_PCI_DEVICE(0x1e40,				TCO_PPT0)},
-	{ ITCO_PCI_DEVICE(0x1e41,				TCO_PPT1)},
-	{ ITCO_PCI_DEVICE(0x1e42,				TCO_PPT2)},
-	{ ITCO_PCI_DEVICE(0x1e43,				TCO_PPT3)},
-	{ ITCO_PCI_DEVICE(0x1e44,				TCO_PPT4)},
-	{ ITCO_PCI_DEVICE(0x1e45,				TCO_PPT5)},
-	{ ITCO_PCI_DEVICE(0x1e46,				TCO_PPT6)},
-	{ ITCO_PCI_DEVICE(0x1e47,				TCO_PPT7)},
-	{ ITCO_PCI_DEVICE(0x1e48,				TCO_PPT8)},
-	{ ITCO_PCI_DEVICE(0x1e49,				TCO_PPT9)},
-	{ ITCO_PCI_DEVICE(0x1e4a,				TCO_PPT10)},
-	{ ITCO_PCI_DEVICE(0x1e4b,				TCO_PPT11)},
-	{ ITCO_PCI_DEVICE(0x1e4c,				TCO_PPT12)},
-	{ ITCO_PCI_DEVICE(0x1e4d,				TCO_PPT13)},
-	{ ITCO_PCI_DEVICE(0x1e4e,				TCO_PPT14)},
-	{ ITCO_PCI_DEVICE(0x1e4f,				TCO_PPT15)},
-	{ ITCO_PCI_DEVICE(0x1e50,				TCO_PPT16)},
-	{ ITCO_PCI_DEVICE(0x1e51,				TCO_PPT17)},
-	{ ITCO_PCI_DEVICE(0x1e52,				TCO_PPT18)},
-	{ ITCO_PCI_DEVICE(0x1e53,				TCO_PPT19)},
-	{ ITCO_PCI_DEVICE(0x1e54,				TCO_PPT20)},
-	{ ITCO_PCI_DEVICE(0x1e55,				TCO_PPT21)},
-	{ ITCO_PCI_DEVICE(0x1e56,				TCO_PPT22)},
-	{ ITCO_PCI_DEVICE(0x1e57,				TCO_PPT23)},
-	{ ITCO_PCI_DEVICE(0x1e58,				TCO_PPT24)},
-	{ ITCO_PCI_DEVICE(0x1e59,				TCO_PPT25)},
-	{ ITCO_PCI_DEVICE(0x1e5a,				TCO_PPT26)},
-	{ ITCO_PCI_DEVICE(0x1e5b,				TCO_PPT27)},
-	{ ITCO_PCI_DEVICE(0x1e5c,				TCO_PPT28)},
-	{ ITCO_PCI_DEVICE(0x1e5d,				TCO_PPT29)},
-	{ ITCO_PCI_DEVICE(0x1e5e,				TCO_PPT30)},
-	{ ITCO_PCI_DEVICE(0x1e5f,				TCO_PPT31)},
+	{ PCI_VDEVICE(INTEL, 0x2410), TCO_ICH},
+	{ PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0},
+	{ PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2},
+	{ PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M},
+	{ PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3},
+	{ PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M},
+	{ PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4},
+	{ PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M},
+	{ PCI_VDEVICE(INTEL, 0x2450), TCO_CICH},
+	{ PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5},
+	{ PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB},
+	{ PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6},
+	{ PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M},
+	{ PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W},
+	{ PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB},
+	{ PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7},
+	{ PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH},
+	{ PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M},
+	{ PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH},
+	{ PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10},
+	{ PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8},
+	{ PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH},
+	{ PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO},
+	{ PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M},
+	{ PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME},
+	{ PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9},
+	{ PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R},
+	{ PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH},
+	{ PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO},
+	{ PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M},
+	{ PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME},
+	{ PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10},
+	{ PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R},
+	{ PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D},
+	{ PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO},
+	{ PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH},
+	{ PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM},
+	{ PCI_VDEVICE(INTEL, 0x3b02), TCO_P55},
+	{ PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55},
+	{ PCI_VDEVICE(INTEL, 0x3b06), TCO_H55},
+	{ PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57},
+	{ PCI_VDEVICE(INTEL, 0x3b08), TCO_H57},
+	{ PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55},
+	{ PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57},
+	{ PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57},
+	{ PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF},
+	{ PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57},
+	{ PCI_VDEVICE(INTEL, 0x3b12), TCO_3400},
+	{ PCI_VDEVICE(INTEL, 0x3b14), TCO_3420},
+	{ PCI_VDEVICE(INTEL, 0x3b16), TCO_3450},
+	{ PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579},
+	{ PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD},
+	{ PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM},
+	{ PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT},
+	{ PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG},
+	{ PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG},
+	{ PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC},
+	{ PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT},
+	{ PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},
 	{ 0, },			/* End of list */
 };
 MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
@@ -1052,15 +923,10 @@
 	iTCO_wdt_stop();
 }
 
-#define iTCO_wdt_suspend NULL
-#define iTCO_wdt_resume  NULL
-
 static struct platform_driver iTCO_wdt_driver = {
 	.probe          = iTCO_wdt_probe,
 	.remove         = __devexit_p(iTCO_wdt_remove),
 	.shutdown       = iTCO_wdt_shutdown,
-	.suspend        = iTCO_wdt_suspend,
-	.resume         = iTCO_wdt_resume,
 	.driver         = {
 		.owner  = THIS_MODULE,
 		.name   = DRV_NAME,
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 86f7cac..b8ef2c6 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -329,12 +329,18 @@
 	}
 }
 
+static const struct of_device_id imx2_wdt_dt_ids[] = {
+	{ .compatible = "fsl,imx21-wdt", },
+	{ /* sentinel */ }
+};
+
 static struct platform_driver imx2_wdt_driver = {
 	.remove		= __exit_p(imx2_wdt_remove),
 	.shutdown	= imx2_wdt_shutdown,
 	.driver		= {
 		.name	= DRIVER_NAME,
 		.owner	= THIS_MODULE,
+		.of_match_table = imx2_wdt_dt_ids,
 	},
 };
 
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index ba43860..1abdc04 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -43,7 +43,7 @@
 #include <linux/signal.h>
 #include <linux/sfi.h>
 #include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/intel_scu_ipc.h>
 #include <asm/apb_timer.h>
 #include <asm/mrst.h>
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c
index 6143f52..8d2d850 100644
--- a/drivers/watchdog/it8712f_wdt.c
+++ b/drivers/watchdog/it8712f_wdt.c
@@ -28,10 +28,10 @@
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/fs.h>
-#include <linux/pci.h>
 #include <linux/spinlock.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
+#include <linux/ioport.h>
 
 #define NAME "it8712f_wdt"
 
@@ -51,7 +51,6 @@
 
 static unsigned long wdt_open;
 static unsigned expect_close;
-static spinlock_t io_lock;
 static unsigned char revision;
 
 /* Dog Food address - We use the game port address */
@@ -121,20 +120,26 @@
 	outb(ldn, VAL);
 }
 
-static inline void superio_enter(void)
+static inline int superio_enter(void)
 {
-	spin_lock(&io_lock);
+	/*
+	 * Try to reserve REG and REG + 1 for exclusive access.
+	 */
+	if (!request_muxed_region(REG, 2, NAME))
+		return -EBUSY;
+
 	outb(0x87, REG);
 	outb(0x01, REG);
 	outb(0x55, REG);
 	outb(0x55, REG);
+	return 0;
 }
 
 static inline void superio_exit(void)
 {
 	outb(0x02, REG);
 	outb(0x02, VAL);
-	spin_unlock(&io_lock);
+	release_region(REG, 2);
 }
 
 static inline void it8712f_wdt_ping(void)
@@ -173,10 +178,13 @@
 		return 0;
 }
 
-static void it8712f_wdt_enable(void)
+static int it8712f_wdt_enable(void)
 {
+	int ret = superio_enter();
+	if (ret)
+		return ret;
+
 	printk(KERN_DEBUG NAME ": enabling watchdog timer\n");
-	superio_enter();
 	superio_select(LDN_GPIO);
 
 	superio_outb(wdt_control_reg, WDT_CONTROL);
@@ -186,13 +194,17 @@
 	superio_exit();
 
 	it8712f_wdt_ping();
+
+	return 0;
 }
 
-static void it8712f_wdt_disable(void)
+static int it8712f_wdt_disable(void)
 {
-	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+	int ret = superio_enter();
+	if (ret)
+		return ret;
 
-	superio_enter();
+	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
 	superio_select(LDN_GPIO);
 
 	superio_outb(0, WDT_CONFIG);
@@ -202,6 +214,7 @@
 	superio_outb(0, WDT_TIMEOUT);
 
 	superio_exit();
+	return 0;
 }
 
 static int it8712f_wdt_notify(struct notifier_block *this,
@@ -252,6 +265,7 @@
 						WDIOF_MAGICCLOSE,
 	};
 	int value;
+	int ret;
 
 	switch (cmd) {
 	case WDIOC_GETSUPPORT:
@@ -259,7 +273,9 @@
 			return -EFAULT;
 		return 0;
 	case WDIOC_GETSTATUS:
-		superio_enter();
+		ret = superio_enter();
+		if (ret)
+			return ret;
 		superio_select(LDN_GPIO);
 
 		value = it8712f_wdt_get_status();
@@ -280,7 +296,9 @@
 		if (value > (max_units * 60))
 			return -EINVAL;
 		margin = value;
-		superio_enter();
+		ret = superio_enter();
+		if (ret)
+			return ret;
 		superio_select(LDN_GPIO);
 
 		it8712f_wdt_update_margin();
@@ -299,10 +317,14 @@
 
 static int it8712f_wdt_open(struct inode *inode, struct file *file)
 {
+	int ret;
 	/* only allow one at a time */
 	if (test_and_set_bit(0, &wdt_open))
 		return -EBUSY;
-	it8712f_wdt_enable();
+
+	ret = it8712f_wdt_enable();
+	if (ret)
+		return ret;
 	return nonseekable_open(inode, file);
 }
 
@@ -313,7 +335,8 @@
 			": watchdog device closed unexpectedly, will not"
 			" disable the watchdog timer\n");
 	} else if (!nowayout) {
-		it8712f_wdt_disable();
+		if (it8712f_wdt_disable())
+			printk(KERN_WARNING NAME "Watchdog disable failed\n");
 	}
 	expect_close = 0;
 	clear_bit(0, &wdt_open);
@@ -340,8 +363,10 @@
 {
 	int err = -ENODEV;
 	int chip_type;
+	int ret = superio_enter();
+	if (ret)
+		return ret;
 
-	superio_enter();
 	chip_type = superio_inw(DEVID);
 	if (chip_type != IT8712F_DEVID)
 		goto exit;
@@ -382,8 +407,6 @@
 {
 	int err = 0;
 
-	spin_lock_init(&io_lock);
-
 	if (it8712f_wdt_find(&address))
 		return -ENODEV;
 
@@ -392,7 +415,11 @@
 		return -EBUSY;
 	}
 
-	it8712f_wdt_disable();
+	err = it8712f_wdt_disable();
+	if (err) {
+		printk(KERN_ERR NAME ": unable to disable watchdog timer.\n");
+		goto out;
+	}
 
 	err = register_reboot_notifier(&it8712f_wdt_notifier);
 	if (err) {
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
index b1bc72f..a2d9a12 100644
--- a/drivers/watchdog/it87_wdt.c
+++ b/drivers/watchdog/it87_wdt.c
@@ -137,7 +137,6 @@
 
 static	unsigned int base, gpact, ciract, max_units, chip_type;
 static	unsigned long wdt_status;
-static	DEFINE_SPINLOCK(spinlock);
 
 static	int nogameport = DEFAULT_NOGAMEPORT;
 static	int exclusive  = DEFAULT_EXCLUSIVE;
@@ -163,18 +162,26 @@
 
 /* Superio Chip */
 
-static inline void superio_enter(void)
+static inline int superio_enter(void)
 {
+	/*
+	 * Try to reserve REG and REG + 1 for exclusive access.
+	 */
+	if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
+		return -EBUSY;
+
 	outb(0x87, REG);
 	outb(0x01, REG);
 	outb(0x55, REG);
 	outb(0x55, REG);
+	return 0;
 }
 
 static inline void superio_exit(void)
 {
 	outb(0x02, REG);
 	outb(0x02, VAL);
+	release_region(REG, 2);
 }
 
 static inline void superio_select(int ldn)
@@ -255,12 +262,11 @@
 	set_bit(WDTS_KEEPALIVE, &wdt_status);
 }
 
-static void wdt_start(void)
+static int wdt_start(void)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&spinlock, flags);
-	superio_enter();
+	int ret = superio_enter();
+	if (ret)
+		return ret;
 
 	superio_select(GPIO);
 	if (test_bit(WDTS_USE_GP, &wdt_status))
@@ -270,15 +276,15 @@
 	wdt_update_timeout();
 
 	superio_exit();
-	spin_unlock_irqrestore(&spinlock, flags);
+
+	return 0;
 }
 
-static void wdt_stop(void)
+static int wdt_stop(void)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&spinlock, flags);
-	superio_enter();
+	int ret = superio_enter();
+	if (ret)
+		return ret;
 
 	superio_select(GPIO);
 	superio_outb(0x00, WDTCTRL);
@@ -288,7 +294,7 @@
 		superio_outb(0x00, WDTVALMSB);
 
 	superio_exit();
-	spin_unlock_irqrestore(&spinlock, flags);
+	return 0;
 }
 
 /**
@@ -303,8 +309,6 @@
 
 static int wdt_set_timeout(int t)
 {
-	unsigned long flags;
-
 	if (t < 1 || t > max_units * 60)
 		return -EINVAL;
 
@@ -313,14 +317,15 @@
 	else
 		timeout = t;
 
-	spin_lock_irqsave(&spinlock, flags);
 	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
-		superio_enter();
+		int ret = superio_enter();
+		if (ret)
+			return ret;
+
 		superio_select(GPIO);
 		wdt_update_timeout();
 		superio_exit();
 	}
-	spin_unlock_irqrestore(&spinlock, flags);
 	return 0;
 }
 
@@ -339,12 +344,12 @@
 
 static int wdt_get_status(int *status)
 {
-	unsigned long flags;
-
 	*status = 0;
 	if (testmode) {
-		spin_lock_irqsave(&spinlock, flags);
-		superio_enter();
+		int ret = superio_enter();
+		if (ret)
+			return ret;
+
 		superio_select(GPIO);
 		if (superio_inb(WDTCTRL) & WDT_ZERO) {
 			superio_outb(0x00, WDTCTRL);
@@ -353,7 +358,6 @@
 		}
 
 		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
 	}
 	if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
 		*status |= WDIOF_KEEPALIVEPING;
@@ -379,9 +383,17 @@
 	if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
 		return -EBUSY;
 	if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+		int ret;
 		if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
 			__module_get(THIS_MODULE);
-		wdt_start();
+
+		ret = wdt_start();
+		if (ret) {
+			clear_bit(WDTS_LOCKED, &wdt_status);
+			clear_bit(WDTS_TIMER_RUN, &wdt_status);
+			clear_bit(WDTS_DEV_OPEN, &wdt_status);
+			return ret;
+		}
 	}
 	return nonseekable_open(inode, file);
 }
@@ -403,7 +415,16 @@
 {
 	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
 		if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
-			wdt_stop();
+			int ret = wdt_stop();
+			if (ret) {
+				/*
+				 * Stop failed. Just keep the watchdog alive
+				 * and hope nothing bad happens.
+				 */
+				set_bit(WDTS_EXPECTED, &wdt_status);
+				wdt_keepalive();
+				return ret;
+			}
 			clear_bit(WDTS_TIMER_RUN, &wdt_status);
 		} else {
 			wdt_keepalive();
@@ -484,7 +505,9 @@
 				    &ident, sizeof(ident)) ? -EFAULT : 0;
 
 	case WDIOC_GETSTATUS:
-		wdt_get_status(&status);
+		rc = wdt_get_status(&status);
+		if (rc)
+			return rc;
 		return put_user(status, uarg.i);
 
 	case WDIOC_GETBOOTSTATUS:
@@ -500,14 +523,22 @@
 
 		switch (new_options) {
 		case WDIOS_DISABLECARD:
-			if (test_bit(WDTS_TIMER_RUN, &wdt_status))
-				wdt_stop();
+			if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+				rc = wdt_stop();
+				if (rc)
+					return rc;
+			}
 			clear_bit(WDTS_TIMER_RUN, &wdt_status);
 			return 0;
 
 		case WDIOS_ENABLECARD:
-			if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
-				wdt_start();
+			if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+				rc = wdt_start();
+				if (rc) {
+					clear_bit(WDTS_TIMER_RUN, &wdt_status);
+					return rc;
+				}
+			}
 			return 0;
 
 		default:
@@ -560,16 +591,17 @@
 	int rc = 0;
 	int try_gameport = !nogameport;
 	u8  chip_rev;
-	unsigned long flags;
+	int gp_rreq_fail = 0;
 
 	wdt_status = 0;
 
-	spin_lock_irqsave(&spinlock, flags);
-	superio_enter();
+	rc = superio_enter();
+	if (rc)
+		return rc;
+
 	chip_type = superio_inw(CHIPID);
 	chip_rev  = superio_inb(CHIPREV) & 0x0f;
 	superio_exit();
-	spin_unlock_irqrestore(&spinlock, flags);
 
 	switch (chip_type) {
 	case IT8702_ID:
@@ -603,8 +635,9 @@
 		return -ENODEV;
 	}
 
-	spin_lock_irqsave(&spinlock, flags);
-	superio_enter();
+	rc = superio_enter();
+	if (rc)
+		return rc;
 
 	superio_select(GPIO);
 	superio_outb(WDT_TOV1, WDTCFG);
@@ -620,21 +653,16 @@
 		}
 		gpact = superio_inb(ACTREG);
 		superio_outb(0x01, ACTREG);
-		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
 		if (request_region(base, 1, WATCHDOG_NAME))
 			set_bit(WDTS_USE_GP, &wdt_status);
 		else
-			rc = -EIO;
-	} else {
-		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
+			gp_rreq_fail = 1;
 	}
 
 	/* If we haven't Gameport support, try to get CIR support */
 	if (!test_bit(WDTS_USE_GP, &wdt_status)) {
 		if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
-			if (rc == -EIO)
+			if (gp_rreq_fail)
 				printk(KERN_ERR PFX
 					"I/O Address 0x%04x and 0x%04x"
 					" already in use\n", base, CIR_BASE);
@@ -646,21 +674,16 @@
 			goto err_out;
 		}
 		base = CIR_BASE;
-		spin_lock_irqsave(&spinlock, flags);
-		superio_enter();
 
 		superio_select(CIR);
 		superio_outw(base, BASEREG);
 		superio_outb(0x00, CIR_ILS);
 		ciract = superio_inb(ACTREG);
 		superio_outb(0x01, ACTREG);
-		if (rc == -EIO) {
+		if (gp_rreq_fail) {
 			superio_select(GAMEPORT);
 			superio_outb(gpact, ACTREG);
 		}
-
-		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
 	}
 
 	if (timeout < 1 || timeout > max_units * 60) {
@@ -704,6 +727,7 @@
 		"nogameport=%d)\n", chip_type, chip_rev, timeout,
 		nowayout, testmode, exclusive, nogameport);
 
+	superio_exit();
 	return 0;
 
 err_out_reboot:
@@ -711,49 +735,37 @@
 err_out_region:
 	release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
 	if (!test_bit(WDTS_USE_GP, &wdt_status)) {
-		spin_lock_irqsave(&spinlock, flags);
-		superio_enter();
 		superio_select(CIR);
 		superio_outb(ciract, ACTREG);
-		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
 	}
 err_out:
 	if (try_gameport) {
-		spin_lock_irqsave(&spinlock, flags);
-		superio_enter();
 		superio_select(GAMEPORT);
 		superio_outb(gpact, ACTREG);
-		superio_exit();
-		spin_unlock_irqrestore(&spinlock, flags);
 	}
 
+	superio_exit();
 	return rc;
 }
 
 static void __exit it87_wdt_exit(void)
 {
-	unsigned long flags;
-	int nolock;
-
-	nolock = !spin_trylock_irqsave(&spinlock, flags);
-	superio_enter();
-	superio_select(GPIO);
-	superio_outb(0x00, WDTCTRL);
-	superio_outb(0x00, WDTCFG);
-	superio_outb(0x00, WDTVALLSB);
-	if (max_units > 255)
-		superio_outb(0x00, WDTVALMSB);
-	if (test_bit(WDTS_USE_GP, &wdt_status)) {
-		superio_select(GAMEPORT);
-		superio_outb(gpact, ACTREG);
-	} else {
-		superio_select(CIR);
-		superio_outb(ciract, ACTREG);
+	if (superio_enter() == 0) {
+		superio_select(GPIO);
+		superio_outb(0x00, WDTCTRL);
+		superio_outb(0x00, WDTCFG);
+		superio_outb(0x00, WDTVALLSB);
+		if (max_units > 255)
+			superio_outb(0x00, WDTVALMSB);
+		if (test_bit(WDTS_USE_GP, &wdt_status)) {
+			superio_select(GAMEPORT);
+			superio_outb(gpact, ACTREG);
+		} else {
+			superio_select(CIR);
+			superio_outb(ciract, ACTREG);
+		}
+		superio_exit();
 	}
-	superio_exit();
-	if (!nolock)
-		spin_unlock_irqrestore(&spinlock, flags);
 
 	misc_deregister(&wdt_miscdev);
 	unregister_reboot_notifier(&wdt_notifier);
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
index 2b4af22..4dc3102 100644
--- a/drivers/watchdog/mpcore_wdt.c
+++ b/drivers/watchdog/mpcore_wdt.c
@@ -407,12 +407,35 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg)
+{
+	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+	mpcore_wdt_stop(wdt);		/* Turn the WDT off */
+	return 0;
+}
+
+static int mpcore_wdt_resume(struct platform_device *dev)
+{
+	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+	/* re-activate timer */
+	if (test_bit(0, &wdt->timer_alive))
+		mpcore_wdt_start(wdt);
+	return 0;
+}
+#else
+#define mpcore_wdt_suspend	NULL
+#define mpcore_wdt_resume	NULL
+#endif
+
 /* work with hotplug and coldplug */
 MODULE_ALIAS("platform:mpcore_wdt");
 
 static struct platform_driver mpcore_wdt_driver = {
 	.probe		= mpcore_wdt_probe,
 	.remove		= __devexit_p(mpcore_wdt_remove),
+	.suspend	= mpcore_wdt_suspend,
+	.resume		= mpcore_wdt_resume,
 	.shutdown	= mpcore_wdt_shutdown,
 	.driver		= {
 		.owner	= THIS_MODULE,
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index 0430e09..ac37bb8 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -225,11 +225,11 @@
 
 	ret = misc_register(&mtx1_wdt_misc);
 	if (ret < 0) {
-		printk(KERN_ERR " mtx-1_wdt : failed to register\n");
+		dev_err(&pdev->dev, "failed to register\n");
 		return ret;
 	}
 	mtx1_wdt_start();
-	printk(KERN_INFO "MTX-1 Watchdog driver\n");
+	dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");
 	return 0;
 }
 
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c
new file mode 100644
index 0000000..4ec741a
--- /dev/null
+++ b/drivers/watchdog/of_xilinx_wdt.c
@@ -0,0 +1,433 @@
+/*
+*   of_xilinx_wdt.c  1.01  A Watchdog Device Driver for Xilinx xps_timebase_wdt
+*
+*   (C) Copyright 2011 (Alejandro Cabrera <aldaya@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.
+*
+*       -----------------------
+*	30-May-2011 Alejandro Cabrera <aldaya@gmail.com>
+*		- If "xlnx,wdt-enable-once" wasn't found on device tree the
+*		  module will use CONFIG_WATCHDOG_NOWAYOUT
+*		- If the device tree parameters ("clock-frequency" and
+*		  "xlnx,wdt-interval") wasn't found the driver won't
+*		  know the wdt reset interval
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+
+/* Register offsets for the Wdt device */
+#define XWT_TWCSR0_OFFSET   0x0 /* Control/Status Register0 */
+#define XWT_TWCSR1_OFFSET   0x4 /* Control/Status Register1 */
+#define XWT_TBR_OFFSET      0x8 /* Timebase Register Offset */
+
+/* Control/Status Register Masks  */
+#define XWT_CSR0_WRS_MASK   0x00000008 /* Reset status */
+#define XWT_CSR0_WDS_MASK   0x00000004 /* Timer state  */
+#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
+
+/* Control/Status Register 0/1 bits  */
+#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
+
+/* SelfTest constants */
+#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
+#define XWT_TIMER_FAILED            0xFFFFFFFF
+
+#define WATCHDOG_NAME     "Xilinx Watchdog"
+#define PFX WATCHDOG_NAME ": "
+
+struct xwdt_device {
+	struct resource  res;
+	void __iomem *base;
+	u32 nowayout;
+	u32 wdt_interval;
+	u32 boot_status;
+};
+
+static struct xwdt_device xdev;
+
+static  u32 timeout;
+static  u32 control_status_reg;
+static  u8  expect_close;
+static  u8  no_timeout;
+static unsigned long driver_open;
+
+static  DEFINE_SPINLOCK(spinlock);
+
+static void xwdt_start(void)
+{
+	spin_lock(&spinlock);
+
+	/* Clean previous status and enable the watchdog timer */
+	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
+
+	iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
+				xdev.base + XWT_TWCSR0_OFFSET);
+
+	iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET);
+
+	spin_unlock(&spinlock);
+}
+
+static void xwdt_stop(void)
+{
+	spin_lock(&spinlock);
+
+	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+
+	iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
+				xdev.base + XWT_TWCSR0_OFFSET);
+
+	iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET);
+
+	spin_unlock(&spinlock);
+	printk(KERN_INFO PFX "Stopped!\n");
+}
+
+static void xwdt_keepalive(void)
+{
+	spin_lock(&spinlock);
+
+	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
+	iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET);
+
+	spin_unlock(&spinlock);
+}
+
+static void xwdt_get_status(int *status)
+{
+	int new_status;
+
+	spin_lock(&spinlock);
+
+	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
+	new_status = ((control_status_reg &
+			(XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0);
+	spin_unlock(&spinlock);
+
+	*status = 0;
+	if (new_status & 1)
+		*status |= WDIOF_CARDRESET;
+}
+
+static u32 xwdt_selftest(void)
+{
+	int i;
+	u32 timer_value1;
+	u32 timer_value2;
+
+	spin_lock(&spinlock);
+
+	timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET);
+	timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
+
+	for (i = 0;
+		((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
+			(timer_value2 == timer_value1)); i++) {
+		timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
+	}
+
+	spin_unlock(&spinlock);
+
+	if (timer_value2 != timer_value1)
+		return ~XWT_TIMER_FAILED;
+	else
+		return XWT_TIMER_FAILED;
+}
+
+static int xwdt_open(struct inode *inode, struct file *file)
+{
+	/* Only one process can handle the wdt at a time */
+	if (test_and_set_bit(0, &driver_open))
+		return -EBUSY;
+
+	/* Make sure that the module are always loaded...*/
+	if (xdev.nowayout)
+		__module_get(THIS_MODULE);
+
+	xwdt_start();
+	printk(KERN_INFO PFX "Started...\n");
+
+	return nonseekable_open(inode, file);
+}
+
+static int xwdt_release(struct inode *inode, struct file *file)
+{
+	if (expect_close == 42) {
+		xwdt_stop();
+	} else {
+		printk(KERN_CRIT PFX
+			"Unexpected close, not stopping watchdog!\n");
+		xwdt_keepalive();
+	}
+
+	clear_bit(0, &driver_open);
+	expect_close = 0;
+	return 0;
+}
+
+/*
+ *      xwdt_write:
+ *      @file: file handle to the watchdog
+ *      @buf: buffer to write (unused as data does not matter here
+ *      @count: count of bytes
+ *      @ppos: pointer to the position to write. No seeks allowed
+ *
+ *      A write to a watchdog device is defined as a keepalive signal. Any
+ *      write of data will do, as we don't define content meaning.
+ */
+static ssize_t xwdt_write(struct file *file, const char __user *buf,
+						size_t len, loff_t *ppos)
+{
+	if (len) {
+		if (!xdev.nowayout) {
+			size_t i;
+
+			/* In case it was set long ago */
+			expect_close = 0;
+
+			for (i = 0; i != len; i++) {
+				char c;
+
+				if (get_user(c, buf + i))
+					return -EFAULT;
+				if (c == 'V')
+					expect_close = 42;
+			}
+		}
+		xwdt_keepalive();
+	}
+	return len;
+}
+
+static const struct watchdog_info ident = {
+	.options =  WDIOF_MAGICCLOSE |
+		    WDIOF_KEEPALIVEPING,
+	.firmware_version =	1,
+	.identity =	WATCHDOG_NAME,
+};
+
+/*
+ *      xwdt_ioctl:
+ *      @file: file handle to the device
+ *      @cmd: watchdog command
+ *      @arg: argument pointer
+ *
+ *      The watchdog API defines a common set of functions for all watchdogs
+ *      according to their available features.
+ */
+static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int status;
+
+	union {
+		struct watchdog_info __user *ident;
+		int __user *i;
+	} uarg;
+
+	uarg.i = (int __user *)arg;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		return copy_to_user(uarg.ident, &ident,
+					sizeof(ident)) ? -EFAULT : 0;
+
+	case WDIOC_GETBOOTSTATUS:
+		return put_user(xdev.boot_status, uarg.i);
+
+	case WDIOC_GETSTATUS:
+		xwdt_get_status(&status);
+		return put_user(status, uarg.i);
+
+	case WDIOC_KEEPALIVE:
+		xwdt_keepalive();
+		return 0;
+
+	case WDIOC_GETTIMEOUT:
+		if (no_timeout)
+			return -ENOTTY;
+		else
+			return put_user(timeout, uarg.i);
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations xwdt_fops = {
+	.owner      = THIS_MODULE,
+	.llseek     = no_llseek,
+	.write      = xwdt_write,
+	.open       = xwdt_open,
+	.release    = xwdt_release,
+	.unlocked_ioctl = xwdt_ioctl,
+};
+
+static struct miscdevice xwdt_miscdev = {
+	.minor      = WATCHDOG_MINOR,
+	.name       = "watchdog",
+	.fops       = &xwdt_fops,
+};
+
+static int __devinit xwdt_probe(struct platform_device *pdev)
+{
+	int rc;
+	u32 *tmptr;
+	u32 *pfreq;
+
+	no_timeout = 0;
+
+	pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent,
+					"clock-frequency", NULL);
+
+	if (pfreq == NULL) {
+		printk(KERN_WARNING PFX
+			"The watchdog clock frequency cannot be obtained!\n");
+		no_timeout = 1;
+	}
+
+	rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res);
+	if (rc) {
+		printk(KERN_WARNING PFX "invalid address!\n");
+		return rc;
+	}
+
+	tmptr = (u32 *)of_get_property(pdev->dev.of_node,
+					"xlnx,wdt-interval", NULL);
+	if (tmptr == NULL) {
+		printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\""
+					" not found in device tree!\n");
+		no_timeout = 1;
+	} else {
+		xdev.wdt_interval = *tmptr;
+	}
+
+	tmptr = (u32 *)of_get_property(pdev->dev.of_node,
+					"xlnx,wdt-enable-once", NULL);
+	if (tmptr == NULL) {
+		printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\""
+					" not found in device tree!\n");
+		xdev.nowayout = WATCHDOG_NOWAYOUT;
+	}
+
+/*
+ *  Twice of the 2^wdt_interval / freq  because the first wdt overflow is
+ *  ignored (interrupt), reset is only generated at second wdt overflow
+ */
+	if (!no_timeout)
+		timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq);
+
+	if (!request_mem_region(xdev.res.start,
+			xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) {
+		rc = -ENXIO;
+		printk(KERN_ERR PFX "memory request failure!\n");
+		goto err_out;
+	}
+
+	xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1);
+	if (xdev.base == NULL) {
+		rc = -ENOMEM;
+		printk(KERN_ERR PFX "ioremap failure!\n");
+		goto release_mem;
+	}
+
+	rc = xwdt_selftest();
+	if (rc == XWT_TIMER_FAILED) {
+		printk(KERN_ERR PFX "SelfTest routine error!\n");
+		goto unmap_io;
+	}
+
+	xwdt_get_status(&xdev.boot_status);
+
+	rc = misc_register(&xwdt_miscdev);
+	if (rc) {
+		printk(KERN_ERR PFX
+			"cannot register miscdev on minor=%d (err=%d)\n",
+						xwdt_miscdev.minor, rc);
+		goto unmap_io;
+	}
+
+	if (no_timeout)
+		printk(KERN_INFO PFX
+			"driver loaded (timeout=? sec, nowayout=%d)\n",
+						    xdev.nowayout);
+	else
+		printk(KERN_INFO PFX
+			"driver loaded (timeout=%d sec, nowayout=%d)\n",
+					timeout, xdev.nowayout);
+
+	expect_close = 0;
+	clear_bit(0, &driver_open);
+
+	return 0;
+
+unmap_io:
+	iounmap(xdev.base);
+release_mem:
+	release_mem_region(xdev.res.start, resource_size(&xdev.res));
+err_out:
+	return rc;
+}
+
+static int __devexit xwdt_remove(struct platform_device *dev)
+{
+	misc_deregister(&xwdt_miscdev);
+	iounmap(xdev.base);
+	release_mem_region(xdev.res.start, resource_size(&xdev.res));
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id __devinitdata xwdt_of_match[] = {
+	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, xwdt_of_match);
+
+static struct platform_driver xwdt_driver = {
+	.probe       = xwdt_probe,
+	.remove      = __devexit_p(xwdt_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = WATCHDOG_NAME,
+		.of_match_table = xwdt_of_match,
+	},
+};
+
+static int __init xwdt_init(void)
+{
+	return platform_driver_register(&xwdt_driver);
+}
+
+static void __exit xwdt_exit(void)
+{
+	platform_driver_unregister(&xwdt_driver);
+}
+
+module_init(xwdt_init);
+module_exit(xwdt_exit);
+
+MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
+MODULE_DESCRIPTION("Xilinx Watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
index b7c1390..e78d899 100644
--- a/drivers/watchdog/pc87413_wdt.c
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -56,6 +56,7 @@
 #define IO_DEFAULT	0x2E		/* Address used on Portwell Boards */
 
 static int io = IO_DEFAULT;
+static int swc_base_addr = -1;
 
 static int timeout = DEFAULT_TIMEOUT;	/* timeout value */
 static unsigned long timer_enabled;	/* is the timer enabled? */
@@ -116,9 +117,8 @@
 
 /* Read SWC I/O base address */
 
-static inline unsigned int pc87413_get_swc_base(void)
+static void pc87413_get_swc_base_addr(void)
 {
-	unsigned int  swc_base_addr = 0;
 	unsigned char addr_l, addr_h = 0;
 
 	/* Step 3: Read SWC I/O Base Address */
@@ -136,12 +136,11 @@
 		"Read SWC I/O Base Address: low %d, high %d, res %d\n",
 						addr_l, addr_h, swc_base_addr);
 #endif
-	return swc_base_addr;
 }
 
 /* Select Bank 3 of SWC */
 
-static inline void pc87413_swc_bank3(unsigned int swc_base_addr)
+static inline void pc87413_swc_bank3(void)
 {
 	/* Step 4: Select Bank3 of SWC */
 	outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
@@ -152,8 +151,7 @@
 
 /* Set watchdog timeout to x minutes */
 
-static inline void pc87413_programm_wdto(unsigned int swc_base_addr,
-					 char pc87413_time)
+static inline void pc87413_programm_wdto(char pc87413_time)
 {
 	/* Step 5: Programm WDTO, Twd. */
 	outb_p(pc87413_time, swc_base_addr + WDTO);
@@ -164,7 +162,7 @@
 
 /* Enable WDEN */
 
-static inline void pc87413_enable_wden(unsigned int swc_base_addr)
+static inline void pc87413_enable_wden(void)
 {
 	/* Step 6: Enable WDEN */
 	outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
@@ -174,7 +172,7 @@
 }
 
 /* Enable SW_WD_TREN */
-static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr)
+static inline void pc87413_enable_sw_wd_tren(void)
 {
 	/* Enable SW_WD_TREN */
 	outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
@@ -185,7 +183,7 @@
 
 /* Disable SW_WD_TREN */
 
-static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr)
+static inline void pc87413_disable_sw_wd_tren(void)
 {
 	/* Disable SW_WD_TREN */
 	outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
@@ -196,7 +194,7 @@
 
 /* Enable SW_WD_TRG */
 
-static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr)
+static inline void pc87413_enable_sw_wd_trg(void)
 {
 	/* Enable SW_WD_TRG */
 	outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
@@ -207,7 +205,7 @@
 
 /* Disable SW_WD_TRG */
 
-static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr)
+static inline void pc87413_disable_sw_wd_trg(void)
 {
 	/* Disable SW_WD_TRG */
 	outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
@@ -222,18 +220,13 @@
 
 static void pc87413_enable(void)
 {
-	unsigned int swc_base_addr;
-
 	spin_lock(&io_lock);
 
-	pc87413_select_wdt_out();
-	pc87413_enable_swc();
-	swc_base_addr = pc87413_get_swc_base();
-	pc87413_swc_bank3(swc_base_addr);
-	pc87413_programm_wdto(swc_base_addr, timeout);
-	pc87413_enable_wden(swc_base_addr);
-	pc87413_enable_sw_wd_tren(swc_base_addr);
-	pc87413_enable_sw_wd_trg(swc_base_addr);
+	pc87413_swc_bank3();
+	pc87413_programm_wdto(timeout);
+	pc87413_enable_wden();
+	pc87413_enable_sw_wd_tren();
+	pc87413_enable_sw_wd_trg();
 
 	spin_unlock(&io_lock);
 }
@@ -242,17 +235,12 @@
 
 static void pc87413_disable(void)
 {
-	unsigned int swc_base_addr;
-
 	spin_lock(&io_lock);
 
-	pc87413_select_wdt_out();
-	pc87413_enable_swc();
-	swc_base_addr = pc87413_get_swc_base();
-	pc87413_swc_bank3(swc_base_addr);
-	pc87413_disable_sw_wd_tren(swc_base_addr);
-	pc87413_disable_sw_wd_trg(swc_base_addr);
-	pc87413_programm_wdto(swc_base_addr, 0);
+	pc87413_swc_bank3();
+	pc87413_disable_sw_wd_tren();
+	pc87413_disable_sw_wd_trg();
+	pc87413_programm_wdto(0);
 
 	spin_unlock(&io_lock);
 }
@@ -261,20 +249,15 @@
 
 static void pc87413_refresh(void)
 {
-	unsigned int swc_base_addr;
-
 	spin_lock(&io_lock);
 
-	pc87413_select_wdt_out();
-	pc87413_enable_swc();
-	swc_base_addr = pc87413_get_swc_base();
-	pc87413_swc_bank3(swc_base_addr);
-	pc87413_disable_sw_wd_tren(swc_base_addr);
-	pc87413_disable_sw_wd_trg(swc_base_addr);
-	pc87413_programm_wdto(swc_base_addr, timeout);
-	pc87413_enable_wden(swc_base_addr);
-	pc87413_enable_sw_wd_tren(swc_base_addr);
-	pc87413_enable_sw_wd_trg(swc_base_addr);
+	pc87413_swc_bank3();
+	pc87413_disable_sw_wd_tren();
+	pc87413_disable_sw_wd_trg();
+	pc87413_programm_wdto(timeout);
+	pc87413_enable_wden();
+	pc87413_enable_sw_wd_tren();
+	pc87413_enable_sw_wd_trg();
 
 	spin_unlock(&io_lock);
 }
@@ -528,7 +511,8 @@
 	printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",
 							WDT_INDEX_IO_PORT);
 
-	/* request_region(io, 2, "pc87413"); */
+	if (!request_muxed_region(io, 2, MODNAME))
+		return -EBUSY;
 
 	ret = register_reboot_notifier(&pc87413_notifier);
 	if (ret != 0) {
@@ -541,12 +525,32 @@
 		printk(KERN_ERR PFX
 			"cannot register miscdev on minor=%d (err=%d)\n",
 			WATCHDOG_MINOR, ret);
-		unregister_reboot_notifier(&pc87413_notifier);
-		return ret;
+		goto reboot_unreg;
 	}
 	printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout);
+
+	pc87413_select_wdt_out();
+	pc87413_enable_swc();
+	pc87413_get_swc_base_addr();
+
+	if (!request_region(swc_base_addr, 0x20, MODNAME)) {
+		printk(KERN_ERR PFX
+			"cannot request SWC region at 0x%x\n", swc_base_addr);
+		ret = -EBUSY;
+		goto misc_unreg;
+	}
+
 	pc87413_enable();
+
+	release_region(io, 2);
 	return 0;
+
+misc_unreg:
+	misc_deregister(&pc87413_miscdev);
+reboot_unreg:
+	unregister_reboot_notifier(&pc87413_notifier);
+	release_region(io, 2);
+	return ret;
 }
 
 /**
@@ -569,7 +573,7 @@
 
 	misc_deregister(&pc87413_miscdev);
 	unregister_reboot_notifier(&pc87413_notifier);
-	/* release_region(io, 2); */
+	release_region(swc_base_addr, 0x20);
 
 	printk(KERN_INFO MODNAME " watchdog component driver removed.\n");
 }
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f7f5aa0..30da88f 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -589,6 +589,15 @@
 #define s3c2410wdt_resume  NULL
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_OF
+static const struct of_device_id s3c2410_wdt_match[] = {
+	{ .compatible = "samsung,s3c2410-wdt" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
+#else
+#define s3c2410_wdt_match NULL
+#endif
 
 static struct platform_driver s3c2410wdt_driver = {
 	.probe		= s3c2410wdt_probe,
@@ -599,6 +608,7 @@
 	.driver		= {
 		.owner	= THIS_MODULE,
 		.name	= "s3c2410-wdt",
+		.of_match_table	= s3c2410_wdt_match,
 	},
 };
 
diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c
index ff11504..93ac589 100644
--- a/drivers/watchdog/sbc7240_wdt.c
+++ b/drivers/watchdog/sbc7240_wdt.c
@@ -29,7 +29,7 @@
 #include <linux/watchdog.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 #define SBC7240_PREFIX "sbc7240_wdt: "
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index c7cf4b0..029467e 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -472,15 +472,10 @@
 	sch311x_wdt_stop();
 }
 
-#define sch311x_wdt_suspend NULL
-#define sch311x_wdt_resume  NULL
-
 static struct platform_driver sch311x_wdt_driver = {
 	.probe		= sch311x_wdt_probe,
 	.remove		= __devexit_p(sch311x_wdt_remove),
 	.shutdown	= sch311x_wdt_shutdown,
-	.suspend	= sch311x_wdt_suspend,
-	.resume		= sch311x_wdt_resume,
 	.driver		= {
 		.owner = THIS_MODULE,
 		.name = DRV_NAME,
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 0d80e08..cc2cfbe 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -134,6 +134,8 @@
 	writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
 	writel(LOCK, wdt->base + WDTLOCK);
 
+	/* Flush posted writes. */
+	readl(wdt->base + WDTLOCK);
 	spin_unlock(&wdt->lock);
 }
 
@@ -144,9 +146,10 @@
 
 	writel(UNLOCK, wdt->base + WDTLOCK);
 	writel(0, wdt->base + WDTCONTROL);
-	writel(0, wdt->base + WDTLOAD);
 	writel(LOCK, wdt->base + WDTLOCK);
 
+	/* Flush posted writes. */
+	readl(wdt->base + WDTLOCK);
 	spin_unlock(&wdt->lock);
 }
 
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
new file mode 100644
index 0000000..cfa1a15
--- /dev/null
+++ b/drivers/watchdog/watchdog_core.c
@@ -0,0 +1,111 @@
+/*
+ *	watchdog_core.c
+ *
+ *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ *						All Rights Reserved.
+ *
+ *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ *	This source code is part of the generic code that can be used
+ *	by all the watchdog timer drivers.
+ *
+ *	Based on source code of the following authors:
+ *	  Matt Domsch <Matt_Domsch@dell.com>,
+ *	  Rob Radez <rob@osinvestor.com>,
+ *	  Rusty Lynch <rusty@linux.co.intel.com>
+ *	  Satyam Sharma <satyam@infradead.org>
+ *	  Randy Dunlap <randy.dunlap@oracle.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.
+ *
+ *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
+ *	admit liability nor provide warranty for any of this software.
+ *	This material is provided "AS-IS" and at no charge.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>	/* For EXPORT_SYMBOL/module stuff/... */
+#include <linux/types.h>	/* For standard types */
+#include <linux/errno.h>	/* For the -ENODEV/... values */
+#include <linux/kernel.h>	/* For printk/panic/... */
+#include <linux/watchdog.h>	/* For watchdog specific items */
+#include <linux/init.h>		/* For __init/__exit/... */
+
+#include "watchdog_dev.h"	/* For watchdog_dev_register/... */
+
+/**
+ * watchdog_register_device() - register a watchdog device
+ * @wdd: watchdog device
+ *
+ * Register a watchdog device with the kernel so that the
+ * watchdog timer can be accessed from userspace.
+ *
+ * A zero is returned on success and a negative errno code for
+ * failure.
+ */
+int watchdog_register_device(struct watchdog_device *wdd)
+{
+	int ret;
+
+	if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
+		return -EINVAL;
+
+	/* Mandatory operations need to be supported */
+	if (wdd->ops->start == NULL || wdd->ops->stop == NULL)
+		return -EINVAL;
+
+	/*
+	 * Check that we have valid min and max timeout values, if
+	 * not reset them both to 0 (=not used or unknown)
+	 */
+	if (wdd->min_timeout > wdd->max_timeout) {
+		pr_info("Invalid min and max timeout values, resetting to 0!\n");
+		wdd->min_timeout = 0;
+		wdd->max_timeout = 0;
+	}
+
+	/*
+	 * Note: now that all watchdog_device data has been verified, we
+	 * will not check this anymore in other functions. If data gets
+	 * corrupted in a later stage then we expect a kernel panic!
+	 */
+
+	/* We only support 1 watchdog device via the /dev/watchdog interface */
+	ret = watchdog_dev_register(wdd);
+	if (ret) {
+		pr_err("error registering /dev/watchdog (err=%d).\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(watchdog_register_device);
+
+/**
+ * watchdog_unregister_device() - unregister a watchdog device
+ * @wdd: watchdog device to unregister
+ *
+ * Unregister a watchdog device that was previously successfully
+ * registered with watchdog_register_device().
+ */
+void watchdog_unregister_device(struct watchdog_device *wdd)
+{
+	int ret;
+
+	if (wdd == NULL)
+		return;
+
+	ret = watchdog_dev_unregister(wdd);
+	if (ret)
+		pr_err("error unregistering /dev/watchdog (err=%d).\n", ret);
+}
+EXPORT_SYMBOL_GPL(watchdog_unregister_device);
+
+MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("WatchDog Timer Driver Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
new file mode 100644
index 0000000..d33520d
--- /dev/null
+++ b/drivers/watchdog/watchdog_dev.c
@@ -0,0 +1,395 @@
+/*
+ *	watchdog_dev.c
+ *
+ *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ *						All Rights Reserved.
+ *
+ *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ *
+ *	This source code is part of the generic code that can be used
+ *	by all the watchdog timer drivers.
+ *
+ *	This part of the generic code takes care of the following
+ *	misc device: /dev/watchdog.
+ *
+ *	Based on source code of the following authors:
+ *	  Matt Domsch <Matt_Domsch@dell.com>,
+ *	  Rob Radez <rob@osinvestor.com>,
+ *	  Rusty Lynch <rusty@linux.co.intel.com>
+ *	  Satyam Sharma <satyam@infradead.org>
+ *	  Randy Dunlap <randy.dunlap@oracle.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.
+ *
+ *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
+ *	admit liability nor provide warranty for any of this software.
+ *	This material is provided "AS-IS" and at no charge.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>	/* For module stuff/... */
+#include <linux/types.h>	/* For standard types (like size_t) */
+#include <linux/errno.h>	/* For the -ENODEV/... values */
+#include <linux/kernel.h>	/* For printk/panic/... */
+#include <linux/fs.h>		/* For file operations */
+#include <linux/watchdog.h>	/* For watchdog specific items */
+#include <linux/miscdevice.h>	/* For handling misc devices */
+#include <linux/init.h>		/* For __init/__exit/... */
+#include <linux/uaccess.h>	/* For copy_to_user/put_user/... */
+
+/* make sure we only register one /dev/watchdog device */
+static unsigned long watchdog_dev_busy;
+/* the watchdog device behind /dev/watchdog */
+static struct watchdog_device *wdd;
+
+/*
+ *	watchdog_ping: ping the watchdog.
+ *	@wddev: the watchdog device to ping
+ *
+ *	If the watchdog has no own ping operation then it needs to be
+ *	restarted via the start operation. This wrapper function does
+ *	exactly that.
+ *	We only ping when the watchdog device is running.
+ */
+
+static int watchdog_ping(struct watchdog_device *wddev)
+{
+	if (test_bit(WDOG_ACTIVE, &wdd->status)) {
+		if (wddev->ops->ping)
+			return wddev->ops->ping(wddev);  /* ping the watchdog */
+		else
+			return wddev->ops->start(wddev); /* restart watchdog */
+	}
+	return 0;
+}
+
+/*
+ *	watchdog_start: wrapper to start the watchdog.
+ *	@wddev: the watchdog device to start
+ *
+ *	Start the watchdog if it is not active and mark it active.
+ *	This function returns zero on success or a negative errno code for
+ *	failure.
+ */
+
+static int watchdog_start(struct watchdog_device *wddev)
+{
+	int err;
+
+	if (!test_bit(WDOG_ACTIVE, &wdd->status)) {
+		err = wddev->ops->start(wddev);
+		if (err < 0)
+			return err;
+
+		set_bit(WDOG_ACTIVE, &wdd->status);
+	}
+	return 0;
+}
+
+/*
+ *	watchdog_stop: wrapper to stop the watchdog.
+ *	@wddev: the watchdog device to stop
+ *
+ *	Stop the watchdog if it is still active and unmark it active.
+ *	This function returns zero on success or a negative errno code for
+ *	failure.
+ *	If the 'nowayout' feature was set, the watchdog cannot be stopped.
+ */
+
+static int watchdog_stop(struct watchdog_device *wddev)
+{
+	int err = -EBUSY;
+
+	if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) {
+		pr_info("%s: nowayout prevents watchdog to be stopped!\n",
+							wdd->info->identity);
+		return err;
+	}
+
+	if (test_bit(WDOG_ACTIVE, &wdd->status)) {
+		err = wddev->ops->stop(wddev);
+		if (err < 0)
+			return err;
+
+		clear_bit(WDOG_ACTIVE, &wdd->status);
+	}
+	return 0;
+}
+
+/*
+ *	watchdog_write: writes to the watchdog.
+ *	@file: file from VFS
+ *	@data: user address of data
+ *	@len: length of data
+ *	@ppos: pointer to the file offset
+ *
+ *	A write to a watchdog device is defined as a keepalive ping.
+ *	Writing the magic 'V' sequence allows the next close to turn
+ *	off the watchdog (if 'nowayout' is not set).
+ */
+
+static ssize_t watchdog_write(struct file *file, const char __user *data,
+						size_t len, loff_t *ppos)
+{
+	size_t i;
+	char c;
+
+	if (len == 0)
+		return 0;
+
+	/*
+	 * Note: just in case someone wrote the magic character
+	 * five months ago...
+	 */
+	clear_bit(WDOG_ALLOW_RELEASE, &wdd->status);
+
+	/* scan to see whether or not we got the magic character */
+	for (i = 0; i != len; i++) {
+		if (get_user(c, data + i))
+			return -EFAULT;
+		if (c == 'V')
+			set_bit(WDOG_ALLOW_RELEASE, &wdd->status);
+	}
+
+	/* someone wrote to us, so we send the watchdog a keepalive ping */
+	watchdog_ping(wdd);
+
+	return len;
+}
+
+/*
+ *	watchdog_ioctl: handle the different ioctl's for the watchdog device.
+ *	@file: file handle to the device
+ *	@cmd: watchdog command
+ *	@arg: argument pointer
+ *
+ *	The watchdog API defines a common set of functions for all watchdogs
+ *	according to their available features.
+ */
+
+static long watchdog_ioctl(struct file *file, unsigned int cmd,
+							unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	unsigned int val;
+	int err;
+
+	if (wdd->ops->ioctl) {
+		err = wdd->ops->ioctl(wdd, cmd, arg);
+		if (err != -ENOIOCTLCMD)
+			return err;
+	}
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		return copy_to_user(argp, wdd->info,
+			sizeof(struct watchdog_info)) ? -EFAULT : 0;
+	case WDIOC_GETSTATUS:
+		val = wdd->ops->status ? wdd->ops->status(wdd) : 0;
+		return put_user(val, p);
+	case WDIOC_GETBOOTSTATUS:
+		return put_user(wdd->bootstatus, p);
+	case WDIOC_SETOPTIONS:
+		if (get_user(val, p))
+			return -EFAULT;
+		if (val & WDIOS_DISABLECARD) {
+			err = watchdog_stop(wdd);
+			if (err < 0)
+				return err;
+		}
+		if (val & WDIOS_ENABLECARD) {
+			err = watchdog_start(wdd);
+			if (err < 0)
+				return err;
+		}
+		return 0;
+	case WDIOC_KEEPALIVE:
+		if (!(wdd->info->options & WDIOF_KEEPALIVEPING))
+			return -EOPNOTSUPP;
+		watchdog_ping(wdd);
+		return 0;
+	case WDIOC_SETTIMEOUT:
+		if ((wdd->ops->set_timeout == NULL) ||
+		    !(wdd->info->options & WDIOF_SETTIMEOUT))
+			return -EOPNOTSUPP;
+		if (get_user(val, p))
+			return -EFAULT;
+		if ((wdd->max_timeout != 0) &&
+		    (val < wdd->min_timeout || val > wdd->max_timeout))
+				return -EINVAL;
+		err = wdd->ops->set_timeout(wdd, val);
+		if (err < 0)
+			return err;
+		wdd->timeout = val;
+		/* If the watchdog is active then we send a keepalive ping
+		 * to make sure that the watchdog keep's running (and if
+		 * possible that it takes the new timeout) */
+		watchdog_ping(wdd);
+		/* Fall */
+	case WDIOC_GETTIMEOUT:
+		/* timeout == 0 means that we don't know the timeout */
+		if (wdd->timeout == 0)
+			return -EOPNOTSUPP;
+		return put_user(wdd->timeout, p);
+	default:
+		return -ENOTTY;
+	}
+}
+
+/*
+ *	watchdog_open: open the /dev/watchdog device.
+ *	@inode: inode of device
+ *	@file: file handle to device
+ *
+ *	When the /dev/watchdog device gets opened, we start the watchdog.
+ *	Watch out: the /dev/watchdog device is single open, so we make sure
+ *	it can only be opened once.
+ */
+
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+	int err = -EBUSY;
+
+	/* the watchdog is single open! */
+	if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
+		return -EBUSY;
+
+	/*
+	 * If the /dev/watchdog device is open, we don't want the module
+	 * to be unloaded.
+	 */
+	if (!try_module_get(wdd->ops->owner))
+		goto out;
+
+	err = watchdog_start(wdd);
+	if (err < 0)
+		goto out_mod;
+
+	/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
+	return nonseekable_open(inode, file);
+
+out_mod:
+	module_put(wdd->ops->owner);
+out:
+	clear_bit(WDOG_DEV_OPEN, &wdd->status);
+	return err;
+}
+
+/*
+ *      watchdog_release: release the /dev/watchdog device.
+ *      @inode: inode of device
+ *      @file: file handle to device
+ *
+ *	This is the code for when /dev/watchdog gets closed. We will only
+ *	stop the watchdog when we have received the magic char (and nowayout
+ *	was not set), else the watchdog will keep running.
+ */
+
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+	int err = -EBUSY;
+
+	/*
+	 * We only stop the watchdog if we received the magic character
+	 * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
+	 * watchdog_stop will fail.
+	 */
+	if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
+	    !(wdd->info->options & WDIOF_MAGICCLOSE))
+		err = watchdog_stop(wdd);
+
+	/* If the watchdog was not stopped, send a keepalive ping */
+	if (err < 0) {
+		pr_crit("%s: watchdog did not stop!\n", wdd->info->identity);
+		watchdog_ping(wdd);
+	}
+
+	/* Allow the owner module to be unloaded again */
+	module_put(wdd->ops->owner);
+
+	/* make sure that /dev/watchdog can be re-opened */
+	clear_bit(WDOG_DEV_OPEN, &wdd->status);
+
+	return 0;
+}
+
+static const struct file_operations watchdog_fops = {
+	.owner		= THIS_MODULE,
+	.write		= watchdog_write,
+	.unlocked_ioctl	= watchdog_ioctl,
+	.open		= watchdog_open,
+	.release	= watchdog_release,
+};
+
+static struct miscdevice watchdog_miscdev = {
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &watchdog_fops,
+};
+
+/*
+ *	watchdog_dev_register:
+ *	@watchdog: watchdog device
+ *
+ *	Register a watchdog device as /dev/watchdog. /dev/watchdog
+ *	is actually a miscdevice and thus we set it up like that.
+ */
+
+int watchdog_dev_register(struct watchdog_device *watchdog)
+{
+	int err;
+
+	/* Only one device can register for /dev/watchdog */
+	if (test_and_set_bit(0, &watchdog_dev_busy)) {
+		pr_err("only one watchdog can use /dev/watchdog.\n");
+		return -EBUSY;
+	}
+
+	wdd = watchdog;
+
+	err = misc_register(&watchdog_miscdev);
+	if (err != 0) {
+		pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
+			watchdog->info->identity, WATCHDOG_MINOR, err);
+		goto out;
+	}
+
+	return 0;
+
+out:
+	wdd = NULL;
+	clear_bit(0, &watchdog_dev_busy);
+	return err;
+}
+
+/*
+ *	watchdog_dev_unregister:
+ *	@watchdog: watchdog device
+ *
+ *	Deregister the /dev/watchdog device.
+ */
+
+int watchdog_dev_unregister(struct watchdog_device *watchdog)
+{
+	/* Check that a watchdog device was registered in the past */
+	if (!test_bit(0, &watchdog_dev_busy) || !wdd)
+		return -ENODEV;
+
+	/* We can only unregister the watchdog device that was registered */
+	if (watchdog != wdd) {
+		pr_err("%s: watchdog was not registered as /dev/watchdog.\n",
+			watchdog->info->identity);
+		return -ENODEV;
+	}
+
+	misc_deregister(&watchdog_miscdev);
+	wdd = NULL;
+	clear_bit(0, &watchdog_dev_busy);
+	return 0;
+}
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h
new file mode 100644
index 0000000..bc7612b
--- /dev/null
+++ b/drivers/watchdog/watchdog_dev.h
@@ -0,0 +1,33 @@
+/*
+ *	watchdog_core.h
+ *
+ *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ *						All Rights Reserved.
+ *
+ *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ *	This source code is part of the generic code that can be used
+ *	by all the watchdog timer drivers.
+ *
+ *	Based on source code of the following authors:
+ *	  Matt Domsch <Matt_Domsch@dell.com>,
+ *	  Rob Radez <rob@osinvestor.com>,
+ *	  Rusty Lynch <rusty@linux.co.intel.com>
+ *	  Satyam Sharma <satyam@infradead.org>
+ *	  Randy Dunlap <randy.dunlap@oracle.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.
+ *
+ *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
+ *	admit liability nor provide warranty for any of this software.
+ *	This material is provided "AS-IS" and at no charge.
+ */
+
+/*
+ *	Functions/procedures to be called by the core
+ */
+int watchdog_dev_register(struct watchdog_device *);
+int watchdog_dev_unregister(struct watchdog_device *);
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index fd725cd..4f44b34 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -82,7 +82,7 @@
 static int get_free_entries(unsigned count)
 {
 	unsigned long flags;
-	int ref, rc;
+	int ref, rc = 0;
 	grant_ref_t head;
 
 	spin_lock_irqsave(&gnttab_list_lock, flags);
diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index 206c4ce0..978d2c6f 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -11,7 +11,6 @@
 #include <xen/xenbus.h>
 #include <xen/events.h>
 #include <asm/xen/pci.h>
-#include <linux/workqueue.h>
 #include "pciback.h"
 
 #define	DRV_NAME	"xen-pciback"
diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c
index 010937b..1b4afd8 100644
--- a/drivers/xen/xen-selfballoon.c
+++ b/drivers/xen/xen-selfballoon.c
@@ -70,10 +70,10 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
-
+#include <linux/workqueue.h>
 #include <xen/balloon.h>
-
 #include <xen/tmem.h>
+#include <xen/xen.h>
 
 /* Enable/disable with sysfs. */
 static int xen_selfballooning_enabled __read_mostly;
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index 4d433d3..f11e43e 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -187,7 +187,7 @@
  */
 static struct inode *anon_inode_mkinode(void)
 {
-	struct inode *inode = new_inode(anon_inode_mnt->mnt_sb);
+	struct inode *inode = new_inode_pseudo(anon_inode_mnt->mnt_sb);
 
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
diff --git a/fs/block_dev.c b/fs/block_dev.c
index c62fb84..f55aad4 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -44,24 +44,28 @@
 {
 	return &BDEV_I(inode)->bdev;
 }
-
 EXPORT_SYMBOL(I_BDEV);
 
 /*
- * move the inode from it's current bdi to the a new bdi. if the inode is dirty
- * we need to move it onto the dirty list of @dst so that the inode is always
- * on the right list.
+ * Move the inode from its current bdi to a new bdi. If the inode is dirty we
+ * need to move it onto the dirty list of @dst so that the inode is always on
+ * the right list.
  */
 static void bdev_inode_switch_bdi(struct inode *inode,
 			struct backing_dev_info *dst)
 {
-	spin_lock(&inode_wb_list_lock);
+	struct backing_dev_info *old = inode->i_data.backing_dev_info;
+
+	if (unlikely(dst == old))		/* deadlock avoidance */
+		return;
+	bdi_lock_two(&old->wb, &dst->wb);
 	spin_lock(&inode->i_lock);
 	inode->i_data.backing_dev_info = dst;
 	if (inode->i_state & I_DIRTY)
 		list_move(&inode->i_wb_list, &dst->wb.b_dirty);
 	spin_unlock(&inode->i_lock);
-	spin_unlock(&inode_wb_list_lock);
+	spin_unlock(&old->wb.list_lock);
+	spin_unlock(&dst->wb.list_lock);
 }
 
 static sector_t max_block(struct block_device *bdev)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 52d7eca..502b9e9 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -34,6 +34,9 @@
 	 */
 	struct btrfs_key location;
 
+	/* Lock for counters */
+	spinlock_t lock;
+
 	/* the extent_tree has caches of all the extent mappings to disk */
 	struct extent_map_tree extent_tree;
 
@@ -134,8 +137,8 @@
 	 * items we think we'll end up using, and reserved_extents is the number
 	 * of extent items we've reserved metadata for.
 	 */
-	atomic_t outstanding_extents;
-	atomic_t reserved_extents;
+	unsigned outstanding_extents;
+	unsigned reserved_extents;
 
 	/*
 	 * ordered_data_close is set by truncate when a file that used
@@ -184,4 +187,13 @@
 	BTRFS_I(inode)->disk_i_size = size;
 }
 
+static inline bool btrfs_is_free_space_inode(struct btrfs_root *root,
+				       struct inode *inode)
+{
+	if (root == root->fs_info->tree_root ||
+	    BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID)
+		return true;
+	return false;
+}
+
 #endif
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 2e66786..011cab3 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -54,8 +54,13 @@
 {
 	int i;
 	for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
-		if (p->nodes[i] && p->locks[i])
-			btrfs_set_lock_blocking(p->nodes[i]);
+		if (!p->nodes[i] || !p->locks[i])
+			continue;
+		btrfs_set_lock_blocking_rw(p->nodes[i], p->locks[i]);
+		if (p->locks[i] == BTRFS_READ_LOCK)
+			p->locks[i] = BTRFS_READ_LOCK_BLOCKING;
+		else if (p->locks[i] == BTRFS_WRITE_LOCK)
+			p->locks[i] = BTRFS_WRITE_LOCK_BLOCKING;
 	}
 }
 
@@ -68,7 +73,7 @@
  * for held
  */
 noinline void btrfs_clear_path_blocking(struct btrfs_path *p,
-					struct extent_buffer *held)
+					struct extent_buffer *held, int held_rw)
 {
 	int i;
 
@@ -79,19 +84,29 @@
 	 * really sure by forcing the path to blocking before we clear
 	 * the path blocking.
 	 */
-	if (held)
-		btrfs_set_lock_blocking(held);
+	if (held) {
+		btrfs_set_lock_blocking_rw(held, held_rw);
+		if (held_rw == BTRFS_WRITE_LOCK)
+			held_rw = BTRFS_WRITE_LOCK_BLOCKING;
+		else if (held_rw == BTRFS_READ_LOCK)
+			held_rw = BTRFS_READ_LOCK_BLOCKING;
+	}
 	btrfs_set_path_blocking(p);
 #endif
 
 	for (i = BTRFS_MAX_LEVEL - 1; i >= 0; i--) {
-		if (p->nodes[i] && p->locks[i])
-			btrfs_clear_lock_blocking(p->nodes[i]);
+		if (p->nodes[i] && p->locks[i]) {
+			btrfs_clear_lock_blocking_rw(p->nodes[i], p->locks[i]);
+			if (p->locks[i] == BTRFS_WRITE_LOCK_BLOCKING)
+				p->locks[i] = BTRFS_WRITE_LOCK;
+			else if (p->locks[i] == BTRFS_READ_LOCK_BLOCKING)
+				p->locks[i] = BTRFS_READ_LOCK;
+		}
 	}
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	if (held)
-		btrfs_clear_lock_blocking(held);
+		btrfs_clear_lock_blocking_rw(held, held_rw);
 #endif
 }
 
@@ -119,7 +134,7 @@
 		if (!p->nodes[i])
 			continue;
 		if (p->locks[i]) {
-			btrfs_tree_unlock(p->nodes[i]);
+			btrfs_tree_unlock_rw(p->nodes[i], p->locks[i]);
 			p->locks[i] = 0;
 		}
 		free_extent_buffer(p->nodes[i]);
@@ -167,6 +182,25 @@
 	return eb;
 }
 
+/* loop around taking references on and locking the root node of the
+ * tree until you end up with a lock on the root.  A locked buffer
+ * is returned, with a reference held.
+ */
+struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
+{
+	struct extent_buffer *eb;
+
+	while (1) {
+		eb = btrfs_root_node(root);
+		btrfs_tree_read_lock(eb);
+		if (eb == root->node)
+			break;
+		btrfs_tree_read_unlock(eb);
+		free_extent_buffer(eb);
+	}
+	return eb;
+}
+
 /* cowonly root (everything not a reference counted cow subvolume), just get
  * put onto a simple dirty list.  transaction.c walks this to make sure they
  * get properly updated on disk.
@@ -626,14 +660,6 @@
 	for (i = start_slot; i < end_slot; i++) {
 		int close = 1;
 
-		if (!parent->map_token) {
-			map_extent_buffer(parent,
-					btrfs_node_key_ptr_offset(i),
-					sizeof(struct btrfs_key_ptr),
-					&parent->map_token, &parent->kaddr,
-					&parent->map_start, &parent->map_len,
-					KM_USER1);
-		}
 		btrfs_node_key(parent, &disk_key, i);
 		if (!progress_passed && comp_keys(&disk_key, progress) < 0)
 			continue;
@@ -656,11 +682,6 @@
 			last_block = blocknr;
 			continue;
 		}
-		if (parent->map_token) {
-			unmap_extent_buffer(parent, parent->map_token,
-					    KM_USER1);
-			parent->map_token = NULL;
-		}
 
 		cur = btrfs_find_tree_block(root, blocknr, blocksize);
 		if (cur)
@@ -701,11 +722,6 @@
 		btrfs_tree_unlock(cur);
 		free_extent_buffer(cur);
 	}
-	if (parent->map_token) {
-		unmap_extent_buffer(parent, parent->map_token,
-				    KM_USER1);
-		parent->map_token = NULL;
-	}
 	return err;
 }
 
@@ -746,7 +762,6 @@
 	struct btrfs_disk_key *tmp = NULL;
 	struct btrfs_disk_key unaligned;
 	unsigned long offset;
-	char *map_token = NULL;
 	char *kaddr = NULL;
 	unsigned long map_start = 0;
 	unsigned long map_len = 0;
@@ -756,18 +771,13 @@
 		mid = (low + high) / 2;
 		offset = p + mid * item_size;
 
-		if (!map_token || offset < map_start ||
+		if (!kaddr || offset < map_start ||
 		    (offset + sizeof(struct btrfs_disk_key)) >
 		    map_start + map_len) {
-			if (map_token) {
-				unmap_extent_buffer(eb, map_token, KM_USER0);
-				map_token = NULL;
-			}
 
 			err = map_private_extent_buffer(eb, offset,
 						sizeof(struct btrfs_disk_key),
-						&map_token, &kaddr,
-						&map_start, &map_len, KM_USER0);
+						&kaddr, &map_start, &map_len);
 
 			if (!err) {
 				tmp = (struct btrfs_disk_key *)(kaddr + offset -
@@ -790,14 +800,10 @@
 			high = mid;
 		else {
 			*slot = mid;
-			if (map_token)
-				unmap_extent_buffer(eb, map_token, KM_USER0);
 			return 0;
 		}
 	}
 	*slot = low;
-	if (map_token)
-		unmap_extent_buffer(eb, map_token, KM_USER0);
 	return 1;
 }
 
@@ -890,7 +896,8 @@
 
 	mid = path->nodes[level];
 
-	WARN_ON(!path->locks[level]);
+	WARN_ON(path->locks[level] != BTRFS_WRITE_LOCK &&
+		path->locks[level] != BTRFS_WRITE_LOCK_BLOCKING);
 	WARN_ON(btrfs_header_generation(mid) != trans->transid);
 
 	orig_ptr = btrfs_node_blockptr(mid, orig_slot);
@@ -1228,7 +1235,6 @@
 	u32 nr;
 	u32 blocksize;
 	u32 nscan = 0;
-	bool map = true;
 
 	if (level != 1)
 		return;
@@ -1250,19 +1256,8 @@
 
 	nritems = btrfs_header_nritems(node);
 	nr = slot;
-	if (node->map_token || path->skip_locking)
-		map = false;
 
 	while (1) {
-		if (map && !node->map_token) {
-			unsigned long offset = btrfs_node_key_ptr_offset(nr);
-			map_private_extent_buffer(node, offset,
-						  sizeof(struct btrfs_key_ptr),
-						  &node->map_token,
-						  &node->kaddr,
-						  &node->map_start,
-						  &node->map_len, KM_USER1);
-		}
 		if (direction < 0) {
 			if (nr == 0)
 				break;
@@ -1281,11 +1276,6 @@
 		if ((search <= target && target - search <= 65536) ||
 		    (search > target && search - target <= 65536)) {
 			gen = btrfs_node_ptr_generation(node, nr);
-			if (map && node->map_token) {
-				unmap_extent_buffer(node, node->map_token,
-						    KM_USER1);
-				node->map_token = NULL;
-			}
 			readahead_tree_block(root, search, blocksize, gen);
 			nread += blocksize;
 		}
@@ -1293,10 +1283,6 @@
 		if ((nread > 65536 || nscan > 32))
 			break;
 	}
-	if (map && node->map_token) {
-		unmap_extent_buffer(node, node->map_token, KM_USER1);
-		node->map_token = NULL;
-	}
 }
 
 /*
@@ -1409,7 +1395,7 @@
 
 		t = path->nodes[i];
 		if (i >= lowest_unlock && i > skip_level && path->locks[i]) {
-			btrfs_tree_unlock(t);
+			btrfs_tree_unlock_rw(t, path->locks[i]);
 			path->locks[i] = 0;
 		}
 	}
@@ -1436,7 +1422,7 @@
 			continue;
 		if (!path->locks[i])
 			continue;
-		btrfs_tree_unlock(path->nodes[i]);
+		btrfs_tree_unlock_rw(path->nodes[i], path->locks[i]);
 		path->locks[i] = 0;
 	}
 }
@@ -1485,6 +1471,8 @@
 			 * we can trust our generation number
 			 */
 			free_extent_buffer(tmp);
+			btrfs_set_path_blocking(p);
+
 			tmp = read_tree_block(root, blocknr, blocksize, gen);
 			if (tmp && btrfs_buffer_uptodate(tmp, gen)) {
 				*eb_ret = tmp;
@@ -1540,20 +1528,27 @@
 static int
 setup_nodes_for_search(struct btrfs_trans_handle *trans,
 		       struct btrfs_root *root, struct btrfs_path *p,
-		       struct extent_buffer *b, int level, int ins_len)
+		       struct extent_buffer *b, int level, int ins_len,
+		       int *write_lock_level)
 {
 	int ret;
 	if ((p->search_for_split || ins_len > 0) && btrfs_header_nritems(b) >=
 	    BTRFS_NODEPTRS_PER_BLOCK(root) - 3) {
 		int sret;
 
+		if (*write_lock_level < level + 1) {
+			*write_lock_level = level + 1;
+			btrfs_release_path(p);
+			goto again;
+		}
+
 		sret = reada_for_balance(root, p, level);
 		if (sret)
 			goto again;
 
 		btrfs_set_path_blocking(p);
 		sret = split_node(trans, root, p, level);
-		btrfs_clear_path_blocking(p, NULL);
+		btrfs_clear_path_blocking(p, NULL, 0);
 
 		BUG_ON(sret > 0);
 		if (sret) {
@@ -1565,13 +1560,19 @@
 		   BTRFS_NODEPTRS_PER_BLOCK(root) / 2) {
 		int sret;
 
+		if (*write_lock_level < level + 1) {
+			*write_lock_level = level + 1;
+			btrfs_release_path(p);
+			goto again;
+		}
+
 		sret = reada_for_balance(root, p, level);
 		if (sret)
 			goto again;
 
 		btrfs_set_path_blocking(p);
 		sret = balance_level(trans, root, p, level);
-		btrfs_clear_path_blocking(p, NULL);
+		btrfs_clear_path_blocking(p, NULL, 0);
 
 		if (sret) {
 			ret = sret;
@@ -1615,27 +1616,78 @@
 	int err;
 	int level;
 	int lowest_unlock = 1;
+	int root_lock;
+	/* everything at write_lock_level or lower must be write locked */
+	int write_lock_level = 0;
 	u8 lowest_level = 0;
 
 	lowest_level = p->lowest_level;
 	WARN_ON(lowest_level && ins_len > 0);
 	WARN_ON(p->nodes[0] != NULL);
 
-	if (ins_len < 0)
+	if (ins_len < 0) {
 		lowest_unlock = 2;
 
+		/* when we are removing items, we might have to go up to level
+		 * two as we update tree pointers  Make sure we keep write
+		 * for those levels as well
+		 */
+		write_lock_level = 2;
+	} else if (ins_len > 0) {
+		/*
+		 * for inserting items, make sure we have a write lock on
+		 * level 1 so we can update keys
+		 */
+		write_lock_level = 1;
+	}
+
+	if (!cow)
+		write_lock_level = -1;
+
+	if (cow && (p->keep_locks || p->lowest_level))
+		write_lock_level = BTRFS_MAX_LEVEL;
+
 again:
+	/*
+	 * we try very hard to do read locks on the root
+	 */
+	root_lock = BTRFS_READ_LOCK;
+	level = 0;
 	if (p->search_commit_root) {
+		/*
+		 * the commit roots are read only
+		 * so we always do read locks
+		 */
 		b = root->commit_root;
 		extent_buffer_get(b);
+		level = btrfs_header_level(b);
 		if (!p->skip_locking)
-			btrfs_tree_lock(b);
+			btrfs_tree_read_lock(b);
 	} else {
-		if (p->skip_locking)
+		if (p->skip_locking) {
 			b = btrfs_root_node(root);
-		else
-			b = btrfs_lock_root_node(root);
+			level = btrfs_header_level(b);
+		} else {
+			/* we don't know the level of the root node
+			 * until we actually have it read locked
+			 */
+			b = btrfs_read_lock_root_node(root);
+			level = btrfs_header_level(b);
+			if (level <= write_lock_level) {
+				/* whoops, must trade for write lock */
+				btrfs_tree_read_unlock(b);
+				free_extent_buffer(b);
+				b = btrfs_lock_root_node(root);
+				root_lock = BTRFS_WRITE_LOCK;
+
+				/* the level might have changed, check again */
+				level = btrfs_header_level(b);
+			}
+		}
 	}
+	p->nodes[level] = b;
+	if (!p->skip_locking)
+		p->locks[level] = root_lock;
 
 	while (b) {
 		level = btrfs_header_level(b);
@@ -1644,10 +1696,6 @@
 		 * setup the path here so we can release it under lock
 		 * contention with the cow code
 		 */
-		p->nodes[level] = b;
-		if (!p->skip_locking)
-			p->locks[level] = 1;
-
 		if (cow) {
 			/*
 			 * if we don't really need to cow this block
@@ -1659,6 +1707,16 @@
 
 			btrfs_set_path_blocking(p);
 
+			/*
+			 * must have write locks on this node and the
+			 * parent
+			 */
+			if (level + 1 > write_lock_level) {
+				write_lock_level = level + 1;
+				btrfs_release_path(p);
+				goto again;
+			}
+
 			err = btrfs_cow_block(trans, root, b,
 					      p->nodes[level + 1],
 					      p->slots[level + 1], &b);
@@ -1671,10 +1729,7 @@
 		BUG_ON(!cow && ins_len);
 
 		p->nodes[level] = b;
-		if (!p->skip_locking)
-			p->locks[level] = 1;
-
-		btrfs_clear_path_blocking(p, NULL);
+		btrfs_clear_path_blocking(p, NULL, 0);
 
 		/*
 		 * we have a lock on b and as long as we aren't changing
@@ -1700,7 +1755,7 @@
 			}
 			p->slots[level] = slot;
 			err = setup_nodes_for_search(trans, root, p, b, level,
-						     ins_len);
+					     ins_len, &write_lock_level);
 			if (err == -EAGAIN)
 				goto again;
 			if (err) {
@@ -1710,6 +1765,19 @@
 			b = p->nodes[level];
 			slot = p->slots[level];
 
+			/*
+			 * slot 0 is special, if we change the key
+			 * we have to update the parent pointer
+			 * which means we must have a write lock
+			 * on the parent
+			 */
+			if (slot == 0 && cow &&
+			    write_lock_level < level + 1) {
+				write_lock_level = level + 1;
+				btrfs_release_path(p);
+				goto again;
+			}
+
 			unlock_up(p, level, lowest_unlock);
 
 			if (level == lowest_level) {
@@ -1728,23 +1796,42 @@
 			}
 
 			if (!p->skip_locking) {
-				btrfs_clear_path_blocking(p, NULL);
-				err = btrfs_try_spin_lock(b);
-
-				if (!err) {
-					btrfs_set_path_blocking(p);
-					btrfs_tree_lock(b);
-					btrfs_clear_path_blocking(p, b);
+				level = btrfs_header_level(b);
+				if (level <= write_lock_level) {
+					err = btrfs_try_tree_write_lock(b);
+					if (!err) {
+						btrfs_set_path_blocking(p);
+						btrfs_tree_lock(b);
+						btrfs_clear_path_blocking(p, b,
+								  BTRFS_WRITE_LOCK);
+					}
+					p->locks[level] = BTRFS_WRITE_LOCK;
+				} else {
+					err = btrfs_try_tree_read_lock(b);
+					if (!err) {
+						btrfs_set_path_blocking(p);
+						btrfs_tree_read_lock(b);
+						btrfs_clear_path_blocking(p, b,
+								  BTRFS_READ_LOCK);
+					}
+					p->locks[level] = BTRFS_READ_LOCK;
 				}
+				p->nodes[level] = b;
 			}
 		} else {
 			p->slots[level] = slot;
 			if (ins_len > 0 &&
 			    btrfs_leaf_free_space(root, b) < ins_len) {
+				if (write_lock_level < 1) {
+					write_lock_level = 1;
+					btrfs_release_path(p);
+					goto again;
+				}
+
 				btrfs_set_path_blocking(p);
 				err = split_leaf(trans, root, key,
 						 p, ins_len, ret == 0);
-				btrfs_clear_path_blocking(p, NULL);
+				btrfs_clear_path_blocking(p, NULL, 0);
 
 				BUG_ON(err > 0);
 				if (err) {
@@ -2025,7 +2112,7 @@
 	add_root_to_dirty_list(root);
 	extent_buffer_get(c);
 	path->nodes[level] = c;
-	path->locks[level] = 1;
+	path->locks[level] = BTRFS_WRITE_LOCK;
 	path->slots[level] = 0;
 	return 0;
 }
@@ -2253,14 +2340,6 @@
 		if (path->slots[0] == i)
 			push_space += data_size;
 
-		if (!left->map_token) {
-			map_extent_buffer(left, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&left->map_token, &left->kaddr,
-					&left->map_start, &left->map_len,
-					KM_USER1);
-		}
-
 		this_item_size = btrfs_item_size(left, item);
 		if (this_item_size + sizeof(*item) + push_space > free_space)
 			break;
@@ -2271,10 +2350,6 @@
 			break;
 		i--;
 	}
-	if (left->map_token) {
-		unmap_extent_buffer(left, left->map_token, KM_USER1);
-		left->map_token = NULL;
-	}
 
 	if (push_items == 0)
 		goto out_unlock;
@@ -2316,21 +2391,10 @@
 	push_space = BTRFS_LEAF_DATA_SIZE(root);
 	for (i = 0; i < right_nritems; i++) {
 		item = btrfs_item_nr(right, i);
-		if (!right->map_token) {
-			map_extent_buffer(right, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&right->map_token, &right->kaddr,
-					&right->map_start, &right->map_len,
-					KM_USER1);
-		}
 		push_space -= btrfs_item_size(right, item);
 		btrfs_set_item_offset(right, item, push_space);
 	}
 
-	if (right->map_token) {
-		unmap_extent_buffer(right, right->map_token, KM_USER1);
-		right->map_token = NULL;
-	}
 	left_nritems -= push_items;
 	btrfs_set_header_nritems(left, left_nritems);
 
@@ -2467,13 +2531,6 @@
 
 	for (i = 0; i < nr; i++) {
 		item = btrfs_item_nr(right, i);
-		if (!right->map_token) {
-			map_extent_buffer(right, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&right->map_token, &right->kaddr,
-					&right->map_start, &right->map_len,
-					KM_USER1);
-		}
 
 		if (!empty && push_items > 0) {
 			if (path->slots[0] < i)
@@ -2496,11 +2553,6 @@
 		push_space += this_item_size + sizeof(*item);
 	}
 
-	if (right->map_token) {
-		unmap_extent_buffer(right, right->map_token, KM_USER1);
-		right->map_token = NULL;
-	}
-
 	if (push_items == 0) {
 		ret = 1;
 		goto out;
@@ -2530,23 +2582,12 @@
 		u32 ioff;
 
 		item = btrfs_item_nr(left, i);
-		if (!left->map_token) {
-			map_extent_buffer(left, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&left->map_token, &left->kaddr,
-					&left->map_start, &left->map_len,
-					KM_USER1);
-		}
 
 		ioff = btrfs_item_offset(left, item);
 		btrfs_set_item_offset(left, item,
 		      ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size));
 	}
 	btrfs_set_header_nritems(left, old_left_nritems + push_items);
-	if (left->map_token) {
-		unmap_extent_buffer(left, left->map_token, KM_USER1);
-		left->map_token = NULL;
-	}
 
 	/* fixup right node */
 	if (push_items > right_nritems) {
@@ -2574,21 +2615,9 @@
 	for (i = 0; i < right_nritems; i++) {
 		item = btrfs_item_nr(right, i);
 
-		if (!right->map_token) {
-			map_extent_buffer(right, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&right->map_token, &right->kaddr,
-					&right->map_start, &right->map_len,
-					KM_USER1);
-		}
-
 		push_space = push_space - btrfs_item_size(right, item);
 		btrfs_set_item_offset(right, item, push_space);
 	}
-	if (right->map_token) {
-		unmap_extent_buffer(right, right->map_token, KM_USER1);
-		right->map_token = NULL;
-	}
 
 	btrfs_mark_buffer_dirty(left);
 	if (right_nritems)
@@ -2729,23 +2758,10 @@
 		struct btrfs_item *item = btrfs_item_nr(right, i);
 		u32 ioff;
 
-		if (!right->map_token) {
-			map_extent_buffer(right, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&right->map_token, &right->kaddr,
-					&right->map_start, &right->map_len,
-					KM_USER1);
-		}
-
 		ioff = btrfs_item_offset(right, item);
 		btrfs_set_item_offset(right, item, ioff + rt_data_off);
 	}
 
-	if (right->map_token) {
-		unmap_extent_buffer(right, right->map_token, KM_USER1);
-		right->map_token = NULL;
-	}
-
 	btrfs_set_header_nritems(l, mid);
 	ret = 0;
 	btrfs_item_key(right, &disk_key, 0);
@@ -3264,23 +3280,10 @@
 		u32 ioff;
 		item = btrfs_item_nr(leaf, i);
 
-		if (!leaf->map_token) {
-			map_extent_buffer(leaf, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&leaf->map_token, &leaf->kaddr,
-					&leaf->map_start, &leaf->map_len,
-					KM_USER1);
-		}
-
 		ioff = btrfs_item_offset(leaf, item);
 		btrfs_set_item_offset(leaf, item, ioff + size_diff);
 	}
 
-	if (leaf->map_token) {
-		unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-		leaf->map_token = NULL;
-	}
-
 	/* shift the data */
 	if (from_end) {
 		memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
@@ -3377,22 +3380,10 @@
 		u32 ioff;
 		item = btrfs_item_nr(leaf, i);
 
-		if (!leaf->map_token) {
-			map_extent_buffer(leaf, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&leaf->map_token, &leaf->kaddr,
-					&leaf->map_start, &leaf->map_len,
-					KM_USER1);
-		}
 		ioff = btrfs_item_offset(leaf, item);
 		btrfs_set_item_offset(leaf, item, ioff - data_size);
 	}
 
-	if (leaf->map_token) {
-		unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-		leaf->map_token = NULL;
-	}
-
 	/* shift the data */
 	memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
 		      data_end - data_size, btrfs_leaf_data(leaf) +
@@ -3494,27 +3485,13 @@
 		 * item0..itemN ... dataN.offset..dataN.size .. data0.size
 		 */
 		/* first correct the data pointers */
-		WARN_ON(leaf->map_token);
 		for (i = slot; i < nritems; i++) {
 			u32 ioff;
 
 			item = btrfs_item_nr(leaf, i);
-			if (!leaf->map_token) {
-				map_extent_buffer(leaf, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&leaf->map_token, &leaf->kaddr,
-					&leaf->map_start, &leaf->map_len,
-					KM_USER1);
-			}
-
 			ioff = btrfs_item_offset(leaf, item);
 			btrfs_set_item_offset(leaf, item, ioff - total_data);
 		}
-		if (leaf->map_token) {
-			unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-			leaf->map_token = NULL;
-		}
-
 		/* shift the items */
 		memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr),
 			      btrfs_item_nr_offset(slot),
@@ -3608,27 +3585,13 @@
 		 * item0..itemN ... dataN.offset..dataN.size .. data0.size
 		 */
 		/* first correct the data pointers */
-		WARN_ON(leaf->map_token);
 		for (i = slot; i < nritems; i++) {
 			u32 ioff;
 
 			item = btrfs_item_nr(leaf, i);
-			if (!leaf->map_token) {
-				map_extent_buffer(leaf, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&leaf->map_token, &leaf->kaddr,
-					&leaf->map_start, &leaf->map_len,
-					KM_USER1);
-			}
-
 			ioff = btrfs_item_offset(leaf, item);
 			btrfs_set_item_offset(leaf, item, ioff - total_data);
 		}
-		if (leaf->map_token) {
-			unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-			leaf->map_token = NULL;
-		}
-
 		/* shift the items */
 		memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr),
 			      btrfs_item_nr_offset(slot),
@@ -3840,22 +3803,10 @@
 			u32 ioff;
 
 			item = btrfs_item_nr(leaf, i);
-			if (!leaf->map_token) {
-				map_extent_buffer(leaf, (unsigned long)item,
-					sizeof(struct btrfs_item),
-					&leaf->map_token, &leaf->kaddr,
-					&leaf->map_start, &leaf->map_len,
-					KM_USER1);
-			}
 			ioff = btrfs_item_offset(leaf, item);
 			btrfs_set_item_offset(leaf, item, ioff + dsize);
 		}
 
-		if (leaf->map_token) {
-			unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-			leaf->map_token = NULL;
-		}
-
 		memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot),
 			      btrfs_item_nr_offset(slot + nr),
 			      sizeof(struct btrfs_item) *
@@ -4004,11 +3955,11 @@
 
 	WARN_ON(!path->keep_locks);
 again:
-	cur = btrfs_lock_root_node(root);
+	cur = btrfs_read_lock_root_node(root);
 	level = btrfs_header_level(cur);
 	WARN_ON(path->nodes[level]);
 	path->nodes[level] = cur;
-	path->locks[level] = 1;
+	path->locks[level] = BTRFS_READ_LOCK;
 
 	if (btrfs_header_generation(cur) < min_trans) {
 		ret = 1;
@@ -4098,12 +4049,12 @@
 		cur = read_node_slot(root, cur, slot);
 		BUG_ON(!cur);
 
-		btrfs_tree_lock(cur);
+		btrfs_tree_read_lock(cur);
 
-		path->locks[level - 1] = 1;
+		path->locks[level - 1] = BTRFS_READ_LOCK;
 		path->nodes[level - 1] = cur;
 		unlock_up(path, level, 1);
-		btrfs_clear_path_blocking(path, NULL);
+		btrfs_clear_path_blocking(path, NULL, 0);
 	}
 out:
 	if (ret == 0)
@@ -4218,30 +4169,21 @@
 	u32 nritems;
 	int ret;
 	int old_spinning = path->leave_spinning;
-	int force_blocking = 0;
+	int next_rw_lock = 0;
 
 	nritems = btrfs_header_nritems(path->nodes[0]);
 	if (nritems == 0)
 		return 1;
 
-	/*
-	 * we take the blocks in an order that upsets lockdep.  Using
-	 * blocking mode is the only way around it.
-	 */
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	force_blocking = 1;
-#endif
-
 	btrfs_item_key_to_cpu(path->nodes[0], &key, nritems - 1);
 again:
 	level = 1;
 	next = NULL;
+	next_rw_lock = 0;
 	btrfs_release_path(path);
 
 	path->keep_locks = 1;
-
-	if (!force_blocking)
-		path->leave_spinning = 1;
+	path->leave_spinning = 1;
 
 	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
 	path->keep_locks = 0;
@@ -4281,11 +4223,12 @@
 		}
 
 		if (next) {
-			btrfs_tree_unlock(next);
+			btrfs_tree_unlock_rw(next, next_rw_lock);
 			free_extent_buffer(next);
 		}
 
 		next = c;
+		next_rw_lock = path->locks[level];
 		ret = read_block_for_search(NULL, root, path, &next, level,
 					    slot, &key);
 		if (ret == -EAGAIN)
@@ -4297,15 +4240,14 @@
 		}
 
 		if (!path->skip_locking) {
-			ret = btrfs_try_spin_lock(next);
+			ret = btrfs_try_tree_read_lock(next);
 			if (!ret) {
 				btrfs_set_path_blocking(path);
-				btrfs_tree_lock(next);
-				if (!force_blocking)
-					btrfs_clear_path_blocking(path, next);
+				btrfs_tree_read_lock(next);
+				btrfs_clear_path_blocking(path, next,
+							  BTRFS_READ_LOCK);
 			}
-			if (force_blocking)
-				btrfs_set_lock_blocking(next);
+			next_rw_lock = BTRFS_READ_LOCK;
 		}
 		break;
 	}
@@ -4314,14 +4256,13 @@
 		level--;
 		c = path->nodes[level];
 		if (path->locks[level])
-			btrfs_tree_unlock(c);
+			btrfs_tree_unlock_rw(c, path->locks[level]);
 
 		free_extent_buffer(c);
 		path->nodes[level] = next;
 		path->slots[level] = 0;
 		if (!path->skip_locking)
-			path->locks[level] = 1;
-
+			path->locks[level] = next_rw_lock;
 		if (!level)
 			break;
 
@@ -4336,16 +4277,14 @@
 		}
 
 		if (!path->skip_locking) {
-			btrfs_assert_tree_locked(path->nodes[level]);
-			ret = btrfs_try_spin_lock(next);
+			ret = btrfs_try_tree_read_lock(next);
 			if (!ret) {
 				btrfs_set_path_blocking(path);
-				btrfs_tree_lock(next);
-				if (!force_blocking)
-					btrfs_clear_path_blocking(path, next);
+				btrfs_tree_read_lock(next);
+				btrfs_clear_path_blocking(path, next,
+							  BTRFS_READ_LOCK);
 			}
-			if (force_blocking)
-				btrfs_set_lock_blocking(next);
+			next_rw_lock = BTRFS_READ_LOCK;
 		}
 	}
 	ret = 0;
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index fe9287b..365c4e1 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -755,6 +755,8 @@
 				   chunks for this space */
 	unsigned int chunk_alloc:1;	/* set if we are allocating a chunk */
 
+	unsigned int flush:1;		/* set if we are trying to make space */
+
 	unsigned int force_alloc;	/* set if we need to force a chunk
 					   alloc for this space */
 
@@ -764,7 +766,7 @@
 	struct list_head block_groups[BTRFS_NR_RAID_TYPES];
 	spinlock_t lock;
 	struct rw_semaphore groups_sem;
-	atomic_t caching_threads;
+	wait_queue_head_t wait;
 };
 
 struct btrfs_block_rsv {
@@ -824,6 +826,7 @@
 	struct list_head list;
 	struct mutex mutex;
 	wait_queue_head_t wait;
+	struct btrfs_work work;
 	struct btrfs_block_group_cache *block_group;
 	u64 progress;
 	atomic_t count;
@@ -1032,6 +1035,8 @@
 	struct btrfs_workers endio_write_workers;
 	struct btrfs_workers endio_freespace_worker;
 	struct btrfs_workers submit_workers;
+	struct btrfs_workers caching_workers;
+
 	/*
 	 * fixup workers take dirty pages that didn't properly go through
 	 * the cow mechanism and make them safe to write.  It happens
@@ -2128,7 +2133,7 @@
 
 /* extent-tree.c */
 static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
-						 int num_items)
+						 unsigned num_items)
 {
 	return (root->leafsize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
 		3 * num_items;
@@ -2222,9 +2227,6 @@
 void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
 int btrfs_check_data_free_space(struct inode *inode, u64 bytes);
 void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes);
-int btrfs_trans_reserve_metadata(struct btrfs_trans_handle *trans,
-				struct btrfs_root *root,
-				int num_items);
 void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
 				struct btrfs_root *root);
 int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
@@ -2330,7 +2332,7 @@
 void btrfs_free_path(struct btrfs_path *p);
 void btrfs_set_path_blocking(struct btrfs_path *p);
 void btrfs_clear_path_blocking(struct btrfs_path *p,
-			       struct extent_buffer *held);
+			       struct extent_buffer *held, int held_rw);
 void btrfs_unlock_up_safe(struct btrfs_path *p, int level);
 
 int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 98c68e6..b52c672 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -735,7 +735,7 @@
 	}
 
 	/* reset all the locked nodes in the patch to spinning locks. */
-	btrfs_clear_path_blocking(path, NULL);
+	btrfs_clear_path_blocking(path, NULL, 0);
 
 	/* insert the keys of the items */
 	ret = setup_items_for_insert(trans, root, path, keys, data_size,
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index 8d27af4..7083d08 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -25,7 +25,7 @@
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "ctree.h"
 
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 685f259..c360a84 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -89,13 +89,8 @@
 	data_size = sizeof(*dir_item) + name_len + data_len;
 	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
 					name, name_len);
-	/*
-	 * FIXME: at some point we should handle xattr's that are larger than
-	 * what we can fit in our leaf.  We set location to NULL b/c we arent
-	 * pointing at anything else, that will change if we store the xattr
-	 * data in a separate inode.
-	 */
-	BUG_ON(IS_ERR(dir_item));
+	if (IS_ERR(dir_item))
+		return PTR_ERR(dir_item);
 	memset(&location, 0, sizeof(location));
 
 	leaf = path->nodes[0];
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index b231ae1..07b3ac6 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -100,38 +100,83 @@
 	struct btrfs_work work;
 };
 
-/* These are used to set the lockdep class on the extent buffer locks.
- * The class is set by the readpage_end_io_hook after the buffer has
- * passed csum validation but before the pages are unlocked.
+/*
+ * Lockdep class keys for extent_buffer->lock's in this root.  For a given
+ * eb, the lockdep key is determined by the btrfs_root it belongs to and
+ * the level the eb occupies in the tree.
  *
- * The lockdep class is also set by btrfs_init_new_buffer on freshly
- * allocated blocks.
+ * Different roots are used for different purposes and may nest inside each
+ * other and they require separate keysets.  As lockdep keys should be
+ * static, assign keysets according to the purpose of the root as indicated
+ * by btrfs_root->objectid.  This ensures that all special purpose roots
+ * have separate keysets.
  *
- * The class is based on the level in the tree block, which allows lockdep
- * to know that lower nodes nest inside the locks of higher nodes.
+ * Lock-nesting across peer nodes is always done with the immediate parent
+ * node locked thus preventing deadlock.  As lockdep doesn't know this, use
+ * subclass to avoid triggering lockdep warning in such cases.
  *
- * We also add a check to make sure the highest level of the tree is
- * the same as our lockdep setup here.  If BTRFS_MAX_LEVEL changes, this
- * code needs update as well.
+ * The key is set by the readpage_end_io_hook after the buffer has passed
+ * csum validation but before the pages are unlocked.  It is also set by
+ * btrfs_init_new_buffer on freshly allocated blocks.
+ *
+ * We also add a check to make sure the highest level of the tree is the
+ * same as our lockdep setup here.  If BTRFS_MAX_LEVEL changes, this code
+ * needs update as well.
  */
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 # if BTRFS_MAX_LEVEL != 8
 #  error
 # endif
-static struct lock_class_key btrfs_eb_class[BTRFS_MAX_LEVEL + 1];
-static const char *btrfs_eb_name[BTRFS_MAX_LEVEL + 1] = {
-	/* leaf */
-	"btrfs-extent-00",
-	"btrfs-extent-01",
-	"btrfs-extent-02",
-	"btrfs-extent-03",
-	"btrfs-extent-04",
-	"btrfs-extent-05",
-	"btrfs-extent-06",
-	"btrfs-extent-07",
-	/* highest possible level */
-	"btrfs-extent-08",
+
+static struct btrfs_lockdep_keyset {
+	u64			id;		/* root objectid */
+	const char		*name_stem;	/* lock name stem */
+	char			names[BTRFS_MAX_LEVEL + 1][20];
+	struct lock_class_key	keys[BTRFS_MAX_LEVEL + 1];
+} btrfs_lockdep_keysets[] = {
+	{ .id = BTRFS_ROOT_TREE_OBJECTID,	.name_stem = "root"	},
+	{ .id = BTRFS_EXTENT_TREE_OBJECTID,	.name_stem = "extent"	},
+	{ .id = BTRFS_CHUNK_TREE_OBJECTID,	.name_stem = "chunk"	},
+	{ .id = BTRFS_DEV_TREE_OBJECTID,	.name_stem = "dev"	},
+	{ .id = BTRFS_FS_TREE_OBJECTID,		.name_stem = "fs"	},
+	{ .id = BTRFS_CSUM_TREE_OBJECTID,	.name_stem = "csum"	},
+	{ .id = BTRFS_ORPHAN_OBJECTID,		.name_stem = "orphan"	},
+	{ .id = BTRFS_TREE_LOG_OBJECTID,	.name_stem = "log"	},
+	{ .id = BTRFS_TREE_RELOC_OBJECTID,	.name_stem = "treloc"	},
+	{ .id = BTRFS_DATA_RELOC_TREE_OBJECTID,	.name_stem = "dreloc"	},
+	{ .id = 0,				.name_stem = "tree"	},
 };
+
+void __init btrfs_init_lockdep(void)
+{
+	int i, j;
+
+	/* initialize lockdep class names */
+	for (i = 0; i < ARRAY_SIZE(btrfs_lockdep_keysets); i++) {
+		struct btrfs_lockdep_keyset *ks = &btrfs_lockdep_keysets[i];
+
+		for (j = 0; j < ARRAY_SIZE(ks->names); j++)
+			snprintf(ks->names[j], sizeof(ks->names[j]),
+				 "btrfs-%s-%02d", ks->name_stem, j);
+	}
+}
+
+void btrfs_set_buffer_lockdep_class(u64 objectid, struct extent_buffer *eb,
+				    int level)
+{
+	struct btrfs_lockdep_keyset *ks;
+
+	BUG_ON(level >= ARRAY_SIZE(ks->keys));
+
+	/* find the matching keyset, id 0 is the default entry */
+	for (ks = btrfs_lockdep_keysets; ks->id; ks++)
+		if (ks->id == objectid)
+			break;
+
+	lockdep_set_class_and_name(&eb->lock,
+				   &ks->keys[level], ks->names[level]);
+}
+
 #endif
 
 /*
@@ -217,7 +262,6 @@
 	unsigned long len;
 	unsigned long cur_len;
 	unsigned long offset = BTRFS_CSUM_SIZE;
-	char *map_token = NULL;
 	char *kaddr;
 	unsigned long map_start;
 	unsigned long map_len;
@@ -228,8 +272,7 @@
 	len = buf->len - offset;
 	while (len > 0) {
 		err = map_private_extent_buffer(buf, offset, 32,
-					&map_token, &kaddr,
-					&map_start, &map_len, KM_USER0);
+					&kaddr, &map_start, &map_len);
 		if (err)
 			return 1;
 		cur_len = min(len, map_len - (offset - map_start));
@@ -237,7 +280,6 @@
 				      crc, cur_len);
 		len -= cur_len;
 		offset += cur_len;
-		unmap_extent_buffer(buf, map_token, KM_USER0);
 	}
 	if (csum_size > sizeof(inline_result)) {
 		result = kzalloc(csum_size * sizeof(char), GFP_NOFS);
@@ -494,15 +536,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level)
-{
-	lockdep_set_class_and_name(&eb->lock,
-			   &btrfs_eb_class[level],
-			   btrfs_eb_name[level]);
-}
-#endif
-
 static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
 			       struct extent_state *state)
 {
@@ -553,7 +586,8 @@
 	}
 	found_level = btrfs_header_level(eb);
 
-	btrfs_set_buffer_lockdep_class(eb, found_level);
+	btrfs_set_buffer_lockdep_class(btrfs_header_owner(eb),
+				       eb, found_level);
 
 	ret = csum_tree_block(root, eb, 1);
 	if (ret) {
@@ -1598,7 +1632,7 @@
 		goto fail_bdi;
 	}
 
-	fs_info->btree_inode->i_mapping->flags &= ~__GFP_FS;
+	mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS);
 
 	INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_ATOMIC);
 	INIT_LIST_HEAD(&fs_info->trans_list);
@@ -1802,6 +1836,9 @@
 			   fs_info->thread_pool_size),
 			   &fs_info->generic_worker);
 
+	btrfs_init_workers(&fs_info->caching_workers, "cache",
+			   2, &fs_info->generic_worker);
+
 	/* a higher idle thresh on the submit workers makes it much more
 	 * likely that bios will be send down in a sane order to the
 	 * devices
@@ -1855,6 +1892,7 @@
 	btrfs_start_workers(&fs_info->endio_write_workers, 1);
 	btrfs_start_workers(&fs_info->endio_freespace_worker, 1);
 	btrfs_start_workers(&fs_info->delayed_workers, 1);
+	btrfs_start_workers(&fs_info->caching_workers, 1);
 
 	fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super);
 	fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages,
@@ -2112,6 +2150,7 @@
 	btrfs_stop_workers(&fs_info->endio_freespace_worker);
 	btrfs_stop_workers(&fs_info->submit_workers);
 	btrfs_stop_workers(&fs_info->delayed_workers);
+	btrfs_stop_workers(&fs_info->caching_workers);
 fail_alloc:
 	kfree(fs_info->delayed_root);
 fail_iput:
@@ -2577,6 +2616,7 @@
 	btrfs_stop_workers(&fs_info->endio_freespace_worker);
 	btrfs_stop_workers(&fs_info->submit_workers);
 	btrfs_stop_workers(&fs_info->delayed_workers);
+	btrfs_stop_workers(&fs_info->caching_workers);
 
 	btrfs_close_devices(fs_info->fs_devices);
 	btrfs_mapping_tree_free(&fs_info->mapping_tree);
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index a0b610a..bec3ea4 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -87,10 +87,14 @@
 
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
-void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level);
+void btrfs_init_lockdep(void);
+void btrfs_set_buffer_lockdep_class(u64 objectid,
+			            struct extent_buffer *eb, int level);
 #else
-static inline void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb,
-						 int level)
+static inline void btrfs_init_lockdep(void)
+{ }
+static inline void btrfs_set_buffer_lockdep_class(u64 objectid,
+					struct extent_buffer *eb, int level)
 {
 }
 #endif
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 71cd456..4d08ed7 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -320,12 +320,12 @@
 	return total_added;
 }
 
-static int caching_kthread(void *data)
+static noinline void caching_thread(struct btrfs_work *work)
 {
-	struct btrfs_block_group_cache *block_group = data;
-	struct btrfs_fs_info *fs_info = block_group->fs_info;
-	struct btrfs_caching_control *caching_ctl = block_group->caching_ctl;
-	struct btrfs_root *extent_root = fs_info->extent_root;
+	struct btrfs_block_group_cache *block_group;
+	struct btrfs_fs_info *fs_info;
+	struct btrfs_caching_control *caching_ctl;
+	struct btrfs_root *extent_root;
 	struct btrfs_path *path;
 	struct extent_buffer *leaf;
 	struct btrfs_key key;
@@ -334,9 +334,14 @@
 	u32 nritems;
 	int ret = 0;
 
+	caching_ctl = container_of(work, struct btrfs_caching_control, work);
+	block_group = caching_ctl->block_group;
+	fs_info = block_group->fs_info;
+	extent_root = fs_info->extent_root;
+
 	path = btrfs_alloc_path();
 	if (!path)
-		return -ENOMEM;
+		goto out;
 
 	last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET);
 
@@ -433,13 +438,11 @@
 	free_excluded_extents(extent_root, block_group);
 
 	mutex_unlock(&caching_ctl->mutex);
+out:
 	wake_up(&caching_ctl->wait);
 
 	put_caching_control(caching_ctl);
-	atomic_dec(&block_group->space_info->caching_threads);
 	btrfs_put_block_group(block_group);
-
-	return 0;
 }
 
 static int cache_block_group(struct btrfs_block_group_cache *cache,
@@ -449,7 +452,6 @@
 {
 	struct btrfs_fs_info *fs_info = cache->fs_info;
 	struct btrfs_caching_control *caching_ctl;
-	struct task_struct *tsk;
 	int ret = 0;
 
 	smp_mb();
@@ -501,6 +503,7 @@
 	caching_ctl->progress = cache->key.objectid;
 	/* one for caching kthread, one for caching block group list */
 	atomic_set(&caching_ctl->count, 2);
+	caching_ctl->work.func = caching_thread;
 
 	spin_lock(&cache->lock);
 	if (cache->cached != BTRFS_CACHE_NO) {
@@ -516,16 +519,9 @@
 	list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups);
 	up_write(&fs_info->extent_commit_sem);
 
-	atomic_inc(&cache->space_info->caching_threads);
 	btrfs_get_block_group(cache);
 
-	tsk = kthread_run(caching_kthread, cache, "btrfs-cache-%llu\n",
-			  cache->key.objectid);
-	if (IS_ERR(tsk)) {
-		ret = PTR_ERR(tsk);
-		printk(KERN_ERR "error running thread %d\n", ret);
-		BUG();
-	}
+	btrfs_queue_worker(&fs_info->caching_workers, &caching_ctl->work);
 
 	return ret;
 }
@@ -2932,9 +2928,10 @@
 	found->full = 0;
 	found->force_alloc = CHUNK_ALLOC_NO_FORCE;
 	found->chunk_alloc = 0;
+	found->flush = 0;
+	init_waitqueue_head(&found->wait);
 	*space_info = found;
 	list_add_rcu(&found->list, &info->space_info);
-	atomic_set(&found->caching_threads, 0);
 	return 0;
 }
 
@@ -3314,6 +3311,14 @@
 	if (reserved == 0)
 		return 0;
 
+	smp_mb();
+	if (root->fs_info->delalloc_bytes == 0) {
+		if (trans)
+			return 0;
+		btrfs_wait_ordered_extents(root, 0, 0);
+		return 0;
+	}
+
 	max_reclaim = min(reserved, to_reclaim);
 
 	while (loops < 1024) {
@@ -3356,6 +3361,8 @@
 		}
 
 	}
+	if (reclaimed >= to_reclaim && !trans)
+		btrfs_wait_ordered_extents(root, 0, 0);
 	return reclaimed >= to_reclaim;
 }
 
@@ -3380,15 +3387,36 @@
 	u64 num_bytes = orig_bytes;
 	int retries = 0;
 	int ret = 0;
-	bool reserved = false;
 	bool committed = false;
+	bool flushing = false;
 
 again:
-	ret = -ENOSPC;
-	if (reserved)
-		num_bytes = 0;
-
+	ret = 0;
 	spin_lock(&space_info->lock);
+	/*
+	 * We only want to wait if somebody other than us is flushing and we are
+	 * actually alloed to flush.
+	 */
+	while (flush && !flushing && space_info->flush) {
+		spin_unlock(&space_info->lock);
+		/*
+		 * If we have a trans handle we can't wait because the flusher
+		 * may have to commit the transaction, which would mean we would
+		 * deadlock since we are waiting for the flusher to finish, but
+		 * hold the current transaction open.
+		 */
+		if (trans)
+			return -EAGAIN;
+		ret = wait_event_interruptible(space_info->wait,
+					       !space_info->flush);
+		/* Must have been interrupted, return */
+		if (ret)
+			return -EINTR;
+
+		spin_lock(&space_info->lock);
+	}
+
+	ret = -ENOSPC;
 	unused = space_info->bytes_used + space_info->bytes_reserved +
 		 space_info->bytes_pinned + space_info->bytes_readonly +
 		 space_info->bytes_may_use;
@@ -3403,8 +3431,7 @@
 	if (unused <= space_info->total_bytes) {
 		unused = space_info->total_bytes - unused;
 		if (unused >= num_bytes) {
-			if (!reserved)
-				space_info->bytes_reserved += orig_bytes;
+			space_info->bytes_reserved += orig_bytes;
 			ret = 0;
 		} else {
 			/*
@@ -3429,17 +3456,14 @@
 	 * to reclaim space we can actually use it instead of somebody else
 	 * stealing it from us.
 	 */
-	if (ret && !reserved) {
-		space_info->bytes_reserved += orig_bytes;
-		reserved = true;
+	if (ret && flush) {
+		flushing = true;
+		space_info->flush = 1;
 	}
 
 	spin_unlock(&space_info->lock);
 
-	if (!ret)
-		return 0;
-
-	if (!flush)
+	if (!ret || !flush)
 		goto out;
 
 	/*
@@ -3447,11 +3471,11 @@
 	 * metadata until after the IO is completed.
 	 */
 	ret = shrink_delalloc(trans, root, num_bytes, 1);
-	if (ret > 0)
-		return 0;
-	else if (ret < 0)
+	if (ret < 0)
 		goto out;
 
+	ret = 0;
+
 	/*
 	 * So if we were overcommitted it's possible that somebody else flushed
 	 * out enough space and we simply didn't have enough space to reclaim,
@@ -3462,11 +3486,11 @@
 		goto again;
 	}
 
-	spin_lock(&space_info->lock);
 	/*
 	 * Not enough space to be reclaimed, don't bother committing the
 	 * transaction.
 	 */
+	spin_lock(&space_info->lock);
 	if (space_info->bytes_pinned < orig_bytes)
 		ret = -ENOSPC;
 	spin_unlock(&space_info->lock);
@@ -3474,10 +3498,13 @@
 		goto out;
 
 	ret = -EAGAIN;
-	if (trans || committed)
+	if (trans)
 		goto out;
 
 	ret = -ENOSPC;
+	if (committed)
+		goto out;
+
 	trans = btrfs_join_transaction(root);
 	if (IS_ERR(trans))
 		goto out;
@@ -3489,12 +3516,12 @@
 	}
 
 out:
-	if (reserved) {
+	if (flushing) {
 		spin_lock(&space_info->lock);
-		space_info->bytes_reserved -= orig_bytes;
+		space_info->flush = 0;
+		wake_up_all(&space_info->wait);
 		spin_unlock(&space_info->lock);
 	}
-
 	return ret;
 }
 
@@ -3704,7 +3731,6 @@
 	if (commit_trans) {
 		if (trans)
 			return -EAGAIN;
-
 		trans = btrfs_join_transaction(root);
 		BUG_ON(IS_ERR(trans));
 		ret = btrfs_commit_transaction(trans, root);
@@ -3874,26 +3900,6 @@
 	return 0;
 }
 
-int btrfs_trans_reserve_metadata(struct btrfs_trans_handle *trans,
-				 struct btrfs_root *root,
-				 int num_items)
-{
-	u64 num_bytes;
-	int ret;
-
-	if (num_items == 0 || root->fs_info->chunk_root == root)
-		return 0;
-
-	num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
-	ret = btrfs_block_rsv_add(trans, root, &root->fs_info->trans_block_rsv,
-				  num_bytes);
-	if (!ret) {
-		trans->bytes_reserved += num_bytes;
-		trans->block_rsv = &root->fs_info->trans_block_rsv;
-	}
-	return ret;
-}
-
 void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
 				  struct btrfs_root *root)
 {
@@ -3944,6 +3950,30 @@
 	return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
 }
 
+static unsigned drop_outstanding_extent(struct inode *inode)
+{
+	unsigned dropped_extents = 0;
+
+	spin_lock(&BTRFS_I(inode)->lock);
+	BUG_ON(!BTRFS_I(inode)->outstanding_extents);
+	BTRFS_I(inode)->outstanding_extents--;
+
+	/*
+	 * If we have more or the same amount of outsanding extents than we have
+	 * reserved then we need to leave the reserved extents count alone.
+	 */
+	if (BTRFS_I(inode)->outstanding_extents >=
+	    BTRFS_I(inode)->reserved_extents)
+		goto out;
+
+	dropped_extents = BTRFS_I(inode)->reserved_extents -
+		BTRFS_I(inode)->outstanding_extents;
+	BTRFS_I(inode)->reserved_extents -= dropped_extents;
+out:
+	spin_unlock(&BTRFS_I(inode)->lock);
+	return dropped_extents;
+}
+
 static u64 calc_csum_metadata_size(struct inode *inode, u64 num_bytes)
 {
 	return num_bytes >>= 3;
@@ -3953,9 +3983,8 @@
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct btrfs_block_rsv *block_rsv = &root->fs_info->delalloc_block_rsv;
-	u64 to_reserve;
-	int nr_extents;
-	int reserved_extents;
+	u64 to_reserve = 0;
+	unsigned nr_extents = 0;
 	int ret;
 
 	if (btrfs_transaction_in_commit(root->fs_info))
@@ -3963,66 +3992,49 @@
 
 	num_bytes = ALIGN(num_bytes, root->sectorsize);
 
-	nr_extents = atomic_read(&BTRFS_I(inode)->outstanding_extents) + 1;
-	reserved_extents = atomic_read(&BTRFS_I(inode)->reserved_extents);
+	spin_lock(&BTRFS_I(inode)->lock);
+	BTRFS_I(inode)->outstanding_extents++;
 
-	if (nr_extents > reserved_extents) {
-		nr_extents -= reserved_extents;
+	if (BTRFS_I(inode)->outstanding_extents >
+	    BTRFS_I(inode)->reserved_extents) {
+		nr_extents = BTRFS_I(inode)->outstanding_extents -
+			BTRFS_I(inode)->reserved_extents;
+		BTRFS_I(inode)->reserved_extents += nr_extents;
+
 		to_reserve = btrfs_calc_trans_metadata_size(root, nr_extents);
-	} else {
-		nr_extents = 0;
-		to_reserve = 0;
 	}
+	spin_unlock(&BTRFS_I(inode)->lock);
 
 	to_reserve += calc_csum_metadata_size(inode, num_bytes);
 	ret = reserve_metadata_bytes(NULL, root, block_rsv, to_reserve, 1);
-	if (ret)
+	if (ret) {
+		unsigned dropped;
+		/*
+		 * We don't need the return value since our reservation failed,
+		 * we just need to clean up our counter.
+		 */
+		dropped = drop_outstanding_extent(inode);
+		WARN_ON(dropped > 1);
 		return ret;
-
-	atomic_add(nr_extents, &BTRFS_I(inode)->reserved_extents);
-	atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+	}
 
 	block_rsv_add_bytes(block_rsv, to_reserve, 1);
 
-	if (block_rsv->size > 512 * 1024 * 1024)
-		shrink_delalloc(NULL, root, to_reserve, 0);
-
 	return 0;
 }
 
 void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes)
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
-	u64 to_free;
-	int nr_extents;
-	int reserved_extents;
+	u64 to_free = 0;
+	unsigned dropped;
 
 	num_bytes = ALIGN(num_bytes, root->sectorsize);
-	atomic_dec(&BTRFS_I(inode)->outstanding_extents);
-	WARN_ON(atomic_read(&BTRFS_I(inode)->outstanding_extents) < 0);
-
-	reserved_extents = atomic_read(&BTRFS_I(inode)->reserved_extents);
-	do {
-		int old, new;
-
-		nr_extents = atomic_read(&BTRFS_I(inode)->outstanding_extents);
-		if (nr_extents >= reserved_extents) {
-			nr_extents = 0;
-			break;
-		}
-		old = reserved_extents;
-		nr_extents = reserved_extents - nr_extents;
-		new = reserved_extents - nr_extents;
-		old = atomic_cmpxchg(&BTRFS_I(inode)->reserved_extents,
-				     reserved_extents, new);
-		if (likely(old == reserved_extents))
-			break;
-		reserved_extents = old;
-	} while (1);
+	dropped = drop_outstanding_extent(inode);
 
 	to_free = calc_csum_metadata_size(inode, num_bytes);
-	if (nr_extents > 0)
-		to_free += btrfs_calc_trans_metadata_size(root, nr_extents);
+	if (dropped > 0)
+		to_free += btrfs_calc_trans_metadata_size(root, dropped);
 
 	btrfs_block_rsv_release(root, &root->fs_info->delalloc_block_rsv,
 				to_free);
@@ -4990,14 +5002,10 @@
 			}
 
 			/*
-			 * We only want to start kthread caching if we are at
-			 * the point where we will wait for caching to make
-			 * progress, or if our ideal search is over and we've
-			 * found somebody to start caching.
+			 * The caching workers are limited to 2 threads, so we
+			 * can queue as much work as we care to.
 			 */
-			if (loop > LOOP_CACHING_NOWAIT ||
-			    (loop > LOOP_FIND_IDEAL &&
-			     atomic_read(&space_info->caching_threads) < 2)) {
+			if (loop > LOOP_FIND_IDEAL) {
 				ret = cache_block_group(block_group, trans,
 							orig_root, 0);
 				BUG_ON(ret);
@@ -5219,8 +5227,7 @@
 		if (loop == LOOP_FIND_IDEAL && found_uncached_bg) {
 			found_uncached_bg = false;
 			loop++;
-			if (!ideal_cache_percent &&
-			    atomic_read(&space_info->caching_threads))
+			if (!ideal_cache_percent)
 				goto search;
 
 			/*
@@ -5623,7 +5630,7 @@
 	if (!buf)
 		return ERR_PTR(-ENOMEM);
 	btrfs_set_header_generation(buf, trans->transid);
-	btrfs_set_buffer_lockdep_class(buf, level);
+	btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
 	btrfs_tree_lock(buf);
 	clean_tree_block(trans, root, buf);
 
@@ -5910,7 +5917,7 @@
 			return 1;
 
 		if (path->locks[level] && !wc->keep_locks) {
-			btrfs_tree_unlock(eb);
+			btrfs_tree_unlock_rw(eb, path->locks[level]);
 			path->locks[level] = 0;
 		}
 		return 0;
@@ -5934,7 +5941,7 @@
 	 * keep the tree lock
 	 */
 	if (path->locks[level] && level > 0) {
-		btrfs_tree_unlock(eb);
+		btrfs_tree_unlock_rw(eb, path->locks[level]);
 		path->locks[level] = 0;
 	}
 	return 0;
@@ -6047,7 +6054,7 @@
 	BUG_ON(level != btrfs_header_level(next));
 	path->nodes[level] = next;
 	path->slots[level] = 0;
-	path->locks[level] = 1;
+	path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 	wc->level = level;
 	if (wc->level == 1)
 		wc->reada_slot = 0;
@@ -6118,7 +6125,7 @@
 			BUG_ON(level == 0);
 			btrfs_tree_lock(eb);
 			btrfs_set_lock_blocking(eb);
-			path->locks[level] = 1;
+			path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 
 			ret = btrfs_lookup_extent_info(trans, root,
 						       eb->start, eb->len,
@@ -6127,8 +6134,7 @@
 			BUG_ON(ret);
 			BUG_ON(wc->refs[level] == 0);
 			if (wc->refs[level] == 1) {
-				btrfs_tree_unlock(eb);
-				path->locks[level] = 0;
+				btrfs_tree_unlock_rw(eb, path->locks[level]);
 				return 1;
 			}
 		}
@@ -6150,7 +6156,7 @@
 		    btrfs_header_generation(eb) == trans->transid) {
 			btrfs_tree_lock(eb);
 			btrfs_set_lock_blocking(eb);
-			path->locks[level] = 1;
+			path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 		}
 		clean_tree_block(trans, root, eb);
 	}
@@ -6229,7 +6235,8 @@
 				return 0;
 
 			if (path->locks[level]) {
-				btrfs_tree_unlock(path->nodes[level]);
+				btrfs_tree_unlock_rw(path->nodes[level],
+						     path->locks[level]);
 				path->locks[level] = 0;
 			}
 			free_extent_buffer(path->nodes[level]);
@@ -6281,7 +6288,7 @@
 		path->nodes[level] = btrfs_lock_root_node(root);
 		btrfs_set_lock_blocking(path->nodes[level]);
 		path->slots[level] = 0;
-		path->locks[level] = 1;
+		path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 		memset(&wc->update_progress, 0,
 		       sizeof(wc->update_progress));
 	} else {
@@ -6449,7 +6456,7 @@
 	level = btrfs_header_level(node);
 	path->nodes[level] = node;
 	path->slots[level] = 0;
-	path->locks[level] = 1;
+	path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
 
 	wc->refs[parent_level] = 1;
 	wc->flags[parent_level] = BTRFS_BLOCK_FLAG_FULL_BACKREF;
@@ -6524,15 +6531,28 @@
 	return flags;
 }
 
-static int set_block_group_ro(struct btrfs_block_group_cache *cache)
+static int set_block_group_ro(struct btrfs_block_group_cache *cache, int force)
 {
 	struct btrfs_space_info *sinfo = cache->space_info;
 	u64 num_bytes;
+	u64 min_allocable_bytes;
 	int ret = -ENOSPC;
 
 	if (cache->ro)
 		return 0;
 
+	/*
+	 * We need some metadata space and system metadata space for
+	 * allocating chunks in some corner cases until we force to set
+	 * it to be readonly.
+	 */
+	if ((sinfo->flags &
+	     (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_METADATA)) &&
+	    !force)
+		min_allocable_bytes = 1 * 1024 * 1024;
+	else
+		min_allocable_bytes = 0;
+
 	spin_lock(&sinfo->lock);
 	spin_lock(&cache->lock);
 	num_bytes = cache->key.offset - cache->reserved - cache->pinned -
@@ -6540,7 +6560,8 @@
 
 	if (sinfo->bytes_used + sinfo->bytes_reserved + sinfo->bytes_pinned +
 	    sinfo->bytes_may_use + sinfo->bytes_readonly +
-	    cache->reserved_pinned + num_bytes <= sinfo->total_bytes) {
+	    cache->reserved_pinned + num_bytes + min_allocable_bytes <=
+	    sinfo->total_bytes) {
 		sinfo->bytes_readonly += num_bytes;
 		sinfo->bytes_reserved += cache->reserved_pinned;
 		cache->reserved_pinned = 0;
@@ -6571,7 +6592,7 @@
 		do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
 			       CHUNK_ALLOC_FORCE);
 
-	ret = set_block_group_ro(cache);
+	ret = set_block_group_ro(cache, 0);
 	if (!ret)
 		goto out;
 	alloc_flags = get_alloc_profile(root, cache->space_info->flags);
@@ -6579,7 +6600,7 @@
 			     CHUNK_ALLOC_FORCE);
 	if (ret < 0)
 		goto out;
-	ret = set_block_group_ro(cache);
+	ret = set_block_group_ro(cache, 0);
 out:
 	btrfs_end_transaction(trans, root);
 	return ret;
@@ -7016,7 +7037,7 @@
 
 		set_avail_alloc_bits(root->fs_info, cache->flags);
 		if (btrfs_chunk_readonly(root, cache->key.objectid))
-			set_block_group_ro(cache);
+			set_block_group_ro(cache, 1);
 	}
 
 	list_for_each_entry_rcu(space_info, &root->fs_info->space_info, list) {
@@ -7030,9 +7051,9 @@
 		 * mirrored block groups.
 		 */
 		list_for_each_entry(cache, &space_info->block_groups[3], list)
-			set_block_group_ro(cache);
+			set_block_group_ro(cache, 1);
 		list_for_each_entry(cache, &space_info->block_groups[4], list)
-			set_block_group_ro(cache);
+			set_block_group_ro(cache, 1);
 	}
 
 	init_global_block_rsv(info);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7055d11..067b174 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -281,11 +281,10 @@
 		if (other->start == state->end + 1 &&
 		    other->state == state->state) {
 			merge_cb(tree, state, other);
-			other->start = state->start;
-			state->tree = NULL;
-			rb_erase(&state->rb_node, &tree->state);
-			free_extent_state(state);
-			state = NULL;
+			state->end = other->end;
+			other->tree = NULL;
+			rb_erase(&other->rb_node, &tree->state);
+			free_extent_state(other);
 		}
 	}
 
@@ -351,7 +350,6 @@
 		       "%llu %llu\n", (unsigned long long)found->start,
 		       (unsigned long long)found->end,
 		       (unsigned long long)start, (unsigned long long)end);
-		free_extent_state(state);
 		return -EEXIST;
 	}
 	state->tree = tree;
@@ -500,7 +498,8 @@
 			cached_state = NULL;
 		}
 
-		if (cached && cached->tree && cached->start == start) {
+		if (cached && cached->tree && cached->start <= start &&
+		    cached->end > start) {
 			if (clear)
 				atomic_dec(&cached->refs);
 			state = cached;
@@ -742,7 +741,8 @@
 	spin_lock(&tree->lock);
 	if (cached_state && *cached_state) {
 		state = *cached_state;
-		if (state->start == start && state->tree) {
+		if (state->start <= start && state->end > start &&
+		    state->tree) {
 			node = &state->rb_node;
 			goto hit_next;
 		}
@@ -783,13 +783,13 @@
 		if (err)
 			goto out;
 
-		next_node = rb_next(node);
 		cache_state(state, cached_state);
 		merge_state(tree, state);
 		if (last_end == (u64)-1)
 			goto out;
 
 		start = last_end + 1;
+		next_node = rb_next(&state->rb_node);
 		if (next_node && start < end && prealloc && !need_resched()) {
 			state = rb_entry(next_node, struct extent_state,
 					 rb_node);
@@ -862,7 +862,6 @@
 		 * Avoid to free 'prealloc' if it can be merged with
 		 * the later extent.
 		 */
-		atomic_inc(&prealloc->refs);
 		err = insert_state(tree, prealloc, start, this_end,
 				   &bits);
 		BUG_ON(err == -EEXIST);
@@ -872,7 +871,6 @@
 			goto out;
 		}
 		cache_state(prealloc, cached_state);
-		free_extent_state(prealloc);
 		prealloc = NULL;
 		start = this_end + 1;
 		goto search_again;
@@ -1564,7 +1562,8 @@
 	int bitset = 0;
 
 	spin_lock(&tree->lock);
-	if (cached && cached->tree && cached->start == start)
+	if (cached && cached->tree && cached->start <= start &&
+	    cached->end > start)
 		node = &cached->rb_node;
 	else
 		node = tree_search(tree, start);
@@ -2432,6 +2431,7 @@
 	pgoff_t index;
 	pgoff_t end;		/* Inclusive */
 	int scanned = 0;
+	int tag;
 
 	pagevec_init(&pvec, 0);
 	if (wbc->range_cyclic) {
@@ -2442,11 +2442,16 @@
 		end = wbc->range_end >> PAGE_CACHE_SHIFT;
 		scanned = 1;
 	}
+	if (wbc->sync_mode == WB_SYNC_ALL)
+		tag = PAGECACHE_TAG_TOWRITE;
+	else
+		tag = PAGECACHE_TAG_DIRTY;
 retry:
+	if (wbc->sync_mode == WB_SYNC_ALL)
+		tag_pages_for_writeback(mapping, index, end);
 	while (!done && !nr_to_write_done && (index <= end) &&
-	       (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
-			      PAGECACHE_TAG_DIRTY, min(end - index,
-				  (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
+	       (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag,
+			min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
 		unsigned i;
 
 		scanned = 1;
@@ -2551,7 +2556,6 @@
 	};
 	struct writeback_control wbc_writepages = {
 		.sync_mode	= wbc->sync_mode,
-		.older_than_this = NULL,
 		.nr_to_write	= 64,
 		.range_start	= page_offset(page) + PAGE_CACHE_SIZE,
 		.range_end	= (loff_t)-1,
@@ -2584,7 +2588,6 @@
 	};
 	struct writeback_control wbc_writepages = {
 		.sync_mode	= mode,
-		.older_than_this = NULL,
 		.nr_to_write	= nr_pages * 2,
 		.range_start	= start,
 		.range_end	= end + 1,
@@ -3022,8 +3025,15 @@
 		return NULL;
 	eb->start = start;
 	eb->len = len;
-	spin_lock_init(&eb->lock);
-	init_waitqueue_head(&eb->lock_wq);
+	rwlock_init(&eb->lock);
+	atomic_set(&eb->write_locks, 0);
+	atomic_set(&eb->read_locks, 0);
+	atomic_set(&eb->blocking_readers, 0);
+	atomic_set(&eb->blocking_writers, 0);
+	atomic_set(&eb->spinning_readers, 0);
+	atomic_set(&eb->spinning_writers, 0);
+	init_waitqueue_head(&eb->write_lock_wq);
+	init_waitqueue_head(&eb->read_lock_wq);
 
 #if LEAK_DEBUG
 	spin_lock_irqsave(&leak_lock, flags);
@@ -3119,7 +3129,7 @@
 		i = 0;
 	}
 	for (; i < num_pages; i++, index++) {
-		p = find_or_create_page(mapping, index, GFP_NOFS | __GFP_HIGHMEM);
+		p = find_or_create_page(mapping, index, GFP_NOFS);
 		if (!p) {
 			WARN_ON(1);
 			goto free_eb;
@@ -3266,6 +3276,22 @@
 	return was_dirty;
 }
 
+static int __eb_straddles_pages(u64 start, u64 len)
+{
+	if (len < PAGE_CACHE_SIZE)
+		return 1;
+	if (start & (PAGE_CACHE_SIZE - 1))
+		return 1;
+	if ((start + len) & (PAGE_CACHE_SIZE - 1))
+		return 1;
+	return 0;
+}
+
+static int eb_straddles_pages(struct extent_buffer *eb)
+{
+	return __eb_straddles_pages(eb->start, eb->len);
+}
+
 int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
 				struct extent_buffer *eb,
 				struct extent_state **cached_state)
@@ -3277,8 +3303,10 @@
 	num_pages = num_extent_pages(eb->start, eb->len);
 	clear_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
 
-	clear_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
-			      cached_state, GFP_NOFS);
+	if (eb_straddles_pages(eb)) {
+		clear_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
+				      cached_state, GFP_NOFS);
+	}
 	for (i = 0; i < num_pages; i++) {
 		page = extent_buffer_page(eb, i);
 		if (page)
@@ -3296,8 +3324,10 @@
 
 	num_pages = num_extent_pages(eb->start, eb->len);
 
-	set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
-			    NULL, GFP_NOFS);
+	if (eb_straddles_pages(eb)) {
+		set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
+				    NULL, GFP_NOFS);
+	}
 	for (i = 0; i < num_pages; i++) {
 		page = extent_buffer_page(eb, i);
 		if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) ||
@@ -3320,9 +3350,12 @@
 	int uptodate;
 	unsigned long index;
 
-	ret = test_range_bit(tree, start, end, EXTENT_UPTODATE, 1, NULL);
-	if (ret)
-		return 1;
+	if (__eb_straddles_pages(start, end - start + 1)) {
+		ret = test_range_bit(tree, start, end,
+				     EXTENT_UPTODATE, 1, NULL);
+		if (ret)
+			return 1;
+	}
 	while (start <= end) {
 		index = start >> PAGE_CACHE_SHIFT;
 		page = find_get_page(tree->mapping, index);
@@ -3350,10 +3383,12 @@
 	if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
 		return 1;
 
-	ret = test_range_bit(tree, eb->start, eb->start + eb->len - 1,
-			   EXTENT_UPTODATE, 1, cached_state);
-	if (ret)
-		return ret;
+	if (eb_straddles_pages(eb)) {
+		ret = test_range_bit(tree, eb->start, eb->start + eb->len - 1,
+				   EXTENT_UPTODATE, 1, cached_state);
+		if (ret)
+			return ret;
+	}
 
 	num_pages = num_extent_pages(eb->start, eb->len);
 	for (i = 0; i < num_pages; i++) {
@@ -3386,9 +3421,11 @@
 	if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
 		return 0;
 
-	if (test_range_bit(tree, eb->start, eb->start + eb->len - 1,
-			   EXTENT_UPTODATE, 1, NULL)) {
-		return 0;
+	if (eb_straddles_pages(eb)) {
+		if (test_range_bit(tree, eb->start, eb->start + eb->len - 1,
+				   EXTENT_UPTODATE, 1, NULL)) {
+			return 0;
+		}
 	}
 
 	if (start) {
@@ -3492,9 +3529,8 @@
 		page = extent_buffer_page(eb, i);
 
 		cur = min(len, (PAGE_CACHE_SIZE - offset));
-		kaddr = kmap_atomic(page, KM_USER1);
+		kaddr = page_address(page);
 		memcpy(dst, kaddr + offset, cur);
-		kunmap_atomic(kaddr, KM_USER1);
 
 		dst += cur;
 		len -= cur;
@@ -3504,9 +3540,9 @@
 }
 
 int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
-			       unsigned long min_len, char **token, char **map,
+			       unsigned long min_len, char **map,
 			       unsigned long *map_start,
-			       unsigned long *map_len, int km)
+			       unsigned long *map_len)
 {
 	size_t offset = start & (PAGE_CACHE_SIZE - 1);
 	char *kaddr;
@@ -3536,42 +3572,12 @@
 	}
 
 	p = extent_buffer_page(eb, i);
-	kaddr = kmap_atomic(p, km);
-	*token = kaddr;
+	kaddr = page_address(p);
 	*map = kaddr + offset;
 	*map_len = PAGE_CACHE_SIZE - offset;
 	return 0;
 }
 
-int map_extent_buffer(struct extent_buffer *eb, unsigned long start,
-		      unsigned long min_len,
-		      char **token, char **map,
-		      unsigned long *map_start,
-		      unsigned long *map_len, int km)
-{
-	int err;
-	int save = 0;
-	if (eb->map_token) {
-		unmap_extent_buffer(eb, eb->map_token, km);
-		eb->map_token = NULL;
-		save = 1;
-	}
-	err = map_private_extent_buffer(eb, start, min_len, token, map,
-				       map_start, map_len, km);
-	if (!err && save) {
-		eb->map_token = *token;
-		eb->kaddr = *map;
-		eb->map_start = *map_start;
-		eb->map_len = *map_len;
-	}
-	return err;
-}
-
-void unmap_extent_buffer(struct extent_buffer *eb, char *token, int km)
-{
-	kunmap_atomic(token, km);
-}
-
 int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
 			  unsigned long start,
 			  unsigned long len)
@@ -3595,9 +3601,8 @@
 
 		cur = min(len, (PAGE_CACHE_SIZE - offset));
 
-		kaddr = kmap_atomic(page, KM_USER0);
+		kaddr = page_address(page);
 		ret = memcmp(ptr, kaddr + offset, cur);
-		kunmap_atomic(kaddr, KM_USER0);
 		if (ret)
 			break;
 
@@ -3630,9 +3635,8 @@
 		WARN_ON(!PageUptodate(page));
 
 		cur = min(len, PAGE_CACHE_SIZE - offset);
-		kaddr = kmap_atomic(page, KM_USER1);
+		kaddr = page_address(page);
 		memcpy(kaddr + offset, src, cur);
-		kunmap_atomic(kaddr, KM_USER1);
 
 		src += cur;
 		len -= cur;
@@ -3661,9 +3665,8 @@
 		WARN_ON(!PageUptodate(page));
 
 		cur = min(len, PAGE_CACHE_SIZE - offset);
-		kaddr = kmap_atomic(page, KM_USER0);
+		kaddr = page_address(page);
 		memset(kaddr + offset, c, cur);
-		kunmap_atomic(kaddr, KM_USER0);
 
 		len -= cur;
 		offset = 0;
@@ -3694,9 +3697,8 @@
 
 		cur = min(len, (unsigned long)(PAGE_CACHE_SIZE - offset));
 
-		kaddr = kmap_atomic(page, KM_USER0);
+		kaddr = page_address(page);
 		read_extent_buffer(src, kaddr + offset, src_offset, cur);
-		kunmap_atomic(kaddr, KM_USER0);
 
 		src_offset += cur;
 		len -= cur;
@@ -3709,20 +3711,17 @@
 		       unsigned long dst_off, unsigned long src_off,
 		       unsigned long len)
 {
-	char *dst_kaddr = kmap_atomic(dst_page, KM_USER0);
+	char *dst_kaddr = page_address(dst_page);
 	if (dst_page == src_page) {
 		memmove(dst_kaddr + dst_off, dst_kaddr + src_off, len);
 	} else {
-		char *src_kaddr = kmap_atomic(src_page, KM_USER1);
+		char *src_kaddr = page_address(src_page);
 		char *p = dst_kaddr + dst_off + len;
 		char *s = src_kaddr + src_off + len;
 
 		while (len--)
 			*--p = *--s;
-
-		kunmap_atomic(src_kaddr, KM_USER1);
 	}
-	kunmap_atomic(dst_kaddr, KM_USER0);
 }
 
 static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len)
@@ -3735,20 +3734,17 @@
 		       unsigned long dst_off, unsigned long src_off,
 		       unsigned long len)
 {
-	char *dst_kaddr = kmap_atomic(dst_page, KM_USER0);
+	char *dst_kaddr = page_address(dst_page);
 	char *src_kaddr;
 
 	if (dst_page != src_page) {
-		src_kaddr = kmap_atomic(src_page, KM_USER1);
+		src_kaddr = page_address(src_page);
 	} else {
 		src_kaddr = dst_kaddr;
 		BUG_ON(areas_overlap(src_off, dst_off, len));
 	}
 
 	memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len);
-	kunmap_atomic(dst_kaddr, KM_USER0);
-	if (dst_page != src_page)
-		kunmap_atomic(src_kaddr, KM_USER1);
 }
 
 void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index a11a92e..21a7ca9 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -120,8 +120,6 @@
 struct extent_buffer {
 	u64 start;
 	unsigned long len;
-	char *map_token;
-	char *kaddr;
 	unsigned long map_start;
 	unsigned long map_len;
 	struct page *first_page;
@@ -130,14 +128,26 @@
 	struct rcu_head rcu_head;
 	atomic_t refs;
 
-	/* the spinlock is used to protect most operations */
-	spinlock_t lock;
+	/* count of read lock holders on the extent buffer */
+	atomic_t write_locks;
+	atomic_t read_locks;
+	atomic_t blocking_writers;
+	atomic_t blocking_readers;
+	atomic_t spinning_readers;
+	atomic_t spinning_writers;
 
-	/*
-	 * when we keep the lock held while blocking, waiters go onto
-	 * the wq
+	/* protects write locks */
+	rwlock_t lock;
+
+	/* readers use lock_wq while they wait for the write
+	 * lock holders to unlock
 	 */
-	wait_queue_head_t lock_wq;
+	wait_queue_head_t write_lock_wq;
+
+	/* writers use read_lock_wq while they wait for readers
+	 * to unlock
+	 */
+	wait_queue_head_t read_lock_wq;
 };
 
 static inline void extent_set_compress_type(unsigned long *bio_flags,
@@ -279,15 +289,10 @@
 int extent_buffer_uptodate(struct extent_io_tree *tree,
 			   struct extent_buffer *eb,
 			   struct extent_state *cached_state);
-int map_extent_buffer(struct extent_buffer *eb, unsigned long offset,
-		      unsigned long min_len, char **token, char **map,
-		      unsigned long *map_start,
-		      unsigned long *map_len, int km);
 int map_private_extent_buffer(struct extent_buffer *eb, unsigned long offset,
-		      unsigned long min_len, char **token, char **map,
+		      unsigned long min_len, char **map,
 		      unsigned long *map_start,
-		      unsigned long *map_len, int km);
-void unmap_extent_buffer(struct extent_buffer *eb, char *token, int km);
+		      unsigned long *map_len);
 int extent_range_uptodate(struct extent_io_tree *tree,
 			  u64 start, u64 end);
 int extent_clear_unlock_delalloc(struct inode *inode,
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 90d4ee5..08bcfa9 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -177,6 +177,15 @@
 
 	WARN_ON(bio->bi_vcnt <= 0);
 
+	/*
+	 * the free space stuff is only read when it hasn't been
+	 * updated in the current transaction.  So, we can safely
+	 * read from the commit root and sidestep a nasty deadlock
+	 * between reading the free space cache and updating the csum tree.
+	 */
+	if (btrfs_is_free_space_inode(root, inode))
+		path->search_commit_root = 1;
+
 	disk_bytenr = (u64)bio->bi_sector << 9;
 	if (dio)
 		offset = logical_offset;
@@ -664,10 +673,6 @@
 	struct btrfs_sector_sum *sector_sum;
 	u32 nritems;
 	u32 ins_size;
-	char *eb_map;
-	char *eb_token;
-	unsigned long map_len;
-	unsigned long map_start;
 	u16 csum_size =
 		btrfs_super_csum_size(&root->fs_info->super_copy);
 
@@ -814,30 +819,9 @@
 	item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
 	item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
 				      btrfs_item_size_nr(leaf, path->slots[0]));
-	eb_token = NULL;
 next_sector:
 
-	if (!eb_token ||
-	   (unsigned long)item + csum_size >= map_start + map_len) {
-		int err;
-
-		if (eb_token)
-			unmap_extent_buffer(leaf, eb_token, KM_USER1);
-		eb_token = NULL;
-		err = map_private_extent_buffer(leaf, (unsigned long)item,
-						csum_size,
-						&eb_token, &eb_map,
-						&map_start, &map_len, KM_USER1);
-		if (err)
-			eb_token = NULL;
-	}
-	if (eb_token) {
-		memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)),
-		       &sector_sum->sum, csum_size);
-	} else {
-		write_extent_buffer(leaf, &sector_sum->sum,
-				    (unsigned long)item, csum_size);
-	}
+	write_extent_buffer(leaf, &sector_sum->sum, (unsigned long)item, csum_size);
 
 	total_bytes += root->sectorsize;
 	sector_sum++;
@@ -850,10 +834,7 @@
 			goto next_sector;
 		}
 	}
-	if (eb_token) {
-		unmap_extent_buffer(leaf, eb_token, KM_USER1);
-		eb_token = NULL;
-	}
+
 	btrfs_mark_buffer_dirty(path->nodes[0]);
 	if (total_bytes < sums->len) {
 		btrfs_release_path(path);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 59cbdb1..a35e51c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1081,7 +1081,8 @@
 
 again:
 	for (i = 0; i < num_pages; i++) {
-		pages[i] = grab_cache_page(inode->i_mapping, index + i);
+		pages[i] = find_or_create_page(inode->i_mapping, index + i,
+					       GFP_NOFS);
 		if (!pages[i]) {
 			faili = i - 1;
 			err = -ENOMEM;
@@ -1238,9 +1239,11 @@
 		 * managed to copy.
 		 */
 		if (num_pages > dirty_pages) {
-			if (copied > 0)
-				atomic_inc(
-					&BTRFS_I(inode)->outstanding_extents);
+			if (copied > 0) {
+				spin_lock(&BTRFS_I(inode)->lock);
+				BTRFS_I(inode)->outstanding_extents++;
+				spin_unlock(&BTRFS_I(inode)->lock);
+			}
 			btrfs_delalloc_release_space(inode,
 					(num_pages - dirty_pages) <<
 					PAGE_CACHE_SHIFT);
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index bf0d615..6377713 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -98,6 +98,12 @@
 		return inode;
 
 	spin_lock(&block_group->lock);
+	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) {
+		printk(KERN_INFO "Old style space inode found, converting.\n");
+		BTRFS_I(inode)->flags &= ~BTRFS_INODE_NODATASUM;
+		block_group->disk_cache_state = BTRFS_DC_CLEAR;
+	}
+
 	if (!btrfs_fs_closing(root->fs_info)) {
 		block_group->inode = igrab(inode);
 		block_group->iref = 1;
@@ -135,7 +141,7 @@
 	btrfs_set_inode_gid(leaf, inode_item, 0);
 	btrfs_set_inode_mode(leaf, inode_item, S_IFREG | 0600);
 	btrfs_set_inode_flags(leaf, inode_item, BTRFS_INODE_NOCOMPRESS |
-			      BTRFS_INODE_PREALLOC | BTRFS_INODE_NODATASUM);
+			      BTRFS_INODE_PREALLOC);
 	btrfs_set_inode_nlink(leaf, inode_item, 1);
 	btrfs_set_inode_transid(leaf, inode_item, trans->transid);
 	btrfs_set_inode_block_group(leaf, inode_item, offset);
@@ -239,17 +245,12 @@
 	struct btrfs_free_space_header *header;
 	struct extent_buffer *leaf;
 	struct page *page;
-	u32 *checksums = NULL, *crc;
-	char *disk_crcs = NULL;
 	struct btrfs_key key;
 	struct list_head bitmaps;
 	u64 num_entries;
 	u64 num_bitmaps;
 	u64 generation;
-	u32 cur_crc = ~(u32)0;
 	pgoff_t index = 0;
-	unsigned long first_page_offset;
-	int num_checksums;
 	int ret = 0;
 
 	INIT_LIST_HEAD(&bitmaps);
@@ -292,16 +293,6 @@
 	if (!num_entries)
 		goto out;
 
-	/* Setup everything for doing checksumming */
-	num_checksums = i_size_read(inode) / PAGE_CACHE_SIZE;
-	checksums = crc = kzalloc(sizeof(u32) * num_checksums, GFP_NOFS);
-	if (!checksums)
-		goto out;
-	first_page_offset = (sizeof(u32) * num_checksums) + sizeof(u64);
-	disk_crcs = kzalloc(first_page_offset, GFP_NOFS);
-	if (!disk_crcs)
-		goto out;
-
 	ret = readahead_cache(inode);
 	if (ret)
 		goto out;
@@ -311,18 +302,12 @@
 		struct btrfs_free_space *e;
 		void *addr;
 		unsigned long offset = 0;
-		unsigned long start_offset = 0;
 		int need_loop = 0;
 
 		if (!num_entries && !num_bitmaps)
 			break;
 
-		if (index == 0) {
-			start_offset = first_page_offset;
-			offset = start_offset;
-		}
-
-		page = grab_cache_page(inode->i_mapping, index);
+		page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
 		if (!page)
 			goto free_cache;
 
@@ -342,8 +327,15 @@
 		if (index == 0) {
 			u64 *gen;
 
-			memcpy(disk_crcs, addr, first_page_offset);
-			gen = addr + (sizeof(u32) * num_checksums);
+			/*
+			 * We put a bogus crc in the front of the first page in
+			 * case old kernels try to mount a fs with the new
+			 * format to make sure they discard the cache.
+			 */
+			addr += sizeof(u64);
+			offset += sizeof(u64);
+
+			gen = addr;
 			if (*gen != BTRFS_I(inode)->generation) {
 				printk(KERN_ERR "btrfs: space cache generation"
 				       " (%llu) does not match inode (%llu)\n",
@@ -355,24 +347,10 @@
 				page_cache_release(page);
 				goto free_cache;
 			}
-			crc = (u32 *)disk_crcs;
+			addr += sizeof(u64);
+			offset += sizeof(u64);
 		}
-		entry = addr + start_offset;
-
-		/* First lets check our crc before we do anything fun */
-		cur_crc = ~(u32)0;
-		cur_crc = btrfs_csum_data(root, addr + start_offset, cur_crc,
-					  PAGE_CACHE_SIZE - start_offset);
-		btrfs_csum_final(cur_crc, (char *)&cur_crc);
-		if (cur_crc != *crc) {
-			printk(KERN_ERR "btrfs: crc mismatch for page %lu\n",
-			       index);
-			kunmap(page);
-			unlock_page(page);
-			page_cache_release(page);
-			goto free_cache;
-		}
-		crc++;
+		entry = addr;
 
 		while (1) {
 			if (!num_entries)
@@ -470,8 +448,6 @@
 
 	ret = 1;
 out:
-	kfree(checksums);
-	kfree(disk_crcs);
 	return ret;
 free_cache:
 	__btrfs_remove_free_space_cache(ctl);
@@ -569,8 +545,7 @@
 	struct btrfs_key key;
 	u64 start, end, len;
 	u64 bytes = 0;
-	u32 *crc, *checksums;
-	unsigned long first_page_offset;
+	u32 crc = ~(u32)0;
 	int index = 0, num_pages = 0;
 	int entries = 0;
 	int bitmaps = 0;
@@ -590,34 +565,13 @@
 	num_pages = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
 		PAGE_CACHE_SHIFT;
 
-	/* Since the first page has all of our checksums and our generation we
-	 * need to calculate the offset into the page that we can start writing
-	 * our entries.
-	 */
-	first_page_offset = (sizeof(u32) * num_pages) + sizeof(u64);
-
 	filemap_write_and_wait(inode->i_mapping);
 	btrfs_wait_ordered_range(inode, inode->i_size &
 				 ~(root->sectorsize - 1), (u64)-1);
 
-	/* make sure we don't overflow that first page */
-	if (first_page_offset + sizeof(struct btrfs_free_space_entry) >= PAGE_CACHE_SIZE) {
-		/* this is really the same as running out of space, where we also return 0 */
-		printk(KERN_CRIT "Btrfs: free space cache was too big for the crc page\n");
-		ret = 0;
-		goto out_update;
-	}
-
-	/* We need a checksum per page. */
-	crc = checksums = kzalloc(sizeof(u32) * num_pages, GFP_NOFS);
-	if (!crc)
-		return -1;
-
 	pages = kzalloc(sizeof(struct page *) * num_pages, GFP_NOFS);
-	if (!pages) {
-		kfree(crc);
+	if (!pages)
 		return -1;
-	}
 
 	/* Get the cluster for this block_group if it exists */
 	if (block_group && !list_empty(&block_group->cluster_list))
@@ -640,7 +594,7 @@
 	 * know and don't freak out.
 	 */
 	while (index < num_pages) {
-		page = grab_cache_page(inode->i_mapping, index);
+		page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
 		if (!page) {
 			int i;
 
@@ -648,7 +602,7 @@
 				unlock_page(pages[i]);
 				page_cache_release(pages[i]);
 			}
-			goto out_free;
+			goto out;
 		}
 		pages[index] = page;
 		index++;
@@ -668,17 +622,11 @@
 	/* Write out the extent entries */
 	do {
 		struct btrfs_free_space_entry *entry;
-		void *addr;
+		void *addr, *orig;
 		unsigned long offset = 0;
-		unsigned long start_offset = 0;
 
 		next_page = false;
 
-		if (index == 0) {
-			start_offset = first_page_offset;
-			offset = start_offset;
-		}
-
 		if (index >= num_pages) {
 			out_of_space = true;
 			break;
@@ -686,10 +634,26 @@
 
 		page = pages[index];
 
-		addr = kmap(page);
-		entry = addr + start_offset;
+		orig = addr = kmap(page);
+		if (index == 0) {
+			u64 *gen;
 
-		memset(addr, 0, PAGE_CACHE_SIZE);
+			/*
+			 * We're going to put in a bogus crc for this page to
+			 * make sure that old kernels who aren't aware of this
+			 * format will be sure to discard the cache.
+			 */
+			addr += sizeof(u64);
+			offset += sizeof(u64);
+
+			gen = addr;
+			*gen = trans->transid;
+			addr += sizeof(u64);
+			offset += sizeof(u64);
+		}
+		entry = addr;
+
+		memset(addr, 0, PAGE_CACHE_SIZE - offset);
 		while (node && !next_page) {
 			struct btrfs_free_space *e;
 
@@ -752,13 +716,19 @@
 				next_page = true;
 			entry++;
 		}
-		*crc = ~(u32)0;
-		*crc = btrfs_csum_data(root, addr + start_offset, *crc,
-				       PAGE_CACHE_SIZE - start_offset);
-		kunmap(page);
 
-		btrfs_csum_final(*crc, (char *)crc);
-		crc++;
+		/* Generate bogus crc value */
+		if (index == 0) {
+			u32 *tmp;
+			crc = btrfs_csum_data(root, orig + sizeof(u64), crc,
+					      PAGE_CACHE_SIZE - sizeof(u64));
+			btrfs_csum_final(crc, (char *)&crc);
+			crc++;
+			tmp = orig;
+			*tmp = crc;
+		}
+
+		kunmap(page);
 
 		bytes += PAGE_CACHE_SIZE;
 
@@ -779,11 +749,7 @@
 
 		addr = kmap(page);
 		memcpy(addr, entry->bitmap, PAGE_CACHE_SIZE);
-		*crc = ~(u32)0;
-		*crc = btrfs_csum_data(root, addr, *crc, PAGE_CACHE_SIZE);
 		kunmap(page);
-		btrfs_csum_final(*crc, (char *)crc);
-		crc++;
 		bytes += PAGE_CACHE_SIZE;
 
 		list_del_init(&entry->list);
@@ -796,7 +762,7 @@
 				     i_size_read(inode) - 1, &cached_state,
 				     GFP_NOFS);
 		ret = 0;
-		goto out_free;
+		goto out;
 	}
 
 	/* Zero out the rest of the pages just to make sure */
@@ -811,20 +777,6 @@
 		index++;
 	}
 
-	/* Write the checksums and trans id to the first page */
-	{
-		void *addr;
-		u64 *gen;
-
-		page = pages[0];
-
-		addr = kmap(page);
-		memcpy(addr, checksums, sizeof(u32) * num_pages);
-		gen = addr + (sizeof(u32) * num_pages);
-		*gen = trans->transid;
-		kunmap(page);
-	}
-
 	ret = btrfs_dirty_pages(root, inode, pages, num_pages, 0,
 					    bytes, &cached_state);
 	btrfs_drop_pages(pages, num_pages);
@@ -833,7 +785,7 @@
 
 	if (ret) {
 		ret = 0;
-		goto out_free;
+		goto out;
 	}
 
 	BTRFS_I(inode)->generation = trans->transid;
@@ -850,7 +802,7 @@
 		clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, bytes - 1,
 				 EXTENT_DIRTY | EXTENT_DELALLOC |
 				 EXTENT_DO_ACCOUNTING, 0, 0, NULL, GFP_NOFS);
-		goto out_free;
+		goto out;
 	}
 	leaf = path->nodes[0];
 	if (ret > 0) {
@@ -866,7 +818,7 @@
 					 EXTENT_DO_ACCOUNTING, 0, 0, NULL,
 					 GFP_NOFS);
 			btrfs_release_path(path);
-			goto out_free;
+			goto out;
 		}
 	}
 	header = btrfs_item_ptr(leaf, path->slots[0],
@@ -879,11 +831,8 @@
 
 	ret = 1;
 
-out_free:
-	kfree(checksums);
+out:
 	kfree(pages);
-
-out_update:
 	if (ret != 1) {
 		invalidate_inode_pages2_range(inode->i_mapping, 0, index);
 		BTRFS_I(inode)->generation = 0;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e91b097..13e6255 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -750,15 +750,6 @@
 	return alloc_hint;
 }
 
-static inline bool is_free_space_inode(struct btrfs_root *root,
-				       struct inode *inode)
-{
-	if (root == root->fs_info->tree_root ||
-	    BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID)
-		return true;
-	return false;
-}
-
 /*
  * when extent_io.c finds a delayed allocation range in the file,
  * the call backs end up in this code.  The basic idea is to
@@ -791,7 +782,7 @@
 	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
 	int ret = 0;
 
-	BUG_ON(is_free_space_inode(root, inode));
+	BUG_ON(btrfs_is_free_space_inode(root, inode));
 	trans = btrfs_join_transaction(root);
 	BUG_ON(IS_ERR(trans));
 	trans->block_rsv = &root->fs_info->delalloc_block_rsv;
@@ -1072,7 +1063,7 @@
 	path = btrfs_alloc_path();
 	BUG_ON(!path);
 
-	nolock = is_free_space_inode(root, inode);
+	nolock = btrfs_is_free_space_inode(root, inode);
 
 	if (nolock)
 		trans = btrfs_join_transaction_nolock(root);
@@ -1298,7 +1289,9 @@
 	if (!(orig->state & EXTENT_DELALLOC))
 		return 0;
 
-	atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+	spin_lock(&BTRFS_I(inode)->lock);
+	BTRFS_I(inode)->outstanding_extents++;
+	spin_unlock(&BTRFS_I(inode)->lock);
 	return 0;
 }
 
@@ -1316,7 +1309,9 @@
 	if (!(other->state & EXTENT_DELALLOC))
 		return 0;
 
-	atomic_dec(&BTRFS_I(inode)->outstanding_extents);
+	spin_lock(&BTRFS_I(inode)->lock);
+	BTRFS_I(inode)->outstanding_extents--;
+	spin_unlock(&BTRFS_I(inode)->lock);
 	return 0;
 }
 
@@ -1337,12 +1332,15 @@
 	if (!(state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) {
 		struct btrfs_root *root = BTRFS_I(inode)->root;
 		u64 len = state->end + 1 - state->start;
-		bool do_list = !is_free_space_inode(root, inode);
+		bool do_list = !btrfs_is_free_space_inode(root, inode);
 
-		if (*bits & EXTENT_FIRST_DELALLOC)
+		if (*bits & EXTENT_FIRST_DELALLOC) {
 			*bits &= ~EXTENT_FIRST_DELALLOC;
-		else
-			atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+		} else {
+			spin_lock(&BTRFS_I(inode)->lock);
+			BTRFS_I(inode)->outstanding_extents++;
+			spin_unlock(&BTRFS_I(inode)->lock);
+		}
 
 		spin_lock(&root->fs_info->delalloc_lock);
 		BTRFS_I(inode)->delalloc_bytes += len;
@@ -1370,12 +1368,15 @@
 	if ((state->state & EXTENT_DELALLOC) && (*bits & EXTENT_DELALLOC)) {
 		struct btrfs_root *root = BTRFS_I(inode)->root;
 		u64 len = state->end + 1 - state->start;
-		bool do_list = !is_free_space_inode(root, inode);
+		bool do_list = !btrfs_is_free_space_inode(root, inode);
 
-		if (*bits & EXTENT_FIRST_DELALLOC)
+		if (*bits & EXTENT_FIRST_DELALLOC) {
 			*bits &= ~EXTENT_FIRST_DELALLOC;
-		else if (!(*bits & EXTENT_DO_ACCOUNTING))
-			atomic_dec(&BTRFS_I(inode)->outstanding_extents);
+		} else if (!(*bits & EXTENT_DO_ACCOUNTING)) {
+			spin_lock(&BTRFS_I(inode)->lock);
+			BTRFS_I(inode)->outstanding_extents--;
+			spin_unlock(&BTRFS_I(inode)->lock);
+		}
 
 		if (*bits & EXTENT_DO_ACCOUNTING)
 			btrfs_delalloc_release_metadata(inode, len);
@@ -1477,7 +1478,7 @@
 
 	skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
 
-	if (is_free_space_inode(root, inode))
+	if (btrfs_is_free_space_inode(root, inode))
 		ret = btrfs_bio_wq_end_io(root->fs_info, bio, 2);
 	else
 		ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
@@ -1726,7 +1727,7 @@
 		return 0;
 	BUG_ON(!ordered_extent);
 
-	nolock = is_free_space_inode(root, inode);
+	nolock = btrfs_is_free_space_inode(root, inode);
 
 	if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) {
 		BUG_ON(!list_empty(&ordered_extent->list));
@@ -2531,13 +2532,6 @@
 
 	inode_item = btrfs_item_ptr(leaf, path->slots[0],
 				    struct btrfs_inode_item);
-	if (!leaf->map_token)
-		map_private_extent_buffer(leaf, (unsigned long)inode_item,
-					  sizeof(struct btrfs_inode_item),
-					  &leaf->map_token, &leaf->kaddr,
-					  &leaf->map_start, &leaf->map_len,
-					  KM_USER1);
-
 	inode->i_mode = btrfs_inode_mode(leaf, inode_item);
 	inode->i_nlink = btrfs_inode_nlink(leaf, inode_item);
 	inode->i_uid = btrfs_inode_uid(leaf, inode_item);
@@ -2575,11 +2569,6 @@
 	if (!maybe_acls)
 		cache_no_acl(inode);
 
-	if (leaf->map_token) {
-		unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-		leaf->map_token = NULL;
-	}
-
 	btrfs_free_path(path);
 
 	switch (inode->i_mode & S_IFMT) {
@@ -2624,13 +2613,6 @@
 			    struct btrfs_inode_item *item,
 			    struct inode *inode)
 {
-	if (!leaf->map_token)
-		map_private_extent_buffer(leaf, (unsigned long)item,
-					  sizeof(struct btrfs_inode_item),
-					  &leaf->map_token, &leaf->kaddr,
-					  &leaf->map_start, &leaf->map_len,
-					  KM_USER1);
-
 	btrfs_set_inode_uid(leaf, item, inode->i_uid);
 	btrfs_set_inode_gid(leaf, item, inode->i_gid);
 	btrfs_set_inode_size(leaf, item, BTRFS_I(inode)->disk_i_size);
@@ -2659,11 +2641,6 @@
 	btrfs_set_inode_rdev(leaf, item, inode->i_rdev);
 	btrfs_set_inode_flags(leaf, item, BTRFS_I(inode)->flags);
 	btrfs_set_inode_block_group(leaf, item, 0);
-
-	if (leaf->map_token) {
-		unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
-		leaf->map_token = NULL;
-	}
 }
 
 /*
@@ -2684,7 +2661,7 @@
 	 * The data relocation inode should also be directly updated
 	 * without delay
 	 */
-	if (!is_free_space_inode(root, inode)
+	if (!btrfs_is_free_space_inode(root, inode)
 	    && root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID) {
 		ret = btrfs_delayed_update_inode(trans, root, inode);
 		if (!ret)
@@ -3398,7 +3375,7 @@
 
 	ret = -ENOMEM;
 again:
-	page = grab_cache_page(mapping, index);
+	page = find_or_create_page(mapping, index, GFP_NOFS);
 	if (!page) {
 		btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE);
 		goto out;
@@ -3634,7 +3611,7 @@
 
 	truncate_inode_pages(&inode->i_data, 0);
 	if (inode->i_nlink && (btrfs_root_refs(&root->root_item) != 0 ||
-			       is_free_space_inode(root, inode)))
+			       btrfs_is_free_space_inode(root, inode)))
 		goto no_delete;
 
 	if (is_bad_inode(inode)) {
@@ -4271,7 +4248,7 @@
 	if (BTRFS_I(inode)->dummy_inode)
 		return 0;
 
-	if (btrfs_fs_closing(root->fs_info) && is_free_space_inode(root, inode))
+	if (btrfs_fs_closing(root->fs_info) && btrfs_is_free_space_inode(root, inode))
 		nolock = true;
 
 	if (wbc->sync_mode == WB_SYNC_ALL) {
@@ -4467,7 +4444,7 @@
 	inode->i_generation = BTRFS_I(inode)->generation;
 	btrfs_set_inode_space_info(root, inode);
 
-	if (mode & S_IFDIR)
+	if (S_ISDIR(mode))
 		owner = 0;
 	else
 		owner = 1;
@@ -4512,7 +4489,7 @@
 
 	btrfs_inherit_iflags(inode, dir);
 
-	if ((mode & S_IFREG)) {
+	if (S_ISREG(mode)) {
 		if (btrfs_test_opt(root, NODATASUM))
 			BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
 		if (btrfs_test_opt(root, NODATACOW) ||
@@ -6728,8 +6705,9 @@
 	ei->index_cnt = (u64)-1;
 	ei->last_unlink_trans = 0;
 
-	atomic_set(&ei->outstanding_extents, 0);
-	atomic_set(&ei->reserved_extents, 0);
+	spin_lock_init(&ei->lock);
+	ei->outstanding_extents = 0;
+	ei->reserved_extents = 0;
 
 	ei->ordered_data_close = 0;
 	ei->orphan_meta_reserved = 0;
@@ -6767,8 +6745,8 @@
 
 	WARN_ON(!list_empty(&inode->i_dentry));
 	WARN_ON(inode->i_data.nrpages);
-	WARN_ON(atomic_read(&BTRFS_I(inode)->outstanding_extents));
-	WARN_ON(atomic_read(&BTRFS_I(inode)->reserved_extents));
+	WARN_ON(BTRFS_I(inode)->outstanding_extents);
+	WARN_ON(BTRFS_I(inode)->reserved_extents);
 
 	/*
 	 * This can happen where we create an inode, but somebody else also
@@ -6823,7 +6801,7 @@
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 
 	if (btrfs_root_refs(&root->root_item) == 0 &&
-	    !is_free_space_inode(root, inode))
+	    !btrfs_is_free_space_inode(root, inode))
 		return 1;
 	else
 		return generic_drop_inode(inode);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6225433..0b980af 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -859,8 +859,8 @@
 	/* step one, lock all the pages */
 	for (i = 0; i < num_pages; i++) {
 		struct page *page;
-		page = grab_cache_page(inode->i_mapping,
-					    start_index + i);
+		page = find_or_create_page(inode->i_mapping,
+					    start_index + i, GFP_NOFS);
 		if (!page)
 			break;
 
@@ -930,7 +930,9 @@
 			  GFP_NOFS);
 
 	if (i_done != num_pages) {
-		atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+		spin_lock(&BTRFS_I(inode)->lock);
+		BTRFS_I(inode)->outstanding_extents++;
+		spin_unlock(&BTRFS_I(inode)->lock);
 		btrfs_delalloc_release_space(inode,
 				     (num_pages - i_done) << PAGE_CACHE_SHIFT);
 	}
diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c
index 66fa43d..d77b67c 100644
--- a/fs/btrfs/locking.c
+++ b/fs/btrfs/locking.c
@@ -24,185 +24,197 @@
 #include "extent_io.h"
 #include "locking.h"
 
-static inline void spin_nested(struct extent_buffer *eb)
+void btrfs_assert_tree_read_locked(struct extent_buffer *eb);
+
+/*
+ * if we currently have a spinning reader or writer lock
+ * (indicated by the rw flag) this will bump the count
+ * of blocking holders and drop the spinlock.
+ */
+void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw)
 {
-	spin_lock(&eb->lock);
+	if (rw == BTRFS_WRITE_LOCK) {
+		if (atomic_read(&eb->blocking_writers) == 0) {
+			WARN_ON(atomic_read(&eb->spinning_writers) != 1);
+			atomic_dec(&eb->spinning_writers);
+			btrfs_assert_tree_locked(eb);
+			atomic_inc(&eb->blocking_writers);
+			write_unlock(&eb->lock);
+		}
+	} else if (rw == BTRFS_READ_LOCK) {
+		btrfs_assert_tree_read_locked(eb);
+		atomic_inc(&eb->blocking_readers);
+		WARN_ON(atomic_read(&eb->spinning_readers) == 0);
+		atomic_dec(&eb->spinning_readers);
+		read_unlock(&eb->lock);
+	}
+	return;
 }
 
 /*
- * Setting a lock to blocking will drop the spinlock and set the
- * flag that forces other procs who want the lock to wait.  After
- * this you can safely schedule with the lock held.
+ * if we currently have a blocking lock, take the spinlock
+ * and drop our blocking count
  */
-void btrfs_set_lock_blocking(struct extent_buffer *eb)
+void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw)
 {
-	if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags)) {
-		set_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags);
-		spin_unlock(&eb->lock);
+	if (rw == BTRFS_WRITE_LOCK_BLOCKING) {
+		BUG_ON(atomic_read(&eb->blocking_writers) != 1);
+		write_lock(&eb->lock);
+		WARN_ON(atomic_read(&eb->spinning_writers));
+		atomic_inc(&eb->spinning_writers);
+		if (atomic_dec_and_test(&eb->blocking_writers))
+			wake_up(&eb->write_lock_wq);
+	} else if (rw == BTRFS_READ_LOCK_BLOCKING) {
+		BUG_ON(atomic_read(&eb->blocking_readers) == 0);
+		read_lock(&eb->lock);
+		atomic_inc(&eb->spinning_readers);
+		if (atomic_dec_and_test(&eb->blocking_readers))
+			wake_up(&eb->read_lock_wq);
 	}
-	/* exit with the spin lock released and the bit set */
+	return;
 }
 
 /*
- * clearing the blocking flag will take the spinlock again.
- * After this you can't safely schedule
+ * take a spinning read lock.  This will wait for any blocking
+ * writers
  */
-void btrfs_clear_lock_blocking(struct extent_buffer *eb)
+void btrfs_tree_read_lock(struct extent_buffer *eb)
 {
-	if (test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags)) {
-		spin_nested(eb);
-		clear_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags);
-		smp_mb__after_clear_bit();
+again:
+	wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0);
+	read_lock(&eb->lock);
+	if (atomic_read(&eb->blocking_writers)) {
+		read_unlock(&eb->lock);
+		wait_event(eb->write_lock_wq,
+			   atomic_read(&eb->blocking_writers) == 0);
+		goto again;
 	}
-	/* exit with the spin lock held */
+	atomic_inc(&eb->read_locks);
+	atomic_inc(&eb->spinning_readers);
 }
 
 /*
- * unfortunately, many of the places that currently set a lock to blocking
- * don't end up blocking for very long, and often they don't block
- * at all.  For a dbench 50 run, if we don't spin on the blocking bit
- * at all, the context switch rate can jump up to 400,000/sec or more.
- *
- * So, we're still stuck with this crummy spin on the blocking bit,
- * at least until the most common causes of the short blocks
- * can be dealt with.
+ * returns 1 if we get the read lock and 0 if we don't
+ * this won't wait for blocking writers
  */
-static int btrfs_spin_on_block(struct extent_buffer *eb)
+int btrfs_try_tree_read_lock(struct extent_buffer *eb)
 {
-	int i;
+	if (atomic_read(&eb->blocking_writers))
+		return 0;
 
-	for (i = 0; i < 512; i++) {
-		if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-			return 1;
-		if (need_resched())
-			break;
-		cpu_relax();
+	read_lock(&eb->lock);
+	if (atomic_read(&eb->blocking_writers)) {
+		read_unlock(&eb->lock);
+		return 0;
 	}
-	return 0;
-}
-
-/*
- * This is somewhat different from trylock.  It will take the
- * spinlock but if it finds the lock is set to blocking, it will
- * return without the lock held.
- *
- * returns 1 if it was able to take the lock and zero otherwise
- *
- * After this call, scheduling is not safe without first calling
- * btrfs_set_lock_blocking()
- */
-int btrfs_try_spin_lock(struct extent_buffer *eb)
-{
-	int i;
-
-	if (btrfs_spin_on_block(eb)) {
-		spin_nested(eb);
-		if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-			return 1;
-		spin_unlock(&eb->lock);
-	}
-	/* spin for a bit on the BLOCKING flag */
-	for (i = 0; i < 2; i++) {
-		cpu_relax();
-		if (!btrfs_spin_on_block(eb))
-			break;
-
-		spin_nested(eb);
-		if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-			return 1;
-		spin_unlock(&eb->lock);
-	}
-	return 0;
-}
-
-/*
- * the autoremove wake function will return 0 if it tried to wake up
- * a process that was already awake, which means that process won't
- * count as an exclusive wakeup.  The waitq code will continue waking
- * procs until it finds one that was actually sleeping.
- *
- * For btrfs, this isn't quite what we want.  We want a single proc
- * to be notified that the lock is ready for taking.  If that proc
- * already happen to be awake, great, it will loop around and try for
- * the lock.
- *
- * So, btrfs_wake_function always returns 1, even when the proc that we
- * tried to wake up was already awake.
- */
-static int btrfs_wake_function(wait_queue_t *wait, unsigned mode,
-			       int sync, void *key)
-{
-	autoremove_wake_function(wait, mode, sync, key);
+	atomic_inc(&eb->read_locks);
+	atomic_inc(&eb->spinning_readers);
 	return 1;
 }
 
 /*
- * returns with the extent buffer spinlocked.
- *
- * This will spin and/or wait as required to take the lock, and then
- * return with the spinlock held.
- *
- * After this call, scheduling is not safe without first calling
- * btrfs_set_lock_blocking()
+ * returns 1 if we get the read lock and 0 if we don't
+ * this won't wait for blocking writers or readers
+ */
+int btrfs_try_tree_write_lock(struct extent_buffer *eb)
+{
+	if (atomic_read(&eb->blocking_writers) ||
+	    atomic_read(&eb->blocking_readers))
+		return 0;
+	write_lock(&eb->lock);
+	if (atomic_read(&eb->blocking_writers) ||
+	    atomic_read(&eb->blocking_readers)) {
+		write_unlock(&eb->lock);
+		return 0;
+	}
+	atomic_inc(&eb->write_locks);
+	atomic_inc(&eb->spinning_writers);
+	return 1;
+}
+
+/*
+ * drop a spinning read lock
+ */
+void btrfs_tree_read_unlock(struct extent_buffer *eb)
+{
+	btrfs_assert_tree_read_locked(eb);
+	WARN_ON(atomic_read(&eb->spinning_readers) == 0);
+	atomic_dec(&eb->spinning_readers);
+	atomic_dec(&eb->read_locks);
+	read_unlock(&eb->lock);
+}
+
+/*
+ * drop a blocking read lock
+ */
+void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb)
+{
+	btrfs_assert_tree_read_locked(eb);
+	WARN_ON(atomic_read(&eb->blocking_readers) == 0);
+	if (atomic_dec_and_test(&eb->blocking_readers))
+		wake_up(&eb->read_lock_wq);
+	atomic_dec(&eb->read_locks);
+}
+
+/*
+ * take a spinning write lock.  This will wait for both
+ * blocking readers or writers
  */
 int btrfs_tree_lock(struct extent_buffer *eb)
 {
-	DEFINE_WAIT(wait);
-	wait.func = btrfs_wake_function;
-
-	if (!btrfs_spin_on_block(eb))
-		goto sleep;
-
-	while(1) {
-		spin_nested(eb);
-
-		/* nobody is blocking, exit with the spinlock held */
-		if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-			return 0;
-
-		/*
-		 * we have the spinlock, but the real owner is blocking.
-		 * wait for them
-		 */
-		spin_unlock(&eb->lock);
-
-		/*
-		 * spin for a bit, and if the blocking flag goes away,
-		 * loop around
-		 */
-		cpu_relax();
-		if (btrfs_spin_on_block(eb))
-			continue;
-sleep:
-		prepare_to_wait_exclusive(&eb->lock_wq, &wait,
-					  TASK_UNINTERRUPTIBLE);
-
-		if (test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-			schedule();
-
-		finish_wait(&eb->lock_wq, &wait);
+again:
+	wait_event(eb->read_lock_wq, atomic_read(&eb->blocking_readers) == 0);
+	wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0);
+	write_lock(&eb->lock);
+	if (atomic_read(&eb->blocking_readers)) {
+		write_unlock(&eb->lock);
+		wait_event(eb->read_lock_wq,
+			   atomic_read(&eb->blocking_readers) == 0);
+		goto again;
 	}
+	if (atomic_read(&eb->blocking_writers)) {
+		write_unlock(&eb->lock);
+		wait_event(eb->write_lock_wq,
+			   atomic_read(&eb->blocking_writers) == 0);
+		goto again;
+	}
+	WARN_ON(atomic_read(&eb->spinning_writers));
+	atomic_inc(&eb->spinning_writers);
+	atomic_inc(&eb->write_locks);
 	return 0;
 }
 
+/*
+ * drop a spinning or a blocking write lock.
+ */
 int btrfs_tree_unlock(struct extent_buffer *eb)
 {
-	/*
-	 * if we were a blocking owner, we don't have the spinlock held
-	 * just clear the bit and look for waiters
-	 */
-	if (test_and_clear_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-		smp_mb__after_clear_bit();
-	else
-		spin_unlock(&eb->lock);
+	int blockers = atomic_read(&eb->blocking_writers);
 
-	if (waitqueue_active(&eb->lock_wq))
-		wake_up(&eb->lock_wq);
+	BUG_ON(blockers > 1);
+
+	btrfs_assert_tree_locked(eb);
+	atomic_dec(&eb->write_locks);
+
+	if (blockers) {
+		WARN_ON(atomic_read(&eb->spinning_writers));
+		atomic_dec(&eb->blocking_writers);
+		smp_wmb();
+		wake_up(&eb->write_lock_wq);
+	} else {
+		WARN_ON(atomic_read(&eb->spinning_writers) != 1);
+		atomic_dec(&eb->spinning_writers);
+		write_unlock(&eb->lock);
+	}
 	return 0;
 }
 
 void btrfs_assert_tree_locked(struct extent_buffer *eb)
 {
-	if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
-		assert_spin_locked(&eb->lock);
+	BUG_ON(!atomic_read(&eb->write_locks));
+}
+
+void btrfs_assert_tree_read_locked(struct extent_buffer *eb)
+{
+	BUG_ON(!atomic_read(&eb->read_locks));
 }
diff --git a/fs/btrfs/locking.h b/fs/btrfs/locking.h
index 5c33a56..17247dd 100644
--- a/fs/btrfs/locking.h
+++ b/fs/btrfs/locking.h
@@ -19,11 +19,43 @@
 #ifndef __BTRFS_LOCKING_
 #define __BTRFS_LOCKING_
 
+#define BTRFS_WRITE_LOCK 1
+#define BTRFS_READ_LOCK 2
+#define BTRFS_WRITE_LOCK_BLOCKING 3
+#define BTRFS_READ_LOCK_BLOCKING 4
+
 int btrfs_tree_lock(struct extent_buffer *eb);
 int btrfs_tree_unlock(struct extent_buffer *eb);
 int btrfs_try_spin_lock(struct extent_buffer *eb);
 
-void btrfs_set_lock_blocking(struct extent_buffer *eb);
-void btrfs_clear_lock_blocking(struct extent_buffer *eb);
+void btrfs_tree_read_lock(struct extent_buffer *eb);
+void btrfs_tree_read_unlock(struct extent_buffer *eb);
+void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb);
+void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw);
+void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw);
 void btrfs_assert_tree_locked(struct extent_buffer *eb);
+int btrfs_try_tree_read_lock(struct extent_buffer *eb);
+int btrfs_try_tree_write_lock(struct extent_buffer *eb);
+
+static inline void btrfs_tree_unlock_rw(struct extent_buffer *eb, int rw)
+{
+	if (rw == BTRFS_WRITE_LOCK || rw == BTRFS_WRITE_LOCK_BLOCKING)
+		btrfs_tree_unlock(eb);
+	else if (rw == BTRFS_READ_LOCK_BLOCKING)
+		btrfs_tree_read_unlock_blocking(eb);
+	else if (rw == BTRFS_READ_LOCK)
+		btrfs_tree_read_unlock(eb);
+	else
+		BUG();
+}
+
+static inline void btrfs_set_lock_blocking(struct extent_buffer *eb)
+{
+	btrfs_set_lock_blocking_rw(eb, BTRFS_WRITE_LOCK);
+}
+
+static inline void btrfs_clear_lock_blocking(struct extent_buffer *eb)
+{
+	btrfs_clear_lock_blocking_rw(eb, BTRFS_WRITE_LOCK_BLOCKING);
+}
 #endif
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 5e0a3dc..59bb176 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2955,7 +2955,8 @@
 			page_cache_sync_readahead(inode->i_mapping,
 						  ra, NULL, index,
 						  last_index + 1 - index);
-			page = grab_cache_page(inode->i_mapping, index);
+			page = find_or_create_page(inode->i_mapping, index,
+						   GFP_NOFS);
 			if (!page) {
 				btrfs_delalloc_release_metadata(inode,
 							PAGE_CACHE_SIZE);
diff --git a/fs/btrfs/struct-funcs.c b/fs/btrfs/struct-funcs.c
index c0f7eca..bc1f6ad 100644
--- a/fs/btrfs/struct-funcs.c
+++ b/fs/btrfs/struct-funcs.c
@@ -50,36 +50,22 @@
 	unsigned long part_offset = (unsigned long)s;			\
 	unsigned long offset = part_offset + offsetof(type, member);	\
 	type *p;							\
-	/* ugly, but we want the fast path here */			\
-	if (eb->map_token && offset >= eb->map_start &&			\
-	    offset + sizeof(((type *)0)->member) <= eb->map_start +	\
-	    eb->map_len) {						\
-		p = (type *)(eb->kaddr + part_offset - eb->map_start);	\
-		return le##bits##_to_cpu(p->member);			\
-	}								\
-	{								\
-		int err;						\
-		char *map_token;					\
-		char *kaddr;						\
-		int unmap_on_exit = (eb->map_token == NULL);		\
-		unsigned long map_start;				\
-		unsigned long map_len;					\
-		u##bits res;						\
-		err = map_extent_buffer(eb, offset,			\
-				sizeof(((type *)0)->member),		\
-				&map_token, &kaddr,			\
-				&map_start, &map_len, KM_USER1);	\
-		if (err) {						\
-			__le##bits leres;				\
-			read_eb_member(eb, s, type, member, &leres);	\
-			return le##bits##_to_cpu(leres);		\
-		}							\
-		p = (type *)(kaddr + part_offset - map_start);		\
-		res = le##bits##_to_cpu(p->member);			\
-		if (unmap_on_exit)					\
-			unmap_extent_buffer(eb, map_token, KM_USER1);	\
-		return res;						\
-	}								\
+	int err;						\
+	char *kaddr;						\
+	unsigned long map_start;				\
+	unsigned long map_len;					\
+	u##bits res;						\
+	err = map_private_extent_buffer(eb, offset,		\
+			sizeof(((type *)0)->member),		\
+			&kaddr, &map_start, &map_len);		\
+	if (err) {						\
+		__le##bits leres;				\
+		read_eb_member(eb, s, type, member, &leres);	\
+		return le##bits##_to_cpu(leres);		\
+	}							\
+	p = (type *)(kaddr + part_offset - map_start);		\
+	res = le##bits##_to_cpu(p->member);			\
+	return res;						\
 }									\
 void btrfs_set_##name(struct extent_buffer *eb,				\
 				    type *s, u##bits val)		\
@@ -87,36 +73,21 @@
 	unsigned long part_offset = (unsigned long)s;			\
 	unsigned long offset = part_offset + offsetof(type, member);	\
 	type *p;							\
-	/* ugly, but we want the fast path here */			\
-	if (eb->map_token && offset >= eb->map_start &&			\
-	    offset + sizeof(((type *)0)->member) <= eb->map_start +	\
-	    eb->map_len) {						\
-		p = (type *)(eb->kaddr + part_offset - eb->map_start);	\
-		p->member = cpu_to_le##bits(val);			\
-		return;							\
-	}								\
-	{								\
-		int err;						\
-		char *map_token;					\
-		char *kaddr;						\
-		int unmap_on_exit = (eb->map_token == NULL);		\
-		unsigned long map_start;				\
-		unsigned long map_len;					\
-		err = map_extent_buffer(eb, offset,			\
-				sizeof(((type *)0)->member),		\
-				&map_token, &kaddr,			\
-				&map_start, &map_len, KM_USER1);	\
-		if (err) {						\
-			__le##bits val2;				\
-			val2 = cpu_to_le##bits(val);			\
-			write_eb_member(eb, s, type, member, &val2);	\
-			return;						\
-		}							\
-		p = (type *)(kaddr + part_offset - map_start);		\
-		p->member = cpu_to_le##bits(val);			\
-		if (unmap_on_exit)					\
-			unmap_extent_buffer(eb, map_token, KM_USER1);	\
-	}								\
+	int err;						\
+	char *kaddr;						\
+	unsigned long map_start;				\
+	unsigned long map_len;					\
+	err = map_private_extent_buffer(eb, offset,		\
+			sizeof(((type *)0)->member),		\
+			&kaddr, &map_start, &map_len);		\
+	if (err) {						\
+		__le##bits val2;				\
+		val2 = cpu_to_le##bits(val);			\
+		write_eb_member(eb, s, type, member, &val2);	\
+		return;						\
+	}							\
+	p = (type *)(kaddr + part_offset - map_start);		\
+	p->member = cpu_to_le##bits(val);			\
 }
 
 #include "ctree.h"
@@ -125,15 +96,6 @@
 		    struct btrfs_disk_key *disk_key, int nr)
 {
 	unsigned long ptr = btrfs_node_key_ptr_offset(nr);
-	if (eb->map_token && ptr >= eb->map_start &&
-	    ptr + sizeof(*disk_key) <= eb->map_start + eb->map_len) {
-		memcpy(disk_key, eb->kaddr + ptr - eb->map_start,
-			sizeof(*disk_key));
-		return;
-	} else if (eb->map_token) {
-		unmap_extent_buffer(eb, eb->map_token, KM_USER1);
-		eb->map_token = NULL;
-	}
 	read_eb_member(eb, (struct btrfs_key_ptr *)ptr,
 		       struct btrfs_key_ptr, key, disk_key);
 }
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 51dcec8..eb55863 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -260,7 +260,7 @@
 {
 	struct btrfs_trans_handle *h;
 	struct btrfs_transaction *cur_trans;
-	int retries = 0;
+	u64 num_bytes = 0;
 	int ret;
 
 	if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
@@ -274,6 +274,19 @@
 		h->block_rsv = NULL;
 		goto got_it;
 	}
+
+	/*
+	 * Do the reservation before we join the transaction so we can do all
+	 * the appropriate flushing if need be.
+	 */
+	if (num_items > 0 && root != root->fs_info->chunk_root) {
+		num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
+		ret = btrfs_block_rsv_add(NULL, root,
+					  &root->fs_info->trans_block_rsv,
+					  num_bytes);
+		if (ret)
+			return ERR_PTR(ret);
+	}
 again:
 	h = kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS);
 	if (!h)
@@ -310,24 +323,9 @@
 		goto again;
 	}
 
-	if (num_items > 0) {
-		ret = btrfs_trans_reserve_metadata(h, root, num_items);
-		if (ret == -EAGAIN && !retries) {
-			retries++;
-			btrfs_commit_transaction(h, root);
-			goto again;
-		} else if (ret == -EAGAIN) {
-			/*
-			 * We have already retried and got EAGAIN, so really we
-			 * don't have space, so set ret to -ENOSPC.
-			 */
-			ret = -ENOSPC;
-		}
-
-		if (ret < 0) {
-			btrfs_end_transaction(h, root);
-			return ERR_PTR(ret);
-		}
+	if (num_bytes) {
+		h->block_rsv = &root->fs_info->trans_block_rsv;
+		h->bytes_reserved = num_bytes;
 	}
 
 got_it:
@@ -499,10 +497,17 @@
 	}
 
 	if (lock && cur_trans->blocked && !cur_trans->in_commit) {
-		if (throttle)
+		if (throttle) {
+			/*
+			 * We may race with somebody else here so end up having
+			 * to call end_transaction on ourselves again, so inc
+			 * our use_count.
+			 */
+			trans->use_count++;
 			return btrfs_commit_transaction(trans, root);
-		else
+		} else {
 			wake_up_process(info->transaction_kthread);
+		}
 	}
 
 	WARN_ON(cur_trans != info->running_transaction);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 4ce8a9f..ac278dd 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1730,8 +1730,8 @@
 				btrfs_read_buffer(next, ptr_gen);
 
 				btrfs_tree_lock(next);
-				clean_tree_block(trans, root, next);
 				btrfs_set_lock_blocking(next);
+				clean_tree_block(trans, root, next);
 				btrfs_wait_tree_block_writeback(next);
 				btrfs_tree_unlock(next);
 
@@ -1796,8 +1796,8 @@
 				next = path->nodes[*level];
 
 				btrfs_tree_lock(next);
-				clean_tree_block(trans, root, next);
 				btrfs_set_lock_blocking(next);
+				clean_tree_block(trans, root, next);
 				btrfs_wait_tree_block_writeback(next);
 				btrfs_tree_unlock(next);
 
@@ -1864,8 +1864,8 @@
 			next = path->nodes[orig_level];
 
 			btrfs_tree_lock(next);
-			clean_tree_block(trans, log, next);
 			btrfs_set_lock_blocking(next);
+			clean_tree_block(trans, log, next);
 			btrfs_wait_tree_block_writeback(next);
 			btrfs_tree_unlock(next);
 
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 19450bc..b89e372 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -3595,7 +3595,7 @@
 	if (!sb)
 		return -ENOMEM;
 	btrfs_set_buffer_uptodate(sb);
-	btrfs_set_buffer_lockdep_class(sb, 0);
+	btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0);
 
 	write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
 	array_size = btrfs_super_sys_array_size(super_copy);
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 5366fe4..d733b9c 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -102,43 +102,57 @@
 	if (!path)
 		return -ENOMEM;
 
-	/* first lets see if we already have this xattr */
-	di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
-				strlen(name), -1);
-	if (IS_ERR(di)) {
-		ret = PTR_ERR(di);
-		goto out;
-	}
-
-	/* ok we already have this xattr, lets remove it */
-	if (di) {
-		/* if we want create only exit */
-		if (flags & XATTR_CREATE) {
-			ret = -EEXIST;
+	if (flags & XATTR_REPLACE) {
+		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name,
+					name_len, -1);
+		if (IS_ERR(di)) {
+			ret = PTR_ERR(di);
 			goto out;
-		}
-
-		ret = btrfs_delete_one_dir_name(trans, root, path, di);
-		BUG_ON(ret);
-		btrfs_release_path(path);
-
-		/* if we don't have a value then we are removing the xattr */
-		if (!value)
-			goto out;
-	} else {
-		btrfs_release_path(path);
-
-		if (flags & XATTR_REPLACE) {
-			/* we couldn't find the attr to replace */
+		} else if (!di) {
 			ret = -ENODATA;
 			goto out;
 		}
+		ret = btrfs_delete_one_dir_name(trans, root, path, di);
+		if (ret)
+			goto out;
+		btrfs_release_path(path);
 	}
 
-	/* ok we have to create a completely new xattr */
+again:
 	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode),
 				      name, name_len, value, size);
-	BUG_ON(ret);
+	if (ret == -EEXIST) {
+		if (flags & XATTR_CREATE)
+			goto out;
+		/*
+		 * We can't use the path we already have since we won't have the
+		 * proper locking for a delete, so release the path and
+		 * re-lookup to delete the thing.
+		 */
+		btrfs_release_path(path);
+		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode),
+					name, name_len, -1);
+		if (IS_ERR(di)) {
+			ret = PTR_ERR(di);
+			goto out;
+		} else if (!di) {
+			/* Shouldn't happen but just in case... */
+			btrfs_release_path(path);
+			goto again;
+		}
+
+		ret = btrfs_delete_one_dir_name(trans, root, path, di);
+		if (ret)
+			goto out;
+
+		/*
+		 * We have a value to set, so go back and try to insert it now.
+		 */
+		if (value) {
+			btrfs_release_path(path);
+			goto again;
+		}
+	}
 out:
 	btrfs_free_path(path);
 	return ret;
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index 0dba691..fb962ef 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -102,7 +102,7 @@
 				path = NULL;
 			spin_lock(&req->r_old_dentry->d_lock);
 			seq_printf(s, " #%llx/%.*s (%s)",
-			   ceph_ino(req->r_old_dentry->d_parent->d_inode),
+			   ceph_ino(req->r_old_dentry_dir),
 				   req->r_old_dentry->d_name.len,
 				   req->r_old_dentry->d_name.name,
 				   path ? path : "");
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 1065ac7..382abc9 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -40,14 +40,6 @@
 	if (dentry->d_fsdata)
 		return 0;
 
-	if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
-	    ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
-		d_set_d_op(dentry, &ceph_dentry_ops);
-	else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
-		d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
-	else
-		d_set_d_op(dentry, &ceph_snap_dentry_ops);
-
 	di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
 	if (!di)
 		return -ENOMEM;          /* oh well */
@@ -58,16 +50,42 @@
 		kmem_cache_free(ceph_dentry_cachep, di);
 		goto out_unlock;
 	}
+
+	if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
+	    ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
+		d_set_d_op(dentry, &ceph_dentry_ops);
+	else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
+		d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
+	else
+		d_set_d_op(dentry, &ceph_snap_dentry_ops);
+
 	di->dentry = dentry;
 	di->lease_session = NULL;
-	dentry->d_fsdata = di;
 	dentry->d_time = jiffies;
+	/* avoid reordering d_fsdata setup so that the check above is safe */
+	smp_mb();
+	dentry->d_fsdata = di;
 	ceph_dentry_lru_add(dentry);
 out_unlock:
 	spin_unlock(&dentry->d_lock);
 	return 0;
 }
 
+struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry)
+{
+	struct inode *inode = NULL;
+
+	if (!dentry)
+		return NULL;
+
+	spin_lock(&dentry->d_lock);
+	if (dentry->d_parent) {
+		inode = dentry->d_parent->d_inode;
+		ihold(inode);
+	}
+	spin_unlock(&dentry->d_lock);
+	return inode;
+}
 
 
 /*
@@ -133,7 +151,7 @@
 		     d_unhashed(dentry) ? "!hashed" : "hashed",
 		     parent->d_subdirs.prev, parent->d_subdirs.next);
 		if (p == &parent->d_subdirs) {
-			fi->at_end = 1;
+			fi->flags |= CEPH_F_ATEND;
 			goto out_unlock;
 		}
 		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
@@ -234,7 +252,7 @@
 	const int max_bytes = fsc->mount_options->max_readdir_bytes;
 
 	dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
-	if (fi->at_end)
+	if (fi->flags & CEPH_F_ATEND)
 		return 0;
 
 	/* always start with . and .. */
@@ -403,7 +421,7 @@
 		dout("readdir next frag is %x\n", frag);
 		goto more;
 	}
-	fi->at_end = 1;
+	fi->flags |= CEPH_F_ATEND;
 
 	/*
 	 * if dir_release_count still matches the dir, no dentries
@@ -435,7 +453,7 @@
 		dput(fi->dentry);
 		fi->dentry = NULL;
 	}
-	fi->at_end = 0;
+	fi->flags &= ~CEPH_F_ATEND;
 }
 
 static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
@@ -463,7 +481,7 @@
 		if (offset != file->f_pos) {
 			file->f_pos = offset;
 			file->f_version = 0;
-			fi->at_end = 0;
+			fi->flags &= ~CEPH_F_ATEND;
 		}
 		retval = offset;
 
@@ -488,21 +506,13 @@
 }
 
 /*
- * Process result of a lookup/open request.
- *
- * Mainly, make sure we return the final req->r_dentry (if it already
- * existed) in place of the original VFS-provided dentry when they
- * differ.
- *
- * Gracefully handle the case where the MDS replies with -ENOENT and
- * no trace (which it may do, at its discretion, e.g., if it doesn't
- * care to issue a lease on the negative dentry).
+ * Handle lookups for the hidden .snap directory.
  */
-struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
-				  struct dentry *dentry, int err)
+int ceph_handle_snapdir(struct ceph_mds_request *req,
+			struct dentry *dentry, int err)
 {
 	struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
-	struct inode *parent = dentry->d_parent->d_inode;
+	struct inode *parent = dentry->d_parent->d_inode; /* we hold i_mutex */
 
 	/* .snap dir? */
 	if (err == -ENOENT &&
@@ -516,7 +526,23 @@
 		d_add(dentry, inode);
 		err = 0;
 	}
+	return err;
+}
 
+/*
+ * Figure out final result of a lookup/open request.
+ *
+ * Mainly, make sure we return the final req->r_dentry (if it already
+ * existed) in place of the original VFS-provided dentry when they
+ * differ.
+ *
+ * Gracefully handle the case where the MDS replies with -ENOENT and
+ * no trace (which it may do, at its discretion, e.g., if it doesn't
+ * care to issue a lease on the negative dentry).
+ */
+struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
+				  struct dentry *dentry, int err)
+{
 	if (err == -ENOENT) {
 		/* no trace? */
 		err = 0;
@@ -610,6 +636,7 @@
 	req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
 	req->r_locked_dir = dir;
 	err = ceph_mdsc_do_request(mdsc, NULL, req);
+	err = ceph_handle_snapdir(req, dentry, err);
 	dentry = ceph_finish_lookup(req, dentry, err);
 	ceph_mdsc_put_request(req);  /* will dput(dentry) */
 	dout("lookup result=%p\n", dentry);
@@ -789,6 +816,7 @@
 	req->r_dentry = dget(dentry);
 	req->r_num_caps = 2;
 	req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
+	req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
 	req->r_locked_dir = dir;
 	req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
 	req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
@@ -887,6 +915,7 @@
 	req->r_dentry = dget(new_dentry);
 	req->r_num_caps = 2;
 	req->r_old_dentry = dget(old_dentry);
+	req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
 	req->r_locked_dir = new_dir;
 	req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
 	req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
@@ -1002,36 +1031,38 @@
  */
 static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
+	int valid = 0;
 	struct inode *dir;
 
 	if (nd && nd->flags & LOOKUP_RCU)
 		return -ECHILD;
 
-	dir = dentry->d_parent->d_inode;
-
 	dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
 	     dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
 	     ceph_dentry(dentry)->offset);
 
+	dir = ceph_get_dentry_parent_inode(dentry);
+
 	/* always trust cached snapped dentries, snapdir dentry */
 	if (ceph_snap(dir) != CEPH_NOSNAP) {
 		dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
 		     dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
-		goto out_touch;
+		valid = 1;
+	} else if (dentry->d_inode &&
+		   ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) {
+		valid = 1;
+	} else if (dentry_lease_is_valid(dentry) ||
+		   dir_lease_is_valid(dir, dentry)) {
+		valid = 1;
 	}
-	if (dentry->d_inode && ceph_snap(dentry->d_inode) == CEPH_SNAPDIR)
-		goto out_touch;
 
-	if (dentry_lease_is_valid(dentry) ||
-	    dir_lease_is_valid(dir, dentry))
-		goto out_touch;
-
-	dout("d_revalidate %p invalid\n", dentry);
-	d_drop(dentry);
-	return 0;
-out_touch:
-	ceph_dentry_lru_touch(dentry);
-	return 1;
+	dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid");
+	if (valid)
+		ceph_dentry_lru_touch(dentry);
+	else
+		d_drop(dentry);
+	iput(dir);
+	return valid;
 }
 
 /*
@@ -1228,9 +1259,8 @@
  * Return name hash for a given dentry.  This is dependent on
  * the parent directory's hash function.
  */
-unsigned ceph_dentry_hash(struct dentry *dn)
+unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
 {
-	struct inode *dir = dn->d_parent->d_inode;
 	struct ceph_inode_info *dci = ceph_inode(dir);
 
 	switch (dci->i_dir_layout.dl_dir_hash) {
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index f67b687..9fbcdec 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -46,7 +46,7 @@
 	int type;
 	struct ceph_nfs_fh *fh = (void *)rawfh;
 	struct ceph_nfs_confh *cfh = (void *)rawfh;
-	struct dentry *parent = dentry->d_parent;
+	struct dentry *parent;
 	struct inode *inode = dentry->d_inode;
 	int connected_handle_length = sizeof(*cfh)/4;
 	int handle_length = sizeof(*fh)/4;
@@ -55,26 +55,33 @@
 	if (ceph_snap(inode) != CEPH_NOSNAP)
 		return -EINVAL;
 
+	spin_lock(&dentry->d_lock);
+	parent = dget(dentry->d_parent);
+	spin_unlock(&dentry->d_lock);
+
 	if (*max_len >= connected_handle_length) {
 		dout("encode_fh %p connectable\n", dentry);
 		cfh->ino = ceph_ino(dentry->d_inode);
 		cfh->parent_ino = ceph_ino(parent->d_inode);
-		cfh->parent_name_hash = ceph_dentry_hash(parent);
+		cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode,
+							 dentry);
 		*max_len = connected_handle_length;
 		type = 2;
 	} else if (*max_len >= handle_length) {
 		if (connectable) {
 			*max_len = connected_handle_length;
-			return 255;
+			type = 255;
+		} else {
+			dout("encode_fh %p\n", dentry);
+			fh->ino = ceph_ino(dentry->d_inode);
+			*max_len = handle_length;
+			type = 1;
 		}
-		dout("encode_fh %p\n", dentry);
-		fh->ino = ceph_ino(dentry->d_inode);
-		*max_len = handle_length;
-		type = 1;
 	} else {
 		*max_len = handle_length;
-		return 255;
+		type = 255;
 	}
+	dput(parent);
 	return type;
 }
 
@@ -123,7 +130,6 @@
 		return dentry;
 	}
 	err = ceph_init_dentry(dentry);
-
 	if (err < 0) {
 		iput(inode);
 		return ERR_PTR(err);
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 0d0eae0..ce549d3 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -122,7 +122,7 @@
 	struct ceph_mds_client *mdsc = fsc->mdsc;
 	struct ceph_mds_request *req;
 	struct ceph_file_info *cf = file->private_data;
-	struct inode *parent_inode = file->f_dentry->d_parent->d_inode;
+	struct inode *parent_inode = NULL;
 	int err;
 	int flags, fmode, wanted;
 
@@ -194,7 +194,10 @@
 	req->r_inode = inode;
 	ihold(inode);
 	req->r_num_caps = 1;
+	if (flags & (O_CREAT|O_TRUNC))
+		parent_inode = ceph_get_dentry_parent_inode(file->f_dentry);
 	err = ceph_mdsc_do_request(mdsc, parent_inode, req);
+	iput(parent_inode);
 	if (!err)
 		err = ceph_init_file(inode, file, req->r_fmode);
 	ceph_mdsc_put_request(req);
@@ -222,9 +225,9 @@
 {
 	struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
 	struct ceph_mds_client *mdsc = fsc->mdsc;
-	struct file *file = nd->intent.open.file;
-	struct inode *parent_inode = get_dentry_parent_inode(file->f_dentry);
+	struct file *file;
 	struct ceph_mds_request *req;
+	struct dentry *ret;
 	int err;
 	int flags = nd->intent.open.flags;
 
@@ -242,16 +245,24 @@
 		req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
 	}
 	req->r_locked_dir = dir;           /* caller holds dir->i_mutex */
-	err = ceph_mdsc_do_request(mdsc, parent_inode, req);
-	dentry = ceph_finish_lookup(req, dentry, err);
-	if (!err && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
+	err = ceph_mdsc_do_request(mdsc,
+				   (flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
+				   req);
+	err = ceph_handle_snapdir(req, dentry, err);
+	if (err)
+		goto out;
+	if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
 		err = ceph_handle_notrace_create(dir, dentry);
-	if (!err)
-		err = ceph_init_file(req->r_dentry->d_inode, file,
-				     req->r_fmode);
+	if (err)
+		goto out;
+	file = lookup_instantiate_filp(nd, req->r_dentry, ceph_open);
+	if (IS_ERR(file))
+		err = PTR_ERR(file);
+out:
+	ret = ceph_finish_lookup(req, dentry, err);
 	ceph_mdsc_put_request(req);
-	dout("ceph_lookup_open result=%p\n", dentry);
-	return dentry;
+	dout("ceph_lookup_open result=%p\n", ret);
+	return ret;
 }
 
 int ceph_release(struct inode *inode, struct file *file)
@@ -643,7 +654,8 @@
 
 	if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 ||
 	    (iocb->ki_filp->f_flags & O_DIRECT) ||
-	    (inode->i_sb->s_flags & MS_SYNCHRONOUS))
+	    (inode->i_sb->s_flags & MS_SYNCHRONOUS) ||
+	    (fi->flags & CEPH_F_SYNC))
 		/* hmm, this isn't really async... */
 		ret = ceph_sync_read(filp, base, len, ppos, &checkeof);
 	else
@@ -712,7 +724,7 @@
 		want = CEPH_CAP_FILE_BUFFER;
 	ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, endoff);
 	if (ret < 0)
-		goto out;
+		goto out_put;
 
 	dout("aio_write %p %llx.%llx %llu~%u  got cap refs on %s\n",
 	     inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len,
@@ -720,12 +732,23 @@
 
 	if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 ||
 	    (iocb->ki_filp->f_flags & O_DIRECT) ||
-	    (inode->i_sb->s_flags & MS_SYNCHRONOUS)) {
+	    (inode->i_sb->s_flags & MS_SYNCHRONOUS) ||
+	    (fi->flags & CEPH_F_SYNC)) {
 		ret = ceph_sync_write(file, iov->iov_base, iov->iov_len,
 			&iocb->ki_pos);
 	} else {
-		ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
+		/*
+		 * buffered write; drop Fw early to avoid slow
+		 * revocation if we get stuck on balance_dirty_pages
+		 */
+		int dirty;
 
+		spin_lock(&inode->i_lock);
+		dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
+		spin_unlock(&inode->i_lock);
+		ceph_put_cap_refs(ci, got);
+
+		ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
 		if ((ret >= 0 || ret == -EIOCBQUEUED) &&
 		    ((file->f_flags & O_SYNC) || IS_SYNC(file->f_mapping->host)
 		     || ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_NEARFULL))) {
@@ -733,7 +756,12 @@
 			if (err < 0)
 				ret = err;
 		}
+
+		if (dirty)
+			__mark_inode_dirty(inode, dirty);
+		goto out;
 	}
+
 	if (ret >= 0) {
 		int dirty;
 		spin_lock(&inode->i_lock);
@@ -743,12 +771,13 @@
 			__mark_inode_dirty(inode, dirty);
 	}
 
-out:
+out_put:
 	dout("aio_write %p %llx.%llx %llu~%u  dropping cap refs on %s\n",
 	     inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len,
 	     ceph_cap_string(got));
 	ceph_put_cap_refs(ci, got);
 
+out:
 	if (ret == -EOLDSNAPC) {
 		dout("aio_write %p %llx.%llx %llu~%u got EOLDSNAPC, retrying\n",
 		     inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len);
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index dfb2831..095799b 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -560,7 +560,8 @@
 	struct ceph_mds_reply_inode *info = iinfo->in;
 	struct ceph_inode_info *ci = ceph_inode(inode);
 	int i;
-	int issued, implemented;
+	int issued = 0, implemented;
+	int updating_inode = 0;
 	struct timespec mtime, atime, ctime;
 	u32 nsplits;
 	struct ceph_buffer *xattr_blob = NULL;
@@ -599,7 +600,8 @@
 	if (le64_to_cpu(info->version) > 0 &&
 	    (ci->i_version & ~1) >= le64_to_cpu(info->version))
 		goto no_change;
-
+	
+	updating_inode = 1;
 	issued = __ceph_caps_issued(ci, &implemented);
 	issued |= implemented | __ceph_caps_dirty(ci);
 
@@ -707,17 +709,6 @@
 		ci->i_rfiles = le64_to_cpu(info->rfiles);
 		ci->i_rsubdirs = le64_to_cpu(info->rsubdirs);
 		ceph_decode_timespec(&ci->i_rctime, &info->rctime);
-
-		/* set dir completion flag? */
-		if (ci->i_files == 0 && ci->i_subdirs == 0 &&
-		    ceph_snap(inode) == CEPH_NOSNAP &&
-		    (le32_to_cpu(info->cap.caps) & CEPH_CAP_FILE_SHARED) &&
-		    (issued & CEPH_CAP_FILE_EXCL) == 0 &&
-		    (ci->i_ceph_flags & CEPH_I_COMPLETE) == 0) {
-			dout(" marking %p complete (empty)\n", inode);
-			/* ci->i_ceph_flags |= CEPH_I_COMPLETE; */
-			ci->i_max_offset = 2;
-		}
 		break;
 	default:
 		pr_err("fill_inode %llx.%llx BAD mode 0%o\n",
@@ -774,6 +765,19 @@
 		__ceph_get_fmode(ci, cap_fmode);
 	}
 
+	/* set dir completion flag? */
+	if (S_ISDIR(inode->i_mode) &&
+	    updating_inode &&                 /* didn't jump to no_change */
+	    ci->i_files == 0 && ci->i_subdirs == 0 &&
+	    ceph_snap(inode) == CEPH_NOSNAP &&
+	    (le32_to_cpu(info->cap.caps) & CEPH_CAP_FILE_SHARED) &&
+	    (issued & CEPH_CAP_FILE_EXCL) == 0 &&
+	    (ci->i_ceph_flags & CEPH_I_COMPLETE) == 0) {
+		dout(" marking %p complete (empty)\n", inode);
+		/* ci->i_ceph_flags |= CEPH_I_COMPLETE; */
+		ci->i_max_offset = 2;
+	}
+
 	/* update delegation info? */
 	if (dirinfo)
 		ceph_fill_dirfrag(inode, dirinfo);
@@ -805,14 +809,14 @@
 		return;
 
 	spin_lock(&dentry->d_lock);
-	dout("update_dentry_lease %p mask %d duration %lu ms ttl %lu\n",
-	     dentry, le16_to_cpu(lease->mask), duration, ttl);
+	dout("update_dentry_lease %p duration %lu ms ttl %lu\n",
+	     dentry, duration, ttl);
 
 	/* make lease_rdcache_gen match directory */
 	dir = dentry->d_parent->d_inode;
 	di->lease_shared_gen = ceph_inode(dir)->i_shared_gen;
 
-	if (lease->mask == 0)
+	if (duration == 0)
 		goto out_unlock;
 
 	if (di->lease_gen == session->s_cap_gen &&
@@ -839,11 +843,13 @@
 /*
  * Set dentry's directory position based on the current dir's max, and
  * order it in d_subdirs, so that dcache_readdir behaves.
+ *
+ * Always called under directory's i_mutex.
  */
 static void ceph_set_dentry_offset(struct dentry *dn)
 {
 	struct dentry *dir = dn->d_parent;
-	struct inode *inode = dn->d_parent->d_inode;
+	struct inode *inode = dir->d_inode;
 	struct ceph_dentry_info *di;
 
 	BUG_ON(!inode);
@@ -1022,9 +1028,7 @@
 
 		/* do we have a dn lease? */
 		have_lease = have_dir_cap ||
-			(le16_to_cpu(rinfo->dlease->mask) &
-			 CEPH_LOCK_DN);
-
+			le32_to_cpu(rinfo->dlease->duration_ms);
 		if (!have_lease)
 			dout("fill_trace  no dentry lease or dir cap\n");
 
@@ -1560,7 +1564,7 @@
 {
 	struct inode *inode = dentry->d_inode;
 	struct ceph_inode_info *ci = ceph_inode(inode);
-	struct inode *parent_inode = dentry->d_parent->d_inode;
+	struct inode *parent_inode;
 	const unsigned int ia_valid = attr->ia_valid;
 	struct ceph_mds_request *req;
 	struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc;
@@ -1743,7 +1747,9 @@
 		req->r_inode_drop = release;
 		req->r_args.setattr.mask = cpu_to_le32(mask);
 		req->r_num_caps = 1;
+		parent_inode = ceph_get_dentry_parent_inode(dentry);
 		err = ceph_mdsc_do_request(mdsc, parent_inode, req);
+		iput(parent_inode);
 	}
 	dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err,
 	     ceph_cap_string(dirtied), mask);
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index ef0b5f4..3b256b5 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -38,7 +38,7 @@
 static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
 {
 	struct inode *inode = file->f_dentry->d_inode;
-	struct inode *parent_inode = file->f_dentry->d_parent->d_inode;
+	struct inode *parent_inode;
 	struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
 	struct ceph_mds_request *req;
 	struct ceph_ioctl_layout l;
@@ -87,7 +87,9 @@
 	req->r_args.setlayout.layout.fl_pg_preferred =
 		cpu_to_le32(l.preferred_osd);
 
+	parent_inode = ceph_get_dentry_parent_inode(file->f_dentry);
 	err = ceph_mdsc_do_request(mdsc, parent_inode, req);
+	iput(parent_inode);
 	ceph_mdsc_put_request(req);
 	return err;
 }
@@ -231,6 +233,14 @@
 	return 0;
 }
 
+static long ceph_ioctl_syncio(struct file *file)
+{
+	struct ceph_file_info *fi = file->private_data;
+
+	fi->flags |= CEPH_F_SYNC;
+	return 0;
+}
+
 long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
@@ -249,6 +259,9 @@
 
 	case CEPH_IOC_LAZYIO:
 		return ceph_ioctl_lazyio(file);
+
+	case CEPH_IOC_SYNCIO:
+		return ceph_ioctl_syncio(file);
 	}
 
 	return -ENOTTY;
diff --git a/fs/ceph/ioctl.h b/fs/ceph/ioctl.h
index 52e8fd7..0c5167e 100644
--- a/fs/ceph/ioctl.h
+++ b/fs/ceph/ioctl.h
@@ -40,5 +40,6 @@
 				   struct ceph_ioctl_dataloc)
 
 #define CEPH_IOC_LAZYIO _IO(CEPH_IOCTL_MAGIC, 4)
+#define CEPH_IOC_SYNCIO _IO(CEPH_IOCTL_MAGIC, 5)
 
 #endif
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 0c1d917..fee028b 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -483,22 +483,26 @@
 		destroy_reply_info(&req->r_reply_info);
 	}
 	if (req->r_inode) {
-		ceph_put_cap_refs(ceph_inode(req->r_inode),
-				  CEPH_CAP_PIN);
+		ceph_put_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
 		iput(req->r_inode);
 	}
 	if (req->r_locked_dir)
-		ceph_put_cap_refs(ceph_inode(req->r_locked_dir),
-				  CEPH_CAP_PIN);
+		ceph_put_cap_refs(ceph_inode(req->r_locked_dir), CEPH_CAP_PIN);
 	if (req->r_target_inode)
 		iput(req->r_target_inode);
 	if (req->r_dentry)
 		dput(req->r_dentry);
 	if (req->r_old_dentry) {
-		ceph_put_cap_refs(
-			ceph_inode(req->r_old_dentry->d_parent->d_inode),
-			CEPH_CAP_PIN);
+		/*
+		 * track (and drop pins for) r_old_dentry_dir
+		 * separately, since r_old_dentry's d_parent may have
+		 * changed between the dir mutex being dropped and
+		 * this request being freed.
+		 */
+		ceph_put_cap_refs(ceph_inode(req->r_old_dentry_dir),
+				  CEPH_CAP_PIN);
 		dput(req->r_old_dentry);
+		iput(req->r_old_dentry_dir);
 	}
 	kfree(req->r_path1);
 	kfree(req->r_path2);
@@ -617,6 +621,12 @@
  */
 struct dentry *get_nonsnap_parent(struct dentry *dentry)
 {
+	/*
+	 * we don't need to worry about protecting the d_parent access
+	 * here because we never renaming inside the snapped namespace
+	 * except to resplice to another snapdir, and either the old or new
+	 * result is a valid result.
+	 */
 	while (!IS_ROOT(dentry) && ceph_snap(dentry->d_inode) != CEPH_NOSNAP)
 		dentry = dentry->d_parent;
 	return dentry;
@@ -652,7 +662,9 @@
 	if (req->r_inode) {
 		inode = req->r_inode;
 	} else if (req->r_dentry) {
-		struct inode *dir = req->r_dentry->d_parent->d_inode;
+		/* ignore race with rename; old or new d_parent is okay */
+		struct dentry *parent = req->r_dentry->d_parent;
+		struct inode *dir = parent->d_inode;
 
 		if (dir->i_sb != mdsc->fsc->sb) {
 			/* not this fs! */
@@ -660,8 +672,7 @@
 		} else if (ceph_snap(dir) != CEPH_NOSNAP) {
 			/* direct snapped/virtual snapdir requests
 			 * based on parent dir inode */
-			struct dentry *dn =
-				get_nonsnap_parent(req->r_dentry->d_parent);
+			struct dentry *dn = get_nonsnap_parent(parent);
 			inode = dn->d_inode;
 			dout("__choose_mds using nonsnap parent %p\n", inode);
 		} else if (req->r_dentry->d_inode) {
@@ -670,7 +681,7 @@
 		} else {
 			/* dir + name */
 			inode = dir;
-			hash = ceph_dentry_hash(req->r_dentry);
+			hash = ceph_dentry_hash(dir, req->r_dentry);
 			is_hash = true;
 		}
 	}
@@ -1931,9 +1942,8 @@
 	if (req->r_locked_dir)
 		ceph_get_cap_refs(ceph_inode(req->r_locked_dir), CEPH_CAP_PIN);
 	if (req->r_old_dentry)
-		ceph_get_cap_refs(
-			ceph_inode(req->r_old_dentry->d_parent->d_inode),
-			CEPH_CAP_PIN);
+		ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir),
+				  CEPH_CAP_PIN);
 
 	/* issue */
 	mutex_lock(&mdsc->mutex);
@@ -2714,7 +2724,6 @@
 	struct ceph_mds_lease *h = msg->front.iov_base;
 	u32 seq;
 	struct ceph_vino vino;
-	int mask;
 	struct qstr dname;
 	int release = 0;
 
@@ -2725,7 +2734,6 @@
 		goto bad;
 	vino.ino = le64_to_cpu(h->ino);
 	vino.snap = CEPH_NOSNAP;
-	mask = le16_to_cpu(h->mask);
 	seq = le32_to_cpu(h->seq);
 	dname.name = (void *)h + sizeof(*h) + sizeof(u32);
 	dname.len = msg->front.iov_len - sizeof(*h) - sizeof(u32);
@@ -2737,8 +2745,8 @@
 
 	/* lookup inode */
 	inode = ceph_find_inode(sb, vino);
-	dout("handle_lease %s, mask %d, ino %llx %p %.*s\n",
-	     ceph_lease_op_name(h->action), mask, vino.ino, inode,
+	dout("handle_lease %s, ino %llx %p %.*s\n",
+	     ceph_lease_op_name(h->action), vino.ino, inode,
 	     dname.len, dname.name);
 	if (inode == NULL) {
 		dout("handle_lease no inode %llx\n", vino.ino);
@@ -2828,7 +2836,6 @@
 		return;
 	lease = msg->front.iov_base;
 	lease->action = action;
-	lease->mask = cpu_to_le16(1);
 	lease->ino = cpu_to_le64(ceph_vino(inode).ino);
 	lease->first = lease->last = cpu_to_le64(ceph_vino(inode).snap);
 	lease->seq = cpu_to_le32(seq);
@@ -2850,7 +2857,7 @@
  * Pass @inode always, @dentry is optional.
  */
 void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc, struct inode *inode,
-			     struct dentry *dentry, int mask)
+			     struct dentry *dentry)
 {
 	struct ceph_dentry_info *di;
 	struct ceph_mds_session *session;
@@ -2858,7 +2865,6 @@
 
 	BUG_ON(inode == NULL);
 	BUG_ON(dentry == NULL);
-	BUG_ON(mask == 0);
 
 	/* is dentry lease valid? */
 	spin_lock(&dentry->d_lock);
@@ -2868,8 +2874,8 @@
 	    di->lease_gen != di->lease_session->s_cap_gen ||
 	    !time_before(jiffies, dentry->d_time)) {
 		dout("lease_release inode %p dentry %p -- "
-		     "no lease on %d\n",
-		     inode, dentry, mask);
+		     "no lease\n",
+		     inode, dentry);
 		spin_unlock(&dentry->d_lock);
 		return;
 	}
@@ -2880,8 +2886,8 @@
 	__ceph_mdsc_drop_dentry_lease(dentry);
 	spin_unlock(&dentry->d_lock);
 
-	dout("lease_release inode %p dentry %p mask %d to mds%d\n",
-	     inode, dentry, mask, session->s_mds);
+	dout("lease_release inode %p dentry %p to mds%d\n",
+	     inode, dentry, session->s_mds);
 	ceph_mdsc_lease_send_msg(session, inode, dentry,
 				 CEPH_MDS_LEASE_RELEASE, seq);
 	ceph_put_mds_session(session);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 7d8a0d6..4bb2399 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -171,6 +171,7 @@
 	struct inode *r_inode;              /* arg1 */
 	struct dentry *r_dentry;            /* arg1 */
 	struct dentry *r_old_dentry;        /* arg2: rename from or link from */
+	struct inode *r_old_dentry_dir;     /* arg2: old dentry's parent dir */
 	char *r_path1, *r_path2;
 	struct ceph_vino r_ino1, r_ino2;
 
@@ -333,7 +334,7 @@
 
 extern void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc,
 				    struct inode *inode,
-				    struct dentry *dn, int mask);
+				    struct dentry *dn);
 
 extern void ceph_invalidate_dir_request(struct ceph_mds_request *req);
 
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index 54b14de..e264371 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -449,6 +449,15 @@
 	spin_lock(&inode->i_lock);
 	used = __ceph_caps_used(ci);
 	dirty = __ceph_caps_dirty(ci);
+
+	/*
+	 * If there is a write in progress, treat that as a dirty Fw,
+	 * even though it hasn't completed yet; by the time we finish
+	 * up this capsnap it will be.
+	 */
+	if (used & CEPH_CAP_FILE_WR)
+		dirty |= CEPH_CAP_FILE_WR;
+
 	if (__ceph_have_pending_cap_snap(ci)) {
 		/* there is no point in queuing multiple "pending" cap_snaps,
 		   as no new writes are allowed to start when pending, so any
@@ -456,13 +465,19 @@
 		   cap_snap.  lucky us. */
 		dout("queue_cap_snap %p already pending\n", inode);
 		kfree(capsnap);
-	} else if (ci->i_wrbuffer_ref_head || (used & CEPH_CAP_FILE_WR) ||
-		   (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL|
-			     CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR))) {
+	} else if (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL|
+			    CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR)) {
 		struct ceph_snap_context *snapc = ci->i_head_snapc;
 
-		dout("queue_cap_snap %p cap_snap %p queuing under %p\n", inode,
-		     capsnap, snapc);
+		/*
+		 * if we are a sync write, we may need to go to the snaprealm
+		 * to get the current snapc.
+		 */
+		if (!snapc)
+			snapc = ci->i_snap_realm->cached_context;
+
+		dout("queue_cap_snap %p cap_snap %p queuing under %p %s\n",
+		     inode, capsnap, snapc, ceph_cap_string(dirty));
 		ihold(inode);
 
 		atomic_set(&capsnap->nref, 1);
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index f2f77fd..d47c5ec 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -73,8 +73,7 @@
 	 */
 	buf->f_bsize = 1 << CEPH_BLOCK_SHIFT;
 	buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);
-	buf->f_bfree = (le64_to_cpu(st.kb) - le64_to_cpu(st.kb_used)) >>
-		(CEPH_BLOCK_SHIFT-10);
+	buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
 	buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
 
 	buf->f_files = le64_to_cpu(st.num_objects);
@@ -780,6 +779,10 @@
 		fsc->backing_dev_info.ra_pages =
 			(fsc->mount_options->rsize + PAGE_CACHE_SIZE - 1)
 			>> PAGE_SHIFT;
+	else
+		fsc->backing_dev_info.ra_pages =
+			default_backing_dev_info.ra_pages;
+
 	err = bdi_register(&fsc->backing_dev_info, NULL, "ceph-%d",
 			   atomic_long_inc_return(&bdi_seq));
 	if (!err)
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 30446b1..a23eed5 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -543,13 +543,16 @@
 /*
  * we keep buffered readdir results attached to file->private_data
  */
+#define CEPH_F_SYNC     1
+#define CEPH_F_ATEND    2
+
 struct ceph_file_info {
-	int fmode;     /* initialized on open */
+	short fmode;     /* initialized on open */
+	short flags;     /* CEPH_F_* */
 
 	/* readdir: position within the dir */
 	u32 frag;
 	struct ceph_mds_request *last_readdir;
-	int at_end;
 
 	/* readdir: position within a frag */
 	unsigned offset;       /* offset of last chunk, adjusted for . and .. */
@@ -789,6 +792,8 @@
 	ceph_snapdir_dentry_ops;
 
 extern int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry);
+extern int ceph_handle_snapdir(struct ceph_mds_request *req,
+			       struct dentry *dentry, int err);
 extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
 					 struct dentry *dentry, int err);
 
@@ -796,7 +801,8 @@
 extern void ceph_dentry_lru_touch(struct dentry *dn);
 extern void ceph_dentry_lru_del(struct dentry *dn);
 extern void ceph_invalidate_dentry_lease(struct dentry *dentry);
-extern unsigned ceph_dentry_hash(struct dentry *dn);
+extern unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn);
+extern struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry);
 
 /*
  * our d_ops vary depending on whether the inode is live,
@@ -819,14 +825,6 @@
 			     int p_locks, int f_locks);
 extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c);
 
-static inline struct inode *get_dentry_parent_inode(struct dentry *dentry)
-{
-	if (dentry && dentry->d_parent)
-		return dentry->d_parent->d_inode;
-
-	return NULL;
-}
-
 /* debugfs.c */
 extern int ceph_fs_debugfs_init(struct ceph_fs_client *client);
 extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client);
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index f42d730..96c6739 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -629,7 +629,7 @@
 	struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
 	struct inode *inode = dentry->d_inode;
 	struct ceph_inode_info *ci = ceph_inode(inode);
-	struct inode *parent_inode = dentry->d_parent->d_inode;
+	struct inode *parent_inode;
 	struct ceph_mds_request *req;
 	struct ceph_mds_client *mdsc = fsc->mdsc;
 	int err;
@@ -677,7 +677,9 @@
 	req->r_data_len = size;
 
 	dout("xattr.ver (before): %lld\n", ci->i_xattrs.version);
+	parent_inode = ceph_get_dentry_parent_inode(dentry);
 	err = ceph_mdsc_do_request(mdsc, parent_inode, req);
+	iput(parent_inode);
 	ceph_mdsc_put_request(req);
 	dout("xattr.ver (after): %lld\n", ci->i_xattrs.version);
 
@@ -788,7 +790,7 @@
 	struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
 	struct ceph_mds_client *mdsc = fsc->mdsc;
 	struct inode *inode = dentry->d_inode;
-	struct inode *parent_inode = dentry->d_parent->d_inode;
+	struct inode *parent_inode;
 	struct ceph_mds_request *req;
 	int err;
 
@@ -802,7 +804,9 @@
 	req->r_num_caps = 1;
 	req->r_path2 = kstrdup(name, GFP_NOFS);
 
+	parent_inode = ceph_get_dentry_parent_inode(dentry);
 	err = ceph_mdsc_do_request(mdsc, parent_inode, req);
+	iput(parent_inode);
 	ceph_mdsc_put_request(req);
 	return err;
 }
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 5a0ee7f..259991b 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -52,19 +52,29 @@
 
 	rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
 	if (rc) {
-		cERROR(1, "%s: Oould not init md5\n", __func__);
+		cERROR(1, "%s: Could not init md5\n", __func__);
 		return rc;
 	}
 
-	crypto_shash_update(&server->secmech.sdescmd5->shash,
+	rc = crypto_shash_update(&server->secmech.sdescmd5->shash,
 		server->session_key.response, server->session_key.len);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		return rc;
+	}
 
-	crypto_shash_update(&server->secmech.sdescmd5->shash,
+	rc = crypto_shash_update(&server->secmech.sdescmd5->shash,
 		cifs_pdu->Protocol, be32_to_cpu(cifs_pdu->smb_buf_length));
+	if (rc) {
+		cERROR(1, "%s: Could not update with payload\n", __func__);
+		return rc;
+	}
 
 	rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
-	return 0;
+	return rc;
 }
 
 /* must be called with server->srv_mutex held */
@@ -112,12 +122,16 @@
 
 	rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
 	if (rc) {
-		cERROR(1, "%s: Oould not init md5\n", __func__);
+		cERROR(1, "%s: Could not init md5\n", __func__);
 		return rc;
 	}
 
-	crypto_shash_update(&server->secmech.sdescmd5->shash,
+	rc = crypto_shash_update(&server->secmech.sdescmd5->shash,
 		server->session_key.response, server->session_key.len);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		return rc;
+	}
 
 	for (i = 0; i < n_vec; i++) {
 		if (iov[i].iov_len == 0)
@@ -131,14 +145,24 @@
 		if (i == 0) {
 			if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
 				break; /* nothing to sign or corrupt header */
+			rc =
 			crypto_shash_update(&server->secmech.sdescmd5->shash,
 				iov[i].iov_base + 4, iov[i].iov_len - 4);
-		} else
+		} else {
+			rc =
 			crypto_shash_update(&server->secmech.sdescmd5->shash,
 				iov[i].iov_base, iov[i].iov_len);
+		}
+		if (rc) {
+			cERROR(1, "%s: Could not update with payload\n",
+							__func__);
+			return rc;
+		}
 	}
 
 	rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
 	return rc;
 }
@@ -463,8 +487,12 @@
 	/* calculate md4 hash of password */
 	E_md4hash(ses->password, nt_hash);
 
-	crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
+	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
 				CIFS_NTHASH_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not set NT Hash as a key", __func__);
+		return rc;
+	}
 
 	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
 	if (rc) {
@@ -478,13 +506,18 @@
 	if (user == NULL) {
 		cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n");
 		rc = -ENOMEM;
-		goto calc_exit_2;
+		return rc;
 	}
 	len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp);
 	UniStrupr(user);
 
-	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
 				(char *)user, 2 * len);
+	kfree(user);
+	if (rc) {
+		cERROR(1, "%s: Could not update with user\n", __func__);
+		return rc;
+	}
 
 	/* convert ses->domainName to unicode and uppercase */
 	if (ses->domainName) {
@@ -494,13 +527,19 @@
 		if (domain == NULL) {
 			cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure");
 			rc = -ENOMEM;
-			goto calc_exit_1;
+			return rc;
 		}
 		len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len,
 					nls_cp);
+		rc =
 		crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
 					(char *)domain, 2 * len);
 		kfree(domain);
+		if (rc) {
+			cERROR(1, "%s: Could not update with domain\n",
+								__func__);
+			return rc;
+		}
 	} else if (ses->serverName) {
 		len = strlen(ses->serverName);
 
@@ -508,21 +547,26 @@
 		if (server == NULL) {
 			cERROR(1, "calc_ntlmv2_hash: server mem alloc failure");
 			rc = -ENOMEM;
-			goto calc_exit_1;
+			return rc;
 		}
 		len = cifs_strtoUCS((__le16 *)server, ses->serverName, len,
 					nls_cp);
+		rc =
 		crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
 					(char *)server, 2 * len);
 		kfree(server);
+		if (rc) {
+			cERROR(1, "%s: Could not update with server\n",
+								__func__);
+			return rc;
+		}
 	}
 
 	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
 					ntlmv2_hash);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
-calc_exit_1:
-	kfree(user);
-calc_exit_2:
 	return rc;
 }
 
@@ -537,8 +581,12 @@
 		return -1;
 	}
 
-	crypto_shash_setkey(ses->server->secmech.hmacmd5,
+	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
 				ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not set NTLMV2 Hash as a key", __func__);
+		return rc;
+	}
 
 	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
 	if (rc) {
@@ -552,11 +600,17 @@
 	else
 		memcpy(ses->auth_key.response + offset,
 			ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
-	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
 		ses->auth_key.response + offset, ses->auth_key.len - offset);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		return rc;
+	}
 
 	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
 		ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
 	return rc;
 }
@@ -626,8 +680,12 @@
 	}
 
 	/* now calculate the session key for NTLMv2 */
-	crypto_shash_setkey(ses->server->secmech.hmacmd5,
+	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
 		ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not set NTLMV2 Hash as a key", __func__);
+		goto setup_ntlmv2_rsp_ret;
+	}
 
 	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
 	if (rc) {
@@ -635,12 +693,18 @@
 		goto setup_ntlmv2_rsp_ret;
 	}
 
-	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
 		ses->auth_key.response + CIFS_SESS_KEY_SIZE,
 		CIFS_HMAC_MD5_HASH_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not update with response\n", __func__);
+		goto setup_ntlmv2_rsp_ret;
+	}
 
 	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
 		ses->auth_key.response);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
 setup_ntlmv2_rsp_ret:
 	kfree(tiblob);
@@ -668,8 +732,12 @@
 
 	desc.tfm = tfm_arc4;
 
-	crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,
+	rc = crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,
 					CIFS_SESS_KEY_SIZE);
+	if (rc) {
+		cERROR(1, "%s: Could not set response as a key", __func__);
+		return rc;
+	}
 
 	sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE);
 	sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
@@ -688,7 +756,7 @@
 
 	crypto_free_blkcipher(tfm_arc4);
 
-	return 0;
+	return rc;
 }
 
 void
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6255fa8..1fcf4e5 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -501,7 +501,7 @@
 	char *ntwrk_buf_start;
 	char *srch_entries_start;
 	char *last_entry;
-	char *presume_name;
+	const char *presume_name;
 	unsigned int resume_name_len;
 	bool endOfSearch:1;
 	bool emptyDir:1;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 499f27f..ae576fb 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -57,11 +57,6 @@
 	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 	unsigned seq;
 
-	if (direntry == NULL)
-		return NULL;  /* not much we can do if dentry is freed and
-		we need to reopen the file after it was closed implicitly
-		when the server crashed */
-
 	dirsep = CIFS_DIR_SEP(cifs_sb);
 	if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
 		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 556b1a0..db3f18c 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -74,8 +74,14 @@
 		cERROR(1, "%s: Could not init md5 shash\n", __func__);
 		goto symlink_hash_err;
 	}
-	crypto_shash_update(&sdescmd5->shash, link_str, link_len);
+	rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len);
+	if (rc) {
+		cERROR(1, "%s: Could not update iwth link_str\n", __func__);
+		goto symlink_hash_err;
+	}
 	rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
+	if (rc)
+		cERROR(1, "%s: Could not generate md5 hash\n", __func__);
 
 symlink_hash_err:
 	crypto_free_shash(md5);
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 965a3af..5de03ec 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -4,6 +4,7 @@
  *   Directory search handling
  *
  *   Copyright (C) International Business Machines  Corp., 2004, 2008
+ *   Copyright (C) Red Hat, Inc., 2011
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -290,10 +291,10 @@
 }
 
 /* return length of unicode string in bytes */
-static int cifs_unicode_bytelen(char *str)
+static int cifs_unicode_bytelen(const char *str)
 {
 	int len;
-	__le16 *ustr = (__le16 *)str;
+	const __le16 *ustr = (const __le16 *)str;
 
 	for (len = 0; len <= PATH_MAX; len++) {
 		if (ustr[len] == 0)
@@ -334,78 +335,128 @@
 
 }
 
+struct cifs_dirent {
+	const char	*name;
+	size_t		namelen;
+	u32		resume_key;
+	u64		ino;
+};
+
+static void cifs_fill_dirent_unix(struct cifs_dirent *de,
+		const FILE_UNIX_INFO *info, bool is_unicode)
+{
+	de->name = &info->FileName[0];
+	if (is_unicode)
+		de->namelen = cifs_unicode_bytelen(de->name);
+	else
+		de->namelen = strnlen(de->name, PATH_MAX);
+	de->resume_key = info->ResumeKey;
+	de->ino = le64_to_cpu(info->basic.UniqueId);
+}
+
+static void cifs_fill_dirent_dir(struct cifs_dirent *de,
+		const FILE_DIRECTORY_INFO *info)
+{
+	de->name = &info->FileName[0];
+	de->namelen = le32_to_cpu(info->FileNameLength);
+	de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_full(struct cifs_dirent *de,
+		const FILE_FULL_DIRECTORY_INFO *info)
+{
+	de->name = &info->FileName[0];
+	de->namelen = le32_to_cpu(info->FileNameLength);
+	de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_search(struct cifs_dirent *de,
+		const SEARCH_ID_FULL_DIR_INFO *info)
+{
+	de->name = &info->FileName[0];
+	de->namelen = le32_to_cpu(info->FileNameLength);
+	de->resume_key = info->FileIndex;
+	de->ino = le64_to_cpu(info->UniqueId);
+}
+
+static void cifs_fill_dirent_both(struct cifs_dirent *de,
+		const FILE_BOTH_DIRECTORY_INFO *info)
+{
+	de->name = &info->FileName[0];
+	de->namelen = le32_to_cpu(info->FileNameLength);
+	de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_std(struct cifs_dirent *de,
+		const FIND_FILE_STANDARD_INFO *info)
+{
+	de->name = &info->FileName[0];
+	/* one byte length, no endianess conversion */
+	de->namelen = info->FileNameLength;
+	de->resume_key = info->ResumeKey;
+}
+
+static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
+		u16 level, bool is_unicode)
+{
+	memset(de, 0, sizeof(*de));
+
+	switch (level) {
+	case SMB_FIND_FILE_UNIX:
+		cifs_fill_dirent_unix(de, info, is_unicode);
+		break;
+	case SMB_FIND_FILE_DIRECTORY_INFO:
+		cifs_fill_dirent_dir(de, info);
+		break;
+	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+		cifs_fill_dirent_full(de, info);
+		break;
+	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
+		cifs_fill_dirent_search(de, info);
+		break;
+	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+		cifs_fill_dirent_both(de, info);
+		break;
+	case SMB_FIND_FILE_INFO_STANDARD:
+		cifs_fill_dirent_std(de, info);
+		break;
+	default:
+		cFYI(1, "Unknown findfirst level %d", level);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 #define UNICODE_DOT cpu_to_le16(0x2e)
 
 /* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
-static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
+static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
 {
 	int rc = 0;
-	char *filename = NULL;
-	int len = 0;
 
-	if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
-		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		if (cfile->srch_inf.unicode) {
-			len = cifs_unicode_bytelen(filename);
-		} else {
-			/* BB should we make this strnlen of PATH_MAX? */
-			len = strnlen(filename, 5);
+	if (!de->name)
+		return 0;
+
+	if (is_unicode) {
+		__le16 *ufilename = (__le16 *)de->name;
+		if (de->namelen == 2) {
+			/* check for . */
+			if (ufilename[0] == UNICODE_DOT)
+				rc = 1;
+		} else if (de->namelen == 4) {
+			/* check for .. */
+			if (ufilename[0] == UNICODE_DOT &&
+			    ufilename[1] == UNICODE_DOT)
+				rc = 2;
 		}
-	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
-		FILE_DIRECTORY_INFO *pFindData =
-			(FILE_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (cfile->srch_inf.info_level ==
-			SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
-		FILE_FULL_DIRECTORY_INFO *pFindData =
-			(FILE_FULL_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (cfile->srch_inf.info_level ==
-			SMB_FIND_FILE_ID_FULL_DIR_INFO) {
-		SEARCH_ID_FULL_DIR_INFO *pFindData =
-			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (cfile->srch_inf.info_level ==
-			SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
-		FILE_BOTH_DIRECTORY_INFO *pFindData =
-			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
-		FIND_FILE_STANDARD_INFO *pFindData =
-			(FIND_FILE_STANDARD_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = pFindData->FileNameLength;
-	} else {
-		cFYI(1, "Unknown findfirst level %d",
-			 cfile->srch_inf.info_level);
-	}
-
-	if (filename) {
-		if (cfile->srch_inf.unicode) {
-			__le16 *ufilename = (__le16 *)filename;
-			if (len == 2) {
-				/* check for . */
-				if (ufilename[0] == UNICODE_DOT)
-					rc = 1;
-			} else if (len == 4) {
-				/* check for .. */
-				if ((ufilename[0] == UNICODE_DOT)
-				   && (ufilename[1] == UNICODE_DOT))
-					rc = 2;
-			}
-		} else /* ASCII */ {
-			if (len == 1) {
-				if (filename[0] == '.')
-					rc = 1;
-			} else if (len == 2) {
-				if ((filename[0] == '.') && (filename[1] == '.'))
-					rc = 2;
-			}
+	} else /* ASCII */ {
+		if (de->namelen == 1) {
+			if (de->name[0] == '.')
+				rc = 1;
+		} else if (de->namelen == 2) {
+			if (de->name[0] == '.' && de->name[1] == '.')
+				rc = 2;
 		}
 	}
 
@@ -427,66 +478,18 @@
 }
 
 static int cifs_save_resume_key(const char *current_entry,
-	struct cifsFileInfo *cifsFile)
+	struct cifsFileInfo *file_info)
 {
-	int rc = 0;
-	unsigned int len = 0;
-	__u16 level;
-	char *filename;
+	struct cifs_dirent de;
+	int rc;
 
-	if ((cifsFile == NULL) || (current_entry == NULL))
-		return -EINVAL;
-
-	level = cifsFile->srch_inf.info_level;
-
-	if (level == SMB_FIND_FILE_UNIX) {
-		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
-
-		filename = &pFindData->FileName[0];
-		if (cifsFile->srch_inf.unicode) {
-			len = cifs_unicode_bytelen(filename);
-		} else {
-			/* BB should we make this strnlen of PATH_MAX? */
-			len = strnlen(filename, PATH_MAX);
-		}
-		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
-	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
-		FILE_DIRECTORY_INFO *pFindData =
-			(FILE_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
-	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
-		FILE_FULL_DIRECTORY_INFO *pFindData =
-			(FILE_FULL_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
-	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
-		SEARCH_ID_FULL_DIR_INFO *pFindData =
-			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
-	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
-		FILE_BOTH_DIRECTORY_INFO *pFindData =
-			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
-	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
-		FIND_FILE_STANDARD_INFO *pFindData =
-			(FIND_FILE_STANDARD_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		/* one byte length, no name conversion */
-		len = (unsigned int)pFindData->FileNameLength;
-		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
-	} else {
-		cFYI(1, "Unknown findfirst level %d", level);
-		return -EINVAL;
+	rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
+			      file_info->srch_inf.unicode);
+	if (!rc) {
+		file_info->srch_inf.presume_name = de.name;
+		file_info->srch_inf.resume_name_len = de.namelen;
+		file_info->srch_inf.resume_key = de.resume_key;
 	}
-	cifsFile->srch_inf.resume_name_len = len;
-	cifsFile->srch_inf.presume_name = filename;
 	return rc;
 }
 
@@ -605,136 +608,70 @@
 	return rc;
 }
 
-/* inode num, inode type and filename returned */
-static int cifs_get_name_from_search_buf(struct qstr *pqst,
-	char *current_entry, __u16 level, unsigned int unicode,
-	struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
+static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir,
+		void *dirent, char *scratch_buf, unsigned int max_len)
 {
-	int rc = 0;
-	unsigned int len = 0;
-	char *filename;
-	struct nls_table *nlt = cifs_sb->local_nls;
-
-	*pinum = 0;
-
-	if (level == SMB_FIND_FILE_UNIX) {
-		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
-
-		filename = &pFindData->FileName[0];
-		if (unicode) {
-			len = cifs_unicode_bytelen(filename);
-		} else {
-			/* BB should we make this strnlen of PATH_MAX? */
-			len = strnlen(filename, PATH_MAX);
-		}
-
-		*pinum = le64_to_cpu(pFindData->basic.UniqueId);
-	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
-		FILE_DIRECTORY_INFO *pFindData =
-			(FILE_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
-		FILE_FULL_DIRECTORY_INFO *pFindData =
-			(FILE_FULL_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
-		SEARCH_ID_FULL_DIR_INFO *pFindData =
-			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-		*pinum = le64_to_cpu(pFindData->UniqueId);
-	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
-		FILE_BOTH_DIRECTORY_INFO *pFindData =
-			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		len = le32_to_cpu(pFindData->FileNameLength);
-	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
-		FIND_FILE_STANDARD_INFO *pFindData =
-			(FIND_FILE_STANDARD_INFO *)current_entry;
-		filename = &pFindData->FileName[0];
-		/* one byte length, no name conversion */
-		len = (unsigned int)pFindData->FileNameLength;
-	} else {
-		cFYI(1, "Unknown findfirst level %d", level);
-		return -EINVAL;
-	}
-
-	if (len > max_len) {
-		cERROR(1, "bad search response length %d past smb end", len);
-		return -EINVAL;
-	}
-
-	if (unicode) {
-		pqst->len = cifs_from_ucs2((char *) pqst->name,
-					   (__le16 *) filename,
-					   UNICODE_NAME_MAX,
-					   min(len, max_len), nlt,
-					   cifs_sb->mnt_cifs_flags &
-						CIFS_MOUNT_MAP_SPECIAL_CHR);
-		pqst->len -= nls_nullsize(nlt);
-	} else {
-		pqst->name = filename;
-		pqst->len = len;
-	}
-	return rc;
-}
-
-static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
-			void *direntry, char *scratch_buf, unsigned int max_len)
-{
-	int rc = 0;
-	struct qstr qstring;
-	struct cifsFileInfo *pCifsF;
-	u64    inum;
-	ino_t  ino;
-	struct super_block *sb;
-	struct cifs_sb_info *cifs_sb;
-	struct dentry *tmp_dentry;
+	struct cifsFileInfo *file_info = file->private_data;
+	struct super_block *sb = file->f_path.dentry->d_sb;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+	struct cifs_dirent de = { NULL, };
 	struct cifs_fattr fattr;
+	struct dentry *dentry;
+	struct qstr name;
+	int rc = 0;
+	ino_t ino;
 
-	/* get filename and len into qstring */
-	/* get dentry */
-	/* decide whether to create and populate ionde */
-	if ((direntry == NULL) || (file == NULL))
-		return -EINVAL;
-
-	pCifsF = file->private_data;
-
-	if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
-		return -ENOENT;
-
-	rc = cifs_entry_is_dot(pfindEntry, pCifsF);
-	/* skip . and .. since we added them first */
-	if (rc != 0)
-		return 0;
-
-	sb = file->f_path.dentry->d_sb;
-	cifs_sb = CIFS_SB(sb);
-
-	qstring.name = scratch_buf;
-	rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
-			pCifsF->srch_inf.info_level,
-			pCifsF->srch_inf.unicode, cifs_sb,
-			max_len, &inum /* returned */);
-
+	rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
+			      file_info->srch_inf.unicode);
 	if (rc)
 		return rc;
 
-	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
-		cifs_unix_basic_to_fattr(&fattr,
-				 &((FILE_UNIX_INFO *) pfindEntry)->basic,
-				 cifs_sb);
-	else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
-		cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
-					pfindEntry, cifs_sb);
-	else
-		cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
-					pfindEntry, cifs_sb);
+	if (de.namelen > max_len) {
+		cERROR(1, "bad search response length %zd past smb end",
+			  de.namelen);
+		return -EINVAL;
+	}
 
-	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
-		fattr.cf_uniqueid = inum;
+	/* skip . and .. since we added them first */
+	if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
+		return 0;
+
+	if (file_info->srch_inf.unicode) {
+		struct nls_table *nlt = cifs_sb->local_nls;
+
+		name.name = scratch_buf;
+		name.len =
+			cifs_from_ucs2((char *)name.name, (__le16 *)de.name,
+				       UNICODE_NAME_MAX,
+				       min(de.namelen, (size_t)max_len), nlt,
+				       cifs_sb->mnt_cifs_flags &
+						CIFS_MOUNT_MAP_SPECIAL_CHR);
+		name.len -= nls_nullsize(nlt);
+	} else {
+		name.name = de.name;
+		name.len = de.namelen;
+	}
+
+	switch (file_info->srch_inf.info_level) {
+	case SMB_FIND_FILE_UNIX:
+		cifs_unix_basic_to_fattr(&fattr,
+					 &((FILE_UNIX_INFO *)find_entry)->basic,
+					 cifs_sb);
+		break;
+	case SMB_FIND_FILE_INFO_STANDARD:
+		cifs_std_info_to_fattr(&fattr,
+				       (FIND_FILE_STANDARD_INFO *)find_entry,
+				       cifs_sb);
+		break;
+	default:
+		cifs_dir_info_to_fattr(&fattr,
+				       (FILE_DIRECTORY_INFO *)find_entry,
+				       cifs_sb);
+		break;
+	}
+
+	if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+		fattr.cf_uniqueid = de.ino;
 	} else {
 		fattr.cf_uniqueid = iunique(sb, ROOT_I);
 		cifs_autodisable_serverino(cifs_sb);
@@ -750,12 +687,12 @@
 		fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
 
 	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
-	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
+	dentry = cifs_readdir_lookup(file->f_dentry, &name, &fattr);
 
-	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
-		     ino, fattr.cf_dtype);
+	rc = filldir(dirent, name.name, name.len, file->f_pos, ino,
+		     fattr.cf_dtype);
 
-	dput(tmp_dentry);
+	dput(dentry);
 	return rc;
 }
 
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 1c5b770..42b9fff 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -157,8 +157,14 @@
 		cERROR(1, "%s: Could not init md4 shash\n", __func__);
 		goto mdfour_err;
 	}
-	crypto_shash_update(&sdescmd4->shash, link_str, link_len);
+	rc = crypto_shash_update(&sdescmd4->shash, link_str, link_len);
+	if (rc) {
+		cERROR(1, "%s: Could not update with link_str\n", __func__);
+		goto mdfour_err;
+	}
 	rc = crypto_shash_final(&sdescmd4->shash, md4_hash);
+	if (rc)
+		cERROR(1, "%s: Could not genereate md4 hash\n", __func__);
 
 mdfour_err:
 	crypto_free_shash(md4);
diff --git a/fs/dcache.c b/fs/dcache.c
index be18598..b05aac3 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2138,8 +2138,9 @@
  * @target: new dentry
  *
  * Update the dcache to reflect the move of a file name. Negative
- * dcache entries should not be moved in this way.  Caller hold
- * rename_lock.
+ * dcache entries should not be moved in this way. Caller must hold
+ * rename_lock, the i_mutex of the source and target directories,
+ * and the sb->s_vfs_rename_mutex if they differ. See lock_rename().
  */
 static void __d_move(struct dentry * dentry, struct dentry * target)
 {
@@ -2202,7 +2203,8 @@
  * @target: new dentry
  *
  * Update the dcache to reflect the move of a file name. Negative
- * dcache entries should not be moved in this way.
+ * dcache entries should not be moved in this way. See the locking
+ * requirements for __d_move.
  */
 void d_move(struct dentry *dentry, struct dentry *target)
 {
@@ -2320,7 +2322,8 @@
  * @inode: inode to bind to the dentry, to which aliases may be attached
  *
  * Introduces an dentry into the tree, substituting an extant disconnected
- * root directory alias in its place if there is one
+ * root directory alias in its place if there is one. Caller must hold the
+ * i_mutex of the parent directory.
  */
 struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 {
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 01d2d9e..44a360c 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -35,7 +35,7 @@
 #include <linux/buffer_head.h>
 #include <linux/rwsem.h>
 #include <linux/uio.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * How many user pages to map in one call to get_user_pages().  This determines
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 43c7c43..b36c557 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -29,6 +29,7 @@
 #define ECRYPTFS_KERNEL_H
 
 #include <keys/user-type.h>
+#include <keys/encrypted-type.h>
 #include <linux/fs.h>
 #include <linux/fs_stack.h>
 #include <linux/namei.h>
@@ -36,125 +37,18 @@
 #include <linux/hash.h>
 #include <linux/nsproxy.h>
 #include <linux/backing-dev.h>
+#include <linux/ecryptfs.h>
 
-/* Version verification for shared data structures w/ userspace */
-#define ECRYPTFS_VERSION_MAJOR 0x00
-#define ECRYPTFS_VERSION_MINOR 0x04
-#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
-/* These flags indicate which features are supported by the kernel
- * module; userspace tools such as the mount helper read
- * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
- * how to behave. */
-#define ECRYPTFS_VERSIONING_PASSPHRASE            0x00000001
-#define ECRYPTFS_VERSIONING_PUBKEY                0x00000002
-#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
-#define ECRYPTFS_VERSIONING_POLICY                0x00000008
-#define ECRYPTFS_VERSIONING_XATTR                 0x00000010
-#define ECRYPTFS_VERSIONING_MULTKEY               0x00000020
-#define ECRYPTFS_VERSIONING_DEVMISC               0x00000040
-#define ECRYPTFS_VERSIONING_HMAC                  0x00000080
-#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION   0x00000100
-#define ECRYPTFS_VERSIONING_GCM                   0x00000200
-#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
-				  | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
-				  | ECRYPTFS_VERSIONING_PUBKEY \
-				  | ECRYPTFS_VERSIONING_XATTR \
-				  | ECRYPTFS_VERSIONING_MULTKEY \
-				  | ECRYPTFS_VERSIONING_DEVMISC \
-				  | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
-#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
-#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
-#define ECRYPTFS_SALT_SIZE 8
-#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
-/* The original signature size is only for what is stored on disk; all
- * in-memory representations are expanded hex, so it better adapted to
- * be passed around or referenced on the command line */
-#define ECRYPTFS_SIG_SIZE 8
-#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
-#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
-#define ECRYPTFS_MAX_KEY_BYTES 64
-#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
 #define ECRYPTFS_DEFAULT_IV_BYTES 16
-#define ECRYPTFS_FILE_VERSION 0x03
 #define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
 #define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
 #define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
 #define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
 #define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
-#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
 #define ECRYPTFS_DEFAULT_NUM_USERS 4
 #define ECRYPTFS_MAX_NUM_USERS 32768
 #define ECRYPTFS_XATTR_NAME "user.ecryptfs"
 
-#define RFC2440_CIPHER_DES3_EDE 0x02
-#define RFC2440_CIPHER_CAST_5 0x03
-#define RFC2440_CIPHER_BLOWFISH 0x04
-#define RFC2440_CIPHER_AES_128 0x07
-#define RFC2440_CIPHER_AES_192 0x08
-#define RFC2440_CIPHER_AES_256 0x09
-#define RFC2440_CIPHER_TWOFISH 0x0a
-#define RFC2440_CIPHER_CAST_6 0x0b
-
-#define RFC2440_CIPHER_RSA 0x01
-
-/**
- * For convenience, we may need to pass around the encrypted session
- * key between kernel and userspace because the authentication token
- * may not be extractable.  For example, the TPM may not release the
- * private key, instead requiring the encrypted data and returning the
- * decrypted data.
- */
-struct ecryptfs_session_key {
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
-#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
-#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
-	u32 flags;
-	u32 encrypted_key_size;
-	u32 decrypted_key_size;
-	u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
-	u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
-};
-
-struct ecryptfs_password {
-	u32 password_bytes;
-	s32 hash_algo;
-	u32 hash_iterations;
-	u32 session_key_encryption_key_bytes;
-#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
-#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
-	u32 flags;
-	/* Iterated-hash concatenation of salt and passphrase */
-	u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
-	u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
-	/* Always in expanded hex */
-	u8 salt[ECRYPTFS_SALT_SIZE];
-};
-
-enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
-
-struct ecryptfs_private_key {
-	u32 key_size;
-	u32 data_len;
-	u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
-	char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
-	u8 data[];
-};
-
-/* May be a password or a private key */
-struct ecryptfs_auth_tok {
-	u16 version; /* 8-bit major and 8-bit minor */
-	u16 token_type;
-#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
-	u32 flags;
-	struct ecryptfs_session_key session_key;
-	u8 reserved[32];
-	union {
-		struct ecryptfs_password password;
-		struct ecryptfs_private_key private_key;
-	} token;
-} __attribute__ ((packed));
-
 void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
 extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
 extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
@@ -185,11 +79,47 @@
 	} param;
 };
 
+#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+	if (key->type == &key_type_encrypted)
+		return (struct ecryptfs_auth_tok *)
+			(&((struct encrypted_key_payload *)key->payload.data)->payload_data);
+	else
+		return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+	return request_key(&key_type_encrypted, sig, NULL);
+}
+
+#else
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+	return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+	return ERR_PTR(-ENOKEY);
+}
+
+#endif /* CONFIG_ENCRYPTED_KEYS */
+
 static inline struct ecryptfs_auth_tok *
 ecryptfs_get_key_payload_data(struct key *key)
 {
-	return (struct ecryptfs_auth_tok *)
-		(((struct user_key_payload*)key->payload.data)->data);
+	struct ecryptfs_auth_tok *auth_tok;
+
+	auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
+	if (!auth_tok)
+		return (struct ecryptfs_auth_tok *)
+			(((struct user_key_payload *)key->payload.data)->data);
+	else
+		return auth_tok;
 }
 
 #define ECRYPTFS_MAX_KEYSET_SIZE 1024
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 340c657..11f8582 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -69,6 +69,7 @@
 	inode->i_ino = lower_inode->i_ino;
 	inode->i_version++;
 	inode->i_mapping->a_ops = &ecryptfs_aops;
+	inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi;
 
 	if (S_ISLNK(inode->i_mode))
 		inode->i_op = &ecryptfs_symlink_iops;
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index fa8049e..08a2b52 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1635,11 +1635,14 @@
 
 	(*auth_tok_key) = request_key(&key_type_user, sig, NULL);
 	if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
-		printk(KERN_ERR "Could not find key with description: [%s]\n",
-		       sig);
-		rc = process_request_key_err(PTR_ERR(*auth_tok_key));
-		(*auth_tok_key) = NULL;
-		goto out;
+		(*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
+		if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+			printk(KERN_ERR "Could not find key with description: [%s]\n",
+			      sig);
+			rc = process_request_key_err(PTR_ERR(*auth_tok_key));
+			(*auth_tok_key) = NULL;
+			goto out;
+		}
 	}
 	down_write(&(*auth_tok_key)->sem);
 	rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok);
@@ -1868,11 +1871,6 @@
 	 * just one will be sufficient to decrypt to get the FEK. */
 find_next_matching_auth_tok:
 	found_auth_tok = 0;
-	if (auth_tok_key) {
-		up_write(&(auth_tok_key->sem));
-		key_put(auth_tok_key);
-		auth_tok_key = NULL;
-	}
 	list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) {
 		candidate_auth_tok = &auth_tok_list_item->auth_tok;
 		if (unlikely(ecryptfs_verbosity > 0)) {
@@ -1909,14 +1907,22 @@
 		memcpy(&(candidate_auth_tok->token.private_key),
 		       &(matching_auth_tok->token.private_key),
 		       sizeof(struct ecryptfs_private_key));
+		up_write(&(auth_tok_key->sem));
+		key_put(auth_tok_key);
 		rc = decrypt_pki_encrypted_session_key(candidate_auth_tok,
 						       crypt_stat);
 	} else if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD) {
 		memcpy(&(candidate_auth_tok->token.password),
 		       &(matching_auth_tok->token.password),
 		       sizeof(struct ecryptfs_password));
+		up_write(&(auth_tok_key->sem));
+		key_put(auth_tok_key);
 		rc = decrypt_passphrase_encrypted_session_key(
 			candidate_auth_tok, crypt_stat);
+	} else {
+		up_write(&(auth_tok_key->sem));
+		key_put(auth_tok_key);
+		rc = -EINVAL;
 	}
 	if (rc) {
 		struct ecryptfs_auth_tok_list_item *auth_tok_list_item_tmp;
@@ -1956,15 +1962,12 @@
 out_wipe_list:
 	wipe_auth_tok_list(&auth_tok_list);
 out:
-	if (auth_tok_key) {
-		up_write(&(auth_tok_key->sem));
-		key_put(auth_tok_key);
-	}
 	return rc;
 }
 
 static int
-pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
+pki_encrypt_session_key(struct key *auth_tok_key,
+			struct ecryptfs_auth_tok *auth_tok,
 			struct ecryptfs_crypt_stat *crypt_stat,
 			struct ecryptfs_key_record *key_rec)
 {
@@ -1979,6 +1982,8 @@
 					 crypt_stat->cipher,
 					 crypt_stat->key_size),
 				 crypt_stat, &payload, &payload_len);
+	up_write(&(auth_tok_key->sem));
+	key_put(auth_tok_key);
 	if (rc) {
 		ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
 		goto out;
@@ -2008,6 +2013,8 @@
  * write_tag_1_packet - Write an RFC2440-compatible tag 1 (public key) packet
  * @dest: Buffer into which to write the packet
  * @remaining_bytes: Maximum number of bytes that can be writtn
+ * @auth_tok_key: The authentication token key to unlock and put when done with
+ *                @auth_tok
  * @auth_tok: The authentication token used for generating the tag 1 packet
  * @crypt_stat: The cryptographic context
  * @key_rec: The key record struct for the tag 1 packet
@@ -2018,7 +2025,7 @@
  */
 static int
 write_tag_1_packet(char *dest, size_t *remaining_bytes,
-		   struct ecryptfs_auth_tok *auth_tok,
+		   struct key *auth_tok_key, struct ecryptfs_auth_tok *auth_tok,
 		   struct ecryptfs_crypt_stat *crypt_stat,
 		   struct ecryptfs_key_record *key_rec, size_t *packet_size)
 {
@@ -2039,12 +2046,15 @@
 		memcpy(key_rec->enc_key,
 		       auth_tok->session_key.encrypted_key,
 		       auth_tok->session_key.encrypted_key_size);
+		up_write(&(auth_tok_key->sem));
+		key_put(auth_tok_key);
 		goto encrypted_session_key_set;
 	}
 	if (auth_tok->session_key.encrypted_key_size == 0)
 		auth_tok->session_key.encrypted_key_size =
 			auth_tok->token.private_key.key_size;
-	rc = pki_encrypt_session_key(auth_tok, crypt_stat, key_rec);
+	rc = pki_encrypt_session_key(auth_tok_key, auth_tok, crypt_stat,
+				     key_rec);
 	if (rc) {
 		printk(KERN_ERR "Failed to encrypt session key via a key "
 		       "module; rc = [%d]\n", rc);
@@ -2421,6 +2431,8 @@
 						&max, auth_tok,
 						crypt_stat, key_rec,
 						&written);
+			up_write(&(auth_tok_key->sem));
+			key_put(auth_tok_key);
 			if (rc) {
 				ecryptfs_printk(KERN_WARNING, "Error "
 						"writing tag 3 packet\n");
@@ -2438,8 +2450,8 @@
 			}
 			(*len) += written;
 		} else if (auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) {
-			rc = write_tag_1_packet(dest_base + (*len),
-						&max, auth_tok,
+			rc = write_tag_1_packet(dest_base + (*len), &max,
+						auth_tok_key, auth_tok,
 						crypt_stat, key_rec, &written);
 			if (rc) {
 				ecryptfs_printk(KERN_WARNING, "Error "
@@ -2448,14 +2460,13 @@
 			}
 			(*len) += written;
 		} else {
+			up_write(&(auth_tok_key->sem));
+			key_put(auth_tok_key);
 			ecryptfs_printk(KERN_WARNING, "Unsupported "
 					"authentication token type\n");
 			rc = -EINVAL;
 			goto out_free;
 		}
-		up_write(&(auth_tok_key->sem));
-		key_put(auth_tok_key);
-		auth_tok_key = NULL;
 	}
 	if (likely(max > 0)) {
 		dest_base[(*len)] = 0x00;
@@ -2468,11 +2479,6 @@
 out:
 	if (rc)
 		(*len) = 0;
-	if (auth_tok_key) {
-		up_write(&(auth_tok_key->sem));
-		key_put(auth_tok_key);
-	}
-
 	mutex_unlock(&crypt_stat->keysig_list_mutex);
 	return rc;
 }
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index f9cfd16..fe047d96 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -37,7 +37,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/mman.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * LOCKING:
diff --git a/fs/exec.c b/fs/exec.c
index 842d570..da80612 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -181,14 +181,7 @@
 		return;
 
 	bprm->vma_pages = pages;
-
-#ifdef SPLIT_RSS_COUNTING
 	add_mm_counter(mm, MM_ANONPAGES, diff);
-#else
-	spin_lock(&mm->page_table_lock);
-	add_mm_counter(mm, MM_ANONPAGES, diff);
-	spin_unlock(&mm->page_table_lock);
-#endif
 }
 
 static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
@@ -277,7 +270,7 @@
 	 * use STACK_TOP because that can depend on attributes which aren't
 	 * configured yet.
 	 */
-	BUG_ON(VM_STACK_FLAGS & VM_STACK_INCOMPLETE_SETUP);
+	BUILD_BUG_ON(VM_STACK_FLAGS & VM_STACK_INCOMPLETE_SETUP);
 	vma->vm_end = STACK_TOP_MAX;
 	vma->vm_start = vma->vm_end - PAGE_SIZE;
 	vma->vm_flags = VM_STACK_FLAGS | VM_STACK_INCOMPLETE_SETUP;
@@ -1430,9 +1423,9 @@
 			}
 		}
 		read_unlock(&binfmt_lock);
+#ifdef CONFIG_MODULES
 		if (retval != -ENOEXEC || bprm->mm == NULL) {
 			break;
-#ifdef CONFIG_MODULES
 		} else {
 #define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
 			if (printable(bprm->buf[0]) &&
@@ -1440,9 +1433,13 @@
 			    printable(bprm->buf[2]) &&
 			    printable(bprm->buf[3]))
 				break; /* -ENOEXEC */
+			if (try)
+				break; /* -ENOEXEC */
 			request_module("binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
-#endif
 		}
+#else
+		break;
+#endif
 	}
 	return retval;
 }
@@ -1649,15 +1646,26 @@
 	return ret;
 }
 
+static void cn_escape(char *str)
+{
+	for (; *str; str++)
+		if (*str == '/')
+			*str = '!';
+}
+
 static int cn_print_exe_file(struct core_name *cn)
 {
 	struct file *exe_file;
-	char *pathbuf, *path, *p;
+	char *pathbuf, *path;
 	int ret;
 
 	exe_file = get_mm_exe_file(current->mm);
-	if (!exe_file)
-		return cn_printf(cn, "(unknown)");
+	if (!exe_file) {
+		char *commstart = cn->corename + cn->used;
+		ret = cn_printf(cn, "%s (path unknown)", current->comm);
+		cn_escape(commstart);
+		return ret;
+	}
 
 	pathbuf = kmalloc(PATH_MAX, GFP_TEMPORARY);
 	if (!pathbuf) {
@@ -1671,9 +1679,7 @@
 		goto free_buf;
 	}
 
-	for (p = path; *p; p++)
-		if (*p == '/')
-			*p = '!';
+	cn_escape(path);
 
 	ret = cn_printf(cn, "%s", path);
 
@@ -1745,16 +1751,22 @@
 				break;
 			}
 			/* hostname */
-			case 'h':
+			case 'h': {
+				char *namestart = cn->corename + cn->used;
 				down_read(&uts_sem);
 				err = cn_printf(cn, "%s",
 					      utsname()->nodename);
 				up_read(&uts_sem);
+				cn_escape(namestart);
 				break;
+			}
 			/* executable */
-			case 'e':
+			case 'e': {
+				char *commstart = cn->corename + cn->used;
 				err = cn_printf(cn, "%s", current->comm);
+				cn_escape(commstart);
 				break;
+			}
 			case 'E':
 				err = cn_print_exe_file(cn);
 				break;
@@ -2118,16 +2130,16 @@
 
 	ispipe = format_corename(&cn, signr);
 
-	if (ispipe == -ENOMEM) {
-		printk(KERN_WARNING "format_corename failed\n");
-		printk(KERN_WARNING "Aborting core\n");
-		goto fail_corename;
-	}
-
  	if (ispipe) {
 		int dump_count;
 		char **helper_argv;
 
+		if (ispipe < 0) {
+			printk(KERN_WARNING "format_corename failed\n");
+			printk(KERN_WARNING "Aborting core\n");
+			goto fail_corename;
+		}
+
 		if (cprm.limit == 1) {
 			/*
 			 * Normally core limits are irrelevant to pipes, since
diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h
index 5c0a6a4..503bfb0 100644
--- a/fs/ext2/acl.h
+++ b/fs/ext2/acl.h
@@ -61,7 +61,6 @@
 #else
 #include <linux/sched.h>
 #define ext2_get_acl	NULL
-#define ext2_get_acl	NULL
 #define ext2_set_acl	NULL
 
 static inline int
diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c
index 5299706..d27b71f 100644
--- a/fs/ext2/xattr.c
+++ b/fs/ext2/xattr.c
@@ -161,6 +161,10 @@
 
 	if (name == NULL)
 		return -EINVAL;
+	name_len = strlen(name);
+	if (name_len > 255)
+		return -ERANGE;
+
 	down_read(&EXT2_I(inode)->xattr_sem);
 	error = -ENODATA;
 	if (!EXT2_I(inode)->i_file_acl)
@@ -181,12 +185,8 @@
 		error = -EIO;
 		goto cleanup;
 	}
-	/* find named attribute */
-	name_len = strlen(name);
 
-	error = -ERANGE;
-	if (name_len > 255)
-		goto cleanup;
+	/* find named attribute */
 	entry = FIRST_ENTRY(bh);
 	while (!IS_LAST_ENTRY(entry)) {
 		struct ext2_xattr_entry *next =
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index fe52297..6386d76 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -21,6 +21,7 @@
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/blkdev.h>
+#include <trace/events/ext3.h>
 
 /*
  * balloc.c contains the blocks allocation and deallocation routines
@@ -161,6 +162,7 @@
 	desc = ext3_get_group_desc(sb, block_group, NULL);
 	if (!desc)
 		return NULL;
+	trace_ext3_read_block_bitmap(sb, block_group);
 	bitmap_blk = le32_to_cpu(desc->bg_block_bitmap);
 	bh = sb_getblk(sb, bitmap_blk);
 	if (unlikely(!bh)) {
@@ -351,6 +353,7 @@
 	struct rb_node * parent = NULL;
 	struct ext3_reserve_window_node *this;
 
+	trace_ext3_rsv_window_add(sb, rsv);
 	while (*p)
 	{
 		parent = *p;
@@ -476,8 +479,10 @@
 	rsv = &block_i->rsv_window_node;
 	if (!rsv_is_empty(&rsv->rsv_window)) {
 		spin_lock(rsv_lock);
-		if (!rsv_is_empty(&rsv->rsv_window))
+		if (!rsv_is_empty(&rsv->rsv_window)) {
+			trace_ext3_discard_reservation(inode, rsv);
 			rsv_window_remove(inode->i_sb, rsv);
+		}
 		spin_unlock(rsv_lock);
 	}
 }
@@ -683,14 +688,10 @@
 void ext3_free_blocks(handle_t *handle, struct inode *inode,
 			ext3_fsblk_t block, unsigned long count)
 {
-	struct super_block * sb;
+	struct super_block *sb = inode->i_sb;
 	unsigned long dquot_freed_blocks;
 
-	sb = inode->i_sb;
-	if (!sb) {
-		printk ("ext3_free_blocks: nonexistent device");
-		return;
-	}
+	trace_ext3_free_blocks(inode, block, count);
 	ext3_free_blocks_sb(handle, sb, block, count, &dquot_freed_blocks);
 	if (dquot_freed_blocks)
 		dquot_free_block(inode, dquot_freed_blocks);
@@ -1136,6 +1137,7 @@
 	else
 		start_block = grp_goal + group_first_block;
 
+	trace_ext3_alloc_new_reservation(sb, start_block);
 	size = my_rsv->rsv_goal_size;
 
 	if (!rsv_is_empty(&my_rsv->rsv_window)) {
@@ -1230,8 +1232,11 @@
 	 * check if the first free block is within the
 	 * free space we just reserved
 	 */
-	if (start_block >= my_rsv->rsv_start && start_block <= my_rsv->rsv_end)
+	if (start_block >= my_rsv->rsv_start &&
+	    start_block <= my_rsv->rsv_end) {
+		trace_ext3_reserved(sb, start_block, my_rsv);
 		return 0;		/* success */
+	}
 	/*
 	 * if the first free bit we found is out of the reservable space
 	 * continue search for next reservable space,
@@ -1514,10 +1519,6 @@
 
 	*errp = -ENOSPC;
 	sb = inode->i_sb;
-	if (!sb) {
-		printk("ext3_new_block: nonexistent device");
-		return 0;
-	}
 
 	/*
 	 * Check quota for allocation of this block.
@@ -1528,8 +1529,10 @@
 		return 0;
 	}
 
+	trace_ext3_request_blocks(inode, goal, num);
+
 	sbi = EXT3_SB(sb);
-	es = EXT3_SB(sb)->s_es;
+	es = sbi->s_es;
 	ext3_debug("goal=%lu.\n", goal);
 	/*
 	 * Allocate a block from reservation only when
@@ -1742,6 +1745,10 @@
 	brelse(bitmap_bh);
 	dquot_free_block(inode, *count-num);
 	*count = num;
+
+	trace_ext3_allocate_blocks(inode, goal, num,
+				   (unsigned long long)ret_block);
+
 	return ret_block;
 
 io_error:
@@ -1996,6 +2003,7 @@
 		if ((next - start) < minblocks)
 			goto free_extent;
 
+		trace_ext3_discard_blocks(sb, discard_block, next - start);
 		 /* Send the TRIM command down to the device */
 		err = sb_issue_discard(sb, discard_block, next - start,
 				       GFP_NOFS, 0);
@@ -2100,7 +2108,7 @@
 	if (unlikely(minlen > EXT3_BLOCKS_PER_GROUP(sb)))
 		return -EINVAL;
 	if (start >= max_blks)
-		goto out;
+		return -EINVAL;
 	if (start + len > max_blks)
 		len = max_blks - start;
 
@@ -2148,8 +2156,6 @@
 
 	if (ret >= 0)
 		ret = 0;
-
-out:
 	range->len = trimmed * sb->s_blocksize;
 
 	return ret;
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
index 2be5b99..724df69 100644
--- a/fs/ext3/file.c
+++ b/fs/ext3/file.c
@@ -71,7 +71,6 @@
 };
 
 const struct inode_operations ext3_file_inode_operations = {
-	.truncate	= ext3_truncate,
 	.setattr	= ext3_setattr,
 #ifdef CONFIG_EXT3_FS_XATTR
 	.setxattr	= generic_setxattr,
diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c
index 0bcf63a..d494c55 100644
--- a/fs/ext3/fsync.c
+++ b/fs/ext3/fsync.c
@@ -30,6 +30,7 @@
 #include <linux/jbd.h>
 #include <linux/ext3_fs.h>
 #include <linux/ext3_jbd.h>
+#include <trace/events/ext3.h>
 
 /*
  * akpm: A new design for ext3_sync_file().
@@ -51,12 +52,14 @@
 	int ret, needs_barrier = 0;
 	tid_t commit_tid;
 
+	trace_ext3_sync_file_enter(file, datasync);
+
 	if (inode->i_sb->s_flags & MS_RDONLY)
 		return 0;
 
 	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
 	if (ret)
-		return ret;
+		goto out;
 
 	/*
 	 * Taking the mutex here just to keep consistent with how fsync was
@@ -83,7 +86,8 @@
 	 */
 	if (ext3_should_journal_data(inode)) {
 		mutex_unlock(&inode->i_mutex);
-		return ext3_force_commit(inode->i_sb);
+		ret = ext3_force_commit(inode->i_sb);
+		goto out;
 	}
 
 	if (datasync)
@@ -104,6 +108,9 @@
 	 */
 	if (needs_barrier)
 		blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
+
 	mutex_unlock(&inode->i_mutex);
+out:
+	trace_ext3_sync_file_exit(inode, ret);
 	return ret;
 }
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c
index bfc2dc4..bf09cbf 100644
--- a/fs/ext3/ialloc.c
+++ b/fs/ext3/ialloc.c
@@ -23,6 +23,7 @@
 #include <linux/buffer_head.h>
 #include <linux/random.h>
 #include <linux/bitops.h>
+#include <trace/events/ext3.h>
 
 #include <asm/byteorder.h>
 
@@ -118,6 +119,7 @@
 
 	ino = inode->i_ino;
 	ext3_debug ("freeing inode %lu\n", ino);
+	trace_ext3_free_inode(inode);
 
 	is_directory = S_ISDIR(inode->i_mode);
 
@@ -426,6 +428,7 @@
 		return ERR_PTR(-EPERM);
 
 	sb = dir->i_sb;
+	trace_ext3_request_inode(dir, mode);
 	inode = new_inode(sb);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
@@ -601,6 +604,7 @@
 	}
 
 	ext3_debug("allocating inode %lu\n", inode->i_ino);
+	trace_ext3_allocate_inode(inode, dir, mode);
 	goto really_out;
 fail:
 	ext3_std_error(sb, err);
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 2978a2a..04da6ac 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -38,10 +38,12 @@
 #include <linux/bio.h>
 #include <linux/fiemap.h>
 #include <linux/namei.h>
+#include <trace/events/ext3.h>
 #include "xattr.h"
 #include "acl.h"
 
 static int ext3_writepage_trans_blocks(struct inode *inode);
+static int ext3_block_truncate_page(struct inode *inode, loff_t from);
 
 /*
  * Test whether an inode is a fast symlink.
@@ -70,6 +72,7 @@
 
 	might_sleep();
 
+	trace_ext3_forget(inode, is_metadata, blocknr);
 	BUFFER_TRACE(bh, "enter");
 
 	jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
@@ -194,20 +197,47 @@
  */
 void ext3_evict_inode (struct inode *inode)
 {
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	struct ext3_block_alloc_info *rsv;
 	handle_t *handle;
 	int want_delete = 0;
 
+	trace_ext3_evict_inode(inode);
 	if (!inode->i_nlink && !is_bad_inode(inode)) {
 		dquot_initialize(inode);
 		want_delete = 1;
 	}
 
+	/*
+	 * When journalling data dirty buffers are tracked only in the journal.
+	 * So although mm thinks everything is clean and ready for reaping the
+	 * inode might still have some pages to write in the running
+	 * transaction or waiting to be checkpointed. Thus calling
+	 * journal_invalidatepage() (via truncate_inode_pages()) to discard
+	 * these buffers can cause data loss. Also even if we did not discard
+	 * these buffers, we would have no way to find them after the inode
+	 * is reaped and thus user could see stale data if he tries to read
+	 * them before the transaction is checkpointed. So be careful and
+	 * force everything to disk here... We use ei->i_datasync_tid to
+	 * store the newest transaction containing inode's data.
+	 *
+	 * Note that directories do not have this problem because they don't
+	 * use page cache.
+	 */
+	if (inode->i_nlink && ext3_should_journal_data(inode) &&
+	    (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
+		tid_t commit_tid = atomic_read(&ei->i_datasync_tid);
+		journal_t *journal = EXT3_SB(inode->i_sb)->s_journal;
+
+		log_start_commit(journal, commit_tid);
+		log_wait_commit(journal, commit_tid);
+		filemap_write_and_wait(&inode->i_data);
+	}
 	truncate_inode_pages(&inode->i_data, 0);
 
 	ext3_discard_reservation(inode);
-	rsv = EXT3_I(inode)->i_block_alloc_info;
-	EXT3_I(inode)->i_block_alloc_info = NULL;
+	rsv = ei->i_block_alloc_info;
+	ei->i_block_alloc_info = NULL;
 	if (unlikely(rsv))
 		kfree(rsv);
 
@@ -231,15 +261,13 @@
 	if (inode->i_blocks)
 		ext3_truncate(inode);
 	/*
-	 * Kill off the orphan record which ext3_truncate created.
-	 * AKPM: I think this can be inside the above `if'.
-	 * Note that ext3_orphan_del() has to be able to cope with the
-	 * deletion of a non-existent orphan - this is because we don't
-	 * know if ext3_truncate() actually created an orphan record.
-	 * (Well, we could do this if we need to, but heck - it works)
+	 * Kill off the orphan record created when the inode lost the last
+	 * link.  Note that ext3_orphan_del() has to be able to cope with the
+	 * deletion of a non-existent orphan - ext3_truncate() could
+	 * have removed the record.
 	 */
 	ext3_orphan_del(handle, inode);
-	EXT3_I(inode)->i_dtime	= get_seconds();
+	ei->i_dtime = get_seconds();
 
 	/*
 	 * One subtle ordering requirement: if anything has gone wrong
@@ -842,6 +870,7 @@
 	ext3_fsblk_t first_block = 0;
 
 
+	trace_ext3_get_blocks_enter(inode, iblock, maxblocks, create);
 	J_ASSERT(handle != NULL || create == 0);
 	depth = ext3_block_to_path(inode,iblock,offsets,&blocks_to_boundary);
 
@@ -886,6 +915,9 @@
 	if (!create || err == -EIO)
 		goto cleanup;
 
+	/*
+	 * Block out ext3_truncate while we alter the tree
+	 */
 	mutex_lock(&ei->truncate_mutex);
 
 	/*
@@ -934,9 +966,6 @@
 	 */
 	count = ext3_blks_to_allocate(partial, indirect_blks,
 					maxblocks, blocks_to_boundary);
-	/*
-	 * Block out ext3_truncate while we alter the tree
-	 */
 	err = ext3_alloc_branch(handle, inode, indirect_blks, &count, goal,
 				offsets + (partial - chain), partial);
 
@@ -970,6 +999,9 @@
 	}
 	BUFFER_TRACE(bh_result, "returned");
 out:
+	trace_ext3_get_blocks_exit(inode, iblock,
+				   depth ? le32_to_cpu(chain[depth-1].key) : 0,
+				   count, err);
 	return err;
 }
 
@@ -1202,6 +1234,16 @@
 	ext3_truncate(inode);
 }
 
+/*
+ * Truncate blocks that were not used by direct IO write. We have to zero out
+ * the last file block as well because direct IO might have written to it.
+ */
+static void ext3_truncate_failed_direct_write(struct inode *inode)
+{
+	ext3_block_truncate_page(inode, inode->i_size);
+	ext3_truncate(inode);
+}
+
 static int ext3_write_begin(struct file *file, struct address_space *mapping,
 				loff_t pos, unsigned len, unsigned flags,
 				struct page **pagep, void **fsdata)
@@ -1217,6 +1259,8 @@
 	 * we allocate blocks but write fails for some reason */
 	int needed_blocks = ext3_writepage_trans_blocks(inode) + 1;
 
+	trace_ext3_write_begin(inode, pos, len, flags);
+
 	index = pos >> PAGE_CACHE_SHIFT;
 	from = pos & (PAGE_CACHE_SIZE - 1);
 	to = from + len;
@@ -1332,6 +1376,7 @@
 	unsigned from, to;
 	int ret = 0, ret2;
 
+	trace_ext3_ordered_write_end(inode, pos, len, copied);
 	copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
 
 	from = pos & (PAGE_CACHE_SIZE - 1);
@@ -1367,6 +1412,7 @@
 	struct inode *inode = file->f_mapping->host;
 	int ret;
 
+	trace_ext3_writeback_write_end(inode, pos, len, copied);
 	copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
 	update_file_sizes(inode, pos, copied);
 	/*
@@ -1391,10 +1437,12 @@
 {
 	handle_t *handle = ext3_journal_current_handle();
 	struct inode *inode = mapping->host;
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	int ret = 0, ret2;
 	int partial = 0;
 	unsigned from, to;
 
+	trace_ext3_journalled_write_end(inode, pos, len, copied);
 	from = pos & (PAGE_CACHE_SIZE - 1);
 	to = from + len;
 
@@ -1419,8 +1467,9 @@
 	if (pos + len > inode->i_size && ext3_can_truncate(inode))
 		ext3_orphan_add(handle, inode);
 	ext3_set_inode_state(inode, EXT3_STATE_JDATA);
-	if (inode->i_size > EXT3_I(inode)->i_disksize) {
-		EXT3_I(inode)->i_disksize = inode->i_size;
+	atomic_set(&ei->i_datasync_tid, handle->h_transaction->t_tid);
+	if (inode->i_size > ei->i_disksize) {
+		ei->i_disksize = inode->i_size;
 		ret2 = ext3_mark_inode_dirty(handle, inode);
 		if (!ret)
 			ret = ret2;
@@ -1577,6 +1626,7 @@
 	if (ext3_journal_current_handle())
 		goto out_fail;
 
+	trace_ext3_ordered_writepage(page);
 	if (!page_has_buffers(page)) {
 		create_empty_buffers(page, inode->i_sb->s_blocksize,
 				(1 << BH_Dirty)|(1 << BH_Uptodate));
@@ -1647,6 +1697,7 @@
 	if (ext3_journal_current_handle())
 		goto out_fail;
 
+	trace_ext3_writeback_writepage(page);
 	if (page_has_buffers(page)) {
 		if (!walk_page_buffers(NULL, page_buffers(page), 0,
 				      PAGE_CACHE_SIZE, NULL, buffer_unmapped)) {
@@ -1689,6 +1740,7 @@
 	if (ext3_journal_current_handle())
 		goto no_write;
 
+	trace_ext3_journalled_writepage(page);
 	handle = ext3_journal_start(inode, ext3_writepage_trans_blocks(inode));
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
@@ -1715,6 +1767,8 @@
 		if (ret == 0)
 			ret = err;
 		ext3_set_inode_state(inode, EXT3_STATE_JDATA);
+		atomic_set(&EXT3_I(inode)->i_datasync_tid,
+			   handle->h_transaction->t_tid);
 		unlock_page(page);
 	} else {
 		/*
@@ -1739,6 +1793,7 @@
 
 static int ext3_readpage(struct file *file, struct page *page)
 {
+	trace_ext3_readpage(page);
 	return mpage_readpage(page, ext3_get_block);
 }
 
@@ -1753,6 +1808,8 @@
 {
 	journal_t *journal = EXT3_JOURNAL(page->mapping->host);
 
+	trace_ext3_invalidatepage(page, offset);
+
 	/*
 	 * If it's a full truncate we just forget about the pending dirtying
 	 */
@@ -1766,6 +1823,7 @@
 {
 	journal_t *journal = EXT3_JOURNAL(page->mapping->host);
 
+	trace_ext3_releasepage(page);
 	WARN_ON(PageChecked(page));
 	if (!page_has_buffers(page))
 		return 0;
@@ -1794,6 +1852,8 @@
 	size_t count = iov_length(iov, nr_segs);
 	int retries = 0;
 
+	trace_ext3_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw);
+
 	if (rw == WRITE) {
 		loff_t final_size = offset + count;
 
@@ -1827,7 +1887,7 @@
 		loff_t end = offset + iov_length(iov, nr_segs);
 
 		if (end > isize)
-			vmtruncate(inode, isize);
+			ext3_truncate_failed_direct_write(inode);
 	}
 	if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
 		goto retry;
@@ -1841,7 +1901,7 @@
 			/* This is really bad luck. We've written the data
 			 * but cannot extend i_size. Truncate allocated blocks
 			 * and pretend the write failed... */
-			ext3_truncate(inode);
+			ext3_truncate_failed_direct_write(inode);
 			ret = PTR_ERR(handle);
 			goto out;
 		}
@@ -1867,6 +1927,8 @@
 			ret = err;
 	}
 out:
+	trace_ext3_direct_IO_exit(inode, offset,
+				iov_length(iov, nr_segs), rw, ret);
 	return ret;
 }
 
@@ -1949,17 +2011,24 @@
  * This required during truncate. We need to physically zero the tail end
  * of that block so it doesn't yield old data if the file is later grown.
  */
-static int ext3_block_truncate_page(handle_t *handle, struct page *page,
-		struct address_space *mapping, loff_t from)
+static int ext3_block_truncate_page(struct inode *inode, loff_t from)
 {
 	ext3_fsblk_t index = from >> PAGE_CACHE_SHIFT;
-	unsigned offset = from & (PAGE_CACHE_SIZE-1);
+	unsigned offset = from & (PAGE_CACHE_SIZE - 1);
 	unsigned blocksize, iblock, length, pos;
-	struct inode *inode = mapping->host;
+	struct page *page;
+	handle_t *handle = NULL;
 	struct buffer_head *bh;
 	int err = 0;
 
+	/* Truncated on block boundary - nothing to do */
 	blocksize = inode->i_sb->s_blocksize;
+	if ((from & (blocksize - 1)) == 0)
+		return 0;
+
+	page = grab_cache_page(inode->i_mapping, index);
+	if (!page)
+		return -ENOMEM;
 	length = blocksize - (offset & (blocksize - 1));
 	iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 
@@ -2004,11 +2073,23 @@
 			goto unlock;
 	}
 
+	/* data=writeback mode doesn't need transaction to zero-out data */
+	if (!ext3_should_writeback_data(inode)) {
+		/* We journal at most one block */
+		handle = ext3_journal_start(inode, 1);
+		if (IS_ERR(handle)) {
+			clear_highpage(page);
+			flush_dcache_page(page);
+			err = PTR_ERR(handle);
+			goto unlock;
+		}
+	}
+
 	if (ext3_should_journal_data(inode)) {
 		BUFFER_TRACE(bh, "get write access");
 		err = ext3_journal_get_write_access(handle, bh);
 		if (err)
-			goto unlock;
+			goto stop;
 	}
 
 	zero_user(page, offset, length);
@@ -2022,6 +2103,9 @@
 			err = ext3_journal_dirty_data(handle, bh);
 		mark_buffer_dirty(bh);
 	}
+stop:
+	if (handle)
+		ext3_journal_stop(handle);
 
 unlock:
 	unlock_page(page);
@@ -2390,8 +2474,6 @@
 
 int ext3_can_truncate(struct inode *inode)
 {
-	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-		return 0;
 	if (S_ISREG(inode->i_mode))
 		return 1;
 	if (S_ISDIR(inode->i_mode))
@@ -2435,7 +2517,6 @@
 	struct ext3_inode_info *ei = EXT3_I(inode);
 	__le32 *i_data = ei->i_data;
 	int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
-	struct address_space *mapping = inode->i_mapping;
 	int offsets[4];
 	Indirect chain[4];
 	Indirect *partial;
@@ -2443,7 +2524,8 @@
 	int n;
 	long last_block;
 	unsigned blocksize = inode->i_sb->s_blocksize;
-	struct page *page;
+
+	trace_ext3_truncate_enter(inode);
 
 	if (!ext3_can_truncate(inode))
 		goto out_notrans;
@@ -2451,37 +2533,12 @@
 	if (inode->i_size == 0 && ext3_should_writeback_data(inode))
 		ext3_set_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE);
 
-	/*
-	 * We have to lock the EOF page here, because lock_page() nests
-	 * outside journal_start().
-	 */
-	if ((inode->i_size & (blocksize - 1)) == 0) {
-		/* Block boundary? Nothing to do */
-		page = NULL;
-	} else {
-		page = grab_cache_page(mapping,
-				inode->i_size >> PAGE_CACHE_SHIFT);
-		if (!page)
-			goto out_notrans;
-	}
-
 	handle = start_transaction(inode);
-	if (IS_ERR(handle)) {
-		if (page) {
-			clear_highpage(page);
-			flush_dcache_page(page);
-			unlock_page(page);
-			page_cache_release(page);
-		}
+	if (IS_ERR(handle))
 		goto out_notrans;
-	}
 
 	last_block = (inode->i_size + blocksize-1)
 					>> EXT3_BLOCK_SIZE_BITS(inode->i_sb);
-
-	if (page)
-		ext3_block_truncate_page(handle, page, mapping, inode->i_size);
-
 	n = ext3_block_to_path(inode, last_block, offsets, NULL);
 	if (n == 0)
 		goto out_stop;	/* error */
@@ -2596,6 +2653,7 @@
 		ext3_orphan_del(handle, inode);
 
 	ext3_journal_stop(handle);
+	trace_ext3_truncate_exit(inode);
 	return;
 out_notrans:
 	/*
@@ -2604,6 +2662,7 @@
 	 */
 	if (inode->i_nlink)
 		ext3_orphan_del(NULL, inode);
+	trace_ext3_truncate_exit(inode);
 }
 
 static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb,
@@ -2745,6 +2804,7 @@
 		 * has in-inode xattrs, or we don't have this inode in memory.
 		 * Read the block from disk.
 		 */
+		trace_ext3_load_inode(inode);
 		get_bh(bh);
 		bh->b_end_io = end_buffer_read_sync;
 		submit_bh(READ_META, bh);
@@ -3229,18 +3289,36 @@
 		}
 
 		error = ext3_orphan_add(handle, inode);
+		if (error) {
+			ext3_journal_stop(handle);
+			goto err_out;
+		}
 		EXT3_I(inode)->i_disksize = attr->ia_size;
-		rc = ext3_mark_inode_dirty(handle, inode);
-		if (!error)
-			error = rc;
+		error = ext3_mark_inode_dirty(handle, inode);
 		ext3_journal_stop(handle);
+		if (error) {
+			/* Some hard fs error must have happened. Bail out. */
+			ext3_orphan_del(NULL, inode);
+			goto err_out;
+		}
+		rc = ext3_block_truncate_page(inode, attr->ia_size);
+		if (rc) {
+			/* Cleanup orphan list and exit */
+			handle = ext3_journal_start(inode, 3);
+			if (IS_ERR(handle)) {
+				ext3_orphan_del(NULL, inode);
+				goto err_out;
+			}
+			ext3_orphan_del(handle, inode);
+			ext3_journal_stop(handle);
+			goto err_out;
+		}
 	}
 
 	if ((attr->ia_valid & ATTR_SIZE) &&
 	    attr->ia_size != i_size_read(inode)) {
-		rc = vmtruncate(inode, attr->ia_size);
-		if (rc)
-			goto err_out;
+		truncate_setsize(inode, attr->ia_size);
+		ext3_truncate(inode);
 	}
 
 	setattr_copy(inode, attr);
@@ -3374,6 +3452,7 @@
 	int err;
 
 	might_sleep();
+	trace_ext3_mark_inode_dirty(inode, _RET_IP_);
 	err = ext3_reserve_inode_write(handle, inode, &iloc);
 	if (!err)
 		err = ext3_mark_iloc_dirty(handle, inode, &iloc);
diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c
index f4090bd..c7f4394 100644
--- a/fs/ext3/ioctl.c
+++ b/fs/ext3/ioctl.c
@@ -285,7 +285,7 @@
 		if (!capable(CAP_SYS_ADMIN))
 			return -EPERM;
 
-		if (copy_from_user(&range, (struct fstrim_range *)arg,
+		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
 				   sizeof(range)))
 			return -EFAULT;
 
@@ -293,7 +293,7 @@
 		if (ret < 0)
 			return ret;
 
-		if (copy_to_user((struct fstrim_range *)arg, &range,
+		if (copy_to_user((struct fstrim_range __user *)arg, &range,
 				 sizeof(range)))
 			return -EFAULT;
 
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index 3b57230..6e18a0b 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -36,6 +36,7 @@
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
+#include <trace/events/ext3.h>
 
 #include "namei.h"
 #include "xattr.h"
@@ -287,7 +288,7 @@
 				while (len--) printk("%c", *name++);
 				ext3fs_dirhash(de->name, de->name_len, &h);
 				printk(":%x.%u ", h.hash,
-				       ((char *) de - base));
+				       (unsigned) ((char *) de - base));
 			}
 			space += EXT3_DIR_REC_LEN(de->name_len);
 			names++;
@@ -1013,7 +1014,7 @@
 
 	*err = -ENOENT;
 errout:
-	dxtrace(printk("%s not found\n", name));
+	dxtrace(printk("%s not found\n", entry->name));
 	dx_release (frames);
 	return NULL;
 }
@@ -2140,6 +2141,7 @@
 	struct ext3_dir_entry_2 * de;
 	handle_t *handle;
 
+	trace_ext3_unlink_enter(dir, dentry);
 	/* Initialize quotas before so that eventual writes go
 	 * in separate transaction */
 	dquot_initialize(dir);
@@ -2185,6 +2187,7 @@
 end_unlink:
 	ext3_journal_stop(handle);
 	brelse (bh);
+	trace_ext3_unlink_exit(dentry, retval);
 	return retval;
 }
 
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index b57ea2f..7beb69a 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -44,6 +44,9 @@
 #include "acl.h"
 #include "namei.h"
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/ext3.h>
+
 #ifdef CONFIG_EXT3_DEFAULTS_TO_ORDERED
   #define EXT3_MOUNT_DEFAULT_DATA_MODE EXT3_MOUNT_ORDERED_DATA
 #else
@@ -497,6 +500,14 @@
 	return &ei->vfs_inode;
 }
 
+static int ext3_drop_inode(struct inode *inode)
+{
+	int drop = generic_drop_inode(inode);
+
+	trace_ext3_drop_inode(inode, drop);
+	return drop;
+}
+
 static void ext3_i_callback(struct rcu_head *head)
 {
 	struct inode *inode = container_of(head, struct inode, i_rcu);
@@ -788,6 +799,7 @@
 	.destroy_inode	= ext3_destroy_inode,
 	.write_inode	= ext3_write_inode,
 	.dirty_inode	= ext3_dirty_inode,
+	.drop_inode	= ext3_drop_inode,
 	.evict_inode	= ext3_evict_inode,
 	.put_super	= ext3_put_super,
 	.sync_fs	= ext3_sync_fs,
@@ -2509,6 +2521,7 @@
 {
 	tid_t target;
 
+	trace_ext3_sync_fs(sb, wait);
 	if (journal_start_commit(EXT3_SB(sb)->s_journal, &target)) {
 		if (wait)
 			log_wait_commit(EXT3_SB(sb)->s_journal, target);
diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c
index 32e6cc2..d565759 100644
--- a/fs/ext3/xattr.c
+++ b/fs/ext3/xattr.c
@@ -803,8 +803,16 @@
 			/* We need to allocate a new block */
 			ext3_fsblk_t goal = ext3_group_first_block_no(sb,
 						EXT3_I(inode)->i_block_group);
-			ext3_fsblk_t block = ext3_new_block(handle, inode,
-							goal, &error);
+			ext3_fsblk_t block;
+
+			/*
+			 * Protect us agaist concurrent allocations to the
+			 * same inode from ext3_..._writepage(). Reservation
+			 * code does not expect racing allocations.
+			 */
+			mutex_lock(&EXT3_I(inode)->truncate_mutex);
+			block = ext3_new_block(handle, inode, goal, &error);
+			mutex_unlock(&EXT3_I(inode)->truncate_mutex);
 			if (error)
 				goto cleanup;
 			ea_idebug(inode, "creating block %d", block);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 678cde8..3e5191f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2741,7 +2741,7 @@
 	index = wbc->range_start >> PAGE_CACHE_SHIFT;
 	end = wbc->range_end >> PAGE_CACHE_SHIFT;
 
-	if (wbc->sync_mode == WB_SYNC_ALL)
+	if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
 		tag = PAGECACHE_TAG_TOWRITE;
 	else
 		tag = PAGECACHE_TAG_DIRTY;
@@ -2973,7 +2973,7 @@
 	}
 
 retry:
-	if (wbc->sync_mode == WB_SYNC_ALL)
+	if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
 		tag_pages_for_writeback(mapping, index, end);
 
 	while (!ret && wbc->nr_to_write > 0) {
diff --git a/fs/file_table.c b/fs/file_table.c
index 01e4c1e..c322794 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -25,7 +25,7 @@
 #include <linux/percpu.h>
 #include <linux/ima.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "internal.h"
 
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index b8c507c..1599aa9 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -35,7 +35,9 @@
 struct wb_writeback_work {
 	long nr_pages;
 	struct super_block *sb;
+	unsigned long *older_than_this;
 	enum writeback_sync_modes sync_mode;
+	unsigned int tagged_writepages:1;
 	unsigned int for_kupdate:1;
 	unsigned int range_cyclic:1;
 	unsigned int for_background:1;
@@ -180,11 +182,12 @@
  */
 void inode_wb_list_del(struct inode *inode)
 {
-	spin_lock(&inode_wb_list_lock);
-	list_del_init(&inode->i_wb_list);
-	spin_unlock(&inode_wb_list_lock);
-}
+	struct backing_dev_info *bdi = inode_to_bdi(inode);
 
+	spin_lock(&bdi->wb.list_lock);
+	list_del_init(&inode->i_wb_list);
+	spin_unlock(&bdi->wb.list_lock);
+}
 
 /*
  * Redirty an inode: set its when-it-was dirtied timestamp and move it to the
@@ -195,11 +198,9 @@
  * the case then the inode must have been redirtied while it was being written
  * out and we don't reset its dirtied_when.
  */
-static void redirty_tail(struct inode *inode)
+static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
 {
-	struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
-
-	assert_spin_locked(&inode_wb_list_lock);
+	assert_spin_locked(&wb->list_lock);
 	if (!list_empty(&wb->b_dirty)) {
 		struct inode *tail;
 
@@ -213,11 +214,9 @@
 /*
  * requeue inode for re-scanning after bdi->b_io list is exhausted.
  */
-static void requeue_io(struct inode *inode)
+static void requeue_io(struct inode *inode, struct bdi_writeback *wb)
 {
-	struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
-
-	assert_spin_locked(&inode_wb_list_lock);
+	assert_spin_locked(&wb->list_lock);
 	list_move(&inode->i_wb_list, &wb->b_more_io);
 }
 
@@ -225,7 +224,7 @@
 {
 	/*
 	 * Prevent speculative execution through
-	 * spin_unlock(&inode_wb_list_lock);
+	 * spin_unlock(&wb->list_lock);
 	 */
 
 	smp_mb();
@@ -250,15 +249,16 @@
 /*
  * Move expired dirty inodes from @delaying_queue to @dispatch_queue.
  */
-static void move_expired_inodes(struct list_head *delaying_queue,
+static int move_expired_inodes(struct list_head *delaying_queue,
 			       struct list_head *dispatch_queue,
-				unsigned long *older_than_this)
+			       unsigned long *older_than_this)
 {
 	LIST_HEAD(tmp);
 	struct list_head *pos, *node;
 	struct super_block *sb = NULL;
 	struct inode *inode;
 	int do_sb_sort = 0;
+	int moved = 0;
 
 	while (!list_empty(delaying_queue)) {
 		inode = wb_inode(delaying_queue->prev);
@@ -269,12 +269,13 @@
 			do_sb_sort = 1;
 		sb = inode->i_sb;
 		list_move(&inode->i_wb_list, &tmp);
+		moved++;
 	}
 
 	/* just one sb in list, splice to dispatch_queue and we're done */
 	if (!do_sb_sort) {
 		list_splice(&tmp, dispatch_queue);
-		return;
+		goto out;
 	}
 
 	/* Move inodes from one superblock together */
@@ -286,6 +287,8 @@
 				list_move(&inode->i_wb_list, dispatch_queue);
 		}
 	}
+out:
+	return moved;
 }
 
 /*
@@ -301,9 +304,11 @@
  */
 static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this)
 {
-	assert_spin_locked(&inode_wb_list_lock);
+	int moved;
+	assert_spin_locked(&wb->list_lock);
 	list_splice_init(&wb->b_more_io, &wb->b_io);
-	move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this);
+	moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this);
+	trace_writeback_queue_io(wb, older_than_this, moved);
 }
 
 static int write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -316,7 +321,8 @@
 /*
  * Wait for writeback on an inode to complete.
  */
-static void inode_wait_for_writeback(struct inode *inode)
+static void inode_wait_for_writeback(struct inode *inode,
+				     struct bdi_writeback *wb)
 {
 	DEFINE_WAIT_BIT(wq, &inode->i_state, __I_SYNC);
 	wait_queue_head_t *wqh;
@@ -324,15 +330,15 @@
 	wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
 	while (inode->i_state & I_SYNC) {
 		spin_unlock(&inode->i_lock);
-		spin_unlock(&inode_wb_list_lock);
+		spin_unlock(&wb->list_lock);
 		__wait_on_bit(wqh, &wq, inode_wait, TASK_UNINTERRUPTIBLE);
-		spin_lock(&inode_wb_list_lock);
+		spin_lock(&wb->list_lock);
 		spin_lock(&inode->i_lock);
 	}
 }
 
 /*
- * Write out an inode's dirty pages.  Called under inode_wb_list_lock and
+ * Write out an inode's dirty pages.  Called under wb->list_lock and
  * inode->i_lock.  Either the caller has an active reference on the inode or
  * the inode has I_WILL_FREE set.
  *
@@ -343,13 +349,15 @@
  * livelocks, etc.
  */
 static int
-writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
+writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
+		       struct writeback_control *wbc)
 {
 	struct address_space *mapping = inode->i_mapping;
+	long nr_to_write = wbc->nr_to_write;
 	unsigned dirty;
 	int ret;
 
-	assert_spin_locked(&inode_wb_list_lock);
+	assert_spin_locked(&wb->list_lock);
 	assert_spin_locked(&inode->i_lock);
 
 	if (!atomic_read(&inode->i_count))
@@ -367,14 +375,16 @@
 		 * completed a full scan of b_io.
 		 */
 		if (wbc->sync_mode != WB_SYNC_ALL) {
-			requeue_io(inode);
+			requeue_io(inode, wb);
+			trace_writeback_single_inode_requeue(inode, wbc,
+							     nr_to_write);
 			return 0;
 		}
 
 		/*
 		 * It's a data-integrity sync.  We must wait.
 		 */
-		inode_wait_for_writeback(inode);
+		inode_wait_for_writeback(inode, wb);
 	}
 
 	BUG_ON(inode->i_state & I_SYNC);
@@ -383,7 +393,7 @@
 	inode->i_state |= I_SYNC;
 	inode->i_state &= ~I_DIRTY_PAGES;
 	spin_unlock(&inode->i_lock);
-	spin_unlock(&inode_wb_list_lock);
+	spin_unlock(&wb->list_lock);
 
 	ret = do_writepages(mapping, wbc);
 
@@ -414,10 +424,19 @@
 			ret = err;
 	}
 
-	spin_lock(&inode_wb_list_lock);
+	spin_lock(&wb->list_lock);
 	spin_lock(&inode->i_lock);
 	inode->i_state &= ~I_SYNC;
 	if (!(inode->i_state & I_FREEING)) {
+		/*
+		 * Sync livelock prevention. Each inode is tagged and synced in
+		 * one shot. If still dirty, it will be redirty_tail()'ed below.
+		 * Update the dirty time to prevent enqueue and sync it again.
+		 */
+		if ((inode->i_state & I_DIRTY) &&
+		    (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages))
+			inode->dirtied_when = jiffies;
+
 		if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
 			/*
 			 * We didn't write back all the pages.  nfs_writepages()
@@ -428,7 +447,7 @@
 				/*
 				 * slice used up: queue for next turn
 				 */
-				requeue_io(inode);
+				requeue_io(inode, wb);
 			} else {
 				/*
 				 * Writeback blocked by something other than
@@ -437,7 +456,7 @@
 				 * retrying writeback of the dirty page/inode
 				 * that cannot be performed immediately.
 				 */
-				redirty_tail(inode);
+				redirty_tail(inode, wb);
 			}
 		} else if (inode->i_state & I_DIRTY) {
 			/*
@@ -446,7 +465,7 @@
 			 * submission or metadata updates after data IO
 			 * completion.
 			 */
-			redirty_tail(inode);
+			redirty_tail(inode, wb);
 		} else {
 			/*
 			 * The inode is clean.  At this point we either have
@@ -457,9 +476,41 @@
 		}
 	}
 	inode_sync_complete(inode);
+	trace_writeback_single_inode(inode, wbc, nr_to_write);
 	return ret;
 }
 
+static long writeback_chunk_size(struct backing_dev_info *bdi,
+				 struct wb_writeback_work *work)
+{
+	long pages;
+
+	/*
+	 * WB_SYNC_ALL mode does livelock avoidance by syncing dirty
+	 * inodes/pages in one big loop. Setting wbc.nr_to_write=LONG_MAX
+	 * here avoids calling into writeback_inodes_wb() more than once.
+	 *
+	 * The intended call sequence for WB_SYNC_ALL writeback is:
+	 *
+	 *      wb_writeback()
+	 *          writeback_sb_inodes()       <== called only once
+	 *              write_cache_pages()     <== called once for each inode
+	 *                   (quickly) tag currently dirty pages
+	 *                   (maybe slowly) sync all tagged pages
+	 */
+	if (work->sync_mode == WB_SYNC_ALL || work->tagged_writepages)
+		pages = LONG_MAX;
+	else {
+		pages = min(bdi->avg_write_bandwidth / 2,
+			    global_dirty_limit / DIRTY_SCOPE);
+		pages = min(pages, work->nr_pages);
+		pages = round_down(pages + MIN_WRITEBACK_PAGES,
+				   MIN_WRITEBACK_PAGES);
+	}
+
+	return pages;
+}
+
 /*
  * Write a portion of b_io inodes which belong to @sb.
  *
@@ -467,24 +518,36 @@
  * inodes. Otherwise write only ones which go sequentially
  * in reverse order.
  *
- * Return 1, if the caller writeback routine should be
- * interrupted. Otherwise return 0.
+ * Return the number of pages and/or inodes written.
  */
-static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb,
-		struct writeback_control *wbc, bool only_this_sb)
+static long writeback_sb_inodes(struct super_block *sb,
+				struct bdi_writeback *wb,
+				struct wb_writeback_work *work)
 {
+	struct writeback_control wbc = {
+		.sync_mode		= work->sync_mode,
+		.tagged_writepages	= work->tagged_writepages,
+		.for_kupdate		= work->for_kupdate,
+		.for_background		= work->for_background,
+		.range_cyclic		= work->range_cyclic,
+		.range_start		= 0,
+		.range_end		= LLONG_MAX,
+	};
+	unsigned long start_time = jiffies;
+	long write_chunk;
+	long wrote = 0;  /* count both pages and inodes */
+
 	while (!list_empty(&wb->b_io)) {
-		long pages_skipped;
 		struct inode *inode = wb_inode(wb->b_io.prev);
 
 		if (inode->i_sb != sb) {
-			if (only_this_sb) {
+			if (work->sb) {
 				/*
 				 * We only want to write back data for this
 				 * superblock, move all inodes not belonging
 				 * to it back onto the dirty list.
 				 */
-				redirty_tail(inode);
+				redirty_tail(inode, wb);
 				continue;
 			}
 
@@ -493,7 +556,7 @@
 			 * Bounce back to the caller to unpin this and
 			 * pin the next superblock.
 			 */
-			return 0;
+			break;
 		}
 
 		/*
@@ -504,96 +567,92 @@
 		spin_lock(&inode->i_lock);
 		if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
 			spin_unlock(&inode->i_lock);
-			requeue_io(inode);
+			redirty_tail(inode, wb);
 			continue;
 		}
-
-		/*
-		 * Was this inode dirtied after sync_sb_inodes was called?
-		 * This keeps sync from extra jobs and livelock.
-		 */
-		if (inode_dirtied_after(inode, wbc->wb_start)) {
-			spin_unlock(&inode->i_lock);
-			return 1;
-		}
-
 		__iget(inode);
+		write_chunk = writeback_chunk_size(wb->bdi, work);
+		wbc.nr_to_write = write_chunk;
+		wbc.pages_skipped = 0;
 
-		pages_skipped = wbc->pages_skipped;
-		writeback_single_inode(inode, wbc);
-		if (wbc->pages_skipped != pages_skipped) {
+		writeback_single_inode(inode, wb, &wbc);
+
+		work->nr_pages -= write_chunk - wbc.nr_to_write;
+		wrote += write_chunk - wbc.nr_to_write;
+		if (!(inode->i_state & I_DIRTY))
+			wrote++;
+		if (wbc.pages_skipped) {
 			/*
 			 * writeback is not making progress due to locked
 			 * buffers.  Skip this inode for now.
 			 */
-			redirty_tail(inode);
+			redirty_tail(inode, wb);
 		}
 		spin_unlock(&inode->i_lock);
-		spin_unlock(&inode_wb_list_lock);
+		spin_unlock(&wb->list_lock);
 		iput(inode);
 		cond_resched();
-		spin_lock(&inode_wb_list_lock);
-		if (wbc->nr_to_write <= 0) {
-			wbc->more_io = 1;
-			return 1;
+		spin_lock(&wb->list_lock);
+		/*
+		 * bail out to wb_writeback() often enough to check
+		 * background threshold and other termination conditions.
+		 */
+		if (wrote) {
+			if (time_is_before_jiffies(start_time + HZ / 10UL))
+				break;
+			if (work->nr_pages <= 0)
+				break;
 		}
-		if (!list_empty(&wb->b_more_io))
-			wbc->more_io = 1;
 	}
-	/* b_io is empty */
-	return 1;
+	return wrote;
 }
 
-void writeback_inodes_wb(struct bdi_writeback *wb,
-		struct writeback_control *wbc)
+static long __writeback_inodes_wb(struct bdi_writeback *wb,
+				  struct wb_writeback_work *work)
 {
-	int ret = 0;
-
-	if (!wbc->wb_start)
-		wbc->wb_start = jiffies; /* livelock avoidance */
-	spin_lock(&inode_wb_list_lock);
-	if (!wbc->for_kupdate || list_empty(&wb->b_io))
-		queue_io(wb, wbc->older_than_this);
+	unsigned long start_time = jiffies;
+	long wrote = 0;
 
 	while (!list_empty(&wb->b_io)) {
 		struct inode *inode = wb_inode(wb->b_io.prev);
 		struct super_block *sb = inode->i_sb;
 
 		if (!grab_super_passive(sb)) {
-			requeue_io(inode);
+			requeue_io(inode, wb);
 			continue;
 		}
-		ret = writeback_sb_inodes(sb, wb, wbc, false);
+		wrote += writeback_sb_inodes(sb, wb, work);
 		drop_super(sb);
 
-		if (ret)
-			break;
+		/* refer to the same tests at the end of writeback_sb_inodes */
+		if (wrote) {
+			if (time_is_before_jiffies(start_time + HZ / 10UL))
+				break;
+			if (work->nr_pages <= 0)
+				break;
+		}
 	}
-	spin_unlock(&inode_wb_list_lock);
 	/* Leave any unwritten inodes on b_io */
+	return wrote;
 }
 
-static void __writeback_inodes_sb(struct super_block *sb,
-		struct bdi_writeback *wb, struct writeback_control *wbc)
+long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages)
 {
-	WARN_ON(!rwsem_is_locked(&sb->s_umount));
+	struct wb_writeback_work work = {
+		.nr_pages	= nr_pages,
+		.sync_mode	= WB_SYNC_NONE,
+		.range_cyclic	= 1,
+	};
 
-	spin_lock(&inode_wb_list_lock);
-	if (!wbc->for_kupdate || list_empty(&wb->b_io))
-		queue_io(wb, wbc->older_than_this);
-	writeback_sb_inodes(sb, wb, wbc, true);
-	spin_unlock(&inode_wb_list_lock);
+	spin_lock(&wb->list_lock);
+	if (list_empty(&wb->b_io))
+		queue_io(wb, NULL);
+	__writeback_inodes_wb(wb, &work);
+	spin_unlock(&wb->list_lock);
+
+	return nr_pages - work.nr_pages;
 }
 
-/*
- * The maximum number of pages to writeout in a single bdi flush/kupdate
- * operation.  We do this so we don't hold I_SYNC against an inode for
- * enormous amounts of time, which would block a userspace task which has
- * been forced to throttle against that inode.  Also, the code reevaluates
- * the dirty each time it has written this many pages.
- */
-#define MAX_WRITEBACK_PAGES     1024
-
 static inline bool over_bground_thresh(void)
 {
 	unsigned long background_thresh, dirty_thresh;
@@ -605,6 +664,16 @@
 }
 
 /*
+ * Called under wb->list_lock. If there are multiple wb per bdi,
+ * only the flusher working on the first wb should do it.
+ */
+static void wb_update_bandwidth(struct bdi_writeback *wb,
+				unsigned long start_time)
+{
+	__bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, start_time);
+}
+
+/*
  * Explicit flushing or periodic writeback of "old" data.
  *
  * Define "old": the first time one of an inode's pages is dirtied, we mark the
@@ -622,47 +691,16 @@
 static long wb_writeback(struct bdi_writeback *wb,
 			 struct wb_writeback_work *work)
 {
-	struct writeback_control wbc = {
-		.sync_mode		= work->sync_mode,
-		.older_than_this	= NULL,
-		.for_kupdate		= work->for_kupdate,
-		.for_background		= work->for_background,
-		.range_cyclic		= work->range_cyclic,
-	};
+	unsigned long wb_start = jiffies;
+	long nr_pages = work->nr_pages;
 	unsigned long oldest_jif;
-	long wrote = 0;
-	long write_chunk;
 	struct inode *inode;
+	long progress;
 
-	if (wbc.for_kupdate) {
-		wbc.older_than_this = &oldest_jif;
-		oldest_jif = jiffies -
-				msecs_to_jiffies(dirty_expire_interval * 10);
-	}
-	if (!wbc.range_cyclic) {
-		wbc.range_start = 0;
-		wbc.range_end = LLONG_MAX;
-	}
+	oldest_jif = jiffies;
+	work->older_than_this = &oldest_jif;
 
-	/*
-	 * WB_SYNC_ALL mode does livelock avoidance by syncing dirty
-	 * inodes/pages in one big loop. Setting wbc.nr_to_write=LONG_MAX
-	 * here avoids calling into writeback_inodes_wb() more than once.
-	 *
-	 * The intended call sequence for WB_SYNC_ALL writeback is:
-	 *
-	 *      wb_writeback()
-	 *          __writeback_inodes_sb()     <== called only once
-	 *              write_cache_pages()     <== called once for each inode
-	 *                   (quickly) tag currently dirty pages
-	 *                   (maybe slowly) sync all tagged pages
-	 */
-	if (wbc.sync_mode == WB_SYNC_NONE)
-		write_chunk = MAX_WRITEBACK_PAGES;
-	else
-		write_chunk = LONG_MAX;
-
-	wbc.wb_start = jiffies; /* livelock avoidance */
+	spin_lock(&wb->list_lock);
 	for (;;) {
 		/*
 		 * Stop writeback when nr_pages has been consumed
@@ -687,52 +725,54 @@
 		if (work->for_background && !over_bground_thresh())
 			break;
 
-		wbc.more_io = 0;
-		wbc.nr_to_write = write_chunk;
-		wbc.pages_skipped = 0;
+		if (work->for_kupdate) {
+			oldest_jif = jiffies -
+				msecs_to_jiffies(dirty_expire_interval * 10);
+			work->older_than_this = &oldest_jif;
+		}
 
-		trace_wbc_writeback_start(&wbc, wb->bdi);
+		trace_writeback_start(wb->bdi, work);
+		if (list_empty(&wb->b_io))
+			queue_io(wb, work->older_than_this);
 		if (work->sb)
-			__writeback_inodes_sb(work->sb, wb, &wbc);
+			progress = writeback_sb_inodes(work->sb, wb, work);
 		else
-			writeback_inodes_wb(wb, &wbc);
-		trace_wbc_writeback_written(&wbc, wb->bdi);
+			progress = __writeback_inodes_wb(wb, work);
+		trace_writeback_written(wb->bdi, work);
 
-		work->nr_pages -= write_chunk - wbc.nr_to_write;
-		wrote += write_chunk - wbc.nr_to_write;
+		wb_update_bandwidth(wb, wb_start);
 
 		/*
-		 * If we consumed everything, see if we have more
-		 */
-		if (wbc.nr_to_write <= 0)
-			continue;
-		/*
-		 * Didn't write everything and we don't have more IO, bail
-		 */
-		if (!wbc.more_io)
-			break;
-		/*
 		 * Did we write something? Try for more
+		 *
+		 * Dirty inodes are moved to b_io for writeback in batches.
+		 * The completion of the current batch does not necessarily
+		 * mean the overall work is done. So we keep looping as long
+		 * as made some progress on cleaning pages or inodes.
 		 */
-		if (wbc.nr_to_write < write_chunk)
+		if (progress)
 			continue;
 		/*
+		 * No more inodes for IO, bail
+		 */
+		if (list_empty(&wb->b_more_io))
+			break;
+		/*
 		 * Nothing written. Wait for some inode to
 		 * become available for writeback. Otherwise
 		 * we'll just busyloop.
 		 */
-		spin_lock(&inode_wb_list_lock);
 		if (!list_empty(&wb->b_more_io))  {
+			trace_writeback_wait(wb->bdi, work);
 			inode = wb_inode(wb->b_more_io.prev);
-			trace_wbc_writeback_wait(&wbc, wb->bdi);
 			spin_lock(&inode->i_lock);
-			inode_wait_for_writeback(inode);
+			inode_wait_for_writeback(inode, wb);
 			spin_unlock(&inode->i_lock);
 		}
-		spin_unlock(&inode_wb_list_lock);
 	}
+	spin_unlock(&wb->list_lock);
 
-	return wrote;
+	return nr_pages - work->nr_pages;
 }
 
 /*
@@ -1063,10 +1103,10 @@
 			}
 
 			spin_unlock(&inode->i_lock);
-			spin_lock(&inode_wb_list_lock);
+			spin_lock(&bdi->wb.list_lock);
 			inode->dirtied_when = jiffies;
 			list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
-			spin_unlock(&inode_wb_list_lock);
+			spin_unlock(&bdi->wb.list_lock);
 
 			if (wakeup_bdi)
 				bdi_wakeup_thread_delayed(bdi);
@@ -1162,10 +1202,11 @@
 {
 	DECLARE_COMPLETION_ONSTACK(done);
 	struct wb_writeback_work work = {
-		.sb		= sb,
-		.sync_mode	= WB_SYNC_NONE,
-		.done		= &done,
-		.nr_pages	= nr,
+		.sb			= sb,
+		.sync_mode		= WB_SYNC_NONE,
+		.tagged_writepages	= 1,
+		.done			= &done,
+		.nr_pages		= nr,
 	};
 
 	WARN_ON(!rwsem_is_locked(&sb->s_umount));
@@ -1267,6 +1308,7 @@
  */
 int write_inode_now(struct inode *inode, int sync)
 {
+	struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
 	int ret;
 	struct writeback_control wbc = {
 		.nr_to_write = LONG_MAX,
@@ -1279,11 +1321,11 @@
 		wbc.nr_to_write = 0;
 
 	might_sleep();
-	spin_lock(&inode_wb_list_lock);
+	spin_lock(&wb->list_lock);
 	spin_lock(&inode->i_lock);
-	ret = writeback_single_inode(inode, &wbc);
+	ret = writeback_single_inode(inode, wb, &wbc);
 	spin_unlock(&inode->i_lock);
-	spin_unlock(&inode_wb_list_lock);
+	spin_unlock(&wb->list_lock);
 	if (sync)
 		inode_sync_wait(inode);
 	return ret;
@@ -1303,13 +1345,14 @@
  */
 int sync_inode(struct inode *inode, struct writeback_control *wbc)
 {
+	struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
 	int ret;
 
-	spin_lock(&inode_wb_list_lock);
+	spin_lock(&wb->list_lock);
 	spin_lock(&inode->i_lock);
-	ret = writeback_single_inode(inode, wbc);
+	ret = writeback_single_inode(inode, wb, wbc);
 	spin_unlock(&inode->i_lock);
-	spin_unlock(&inode_wb_list_lock);
+	spin_unlock(&wb->list_lock);
 	return ret;
 }
 EXPORT_SYMBOL(sync_inode);
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index 29e1ace..8a139ff 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -16,7 +16,7 @@
 #include <linux/gfs2_ondisk.h>
 #include <linux/rcupdate.h>
 #include <linux/rculist_bl.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "gfs2.h"
 #include "incore.h"
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 516516e..3bc073a 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -1018,13 +1018,13 @@
 		fsname++;
 	if (lm->lm_mount == NULL) {
 		fs_info(sdp, "Now mounting FS...\n");
-		complete(&sdp->sd_locking_init);
+		complete_all(&sdp->sd_locking_init);
 		return 0;
 	}
 	ret = lm->lm_mount(sdp, fsname);
 	if (ret == 0)
 		fs_info(sdp, "Joined cluster. Now mounting FS...\n");
-	complete(&sdp->sd_locking_init);
+	complete_all(&sdp->sd_locking_init);
 	return ret;
 }
 
diff --git a/fs/inode.c b/fs/inode.c
index 96c77b8..d0c72ff 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -37,7 +37,7 @@
  *   inode->i_sb->s_inode_lru, inode->i_lru
  * inode_sb_list_lock protects:
  *   sb->s_inodes, inode->i_sb_list
- * inode_wb_list_lock protects:
+ * bdi->wb.list_lock protects:
  *   bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list
  * inode_hash_lock protects:
  *   inode_hashtable, inode->i_hash
@@ -48,7 +48,7 @@
  *   inode->i_lock
  *     inode->i_sb->s_inode_lru_lock
  *
- * inode_wb_list_lock
+ * bdi->wb.list_lock
  *   inode->i_lock
  *
  * inode_hash_lock
@@ -65,7 +65,6 @@
 static __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_hash_lock);
 
 __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock);
-__cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_wb_list_lock);
 
 /*
  * Empty aops. Can be used for the cases where the user does not
@@ -362,9 +361,11 @@
 
 static inline void inode_sb_list_del(struct inode *inode)
 {
-	spin_lock(&inode_sb_list_lock);
-	list_del_init(&inode->i_sb_list);
-	spin_unlock(&inode_sb_list_lock);
+	if (!list_empty(&inode->i_sb_list)) {
+		spin_lock(&inode_sb_list_lock);
+		list_del_init(&inode->i_sb_list);
+		spin_unlock(&inode_sb_list_lock);
+	}
 }
 
 static unsigned long hash(struct super_block *sb, unsigned long hashval)
@@ -797,6 +798,29 @@
 EXPORT_SYMBOL(get_next_ino);
 
 /**
+ *	new_inode_pseudo 	- obtain an inode
+ *	@sb: superblock
+ *
+ *	Allocates a new inode for given superblock.
+ *	Inode wont be chained in superblock s_inodes list
+ *	This means :
+ *	- fs can't be unmount
+ *	- quotas, fsnotify, writeback can't work
+ */
+struct inode *new_inode_pseudo(struct super_block *sb)
+{
+	struct inode *inode = alloc_inode(sb);
+
+	if (inode) {
+		spin_lock(&inode->i_lock);
+		inode->i_state = 0;
+		spin_unlock(&inode->i_lock);
+		INIT_LIST_HEAD(&inode->i_sb_list);
+	}
+	return inode;
+}
+
+/**
  *	new_inode 	- obtain an inode
  *	@sb: superblock
  *
@@ -814,13 +838,9 @@
 
 	spin_lock_prefetch(&inode_sb_list_lock);
 
-	inode = alloc_inode(sb);
-	if (inode) {
-		spin_lock(&inode->i_lock);
-		inode->i_state = 0;
-		spin_unlock(&inode->i_lock);
+	inode = new_inode_pseudo(sb);
+	if (inode)
 		inode_sb_list_add(inode);
-	}
 	return inode;
 }
 EXPORT_SYMBOL(new_inode);
diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c
index e4b87bc..f94fc48 100644
--- a/fs/jbd/checkpoint.c
+++ b/fs/jbd/checkpoint.c
@@ -22,6 +22,8 @@
 #include <linux/jbd.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <trace/events/jbd.h>
 
 /*
  * Unlink a buffer from a transaction checkpoint list.
@@ -95,10 +97,14 @@
 
 	if (jh->b_jlist == BJ_None && !buffer_locked(bh) &&
 	    !buffer_dirty(bh) && !buffer_write_io_error(bh)) {
+		/*
+		 * Get our reference so that bh cannot be freed before
+		 * we unlock it
+		 */
+		get_bh(bh);
 		JBUFFER_TRACE(jh, "remove from checkpoint list");
 		ret = __journal_remove_checkpoint(jh) + 1;
 		jbd_unlock_bh_state(bh);
-		journal_remove_journal_head(bh);
 		BUFFER_TRACE(bh, "release");
 		__brelse(bh);
 	} else {
@@ -220,8 +226,8 @@
 			spin_lock(&journal->j_list_lock);
 			goto restart;
 		}
+		get_bh(bh);
 		if (buffer_locked(bh)) {
-			get_bh(bh);
 			spin_unlock(&journal->j_list_lock);
 			jbd_unlock_bh_state(bh);
 			wait_on_buffer(bh);
@@ -240,7 +246,6 @@
 		 */
 		released = __journal_remove_checkpoint(jh);
 		jbd_unlock_bh_state(bh);
-		journal_remove_journal_head(bh);
 		__brelse(bh);
 	}
 
@@ -253,9 +258,12 @@
 __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count)
 {
 	int i;
+	struct blk_plug plug;
 
+	blk_start_plug(&plug);
 	for (i = 0; i < *batch_count; i++)
-		write_dirty_buffer(bhs[i], WRITE);
+		write_dirty_buffer(bhs[i], WRITE_SYNC);
+	blk_finish_plug(&plug);
 
 	for (i = 0; i < *batch_count; i++) {
 		struct buffer_head *bh = bhs[i];
@@ -304,12 +312,12 @@
 		ret = 1;
 		if (unlikely(buffer_write_io_error(bh)))
 			ret = -EIO;
+		get_bh(bh);
 		J_ASSERT_JH(jh, !buffer_jbddirty(bh));
 		BUFFER_TRACE(bh, "remove from checkpoint");
 		__journal_remove_checkpoint(jh);
 		spin_unlock(&journal->j_list_lock);
 		jbd_unlock_bh_state(bh);
-		journal_remove_journal_head(bh);
 		__brelse(bh);
 	} else {
 		/*
@@ -358,6 +366,7 @@
 	 * journal straight away.
 	 */
 	result = cleanup_journal_tail(journal);
+	trace_jbd_checkpoint(journal, result);
 	jbd_debug(1, "cleanup_journal_tail returned %d\n", result);
 	if (result <= 0)
 		return result;
@@ -503,6 +512,7 @@
 	if (blocknr < journal->j_tail)
 		freed = freed + journal->j_last - journal->j_first;
 
+	trace_jbd_cleanup_journal_tail(journal, first_tid, blocknr, freed);
 	jbd_debug(1,
 		  "Cleaning journal tail from %d to %d (offset %u), "
 		  "freeing %u\n",
@@ -523,9 +533,9 @@
 /*
  * journal_clean_one_cp_list
  *
- * Find all the written-back checkpoint buffers in the given list and release them.
+ * Find all the written-back checkpoint buffers in the given list and release
+ * them.
  *
- * Called with the journal locked.
  * Called with j_list_lock held.
  * Returns number of bufers reaped (for debug)
  */
@@ -632,8 +642,8 @@
  * checkpoint lists.
  *
  * The function returns 1 if it frees the transaction, 0 otherwise.
+ * The function can free jh and bh.
  *
- * This function is called with the journal locked.
  * This function is called with j_list_lock held.
  * This function is called with jbd_lock_bh_state(jh2bh(jh))
  */
@@ -652,13 +662,14 @@
 	}
 	journal = transaction->t_journal;
 
+	JBUFFER_TRACE(jh, "removing from transaction");
 	__buffer_unlink(jh);
 	jh->b_cp_transaction = NULL;
+	journal_put_journal_head(jh);
 
 	if (transaction->t_checkpoint_list != NULL ||
 	    transaction->t_checkpoint_io_list != NULL)
 		goto out;
-	JBUFFER_TRACE(jh, "transaction has no more buffers");
 
 	/*
 	 * There is one special case to worry about: if we have just pulled the
@@ -669,10 +680,8 @@
 	 * The locking here around t_state is a bit sleazy.
 	 * See the comment at the end of journal_commit_transaction().
 	 */
-	if (transaction->t_state != T_FINISHED) {
-		JBUFFER_TRACE(jh, "belongs to running/committing transaction");
+	if (transaction->t_state != T_FINISHED)
 		goto out;
-	}
 
 	/* OK, that was the last buffer for the transaction: we can now
 	   safely remove this transaction from the log */
@@ -684,7 +693,6 @@
 	wake_up(&journal->j_wait_logspace);
 	ret = 1;
 out:
-	JBUFFER_TRACE(jh, "exit");
 	return ret;
 }
 
@@ -703,6 +711,8 @@
 	J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh)));
 	J_ASSERT_JH(jh, jh->b_cp_transaction == NULL);
 
+	/* Get reference for checkpointing transaction */
+	journal_grab_journal_head(jh2bh(jh));
 	jh->b_cp_transaction = transaction;
 
 	if (!transaction->t_checkpoint_list) {
@@ -752,6 +762,7 @@
 	J_ASSERT(journal->j_committing_transaction != transaction);
 	J_ASSERT(journal->j_running_transaction != transaction);
 
+	trace_jbd_drop_transaction(journal, transaction);
 	jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid);
 	kfree(transaction);
 }
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index 72ffa97..8799207 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -21,6 +21,7 @@
 #include <linux/pagemap.h>
 #include <linux/bio.h>
 #include <linux/blkdev.h>
+#include <trace/events/jbd.h>
 
 /*
  * Default IO end handler for temporary BJ_IO buffer_heads.
@@ -204,6 +205,8 @@
 			if (!trylock_buffer(bh)) {
 				BUFFER_TRACE(bh, "needs blocking lock");
 				spin_unlock(&journal->j_list_lock);
+				trace_jbd_do_submit_data(journal,
+						     commit_transaction);
 				/* Write out all data to prevent deadlocks */
 				journal_do_submit_data(wbuf, bufs, write_op);
 				bufs = 0;
@@ -236,6 +239,8 @@
 			jbd_unlock_bh_state(bh);
 			if (bufs == journal->j_wbufsize) {
 				spin_unlock(&journal->j_list_lock);
+				trace_jbd_do_submit_data(journal,
+						     commit_transaction);
 				journal_do_submit_data(wbuf, bufs, write_op);
 				bufs = 0;
 				goto write_out_data;
@@ -253,10 +258,6 @@
 			jbd_unlock_bh_state(bh);
 			if (locked)
 				unlock_buffer(bh);
-			journal_remove_journal_head(bh);
-			/* One for our safety reference, other for
-			 * journal_remove_journal_head() */
-			put_bh(bh);
 			release_data_buffer(bh);
 		}
 
@@ -266,6 +267,7 @@
 		}
 	}
 	spin_unlock(&journal->j_list_lock);
+	trace_jbd_do_submit_data(journal, commit_transaction);
 	journal_do_submit_data(wbuf, bufs, write_op);
 
 	return err;
@@ -316,12 +318,14 @@
 	commit_transaction = journal->j_running_transaction;
 	J_ASSERT(commit_transaction->t_state == T_RUNNING);
 
+	trace_jbd_start_commit(journal, commit_transaction);
 	jbd_debug(1, "JBD: starting commit of transaction %d\n",
 			commit_transaction->t_tid);
 
 	spin_lock(&journal->j_state_lock);
 	commit_transaction->t_state = T_LOCKED;
 
+	trace_jbd_commit_locking(journal, commit_transaction);
 	spin_lock(&commit_transaction->t_handle_lock);
 	while (commit_transaction->t_updates) {
 		DEFINE_WAIT(wait);
@@ -392,6 +396,7 @@
 	 */
 	journal_switch_revoke_table(journal);
 
+	trace_jbd_commit_flushing(journal, commit_transaction);
 	commit_transaction->t_state = T_FLUSH;
 	journal->j_committing_transaction = commit_transaction;
 	journal->j_running_transaction = NULL;
@@ -446,14 +451,9 @@
 		}
 		if (buffer_jbd(bh) && bh2jh(bh) == jh &&
 		    jh->b_transaction == commit_transaction &&
-		    jh->b_jlist == BJ_Locked) {
+		    jh->b_jlist == BJ_Locked)
 			__journal_unfile_buffer(jh);
-			jbd_unlock_bh_state(bh);
-			journal_remove_journal_head(bh);
-			put_bh(bh);
-		} else {
-			jbd_unlock_bh_state(bh);
-		}
+		jbd_unlock_bh_state(bh);
 		release_data_buffer(bh);
 		cond_resched_lock(&journal->j_list_lock);
 	}
@@ -493,6 +493,7 @@
 	commit_transaction->t_state = T_COMMIT;
 	spin_unlock(&journal->j_state_lock);
 
+	trace_jbd_commit_logging(journal, commit_transaction);
 	J_ASSERT(commit_transaction->t_nr_buffers <=
 		 commit_transaction->t_outstanding_credits);
 
@@ -797,10 +798,16 @@
 	while (commit_transaction->t_forget) {
 		transaction_t *cp_transaction;
 		struct buffer_head *bh;
+		int try_to_free = 0;
 
 		jh = commit_transaction->t_forget;
 		spin_unlock(&journal->j_list_lock);
 		bh = jh2bh(jh);
+		/*
+		 * Get a reference so that bh cannot be freed before we are
+		 * done with it.
+		 */
+		get_bh(bh);
 		jbd_lock_bh_state(bh);
 		J_ASSERT_JH(jh,	jh->b_transaction == commit_transaction ||
 			jh->b_transaction == journal->j_running_transaction);
@@ -858,28 +865,27 @@
 			__journal_insert_checkpoint(jh, commit_transaction);
 			if (is_journal_aborted(journal))
 				clear_buffer_jbddirty(bh);
-			JBUFFER_TRACE(jh, "refile for checkpoint writeback");
-			__journal_refile_buffer(jh);
-			jbd_unlock_bh_state(bh);
 		} else {
 			J_ASSERT_BH(bh, !buffer_dirty(bh));
-			/* The buffer on BJ_Forget list and not jbddirty means
+			/*
+			 * The buffer on BJ_Forget list and not jbddirty means
 			 * it has been freed by this transaction and hence it
 			 * could not have been reallocated until this
 			 * transaction has committed. *BUT* it could be
 			 * reallocated once we have written all the data to
 			 * disk and before we process the buffer on BJ_Forget
-			 * list. */
-			JBUFFER_TRACE(jh, "refile or unfile freed buffer");
-			__journal_refile_buffer(jh);
-			if (!jh->b_transaction) {
-				jbd_unlock_bh_state(bh);
-				 /* needs a brelse */
-				journal_remove_journal_head(bh);
-				release_buffer_page(bh);
-			} else
-				jbd_unlock_bh_state(bh);
+			 * list.
+			 */
+			if (!jh->b_next_transaction)
+				try_to_free = 1;
 		}
+		JBUFFER_TRACE(jh, "refile or unfile freed buffer");
+		__journal_refile_buffer(jh);
+		jbd_unlock_bh_state(bh);
+		if (try_to_free)
+			release_buffer_page(bh);
+		else
+			__brelse(bh);
 		cond_resched_lock(&journal->j_list_lock);
 	}
 	spin_unlock(&journal->j_list_lock);
@@ -946,6 +952,7 @@
 	}
 	spin_unlock(&journal->j_list_lock);
 
+	trace_jbd_end_commit(journal, commit_transaction);
 	jbd_debug(1, "JBD: commit %d complete, head %d\n",
 		  journal->j_commit_sequence, journal->j_tail_sequence);
 
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index e2d4285..9fe061f 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -38,6 +38,9 @@
 #include <linux/debugfs.h>
 #include <linux/ratelimit.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/jbd.h>
+
 #include <asm/uaccess.h>
 #include <asm/page.h>
 
@@ -1065,6 +1068,7 @@
 	} else
 		write_dirty_buffer(bh, WRITE);
 
+	trace_jbd_update_superblock_end(journal, wait);
 out:
 	/* If we have just flushed the log (by marking s_start==0), then
 	 * any future commit will have to be careful to update the
@@ -1799,10 +1803,9 @@
  * When a buffer has its BH_JBD bit set it is immune from being released by
  * core kernel code, mainly via ->b_count.
  *
- * A journal_head may be detached from its buffer_head when the journal_head's
- * b_transaction, b_cp_transaction and b_next_transaction pointers are NULL.
- * Various places in JBD call journal_remove_journal_head() to indicate that the
- * journal_head can be dropped if needed.
+ * A journal_head is detached from its buffer_head when the journal_head's
+ * b_jcount reaches zero. Running transaction (b_transaction) and checkpoint
+ * transaction (b_cp_transaction) hold their references to b_jcount.
  *
  * Various places in the kernel want to attach a journal_head to a buffer_head
  * _before_ attaching the journal_head to a transaction.  To protect the
@@ -1815,17 +1818,16 @@
  *	(Attach a journal_head if needed.  Increments b_jcount)
  *	struct journal_head *jh = journal_add_journal_head(bh);
  *	...
- *	jh->b_transaction = xxx;
- *	journal_put_journal_head(jh);
- *
- * Now, the journal_head's b_jcount is zero, but it is safe from being released
- * because it has a non-zero b_transaction.
+ *      (Get another reference for transaction)
+ *      journal_grab_journal_head(bh);
+ *      jh->b_transaction = xxx;
+ *      (Put original reference)
+ *      journal_put_journal_head(jh);
  */
 
 /*
  * Give a buffer_head a journal_head.
  *
- * Doesn't need the journal lock.
  * May sleep.
  */
 struct journal_head *journal_add_journal_head(struct buffer_head *bh)
@@ -1889,61 +1891,29 @@
 	struct journal_head *jh = bh2jh(bh);
 
 	J_ASSERT_JH(jh, jh->b_jcount >= 0);
-
-	get_bh(bh);
-	if (jh->b_jcount == 0) {
-		if (jh->b_transaction == NULL &&
-				jh->b_next_transaction == NULL &&
-				jh->b_cp_transaction == NULL) {
-			J_ASSERT_JH(jh, jh->b_jlist == BJ_None);
-			J_ASSERT_BH(bh, buffer_jbd(bh));
-			J_ASSERT_BH(bh, jh2bh(jh) == bh);
-			BUFFER_TRACE(bh, "remove journal_head");
-			if (jh->b_frozen_data) {
-				printk(KERN_WARNING "%s: freeing "
-						"b_frozen_data\n",
-						__func__);
-				jbd_free(jh->b_frozen_data, bh->b_size);
-			}
-			if (jh->b_committed_data) {
-				printk(KERN_WARNING "%s: freeing "
-						"b_committed_data\n",
-						__func__);
-				jbd_free(jh->b_committed_data, bh->b_size);
-			}
-			bh->b_private = NULL;
-			jh->b_bh = NULL;	/* debug, really */
-			clear_buffer_jbd(bh);
-			__brelse(bh);
-			journal_free_journal_head(jh);
-		} else {
-			BUFFER_TRACE(bh, "journal_head was locked");
-		}
+	J_ASSERT_JH(jh, jh->b_transaction == NULL);
+	J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+	J_ASSERT_JH(jh, jh->b_cp_transaction == NULL);
+	J_ASSERT_JH(jh, jh->b_jlist == BJ_None);
+	J_ASSERT_BH(bh, buffer_jbd(bh));
+	J_ASSERT_BH(bh, jh2bh(jh) == bh);
+	BUFFER_TRACE(bh, "remove journal_head");
+	if (jh->b_frozen_data) {
+		printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__);
+		jbd_free(jh->b_frozen_data, bh->b_size);
 	}
+	if (jh->b_committed_data) {
+		printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__);
+		jbd_free(jh->b_committed_data, bh->b_size);
+	}
+	bh->b_private = NULL;
+	jh->b_bh = NULL;	/* debug, really */
+	clear_buffer_jbd(bh);
+	journal_free_journal_head(jh);
 }
 
 /*
- * journal_remove_journal_head(): if the buffer isn't attached to a transaction
- * and has a zero b_jcount then remove and release its journal_head.   If we did
- * see that the buffer is not used by any transaction we also "logically"
- * decrement ->b_count.
- *
- * We in fact take an additional increment on ->b_count as a convenience,
- * because the caller usually wants to do additional things with the bh
- * after calling here.
- * The caller of journal_remove_journal_head() *must* run __brelse(bh) at some
- * time.  Once the caller has run __brelse(), the buffer is eligible for
- * reaping by try_to_free_buffers().
- */
-void journal_remove_journal_head(struct buffer_head *bh)
-{
-	jbd_lock_bh_journal_head(bh);
-	__journal_remove_journal_head(bh);
-	jbd_unlock_bh_journal_head(bh);
-}
-
-/*
- * Drop a reference on the passed journal_head.  If it fell to zero then try to
+ * Drop a reference on the passed journal_head.  If it fell to zero then
  * release the journal_head from the buffer_head.
  */
 void journal_put_journal_head(struct journal_head *jh)
@@ -1953,11 +1923,12 @@
 	jbd_lock_bh_journal_head(bh);
 	J_ASSERT_JH(jh, jh->b_jcount > 0);
 	--jh->b_jcount;
-	if (!jh->b_jcount && !jh->b_transaction) {
+	if (!jh->b_jcount) {
 		__journal_remove_journal_head(bh);
+		jbd_unlock_bh_journal_head(bh);
 		__brelse(bh);
-	}
-	jbd_unlock_bh_journal_head(bh);
+	} else
+		jbd_unlock_bh_journal_head(bh);
 }
 
 /*
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index f7ee81a..7e59c6e 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -26,6 +26,7 @@
 #include <linux/mm.h>
 #include <linux/highmem.h>
 #include <linux/hrtimer.h>
+#include <linux/backing-dev.h>
 
 static void __journal_temp_unlink_buffer(struct journal_head *jh);
 
@@ -99,11 +100,10 @@
 
 alloc_transaction:
 	if (!journal->j_running_transaction) {
-		new_transaction = kzalloc(sizeof(*new_transaction),
-						GFP_NOFS|__GFP_NOFAIL);
+		new_transaction = kzalloc(sizeof(*new_transaction), GFP_NOFS);
 		if (!new_transaction) {
-			ret = -ENOMEM;
-			goto out;
+			congestion_wait(BLK_RW_ASYNC, HZ/50);
+			goto alloc_transaction;
 		}
 	}
 
@@ -696,7 +696,6 @@
 	if (!jh->b_transaction) {
 		JBUFFER_TRACE(jh, "no transaction");
 		J_ASSERT_JH(jh, !jh->b_next_transaction);
-		jh->b_transaction = transaction;
 		JBUFFER_TRACE(jh, "file as BJ_Reserved");
 		spin_lock(&journal->j_list_lock);
 		__journal_file_buffer(jh, transaction, BJ_Reserved);
@@ -818,7 +817,6 @@
 		 * committed and so it's safe to clear the dirty bit.
 		 */
 		clear_buffer_dirty(jh2bh(jh));
-		jh->b_transaction = transaction;
 
 		/* first access by this transaction */
 		jh->b_modified = 0;
@@ -844,8 +842,8 @@
 	 */
 	JBUFFER_TRACE(jh, "cancelling revoke");
 	journal_cancel_revoke(handle, jh);
-	journal_put_journal_head(jh);
 out:
+	journal_put_journal_head(jh);
 	return err;
 }
 
@@ -1069,8 +1067,9 @@
 				ret = -EIO;
 				goto no_journal;
 			}
-
-			if (jh->b_transaction != NULL) {
+			/* We might have slept so buffer could be refiled now */
+			if (jh->b_transaction != NULL &&
+			    jh->b_transaction != handle->h_transaction) {
 				JBUFFER_TRACE(jh, "unfile from commit");
 				__journal_temp_unlink_buffer(jh);
 				/* It still points to the committing
@@ -1091,8 +1090,6 @@
 		if (jh->b_jlist != BJ_SyncData && jh->b_jlist != BJ_Locked) {
 			JBUFFER_TRACE(jh, "not on correct data list: unfile");
 			J_ASSERT_JH(jh, jh->b_jlist != BJ_Shadow);
-			__journal_temp_unlink_buffer(jh);
-			jh->b_transaction = handle->h_transaction;
 			JBUFFER_TRACE(jh, "file as data");
 			__journal_file_buffer(jh, handle->h_transaction,
 						BJ_SyncData);
@@ -1300,8 +1297,6 @@
 			__journal_file_buffer(jh, transaction, BJ_Forget);
 		} else {
 			__journal_unfile_buffer(jh);
-			journal_remove_journal_head(bh);
-			__brelse(bh);
 			if (!buffer_jbd(bh)) {
 				spin_unlock(&journal->j_list_lock);
 				jbd_unlock_bh_state(bh);
@@ -1622,19 +1617,32 @@
 		mark_buffer_dirty(bh);	/* Expose it to the VM */
 }
 
+/*
+ * Remove buffer from all transactions.
+ *
+ * Called with bh_state lock and j_list_lock
+ *
+ * jh and bh may be already freed when this function returns.
+ */
 void __journal_unfile_buffer(struct journal_head *jh)
 {
 	__journal_temp_unlink_buffer(jh);
 	jh->b_transaction = NULL;
+	journal_put_journal_head(jh);
 }
 
 void journal_unfile_buffer(journal_t *journal, struct journal_head *jh)
 {
-	jbd_lock_bh_state(jh2bh(jh));
+	struct buffer_head *bh = jh2bh(jh);
+
+	/* Get reference so that buffer cannot be freed before we unlock it */
+	get_bh(bh);
+	jbd_lock_bh_state(bh);
 	spin_lock(&journal->j_list_lock);
 	__journal_unfile_buffer(jh);
 	spin_unlock(&journal->j_list_lock);
-	jbd_unlock_bh_state(jh2bh(jh));
+	jbd_unlock_bh_state(bh);
+	__brelse(bh);
 }
 
 /*
@@ -1661,16 +1669,12 @@
 			/* A written-back ordered data buffer */
 			JBUFFER_TRACE(jh, "release data");
 			__journal_unfile_buffer(jh);
-			journal_remove_journal_head(bh);
-			__brelse(bh);
 		}
 	} else if (jh->b_cp_transaction != NULL && jh->b_transaction == NULL) {
 		/* written-back checkpointed metadata buffer */
 		if (jh->b_jlist == BJ_None) {
 			JBUFFER_TRACE(jh, "remove from checkpoint list");
 			__journal_remove_checkpoint(jh);
-			journal_remove_journal_head(bh);
-			__brelse(bh);
 		}
 	}
 	spin_unlock(&journal->j_list_lock);
@@ -1733,7 +1737,7 @@
 		/*
 		 * We take our own ref against the journal_head here to avoid
 		 * having to add tons of locking around each instance of
-		 * journal_remove_journal_head() and journal_put_journal_head().
+		 * journal_put_journal_head().
 		 */
 		jh = journal_grab_journal_head(bh);
 		if (!jh)
@@ -1770,10 +1774,9 @@
 	int may_free = 1;
 	struct buffer_head *bh = jh2bh(jh);
 
-	__journal_unfile_buffer(jh);
-
 	if (jh->b_cp_transaction) {
 		JBUFFER_TRACE(jh, "on running+cp transaction");
+		__journal_temp_unlink_buffer(jh);
 		/*
 		 * We don't want to write the buffer anymore, clear the
 		 * bit so that we don't confuse checks in
@@ -1784,8 +1787,7 @@
 		may_free = 0;
 	} else {
 		JBUFFER_TRACE(jh, "on running transaction");
-		journal_remove_journal_head(bh);
-		__brelse(bh);
+		__journal_unfile_buffer(jh);
 	}
 	return may_free;
 }
@@ -2070,6 +2072,8 @@
 
 	if (jh->b_transaction)
 		__journal_temp_unlink_buffer(jh);
+	else
+		journal_grab_journal_head(bh);
 	jh->b_transaction = transaction;
 
 	switch (jlist) {
@@ -2127,9 +2131,10 @@
  * already started to be used by a subsequent transaction, refile the
  * buffer on that transaction's metadata list.
  *
- * Called under journal->j_list_lock
- *
+ * Called under j_list_lock
  * Called under jbd_lock_bh_state(jh2bh(jh))
+ *
+ * jh and bh may be already free when this function returns
  */
 void __journal_refile_buffer(struct journal_head *jh)
 {
@@ -2153,6 +2158,11 @@
 
 	was_dirty = test_clear_buffer_jbddirty(bh);
 	__journal_temp_unlink_buffer(jh);
+	/*
+	 * We set b_transaction here because b_next_transaction will inherit
+	 * our jh reference and thus __journal_file_buffer() must not take a
+	 * new one.
+	 */
 	jh->b_transaction = jh->b_next_transaction;
 	jh->b_next_transaction = NULL;
 	if (buffer_freed(bh))
@@ -2169,30 +2179,21 @@
 }
 
 /*
- * For the unlocked version of this call, also make sure that any
- * hanging journal_head is cleaned up if necessary.
+ * __journal_refile_buffer() with necessary locking added. We take our bh
+ * reference so that we can safely unlock bh.
  *
- * __journal_refile_buffer is usually called as part of a single locked
- * operation on a buffer_head, in which the caller is probably going to
- * be hooking the journal_head onto other lists.  In that case it is up
- * to the caller to remove the journal_head if necessary.  For the
- * unlocked journal_refile_buffer call, the caller isn't going to be
- * doing anything else to the buffer so we need to do the cleanup
- * ourselves to avoid a jh leak.
- *
- * *** The journal_head may be freed by this call! ***
+ * The jh and bh may be freed by this call.
  */
 void journal_refile_buffer(journal_t *journal, struct journal_head *jh)
 {
 	struct buffer_head *bh = jh2bh(jh);
 
+	/* Get reference so that buffer cannot be freed before we unlock it */
+	get_bh(bh);
 	jbd_lock_bh_state(bh);
 	spin_lock(&journal->j_list_lock);
-
 	__journal_refile_buffer(jh);
 	jbd_unlock_bh_state(bh);
-	journal_remove_journal_head(bh);
-
 	spin_unlock(&journal->j_list_lock);
 	__brelse(bh);
 }
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index eeead33..b81b35d 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -80,7 +80,7 @@
 				  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 	if (ret) {
 		jffs2_free_raw_inode(ri);
-		if (S_ISLNK(inode->i_mode & S_IFMT))
+		if (S_ISLNK(inode->i_mode))
 			 kfree(mdata);
 		return ret;
 	}
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 4496872..9cbd11a 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -3161,7 +3161,7 @@
 {
 	int rc;
 	int dbitno, word, rembits, nb, nwords, wbitno, agno;
-	s8 oldroot, *leaf;
+	s8 oldroot;
 	struct dmaptree *tp = (struct dmaptree *) & dp->tree;
 
 	/* save the current value of the root (i.e. maximum free string)
@@ -3169,9 +3169,6 @@
 	 */
 	oldroot = tp->stree[ROOT];
 
-	/* pick up a pointer to the leaves of the dmap tree */
-	leaf = tp->stree + LEAFIND;
-
 	/* determine the bit number and word within the dmap of the
 	 * starting block.
 	 */
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index f6cc0c0..af96060 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -1143,7 +1143,6 @@
 	struct jfs_log *log;
 	struct tblock *tblk;
 	struct lrd *lrd;
-	int lsn;
 	struct inode *ip;
 	struct jfs_inode_info *jfs_ip;
 	int k, n;
@@ -1310,7 +1309,7 @@
 	 */
 	lrd->type = cpu_to_le16(LOG_COMMIT);
 	lrd->length = 0;
-	lsn = lmLog(log, tblk, lrd, NULL);
+	lmLog(log, tblk, lrd, NULL);
 
 	lmGroupCommit(log, tblk);
 
@@ -2935,7 +2934,6 @@
 {
 	struct inode *ip;
 	struct jfs_inode_info *jfs_ip;
-	int rc;
 	tid_t tid;
 
 	do {
@@ -2961,7 +2959,7 @@
 				 */
 				TXN_UNLOCK();
 				tid = txBegin(ip->i_sb, COMMIT_INODE);
-				rc = txCommit(tid, 1, &ip, 0);
+				txCommit(tid, 1, &ip, 0);
 				txEnd(tid);
 				mutex_unlock(&jfs_ip->commit_mutex);
 
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 29b1f1a..e17545e 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -893,7 +893,7 @@
 	unchar *i_fastsymlink;
 	s64 xlen = 0;
 	int bmask = 0, xsize;
-	s64 extent = 0, xaddr;
+	s64 xaddr;
 	struct metapage *mp;
 	struct super_block *sb;
 	struct tblock *tblk;
@@ -993,7 +993,6 @@
 			txAbort(tid, 0);
 			goto out3;
 		}
-		extent = xaddr;
 		ip->i_size = ssize - 1;
 		while (ssize) {
 			/* This is kind of silly since PATH_MAX == 4K */
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index e374050..8392cb8 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -302,7 +302,8 @@
 				/* We appear to be out of the grace period */
 				wake_up_all(&host->h_gracewait);
 			}
-			dprintk("lockd: server returns status %d\n", resp->status);
+			dprintk("lockd: server returns status %d\n",
+				ntohl(resp->status));
 			return 0;	/* Okay, call complete */
 		}
 
@@ -690,7 +691,8 @@
 		goto out;
 
 	if (resp->status != nlm_lck_denied_nolocks)
-		printk("lockd: unexpected unlock status: %d\n", resp->status);
+		printk("lockd: unexpected unlock status: %d\n",
+			ntohl(resp->status));
 	/* What to do now? I'm out of my depth... */
 	status = -ENOLCK;
 out:
@@ -843,6 +845,7 @@
 		return -ENOLCK;
 #endif
 	}
-	printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
+	printk(KERN_NOTICE "lockd: unexpected server status %d\n",
+		 ntohl(status));
 	return -ENOLCK;
 }
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index 8151554..2cde5d9 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -77,6 +77,7 @@
 config NFS_V4_1
 	bool "NFS client support for NFSv4.1 (EXPERIMENTAL)"
 	depends on NFS_FS && NFS_V4 && EXPERIMENTAL
+	select SUNRPC_BACKCHANNEL
 	select PNFS_FILE_LAYOUT
 	help
 	  This option enables support for minor version 1 of the NFSv4 protocol
diff --git a/fs/nfs/cache_lib.h b/fs/nfs/cache_lib.h
index 76f856e..7cf6caf 100644
--- a/fs/nfs/cache_lib.h
+++ b/fs/nfs/cache_lib.h
@@ -6,7 +6,7 @@
 
 #include <linux/completion.h>
 #include <linux/sunrpc/cache.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * Deferred request handling
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index d4d1954..74780f9 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -111,6 +111,7 @@
 static u32 initiate_file_draining(struct nfs_client *clp,
 				  struct cb_layoutrecallargs *args)
 {
+	struct nfs_server *server;
 	struct pnfs_layout_hdr *lo;
 	struct inode *ino;
 	bool found = false;
@@ -118,21 +119,28 @@
 	LIST_HEAD(free_me_list);
 
 	spin_lock(&clp->cl_lock);
-	list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
-		if (nfs_compare_fh(&args->cbl_fh,
-				   &NFS_I(lo->plh_inode)->fh))
-			continue;
-		ino = igrab(lo->plh_inode);
-		if (!ino)
-			continue;
-		found = true;
-		/* Without this, layout can be freed as soon
-		 * as we release cl_lock.
-		 */
-		get_layout_hdr(lo);
-		break;
+	rcu_read_lock();
+	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+		list_for_each_entry(lo, &server->layouts, plh_layouts) {
+			if (nfs_compare_fh(&args->cbl_fh,
+					   &NFS_I(lo->plh_inode)->fh))
+				continue;
+			ino = igrab(lo->plh_inode);
+			if (!ino)
+				continue;
+			found = true;
+			/* Without this, layout can be freed as soon
+			 * as we release cl_lock.
+			 */
+			get_layout_hdr(lo);
+			break;
+		}
+		if (found)
+			break;
 	}
+	rcu_read_unlock();
 	spin_unlock(&clp->cl_lock);
+
 	if (!found)
 		return NFS4ERR_NOMATCHING_LAYOUT;
 
@@ -154,6 +162,7 @@
 static u32 initiate_bulk_draining(struct nfs_client *clp,
 				  struct cb_layoutrecallargs *args)
 {
+	struct nfs_server *server;
 	struct pnfs_layout_hdr *lo;
 	struct inode *ino;
 	u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
@@ -167,18 +176,24 @@
 	};
 
 	spin_lock(&clp->cl_lock);
-	list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
 		if ((args->cbl_recall_type == RETURN_FSID) &&
-		    memcmp(&NFS_SERVER(lo->plh_inode)->fsid,
-			   &args->cbl_fsid, sizeof(struct nfs_fsid)))
+		    memcmp(&server->fsid, &args->cbl_fsid,
+			   sizeof(struct nfs_fsid)))
 			continue;
-		if (!igrab(lo->plh_inode))
-			continue;
-		get_layout_hdr(lo);
-		BUG_ON(!list_empty(&lo->plh_bulk_recall));
-		list_add(&lo->plh_bulk_recall, &recall_list);
+
+		list_for_each_entry(lo, &server->layouts, plh_layouts) {
+			if (!igrab(lo->plh_inode))
+				continue;
+			get_layout_hdr(lo);
+			BUG_ON(!list_empty(&lo->plh_bulk_recall));
+			list_add(&lo->plh_bulk_recall, &recall_list);
+		}
 	}
+	rcu_read_unlock();
 	spin_unlock(&clp->cl_lock);
+
 	list_for_each_entry_safe(lo, tmp,
 				 &recall_list, plh_bulk_recall) {
 		ino = lo->plh_inode;
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index b3dc2b8..19ea7d9 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -188,9 +188,6 @@
 	cred = rpc_lookup_machine_cred();
 	if (!IS_ERR(cred))
 		clp->cl_machine_cred = cred;
-#if defined(CONFIG_NFS_V4_1)
-	INIT_LIST_HEAD(&clp->cl_layouts);
-#endif
 	nfs_fscache_get_client_cookie(clp);
 
 	return clp;
@@ -293,6 +290,7 @@
 	nfs4_deviceid_purge_client(clp);
 
 	kfree(clp->cl_hostname);
+	kfree(clp->server_scope);
 	kfree(clp);
 
 	dprintk("<-- nfs_free_client()\n");
@@ -1062,6 +1060,7 @@
 	INIT_LIST_HEAD(&server->client_link);
 	INIT_LIST_HEAD(&server->master_link);
 	INIT_LIST_HEAD(&server->delegations);
+	INIT_LIST_HEAD(&server->layouts);
 
 	atomic_set(&server->active, 0);
 
@@ -1464,7 +1463,7 @@
 	dprintk("<-- %s %p\n", __func__, clp);
 	return clp;
 }
-EXPORT_SYMBOL(nfs4_set_ds_client);
+EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
 
 /*
  * Session has been established, and the client marked ready.
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index dd25c2a..321a66b 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -398,12 +398,11 @@
 	return err;
 }
 
-static void nfs_mark_return_delegation(struct nfs_delegation *delegation)
+static void nfs_mark_return_delegation(struct nfs_server *server,
+		struct nfs_delegation *delegation)
 {
-	struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client;
-
 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
-	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
 }
 
 /**
@@ -441,7 +440,7 @@
 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
 			continue;
 		if (delegation->type & flags)
-			nfs_mark_return_delegation(delegation);
+			nfs_mark_return_delegation(server, delegation);
 	}
 }
 
@@ -508,7 +507,7 @@
 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
 			continue;
-		nfs_mark_return_delegation(delegation);
+		nfs_mark_return_delegation(server, delegation);
 	}
 }
 
@@ -539,7 +538,8 @@
 int nfs_async_inode_return_delegation(struct inode *inode,
 				      const nfs4_stateid *stateid)
 {
-	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+	struct nfs_server *server = NFS_SERVER(inode);
+	struct nfs_client *clp = server->nfs_client;
 	struct nfs_delegation *delegation;
 
 	rcu_read_lock();
@@ -549,7 +549,7 @@
 		rcu_read_unlock();
 		return -ENOENT;
 	}
-	nfs_mark_return_delegation(delegation);
+	nfs_mark_return_delegation(server, delegation);
 	rcu_read_unlock();
 
 	nfs_delegation_run_state_manager(clp);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index b35d25b..1940f1a 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -53,7 +53,7 @@
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "internal.h"
 #include "iostat.h"
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 2a55347..ab12913 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -277,6 +277,9 @@
 extern char *nfs_path(char **p, struct dentry *dentry,
 		      char *buffer, ssize_t buflen);
 extern struct vfsmount *nfs_d_automount(struct path *path);
+#ifdef CONFIG_NFS_V4
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
+#endif
 
 /* getroot.c */
 extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
@@ -288,12 +291,22 @@
 extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh);
 #endif
 
+struct nfs_pageio_descriptor;
 /* read.c */
 extern int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
 			     const struct rpc_call_ops *call_ops);
 extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
+extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
+		struct list_head *head);
+
+extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
+extern void nfs_readdata_release(struct nfs_read_data *rdata);
 
 /* write.c */
+extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
+		struct list_head *head);
+extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio);
+extern void nfs_writedata_release(struct nfs_write_data *wdata);
 extern void nfs_commit_free(struct nfs_write_data *p);
 extern int nfs_initiate_write(struct nfs_write_data *data,
 			      struct rpc_clnt *clnt,
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 1f063ba..8102391 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -119,7 +119,7 @@
 }
 
 #ifdef CONFIG_NFS_V4
-static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
 {
 	struct gss_api_mech *mech;
 	struct xdr_netobj oid;
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b788f2e..1909ee8 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -48,6 +48,7 @@
 	NFS4CLNT_SESSION_RESET,
 	NFS4CLNT_RECALL_SLOT,
 	NFS4CLNT_LEASE_CONFIRM,
+	NFS4CLNT_SERVER_SCOPE_MISMATCH,
 };
 
 enum nfs4_session_state {
@@ -66,6 +67,8 @@
 			int cache_reply);
 	int	(*validate_stateid)(struct nfs_delegation *,
 			const nfs4_stateid *);
+	int	(*find_root_sec)(struct nfs_server *, struct nfs_fh *,
+			struct nfs_fsinfo *);
 	const struct nfs4_state_recovery_ops *reboot_recovery_ops;
 	const struct nfs4_state_recovery_ops *nograce_recovery_ops;
 	const struct nfs4_state_maintenance_ops *state_renewal_ops;
@@ -349,6 +352,8 @@
 extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
 extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
 extern void nfs41_handle_recall_slot(struct nfs_client *clp);
+extern void nfs41_handle_server_scope(struct nfs_client *,
+				      struct server_scope **);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
 extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
 extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t, pid_t);
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c
index f9d03ab..be93a62 100644
--- a/fs/nfs/nfs4filelayout.c
+++ b/fs/nfs/nfs4filelayout.c
@@ -334,6 +334,9 @@
 		__func__, data->inode->i_ino,
 		data->args.pgbase, (size_t)data->args.count, offset);
 
+	if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
+		return PNFS_NOT_ATTEMPTED;
+
 	/* Retrieve the correct rpc_client for the byte range */
 	j = nfs4_fl_calc_j_index(lseg, offset);
 	idx = nfs4_fl_calc_ds_index(lseg, j);
@@ -344,8 +347,7 @@
 		set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
 		return PNFS_NOT_ATTEMPTED;
 	}
-	dprintk("%s USE DS:ip %x %hu\n", __func__,
-		ntohl(ds->ds_ip_addr), ntohs(ds->ds_port));
+	dprintk("%s USE DS: %s\n", __func__, ds->ds_remotestr);
 
 	/* No multipath support. Use first DS */
 	data->ds_clp = ds->ds_clp;
@@ -374,6 +376,9 @@
 	struct nfs_fh *fh;
 	int status;
 
+	if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
+		return PNFS_NOT_ATTEMPTED;
+
 	/* Retrieve the correct rpc_client for the byte range */
 	j = nfs4_fl_calc_j_index(lseg, offset);
 	idx = nfs4_fl_calc_ds_index(lseg, j);
@@ -384,9 +389,9 @@
 		set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
 		return PNFS_NOT_ATTEMPTED;
 	}
-	dprintk("%s ino %lu sync %d req %Zu@%llu DS:%x:%hu\n", __func__,
+	dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s\n", __func__,
 		data->inode->i_ino, sync, (size_t) data->args.count, offset,
-		ntohl(ds->ds_ip_addr), ntohs(ds->ds_port));
+		ds->ds_remotestr);
 
 	data->write_done_cb = filelayout_write_done_cb;
 	data->ds_clp = ds->ds_clp;
@@ -428,6 +433,14 @@
 
 	dprintk("--> %s\n", __func__);
 
+	/* FIXME: remove this check when layout segment support is added */
+	if (lgr->range.offset != 0 ||
+	    lgr->range.length != NFS4_MAX_UINT64) {
+		dprintk("%s Only whole file layouts supported. Use MDS i/o\n",
+			__func__);
+		goto out;
+	}
+
 	if (fl->pattern_offset > lgr->range.offset) {
 		dprintk("%s pattern_offset %lld too large\n",
 				__func__, fl->pattern_offset);
@@ -449,6 +462,10 @@
 			goto out;
 	} else
 		dsaddr = container_of(d, struct nfs4_file_layout_dsaddr, id_node);
+	/* Found deviceid is being reaped */
+	if (test_bit(NFS_DEVICEID_INVALID, &dsaddr->id_node.flags))
+			goto out_put;
+
 	fl->dsaddr = dsaddr;
 
 	if (fl->first_stripe_index < 0 ||
@@ -659,7 +676,7 @@
  * return true  : coalesce page
  * return false : don't coalesce page
  */
-bool
+static bool
 filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
 		   struct nfs_page *req)
 {
@@ -670,8 +687,6 @@
 	    !nfs_generic_pg_test(pgio, prev, req))
 		return false;
 
-	if (!pgio->pg_lseg)
-		return 1;
 	p_stripe = (u64)prev->wb_index << PAGE_CACHE_SHIFT;
 	r_stripe = (u64)req->wb_index << PAGE_CACHE_SHIFT;
 	stripe_unit = FILELAYOUT_LSEG(pgio->pg_lseg)->stripe_unit;
@@ -682,6 +697,52 @@
 	return (p_stripe == r_stripe);
 }
 
+void
+filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,
+			struct nfs_page *req)
+{
+	BUG_ON(pgio->pg_lseg != NULL);
+
+	pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
+					   req->wb_context,
+					   0,
+					   NFS4_MAX_UINT64,
+					   IOMODE_READ,
+					   GFP_KERNEL);
+	/* If no lseg, fall back to read through mds */
+	if (pgio->pg_lseg == NULL)
+		nfs_pageio_reset_read_mds(pgio);
+}
+
+void
+filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,
+			 struct nfs_page *req)
+{
+	BUG_ON(pgio->pg_lseg != NULL);
+
+	pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
+					   req->wb_context,
+					   0,
+					   NFS4_MAX_UINT64,
+					   IOMODE_RW,
+					   GFP_NOFS);
+	/* If no lseg, fall back to write through mds */
+	if (pgio->pg_lseg == NULL)
+		nfs_pageio_reset_write_mds(pgio);
+}
+
+static const struct nfs_pageio_ops filelayout_pg_read_ops = {
+	.pg_init = filelayout_pg_init_read,
+	.pg_test = filelayout_pg_test,
+	.pg_doio = pnfs_generic_pg_readpages,
+};
+
+static const struct nfs_pageio_ops filelayout_pg_write_ops = {
+	.pg_init = filelayout_pg_init_write,
+	.pg_test = filelayout_pg_test,
+	.pg_doio = pnfs_generic_pg_writepages,
+};
+
 static bool filelayout_mark_pnfs_commit(struct pnfs_layout_segment *lseg)
 {
 	return !FILELAYOUT_LSEG(lseg)->commit_through_mds;
@@ -879,7 +940,8 @@
 	.owner			= THIS_MODULE,
 	.alloc_lseg		= filelayout_alloc_lseg,
 	.free_lseg		= filelayout_free_lseg,
-	.pg_test		= filelayout_pg_test,
+	.pg_read_ops		= &filelayout_pg_read_ops,
+	.pg_write_ops		= &filelayout_pg_write_ops,
 	.mark_pnfs_commit	= filelayout_mark_pnfs_commit,
 	.choose_commit_list	= filelayout_choose_commit_list,
 	.commit_pagelist	= filelayout_commit_pagelist,
@@ -902,5 +964,7 @@
 	pnfs_unregister_layoutdriver(&filelayout_type);
 }
 
+MODULE_ALIAS("nfs-layouttype4-1");
+
 module_init(nfs4filelayout_init);
 module_exit(nfs4filelayout_exit);
diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h
index cebe01e..2e42284 100644
--- a/fs/nfs/nfs4filelayout.h
+++ b/fs/nfs/nfs4filelayout.h
@@ -47,10 +47,17 @@
 };
 
 /* Individual ip address */
+struct nfs4_pnfs_ds_addr {
+	struct sockaddr_storage	da_addr;
+	size_t			da_addrlen;
+	struct list_head	da_node;  /* nfs4_pnfs_dev_hlist dev_dslist */
+	char			*da_remotestr;	/* human readable addr+port */
+};
+
 struct nfs4_pnfs_ds {
 	struct list_head	ds_node;  /* nfs4_pnfs_dev_hlist dev_dslist */
-	u32			ds_ip_addr;
-	u32			ds_port;
+	char			*ds_remotestr;	/* comma sep list of addrs */
+	struct list_head	ds_addrs;
 	struct nfs_client	*ds_clp;
 	atomic_t		ds_count;
 };
@@ -89,6 +96,12 @@
 			    generic_hdr);
 }
 
+static inline struct nfs4_deviceid_node *
+FILELAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg)
+{
+	return &FILELAYOUT_LSEG(lseg)->dsaddr->id_node;
+}
+
 extern struct nfs_fh *
 nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j);
 
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c
index 3b7bf13..ed388aa 100644
--- a/fs/nfs/nfs4filelayoutdev.c
+++ b/fs/nfs/nfs4filelayoutdev.c
@@ -56,54 +56,139 @@
 		printk("%s NULL device\n", __func__);
 		return;
 	}
-	printk("        ip_addr %x port %hu\n"
+	printk("        ds %s\n"
 		"        ref count %d\n"
 		"        client %p\n"
 		"        cl_exchange_flags %x\n",
-		ntohl(ds->ds_ip_addr), ntohs(ds->ds_port),
+		ds->ds_remotestr,
 		atomic_read(&ds->ds_count), ds->ds_clp,
 		ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0);
 }
 
-/* nfs4_ds_cache_lock is held */
+static bool
+same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
+{
+	struct sockaddr_in *a, *b;
+	struct sockaddr_in6 *a6, *b6;
+
+	if (addr1->sa_family != addr2->sa_family)
+		return false;
+
+	switch (addr1->sa_family) {
+	case AF_INET:
+		a = (struct sockaddr_in *)addr1;
+		b = (struct sockaddr_in *)addr2;
+
+		if (a->sin_addr.s_addr == b->sin_addr.s_addr &&
+		    a->sin_port == b->sin_port)
+			return true;
+		break;
+
+	case AF_INET6:
+		a6 = (struct sockaddr_in6 *)addr1;
+		b6 = (struct sockaddr_in6 *)addr2;
+
+		/* LINKLOCAL addresses must have matching scope_id */
+		if (ipv6_addr_scope(&a6->sin6_addr) ==
+		    IPV6_ADDR_SCOPE_LINKLOCAL &&
+		    a6->sin6_scope_id != b6->sin6_scope_id)
+			return false;
+
+		if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) &&
+		    a6->sin6_port == b6->sin6_port)
+			return true;
+		break;
+
+	default:
+		dprintk("%s: unhandled address family: %u\n",
+			__func__, addr1->sa_family);
+		return false;
+	}
+
+	return false;
+}
+
+/*
+ * Lookup DS by addresses.  The first matching address returns true.
+ * nfs4_ds_cache_lock is held
+ */
 static struct nfs4_pnfs_ds *
-_data_server_lookup_locked(u32 ip_addr, u32 port)
+_data_server_lookup_locked(struct list_head *dsaddrs)
 {
 	struct nfs4_pnfs_ds *ds;
+	struct nfs4_pnfs_ds_addr *da1, *da2;
 
-	dprintk("_data_server_lookup: ip_addr=%x port=%hu\n",
-			ntohl(ip_addr), ntohs(port));
-
-	list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) {
-		if (ds->ds_ip_addr == ip_addr &&
-		    ds->ds_port == port) {
-			return ds;
+	list_for_each_entry(da1, dsaddrs, da_node) {
+		list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) {
+			list_for_each_entry(da2, &ds->ds_addrs, da_node) {
+				if (same_sockaddr(
+					(struct sockaddr *)&da1->da_addr,
+					(struct sockaddr *)&da2->da_addr))
+					return ds;
+			}
 		}
 	}
 	return NULL;
 }
 
 /*
+ * Compare two lists of addresses.
+ */
+static bool
+_data_server_match_all_addrs_locked(struct list_head *dsaddrs1,
+				    struct list_head *dsaddrs2)
+{
+	struct nfs4_pnfs_ds_addr *da1, *da2;
+	size_t count1 = 0,
+	       count2 = 0;
+
+	list_for_each_entry(da1, dsaddrs1, da_node)
+		count1++;
+
+	list_for_each_entry(da2, dsaddrs2, da_node) {
+		bool found = false;
+		count2++;
+		list_for_each_entry(da1, dsaddrs1, da_node) {
+			if (same_sockaddr((struct sockaddr *)&da1->da_addr,
+				(struct sockaddr *)&da2->da_addr)) {
+				found = true;
+				break;
+			}
+		}
+		if (!found)
+			return false;
+	}
+
+	return (count1 == count2);
+}
+
+/*
  * Create an rpc connection to the nfs4_pnfs_ds data server
- * Currently only support IPv4
+ * Currently only supports IPv4 and IPv6 addresses
  */
 static int
 nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
 {
-	struct nfs_client *clp;
-	struct sockaddr_in sin;
+	struct nfs_client *clp = ERR_PTR(-EIO);
+	struct nfs4_pnfs_ds_addr *da;
 	int status = 0;
 
-	dprintk("--> %s ip:port %x:%hu au_flavor %d\n", __func__,
-		ntohl(ds->ds_ip_addr), ntohs(ds->ds_port),
+	dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr,
 		mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor);
 
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = ds->ds_ip_addr;
-	sin.sin_port = ds->ds_port;
+	BUG_ON(list_empty(&ds->ds_addrs));
 
-	clp = nfs4_set_ds_client(mds_srv->nfs_client, (struct sockaddr *)&sin,
-				 sizeof(sin), IPPROTO_TCP);
+	list_for_each_entry(da, &ds->ds_addrs, da_node) {
+		dprintk("%s: DS %s: trying address %s\n",
+			__func__, ds->ds_remotestr, da->da_remotestr);
+
+		clp = nfs4_set_ds_client(mds_srv->nfs_client,
+				 (struct sockaddr *)&da->da_addr,
+				 da->da_addrlen, IPPROTO_TCP);
+		if (!IS_ERR(clp))
+			break;
+	}
+
 	if (IS_ERR(clp)) {
 		status = PTR_ERR(clp);
 		goto out;
@@ -115,8 +200,8 @@
 			goto out_put;
 		}
 		ds->ds_clp = clp;
-		dprintk("%s [existing] ip=%x, port=%hu\n", __func__,
-			ntohl(ds->ds_ip_addr), ntohs(ds->ds_port));
+		dprintk("%s [existing] server=%s\n", __func__,
+			ds->ds_remotestr);
 		goto out;
 	}
 
@@ -135,8 +220,7 @@
 		goto out_put;
 
 	ds->ds_clp = clp;
-	dprintk("%s [new] ip=%x, port=%hu\n", __func__, ntohl(ds->ds_ip_addr),
-		ntohs(ds->ds_port));
+	dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
 out:
 	return status;
 out_put:
@@ -147,12 +231,25 @@
 static void
 destroy_ds(struct nfs4_pnfs_ds *ds)
 {
+	struct nfs4_pnfs_ds_addr *da;
+
 	dprintk("--> %s\n", __func__);
 	ifdebug(FACILITY)
 		print_ds(ds);
 
 	if (ds->ds_clp)
 		nfs_put_client(ds->ds_clp);
+
+	while (!list_empty(&ds->ds_addrs)) {
+		da = list_first_entry(&ds->ds_addrs,
+				      struct nfs4_pnfs_ds_addr,
+				      da_node);
+		list_del_init(&da->da_node);
+		kfree(da->da_remotestr);
+		kfree(da);
+	}
+
+	kfree(ds->ds_remotestr);
 	kfree(ds);
 }
 
@@ -179,31 +276,96 @@
 	kfree(dsaddr);
 }
 
-static struct nfs4_pnfs_ds *
-nfs4_pnfs_ds_add(struct inode *inode, u32 ip_addr, u32 port, gfp_t gfp_flags)
+/*
+ * Create a string with a human readable address and port to avoid
+ * complicated setup around many dprinks.
+ */
+static char *
+nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags)
 {
-	struct nfs4_pnfs_ds *tmp_ds, *ds;
+	struct nfs4_pnfs_ds_addr *da;
+	char *remotestr;
+	size_t len;
+	char *p;
 
-	ds = kzalloc(sizeof(*tmp_ds), gfp_flags);
+	len = 3;        /* '{', '}' and eol */
+	list_for_each_entry(da, dsaddrs, da_node) {
+		len += strlen(da->da_remotestr) + 1;    /* string plus comma */
+	}
+
+	remotestr = kzalloc(len, gfp_flags);
+	if (!remotestr)
+		return NULL;
+
+	p = remotestr;
+	*(p++) = '{';
+	len--;
+	list_for_each_entry(da, dsaddrs, da_node) {
+		size_t ll = strlen(da->da_remotestr);
+
+		if (ll > len)
+			goto out_err;
+
+		memcpy(p, da->da_remotestr, ll);
+		p += ll;
+		len -= ll;
+
+		if (len < 1)
+			goto out_err;
+		(*p++) = ',';
+		len--;
+	}
+	if (len < 2)
+		goto out_err;
+	*(p++) = '}';
+	*p = '\0';
+	return remotestr;
+out_err:
+	kfree(remotestr);
+	return NULL;
+}
+
+static struct nfs4_pnfs_ds *
+nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags)
+{
+	struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
+	char *remotestr;
+
+	if (list_empty(dsaddrs)) {
+		dprintk("%s: no addresses defined\n", __func__);
+		goto out;
+	}
+
+	ds = kzalloc(sizeof(*ds), gfp_flags);
 	if (!ds)
 		goto out;
 
+	/* this is only used for debugging, so it's ok if its NULL */
+	remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
+
 	spin_lock(&nfs4_ds_cache_lock);
-	tmp_ds = _data_server_lookup_locked(ip_addr, port);
+	tmp_ds = _data_server_lookup_locked(dsaddrs);
 	if (tmp_ds == NULL) {
-		ds->ds_ip_addr = ip_addr;
-		ds->ds_port = port;
+		INIT_LIST_HEAD(&ds->ds_addrs);
+		list_splice_init(dsaddrs, &ds->ds_addrs);
+		ds->ds_remotestr = remotestr;
 		atomic_set(&ds->ds_count, 1);
 		INIT_LIST_HEAD(&ds->ds_node);
 		ds->ds_clp = NULL;
 		list_add(&ds->ds_node, &nfs4_data_server_cache);
-		dprintk("%s add new data server ip 0x%x\n", __func__,
-			ds->ds_ip_addr);
+		dprintk("%s add new data server %s\n", __func__,
+			ds->ds_remotestr);
 	} else {
+		if (!_data_server_match_all_addrs_locked(&tmp_ds->ds_addrs,
+							 dsaddrs)) {
+			dprintk("%s:  multipath address mismatch: %s != %s",
+				__func__, tmp_ds->ds_remotestr, remotestr);
+		}
+		kfree(remotestr);
 		kfree(ds);
 		atomic_inc(&tmp_ds->ds_count);
-		dprintk("%s data server found ip 0x%x, inc'ed ds_count to %d\n",
-			__func__, tmp_ds->ds_ip_addr,
+		dprintk("%s data server %s found, inc'ed ds_count to %d\n",
+			__func__, tmp_ds->ds_remotestr,
 			atomic_read(&tmp_ds->ds_count));
 		ds = tmp_ds;
 	}
@@ -213,18 +375,22 @@
 }
 
 /*
- * Currently only support ipv4, and one multi-path address.
+ * Currently only supports ipv4, ipv6 and one multi-path address.
  */
-static struct nfs4_pnfs_ds *
-decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode, gfp_t gfp_flags)
+static struct nfs4_pnfs_ds_addr *
+decode_ds_addr(struct xdr_stream *streamp, gfp_t gfp_flags)
 {
-	struct nfs4_pnfs_ds *ds = NULL;
-	char *buf;
-	const char *ipend, *pstr;
-	u32 ip_addr, port;
-	int nlen, rlen, i;
+	struct nfs4_pnfs_ds_addr *da = NULL;
+	char *buf, *portstr;
+	u32 port;
+	int nlen, rlen;
 	int tmp[2];
 	__be32 *p;
+	char *netid, *match_netid;
+	size_t len, match_netid_len;
+	char *startsep = "";
+	char *endsep = "";
+
 
 	/* r_netid */
 	p = xdr_inline_decode(streamp, 4);
@@ -236,64 +402,123 @@
 	if (unlikely(!p))
 		goto out_err;
 
-	/* Check that netid is "tcp" */
-	if (nlen != 3 ||  memcmp((char *)p, "tcp", 3)) {
-		dprintk("%s: ERROR: non ipv4 TCP r_netid\n", __func__);
+	netid = kmalloc(nlen+1, gfp_flags);
+	if (unlikely(!netid))
 		goto out_err;
-	}
 
-	/* r_addr */
+	netid[nlen] = '\0';
+	memcpy(netid, p, nlen);
+
+	/* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
 	p = xdr_inline_decode(streamp, 4);
 	if (unlikely(!p))
-		goto out_err;
+		goto out_free_netid;
 	rlen = be32_to_cpup(p);
 
 	p = xdr_inline_decode(streamp, rlen);
 	if (unlikely(!p))
-		goto out_err;
+		goto out_free_netid;
 
-	/* ipv6 length plus port is legal */
-	if (rlen > INET6_ADDRSTRLEN + 8) {
+	/* port is ".ABC.DEF", 8 chars max */
+	if (rlen > INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8) {
 		dprintk("%s: Invalid address, length %d\n", __func__,
 			rlen);
-		goto out_err;
+		goto out_free_netid;
 	}
 	buf = kmalloc(rlen + 1, gfp_flags);
 	if (!buf) {
 		dprintk("%s: Not enough memory\n", __func__);
-		goto out_err;
+		goto out_free_netid;
 	}
 	buf[rlen] = '\0';
 	memcpy(buf, p, rlen);
 
-	/* replace the port dots with dashes for the in4_pton() delimiter*/
-	for (i = 0; i < 2; i++) {
-		char *res = strrchr(buf, '.');
-		if (!res) {
-			dprintk("%s: Failed finding expected dots in port\n",
-				__func__);
-			goto out_free;
-		}
-		*res = '-';
+	/* replace port '.' with '-' */
+	portstr = strrchr(buf, '.');
+	if (!portstr) {
+		dprintk("%s: Failed finding expected dot in port\n",
+			__func__);
+		goto out_free_buf;
+	}
+	*portstr = '-';
+
+	/* find '.' between address and port */
+	portstr = strrchr(buf, '.');
+	if (!portstr) {
+		dprintk("%s: Failed finding expected dot between address and "
+			"port\n", __func__);
+		goto out_free_buf;
+	}
+	*portstr = '\0';
+
+	da = kzalloc(sizeof(*da), gfp_flags);
+	if (unlikely(!da))
+		goto out_free_buf;
+
+	INIT_LIST_HEAD(&da->da_node);
+
+	if (!rpc_pton(buf, portstr-buf, (struct sockaddr *)&da->da_addr,
+		      sizeof(da->da_addr))) {
+		dprintk("%s: error parsing address %s\n", __func__, buf);
+		goto out_free_da;
 	}
 
-	/* Currently only support ipv4 address */
-	if (in4_pton(buf, rlen, (u8 *)&ip_addr, '-', &ipend) == 0) {
-		dprintk("%s: Only ipv4 addresses supported\n", __func__);
-		goto out_free;
-	}
-
-	/* port */
-	pstr = ipend;
-	sscanf(pstr, "-%d-%d", &tmp[0], &tmp[1]);
+	portstr++;
+	sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
 	port = htons((tmp[0] << 8) | (tmp[1]));
 
-	ds = nfs4_pnfs_ds_add(inode, ip_addr, port, gfp_flags);
-	dprintk("%s: Decoded address and port %s\n", __func__, buf);
-out_free:
+	switch (da->da_addr.ss_family) {
+	case AF_INET:
+		((struct sockaddr_in *)&da->da_addr)->sin_port = port;
+		da->da_addrlen = sizeof(struct sockaddr_in);
+		match_netid = "tcp";
+		match_netid_len = 3;
+		break;
+
+	case AF_INET6:
+		((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port;
+		da->da_addrlen = sizeof(struct sockaddr_in6);
+		match_netid = "tcp6";
+		match_netid_len = 4;
+		startsep = "[";
+		endsep = "]";
+		break;
+
+	default:
+		dprintk("%s: unsupported address family: %u\n",
+			__func__, da->da_addr.ss_family);
+		goto out_free_da;
+	}
+
+	if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) {
+		dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n",
+			__func__, netid, match_netid);
+		goto out_free_da;
+	}
+
+	/* save human readable address */
+	len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7;
+	da->da_remotestr = kzalloc(len, gfp_flags);
+
+	/* NULL is ok, only used for dprintk */
+	if (da->da_remotestr)
+		snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep,
+			 buf, endsep, ntohs(port));
+
+	dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr);
 	kfree(buf);
+	kfree(netid);
+	return da;
+
+out_free_da:
+	kfree(da);
+out_free_buf:
+	dprintk("%s: Error parsing DS addr: %s\n", __func__, buf);
+	kfree(buf);
+out_free_netid:
+	kfree(netid);
 out_err:
-	return ds;
+	return NULL;
 }
 
 /* Decode opaque device data and return the result */
@@ -310,6 +535,8 @@
 	struct xdr_stream stream;
 	struct xdr_buf buf;
 	struct page *scratch;
+	struct list_head dsaddrs;
+	struct nfs4_pnfs_ds_addr *da;
 
 	/* set up xdr stream */
 	scratch = alloc_page(gfp_flags);
@@ -386,6 +613,8 @@
 				NFS_SERVER(ino)->nfs_client,
 				&pdev->dev_id);
 
+	INIT_LIST_HEAD(&dsaddrs);
+
 	for (i = 0; i < dsaddr->ds_num; i++) {
 		int j;
 		u32 mp_count;
@@ -395,48 +624,43 @@
 			goto out_err_free_deviceid;
 
 		mp_count = be32_to_cpup(p); /* multipath count */
-		if (mp_count > 1) {
-			printk(KERN_WARNING
-			       "%s: Multipath count %d not supported, "
-			       "skipping all greater than 1\n", __func__,
-				mp_count);
-		}
 		for (j = 0; j < mp_count; j++) {
-			if (j == 0) {
-				dsaddr->ds_list[i] = decode_and_add_ds(&stream,
-					ino, gfp_flags);
-				if (dsaddr->ds_list[i] == NULL)
-					goto out_err_free_deviceid;
-			} else {
-				u32 len;
-				/* skip extra multipath */
+			da = decode_ds_addr(&stream, gfp_flags);
+			if (da)
+				list_add_tail(&da->da_node, &dsaddrs);
+		}
+		if (list_empty(&dsaddrs)) {
+			dprintk("%s: no suitable DS addresses found\n",
+				__func__);
+			goto out_err_free_deviceid;
+		}
 
-				/* read len, skip */
-				p = xdr_inline_decode(&stream, 4);
-				if (unlikely(!p))
-					goto out_err_free_deviceid;
-				len = be32_to_cpup(p);
+		dsaddr->ds_list[i] = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags);
+		if (!dsaddr->ds_list[i])
+			goto out_err_drain_dsaddrs;
 
-				p = xdr_inline_decode(&stream, len);
-				if (unlikely(!p))
-					goto out_err_free_deviceid;
-
-				/* read len, skip */
-				p = xdr_inline_decode(&stream, 4);
-				if (unlikely(!p))
-					goto out_err_free_deviceid;
-				len = be32_to_cpup(p);
-
-				p = xdr_inline_decode(&stream, len);
-				if (unlikely(!p))
-					goto out_err_free_deviceid;
-			}
+		/* If DS was already in cache, free ds addrs */
+		while (!list_empty(&dsaddrs)) {
+			da = list_first_entry(&dsaddrs,
+					      struct nfs4_pnfs_ds_addr,
+					      da_node);
+			list_del_init(&da->da_node);
+			kfree(da->da_remotestr);
+			kfree(da);
 		}
 	}
 
 	__free_page(scratch);
 	return dsaddr;
 
+out_err_drain_dsaddrs:
+	while (!list_empty(&dsaddrs)) {
+		da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr,
+				      da_node);
+		list_del_init(&da->da_node);
+		kfree(da->da_remotestr);
+		kfree(da);
+	}
 out_err_free_deviceid:
 	nfs4_fl_free_deviceid(dsaddr);
 	/* stripe_indicies was part of dsaddr */
@@ -591,13 +815,13 @@
 
 static void
 filelayout_mark_devid_negative(struct nfs4_file_layout_dsaddr *dsaddr,
-			       int err, u32 ds_addr)
+			       int err, const char *ds_remotestr)
 {
 	u32 *p = (u32 *)&dsaddr->id_node.deviceid;
 
-	printk(KERN_ERR "NFS: data server %x connection error %d."
+	printk(KERN_ERR "NFS: data server %s connection error %d."
 		" Deviceid [%x%x%x%x] marked out of use.\n",
-		ds_addr, err, p[0], p[1], p[2], p[3]);
+		ds_remotestr, err, p[0], p[1], p[2], p[3]);
 
 	spin_lock(&nfs4_ds_cache_lock);
 	dsaddr->flags |= NFS4_DEVICE_ID_NEG_ENTRY;
@@ -628,7 +852,7 @@
 		err = nfs4_ds_connect(s, ds);
 		if (err) {
 			filelayout_mark_devid_negative(dsaddr, err,
-						       ntohl(ds->ds_ip_addr));
+						       ds->ds_remotestr);
 			return NULL;
 		}
 	}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 26bece8..079614d 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -80,7 +80,10 @@
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 			    struct nfs_fattr *fattr, struct iattr *sattr,
 			    struct nfs4_state *state);
-
+#ifdef CONFIG_NFS_V4_1
+static int nfs41_test_stateid(struct nfs_server *, struct nfs4_state *);
+static int nfs41_free_stateid(struct nfs_server *, struct nfs4_state *);
+#endif
 /* Prevent leaks of NFSv4 errors into userland */
 static int nfs4_map_errors(int err)
 {
@@ -1689,6 +1692,20 @@
 	return ret;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+	int status;
+	struct nfs_server *server = NFS_SERVER(state->inode);
+
+	status = nfs41_test_stateid(server, state);
+	if (status == NFS_OK)
+		return 0;
+	nfs41_free_stateid(server, state);
+	return nfs4_open_expired(sp, state);
+}
+#endif
+
 /*
  * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
  * fields corresponding to attributes that were used to store the verifier.
@@ -2252,13 +2269,14 @@
 static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
 			      struct nfs_fsinfo *info)
 {
+	int minor_version = server->nfs_client->cl_minorversion;
 	int status = nfs4_lookup_root(server, fhandle, info);
 	if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
 		/*
 		 * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
 		 * by nfs4_map_errors() as this function exits.
 		 */
-		status = nfs4_find_root_sec(server, fhandle, info);
+		status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
 	if (status == 0)
 		status = nfs4_server_capabilities(server, fhandle);
 	if (status == 0)
@@ -4441,6 +4459,20 @@
 	return err;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
+{
+	int status;
+	struct nfs_server *server = NFS_SERVER(state->inode);
+
+	status = nfs41_test_stateid(server, state);
+	if (status == NFS_OK)
+		return 0;
+	nfs41_free_stateid(server, state);
+	return nfs4_lock_expired(state, request);
+}
+#endif
+
 static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
 	struct nfs_inode *nfsi = NFS_I(state->inode);
@@ -4779,6 +4811,16 @@
 	return -NFS4ERR_INVAL;
 }
 
+static bool
+nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
+{
+	if (a->server_scope_sz == b->server_scope_sz &&
+	    memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
+		return true;
+
+	return false;
+}
+
 /*
  * nfs4_proc_exchange_id()
  *
@@ -4821,9 +4863,31 @@
 				init_utsname()->domainname,
 				clp->cl_rpcclient->cl_auth->au_flavor);
 
+	res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
+	if (unlikely(!res.server_scope))
+		return -ENOMEM;
+
 	status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
 	if (!status)
 		status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
+
+	if (!status) {
+		if (clp->server_scope &&
+		    !nfs41_same_server_scope(clp->server_scope,
+					     res.server_scope)) {
+			dprintk("%s: server_scope mismatch detected\n",
+				__func__);
+			set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
+			kfree(clp->server_scope);
+			clp->server_scope = NULL;
+		}
+
+		if (!clp->server_scope)
+			clp->server_scope = res.server_scope;
+		else
+			kfree(res.server_scope);
+	}
+
 	dprintk("<-- %s status= %d\n", __func__, status);
 	return status;
 }
@@ -5704,7 +5768,7 @@
 {
 	struct nfs4_layoutreturn *lrp = calldata;
 	struct nfs_server *server;
-	struct pnfs_layout_hdr *lo = NFS_I(lrp->args.inode)->layout;
+	struct pnfs_layout_hdr *lo = lrp->args.layout;
 
 	dprintk("--> %s\n", __func__);
 
@@ -5733,7 +5797,7 @@
 	struct nfs4_layoutreturn *lrp = calldata;
 
 	dprintk("--> %s\n", __func__);
-	put_layout_hdr(NFS_I(lrp->args.inode)->layout);
+	put_layout_hdr(lrp->args.layout);
 	kfree(calldata);
 	dprintk("<-- %s\n", __func__);
 }
@@ -5901,6 +5965,143 @@
 	rpc_put_task(task);
 	return status;
 }
+
+static int
+_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+		    struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+	struct nfs41_secinfo_no_name_args args = {
+		.style = SECINFO_STYLE_CURRENT_FH,
+	};
+	struct nfs4_secinfo_res res = {
+		.flavors = flavors,
+	};
+	struct rpc_message msg = {
+		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
+		.rpc_argp = &args,
+		.rpc_resp = &res,
+	};
+	return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
+}
+
+static int
+nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
+			   struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
+{
+	struct nfs4_exception exception = { };
+	int err;
+	do {
+		err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+		switch (err) {
+		case 0:
+		case -NFS4ERR_WRONGSEC:
+		case -NFS4ERR_NOTSUPP:
+			break;
+		default:
+			err = nfs4_handle_exception(server, err, &exception);
+		}
+	} while (exception.retry);
+	return err;
+}
+
+static int
+nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
+		    struct nfs_fsinfo *info)
+{
+	int err;
+	struct page *page;
+	rpc_authflavor_t flavor;
+	struct nfs4_secinfo_flavors *flavors;
+
+	page = alloc_page(GFP_KERNEL);
+	if (!page) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	flavors = page_address(page);
+	err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
+
+	/*
+	 * Fall back on "guess and check" method if
+	 * the server doesn't support SECINFO_NO_NAME
+	 */
+	if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
+		err = nfs4_find_root_sec(server, fhandle, info);
+		goto out_freepage;
+	}
+	if (err)
+		goto out_freepage;
+
+	flavor = nfs_find_best_sec(flavors);
+	if (err == 0)
+		err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
+
+out_freepage:
+	put_page(page);
+	if (err == -EACCES)
+		return -EPERM;
+out:
+	return err;
+}
+static int _nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+	int status;
+	struct nfs41_test_stateid_args args = {
+		.stateid = &state->stateid,
+	};
+	struct nfs41_test_stateid_res res;
+	struct rpc_message msg = {
+		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
+		.rpc_argp = &args,
+		.rpc_resp = &res,
+	};
+	args.seq_args.sa_session = res.seq_res.sr_session = NULL;
+	status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
+	return status;
+}
+
+static int nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+	struct nfs4_exception exception = { };
+	int err;
+	do {
+		err = nfs4_handle_exception(server,
+				_nfs41_test_stateid(server, state),
+				&exception);
+	} while (exception.retry);
+	return err;
+}
+
+static int _nfs4_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+	int status;
+	struct nfs41_free_stateid_args args = {
+		.stateid = &state->stateid,
+	};
+	struct nfs41_free_stateid_res res;
+	struct rpc_message msg = {
+		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
+		.rpc_argp = &args,
+		.rpc_resp = &res,
+	};
+
+	args.seq_args.sa_session = res.seq_res.sr_session = NULL;
+	status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
+	return status;
+}
+
+static int nfs41_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+	struct nfs4_exception exception = { };
+	int err;
+	do {
+		err = nfs4_handle_exception(server,
+				_nfs4_free_stateid(server, state),
+				&exception);
+	} while (exception.retry);
+	return err;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@ -5937,8 +6138,8 @@
 struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
 	.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
 	.state_flag_bit	= NFS_STATE_RECLAIM_NOGRACE,
-	.recover_open	= nfs4_open_expired,
-	.recover_lock	= nfs4_lock_expired,
+	.recover_open	= nfs41_open_expired,
+	.recover_lock	= nfs41_lock_expired,
 	.establish_clid = nfs41_init_clientid,
 	.get_clid_cred	= nfs4_get_exchange_id_cred,
 };
@@ -5962,6 +6163,7 @@
 	.minor_version = 0,
 	.call_sync = _nfs4_call_sync,
 	.validate_stateid = nfs4_validate_delegation_stateid,
+	.find_root_sec = nfs4_find_root_sec,
 	.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
 	.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
 	.state_renewal_ops = &nfs40_state_renewal_ops,
@@ -5972,6 +6174,7 @@
 	.minor_version = 1,
 	.call_sync = _nfs4_call_sync_session,
 	.validate_stateid = nfs41_validate_delegation_stateid,
+	.find_root_sec = nfs41_find_root_sec,
 	.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
 	.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
 	.state_renewal_ops = &nfs41_state_renewal_ops,
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 7acfe88..72ab97e 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1643,7 +1643,14 @@
 				goto out_error;
 			}
 			clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
-			set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
+
+			if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH,
+					       &clp->cl_state))
+				nfs4_state_start_reclaim_nograce(clp);
+			else
+				set_bit(NFS4CLNT_RECLAIM_REBOOT,
+					&clp->cl_state);
+
 			pnfs_destroy_all_layouts(clp);
 		}
 
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index e6e8f3b..c191a9b 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -343,6 +343,14 @@
 				1 /* FIXME: opaque lrf_body always empty at the moment */)
 #define decode_layoutreturn_maxsz (op_decode_hdr_maxsz + \
 				1 + decode_stateid_maxsz)
+#define encode_secinfo_no_name_maxsz (op_encode_hdr_maxsz + 1)
+#define decode_secinfo_no_name_maxsz decode_secinfo_maxsz
+#define encode_test_stateid_maxsz	(op_encode_hdr_maxsz + 2 + \
+					 XDR_QUADLEN(NFS4_STATEID_SIZE))
+#define decode_test_stateid_maxsz	(op_decode_hdr_maxsz + 2 + 1)
+#define encode_free_stateid_maxsz	(op_encode_hdr_maxsz + 1 + \
+					 XDR_QUADLEN(NFS4_STATEID_SIZE))
+#define decode_free_stateid_maxsz	(op_decode_hdr_maxsz + 1)
 #else /* CONFIG_NFS_V4_1 */
 #define encode_sequence_maxsz	0
 #define decode_sequence_maxsz	0
@@ -772,6 +780,26 @@
 				decode_sequence_maxsz + \
 				decode_putfh_maxsz + \
 				decode_layoutreturn_maxsz)
+#define NFS4_enc_secinfo_no_name_sz	(compound_encode_hdr_maxsz + \
+					encode_sequence_maxsz + \
+					encode_putrootfh_maxsz +\
+					encode_secinfo_no_name_maxsz)
+#define NFS4_dec_secinfo_no_name_sz	(compound_decode_hdr_maxsz + \
+					decode_sequence_maxsz + \
+					decode_putrootfh_maxsz + \
+					decode_secinfo_no_name_maxsz)
+#define NFS4_enc_test_stateid_sz	(compound_encode_hdr_maxsz + \
+					 encode_sequence_maxsz + \
+					 encode_test_stateid_maxsz)
+#define NFS4_dec_test_stateid_sz	(compound_decode_hdr_maxsz + \
+					 decode_sequence_maxsz + \
+					 decode_test_stateid_maxsz)
+#define NFS4_enc_free_stateid_sz	(compound_encode_hdr_maxsz + \
+					 encode_sequence_maxsz + \
+					 encode_free_stateid_maxsz)
+#define NFS4_dec_free_stateid_sz	(compound_decode_hdr_maxsz + \
+					 decode_sequence_maxsz + \
+					 decode_free_stateid_maxsz)
 
 const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
 				      compound_encode_hdr_maxsz +
@@ -1938,6 +1966,46 @@
 	hdr->nops++;
 	hdr->replen += decode_layoutreturn_maxsz;
 }
+
+static int
+encode_secinfo_no_name(struct xdr_stream *xdr,
+		       const struct nfs41_secinfo_no_name_args *args,
+		       struct compound_hdr *hdr)
+{
+	__be32 *p;
+	p = reserve_space(xdr, 8);
+	*p++ = cpu_to_be32(OP_SECINFO_NO_NAME);
+	*p++ = cpu_to_be32(args->style);
+	hdr->nops++;
+	hdr->replen += decode_secinfo_no_name_maxsz;
+	return 0;
+}
+
+static void encode_test_stateid(struct xdr_stream *xdr,
+				struct nfs41_test_stateid_args *args,
+				struct compound_hdr *hdr)
+{
+	__be32 *p;
+
+	p = reserve_space(xdr, 8 + NFS4_STATEID_SIZE);
+	*p++ = cpu_to_be32(OP_TEST_STATEID);
+	*p++ = cpu_to_be32(1);
+	xdr_encode_opaque_fixed(p, args->stateid->data, NFS4_STATEID_SIZE);
+	hdr->nops++;
+	hdr->replen += decode_test_stateid_maxsz;
+}
+
+static void encode_free_stateid(struct xdr_stream *xdr,
+				struct nfs41_free_stateid_args *args,
+				struct compound_hdr *hdr)
+{
+	__be32 *p;
+	p = reserve_space(xdr, 4 + NFS4_STATEID_SIZE);
+	*p++ = cpu_to_be32(OP_FREE_STATEID);
+	xdr_encode_opaque_fixed(p, args->stateid->data, NFS4_STATEID_SIZE);
+	hdr->nops++;
+	hdr->replen += decode_free_stateid_maxsz;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -2790,6 +2858,59 @@
 	encode_layoutreturn(xdr, args, &hdr);
 	encode_nops(&hdr);
 }
+
+/*
+ * Encode SECINFO_NO_NAME request
+ */
+static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req,
+					struct xdr_stream *xdr,
+					struct nfs41_secinfo_no_name_args *args)
+{
+	struct compound_hdr hdr = {
+		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
+	};
+
+	encode_compound_hdr(xdr, req, &hdr);
+	encode_sequence(xdr, &args->seq_args, &hdr);
+	encode_putrootfh(xdr, &hdr);
+	encode_secinfo_no_name(xdr, args, &hdr);
+	encode_nops(&hdr);
+	return 0;
+}
+
+/*
+ *  Encode TEST_STATEID request
+ */
+static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
+				      struct xdr_stream *xdr,
+				      struct nfs41_test_stateid_args *args)
+{
+	struct compound_hdr hdr = {
+		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
+	};
+
+	encode_compound_hdr(xdr, req, &hdr);
+	encode_sequence(xdr, &args->seq_args, &hdr);
+	encode_test_stateid(xdr, args, &hdr);
+	encode_nops(&hdr);
+}
+
+/*
+ *  Encode FREE_STATEID request
+ */
+static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
+				     struct xdr_stream *xdr,
+				     struct nfs41_free_stateid_args *args)
+{
+	struct compound_hdr hdr = {
+		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
+	};
+
+	encode_compound_hdr(xdr, req, &hdr);
+	encode_sequence(xdr, &args->seq_args, &hdr);
+	encode_free_stateid(xdr, args, &hdr);
+	encode_nops(&hdr);
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
@@ -4977,11 +5098,17 @@
 	if (unlikely(status))
 		return status;
 
-	/* Throw away server_scope */
+	/* Save server_scope */
 	status = decode_opaque_inline(xdr, &dummy, &dummy_str);
 	if (unlikely(status))
 		return status;
 
+	if (unlikely(dummy > NFS4_OPAQUE_LIMIT))
+		return -EIO;
+
+	memcpy(res->server_scope->server_scope, dummy_str, dummy);
+	res->server_scope->server_scope_sz = dummy;
+
 	/* Throw away Implementation id array */
 	status = decode_opaque_inline(xdr, &dummy, &dummy_str);
 	if (unlikely(status))
@@ -5322,6 +5449,55 @@
 	print_overflow_msg(__func__, xdr);
 	return -EIO;
 }
+
+static int decode_test_stateid(struct xdr_stream *xdr,
+			       struct nfs41_test_stateid_res *res)
+{
+	__be32 *p;
+	int status;
+	int num_res;
+
+	status = decode_op_hdr(xdr, OP_TEST_STATEID);
+	if (status)
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		goto out_overflow;
+	num_res = be32_to_cpup(p++);
+	if (num_res != 1)
+		goto out;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		goto out_overflow;
+	res->status = be32_to_cpup(p++);
+	return res->status;
+out_overflow:
+	print_overflow_msg(__func__, xdr);
+out:
+	return -EIO;
+}
+
+static int decode_free_stateid(struct xdr_stream *xdr,
+			       struct nfs41_free_stateid_res *res)
+{
+	__be32 *p;
+	int status;
+
+	status = decode_op_hdr(xdr, OP_FREE_STATEID);
+	if (status)
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		goto out_overflow;
+	res->status = be32_to_cpup(p++);
+	return res->status;
+out_overflow:
+	print_overflow_msg(__func__, xdr);
+	return -EIO;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -6461,6 +6637,72 @@
 out:
 	return status;
 }
+
+/*
+ * Decode SECINFO_NO_NAME response
+ */
+static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp,
+					struct xdr_stream *xdr,
+					struct nfs4_secinfo_res *res)
+{
+	struct compound_hdr hdr;
+	int status;
+
+	status = decode_compound_hdr(xdr, &hdr);
+	if (status)
+		goto out;
+	status = decode_sequence(xdr, &res->seq_res, rqstp);
+	if (status)
+		goto out;
+	status = decode_putrootfh(xdr);
+	if (status)
+		goto out;
+	status = decode_secinfo(xdr, res);
+out:
+	return status;
+}
+
+/*
+ * Decode TEST_STATEID response
+ */
+static int nfs4_xdr_dec_test_stateid(struct rpc_rqst *rqstp,
+				     struct xdr_stream *xdr,
+				     struct nfs41_test_stateid_res *res)
+{
+	struct compound_hdr hdr;
+	int status;
+
+	status = decode_compound_hdr(xdr, &hdr);
+	if (status)
+		goto out;
+	status = decode_sequence(xdr, &res->seq_res, rqstp);
+	if (status)
+		goto out;
+	status = decode_test_stateid(xdr, res);
+out:
+	return status;
+}
+
+/*
+ * Decode FREE_STATEID response
+ */
+static int nfs4_xdr_dec_free_stateid(struct rpc_rqst *rqstp,
+				     struct xdr_stream *xdr,
+				     struct nfs41_free_stateid_res *res)
+{
+	struct compound_hdr hdr;
+	int status;
+
+	status = decode_compound_hdr(xdr, &hdr);
+	if (status)
+		goto out;
+	status = decode_sequence(xdr, &res->seq_res, rqstp);
+	if (status)
+		goto out;
+	status = decode_free_stateid(xdr, res);
+out:
+	return status;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /**
@@ -6663,6 +6905,9 @@
 	PROC(LAYOUTGET,		enc_layoutget,		dec_layoutget),
 	PROC(LAYOUTCOMMIT,	enc_layoutcommit,	dec_layoutcommit),
 	PROC(LAYOUTRETURN,	enc_layoutreturn,	dec_layoutreturn),
+	PROC(SECINFO_NO_NAME,	enc_secinfo_no_name,	dec_secinfo_no_name),
+	PROC(TEST_STATEID,	enc_test_stateid,	dec_test_stateid),
+	PROC(FREE_STATEID,	enc_free_stateid,	dec_free_stateid),
 #endif /* CONFIG_NFS_V4_1 */
 };
 
diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c
index 8ff2ea3..9383ca7 100644
--- a/fs/nfs/objlayout/objio_osd.c
+++ b/fs/nfs/objlayout/objio_osd.c
@@ -1000,13 +1000,22 @@
 	if (!pnfs_generic_pg_test(pgio, prev, req))
 		return false;
 
-	if (pgio->pg_lseg == NULL)
-		return true;
-
 	return pgio->pg_count + req->wb_bytes <=
 			OBJIO_LSEG(pgio->pg_lseg)->max_io_size;
 }
 
+static const struct nfs_pageio_ops objio_pg_read_ops = {
+	.pg_init = pnfs_generic_pg_init_read,
+	.pg_test = objio_pg_test,
+	.pg_doio = pnfs_generic_pg_readpages,
+};
+
+static const struct nfs_pageio_ops objio_pg_write_ops = {
+	.pg_init = pnfs_generic_pg_init_write,
+	.pg_test = objio_pg_test,
+	.pg_doio = pnfs_generic_pg_writepages,
+};
+
 static struct pnfs_layoutdriver_type objlayout_type = {
 	.id = LAYOUT_OSD2_OBJECTS,
 	.name = "LAYOUT_OSD2_OBJECTS",
@@ -1020,7 +1029,8 @@
 
 	.read_pagelist           = objlayout_read_pagelist,
 	.write_pagelist          = objlayout_write_pagelist,
-	.pg_test                 = objio_pg_test,
+	.pg_read_ops             = &objio_pg_read_ops,
+	.pg_write_ops            = &objio_pg_write_ops,
 
 	.free_deviceid_node	 = objio_free_deviceid_node,
 
@@ -1055,5 +1065,7 @@
 	       __func__);
 }
 
+MODULE_ALIAS("nfs-layouttype4-2");
+
 module_init(objlayout_init);
 module_exit(objlayout_exit);
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 18449f4..b60970c 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -230,7 +230,7 @@
  */
 void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
 		     struct inode *inode,
-		     int (*doio)(struct nfs_pageio_descriptor *),
+		     const struct nfs_pageio_ops *pg_ops,
 		     size_t bsize,
 		     int io_flags)
 {
@@ -240,13 +240,12 @@
 	desc->pg_bsize = bsize;
 	desc->pg_base = 0;
 	desc->pg_moreio = 0;
+	desc->pg_recoalesce = 0;
 	desc->pg_inode = inode;
-	desc->pg_doio = doio;
+	desc->pg_ops = pg_ops;
 	desc->pg_ioflags = io_flags;
 	desc->pg_error = 0;
 	desc->pg_lseg = NULL;
-	desc->pg_test = nfs_generic_pg_test;
-	pnfs_pageio_init(desc, inode);
 }
 
 /**
@@ -276,7 +275,7 @@
 		return false;
 	if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
 		return false;
-	return pgio->pg_test(pgio, prev, req);
+	return pgio->pg_ops->pg_test(pgio, prev, req);
 }
 
 /**
@@ -297,6 +296,8 @@
 		if (!nfs_can_coalesce_requests(prev, req, desc))
 			return 0;
 	} else {
+		if (desc->pg_ops->pg_init)
+			desc->pg_ops->pg_init(desc, req);
 		desc->pg_base = req->wb_pgbase;
 	}
 	nfs_list_remove_request(req);
@@ -311,7 +312,7 @@
 static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
 {
 	if (!list_empty(&desc->pg_list)) {
-		int error = desc->pg_doio(desc);
+		int error = desc->pg_ops->pg_doio(desc);
 		if (error < 0)
 			desc->pg_error = error;
 		else
@@ -331,7 +332,7 @@
  * Returns true if the request 'req' was successfully coalesced into the
  * existing list of pages 'desc'.
  */
-int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
+static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
 			   struct nfs_page *req)
 {
 	while (!nfs_pageio_do_add_request(desc, req)) {
@@ -340,17 +341,67 @@
 		if (desc->pg_error < 0)
 			return 0;
 		desc->pg_moreio = 0;
+		if (desc->pg_recoalesce)
+			return 0;
 	}
 	return 1;
 }
 
+static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
+{
+	LIST_HEAD(head);
+
+	do {
+		list_splice_init(&desc->pg_list, &head);
+		desc->pg_bytes_written -= desc->pg_count;
+		desc->pg_count = 0;
+		desc->pg_base = 0;
+		desc->pg_recoalesce = 0;
+
+		while (!list_empty(&head)) {
+			struct nfs_page *req;
+
+			req = list_first_entry(&head, struct nfs_page, wb_list);
+			nfs_list_remove_request(req);
+			if (__nfs_pageio_add_request(desc, req))
+				continue;
+			if (desc->pg_error < 0)
+				return 0;
+			break;
+		}
+	} while (desc->pg_recoalesce);
+	return 1;
+}
+
+int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
+		struct nfs_page *req)
+{
+	int ret;
+
+	do {
+		ret = __nfs_pageio_add_request(desc, req);
+		if (ret)
+			break;
+		if (desc->pg_error < 0)
+			break;
+		ret = nfs_do_recoalesce(desc);
+	} while (ret);
+	return ret;
+}
+
 /**
  * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
  * @desc: pointer to io descriptor
  */
 void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
 {
-	nfs_pageio_doio(desc);
+	for (;;) {
+		nfs_pageio_doio(desc);
+		if (!desc->pg_recoalesce)
+			break;
+		if (!nfs_do_recoalesce(desc))
+			break;
+	}
 }
 
 /**
@@ -369,7 +420,7 @@
 	if (!list_empty(&desc->pg_list)) {
 		struct nfs_page *prev = nfs_list_entry(desc->pg_list.prev);
 		if (index != prev->wb_index + 1)
-			nfs_pageio_doio(desc);
+			nfs_pageio_complete(desc);
 	}
 }
 
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 29c0ca7..38e5508 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -28,6 +28,7 @@
  */
 
 #include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
 #include "internal.h"
 #include "pnfs.h"
 #include "iostat.h"
@@ -448,11 +449,20 @@
 void
 pnfs_destroy_all_layouts(struct nfs_client *clp)
 {
+	struct nfs_server *server;
 	struct pnfs_layout_hdr *lo;
 	LIST_HEAD(tmp_list);
 
+	nfs4_deviceid_mark_client_invalid(clp);
+	nfs4_deviceid_purge_client(clp);
+
 	spin_lock(&clp->cl_lock);
-	list_splice_init(&clp->cl_layouts, &tmp_list);
+	rcu_read_lock();
+	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+		if (!list_empty(&server->layouts))
+			list_splice_init(&server->layouts, &tmp_list);
+	}
+	rcu_read_unlock();
 	spin_unlock(&clp->cl_lock);
 
 	while (!list_empty(&tmp_list)) {
@@ -661,6 +671,7 @@
 	lrp->args.stateid = stateid;
 	lrp->args.layout_type = NFS_SERVER(ino)->pnfs_curr_ld->id;
 	lrp->args.inode = ino;
+	lrp->args.layout = lo;
 	lrp->clp = NFS_SERVER(ino)->nfs_client;
 
 	status = nfs4_proc_layoutreturn(lrp);
@@ -920,7 +931,8 @@
 	};
 	unsigned pg_offset;
 	struct nfs_inode *nfsi = NFS_I(ino);
-	struct nfs_client *clp = NFS_SERVER(ino)->nfs_client;
+	struct nfs_server *server = NFS_SERVER(ino);
+	struct nfs_client *clp = server->nfs_client;
 	struct pnfs_layout_hdr *lo;
 	struct pnfs_layout_segment *lseg = NULL;
 	bool first = false;
@@ -964,7 +976,7 @@
 		 */
 		spin_lock(&clp->cl_lock);
 		BUG_ON(!list_empty(&lo->plh_layouts));
-		list_add_tail(&lo->plh_layouts, &clp->cl_layouts);
+		list_add_tail(&lo->plh_layouts, &server->layouts);
 		spin_unlock(&clp->cl_lock);
 	}
 
@@ -973,7 +985,8 @@
 		arg.offset -= pg_offset;
 		arg.length += pg_offset;
 	}
-	arg.length = PAGE_CACHE_ALIGN(arg.length);
+	if (arg.length != NFS4_MAX_UINT64)
+		arg.length = PAGE_CACHE_ALIGN(arg.length);
 
 	lseg = send_layoutget(lo, ctx, &arg, gfp_flags);
 	if (!lseg && first) {
@@ -991,6 +1004,7 @@
 	spin_unlock(&ino->i_lock);
 	goto out;
 }
+EXPORT_SYMBOL_GPL(pnfs_update_layout);
 
 int
 pnfs_layout_process(struct nfs4_layoutget *lgp)
@@ -1048,35 +1062,71 @@
 	goto out;
 }
 
+void
+pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+	BUG_ON(pgio->pg_lseg != NULL);
+
+	pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
+					   req->wb_context,
+					   req_offset(req),
+					   req->wb_bytes,
+					   IOMODE_READ,
+					   GFP_KERNEL);
+	/* If no lseg, fall back to read through mds */
+	if (pgio->pg_lseg == NULL)
+		nfs_pageio_reset_read_mds(pgio);
+
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_read);
+
+void
+pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+	BUG_ON(pgio->pg_lseg != NULL);
+
+	pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
+					   req->wb_context,
+					   req_offset(req),
+					   req->wb_bytes,
+					   IOMODE_RW,
+					   GFP_NOFS);
+	/* If no lseg, fall back to write through mds */
+	if (pgio->pg_lseg == NULL)
+		nfs_pageio_reset_write_mds(pgio);
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write);
+
+bool
+pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode)
+{
+	struct nfs_server *server = NFS_SERVER(inode);
+	struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
+
+	if (ld == NULL)
+		return false;
+	nfs_pageio_init(pgio, inode, ld->pg_read_ops, server->rsize, 0);
+	return true;
+}
+
+bool
+pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags)
+{
+	struct nfs_server *server = NFS_SERVER(inode);
+	struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
+
+	if (ld == NULL)
+		return false;
+	nfs_pageio_init(pgio, inode, ld->pg_write_ops, server->wsize, ioflags);
+	return true;
+}
+
 bool
 pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
 		     struct nfs_page *req)
 {
-	enum pnfs_iomode access_type;
-	gfp_t gfp_flags;
-
-	/* We assume that pg_ioflags == 0 iff we're reading a page */
-	if (pgio->pg_ioflags == 0) {
-		access_type = IOMODE_READ;
-		gfp_flags = GFP_KERNEL;
-	} else {
-		access_type = IOMODE_RW;
-		gfp_flags = GFP_NOFS;
-	}
-
-	if (pgio->pg_lseg == NULL) {
-		if (pgio->pg_count != prev->wb_bytes)
-			return true;
-		/* This is first coelesce call for a series of nfs_pages */
-		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
-						   prev->wb_context,
-						   req_offset(prev),
-						   pgio->pg_count,
-						   access_type,
-						   gfp_flags);
-		if (pgio->pg_lseg == NULL)
-			return true;
-	}
+	if (pgio->pg_lseg == NULL)
+		return nfs_generic_pg_test(pgio, prev, req);
 
 	/*
 	 * Test if a nfs_page is fully contained in the pnfs_layout_range.
@@ -1120,15 +1170,30 @@
 }
 EXPORT_SYMBOL_GPL(pnfs_ld_write_done);
 
-enum pnfs_try_status
+static void
+pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
+		struct nfs_write_data *data)
+{
+	list_splice_tail_init(&data->pages, &desc->pg_list);
+	if (data->req && list_empty(&data->req->wb_list))
+		nfs_list_add_request(data->req, &desc->pg_list);
+	nfs_pageio_reset_write_mds(desc);
+	desc->pg_recoalesce = 1;
+	nfs_writedata_release(data);
+}
+
+static enum pnfs_try_status
 pnfs_try_to_write_data(struct nfs_write_data *wdata,
-			const struct rpc_call_ops *call_ops, int how)
+			const struct rpc_call_ops *call_ops,
+			struct pnfs_layout_segment *lseg,
+			int how)
 {
 	struct inode *inode = wdata->inode;
 	enum pnfs_try_status trypnfs;
 	struct nfs_server *nfss = NFS_SERVER(inode);
 
 	wdata->mds_ops = call_ops;
+	wdata->lseg = get_lseg(lseg);
 
 	dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__,
 		inode->i_ino, wdata->args.count, wdata->args.offset, how);
@@ -1144,6 +1209,44 @@
 	return trypnfs;
 }
 
+static void
+pnfs_do_multiple_writes(struct nfs_pageio_descriptor *desc, struct list_head *head, int how)
+{
+	struct nfs_write_data *data;
+	const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+	struct pnfs_layout_segment *lseg = desc->pg_lseg;
+
+	desc->pg_lseg = NULL;
+	while (!list_empty(head)) {
+		enum pnfs_try_status trypnfs;
+
+		data = list_entry(head->next, struct nfs_write_data, list);
+		list_del_init(&data->list);
+
+		trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how);
+		if (trypnfs == PNFS_NOT_ATTEMPTED)
+			pnfs_write_through_mds(desc, data);
+	}
+	put_lseg(lseg);
+}
+
+int
+pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
+{
+	LIST_HEAD(head);
+	int ret;
+
+	ret = nfs_generic_flush(desc, &head);
+	if (ret != 0) {
+		put_lseg(desc->pg_lseg);
+		desc->pg_lseg = NULL;
+		return ret;
+	}
+	pnfs_do_multiple_writes(desc, &head, desc->pg_ioflags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages);
+
 /*
  * Called by non rpc-based layout drivers
  */
@@ -1167,18 +1270,32 @@
 }
 EXPORT_SYMBOL_GPL(pnfs_ld_read_done);
 
+static void
+pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
+		struct nfs_read_data *data)
+{
+	list_splice_tail_init(&data->pages, &desc->pg_list);
+	if (data->req && list_empty(&data->req->wb_list))
+		nfs_list_add_request(data->req, &desc->pg_list);
+	nfs_pageio_reset_read_mds(desc);
+	desc->pg_recoalesce = 1;
+	nfs_readdata_release(data);
+}
+
 /*
  * Call the appropriate parallel I/O subsystem read function.
  */
-enum pnfs_try_status
+static enum pnfs_try_status
 pnfs_try_to_read_data(struct nfs_read_data *rdata,
-		       const struct rpc_call_ops *call_ops)
+		       const struct rpc_call_ops *call_ops,
+		       struct pnfs_layout_segment *lseg)
 {
 	struct inode *inode = rdata->inode;
 	struct nfs_server *nfss = NFS_SERVER(inode);
 	enum pnfs_try_status trypnfs;
 
 	rdata->mds_ops = call_ops;
+	rdata->lseg = get_lseg(lseg);
 
 	dprintk("%s: Reading ino:%lu %u@%llu\n",
 		__func__, inode->i_ino, rdata->args.count, rdata->args.offset);
@@ -1194,6 +1311,44 @@
 	return trypnfs;
 }
 
+static void
+pnfs_do_multiple_reads(struct nfs_pageio_descriptor *desc, struct list_head *head)
+{
+	struct nfs_read_data *data;
+	const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
+	struct pnfs_layout_segment *lseg = desc->pg_lseg;
+
+	desc->pg_lseg = NULL;
+	while (!list_empty(head)) {
+		enum pnfs_try_status trypnfs;
+
+		data = list_entry(head->next, struct nfs_read_data, list);
+		list_del_init(&data->list);
+
+		trypnfs = pnfs_try_to_read_data(data, call_ops, lseg);
+		if (trypnfs == PNFS_NOT_ATTEMPTED)
+			pnfs_read_through_mds(desc, data);
+	}
+	put_lseg(lseg);
+}
+
+int
+pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
+{
+	LIST_HEAD(head);
+	int ret;
+
+	ret = nfs_generic_pagein(desc, &head);
+	if (ret != 0) {
+		put_lseg(desc->pg_lseg);
+		desc->pg_lseg = NULL;
+		return ret;
+	}
+	pnfs_do_multiple_reads(desc, &head);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages);
+
 /*
  * Currently there is only one (whole file) write lseg.
  */
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 96bf4e6..078670d 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -87,7 +87,8 @@
 	void (*free_lseg) (struct pnfs_layout_segment *lseg);
 
 	/* test for nfs page cache coalescing */
-	bool (*pg_test)(struct nfs_pageio_descriptor *, struct nfs_page *, struct nfs_page *);
+	const struct nfs_pageio_ops *pg_read_ops;
+	const struct nfs_pageio_ops *pg_write_ops;
 
 	/* Returns true if layoutdriver wants to divert this request to
 	 * driver's commit routine.
@@ -148,16 +149,16 @@
 /* pnfs.c */
 void get_layout_hdr(struct pnfs_layout_hdr *lo);
 void put_lseg(struct pnfs_layout_segment *lseg);
-struct pnfs_layout_segment *
-pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx,
-		   loff_t pos, u64 count, enum pnfs_iomode access_type,
-		   gfp_t gfp_flags);
+
+bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *);
+bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int);
+
 void set_pnfs_layoutdriver(struct nfs_server *, u32 id);
 void unset_pnfs_layoutdriver(struct nfs_server *);
-enum pnfs_try_status pnfs_try_to_write_data(struct nfs_write_data *,
-					     const struct rpc_call_ops *, int);
-enum pnfs_try_status pnfs_try_to_read_data(struct nfs_read_data *,
-					    const struct rpc_call_ops *);
+void pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *, struct nfs_page *);
+int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc);
+void pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *, struct nfs_page *);
+int pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc);
 bool pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, struct nfs_page *req);
 int pnfs_layout_process(struct nfs4_layoutget *lgp);
 void pnfs_free_lseg_list(struct list_head *tmp_list);
@@ -182,6 +183,19 @@
 int _pnfs_return_layout(struct inode *);
 int pnfs_ld_write_done(struct nfs_write_data *);
 int pnfs_ld_read_done(struct nfs_read_data *);
+struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino,
+					       struct nfs_open_context *ctx,
+					       loff_t pos,
+					       u64 count,
+					       enum pnfs_iomode iomode,
+					       gfp_t gfp_flags);
+
+void nfs4_deviceid_mark_client_invalid(struct nfs_client *clp);
+
+/* nfs4_deviceid_flags */
+enum {
+	NFS_DEVICEID_INVALID = 0,       /* set when MDS clientid recalled */
+};
 
 /* pnfs_dev.c */
 struct nfs4_deviceid_node {
@@ -189,13 +203,13 @@
 	struct hlist_node		tmpnode;
 	const struct pnfs_layoutdriver_type *ld;
 	const struct nfs_client		*nfs_client;
+	unsigned long 			flags;
 	struct nfs4_deviceid		deviceid;
 	atomic_t			ref;
 };
 
 void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id);
 struct nfs4_deviceid_node *nfs4_find_get_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *);
-struct nfs4_deviceid_node *nfs4_unhash_put_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *);
 void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *);
 void nfs4_init_deviceid_node(struct nfs4_deviceid_node *,
 			     const struct pnfs_layoutdriver_type *,
@@ -293,15 +307,6 @@
 	return 0;
 }
 
-static inline void pnfs_pageio_init(struct nfs_pageio_descriptor *pgio,
-				    struct inode *inode)
-{
-	struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;
-
-	if (ld)
-		pgio->pg_test = ld->pg_test;
-}
-
 #else  /* CONFIG_NFS_V4_1 */
 
 static inline void pnfs_destroy_all_layouts(struct nfs_client *clp)
@@ -322,28 +327,6 @@
 {
 }
 
-static inline struct pnfs_layout_segment *
-pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx,
-		   loff_t pos, u64 count, enum pnfs_iomode access_type,
-		   gfp_t gfp_flags)
-{
-	return NULL;
-}
-
-static inline enum pnfs_try_status
-pnfs_try_to_read_data(struct nfs_read_data *data,
-		      const struct rpc_call_ops *call_ops)
-{
-	return PNFS_NOT_ATTEMPTED;
-}
-
-static inline enum pnfs_try_status
-pnfs_try_to_write_data(struct nfs_write_data *data,
-		       const struct rpc_call_ops *call_ops, int how)
-{
-	return PNFS_NOT_ATTEMPTED;
-}
-
 static inline int pnfs_return_layout(struct inode *ino)
 {
 	return 0;
@@ -385,9 +368,14 @@
 {
 }
 
-static inline void pnfs_pageio_init(struct nfs_pageio_descriptor *pgio,
-				    struct inode *inode)
+static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode)
 {
+	return false;
+}
+
+static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags)
+{
+	return false;
 }
 
 static inline void
diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c
index f0f8e1e..6fda522 100644
--- a/fs/nfs/pnfs_dev.c
+++ b/fs/nfs/pnfs_dev.c
@@ -100,8 +100,8 @@
 
 	rcu_read_lock();
 	d = _lookup_deviceid(ld, clp, id, hash);
-	if (d && !atomic_inc_not_zero(&d->ref))
-		d = NULL;
+	if (d != NULL)
+		atomic_inc(&d->ref);
 	rcu_read_unlock();
 	return d;
 }
@@ -115,15 +115,15 @@
 EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid);
 
 /*
- * Unhash and put deviceid
+ * Remove a deviceid from cache
  *
  * @clp nfs_client associated with deviceid
  * @id the deviceid to unhash
  *
  * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise.
  */
-struct nfs4_deviceid_node *
-nfs4_unhash_put_deviceid(const struct pnfs_layoutdriver_type *ld,
+void
+nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld,
 			 const struct nfs_client *clp, const struct nfs4_deviceid *id)
 {
 	struct nfs4_deviceid_node *d;
@@ -134,7 +134,7 @@
 	rcu_read_unlock();
 	if (!d) {
 		spin_unlock(&nfs4_deviceid_lock);
-		return NULL;
+		return;
 	}
 	hlist_del_init_rcu(&d->node);
 	spin_unlock(&nfs4_deviceid_lock);
@@ -142,28 +142,7 @@
 
 	/* balance the initial ref set in pnfs_insert_deviceid */
 	if (atomic_dec_and_test(&d->ref))
-		return d;
-
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(nfs4_unhash_put_deviceid);
-
-/*
- * Delete a deviceid from cache
- *
- * @clp struct nfs_client qualifying the deviceid
- * @id deviceid to delete
- */
-void
-nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld,
-		     const struct nfs_client *clp, const struct nfs4_deviceid *id)
-{
-	struct nfs4_deviceid_node *d;
-
-	d = nfs4_unhash_put_deviceid(ld, clp, id);
-	if (!d)
-		return;
-	d->ld->free_deviceid_node(d);
+		d->ld->free_deviceid_node(d);
 }
 EXPORT_SYMBOL_GPL(nfs4_delete_deviceid);
 
@@ -177,6 +156,7 @@
 	INIT_HLIST_NODE(&d->tmpnode);
 	d->ld = ld;
 	d->nfs_client = nfs_client;
+	d->flags = 0;
 	d->deviceid = *id;
 	atomic_set(&d->ref, 1);
 }
@@ -221,16 +201,15 @@
  *
  * @d deviceid node to put
  *
- * @ret true iff the node was deleted
+ * return true iff the node was deleted
+ * Note that since the test for d->ref == 0 is sufficient to establish
+ * that the node is no longer hashed in the global device id cache.
  */
 bool
 nfs4_put_deviceid_node(struct nfs4_deviceid_node *d)
 {
-	if (!atomic_dec_and_lock(&d->ref, &nfs4_deviceid_lock))
+	if (!atomic_dec_and_test(&d->ref))
 		return false;
-	hlist_del_init_rcu(&d->node);
-	spin_unlock(&nfs4_deviceid_lock);
-	synchronize_rcu();
 	d->ld->free_deviceid_node(d);
 	return true;
 }
@@ -275,3 +254,22 @@
 	for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++)
 		_deviceid_purge_client(clp, h);
 }
+
+/*
+ * Stop use of all deviceids associated with an nfs_client
+ */
+void
+nfs4_deviceid_mark_client_invalid(struct nfs_client *clp)
+{
+	struct nfs4_deviceid_node *d;
+	struct hlist_node *n;
+	int i;
+
+	rcu_read_lock();
+	for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){
+		hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[i], node)
+			if (d->nfs_client == clp)
+				set_bit(NFS_DEVICEID_INVALID, &d->flags);
+	}
+	rcu_read_unlock();
+}
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index a68679f..2171c04 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -30,8 +30,7 @@
 
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc);
-static int nfs_pagein_one(struct nfs_pageio_descriptor *desc);
+static const struct nfs_pageio_ops nfs_pageio_read_ops;
 static const struct rpc_call_ops nfs_read_partial_ops;
 static const struct rpc_call_ops nfs_read_full_ops;
 
@@ -68,7 +67,7 @@
 	mempool_free(p, nfs_rdata_mempool);
 }
 
-static void nfs_readdata_release(struct nfs_read_data *rdata)
+void nfs_readdata_release(struct nfs_read_data *rdata)
 {
 	put_lseg(rdata->lseg);
 	put_nfs_open_context(rdata->args.context);
@@ -113,6 +112,27 @@
 	}
 }
 
+static void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
+		struct inode *inode)
+{
+	nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops,
+			NFS_SERVER(inode)->rsize, 0);
+}
+
+void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
+{
+	pgio->pg_ops = &nfs_pageio_read_ops;
+	pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize;
+}
+EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
+
+static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
+		struct inode *inode)
+{
+	if (!pnfs_pageio_init_read(pgio, inode))
+		nfs_pageio_init_read_mds(pgio, inode);
+}
+
 int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
 		       struct page *page)
 {
@@ -131,14 +151,9 @@
 	if (len < PAGE_CACHE_SIZE)
 		zero_user_segment(page, len, PAGE_CACHE_SIZE);
 
-	nfs_pageio_init(&pgio, inode, NULL, 0, 0);
-	nfs_list_add_request(new, &pgio.pg_list);
-	pgio.pg_count = len;
-
-	if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
-		nfs_pagein_multi(&pgio);
-	else
-		nfs_pagein_one(&pgio);
+	nfs_pageio_init_read(&pgio, inode);
+	nfs_pageio_add_request(&pgio, new);
+	nfs_pageio_complete(&pgio);
 	return 0;
 }
 
@@ -202,17 +217,14 @@
 /*
  * Set up the NFS read request struct
  */
-static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
-		const struct rpc_call_ops *call_ops,
-		unsigned int count, unsigned int offset,
-		struct pnfs_layout_segment *lseg)
+static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
+		unsigned int count, unsigned int offset)
 {
 	struct inode *inode = req->wb_context->dentry->d_inode;
 
 	data->req	  = req;
 	data->inode	  = inode;
 	data->cred	  = req->wb_context->cred;
-	data->lseg	  = get_lseg(lseg);
 
 	data->args.fh     = NFS_FH(inode);
 	data->args.offset = req_offset(req) + offset;
@@ -226,14 +238,36 @@
 	data->res.count   = count;
 	data->res.eof     = 0;
 	nfs_fattr_init(&data->fattr);
+}
 
-	if (data->lseg &&
-	    (pnfs_try_to_read_data(data, call_ops) == PNFS_ATTEMPTED))
-		return 0;
+static int nfs_do_read(struct nfs_read_data *data,
+		const struct rpc_call_ops *call_ops)
+{
+	struct inode *inode = data->args.context->dentry->d_inode;
 
 	return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops);
 }
 
+static int
+nfs_do_multiple_reads(struct list_head *head,
+		const struct rpc_call_ops *call_ops)
+{
+	struct nfs_read_data *data;
+	int ret = 0;
+
+	while (!list_empty(head)) {
+		int ret2;
+
+		data = list_entry(head->next, struct nfs_read_data, list);
+		list_del_init(&data->list);
+
+		ret2 = nfs_do_read(data, call_ops);
+		if (ret == 0)
+			ret = ret2;
+	}
+	return ret;
+}
+
 static void
 nfs_async_read_error(struct list_head *head)
 {
@@ -260,20 +294,19 @@
  * won't see the new data until our attribute cache is updated.  This is more
  * or less conventional NFS client behavior.
  */
-static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc)
+static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
 {
 	struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
 	struct page *page = req->wb_page;
 	struct nfs_read_data *data;
-	size_t rsize = NFS_SERVER(desc->pg_inode)->rsize, nbytes;
+	size_t rsize = desc->pg_bsize, nbytes;
 	unsigned int offset;
 	int requests = 0;
 	int ret = 0;
-	struct pnfs_layout_segment *lseg;
-	LIST_HEAD(list);
 
 	nfs_list_remove_request(req);
 
+	offset = 0;
 	nbytes = desc->pg_count;
 	do {
 		size_t len = min(nbytes,rsize);
@@ -281,45 +314,21 @@
 		data = nfs_readdata_alloc(1);
 		if (!data)
 			goto out_bad;
-		list_add(&data->pages, &list);
+		data->pagevec[0] = page;
+		nfs_read_rpcsetup(req, data, len, offset);
+		list_add(&data->list, res);
 		requests++;
 		nbytes -= len;
+		offset += len;
 	} while(nbytes != 0);
 	atomic_set(&req->wb_complete, requests);
-
-	BUG_ON(desc->pg_lseg != NULL);
-	lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
-				  req_offset(req), desc->pg_count,
-				  IOMODE_READ, GFP_KERNEL);
 	ClearPageError(page);
-	offset = 0;
-	nbytes = desc->pg_count;
-	do {
-		int ret2;
-
-		data = list_entry(list.next, struct nfs_read_data, pages);
-		list_del_init(&data->pages);
-
-		data->pagevec[0] = page;
-
-		if (nbytes < rsize)
-			rsize = nbytes;
-		ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
-					 rsize, offset, lseg);
-		if (ret == 0)
-			ret = ret2;
-		offset += rsize;
-		nbytes -= rsize;
-	} while (nbytes != 0);
-	put_lseg(lseg);
-	desc->pg_lseg = NULL;
-
+	desc->pg_rpc_callops = &nfs_read_partial_ops;
 	return ret;
-
 out_bad:
-	while (!list_empty(&list)) {
-		data = list_entry(list.next, struct nfs_read_data, pages);
-		list_del(&data->pages);
+	while (!list_empty(res)) {
+		data = list_entry(res->next, struct nfs_read_data, list);
+		list_del(&data->list);
 		nfs_readdata_free(data);
 	}
 	SetPageError(page);
@@ -327,19 +336,19 @@
 	return -ENOMEM;
 }
 
-static int nfs_pagein_one(struct nfs_pageio_descriptor *desc)
+static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
 {
 	struct nfs_page		*req;
 	struct page		**pages;
 	struct nfs_read_data	*data;
 	struct list_head *head = &desc->pg_list;
-	struct pnfs_layout_segment *lseg = desc->pg_lseg;
-	int ret = -ENOMEM;
+	int ret = 0;
 
 	data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base,
 						     desc->pg_count));
 	if (!data) {
 		nfs_async_read_error(head);
+		ret = -ENOMEM;
 		goto out;
 	}
 
@@ -352,19 +361,37 @@
 		*pages++ = req->wb_page;
 	}
 	req = nfs_list_entry(data->pages.next);
-	if ((!lseg) && list_is_singular(&data->pages))
-		lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
-					  req_offset(req), desc->pg_count,
-					  IOMODE_READ, GFP_KERNEL);
 
-	ret = nfs_read_rpcsetup(req, data, &nfs_read_full_ops, desc->pg_count,
-				0, lseg);
+	nfs_read_rpcsetup(req, data, desc->pg_count, 0);
+	list_add(&data->list, res);
+	desc->pg_rpc_callops = &nfs_read_full_ops;
 out:
-	put_lseg(lseg);
-	desc->pg_lseg = NULL;
 	return ret;
 }
 
+int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head)
+{
+	if (desc->pg_bsize < PAGE_CACHE_SIZE)
+		return nfs_pagein_multi(desc, head);
+	return nfs_pagein_one(desc, head);
+}
+
+static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
+{
+	LIST_HEAD(head);
+	int ret;
+
+	ret = nfs_generic_pagein(desc, &head);
+	if (ret == 0)
+		ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops);
+	return ret;
+}
+
+static const struct nfs_pageio_ops nfs_pageio_read_ops = {
+	.pg_test = nfs_generic_pg_test,
+	.pg_doio = nfs_generic_pg_readpages,
+};
+
 /*
  * This is the callback from RPC telling us whether a reply was
  * received or some error occurred (timeout or socket shutdown).
@@ -635,8 +662,6 @@
 		.pgio = &pgio,
 	};
 	struct inode *inode = mapping->host;
-	struct nfs_server *server = NFS_SERVER(inode);
-	size_t rsize = server->rsize;
 	unsigned long npages;
 	int ret = -ESTALE;
 
@@ -664,10 +689,7 @@
 	if (ret == 0)
 		goto read_complete; /* all pages were read */
 
-	if (rsize < PAGE_CACHE_SIZE)
-		nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0);
-	else
-		nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0);
+	nfs_pageio_init_read(&pgio, inode);
 
 	ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
 
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index 8d6864c..b2fbbde 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -147,7 +147,7 @@
 
 	alias = d_lookup(parent, &data->args.name);
 	if (alias != NULL) {
-		int ret = 0;
+		int ret;
 		void *devname_garbage = NULL;
 
 		/*
@@ -155,14 +155,16 @@
 		 * the sillyrename information to the aliased dentry.
 		 */
 		nfs_free_dname(data);
+		ret = nfs_copy_dname(alias, data);
 		spin_lock(&alias->d_lock);
-		if (alias->d_inode != NULL &&
+		if (ret == 0 && alias->d_inode != NULL &&
 		    !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
 			devname_garbage = alias->d_fsdata;
 			alias->d_fsdata = data;
 			alias->d_flags |= DCACHE_NFSFS_RENAMED;
 			ret = 1;
-		}
+		} else
+			ret = 0;
 		spin_unlock(&alias->d_lock);
 		nfs_dec_sillycount(dir);
 		dput(alias);
@@ -171,8 +173,7 @@
 		 * point dentry is definitely not a root, so we won't need
 		 * that anymore.
 		 */
-		if (devname_garbage)
-			kfree(devname_garbage);
+		kfree(devname_garbage);
 		return ret;
 	}
 	data->dir = igrab(dir);
@@ -204,8 +205,6 @@
 	if (parent == NULL)
 		goto out_free;
 	dir = parent->d_inode;
-	if (nfs_copy_dname(dentry, data) != 0)
-		goto out_dput;
 	/* Non-exclusive lock protects against concurrent lookup() calls */
 	spin_lock(&dir->i_lock);
 	if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
@@ -366,6 +365,8 @@
 	struct nfs_renamedata *data = calldata;
 	struct inode *old_dir = data->old_dir;
 	struct inode *new_dir = data->new_dir;
+	struct dentry *old_dentry = data->old_dentry;
+	struct dentry *new_dentry = data->new_dentry;
 
 	if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) {
 		nfs_restart_rpc(task, NFS_SERVER(old_dir)->nfs_client);
@@ -373,12 +374,12 @@
 	}
 
 	if (task->tk_status != 0) {
-		nfs_cancel_async_unlink(data->old_dentry);
+		nfs_cancel_async_unlink(old_dentry);
 		return;
 	}
 
-	nfs_set_verifier(data->old_dentry, nfs_save_change_attribute(old_dir));
-	d_move(data->old_dentry, data->new_dentry);
+	d_drop(old_dentry);
+	d_drop(new_dentry);
 }
 
 /**
@@ -501,6 +502,14 @@
  * and only performs the unlink once the last reference to it is put.
  *
  * The final cleanup is done during dentry_iput.
+ *
+ * (Note: NFSv4 is stateful, and has opens, so in theory an NFSv4 server
+ * could take responsibility for keeping open files referenced.  The server
+ * would also need to ensure that opened-but-deleted files were kept over
+ * reboots.  However, we may not assume a server does so.  (RFC 5661
+ * does provide an OPEN4_RESULT_PRESERVE_UNLINKED flag that a server can
+ * use to advertise that it does this; some day we may take advantage of
+ * it.))
  */
 int
 nfs_sillyrename(struct inode *dir, struct dentry *dentry)
@@ -560,6 +569,14 @@
 	if (error)
 		goto out_dput;
 
+	/* populate unlinkdata with the right dname */
+	error = nfs_copy_dname(sdentry,
+				(struct nfs_unlinkdata *)dentry->d_fsdata);
+	if (error) {
+		nfs_cancel_async_unlink(dentry);
+		goto out_dput;
+	}
+
 	/* run the rename task, undo unlink if it fails */
 	task = nfs_async_rename(dir, dir, dentry, sdentry);
 	if (IS_ERR(task)) {
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 0857931..b39b37f 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -97,7 +97,7 @@
 	mempool_free(p, nfs_wdata_mempool);
 }
 
-static void nfs_writedata_release(struct nfs_write_data *wdata)
+void nfs_writedata_release(struct nfs_write_data *wdata)
 {
 	put_lseg(wdata->lseg);
 	put_nfs_open_context(wdata->args.context);
@@ -845,11 +845,9 @@
 /*
  * Set up the argument/result storage required for the RPC call.
  */
-static int nfs_write_rpcsetup(struct nfs_page *req,
+static void nfs_write_rpcsetup(struct nfs_page *req,
 		struct nfs_write_data *data,
-		const struct rpc_call_ops *call_ops,
 		unsigned int count, unsigned int offset,
-		struct pnfs_layout_segment *lseg,
 		int how)
 {
 	struct inode *inode = req->wb_context->dentry->d_inode;
@@ -860,7 +858,6 @@
 	data->req = req;
 	data->inode = inode = req->wb_context->dentry->d_inode;
 	data->cred = req->wb_context->cred;
-	data->lseg = get_lseg(lseg);
 
 	data->args.fh     = NFS_FH(inode);
 	data->args.offset = req_offset(req) + offset;
@@ -872,24 +869,51 @@
 	data->args.context = get_nfs_open_context(req->wb_context);
 	data->args.lock_context = req->wb_lock_context;
 	data->args.stable  = NFS_UNSTABLE;
-	if (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
-		data->args.stable = NFS_DATA_SYNC;
-		if (!nfs_need_commit(NFS_I(inode)))
-			data->args.stable = NFS_FILE_SYNC;
+	switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
+	case 0:
+		break;
+	case FLUSH_COND_STABLE:
+		if (nfs_need_commit(NFS_I(inode)))
+			break;
+	default:
+		data->args.stable = NFS_FILE_SYNC;
 	}
 
 	data->res.fattr   = &data->fattr;
 	data->res.count   = count;
 	data->res.verf    = &data->verf;
 	nfs_fattr_init(&data->fattr);
+}
 
-	if (data->lseg &&
-	    (pnfs_try_to_write_data(data, call_ops, how) == PNFS_ATTEMPTED))
-		return 0;
+static int nfs_do_write(struct nfs_write_data *data,
+		const struct rpc_call_ops *call_ops,
+		int how)
+{
+	struct inode *inode = data->args.context->dentry->d_inode;
 
 	return nfs_initiate_write(data, NFS_CLIENT(inode), call_ops, how);
 }
 
+static int nfs_do_multiple_writes(struct list_head *head,
+		const struct rpc_call_ops *call_ops,
+		int how)
+{
+	struct nfs_write_data *data;
+	int ret = 0;
+
+	while (!list_empty(head)) {
+		int ret2;
+
+		data = list_entry(head->next, struct nfs_write_data, list);
+		list_del_init(&data->list);
+		
+		ret2 = nfs_do_write(data, call_ops, how);
+		 if (ret == 0)
+			 ret = ret2;
+	}
+	return ret;
+}
+
 /* If a nfs_flush_* function fails, it should remove reqs from @head and
  * call this on each, which will prepare them to be retried on next
  * writeback using standard nfs.
@@ -907,17 +931,15 @@
  * Generate multiple small requests to write out a single
  * contiguous dirty area on one page.
  */
-static int nfs_flush_multi(struct nfs_pageio_descriptor *desc)
+static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
 {
 	struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
 	struct page *page = req->wb_page;
 	struct nfs_write_data *data;
-	size_t wsize = NFS_SERVER(desc->pg_inode)->wsize, nbytes;
+	size_t wsize = desc->pg_bsize, nbytes;
 	unsigned int offset;
 	int requests = 0;
 	int ret = 0;
-	struct pnfs_layout_segment *lseg;
-	LIST_HEAD(list);
 
 	nfs_list_remove_request(req);
 
@@ -927,6 +949,7 @@
 		desc->pg_ioflags &= ~FLUSH_COND_STABLE;
 
 
+	offset = 0;
 	nbytes = desc->pg_count;
 	do {
 		size_t len = min(nbytes, wsize);
@@ -934,45 +957,21 @@
 		data = nfs_writedata_alloc(1);
 		if (!data)
 			goto out_bad;
-		list_add(&data->pages, &list);
+		data->pagevec[0] = page;
+		nfs_write_rpcsetup(req, data, wsize, offset, desc->pg_ioflags);
+		list_add(&data->list, res);
 		requests++;
 		nbytes -= len;
+		offset += len;
 	} while (nbytes != 0);
 	atomic_set(&req->wb_complete, requests);
-
-	BUG_ON(desc->pg_lseg);
-	lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
-				  req_offset(req), desc->pg_count,
-				  IOMODE_RW, GFP_NOFS);
-	ClearPageError(page);
-	offset = 0;
-	nbytes = desc->pg_count;
-	do {
-		int ret2;
-
-		data = list_entry(list.next, struct nfs_write_data, pages);
-		list_del_init(&data->pages);
-
-		data->pagevec[0] = page;
-
-		if (nbytes < wsize)
-			wsize = nbytes;
-		ret2 = nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
-					  wsize, offset, lseg, desc->pg_ioflags);
-		if (ret == 0)
-			ret = ret2;
-		offset += wsize;
-		nbytes -= wsize;
-	} while (nbytes != 0);
-
-	put_lseg(lseg);
-	desc->pg_lseg = NULL;
+	desc->pg_rpc_callops = &nfs_write_partial_ops;
 	return ret;
 
 out_bad:
-	while (!list_empty(&list)) {
-		data = list_entry(list.next, struct nfs_write_data, pages);
-		list_del(&data->pages);
+	while (!list_empty(res)) {
+		data = list_entry(res->next, struct nfs_write_data, list);
+		list_del(&data->list);
 		nfs_writedata_free(data);
 	}
 	nfs_redirty_request(req);
@@ -987,14 +986,13 @@
  * This is the case if nfs_updatepage detects a conflicting request
  * that has been written but not committed.
  */
-static int nfs_flush_one(struct nfs_pageio_descriptor *desc)
+static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
 {
 	struct nfs_page		*req;
 	struct page		**pages;
 	struct nfs_write_data	*data;
 	struct list_head *head = &desc->pg_list;
-	struct pnfs_layout_segment *lseg = desc->pg_lseg;
-	int ret;
+	int ret = 0;
 
 	data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base,
 						      desc->pg_count));
@@ -1016,32 +1014,62 @@
 		*pages++ = req->wb_page;
 	}
 	req = nfs_list_entry(data->pages.next);
-	if ((!lseg) && list_is_singular(&data->pages))
-		lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
-					  req_offset(req), desc->pg_count,
-					  IOMODE_RW, GFP_NOFS);
 
 	if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
 	    (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit))
 		desc->pg_ioflags &= ~FLUSH_COND_STABLE;
 
 	/* Set up the argument struct */
-	ret = nfs_write_rpcsetup(req, data, &nfs_write_full_ops, desc->pg_count, 0, lseg, desc->pg_ioflags);
+	nfs_write_rpcsetup(req, data, desc->pg_count, 0, desc->pg_ioflags);
+	list_add(&data->list, res);
+	desc->pg_rpc_callops = &nfs_write_full_ops;
 out:
-	put_lseg(lseg); /* Cleans any gotten in ->pg_test */
-	desc->pg_lseg = NULL;
 	return ret;
 }
 
+int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head)
+{
+	if (desc->pg_bsize < PAGE_CACHE_SIZE)
+		return nfs_flush_multi(desc, head);
+	return nfs_flush_one(desc, head);
+}
+
+static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
+{
+	LIST_HEAD(head);
+	int ret;
+
+	ret = nfs_generic_flush(desc, &head);
+	if (ret == 0)
+		ret = nfs_do_multiple_writes(&head, desc->pg_rpc_callops,
+				desc->pg_ioflags);
+	return ret;
+}
+
+static const struct nfs_pageio_ops nfs_pageio_write_ops = {
+	.pg_test = nfs_generic_pg_test,
+	.pg_doio = nfs_generic_pg_writepages,
+};
+
+static void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
+				  struct inode *inode, int ioflags)
+{
+	nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops,
+				NFS_SERVER(inode)->wsize, ioflags);
+}
+
+void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
+{
+	pgio->pg_ops = &nfs_pageio_write_ops;
+	pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize;
+}
+EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
+
 static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
 				  struct inode *inode, int ioflags)
 {
-	size_t wsize = NFS_SERVER(inode)->wsize;
-
-	if (wsize < PAGE_CACHE_SIZE)
-		nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
-	else
-		nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags);
+	if (!pnfs_pageio_init_write(pgio, inode, ioflags))
+		nfs_pageio_init_write_mds(pgio, inode, ioflags);
 }
 
 /*
@@ -1566,8 +1594,7 @@
 		int status;
 		bool sync = true;
 
-		if (wbc->sync_mode == WB_SYNC_NONE || wbc->nonblocking ||
-		    wbc->for_background)
+		if (wbc->sync_mode == WB_SYNC_NONE)
 			sync = false;
 
 		status = pnfs_layoutcommit_inode(inode, sync);
diff --git a/fs/notify/group.c b/fs/notify/group.c
index d309f38..63fc294 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -26,7 +26,7 @@
 #include <linux/fsnotify_backend.h>
 #include "fsnotify.h"
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * Final freeing of a group
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 07ea8d3..b13c00a 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -23,7 +23,7 @@
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/fsnotify_backend.h>
 #include "fsnotify.h"
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 252ab1f..e14587d 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -92,7 +92,7 @@
 #include <linux/spinlock.h>
 #include <linux/srcu.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/fsnotify_backend.h>
 #include "fsnotify.h"
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index f39260f..ee18815 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -43,7 +43,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/fsnotify_backend.h>
 #include "fsnotify.h"
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
index e86577d..778fe6c 100644
--- a/fs/notify/vfsmount_mark.c
+++ b/fs/notify/vfsmount_mark.c
@@ -24,7 +24,7 @@
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/fsnotify_backend.h>
 #include "fsnotify.h"
diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
index 2dabf81..fe8e7e9 100644
--- a/fs/ntfs/inode.h
+++ b/fs/ntfs/inode.h
@@ -24,7 +24,7 @@
 #ifndef _LINUX_NTFS_INODE_H
 #define _LINUX_NTFS_INODE_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/fs.h>
 #include <linux/list.h>
diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c
index 3b8d397..98e5442 100644
--- a/fs/omfs/dir.c
+++ b/fs/omfs/dir.c
@@ -93,7 +93,7 @@
 
 	memset(bh->b_data, 0, sizeof(struct omfs_inode));
 
-	if (inode->i_mode & S_IFDIR) {
+	if (S_ISDIR(inode->i_mode)) {
 		memset(&bh->b_data[OMFS_DIR_START], 0xff,
 			sbi->s_sys_blocksize - OMFS_DIR_START);
 	} else
diff --git a/fs/open.c b/fs/open.c
index 739b751..f711921 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -446,74 +446,52 @@
 	return error;
 }
 
-SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode)
+static int chmod_common(struct path *path, umode_t mode)
 {
-	struct inode * inode;
-	struct dentry * dentry;
-	struct file * file;
-	int err = -EBADF;
+	struct inode *inode = path->dentry->d_inode;
 	struct iattr newattrs;
+	int error;
 
-	file = fget(fd);
-	if (!file)
-		goto out;
-
-	dentry = file->f_path.dentry;
-	inode = dentry->d_inode;
-
-	audit_inode(NULL, dentry);
-
-	err = mnt_want_write_file(file);
-	if (err)
-		goto out_putf;
+	error = mnt_want_write(path->mnt);
+	if (error)
+		return error;
 	mutex_lock(&inode->i_mutex);
-	err = security_path_chmod(dentry, file->f_vfsmnt, mode);
-	if (err)
+	error = security_path_chmod(path->dentry, path->mnt, mode);
+	if (error)
 		goto out_unlock;
-	if (mode == (mode_t) -1)
-		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	err = notify_change(dentry, &newattrs);
+	error = notify_change(path->dentry, &newattrs);
 out_unlock:
 	mutex_unlock(&inode->i_mutex);
-	mnt_drop_write(file->f_path.mnt);
-out_putf:
-	fput(file);
-out:
+	mnt_drop_write(path->mnt);
+	return error;
+}
+
+SYSCALL_DEFINE2(fchmod, unsigned int, fd, mode_t, mode)
+{
+	struct file * file;
+	int err = -EBADF;
+
+	file = fget(fd);
+	if (file) {
+		audit_inode(NULL, file->f_path.dentry);
+		err = chmod_common(&file->f_path, mode);
+		fput(file);
+	}
 	return err;
 }
 
 SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode)
 {
 	struct path path;
-	struct inode *inode;
 	int error;
-	struct iattr newattrs;
 
 	error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
-	if (error)
-		goto out;
-	inode = path.dentry->d_inode;
-
-	error = mnt_want_write(path.mnt);
-	if (error)
-		goto dput_and_out;
-	mutex_lock(&inode->i_mutex);
-	error = security_path_chmod(path.dentry, path.mnt, mode);
-	if (error)
-		goto out_unlock;
-	if (mode == (mode_t) -1)
-		mode = inode->i_mode;
-	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	error = notify_change(path.dentry, &newattrs);
-out_unlock:
-	mutex_unlock(&inode->i_mutex);
-	mnt_drop_write(path.mnt);
-dput_and_out:
-	path_put(&path);
-out:
+	if (!error) {
+		error = chmod_common(&path, mode);
+		path_put(&path);
+	}
 	return error;
 }
 
diff --git a/fs/pipe.c b/fs/pipe.c
index 1b7f9af..0e0be1d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -948,7 +948,7 @@
 
 static struct inode * get_pipe_inode(void)
 {
-	struct inode *inode = new_inode(pipe_mnt->mnt_sb);
+	struct inode *inode = new_inode_pseudo(pipe_mnt->mnt_sb);
 	struct pipe_inode_info *pipe;
 
 	if (!inode)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index a6227d2..d43729a7 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/posix_acl.h>
diff --git a/fs/proc/base.c b/fs/proc/base.c
index c9e3f65..08e3ecc 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2706,9 +2706,16 @@
 {
 	struct task_io_accounting acct = task->ioac;
 	unsigned long flags;
+	int result;
 
-	if (!ptrace_may_access(task, PTRACE_MODE_READ))
-		return -EACCES;
+	result = mutex_lock_killable(&task->signal->cred_guard_mutex);
+	if (result)
+		return result;
+
+	if (!ptrace_may_access(task, PTRACE_MODE_READ)) {
+		result = -EACCES;
+		goto out_unlock;
+	}
 
 	if (whole && lock_task_sighand(task, &flags)) {
 		struct task_struct *t = task;
@@ -2719,7 +2726,7 @@
 
 		unlock_task_sighand(task, &flags);
 	}
-	return sprintf(buffer,
+	result = sprintf(buffer,
 			"rchar: %llu\n"
 			"wchar: %llu\n"
 			"syscr: %llu\n"
@@ -2734,6 +2741,9 @@
 			(unsigned long long)acct.read_bytes,
 			(unsigned long long)acct.write_bytes,
 			(unsigned long long)acct.cancelled_write_bytes);
+out_unlock:
+	mutex_unlock(&task->signal->cred_guard_mutex);
+	return result;
 }
 
 static int proc_tid_io_accounting(struct task_struct *task, char *buffer)
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index f1637f1..9d99131 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -620,8 +620,7 @@
 	if (!ent) goto out;
 
 	memset(ent, 0, sizeof(struct proc_dir_entry));
-	memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
-	ent->name = ((char *) ent) + sizeof(*ent);
+	memcpy(ent->name, fn, len + 1);
 	ent->namelen = len;
 	ent->mode = mode;
 	ent->nlink = nlink;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 74b48cf..7ed72d6 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -319,7 +319,7 @@
 	if (!pde->proc_fops) {
 		spin_unlock(&pde->pde_unload_lock);
 		kfree(pdeo);
-		return -EINVAL;
+		return -ENOENT;
 	}
 	pde->pde_users++;
 	open = pde->proc_fops->open;
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c
index ed257d1..5861741 100644
--- a/fs/proc/meminfo.c
+++ b/fs/proc/meminfo.c
@@ -10,7 +10,7 @@
 #include <linux/seq_file.h>
 #include <linux/swap.h>
 #include <linux/vmstat.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include "internal.h"
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index 9020ac1..f738024 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -197,15 +197,15 @@
 	int err;
 
 	err = -ENOMEM;
-	netd = kzalloc(sizeof(*netd), GFP_KERNEL);
+	netd = kzalloc(sizeof(*netd) + 4, GFP_KERNEL);
 	if (!netd)
 		goto out;
 
 	netd->data = net;
 	netd->nlink = 2;
-	netd->name = "net";
 	netd->namelen = 3;
 	netd->parent = &proc_root;
+	memcpy(netd->name, "net", 4);
 
 	err = -EEXIST;
 	net_statd = proc_net_mkdir(net, "stat", netd);
diff --git a/fs/proc/root.c b/fs/proc/root.c
index d6c3b41..9a8a2b7 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -186,13 +186,13 @@
 struct proc_dir_entry proc_root = {
 	.low_ino	= PROC_ROOT_INO, 
 	.namelen	= 5, 
-	.name		= "/proc",
 	.mode		= S_IFDIR | S_IRUGO | S_IXUGO, 
 	.nlink		= 2, 
 	.count		= ATOMIC_INIT(1),
 	.proc_iops	= &proc_root_inode_operations, 
 	.proc_fops	= &proc_root_operations,
 	.parent		= &proc_root,
+	.name		= "/proc",
 };
 
 int pid_ns_prepare_proc(struct pid_namespace *ns)
diff --git a/fs/read_write.c b/fs/read_write.c
index 5907b49..179f1c3 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -166,8 +166,10 @@
 			 * long as offset isn't at the end of the file then the
 			 * offset is data.
 			 */
-			if (offset >= inode->i_size)
-				return -ENXIO;
+			if (offset >= inode->i_size) {
+				retval = -ENXIO;
+				goto out;
+			}
 			break;
 		case SEEK_HOLE:
 			/*
@@ -175,8 +177,10 @@
 			 * as long as offset isn't i_size or larger, return
 			 * i_size.
 			 */
-			if (offset >= inode->i_size)
-				return -ENXIO;
+			if (offset >= inode->i_size) {
+				retval = -ENXIO;
+				goto out;
+			}
 			offset = inode->i_size;
 			break;
 	}
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index b2b4119..d1fe745 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -1224,6 +1224,9 @@
 		rw = READ;
 	}
 
+	/* we only use the buffer cache for meta-data */
+	rw |= REQ_META;
+
 next_chunk:
 	atomic_inc(&bp->b_io_remaining);
 	nr_pages = BIO_MAX_SECTORS >> (PAGE_SHIFT - BBSHIFT);
diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c
index 825390e..7f7b424 100644
--- a/fs/xfs/linux-2.6/xfs_file.c
+++ b/fs/xfs/linux-2.6/xfs_file.c
@@ -149,7 +149,9 @@
 
 	xfs_iflags_clear(ip, XFS_ITRUNCATED);
 
+	xfs_ilock(ip, XFS_IOLOCK_SHARED);
 	xfs_ioend_wait(ip);
+	xfs_iunlock(ip, XFS_IOLOCK_SHARED);
 
 	if (mp->m_flags & XFS_MOUNT_BARRIER) {
 		/*
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c
index acca2c5..f7ce7de 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl.c
@@ -265,7 +265,7 @@
 		return PTR_ERR(filp);
 	}
 
-	if (inode->i_mode & S_IFREG) {
+	if (S_ISREG(inode->i_mode)) {
 		filp->f_flags |= O_NOATIME;
 		filp->f_mode |= FMODE_NOCMTIME;
 	}
@@ -850,14 +850,14 @@
 		di_flags |= XFS_DIFLAG_NODEFRAG;
 	if (xflags & XFS_XFLAG_FILESTREAM)
 		di_flags |= XFS_DIFLAG_FILESTREAM;
-	if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+	if (S_ISDIR(ip->i_d.di_mode)) {
 		if (xflags & XFS_XFLAG_RTINHERIT)
 			di_flags |= XFS_DIFLAG_RTINHERIT;
 		if (xflags & XFS_XFLAG_NOSYMLINKS)
 			di_flags |= XFS_DIFLAG_NOSYMLINKS;
 		if (xflags & XFS_XFLAG_EXTSZINHERIT)
 			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
-	} else if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
+	} else if (S_ISREG(ip->i_d.di_mode)) {
 		if (xflags & XFS_XFLAG_REALTIME)
 			di_flags |= XFS_DIFLAG_REALTIME;
 		if (xflags & XFS_XFLAG_EXTSIZE)
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 6544c32..b9c172b 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -1194,9 +1194,14 @@
 		break;
 	}
 
-	/* if there is no attribute fork no ACL can exist on this inode */
-	if (!XFS_IFORK_Q(ip))
+	/*
+	 * If there is no attribute fork no ACL can exist on this inode,
+	 * and it can't have any file capabilities attached to it either.
+	 */
+	if (!XFS_IFORK_Q(ip)) {
+		inode_has_no_xattr(inode);
 		cache_no_acl(inode);
+	}
 
 	xfs_iflags_clear(ip, XFS_INEW);
 	barrier();
diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h
index 2c656ef..39632d9 100644
--- a/fs/xfs/xfs_acl.h
+++ b/fs/xfs/xfs_acl.h
@@ -51,7 +51,10 @@
 extern const struct xattr_handler xfs_xattr_acl_access_handler;
 extern const struct xattr_handler xfs_xattr_acl_default_handler;
 #else
-# define xfs_get_acl(inode, type)			NULL
+static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type)
+{
+	return NULL;
+}
 # define xfs_inherit_acl(inode, default_acl)		0
 # define xfs_acl_chmod(inode)				0
 # define posix_acl_access_exists(inode)			0
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index c51a3f9..ab3e5c6 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -414,7 +414,7 @@
 
 	if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip))
 		return 0;
-	if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+	if (S_ISDIR(ip->i_d.di_mode)) {
 		mp = ip->i_mount;
 		memset(&dargs, 0, sizeof(dargs));
 		dargs.dp = ip;
@@ -3344,8 +3344,7 @@
 	 * We don't want to deal with the case of keeping inode data inline yet.
 	 * So sending the data fork of a regular inode is invalid.
 	 */
-	ASSERT(!((ip->i_d.di_mode & S_IFMT) == S_IFREG &&
-		 whichfork == XFS_DATA_FORK));
+	ASSERT(!(S_ISREG(ip->i_d.di_mode) && whichfork == XFS_DATA_FORK));
 	ifp = XFS_IFORK_PTR(ip, whichfork);
 	ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
 	flags = 0;
@@ -4052,7 +4051,7 @@
 
 #ifndef DEBUG
 	if (whichfork == XFS_DATA_FORK) {
-		return ((ip->i_d.di_mode & S_IFMT) == S_IFREG) ?
+		return S_ISREG(ip->i_d.di_mode) ?
 			(ip->i_size == ip->i_mount->m_sb.sb_blocksize) :
 			(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
 	}
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 2925726..5bfcb87 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -692,6 +692,24 @@
 	return(error);
 }
 
+#ifdef	DEBUG
+static void
+xfs_da_blkinfo_onlychild_validate(struct xfs_da_blkinfo *blkinfo, __u16 level)
+{
+	__be16	magic = blkinfo->magic;
+
+	if (level == 1) {
+		ASSERT(magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) ||
+		       magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
+	} else
+		ASSERT(magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
+	ASSERT(!blkinfo->forw);
+	ASSERT(!blkinfo->back);
+}
+#else	/* !DEBUG */
+#define	xfs_da_blkinfo_onlychild_validate(blkinfo, level)
+#endif	/* !DEBUG */
+
 /*
  * We have only one entry in the root.  Copy the only remaining child of
  * the old root to block 0 as the new root node.
@@ -700,8 +718,6 @@
 xfs_da_root_join(xfs_da_state_t *state, xfs_da_state_blk_t *root_blk)
 {
 	xfs_da_intnode_t *oldroot;
-	/* REFERENCED */
-	xfs_da_blkinfo_t *blkinfo;
 	xfs_da_args_t *args;
 	xfs_dablk_t child;
 	xfs_dabuf_t *bp;
@@ -732,15 +748,9 @@
 	if (error)
 		return(error);
 	ASSERT(bp != NULL);
-	blkinfo = bp->data;
-	if (be16_to_cpu(oldroot->hdr.level) == 1) {
-		ASSERT(blkinfo->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) ||
-		       blkinfo->magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC));
-	} else {
-		ASSERT(blkinfo->magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
-	}
-	ASSERT(!blkinfo->forw);
-	ASSERT(!blkinfo->back);
+	xfs_da_blkinfo_onlychild_validate(bp->data,
+					be16_to_cpu(oldroot->hdr.level));
+
 	memcpy(root_blk->bp->data, bp->data, state->blocksize);
 	xfs_da_log_buf(args->trans, root_blk->bp, 0, state->blocksize - 1);
 	error = xfs_da_shrink_inode(args, child, bp);
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index 4580ce0..a2e2701 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -121,7 +121,7 @@
 {
 	xfs_dir2_sf_hdr_t	*sfp;
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	if (dp->i_d.di_size == 0)	/* might happen during shutdown. */
 		return 1;
 	if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
@@ -179,7 +179,7 @@
 	memset((char *)&args, 0, sizeof(args));
 	args.dp = dp;
 	args.trans = tp;
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino)))
 		return error;
 	return xfs_dir2_sf_create(&args, pdp->i_ino);
@@ -202,7 +202,7 @@
 	int			rval;
 	int			v;		/* type-checking value */
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
 		return rval;
 	XFS_STATS_INC(xs_dir_create);
@@ -278,7 +278,7 @@
 	int		rval;
 	int		v;		/* type-checking value */
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	XFS_STATS_INC(xs_dir_lookup);
 
 	memset(&args, 0, sizeof(xfs_da_args_t));
@@ -333,7 +333,7 @@
 	int		rval;
 	int		v;		/* type-checking value */
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	XFS_STATS_INC(xs_dir_remove);
 
 	memset(&args, 0, sizeof(xfs_da_args_t));
@@ -382,7 +382,7 @@
 	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
 		return XFS_ERROR(EIO);
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 	XFS_STATS_INC(xs_dir_getdents);
 
 	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
@@ -414,7 +414,7 @@
 	int		rval;
 	int		v;		/* type-checking value */
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 
 	if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
 		return rval;
@@ -464,7 +464,7 @@
 	if (resblks)
 		return 0;
 
-	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+	ASSERT(S_ISDIR(dp->i_d.di_mode));
 
 	memset(&args, 0, sizeof(xfs_da_args_t));
 	args.name = name->name;
diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c
index 9124425..3ff3d9e 100644
--- a/fs/xfs/xfs_filestream.c
+++ b/fs/xfs/xfs_filestream.c
@@ -344,9 +344,9 @@
 	 * Either ip is a regular file and pip is a directory, or ip is a
 	 * directory and pip is NULL.
 	 */
-	ASSERT(ip && (((ip->i_d.di_mode & S_IFREG) && pip &&
-	               (pip->i_d.di_mode & S_IFDIR)) ||
-	              ((ip->i_d.di_mode & S_IFDIR) && !pip)));
+	ASSERT(ip && ((S_ISREG(ip->i_d.di_mode) && pip &&
+	               S_ISDIR(pip->i_d.di_mode)) ||
+	              (S_ISDIR(ip->i_d.di_mode) && !pip)));
 
 	mp = ip->i_mount;
 	cache = mp->m_filestream;
@@ -537,7 +537,7 @@
 	xfs_agnumber_t	ag;
 	int		ref;
 
-	if (!(ip->i_d.di_mode & (S_IFREG | S_IFDIR))) {
+	if (!S_ISREG(ip->i_d.di_mode) && !S_ISDIR(ip->i_d.di_mode)) {
 		ASSERT(0);
 		return NULLAGNUMBER;
 	}
@@ -579,9 +579,9 @@
 	xfs_agnumber_t	ag, rotorstep, startag;
 	int		err = 0;
 
-	ASSERT(pip->i_d.di_mode & S_IFDIR);
-	ASSERT(ip->i_d.di_mode & S_IFREG);
-	if (!(pip->i_d.di_mode & S_IFDIR) || !(ip->i_d.di_mode & S_IFREG))
+	ASSERT(S_ISDIR(pip->i_d.di_mode));
+	ASSERT(S_ISREG(ip->i_d.di_mode));
+	if (!S_ISDIR(pip->i_d.di_mode) || !S_ISREG(ip->i_d.di_mode))
 		return -EINVAL;
 
 	mp = pip->i_mount;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 3cc21dd..2fcca4b 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -368,7 +368,7 @@
 			/*
 			 * no local regular files yet
 			 */
-			if (unlikely((be16_to_cpu(dip->di_mode) & S_IFMT) == S_IFREG)) {
+			if (unlikely(S_ISREG(be16_to_cpu(dip->di_mode)))) {
 				xfs_warn(ip->i_mount,
 			"corrupt inode %Lu (local format for regular file).",
 					(unsigned long long) ip->i_ino);
@@ -1040,7 +1040,7 @@
 
 	if (pip && XFS_INHERIT_GID(pip)) {
 		ip->i_d.di_gid = pip->i_d.di_gid;
-		if ((pip->i_d.di_mode & S_ISGID) && (mode & S_IFMT) == S_IFDIR) {
+		if ((pip->i_d.di_mode & S_ISGID) && S_ISDIR(mode)) {
 			ip->i_d.di_mode |= S_ISGID;
 		}
 	}
@@ -1097,14 +1097,14 @@
 		if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
 			uint	di_flags = 0;
 
-			if ((mode & S_IFMT) == S_IFDIR) {
+			if (S_ISDIR(mode)) {
 				if (pip->i_d.di_flags & XFS_DIFLAG_RTINHERIT)
 					di_flags |= XFS_DIFLAG_RTINHERIT;
 				if (pip->i_d.di_flags & XFS_DIFLAG_EXTSZINHERIT) {
 					di_flags |= XFS_DIFLAG_EXTSZINHERIT;
 					ip->i_d.di_extsize = pip->i_d.di_extsize;
 				}
-			} else if ((mode & S_IFMT) == S_IFREG) {
+			} else if (S_ISREG(mode)) {
 				if (pip->i_d.di_flags & XFS_DIFLAG_RTINHERIT)
 					di_flags |= XFS_DIFLAG_REALTIME;
 				if (pip->i_d.di_flags & XFS_DIFLAG_EXTSZINHERIT) {
@@ -1188,7 +1188,7 @@
 	int			nimaps;
 	xfs_bmbt_irec_t		imaps[2];
 
-	if ((ip->i_d.di_mode & S_IFMT) != S_IFREG)
+	if (!S_ISREG(ip->i_d.di_mode))
 		return;
 
 	if (XFS_IS_REALTIME_INODE(ip))
@@ -1828,7 +1828,7 @@
 	ASSERT(ip->i_d.di_nextents == 0);
 	ASSERT(ip->i_d.di_anextents == 0);
 	ASSERT((ip->i_d.di_size == 0 && ip->i_size == 0) ||
-	       ((ip->i_d.di_mode & S_IFMT) != S_IFREG));
+	       (!S_ISREG(ip->i_d.di_mode)));
 	ASSERT(ip->i_d.di_nblocks == 0);
 
 	/*
@@ -2671,7 +2671,7 @@
 			__func__, ip->i_ino, ip, ip->i_d.di_magic);
 		goto corrupt_out;
 	}
-	if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
+	if (S_ISREG(ip->i_d.di_mode)) {
 		if (XFS_TEST_ERROR(
 		    (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS) &&
 		    (ip->i_d.di_format != XFS_DINODE_FMT_BTREE),
@@ -2681,7 +2681,7 @@
 				__func__, ip->i_ino, ip);
 			goto corrupt_out;
 		}
-	} else if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+	} else if (S_ISDIR(ip->i_d.di_mode)) {
 		if (XFS_TEST_ERROR(
 		    (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS) &&
 		    (ip->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index a97644a..2380a4b 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -263,7 +263,7 @@
 	struct inode		i_vnode;	/* embedded VFS inode */
 } xfs_inode_t;
 
-#define XFS_ISIZE(ip)	(((ip)->i_d.di_mode & S_IFMT) == S_IFREG) ? \
+#define XFS_ISIZE(ip)	S_ISREG((ip)->i_d.di_mode) ? \
 				(ip)->i_size : (ip)->i_d.di_size;
 
 /* Convert from vfs inode to xfs inode */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 8fe4206..052a2c0 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -2283,7 +2283,7 @@
 	/* Take the opportunity to reset the flush iteration count */
 	dicp->di_flushiter = 0;
 
-	if (unlikely((dicp->di_mode & S_IFMT) == S_IFREG)) {
+	if (unlikely(S_ISREG(dicp->di_mode))) {
 		if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) &&
 		    (dicp->di_format != XFS_DINODE_FMT_BTREE)) {
 			XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
@@ -2296,7 +2296,7 @@
 			error = EFSCORRUPTED;
 			goto error;
 		}
-	} else if (unlikely((dicp->di_mode & S_IFMT) == S_IFDIR)) {
+	} else if (unlikely(S_ISDIR(dicp->di_mode))) {
 		if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) &&
 		    (dicp->di_format != XFS_DINODE_FMT_BTREE) &&
 		    (dicp->di_format != XFS_DINODE_FMT_LOCAL)) {
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 7f25245..092e16a 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1331,7 +1331,7 @@
 
 	ASSERT(rip != NULL);
 
-	if (unlikely((rip->i_d.di_mode & S_IFMT) != S_IFDIR)) {
+	if (unlikely(!S_ISDIR(rip->i_d.di_mode))) {
 		xfs_warn(mp, "corrupted root inode %llu: not a directory",
 			(unsigned long long)rip->i_ino);
 		xfs_iunlock(rip, XFS_ILOCK_EXCL);
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c
index 77a5989..df78c29 100644
--- a/fs/xfs/xfs_rename.c
+++ b/fs/xfs/xfs_rename.c
@@ -116,7 +116,7 @@
 	trace_xfs_rename(src_dp, target_dp, src_name, target_name);
 
 	new_parent = (src_dp != target_dp);
-	src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR);
+	src_is_directory = S_ISDIR(src_ip->i_d.di_mode);
 
 	if (src_is_directory) {
 		/*
@@ -226,7 +226,7 @@
 		 * target and source are directories and that target can be
 		 * destroyed, or that neither is a directory.
 		 */
-		if ((target_ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+		if (S_ISDIR(target_ip->i_d.di_mode)) {
 			/*
 			 * Make sure target dir is empty.
 			 */
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 88d1214..9322e13 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -121,7 +121,7 @@
 
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
 
-	ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFLNK);
+	ASSERT(S_ISLNK(ip->i_d.di_mode));
 	ASSERT(ip->i_d.di_size <= MAXPATHLEN);
 
 	pathlen = ip->i_d.di_size;
@@ -529,7 +529,7 @@
 	if (ip->i_d.di_nlink == 0)
 		return 0;
 
-	if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
+	if ((S_ISREG(ip->i_d.di_mode) &&
 	     ((ip->i_size > 0) || (VN_CACHED(VFS_I(ip)) > 0 ||
 	       ip->i_delayed_blks > 0)) &&
 	     (ip->i_df.if_flags & XFS_IFEXTENTS))  &&
@@ -610,7 +610,7 @@
 	truncate = ((ip->i_d.di_nlink == 0) &&
 	    ((ip->i_d.di_size != 0) || (ip->i_size != 0) ||
 	     (ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) &&
-	    ((ip->i_d.di_mode & S_IFMT) == S_IFREG));
+	    S_ISREG(ip->i_d.di_mode));
 
 	mp = ip->i_mount;
 
@@ -621,7 +621,7 @@
 		goto out;
 
 	if (ip->i_d.di_nlink != 0) {
-		if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
+		if ((S_ISREG(ip->i_d.di_mode) &&
                      ((ip->i_size > 0) || (VN_CACHED(VFS_I(ip)) > 0 ||
                        ip->i_delayed_blks > 0)) &&
 		      (ip->i_df.if_flags & XFS_IFEXTENTS) &&
@@ -669,7 +669,7 @@
 			xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
 			return VN_INACTIVE_CACHE;
 		}
-	} else if ((ip->i_d.di_mode & S_IFMT) == S_IFLNK) {
+	} else if (S_ISLNK(ip->i_d.di_mode)) {
 
 		/*
 		 * If we get an error while cleaning up a
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 2ce1be9..f72403c 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -59,7 +59,7 @@
 #include <linux/ctype.h>
 #include <linux/sched.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/div64.h>
 #include <asm/acpi.h>
 #include <linux/slab.h>
diff --git a/include/acpi/video.h b/include/acpi/video.h
index 0e98e67..61109f2 100644
--- a/include/acpi/video.h
+++ b/include/acpi/video.h
@@ -5,6 +5,8 @@
 
 struct acpi_device;
 
+#define ACPI_VIDEO_CLASS	"video"
+
 #define ACPI_VIDEO_DISPLAY_CRT  1
 #define ACPI_VIDEO_DISPLAY_TV   2
 #define ACPI_VIDEO_DISPLAY_DVI  3
diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h
index e994197..e37963c 100644
--- a/include/asm-generic/atomic.h
+++ b/include/asm-generic/atomic.h
@@ -1,5 +1,7 @@
 /*
- * Generic C implementation of atomic counter operations
+ * Generic C implementation of atomic counter operations. Usable on
+ * UP systems only. Do not include in machine independent code.
+ *
  * Originally implemented for MN10300.
  *
  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
@@ -14,7 +16,11 @@
 #define __ASM_GENERIC_ATOMIC_H
 
 #ifdef CONFIG_SMP
-#error not SMP safe
+/* Force people to define core atomics */
+# if !defined(atomic_add_return) || !defined(atomic_sub_return) || \
+     !defined(atomic_clear_mask) || !defined(atomic_set_mask)
+#  error "SMP requires a little arch-specific magic"
+# endif
 #endif
 
 /*
@@ -32,7 +38,9 @@
  *
  * Atomically reads the value of @v.
  */
+#ifndef atomic_read
 #define atomic_read(v)	(*(volatile int *)&(v)->counter)
+#endif
 
 /**
  * atomic_set - set atomic variable
@@ -53,6 +61,7 @@
  *
  * Atomically adds @i to @v and returns the result
  */
+#ifndef atomic_add_return
 static inline int atomic_add_return(int i, atomic_t *v)
 {
 	unsigned long flags;
@@ -66,6 +75,7 @@
 
 	return temp;
 }
+#endif
 
 /**
  * atomic_sub_return - subtract integer from atomic variable
@@ -74,6 +84,7 @@
  *
  * Atomically subtracts @i from @v and returns the result
  */
+#ifndef atomic_sub_return
 static inline int atomic_sub_return(int i, atomic_t *v)
 {
 	unsigned long flags;
@@ -87,6 +98,7 @@
 
 	return temp;
 }
+#endif
 
 static inline int atomic_add_negative(int i, atomic_t *v)
 {
@@ -117,8 +129,8 @@
 #define atomic_inc_return(v)		atomic_add_return(1, (v))
 
 #define atomic_sub_and_test(i, v)	(atomic_sub_return((i), (v)) == 0)
-#define atomic_dec_and_test(v)		(atomic_sub_return(1, (v)) == 0)
-#define atomic_inc_and_test(v)		(atomic_add_return(1, (v)) == 0)
+#define atomic_dec_and_test(v)		(atomic_dec_return(v) == 0)
+#define atomic_inc_and_test(v)		(atomic_inc_return(v) == 0)
 
 #define atomic_xchg(ptr, v)		(xchg(&(ptr)->counter, (v)))
 #define atomic_cmpxchg(v, old, new)	(cmpxchg(&((v)->counter), (old), (new)))
@@ -129,26 +141,51 @@
 
 #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
 
-static inline int atomic_add_unless(atomic_t *v, int a, int u)
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
   int c, old;
   c = atomic_read(v);
   while (c != u && (old = atomic_cmpxchg(v, c, c + a)) != c)
     c = old;
-  return c != u;
+  return c;
 }
 
-#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
-
-static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
+/**
+ * atomic_clear_mask - Atomically clear bits in atomic variable
+ * @mask: Mask of the bits to be cleared
+ * @v: pointer of type atomic_t
+ *
+ * Atomically clears the bits set in @mask from @v
+ */
+#ifndef atomic_clear_mask
+static inline void atomic_clear_mask(unsigned long mask, atomic_t *v)
 {
 	unsigned long flags;
 
 	mask = ~mask;
 	raw_local_irq_save(flags); /* Don't trace it in a irqsoff handler */
-	*addr &= mask;
+	v->counter &= mask;
 	raw_local_irq_restore(flags);
 }
+#endif
+
+/**
+ * atomic_set_mask - Atomically set bits in atomic variable
+ * @mask: Mask of the bits to be set
+ * @v: pointer of type atomic_t
+ *
+ * Atomically sets the bits set in @mask in @v
+ */
+#ifndef atomic_set_mask
+static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
+{
+	unsigned long flags;
+
+	raw_local_irq_save(flags); /* Don't trace it in a irqsoff handler */
+	v->counter |= mask;
+	raw_local_irq_restore(flags);
+}
+#endif
 
 /* Assume that atomic operations are already serializing */
 #define smp_mb__before_atomic_dec()	barrier()
@@ -156,7 +193,5 @@
 #define smp_mb__before_atomic_inc()	barrier()
 #define smp_mb__after_atomic_inc()	barrier()
 
-#include <asm-generic/atomic-long.h>
-
 #endif /* __KERNEL__ */
 #endif /* __ASM_GENERIC_ATOMIC_H */
diff --git a/include/asm-generic/bitops/ext2-atomic-setbit.h b/include/asm-generic/bitops/ext2-atomic-setbit.h
new file mode 100644
index 0000000..5a09978
--- /dev/null
+++ b/include/asm-generic/bitops/ext2-atomic-setbit.h
@@ -0,0 +1,11 @@
+#ifndef _ASM_GENERIC_BITOPS_EXT2_ATOMIC_SETBIT_H_
+#define _ASM_GENERIC_BITOPS_EXT2_ATOMIC_SETBIT_H_
+
+/*
+ * Atomic bitops based version of ext2 atomic bitops
+ */
+
+#define ext2_set_bit_atomic(l, nr, addr)	test_and_set_bit_le(nr, addr)
+#define ext2_clear_bit_atomic(l, nr, addr)	test_and_clear_bit_le(nr, addr)
+
+#endif /* _ASM_GENERIC_BITOPS_EXT2_ATOMIC_SETBIT_H_ */
diff --git a/include/asm-generic/bitops/ext2-atomic.h b/include/asm-generic/bitops/ext2-atomic.h
index ecf1c9d..87f0f10 100644
--- a/include/asm-generic/bitops/ext2-atomic.h
+++ b/include/asm-generic/bitops/ext2-atomic.h
@@ -1,6 +1,10 @@
 #ifndef _ASM_GENERIC_BITOPS_EXT2_ATOMIC_H_
 #define _ASM_GENERIC_BITOPS_EXT2_ATOMIC_H_
 
+/*
+ * Spinlock based version of ext2 atomic bitops
+ */
+
 #define ext2_set_bit_atomic(lock, nr, addr)		\
 	({						\
 		int ret;				\
diff --git a/include/asm-generic/local.h b/include/asm-generic/local.h
index c8a5d68..9ceb03b 100644
--- a/include/asm-generic/local.h
+++ b/include/asm-generic/local.h
@@ -2,7 +2,7 @@
 #define _ASM_GENERIC_LOCAL_H
 
 #include <linux/percpu.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/types.h>
 
 /*
diff --git a/include/asm-generic/local64.h b/include/asm-generic/local64.h
index 02ac760..5980002 100644
--- a/include/asm-generic/local64.h
+++ b/include/asm-generic/local64.h
@@ -55,7 +55,7 @@
 
 #else /* BITS_PER_LONG != 64 */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /* Don't use typedef: don't want them to be mixed with atomic_t's. */
 typedef struct {
diff --git a/include/asm-generic/pci-bridge.h b/include/asm-generic/pci-bridge.h
new file mode 100644
index 0000000..4a5aca2
--- /dev/null
+++ b/include/asm-generic/pci-bridge.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _ASM_GENERIC_PCI_BRIDGE_H
+#define _ASM_GENERIC_PCI_BRIDGE_H
+
+#ifdef __KERNEL__
+
+enum {
+	/* Force re-assigning all resources (ignore firmware
+	 * setup completely)
+	 */
+	PCI_REASSIGN_ALL_RSRC	= 0x00000001,
+
+	/* Re-assign all bus numbers */
+	PCI_REASSIGN_ALL_BUS	= 0x00000002,
+
+	/* Do not try to assign, just use existing setup */
+	PCI_PROBE_ONLY		= 0x00000004,
+
+	/* Don't bother with ISA alignment unless the bridge has
+	 * ISA forwarding enabled
+	 */
+	PCI_CAN_SKIP_ISA_ALIGN	= 0x00000008,
+
+	/* Enable domain numbers in /proc */
+	PCI_ENABLE_PROC_DOMAINS	= 0x00000010,
+	/* ... except for domain 0 */
+	PCI_COMPAT_DOMAIN_0	= 0x00000020,
+};
+
+#ifdef CONFIG_PCI
+extern unsigned int pci_flags;
+
+static inline void pci_set_flags(int flags)
+{
+	pci_flags = flags;
+}
+
+static inline void pci_add_flags(int flags)
+{
+	pci_flags |= flags;
+}
+
+static inline int pci_has_flag(int flag)
+{
+	return pci_flags & flag;
+}
+#else
+static inline void pci_set_flags(int flags) { }
+static inline void pci_add_flags(int flags) { }
+static inline int pci_has_flag(int flag)
+{
+	return 0;
+}
+#endif	/* CONFIG_PCI */
+
+#endif	/* __KERNEL__ */
+#endif	/* _ASM_GENERIC_PCI_BRIDGE_H */
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 738b3a5..9b7c2bb 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -886,6 +886,8 @@
 	 */
 	int (*gem_init_object) (struct drm_gem_object *obj);
 	void (*gem_free_object) (struct drm_gem_object *obj);
+	int (*gem_open_object) (struct drm_gem_object *, struct drm_file *);
+	void (*gem_close_object) (struct drm_gem_object *, struct drm_file *);
 
 	/* vga arb irq handler */
 	void (*vgaarb_irq)(struct drm_device *dev, bool state);
@@ -1539,6 +1541,8 @@
 					    size_t size);
 int drm_gem_object_init(struct drm_device *dev,
 			struct drm_gem_object *obj, size_t size);
+int drm_gem_private_object_init(struct drm_device *dev,
+			struct drm_gem_object *obj, size_t size);
 void drm_gem_object_handle_free(struct drm_gem_object *obj);
 void drm_gem_vm_open(struct vm_area_struct *vma);
 void drm_gem_vm_close(struct vm_area_struct *vma);
@@ -1672,13 +1676,6 @@
 	return pci_find_capability(dev->pdev, PCI_CAP_ID_AGP);
 }
 
-
-static __inline__ int drm_pci_device_is_pcie(struct drm_device *dev)
-{
-	return pci_find_capability(dev->pdev, PCI_CAP_ID_EXP);
-}
-
-
 extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver);
 extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver);
 extern int drm_get_pci_dev(struct pci_dev *pdev,
diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
index 787f7b6..b65be60 100644
--- a/include/drm/radeon_drm.h
+++ b/include/drm/radeon_drm.h
@@ -911,6 +911,7 @@
 #define RADEON_INFO_NUM_BACKENDS	0x0a /* DB/backends for r600+ - need for OQ */
 #define RADEON_INFO_NUM_TILE_PIPES	0x0b /* tile pipes for r600+ */
 #define RADEON_INFO_FUSION_GART_WORKING	0x0c /* fusion writes to GTT were broken before this */
+#define RADEON_INFO_BACKEND_MAP		0x0d /* pipe to backend map, needed by mesa */
 
 struct drm_radeon_info {
 	uint32_t		request;
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 62a0e4c..42e3469 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -662,9 +662,6 @@
 
 extern void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map);
 
-#if 0
-#endif
-
 /**
  * ttm_fbdev_mmap - mmap fbdev memory backed by a ttm buffer object.
  *
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index 09af2d7..94eb143 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -78,7 +78,7 @@
 	 *
 	 * Bind the backend pages into the aperture in the location
 	 * indicated by @bo_mem. This function should be able to handle
-	 * differences between aperture- and system page sizes.
+	 * differences between aperture and system page sizes.
 	 */
 	int (*bind) (struct ttm_backend *backend, struct ttm_mem_reg *bo_mem);
 
@@ -88,7 +88,7 @@
 	 * @backend: Pointer to a struct ttm_backend.
 	 *
 	 * Unbind previously bound backend pages. This function should be
-	 * able to handle differences between aperture- and system page sizes.
+	 * able to handle differences between aperture and system page sizes.
 	 */
 	int (*unbind) (struct ttm_backend *backend);
 
@@ -786,7 +786,7 @@
  * ttm_bo_device_init
  *
  * @bdev: A pointer to a struct ttm_bo_device to initialize.
- * @mem_global: A pointer to an initialized struct ttm_mem_global.
+ * @glob: A pointer to an initialized struct ttm_bo_global.
  * @driver: A pointer to a struct ttm_bo_driver set up by the caller.
  * @file_page_offset: Offset into the device address space that is available
  * for buffer data. This ensures compatibility with other users of the
diff --git a/include/drm/ttm/ttm_lock.h b/include/drm/ttm/ttm_lock.h
index 81ba0b0..2e7f0c9 100644
--- a/include/drm/ttm/ttm_lock.h
+++ b/include/drm/ttm/ttm_lock.h
@@ -51,7 +51,7 @@
 
 #include "ttm/ttm_object.h"
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /**
  * struct ttm_lock
diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h
index b199170..26c1f78 100644
--- a/include/drm/ttm/ttm_memory.h
+++ b/include/drm/ttm/ttm_memory.h
@@ -41,7 +41,7 @@
  * @do_shrink: The callback function.
  *
  * Arguments to the do_shrink functions are intended to be passed using
- * inheritance. That is, the argument class derives from struct ttm_mem_srink,
+ * inheritance. That is, the argument class derives from struct ttm_mem_shrink,
  * and can be accessed using container_of().
  */
 
diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
index 0d9db09..e46054e 100644
--- a/include/drm/ttm/ttm_object.h
+++ b/include/drm/ttm/ttm_object.h
@@ -111,7 +111,7 @@
  *
  * @ref_obj_release: A function to be called when a reference object
  * with another ttm_ref_type than TTM_REF_USAGE is deleted.
- * this function may, for example, release a lock held by a user-space
+ * This function may, for example, release a lock held by a user-space
  * process.
  *
  * This struct is intended to be used as a base struct for objects that
@@ -172,7 +172,7 @@
 /**
  * ttm_base_object_unref
  *
- * @p_base: Pointer to a pointer referncing a struct ttm_base_object.
+ * @p_base: Pointer to a pointer referencing a struct ttm_base_object.
  *
  * Decrements the base object refcount and clears the pointer pointed to by
  * p_base.
diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h
index 8062890..129de12 100644
--- a/include/drm/ttm/ttm_page_alloc.h
+++ b/include/drm/ttm/ttm_page_alloc.h
@@ -32,7 +32,7 @@
 /**
  * Get count number of pages from pool to pages list.
  *
- * @pages: heado of empty linked list where pages are filled.
+ * @pages: head of empty linked list where pages are filled.
  * @flags: ttm flags for page allocation.
  * @cstate: ttm caching state for the page.
  * @count: number of pages to allocate.
diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
index 9585501..1d45413 100644
--- a/include/keys/encrypted-type.h
+++ b/include/keys/encrypted-type.h
@@ -1,6 +1,11 @@
 /*
  * Copyright (C) 2010 IBM Corporation
- * Author: Mimi Zohar <zohar@us.ibm.com>
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
  *
  * 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
@@ -15,13 +20,17 @@
 
 struct encrypted_key_payload {
 	struct rcu_head rcu;
+	char *format;		/* datablob: format */
 	char *master_desc;	/* datablob: master key name */
 	char *datalen;		/* datablob: decrypted key length */
 	u8 *iv;			/* datablob: iv */
 	u8 *encrypted_data;	/* datablob: encrypted data */
 	unsigned short datablob_len;	/* length of datablob */
 	unsigned short decrypted_datalen;	/* decrypted data length */
-	u8 decrypted_data[0];	/* decrypted data +  datablob + hmac */
+	unsigned short payload_datalen;		/* payload data length */
+	unsigned short encrypted_key_format;	/* encrypted key format */
+	u8 *decrypted_data;	/* decrypted data */
+	u8 payload_data[0];	/* payload data + datablob + hmac */
 };
 
 extern struct key_type key_type_encrypted;
diff --git a/include/linux/aer.h b/include/linux/aer.h
index 8414de2..544abdb 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -51,5 +51,8 @@
 
 extern void cper_print_aer(const char *prefix, int cper_severity,
 			   struct aer_capability_regs *aer);
+extern int cper_severity_to_aer(int cper_severity);
+extern void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
+			      int severity);
 #endif //_AER_H_
 
diff --git a/include/linux/aio.h b/include/linux/aio.h
index 7a8db41..2dcb72b 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -7,7 +7,7 @@
 #include <linux/uio.h>
 #include <linux/rcupdate.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define AIO_MAXSEGS		4
 #define AIO_KIOGRP_NR_ATOMIC	8
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index 381f4ce..49a83ca 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -220,7 +220,7 @@
 #include <linux/skbuff.h> /* struct sk_buff */
 #include <linux/uio.h>
 #include <net/sock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
diff --git a/include/linux/atomic.h b/include/linux/atomic.h
index bc6615d..42b77b5 100644
--- a/include/linux/atomic.h
+++ b/include/linux/atomic.h
@@ -1,8 +1,32 @@
+/* Atomic operations usable in machine independent code */
 #ifndef _LINUX_ATOMIC_H
 #define _LINUX_ATOMIC_H
 #include <asm/atomic.h>
 
 /**
+ * atomic_add_unless - add unless the number is already a given value
+ * @v: pointer of type atomic_t
+ * @a: the amount to add to v...
+ * @u: ...unless v is equal to u.
+ *
+ * Atomically adds @a to @v, so long as @v was not already @u.
+ * Returns non-zero if @v was not @u, and zero otherwise.
+ */
+static inline int atomic_add_unless(atomic_t *v, int a, int u)
+{
+	return __atomic_add_unless(v, a, u) != u;
+}
+
+/**
+ * atomic_inc_not_zero - increment unless the number is zero
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1, so long as @v is non-zero.
+ * Returns non-zero if @v was non-zero, and zero otherwise.
+ */
+#define atomic_inc_not_zero(v)		atomic_add_unless((v), 1, 0)
+
+/**
  * atomic_inc_not_zero_hint - increment if not null
  * @v: pointer of type atomic_t
  * @hint: probable value of the atomic before the increment
@@ -73,4 +97,8 @@
 }
 #endif /* #ifndef CONFIG_ARCH_HAS_ATOMIC_OR */
 
+#include <asm-generic/atomic-long.h>
+#ifdef CONFIG_GENERIC_ATOMIC64
+#include <asm-generic/atomic64.h>
+#endif
 #endif /* _LINUX_ATOMIC_H */
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 96f4094..3b2f9cb 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -16,7 +16,7 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/writeback.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct page;
 struct device;
@@ -40,6 +40,7 @@
 enum bdi_stat_item {
 	BDI_RECLAIMABLE,
 	BDI_WRITEBACK,
+	BDI_WRITTEN,
 	NR_BDI_STAT_ITEMS
 };
 
@@ -57,6 +58,7 @@
 	struct list_head b_dirty;	/* dirty inodes */
 	struct list_head b_io;		/* parked for writeback */
 	struct list_head b_more_io;	/* parked for more writeback */
+	spinlock_t list_lock;		/* protects the b_* lists */
 };
 
 struct backing_dev_info {
@@ -71,6 +73,11 @@
 
 	struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];
 
+	unsigned long bw_time_stamp;	/* last time write bw is updated */
+	unsigned long written_stamp;	/* pages written at bw_time_stamp */
+	unsigned long write_bandwidth;	/* the estimated write bandwidth */
+	unsigned long avg_write_bandwidth; /* further smoothed write bw */
+
 	struct prop_local_percpu completions;
 	int dirty_exceeded;
 
@@ -106,6 +113,7 @@
 int bdi_has_dirty_io(struct backing_dev_info *bdi);
 void bdi_arm_supers_timer(void);
 void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi);
+void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2);
 
 extern spinlock_t bdi_lock;
 extern struct list_head bdi_list;
diff --git a/include/linux/bit_spinlock.h b/include/linux/bit_spinlock.h
index 564d997..ac4d9f8 100644
--- a/include/linux/bit_spinlock.h
+++ b/include/linux/bit_spinlock.h
@@ -3,7 +3,7 @@
 
 #include <linux/kernel.h>
 #include <linux/preempt.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  *  bit-based spin_lock()
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index dcafe0b..3bac44c 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -144,6 +144,7 @@
 extern void bitmap_release_region(unsigned long *bitmap, int pos, int order);
 extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
 extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
+extern int bitmap_ord_to_pos(const unsigned long *bitmap, int n, int bits);
 
 #define BITMAP_LAST_WORD_MASK(nbits)					\
 (									\
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 503c8a6..458f497 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -12,7 +12,7 @@
 #include <linux/linkage.h>
 #include <linux/pagemap.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_BLOCK
 
diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h
index 31d91a6..d7adf15 100644
--- a/include/linux/ceph/messenger.h
+++ b/include/linux/ceph/messenger.h
@@ -94,6 +94,7 @@
 	bool more_to_follow;
 	bool needs_out_seq;
 	int front_max;
+	unsigned long ack_stamp;        /* tx: when we were acked */
 
 	struct ceph_msgpool *pool;
 };
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index 645778a..3081c58 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -42,7 +42,7 @@
 #include <linux/mutex.h>
 #include <linux/err.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define CONFIGFS_ITEM_NAME_LEN	20
 
diff --git a/include/linux/connector.h b/include/linux/connector.h
index f696bcc..0c69ad8 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -75,7 +75,7 @@
 
 #ifdef __KERNEL__
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/list.h>
 #include <linux/workqueue.h>
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 11be48e..6216115 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -324,11 +324,16 @@
 /* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */
 #ifdef CONFIG_CPU_FREQ
 unsigned int cpufreq_quick_get(unsigned int cpu);
+unsigned int cpufreq_quick_get_max(unsigned int cpu);
 #else
 static inline unsigned int cpufreq_quick_get(unsigned int cpu)
 {
 	return 0;
 }
+static inline unsigned int cpufreq_quick_get_max(unsigned int cpu)
+{
+	return 0;
+}
 #endif
 
 
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index b24ac56..4f7a632 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -617,6 +617,20 @@
  *	  ... use 'tmpmask' like a normal struct cpumask * ...
  *
  *	free_cpumask_var(tmpmask);
+ *
+ *
+ * However, one notable exception is there. alloc_cpumask_var() allocates
+ * only nr_cpumask_bits bits (in the other hand, real cpumask_t always has
+ * NR_CPUS bits). Therefore you don't have to dereference cpumask_var_t.
+ *
+ *	cpumask_var_t tmpmask;
+ *	if (!alloc_cpumask_var(&tmpmask, GFP_KERNEL))
+ *		return -ENOMEM;
+ *
+ *	var = *tmpmask;
+ *
+ * This code makes NR_CPUS length memcopy and brings to a memory corruption.
+ * cpumask_copy() provide safe copy functionality.
  */
 #ifdef CONFIG_CPUMASK_OFFSTACK
 typedef struct cpumask *cpumask_var_t;
diff --git a/include/linux/cred.h b/include/linux/cred.h
index f240f2f..48e82af 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -16,7 +16,7 @@
 #include <linux/init.h>
 #include <linux/key.h>
 #include <linux/selinux.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct user_struct;
 struct cred;
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index a6a7a1c..e5e468e 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -17,7 +17,7 @@
 #ifndef _LINUX_CRYPTO_H
 #define _LINUX_CRYPTO_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index 6fe2114..c077aec 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -11,6 +11,8 @@
 #ifndef _CS5535_H
 #define _CS5535_H
 
+#include <asm/msr.h>
+
 /* MSRs */
 #define MSR_GLIU_P2D_RO0	0x10000029
 
@@ -38,17 +40,75 @@
 #define MSR_MFGPT_NR		0x51400029
 #define MSR_MFGPT_SETUP		0x5140002B
 
+#define MSR_RTC_DOMA_OFFSET	0x51400055
+#define MSR_RTC_MONA_OFFSET	0x51400056
+#define MSR_RTC_CEN_OFFSET	0x51400057
+
 #define MSR_LX_SPARE_MSR	0x80000011	/* DC-specific */
 
 #define MSR_GX_GLD_MSR_CONFIG	0xC0002001
 #define MSR_GX_MSR_PADSEL	0xC0002011
 
+static inline int cs5535_pic_unreqz_select_high(unsigned int group,
+						unsigned int irq)
+{
+	uint32_t lo, hi;
+
+	rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+	lo &= ~(0xF << (group * 4));
+	lo |= (irq & 0xF) << (group * 4);
+	wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+	return 0;
+}
+
+/* PIC registers */
+#define CS5536_PIC_INT_SEL1	0x4d0
+#define CS5536_PIC_INT_SEL2	0x4d1
+
 /* resource sizes */
 #define LBAR_GPIO_SIZE		0xFF
 #define LBAR_MFGPT_SIZE		0x40
 #define LBAR_ACPI_SIZE		0x40
 #define LBAR_PMS_SIZE		0x80
 
+/*
+ * PMC registers (PMS block)
+ * It is only safe to access these registers as dword accesses.
+ * See CS5536 Specification Update erratas 17 & 18
+ */
+#define CS5536_PM_SCLK		0x10
+#define CS5536_PM_IN_SLPCTL	0x20
+#define CS5536_PM_WKXD		0x34
+#define CS5536_PM_WKD		0x30
+#define CS5536_PM_SSC		0x54
+
+/*
+ * PM registers (ACPI block)
+ * It is only safe to access these registers as dword accesses.
+ * See CS5536 Specification Update erratas 17 & 18
+ */
+#define CS5536_PM1_STS		0x00
+#define CS5536_PM1_EN		0x02
+#define CS5536_PM1_CNT		0x08
+#define CS5536_PM_GPE0_STS	0x18
+#define CS5536_PM_GPE0_EN	0x1c
+
+/* CS5536_PM1_STS bits */
+#define CS5536_WAK_FLAG		(1 << 15)
+#define CS5536_PWRBTN_FLAG	(1 << 8)
+
+/* CS5536_PM1_EN bits */
+#define CS5536_PM_PWRBTN	(1 << 8)
+#define CS5536_PM_RTC		(1 << 10)
+
+/* CS5536_PM_GPE0_STS bits */
+#define CS5536_GPIOM7_PME_FLAG	(1 << 31)
+#define CS5536_GPIOM6_PME_FLAG	(1 << 30)
+
+/* CS5536_PM_GPE0_EN bits */
+#define CS5536_GPIOM7_PME_EN	(1 << 31)
+#define CS5536_GPIOM6_PME_EN	(1 << 30)
+
 /* VSA2 magic values */
 #define VSA_VRC_INDEX		0xAC1C
 #define VSA_VRC_DATA		0xAC1E
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 3f22d8d..d37d2a7 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_DCACHE_H
 #define __LINUX_DCACHE_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
 #include <linux/rculist_bl.h>
diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h
index 2833452..5033fb8 100644
--- a/include/linux/debug_locks.h
+++ b/include/linux/debug_locks.h
@@ -2,7 +2,7 @@
 #define __LINUX_DEBUG_LOCKING_H
 
 #include <linux/kernel.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 struct task_struct;
diff --git a/include/linux/device.h b/include/linux/device.h
index 160d4dd..c20dfbf 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -22,7 +22,7 @@
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/pm.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/device.h>
 
 struct device;
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 1a167c4..347fdc3 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -46,27 +46,6 @@
 
 #define DMA_BIT_MASK(n)	(((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
 
-typedef u64 DMA_nnBIT_MASK __deprecated;
-
-/*
- * NOTE: do not use the below macros in new code and do not add new definitions
- * here.
- *
- * Instead, just open-code DMA_BIT_MASK(n) within your driver
- */
-#define DMA_64BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(64)
-#define DMA_48BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(48)
-#define DMA_47BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(47)
-#define DMA_40BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(40)
-#define DMA_39BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(39)
-#define DMA_35BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(35)
-#define DMA_32BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(32)
-#define DMA_31BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(31)
-#define DMA_30BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(30)
-#define DMA_29BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(29)
-#define DMA_28BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(28)
-#define DMA_24BIT_MASK	(DMA_nnBIT_MASK)DMA_BIT_MASK(24)
-
 #define DMA_MASK_NONE	0x0ULL
 
 static inline int valid_dma_direction(int dma_direction)
diff --git a/include/linux/dvb/audio.h b/include/linux/dvb/audio.h
index fec66bd..d47bccd 100644
--- a/include/linux/dvb/audio.h
+++ b/include/linux/dvb/audio.h
@@ -67,7 +67,7 @@
 
 
 typedef
-struct audio_karaoke{  /* if Vocal1 or Vocal2 are non-zero, they get mixed  */
+struct audio_karaoke {  /* if Vocal1 or Vocal2 are non-zero, they get mixed  */
 	int vocal1;    /* into left and right t at 70% each */
 	int vocal2;    /* if both, Vocal1 and Vocal2 are non-zero, Vocal1 gets*/
 	int melody;    /* mixed into the left channel and */
diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h
new file mode 100644
index 0000000..2224a8c
--- /dev/null
+++ b/include/linux/ecryptfs.h
@@ -0,0 +1,113 @@
+#ifndef _LINUX_ECRYPTFS_H
+#define _LINUX_ECRYPTFS_H
+
+/* Version verification for shared data structures w/ userspace */
+#define ECRYPTFS_VERSION_MAJOR 0x00
+#define ECRYPTFS_VERSION_MINOR 0x04
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
+/* These flags indicate which features are supported by the kernel
+ * module; userspace tools such as the mount helper read
+ * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
+ * how to behave. */
+#define ECRYPTFS_VERSIONING_PASSPHRASE            0x00000001
+#define ECRYPTFS_VERSIONING_PUBKEY                0x00000002
+#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
+#define ECRYPTFS_VERSIONING_POLICY                0x00000008
+#define ECRYPTFS_VERSIONING_XATTR                 0x00000010
+#define ECRYPTFS_VERSIONING_MULTKEY               0x00000020
+#define ECRYPTFS_VERSIONING_DEVMISC               0x00000040
+#define ECRYPTFS_VERSIONING_HMAC                  0x00000080
+#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION   0x00000100
+#define ECRYPTFS_VERSIONING_GCM                   0x00000200
+#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
+				  | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
+				  | ECRYPTFS_VERSIONING_PUBKEY \
+				  | ECRYPTFS_VERSIONING_XATTR \
+				  | ECRYPTFS_VERSIONING_MULTKEY \
+				  | ECRYPTFS_VERSIONING_DEVMISC \
+				  | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
+#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
+#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
+#define ECRYPTFS_SALT_SIZE 8
+#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
+/* The original signature size is only for what is stored on disk; all
+ * in-memory representations are expanded hex, so it better adapted to
+ * be passed around or referenced on the command line */
+#define ECRYPTFS_SIG_SIZE 8
+#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
+#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
+#define ECRYPTFS_MAX_KEY_BYTES 64
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_FILE_VERSION 0x03
+#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
+
+#define RFC2440_CIPHER_DES3_EDE 0x02
+#define RFC2440_CIPHER_CAST_5 0x03
+#define RFC2440_CIPHER_BLOWFISH 0x04
+#define RFC2440_CIPHER_AES_128 0x07
+#define RFC2440_CIPHER_AES_192 0x08
+#define RFC2440_CIPHER_AES_256 0x09
+#define RFC2440_CIPHER_TWOFISH 0x0a
+#define RFC2440_CIPHER_CAST_6 0x0b
+
+#define RFC2440_CIPHER_RSA 0x01
+
+/**
+ * For convenience, we may need to pass around the encrypted session
+ * key between kernel and userspace because the authentication token
+ * may not be extractable.  For example, the TPM may not release the
+ * private key, instead requiring the encrypted data and returning the
+ * decrypted data.
+ */
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
+	u32 flags;
+	u32 encrypted_key_size;
+	u32 decrypted_key_size;
+	u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+	u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+	u32 password_bytes;
+	s32 hash_algo;
+	u32 hash_iterations;
+	u32 session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+	u32 flags;
+	/* Iterated-hash concatenation of salt and passphrase */
+	u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+	u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+	/* Always in expanded hex */
+	u8 salt[ECRYPTFS_SALT_SIZE];
+};
+
+enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
+
+struct ecryptfs_private_key {
+	u32 key_size;
+	u32 data_len;
+	u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+	char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
+	u8 data[];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+	u16 version; /* 8-bit major and 8-bit minor */
+	u16 token_type;
+#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
+	u32 flags;
+	struct ecryptfs_session_key session_key;
+	u8 reserved[32];
+	union {
+		struct ecryptfs_password password;
+		struct ecryptfs_private_key private_key;
+	} token;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_ECRYPTFS_H */
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 36c6644..4a73257 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -12,7 +12,7 @@
 #ifndef _LINUX_EDAC_H_
 #define _LINUX_EDAC_H_
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/sysdev.h>
 
 #define EDAC_OPSTATE_INVAL	-1
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 2dfa707..53792bf 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -18,6 +18,7 @@
 
 #include <linux/types.h>
 #include <linux/magic.h>
+#include <linux/fs.h>
 
 /*
  * The second extended filesystem constants/structures
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
index 0c473fd..67a803a 100644
--- a/include/linux/ext3_fs.h
+++ b/include/linux/ext3_fs.h
@@ -418,12 +418,11 @@
 #define EXT2_MOUNT_DATA_FLAGS		EXT3_MOUNT_DATA_FLAGS
 #endif
 
-#define ext3_set_bit			__test_and_set_bit_le
+#define ext3_set_bit			__set_bit_le
 #define ext3_set_bit_atomic		ext2_set_bit_atomic
-#define ext3_clear_bit			__test_and_clear_bit_le
+#define ext3_clear_bit			__clear_bit_le
 #define ext3_clear_bit_atomic		ext2_clear_bit_atomic
 #define ext3_test_bit			test_bit_le
-#define ext3_find_first_zero_bit	find_first_zero_bit_le
 #define ext3_find_next_zero_bit		find_next_zero_bit_le
 
 /*
@@ -913,7 +912,7 @@
 extern int ext3_change_inode_journal_flag(struct inode *, int);
 extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *);
 extern int ext3_can_truncate(struct inode *inode);
-extern void ext3_truncate (struct inode *);
+extern void ext3_truncate(struct inode *inode);
 extern void ext3_set_inode_flags(struct inode *);
 extern void ext3_get_inode_flags(struct ext3_inode_info *);
 extern void ext3_set_aops(struct inode *inode);
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h
index 7b64ad4..3ff060a 100644
--- a/include/linux/fault-inject.h
+++ b/include/linux/fault-inject.h
@@ -5,7 +5,7 @@
 
 #include <linux/types.h>
 #include <linux/debugfs.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * For explanation of the elements of this struct, see
@@ -27,23 +27,7 @@
 	unsigned long count;
 
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-
-	struct {
-		struct dentry *dir;
-
-		struct dentry *probability_file;
-		struct dentry *interval_file;
-		struct dentry *times_file;
-		struct dentry *space_file;
-		struct dentry *verbose_file;
-		struct dentry *task_filter_file;
-		struct dentry *stacktrace_depth_file;
-		struct dentry *require_start_file;
-		struct dentry *require_end_file;
-		struct dentry *reject_start_file;
-		struct dentry *reject_end_file;
-	} dentries;
-
+	struct dentry *dir;
 #endif
 };
 
@@ -57,7 +41,6 @@
 
 #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER
 int setup_fault_attr(struct fault_attr *attr, char *str);
-void should_fail_srandom(unsigned long entropy);
 bool should_fail(struct fault_attr *attr, ssize_t size);
 
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
index df7e3cf..82163c4 100644
--- a/include/linux/fdtable.h
+++ b/include/linux/fdtable.h
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/fs.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * The default fd array needs to be at least BITS_PER_LONG,
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 9ee3f9f..741956f 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -9,7 +9,7 @@
 #include <linux/types.h>
 
 #ifdef __KERNEL__
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #endif
 
 /*
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 5e6f427..84ccf8e 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -14,7 +14,7 @@
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 #define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5f523eb..f23bcb7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2310,7 +2310,8 @@
 extern void iget_failed(struct inode *);
 extern void end_writeback(struct inode *);
 extern void __destroy_inode(struct inode *);
-extern struct inode *new_inode(struct super_block *);
+extern struct inode *new_inode_pseudo(struct super_block *sb);
+extern struct inode *new_inode(struct super_block *sb);
 extern void free_inode_nonrcu(struct inode *inode);
 extern int should_remove_suid(struct dentry *);
 extern int file_remove_suid(struct file *);
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 69ad89b..91d0e0a3 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -16,7 +16,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily
diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
index 7472449..0aa0cbd 100644
--- a/include/linux/i2c-omap.h
+++ b/include/linux/i2c-omap.h
@@ -3,6 +3,33 @@
 
 #include <linux/platform_device.h>
 
+/*
+ * Version 2 of the I2C peripheral unit has a different register
+ * layout and extra registers.  The ID register in the V2 peripheral
+ * unit on the OMAP4430 reports the same ID as the V1 peripheral
+ * unit on the OMAP3530, so we must inform the driver which IP
+ * version we know it is running on from platform / cpu-specific
+ * code using these constants in the hwmod class definition.
+ */
+
+#define OMAP_I2C_IP_VERSION_1 1
+#define OMAP_I2C_IP_VERSION_2 2
+
+/* struct omap_i2c_bus_platform_data .flags meanings */
+
+#define OMAP_I2C_FLAG_NO_FIFO			BIT(0)
+#define OMAP_I2C_FLAG_SIMPLE_CLOCK		BIT(1)
+#define OMAP_I2C_FLAG_16BIT_DATA_REG		BIT(2)
+#define OMAP_I2C_FLAG_RESET_REGS_POSTIDLE	BIT(3)
+#define OMAP_I2C_FLAG_APPLY_ERRATA_I207	BIT(4)
+#define OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK	BIT(5)
+#define OMAP_I2C_FLAG_FORCE_19200_INT_CLK	BIT(6)
+/* how the CPU address bus must be translated for I2C unit access */
+#define OMAP_I2C_FLAG_BUS_SHIFT_NONE 0
+#define OMAP_I2C_FLAG_BUS_SHIFT_1		BIT(7)
+#define OMAP_I2C_FLAG_BUS_SHIFT_2		BIT(8)
+#define OMAP_I2C_FLAG_BUS_SHIFT__SHIFT 7
+
 struct omap_i2c_bus_platform_data {
 	u32		clkrate;
 	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index ba4f886..114c0f6 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -657,28 +657,41 @@
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 extern int twl4030_remove_script(u8 flags);
 
-struct twl4030_codec_audio_data {
+struct twl4030_codec_data {
 	unsigned int digimic_delay; /* in ms */
 	unsigned int ramp_delay_value;
 	unsigned int offset_cncl_path;
 	unsigned int check_defaults:1;
 	unsigned int reset_registers:1;
 	unsigned int hs_extmute:1;
+	u16 hs_left_step;
+	u16 hs_right_step;
+	u16 hf_left_step;
+	u16 hf_right_step;
 	void (*set_hs_extmute)(int mute);
 };
 
-struct twl4030_codec_vibra_data {
+struct twl4030_vibra_data {
 	unsigned int	coexist;
+
+	/* twl6040 */
+	unsigned int vibldrv_res;	/* left driver resistance */
+	unsigned int vibrdrv_res;	/* right driver resistance */
+	unsigned int viblmotor_res;	/* left motor resistance */
+	unsigned int vibrmotor_res;	/* right motor resistance */
+	int vddvibl_uV;			/* VDDVIBL volt, set 0 for fixed reg */
+	int vddvibr_uV;			/* VDDVIBR volt, set 0 for fixed reg */
 };
 
-struct twl4030_codec_data {
+struct twl4030_audio_data {
 	unsigned int	audio_mclk;
-	struct twl4030_codec_audio_data		*audio;
-	struct twl4030_codec_vibra_data		*vibra;
+	struct twl4030_codec_data *codec;
+	struct twl4030_vibra_data *vibra;
 
 	/* twl6040 */
 	int audpwron_gpio;	/* audio power-on gpio */
 	int naudint_irq;	/* audio interrupt */
+	unsigned int irq_base;
 };
 
 struct twl4030_platform_data {
@@ -690,7 +703,7 @@
 	struct twl4030_keypad_data		*keypad;
 	struct twl4030_usb_data			*usb;
 	struct twl4030_power_data		*power;
-	struct twl4030_codec_data		*codec;
+	struct twl4030_audio_data		*audio;
 
 	/* Common LDO regulators for TWL4030/TWL6030 */
 	struct regulator_init_data		*vdac;
diff --git a/include/linux/if.h b/include/linux/if.h
index 3bc63e6..03489ca 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -76,6 +76,8 @@
 #define IFF_BRIDGE_PORT	0x4000		/* device used as bridge port */
 #define IFF_OVS_DATAPATH	0x8000	/* device used as Open vSwitch
 					 * datapath port */
+#define IFF_TX_SKB_SHARING	0x10000	/* The interface supports sharing
+					 * skbs on transmit */
 
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
diff --git a/include/linux/input.h b/include/linux/input.h
index 771d6d8..068784e 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -119,9 +119,9 @@
 #define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
 #define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */
 
-#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
-#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)	/* get abs value/limits */
-#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)	/* set abs value/limits */
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + (ev), len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + (abs), struct input_absinfo)	/* get abs value/limits */
+#define EVIOCSABS(abs)		_IOW('E', 0xc0 + (abs), struct input_absinfo)	/* set abs value/limits */
 
 #define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
 #define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
new file mode 100644
index 0000000..f6bac89
--- /dev/null
+++ b/include/linux/input/kxtj9.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef __KXTJ9_H__
+#define __KXTJ9_H__
+
+#define KXTJ9_I2C_ADDR		0x0F
+
+struct kxtj9_platform_data {
+	unsigned int min_interval;	/* minimum poll interval (in milli-seconds) */
+
+	/*
+	 * By default, x is axis 0, y is axis 1, z is axis 2; these can be
+	 * changed to account for sensor orientation within the host device.
+	 */
+	u8 axis_map_x;
+	u8 axis_map_y;
+	u8 axis_map_z;
+
+	/*
+	 * Each axis can be negated to account for sensor orientation within
+	 * the host device.
+	 */
+	bool negate_x;
+	bool negate_y;
+	bool negate_z;
+
+	/* CTRL_REG1: set resolution, g-range, data ready enable */
+	/* Output resolution: 8-bit valid or 12-bit valid */
+	#define RES_8BIT		0
+	#define RES_12BIT		(1 << 6)
+	u8 res_12bit;
+	/* Output g-range: +/-2g, 4g, or 8g */
+	#define KXTJ9_G_2G		0
+	#define KXTJ9_G_4G		(1 << 3)
+	#define KXTJ9_G_8G		(1 << 4)
+	u8 g_range;
+
+	/* DATA_CTRL_REG: controls the output data rate of the part */
+	#define ODR12_5F		0
+	#define ODR25F			1
+	#define ODR50F			2
+	#define ODR100F			3
+	#define ODR200F			4
+	#define ODR400F			5
+	#define ODR800F			6
+	u8 data_odr_init;
+
+	int (*init)(void);
+	void (*exit)(void);
+	int (*power_on)(void);
+	int (*power_off)(void);
+};
+#endif  /* __KXTJ9_H__ */
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index f6efed0..a103732 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -17,7 +17,7 @@
 #include <linux/kref.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/ptrace.h>
 #include <asm/system.h>
 #include <trace/events/irq.h>
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index a6d1655..8a297a5 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -44,6 +44,11 @@
 	size_t		shm_ctlall;
 	int		shm_ctlmni;
 	int		shm_tot;
+	/*
+	 * Defines whether IPC_RMID is forced for _all_ shm segments regardless
+	 * of shmctl()
+	 */
+	int		shm_rmid_forced;
 
 	struct notifier_block ipcns_nb;
 
@@ -72,6 +77,7 @@
 extern int cond_register_ipcns_notifier(struct ipc_namespace *);
 extern void unregister_ipcns_notifier(struct ipc_namespace *);
 extern int ipcns_notify(unsigned long);
+extern void shm_destroy_orphaned(struct ipc_namespace *ns);
 #else /* CONFIG_SYSVIPC */
 static inline int register_ipcns_notifier(struct ipc_namespace *ns)
 { return 0; }
@@ -79,6 +85,7 @@
 { return 0; }
 static inline void unregister_ipcns_notifier(struct ipc_namespace *ns) { }
 static inline int ipcns_notify(unsigned long l) { return 0; }
+static inline void shm_destroy_orphaned(struct ipc_namespace *ns) {}
 #endif /* CONFIG_SYSVIPC */
 
 #ifdef CONFIG_POSIX_MQUEUE
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 5f69504..87a06f3 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -108,14 +108,18 @@
 };
 
 struct msi_desc;
+struct irq_domain;
 
 /**
  * struct irq_data - per irq and irq chip data passed down to chip functions
  * @irq:		interrupt number
+ * @hwirq:		hardware interrupt number, local to the interrupt domain
  * @node:		node index useful for balancing
  * @state_use_accessors: status information for irq chip functions.
  *			Use accessor functions to deal with it
  * @chip:		low level interrupt hardware access
+ * @domain:		Interrupt translation domain; responsible for mapping
+ *			between hwirq number and linux irq number.
  * @handler_data:	per-IRQ data for the irq_chip methods
  * @chip_data:		platform-specific per-chip private data for the chip
  *			methods, to allow shared chip implementations
@@ -128,9 +132,11 @@
  */
 struct irq_data {
 	unsigned int		irq;
+	unsigned long		hwirq;
 	unsigned int		node;
 	unsigned int		state_use_accessors;
 	struct irq_chip		*chip;
+	struct irq_domain	*domain;
 	void			*handler_data;
 	void			*chip_data;
 	struct msi_desc		*msi_desc;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
new file mode 100644
index 0000000..e807ad6
--- /dev/null
+++ b/include/linux/irqdomain.h
@@ -0,0 +1,91 @@
+/*
+ * irq_domain - IRQ translation domains
+ *
+ * Translation infrastructure between hw and linux irq numbers.  This is
+ * helpful for interrupt controllers to implement mapping between hardware
+ * irq numbers and the Linux irq number space.
+ *
+ * irq_domains also have a hook for translating device tree interrupt
+ * representation into a hardware irq number that can be mapped back to a
+ * Linux irq number without any extra platform support code.
+ *
+ * irq_domain is expected to be embedded in an interrupt controller's private
+ * data structure.
+ */
+#ifndef _LINUX_IRQDOMAIN_H
+#define _LINUX_IRQDOMAIN_H
+
+#include <linux/irq.h>
+#include <linux/mod_devicetable.h>
+
+#ifdef CONFIG_IRQ_DOMAIN
+struct device_node;
+struct irq_domain;
+
+/**
+ * struct irq_domain_ops - Methods for irq_domain objects
+ * @to_irq: (optional) given a local hardware irq number, return the linux
+ *          irq number.  If to_irq is not implemented, then the irq_domain
+ *          will use this translation: irq = (domain->irq_base + hwirq)
+ * @dt_translate: Given a device tree node and interrupt specifier, decode
+ *                the hardware irq number and linux irq type value.
+ */
+struct irq_domain_ops {
+	unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
+
+#ifdef CONFIG_OF
+	int (*dt_translate)(struct irq_domain *d, struct device_node *node,
+			    const u32 *intspec, unsigned int intsize,
+			    unsigned long *out_hwirq, unsigned int *out_type);
+#endif /* CONFIG_OF */
+};
+
+/**
+ * struct irq_domain - Hardware interrupt number translation object
+ * @list: Element in global irq_domain list.
+ * @irq_base: Start of irq_desc range assigned to the irq_domain.  The creator
+ *            of the irq_domain is responsible for allocating the array of
+ *            irq_desc structures.
+ * @nr_irq: Number of irqs managed by the irq domain
+ * @ops: pointer to irq_domain methods
+ * @priv: private data pointer for use by owner.  Not touched by irq_domain
+ *        core code.
+ * @of_node: (optional) Pointer to device tree nodes associated with the
+ *           irq_domain.  Used when decoding device tree interrupt specifiers.
+ */
+struct irq_domain {
+	struct list_head list;
+	unsigned int irq_base;
+	unsigned int nr_irq;
+	const struct irq_domain_ops *ops;
+	void *priv;
+	struct device_node *of_node;
+};
+
+/**
+ * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
+ *
+ * Returns the linux irq number associated with a hardware irq.  By default,
+ * the mapping is irq == domain->irq_base + hwirq, but this mapping can
+ * be overridden if the irq_domain implements a .to_irq() hook.
+ */
+static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
+					     unsigned long hwirq)
+{
+	return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq;
+}
+
+extern void irq_domain_add(struct irq_domain *domain);
+extern void irq_domain_del(struct irq_domain *domain);
+#endif /* CONFIG_IRQ_DOMAIN */
+
+#if defined(CONFIG_IRQ_DOMAIN) && defined(CONFIG_OF_IRQ)
+extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
+extern void irq_domain_generate_simple(const struct of_device_id *match,
+					u64 phys_base, unsigned int irq_start);
+#else /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
+static inline void irq_domain_generate_simple(const struct of_device_id *match,
+					u64 phys_base, unsigned int irq_start) { }
+#endif /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
+
+#endif /* _LINUX_IRQDOMAIN_H */
diff --git a/include/linux/jbd.h b/include/linux/jbd.h
index e069650..e6a5e34 100644
--- a/include/linux/jbd.h
+++ b/include/linux/jbd.h
@@ -940,7 +940,6 @@
  */
 struct journal_head *journal_add_journal_head(struct buffer_head *bh);
 struct journal_head *journal_grab_journal_head(struct buffer_head *bh);
-void journal_remove_journal_head(struct buffer_head *bh);
 void journal_put_journal_head(struct journal_head *jh);
 
 /*
diff --git a/include/linux/journal-head.h b/include/linux/journal-head.h
index 44e95d0..423cb6d 100644
--- a/include/linux/journal-head.h
+++ b/include/linux/journal-head.h
@@ -45,7 +45,7 @@
 	 * has been cowed
 	 * [jbd_lock_bh_state()]
 	 */
-	unsigned b_cow_tid;
+	tid_t b_cow_tid;
 
 	/*
 	 * Copy of the buffer data frozen for writing to the log.
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 83e745f..66f23dc 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -54,7 +54,7 @@
 
 #else
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define JUMP_LABEL_INIT {ATOMIC_INIT(0)}
 
diff --git a/include/linux/kconfig.h b/include/linux/kconfig.h
new file mode 100644
index 0000000..067eda0
--- /dev/null
+++ b/include/linux/kconfig.h
@@ -0,0 +1,32 @@
+#ifndef __LINUX_KCONFIG_H
+#define __LINUX_KCONFIG_H
+
+#include <generated/autoconf.h>
+
+/*
+ * Helper macros to use CONFIG_ options in C expressions. Note that
+ * these only work with boolean and tristate options.
+ */
+
+/*
+ * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
+ * 0 otherwise.
+ *
+ */
+#define IS_ENABLED(option) \
+	(__enabled_ ## option || __enabled_ ## option ## _MODULE)
+
+/*
+ * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0
+ * otherwise. For boolean options, this is equivalent to
+ * IS_ENABLED(CONFIG_FOO).
+ */
+#define IS_BUILTIN(option) __enabled_ ## option
+
+/*
+ * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0
+ * otherwise.
+ */
+#define IS_MODULE(option) __enabled_ ## option ## _MODULE
+
+#endif /* __LINUX_KCONFIG_H */
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index aadff7c..529d9a0 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -16,7 +16,7 @@
 #ifdef	CONFIG_KGDB_KDB
 #include <linux/init.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define KDB_POLL_FUNC_MAX	5
 extern int kdb_poll_idx;
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 9a43ad7..46ac9a5 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -56,6 +56,14 @@
 
 #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define DIV_ROUND_UP_ULL(ll,d) \
+	({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; })
+
+#if BITS_PER_LONG == 32
+# define DIV_ROUND_UP_SECTOR_T(ll,d) DIV_ROUND_UP_ULL(ll, d)
+#else
+# define DIV_ROUND_UP_SECTOR_T(ll,d) DIV_ROUND_UP(ll,d)
+#endif
 
 /* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */
 #define roundup(x, y) (					\
diff --git a/include/linux/key.h b/include/linux/key.h
index 6ea4eeb..183a6af 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -21,7 +21,7 @@
 #include <linux/rcupdate.h>
 #include <linux/sysctl.h>
 #include <linux/rwsem.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef __KERNEL__
 
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index 10ca03d..fa39183 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -16,7 +16,7 @@
 #include <linux/serial_8250.h>
 #include <linux/linkage.h>
 #include <linux/init.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #ifdef CONFIG_HAVE_ARCH_KGDB
 #include <asm/kgdb.h>
 #endif
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 9229b64..668729c 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -25,7 +25,7 @@
 #include <linux/kobject_ns.h>
 #include <linux/kernel.h>
 #include <linux/wait.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define UEVENT_HELPER_PATH_LEN		256
 #define UEVENT_NUM_ENVP			32	/* number of env pointers */
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 50940da..b966007 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -39,6 +39,16 @@
 					struct mem_cgroup *mem_cont,
 					int active, int file);
 
+struct memcg_scanrecord {
+	struct mem_cgroup *mem; /* scanend memory cgroup */
+	struct mem_cgroup *root; /* scan target hierarchy root */
+	int context;		/* scanning context (see memcontrol.c) */
+	unsigned long nr_scanned[2]; /* the number of scanned pages */
+	unsigned long nr_rotated[2]; /* the number of rotated pages */
+	unsigned long nr_freed[2]; /* the number of freed pages */
+	unsigned long elapsed; /* nsec of time elapsed while scanning */
+};
+
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR
 /*
  * All "charge" functions with gfp_mask should use GFP_KERNEL or
@@ -111,8 +121,7 @@
 int mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg);
 int mem_cgroup_select_victim_node(struct mem_cgroup *memcg);
 unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg,
-						struct zone *zone,
-						enum lru_list lru);
+					int nid, int zid, unsigned int lrumask);
 struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
 						      struct zone *zone);
 struct zone_reclaim_stat*
@@ -120,6 +129,15 @@
 extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
 					struct task_struct *p);
 
+extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
+						  gfp_t gfp_mask, bool noswap,
+						  struct memcg_scanrecord *rec);
+extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
+						gfp_t gfp_mask, bool noswap,
+						struct zone *zone,
+						struct memcg_scanrecord *rec,
+						unsigned long *nr_scanned);
+
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
 extern int do_swap_account;
 #endif
@@ -313,8 +331,8 @@
 }
 
 static inline unsigned long
-mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, struct zone *zone,
-			     enum lru_list lru)
+mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, int nid, int zid,
+				unsigned int lru_mask)
 {
 	return 0;
 }
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-audio.h
similarity index 95%
rename from include/linux/mfd/twl4030-codec.h
rename to include/linux/mfd/twl4030-audio.h
index 5cc16bb..3d22b72 100644
--- a/include/linux/mfd/twl4030-codec.h
+++ b/include/linux/mfd/twl4030-audio.h
@@ -1,5 +1,5 @@
 /*
- * MFD driver for twl4030 codec submodule
+ * MFD driver for twl4030 audio submodule
  *
  * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
  *
@@ -259,14 +259,14 @@
 #define TWL4030_VIBRA_DIR_SEL		0x20
 
 /* TWL4030 codec resource IDs */
-enum twl4030_codec_res {
-	TWL4030_CODEC_RES_POWER = 0,
-	TWL4030_CODEC_RES_APLL,
-	TWL4030_CODEC_RES_MAX,
+enum twl4030_audio_res {
+	TWL4030_AUDIO_RES_POWER = 0,
+	TWL4030_AUDIO_RES_APLL,
+	TWL4030_AUDIO_RES_MAX,
 };
 
-int twl4030_codec_disable_resource(enum twl4030_codec_res id);
-int twl4030_codec_enable_resource(enum twl4030_codec_res id);
-unsigned int twl4030_codec_get_mclk(void);
+int twl4030_audio_disable_resource(enum twl4030_audio_res id);
+int twl4030_audio_enable_resource(enum twl4030_audio_res id);
+unsigned int twl4030_audio_get_mclk(void);
 
 #endif	/* End of __TWL4030_CODEC_H__ */
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h
new file mode 100644
index 0000000..4c806f6
--- /dev/null
+++ b/include/linux/mfd/twl6040.h
@@ -0,0 +1,228 @@
+/*
+ * MFD driver for twl6040
+ *
+ * Authors:     Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ *              Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright:   (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL6040_CODEC_H__
+#define __TWL6040_CODEC_H__
+
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+
+#define TWL6040_REG_ASICID		0x01
+#define TWL6040_REG_ASICREV		0x02
+#define TWL6040_REG_INTID		0x03
+#define TWL6040_REG_INTMR		0x04
+#define TWL6040_REG_NCPCTL		0x05
+#define TWL6040_REG_LDOCTL		0x06
+#define TWL6040_REG_HPPLLCTL		0x07
+#define TWL6040_REG_LPPLLCTL		0x08
+#define TWL6040_REG_LPPLLDIV		0x09
+#define TWL6040_REG_AMICBCTL		0x0A
+#define TWL6040_REG_DMICBCTL		0x0B
+#define TWL6040_REG_MICLCTL		0x0C
+#define TWL6040_REG_MICRCTL		0x0D
+#define TWL6040_REG_MICGAIN		0x0E
+#define TWL6040_REG_LINEGAIN		0x0F
+#define TWL6040_REG_HSLCTL		0x10
+#define TWL6040_REG_HSRCTL		0x11
+#define TWL6040_REG_HSGAIN		0x12
+#define TWL6040_REG_EARCTL		0x13
+#define TWL6040_REG_HFLCTL		0x14
+#define TWL6040_REG_HFLGAIN		0x15
+#define TWL6040_REG_HFRCTL		0x16
+#define TWL6040_REG_HFRGAIN		0x17
+#define TWL6040_REG_VIBCTLL		0x18
+#define TWL6040_REG_VIBDATL		0x19
+#define TWL6040_REG_VIBCTLR		0x1A
+#define TWL6040_REG_VIBDATR		0x1B
+#define TWL6040_REG_HKCTL1		0x1C
+#define TWL6040_REG_HKCTL2		0x1D
+#define TWL6040_REG_GPOCTL		0x1E
+#define TWL6040_REG_ALB			0x1F
+#define TWL6040_REG_DLB			0x20
+#define TWL6040_REG_TRIM1		0x28
+#define TWL6040_REG_TRIM2		0x29
+#define TWL6040_REG_TRIM3		0x2A
+#define TWL6040_REG_HSOTRIM		0x2B
+#define TWL6040_REG_HFOTRIM		0x2C
+#define TWL6040_REG_ACCCTL		0x2D
+#define TWL6040_REG_STATUS		0x2E
+
+#define TWL6040_CACHEREGNUM		(TWL6040_REG_STATUS + 1)
+
+#define TWL6040_VIOREGNUM		18
+#define TWL6040_VDDREGNUM		21
+
+/* INTID (0x03) fields */
+
+#define TWL6040_THINT			0x01
+#define TWL6040_PLUGINT			0x02
+#define TWL6040_UNPLUGINT		0x04
+#define TWL6040_HOOKINT			0x08
+#define TWL6040_HFINT			0x10
+#define TWL6040_VIBINT			0x20
+#define TWL6040_READYINT		0x40
+
+/* INTMR (0x04) fields */
+
+#define TWL6040_THMSK			0x01
+#define TWL6040_PLUGMSK			0x02
+#define TWL6040_HOOKMSK			0x08
+#define TWL6040_HFMSK			0x10
+#define TWL6040_VIBMSK			0x20
+#define TWL6040_READYMSK		0x40
+#define TWL6040_ALLINT_MSK		0x7B
+
+/* NCPCTL (0x05) fields */
+
+#define TWL6040_NCPENA			0x01
+#define TWL6040_NCPOPEN			0x40
+
+/* LDOCTL (0x06) fields */
+
+#define TWL6040_LSLDOENA		0x01
+#define TWL6040_HSLDOENA		0x04
+#define TWL6040_REFENA			0x40
+#define TWL6040_OSCENA			0x80
+
+/* HPPLLCTL (0x07) fields */
+
+#define TWL6040_HPLLENA			0x01
+#define TWL6040_HPLLRST			0x02
+#define TWL6040_HPLLBP			0x04
+#define TWL6040_HPLLSQRENA		0x08
+#define TWL6040_MCLK_12000KHZ		(0 << 5)
+#define TWL6040_MCLK_19200KHZ		(1 << 5)
+#define TWL6040_MCLK_26000KHZ		(2 << 5)
+#define TWL6040_MCLK_38400KHZ		(3 << 5)
+#define TWL6040_MCLK_MSK		0x60
+
+/* LPPLLCTL (0x08) fields */
+
+#define TWL6040_LPLLENA			0x01
+#define TWL6040_LPLLRST			0x02
+#define TWL6040_LPLLSEL			0x04
+#define TWL6040_LPLLFIN			0x08
+#define TWL6040_HPLLSEL			0x10
+
+/* HSLCTL (0x10) fields */
+
+#define TWL6040_HSDACMODEL		0x02
+#define TWL6040_HSDRVMODEL		0x08
+
+/* HSRCTL (0x11) fields */
+
+#define TWL6040_HSDACMODER		0x02
+#define TWL6040_HSDRVMODER		0x08
+
+/* VIBCTLL (0x18) fields */
+
+#define TWL6040_VIBENAL			0x01
+#define TWL6040_VIBCTRLL		0x04
+#define TWL6040_VIBCTRLLP		0x08
+#define TWL6040_VIBCTRLLN		0x10
+
+/* VIBDATL (0x19) fields */
+
+#define TWL6040_VIBDAT_MAX		0x64
+
+/* VIBCTLR (0x1A) fields */
+
+#define TWL6040_VIBENAR			0x01
+#define TWL6040_VIBCTRLR		0x04
+#define TWL6040_VIBCTRLRP		0x08
+#define TWL6040_VIBCTRLRN		0x10
+
+/* GPOCTL (0x1E) fields */
+
+#define TWL6040_GPO1			0x01
+#define TWL6040_GPO2			0x02
+#define TWL6040_GPO3			0x03
+
+/* ACCCTL (0x2D) fields */
+
+#define TWL6040_I2CSEL			0x01
+#define TWL6040_RESETSPLIT		0x04
+#define TWL6040_INTCLRMODE		0x08
+
+/* STATUS (0x2E) fields */
+
+#define TWL6040_PLUGCOMP		0x02
+#define TWL6040_VIBLOCDET		0x10
+#define TWL6040_VIBROCDET		0x20
+#define TWL6040_TSHUTDET                0x40
+
+#define TWL6040_CELLS			2
+
+#define TWL6040_REV_ES1_0		0x00
+#define TWL6040_REV_ES1_1		0x01
+#define TWL6040_REV_ES1_2		0x02
+
+#define TWL6040_IRQ_TH			0
+#define TWL6040_IRQ_PLUG		1
+#define TWL6040_IRQ_HOOK		2
+#define TWL6040_IRQ_HF			3
+#define TWL6040_IRQ_VIB			4
+#define TWL6040_IRQ_READY		5
+
+/* PLL selection */
+#define TWL6040_SYSCLK_SEL_LPPLL	0
+#define TWL6040_SYSCLK_SEL_HPPLL	1
+
+struct twl6040 {
+	struct device *dev;
+	struct mutex mutex;
+	struct mutex io_mutex;
+	struct mutex irq_mutex;
+	struct mfd_cell cells[TWL6040_CELLS];
+	struct completion ready;
+
+	int audpwron;
+	int power_count;
+	int rev;
+
+	int pll;
+	unsigned int sysclk;
+
+	unsigned int irq;
+	unsigned int irq_base;
+	u8 irq_masks_cur;
+	u8 irq_masks_cache;
+};
+
+int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg);
+int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg,
+		      u8 val);
+int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg,
+		     u8 mask);
+int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg,
+		       u8 mask);
+int twl6040_power(struct twl6040 *twl6040, int on);
+int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
+		    unsigned int freq_in, unsigned int freq_out);
+int twl6040_get_pll(struct twl6040 *twl6040);
+unsigned int twl6040_get_sysclk(struct twl6040 *twl6040);
+int twl6040_irq_init(struct twl6040 *twl6040);
+void twl6040_irq_exit(struct twl6040 *twl6040);
+
+#endif  /* End of __TWL6040_CODEC_H__ */
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 387329e..53ef894 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -37,7 +37,7 @@
 #include <linux/completion.h>
 #include <linux/radix-tree.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define MAX_MSIX_P_PORT		17
 #define MAX_MSIX		64
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 027935c..774b895 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -30,23 +30,61 @@
  * moment. Note that we have no way to track which tasks are using
  * a page, though if it is a pagecache page, rmap structures can tell us
  * who is mapping it.
+ *
+ * The objects in struct page are organized in double word blocks in
+ * order to allows us to use atomic double word operations on portions
+ * of struct page. That is currently only used by slub but the arrangement
+ * allows the use of atomic double word operations on the flags/mapping
+ * and lru list pointers also.
  */
 struct page {
+	/* First double word block */
 	unsigned long flags;		/* Atomic flags, some possibly
 					 * updated asynchronously */
-	atomic_t _count;		/* Usage count, see below. */
-	union {
-		atomic_t _mapcount;	/* Count of ptes mapped in mms,
-					 * to show when page is mapped
-					 * & limit reverse map searches.
+	struct address_space *mapping;	/* If low bit clear, points to
+					 * inode address_space, or NULL.
+					 * If page mapped as anonymous
+					 * memory, low bit is set, and
+					 * it points to anon_vma object:
+					 * see PAGE_MAPPING_ANON below.
 					 */
-		struct {		/* SLUB */
-			u16 inuse;
-			u16 objects;
+	/* Second double word */
+	struct {
+		union {
+			pgoff_t index;		/* Our offset within mapping. */
+			void *freelist;		/* slub first free object */
+		};
+
+		union {
+			/* Used for cmpxchg_double in slub */
+			unsigned long counters;
+
+			struct {
+
+				union {
+					atomic_t _mapcount;	/* Count of ptes mapped in mms,
+							 * to show when page is mapped
+							 * & limit reverse map searches.
+							 */
+
+					struct {
+						unsigned inuse:16;
+						unsigned objects:15;
+						unsigned frozen:1;
+					};
+				};
+				atomic_t _count;		/* Usage count, see below. */
+			};
 		};
 	};
+
+	/* Third double word block */
+	struct list_head lru;		/* Pageout list, eg. active_list
+					 * protected by zone->lru_lock !
+					 */
+
+	/* Remainder is not double word aligned */
 	union {
-	    struct {
 		unsigned long private;		/* Mapping-private opaque data:
 					 	 * usually used for buffer_heads
 						 * if PagePrivate set; used for
@@ -54,27 +92,13 @@
 						 * indicates order in the buddy
 						 * system if PG_buddy is set.
 						 */
-		struct address_space *mapping;	/* If low bit clear, points to
-						 * inode address_space, or NULL.
-						 * If page mapped as anonymous
-						 * memory, low bit is set, and
-						 * it points to anon_vma object:
-						 * see PAGE_MAPPING_ANON below.
-						 */
-	    };
 #if USE_SPLIT_PTLOCKS
-	    spinlock_t ptl;
+		spinlock_t ptl;
 #endif
-	    struct kmem_cache *slab;	/* SLUB: Pointer to slab */
-	    struct page *first_page;	/* Compound tail pages */
+		struct kmem_cache *slab;	/* SLUB: Pointer to slab */
+		struct page *first_page;	/* Compound tail pages */
 	};
-	union {
-		pgoff_t index;		/* Our offset within mapping. */
-		void *freelist;		/* SLUB: freelist req. slab lock */
-	};
-	struct list_head lru;		/* Pageout list, eg. active_list
-					 * protected by zone->lru_lock !
-					 */
+
 	/*
 	 * On machines where all RAM is mapped into kernel address space,
 	 * we can simply calculate the virtual address. On machines with
@@ -100,7 +124,16 @@
 	 */
 	void *shadow;
 #endif
-};
+}
+/*
+ * If another subsystem starts using the double word pairing for atomic
+ * operations on struct page then it must change the #if to ensure
+ * proper alignment of the page struct.
+ */
+#if defined(CONFIG_SLUB) && defined(CONFIG_CMPXCHG_LOCAL)
+	__attribute__((__aligned__(2*sizeof(unsigned long))))
+#endif
+;
 
 typedef unsigned long __nocast vm_flags_t;
 
diff --git a/include/linux/mman.h b/include/linux/mman.h
index 9872d6c..8b74e9b 100644
--- a/include/linux/mman.h
+++ b/include/linux/mman.h
@@ -14,7 +14,7 @@
 #include <linux/mm.h>
 #include <linux/percpu_counter.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern int sysctl_overcommit_memory;
 extern int sysctl_overcommit_ratio;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 9f7c3eb..be1ac8d 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -16,7 +16,7 @@
 #include <linux/nodemask.h>
 #include <linux/pageblock-flags.h>
 #include <generated/bounds.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 
 /* Free memory management - zoned buddy allocator.  */
@@ -158,6 +158,12 @@
 	return (l == LRU_UNEVICTABLE);
 }
 
+/* Mask used at gathering information at once (see memcontrol.c) */
+#define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
+#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
+#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
+#define LRU_ALL	     ((1 << NR_LRU_LISTS) - 1)
+
 enum zone_watermarks {
 	WMARK_MIN,
 	WMARK_LOW,
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 604f122..33fe53d 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -14,7 +14,7 @@
 #include <linux/nodemask.h>
 #include <linux/spinlock.h>
 #include <linux/seqlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct super_block;
 struct vfsmount;
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 7f87217..9121595 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -15,7 +15,7 @@
 #include <linux/linkage.h>
 #include <linux/lockdep.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * Simple, straightforward mutexes with strict semantics:
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ea6f4aa..ddee79b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -34,7 +34,7 @@
 #include <linux/pm_qos_params.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/cache.h>
 #include <asm/byteorder.h>
 
@@ -1132,7 +1132,7 @@
 	spinlock_t		addr_list_lock;
 	struct netdev_hw_addr_list	uc;	/* Unicast mac addresses */
 	struct netdev_hw_addr_list	mc;	/* Multicast mac addresses */
-	int			uc_promisc;
+	bool			uc_promisc;
 	unsigned int		promiscuity;
 	unsigned int		allmulti;
 
@@ -1679,9 +1679,12 @@
 static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
 					unsigned int offset)
 {
+	if (!pskb_may_pull(skb, hlen))
+		return NULL;
+
 	NAPI_GRO_CB(skb)->frag0 = NULL;
 	NAPI_GRO_CB(skb)->frag0_len = 0;
-	return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
+	return skb->data + offset;
 }
 
 static inline void *skb_gro_mac_header(struct sk_buff *skb)
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 504b289..a3c4bc8 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -563,6 +563,9 @@
 	NFSPROC4_CLNT_GETDEVICEINFO,
 	NFSPROC4_CLNT_LAYOUTCOMMIT,
 	NFSPROC4_CLNT_LAYOUTRETURN,
+	NFSPROC4_CLNT_SECINFO_NO_NAME,
+	NFSPROC4_CLNT_TEST_STATEID,
+	NFSPROC4_CLNT_FREE_STATEID,
 };
 
 /* nfs41 types */
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 87694ca..50a661f 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -7,7 +7,7 @@
 #include <linux/nfs_xdr.h>
 #include <linux/sunrpc/xprt.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct nfs4_session;
 struct nfs_iostats;
@@ -16,6 +16,7 @@
 struct nfs4_sequence_res;
 struct nfs_server;
 struct nfs4_minor_version_ops;
+struct server_scope;
 
 /*
  * The nfs_client identifies our client state to the server.
@@ -77,12 +78,13 @@
 	/* The flags used for obtaining the clientid during EXCHANGE_ID */
 	u32			cl_exchange_flags;
 	struct nfs4_session	*cl_session; 	/* sharred session */
-	struct list_head	cl_layouts;
 #endif /* CONFIG_NFS_V4 */
 
 #ifdef CONFIG_NFS_FSCACHE
 	struct fscache_cookie	*fscache;	/* client index cache cookie */
 #endif
+
+	struct server_scope	*server_scope;	/* from exchange_id */
 };
 
 /*
@@ -149,6 +151,7 @@
 	struct rb_root		openowner_id;
 	struct rb_root		lockowner_id;
 #endif
+	struct list_head	layouts;
 	struct list_head	delegations;
 	void (*destroy)(struct nfs_server *);
 
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 25311b3..e2791a2 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -55,20 +55,28 @@
 	struct nfs_writeverf	wb_verf;	/* Commit cookie */
 };
 
+struct nfs_pageio_descriptor;
+struct nfs_pageio_ops {
+	void	(*pg_init)(struct nfs_pageio_descriptor *, struct nfs_page *);
+	bool	(*pg_test)(struct nfs_pageio_descriptor *, struct nfs_page *, struct nfs_page *);
+	int	(*pg_doio)(struct nfs_pageio_descriptor *);
+};
+
 struct nfs_pageio_descriptor {
 	struct list_head	pg_list;
 	unsigned long		pg_bytes_written;
 	size_t			pg_count;
 	size_t			pg_bsize;
 	unsigned int		pg_base;
-	char			pg_moreio;
+	unsigned char		pg_moreio : 1,
+				pg_recoalesce : 1;
 
 	struct inode		*pg_inode;
-	int			(*pg_doio)(struct nfs_pageio_descriptor *);
+	const struct nfs_pageio_ops *pg_ops;
 	int 			pg_ioflags;
 	int			pg_error;
+	const struct rpc_call_ops *pg_rpc_callops;
 	struct pnfs_layout_segment *pg_lseg;
-	bool			(*pg_test)(struct nfs_pageio_descriptor *, struct nfs_page *, struct nfs_page *);
 };
 
 #define NFS_WBACK_BUSY(req)	(test_bit(PG_BUSY,&(req)->wb_flags))
@@ -85,7 +93,7 @@
 			  pgoff_t idx_start, unsigned int npages, int tag);
 extern	void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
 			     struct inode *inode,
-			     int (*doio)(struct nfs_pageio_descriptor *desc),
+			     const struct nfs_pageio_ops *pg_ops,
 			     size_t bsize,
 			     int how);
 extern	int nfs_pageio_add_request(struct nfs_pageio_descriptor *,
@@ -100,7 +108,6 @@
 extern	int nfs_set_page_tag_locked(struct nfs_page *req);
 extern  void nfs_clear_page_tag_locked(struct nfs_page *req);
 
-
 /*
  * Lock the page of an asynchronous request without getting a new reference
  */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 00848d8..5b11595 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -269,9 +269,10 @@
 };
 
 struct nfs4_layoutreturn_args {
-	__u32   layout_type;
+	struct pnfs_layout_hdr *layout;
 	struct inode *inode;
 	nfs4_stateid stateid;
+	__u32   layout_type;
 	struct nfs4_sequence_args seq_args;
 };
 
@@ -1060,6 +1061,7 @@
 struct nfs41_exchange_id_res {
 	struct nfs_client		*client;
 	u32				flags;
+	struct server_scope		*server_scope;
 };
 
 struct nfs41_create_session_args {
@@ -1083,6 +1085,34 @@
 struct nfs41_reclaim_complete_res {
 	struct nfs4_sequence_res	seq_res;
 };
+
+#define SECINFO_STYLE_CURRENT_FH 0
+#define SECINFO_STYLE_PARENT 1
+struct nfs41_secinfo_no_name_args {
+	int				style;
+	struct nfs4_sequence_args	seq_args;
+};
+
+struct nfs41_test_stateid_args {
+	nfs4_stateid			*stateid;
+	struct nfs4_sequence_args	seq_args;
+};
+
+struct nfs41_test_stateid_res {
+	unsigned int			status;
+	struct nfs4_sequence_res	seq_res;
+};
+
+struct nfs41_free_stateid_args {
+	nfs4_stateid			*stateid;
+	struct nfs4_sequence_args	seq_args;
+};
+
+struct nfs41_free_stateid_res {
+	unsigned int			status;
+	struct nfs4_sequence_res	seq_res;
+};
+
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs_page;
@@ -1096,6 +1126,7 @@
 	struct rpc_cred		*cred;
 	struct nfs_fattr	fattr;	/* fattr storage */
 	struct list_head	pages;	/* Coalesced read requests */
+	struct list_head	list;	/* lists of struct nfs_read_data */
 	struct nfs_page		*req;	/* multi ops per nfs_page */
 	struct page		**pagevec;
 	unsigned int		npages;	/* Max length of pagevec */
@@ -1119,6 +1150,7 @@
 	struct nfs_fattr	fattr;
 	struct nfs_writeverf	verf;
 	struct list_head	pages;		/* Coalesced requests we wish to flush */
+	struct list_head	list;		/* lists of struct nfs_write_data */
 	struct nfs_page		*req;		/* multi ops per nfs_page */
 	struct page		**pagevec;
 	unsigned int		npages;		/* Max length of pagevec */
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index dba35e4..7afc363 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -66,6 +66,8 @@
  * int num_online_nodes()		Number of online Nodes
  * int num_possible_nodes()		Number of all possible Nodes
  *
+ * int node_random(mask)		Random node with set bit in mask
+ *
  * int node_online(node)		Is some node online?
  * int node_possible(node)		Is some node possible?
  *
@@ -430,6 +432,7 @@
 	node_clear_state(nid, N_ONLINE);
 	nr_online_nodes = num_node_state(N_ONLINE);
 }
+
 #else
 
 static inline int node_state(int node, enum node_states state)
@@ -460,6 +463,16 @@
 
 #define node_set_online(node)	   node_set_state((node), N_ONLINE)
 #define node_set_offline(node)	   node_clear_state((node), N_ONLINE)
+
+#endif
+
+#if defined(CONFIG_NUMA) && (MAX_NUMNODES > 1)
+extern int node_random(const nodemask_t *maskp);
+#else
+static inline int node_random(const nodemask_t *mask)
+{
+	return 0;
+}
 #endif
 
 #define node_online_map 	node_states[N_ONLINE]
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index e6955f5..cd2e61c 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -63,6 +63,9 @@
 extern unsigned int irq_create_of_mapping(struct device_node *controller,
 					  const u32 *intspec,
 					  unsigned int intsize);
+#ifdef CONFIG_IRQ_DOMAIN
+extern void irq_dispose_mapping(unsigned int irq);
+#endif
 extern int of_irq_to_resource(struct device_node *dev, int index,
 			      struct resource *r);
 extern int of_irq_count(struct device_node *dev);
@@ -70,6 +73,7 @@
 		struct resource *res, int nr_irqs);
 extern struct device_node *of_irq_find_parent(struct device_node *child);
 
+
 #endif /* CONFIG_OF_IRQ */
 #endif /* CONFIG_OF */
 #endif /* __OF_IRQ_H */
diff --git a/include/linux/of_net.h b/include/linux/of_net.h
index e913081..f474641 100644
--- a/include/linux/of_net.h
+++ b/include/linux/of_net.h
@@ -9,6 +9,7 @@
 
 #ifdef CONFIG_OF_NET
 #include <linux/of.h>
+extern const int of_get_phy_mode(struct device_node *np);
 extern const void *of_get_mac_address(struct device_node *np);
 #endif
 
diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h
index 7f5cfd3..49c8727 100644
--- a/include/linux/oprofile.h
+++ b/include/linux/oprofile.h
@@ -18,7 +18,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/printk.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
  
 /* Each escaped entry is prefixed by ESCAPE_CODE
  * then one of the following codes, then the
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 3e5a1b1..e90a673 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -124,9 +124,6 @@
 
 	/* SLOB */
 	PG_slob_free = PG_private,
-
-	/* SLUB */
-	PG_slub_frozen = PG_active,
 };
 
 #ifndef __GENERATING_BOUNDS_H
@@ -212,8 +209,6 @@
 
 __PAGEFLAG(SlobFree, slob_free)
 
-__PAGEFLAG(SlubFrozen, slub_frozen)
-
 /*
  * Private page markings that may be used by the filesystem that owns the page
  * for its own purposes.
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 4e4203a..f27893b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -49,7 +49,7 @@
 #include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/kobject.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/irqreturn.h>
@@ -843,8 +843,8 @@
 void pci_disable_ido(struct pci_dev *dev, unsigned long type);
 
 enum pci_obff_signal_type {
-	PCI_EXP_OBFF_SIGNAL_L0,
-	PCI_EXP_OBFF_SIGNAL_ALWAYS,
+	PCI_EXP_OBFF_SIGNAL_L0 = 0,
+	PCI_EXP_OBFF_SIGNAL_ALWAYS = 1,
 };
 int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type);
 void pci_disable_obff(struct pci_dev *dev);
@@ -879,7 +879,7 @@
 void pdev_sort_resources(struct pci_dev *, struct resource_list *);
 int pci_enable_resources(struct pci_dev *, int mask);
 void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
-		    int (*)(struct pci_dev *, u8, u8));
+		    int (*)(const struct pci_dev *, u8, u8));
 #define HAVE_PCI_REQ_REGIONS	2
 int __must_check pci_request_regions(struct pci_dev *, const char *);
 int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 3f2711c..245bafd 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -509,7 +509,7 @@
 #include <linux/cpu.h>
 #include <linux/irq_work.h>
 #include <linux/jump_label.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/local.h>
 
 #define PERF_MAX_STACK_DEPTH		255
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7da5fa8..54fc413 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -26,7 +26,7 @@
 #include <linux/workqueue.h>
 #include <linux/mod_devicetable.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define PHY_BASIC_FEATURES	(SUPPORTED_10baseT_Half | \
 				 SUPPORTED_10baseT_Full | \
@@ -53,6 +53,7 @@
 
 /* Interface Mode definitions */
 typedef enum {
+	PHY_INTERFACE_MODE_NA,
 	PHY_INTERFACE_MODE_MII,
 	PHY_INTERFACE_MODE_GMII,
 	PHY_INTERFACE_MODE_SGMII,
@@ -62,7 +63,8 @@
 	PHY_INTERFACE_MODE_RGMII_ID,
 	PHY_INTERFACE_MODE_RGMII_RXID,
 	PHY_INTERFACE_MODE_RGMII_TXID,
-	PHY_INTERFACE_MODE_RTBI
+	PHY_INTERFACE_MODE_RTBI,
+	PHY_INTERFACE_MODE_SMII,
 } phy_interface_t;
 
 
diff --git a/include/linux/platform_data/ntc_thermistor.h b/include/linux/platform_data/ntc_thermistor.h
new file mode 100644
index 0000000..abd2862
--- /dev/null
+++ b/include/linux/platform_data/ntc_thermistor.h
@@ -0,0 +1,53 @@
+/*
+ * ntc_thermistor.h - NTC Thermistors
+ *
+ *  Copyright (C) 2010 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.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
+ */
+#ifndef _LINUX_NTC_H
+#define _LINUX_NTC_H
+
+enum ntc_thermistor_type {
+	TYPE_NCPXXWB473,
+	TYPE_NCPXXWL333,
+};
+
+struct ntc_thermistor_platform_data {
+	/*
+	 * One (not both) of read_uV and read_ohm should be provided and only
+	 * one of the two should be provided.
+	 * Both functions should return negative value for an error case.
+	 *
+	 * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required to use
+	 * read_uV()
+	 *
+	 * How to setup pullup_ohm, pulldown_ohm, and connect is
+	 * described at Documentation/hwmon/ntc
+	 *
+	 * pullup/down_ohm: 0 for infinite / not-connected
+	 */
+	int (*read_uV)(void);
+	unsigned int pullup_uV;
+
+	unsigned int pullup_ohm;
+	unsigned int pulldown_ohm;
+	enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect;
+
+	int (*read_ohm)(void);
+};
+
+#endif /* _LINUX_NTC_H */
diff --git a/include/linux/pnfs_osd_xdr.h b/include/linux/pnfs_osd_xdr.h
index 76efbdd..435dd5f 100644
--- a/include/linux/pnfs_osd_xdr.h
+++ b/include/linux/pnfs_osd_xdr.h
@@ -41,9 +41,6 @@
 
 #include <linux/nfs_fs.h>
 #include <linux/nfs_page.h>
-#include <scsi/osd_protocol.h>
-
-#define PNFS_OSD_OSDNAME_MAXSIZE 256
 
 /*
  * draft-ietf-nfsv4-minorversion-22
@@ -99,12 +96,6 @@
 #define _DEVID_HI(oid_device_id) \
 	(unsigned long long)be64_to_cpup(((__be64 *)(oid_device_id)->data) + 1)
 
-static inline int
-pnfs_osd_objid_xdr_sz(void)
-{
-	return (NFS4_DEVICEID4_SIZE / 4) + 2 + 2;
-}
-
 enum pnfs_osd_version {
 	PNFS_OSD_MISSING              = 0,
 	PNFS_OSD_VERSION_1            = 1,
@@ -189,8 +180,6 @@
 	struct nfs4_string		oti_scsi_device_id;
 };
 
-enum { PNFS_OSD_TARGETID_MAX = 1 + PNFS_OSD_OSDNAME_MAXSIZE / 4 };
-
 /*   struct netaddr4 {
  *       // see struct rpcb in RFC1833
  *       string r_netid<>;    // network id
@@ -207,12 +196,6 @@
 	struct pnfs_osd_net_addr	ota_netaddr;
 };
 
-enum {
-	NETWORK_ID_MAX = 16 / 4,
-	UNIVERSAL_ADDRESS_MAX = 64 / 4,
-	PNFS_OSD_TARGETADDR_MAX = 3 +  NETWORK_ID_MAX + UNIVERSAL_ADDRESS_MAX,
-};
-
 struct pnfs_osd_deviceaddr {
 	struct pnfs_osd_targetid	oda_targetid;
 	struct pnfs_osd_targetaddr	oda_targetaddr;
@@ -222,15 +205,6 @@
 	struct nfs4_string		oda_osdname;
 };
 
-enum {
-	ODA_OSDNAME_MAX = PNFS_OSD_OSDNAME_MAXSIZE / 4,
-	PNFS_OSD_DEVICEADDR_MAX =
-		PNFS_OSD_TARGETID_MAX + PNFS_OSD_TARGETADDR_MAX +
-		2 /*oda_lun*/ +
-		1 + OSD_SYSTEMID_LEN +
-		1 + ODA_OSDNAME_MAX,
-};
-
 /* LAYOUTCOMMIT: layoutupdate */
 
 /*   union pnfs_osd_deltaspaceused4 switch (bool dsu_valid) {
@@ -279,7 +253,7 @@
 	u32			oer_errno;
 };
 
-/* OSD XDR API */
+/* OSD XDR Client API */
 /* Layout helpers */
 /* Layout decoding is done in two parts:
  * 1. First Call pnfs_osd_xdr_decode_layout_map to read in only the header part
@@ -337,8 +311,7 @@
 pnfs_osd_xdr_encode_layoutupdate(struct xdr_stream *xdr,
 				 struct pnfs_osd_layoutupdate *lou);
 
-/* osd_ioerror encoding/decoding (layout_return) */
-/* Client */
+/* osd_ioerror encoding (layout_return) */
 extern __be32 *pnfs_osd_xdr_ioerr_reserve_space(struct xdr_stream *xdr);
 extern void pnfs_osd_xdr_encode_ioerr(__be32 *p, struct pnfs_osd_ioerr *ioerr);
 
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index e7576cf..643b96c 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -5,7 +5,7 @@
 #include <linux/fs.h>
 #include <linux/spinlock.h>
 #include <linux/magic.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct net;
 struct completion;
@@ -50,8 +50,6 @@
 
 struct proc_dir_entry {
 	unsigned int low_ino;
-	unsigned int namelen;
-	const char *name;
 	mode_t mode;
 	nlink_t nlink;
 	uid_t uid;
@@ -73,9 +71,11 @@
 	write_proc_t *write_proc;
 	atomic_t count;		/* use count */
 	int pde_users;	/* number of callers into module in progress */
-	spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
 	struct completion *pde_unload_completion;
 	struct list_head pde_openers;	/* who did ->open, but not ->release */
+	spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
+	u8 namelen;
+	char name[];
 };
 
 enum kcore_type {
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 9a85412..cb78556 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -180,7 +180,7 @@
 #include <linux/dqblk_v1.h>
 #include <linux/dqblk_v2.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
 typedef long long qsize_t;	/* Type in which we store sizes */
@@ -415,13 +415,5 @@
 	{QFMT_VFS_V0, "quota_v2"},\
 	{0, NULL}}
 
-#else
-
-# /* nodep */ include <sys/cdefs.h>
-
-__BEGIN_DECLS
-long quotactl __P ((unsigned int, const char *, int, caddr_t));
-__END_DECLS
-
 #endif /* __KERNEL__ */
 #endif /* _QUOTA_ */
diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h
index 75cbf4f..9e65d9e 100644
--- a/include/linux/raid/md_p.h
+++ b/include/linux/raid/md_p.h
@@ -245,10 +245,16 @@
 	__u8	device_uuid[16]; /* user-space setable, ignored by kernel */
 	__u8	devflags;	/* per-device flags.  Only one defined...*/
 #define	WriteMostly1	1	/* mask for writemostly flag in above */
-	__u8	pad2[64-57];	/* set to 0 when writing */
+	/* Bad block log.  If there are any bad blocks the feature flag is set.
+	 * If offset and size are non-zero, that space is reserved and available
+	 */
+	__u8	bblog_shift;	/* shift from sectors to block size */
+	__le16	bblog_size;	/* number of sectors reserved for list */
+	__le32	bblog_offset;	/* sector offset from superblock to bblog,
+				 * signed - not unsigned */
 
 	/* array state information - 64 bytes */
-	__le64	utime;		/* 40 bits second, 24 btes microseconds */
+	__le64	utime;		/* 40 bits second, 24 bits microseconds */
 	__le64	events;		/* incremented when superblock updated */
 	__le64	resync_offset;	/* data before this offset (from data_offset) known to be in sync */
 	__le32	sb_csum;	/* checksum up to devs[max_dev] */
@@ -270,8 +276,8 @@
 					   * must be honoured
 					   */
 #define	MD_FEATURE_RESHAPE_ACTIVE	4
+#define	MD_FEATURE_BAD_BLOCKS		8 /* badblock list is not empty */
 
-#define	MD_FEATURE_ALL			(1|2|4)
+#define	MD_FEATURE_ALL			(1|2|4|8)
 
 #endif 
-
diff --git a/include/linux/ramoops.h b/include/linux/ramoops.h
index 0ae68a2..484fef8 100644
--- a/include/linux/ramoops.h
+++ b/include/linux/ramoops.h
@@ -10,6 +10,8 @@
 struct ramoops_platform_data {
 	unsigned long	mem_size;
 	unsigned long	mem_address;
+	unsigned long	record_size;
+	int		dump_oops;
 };
 
 #endif
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 77950df..6a67414 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -15,7 +15,7 @@
 #include <linux/spinlock.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct rw_semaphore;
 
diff --git a/include/linux/sem.h b/include/linux/sem.h
index f2961af..1feb2de 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -77,7 +77,7 @@
 #define SEMUSZ  20		/* sizeof struct sem_undo */
 
 #ifdef __KERNEL__
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/rcupdate.h>
 #include <linux/cache.h>
 
diff --git a/include/linux/shm.h b/include/linux/shm.h
index eca6235..92808b8 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -95,6 +95,9 @@
 	pid_t			shm_cprid;
 	pid_t			shm_lprid;
 	struct user_struct	*mlock_user;
+
+	/* The task created the shm object.  NULL if the task is dead. */
+	struct task_struct	*shm_creator;
 };
 
 /* shm_mode upper byte flags */
@@ -106,6 +109,7 @@
 #ifdef CONFIG_SYSVIPC
 long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr);
 extern int is_file_shm_hugepages(struct file *file);
+extern void exit_shm(struct task_struct *task);
 #else
 static inline long do_shmat(int shmid, char __user *shmaddr,
 				int shmflg, unsigned long *addr)
@@ -116,6 +120,9 @@
 {
 	return 0;
 }
+static inline void exit_shm(struct task_struct *task)
+{
+}
 #endif
 
 #endif /* __KERNEL__ */
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index a24218c..7b996ed 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -20,7 +20,7 @@
 #include <linux/time.h>
 #include <linux/cache.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/types.h>
 #include <linux/spinlock.h>
 #include <linux/net.h>
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index 4b35c06..f58d641 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -24,6 +24,7 @@
 	ALLOC_FROM_PARTIAL,	/* Cpu slab acquired from partial list */
 	ALLOC_SLAB,		/* Cpu slab acquired from page allocator */
 	ALLOC_REFILL,		/* Refill cpu slab from slab freelist */
+	ALLOC_NODE_MISMATCH,	/* Switching cpu slab */
 	FREE_SLAB,		/* Slab freed to the page allocator */
 	CPUSLAB_FLUSH,		/* Abandoning of the cpu slab */
 	DEACTIVATE_FULL,	/* Cpu slab was full when deactivated */
@@ -31,8 +32,10 @@
 	DEACTIVATE_TO_HEAD,	/* Cpu slab was moved to the head of partials */
 	DEACTIVATE_TO_TAIL,	/* Cpu slab was moved to the tail of partials */
 	DEACTIVATE_REMOTE_FREES,/* Slab contained remotely freed objects */
+	DEACTIVATE_BYPASS,	/* Implicit deactivation */
 	ORDER_FALLBACK,		/* Number of times fallback was necessary */
 	CMPXCHG_DOUBLE_CPU_FAIL,/* Failure of this_cpu_cmpxchg_double */
+	CMPXCHG_DOUBLE_FAIL,	/* Number of times that cmpxchg double did not match */
 	NR_SLUB_STAT_ITEMS };
 
 struct kmem_cache_cpu {
diff --git a/include/linux/sonet.h b/include/linux/sonet.h
index 67ad11f..de8832d 100644
--- a/include/linux/sonet.h
+++ b/include/linux/sonet.h
@@ -58,7 +58,7 @@
 
 #ifdef __KERNEL__
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct k_sonet_stats {
 #define __HANDLE_ITEM(i) atomic_t i
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 0b22d51..7df6c17 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -384,7 +384,7 @@
  * Pull the atomic_t declaration:
  * (asm-mips/atomic.h needs above definitions)
  */
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 /**
  * atomic_dec_and_lock - lock on reaching reference count zero
  * @atomic: the atomic counter
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index 8521067..febc4db 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -15,7 +15,7 @@
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/xdr.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/rcupdate.h>
 
 /* size of the nodename buffer */
diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h
index 0828842..f7f3ce3 100644
--- a/include/linux/sunrpc/bc_xprt.h
+++ b/include/linux/sunrpc/bc_xprt.h
@@ -31,7 +31,7 @@
 #include <linux/sunrpc/xprt.h>
 #include <linux/sunrpc/sched.h>
 
-#ifdef CONFIG_NFS_V4_1
+#ifdef CONFIG_SUNRPC_BACKCHANNEL
 struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt);
 void xprt_free_bc_request(struct rpc_rqst *req);
 int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
@@ -47,7 +47,7 @@
 		return 1;
 	return 0;
 }
-#else /* CONFIG_NFS_V4_1 */
+#else /* CONFIG_SUNRPC_BACKCHANNEL */
 static inline int xprt_setup_backchannel(struct rpc_xprt *xprt,
 					 unsigned int min_reqs)
 {
@@ -62,6 +62,6 @@
 static inline void xprt_free_bc_request(struct rpc_rqst *req)
 {
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 #endif /* _LINUX_SUNRPC_BC_XPRT_H */
 
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index d1c79a9..5efd8ce 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -15,7 +15,7 @@
 
 #include <linux/kref.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/proc_fs.h>
 
 /*
diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
index fe2d8e6..e775689 100644
--- a/include/linux/sunrpc/sched.h
+++ b/include/linux/sunrpc/sched.h
@@ -227,6 +227,10 @@
 void		rpc_destroy_wait_queue(struct rpc_wait_queue *);
 void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
 					rpc_action action);
+void		rpc_sleep_on_priority(struct rpc_wait_queue *,
+					struct rpc_task *,
+					rpc_action action,
+					int priority);
 void		rpc_wake_up_queued_task(struct rpc_wait_queue *,
 					struct rpc_task *);
 void		rpc_wake_up(struct rpc_wait_queue *);
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 2f1e518..223588a 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -92,7 +92,7 @@
 	struct module *		sv_module;	/* optional module to count when
 						 * adding threads */
 	svc_thread_fn		sv_function;	/* main function for threads */
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 	struct list_head	sv_cb_list;	/* queue for callback requests
 						 * that arrive over the same
 						 * connection */
@@ -100,7 +100,7 @@
 	wait_queue_head_t	sv_cb_waitq;	/* sleep here if there are no
 						 * entries in the svc_cb_list */
 	struct svc_xprt		*sv_bc_xprt;	/* callback on fore channel */
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 };
 
 /*
diff --git a/include/linux/sunrpc/timer.h b/include/linux/sunrpc/timer.h
index a67fd73..697d6e6 100644
--- a/include/linux/sunrpc/timer.h
+++ b/include/linux/sunrpc/timer.h
@@ -9,7 +9,7 @@
 #ifndef _LINUX_SUNRPC_TIMER_H
 #define _LINUX_SUNRPC_TIMER_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct rpc_rtt {
 	unsigned long timeo;	/* default timeout value */
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 81cce3b..15518a1 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -22,6 +22,7 @@
 #define RPC_MIN_SLOT_TABLE	(2U)
 #define RPC_DEF_SLOT_TABLE	(16U)
 #define RPC_MAX_SLOT_TABLE	(128U)
+#define RPC_MAX_SLOT_TABLE_LIMIT	(65536U)
 
 /*
  * This describes a timeout strategy
@@ -100,18 +101,18 @@
 	ktime_t			rq_xtime;	/* transmit time stamp */
 	int			rq_ntrans;
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 	struct list_head	rq_bc_list;	/* Callback service list */
 	unsigned long		rq_bc_pa_state;	/* Backchannel prealloc state */
 	struct list_head	rq_bc_pa_list;	/* Backchannel prealloc list */
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANEL */
 };
 #define rq_svec			rq_snd_buf.head
 #define rq_slen			rq_snd_buf.len
 
 struct rpc_xprt_ops {
 	void		(*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize);
-	int		(*reserve_xprt)(struct rpc_task *task);
+	int		(*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
 	void		(*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
 	void		(*rpcbind)(struct rpc_task *task);
 	void		(*set_port)(struct rpc_xprt *xprt, unsigned short port);
@@ -164,12 +165,12 @@
 
 	struct rpc_wait_queue	binding;	/* requests waiting on rpcbind */
 	struct rpc_wait_queue	sending;	/* requests waiting to send */
-	struct rpc_wait_queue	resend;		/* requests waiting to resend */
 	struct rpc_wait_queue	pending;	/* requests in flight */
 	struct rpc_wait_queue	backlog;	/* waiting for slot */
 	struct list_head	free;		/* free slots */
-	struct rpc_rqst *	slot;		/* slot table storage */
-	unsigned int		max_reqs;	/* total slots */
+	unsigned int		max_reqs;	/* max number of slots */
+	unsigned int		min_reqs;	/* min number of slots */
+	atomic_t		num_reqs;	/* total slots */
 	unsigned long		state;		/* transport state */
 	unsigned char		shutdown   : 1,	/* being shut down */
 				resvport   : 1; /* use a reserved port */
@@ -200,7 +201,7 @@
 	u32			xid;		/* Next XID value to use */
 	struct rpc_task *	snd_task;	/* Task blocked in send */
 	struct svc_xprt		*bc_xprt;	/* NFSv4.1 backchannel */
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 	struct svc_serv		*bc_serv;       /* The RPC service which will */
 						/* process the callback */
 	unsigned int		bc_alloc_count;	/* Total number of preallocs */
@@ -208,7 +209,7 @@
 						 * items */
 	struct list_head	bc_pa_list;	/* List of preallocated
 						 * backchannel rpc_rqst's */
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 	struct list_head	recv;
 
 	struct {
@@ -228,15 +229,15 @@
 	const char		*address_strings[RPC_DISPLAY_MAX];
 };
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /*
  * Backchannel flags
  */
 #define	RPC_BC_PA_IN_USE	0x0001		/* Preallocated backchannel */
 						/* buffer in use */
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 static inline int bc_prealloc(struct rpc_rqst *req)
 {
 	return test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
@@ -246,7 +247,7 @@
 {
 	return 0;
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 struct xprt_create {
 	int			ident;		/* XPRT_TRANSPORT identifier */
@@ -271,8 +272,8 @@
 struct rpc_xprt		*xprt_create_transport(struct xprt_create *args);
 void			xprt_connect(struct rpc_task *task);
 void			xprt_reserve(struct rpc_task *task);
-int			xprt_reserve_xprt(struct rpc_task *task);
-int			xprt_reserve_xprt_cong(struct rpc_task *task);
+int			xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
+int			xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
 int			xprt_prepare_transmit(struct rpc_task *task);
 void			xprt_transmit(struct rpc_task *task);
 void			xprt_end_transmit(struct rpc_task *task);
@@ -282,7 +283,9 @@
 void			xprt_release(struct rpc_task *task);
 struct rpc_xprt *	xprt_get(struct rpc_xprt *xprt);
 void			xprt_put(struct rpc_xprt *xprt);
-struct rpc_xprt *	xprt_alloc(struct net *net, int size, int max_req);
+struct rpc_xprt *	xprt_alloc(struct net *net, size_t size,
+				unsigned int num_prealloc,
+				unsigned int max_req);
 void			xprt_free(struct rpc_xprt *);
 
 static inline __be32 *xprt_skip_transport_header(struct rpc_xprt *xprt, __be32 *p)
@@ -321,7 +324,6 @@
 #define XPRT_CLOSING		(6)
 #define XPRT_CONNECTION_ABORT	(7)
 #define XPRT_CONNECTION_CLOSE	(8)
-#define XPRT_INITIALIZED	(9)
 
 static inline void xprt_set_connected(struct rpc_xprt *xprt)
 {
diff --git a/include/linux/swap.h b/include/linux/swap.h
index a273468..14d6249 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -9,7 +9,7 @@
 #include <linux/sched.h>
 #include <linux/node.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/page.h>
 
 struct notifier_block;
@@ -251,14 +251,6 @@
 /* linux/mm/vmscan.c */
 extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
 					gfp_t gfp_mask, nodemask_t *mask);
-extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
-						  gfp_t gfp_mask, bool noswap,
-						  unsigned int swappiness);
-extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
-						gfp_t gfp_mask, bool noswap,
-						unsigned int swappiness,
-						struct zone *zone,
-						unsigned long *nr_scanned);
 extern int __isolate_lru_page(struct page *page, int mode, int file);
 extern unsigned long shrink_all_memory(unsigned long nr_pages);
 extern int vm_swappiness;
@@ -299,7 +291,14 @@
 
 extern int kswapd_run(int nid);
 extern void kswapd_stop(int nid);
-
+#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+extern int mem_cgroup_swappiness(struct mem_cgroup *mem);
+#else
+static inline int mem_cgroup_swappiness(struct mem_cgroup *mem)
+{
+	return vm_swappiness;
+}
+#endif
 #ifdef CONFIG_SWAP
 /* linux/mm/page_io.c */
 extern int swap_readpage(struct page *);
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index e2696d7..d7d2f21 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -17,7 +17,7 @@
 #include <linux/list.h>
 #include <linux/lockdep.h>
 #include <linux/kobject_ns.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct kobject;
 struct module;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 8a4c309..fca24cc 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -376,7 +376,16 @@
 #define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */
 #define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG     */
 #define V4L2_PIX_FMT_DV       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
-#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */
+#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 Multiplexed */
+#define V4L2_PIX_FMT_H264     v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
+#define V4L2_PIX_FMT_H264_NO_SC v4l2_fourcc('A', 'V', 'C', '1') /* H264 without start codes */
+#define V4L2_PIX_FMT_H263     v4l2_fourcc('H', '2', '6', '3') /* H263          */
+#define V4L2_PIX_FMT_MPEG1    v4l2_fourcc('M', 'P', 'G', '1') /* MPEG-1 ES     */
+#define V4L2_PIX_FMT_MPEG2    v4l2_fourcc('M', 'P', 'G', '2') /* MPEG-2 ES     */
+#define V4L2_PIX_FMT_MPEG4    v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4 ES     */
+#define V4L2_PIX_FMT_XVID     v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid           */
+#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
+#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -402,6 +411,7 @@
 #define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
 #define V4L2_PIX_FMT_KONICA420  v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
 #define V4L2_PIX_FMT_JPGL	v4l2_fourcc('J', 'P', 'G', 'L') /* JPEG-Lite */
+#define V4L2_PIX_FMT_SE401      v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */
 
 /*
  *	F O R M A T   E N U M E R A T I O N
@@ -1026,6 +1036,7 @@
 #define V4L2_CTRL_CLASS_MPEG 0x00990000	/* MPEG-compression controls */
 #define V4L2_CTRL_CLASS_CAMERA 0x009a0000	/* Camera class controls */
 #define V4L2_CTRL_CLASS_FM_TX 0x009b0000	/* FM Modulator control class */
+#define V4L2_CTRL_CLASS_FLASH 0x009c0000	/* Camera flash controls */
 
 #define V4L2_CTRL_ID_MASK      	  (0x0fffffff)
 #define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
@@ -1039,6 +1050,7 @@
 	V4L2_CTRL_TYPE_INTEGER64     = 5,
 	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
 	V4L2_CTRL_TYPE_STRING        = 7,
+	V4L2_CTRL_TYPE_BITMASK       = 8,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
@@ -1144,14 +1156,19 @@
 #define V4L2_CID_ILLUMINATORS_1			(V4L2_CID_BASE+37)
 #define V4L2_CID_ILLUMINATORS_2			(V4L2_CID_BASE+38)
 
+#define V4L2_CID_MIN_BUFFERS_FOR_CAPTURE	(V4L2_CID_BASE+39)
+#define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT		(V4L2_CID_BASE+40)
+
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+39)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+41)
+
+/* Minimum number of buffer neede by the device */
 
 /*  MPEG-class control IDs defined by V4L2 */
 #define V4L2_CID_MPEG_BASE 			(V4L2_CTRL_CLASS_MPEG | 0x900)
 #define V4L2_CID_MPEG_CLASS 			(V4L2_CTRL_CLASS_MPEG | 1)
 
-/*  MPEG streams */
+/*  MPEG streams, specific to multiplexed streams */
 #define V4L2_CID_MPEG_STREAM_TYPE 		(V4L2_CID_MPEG_BASE+0)
 enum v4l2_mpeg_stream_type {
 	V4L2_MPEG_STREAM_TYPE_MPEG2_PS   = 0, /* MPEG-2 program stream */
@@ -1173,7 +1190,7 @@
 	V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1,  /* VBI in private packets, IVTV format */
 };
 
-/*  MPEG audio */
+/*  MPEG audio controls specific to multiplexed streams  */
 #define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ 	(V4L2_CID_MPEG_BASE+100)
 enum v4l2_mpeg_audio_sampling_freq {
 	V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
@@ -1289,7 +1306,7 @@
 	V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
 };
 
-/*  MPEG video */
+/*  MPEG video controls specific to multiplexed streams */
 #define V4L2_CID_MPEG_VIDEO_ENCODING 		(V4L2_CID_MPEG_BASE+200)
 enum v4l2_mpeg_video_encoding {
 	V4L2_MPEG_VIDEO_ENCODING_MPEG_1     = 0,
@@ -1317,6 +1334,141 @@
 #define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
 #define V4L2_CID_MPEG_VIDEO_MUTE 		(V4L2_CID_MPEG_BASE+210)
 #define V4L2_CID_MPEG_VIDEO_MUTE_YUV 		(V4L2_CID_MPEG_BASE+211)
+#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE		(V4L2_CID_MPEG_BASE+212)
+#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER	(V4L2_CID_MPEG_BASE+213)
+#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB		(V4L2_CID_MPEG_BASE+214)
+#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE			(V4L2_CID_MPEG_BASE+215)
+#define V4L2_CID_MPEG_VIDEO_HEADER_MODE				(V4L2_CID_MPEG_BASE+216)
+enum v4l2_mpeg_video_header_mode {
+	V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE			= 0,
+	V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME	= 1,
+
+};
+#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC			(V4L2_CID_MPEG_BASE+217)
+#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE		(V4L2_CID_MPEG_BASE+218)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES	(V4L2_CID_MPEG_BASE+219)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB		(V4L2_CID_MPEG_BASE+220)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE		(V4L2_CID_MPEG_BASE+221)
+enum v4l2_mpeg_video_multi_slice_mode {
+	V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE		= 0,
+	V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB		= 1,
+	V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES	= 2,
+};
+#define V4L2_CID_MPEG_VIDEO_VBV_SIZE			(V4L2_CID_MPEG_BASE+222)
+#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP		(V4L2_CID_MPEG_BASE+300)
+#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP		(V4L2_CID_MPEG_BASE+301)
+#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP		(V4L2_CID_MPEG_BASE+302)
+#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP			(V4L2_CID_MPEG_BASE+303)
+#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP			(V4L2_CID_MPEG_BASE+304)
+#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP		(V4L2_CID_MPEG_BASE+350)
+#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP		(V4L2_CID_MPEG_BASE+351)
+#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP		(V4L2_CID_MPEG_BASE+352)
+#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP			(V4L2_CID_MPEG_BASE+353)
+#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP			(V4L2_CID_MPEG_BASE+354)
+#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM		(V4L2_CID_MPEG_BASE+355)
+#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE		(V4L2_CID_MPEG_BASE+356)
+#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE		(V4L2_CID_MPEG_BASE+357)
+enum v4l2_mpeg_video_h264_entropy_mode {
+	V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC	= 0,
+	V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC	= 1,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD		(V4L2_CID_MPEG_BASE+358)
+#define V4L2_CID_MPEG_VIDEO_H264_LEVEL			(V4L2_CID_MPEG_BASE+359)
+enum v4l2_mpeg_video_h264_level {
+	V4L2_MPEG_VIDEO_H264_LEVEL_1_0	= 0,
+	V4L2_MPEG_VIDEO_H264_LEVEL_1B	= 1,
+	V4L2_MPEG_VIDEO_H264_LEVEL_1_1	= 2,
+	V4L2_MPEG_VIDEO_H264_LEVEL_1_2	= 3,
+	V4L2_MPEG_VIDEO_H264_LEVEL_1_3	= 4,
+	V4L2_MPEG_VIDEO_H264_LEVEL_2_0	= 5,
+	V4L2_MPEG_VIDEO_H264_LEVEL_2_1	= 6,
+	V4L2_MPEG_VIDEO_H264_LEVEL_2_2	= 7,
+	V4L2_MPEG_VIDEO_H264_LEVEL_3_0	= 8,
+	V4L2_MPEG_VIDEO_H264_LEVEL_3_1	= 9,
+	V4L2_MPEG_VIDEO_H264_LEVEL_3_2	= 10,
+	V4L2_MPEG_VIDEO_H264_LEVEL_4_0	= 11,
+	V4L2_MPEG_VIDEO_H264_LEVEL_4_1	= 12,
+	V4L2_MPEG_VIDEO_H264_LEVEL_4_2	= 13,
+	V4L2_MPEG_VIDEO_H264_LEVEL_5_0	= 14,
+	V4L2_MPEG_VIDEO_H264_LEVEL_5_1	= 15,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA	(V4L2_CID_MPEG_BASE+360)
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA	(V4L2_CID_MPEG_BASE+361)
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE	(V4L2_CID_MPEG_BASE+362)
+enum v4l2_mpeg_video_h264_loop_filter_mode {
+	V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED				= 0,
+	V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED				= 1,
+	V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY	= 2,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_PROFILE		(V4L2_CID_MPEG_BASE+363)
+enum v4l2_mpeg_video_h264_profile {
+	V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE			= 0,
+	V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE	= 1,
+	V4L2_MPEG_VIDEO_H264_PROFILE_MAIN			= 2,
+	V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED			= 3,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH			= 4,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10			= 5,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422			= 6,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE	= 7,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA		= 8,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA		= 9,
+	V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA		= 10,
+	V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA		= 11,
+	V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE		= 12,
+	V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH		= 13,
+	V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA	= 14,
+	V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH		= 15,
+	V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH		= 16,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT	(V4L2_CID_MPEG_BASE+364)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH	(V4L2_CID_MPEG_BASE+365)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE		(V4L2_CID_MPEG_BASE+366)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC		(V4L2_CID_MPEG_BASE+367)
+enum v4l2_mpeg_video_h264_vui_sar_idc {
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED	= 0,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1		= 1,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11		= 2,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11		= 3,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11		= 4,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33		= 5,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11		= 6,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11		= 7,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11		= 8,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33		= 9,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11		= 10,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11		= 11,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33		= 12,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99		= 13,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3		= 14,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2		= 15,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1		= 16,
+	V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED	= 17,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP	(V4L2_CID_MPEG_BASE+400)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP	(V4L2_CID_MPEG_BASE+401)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP	(V4L2_CID_MPEG_BASE+402)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP	(V4L2_CID_MPEG_BASE+403)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP	(V4L2_CID_MPEG_BASE+404)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL		(V4L2_CID_MPEG_BASE+405)
+enum v4l2_mpeg_video_mpeg4_level {
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_0	= 0,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B	= 1,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_1	= 2,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_2	= 3,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_3	= 4,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B	= 5,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_4	= 6,
+	V4L2_MPEG_VIDEO_MPEG4_LEVEL_5	= 7,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE	(V4L2_CID_MPEG_BASE+406)
+enum v4l2_mpeg_video_mpeg4_profile {
+	V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE				= 0,
+	V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE			= 1,
+	V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE				= 2,
+	V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE			= 3,
+	V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY	= 4,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL		(V4L2_CID_MPEG_BASE+407)
 
 /*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
 #define V4L2_CID_MPEG_CX2341X_BASE 				(V4L2_CTRL_CLASS_MPEG | 0x1000)
@@ -1359,6 +1511,33 @@
 #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+10)
 #define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS 	(V4L2_CID_MPEG_CX2341X_BASE+11)
 
+/*  MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */
+#define V4L2_CID_MPEG_MFC51_BASE				(V4L2_CTRL_CLASS_MPEG | 0x1100)
+
+#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY		(V4L2_CID_MPEG_MFC51_BASE+0)
+#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE	(V4L2_CID_MPEG_MFC51_BASE+1)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE			(V4L2_CID_MPEG_MFC51_BASE+2)
+enum v4l2_mpeg_mfc51_video_frame_skip_mode {
+	V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED		= 0,
+	V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT	= 1,
+	V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT		= 2,
+};
+#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE			(V4L2_CID_MPEG_MFC51_BASE+3)
+enum v4l2_mpeg_mfc51_video_force_frame_type {
+	V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED		= 0,
+	V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME		= 1,
+	V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED	= 2,
+};
+#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING				(V4L2_CID_MPEG_MFC51_BASE+4)
+#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV				(V4L2_CID_MPEG_MFC51_BASE+5)
+#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT			(V4L2_CID_MPEG_MFC51_BASE+6)
+#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF			(V4L2_CID_MPEG_MFC51_BASE+7)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY		(V4L2_CID_MPEG_MFC51_BASE+50)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK			(V4L2_CID_MPEG_MFC51_BASE+51)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH		(V4L2_CID_MPEG_MFC51_BASE+52)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC		(V4L2_CID_MPEG_MFC51_BASE+53)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P		(V4L2_CID_MPEG_MFC51_BASE+54)
+
 /*  Camera class control IDs */
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
 #define V4L2_CID_CAMERA_CLASS 		(V4L2_CTRL_CLASS_CAMERA | 1)
@@ -1427,6 +1606,41 @@
 #define V4L2_CID_TUNE_POWER_LEVEL		(V4L2_CID_FM_TX_CLASS_BASE + 113)
 #define V4L2_CID_TUNE_ANTENNA_CAPACITOR		(V4L2_CID_FM_TX_CLASS_BASE + 114)
 
+/* Flash and privacy (indicator) light controls */
+#define V4L2_CID_FLASH_CLASS_BASE		(V4L2_CTRL_CLASS_FLASH | 0x900)
+#define V4L2_CID_FLASH_CLASS			(V4L2_CTRL_CLASS_FLASH | 1)
+
+#define V4L2_CID_FLASH_LED_MODE			(V4L2_CID_FLASH_CLASS_BASE + 1)
+enum v4l2_flash_led_mode {
+	V4L2_FLASH_LED_MODE_NONE,
+	V4L2_FLASH_LED_MODE_FLASH,
+	V4L2_FLASH_LED_MODE_TORCH,
+};
+
+#define V4L2_CID_FLASH_STROBE_SOURCE		(V4L2_CID_FLASH_CLASS_BASE + 2)
+enum v4l2_flash_strobe_source {
+	V4L2_FLASH_STROBE_SOURCE_SOFTWARE,
+	V4L2_FLASH_STROBE_SOURCE_EXTERNAL,
+};
+
+#define V4L2_CID_FLASH_STROBE			(V4L2_CID_FLASH_CLASS_BASE + 3)
+#define V4L2_CID_FLASH_STROBE_STOP		(V4L2_CID_FLASH_CLASS_BASE + 4)
+#define V4L2_CID_FLASH_STROBE_STATUS		(V4L2_CID_FLASH_CLASS_BASE + 5)
+
+#define V4L2_CID_FLASH_TIMEOUT			(V4L2_CID_FLASH_CLASS_BASE + 6)
+#define V4L2_CID_FLASH_INTENSITY		(V4L2_CID_FLASH_CLASS_BASE + 7)
+#define V4L2_CID_FLASH_TORCH_INTENSITY		(V4L2_CID_FLASH_CLASS_BASE + 8)
+#define V4L2_CID_FLASH_INDICATOR_INTENSITY	(V4L2_CID_FLASH_CLASS_BASE + 9)
+
+#define V4L2_CID_FLASH_FAULT			(V4L2_CID_FLASH_CLASS_BASE + 10)
+#define V4L2_FLASH_FAULT_OVER_VOLTAGE		(1 << 0)
+#define V4L2_FLASH_FAULT_TIMEOUT		(1 << 1)
+#define V4L2_FLASH_FAULT_OVER_TEMPERATURE	(1 << 2)
+#define V4L2_FLASH_FAULT_SHORT_CIRCUIT		(1 << 3)
+
+#define V4L2_CID_FLASH_CHARGE			(V4L2_CID_FLASH_CLASS_BASE + 11)
+#define V4L2_CID_FLASH_READY			(V4L2_CID_FLASH_CLASS_BASE + 12)
+
 /*
  *	T U N I N G
  */
@@ -1791,6 +2005,7 @@
 #define V4L2_EVENT_ALL				0
 #define V4L2_EVENT_VSYNC			1
 #define V4L2_EVENT_EOS				2
+#define V4L2_EVENT_CTRL				3
 #define V4L2_EVENT_PRIVATE_START		0x08000000
 
 /* Payload for V4L2_EVENT_VSYNC */
@@ -1799,21 +2014,46 @@
 	__u8 field;
 } __attribute__ ((packed));
 
+/* Payload for V4L2_EVENT_CTRL */
+#define V4L2_EVENT_CTRL_CH_VALUE		(1 << 0)
+#define V4L2_EVENT_CTRL_CH_FLAGS		(1 << 1)
+
+struct v4l2_event_ctrl {
+	__u32 changes;
+	__u32 type;
+	union {
+		__s32 value;
+		__s64 value64;
+	};
+	__u32 flags;
+	__s32 minimum;
+	__s32 maximum;
+	__s32 step;
+	__s32 default_value;
+};
+
 struct v4l2_event {
 	__u32				type;
 	union {
 		struct v4l2_event_vsync vsync;
+		struct v4l2_event_ctrl	ctrl;
 		__u8			data[64];
 	} u;
 	__u32				pending;
 	__u32				sequence;
 	struct timespec			timestamp;
-	__u32				reserved[9];
+	__u32				id;
+	__u32				reserved[8];
 };
 
+#define V4L2_EVENT_SUB_FL_SEND_INITIAL		(1 << 0)
+#define V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK	(1 << 1)
+
 struct v4l2_event_subscription {
 	__u32				type;
-	__u32				reserved[7];
+	__u32				id;
+	__u32				flags;
+	__u32				reserved[5];
 };
 
 /*
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index bcd942f..65efb92 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -6,7 +6,7 @@
 #include <linux/mm.h>
 #include <linux/mmzone.h>
 #include <linux/vm_event_item.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 extern int sysctl_stat_interval;
 
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 4d05e14..c2164fa 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -137,7 +137,7 @@
 
 static inline bool vt_force_oops_output(struct vc_data *vc)
 {
-	if (oops_in_progress && vc->vc_panic_force_write)
+	if (oops_in_progress && vc->vc_panic_force_write  && panic_timeout >= 0)
 		return true;
 	return false;
 }
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 011bcfe..111843f 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -59,6 +59,84 @@
 #define WATCHDOG_NOWAYOUT	0
 #endif
 
+struct watchdog_ops;
+struct watchdog_device;
+
+/** struct watchdog_ops - The watchdog-devices operations
+ *
+ * @owner:	The module owner.
+ * @start:	The routine for starting the watchdog device.
+ * @stop:	The routine for stopping the watchdog device.
+ * @ping:	The routine that sends a keepalive ping to the watchdog device.
+ * @status:	The routine that shows the status of the watchdog device.
+ * @set_timeout:The routine for setting the watchdog devices timeout value.
+ * @ioctl:	The routines that handles extra ioctl calls.
+ *
+ * The watchdog_ops structure contains a list of low-level operations
+ * that control a watchdog device. It also contains the module that owns
+ * these operations. The start and stop function are mandatory, all other
+ * functions are optonal.
+ */
+struct watchdog_ops {
+	struct module *owner;
+	/* mandatory operations */
+	int (*start)(struct watchdog_device *);
+	int (*stop)(struct watchdog_device *);
+	/* optional operations */
+	int (*ping)(struct watchdog_device *);
+	unsigned int (*status)(struct watchdog_device *);
+	int (*set_timeout)(struct watchdog_device *, unsigned int);
+	long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
+};
+
+/** struct watchdog_device - The structure that defines a watchdog device
+ *
+ * @info:	Pointer to a watchdog_info structure.
+ * @ops:	Pointer to the list of watchdog operations.
+ * @bootstatus:	Status of the watchdog device at boot.
+ * @timeout:	The watchdog devices timeout value.
+ * @min_timeout:The watchdog devices minimum timeout value.
+ * @max_timeout:The watchdog devices maximum timeout value.
+ * @driver-data:Pointer to the drivers private data.
+ * @status:	Field that contains the devices internal status bits.
+ *
+ * The watchdog_device structure contains all information about a
+ * watchdog timer device.
+ *
+ * The driver-data field may not be accessed directly. It must be accessed
+ * via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
+ */
+struct watchdog_device {
+	const struct watchdog_info *info;
+	const struct watchdog_ops *ops;
+	unsigned int bootstatus;
+	unsigned int timeout;
+	unsigned int min_timeout;
+	unsigned int max_timeout;
+	void *driver_data;
+	unsigned long status;
+/* Bit numbers for status flags */
+#define WDOG_ACTIVE		0	/* Is the watchdog running/active */
+#define WDOG_DEV_OPEN		1	/* Opened via /dev/watchdog ? */
+#define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
+#define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
+};
+
+/* Use the following functions to manipulate watchdog driver specific data */
+static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
+{
+	wdd->driver_data = data;
+}
+
+static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
+{
+	return wdd->driver_data;
+}
+
+/* drivers/watchdog/core/watchdog_core.c */
+extern int watchdog_register_device(struct watchdog_device *);
+extern void watchdog_unregister_device(struct watchdog_device *);
+
 #endif	/* __KERNEL__ */
 
 #endif  /* ifndef _LINUX_WATCHDOG_H */
diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
index 38e8c4d..fd98bb9 100644
--- a/include/linux/wm97xx.h
+++ b/include/linux/wm97xx.h
@@ -38,7 +38,11 @@
 #define WM97XX_ADCSEL_X		0x1000	/* x coord measurement */
 #define WM97XX_ADCSEL_Y		0x2000	/* y coord measurement */
 #define WM97XX_ADCSEL_PRES	0x3000	/* pressure measurement */
-#define WM97XX_ADCSEL_MASK	0x7000
+#define WM97XX_AUX_ID1		0x4000
+#define WM97XX_AUX_ID2		0x5000
+#define WM97XX_AUX_ID3		0x6000
+#define WM97XX_AUX_ID4		0x7000
+#define WM97XX_ADCSEL_MASK	0x7000	/* ADC selection mask */
 #define WM97XX_COO		0x0800	/* enable coordinate mode */
 #define WM97XX_CTC		0x0400	/* enable continuous mode */
 #define WM97XX_CM_RATE_93	0x0000	/* 93.75Hz continuous rate */
@@ -61,13 +65,6 @@
 #define WM97XX_PRP_DET_DIG	0xc000	/* setect on, digitise on */
 #define WM97XX_RPR		0x2000	/* wake up on pen down */
 #define WM97XX_PEN_DOWN		0x8000	/* pen is down */
-#define WM97XX_ADCSRC_MASK	0x7000	/* ADC source mask */
-
-#define WM97XX_AUX_ID1		0x8001
-#define WM97XX_AUX_ID2		0x8002
-#define WM97XX_AUX_ID3		0x8003
-#define WM97XX_AUX_ID4		0x8004
-
 
 /* WM9712 Bits */
 #define WM9712_45W		0x1000	/* set for 5-wire touchscreen */
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 2be2887..0d556de 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -10,7 +10,7 @@
 #include <linux/bitops.h>
 #include <linux/lockdep.h>
 #include <linux/threads.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct workqueue_struct;
 
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 17e7ccc..f1bfa12e 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -7,9 +7,39 @@
 #include <linux/sched.h>
 #include <linux/fs.h>
 
-struct backing_dev_info;
+/*
+ * The 1/4 region under the global dirty thresh is for smooth dirty throttling:
+ *
+ *	(thresh - thresh/DIRTY_FULL_SCOPE, thresh)
+ *
+ * The 1/16 region above the global dirty limit will be put to maximum pauses:
+ *
+ *	(limit, limit + limit/DIRTY_MAXPAUSE_AREA)
+ *
+ * The 1/16 region above the max-pause region, dirty exceeded bdi's will be put
+ * to loops:
+ *
+ *	(limit + limit/DIRTY_MAXPAUSE_AREA, limit + limit/DIRTY_PASSGOOD_AREA)
+ *
+ * Further beyond, all dirtier tasks will enter a loop waiting (possibly long
+ * time) for the dirty pages to drop, unless written enough pages.
+ *
+ * The global dirty threshold is normally equal to the global dirty limit,
+ * except when the system suddenly allocates a lot of anonymous memory and
+ * knocks down the global dirty threshold quickly, in which case the global
+ * dirty limit will follow down slowly to prevent livelocking all dirtier tasks.
+ */
+#define DIRTY_SCOPE		8
+#define DIRTY_FULL_SCOPE	(DIRTY_SCOPE / 2)
+#define DIRTY_MAXPAUSE_AREA		16
+#define DIRTY_PASSGOOD_AREA		8
 
-extern spinlock_t inode_wb_list_lock;
+/*
+ * 4MB minimal write chunk size
+ */
+#define MIN_WRITEBACK_PAGES	(4096UL >> (PAGE_CACHE_SHIFT - 10))
+
+struct backing_dev_info;
 
 /*
  * fs/fs-writeback.c
@@ -26,11 +56,6 @@
  */
 struct writeback_control {
 	enum writeback_sync_modes sync_mode;
-	unsigned long *older_than_this;	/* If !NULL, only write back inodes
-					   older than this */
-	unsigned long wb_start;         /* Time writeback_inodes_wb was
-					   called. This is needed to avoid
-					   extra jobs and livelock */
 	long nr_to_write;		/* Write this many pages, and decrement
 					   this for each page written */
 	long pages_skipped;		/* Pages which were not written */
@@ -43,13 +68,11 @@
 	loff_t range_start;
 	loff_t range_end;
 
-	unsigned nonblocking:1;		/* Don't get stuck on request queues */
-	unsigned encountered_congestion:1; /* An output: a queue is full */
 	unsigned for_kupdate:1;		/* A kupdate writeback */
 	unsigned for_background:1;	/* A background writeback */
+	unsigned tagged_writepages:1;	/* tag-and-write to avoid livelock */
 	unsigned for_reclaim:1;		/* Invoked from the page allocator */
 	unsigned range_cyclic:1;	/* range_start is cyclic */
-	unsigned more_io:1;		/* more io to be dispatched */
 };
 
 /*
@@ -62,8 +85,7 @@
 int writeback_inodes_sb_if_idle(struct super_block *);
 int writeback_inodes_sb_nr_if_idle(struct super_block *, unsigned long nr);
 void sync_inodes_sb(struct super_block *);
-void writeback_inodes_wb(struct bdi_writeback *wb,
-		struct writeback_control *wbc);
+long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages);
 long wb_do_writeback(struct bdi_writeback *wb, int force_wait);
 void wakeup_flusher_threads(long nr_pages);
 
@@ -94,6 +116,8 @@
 #endif
 void throttle_vm_writeout(gfp_t gfp_mask);
 
+extern unsigned long global_dirty_limit;
+
 /* These are exported to sysctl. */
 extern int dirty_background_ratio;
 extern unsigned long dirty_background_bytes;
@@ -128,6 +152,13 @@
 unsigned long bdi_dirty_limit(struct backing_dev_info *bdi,
 			       unsigned long dirty);
 
+void __bdi_update_bandwidth(struct backing_dev_info *bdi,
+			    unsigned long thresh,
+			    unsigned long dirty,
+			    unsigned long bdi_thresh,
+			    unsigned long bdi_dirty,
+			    unsigned long start_time);
+
 void page_writeback_init(void);
 void balance_dirty_pages_ratelimited_nr(struct address_space *mapping,
 					unsigned long nr_pages_dirtied);
diff --git a/include/media/adp1653.h b/include/media/adp1653.h
new file mode 100644
index 0000000..50a1af8
--- /dev/null
+++ b/include/media/adp1653.h
@@ -0,0 +1,126 @@
+/*
+ * include/media/adp1653.h
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ *	Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *	Tuukka Toivonen <tuukkat76@gmail.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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef ADP1653_H
+#define ADP1653_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define ADP1653_NAME				"adp1653"
+#define ADP1653_I2C_ADDR			(0x60 >> 1)
+
+/* Register definitions */
+#define ADP1653_REG_OUT_SEL			0x00
+#define ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN	0x01
+#define ADP1653_REG_OUT_SEL_HPLED_TORCH_MAX	0x0b
+#define ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN	0x0c
+#define ADP1653_REG_OUT_SEL_HPLED_FLASH_MAX	0x1f
+#define ADP1653_REG_OUT_SEL_HPLED_SHIFT		3
+#define ADP1653_REG_OUT_SEL_ILED_MAX		0x07
+#define ADP1653_REG_OUT_SEL_ILED_SHIFT		0
+
+#define ADP1653_REG_CONFIG			0x01
+#define ADP1653_REG_CONFIG_TMR_CFG		(1 << 4)
+#define ADP1653_REG_CONFIG_TMR_SET_MAX		0x0f
+#define ADP1653_REG_CONFIG_TMR_SET_SHIFT	0
+
+#define ADP1653_REG_SW_STROBE			0x02
+#define ADP1653_REG_SW_STROBE_SW_STROBE		(1 << 0)
+
+#define ADP1653_REG_FAULT			0x03
+#define ADP1653_REG_FAULT_FLT_SCP		(1 << 3)
+#define ADP1653_REG_FAULT_FLT_OT		(1 << 2)
+#define ADP1653_REG_FAULT_FLT_TMR		(1 << 1)
+#define ADP1653_REG_FAULT_FLT_OV		(1 << 0)
+
+#define ADP1653_INDICATOR_INTENSITY_MIN		0
+#define ADP1653_INDICATOR_INTENSITY_STEP	2500
+#define ADP1653_INDICATOR_INTENSITY_MAX		\
+	(ADP1653_REG_OUT_SEL_ILED_MAX * ADP1653_INDICATOR_INTENSITY_STEP)
+#define ADP1653_INDICATOR_INTENSITY_uA_TO_REG(a) \
+	((a) / ADP1653_INDICATOR_INTENSITY_STEP)
+#define ADP1653_INDICATOR_INTENSITY_REG_TO_uA(a) \
+	((a) * ADP1653_INDICATOR_INTENSITY_STEP)
+
+#define ADP1653_FLASH_INTENSITY_BASE		35
+#define ADP1653_FLASH_INTENSITY_STEP		15
+#define ADP1653_FLASH_INTENSITY_MIN					\
+	(ADP1653_FLASH_INTENSITY_BASE					\
+	 + ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN * ADP1653_FLASH_INTENSITY_STEP)
+#define ADP1653_FLASH_INTENSITY_MAX			\
+	(ADP1653_FLASH_INTENSITY_MIN +			\
+	 (ADP1653_REG_OUT_SEL_HPLED_FLASH_MAX -		\
+	  ADP1653_REG_OUT_SEL_HPLED_FLASH_MIN + 1) *	\
+	 ADP1653_FLASH_INTENSITY_STEP)
+
+#define ADP1653_FLASH_INTENSITY_mA_TO_REG(a)				\
+	((a) < ADP1653_FLASH_INTENSITY_BASE ? 0 :			\
+	 (((a) - ADP1653_FLASH_INTENSITY_BASE) / ADP1653_FLASH_INTENSITY_STEP))
+#define ADP1653_FLASH_INTENSITY_REG_TO_mA(a)		\
+	((a) * ADP1653_FLASH_INTENSITY_STEP + ADP1653_FLASH_INTENSITY_BASE)
+
+#define ADP1653_TORCH_INTENSITY_MIN					\
+	(ADP1653_FLASH_INTENSITY_BASE					\
+	 + ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN * ADP1653_FLASH_INTENSITY_STEP)
+#define ADP1653_TORCH_INTENSITY_MAX			\
+	(ADP1653_TORCH_INTENSITY_MIN +			\
+	 (ADP1653_REG_OUT_SEL_HPLED_TORCH_MAX -		\
+	  ADP1653_REG_OUT_SEL_HPLED_TORCH_MIN + 1) *	\
+	 ADP1653_FLASH_INTENSITY_STEP)
+
+struct adp1653_platform_data {
+	int (*power)(struct v4l2_subdev *sd, int on);
+
+	u32 max_flash_timeout;		/* flash light timeout in us */
+	u32 max_flash_intensity;	/* led intensity, flash mode */
+	u32 max_torch_intensity;	/* led intensity, torch mode */
+	u32 max_indicator_intensity;	/* indicator led intensity */
+};
+
+#define to_adp1653_flash(sd)	container_of(sd, struct adp1653_flash, subdev)
+
+struct adp1653_flash {
+	struct v4l2_subdev subdev;
+	struct adp1653_platform_data *platform_data;
+
+	struct v4l2_ctrl_handler ctrls;
+	struct v4l2_ctrl *led_mode;
+	struct v4l2_ctrl *flash_timeout;
+	struct v4l2_ctrl *flash_intensity;
+	struct v4l2_ctrl *torch_intensity;
+	struct v4l2_ctrl *indicator_intensity;
+
+	struct mutex power_lock;
+	int power_count;
+	int fault;
+};
+
+#endif /* ADP1653_H */
diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h
new file mode 100644
index 0000000..26cece5
--- /dev/null
+++ b/include/media/atmel-isi.h
@@ -0,0 +1,119 @@
+/*
+ * Register definitions for the Atmel Image Sensor Interface.
+ *
+ * Copyright (C) 2011 Atmel Corporation
+ * Josh Wu, <josh.wu@atmel.com>
+ *
+ * Based on previous work by Lars Haring, <lars.haring@atmel.com>
+ * and Sedji Gaouaou
+ *
+ * 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 __ATMEL_ISI_H__
+#define __ATMEL_ISI_H__
+
+#include <linux/types.h>
+
+/* ISI_V2 register offsets */
+#define ISI_CFG1				0x0000
+#define ISI_CFG2				0x0004
+#define ISI_PSIZE				0x0008
+#define ISI_PDECF				0x000c
+#define ISI_Y2R_SET0				0x0010
+#define ISI_Y2R_SET1				0x0014
+#define ISI_R2Y_SET0				0x0018
+#define ISI_R2Y_SET1				0x001C
+#define ISI_R2Y_SET2				0x0020
+#define ISI_CTRL				0x0024
+#define ISI_STATUS				0x0028
+#define ISI_INTEN				0x002C
+#define ISI_INTDIS				0x0030
+#define ISI_INTMASK				0x0034
+#define ISI_DMA_CHER				0x0038
+#define ISI_DMA_CHDR				0x003C
+#define ISI_DMA_CHSR				0x0040
+#define ISI_DMA_P_ADDR				0x0044
+#define ISI_DMA_P_CTRL				0x0048
+#define ISI_DMA_P_DSCR				0x004C
+#define ISI_DMA_C_ADDR				0x0050
+#define ISI_DMA_C_CTRL				0x0054
+#define ISI_DMA_C_DSCR				0x0058
+
+/* Bitfields in CFG1 */
+#define ISI_CFG1_HSYNC_POL_ACTIVE_LOW		(1 << 2)
+#define ISI_CFG1_VSYNC_POL_ACTIVE_LOW		(1 << 3)
+#define ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING	(1 << 4)
+#define ISI_CFG1_EMB_SYNC			(1 << 6)
+#define ISI_CFG1_CRC_SYNC			(1 << 7)
+/* Constants for FRATE(ISI_V2) */
+#define		ISI_CFG1_FRATE_CAPTURE_ALL	(0 << 8)
+#define		ISI_CFG1_FRATE_DIV_2		(1 << 8)
+#define		ISI_CFG1_FRATE_DIV_3		(2 << 8)
+#define		ISI_CFG1_FRATE_DIV_4		(3 << 8)
+#define		ISI_CFG1_FRATE_DIV_5		(4 << 8)
+#define		ISI_CFG1_FRATE_DIV_6		(5 << 8)
+#define		ISI_CFG1_FRATE_DIV_7		(6 << 8)
+#define		ISI_CFG1_FRATE_DIV_8		(7 << 8)
+#define ISI_CFG1_DISCR				(1 << 11)
+#define ISI_CFG1_FULL_MODE			(1 << 12)
+
+/* Bitfields in CFG2 */
+#define ISI_CFG2_GRAYSCALE			(1 << 13)
+/* Constants for YCC_SWAP(ISI_V2) */
+#define		ISI_CFG2_YCC_SWAP_DEFAULT	(0 << 28)
+#define		ISI_CFG2_YCC_SWAP_MODE_1	(1 << 28)
+#define		ISI_CFG2_YCC_SWAP_MODE_2	(2 << 28)
+#define		ISI_CFG2_YCC_SWAP_MODE_3	(3 << 28)
+#define ISI_CFG2_IM_VSIZE_OFFSET		0
+#define ISI_CFG2_IM_HSIZE_OFFSET		16
+#define ISI_CFG2_IM_VSIZE_MASK		(0x7FF << ISI_CFG2_IM_VSIZE_OFFSET)
+#define ISI_CFG2_IM_HSIZE_MASK		(0x7FF << ISI_CFG2_IM_HSIZE_OFFSET)
+
+/* Bitfields in CTRL */
+/* Also using in SR(ISI_V2) */
+#define ISI_CTRL_EN				(1 << 0)
+#define ISI_CTRL_CDC				(1 << 8)
+/* Also using in SR/IER/IDR/IMR(ISI_V2) */
+#define ISI_CTRL_DIS				(1 << 1)
+#define ISI_CTRL_SRST				(1 << 2)
+
+/* Bitfields in SR */
+#define ISI_SR_SIP				(1 << 19)
+/* Also using in SR/IER/IDR/IMR */
+#define ISI_SR_VSYNC				(1 << 10)
+#define ISI_SR_PXFR_DONE			(1 << 16)
+#define ISI_SR_CXFR_DONE			(1 << 17)
+#define ISI_SR_P_OVR				(1 << 24)
+#define ISI_SR_C_OVR				(1 << 25)
+#define ISI_SR_CRC_ERR				(1 << 26)
+#define ISI_SR_FR_OVR				(1 << 27)
+
+/* Bitfields in DMA_C_CTRL & in DMA_P_CTRL */
+#define ISI_DMA_CTRL_FETCH			(1 << 0)
+#define ISI_DMA_CTRL_WB				(1 << 1)
+#define ISI_DMA_CTRL_IEN			(1 << 2)
+#define ISI_DMA_CTRL_DONE			(1 << 3)
+
+/* Bitfields in DMA_CHSR/CHER/CHDR */
+#define ISI_DMA_CHSR_P_CH			(1 << 0)
+#define ISI_DMA_CHSR_C_CH			(1 << 1)
+
+/* Definition for isi_platform_data */
+#define ISI_DATAWIDTH_8				0x01
+#define ISI_DATAWIDTH_10			0x02
+
+struct isi_platform_data {
+	u8 has_emb_sync;
+	u8 emb_crc_sync;
+	u8 hsync_act_low;
+	u8 vsync_act_low;
+	u8 pclk_act_falling;
+	u8 isi_full_mode;
+	u32 data_width_flags;
+	/* Using for ISI_CFG1 */
+	u32 frate;
+};
+
+#endif /* __ATMEL_ISI_H__ */
diff --git a/include/media/davinci/vpbe.h b/include/media/davinci/vpbe.h
new file mode 100644
index 0000000..8b11fb0
--- /dev/null
+++ b/include/media/davinci/vpbe.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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
+ */
+#ifndef _VPBE_H
+#define _VPBE_H
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_osd.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpbe_types.h>
+
+/* OSD configuration info */
+struct osd_config_info {
+	char module_name[32];
+};
+
+struct vpbe_output {
+	struct v4l2_output output;
+	/*
+	 * If output capabilities include dv_preset, list supported presets
+	 * below
+	 */
+	char *subdev_name;
+	/*
+	 * defualt_mode identifies the default timings set at the venc or
+	 * external encoder.
+	 */
+	char *default_mode;
+	/*
+	 * Fields below are used for supporting multiple modes. For example,
+	 * LCD panel might support different modes and they are listed here.
+	 * Similarly for supporting external encoders, lcd controller port
+	 * requires a set of non-standard timing values to be listed here for
+	 * each supported mode since venc is used in non-standard timing mode
+	 * for interfacing with external encoder similar to configuring lcd
+	 * panel timings
+	 */
+	unsigned int num_modes;
+	struct vpbe_enc_mode_info *modes;
+	/*
+	 * Bus configuration goes here for external encoders. Some encoders
+	 * may require multiple interface types for each of the output. For
+	 * example, SD modes would use YCC8 where as HD mode would use YCC16.
+	 * Not sure if this is needed on a per mode basis instead of per
+	 * output basis. If per mode is needed, we may have to move this to
+	 * mode_info structure
+	 */
+};
+
+/* encoder configuration info */
+struct encoder_config_info {
+	char module_name[32];
+	/* Is this an i2c device ? */
+	unsigned int is_i2c:1;
+	/* i2c subdevice board info */
+	struct i2c_board_info board_info;
+};
+
+/* structure for defining vpbe display subsystem components */
+struct vpbe_config {
+	char module_name[32];
+	/* i2c bus adapter no */
+	int i2c_adapter_id;
+	struct osd_config_info osd;
+	struct encoder_config_info venc;
+	/* external encoder information goes here */
+	int num_ext_encoders;
+	struct encoder_config_info *ext_encoders;
+	int num_outputs;
+	/* Order is venc outputs followed by LCD and then external encoders */
+	struct vpbe_output *outputs;
+};
+
+struct vpbe_device;
+
+struct vpbe_device_ops {
+	/* crop cap for the display */
+	int (*g_cropcap)(struct vpbe_device *vpbe_dev,
+			 struct v4l2_cropcap *cropcap);
+
+	/* Enumerate the outputs */
+	int (*enum_outputs)(struct vpbe_device *vpbe_dev,
+			    struct v4l2_output *output);
+
+	/* Set output to the given index */
+	int (*set_output)(struct vpbe_device *vpbe_dev,
+			 int index);
+
+	/* Get current output */
+	unsigned int (*get_output)(struct vpbe_device *vpbe_dev);
+
+	/* Set DV preset at current output */
+	int (*s_dv_preset)(struct vpbe_device *vpbe_dev,
+			   struct v4l2_dv_preset *dv_preset);
+
+	/* Get DV presets supported at the output */
+	int (*g_dv_preset)(struct vpbe_device *vpbe_dev,
+			   struct v4l2_dv_preset *dv_preset);
+
+	/* Enumerate the DV Presets supported at the output */
+	int (*enum_dv_presets)(struct vpbe_device *vpbe_dev,
+			       struct v4l2_dv_enum_preset *preset_info);
+
+	/* Set std at the output */
+	int (*s_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id);
+
+	/* Get the current std at the output */
+	int (*g_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id);
+
+	/* initialize the device */
+	int (*initialize)(struct device *dev, struct vpbe_device *vpbe_dev);
+
+	/* De-initialize the device */
+	void (*deinitialize)(struct device *dev, struct vpbe_device *vpbe_dev);
+
+	/* Get the current mode info */
+	int (*get_mode_info)(struct vpbe_device *vpbe_dev,
+			     struct vpbe_enc_mode_info*);
+
+	/*
+	 * Set the current mode in the encoder. Alternate way of setting
+	 * standard or DV preset or custom timings in the encoder
+	 */
+	int (*set_mode)(struct vpbe_device *vpbe_dev,
+			struct vpbe_enc_mode_info*);
+	/* Power management operations */
+	int (*suspend)(struct vpbe_device *vpbe_dev);
+	int (*resume)(struct vpbe_device *vpbe_dev);
+};
+
+/* struct for vpbe device */
+struct vpbe_device {
+	/* V4l2 device */
+	struct v4l2_device v4l2_dev;
+	/* vpbe dispay controller cfg */
+	struct vpbe_config *cfg;
+	/* parent device */
+	struct device *pdev;
+	/* external encoder v4l2 sub devices */
+	struct v4l2_subdev **encoders;
+	/* current encoder index */
+	int current_sd_index;
+	struct mutex lock;
+	/* device initialized */
+	int initialized;
+	/* vpbe dac clock */
+	struct clk *dac_clk;
+	/* osd_device pointer */
+	struct osd_state *osd_device;
+	/*
+	 * fields below are accessed by users of vpbe_device. Not the
+	 * ones above
+	 */
+
+	/* current output */
+	int current_out_index;
+	/* lock used by caller to do atomic operation on vpbe device */
+	/* current timings set in the controller */
+	struct vpbe_enc_mode_info current_timings;
+	/* venc sub device */
+	struct v4l2_subdev *venc;
+	/* device operations below */
+	struct vpbe_device_ops ops;
+};
+
+#endif
diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h
new file mode 100644
index 0000000..dbf6b37
--- /dev/null
+++ b/include/media/davinci/vpbe_display.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef VPBE_DISPLAY_H
+#define VPBE_DISPLAY_H
+
+/* Header files */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_osd.h>
+#include <media/davinci/vpbe.h>
+
+#define VPBE_DISPLAY_MAX_DEVICES 2
+
+enum vpbe_display_device_id {
+	VPBE_DISPLAY_DEVICE_0,
+	VPBE_DISPLAY_DEVICE_1
+};
+
+#define VPBE_DISPLAY_DRV_NAME	"vpbe-display"
+
+#define VPBE_DISPLAY_MAJOR_RELEASE              1
+#define VPBE_DISPLAY_MINOR_RELEASE              0
+#define VPBE_DISPLAY_BUILD                      1
+#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \
+	(VPBE_DISPLAY_MINOR_RELEASE << 8)  | \
+	VPBE_DISPLAY_BUILD)
+
+#define VPBE_DISPLAY_VALID_FIELD(field)   ((V4L2_FIELD_NONE == field) || \
+	 (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field))
+
+/* Exp ratio numerator and denominator constants */
+#define VPBE_DISPLAY_H_EXP_RATIO_N	9
+#define VPBE_DISPLAY_H_EXP_RATIO_D	8
+#define VPBE_DISPLAY_V_EXP_RATIO_N	6
+#define VPBE_DISPLAY_V_EXP_RATIO_D	5
+
+/* Zoom multiplication factor */
+#define VPBE_DISPLAY_ZOOM_4X	4
+#define VPBE_DISPLAY_ZOOM_2X	2
+
+/* Structures */
+struct display_layer_info {
+	int enable;
+	/* Layer ID used by Display Manager */
+	enum osd_layer id;
+	struct osd_layer_config config;
+	enum osd_zoom_factor h_zoom;
+	enum osd_zoom_factor v_zoom;
+	enum osd_h_exp_ratio h_exp;
+	enum osd_v_exp_ratio v_exp;
+};
+
+/* vpbe display object structure */
+struct vpbe_layer {
+	/* number of buffers in fbuffers */
+	unsigned int numbuffers;
+	/* Pointer to the vpbe_display */
+	struct vpbe_display *disp_dev;
+	/* Pointer pointing to current v4l2_buffer */
+	struct videobuf_buffer *cur_frm;
+	/* Pointer pointing to next v4l2_buffer */
+	struct videobuf_buffer *next_frm;
+	/* videobuf specific parameters
+	 * Buffer queue used in video-buf
+	 */
+	struct videobuf_queue buffer_queue;
+	/* Queue of filled frames */
+	struct list_head dma_queue;
+	/* Used in video-buf */
+	spinlock_t irqlock;
+	/* V4l2 specific parameters */
+	/* Identifies video device for this layer */
+	struct video_device video_dev;
+	/* This field keeps track of type of buffer exchange mechanism user
+	 * has selected
+	 */
+	enum v4l2_memory memory;
+	/* Used to keep track of state of the priority */
+	struct v4l2_prio_state prio;
+	/* Used to store pixel format */
+	struct v4l2_pix_format pix_fmt;
+	enum v4l2_field buf_field;
+	/* Video layer configuration params */
+	struct display_layer_info layer_info;
+	/* vpbe specific parameters
+	 * enable window for display
+	 */
+	unsigned char window_enable;
+	/* number of open instances of the layer */
+	unsigned int usrs;
+	/* number of users performing IO */
+	unsigned int io_usrs;
+	/* Indicates id of the field which is being displayed */
+	unsigned int field_id;
+	/* Indicates whether streaming started */
+	unsigned char started;
+	/* Identifies device object */
+	enum vpbe_display_device_id device_id;
+	/* facilitation of ioctl ops lock by v4l2*/
+	struct mutex opslock;
+	u8 layer_first_int;
+};
+
+/* vpbe device structure */
+struct vpbe_display {
+	/* layer specific parameters */
+	/* lock for isr updates to buf layers*/
+	spinlock_t dma_queue_lock;
+	/* C-Plane offset from start of y-plane */
+	unsigned int cbcr_ofst;
+	struct vpbe_layer *dev[VPBE_DISPLAY_MAX_DEVICES];
+	struct vpbe_device *vpbe_dev;
+	struct osd_state *osd_device;
+};
+
+/* File handle structure */
+struct vpbe_fh {
+	/* vpbe device structure */
+	struct vpbe_display *disp_dev;
+	/* pointer to layer object for opened device */
+	struct vpbe_layer *layer;
+	/* Indicates whether this file handle is doing IO */
+	unsigned char io_allowed;
+	/* Used to keep track priority of this instance */
+	enum v4l2_priority prio;
+};
+
+struct buf_config_params {
+	unsigned char min_numbuffers;
+	unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES];
+	unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES];
+	unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES];
+};
+
+#endif	/* VPBE_DISPLAY_H */
diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h
new file mode 100644
index 0000000..d7e397a
--- /dev/null
+++ b/include/media/davinci/vpbe_osd.h
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2007-2009 Texas Instruments Inc
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ *
+ * Andy Lowe (alowe@mvista.com), MontaVista Software
+ * - Initial version
+ * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd.
+ * - ported to sub device interface
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2..
+ *
+ * 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
+ *
+ */
+#ifndef _OSD_H
+#define _OSD_H
+
+#include <media/davinci/vpbe_types.h>
+
+#define VPBE_OSD_SUBDEV_NAME "vpbe-osd"
+
+/**
+ * enum osd_layer
+ * @WIN_OSD0: On-Screen Display Window 0
+ * @WIN_VID0: Video Window 0
+ * @WIN_OSD1: On-Screen Display Window 1
+ * @WIN_VID1: Video Window 1
+ *
+ * Description:
+ * An enumeration of the osd display layers.
+ */
+enum osd_layer {
+	WIN_OSD0,
+	WIN_VID0,
+	WIN_OSD1,
+	WIN_VID1,
+};
+
+/**
+ * enum osd_win_layer
+ * @OSDWIN_OSD0: On-Screen Display Window 0
+ * @OSDWIN_OSD1: On-Screen Display Window 1
+ *
+ * Description:
+ * An enumeration of the OSD Window layers.
+ */
+enum osd_win_layer {
+	OSDWIN_OSD0,
+	OSDWIN_OSD1,
+};
+
+/**
+ * enum osd_pix_format
+ * @PIXFMT_1BPP: 1-bit-per-pixel bitmap
+ * @PIXFMT_2BPP: 2-bits-per-pixel bitmap
+ * @PIXFMT_4BPP: 4-bits-per-pixel bitmap
+ * @PIXFMT_8BPP: 8-bits-per-pixel bitmap
+ * @PIXFMT_RGB565: 16-bits-per-pixel RGB565
+ * @PIXFMT_YCbCrI: YUV 4:2:2
+ * @PIXFMT_RGB888: 24-bits-per-pixel RGB888
+ * @PIXFMT_YCrCbI: YUV 4:2:2 with chroma swap
+ * @PIXFMT_NV12: YUV 4:2:0 planar
+ * @PIXFMT_OSD_ATTR: OSD Attribute Window pixel format (4bpp)
+ *
+ * Description:
+ * An enumeration of the DaVinci pixel formats.
+ */
+enum osd_pix_format {
+	PIXFMT_1BPP = 0,
+	PIXFMT_2BPP,
+	PIXFMT_4BPP,
+	PIXFMT_8BPP,
+	PIXFMT_RGB565,
+	PIXFMT_YCbCrI,
+	PIXFMT_RGB888,
+	PIXFMT_YCrCbI,
+	PIXFMT_NV12,
+	PIXFMT_OSD_ATTR,
+};
+
+/**
+ * enum osd_h_exp_ratio
+ * @H_EXP_OFF: no expansion (1/1)
+ * @H_EXP_9_OVER_8: 9/8 expansion ratio
+ * @H_EXP_3_OVER_2: 3/2 expansion ratio
+ *
+ * Description:
+ * An enumeration of the available horizontal expansion ratios.
+ */
+enum osd_h_exp_ratio {
+	H_EXP_OFF,
+	H_EXP_9_OVER_8,
+	H_EXP_3_OVER_2,
+};
+
+/**
+ * enum osd_v_exp_ratio
+ * @V_EXP_OFF: no expansion (1/1)
+ * @V_EXP_6_OVER_5: 6/5 expansion ratio
+ *
+ * Description:
+ * An enumeration of the available vertical expansion ratios.
+ */
+enum osd_v_exp_ratio {
+	V_EXP_OFF,
+	V_EXP_6_OVER_5,
+};
+
+/**
+ * enum osd_zoom_factor
+ * @ZOOM_X1: no zoom (x1)
+ * @ZOOM_X2: x2 zoom
+ * @ZOOM_X4: x4 zoom
+ *
+ * Description:
+ * An enumeration of the available zoom factors.
+ */
+enum osd_zoom_factor {
+	ZOOM_X1,
+	ZOOM_X2,
+	ZOOM_X4,
+};
+
+/**
+ * enum osd_clut
+ * @ROM_CLUT: ROM CLUT
+ * @RAM_CLUT: RAM CLUT
+ *
+ * Description:
+ * An enumeration of the available Color Lookup Tables (CLUTs).
+ */
+enum osd_clut {
+	ROM_CLUT,
+	RAM_CLUT,
+};
+
+/**
+ * enum osd_rom_clut
+ * @ROM_CLUT0: Macintosh CLUT
+ * @ROM_CLUT1: CLUT from DM270 and prior devices
+ *
+ * Description:
+ * An enumeration of the ROM Color Lookup Table (CLUT) options.
+ */
+enum osd_rom_clut {
+	ROM_CLUT0,
+	ROM_CLUT1,
+};
+
+/**
+ * enum osd_blending_factor
+ * @OSD_0_VID_8: OSD pixels are fully transparent
+ * @OSD_1_VID_7: OSD pixels contribute 1/8, video pixels contribute 7/8
+ * @OSD_2_VID_6: OSD pixels contribute 2/8, video pixels contribute 6/8
+ * @OSD_3_VID_5: OSD pixels contribute 3/8, video pixels contribute 5/8
+ * @OSD_4_VID_4: OSD pixels contribute 4/8, video pixels contribute 4/8
+ * @OSD_5_VID_3: OSD pixels contribute 5/8, video pixels contribute 3/8
+ * @OSD_6_VID_2: OSD pixels contribute 6/8, video pixels contribute 2/8
+ * @OSD_8_VID_0: OSD pixels are fully opaque
+ *
+ * Description:
+ * An enumeration of the DaVinci pixel blending factor options.
+ */
+enum osd_blending_factor {
+	OSD_0_VID_8,
+	OSD_1_VID_7,
+	OSD_2_VID_6,
+	OSD_3_VID_5,
+	OSD_4_VID_4,
+	OSD_5_VID_3,
+	OSD_6_VID_2,
+	OSD_8_VID_0,
+};
+
+/**
+ * enum osd_blink_interval
+ * @BLINK_X1: blink interval is 1 vertical refresh cycle
+ * @BLINK_X2: blink interval is 2 vertical refresh cycles
+ * @BLINK_X3: blink interval is 3 vertical refresh cycles
+ * @BLINK_X4: blink interval is 4 vertical refresh cycles
+ *
+ * Description:
+ * An enumeration of the DaVinci pixel blinking interval options.
+ */
+enum osd_blink_interval {
+	BLINK_X1,
+	BLINK_X2,
+	BLINK_X3,
+	BLINK_X4,
+};
+
+/**
+ * enum osd_cursor_h_width
+ * @H_WIDTH_1: horizontal line width is 1 pixel
+ * @H_WIDTH_4: horizontal line width is 4 pixels
+ * @H_WIDTH_8: horizontal line width is 8 pixels
+ * @H_WIDTH_12: horizontal line width is 12 pixels
+ * @H_WIDTH_16: horizontal line width is 16 pixels
+ * @H_WIDTH_20: horizontal line width is 20 pixels
+ * @H_WIDTH_24: horizontal line width is 24 pixels
+ * @H_WIDTH_28: horizontal line width is 28 pixels
+ */
+enum osd_cursor_h_width {
+	H_WIDTH_1,
+	H_WIDTH_4,
+	H_WIDTH_8,
+	H_WIDTH_12,
+	H_WIDTH_16,
+	H_WIDTH_20,
+	H_WIDTH_24,
+	H_WIDTH_28,
+};
+
+/**
+ * enum davinci_cursor_v_width
+ * @V_WIDTH_1: vertical line width is 1 line
+ * @V_WIDTH_2: vertical line width is 2 lines
+ * @V_WIDTH_4: vertical line width is 4 lines
+ * @V_WIDTH_6: vertical line width is 6 lines
+ * @V_WIDTH_8: vertical line width is 8 lines
+ * @V_WIDTH_10: vertical line width is 10 lines
+ * @V_WIDTH_12: vertical line width is 12 lines
+ * @V_WIDTH_14: vertical line width is 14 lines
+ */
+enum osd_cursor_v_width {
+	V_WIDTH_1,
+	V_WIDTH_2,
+	V_WIDTH_4,
+	V_WIDTH_6,
+	V_WIDTH_8,
+	V_WIDTH_10,
+	V_WIDTH_12,
+	V_WIDTH_14,
+};
+
+/**
+ * struct osd_cursor_config
+ * @xsize: horizontal size in pixels
+ * @ysize: vertical size in lines
+ * @xpos: horizontal offset in pixels from the left edge of the display
+ * @ypos: vertical offset in lines from the top of the display
+ * @interlaced: Non-zero if the display is interlaced, or zero otherwise
+ * @h_width: horizontal line width
+ * @v_width: vertical line width
+ * @clut: the CLUT selector (ROM or RAM) for the cursor color
+ * @clut_index: an index into the CLUT for the cursor color
+ *
+ * Description:
+ * A structure describing the configuration parameters of the hardware
+ * rectangular cursor.
+ */
+struct osd_cursor_config {
+	unsigned xsize;
+	unsigned ysize;
+	unsigned xpos;
+	unsigned ypos;
+	int interlaced;
+	enum osd_cursor_h_width h_width;
+	enum osd_cursor_v_width v_width;
+	enum osd_clut clut;
+	unsigned char clut_index;
+};
+
+/**
+ * struct osd_layer_config
+ * @pixfmt: pixel format
+ * @line_length: offset in bytes between start of each line in memory
+ * @xsize: number of horizontal pixels displayed per line
+ * @ysize: number of lines displayed
+ * @xpos: horizontal offset in pixels from the left edge of the display
+ * @ypos: vertical offset in lines from the top of the display
+ * @interlaced: Non-zero if the display is interlaced, or zero otherwise
+ *
+ * Description:
+ * A structure describing the configuration parameters of an On-Screen Display
+ * (OSD) or video layer related to how the image is stored in memory.
+ * @line_length must be a multiple of the cache line size (32 bytes).
+ */
+struct osd_layer_config {
+	enum osd_pix_format pixfmt;
+	unsigned line_length;
+	unsigned xsize;
+	unsigned ysize;
+	unsigned xpos;
+	unsigned ypos;
+	int interlaced;
+};
+
+/* parameters that apply on a per-window (OSD or video) basis */
+struct osd_window_state {
+	int is_allocated;
+	int is_enabled;
+	unsigned long fb_base_phys;
+	enum osd_zoom_factor h_zoom;
+	enum osd_zoom_factor v_zoom;
+	struct osd_layer_config lconfig;
+};
+
+/* parameters that apply on a per-OSD-window basis */
+struct osd_osdwin_state {
+	enum osd_clut clut;
+	enum osd_blending_factor blend;
+	int colorkey_blending;
+	unsigned colorkey;
+	int rec601_attenuation;
+	/* index is pixel value */
+	unsigned char palette_map[16];
+};
+
+/* hardware rectangular cursor parameters */
+struct osd_cursor_state {
+	int is_enabled;
+	struct osd_cursor_config config;
+};
+
+struct osd_state;
+
+struct vpbe_osd_ops {
+	int (*initialize)(struct osd_state *sd);
+	int (*request_layer)(struct osd_state *sd, enum osd_layer layer);
+	void (*release_layer)(struct osd_state *sd, enum osd_layer layer);
+	int (*enable_layer)(struct osd_state *sd, enum osd_layer layer,
+			    int otherwin);
+	void (*disable_layer)(struct osd_state *sd, enum osd_layer layer);
+	int (*set_layer_config)(struct osd_state *sd, enum osd_layer layer,
+				struct osd_layer_config *lconfig);
+	void (*get_layer_config)(struct osd_state *sd, enum osd_layer layer,
+				 struct osd_layer_config *lconfig);
+	void (*start_layer)(struct osd_state *sd, enum osd_layer layer,
+			    unsigned long fb_base_phys,
+			    unsigned long cbcr_ofst);
+	void (*set_left_margin)(struct osd_state *sd, u32 val);
+	void (*set_top_margin)(struct osd_state *sd, u32 val);
+	void (*set_interpolation_filter)(struct osd_state *sd, int filter);
+	int (*set_vid_expansion)(struct osd_state *sd,
+					enum osd_h_exp_ratio h_exp,
+					enum osd_v_exp_ratio v_exp);
+	void (*get_vid_expansion)(struct osd_state *sd,
+					enum osd_h_exp_ratio *h_exp,
+					enum osd_v_exp_ratio *v_exp);
+	void (*set_zoom)(struct osd_state *sd, enum osd_layer layer,
+				enum osd_zoom_factor h_zoom,
+				enum osd_zoom_factor v_zoom);
+};
+
+struct osd_state {
+	enum vpbe_version vpbe_type;
+	spinlock_t lock;
+	struct device *dev;
+	dma_addr_t osd_base_phys;
+	unsigned long osd_base;
+	unsigned long osd_size;
+	/* 1-->the isr will toggle the VID0 ping-pong buffer */
+	int pingpong;
+	int interpolation_filter;
+	int field_inversion;
+	enum osd_h_exp_ratio osd_h_exp;
+	enum osd_v_exp_ratio osd_v_exp;
+	enum osd_h_exp_ratio vid_h_exp;
+	enum osd_v_exp_ratio vid_v_exp;
+	enum osd_clut backg_clut;
+	unsigned backg_clut_index;
+	enum osd_rom_clut rom_clut;
+	int is_blinking;
+	/* attribute window blinking enabled */
+	enum osd_blink_interval blink;
+	/* YCbCrI or YCrCbI */
+	enum osd_pix_format yc_pixfmt;
+	/* columns are Y, Cb, Cr */
+	unsigned char clut_ram[256][3];
+	struct osd_cursor_state cursor;
+	/* OSD0, VID0, OSD1, VID1 */
+	struct osd_window_state win[4];
+	/* OSD0, OSD1 */
+	struct osd_osdwin_state osdwin[2];
+	/* OSD device Operations */
+	struct vpbe_osd_ops ops;
+};
+
+struct osd_platform_data {
+	enum vpbe_version vpbe_type;
+	int  field_inv_wa_enable;
+};
+
+#endif
diff --git a/include/media/davinci/vpbe_types.h b/include/media/davinci/vpbe_types.h
new file mode 100644
index 0000000..727f551
--- /dev/null
+++ b/include/media/davinci/vpbe_types.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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
+ */
+#ifndef _VPBE_TYPES_H
+#define _VPBE_TYPES_H
+
+enum vpbe_version {
+	VPBE_VERSION_1 = 1,
+	VPBE_VERSION_2,
+	VPBE_VERSION_3,
+};
+
+/* vpbe_timing_type - Timing types used in vpbe device */
+enum vpbe_enc_timings_type {
+	VPBE_ENC_STD = 0x1,
+	VPBE_ENC_DV_PRESET = 0x2,
+	VPBE_ENC_CUSTOM_TIMINGS = 0x4,
+	/* Used when set timings through FB device interface */
+	VPBE_ENC_TIMINGS_INVALID = 0x8,
+};
+
+union vpbe_timings {
+	v4l2_std_id std_id;
+	unsigned int dv_preset;
+};
+
+/*
+ * struct vpbe_enc_mode_info
+ * @name: ptr to name string of the standard, "NTSC", "PAL" etc
+ * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard
+ * @interlaced: 1 - interlaced, 0 - non interlaced/progressive
+ * @xres: x or horizontal resolution of the display
+ * @yres: y or vertical resolution of the display
+ * @fps: frame per second
+ * @left_margin: left margin of the display
+ * @right_margin: right margin of the display
+ * @upper_margin: upper margin of the display
+ * @lower_margin: lower margin of the display
+ * @hsync_len: h-sync length
+ * @vsync_len: v-sync length
+ * @flags: bit field: bit usage is documented below
+ *
+ * Description:
+ *  Structure holding timing and resolution information of a standard.
+ * Used by vpbe_device to set required non-standard timing in the
+ * venc when lcd controller output is connected to a external encoder.
+ * A table of timings is maintained in vpbe device to set this in
+ * venc when external encoder is connected to lcd controller output.
+ * Encoder may provide a g_dv_timings() API to override these values
+ * as needed.
+ *
+ *  Notes
+ *  ------
+ *  if_type should be used only by encoder manager and encoder.
+ *  flags usage
+ *     b0 (LSB) - hsync polarity, 0 - negative, 1 - positive
+ *     b1       - vsync polarity, 0 - negative, 1 - positive
+ *     b2       - field id polarity, 0 - negative, 1  - positive
+ */
+struct vpbe_enc_mode_info {
+	unsigned char *name;
+	enum vpbe_enc_timings_type timings_type;
+	union vpbe_timings timings;
+	unsigned int interlaced;
+	unsigned int xres;
+	unsigned int yres;
+	struct v4l2_fract aspect;
+	struct v4l2_fract fps;
+	unsigned int left_margin;
+	unsigned int right_margin;
+	unsigned int upper_margin;
+	unsigned int lower_margin;
+	unsigned int hsync_len;
+	unsigned int vsync_len;
+	unsigned int flags;
+};
+
+#endif
diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h
new file mode 100644
index 0000000..426c205
--- /dev/null
+++ b/include/media/davinci/vpbe_venc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * 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
+ */
+#ifndef _VPBE_VENC_H
+#define _VPBE_VENC_H
+
+#include <media/v4l2-subdev.h>
+#include <media/davinci/vpbe_types.h>
+
+#define VPBE_VENC_SUBDEV_NAME "vpbe-venc"
+
+/* venc events */
+#define VENC_END_OF_FRAME	BIT(0)
+#define VENC_FIRST_FIELD	BIT(1)
+#define VENC_SECOND_FIELD	BIT(2)
+
+struct venc_platform_data {
+	enum vpbe_version venc_type;
+	int (*setup_clock)(enum vpbe_enc_timings_type type,
+			   unsigned int mode);
+	/* Number of LCD outputs supported */
+	int num_lcd_outputs;
+};
+
+enum venc_ioctls {
+	VENC_GET_FLD = 1,
+};
+
+/* exported functions */
+struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
+		const char *venc_name);
+#endif
diff --git a/include/media/mmp-camera.h b/include/media/mmp-camera.h
new file mode 100644
index 0000000..7611963
--- /dev/null
+++ b/include/media/mmp-camera.h
@@ -0,0 +1,9 @@
+/*
+ * Information for the Marvell Armada MMP camera
+ */
+
+struct mmp_camera_platform_data {
+	struct platform_device *i2c_device;
+	int sensor_power_gpio;
+	int sensor_reset_gpio;
+};
diff --git a/drivers/media/video/ov7670.h b/include/media/ov7670.h
similarity index 100%
rename from drivers/media/video/ov7670.h
rename to include/media/ov7670.h
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 60536c7..b1f19b7 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -117,7 +117,7 @@
 	int				(*s_tx_carrier)(struct rc_dev *dev, u32 carrier);
 	int				(*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle);
 	int				(*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max);
-	int				(*tx_ir)(struct rc_dev *dev, int *txbuf, u32 n);
+	int				(*tx_ir)(struct rc_dev *dev, unsigned *txbuf, unsigned n);
 	void				(*s_idle)(struct rc_dev *dev, bool enable);
 	int				(*s_learning_mode)(struct rc_dev *dev, int enable);
 	int				(*s_carrier_report) (struct rc_dev *dev, int enable);
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 4e1409e..17c9759 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -18,12 +18,13 @@
 #define RC_TYPE_JVC	(1  << 3)	/* JVC protocol */
 #define RC_TYPE_SONY	(1  << 4)	/* Sony12/15/20 protocol */
 #define RC_TYPE_RC5_SZ	(1  << 5)	/* RC5 variant used by Streamzap */
+#define RC_TYPE_MCE_KBD	(1  << 29)	/* RC6-ish MCE keyboard/mouse */
 #define RC_TYPE_LIRC	(1  << 30)	/* Pass raw IR to lirc userspace */
 #define RC_TYPE_OTHER	(1u << 31)
 
 #define RC_TYPE_ALL (RC_TYPE_RC5 | RC_TYPE_NEC  | RC_TYPE_RC6  | \
 		     RC_TYPE_JVC | RC_TYPE_SONY | RC_TYPE_LIRC | \
-		     RC_TYPE_RC5_SZ | RC_TYPE_OTHER)
+		     RC_TYPE_RC5_SZ | RC_TYPE_MCE_KBD | RC_TYPE_OTHER)
 
 struct rc_map_table {
 	u32	scancode;
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
index 80346a6..48413b4 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/sh_mobile_ceu.h
@@ -7,10 +7,18 @@
 #define SH_CEU_FLAG_VSYNC_LOW		(1 << 3) /* default High if possible */
 
 struct device;
+struct resource;
+
+struct sh_mobile_ceu_companion {
+	u32		num_resources;
+	struct resource	*resource;
+	int		id;
+	void		*platform_data;
+};
 
 struct sh_mobile_ceu_info {
 	unsigned long flags;
-	struct device *csi2_dev;
+	struct sh_mobile_ceu_companion *csi2;
 };
 
 #endif /* __ASM_SH_MOBILE_CEU_H__ */
diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h
index 4d26151..c586c4f 100644
--- a/include/media/sh_mobile_csi2.h
+++ b/include/media/sh_mobile_csi2.h
@@ -11,6 +11,8 @@
 #ifndef SH_MIPI_CSI
 #define SH_MIPI_CSI
 
+#include <linux/list.h>
+
 enum sh_csi2_phy {
 	SH_CSI2_PHY_MAIN,
 	SH_CSI2_PHY_SUB,
@@ -33,14 +35,14 @@
 	struct platform_device *pdev;	/* client platform device */
 };
 
+struct v4l2_device;
+
 struct sh_csi2_pdata {
 	enum sh_csi2_type type;
 	unsigned int flags;
 	struct sh_csi2_client_config *clients;
 	int num_clients;
+	struct v4l2_device *v4l2_dev;
 };
 
-struct device;
-struct v4l2_device;
-
 #endif
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 238bd33..7582952 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -20,14 +20,15 @@
 #include <media/videobuf2-core.h>
 #include <media/v4l2-device.h>
 
-extern struct bus_type soc_camera_bus_type;
-
 struct file;
+struct soc_camera_link;
 
 struct soc_camera_device {
-	struct list_head list;
-	struct device dev;
+	struct list_head list;		/* list of all registered devices */
+	struct soc_camera_link *link;
 	struct device *pdev;		/* Platform device */
+	struct device *parent;		/* Camera host device */
+	struct device *control;		/* E.g., the i2c client */
 	s32 user_width;
 	s32 user_height;
 	u32 bytesperline;		/* for padding, zero if unused */
@@ -66,8 +67,6 @@
 	struct module *owner;
 	int (*add)(struct soc_camera_device *);
 	void (*remove)(struct soc_camera_device *);
-	int (*suspend)(struct soc_camera_device *, pm_message_t);
-	int (*resume)(struct soc_camera_device *);
 	/*
 	 * .get_formats() is called for each client device format, but
 	 * .put_formats() is only called once. Further, if any of the calls to
@@ -109,12 +108,6 @@
 #define SOCAM_SENSOR_INVERT_HSYNC	(1 << 2)
 #define SOCAM_SENSOR_INVERT_VSYNC	(1 << 3)
 #define SOCAM_SENSOR_INVERT_DATA	(1 << 4)
-#define SOCAM_MIPI_1LANE		(1 << 5)
-#define SOCAM_MIPI_2LANE		(1 << 6)
-#define SOCAM_MIPI_3LANE		(1 << 7)
-#define SOCAM_MIPI_4LANE		(1 << 8)
-#define SOCAM_MIPI	(SOCAM_MIPI_1LANE | SOCAM_MIPI_2LANE | \
-			SOCAM_MIPI_3LANE | SOCAM_MIPI_4LANE)
 
 struct i2c_board_info;
 struct regulator_bulk_data;
@@ -134,11 +127,11 @@
 	int num_regulators;
 
 	/*
-	 * For non-I2C devices platform platform has to provide methods to
-	 * add a device to the system and to remove
+	 * For non-I2C devices platform has to provide methods to add a device
+	 * to the system and to remove it
 	 */
-	int (*add_device)(struct soc_camera_link *, struct device *);
-	void (*del_device)(struct soc_camera_link *);
+	int (*add_device)(struct soc_camera_device *);
+	void (*del_device)(struct soc_camera_device *);
 	/* Optional callbacks to power on or off and reset the sensor */
 	int (*power)(struct device *, int);
 	int (*reset)(struct device *);
@@ -152,12 +145,6 @@
 	void (*free_bus)(struct soc_camera_link *);
 };
 
-static inline struct soc_camera_device *to_soc_camera_dev(
-	const struct device *dev)
-{
-	return container_of(dev, struct soc_camera_device, dev);
-}
-
 static inline struct soc_camera_host *to_soc_camera_host(
 	const struct device *dev)
 {
@@ -169,13 +156,13 @@
 static inline struct soc_camera_link *to_soc_camera_link(
 	const struct soc_camera_device *icd)
 {
-	return icd->dev.platform_data;
+	return icd->link;
 }
 
 static inline struct device *to_soc_camera_control(
 	const struct soc_camera_device *icd)
 {
-	return dev_get_drvdata(&icd->dev);
+	return icd->control;
 }
 
 static inline struct v4l2_subdev *soc_camera_to_subdev(
@@ -207,11 +194,8 @@
 };
 
 struct soc_camera_ops {
-	int (*suspend)(struct soc_camera_device *, pm_message_t state);
-	int (*resume)(struct soc_camera_device *);
 	unsigned long (*query_bus_param)(struct soc_camera_device *);
 	int (*set_bus_param)(struct soc_camera_device *, unsigned long);
-	int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);
 	const struct v4l2_queryctrl *controls;
 	int num_controls;
 };
@@ -270,6 +254,12 @@
 #define SOCAM_PCLK_SAMPLE_FALLING	(1 << 13)
 #define SOCAM_DATA_ACTIVE_HIGH		(1 << 14)
 #define SOCAM_DATA_ACTIVE_LOW		(1 << 15)
+#define SOCAM_MIPI_1LANE		(1 << 16)
+#define SOCAM_MIPI_2LANE		(1 << 17)
+#define SOCAM_MIPI_3LANE		(1 << 18)
+#define SOCAM_MIPI_4LANE		(1 << 19)
+#define SOCAM_MIPI	(SOCAM_MIPI_1LANE | SOCAM_MIPI_2LANE | \
+			SOCAM_MIPI_3LANE | SOCAM_MIPI_4LANE)
 
 #define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \
 			      SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \
diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h
index 6d7a4fd..74f0fa1 100644
--- a/include/media/soc_camera_platform.h
+++ b/include/media/soc_camera_platform.h
@@ -21,7 +21,7 @@
 	unsigned long format_depth;
 	struct v4l2_mbus_framefmt format;
 	unsigned long bus_param;
-	struct device *dev;
+	struct soc_camera_device *icd;
 	int (*set_capture)(struct soc_camera_platform_info *info, int enable);
 };
 
@@ -30,8 +30,7 @@
 	*pdev = NULL;
 }
 
-static inline int soc_camera_platform_add(const struct soc_camera_link *icl,
-					  struct device *dev,
+static inline int soc_camera_platform_add(struct soc_camera_device *icd,
 					  struct platform_device **pdev,
 					  struct soc_camera_link *plink,
 					  void (*release)(struct device *dev),
@@ -40,7 +39,7 @@
 	struct soc_camera_platform_info *info = plink->priv;
 	int ret;
 
-	if (icl != plink)
+	if (icd->link != plink)
 		return -ENODEV;
 
 	if (*pdev)
@@ -50,7 +49,7 @@
 	if (!*pdev)
 		return -ENOMEM;
 
-	info->dev = dev;
+	info->icd = icd;
 
 	(*pdev)->dev.platform_data = info;
 	(*pdev)->dev.release = release;
@@ -59,17 +58,17 @@
 	if (ret < 0) {
 		platform_device_put(*pdev);
 		*pdev = NULL;
-		info->dev = NULL;
+		info->icd = NULL;
 	}
 
 	return ret;
 }
 
-static inline void soc_camera_platform_del(const struct soc_camera_link *icl,
+static inline void soc_camera_platform_del(const struct soc_camera_device *icd,
 					   struct platform_device *pdev,
 					   const struct soc_camera_link *plink)
 {
-	if (icl != plink || !pdev)
+	if (icd->link != plink || !pdev)
 		return;
 
 	platform_device_unregister(pdev);
diff --git a/include/media/timb_radio.h b/include/media/timb_radio.h
index a59a848..a40a6a3 100644
--- a/include/media/timb_radio.h
+++ b/include/media/timb_radio.h
@@ -23,13 +23,8 @@
 
 struct timb_radio_platform_data {
 	int i2c_adapter; /* I2C adapter where the tuner and dsp are attached */
-	struct {
-		struct i2c_board_info *info;
-	} tuner;
-	struct {
-		const char *module_name;
-		struct i2c_board_info *info;
-	} dsp;
+	struct i2c_board_info *tuner;
+	struct i2c_board_info *dsp;
 };
 
 #endif
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 963e334..89c290b 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -127,6 +127,8 @@
 #define TUNER_PHILIPS_FMD1216MEX_MK3	78
 #define TUNER_PHILIPS_FM1216MK5		79
 #define TUNER_PHILIPS_FQ1216LME_MK3	80	/* Active loopthrough, no FM */
+#define TUNER_XC4000			81	/* Xceive Silicon Tuner */
+
 #define TUNER_PARTSNIC_PTI_5NF05	81
 #define TUNER_PHILIPS_CU1216L           82
 #define TUNER_NXP_TDA18271		83
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index b3edb67..63fd9d3 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -76,6 +76,7 @@
 	V4L2_IDENT_OV6650 = 258,
 	V4L2_IDENT_OV2640 = 259,
 	V4L2_IDENT_OV9740 = 260,
+	V4L2_IDENT_OV5642 = 261,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
@@ -185,8 +186,9 @@
 	/* module wm8775: just ident 8775 */
 	V4L2_IDENT_WM8775 = 8775,
 
-	/* module cafe_ccic, just ident 8801 */
+	/* Marvell controllers starting at 8801 */
 	V4L2_IDENT_CAFE = 8801,
+	V4L2_IDENT_ARMADA610 = 8802,
 
 	/* AKM AK8813/AK8814 */
 	V4L2_IDENT_AK8813 = 8813,
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 97d0638..13fe4d7 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -27,9 +27,12 @@
 
 /* forward references */
 struct v4l2_ctrl_handler;
+struct v4l2_ctrl_helper;
 struct v4l2_ctrl;
 struct video_device;
 struct v4l2_subdev;
+struct v4l2_subscribed_event;
+struct v4l2_fh;
 
 /** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
   * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
@@ -51,6 +54,7 @@
 
 /** struct v4l2_ctrl - The control structure.
   * @node:	The list node.
+  * @ev_subs:	The list of control event subscriptions.
   * @handler:	The handler that owns the control.
   * @cluster:	Point to start of cluster array.
   * @ncontrols:	Number of controls in cluster array.
@@ -65,6 +69,15 @@
   *		control's current value cannot be cached and needs to be
   *		retrieved through the g_volatile_ctrl op. Drivers can set
   *		this flag.
+  * @is_auto:   If set, then this control selects whether the other cluster
+  *		members are in 'automatic' mode or 'manual' mode. This is
+  *		used for autogain/gain type clusters. Drivers should never
+  *		set this flag directly.
+  * @manual_mode_value: If the is_auto flag is set, then this is the value
+  *		of the auto control that determines if that control is in
+  *		manual mode. So if the value of the auto control equals this
+  *		value, then the whole cluster is in manual mode. Drivers should
+  *		never set this flag directly.
   * @ops:	The control ops.
   * @id:	The control ID.
   * @name:	The control name.
@@ -97,6 +110,7 @@
 struct v4l2_ctrl {
 	/* Administrative fields */
 	struct list_head node;
+	struct list_head ev_subs;
 	struct v4l2_ctrl_handler *handler;
 	struct v4l2_ctrl **cluster;
 	unsigned ncontrols;
@@ -105,6 +119,8 @@
 	unsigned int is_new:1;
 	unsigned int is_private:1;
 	unsigned int is_volatile:1;
+	unsigned int is_auto:1;
+	unsigned int manual_mode_value:8;
 
 	const struct v4l2_ctrl_ops *ops;
 	u32 id;
@@ -134,6 +150,7 @@
   * @node:	List node for the sorted list.
   * @next:	Single-link list node for the hash.
   * @ctrl:	The actual control information.
+  * @helper:	Pointer to helper struct. Used internally in prepare_ext_ctrls().
   *
   * Each control handler has a list of these refs. The list_head is used to
   * keep a sorted-by-control-ID list of all controls, while the next pointer
@@ -143,6 +160,7 @@
 	struct list_head node;
 	struct v4l2_ctrl_ref *next;
 	struct v4l2_ctrl *ctrl;
+	struct v4l2_ctrl_helper *helper;
 };
 
 /** struct v4l2_ctrl_handler - The control handler keeps track of all the
@@ -363,6 +381,40 @@
 void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
 
 
+/** v4l2_ctrl_auto_cluster() - Mark all controls in the cluster as belonging to
+  * that cluster and set it up for autofoo/foo-type handling.
+  * @ncontrols:	The number of controls in this cluster.
+  * @controls:	The cluster control array of size @ncontrols. The first control
+  *		must be the 'auto' control (e.g. autogain, autoexposure, etc.)
+  * @manual_val: The value for the first control in the cluster that equals the
+  *		manual setting.
+  * @set_volatile: If true, then all controls except the first auto control will
+  *		have is_volatile set to true. If false, then is_volatile will not
+  *		be touched.
+  *
+  * Use for control groups where one control selects some automatic feature and
+  * the other controls are only active whenever the automatic feature is turned
+  * off (manual mode). Typical examples: autogain vs gain, auto-whitebalance vs
+  * red and blue balance, etc.
+  *
+  * The behavior of such controls is as follows:
+  *
+  * When the autofoo control is set to automatic, then any manual controls
+  * are set to inactive and any reads will call g_volatile_ctrl (if the control
+  * was marked volatile).
+  *
+  * When the autofoo control is set to manual, then any manual controls will
+  * be marked active, and any reads will just return the current value without
+  * going through g_volatile_ctrl.
+  *
+  * In addition, this function will set the V4L2_CTRL_FLAG_UPDATE flag
+  * on the autofoo control and V4L2_CTRL_FLAG_INACTIVE on the foo control(s)
+  * if autofoo is in auto mode.
+  */
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+			u8 manual_val, bool set_volatile);
+
+
 /** v4l2_ctrl_find() - Find a control with the given ID.
   * @hdl:	The control handler.
   * @id:	The control ID to find.
@@ -379,9 +431,9 @@
   * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically.
   * Does nothing if @ctrl == NULL.
   * This will usually be called from within the s_ctrl op.
+  * The V4L2_EVENT_CTRL event will be generated afterwards.
   *
-  * This function can be called regardless of whether the control handler
-  * is locked or not.
+  * This function assumes that the control handler is locked.
   */
 void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
 
@@ -391,11 +443,12 @@
   *
   * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically.
   * Does nothing if @ctrl == NULL.
+  * The V4L2_EVENT_CTRL event will be generated afterwards.
   * This will usually be called when starting or stopping streaming in the
   * driver.
   *
-  * This function can be called regardless of whether the control handler
-  * is locked or not.
+  * This function assumes that the control handler is not locked and will
+  * take the lock itself.
   */
 void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
 
@@ -440,15 +493,22 @@
   */
 int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
 
+/* Internal helper functions that deal with control events. */
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+		struct v4l2_subscribed_event *sev);
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+		struct v4l2_subscribed_event *sev);
 
 /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
 int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
 int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
 int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
-int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+						struct v4l2_control *ctrl);
 int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
 int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
-int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+						struct v4l2_ext_controls *c);
 
 /* Helpers for subdevices. If the associated ctrl_handler == NULL then they
    will all return -EINVAL. */
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
index 3b86177..5f14e88 100644
--- a/include/media/v4l2-event.h
+++ b/include/media/v4l2-event.h
@@ -29,39 +29,95 @@
 #include <linux/videodev2.h>
 #include <linux/wait.h>
 
+/*
+ * Overview:
+ *
+ * Events are subscribed per-filehandle. An event specification consists of a
+ * type and is optionally associated with an object identified through the
+ * 'id' field. So an event is uniquely identified by the (type, id) tuple.
+ *
+ * The v4l2-fh struct has a list of subscribed events. The v4l2_subscribed_event
+ * struct is added to that list, one for every subscribed event.
+ *
+ * Each v4l2_subscribed_event struct ends with an array of v4l2_kevent structs.
+ * This array (ringbuffer, really) is used to store any events raised by the
+ * driver. The v4l2_kevent struct links into the 'available' list of the
+ * v4l2_fh struct so VIDIOC_DQEVENT will know which event to dequeue first.
+ *
+ * Finally, if the event subscription is associated with a particular object
+ * such as a V4L2 control, then that object needs to know about that as well
+ * so that an event can be raised by that object. So the 'node' field can
+ * be used to link the v4l2_subscribed_event struct into a list of that
+ * object.
+ *
+ * So to summarize:
+ *
+ * struct v4l2_fh has two lists: one of the subscribed events, and one of the
+ * pending events.
+ *
+ * struct v4l2_subscribed_event has a ringbuffer of raised (pending) events of
+ * that particular type.
+ *
+ * If struct v4l2_subscribed_event is associated with a specific object, then
+ * that object will have an internal list of struct v4l2_subscribed_event so
+ * it knows who subscribed an event to that object.
+ */
+
 struct v4l2_fh;
+struct v4l2_subscribed_event;
 struct video_device;
 
+/** struct v4l2_kevent - Internal kernel event struct.
+  * @list:	List node for the v4l2_fh->available list.
+  * @sev:	Pointer to parent v4l2_subscribed_event.
+  * @event:	The event itself.
+  */
 struct v4l2_kevent {
 	struct list_head	list;
+	struct v4l2_subscribed_event *sev;
 	struct v4l2_event	event;
 };
 
+/** struct v4l2_subscribed_event - Internal struct representing a subscribed event.
+  * @list:	List node for the v4l2_fh->subscribed list.
+  * @type:	Event type.
+  * @id:	Associated object ID (e.g. control ID). 0 if there isn't any.
+  * @flags:	Copy of v4l2_event_subscription->flags.
+  * @fh:	Filehandle that subscribed to this event.
+  * @node:	List node that hooks into the object's event list (if there is one).
+  * @replace:	Optional callback that can replace event 'old' with event 'new'.
+  * @merge:	Optional callback that can merge event 'old' into event 'new'.
+  * @elems:	The number of elements in the events array.
+  * @first:	The index of the events containing the oldest available event.
+  * @in_use:	The number of queued events.
+  * @events:	An array of @elems events.
+  */
 struct v4l2_subscribed_event {
 	struct list_head	list;
 	u32			type;
+	u32			id;
+	u32			flags;
+	struct v4l2_fh		*fh;
+	struct list_head	node;
+	void			(*replace)(struct v4l2_event *old,
+					   const struct v4l2_event *new);
+	void			(*merge)(const struct v4l2_event *old,
+					 struct v4l2_event *new);
+	unsigned		elems;
+	unsigned		first;
+	unsigned		in_use;
+	struct v4l2_kevent	events[];
 };
 
-struct v4l2_events {
-	wait_queue_head_t	wait;
-	struct list_head	subscribed; /* Subscribed events */
-	struct list_head	free; /* Events ready for use */
-	struct list_head	available; /* Dequeueable event */
-	unsigned int		navailable;
-	unsigned int		nallocated; /* Number of allocated events */
-	u32			sequence;
-};
-
-int v4l2_event_init(struct v4l2_fh *fh);
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);
-void v4l2_event_free(struct v4l2_fh *fh);
 int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
 		       int nonblocking);
 void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev);
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev);
 int v4l2_event_pending(struct v4l2_fh *fh);
 int v4l2_event_subscribe(struct v4l2_fh *fh,
-			 struct v4l2_event_subscription *sub);
+			 struct v4l2_event_subscription *sub, unsigned elems);
 int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 			   struct v4l2_event_subscription *sub);
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
 
 #endif /* V4L2_EVENT_H */
diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h
index 0206aa5..52513c2 100644
--- a/include/media/v4l2-fh.h
+++ b/include/media/v4l2-fh.h
@@ -29,13 +29,20 @@
 #include <linux/list.h>
 
 struct video_device;
-struct v4l2_events;
+struct v4l2_ctrl_handler;
 
 struct v4l2_fh {
 	struct list_head	list;
 	struct video_device	*vdev;
-	struct v4l2_events      *events; /* events, pending and subscribed */
+	struct v4l2_ctrl_handler *ctrl_handler;
 	enum v4l2_priority	prio;
+
+	/* Events */
+	wait_queue_head_t	wait;
+	struct list_head	subscribed; /* Subscribed events */
+	struct list_head	available; /* Dequeueable event */
+	unsigned int		navailable;
+	u32			sequence;
 };
 
 /*
@@ -44,7 +51,7 @@
  * from driver's v4l2_file_operations->open() handler if the driver
  * uses v4l2_fh.
  */
-int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev);
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev);
 /*
  * Add the fh to the list of file handles on a video_device. The file
  * handle must be initialised first.
diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index 971c7fa..6114007 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -13,6 +13,69 @@
 
 #include <linux/v4l2-mediabus.h>
 
+/* Parallel flags */
+/*
+ * Can the client run in master or in slave mode. By "Master mode" an operation
+ * mode is meant, when the client (e.g., a camera sensor) is producing
+ * horizontal and vertical synchronisation. In "Slave mode" the host is
+ * providing these signals to the slave.
+ */
+#define V4L2_MBUS_MASTER			(1 << 0)
+#define V4L2_MBUS_SLAVE				(1 << 1)
+/* Which signal polarities it supports */
+/* Note: in BT.656 mode HSYNC and VSYNC are unused */
+#define V4L2_MBUS_HSYNC_ACTIVE_HIGH		(1 << 2)
+#define V4L2_MBUS_HSYNC_ACTIVE_LOW		(1 << 3)
+#define V4L2_MBUS_VSYNC_ACTIVE_HIGH		(1 << 4)
+#define V4L2_MBUS_VSYNC_ACTIVE_LOW		(1 << 5)
+#define V4L2_MBUS_PCLK_SAMPLE_RISING		(1 << 6)
+#define V4L2_MBUS_PCLK_SAMPLE_FALLING		(1 << 7)
+#define V4L2_MBUS_DATA_ACTIVE_HIGH		(1 << 8)
+#define V4L2_MBUS_DATA_ACTIVE_LOW		(1 << 9)
+
+/* Serial flags */
+/* How many lanes the client can use */
+#define V4L2_MBUS_CSI2_1_LANE			(1 << 0)
+#define V4L2_MBUS_CSI2_2_LANE			(1 << 1)
+#define V4L2_MBUS_CSI2_3_LANE			(1 << 2)
+#define V4L2_MBUS_CSI2_4_LANE			(1 << 3)
+/* On which channels it can send video data */
+#define V4L2_MBUS_CSI2_CHANNEL_0		(1 << 4)
+#define V4L2_MBUS_CSI2_CHANNEL_1		(1 << 5)
+#define V4L2_MBUS_CSI2_CHANNEL_2		(1 << 6)
+#define V4L2_MBUS_CSI2_CHANNEL_3		(1 << 7)
+/* Does it support only continuous or also non-continuous clock mode */
+#define V4L2_MBUS_CSI2_CONTINUOUS_CLOCK		(1 << 8)
+#define V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK	(1 << 9)
+
+#define V4L2_MBUS_CSI2_LANES		(V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_2_LANE | \
+					 V4L2_MBUS_CSI2_3_LANE | V4L2_MBUS_CSI2_4_LANE)
+#define V4L2_MBUS_CSI2_CHANNELS		(V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CHANNEL_1 | \
+					 V4L2_MBUS_CSI2_CHANNEL_2 | V4L2_MBUS_CSI2_CHANNEL_3)
+
+/**
+ * v4l2_mbus_type - media bus type
+ * @V4L2_MBUS_PARALLEL:	parallel interface with hsync and vsync
+ * @V4L2_MBUS_BT656:	parallel interface with embedded synchronisation, can
+ *			also be used for BT.1120
+ * @V4L2_MBUS_CSI2:	MIPI CSI-2 serial interface
+ */
+enum v4l2_mbus_type {
+	V4L2_MBUS_PARALLEL,
+	V4L2_MBUS_BT656,
+	V4L2_MBUS_CSI2,
+};
+
+/**
+ * v4l2_mbus_config - media bus configuration
+ * @type:	in: interface type
+ * @flags:	in / out: configuration flags, depending on @type
+ */
+struct v4l2_mbus_config {
+	enum v4l2_mbus_type type;
+	unsigned int flags;
+};
+
 static inline void v4l2_fill_pix_format(struct v4l2_pix_format *pix_fmt,
 				const struct v4l2_mbus_framefmt *mbus_fmt)
 {
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 2884e3e..257da1a 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -229,6 +229,12 @@
    s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by
 	video input devices.
 
+   g_std_output: get current standard for video OUTPUT devices. This is ignored
+	by video input devices.
+
+   g_tvnorms_output: get v4l2_std_id with all standards supported by video
+	OUTPUT device. This is ignored by video input devices.
+
    s_crystal_freq: sets the frequency of the crystal used to generate the
 	clocks in Hz. An extra flags field allows device specific configuration
 	regarding clock frequency dividers, etc. If not used, then set flags
@@ -243,6 +249,8 @@
    s_dv_preset: set dv (Digital Video) preset in the sub device. Similar to
 	s_std()
 
+   g_dv_preset: get current dv (Digital Video) preset in the sub device.
+
    query_dv_preset: query dv preset in the sub device. This is similar to
 	querystd()
 
@@ -259,12 +267,20 @@
    try_mbus_fmt: try to set a pixel format on a video data source
 
    s_mbus_fmt: set a pixel format on a video data source
+
+   g_mbus_config: get supported mediabus configurations
+
+   s_mbus_config: set a certain mediabus configuration. This operation is added
+	for compatibility with soc-camera drivers and should not be used by new
+	software.
  */
 struct v4l2_subdev_video_ops {
 	int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
 	int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
 	int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
+	int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
 	int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
+	int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
 	int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
 	int (*s_stream)(struct v4l2_subdev *sd, int enable);
 	int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);
@@ -282,6 +298,8 @@
 			struct v4l2_dv_enum_preset *preset);
 	int (*s_dv_preset)(struct v4l2_subdev *sd,
 			struct v4l2_dv_preset *preset);
+	int (*g_dv_preset)(struct v4l2_subdev *sd,
+			struct v4l2_dv_preset *preset);
 	int (*query_dv_preset)(struct v4l2_subdev *sd,
 			struct v4l2_dv_preset *preset);
 	int (*s_dv_timings)(struct v4l2_subdev *sd,
@@ -298,6 +316,10 @@
 			    struct v4l2_mbus_framefmt *fmt);
 	int (*s_mbus_fmt)(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *fmt);
+	int (*g_mbus_config)(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_config *cfg);
+	int (*s_mbus_config)(struct v4l2_subdev *sd,
+			     const struct v4l2_mbus_config *cfg);
 };
 
 /*
@@ -513,8 +535,6 @@
 	void *host_priv;
 	/* subdev device node */
 	struct video_device devnode;
-	/* number of events to be allocated on open */
-	unsigned int nevents;
 };
 
 #define media_entity_to_v4l2_subdev(ent) \
diff --git a/include/net/ax25.h b/include/net/ax25.h
index 206d222..94e09d3 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -11,7 +11,7 @@
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define	AX25_T1CLAMPLO  		1
 #define	AX25_T1CLAMPHI 			(30 * HZ)
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index abd4436..3b93874 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -41,7 +41,7 @@
 #include <linux/skbuff.h>
 #include <net/netlabel.h>
 #include <net/request_sock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /* known doi values */
 #define CIPSO_V4_DOI_UNKNOWN          0x00000000
diff --git a/include/net/flow.h b/include/net/flow.h
index c6d5fe5..78113da 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -8,7 +8,7 @@
 #define _NET_FLOW_H
 
 #include <linux/in6.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct flowi_common {
 	int	flowic_oif;
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index e9c2ed8..808fc5f 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -33,7 +33,7 @@
 #include <net/tcp_states.h>
 #include <net/netns/hash.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 
 /* This is for all connections with a full identity, no wildcards.
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index 17404b5..f1a7709 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -28,7 +28,7 @@
 #include <net/tcp_states.h>
 #include <net/timewait_sock.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct inet_hashinfo;
 
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
index 4233e6f..78c83e6 100644
--- a/include/net/inetpeer.h
+++ b/include/net/inetpeer.h
@@ -13,7 +13,7 @@
 #include <linux/spinlock.h>
 #include <linux/rtnetlink.h>
 #include <net/ipv6.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct inetpeer_addr_base {
 	union {
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index b1370c4..1aaf915 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -13,7 +13,7 @@
 #include <linux/sysctl.h>               /* for ctl_path */
 #include <linux/list.h>                 /* for struct list_head */
 #include <linux/spinlock.h>             /* for struct rwlock_t */
-#include <asm/atomic.h>                 /* for struct atomic_t */
+#include <linux/atomic.h>                 /* for struct atomic_t */
 #include <linux/compiler.h>
 #include <linux/timer.h>
 
diff --git a/include/net/lib80211.h b/include/net/lib80211.h
index 848cce1..b95bbb0 100644
--- a/include/net/lib80211.h
+++ b/include/net/lib80211.h
@@ -26,7 +26,7 @@
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/if.h>
 #include <linux/skbuff.h>
 #include <linux/ieee80211.h>
diff --git a/include/net/llc.h b/include/net/llc.h
index 5503b74..226c846 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -20,7 +20,7 @@
 #include <linux/hash.h>
 #include <linux/jhash.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct net_device;
 struct packet_type;
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 4ba8521..2720884 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -16,7 +16,7 @@
  *		- Add neighbour cache statistics like rtstat
  */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/rcupdate.h>
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 1ab1aec..3bb6fa0 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -4,7 +4,7 @@
 #ifndef __NET_NET_NAMESPACE_H
 #define __NET_NET_NAMESPACE_H
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/workqueue.h>
 #include <linux/list.h>
 #include <linux/sysctl.h>
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 5d4f8e5..0b7f05e 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -16,7 +16,7 @@
 
 #include <linux/bitops.h>
 #include <linux/compiler.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/netfilter/nf_conntrack_tcp.h>
 #include <linux/netfilter/nf_conntrack_dccp.h>
diff --git a/include/net/netlabel.h b/include/net/netlabel.h
index 9db401a..f21a16e 100644
--- a/include/net/netlabel.h
+++ b/include/net/netlabel.h
@@ -38,7 +38,7 @@
 #include <linux/in6.h>
 #include <net/netlink.h>
 #include <net/request_sock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct cipso_v4_doi;
 
diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h
index 341eb08..0249399 100644
--- a/include/net/netns/conntrack.h
+++ b/include/net/netns/conntrack.h
@@ -3,7 +3,7 @@
 
 #include <linux/list.h>
 #include <linux/list_nulls.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct ctl_table_header;
 struct nf_conntrack_ecache;
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 31d7ea2..f7d9c3f 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -60,7 +60,7 @@
 #include <linux/in6.h>		/* We get struct in6_addr     */
 #include <linux/ipv6.h>
 #include <asm/param.h>		/* We get MAXHOSTNAMELEN.     */
-#include <asm/atomic.h>		/* This gets us atomic counters.  */
+#include <linux/atomic.h>		/* This gets us atomic counters.  */
 #include <linux/skbuff.h>	/* We need sk_buff_head. */
 #include <linux/workqueue.h>	/* We need tq_struct.	 */
 #include <linux/sctp.h>		/* We need sctp* header structs.  */
diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h
index 7b82080..3bbbd78 100644
--- a/include/pcmcia/ds.h
+++ b/include/pcmcia/ds.h
@@ -26,7 +26,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <pcmcia/ss.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 
 /*
diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h
index 1082afa..d44a563 100644
--- a/include/rdma/ib_sa.h
+++ b/include/rdma/ib_sa.h
@@ -38,7 +38,7 @@
 #include <linux/completion.h>
 #include <linux/compiler.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_mad.h>
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index bf4306a..228be3e 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -49,7 +49,7 @@
 #include <linux/scatterlist.h>
 #include <linux/workqueue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 
 extern struct workqueue_struct *ib_wq;
diff --git a/include/rxrpc/types.h b/include/rxrpc/types.h
index 327a5fc..30d48f6 100644
--- a/include/rxrpc/types.h
+++ b/include/rxrpc/types.h
@@ -17,7 +17,7 @@
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 typedef uint32_t	rxrpc_seq_t;	/* Rx message sequence number */
 typedef uint32_t	rxrpc_serial_t;	/* Rx message serial number */
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 4ad0204..8225d80 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -78,7 +78,6 @@
 };
 
 struct fcoe_rcv_info {
-	struct packet_type  *ptype;
 	struct fc_lport	*fr_dev;	/* transport layer private pointer */
 	struct fc_seq	*fr_seq;	/* for use with exchange manager */
 	struct fc_fcp_pkt *fr_fsp;	/* for the corresponding fcp I/O */
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
index ea68b3c..988ba06 100644
--- a/include/scsi/iscsi_proto.h
+++ b/include/scsi/iscsi_proto.h
@@ -29,10 +29,40 @@
 /* default iSCSI listen port for incoming connections */
 #define ISCSI_LISTEN_PORT	3260
 
+/* iSCSI header length */
+#define ISCSI_HDR_LEN		48
+
+/* iSCSI CRC32C length */
+#define ISCSI_CRC_LEN		4
+
 /* Padding word length */
 #define ISCSI_PAD_LEN		4
 
 /*
+ * Serial Number Arithmetic, 32 bits, RFC1982
+ */
+
+static inline int iscsi_sna_lt(u32 n1, u32 n2)
+{
+	return (s32)(n1 - n2) < 0;
+}
+
+static inline int iscsi_sna_lte(u32 n1, u32 n2)
+{
+	return (s32)(n1 - n2) <= 0;
+}
+
+static inline int iscsi_sna_gt(u32 n1, u32 n2)
+{
+	return (s32)(n1 - n2) > 0;
+}
+
+static inline int iscsi_sna_gte(u32 n1, u32 n2)
+{
+	return (s32)(n1 - n2) >= 0;
+}
+
+/*
  * useful common(control and data pathes) macro
  */
 #define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2]))
@@ -116,7 +146,7 @@
 #define ISCSI_CDB_SIZE			16
 
 /* iSCSI PDU Header */
-struct iscsi_cmd {
+struct iscsi_scsi_req {
 	uint8_t opcode;
 	uint8_t flags;
 	__be16 rsvd2;
@@ -161,7 +191,7 @@
 };
 
 /* SCSI Response Header */
-struct iscsi_cmd_rsp {
+struct iscsi_scsi_rsp {
 	uint8_t opcode;
 	uint8_t flags;
 	uint8_t response;
@@ -406,7 +436,7 @@
 };
 
 /* Login Header */
-struct iscsi_login {
+struct iscsi_login_req {
 	uint8_t opcode;
 	uint8_t flags;
 	uint8_t max_version;	/* Max. version supported */
@@ -427,7 +457,13 @@
 #define ISCSI_FLAG_LOGIN_TRANSIT		0x80
 #define ISCSI_FLAG_LOGIN_CONTINUE		0x40
 #define ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK	0x0C	/* 2 bits */
+#define ISCSI_FLAG_LOGIN_CURRENT_STAGE1		0x04
+#define ISCSI_FLAG_LOGIN_CURRENT_STAGE2		0x08
+#define ISCSI_FLAG_LOGIN_CURRENT_STAGE3		0x0C
 #define ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK	0x03	/* 2 bits */
+#define ISCSI_FLAG_LOGIN_NEXT_STAGE1		0x01
+#define ISCSI_FLAG_LOGIN_NEXT_STAGE2		0x02
+#define ISCSI_FLAG_LOGIN_NEXT_STAGE3		0x03
 
 #define ISCSI_LOGIN_CURRENT_STAGE(flags) \
 	((flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2)
@@ -550,17 +586,25 @@
 struct iscsi_snack {
 	uint8_t opcode;
 	uint8_t flags;
-	uint8_t rsvd2[14];
+	uint8_t rsvd2[2];
+	uint8_t hlength;
+	uint8_t dlength[3];
+	uint8_t lun[8];
 	itt_t	 itt;
+	__be32  ttt;
+	uint8_t rsvd3[4];
+	__be32  exp_statsn;
+	uint8_t rsvd4[8];
 	__be32	begrun;
 	__be32	runlength;
-	__be32	exp_statsn;
-	__be32	rsvd3;
-	__be32	exp_datasn;
-	uint8_t rsvd6[8];
 };
 
 /* SNACK PDU flags */
+#define ISCSI_FLAG_SNACK_TYPE_DATA		0
+#define ISCSI_FLAG_SNACK_TYPE_R2T		0
+#define ISCSI_FLAG_SNACK_TYPE_STATUS		1
+#define ISCSI_FLAG_SNACK_TYPE_DATA_ACK		2
+#define ISCSI_FLAG_SNACK_TYPE_RDATA		3
 #define ISCSI_FLAG_SNACK_TYPE_MASK	0x0F	/* 4 bits */
 
 /* Reject Message Header */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index dd82e02..d371c3c 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -7,7 +7,7 @@
 #include <linux/workqueue.h>
 #include <linux/blkdev.h>
 #include <scsi/scsi.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 struct request_queue;
 struct scsi_cmnd;
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index e1bad11..57e71fa 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -507,6 +507,18 @@
 void snd_pcm_vma_notify_data(void *client, void *data);
 int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
 
+
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
+			   char *name, size_t len);
+#else
+static inline void
+snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
+{
+	*buf = 0;
+}
+#endif
+
 /*
  *  PCM library
  */
@@ -749,17 +761,18 @@
 	return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
 }
 
-#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)))
-#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)))
-#define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
-#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min
-#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min
-#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min
-#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8)
-#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
-#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
-#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
-
+#define params_channels(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min)
+#define params_rate(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_RATE)->min)
+#define params_period_size(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min)
+#define params_periods(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIODS)->min)
+#define params_buffer_size(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min)
+#define params_buffer_bytes(p) \
+	(hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min)
 
 int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
 void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c);
diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h
index 85cf1cf..f494f1e 100644
--- a/include/sound/pcm_params.h
+++ b/include/sound/pcm_params.h
@@ -337,5 +337,19 @@
 	return 0;
 }
 
-#endif /* __SOUND_PCM_PARAMS_H */
+#define params_access(p) ((__force snd_pcm_access_t)\
+		snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_ACCESS)))
+#define params_format(p) ((__force snd_pcm_format_t)\
+		snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_FORMAT)))
+#define params_subformat(p) \
+	snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
 
+static inline unsigned int
+params_period_bytes(const struct snd_pcm_hw_params *p)
+{
+	return (params_period_size(p) *
+		snd_pcm_format_physical_width(params_format(p)) *
+		params_channels(p)) / 8;
+}
+
+#endif /* __SOUND_PCM_PARAMS_H */
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index e09505c..e0583b7 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -266,6 +266,12 @@
 	.get = snd_soc_dapm_get_enum_virt, \
 	.put = snd_soc_dapm_put_enum_virt, \
 	.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = xget, \
+	.put = xput, \
+	.private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_enum_double, \
diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h
index d2ea112..726e947 100644
--- a/include/sound/tea575x-tuner.h
+++ b/include/sound/tea575x-tuner.h
@@ -23,8 +23,8 @@
  */
 
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
 
 #define TEA575X_FMIF	10700
 
@@ -42,18 +42,20 @@
 };
 
 struct snd_tea575x {
-	struct video_device *vd;	/* video device */
+	struct video_device vd;		/* video device */
 	bool tea5759;			/* 5759 chip is present */
 	bool mute;			/* Device is muted? */
 	bool stereo;			/* receiving stereo */
 	bool tuned;			/* tuned to a station */
 	unsigned int val;		/* hw value */
 	unsigned long freq;		/* frequency */
-	unsigned long in_use;		/* set if the device is in use */
+	struct mutex mutex;
 	struct snd_tea575x_ops *ops;
 	void *private_data;
 	u8 card[32];
 	u8 bus_info[32];
+	struct v4l2_ctrl_handler ctrl_handler;
+	int (*ext_init)(struct snd_tea575x *tea);
 };
 
 int snd_tea575x_init(struct snd_tea575x *tea);
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index 4114129..b31702a 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -284,7 +284,6 @@
 		__field(	long,   pages_skipped		)
 		__field(	loff_t, range_start		)
 		__field(	loff_t, range_end		)
-		__field(	char,   nonblocking		)
 		__field(	char,   for_kupdate		)
 		__field(	char,   for_reclaim		)
 		__field(	char,   range_cyclic		)
@@ -299,7 +298,6 @@
 		__entry->pages_skipped	= wbc->pages_skipped;
 		__entry->range_start	= wbc->range_start;
 		__entry->range_end	= wbc->range_end;
-		__entry->nonblocking	= wbc->nonblocking;
 		__entry->for_kupdate	= wbc->for_kupdate;
 		__entry->for_reclaim	= wbc->for_reclaim;
 		__entry->range_cyclic	= wbc->range_cyclic;
@@ -310,13 +308,13 @@
 
 	TP_printk("root = %llu(%s), ino = %lu, page_index = %lu, "
 		  "nr_to_write = %ld, pages_skipped = %ld, range_start = %llu, "
-		  "range_end = %llu, nonblocking = %d, for_kupdate = %d, "
+		  "range_end = %llu, for_kupdate = %d, "
 		  "for_reclaim = %d, range_cyclic = %d, writeback_index = %lu",
 		  show_root_type(__entry->root_objectid),
 		  (unsigned long)__entry->ino, __entry->index,
 		  __entry->nr_to_write, __entry->pages_skipped,
 		  __entry->range_start, __entry->range_end,
-		  __entry->nonblocking, __entry->for_kupdate,
+		  __entry->for_kupdate,
 		  __entry->for_reclaim, __entry->range_cyclic,
 		  (unsigned long)__entry->writeback_index)
 );
diff --git a/include/trace/events/ext3.h b/include/trace/events/ext3.h
new file mode 100644
index 0000000..7b53c05
--- /dev/null
+++ b/include/trace/events/ext3.h
@@ -0,0 +1,864 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ext3
+
+#if !defined(_TRACE_EXT3_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_EXT3_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(ext3_free_inode,
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	umode_t, mode			)
+		__field(	uid_t,	uid			)
+		__field(	gid_t,	gid			)
+		__field(	blkcnt_t, blocks		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->mode	= inode->i_mode;
+		__entry->uid	= inode->i_uid;
+		__entry->gid	= inode->i_gid;
+		__entry->blocks	= inode->i_blocks;
+	),
+
+	TP_printk("dev %d,%d ino %lu mode 0%o uid %u gid %u blocks %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->mode, __entry->uid, __entry->gid,
+		  (unsigned long) __entry->blocks)
+);
+
+TRACE_EVENT(ext3_request_inode,
+	TP_PROTO(struct inode *dir, int mode),
+
+	TP_ARGS(dir, mode),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	dir			)
+		__field(	umode_t, mode			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= dir->i_sb->s_dev;
+		__entry->dir	= dir->i_ino;
+		__entry->mode	= mode;
+	),
+
+	TP_printk("dev %d,%d dir %lu mode 0%o",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->dir, __entry->mode)
+);
+
+TRACE_EVENT(ext3_allocate_inode,
+	TP_PROTO(struct inode *inode, struct inode *dir, int mode),
+
+	TP_ARGS(inode, dir, mode),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	ino_t,	dir			)
+		__field(	umode_t, mode			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->dir	= dir->i_ino;
+		__entry->mode	= mode;
+	),
+
+	TP_printk("dev %d,%d ino %lu dir %lu mode 0%o",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long) __entry->dir, __entry->mode)
+);
+
+TRACE_EVENT(ext3_evict_inode,
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	int,	nlink			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->nlink	= inode->i_nlink;
+	),
+
+	TP_printk("dev %d,%d ino %lu nlink %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino, __entry->nlink)
+);
+
+TRACE_EVENT(ext3_drop_inode,
+	TP_PROTO(struct inode *inode, int drop),
+
+	TP_ARGS(inode, drop),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	int,	drop			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->drop	= drop;
+	),
+
+	TP_printk("dev %d,%d ino %lu drop %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino, __entry->drop)
+);
+
+TRACE_EVENT(ext3_mark_inode_dirty,
+	TP_PROTO(struct inode *inode, unsigned long IP),
+
+	TP_ARGS(inode, IP),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(unsigned long,	ip			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->ip	= IP;
+	),
+
+	TP_printk("dev %d,%d ino %lu caller %pF",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino, (void *)__entry->ip)
+);
+
+TRACE_EVENT(ext3_write_begin,
+	TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
+		 unsigned int flags),
+
+	TP_ARGS(inode, pos, len, flags),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	loff_t,	pos			)
+		__field(	unsigned int, len		)
+		__field(	unsigned int, flags		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->pos	= pos;
+		__entry->len	= len;
+		__entry->flags	= flags;
+	),
+
+	TP_printk("dev %d,%d ino %lu pos %llu len %u flags %u",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long long) __entry->pos, __entry->len,
+		  __entry->flags)
+);
+
+DECLARE_EVENT_CLASS(ext3__write_end,
+	TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
+			unsigned int copied),
+
+	TP_ARGS(inode, pos, len, copied),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	loff_t,	pos			)
+		__field(	unsigned int, len		)
+		__field(	unsigned int, copied		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->pos	= pos;
+		__entry->len	= len;
+		__entry->copied	= copied;
+	),
+
+	TP_printk("dev %d,%d ino %lu pos %llu len %u copied %u",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long long) __entry->pos, __entry->len,
+		  __entry->copied)
+);
+
+DEFINE_EVENT(ext3__write_end, ext3_ordered_write_end,
+
+	TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
+		 unsigned int copied),
+
+	TP_ARGS(inode, pos, len, copied)
+);
+
+DEFINE_EVENT(ext3__write_end, ext3_writeback_write_end,
+
+	TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
+		 unsigned int copied),
+
+	TP_ARGS(inode, pos, len, copied)
+);
+
+DEFINE_EVENT(ext3__write_end, ext3_journalled_write_end,
+
+	TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
+		 unsigned int copied),
+
+	TP_ARGS(inode, pos, len, copied)
+);
+
+DECLARE_EVENT_CLASS(ext3__page_op,
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	pgoff_t, index			)
+
+	),
+
+	TP_fast_assign(
+		__entry->index	= page->index;
+		__entry->ino	= page->mapping->host->i_ino;
+		__entry->dev	= page->mapping->host->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu page_index %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino, __entry->index)
+);
+
+DEFINE_EVENT(ext3__page_op, ext3_ordered_writepage,
+
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page)
+);
+
+DEFINE_EVENT(ext3__page_op, ext3_writeback_writepage,
+
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page)
+);
+
+DEFINE_EVENT(ext3__page_op, ext3_journalled_writepage,
+
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page)
+);
+
+DEFINE_EVENT(ext3__page_op, ext3_readpage,
+
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page)
+);
+
+DEFINE_EVENT(ext3__page_op, ext3_releasepage,
+
+	TP_PROTO(struct page *page),
+
+	TP_ARGS(page)
+);
+
+TRACE_EVENT(ext3_invalidatepage,
+	TP_PROTO(struct page *page, unsigned long offset),
+
+	TP_ARGS(page, offset),
+
+	TP_STRUCT__entry(
+		__field(	pgoff_t, index			)
+		__field(	unsigned long, offset		)
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+
+	),
+
+	TP_fast_assign(
+		__entry->index	= page->index;
+		__entry->offset	= offset;
+		__entry->ino	= page->mapping->host->i_ino;
+		__entry->dev	= page->mapping->host->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu page_index %lu offset %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->index, __entry->offset)
+);
+
+TRACE_EVENT(ext3_discard_blocks,
+	TP_PROTO(struct super_block *sb, unsigned long blk,
+			unsigned long count),
+
+	TP_ARGS(sb, blk, count),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,		dev		)
+		__field(	unsigned long,	blk		)
+		__field(	unsigned long,	count		)
+
+	),
+
+	TP_fast_assign(
+		__entry->dev	= sb->s_dev;
+		__entry->blk	= blk;
+		__entry->count	= count;
+	),
+
+	TP_printk("dev %d,%d blk %lu count %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->blk, __entry->count)
+);
+
+TRACE_EVENT(ext3_request_blocks,
+	TP_PROTO(struct inode *inode, unsigned long goal,
+		 unsigned long count),
+
+	TP_ARGS(inode, goal, count),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	unsigned long, count		)
+		__field(	unsigned long,	goal		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->count	= count;
+		__entry->goal	= goal;
+	),
+
+	TP_printk("dev %d,%d ino %lu count %lu goal %lu ",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->count, __entry->goal)
+);
+
+TRACE_EVENT(ext3_allocate_blocks,
+	TP_PROTO(struct inode *inode, unsigned long goal,
+		 unsigned long count, unsigned long block),
+
+	TP_ARGS(inode, goal, count, block),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	unsigned long,	block		)
+		__field(	unsigned long, count		)
+		__field(	unsigned long,	goal		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->block	= block;
+		__entry->count	= count;
+		__entry->goal	= goal;
+	),
+
+	TP_printk("dev %d,%d ino %lu count %lu block %lu goal %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		   __entry->count, __entry->block,
+		  __entry->goal)
+);
+
+TRACE_EVENT(ext3_free_blocks,
+	TP_PROTO(struct inode *inode, unsigned long block,
+		 unsigned long count),
+
+	TP_ARGS(inode, block, count),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	umode_t, mode			)
+		__field(	unsigned long,	block		)
+		__field(	unsigned long,	count		)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= inode->i_sb->s_dev;
+		__entry->ino		= inode->i_ino;
+		__entry->mode		= inode->i_mode;
+		__entry->block		= block;
+		__entry->count		= count;
+	),
+
+	TP_printk("dev %d,%d ino %lu mode 0%o block %lu count %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->mode, __entry->block, __entry->count)
+);
+
+TRACE_EVENT(ext3_sync_file_enter,
+	TP_PROTO(struct file *file, int datasync),
+
+	TP_ARGS(file, datasync),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	ino_t,	parent			)
+		__field(	int,	datasync		)
+	),
+
+	TP_fast_assign(
+		struct dentry *dentry = file->f_path.dentry;
+
+		__entry->dev		= dentry->d_inode->i_sb->s_dev;
+		__entry->ino		= dentry->d_inode->i_ino;
+		__entry->datasync	= datasync;
+		__entry->parent		= dentry->d_parent->d_inode->i_ino;
+	),
+
+	TP_printk("dev %d,%d ino %lu parent %ld datasync %d ",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long) __entry->parent, __entry->datasync)
+);
+
+TRACE_EVENT(ext3_sync_file_exit,
+	TP_PROTO(struct inode *inode, int ret),
+
+	TP_ARGS(inode, ret),
+
+	TP_STRUCT__entry(
+		__field(	int,	ret			)
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+	),
+
+	TP_fast_assign(
+		__entry->ret		= ret;
+		__entry->ino		= inode->i_ino;
+		__entry->dev		= inode->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu ret %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->ret)
+);
+
+TRACE_EVENT(ext3_sync_fs,
+	TP_PROTO(struct super_block *sb, int wait),
+
+	TP_ARGS(sb, wait),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	int,	wait			)
+
+	),
+
+	TP_fast_assign(
+		__entry->dev	= sb->s_dev;
+		__entry->wait	= wait;
+	),
+
+	TP_printk("dev %d,%d wait %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->wait)
+);
+
+TRACE_EVENT(ext3_rsv_window_add,
+	TP_PROTO(struct super_block *sb,
+		 struct ext3_reserve_window_node *rsv_node),
+
+	TP_ARGS(sb, rsv_node),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	start		)
+		__field(	unsigned long,	end		)
+		__field(	dev_t,	dev			)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= sb->s_dev;
+		__entry->start	= rsv_node->rsv_window._rsv_start;
+		__entry->end	= rsv_node->rsv_window._rsv_end;
+	),
+
+	TP_printk("dev %d,%d start %lu end %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->start, __entry->end)
+);
+
+TRACE_EVENT(ext3_discard_reservation,
+	TP_PROTO(struct inode *inode,
+		 struct ext3_reserve_window_node *rsv_node),
+
+	TP_ARGS(inode, rsv_node),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	start		)
+		__field(	unsigned long,	end		)
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+	),
+
+	TP_fast_assign(
+		__entry->start	= rsv_node->rsv_window._rsv_start;
+		__entry->end	= rsv_node->rsv_window._rsv_end;
+		__entry->ino	= inode->i_ino;
+		__entry->dev	= inode->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu start %lu end %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long)__entry->ino, __entry->start,
+		  __entry->end)
+);
+
+TRACE_EVENT(ext3_alloc_new_reservation,
+	TP_PROTO(struct super_block *sb, unsigned long goal),
+
+	TP_ARGS(sb, goal),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	unsigned long,	goal		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= sb->s_dev;
+		__entry->goal	= goal;
+	),
+
+	TP_printk("dev %d,%d goal %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->goal)
+);
+
+TRACE_EVENT(ext3_reserved,
+	TP_PROTO(struct super_block *sb, unsigned long block,
+		 struct ext3_reserve_window_node *rsv_node),
+
+	TP_ARGS(sb, block, rsv_node),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,	block		)
+		__field(	unsigned long,	start		)
+		__field(	unsigned long,	end		)
+		__field(	dev_t,	dev			)
+	),
+
+	TP_fast_assign(
+		__entry->block	= block;
+		__entry->start	= rsv_node->rsv_window._rsv_start;
+		__entry->end	= rsv_node->rsv_window._rsv_end;
+		__entry->dev	= sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d block %lu, start %lu end %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->block, __entry->start, __entry->end)
+);
+
+TRACE_EVENT(ext3_forget,
+	TP_PROTO(struct inode *inode, int is_metadata, unsigned long block),
+
+	TP_ARGS(inode, is_metadata, block),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	ino_t,	ino			)
+		__field(	umode_t, mode			)
+		__field(	int,	is_metadata		)
+		__field(	unsigned long,	block		)
+	),
+
+	TP_fast_assign(
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->ino	= inode->i_ino;
+		__entry->mode	= inode->i_mode;
+		__entry->is_metadata = is_metadata;
+		__entry->block	= block;
+	),
+
+	TP_printk("dev %d,%d ino %lu mode 0%o is_metadata %d block %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->mode, __entry->is_metadata, __entry->block)
+);
+
+TRACE_EVENT(ext3_read_block_bitmap,
+	TP_PROTO(struct super_block *sb, unsigned int group),
+
+	TP_ARGS(sb, group),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	__u32,	group			)
+
+	),
+
+	TP_fast_assign(
+		__entry->dev	= sb->s_dev;
+		__entry->group	= group;
+	),
+
+	TP_printk("dev %d,%d group %u",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->group)
+);
+
+TRACE_EVENT(ext3_direct_IO_enter,
+	TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, int rw),
+
+	TP_ARGS(inode, offset, len, rw),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+		__field(	loff_t,	pos			)
+		__field(	unsigned long,	len		)
+		__field(	int,	rw			)
+	),
+
+	TP_fast_assign(
+		__entry->ino	= inode->i_ino;
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->pos	= offset;
+		__entry->len	= len;
+		__entry->rw	= rw;
+	),
+
+	TP_printk("dev %d,%d ino %lu pos %llu len %lu rw %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long long) __entry->pos, __entry->len,
+		  __entry->rw)
+);
+
+TRACE_EVENT(ext3_direct_IO_exit,
+	TP_PROTO(struct inode *inode, loff_t offset, unsigned long len,
+		 int rw, int ret),
+
+	TP_ARGS(inode, offset, len, rw, ret),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+		__field(	loff_t,	pos			)
+		__field(	unsigned long,	len		)
+		__field(	int,	rw			)
+		__field(	int,	ret			)
+	),
+
+	TP_fast_assign(
+		__entry->ino	= inode->i_ino;
+		__entry->dev	= inode->i_sb->s_dev;
+		__entry->pos	= offset;
+		__entry->len	= len;
+		__entry->rw	= rw;
+		__entry->ret	= ret;
+	),
+
+	TP_printk("dev %d,%d ino %lu pos %llu len %lu rw %d ret %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long long) __entry->pos, __entry->len,
+		  __entry->rw, __entry->ret)
+);
+
+TRACE_EVENT(ext3_unlink_enter,
+	TP_PROTO(struct inode *parent, struct dentry *dentry),
+
+	TP_ARGS(parent, dentry),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,	parent			)
+		__field(	ino_t,	ino			)
+		__field(	loff_t,	size			)
+		__field(	dev_t,	dev			)
+	),
+
+	TP_fast_assign(
+		__entry->parent		= parent->i_ino;
+		__entry->ino		= dentry->d_inode->i_ino;
+		__entry->size		= dentry->d_inode->i_size;
+		__entry->dev		= dentry->d_inode->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu size %lld parent %ld",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  (unsigned long long)__entry->size,
+		  (unsigned long) __entry->parent)
+);
+
+TRACE_EVENT(ext3_unlink_exit,
+	TP_PROTO(struct dentry *dentry, int ret),
+
+	TP_ARGS(dentry, ret),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,	ino			)
+		__field(	dev_t,	dev			)
+		__field(	int,	ret			)
+	),
+
+	TP_fast_assign(
+		__entry->ino		= dentry->d_inode->i_ino;
+		__entry->dev		= dentry->d_inode->i_sb->s_dev;
+		__entry->ret		= ret;
+	),
+
+	TP_printk("dev %d,%d ino %lu ret %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->ret)
+);
+
+DECLARE_EVENT_CLASS(ext3__truncate,
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,		ino		)
+		__field(	dev_t,		dev		)
+		__field(	blkcnt_t,	blocks		)
+	),
+
+	TP_fast_assign(
+		__entry->ino    = inode->i_ino;
+		__entry->dev    = inode->i_sb->s_dev;
+		__entry->blocks	= inode->i_blocks;
+	),
+
+	TP_printk("dev %d,%d ino %lu blocks %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino, (unsigned long) __entry->blocks)
+);
+
+DEFINE_EVENT(ext3__truncate, ext3_truncate_enter,
+
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode)
+);
+
+DEFINE_EVENT(ext3__truncate, ext3_truncate_exit,
+
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode)
+);
+
+TRACE_EVENT(ext3_get_blocks_enter,
+	TP_PROTO(struct inode *inode, unsigned long lblk,
+		 unsigned long len, int create),
+
+	TP_ARGS(inode, lblk, len, create),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,		ino		)
+		__field(	dev_t,		dev		)
+		__field(	unsigned long,	lblk		)
+		__field(	unsigned long,	len		)
+		__field(	int,		create		)
+	),
+
+	TP_fast_assign(
+		__entry->ino    = inode->i_ino;
+		__entry->dev    = inode->i_sb->s_dev;
+		__entry->lblk	= lblk;
+		__entry->len	= len;
+		__entry->create	= create;
+	),
+
+	TP_printk("dev %d,%d ino %lu lblk %lu len %lu create %u",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		  __entry->lblk, __entry->len, __entry->create)
+);
+
+TRACE_EVENT(ext3_get_blocks_exit,
+	TP_PROTO(struct inode *inode, unsigned long lblk,
+		 unsigned long pblk, unsigned long len, int ret),
+
+	TP_ARGS(inode, lblk, pblk, len, ret),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,		ino		)
+		__field(	dev_t,		dev		)
+		__field(	unsigned long,	lblk		)
+		__field(	unsigned long,	pblk		)
+		__field(	unsigned long,	len		)
+		__field(	int,		ret		)
+	),
+
+	TP_fast_assign(
+		__entry->ino    = inode->i_ino;
+		__entry->dev    = inode->i_sb->s_dev;
+		__entry->lblk	= lblk;
+		__entry->pblk	= pblk;
+		__entry->len	= len;
+		__entry->ret	= ret;
+	),
+
+	TP_printk("dev %d,%d ino %lu lblk %lu pblk %lu len %lu ret %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino,
+		   __entry->lblk, __entry->pblk,
+		  __entry->len, __entry->ret)
+);
+
+TRACE_EVENT(ext3_load_inode,
+	TP_PROTO(struct inode *inode),
+
+	TP_ARGS(inode),
+
+	TP_STRUCT__entry(
+		__field(	ino_t,	ino		)
+		__field(	dev_t,	dev		)
+	),
+
+	TP_fast_assign(
+		__entry->ino		= inode->i_ino;
+		__entry->dev		= inode->i_sb->s_dev;
+	),
+
+	TP_printk("dev %d,%d ino %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long) __entry->ino)
+);
+
+#endif /* _TRACE_EXT3_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
index 5ce2b2f..6363193 100644
--- a/include/trace/events/ext4.h
+++ b/include/trace/events/ext4.h
@@ -380,7 +380,6 @@
 		__field(	int,	pages_written		)
 		__field(	long,	pages_skipped		)
 		__field(	int,	sync_mode		)
-		__field(	char,	more_io			)	
 		__field(       pgoff_t,	writeback_index		)
 	),
 
@@ -391,16 +390,15 @@
 		__entry->pages_written	= pages_written;
 		__entry->pages_skipped	= wbc->pages_skipped;
 		__entry->sync_mode	= wbc->sync_mode;
-		__entry->more_io	= wbc->more_io;
 		__entry->writeback_index = inode->i_mapping->writeback_index;
 	),
 
 	TP_printk("dev %d,%d ino %lu ret %d pages_written %d pages_skipped %ld "
-		  " more_io %d sync_mode %d writeback_index %lu",
+		  "sync_mode %d writeback_index %lu",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  (unsigned long) __entry->ino, __entry->ret,
 		  __entry->pages_written, __entry->pages_skipped,
-		  __entry->more_io, __entry->sync_mode,
+		  __entry->sync_mode,
 		  (unsigned long) __entry->writeback_index)
 );
 
diff --git a/include/trace/events/jbd.h b/include/trace/events/jbd.h
new file mode 100644
index 0000000..aff64d8
--- /dev/null
+++ b/include/trace/events/jbd.h
@@ -0,0 +1,203 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM jbd
+
+#if !defined(_TRACE_JBD_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_JBD_H
+
+#include <linux/jbd.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(jbd_checkpoint,
+
+	TP_PROTO(journal_t *journal, int result),
+
+	TP_ARGS(journal, result),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	int,	result			)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->result		= result;
+	),
+
+	TP_printk("dev %d,%d result %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->result)
+);
+
+DECLARE_EVENT_CLASS(jbd_commit,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	char,	sync_commit		)
+		__field(	int,	transaction		)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->sync_commit = commit_transaction->t_synchronous_commit;
+		__entry->transaction	= commit_transaction->t_tid;
+	),
+
+	TP_printk("dev %d,%d transaction %d sync %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->transaction, __entry->sync_commit)
+);
+
+DEFINE_EVENT(jbd_commit, jbd_start_commit,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction)
+);
+
+DEFINE_EVENT(jbd_commit, jbd_commit_locking,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction)
+);
+
+DEFINE_EVENT(jbd_commit, jbd_commit_flushing,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction)
+);
+
+DEFINE_EVENT(jbd_commit, jbd_commit_logging,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction)
+);
+
+TRACE_EVENT(jbd_drop_transaction,
+
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	char,	sync_commit		)
+		__field(	int,	transaction		)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->sync_commit = commit_transaction->t_synchronous_commit;
+		__entry->transaction	= commit_transaction->t_tid;
+	),
+
+	TP_printk("dev %d,%d transaction %d sync %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->transaction, __entry->sync_commit)
+);
+
+TRACE_EVENT(jbd_end_commit,
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	char,	sync_commit		)
+		__field(	int,	transaction		)
+		__field(	int,	head			)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->sync_commit = commit_transaction->t_synchronous_commit;
+		__entry->transaction	= commit_transaction->t_tid;
+		__entry->head		= journal->j_tail_sequence;
+	),
+
+	TP_printk("dev %d,%d transaction %d sync %d head %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->transaction, __entry->sync_commit, __entry->head)
+);
+
+TRACE_EVENT(jbd_do_submit_data,
+	TP_PROTO(journal_t *journal, transaction_t *commit_transaction),
+
+	TP_ARGS(journal, commit_transaction),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	char,	sync_commit		)
+		__field(	int,	transaction		)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->sync_commit = commit_transaction->t_synchronous_commit;
+		__entry->transaction	= commit_transaction->t_tid;
+	),
+
+	TP_printk("dev %d,%d transaction %d sync %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		   __entry->transaction, __entry->sync_commit)
+);
+
+TRACE_EVENT(jbd_cleanup_journal_tail,
+
+	TP_PROTO(journal_t *journal, tid_t first_tid,
+		 unsigned long block_nr, unsigned long freed),
+
+	TP_ARGS(journal, first_tid, block_nr, freed),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	tid_t,	tail_sequence		)
+		__field(	tid_t,	first_tid		)
+		__field(unsigned long,	block_nr		)
+		__field(unsigned long,	freed			)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->tail_sequence	= journal->j_tail_sequence;
+		__entry->first_tid	= first_tid;
+		__entry->block_nr	= block_nr;
+		__entry->freed		= freed;
+	),
+
+	TP_printk("dev %d,%d from %u to %u offset %lu freed %lu",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->tail_sequence, __entry->first_tid,
+		  __entry->block_nr, __entry->freed)
+);
+
+TRACE_EVENT(jbd_update_superblock_end,
+	TP_PROTO(journal_t *journal, int wait),
+
+	TP_ARGS(journal, wait),
+
+	TP_STRUCT__entry(
+		__field(	dev_t,	dev			)
+		__field(	int,	wait			)
+	),
+
+	TP_fast_assign(
+		__entry->dev		= journal->j_fs_dev->bd_dev;
+		__entry->wait		= wait;
+	),
+
+	TP_printk("dev %d,%d wait %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		   __entry->wait)
+);
+
+#endif /* _TRACE_JBD_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 4e249b9..6bca4cc 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -8,6 +8,19 @@
 #include <linux/device.h>
 #include <linux/writeback.h>
 
+#define show_inode_state(state)					\
+	__print_flags(state, "|",				\
+		{I_DIRTY_SYNC,		"I_DIRTY_SYNC"},	\
+		{I_DIRTY_DATASYNC,	"I_DIRTY_DATASYNC"},	\
+		{I_DIRTY_PAGES,		"I_DIRTY_PAGES"},	\
+		{I_NEW,			"I_NEW"},		\
+		{I_WILL_FREE,		"I_WILL_FREE"},		\
+		{I_FREEING,		"I_FREEING"},		\
+		{I_CLEAR,		"I_CLEAR"},		\
+		{I_SYNC,		"I_SYNC"},		\
+		{I_REFERENCED,		"I_REFERENCED"}		\
+	)
+
 struct wb_writeback_work;
 
 DECLARE_EVENT_CLASS(writeback_work_class,
@@ -49,6 +62,9 @@
 DEFINE_WRITEBACK_WORK_EVENT(writeback_nothread);
 DEFINE_WRITEBACK_WORK_EVENT(writeback_queue);
 DEFINE_WRITEBACK_WORK_EVENT(writeback_exec);
+DEFINE_WRITEBACK_WORK_EVENT(writeback_start);
+DEFINE_WRITEBACK_WORK_EVENT(writeback_written);
+DEFINE_WRITEBACK_WORK_EVENT(writeback_wait);
 
 TRACE_EVENT(writeback_pages_written,
 	TP_PROTO(long pages_written),
@@ -88,6 +104,30 @@
 DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister);
 DEFINE_WRITEBACK_EVENT(writeback_thread_start);
 DEFINE_WRITEBACK_EVENT(writeback_thread_stop);
+DEFINE_WRITEBACK_EVENT(balance_dirty_start);
+DEFINE_WRITEBACK_EVENT(balance_dirty_wait);
+
+TRACE_EVENT(balance_dirty_written,
+
+	TP_PROTO(struct backing_dev_info *bdi, int written),
+
+	TP_ARGS(bdi, written),
+
+	TP_STRUCT__entry(
+		__array(char,	name, 32)
+		__field(int,	written)
+	),
+
+	TP_fast_assign(
+		strncpy(__entry->name, dev_name(bdi->dev), 32);
+		__entry->written = written;
+	),
+
+	TP_printk("bdi %s written %d",
+		  __entry->name,
+		  __entry->written
+	)
+);
 
 DECLARE_EVENT_CLASS(wbc_class,
 	TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi),
@@ -101,8 +141,6 @@
 		__field(int, for_background)
 		__field(int, for_reclaim)
 		__field(int, range_cyclic)
-		__field(int, more_io)
-		__field(unsigned long, older_than_this)
 		__field(long, range_start)
 		__field(long, range_end)
 	),
@@ -116,15 +154,12 @@
 		__entry->for_background	= wbc->for_background;
 		__entry->for_reclaim	= wbc->for_reclaim;
 		__entry->range_cyclic	= wbc->range_cyclic;
-		__entry->more_io	= wbc->more_io;
-		__entry->older_than_this = wbc->older_than_this ?
-						*wbc->older_than_this : 0;
 		__entry->range_start	= (long)wbc->range_start;
 		__entry->range_end	= (long)wbc->range_end;
 	),
 
 	TP_printk("bdi %s: towrt=%ld skip=%ld mode=%d kupd=%d "
-		"bgrd=%d reclm=%d cyclic=%d more=%d older=0x%lx "
+		"bgrd=%d reclm=%d cyclic=%d "
 		"start=0x%lx end=0x%lx",
 		__entry->name,
 		__entry->nr_to_write,
@@ -134,8 +169,6 @@
 		__entry->for_background,
 		__entry->for_reclaim,
 		__entry->range_cyclic,
-		__entry->more_io,
-		__entry->older_than_this,
 		__entry->range_start,
 		__entry->range_end)
 )
@@ -144,14 +177,79 @@
 DEFINE_EVENT(wbc_class, name, \
 	TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi), \
 	TP_ARGS(wbc, bdi))
-DEFINE_WBC_EVENT(wbc_writeback_start);
-DEFINE_WBC_EVENT(wbc_writeback_written);
-DEFINE_WBC_EVENT(wbc_writeback_wait);
-DEFINE_WBC_EVENT(wbc_balance_dirty_start);
-DEFINE_WBC_EVENT(wbc_balance_dirty_written);
-DEFINE_WBC_EVENT(wbc_balance_dirty_wait);
 DEFINE_WBC_EVENT(wbc_writepage);
 
+TRACE_EVENT(writeback_queue_io,
+	TP_PROTO(struct bdi_writeback *wb,
+		 unsigned long *older_than_this,
+		 int moved),
+	TP_ARGS(wb, older_than_this, moved),
+	TP_STRUCT__entry(
+		__array(char,		name, 32)
+		__field(unsigned long,	older)
+		__field(long,		age)
+		__field(int,		moved)
+	),
+	TP_fast_assign(
+		strncpy(__entry->name, dev_name(wb->bdi->dev), 32);
+		__entry->older	= older_than_this ?  *older_than_this : 0;
+		__entry->age	= older_than_this ?
+				  (jiffies - *older_than_this) * 1000 / HZ : -1;
+		__entry->moved	= moved;
+	),
+	TP_printk("bdi %s: older=%lu age=%ld enqueue=%d",
+		__entry->name,
+		__entry->older,	/* older_than_this in jiffies */
+		__entry->age,	/* older_than_this in relative milliseconds */
+		__entry->moved)
+);
+
+TRACE_EVENT(global_dirty_state,
+
+	TP_PROTO(unsigned long background_thresh,
+		 unsigned long dirty_thresh
+	),
+
+	TP_ARGS(background_thresh,
+		dirty_thresh
+	),
+
+	TP_STRUCT__entry(
+		__field(unsigned long,	nr_dirty)
+		__field(unsigned long,	nr_writeback)
+		__field(unsigned long,	nr_unstable)
+		__field(unsigned long,	background_thresh)
+		__field(unsigned long,	dirty_thresh)
+		__field(unsigned long,	dirty_limit)
+		__field(unsigned long,	nr_dirtied)
+		__field(unsigned long,	nr_written)
+	),
+
+	TP_fast_assign(
+		__entry->nr_dirty	= global_page_state(NR_FILE_DIRTY);
+		__entry->nr_writeback	= global_page_state(NR_WRITEBACK);
+		__entry->nr_unstable	= global_page_state(NR_UNSTABLE_NFS);
+		__entry->nr_dirtied	= global_page_state(NR_DIRTIED);
+		__entry->nr_written	= global_page_state(NR_WRITTEN);
+		__entry->background_thresh = background_thresh;
+		__entry->dirty_thresh	= dirty_thresh;
+		__entry->dirty_limit = global_dirty_limit;
+	),
+
+	TP_printk("dirty=%lu writeback=%lu unstable=%lu "
+		  "bg_thresh=%lu thresh=%lu limit=%lu "
+		  "dirtied=%lu written=%lu",
+		  __entry->nr_dirty,
+		  __entry->nr_writeback,
+		  __entry->nr_unstable,
+		  __entry->background_thresh,
+		  __entry->dirty_thresh,
+		  __entry->dirty_limit,
+		  __entry->nr_dirtied,
+		  __entry->nr_written
+	)
+);
+
 DECLARE_EVENT_CLASS(writeback_congest_waited_template,
 
 	TP_PROTO(unsigned int usec_timeout, unsigned int usec_delayed),
@@ -187,6 +285,63 @@
 	TP_ARGS(usec_timeout, usec_delayed)
 );
 
+DECLARE_EVENT_CLASS(writeback_single_inode_template,
+
+	TP_PROTO(struct inode *inode,
+		 struct writeback_control *wbc,
+		 unsigned long nr_to_write
+	),
+
+	TP_ARGS(inode, wbc, nr_to_write),
+
+	TP_STRUCT__entry(
+		__array(char, name, 32)
+		__field(unsigned long, ino)
+		__field(unsigned long, state)
+		__field(unsigned long, age)
+		__field(unsigned long, writeback_index)
+		__field(long, nr_to_write)
+		__field(unsigned long, wrote)
+	),
+
+	TP_fast_assign(
+		strncpy(__entry->name,
+			dev_name(inode->i_mapping->backing_dev_info->dev), 32);
+		__entry->ino		= inode->i_ino;
+		__entry->state		= inode->i_state;
+		__entry->age		= (jiffies - inode->dirtied_when) *
+								1000 / HZ;
+		__entry->writeback_index = inode->i_mapping->writeback_index;
+		__entry->nr_to_write	= nr_to_write;
+		__entry->wrote		= nr_to_write - wbc->nr_to_write;
+	),
+
+	TP_printk("bdi %s: ino=%lu state=%s age=%lu "
+		  "index=%lu to_write=%ld wrote=%lu",
+		  __entry->name,
+		  __entry->ino,
+		  show_inode_state(__entry->state),
+		  __entry->age,
+		  __entry->writeback_index,
+		  __entry->nr_to_write,
+		  __entry->wrote
+	)
+);
+
+DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode_requeue,
+	TP_PROTO(struct inode *inode,
+		 struct writeback_control *wbc,
+		 unsigned long nr_to_write),
+	TP_ARGS(inode, wbc, nr_to_write)
+);
+
+DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode,
+	TP_PROTO(struct inode *inode,
+		 struct writeback_control *wbc,
+		 unsigned long nr_to_write),
+	TP_ARGS(inode, wbc, nr_to_write)
+);
+
 #endif /* _TRACE_WRITEBACK_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h
index 44d8dec..92f1a79 100644
--- a/include/trace/events/xen.h
+++ b/include/trace/events/xen.h
@@ -8,6 +8,8 @@
 #include <asm/paravirt_types.h>
 #include <asm/xen/trace_types.h>
 
+struct multicall_entry;
+
 /* Multicalls */
 DECLARE_EVENT_CLASS(xen_mc__batch,
 	    TP_PROTO(enum paravirt_lazy_mode mode),
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index 56410fa..00fba2b 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -31,12 +31,37 @@
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
+
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = get_ipc(table);
 
 	return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
 }
 
+static int proc_ipc_dointvec_minmax(ctl_table *table, int write,
+	void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table ipc_table;
+
+	memcpy(&ipc_table, table, sizeof(ipc_table));
+	ipc_table.data = get_ipc(table);
+
+	return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
+}
+
+static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write,
+	void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+	int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+	if (err < 0)
+		return err;
+	if (ns->shm_rmid_forced)
+		shm_destroy_orphaned(ns);
+	return err;
+}
+
 static int proc_ipc_callback_dointvec(ctl_table *table, int write,
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -125,6 +150,8 @@
 #else
 #define proc_ipc_doulongvec_minmax NULL
 #define proc_ipc_dointvec	   NULL
+#define proc_ipc_dointvec_minmax   NULL
+#define proc_ipc_dointvec_minmax_orphans   NULL
 #define proc_ipc_callback_dointvec NULL
 #define proc_ipcauto_dointvec_minmax NULL
 #endif
@@ -155,6 +182,15 @@
 		.proc_handler	= proc_ipc_dointvec,
 	},
 	{
+		.procname	= "shm_rmid_forced",
+		.data		= &init_ipc_ns.shm_rmid_forced,
+		.maxlen		= sizeof(init_ipc_ns.shm_rmid_forced),
+		.mode		= 0644,
+		.proc_handler	= proc_ipc_dointvec_minmax_orphans,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+	{
 		.procname	= "msgmax",
 		.data		= &init_ipc_ns.msg_ctlmax,
 		.maxlen		= sizeof (init_ipc_ns.msg_ctlmax),
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 14fb6d6..ed049ea 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -113,72 +113,75 @@
 {
 	struct user_struct *u = current_user();
 	struct inode *inode;
+	int ret = -ENOMEM;
 
 	inode = new_inode(sb);
-	if (inode) {
-		inode->i_ino = get_next_ino();
-		inode->i_mode = mode;
-		inode->i_uid = current_fsuid();
-		inode->i_gid = current_fsgid();
-		inode->i_mtime = inode->i_ctime = inode->i_atime =
-				CURRENT_TIME;
+	if (!inode)
+		goto err;
 
-		if (S_ISREG(mode)) {
-			struct mqueue_inode_info *info;
-			struct task_struct *p = current;
-			unsigned long mq_bytes, mq_msg_tblsz;
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+	inode->i_uid = current_fsuid();
+	inode->i_gid = current_fsgid();
+	inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME;
 
-			inode->i_fop = &mqueue_file_operations;
-			inode->i_size = FILENT_SIZE;
-			/* mqueue specific info */
-			info = MQUEUE_I(inode);
-			spin_lock_init(&info->lock);
-			init_waitqueue_head(&info->wait_q);
-			INIT_LIST_HEAD(&info->e_wait_q[0].list);
-			INIT_LIST_HEAD(&info->e_wait_q[1].list);
-			info->notify_owner = NULL;
-			info->qsize = 0;
-			info->user = NULL;	/* set when all is ok */
-			memset(&info->attr, 0, sizeof(info->attr));
-			info->attr.mq_maxmsg = ipc_ns->mq_msg_max;
-			info->attr.mq_msgsize = ipc_ns->mq_msgsize_max;
-			if (attr) {
-				info->attr.mq_maxmsg = attr->mq_maxmsg;
-				info->attr.mq_msgsize = attr->mq_msgsize;
-			}
-			mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
-			info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
-			if (!info->messages)
-				goto out_inode;
+	if (S_ISREG(mode)) {
+		struct mqueue_inode_info *info;
+		struct task_struct *p = current;
+		unsigned long mq_bytes, mq_msg_tblsz;
 
-			mq_bytes = (mq_msg_tblsz +
-				(info->attr.mq_maxmsg * info->attr.mq_msgsize));
-
-			spin_lock(&mq_lock);
-			if (u->mq_bytes + mq_bytes < u->mq_bytes ||
-		 	    u->mq_bytes + mq_bytes >
-			    task_rlimit(p, RLIMIT_MSGQUEUE)) {
-				spin_unlock(&mq_lock);
-				/* mqueue_evict_inode() releases info->messages */
-				goto out_inode;
-			}
-			u->mq_bytes += mq_bytes;
-			spin_unlock(&mq_lock);
-
-			/* all is ok */
-			info->user = get_uid(u);
-		} else if (S_ISDIR(mode)) {
-			inc_nlink(inode);
-			/* Some things misbehave if size == 0 on a directory */
-			inode->i_size = 2 * DIRENT_SIZE;
-			inode->i_op = &mqueue_dir_inode_operations;
-			inode->i_fop = &simple_dir_operations;
+		inode->i_fop = &mqueue_file_operations;
+		inode->i_size = FILENT_SIZE;
+		/* mqueue specific info */
+		info = MQUEUE_I(inode);
+		spin_lock_init(&info->lock);
+		init_waitqueue_head(&info->wait_q);
+		INIT_LIST_HEAD(&info->e_wait_q[0].list);
+		INIT_LIST_HEAD(&info->e_wait_q[1].list);
+		info->notify_owner = NULL;
+		info->qsize = 0;
+		info->user = NULL;	/* set when all is ok */
+		memset(&info->attr, 0, sizeof(info->attr));
+		info->attr.mq_maxmsg = ipc_ns->mq_msg_max;
+		info->attr.mq_msgsize = ipc_ns->mq_msgsize_max;
+		if (attr) {
+			info->attr.mq_maxmsg = attr->mq_maxmsg;
+			info->attr.mq_msgsize = attr->mq_msgsize;
 		}
+		mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
+		info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
+		if (!info->messages)
+			goto out_inode;
+
+		mq_bytes = (mq_msg_tblsz +
+			(info->attr.mq_maxmsg * info->attr.mq_msgsize));
+
+		spin_lock(&mq_lock);
+		if (u->mq_bytes + mq_bytes < u->mq_bytes ||
+		    u->mq_bytes + mq_bytes > task_rlimit(p, RLIMIT_MSGQUEUE)) {
+			spin_unlock(&mq_lock);
+			/* mqueue_evict_inode() releases info->messages */
+			ret = -EMFILE;
+			goto out_inode;
+		}
+		u->mq_bytes += mq_bytes;
+		spin_unlock(&mq_lock);
+
+		/* all is ok */
+		info->user = get_uid(u);
+	} else if (S_ISDIR(mode)) {
+		inc_nlink(inode);
+		/* Some things misbehave if size == 0 on a directory */
+		inode->i_size = 2 * DIRENT_SIZE;
+		inode->i_op = &mqueue_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
 	}
+
 	return inode;
 out_inode:
 	iput(inode);
-	return NULL;
+err:
+	return ERR_PTR(ret);
 }
 
 static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
@@ -194,8 +197,8 @@
 
 	inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO,
 				NULL);
-	if (!inode) {
-		error = -ENOMEM;
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
 		goto out;
 	}
 
@@ -315,8 +318,8 @@
 	spin_unlock(&mq_lock);
 
 	inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr);
-	if (!inode) {
-		error = -ENOMEM;
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
 		spin_lock(&mq_lock);
 		ipc_ns->mq_queues_count--;
 		goto out_unlock;
diff --git a/ipc/shm.c b/ipc/shm.c
index 27884ad..9fb044f3b 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -74,6 +74,7 @@
 	ns->shm_ctlmax = SHMMAX;
 	ns->shm_ctlall = SHMALL;
 	ns->shm_ctlmni = SHMMNI;
+	ns->shm_rmid_forced = 0;
 	ns->shm_tot = 0;
 	ipc_init_ids(&shm_ids(ns));
 }
@@ -130,6 +131,12 @@
 	return container_of(ipcp, struct shmid_kernel, shm_perm);
 }
 
+static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp)
+{
+	rcu_read_lock();
+	spin_lock(&ipcp->shm_perm.lock);
+}
+
 static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
 						int id)
 {
@@ -187,6 +194,23 @@
 }
 
 /*
+ * shm_may_destroy - identifies whether shm segment should be destroyed now
+ *
+ * Returns true if and only if there are no active users of the segment and
+ * one of the following is true:
+ *
+ * 1) shmctl(id, IPC_RMID, NULL) was called for this shp
+ *
+ * 2) sysctl kernel.shm_rmid_forced is set to 1.
+ */
+static bool shm_may_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
+{
+	return (shp->shm_nattch == 0) &&
+	       (ns->shm_rmid_forced ||
+		(shp->shm_perm.mode & SHM_DEST));
+}
+
+/*
  * remove the attach descriptor vma.
  * free memory for segment if it is marked destroyed.
  * The descriptor has already been removed from the current->mm->mmap list
@@ -206,14 +230,87 @@
 	shp->shm_lprid = task_tgid_vnr(current);
 	shp->shm_dtim = get_seconds();
 	shp->shm_nattch--;
-	if(shp->shm_nattch == 0 &&
-	   shp->shm_perm.mode & SHM_DEST)
+	if (shm_may_destroy(ns, shp))
 		shm_destroy(ns, shp);
 	else
 		shm_unlock(shp);
 	up_write(&shm_ids(ns).rw_mutex);
 }
 
+/* Called with ns->shm_ids(ns).rw_mutex locked */
+static int shm_try_destroy_current(int id, void *p, void *data)
+{
+	struct ipc_namespace *ns = data;
+	struct kern_ipc_perm *ipcp = p;
+	struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm);
+
+	if (shp->shm_creator != current)
+		return 0;
+
+	/*
+	 * Mark it as orphaned to destroy the segment when
+	 * kernel.shm_rmid_forced is changed.
+	 * It is noop if the following shm_may_destroy() returns true.
+	 */
+	shp->shm_creator = NULL;
+
+	/*
+	 * Don't even try to destroy it.  If shm_rmid_forced=0 and IPC_RMID
+	 * is not set, it shouldn't be deleted here.
+	 */
+	if (!ns->shm_rmid_forced)
+		return 0;
+
+	if (shm_may_destroy(ns, shp)) {
+		shm_lock_by_ptr(shp);
+		shm_destroy(ns, shp);
+	}
+	return 0;
+}
+
+/* Called with ns->shm_ids(ns).rw_mutex locked */
+static int shm_try_destroy_orphaned(int id, void *p, void *data)
+{
+	struct ipc_namespace *ns = data;
+	struct kern_ipc_perm *ipcp = p;
+	struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm);
+
+	/*
+	 * We want to destroy segments without users and with already
+	 * exit'ed originating process.
+	 *
+	 * As shp->* are changed under rw_mutex, it's safe to skip shp locking.
+	 */
+	if (shp->shm_creator != NULL)
+		return 0;
+
+	if (shm_may_destroy(ns, shp)) {
+		shm_lock_by_ptr(shp);
+		shm_destroy(ns, shp);
+	}
+	return 0;
+}
+
+void shm_destroy_orphaned(struct ipc_namespace *ns)
+{
+	down_write(&shm_ids(ns).rw_mutex);
+	if (&shm_ids(ns).in_use)
+		idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);
+	up_write(&shm_ids(ns).rw_mutex);
+}
+
+
+void exit_shm(struct task_struct *task)
+{
+	struct ipc_namespace *ns = task->nsproxy->ipc_ns;
+
+	/* Destroy all already created segments, but not mapped yet */
+	down_write(&shm_ids(ns).rw_mutex);
+	if (&shm_ids(ns).in_use)
+		idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
+	up_write(&shm_ids(ns).rw_mutex);
+}
+
 static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct file *file = vma->vm_file;
@@ -404,6 +501,7 @@
 	shp->shm_segsz = size;
 	shp->shm_nattch = 0;
 	shp->shm_file = file;
+	shp->shm_creator = current;
 	/*
 	 * shmid gets reported as "inode#" in /proc/pid/maps.
 	 * proc-ps tools use this. Changing this will break them.
@@ -950,8 +1048,7 @@
 	shp = shm_lock(ns, shmid);
 	BUG_ON(IS_ERR(shp));
 	shp->shm_nattch--;
-	if(shp->shm_nattch == 0 &&
-	   shp->shm_perm.mode & SHM_DEST)
+	if (shm_may_destroy(ns, shp))
 		shm_destroy(ns, shp);
 	else
 		shm_unlock(shp);
diff --git a/kernel/audit.c b/kernel/audit.c
index 52501b5..0a1355c 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -43,7 +43,7 @@
 
 #include <linux/init.h>
 #include <asm/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 00d79df..ce4b054 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -44,7 +44,7 @@
 
 #include <linux/init.h>
 #include <asm/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
 #include <linux/mm.h>
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index a63507b9..1d2b6ce 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -27,9 +27,11 @@
  */
 
 #include <linux/cgroup.h>
+#include <linux/cred.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/init_task.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -59,7 +61,7 @@
 #include <linux/poll.h>
 #include <linux/flex_array.h> /* used in cgroup_attach_proc */
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 static DEFINE_MUTEX(cgroup_mutex);
 
@@ -1514,6 +1516,7 @@
 		struct cgroup *root_cgrp = &root->top_cgroup;
 		struct inode *inode;
 		struct cgroupfs_root *existing_root;
+		const struct cred *cred;
 		int i;
 
 		BUG_ON(sb->s_root != NULL);
@@ -1593,7 +1596,9 @@
 		BUG_ON(!list_empty(&root_cgrp->children));
 		BUG_ON(root->number_of_cgroups != 1);
 
+		cred = override_creds(&init_cred);
 		cgroup_populate_dir(root_cgrp);
+		revert_creds(cred);
 		mutex_unlock(&cgroup_mutex);
 		mutex_unlock(&inode->i_mutex);
 	} else {
diff --git a/kernel/compat.c b/kernel/compat.c
index 18197ae..e2435ee 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -158,6 +158,7 @@
 			__put_user(ts->tv_sec, &cts->tv_sec) ||
 			__put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
 }
+EXPORT_SYMBOL_GPL(put_compat_timespec);
 
 static long compat_nanosleep_restart(struct restart_block *restart)
 {
@@ -992,11 +993,8 @@
 	sigset_from_compat(&newset, &newset32);
 	sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP));
 
-	spin_lock_irq(&current->sighand->siglock);
 	current->saved_sigmask = current->blocked;
-	current->blocked = newset;
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
+	set_current_blocked(&newset);
 
 	current->state = TASK_INTERRUPTIBLE;
 	schedule();
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 9c9b754..10131fd 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -55,7 +55,7 @@
 #include <linux/sort.h>
 
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
 #include <linux/cgroup.h>
@@ -2460,11 +2460,19 @@
 
 int cpuset_mem_spread_node(void)
 {
+	if (current->cpuset_mem_spread_rotor == NUMA_NO_NODE)
+		current->cpuset_mem_spread_rotor =
+			node_random(&current->mems_allowed);
+
 	return cpuset_spread_node(&current->cpuset_mem_spread_rotor);
 }
 
 int cpuset_slab_spread_node(void)
 {
+	if (current->cpuset_slab_spread_rotor == NUMA_NO_NODE)
+		current->cpuset_slab_spread_rotor =
+			node_random(&current->mems_allowed);
+
 	return cpuset_spread_node(&current->cpuset_slab_spread_rotor);
 }
 
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index bad6786..0d7c087 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -51,7 +51,7 @@
 
 #include <asm/cacheflush.h>
 #include <asm/byteorder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/system.h>
 
 #include "debug_core.h"
diff --git a/kernel/exit.c b/kernel/exit.c
index 9ee58bb..2913b35 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -980,6 +980,7 @@
 	trace_sched_process_exit(tsk);
 
 	exit_sem(tsk);
+	exit_shm(tsk);
 	exit_files(tsk);
 	exit_fs(tsk);
 	check_stack_usage();
diff --git a/kernel/fork.c b/kernel/fork.c
index 17bf7c8..e7ceaca 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -80,7 +80,7 @@
  * Protected counters by write_lock_irq(&tasklist_lock)
  */
 unsigned long total_forks;	/* Handle normal Linux uptimes. */
-int nr_threads; 		/* The idle threads do not count.. */
+int nr_threads;			/* The idle threads do not count.. */
 
 int max_threads;		/* tunable limit on nr_threads */
 
@@ -232,7 +232,7 @@
 	/*
 	 * we need to allow at least 20 threads to boot a system
 	 */
-	if(max_threads < 20)
+	if (max_threads < 20)
 		max_threads = 20;
 
 	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
@@ -268,7 +268,7 @@
 		return NULL;
 	}
 
- 	err = arch_dup_task_struct(tsk, orig);
+	err = arch_dup_task_struct(tsk, orig);
 	if (err)
 		goto out;
 
@@ -288,8 +288,11 @@
 	tsk->stack_canary = get_random_int();
 #endif
 
-	/* One for us, one for whoever does the "release_task()" (usually parent) */
-	atomic_set(&tsk->usage,2);
+	/*
+	 * One for us, one for whoever does the "release_task()" (usually
+	 * parent)
+	 */
+	atomic_set(&tsk->usage, 2);
 #ifdef CONFIG_BLK_DEV_IO_TRACE
 	tsk->btrace_seq = 0;
 #endif
@@ -437,7 +440,7 @@
 	goto out;
 }
 
-static inline int mm_alloc_pgd(struct mm_struct * mm)
+static inline int mm_alloc_pgd(struct mm_struct *mm)
 {
 	mm->pgd = pgd_alloc(mm);
 	if (unlikely(!mm->pgd))
@@ -445,7 +448,7 @@
 	return 0;
 }
 
-static inline void mm_free_pgd(struct mm_struct * mm)
+static inline void mm_free_pgd(struct mm_struct *mm)
 {
 	pgd_free(mm, mm->pgd);
 }
@@ -482,7 +485,7 @@
 #endif
 }
 
-static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
+static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
 {
 	atomic_set(&mm->mm_users, 1);
 	atomic_set(&mm->mm_count, 1);
@@ -513,9 +516,9 @@
 /*
  * Allocate and initialize an mm_struct.
  */
-struct mm_struct * mm_alloc(void)
+struct mm_struct *mm_alloc(void)
 {
-	struct mm_struct * mm;
+	struct mm_struct *mm;
 
 	mm = allocate_mm();
 	if (!mm)
@@ -583,7 +586,7 @@
 void removed_exe_file_vma(struct mm_struct *mm)
 {
 	mm->num_exe_file_vmas--;
-	if ((mm->num_exe_file_vmas == 0) && mm->exe_file){
+	if ((mm->num_exe_file_vmas == 0) && mm->exe_file) {
 		fput(mm->exe_file);
 		mm->exe_file = NULL;
 	}
@@ -775,9 +778,9 @@
 	return NULL;
 }
 
-static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
+static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
 {
-	struct mm_struct * mm, *oldmm;
+	struct mm_struct *mm, *oldmm;
 	int retval;
 
 	tsk->min_flt = tsk->maj_flt = 0;
@@ -844,7 +847,7 @@
 	return 0;
 }
 
-static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
 {
 	struct files_struct *oldf, *newf;
 	int error = 0;
@@ -1166,13 +1169,17 @@
 	cgroup_fork(p);
 #ifdef CONFIG_NUMA
 	p->mempolicy = mpol_dup(p->mempolicy);
- 	if (IS_ERR(p->mempolicy)) {
- 		retval = PTR_ERR(p->mempolicy);
- 		p->mempolicy = NULL;
- 		goto bad_fork_cleanup_cgroup;
- 	}
+	if (IS_ERR(p->mempolicy)) {
+		retval = PTR_ERR(p->mempolicy);
+		p->mempolicy = NULL;
+		goto bad_fork_cleanup_cgroup;
+	}
 	mpol_fix_fork_child_flag(p);
 #endif
+#ifdef CONFIG_CPUSETS
+	p->cpuset_mem_spread_rotor = NUMA_NO_NODE;
+	p->cpuset_slab_spread_rotor = NUMA_NO_NODE;
+#endif
 #ifdef CONFIG_TRACE_IRQFLAGS
 	p->irq_events = 0;
 #ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
@@ -1212,25 +1219,33 @@
 	retval = perf_event_init_task(p);
 	if (retval)
 		goto bad_fork_cleanup_policy;
-
-	if ((retval = audit_alloc(p)))
+	retval = audit_alloc(p);
+	if (retval)
 		goto bad_fork_cleanup_policy;
 	/* copy all the process information */
-	if ((retval = copy_semundo(clone_flags, p)))
+	retval = copy_semundo(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_audit;
-	if ((retval = copy_files(clone_flags, p)))
+	retval = copy_files(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_semundo;
-	if ((retval = copy_fs(clone_flags, p)))
+	retval = copy_fs(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_files;
-	if ((retval = copy_sighand(clone_flags, p)))
+	retval = copy_sighand(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_fs;
-	if ((retval = copy_signal(clone_flags, p)))
+	retval = copy_signal(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_sighand;
-	if ((retval = copy_mm(clone_flags, p)))
+	retval = copy_mm(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_signal;
-	if ((retval = copy_namespaces(clone_flags, p)))
+	retval = copy_namespaces(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_mm;
-	if ((retval = copy_io(clone_flags, p)))
+	retval = copy_io(clone_flags, p);
+	if (retval)
 		goto bad_fork_cleanup_namespaces;
 	retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);
 	if (retval)
@@ -1252,7 +1267,7 @@
 	/*
 	 * Clear TID on mm_release()?
 	 */
-	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
+	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL;
 #ifdef CONFIG_BLOCK
 	p->plug = NULL;
 #endif
@@ -1320,7 +1335,7 @@
 	 * it's process group.
 	 * A fatal signal pending means that current will exit, so the new
 	 * thread can't slip out of an OOM kill (or normal SIGKILL).
- 	 */
+	*/
 	recalc_sigpending();
 	if (signal_pending(current)) {
 		spin_unlock(&current->sighand->siglock);
@@ -1681,12 +1696,14 @@
 	 */
 	if (unshare_flags & (CLONE_NEWIPC|CLONE_SYSVSEM))
 		do_sysvsem = 1;
-	if ((err = unshare_fs(unshare_flags, &new_fs)))
+	err = unshare_fs(unshare_flags, &new_fs);
+	if (err)
 		goto bad_unshare_out;
-	if ((err = unshare_fd(unshare_flags, &new_fd)))
+	err = unshare_fd(unshare_flags, &new_fd);
+	if (err)
 		goto bad_unshare_cleanup_fs;
-	if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy,
-			new_fs)))
+	err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, new_fs);
+	if (err)
 		goto bad_unshare_cleanup_fd;
 
 	if (new_fs || new_fd || do_sysvsem || new_nsproxy) {
diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig
index 5bf924d..a920281 100644
--- a/kernel/gcov/Kconfig
+++ b/kernel/gcov/Kconfig
@@ -3,7 +3,7 @@
 config GCOV_KERNEL
 	bool "Enable gcov-based kernel profiling"
 	depends on DEBUG_FS
-	select CONSTRUCTORS
+	select CONSTRUCTORS if !UML
 	default n
 	---help---
 	This option enables gcov-based code profiling (e.g. for code coverage
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index d1d051b3..5a38bf4 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -52,6 +52,10 @@
 config GENERIC_IRQ_CHIP
        bool
 
+# Generic irq_domain hw <--> linux irq number translation
+config IRQ_DOMAIN
+	bool
+
 # Support forced irq threading
 config IRQ_FORCED_THREADING
        bool
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 7329005..fff1738 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -2,6 +2,7 @@
 obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
 obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
 obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
+obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
 obj-$(CONFIG_PM_SLEEP) += pm.o
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
new file mode 100644
index 0000000..d5828da
--- /dev/null
+++ b/kernel/irq/irqdomain.c
@@ -0,0 +1,180 @@
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+static LIST_HEAD(irq_domain_list);
+static DEFINE_MUTEX(irq_domain_mutex);
+
+/**
+ * irq_domain_add() - Register an irq_domain
+ * @domain: ptr to initialized irq_domain structure
+ *
+ * Registers an irq_domain structure.  The irq_domain must at a minimum be
+ * initialized with an ops structure pointer, and either a ->to_irq hook or
+ * a valid irq_base value.  Everything else is optional.
+ */
+void irq_domain_add(struct irq_domain *domain)
+{
+	struct irq_data *d;
+	int hwirq;
+
+	/*
+	 * This assumes that the irq_domain owner has already allocated
+	 * the irq_descs.  This block will be removed when support for dynamic
+	 * allocation of irq_descs is added to irq_domain.
+	 */
+	for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
+		d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
+		if (d || d->domain) {
+			/* things are broken; just report, don't clean up */
+			WARN(1, "error: irq_desc already assigned to a domain");
+			return;
+		}
+		d->domain = domain;
+		d->hwirq = hwirq;
+	}
+
+	mutex_lock(&irq_domain_mutex);
+	list_add(&domain->list, &irq_domain_list);
+	mutex_unlock(&irq_domain_mutex);
+}
+
+/**
+ * irq_domain_del() - Unregister an irq_domain
+ * @domain: ptr to registered irq_domain.
+ */
+void irq_domain_del(struct irq_domain *domain)
+{
+	struct irq_data *d;
+	int hwirq;
+
+	mutex_lock(&irq_domain_mutex);
+	list_del(&domain->list);
+	mutex_unlock(&irq_domain_mutex);
+
+	/* Clear the irq_domain assignments */
+	for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
+		d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
+		d->domain = NULL;
+	}
+}
+
+#if defined(CONFIG_OF_IRQ)
+/**
+ * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
+ *
+ * Used by the device tree interrupt mapping code to translate a device tree
+ * interrupt specifier to a valid linux irq number.  Returns either a valid
+ * linux IRQ number or 0.
+ *
+ * When the caller no longer need the irq number returned by this function it
+ * should arrange to call irq_dispose_mapping().
+ */
+unsigned int irq_create_of_mapping(struct device_node *controller,
+				   const u32 *intspec, unsigned int intsize)
+{
+	struct irq_domain *domain;
+	unsigned long hwirq;
+	unsigned int irq, type;
+	int rc = -EINVAL;
+
+	/* Find a domain which can translate the irq spec */
+	mutex_lock(&irq_domain_mutex);
+	list_for_each_entry(domain, &irq_domain_list, list) {
+		if (!domain->ops->dt_translate)
+			continue;
+		rc = domain->ops->dt_translate(domain, controller,
+					intspec, intsize, &hwirq, &type);
+		if (rc == 0)
+			break;
+	}
+	mutex_unlock(&irq_domain_mutex);
+
+	if (rc != 0)
+		return 0;
+
+	irq = irq_domain_to_irq(domain, hwirq);
+	if (type != IRQ_TYPE_NONE)
+		irq_set_irq_type(irq, type);
+	pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
+		 controller->full_name, (int)hwirq, irq, type);
+	return irq;
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+
+/**
+ * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
+ * @irq: linux irq number to be discarded
+ *
+ * Calling this function indicates the caller no longer needs a reference to
+ * the linux irq number returned by a prior call to irq_create_of_mapping().
+ */
+void irq_dispose_mapping(unsigned int irq)
+{
+	/*
+	 * nothing yet; will be filled when support for dynamic allocation of
+	 * irq_descs is added to irq_domain
+	 */
+}
+EXPORT_SYMBOL_GPL(irq_dispose_mapping);
+
+int irq_domain_simple_dt_translate(struct irq_domain *d,
+			    struct device_node *controller,
+			    const u32 *intspec, unsigned int intsize,
+			    unsigned long *out_hwirq, unsigned int *out_type)
+{
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize < 1)
+		return -EINVAL;
+
+	*out_hwirq = intspec[0];
+	*out_type = IRQ_TYPE_NONE;
+	if (intsize > 1)
+		*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+struct irq_domain_ops irq_domain_simple_ops = {
+	.dt_translate = irq_domain_simple_dt_translate,
+};
+EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
+
+/**
+ * irq_domain_create_simple() - Set up a 'simple' translation range
+ */
+void irq_domain_add_simple(struct device_node *controller, int irq_base)
+{
+	struct irq_domain *domain;
+
+	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+	if (!domain) {
+		WARN_ON(1);
+		return;
+	}
+
+	domain->irq_base = irq_base;
+	domain->of_node = of_node_get(controller);
+	domain->ops = &irq_domain_simple_ops;
+	irq_domain_add(domain);
+}
+EXPORT_SYMBOL_GPL(irq_domain_add_simple);
+
+void irq_domain_generate_simple(const struct of_device_id *match,
+				u64 phys_base, unsigned int irq_start)
+{
+	struct device_node *node;
+	pr_info("looking for phys_base=%llx, irq_start=%i\n",
+		(unsigned long long) phys_base, (int) irq_start);
+	node = of_find_matching_node_by_address(NULL, match, phys_base);
+	if (node)
+		irq_domain_add_simple(node, irq_start);
+	else
+		pr_info("no node found\n");
+}
+EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
+#endif /* CONFIG_OF_IRQ */
diff --git a/kernel/panic.c b/kernel/panic.c
index 6923167..d7bb697 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -119,6 +119,8 @@
 			}
 			mdelay(PANIC_TIMER_STEP);
 		}
+	}
+	if (panic_timeout != 0) {
 		/*
 		 * This will not be a clean reboot, with everything
 		 * shutting down.  But if there is a chance of
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index 7784bd2..ddddb32 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -37,7 +37,7 @@
 #include <linux/smp.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/percpu.h>
 #include <linux/notifier.h>
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index ced7210..98f51b1 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -33,7 +33,7 @@
 #include <linux/rcupdate.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/completion.h>
 #include <linux/moduleparam.h>
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c
index 4e14487..3b0c098 100644
--- a/kernel/rcutree_trace.c
+++ b/kernel/rcutree_trace.c
@@ -31,7 +31,7 @@
 #include <linux/rcupdate.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/completion.h>
diff --git a/kernel/rwsem.c b/kernel/rwsem.c
index 176e5e5..9f48f3d 100644
--- a/kernel/rwsem.c
+++ b/kernel/rwsem.c
@@ -11,7 +11,7 @@
 #include <linux/rwsem.h>
 
 #include <asm/system.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * lock for reading
diff --git a/kernel/signal.c b/kernel/signal.c
index d7f70ae..291c970 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3102,15 +3102,11 @@
 
 SYSCALL_DEFINE1(ssetmask, int, newmask)
 {
-	int old;
+	int old = current->blocked.sig[0];
+	sigset_t newset;
 
-	spin_lock_irq(&current->sighand->siglock);
-	old = current->blocked.sig[0];
-
-	siginitset(&current->blocked, newmask & ~(sigmask(SIGKILL)|
-						  sigmask(SIGSTOP)));
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
+	siginitset(&newset, newmask & ~(sigmask(SIGKILL) | sigmask(SIGSTOP)));
+	set_current_blocked(&newset);
 
 	return old;
 }
@@ -3167,11 +3163,8 @@
 		return -EFAULT;
 	sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP));
 
-	spin_lock_irq(&current->sighand->siglock);
 	current->saved_sigmask = current->blocked;
-	current->blocked = newset;
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
+	set_current_blocked(&newset);
 
 	current->state = TASK_INTERRUPTIBLE;
 	schedule();
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index c112475..ba5070c 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -19,7 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/kallsyms.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * Structure to determine completion condition and record errors.  May
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index fc0f220..d1db288 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -28,7 +28,7 @@
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <net/genetlink.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * Maximum length of a cpumask that can be specified in
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 3f381d0..616846b 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -2,7 +2,7 @@
 #define _LINUX_KERNEL_TRACE_H
 
 #include <linux/fs.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/sched.h>
 #include <linux/clocksource.h>
 #include <linux/ring_buffer.h>
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index 017fa37..fd3c8aa 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "trace.h"
 #include "trace_output.h"
diff --git a/lib/atomic64.c b/lib/atomic64.c
index a21c12b..e12ae0d 100644
--- a/lib/atomic64.c
+++ b/lib/atomic64.c
@@ -14,7 +14,7 @@
 #include <linux/spinlock.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * We use a hashed array of spinlocks to provide exclusive access
diff --git a/lib/atomic64_test.c b/lib/atomic64_test.c
index 44524cc..0c33cde 100644
--- a/lib/atomic64_test.c
+++ b/lib/atomic64_test.c
@@ -10,7 +10,7 @@
  */
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #define INIT(c) do { atomic64_set(&v, c); r = c; } while (0)
 static __init int test_atomic64(void)
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 3f3b681..37ef4b0 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -756,7 +756,7 @@
  *
  * The bit positions 0 through @bits are valid positions in @buf.
  */
-static int bitmap_ord_to_pos(const unsigned long *buf, int ord, int bits)
+int bitmap_ord_to_pos(const unsigned long *buf, int ord, int bits)
 {
 	int pos = 0;
 
diff --git a/lib/cpumask.c b/lib/cpumask.c
index 05d6aca..af3e5817 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -30,7 +30,7 @@
 {
 	int cpu;
 
-	for_each_cpu_mask(cpu, *mask) {
+	for_each_cpu(cpu, mask) {
 		if (cpu_online(cpu))
 			break;
 	}
@@ -131,7 +131,7 @@
  */
 bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
 {
-	return alloc_cpumask_var_node(mask, flags, numa_node_id());
+	return alloc_cpumask_var_node(mask, flags, NUMA_NO_NODE);
 }
 EXPORT_SYMBOL(alloc_cpumask_var);
 
diff --git a/lib/crc32.c b/lib/crc32.c
index 4855995..a6e633a 100644
--- a/lib/crc32.c
+++ b/lib/crc32.c
@@ -26,7 +26,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/init.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "crc32defs.h"
 #if CRC_LE_BITS == 8
 # define tole(x) __constant_cpu_to_le32(x)
diff --git a/lib/dec_and_lock.c b/lib/dec_and_lock.c
index e73822a..b525772 100644
--- a/lib/dec_and_lock.c
+++ b/lib/dec_and_lock.c
@@ -1,6 +1,6 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * This is an implementation of the notion of "decrement a
diff --git a/lib/fault-inject.c b/lib/fault-inject.c
index 7e65af7..2577b12 100644
--- a/lib/fault-inject.c
+++ b/lib/fault-inject.c
@@ -8,7 +8,6 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/stacktrace.h>
-#include <linux/kallsyms.h>
 #include <linux/fault-inject.h>
 
 /*
@@ -140,16 +139,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
-static int debugfs_ul_set_MAX_STACK_TRACE_DEPTH(void *data, u64 val)
-{
-	*(unsigned long *)data =
-		val < MAX_STACK_TRACE_DEPTH ?
-		val : MAX_STACK_TRACE_DEPTH;
-	return 0;
-}
-#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
-
 static int debugfs_ul_get(void *data, u64 *val)
 {
 	*val = *(unsigned long *)data;
@@ -165,16 +154,26 @@
 }
 
 #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
-DEFINE_SIMPLE_ATTRIBUTE(fops_ul_MAX_STACK_TRACE_DEPTH, debugfs_ul_get,
-			debugfs_ul_set_MAX_STACK_TRACE_DEPTH, "%llu\n");
 
-static struct dentry *debugfs_create_ul_MAX_STACK_TRACE_DEPTH(
+static int debugfs_stacktrace_depth_set(void *data, u64 val)
+{
+	*(unsigned long *)data =
+		min_t(unsigned long, val, MAX_STACK_TRACE_DEPTH);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_stacktrace_depth, debugfs_ul_get,
+			debugfs_stacktrace_depth_set, "%llu\n");
+
+static struct dentry *debugfs_create_stacktrace_depth(
 	const char *name, mode_t mode,
 	struct dentry *parent, unsigned long *value)
 {
 	return debugfs_create_file(name, mode, parent, value,
-				   &fops_ul_MAX_STACK_TRACE_DEPTH);
+				   &fops_stacktrace_depth);
 }
+
 #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 
 static int debugfs_atomic_t_set(void *data, u64 val)
@@ -200,48 +199,7 @@
 
 void cleanup_fault_attr_dentries(struct fault_attr *attr)
 {
-	debugfs_remove(attr->dentries.probability_file);
-	attr->dentries.probability_file = NULL;
-
-	debugfs_remove(attr->dentries.interval_file);
-	attr->dentries.interval_file = NULL;
-
-	debugfs_remove(attr->dentries.times_file);
-	attr->dentries.times_file = NULL;
-
-	debugfs_remove(attr->dentries.space_file);
-	attr->dentries.space_file = NULL;
-
-	debugfs_remove(attr->dentries.verbose_file);
-	attr->dentries.verbose_file = NULL;
-
-	debugfs_remove(attr->dentries.task_filter_file);
-	attr->dentries.task_filter_file = NULL;
-
-#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
-
-	debugfs_remove(attr->dentries.stacktrace_depth_file);
-	attr->dentries.stacktrace_depth_file = NULL;
-
-	debugfs_remove(attr->dentries.require_start_file);
-	attr->dentries.require_start_file = NULL;
-
-	debugfs_remove(attr->dentries.require_end_file);
-	attr->dentries.require_end_file = NULL;
-
-	debugfs_remove(attr->dentries.reject_start_file);
-	attr->dentries.reject_start_file = NULL;
-
-	debugfs_remove(attr->dentries.reject_end_file);
-	attr->dentries.reject_end_file = NULL;
-
-#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
-
-	if (attr->dentries.dir)
-		WARN_ON(!simple_empty(attr->dentries.dir));
-
-	debugfs_remove(attr->dentries.dir);
-	attr->dentries.dir = NULL;
+	debugfs_remove_recursive(attr->dir);
 }
 
 int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
@@ -249,66 +207,46 @@
 	mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
 	struct dentry *dir;
 
-	memset(&attr->dentries, 0, sizeof(attr->dentries));
-
 	dir = debugfs_create_dir(name, NULL);
 	if (!dir)
+		return -ENOMEM;
+
+	attr->dir = dir;
+
+	if (!debugfs_create_ul("probability", mode, dir, &attr->probability))
 		goto fail;
-	attr->dentries.dir = dir;
-
-	attr->dentries.probability_file =
-		debugfs_create_ul("probability", mode, dir, &attr->probability);
-
-	attr->dentries.interval_file =
-		debugfs_create_ul("interval", mode, dir, &attr->interval);
-
-	attr->dentries.times_file =
-		debugfs_create_atomic_t("times", mode, dir, &attr->times);
-
-	attr->dentries.space_file =
-		debugfs_create_atomic_t("space", mode, dir, &attr->space);
-
-	attr->dentries.verbose_file =
-		debugfs_create_ul("verbose", mode, dir, &attr->verbose);
-
-	attr->dentries.task_filter_file = debugfs_create_bool("task-filter",
-						mode, dir, &attr->task_filter);
-
-	if (!attr->dentries.probability_file || !attr->dentries.interval_file ||
-	    !attr->dentries.times_file || !attr->dentries.space_file ||
-	    !attr->dentries.verbose_file || !attr->dentries.task_filter_file)
+	if (!debugfs_create_ul("interval", mode, dir, &attr->interval))
+		goto fail;
+	if (!debugfs_create_atomic_t("times", mode, dir, &attr->times))
+		goto fail;
+	if (!debugfs_create_atomic_t("space", mode, dir, &attr->space))
+		goto fail;
+	if (!debugfs_create_ul("verbose", mode, dir, &attr->verbose))
+		goto fail;
+	if (!debugfs_create_bool("task-filter", mode, dir, &attr->task_filter))
 		goto fail;
 
 #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 
-	attr->dentries.stacktrace_depth_file =
-		debugfs_create_ul_MAX_STACK_TRACE_DEPTH(
-			"stacktrace-depth", mode, dir, &attr->stacktrace_depth);
-
-	attr->dentries.require_start_file =
-		debugfs_create_ul("require-start", mode, dir, &attr->require_start);
-
-	attr->dentries.require_end_file =
-		debugfs_create_ul("require-end", mode, dir, &attr->require_end);
-
-	attr->dentries.reject_start_file =
-		debugfs_create_ul("reject-start", mode, dir, &attr->reject_start);
-
-	attr->dentries.reject_end_file =
-		debugfs_create_ul("reject-end", mode, dir, &attr->reject_end);
-
-	if (!attr->dentries.stacktrace_depth_file ||
-	    !attr->dentries.require_start_file ||
-	    !attr->dentries.require_end_file ||
-	    !attr->dentries.reject_start_file ||
-	    !attr->dentries.reject_end_file)
+	if (!debugfs_create_stacktrace_depth("stacktrace-depth", mode, dir,
+				&attr->stacktrace_depth))
+		goto fail;
+	if (!debugfs_create_ul("require-start", mode, dir,
+				&attr->require_start))
+		goto fail;
+	if (!debugfs_create_ul("require-end", mode, dir, &attr->require_end))
+		goto fail;
+	if (!debugfs_create_ul("reject-start", mode, dir, &attr->reject_start))
+		goto fail;
+	if (!debugfs_create_ul("reject-end", mode, dir, &attr->reject_end))
 		goto fail;
 
 #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 
 	return 0;
 fail:
-	cleanup_fault_attr_dentries(attr);
+	debugfs_remove_recursive(attr->dir);
+
 	return -ENOMEM;
 }
 
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 8290b1e..d6edf8d 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -45,6 +45,17 @@
 static int bdi_sync_supers(void *);
 static void sync_supers_timer_fn(unsigned long);
 
+void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2)
+{
+	if (wb1 < wb2) {
+		spin_lock(&wb1->list_lock);
+		spin_lock_nested(&wb2->list_lock, 1);
+	} else {
+		spin_lock(&wb2->list_lock);
+		spin_lock_nested(&wb1->list_lock, 1);
+	}
+}
+
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
@@ -67,34 +78,42 @@
 	struct inode *inode;
 
 	nr_dirty = nr_io = nr_more_io = 0;
-	spin_lock(&inode_wb_list_lock);
+	spin_lock(&wb->list_lock);
 	list_for_each_entry(inode, &wb->b_dirty, i_wb_list)
 		nr_dirty++;
 	list_for_each_entry(inode, &wb->b_io, i_wb_list)
 		nr_io++;
 	list_for_each_entry(inode, &wb->b_more_io, i_wb_list)
 		nr_more_io++;
-	spin_unlock(&inode_wb_list_lock);
+	spin_unlock(&wb->list_lock);
 
 	global_dirty_limits(&background_thresh, &dirty_thresh);
 	bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh);
 
 #define K(x) ((x) << (PAGE_SHIFT - 10))
 	seq_printf(m,
-		   "BdiWriteback:     %8lu kB\n"
-		   "BdiReclaimable:   %8lu kB\n"
-		   "BdiDirtyThresh:   %8lu kB\n"
-		   "DirtyThresh:      %8lu kB\n"
-		   "BackgroundThresh: %8lu kB\n"
-		   "b_dirty:          %8lu\n"
-		   "b_io:             %8lu\n"
-		   "b_more_io:        %8lu\n"
-		   "bdi_list:         %8u\n"
-		   "state:            %8lx\n",
+		   "BdiWriteback:       %10lu kB\n"
+		   "BdiReclaimable:     %10lu kB\n"
+		   "BdiDirtyThresh:     %10lu kB\n"
+		   "DirtyThresh:        %10lu kB\n"
+		   "BackgroundThresh:   %10lu kB\n"
+		   "BdiWritten:         %10lu kB\n"
+		   "BdiWriteBandwidth:  %10lu kBps\n"
+		   "b_dirty:            %10lu\n"
+		   "b_io:               %10lu\n"
+		   "b_more_io:          %10lu\n"
+		   "bdi_list:           %10u\n"
+		   "state:              %10lx\n",
 		   (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)),
 		   (unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)),
-		   K(bdi_thresh), K(dirty_thresh),
-		   K(background_thresh), nr_dirty, nr_io, nr_more_io,
+		   K(bdi_thresh),
+		   K(dirty_thresh),
+		   K(background_thresh),
+		   (unsigned long) K(bdi_stat(bdi, BDI_WRITTEN)),
+		   (unsigned long) K(bdi->write_bandwidth),
+		   nr_dirty,
+		   nr_io,
+		   nr_more_io,
 		   !list_empty(&bdi->bdi_list), bdi->state);
 #undef K
 
@@ -249,18 +268,6 @@
 	return wb_has_dirty_io(&bdi->wb);
 }
 
-static void bdi_flush_io(struct backing_dev_info *bdi)
-{
-	struct writeback_control wbc = {
-		.sync_mode		= WB_SYNC_NONE,
-		.older_than_this	= NULL,
-		.range_cyclic		= 1,
-		.nr_to_write		= 1024,
-	};
-
-	writeback_inodes_wb(&bdi->wb, &wbc);
-}
-
 /*
  * kupdated() used to do this. We cannot do it from the bdi_forker_thread()
  * or we risk deadlocking on ->s_umount. The longer term solution would be
@@ -446,9 +453,10 @@
 			if (IS_ERR(task)) {
 				/*
 				 * If thread creation fails, force writeout of
-				 * the bdi from the thread.
+				 * the bdi from the thread. Hopefully 1024 is
+				 * large enough for efficient IO.
 				 */
-				bdi_flush_io(bdi);
+				writeback_inodes_wb(&bdi->wb, 1024);
 			} else {
 				/*
 				 * The spinlock makes sure we do not lose
@@ -629,9 +637,15 @@
 	INIT_LIST_HEAD(&wb->b_dirty);
 	INIT_LIST_HEAD(&wb->b_io);
 	INIT_LIST_HEAD(&wb->b_more_io);
+	spin_lock_init(&wb->list_lock);
 	setup_timer(&wb->wakeup_timer, wakeup_timer_fn, (unsigned long)bdi);
 }
 
+/*
+ * Initial write bandwidth: 100 MB/s
+ */
+#define INIT_BW		(100 << (20 - PAGE_SHIFT))
+
 int bdi_init(struct backing_dev_info *bdi)
 {
 	int i, err;
@@ -654,6 +668,13 @@
 	}
 
 	bdi->dirty_exceeded = 0;
+
+	bdi->bw_time_stamp = jiffies;
+	bdi->written_stamp = 0;
+
+	bdi->write_bandwidth = INIT_BW;
+	bdi->avg_write_bandwidth = INIT_BW;
+
 	err = prop_local_init_percpu(&bdi->completions);
 
 	if (err) {
@@ -677,11 +698,12 @@
 	if (bdi_has_dirty_io(bdi)) {
 		struct bdi_writeback *dst = &default_backing_dev_info.wb;
 
-		spin_lock(&inode_wb_list_lock);
+		bdi_lock_two(&bdi->wb, dst);
 		list_splice(&bdi->wb.b_dirty, &dst->b_dirty);
 		list_splice(&bdi->wb.b_io, &dst->b_io);
 		list_splice(&bdi->wb.b_more_io, &dst->b_more_io);
-		spin_unlock(&inode_wb_list_lock);
+		spin_unlock(&bdi->wb.list_lock);
+		spin_unlock(&dst->list_lock);
 	}
 
 	bdi_unregister(bdi);
diff --git a/mm/failslab.c b/mm/failslab.c
index c5f88f2..1ce58c2 100644
--- a/mm/failslab.c
+++ b/mm/failslab.c
@@ -5,10 +5,6 @@
 	struct fault_attr attr;
 	u32 ignore_gfp_wait;
 	int cache_filter;
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-	struct dentry *ignore_gfp_wait_file;
-	struct dentry *cache_filter_file;
-#endif
 } failslab = {
 	.attr = FAULT_ATTR_INITIALIZER,
 	.ignore_gfp_wait = 1,
@@ -39,31 +35,24 @@
 static int __init failslab_debugfs_init(void)
 {
 	mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
-	struct dentry *dir;
 	int err;
 
 	err = init_fault_attr_dentries(&failslab.attr, "failslab");
 	if (err)
 		return err;
-	dir = failslab.attr.dentries.dir;
 
-	failslab.ignore_gfp_wait_file =
-		debugfs_create_bool("ignore-gfp-wait", mode, dir,
-				      &failslab.ignore_gfp_wait);
+	if (!debugfs_create_bool("ignore-gfp-wait", mode, failslab.attr.dir,
+				&failslab.ignore_gfp_wait))
+		goto fail;
+	if (!debugfs_create_bool("cache-filter", mode, failslab.attr.dir,
+				&failslab.cache_filter))
+		goto fail;
 
-	failslab.cache_filter_file =
-		debugfs_create_bool("cache-filter", mode, dir,
-				      &failslab.cache_filter);
+	return 0;
+fail:
+	cleanup_fault_attr_dentries(&failslab.attr);
 
-	if (!failslab.ignore_gfp_wait_file ||
-	    !failslab.cache_filter_file) {
-		err = -ENOMEM;
-		debugfs_remove(failslab.cache_filter_file);
-		debugfs_remove(failslab.ignore_gfp_wait_file);
-		cleanup_fault_attr_dentries(&failslab.attr);
-	}
-
-	return err;
+	return -ENOMEM;
 }
 
 late_initcall(failslab_debugfs_init);
diff --git a/mm/filemap.c b/mm/filemap.c
index 10a1711..867d402 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -78,7 +78,7 @@
  *  ->i_mutex			(generic_file_buffered_write)
  *    ->mmap_sem		(fault_in_pages_readable->do_page_fault)
  *
- *  inode_wb_list_lock
+ *  bdi->wb.list_lock
  *    sb_lock			(fs/fs-writeback.c)
  *    ->mapping->tree_lock	(__sync_single_inode)
  *
@@ -96,9 +96,9 @@
  *    ->zone.lru_lock		(check_pte_range->isolate_lru_page)
  *    ->private_lock		(page_remove_rmap->set_page_dirty)
  *    ->tree_lock		(page_remove_rmap->set_page_dirty)
- *    inode_wb_list_lock	(page_remove_rmap->set_page_dirty)
+ *    bdi.wb->list_lock		(page_remove_rmap->set_page_dirty)
  *    ->inode->i_lock		(page_remove_rmap->set_page_dirty)
- *    inode_wb_list_lock	(zap_pte_range->set_page_dirty)
+ *    bdi.wb->list_lock		(zap_pte_range->set_page_dirty)
  *    ->inode->i_lock		(zap_pte_range->set_page_dirty)
  *    ->private_lock		(zap_pte_range->__set_page_dirty_buffers)
  *
diff --git a/mm/init-mm.c b/mm/init-mm.c
index 4019979..a56a851 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -5,7 +5,7 @@
 #include <linux/list.h>
 #include <linux/cpumask.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/pgtable.h>
 #include <asm/mmu.h>
 
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index aacee45..d6880f5 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -96,7 +96,7 @@
 
 #include <asm/sections.h>
 #include <asm/processor.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <linux/kmemcheck.h>
 #include <linux/kmemleak.h>
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index e013b8e..5f84d235 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -205,6 +205,50 @@
 static void mem_cgroup_threshold(struct mem_cgroup *mem);
 static void mem_cgroup_oom_notify(struct mem_cgroup *mem);
 
+enum {
+	SCAN_BY_LIMIT,
+	SCAN_BY_SYSTEM,
+	NR_SCAN_CONTEXT,
+	SCAN_BY_SHRINK,	/* not recorded now */
+};
+
+enum {
+	SCAN,
+	SCAN_ANON,
+	SCAN_FILE,
+	ROTATE,
+	ROTATE_ANON,
+	ROTATE_FILE,
+	FREED,
+	FREED_ANON,
+	FREED_FILE,
+	ELAPSED,
+	NR_SCANSTATS,
+};
+
+struct scanstat {
+	spinlock_t	lock;
+	unsigned long	stats[NR_SCAN_CONTEXT][NR_SCANSTATS];
+	unsigned long	rootstats[NR_SCAN_CONTEXT][NR_SCANSTATS];
+};
+
+const char *scanstat_string[NR_SCANSTATS] = {
+	"scanned_pages",
+	"scanned_anon_pages",
+	"scanned_file_pages",
+	"rotated_pages",
+	"rotated_anon_pages",
+	"rotated_file_pages",
+	"freed_pages",
+	"freed_anon_pages",
+	"freed_file_pages",
+	"elapsed_ns",
+};
+#define SCANSTAT_WORD_LIMIT	"_by_limit"
+#define SCANSTAT_WORD_SYSTEM	"_by_system"
+#define SCANSTAT_WORD_HIERARCHY	"_under_hierarchy"
+
+
 /*
  * The memory controller data structure. The memory controller controls both
  * page cache and RSS per cgroup. We would eventually like to provide
@@ -246,10 +290,13 @@
 	 * Should the accounting and control be hierarchical, per subtree?
 	 */
 	bool use_hierarchy;
-	atomic_t	oom_lock;
+
+	bool		oom_lock;
+	atomic_t	under_oom;
+
 	atomic_t	refcnt;
 
-	unsigned int	swappiness;
+	int	swappiness;
 	/* OOM-Killer disable */
 	int		oom_kill_disable;
 
@@ -267,7 +314,8 @@
 
 	/* For oom notifier event fd */
 	struct list_head oom_notify;
-
+	/* For recording LRU-scan statistics */
+	struct scanstat scanstat;
 	/*
 	 * Should we move charges of a task when a task is moved into this
 	 * mem_cgroup ? And what type of charges should we move ?
@@ -636,27 +684,44 @@
 	preempt_enable();
 }
 
-static unsigned long
-mem_cgroup_get_zonestat_node(struct mem_cgroup *mem, int nid, enum lru_list idx)
+unsigned long
+mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *mem, int nid, int zid,
+			unsigned int lru_mask)
 {
 	struct mem_cgroup_per_zone *mz;
+	enum lru_list l;
+	unsigned long ret = 0;
+
+	mz = mem_cgroup_zoneinfo(mem, nid, zid);
+
+	for_each_lru(l) {
+		if (BIT(l) & lru_mask)
+			ret += MEM_CGROUP_ZSTAT(mz, l);
+	}
+	return ret;
+}
+
+static unsigned long
+mem_cgroup_node_nr_lru_pages(struct mem_cgroup *mem,
+			int nid, unsigned int lru_mask)
+{
 	u64 total = 0;
 	int zid;
 
-	for (zid = 0; zid < MAX_NR_ZONES; zid++) {
-		mz = mem_cgroup_zoneinfo(mem, nid, zid);
-		total += MEM_CGROUP_ZSTAT(mz, idx);
-	}
+	for (zid = 0; zid < MAX_NR_ZONES; zid++)
+		total += mem_cgroup_zone_nr_lru_pages(mem, nid, zid, lru_mask);
+
 	return total;
 }
-static unsigned long mem_cgroup_get_local_zonestat(struct mem_cgroup *mem,
-					enum lru_list idx)
+
+static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *mem,
+			unsigned int lru_mask)
 {
 	int nid;
 	u64 total = 0;
 
-	for_each_online_node(nid)
-		total += mem_cgroup_get_zonestat_node(mem, nid, idx);
+	for_each_node_state(nid, N_HIGH_MEMORY)
+		total += mem_cgroup_node_nr_lru_pages(mem, nid, lru_mask);
 	return total;
 }
 
@@ -1043,6 +1108,21 @@
 	mem_cgroup_add_lru_list(page, to);
 }
 
+/*
+ * Checks whether given mem is same or in the root_mem's
+ * hierarchy subtree
+ */
+static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_mem,
+		struct mem_cgroup *mem)
+{
+	if (root_mem != mem) {
+		return (root_mem->use_hierarchy &&
+			css_is_ancestor(&mem->css, &root_mem->css));
+	}
+
+	return true;
+}
+
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem)
 {
 	int ret;
@@ -1062,10 +1142,7 @@
 	 * enabled in "curr" and "curr" is a child of "mem" in *cgroup*
 	 * hierarchy(even if use_hierarchy is disabled in "mem").
 	 */
-	if (mem->use_hierarchy)
-		ret = css_is_ancestor(&curr->css, &mem->css);
-	else
-		ret = (curr == mem);
+	ret = mem_cgroup_same_or_subtree(mem, curr);
 	css_put(&curr->css);
 	return ret;
 }
@@ -1077,8 +1154,8 @@
 	unsigned long gb;
 	unsigned long inactive_ratio;
 
-	inactive = mem_cgroup_get_local_zonestat(memcg, LRU_INACTIVE_ANON);
-	active = mem_cgroup_get_local_zonestat(memcg, LRU_ACTIVE_ANON);
+	inactive = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_INACTIVE_ANON));
+	active = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_ACTIVE_ANON));
 
 	gb = (inactive + active) >> (30 - PAGE_SHIFT);
 	if (gb)
@@ -1117,109 +1194,12 @@
 	unsigned long active;
 	unsigned long inactive;
 
-	inactive = mem_cgroup_get_local_zonestat(memcg, LRU_INACTIVE_FILE);
-	active = mem_cgroup_get_local_zonestat(memcg, LRU_ACTIVE_FILE);
+	inactive = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_INACTIVE_FILE));
+	active = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_ACTIVE_FILE));
 
 	return (active > inactive);
 }
 
-unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg,
-						struct zone *zone,
-						enum lru_list lru)
-{
-	int nid = zone_to_nid(zone);
-	int zid = zone_idx(zone);
-	struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(memcg, nid, zid);
-
-	return MEM_CGROUP_ZSTAT(mz, lru);
-}
-
-static unsigned long mem_cgroup_node_nr_file_lru_pages(struct mem_cgroup *memcg,
-							int nid)
-{
-	unsigned long ret;
-
-	ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_FILE) +
-		mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_FILE);
-
-	return ret;
-}
-
-static unsigned long mem_cgroup_node_nr_anon_lru_pages(struct mem_cgroup *memcg,
-							int nid)
-{
-	unsigned long ret;
-
-	ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_ANON) +
-		mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_ANON);
-	return ret;
-}
-
-#if MAX_NUMNODES > 1
-static unsigned long mem_cgroup_nr_file_lru_pages(struct mem_cgroup *memcg)
-{
-	u64 total = 0;
-	int nid;
-
-	for_each_node_state(nid, N_HIGH_MEMORY)
-		total += mem_cgroup_node_nr_file_lru_pages(memcg, nid);
-
-	return total;
-}
-
-static unsigned long mem_cgroup_nr_anon_lru_pages(struct mem_cgroup *memcg)
-{
-	u64 total = 0;
-	int nid;
-
-	for_each_node_state(nid, N_HIGH_MEMORY)
-		total += mem_cgroup_node_nr_anon_lru_pages(memcg, nid);
-
-	return total;
-}
-
-static unsigned long
-mem_cgroup_node_nr_unevictable_lru_pages(struct mem_cgroup *memcg, int nid)
-{
-	return mem_cgroup_get_zonestat_node(memcg, nid, LRU_UNEVICTABLE);
-}
-
-static unsigned long
-mem_cgroup_nr_unevictable_lru_pages(struct mem_cgroup *memcg)
-{
-	u64 total = 0;
-	int nid;
-
-	for_each_node_state(nid, N_HIGH_MEMORY)
-		total += mem_cgroup_node_nr_unevictable_lru_pages(memcg, nid);
-
-	return total;
-}
-
-static unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
-							int nid)
-{
-	enum lru_list l;
-	u64 total = 0;
-
-	for_each_lru(l)
-		total += mem_cgroup_get_zonestat_node(memcg, nid, l);
-
-	return total;
-}
-
-static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg)
-{
-	u64 total = 0;
-	int nid;
-
-	for_each_node_state(nid, N_HIGH_MEMORY)
-		total += mem_cgroup_node_nr_lru_pages(memcg, nid);
-
-	return total;
-}
-#endif /* CONFIG_NUMA */
-
 struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
 						      struct zone *zone)
 {
@@ -1329,7 +1309,7 @@
 	return margin >> PAGE_SHIFT;
 }
 
-static unsigned int get_swappiness(struct mem_cgroup *memcg)
+int mem_cgroup_swappiness(struct mem_cgroup *memcg)
 {
 	struct cgroup *cgrp = memcg->css.cgroup;
 
@@ -1401,10 +1381,9 @@
 	to = mc.to;
 	if (!from)
 		goto unlock;
-	if (from == mem || to == mem
-	    || (mem->use_hierarchy && css_is_ancestor(&from->css, &mem->css))
-	    || (mem->use_hierarchy && css_is_ancestor(&to->css,	&mem->css)))
-		ret = true;
+
+	ret = mem_cgroup_same_or_subtree(mem, from)
+		|| mem_cgroup_same_or_subtree(mem, to);
 unlock:
 	spin_unlock(&mc.lock);
 	return ret;
@@ -1576,11 +1555,11 @@
 static bool test_mem_cgroup_node_reclaimable(struct mem_cgroup *mem,
 		int nid, bool noswap)
 {
-	if (mem_cgroup_node_nr_file_lru_pages(mem, nid))
+	if (mem_cgroup_node_nr_lru_pages(mem, nid, LRU_ALL_FILE))
 		return true;
 	if (noswap || !total_swap_pages)
 		return false;
-	if (mem_cgroup_node_nr_anon_lru_pages(mem, nid))
+	if (mem_cgroup_node_nr_lru_pages(mem, nid, LRU_ALL_ANON))
 		return true;
 	return false;
 
@@ -1700,6 +1679,44 @@
 }
 #endif
 
+static void __mem_cgroup_record_scanstat(unsigned long *stats,
+			   struct memcg_scanrecord *rec)
+{
+
+	stats[SCAN] += rec->nr_scanned[0] + rec->nr_scanned[1];
+	stats[SCAN_ANON] += rec->nr_scanned[0];
+	stats[SCAN_FILE] += rec->nr_scanned[1];
+
+	stats[ROTATE] += rec->nr_rotated[0] + rec->nr_rotated[1];
+	stats[ROTATE_ANON] += rec->nr_rotated[0];
+	stats[ROTATE_FILE] += rec->nr_rotated[1];
+
+	stats[FREED] += rec->nr_freed[0] + rec->nr_freed[1];
+	stats[FREED_ANON] += rec->nr_freed[0];
+	stats[FREED_FILE] += rec->nr_freed[1];
+
+	stats[ELAPSED] += rec->elapsed;
+}
+
+static void mem_cgroup_record_scanstat(struct memcg_scanrecord *rec)
+{
+	struct mem_cgroup *mem;
+	int context = rec->context;
+
+	if (context >= NR_SCAN_CONTEXT)
+		return;
+
+	mem = rec->mem;
+	spin_lock(&mem->scanstat.lock);
+	__mem_cgroup_record_scanstat(mem->scanstat.stats[context], rec);
+	spin_unlock(&mem->scanstat.lock);
+
+	mem = rec->root;
+	spin_lock(&mem->scanstat.lock);
+	__mem_cgroup_record_scanstat(mem->scanstat.rootstats[context], rec);
+	spin_unlock(&mem->scanstat.lock);
+}
+
 /*
  * Scan the hierarchy if needed to reclaim memory. We remember the last child
  * we reclaimed from, so that we don't end up penalizing one child extensively
@@ -1724,15 +1741,25 @@
 	bool noswap = reclaim_options & MEM_CGROUP_RECLAIM_NOSWAP;
 	bool shrink = reclaim_options & MEM_CGROUP_RECLAIM_SHRINK;
 	bool check_soft = reclaim_options & MEM_CGROUP_RECLAIM_SOFT;
+	struct memcg_scanrecord rec;
 	unsigned long excess;
-	unsigned long nr_scanned;
+	unsigned long scanned;
 
 	excess = res_counter_soft_limit_excess(&root_mem->res) >> PAGE_SHIFT;
 
 	/* If memsw_is_minimum==1, swap-out is of-no-use. */
-	if (!check_soft && root_mem->memsw_is_minimum)
+	if (!check_soft && !shrink && root_mem->memsw_is_minimum)
 		noswap = true;
 
+	if (shrink)
+		rec.context = SCAN_BY_SHRINK;
+	else if (check_soft)
+		rec.context = SCAN_BY_SYSTEM;
+	else
+		rec.context = SCAN_BY_LIMIT;
+
+	rec.root = root_mem;
+
 	while (1) {
 		victim = mem_cgroup_select_victim(root_mem);
 		if (victim == root_mem) {
@@ -1773,15 +1800,23 @@
 			css_put(&victim->css);
 			continue;
 		}
+		rec.mem = victim;
+		rec.nr_scanned[0] = 0;
+		rec.nr_scanned[1] = 0;
+		rec.nr_rotated[0] = 0;
+		rec.nr_rotated[1] = 0;
+		rec.nr_freed[0] = 0;
+		rec.nr_freed[1] = 0;
+		rec.elapsed = 0;
 		/* we use swappiness of local cgroup */
 		if (check_soft) {
 			ret = mem_cgroup_shrink_node_zone(victim, gfp_mask,
-				noswap, get_swappiness(victim), zone,
-				&nr_scanned);
-			*total_scanned += nr_scanned;
+				noswap, zone, &rec, &scanned);
+			*total_scanned += scanned;
 		} else
 			ret = try_to_free_mem_cgroup_pages(victim, gfp_mask,
-						noswap, get_swappiness(victim));
+						noswap, &rec);
+		mem_cgroup_record_scanstat(&rec);
 		css_put(&victim->css);
 		/*
 		 * At shrinking usage, we can't check we should stop here or
@@ -1803,38 +1838,84 @@
 /*
  * Check OOM-Killer is already running under our hierarchy.
  * If someone is running, return false.
+ * Has to be called with memcg_oom_lock
  */
 static bool mem_cgroup_oom_lock(struct mem_cgroup *mem)
 {
-	int x, lock_count = 0;
-	struct mem_cgroup *iter;
+	int lock_count = -1;
+	struct mem_cgroup *iter, *failed = NULL;
+	bool cond = true;
 
-	for_each_mem_cgroup_tree(iter, mem) {
-		x = atomic_inc_return(&iter->oom_lock);
-		lock_count = max(x, lock_count);
+	for_each_mem_cgroup_tree_cond(iter, mem, cond) {
+		bool locked = iter->oom_lock;
+
+		iter->oom_lock = true;
+		if (lock_count == -1)
+			lock_count = iter->oom_lock;
+		else if (lock_count != locked) {
+			/*
+			 * this subtree of our hierarchy is already locked
+			 * so we cannot give a lock.
+			 */
+			lock_count = 0;
+			failed = iter;
+			cond = false;
+		}
 	}
 
-	if (lock_count == 1)
-		return true;
-	return false;
+	if (!failed)
+		goto done;
+
+	/*
+	 * OK, we failed to lock the whole subtree so we have to clean up
+	 * what we set up to the failing subtree
+	 */
+	cond = true;
+	for_each_mem_cgroup_tree_cond(iter, mem, cond) {
+		if (iter == failed) {
+			cond = false;
+			continue;
+		}
+		iter->oom_lock = false;
+	}
+done:
+	return lock_count;
 }
 
+/*
+ * Has to be called with memcg_oom_lock
+ */
 static int mem_cgroup_oom_unlock(struct mem_cgroup *mem)
 {
 	struct mem_cgroup *iter;
 
+	for_each_mem_cgroup_tree(iter, mem)
+		iter->oom_lock = false;
+	return 0;
+}
+
+static void mem_cgroup_mark_under_oom(struct mem_cgroup *mem)
+{
+	struct mem_cgroup *iter;
+
+	for_each_mem_cgroup_tree(iter, mem)
+		atomic_inc(&iter->under_oom);
+}
+
+static void mem_cgroup_unmark_under_oom(struct mem_cgroup *mem)
+{
+	struct mem_cgroup *iter;
+
 	/*
 	 * When a new child is created while the hierarchy is under oom,
 	 * mem_cgroup_oom_lock() may not be called. We have to use
 	 * atomic_add_unless() here.
 	 */
 	for_each_mem_cgroup_tree(iter, mem)
-		atomic_add_unless(&iter->oom_lock, -1, 0);
-	return 0;
+		atomic_add_unless(&iter->under_oom, -1, 0);
 }
 
-
-static DEFINE_MUTEX(memcg_oom_mutex);
+static DEFINE_SPINLOCK(memcg_oom_lock);
 static DECLARE_WAIT_QUEUE_HEAD(memcg_oom_waitq);
 
 struct oom_wait_info {
@@ -1845,25 +1926,20 @@
 static int memcg_oom_wake_function(wait_queue_t *wait,
 	unsigned mode, int sync, void *arg)
 {
-	struct mem_cgroup *wake_mem = (struct mem_cgroup *)arg;
+	struct mem_cgroup *wake_mem = (struct mem_cgroup *)arg,
+			  *oom_wait_mem;
 	struct oom_wait_info *oom_wait_info;
 
 	oom_wait_info = container_of(wait, struct oom_wait_info, wait);
+	oom_wait_mem = oom_wait_info->mem;
 
-	if (oom_wait_info->mem == wake_mem)
-		goto wakeup;
-	/* if no hierarchy, no match */
-	if (!oom_wait_info->mem->use_hierarchy || !wake_mem->use_hierarchy)
-		return 0;
 	/*
 	 * Both of oom_wait_info->mem and wake_mem are stable under us.
 	 * Then we can use css_is_ancestor without taking care of RCU.
 	 */
-	if (!css_is_ancestor(&oom_wait_info->mem->css, &wake_mem->css) &&
-	    !css_is_ancestor(&wake_mem->css, &oom_wait_info->mem->css))
+	if (!mem_cgroup_same_or_subtree(oom_wait_mem, wake_mem)
+			&& !mem_cgroup_same_or_subtree(wake_mem, oom_wait_mem))
 		return 0;
-
-wakeup:
 	return autoremove_wake_function(wait, mode, sync, arg);
 }
 
@@ -1875,7 +1951,7 @@
 
 static void memcg_oom_recover(struct mem_cgroup *mem)
 {
-	if (mem && atomic_read(&mem->oom_lock))
+	if (mem && atomic_read(&mem->under_oom))
 		memcg_wakeup_oom(mem);
 }
 
@@ -1893,8 +1969,10 @@
 	owait.wait.private = current;
 	INIT_LIST_HEAD(&owait.wait.task_list);
 	need_to_kill = true;
+	mem_cgroup_mark_under_oom(mem);
+
 	/* At first, try to OOM lock hierarchy under mem.*/
-	mutex_lock(&memcg_oom_mutex);
+	spin_lock(&memcg_oom_lock);
 	locked = mem_cgroup_oom_lock(mem);
 	/*
 	 * Even if signal_pending(), we can't quit charge() loop without
@@ -1906,7 +1984,7 @@
 		need_to_kill = false;
 	if (locked)
 		mem_cgroup_oom_notify(mem);
-	mutex_unlock(&memcg_oom_mutex);
+	spin_unlock(&memcg_oom_lock);
 
 	if (need_to_kill) {
 		finish_wait(&memcg_oom_waitq, &owait.wait);
@@ -1915,10 +1993,13 @@
 		schedule();
 		finish_wait(&memcg_oom_waitq, &owait.wait);
 	}
-	mutex_lock(&memcg_oom_mutex);
-	mem_cgroup_oom_unlock(mem);
+	spin_lock(&memcg_oom_lock);
+	if (locked)
+		mem_cgroup_oom_unlock(mem);
 	memcg_wakeup_oom(mem);
-	mutex_unlock(&memcg_oom_mutex);
+	spin_unlock(&memcg_oom_lock);
+
+	mem_cgroup_unmark_under_oom(mem);
 
 	if (test_thread_flag(TIF_MEMDIE) || fatal_signal_pending(current))
 		return false;
@@ -2011,7 +2092,6 @@
 #define FLUSHING_CACHED_CHARGE	(0)
 };
 static DEFINE_PER_CPU(struct memcg_stock_pcp, memcg_stock);
-static DEFINE_MUTEX(percpu_charge_mutex);
 
 /*
  * Try to consume stocked charge on this cpu. If success, one page is consumed
@@ -2079,19 +2159,14 @@
 }
 
 /*
- * Tries to drain stocked charges in other cpus. This function is asynchronous
- * and just put a work per cpu for draining localy on each cpu. Caller can
- * expects some charges will be back to res_counter later but cannot wait for
- * it.
+ * Drains all per-CPU charge caches for given root_mem resp. subtree
+ * of the hierarchy under it. sync flag says whether we should block
+ * until the work is done.
  */
-static void drain_all_stock_async(struct mem_cgroup *root_mem)
+static void drain_all_stock(struct mem_cgroup *root_mem, bool sync)
 {
 	int cpu, curcpu;
-	/*
-	 * If someone calls draining, avoid adding more kworker runs.
-	 */
-	if (!mutex_trylock(&percpu_charge_mutex))
-		return;
+
 	/* Notify other cpus that system-wide "drain" is running */
 	get_online_cpus();
 	/*
@@ -2105,34 +2180,48 @@
 		struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
 		struct mem_cgroup *mem;
 
-		if (cpu == curcpu)
-			continue;
-
 		mem = stock->cached;
-		if (!mem)
+		if (!mem || !stock->nr_pages)
 			continue;
-		if (mem != root_mem) {
-			if (!root_mem->use_hierarchy)
-				continue;
-			/* check whether "mem" is under tree of "root_mem" */
-			if (!css_is_ancestor(&mem->css, &root_mem->css))
-				continue;
+		if (!mem_cgroup_same_or_subtree(root_mem, mem))
+			continue;
+		if (!test_and_set_bit(FLUSHING_CACHED_CHARGE, &stock->flags)) {
+			if (cpu == curcpu)
+				drain_local_stock(&stock->work);
+			else
+				schedule_work_on(cpu, &stock->work);
 		}
-		if (!test_and_set_bit(FLUSHING_CACHED_CHARGE, &stock->flags))
-			schedule_work_on(cpu, &stock->work);
 	}
+
+	if (!sync)
+		goto out;
+
+	for_each_online_cpu(cpu) {
+		struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
+		if (mem_cgroup_same_or_subtree(root_mem, stock->cached) &&
+				test_bit(FLUSHING_CACHED_CHARGE, &stock->flags))
+			flush_work(&stock->work);
+	}
+out:
  	put_online_cpus();
-	mutex_unlock(&percpu_charge_mutex);
-	/* We don't wait for flush_work */
+}
+
+/*
+ * Tries to drain stocked charges in other cpus. This function is asynchronous
+ * and just put a work per cpu for draining localy on each cpu. Caller can
+ * expects some charges will be back to res_counter later but cannot wait for
+ * it.
+ */
+static void drain_all_stock_async(struct mem_cgroup *root_mem)
+{
+	drain_all_stock(root_mem, false);
 }
 
 /* This is a synchronous drain interface. */
-static void drain_all_stock_sync(void)
+static void drain_all_stock_sync(struct mem_cgroup *root_mem)
 {
 	/* called when force_empty is called */
-	mutex_lock(&percpu_charge_mutex);
-	schedule_on_each_cpu(drain_local_stock);
-	mutex_unlock(&percpu_charge_mutex);
+	drain_all_stock(root_mem, true);
 }
 
 /*
@@ -3780,7 +3869,7 @@
 			goto out;
 		/* This is for making all *used* pages to be on LRU. */
 		lru_add_drain_all();
-		drain_all_stock_sync();
+		drain_all_stock_sync(mem);
 		ret = 0;
 		mem_cgroup_start_move(mem);
 		for_each_node_state(node, N_HIGH_MEMORY) {
@@ -3819,14 +3908,18 @@
 	/* try to free all pages in this cgroup */
 	shrink = 1;
 	while (nr_retries && mem->res.usage > 0) {
+		struct memcg_scanrecord rec;
 		int progress;
 
 		if (signal_pending(current)) {
 			ret = -EINTR;
 			goto out;
 		}
+		rec.context = SCAN_BY_SHRINK;
+		rec.mem = mem;
+		rec.root = mem;
 		progress = try_to_free_mem_cgroup_pages(mem, GFP_KERNEL,
-						false, get_swappiness(mem));
+						false, &rec);
 		if (!progress) {
 			nr_retries--;
 			/* maybe some writeback is necessary */
@@ -4152,15 +4245,15 @@
 	s->stat[MCS_PGMAJFAULT] += val;
 
 	/* per zone stat */
-	val = mem_cgroup_get_local_zonestat(mem, LRU_INACTIVE_ANON);
+	val = mem_cgroup_nr_lru_pages(mem, BIT(LRU_INACTIVE_ANON));
 	s->stat[MCS_INACTIVE_ANON] += val * PAGE_SIZE;
-	val = mem_cgroup_get_local_zonestat(mem, LRU_ACTIVE_ANON);
+	val = mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON));
 	s->stat[MCS_ACTIVE_ANON] += val * PAGE_SIZE;
-	val = mem_cgroup_get_local_zonestat(mem, LRU_INACTIVE_FILE);
+	val = mem_cgroup_nr_lru_pages(mem, BIT(LRU_INACTIVE_FILE));
 	s->stat[MCS_INACTIVE_FILE] += val * PAGE_SIZE;
-	val = mem_cgroup_get_local_zonestat(mem, LRU_ACTIVE_FILE);
+	val = mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_FILE));
 	s->stat[MCS_ACTIVE_FILE] += val * PAGE_SIZE;
-	val = mem_cgroup_get_local_zonestat(mem, LRU_UNEVICTABLE);
+	val = mem_cgroup_nr_lru_pages(mem, BIT(LRU_UNEVICTABLE));
 	s->stat[MCS_UNEVICTABLE] += val * PAGE_SIZE;
 }
 
@@ -4182,35 +4275,37 @@
 	struct cgroup *cont = m->private;
 	struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cont);
 
-	total_nr = mem_cgroup_nr_lru_pages(mem_cont);
+	total_nr = mem_cgroup_nr_lru_pages(mem_cont, LRU_ALL);
 	seq_printf(m, "total=%lu", total_nr);
 	for_each_node_state(nid, N_HIGH_MEMORY) {
-		node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid);
+		node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid, LRU_ALL);
 		seq_printf(m, " N%d=%lu", nid, node_nr);
 	}
 	seq_putc(m, '\n');
 
-	file_nr = mem_cgroup_nr_file_lru_pages(mem_cont);
+	file_nr = mem_cgroup_nr_lru_pages(mem_cont, LRU_ALL_FILE);
 	seq_printf(m, "file=%lu", file_nr);
 	for_each_node_state(nid, N_HIGH_MEMORY) {
-		node_nr = mem_cgroup_node_nr_file_lru_pages(mem_cont, nid);
+		node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid,
+				LRU_ALL_FILE);
 		seq_printf(m, " N%d=%lu", nid, node_nr);
 	}
 	seq_putc(m, '\n');
 
-	anon_nr = mem_cgroup_nr_anon_lru_pages(mem_cont);
+	anon_nr = mem_cgroup_nr_lru_pages(mem_cont, LRU_ALL_ANON);
 	seq_printf(m, "anon=%lu", anon_nr);
 	for_each_node_state(nid, N_HIGH_MEMORY) {
-		node_nr = mem_cgroup_node_nr_anon_lru_pages(mem_cont, nid);
+		node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid,
+				LRU_ALL_ANON);
 		seq_printf(m, " N%d=%lu", nid, node_nr);
 	}
 	seq_putc(m, '\n');
 
-	unevictable_nr = mem_cgroup_nr_unevictable_lru_pages(mem_cont);
+	unevictable_nr = mem_cgroup_nr_lru_pages(mem_cont, BIT(LRU_UNEVICTABLE));
 	seq_printf(m, "unevictable=%lu", unevictable_nr);
 	for_each_node_state(nid, N_HIGH_MEMORY) {
-		node_nr = mem_cgroup_node_nr_unevictable_lru_pages(mem_cont,
-									nid);
+		node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid,
+				BIT(LRU_UNEVICTABLE));
 		seq_printf(m, " N%d=%lu", nid, node_nr);
 	}
 	seq_putc(m, '\n');
@@ -4288,7 +4383,7 @@
 {
 	struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
 
-	return get_swappiness(memcg);
+	return mem_cgroup_swappiness(memcg);
 }
 
 static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,
@@ -4578,15 +4673,15 @@
 	if (!event)
 		return -ENOMEM;
 
-	mutex_lock(&memcg_oom_mutex);
+	spin_lock(&memcg_oom_lock);
 
 	event->eventfd = eventfd;
 	list_add(&event->list, &memcg->oom_notify);
 
 	/* already in OOM ? */
-	if (atomic_read(&memcg->oom_lock))
+	if (atomic_read(&memcg->under_oom))
 		eventfd_signal(eventfd, 1);
-	mutex_unlock(&memcg_oom_mutex);
+	spin_unlock(&memcg_oom_lock);
 
 	return 0;
 }
@@ -4600,7 +4695,7 @@
 
 	BUG_ON(type != _OOM_TYPE);
 
-	mutex_lock(&memcg_oom_mutex);
+	spin_lock(&memcg_oom_lock);
 
 	list_for_each_entry_safe(ev, tmp, &mem->oom_notify, list) {
 		if (ev->eventfd == eventfd) {
@@ -4609,7 +4704,7 @@
 		}
 	}
 
-	mutex_unlock(&memcg_oom_mutex);
+	spin_unlock(&memcg_oom_lock);
 }
 
 static int mem_cgroup_oom_control_read(struct cgroup *cgrp,
@@ -4619,7 +4714,7 @@
 
 	cb->fill(cb, "oom_kill_disable", mem->oom_kill_disable);
 
-	if (atomic_read(&mem->oom_lock))
+	if (atomic_read(&mem->under_oom))
 		cb->fill(cb, "under_oom", 1);
 	else
 		cb->fill(cb, "under_oom", 0);
@@ -4668,6 +4763,54 @@
 }
 #endif /* CONFIG_NUMA */
 
+static int mem_cgroup_vmscan_stat_read(struct cgroup *cgrp,
+				struct cftype *cft,
+				struct cgroup_map_cb *cb)
+{
+	struct mem_cgroup *mem = mem_cgroup_from_cont(cgrp);
+	char string[64];
+	int i;
+
+	for (i = 0; i < NR_SCANSTATS; i++) {
+		strcpy(string, scanstat_string[i]);
+		strcat(string, SCANSTAT_WORD_LIMIT);
+		cb->fill(cb, string,  mem->scanstat.stats[SCAN_BY_LIMIT][i]);
+	}
+
+	for (i = 0; i < NR_SCANSTATS; i++) {
+		strcpy(string, scanstat_string[i]);
+		strcat(string, SCANSTAT_WORD_SYSTEM);
+		cb->fill(cb, string,  mem->scanstat.stats[SCAN_BY_SYSTEM][i]);
+	}
+
+	for (i = 0; i < NR_SCANSTATS; i++) {
+		strcpy(string, scanstat_string[i]);
+		strcat(string, SCANSTAT_WORD_LIMIT);
+		strcat(string, SCANSTAT_WORD_HIERARCHY);
+		cb->fill(cb, string,  mem->scanstat.rootstats[SCAN_BY_LIMIT][i]);
+	}
+	for (i = 0; i < NR_SCANSTATS; i++) {
+		strcpy(string, scanstat_string[i]);
+		strcat(string, SCANSTAT_WORD_SYSTEM);
+		strcat(string, SCANSTAT_WORD_HIERARCHY);
+		cb->fill(cb, string,  mem->scanstat.rootstats[SCAN_BY_SYSTEM][i]);
+	}
+	return 0;
+}
+
+static int mem_cgroup_reset_vmscan_stat(struct cgroup *cgrp,
+				unsigned int event)
+{
+	struct mem_cgroup *mem = mem_cgroup_from_cont(cgrp);
+
+	spin_lock(&mem->scanstat.lock);
+	memset(&mem->scanstat.stats, 0, sizeof(mem->scanstat.stats));
+	memset(&mem->scanstat.rootstats, 0, sizeof(mem->scanstat.rootstats));
+	spin_unlock(&mem->scanstat.lock);
+	return 0;
+}
+
+
 static struct cftype mem_cgroup_files[] = {
 	{
 		.name = "usage_in_bytes",
@@ -4738,6 +4881,11 @@
 		.mode = S_IRUGO,
 	},
 #endif
+	{
+		.name = "vmscan_stat",
+		.read_map = mem_cgroup_vmscan_stat_read,
+		.trigger = mem_cgroup_reset_vmscan_stat,
+	},
 };
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
@@ -4997,10 +5145,11 @@
 	INIT_LIST_HEAD(&mem->oom_notify);
 
 	if (parent)
-		mem->swappiness = get_swappiness(parent);
+		mem->swappiness = mem_cgroup_swappiness(parent);
 	atomic_set(&mem->refcnt, 1);
 	mem->move_charge_at_immigrate = 0;
 	mutex_init(&mem->thresholds_lock);
+	spin_lock_init(&mem->scanstat.lock);
 	return &mem->css;
 free_out:
 	__mem_cgroup_free(mem);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index e7fb9d2..8b57173 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -93,6 +93,7 @@
 
 #include <asm/tlbflush.h>
 #include <asm/uaccess.h>
+#include <linux/random.h>
 
 #include "internal.h"
 
@@ -1645,6 +1646,21 @@
 		return interleave_nodes(pol);
 }
 
+/*
+ * Return the bit number of a random bit set in the nodemask.
+ * (returns -1 if nodemask is empty)
+ */
+int node_random(const nodemask_t *maskp)
+{
+	int w, bit = -1;
+
+	w = nodes_weight(*maskp);
+	if (w)
+		bit = bitmap_ord_to_pos(maskp->bits,
+			get_random_int() % w, MAX_NUMNODES);
+	return bit;
+}
+
 #ifdef CONFIG_HUGETLBFS
 /*
  * huge_zonelist(@vma, @addr, @gfp_flags, @mpol)
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index d8767b3..d196074 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -37,6 +37,16 @@
 #include <trace/events/writeback.h>
 
 /*
+ * Sleep at most 200ms at a time in balance_dirty_pages().
+ */
+#define MAX_PAUSE		max(HZ/5, 1)
+
+/*
+ * Estimate write bandwidth at 200ms intervals.
+ */
+#define BANDWIDTH_INTERVAL	max(HZ/5, 1)
+
+/*
  * After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited
  * will look to see if it needs to force writeback or throttling.
  */
@@ -111,6 +121,7 @@
 
 /* End of sysctl-exported parameters */
 
+unsigned long global_dirty_limit;
 
 /*
  * Scale the writeback cache size proportional to the relative writeout speeds.
@@ -219,6 +230,7 @@
  */
 static inline void __bdi_writeout_inc(struct backing_dev_info *bdi)
 {
+	__inc_bdi_stat(bdi, BDI_WRITTEN);
 	__prop_inc_percpu_max(&vm_completions, &bdi->completions,
 			      bdi->max_prop_frac);
 }
@@ -244,13 +256,8 @@
 static void bdi_writeout_fraction(struct backing_dev_info *bdi,
 		long *numerator, long *denominator)
 {
-	if (bdi_cap_writeback_dirty(bdi)) {
-		prop_fraction_percpu(&vm_completions, &bdi->completions,
+	prop_fraction_percpu(&vm_completions, &bdi->completions,
 				numerator, denominator);
-	} else {
-		*numerator = 0;
-		*denominator = 1;
-	}
 }
 
 static inline void task_dirties_fraction(struct task_struct *tsk,
@@ -274,12 +281,13 @@
  * effectively curb the growth of dirty pages. Light dirtiers with high enough
  * dirty threshold may never get throttled.
  */
+#define TASK_LIMIT_FRACTION 8
 static unsigned long task_dirty_limit(struct task_struct *tsk,
 				       unsigned long bdi_dirty)
 {
 	long numerator, denominator;
 	unsigned long dirty = bdi_dirty;
-	u64 inv = dirty >> 3;
+	u64 inv = dirty / TASK_LIMIT_FRACTION;
 
 	task_dirties_fraction(tsk, &numerator, &denominator);
 	inv *= numerator;
@@ -290,6 +298,12 @@
 	return max(dirty, bdi_dirty/2);
 }
 
+/* Minimum limit for any task */
+static unsigned long task_min_dirty_limit(unsigned long bdi_dirty)
+{
+	return bdi_dirty - bdi_dirty / TASK_LIMIT_FRACTION;
+}
+
 /*
  *
  */
@@ -397,6 +411,11 @@
 	return x + 1;	/* Ensure that we never return 0 */
 }
 
+static unsigned long hard_dirty_limit(unsigned long thresh)
+{
+	return max(thresh, global_dirty_limit);
+}
+
 /*
  * global_dirty_limits - background-writeback and dirty-throttling thresholds
  *
@@ -435,12 +454,20 @@
 	}
 	*pbackground = background;
 	*pdirty = dirty;
+	trace_global_dirty_state(background, dirty);
 }
 
-/*
+/**
  * bdi_dirty_limit - @bdi's share of dirty throttling threshold
+ * @bdi: the backing_dev_info to query
+ * @dirty: global dirty limit in pages
  *
- * Allocate high/low dirty limits to fast/slow devices, in order to prevent
+ * Returns @bdi's dirty limit in pages. The term "dirty" in the context of
+ * dirty balancing includes all PG_dirty, PG_writeback and NFS unstable pages.
+ * And the "limit" in the name is not seriously taken as hard limit in
+ * balance_dirty_pages().
+ *
+ * It allocates high/low dirty limits to fast/slow devices, in order to prevent
  * - starving fast devices
  * - piling up dirty pages (that will take long time to sync) on slow devices
  *
@@ -468,6 +495,153 @@
 	return bdi_dirty;
 }
 
+static void bdi_update_write_bandwidth(struct backing_dev_info *bdi,
+				       unsigned long elapsed,
+				       unsigned long written)
+{
+	const unsigned long period = roundup_pow_of_two(3 * HZ);
+	unsigned long avg = bdi->avg_write_bandwidth;
+	unsigned long old = bdi->write_bandwidth;
+	u64 bw;
+
+	/*
+	 * bw = written * HZ / elapsed
+	 *
+	 *                   bw * elapsed + write_bandwidth * (period - elapsed)
+	 * write_bandwidth = ---------------------------------------------------
+	 *                                          period
+	 */
+	bw = written - bdi->written_stamp;
+	bw *= HZ;
+	if (unlikely(elapsed > period)) {
+		do_div(bw, elapsed);
+		avg = bw;
+		goto out;
+	}
+	bw += (u64)bdi->write_bandwidth * (period - elapsed);
+	bw >>= ilog2(period);
+
+	/*
+	 * one more level of smoothing, for filtering out sudden spikes
+	 */
+	if (avg > old && old >= (unsigned long)bw)
+		avg -= (avg - old) >> 3;
+
+	if (avg < old && old <= (unsigned long)bw)
+		avg += (old - avg) >> 3;
+
+out:
+	bdi->write_bandwidth = bw;
+	bdi->avg_write_bandwidth = avg;
+}
+
+/*
+ * The global dirtyable memory and dirty threshold could be suddenly knocked
+ * down by a large amount (eg. on the startup of KVM in a swapless system).
+ * This may throw the system into deep dirty exceeded state and throttle
+ * heavy/light dirtiers alike. To retain good responsiveness, maintain
+ * global_dirty_limit for tracking slowly down to the knocked down dirty
+ * threshold.
+ */
+static void update_dirty_limit(unsigned long thresh, unsigned long dirty)
+{
+	unsigned long limit = global_dirty_limit;
+
+	/*
+	 * Follow up in one step.
+	 */
+	if (limit < thresh) {
+		limit = thresh;
+		goto update;
+	}
+
+	/*
+	 * Follow down slowly. Use the higher one as the target, because thresh
+	 * may drop below dirty. This is exactly the reason to introduce
+	 * global_dirty_limit which is guaranteed to lie above the dirty pages.
+	 */
+	thresh = max(thresh, dirty);
+	if (limit > thresh) {
+		limit -= (limit - thresh) >> 5;
+		goto update;
+	}
+	return;
+update:
+	global_dirty_limit = limit;
+}
+
+static void global_update_bandwidth(unsigned long thresh,
+				    unsigned long dirty,
+				    unsigned long now)
+{
+	static DEFINE_SPINLOCK(dirty_lock);
+	static unsigned long update_time;
+
+	/*
+	 * check locklessly first to optimize away locking for the most time
+	 */
+	if (time_before(now, update_time + BANDWIDTH_INTERVAL))
+		return;
+
+	spin_lock(&dirty_lock);
+	if (time_after_eq(now, update_time + BANDWIDTH_INTERVAL)) {
+		update_dirty_limit(thresh, dirty);
+		update_time = now;
+	}
+	spin_unlock(&dirty_lock);
+}
+
+void __bdi_update_bandwidth(struct backing_dev_info *bdi,
+			    unsigned long thresh,
+			    unsigned long dirty,
+			    unsigned long bdi_thresh,
+			    unsigned long bdi_dirty,
+			    unsigned long start_time)
+{
+	unsigned long now = jiffies;
+	unsigned long elapsed = now - bdi->bw_time_stamp;
+	unsigned long written;
+
+	/*
+	 * rate-limit, only update once every 200ms.
+	 */
+	if (elapsed < BANDWIDTH_INTERVAL)
+		return;
+
+	written = percpu_counter_read(&bdi->bdi_stat[BDI_WRITTEN]);
+
+	/*
+	 * Skip quiet periods when disk bandwidth is under-utilized.
+	 * (at least 1s idle time between two flusher runs)
+	 */
+	if (elapsed > HZ && time_before(bdi->bw_time_stamp, start_time))
+		goto snapshot;
+
+	if (thresh)
+		global_update_bandwidth(thresh, dirty, now);
+
+	bdi_update_write_bandwidth(bdi, elapsed, written);
+
+snapshot:
+	bdi->written_stamp = written;
+	bdi->bw_time_stamp = now;
+}
+
+static void bdi_update_bandwidth(struct backing_dev_info *bdi,
+				 unsigned long thresh,
+				 unsigned long dirty,
+				 unsigned long bdi_thresh,
+				 unsigned long bdi_dirty,
+				 unsigned long start_time)
+{
+	if (time_is_after_eq_jiffies(bdi->bw_time_stamp + BANDWIDTH_INTERVAL))
+		return;
+	spin_lock(&bdi->wb.list_lock);
+	__bdi_update_bandwidth(bdi, thresh, dirty, bdi_thresh, bdi_dirty,
+			       start_time);
+	spin_unlock(&bdi->wb.list_lock);
+}
+
 /*
  * balance_dirty_pages() must be called by processes which are generating dirty
  * data.  It looks at the number of dirty pages in the machine and will force
@@ -478,27 +652,25 @@
 static void balance_dirty_pages(struct address_space *mapping,
 				unsigned long write_chunk)
 {
-	long nr_reclaimable, bdi_nr_reclaimable;
-	long nr_writeback, bdi_nr_writeback;
+	unsigned long nr_reclaimable, bdi_nr_reclaimable;
+	unsigned long nr_dirty;  /* = file_dirty + writeback + unstable_nfs */
+	unsigned long bdi_dirty;
 	unsigned long background_thresh;
 	unsigned long dirty_thresh;
 	unsigned long bdi_thresh;
+	unsigned long task_bdi_thresh;
+	unsigned long min_task_bdi_thresh;
 	unsigned long pages_written = 0;
 	unsigned long pause = 1;
 	bool dirty_exceeded = false;
+	bool clear_dirty_exceeded = true;
 	struct backing_dev_info *bdi = mapping->backing_dev_info;
+	unsigned long start_time = jiffies;
 
 	for (;;) {
-		struct writeback_control wbc = {
-			.sync_mode	= WB_SYNC_NONE,
-			.older_than_this = NULL,
-			.nr_to_write	= write_chunk,
-			.range_cyclic	= 1,
-		};
-
 		nr_reclaimable = global_page_state(NR_FILE_DIRTY) +
 					global_page_state(NR_UNSTABLE_NFS);
-		nr_writeback = global_page_state(NR_WRITEBACK);
+		nr_dirty = nr_reclaimable + global_page_state(NR_WRITEBACK);
 
 		global_dirty_limits(&background_thresh, &dirty_thresh);
 
@@ -507,12 +679,12 @@
 		 * catch-up. This avoids (excessively) small writeouts
 		 * when the bdi limits are ramping up.
 		 */
-		if (nr_reclaimable + nr_writeback <=
-				(background_thresh + dirty_thresh) / 2)
+		if (nr_dirty <= (background_thresh + dirty_thresh) / 2)
 			break;
 
 		bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh);
-		bdi_thresh = task_dirty_limit(current, bdi_thresh);
+		min_task_bdi_thresh = task_min_dirty_limit(bdi_thresh);
+		task_bdi_thresh = task_dirty_limit(current, bdi_thresh);
 
 		/*
 		 * In order to avoid the stacked BDI deadlock we need
@@ -524,12 +696,14 @@
 		 * actually dirty; with m+n sitting in the percpu
 		 * deltas.
 		 */
-		if (bdi_thresh < 2*bdi_stat_error(bdi)) {
+		if (task_bdi_thresh < 2 * bdi_stat_error(bdi)) {
 			bdi_nr_reclaimable = bdi_stat_sum(bdi, BDI_RECLAIMABLE);
-			bdi_nr_writeback = bdi_stat_sum(bdi, BDI_WRITEBACK);
+			bdi_dirty = bdi_nr_reclaimable +
+				    bdi_stat_sum(bdi, BDI_WRITEBACK);
 		} else {
 			bdi_nr_reclaimable = bdi_stat(bdi, BDI_RECLAIMABLE);
-			bdi_nr_writeback = bdi_stat(bdi, BDI_WRITEBACK);
+			bdi_dirty = bdi_nr_reclaimable +
+				    bdi_stat(bdi, BDI_WRITEBACK);
 		}
 
 		/*
@@ -538,9 +712,10 @@
 		 * bdi or process from holding back light ones; The latter is
 		 * the last resort safeguard.
 		 */
-		dirty_exceeded =
-			(bdi_nr_reclaimable + bdi_nr_writeback > bdi_thresh)
-			|| (nr_reclaimable + nr_writeback > dirty_thresh);
+		dirty_exceeded = (bdi_dirty > task_bdi_thresh) ||
+				  (nr_dirty > dirty_thresh);
+		clear_dirty_exceeded = (bdi_dirty <= min_task_bdi_thresh) &&
+					(nr_dirty <= dirty_thresh);
 
 		if (!dirty_exceeded)
 			break;
@@ -548,6 +723,9 @@
 		if (!bdi->dirty_exceeded)
 			bdi->dirty_exceeded = 1;
 
+		bdi_update_bandwidth(bdi, dirty_thresh, nr_dirty,
+				     bdi_thresh, bdi_dirty, start_time);
+
 		/* Note: nr_reclaimable denotes nr_dirty + nr_unstable.
 		 * Unstable writes are a feature of certain networked
 		 * filesystems (i.e. NFS) in which data may have been
@@ -557,17 +735,40 @@
 		 * threshold otherwise wait until the disk writes catch
 		 * up.
 		 */
-		trace_wbc_balance_dirty_start(&wbc, bdi);
-		if (bdi_nr_reclaimable > bdi_thresh) {
-			writeback_inodes_wb(&bdi->wb, &wbc);
-			pages_written += write_chunk - wbc.nr_to_write;
-			trace_wbc_balance_dirty_written(&wbc, bdi);
+		trace_balance_dirty_start(bdi);
+		if (bdi_nr_reclaimable > task_bdi_thresh) {
+			pages_written += writeback_inodes_wb(&bdi->wb,
+							     write_chunk);
+			trace_balance_dirty_written(bdi, pages_written);
 			if (pages_written >= write_chunk)
 				break;		/* We've done our duty */
 		}
-		trace_wbc_balance_dirty_wait(&wbc, bdi);
 		__set_current_state(TASK_UNINTERRUPTIBLE);
 		io_schedule_timeout(pause);
+		trace_balance_dirty_wait(bdi);
+
+		dirty_thresh = hard_dirty_limit(dirty_thresh);
+		/*
+		 * max-pause area. If dirty exceeded but still within this
+		 * area, no need to sleep for more than 200ms: (a) 8 pages per
+		 * 200ms is typically more than enough to curb heavy dirtiers;
+		 * (b) the pause time limit makes the dirtiers more responsive.
+		 */
+		if (nr_dirty < dirty_thresh +
+			       dirty_thresh / DIRTY_MAXPAUSE_AREA &&
+		    time_after(jiffies, start_time + MAX_PAUSE))
+			break;
+		/*
+		 * pass-good area. When some bdi gets blocked (eg. NFS server
+		 * not responding), or write bandwidth dropped dramatically due
+		 * to concurrent reads, or dirty threshold suddenly dropped and
+		 * the dirty pages cannot be brought down anytime soon (eg. on
+		 * slow USB stick), at least let go of the good bdi's.
+		 */
+		if (nr_dirty < dirty_thresh +
+			       dirty_thresh / DIRTY_PASSGOOD_AREA &&
+		    bdi_dirty < bdi_thresh)
+			break;
 
 		/*
 		 * Increase the delay for each loop, up to our previous
@@ -578,7 +779,8 @@
 			pause = HZ / 10;
 	}
 
-	if (!dirty_exceeded && bdi->dirty_exceeded)
+	/* Clear dirty_exceeded flag only when no task can exceed the limit */
+	if (clear_dirty_exceeded && bdi->dirty_exceeded)
 		bdi->dirty_exceeded = 0;
 
 	if (writeback_in_progress(bdi))
@@ -626,9 +828,13 @@
 void balance_dirty_pages_ratelimited_nr(struct address_space *mapping,
 					unsigned long nr_pages_dirtied)
 {
+	struct backing_dev_info *bdi = mapping->backing_dev_info;
 	unsigned long ratelimit;
 	unsigned long *p;
 
+	if (!bdi_cap_account_dirty(bdi))
+		return;
+
 	ratelimit = ratelimit_pages;
 	if (mapping->backing_dev_info->dirty_exceeded)
 		ratelimit = 8;
@@ -892,12 +1098,12 @@
 			range_whole = 1;
 		cycled = 1; /* ignore range_cyclic tests */
 	}
-	if (wbc->sync_mode == WB_SYNC_ALL)
+	if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
 		tag = PAGECACHE_TAG_TOWRITE;
 	else
 		tag = PAGECACHE_TAG_DIRTY;
 retry:
-	if (wbc->sync_mode == WB_SYNC_ALL)
+	if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
 		tag_pages_for_writeback(mapping, index, end);
 	done_index = index;
 	while (!done && (index <= end)) {
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 0944723..1dbcf88 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1370,21 +1370,12 @@
 
 #ifdef CONFIG_FAIL_PAGE_ALLOC
 
-static struct fail_page_alloc_attr {
+static struct {
 	struct fault_attr attr;
 
 	u32 ignore_gfp_highmem;
 	u32 ignore_gfp_wait;
 	u32 min_order;
-
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-
-	struct dentry *ignore_gfp_highmem_file;
-	struct dentry *ignore_gfp_wait_file;
-	struct dentry *min_order_file;
-
-#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
-
 } fail_page_alloc = {
 	.attr = FAULT_ATTR_INITIALIZER,
 	.ignore_gfp_wait = 1,
@@ -1424,30 +1415,24 @@
 				       "fail_page_alloc");
 	if (err)
 		return err;
-	dir = fail_page_alloc.attr.dentries.dir;
 
-	fail_page_alloc.ignore_gfp_wait_file =
-		debugfs_create_bool("ignore-gfp-wait", mode, dir,
-				      &fail_page_alloc.ignore_gfp_wait);
+	dir = fail_page_alloc.attr.dir;
 
-	fail_page_alloc.ignore_gfp_highmem_file =
-		debugfs_create_bool("ignore-gfp-highmem", mode, dir,
-				      &fail_page_alloc.ignore_gfp_highmem);
-	fail_page_alloc.min_order_file =
-		debugfs_create_u32("min-order", mode, dir,
-				   &fail_page_alloc.min_order);
+	if (!debugfs_create_bool("ignore-gfp-wait", mode, dir,
+				&fail_page_alloc.ignore_gfp_wait))
+		goto fail;
+	if (!debugfs_create_bool("ignore-gfp-highmem", mode, dir,
+				&fail_page_alloc.ignore_gfp_highmem))
+		goto fail;
+	if (!debugfs_create_u32("min-order", mode, dir,
+				&fail_page_alloc.min_order))
+		goto fail;
 
-	if (!fail_page_alloc.ignore_gfp_wait_file ||
-            !fail_page_alloc.ignore_gfp_highmem_file ||
-            !fail_page_alloc.min_order_file) {
-		err = -ENOMEM;
-		debugfs_remove(fail_page_alloc.ignore_gfp_wait_file);
-		debugfs_remove(fail_page_alloc.ignore_gfp_highmem_file);
-		debugfs_remove(fail_page_alloc.min_order_file);
-		cleanup_fault_attr_dentries(&fail_page_alloc.attr);
-	}
+	return 0;
+fail:
+	cleanup_fault_attr_dentries(&fail_page_alloc.attr);
 
-	return err;
+	return -ENOMEM;
 }
 
 late_initcall(fail_page_alloc_debugfs);
diff --git a/mm/rmap.c b/mm/rmap.c
index 9701574..8005080 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -31,11 +31,11 @@
  *               mmlist_lock (in mmput, drain_mmlist and others)
  *               mapping->private_lock (in __set_page_dirty_buffers)
  *               inode->i_lock (in set_page_dirty's __mark_inode_dirty)
- *               inode_wb_list_lock (in set_page_dirty's __mark_inode_dirty)
+ *               bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty)
  *                 sb_lock (within inode_lock in fs/fs-writeback.c)
  *                 mapping->tree_lock (widely used, in set_page_dirty,
  *                           in arch-dependent flush_dcache_mmap_lock,
- *                           within inode_wb_list_lock in __sync_single_inode)
+ *                           within bdi.wb->list_lock in __sync_single_inode)
  *
  * anon_vma->mutex,mapping->i_mutex      (memory_failure, collect_procs_anon)
  *   ->tasklist_lock
diff --git a/mm/slob.c b/mm/slob.c
index 0ae8818..bf39181 100644
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -70,7 +70,7 @@
 
 #include <trace/events/kmem.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * slob_block has a field 'units', which indicates size of block if +ve,
diff --git a/mm/slub.c b/mm/slub.c
index f8f5e8e..eb5a8f9 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2,10 +2,11 @@
  * SLUB: A slab allocator that limits cache line use instead of queuing
  * objects in per cpu and per node lists.
  *
- * The allocator synchronizes using per slab locks and only
- * uses a centralized lock to manage a pool of partial slabs.
+ * The allocator synchronizes using per slab locks or atomic operatios
+ * and only uses a centralized lock to manage a pool of partial slabs.
  *
  * (C) 2007 SGI, Christoph Lameter
+ * (C) 2011 Linux Foundation, Christoph Lameter
  */
 
 #include <linux/mm.h>
@@ -33,15 +34,27 @@
 
 /*
  * Lock order:
- *   1. slab_lock(page)
- *   2. slab->list_lock
+ *   1. slub_lock (Global Semaphore)
+ *   2. node->list_lock
+ *   3. slab_lock(page) (Only on some arches and for debugging)
  *
- *   The slab_lock protects operations on the object of a particular
- *   slab and its metadata in the page struct. If the slab lock
- *   has been taken then no allocations nor frees can be performed
- *   on the objects in the slab nor can the slab be added or removed
- *   from the partial or full lists since this would mean modifying
- *   the page_struct of the slab.
+ *   slub_lock
+ *
+ *   The role of the slub_lock is to protect the list of all the slabs
+ *   and to synchronize major metadata changes to slab cache structures.
+ *
+ *   The slab_lock is only used for debugging and on arches that do not
+ *   have the ability to do a cmpxchg_double. It only protects the second
+ *   double word in the page struct. Meaning
+ *	A. page->freelist	-> List of object free in a page
+ *	B. page->counters	-> Counters of objects
+ *	C. page->frozen		-> frozen state
+ *
+ *   If a slab is frozen then it is exempt from list management. It is not
+ *   on any list. The processor that froze the slab is the one who can
+ *   perform list operations on the page. Other processors may put objects
+ *   onto the freelist but the processor that froze the slab is the only
+ *   one that can retrieve the objects from the page's freelist.
  *
  *   The list_lock protects the partial and full list on each node and
  *   the partial slab counter. If taken then no new slabs may be added or
@@ -54,20 +67,6 @@
  *   slabs, operations can continue without any centralized lock. F.e.
  *   allocating a long series of objects that fill up slabs does not require
  *   the list lock.
- *
- *   The lock order is sometimes inverted when we are trying to get a slab
- *   off a list. We take the list_lock and then look for a page on the list
- *   to use. While we do that objects in the slabs may be freed. We can
- *   only operate on the slab if we have also taken the slab_lock. So we use
- *   a slab_trylock() on the slab. If trylock was successful then no frees
- *   can occur anymore and we can use the slab for allocations etc. If the
- *   slab_trylock() does not succeed then frees are in progress in the slab and
- *   we must stay away from it for a while since we may cause a bouncing
- *   cacheline if we try to acquire the lock. So go onto the next slab.
- *   If all pages are busy then we may allocate a new slab instead of reusing
- *   a partial slab. A new slab has no one operating on it and thus there is
- *   no danger of cacheline contention.
- *
  *   Interrupts are disabled during allocation and deallocation in order to
  *   make the slab allocator safe to use in the context of an irq. In addition
  *   interrupts are disabled to ensure that the processor does not change
@@ -132,6 +131,9 @@
 /* Enable to test recovery from slab corruption on boot */
 #undef SLUB_RESILIENCY_TEST
 
+/* Enable to log cmpxchg failures */
+#undef SLUB_DEBUG_CMPXCHG
+
 /*
  * Mininum number of partial slabs. These will be left on the partial
  * lists even if they are empty. kmem_cache_shrink may reclaim them.
@@ -167,10 +169,11 @@
 
 #define OO_SHIFT	16
 #define OO_MASK		((1 << OO_SHIFT) - 1)
-#define MAX_OBJS_PER_PAGE	65535 /* since page.objects is u16 */
+#define MAX_OBJS_PER_PAGE	32767 /* since page.objects is u15 */
 
 /* Internal SLUB flags */
 #define __OBJECT_POISON		0x80000000UL /* Poison object */
+#define __CMPXCHG_DOUBLE	0x40000000UL /* Use cmpxchg_double */
 
 static int kmem_size = sizeof(struct kmem_cache);
 
@@ -343,11 +346,99 @@
 	return x.x & OO_MASK;
 }
 
+/*
+ * Per slab locking using the pagelock
+ */
+static __always_inline void slab_lock(struct page *page)
+{
+	bit_spin_lock(PG_locked, &page->flags);
+}
+
+static __always_inline void slab_unlock(struct page *page)
+{
+	__bit_spin_unlock(PG_locked, &page->flags);
+}
+
+/* Interrupts must be disabled (for the fallback code to work right) */
+static inline bool __cmpxchg_double_slab(struct kmem_cache *s, struct page *page,
+		void *freelist_old, unsigned long counters_old,
+		void *freelist_new, unsigned long counters_new,
+		const char *n)
+{
+	VM_BUG_ON(!irqs_disabled());
+#ifdef CONFIG_CMPXCHG_DOUBLE
+	if (s->flags & __CMPXCHG_DOUBLE) {
+		if (cmpxchg_double(&page->freelist,
+			freelist_old, counters_old,
+			freelist_new, counters_new))
+		return 1;
+	} else
+#endif
+	{
+		slab_lock(page);
+		if (page->freelist == freelist_old && page->counters == counters_old) {
+			page->freelist = freelist_new;
+			page->counters = counters_new;
+			slab_unlock(page);
+			return 1;
+		}
+		slab_unlock(page);
+	}
+
+	cpu_relax();
+	stat(s, CMPXCHG_DOUBLE_FAIL);
+
+#ifdef SLUB_DEBUG_CMPXCHG
+	printk(KERN_INFO "%s %s: cmpxchg double redo ", n, s->name);
+#endif
+
+	return 0;
+}
+
+static inline bool cmpxchg_double_slab(struct kmem_cache *s, struct page *page,
+		void *freelist_old, unsigned long counters_old,
+		void *freelist_new, unsigned long counters_new,
+		const char *n)
+{
+#ifdef CONFIG_CMPXCHG_DOUBLE
+	if (s->flags & __CMPXCHG_DOUBLE) {
+		if (cmpxchg_double(&page->freelist,
+			freelist_old, counters_old,
+			freelist_new, counters_new))
+		return 1;
+	} else
+#endif
+	{
+		unsigned long flags;
+
+		local_irq_save(flags);
+		slab_lock(page);
+		if (page->freelist == freelist_old && page->counters == counters_old) {
+			page->freelist = freelist_new;
+			page->counters = counters_new;
+			slab_unlock(page);
+			local_irq_restore(flags);
+			return 1;
+		}
+		slab_unlock(page);
+		local_irq_restore(flags);
+	}
+
+	cpu_relax();
+	stat(s, CMPXCHG_DOUBLE_FAIL);
+
+#ifdef SLUB_DEBUG_CMPXCHG
+	printk(KERN_INFO "%s %s: cmpxchg double redo ", n, s->name);
+#endif
+
+	return 0;
+}
+
 #ifdef CONFIG_SLUB_DEBUG
 /*
  * Determine a map of object in use on a page.
  *
- * Slab lock or node listlock must be held to guarantee that the page does
+ * Node listlock must be held to guarantee that the page does
  * not vanish from under us.
  */
 static void get_map(struct kmem_cache *s, struct page *page, unsigned long *map)
@@ -838,10 +929,11 @@
 static int on_freelist(struct kmem_cache *s, struct page *page, void *search)
 {
 	int nr = 0;
-	void *fp = page->freelist;
+	void *fp;
 	void *object = NULL;
 	unsigned long max_objects;
 
+	fp = page->freelist;
 	while (fp && nr <= page->objects) {
 		if (fp == search)
 			return 1;
@@ -946,26 +1038,27 @@
 
 /*
  * Tracking of fully allocated slabs for debugging purposes.
+ *
+ * list_lock must be held.
  */
-static void add_full(struct kmem_cache_node *n, struct page *page)
+static void add_full(struct kmem_cache *s,
+	struct kmem_cache_node *n, struct page *page)
 {
-	spin_lock(&n->list_lock);
-	list_add(&page->lru, &n->full);
-	spin_unlock(&n->list_lock);
-}
-
-static void remove_full(struct kmem_cache *s, struct page *page)
-{
-	struct kmem_cache_node *n;
-
 	if (!(s->flags & SLAB_STORE_USER))
 		return;
 
-	n = get_node(s, page_to_nid(page));
+	list_add(&page->lru, &n->full);
+}
 
-	spin_lock(&n->list_lock);
+/*
+ * list_lock must be held.
+ */
+static void remove_full(struct kmem_cache *s, struct page *page)
+{
+	if (!(s->flags & SLAB_STORE_USER))
+		return;
+
 	list_del(&page->lru);
-	spin_unlock(&n->list_lock);
 }
 
 /* Tracking of the number of slabs for debugging purposes */
@@ -1021,11 +1114,6 @@
 	if (!check_slab(s, page))
 		goto bad;
 
-	if (!on_freelist(s, page, object)) {
-		object_err(s, page, object, "Object already allocated");
-		goto bad;
-	}
-
 	if (!check_valid_pointer(s, page, object)) {
 		object_err(s, page, object, "Freelist Pointer check fails");
 		goto bad;
@@ -1058,6 +1146,12 @@
 static noinline int free_debug_processing(struct kmem_cache *s,
 		 struct page *page, void *object, unsigned long addr)
 {
+	unsigned long flags;
+	int rc = 0;
+
+	local_irq_save(flags);
+	slab_lock(page);
+
 	if (!check_slab(s, page))
 		goto fail;
 
@@ -1072,7 +1166,7 @@
 	}
 
 	if (!check_object(s, page, object, SLUB_RED_ACTIVE))
-		return 0;
+		goto out;
 
 	if (unlikely(s != page->slab)) {
 		if (!PageSlab(page)) {
@@ -1089,18 +1183,19 @@
 		goto fail;
 	}
 
-	/* Special debug activities for freeing objects */
-	if (!PageSlubFrozen(page) && !page->freelist)
-		remove_full(s, page);
 	if (s->flags & SLAB_STORE_USER)
 		set_track(s, object, TRACK_FREE, addr);
 	trace(s, page, object, 0);
 	init_object(s, object, SLUB_RED_INACTIVE);
-	return 1;
+	rc = 1;
+out:
+	slab_unlock(page);
+	local_irq_restore(flags);
+	return rc;
 
 fail:
 	slab_fix(s, "Object at 0x%p not freed", object);
-	return 0;
+	goto out;
 }
 
 static int __init setup_slub_debug(char *str)
@@ -1200,7 +1295,9 @@
 			{ return 1; }
 static inline int check_object(struct kmem_cache *s, struct page *page,
 			void *object, u8 val) { return 1; }
-static inline void add_full(struct kmem_cache_node *n, struct page *page) {}
+static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
+					struct page *page) {}
+static inline void remove_full(struct kmem_cache *s, struct page *page) {}
 static inline unsigned long kmem_cache_flags(unsigned long objsize,
 	unsigned long flags, const char *name,
 	void (*ctor)(void *))
@@ -1252,6 +1349,11 @@
 	struct kmem_cache_order_objects oo = s->oo;
 	gfp_t alloc_gfp;
 
+	flags &= gfp_allowed_mask;
+
+	if (flags & __GFP_WAIT)
+		local_irq_enable();
+
 	flags |= s->allocflags;
 
 	/*
@@ -1268,12 +1370,17 @@
 		 * Try a lower order alloc if possible
 		 */
 		page = alloc_slab_page(flags, node, oo);
-		if (!page)
-			return NULL;
 
-		stat(s, ORDER_FALLBACK);
+		if (page)
+			stat(s, ORDER_FALLBACK);
 	}
 
+	if (flags & __GFP_WAIT)
+		local_irq_disable();
+
+	if (!page)
+		return NULL;
+
 	if (kmemcheck_enabled
 		&& !(s->flags & (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS))) {
 		int pages = 1 << oo_order(oo);
@@ -1341,6 +1448,7 @@
 
 	page->freelist = start;
 	page->inuse = 0;
+	page->frozen = 1;
 out:
 	return page;
 }
@@ -1418,77 +1526,87 @@
 }
 
 /*
- * Per slab locking using the pagelock
+ * Management of partially allocated slabs.
+ *
+ * list_lock must be held.
  */
-static __always_inline void slab_lock(struct page *page)
-{
-	bit_spin_lock(PG_locked, &page->flags);
-}
-
-static __always_inline void slab_unlock(struct page *page)
-{
-	__bit_spin_unlock(PG_locked, &page->flags);
-}
-
-static __always_inline int slab_trylock(struct page *page)
-{
-	int rc = 1;
-
-	rc = bit_spin_trylock(PG_locked, &page->flags);
-	return rc;
-}
-
-/*
- * Management of partially allocated slabs
- */
-static void add_partial(struct kmem_cache_node *n,
+static inline void add_partial(struct kmem_cache_node *n,
 				struct page *page, int tail)
 {
-	spin_lock(&n->list_lock);
 	n->nr_partial++;
 	if (tail)
 		list_add_tail(&page->lru, &n->partial);
 	else
 		list_add(&page->lru, &n->partial);
-	spin_unlock(&n->list_lock);
 }
 
-static inline void __remove_partial(struct kmem_cache_node *n,
+/*
+ * list_lock must be held.
+ */
+static inline void remove_partial(struct kmem_cache_node *n,
 					struct page *page)
 {
 	list_del(&page->lru);
 	n->nr_partial--;
 }
 
-static void remove_partial(struct kmem_cache *s, struct page *page)
-{
-	struct kmem_cache_node *n = get_node(s, page_to_nid(page));
-
-	spin_lock(&n->list_lock);
-	__remove_partial(n, page);
-	spin_unlock(&n->list_lock);
-}
-
 /*
- * Lock slab and remove from the partial list.
+ * Lock slab, remove from the partial list and put the object into the
+ * per cpu freelist.
  *
  * Must hold list_lock.
  */
-static inline int lock_and_freeze_slab(struct kmem_cache_node *n,
-							struct page *page)
+static inline int acquire_slab(struct kmem_cache *s,
+		struct kmem_cache_node *n, struct page *page)
 {
-	if (slab_trylock(page)) {
-		__remove_partial(n, page);
-		__SetPageSlubFrozen(page);
+	void *freelist;
+	unsigned long counters;
+	struct page new;
+
+	/*
+	 * Zap the freelist and set the frozen bit.
+	 * The old freelist is the list of objects for the
+	 * per cpu allocation list.
+	 */
+	do {
+		freelist = page->freelist;
+		counters = page->counters;
+		new.counters = counters;
+		new.inuse = page->objects;
+
+		VM_BUG_ON(new.frozen);
+		new.frozen = 1;
+
+	} while (!__cmpxchg_double_slab(s, page,
+			freelist, counters,
+			NULL, new.counters,
+			"lock and freeze"));
+
+	remove_partial(n, page);
+
+	if (freelist) {
+		/* Populate the per cpu freelist */
+		this_cpu_write(s->cpu_slab->freelist, freelist);
+		this_cpu_write(s->cpu_slab->page, page);
+		this_cpu_write(s->cpu_slab->node, page_to_nid(page));
 		return 1;
+	} else {
+		/*
+		 * Slab page came from the wrong list. No object to allocate
+		 * from. Put it onto the correct list and continue partial
+		 * scan.
+		 */
+		printk(KERN_ERR "SLUB: %s : Page without available objects on"
+			" partial list\n", s->name);
+		return 0;
 	}
-	return 0;
 }
 
 /*
  * Try to allocate a partial slab from a specific node.
  */
-static struct page *get_partial_node(struct kmem_cache_node *n)
+static struct page *get_partial_node(struct kmem_cache *s,
+					struct kmem_cache_node *n)
 {
 	struct page *page;
 
@@ -1503,7 +1621,7 @@
 
 	spin_lock(&n->list_lock);
 	list_for_each_entry(page, &n->partial, lru)
-		if (lock_and_freeze_slab(n, page))
+		if (acquire_slab(s, n, page))
 			goto out;
 	page = NULL;
 out:
@@ -1554,7 +1672,7 @@
 
 		if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
 				n->nr_partial > s->min_partial) {
-			page = get_partial_node(n);
+			page = get_partial_node(s, n);
 			if (page) {
 				put_mems_allowed();
 				return page;
@@ -1574,60 +1692,13 @@
 	struct page *page;
 	int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;
 
-	page = get_partial_node(get_node(s, searchnode));
+	page = get_partial_node(s, get_node(s, searchnode));
 	if (page || node != NUMA_NO_NODE)
 		return page;
 
 	return get_any_partial(s, flags);
 }
 
-/*
- * Move a page back to the lists.
- *
- * Must be called with the slab lock held.
- *
- * On exit the slab lock will have been dropped.
- */
-static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail)
-	__releases(bitlock)
-{
-	struct kmem_cache_node *n = get_node(s, page_to_nid(page));
-
-	__ClearPageSlubFrozen(page);
-	if (page->inuse) {
-
-		if (page->freelist) {
-			add_partial(n, page, tail);
-			stat(s, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);
-		} else {
-			stat(s, DEACTIVATE_FULL);
-			if (kmem_cache_debug(s) && (s->flags & SLAB_STORE_USER))
-				add_full(n, page);
-		}
-		slab_unlock(page);
-	} else {
-		stat(s, DEACTIVATE_EMPTY);
-		if (n->nr_partial < s->min_partial) {
-			/*
-			 * Adding an empty slab to the partial slabs in order
-			 * to avoid page allocator overhead. This slab needs
-			 * to come after the other slabs with objects in
-			 * so that the others get filled first. That way the
-			 * size of the partial list stays small.
-			 *
-			 * kmem_cache_shrink can reclaim any empty slabs from
-			 * the partial list.
-			 */
-			add_partial(n, page, 1);
-			slab_unlock(page);
-		} else {
-			slab_unlock(page);
-			stat(s, FREE_SLAB);
-			discard_slab(s, page);
-		}
-	}
-}
-
 #ifdef CONFIG_PREEMPT
 /*
  * Calculate the next globally unique transaction for disambiguiation
@@ -1697,42 +1768,161 @@
 /*
  * Remove the cpu slab
  */
+
+/*
+ * Remove the cpu slab
+ */
 static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
-	__releases(bitlock)
 {
+	enum slab_modes { M_NONE, M_PARTIAL, M_FULL, M_FREE };
 	struct page *page = c->page;
-	int tail = 1;
+	struct kmem_cache_node *n = get_node(s, page_to_nid(page));
+	int lock = 0;
+	enum slab_modes l = M_NONE, m = M_NONE;
+	void *freelist;
+	void *nextfree;
+	int tail = 0;
+	struct page new;
+	struct page old;
 
-	if (page->freelist)
+	if (page->freelist) {
 		stat(s, DEACTIVATE_REMOTE_FREES);
-	/*
-	 * Merge cpu freelist into slab freelist. Typically we get here
-	 * because both freelists are empty. So this is unlikely
-	 * to occur.
-	 */
-	while (unlikely(c->freelist)) {
-		void **object;
-
-		tail = 0;	/* Hot objects. Put the slab first */
-
-		/* Retrieve object from cpu_freelist */
-		object = c->freelist;
-		c->freelist = get_freepointer(s, c->freelist);
-
-		/* And put onto the regular freelist */
-		set_freepointer(s, object, page->freelist);
-		page->freelist = object;
-		page->inuse--;
+		tail = 1;
 	}
-	c->page = NULL;
+
 	c->tid = next_tid(c->tid);
-	unfreeze_slab(s, page, tail);
+	c->page = NULL;
+	freelist = c->freelist;
+	c->freelist = NULL;
+
+	/*
+	 * Stage one: Free all available per cpu objects back
+	 * to the page freelist while it is still frozen. Leave the
+	 * last one.
+	 *
+	 * There is no need to take the list->lock because the page
+	 * is still frozen.
+	 */
+	while (freelist && (nextfree = get_freepointer(s, freelist))) {
+		void *prior;
+		unsigned long counters;
+
+		do {
+			prior = page->freelist;
+			counters = page->counters;
+			set_freepointer(s, freelist, prior);
+			new.counters = counters;
+			new.inuse--;
+			VM_BUG_ON(!new.frozen);
+
+		} while (!__cmpxchg_double_slab(s, page,
+			prior, counters,
+			freelist, new.counters,
+			"drain percpu freelist"));
+
+		freelist = nextfree;
+	}
+
+	/*
+	 * Stage two: Ensure that the page is unfrozen while the
+	 * list presence reflects the actual number of objects
+	 * during unfreeze.
+	 *
+	 * We setup the list membership and then perform a cmpxchg
+	 * with the count. If there is a mismatch then the page
+	 * is not unfrozen but the page is on the wrong list.
+	 *
+	 * Then we restart the process which may have to remove
+	 * the page from the list that we just put it on again
+	 * because the number of objects in the slab may have
+	 * changed.
+	 */
+redo:
+
+	old.freelist = page->freelist;
+	old.counters = page->counters;
+	VM_BUG_ON(!old.frozen);
+
+	/* Determine target state of the slab */
+	new.counters = old.counters;
+	if (freelist) {
+		new.inuse--;
+		set_freepointer(s, freelist, old.freelist);
+		new.freelist = freelist;
+	} else
+		new.freelist = old.freelist;
+
+	new.frozen = 0;
+
+	if (!new.inuse && n->nr_partial < s->min_partial)
+		m = M_FREE;
+	else if (new.freelist) {
+		m = M_PARTIAL;
+		if (!lock) {
+			lock = 1;
+			/*
+			 * Taking the spinlock removes the possiblity
+			 * that acquire_slab() will see a slab page that
+			 * is frozen
+			 */
+			spin_lock(&n->list_lock);
+		}
+	} else {
+		m = M_FULL;
+		if (kmem_cache_debug(s) && !lock) {
+			lock = 1;
+			/*
+			 * This also ensures that the scanning of full
+			 * slabs from diagnostic functions will not see
+			 * any frozen slabs.
+			 */
+			spin_lock(&n->list_lock);
+		}
+	}
+
+	if (l != m) {
+
+		if (l == M_PARTIAL)
+
+			remove_partial(n, page);
+
+		else if (l == M_FULL)
+
+			remove_full(s, page);
+
+		if (m == M_PARTIAL) {
+
+			add_partial(n, page, tail);
+			stat(s, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);
+
+		} else if (m == M_FULL) {
+
+			stat(s, DEACTIVATE_FULL);
+			add_full(s, n, page);
+
+		}
+	}
+
+	l = m;
+	if (!__cmpxchg_double_slab(s, page,
+				old.freelist, old.counters,
+				new.freelist, new.counters,
+				"unfreezing slab"))
+		goto redo;
+
+	if (lock)
+		spin_unlock(&n->list_lock);
+
+	if (m == M_FREE) {
+		stat(s, DEACTIVATE_EMPTY);
+		discard_slab(s, page);
+		stat(s, FREE_SLAB);
+	}
 }
 
 static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
 {
 	stat(s, CPUSLAB_FLUSH);
-	slab_lock(c->page);
 	deactivate_slab(s, c);
 }
 
@@ -1861,6 +2051,8 @@
 	void **object;
 	struct page *page;
 	unsigned long flags;
+	struct page new;
+	unsigned long counters;
 
 	local_irq_save(flags);
 #ifdef CONFIG_PREEMPT
@@ -1879,72 +2071,97 @@
 	if (!page)
 		goto new_slab;
 
-	slab_lock(page);
-	if (unlikely(!node_match(c, node)))
-		goto another_slab;
+	if (unlikely(!node_match(c, node))) {
+		stat(s, ALLOC_NODE_MISMATCH);
+		deactivate_slab(s, c);
+		goto new_slab;
+	}
+
+	stat(s, ALLOC_SLOWPATH);
+
+	do {
+		object = page->freelist;
+		counters = page->counters;
+		new.counters = counters;
+		VM_BUG_ON(!new.frozen);
+
+		/*
+		 * If there is no object left then we use this loop to
+		 * deactivate the slab which is simple since no objects
+		 * are left in the slab and therefore we do not need to
+		 * put the page back onto the partial list.
+		 *
+		 * If there are objects left then we retrieve them
+		 * and use them to refill the per cpu queue.
+		*/
+
+		new.inuse = page->objects;
+		new.frozen = object != NULL;
+
+	} while (!__cmpxchg_double_slab(s, page,
+			object, counters,
+			NULL, new.counters,
+			"__slab_alloc"));
+
+	if (unlikely(!object)) {
+		c->page = NULL;
+		stat(s, DEACTIVATE_BYPASS);
+		goto new_slab;
+	}
 
 	stat(s, ALLOC_REFILL);
 
 load_freelist:
-	object = page->freelist;
-	if (unlikely(!object))
-		goto another_slab;
-	if (kmem_cache_debug(s))
-		goto debug;
-
+	VM_BUG_ON(!page->frozen);
 	c->freelist = get_freepointer(s, object);
-	page->inuse = page->objects;
-	page->freelist = NULL;
-
-	slab_unlock(page);
 	c->tid = next_tid(c->tid);
 	local_irq_restore(flags);
-	stat(s, ALLOC_SLOWPATH);
 	return object;
 
-another_slab:
-	deactivate_slab(s, c);
-
 new_slab:
 	page = get_partial(s, gfpflags, node);
 	if (page) {
 		stat(s, ALLOC_FROM_PARTIAL);
-		c->node = page_to_nid(page);
-		c->page = page;
+		object = c->freelist;
+
+		if (kmem_cache_debug(s))
+			goto debug;
 		goto load_freelist;
 	}
 
-	gfpflags &= gfp_allowed_mask;
-	if (gfpflags & __GFP_WAIT)
-		local_irq_enable();
-
 	page = new_slab(s, gfpflags, node);
 
-	if (gfpflags & __GFP_WAIT)
-		local_irq_disable();
-
 	if (page) {
 		c = __this_cpu_ptr(s->cpu_slab);
-		stat(s, ALLOC_SLAB);
 		if (c->page)
 			flush_slab(s, c);
 
-		slab_lock(page);
-		__SetPageSlubFrozen(page);
+		/*
+		 * No other reference to the page yet so we can
+		 * muck around with it freely without cmpxchg
+		 */
+		object = page->freelist;
+		page->freelist = NULL;
+		page->inuse = page->objects;
+
+		stat(s, ALLOC_SLAB);
 		c->node = page_to_nid(page);
 		c->page = page;
+
+		if (kmem_cache_debug(s))
+			goto debug;
 		goto load_freelist;
 	}
 	if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit())
 		slab_out_of_memory(s, gfpflags, node);
 	local_irq_restore(flags);
 	return NULL;
-debug:
-	if (!alloc_debug_processing(s, page, object, addr))
-		goto another_slab;
 
-	page->inuse++;
-	page->freelist = get_freepointer(s, object);
+debug:
+	if (!object || !alloc_debug_processing(s, page, object, addr))
+		goto new_slab;
+
+	c->freelist = get_freepointer(s, object);
 	deactivate_slab(s, c);
 	c->page = NULL;
 	c->node = NUMA_NO_NODE;
@@ -2096,40 +2313,75 @@
 {
 	void *prior;
 	void **object = (void *)x;
-	unsigned long flags;
+	int was_frozen;
+	int inuse;
+	struct page new;
+	unsigned long counters;
+	struct kmem_cache_node *n = NULL;
+	unsigned long uninitialized_var(flags);
 
-	local_irq_save(flags);
-	slab_lock(page);
 	stat(s, FREE_SLOWPATH);
 
 	if (kmem_cache_debug(s) && !free_debug_processing(s, page, x, addr))
-		goto out_unlock;
+		return;
 
-	prior = page->freelist;
-	set_freepointer(s, object, prior);
-	page->freelist = object;
-	page->inuse--;
+	do {
+		prior = page->freelist;
+		counters = page->counters;
+		set_freepointer(s, object, prior);
+		new.counters = counters;
+		was_frozen = new.frozen;
+		new.inuse--;
+		if ((!new.inuse || !prior) && !was_frozen && !n) {
+                        n = get_node(s, page_to_nid(page));
+			/*
+			 * Speculatively acquire the list_lock.
+			 * If the cmpxchg does not succeed then we may
+			 * drop the list_lock without any processing.
+			 *
+			 * Otherwise the list_lock will synchronize with
+			 * other processors updating the list of slabs.
+			 */
+                        spin_lock_irqsave(&n->list_lock, flags);
+		}
+		inuse = new.inuse;
 
-	if (unlikely(PageSlubFrozen(page))) {
-		stat(s, FREE_FROZEN);
-		goto out_unlock;
-	}
+	} while (!cmpxchg_double_slab(s, page,
+		prior, counters,
+		object, new.counters,
+		"__slab_free"));
 
-	if (unlikely(!page->inuse))
-		goto slab_empty;
+	if (likely(!n)) {
+                /*
+		 * The list lock was not taken therefore no list
+		 * activity can be necessary.
+		 */
+                if (was_frozen)
+                        stat(s, FREE_FROZEN);
+                return;
+        }
 
 	/*
-	 * Objects left in the slab. If it was not on the partial list before
-	 * then add it.
+	 * was_frozen may have been set after we acquired the list_lock in
+	 * an earlier loop. So we need to check it here again.
 	 */
-	if (unlikely(!prior)) {
-		add_partial(get_node(s, page_to_nid(page)), page, 1);
-		stat(s, FREE_ADD_PARTIAL);
-	}
+	if (was_frozen)
+		stat(s, FREE_FROZEN);
+	else {
+		if (unlikely(!inuse && n->nr_partial > s->min_partial))
+                        goto slab_empty;
 
-out_unlock:
-	slab_unlock(page);
-	local_irq_restore(flags);
+		/*
+		 * Objects left in the slab. If it was not on the partial list before
+		 * then add it.
+		 */
+		if (unlikely(!prior)) {
+			remove_full(s, page);
+			add_partial(n, page, 0);
+			stat(s, FREE_ADD_PARTIAL);
+		}
+	}
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	return;
 
 slab_empty:
@@ -2137,11 +2389,11 @@
 		/*
 		 * Slab still on the partial list.
 		 */
-		remove_partial(s, page);
+		remove_partial(n, page);
 		stat(s, FREE_REMOVE_PARTIAL);
 	}
-	slab_unlock(page);
-	local_irq_restore(flags);
+
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	stat(s, FREE_SLAB);
 	discard_slab(s, page);
 }
@@ -2415,7 +2667,6 @@
 {
 	struct page *page;
 	struct kmem_cache_node *n;
-	unsigned long flags;
 
 	BUG_ON(kmem_cache_node->size < sizeof(struct kmem_cache_node));
 
@@ -2433,6 +2684,7 @@
 	BUG_ON(!n);
 	page->freelist = get_freepointer(kmem_cache_node, n);
 	page->inuse++;
+	page->frozen = 0;
 	kmem_cache_node->node[node] = n;
 #ifdef CONFIG_SLUB_DEBUG
 	init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);
@@ -2441,14 +2693,7 @@
 	init_kmem_cache_node(n, kmem_cache_node);
 	inc_slabs_node(kmem_cache_node, node, page->objects);
 
-	/*
-	 * lockdep requires consistent irq usage for each lock
-	 * so even though there cannot be a race this early in
-	 * the boot sequence, we still disable irqs.
-	 */
-	local_irq_save(flags);
 	add_partial(n, page, 0);
-	local_irq_restore(flags);
 }
 
 static void free_kmem_cache_nodes(struct kmem_cache *s)
@@ -2654,6 +2899,12 @@
 		}
 	}
 
+#ifdef CONFIG_CMPXCHG_DOUBLE
+	if (system_has_cmpxchg_double() && (s->flags & SLAB_DEBUG_FLAGS) == 0)
+		/* Enable fast mode */
+		s->flags |= __CMPXCHG_DOUBLE;
+#endif
+
 	/*
 	 * The larger the object size is, the more pages we want on the partial
 	 * list to avoid pounding the page allocator excessively.
@@ -2726,7 +2977,7 @@
 	spin_lock_irqsave(&n->list_lock, flags);
 	list_for_each_entry_safe(page, h, &n->partial, lru) {
 		if (!page->inuse) {
-			__remove_partial(n, page);
+			remove_partial(n, page);
 			discard_slab(s, page);
 		} else {
 			list_slab_objects(s, page,
@@ -3094,14 +3345,8 @@
 		 * list_lock. page->inuse here is the upper limit.
 		 */
 		list_for_each_entry_safe(page, t, &n->partial, lru) {
-			if (!page->inuse && slab_trylock(page)) {
-				/*
-				 * Must hold slab lock here because slab_free
-				 * may have freed the last object and be
-				 * waiting to release the slab.
-				 */
-				__remove_partial(n, page);
-				slab_unlock(page);
+			if (!page->inuse) {
+				remove_partial(n, page);
 				discard_slab(s, page);
 			} else {
 				list_move(&page->lru,
@@ -3689,12 +3934,9 @@
 static void validate_slab_slab(struct kmem_cache *s, struct page *page,
 						unsigned long *map)
 {
-	if (slab_trylock(page)) {
-		validate_slab(s, page, map);
-		slab_unlock(page);
-	} else
-		printk(KERN_INFO "SLUB %s: Skipped busy slab 0x%p\n",
-			s->name, page);
+	slab_lock(page);
+	validate_slab(s, page, map);
+	slab_unlock(page);
 }
 
 static int validate_slab_node(struct kmem_cache *s,
@@ -4342,8 +4584,10 @@
 				const char *buf, size_t length)
 {
 	s->flags &= ~SLAB_DEBUG_FREE;
-	if (buf[0] == '1')
+	if (buf[0] == '1') {
+		s->flags &= ~__CMPXCHG_DOUBLE;
 		s->flags |= SLAB_DEBUG_FREE;
+	}
 	return length;
 }
 SLAB_ATTR(sanity_checks);
@@ -4357,8 +4601,10 @@
 							size_t length)
 {
 	s->flags &= ~SLAB_TRACE;
-	if (buf[0] == '1')
+	if (buf[0] == '1') {
+		s->flags &= ~__CMPXCHG_DOUBLE;
 		s->flags |= SLAB_TRACE;
+	}
 	return length;
 }
 SLAB_ATTR(trace);
@@ -4375,8 +4621,10 @@
 		return -EBUSY;
 
 	s->flags &= ~SLAB_RED_ZONE;
-	if (buf[0] == '1')
+	if (buf[0] == '1') {
+		s->flags &= ~__CMPXCHG_DOUBLE;
 		s->flags |= SLAB_RED_ZONE;
+	}
 	calculate_sizes(s, -1);
 	return length;
 }
@@ -4394,8 +4642,10 @@
 		return -EBUSY;
 
 	s->flags &= ~SLAB_POISON;
-	if (buf[0] == '1')
+	if (buf[0] == '1') {
+		s->flags &= ~__CMPXCHG_DOUBLE;
 		s->flags |= SLAB_POISON;
+	}
 	calculate_sizes(s, -1);
 	return length;
 }
@@ -4413,8 +4663,10 @@
 		return -EBUSY;
 
 	s->flags &= ~SLAB_STORE_USER;
-	if (buf[0] == '1')
+	if (buf[0] == '1') {
+		s->flags &= ~__CMPXCHG_DOUBLE;
 		s->flags |= SLAB_STORE_USER;
+	}
 	calculate_sizes(s, -1);
 	return length;
 }
@@ -4579,6 +4831,7 @@
 STAT_ATTR(ALLOC_FROM_PARTIAL, alloc_from_partial);
 STAT_ATTR(ALLOC_SLAB, alloc_slab);
 STAT_ATTR(ALLOC_REFILL, alloc_refill);
+STAT_ATTR(ALLOC_NODE_MISMATCH, alloc_node_mismatch);
 STAT_ATTR(FREE_SLAB, free_slab);
 STAT_ATTR(CPUSLAB_FLUSH, cpuslab_flush);
 STAT_ATTR(DEACTIVATE_FULL, deactivate_full);
@@ -4586,7 +4839,10 @@
 STAT_ATTR(DEACTIVATE_TO_HEAD, deactivate_to_head);
 STAT_ATTR(DEACTIVATE_TO_TAIL, deactivate_to_tail);
 STAT_ATTR(DEACTIVATE_REMOTE_FREES, deactivate_remote_frees);
+STAT_ATTR(DEACTIVATE_BYPASS, deactivate_bypass);
 STAT_ATTR(ORDER_FALLBACK, order_fallback);
+STAT_ATTR(CMPXCHG_DOUBLE_CPU_FAIL, cmpxchg_double_cpu_fail);
+STAT_ATTR(CMPXCHG_DOUBLE_FAIL, cmpxchg_double_fail);
 #endif
 
 static struct attribute *slab_attrs[] = {
@@ -4636,6 +4892,7 @@
 	&alloc_from_partial_attr.attr,
 	&alloc_slab_attr.attr,
 	&alloc_refill_attr.attr,
+	&alloc_node_mismatch_attr.attr,
 	&free_slab_attr.attr,
 	&cpuslab_flush_attr.attr,
 	&deactivate_full_attr.attr,
@@ -4643,7 +4900,10 @@
 	&deactivate_to_head_attr.attr,
 	&deactivate_to_tail_attr.attr,
 	&deactivate_remote_frees_attr.attr,
+	&deactivate_bypass_attr.attr,
 	&order_fallback_attr.attr,
+	&cmpxchg_double_fail_attr.attr,
+	&cmpxchg_double_cpu_fail_attr.attr,
 #endif
 #ifdef CONFIG_FAILSLAB
 	&failslab_attr.attr,
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index ab8494c..464621d 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -26,7 +26,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pfn.h>
 #include <linux/kmemleak.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
 #include <asm/shmparam.h>
diff --git a/mm/vmscan.c b/mm/vmscan.c
index febbc04..7ef6912 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -95,8 +95,6 @@
 	/* Can pages be swapped as part of reclaim? */
 	int may_swap;
 
-	int swappiness;
-
 	int order;
 
 	/*
@@ -107,6 +105,7 @@
 
 	/* Which cgroup do we reclaim from */
 	struct mem_cgroup *mem_cgroup;
+	struct memcg_scanrecord *memcg_record;
 
 	/*
 	 * Nodemask of nodes allowed by the caller. If NULL, all nodes
@@ -173,7 +172,8 @@
 				struct scan_control *sc, enum lru_list lru)
 {
 	if (!scanning_global_lru(sc))
-		return mem_cgroup_zone_nr_lru_pages(sc->mem_cgroup, zone, lru);
+		return mem_cgroup_zone_nr_lru_pages(sc->mem_cgroup,
+				zone_to_nid(zone), zone_idx(zone), BIT(lru));
 
 	return zone_page_state(zone, NR_LRU_BASE + lru);
 }
@@ -1349,6 +1349,8 @@
 			int file = is_file_lru(lru);
 			int numpages = hpage_nr_pages(page);
 			reclaim_stat->recent_rotated[file] += numpages;
+			if (!scanning_global_lru(sc))
+				sc->memcg_record->nr_rotated[file] += numpages;
 		}
 		if (!pagevec_add(&pvec, page)) {
 			spin_unlock_irq(&zone->lru_lock);
@@ -1392,6 +1394,10 @@
 
 	reclaim_stat->recent_scanned[0] += *nr_anon;
 	reclaim_stat->recent_scanned[1] += *nr_file;
+	if (!scanning_global_lru(sc)) {
+		sc->memcg_record->nr_scanned[0] += *nr_anon;
+		sc->memcg_record->nr_scanned[1] += *nr_file;
+	}
 }
 
 /*
@@ -1505,6 +1511,9 @@
 		nr_reclaimed += shrink_page_list(&page_list, zone, sc);
 	}
 
+	if (!scanning_global_lru(sc))
+		sc->memcg_record->nr_freed[file] += nr_reclaimed;
+
 	local_irq_disable();
 	if (current_is_kswapd())
 		__count_vm_events(KSWAPD_STEAL, nr_reclaimed);
@@ -1604,6 +1613,8 @@
 	}
 
 	reclaim_stat->recent_scanned[file] += nr_taken;
+	if (!scanning_global_lru(sc))
+		sc->memcg_record->nr_scanned[file] += nr_taken;
 
 	__count_zone_vm_events(PGREFILL, zone, pgscanned);
 	if (file)
@@ -1655,6 +1666,8 @@
 	 * get_scan_ratio.
 	 */
 	reclaim_stat->recent_rotated[file] += nr_rotated;
+	if (!scanning_global_lru(sc))
+		sc->memcg_record->nr_rotated[file] += nr_rotated;
 
 	move_active_pages_to_lru(zone, &l_active,
 						LRU_ACTIVE + file * LRU_FILE);
@@ -1770,6 +1783,13 @@
 	return shrink_inactive_list(nr_to_scan, zone, sc, priority, file);
 }
 
+static int vmscan_swappiness(struct scan_control *sc)
+{
+	if (scanning_global_lru(sc))
+		return vm_swappiness;
+	return mem_cgroup_swappiness(sc->mem_cgroup);
+}
+
 /*
  * Determine how aggressively the anon and file LRU lists should be
  * scanned.  The relative value of each set of LRU lists is determined
@@ -1789,6 +1809,7 @@
 	enum lru_list l;
 	int noswap = 0;
 	int force_scan = 0;
+	unsigned long nr_force_scan[2];
 
 
 	anon  = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_ANON) +
@@ -1811,6 +1832,8 @@
 		fraction[0] = 0;
 		fraction[1] = 1;
 		denominator = 1;
+		nr_force_scan[0] = 0;
+		nr_force_scan[1] = SWAP_CLUSTER_MAX;
 		goto out;
 	}
 
@@ -1822,6 +1845,8 @@
 			fraction[0] = 1;
 			fraction[1] = 0;
 			denominator = 1;
+			nr_force_scan[0] = SWAP_CLUSTER_MAX;
+			nr_force_scan[1] = 0;
 			goto out;
 		}
 	}
@@ -1830,8 +1855,8 @@
 	 * With swappiness at 100, anonymous and file have the same priority.
 	 * This scanning priority is essentially the inverse of IO cost.
 	 */
-	anon_prio = sc->swappiness;
-	file_prio = 200 - sc->swappiness;
+	anon_prio = vmscan_swappiness(sc);
+	file_prio = 200 - vmscan_swappiness(sc);
 
 	/*
 	 * OK, so we have swap space and a fair amount of page cache
@@ -1870,6 +1895,11 @@
 	fraction[0] = ap;
 	fraction[1] = fp;
 	denominator = ap + fp + 1;
+	if (force_scan) {
+		unsigned long scan = SWAP_CLUSTER_MAX;
+		nr_force_scan[0] = div64_u64(scan * ap, denominator);
+		nr_force_scan[1] = div64_u64(scan * fp, denominator);
+	}
 out:
 	for_each_evictable_lru(l) {
 		int file = is_file_lru(l);
@@ -1890,12 +1920,8 @@
 		 * memcg, priority drop can cause big latency. So, it's better
 		 * to scan small amount. See may_noscan above.
 		 */
-		if (!scan && force_scan) {
-			if (file)
-				scan = SWAP_CLUSTER_MAX;
-			else if (!noswap)
-				scan = SWAP_CLUSTER_MAX;
-		}
+		if (!scan && force_scan)
+			scan = nr_force_scan[file];
 		nr[l] = scan;
 	}
 }
@@ -2220,7 +2246,6 @@
 		.nr_to_reclaim = SWAP_CLUSTER_MAX,
 		.may_unmap = 1,
 		.may_swap = 1,
-		.swappiness = vm_swappiness,
 		.order = order,
 		.mem_cgroup = NULL,
 		.nodemask = nodemask,
@@ -2243,10 +2268,10 @@
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR
 
 unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
-						gfp_t gfp_mask, bool noswap,
-						unsigned int swappiness,
-						struct zone *zone,
-						unsigned long *nr_scanned)
+					gfp_t gfp_mask, bool noswap,
+					struct zone *zone,
+					struct memcg_scanrecord *rec,
+					unsigned long *scanned)
 {
 	struct scan_control sc = {
 		.nr_scanned = 0,
@@ -2254,10 +2279,11 @@
 		.may_writepage = !laptop_mode,
 		.may_unmap = 1,
 		.may_swap = !noswap,
-		.swappiness = swappiness,
 		.order = 0,
 		.mem_cgroup = mem,
+		.memcg_record = rec,
 	};
+	unsigned long start, end;
 
 	sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
 			(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
@@ -2266,6 +2292,7 @@
 						      sc.may_writepage,
 						      sc.gfp_mask);
 
+	start = sched_clock();
 	/*
 	 * NOTE: Although we can get the priority field, using it
 	 * here is not a good idea, since it limits the pages we can scan.
@@ -2274,29 +2301,34 @@
 	 * the priority and make it zero.
 	 */
 	shrink_zone(0, zone, &sc);
+	end = sched_clock();
+
+	if (rec)
+		rec->elapsed += end - start;
+	*scanned = sc.nr_scanned;
 
 	trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
 
-	*nr_scanned = sc.nr_scanned;
 	return sc.nr_reclaimed;
 }
 
 unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont,
 					   gfp_t gfp_mask,
 					   bool noswap,
-					   unsigned int swappiness)
+					   struct memcg_scanrecord *rec)
 {
 	struct zonelist *zonelist;
 	unsigned long nr_reclaimed;
+	unsigned long start, end;
 	int nid;
 	struct scan_control sc = {
 		.may_writepage = !laptop_mode,
 		.may_unmap = 1,
 		.may_swap = !noswap,
 		.nr_to_reclaim = SWAP_CLUSTER_MAX,
-		.swappiness = swappiness,
 		.order = 0,
 		.mem_cgroup = mem_cont,
+		.memcg_record = rec,
 		.nodemask = NULL, /* we don't care the placement */
 		.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
 				(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK),
@@ -2305,6 +2337,7 @@
 		.gfp_mask = sc.gfp_mask,
 	};
 
+	start = sched_clock();
 	/*
 	 * Unlike direct reclaim via alloc_pages(), memcg's reclaim doesn't
 	 * take care of from where we get pages. So the node where we start the
@@ -2319,6 +2352,9 @@
 					    sc.gfp_mask);
 
 	nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink);
+	end = sched_clock();
+	if (rec)
+		rec->elapsed += end - start;
 
 	trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed);
 
@@ -2445,7 +2481,6 @@
 		 * we want to put equal scanning pressure on each zone.
 		 */
 		.nr_to_reclaim = ULONG_MAX,
-		.swappiness = vm_swappiness,
 		.order = order,
 		.mem_cgroup = NULL,
 	};
@@ -2915,7 +2950,6 @@
 		.may_writepage = 1,
 		.nr_to_reclaim = nr_to_reclaim,
 		.hibernation_mode = 1,
-		.swappiness = vm_swappiness,
 		.order = 0,
 	};
 	struct shrink_control shrink = {
@@ -3102,7 +3136,6 @@
 		.nr_to_reclaim = max_t(unsigned long, nr_pages,
 				       SWAP_CLUSTER_MAX),
 		.gfp_mask = gfp_mask,
-		.swappiness = vm_swappiness,
 		.order = order,
 	};
 	struct shrink_control shrink = {
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 934e221..9d40a07 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -695,7 +695,7 @@
 	ether_setup(dev);
 
 	dev->priv_flags		|= IFF_802_1Q_VLAN;
-	dev->priv_flags		&= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	dev->tx_queue_len	= 0;
 
 	dev->netdev_ops		= &vlan_netdev_ops;
diff --git a/net/atm/atm_misc.c b/net/atm/atm_misc.c
index fc63526..f41f026 100644
--- a/net/atm/atm_misc.c
+++ b/net/atm/atm_misc.c
@@ -9,7 +9,7 @@
 #include <linux/sonet.h>
 #include <linux/bitops.h>
 #include <linux/errno.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 int atm_charge(struct atm_vcc *vcc, int truesize)
 {
diff --git a/net/atm/clip.c b/net/atm/clip.c
index 4bc8c67..8523940 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -37,7 +37,7 @@
 #include <linux/uaccess.h>
 #include <asm/byteorder.h> /* for htons etc. */
 #include <asm/system.h> /* save/restore_flags */
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "common.h"
 #include "resources.h"
diff --git a/net/atm/common.c b/net/atm/common.c
index 22b963d..14ff9fe 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -23,7 +23,7 @@
 #include <linux/uaccess.h>
 #include <linux/poll.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "resources.h"		/* atm_find_dev */
 #include "common.h"		/* prototypes */
diff --git a/net/atm/lec.c b/net/atm/lec.c
index ba48daa..215c9fa 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -1335,7 +1335,7 @@
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/param.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/inetdevice.h>
 #include <net/route.h>
 
diff --git a/net/atm/proc.c b/net/atm/proc.c
index be3afde..0d020de 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -27,7 +27,7 @@
 #include <net/atmclip.h>
 #include <linux/uaccess.h>
 #include <linux/param.h> /* for HZ */
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "resources.h"
 #include "common.h" /* atm_proc_init prototype */
 #include "signaling.h" /* to get sigd - ugly too */
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index 8c100c9..d4f5dff 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -231,6 +231,7 @@
 	dev->addr_len = ETH_ALEN;
 
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops = &bnep_netdev_ops;
 
 	dev->watchdog_timeo  = HZ * 2;
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index e0dfbc1..68def3b 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -21,7 +21,7 @@
 #include <linux/jhash.h>
 #include <linux/random.h>
 #include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/unaligned.h>
 #include "br_private.h"
 
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 78b55f4..c340e2e 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -486,13 +486,10 @@
 	m = list_first_entry(&con->out_queue,
 		       struct ceph_msg, list_head);
 	con->out_msg = m;
-	if (test_bit(LOSSYTX, &con->state)) {
-		list_del_init(&m->list_head);
-	} else {
-		/* put message on sent list */
-		ceph_msg_get(m);
-		list_move_tail(&m->list_head, &con->out_sent);
-	}
+
+	/* put message on sent list */
+	ceph_msg_get(m);
+	list_move_tail(&m->list_head, &con->out_sent);
 
 	/*
 	 * only assign outgoing seq # if we haven't sent this message
@@ -1399,6 +1396,7 @@
 			break;
 		dout("got ack for seq %llu type %d at %p\n", seq,
 		     le16_to_cpu(m->hdr.type), m);
+		m->ack_stamp = jiffies;
 		ceph_msg_remove(m);
 	}
 	prepare_read_tag(con);
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 7330c27..ce310ee 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -1085,9 +1085,15 @@
 		req = list_entry(osdc->req_lru.next, struct ceph_osd_request,
 				 r_req_lru_item);
 
+		/* hasn't been long enough since we sent it? */
 		if (time_before(jiffies, req->r_stamp + timeout))
 			break;
 
+		/* hasn't been long enough since it was acked? */
+		if (req->r_request->ack_stamp == 0 ||
+		    time_before(jiffies, req->r_request->ack_stamp + timeout))
+			break;
+
 		BUG_ON(req == last_req && req->r_stamp == last_stamp);
 		last_req = req;
 		last_stamp = req->r_stamp;
diff --git a/net/core/dev.c b/net/core/dev.c
index 9444c5c..17d67b5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4497,10 +4497,10 @@
 		 */
 		if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
 			__dev_set_promiscuity(dev, 1);
-			dev->uc_promisc = 1;
+			dev->uc_promisc = true;
 		} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
 			__dev_set_promiscuity(dev, -1);
-			dev->uc_promisc = 0;
+			dev->uc_promisc = false;
 		}
 
 		if (ops->ndo_set_multicast_list)
diff --git a/net/core/flow.c b/net/core/flow.c
index 990703b..bf32c33 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -22,7 +22,7 @@
 #include <linux/cpumask.h>
 #include <linux/mutex.h>
 #include <net/flow.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/security.h>
 
 struct flow_cache_entry {
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f76079c..e35a6fb 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -1070,7 +1070,9 @@
 		len = num_arg(&user_buffer[i], 10, &value);
 		if (len < 0)
 			return len;
-
+		if ((value > 0) &&
+		    (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
+			return -ENOTSUPP;
 		i += len;
 		pkt_dev->clone_skb = value;
 
@@ -3555,7 +3557,6 @@
 	pkt_dev->min_pkt_size = ETH_ZLEN;
 	pkt_dev->max_pkt_size = ETH_ZLEN;
 	pkt_dev->nfrags = 0;
-	pkt_dev->clone_skb = pg_clone_skb_d;
 	pkt_dev->delay = pg_delay_d;
 	pkt_dev->count = pg_count_d;
 	pkt_dev->sofar = 0;
@@ -3563,7 +3564,6 @@
 	pkt_dev->udp_src_max = 9;
 	pkt_dev->udp_dst_min = 9;
 	pkt_dev->udp_dst_max = 9;
-
 	pkt_dev->vlan_p = 0;
 	pkt_dev->vlan_cfi = 0;
 	pkt_dev->vlan_id = 0xffff;
@@ -3575,6 +3575,8 @@
 	err = pktgen_setup_dev(pkt_dev, ifname);
 	if (err)
 		goto out1;
+	if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
+		pkt_dev->clone_skb = pg_clone_skb_d;
 
 	pkt_dev->entry = proc_create_data(ifname, 0600, pg_proc_dir,
 					  &pktgen_if_fops, pkt_dev);
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
index 2bd8e53..9e885f1 100644
--- a/net/decnet/dn_fib.c
+++ b/net/decnet/dn_fib.c
@@ -30,7 +30,7 @@
 #include <linux/netdevice.h>
 #include <linux/timer.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index 0dc3fe6..7f0eb08 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -38,7 +38,7 @@
 #include <linux/seq_file.h>
 #include <linux/rcupdate.h>
 #include <linux/jhash.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <net/net_namespace.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
index cd0354e..a9a62f2 100644
--- a/net/decnet/dn_table.c
+++ b/net/decnet/dn_table.c
@@ -25,7 +25,7 @@
 #include <linux/netdevice.h>
 #include <linux/timer.h>
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <linux/route.h> /* RTF_xxx */
 #include <net/neighbour.h>
diff --git a/net/decnet/dn_timer.c b/net/decnet/dn_timer.c
index 0982571..67f691b 100644
--- a/net/decnet/dn_timer.c
+++ b/net/decnet/dn_timer.c
@@ -22,7 +22,7 @@
 #include <linux/timer.h>
 #include <linux/spinlock.h>
 #include <net/sock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <net/flow.h>
 #include <net/dn.h>
 
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 5cffb63..27997d3 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -231,6 +231,7 @@
  * eth_header_cache - fill cache entry from neighbour
  * @neigh: source neighbour
  * @hh: destination cache entry
+ * @type: Ethernet type field
  * Create an Ethernet header template from the neighbour.
  */
 int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type)
@@ -339,6 +340,7 @@
 	dev->addr_len		= ETH_ALEN;
 	dev->tx_queue_len	= 1000;	/* Ethernet wants good queues */
 	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
+	dev->priv_flags		= IFF_TX_SKB_SHARING;
 
 	memset(dev->broadcast, 0xFF, ETH_ALEN);
 
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 2b3c23c..2c2a98e 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -50,7 +50,7 @@
 #include <net/tcp.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/bug.h>
 #include <asm/unaligned.h>
 
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 37b3c18..bc19bd0 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1134,15 +1134,15 @@
 					struct in_device *in_dev)
 
 {
-	struct in_ifaddr *ifa = in_dev->ifa_list;
+	struct in_ifaddr *ifa;
 
-	if (!ifa)
-		return;
-
-	arp_send(ARPOP_REQUEST, ETH_P_ARP,
-		 ifa->ifa_local, dev,
-		 ifa->ifa_local, NULL,
-		 dev->dev_addr, NULL);
+	for (ifa = in_dev->ifa_list; ifa;
+	     ifa = ifa->ifa_next) {
+		arp_send(ARPOP_REQUEST, ETH_P_ARP,
+			 ifa->ifa_local, dev,
+			 ifa->ifa_local, NULL,
+			 dev->dev_addr, NULL);
+	}
 }
 
 /* Called only under RTNL semaphore */
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 0852678..1457acb 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -38,7 +38,7 @@
  */
 
 #include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/byteorder.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a06c53c..a55500c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1481,6 +1481,8 @@
 static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
 {
 	struct in6_addr addr;
+	if (ifp->prefix_len == 127) /* RFC 6164 */
+		return;
 	ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
 	if (ipv6_addr_any(&addr))
 		return;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 36c2842..0bc9888 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -40,7 +40,7 @@
 #include <linux/slab.h>
 
 #include <asm/uaccess.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <net/icmp.h>
 #include <net/ip.h>
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index f2b7138..075a380 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -51,7 +51,7 @@
 #include <linux/cpu.h>
 #include <linux/reboot.h>
 #include <net/iucv/iucv.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/ebcdic.h>
 #include <asm/io.h>
 #include <asm/irq.h>
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index ed8a233..ad4ac26 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -55,7 +55,7 @@
 #include <net/protocol.h>
 
 #include <asm/byteorder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "l2tp_core.h"
 
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index a8193f5..d2726a7 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -103,7 +103,7 @@
 static void l2tp_eth_dev_setup(struct net_device *dev)
 {
 	ether_setup(dev);
-
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops		= &l2tp_eth_netdev_ops;
 	dev->destructor		= free_netdev;
 }
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 39a21d0..f42cd09 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -97,7 +97,7 @@
 #include <net/xfrm.h>
 
 #include <asm/byteorder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "l2tp_core.h"
 
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index cd5fb40..556e7e6 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -698,6 +698,7 @@
 static void ieee80211_if_setup(struct net_device *dev)
 {
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops = &ieee80211_dataif_ops;
 	dev->destructor = free_netdev;
 }
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 2e7ccbb..2d8158a 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -33,7 +33,7 @@
 #include <net/netfilter/nf_log.h>
 #include <net/netfilter/nfnetlink_log.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_BRIDGE_NETFILTER
 #include "../bridge/br_private.h"
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 49132bd..00bd475 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -31,7 +31,7 @@
 #include <net/sock.h>
 #include <net/netfilter/nf_queue.h>
 
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #ifdef CONFIG_BRIDGE_NETFILTER
 #include "../bridge/br_private.h"
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index bae5756..dd53a36 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -39,7 +39,7 @@
 #include <net/genetlink.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "netlabel_user.h"
 #include "netlabel_cipso_v4.h"
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 1b83e00..b528dd9 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -39,7 +39,7 @@
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
 #include <asm/bug.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "netlabel_domainhash.h"
 #include "netlabel_unlabeled.h"
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 4f251b1..dff8a08 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -42,7 +42,7 @@
 #include <net/ipv6.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "netlabel_domainhash.h"
 #include "netlabel_user.h"
diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h
index 0a25838..8db37f4 100644
--- a/net/netlabel/netlabel_mgmt.h
+++ b/net/netlabel/netlabel_mgmt.h
@@ -32,7 +32,7 @@
 #define _NETLABEL_MGMT_H
 
 #include <net/netlabel.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 /*
  * The following NetLabel payloads are supported by the management interface.
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 9a290ef..f1ecf84 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -52,7 +52,7 @@
 #include <net/net_namespace.h>
 #include <net/netlabel.h>
 #include <asm/bug.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "netlabel_user.h"
 #include "netlabel_addrlist.h"
diff --git a/net/socket.c b/net/socket.c
index 02dc82d..b1cbbcd 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -467,7 +467,7 @@
 	struct inode *inode;
 	struct socket *sock;
 
-	inode = new_inode(sock_mnt->mnt_sb);
+	inode = new_inode_pseudo(sock_mnt->mnt_sb);
 	if (!inode)
 		return NULL;
 
@@ -580,7 +580,7 @@
 }
 EXPORT_SYMBOL(sock_sendmsg);
 
-int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size)
+static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size)
 {
 	struct kiocb iocb;
 	struct sock_iocb siocb;
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index b2198e6..ffd243d 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -4,6 +4,10 @@
 config SUNRPC_GSS
 	tristate
 
+config SUNRPC_BACKCHANNEL
+	bool
+	depends on SUNRPC
+
 config SUNRPC_XPRT_RDMA
 	tristate
 	depends on SUNRPC && INFINIBAND && INFINIBAND_ADDR_TRANS && EXPERIMENTAL
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
index 9d2fca5..8209a04 100644
--- a/net/sunrpc/Makefile
+++ b/net/sunrpc/Makefile
@@ -13,6 +13,6 @@
 	    addr.o rpcb_clnt.o timer.o xdr.o \
 	    sunrpc_syms.o cache.o rpc_pipe.o \
 	    svc_xprt.o
-sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
+sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o bc_svc.o
 sunrpc-$(CONFIG_PROC_FS) += stats.o
 sunrpc-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
index cf06af3..91eaa26 100644
--- a/net/sunrpc/backchannel_rqst.c
+++ b/net/sunrpc/backchannel_rqst.c
@@ -29,8 +29,6 @@
 #define RPCDBG_FACILITY	RPCDBG_TRANS
 #endif
 
-#if defined(CONFIG_NFS_V4_1)
-
 /*
  * Helper routines that track the number of preallocation elements
  * on the transport.
@@ -174,7 +172,7 @@
 	dprintk("RPC:       setup backchannel transport failed\n");
 	return -1;
 }
-EXPORT_SYMBOL(xprt_setup_backchannel);
+EXPORT_SYMBOL_GPL(xprt_setup_backchannel);
 
 /*
  * Destroys the backchannel preallocated structures.
@@ -204,7 +202,7 @@
 	dprintk("RPC:        backchannel list empty= %s\n",
 		list_empty(&xprt->bc_pa_list) ? "true" : "false");
 }
-EXPORT_SYMBOL(xprt_destroy_backchannel);
+EXPORT_SYMBOL_GPL(xprt_destroy_backchannel);
 
 /*
  * One or more rpc_rqst structure have been preallocated during the
@@ -279,4 +277,3 @@
 	spin_unlock_bh(&xprt->bc_pa_lock);
 }
 
-#endif /* CONFIG_NFS_V4_1 */
diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c
index 1dd1a68..0b2eb38 100644
--- a/net/sunrpc/bc_svc.c
+++ b/net/sunrpc/bc_svc.c
@@ -27,8 +27,6 @@
  * reply over an existing open connection previously established by the client.
  */
 
-#if defined(CONFIG_NFS_V4_1)
-
 #include <linux/module.h>
 
 #include <linux/sunrpc/xprt.h>
@@ -63,4 +61,3 @@
 	return ret;
 }
 
-#endif /* CONFIG_NFS_V4_1 */
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index c50818f..c5347d2 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -64,9 +64,9 @@
 static void	call_bind(struct rpc_task *task);
 static void	call_bind_status(struct rpc_task *task);
 static void	call_transmit(struct rpc_task *task);
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 static void	call_bc_transmit(struct rpc_task *task);
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 static void	call_status(struct rpc_task *task);
 static void	call_transmit_status(struct rpc_task *task);
 static void	call_refresh(struct rpc_task *task);
@@ -715,7 +715,7 @@
 }
 EXPORT_SYMBOL_GPL(rpc_call_async);
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /**
  * rpc_run_bc_task - Allocate a new RPC task for backchannel use, then run
  * rpc_execute against it
@@ -758,7 +758,7 @@
 	dprintk("RPC: rpc_run_bc_task: task= %p\n", task);
 	return task;
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 void
 rpc_call_start(struct rpc_task *task)
@@ -1361,7 +1361,7 @@
 	}
 }
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /*
  * 5b.	Send the backchannel RPC reply.  On error, drop the reply.  In
  * addition, disconnect on connectivity errors.
@@ -1425,7 +1425,7 @@
 	}
 	rpc_wake_up_queued_task(&req->rq_xprt->pending, task);
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 /*
  * 6.	Sort out the RPC call status
@@ -1550,8 +1550,7 @@
 	kxdrdproc_t	decode = task->tk_msg.rpc_proc->p_decode;
 	__be32		*p;
 
-	dprintk("RPC: %5u call_decode (status %d)\n",
-			task->tk_pid, task->tk_status);
+	dprint_status(task);
 
 	if (task->tk_flags & RPC_CALL_MAJORSEEN) {
 		if (clnt->cl_chatty)
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 4814e24..d12ffa5 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -97,14 +97,16 @@
 /*
  * Add new request to a priority queue.
  */
-static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue, struct rpc_task *task)
+static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue,
+		struct rpc_task *task,
+		unsigned char queue_priority)
 {
 	struct list_head *q;
 	struct rpc_task *t;
 
 	INIT_LIST_HEAD(&task->u.tk_wait.links);
-	q = &queue->tasks[task->tk_priority];
-	if (unlikely(task->tk_priority > queue->maxpriority))
+	q = &queue->tasks[queue_priority];
+	if (unlikely(queue_priority > queue->maxpriority))
 		q = &queue->tasks[queue->maxpriority];
 	list_for_each_entry(t, q, u.tk_wait.list) {
 		if (t->tk_owner == task->tk_owner) {
@@ -123,12 +125,14 @@
  * improve overall performance.
  * Everyone else gets appended to the queue to ensure proper FIFO behavior.
  */
-static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
+static void __rpc_add_wait_queue(struct rpc_wait_queue *queue,
+		struct rpc_task *task,
+		unsigned char queue_priority)
 {
 	BUG_ON (RPC_IS_QUEUED(task));
 
 	if (RPC_IS_PRIORITY(queue))
-		__rpc_add_wait_queue_priority(queue, task);
+		__rpc_add_wait_queue_priority(queue, task, queue_priority);
 	else if (RPC_IS_SWAPPER(task))
 		list_add(&task->u.tk_wait.list, &queue->tasks[0]);
 	else
@@ -311,13 +315,15 @@
  * NB: An RPC task will only receive interrupt-driven events as long
  * as it's on a wait queue.
  */
-static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
-			rpc_action action)
+static void __rpc_sleep_on_priority(struct rpc_wait_queue *q,
+		struct rpc_task *task,
+		rpc_action action,
+		unsigned char queue_priority)
 {
 	dprintk("RPC: %5u sleep_on(queue \"%s\" time %lu)\n",
 			task->tk_pid, rpc_qname(q), jiffies);
 
-	__rpc_add_wait_queue(q, task);
+	__rpc_add_wait_queue(q, task, queue_priority);
 
 	BUG_ON(task->tk_callback != NULL);
 	task->tk_callback = action;
@@ -334,11 +340,25 @@
 	 * Protect the queue operations.
 	 */
 	spin_lock_bh(&q->lock);
-	__rpc_sleep_on(q, task, action);
+	__rpc_sleep_on_priority(q, task, action, task->tk_priority);
 	spin_unlock_bh(&q->lock);
 }
 EXPORT_SYMBOL_GPL(rpc_sleep_on);
 
+void rpc_sleep_on_priority(struct rpc_wait_queue *q, struct rpc_task *task,
+		rpc_action action, int priority)
+{
+	/* We shouldn't ever put an inactive task to sleep */
+	BUG_ON(!RPC_IS_ACTIVATED(task));
+
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_bh(&q->lock);
+	__rpc_sleep_on_priority(q, task, action, priority - RPC_PRIORITY_LOW);
+	spin_unlock_bh(&q->lock);
+}
+
 /**
  * __rpc_do_wake_up_task - wake up a single rpc_task
  * @queue: wait queue
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 2b90292..6a69a11 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1252,7 +1252,7 @@
 	}
 }
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /*
  * Process a backchannel RPC request that arrived over an existing
  * outbound connection
@@ -1300,8 +1300,8 @@
 		return 0;
 	}
 }
-EXPORT_SYMBOL(bc_svc_process);
-#endif /* CONFIG_NFS_V4_1 */
+EXPORT_SYMBOL_GPL(bc_svc_process);
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 /*
  * Return (transport-specific) limit on the rpc payload.
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index f2cb5b8..767d494 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -68,12 +68,12 @@
 static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
 					  struct net *, struct sockaddr *,
 					  int, int);
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int,
 					     struct net *, struct sockaddr *,
 					     int, int);
 static void svc_bc_sock_free(struct svc_xprt *xprt);
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 static struct lock_class_key svc_key[2];
@@ -1243,7 +1243,7 @@
 	return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
 }
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int,
 					     struct net *, struct sockaddr *,
 					     int, int);
@@ -1284,7 +1284,7 @@
 {
 	svc_unreg_xprt_class(&svc_tcp_bc_class);
 }
-#else /* CONFIG_NFS_V4_1 */
+#else /* CONFIG_SUNRPC_BACKCHANNEL */
 static void svc_init_bc_xprt_sock(void)
 {
 }
@@ -1292,7 +1292,7 @@
 static void svc_cleanup_bc_xprt_sock(void)
 {
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 static struct svc_xprt_ops svc_tcp_ops = {
 	.xpo_create = svc_tcp_create,
@@ -1623,7 +1623,7 @@
 	kfree(svsk);
 }
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /*
  * Create a back channel svc_xprt which shares the fore channel socket.
  */
@@ -1662,4 +1662,4 @@
 	if (xprt)
 		kfree(container_of(xprt, struct svc_sock, sk_xprt));
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index f008c14..277ebd4 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -126,7 +126,7 @@
 	kaddr[buf->page_base + len] = '\0';
 	kunmap_atomic(kaddr, KM_USER0);
 }
-EXPORT_SYMBOL(xdr_terminate_string);
+EXPORT_SYMBOL_GPL(xdr_terminate_string);
 
 void
 xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index ce5eb68..9b6a4d1 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -62,6 +62,7 @@
 /*
  * Local functions
  */
+static void	 xprt_init(struct rpc_xprt *xprt, struct net *net);
 static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
 static void	xprt_connect_status(struct rpc_task *task);
 static int      __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
@@ -191,10 +192,10 @@
  * transport connects from colliding with writes.  No congestion control
  * is provided.
  */
-int xprt_reserve_xprt(struct rpc_task *task)
+int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
 {
 	struct rpc_rqst *req = task->tk_rqstp;
-	struct rpc_xprt	*xprt = req->rq_xprt;
+	int priority;
 
 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
 		if (task == xprt->snd_task)
@@ -202,8 +203,10 @@
 		goto out_sleep;
 	}
 	xprt->snd_task = task;
-	req->rq_bytes_sent = 0;
-	req->rq_ntrans++;
+	if (req != NULL) {
+		req->rq_bytes_sent = 0;
+		req->rq_ntrans++;
+	}
 
 	return 1;
 
@@ -212,10 +215,13 @@
 			task->tk_pid, xprt);
 	task->tk_timeout = 0;
 	task->tk_status = -EAGAIN;
-	if (req->rq_ntrans)
-		rpc_sleep_on(&xprt->resend, task, NULL);
+	if (req == NULL)
+		priority = RPC_PRIORITY_LOW;
+	else if (!req->rq_ntrans)
+		priority = RPC_PRIORITY_NORMAL;
 	else
-		rpc_sleep_on(&xprt->sending, task, NULL);
+		priority = RPC_PRIORITY_HIGH;
+	rpc_sleep_on_priority(&xprt->sending, task, NULL, priority);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(xprt_reserve_xprt);
@@ -239,22 +245,24 @@
  * integrated into the decision of whether a request is allowed to be
  * woken up and given access to the transport.
  */
-int xprt_reserve_xprt_cong(struct rpc_task *task)
+int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
 {
-	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst *req = task->tk_rqstp;
+	int priority;
 
 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
 		if (task == xprt->snd_task)
 			return 1;
 		goto out_sleep;
 	}
+	if (req == NULL) {
+		xprt->snd_task = task;
+		return 1;
+	}
 	if (__xprt_get_cong(xprt, task)) {
 		xprt->snd_task = task;
-		if (req) {
-			req->rq_bytes_sent = 0;
-			req->rq_ntrans++;
-		}
+		req->rq_bytes_sent = 0;
+		req->rq_ntrans++;
 		return 1;
 	}
 	xprt_clear_locked(xprt);
@@ -262,10 +270,13 @@
 	dprintk("RPC: %5u failed to lock transport %p\n", task->tk_pid, xprt);
 	task->tk_timeout = 0;
 	task->tk_status = -EAGAIN;
-	if (req && req->rq_ntrans)
-		rpc_sleep_on(&xprt->resend, task, NULL);
+	if (req == NULL)
+		priority = RPC_PRIORITY_LOW;
+	else if (!req->rq_ntrans)
+		priority = RPC_PRIORITY_NORMAL;
 	else
-		rpc_sleep_on(&xprt->sending, task, NULL);
+		priority = RPC_PRIORITY_HIGH;
+	rpc_sleep_on_priority(&xprt->sending, task, NULL, priority);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(xprt_reserve_xprt_cong);
@@ -275,7 +286,7 @@
 	int retval;
 
 	spin_lock_bh(&xprt->transport_lock);
-	retval = xprt->ops->reserve_xprt(task);
+	retval = xprt->ops->reserve_xprt(xprt, task);
 	spin_unlock_bh(&xprt->transport_lock);
 	return retval;
 }
@@ -288,12 +299,9 @@
 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
 		return;
 
-	task = rpc_wake_up_next(&xprt->resend);
-	if (!task) {
-		task = rpc_wake_up_next(&xprt->sending);
-		if (!task)
-			goto out_unlock;
-	}
+	task = rpc_wake_up_next(&xprt->sending);
+	if (task == NULL)
+		goto out_unlock;
 
 	req = task->tk_rqstp;
 	xprt->snd_task = task;
@@ -310,24 +318,25 @@
 static void __xprt_lock_write_next_cong(struct rpc_xprt *xprt)
 {
 	struct rpc_task *task;
+	struct rpc_rqst *req;
 
 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
 		return;
 	if (RPCXPRT_CONGESTED(xprt))
 		goto out_unlock;
-	task = rpc_wake_up_next(&xprt->resend);
-	if (!task) {
-		task = rpc_wake_up_next(&xprt->sending);
-		if (!task)
-			goto out_unlock;
+	task = rpc_wake_up_next(&xprt->sending);
+	if (task == NULL)
+		goto out_unlock;
+
+	req = task->tk_rqstp;
+	if (req == NULL) {
+		xprt->snd_task = task;
+		return;
 	}
 	if (__xprt_get_cong(xprt, task)) {
-		struct rpc_rqst *req = task->tk_rqstp;
 		xprt->snd_task = task;
-		if (req) {
-			req->rq_bytes_sent = 0;
-			req->rq_ntrans++;
-		}
+		req->rq_bytes_sent = 0;
+		req->rq_ntrans++;
 		return;
 	}
 out_unlock:
@@ -852,7 +861,7 @@
 		err = req->rq_reply_bytes_recvd;
 		goto out_unlock;
 	}
-	if (!xprt->ops->reserve_xprt(task))
+	if (!xprt->ops->reserve_xprt(xprt, task))
 		err = -EAGAIN;
 out_unlock:
 	spin_unlock_bh(&xprt->transport_lock);
@@ -928,28 +937,66 @@
 	spin_unlock_bh(&xprt->transport_lock);
 }
 
+static struct rpc_rqst *xprt_dynamic_alloc_slot(struct rpc_xprt *xprt, gfp_t gfp_flags)
+{
+	struct rpc_rqst *req = ERR_PTR(-EAGAIN);
+
+	if (!atomic_add_unless(&xprt->num_reqs, 1, xprt->max_reqs))
+		goto out;
+	req = kzalloc(sizeof(struct rpc_rqst), gfp_flags);
+	if (req != NULL)
+		goto out;
+	atomic_dec(&xprt->num_reqs);
+	req = ERR_PTR(-ENOMEM);
+out:
+	return req;
+}
+
+static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
+{
+	if (atomic_add_unless(&xprt->num_reqs, -1, xprt->min_reqs)) {
+		kfree(req);
+		return true;
+	}
+	return false;
+}
+
 static void xprt_alloc_slot(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct rpc_rqst *req;
 
-	task->tk_status = 0;
-	if (task->tk_rqstp)
-		return;
 	if (!list_empty(&xprt->free)) {
-		struct rpc_rqst	*req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
-		list_del_init(&req->rq_list);
-		task->tk_rqstp = req;
-		xprt_request_init(task, xprt);
-		return;
+		req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
+		list_del(&req->rq_list);
+		goto out_init_req;
 	}
-	dprintk("RPC:       waiting for request slot\n");
+	req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT);
+	if (!IS_ERR(req))
+		goto out_init_req;
+	switch (PTR_ERR(req)) {
+	case -ENOMEM:
+		rpc_delay(task, HZ >> 2);
+		dprintk("RPC:       dynamic allocation of request slot "
+				"failed! Retrying\n");
+		break;
+	case -EAGAIN:
+		rpc_sleep_on(&xprt->backlog, task, NULL);
+		dprintk("RPC:       waiting for request slot\n");
+	}
 	task->tk_status = -EAGAIN;
-	task->tk_timeout = 0;
-	rpc_sleep_on(&xprt->backlog, task, NULL);
+	return;
+out_init_req:
+	task->tk_status = 0;
+	task->tk_rqstp = req;
+	xprt_request_init(task, xprt);
 }
 
 static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
 {
+	if (xprt_dynamic_free_slot(xprt, req))
+		return;
+
 	memset(req, 0, sizeof(*req));	/* mark unused */
 
 	spin_lock(&xprt->reserve_lock);
@@ -958,25 +1005,49 @@
 	spin_unlock(&xprt->reserve_lock);
 }
 
-struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)
+static void xprt_free_all_slots(struct rpc_xprt *xprt)
+{
+	struct rpc_rqst *req;
+	while (!list_empty(&xprt->free)) {
+		req = list_first_entry(&xprt->free, struct rpc_rqst, rq_list);
+		list_del(&req->rq_list);
+		kfree(req);
+	}
+}
+
+struct rpc_xprt *xprt_alloc(struct net *net, size_t size,
+		unsigned int num_prealloc,
+		unsigned int max_alloc)
 {
 	struct rpc_xprt *xprt;
+	struct rpc_rqst *req;
+	int i;
 
 	xprt = kzalloc(size, GFP_KERNEL);
 	if (xprt == NULL)
 		goto out;
-	atomic_set(&xprt->count, 1);
 
-	xprt->max_reqs = max_req;
-	xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
-	if (xprt->slot == NULL)
+	xprt_init(xprt, net);
+
+	for (i = 0; i < num_prealloc; i++) {
+		req = kzalloc(sizeof(struct rpc_rqst), GFP_KERNEL);
+		if (!req)
+			break;
+		list_add(&req->rq_list, &xprt->free);
+	}
+	if (i < num_prealloc)
 		goto out_free;
+	if (max_alloc > num_prealloc)
+		xprt->max_reqs = max_alloc;
+	else
+		xprt->max_reqs = num_prealloc;
+	xprt->min_reqs = num_prealloc;
+	atomic_set(&xprt->num_reqs, num_prealloc);
 
-	xprt->xprt_net = get_net(net);
 	return xprt;
 
 out_free:
-	kfree(xprt);
+	xprt_free(xprt);
 out:
 	return NULL;
 }
@@ -985,7 +1056,7 @@
 void xprt_free(struct rpc_xprt *xprt)
 {
 	put_net(xprt->xprt_net);
-	kfree(xprt->slot);
+	xprt_free_all_slots(xprt);
 	kfree(xprt);
 }
 EXPORT_SYMBOL_GPL(xprt_free);
@@ -1001,10 +1072,24 @@
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 
-	task->tk_status = -EIO;
+	task->tk_status = 0;
+	if (task->tk_rqstp != NULL)
+		return;
+
+	/* Note: grabbing the xprt_lock_write() here is not strictly needed,
+	 * but ensures that we throttle new slot allocation if the transport
+	 * is congested (e.g. if reconnecting or if we're out of socket
+	 * write buffer space).
+	 */
+	task->tk_timeout = 0;
+	task->tk_status = -EAGAIN;
+	if (!xprt_lock_write(xprt, task))
+		return;
+
 	spin_lock(&xprt->reserve_lock);
 	xprt_alloc_slot(task);
 	spin_unlock(&xprt->reserve_lock);
+	xprt_release_write(xprt, task);
 }
 
 static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
@@ -1021,6 +1106,7 @@
 {
 	struct rpc_rqst	*req = task->tk_rqstp;
 
+	INIT_LIST_HEAD(&req->rq_list);
 	req->rq_timeout = task->tk_client->cl_timeout->to_initval;
 	req->rq_task	= task;
 	req->rq_xprt    = xprt;
@@ -1073,6 +1159,34 @@
 		xprt_free_bc_request(req);
 }
 
+static void xprt_init(struct rpc_xprt *xprt, struct net *net)
+{
+	atomic_set(&xprt->count, 1);
+
+	spin_lock_init(&xprt->transport_lock);
+	spin_lock_init(&xprt->reserve_lock);
+
+	INIT_LIST_HEAD(&xprt->free);
+	INIT_LIST_HEAD(&xprt->recv);
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
+	spin_lock_init(&xprt->bc_pa_lock);
+	INIT_LIST_HEAD(&xprt->bc_pa_list);
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
+
+	xprt->last_used = jiffies;
+	xprt->cwnd = RPC_INITCWND;
+	xprt->bind_index = 0;
+
+	rpc_init_wait_queue(&xprt->binding, "xprt_binding");
+	rpc_init_wait_queue(&xprt->pending, "xprt_pending");
+	rpc_init_priority_wait_queue(&xprt->sending, "xprt_sending");
+	rpc_init_priority_wait_queue(&xprt->backlog, "xprt_backlog");
+
+	xprt_init_xid(xprt);
+
+	xprt->xprt_net = get_net(net);
+}
+
 /**
  * xprt_create_transport - create an RPC transport
  * @args: rpc transport creation arguments
@@ -1081,7 +1195,6 @@
 struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
 {
 	struct rpc_xprt	*xprt;
-	struct rpc_rqst	*req;
 	struct xprt_class *t;
 
 	spin_lock(&xprt_list_lock);
@@ -1100,46 +1213,17 @@
 	if (IS_ERR(xprt)) {
 		dprintk("RPC:       xprt_create_transport: failed, %ld\n",
 				-PTR_ERR(xprt));
-		return xprt;
+		goto out;
 	}
-	if (test_and_set_bit(XPRT_INITIALIZED, &xprt->state))
-		/* ->setup returned a pre-initialized xprt: */
-		return xprt;
-
-	spin_lock_init(&xprt->transport_lock);
-	spin_lock_init(&xprt->reserve_lock);
-
-	INIT_LIST_HEAD(&xprt->free);
-	INIT_LIST_HEAD(&xprt->recv);
-#if defined(CONFIG_NFS_V4_1)
-	spin_lock_init(&xprt->bc_pa_lock);
-	INIT_LIST_HEAD(&xprt->bc_pa_list);
-#endif /* CONFIG_NFS_V4_1 */
-
 	INIT_WORK(&xprt->task_cleanup, xprt_autoclose);
 	if (xprt_has_timer(xprt))
 		setup_timer(&xprt->timer, xprt_init_autodisconnect,
 			    (unsigned long)xprt);
 	else
 		init_timer(&xprt->timer);
-	xprt->last_used = jiffies;
-	xprt->cwnd = RPC_INITCWND;
-	xprt->bind_index = 0;
-
-	rpc_init_wait_queue(&xprt->binding, "xprt_binding");
-	rpc_init_wait_queue(&xprt->pending, "xprt_pending");
-	rpc_init_wait_queue(&xprt->sending, "xprt_sending");
-	rpc_init_wait_queue(&xprt->resend, "xprt_resend");
-	rpc_init_priority_wait_queue(&xprt->backlog, "xprt_backlog");
-
-	/* initialize free list */
-	for (req = &xprt->slot[xprt->max_reqs-1]; req >= &xprt->slot[0]; req--)
-		list_add(&req->rq_list, &xprt->free);
-
-	xprt_init_xid(xprt);
-
 	dprintk("RPC:       created transport %p with %u slots\n", xprt,
 			xprt->max_reqs);
+out:
 	return xprt;
 }
 
@@ -1157,7 +1241,6 @@
 	rpc_destroy_wait_queue(&xprt->binding);
 	rpc_destroy_wait_queue(&xprt->pending);
 	rpc_destroy_wait_queue(&xprt->sending);
-	rpc_destroy_wait_queue(&xprt->resend);
 	rpc_destroy_wait_queue(&xprt->backlog);
 	cancel_work_sync(&xprt->task_cleanup);
 	/*
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 0867070..b446e10 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -283,6 +283,7 @@
 	}
 
 	xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt),
+			xprt_rdma_slot_table_entries,
 			xprt_rdma_slot_table_entries);
 	if (xprt == NULL) {
 		dprintk("RPC:       %s: couldn't allocate rpcrdma_xprt\n",
@@ -452,9 +453,8 @@
 }
 
 static int
-xprt_rdma_reserve_xprt(struct rpc_task *task)
+xprt_rdma_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
 {
-	struct rpc_xprt *xprt = task->tk_xprt;
 	struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
 	int credits = atomic_read(&r_xprt->rx_buf.rb_credits);
 
@@ -466,7 +466,7 @@
 		BUG_ON(r_xprt->rx_buf.rb_cwndscale <= 0);
 	}
 	xprt->cwnd = credits * r_xprt->rx_buf.rb_cwndscale;
-	return xprt_reserve_xprt_cong(task);
+	return xprt_reserve_xprt_cong(xprt, task);
 }
 
 /*
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index cae761a..08c5d5a 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -42,7 +42,7 @@
 
 #include <linux/wait.h> 		/* wait_queue_head_t, etc */
 #include <linux/spinlock.h> 		/* spinlock_t, etc */
-#include <asm/atomic.h>			/* atomic_t, etc */
+#include <linux/atomic.h>			/* atomic_t, etc */
 
 #include <rdma/rdma_cm.h>		/* RDMA connection api */
 #include <rdma/ib_verbs.h>		/* RDMA verbs api */
@@ -109,7 +109,7 @@
  */
 
 /* temporary static scatter/gather max */
-#define RPCRDMA_MAX_DATA_SEGS	(8)	/* max scatter/gather */
+#define RPCRDMA_MAX_DATA_SEGS	(64)	/* max scatter/gather */
 #define RPCRDMA_MAX_SEGS 	(RPCRDMA_MAX_DATA_SEGS + 2) /* head+tail = 2 */
 #define MAX_RPCRDMAHDR	(\
 	/* max supported RPC/RDMA header */ \
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 72abb73..d7f97ef 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -37,7 +37,7 @@
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/file.h>
-#ifdef CONFIG_NFS_V4_1
+#ifdef CONFIG_SUNRPC_BACKCHANNEL
 #include <linux/sunrpc/bc_xprt.h>
 #endif
 
@@ -54,7 +54,8 @@
  * xprtsock tunables
  */
 unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE;
-unsigned int xprt_tcp_slot_table_entries = RPC_DEF_SLOT_TABLE;
+unsigned int xprt_tcp_slot_table_entries = RPC_MIN_SLOT_TABLE;
+unsigned int xprt_max_tcp_slot_table_entries = RPC_MAX_SLOT_TABLE;
 
 unsigned int xprt_min_resvport = RPC_DEF_MIN_RESVPORT;
 unsigned int xprt_max_resvport = RPC_DEF_MAX_RESVPORT;
@@ -75,6 +76,7 @@
 
 static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
 static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
+static unsigned int max_tcp_slot_table_limit = RPC_MAX_SLOT_TABLE_LIMIT;
 static unsigned int xprt_min_resvport_limit = RPC_MIN_RESVPORT;
 static unsigned int xprt_max_resvport_limit = RPC_MAX_RESVPORT;
 
@@ -104,6 +106,15 @@
 		.extra2		= &max_slot_table_size
 	},
 	{
+		.procname	= "tcp_max_slot_table_entries",
+		.data		= &xprt_max_tcp_slot_table_entries,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &min_slot_table_size,
+		.extra2		= &max_tcp_slot_table_limit
+	},
+	{
 		.procname	= "min_resvport",
 		.data		= &xprt_min_resvport,
 		.maxlen		= sizeof(unsigned int),
@@ -755,6 +766,8 @@
 	if (task == NULL)
 		goto out_release;
 	req = task->tk_rqstp;
+	if (req == NULL)
+		goto out_release;
 	if (req->rq_bytes_sent == 0)
 		goto out_release;
 	if (req->rq_bytes_sent == req->rq_snd_buf.len)
@@ -1236,7 +1249,7 @@
 	return 0;
 }
 
-#if defined(CONFIG_NFS_V4_1)
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
 /*
  * Obtains an rpc_rqst previously allocated and invokes the common
  * tcp read code to read the data.  The result is placed in the callback
@@ -1299,7 +1312,7 @@
 {
 	return xs_tcp_read_reply(xprt, desc);
 }
-#endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
 /*
  * Read data off the transport.  This can be either an RPC_CALL or an
@@ -2489,7 +2502,8 @@
 }
 
 static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
-				      unsigned int slot_table_size)
+				      unsigned int slot_table_size,
+				      unsigned int max_slot_table_size)
 {
 	struct rpc_xprt *xprt;
 	struct sock_xprt *new;
@@ -2499,7 +2513,8 @@
 		return ERR_PTR(-EBADF);
 	}
 
-	xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size);
+	xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size,
+			max_slot_table_size);
 	if (xprt == NULL) {
 		dprintk("RPC:       xs_setup_xprt: couldn't allocate "
 				"rpc_xprt\n");
@@ -2541,7 +2556,8 @@
 	struct rpc_xprt *xprt;
 	struct rpc_xprt *ret;
 
-	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
+	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
+			xprt_max_tcp_slot_table_entries);
 	if (IS_ERR(xprt))
 		return xprt;
 	transport = container_of(xprt, struct sock_xprt, xprt);
@@ -2605,7 +2621,8 @@
 	struct sock_xprt *transport;
 	struct rpc_xprt *ret;
 
-	xprt = xs_setup_xprt(args, xprt_udp_slot_table_entries);
+	xprt = xs_setup_xprt(args, xprt_udp_slot_table_entries,
+			xprt_udp_slot_table_entries);
 	if (IS_ERR(xprt))
 		return xprt;
 	transport = container_of(xprt, struct sock_xprt, xprt);
@@ -2681,7 +2698,8 @@
 	struct sock_xprt *transport;
 	struct rpc_xprt *ret;
 
-	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
+	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
+			xprt_max_tcp_slot_table_entries);
 	if (IS_ERR(xprt))
 		return xprt;
 	transport = container_of(xprt, struct sock_xprt, xprt);
@@ -2760,7 +2778,8 @@
 		 */
 		 return args->bc_xprt->xpt_bc_xprt;
 	}
-	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
+	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
+			xprt_tcp_slot_table_entries);
 	if (IS_ERR(xprt))
 		return xprt;
 	transport = container_of(xprt, struct sock_xprt, xprt);
@@ -2947,8 +2966,26 @@
 #define param_check_slot_table_size(name, p) \
 	__param_check(name, p, unsigned int);
 
+static int param_set_max_slot_table_size(const char *val,
+				     const struct kernel_param *kp)
+{
+	return param_set_uint_minmax(val, kp,
+			RPC_MIN_SLOT_TABLE,
+			RPC_MAX_SLOT_TABLE_LIMIT);
+}
+
+static struct kernel_param_ops param_ops_max_slot_table_size = {
+	.set = param_set_max_slot_table_size,
+	.get = param_get_uint,
+};
+
+#define param_check_max_slot_table_size(name, p) \
+	__param_check(name, p, unsigned int);
+
 module_param_named(tcp_slot_table_entries, xprt_tcp_slot_table_entries,
 		   slot_table_size, 0644);
+module_param_named(tcp_max_slot_table_entries, xprt_max_tcp_slot_table_entries,
+		   max_slot_table_size, 0644);
 module_param_named(udp_slot_table_entries, xprt_udp_slot_table_entries,
 		   slot_table_size, 0644);
 
diff --git a/net/tipc/core.h b/net/tipc/core.h
index d234a98..2761af3 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -47,7 +47,7 @@
 #include <linux/string.h>
 #include <asm/uaccess.h>
 #include <linux/interrupt.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <asm/hardirq.h>
 #include <linux/netdevice.h>
 #include <linux/in.h>
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 1ad0f39..02751db 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -903,7 +903,7 @@
 	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
 	    !is_world_regdom(last_request->alpha2)) {
 		REG_DBG_PRINT("Ignoring regulatory request %s "
-			      "since the driver requires its own regulaotry "
+			      "since the driver requires its own regulatory "
 			      "domain to be set first",
 			      reg_initiator_name(initiator));
 		return true;
@@ -1125,12 +1125,13 @@
 	enum ieee80211_band band;
 
 	if (ignore_reg_update(wiphy, initiator))
-		goto out;
+		return;
+
 	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 		if (wiphy->bands[band])
 			handle_band(wiphy, band, initiator);
 	}
-out:
+
 	reg_process_beacons(wiphy);
 	reg_process_ht_flags(wiphy);
 	if (wiphy->reg_notifier)
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 0b4276c..82d2eb2 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -170,8 +170,8 @@
 nconf-objs     := nconf.o zconf.tab.o nconf.gui.o
 kxgettext-objs	:= kxgettext.o zconf.tab.o
 qconf-cxxobjs	:= qconf.o
-qconf-objs	:= kconfig_load.o zconf.tab.o
-gconf-objs	:= gconf.o kconfig_load.o zconf.tab.o
+qconf-objs	:= zconf.tab.o
+gconf-objs	:= gconf.o zconf.tab.o
 
 hostprogs-y := conf
 
@@ -203,7 +203,7 @@
 	hostprogs-y += gconf
 endif
 
-clean-files	:= lkc_defs.h qconf.moc .tmp_qtcheck .tmp_gtkcheck
+clean-files	:= qconf.moc .tmp_qtcheck .tmp_gtkcheck
 clean-files	+= zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h
 clean-files     += mconf qconf gconf nconf
 clean-files     += config.pot linux.pot
@@ -226,12 +226,12 @@
 LEX_PREFIX_zconf	:= zconf
 YACC_PREFIX_zconf	:= zconf
 
-HOSTLOADLIBES_qconf	= $(KC_QT_LIBS) -ldl
-HOSTCXXFLAGS_qconf.o	= $(KC_QT_CFLAGS) -D LKC_DIRECT_LINK
+HOSTLOADLIBES_qconf	= $(KC_QT_LIBS)
+HOSTCXXFLAGS_qconf.o	= $(KC_QT_CFLAGS)
 
-HOSTLOADLIBES_gconf	= `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` -ldl
+HOSTLOADLIBES_gconf	= `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0`
 HOSTCFLAGS_gconf.o	= `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \
-                          -D LKC_DIRECT_LINK
+                          -Wno-missing-prototypes
 
 HOSTLOADLIBES_mconf   = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC))
 
@@ -321,18 +321,11 @@
 
 $(obj)/zconf.tab.o: $(obj)/zconf.lex.c $(obj)/zconf.hash.c
 
-$(obj)/kconfig_load.o: $(obj)/lkc_defs.h
-
-$(obj)/qconf.o: $(obj)/qconf.moc $(obj)/lkc_defs.h
-
-$(obj)/gconf.o: $(obj)/lkc_defs.h
+$(obj)/qconf.o: $(obj)/qconf.moc
 
 $(obj)/%.moc: $(src)/%.h
 	$(KC_QT_MOC) -i $< -o $@
 
-$(obj)/lkc_defs.h: $(src)/lkc_proto.h
-	$(Q)sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/'
-
 # Extract gconf menu items for I18N support
 $(obj)/gconf.glade.h: $(obj)/gconf.glade
 	$(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 006ad81..f208f90 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -14,11 +14,11 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 static void conf(struct menu *menu);
 static void check_conf(struct menu *menu);
+static void xfgets(char *str, int size, FILE *in);
 
 enum input_mode {
 	oldaskconfig,
@@ -35,8 +35,6 @@
 	oldnoconfig,
 } input_mode = oldaskconfig;
 
-char *defconfig_file;
-
 static int indent = 1;
 static int valid_stdin = 1;
 static int sync_kconfig;
@@ -106,6 +104,7 @@
 			return 0;
 		}
 		check_stdin();
+		/* fall through */
 	case oldaskconfig:
 		fflush(stdout);
 		xfgets(line, 128, stdin);
@@ -150,6 +149,7 @@
 				def = NULL;
 				break;
 			}
+			/* fall through */
 		default:
 			line[strlen(line)-1] = 0;
 			def = line;
@@ -304,6 +304,7 @@
 				break;
 			}
 			check_stdin();
+			/* fall through */
 		case oldaskconfig:
 			fflush(stdout);
 			xfgets(line, 128, stdin);
@@ -369,6 +370,7 @@
 				check_conf(menu);
 				return;
 			}
+			/* fall through */
 		case P_COMMENT:
 			prompt = menu_get_prompt(menu);
 			if (prompt)
@@ -456,10 +458,30 @@
 	{NULL, 0, NULL, 0}
 };
 
+static void conf_usage(const char *progname)
+{
+
+	printf("Usage: %s [option] <kconfig-file>\n", progname);
+	printf("[option] is _one_ of the following:\n");
+	printf("  --listnewconfig         List new options\n");
+	printf("  --oldaskconfig          Start a new configuration using a line-oriented program\n");
+	printf("  --oldconfig             Update a configuration using a provided .config as base\n");
+	printf("  --silentoldconfig       Same as oldconfig, but quietly, additionally update deps\n");
+	printf("  --oldnoconfig           Same as silentoldconfig but set new symbols to no\n");
+	printf("  --defconfig <file>      New config with default defined in <file>\n");
+	printf("  --savedefconfig <file>  Save the minimal current configuration to <file>\n");
+	printf("  --allnoconfig           New config where all options are answered with no\n");
+	printf("  --allyesconfig          New config where all options are answered with yes\n");
+	printf("  --allmodconfig          New config where all options are answered with mod\n");
+	printf("  --alldefconfig          New config with all symbols set to default\n");
+	printf("  --randconfig            New config with random answer to all options\n");
+}
+
 int main(int ac, char **av)
 {
+	const char *progname = av[0];
 	int opt;
-	const char *name;
+	const char *name, *defconfig_file = NULL /* gcc uninit */;
 	struct stat tmpstat;
 
 	setlocale(LC_ALL, "");
@@ -491,14 +513,24 @@
 			srand(seed);
 			break;
 		}
+		case oldaskconfig:
+		case oldconfig:
+		case allnoconfig:
+		case allyesconfig:
+		case allmodconfig:
+		case alldefconfig:
+		case listnewconfig:
+		case oldnoconfig:
+			break;
 		case '?':
-			fprintf(stderr, _("See README for usage info\n"));
+			conf_usage(progname);
 			exit(1);
 			break;
 		}
 	}
 	if (ac == optind) {
 		printf(_("%s: Kconfig file missing\n"), av[0]);
+		conf_usage(progname);
 		exit(1);
 	}
 	name = av[optind];
@@ -641,13 +673,11 @@
 	}
 	return 0;
 }
+
 /*
  * Helper function to facilitate fgets() by Jean Sacren.
  */
-void xfgets(str, size, in)
-	char *str;
-	int size;
-	FILE *in;
+void xfgets(char *str, int size, FILE *in)
 {
 	if (fgets(str, size, in) == NULL)
 		fprintf(stderr, "\nError in reading or end of file.\n");
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 2bafd9a..59b667c 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -7,13 +7,13 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 static void conf_warning(const char *fmt, ...)
@@ -128,6 +128,7 @@
 			sym->flags |= def_flags;
 			break;
 		}
+		/* fall through */
 	case S_BOOLEAN:
 		if (p[0] == 'y') {
 			sym->def[def].tri = yes;
@@ -140,7 +141,7 @@
 			break;
 		}
 		conf_warning("symbol value '%s' invalid for %s", p, sym->name);
-		break;
+		return 1;
 	case S_OTHER:
 		if (*p != '"') {
 			for (p2 = p; *p2 && !isspace(*p2); p2++)
@@ -148,6 +149,7 @@
 			sym->type = S_STRING;
 			goto done;
 		}
+		/* fall through */
 	case S_STRING:
 		if (*p++ != '"')
 			break;
@@ -162,6 +164,7 @@
 			conf_warning("invalid string found");
 			return 1;
 		}
+		/* fall through */
 	case S_INT:
 	case S_HEX:
 	done:
@@ -237,6 +240,7 @@
 		case S_STRING:
 			if (sym->def[def].val)
 				free(sym->def[def].val);
+			/* fall through */
 		default:
 			sym->def[def].val = NULL;
 			sym->def[def].tri = no;
@@ -363,6 +367,7 @@
 					break;
 				if (!sym_is_choice(sym))
 					goto sym_ok;
+				/* fall through */
 			default:
 				if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
 					goto sym_ok;
@@ -417,64 +422,202 @@
 	return 0;
 }
 
-/* Write a S_STRING */
-static void conf_write_string(bool headerfile, const char *name,
-                              const char *str, FILE *out)
+/*
+ * Kconfig configuration printer
+ *
+ * This printer is used when generating the resulting configuration after
+ * kconfig invocation and `defconfig' files. Unset symbol might be omitted by
+ * passing a non-NULL argument to the printer.
+ *
+ */
+static void
+kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
 {
-	int l;
-	if (headerfile)
-		fprintf(out, "#define %s%s \"", CONFIG_, name);
-	else
-		fprintf(out, "%s%s=\"", CONFIG_, name);
-
-	while (1) {
-		l = strcspn(str, "\"\\");
-		if (l) {
-			xfwrite(str, l, 1, out);
-			str += l;
-		}
-		if (!*str)
-			break;
-		fprintf(out, "\\%c", *str++);
-	}
-	fputs("\"\n", out);
-}
-
-static void conf_write_symbol(struct symbol *sym, FILE *out, bool write_no)
-{
-	const char *str;
 
 	switch (sym->type) {
 	case S_BOOLEAN:
 	case S_TRISTATE:
-		switch (sym_get_tristate_value(sym)) {
-		case no:
-			if (write_no)
-				fprintf(out, "# %s%s is not set\n",
+		if (*value == 'n') {
+			bool skip_unset = (arg != NULL);
+
+			if (!skip_unset)
+				fprintf(fp, "# %s%s is not set\n",
 				    CONFIG_, sym->name);
-			break;
-		case mod:
-			fprintf(out, "%s%s=m\n", CONFIG_, sym->name);
-			break;
-		case yes:
-			fprintf(out, "%s%s=y\n", CONFIG_, sym->name);
-			break;
+			return;
 		}
 		break;
+	default:
+		break;
+	}
+
+	fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value);
+}
+
+static void
+kconfig_print_comment(FILE *fp, const char *value, void *arg)
+{
+	const char *p = value;
+	size_t l;
+
+	for (;;) {
+		l = strcspn(p, "\n");
+		fprintf(fp, "#");
+		if (l) {
+			fprintf(fp, " ");
+			fwrite(p, l, 1, fp);
+			p += l;
+		}
+		fprintf(fp, "\n");
+		if (*p++ == '\0')
+			break;
+	}
+}
+
+static struct conf_printer kconfig_printer_cb =
+{
+	.print_symbol = kconfig_print_symbol,
+	.print_comment = kconfig_print_comment,
+};
+
+/*
+ * Header printer
+ *
+ * This printer is used when generating the `include/generated/autoconf.h' file.
+ */
+static void
+header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE: {
+		const char *suffix = "";
+
+		switch (*value) {
+		case 'n':
+			break;
+		case 'm':
+			suffix = "_MODULE";
+			/* fall through */
+		default:
+			fprintf(fp, "#define %s%s%s 1\n",
+			    CONFIG_, sym->name, suffix);
+		}
+		/*
+		 * Generate the __enabled_CONFIG_* and
+		 * __enabled_CONFIG_*_MODULE macros for use by the
+		 * IS_{ENABLED,BUILTIN,MODULE} macros. The _MODULE variant is
+		 * generated even for booleans so that the IS_ENABLED() macro
+		 * works.
+		 */
+		fprintf(fp, "#define __enabled_" CONFIG_ "%s %d\n",
+				sym->name, (*value == 'y'));
+		fprintf(fp, "#define __enabled_" CONFIG_ "%s_MODULE %d\n",
+				sym->name, (*value == 'm'));
+		break;
+	}
+	case S_HEX: {
+		const char *prefix = "";
+
+		if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X'))
+			prefix = "0x";
+		fprintf(fp, "#define %s%s %s%s\n",
+		    CONFIG_, sym->name, prefix, value);
+		break;
+	}
 	case S_STRING:
-		conf_write_string(false, sym->name, sym_get_string_value(sym), out);
-		break;
-	case S_HEX:
 	case S_INT:
-		str = sym_get_string_value(sym);
-		fprintf(out, "%s%s=%s\n", CONFIG_, sym->name, str);
+		fprintf(fp, "#define %s%s %s\n",
+		    CONFIG_, sym->name, value);
 		break;
+	default:
+		break;
+	}
+
+}
+
+static void
+header_print_comment(FILE *fp, const char *value, void *arg)
+{
+	const char *p = value;
+	size_t l;
+
+	fprintf(fp, "/*\n");
+	for (;;) {
+		l = strcspn(p, "\n");
+		fprintf(fp, " *");
+		if (l) {
+			fprintf(fp, " ");
+			fwrite(p, l, 1, fp);
+			p += l;
+		}
+		fprintf(fp, "\n");
+		if (*p++ == '\0')
+			break;
+	}
+	fprintf(fp, " */\n");
+}
+
+static struct conf_printer header_printer_cb =
+{
+	.print_symbol = header_print_symbol,
+	.print_comment = header_print_comment,
+};
+
+/*
+ * Tristate printer
+ *
+ * This printer is used when generating the `include/config/tristate.conf' file.
+ */
+static void
+tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+	if (sym->type == S_TRISTATE && *value != 'n')
+		fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value));
+}
+
+static struct conf_printer tristate_printer_cb =
+{
+	.print_symbol = tristate_print_symbol,
+	.print_comment = kconfig_print_comment,
+};
+
+static void conf_write_symbol(FILE *fp, struct symbol *sym,
+			      struct conf_printer *printer, void *printer_arg)
+{
+	const char *str;
+
+	switch (sym->type) {
 	case S_OTHER:
 	case S_UNKNOWN:
 		break;
+	case S_STRING:
+		str = sym_get_string_value(sym);
+		str = sym_escape_string_value(str);
+		printer->print_symbol(fp, sym, str, printer_arg);
+		free((void *)str);
+		break;
+	default:
+		str = sym_get_string_value(sym);
+		printer->print_symbol(fp, sym, str, printer_arg);
 	}
 }
 
+static void
+conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg)
+{
+	char buf[256];
+
+	snprintf(buf, sizeof(buf),
+	    "\n"
+	    "Automatically generated file; DO NOT EDIT.\n"
+	    "%s\n",
+	    rootmenu.prompt->text);
+
+	printer->print_comment(fp, buf, printer_arg);
+}
+
 /*
  * Write out a minimal config.
  * All values that has default values are skipped as this is redundant.
@@ -531,7 +674,7 @@
 						goto next_menu;
 				}
 			}
-			conf_write_symbol(sym, out, true);
+			conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
 		}
 next_menu:
 		if (menu->list != NULL) {
@@ -596,11 +739,7 @@
 	if (!out)
 		return 1;
 
-	fprintf(out, _("#\n"
-		       "# Automatically generated make config: don't edit\n"
-		       "# %s\n"
-		       "#\n"),
-		     rootmenu.prompt->text);
+	conf_write_heading(out, &kconfig_printer_cb, NULL);
 
 	if (!conf_get_changed())
 		sym_clear_all_valid();
@@ -621,8 +760,8 @@
 			if (!(sym->flags & SYMBOL_WRITE))
 				goto next;
 			sym->flags &= ~SYMBOL_WRITE;
-			/* Write config symbol to file */
-			conf_write_symbol(sym, out, true);
+
+			conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
 		}
 
 next:
@@ -771,7 +910,6 @@
 int conf_write_autoconf(void)
 {
 	struct symbol *sym;
-	const char *str;
 	const char *name;
 	FILE *out, *tristate, *out_h;
 	int i;
@@ -800,68 +938,23 @@
 		return 1;
 	}
 
-	fprintf(out, "#\n"
-		     "# Automatically generated make config: don't edit\n"
-		     "# %s\n"
-		     "#\n",
-		     rootmenu.prompt->text);
-	fprintf(tristate, "#\n"
-			  "# Automatically generated - do not edit\n"
-			  "\n");
-	fprintf(out_h, "/*\n"
-		       " * Automatically generated C config: don't edit\n"
-		       " * %s\n"
-		       " */\n",
-		       rootmenu.prompt->text);
+	conf_write_heading(out, &kconfig_printer_cb, NULL);
+
+	conf_write_heading(tristate, &tristate_printer_cb, NULL);
+
+	conf_write_heading(out_h, &header_printer_cb, NULL);
 
 	for_all_symbols(i, sym) {
 		sym_calc_value(sym);
 		if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
 			continue;
 
-		/* write symbol to config file */
-		conf_write_symbol(sym, out, false);
+		/* write symbol to auto.conf, tristate and header files */
+		conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
 
-		/* update autoconf and tristate files */
-		switch (sym->type) {
-		case S_BOOLEAN:
-		case S_TRISTATE:
-			switch (sym_get_tristate_value(sym)) {
-			case no:
-				break;
-			case mod:
-				fprintf(tristate, "%s%s=M\n",
-				    CONFIG_, sym->name);
-				fprintf(out_h, "#define %s%s_MODULE 1\n",
-				    CONFIG_, sym->name);
-				break;
-			case yes:
-				if (sym->type == S_TRISTATE)
-					fprintf(tristate,"%s%s=Y\n",
-					    CONFIG_, sym->name);
-				fprintf(out_h, "#define %s%s 1\n",
-				    CONFIG_, sym->name);
-				break;
-			}
-			break;
-		case S_STRING:
-			conf_write_string(true, sym->name, sym_get_string_value(sym), out_h);
-			break;
-		case S_HEX:
-			str = sym_get_string_value(sym);
-			if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
-				fprintf(out_h, "#define %s%s 0x%s\n",
-				    CONFIG_, sym->name, str);
-				break;
-			}
-		case S_INT:
-			str = sym_get_string_value(sym);
-			fprintf(out_h, "#define %s%s %s\n",
-			    CONFIG_, sym->name, str);
-			break;
-		default:
-			break;
-		}
+		conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1);
+
+		conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
 	}
 	fclose(out);
 	fclose(tristate);
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 0010034..290ce41 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -7,15 +7,13 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 #define DEBUG_EXPR	0
 
 struct expr *expr_alloc_symbol(struct symbol *sym)
 {
-	struct expr *e = malloc(sizeof(*e));
-	memset(e, 0, sizeof(*e));
+	struct expr *e = calloc(1, sizeof(*e));
 	e->type = E_SYMBOL;
 	e->left.sym = sym;
 	return e;
@@ -23,8 +21,7 @@
 
 struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
 {
-	struct expr *e = malloc(sizeof(*e));
-	memset(e, 0, sizeof(*e));
+	struct expr *e = calloc(1, sizeof(*e));
 	e->type = type;
 	e->left.expr = ce;
 	return e;
@@ -32,8 +29,7 @@
 
 struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
 {
-	struct expr *e = malloc(sizeof(*e));
-	memset(e, 0, sizeof(*e));
+	struct expr *e = calloc(1, sizeof(*e));
 	e->type = type;
 	e->left.expr = e1;
 	e->right.expr = e2;
@@ -42,8 +38,7 @@
 
 struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
 {
-	struct expr *e = malloc(sizeof(*e));
-	memset(e, 0, sizeof(*e));
+	struct expr *e = calloc(1, sizeof(*e));
 	e->type = type;
 	e->left.sym = s1;
 	e->right.sym = s2;
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 16bfae2..80fce57 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -172,8 +172,6 @@
 #define MENU_CHANGED		0x0001
 #define MENU_ROOT		0x0002
 
-#ifndef SWIG
-
 extern struct file *file_list;
 extern struct file *current_file;
 struct file *lookup_file(const char *name);
@@ -218,7 +216,6 @@
 {
 	return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
 }
-#endif
 
 #ifdef __cplusplus
 }
diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c
index a11d5f7..9f44380 100644
--- a/scripts/kconfig/gconf.c
+++ b/scripts/kconfig/gconf.c
@@ -285,8 +285,6 @@
 static void renderer_edited(GtkCellRendererText * cell,
 			    const gchar * path_string,
 			    const gchar * new_text, gpointer user_data);
-static void renderer_toggled(GtkCellRendererToggle * cellrenderertoggle,
-			     gchar * arg1, gpointer user_data);
 
 void init_right_tree(void)
 {
@@ -320,8 +318,6 @@
 					    "inconsistent", COL_BTNINC,
 					    "visible", COL_BTNVIS,
 					    "radio", COL_BTNRAD, NULL);
-	/*g_signal_connect(G_OBJECT(renderer), "toggled",
-	   G_CALLBACK(renderer_toggled), NULL); */
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
 					renderer, FALSE);
@@ -888,35 +884,6 @@
 		display_tree_part();	//fixme: keep exp/coll
 }
 
-static void renderer_toggled(GtkCellRendererToggle * cell,
-			     gchar * path_string, gpointer user_data)
-{
-	GtkTreePath *path, *sel_path = NULL;
-	GtkTreeIter iter, sel_iter;
-	GtkTreeSelection *sel;
-	struct menu *menu;
-
-	path = gtk_tree_path_new_from_string(path_string);
-	if (!gtk_tree_model_get_iter(model2, &iter, path))
-		return;
-
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w));
-	if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter))
-		sel_path = gtk_tree_model_get_path(model2, &sel_iter);
-	if (!sel_path)
-		goto out1;
-	if (gtk_tree_path_compare(path, sel_path))
-		goto out2;
-
-	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
-	toggle_sym_value(menu);
-
-      out2:
-	gtk_tree_path_free(sel_path);
-      out1:
-	gtk_tree_path_free(path);
-}
-
 static gint column2index(GtkTreeViewColumn * column)
 {
 	gint i;
@@ -1172,6 +1139,7 @@
 			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
 		if (sym_is_choice(sym))
 			break;
+		/* fall through */
 	case S_TRISTATE:
 		val = sym_get_tristate_value(sym);
 		switch (val) {
@@ -1506,10 +1474,6 @@
 	char *env;
 	gchar *glade_file;
 
-#ifndef LKC_DIRECT_LINK
-	kconfig_load();
-#endif
-
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	bind_textdomain_codeset(PACKAGE, "UTF-8");
 	textdomain(PACKAGE);
diff --git a/scripts/kconfig/kconfig_load.c b/scripts/kconfig/kconfig_load.c
deleted file mode 100644
index dbdcaad..0000000
--- a/scripts/kconfig/kconfig_load.c
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "lkc.h"
-
-#define P(name,type,arg)	type (*name ## _p) arg
-#include "lkc_proto.h"
-#undef P
-
-void kconfig_load(void)
-{
-	void *handle;
-	char *error;
-
-	handle = dlopen("./libkconfig.so", RTLD_LAZY);
-	if (!handle) {
-		handle = dlopen("./scripts/kconfig/libkconfig.so", RTLD_LAZY);
-		if (!handle) {
-			fprintf(stderr, "%s\n", dlerror());
-			exit(1);
-		}
-	}
-
-#define P(name,type,arg)			\
-{						\
-	name ## _p = dlsym(handle, #name);	\
-        if ((error = dlerror()))  {		\
-                fprintf(stderr, "%s\n", error);	\
-		exit(1);			\
-	}					\
-}
-#include "lkc_proto.h"
-#undef P
-}
diff --git a/scripts/kconfig/kxgettext.c b/scripts/kconfig/kxgettext.c
index e9d8e79..2858738 100644
--- a/scripts/kconfig/kxgettext.c
+++ b/scripts/kconfig/kxgettext.c
@@ -7,7 +7,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 static char *escape(const char* text, char *bf, int len)
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index f34a0a9..b633bdb 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -21,12 +21,7 @@
 extern "C" {
 #endif
 
-#ifdef LKC_DIRECT_LINK
 #define P(name,type,arg)	extern type name arg
-#else
-#include "lkc_defs.h"
-#define P(name,type,arg)	extern type (*name ## _p) arg
-#endif
 #include "lkc_proto.h"
 #undef P
 
@@ -79,9 +74,6 @@
 int zconf_lineno(void);
 const char *zconf_curname(void);
 
-/* conf.c */
-void xfgets(char *str, int size, FILE *in);
-
 /* confdata.c */
 const char *conf_get_configname(void);
 const char *conf_get_autoconfig_name(void);
@@ -90,6 +82,11 @@
 void sym_add_change_count(int count);
 void conf_set_all_new_symbols(enum conf_def_mode mode);
 
+struct conf_printer {
+	void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
+	void (*print_comment)(FILE *, const char *, void *);
+};
+
 /* confdata.c and expr.c */
 static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
 {
@@ -97,9 +94,6 @@
 		fprintf(stderr, "\nError in writing or end of file.\n");
 }
 
-/* kconfig_load.c */
-void kconfig_load(void);
-
 /* menu.c */
 void _menu_init(void);
 void menu_warn(struct menu *menu, const char *fmt, ...);
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 17342fe..47fe9c3 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -31,6 +31,7 @@
 P(sym_lookup,struct symbol *,(const char *name, int flags));
 P(sym_find,struct symbol *,(const char *name));
 P(sym_expand_string_value,const char *,(const char *in));
+P(sym_escape_string_value, const char *,(const char *in));
 P(sym_re_search,struct symbol **,(const char *pattern));
 P(sym_type_name,const char *,(enum symbol_type type));
 P(sym_calc_value,void,(struct symbol *sym));
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index d433c7a..820d2b6 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -18,7 +18,6 @@
 #include <unistd.h>
 #include <locale.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 #include "lxdialog/dialog.h"
 
@@ -845,6 +844,7 @@
 				"\n\n"));
 			return 1;
 		}
+		/* fall through */
 	case -1:
 		printf(_("\n\n"
 			"*** End of the configuration.\n"
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 5fdf10d..d660086 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -3,10 +3,11 @@
  * Released under the terms of the GNU GPL v2.0.
  */
 
+#include <ctype.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 static const char nohelp_text[] = N_(
@@ -350,7 +351,7 @@
 			last_menu->next = NULL;
 		}
 
-		sym->dir_dep.expr = parent->dep;
+		sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
 	}
 	for (menu = parent->list; menu; menu = menu->next) {
 		if (sym && sym_is_choice(sym) &&
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 488dd74..39ca1f1 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -7,7 +7,7 @@
  */
 #define _GNU_SOURCE
 #include <string.h>
-#define LKC_DIRECT_LINK
+
 #include "lkc.h"
 #include "nconf.h"
 #include <ctype.h>
@@ -1067,7 +1067,6 @@
 	struct menu *submenu = 0;
 	const char *prompt = menu_get_prompt(menu);
 	struct symbol *sym;
-	struct menu *active_menu = NULL;
 	int res;
 	int current_index = 0;
 	int last_top_row = 0;
@@ -1152,13 +1151,9 @@
 			continue;
 
 		submenu = (struct menu *) item_data();
-		active_menu = (struct menu *)item_data();
 		if (!submenu || !menu_is_visible(submenu))
 			continue;
-		if (submenu)
-			sym = submenu->sym;
-		else
-			sym = NULL;
+		sym = submenu->sym;
 
 		switch (res) {
 		case ' ':
@@ -1222,20 +1217,13 @@
 
 static void show_help(struct menu *menu)
 {
-	struct gstr help = str_new();
+	struct gstr help;
 
-	if (menu && menu->sym && menu_has_help(menu)) {
-		if (menu->sym->name) {
-			str_printf(&help, "%s%s:\n\n", CONFIG_, menu->sym->name);
-			str_append(&help, _(menu_get_help(menu)));
-			str_append(&help, "\n");
-			get_symbol_str(&help, menu->sym);
-		} else {
-			str_append(&help, _(menu_get_help(menu)));
-		}
-	} else {
-		str_append(&help, nohelp_text);
-	}
+	if (!menu)
+		return;
+
+	help = str_new();
+	menu_get_ext_help(menu, &help);
 	show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help));
 	str_free(&help);
 }
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index c2796b8..df274fe 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -1478,10 +1478,13 @@
 	ConfigView::updateListAll();
 }
 
-void ConfigMainWindow::saveConfig(void)
+bool ConfigMainWindow::saveConfig(void)
 {
-	if (conf_write(NULL))
+	if (conf_write(NULL)) {
 		QMessageBox::information(this, "qconf", _("Unable to save configuration!"));
+		return false;
+	}
+	return true;
 }
 
 void ConfigMainWindow::saveConfigAs(void)
@@ -1642,7 +1645,11 @@
 	mb.setButtonText(QMessageBox::Cancel, _("Cancel Exit"));
 	switch (mb.exec()) {
 	case QMessageBox::Yes:
-		saveConfig();
+		if (saveConfig())
+			e->accept();
+		else
+			e->ignore();
+		break;
 	case QMessageBox::No:
 		e->accept();
 		break;
@@ -1745,10 +1752,6 @@
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
 
-#ifndef LKC_DIRECT_LINK
-	kconfig_load();
-#endif
-
 	progname = av[0];
 	configApp = new QApplication(ac, av);
 	if (ac > 1 && av[1][0] == '-') {
diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
index 91677d9..3715b3e 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -311,7 +311,7 @@
 	void listFocusChanged(void);
 	void goBack(void);
 	void loadConfig(void);
-	void saveConfig(void);
+	bool saveConfig(void);
 	void saveConfigAs(void);
 	void searchConfig(void);
 	void showSingleView(void);
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index a796c95..071f00c 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -9,7 +9,6 @@
 #include <regex.h>
 #include <sys/utsname.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 struct symbol symbol_yes = {
@@ -751,7 +750,8 @@
 		case no:
 			return "n";
 		case mod:
-			return "m";
+			sym_calc_value(modules_sym);
+			return (modules_sym->curr.tri == no) ? "n" : "m";
 		case yes:
 			return "y";
 		}
@@ -893,6 +893,49 @@
 	return res;
 }
 
+const char *sym_escape_string_value(const char *in)
+{
+	const char *p;
+	size_t reslen;
+	char *res;
+	size_t l;
+
+	reslen = strlen(in) + strlen("\"\"") + 1;
+
+	p = in;
+	for (;;) {
+		l = strcspn(p, "\"\\");
+		p += l;
+
+		if (p[0] == '\0')
+			break;
+
+		reslen++;
+		p++;
+	}
+
+	res = malloc(reslen);
+	res[0] = '\0';
+
+	strcat(res, "\"");
+
+	p = in;
+	for (;;) {
+		l = strcspn(p, "\"\\");
+		strncat(res, p, l);
+		p += l;
+
+		if (p[0] == '\0')
+			break;
+
+		strcat(res, "\\");
+		strncat(res, p++, 1);
+	}
+
+	strcat(res, "\"");
+	return res;
+}
+
 struct symbol **sym_re_search(const char *pattern)
 {
 	struct symbol *sym, **sym_arr = NULL;
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 6330cc8..d0b8b23 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -5,6 +5,8 @@
  * Released under the terms of the GNU GPL v2.0.
  */
 
+#include <stdarg.h>
+#include <stdlib.h>
 #include <string.h>
 #include "lkc.h"
 
diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/zconf.l
index ddee5fc..00f9d3a 100644
--- a/scripts/kconfig/zconf.l
+++ b/scripts/kconfig/zconf.l
@@ -14,7 +14,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 #define START_STRSIZE	16
diff --git a/scripts/kconfig/zconf.lex.c_shipped b/scripts/kconfig/zconf.lex.c_shipped
index 906c099..c32b1a4 100644
--- a/scripts/kconfig/zconf.lex.c_shipped
+++ b/scripts/kconfig/zconf.lex.c_shipped
@@ -776,7 +776,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 #define START_STRSIZE	16
diff --git a/scripts/kconfig/zconf.tab.c_shipped b/scripts/kconfig/zconf.tab.c_shipped
index 211e1a2..f636141 100644
--- a/scripts/kconfig/zconf.tab.c_shipped
+++ b/scripts/kconfig/zconf.tab.c_shipped
@@ -87,7 +87,6 @@
 #include <string.h>
 #include <stdbool.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y
index c38cc5a..864da07 100644
--- a/scripts/kconfig/zconf.y
+++ b/scripts/kconfig/zconf.y
@@ -11,7 +11,6 @@
 #include <string.h>
 #include <stdbool.h>
 
-#define LKC_DIRECT_LINK
 #include "lkc.h"
 
 #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 7312bf9..c1e18ba 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -73,7 +73,6 @@
 		cred = get_task_cred(tracer);
 		tracerp = aa_cred_profile(cred);
 	}
-	rcu_read_unlock();
 
 	/* not ptraced */
 	if (!tracer || unconfined(tracerp))
@@ -82,6 +81,7 @@
 	error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
 
 out:
+	rcu_read_unlock();
 	if (cred)
 		put_cred(cred);
 
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3d2fd14..3783202 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -127,7 +127,7 @@
 	*inheritable = cred->cap_inheritable;
 	*permitted = cred->cap_permitted;
 
-	if (!unconfined(profile)) {
+	if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
 		*effective = cap_intersect(*effective, profile->caps.allow);
 		*permitted = cap_intersect(*permitted, profile->caps.allow);
 	}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 39d66dc..26b46ff 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -86,7 +86,7 @@
 				  struct inode *inode,
 				  struct file *file)
 {
-	mode_t mode = file->f_mode;
+	fmode_t mode = file->f_mode;
 
 	mutex_lock(&iint->mutex);
 	if (mode & FMODE_WRITE &&
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 1bf090a..b34cc6e 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,7 +14,7 @@
 	user_defined.o
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/ecryptfs_format.c b/security/keys/ecryptfs_format.c
new file mode 100644
index 0000000..6daa3b6
--- /dev/null
+++ b/security/keys/ecryptfs_format.c
@@ -0,0 +1,81 @@
+/*
+ * ecryptfs_format.c: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include "ecryptfs_format.h"
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
+{
+	return auth_tok->token.password.session_key_encryption_key;
+}
+EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
+
+/*
+ * ecryptfs_get_versions()
+ *
+ * Source code taken from the software 'ecryptfs-utils' version 83.
+ *
+ */
+void ecryptfs_get_versions(int *major, int *minor, int *file_version)
+{
+	*major = ECRYPTFS_VERSION_MAJOR;
+	*minor = ECRYPTFS_VERSION_MINOR;
+	if (file_version)
+		*file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
+}
+EXPORT_SYMBOL(ecryptfs_get_versions);
+
+/*
+ * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
+ *
+ * Fill the ecryptfs_auth_tok structure with required ecryptfs data.
+ * The source code is inspired to the original function generate_payload()
+ * shipped with the software 'ecryptfs-utils' version 83.
+ *
+ */
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+			   const char *key_desc)
+{
+	int major, minor;
+
+	ecryptfs_get_versions(&major, &minor, NULL);
+	auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
+			     | ((uint16_t)minor & 0x00FF));
+	auth_tok->token_type = ECRYPTFS_PASSWORD;
+	strncpy((char *)auth_tok->token.password.signature, key_desc,
+		ECRYPTFS_PASSWORD_SIG_SIZE);
+	auth_tok->token.password.session_key_encryption_key_bytes =
+		ECRYPTFS_MAX_KEY_BYTES;
+	/*
+	 * Removed auth_tok->token.password.salt and
+	 * auth_tok->token.password.session_key_encryption_key
+	 * initialization from the original code
+	 */
+	/* TODO: Make the hash parameterizable via policy */
+	auth_tok->token.password.flags |=
+		ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
+	/* The kernel code will encrypt the session key. */
+	auth_tok->session_key.encrypted_key[0] = 0;
+	auth_tok->session_key.encrypted_key_size = 0;
+	/* Default; subject to change by kernel eCryptfs */
+	auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
+	auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
+	return 0;
+}
+EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/ecryptfs_format.h b/security/keys/ecryptfs_format.h
new file mode 100644
index 0000000..40294de
--- /dev/null
+++ b/security/keys/ecryptfs_format.h
@@ -0,0 +1,30 @@
+/*
+ * ecryptfs_format.h: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#ifndef __KEYS_ECRYPTFS_H
+#define __KEYS_ECRYPTFS_H
+
+#include <linux/ecryptfs.h>
+
+#define PGP_DIGEST_ALGO_SHA512   10
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
+void ecryptfs_get_versions(int *major, int *minor, int *file_version);
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+			   const char *key_desc);
+
+#endif /* __KEYS_ECRYPTFS_H */
diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c
index b1cba5b..e7eca9e 100644
--- a/security/keys/encrypted.c
+++ b/security/keys/encrypted.c
@@ -1,8 +1,11 @@
 /*
  * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
  *
- * Author:
+ * Authors:
  * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
  *
  * 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
@@ -26,22 +29,27 @@
 #include <linux/rcupdate.h>
 #include <linux/scatterlist.h>
 #include <linux/crypto.h>
+#include <linux/ctype.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
 #include <crypto/aes.h>
 
 #include "encrypted.h"
+#include "ecryptfs_format.h"
 
 static const char KEY_TRUSTED_PREFIX[] = "trusted:";
 static const char KEY_USER_PREFIX[] = "user:";
 static const char hash_alg[] = "sha256";
 static const char hmac_alg[] = "hmac(sha256)";
 static const char blkcipher_alg[] = "cbc(aes)";
+static const char key_format_default[] = "default";
+static const char key_format_ecryptfs[] = "ecryptfs";
 static unsigned int ivsize;
 static int blksize;
 
 #define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
 #define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define KEY_ECRYPTFS_DESC_LEN 16
 #define HASH_SIZE SHA256_DIGEST_SIZE
 #define MAX_DATA_SIZE 4096
 #define MIN_DATA_SIZE  20
@@ -58,6 +66,16 @@
 	Opt_err = -1, Opt_new, Opt_load, Opt_update
 };
 
+enum {
+	Opt_error = -1, Opt_default, Opt_ecryptfs
+};
+
+static const match_table_t key_format_tokens = {
+	{Opt_default, "default"},
+	{Opt_ecryptfs, "ecryptfs"},
+	{Opt_error, NULL}
+};
+
 static const match_table_t key_tokens = {
 	{Opt_new, "new"},
 	{Opt_load, "load"},
@@ -82,9 +100,37 @@
 }
 
 /*
+ * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key
+ *
+ * The description of a encrypted key with format 'ecryptfs' must contain
+ * exactly 16 hexadecimal characters.
+ *
+ */
+static int valid_ecryptfs_desc(const char *ecryptfs_desc)
+{
+	int i;
+
+	if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) {
+		pr_err("encrypted_key: key description must be %d hexadecimal "
+		       "characters long\n", KEY_ECRYPTFS_DESC_LEN);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) {
+		if (!isxdigit(ecryptfs_desc[i])) {
+			pr_err("encrypted_key: key description must contain "
+			       "only hexadecimal characters\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
  * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
  *
- * key-type:= "trusted:" | "encrypted:"
+ * key-type:= "trusted:" | "user:"
  * desc:= master-key description
  *
  * Verify that 'key-type' is valid and that 'desc' exists. On key update,
@@ -118,8 +164,9 @@
  * datablob_parse - parse the keyctl data
  *
  * datablob format:
- * new <master-key name> <decrypted data length>
- * load <master-key name> <decrypted data length> <encrypted iv + data>
+ * new [<format>] <master-key name> <decrypted data length>
+ * load [<format>] <master-key name> <decrypted data length>
+ *     <encrypted iv + data>
  * update <new-master-key name>
  *
  * Tokenizes a copy of the keyctl data, returning a pointer to each token,
@@ -127,52 +174,95 @@
  *
  * On success returns 0, otherwise -EINVAL.
  */
-static int datablob_parse(char *datablob, char **master_desc,
-			  char **decrypted_datalen, char **hex_encoded_iv)
+static int datablob_parse(char *datablob, const char **format,
+			  char **master_desc, char **decrypted_datalen,
+			  char **hex_encoded_iv)
 {
 	substring_t args[MAX_OPT_ARGS];
 	int ret = -EINVAL;
 	int key_cmd;
-	char *p;
+	int key_format;
+	char *p, *keyword;
 
-	p = strsep(&datablob, " \t");
-	if (!p)
+	keyword = strsep(&datablob, " \t");
+	if (!keyword) {
+		pr_info("encrypted_key: insufficient parameters specified\n");
 		return ret;
-	key_cmd = match_token(p, key_tokens, args);
+	}
+	key_cmd = match_token(keyword, key_tokens, args);
 
-	*master_desc = strsep(&datablob, " \t");
-	if (!*master_desc)
-		goto out;
+	/* Get optional format: default | ecryptfs */
+	p = strsep(&datablob, " \t");
+	if (!p) {
+		pr_err("encrypted_key: insufficient parameters specified\n");
+		return ret;
+	}
 
-	if (valid_master_desc(*master_desc, NULL) < 0)
+	key_format = match_token(p, key_format_tokens, args);
+	switch (key_format) {
+	case Opt_ecryptfs:
+	case Opt_default:
+		*format = p;
+		*master_desc = strsep(&datablob, " \t");
+		break;
+	case Opt_error:
+		*master_desc = p;
+		break;
+	}
+
+	if (!*master_desc) {
+		pr_info("encrypted_key: master key parameter is missing\n");
 		goto out;
+	}
+
+	if (valid_master_desc(*master_desc, NULL) < 0) {
+		pr_info("encrypted_key: master key parameter \'%s\' "
+			"is invalid\n", *master_desc);
+		goto out;
+	}
 
 	if (decrypted_datalen) {
 		*decrypted_datalen = strsep(&datablob, " \t");
-		if (!*decrypted_datalen)
+		if (!*decrypted_datalen) {
+			pr_info("encrypted_key: keylen parameter is missing\n");
 			goto out;
+		}
 	}
 
 	switch (key_cmd) {
 	case Opt_new:
-		if (!decrypted_datalen)
+		if (!decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .update method\n", keyword);
 			break;
+		}
 		ret = 0;
 		break;
 	case Opt_load:
-		if (!decrypted_datalen)
+		if (!decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .update method\n", keyword);
 			break;
+		}
 		*hex_encoded_iv = strsep(&datablob, " \t");
-		if (!*hex_encoded_iv)
+		if (!*hex_encoded_iv) {
+			pr_info("encrypted_key: hex blob is missing\n");
 			break;
+		}
 		ret = 0;
 		break;
 	case Opt_update:
-		if (decrypted_datalen)
+		if (decrypted_datalen) {
+			pr_info("encrypted_key: keyword \'%s\' not allowed "
+				"when called from .instantiate method\n",
+				keyword);
 			break;
+		}
 		ret = 0;
 		break;
 	case Opt_err:
+		pr_info("encrypted_key: keyword \'%s\' not recognized\n",
+			keyword);
 		break;
 	}
 out:
@@ -197,8 +287,8 @@
 	ascii_buf[asciiblob_len] = '\0';
 
 	/* copy datablob master_desc and datalen strings */
-	len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
-		      epayload->datalen);
+	len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
+		      epayload->master_desc, epayload->datalen);
 
 	/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
 	bufp = &ascii_buf[len];
@@ -378,11 +468,13 @@
 	} else
 		goto out;
 
-	if (IS_ERR(mkey))
+	if (IS_ERR(mkey)) {
 		pr_info("encrypted_key: key %s not found",
 			epayload->master_desc);
-	if (mkey)
-		dump_master_key(*master_key, *master_keylen);
+		goto out;
+	}
+
+	dump_master_key(*master_key, *master_keylen);
 out:
 	return mkey;
 }
@@ -439,9 +531,9 @@
 	if (ret < 0)
 		goto out;
 
-	digest = epayload->master_desc + epayload->datablob_len;
+	digest = epayload->format + epayload->datablob_len;
 	ret = calc_hmac(digest, derived_key, sizeof derived_key,
-			epayload->master_desc, epayload->datablob_len);
+			epayload->format, epayload->datablob_len);
 	if (!ret)
 		dump_hmac(NULL, digest, HASH_SIZE);
 out:
@@ -450,26 +542,35 @@
 
 /* verify HMAC before decrypting encrypted key */
 static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
-				const u8 *master_key, size_t master_keylen)
+				const u8 *format, const u8 *master_key,
+				size_t master_keylen)
 {
 	u8 derived_key[HASH_SIZE];
 	u8 digest[HASH_SIZE];
 	int ret;
+	char *p;
+	unsigned short len;
 
 	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
 	if (ret < 0)
 		goto out;
 
-	ret = calc_hmac(digest, derived_key, sizeof derived_key,
-			epayload->master_desc, epayload->datablob_len);
+	len = epayload->datablob_len;
+	if (!format) {
+		p = epayload->master_desc;
+		len -= strlen(epayload->format) + 1;
+	} else
+		p = epayload->format;
+
+	ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
 	if (ret < 0)
 		goto out;
-	ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
+	ret = memcmp(digest, epayload->format + epayload->datablob_len,
 		     sizeof digest);
 	if (ret) {
 		ret = -EINVAL;
 		dump_hmac("datablob",
-			  epayload->master_desc + epayload->datablob_len,
+			  epayload->format + epayload->datablob_len,
 			  HASH_SIZE);
 		dump_hmac("calc", digest, HASH_SIZE);
 	}
@@ -514,13 +615,16 @@
 
 /* Allocate memory for decrypted key and datablob. */
 static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+							 const char *format,
 							 const char *master_desc,
 							 const char *datalen)
 {
 	struct encrypted_key_payload *epayload = NULL;
 	unsigned short datablob_len;
 	unsigned short decrypted_datalen;
+	unsigned short payload_datalen;
 	unsigned int encrypted_datalen;
+	unsigned int format_len;
 	long dlen;
 	int ret;
 
@@ -528,29 +632,43 @@
 	if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
 		return ERR_PTR(-EINVAL);
 
+	format_len = (!format) ? strlen(key_format_default) : strlen(format);
 	decrypted_datalen = dlen;
+	payload_datalen = decrypted_datalen;
+	if (format && !strcmp(format, key_format_ecryptfs)) {
+		if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
+			pr_err("encrypted_key: keylen for the ecryptfs format "
+			       "must be equal to %d bytes\n",
+			       ECRYPTFS_MAX_KEY_BYTES);
+			return ERR_PTR(-EINVAL);
+		}
+		decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
+		payload_datalen = sizeof(struct ecryptfs_auth_tok);
+	}
+
 	encrypted_datalen = roundup(decrypted_datalen, blksize);
 
-	datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
-	    + ivsize + 1 + encrypted_datalen;
+	datablob_len = format_len + 1 + strlen(master_desc) + 1
+	    + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
 
-	ret = key_payload_reserve(key, decrypted_datalen + datablob_len
+	ret = key_payload_reserve(key, payload_datalen + datablob_len
 				  + HASH_SIZE + 1);
 	if (ret < 0)
 		return ERR_PTR(ret);
 
-	epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
+	epayload = kzalloc(sizeof(*epayload) + payload_datalen +
 			   datablob_len + HASH_SIZE + 1, GFP_KERNEL);
 	if (!epayload)
 		return ERR_PTR(-ENOMEM);
 
+	epayload->payload_datalen = payload_datalen;
 	epayload->decrypted_datalen = decrypted_datalen;
 	epayload->datablob_len = datablob_len;
 	return epayload;
 }
 
 static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
-				 const char *hex_encoded_iv)
+				 const char *format, const char *hex_encoded_iv)
 {
 	struct key *mkey;
 	u8 derived_key[HASH_SIZE];
@@ -571,14 +689,14 @@
 	hex2bin(epayload->iv, hex_encoded_iv, ivsize);
 	hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
 
-	hmac = epayload->master_desc + epayload->datablob_len;
+	hmac = epayload->format + epayload->datablob_len;
 	hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
 
 	mkey = request_master_key(epayload, &master_key, &master_keylen);
 	if (IS_ERR(mkey))
 		return PTR_ERR(mkey);
 
-	ret = datablob_hmac_verify(epayload, master_key, master_keylen);
+	ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
 	if (ret < 0) {
 		pr_err("encrypted_key: bad hmac (%d)\n", ret);
 		goto out;
@@ -598,13 +716,28 @@
 }
 
 static void __ekey_init(struct encrypted_key_payload *epayload,
-			const char *master_desc, const char *datalen)
+			const char *format, const char *master_desc,
+			const char *datalen)
 {
-	epayload->master_desc = epayload->decrypted_data
-	    + epayload->decrypted_datalen;
+	unsigned int format_len;
+
+	format_len = (!format) ? strlen(key_format_default) : strlen(format);
+	epayload->format = epayload->payload_data + epayload->payload_datalen;
+	epayload->master_desc = epayload->format + format_len + 1;
 	epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
 	epayload->iv = epayload->datalen + strlen(datalen) + 1;
 	epayload->encrypted_data = epayload->iv + ivsize + 1;
+	epayload->decrypted_data = epayload->payload_data;
+
+	if (!format)
+		memcpy(epayload->format, key_format_default, format_len);
+	else {
+		if (!strcmp(format, key_format_ecryptfs))
+			epayload->decrypted_data =
+				ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
+
+		memcpy(epayload->format, format, format_len);
+	}
 
 	memcpy(epayload->master_desc, master_desc, strlen(master_desc));
 	memcpy(epayload->datalen, datalen, strlen(datalen));
@@ -617,19 +750,29 @@
  * itself.  For an old key, decrypt the hex encoded data.
  */
 static int encrypted_init(struct encrypted_key_payload *epayload,
+			  const char *key_desc, const char *format,
 			  const char *master_desc, const char *datalen,
 			  const char *hex_encoded_iv)
 {
 	int ret = 0;
 
-	__ekey_init(epayload, master_desc, datalen);
+	if (format && !strcmp(format, key_format_ecryptfs)) {
+		ret = valid_ecryptfs_desc(key_desc);
+		if (ret < 0)
+			return ret;
+
+		ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
+				       key_desc);
+	}
+
+	__ekey_init(epayload, format, master_desc, datalen);
 	if (!hex_encoded_iv) {
 		get_random_bytes(epayload->iv, ivsize);
 
 		get_random_bytes(epayload->decrypted_data,
 				 epayload->decrypted_datalen);
 	} else
-		ret = encrypted_key_decrypt(epayload, hex_encoded_iv);
+		ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
 	return ret;
 }
 
@@ -646,6 +789,7 @@
 {
 	struct encrypted_key_payload *epayload = NULL;
 	char *datablob = NULL;
+	const char *format = NULL;
 	char *master_desc = NULL;
 	char *decrypted_datalen = NULL;
 	char *hex_encoded_iv = NULL;
@@ -659,18 +803,19 @@
 		return -ENOMEM;
 	datablob[datalen] = 0;
 	memcpy(datablob, data, datalen);
-	ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
-			     &hex_encoded_iv);
+	ret = datablob_parse(datablob, &format, &master_desc,
+			     &decrypted_datalen, &hex_encoded_iv);
 	if (ret < 0)
 		goto out;
 
-	epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
+	epayload = encrypted_key_alloc(key, format, master_desc,
+				       decrypted_datalen);
 	if (IS_ERR(epayload)) {
 		ret = PTR_ERR(epayload);
 		goto out;
 	}
-	ret = encrypted_init(epayload, master_desc, decrypted_datalen,
-			     hex_encoded_iv);
+	ret = encrypted_init(epayload, key->description, format, master_desc,
+			     decrypted_datalen, hex_encoded_iv);
 	if (ret < 0) {
 		kfree(epayload);
 		goto out;
@@ -706,6 +851,7 @@
 	struct encrypted_key_payload *new_epayload;
 	char *buf;
 	char *new_master_desc = NULL;
+	const char *format = NULL;
 	int ret = 0;
 
 	if (datalen <= 0 || datalen > 32767 || !data)
@@ -717,7 +863,7 @@
 
 	buf[datalen] = 0;
 	memcpy(buf, data, datalen);
-	ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
+	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
 	if (ret < 0)
 		goto out;
 
@@ -725,18 +871,19 @@
 	if (ret < 0)
 		goto out;
 
-	new_epayload = encrypted_key_alloc(key, new_master_desc,
-					   epayload->datalen);
+	new_epayload = encrypted_key_alloc(key, epayload->format,
+					   new_master_desc, epayload->datalen);
 	if (IS_ERR(new_epayload)) {
 		ret = PTR_ERR(new_epayload);
 		goto out;
 	}
 
-	__ekey_init(new_epayload, new_master_desc, epayload->datalen);
+	__ekey_init(new_epayload, epayload->format, new_master_desc,
+		    epayload->datalen);
 
 	memcpy(new_epayload->iv, epayload->iv, ivsize);
-	memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
-	       epayload->decrypted_datalen);
+	memcpy(new_epayload->payload_data, epayload->payload_data,
+	       epayload->payload_datalen);
 
 	rcu_assign_pointer(key->payload.data, new_epayload);
 	call_rcu(&epayload->rcu, encrypted_rcu_free);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 6cff375..60d4e3f 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -251,6 +251,8 @@
 
 	if (IS_ERR(authkey_ref)) {
 		authkey = ERR_CAST(authkey_ref);
+		if (authkey == ERR_PTR(-EAGAIN))
+			authkey = ERR_PTR(-ENOKEY);
 		goto error;
 	}
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9f4c77d..a38316b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -57,7 +57,7 @@
 #include <net/netlabel.h>
 #include <linux/uaccess.h>
 #include <asm/ioctls.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>	/* for network interface checks */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 68178b7..48665ec 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -46,7 +46,7 @@
 #include <net/xfrm.h>
 #include <net/checksum.h>
 #include <net/udp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "avc.h"
 #include "objsec.h"
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index c8f3857..7c7f8c1 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -9,3 +9,64 @@
 	  Required userspace tools and further information may be
 	  found at <http://tomoyo.sourceforge.jp/>.
 	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
+	int "Default maximal count for learning mode"
+	default 2048
+	range 0 2147483647
+	depends on SECURITY_TOMOYO
+	help
+	  This is the default value for maximal ACL entries
+	  that are automatically appended into policy at "learning mode".
+	  Some programs access thousands of objects, so running
+	  such programs in "learning mode" dulls the system response
+	  and consumes much memory.
+	  This is the safeguard for such programs.
+
+config SECURITY_TOMOYO_MAX_AUDIT_LOG
+	int "Default maximal count for audit log"
+	default 1024
+	range 0 2147483647
+	depends on SECURITY_TOMOYO
+	help
+	  This is the default value for maximal entries for
+	  audit logs that the kernel can hold on memory.
+	  You can read the log via /sys/kernel/security/tomoyo/audit.
+	  If you don't need audit logs, you may set this value to 0.
+
+config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+	bool "Activate without calling userspace policy loader."
+	default n
+	depends on SECURITY_TOMOYO
+	---help---
+	  Say Y here if you want to activate access control as soon as built-in
+	  policy was loaded. This option will be useful for systems where
+	  operations which can lead to the hijacking of the boot sequence are
+	  needed before loading the policy. For example, you can activate
+	  immediately after loading the fixed part of policy which will allow
+	  only operations needed for mounting a partition which contains the
+	  variant part of policy and verifying (e.g. running GPG check) and
+	  loading the variant part of policy. Since you can start using
+	  enforcing mode from the beginning, you can reduce the possibility of
+	  hijacking the boot sequence.
+
+config SECURITY_TOMOYO_POLICY_LOADER
+	string "Location of userspace policy loader"
+	default "/sbin/tomoyo-init"
+	depends on SECURITY_TOMOYO
+	depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+	---help---
+	  This is the default pathname of policy loader which is called before
+	  activation. You can override this setting via TOMOYO_loader= kernel
+	  command line option.
+
+config SECURITY_TOMOYO_ACTIVATION_TRIGGER
+	string "Trigger for calling userspace policy loader"
+	default "/sbin/init"
+	depends on SECURITY_TOMOYO
+	depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+	---help---
+	  This is the default pathname of activation trigger.
+	  You can override this setting via TOMOYO_trigger= kernel command line
+	  option. For example, if you pass init=/bin/systemd option, you may
+	  want to also pass TOMOYO_trigger=/bin/systemd option.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 91640e9..95278b7 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -1 +1,48 @@
-obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+
+$(obj)/policy/profile.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating an empty policy/profile.conf
+	@touch $@
+
+$(obj)/policy/exception_policy.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating a default policy/exception_policy.conf
+	@echo initialize_domain /sbin/modprobe from any >> $@
+	@echo initialize_domain /sbin/hotplug from any >> $@
+
+$(obj)/policy/domain_policy.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating an empty policy/domain_policy.conf
+	@touch $@
+
+$(obj)/policy/manager.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating an empty policy/manager.conf
+	@touch $@
+
+$(obj)/policy/stat.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating an empty policy/stat.conf
+	@touch $@
+
+$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
+	@echo Generating built-in policy for TOMOYO 2.4.x.
+	@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
+	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
+	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
+	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
+	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
+	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@mv $@.tmp $@
+
+$(obj)/common.o: $(obj)/builtin-policy.h
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
new file mode 100644
index 0000000..5dbb1f7
--- /dev/null
+++ b/security/tomoyo/audit.c
@@ -0,0 +1,456 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/**
+ * tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @dump: Pointer to "struct tomoyo_page_dump".
+ *
+ * Returns the contents of @bprm on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_bprm(struct linux_binprm *bprm,
+			       struct tomoyo_page_dump *dump)
+{
+	static const int tomoyo_buffer_len = 4096 * 2;
+	char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS);
+	char *cp;
+	char *last_start;
+	int len;
+	unsigned long pos = bprm->p;
+	int offset = pos % PAGE_SIZE;
+	int argv_count = bprm->argc;
+	int envp_count = bprm->envc;
+	bool truncated = false;
+	if (!buffer)
+		return NULL;
+	len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
+	cp = buffer + len;
+	if (!argv_count) {
+		memmove(cp, "} envp[]={ ", 11);
+		cp += 11;
+	}
+	last_start = cp;
+	while (argv_count || envp_count) {
+		if (!tomoyo_dump_page(bprm, pos, dump))
+			goto out;
+		pos += PAGE_SIZE - offset;
+		/* Read. */
+		while (offset < PAGE_SIZE) {
+			const char *kaddr = dump->data;
+			const unsigned char c = kaddr[offset++];
+			if (cp == last_start)
+				*cp++ = '"';
+			if (cp >= buffer + tomoyo_buffer_len - 32) {
+				/* Reserve some room for "..." string. */
+				truncated = true;
+			} else if (c == '\\') {
+				*cp++ = '\\';
+				*cp++ = '\\';
+			} else if (c > ' ' && c < 127) {
+				*cp++ = c;
+			} else if (!c) {
+				*cp++ = '"';
+				*cp++ = ' ';
+				last_start = cp;
+			} else {
+				*cp++ = '\\';
+				*cp++ = (c >> 6) + '0';
+				*cp++ = ((c >> 3) & 7) + '0';
+				*cp++ = (c & 7) + '0';
+			}
+			if (c)
+				continue;
+			if (argv_count) {
+				if (--argv_count == 0) {
+					if (truncated) {
+						cp = last_start;
+						memmove(cp, "... ", 4);
+						cp += 4;
+					}
+					memmove(cp, "} envp[]={ ", 11);
+					cp += 11;
+					last_start = cp;
+					truncated = false;
+				}
+			} else if (envp_count) {
+				if (--envp_count == 0) {
+					if (truncated) {
+						cp = last_start;
+						memmove(cp, "... ", 4);
+						cp += 4;
+					}
+				}
+			}
+			if (!argv_count && !envp_count)
+				break;
+		}
+		offset = 0;
+	}
+	*cp++ = '}';
+	*cp = '\0';
+	return buffer;
+out:
+	snprintf(buffer, tomoyo_buffer_len - 1,
+		 "argv[]={ ... } envp[]= { ... }");
+	return buffer;
+}
+
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns file type string.
+ */
+static inline const char *tomoyo_filetype(const mode_t mode)
+{
+	switch (mode & S_IFMT) {
+	case S_IFREG:
+	case 0:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
+	case S_IFDIR:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
+	case S_IFLNK:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
+	case S_IFIFO:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
+	case S_IFSOCK:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
+	case S_IFBLK:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
+	case S_IFCHR:
+		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
+	}
+	return "unknown"; /* This should not happen. */
+}
+
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ *
+ * This function uses kmalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+	struct tomoyo_time stamp;
+	const pid_t gpid = task_pid_nr(current);
+	struct tomoyo_obj_info *obj = r->obj;
+	static const int tomoyo_buffer_len = 4096;
+	char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
+	int pos;
+	u8 i;
+	if (!buffer)
+		return NULL;
+	{
+		struct timeval tv;
+		do_gettimeofday(&tv);
+		tomoyo_convert_time(tv.tv_sec, &stamp);
+	}
+	pos = snprintf(buffer, tomoyo_buffer_len - 1,
+		       "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
+		       "granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
+		       "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
+		       "fsuid=%u fsgid=%u }", stamp.year, stamp.month,
+		       stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
+		       tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
+		       tomoyo_sys_getpid(), tomoyo_sys_getppid(),
+		       current_uid(), current_gid(), current_euid(),
+		       current_egid(), current_suid(), current_sgid(),
+		       current_fsuid(), current_fsgid());
+	if (!obj)
+		goto no_obj_info;
+	if (!obj->validate_done) {
+		tomoyo_get_attributes(obj);
+		obj->validate_done = true;
+	}
+	for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+		struct tomoyo_mini_stat *stat;
+		unsigned int dev;
+		mode_t mode;
+		if (!obj->stat_valid[i])
+			continue;
+		stat = &obj->stat[i];
+		dev = stat->dev;
+		mode = stat->mode;
+		if (i & 1) {
+			pos += snprintf(buffer + pos,
+					tomoyo_buffer_len - 1 - pos,
+					" path%u.parent={ uid=%u gid=%u "
+					"ino=%lu perm=0%o }", (i >> 1) + 1,
+					stat->uid, stat->gid, (unsigned long)
+					stat->ino, stat->mode & S_IALLUGO);
+			continue;
+		}
+		pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+				" path%u={ uid=%u gid=%u ino=%lu major=%u"
+				" minor=%u perm=0%o type=%s", (i >> 1) + 1,
+				stat->uid, stat->gid, (unsigned long)
+				stat->ino, MAJOR(dev), MINOR(dev),
+				mode & S_IALLUGO, tomoyo_filetype(mode));
+		if (S_ISCHR(mode) || S_ISBLK(mode)) {
+			dev = stat->rdev;
+			pos += snprintf(buffer + pos,
+					tomoyo_buffer_len - 1 - pos,
+					" dev_major=%u dev_minor=%u",
+					MAJOR(dev), MINOR(dev));
+		}
+		pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+				" }");
+	}
+no_obj_info:
+	if (pos < tomoyo_buffer_len - 1)
+		return buffer;
+	kfree(buffer);
+	return NULL;
+}
+
+/**
+ * tomoyo_init_log - Allocate buffer for audit logs.
+ *
+ * @r:    Pointer to "struct tomoyo_request_info".
+ * @len:  Buffer size needed for @fmt and @args.
+ * @fmt:  The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns pointer to allocated memory.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+		      va_list args)
+{
+	char *buf = NULL;
+	char *bprm_info = NULL;
+	const char *header = NULL;
+	char *realpath = NULL;
+	const char *symlink = NULL;
+	int pos;
+	const char *domainname = r->domain->domainname->name;
+	header = tomoyo_print_header(r);
+	if (!header)
+		return NULL;
+	/* +10 is for '\n' etc. and '\0'. */
+	len += strlen(domainname) + strlen(header) + 10;
+	if (r->ee) {
+		struct file *file = r->ee->bprm->file;
+		realpath = tomoyo_realpath_from_path(&file->f_path);
+		bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
+		if (!realpath || !bprm_info)
+			goto out;
+		/* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */
+		len += strlen(realpath) + 80 + strlen(bprm_info);
+	} else if (r->obj && r->obj->symlink_target) {
+		symlink = r->obj->symlink_target->name;
+		/* +18 is for " symlink.target=\"%s\"" */
+		len += 18 + strlen(symlink);
+	}
+	len = tomoyo_round2(len);
+	buf = kzalloc(len, GFP_NOFS);
+	if (!buf)
+		goto out;
+	len--;
+	pos = snprintf(buf, len, "%s", header);
+	if (realpath) {
+		struct linux_binprm *bprm = r->ee->bprm;
+		pos += snprintf(buf + pos, len - pos,
+				" exec={ realpath=\"%s\" argc=%d envc=%d %s }",
+				realpath, bprm->argc, bprm->envc, bprm_info);
+	} else if (symlink)
+		pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
+				symlink);
+	pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
+	vsnprintf(buf + pos, len - pos, fmt, args);
+out:
+	kfree(realpath);
+	kfree(bprm_info);
+	kfree(header);
+	return buf;
+}
+
+/* Wait queue for /sys/kernel/security/tomoyo/audit. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
+
+/* Structure for audit log. */
+struct tomoyo_log {
+	struct list_head list;
+	char *log;
+	int size;
+};
+
+/* The list for "struct tomoyo_log". */
+static LIST_HEAD(tomoyo_log);
+
+/* Lock for "struct list_head tomoyo_log". */
+static DEFINE_SPINLOCK(tomoyo_log_lock);
+
+/* Length of "stuct list_head tomoyo_log". */
+static unsigned int tomoyo_log_count;
+
+/**
+ * tomoyo_get_audit - Get audit mode.
+ *
+ * @ns:          Pointer to "struct tomoyo_policy_namespace".
+ * @profile:     Profile number.
+ * @index:       Index number of functionality.
+ * @is_granted:  True if granted log, false otherwise.
+ *
+ * Returns true if this request should be audited, false otherwise.
+ */
+static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
+			     const u8 profile, const u8 index,
+			     const bool is_granted)
+{
+	u8 mode;
+	const u8 category = tomoyo_index2category[index] +
+		TOMOYO_MAX_MAC_INDEX;
+	struct tomoyo_profile *p;
+	if (!tomoyo_policy_loaded)
+		return false;
+	p = tomoyo_profile(ns, profile);
+	if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
+		return false;
+	mode = p->config[index];
+	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+		mode = p->config[category];
+	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+		mode = p->default_config;
+	if (is_granted)
+		return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
+	return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
+}
+
+/**
+ * tomoyo_write_log2 - Write an audit log.
+ *
+ * @r:    Pointer to "struct tomoyo_request_info".
+ * @len:  Buffer size needed for @fmt and @args.
+ * @fmt:  The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+		       va_list args)
+{
+	char *buf;
+	struct tomoyo_log *entry;
+	bool quota_exceeded = false;
+	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+		goto out;
+	buf = tomoyo_init_log(r, len, fmt, args);
+	if (!buf)
+		goto out;
+	entry = kzalloc(sizeof(*entry), GFP_NOFS);
+	if (!entry) {
+		kfree(buf);
+		goto out;
+	}
+	entry->log = buf;
+	len = tomoyo_round2(strlen(buf) + 1);
+	/*
+	 * The entry->size is used for memory quota checks.
+	 * Don't go beyond strlen(entry->log).
+	 */
+	entry->size = len + tomoyo_round2(sizeof(*entry));
+	spin_lock(&tomoyo_log_lock);
+	if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
+	    tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
+	    tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
+		quota_exceeded = true;
+	} else {
+		tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
+		list_add_tail(&entry->list, &tomoyo_log);
+		tomoyo_log_count++;
+	}
+	spin_unlock(&tomoyo_log_lock);
+	if (quota_exceeded) {
+		kfree(buf);
+		kfree(entry);
+		goto out;
+	}
+	wake_up(&tomoyo_log_wait);
+out:
+	return;
+}
+
+/**
+ * tomoyo_write_log - Write an audit log.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+	va_list args;
+	int len;
+	va_start(args, fmt);
+	len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+	va_end(args);
+	va_start(args, fmt);
+	tomoyo_write_log2(r, len, fmt, args);
+	va_end(args);
+}
+
+/**
+ * tomoyo_read_log - Read an audit log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+void tomoyo_read_log(struct tomoyo_io_buffer *head)
+{
+	struct tomoyo_log *ptr = NULL;
+	if (head->r.w_pos)
+		return;
+	kfree(head->read_buf);
+	head->read_buf = NULL;
+	spin_lock(&tomoyo_log_lock);
+	if (!list_empty(&tomoyo_log)) {
+		ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
+		list_del(&ptr->list);
+		tomoyo_log_count--;
+		tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
+	}
+	spin_unlock(&tomoyo_log_lock);
+	if (ptr) {
+		head->read_buf = ptr->log;
+		head->r.w[head->r.w_pos++] = head->read_buf;
+		kfree(ptr);
+	}
+}
+
+/**
+ * tomoyo_poll_log - Wait for an audit log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read an audit log.
+ */
+int tomoyo_poll_log(struct file *file, poll_table *wait)
+{
+	if (tomoyo_log_count)
+		return POLLIN | POLLRDNORM;
+	poll_wait(file, &tomoyo_log_wait, wait);
+	if (tomoyo_log_count)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index a0d09e5..c8439cf2 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/common.c
  *
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/uaccess.h>
@@ -11,56 +9,133 @@
 #include <linux/security.h>
 #include "common.h"
 
-static struct tomoyo_profile tomoyo_default_profile = {
-	.learning = &tomoyo_default_profile.preference,
-	.permissive = &tomoyo_default_profile.preference,
-	.enforcing = &tomoyo_default_profile.preference,
-	.preference.enforcing_verbose = true,
-	.preference.learning_max_entry = 2048,
-	.preference.learning_verbose = false,
-	.preference.permissive_verbose = true
-};
-
-/* Profile version. Currently only 20090903 is defined. */
-static unsigned int tomoyo_profile_version;
-
-/* Profile table. Memory is allocated as needed. */
-static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
-
-/* String table for functionality that takes 4 modes. */
-static const char *tomoyo_mode[4] = {
-	"disabled", "learning", "permissive", "enforcing"
+/* String table for operation mode. */
+const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
+	[TOMOYO_CONFIG_DISABLED]   = "disabled",
+	[TOMOYO_CONFIG_LEARNING]   = "learning",
+	[TOMOYO_CONFIG_PERMISSIVE] = "permissive",
+	[TOMOYO_CONFIG_ENFORCING]  = "enforcing"
 };
 
 /* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
-	[TOMOYO_MAC_FILE_EXECUTE]    = "file::execute",
-	[TOMOYO_MAC_FILE_OPEN]       = "file::open",
-	[TOMOYO_MAC_FILE_CREATE]     = "file::create",
-	[TOMOYO_MAC_FILE_UNLINK]     = "file::unlink",
-	[TOMOYO_MAC_FILE_MKDIR]      = "file::mkdir",
-	[TOMOYO_MAC_FILE_RMDIR]      = "file::rmdir",
-	[TOMOYO_MAC_FILE_MKFIFO]     = "file::mkfifo",
-	[TOMOYO_MAC_FILE_MKSOCK]     = "file::mksock",
-	[TOMOYO_MAC_FILE_TRUNCATE]   = "file::truncate",
-	[TOMOYO_MAC_FILE_SYMLINK]    = "file::symlink",
-	[TOMOYO_MAC_FILE_REWRITE]    = "file::rewrite",
-	[TOMOYO_MAC_FILE_MKBLOCK]    = "file::mkblock",
-	[TOMOYO_MAC_FILE_MKCHAR]     = "file::mkchar",
-	[TOMOYO_MAC_FILE_LINK]       = "file::link",
-	[TOMOYO_MAC_FILE_RENAME]     = "file::rename",
-	[TOMOYO_MAC_FILE_CHMOD]      = "file::chmod",
-	[TOMOYO_MAC_FILE_CHOWN]      = "file::chown",
-	[TOMOYO_MAC_FILE_CHGRP]      = "file::chgrp",
-	[TOMOYO_MAC_FILE_IOCTL]      = "file::ioctl",
-	[TOMOYO_MAC_FILE_CHROOT]     = "file::chroot",
-	[TOMOYO_MAC_FILE_MOUNT]      = "file::mount",
-	[TOMOYO_MAC_FILE_UMOUNT]     = "file::umount",
-	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
+	[TOMOYO_MAC_FILE_EXECUTE]    = "execute",
+	[TOMOYO_MAC_FILE_OPEN]       = "open",
+	[TOMOYO_MAC_FILE_CREATE]     = "create",
+	[TOMOYO_MAC_FILE_UNLINK]     = "unlink",
+	[TOMOYO_MAC_FILE_GETATTR]    = "getattr",
+	[TOMOYO_MAC_FILE_MKDIR]      = "mkdir",
+	[TOMOYO_MAC_FILE_RMDIR]      = "rmdir",
+	[TOMOYO_MAC_FILE_MKFIFO]     = "mkfifo",
+	[TOMOYO_MAC_FILE_MKSOCK]     = "mksock",
+	[TOMOYO_MAC_FILE_TRUNCATE]   = "truncate",
+	[TOMOYO_MAC_FILE_SYMLINK]    = "symlink",
+	[TOMOYO_MAC_FILE_MKBLOCK]    = "mkblock",
+	[TOMOYO_MAC_FILE_MKCHAR]     = "mkchar",
+	[TOMOYO_MAC_FILE_LINK]       = "link",
+	[TOMOYO_MAC_FILE_RENAME]     = "rename",
+	[TOMOYO_MAC_FILE_CHMOD]      = "chmod",
+	[TOMOYO_MAC_FILE_CHOWN]      = "chown",
+	[TOMOYO_MAC_FILE_CHGRP]      = "chgrp",
+	[TOMOYO_MAC_FILE_IOCTL]      = "ioctl",
+	[TOMOYO_MAC_FILE_CHROOT]     = "chroot",
+	[TOMOYO_MAC_FILE_MOUNT]      = "mount",
+	[TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
+	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
 	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 };
 
+/* String table for conditions. */
+const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+	[TOMOYO_TASK_UID]             = "task.uid",
+	[TOMOYO_TASK_EUID]            = "task.euid",
+	[TOMOYO_TASK_SUID]            = "task.suid",
+	[TOMOYO_TASK_FSUID]           = "task.fsuid",
+	[TOMOYO_TASK_GID]             = "task.gid",
+	[TOMOYO_TASK_EGID]            = "task.egid",
+	[TOMOYO_TASK_SGID]            = "task.sgid",
+	[TOMOYO_TASK_FSGID]           = "task.fsgid",
+	[TOMOYO_TASK_PID]             = "task.pid",
+	[TOMOYO_TASK_PPID]            = "task.ppid",
+	[TOMOYO_EXEC_ARGC]            = "exec.argc",
+	[TOMOYO_EXEC_ENVC]            = "exec.envc",
+	[TOMOYO_TYPE_IS_SOCKET]       = "socket",
+	[TOMOYO_TYPE_IS_SYMLINK]      = "symlink",
+	[TOMOYO_TYPE_IS_FILE]         = "file",
+	[TOMOYO_TYPE_IS_BLOCK_DEV]    = "block",
+	[TOMOYO_TYPE_IS_DIRECTORY]    = "directory",
+	[TOMOYO_TYPE_IS_CHAR_DEV]     = "char",
+	[TOMOYO_TYPE_IS_FIFO]         = "fifo",
+	[TOMOYO_MODE_SETUID]          = "setuid",
+	[TOMOYO_MODE_SETGID]          = "setgid",
+	[TOMOYO_MODE_STICKY]          = "sticky",
+	[TOMOYO_MODE_OWNER_READ]      = "owner_read",
+	[TOMOYO_MODE_OWNER_WRITE]     = "owner_write",
+	[TOMOYO_MODE_OWNER_EXECUTE]   = "owner_execute",
+	[TOMOYO_MODE_GROUP_READ]      = "group_read",
+	[TOMOYO_MODE_GROUP_WRITE]     = "group_write",
+	[TOMOYO_MODE_GROUP_EXECUTE]   = "group_execute",
+	[TOMOYO_MODE_OTHERS_READ]     = "others_read",
+	[TOMOYO_MODE_OTHERS_WRITE]    = "others_write",
+	[TOMOYO_MODE_OTHERS_EXECUTE]  = "others_execute",
+	[TOMOYO_EXEC_REALPATH]        = "exec.realpath",
+	[TOMOYO_SYMLINK_TARGET]       = "symlink.target",
+	[TOMOYO_PATH1_UID]            = "path1.uid",
+	[TOMOYO_PATH1_GID]            = "path1.gid",
+	[TOMOYO_PATH1_INO]            = "path1.ino",
+	[TOMOYO_PATH1_MAJOR]          = "path1.major",
+	[TOMOYO_PATH1_MINOR]          = "path1.minor",
+	[TOMOYO_PATH1_PERM]           = "path1.perm",
+	[TOMOYO_PATH1_TYPE]           = "path1.type",
+	[TOMOYO_PATH1_DEV_MAJOR]      = "path1.dev_major",
+	[TOMOYO_PATH1_DEV_MINOR]      = "path1.dev_minor",
+	[TOMOYO_PATH2_UID]            = "path2.uid",
+	[TOMOYO_PATH2_GID]            = "path2.gid",
+	[TOMOYO_PATH2_INO]            = "path2.ino",
+	[TOMOYO_PATH2_MAJOR]          = "path2.major",
+	[TOMOYO_PATH2_MINOR]          = "path2.minor",
+	[TOMOYO_PATH2_PERM]           = "path2.perm",
+	[TOMOYO_PATH2_TYPE]           = "path2.type",
+	[TOMOYO_PATH2_DEV_MAJOR]      = "path2.dev_major",
+	[TOMOYO_PATH2_DEV_MINOR]      = "path2.dev_minor",
+	[TOMOYO_PATH1_PARENT_UID]     = "path1.parent.uid",
+	[TOMOYO_PATH1_PARENT_GID]     = "path1.parent.gid",
+	[TOMOYO_PATH1_PARENT_INO]     = "path1.parent.ino",
+	[TOMOYO_PATH1_PARENT_PERM]    = "path1.parent.perm",
+	[TOMOYO_PATH2_PARENT_UID]     = "path2.parent.uid",
+	[TOMOYO_PATH2_PARENT_GID]     = "path2.parent.gid",
+	[TOMOYO_PATH2_PARENT_INO]     = "path2.parent.ino",
+	[TOMOYO_PATH2_PARENT_PERM]    = "path2.parent.perm",
+};
+
+/* String table for PREFERENCE keyword. */
+static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
+	[TOMOYO_PREF_MAX_AUDIT_LOG]      = "max_audit_log",
+	[TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
+};
+
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+	[TOMOYO_TYPE_EXECUTE]    = "execute",
+	[TOMOYO_TYPE_READ]       = "read",
+	[TOMOYO_TYPE_WRITE]      = "write",
+	[TOMOYO_TYPE_APPEND]     = "append",
+	[TOMOYO_TYPE_UNLINK]     = "unlink",
+	[TOMOYO_TYPE_GETATTR]    = "getattr",
+	[TOMOYO_TYPE_RMDIR]      = "rmdir",
+	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
+	[TOMOYO_TYPE_SYMLINK]    = "symlink",
+	[TOMOYO_TYPE_CHROOT]     = "chroot",
+	[TOMOYO_TYPE_UMOUNT]     = "unmount",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+	[TOMOYO_MAC_CATEGORY_FILE]       = "file",
+};
+
 /* Permit policy management by non-root user? */
 static bool tomoyo_manage_by_non_root;
 
@@ -71,11 +146,20 @@
  *
  * @value: Bool value.
  */
-static const char *tomoyo_yesno(const unsigned int value)
+const char *tomoyo_yesno(const unsigned int value)
 {
 	return value ? "yes" : "no";
 }
 
+/**
+ * tomoyo_addprintf - strncat()-like-snprintf().
+ *
+ * @buffer: Buffer to write to. Must be '\0'-terminated.
+ * @len:    Size of @buffer.
+ * @fmt:    The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
 static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
 {
 	va_list args;
@@ -96,7 +180,7 @@
 {
 	while (head->r.w_pos) {
 		const char *w = head->r.w[0];
-		int len = strlen(w);
+		size_t len = strlen(w);
 		if (len) {
 			if (len > head->read_user_buf_avail)
 				len = head->read_user_buf_avail;
@@ -111,7 +195,7 @@
 		head->r.w[0] = w;
 		if (*w)
 			return false;
-		/* Add '\0' for query. */
+		/* Add '\0' for audit logs and query. */
 		if (head->poll) {
 			if (!head->read_user_buf_avail ||
 			    copy_to_user(head->read_user_buf, "", 1))
@@ -155,8 +239,8 @@
 void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
 {
 	va_list args;
-	int len;
-	int pos = head->r.avail;
+	size_t len;
+	size_t pos = head->r.avail;
 	int size = head->readbuf_size - pos;
 	if (size <= 0)
 		return;
@@ -171,11 +255,25 @@
 	tomoyo_set_string(head, head->read_buf + pos);
 }
 
+/**
+ * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_set_space(struct tomoyo_io_buffer *head)
 {
 	tomoyo_set_string(head, " ");
 }
 
+/**
+ * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
 static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
 {
 	tomoyo_set_string(head, "\n");
@@ -183,6 +281,62 @@
 }
 
 /**
+ * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
+{
+	tomoyo_set_string(head, "/");
+}
+
+/* List of namespaces. */
+LIST_HEAD(tomoyo_namespace_list);
+/* True if namespace other than tomoyo_kernel_namespace is defined. */
+static bool tomoyo_namespace_enabled;
+
+/**
+ * tomoyo_init_policy_namespace - Initialize namespace.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ *
+ * Returns nothing.
+ */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
+{
+	unsigned int idx;
+	for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+		INIT_LIST_HEAD(&ns->acl_group[idx]);
+	for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+		INIT_LIST_HEAD(&ns->group_list[idx]);
+	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+		INIT_LIST_HEAD(&ns->policy_list[idx]);
+	ns->profile_version = 20100903;
+	tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
+	list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
+}
+
+/**
+ * tomoyo_print_namespace - Print namespace header.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
+{
+	if (!tomoyo_namespace_enabled)
+		return;
+	tomoyo_set_string(head,
+			  container_of(head->r.ns,
+				       struct tomoyo_policy_namespace,
+				       namespace_list)->name);
+	tomoyo_set_space(head);
+}
+
+/**
  * tomoyo_print_name_union - Print a tomoyo_name_union.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -192,7 +346,7 @@
 				    const struct tomoyo_name_union *ptr)
 {
 	tomoyo_set_space(head);
-	if (ptr->is_group) {
+	if (ptr->group) {
 		tomoyo_set_string(head, "@");
 		tomoyo_set_string(head, ptr->group->group_name->name);
 	} else {
@@ -201,24 +355,46 @@
 }
 
 /**
- * tomoyo_print_number_union - Print a tomoyo_number_union.
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
  *
- * @head:       Pointer to "struct tomoyo_io_buffer".
- * @ptr:        Pointer to "struct tomoyo_number_union".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
  */
-static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
-				      const struct tomoyo_number_union *ptr)
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+					   const struct tomoyo_name_union *ptr)
 {
-	tomoyo_set_space(head);
-	if (ptr->is_group) {
+	if (ptr->group) {
+		tomoyo_set_string(head, "@");
+		tomoyo_set_string(head, ptr->group->group_name->name);
+	} else {
+		tomoyo_set_string(head, "\"");
+		tomoyo_set_string(head, ptr->filename->name);
+		tomoyo_set_string(head, "\"");
+	}
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
+{
+	if (ptr->group) {
 		tomoyo_set_string(head, "@");
 		tomoyo_set_string(head, ptr->group->group_name->name);
 	} else {
 		int i;
 		unsigned long min = ptr->values[0];
 		const unsigned long max = ptr->values[1];
-		u8 min_type = ptr->min_type;
-		const u8 max_type = ptr->max_type;
+		u8 min_type = ptr->value_type[0];
+		const u8 max_type = ptr->value_type[1];
 		char buffer[128];
 		buffer[0] = '\0';
 		for (i = 0; i < 2; i++) {
@@ -232,8 +408,8 @@
 						 "0%lo", min);
 				break;
 			default:
-				tomoyo_addprintf(buffer, sizeof(buffer),
-						 "%lu", min);
+				tomoyo_addprintf(buffer, sizeof(buffer), "%lu",
+						 min);
 				break;
 			}
 			if (min == max && min_type == max_type)
@@ -247,35 +423,53 @@
 }
 
 /**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+				      const struct tomoyo_number_union *ptr)
+{
+	tomoyo_set_space(head);
+	tomoyo_print_number_union_nospace(head, ptr);
+}
+
+/**
  * tomoyo_assign_profile - Create a new profile.
  *
+ * @ns:      Pointer to "struct tomoyo_policy_namespace".
  * @profile: Profile number to create.
  *
  * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
  */
-static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
+static struct tomoyo_profile *tomoyo_assign_profile
+(struct tomoyo_policy_namespace *ns, const unsigned int profile)
 {
 	struct tomoyo_profile *ptr;
 	struct tomoyo_profile *entry;
 	if (profile >= TOMOYO_MAX_PROFILES)
 		return NULL;
-	ptr = tomoyo_profile_ptr[profile];
+	ptr = ns->profile_ptr[profile];
 	if (ptr)
 		return ptr;
 	entry = kzalloc(sizeof(*entry), GFP_NOFS);
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		goto out;
-	ptr = tomoyo_profile_ptr[profile];
+	ptr = ns->profile_ptr[profile];
 	if (!ptr && tomoyo_memory_ok(entry)) {
 		ptr = entry;
-		ptr->learning = &tomoyo_default_profile.preference;
-		ptr->permissive = &tomoyo_default_profile.preference;
-		ptr->enforcing = &tomoyo_default_profile.preference;
-		ptr->default_config = TOMOYO_CONFIG_DISABLED;
+		ptr->default_config = TOMOYO_CONFIG_DISABLED |
+			TOMOYO_CONFIG_WANT_GRANT_LOG |
+			TOMOYO_CONFIG_WANT_REJECT_LOG;
 		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
 		       sizeof(ptr->config));
+		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
+		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
 		mb(); /* Avoid out-of-order execution. */
-		tomoyo_profile_ptr[profile] = ptr;
+		ns->profile_ptr[profile] = ptr;
 		entry = NULL;
 	}
 	mutex_unlock(&tomoyo_policy_lock);
@@ -287,19 +481,29 @@
 /**
  * tomoyo_profile - Find a profile.
  *
+ * @ns:      Pointer to "struct tomoyo_policy_namespace".
  * @profile: Profile number to find.
  *
  * Returns pointer to "struct tomoyo_profile".
  */
-struct tomoyo_profile *tomoyo_profile(const u8 profile)
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+				      const u8 profile)
 {
-	struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
-	if (!tomoyo_policy_loaded)
-		return &tomoyo_default_profile;
-	BUG_ON(!ptr);
+	static struct tomoyo_profile tomoyo_null_profile;
+	struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+	if (!ptr)
+		ptr = &tomoyo_null_profile;
 	return ptr;
 }
 
+/**
+ * tomoyo_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find:   Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
 static s8 tomoyo_find_yesno(const char *string, const char *find)
 {
 	const char *cp = strstr(string, find);
@@ -313,18 +517,15 @@
 	return -1;
 }
 
-static void tomoyo_set_bool(bool *b, const char *string, const char *find)
-{
-	switch (tomoyo_find_yesno(string, find)) {
-	case 1:
-		*b = true;
-		break;
-	case 0:
-		*b = false;
-		break;
-	}
-}
-
+/**
+ * tomoyo_set_uint - Set value for specified preference.
+ *
+ * @i:      Pointer to "unsigned int".
+ * @string: String to check.
+ * @find:   Name of keyword.
+ *
+ * Returns nothing.
+ */
 static void tomoyo_set_uint(unsigned int *i, const char *string,
 			    const char *find)
 {
@@ -333,51 +534,16 @@
 		sscanf(cp + strlen(find), "=%u", i);
 }
 
-static void tomoyo_set_pref(const char *name, const char *value,
-			    const bool use_default,
-			    struct tomoyo_profile *profile)
-{
-	struct tomoyo_preference **pref;
-	bool *verbose;
-	if (!strcmp(name, "enforcing")) {
-		if (use_default) {
-			pref = &profile->enforcing;
-			goto set_default;
-		}
-		profile->enforcing = &profile->preference;
-		verbose = &profile->preference.enforcing_verbose;
-		goto set_verbose;
-	}
-	if (!strcmp(name, "permissive")) {
-		if (use_default) {
-			pref = &profile->permissive;
-			goto set_default;
-		}
-		profile->permissive = &profile->preference;
-		verbose = &profile->preference.permissive_verbose;
-		goto set_verbose;
-	}
-	if (!strcmp(name, "learning")) {
-		if (use_default) {
-			pref = &profile->learning;
-			goto set_default;
-		}
-		profile->learning = &profile->preference;
-		tomoyo_set_uint(&profile->preference.learning_max_entry, value,
-			     "max_entry");
-		verbose = &profile->preference.learning_verbose;
-		goto set_verbose;
-	}
-	return;
- set_default:
-	*pref = &tomoyo_default_profile.preference;
-	return;
- set_verbose:
-	tomoyo_set_bool(verbose, value, "verbose");
-}
-
+/**
+ * tomoyo_set_mode - Set mode for specified profile.
+ *
+ * @name:    Name of functionality.
+ * @value:   Mode for @name.
+ * @profile: Pointer to "struct tomoyo_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_set_mode(char *name, const char *value,
-			   const bool use_default,
 			   struct tomoyo_profile *profile)
 {
 	u8 i;
@@ -389,7 +555,17 @@
 		config = 0;
 		for (i = 0; i < TOMOYO_MAX_MAC_INDEX
 			     + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
-			if (strcmp(name, tomoyo_mac_keywords[i]))
+			int len = 0;
+			if (i < TOMOYO_MAX_MAC_INDEX) {
+				const u8 c = tomoyo_index2category[i];
+				const char *category =
+					tomoyo_category_keywords[c];
+				len = strlen(category);
+				if (strncmp(name, category, len) ||
+				    name[len++] != ':' || name[len++] != ':')
+					continue;
+			}
+			if (strcmp(name + len, tomoyo_mac_keywords[i]))
 				continue;
 			config = profile->config[i];
 			break;
@@ -399,7 +575,7 @@
 	} else {
 		return -EINVAL;
 	}
-	if (use_default) {
+	if (strstr(value, "use_default")) {
 		config = TOMOYO_CONFIG_USE_DEFAULT;
 	} else {
 		u8 mode;
@@ -410,6 +586,24 @@
 				 * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
 				 */
 				config = (config & ~7) | mode;
+		if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+			switch (tomoyo_find_yesno(value, "grant_log")) {
+			case 1:
+				config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+				break;
+			case 0:
+				config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+				break;
+			}
+			switch (tomoyo_find_yesno(value, "reject_log")) {
+			case 1:
+				config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+				break;
+			case 0:
+				config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+				break;
+			}
+		}
 	}
 	if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
 		profile->config[i] = config;
@@ -429,34 +623,22 @@
 {
 	char *data = head->write_buf;
 	unsigned int i;
-	bool use_default = false;
 	char *cp;
 	struct tomoyo_profile *profile;
-	if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+	if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
+	    == 1)
 		return 0;
 	i = simple_strtoul(data, &cp, 10);
-	if (data == cp) {
-		profile = &tomoyo_default_profile;
-	} else {
-		if (*cp != '-')
-			return -EINVAL;
-		data = cp + 1;
-		profile = tomoyo_assign_profile(i);
-		if (!profile)
-			return -EINVAL;
-	}
+	if (*cp != '-')
+		return -EINVAL;
+	data = cp + 1;
+	profile = tomoyo_assign_profile(head->w.ns, i);
+	if (!profile)
+		return -EINVAL;
 	cp = strchr(data, '=');
 	if (!cp)
 		return -EINVAL;
 	*cp++ = '\0';
-	if (profile != &tomoyo_default_profile)
-		use_default = strstr(cp, "use_default") != NULL;
-	if (tomoyo_str_starts(&data, "PREFERENCE::")) {
-		tomoyo_set_pref(data, cp, use_default, profile);
-		return 0;
-	}
-	if (profile == &tomoyo_default_profile)
-		return -EINVAL;
 	if (!strcmp(data, "COMMENT")) {
 		static DEFINE_SPINLOCK(lock);
 		const struct tomoyo_path_info *new_comment
@@ -471,77 +653,62 @@
 		tomoyo_put_name(old_comment);
 		return 0;
 	}
-	return tomoyo_set_mode(data, cp, use_default, profile);
+	if (!strcmp(data, "PREFERENCE")) {
+		for (i = 0; i < TOMOYO_MAX_PREF; i++)
+			tomoyo_set_uint(&profile->pref[i], cp,
+					tomoyo_pref_keywords[i]);
+		return 0;
+	}
+	return tomoyo_set_mode(data, cp, profile);
 }
 
-static void tomoyo_print_preference(struct tomoyo_io_buffer *head,
-				    const int idx)
-{
-	struct tomoyo_preference *pref = &tomoyo_default_profile.preference;
-	const struct tomoyo_profile *profile = idx >= 0 ?
-		tomoyo_profile_ptr[idx] : NULL;
-	char buffer[16] = "";
-	if (profile) {
-		buffer[sizeof(buffer) - 1] = '\0';
-		snprintf(buffer, sizeof(buffer) - 1, "%u-", idx);
-	}
-	if (profile) {
-		pref = profile->learning;
-		if (pref == &tomoyo_default_profile.preference)
-			goto skip1;
-	}
-	tomoyo_io_printf(head, "%sPREFERENCE::%s={ "
-			 "verbose=%s max_entry=%u }\n",
-			 buffer, "learning",
-			 tomoyo_yesno(pref->learning_verbose),
-			 pref->learning_max_entry);
- skip1:
-	if (profile) {
-		pref = profile->permissive;
-		if (pref == &tomoyo_default_profile.preference)
-			goto skip2;
-	}
-	tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
-			 buffer, "permissive",
-			 tomoyo_yesno(pref->permissive_verbose));
- skip2:
-	if (profile) {
-		pref = profile->enforcing;
-		if (pref == &tomoyo_default_profile.preference)
-			return;
-	}
-	tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
-			 buffer, "enforcing",
-			 tomoyo_yesno(pref->enforcing_verbose));
-}
-
+/**
+ * tomoyo_print_config - Print mode for specified functionality.
+ *
+ * @head:   Pointer to "struct tomoyo_io_buffer".
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
 static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
 {
-	tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]);
+	tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
+			 tomoyo_mode[config & 3],
+			 tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
+			 tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
 }
 
 /**
  * tomoyo_read_profile - Read profile table.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
  */
 static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
 {
 	u8 index;
+	struct tomoyo_policy_namespace *ns =
+		container_of(head->r.ns, typeof(*ns), namespace_list);
 	const struct tomoyo_profile *profile;
+	if (head->r.eof)
+		return;
  next:
 	index = head->r.index;
-	profile = tomoyo_profile_ptr[index];
+	profile = ns->profile_ptr[index];
 	switch (head->r.step) {
 	case 0:
-		tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
-		tomoyo_print_preference(head, -1);
+		tomoyo_print_namespace(head);
+		tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
+				 ns->profile_version);
 		head->r.step++;
 		break;
 	case 1:
 		for ( ; head->r.index < TOMOYO_MAX_PROFILES;
 		      head->r.index++)
-			if (tomoyo_profile_ptr[head->r.index])
+			if (ns->profile_ptr[head->r.index])
 				break;
 		if (head->r.index == TOMOYO_MAX_PROFILES)
 			return;
@@ -549,16 +716,25 @@
 		break;
 	case 2:
 		{
+			u8 i;
 			const struct tomoyo_path_info *comment =
 				profile->comment;
+			tomoyo_print_namespace(head);
 			tomoyo_io_printf(head, "%u-COMMENT=", index);
 			tomoyo_set_string(head, comment ? comment->name : "");
 			tomoyo_set_lf(head);
+			tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
+			for (i = 0; i < TOMOYO_MAX_PREF; i++)
+				tomoyo_io_printf(head, "%s=%u ",
+						 tomoyo_pref_keywords[i],
+						 profile->pref[i]);
+			tomoyo_set_string(head, "}\n");
 			head->r.step++;
 		}
 		break;
 	case 3:
 		{
+			tomoyo_print_namespace(head);
 			tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
 			tomoyo_print_config(head, profile->default_config);
 			head->r.bit = 0;
@@ -572,15 +748,22 @@
 			const u8 config = profile->config[i];
 			if (config == TOMOYO_CONFIG_USE_DEFAULT)
 				continue;
-			tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
-					 tomoyo_mac_keywords[i]);
+			tomoyo_print_namespace(head);
+			if (i < TOMOYO_MAX_MAC_INDEX)
+				tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+						 index,
+						 tomoyo_category_keywords
+						 [tomoyo_index2category[i]],
+						 tomoyo_mac_keywords[i]);
+			else
+				tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+						 tomoyo_mac_keywords[i]);
 			tomoyo_print_config(head, config);
 			head->r.bit++;
 			break;
 		}
 		if (head->r.bit == TOMOYO_MAX_MAC_INDEX
 		    + TOMOYO_MAX_MAC_CATEGORY_INDEX) {
-			tomoyo_print_preference(head, index);
 			head->r.index++;
 			head->r.step = 1;
 		}
@@ -590,6 +773,14 @@
 		goto next;
 }
 
+/**
+ * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
 				const struct tomoyo_acl_head *b)
 {
@@ -611,8 +802,13 @@
 				       const bool is_delete)
 {
 	struct tomoyo_manager e = { };
-	int error;
-
+	struct tomoyo_acl_param param = {
+		/* .ns = &tomoyo_kernel_namespace, */
+		.is_delete = is_delete,
+		.list = &tomoyo_kernel_namespace.
+		policy_list[TOMOYO_ID_MANAGER],
+	};
+	int error = is_delete ? -ENOENT : -ENOMEM;
 	if (tomoyo_domain_def(manager)) {
 		if (!tomoyo_correct_domain(manager))
 			return -EINVAL;
@@ -622,12 +818,11 @@
 			return -EINVAL;
 	}
 	e.manager = tomoyo_get_name(manager);
-	if (!e.manager)
-		return -ENOMEM;
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-				     tomoyo_same_manager);
-	tomoyo_put_name(e.manager);
+	if (e.manager) {
+		error = tomoyo_update_policy(&e.head, sizeof(e), &param,
+					     tomoyo_same_manager);
+		tomoyo_put_name(e.manager);
+	}
 	return error;
 }
 
@@ -643,13 +838,12 @@
 static int tomoyo_write_manager(struct tomoyo_io_buffer *head)
 {
 	char *data = head->write_buf;
-	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
 
 	if (!strcmp(data, "manage_by_non_root")) {
-		tomoyo_manage_by_non_root = !is_delete;
+		tomoyo_manage_by_non_root = !head->w.is_delete;
 		return 0;
 	}
-	return tomoyo_update_manager_entry(data, is_delete);
+	return tomoyo_update_manager_entry(data, head->w.is_delete);
 }
 
 /**
@@ -663,8 +857,8 @@
 {
 	if (head->r.eof)
 		return;
-	list_for_each_cookie(head->r.acl,
-			     &tomoyo_policy_list[TOMOYO_ID_MANAGER]) {
+	list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
+			     policy_list[TOMOYO_ID_MANAGER]) {
 		struct tomoyo_manager *ptr =
 			list_entry(head->r.acl, typeof(*ptr), head.list);
 		if (ptr->head.is_deleted)
@@ -697,8 +891,8 @@
 		return true;
 	if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
 		return false;
-	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-				head.list) {
+	list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+				policy_list[TOMOYO_ID_MANAGER], head.list) {
 		if (!ptr->head.is_deleted && ptr->is_domain
 		    && !tomoyo_pathcmp(domainname, ptr->manager)) {
 			found = true;
@@ -710,8 +904,8 @@
 	exe = tomoyo_get_exe();
 	if (!exe)
 		return false;
-	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
-				head.list) {
+	list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+				policy_list[TOMOYO_ID_MANAGER], head.list) {
 		if (!ptr->head.is_deleted && !ptr->is_domain
 		    && !strcmp(exe, ptr->manager->name)) {
 			found = true;
@@ -732,7 +926,7 @@
 }
 
 /**
- * tomoyo_select_one - Parse select command.
+ * tomoyo_select_domain - Parse select command.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @data: String to parse.
@@ -741,16 +935,15 @@
  *
  * Caller holds tomoyo_read_lock().
  */
-static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
+static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
+				 const char *data)
 {
 	unsigned int pid;
 	struct tomoyo_domain_info *domain = NULL;
 	bool global_pid = false;
-
-	if (!strcmp(data, "allow_execute")) {
-		head->r.print_execute_only = true;
-		return true;
-	}
+	if (strncmp(data, "select ", 7))
+		return false;
+	data += 7;
 	if (sscanf(data, "pid=%u", &pid) == 1 ||
 	    (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
 		struct task_struct *p;
@@ -769,7 +962,7 @@
 			domain = tomoyo_find_domain(data + 7);
 	} else
 		return false;
-	head->write_var1 = domain;
+	head->w.domain = domain;
 	/* Accessing read_buf is safe because head->io_sem is held. */
 	if (!head->read_buf)
 		return true; /* Do nothing if open(O_WRONLY). */
@@ -821,20 +1014,47 @@
 /**
  * tomoyo_write_domain2 - Write domain policy.
  *
- * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ns:        Pointer to "struct tomoyo_policy_namespace".
+ * @list:      Pointer to "struct list_head".
+ * @data:      Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
+				struct list_head *list, char *data,
 				const bool is_delete)
 {
-	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
-		return tomoyo_write_mount(data, domain, is_delete);
-	return tomoyo_write_file(data, domain, is_delete);
+	struct tomoyo_acl_param param = {
+		.ns = ns,
+		.list = list,
+		.data = data,
+		.is_delete = is_delete,
+	};
+	static const struct {
+		const char *keyword;
+		int (*write) (struct tomoyo_acl_param *);
+	} tomoyo_callback[1] = {
+		{ "file ", tomoyo_write_file },
+	};
+	u8 i;
+	for (i = 0; i < 1; i++) {
+		if (!tomoyo_str_starts(&param.data,
+				       tomoyo_callback[i].keyword))
+			continue;
+		return tomoyo_callback[i].write(&param);
+	}
+	return -EINVAL;
 }
 
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+	[TOMOYO_DIF_QUOTA_WARNED]      = "quota_exceeded\n",
+	[TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
 /**
  * tomoyo_write_domain - Write domain policy.
  *
@@ -847,69 +1067,198 @@
 static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
 {
 	char *data = head->write_buf;
-	struct tomoyo_domain_info *domain = head->write_var1;
-	bool is_delete = false;
-	bool is_select = false;
+	struct tomoyo_policy_namespace *ns;
+	struct tomoyo_domain_info *domain = head->w.domain;
+	const bool is_delete = head->w.is_delete;
+	bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
 	unsigned int profile;
-
-	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
-		is_delete = true;
-	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
-		is_select = true;
-	if (is_select && tomoyo_select_one(head, data))
-		return 0;
-	/* Don't allow updating policies by non manager programs. */
-	if (!tomoyo_manager())
-		return -EPERM;
-	if (tomoyo_domain_def(data)) {
+	if (*data == '<') {
 		domain = NULL;
 		if (is_delete)
 			tomoyo_delete_domain(data);
 		else if (is_select)
 			domain = tomoyo_find_domain(data);
 		else
-			domain = tomoyo_assign_domain(data, 0);
-		head->write_var1 = domain;
+			domain = tomoyo_assign_domain(data, false);
+		head->w.domain = domain;
 		return 0;
 	}
 	if (!domain)
 		return -EINVAL;
-
-	if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+	ns = domain->ns;
+	if (sscanf(data, "use_profile %u", &profile) == 1
 	    && profile < TOMOYO_MAX_PROFILES) {
-		if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+		if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
 			domain->profile = (u8) profile;
 		return 0;
 	}
-	if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
-		domain->ignore_global_allow_read = !is_delete;
+	if (sscanf(data, "use_group %u\n", &profile) == 1
+	    && profile < TOMOYO_MAX_ACL_GROUPS) {
+		if (!is_delete)
+			domain->group = (u8) profile;
 		return 0;
 	}
-	if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
-		domain->quota_warned = !is_delete;
+	for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+		const char *cp = tomoyo_dif[profile];
+		if (strncmp(data, cp, strlen(cp) - 1))
+			continue;
+		domain->flags[profile] = !is_delete;
 		return 0;
 	}
-	if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) {
-		domain->transition_failed = !is_delete;
-		return 0;
-	}
-	return tomoyo_write_domain2(data, domain, is_delete);
+	return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
+				    is_delete);
 }
 
 /**
- * tomoyo_fns - Find next set bit.
+ * tomoyo_print_condition - Print condition part.
  *
- * @perm: 8 bits value.
- * @bit:  First bit to find.
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
  *
- * Returns next on-bit on success, 8 otherwise.
+ * Returns true on success, false otherwise.
  */
-static u8 tomoyo_fns(const u8 perm, u8 bit)
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+				   const struct tomoyo_condition *cond)
 {
-	for ( ; bit < 8; bit++)
-		if (perm & (1 << bit))
+	switch (head->r.cond_step) {
+	case 0:
+		head->r.cond_index = 0;
+		head->r.cond_step++;
+		/* fall through */
+	case 1:
+		{
+			const u16 condc = cond->condc;
+			const struct tomoyo_condition_element *condp =
+				(typeof(condp)) (cond + 1);
+			const struct tomoyo_number_union *numbers_p =
+				(typeof(numbers_p)) (condp + condc);
+			const struct tomoyo_name_union *names_p =
+				(typeof(names_p))
+				(numbers_p + cond->numbers_count);
+			const struct tomoyo_argv *argv =
+				(typeof(argv)) (names_p + cond->names_count);
+			const struct tomoyo_envp *envp =
+				(typeof(envp)) (argv + cond->argc);
+			u16 skip;
+			for (skip = 0; skip < head->r.cond_index; skip++) {
+				const u8 left = condp->left;
+				const u8 right = condp->right;
+				condp++;
+				switch (left) {
+				case TOMOYO_ARGV_ENTRY:
+					argv++;
+					continue;
+				case TOMOYO_ENVP_ENTRY:
+					envp++;
+					continue;
+				case TOMOYO_NUMBER_UNION:
+					numbers_p++;
+					break;
+				}
+				switch (right) {
+				case TOMOYO_NAME_UNION:
+					names_p++;
+					break;
+				case TOMOYO_NUMBER_UNION:
+					numbers_p++;
+					break;
+				}
+			}
+			while (head->r.cond_index < condc) {
+				const u8 match = condp->equals;
+				const u8 left = condp->left;
+				const u8 right = condp->right;
+				if (!tomoyo_flush(head))
+					return false;
+				condp++;
+				head->r.cond_index++;
+				tomoyo_set_space(head);
+				switch (left) {
+				case TOMOYO_ARGV_ENTRY:
+					tomoyo_io_printf(head,
+							 "exec.argv[%lu]%s=\"",
+							 argv->index, argv->
+							 is_not ? "!" : "");
+					tomoyo_set_string(head,
+							  argv->value->name);
+					tomoyo_set_string(head, "\"");
+					argv++;
+					continue;
+				case TOMOYO_ENVP_ENTRY:
+					tomoyo_set_string(head,
+							  "exec.envp[\"");
+					tomoyo_set_string(head,
+							  envp->name->name);
+					tomoyo_io_printf(head, "\"]%s=", envp->
+							 is_not ? "!" : "");
+					if (envp->value) {
+						tomoyo_set_string(head, "\"");
+						tomoyo_set_string(head, envp->
+								  value->name);
+						tomoyo_set_string(head, "\"");
+					} else {
+						tomoyo_set_string(head,
+								  "NULL");
+					}
+					envp++;
+					continue;
+				case TOMOYO_NUMBER_UNION:
+					tomoyo_print_number_union_nospace
+						(head, numbers_p++);
+					break;
+				default:
+					tomoyo_set_string(head,
+					       tomoyo_condition_keyword[left]);
+					break;
+				}
+				tomoyo_set_string(head, match ? "=" : "!=");
+				switch (right) {
+				case TOMOYO_NAME_UNION:
+					tomoyo_print_name_union_quoted
+						(head, names_p++);
+					break;
+				case TOMOYO_NUMBER_UNION:
+					tomoyo_print_number_union_nospace
+						(head, numbers_p++);
+					break;
+				default:
+					tomoyo_set_string(head,
+					  tomoyo_condition_keyword[right]);
+					break;
+				}
+			}
+		}
+		head->r.cond_step++;
+		/* fall through */
+	case 2:
+		if (!tomoyo_flush(head))
 			break;
-	return bit;
+		head->r.cond_step++;
+		/* fall through */
+	case 3:
+		tomoyo_set_lf(head);
+		return true;
+	}
+	return false;
+}
+
+/**
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
+ *
+ * @head:     Pointer to "struct tomoyo_io_buffer".
+ * @category: Category name.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_group(struct tomoyo_io_buffer *head,
+			     const char *category)
+{
+	if (head->type == TOMOYO_EXCEPTIONPOLICY) {
+		tomoyo_print_namespace(head);
+		tomoyo_io_printf(head, "acl_group %u ",
+				 head->r.acl_group_index);
+	}
+	tomoyo_set_string(head, category);
 }
 
 /**
@@ -924,63 +1273,96 @@
 			       struct tomoyo_acl_info *acl)
 {
 	const u8 acl_type = acl->type;
+	bool first = true;
 	u8 bit;
 
+	if (head->r.print_cond_part)
+		goto print_cond_part;
 	if (acl->is_deleted)
 		return true;
- next:
-	bit = head->r.bit;
 	if (!tomoyo_flush(head))
 		return false;
 	else if (acl_type == TOMOYO_TYPE_PATH_ACL) {
 		struct tomoyo_path_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
 		const u16 perm = ptr->perm;
-		for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+		for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
 			if (!(perm & (1 << bit)))
 				continue;
-			if (head->r.print_execute_only &&
+			if (head->r.print_transition_related_only &&
 			    bit != TOMOYO_TYPE_EXECUTE)
 				continue;
-			/* Print "read/write" instead of "read" and "write". */
-			if ((bit == TOMOYO_TYPE_READ ||
-			     bit == TOMOYO_TYPE_WRITE)
-			    && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
-				continue;
-			break;
+			if (first) {
+				tomoyo_set_group(head, "file ");
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_path_keyword[bit]);
 		}
-		if (bit >= TOMOYO_MAX_PATH_OPERATION)
-			goto done;
-		tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+		if (first)
+			return true;
 		tomoyo_print_name_union(head, &ptr->name);
-	} else if (head->r.print_execute_only) {
+	} else if (head->r.print_transition_related_only) {
 		return true;
 	} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
 		struct tomoyo_path2_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
-		bit = tomoyo_fns(ptr->perm, bit);
-		if (bit >= TOMOYO_MAX_PATH2_OPERATION)
-			goto done;
-		tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]);
+		const u8 perm = ptr->perm;
+		for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "file ");
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_mac_keywords
+					  [tomoyo_pp2mac[bit]]);
+		}
+		if (first)
+			return true;
 		tomoyo_print_name_union(head, &ptr->name1);
 		tomoyo_print_name_union(head, &ptr->name2);
 	} else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
 		struct tomoyo_path_number_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
-		bit = tomoyo_fns(ptr->perm, bit);
-		if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION)
-			goto done;
-		tomoyo_io_printf(head, "allow_%s",
-				 tomoyo_path_number_keyword[bit]);
+		const u8 perm = ptr->perm;
+		for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "file ");
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_mac_keywords
+					  [tomoyo_pn2mac[bit]]);
+		}
+		if (first)
+			return true;
 		tomoyo_print_name_union(head, &ptr->name);
 		tomoyo_print_number_union(head, &ptr->number);
 	} else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
 		struct tomoyo_mkdev_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
-		bit = tomoyo_fns(ptr->perm, bit);
-		if (bit >= TOMOYO_MAX_MKDEV_OPERATION)
-			goto done;
-		tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]);
+		const u8 perm = ptr->perm;
+		for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "file ");
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_mac_keywords
+					  [tomoyo_pnnn2mac[bit]]);
+		}
+		if (first)
+			return true;
 		tomoyo_print_name_union(head, &ptr->name);
 		tomoyo_print_number_union(head, &ptr->mode);
 		tomoyo_print_number_union(head, &ptr->major);
@@ -988,35 +1370,41 @@
 	} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
 		struct tomoyo_mount_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
-		tomoyo_io_printf(head, "allow_mount");
+		tomoyo_set_group(head, "file mount");
 		tomoyo_print_name_union(head, &ptr->dev_name);
 		tomoyo_print_name_union(head, &ptr->dir_name);
 		tomoyo_print_name_union(head, &ptr->fs_type);
 		tomoyo_print_number_union(head, &ptr->flags);
 	}
-	head->r.bit = bit + 1;
-	tomoyo_io_printf(head, "\n");
-	if (acl_type != TOMOYO_TYPE_MOUNT_ACL)
-		goto next;
- done:
-	head->r.bit = 0;
+	if (acl->cond) {
+		head->r.print_cond_part = true;
+		head->r.cond_step = 0;
+		if (!tomoyo_flush(head))
+			return false;
+print_cond_part:
+		if (!tomoyo_print_condition(head, acl->cond))
+			return false;
+		head->r.print_cond_part = false;
+	} else {
+		tomoyo_set_lf(head);
+	}
 	return true;
 }
 
 /**
  * tomoyo_read_domain2 - Read domain policy.
  *
- * @head:   Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
  *
  * Caller holds tomoyo_read_lock().
  *
  * Returns true on success, false otherwise.
  */
 static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
-				struct tomoyo_domain_info *domain)
+				struct list_head *list)
 {
-	list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+	list_for_each_cookie(head->r.acl, list) {
 		struct tomoyo_acl_info *ptr =
 			list_entry(head->r.acl, typeof(*ptr), list);
 		if (!tomoyo_print_entry(head, ptr))
@@ -1041,6 +1429,7 @@
 		struct tomoyo_domain_info *domain =
 			list_entry(head->r.domain, typeof(*domain), list);
 		switch (head->r.step) {
+			u8 i;
 		case 0:
 			if (domain->is_deleted &&
 			    !head->r.print_this_domain_only)
@@ -1048,22 +1437,18 @@
 			/* Print domainname and flags. */
 			tomoyo_set_string(head, domain->domainname->name);
 			tomoyo_set_lf(head);
-			tomoyo_io_printf(head,
-					 TOMOYO_KEYWORD_USE_PROFILE "%u\n",
+			tomoyo_io_printf(head, "use_profile %u\n",
 					 domain->profile);
-			if (domain->quota_warned)
-				tomoyo_set_string(head, "quota_exceeded\n");
-			if (domain->transition_failed)
-				tomoyo_set_string(head, "transition_failed\n");
-			if (domain->ignore_global_allow_read)
-				tomoyo_set_string(head,
-				       TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ
-						  "\n");
+			tomoyo_io_printf(head, "use_group %u\n",
+					 domain->group);
+			for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+				if (domain->flags[i])
+					tomoyo_set_string(head, tomoyo_dif[i]);
 			head->r.step++;
 			tomoyo_set_lf(head);
 			/* fall through */
 		case 1:
-			if (!tomoyo_read_domain2(head, domain))
+			if (!tomoyo_read_domain2(head, &domain->acl_info_list))
 				return;
 			head->r.step++;
 			if (!tomoyo_set_lf(head))
@@ -1080,73 +1465,6 @@
 }
 
 /**
- * tomoyo_write_domain_profile - Assign profile for specified domain.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0 on success, -EINVAL otherwise.
- *
- * This is equivalent to doing
- *
- *     ( echo "select " $domainname; echo "use_profile " $profile ) |
- *     /usr/sbin/tomoyo-loadpolicy -d
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
-{
-	char *data = head->write_buf;
-	char *cp = strchr(data, ' ');
-	struct tomoyo_domain_info *domain;
-	unsigned long profile;
-
-	if (!cp)
-		return -EINVAL;
-	*cp = '\0';
-	domain = tomoyo_find_domain(cp + 1);
-	if (strict_strtoul(data, 10, &profile))
-		return -EINVAL;
-	if (domain && profile < TOMOYO_MAX_PROFILES
-	    && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
-		domain->profile = (u8) profile;
-	return 0;
-}
-
-/**
- * tomoyo_read_domain_profile - Read only domainname and profile.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns list of profile number and domainname pairs.
- *
- * This is equivalent to doing
- *
- *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
- *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
- *     domainname = $0; } else if ( $1 == "use_profile" ) {
- *     print $2 " " domainname; domainname = ""; } } ; '
- *
- * Caller holds tomoyo_read_lock().
- */
-static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
-{
-	if (head->r.eof)
-		return;
-	list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
-		struct tomoyo_domain_info *domain =
-			list_entry(head->r.domain, typeof(*domain), list);
-		if (domain->is_deleted)
-			continue;
-		if (!tomoyo_flush(head))
-			return;
-		tomoyo_io_printf(head, "%u ", domain->profile);
-		tomoyo_set_string(head, domain->domainname->name);
-		tomoyo_set_lf(head);
-	}
-	head->r.eof = true;
-}
-
-/**
  * tomoyo_write_pid: Specify PID to obtain domainname.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -1204,18 +1522,20 @@
 	tomoyo_set_string(head, domain->domainname->name);
 }
 
+/* String table for domain transition control keywords. */
 static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
-	[TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE]
-	= TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN,
-	[TOMOYO_TRANSITION_CONTROL_INITIALIZE]
-	= TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
-	[TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN,
-	[TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN
+	[TOMOYO_TRANSITION_CONTROL_NO_RESET]      = "no_reset_domain ",
+	[TOMOYO_TRANSITION_CONTROL_RESET]         = "reset_domain ",
+	[TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+	[TOMOYO_TRANSITION_CONTROL_INITIALIZE]    = "initialize_domain ",
+	[TOMOYO_TRANSITION_CONTROL_NO_KEEP]       = "no_keep_domain ",
+	[TOMOYO_TRANSITION_CONTROL_KEEP]          = "keep_domain ",
 };
 
+/* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
-	[TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
-	[TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+	[TOMOYO_PATH_GROUP]   = "path_group ",
+	[TOMOYO_NUMBER_GROUP] = "number_group ",
 };
 
 /**
@@ -1229,29 +1549,30 @@
  */
 static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
 {
-	char *data = head->write_buf;
-	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
-	u8 i;
-	static const struct {
-		const char *keyword;
-		int (*write) (char *, const bool);
-	} tomoyo_callback[4] = {
-		{ TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator },
-		{ TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern },
-		{ TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite },
-		{ TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable },
+	const bool is_delete = head->w.is_delete;
+	struct tomoyo_acl_param param = {
+		.ns = head->w.ns,
+		.is_delete = is_delete,
+		.data = head->write_buf,
 	};
-
+	u8 i;
+	if (tomoyo_str_starts(&param.data, "aggregator "))
+		return tomoyo_write_aggregator(&param);
 	for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
-		if (tomoyo_str_starts(&data, tomoyo_transition_type[i]))
-			return tomoyo_write_transition_control(data, is_delete,
-							       i);
-	for (i = 0; i < 4; i++)
-		if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword))
-			return tomoyo_callback[i].write(data, is_delete);
+		if (tomoyo_str_starts(&param.data, tomoyo_transition_type[i]))
+			return tomoyo_write_transition_control(&param, i);
 	for (i = 0; i < TOMOYO_MAX_GROUP; i++)
-		if (tomoyo_str_starts(&data, tomoyo_group_name[i]))
-			return tomoyo_write_group(data, is_delete, i);
+		if (tomoyo_str_starts(&param.data, tomoyo_group_name[i]))
+			return tomoyo_write_group(&param, i);
+	if (tomoyo_str_starts(&param.data, "acl_group ")) {
+		unsigned int group;
+		char *data;
+		group = simple_strtoul(param.data, &data, 10);
+		if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+			return tomoyo_write_domain2
+				(head->w.ns, &head->w.ns->acl_group[group],
+				 data, is_delete);
+	}
 	return -EINVAL;
 }
 
@@ -1267,9 +1588,12 @@
  */
 static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
 {
-	list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) {
+	struct tomoyo_policy_namespace *ns =
+		container_of(head->r.ns, typeof(*ns), namespace_list);
+	struct list_head *list = &ns->group_list[idx];
+	list_for_each_cookie(head->r.group, list) {
 		struct tomoyo_group *group =
-			list_entry(head->r.group, typeof(*group), list);
+			list_entry(head->r.group, typeof(*group), head.list);
 		list_for_each_cookie(head->r.acl, &group->member_list) {
 			struct tomoyo_acl_head *ptr =
 				list_entry(head->r.acl, typeof(*ptr), list);
@@ -1277,6 +1601,7 @@
 				continue;
 			if (!tomoyo_flush(head))
 				return false;
+			tomoyo_print_namespace(head);
 			tomoyo_set_string(head, tomoyo_group_name[idx]);
 			tomoyo_set_string(head, group->group_name->name);
 			if (idx == TOMOYO_PATH_GROUP) {
@@ -1310,7 +1635,10 @@
  */
 static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
 {
-	list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) {
+	struct tomoyo_policy_namespace *ns =
+		container_of(head->r.ns, typeof(*ns), namespace_list);
+	struct list_head *list = &ns->policy_list[idx];
+	list_for_each_cookie(head->r.acl, list) {
 		struct tomoyo_acl_head *acl =
 			container_of(head->r.acl, typeof(*acl), list);
 		if (acl->is_deleted)
@@ -1322,35 +1650,23 @@
 			{
 				struct tomoyo_transition_control *ptr =
 					container_of(acl, typeof(*ptr), head);
-				tomoyo_set_string(head,
-						  tomoyo_transition_type
+				tomoyo_print_namespace(head);
+				tomoyo_set_string(head, tomoyo_transition_type
 						  [ptr->type]);
-				if (ptr->program)
-					tomoyo_set_string(head,
-							  ptr->program->name);
-				if (ptr->program && ptr->domainname)
-					tomoyo_set_string(head, " from ");
-				if (ptr->domainname)
-					tomoyo_set_string(head,
-							  ptr->domainname->
-							  name);
-			}
-			break;
-		case TOMOYO_ID_GLOBALLY_READABLE:
-			{
-				struct tomoyo_readable_file *ptr =
-					container_of(acl, typeof(*ptr), head);
-				tomoyo_set_string(head,
-						  TOMOYO_KEYWORD_ALLOW_READ);
-				tomoyo_set_string(head, ptr->filename->name);
+				tomoyo_set_string(head, ptr->program ?
+						  ptr->program->name : "any");
+				tomoyo_set_string(head, " from ");
+				tomoyo_set_string(head, ptr->domainname ?
+						  ptr->domainname->name :
+						  "any");
 			}
 			break;
 		case TOMOYO_ID_AGGREGATOR:
 			{
 				struct tomoyo_aggregator *ptr =
 					container_of(acl, typeof(*ptr), head);
-				tomoyo_set_string(head,
-						  TOMOYO_KEYWORD_AGGREGATOR);
+				tomoyo_print_namespace(head);
+				tomoyo_set_string(head, "aggregator ");
 				tomoyo_set_string(head,
 						  ptr->original_name->name);
 				tomoyo_set_space(head);
@@ -1358,24 +1674,6 @@
 					       ptr->aggregated_name->name);
 			}
 			break;
-		case TOMOYO_ID_PATTERN:
-			{
-				struct tomoyo_no_pattern *ptr =
-					container_of(acl, typeof(*ptr), head);
-				tomoyo_set_string(head,
-						  TOMOYO_KEYWORD_FILE_PATTERN);
-				tomoyo_set_string(head, ptr->pattern->name);
-			}
-			break;
-		case TOMOYO_ID_NO_REWRITE:
-			{
-				struct tomoyo_no_rewrite *ptr =
-					container_of(acl, typeof(*ptr), head);
-				tomoyo_set_string(head,
-						  TOMOYO_KEYWORD_DENY_REWRITE);
-				tomoyo_set_string(head, ptr->pattern->name);
-			}
-			break;
 		default:
 			continue;
 		}
@@ -1394,6 +1692,8 @@
  */
 static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
 {
+	struct tomoyo_policy_namespace *ns =
+		container_of(head->r.ns, typeof(*ns), namespace_list);
 	if (head->r.eof)
 		return;
 	while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1406,95 +1706,40 @@
 		head->r.step++;
 	if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
 		return;
+	while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+	       + TOMOYO_MAX_ACL_GROUPS) {
+		head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+			- TOMOYO_MAX_GROUP;
+		if (!tomoyo_read_domain2(head, &ns->acl_group
+					 [head->r.acl_group_index]))
+			return;
+		head->r.step++;
+	}
 	head->r.eof = true;
 }
 
-/**
- * tomoyo_print_header - Get header line of audit log.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns string representation.
- *
- * This function uses kmalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_print_header(struct tomoyo_request_info *r)
-{
-	struct timeval tv;
-	const pid_t gpid = task_pid_nr(current);
-	static const int tomoyo_buffer_len = 4096;
-	char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
-	pid_t ppid;
-	if (!buffer)
-		return NULL;
-	do_gettimeofday(&tv);
-	rcu_read_lock();
-	ppid = task_tgid_vnr(current->real_parent);
-	rcu_read_unlock();
-	snprintf(buffer, tomoyo_buffer_len - 1,
-		 "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
-		 " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
-		 " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
-		 tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid,
-		 task_tgid_vnr(current), ppid,
-		 current_uid(), current_gid(), current_euid(),
-		 current_egid(), current_suid(), current_sgid(),
-		 current_fsuid(), current_fsgid());
-	return buffer;
-}
-
-/**
- * tomoyo_init_audit_log - Allocate buffer for audit logs.
- *
- * @len: Required size.
- * @r:   Pointer to "struct tomoyo_request_info".
- *
- * Returns pointer to allocated memory.
- *
- * The @len is updated to add the header lines' size on success.
- *
- * This function uses kzalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
-{
-	char *buf = NULL;
-	const char *header;
-	const char *domainname;
-	if (!r->domain)
-		r->domain = tomoyo_domain();
-	domainname = r->domain->domainname->name;
-	header = tomoyo_print_header(r);
-	if (!header)
-		return NULL;
-	*len += strlen(domainname) + strlen(header) + 10;
-	buf = kzalloc(*len, GFP_NOFS);
-	if (buf)
-		snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
-	kfree(header);
-	return buf;
-}
-
-/* Wait queue for tomoyo_query_list. */
+/* Wait queue for kernel -> userspace notification. */
 static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
-
-/* Lock for manipulating tomoyo_query_list. */
-static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+/* Wait queue for userspace -> kernel notification. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);
 
 /* Structure for query. */
 struct tomoyo_query {
 	struct list_head list;
 	char *query;
-	int query_len;
+	size_t query_len;
 	unsigned int serial;
-	int timer;
-	int answer;
+	u8 timer;
+	u8 answer;
+	u8 retry;
 };
 
 /* The list for "struct tomoyo_query". */
 static LIST_HEAD(tomoyo_query_list);
 
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
 /*
  * Number of "struct file" referring /sys/kernel/security/tomoyo/query
  * interface.
@@ -1502,10 +1747,82 @@
 static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 
 /**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+	char *start = str;
+	while (*(unsigned char *) str > (unsigned char) ' ')
+		str++;
+	*str = '\0';
+	return strlen(start) + 1;
+}
+
+/**
+ * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @header: Lines containing ACL.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
+{
+	char *buffer;
+	char *realpath = NULL;
+	char *argv0 = NULL;
+	char *symlink = NULL;
+	char *cp = strchr(header, '\n');
+	int len;
+	if (!cp)
+		return;
+	cp = strchr(cp + 1, '\n');
+	if (!cp)
+		return;
+	*cp++ = '\0';
+	len = strlen(cp) + 1;
+	/* strstr() will return NULL if ordering is wrong. */
+	if (*cp == 'f') {
+		argv0 = strstr(header, " argv[]={ \"");
+		if (argv0) {
+			argv0 += 10;
+			len += tomoyo_truncate(argv0) + 14;
+		}
+		realpath = strstr(header, " exec={ realpath=\"");
+		if (realpath) {
+			realpath += 8;
+			len += tomoyo_truncate(realpath) + 6;
+		}
+		symlink = strstr(header, " symlink.target=\"");
+		if (symlink)
+			len += tomoyo_truncate(symlink + 1) + 1;
+	}
+	buffer = kmalloc(len, GFP_NOFS);
+	if (!buffer)
+		return;
+	snprintf(buffer, len - 1, "%s", cp);
+	if (realpath)
+		tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+	if (argv0)
+		tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
+	if (symlink)
+		tomoyo_addprintf(buffer, len, "%s", symlink);
+	tomoyo_normalize_line(buffer);
+	if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+				  false))
+		tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+	kfree(buffer);
+}
+
+/**
  * tomoyo_supervisor - Ask for the supervisor's decision.
  *
- * @r:       Pointer to "struct tomoyo_request_info".
- * @fmt:     The printf()'s format string, followed by parameters.
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
  *
  * Returns 0 if the supervisor decided to permit the access request which
  * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
@@ -1515,88 +1832,79 @@
 int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
 {
 	va_list args;
-	int error = -EPERM;
-	int pos;
+	int error;
 	int len;
 	static unsigned int tomoyo_serial;
-	struct tomoyo_query *entry = NULL;
+	struct tomoyo_query entry = { };
 	bool quota_exceeded = false;
-	char *header;
+	va_start(args, fmt);
+	len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+	va_end(args);
+	/* Write /sys/kernel/security/tomoyo/audit. */
+	va_start(args, fmt);
+	tomoyo_write_log2(r, len, fmt, args);
+	va_end(args);
+	/* Nothing more to do if granted. */
+	if (r->granted)
+		return 0;
+	if (r->mode)
+		tomoyo_update_stat(r->mode);
 	switch (r->mode) {
-		char *buffer;
+	case TOMOYO_CONFIG_ENFORCING:
+		error = -EPERM;
+		if (atomic_read(&tomoyo_query_observers))
+			break;
+		goto out;
 	case TOMOYO_CONFIG_LEARNING:
-		if (!tomoyo_domain_quota_is_ok(r))
-			return 0;
-		va_start(args, fmt);
-		len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
-		va_end(args);
-		buffer = kmalloc(len, GFP_NOFS);
-		if (!buffer)
-			return 0;
-		va_start(args, fmt);
-		vsnprintf(buffer, len - 1, fmt, args);
-		va_end(args);
-		tomoyo_normalize_line(buffer);
-		tomoyo_write_domain2(buffer, r->domain, false);
-		kfree(buffer);
+		error = 0;
+		/* Check max_learning_entry parameter. */
+		if (tomoyo_domain_quota_is_ok(r))
+			break;
 		/* fall through */
-	case TOMOYO_CONFIG_PERMISSIVE:
+	default:
 		return 0;
 	}
-	if (!r->domain)
-		r->domain = tomoyo_domain();
-	if (!atomic_read(&tomoyo_query_observers))
-		return -EPERM;
+	/* Get message. */
 	va_start(args, fmt);
-	len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+	entry.query = tomoyo_init_log(r, len, fmt, args);
 	va_end(args);
-	header = tomoyo_init_audit_log(&len, r);
-	if (!header)
+	if (!entry.query)
 		goto out;
-	entry = kzalloc(sizeof(*entry), GFP_NOFS);
-	if (!entry)
+	entry.query_len = strlen(entry.query) + 1;
+	if (!error) {
+		tomoyo_add_entry(r->domain, entry.query);
 		goto out;
-	entry->query = kzalloc(len, GFP_NOFS);
-	if (!entry->query)
-		goto out;
-	len = ksize(entry->query);
+	}
+	len = tomoyo_round2(entry.query_len);
 	spin_lock(&tomoyo_query_list_lock);
-	if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
-	    sizeof(*entry) >= tomoyo_quota_for_query) {
+	if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
+	    tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
+	    >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
 		quota_exceeded = true;
 	} else {
-		tomoyo_query_memory_size += len + sizeof(*entry);
-		entry->serial = tomoyo_serial++;
+		entry.serial = tomoyo_serial++;
+		entry.retry = r->retry;
+		tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
+		list_add_tail(&entry.list, &tomoyo_query_list);
 	}
 	spin_unlock(&tomoyo_query_list_lock);
 	if (quota_exceeded)
 		goto out;
-	pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s",
-		       entry->serial, r->retry, header);
-	kfree(header);
-	header = NULL;
-	va_start(args, fmt);
-	vsnprintf(entry->query + pos, len - 1 - pos, fmt, args);
-	entry->query_len = strlen(entry->query) + 1;
-	va_end(args);
-	spin_lock(&tomoyo_query_list_lock);
-	list_add_tail(&entry->list, &tomoyo_query_list);
-	spin_unlock(&tomoyo_query_list_lock);
 	/* Give 10 seconds for supervisor's opinion. */
-	for (entry->timer = 0;
-	     atomic_read(&tomoyo_query_observers) && entry->timer < 100;
-	     entry->timer++) {
-		wake_up(&tomoyo_query_wait);
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(HZ / 10);
-		if (entry->answer)
+	while (entry.timer < 10) {
+		wake_up_all(&tomoyo_query_wait);
+		if (wait_event_interruptible_timeout
+		    (tomoyo_answer_wait, entry.answer ||
+		     !atomic_read(&tomoyo_query_observers), HZ))
 			break;
+		else
+			entry.timer++;
 	}
 	spin_lock(&tomoyo_query_list_lock);
-	list_del(&entry->list);
-	tomoyo_query_memory_size -= len + sizeof(*entry);
+	list_del(&entry.list);
+	tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
 	spin_unlock(&tomoyo_query_list_lock);
-	switch (entry->answer) {
+	switch (entry.answer) {
 	case 3: /* Asked to retry by administrator. */
 		error = TOMOYO_RETRY_REQUEST;
 		r->retry++;
@@ -1605,18 +1913,12 @@
 		/* Granted by administrator. */
 		error = 0;
 		break;
-	case 0:
-		/* Timed out. */
-		break;
 	default:
-		/* Rejected by administrator. */
+		/* Timed out or rejected by administrator. */
 		break;
 	}
- out:
-	if (entry)
-		kfree(entry->query);
-	kfree(entry);
-	kfree(header);
+out:
+	kfree(entry.query);
 	return error;
 }
 
@@ -1663,8 +1965,8 @@
 static void tomoyo_read_query(struct tomoyo_io_buffer *head)
 {
 	struct list_head *tmp;
-	int pos = 0;
-	int len = 0;
+	unsigned int pos = 0;
+	size_t len = 0;
 	char *buf;
 	if (head->r.w_pos)
 		return;
@@ -1687,7 +1989,7 @@
 		head->r.query_index = 0;
 		return;
 	}
-	buf = kzalloc(len, GFP_NOFS);
+	buf = kzalloc(len + 32, GFP_NOFS);
 	if (!buf)
 		return;
 	pos = 0;
@@ -1703,7 +2005,8 @@
 		 * can change, but I don't care.
 		 */
 		if (len == ptr->query_len)
-			memmove(buf, ptr->query, len);
+			snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
+				 ptr->retry, ptr->query);
 		break;
 	}
 	spin_unlock(&tomoyo_query_list_lock);
@@ -1760,7 +2063,7 @@
 static void tomoyo_read_version(struct tomoyo_io_buffer *head)
 {
 	if (!head->r.eof) {
-		tomoyo_io_printf(head, "2.3.0");
+		tomoyo_io_printf(head, "2.4.0");
 		head->r.eof = true;
 	}
 }
@@ -1785,15 +2088,111 @@
 	}
 }
 
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+	[TOMOYO_STAT_POLICY_UPDATES]    = "update:",
+	[TOMOYO_STAT_POLICY_LEARNING]   = "violation in learning mode:",
+	[TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+	[TOMOYO_STAT_POLICY_ENFORCING]  = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+	[TOMOYO_MEMORY_POLICY] = "policy:",
+	[TOMOYO_MEMORY_AUDIT]  = "audit log:",
+	[TOMOYO_MEMORY_QUERY]  = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+	struct timeval tv;
+	do_gettimeofday(&tv);
+	/*
+	 * I don't use atomic operations because race condition is not fatal.
+	 */
+	tomoyo_stat_updated[index]++;
+	tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
+/**
+ * tomoyo_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
+{
+	u8 i;
+	unsigned int total = 0;
+	if (head->r.eof)
+		return;
+	for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+		tomoyo_io_printf(head, "Policy %-30s %10u",
+				 tomoyo_policy_headers[i],
+				 tomoyo_stat_updated[i]);
+		if (tomoyo_stat_modified[i]) {
+			struct tomoyo_time stamp;
+			tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+			tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+					 "%02u:%02u:%02u)",
+					 stamp.year, stamp.month, stamp.day,
+					 stamp.hour, stamp.min, stamp.sec);
+		}
+		tomoyo_set_lf(head);
+	}
+	for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+		unsigned int used = tomoyo_memory_used[i];
+		total += used;
+		tomoyo_io_printf(head, "Memory used by %-22s %10u",
+				 tomoyo_memory_headers[i], used);
+		used = tomoyo_memory_quota[i];
+		if (used)
+			tomoyo_io_printf(head, " (Quota: %10u)", used);
+		tomoyo_set_lf(head);
+	}
+	tomoyo_io_printf(head, "Total memory used:                    %10u\n",
+			 total);
+	head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	u8 i;
+	if (tomoyo_str_starts(&data, "Memory used by "))
+		for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+			if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+				sscanf(data, "%u", &tomoyo_memory_quota[i]);
+	return 0;
+}
+
 /**
  * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
  *
  * @type: Type of interface.
  * @file: Pointer to "struct file".
  *
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
  */
 int tomoyo_open_control(const u8 type, struct file *file)
 {
@@ -1814,15 +2213,15 @@
 		head->write = tomoyo_write_exception;
 		head->read = tomoyo_read_exception;
 		break;
+	case TOMOYO_AUDIT:
+		/* /sys/kernel/security/tomoyo/audit */
+		head->poll = tomoyo_poll_log;
+		head->read = tomoyo_read_log;
+		break;
 	case TOMOYO_SELFDOMAIN:
 		/* /sys/kernel/security/tomoyo/self_domain */
 		head->read = tomoyo_read_self_domain;
 		break;
-	case TOMOYO_DOMAIN_STATUS:
-		/* /sys/kernel/security/tomoyo/.domain_status */
-		head->write = tomoyo_write_domain_profile;
-		head->read = tomoyo_read_domain_profile;
-		break;
 	case TOMOYO_PROCESS_STATUS:
 		/* /sys/kernel/security/tomoyo/.process_status */
 		head->write = tomoyo_write_pid;
@@ -1833,11 +2232,11 @@
 		head->read = tomoyo_read_version;
 		head->readbuf_size = 128;
 		break;
-	case TOMOYO_MEMINFO:
-		/* /sys/kernel/security/tomoyo/meminfo */
-		head->write = tomoyo_write_memory_quota;
-		head->read = tomoyo_read_memory_counter;
-		head->readbuf_size = 512;
+	case TOMOYO_STAT:
+		/* /sys/kernel/security/tomoyo/stat */
+		head->write = tomoyo_write_stat;
+		head->read = tomoyo_read_stat;
+		head->readbuf_size = 1024;
 		break;
 	case TOMOYO_PROFILE:
 		/* /sys/kernel/security/tomoyo/profile */
@@ -1887,26 +2286,16 @@
 			return -ENOMEM;
 		}
 	}
-	if (type != TOMOYO_QUERY)
-		head->reader_idx = tomoyo_read_lock();
-	file->private_data = head;
-	/*
-	 * Call the handler now if the file is
-	 * /sys/kernel/security/tomoyo/self_domain
-	 * so that the user can use
-	 * cat < /sys/kernel/security/tomoyo/self_domain"
-	 * to know the current process's domainname.
-	 */
-	if (type == TOMOYO_SELFDOMAIN)
-		tomoyo_read_control(file, NULL, 0);
 	/*
 	 * If the file is /sys/kernel/security/tomoyo/query , increment the
 	 * observer counter.
 	 * The obserber counter is used by tomoyo_supervisor() to see if
 	 * there is some process monitoring /sys/kernel/security/tomoyo/query.
 	 */
-	else if (type == TOMOYO_QUERY)
+	if (type == TOMOYO_QUERY)
 		atomic_inc(&tomoyo_query_observers);
+	file->private_data = head;
+	tomoyo_notify_gc(head, true);
 	return 0;
 }
 
@@ -1917,7 +2306,8 @@
  * @wait: Pointer to "poll_table".
  *
  * Waits for read readiness.
- * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.
  */
 int tomoyo_poll_control(struct file *file, poll_table *wait)
 {
@@ -1928,21 +2318,58 @@
 }
 
 /**
+ * tomoyo_set_namespace_cursor - Set namespace to read.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
+{
+	struct list_head *ns;
+	if (head->type != TOMOYO_EXCEPTIONPOLICY &&
+	    head->type != TOMOYO_PROFILE)
+		return;
+	/*
+	 * If this is the first read, or reading previous namespace finished
+	 * and has more namespaces to read, update the namespace cursor.
+	 */
+	ns = head->r.ns;
+	if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) {
+		/* Clearing is OK because tomoyo_flush() returned true. */
+		memset(&head->r, 0, sizeof(head->r));
+		head->r.ns = ns ? ns->next : tomoyo_namespace_list.next;
+	}
+}
+
+/**
+ * tomoyo_has_more_namespace - Check for unread namespaces.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if we have more entries to print, false otherwise.
+ */
+static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
+{
+	return (head->type == TOMOYO_EXCEPTIONPOLICY ||
+		head->type == TOMOYO_PROFILE) && head->r.eof &&
+		head->r.ns->next != &tomoyo_namespace_list;
+}
+
+/**
  * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file:       Pointer to "struct file".
+ * @head:       Pointer to "struct tomoyo_io_buffer".
  * @buffer:     Poiner to buffer to write to.
  * @buffer_len: Size of @buffer.
  *
  * Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_read_control(struct file *file, char __user *buffer,
-			const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+			    const int buffer_len)
 {
 	int len;
-	struct tomoyo_io_buffer *head = file->private_data;
+	int idx;
 
 	if (!head->read)
 		return -ENOSYS;
@@ -1950,64 +2377,156 @@
 		return -EINTR;
 	head->read_user_buf = buffer;
 	head->read_user_buf_avail = buffer_len;
+	idx = tomoyo_read_lock();
 	if (tomoyo_flush(head))
 		/* Call the policy handler. */
-		head->read(head);
-	tomoyo_flush(head);
+		do {
+			tomoyo_set_namespace_cursor(head);
+			head->read(head);
+		} while (tomoyo_flush(head) &&
+			 tomoyo_has_more_namespace(head));
+	tomoyo_read_unlock(idx);
 	len = head->read_user_buf - buffer;
 	mutex_unlock(&head->io_sem);
 	return len;
 }
 
 /**
+ * tomoyo_parse_policy - Parse a policy line.
+ *
+ * @head: Poiter to "struct tomoyo_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
+{
+	/* Delete request? */
+	head->w.is_delete = !strncmp(line, "delete ", 7);
+	if (head->w.is_delete)
+		memmove(line, line + 7, strlen(line + 7) + 1);
+	/* Selecting namespace to update. */
+	if (head->type == TOMOYO_EXCEPTIONPOLICY ||
+	    head->type == TOMOYO_PROFILE) {
+		if (*line == '<') {
+			char *cp = strchr(line, ' ');
+			if (cp) {
+				*cp++ = '\0';
+				head->w.ns = tomoyo_assign_namespace(line);
+				memmove(line, cp, strlen(cp) + 1);
+			} else
+				head->w.ns = NULL;
+		} else
+			head->w.ns = &tomoyo_kernel_namespace;
+		/* Don't allow updating if namespace is invalid. */
+		if (!head->w.ns)
+			return -ENOENT;
+	}
+	/* Do the update. */
+	return head->write(head);
+}
+
+/**
  * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file:       Pointer to "struct file".
+ * @head:       Pointer to "struct tomoyo_io_buffer".
  * @buffer:     Pointer to buffer to read from.
  * @buffer_len: Size of @buffer.
  *
  * Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
-			 const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+			     const char __user *buffer, const int buffer_len)
 {
-	struct tomoyo_io_buffer *head = file->private_data;
 	int error = buffer_len;
-	int avail_len = buffer_len;
+	size_t avail_len = buffer_len;
 	char *cp0 = head->write_buf;
-
+	int idx;
 	if (!head->write)
 		return -ENOSYS;
 	if (!access_ok(VERIFY_READ, buffer, buffer_len))
 		return -EFAULT;
-	/* Don't allow updating policies by non manager programs. */
-	if (head->write != tomoyo_write_pid &&
-	    head->write != tomoyo_write_domain && !tomoyo_manager())
-		return -EPERM;
 	if (mutex_lock_interruptible(&head->io_sem))
 		return -EINTR;
+	idx = tomoyo_read_lock();
 	/* Read a line and dispatch it to the policy handler. */
 	while (avail_len > 0) {
 		char c;
-		if (head->write_avail >= head->writebuf_size - 1) {
-			error = -ENOMEM;
-			break;
-		} else if (get_user(c, buffer)) {
+		if (head->w.avail >= head->writebuf_size - 1) {
+			const int len = head->writebuf_size * 2;
+			char *cp = kzalloc(len, GFP_NOFS);
+			if (!cp) {
+				error = -ENOMEM;
+				break;
+			}
+			memmove(cp, cp0, head->w.avail);
+			kfree(cp0);
+			head->write_buf = cp;
+			cp0 = cp;
+			head->writebuf_size = len;
+		}
+		if (get_user(c, buffer)) {
 			error = -EFAULT;
 			break;
 		}
 		buffer++;
 		avail_len--;
-		cp0[head->write_avail++] = c;
+		cp0[head->w.avail++] = c;
 		if (c != '\n')
 			continue;
-		cp0[head->write_avail - 1] = '\0';
-		head->write_avail = 0;
+		cp0[head->w.avail - 1] = '\0';
+		head->w.avail = 0;
 		tomoyo_normalize_line(cp0);
-		head->write(head);
+		if (!strcmp(cp0, "reset")) {
+			head->w.ns = &tomoyo_kernel_namespace;
+			head->w.domain = NULL;
+			memset(&head->r, 0, sizeof(head->r));
+			continue;
+		}
+		/* Don't allow updating policies by non manager programs. */
+		switch (head->type) {
+		case TOMOYO_PROCESS_STATUS:
+			/* This does not write anything. */
+			break;
+		case TOMOYO_DOMAINPOLICY:
+			if (tomoyo_select_domain(head, cp0))
+				continue;
+			/* fall through */
+		case TOMOYO_EXCEPTIONPOLICY:
+			if (!strcmp(cp0, "select transition_only")) {
+				head->r.print_transition_related_only = true;
+				continue;
+			}
+			/* fall through */
+		default:
+			if (!tomoyo_manager()) {
+				error = -EPERM;
+				goto out;
+			}
+		}
+		switch (tomoyo_parse_policy(head, cp0)) {
+		case -EPERM:
+			error = -EPERM;
+			goto out;
+		case 0:
+			switch (head->type) {
+			case TOMOYO_DOMAINPOLICY:
+			case TOMOYO_EXCEPTIONPOLICY:
+			case TOMOYO_STAT:
+			case TOMOYO_PROFILE:
+			case TOMOYO_MANAGER:
+				tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+				break;
+			default:
+				break;
+			}
+			break;
+		}
 	}
+out:
+	tomoyo_read_unlock(idx);
 	mutex_unlock(&head->io_sem);
 	return error;
 }
@@ -2015,35 +2534,20 @@
 /**
  * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
  *
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
  *
- * Releases memory and returns 0.
- *
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
  */
-int tomoyo_close_control(struct file *file)
+int tomoyo_close_control(struct tomoyo_io_buffer *head)
 {
-	struct tomoyo_io_buffer *head = file->private_data;
-	const bool is_write = !!head->write_buf;
-
 	/*
 	 * If the file is /sys/kernel/security/tomoyo/query , decrement the
 	 * observer counter.
 	 */
-	if (head->type == TOMOYO_QUERY)
-		atomic_dec(&tomoyo_query_observers);
-	else
-		tomoyo_read_unlock(head->reader_idx);
-	/* Release memory used for policy I/O. */
-	kfree(head->read_buf);
-	head->read_buf = NULL;
-	kfree(head->write_buf);
-	head->write_buf = NULL;
-	kfree(head);
-	head = NULL;
-	file->private_data = NULL;
-	if (is_write)
-		tomoyo_run_gc();
+	if (head->type == TOMOYO_QUERY &&
+	    atomic_dec_and_test(&tomoyo_query_observers))
+		wake_up_all(&tomoyo_answer_wait);
+	tomoyo_notify_gc(head, false);
 	return 0;
 }
 
@@ -2055,27 +2559,90 @@
 	struct tomoyo_domain_info *domain;
 	const int idx = tomoyo_read_lock();
 	tomoyo_policy_loaded = true;
-	/* Check all profiles currently assigned to domains are defined. */
+	printk(KERN_INFO "TOMOYO: 2.4.0\n");
 	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
 		const u8 profile = domain->profile;
-		if (tomoyo_profile_ptr[profile])
+		const struct tomoyo_policy_namespace *ns = domain->ns;
+		if (ns->profile_version != 20100903)
+			printk(KERN_ERR
+			       "Profile version %u is not supported.\n",
+			       ns->profile_version);
+		else if (!ns->profile_ptr[profile])
+			printk(KERN_ERR
+			       "Profile %u (used by '%s') is not defined.\n",
+			       profile, domain->domainname->name);
+		else
 			continue;
-		printk(KERN_ERR "You need to define profile %u before using it.\n",
-		       profile);
-		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
+		printk(KERN_ERR
+		       "Userland tools for TOMOYO 2.4 must be installed and "
+		       "policy must be initialized.\n");
+		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
 		       "for more information.\n");
-		panic("Profile %u (used by '%s') not defined.\n",
-		      profile, domain->domainname->name);
+		panic("STOP!");
 	}
 	tomoyo_read_unlock(idx);
-	if (tomoyo_profile_version != 20090903) {
-		printk(KERN_ERR "You need to install userland programs for "
-		       "TOMOYO 2.3 and initialize policy configuration.\n");
-		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
-		       "for more information.\n");
-		panic("Profile version %u is not supported.\n",
-		      tomoyo_profile_version);
-	}
-	printk(KERN_INFO "TOMOYO: 2.3.0\n");
 	printk(KERN_INFO "Mandatory Access Control activated.\n");
 }
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+	/*
+	 * This include file is manually created and contains built-in policy
+	 * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+	 * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+	 * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+	 */
+#include "builtin-policy.h"
+	u8 i;
+	const int idx = tomoyo_read_lock();
+	for (i = 0; i < 5; i++) {
+		struct tomoyo_io_buffer head = { };
+		char *start = "";
+		switch (i) {
+		case 0:
+			start = tomoyo_builtin_profile;
+			head.type = TOMOYO_PROFILE;
+			head.write = tomoyo_write_profile;
+			break;
+		case 1:
+			start = tomoyo_builtin_exception_policy;
+			head.type = TOMOYO_EXCEPTIONPOLICY;
+			head.write = tomoyo_write_exception;
+			break;
+		case 2:
+			start = tomoyo_builtin_domain_policy;
+			head.type = TOMOYO_DOMAINPOLICY;
+			head.write = tomoyo_write_domain;
+			break;
+		case 3:
+			start = tomoyo_builtin_manager;
+			head.type = TOMOYO_MANAGER;
+			head.write = tomoyo_write_manager;
+			break;
+		case 4:
+			start = tomoyo_builtin_stat;
+			head.type = TOMOYO_STAT;
+			head.write = tomoyo_write_stat;
+			break;
+		}
+		while (1) {
+			char *end = strchr(start, '\n');
+			if (!end)
+				break;
+			*end = '\0';
+			tomoyo_normalize_line(start);
+			head.write_buf = start;
+			tomoyo_parse_policy(&head, start);
+			start = end + 1;
+		}
+	}
+	tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+	tomoyo_check_profile();
+#endif
+}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 7c66bd8..f7fbaa6 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -21,7 +21,8 @@
 #include <linux/list.h>
 #include <linux/cred.h>
 #include <linux/poll.h>
-struct linux_binprm;
+#include <linux/binfmts.h>
+#include <linux/highmem.h>
 
 /********** Constants definitions. **********/
 
@@ -38,66 +39,149 @@
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 
+/* Group number is an integer between 0 and 255. */
+#define TOMOYO_MAX_ACL_GROUPS 256
+
+/* Index numbers for "struct tomoyo_condition". */
+enum tomoyo_conditions_index {
+	TOMOYO_TASK_UID,             /* current_uid()   */
+	TOMOYO_TASK_EUID,            /* current_euid()  */
+	TOMOYO_TASK_SUID,            /* current_suid()  */
+	TOMOYO_TASK_FSUID,           /* current_fsuid() */
+	TOMOYO_TASK_GID,             /* current_gid()   */
+	TOMOYO_TASK_EGID,            /* current_egid()  */
+	TOMOYO_TASK_SGID,            /* current_sgid()  */
+	TOMOYO_TASK_FSGID,           /* current_fsgid() */
+	TOMOYO_TASK_PID,             /* sys_getpid()   */
+	TOMOYO_TASK_PPID,            /* sys_getppid()  */
+	TOMOYO_EXEC_ARGC,            /* "struct linux_binprm *"->argc */
+	TOMOYO_EXEC_ENVC,            /* "struct linux_binprm *"->envc */
+	TOMOYO_TYPE_IS_SOCKET,       /* S_IFSOCK */
+	TOMOYO_TYPE_IS_SYMLINK,      /* S_IFLNK */
+	TOMOYO_TYPE_IS_FILE,         /* S_IFREG */
+	TOMOYO_TYPE_IS_BLOCK_DEV,    /* S_IFBLK */
+	TOMOYO_TYPE_IS_DIRECTORY,    /* S_IFDIR */
+	TOMOYO_TYPE_IS_CHAR_DEV,     /* S_IFCHR */
+	TOMOYO_TYPE_IS_FIFO,         /* S_IFIFO */
+	TOMOYO_MODE_SETUID,          /* S_ISUID */
+	TOMOYO_MODE_SETGID,          /* S_ISGID */
+	TOMOYO_MODE_STICKY,          /* S_ISVTX */
+	TOMOYO_MODE_OWNER_READ,      /* S_IRUSR */
+	TOMOYO_MODE_OWNER_WRITE,     /* S_IWUSR */
+	TOMOYO_MODE_OWNER_EXECUTE,   /* S_IXUSR */
+	TOMOYO_MODE_GROUP_READ,      /* S_IRGRP */
+	TOMOYO_MODE_GROUP_WRITE,     /* S_IWGRP */
+	TOMOYO_MODE_GROUP_EXECUTE,   /* S_IXGRP */
+	TOMOYO_MODE_OTHERS_READ,     /* S_IROTH */
+	TOMOYO_MODE_OTHERS_WRITE,    /* S_IWOTH */
+	TOMOYO_MODE_OTHERS_EXECUTE,  /* S_IXOTH */
+	TOMOYO_EXEC_REALPATH,
+	TOMOYO_SYMLINK_TARGET,
+	TOMOYO_PATH1_UID,
+	TOMOYO_PATH1_GID,
+	TOMOYO_PATH1_INO,
+	TOMOYO_PATH1_MAJOR,
+	TOMOYO_PATH1_MINOR,
+	TOMOYO_PATH1_PERM,
+	TOMOYO_PATH1_TYPE,
+	TOMOYO_PATH1_DEV_MAJOR,
+	TOMOYO_PATH1_DEV_MINOR,
+	TOMOYO_PATH2_UID,
+	TOMOYO_PATH2_GID,
+	TOMOYO_PATH2_INO,
+	TOMOYO_PATH2_MAJOR,
+	TOMOYO_PATH2_MINOR,
+	TOMOYO_PATH2_PERM,
+	TOMOYO_PATH2_TYPE,
+	TOMOYO_PATH2_DEV_MAJOR,
+	TOMOYO_PATH2_DEV_MINOR,
+	TOMOYO_PATH1_PARENT_UID,
+	TOMOYO_PATH1_PARENT_GID,
+	TOMOYO_PATH1_PARENT_INO,
+	TOMOYO_PATH1_PARENT_PERM,
+	TOMOYO_PATH2_PARENT_UID,
+	TOMOYO_PATH2_PARENT_GID,
+	TOMOYO_PATH2_PARENT_INO,
+	TOMOYO_PATH2_PARENT_PERM,
+	TOMOYO_MAX_CONDITION_KEYWORD,
+	TOMOYO_NUMBER_UNION,
+	TOMOYO_NAME_UNION,
+	TOMOYO_ARGV_ENTRY,
+	TOMOYO_ENVP_ENTRY,
+};
+
+
+/* Index numbers for stat(). */
+enum tomoyo_path_stat_index {
+	/* Do not change this order. */
+	TOMOYO_PATH1,
+	TOMOYO_PATH1_PARENT,
+	TOMOYO_PATH2,
+	TOMOYO_PATH2_PARENT,
+	TOMOYO_MAX_PATH_STAT
+};
+
+/* Index numbers for operation mode. */
 enum tomoyo_mode_index {
 	TOMOYO_CONFIG_DISABLED,
 	TOMOYO_CONFIG_LEARNING,
 	TOMOYO_CONFIG_PERMISSIVE,
 	TOMOYO_CONFIG_ENFORCING,
-	TOMOYO_CONFIG_USE_DEFAULT = 255
+	TOMOYO_CONFIG_MAX_MODE,
+	TOMOYO_CONFIG_WANT_REJECT_LOG =  64,
+	TOMOYO_CONFIG_WANT_GRANT_LOG  = 128,
+	TOMOYO_CONFIG_USE_DEFAULT     = 255,
 };
 
+/* Index numbers for entry type. */
 enum tomoyo_policy_id {
 	TOMOYO_ID_GROUP,
 	TOMOYO_ID_PATH_GROUP,
 	TOMOYO_ID_NUMBER_GROUP,
 	TOMOYO_ID_TRANSITION_CONTROL,
 	TOMOYO_ID_AGGREGATOR,
-	TOMOYO_ID_GLOBALLY_READABLE,
-	TOMOYO_ID_PATTERN,
-	TOMOYO_ID_NO_REWRITE,
 	TOMOYO_ID_MANAGER,
+	TOMOYO_ID_CONDITION,
 	TOMOYO_ID_NAME,
 	TOMOYO_ID_ACL,
 	TOMOYO_ID_DOMAIN,
 	TOMOYO_MAX_POLICY
 };
 
+/* Index numbers for domain's attributes. */
+enum tomoyo_domain_info_flags_index {
+	/* Quota warnning flag.   */
+	TOMOYO_DIF_QUOTA_WARNED,
+	/*
+	 * This domain was unable to create a new domain at
+	 * tomoyo_find_next_domain() because the name of the domain to be
+	 * created was too long or it could not allocate memory.
+	 * More than one process continued execve() without domain transition.
+	 */
+	TOMOYO_DIF_TRANSITION_FAILED,
+	TOMOYO_MAX_DOMAIN_INFO_FLAGS
+};
+
+/* Index numbers for group entries. */
 enum tomoyo_group_id {
 	TOMOYO_PATH_GROUP,
 	TOMOYO_NUMBER_GROUP,
 	TOMOYO_MAX_GROUP
 };
 
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_AGGREGATOR                "aggregator "
-#define TOMOYO_KEYWORD_ALLOW_MOUNT               "allow_mount "
-#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
-#define TOMOYO_KEYWORD_DELETE                    "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
-#define TOMOYO_KEYWORD_PATH_GROUP                "path_group "
-#define TOMOYO_KEYWORD_NUMBER_GROUP              "number_group "
-#define TOMOYO_KEYWORD_SELECT                    "select "
-#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
-#define TOMOYO_KEYWORD_QUOTA_EXCEEDED            "quota_exceeded"
-#define TOMOYO_KEYWORD_TRANSITION_FAILED         "transition_failed"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME                         "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
+/* Index numbers for type of numeric values. */
+enum tomoyo_value_type {
+	TOMOYO_VALUE_TYPE_INVALID,
+	TOMOYO_VALUE_TYPE_DECIMAL,
+	TOMOYO_VALUE_TYPE_OCTAL,
+	TOMOYO_VALUE_TYPE_HEXADECIMAL,
+};
 
-/* Value type definition. */
-#define TOMOYO_VALUE_TYPE_INVALID     0
-#define TOMOYO_VALUE_TYPE_DECIMAL     1
-#define TOMOYO_VALUE_TYPE_OCTAL       2
-#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
-
+/* Index numbers for domain transition control keywords. */
 enum tomoyo_transition_type {
 	/* Do not change this order, */
+	TOMOYO_TRANSITION_CONTROL_NO_RESET,
+	TOMOYO_TRANSITION_CONTROL_RESET,
 	TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,
 	TOMOYO_TRANSITION_CONTROL_INITIALIZE,
 	TOMOYO_TRANSITION_CONTROL_NO_KEEP,
@@ -114,35 +198,29 @@
 	TOMOYO_TYPE_MOUNT_ACL,
 };
 
-/* Index numbers for File Controls. */
-
-/*
- * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
- * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
- * TOMOYO_TYPE_READ_WRITE is set.
- * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
- * or TOMOYO_TYPE_WRITE is cleared.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if
- * TOMOYO_TYPE_READ_WRITE is cleared.
- */
-
+/* Index numbers for access controls with one pathname. */
 enum tomoyo_path_acl_index {
-	TOMOYO_TYPE_READ_WRITE,
 	TOMOYO_TYPE_EXECUTE,
 	TOMOYO_TYPE_READ,
 	TOMOYO_TYPE_WRITE,
+	TOMOYO_TYPE_APPEND,
 	TOMOYO_TYPE_UNLINK,
+	TOMOYO_TYPE_GETATTR,
 	TOMOYO_TYPE_RMDIR,
 	TOMOYO_TYPE_TRUNCATE,
 	TOMOYO_TYPE_SYMLINK,
-	TOMOYO_TYPE_REWRITE,
 	TOMOYO_TYPE_CHROOT,
 	TOMOYO_TYPE_UMOUNT,
 	TOMOYO_MAX_PATH_OPERATION
 };
 
-#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE))
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_memory_stat_type {
+	TOMOYO_MEMORY_POLICY,
+	TOMOYO_MEMORY_AUDIT,
+	TOMOYO_MEMORY_QUERY,
+	TOMOYO_MAX_MEMORY_STAT
+};
 
 enum tomoyo_mkdev_acl_index {
 	TOMOYO_TYPE_MKBLOCK,
@@ -150,6 +228,7 @@
 	TOMOYO_MAX_MKDEV_OPERATION
 };
 
+/* Index numbers for access controls with two pathnames. */
 enum tomoyo_path2_acl_index {
 	TOMOYO_TYPE_LINK,
 	TOMOYO_TYPE_RENAME,
@@ -157,6 +236,7 @@
 	TOMOYO_MAX_PATH2_OPERATION
 };
 
+/* Index numbers for access controls with one pathname and one number. */
 enum tomoyo_path_number_acl_index {
 	TOMOYO_TYPE_CREATE,
 	TOMOYO_TYPE_MKDIR,
@@ -169,31 +249,45 @@
 	TOMOYO_MAX_PATH_NUMBER_OPERATION
 };
 
+/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */
 enum tomoyo_securityfs_interface_index {
 	TOMOYO_DOMAINPOLICY,
 	TOMOYO_EXCEPTIONPOLICY,
-	TOMOYO_DOMAIN_STATUS,
 	TOMOYO_PROCESS_STATUS,
-	TOMOYO_MEMINFO,
+	TOMOYO_STAT,
 	TOMOYO_SELFDOMAIN,
+	TOMOYO_AUDIT,
 	TOMOYO_VERSION,
 	TOMOYO_PROFILE,
 	TOMOYO_QUERY,
 	TOMOYO_MANAGER
 };
 
+/* Index numbers for special mount operations. */
+enum tomoyo_special_mount {
+	TOMOYO_MOUNT_BIND,            /* mount --bind /source /dest   */
+	TOMOYO_MOUNT_MOVE,            /* mount --move /old /new       */
+	TOMOYO_MOUNT_REMOUNT,         /* mount -o remount /dir        */
+	TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */
+	TOMOYO_MOUNT_MAKE_PRIVATE,    /* mount --make-private /dir    */
+	TOMOYO_MOUNT_MAKE_SLAVE,      /* mount --make-slave /dir      */
+	TOMOYO_MOUNT_MAKE_SHARED,     /* mount --make-shared /dir     */
+	TOMOYO_MAX_SPECIAL_MOUNT
+};
+
+/* Index numbers for functionality. */
 enum tomoyo_mac_index {
 	TOMOYO_MAC_FILE_EXECUTE,
 	TOMOYO_MAC_FILE_OPEN,
 	TOMOYO_MAC_FILE_CREATE,
 	TOMOYO_MAC_FILE_UNLINK,
+	TOMOYO_MAC_FILE_GETATTR,
 	TOMOYO_MAC_FILE_MKDIR,
 	TOMOYO_MAC_FILE_RMDIR,
 	TOMOYO_MAC_FILE_MKFIFO,
 	TOMOYO_MAC_FILE_MKSOCK,
 	TOMOYO_MAC_FILE_TRUNCATE,
 	TOMOYO_MAC_FILE_SYMLINK,
-	TOMOYO_MAC_FILE_REWRITE,
 	TOMOYO_MAC_FILE_MKBLOCK,
 	TOMOYO_MAC_FILE_MKCHAR,
 	TOMOYO_MAC_FILE_LINK,
@@ -209,38 +303,66 @@
 	TOMOYO_MAX_MAC_INDEX
 };
 
+/* Index numbers for category of functionality. */
 enum tomoyo_mac_category_index {
 	TOMOYO_MAC_CATEGORY_FILE,
 	TOMOYO_MAX_MAC_CATEGORY_INDEX
 };
 
-#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
+/*
+ * Retry this request. Returned by tomoyo_supervisor() if policy violation has
+ * occurred in enforcing mode and the userspace daemon decided to retry.
+ *
+ * We must choose a positive value in order to distinguish "granted" (which is
+ * 0) and "rejected" (which is a negative value) and "retry".
+ */
+#define TOMOYO_RETRY_REQUEST 1
+
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_policy_stat_type {
+	/* Do not change this order. */
+	TOMOYO_STAT_POLICY_UPDATES,
+	TOMOYO_STAT_POLICY_LEARNING,   /* == TOMOYO_CONFIG_LEARNING */
+	TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */
+	TOMOYO_STAT_POLICY_ENFORCING,  /* == TOMOYO_CONFIG_ENFORCING */
+	TOMOYO_MAX_POLICY_STAT
+};
+
+/* Index numbers for profile's PREFERENCE values. */
+enum tomoyo_pref_index {
+	TOMOYO_PREF_MAX_AUDIT_LOG,
+	TOMOYO_PREF_MAX_LEARNING_ENTRY,
+	TOMOYO_MAX_PREF
+};
 
 /********** Structure definitions. **********/
 
-/*
- * tomoyo_acl_head is a structure which is used for holding elements not in
- * domain policy.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_policy_list[] .
- *  (2) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
+/* Common header for holding ACL entries. */
 struct tomoyo_acl_head {
 	struct list_head list;
 	bool is_deleted;
 } __packed;
 
-/*
- * tomoyo_request_info is a structure which is used for holding
- *
- * (1) Domain information of current process.
- * (2) How many retries are made for this request.
- * (3) Profile number used for this request.
- * (4) Access control mode of the profile.
- */
+/* Common header for shared entries. */
+struct tomoyo_shared_acl_head {
+	struct list_head list;
+	atomic_t users;
+} __packed;
+
+struct tomoyo_policy_namespace;
+
+/* Structure for request info. */
 struct tomoyo_request_info {
+	/*
+	 * For holding parameters specific to operations which deal files.
+	 * NULL if not dealing files.
+	 */
+	struct tomoyo_obj_info *obj;
+	/*
+	 * For holding parameters specific to execve() request.
+	 * NULL if not dealing do_execve().
+	 */
+	struct tomoyo_execve *ee;
 	struct tomoyo_domain_info *domain;
 	/* For holding parameters. */
 	union {
@@ -248,11 +370,13 @@
 			const struct tomoyo_path_info *filename;
 			/* For using wildcards at tomoyo_find_next_domain(). */
 			const struct tomoyo_path_info *matched_path;
+			/* One of values in "enum tomoyo_path_acl_index". */
 			u8 operation;
 		} path;
 		struct {
 			const struct tomoyo_path_info *filename1;
 			const struct tomoyo_path_info *filename2;
+			/* One of values in "enum tomoyo_path2_acl_index". */
 			u8 operation;
 		} path2;
 		struct {
@@ -260,11 +384,16 @@
 			unsigned int mode;
 			unsigned int major;
 			unsigned int minor;
+			/* One of values in "enum tomoyo_mkdev_acl_index". */
 			u8 operation;
 		} mkdev;
 		struct {
 			const struct tomoyo_path_info *filename;
 			unsigned long number;
+			/*
+			 * One of values in
+			 * "enum tomoyo_path_number_acl_index".
+			 */
 			u8 operation;
 		} path_number;
 		struct {
@@ -283,26 +412,7 @@
 	u8 type;
 };
 
-/*
- * tomoyo_path_info is a structure which is used for holding a string data
- * used by TOMOYO.
- * This structure has several fields for supporting pattern matching.
- *
- * (1) "name" is the '\0' terminated string data.
- * (2) "hash" is full_name_hash(name, strlen(name)).
- *     This allows tomoyo_pathcmp() to compare by hash before actually compare
- *     using strcmp().
- * (3) "const_len" is the length of the initial segment of "name" which
- *     consists entirely of non wildcard characters. In other words, the length
- *     which we can compare two strings using strncmp().
- * (4) "is_dir" is a bool which is true if "name" ends with "/",
- *     false otherwise.
- *     TOMOYO distinguishes directory and non-directory. A directory ends with
- *     "/" and non-directory does not end with "/".
- * (5) "is_patterned" is a bool which is true if "name" contains wildcard
- *     characters, false otherwise. This allows TOMOYO to use "hash" and
- *     strcmp() for string comparison if "is_patterned" is false.
- */
+/* Structure for holding a token. */
 struct tomoyo_path_info {
 	const char *name;
 	u32 hash;          /* = full_name_hash(name, strlen(name)) */
@@ -311,36 +421,32 @@
 	bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
 };
 
-/*
- * tomoyo_name is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
- */
+/* Structure for holding string data. */
 struct tomoyo_name {
-	struct list_head list;
-	atomic_t users;
+	struct tomoyo_shared_acl_head head;
 	struct tomoyo_path_info entry;
 };
 
+/* Structure for holding a word. */
 struct tomoyo_name_union {
+	/* Either @filename or @group is NULL. */
 	const struct tomoyo_path_info *filename;
 	struct tomoyo_group *group;
-	u8 is_group;
 };
 
+/* Structure for holding a number. */
 struct tomoyo_number_union {
 	unsigned long values[2];
-	struct tomoyo_group *group;
-	u8 min_type;
-	u8 max_type;
-	u8 is_group;
+	struct tomoyo_group *group; /* Maybe NULL. */
+	/* One of values in "enum tomoyo_value_type". */
+	u8 value_type[2];
 };
 
 /* Structure for "path_group"/"number_group" directive. */
 struct tomoyo_group {
-	struct list_head list;
+	struct tomoyo_shared_acl_head head;
 	const struct tomoyo_path_info *group_name;
 	struct list_head member_list;
-	atomic_t users;
 };
 
 /* Structure for "path_group" directive. */
@@ -355,130 +461,158 @@
 	struct tomoyo_number_union number;
 };
 
-/*
- * tomoyo_acl_info is a structure which is used for holding
- *
- *  (1) "list" which is linked to the ->acl_info_list of
- *      "struct tomoyo_domain_info"
- *  (2) "is_deleted" is a bool which is true if this domain is marked as
- *      "deleted", false otherwise.
- *  (3) "type" which tells type of the entry.
- *
- * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl"
- * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed
- * "u8" without enlarging their structure size.
- */
+/* Subset of "struct stat". Used by conditional ACL and audit logs. */
+struct tomoyo_mini_stat {
+	uid_t uid;
+	gid_t gid;
+	ino_t ino;
+	mode_t mode;
+	dev_t dev;
+	dev_t rdev;
+};
+
+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */
+struct tomoyo_page_dump {
+	struct page *page;    /* Previously dumped page. */
+	char *data;           /* Contents of "page". Size is PAGE_SIZE. */
+};
+
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+	/*
+	 * True if tomoyo_get_attributes() was already called, false otherwise.
+	 */
+	bool validate_done;
+	/* True if @stat[] is valid. */
+	bool stat_valid[TOMOYO_MAX_PATH_STAT];
+	/* First pathname. Initialized with { NULL, NULL } if no path. */
+	struct path path1;
+	/* Second pathname. Initialized with { NULL, NULL } if no path. */
+	struct path path2;
+	/*
+	 * Information on @path1, @path1's parent directory, @path2, @path2's
+	 * parent directory.
+	 */
+	struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
+	/*
+	 * Content of symbolic link to be created. NULL for operations other
+	 * than symlink().
+	 */
+	struct tomoyo_path_info *symlink_target;
+};
+
+/* Structure for argv[]. */
+struct tomoyo_argv {
+	unsigned long index;
+	const struct tomoyo_path_info *value;
+	bool is_not;
+};
+
+/* Structure for envp[]. */
+struct tomoyo_envp {
+	const struct tomoyo_path_info *name;
+	const struct tomoyo_path_info *value;
+	bool is_not;
+};
+
+/* Structure for execve() operation. */
+struct tomoyo_execve {
+	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj;
+	struct linux_binprm *bprm;
+	/* For dumping argv[] and envp[]. */
+	struct tomoyo_page_dump dump;
+	/* For temporary use. */
+	char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+};
+
+/* Structure for entries which follows "struct tomoyo_condition". */
+struct tomoyo_condition_element {
+	/*
+	 * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a
+	 * "struct tomoyo_envp" for TOMOYO_ENVP_ENTRY is attached to the tail
+	 * of the array of this struct.
+	 */
+	u8 left;
+	/*
+	 * Right hand operand. A "struct tomoyo_number_union" for
+	 * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for
+	 * TOMOYO_NAME_UNION is attached to the tail of the array of this
+	 * struct.
+	 */
+	u8 right;
+	/* Equation operator. True if equals or overlaps, false otherwise. */
+	bool equals;
+};
+
+/* Structure for optional arguments. */
+struct tomoyo_condition {
+	struct tomoyo_shared_acl_head head;
+	u32 size; /* Memory size allocated for this entry. */
+	u16 condc; /* Number of conditions in this struct. */
+	u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
+	u16 names_count; /* Number of "struct tomoyo_name_union names". */
+	u16 argc; /* Number of "struct tomoyo_argv". */
+	u16 envc; /* Number of "struct tomoyo_envp". */
+	/*
+	 * struct tomoyo_condition_element condition[condc];
+	 * struct tomoyo_number_union values[numbers_count];
+	 * struct tomoyo_name_union names[names_count];
+	 * struct tomoyo_argv argv[argc];
+	 * struct tomoyo_envp envp[envc];
+	 */
+};
+
+/* Common header for individual entries. */
 struct tomoyo_acl_info {
 	struct list_head list;
+	struct tomoyo_condition *cond; /* Maybe NULL. */
 	bool is_deleted;
-	u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */
+	u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
 } __packed;
 
-/*
- * tomoyo_domain_info is a structure which is used for holding permissions
- * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_list .
- *  (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
- *  (3) "domainname" which holds the name of the domain.
- *  (4) "profile" which remembers profile number assigned to this domain.
- *  (5) "is_deleted" is a bool which is true if this domain is marked as
- *      "deleted", false otherwise.
- *  (6) "quota_warned" is a bool which is used for suppressing warning message
- *      when learning mode learned too much entries.
- *  (7) "ignore_global_allow_read" is a bool which is true if this domain
- *      should ignore "allow_read" directive in exception policy.
- *  (8) "transition_failed" is a bool which is set to true when this domain was
- *      unable to create a new domain at tomoyo_find_next_domain() because the
- *      name of the domain to be created was too long or it could not allocate
- *      memory. If set to true, more than one process continued execve()
- *      without domain transition.
- *  (9) "users" is an atomic_t that holds how many "struct cred"->security
- *      are referring this "struct tomoyo_domain_info". If is_deleted == true
- *      and users == 0, this struct will be kfree()d upon next garbage
- *      collection.
- *
- * A domain's lifecycle is an analogy of files on / directory.
- * Multiple domains with the same domainname cannot be created (as with
- * creating files with the same filename fails with -EEXIST).
- * If a process reached a domain, that process can reside in that domain after
- * that domain is marked as "deleted" (as with a process can access an already
- * open()ed file after that file was unlink()ed).
- */
+/* Structure for domain information. */
 struct tomoyo_domain_info {
 	struct list_head list;
 	struct list_head acl_info_list;
 	/* Name of this domain. Never NULL.          */
 	const struct tomoyo_path_info *domainname;
+	/* Namespace for this domain. Never NULL. */
+	struct tomoyo_policy_namespace *ns;
 	u8 profile;        /* Profile number to use. */
+	u8 group;          /* Group number to use.   */
 	bool is_deleted;   /* Delete flag.           */
-	bool quota_warned; /* Quota warnning flag.   */
-	bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
-	bool transition_failed; /* Domain transition failed flag. */
+	bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
 	atomic_t users; /* Number of referring credentials. */
 };
 
 /*
- * tomoyo_path_acl is a structure which is used for holding an
- * entry with one pathname operation (e.g. open(), mkdir()).
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name" is the pathname.
- *
- * Directives held by this structure are "allow_read/write", "allow_execute",
- * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
- * "allow_unmount".
+ * Structure for "file execute", "file read", "file write", "file append",
+ * "file unlink", "file getattr", "file rmdir", "file truncate",
+ * "file symlink", "file chroot" and "file unmount" directive.
  */
 struct tomoyo_path_acl {
 	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
-	u16 perm;
+	u16 perm; /* Bitmask of values in "enum tomoyo_path_acl_index". */
 	struct tomoyo_name_union name;
 };
 
 /*
- * tomoyo_path_number_acl is a structure which is used for holding an
- * entry with one pathname and one number operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name" is the pathname.
- *  (4) "number" is the numeric value.
- *
- * Directives held by this structure are "allow_create", "allow_mkdir",
- * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown"
- * and "allow_chgrp".
- *
+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock",
+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.
  */
 struct tomoyo_path_number_acl {
 	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+	/* Bitmask of values in "enum tomoyo_path_number_acl_index". */
 	u8 perm;
 	struct tomoyo_name_union name;
 	struct tomoyo_number_union number;
 };
 
-/*
- * tomoyo_mkdev_acl is a structure which is used for holding an
- * entry with one pathname and three numbers operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "mode" is the create mode.
- *  (4) "major" is the major number of device node.
- *  (5) "minor" is the minor number of device node.
- *
- * Directives held by this structure are "allow_mkchar", "allow_mkblock".
- *
- */
+/* Structure for "file mkblock" and "file mkchar" directive. */
 struct tomoyo_mkdev_acl {
 	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */
-	u8 perm;
+	u8 perm; /* Bitmask of values in "enum tomoyo_mkdev_acl_index". */
 	struct tomoyo_name_union name;
 	struct tomoyo_number_union mode;
 	struct tomoyo_number_union major;
@@ -486,38 +620,16 @@
 };
 
 /*
- * tomoyo_path2_acl is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "perm" which is a bitmask of permitted operations.
- *  (3) "name1" is the source/old pathname.
- *  (4) "name2" is the destination/new pathname.
- *
- * Directives held by this structure are "allow_rename", "allow_link" and
- * "allow_pivot_root".
+ * Structure for "file rename", "file link" and "file pivot_root" directive.
  */
 struct tomoyo_path2_acl {
 	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
-	u8 perm;
+	u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */
 	struct tomoyo_name_union name1;
 	struct tomoyo_name_union name2;
 };
 
-/*
- * tomoyo_mount_acl is a structure which is used for holding an
- * entry for mount operation.
- * It has following fields.
- *
- *  (1) "head" which is a "struct tomoyo_acl_info".
- *  (2) "dev_name" is the device name.
- *  (3) "dir_name" is the mount point.
- *  (4) "fs_type" is the filesystem type.
- *  (5) "flags" is the mount flags.
- *
- * Directive held by this structure is "allow_mount".
- */
+/* Structure for "file mount" directive. */
 struct tomoyo_mount_acl {
 	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
 	struct tomoyo_name_union dev_name;
@@ -526,7 +638,15 @@
 	struct tomoyo_number_union flags;
 };
 
-#define TOMOYO_MAX_IO_READ_QUEUE 32
+/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
+struct tomoyo_acl_param {
+	char *data;
+	struct list_head *list;
+	struct tomoyo_policy_namespace *ns;
+	bool is_delete;
+};
+
+#define TOMOYO_MAX_IO_READ_QUEUE 64
 
 /*
  * Structure for reading/writing policy via /sys/kernel/security/tomoyo
@@ -538,95 +658,55 @@
 	int (*poll) (struct file *file, poll_table *wait);
 	/* Exclusive lock for this structure.   */
 	struct mutex io_sem;
-	/* Index returned by tomoyo_read_lock(). */
-	int reader_idx;
 	char __user *read_user_buf;
-	int read_user_buf_avail;
+	size_t read_user_buf_avail;
 	struct {
+		struct list_head *ns;
 		struct list_head *domain;
 		struct list_head *group;
 		struct list_head *acl;
-		int avail;
-		int step;
-		int query_index;
+		size_t avail;
+		unsigned int step;
+		unsigned int query_index;
 		u16 index;
+		u16 cond_index;
+		u8 acl_group_index;
+		u8 cond_step;
 		u8 bit;
 		u8 w_pos;
 		bool eof;
 		bool print_this_domain_only;
-		bool print_execute_only;
+		bool print_transition_related_only;
+		bool print_cond_part;
 		const char *w[TOMOYO_MAX_IO_READ_QUEUE];
 	} r;
-	/* The position currently writing to.   */
-	struct tomoyo_domain_info *write_var1;
+	struct {
+		struct tomoyo_policy_namespace *ns;
+		/* The position currently writing to.   */
+		struct tomoyo_domain_info *domain;
+		/* Bytes available for writing.         */
+		size_t avail;
+		bool is_delete;
+	} w;
 	/* Buffer for reading.                  */
 	char *read_buf;
 	/* Size of read buffer.                 */
-	int readbuf_size;
+	size_t readbuf_size;
 	/* Buffer for writing.                  */
 	char *write_buf;
-	/* Bytes available for writing.         */
-	int write_avail;
 	/* Size of write buffer.                */
-	int writebuf_size;
+	size_t writebuf_size;
 	/* Type of this interface.              */
-	u8 type;
+	enum tomoyo_securityfs_interface_index type;
+	/* Users counter protected by tomoyo_io_buffer_list_lock. */
+	u8 users;
+	/* List for telling GC not to kfree() elements. */
+	struct list_head list;
 };
 
 /*
- * tomoyo_readable_file is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- */
-struct tomoyo_readable_file {
-	struct tomoyo_acl_head head;
-	const struct tomoyo_path_info *filename;
-};
-
-/*
- * tomoyo_no_pattern is a structure which is used for holding
- * "file_pattern" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "pattern" is a pathname pattern which is used for converting pathnames
- *      to pathname patterns during learning mode.
- */
-struct tomoyo_no_pattern {
-	struct tomoyo_acl_head head;
-	const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_no_rewrite is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "pattern" is a pathname which is by default not permitted to modify
- *      already existing content.
- */
-struct tomoyo_no_rewrite {
-	struct tomoyo_acl_head head;
-	const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_transition_control is a structure which is used for holding
- * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain"
- * entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "type" is type of this entry.
- *  (3) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- *  (4) "domainname" which is "a domainname" or "the last component of a
- *      domainname".
- *  (5) "program" which is a program's pathname.
+ * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/
+ * "no_keep_domain" keyword.
  */
 struct tomoyo_transition_control {
 	struct tomoyo_acl_head head;
@@ -637,32 +717,14 @@
 	const struct tomoyo_path_info *program;    /* Maybe NULL */
 };
 
-/*
- * tomoyo_aggregator is a structure which is used for holding
- * "aggregator" entries.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "original_name" which is originally requested name.
- *  (3) "aggregated_name" which is name to rewrite.
- */
+/* Structure for "aggregator" keyword. */
 struct tomoyo_aggregator {
 	struct tomoyo_acl_head head;
 	const struct tomoyo_path_info *original_name;
 	const struct tomoyo_path_info *aggregated_name;
 };
 
-/*
- * tomoyo_manager is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- *  (1) "head" is "struct tomoyo_acl_head".
- *  (2) "is_domain" is a bool which is true if "manager" is a domainname, false
- *      otherwise.
- *  (3) "manager" is a domainname or a program's pathname.
- */
+/* Structure for policy manager. */
 struct tomoyo_manager {
 	struct tomoyo_acl_head head;
 	bool is_domain;  /* True if manager is a domainname. */
@@ -677,6 +739,7 @@
 	bool permissive_verbose;
 };
 
+/* Structure for /sys/kernel/security/tomnoyo/profile interface. */
 struct tomoyo_profile {
 	const struct tomoyo_path_info *comment;
 	struct tomoyo_preference *learning;
@@ -685,246 +748,254 @@
 	struct tomoyo_preference preference;
 	u8 default_config;
 	u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+	unsigned int pref[TOMOYO_MAX_PREF];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct tomoyo_time {
+	u16 year;
+	u8 month;
+	u8 day;
+	u8 hour;
+	u8 min;
+	u8 sec;
+};
+
+/* Structure for policy namespace. */
+struct tomoyo_policy_namespace {
+	/* Profile table. Memory is allocated as needed. */
+	struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES];
+	/* List of "struct tomoyo_group". */
+	struct list_head group_list[TOMOYO_MAX_GROUP];
+	/* List of policy. */
+	struct list_head policy_list[TOMOYO_MAX_POLICY];
+	/* The global ACL referred by "use_group" keyword. */
+	struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
+	/* List for connecting to tomoyo_namespace_list list. */
+	struct list_head namespace_list;
+	/* Profile version. Currently only 20100903 is defined. */
+	unsigned int profile_version;
+	/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
+	const char *name;
 };
 
 /********** Function prototypes. **********/
 
-/* Check whether the given string starts with the given keyword. */
-bool tomoyo_str_starts(char **src, const char *find);
-/* Get tomoyo_realpath() of current process. */
-const char *tomoyo_get_exe(void);
-/* Format string. */
-void tomoyo_normalize_line(unsigned char *buffer);
-/* Print warning or error message on console. */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
-     __attribute__ ((format(printf, 2, 3)));
-/* Check all profiles currently assigned to domains are defined. */
-void tomoyo_check_profile(void);
-/* Open operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_open_control(const u8 type, struct file *file);
-/* Close /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_close_control(struct file *file);
-/* Poll operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_poll_control(struct file *file, poll_table *wait);
-/* Read operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_read_control(struct file *file, char __user *buffer,
-			const int buffer_len);
-/* Write operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
-			 const int buffer_len);
-/* Check whether the domain has too many ACL entries to hold. */
-bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
-/* Print out of memory warning message. */
-void tomoyo_warn_oom(const char *function);
-/* Check whether the given name matches the given name_union. */
-const struct tomoyo_path_info *
-tomoyo_compare_name_union(const struct tomoyo_path_info *name,
-			  const struct tomoyo_name_union *ptr);
-/* Check whether the given number matches the given number_union. */
 bool tomoyo_compare_number_union(const unsigned long value,
 				 const struct tomoyo_number_union *ptr);
-int tomoyo_get_mode(const u8 profile, const u8 index);
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
-	__attribute__ ((format(printf, 2, 3)));
-/* Check whether the domainname is correct. */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+		      const struct tomoyo_condition *cond);
 bool tomoyo_correct_domain(const unsigned char *domainname);
-/* Check whether the token is correct. */
 bool tomoyo_correct_path(const char *filename);
 bool tomoyo_correct_word(const char *string);
-/* Check whether the token can be a domainname. */
 bool tomoyo_domain_def(const unsigned char *buffer);
-bool tomoyo_parse_name_union(const char *filename,
-			     struct tomoyo_name_union *ptr);
-/* Check whether the given filename matches the given path_group. */
-const struct tomoyo_path_info *
-tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
-			  const struct tomoyo_group *group);
-/* Check whether the given value matches the given number_group. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+		      struct tomoyo_page_dump *dump);
+bool tomoyo_memory_ok(void *ptr);
 bool tomoyo_number_matches_group(const unsigned long min,
 				 const unsigned long max,
 				 const struct tomoyo_group *group);
-/* Check whether the given filename matches the given pattern. */
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
+			     struct tomoyo_name_union *ptr);
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_number_union *ptr);
 bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
 				 const struct tomoyo_path_info *pattern);
-
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
-/* Tokenize a line. */
-bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
-/* Write domain policy violation warning message to console? */
-bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
-/* Fill "struct tomoyo_request_info". */
+bool tomoyo_permstr(const char *string, const char *keyword);
+bool tomoyo_str_starts(char **src, const char *find);
+char *tomoyo_encode(const char *str);
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+		      va_list args);
+char *tomoyo_read_token(struct tomoyo_acl_param *param);
+char *tomoyo_realpath_from_path(struct path *path);
+char *tomoyo_realpath_nofollow(const char *pathname);
+const char *tomoyo_get_exe(void);
+const char *tomoyo_yesno(const unsigned int value);
+const struct tomoyo_path_info *tomoyo_compare_name_union
+(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
+const struct tomoyo_path_info *tomoyo_get_name(const char *name);
+const struct tomoyo_path_info *tomoyo_path_matches_group
+(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+				 struct path *path, const int flag);
+int tomoyo_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+		    const u8 index);
 int tomoyo_init_request_info(struct tomoyo_request_info *r,
 			     struct tomoyo_domain_info *domain,
 			     const u8 index);
-/* Check permission for mount operation. */
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
-			    unsigned long flags, void *data_page);
-/* Create "aggregator" entry in exception policy. */
-int tomoyo_write_aggregator(char *data, const bool is_delete);
-int tomoyo_write_transition_control(char *data, const bool is_delete,
-				    const u8 type);
-/*
- * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
- * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
- * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
- * "allow_link" entry in domain policy.
- */
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
-		      const bool is_delete);
-/* Create "allow_read" entry in exception policy. */
-int tomoyo_write_globally_readable(char *data, const bool is_delete);
-/* Create "allow_mount" entry in domain policy. */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
-		       const bool is_delete);
-/* Create "deny_rewrite" entry in exception policy. */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete);
-/* Create "file_pattern" entry in exception policy. */
-int tomoyo_write_pattern(char *data, const bool is_delete);
-/* Create "path_group"/"number_group" entry in exception policy. */
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type);
-int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
-     __attribute__ ((format(printf, 2, 3)));
-/* Find a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
-/* Find or create a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
-						const u8 profile);
-struct tomoyo_profile *tomoyo_profile(const u8 profile);
-/*
- * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
- */
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type);
-
-/* Check mode for specified functionality. */
-unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
-				const u8 index);
-/* Fill in "struct tomoyo_path_info" members. */
-void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
-/* Run policy loader when /sbin/init starts. */
-void tomoyo_load_policy(const char *filename);
-
-void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
-
-/* Convert binary string to ascii string. */
-char *tomoyo_encode(const char *str);
-
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and does not follow the final symlink.
- */
-char *tomoyo_realpath_nofollow(const char *pathname);
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and the pathname is already solved.
- */
-char *tomoyo_realpath_from_path(struct path *path);
-/* Get patterned pathname. */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename);
-
-/* Check memory quota. */
-bool tomoyo_memory_ok(void *ptr);
-void *tomoyo_commit_ok(void *data, const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
-const struct tomoyo_path_info *tomoyo_get_name(const char *name);
-
-/* Check for memory usage. */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize mm related code. */
-void __init tomoyo_mm_init(void);
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-			   const struct tomoyo_path_info *filename);
-int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
-				 struct path *path, const int flag);
-int tomoyo_path_number_perm(const u8 operation, struct path *path,
-			    unsigned long number);
 int tomoyo_mkdev_perm(const u8 operation, struct path *path,
 		      const unsigned int mode, unsigned int dev);
-int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+			    const char *type, unsigned long flags,
+			    void *data_page);
+int tomoyo_open_control(const u8 type, struct file *file);
 int tomoyo_path2_perm(const u8 operation, struct path *path1,
 		      struct path *path2);
-int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
-void tomoyo_print_ulong(char *buffer, const int buffer_len,
-			const unsigned long value, const u8 type);
-
-/* Drop refcount on tomoyo_name_union. */
-void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
-
-/* Run garbage collector. */
-void tomoyo_run_gc(void);
-
-void tomoyo_memory_free(void *ptr);
-
+int tomoyo_path_number_perm(const u8 operation, struct path *path,
+			    unsigned long number);
+int tomoyo_path_perm(const u8 operation, struct path *path,
+		     const char *target);
+int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+			   const struct tomoyo_path_info *filename);
+int tomoyo_poll_control(struct file *file, poll_table *wait);
+int tomoyo_poll_log(struct file *file, poll_table *wait);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+	__printf(2, 3);
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
-			 bool is_delete, struct tomoyo_domain_info *domain,
-			 bool (*check_duplicate) (const struct tomoyo_acl_info
-						  *,
-						  const struct tomoyo_acl_info
-						  *),
-			 bool (*merge_duplicate) (struct tomoyo_acl_info *,
-						  struct tomoyo_acl_info *,
-						  const bool));
+			 struct tomoyo_acl_param *param,
+			 bool (*check_duplicate)
+			 (const struct tomoyo_acl_info *,
+			  const struct tomoyo_acl_info *),
+			 bool (*merge_duplicate)
+			 (struct tomoyo_acl_info *, struct tomoyo_acl_info *,
+			  const bool));
 int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
-			 bool is_delete, struct list_head *list,
-			 bool (*check_duplicate) (const struct tomoyo_acl_head
-						  *,
-						  const struct tomoyo_acl_head
-						  *));
+			 struct tomoyo_acl_param *param,
+			 bool (*check_duplicate)
+			 (const struct tomoyo_acl_head *,
+			  const struct tomoyo_acl_head *));
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
+int tomoyo_write_file(struct tomoyo_acl_param *param);
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
+				    const u8 type);
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+			    const int buffer_len);
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+			     const char __user *buffer, const int buffer_len);
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param);
+struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
+						const bool transit);
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+				      const u8 idx);
+struct tomoyo_policy_namespace *tomoyo_assign_namespace
+(const char *domainname);
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+				      const u8 profile);
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+				const u8 index);
+u8 tomoyo_parse_ulong(unsigned long *result, char **str);
+void *tomoyo_commit_ok(void *data, const unsigned int size);
+void __init tomoyo_load_builtin_policy(void);
+void __init tomoyo_mm_init(void);
 void tomoyo_check_acl(struct tomoyo_request_info *r,
 		      bool (*check_entry) (struct tomoyo_request_info *,
 					   const struct tomoyo_acl_info *));
+void tomoyo_check_profile(void);
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp);
+void tomoyo_del_condition(struct list_head *element);
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
+void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+	 __printf(2, 3);
+void tomoyo_load_policy(const char *filename);
+void tomoyo_memory_free(void *ptr);
+void tomoyo_normalize_line(unsigned char *buffer);
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+			const unsigned long value, const u8 type);
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+void tomoyo_read_log(struct tomoyo_io_buffer *head);
+void tomoyo_update_stat(const u8 index);
+void tomoyo_warn_oom(const char *function);
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+	__printf(2, 3);
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+		       va_list args);
 
 /********** External variable definitions. **********/
 
-/* Lock for GC. */
-extern struct srcu_struct tomoyo_ss;
-
-/* The list for "struct tomoyo_domain_info". */
-extern struct list_head tomoyo_domain_list;
-
-extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
-extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
-
-/* Lock for protecting policy. */
-extern struct mutex tomoyo_policy_lock;
-
-/* Has /sbin/init started? */
 extern bool tomoyo_policy_loaded;
-
-/* The kernel's domain. */
+extern const char * const tomoyo_condition_keyword
+[TOMOYO_MAX_CONDITION_KEYWORD];
+extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
+extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+					      + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
+extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
+extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
+extern const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION];
+extern struct list_head tomoyo_condition_list;
+extern struct list_head tomoyo_domain_list;
+extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+extern struct list_head tomoyo_namespace_list;
+extern struct mutex tomoyo_policy_lock;
+extern struct srcu_struct tomoyo_ss;
 extern struct tomoyo_domain_info tomoyo_kernel_domain;
-
-extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
-extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION];
-extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION];
-extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION];
-
-extern unsigned int tomoyo_quota_for_query;
-extern unsigned int tomoyo_query_memory_size;
+extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
+extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
 
 /********** Inlined functions. **********/
 
+/**
+ * tomoyo_read_lock - Take lock for protecting policy.
+ *
+ * Returns index number for tomoyo_read_unlock().
+ */
 static inline int tomoyo_read_lock(void)
 {
 	return srcu_read_lock(&tomoyo_ss);
 }
 
+/**
+ * tomoyo_read_unlock - Release lock for protecting policy.
+ *
+ * @idx: Index number returned by tomoyo_read_lock().
+ *
+ * Returns nothing.
+ */
 static inline void tomoyo_read_unlock(int idx)
 {
 	srcu_read_unlock(&tomoyo_ss, idx);
 }
 
-/* strcmp() for "struct tomoyo_path_info" structure. */
+/**
+ * tomoyo_sys_getppid - Copy of getppid().
+ *
+ * Returns parent process's PID.
+ *
+ * Alpha does not have getppid() defined. To be able to build this module on
+ * Alpha, I have to copy getppid() from kernel/timer.c.
+ */
+static inline pid_t tomoyo_sys_getppid(void)
+{
+	pid_t pid;
+	rcu_read_lock();
+	pid = task_tgid_vnr(current->real_parent);
+	rcu_read_unlock();
+	return pid;
+}
+
+/**
+ * tomoyo_sys_getpid - Copy of getpid().
+ *
+ * Returns current thread's PID.
+ *
+ * Alpha does not have getpid() defined. To be able to build this module on
+ * Alpha, I have to copy getpid() from kernel/timer.c.
+ */
+static inline pid_t tomoyo_sys_getpid(void)
+{
+	return task_tgid_vnr(current);
+}
+
+/**
+ * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure.
+ *
+ * @a: Pointer to "struct tomoyo_path_info".
+ * @b: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
 				  const struct tomoyo_path_info *b)
 {
@@ -932,77 +1003,155 @@
 }
 
 /**
- * tomoyo_valid - Check whether the character is a valid char.
+ * tomoyo_put_name - Drop reference on "struct tomoyo_name".
  *
- * @c: The character to check.
+ * @name: Pointer to "struct tomoyo_path_info". Maybe NULL.
  *
- * Returns true if @c is a valid character, false otherwise.
+ * Returns nothing.
  */
-static inline bool tomoyo_valid(const unsigned char c)
-{
-	return c > ' ' && c < 127;
-}
-
-/**
- * tomoyo_invalid - Check whether the character is an invalid char.
- *
- * @c: The character to check.
- *
- * Returns true if @c is an invalid character, false otherwise.
- */
-static inline bool tomoyo_invalid(const unsigned char c)
-{
-	return c && (c <= ' ' || c >= 127);
-}
-
 static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
 {
 	if (name) {
 		struct tomoyo_name *ptr =
 			container_of(name, typeof(*ptr), entry);
-		atomic_dec(&ptr->users);
+		atomic_dec(&ptr->head.users);
 	}
 }
 
+/**
+ * tomoyo_put_condition - Drop reference on "struct tomoyo_condition".
+ *
+ * @cond: Pointer to "struct tomoyo_condition". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_put_condition(struct tomoyo_condition *cond)
+{
+	if (cond)
+		atomic_dec(&cond->head.users);
+}
+
+/**
+ * tomoyo_put_group - Drop reference on "struct tomoyo_group".
+ *
+ * @group: Pointer to "struct tomoyo_group". Maybe NULL.
+ *
+ * Returns nothing.
+ */
 static inline void tomoyo_put_group(struct tomoyo_group *group)
 {
 	if (group)
-		atomic_dec(&group->users);
+		atomic_dec(&group->head.users);
 }
 
+/**
+ * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" for current thread.
+ */
 static inline struct tomoyo_domain_info *tomoyo_domain(void)
 {
 	return current_cred()->security;
 }
 
+/**
+ * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct tomoyo_security" for specified thread.
+ */
 static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
 							    *task)
 {
 	return task_cred_xxx(task, security);
 }
 
-static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1,
-					   const struct tomoyo_acl_info *p2)
-{
-	return p1->type == p2->type;
-}
-
+/**
+ * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_name_union".
+ * @b: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static inline bool tomoyo_same_name_union
-(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b)
 {
-	return p1->filename == p2->filename && p1->group == p2->group &&
-		p1->is_group == p2->is_group;
+	return a->filename == b->filename && a->group == b->group;
 }
 
+/**
+ * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_number_union".
+ * @b: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static inline bool tomoyo_same_number_union
-(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b)
 {
-	return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
-		&& p1->group == p2->group && p1->min_type == p2->min_type &&
-		p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+	return a->values[0] == b->values[0] && a->values[1] == b->values[1] &&
+		a->group == b->group && a->value_type[0] == b->value_type[0] &&
+		a->value_type[1] == b->value_type[1];
 }
 
 /**
+ * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" for current thread.
+ */
+static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void)
+{
+	return tomoyo_domain()->ns;
+}
+
+#if defined(CONFIG_SLOB)
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns @size.
+ *
+ * Since SLOB does not round up, this function simply returns @size.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+	return size;
+}
+
+#else
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns rounded size.
+ *
+ * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of
+ * (e.g.) 128 bytes.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+#if PAGE_SIZE == 4096
+	size_t bsize = 32;
+#else
+	size_t bsize = 64;
+#endif
+	if (!size)
+		return 0;
+	while (size > bsize)
+		bsize <<= 1;
+	return bsize;
+}
+
+#endif
+
+/**
  * list_for_each_cookie - iterate over a list with cookie.
  * @pos:        the &struct list_head to use as a loop cursor.
  * @head:       the head for your list.
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
new file mode 100644
index 0000000..8a05f71
--- /dev/null
+++ b/security/tomoyo/condition.c
@@ -0,0 +1,1035 @@
+/*
+ * security/tomoyo/condition.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* List of "struct tomoyo_condition". */
+LIST_HEAD(tomoyo_condition_list);
+
+/**
+ * tomoyo_argv - Check argv[] in "struct linux_binbrm".
+ *
+ * @index:   Index number of @arg_ptr.
+ * @arg_ptr: Contents of argv[@index].
+ * @argc:    Length of @argv.
+ * @argv:    Pointer to "struct tomoyo_argv".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
+			const int argc, const struct tomoyo_argv *argv,
+			u8 *checked)
+{
+	int i;
+	struct tomoyo_path_info arg;
+	arg.name = arg_ptr;
+	for (i = 0; i < argc; argv++, checked++, i++) {
+		bool result;
+		if (index != argv->index)
+			continue;
+		*checked = 1;
+		tomoyo_fill_path_info(&arg);
+		result = tomoyo_path_matches_pattern(&arg, argv->value);
+		if (argv->is_not)
+			result = !result;
+		if (!result)
+			return false;
+	}
+	return true;
+}
+
+/**
+ * tomoyo_envp - Check envp[] in "struct linux_binbrm".
+ *
+ * @env_name:  The name of environment variable.
+ * @env_value: The value of environment variable.
+ * @envc:      Length of @envp.
+ * @envp:      Pointer to "struct tomoyo_envp".
+ * @checked:   Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_envp(const char *env_name, const char *env_value,
+			const int envc, const struct tomoyo_envp *envp,
+			u8 *checked)
+{
+	int i;
+	struct tomoyo_path_info name;
+	struct tomoyo_path_info value;
+	name.name = env_name;
+	tomoyo_fill_path_info(&name);
+	value.name = env_value;
+	tomoyo_fill_path_info(&value);
+	for (i = 0; i < envc; envp++, checked++, i++) {
+		bool result;
+		if (!tomoyo_path_matches_pattern(&name, envp->name))
+			continue;
+		*checked = 1;
+		if (envp->value) {
+			result = tomoyo_path_matches_pattern(&value,
+							     envp->value);
+			if (envp->is_not)
+				result = !result;
+		} else {
+			result = true;
+			if (!envp->is_not)
+				result = !result;
+		}
+		if (!result)
+			return false;
+	}
+	return true;
+}
+
+/**
+ * tomoyo_scan_bprm - Scan "struct linux_binprm".
+ *
+ * @ee:   Pointer to "struct tomoyo_execve".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct tomoyo_argv".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
+			     const u16 argc, const struct tomoyo_argv *argv,
+			     const u16 envc, const struct tomoyo_envp *envp)
+{
+	struct linux_binprm *bprm = ee->bprm;
+	struct tomoyo_page_dump *dump = &ee->dump;
+	char *arg_ptr = ee->tmp;
+	int arg_len = 0;
+	unsigned long pos = bprm->p;
+	int offset = pos % PAGE_SIZE;
+	int argv_count = bprm->argc;
+	int envp_count = bprm->envc;
+	bool result = true;
+	u8 local_checked[32];
+	u8 *checked;
+	if (argc + envc <= sizeof(local_checked)) {
+		checked = local_checked;
+		memset(local_checked, 0, sizeof(local_checked));
+	} else {
+		checked = kzalloc(argc + envc, GFP_NOFS);
+		if (!checked)
+			return false;
+	}
+	while (argv_count || envp_count) {
+		if (!tomoyo_dump_page(bprm, pos, dump)) {
+			result = false;
+			goto out;
+		}
+		pos += PAGE_SIZE - offset;
+		while (offset < PAGE_SIZE) {
+			/* Read. */
+			const char *kaddr = dump->data;
+			const unsigned char c = kaddr[offset++];
+			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+				if (c == '\\') {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = '\\';
+				} else if (c > ' ' && c < 127) {
+					arg_ptr[arg_len++] = c;
+				} else {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = (c >> 6) + '0';
+					arg_ptr[arg_len++] =
+						((c >> 3) & 7) + '0';
+					arg_ptr[arg_len++] = (c & 7) + '0';
+				}
+			} else {
+				arg_ptr[arg_len] = '\0';
+			}
+			if (c)
+				continue;
+			/* Check. */
+			if (argv_count) {
+				if (!tomoyo_argv(bprm->argc - argv_count,
+						 arg_ptr, argc, argv,
+						 checked)) {
+					result = false;
+					break;
+				}
+				argv_count--;
+			} else if (envp_count) {
+				char *cp = strchr(arg_ptr, '=');
+				if (cp) {
+					*cp = '\0';
+					if (!tomoyo_envp(arg_ptr, cp + 1,
+							 envc, envp,
+							 checked + argc)) {
+						result = false;
+						break;
+					}
+				}
+				envp_count--;
+			} else {
+				break;
+			}
+			arg_len = 0;
+		}
+		offset = 0;
+		if (!result)
+			break;
+	}
+out:
+	if (result) {
+		int i;
+		/* Check not-yet-checked entries. */
+		for (i = 0; i < argc; i++) {
+			if (checked[i])
+				continue;
+			/*
+			 * Return true only if all unchecked indexes in
+			 * bprm->argv[] are not matched.
+			 */
+			if (argv[i].is_not)
+				continue;
+			result = false;
+			break;
+		}
+		for (i = 0; i < envc; envp++, i++) {
+			if (checked[argc + i])
+				continue;
+			/*
+			 * Return true only if all unchecked environ variables
+			 * in bprm->envp[] are either undefined or not matched.
+			 */
+			if ((!envp->value && !envp->is_not) ||
+			    (envp->value && envp->is_not))
+				continue;
+			result = false;
+			break;
+		}
+	}
+	if (checked != local_checked)
+		kfree(checked);
+	return result;
+}
+
+/**
+ * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
+ *
+ * @file:  Pointer to "struct file".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ * @match: True if "exec.realpath=", false if "exec.realpath!=".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_exec_realpath(struct file *file,
+				      const struct tomoyo_name_union *ptr,
+				      const bool match)
+{
+	bool result;
+	struct tomoyo_path_info exe;
+	if (!file)
+		return false;
+	exe.name = tomoyo_realpath_from_path(&file->f_path);
+	if (!exe.name)
+		return false;
+	tomoyo_fill_path_info(&exe);
+	result = tomoyo_compare_name_union(&exe, ptr);
+	kfree(exe.name);
+	return result == match;
+}
+
+/**
+ * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
+{
+	char *cp = start + strlen(start) - 1;
+	if (cp == start || *start++ != '"' || *cp != '"')
+		return NULL;
+	*cp = '\0';
+	if (*start && !tomoyo_correct_word(start))
+		return NULL;
+	return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_name_union_quoted - Parse a quoted word.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
+					   struct tomoyo_name_union *ptr)
+{
+	char *filename = param->data;
+	if (*filename == '@')
+		return tomoyo_parse_name_union(param, ptr);
+	ptr->filename = tomoyo_get_dqword(filename);
+	return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_parse_argv - Parse an argv[] condition part.
+ *
+ * @left:  Lefthand value.
+ * @right: Righthand value.
+ * @argv:  Pointer to "struct tomoyo_argv".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_argv(char *left, char *right,
+			      struct tomoyo_argv *argv)
+{
+	if (tomoyo_parse_ulong(&argv->index, &left) !=
+	    TOMOYO_VALUE_TYPE_DECIMAL || *left++ != ']' || *left)
+		return false;
+	argv->value = tomoyo_get_dqword(right);
+	return argv->value != NULL;
+}
+
+/**
+ * tomoyo_parse_envp - Parse an envp[] condition part.
+ *
+ * @left:  Lefthand value.
+ * @right: Righthand value.
+ * @envp:  Pointer to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_envp(char *left, char *right,
+			      struct tomoyo_envp *envp)
+{
+	const struct tomoyo_path_info *name;
+	const struct tomoyo_path_info *value;
+	char *cp = left + strlen(left) - 1;
+	if (*cp-- != ']' || *cp != '"')
+		goto out;
+	*cp = '\0';
+	if (!tomoyo_correct_word(left))
+		goto out;
+	name = tomoyo_get_name(left);
+	if (!name)
+		goto out;
+	if (!strcmp(right, "NULL")) {
+		value = NULL;
+	} else {
+		value = tomoyo_get_dqword(right);
+		if (!value) {
+			tomoyo_put_name(name);
+			goto out;
+		}
+	}
+	envp->name = name;
+	envp->value = value;
+	return true;
+out:
+	return false;
+}
+
+/**
+ * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
+ *
+ * @a: Pointer to "struct tomoyo_condition".
+ * @b: Pointer to "struct tomoyo_condition".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
+					 const struct tomoyo_condition *b)
+{
+	return a->size == b->size && a->condc == b->condc &&
+		a->numbers_count == b->numbers_count &&
+		a->names_count == b->names_count &&
+		a->argc == b->argc && a->envc == b->envc &&
+		!memcmp(a + 1, b + 1, a->size - sizeof(*a));
+}
+
+/**
+ * tomoyo_condition_type - Get condition type.
+ *
+ * @word: Keyword string.
+ *
+ * Returns one of values in "enum tomoyo_conditions_index" on success,
+ * TOMOYO_MAX_CONDITION_KEYWORD otherwise.
+ */
+static u8 tomoyo_condition_type(const char *word)
+{
+	u8 i;
+	for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) {
+		if (!strcmp(word, tomoyo_condition_keyword[i]))
+			break;
+	}
+	return i;
+}
+
+/* Define this to enable debug mode. */
+/* #define DEBUG_CONDITION */
+
+#ifdef DEBUG_CONDITION
+#define dprintk printk
+#else
+#define dprintk(...) do { } while (0)
+#endif
+
+/**
+ * tomoyo_commit_condition - Commit "struct tomoyo_condition".
+ *
+ * @entry: Pointer to "struct tomoyo_condition".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ *
+ * This function merges duplicated entries. This function returns NULL if
+ * @entry is not duplicated but memory quota for policy has exceeded.
+ */
+static struct tomoyo_condition *tomoyo_commit_condition
+(struct tomoyo_condition *entry)
+{
+	struct tomoyo_condition *ptr;
+	bool found = false;
+	if (mutex_lock_interruptible(&tomoyo_policy_lock)) {
+		dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+		ptr = NULL;
+		found = true;
+		goto out;
+	}
+	list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
+		if (!tomoyo_same_condition(ptr, entry))
+			continue;
+		/* Same entry found. Share this entry. */
+		atomic_inc(&ptr->head.users);
+		found = true;
+		break;
+	}
+	if (!found) {
+		if (tomoyo_memory_ok(entry)) {
+			atomic_set(&entry->head.users, 1);
+			list_add_rcu(&entry->head.list,
+				     &tomoyo_condition_list);
+		} else {
+			found = true;
+			ptr = NULL;
+		}
+	}
+	mutex_unlock(&tomoyo_policy_lock);
+out:
+	if (found) {
+		tomoyo_del_condition(&entry->head.list);
+		kfree(entry);
+		entry = ptr;
+	}
+	return entry;
+}
+
+/**
+ * tomoyo_get_condition - Parse condition part.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_condition *entry = NULL;
+	struct tomoyo_condition_element *condp = NULL;
+	struct tomoyo_number_union *numbers_p = NULL;
+	struct tomoyo_name_union *names_p = NULL;
+	struct tomoyo_argv *argv = NULL;
+	struct tomoyo_envp *envp = NULL;
+	struct tomoyo_condition e = { };
+	char * const start_of_string = param->data;
+	char * const end_of_string = start_of_string + strlen(start_of_string);
+	char *pos;
+rerun:
+	pos = start_of_string;
+	while (1) {
+		u8 left = -1;
+		u8 right = -1;
+		char *left_word = pos;
+		char *cp;
+		char *right_word;
+		bool is_not;
+		if (!*left_word)
+			break;
+		/*
+		 * Since left-hand condition does not allow use of "path_group"
+		 * or "number_group" and environment variable's names do not
+		 * accept '=', it is guaranteed that the original line consists
+		 * of one or more repetition of $left$operator$right blocks
+		 * where "$left is free from '=' and ' '" and "$operator is
+		 * either '=' or '!='" and "$right is free from ' '".
+		 * Therefore, we can reconstruct the original line at the end
+		 * of dry run even if we overwrite $operator with '\0'.
+		 */
+		cp = strchr(pos, ' ');
+		if (cp) {
+			*cp = '\0'; /* Will restore later. */
+			pos = cp + 1;
+		} else {
+			pos = "";
+		}
+		right_word = strchr(left_word, '=');
+		if (!right_word || right_word == left_word)
+			goto out;
+		is_not = *(right_word - 1) == '!';
+		if (is_not)
+			*(right_word++ - 1) = '\0'; /* Will restore later. */
+		else if (*(right_word + 1) != '=')
+			*right_word++ = '\0'; /* Will restore later. */
+		else
+			goto out;
+		dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
+			is_not ? "!" : "", right_word);
+		if (!strncmp(left_word, "exec.argv[", 10)) {
+			if (!argv) {
+				e.argc++;
+				e.condc++;
+			} else {
+				e.argc--;
+				e.condc--;
+				left = TOMOYO_ARGV_ENTRY;
+				argv->is_not = is_not;
+				if (!tomoyo_parse_argv(left_word + 10,
+						       right_word, argv++))
+					goto out;
+			}
+			goto store_value;
+		}
+		if (!strncmp(left_word, "exec.envp[\"", 11)) {
+			if (!envp) {
+				e.envc++;
+				e.condc++;
+			} else {
+				e.envc--;
+				e.condc--;
+				left = TOMOYO_ENVP_ENTRY;
+				envp->is_not = is_not;
+				if (!tomoyo_parse_envp(left_word + 11,
+						       right_word, envp++))
+					goto out;
+			}
+			goto store_value;
+		}
+		left = tomoyo_condition_type(left_word);
+		dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word,
+			left);
+		if (left == TOMOYO_MAX_CONDITION_KEYWORD) {
+			if (!numbers_p) {
+				e.numbers_count++;
+			} else {
+				e.numbers_count--;
+				left = TOMOYO_NUMBER_UNION;
+				param->data = left_word;
+				if (*left_word == '@' ||
+				    !tomoyo_parse_number_union(param,
+							       numbers_p++))
+					goto out;
+			}
+		}
+		if (!condp)
+			e.condc++;
+		else
+			e.condc--;
+		if (left == TOMOYO_EXEC_REALPATH ||
+		    left == TOMOYO_SYMLINK_TARGET) {
+			if (!names_p) {
+				e.names_count++;
+			} else {
+				e.names_count--;
+				right = TOMOYO_NAME_UNION;
+				param->data = right_word;
+				if (!tomoyo_parse_name_union_quoted(param,
+								    names_p++))
+					goto out;
+			}
+			goto store_value;
+		}
+		right = tomoyo_condition_type(right_word);
+		if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
+			if (!numbers_p) {
+				e.numbers_count++;
+			} else {
+				e.numbers_count--;
+				right = TOMOYO_NUMBER_UNION;
+				param->data = right_word;
+				if (!tomoyo_parse_number_union(param,
+							       numbers_p++))
+					goto out;
+			}
+		}
+store_value:
+		if (!condp) {
+			dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
+				"match=%u\n", __LINE__, left, right, !is_not);
+			continue;
+		}
+		condp->left = left;
+		condp->right = right;
+		condp->equals = !is_not;
+		dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n",
+			__LINE__, condp->left, condp->right,
+			condp->equals);
+		condp++;
+	}
+	dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n",
+		__LINE__, e.condc, e.numbers_count, e.names_count, e.argc,
+		e.envc);
+	if (entry) {
+		BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc |
+		       e.condc);
+		return tomoyo_commit_condition(entry);
+	}
+	e.size = sizeof(*entry)
+		+ e.condc * sizeof(struct tomoyo_condition_element)
+		+ e.numbers_count * sizeof(struct tomoyo_number_union)
+		+ e.names_count * sizeof(struct tomoyo_name_union)
+		+ e.argc * sizeof(struct tomoyo_argv)
+		+ e.envc * sizeof(struct tomoyo_envp);
+	entry = kzalloc(e.size, GFP_NOFS);
+	if (!entry)
+		return NULL;
+	*entry = e;
+	condp = (struct tomoyo_condition_element *) (entry + 1);
+	numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
+	names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
+	argv = (struct tomoyo_argv *) (names_p + e.names_count);
+	envp = (struct tomoyo_envp *) (argv + e.argc);
+	{
+		bool flag = false;
+		for (pos = start_of_string; pos < end_of_string; pos++) {
+			if (*pos)
+				continue;
+			if (flag) /* Restore " ". */
+				*pos = ' ';
+			else if (*(pos + 1) == '=') /* Restore "!=". */
+				*pos = '!';
+			else /* Restore "=". */
+				*pos = '=';
+			flag = !flag;
+		}
+	}
+	goto rerun;
+out:
+	dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+	if (entry) {
+		tomoyo_del_condition(&entry->head.list);
+		kfree(entry);
+	}
+	return NULL;
+}
+
+/**
+ * tomoyo_get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct tomoyo_obj_info".
+ *
+ * Returns nothing.
+ */
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
+{
+	u8 i;
+	struct dentry *dentry = NULL;
+
+	for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+		struct inode *inode;
+		switch (i) {
+		case TOMOYO_PATH1:
+			dentry = obj->path1.dentry;
+			if (!dentry)
+				continue;
+			break;
+		case TOMOYO_PATH2:
+			dentry = obj->path2.dentry;
+			if (!dentry)
+				continue;
+			break;
+		default:
+			if (!dentry)
+				continue;
+			dentry = dget_parent(dentry);
+			break;
+		}
+		inode = dentry->d_inode;
+		if (inode) {
+			struct tomoyo_mini_stat *stat = &obj->stat[i];
+			stat->uid  = inode->i_uid;
+			stat->gid  = inode->i_gid;
+			stat->ino  = inode->i_ino;
+			stat->mode = inode->i_mode;
+			stat->dev  = inode->i_sb->s_dev;
+			stat->rdev = inode->i_rdev;
+			obj->stat_valid[i] = true;
+		}
+		if (i & 1) /* i == TOMOYO_PATH1_PARENT ||
+			      i == TOMOYO_PATH2_PARENT */
+			dput(dentry);
+	}
+}
+
+/**
+ * tomoyo_condition - Check condition part.
+ *
+ * @r:    Pointer to "struct tomoyo_request_info".
+ * @cond: Pointer to "struct tomoyo_condition". Maybe NULL.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+		      const struct tomoyo_condition *cond)
+{
+	u32 i;
+	unsigned long min_v[2] = { 0, 0 };
+	unsigned long max_v[2] = { 0, 0 };
+	const struct tomoyo_condition_element *condp;
+	const struct tomoyo_number_union *numbers_p;
+	const struct tomoyo_name_union *names_p;
+	const struct tomoyo_argv *argv;
+	const struct tomoyo_envp *envp;
+	struct tomoyo_obj_info *obj;
+	u16 condc;
+	u16 argc;
+	u16 envc;
+	struct linux_binprm *bprm = NULL;
+	if (!cond)
+		return true;
+	condc = cond->condc;
+	argc = cond->argc;
+	envc = cond->envc;
+	obj = r->obj;
+	if (r->ee)
+		bprm = r->ee->bprm;
+	if (!bprm && (argc || envc))
+		return false;
+	condp = (struct tomoyo_condition_element *) (cond + 1);
+	numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+	names_p = (const struct tomoyo_name_union *)
+		(numbers_p + cond->numbers_count);
+	argv = (const struct tomoyo_argv *) (names_p + cond->names_count);
+	envp = (const struct tomoyo_envp *) (argv + argc);
+	for (i = 0; i < condc; i++) {
+		const bool match = condp->equals;
+		const u8 left = condp->left;
+		const u8 right = condp->right;
+		bool is_bitop[2] = { false, false };
+		u8 j;
+		condp++;
+		/* Check argv[] and envp[] later. */
+		if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
+			continue;
+		/* Check string expressions. */
+		if (right == TOMOYO_NAME_UNION) {
+			const struct tomoyo_name_union *ptr = names_p++;
+			switch (left) {
+				struct tomoyo_path_info *symlink;
+				struct tomoyo_execve *ee;
+				struct file *file;
+			case TOMOYO_SYMLINK_TARGET:
+				symlink = obj ? obj->symlink_target : NULL;
+				if (!symlink ||
+				    !tomoyo_compare_name_union(symlink, ptr)
+				    == match)
+					goto out;
+				break;
+			case TOMOYO_EXEC_REALPATH:
+				ee = r->ee;
+				file = ee ? ee->bprm->file : NULL;
+				if (!tomoyo_scan_exec_realpath(file, ptr,
+							       match))
+					goto out;
+				break;
+			}
+			continue;
+		}
+		/* Check numeric or bit-op expressions. */
+		for (j = 0; j < 2; j++) {
+			const u8 index = j ? right : left;
+			unsigned long value = 0;
+			switch (index) {
+			case TOMOYO_TASK_UID:
+				value = current_uid();
+				break;
+			case TOMOYO_TASK_EUID:
+				value = current_euid();
+				break;
+			case TOMOYO_TASK_SUID:
+				value = current_suid();
+				break;
+			case TOMOYO_TASK_FSUID:
+				value = current_fsuid();
+				break;
+			case TOMOYO_TASK_GID:
+				value = current_gid();
+				break;
+			case TOMOYO_TASK_EGID:
+				value = current_egid();
+				break;
+			case TOMOYO_TASK_SGID:
+				value = current_sgid();
+				break;
+			case TOMOYO_TASK_FSGID:
+				value = current_fsgid();
+				break;
+			case TOMOYO_TASK_PID:
+				value = tomoyo_sys_getpid();
+				break;
+			case TOMOYO_TASK_PPID:
+				value = tomoyo_sys_getppid();
+				break;
+			case TOMOYO_TYPE_IS_SOCKET:
+				value = S_IFSOCK;
+				break;
+			case TOMOYO_TYPE_IS_SYMLINK:
+				value = S_IFLNK;
+				break;
+			case TOMOYO_TYPE_IS_FILE:
+				value = S_IFREG;
+				break;
+			case TOMOYO_TYPE_IS_BLOCK_DEV:
+				value = S_IFBLK;
+				break;
+			case TOMOYO_TYPE_IS_DIRECTORY:
+				value = S_IFDIR;
+				break;
+			case TOMOYO_TYPE_IS_CHAR_DEV:
+				value = S_IFCHR;
+				break;
+			case TOMOYO_TYPE_IS_FIFO:
+				value = S_IFIFO;
+				break;
+			case TOMOYO_MODE_SETUID:
+				value = S_ISUID;
+				break;
+			case TOMOYO_MODE_SETGID:
+				value = S_ISGID;
+				break;
+			case TOMOYO_MODE_STICKY:
+				value = S_ISVTX;
+				break;
+			case TOMOYO_MODE_OWNER_READ:
+				value = S_IRUSR;
+				break;
+			case TOMOYO_MODE_OWNER_WRITE:
+				value = S_IWUSR;
+				break;
+			case TOMOYO_MODE_OWNER_EXECUTE:
+				value = S_IXUSR;
+				break;
+			case TOMOYO_MODE_GROUP_READ:
+				value = S_IRGRP;
+				break;
+			case TOMOYO_MODE_GROUP_WRITE:
+				value = S_IWGRP;
+				break;
+			case TOMOYO_MODE_GROUP_EXECUTE:
+				value = S_IXGRP;
+				break;
+			case TOMOYO_MODE_OTHERS_READ:
+				value = S_IROTH;
+				break;
+			case TOMOYO_MODE_OTHERS_WRITE:
+				value = S_IWOTH;
+				break;
+			case TOMOYO_MODE_OTHERS_EXECUTE:
+				value = S_IXOTH;
+				break;
+			case TOMOYO_EXEC_ARGC:
+				if (!bprm)
+					goto out;
+				value = bprm->argc;
+				break;
+			case TOMOYO_EXEC_ENVC:
+				if (!bprm)
+					goto out;
+				value = bprm->envc;
+				break;
+			case TOMOYO_NUMBER_UNION:
+				/* Fetch values later. */
+				break;
+			default:
+				if (!obj)
+					goto out;
+				if (!obj->validate_done) {
+					tomoyo_get_attributes(obj);
+					obj->validate_done = true;
+				}
+				{
+					u8 stat_index;
+					struct tomoyo_mini_stat *stat;
+					switch (index) {
+					case TOMOYO_PATH1_UID:
+					case TOMOYO_PATH1_GID:
+					case TOMOYO_PATH1_INO:
+					case TOMOYO_PATH1_MAJOR:
+					case TOMOYO_PATH1_MINOR:
+					case TOMOYO_PATH1_TYPE:
+					case TOMOYO_PATH1_DEV_MAJOR:
+					case TOMOYO_PATH1_DEV_MINOR:
+					case TOMOYO_PATH1_PERM:
+						stat_index = TOMOYO_PATH1;
+						break;
+					case TOMOYO_PATH2_UID:
+					case TOMOYO_PATH2_GID:
+					case TOMOYO_PATH2_INO:
+					case TOMOYO_PATH2_MAJOR:
+					case TOMOYO_PATH2_MINOR:
+					case TOMOYO_PATH2_TYPE:
+					case TOMOYO_PATH2_DEV_MAJOR:
+					case TOMOYO_PATH2_DEV_MINOR:
+					case TOMOYO_PATH2_PERM:
+						stat_index = TOMOYO_PATH2;
+						break;
+					case TOMOYO_PATH1_PARENT_UID:
+					case TOMOYO_PATH1_PARENT_GID:
+					case TOMOYO_PATH1_PARENT_INO:
+					case TOMOYO_PATH1_PARENT_PERM:
+						stat_index =
+							TOMOYO_PATH1_PARENT;
+						break;
+					case TOMOYO_PATH2_PARENT_UID:
+					case TOMOYO_PATH2_PARENT_GID:
+					case TOMOYO_PATH2_PARENT_INO:
+					case TOMOYO_PATH2_PARENT_PERM:
+						stat_index =
+							TOMOYO_PATH2_PARENT;
+						break;
+					default:
+						goto out;
+					}
+					if (!obj->stat_valid[stat_index])
+						goto out;
+					stat = &obj->stat[stat_index];
+					switch (index) {
+					case TOMOYO_PATH1_UID:
+					case TOMOYO_PATH2_UID:
+					case TOMOYO_PATH1_PARENT_UID:
+					case TOMOYO_PATH2_PARENT_UID:
+						value = stat->uid;
+						break;
+					case TOMOYO_PATH1_GID:
+					case TOMOYO_PATH2_GID:
+					case TOMOYO_PATH1_PARENT_GID:
+					case TOMOYO_PATH2_PARENT_GID:
+						value = stat->gid;
+						break;
+					case TOMOYO_PATH1_INO:
+					case TOMOYO_PATH2_INO:
+					case TOMOYO_PATH1_PARENT_INO:
+					case TOMOYO_PATH2_PARENT_INO:
+						value = stat->ino;
+						break;
+					case TOMOYO_PATH1_MAJOR:
+					case TOMOYO_PATH2_MAJOR:
+						value = MAJOR(stat->dev);
+						break;
+					case TOMOYO_PATH1_MINOR:
+					case TOMOYO_PATH2_MINOR:
+						value = MINOR(stat->dev);
+						break;
+					case TOMOYO_PATH1_TYPE:
+					case TOMOYO_PATH2_TYPE:
+						value = stat->mode & S_IFMT;
+						break;
+					case TOMOYO_PATH1_DEV_MAJOR:
+					case TOMOYO_PATH2_DEV_MAJOR:
+						value = MAJOR(stat->rdev);
+						break;
+					case TOMOYO_PATH1_DEV_MINOR:
+					case TOMOYO_PATH2_DEV_MINOR:
+						value = MINOR(stat->rdev);
+						break;
+					case TOMOYO_PATH1_PERM:
+					case TOMOYO_PATH2_PERM:
+					case TOMOYO_PATH1_PARENT_PERM:
+					case TOMOYO_PATH2_PARENT_PERM:
+						value = stat->mode & S_IALLUGO;
+						break;
+					}
+				}
+				break;
+			}
+			max_v[j] = value;
+			min_v[j] = value;
+			switch (index) {
+			case TOMOYO_MODE_SETUID:
+			case TOMOYO_MODE_SETGID:
+			case TOMOYO_MODE_STICKY:
+			case TOMOYO_MODE_OWNER_READ:
+			case TOMOYO_MODE_OWNER_WRITE:
+			case TOMOYO_MODE_OWNER_EXECUTE:
+			case TOMOYO_MODE_GROUP_READ:
+			case TOMOYO_MODE_GROUP_WRITE:
+			case TOMOYO_MODE_GROUP_EXECUTE:
+			case TOMOYO_MODE_OTHERS_READ:
+			case TOMOYO_MODE_OTHERS_WRITE:
+			case TOMOYO_MODE_OTHERS_EXECUTE:
+				is_bitop[j] = true;
+			}
+		}
+		if (left == TOMOYO_NUMBER_UNION) {
+			/* Fetch values now. */
+			const struct tomoyo_number_union *ptr = numbers_p++;
+			min_v[0] = ptr->values[0];
+			max_v[0] = ptr->values[1];
+		}
+		if (right == TOMOYO_NUMBER_UNION) {
+			/* Fetch values now. */
+			const struct tomoyo_number_union *ptr = numbers_p++;
+			if (ptr->group) {
+				if (tomoyo_number_matches_group(min_v[0],
+								max_v[0],
+								ptr->group)
+				    == match)
+					continue;
+			} else {
+				if ((min_v[0] <= ptr->values[1] &&
+				     max_v[0] >= ptr->values[0]) == match)
+					continue;
+			}
+			goto out;
+		}
+		/*
+		 * Bit operation is valid only when counterpart value
+		 * represents permission.
+		 */
+		if (is_bitop[0] && is_bitop[1]) {
+			goto out;
+		} else if (is_bitop[0]) {
+			switch (right) {
+			case TOMOYO_PATH1_PERM:
+			case TOMOYO_PATH1_PARENT_PERM:
+			case TOMOYO_PATH2_PERM:
+			case TOMOYO_PATH2_PARENT_PERM:
+				if (!(max_v[0] & max_v[1]) == !match)
+					continue;
+			}
+			goto out;
+		} else if (is_bitop[1]) {
+			switch (left) {
+			case TOMOYO_PATH1_PERM:
+			case TOMOYO_PATH1_PARENT_PERM:
+			case TOMOYO_PATH2_PERM:
+			case TOMOYO_PATH2_PARENT_PERM:
+				if (!(max_v[0] & max_v[1]) == !match)
+					continue;
+			}
+			goto out;
+		}
+		/* Normal value range comparison. */
+		if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match)
+			continue;
+out:
+		return false;
+	}
+	/* Check argv[] and envp[] now. */
+	if (r->ee && (argc || envc))
+		return tomoyo_scan_bprm(r->ee, argc, argv, envc, envp);
+	return true;
+}
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 3538840..cd0f92d 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/domain.c
  *
- * Domain transition functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include "common.h"
@@ -20,8 +18,7 @@
  *
  * @new_entry:       Pointer to "struct tomoyo_acl_info".
  * @size:            Size of @new_entry in bytes.
- * @is_delete:       True if it is a delete request.
- * @list:            Pointer to "struct list_head".
+ * @param:           Pointer to "struct tomoyo_acl_param".
  * @check_duplicate: Callback function to find duplicated entry.
  *
  * Returns 0 on success, negative value otherwise.
@@ -29,25 +26,26 @@
  * Caller holds tomoyo_read_lock().
  */
 int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
-			 bool is_delete, struct list_head *list,
+			 struct tomoyo_acl_param *param,
 			 bool (*check_duplicate) (const struct tomoyo_acl_head
 						  *,
 						  const struct tomoyo_acl_head
 						  *))
 {
-	int error = is_delete ? -ENOENT : -ENOMEM;
+	int error = param->is_delete ? -ENOENT : -ENOMEM;
 	struct tomoyo_acl_head *entry;
+	struct list_head *list = param->list;
 
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return -ENOMEM;
 	list_for_each_entry_rcu(entry, list, list) {
 		if (!check_duplicate(entry, new_entry))
 			continue;
-		entry->is_deleted = is_delete;
+		entry->is_deleted = param->is_delete;
 		error = 0;
 		break;
 	}
-	if (error && !is_delete) {
+	if (error && !param->is_delete) {
 		entry = tomoyo_commit_ok(new_entry, size);
 		if (entry) {
 			list_add_tail_rcu(&entry->list, list);
@@ -59,12 +57,25 @@
 }
 
 /**
+ * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
+					const struct tomoyo_acl_info *b)
+{
+	return a->type == b->type && a->cond == b->cond;
+}
+
+/**
  * tomoyo_update_domain - Update an entry for domain policy.
  *
  * @new_entry:       Pointer to "struct tomoyo_acl_info".
  * @size:            Size of @new_entry in bytes.
- * @is_delete:       True if it is a delete request.
- * @domain:          Pointer to "struct tomoyo_domain_info".
+ * @param:           Pointer to "struct tomoyo_acl_param".
  * @check_duplicate: Callback function to find duplicated entry.
  * @merge_duplicate: Callback function to merge duplicated entry.
  *
@@ -73,7 +84,7 @@
  * Caller holds tomoyo_read_lock().
  */
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
-			 bool is_delete, struct tomoyo_domain_info *domain,
+			 struct tomoyo_acl_param *param,
 			 bool (*check_duplicate) (const struct tomoyo_acl_info
 						  *,
 						  const struct tomoyo_acl_info
@@ -82,13 +93,21 @@
 						  struct tomoyo_acl_info *,
 						  const bool))
 {
+	const bool is_delete = param->is_delete;
 	int error = is_delete ? -ENOENT : -ENOMEM;
 	struct tomoyo_acl_info *entry;
+	struct list_head * const list = param->list;
 
+	if (param->data[0]) {
+		new_entry->cond = tomoyo_get_condition(param);
+		if (!new_entry->cond)
+			return -EINVAL;
+	}
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
-		return error;
-	list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
-		if (!check_duplicate(entry, new_entry))
+		goto out;
+	list_for_each_entry_rcu(entry, list, list) {
+		if (!tomoyo_same_acl_head(entry, new_entry) ||
+		    !check_duplicate(entry, new_entry))
 			continue;
 		if (merge_duplicate)
 			entry->is_deleted = merge_duplicate(entry, new_entry,
@@ -101,28 +120,50 @@
 	if (error && !is_delete) {
 		entry = tomoyo_commit_ok(new_entry, size);
 		if (entry) {
-			list_add_tail_rcu(&entry->list, &domain->acl_info_list);
+			list_add_tail_rcu(&entry->list, list);
 			error = 0;
 		}
 	}
 	mutex_unlock(&tomoyo_policy_lock);
+out:
+	tomoyo_put_condition(new_entry->cond);
 	return error;
 }
 
+/**
+ * tomoyo_check_acl - Do permission check.
+ *
+ * @r:           Pointer to "struct tomoyo_request_info".
+ * @check_entry: Callback function to check type specific parameters.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
 void tomoyo_check_acl(struct tomoyo_request_info *r,
 		      bool (*check_entry) (struct tomoyo_request_info *,
 					   const struct tomoyo_acl_info *))
 {
 	const struct tomoyo_domain_info *domain = r->domain;
 	struct tomoyo_acl_info *ptr;
+	bool retried = false;
+	const struct list_head *list = &domain->acl_info_list;
 
-	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+retry:
+	list_for_each_entry_rcu(ptr, list, list) {
 		if (ptr->is_deleted || ptr->type != r->param_type)
 			continue;
-		if (check_entry(r, ptr)) {
-			r->granted = true;
-			return;
-		}
+		if (!check_entry(r, ptr))
+			continue;
+		if (!tomoyo_condition(r, ptr->cond))
+			continue;
+		r->granted = true;
+		return;
+	}
+	if (!retried) {
+		retried = true;
+		list = &domain->ns->acl_group[domain->group];
+		goto retry;
 	}
 	r->granted = false;
 }
@@ -130,24 +171,29 @@
 /* The list for "struct tomoyo_domain_info". */
 LIST_HEAD(tomoyo_domain_list);
 
-struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
-
 /**
  * tomoyo_last_word - Get last component of a domainname.
  *
- * @domainname: Domainname to check.
+ * @name: Domainname to check.
  *
  * Returns the last word of @domainname.
  */
 static const char *tomoyo_last_word(const char *name)
 {
-        const char *cp = strrchr(name, ' ');
-        if (cp)
-                return cp + 1;
-        return name;
+	const char *cp = strrchr(name, ' ');
+	if (cp)
+		return cp + 1;
+	return name;
 }
 
+/**
+ * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
 					   const struct tomoyo_acl_head *b)
 {
@@ -163,30 +209,36 @@
 }
 
 /**
- * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
+ * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
  *
- * @domainname: The name of domain. Maybe NULL.
- * @program:    The name of program. Maybe NULL.
- * @type:       Type of transition.
- * @is_delete:  True if it is a delete request.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @type:  Type of this entry.
  *
  * Returns 0 on success, negative value otherwise.
  */
-static int tomoyo_update_transition_control_entry(const char *domainname,
-						  const char *program,
-						  const u8 type,
-						  const bool is_delete)
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
+				    const u8 type)
 {
 	struct tomoyo_transition_control e = { .type = type };
-	int error = is_delete ? -ENOENT : -ENOMEM;
-	if (program) {
+	int error = param->is_delete ? -ENOENT : -ENOMEM;
+	char *program = param->data;
+	char *domainname = strstr(program, " from ");
+	if (domainname) {
+		*domainname = '\0';
+		domainname += 6;
+	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
+		   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
+		domainname = program;
+		program = NULL;
+	}
+	if (program && strcmp(program, "any")) {
 		if (!tomoyo_correct_path(program))
 			return -EINVAL;
 		e.program = tomoyo_get_name(program);
 		if (!e.program)
 			goto out;
 	}
-	if (domainname) {
+	if (domainname && strcmp(domainname, "any")) {
 		if (!tomoyo_correct_domain(domainname)) {
 			if (!tomoyo_correct_path(domainname))
 				goto out;
@@ -196,126 +248,136 @@
 		if (!e.domainname)
 			goto out;
 	}
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list
-				     [TOMOYO_ID_TRANSITION_CONTROL],
+	param->list = &param->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
+	error = tomoyo_update_policy(&e.head, sizeof(e), param,
 				     tomoyo_same_transition_control);
- out:
+out:
 	tomoyo_put_name(e.domainname);
 	tomoyo_put_name(e.program);
 	return error;
 }
 
 /**
- * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
+ * tomoyo_scan_transition - Try to find specific domain transition type.
  *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
- * @type:      Type of this entry.
+ * @list:       Pointer to "struct list_head".
+ * @domainname: The name of current domain.
+ * @program:    The name of requested program.
+ * @last_name:  The last component of @domainname.
+ * @type:       One of values in "enum tomoyo_transition_type".
  *
- * Returns 0 on success, negative value otherwise.
+ * Returns true if found one, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_transition_control(char *data, const bool is_delete,
-				    const u8 type)
+static inline bool tomoyo_scan_transition
+(const struct list_head *list, const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program, const char *last_name,
+ const enum tomoyo_transition_type type)
 {
-	char *domainname = strstr(data, " from ");
-	if (domainname) {
-		*domainname = '\0';
-		domainname += 6;
-	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
-		   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
-		domainname = data;
-		data = NULL;
+	const struct tomoyo_transition_control *ptr;
+	list_for_each_entry_rcu(ptr, list, head.list) {
+		if (ptr->head.is_deleted || ptr->type != type)
+			continue;
+		if (ptr->domainname) {
+			if (!ptr->is_last_name) {
+				if (ptr->domainname != domainname)
+					continue;
+			} else {
+				/*
+				 * Use direct strcmp() since this is
+				 * unlikely used.
+				 */
+				if (strcmp(ptr->domainname->name, last_name))
+					continue;
+			}
+		}
+		if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+			continue;
+		return true;
 	}
-	return tomoyo_update_transition_control_entry(domainname, data, type,
-						      is_delete);
+	return false;
 }
 
 /**
  * tomoyo_transition_type - Get domain transition type.
  *
- * @domainname: The name of domain.
- * @program:    The name of program.
+ * @ns:         Pointer to "struct tomoyo_policy_namespace".
+ * @domainname: The name of current domain.
+ * @program:    The name of requested program.
  *
- * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
- * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
- * @program suppresses domain transition, others otherwise.
+ * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes
+ * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if
+ * executing @program reinitializes domain transition within that namespace,
+ * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname ,
+ * others otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
-				 const struct tomoyo_path_info *program)
+static enum tomoyo_transition_type tomoyo_transition_type
+(const struct tomoyo_policy_namespace *ns,
+ const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program)
 {
-	const struct tomoyo_transition_control *ptr;
 	const char *last_name = tomoyo_last_word(domainname->name);
-	u8 type;
-	for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
- next:
-		list_for_each_entry_rcu(ptr, &tomoyo_policy_list
-					[TOMOYO_ID_TRANSITION_CONTROL],
-					head.list) {
-			if (ptr->head.is_deleted || ptr->type != type)
-				continue;
-			if (ptr->domainname) {
-				if (!ptr->is_last_name) {
-					if (ptr->domainname != domainname)
-						continue;
-				} else {
-					/*
-					 * Use direct strcmp() since this is
-					 * unlikely used.
-					 */
-					if (strcmp(ptr->domainname->name,
-						   last_name))
-						continue;
-				}
-			}
-			if (ptr->program &&
-			    tomoyo_pathcmp(ptr->program, program))
-				continue;
-			if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
-				/*
-				 * Do not check for initialize_domain if
-				 * no_initialize_domain matched.
-				 */
-				type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
-				goto next;
-			}
-			goto done;
+	enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
+	while (type < TOMOYO_MAX_TRANSITION_TYPE) {
+		const struct list_head * const list =
+			&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
+		if (!tomoyo_scan_transition(list, domainname, program,
+					    last_name, type)) {
+			type++;
+			continue;
 		}
+		if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET &&
+		    type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE)
+			break;
+		/*
+		 * Do not check for reset_domain if no_reset_domain matched.
+		 * Do not check for initialize_domain if no_initialize_domain
+		 * matched.
+		 */
+		type++;
+		type++;
 	}
- done:
 	return type;
 }
 
+/**
+ * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
 				   const struct tomoyo_acl_head *b)
 {
-	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
-	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
+	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
+							  head);
+	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
+							  head);
 	return p1->original_name == p2->original_name &&
 		p1->aggregated_name == p2->aggregated_name;
 }
 
 /**
- * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
+ * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
  *
- * @original_name:   The original program's name.
- * @aggregated_name: The program name to use.
- * @is_delete:       True if it is a delete request.
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_update_aggregator_entry(const char *original_name,
-					  const char *aggregated_name,
-					  const bool is_delete)
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
 {
 	struct tomoyo_aggregator e = { };
-	int error = is_delete ? -ENOENT : -ENOMEM;
-
-	if (!tomoyo_correct_path(original_name) ||
+	int error = param->is_delete ? -ENOENT : -ENOMEM;
+	const char *original_name = tomoyo_read_token(param);
+	const char *aggregated_name = tomoyo_read_token(param);
+	if (!tomoyo_correct_word(original_name) ||
 	    !tomoyo_correct_path(aggregated_name))
 		return -EINVAL;
 	e.original_name = tomoyo_get_name(original_name);
@@ -323,83 +385,181 @@
 	if (!e.original_name || !e.aggregated_name ||
 	    e.aggregated_name->is_patterned) /* No patterns allowed. */
 		goto out;
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
+	param->list = &param->ns->policy_list[TOMOYO_ID_AGGREGATOR];
+	error = tomoyo_update_policy(&e.head, sizeof(e), param,
 				     tomoyo_same_aggregator);
- out:
+out:
 	tomoyo_put_name(e.original_name);
 	tomoyo_put_name(e.aggregated_name);
 	return error;
 }
 
 /**
- * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
+ * tomoyo_find_namespace - Find specified namespace.
  *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
+ * @name: Name of namespace to find.
+ * @len:  Length of @name.
  *
- * Returns 0 on success, negative value otherwise.
+ * Returns pointer to "struct tomoyo_policy_namespace" if found,
+ * NULL otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_aggregator(char *data, const bool is_delete)
+static struct tomoyo_policy_namespace *tomoyo_find_namespace
+(const char *name, const unsigned int len)
 {
-	char *cp = strchr(data, ' ');
-
-	if (!cp)
-		return -EINVAL;
-	*cp++ = '\0';
-	return tomoyo_update_aggregator_entry(data, cp, is_delete);
+	struct tomoyo_policy_namespace *ns;
+	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
+		if (strncmp(name, ns->name, len) ||
+		    (name[len] && name[len] != ' '))
+			continue;
+		return ns;
+	}
+	return NULL;
 }
 
 /**
- * tomoyo_assign_domain - Create a domain.
+ * tomoyo_assign_namespace - Create a new namespace.
+ *
+ * @domainname: Name of namespace to create.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" on success,
+ * NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
+{
+	struct tomoyo_policy_namespace *ptr;
+	struct tomoyo_policy_namespace *entry;
+	const char *cp = domainname;
+	unsigned int len = 0;
+	while (*cp && *cp++ != ' ')
+		len++;
+	ptr = tomoyo_find_namespace(domainname, len);
+	if (ptr)
+		return ptr;
+	if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname))
+		return NULL;
+	entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS);
+	if (!entry)
+		return NULL;
+	if (mutex_lock_interruptible(&tomoyo_policy_lock))
+		goto out;
+	ptr = tomoyo_find_namespace(domainname, len);
+	if (!ptr && tomoyo_memory_ok(entry)) {
+		char *name = (char *) (entry + 1);
+		ptr = entry;
+		memmove(name, domainname, len);
+		name[len] = '\0';
+		entry->name = name;
+		tomoyo_init_policy_namespace(entry);
+		entry = NULL;
+	}
+	mutex_unlock(&tomoyo_policy_lock);
+out:
+	kfree(entry);
+	return ptr;
+}
+
+/**
+ * tomoyo_namespace_jump - Check for namespace jump.
+ *
+ * @domainname: Name of domain.
+ *
+ * Returns true if namespace differs, false otherwise.
+ */
+static bool tomoyo_namespace_jump(const char *domainname)
+{
+	const char *namespace = tomoyo_current_namespace()->name;
+	const int len = strlen(namespace);
+	return strncmp(domainname, namespace, len) ||
+		(domainname[len] && domainname[len] != ' ');
+}
+
+/**
+ * tomoyo_assign_domain - Create a domain or a namespace.
  *
  * @domainname: The name of domain.
- * @profile:    Profile number to assign if the domain was newly created.
+ * @transit:    True if transit to domain found or created.
  *
  * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
 struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
-						const u8 profile)
+						const bool transit)
 {
-	struct tomoyo_domain_info *entry;
-	struct tomoyo_domain_info *domain = NULL;
-	const struct tomoyo_path_info *saved_domainname;
-	bool found = false;
-
-	if (!tomoyo_correct_domain(domainname))
+	struct tomoyo_domain_info e = { };
+	struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
+	bool created = false;
+	if (entry) {
+		if (transit) {
+			/*
+			 * Since namespace is created at runtime, profiles may
+			 * not be created by the moment the process transits to
+			 * that domain. Do not perform domain transition if
+			 * profile for that domain is not yet created.
+			 */
+			if (!entry->ns->profile_ptr[entry->profile])
+				return NULL;
+		}
+		return entry;
+	}
+	/* Requested domain does not exist. */
+	/* Don't create requested domain if domainname is invalid. */
+	if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 ||
+	    !tomoyo_correct_domain(domainname))
 		return NULL;
-	saved_domainname = tomoyo_get_name(domainname);
-	if (!saved_domainname)
+	/*
+	 * Since definition of profiles and acl_groups may differ across
+	 * namespaces, do not inherit "use_profile" and "use_group" settings
+	 * by automatically creating requested domain upon domain transition.
+	 */
+	if (transit && tomoyo_namespace_jump(domainname))
 		return NULL;
-	entry = kzalloc(sizeof(*entry), GFP_NOFS);
+	e.ns = tomoyo_assign_namespace(domainname);
+	if (!e.ns)
+		return NULL;
+	/*
+	 * "use_profile" and "use_group" settings for automatically created
+	 * domains are inherited from current domain. These are 0 for manually
+	 * created domains.
+	 */
+	if (transit) {
+		const struct tomoyo_domain_info *domain = tomoyo_domain();
+		e.profile = domain->profile;
+		e.group = domain->group;
+	}
+	e.domainname = tomoyo_get_name(domainname);
+	if (!e.domainname)
+		return NULL;
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		goto out;
-	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
-		if (domain->is_deleted ||
-		    tomoyo_pathcmp(saved_domainname, domain->domainname))
-			continue;
-		found = true;
-		break;
-	}
-	if (!found && tomoyo_memory_ok(entry)) {
-		INIT_LIST_HEAD(&entry->acl_info_list);
-		entry->domainname = saved_domainname;
-		saved_domainname = NULL;
-		entry->profile = profile;
-		list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
-		domain = entry;
-		entry = NULL;
-		found = true;
+	entry = tomoyo_find_domain(domainname);
+	if (!entry) {
+		entry = tomoyo_commit_ok(&e, sizeof(e));
+		if (entry) {
+			INIT_LIST_HEAD(&entry->acl_info_list);
+			list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+			created = true;
+		}
 	}
 	mutex_unlock(&tomoyo_policy_lock);
- out:
-	tomoyo_put_name(saved_domainname);
-	kfree(entry);
-	return found ? domain : NULL;
+out:
+	tomoyo_put_name(e.domainname);
+	if (entry && transit) {
+		if (created) {
+			struct tomoyo_request_info r;
+			tomoyo_init_request_info(&r, entry,
+						 TOMOYO_MAC_FILE_EXECUTE);
+			r.granted = false;
+			tomoyo_write_log(&r, "use_profile %u\n",
+					 entry->profile);
+			tomoyo_write_log(&r, "use_group %u\n", entry->group);
+		}
+	}
+	return entry;
 }
 
 /**
@@ -413,22 +573,27 @@
  */
 int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
-	struct tomoyo_request_info r;
-	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
 	struct tomoyo_domain_info *old_domain = tomoyo_domain();
 	struct tomoyo_domain_info *domain = NULL;
 	const char *original_name = bprm->filename;
-	u8 mode;
-	bool is_enforce;
 	int retval = -ENOMEM;
 	bool need_kfree = false;
+	bool reject_on_transition_failure = false;
 	struct tomoyo_path_info rn = { }; /* real name */
-
-	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
-	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
-	if (!tmp)
-		goto out;
-
+	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+	if (!ee)
+		return -ENOMEM;
+	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+	if (!ee->tmp) {
+		kfree(ee);
+		return -ENOMEM;
+	}
+	/* ee->dump->data is allocated by tomoyo_dump_page(). */
+	tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+	ee->r.ee = ee;
+	ee->bprm = bprm;
+	ee->r.obj = &ee->obj;
+	ee->obj.path1 = bprm->file->f_path;
  retry:
 	if (need_kfree) {
 		kfree(rn.name);
@@ -445,8 +610,10 @@
 	/* Check 'aggregator' directive. */
 	{
 		struct tomoyo_aggregator *ptr;
-		list_for_each_entry_rcu(ptr, &tomoyo_policy_list
-					[TOMOYO_ID_AGGREGATOR], head.list) {
+		struct list_head *list =
+			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
+		/* Check 'aggregator' directive. */
+		list_for_each_entry_rcu(ptr, list, head.list) {
 			if (ptr->head.is_deleted ||
 			    !tomoyo_path_matches_pattern(&rn,
 							 ptr->original_name))
@@ -460,7 +627,7 @@
 	}
 
 	/* Check execute permission. */
-	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
+	retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
 	if (retval == TOMOYO_RETRY_REQUEST)
 		goto retry;
 	if (retval < 0)
@@ -471,20 +638,30 @@
 	 * wildcard) rather than the pathname passed to execve()
 	 * (which never contains wildcard).
 	 */
-	if (r.param.path.matched_path) {
+	if (ee->r.param.path.matched_path) {
 		if (need_kfree)
 			kfree(rn.name);
 		need_kfree = false;
 		/* This is OK because it is read only. */
-		rn = *r.param.path.matched_path;
+		rn = *ee->r.param.path.matched_path;
 	}
 
 	/* Calculate domain to transit to. */
-	switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
+	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
+				       &rn)) {
+	case TOMOYO_TRANSITION_CONTROL_RESET:
+		/* Transit to the root of specified namespace. */
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+		/*
+		 * Make do_execve() fail if domain transition across namespaces
+		 * has failed.
+		 */
+		reject_on_transition_failure = true;
+		break;
 	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
-		/* Transit to the child of tomoyo_kernel_domain domain. */
-		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
-			 "%s", rn.name);
+		/* Transit to the child of current namespace's root. */
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+			 old_domain->ns->name, rn.name);
 		break;
 	case TOMOYO_TRANSITION_CONTROL_KEEP:
 		/* Keep current domain. */
@@ -502,33 +679,32 @@
 			domain = old_domain;
 		} else {
 			/* Normal domain transition. */
-			snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
 				 old_domain->domainname->name, rn.name);
 		}
 		break;
 	}
-	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
-		goto done;
-	domain = tomoyo_find_domain(tmp);
+	if (!domain)
+		domain = tomoyo_assign_domain(ee->tmp, true);
 	if (domain)
-		goto done;
-	if (is_enforce) {
-		int error = tomoyo_supervisor(&r, "# wants to create domain\n"
-					      "%s\n", tmp);
-		if (error == TOMOYO_RETRY_REQUEST)
-			goto retry;
-		if (error < 0)
-			goto done;
+		retval = 0;
+	else if (reject_on_transition_failure) {
+		printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
+		       ee->tmp);
+		retval = -ENOMEM;
+	} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
+		retval = -ENOMEM;
+	else {
+		retval = 0;
+		if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
+			old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
+			ee->r.granted = false;
+			tomoyo_write_log(&ee->r, "%s", tomoyo_dif
+					 [TOMOYO_DIF_TRANSITION_FAILED]);
+			printk(KERN_WARNING
+			       "ERROR: Domain '%s' not defined.\n", ee->tmp);
+		}
 	}
-	domain = tomoyo_assign_domain(tmp, old_domain->profile);
- done:
-	if (domain)
-		goto out;
-	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
-	if (is_enforce)
-		retval = -EPERM;
-	else
-		old_domain->transition_failed = true;
  out:
 	if (!domain)
 		domain = old_domain;
@@ -537,6 +713,54 @@
 	bprm->cred->security = domain;
 	if (need_kfree)
 		kfree(rn.name);
-	kfree(tmp);
+	kfree(ee->tmp);
+	kfree(ee->dump.data);
+	kfree(ee);
 	return retval;
 }
+
+/**
+ * tomoyo_dump_page - Dump a page to buffer.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @pos:  Location to dump.
+ * @dump: Poiner to "struct tomoyo_page_dump".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+		      struct tomoyo_page_dump *dump)
+{
+	struct page *page;
+	/* dump->data is released by tomoyo_finish_execve(). */
+	if (!dump->data) {
+		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
+		if (!dump->data)
+			return false;
+	}
+	/* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
+#ifdef CONFIG_MMU
+	if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
+		return false;
+#else
+	page = bprm->page[pos / PAGE_SIZE];
+#endif
+	if (page != dump->page) {
+		const unsigned int offset = pos % PAGE_SIZE;
+		/*
+		 * Maybe kmap()/kunmap() should be used here.
+		 * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
+		 * So do I.
+		 */
+		char *kaddr = kmap_atomic(page, KM_USER0);
+		dump->page = page;
+		memcpy(dump->data + offset, kaddr + offset,
+		       PAGE_SIZE - offset);
+		kunmap_atomic(kaddr, KM_USER0);
+	}
+	/* Same with put_arg_page(page) in fs/exec.c */
+#ifdef CONFIG_MMU
+	put_page(page);
+#endif
+	return true;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index d64e8ec..743c35f 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -1,80 +1,51 @@
 /*
  * security/tomoyo/file.c
  *
- * Pathname restriction functions.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include "common.h"
 #include <linux/slab.h>
 
-/* Keyword array for operations with one pathname. */
-const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
-	[TOMOYO_TYPE_READ_WRITE] = "read/write",
-	[TOMOYO_TYPE_EXECUTE]    = "execute",
-	[TOMOYO_TYPE_READ]       = "read",
-	[TOMOYO_TYPE_WRITE]      = "write",
-	[TOMOYO_TYPE_UNLINK]     = "unlink",
-	[TOMOYO_TYPE_RMDIR]      = "rmdir",
-	[TOMOYO_TYPE_TRUNCATE]   = "truncate",
-	[TOMOYO_TYPE_SYMLINK]    = "symlink",
-	[TOMOYO_TYPE_REWRITE]    = "rewrite",
-	[TOMOYO_TYPE_CHROOT]     = "chroot",
-	[TOMOYO_TYPE_UMOUNT]     = "unmount",
-};
-
-/* Keyword array for operations with one pathname and three numbers. */
-const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = {
-	[TOMOYO_TYPE_MKBLOCK]    = "mkblock",
-	[TOMOYO_TYPE_MKCHAR]     = "mkchar",
-};
-
-/* Keyword array for operations with two pathnames. */
-const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
-	[TOMOYO_TYPE_LINK]       = "link",
-	[TOMOYO_TYPE_RENAME]     = "rename",
-	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
-};
-
-/* Keyword array for operations with one pathname and one number. */
-const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
-	[TOMOYO_TYPE_CREATE]     = "create",
-	[TOMOYO_TYPE_MKDIR]      = "mkdir",
-	[TOMOYO_TYPE_MKFIFO]     = "mkfifo",
-	[TOMOYO_TYPE_MKSOCK]     = "mksock",
-	[TOMOYO_TYPE_IOCTL]      = "ioctl",
-	[TOMOYO_TYPE_CHMOD]      = "chmod",
-	[TOMOYO_TYPE_CHOWN]      = "chown",
-	[TOMOYO_TYPE_CHGRP]      = "chgrp",
-};
-
+/*
+ * Mapping table from "enum tomoyo_path_acl_index" to "enum tomoyo_mac_index".
+ */
 static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
-	[TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
 	[TOMOYO_TYPE_EXECUTE]    = TOMOYO_MAC_FILE_EXECUTE,
 	[TOMOYO_TYPE_READ]       = TOMOYO_MAC_FILE_OPEN,
 	[TOMOYO_TYPE_WRITE]      = TOMOYO_MAC_FILE_OPEN,
+	[TOMOYO_TYPE_APPEND]     = TOMOYO_MAC_FILE_OPEN,
 	[TOMOYO_TYPE_UNLINK]     = TOMOYO_MAC_FILE_UNLINK,
+	[TOMOYO_TYPE_GETATTR]    = TOMOYO_MAC_FILE_GETATTR,
 	[TOMOYO_TYPE_RMDIR]      = TOMOYO_MAC_FILE_RMDIR,
 	[TOMOYO_TYPE_TRUNCATE]   = TOMOYO_MAC_FILE_TRUNCATE,
 	[TOMOYO_TYPE_SYMLINK]    = TOMOYO_MAC_FILE_SYMLINK,
-	[TOMOYO_TYPE_REWRITE]    = TOMOYO_MAC_FILE_REWRITE,
 	[TOMOYO_TYPE_CHROOT]     = TOMOYO_MAC_FILE_CHROOT,
 	[TOMOYO_TYPE_UMOUNT]     = TOMOYO_MAC_FILE_UMOUNT,
 };
 
-static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_mkdev_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
 	[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
 	[TOMOYO_TYPE_MKCHAR]  = TOMOYO_MAC_FILE_MKCHAR,
 };
 
-static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path2_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
 	[TOMOYO_TYPE_LINK]       = TOMOYO_MAC_FILE_LINK,
 	[TOMOYO_TYPE_RENAME]     = TOMOYO_MAC_FILE_RENAME,
 	[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
 };
 
-static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path_number_acl_index" to
+ * "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
 	[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
 	[TOMOYO_TYPE_MKDIR]  = TOMOYO_MAC_FILE_MKDIR,
 	[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
@@ -85,41 +56,76 @@
 	[TOMOYO_TYPE_CHGRP]  = TOMOYO_MAC_FILE_CHGRP,
 };
 
+/**
+ * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
+ */
 void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
 {
-	if (!ptr)
-		return;
-	if (ptr->is_group)
-		tomoyo_put_group(ptr->group);
-	else
-		tomoyo_put_name(ptr->filename);
+	tomoyo_put_group(ptr->group);
+	tomoyo_put_name(ptr->filename);
 }
 
+/**
+ * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not.
+ *
+ * @name: Pointer to "struct tomoyo_path_info".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise.
+ */
 const struct tomoyo_path_info *
 tomoyo_compare_name_union(const struct tomoyo_path_info *name,
 			  const struct tomoyo_name_union *ptr)
 {
-	if (ptr->is_group)
+	if (ptr->group)
 		return tomoyo_path_matches_group(name, ptr->group);
 	if (tomoyo_path_matches_pattern(name, ptr->filename))
 		return ptr->filename;
 	return NULL;
 }
 
+/**
+ * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
 void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
 {
-	if (ptr && ptr->is_group)
-		tomoyo_put_group(ptr->group);
+	tomoyo_put_group(ptr->group);
 }
 
+/**
+ * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not.
+ *
+ * @value: Number to check.
+ * @ptr:   Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @value matches @ptr, false otherwise.
+ */
 bool tomoyo_compare_number_union(const unsigned long value,
 				 const struct tomoyo_number_union *ptr)
 {
-	if (ptr->is_group)
+	if (ptr->group)
 		return tomoyo_number_matches_group(value, value, ptr->group);
 	return value >= ptr->values[0] && value <= ptr->values[1];
 }
 
+/**
+ * tomoyo_add_slash - Add trailing '/' if needed.
+ *
+ * @buf: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns nothing.
+ *
+ * @buf must be generated by tomoyo_encode() because this function does not
+ * allocate memory for adding '/'.
+ */
 static void tomoyo_add_slash(struct tomoyo_path_info *buf)
 {
 	if (buf->is_dir)
@@ -132,24 +138,6 @@
 }
 
 /**
- * tomoyo_strendswith - Check whether the token ends with the given token.
- *
- * @name: The token to check.
- * @tail: The token to find.
- *
- * Returns true if @name ends with @tail, false otherwise.
- */
-static bool tomoyo_strendswith(const char *name, const char *tail)
-{
-	int len;
-
-	if (!name || !tail)
-		return false;
-	len = strlen(name) - strlen(tail);
-	return len >= 0 && !strcmp(name + len, tail);
-}
-
-/**
  * tomoyo_get_realpath - Get realpath.
  *
  * @buf:  Pointer to "struct tomoyo_path_info".
@@ -164,7 +152,7 @@
 		tomoyo_fill_path_info(buf);
 		return true;
 	}
-        return false;
+	return false;
 }
 
 /**
@@ -176,13 +164,9 @@
  */
 static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
 {
-	const char *operation = tomoyo_path_keyword[r->param.path.operation];
-	const struct tomoyo_path_info *filename = r->param.path.filename;
-	if (r->granted)
-		return 0;
-	tomoyo_warn_log(r, "%s %s", operation, filename->name);
-	return tomoyo_supervisor(r, "allow_%s %s\n", operation,
-				 tomoyo_pattern(filename));
+	return tomoyo_supervisor(r, "file %s %s\n", tomoyo_path_keyword
+				 [r->param.path.operation],
+				 r->param.path.filename->name);
 }
 
 /**
@@ -194,16 +178,10 @@
  */
 static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
 {
-	const char *operation = tomoyo_path2_keyword[r->param.path2.operation];
-	const struct tomoyo_path_info *filename1 = r->param.path2.filename1;
-	const struct tomoyo_path_info *filename2 = r->param.path2.filename2;
-	if (r->granted)
-		return 0;
-	tomoyo_warn_log(r, "%s %s %s", operation, filename1->name,
-			filename2->name);
-	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
-				 tomoyo_pattern(filename1),
-				 tomoyo_pattern(filename2));
+	return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+				 [tomoyo_pp2mac[r->param.path2.operation]],
+				 r->param.path2.filename1->name,
+				 r->param.path2.filename2->name);
 }
 
 /**
@@ -215,24 +193,18 @@
  */
 static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)
 {
-	const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation];
-	const struct tomoyo_path_info *filename = r->param.mkdev.filename;
-	const unsigned int major = r->param.mkdev.major;
-	const unsigned int minor = r->param.mkdev.minor;
-	const unsigned int mode = r->param.mkdev.mode;
-	if (r->granted)
-		return 0;
-	tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode,
-			major, minor);
-	return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation,
-				 tomoyo_pattern(filename), mode, major, minor);
+	return tomoyo_supervisor(r, "file %s %s 0%o %u %u\n",
+				 tomoyo_mac_keywords
+				 [tomoyo_pnnn2mac[r->param.mkdev.operation]],
+				 r->param.mkdev.filename->name,
+				 r->param.mkdev.mode, r->param.mkdev.major,
+				 r->param.mkdev.minor);
 }
 
 /**
  * tomoyo_audit_path_number_log - Audit path/number request log.
  *
- * @r:     Pointer to "struct tomoyo_request_info".
- * @error: Error code.
+ * @r: Pointer to "struct tomoyo_request_info".
  *
  * Returns 0 on success, negative value otherwise.
  */
@@ -240,11 +212,7 @@
 {
 	const u8 type = r->param.path_number.operation;
 	u8 radix;
-	const struct tomoyo_path_info *filename = r->param.path_number.filename;
-	const char *operation = tomoyo_path_number_keyword[type];
 	char buffer[64];
-	if (r->granted)
-		return 0;
 	switch (type) {
 	case TOMOYO_TYPE_CREATE:
 	case TOMOYO_TYPE_MKDIR:
@@ -262,251 +230,23 @@
 	}
 	tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,
 			   radix);
-	tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer);
-	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
-				 tomoyo_pattern(filename), buffer);
-}
-
-static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
-					  const struct tomoyo_acl_head *b)
-{
-	return container_of(a, struct tomoyo_readable_file,
-			    head)->filename ==
-		container_of(b, struct tomoyo_readable_file,
-			     head)->filename;
+	return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+				 [tomoyo_pn2mac[type]],
+				 r->param.path_number.filename->name, buffer);
 }
 
 /**
- * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list.
+ * tomoyo_check_path_acl - Check permission for path operation.
  *
- * @filename:  Filename unconditionally permitted to open() for reading.
- * @is_delete: True if it is a delete request.
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
  *
- * Returns 0 on success, negative value otherwise.
+ * Returns true if granted, false otherwise.
  *
- * Caller holds tomoyo_read_lock().
+ * To be able to use wildcard for domain transition, this function sets
+ * matching entry on success. Since the caller holds tomoyo_read_lock(),
+ * it is safe to set matching entry.
  */
-static int tomoyo_update_globally_readable_entry(const char *filename,
-						 const bool is_delete)
-{
-	struct tomoyo_readable_file e = { };
-	int error;
-
-	if (!tomoyo_correct_word(filename))
-		return -EINVAL;
-	e.filename = tomoyo_get_name(filename);
-	if (!e.filename)
-		return -ENOMEM;
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list
-				     [TOMOYO_ID_GLOBALLY_READABLE],
-				     tomoyo_same_globally_readable);
-	tomoyo_put_name(e.filename);
-	return error;
-}
-
-/**
- * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
- *
- * @filename: The filename to check.
- *
- * Returns true if any domain can open @filename for reading, false otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static bool tomoyo_globally_readable_file(const struct tomoyo_path_info *
-					     filename)
-{
-	struct tomoyo_readable_file *ptr;
-	bool found = false;
-
-	list_for_each_entry_rcu(ptr, &tomoyo_policy_list
-				[TOMOYO_ID_GLOBALLY_READABLE], head.list) {
-		if (!ptr->head.is_deleted &&
-		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
-			found = true;
-			break;
-		}
-	}
-	return found;
-}
-
-/**
- * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list.
- *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_globally_readable(char *data, const bool is_delete)
-{
-	return tomoyo_update_globally_readable_entry(data, is_delete);
-}
-
-static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
-				const struct tomoyo_acl_head *b)
-{
-	return container_of(a, struct tomoyo_no_pattern, head)->pattern ==
-		container_of(b, struct tomoyo_no_pattern, head)->pattern;
-}
-
-/**
- * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list.
- *
- * @pattern:   Pathname pattern.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_file_pattern_entry(const char *pattern,
-					    const bool is_delete)
-{
-	struct tomoyo_no_pattern e = { };
-	int error;
-
-	if (!tomoyo_correct_word(pattern))
-		return -EINVAL;
-	e.pattern = tomoyo_get_name(pattern);
-	if (!e.pattern)
-		return -ENOMEM;
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list[TOMOYO_ID_PATTERN],
-				     tomoyo_same_pattern);
-	tomoyo_put_name(e.pattern);
-	return error;
-}
-
-/**
- * tomoyo_pattern - Get patterned pathname.
- *
- * @filename: The filename to find patterned pathname.
- *
- * Returns pointer to pathname pattern if matched, @filename otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename)
-{
-	struct tomoyo_no_pattern *ptr;
-	const struct tomoyo_path_info *pattern = NULL;
-
-	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN],
-				head.list) {
-		if (ptr->head.is_deleted)
-			continue;
-		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
-			continue;
-		pattern = ptr->pattern;
-		if (tomoyo_strendswith(pattern->name, "/\\*")) {
-			/* Do nothing. Try to find the better match. */
-		} else {
-			/* This would be the better match. Use this. */
-			break;
-		}
-	}
-	if (pattern)
-		filename = pattern;
-	return filename->name;
-}
-
-/**
- * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list.
- *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_pattern(char *data, const bool is_delete)
-{
-	return tomoyo_update_file_pattern_entry(data, is_delete);
-}
-
-static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
-				   const struct tomoyo_acl_head *b)
-{
-	return container_of(a, struct tomoyo_no_rewrite, head)->pattern
-		== container_of(b, struct tomoyo_no_rewrite, head)
-		->pattern;
-}
-
-/**
- * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list.
- *
- * @pattern:   Pathname pattern that are not rewritable by default.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_no_rewrite_entry(const char *pattern,
-					  const bool is_delete)
-{
-	struct tomoyo_no_rewrite e = { };
-	int error;
-
-	if (!tomoyo_correct_word(pattern))
-		return -EINVAL;
-	e.pattern = tomoyo_get_name(pattern);
-	if (!e.pattern)
-		return -ENOMEM;
-	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-				     &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
-				     tomoyo_same_no_rewrite);
-	tomoyo_put_name(e.pattern);
-	return error;
-}
-
-/**
- * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
- *
- * @filename: Filename to check.
- *
- * Returns true if @filename is specified by "deny_rewrite" directive,
- * false otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename)
-{
-	struct tomoyo_no_rewrite *ptr;
-	bool found = false;
-
-	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
-				head.list) {
-		if (ptr->head.is_deleted)
-			continue;
-		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
-			continue;
-		found = true;
-		break;
-	}
-	return found;
-}
-
-/**
- * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list.
- *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete)
-{
-	return tomoyo_update_no_rewrite_entry(data, is_delete);
-}
-
 static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
 				  const struct tomoyo_acl_info *ptr)
 {
@@ -521,6 +261,14 @@
 	return false;
 }
 
+/**
+ * tomoyo_check_path_number_acl - Check permission for path number operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
 static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
 					 const struct tomoyo_acl_info *ptr)
 {
@@ -533,6 +281,14 @@
 					  &acl->name);
 }
 
+/**
+ * tomoyo_check_path2_acl - Check permission for path path operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
 static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
 				   const struct tomoyo_acl_info *ptr)
 {
@@ -544,8 +300,16 @@
 					     &acl->name2);
 }
 
+/**
+ * tomoyo_check_mkdev_acl - Check permission for path number number number operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
 static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
-				const struct tomoyo_acl_info *ptr)
+				   const struct tomoyo_acl_info *ptr)
 {
 	const struct tomoyo_mkdev_acl *acl =
 		container_of(ptr, typeof(*acl), head);
@@ -560,15 +324,31 @@
 					  &acl->name);
 }
 
+/**
+ * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
 static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
 				 const struct tomoyo_acl_info *b)
 {
 	const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
 	const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
-	return tomoyo_same_acl_head(&p1->head, &p2->head) &&
-		tomoyo_same_name_union(&p1->name, &p2->name);
+	return tomoyo_same_name_union(&p1->name, &p2->name);
 }
 
+/**
+ * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
 static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
 				  struct tomoyo_acl_info *b,
 				  const bool is_delete)
@@ -577,19 +357,10 @@
 		->perm;
 	u16 perm = *a_perm;
 	const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
-	if (is_delete) {
+	if (is_delete)
 		perm &= ~b_perm;
-		if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
-			perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
-		else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
-			perm &= ~TOMOYO_RW_MASK;
-	} else {
+	else
 		perm |= b_perm;
-		if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
-			perm |= (1 << TOMOYO_TYPE_READ_WRITE);
-		else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
-			perm |= TOMOYO_RW_MASK;
-	}
 	*a_perm = perm;
 	return !perm;
 }
@@ -597,52 +368,62 @@
 /**
  * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
  *
- * @type:      Type of operation.
- * @filename:  Filename.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm:  Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_update_path_acl(const u8 type, const char *filename,
-				  struct tomoyo_domain_info * const domain,
-				  const bool is_delete)
+static int tomoyo_update_path_acl(const u16 perm,
+				  struct tomoyo_acl_param *param)
 {
 	struct tomoyo_path_acl e = {
 		.head.type = TOMOYO_TYPE_PATH_ACL,
-		.perm = 1 << type
+		.perm = perm
 	};
 	int error;
-	if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
-		e.perm |= TOMOYO_RW_MASK;
-	if (!tomoyo_parse_name_union(filename, &e.name))
-		return -EINVAL;
-	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
-				     tomoyo_same_path_acl,
-				     tomoyo_merge_path_acl);
+	if (!tomoyo_parse_name_union(param, &e.name))
+		error = -EINVAL;
+	else
+		error = tomoyo_update_domain(&e.head, sizeof(e), param,
+					     tomoyo_same_path_acl,
+					     tomoyo_merge_path_acl);
 	tomoyo_put_name_union(&e.name);
 	return error;
 }
 
+/**
+ * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
 static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
 					 const struct tomoyo_acl_info *b)
 {
-	const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1),
-								head);
-	const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2),
-								head);
-	return tomoyo_same_acl_head(&p1->head, &p2->head)
-		&& tomoyo_same_name_union(&p1->name, &p2->name)
-		&& tomoyo_same_number_union(&p1->mode, &p2->mode)
-		&& tomoyo_same_number_union(&p1->major, &p2->major)
-		&& tomoyo_same_number_union(&p1->minor, &p2->minor);
+	const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
+	return tomoyo_same_name_union(&p1->name, &p2->name) &&
+		tomoyo_same_number_union(&p1->mode, &p2->mode) &&
+		tomoyo_same_number_union(&p1->major, &p2->major) &&
+		tomoyo_same_number_union(&p1->minor, &p2->minor);
 }
 
+/**
+ * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
 static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
-					  struct tomoyo_acl_info *b,
-					  const bool is_delete)
+				   struct tomoyo_acl_info *b,
+				   const bool is_delete)
 {
 	u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,
 					 head)->perm;
@@ -660,37 +441,30 @@
 /**
  * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.
  *
- * @type:      Type of operation.
- * @filename:  Filename.
- * @mode:      Create mode.
- * @major:     Device major number.
- * @minor:     Device minor number.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm:  Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
-					  char *mode, char *major, char *minor,
-					  struct tomoyo_domain_info * const
-					  domain, const bool is_delete)
+static int tomoyo_update_mkdev_acl(const u8 perm,
+				   struct tomoyo_acl_param *param)
 {
 	struct tomoyo_mkdev_acl e = {
 		.head.type = TOMOYO_TYPE_MKDEV_ACL,
-		.perm = 1 << type
+		.perm = perm
 	};
-	int error = is_delete ? -ENOENT : -ENOMEM;
-	if (!tomoyo_parse_name_union(filename, &e.name) ||
-	    !tomoyo_parse_number_union(mode, &e.mode) ||
-	    !tomoyo_parse_number_union(major, &e.major) ||
-	    !tomoyo_parse_number_union(minor, &e.minor))
-		goto out;
-	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
-				     tomoyo_same_mkdev_acl,
-				     tomoyo_merge_mkdev_acl);
- out:
+	int error;
+	if (!tomoyo_parse_name_union(param, &e.name) ||
+	    !tomoyo_parse_number_union(param, &e.mode) ||
+	    !tomoyo_parse_number_union(param, &e.major) ||
+	    !tomoyo_parse_number_union(param, &e.minor))
+		error = -EINVAL;
+	else
+		error = tomoyo_update_domain(&e.head, sizeof(e), param,
+					     tomoyo_same_mkdev_acl,
+					     tomoyo_merge_mkdev_acl);
 	tomoyo_put_name_union(&e.name);
 	tomoyo_put_number_union(&e.mode);
 	tomoyo_put_number_union(&e.major);
@@ -698,16 +472,32 @@
 	return error;
 }
 
+/**
+ * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
 static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
 				  const struct tomoyo_acl_info *b)
 {
 	const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
 	const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
-	return tomoyo_same_acl_head(&p1->head, &p2->head)
-		&& tomoyo_same_name_union(&p1->name1, &p2->name1)
-		&& tomoyo_same_name_union(&p1->name2, &p2->name2);
+	return tomoyo_same_name_union(&p1->name1, &p2->name1) &&
+		tomoyo_same_name_union(&p1->name2, &p2->name2);
 }
 
+/**
+ * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
 static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
 				   struct tomoyo_acl_info *b,
 				   const bool is_delete)
@@ -727,33 +517,28 @@
 /**
  * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
  *
- * @type:      Type of operation.
- * @filename1: First filename.
- * @filename2: Second filename.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm:  Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
-				   const char *filename2,
-				   struct tomoyo_domain_info * const domain,
-				   const bool is_delete)
+static int tomoyo_update_path2_acl(const u8 perm,
+				   struct tomoyo_acl_param *param)
 {
 	struct tomoyo_path2_acl e = {
 		.head.type = TOMOYO_TYPE_PATH2_ACL,
-		.perm = 1 << type
+		.perm = perm
 	};
-	int error = is_delete ? -ENOENT : -ENOMEM;
-	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
-	    !tomoyo_parse_name_union(filename2, &e.name2))
-		goto out;
-	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
-				     tomoyo_same_path2_acl,
-				     tomoyo_merge_path2_acl);
- out:
+	int error;
+	if (!tomoyo_parse_name_union(param, &e.name1) ||
+	    !tomoyo_parse_name_union(param, &e.name2))
+		error = -EINVAL;
+	else
+		error = tomoyo_update_domain(&e.head, sizeof(e), param,
+					     tomoyo_same_path2_acl,
+					     tomoyo_merge_path2_acl);
 	tomoyo_put_name_union(&e.name1);
 	tomoyo_put_name_union(&e.name2);
 	return error;
@@ -775,9 +560,8 @@
 {
 	int error;
 
- next:
 	r->type = tomoyo_p2mac[operation];
-	r->mode = tomoyo_get_mode(r->profile, r->type);
+	r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
 	if (r->mode == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	r->param_type = TOMOYO_TYPE_PATH_ACL;
@@ -785,10 +569,6 @@
 	r->param.path.operation = operation;
 	do {
 		tomoyo_check_acl(r, tomoyo_check_path_acl);
-		if (!r->granted && operation == TOMOYO_TYPE_READ &&
-		    !r->domain->ignore_global_allow_read &&
-		    tomoyo_globally_readable_file(filename))
-			r->granted = true;
 		error = tomoyo_audit_path_log(r);
 		/*
 		 * Do not retry for execute request, for alias may have
@@ -796,19 +576,17 @@
 		 */
 	} while (error == TOMOYO_RETRY_REQUEST &&
 		 operation != TOMOYO_TYPE_EXECUTE);
-	/*
-	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
-	 * we need to check "allow_rewrite" permission if the filename is
-	 * specified by "deny_rewrite" keyword.
-	 */
-	if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
-	    tomoyo_no_rewrite_file(filename)) {
-		operation = TOMOYO_TYPE_REWRITE;
-		goto next;
-	}
 	return error;
 }
 
+/**
+ * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
 static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
 					const struct tomoyo_acl_info *b)
 {
@@ -816,11 +594,19 @@
 							       head);
 	const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
 							       head);
-	return tomoyo_same_acl_head(&p1->head, &p2->head)
-		&& tomoyo_same_name_union(&p1->name, &p2->name)
-		&& tomoyo_same_number_union(&p1->number, &p2->number);
+	return tomoyo_same_name_union(&p1->name, &p2->name) &&
+		tomoyo_same_number_union(&p1->number, &p2->number);
 }
 
+/**
+ * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
 static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
 					 struct tomoyo_acl_info *b,
 					 const bool is_delete)
@@ -841,33 +627,26 @@
 /**
  * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
  *
- * @type:      Type of operation.
- * @filename:  Filename.
- * @number:    Number.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm:  Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  */
-static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
-					 char *number,
-					 struct tomoyo_domain_info * const
-					 domain,
-					 const bool is_delete)
+static int tomoyo_update_path_number_acl(const u8 perm,
+					 struct tomoyo_acl_param *param)
 {
 	struct tomoyo_path_number_acl e = {
 		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
-		.perm = 1 << type
+		.perm = perm
 	};
-	int error = is_delete ? -ENOENT : -ENOMEM;
-	if (!tomoyo_parse_name_union(filename, &e.name))
-		return -EINVAL;
-	if (!tomoyo_parse_number_union(number, &e.number))
-		goto out;
-	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
-				     tomoyo_same_path_number_acl,
-				     tomoyo_merge_path_number_acl);
- out:
+	int error;
+	if (!tomoyo_parse_name_union(param, &e.name) ||
+	    !tomoyo_parse_number_union(param, &e.number))
+		error = -EINVAL;
+	else
+		error = tomoyo_update_domain(&e.head, sizeof(e), param,
+					     tomoyo_same_path_number_acl,
+					     tomoyo_merge_path_number_acl);
 	tomoyo_put_name_union(&e.name);
 	tomoyo_put_number_union(&e.number);
 	return error;
@@ -886,16 +665,20 @@
 			    unsigned long number)
 {
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj = {
+		.path1 = *path,
+	};
 	int error = -ENOMEM;
 	struct tomoyo_path_info buf;
 	int idx;
 
 	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
-	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
+	    == TOMOYO_CONFIG_DISABLED || !path->dentry)
 		return 0;
 	idx = tomoyo_read_lock();
 	if (!tomoyo_get_realpath(&buf, path))
 		goto out;
+	r.obj = &obj;
 	if (type == TOMOYO_TYPE_MKDIR)
 		tomoyo_add_slash(&buf);
 	r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL;
@@ -930,45 +713,30 @@
 	int error = 0;
 	struct tomoyo_path_info buf;
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj = {
+		.path1 = *path,
+	};
 	int idx;
 
-	if (!path->mnt ||
-	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
-		return 0;
 	buf.name = NULL;
 	r.mode = TOMOYO_CONFIG_DISABLED;
 	idx = tomoyo_read_lock();
-	/*
-	 * If the filename is specified by "deny_rewrite" keyword,
-	 * we need to check "allow_rewrite" permission when the filename is not
-	 * opened for append mode or the filename is truncated at open time.
-	 */
-	if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
-	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
+	if (acc_mode &&
+	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
 	    != TOMOYO_CONFIG_DISABLED) {
 		if (!tomoyo_get_realpath(&buf, path)) {
 			error = -ENOMEM;
 			goto out;
 		}
-		if (tomoyo_no_rewrite_file(&buf))
-			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+		r.obj = &obj;
+		if (acc_mode & MAY_READ)
+			error = tomoyo_path_permission(&r, TOMOYO_TYPE_READ,
 						       &buf);
-	}
-	if (!error && acc_mode &&
-	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
-	    != TOMOYO_CONFIG_DISABLED) {
-		u8 operation;
-		if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
-			error = -ENOMEM;
-			goto out;
-		}
-		if (acc_mode == (MAY_READ | MAY_WRITE))
-			operation = TOMOYO_TYPE_READ_WRITE;
-		else if (acc_mode == MAY_READ)
-			operation = TOMOYO_TYPE_READ;
-		else
-			operation = TOMOYO_TYPE_WRITE;
-		error = tomoyo_path_permission(&r, operation, &buf);
+		if (!error && (acc_mode & MAY_WRITE))
+			error = tomoyo_path_permission(&r, (flag & O_APPEND) ?
+						       TOMOYO_TYPE_APPEND :
+						       TOMOYO_TYPE_WRITE,
+						       &buf);
 	}
  out:
 	kfree(buf.name);
@@ -979,46 +747,57 @@
 }
 
 /**
- * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
+ * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount".
  *
  * @operation: Type of operation.
  * @path:      Pointer to "struct path".
+ * @target:    Symlink's target if @operation is TOMOYO_TYPE_SYMLINK,
+ *             NULL otherwise.
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_path_perm(const u8 operation, struct path *path)
+int tomoyo_path_perm(const u8 operation, struct path *path, const char *target)
 {
-	int error = -ENOMEM;
-	struct tomoyo_path_info buf;
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj = {
+		.path1 = *path,
+	};
+	int error;
+	struct tomoyo_path_info buf;
+	bool is_enforce;
+	struct tomoyo_path_info symlink_target;
 	int idx;
 
-	if (!path->mnt)
-		return 0;
 	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
 	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
+	is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+	error = -ENOMEM;
 	buf.name = NULL;
 	idx = tomoyo_read_lock();
 	if (!tomoyo_get_realpath(&buf, path))
 		goto out;
+	r.obj = &obj;
 	switch (operation) {
-	case TOMOYO_TYPE_REWRITE:
-		if (!tomoyo_no_rewrite_file(&buf)) {
-			error = 0;
-			goto out;
-		}
-		break;
 	case TOMOYO_TYPE_RMDIR:
 	case TOMOYO_TYPE_CHROOT:
 		tomoyo_add_slash(&buf);
 		break;
+	case TOMOYO_TYPE_SYMLINK:
+		symlink_target.name = tomoyo_encode(target);
+		if (!symlink_target.name)
+			goto out;
+		tomoyo_fill_path_info(&symlink_target);
+		obj.symlink_target = &symlink_target;
+		break;
 	}
 	error = tomoyo_path_permission(&r, operation, &buf);
+	if (operation == TOMOYO_TYPE_SYMLINK)
+		kfree(symlink_target.name);
  out:
 	kfree(buf.name);
 	tomoyo_read_unlock(idx);
-	if (r.mode != TOMOYO_CONFIG_ENFORCING)
+	if (!is_enforce)
 		error = 0;
 	return error;
 }
@@ -1034,20 +813,23 @@
  * Returns 0 on success, negative value otherwise.
  */
 int tomoyo_mkdev_perm(const u8 operation, struct path *path,
-			     const unsigned int mode, unsigned int dev)
+		      const unsigned int mode, unsigned int dev)
 {
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj = {
+		.path1 = *path,
+	};
 	int error = -ENOMEM;
 	struct tomoyo_path_info buf;
 	int idx;
 
-	if (!path->mnt ||
-	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
+	if (tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
 	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	idx = tomoyo_read_lock();
 	error = -ENOMEM;
 	if (tomoyo_get_realpath(&buf, path)) {
+		r.obj = &obj;
 		dev = new_decode_dev(dev);
 		r.param_type = TOMOYO_TYPE_MKDEV_ACL;
 		r.param.mkdev.filename = &buf;
@@ -1081,10 +863,13 @@
 	struct tomoyo_path_info buf1;
 	struct tomoyo_path_info buf2;
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj = {
+		.path1 = *path1,
+		.path2 = *path2,
+	};
 	int idx;
 
-	if (!path1->mnt || !path2->mnt ||
-	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+	if (tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
 	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	buf1.name = NULL;
@@ -1096,16 +881,17 @@
 	switch (operation) {
 		struct dentry *dentry;
 	case TOMOYO_TYPE_RENAME:
-        case TOMOYO_TYPE_LINK:
+	case TOMOYO_TYPE_LINK:
 		dentry = path1->dentry;
-	        if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
-                        break;
-                /* fall through */
-        case TOMOYO_TYPE_PIVOT_ROOT:
-                tomoyo_add_slash(&buf1);
-                tomoyo_add_slash(&buf2);
+		if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
+			break;
+		/* fall through */
+	case TOMOYO_TYPE_PIVOT_ROOT:
+		tomoyo_add_slash(&buf1);
+		tomoyo_add_slash(&buf2);
 		break;
-        }
+	}
+	r.obj = &obj;
 	r.param_type = TOMOYO_TYPE_PATH2_ACL;
 	r.param.path2.operation = operation;
 	r.param.path2.filename1 = &buf1;
@@ -1124,53 +910,91 @@
 }
 
 /**
- * tomoyo_write_file - Update file related list.
+ * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry.
  *
- * @data:      String to parse.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
+				  const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
+	return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
+		tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
+		tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
+		tomoyo_same_number_union(&p1->flags, &p2->flags);
+}
+
+/**
+ * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
  *
  * Returns 0 on success, negative value otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
-		      const bool is_delete)
+static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param)
 {
-	char *w[5];
+	struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
+	int error;
+	if (!tomoyo_parse_name_union(param, &e.dev_name) ||
+	    !tomoyo_parse_name_union(param, &e.dir_name) ||
+	    !tomoyo_parse_name_union(param, &e.fs_type) ||
+	    !tomoyo_parse_number_union(param, &e.flags))
+		error = -EINVAL;
+	else
+		error = tomoyo_update_domain(&e.head, sizeof(e), param,
+					     tomoyo_same_mount_acl, NULL);
+	tomoyo_put_name_union(&e.dev_name);
+	tomoyo_put_name_union(&e.dir_name);
+	tomoyo_put_name_union(&e.fs_type);
+	tomoyo_put_number_union(&e.flags);
+	return error;
+}
+
+/**
+ * tomoyo_write_file - Update file related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_file(struct tomoyo_acl_param *param)
+{
+	u16 perm = 0;
 	u8 type;
-	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
-		return -EINVAL;
-	if (strncmp(w[0], "allow_", 6))
-		goto out;
-	w[0] += 6;
-	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
-		if (strcmp(w[0], tomoyo_path_keyword[type]))
-			continue;
-		return tomoyo_update_path_acl(type, w[1], domain, is_delete);
-	}
-	if (!w[2][0])
-		goto out;
-	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
-		if (strcmp(w[0], tomoyo_path2_keyword[type]))
-			continue;
-		return tomoyo_update_path2_acl(type, w[1], w[2], domain,
-					       is_delete);
-	}
-	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
-		if (strcmp(w[0], tomoyo_path_number_keyword[type]))
-			continue;
-		return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
-						     is_delete);
-	}
-	if (!w[3][0] || !w[4][0])
-		goto out;
-	for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) {
-		if (strcmp(w[0], tomoyo_mkdev_keyword[type]))
-			continue;
-		return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3],
-					       w[4], domain, is_delete);
-	}
- out:
+	const char *operation = tomoyo_read_token(param);
+	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++)
+		if (tomoyo_permstr(operation, tomoyo_path_keyword[type]))
+			perm |= 1 << type;
+	if (perm)
+		return tomoyo_update_path_acl(perm, param);
+	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++)
+		if (tomoyo_permstr(operation,
+				   tomoyo_mac_keywords[tomoyo_pp2mac[type]]))
+			perm |= 1 << type;
+	if (perm)
+		return tomoyo_update_path2_acl(perm, param);
+	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++)
+		if (tomoyo_permstr(operation,
+				   tomoyo_mac_keywords[tomoyo_pn2mac[type]]))
+			perm |= 1 << type;
+	if (perm)
+		return tomoyo_update_path_number_acl(perm, param);
+	for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++)
+		if (tomoyo_permstr(operation,
+				   tomoyo_mac_keywords[tomoyo_pnnn2mac[type]]))
+			perm |= 1 << type;
+	if (perm)
+		return tomoyo_update_mkdev_acl(perm, param);
+	if (tomoyo_permstr(operation,
+			   tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT]))
+		return tomoyo_update_mount_acl(param);
 	return -EINVAL;
 }
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index a877e4c..ae135fb 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -1,58 +1,205 @@
 /*
  * security/tomoyo/gc.c
  *
- * Implementation of the Domain-Based Mandatory Access Control.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
- *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include "common.h"
 #include <linux/kthread.h>
 #include <linux/slab.h>
 
+/* The list for "struct tomoyo_io_buffer". */
+static LIST_HEAD(tomoyo_io_buffer_list);
+/* Lock for protecting tomoyo_io_buffer_list. */
+static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
+
+/* Size of an element. */
+static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
+	[TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
+	[TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
+	[TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
+	[TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
+	[TOMOYO_ID_TRANSITION_CONTROL] =
+	sizeof(struct tomoyo_transition_control),
+	[TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
+	/* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
+	/* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
+	/* [TOMOYO_ID_ACL] =
+	   tomoyo_acl_size["struct tomoyo_acl_info"->type], */
+	[TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
+};
+
+/* Size of a domain ACL element. */
+static const u8 tomoyo_acl_size[] = {
+	[TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
+	[TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
+	[TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
+	[TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
+	[TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
+};
+
+/**
+ * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true if @element is used by /sys/kernel/security/tomoyo/ users,
+ * false otherwise.
+ */
+static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
+{
+	struct tomoyo_io_buffer *head;
+	bool in_use = false;
+
+	spin_lock(&tomoyo_io_buffer_list_lock);
+	list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
+		head->users++;
+		spin_unlock(&tomoyo_io_buffer_list_lock);
+		if (mutex_lock_interruptible(&head->io_sem)) {
+			in_use = true;
+			goto out;
+		}
+		if (head->r.domain == element || head->r.group == element ||
+		    head->r.acl == element || &head->w.domain->list == element)
+			in_use = true;
+		mutex_unlock(&head->io_sem);
+out:
+		spin_lock(&tomoyo_io_buffer_list_lock);
+		head->users--;
+		if (in_use)
+			break;
+	}
+	spin_unlock(&tomoyo_io_buffer_list_lock);
+	return in_use;
+}
+
+/**
+ * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
+ *
+ * @string: String to check.
+ * @size:   Memory allocated for @string .
+ *
+ * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
+ * false otherwise.
+ */
+static bool tomoyo_name_used_by_io_buffer(const char *string,
+					  const size_t size)
+{
+	struct tomoyo_io_buffer *head;
+	bool in_use = false;
+
+	spin_lock(&tomoyo_io_buffer_list_lock);
+	list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
+		int i;
+		head->users++;
+		spin_unlock(&tomoyo_io_buffer_list_lock);
+		if (mutex_lock_interruptible(&head->io_sem)) {
+			in_use = true;
+			goto out;
+		}
+		for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
+			const char *w = head->r.w[i];
+			if (w < string || w > string + size)
+				continue;
+			in_use = true;
+			break;
+		}
+		mutex_unlock(&head->io_sem);
+out:
+		spin_lock(&tomoyo_io_buffer_list_lock);
+		head->users--;
+		if (in_use)
+			break;
+	}
+	spin_unlock(&tomoyo_io_buffer_list_lock);
+	return in_use;
+}
+
+/* Structure for garbage collection. */
 struct tomoyo_gc {
 	struct list_head list;
-	int type;
+	enum tomoyo_policy_id type;
+	size_t size;
 	struct list_head *element;
 };
-static LIST_HEAD(tomoyo_gc_queue);
-static DEFINE_MUTEX(tomoyo_gc_mutex);
+/* List of entries to be deleted. */
+static LIST_HEAD(tomoyo_gc_list);
+/* Length of tomoyo_gc_list. */
+static int tomoyo_gc_list_len;
 
-/* Caller holds tomoyo_policy_lock mutex. */
+/**
+ * tomoyo_add_to_gc - Add an entry to to be deleted list.
+ *
+ * @type:    One of values in "enum tomoyo_policy_id".
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ *
+ * Adding an entry needs kmalloc(). Thus, if we try to add thousands of
+ * entries at once, it will take too long time. Thus, do not add more than 128
+ * entries per a scan. But to be able to handle worst case where all entries
+ * are in-use, we accept one more entry per a scan.
+ *
+ * If we use singly linked list using "struct list_head"->prev (which is
+ * LIST_POISON2), we can avoid kmalloc().
+ */
 static bool tomoyo_add_to_gc(const int type, struct list_head *element)
 {
 	struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
 	if (!entry)
 		return false;
 	entry->type = type;
+	if (type == TOMOYO_ID_ACL)
+		entry->size = tomoyo_acl_size[
+			      container_of(element,
+					   typeof(struct tomoyo_acl_info),
+					   list)->type];
+	else if (type == TOMOYO_ID_NAME)
+		entry->size = strlen(container_of(element,
+						  typeof(struct tomoyo_name),
+						  head.list)->entry.name) + 1;
+	else if (type == TOMOYO_ID_CONDITION)
+		entry->size =
+			container_of(element, typeof(struct tomoyo_condition),
+				     head.list)->size;
+	else
+		entry->size = tomoyo_element_size[type];
 	entry->element = element;
-	list_add(&entry->list, &tomoyo_gc_queue);
+	list_add(&entry->list, &tomoyo_gc_list);
 	list_del_rcu(element);
-	return true;
+	return tomoyo_gc_list_len++ < 128;
 }
 
-static void tomoyo_del_allow_read(struct list_head *element)
+/**
+ * tomoyo_element_linked_by_gc - Validate next element of an entry.
+ *
+ * @element: Pointer to an element.
+ * @size:    Size of @element in byte.
+ *
+ * Returns true if @element is linked by other elements in the garbage
+ * collector's queue, false otherwise.
+ */
+static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
 {
-	struct tomoyo_readable_file *ptr =
-		container_of(element, typeof(*ptr), head.list);
-	tomoyo_put_name(ptr->filename);
+	struct tomoyo_gc *p;
+	list_for_each_entry(p, &tomoyo_gc_list, list) {
+		const u8 *ptr = (const u8 *) p->element->next;
+		if (ptr < element || element + size < ptr)
+			continue;
+		return true;
+	}
+	return false;
 }
 
-static void tomoyo_del_file_pattern(struct list_head *element)
-{
-	struct tomoyo_no_pattern *ptr =
-		container_of(element, typeof(*ptr), head.list);
-	tomoyo_put_name(ptr->pattern);
-}
-
-static void tomoyo_del_no_rewrite(struct list_head *element)
-{
-	struct tomoyo_no_rewrite *ptr =
-		container_of(element, typeof(*ptr), head.list);
-	tomoyo_put_name(ptr->pattern);
-}
-
+/**
+ * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_transition_control(struct list_head *element)
 {
 	struct tomoyo_transition_control *ptr =
@@ -61,6 +208,13 @@
 	tomoyo_put_name(ptr->program);
 }
 
+/**
+ * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_aggregator(struct list_head *element)
 {
 	struct tomoyo_aggregator *ptr =
@@ -69,6 +223,13 @@
 	tomoyo_put_name(ptr->aggregated_name);
 }
 
+/**
+ * tomoyo_del_manager - Delete members in "struct tomoyo_manager".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_manager(struct list_head *element)
 {
 	struct tomoyo_manager *ptr =
@@ -76,10 +237,18 @@
 	tomoyo_put_name(ptr->manager);
 }
 
+/**
+ * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_acl(struct list_head *element)
 {
 	struct tomoyo_acl_info *acl =
 		container_of(element, typeof(*acl), list);
+	tomoyo_put_condition(acl->cond);
 	switch (acl->type) {
 	case TOMOYO_TYPE_PATH_ACL:
 		{
@@ -127,6 +296,13 @@
 	}
 }
 
+/**
+ * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true if deleted, false otherwise.
+ */
 static bool tomoyo_del_domain(struct list_head *element)
 {
 	struct tomoyo_domain_info *domain =
@@ -165,13 +341,65 @@
 	return true;
 }
 
+/**
+ * tomoyo_del_condition - Delete members in "struct tomoyo_condition".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+void tomoyo_del_condition(struct list_head *element)
+{
+	struct tomoyo_condition *cond = container_of(element, typeof(*cond),
+						     head.list);
+	const u16 condc = cond->condc;
+	const u16 numbers_count = cond->numbers_count;
+	const u16 names_count = cond->names_count;
+	const u16 argc = cond->argc;
+	const u16 envc = cond->envc;
+	unsigned int i;
+	const struct tomoyo_condition_element *condp
+		= (const struct tomoyo_condition_element *) (cond + 1);
+	struct tomoyo_number_union *numbers_p
+		= (struct tomoyo_number_union *) (condp + condc);
+	struct tomoyo_name_union *names_p
+		= (struct tomoyo_name_union *) (numbers_p + numbers_count);
+	const struct tomoyo_argv *argv
+		= (const struct tomoyo_argv *) (names_p + names_count);
+	const struct tomoyo_envp *envp
+		= (const struct tomoyo_envp *) (argv + argc);
+	for (i = 0; i < numbers_count; i++)
+		tomoyo_put_number_union(numbers_p++);
+	for (i = 0; i < names_count; i++)
+		tomoyo_put_name_union(names_p++);
+	for (i = 0; i < argc; argv++, i++)
+		tomoyo_put_name(argv->value);
+	for (i = 0; i < envc; envp++, i++) {
+		tomoyo_put_name(envp->name);
+		tomoyo_put_name(envp->value);
+	}
+}
 
+/**
+ * tomoyo_del_name - Delete members in "struct tomoyo_name".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_name(struct list_head *element)
 {
 	const struct tomoyo_name *ptr =
-		container_of(element, typeof(*ptr), list);
+		container_of(element, typeof(*ptr), head.list);
 }
 
+/**
+ * tomoyo_del_path_group - Delete members in "struct tomoyo_path_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_path_group(struct list_head *element)
 {
 	struct tomoyo_path_group *member =
@@ -179,20 +407,43 @@
 	tomoyo_put_name(member->member_name);
 }
 
+/**
+ * tomoyo_del_group - Delete "struct tomoyo_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_group(struct list_head *element)
 {
 	struct tomoyo_group *group =
-		container_of(element, typeof(*group), list);
+		container_of(element, typeof(*group), head.list);
 	tomoyo_put_name(group->group_name);
 }
 
+/**
+ * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
 static void tomoyo_del_number_group(struct list_head *element)
 {
 	struct tomoyo_number_group *member =
 		container_of(element, typeof(*member), head.list);
 }
 
-static bool tomoyo_collect_member(struct list_head *member_list, int id)
+/**
+ * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head".
+ *
+ * @id:          One of values in "enum tomoyo_policy_id".
+ * @member_list: Pointer to "struct list_head".
+ *
+ * Returns true if some elements are deleted, false otherwise.
+ */
+static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
+				  struct list_head *member_list)
 {
 	struct tomoyo_acl_head *member;
 	list_for_each_entry(member, member_list, list) {
@@ -201,13 +452,20 @@
 		if (!tomoyo_add_to_gc(id, &member->list))
 			return false;
 	}
-        return true;
+	return true;
 }
 
-static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain)
+/**
+ * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info".
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns true if some elements are deleted, false otherwise.
+ */
+static bool tomoyo_collect_acl(struct list_head *list)
 {
 	struct tomoyo_acl_info *acl;
-	list_for_each_entry(acl, &domain->acl_info_list, list) {
+	list_for_each_entry(acl, list, list) {
 		if (!acl->is_deleted)
 			continue;
 		if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
@@ -216,19 +474,24 @@
 	return true;
 }
 
+/**
+ * tomoyo_collect_entry - Scan lists for deleted elements.
+ *
+ * Returns nothing.
+ */
 static void tomoyo_collect_entry(void)
 {
 	int i;
+	enum tomoyo_policy_id id;
+	struct tomoyo_policy_namespace *ns;
+	int idx;
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return;
-	for (i = 0; i < TOMOYO_MAX_POLICY; i++) {
-		if (!tomoyo_collect_member(&tomoyo_policy_list[i], i))
-			goto unlock;
-	}
+	idx = tomoyo_read_lock();
 	{
 		struct tomoyo_domain_info *domain;
 		list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
-			if (!tomoyo_collect_acl(domain))
+			if (!tomoyo_collect_acl(&domain->acl_info_list))
 				goto unlock;
 			if (!domain->is_deleted || atomic_read(&domain->users))
 				continue;
@@ -241,48 +504,93 @@
 				goto unlock;
 		}
 	}
-	for (i = 0; i < TOMOYO_MAX_HASH; i++) {
-		struct tomoyo_name *ptr;
-		list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) {
+	list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
+		for (id = 0; id < TOMOYO_MAX_POLICY; id++)
+			if (!tomoyo_collect_member(id, &ns->policy_list[id]))
+				goto unlock;
+		for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
+			if (!tomoyo_collect_acl(&ns->acl_group[i]))
+				goto unlock;
+		for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
+			struct list_head *list = &ns->group_list[i];
+			struct tomoyo_group *group;
+			switch (i) {
+			case 0:
+				id = TOMOYO_ID_PATH_GROUP;
+				break;
+			default:
+				id = TOMOYO_ID_NUMBER_GROUP;
+				break;
+			}
+			list_for_each_entry(group, list, head.list) {
+				if (!tomoyo_collect_member
+				    (id, &group->member_list))
+					goto unlock;
+				if (!list_empty(&group->member_list) ||
+				    atomic_read(&group->head.users))
+					continue;
+				if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
+						      &group->head.list))
+					goto unlock;
+			}
+		}
+	}
+	id = TOMOYO_ID_CONDITION;
+	for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) {
+		struct list_head *list = !i ?
+			&tomoyo_condition_list : &tomoyo_name_list[i - 1];
+		struct tomoyo_shared_acl_head *ptr;
+		list_for_each_entry(ptr, list, list) {
 			if (atomic_read(&ptr->users))
 				continue;
-			if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list))
+			if (!tomoyo_add_to_gc(id, &ptr->list))
 				goto unlock;
 		}
+		id = TOMOYO_ID_NAME;
 	}
-	for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
-		struct list_head *list = &tomoyo_group_list[i];
-		int id;
-		struct tomoyo_group *group;
-		switch (i) {
-		case 0:
-			id = TOMOYO_ID_PATH_GROUP;
-			break;
-		default:
-			id = TOMOYO_ID_NUMBER_GROUP;
-			break;
-		}
-		list_for_each_entry(group, list, list) {
-			if (!tomoyo_collect_member(&group->member_list, id))
-				goto unlock;
-			if (!list_empty(&group->member_list) ||
-			    atomic_read(&group->users))
-				continue;
-			if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list))
-				goto unlock;
-		}
-	}
- unlock:
+unlock:
+	tomoyo_read_unlock(idx);
 	mutex_unlock(&tomoyo_policy_lock);
 }
 
-static void tomoyo_kfree_entry(void)
+/**
+ * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
+ *
+ * Returns true if some entries were kfree()d, false otherwise.
+ */
+static bool tomoyo_kfree_entry(void)
 {
 	struct tomoyo_gc *p;
 	struct tomoyo_gc *tmp;
+	bool result = false;
 
-	list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+	list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
 		struct list_head *element = p->element;
+
+		/*
+		 * list_del_rcu() in tomoyo_add_to_gc() guarantees that the
+		 * list element became no longer reachable from the list which
+		 * the element was originally on (e.g. tomoyo_domain_list).
+		 * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
+		 * that the list element became no longer referenced by syscall
+		 * users.
+		 *
+		 * However, there are three users which may still be using the
+		 * list element. We need to defer until all of these users
+		 * forget the list element.
+		 *
+		 * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
+		 * group,acl} and "struct tomoyo_io_buffer"->w.domain forget
+		 * the list element.
+		 */
+		if (tomoyo_struct_used_by_io_buffer(element))
+			continue;
+		/*
+		 * Secondly, defer until all other elements in the
+		 * tomoyo_gc_list list forget the list element.
+		 */
+		if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
+			continue;
 		switch (p->type) {
 		case TOMOYO_ID_TRANSITION_CONTROL:
 			tomoyo_del_transition_control(element);
@@ -290,19 +598,21 @@
 		case TOMOYO_ID_AGGREGATOR:
 			tomoyo_del_aggregator(element);
 			break;
-		case TOMOYO_ID_GLOBALLY_READABLE:
-			tomoyo_del_allow_read(element);
-			break;
-		case TOMOYO_ID_PATTERN:
-			tomoyo_del_file_pattern(element);
-			break;
-		case TOMOYO_ID_NO_REWRITE:
-			tomoyo_del_no_rewrite(element);
-			break;
 		case TOMOYO_ID_MANAGER:
 			tomoyo_del_manager(element);
 			break;
+		case TOMOYO_ID_CONDITION:
+			tomoyo_del_condition(element);
+			break;
 		case TOMOYO_ID_NAME:
+			/*
+			 * Thirdly, defer until all "struct tomoyo_io_buffer"
+			 * ->r.w[] forget the list element.
+			 */
+			if (tomoyo_name_used_by_io_buffer(
+			    container_of(element, typeof(struct tomoyo_name),
+					 head.list)->entry.name, p->size))
+				continue;
 			tomoyo_del_name(element);
 			break;
 		case TOMOYO_ID_ACL:
@@ -321,34 +631,95 @@
 		case TOMOYO_ID_NUMBER_GROUP:
 			tomoyo_del_number_group(element);
 			break;
+		case TOMOYO_MAX_POLICY:
+			break;
 		}
 		tomoyo_memory_free(element);
 		list_del(&p->list);
 		kfree(p);
+		tomoyo_gc_list_len--;
+		result = true;
 	}
+	return result;
 }
 
+/**
+ * tomoyo_gc_thread - Garbage collector thread function.
+ *
+ * @unused: Unused.
+ *
+ * In case OOM-killer choose this thread for termination, we create this thread
+ * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
+ * close()d.
+ *
+ * Returns 0.
+ */
 static int tomoyo_gc_thread(void *unused)
 {
+	/* Garbage collector thread is exclusive. */
+	static DEFINE_MUTEX(tomoyo_gc_mutex);
+	if (!mutex_trylock(&tomoyo_gc_mutex))
+		goto out;
 	daemonize("GC for TOMOYO");
-	if (mutex_trylock(&tomoyo_gc_mutex)) {
-		int i;
-		for (i = 0; i < 10; i++) {
-			tomoyo_collect_entry();
-			if (list_empty(&tomoyo_gc_queue))
-				break;
-			synchronize_srcu(&tomoyo_ss);
-			tomoyo_kfree_entry();
+	do {
+		tomoyo_collect_entry();
+		if (list_empty(&tomoyo_gc_list))
+			break;
+		synchronize_srcu(&tomoyo_ss);
+	} while (tomoyo_kfree_entry());
+	{
+		struct tomoyo_io_buffer *head;
+		struct tomoyo_io_buffer *tmp;
+
+		spin_lock(&tomoyo_io_buffer_list_lock);
+		list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list,
+					 list) {
+			if (head->users)
+				continue;
+			list_del(&head->list);
+			kfree(head->read_buf);
+			kfree(head->write_buf);
+			kfree(head);
 		}
-		mutex_unlock(&tomoyo_gc_mutex);
+		spin_unlock(&tomoyo_io_buffer_list_lock);
 	}
-	do_exit(0);
+	mutex_unlock(&tomoyo_gc_mutex);
+out:
+	/* This acts as do_exit(0). */
+	return 0;
 }
 
-void tomoyo_run_gc(void)
+/**
+ * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users.
+ *
+ * @head:        Pointer to "struct tomoyo_io_buffer".
+ * @is_register: True if register, false if unregister.
+ *
+ * Returns nothing.
+ */
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
 {
-	struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
-						  "GC for TOMOYO");
-	if (!IS_ERR(task))
-		wake_up_process(task);
+	bool is_write = false;
+
+	spin_lock(&tomoyo_io_buffer_list_lock);
+	if (is_register) {
+		head->users = 1;
+		list_add(&head->list, &tomoyo_io_buffer_list);
+	} else {
+		is_write = head->write_buf != NULL;
+		if (!--head->users) {
+			list_del(&head->list);
+			kfree(head->read_buf);
+			kfree(head->write_buf);
+			kfree(head);
+		}
+	}
+	spin_unlock(&tomoyo_io_buffer_list_lock);
+	if (is_write) {
+		struct task_struct *task = kthread_create(tomoyo_gc_thread,
+							  NULL,
+							  "GC for TOMOYO");
+		if (!IS_ERR(task))
+			wake_up_process(task);
+	}
 }
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index e94352c..5fb0e12 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -1,21 +1,37 @@
 /*
  * security/tomoyo/group.c
  *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/slab.h>
 #include "common.h"
 
+/**
+ * tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a,
-				const struct tomoyo_acl_head *b)
+				   const struct tomoyo_acl_head *b)
 {
 	return container_of(a, struct tomoyo_path_group, head)->member_name ==
 		container_of(b, struct tomoyo_path_group, head)->member_name;
 }
 
+/**
+ * tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
 static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
-				  const struct tomoyo_acl_head *b)
+				     const struct tomoyo_acl_head *b)
 {
 	return !memcmp(&container_of(a, struct tomoyo_number_group, head)
 		       ->number,
@@ -28,48 +44,41 @@
 /**
  * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
  *
- * @data:      String to parse.
- * @is_delete: True if it is a delete request.
- * @type:      Type of this group.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @type:  Type of this group.
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
 {
-	struct tomoyo_group *group;
-	struct list_head *member;
-	char *w[2];
+	struct tomoyo_group *group = tomoyo_get_group(param, type);
 	int error = -EINVAL;
-	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
-		return -EINVAL;
-	group = tomoyo_get_group(w[0], type);
 	if (!group)
 		return -ENOMEM;
-	member = &group->member_list;
+	param->list = &group->member_list;
 	if (type == TOMOYO_PATH_GROUP) {
 		struct tomoyo_path_group e = { };
-		e.member_name = tomoyo_get_name(w[1]);
+		e.member_name = tomoyo_get_name(tomoyo_read_token(param));
 		if (!e.member_name) {
 			error = -ENOMEM;
 			goto out;
 		}
-		error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-					     member, tomoyo_same_path_group);
+		error = tomoyo_update_policy(&e.head, sizeof(e), param,
+					  tomoyo_same_path_group);
 		tomoyo_put_name(e.member_name);
 	} else if (type == TOMOYO_NUMBER_GROUP) {
 		struct tomoyo_number_group e = { };
-		if (w[1][0] == '@'
-		    || !tomoyo_parse_number_union(w[1], &e.number)
-		    || e.number.values[0] > e.number.values[1])
+		if (param->data[0] == '@' ||
+		    !tomoyo_parse_number_union(param, &e.number))
 			goto out;
-		error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
-					     member, tomoyo_same_number_group);
+		error = tomoyo_update_policy(&e.head, sizeof(e), param,
+					  tomoyo_same_number_group);
 		/*
 		 * tomoyo_put_number_union() is not needed because
-		 * w[1][0] != '@'.
+		 * param->data[0] != '@'.
 		 */
 	}
- out:
+out:
 	tomoyo_put_group(group);
 	return error;
 }
@@ -77,8 +86,8 @@
 /**
  * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
  *
- * @pathname:        The name of pathname.
- * @group:           Pointer to "struct tomoyo_path_group".
+ * @pathname: The name of pathname.
+ * @group:    Pointer to "struct tomoyo_path_group".
  *
  * Returns matched member's pathname if @pathname matches pathnames in @group,
  * NULL otherwise.
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c
index 3312e56..6797540 100644
--- a/security/tomoyo/load_policy.c
+++ b/security/tomoyo/load_policy.c
@@ -1,15 +1,32 @@
 /*
  * security/tomoyo/load_policy.c
  *
- * Policy loader launcher for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include "common.h"
 
-/* path to policy loader */
-static const char *tomoyo_loader = "/sbin/tomoyo-init";
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+
+/*
+ * Path to the policy loader. (default = CONFIG_SECURITY_TOMOYO_POLICY_LOADER)
+ */
+static const char *tomoyo_loader;
+
+/**
+ * tomoyo_loader_setup - Set policy loader.
+ *
+ * @str: Program to use as a policy loader (e.g. /sbin/tomoyo-init ).
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_loader_setup(char *str)
+{
+	tomoyo_loader = str;
+	return 0;
+}
+
+__setup("TOMOYO_loader=", tomoyo_loader_setup);
 
 /**
  * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
@@ -18,24 +35,38 @@
  */
 static bool tomoyo_policy_loader_exists(void)
 {
-	/*
-	 * Don't activate MAC if the policy loader doesn't exist.
-	 * If the initrd includes /sbin/init but real-root-dev has not
-	 * mounted on / yet, activating MAC will block the system since
-	 * policies are not loaded yet.
-	 * Thus, let do_execve() call this function every time.
-	 */
 	struct path path;
-
+	if (!tomoyo_loader)
+		tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
 	if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
-		printk(KERN_INFO "Not activating Mandatory Access Control now "
-		       "since %s doesn't exist.\n", tomoyo_loader);
+		printk(KERN_INFO "Not activating Mandatory Access Control "
+		       "as %s does not exist.\n", tomoyo_loader);
 		return false;
 	}
 	path_put(&path);
 	return true;
 }
 
+/*
+ * Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER)
+ */
+static const char *tomoyo_trigger;
+
+/**
+ * tomoyo_trigger_setup - Set trigger for activation.
+ *
+ * @str: Program to use as an activation trigger (e.g. /sbin/init ).
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_trigger_setup(char *str)
+{
+	tomoyo_trigger = str;
+	return 0;
+}
+
+__setup("TOMOYO_trigger=", tomoyo_trigger_setup);
+
 /**
  * tomoyo_load_policy - Run external policy loader to load policy.
  *
@@ -51,24 +82,19 @@
  */
 void tomoyo_load_policy(const char *filename)
 {
+	static bool done;
 	char *argv[2];
 	char *envp[3];
 
-	if (tomoyo_policy_loaded)
+	if (tomoyo_policy_loaded || done)
 		return;
-	/*
-	 * Check filename is /sbin/init or /sbin/tomoyo-start.
-	 * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
-	 * be passed.
-	 * You can create /sbin/tomoyo-start by
-	 * "ln -s /bin/true /sbin/tomoyo-start".
-	 */
-	if (strcmp(filename, "/sbin/init") &&
-	    strcmp(filename, "/sbin/tomoyo-start"))
+	if (!tomoyo_trigger)
+		tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER;
+	if (strcmp(filename, tomoyo_trigger))
 		return;
 	if (!tomoyo_policy_loader_exists())
 		return;
-
+	done = true;
 	printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
 	       tomoyo_loader);
 	argv[0] = (char *) tomoyo_loader;
@@ -79,3 +105,5 @@
 	call_usermodehelper(argv[0], argv, envp, 1);
 	tomoyo_check_profile();
 }
+
+#endif
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
index 42a7b1b..7a56051 100644
--- a/security/tomoyo/memory.c
+++ b/security/tomoyo/memory.c
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/memory.c
  *
- * Memory management functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/hash.h>
@@ -29,10 +27,12 @@
 		panic("MAC Initialization failed.\n");
 }
 
-/* Memory allocated for policy. */
-static atomic_t tomoyo_policy_memory_size;
-/* Quota for holding policy. */
-static unsigned int tomoyo_quota_for_policy;
+/* Lock for protecting tomoyo_memory_used. */
+static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
+/* Memoy currently used by policy/audit log/query. */
+unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
+/* Memory quota for "policy"/"audit log"/"query". */
+unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
 
 /**
  * tomoyo_memory_ok - Check memory quota.
@@ -45,15 +45,20 @@
  */
 bool tomoyo_memory_ok(void *ptr)
 {
-	size_t s = ptr ? ksize(ptr) : 0;
-	atomic_add(s, &tomoyo_policy_memory_size);
-	if (ptr && (!tomoyo_quota_for_policy ||
-		    atomic_read(&tomoyo_policy_memory_size)
-		    <= tomoyo_quota_for_policy)) {
-		memset(ptr, 0, s);
-		return true;
+	if (ptr) {
+		const size_t s = ksize(ptr);
+		bool result;
+		spin_lock(&tomoyo_policy_memory_lock);
+		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
+		result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
+			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
+			tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
+		if (!result)
+			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
+		spin_unlock(&tomoyo_policy_memory_lock);
+		if (result)
+			return true;
 	}
-	atomic_sub(s, &tomoyo_policy_memory_size);
 	tomoyo_warn_oom(__func__);
 	return false;
 }
@@ -86,22 +91,28 @@
  */
 void tomoyo_memory_free(void *ptr)
 {
-	atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
+	size_t s = ksize(ptr);
+	spin_lock(&tomoyo_policy_memory_lock);
+	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
+	spin_unlock(&tomoyo_policy_memory_lock);
 	kfree(ptr);
 }
 
 /**
  * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
  *
- * @group_name: The name of address group.
- * @idx:        Index number.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @idx:   Index number.
  *
  * Returns pointer to "struct tomoyo_group" on success, NULL otherwise.
  */
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+				      const u8 idx)
 {
 	struct tomoyo_group e = { };
 	struct tomoyo_group *group = NULL;
+	struct list_head *list;
+	const char *group_name = tomoyo_read_token(param);
 	bool found = false;
 	if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
 		return NULL;
@@ -110,10 +121,11 @@
 		return NULL;
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		goto out;
-	list_for_each_entry(group, &tomoyo_group_list[idx], list) {
+	list = &param->ns->group_list[idx];
+	list_for_each_entry(group, list, head.list) {
 		if (e.group_name != group->group_name)
 			continue;
-		atomic_inc(&group->users);
+		atomic_inc(&group->head.users);
 		found = true;
 		break;
 	}
@@ -121,15 +133,14 @@
 		struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
 		if (entry) {
 			INIT_LIST_HEAD(&entry->member_list);
-			atomic_set(&entry->users, 1);
-			list_add_tail_rcu(&entry->list,
-					  &tomoyo_group_list[idx]);
+			atomic_set(&entry->head.users, 1);
+			list_add_tail_rcu(&entry->head.list, list);
 			group = entry;
 			found = true;
 		}
 	}
 	mutex_unlock(&tomoyo_policy_lock);
- out:
+out:
 	tomoyo_put_name(e.group_name);
 	return found ? group : NULL;
 }
@@ -154,7 +165,6 @@
 	struct tomoyo_name *ptr;
 	unsigned int hash;
 	int len;
-	int allocated_len;
 	struct list_head *head;
 
 	if (!name)
@@ -164,120 +174,43 @@
 	head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return NULL;
-	list_for_each_entry(ptr, head, list) {
+	list_for_each_entry(ptr, head, head.list) {
 		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
 			continue;
-		atomic_inc(&ptr->users);
+		atomic_inc(&ptr->head.users);
 		goto out;
 	}
 	ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
-	allocated_len = ptr ? ksize(ptr) : 0;
-	if (!ptr || (tomoyo_quota_for_policy &&
-		     atomic_read(&tomoyo_policy_memory_size) + allocated_len
-		     > tomoyo_quota_for_policy)) {
+	if (tomoyo_memory_ok(ptr)) {
+		ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+		memmove((char *) ptr->entry.name, name, len);
+		atomic_set(&ptr->head.users, 1);
+		tomoyo_fill_path_info(&ptr->entry);
+		list_add_tail(&ptr->head.list, head);
+	} else {
 		kfree(ptr);
 		ptr = NULL;
-		tomoyo_warn_oom(__func__);
-		goto out;
 	}
-	atomic_add(allocated_len, &tomoyo_policy_memory_size);
-	ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
-	memmove((char *) ptr->entry.name, name, len);
-	atomic_set(&ptr->users, 1);
-	tomoyo_fill_path_info(&ptr->entry);
-	list_add_tail(&ptr->list, head);
- out:
+out:
 	mutex_unlock(&tomoyo_policy_lock);
 	return ptr ? &ptr->entry : NULL;
 }
 
+/* Initial namespace.*/
+struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+
 /**
  * tomoyo_mm_init - Initialize mm related code.
  */
 void __init tomoyo_mm_init(void)
 {
 	int idx;
-
-	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
-		INIT_LIST_HEAD(&tomoyo_policy_list[idx]);
-	for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
-		INIT_LIST_HEAD(&tomoyo_group_list[idx]);
 	for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
 		INIT_LIST_HEAD(&tomoyo_name_list[idx]);
+	tomoyo_kernel_namespace.name = "<kernel>";
+	tomoyo_init_policy_namespace(&tomoyo_kernel_namespace);
+	tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;
 	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
-	tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+	tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");
 	list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
-	idx = tomoyo_read_lock();
-	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
-		panic("Can't register tomoyo_kernel_domain");
-	{
-		/* Load built-in policy. */
-		tomoyo_write_transition_control("/sbin/hotplug", false,
-					TOMOYO_TRANSITION_CONTROL_INITIALIZE);
-		tomoyo_write_transition_control("/sbin/modprobe", false,
-					TOMOYO_TRANSITION_CONTROL_INITIALIZE);
-	}
-	tomoyo_read_unlock(idx);
-}
-
-
-/* Memory allocated for query lists. */
-unsigned int tomoyo_query_memory_size;
-/* Quota for holding query lists. */
-unsigned int tomoyo_quota_for_query;
-
-/**
- * tomoyo_read_memory_counter - Check for memory usage in bytes.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns memory usage.
- */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
-{
-	if (!head->r.eof) {
-		const unsigned int policy
-			= atomic_read(&tomoyo_policy_memory_size);
-		const unsigned int query = tomoyo_query_memory_size;
-		char buffer[64];
-
-		memset(buffer, 0, sizeof(buffer));
-		if (tomoyo_quota_for_policy)
-			snprintf(buffer, sizeof(buffer) - 1,
-				 "   (Quota: %10u)",
-				 tomoyo_quota_for_policy);
-		else
-			buffer[0] = '\0';
-		tomoyo_io_printf(head, "Policy:       %10u%s\n", policy,
-				 buffer);
-		if (tomoyo_quota_for_query)
-			snprintf(buffer, sizeof(buffer) - 1,
-				 "   (Quota: %10u)",
-				 tomoyo_quota_for_query);
-		else
-			buffer[0] = '\0';
-		tomoyo_io_printf(head, "Query lists:  %10u%s\n", query,
-				 buffer);
-		tomoyo_io_printf(head, "Total:        %10u\n", policy + query);
-		head->r.eof = true;
-	}
-}
-
-/**
- * tomoyo_write_memory_quota - Set memory quota.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0.
- */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
-{
-	char *data = head->write_buf;
-	unsigned int size;
-
-	if (sscanf(data, "Policy: %u", &size) == 1)
-		tomoyo_quota_for_policy = size;
-	else if (sscanf(data, "Query lists: %u", &size) == 1)
-		tomoyo_quota_for_query = size;
-	return 0;
 }
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 9fc2e15..bee09d0 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -1,28 +1,22 @@
 /*
  * security/tomoyo/mount.c
  *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/slab.h>
 #include "common.h"
 
-/* Keywords for mount restrictions. */
-
-/* Allow to call 'mount --bind /source_dir /dest_dir' */
-#define TOMOYO_MOUNT_BIND_KEYWORD                        "--bind"
-/* Allow to call 'mount --move /old_dir    /new_dir ' */
-#define TOMOYO_MOUNT_MOVE_KEYWORD                        "--move"
-/* Allow to call 'mount -o remount /dir             ' */
-#define TOMOYO_MOUNT_REMOUNT_KEYWORD                     "--remount"
-/* Allow to call 'mount --make-unbindable /dir'       */
-#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD             "--make-unbindable"
-/* Allow to call 'mount --make-private /dir'          */
-#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD                "--make-private"
-/* Allow to call 'mount --make-slave /dir'            */
-#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD                  "--make-slave"
-/* Allow to call 'mount --make-shared /dir'           */
-#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD                 "--make-shared"
+/* String table for special mount operations. */
+static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = {
+	[TOMOYO_MOUNT_BIND]            = "--bind",
+	[TOMOYO_MOUNT_MOVE]            = "--move",
+	[TOMOYO_MOUNT_REMOUNT]         = "--remount",
+	[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable",
+	[TOMOYO_MOUNT_MAKE_PRIVATE]    = "--make-private",
+	[TOMOYO_MOUNT_MAKE_SLAVE]      = "--make-slave",
+	[TOMOYO_MOUNT_MAKE_SHARED]     = "--make-shared",
+};
 
 /**
  * tomoyo_audit_mount_log - Audit mount log.
@@ -33,50 +27,42 @@
  */
 static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
 {
-	const char *dev = r->param.mount.dev->name;
-	const char *dir = r->param.mount.dir->name;
-	const char *type = r->param.mount.type->name;
-	const unsigned long flags = r->param.mount.flags;
-	if (r->granted)
-		return 0;
-	if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD))
-		tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags);
-	else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD)
-		 || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD))
-		tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir,
-				flags);
-	else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
-		 !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
-		 !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
-		 !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD))
-		tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags);
-	else
-		tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir,
-				flags);
-	return tomoyo_supervisor(r,
-				 TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n",
-				 tomoyo_pattern(r->param.mount.dev),
-				 tomoyo_pattern(r->param.mount.dir), type,
-				 flags);
+	return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n",
+				 r->param.mount.dev->name,
+				 r->param.mount.dir->name,
+				 r->param.mount.type->name,
+				 r->param.mount.flags);
 }
 
+/**
+ * tomoyo_check_mount_acl - Check permission for path path path number operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
 static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
 				   const struct tomoyo_acl_info *ptr)
 {
 	const struct tomoyo_mount_acl *acl =
 		container_of(ptr, typeof(*acl), head);
-	return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) &&
-		tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) &&
-		tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) &&
+	return tomoyo_compare_number_union(r->param.mount.flags,
+					   &acl->flags) &&
+		tomoyo_compare_name_union(r->param.mount.type,
+					  &acl->fs_type) &&
+		tomoyo_compare_name_union(r->param.mount.dir,
+					  &acl->dir_name) &&
 		(!r->param.mount.need_dev ||
-		 tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name));
+		 tomoyo_compare_name_union(r->param.mount.dev,
+					   &acl->dev_name));
 }
 
 /**
  * tomoyo_mount_acl - Check permission for mount() operation.
  *
  * @r:        Pointer to "struct tomoyo_request_info".
- * @dev_name: Name of device file.
+ * @dev_name: Name of device file. Maybe NULL.
  * @dir:      Pointer to "struct path".
  * @type:     Name of filesystem type.
  * @flags:    Mount options.
@@ -86,8 +72,10 @@
  * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
-			    struct path *dir, char *type, unsigned long flags)
+			    struct path *dir, const char *type,
+			    unsigned long flags)
 {
+	struct tomoyo_obj_info obj = { };
 	struct path path;
 	struct file_system_type *fstype = NULL;
 	const char *requested_type = NULL;
@@ -98,6 +86,7 @@
 	struct tomoyo_path_info rdir;
 	int need_dev = 0;
 	int error = -ENOMEM;
+	r->obj = &obj;
 
 	/* Get fstype. */
 	requested_type = tomoyo_encode(type);
@@ -107,6 +96,7 @@
 	tomoyo_fill_path_info(&rtype);
 
 	/* Get mount point. */
+	obj.path2 = *dir;
 	requested_dir_name = tomoyo_realpath_from_path(dir);
 	if (!requested_dir_name) {
 		error = -ENOMEM;
@@ -116,15 +106,15 @@
 	tomoyo_fill_path_info(&rdir);
 
 	/* Compare fs name. */
-	if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) {
+	if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {
 		/* dev_name is ignored. */
-	} else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
-		   !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
-		   !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
-		   !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) {
+	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] ||
+		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] ||
+		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] ||
+		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {
 		/* dev_name is ignored. */
-	} else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) ||
-		   !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) {
+	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
+		   type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
 		need_dev = -1; /* dev_name is a directory */
 	} else {
 		fstype = get_fs_type(type);
@@ -142,8 +132,8 @@
 			error = -ENOENT;
 			goto out;
 		}
+		obj.path1 = path;
 		requested_dev_name = tomoyo_realpath_from_path(&path);
-		path_put(&path);
 		if (!requested_dev_name) {
 			error = -ENOENT;
 			goto out;
@@ -176,22 +166,26 @@
 	if (fstype)
 		put_filesystem(fstype);
 	kfree(requested_type);
+	/* Drop refcount obtained by kern_path(). */
+	if (obj.path1.dentry)
+		path_put(&obj.path1);
 	return error;
 }
 
 /**
  * tomoyo_mount_permission - Check permission for mount() operation.
  *
- * @dev_name:  Name of device file.
+ * @dev_name:  Name of device file. Maybe NULL.
  * @path:      Pointer to "struct path".
- * @type:      Name of filesystem type. May be NULL.
+ * @type:      Name of filesystem type. Maybe NULL.
  * @flags:     Mount options.
- * @data_page: Optional data. May be NULL.
+ * @data_page: Optional data. Maybe NULL.
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
-			    unsigned long flags, void *data_page)
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+			    const char *type, unsigned long flags,
+			    void *data_page)
 {
 	struct tomoyo_request_info r;
 	int error;
@@ -203,31 +197,31 @@
 	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
 		flags &= ~MS_MGC_MSK;
 	if (flags & MS_REMOUNT) {
-		type = TOMOYO_MOUNT_REMOUNT_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];
 		flags &= ~MS_REMOUNT;
 	}
 	if (flags & MS_MOVE) {
-		type = TOMOYO_MOUNT_MOVE_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_MOVE];
 		flags &= ~MS_MOVE;
 	}
 	if (flags & MS_BIND) {
-		type = TOMOYO_MOUNT_BIND_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_BIND];
 		flags &= ~MS_BIND;
 	}
 	if (flags & MS_UNBINDABLE) {
-		type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE];
 		flags &= ~MS_UNBINDABLE;
 	}
 	if (flags & MS_PRIVATE) {
-		type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];
 		flags &= ~MS_PRIVATE;
 	}
 	if (flags & MS_SLAVE) {
-		type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];
 		flags &= ~MS_SLAVE;
 	}
 	if (flags & MS_SHARED) {
-		type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD;
+		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED];
 		flags &= ~MS_SHARED;
 	}
 	if (!type)
@@ -237,49 +231,3 @@
 	tomoyo_read_unlock(idx);
 	return error;
 }
-
-static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
-				  const struct tomoyo_acl_info *b)
-{
-	const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
-	const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
-	return tomoyo_same_acl_head(&p1->head, &p2->head) &&
-		tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
-		tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
-		tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
-		tomoyo_same_number_union(&p1->flags, &p2->flags);
-}
-
-/**
- * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list.
- *
- * @data:      String to parse.
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
-		       const bool is_delete)
-{
-	struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
-	int error = is_delete ? -ENOENT : -ENOMEM;
-	char *w[4];
-	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
-		return -EINVAL;
-	if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
-	    !tomoyo_parse_name_union(w[1], &e.dir_name) ||
-	    !tomoyo_parse_name_union(w[2], &e.fs_type) ||
-	    !tomoyo_parse_number_union(w[3], &e.flags))
-		goto out;
-	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
-				     tomoyo_same_mount_acl, NULL);
- out:
-	tomoyo_put_name_union(&e.dev_name);
-	tomoyo_put_name_union(&e.dir_name);
-	tomoyo_put_name_union(&e.fs_type);
-	tomoyo_put_number_union(&e.flags);
-	return error;
-}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 8d95e91..6c601bd 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/realpath.c
  *
- * Pathname calculation functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/types.h>
@@ -70,6 +68,161 @@
 }
 
 /**
+ * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
+ *
+ * @path:   Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
+				      const int buflen)
+{
+	char *pos = ERR_PTR(-ENOMEM);
+	if (buflen >= 256) {
+		struct path ns_root = { };
+		/* go to whatever namespace root we are under */
+		pos = __d_path(path, &ns_root, buffer, buflen - 1);
+		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+			struct inode *inode = path->dentry->d_inode;
+			if (inode && S_ISDIR(inode->i_mode)) {
+				buffer[buflen - 2] = '/';
+				buffer[buflen - 1] = '\0';
+			}
+		}
+	}
+	return pos;
+}
+
+/**
+ * tomoyo_get_dentry_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
+				    const int buflen)
+{
+	char *pos = ERR_PTR(-ENOMEM);
+	if (buflen >= 256) {
+		pos = dentry_path_raw(dentry, buffer, buflen - 1);
+		if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+			struct inode *inode = dentry->d_inode;
+			if (inode && S_ISDIR(inode->i_mode)) {
+				buffer[buflen - 2] = '/';
+				buffer[buflen - 1] = '\0';
+			}
+		}
+	}
+	return pos;
+}
+
+/**
+ * tomoyo_get_local_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ */
+static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
+				   const int buflen)
+{
+	struct super_block *sb = dentry->d_sb;
+	char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
+	if (IS_ERR(pos))
+		return pos;
+	/* Convert from $PID to self if $PID is current thread. */
+	if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
+		char *ep;
+		const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
+		if (*ep == '/' && pid && pid ==
+		    task_tgid_nr_ns(current, sb->s_fs_info)) {
+			pos = ep - 5;
+			if (pos < buffer)
+				goto out;
+			memmove(pos, "/self", 5);
+		}
+		goto prepend_filesystem_name;
+	}
+	/* Use filesystem name for unnamed devices. */
+	if (!MAJOR(sb->s_dev))
+		goto prepend_filesystem_name;
+	{
+		struct inode *inode = sb->s_root->d_inode;
+		/*
+		 * Use filesystem name if filesystem does not support rename()
+		 * operation.
+		 */
+		if (inode->i_op && !inode->i_op->rename)
+			goto prepend_filesystem_name;
+	}
+	/* Prepend device name. */
+	{
+		char name[64];
+		int name_len;
+		const dev_t dev = sb->s_dev;
+		name[sizeof(name) - 1] = '\0';
+		snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
+			 MINOR(dev));
+		name_len = strlen(name);
+		pos -= name_len;
+		if (pos < buffer)
+			goto out;
+		memmove(pos, name, name_len);
+		return pos;
+	}
+	/* Prepend filesystem name. */
+prepend_filesystem_name:
+	{
+		const char *name = sb->s_type->name;
+		const int name_len = strlen(name);
+		pos -= name_len + 1;
+		if (pos < buffer)
+			goto out;
+		memmove(pos, name, name_len);
+		pos[name_len] = ':';
+	}
+	return pos;
+out:
+	return ERR_PTR(-ENOMEM);
+}
+
+/**
+ * tomoyo_get_socket_name - Get the name of a socket.
+ *
+ * @path:   Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer.
+ */
+static char *tomoyo_get_socket_name(struct path *path, char * const buffer,
+				    const int buflen)
+{
+	struct inode *inode = path->dentry->d_inode;
+	struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+	struct sock *sk = sock ? sock->sk : NULL;
+	if (sk) {
+		snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
+			 "protocol=%u]", sk->sk_family, sk->sk_type,
+			 sk->sk_protocol);
+	} else {
+		snprintf(buffer, buflen, "socket:[unknown]");
+	}
+	return buffer;
+}
+
+/**
  * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
  *
  * @path: Pointer to "struct path".
@@ -90,55 +243,42 @@
 	char *name = NULL;
 	unsigned int buf_len = PAGE_SIZE / 2;
 	struct dentry *dentry = path->dentry;
-	bool is_dir;
+	struct super_block *sb;
 	if (!dentry)
 		return NULL;
-	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
+	sb = dentry->d_sb;
 	while (1) {
-		struct path ns_root = { .mnt = NULL, .dentry = NULL };
 		char *pos;
+		struct inode *inode;
 		buf_len <<= 1;
 		kfree(buf);
 		buf = kmalloc(buf_len, GFP_NOFS);
 		if (!buf)
 			break;
+		/* To make sure that pos is '\0' terminated. */
+		buf[buf_len - 1] = '\0';
 		/* Get better name for socket. */
-		if (dentry->d_sb->s_magic == SOCKFS_MAGIC) {
-			struct inode *inode = dentry->d_inode;
-			struct socket *sock = inode ? SOCKET_I(inode) : NULL;
-			struct sock *sk = sock ? sock->sk : NULL;
-			if (sk) {
-				snprintf(buf, buf_len - 1, "socket:[family=%u:"
-					 "type=%u:protocol=%u]", sk->sk_family,
-					 sk->sk_type, sk->sk_protocol);
-			} else {
-				snprintf(buf, buf_len - 1, "socket:[unknown]");
-			}
-			name = tomoyo_encode(buf);
-			break;
+		if (sb->s_magic == SOCKFS_MAGIC) {
+			pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
+			goto encode;
 		}
-		/* For "socket:[\$]" and "pipe:[\$]". */
+		/* For "pipe:[\$]". */
 		if (dentry->d_op && dentry->d_op->d_dname) {
 			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
-			if (IS_ERR(pos))
-				continue;
-			name = tomoyo_encode(pos);
-			break;
+			goto encode;
 		}
-		/* If we don't have a vfsmount, we can't calculate. */
-		if (!path->mnt)
-			break;
-		/* go to whatever namespace root we are under */
-		pos = __d_path(path, &ns_root, buf, buf_len);
-		/* Prepend "/proc" prefix if using internal proc vfs mount. */
-		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
-		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
-			pos -= 5;
-			if (pos >= buf)
-				memcpy(pos, "/proc", 5);
-			else
-				pos = ERR_PTR(-ENOMEM);
-		}
+		inode = sb->s_root->d_inode;
+		/*
+		 * Get local name for filesystems without rename() operation
+		 * or dentry without vfsmount.
+		 */
+		if (!path->mnt || (inode->i_op && !inode->i_op->rename))
+			pos = tomoyo_get_local_path(path->dentry, buf,
+						    buf_len - 1);
+		/* Get absolute name for the rest. */
+		else
+			pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
+encode:
 		if (IS_ERR(pos))
 			continue;
 		name = tomoyo_encode(pos);
@@ -147,16 +287,6 @@
 	kfree(buf);
 	if (!name)
 		tomoyo_warn_oom(__func__);
-	else if (is_dir && *name) {
-		/* Append trailing '/' if dentry is a directory. */
-		char *pos = name + strlen(name) - 1;
-		if (*pos != '/')
-			/*
-			 * This is OK because tomoyo_encode() reserves space
-			 * for appending "/".
-			 */
-			*++pos = '/';
-	}
 	return name;
 }
 
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index e43d555..a49c3bf 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -1,9 +1,7 @@
 /*
- * security/tomoyo/common.c
+ * security/tomoyo/securityfs_if.c
  *
- * Securityfs interface for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/security.h>
@@ -34,11 +32,11 @@
  */
 static int tomoyo_release(struct inode *inode, struct file *file)
 {
-	return tomoyo_close_control(file);
+	return tomoyo_close_control(file->private_data);
 }
 
 /**
- * tomoyo_poll - poll() for /proc/ccs/ interface.
+ * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
  *
  * @file: Pointer to "struct file".
  * @wait: Pointer to "poll_table".
@@ -63,7 +61,7 @@
 static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
 			   loff_t *ppos)
 {
-	return tomoyo_read_control(file, buf, count);
+	return tomoyo_read_control(file->private_data, buf, count);
 }
 
 /**
@@ -79,7 +77,7 @@
 static ssize_t tomoyo_write(struct file *file, const char __user *buf,
 			    size_t count, loff_t *ppos)
 {
-	return tomoyo_write_control(file, buf, count);
+	return tomoyo_write_control(file->private_data, buf, count);
 }
 
 /*
@@ -135,14 +133,14 @@
 			    TOMOYO_DOMAINPOLICY);
 	tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
 			    TOMOYO_EXCEPTIONPOLICY);
+	tomoyo_create_entry("audit",            0400, tomoyo_dir,
+			    TOMOYO_AUDIT);
 	tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
 			    TOMOYO_SELFDOMAIN);
-	tomoyo_create_entry(".domain_status",   0600, tomoyo_dir,
-			    TOMOYO_DOMAIN_STATUS);
 	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
 			    TOMOYO_PROCESS_STATUS);
-	tomoyo_create_entry("meminfo",          0600, tomoyo_dir,
-			    TOMOYO_MEMINFO);
+	tomoyo_create_entry("stat",             0644, tomoyo_dir,
+			    TOMOYO_STAT);
 	tomoyo_create_entry("profile",          0600, tomoyo_dir,
 			    TOMOYO_PROFILE);
 	tomoyo_create_entry("manager",          0600, tomoyo_dir,
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 95d3f95..f776400 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -1,20 +1,35 @@
 /*
  * security/tomoyo/tomoyo.c
  *
- * LSM hooks for TOMOYO Linux.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/security.h>
 #include "common.h"
 
+/**
+ * tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
+ *
+ * @new: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0.
+ */
 static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
 {
 	new->security = NULL;
 	return 0;
 }
 
+/**
+ * tomoyo_cred_prepare - Target for security_prepare_creds().
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0.
+ */
 static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
 			       gfp_t gfp)
 {
@@ -25,11 +40,22 @@
 	return 0;
 }
 
+/**
+ * tomoyo_cred_transfer - Target for security_transfer_creds().
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ */
 static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
 {
 	tomoyo_cred_prepare(new, old, 0);
 }
 
+/**
+ * tomoyo_cred_free - Target for security_cred_free().
+ *
+ * @cred: Pointer to "struct cred".
+ */
 static void tomoyo_cred_free(struct cred *cred)
 {
 	struct tomoyo_domain_info *domain = cred->security;
@@ -37,6 +63,13 @@
 		atomic_dec(&domain->users);
 }
 
+/**
+ * tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
 {
 	int rc;
@@ -51,12 +84,14 @@
 	 */
 	if (bprm->cred_prepared)
 		return 0;
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
 	/*
 	 * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
 	 * for the first time.
 	 */
 	if (!tomoyo_policy_loaded)
 		tomoyo_load_policy(bprm->filename);
+#endif
 	/*
 	 * Release reference to "struct tomoyo_domain_info" stored inside
 	 * "bprm->cred->security". New reference to "struct tomoyo_domain_info"
@@ -73,6 +108,13 @@
 	return 0;
 }
 
+/**
+ * tomoyo_bprm_check_security - Target for security_bprm_check().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
 {
 	struct tomoyo_domain_info *domain = bprm->cred->security;
@@ -90,20 +132,59 @@
 	/*
 	 * Read permission is checked against interpreters using next domain.
 	 */
-	return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY);
+	return tomoyo_check_open_permission(domain, &bprm->file->f_path,
+					    O_RDONLY);
 }
 
+/**
+ * tomoyo_inode_getattr - Target for security_inode_getattr().
+ *
+ * @mnt:    Pointer to "struct vfsmount".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+	struct path path = { mnt, dentry };
+	return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path, NULL);
+}
+
+/**
+ * tomoyo_path_truncate - Target for security_path_truncate().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_truncate(struct path *path)
 {
-	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
+	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
 }
 
+/**
+ * tomoyo_path_unlink - Target for security_path_unlink().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
 }
 
+/**
+ * tomoyo_path_mkdir - Target for security_path_mkdir().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   DAC permission mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
 			     int mode)
 {
@@ -112,19 +193,46 @@
 				       mode & S_IALLUGO);
 }
 
+/**
+ * tomoyo_path_rmdir - Target for security_path_rmdir().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
 }
 
+/**
+ * tomoyo_path_symlink - Target for security_path_symlink().
+ *
+ * @parent:   Pointer to "struct path".
+ * @dentry:   Pointer to "struct dentry".
+ * @old_name: Symlink's content.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
 			       const char *old_name)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
 }
 
+/**
+ * tomoyo_path_mknod - Target for security_path_mknod().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   DAC permission mode.
+ * @dev:    Device attributes.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
 			     int mode, unsigned int dev)
 {
@@ -155,6 +263,15 @@
 	return tomoyo_path_number_perm(type, &path, perm);
 }
 
+/**
+ * tomoyo_path_link - Target for security_path_link().
+ *
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_dir:    Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
 			    struct dentry *new_dentry)
 {
@@ -163,6 +280,16 @@
 	return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
 }
 
+/**
+ * tomoyo_path_rename - Target for security_path_rename().
+ *
+ * @old_parent: Pointer to "struct path".
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_parent: Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_rename(struct path *old_parent,
 			      struct dentry *old_dentry,
 			      struct path *new_parent,
@@ -173,14 +300,32 @@
 	return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
 }
 
+/**
+ * tomoyo_file_fcntl - Target for security_file_fcntl().
+ *
+ * @file: Pointer to "struct file".
+ * @cmd:  Command for fcntl().
+ * @arg:  Argument for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
 			     unsigned long arg)
 {
-	if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
-		return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path);
-	return 0;
+	if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)))
+		return 0;
+	return tomoyo_check_open_permission(tomoyo_domain(), &file->f_path,
+					    O_WRONLY | (arg & O_APPEND));
 }
 
+/**
+ * tomoyo_dentry_open - Target for security_dentry_open().
+ *
+ * @f:    Pointer to "struct file".
+ * @cred: Pointer to "struct cred".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
 {
 	int flags = f->f_flags;
@@ -190,12 +335,30 @@
 	return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
 }
 
+/**
+ * tomoyo_file_ioctl - Target for security_file_ioctl().
+ *
+ * @file: Pointer to "struct file".
+ * @cmd:  Command for ioctl().
+ * @arg:  Argument for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
 			     unsigned long arg)
 {
 	return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);
 }
 
+/**
+ * tomoyo_path_chmod - Target for security_path_chmod().
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @mnt:    Pointer to "struct vfsmount".
+ * @mode:   DAC permission mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
 			     mode_t mode)
 {
@@ -204,6 +367,15 @@
 				       mode & S_IALLUGO);
 }
 
+/**
+ * tomoyo_path_chown - Target for security_path_chown().
+ *
+ * @path: Pointer to "struct path".
+ * @uid:  Owner ID.
+ * @gid:  Group ID.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
 {
 	int error = 0;
@@ -214,23 +386,57 @@
 	return error;
 }
 
+/**
+ * tomoyo_path_chroot - Target for security_path_chroot().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_path_chroot(struct path *path)
 {
-	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
 }
 
+/**
+ * tomoyo_sb_mount - Target for security_sb_mount().
+ *
+ * @dev_name: Name of device file. Maybe NULL.
+ * @path:     Pointer to "struct path".
+ * @type:     Name of filesystem type. Maybe NULL.
+ * @flags:    Mount options.
+ * @data:     Optional data. Maybe NULL.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_sb_mount(char *dev_name, struct path *path,
 			   char *type, unsigned long flags, void *data)
 {
 	return tomoyo_mount_permission(dev_name, path, type, flags, data);
 }
 
+/**
+ * tomoyo_sb_umount - Target for security_sb_umount().
+ *
+ * @mnt:   Pointer to "struct vfsmount".
+ * @flags: Unmount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
 {
 	struct path path = { mnt, mnt->mnt_root };
-	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
 }
 
+/**
+ * tomoyo_sb_pivotroot - Target for security_sb_pivotroot().
+ *
+ * @old_path: Pointer to "struct path".
+ * @new_path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
 static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
 {
 	return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
@@ -258,6 +464,7 @@
 	.path_mknod          = tomoyo_path_mknod,
 	.path_link           = tomoyo_path_link,
 	.path_rename         = tomoyo_path_rename,
+	.inode_getattr       = tomoyo_inode_getattr,
 	.file_ioctl          = tomoyo_file_ioctl,
 	.path_chmod          = tomoyo_path_chmod,
 	.path_chown          = tomoyo_path_chown,
@@ -270,6 +477,11 @@
 /* Lock for GC. */
 struct srcu_struct tomoyo_ss;
 
+/**
+ * tomoyo_init - Register TOMOYO Linux as a LSM module.
+ *
+ * Returns 0.
+ */
 static int __init tomoyo_init(void)
 {
 	struct cred *cred = (struct cred *) current_cred();
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 6d53932..c36bd11 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/util.c
  *
- * Utility functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include <linux/slab.h>
@@ -15,18 +13,130 @@
 /* Has /sbin/init started? */
 bool tomoyo_policy_loaded;
 
+/*
+ * Mapping table from "enum tomoyo_mac_index" to
+ * "enum tomoyo_mac_category_index".
+ */
+const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
+	/* CONFIG::file group */
+	[TOMOYO_MAC_FILE_EXECUTE]    = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_OPEN]       = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_CREATE]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_UNLINK]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_GETATTR]    = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MKDIR]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_RMDIR]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MKFIFO]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MKSOCK]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_TRUNCATE]   = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_SYMLINK]    = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MKBLOCK]    = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MKCHAR]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_LINK]       = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_RENAME]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_CHMOD]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_CHOWN]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_CHGRP]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_IOCTL]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_CHROOT]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE,
+	[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+};
+
+/**
+ * tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss.
+ *
+ * @time:  Seconds since 1970/01/01 00:00:00.
+ * @stamp: Pointer to "struct tomoyo_time".
+ *
+ * Returns nothing.
+ *
+ * This function does not handle Y2038 problem.
+ */
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp)
+{
+	static const u16 tomoyo_eom[2][12] = {
+		{ 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+		{ 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+	};
+	u16 y;
+	u8 m;
+	bool r;
+	stamp->sec = time % 60;
+	time /= 60;
+	stamp->min = time % 60;
+	time /= 60;
+	stamp->hour = time % 24;
+	time /= 24;
+	for (y = 1970; ; y++) {
+		const unsigned short days = (y & 3) ? 365 : 366;
+		if (time < days)
+			break;
+		time -= days;
+	}
+	r = (y & 3) == 0;
+	for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++)
+		;
+	if (m)
+		time -= tomoyo_eom[r][m - 1];
+	stamp->year = y;
+	stamp->month = ++m;
+	stamp->day = ++time;
+}
+
+/**
+ * tomoyo_permstr - Find permission keywords.
+ *
+ * @string: String representation for permissions in foo/bar/buz format.
+ * @keyword: Keyword to find from @string/
+ *
+ * Returns ture if @keyword was found in @string, false otherwise.
+ *
+ * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2.
+ */
+bool tomoyo_permstr(const char *string, const char *keyword)
+{
+	const char *cp = strstr(string, keyword);
+	if (cp)
+		return cp == string || *(cp - 1) == '/';
+	return false;
+}
+
+/**
+ * tomoyo_read_token - Read a word from a line.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns a word on success, "" otherwise.
+ *
+ * To allow the caller to skip NULL check, this function returns "" rather than
+ * NULL if there is no more words to read.
+ */
+char *tomoyo_read_token(struct tomoyo_acl_param *param)
+{
+	char *pos = param->data;
+	char *del = strchr(pos, ' ');
+	if (del)
+		*del++ = '\0';
+	else
+		del = pos + strlen(pos);
+	param->data = del;
+	return pos;
+}
+
 /**
  * tomoyo_parse_ulong - Parse an "unsigned long" value.
  *
  * @result: Pointer to "unsigned long".
  * @str:    Pointer to string to parse.
  *
- * Returns value type on success, 0 otherwise.
+ * Returns one of values in "enum tomoyo_value_type".
  *
  * The @src is updated to point the first character after the value
  * on success.
  */
-static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+u8 tomoyo_parse_ulong(unsigned long *result, char **str)
 {
 	const char *cp = *str;
 	char *ep;
@@ -43,7 +153,7 @@
 	}
 	*result = simple_strtoul(cp, &ep, base);
 	if (cp == ep)
-		return 0;
+		return TOMOYO_VALUE_TYPE_INVALID;
 	*str = ep;
 	switch (base) {
 	case 16:
@@ -81,63 +191,65 @@
 /**
  * tomoyo_parse_name_union - Parse a tomoyo_name_union.
  *
- * @filename: Name or name group.
- * @ptr:      Pointer to "struct tomoyo_name_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
  *
  * Returns true on success, false otherwise.
  */
-bool tomoyo_parse_name_union(const char *filename,
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
 			     struct tomoyo_name_union *ptr)
 {
-	if (!tomoyo_correct_word(filename))
-		return false;
-	if (filename[0] == '@') {
-		ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP);
-		ptr->is_group = true;
+	char *filename;
+	if (param->data[0] == '@') {
+		param->data++;
+		ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
 		return ptr->group != NULL;
 	}
+	filename = tomoyo_read_token(param);
+	if (!tomoyo_correct_word(filename))
+		return false;
 	ptr->filename = tomoyo_get_name(filename);
-	ptr->is_group = false;
 	return ptr->filename != NULL;
 }
 
 /**
  * tomoyo_parse_number_union - Parse a tomoyo_number_union.
  *
- * @data: Number or number range or number group.
- * @ptr:  Pointer to "struct tomoyo_number_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_number_union".
  *
  * Returns true on success, false otherwise.
  */
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_number_union *ptr)
 {
+	char *data;
 	u8 type;
 	unsigned long v;
-	memset(num, 0, sizeof(*num));
-	if (data[0] == '@') {
-		if (!tomoyo_correct_word(data))
-			return false;
-		num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP);
-		num->is_group = true;
-		return num->group != NULL;
+	memset(ptr, 0, sizeof(*ptr));
+	if (param->data[0] == '@') {
+		param->data++;
+		ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP);
+		return ptr->group != NULL;
 	}
+	data = tomoyo_read_token(param);
 	type = tomoyo_parse_ulong(&v, &data);
-	if (!type)
+	if (type == TOMOYO_VALUE_TYPE_INVALID)
 		return false;
-	num->values[0] = v;
-	num->min_type = type;
+	ptr->values[0] = v;
+	ptr->value_type[0] = type;
 	if (!*data) {
-		num->values[1] = v;
-		num->max_type = type;
+		ptr->values[1] = v;
+		ptr->value_type[1] = type;
 		return true;
 	}
 	if (*data++ != '-')
 		return false;
 	type = tomoyo_parse_ulong(&v, &data);
-	if (!type || *data)
+	if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
 		return false;
-	num->values[1] = v;
-	num->max_type = type;
+	ptr->values[1] = v;
+	ptr->value_type[1] = type;
 	return true;
 }
 
@@ -185,6 +297,30 @@
 }
 
 /**
+ * tomoyo_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_valid(const unsigned char c)
+{
+	return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_invalid(const unsigned char c)
+{
+	return c && (c <= ' ' || c >= 127);
+}
+
+/**
  * tomoyo_str_starts - Check whether the given string starts with the given keyword.
  *
  * @src:  Pointer to pointer to the string.
@@ -238,36 +374,9 @@
 }
 
 /**
- * tomoyo_tokenize - Tokenize string.
- *
- * @buffer: The line to tokenize.
- * @w:      Pointer to "char *".
- * @size:   Sizeof @w .
- *
- * Returns true on success, false otherwise.
- */
-bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
-{
-	int count = size / sizeof(char *);
-	int i;
-	for (i = 0; i < count; i++)
-		w[i] = "";
-	for (i = 0; i < count; i++) {
-		char *cp = strchr(buffer, ' ');
-		if (cp)
-			*cp = '\0';
-		w[i] = buffer;
-		if (!cp)
-			break;
-		buffer = cp + 1;
-	}
-	return i < count || !*buffer;
-}
-
-/**
  * tomoyo_correct_word2 - Validate a string.
  *
- * @string: The string to check. May be non-'\0'-terminated.
+ * @string: The string to check. Maybe non-'\0'-terminated.
  * @len:    Length of @string.
  *
  * Check whether the given string follows the naming rules.
@@ -377,26 +486,21 @@
  */
 bool tomoyo_correct_domain(const unsigned char *domainname)
 {
-	if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
-				   TOMOYO_ROOT_NAME_LEN))
-		goto out;
-	domainname += TOMOYO_ROOT_NAME_LEN;
-	if (!*domainname)
+	if (!domainname || !tomoyo_domain_def(domainname))
+		return false;
+	domainname = strchr(domainname, ' ');
+	if (!domainname++)
 		return true;
-	if (*domainname++ != ' ')
-		goto out;
 	while (1) {
 		const unsigned char *cp = strchr(domainname, ' ');
 		if (!cp)
 			break;
 		if (*domainname != '/' ||
 		    !tomoyo_correct_word2(domainname, cp - domainname))
-			goto out;
+			return false;
 		domainname = cp + 1;
 	}
 	return tomoyo_correct_path(domainname);
- out:
-	return false;
 }
 
 /**
@@ -408,7 +512,19 @@
  */
 bool tomoyo_domain_def(const unsigned char *buffer)
 {
-	return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+	const unsigned char *cp;
+	int len;
+	if (*buffer != '<')
+		return false;
+	cp = strchr(buffer, ' ');
+	if (!cp)
+		len = strlen(buffer);
+	else
+		len = cp - buffer;
+	if (buffer[len - 1] != '>' ||
+	    !tomoyo_correct_word2(buffer + 1, len - 2))
+		return false;
+	return true;
 }
 
 /**
@@ -794,22 +910,24 @@
 /**
  * tomoyo_get_mode - Get MAC mode.
  *
+ * @ns:      Pointer to "struct tomoyo_policy_namespace".
  * @profile: Profile number.
  * @index:   Index number of functionality.
  *
  * Returns mode.
  */
-int tomoyo_get_mode(const u8 profile, const u8 index)
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+		    const u8 index)
 {
 	u8 mode;
 	const u8 category = TOMOYO_MAC_CATEGORY_FILE;
 	if (!tomoyo_policy_loaded)
 		return TOMOYO_CONFIG_DISABLED;
-	mode = tomoyo_profile(profile)->config[index];
+	mode = tomoyo_profile(ns, profile)->config[index];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(profile)->config[category];
+		mode = tomoyo_profile(ns, profile)->config[category];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(profile)->default_config;
+		mode = tomoyo_profile(ns, profile)->default_config;
 	return mode & 3;
 }
 
@@ -833,65 +951,11 @@
 	profile = domain->profile;
 	r->profile = profile;
 	r->type = index;
-	r->mode = tomoyo_get_mode(profile, index);
+	r->mode = tomoyo_get_mode(domain->ns, profile, index);
 	return r->mode;
 }
 
 /**
- * tomoyo_last_word - Get last component of a line.
- *
- * @line: A line.
- *
- * Returns the last word of a line.
- */
-const char *tomoyo_last_word(const char *name)
-{
-	const char *cp = strrchr(name, ' ');
-	if (cp)
-		return cp + 1;
-	return name;
-}
-
-/**
- * tomoyo_warn_log - Print warning or error message on console.
- *
- * @r:   Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
- */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
-{
-	va_list args;
-	char *buffer;
-	const struct tomoyo_domain_info * const domain = r->domain;
-	const struct tomoyo_profile *profile = tomoyo_profile(domain->profile);
-	switch (r->mode) {
-	case TOMOYO_CONFIG_ENFORCING:
-		if (!profile->enforcing->enforcing_verbose)
-			return;
-		break;
-	case TOMOYO_CONFIG_PERMISSIVE:
-		if (!profile->permissive->permissive_verbose)
-			return;
-		break;
-	case TOMOYO_CONFIG_LEARNING:
-		if (!profile->learning->learning_verbose)
-			return;
-		break;
-	}
-	buffer = kmalloc(4096, GFP_NOFS);
-	if (!buffer)
-		return;
-	va_start(args, fmt);
-	vsnprintf(buffer, 4095, fmt, args);
-	va_end(args);
-	buffer[4095] = '\0';
-	printk(KERN_WARNING "%s: Access %s denied for %s\n",
-	       r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
-	       tomoyo_last_word(domain->domainname->name));
-	kfree(buffer);
-}
-
-/**
  * tomoyo_domain_quota_is_ok - Check for domain's quota.
  *
  * @r: Pointer to "struct tomoyo_request_info".
@@ -911,52 +975,43 @@
 	if (!domain)
 		return true;
 	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+		u16 perm;
+		u8 i;
 		if (ptr->is_deleted)
 			continue;
 		switch (ptr->type) {
-			u16 perm;
-			u8 i;
 		case TOMOYO_TYPE_PATH_ACL:
 			perm = container_of(ptr, struct tomoyo_path_acl, head)
 				->perm;
-			for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
-				if (perm & (1 << i))
-					count++;
-			if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
-				count -= 2;
 			break;
 		case TOMOYO_TYPE_PATH2_ACL:
 			perm = container_of(ptr, struct tomoyo_path2_acl, head)
 				->perm;
-			for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
-				if (perm & (1 << i))
-					count++;
 			break;
 		case TOMOYO_TYPE_PATH_NUMBER_ACL:
 			perm = container_of(ptr, struct tomoyo_path_number_acl,
 					    head)->perm;
-			for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++)
-				if (perm & (1 << i))
-					count++;
 			break;
 		case TOMOYO_TYPE_MKDEV_ACL:
 			perm = container_of(ptr, struct tomoyo_mkdev_acl,
 					    head)->perm;
-			for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++)
-				if (perm & (1 << i))
-					count++;
 			break;
 		default:
-			count++;
+			perm = 1;
 		}
+		for (i = 0; i < 16; i++)
+			if (perm & (1 << i))
+				count++;
 	}
-	if (count < tomoyo_profile(domain->profile)->learning->
-	    learning_max_entry)
+	if (count < tomoyo_profile(domain->ns, domain->profile)->
+	    pref[TOMOYO_PREF_MAX_LEARNING_ENTRY])
 		return true;
-	if (!domain->quota_warned) {
-		domain->quota_warned = true;
-		printk(KERN_WARNING "TOMOYO-WARNING: "
-		       "Domain '%s' has so many ACLs to hold. "
+	if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) {
+		domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true;
+		/* r->granted = false; */
+		tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]);
+		printk(KERN_WARNING "WARNING: "
+		       "Domain '%s' has too many ACLs to hold. "
 		       "Stopped learning mode.\n", domain->domainname->name);
 	}
 	return false;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index f134130..86d0caf 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -128,7 +128,8 @@
 	}
 }
 
-static void pcm_debug_name(struct snd_pcm_substream *substream,
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
 			   char *name, size_t len)
 {
 	snprintf(name, len, "pcmC%dD%d%c:%d",
@@ -137,6 +138,8 @@
 		 substream->stream ? 'c' : 'p',
 		 substream->number);
 }
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
 
 #define XRUN_DEBUG_BASIC	(1<<0)
 #define XRUN_DEBUG_STACK	(1<<1)	/* dump also stack */
@@ -168,7 +171,7 @@
 	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
 	if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
 		char name[16];
-		pcm_debug_name(substream, name, sizeof(name));
+		snd_pcm_debug_name(substream, name, sizeof(name));
 		snd_printd(KERN_DEBUG "XRUN: %s\n", name);
 		dump_stack_on_xrun(substream);
 	}
@@ -243,7 +246,7 @@
 		return;
 	if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
 		return;
-	pcm_debug_name(substream, name, sizeof(name));
+	snd_pcm_debug_name(substream, name, sizeof(name));
 	for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
 		entry = &log->entries[idx];
 		if (entry->period_size == 0)
@@ -319,7 +322,7 @@
 	if (pos >= runtime->buffer_size) {
 		if (printk_ratelimit()) {
 			char name[16];
-			pcm_debug_name(substream, name, sizeof(name));
+			snd_pcm_debug_name(substream, name, sizeof(name));
 			xrun_log_show(substream);
 			snd_printd(KERN_ERR  "BUG: %s, pos = %ld, "
 				   "buffer size = %ld, period size = %ld\n",
@@ -364,7 +367,7 @@
 	if (xrun_debug(substream, in_interrupt ?
 			XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
 		char name[16];
-		pcm_debug_name(substream, name, sizeof(name));
+		snd_pcm_debug_name(substream, name, sizeof(name));
 		snd_printd("%s_update: %s: pos=%u/%u/%u, "
 			   "hwptr=%ld/%ld/%ld/%ld\n",
 			   in_interrupt ? "period" : "hwptr",
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index 4831800..484a35b 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -22,11 +22,11 @@
 
 #include <asm/io.h>
 #include <linux/delay.h>
-#include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/version.h>
-#include <sound/core.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
 #include <sound/tea575x-tuner.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -62,17 +62,6 @@
 #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
 #define TEA575X_BIT_FREQ_MASK	0x7fff
 
-static struct v4l2_queryctrl radio_qctrl[] = {
-	{
-		.id            = V4L2_CID_AUDIO_MUTE,
-		.name          = "Mute",
-		.minimum       = 0,
-		.maximum       = 1,
-		.default_value = 1,
-		.type          = V4L2_CTRL_TYPE_BOOLEAN,
-	}
-};
-
 /*
  * lowlevel part
  */
@@ -266,83 +255,23 @@
 	return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-					struct v4l2_queryctrl *qc)
+static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-		if (qc->id && qc->id == radio_qctrl[i].id) {
-			memcpy(qc, &(radio_qctrl[i]),
-						sizeof(*qc));
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
+	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = tea->mute;
+		tea->mute = ctrl->val;
+		snd_tea575x_set_freq(tea);
 		return 0;
 	}
+
 	return -EINVAL;
 }
 
-static int vidioc_s_ctrl(struct file *file, void *priv,
-					struct v4l2_control *ctrl)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		if (tea->mute != ctrl->value) {
-			tea->mute = ctrl->value;
-			snd_tea575x_set_freq(tea);
-		}
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-	*i = 0;
-	return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-	if (i != 0)
-		return -EINVAL;
-	return 0;
-}
-
-static int snd_tea575x_exclusive_open(struct file *file)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0;
-}
-
-static int snd_tea575x_exclusive_release(struct file *file)
-{
-	struct snd_tea575x *tea = video_drvdata(file);
-
-	clear_bit(0, &tea->in_use);
-	return 0;
-}
-
 static const struct v4l2_file_operations tea575x_fops = {
 	.owner		= THIS_MODULE,
-	.open           = snd_tea575x_exclusive_open,
-	.release        = snd_tea575x_exclusive_release,
-	.ioctl		= video_ioctl2,
+	.unlocked_ioctl	= video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
@@ -351,20 +280,19 @@
 	.vidioc_s_tuner     = vidioc_s_tuner,
 	.vidioc_g_audio     = vidioc_g_audio,
 	.vidioc_s_audio     = vidioc_s_audio,
-	.vidioc_g_input     = vidioc_g_input,
-	.vidioc_s_input     = vidioc_s_input,
 	.vidioc_g_frequency = vidioc_g_frequency,
 	.vidioc_s_frequency = vidioc_s_frequency,
-	.vidioc_queryctrl   = vidioc_queryctrl,
-	.vidioc_g_ctrl      = vidioc_g_ctrl,
-	.vidioc_s_ctrl      = vidioc_s_ctrl,
 };
 
 static struct video_device tea575x_radio = {
 	.name           = "tea575x-tuner",
 	.fops           = &tea575x_fops,
 	.ioctl_ops 	= &tea575x_ioctl_ops,
-	.release	= video_device_release,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+	.s_ctrl = tea575x_s_ctrl,
 };
 
 /*
@@ -373,7 +301,6 @@
 int snd_tea575x_init(struct snd_tea575x *tea)
 {
 	int retval;
-	struct video_device *tea575x_radio_inst;
 
 	tea->mute = 1;
 
@@ -381,43 +308,49 @@
 	if (snd_tea575x_read(tea) != 0x55AA)
 		return -ENODEV;
 
-	tea->in_use = 0;
 	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+	snd_tea575x_set_freq(tea);
 
-	tea575x_radio_inst = video_device_alloc();
-	if (tea575x_radio_inst == NULL) {
-		printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-		return -ENOMEM;
-	}
+	tea->vd = tea575x_radio;
+	video_set_drvdata(&tea->vd, tea);
+	mutex_init(&tea->mutex);
+	tea->vd.lock = &tea->mutex;
 
-	memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
-
-	strcpy(tea575x_radio.name, tea->tea5759 ?
-				   "TEA5759 radio" : "TEA5757 radio");
-
-	video_set_drvdata(tea575x_radio_inst, tea);
-
-	retval = video_register_device(tea575x_radio_inst,
-				       VFL_TYPE_RADIO, radio_nr);
+	v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+	tea->vd.ctrl_handler = &tea->ctrl_handler;
+	v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+	retval = tea->ctrl_handler.error;
 	if (retval) {
-		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
-		kfree(tea575x_radio_inst);
+		printk(KERN_ERR "tea575x-tuner: can't initialize controls\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
 		return retval;
 	}
 
-	snd_tea575x_set_freq(tea);
-	tea->vd = tea575x_radio_inst;
+	if (tea->ext_init) {
+		retval = tea->ext_init(tea);
+		if (retval) {
+			v4l2_ctrl_handler_free(&tea->ctrl_handler);
+			return retval;
+		}
+	}
+
+	v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, radio_nr);
+	if (retval) {
+		printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
+		v4l2_ctrl_handler_free(&tea->ctrl_handler);
+		return retval;
+	}
 
 	return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)
 {
-	if (tea->vd) {
-		video_unregister_device(tea->vd);
-		tea->vd = NULL;
-	}
+	video_unregister_device(&tea->vd);
+	v4l2_ctrl_handler_free(&tea->ctrl_handler);
 }
 
 static int __init alsa_tea575x_module_init(void)
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index 3773e24..a168ba3 100644
--- a/sound/isa/msnd/msnd.h
+++ b/sound/isa/msnd/msnd.h
@@ -249,7 +249,7 @@
 
 	/* State variables */
 	enum { msndClassic, msndPinnacle } type;
-	mode_t mode;
+	fmode_t mode;
 	unsigned long flags;
 #define F_RESETTING			0
 #define F_HAVEDIGITAL			1
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index 4d2a6ae..8a197fd 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -458,7 +458,7 @@
 	return mask;
 }
 
-static void change_bits(ad1848_info * devc, unsigned char *regval,
+static void oss_change_bits(ad1848_info *devc, unsigned char *regval,
 			unsigned char *muteval, int dev, int chn, int newval)
 {
 	unsigned char mask;
@@ -516,10 +516,10 @@
 
 	if (muteregoffs != regoffs) {
 		muteval = ad_read(devc, muteregoffs);
-		change_bits(devc, &val, &muteval, dev, channel, value);
+		oss_change_bits(devc, &val, &muteval, dev, channel, value);
 	}
 	else
-		change_bits(devc, &val, &val, dev, channel, value);
+		oss_change_bits(devc, &val, &val, dev, channel, value);
 
 	spin_lock_irqsave(&devc->lock,flags);
 	ad_write(devc, regoffs, val);
diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c
index 2039d31..f8f3b7a 100644
--- a/sound/oss/sb_mixer.c
+++ b/sound/oss/sb_mixer.c
@@ -232,7 +232,7 @@
 	return 1;
 }
 
-static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
+static void oss_change_bits(sb_devc *devc, unsigned char *regval, int dev, int chn, int newval)
 {
 	unsigned char mask;
 	int shift;
@@ -284,7 +284,7 @@
 		return -EINVAL;
 
 	val = sb_getmixer(devc, regoffs);
-	change_bits(devc, &val, dev, LEFT_CHN, left);
+	oss_change_bits(devc, &val, dev, LEFT_CHN, left);
 
 	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
 								 * Change register
@@ -304,7 +304,7 @@
 							 * Read the new one
 							 */
 	}
-	change_bits(devc, &val, dev, RIGHT_CHN, right);
+	oss_change_bits(devc, &val, dev, RIGHT_CHN, right);
 
 	sb_setmixer(devc, regoffs, val);
 
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index e90d103..50abf5b 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -565,8 +565,8 @@
 
 config SND_TEA575X
 	tristate
-	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
-	default SND_FM801 || SND_ES1968
+	depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+	default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
 
 source "sound/pci/hda/Kconfig"
 
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index b941d25..eae62eb 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -41,31 +41,10 @@
 #include <sound/tlv.h>
 #include <sound/hwdep.h>
 
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
 MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
 
-#if defined CONFIG_SND_DEBUG
-/* copied from pcm_lib.c, hope later patch will make that version public
-and this copy can be removed */
-static inline void
-snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
-{
-	snprintf(buf, size, "pcmC%dD%d%c:%d",
-		 substream->pcm->card->number,
-		 substream->pcm->device,
-		 substream->stream ? 'c' : 'p',
-		 substream->number);
-}
-#else
-static inline void
-snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
-{
-	*buf = 0;
-}
-#endif
-
 #if defined CONFIG_SND_DEBUG_VERBOSE
 /**
  * snd_printddd - very verbose debug printk
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 65fcf47..9683f84 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -107,7 +107,6 @@
 	union hpi_response_buffer_v1 *hr;
 	u16 res_max_size;
 	u32 uncopied_bytes;
-	struct hpi_adapter *pa = NULL;
 	int err = 0;
 
 	if (cmd != HPI_IOCTL_LINUX)
@@ -182,8 +181,9 @@
 		/* -1=no data 0=read from user mem, 1=write to user mem */
 		int wrflag = -1;
 		u32 adapter = hm->h.adapter_index;
+		struct hpi_adapter *pa = &adapters[adapter];
 
-		if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
+		if ((adapter >= HPI_MAX_ADAPTERS) || (!pa->type)) {
 			hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
 				HPI_ADAPTER_OPEN,
 				HPI_ERROR_BAD_ADAPTER_NUMBER);
@@ -197,9 +197,7 @@
 			goto out;
 		}
 
-		pa = &adapters[adapter];
-
-		if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
+		if (mutex_lock_interruptible(&pa->mutex)) {
 			err = -EINTR;
 			goto out;
 		}
@@ -235,8 +233,7 @@
 							"stream buffer size %d\n",
 							size);
 
-						mutex_unlock(&adapters
-							[adapter].mutex);
+						mutex_unlock(&pa->mutex);
 						err = -EINVAL;
 						goto out;
 					}
@@ -277,7 +274,7 @@
 					uncopied_bytes, size);
 		}
 
-		mutex_unlock(&adapters[adapter].mutex);
+		mutex_unlock(&pa->mutex);
 	}
 
 	/* on return response size must be set */
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index fe7ad64..43c7e12 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -52,7 +52,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/darla20_dsp.fw");
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index d1fd34b..95b0330 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -56,7 +56,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/darla24_dsp.fw");
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 1dffdc5..8723c40 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -64,7 +64,7 @@
 #include <sound/initval.h>
 #include <sound/rawmidi.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index 050e54a..0058c67 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -56,7 +56,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/gina20_dsp.fw");
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index 5748fc6..14e4925 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -62,7 +62,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 4ae5e35..f416b15 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -54,7 +54,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 3550715..e594a3b 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -54,7 +54,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c
index 19b191f..f0d00bf 100644
--- a/sound/pci/echoaudio/indigodjx.c
+++ b/sound/pci/echoaudio/indigodjx.c
@@ -54,7 +54,7 @@
 #include <sound/pcm_params.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index a9fcedf..1af0037 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -55,7 +55,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c
index bcdfac6..0b51163 100644
--- a/sound/pci/echoaudio/indigoiox.c
+++ b/sound/pci/echoaudio/indigoiox.c
@@ -55,7 +55,7 @@
 #include <sound/pcm_params.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index d3a98c5..3f63ab8 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -62,7 +62,7 @@
 #include <sound/initval.h>
 #include <sound/rawmidi.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/layla20_dsp.fw");
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 2a1dca6..2831372 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -64,7 +64,7 @@
 #include <sound/initval.h>
 #include <sound/rawmidi.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 9cdf14c..eddaeb4 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -63,7 +63,7 @@
 #include <sound/initval.h>
 #include <sound/rawmidi.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 1047be4..0364011 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -60,7 +60,7 @@
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 #include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 #include "echoaudio.h"
 
 MODULE_FIRMWARE("ea/loader_dsp.fw");
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 7489b46..bb7e102 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -243,6 +243,7 @@
 
 config SND_HDA_POWER_SAVE
 	bool "Aggressive power-saving on HD-audio"
+	depends on PM
 	help
 	  Say Y here to enable more aggressive power-saving mode on
 	  HD-audio driver.  The power-saving timeout can be configured
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 9c27a3a..3e7850c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -91,8 +91,10 @@
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_power_work(struct work_struct *work);
 static void hda_keep_power_on(struct hda_codec *codec);
+#define hda_codec_is_power_on(codec)	((codec)->power_on)
 #else
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
+#define hda_codec_is_power_on(codec)	1
 #endif
 
 /**
@@ -1101,7 +1103,7 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
 static void restore_shutup_pins(struct hda_codec *codec)
 {
@@ -1499,7 +1501,7 @@
 	}
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /* clean up all streams; called from suspend */
 static void hda_cleanup_all_streams(struct hda_codec *codec)
 {
@@ -1838,7 +1840,7 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /**
  * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
  * @codec: HD-audio codec
@@ -1868,7 +1870,7 @@
 	}
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
 			     unsigned int ofs)
@@ -3082,7 +3084,7 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /*
  * command cache
  */
@@ -3199,53 +3201,32 @@
 					  seq->param);
 }
 EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
-/*
- * set power state of the codec
- */
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
-				unsigned int power_state)
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+				    unsigned int power_state,
+				    bool eapd_workaround)
 {
-	hda_nid_t nid;
+	hda_nid_t nid = codec->start_nid;
 	int i;
 
-	/* this delay seems necessary to avoid click noise at power-down */
-	if (power_state == AC_PWRST_D3)
-		msleep(100);
-	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
-			    power_state);
-	/* partial workaround for "azx_get_response timeout" */
-	if (power_state == AC_PWRST_D0 &&
-	    (codec->vendor_id & 0xffff0000) == 0x14f10000)
-		msleep(10);
-
-	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		unsigned int wcaps = get_wcaps(codec, nid);
-		if (wcaps & AC_WCAP_POWER) {
-			unsigned int wid_type = get_wcaps_type(wcaps);
-			if (power_state == AC_PWRST_D3 &&
-			    wid_type == AC_WID_PIN) {
-				unsigned int pincap;
-				/*
-				 * don't power down the widget if it controls
-				 * eapd and EAPD_BTLENABLE is set.
-				 */
-				pincap = snd_hda_query_pin_caps(codec, nid);
-				if (pincap & AC_PINCAP_EAPD) {
-					int eapd = snd_hda_codec_read(codec,
-						nid, 0,
+		if (!(wcaps & AC_WCAP_POWER))
+			continue;
+		/* don't power down the widget if it controls eapd and
+		 * EAPD_BTLENABLE is set.
+		 */
+		if (eapd_workaround && power_state == AC_PWRST_D3 &&
+		    get_wcaps_type(wcaps) == AC_WID_PIN &&
+		    (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+			int eapd = snd_hda_codec_read(codec, nid, 0,
 						AC_VERB_GET_EAPD_BTLENABLE, 0);
-					eapd &= 0x02;
-					if (eapd)
-						continue;
-				}
-			}
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_POWER_STATE,
-					    power_state);
+			if (eapd & 0x02)
+				continue;
 		}
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+				    power_state);
 	}
 
 	if (power_state == AC_PWRST_D0) {
@@ -3262,6 +3243,26 @@
 		} while (time_after_eq(end_time, jiffies));
 	}
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+				unsigned int power_state)
+{
+	if (codec->patch_ops.set_power_state) {
+		codec->patch_ops.set_power_state(codec, fg, power_state);
+		return;
+	}
+
+	/* this delay seems necessary to avoid click noise at power-down */
+	if (power_state == AC_PWRST_D3)
+		msleep(100);
+	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+			    power_state);
+	snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+}
 
 #ifdef CONFIG_SND_HDA_HWDEP
 /* execute additional init verbs */
@@ -3274,7 +3275,7 @@
 static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /*
  * call suspend and power-down; used both from PM and power-save
  */
@@ -3315,7 +3316,7 @@
 		snd_hda_codec_resume_cache(codec);
 	}
 }
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 
 /**
@@ -4071,9 +4072,6 @@
 EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
-				unsigned int power_state);
-
 static void hda_power_work(struct work_struct *work)
 {
 	struct hda_codec *codec =
@@ -4376,11 +4374,8 @@
 	if (!bus)
 		return;
 	list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-		if (!codec->power_on)
-			continue;
-#endif
-		if (codec->patch_ops.reboot_notify)
+		if (hda_codec_is_power_on(codec) &&
+		    codec->patch_ops.reboot_notify)
 			codec->patch_ops.reboot_notify(codec);
 	}
 }
@@ -5079,11 +5074,10 @@
 	struct hda_codec *codec;
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-		if (!codec->power_on)
-			continue;
-#endif
-		hda_call_codec_suspend(codec);
+		if (hda_codec_is_power_on(codec))
+			hda_call_codec_suspend(codec);
+		if (codec->patch_ops.post_suspend)
+			codec->patch_ops.post_suspend(codec);
 	}
 	return 0;
 }
@@ -5103,6 +5097,8 @@
 	struct hda_codec *codec;
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
+		if (codec->patch_ops.pre_resume)
+			codec->patch_ops.pre_resume(codec);
 		if (snd_hda_codec_needs_resume(codec))
 			hda_call_codec_resume(codec);
 	}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index f465e07..755f2b0 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -26,10 +26,6 @@
 #include <sound/pcm.h>
 #include <sound/hwdep.h>
 
-#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
-#define SND_HDA_NEEDS_RESUME	/* resume control code is required */
-#endif
-
 /*
  * nodes
  */
@@ -704,8 +700,12 @@
 	int (*init)(struct hda_codec *codec);
 	void (*free)(struct hda_codec *codec);
 	void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef SND_HDA_NEEDS_RESUME
+	void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
+				unsigned int power_state);
+#ifdef CONFIG_PM
 	int (*suspend)(struct hda_codec *codec, pm_message_t state);
+	int (*post_suspend)(struct hda_codec *codec);
+	int (*pre_resume)(struct hda_codec *codec);
 	int (*resume)(struct hda_codec *codec);
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -927,7 +927,7 @@
 int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
 
 /* cached write */
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm);
 void snd_hda_sequence_write_cache(struct hda_codec *codec,
@@ -1008,6 +1008,9 @@
  */
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
 void snd_hda_bus_reboot_notify(struct hda_bus *bus);
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+				    unsigned int power_state,
+				    bool eapd_workaround);
 
 /*
  * power management
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 88b277e..2e7ac31 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -131,7 +131,7 @@
 			     int direction, int idx, int mask, int val);
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 			     int dir, int idx, int mask, int val);
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 #endif
 
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 1362c8b..8648917 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -563,7 +563,7 @@
 	snd_hda_detach_beep_device(codec);
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
 {
 	ad198x_shutup(codec);
@@ -579,7 +579,7 @@
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	.check_power_status = ad198x_check_power_status,
 #endif
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 	.suspend = ad198x_suspend,
 #endif
 	.reboot_notify = ad198x_shutup,
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 7f93739..47d6ffc 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -25,6 +25,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include <sound/tlv.h>
 
 /*
  */
@@ -61,9 +62,15 @@
 
 	unsigned int hp_detect:1;
 	unsigned int mic_detect:1;
+	/* CS421x */
+	unsigned int spdif_detect:1;
+	unsigned int sense_b:1;
+	hda_nid_t vendor_nid;
+	struct hda_input_mux input_mux;
+	unsigned int last_input;
 };
 
-/* available models */
+/* available models with CS420x */
 enum {
 	CS420X_MBP53,
 	CS420X_MBP55,
@@ -72,6 +79,12 @@
 	CS420X_MODELS
 };
 
+/* CS421x boards */
+enum {
+	CS421X_CDB4210,
+	CS421X_MODELS
+};
+
 /* Vendor-specific processing widget */
 #define CS420X_VENDOR_NID	0x11
 #define CS_DIG_OUT1_PIN_NID	0x10
@@ -111,21 +124,42 @@
 /* 0x0009 - 0x0014 -> 12 test regs */
 /* 0x0015 - visibility reg */
 
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+#define CS4210_DAC_NID		0x02
+#define CS4210_ADC_NID		0x03
+#define CS421X_VENDOR_NID	0x0B
+#define CS421X_DMIC_PIN_NID	0x09 /* Port E */
+#define CS421X_SPDIF_PIN_NID	0x0A /* Port H */
+
+#define CS421X_IDX_DEV_CFG	0x01
+#define CS421X_IDX_ADC_CFG	0x02
+#define CS421X_IDX_DAC_CFG	0x03
+#define CS421X_IDX_SPK_CTL	0x04
+
+#define SPDIF_EVENT		0x04
 
 static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
 {
-	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+	struct cs_spec *spec = codec->spec;
+	snd_hda_codec_write(codec, spec->vendor_nid, 0,
 			    AC_VERB_SET_COEF_INDEX, idx);
-	return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+	return snd_hda_codec_read(codec, spec->vendor_nid, 0,
 				  AC_VERB_GET_PROC_COEF, 0);
 }
 
 static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
 				      unsigned int coef)
 {
-	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+	struct cs_spec *spec = codec->spec;
+	snd_hda_codec_write(codec, spec->vendor_nid, 0,
 			    AC_VERB_SET_COEF_INDEX, idx);
-	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+	snd_hda_codec_write(codec, spec->vendor_nid, 0,
 			    AC_VERB_SET_PROC_COEF, coef);
 }
 
@@ -347,15 +381,12 @@
 	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		unsigned int type;
-		int idx;
 		type = get_wcaps_type(get_wcaps(codec, nid));
 		if (type != AC_WID_AUD_IN)
 			continue;
-		idx = snd_hda_get_conn_index(codec, nid, pin, 0);
-		if (idx >= 0) {
-			*idxp = idx;
+		*idxp = snd_hda_get_conn_index(codec, nid, pin, false);
+		if (*idxp >= 0)
 			return nid;
-		}
 	}
 	return 0;
 }
@@ -835,6 +866,8 @@
 
 /*
  * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
  */
 
 static void cs_automute(struct hda_codec *codec)
@@ -842,9 +875,25 @@
 	struct cs_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	unsigned int hp_present;
+	unsigned int spdif_present;
 	hda_nid_t nid;
 	int i;
 
+	spdif_present = 0;
+	if (cfg->dig_outs) {
+		nid = cfg->dig_out_pins[0];
+		if (is_jack_detectable(codec, nid)) {
+			/*
+			TODO: SPDIF output redirect when SENSE_B is enabled.
+			Shared (SENSE_A) jack (e.g HP/mini-TOSLINK)
+			assumed.
+			*/
+			if (snd_hda_jack_detect(codec, nid)
+				/* && spec->sense_b */)
+				spdif_present = 1;
+		}
+	}
+
 	hp_present = 0;
 	for (i = 0; i < cfg->hp_outs; i++) {
 		nid = cfg->hp_pins[i];
@@ -854,11 +903,19 @@
 		if (hp_present)
 			break;
 	}
+
+	/* mute speakers if spdif or hp jack is plugged in */
 	for (i = 0; i < cfg->speaker_outs; i++) {
 		nid = cfg->speaker_pins[i];
 		snd_hda_codec_write(codec, nid, 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
 				    hp_present ? 0 : PIN_OUT);
+		/* detect on spdif is specific to CS421x */
+		if (spec->vendor_nid == CS421X_VENDOR_NID) {
+			snd_hda_codec_write(codec, nid, 0,
+					AC_VERB_SET_PIN_WIDGET_CONTROL,
+					spdif_present ? 0 : PIN_OUT);
+		}
 	}
 	if (spec->board_config == CS420X_MBP53 ||
 	    spec->board_config == CS420X_MBP55 ||
@@ -867,21 +924,62 @@
 		snd_hda_codec_write(codec, 0x01, 0,
 				    AC_VERB_SET_GPIO_DATA, gpio);
 	}
+
+	/* specific to CS421x */
+	if (spec->vendor_nid == CS421X_VENDOR_NID) {
+		/* mute HPs if spdif jack (SENSE_B) is present */
+		for (i = 0; i < cfg->hp_outs; i++) {
+			nid = cfg->hp_pins[i];
+			snd_hda_codec_write(codec, nid, 0,
+				AC_VERB_SET_PIN_WIDGET_CONTROL,
+				(spdif_present && spec->sense_b) ? 0 : PIN_HP);
+		}
+
+		/* SPDIF TX on/off */
+		if (cfg->dig_outs) {
+			nid = cfg->dig_out_pins[0];
+			snd_hda_codec_write(codec, nid, 0,
+				AC_VERB_SET_PIN_WIDGET_CONTROL,
+				spdif_present ? PIN_OUT : 0);
+
+		}
+		/* Update board GPIOs if neccessary ... */
+	}
 }
 
+/*
+ * Auto-input redirect for CS421x
+ * Switch max 3 inputs of a single ADC (nid 3)
+*/
+
 static void cs_automic(struct hda_codec *codec)
 {
 	struct cs_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	hda_nid_t nid;
 	unsigned int present;
-	
+
 	nid = cfg->inputs[spec->automic_idx].pin;
 	present = snd_hda_jack_detect(codec, nid);
-	if (present)
-		change_cur_input(codec, spec->automic_idx, 0);
-	else
-		change_cur_input(codec, !spec->automic_idx, 0);
+
+	/* specific to CS421x, single ADC */
+	if (spec->vendor_nid == CS421X_VENDOR_NID) {
+		if (present) {
+			spec->last_input = spec->cur_input;
+			spec->cur_input = spec->automic_idx;
+		} else  {
+			spec->cur_input = spec->last_input;
+		}
+
+		snd_hda_codec_write_cache(codec, spec->cur_adc, 0,
+					AC_VERB_SET_CONNECT_SEL,
+					spec->adc_idx[spec->cur_input]);
+	} else {
+		if (present)
+			change_cur_input(codec, spec->automic_idx, 0);
+		else
+			change_cur_input(codec, !spec->automic_idx, 0);
+	}
 }
 
 /*
@@ -911,23 +1009,28 @@
 	for (i = 0; i < cfg->line_outs; i++)
 		snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	/* HP */
 	for (i = 0; i < cfg->hp_outs; i++) {
 		hda_nid_t nid = cfg->hp_pins[i];
 		snd_hda_codec_write(codec, nid, 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
 		if (!cfg->speaker_outs)
 			continue;
-		if (is_jack_detectable(codec, nid)) {
+		if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
 			snd_hda_codec_write(codec, nid, 0,
 					    AC_VERB_SET_UNSOLICITED_ENABLE,
 					    AC_USRSP_EN | HP_EVENT);
 			spec->hp_detect = 1;
 		}
 	}
+
+	/* Speaker */
 	for (i = 0; i < cfg->speaker_outs; i++)
 		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-	if (spec->hp_detect)
+
+	/* SPDIF is enabled on presence detect for CS421x */
+	if (spec->hp_detect || spec->spdif_detect)
 		cs_automute(codec);
 }
 
@@ -961,19 +1064,31 @@
 					    AC_VERB_SET_UNSOLICITED_ENABLE,
 					    AC_USRSP_EN | MIC_EVENT);
 	}
-	change_cur_input(codec, spec->cur_input, 1);
-	if (spec->mic_detect)
-		cs_automic(codec);
+	/* specific to CS421x */
+	if (spec->vendor_nid == CS421X_VENDOR_NID) {
+		if (spec->mic_detect)
+			cs_automic(codec);
+		else  {
+			spec->cur_adc = spec->adc_nid[spec->cur_input];
+			snd_hda_codec_write(codec, spec->cur_adc, 0,
+					AC_VERB_SET_CONNECT_SEL,
+					spec->adc_idx[spec->cur_input]);
+		}
+	} else {
+		change_cur_input(codec, spec->cur_input, 1);
+		if (spec->mic_detect)
+			cs_automic(codec);
 
-	coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
-	if (is_active_pin(codec, CS_DMIC2_PIN_NID))
-		coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
-	if (is_active_pin(codec, CS_DMIC1_PIN_NID))
-		coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 
-				 * No effect if SPDIF_OUT2 is selected in 
-				 * IDX_SPDIF_CTL.
-				  */
-	cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+		coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+		if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+			coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */
+		if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+			coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off
+					 * No effect if SPDIF_OUT2 is
+					 * selected in IDX_SPDIF_CTL.
+					*/
+		cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+	}
 }
 
 static const struct hda_verb cs_coef_init_verbs[] = {
@@ -1221,16 +1336,16 @@
 	[CS420X_IMAC27] = imac27_pincfgs,
 };
 
-static void fix_pincfg(struct hda_codec *codec, int model)
+static void fix_pincfg(struct hda_codec *codec, int model,
+		       const struct cs_pincfg **pin_configs)
 {
-	const struct cs_pincfg *cfg = cs_pincfgs[model];
+	const struct cs_pincfg *cfg = pin_configs[model];
 	if (!cfg)
 		return;
 	for (; cfg->nid; cfg++)
 		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
 }
 
-
 static int patch_cs420x(struct hda_codec *codec)
 {
 	struct cs_spec *spec;
@@ -1241,11 +1356,13 @@
 		return -ENOMEM;
 	codec->spec = spec;
 
+	spec->vendor_nid = CS420X_VENDOR_NID;
+
 	spec->board_config =
 		snd_hda_check_board_config(codec, CS420X_MODELS,
 					   cs420x_models, cs420x_cfg_tbl);
 	if (spec->board_config >= 0)
-		fix_pincfg(codec, spec->board_config);
+		fix_pincfg(codec, spec->board_config, cs_pincfgs);
 
 	switch (spec->board_config) {
 	case CS420X_IMAC27:
@@ -1272,6 +1389,562 @@
 	return err;
 }
 
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+
+/* CS4210 board names */
+static const char *cs421x_models[CS421X_MODELS] = {
+	[CS421X_CDB4210] = "cdb4210",
+};
+
+static const struct snd_pci_quirk cs421x_cfg_tbl[] = {
+	/* Test Intel board + CDB2410  */
+	SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+	{} /* terminator */
+};
+
+/* CS4210 board pinconfigs */
+/* Default CS4210 (CDB4210)*/
+static const struct cs_pincfg cdb4210_pincfgs[] = {
+	{ 0x05, 0x0321401f },
+	{ 0x06, 0x90170010 },
+	{ 0x07, 0x03813031 },
+	{ 0x08, 0xb7a70037 },
+	{ 0x09, 0xb7a6003e },
+	{ 0x0a, 0x034510f0 },
+	{} /* terminator */
+};
+
+static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = {
+	[CS421X_CDB4210] = cdb4210_pincfgs,
+};
+
+static const struct hda_verb cs421x_coef_init_verbs[] = {
+	{0x0B, AC_VERB_SET_PROC_STATE, 1},
+	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
+	/*
+	    Disable Coefficient Index Auto-Increment(DAI)=1,
+	    PDREF=0
+	*/
+	{0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
+	/* ADC SZCMode = Digital Soft Ramp */
+	{0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
+	{0x0B, AC_VERB_SET_PROC_COEF,
+	 (0x0002 /* DAC SZCMode = Digital Soft Ramp */
+	  | 0x0004 /* Mute DAC on FIFO error */
+	  | 0x0008 /* Enable DAC High Pass Filter */
+	  )},
+	{} /* terminator */
+};
+
+/* Errata: CS4210 rev A1 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/
+ *
+ * Description:
+ * 1. Performance degredation is present in the ADC.
+ * 2. Speaker output is not completely muted upon HP detect.
+ * 3. Noise is present when clipping occurs on the amplified
+ *    speaker outputs.
+ *
+ * Workaround:
+ * The following verb sequence written to the registers during
+ * initialization will correct the issues listed above.
+ */
+
+static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+	{0x0B, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
+	{0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
+	{0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
+	{0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
+	{0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
+
+	{0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
+	{0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
+
+	{} /* terminator */
+};
+
+/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
+static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
+
+static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 3;
+	return 0;
+}
+
+static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] =
+		cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
+	return 0;
+}
+
+static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	unsigned int vol = ucontrol->value.integer.value[0];
+	unsigned int coef =
+		cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
+	unsigned int original_coef = coef;
+
+	coef &= ~0x0003;
+	coef |= (vol & 0x0003);
+	if (original_coef == coef)
+		return 0;
+	else {
+		cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+		return 1;
+	}
+}
+
+static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = {
+
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+	.name = "Speaker Boost Playback Volume",
+	.info = cs421x_boost_vol_info,
+	.get = cs421x_boost_vol_get,
+	.put = cs421x_boost_vol_put,
+	.tlv = { .p = cs421x_speaker_boost_db_scale },
+};
+
+static void cs421x_pinmux_init(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	unsigned int def_conf, coef;
+
+	/* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+	coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+
+	if (spec->gpio_mask)
+		coef |= 0x0008; /* B1,B2 are GPIOs */
+	else
+		coef &= ~0x0008;
+
+	if (spec->sense_b)
+		coef |= 0x0010; /* B2 is SENSE_B, not inverted  */
+	else
+		coef &= ~0x0010;
+
+	cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+	if ((spec->gpio_mask || spec->sense_b) &&
+	    is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
+
+		/*
+		    GPIO or SENSE_B forced - disconnect the DMIC pin.
+		*/
+		def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
+		def_conf &= ~AC_DEFCFG_PORT_CONN;
+		def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
+		snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
+	}
+}
+
+static void init_cs421x_digital(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+
+	for (i = 0; i < cfg->dig_outs; i++) {
+		hda_nid_t nid = cfg->dig_out_pins[i];
+		if (!cfg->speaker_outs)
+			continue;
+		if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+
+			snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_UNSOLICITED_ENABLE,
+				    AC_USRSP_EN | SPDIF_EVENT);
+			spec->spdif_detect = 1;
+		}
+	}
+}
+
+static int cs421x_init(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+
+	snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
+	snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
+
+	cs421x_pinmux_init(codec);
+
+	if (spec->gpio_mask) {
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+				    spec->gpio_mask);
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+				    spec->gpio_dir);
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+				    spec->gpio_data);
+	}
+
+	init_output(codec);
+	init_input(codec);
+	init_cs421x_digital(codec);
+
+	return 0;
+}
+
+/*
+ * CS4210 Input MUX (1 ADC)
+ */
+static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_input;
+	return 0;
+}
+
+static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+				spec->adc_nid[0], &spec->cur_input);
+
+}
+
+static struct snd_kcontrol_new cs421x_capture_source = {
+
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = cs421x_mux_enum_info,
+	.get = cs421x_mux_enum_get,
+	.put = cs421x_mux_enum_put,
+};
+
+static int cs421x_add_input_volume_control(struct hda_codec *codec, int item)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	const struct hda_input_mux *imux = &spec->input_mux;
+	hda_nid_t pin = cfg->inputs[item].pin;
+	struct snd_kcontrol *kctl;
+	u32 caps;
+
+	if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+		return 0;
+
+	caps = query_amp_caps(codec, pin, HDA_INPUT);
+	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	if (caps <= 1)
+		return 0;
+
+	return add_volume(codec,  imux->items[item].label, 0,
+			  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
+/* add a (input-boost) volume control to the given input pin */
+static int build_cs421x_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct hda_input_mux *imux = &spec->input_mux;
+	int i, err, type_idx;
+	const char *label;
+
+	if (!spec->num_inputs)
+		return 0;
+
+	/* make bind-capture */
+	spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+	spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+	for (i = 0; i < 2; i++) {
+		struct snd_kcontrol *kctl;
+		int n;
+		if (!spec->capture_bind[i])
+			return -ENOMEM;
+		kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->private_value = (long)spec->capture_bind[i];
+		err = snd_hda_ctl_add(codec, 0, kctl);
+		if (err < 0)
+			return err;
+		for (n = 0; n < AUTO_PIN_LAST; n++) {
+			if (!spec->adc_nid[n])
+				continue;
+			err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
+			if (err < 0)
+				return err;
+		}
+	}
+
+	/* Add Input MUX Items + Capture Volume/Switch */
+	for (i = 0; i < spec->num_inputs; i++) {
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx);
+
+		err = cs421x_add_input_volume_control(codec, i);
+		if (err < 0)
+			return err;
+	}
+
+	/*
+	    Add 'Capture Source' Switch if
+		* 2 inputs and no mic detec
+		* 3 inputs
+	*/
+	if ((spec->num_inputs == 2 && !spec->mic_detect) ||
+	    (spec->num_inputs == 3)) {
+
+		err = snd_hda_ctl_add(codec, spec->adc_nid[0],
+			      snd_ctl_new1(&cs421x_capture_source, codec));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Single DAC (Mute/Gain) */
+static int build_cs421x_output(struct hda_codec *codec)
+{
+	hda_nid_t dac = CS4210_DAC_NID;
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct snd_kcontrol *kctl;
+	int err;
+	char *name = "HP/Speakers";
+
+	fix_volume_caps(codec, dac);
+	if (!spec->vmaster_sw) {
+		err = add_vmaster(codec, dac);
+		if (err < 0)
+			return err;
+	}
+
+	err = add_mute(codec, name, 0,
+			HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
+	if (err < 0)
+		return err;
+
+	err = add_volume(codec, name, 0,
+			HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+	if (err < 0)
+		return err;
+
+	if (cfg->speaker_outs) {
+		err = snd_hda_ctl_add(codec, 0,
+			snd_ctl_new1(&cs421x_speaker_bost_ctl, codec));
+		if (err < 0)
+			return err;
+	}
+	return err;
+}
+
+static int cs421x_build_controls(struct hda_codec *codec)
+{
+	int err;
+
+	err = build_cs421x_output(codec);
+	if (err < 0)
+		return err;
+	err = build_cs421x_input(codec);
+	if (err < 0)
+		return err;
+	err = build_digital_output(codec);
+	if (err < 0)
+		return err;
+	return cs421x_init(codec);
+}
+
+static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	switch ((res >> 26) & 0x3f) {
+	case HP_EVENT:
+	case SPDIF_EVENT:
+		cs_automute(codec);
+		break;
+
+	case MIC_EVENT:
+		cs_automic(codec);
+		break;
+	}
+}
+
+static int parse_cs421x_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t pin = cfg->inputs[i].pin;
+		spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+		spec->cur_input = spec->last_input = i;
+		spec->num_inputs++;
+
+		/* check whether the automatic mic switch is available */
+		if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) {
+			spec->mic_detect = 1;
+			spec->automic_idx = i;
+		}
+	}
+	return 0;
+}
+
+static int cs421x_parse_auto_config(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+	if (err < 0)
+		return err;
+	err = parse_output(codec);
+	if (err < 0)
+		return err;
+	err = parse_cs421x_input(codec);
+	if (err < 0)
+		return err;
+	err = parse_digital_output(codec);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+	Manage PDREF, when transitioning to D3hot
+	(DAC,ADC) -> D3, PDREF=1, AFG->D3
+*/
+static int cs421x_suspend(struct hda_codec *codec, pm_message_t state)
+{
+	unsigned int coef;
+
+	snd_hda_shutup_pins(codec);
+
+	snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
+			    AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
+	snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
+			    AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
+
+	coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+	coef |= 0x0004; /* PDREF */
+	cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+	return 0;
+}
+#endif
+
+static struct hda_codec_ops cs4210_patch_ops = {
+	.build_controls = cs421x_build_controls,
+	.build_pcms = cs_build_pcms,
+	.init = cs421x_init,
+	.free = cs_free,
+	.unsol_event = cs421x_unsol_event,
+#ifdef CONFIG_PM
+	.suspend = cs421x_suspend,
+#endif
+};
+
+static int patch_cs421x(struct hda_codec *codec)
+{
+	struct cs_spec *spec;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+
+	spec->vendor_nid = CS421X_VENDOR_NID;
+
+	spec->board_config =
+		snd_hda_check_board_config(codec, CS421X_MODELS,
+					   cs421x_models, cs421x_cfg_tbl);
+	if (spec->board_config >= 0)
+		fix_pincfg(codec, spec->board_config, cs421x_pincfgs);
+	/*
+	    Setup GPIO/SENSE for each board (if used)
+	*/
+	switch (spec->board_config) {
+	case CS421X_CDB4210:
+		snd_printd("CS4210 board: %s\n",
+			cs421x_models[spec->board_config]);
+/*		spec->gpio_mask = 3;
+		spec->gpio_dir = 3;
+		spec->gpio_data = 3;
+*/
+		spec->sense_b = 1;
+
+		break;
+	}
+
+	/*
+	    Update the GPIO/DMIC/SENSE_B pinmux before the configuration
+	    is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
+	    is disabled.
+	*/
+	cs421x_pinmux_init(codec);
+
+	err = cs421x_parse_auto_config(codec);
+	if (err < 0)
+		goto error;
+
+	codec->patch_ops = cs4210_patch_ops;
+
+	return 0;
+
+ error:
+	kfree(codec->spec);
+	codec->spec = NULL;
+	return err;
+}
+
 
 /*
  * patch entries
@@ -1279,11 +1952,13 @@
 static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
 	{ .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
 	{ .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+	{ .id = 0x10134210, .name = "CS4210", .patch = patch_cs421x },
 	{} /* terminator */
 };
 
 MODULE_ALIAS("snd-hda-codec-id:10134206");
 MODULE_ALIAS("snd-hda-codec-id:10134207");
+MODULE_ALIAS("snd-hda-codec-id:10134210");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 884f67b..502fc94 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -446,6 +446,19 @@
 	return 0;
 }
 
+static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
+			       unsigned int power_state)
+{
+	if (power_state == AC_PWRST_D3)
+		msleep(100);
+	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+			    power_state);
+	/* partial workaround for "azx_get_response timeout" */
+	if (power_state == AC_PWRST_D0)
+		msleep(10);
+	snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+}
+
 static int conexant_init(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
@@ -588,6 +601,7 @@
 	.build_pcms = conexant_build_pcms,
 	.init = conexant_init,
 	.free = conexant_free,
+	.set_power_state = conexant_set_power,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	.suspend = conexant_suspend,
 #endif
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 52ce075..e125c60 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -895,13 +895,15 @@
 	if (present == 3)
 		spec->automute_hp_lo = 1; /* both HP and LO automute */
 
-	if (!cfg->speaker_pins[0]) {
+	if (!cfg->speaker_pins[0] &&
+	    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
 		memcpy(cfg->speaker_pins, cfg->line_out_pins,
 		       sizeof(cfg->speaker_pins));
 		cfg->speaker_outs = cfg->line_outs;
 	}
 
-	if (!cfg->hp_pins[0]) {
+	if (!cfg->hp_pins[0] &&
+	    cfg->line_out_type == AUTO_PIN_HP_OUT) {
 		memcpy(cfg->hp_pins, cfg->line_out_pins,
 		       sizeof(cfg->hp_pins));
 		cfg->hp_outs = cfg->line_outs;
@@ -920,6 +922,7 @@
 		spec->automute_mode = ALC_AUTOMUTE_PIN;
 	}
 	if (spec->automute && cfg->line_out_pins[0] &&
+	    cfg->speaker_pins[0] &&
 	    cfg->line_out_pins[0] != cfg->hp_pins[0] &&
 	    cfg->line_out_pins[0] != cfg->speaker_pins[0]) {
 		for (i = 0; i < cfg->line_outs; i++) {
@@ -1911,7 +1914,7 @@
 				return err;
 		}
 	}
-	if (spec->cap_mixer) {
+	if (spec->cap_mixer && spec->adc_nids) {
 		const char *kname = kctl ? kctl->id.name : NULL;
 		for (knew = spec->cap_mixer; knew->name; knew++) {
 			if (kname && strcmp(knew->name, kname) == 0)
@@ -2386,7 +2389,7 @@
 }
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int alc_resume(struct hda_codec *codec)
 {
 	msleep(150); /* to avoid pop noise */
@@ -2406,7 +2409,7 @@
 	.init = alc_init,
 	.free = alc_free,
 	.unsol_event = alc_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 	.resume = alc_resume,
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -2801,7 +2804,8 @@
 	int i;
 
  again:
-	spec->multiout.num_dacs = 0;
+	/* set num_dacs once to full for alc_auto_look_for_dac() */
+	spec->multiout.num_dacs = cfg->line_outs;
 	spec->multiout.hp_nid = 0;
 	spec->multiout.extra_out_nid[0] = 0;
 	memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
@@ -2834,6 +2838,8 @@
 		}
 	}
 
+	/* re-count num_dacs and squash invalid entries */
+	spec->multiout.num_dacs = 0;
 	for (i = 0; i < cfg->line_outs; i++) {
 		if (spec->private_dac_nids[i])
 			spec->multiout.num_dacs++;
@@ -3674,7 +3680,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc880_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -3801,7 +3807,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc260_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -3980,7 +3986,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc882_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -4134,7 +4140,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc262_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -4290,7 +4296,7 @@
 					  (0 << AC_AMPCAP_MUTE_SHIFT));
 	}
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -4410,7 +4416,7 @@
 	}
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int alc269_resume(struct hda_codec *codec)
 {
 	if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
@@ -4433,7 +4439,7 @@
 	hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 static void alc269_fixup_hweq(struct hda_codec *codec,
 			       const struct alc_fixup *fix, int action)
@@ -4702,7 +4708,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc269_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -4725,7 +4731,7 @@
 	spec->vmaster_nid = 0x02;
 
 	codec->patch_ops = alc_patch_ops;
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 	codec->patch_ops.resume = alc269_resume;
 #endif
 	if (board_config == ALC_MODEL_AUTO)
@@ -4840,7 +4846,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc861_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -4981,7 +4987,7 @@
 		add_verb(spec, alc660vd_eapd_verbs);
 	}
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -5197,7 +5203,7 @@
 	if (board_config != ALC_MODEL_AUTO)
 		setup_preset(codec, &alc662_presets[board_config]);
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
@@ -5333,7 +5339,7 @@
 #endif
 	}
 
-	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
+	if (!spec->no_analog && !spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
 		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 56425a5..aa376b5 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -95,6 +95,7 @@
 	STAC_92HD83XXX_PWR_REF,
 	STAC_DELL_S14,
 	STAC_92HD83XXX_HP,
+	STAC_92HD83XXX_HP_cNB11_INTQUAD,
 	STAC_HP_DV7_4000,
 	STAC_92HD83XXX_MODELS
 };
@@ -212,6 +213,7 @@
 	unsigned int gpio_mute;
 	unsigned int gpio_led;
 	unsigned int gpio_led_polarity;
+	unsigned int vref_led;
 
 	/* stream */
 	unsigned int stream_delay;
@@ -671,6 +673,30 @@
 	return 0;
 }
 
+static int stac_vrefout_set(struct hda_codec *codec,
+					hda_nid_t nid, unsigned int new_vref)
+{
+	int error, pinctl;
+
+	snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref);
+	pinctl = snd_hda_codec_read(codec, nid, 0,
+				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+	if (pinctl < 0)
+		return pinctl;
+
+	pinctl &= 0xff;
+	pinctl &= ~AC_PINCTL_VREFEN;
+	pinctl |= (new_vref & AC_PINCTL_VREFEN);
+
+	error = snd_hda_codec_write_cache(codec, nid, 0,
+					AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+	if (error < 0)
+		return error;
+
+	return 1;
+}
+
 static unsigned int stac92xx_vref_set(struct hda_codec *codec,
 					hda_nid_t nid, unsigned int new_vref)
 {
@@ -1636,10 +1662,17 @@
 	0x40f000f0, 0x40f000f0,
 };
 
+static const unsigned int hp_cNB11_intquad_pin_configs[10] = {
+	0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110,
+	0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130,
+	0x40f000f0, 0x40f000f0,
+};
+
 static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
 	[STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
 	[STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
 	[STAC_DELL_S14] = dell_s14_pin_configs,
+	[STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs,
 	[STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
 };
 
@@ -1649,6 +1682,7 @@
 	[STAC_92HD83XXX_PWR_REF] = "mic-ref",
 	[STAC_DELL_S14] = "dell-s14",
 	[STAC_92HD83XXX_HP] = "hp",
+	[STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
 	[STAC_HP_DV7_4000] = "hp-dv7-4000",
 };
 
@@ -1661,7 +1695,47 @@
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
 		      "unknown Dell", STAC_DELL_S14),
 	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
-		      "HP", STAC_92HD83XXX_HP),
+			  "HP", STAC_92HD83XXX_HP),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
+			  "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
 	{} /* terminator */
 };
 
@@ -4020,6 +4094,8 @@
 {
 	unsigned int gpiostate, gpiomask, gpiodir;
 
+	snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
+
 	gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
 				       AC_VERB_GET_GPIO_DATA, 0);
 	gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
@@ -4209,10 +4285,12 @@
 		spec->eapd_switch = val;
 	get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
 	if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
-		spec->gpio_mask |= spec->gpio_led;
-		spec->gpio_dir |= spec->gpio_led;
-		if (spec->gpio_led_polarity)
-			spec->gpio_data |= spec->gpio_led;
+		if (spec->gpio_led <= 8) {
+			spec->gpio_mask |= spec->gpio_led;
+			spec->gpio_dir |= spec->gpio_led;
+			if (spec->gpio_led_polarity)
+				spec->gpio_data |= spec->gpio_led;
+		}
 	}
 }
 
@@ -4382,11 +4460,26 @@
 	snd_array_free(&spec->kctls);
 }
 
+static void stac92xx_shutup_pins(struct hda_codec *codec)
+{
+	unsigned int i, def_conf;
+
+	if (codec->bus->shutdown)
+		return;
+	for (i = 0; i < codec->init_pins.used; i++) {
+		struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+		def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
+		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
+			snd_hda_codec_write(codec, pin->nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+	}
+}
+
 static void stac92xx_shutup(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
 
-	snd_hda_shutup_pins(codec);
+	stac92xx_shutup_pins(codec);
 
 	if (spec->eapd_mask)
 		stac_gpio_set(codec, spec->gpio_mask,
@@ -4784,10 +4877,11 @@
 	if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
 		while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
 								NULL, dev))) {
-			if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
+			if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
 				  &spec->gpio_led_polarity,
 				  &spec->gpio_led) == 2) {
-				spec->gpio_led = 1 << spec->gpio_led;
+				if (spec->gpio_led < 4)
+					spec->gpio_led = 1 << spec->gpio_led;
 				return 1;
 			}
 			if (sscanf(dev->name, "HP_Mute_LED_%d",
@@ -4885,7 +4979,7 @@
 #define stac927x_proc_hook	NULL
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int stac92xx_resume(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
@@ -4901,29 +4995,81 @@
 			stac_issue_unsol_event(codec,
 					       spec->autocfg.line_out_pins[0]);
 	}
-	/* sync mute LED */
-	if (spec->gpio_led)
-		hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
 
-/*
- * using power check for controlling mute led of HP notebooks
- * check for mute state only on Speakers (nid = 0x10)
- *
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
- * the LED is NOT working properly !
- *
- * Changed name to reflect that it now works for any designated
- * model, not just HP HDX.
- */
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+	stac92xx_shutup(codec);
+	return 0;
+}
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_check_power_status(struct hda_codec *codec,
-					      hda_nid_t nid)
+static int stac92xx_pre_resume(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	int i, muted = 1;
+
+	/* sync mute LED */
+	if (spec->gpio_led) {
+		if (spec->gpio_led <= 8) {
+			stac_gpio_set(codec, spec->gpio_mask,
+					spec->gpio_dir, spec->gpio_data);
+		} else {
+			stac_vrefout_set(codec,
+					spec->gpio_led, spec->vref_led);
+		}
+	}
+	return 0;
+}
+
+static int stac92xx_post_suspend(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	if (spec->gpio_led > 8) {
+		/* with vref-out pin used for mute led control
+		 * codec AFG is prevented from D3 state, but on
+		 * system suspend it can (and should) be used
+		 */
+		snd_hda_codec_read(codec, codec->afg, 0,
+				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+	}
+	return 0;
+}
+
+static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+				unsigned int power_state)
+{
+	unsigned int afg_power_state = power_state;
+	struct sigmatel_spec *spec = codec->spec;
+
+	if (power_state == AC_PWRST_D3) {
+		if (spec->gpio_led > 8) {
+			/* with vref-out pin used for mute led control
+			 * codec AFG is prevented from D3 state
+			 */
+			afg_power_state = AC_PWRST_D1;
+		}
+		/* this delay seems necessary to avoid click noise at power-down */
+		msleep(100);
+	}
+	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+			afg_power_state);
+	snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
+}
+
+/*
+ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed
+ * as mute LED state is updated in check_power_status hook
+ */
+static int stac92xx_update_led_status(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i, num_ext_dacs, muted = 1;
+	unsigned int muted_lvl, notmtd_lvl;
+	hda_nid_t nid;
+
+	if (!spec->gpio_led)
+		return 0;
 
 	for (i = 0; i < spec->multiout.num_dacs; i++) {
 		nid = spec->multiout.dac_nids[i];
@@ -4933,27 +5079,58 @@
 			break;
 		}
 	}
-	if (muted)
-		spec->gpio_data &= ~spec->gpio_led; /* orange */
-	else
-		spec->gpio_data |= spec->gpio_led; /* white */
-
-	if (!spec->gpio_led_polarity) {
-		/* LED state is inverted on these systems */
-		spec->gpio_data ^= spec->gpio_led;
+	if (muted && spec->multiout.hp_nid)
+		if (!(snd_hda_codec_amp_read(codec,
+				spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) &
+					HDA_AMP_MUTE)) {
+			muted = 0; /* HP is not muted */
+		}
+	num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid);
+	for (i = 0; muted && i < num_ext_dacs; i++) {
+		nid = spec->multiout.extra_out_nid[i];
+		if (nid == 0)
+			break;
+		if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+		      HDA_AMP_MUTE)) {
+			muted = 0; /* extra output is not muted */
+		}
 	}
+	/*polarity defines *not* muted state level*/
+	if (spec->gpio_led <= 8) {
+		if (muted)
+			spec->gpio_data &= ~spec->gpio_led; /* orange */
+		else
+			spec->gpio_data |= spec->gpio_led; /* white */
 
-	stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+		if (!spec->gpio_led_polarity) {
+			/* LED state is inverted on these systems */
+			spec->gpio_data ^= spec->gpio_led;
+		}
+		stac_gpio_set(codec, spec->gpio_mask,
+				spec->gpio_dir, spec->gpio_data);
+	} else {
+		notmtd_lvl = spec->gpio_led_polarity ?
+				AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD;
+		muted_lvl = spec->gpio_led_polarity ?
+				AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ;
+		spec->vref_led = muted ? muted_lvl : notmtd_lvl;
+		stac_vrefout_set(codec,	spec->gpio_led, spec->vref_led);
+	}
 	return 0;
 }
-#endif
 
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+/*
+ * use power check for controlling mute led of HP notebooks
+ */
+static int stac92xx_check_power_status(struct hda_codec *codec,
+					      hda_nid_t nid)
 {
-	stac92xx_shutup(codec);
+	stac92xx_update_led_status(codec);
+
 	return 0;
 }
-#endif
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+#endif /* CONFIG_PM */
 
 static const struct hda_codec_ops stac92xx_patch_ops = {
 	.build_controls = stac92xx_build_controls,
@@ -4961,7 +5138,7 @@
 	.init = stac92xx_init,
 	.free = stac92xx_free,
 	.unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 	.suspend = stac92xx_suspend,
 	.resume = stac92xx_resume,
 #endif
@@ -5477,12 +5654,19 @@
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (spec->gpio_led) {
-		spec->gpio_mask |= spec->gpio_led;
-		spec->gpio_dir |= spec->gpio_led;
-		spec->gpio_data |= spec->gpio_led;
-		/* register check_power_status callback. */
+		if (spec->gpio_led <= 8) {
+			spec->gpio_mask |= spec->gpio_led;
+			spec->gpio_dir |= spec->gpio_led;
+			spec->gpio_data |= spec->gpio_led;
+		} else {
+			codec->patch_ops.set_power_state =
+					stac92xx_set_power_state;
+			codec->patch_ops.post_suspend =
+					stac92xx_post_suspend;
+		}
+		codec->patch_ops.pre_resume = stac92xx_pre_resume;
 		codec->patch_ops.check_power_status =
-			stac92xx_hp_check_power_status;
+			stac92xx_check_power_status;
 	}
 #endif	
 
@@ -5805,12 +5989,19 @@
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (spec->gpio_led) {
-		spec->gpio_mask |= spec->gpio_led;
-		spec->gpio_dir |= spec->gpio_led;
-		spec->gpio_data |= spec->gpio_led;
-		/* register check_power_status callback. */
+		if (spec->gpio_led <= 8) {
+			spec->gpio_mask |= spec->gpio_led;
+			spec->gpio_dir |= spec->gpio_led;
+			spec->gpio_data |= spec->gpio_led;
+		} else {
+			codec->patch_ops.set_power_state =
+					stac92xx_set_power_state;
+			codec->patch_ops.post_suspend =
+					stac92xx_post_suspend;
+		}
+		codec->patch_ops.pre_resume = stac92xx_pre_resume;
 		codec->patch_ops.check_power_status =
-			stac92xx_hp_check_power_status;
+			stac92xx_check_power_status;
 	}
 #endif	
 
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index f38160b..84d8798 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1708,7 +1708,7 @@
 		via_gpio_control(codec);
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int via_suspend(struct hda_codec *codec, pm_message_t state)
 {
 	struct via_spec *spec = codec->spec;
@@ -1736,7 +1736,7 @@
 	.init = via_init,
 	.free = via_free,
 	.unsol_event = via_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 	.suspend = via_suspend,
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
index e2a124a..6792eda 100644
--- a/sound/pci/lx6464es/lx6464es.h
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -26,7 +26,7 @@
 #define LX6464ES_H
 
 #include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 36a030f..379b2e3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -250,10 +250,11 @@
 	tristate
 
 config SND_SOC_TWL4030
-	select TWL4030_CODEC
+	select MFD_TWL4030_AUDIO
 	tristate
 
 config SND_SOC_TWL6040
+	select TWL6040_CORE
 	tristate
 
 config SND_SOC_UDA134X
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index ff29380..76258f2 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -907,6 +907,7 @@
 				struct regulator_init_data *init_data,
 				int voltage)
 {
+	dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
 	return -EINVAL;
 }
 
@@ -1218,6 +1219,34 @@
 	return 0;
 }
 
+static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
+{
+	struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	/* set internal ldo to 1.2v */
+	ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE);
+	if (ret) {
+		dev_err(codec->dev,
+			"Failed to register vddd internal supplies: %d\n", ret);
+		return ret;
+	}
+
+	sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+			sgtl5000->supplies);
+
+	if (ret) {
+		ldo_regulator_remove(codec);
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		return ret;
+	}
+
+	dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
+	return 0;
+}
+
 static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
 {
 	u16 reg;
@@ -1235,30 +1264,9 @@
 	if (!ret)
 		external_vddd = 1;
 	else {
-		/* set internal ldo to 1.2v */
-		int voltage = LDO_VOLTAGE;
-
-		ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
-		if (ret) {
-			dev_err(codec->dev,
-			"Failed to register vddd internal supplies: %d\n",
-				ret);
+		ret = sgtl5000_replace_vddd_with_ldo(codec);
+		if (ret)
 			return ret;
-		}
-
-		sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
-		ret = regulator_bulk_get(codec->dev,
-				ARRAY_SIZE(sgtl5000->supplies),
-				sgtl5000->supplies);
-
-		if (ret) {
-			ldo_regulator_remove(codec);
-			dev_err(codec->dev,
-				"Failed to request supplies: %d\n", ret);
-
-			return ret;
-		}
 	}
 
 	ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
@@ -1287,7 +1295,6 @@
 	 * roll back to use internal LDO
 	 */
 	if (external_vddd && rev >= 0x11) {
-		int voltage = LDO_VOLTAGE;
 		/* disable all regulator first */
 		regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
 					sgtl5000->supplies);
@@ -1295,23 +1302,10 @@
 		regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
 					sgtl5000->supplies);
 
-		ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+		ret = sgtl5000_replace_vddd_with_ldo(codec);
 		if (ret)
 			return ret;
 
-		sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
-		ret = regulator_bulk_get(codec->dev,
-				ARRAY_SIZE(sgtl5000->supplies),
-				sgtl5000->supplies);
-		if (ret) {
-			ldo_regulator_remove(codec);
-			dev_err(codec->dev,
-				"Failed to request supplies: %d\n", ret);
-
-			return ret;
-		}
-
 		ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
 						sgtl5000->supplies);
 		if (ret)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index bec788b..71674be 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -36,7 +36,7 @@
 #include <sound/tlv.h>
 
 /* Register descriptions are here */
-#include <linux/mfd/twl4030-codec.h>
+#include <linux/mfd/twl4030-audio.h>
 
 /* Shadow register used by the audio driver */
 #define TWL4030_REG_SW_SHADOW		0x4A
@@ -251,9 +251,9 @@
 		return;
 
 	if (enable)
-		mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
+		mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
 	else
-		mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
+		mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
 
 	if (mode >= 0) {
 		twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
@@ -297,7 +297,7 @@
 
 static void twl4030_init_chip(struct snd_soc_codec *codec)
 {
-	struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev);
+	struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 reg, byte;
 	int i = 0;
@@ -375,13 +375,13 @@
 	if (enable) {
 		twl4030->apll_enabled++;
 		if (twl4030->apll_enabled == 1)
-			status = twl4030_codec_enable_resource(
-							TWL4030_CODEC_RES_APLL);
+			status = twl4030_audio_enable_resource(
+							TWL4030_AUDIO_RES_APLL);
 	} else {
 		twl4030->apll_enabled--;
 		if (!twl4030->apll_enabled)
-			status = twl4030_codec_disable_resource(
-							TWL4030_CODEC_RES_APLL);
+			status = twl4030_audio_disable_resource(
+							TWL4030_AUDIO_RES_APLL);
 	}
 
 	if (status >= 0)
@@ -732,7 +732,7 @@
 
 static void headset_ramp(struct snd_soc_codec *codec, int ramp)
 {
-	struct twl4030_codec_audio_data *pdata = codec->dev->platform_data;
+	struct twl4030_codec_data *pdata = codec->dev->platform_data;
 	unsigned char hs_gain, hs_pop;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	/* Base values for ramp delay calculation: 2^19 - 2^26 */
@@ -2260,7 +2260,7 @@
 	}
 	snd_soc_codec_set_drvdata(codec, twl4030);
 	/* Set the defaults, and power up the codec */
-	twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
+	twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
 	codec->dapm.idle_bias_off = 1;
 
 	twl4030_init_chip(codec);
@@ -2297,7 +2297,7 @@
 
 static int __devinit twl4030_codec_probe(struct platform_device *pdev)
 {
-	struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
+	struct twl4030_codec_data *pdata = pdev->dev.platform_data;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "platform_data is missing\n");
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index cd63bba..443032b 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -24,11 +24,10 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
-#include <linux/i2c.h>
-#include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/i2c/twl.h>
+#include <linux/mfd/twl6040.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -77,14 +76,19 @@
 
 /* codec private data */
 struct twl6040_data {
-	int audpwron;
-	int naudint;
+	int plug_irq;
 	int codec_powered;
 	int pll;
 	int non_lp;
+	int pll_power_mode;
+	int hs_power_mode;
+	int hs_power_mode_locked;
+	unsigned int clk_in;
 	unsigned int sysclk;
-	struct snd_pcm_hw_constraint_list *sysclk_constraints;
-	struct completion ready;
+	u16 hs_left_step;
+	u16 hs_right_step;
+	u16 hf_left_step;
+	u16 hf_right_step;
 	struct twl6040_jack_data hs_jack;
 	struct snd_soc_codec *codec;
 	struct workqueue_struct *workqueue;
@@ -206,6 +210,32 @@
 	TWL6040_REG_DLB,
 };
 
+/* set of rates for each pll: low-power and high-performance */
+static unsigned int lp_rates[] = {
+	8000,
+	11250,
+	16000,
+	22500,
+	32000,
+	44100,
+	48000,
+	88200,
+	96000,
+};
+
+static unsigned int hp_rates[] = {
+	8000,
+	16000,
+	32000,
+	48000,
+	96000,
+};
+
+static struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
+	{ .count = ARRAY_SIZE(lp_rates), .list = lp_rates, },
+	{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
+};
+
 /*
  * read twl6040 register cache
  */
@@ -239,12 +269,13 @@
 static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
 			unsigned int reg)
 {
+	struct twl6040 *twl6040 = codec->control_data;
 	u8 value;
 
 	if (reg >= TWL6040_CACHEREGNUM)
 		return -EIO;
 
-	twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg);
+	value = twl6040_reg_read(twl6040, reg);
 	twl6040_write_reg_cache(codec, reg, value);
 
 	return value;
@@ -256,11 +287,13 @@
 static int twl6040_write(struct snd_soc_codec *codec,
 			unsigned int reg, unsigned int value)
 {
+	struct twl6040 *twl6040 = codec->control_data;
+
 	if (reg >= TWL6040_CACHEREGNUM)
 		return -EIO;
 
 	twl6040_write_reg_cache(codec, reg, value);
-	return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg);
+	return twl6040_reg_write(twl6040, reg, value);
 }
 
 static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
@@ -268,15 +301,21 @@
 	u8 *cache = codec->reg_cache;
 	int reg, i;
 
-	/* allow registers to be accessed by i2c */
-	twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]);
-
 	for (i = 0; i < TWL6040_VIOREGNUM; i++) {
 		reg = twl6040_vio_reg[i];
-		/* skip read-only registers (ASICID, ASICREV, STATUS) */
+		/*
+		 * skip read-only registers (ASICID, ASICREV, STATUS)
+		 * and registers shared among MFD children
+		 */
 		switch (reg) {
 		case TWL6040_REG_ASICID:
 		case TWL6040_REG_ASICREV:
+		case TWL6040_REG_INTID:
+		case TWL6040_REG_INTMR:
+		case TWL6040_REG_NCPCTL:
+		case TWL6040_REG_LDOCTL:
+		case TWL6040_REG_GPOCTL:
+		case TWL6040_REG_ACCCTL:
 		case TWL6040_REG_STATUS:
 			continue;
 		default:
@@ -293,6 +332,20 @@
 
 	for (i = 0; i < TWL6040_VDDREGNUM; i++) {
 		reg = twl6040_vdd_reg[i];
+		/* skip vibra and PLL registers */
+		switch (reg) {
+		case TWL6040_REG_VIBCTLL:
+		case TWL6040_REG_VIBDATL:
+		case TWL6040_REG_VIBCTLR:
+		case TWL6040_REG_VIBDATR:
+		case TWL6040_REG_HPPLLCTL:
+		case TWL6040_REG_LPPLLCTL:
+		case TWL6040_REG_LPPLLDIV:
+			continue;
+		default:
+			break;
+		}
+
 		twl6040_write(codec, reg, cache[reg]);
 	}
 }
@@ -317,7 +370,11 @@
 	if (headset->ramp == TWL6040_RAMP_UP) {
 		/* ramp step up */
 		if (val < headset->left_vol) {
-			val += left_step;
+			if (val + left_step > headset->left_vol)
+				val = headset->left_vol;
+			else
+				val += left_step;
+
 			reg &= ~TWL6040_HSL_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HSGAIN,
 					(reg | (~val & TWL6040_HSL_VOL_MASK)));
@@ -327,7 +384,11 @@
 	} else if (headset->ramp == TWL6040_RAMP_DOWN) {
 		/* ramp step down */
 		if (val > 0x0) {
-			val -= left_step;
+			if ((int)val - (int)left_step < 0)
+				val = 0;
+			else
+				val -= left_step;
+
 			reg &= ~TWL6040_HSL_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HSGAIN, reg |
 						(~val & TWL6040_HSL_VOL_MASK));
@@ -344,7 +405,11 @@
 	if (headset->ramp == TWL6040_RAMP_UP) {
 		/* ramp step up */
 		if (val < headset->right_vol) {
-			val += right_step;
+			if (val + right_step > headset->right_vol)
+				val = headset->right_vol;
+			else
+				val += right_step;
+
 			reg &= ~TWL6040_HSR_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HSGAIN,
 				(reg | (~val << TWL6040_HSR_VOL_SHIFT)));
@@ -354,7 +419,11 @@
 	} else if (headset->ramp == TWL6040_RAMP_DOWN) {
 		/* ramp step down */
 		if (val > 0x0) {
-			val -= right_step;
+			if ((int)val - (int)right_step < 0)
+				val = 0;
+			else
+				val -= right_step;
+
 			reg &= ~TWL6040_HSR_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HSGAIN,
 					 reg | (~val << TWL6040_HSR_VOL_SHIFT));
@@ -385,7 +454,11 @@
 	if (handsfree->ramp == TWL6040_RAMP_UP) {
 		/* ramp step up */
 		if (val < handsfree->left_vol) {
-			val += left_step;
+			if (val + left_step > handsfree->left_vol)
+				val = handsfree->left_vol;
+			else
+				val += left_step;
+
 			reg &= ~TWL6040_HF_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HFLGAIN,
 						reg | (0x1D - val));
@@ -395,7 +468,11 @@
 	} else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
 		/* ramp step down */
 		if (val > 0) {
-			val -= left_step;
+			if ((int)val - (int)left_step < 0)
+				val = 0;
+			else
+				val -= left_step;
+
 			reg &= ~TWL6040_HF_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HFLGAIN,
 						reg | (0x1D - val));
@@ -412,7 +489,11 @@
 	if (handsfree->ramp == TWL6040_RAMP_UP) {
 		/* ramp step up */
 		if (val < handsfree->right_vol) {
-			val += right_step;
+			if (val + right_step > handsfree->right_vol)
+				val = handsfree->right_vol;
+			else
+				val += right_step;
+
 			reg &= ~TWL6040_HF_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HFRGAIN,
 						reg | (0x1D - val));
@@ -422,7 +503,11 @@
 	} else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
 		/* ramp step down */
 		if (val > 0) {
-			val -= right_step;
+			if ((int)val - (int)right_step < 0)
+				val = 0;
+			else
+				val -= right_step;
+
 			reg &= ~TWL6040_HF_VOL_MASK;
 			twl6040_write(codec, TWL6040_REG_HFRGAIN,
 						reg | (0x1D - val));
@@ -451,11 +536,9 @@
 
 	/* HS PGA volumes have 4 bits of resolution to ramp */
 	for (i = 0; i <= 16; i++) {
-		headset_complete = 1;
-		if (headset->ramp != TWL6040_RAMP_NONE)
-			headset_complete = twl6040_hs_ramp_step(codec,
-							headset->left_step,
-							headset->right_step);
+		headset_complete = twl6040_hs_ramp_step(codec,
+						headset->left_step,
+						headset->right_step);
 
 		/* ramp finished ? */
 		if (headset_complete)
@@ -496,11 +579,9 @@
 
 	/* HF PGA volumes have 5 bits of resolution to ramp */
 	for (i = 0; i <= 32; i++) {
-		handsfree_complete = 1;
-		if (handsfree->ramp != TWL6040_RAMP_NONE)
-			handsfree_complete = twl6040_hf_ramp_step(codec,
-							handsfree->left_step,
-							handsfree->right_step);
+		handsfree_complete = twl6040_hf_ramp_step(codec,
+						handsfree->left_step,
+						handsfree->right_step);
 
 		/* ramp finished ? */
 		if (handsfree_complete)
@@ -541,12 +622,16 @@
 		out = &priv->headset;
 		work = &priv->hs_delayed_work;
 		queue = priv->hs_workqueue;
+		out->left_step = priv->hs_left_step;
+		out->right_step = priv->hs_right_step;
 		out->step_delay = 5;	/* 5 ms between volume ramp steps */
 		break;
 	case 4:
 		out = &priv->handsfree;
 		work = &priv->hf_delayed_work;
 		queue = priv->hf_workqueue;
+		out->left_step = priv->hf_left_step;
+		out->right_step = priv->hf_right_step;
 		out->step_delay = 5;	/* 5 ms between volume ramp steps */
 		if (SND_SOC_DAPM_EVENT_ON(event))
 			priv->non_lp++;
@@ -579,8 +664,6 @@
 
 		if (!delayed_work_pending(work)) {
 			/* use volume ramp for power-down */
-			out->left_step = 1;
-			out->right_step = 1;
 			out->ramp = TWL6040_RAMP_DOWN;
 			INIT_COMPLETION(out->ramp_done);
 
@@ -596,88 +679,6 @@
 	return 0;
 }
 
-/* twl6040 codec manual power-up sequence */
-static void twl6040_power_up(struct snd_soc_codec *codec)
-{
-	u8 ncpctl, ldoctl, lppllctl, accctl;
-
-	ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
-	ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
-	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
-	accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
-
-	/* enable reference system */
-	ldoctl |= TWL6040_REFENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	msleep(10);
-	/* enable internal oscillator */
-	ldoctl |= TWL6040_OSCENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(10);
-	/* enable high-side ldo */
-	ldoctl |= TWL6040_HSLDOENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(244);
-	/* enable negative charge pump */
-	ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN;
-	twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
-	udelay(488);
-	/* enable low-side ldo */
-	ldoctl |= TWL6040_LSLDOENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(244);
-	/* enable low-power pll */
-	lppllctl |= TWL6040_LPLLENA;
-	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-	/* reset state machine */
-	accctl |= TWL6040_RESETSPLIT;
-	twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
-	mdelay(5);
-	accctl &= ~TWL6040_RESETSPLIT;
-	twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
-	/* disable internal oscillator */
-	ldoctl &= ~TWL6040_OSCENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-}
-
-/* twl6040 codec manual power-down sequence */
-static void twl6040_power_down(struct snd_soc_codec *codec)
-{
-	u8 ncpctl, ldoctl, lppllctl, accctl;
-
-	ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
-	ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
-	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
-	accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
-
-	/* enable internal oscillator */
-	ldoctl |= TWL6040_OSCENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(10);
-	/* disable low-power pll */
-	lppllctl &= ~TWL6040_LPLLENA;
-	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-	/* disable low-side ldo */
-	ldoctl &= ~TWL6040_LSLDOENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(244);
-	/* disable negative charge pump */
-	ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN);
-	twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
-	udelay(488);
-	/* disable high-side ldo */
-	ldoctl &= ~TWL6040_HSLDOENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	udelay(244);
-	/* disable internal oscillator */
-	ldoctl &= ~TWL6040_OSCENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	/* disable reference system */
-	ldoctl &= ~TWL6040_REFENA;
-	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-	msleep(10);
-}
-
 /* set headset dac and driver power mode */
 static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
 {
@@ -713,15 +714,26 @@
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
 
-	if (SND_SOC_DAPM_EVENT_ON(event))
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
 		priv->non_lp++;
-	else
+		if (!strcmp(w->name, "Earphone Driver")) {
+			/* Earphone doesn't support low power mode */
+			priv->hs_power_mode_locked = 1;
+			ret = headset_power_mode(codec, 1);
+		}
+	} else {
 		priv->non_lp--;
+		if (!strcmp(w->name, "Earphone Driver")) {
+			priv->hs_power_mode_locked = 0;
+			ret = headset_power_mode(codec, priv->hs_power_mode);
+		}
+	}
 
 	msleep(1);
 
-	return 0;
+	return ret;
 }
 
 static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
@@ -766,33 +778,19 @@
 }
 
 /* audio interrupt handler */
-static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+static irqreturn_t twl6040_audio_handler(int irq, void *data)
 {
 	struct snd_soc_codec *codec = data;
+	struct twl6040 *twl6040 = codec->control_data;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 	u8 intid;
 
-	twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
-
-	if (intid & TWL6040_THINT)
-		dev_alert(codec->dev, "die temp over-limit detection\n");
+	intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
 
 	if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT))
 		queue_delayed_work(priv->workqueue, &priv->delayed_work,
 							msecs_to_jiffies(200));
 
-	if (intid & TWL6040_HOOKINT)
-		dev_info(codec->dev, "hook detection\n");
-
-	if (intid & TWL6040_HFINT)
-		dev_alert(codec->dev, "hf drivers over current detection\n");
-
-	if (intid & TWL6040_VIBINT)
-		dev_alert(codec->dev, "vib drivers over current detection\n");
-
-	if (intid & TWL6040_READYINT)
-		complete(&priv->ready);
-
 	return IRQ_HANDLED;
 }
 
@@ -1040,6 +1038,73 @@
 static const struct snd_kcontrol_new ep_driver_switch_controls =
 	SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
 
+/* Headset power mode */
+static const char *twl6040_power_mode_texts[] = {
+	"Low-Power", "High-Perfomance",
+};
+
+static const struct soc_enum twl6040_power_mode_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts),
+			twl6040_power_mode_texts);
+
+static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
+
+	return 0;
+}
+
+static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+	int high_perf = ucontrol->value.enumerated.item[0];
+	int ret = 0;
+
+	if (!priv->hs_power_mode_locked)
+		ret = headset_power_mode(codec, high_perf);
+
+	if (!ret)
+		priv->hs_power_mode = high_perf;
+
+	return ret;
+}
+
+static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
+
+	return 0;
+}
+
+static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+	priv->pll_power_mode = ucontrol->value.enumerated.item[0];
+
+	return 0;
+}
+
+int twl6040_get_clk_id(struct snd_soc_codec *codec)
+{
+	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+	return priv->pll_power_mode;
+}
+EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
+
 static const struct snd_kcontrol_new twl6040_snd_controls[] = {
 	/* Capture gains */
 	SOC_DOUBLE_TLV("Capture Preamplifier Volume",
@@ -1058,6 +1123,13 @@
 		TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
 	SOC_SINGLE_TLV("Earphone Playback Volume",
 		TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
+
+	SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum,
+		twl6040_headset_power_get_enum,
+		twl6040_headset_power_put_enum),
+
+	SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
+		twl6040_pll_get_enum, twl6040_pll_put_enum),
 };
 
 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
@@ -1231,36 +1303,11 @@
 	return 0;
 }
 
-static int twl6040_power_up_completion(struct snd_soc_codec *codec,
-					int naudint)
-{
-	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	int time_left;
-	u8 intid;
-
-	time_left = wait_for_completion_timeout(&priv->ready,
-				msecs_to_jiffies(144));
-
-	if (!time_left) {
-		twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid,
-							TWL6040_REG_INTID);
-		if (!(intid & TWL6040_READYINT)) {
-			dev_err(codec->dev, "timeout waiting for READYINT\n");
-			return -ETIMEDOUT;
-		}
-	}
-
-	priv->codec_powered = 1;
-
-	return 0;
-}
-
 static int twl6040_set_bias_level(struct snd_soc_codec *codec,
 				enum snd_soc_bias_level level)
 {
+	struct twl6040 *twl6040 = codec->control_data;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	int audpwron = priv->audpwron;
-	int naudint = priv->naudint;
 	int ret;
 
 	switch (level) {
@@ -1272,58 +1319,23 @@
 		if (priv->codec_powered)
 			break;
 
-		if (gpio_is_valid(audpwron)) {
-			/* use AUDPWRON line */
-			gpio_set_value(audpwron, 1);
+		ret = twl6040_power(twl6040, 1);
+		if (ret)
+			return ret;
 
-			/* wait for power-up completion */
-			ret = twl6040_power_up_completion(codec, naudint);
-			if (ret)
-				return ret;
-
-			/* sync registers updated during power-up sequence */
-			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
-			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
-			twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
-		} else {
-			/* use manual power-up sequence */
-			twl6040_power_up(codec);
-			priv->codec_powered = 1;
-		}
+		priv->codec_powered = 1;
 
 		/* initialize vdd/vss registers with reg_cache */
 		twl6040_init_vdd_regs(codec);
 
 		/* Set external boost GPO */
 		twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
-
-		/* Set initial minimal gain values */
-		twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF);
-		twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E);
-		twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D);
-		twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D);
 		break;
 	case SND_SOC_BIAS_OFF:
 		if (!priv->codec_powered)
 			break;
 
-		if (gpio_is_valid(audpwron)) {
-			/* use AUDPWRON line */
-			gpio_set_value(audpwron, 0);
-
-			/* power-down sequence latency */
-			udelay(500);
-
-			/* sync registers updated during power-down sequence */
-			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
-			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
-			twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
-						0x00);
-		} else {
-			/* use manual power-down sequence */
-			twl6040_power_down(codec);
-		}
-
+		twl6040_power(twl6040, 0);
 		priv->codec_powered = 0;
 		break;
 	}
@@ -1333,27 +1345,6 @@
 	return 0;
 }
 
-/* set of rates for each pll: low-power and high-performance */
-
-static unsigned int lp_rates[] = {
-	88200,
-	96000,
-};
-
-static struct snd_pcm_hw_constraint_list lp_constraints = {
-	.count	= ARRAY_SIZE(lp_rates),
-	.list	= lp_rates,
-};
-
-static unsigned int hp_rates[] = {
-	96000,
-};
-
-static struct snd_pcm_hw_constraint_list hp_constraints = {
-	.count	= ARRAY_SIZE(hp_rates),
-	.list	= hp_rates,
-};
-
 static int twl6040_startup(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
@@ -1363,7 +1354,7 @@
 
 	snd_pcm_hw_constraint_list(substream->runtime, 0,
 				SNDRV_PCM_HW_PARAM_RATE,
-				priv->sysclk_constraints);
+				&sysclk_constraints[priv->pll_power_mode]);
 
 	return 0;
 }
@@ -1375,22 +1366,27 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->codec;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	u8 lppllctl;
 	int rate;
 
-	/* nothing to do for high-perf pll, it supports only 48 kHz */
-	if (priv->pll == TWL6040_HPPLL_ID)
-		return 0;
-
-	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
-
 	rate = params_rate(params);
 	switch (rate) {
 	case 11250:
 	case 22500:
 	case 44100:
 	case 88200:
-		lppllctl |= TWL6040_LPLLFIN;
+		/* These rates are not supported when HPPLL is in use */
+		if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) {
+			dev_err(codec->dev, "HPPLL does not support rate %d\n",
+				rate);
+			return -EINVAL;
+		}
+		/* Capture is not supported with 17.64MHz sysclk */
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			dev_err(codec->dev,
+				"capture mode is not supported at %dHz\n",
+				rate);
+			return -EINVAL;
+		}
 		priv->sysclk = 17640000;
 		break;
 	case 8000:
@@ -1398,7 +1394,6 @@
 	case 32000:
 	case 48000:
 	case 96000:
-		lppllctl &= ~TWL6040_LPLLFIN;
 		priv->sysclk = 19200000;
 		break;
 	default:
@@ -1406,8 +1401,6 @@
 		return -EINVAL;
 	}
 
-	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-
 	return 0;
 }
 
@@ -1416,7 +1409,9 @@
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->codec;
+	struct twl6040 *twl6040 = codec->control_data;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+	int ret;
 
 	if (!priv->sysclk) {
 		dev_err(codec->dev,
@@ -1424,24 +1419,19 @@
 		return -EINVAL;
 	}
 
-	/*
-	 * capture is not supported at 17.64 MHz,
-	 * it's reserved for headset low-power playback scenario
-	 */
-	if ((priv->sysclk == 17640000) &&
-			substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		dev_err(codec->dev,
-			"capture mode is not supported at %dHz\n",
-			priv->sysclk);
-		return -EINVAL;
-	}
-
 	if ((priv->sysclk == 17640000) && priv->non_lp) {
 			dev_err(codec->dev,
 				"some enabled paths aren't supported at %dHz\n",
 				priv->sysclk);
 			return -EPERM;
 	}
+
+	ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
+	if (ret) {
+		dev_err(codec->dev, "Can not set PLL (%d)\n", ret);
+		return -EPERM;
+	}
+
 	return 0;
 }
 
@@ -1450,99 +1440,12 @@
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	u8 hppllctl, lppllctl;
-
-	hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL);
-	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
 
 	switch (clk_id) {
 	case TWL6040_SYSCLK_SEL_LPPLL:
-		switch (freq) {
-		case 32768:
-			/* headset dac and driver must be in low-power mode */
-			headset_power_mode(codec, 0);
-
-			/* clk32k input requires low-power pll */
-			lppllctl |= TWL6040_LPLLENA;
-			twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-			mdelay(5);
-			lppllctl &= ~TWL6040_HPLLSEL;
-			twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-			hppllctl &= ~TWL6040_HPLLENA;
-			twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
-			break;
-		default:
-			dev_err(codec->dev, "unknown mclk freq %d\n", freq);
-			return -EINVAL;
-		}
-
-		/* lppll divider */
-		switch (priv->sysclk) {
-		case 17640000:
-			lppllctl |= TWL6040_LPLLFIN;
-			break;
-		case 19200000:
-			lppllctl &= ~TWL6040_LPLLFIN;
-			break;
-		default:
-			/* sysclk not yet configured */
-			lppllctl &= ~TWL6040_LPLLFIN;
-			priv->sysclk = 19200000;
-			break;
-		}
-
-		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-
-		priv->pll = TWL6040_LPPLL_ID;
-		priv->sysclk_constraints = &lp_constraints;
-		break;
 	case TWL6040_SYSCLK_SEL_HPPLL:
-		hppllctl &= ~TWL6040_MCLK_MSK;
-
-		switch (freq) {
-		case 12000000:
-			/* mclk input, pll enabled */
-			hppllctl |= TWL6040_MCLK_12000KHZ |
-				    TWL6040_HPLLSQRBP |
-				    TWL6040_HPLLENA;
-			break;
-		case 19200000:
-			/* mclk input, pll disabled */
-			hppllctl |= TWL6040_MCLK_19200KHZ |
-				    TWL6040_HPLLSQRENA |
-				    TWL6040_HPLLBP;
-			break;
-		case 26000000:
-			/* mclk input, pll enabled */
-			hppllctl |= TWL6040_MCLK_26000KHZ |
-				    TWL6040_HPLLSQRBP |
-				    TWL6040_HPLLENA;
-			break;
-		case 38400000:
-			/* clk slicer, pll disabled */
-			hppllctl |= TWL6040_MCLK_38400KHZ |
-				    TWL6040_HPLLSQRENA |
-				    TWL6040_HPLLBP;
-			break;
-		default:
-			dev_err(codec->dev, "unknown mclk freq %d\n", freq);
-			return -EINVAL;
-		}
-
-		/* headset dac and driver must be in high-performance mode */
-		headset_power_mode(codec, 1);
-
-		twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
-		udelay(500);
-		lppllctl |= TWL6040_HPLLSEL;
-		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-		lppllctl &= ~TWL6040_LPLLENA;
-		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-
-		/* high-performance pll can provide only 19.2 MHz */
-		priv->pll = TWL6040_HPPLL_ID;
-		priv->sysclk = 19200000;
-		priv->sysclk_constraints = &hp_constraints;
+		priv->pll = clk_id;
+		priv->clk_in = freq;
 		break;
 	default:
 		dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
@@ -1559,12 +1462,13 @@
 	.set_sysclk	= twl6040_set_dai_sysclk,
 };
 
-static struct snd_soc_dai_driver twl6040_dai = {
+static struct snd_soc_dai_driver twl6040_dai[] = {
+{
 	.name = "twl6040-hifi",
 	.playback = {
 		.stream_name = "Playback",
 		.channels_min = 1,
-		.channels_max = 4,
+		.channels_max = 2,
 		.rates = TWL6040_RATES,
 		.formats = TWL6040_FORMATS,
 	},
@@ -1576,6 +1480,51 @@
 		.formats = TWL6040_FORMATS,
 	},
 	.ops = &twl6040_dai_ops,
+},
+{
+	.name = "twl6040-ul",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = TWL6040_RATES,
+		.formats = TWL6040_FORMATS,
+	},
+	.ops = &twl6040_dai_ops,
+},
+{
+	.name = "twl6040-dl1",
+	.playback = {
+		.stream_name = "Headset Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = TWL6040_RATES,
+		.formats = TWL6040_FORMATS,
+	},
+	.ops = &twl6040_dai_ops,
+},
+{
+	.name = "twl6040-dl2",
+	.playback = {
+		.stream_name = "Handsfree Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = TWL6040_RATES,
+		.formats = TWL6040_FORMATS,
+	},
+	.ops = &twl6040_dai_ops,
+},
+{
+	.name = "twl6040-vib",
+	.playback = {
+		.stream_name = "Vibra Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.formats = TWL6040_FORMATS,
+	},
+	.ops = &twl6040_dai_ops,
+},
 };
 
 #ifdef CONFIG_PM
@@ -1600,11 +1549,11 @@
 
 static int twl6040_probe(struct snd_soc_codec *codec)
 {
-	struct twl4030_codec_data *twl_codec = codec->dev->platform_data;
 	struct twl6040_data *priv;
-	int audpwron, naudint;
+	struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
+	struct platform_device *pdev = container_of(codec->dev,
+						   struct platform_device, dev);
 	int ret = 0;
-	u8 icrev, intmr = TWL6040_ALLINT_MSK;
 
 	priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
 	if (priv == NULL)
@@ -1612,23 +1561,32 @@
 	snd_soc_codec_set_drvdata(codec, priv);
 
 	priv->codec = codec;
+	codec->control_data = dev_get_drvdata(codec->dev->parent);
 
-	twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV);
+	if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
+		priv->hs_left_step = pdata->hs_left_step;
+		priv->hs_right_step = pdata->hs_right_step;
+	} else {
+		priv->hs_left_step = 1;
+		priv->hs_right_step = 1;
+	}
 
-	if (twl_codec && (icrev > 0))
-		audpwron = twl_codec->audpwron_gpio;
-	else
-		audpwron = -EINVAL;
+	if (pdata && pdata->hf_left_step && pdata->hf_right_step) {
+		priv->hf_left_step = pdata->hf_left_step;
+		priv->hf_right_step = pdata->hf_right_step;
+	} else {
+		priv->hf_left_step = 1;
+		priv->hf_right_step = 1;
+	}
 
-	if (twl_codec)
-		naudint = twl_codec->naudint_irq;
-	else
-		naudint = 0;
+	priv->plug_irq = platform_get_irq(pdev, 0);
+	if (priv->plug_irq < 0) {
+		dev_err(codec->dev, "invalid irq\n");
+		ret = -EINVAL;
+		goto work_err;
+	}
 
-	priv->audpwron = audpwron;
-	priv->naudint = naudint;
 	priv->workqueue = create_singlethread_workqueue("twl6040-codec");
-
 	if (!priv->workqueue) {
 		ret = -ENOMEM;
 		goto work_err;
@@ -1638,56 +1596,33 @@
 
 	mutex_init(&priv->mutex);
 
-	init_completion(&priv->ready);
 	init_completion(&priv->headset.ramp_done);
 	init_completion(&priv->handsfree.ramp_done);
 
-	if (gpio_is_valid(audpwron)) {
-		ret = gpio_request(audpwron, "audpwron");
-		if (ret)
-			goto gpio1_err;
-
-		ret = gpio_direction_output(audpwron, 0);
-		if (ret)
-			goto gpio2_err;
-
-		priv->codec_powered = 0;
-
-		/* enable only codec ready interrupt */
-		intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK);
-
-		/* reset interrupt status to allow correct power up sequence */
-		twl6040_read_reg_volatile(codec, TWL6040_REG_INTID);
-	}
-	twl6040_write(codec, TWL6040_REG_INTMR, intmr);
-
-	if (naudint) {
-		/* audio interrupt */
-		ret = request_threaded_irq(naudint, NULL,
-				twl6040_naudint_handler,
-				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-				"twl6040_codec", codec);
-		if (ret)
-			goto gpio2_err;
-	}
-
-	/* init vio registers */
-	twl6040_init_vio_regs(codec);
-
 	priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf");
 	if (priv->hf_workqueue == NULL) {
 		ret = -ENOMEM;
-		goto irq_err;
+		goto hfwq_err;
 	}
 	priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs");
 	if (priv->hs_workqueue == NULL) {
 		ret = -ENOMEM;
-		goto wq_err;
+		goto hswq_err;
 	}
 
 	INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work);
 	INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work);
 
+	ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
+				   0, "twl6040_irq_plug", codec);
+	if (ret) {
+		dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
+		goto plugirq_err;
+	}
+
+	/* init vio registers */
+	twl6040_init_vio_regs(codec);
+
 	/* power on device */
 	ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	if (ret)
@@ -1700,16 +1635,12 @@
 	return 0;
 
 bias_err:
+	free_irq(priv->plug_irq, codec);
+plugirq_err:
 	destroy_workqueue(priv->hs_workqueue);
-wq_err:
+hswq_err:
 	destroy_workqueue(priv->hf_workqueue);
-irq_err:
-	if (naudint)
-		free_irq(naudint, codec);
-gpio2_err:
-	if (gpio_is_valid(audpwron))
-		gpio_free(audpwron);
-gpio1_err:
+hfwq_err:
 	destroy_workqueue(priv->workqueue);
 work_err:
 	kfree(priv);
@@ -1719,17 +1650,9 @@
 static int twl6040_remove(struct snd_soc_codec *codec)
 {
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	int audpwron = priv->audpwron;
-	int naudint = priv->naudint;
 
 	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	if (gpio_is_valid(audpwron))
-		gpio_free(audpwron);
-
-	if (naudint)
-		free_irq(naudint, codec);
-
+	free_irq(priv->plug_irq, codec);
 	destroy_workqueue(priv->workqueue);
 	destroy_workqueue(priv->hf_workqueue);
 	destroy_workqueue(priv->hs_workqueue);
@@ -1753,8 +1676,8 @@
 
 static int __devinit twl6040_codec_probe(struct platform_device *pdev)
 {
-	return snd_soc_register_codec(&pdev->dev,
-			&soc_codec_dev_twl6040, &twl6040_dai, 1);
+	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040,
+				      twl6040_dai, ARRAY_SIZE(twl6040_dai));
 }
 
 static int __devexit twl6040_codec_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h
index 23aeed0..d8de678 100644
--- a/sound/soc/codecs/twl6040.h
+++ b/sound/soc/codecs/twl6040.h
@@ -22,125 +22,8 @@
 #ifndef __TWL6040_H__
 #define __TWL6040_H__
 
-#define TWL6040_REG_ASICID		0x01
-#define TWL6040_REG_ASICREV		0x02
-#define TWL6040_REG_INTID		0x03
-#define TWL6040_REG_INTMR		0x04
-#define TWL6040_REG_NCPCTL		0x05
-#define TWL6040_REG_LDOCTL		0x06
-#define TWL6040_REG_HPPLLCTL		0x07
-#define TWL6040_REG_LPPLLCTL		0x08
-#define TWL6040_REG_LPPLLDIV		0x09
-#define TWL6040_REG_AMICBCTL		0x0A
-#define TWL6040_REG_DMICBCTL		0x0B
-#define TWL6040_REG_MICLCTL		0x0C
-#define TWL6040_REG_MICRCTL		0x0D
-#define TWL6040_REG_MICGAIN		0x0E
-#define TWL6040_REG_LINEGAIN		0x0F
-#define TWL6040_REG_HSLCTL		0x10
-#define TWL6040_REG_HSRCTL		0x11
-#define TWL6040_REG_HSGAIN		0x12
-#define TWL6040_REG_EARCTL		0x13
-#define TWL6040_REG_HFLCTL		0x14
-#define TWL6040_REG_HFLGAIN		0x15
-#define TWL6040_REG_HFRCTL		0x16
-#define TWL6040_REG_HFRGAIN		0x17
-#define TWL6040_REG_VIBCTLL		0x18
-#define TWL6040_REG_VIBDATL		0x19
-#define TWL6040_REG_VIBCTLR		0x1A
-#define TWL6040_REG_VIBDATR		0x1B
-#define TWL6040_REG_HKCTL1		0x1C
-#define TWL6040_REG_HKCTL2		0x1D
-#define TWL6040_REG_GPOCTL		0x1E
-#define TWL6040_REG_ALB			0x1F
-#define TWL6040_REG_DLB			0x20
-#define TWL6040_REG_TRIM1		0x28
-#define TWL6040_REG_TRIM2		0x29
-#define TWL6040_REG_TRIM3		0x2A
-#define TWL6040_REG_HSOTRIM		0x2B
-#define TWL6040_REG_HFOTRIM		0x2C
-#define TWL6040_REG_ACCCTL		0x2D
-#define TWL6040_REG_STATUS		0x2E
-
-#define TWL6040_CACHEREGNUM		(TWL6040_REG_STATUS + 1)
-
-#define TWL6040_VIOREGNUM		18
-#define TWL6040_VDDREGNUM		21
-
-/* INTID (0x03) fields */
-
-#define TWL6040_THINT			0x01
-#define TWL6040_PLUGINT			0x02
-#define TWL6040_UNPLUGINT		0x04
-#define TWL6040_HOOKINT			0x08
-#define TWL6040_HFINT			0x10
-#define TWL6040_VIBINT			0x20
-#define TWL6040_READYINT		0x40
-
-/* INTMR (0x04) fields */
-
-#define TWL6040_PLUGMSK			0x02
-#define TWL6040_READYMSK		0x40
-#define TWL6040_ALLINT_MSK		0x7B
-
-/* NCPCTL (0x05) fields */
-
-#define TWL6040_NCPENA			0x01
-#define TWL6040_NCPOPEN			0x40
-
-/* LDOCTL (0x06) fields */
-
-#define TWL6040_LSLDOENA		0x01
-#define TWL6040_HSLDOENA		0x04
-#define TWL6040_REFENA			0x40
-#define TWL6040_OSCENA			0x80
-
-/* HPPLLCTL (0x07) fields */
-
-#define TWL6040_HPLLENA			0x01
-#define TWL6040_HPLLRST			0x02
-#define TWL6040_HPLLBP			0x04
-#define TWL6040_HPLLSQRENA		0x08
-#define TWL6040_HPLLSQRBP		0x10
-#define TWL6040_MCLK_12000KHZ		(0 << 5)
-#define TWL6040_MCLK_19200KHZ		(1 << 5)
-#define TWL6040_MCLK_26000KHZ		(2 << 5)
-#define TWL6040_MCLK_38400KHZ		(3 << 5)
-#define TWL6040_MCLK_MSK		0x60
-
-/* LPPLLCTL (0x08) fields */
-
-#define TWL6040_LPLLENA			0x01
-#define TWL6040_LPLLRST			0x02
-#define TWL6040_LPLLSEL			0x04
-#define TWL6040_LPLLFIN			0x08
-#define TWL6040_HPLLSEL			0x10
-
-/* HSLCTL (0x10) fields */
-
-#define TWL6040_HSDACMODEL		0x02
-#define TWL6040_HSDRVMODEL		0x08
-
-/* HSRCTL (0x11) fields */
-
-#define TWL6040_HSDACMODER		0x02
-#define TWL6040_HSDRVMODER		0x08
-
-/* ACCCTL (0x2D) fields */
-
-#define TWL6040_RESETSPLIT		0x04
-
-#define TWL6040_SYSCLK_SEL_LPPLL	1
-#define TWL6040_SYSCLK_SEL_HPPLL	2
-
-#define TWL6040_HPPLL_ID		1
-#define TWL6040_LPPLL_ID		2
-
-/* STATUS (0x2E) fields */
-
-#define TWL6040_PLUGCOMP		0x02
-
 void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
 			    struct snd_soc_jack *jack, int report);
+int twl6040_get_clk_id(struct snd_soc_codec *codec);
 
 #endif /* End of __TWL6040_H__ */
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 8499c56..60d740e 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3409,6 +3409,9 @@
 	active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
 	active &= ~mask;
 
+	/* Acknowledge the interrupts */
+	snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
+
 	if (active & WM8962_FLL_LOCK_EINT) {
 		dev_dbg(codec->dev, "FLL locked\n");
 		complete(&wm8962->fll_lock);
@@ -3433,9 +3436,6 @@
 				      msecs_to_jiffies(250));
 	}
 
-	/* Acknowledge the interrupts */
-	snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
-
 	return IRQ_HANDLED;
 }
 
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c
index 9259f1f..1f11525 100644
--- a/sound/soc/davinci/davinci-vcif.c
+++ b/sound/soc/davinci/davinci-vcif.c
@@ -62,9 +62,9 @@
 	w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
+		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
 	else
-		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
+		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
 
 	writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
 }
@@ -80,9 +80,9 @@
 	/* Reset transmitter/receiver and sample rate/frame sync generators */
 	w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
+		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
 	else
-		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
+		MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
 
 	writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
 }
@@ -159,6 +159,7 @@
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		davinci_vcif_start(substream);
+		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index 3f72d17..9f6a758 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -36,7 +36,7 @@
 #include <plat/mcbsp.h>
 
 /* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-codec.h>
+#include <linux/mfd/twl4030-audio.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index 189e039..b80efb0 100644
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -21,6 +21,8 @@
 
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/mfd/twl6040.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -34,8 +36,6 @@
 #include "omap-pcm.h"
 #include "../codecs/twl6040.h"
 
-static int twl6040_power_mode;
-
 static int sdp4430_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
@@ -44,13 +44,13 @@
 	int clk_id, freq;
 	int ret;
 
-	if (twl6040_power_mode) {
-		clk_id = TWL6040_SYSCLK_SEL_HPPLL;
+	clk_id = twl6040_get_clk_id(rtd->codec);
+	if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
 		freq = 38400000;
-	} else {
-		clk_id = TWL6040_SYSCLK_SEL_LPPLL;
+	else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
 		freq = 32768;
-	}
+	else
+		return -EINVAL;
 
 	/* set the codec mclk */
 	ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
@@ -81,35 +81,6 @@
 	},
 };
 
-static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	ucontrol->value.integer.value[0] = twl6040_power_mode;
-	return 0;
-}
-
-static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	if (twl6040_power_mode == ucontrol->value.integer.value[0])
-		return 0;
-
-	twl6040_power_mode = ucontrol->value.integer.value[0];
-
-	return 1;
-}
-
-static const char *power_texts[] = {"Low-Power", "High-Performance"};
-
-static const struct soc_enum sdp4430_enum[] = {
-	SOC_ENUM_SINGLE_EXT(2, power_texts),
-};
-
-static const struct snd_kcontrol_new sdp4430_controls[] = {
-	SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0],
-		sdp4430_get_power_mode, sdp4430_set_power_mode),
-};
-
 /* SDP4430 machine DAPM */
 static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = {
 	SND_SOC_DAPM_MIC("Ext Mic", NULL),
@@ -152,12 +123,6 @@
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
-	/* Add SDP4430 specific controls */
-	ret = snd_soc_add_controls(codec, sdp4430_controls,
-				ARRAY_SIZE(sdp4430_controls));
-	if (ret)
-		return ret;
-
 	/* Add SDP4430 specific widgets */
 	ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets,
 				ARRAY_SIZE(sdp4430_twl6040_dapm_widgets));
@@ -237,9 +202,6 @@
 	if (ret)
 		goto err;
 
-	/* Codec starts in HP mode */
-	twl6040_power_mode = 1;
-
 	return 0;
 
 err:
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
index 0170994..9a2666f 100644
--- a/sound/soc/omap/zoom2.c
+++ b/sound/soc/omap/zoom2.c
@@ -32,7 +32,7 @@
 #include <plat/mcbsp.h>
 
 /* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-codec.h>
+#include <linux/mfd/twl4030-audio.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 1568eea..c086b78 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -21,6 +21,7 @@
 #include <plat/audio.h>
 
 #include "dma.h"
+#include "idma.h"
 #include "i2s.h"
 #include "i2s-regs.h"
 
@@ -60,6 +61,7 @@
 	/* DMA parameters */
 	struct s3c_dma_params dma_playback;
 	struct s3c_dma_params dma_capture;
+	struct s3c_dma_params idma_playback;
 	u32	quirks;
 	u32	suspend_i2smod;
 	u32	suspend_i2scon;
@@ -877,6 +879,10 @@
 	if (i2s->quirks & QUIRK_NEED_RSTCLR)
 		writel(CON_RSTCLR, i2s->addr + I2SCON);
 
+	if (i2s->quirks & QUIRK_SEC_DAI)
+		idma_reg_addr_init((void *)i2s->addr,
+					i2s->sec_dai->idma_playback.dma_addr);
+
 probe_exit:
 	/* Reset any constraint on RFS and BFS */
 	i2s->rfs = 0;
@@ -1077,6 +1083,7 @@
 		sec_dai->dma_playback.dma_size = 4;
 		sec_dai->base = regs_base;
 		sec_dai->quirks = quirks;
+		sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr;
 		sec_dai->pri_dai = pri_dai;
 		pri_dai->sec_dai = sec_dai;
 	}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e44267f..83ad8ca 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -577,6 +577,7 @@
 			case SND_SOC_BIAS_OFF:
 				codec->driver->suspend(codec, PMSG_SUSPEND);
 				codec->suspended = 1;
+				codec->cache_sync = 1;
 				break;
 			default:
 				dev_dbg(codec->dev, "CODEC is on over suspend\n");
@@ -1140,7 +1141,7 @@
 			}
 		}
 		cpu_dai->probed = 1;
-		/* mark cpu_dai as probed and add to card cpu_dai list */
+		/* mark cpu_dai as probed and add to card dai list */
 		list_add(&cpu_dai->card_list, &card->dai_dev_list);
 	}
 
@@ -1171,7 +1172,7 @@
 			}
 		}
 
-		/* mark cpu_dai as probed and add to card cpu_dai list */
+		/* mark codec_dai as probed and add to card dai list */
 		codec_dai->probed = 1;
 		list_add(&codec_dai->card_list, &card->dai_dev_list);
 	}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index fbfcda0..7e15914 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -124,6 +124,36 @@
 	return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
 }
 
+/* get snd_card from DAPM context */
+static inline struct snd_card *dapm_get_snd_card(
+	struct snd_soc_dapm_context *dapm)
+{
+	if (dapm->codec)
+		return dapm->codec->card->snd_card;
+	else if (dapm->platform)
+		return dapm->platform->card->snd_card;
+	else
+		BUG();
+
+	/* unreachable */
+	return NULL;
+}
+
+/* get soc_card from DAPM context */
+static inline struct snd_soc_card *dapm_get_soc_card(
+		struct snd_soc_dapm_context *dapm)
+{
+	if (dapm->codec)
+		return dapm->codec->card;
+	else if (dapm->platform)
+		return dapm->platform->card;
+	else
+		BUG();
+
+	/* unreachable */
+	return NULL;
+}
+
 static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
 {
 	if (w->codec)
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 73f9cba..1b839a0 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -69,7 +69,7 @@
 
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
 MODULE_DESCRIPTION("Sun DBRI");
diff --git a/tools/slub/slabinfo.c b/tools/slub/slabinfo.c
index 516551c..868cc93 100644
--- a/tools/slub/slabinfo.c
+++ b/tools/slub/slabinfo.c
@@ -2,8 +2,9 @@
  * Slabinfo: Tool to get reports about slabs
  *
  * (C) 2007 sgi, Christoph Lameter
+ * (C) 2011 Linux Foundation, Christoph Lameter
  *
- * Compile by:
+ * Compile with:
  *
  * gcc -o slabinfo slabinfo.c
  */
@@ -39,6 +40,8 @@
 	unsigned long cpuslab_flush, deactivate_full, deactivate_empty;
 	unsigned long deactivate_to_head, deactivate_to_tail;
 	unsigned long deactivate_remote_frees, order_fallback;
+	unsigned long cmpxchg_double_cpu_fail, cmpxchg_double_fail;
+	unsigned long alloc_node_mismatch, deactivate_bypass;
 	int numa[MAX_NODES];
 	int numa_partial[MAX_NODES];
 } slabinfo[MAX_SLABS];
@@ -99,7 +102,7 @@
 
 static void usage(void)
 {
-	printf("slabinfo 5/7/2007. (c) 2007 sgi.\n\n"
+	printf("slabinfo 4/15/2011. (c) 2007 sgi/(c) 2011 Linux Foundation.\n\n"
 		"slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n"
 		"-a|--aliases           Show aliases\n"
 		"-A|--activity          Most active slabs first\n"
@@ -293,7 +296,7 @@
 static void first_line(void)
 {
 	if (show_activity)
-		printf("Name                   Objects      Alloc       Free   %%Fast Fallb O\n");
+		printf("Name                   Objects      Alloc       Free   %%Fast Fallb O CmpX   UL\n");
 	else
 		printf("Name                   Objects Objsize    Space "
 			"Slabs/Part/Cpu  O/S O %%Fr %%Ef Flg\n");
@@ -379,14 +382,14 @@
 	printf("\n%s: Kernel object allocation\n", s->name);
 	printf("-----------------------------------------------------------------------\n");
 	if (read_slab_obj(s, "alloc_calls"))
-		printf(buffer);
+		printf("%s", buffer);
 	else
 		printf("No Data\n");
 
 	printf("\n%s: Kernel object freeing\n", s->name);
 	printf("------------------------------------------------------------------------\n");
 	if (read_slab_obj(s, "free_calls"))
-		printf(buffer);
+		printf("%s", buffer);
 	else
 		printf("No Data\n");
 
@@ -400,7 +403,7 @@
 	if (read_slab_obj(s, "ops")) {
 		printf("\n%s: kmem_cache operations\n", s->name);
 		printf("--------------------------------------------\n");
-		printf(buffer);
+		printf("%s", buffer);
 	} else
 		printf("\n%s has no kmem_cache operations\n", s->name);
 }
@@ -462,19 +465,32 @@
 	if (s->cpuslab_flush)
 		printf("Flushes %8lu\n", s->cpuslab_flush);
 
-	if (s->alloc_refill)
-		printf("Refill %8lu\n", s->alloc_refill);
-
 	total = s->deactivate_full + s->deactivate_empty +
-			s->deactivate_to_head + s->deactivate_to_tail;
+			s->deactivate_to_head + s->deactivate_to_tail + s->deactivate_bypass;
 
-	if (total)
-		printf("Deactivate Full=%lu(%lu%%) Empty=%lu(%lu%%) "
-			"ToHead=%lu(%lu%%) ToTail=%lu(%lu%%)\n",
-			s->deactivate_full, (s->deactivate_full * 100) / total,
-			s->deactivate_empty, (s->deactivate_empty * 100) / total,
-			s->deactivate_to_head, (s->deactivate_to_head * 100) / total,
+	if (total) {
+		printf("\nSlab Deactivation             Ocurrences  %%\n");
+		printf("-------------------------------------------------\n");
+		printf("Slab full                     %7lu  %3lu%%\n",
+			s->deactivate_full, (s->deactivate_full * 100) / total);
+		printf("Slab empty                    %7lu  %3lu%%\n",
+			s->deactivate_empty, (s->deactivate_empty * 100) / total);
+		printf("Moved to head of partial list %7lu  %3lu%%\n",
+			s->deactivate_to_head, (s->deactivate_to_head * 100) / total);
+		printf("Moved to tail of partial list %7lu  %3lu%%\n",
 			s->deactivate_to_tail, (s->deactivate_to_tail * 100) / total);
+		printf("Deactivation bypass           %7lu  %3lu%%\n",
+			s->deactivate_bypass, (s->deactivate_bypass * 100) / total);
+		printf("Refilled from foreign frees   %7lu  %3lu%%\n",
+			s->alloc_refill, (s->alloc_refill * 100) / total);
+		printf("Node mismatch                 %7lu  %3lu%%\n",
+			s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);
+	}
+
+	if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail)
+		printf("\nCmpxchg_double Looping\n------------------------\n");
+		printf("Locked Cmpxchg Double redos   %lu\nUnlocked Cmpxchg Double redos %lu\n",
+			s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);
 }
 
 static void report(struct slabinfo *s)
@@ -573,12 +589,13 @@
 		total_alloc = s->alloc_fastpath + s->alloc_slowpath;
 		total_free = s->free_fastpath + s->free_slowpath;
 
-		printf("%-21s %8ld %10ld %10ld %3ld %3ld %5ld %1d\n",
+		printf("%-21s %8ld %10ld %10ld %3ld %3ld %5ld %1d %4ld %4ld\n",
 			s->name, s->objects,
 			total_alloc, total_free,
 			total_alloc ? (s->alloc_fastpath * 100 / total_alloc) : 0,
 			total_free ? (s->free_fastpath * 100 / total_free) : 0,
-			s->order_fallback, s->order);
+			s->order_fallback, s->order, s->cmpxchg_double_fail,
+			s->cmpxchg_double_cpu_fail);
 	}
 	else
 		printf("%-21s %8ld %7d %8s %14s %4d %1d %3ld %3ld %s\n",
@@ -1190,6 +1207,10 @@
 			slab->deactivate_to_tail = get_obj("deactivate_to_tail");
 			slab->deactivate_remote_frees = get_obj("deactivate_remote_frees");
 			slab->order_fallback = get_obj("order_fallback");
+			slab->cmpxchg_double_cpu_fail = get_obj("cmpxchg_double_cpu_fail");
+			slab->cmpxchg_double_fail = get_obj("cmpxchg_double_fail");
+			slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
+			slab->deactivate_bypass = get_obj("deactivate_bypass");
 			chdir("..");
 			if (slab->name[0] == ':')
 				alias_targets++;
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index cef28e6..8d02ccb 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -27,7 +27,7 @@
 $default{"BUILD_TYPE"}		= "randconfig";
 $default{"MAKE_CMD"}		= "make";
 $default{"TIMEOUT"}		= 120;
-$default{"TMP_DIR"}		= "/tmp/ktest";
+$default{"TMP_DIR"}		= "/tmp/ktest/\${MACHINE}";
 $default{"SLEEP_TIME"}		= 60;	# sleep time between tests
 $default{"BUILD_NOCLEAN"}	= 0;
 $default{"REBOOT_ON_ERROR"}	= 0;
@@ -41,6 +41,7 @@
 $default{"BISECT_MANUAL"}	= 0;
 $default{"BISECT_SKIP"}		= 1;
 $default{"SUCCESS_LINE"}	= "login:";
+$default{"DETECT_TRIPLE_FAULT"} = 1;
 $default{"BOOTED_TIMEOUT"}	= 1;
 $default{"DIE_ON_FAILURE"}	= 1;
 $default{"SSH_EXEC"}		= "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND";
@@ -62,6 +63,10 @@
 my $test_type;
 my $build_type;
 my $build_options;
+my $pre_build;
+my $post_build;
+my $pre_build_die;
+my $post_build_die;
 my $reboot_type;
 my $reboot_script;
 my $power_cycle;
@@ -81,12 +86,17 @@
 my $post_install;
 my $noclean;
 my $minconfig;
+my $start_minconfig;
+my $start_minconfig_defined;
+my $output_minconfig;
+my $ignore_config;
 my $addconfig;
 my $in_bisect = 0;
 my $bisect_bad = "";
 my $reverse_bisect;
 my $bisect_manual;
 my $bisect_skip;
+my $config_bisect_good;
 my $in_patchcheck = 0;
 my $run_test;
 my $redirect;
@@ -98,9 +108,12 @@
 my $sleep_time;
 my $bisect_sleep_time;
 my $patchcheck_sleep_time;
+my $ignore_warnings;
 my $store_failures;
+my $test_name;
 my $timeout;
 my $booted_timeout;
+my $detect_triplefault;
 my $console;
 my $success_line;
 my $stop_after_success;
@@ -115,6 +128,7 @@
 my %entered_configs;
 my %config_help;
 my %variable;
+my %force_config;
 
 $config_help{"MACHINE"} = << "EOF"
  The machine hostname that you will test.
@@ -204,6 +218,26 @@
 EOF
     ;
 
+sub read_yn {
+    my ($prompt) = @_;
+
+    my $ans;
+
+    for (;;) {
+	print "$prompt [Y/n] ";
+	$ans = <STDIN>;
+	chomp $ans;
+	if ($ans =~ /^\s*$/) {
+	    $ans = "y";
+	}
+	last if ($ans =~ /^y$/i || $ans =~ /^n$/i);
+	print "Please answer either 'y' or 'n'.\n";
+    }
+    if ($ans !~ /^y$/i) {
+	return 0;
+    }
+    return 1;
+}
 
 sub get_ktest_config {
     my ($config) = @_;
@@ -335,6 +369,7 @@
     my $num_tests_set = 0;
     my $skip = 0;
     my $rest;
+    my $test_case = 0;
 
     while (<IN>) {
 
@@ -360,6 +395,7 @@
 		$rest = $1;
 		$skip = 1;
 	    } else {
+		$test_case = 1;
 		$skip = 0;
 	    }
 
@@ -464,6 +500,15 @@
     # make sure we have all mandatory configs
     get_ktest_configs;
 
+    # was a test specified?
+    if (!$test_case) {
+	print "No test case specified.\n";
+	print "What test case would you like to run?\n";
+	my $ans = <STDIN>;
+	chomp $ans;
+	$default{"TEST_TYPE"} = $ans;
+    }
+
     # set any defaults
 
     foreach my $default (keys %default) {
@@ -473,6 +518,69 @@
     }
 }
 
+sub __eval_option {
+    my ($option, $i) = @_;
+
+    # Add space to evaluate the character before $
+    $option = " $option";
+    my $retval = "";
+
+    while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) {
+	my $start = $1;
+	my $var = $2;
+	my $end = $3;
+
+	# Append beginning of line
+	$retval = "$retval$start";
+
+	# If the iteration option OPT[$i] exists, then use that.
+	# otherwise see if the default OPT (without [$i]) exists.
+
+	my $o = "$var\[$i\]";
+
+	if (defined($opt{$o})) {
+	    $o = $opt{$o};
+	    $retval = "$retval$o";
+	} elsif (defined($opt{$var})) {
+	    $o = $opt{$var};
+	    $retval = "$retval$o";
+	} else {
+	    $retval = "$retval\$\{$var\}";
+	}
+
+	$option = $end;
+    }
+
+    $retval = "$retval$option";
+
+    $retval =~ s/^ //;
+
+    return $retval;
+}
+
+sub eval_option {
+    my ($option, $i) = @_;
+
+    my $prev = "";
+
+    # Since an option can evaluate to another option,
+    # keep iterating until we do not evaluate any more
+    # options.
+    my $r = 0;
+    while ($prev ne $option) {
+	# Check for recursive evaluations.
+	# 100 deep should be more than enough.
+	if ($r++ > 100) {
+	    die "Over 100 evaluations accurred with $option\n" .
+		"Check for recursive variables\n";
+	}
+	$prev = $option;
+	$option = __eval_option($option, $i);
+    }
+
+    return $option;
+}
+
 sub _logit {
     if (defined($opt{"LOG_FILE"})) {
 	open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
@@ -617,9 +725,15 @@
 	    end_monitor;
 	}
 
+	my $name = "";
+
+	if (defined($test_name)) {
+	    $name = " ($test_name)";
+	}
+
 	doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
 	doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
-	doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n";
+	doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n";
 	doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
 	doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
 
@@ -836,17 +950,35 @@
     my $failure_start;
     my $monitor_start = time;
     my $done = 0;
+    my $version_found = 0;
 
     while (!$done) {
 
-	if ($booted) {
+	if ($bug && defined($stop_after_failure) &&
+	    $stop_after_failure >= 0) {
+	    my $time = $stop_after_failure - (time - $failure_start);
+	    $line = wait_for_input($monitor_fp, $time);
+	    if (!defined($line)) {
+		doprint "bug timed out after $booted_timeout seconds\n";
+		doprint "Test forced to stop after $stop_after_failure seconds after failure\n";
+		last;
+	    }
+	} elsif ($booted) {
 	    $line = wait_for_input($monitor_fp, $booted_timeout);
+	    if (!defined($line)) {
+		my $s = $booted_timeout == 1 ? "" : "s";
+		doprint "Successful boot found: break after $booted_timeout second$s\n";
+		last;
+	    }
 	} else {
 	    $line = wait_for_input($monitor_fp);
+	    if (!defined($line)) {
+		my $s = $timeout == 1 ? "" : "s";
+		doprint "Timed out after $timeout second$s\n";
+		last;
+	    }
 	}
 
-	last if (!defined($line));
-
 	doprint $line;
 	print DMESG $line;
 
@@ -896,6 +1028,22 @@
 	    $bug = 1;
 	}
 
+	# Detect triple faults by testing the banner
+	if ($full_line =~ /\bLinux version (\S+).*\n/) {
+	    if ($1 eq $version) {
+		$version_found = 1;
+	    } elsif ($version_found && $detect_triplefault) {
+		# We already booted into the kernel we are testing,
+		# but now we booted into another kernel?
+		# Consider this a triple fault.
+		doprint "Aleady booted in Linux kernel $version, but now\n";
+		doprint "we booted into Linux kernel $1.\n";
+		doprint "Assuming that this is a triple fault.\n";
+		doprint "To disable this: set DETECT_TRIPLE_FAULT to 0\n";
+		last;
+	    }
+	}
+
 	if ($line =~ /\n/) {
 	    $full_line = "";
 	}
@@ -923,6 +1071,16 @@
     return 1;
 }
 
+sub do_post_install {
+
+    return if (!defined($post_install));
+
+    my $cp_post_install = $post_install;
+    $cp_post_install =~ s/\$KERNEL_VERSION/$version/g;
+    run_command "$cp_post_install" or
+	dodie "Failed to run post install";
+}
+
 sub install {
 
     run_scp "$outputdir/$build_target", "$target_image" or
@@ -942,6 +1100,7 @@
     close(IN);
 
     if (!$install_mods) {
+	do_post_install;
 	doprint "No modules needed\n";
 	return;
     }
@@ -964,17 +1123,29 @@
 
     unlink "$tmpdir/$modtar";
 
-    run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
+    run_ssh "'(cd / && tar xjf /tmp/$modtar)'" or
 	dodie "failed to tar modules";
 
     run_ssh "rm -f /tmp/$modtar";
 
-    return if (!defined($post_install));
+    do_post_install;
+}
 
-    my $cp_post_install = $post_install;
-    $cp_post_install =~ s/\$KERNEL_VERSION/$version/g;
-    run_command "$cp_post_install" or
-	dodie "Failed to run post install";
+sub get_version {
+    # get the release name
+    doprint "$make kernelrelease ... ";
+    $version = `$make kernelrelease | tail -1`;
+    chomp($version);
+    doprint "$version\n";
+}
+
+sub start_monitor_and_boot {
+    get_grub_index;
+    get_version;
+    install;
+
+    start_monitor;
+    return monitor;
 }
 
 sub check_buildlog {
@@ -1009,24 +1180,84 @@
     return 1;
 }
 
-sub make_oldconfig {
-    my ($defconfig) = @_;
+sub apply_min_config {
+    my $outconfig = "$output_config.new";
 
-    if (!run_command "$defconfig $make oldnoconfig") {
+    # Read the config file and remove anything that
+    # is in the force_config hash (from minconfig and others)
+    # then add the force config back.
+
+    doprint "Applying minimum configurations into $output_config.new\n";
+
+    open (OUT, ">$outconfig") or
+	dodie "Can't create $outconfig";
+
+    if (-f $output_config) {
+	open (IN, $output_config) or
+	    dodie "Failed to open $output_config";
+	while (<IN>) {
+	    if (/^(# )?(CONFIG_[^\s=]*)/) {
+		next if (defined($force_config{$2}));
+	    }
+	    print OUT;
+	}
+	close IN;
+    }
+    foreach my $config (keys %force_config) {
+	print OUT "$force_config{$config}\n";
+    }
+    close OUT;
+
+    run_command "mv $outconfig $output_config";
+}
+
+sub make_oldconfig {
+
+    my @force_list = keys %force_config;
+
+    if ($#force_list >= 0) {
+	apply_min_config;
+    }
+
+    if (!run_command "$make oldnoconfig") {
 	# Perhaps oldnoconfig doesn't exist in this version of the kernel
 	# try a yes '' | oldconfig
 	doprint "oldnoconfig failed, trying yes '' | make oldconfig\n";
-	run_command "yes '' | $defconfig $make oldconfig" or
+	run_command "yes '' | $make oldconfig" or
 	    dodie "failed make config oldconfig";
     }
 }
 
+# read a config file and use this to force new configs.
+sub load_force_config {
+    my ($config) = @_;
+
+    open(IN, $config) or
+	dodie "failed to read $config";
+    while (<IN>) {
+	chomp;
+	if (/^(CONFIG[^\s=]*)(\s*=.*)/) {
+	    $force_config{$1} = $_;
+	} elsif (/^# (CONFIG_\S*) is not set/) {
+	    $force_config{$1} = $_;
+	}
+    }
+    close IN;
+}
+
 sub build {
     my ($type) = @_;
-    my $defconfig = "";
 
     unlink $buildlog;
 
+    if (defined($pre_build)) {
+	my $ret = run_command $pre_build;
+	if (!$ret && defined($pre_build_die) &&
+	    $pre_build_die) {
+	    dodie "failed to pre_build\n";
+	}
+    }
+
     if ($type =~ /^useconfig:(.*)/) {
 	run_command "cp $1 $output_config" or
 	    dodie "could not copy $1 to .config";
@@ -1063,24 +1294,33 @@
     close(OUT);
 
     if (defined($minconfig)) {
-	$defconfig = "KCONFIG_ALLCONFIG=$minconfig";
+	load_force_config($minconfig);
     }
 
-    if ($type eq "oldnoconfig") {
-	make_oldconfig $defconfig;
-    } else {
-	run_command "$defconfig $make $type" or
+    if ($type ne "oldnoconfig") {
+	run_command "$make $type" or
 	    dodie "failed make config";
     }
+    # Run old config regardless, to enforce min configurations
+    make_oldconfig;
 
     $redirect = "$buildlog";
-    if (!run_command "$make $build_options") {
-	undef $redirect;
+    my $build_ret = run_command "$make $build_options";
+    undef $redirect;
+
+    if (defined($post_build)) {
+	my $ret = run_command $post_build;
+	if (!$ret && defined($post_build_die) &&
+	    $post_build_die) {
+	    dodie "failed to post_build\n";
+	}
+    }
+
+    if (!$build_ret) {
 	# bisect may need this to pass
 	return 0 if ($in_bisect);
 	fail "failed build" and return 0;
     }
-    undef $redirect;
 
     return 1;
 }
@@ -1102,9 +1342,15 @@
 
     $successes++;
 
+    my $name = "";
+
+    if (defined($test_name)) {
+	$name = " ($test_name)";
+    }
+
     doprint "\n\n*******************************************\n";
     doprint     "*******************************************\n";
-    doprint     "KTEST RESULT: TEST $i SUCCESS!!!!         **\n";
+    doprint     "KTEST RESULT: TEST $i$name SUCCESS!!!!         **\n";
     doprint     "*******************************************\n";
     doprint     "*******************************************\n";
 
@@ -1117,14 +1363,6 @@
     }
 }
 
-sub get_version {
-    # get the release name
-    doprint "$make kernelrelease ... ";
-    $version = `$make kernelrelease | tail -1`;
-    chomp($version);
-    doprint "$version\n";
-}
-
 sub answer_bisect {
     for (;;) {
 	doprint "Pass or fail? [p/f]";
@@ -1289,12 +1527,7 @@
 	dodie "Failed on build" if $failed;
 
 	# Now boot the box
-	get_grub_index;
-	get_version;
-	install;
-
-	start_monitor;
-	monitor or $failed = 1;
+	start_monitor_and_boot or $failed = 1;
 
 	if ($type ne "boot") {
 	    if ($failed && $bisect_skip) {
@@ -1473,21 +1706,27 @@
 
 my %dependency;
 
-sub process_config_ignore {
-    my ($config) = @_;
+sub assign_configs {
+    my ($hash, $config) = @_;
 
     open (IN, $config)
 	or dodie "Failed to read $config";
 
     while (<IN>) {
 	if (/^((CONFIG\S*)=.*)/) {
-	    $config_ignore{$2} = $1;
+	    ${$hash}{$2} = $1;
 	}
     }
 
     close(IN);
 }
 
+sub process_config_ignore {
+    my ($config) = @_;
+
+    assign_configs \%config_ignore, $config;
+}
+
 sub read_current_config {
     my ($config_ref) = @_;
 
@@ -1546,7 +1785,7 @@
     close(OUT);
 
 #    exit;
-    make_oldconfig "";
+    make_oldconfig;
 }
 
 sub compare_configs {
@@ -1718,6 +1957,10 @@
 
     my $tmpconfig = "$tmpdir/use_config";
 
+    if (defined($config_bisect_good)) {
+	process_config_ignore $config_bisect_good;
+    }
+
     # Make the file with the bad config and the min config
     if (defined($minconfig)) {
 	# read the min config for things to ignore
@@ -1727,15 +1970,8 @@
 	unlink $tmpconfig;
     }
 
-    # Add other configs
-    if (defined($addconfig)) {
-	run_command "cat $addconfig >> $tmpconfig" or
-	    dodie "failed to append $addconfig";
-    }
-
-    my $defconfig = "";
     if (-f $tmpconfig) {
-	$defconfig = "KCONFIG_ALLCONFIG=$tmpconfig";
+	load_force_config($tmpconfig);
 	process_config_ignore $tmpconfig;
     }
 
@@ -1755,8 +1991,8 @@
     }
     close(IN);
 
-    # Now run oldconfig with the minconfig (and addconfigs)
-    make_oldconfig $defconfig;
+    # Now run oldconfig with the minconfig
+    make_oldconfig;
 
     # check to see what we lost (or gained)
     open (IN, $output_config)
@@ -1882,6 +2118,13 @@
     @list = reverse @list;
 
     my $save_clean = $noclean;
+    my %ignored_warnings;
+
+    if (defined($ignore_warnings)) {
+	foreach my $sha1 (split /\s+/, $ignore_warnings) {
+	    $ignored_warnings{$sha1} = 1;
+	}
+    }
 
     $in_patchcheck = 1;
     foreach my $item (@list) {
@@ -1908,18 +2151,16 @@
 	    build "oldconfig" or return 0;
 	}
 
-	check_buildlog $sha1 or return 0;
+
+	if (!defined($ignored_warnings{$sha1})) {
+	    check_buildlog $sha1 or return 0;
+	}
 
 	next if ($type eq "build");
 
-	get_grub_index;
-	get_version;
-	install;
-
 	my $failed = 0;
 
-	start_monitor;
-	monitor or $failed = 1;
+	start_monitor_and_boot or $failed = 1;
 
 	if (!$failed && $type ne "boot"){
 	    do_run_test or $failed = 1;
@@ -1936,24 +2177,505 @@
     return 1;
 }
 
+my %depends;
+my $iflevel = 0;
+my @ifdeps;
+
+# prevent recursion
+my %read_kconfigs;
+
+# taken from streamline_config.pl
+sub read_kconfig {
+    my ($kconfig) = @_;
+
+    my $state = "NONE";
+    my $config;
+    my @kconfigs;
+
+    my $cont = 0;
+    my $line;
+
+
+    if (! -f $kconfig) {
+	doprint "file $kconfig does not exist, skipping\n";
+	return;
+    }
+
+    open(KIN, "$kconfig")
+	or die "Can't open $kconfig";
+    while (<KIN>) {
+	chomp;
+
+	# Make sure that lines ending with \ continue
+	if ($cont) {
+	    $_ = $line . " " . $_;
+	}
+
+	if (s/\\$//) {
+	    $cont = 1;
+	    $line = $_;
+	    next;
+	}
+
+	$cont = 0;
+
+	# collect any Kconfig sources
+	if (/^source\s*"(.*)"/) {
+	    $kconfigs[$#kconfigs+1] = $1;
+	}
+
+	# configs found
+	if (/^\s*(menu)?config\s+(\S+)\s*$/) {
+	    $state = "NEW";
+	    $config = $2;
+
+	    for (my $i = 0; $i < $iflevel; $i++) {
+		if ($i) {
+		    $depends{$config} .= " " . $ifdeps[$i];
+		} else {
+		    $depends{$config} = $ifdeps[$i];
+		}
+		$state = "DEP";
+	    }
+
+	# collect the depends for the config
+	} elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) {
+
+	    if (defined($depends{$1})) {
+		$depends{$config} .= " " . $1;
+	    } else {
+		$depends{$config} = $1;
+	    }
+
+	# Get the configs that select this config
+	} elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) {
+	    if (defined($depends{$1})) {
+		$depends{$1} .= " " . $config;
+	    } else {
+		$depends{$1} = $config;
+	    }
+
+	# Check for if statements
+	} elsif (/^if\s+(.*\S)\s*$/) {
+	    my $deps = $1;
+	    # remove beginning and ending non text
+	    $deps =~ s/^[^a-zA-Z0-9_]*//;
+	    $deps =~ s/[^a-zA-Z0-9_]*$//;
+
+	    my @deps = split /[^a-zA-Z0-9_]+/, $deps;
+
+	    $ifdeps[$iflevel++] = join ':', @deps;
+
+	} elsif (/^endif/) {
+
+	    $iflevel-- if ($iflevel);
+
+	# stop on "help"
+	} elsif (/^\s*help\s*$/) {
+	    $state = "NONE";
+	}
+    }
+    close(KIN);
+
+    # read in any configs that were found.
+    foreach $kconfig (@kconfigs) {
+	if (!defined($read_kconfigs{$kconfig})) {
+	    $read_kconfigs{$kconfig} = 1;
+	    read_kconfig("$builddir/$kconfig");
+	}
+    }
+}
+
+sub read_depends {
+    # find out which arch this is by the kconfig file
+    open (IN, $output_config)
+	or dodie "Failed to read $output_config";
+    my $arch;
+    while (<IN>) {
+	if (m,Linux/(\S+)\s+\S+\s+Kernel Configuration,) {
+	    $arch = $1;
+	    last;
+	}
+    }
+    close IN;
+
+    if (!defined($arch)) {
+	doprint "Could not find arch from config file\n";
+	doprint "no dependencies used\n";
+	return;
+    }
+
+    # arch is really the subarch, we need to know
+    # what directory to look at.
+    if ($arch eq "i386" || $arch eq "x86_64") {
+	$arch = "x86";
+    } elsif ($arch =~ /^tile/) {
+	$arch = "tile";
+    }
+
+    my $kconfig = "$builddir/arch/$arch/Kconfig";
+
+    if (! -f $kconfig && $arch =~ /\d$/) {
+	my $orig = $arch;
+ 	# some subarchs have numbers, truncate them
+	$arch =~ s/\d*$//;
+	$kconfig = "$builddir/arch/$arch/Kconfig";
+	if (! -f $kconfig) {
+	    doprint "No idea what arch dir $orig is for\n";
+	    doprint "no dependencies used\n";
+	    return;
+	}
+    }
+
+    read_kconfig($kconfig);
+}
+
+sub read_config_list {
+    my ($config) = @_;
+
+    open (IN, $config)
+	or dodie "Failed to read $config";
+
+    while (<IN>) {
+	if (/^((CONFIG\S*)=.*)/) {
+	    if (!defined($config_ignore{$2})) {
+		$config_list{$2} = $1;
+	    }
+	}
+    }
+
+    close(IN);
+}
+
+sub read_output_config {
+    my ($config) = @_;
+
+    assign_configs \%config_ignore, $config;
+}
+
+sub make_new_config {
+    my @configs = @_;
+
+    open (OUT, ">$output_config")
+	or dodie "Failed to write $output_config";
+
+    foreach my $config (@configs) {
+	print OUT "$config\n";
+    }
+    close OUT;
+}
+
+sub get_depends {
+    my ($dep) = @_;
+
+    my $kconfig = $dep;
+    $kconfig =~ s/CONFIG_//;
+
+    $dep = $depends{"$kconfig"};
+
+    # the dep string we have saves the dependencies as they
+    # were found, including expressions like ! && ||. We
+    # want to split this out into just an array of configs.
+
+    my $valid = "A-Za-z_0-9";
+
+    my @configs;
+
+    while ($dep =~ /[$valid]/) {
+
+	if ($dep =~ /^[^$valid]*([$valid]+)/) {
+	    my $conf = "CONFIG_" . $1;
+
+	    $configs[$#configs + 1] = $conf;
+
+	    $dep =~ s/^[^$valid]*[$valid]+//;
+	} else {
+	    die "this should never happen";
+	}
+    }
+
+    return @configs;
+}
+
+my %min_configs;
+my %keep_configs;
+my %save_configs;
+my %processed_configs;
+my %nochange_config;
+
+sub test_this_config {
+    my ($config) = @_;
+
+    my $found;
+
+    # if we already processed this config, skip it
+    if (defined($processed_configs{$config})) {
+	return undef;
+    }
+    $processed_configs{$config} = 1;
+
+    # if this config failed during this round, skip it
+    if (defined($nochange_config{$config})) {
+	return undef;
+    }
+
+    my $kconfig = $config;
+    $kconfig =~ s/CONFIG_//;
+
+    # Test dependencies first
+    if (defined($depends{"$kconfig"})) {
+	my @parents = get_depends $config;
+	foreach my $parent (@parents) {
+	    # if the parent is in the min config, check it first
+	    next if (!defined($min_configs{$parent}));
+	    $found = test_this_config($parent);
+	    if (defined($found)) {
+		return $found;
+	    }
+	}
+    }
+
+    # Remove this config from the list of configs
+    # do a make oldnoconfig and then read the resulting
+    # .config to make sure it is missing the config that
+    # we had before
+    my %configs = %min_configs;
+    delete $configs{$config};
+    make_new_config ((values %configs), (values %keep_configs));
+    make_oldconfig;
+    undef %configs;
+    assign_configs \%configs, $output_config;
+
+    return $config if (!defined($configs{$config}));
+
+    doprint "disabling config $config did not change .config\n";
+
+    $nochange_config{$config} = 1;
+
+    return undef;
+}
+
+sub make_min_config {
+    my ($i) = @_;
+
+    if (!defined($output_minconfig)) {
+	fail "OUTPUT_MIN_CONFIG not defined" and return;
+    }
+
+    # If output_minconfig exists, and the start_minconfig
+    # came from min_config, than ask if we should use
+    # that instead.
+    if (-f $output_minconfig && !$start_minconfig_defined) {
+	print "$output_minconfig exists\n";
+	if (read_yn " Use it as minconfig?") {
+	    $start_minconfig = $output_minconfig;
+	}
+    }
+
+    if (!defined($start_minconfig)) {
+	fail "START_MIN_CONFIG or MIN_CONFIG not defined" and return;
+    }
+
+    my $temp_config = "$tmpdir/temp_config";
+
+    # First things first. We build an allnoconfig to find
+    # out what the defaults are that we can't touch.
+    # Some are selections, but we really can't handle selections.
+
+    my $save_minconfig = $minconfig;
+    undef $minconfig;
+
+    run_command "$make allnoconfig" or return 0;
+
+    read_depends;
+
+    process_config_ignore $output_config;
+
+    undef %save_configs;
+    undef %min_configs;
+
+    if (defined($ignore_config)) {
+	# make sure the file exists
+	`touch $ignore_config`;
+	assign_configs \%save_configs, $ignore_config;
+    }
+
+    %keep_configs = %save_configs;
+
+    doprint "Load initial configs from $start_minconfig\n";
+
+    # Look at the current min configs, and save off all the
+    # ones that were set via the allnoconfig
+    assign_configs \%min_configs, $start_minconfig;
+
+    my @config_keys = keys %min_configs;
+
+    # Remove anything that was set by the make allnoconfig
+    # we shouldn't need them as they get set for us anyway.
+    foreach my $config (@config_keys) {
+	# Remove anything in the ignore_config
+	if (defined($keep_configs{$config})) {
+	    my $file = $ignore_config;
+	    $file =~ s,.*/(.*?)$,$1,;
+	    doprint "$config set by $file ... ignored\n";
+	    delete $min_configs{$config};
+	    next;
+	}
+	# But make sure the settings are the same. If a min config
+	# sets a selection, we do not want to get rid of it if
+	# it is not the same as what we have. Just move it into
+	# the keep configs.
+	if (defined($config_ignore{$config})) {
+	    if ($config_ignore{$config} ne $min_configs{$config}) {
+		doprint "$config is in allnoconfig as '$config_ignore{$config}'";
+		doprint " but it is '$min_configs{$config}' in minconfig .. keeping\n";
+		$keep_configs{$config} = $min_configs{$config};
+	    } else {
+		doprint "$config set by allnoconfig ... ignored\n";
+	    }
+	    delete $min_configs{$config};
+	}
+    }
+
+    my $done = 0;
+    my $take_two = 0;
+
+    while (!$done) {
+
+	my $config;
+	my $found;
+
+	# Now disable each config one by one and do a make oldconfig
+	# till we find a config that changes our list.
+
+	# Put configs that did not modify the config at the end.
+	my @test_configs = keys %min_configs;
+	my $reset = 1;
+	for (my $i = 0; $i < $#test_configs; $i++) {
+	    if (!defined($nochange_config{$test_configs[0]})) {
+		$reset = 0;
+		last;
+	    }
+	    # This config didn't change the .config last time.
+	    # Place it at the end
+	    my $config = shift @test_configs;
+	    push @test_configs, $config;
+	}
+
+	# if every test config has failed to modify the .config file
+	# in the past, then reset and start over.
+	if ($reset) {
+	    undef %nochange_config;
+	}
+
+	undef %processed_configs;
+
+	foreach my $config (@test_configs) {
+
+	    $found = test_this_config $config;
+
+	    last if (defined($found));
+
+	    # oh well, try another config
+	}
+
+	if (!defined($found)) {
+	    # we could have failed due to the nochange_config hash
+	    # reset and try again
+	    if (!$take_two) {
+		undef %nochange_config;
+		$take_two = 1;
+		next;
+	    }
+	    doprint "No more configs found that we can disable\n";
+	    $done = 1;
+	    last;
+	}
+	$take_two = 0;
+
+	$config = $found;
+
+	doprint "Test with $config disabled\n";
+
+	# set in_bisect to keep build and monitor from dieing
+	$in_bisect = 1;
+
+	my $failed = 0;
+	build "oldconfig";
+	start_monitor_and_boot or $failed = 1;
+	end_monitor;
+
+	$in_bisect = 0;
+
+	if ($failed) {
+	    doprint "$min_configs{$config} is needed to boot the box... keeping\n";
+	    # this config is needed, add it to the ignore list.
+	    $keep_configs{$config} = $min_configs{$config};
+	    $save_configs{$config} = $min_configs{$config};
+	    delete $min_configs{$config};
+
+	    # update new ignore configs
+	    if (defined($ignore_config)) {
+		open (OUT, ">$temp_config")
+		    or die "Can't write to $temp_config";
+		foreach my $config (keys %save_configs) {
+		    print OUT "$save_configs{$config}\n";
+		}
+		close OUT;
+		run_command "mv $temp_config $ignore_config" or
+		    dodie "failed to copy update to $ignore_config";
+	    }
+
+	} else {
+	    # We booted without this config, remove it from the minconfigs.
+	    doprint "$config is not needed, disabling\n";
+
+	    delete $min_configs{$config};
+
+	    # Also disable anything that is not enabled in this config
+	    my %configs;
+	    assign_configs \%configs, $output_config;
+	    my @config_keys = keys %min_configs;
+	    foreach my $config (@config_keys) {
+		if (!defined($configs{$config})) {
+		    doprint "$config is not set, disabling\n";
+		    delete $min_configs{$config};
+		}
+	    }
+
+	    # Save off all the current mandidory configs
+	    open (OUT, ">$temp_config")
+		or die "Can't write to $temp_config";
+	    foreach my $config (keys %keep_configs) {
+		print OUT "$keep_configs{$config}\n";
+	    }
+	    foreach my $config (keys %min_configs) {
+		print OUT "$min_configs{$config}\n";
+	    }
+	    close OUT;
+
+	    run_command "mv $temp_config $output_minconfig" or
+		dodie "failed to copy update to $output_minconfig";
+	}
+
+	doprint "Reboot and wait $sleep_time seconds\n";
+	reboot;
+	start_monitor;
+	wait_for_monitor $sleep_time;
+	end_monitor;
+    }
+
+    success $i;
+    return 1;
+}
+
 $#ARGV < 1 or die "ktest.pl version: $VERSION\n   usage: ktest.pl config-file\n";
 
 if ($#ARGV == 0) {
     $ktest_config = $ARGV[0];
     if (! -f $ktest_config) {
 	print "$ktest_config does not exist.\n";
-	my $ans;
-        for (;;) {
-	    print "Create it? [Y/n] ";
-	    $ans = <STDIN>;
-	    chomp $ans;
-	    if ($ans =~ /^\s*$/) {
-		$ans = "y";
-	    }
-	    last if ($ans =~ /^y$/i || $ans =~ /^n$/i);
-	    print "Please answer either 'y' or 'n'.\n";
-	}
-	if ($ans !~ /^y$/i) {
+	if (!read_yn "Create it?") {
 	    exit 0;
 	}
     }
@@ -1977,6 +2699,10 @@
 }
 read_config $ktest_config;
 
+if (defined($opt{"LOG_FILE"})) {
+    $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1);
+}
+
 # Append any configs entered in manually to the config file.
 my @new_configs = keys %entered_configs;
 if ($#new_configs >= 0) {
@@ -2045,70 +2771,13 @@
     return undef;
 }
 
-sub eval_option {
-    my ($option, $i) = @_;
-
-    # Add space to evaluate the character before $
-    $option = " $option";
-    my $retval = "";
-
-    while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) {
-	my $start = $1;
-	my $var = $2;
-	my $end = $3;
-
-	# Append beginning of line
-	$retval = "$retval$start";
-
-	# If the iteration option OPT[$i] exists, then use that.
-	# otherwise see if the default OPT (without [$i]) exists.
-
-	my $o = "$var\[$i\]";
-
-	if (defined($opt{$o})) {
-	    $o = $opt{$o};
-	    $retval = "$retval$o";
-	} elsif (defined($opt{$var})) {
-	    $o = $opt{$var};
-	    $retval = "$retval$o";
-	} else {
-	    $retval = "$retval\$\{$var\}";
-	}
-
-	$option = $end;
-    }
-
-    $retval = "$retval$option";
-
-    $retval =~ s/^ //;
-
-    return $retval;
-}
-
 sub set_test_option {
     my ($name, $i) = @_;
 
     my $option = __set_test_option($name, $i);
     return $option if (!defined($option));
 
-    my $prev = "";
-
-    # Since an option can evaluate to another option,
-    # keep iterating until we do not evaluate any more
-    # options.
-    my $r = 0;
-    while ($prev ne $option) {
-	# Check for recursive evaluations.
-	# 100 deep should be more than enough.
-	if ($r++ > 100) {
-	    die "Over 100 evaluations accurred with $name\n" .
-		"Check for recursive variables\n";
-	}
-	$prev = $option;
-	$option = eval_option($option, $i);
-    }
-
-    return $option;
+    return eval_option($option, $i);
 }
 
 # First we need to do is the builds
@@ -2126,10 +2795,17 @@
     $test_type = set_test_option("TEST_TYPE", $i);
     $build_type = set_test_option("BUILD_TYPE", $i);
     $build_options = set_test_option("BUILD_OPTIONS", $i);
+    $pre_build = set_test_option("PRE_BUILD", $i);
+    $post_build = set_test_option("POST_BUILD", $i);
+    $pre_build_die = set_test_option("PRE_BUILD_DIE", $i);
+    $post_build_die = set_test_option("POST_BUILD_DIE", $i);
     $power_cycle = set_test_option("POWER_CYCLE", $i);
     $reboot = set_test_option("REBOOT", $i);
     $noclean = set_test_option("BUILD_NOCLEAN", $i);
     $minconfig = set_test_option("MIN_CONFIG", $i);
+    $output_minconfig = set_test_option("OUTPUT_MIN_CONFIG", $i);
+    $start_minconfig = set_test_option("START_MIN_CONFIG", $i);
+    $ignore_config = set_test_option("IGNORE_CONFIG", $i);
     $run_test = set_test_option("TEST", $i);
     $addconfig = set_test_option("ADD_CONFIG", $i);
     $reboot_type = set_test_option("REBOOT_TYPE", $i);
@@ -2145,12 +2821,16 @@
     $sleep_time = set_test_option("SLEEP_TIME", $i);
     $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i);
     $patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i);
+    $ignore_warnings = set_test_option("IGNORE_WARNINGS", $i);
     $bisect_manual = set_test_option("BISECT_MANUAL", $i);
     $bisect_skip = set_test_option("BISECT_SKIP", $i);
+    $config_bisect_good = set_test_option("CONFIG_BISECT_GOOD", $i);
     $store_failures = set_test_option("STORE_FAILURES", $i);
+    $test_name = set_test_option("TEST_NAME", $i);
     $timeout = set_test_option("TIMEOUT", $i);
     $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i);
     $console = set_test_option("CONSOLE", $i);
+    $detect_triplefault = set_test_option("DETECT_TRIPLE_FAULT", $i);
     $success_line = set_test_option("SUCCESS_LINE", $i);
     $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i);
     $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i);
@@ -2161,6 +2841,13 @@
     $target_image = set_test_option("TARGET_IMAGE", $i);
     $localversion = set_test_option("LOCALVERSION", $i);
 
+    $start_minconfig_defined = 1;
+
+    if (!defined($start_minconfig)) {
+	$start_minconfig_defined = 0;
+	$start_minconfig = $minconfig;
+    }
+
     chdir $builddir || die "can't change directory to $builddir";
 
     if (!-d $tmpdir) {
@@ -2193,6 +2880,10 @@
 	$run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
     }
 
+    if ($test_type eq "make_min_config") {
+	$run_type = "";
+    }
+
     # mistake in config file?
     if (!defined($run_type)) {
 	$run_type = "ERROR";
@@ -2204,11 +2895,12 @@
     unlink $dmesg;
     unlink $buildlog;
 
-    if (!defined($minconfig)) {
-	$minconfig = $addconfig;
-
-    } elsif (defined($addconfig)) {
-	run_command "cat $addconfig $minconfig > $tmpdir/add_config" or
+    if (defined($addconfig)) {
+	my $min = $minconfig;
+	if (!defined($minconfig)) {
+	    $min = "";
+	}
+	run_command "cat $addconfig $min > $tmpdir/add_config" or
 	    dodie "Failed to create temp config";
 	$minconfig = "$tmpdir/add_config";
     }
@@ -2228,6 +2920,9 @@
     } elsif ($test_type eq "patchcheck") {
 	patchcheck $i;
 	next;
+    } elsif ($test_type eq "make_min_config") {
+	make_min_config $i;
+	next;
     }
 
     if ($build_type ne "nobuild") {
@@ -2235,13 +2930,8 @@
     }
 
     if ($test_type ne "build") {
-	get_grub_index;
-	get_version;
-	install;
-
 	my $failed = 0;
-	start_monitor;
-	monitor or $failed = 1;;
+	start_monitor_and_boot or $failed = 1;
 
 	if (!$failed && $test_type ne "boot" && defined($run_test)) {
 	    do_run_test or $failed = 1;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 48cbcc80..b8bcd14 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -293,6 +293,38 @@
 # or on some systems:
 #POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION
 
+# If there is a script that you require to run before the build is done
+# you can specify it with PRE_BUILD.
+#
+# One example may be if you must add a temporary patch to the build to
+# fix a unrelated bug to perform a patchcheck test. This will apply the
+# patch before each build that is made. Use the POST_BUILD to do a git reset --hard
+# to remove the patch.
+#
+# (default undef)
+#PRE_BUILD = cd ${BUILD_DIR} && patch -p1 < /tmp/temp.patch
+
+# To specify if the test should fail if the PRE_BUILD fails,
+# PRE_BUILD_DIE needs to be set to 1. Otherwise the PRE_BUILD
+# result is ignored.
+# (default 0)
+# PRE_BUILD_DIE = 1
+
+# If there is a script that should run after the build is done
+# you can specify it with POST_BUILD.
+#
+# As the example in PRE_BUILD, POST_BUILD can be used to reset modifications
+# made by the PRE_BUILD.
+#
+# (default undef)
+#POST_BUILD = cd ${BUILD_DIR} && git reset --hard
+
+# To specify if the test should fail if the POST_BUILD fails,
+# POST_BUILD_DIE needs to be set to 1. Otherwise the POST_BUILD
+# result is ignored.
+# (default 0)
+#POST_BUILD_DIE = 1
+
 # Way to reboot the box to the test kernel.
 # Only valid options so far are "grub" and "script"
 # (default grub)
@@ -360,8 +392,8 @@
 #ADD_CONFIG = /home/test/config-broken
 
 # The location on the host where to write temp files
-# (default /tmp/ktest)
-#TMP_DIR = /tmp/ktest
+# (default /tmp/ktest/${MACHINE})
+#TMP_DIR = /tmp/ktest/${MACHINE}
 
 # Optional log file to write the status (recommended)
 #  Note, this is a DEFAULT section only option.
@@ -518,6 +550,16 @@
 # The variables SSH_USER and MACHINE are defined.
 #REBOOT = ssh $SSH_USER@$MACHINE reboot
 
+# The way triple faults are detected is by testing the kernel
+# banner. If the kernel banner for the kernel we are testing is
+# found, and then later a kernel banner for another kernel version
+# is found, it is considered that we encountered a triple fault,
+# and there is no panic or callback, but simply a reboot.
+# To disable this (because it did a false positive) set the following
+# to 0.
+# (default 1)
+#DETECT_TRIPLE_FAULT = 0
+
 #### Per test run options ####
 # The following options are only allowed in TEST_START sections.
 # They are ignored in the DEFAULTS sections.
@@ -535,6 +577,12 @@
 #  all preceding tests until a new CHECKOUT is set.
 #
 #
+# TEST_NAME = name
+#
+#  If you want the test to have a name that is displayed in
+#  the test result banner at the end of the test, then use this
+#  option. This is useful to search for the RESULT keyword and
+#  not have to translate a test number to a test in the config.
 #
 # For TEST_TYPE = patchcheck
 #
@@ -556,7 +604,12 @@
 #      build, boot, test.
 #
 #   Note, the build test will look for warnings, if a warning occurred
-#     in a file that a commit touches, the build will fail.
+#     in a file that a commit touches, the build will fail, unless
+#     IGNORE_WARNINGS is set for the given commit's sha1
+#
+#   IGNORE_WARNINGS can be used to disable the failure of patchcheck
+#     on a particuler commit (SHA1). You can add more than one commit
+#     by adding a list of SHA1s that are space delimited.
 #
 #   If BUILD_NOCLEAN is set, then make mrproper will not be run on
 #   any of the builds, just like all other TEST_TYPE tests. But
@@ -571,6 +624,7 @@
 #   PATCHCHECK_TYPE = boot
 #   PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7
 #   PATCHCHECK_END = HEAD~2
+#   IGNORE_WARNINGS = 42f9c6b69b54946ffc0515f57d01dc7f5c0e4712 0c17ca2c7187f431d8ffc79e81addc730f33d128
 #
 #
 #
@@ -739,13 +793,18 @@
 #	boot	- bad builds but fails to boot
 #	test	- bad boots but fails a test
 #
-#   CONFIG_BISECT is the config that failed to boot
+#  CONFIG_BISECT is the config that failed to boot
 #
-#   If BISECT_MANUAL is set, it will pause between iterations.
-#   This is useful to use just ktest.pl just for the config bisect.
-#   If you set it to build, it will run the bisect and you can
-#   control what happens in between iterations. It will ask you if
-#   the test succeeded or not and continue the config bisect.
+#  If BISECT_MANUAL is set, it will pause between iterations.
+#  This is useful to use just ktest.pl just for the config bisect.
+#  If you set it to build, it will run the bisect and you can
+#  control what happens in between iterations. It will ask you if
+#  the test succeeded or not and continue the config bisect.
+#
+# CONFIG_BISECT_GOOD (optional)
+#  If you have a good config to start with, then you
+#  can specify it with CONFIG_BISECT_GOOD. Otherwise
+#  the MIN_CONFIG is the base.
 #
 # Example:
 #   TEST_START
@@ -755,3 +814,68 @@
 #   MIN_CONFIG = /home/test/config-min
 #   BISECT_MANUAL = 1
 #
+#
+#
+# For TEST_TYPE = make_min_config
+#
+#  After doing a make localyesconfig, your kernel configuration may
+#  not be the most useful minimum configuration. Having a true minimum
+#  config that you can use against other configs is very useful if
+#  someone else has a config that breaks on your code. By only forcing
+#  those configurations that are truly required to boot your machine
+#  will give you less of a chance that one of your set configurations
+#  will make the bug go away. This will give you a better chance to
+#  be able to reproduce the reported bug matching the broken config.
+#
+#  Note, this does take some time, and may require you to run the
+#  test over night, or perhaps over the weekend. But it also allows
+#  you to interrupt it, and gives you the current minimum config
+#  that was found till that time.
+#
+#  Note, this test automatically assumes a BUILD_TYPE of oldconfig
+#  and its test type acts like boot.
+#  TODO: add a test version that makes the config do more than just
+#   boot, like having network access.
+#
+#  To save time, the test does not just grab any option and test
+#  it. The Kconfig files are examined to determine the dependencies
+#  of the configs. If a config is chosen that depends on another
+#  config, that config will be checked first. By checking the
+#  parents first, we can eliminate whole groups of configs that
+#  may have been enabled.
+#
+#  For example, if a USB device config is chosen and depends on CONFIG_USB,
+#  the CONFIG_USB will be tested before the device. If CONFIG_USB is
+#  found not to be needed, it, as well as all configs that depend on
+#  it, will be disabled and removed from the current min_config.
+#
+#  OUTPUT_MIN_CONFIG is the path and filename of the file that will
+#   be created from the MIN_CONFIG. If you interrupt the test, set
+#   this file as your new min config, and use it to continue the test.
+#   This file does not need to exist on start of test.
+#   This file is not created until a config is found that can be removed.
+#   If this file exists, you will be prompted if you want to use it
+#   as the min_config (overriding MIN_CONFIG) if START_MIN_CONFIG
+#   is not defined.
+#   (required field)
+#
+#  START_MIN_CONFIG is the config to use to start the test with.
+#   you can set this as the same OUTPUT_MIN_CONFIG, but if you do
+#   the OUTPUT_MIN_CONFIG file must exist.
+#   (default MIN_CONFIG)
+#
+#  IGNORE_CONFIG is used to specify a config file that has configs that
+#   you already know must be set. Configs are written here that have
+#   been tested and proved to be required. It is best to define this
+#   file if you intend on interrupting the test and running it where
+#   it left off. New configs that it finds will be written to this file
+#   and will not be tested again in later runs.
+#   (optional)
+#
+# Example:
+#
+#  TEST_TYPE = make_min_config
+#  OUTPUT_MIN_CONFIG = /path/to/config-new-min
+#  START_MIN_CONFIG = /path/to/config-min
+#  IGNORE_CONFIG = /path/to/config-tested
+#