Merge "input: cyttsp-i2c: Don't set mode on unsettable regulators" into msm-3.0
diff --git a/Documentation/arm/msm/tspp.txt b/Documentation/arm/msm/tspp.txt
new file mode 100644
index 0000000..a56f014
--- /dev/null
+++ b/Documentation/arm/msm/tspp.txt
@@ -0,0 +1,250 @@
+Introduction
+============
+The TSPP (Transport stream packet processor) is a hardware accelerator
+designed to process MPEG2 TS (transport stream) data. It is mainly used for
+broadcast terrestrial services to off-load the host CPU from real-time
+sensitive TS processing for high bandwidth streams (~20Mbps). Data is received
+either via TSIF (Transport stream interface) or system memory.
+The TSPP driver manages the TSIF 12Seg HW core, which consists of a TSPP, a
+BAM (Bus access manager, used for DMA) and two TSIF inputs.
+It is applicable to the TSIF 12Seg core found on select Qualcomm MSM chips.
+
+For more information on the TSIF interface, please refer to TSIF documentation
+(Documentation/arm/msm/tsif.txt).
+For more information on the BAM interface, please refer to SPS documentation
+(Documentation/dma/sps/sps_architecture.txt).
+
+Hardware description
+====================
+The TSPP unit expands the capabilities of the TSIF interface by adding MPEG2
+stream processing such as:
+	- Elementary Stream PID filtering and de-multiplexing
+	- Several TSP processing operation modes:
+		- TSP Raw processing (with or w/o suffix)
+		- TSP PES assembly
+	- Error handling
+	- Multi2 decryption
+
+    +-------------+  +------+
+  +>|ARM subsystem|  |Memory|
+  | +-------------+  +------+
+  |        |            |
+ I|        |            |
+ R|  ========================== System bus
+ Q|             |
+ s|             |  TSIF 12Seg
+  | +-----------------------+
+  | |  +---+                |
+  | |  |BAM|                |
+  | |  +---+                |  GPIOs
+  | |    |        +------+--|-------- TSIF 0 clk
+  | |  +----+     |TSIF 0|--|-------- TSIF 0 data
+  +-|  |    |-----+------+--|-------- TSIF 0 en
+    |  |TSPP|               |
+    |  |    |-----+------+--|-------- TSIF 1 clk
+    |  +----+     |TSIF 1|--|-------- TSIF 1 data
+    |             +------+--|-------- TSIF 1 en
+    +-----------------------+
+
+   The TSPP unit receives an MPEG2 transport stream either via the two TSIF
+	interfaces, or via system memory.
+   It uses the BAM interface to transfer data to and from system memory.
+   The ARM subsystem receives interrupts on various error conditions from TSPP
+   and TSIF units, and on data transfer events from the BAM.
+
+Software Description
+====================
+The TSPP driver is responsible for:
+ - TSPP/TSIF hardware configuration (using SPS driver to configure BAM
+   hardware)
+ - TSIF GPIO/Clocks configuration
+ - Memory resource management
+ - Handling TSIF/TSPP interrupts and BAM events
+ - TSPP Power management
+
+TSPP Terminology
+----------------
+Device - the TSPP hardware instance
+  - the device contains many streams
+Stream: All data that is received from a particular TS packet source
+  - For example, MPEG2 Transport Stream from TSIF0
+  - A stream can consist of many channels
+Channel: A channel routes part of a stream to a specified memory location
+  - For example, video and audio can be routed to different memory locations
+    using different channels
+  - Channel contents are defined by filters
+Filter: Enables TS packet filtering and routing according to PID (packet ID)
+  - The decision regarding which PIDs in the stream will be routed to a
+    channel is done via filters
+  - Several filters can be registered to the same channel
+  - Filters can pass TS packets as-is (Raw mode) or assemble them into PES
+    packets (PES mode)
+  - Groups of contiguous PIDs can be filtered together (i.e. PSI/SI 0x0-0x2F,
+    1Seg PMTs 0x1FC8-0x1FCF)
+  - Filters can be used to discard packets (e.g. eliminate processing of
+    unwanted channels)
+
+Init flow:
+----------
+Driver registers BAM (via SPS driver) and initializes TSIF/TSPP hardware.
+
+Control path:
+-------------
+1. Client opens a TSPP stream and channel
+2. Client notifies the driver of the source stream (TSIF0/TSIF1/Memory)
+  - TSPP driver allocates memory for the channel (circular buffer)
+  - As the amount of memory varies according to stream bandwidth (which the
+    driver doesn't know), a client can hint to the driver about the required
+	 memory or stream bandwidth
+  - TSPP driver configures TSIF/BAM hardware according to selected stream
+3. Client notifies the driver to filter a set of PIDs for the selected channel
+  - TSPP driver configures TSPP hardware filters accordingly
+4. Client can now read data received from the selected channel
+
+Optional: Scrambling keys can be configured for a filter to decrypt Multi2
+encrypted streams.  The scrambling keys are received encrypted in-stream every
+~2 seconds. The client uses a smart card and master key to decrypt the
+received scrambling keys.  The master key remains inside the smart card and is
+never revealed to software.
+
+Conceptual flow:
+Client                  TSPP Driver                 SPS Driver         Hardware
+ |                           |                           |                   |
+ |                           | Init TSIF/TSPP            |                   |
+ |                           |---------------------------|------------------>|
+ |                           | Register BAM              |                   |
+ |                           |-------------------------->|                   |
+ |                           |                           |                   |
+ | open(tspp.5)              |                           |                   |
+ |-------------------------->|                           |                   |
+ | ioctl(tspp.5,SOURCE,TSIF0)|                           |                   |
+ |-------------------------->|                           |                   |
+ |                           | buff[0..N] = alloc()      |                   |
+ |                           |---------->                |                   |
+ |                           | Connect(TSPP,MEM)         |                   |
+ |                           |-------------------------->|                   |
+ |                           | Transfer(buff[0..N])      |                   |
+ |                           |-------------------------->|                   |
+ |                           |                           |                   |
+ |                           | Configure TSIF0           |                   |
+ |                           |---------------------------|------------------>|
+ |                           |                           |                   |
+ | ioctl(tspp.5,FILTER,pid)  |                           |                   |
+ |-------------------------->|                           |                   |
+ |                           | Configure TSPP filters    |                   |
+ |                           |---------------------------|------------------>|
+ |                           |                           |                   |
+ |                           |                           |               INT |
+ |                           |                           |<------------------|
+ |                           |       notify(EOT,buff[x]) |                   |
+ |                           |<--------------------------|                   |
+ |                           | Transfer(buff[y])         |                   |
+ |                           |-------------------------->|                   |
+ |                           |                           |                   |
+ | read(tspp.5)              |                           |                   |
+ |-------------------------->|                           |                   |
+ |                           |                           |                   |
+
+
+Data path:
+----------
+The TSPP driver is not involved in data transfer, other than managing the
+circular buffer pointers when a transfer is complete.  Data loss can occur if
+a client cannot keep up with stream bandwidth.
+The driver does not notify the application if there is data loss.  It is
+assumed that the application will read data when the data is ready, and when
+the application is able.
+
+API
+===
+int tspp_open_stream(tspp_device *dev, void *stream, void *channel, tspp_mode
+	mode);
+int tspp_close_stream(tspp_device *dev, void *stream);
+int tspp_open_channel(tspp_device *dev, int dest, int bufsize, void *channel);
+int tspp_close_channel(tspp_device *dev, void *channel);
+int tspp_register_filter(tspp_device *dev, void *channel, tspp_filter *filter);
+int tspp_unregister_filter(tspp_device *dev, void *channel, int pid);
+
+Refer to chrdev implementation in kernel/drivers/misc/tspp.c for an example of
+how to use this api.
+
+Each stream is represented by a chrdev device
+16 available devices: /dev/tspp00 - /dev/tspp15
+Each device implements these functions:
+open()
+close()
+read()
+ioctl()
+
+ioctl() contains several sub-functions:
+0: select source
+1: add filter
+2: remove filter
+3: set decryption keys
+4: set initial cbc values (IV)
+5: set system keys
+6: set buffer size
+
+You can refer to include/linux/tspp.h for the details on the structures
+associated with these IOCTL calls.
+
+Design
+======
+Design is based on the existing TSIF driver, with added control functionality
+for the extra hardware features.
+
+Power Management
+===============
+TSPP driver prevents MSM from sleeping while TSPP hardware is active.
+To achieve this, the driver holds the wake lock.  When no channels are
+configured to receive data, the driver stops the clocks to save power.
+It will register for suspend/resume in the future.
+
+SMP/multi-core
+==============
+Driver is fully SMP aware.
+
+Performance
+===========
+Control flows are rare.
+Data path only involves maintaining the circular buffer pointers.
+
+Interface
+=========
+Driver exposes a char device interface to user-space for each channel.
+Control transactions are performed via ioctl on the channel. Data is read as
+any regular char device (blocking and non-blocking).  Please see sequence
+under software description for more info.
+
+Debugfs may be used for debug purposes.  Through debugfs, all of the TSIF
+registers can be accessed under /sys/kernel/debug/tsif0 and tsif1.  The
+TSPP registers are found under /sys/kernel/debug/tspp0.  If you cannot
+see these devices, then either the driver has been built without debugfs
+support (check the define TSPP_USE_DEBUGFS in tspp.c) or the debugfs has
+not been mounted correctly (or mounted in a different location).
+
+Driver Parameters
+=================
+TSPP driver has target-specific parameters:
+- Memory base addresses for TSIF/TSPP/BAM
+- TSIF/TSPP/BAM IRQ numbers
+- TSIF GPIO numbers
+
+Config Options
+==============
+To enable the driver, set CONFIG_TSPP (=y or =m) in the appropriate
+config file for the platform.
+
+Dependencies
+============
+The TSPP driver depends on the SPS driver to configure BAM hardware.
+
+User space utilities
+====================
+The TSPP test suite can be found in:
+vendor/qcom/proprietary/kernel-tests/tspp
+
+Known Issues
+============
+Currently PES processing mode cannot be configured for streams in which the PES
+length is 0. This is a HW limitation.
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 00ff580..fc1d81a 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -295,6 +295,7 @@
 CONFIG_MMC_MSM_CARD_HW_DETECTION=y
 CONFIG_MMC_MSM_SDC3_SUPPORT=y
 CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y
+CONFIG_LEDS_GPIO=y
 CONFIG_LEDS_MSM_PDM=y
 CONFIG_SWITCH=y
 CONFIG_SWITCH_GPIO=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index da8b9df..bf4011f 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -291,6 +291,7 @@
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8960=y
 CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
 CONFIG_MFD_PM8921_CORE=y
 CONFIG_MFD_PM8821_CORE=y
 CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 10b55dc..6248c98 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -292,6 +292,7 @@
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8960=y
 CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
 CONFIG_MFD_PM8921_CORE=y
 CONFIG_MFD_PM8821_CORE=y
 CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/mach-msm/board-8930-regulator.c b/arch/arm/mach-msm/board-8930-regulator.c
index 989fde3..c652238 100644
--- a/arch/arm/mach-msm/board-8930-regulator.c
+++ b/arch/arm/mach-msm/board-8930-regulator.c
@@ -110,6 +110,9 @@
 	REGULATOR_SUPPLY("dsi_vddio",		"mipi_dsi.1"),
 	REGULATOR_SUPPLY("hdmi_avdd",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("hdmi_vcc",		"hdmi_msm.0"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_riva"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
 };
 VREG_CONSUMERS(L24) = {
 	REGULATOR_SUPPLY("8038_l24",		NULL),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 69ea6ee..929f280 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1701,14 +1701,6 @@
 	.tsens_num_sensor	= 5,
 };
 
-static struct platform_device msm_tsens_device = {
-	.name	= "tsens8960-tm",
-	.id	= -1,
-	.dev	= {
-		.platform_data = &msm_tsens_pdata,
-	},
-};
-
 #ifdef CONFIG_MSM_FAKE_BATTERY
 static struct platform_device fish_battery_device = {
 	.name = "fish_battery",
@@ -1891,7 +1883,6 @@
 	&msm_bus_mm_fabric,
 	&msm_bus_sys_fpb,
 	&msm_bus_cpss_fpb,
-	&msm_tsens_device,
 };
 
 static void __init msm8930_i2c_init(void)
@@ -2213,6 +2204,7 @@
 	if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
 		pr_err("meminfo_init() failed!\n");
 
+	msm_tsens_early_init(&msm_tsens_pdata);
 	BUG_ON(msm_rpm_init(&msm8930_rpm_data));
 	BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
 
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index f36c3a1..5520bf9 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -110,6 +110,9 @@
 	REGULATOR_SUPPLY("8921_l23",		NULL),
 	REGULATOR_SUPPLY("dsi_vddio",		"mipi_dsi.1"),
 	REGULATOR_SUPPLY("hdmi_avdd",		"hdmi_msm.0"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_riva"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
 };
 VREG_CONSUMERS(L24) = {
 	REGULATOR_SUPPLY("8921_l24",		NULL),
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 6683f90..d464375 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1572,14 +1572,6 @@
 		.tsens_num_sensor	= 5,
 };
 
-static struct platform_device msm_tsens_device = {
-	.name	= "tsens8960-tm",
-	.id	= -1,
-	.dev	= {
-		.platform_data = &msm_tsens_pdata,
-	},
-};
-
 #ifdef CONFIG_MSM_FAKE_BATTERY
 static struct platform_device fish_battery_device = {
 	.name = "fish_battery",
@@ -1797,7 +1789,6 @@
 	&msm_bus_mm_fabric,
 	&msm_bus_sys_fpb,
 	&msm_bus_cpss_fpb,
-	&msm_tsens_device,
 };
 
 static void __init msm8960_i2c_init(void)
@@ -2177,6 +2168,7 @@
 		&msm8960_device_watchdog.dev.platform_data;
 
 	wdog_pdata->bark_time = 15000;
+	msm_tsens_early_init(&msm_tsens_pdata);
 	BUG_ON(msm_rpm_init(&msm8960_rpm_data));
 	BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
 	regulator_suppress_info_printing();
@@ -2213,6 +2205,7 @@
 
 static void __init msm8960_rumi3_init(void)
 {
+	msm_tsens_early_init(&msm_tsens_pdata);
 	BUG_ON(msm_rpm_init(&msm8960_rpm_data));
 	BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
 	regulator_suppress_info_printing();
@@ -2247,6 +2240,7 @@
 	if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
 		pr_err("meminfo_init() failed!\n");
 
+	msm_tsens_early_init(&msm_tsens_pdata);
 	BUG_ON(msm_rpm_init(&msm8960_rpm_data));
 	BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
 
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index a21abb8..1f2a5ec 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -793,10 +793,75 @@
 				__func__, rc);
 }
 
+/* 8625 keypad device information */
+static unsigned int kp_row_gpios_8625[] = {31};
+static unsigned int kp_col_gpios_8625[] = {36, 37};
+
+static const unsigned short keymap_8625[] = {
+	KEY_VOLUMEUP,
+	KEY_VOLUMEDOWN,
+};
+
+static struct gpio_event_matrix_info kp_matrix_info_8625 = {
+	.info.func      = gpio_event_matrix_func,
+	.keymap         = keymap_8625,
+	.output_gpios   = kp_row_gpios_8625,
+	.input_gpios    = kp_col_gpios_8625,
+	.noutputs       = ARRAY_SIZE(kp_row_gpios_8625),
+	.ninputs        = ARRAY_SIZE(kp_col_gpios_8625),
+	.settle_time.tv_nsec = 40 * NSEC_PER_USEC,
+	.poll_time.tv_nsec = 20 * NSEC_PER_MSEC,
+	.flags          = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE |
+			  GPIOKPF_PRINT_UNMAPPED_KEYS,
+};
+
+static struct gpio_event_info *kp_info_8625[] = {
+	&kp_matrix_info_8625.info,
+};
+static struct gpio_event_platform_data kp_pdata_8625 = {
+	.name           = "8625_kp",
+	.info           = kp_info_8625,
+	.info_count     = ARRAY_SIZE(kp_info_8625)
+};
+
+static struct platform_device kp_pdev_8625 = {
+	.name   = GPIO_EVENT_DEV_NAME,
+	.id     = -1,
+	.dev    = {
+		.platform_data  = &kp_pdata_8625,
+	},
+};
+
+#define LED_RED_GPIO_8625 49
+#define LED_GREEN_GPIO_8625 34
+
+static struct gpio_led gpio_leds_config_8625[] = {
+	{
+		.name = "green",
+		.gpio = LED_GREEN_GPIO_8625,
+	},
+	{
+		.name = "red",
+		.gpio = LED_RED_GPIO_8625,
+	},
+};
+
+static struct gpio_led_platform_data gpio_leds_pdata_8625 = {
+	.num_leds = ARRAY_SIZE(gpio_leds_config_8625),
+	.leds = gpio_leds_config_8625,
+};
+
+static struct platform_device gpio_leds_8625 = {
+	.name          = "leds-gpio",
+	.id            = -1,
+	.dev           = {
+		.platform_data = &gpio_leds_pdata_8625,
+	},
+};
+
 static void msm7627a_add_io_devices(void)
 {
-	if (machine_is_msm7627a_evb())
-		return;
+	int rc;
 
 #if defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C) || \
 	defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C_MODULE)
@@ -810,6 +875,30 @@
 	msm_init_pmic_vibrator();
 #endif
 
+	/* keypad */
+	if (machine_is_msm7627a_evb())
+		platform_device_register(&kp_pdev_8625);
+
+	/* leds */
+	if (machine_is_msm7627a_evb()) {
+		rc = gpio_tlmm_config(GPIO_CFG(LED_RED_GPIO_8625, 0,
+				GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP,
+				GPIO_CFG_16MA), GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("%s: gpio_tlmm_config for %d failed\n",
+				__func__, LED_RED_GPIO_8625);
+		}
+
+		rc = gpio_tlmm_config(GPIO_CFG(LED_GREEN_GPIO_8625, 0,
+				GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP,
+				GPIO_CFG_16MA), GPIO_CFG_ENABLE);
+		if (rc) {
+			pr_err("%s: gpio_tlmm_config for %d failed\n",
+				__func__, LED_GREEN_GPIO_8625);
+		}
+
+		platform_device_register(&gpio_leds_8625);
+	}
 }
 
 #define UART1DM_RX_GPIO		45
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index d9e1724..afd3612d 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -4813,6 +4813,8 @@
 
 static struct clk_lookup msm_clocks_8064[] = {
 	CLK_LOOKUP("cxo",		cxo_clk.c,		NULL),
+	CLK_LOOKUP("cxo",		cxo_clk.c,		"wcnss_wlan.0"),
+	CLK_LOOKUP("cxo",		cxo_clk.c,		"pil_riva"),
 	CLK_LOOKUP("pll2",		pll2_clk.c,		NULL),
 	CLK_LOOKUP("pll8",		pll8_clk.c,		NULL),
 	CLK_LOOKUP("pll4",		pll4_clk.c,		NULL),
@@ -5050,6 +5052,8 @@
 
 static struct clk_lookup msm_clocks_8960_v1[] __initdata = {
 	CLK_LOOKUP("cxo",		cxo_clk.c,		NULL),
+	CLK_LOOKUP("cxo",		cxo_clk.c,		"wcnss_wlan.0"),
+	CLK_LOOKUP("cxo",		cxo_clk.c,		"pil_riva"),
 	CLK_LOOKUP("pll2",		pll2_clk.c,		NULL),
 	CLK_LOOKUP("pll8",		pll8_clk.c,		NULL),
 	CLK_LOOKUP("pll4",		pll4_clk.c,		NULL),
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 050ec41..2d5ae78 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, 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
@@ -454,12 +454,20 @@
  * Frequency-related functions
  */
 
-/* Set a clock's frequency. */
-static int _rcg_clk_set_rate(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+/* Set a clock to an exact rate. */
+int rcg_clk_set_rate(struct clk *c, unsigned long rate)
 {
-	struct clk_freq_tbl *cf;
-	int rc = 0;
+	struct rcg_clk *clk = to_rcg_clk(c);
+	struct clk_freq_tbl *nf, *cf;
 	struct clk *chld;
+	int rc = 0;
+
+	for (nf = clk->freq_tbl; nf->freq_hz != FREQ_END
+			&& nf->freq_hz != rate; nf++)
+		;
+
+	if (nf->freq_hz == FREQ_END)
+		return -EINVAL;
 
 	/* Check if frequency is actually changed. */
 	cf = clk->current_freq;
@@ -523,22 +531,6 @@
 	return rc;
 }
 
-/* Set a clock to an exact rate. */
-int rcg_clk_set_rate(struct clk *c, unsigned long rate)
-{
-	struct rcg_clk *clk = to_rcg_clk(c);
-	struct clk_freq_tbl *nf;
-
-	for (nf = clk->freq_tbl; nf->freq_hz != FREQ_END
-			&& nf->freq_hz != rate; nf++)
-		;
-
-	if (nf->freq_hz == FREQ_END)
-		return -EINVAL;
-
-	return _rcg_clk_set_rate(clk, nf);
-}
-
 /* Get the currently-set rate of a clock in Hz. */
 unsigned long rcg_clk_get_rate(struct clk *c)
 {
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 802ee2a..af272af 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, 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
@@ -149,7 +149,14 @@
 			    (msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) |
 			    (msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) |
 			    (msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) |
-			    ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20);
+			    /*
+			     * 8660 RPM has XO_CORE at bit 18 and 8960 RPM has
+			     * XO_CORE at bit 20. Since the opposite bit is
+			     * reserved in both cases, just set both and be
+			     * done with it.
+			     */
+			    ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20) |
+			    ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 18);
 		ret = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
 	}
 
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index d8ebab5..cdbfc48 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -19,6 +19,7 @@
 #include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/workqueue.h>
 
 #include <mach/msm_bus.h>
 #include <mach/msm_iomap.h>
@@ -68,9 +69,10 @@
 	void __iomem *modem_base;
 	unsigned long start_addr;
 	struct regulator *vreg;
+	struct regulator *pll_supply;
 	bool vreg_enabled;
 	struct msm_xo_voter *xo;
-	struct timer_list xo_timer;
+	struct delayed_work work;
 };
 
 static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -87,27 +89,32 @@
 	return 0;
 }
 
-static void pil_q6v4_make_xo_proxy_votes(struct device *dev)
+static void pil_q6v4_make_proxy_votes(struct device *dev)
 {
 	struct q6v4_data *drv = dev_get_drvdata(dev);
+	int ret;
 
 	msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
-	mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+	if (drv->pll_supply) {
+		ret = regulator_enable(drv->pll_supply);
+		if (ret)
+			dev_err(dev, "failed to enable pll supply\n");
+	}
+	schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
 }
 
-static void pil_q6v4_remove_xo_proxy_votes(unsigned long data)
+static void pil_q6v4_remove_proxy_votes(struct work_struct *work)
 {
-	struct q6v4_data *drv = (struct q6v4_data *)data;
-
+	struct q6v4_data *drv = container_of(work, struct q6v4_data, work.work);
+	if (drv->pll_supply)
+		regulator_disable(drv->pll_supply);
 	msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
 }
 
-static void pil_q6v4_remove_xo_proxy_votes_now(struct device *dev)
+static void pil_q6v4_remove_proxy_votes_now(struct device *dev)
 {
 	struct q6v4_data *drv = dev_get_drvdata(dev);
-
-	if (del_timer(&drv->xo_timer))
-		pil_q6v4_remove_xo_proxy_votes((unsigned long)drv);
+	flush_delayed_work(&drv->work);
 }
 
 static int pil_q6v4_power_up(struct device *dev)
@@ -184,7 +191,7 @@
 	const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
 	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
 
-	pil_q6v4_make_xo_proxy_votes(pil->dev);
+	pil_q6v4_make_proxy_votes(pil->dev);
 
 	err = pil_q6v4_power_up(pil->dev);
 	if (err)
@@ -300,7 +307,7 @@
 		drv->vreg_enabled = false;
 	}
 
-	pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
+	pil_q6v4_remove_proxy_votes_now(pil->dev);
 
 	return 0;
 }
@@ -324,7 +331,7 @@
 	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
 	int err;
 
-	pil_q6v4_make_xo_proxy_votes(pil->dev);
+	pil_q6v4_make_proxy_votes(pil->dev);
 
 	err = pil_q6v4_power_up(pil->dev);
 	if (err)
@@ -355,7 +362,7 @@
 		drv->vreg_enabled = false;
 	}
 
-	pil_q6v4_remove_xo_proxy_votes_now(pil->dev);
+	pil_q6v4_remove_proxy_votes_now(pil->dev);
 
 	return ret;
 }
@@ -373,6 +380,7 @@
 	struct q6v4_data *drv;
 	struct resource *res;
 	struct pil_desc *desc;
+	int ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res)
@@ -396,9 +404,26 @@
 	}
 
 	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!drv)
+	if (!desc)
 		return -ENOMEM;
 
+	drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
+	if (IS_ERR(drv->pll_supply)) {
+		drv->pll_supply = NULL;
+	} else {
+		ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to set pll voltage\n");
+			goto err;
+		}
+
+		ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to set pll optimum mode\n");
+			goto err;
+		}
+	}
+
 	desc->name = pdata->name;
 	desc->depends_on = pdata->depends;
 	desc->dev = &pdev->dev;
@@ -412,26 +437,39 @@
 	}
 
 	drv->vreg = regulator_get(&pdev->dev, "core_vdd");
-	if (IS_ERR(drv->vreg))
-		return PTR_ERR(drv->vreg);
-
-	setup_timer(&drv->xo_timer, pil_q6v4_remove_xo_proxy_votes,
-		    (unsigned long)drv);
-	drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
-	if (IS_ERR(drv->xo))
-		return PTR_ERR(drv->xo);
-
-	if (msm_pil_register(desc)) {
-		regulator_put(drv->vreg);
-		return -EINVAL;
+	if (IS_ERR(drv->vreg)) {
+		ret = PTR_ERR(drv->vreg);
+		goto err;
 	}
+
+	drv->xo = msm_xo_get(pdata->xo_id, pdata->name);
+	if (IS_ERR(drv->xo)) {
+		ret = PTR_ERR(drv->xo);
+		goto err_xo;
+	}
+	INIT_DELAYED_WORK(&drv->work, pil_q6v4_remove_proxy_votes);
+
+	ret = msm_pil_register(desc);
+	if (ret)
+		goto err_pil;
 	return 0;
+err_pil:
+	cancel_delayed_work_sync(&drv->work);
+	msm_xo_put(drv->xo);
+err_xo:
+	regulator_put(drv->vreg);
+err:
+	regulator_put(drv->pll_supply);
+	return ret;
 }
 
 static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
 {
 	struct q6v4_data *drv = platform_get_drvdata(pdev);
+	cancel_delayed_work_sync(&drv->work);
+	msm_xo_put(drv->xo);
 	regulator_put(drv->vreg);
+	regulator_put(drv->pll_supply);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index 6848c59..7e78a15 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -18,9 +18,11 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
 
 #include <mach/msm_iomap.h>
-#include <mach/msm_xo.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -80,38 +82,40 @@
 struct riva_data {
 	void __iomem *base;
 	unsigned long start_addr;
-	struct msm_xo_voter *xo;
-	struct timer_list xo_timer;
+	struct clk *xo;
+	bool use_cxo;
+	struct delayed_work work;
+	struct regulator *pll_supply;
 };
 
-static void pil_riva_make_xo_proxy_votes(struct device *dev)
+static void pil_riva_make_proxy_votes(struct device *dev)
 {
 	struct riva_data *drv = dev_get_drvdata(dev);
+	int ret;
 
-	msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
-	mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+	if (drv->use_cxo) {
+		ret = clk_prepare_enable(drv->xo);
+		if (ret)
+			dev_err(dev, "failed to enable xo\n");
+	}
+	ret = regulator_enable(drv->pll_supply);
+	if (ret)
+		dev_err(dev, "failed to enable pll supply\n");
+	schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
 }
 
-static void pil_riva_remove_xo_proxy_votes(unsigned long data)
+static void pil_riva_remove_proxy_votes(struct work_struct *work)
 {
-	struct riva_data *drv = (struct riva_data *)data;
-
-	msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
+	struct riva_data *drv = container_of(work, struct riva_data, work.work);
+	regulator_disable(drv->pll_supply);
+	if (drv->use_cxo)
+		clk_disable_unprepare(drv->xo);
 }
 
-static void pil_riva_remove_xo_proxy_votes_now(struct device *dev)
+static void pil_riva_remove_proxy_votes_now(struct device *dev)
 {
 	struct riva_data *drv = dev_get_drvdata(dev);
-
-	if (del_timer(&drv->xo_timer))
-		pil_riva_remove_xo_proxy_votes((unsigned long)drv);
-}
-
-static bool cxo_is_needed(struct riva_data *drv)
-{
-	u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
-	return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
-		!= RIVA_PMU_CFG_IRIS_XO_MODE_48;
+	flush_delayed_work(&drv->work);
 }
 
 static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
@@ -128,30 +132,38 @@
 	return 0;
 }
 
+static bool cxo_is_needed(struct riva_data *drv)
+{
+	u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
+	return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
+		!= RIVA_PMU_CFG_IRIS_XO_MODE_48;
+}
+
 static int pil_riva_reset(struct pil_desc *pil)
 {
 	u32 reg, sel;
-	bool use_cxo;
 	struct riva_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *base = drv->base;
 	unsigned long start_addr = drv->start_addr;
+	int ret;
 
+	ret = clk_prepare_enable(drv->xo);
+	if (ret)
+		return ret;
 	/* Enable A2XB bridge */
 	reg = readl_relaxed(base + RIVA_PMU_A2XB_CFG);
 	reg |= RIVA_PMU_A2XB_CFG_EN;
 	writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG);
 
-	/* Proxy-vote for CXO if it's needed */
-	use_cxo = cxo_is_needed(drv);
-	if (use_cxo)
-		pil_riva_make_xo_proxy_votes(pil->dev);
+	drv->use_cxo = cxo_is_needed(drv);
+	pil_riva_make_proxy_votes(pil->dev);
 
 	/* Program PLL 13 to 960 MHz */
 	reg = readl_relaxed(RIVA_PLL_MODE);
 	reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
 	writel_relaxed(reg, RIVA_PLL_MODE);
 
-	if (use_cxo)
+	if (drv->use_cxo)
 		writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
 	else
 		writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
@@ -161,7 +173,7 @@
 
 	reg = readl_relaxed(RIVA_PLL_MODE);
 	reg &= ~(PLL_MODE_REF_XO_SEL);
-	reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
+	reg |= drv->use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
 	writel_relaxed(reg, RIVA_PLL_MODE);
 
 	/* Enable PLL 13 */
@@ -226,6 +238,7 @@
 	/* Take cCPU out of reset */
 	reg |= RIVA_PMU_OVRD_VAL_CCPU_RESET;
 	writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL);
+	clk_disable_unprepare(drv->xo);
 
 	return 0;
 }
@@ -234,7 +247,11 @@
 {
 	struct riva_data *drv = dev_get_drvdata(pil->dev);
 	u32 reg;
+	int ret;
 
+	ret = clk_prepare_enable(drv->xo);
+	if (ret)
+		return ret;
 	/* Put cCPU and cCPU clock into reset */
 	reg = readl_relaxed(drv->base + RIVA_PMU_OVRD_VAL);
 	reg &= ~(RIVA_PMU_OVRD_VAL_CCPU_RESET | RIVA_PMU_OVRD_VAL_CCPU_CLK);
@@ -253,7 +270,8 @@
 	writel_relaxed(0, RIVA_RESET);
 	mb();
 
-	pil_riva_remove_xo_proxy_votes_now(pil->dev);
+	clk_disable_unprepare(drv->xo);
+	pil_riva_remove_proxy_votes_now(pil->dev);
 
 	return 0;
 }
@@ -274,19 +292,29 @@
 static int pil_riva_reset_trusted(struct pil_desc *pil)
 {
 	struct riva_data *drv = dev_get_drvdata(pil->dev);
+	int ret;
 
-	/* Proxy-vote for CXO if it's needed */
-	if (cxo_is_needed(drv))
-		pil_riva_make_xo_proxy_votes(pil->dev);
-
-	return pas_auth_and_reset(PAS_RIVA);
+	ret = clk_prepare_enable(drv->xo);
+	if (ret)
+		return ret;
+	/* Proxy-vote for resources RIVA needs */
+	pil_riva_make_proxy_votes(pil->dev);
+	ret = pas_auth_and_reset(PAS_RIVA);
+	clk_disable_unprepare(drv->xo);
+	return ret;
 }
 
 static int pil_riva_shutdown_trusted(struct pil_desc *pil)
 {
-	int ret = pas_shutdown(PAS_RIVA);
+	int ret;
+	struct riva_data *drv = dev_get_drvdata(pil->dev);
 
-	pil_riva_remove_xo_proxy_votes_now(pil->dev);
+	ret = clk_prepare_enable(drv->xo);
+	if (ret)
+		return ret;
+	ret = pas_shutdown(PAS_RIVA);
+	pil_riva_remove_proxy_votes_now(pil->dev);
+	clk_disable_unprepare(drv->xo);
 
 	return ret;
 }
@@ -303,6 +331,7 @@
 	struct riva_data *drv;
 	struct resource *res;
 	struct pil_desc *desc;
+	int ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res)
@@ -321,6 +350,23 @@
 	if (!desc)
 		return -ENOMEM;
 
+	drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
+	if (IS_ERR(drv->pll_supply)) {
+		dev_err(&pdev->dev, "failed to get pll supply\n");
+		return PTR_ERR(drv->pll_supply);
+	}
+	ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to set pll supply voltage\n");
+		goto err;
+	}
+
+	ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to set pll supply optimum mode\n");
+		goto err;
+	}
+
 	desc->name = "wcnss";
 	desc->dev = &pdev->dev;
 
@@ -332,17 +378,31 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	setup_timer(&drv->xo_timer, pil_riva_remove_xo_proxy_votes,
-		    (unsigned long)drv);
-	drv->xo = msm_xo_get(MSM_XO_CXO, desc->name);
-	if (IS_ERR(drv->xo))
-		return PTR_ERR(drv->xo);
+	drv->xo = clk_get(&pdev->dev, "cxo");
+	if (IS_ERR(drv->xo)) {
+		ret = PTR_ERR(drv->xo);
+		goto err;
+	}
+	INIT_DELAYED_WORK(&drv->work, pil_riva_remove_proxy_votes);
 
-	return msm_pil_register(desc);
+	ret = msm_pil_register(desc);
+	if (ret)
+		goto err_register;
+	return 0;
+err_register:
+	cancel_delayed_work_sync(&drv->work);
+	clk_put(drv->xo);
+err:
+	regulator_put(drv->pll_supply);
+	return ret;
 }
 
 static int __devexit pil_riva_remove(struct platform_device *pdev)
 {
+	struct riva_data *drv = platform_get_drvdata(pdev);
+	cancel_delayed_work_sync(&drv->work);
+	clk_put(drv->xo);
+	regulator_put(drv->pll_supply);
 	return 0;
 }
 
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 6853654..e147b0e 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -80,11 +80,14 @@
 	case MSM8660_MACHINE_ID:
 		return APQ8060_TOOLS_ID;
 	case AO8960_MACHINE_ID:
+	case MSM8260A_MACHINE_ID:
 		return AO8960_TOOLS_ID;
 	case APQ8064_MACHINE_ID:
 		return APQ8064_TOOLS_ID;
 	case MSM8930_MACHINE_ID:
 		return MSM8930_TOOLS_ID;
+	case MSM8974_MACHINE_ID:
+		return MSM8974_TOOLS_ID;
 	default:
 		return 0;
 	}
@@ -105,12 +108,28 @@
 	case APQ8030_MACHINE_ID:
 	case MSM8627_MACHINE_ID:
 	case MSM8227_MACHINE_ID:
+	case MSM8974_MACHINE_ID:
+	case MDM9615_MACHINE_ID:
+	case MSM8260A_MACHINE_ID:
 		return 1;
 	default:
 		return 0;
 	}
 }
 
+/*
+ * This will return TRUE for targets which support apps as master.
+ * Thus, SW DLOAD and Mode Reset are supported on apps processor.
+ * This applies to 8960 and newer targets.
+ */
+int chk_apps_master(void)
+{
+	if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615())
+		return 1;
+	else
+		return 0;
+}
+
 void __diag_smd_send_req(void)
 {
 	void *buf = NULL;
@@ -532,8 +551,7 @@
 	} else {
 		if (len > 0) {
 			if (entry.client_id == MODEM_PROC && driver->ch) {
-				if ((cpu_is_msm8960() || cpu_is_msm8930() ||
-					cpu_is_msm9615()) &&
+				if (chk_apps_master() &&
 					 (int)(*(char *)buf) == MODE_CMD)
 					if ((int)(*(char *)(buf+1)) ==
 						RESET_ID)
@@ -573,8 +591,7 @@
 	temp += 2;
 	data_type = APPS_DATA;
 	/* Dont send any command other than mode reset */
-	if ((cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615()) &&
-		cmd_code == MODE_CMD) {
+	if (chk_apps_master() && cmd_code == MODE_CMD) {
 		if (subsys_id != RESET_ID)
 			data_type = MODEM_DATA;
 	}
@@ -858,8 +875,7 @@
 		return 0;
 	}
 	/* Check for download command */
-	else if ((cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_msm8930()
-		|| cpu_is_msm9615()) && (*buf == 0x3A)) {
+	else if ((cpu_is_msm8x60() || chk_apps_master()) && (*buf == 0x3A)) {
 		/* send response back */
 		driver->apps_rsp_buf[0] = *buf;
 		ENCODE_RSP_AND_SEND(0);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 9730d4f..f2a2210 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -135,7 +135,7 @@
 {
 	int r = 0;
 
-	/* open control ports only on 8960 */
+	/* open control ports only on 8960 & newer targets */
 	if (chk_apps_only()) {
 		if (pdev->id == SMD_APPS_MODEM)
 			r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver,
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 5e325e9..6bb0739 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -438,6 +438,7 @@
 	ion_buffer_put(buffer);
 	return handle;
 }
+EXPORT_SYMBOL(ion_alloc);
 
 void ion_free(struct ion_client *client, struct ion_handle *handle)
 {
@@ -455,6 +456,7 @@
 	}
 	ion_handle_put(handle);
 }
+EXPORT_SYMBOL(ion_free);
 
 static void ion_client_get(struct ion_client *client);
 static int ion_client_put(struct ion_client *client);
@@ -512,6 +514,7 @@
 	ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len);
 	return ret;
 }
+EXPORT_SYMBOL(ion_phys);
 
 void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle,
 			unsigned long flags)
@@ -558,6 +561,7 @@
 	mutex_unlock(&client->lock);
 	return vaddr;
 }
+EXPORT_SYMBOL(ion_map_kernel);
 
 int __ion_iommu_map(struct ion_buffer *buffer,
 		int domain_num, int partition_num, unsigned long align,
@@ -776,6 +780,7 @@
 	mutex_unlock(&client->lock);
 	return sglist;
 }
+EXPORT_SYMBOL(ion_map_dma);
 
 void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)
 {
@@ -791,6 +796,7 @@
 	mutex_unlock(&buffer->lock);
 	mutex_unlock(&client->lock);
 }
+EXPORT_SYMBOL(ion_unmap_kernel);
 
 void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle)
 {
@@ -806,7 +812,7 @@
 	mutex_unlock(&buffer->lock);
 	mutex_unlock(&client->lock);
 }
-
+EXPORT_SYMBOL(ion_unmap_dma);
 
 struct ion_buffer *ion_share(struct ion_client *client,
 				 struct ion_handle *handle)
@@ -828,6 +834,7 @@
 	 */
 	return handle->buffer;
 }
+EXPORT_SYMBOL(ion_share);
 
 struct ion_handle *ion_import(struct ion_client *client,
 			      struct ion_buffer *buffer)
@@ -849,6 +856,7 @@
 	mutex_unlock(&client->lock);
 	return handle;
 }
+EXPORT_SYMBOL(ion_import);
 
 static int check_vaddr_bounds(unsigned long start, unsigned long end)
 {
@@ -937,6 +945,7 @@
 	fput(file);
 	return handle;
 }
+EXPORT_SYMBOL(ion_import_fd);
 
 static int ion_debug_client_show(struct seq_file *s, void *unused)
 {
@@ -1137,6 +1146,7 @@
 	if (client)
 		ion_client_put(client);
 }
+EXPORT_SYMBOL(ion_client_destroy);
 
 int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle,
 			unsigned long *flags)
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
index 908b63d..7b8f3e6 100644
--- a/drivers/gpu/msm/Makefile
+++ b/drivers/gpu/msm/Makefile
@@ -8,7 +8,8 @@
 	kgsl_pwrscale.o \
 	kgsl_mmu.o \
 	kgsl_gpummu.o \
-	kgsl_iommu.o
+	kgsl_iommu.o \
+	kgsl_snapshot.o
 
 msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o
 msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o
@@ -20,8 +21,10 @@
 	adreno_ringbuffer.o \
 	adreno_drawctxt.o \
 	adreno_postmortem.o \
+	adreno_snapshot.o \
 	adreno_a2xx.o \
 	adreno_a2xx_trace.o \
+	adreno_a2xx_snapshot.o \
 	adreno.o
 
 msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o
diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h
index 5ffdea1..50b2745 100644
--- a/drivers/gpu/msm/a2xx_reg.h
+++ b/drivers/gpu/msm/a2xx_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-2012, 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
@@ -416,4 +416,37 @@
 #define REG_A225_GRAS_UCP5W              0x2357
 #define REG_A225_GRAS_UCP_ENABLED        0x2360
 
+/* Debug registers used by snapshot */
+#define REG_PA_SU_DEBUG_CNTL            0x0C80
+#define REG_PA_SU_DEBUG_DATA            0x0C81
+#define REG_RB_DEBUG_CNTL               0x0F26
+#define REG_RB_DEBUG_DATA               0x0F27
+#define REG_PC_DEBUG_CNTL               0x0C38
+#define REG_PC_DEBUG_DATA               0x0C39
+#define REG_GRAS_DEBUG_CNTL             0x0C80
+#define REG_GRAS_DEBUG_DATA             0x0C81
+#define REG_SQ_DEBUG_MISC               0x0D05
+#define REG_SQ_DEBUG_INPUT_FSM          0x0DAE
+#define REG_SQ_DEBUG_CONST_MGR_FSM      0x0DAF
+#define REG_SQ_DEBUG_EXP_ALLOC          0x0DB3
+#define REG_SQ_DEBUG_FSM_ALU_0          0x0DB1
+#define REG_SQ_DEBUG_FSM_ALU_1          0x0DB2
+#define REG_SQ_DEBUG_PTR_BUFF           0x0DB4
+#define REG_SQ_DEBUG_GPR_VTX            0x0DB5
+#define REG_SQ_DEBUG_GPR_PIX            0x0DB6
+#define REG_SQ_DEBUG_TB_STATUS_SEL      0x0DB7
+#define REG_SQ_DEBUG_VTX_TB_0           0x0DB8
+#define REG_SQ_DEBUG_VTX_TB_1           0x0DB9
+#define REG_SQ_DEBUG_VTX_TB_STATE_MEM   0x0DBB
+#define REG_SQ_DEBUG_TP_FSM             0x0DB0
+#define REG_SQ_DEBUG_VTX_TB_STATUS_REG  0x0DBA
+#define REG_SQ_DEBUG_PIX_TB_0           0x0DBC
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_0 0x0DBD
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_1 0x0DBE
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_2 0x0DBF
+#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_3 0x0DC0
+#define REG_SQ_DEBUG_PIX_TB_STATE_MEM   0x0DC1
+#define REG_SQ_DEBUG_MISC_0             0x2309
+#define REG_SQ_DEBUG_MISC_1             0x230A
+
 #endif /* __A200_REG_H */
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 03e447b..ce76a8f 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -728,8 +728,21 @@
 	} else {
 		kgsl_pwrctrl_set_state(device, KGSL_STATE_DUMP_AND_RECOVER);
 		INIT_COMPLETION(device->recovery_gate);
-		/* Detected a hang - trigger an automatic dump */
+		/* Detected a hang */
+
+
+		/*
+		 * Trigger an automatic dump of the state to
+		 * the console
+		 */
 		adreno_postmortem_dump(device, 0);
+
+		/*
+		 * Make a GPU snapshot.  For now, do it after the PM dump so we
+		 * can at least be sure the PM dump will work as it always has
+		 */
+		kgsl_device_snapshot(device, 1);
+
 		result = adreno_recover_hang(device);
 		if (result)
 			kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
@@ -1094,7 +1107,8 @@
 			* get an interrupt */
 			cmds[0] = cp_type3_packet(CP_NOP, 1);
 			cmds[1] = 0;
-			adreno_ringbuffer_issuecmds(device, 0, &cmds[0], 2);
+			adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+				&cmds[0], 2);
 		}
 		mutex_unlock(&device->mutex);
 	}
@@ -1341,6 +1355,7 @@
 	.power_stats = adreno_power_stats,
 	.irqctrl = adreno_irqctrl,
 	.gpuid = adreno_gpuid,
+	.snapshot = adreno_snapshot,
 	/* Optional functions */
 	.setstate = adreno_setstate,
 	.drawctxt_create = adreno_drawctxt_create,
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index a3d2c00..bdba624 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -24,6 +24,7 @@
 		KGSL_CONTAINER_OF(device, struct adreno_device, dev)
 
 /* Flags to control command packet settings */
+#define KGSL_CMD_FLAGS_NONE             0x00000000
 #define KGSL_CMD_FLAGS_PMODE		0x00000001
 #define KGSL_CMD_FLAGS_NO_TS_CMP	0x00000002
 #define KGSL_CMD_FLAGS_NOT_KERNEL_CMD	0x00000004
@@ -84,6 +85,7 @@
 	void (*ctxt_restore)(struct adreno_device *, struct adreno_context *);
 	irqreturn_t (*irq_handler)(struct adreno_device *);
 	void (*irq_control)(struct adreno_device *, int);
+	void * (*snapshot)(struct adreno_device *, void *, int *, int);
 };
 
 extern struct adreno_gpudev adreno_a2xx_gpudev;
@@ -108,6 +110,9 @@
 uint8_t *adreno_convertaddr(struct kgsl_device *device,
 	unsigned int pt_base, unsigned int gpuaddr, unsigned int size);
 
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+		int hang);
+
 static inline int adreno_is_a200(struct adreno_device *adreno_dev)
 {
 	return (adreno_dev->gpurev == ADRENO_REV_A200);
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 16402c3..cf9a60a 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1405,7 +1405,8 @@
 		"active context flags %08x\n", context->flags);
 
 	/* save registers and constants. */
-	adreno_ringbuffer_issuecmds(device, 0, context->reg_save, 3);
+	adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+		context->reg_save, 3);
 
 	if (context->flags & CTXT_FLAGS_SHADER_SAVE) {
 		/* save shader partitioning and instructions. */
@@ -1415,7 +1416,7 @@
 		/* fixup shader partitioning parameter for
 		 *  SET_SHADER_BASES.
 		 */
-		adreno_ringbuffer_issuecmds(device, 0,
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
 			context->shader_fixup, 3);
 
 		context->flags |= CTXT_FLAGS_SHADER_RESTORE;
@@ -1430,7 +1431,7 @@
 			context->context_gmem_shadow.gmem_save, 3);
 
 		/* Restore TP0_CHICKEN */
-		adreno_ringbuffer_issuecmds(device, 0,
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
 			context->chicken_restore, 3);
 
 		context->flags |= CTXT_FLAGS_GMEM_RESTORE;
@@ -1481,7 +1482,7 @@
 	cmds[3] = device->memstore.gpuaddr +
 		KGSL_DEVICE_MEMSTORE_OFFSET(current_context);
 	cmds[4] = (unsigned int) context;
-	adreno_ringbuffer_issuecmds(device, 0, cmds, 5);
+	adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5);
 	kgsl_mmu_setstate(device, context->pagetable);
 
 #ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP
@@ -1498,26 +1499,27 @@
 			context->context_gmem_shadow.gmem_restore, 3);
 
 		/* Restore TP0_CHICKEN */
-		adreno_ringbuffer_issuecmds(device, 0,
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
 			context->chicken_restore, 3);
 
 		context->flags &= ~CTXT_FLAGS_GMEM_RESTORE;
 	}
 
 	/* restore registers and constants. */
-	adreno_ringbuffer_issuecmds(device, 0,
+	adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
 		context->reg_restore, 3);
 
 	/* restore shader instructions & partitioning. */
 	if (context->flags & CTXT_FLAGS_SHADER_RESTORE) {
-		adreno_ringbuffer_issuecmds(device, 0,
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
 			context->shader_restore, 3);
 	}
 
 	if (adreno_is_a20x(adreno_dev)) {
 		cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1);
 		cmds[1] = context->bin_base_offset;
-		adreno_ringbuffer_issuecmds(device, 0, cmds, 2);
+		adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
+			cmds, 2);
 	}
 }
 
@@ -1704,6 +1706,10 @@
 	wmb();
 }
 
+/* Defined in adreno_a2xx_snapshot.c */
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang);
+
 struct adreno_gpudev adreno_a2xx_gpudev = {
 	.ctxt_gpustate_shadow = a2xx_ctxt_gpustate_shadow,
 	.ctxt_gmem_shadow = a2xx_ctxt_gmem_shadow,
@@ -1711,4 +1717,5 @@
 	.ctxt_restore = a2xx_ctxt_restore,
 	.irq_handler = a2xx_irq_handler,
 	.irq_control = a2xx_irq_control,
+	.snapshot = a2xx_snapshot,
 };
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
new file mode 100644
index 0000000..30d692b
--- /dev/null
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -0,0 +1,356 @@
+/* Copyright (c) 2012, 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.
+ *
+ */
+
+#include "kgsl.h"
+#include "adreno.h"
+#include "kgsl_snapshot.h"
+
+#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+		+ sizeof(struct kgsl_snapshot_debug))
+
+/* Dump the SX debug registers into a GPU snapshot debug section */
+
+#define SXDEBUG_COUNT 0x1B
+
+static int a2xx_snapshot_sxdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(SXDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "SX DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SX;
+	header->size = SXDEBUG_COUNT;
+
+	for (i = 0; i < SXDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(SXDEBUG_COUNT);
+}
+
+#define CPDEBUG_COUNT 0x20
+
+static int a2xx_snapshot_cpdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(CPDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "CP DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_CP;
+	header->size = CPDEBUG_COUNT;
+
+	for (i = 0; i < CPDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(CPDEBUG_COUNT);
+}
+
+/*
+ * The contents of the SQ debug sections are dword pairs:
+ * [register offset]:[value]
+ * This macro writes both dwords for the given register
+ */
+
+#define SQ_DEBUG_WRITE(_device, _reg, _data, _offset) \
+	do { _data[(_offset)++] = (_reg); \
+	adreno_regread(_device, (_reg), &_data[(_offset)++]); } while (0)
+
+#define SQ_DEBUG_BANK_SIZE 23
+
+static int a2xx_snapshot_sqdebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, offset = 0;
+	int size = SQ_DEBUG_BANK_SIZE * 2 * 2;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "SQ Debug");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SQ;
+	header->size = size;
+
+	for (i = 0; i < 2; i++) {
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_CONST_MGR_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_EXP_ALLOC+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_PIX+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_VTX+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_INPUT_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_2+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device,
+			REG_SQ_DEBUG_PIX_TB_STATUS_REG_3+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PTR_BUFF+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TB_STATUS_SEL+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TP_FSM+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_0+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_1+i*0x1000,
+			data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM+i*0x1000,
+			data, offset);
+	}
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define SQ_DEBUG_THREAD_SIZE 7
+
+static int a2xx_snapshot_sqthreaddebug(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i, offset = 0;
+	int size = SQ_DEBUG_THREAD_SIZE * 2 * 16;
+
+	if (remain < DEBUG_SECTION_SZ(size)) {
+		SNAPSHOT_ERR_NOMEM(device, "SQ THREAD DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_SQTHREAD;
+	header->size = size;
+
+	for (i = 0; i < 16; i++) {
+		adreno_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL,
+				i | (6<<4) | (i<<7) | (1<<11) | (1<<12)
+				| (i<<16) | (6<<20) | (i<<23));
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATUS_REG,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_0,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_1,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_2,
+			 data, offset);
+		SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_3,
+			 data, offset);
+	}
+
+	return DEBUG_SECTION_SZ(size);
+}
+
+#define MIUDEBUG_COUNT 0x10
+
+static int a2xx_snapshot_miudebug(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < DEBUG_SECTION_SZ(MIUDEBUG_COUNT)) {
+		SNAPSHOT_ERR_NOMEM(device, "MIU DEBUG");
+		return 0;
+	}
+
+	header->type = SNAPSHOT_DEBUG_MIU;
+	header->size = MIUDEBUG_COUNT;
+
+	for (i = 0; i < MIUDEBUG_COUNT; i++) {
+		adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i);
+		adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]);
+	}
+
+	adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0);
+
+	return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
+}
+
+/* Helper function to snapshot a section of indexed registers */
+
+static void *a2xx_snapshot_indexed_registers(struct kgsl_device *device,
+		void *snapshot, int *remain,
+		unsigned int index, unsigned int data, unsigned int start,
+		unsigned int count)
+{
+	struct kgsl_snapshot_indexed_registers iregs;
+	iregs.index = index;
+	iregs.data = data;
+	iregs.start = start;
+	iregs.count = count;
+
+	return kgsl_snapshot_add_section(device,
+		 KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot,
+		 remain, kgsl_snapshot_dump_indexed_regs, &iregs);
+}
+
+/* A2XX GPU snapshot function - this is where all of the A2XX specific
+ * bits and pieces are grabbed into the snapshot memory
+ */
+
+void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot,
+	int *remain, int hang)
+{
+	struct kgsl_device *device = &adreno_dev->dev;
+	struct kgsl_snapshot_registers regs;
+	unsigned int pmoverride;
+
+	/* Choose the register set to dump */
+
+	if (adreno_is_a20x(adreno_dev)) {
+		regs.regs = (unsigned int *) a200_registers;
+		regs.count = a200_registers_count;
+	} else {
+		regs.regs = (unsigned int *) a220_registers;
+		regs.count = a220_registers_count;
+	}
+
+	/* Master set of (non debug) registers */
+	snapshot = kgsl_snapshot_add_section(device,
+		KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
+		kgsl_snapshot_dump_regs, &regs);
+
+	/* CP_STATE_DEBUG indexed registers */
+	snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_STATE_DEBUG_INDEX,
+			REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+
+	/* CP_ME indexed registers */
+	snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS,
+			64, 44);
+
+	/*
+	 * Need to temporarily turn off clock gating for the debug bus to
+	 * work
+	 */
+
+	adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride);
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF);
+
+	/* SX debug registers */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_sxdebug, NULL);
+
+	/* SU debug indexed registers (only for < 470) */
+	if (!adreno_is_a22x(adreno_dev))
+		snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+				remain, REG_PA_SU_DEBUG_CNTL,
+				REG_PA_SU_DEBUG_DATA,
+				0, 0x1B);
+
+	/* CP debug registers */
+	snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_cpdebug, NULL);
+
+	/* MH debug indexed registers */
+	snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, MH_DEBUG_CTRL, MH_DEBUG_DATA, 0x0, 0x40);
+
+	/* Leia only register sets */
+	if (adreno_is_a22x(adreno_dev)) {
+		/* RB DEBUG indexed regisers */
+		snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA, 0, 8);
+
+		/* RB DEBUG indexed registers bank 2 */
+		snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA + 0x1000,
+			0, 8);
+
+		/* PC_DEBUG indexed registers */
+		snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_PC_DEBUG_CNTL, REG_PC_DEBUG_DATA, 0, 8);
+
+		/* GRAS_DEBUG indexed registers */
+		snapshot = a2xx_snapshot_indexed_registers(device, snapshot,
+			remain, REG_GRAS_DEBUG_CNTL, REG_GRAS_DEBUG_DATA, 0, 4);
+
+		/* MIU debug registers */
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_miudebug, NULL);
+
+		/* SQ DEBUG debug registers */
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+			a2xx_snapshot_sqdebug, NULL);
+
+		/*
+		 * Reading SQ THREAD causes bad things to happen on a running
+		 * system, so only read it if the GPU is already hung
+		 */
+
+		if (hang) {
+			/* SQ THREAD debug registers */
+			snapshot = kgsl_snapshot_add_section(device,
+				KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+				a2xx_snapshot_sqthreaddebug, NULL);
+		}
+	}
+
+	/* Reset the clock gating */
+	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
+
+	return snapshot;
+}
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
new file mode 100644
index 0000000..fb88a72
--- /dev/null
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#include "kgsl.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+#include "adreno.h"
+#include "adreno_pm4types.h"
+#include "a2xx_reg.h"
+
+/* Number of dwords of ringbuffer history to record */
+#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
+
+/* Maintain a list of the objects we see during parsing */
+
+#define SNAPSHOT_OBJ_BUFSIZE 64
+
+#define SNAPSHOT_OBJ_TYPE_IB 0
+
+static struct kgsl_snapshot_obj {
+	int type;
+	uint32_t gpuaddr;
+	uint32_t ptbase;
+	void *ptr;
+	int dwords;
+} objbuf[SNAPSHOT_OBJ_BUFSIZE];
+
+/* Pointer to the next open entry in the object list */
+static int objbufptr;
+
+/* Push a new buffer object onto the list */
+static void push_object(struct kgsl_device *device, int type, uint32_t ptbase,
+	uint32_t gpuaddr, int dwords)
+{
+	int index;
+	void *ptr;
+
+	/* Go through the list and see that object has already been seen */
+	for (index = 0; index < objbufptr; index++) {
+		if (objbuf[index].gpuaddr == gpuaddr &&
+			objbuf[index].ptbase == ptbase)
+			return;
+	}
+
+	if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) {
+		KGSL_DRV_ERR(device, "snapshot: too many snapshot objects\n");
+		return;
+	}
+
+	/*
+	 * adreno_convertaddr verifies that the IB size is valid - at least in
+	 * the context of it being smaller then the allocated memory space
+	 */
+	ptr = adreno_convertaddr(device, ptbase, gpuaddr, dwords << 2);
+
+	if (ptr == NULL) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Can't find GPU address for %x\n", gpuaddr);
+		return;
+	}
+
+	/* Put it on the list of things to parse */
+	objbuf[objbufptr].type = type;
+	objbuf[objbufptr].gpuaddr = gpuaddr;
+	objbuf[objbufptr].ptbase = ptbase;
+	objbuf[objbufptr].dwords = dwords;
+	objbuf[objbufptr++].ptr = ptr;
+}
+
+/* Snapshot the istore memory */
+static int snapshot_istore(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_istore *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int count, i;
+
+	count = adreno_dev->istore_size * ADRENO_ISTORE_WORDS;
+
+	if (remain < (count * 4) + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the istore section");
+		return 0;
+	}
+
+	header->count = adreno_dev->istore_size;
+
+	for (i = 0; i < count; i++)
+		kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+	return (count * 4) + sizeof(*header);
+}
+
+/* Snapshot the ringbuffer memory */
+static int snapshot_rb(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_rb *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
+	unsigned int rbbase, ptbase, rptr, *rbptr;
+	int start, stop, index;
+	int numitems, size;
+
+	/* Get the GPU address of the ringbuffer */
+	kgsl_regread(device, REG_CP_RB_BASE, &rbbase);
+
+	/* Get the physical address of the MMU pagetable */
+	ptbase = kgsl_mmu_get_current_ptbase(device);
+
+	/* Get the current read pointers for the RB */
+	kgsl_regread(device, REG_CP_RB_RPTR, &rptr);
+
+	/* start the dump at the rptr minus some history */
+	start = (int) rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY;
+	if (start < 0)
+		start += rb->sizedwords;
+
+	/*
+	 * Stop the dump at the point where the software last wrote.  Don't use
+	 * the hardware value here on the chance that it didn't get properly
+	 * updated
+	 */
+
+	stop = (int) rb->wptr + 16;
+	if (stop > rb->sizedwords)
+		stop -= rb->sizedwords;
+
+	/* Set up the header for the section */
+
+	numitems = (stop > start) ? stop - start :
+		(rb->sizedwords - start) + stop;
+
+	size = (numitems << 2);
+
+	if (remain < size + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the rb section");
+		return 0;
+	}
+
+	/* Write the sub-header for the section */
+	header->start = start;
+	header->end = stop;
+	header->wptr = rb->wptr;
+	header->rbsize = rb->sizedwords;
+	header->count = numitems;
+
+	index = start;
+	rbptr = rb->buffer_desc.hostptr;
+
+	/*
+	 * Loop through the RB, copying the data and looking for indirect
+	 * buffers and MMU pagetable changes
+	 */
+
+	while (index != rb->wptr) {
+		*data = rbptr[index];
+
+		if (rbptr[index] == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2))
+			push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+				rbptr[index + 1], rbptr[index + 2]);
+
+		/*
+		 * FIXME: Handle upcoming MMU pagetable changes, but only
+		 * between the rptr and the wptr
+		 */
+
+		index = index + 1;
+
+		if (index == rb->sizedwords)
+			index = 0;
+
+		data++;
+	}
+
+	/* Dump 16 dwords past the wptr, but don't  bother interpeting it */
+
+	while (index != stop) {
+		*data = rbptr[index];
+		index = index + 1;
+
+		if (index == rb->sizedwords)
+			index = 0;
+
+		data++;
+	}
+
+	/* Return the size of the section */
+	return size + sizeof(*header);
+}
+
+/* Snapshot the memory for an indirect buffer */
+static int snapshot_ib(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_ib *header = snapshot;
+	struct kgsl_snapshot_obj *obj = priv;
+	unsigned int *src = obj->ptr;
+	unsigned int *dst = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < (obj->dwords << 2) + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the ib section");
+		return 0;
+	}
+
+	/* Write the sub-header for the section */
+	header->gpuaddr = obj->gpuaddr;
+	header->ptbase = obj->ptbase;
+	header->size = obj->dwords;
+
+	/* Write the contents of the ib */
+	for (i = 0; i < obj->dwords; i++) {
+		*dst = *src;
+		/* If another IB is discovered, then push it on the list too */
+
+		if (*src == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) {
+			push_object(device, SNAPSHOT_OBJ_TYPE_IB, obj->ptbase,
+				*(src + 1), *(src + 2));
+		}
+
+		src++;
+		dst++;
+	}
+
+	return (obj->dwords << 2) + sizeof(*header);
+}
+
+/* Dump another item on the current pending list */
+static void *dump_object(struct kgsl_device *device, int obj, void *snapshot,
+	int *remain)
+{
+	switch (objbuf[obj].type) {
+	case SNAPSHOT_OBJ_TYPE_IB:
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_IB, snapshot, remain,
+			snapshot_ib, &objbuf[obj]);
+		break;
+	default:
+		KGSL_DRV_ERR(device,
+			"snapshot: Invalid snapshot object type: %d\n",
+			objbuf[obj].type);
+		break;
+	}
+
+	return snapshot;
+}
+
+/* adreno_snapshot - Snapshot the Adreno GPU state
+ * @device - KGSL device to snapshot
+ * @snapshot - Pointer to the start of memory to write into
+ * @remain - A pointer to how many bytes of memory are remaining in the snapshot
+ * @hang - set if this snapshot was automatically triggered by a GPU hang
+ * This is a hook function called by kgsl_snapshot to snapshot the
+ * Adreno specific information for the GPU snapshot.  In turn, this function
+ * calls the GPU specific snapshot function to get core specific information.
+ */
+
+void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
+		int hang)
+{
+	int i;
+	uint32_t ptbase, ibbase, ibsize;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	/* Reset the list of objects */
+	objbufptr = 0;
+
+	/* Get the physical address of the MMU pagetable */
+	ptbase = kgsl_mmu_get_current_ptbase(device);
+
+	/* Dump the ringbuffer */
+	snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB,
+		snapshot, remain, snapshot_rb, NULL);
+
+	/*
+	 * Make sure that the IBs described in the CP registers are on the
+	 * list of objects
+	 */
+	kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
+	kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
+
+	if (ibsize)
+		push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+			ibbase, ibsize);
+
+	kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+	kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
+
+	if (ibsize)
+		push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
+			ibbase, ibsize);
+
+	/*
+	 * Go through the list of found objects and dump each one.  As the IBs
+	 * are parsed, more objects might be found, and objbufptr will increase
+	 */
+	for (i = 0; i < objbufptr; i++)
+		snapshot = dump_object(device, i, snapshot, remain);
+
+	/*
+	 * Only dump the istore on a hang - reading it on a running system
+	 * has a non 0 chance of hanging the GPU
+	 */
+
+	if (hang) {
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+			snapshot_istore, NULL);
+	}
+
+	/* Add GPU specific sections - registers mainly, but other stuff too */
+	if (adreno_dev->gpudev->snapshot)
+		snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
+			remain, hang);
+
+	return snapshot;
+}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index c6e78dd..1b6696b 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2097,6 +2097,8 @@
 	if (minor == KGSL_DEVICE_MAX)
 		return;
 
+	kgsl_device_snapshot_close(device);
+
 	kgsl_cffdump_close(device->id);
 	kgsl_pwrctrl_uninit_sysfs(device);
 
@@ -2199,6 +2201,9 @@
 
 	idr_init(&device->context_idr);
 
+	/* Initalize the snapshot engine */
+	kgsl_device_snapshot_init(device);
+
 	/* sysfs and debugfs initalization - failure here is non fatal */
 
 	/* Initialize logging */
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index f82b038..8ea5279 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-2012, 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
@@ -90,6 +90,8 @@
 		struct kgsl_power_stats *stats);
 	void (*irqctrl)(struct kgsl_device *device, int state);
 	unsigned int (*gpuid)(struct kgsl_device *device);
+	void * (*snapshot)(struct kgsl_device *device, void *snapshot,
+		int *remain, int hang);
 	/* Optional functions - these functions are not mandatory.  The
 	   driver will check that the function pointer is not NULL before
 	   calling the hook */
@@ -164,6 +166,15 @@
 	struct idr context_idr;
 	struct early_suspend display_off;
 
+	void *snapshot;		/* Pointer to the snapshot memory region */
+	int snapshot_maxsize;   /* Max size of the snapshot region */
+	int snapshot_size;      /* Current size of the snapshot region */
+	u32 snapshot_timestamp;	/* Timestamp of the last valid snapshot */
+	int snapshot_frozen;	/* 1 if the snapshot output is frozen until
+				   it gets read by the user.  This avoids
+				   losing the output on multiple hangs  */
+	struct kobject snapshot_kobj;
+
 	/* Logging levels */
 	int cmd_log;
 	int ctxt_log;
@@ -320,4 +331,8 @@
 
 const char *kgsl_pwrstate_to_str(unsigned int state);
 
+int kgsl_device_snapshot_init(struct kgsl_device *device);
+int kgsl_device_snapshot(struct kgsl_device *device, int hang);
+void kgsl_device_snapshot_close(struct kgsl_device *device);
+
 #endif  /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
new file mode 100644
index 0000000..72df148b
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -0,0 +1,484 @@
+/* Copyright (c) 2012, 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.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/sysfs.h>
+#include <linux/utsname.h>
+#include <linux/sched.h>
+#include <linux/idr.h>
+
+#include "kgsl.h"
+#include "kgsl_log.h"
+#include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
+#include "kgsl_snapshot.h"
+
+/* idr_for_each function to count the number of contexts */
+
+static int snapshot_context_count(int id, void *ptr, void *data)
+{
+	int *count = data;
+	*count = *count + 1;
+
+	return 0;
+}
+
+/*
+ * To simplify the iterator loop use a global pointer instead of trying
+ * to pass around double star references to the snapshot data
+ */
+
+static void *_ctxtptr;
+
+static int snapshot_context_info(int id, void *ptr, void *data)
+{
+	struct kgsl_snapshot_linux_context *header = _ctxtptr;
+	struct kgsl_context *context = ptr;
+	struct kgsl_device *device = context->dev_priv->device;
+
+	header->id = id;
+
+	/* Future-proof for per-context timestamps - for now, just
+	 * return the global timestamp for all contexts
+	 */
+
+	header->timestamp_queued = -1;
+	header->timestamp_retired = device->ftbl->readtimestamp(device,
+		KGSL_TIMESTAMP_RETIRED);
+
+	_ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
+
+	return 0;
+}
+
+/* Snapshot the Linux specific information */
+static int snapshot_os(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_linux *header = snapshot;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct task_struct *task;
+	pid_t pid;
+	int hang = (int) priv;
+	int ctxtcount = 0;
+	int size = sizeof(*header);
+
+	/* Figure out how many active contexts there are - these will
+	 * be appended on the end of the structure */
+
+	idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
+
+	size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
+
+	/* Make sure there is enough room for the data */
+	if (remain < size) {
+		SNAPSHOT_ERR_NOMEM(device, "OS");
+		return 0;
+	}
+
+	memset(header, 0, sizeof(*header));
+
+	header->osid = KGSL_SNAPSHOT_OS_LINUX;
+
+	header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING;
+
+	/* Get the kernel build information */
+	strlcpy(header->release, utsname()->release, sizeof(header->release));
+	strlcpy(header->version, utsname()->version, sizeof(header->version));
+
+	/* Get the Unix time for the timestamp */
+	header->seconds = get_seconds();
+
+	/* Remember the power information */
+	header->power_flags = pwr->power_flags;
+	header->power_level = pwr->active_pwrlevel;
+	header->power_interval_timeout = pwr->interval_timeout;
+	header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
+	header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
+
+	/* Future proof for per-context timestamps */
+	header->current_context = -1;
+
+	/* Get the current PT base */
+	header->ptbase = kgsl_mmu_get_current_ptbase(device);
+	/* And the PID for the task leader */
+	pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(header->ptbase);
+
+	task = find_task_by_vpid(pid);
+
+	if (task)
+		get_task_comm(header->comm, task);
+
+	header->ctxtcount = ctxtcount;
+
+	/* append information for each context */
+	_ctxtptr = snapshot + sizeof(*header);
+	idr_for_each(&device->context_idr, snapshot_context_info, NULL);
+
+	/* Return the size of the data segment */
+	return size;
+}
+/*
+ * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_indexed_registers data
+ *
+ * Given a indexed register cmd/data pair and a count, dump each indexed
+ * register
+ */
+
+int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv)
+{
+	struct kgsl_snapshot_indexed_registers *iregs = priv;
+	struct kgsl_snapshot_indexed_regs *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	int i;
+
+	if (remain < (iregs->count * 4) + sizeof(*header)) {
+		SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS");
+		return 0;
+	}
+
+	header->index_reg = iregs->index;
+	header->data_reg = iregs->data;
+	header->count = iregs->count;
+	header->start = iregs->start;
+
+	for (i = 0; i < iregs->count; i++) {
+		kgsl_regwrite(device, iregs->index, iregs->start + i);
+		kgsl_regread(device, iregs->data, &data[i]);
+	}
+
+	return (iregs->count * 4) + sizeof(*header);
+}
+EXPORT_SYMBOL(kgsl_snapshot_dump_indexed_regs);
+
+/*
+ * kgsl_snapshot_dump_regs - helper function to dump device registers
+ * @device - the device to dump registers from
+ * @snapshot - pointer to the start of the region of memory for the snapshot
+ * @remain - a pointer to the number of bytes remaining in the snapshot
+ * @priv - A pointer to the kgsl_snapshot_registers data
+ *
+ * Given an array of register ranges pairs (start,end [inclusive]), dump the
+ * registers into a snapshot register section.  The snapshot region stores a
+ * part of dwords for each register - the word address of the register, and
+ * the value.
+ */
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_regs *header = snapshot;
+	struct kgsl_snapshot_registers *regs = priv;
+	unsigned int *data = snapshot + sizeof(*header);
+	int count = 0, i, j;
+
+	/* Figure out how many registers we are going to dump */
+
+	for (i = 0; i < regs->count; i++) {
+		int start = regs->regs[i * 2];
+		int end = regs->regs[i * 2 + 1];
+
+		count += (end - start + 1);
+	}
+
+	if (remain < (count * 8) + sizeof(*header)) {
+		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+		return 0;
+	}
+
+	for (i = 0; i < regs->count; i++) {
+		unsigned int start = regs->regs[i * 2];
+		unsigned int end = regs->regs[i * 2 + 1];
+
+		for (j = start; j <= end; j++) {
+			unsigned int val;
+
+			kgsl_regread(device, j, &val);
+			*data++ = j;
+			*data++ = val;
+		}
+	}
+
+	header->count = count;
+
+	/* Return the size of the section */
+	return (count * 8) + sizeof(*header);
+}
+EXPORT_SYMBOL(kgsl_snapshot_dump_regs);
+
+/*
+ * kgsl_snapshot - construct a device snapshot
+ * @device - device to snapshot
+ * @hang - set to 1 if the snapshot was triggered following a hnag
+ * Given a device, construct a binary snapshot dump of the current device state
+ * and store it in the device snapshot memory.
+ */
+int kgsl_device_snapshot(struct kgsl_device *device, int hang)
+{
+	struct kgsl_snapshot_header *header = device->snapshot;
+	int remain = device->snapshot_maxsize - sizeof(*header);
+	void *snapshot;
+
+	/*
+	 * The first hang is always the one we are interested in. To
+	 * avoid a subsequent hang blowing away the first, the snapshot
+	 * is frozen until it is dumped via sysfs.
+	 *
+	 * Note that triggered snapshots are always taken regardless
+	 * of the state and never frozen.
+	 */
+
+	if (hang && device->snapshot_frozen == 1)
+		return 0;
+
+	if (device->snapshot == NULL) {
+		KGSL_DRV_ERR(device,
+			"snapshot: No snapshot memory available\n");
+		return -ENOMEM;
+	}
+
+	if (remain < sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the header\n");
+		return -ENOMEM;
+	}
+
+	header->magic = SNAPSHOT_MAGIC;
+
+	header->gpuid = kgsl_gpuid(device);
+
+	/* Get a pointer to the first section (right after the header) */
+	snapshot = ((void *) device->snapshot) + sizeof(*header);
+
+	/* Build the Linux specific header */
+	snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
+		snapshot, &remain, snapshot_os, (void *) hang);
+
+	/* Get the device specific sections */
+	if (device->ftbl->snapshot)
+		snapshot = device->ftbl->snapshot(device, snapshot, &remain,
+			hang);
+
+	/* Add the empty end section to let the parser know we are done */
+	snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_END,
+		snapshot, &remain, NULL, NULL);
+
+	device->snapshot_timestamp = get_seconds();
+	device->snapshot_size = (int) (snapshot - device->snapshot);
+
+	/* Freeze the snapshot on a hang until it gets read */
+	device->snapshot_frozen = (hang) ? 1 : 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot);
+
+/* An attribute for showing snapshot details */
+struct kgsl_snapshot_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct kgsl_device *device, char *buf);
+	ssize_t (*store)(struct kgsl_device *device, const char *buf,
+		size_t count);
+};
+
+#define to_snapshot_attr(a) \
+container_of(a, struct kgsl_snapshot_attribute, attr)
+
+#define kobj_to_device(a) \
+container_of(a, struct kgsl_device, snapshot_kobj)
+
+/* Dump the sysfs binary data to the user */
+static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
+	struct bin_attribute *attr, char *buf, loff_t off,
+	size_t count)
+{
+	struct kgsl_device *device = kobj_to_device(kobj);
+
+	if (device == NULL)
+		return 0;
+
+	/* Return nothing if we haven't taken a snapshot yet */
+	if (device->snapshot_timestamp == 0)
+		return 0;
+
+	/* Get the mutex to keep things from changing while we are dumping */
+	mutex_lock(&device->mutex);
+
+	/*
+	 * Release the freeze on the snapshot the first time the buffer is read
+	 */
+
+	device->snapshot_frozen = 0;
+
+	if (off >= device->snapshot_size) {
+		count = 0;
+		goto exit;
+	}
+
+	if (off + count > device->snapshot_size)
+		count = device->snapshot_size - off;
+
+	memcpy(buf, device->snapshot + off, count);
+
+exit:
+	mutex_unlock(&device->mutex);
+	return count;
+}
+
+/* Show the timestamp of the last collected snapshot */
+static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp);
+}
+
+/* manually trigger a new snapshot to be collected */
+static ssize_t trigger_store(struct kgsl_device *device, const char *buf,
+	size_t count)
+{
+	if (device && count > 0) {
+		mutex_lock(&device->mutex);
+		kgsl_device_snapshot(device, 0);
+		mutex_unlock(&device->mutex);
+	}
+
+	return count;
+}
+
+static struct bin_attribute snapshot_attr = {
+	.attr.name = "dump",
+	.attr.mode = 0444,
+	.size = 0,
+	.read = snapshot_show
+};
+
+#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \
+struct kgsl_snapshot_attribute attr_##_name = { \
+	.attr = { .name = __stringify(_name), .mode = _mode }, \
+	.show = _show, \
+	.store = _store, \
+}
+
+SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store);
+SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
+
+static void snapshot_sysfs_release(struct kobject *kobj)
+{
+}
+
+static ssize_t snapshot_sysfs_show(struct kobject *kobj,
+	struct attribute *attr, char *buf)
+{
+	struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+	struct kgsl_device *device = kobj_to_device(kobj);
+	ssize_t ret;
+
+	if (device && pattr->show)
+		ret = pattr->show(device, buf);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static ssize_t snapshot_sysfs_store(struct kobject *kobj,
+	struct attribute *attr, const char *buf, size_t count)
+{
+	struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr);
+	struct kgsl_device *device = kobj_to_device(kobj);
+	ssize_t ret;
+
+	if (device && pattr->store)
+		ret = pattr->store(device, buf, count);
+	else
+		ret = -EIO;
+
+	return ret;
+}
+
+static const struct sysfs_ops snapshot_sysfs_ops = {
+	.show = snapshot_sysfs_show,
+	.store = snapshot_sysfs_store,
+};
+
+static struct kobj_type ktype_snapshot = {
+	.sysfs_ops = &snapshot_sysfs_ops,
+	.default_attrs = NULL,
+	.release = snapshot_sysfs_release,
+};
+
+/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot
+ * @device - The device to initalize
+ *
+ * Allocate memory for a GPU snapshot for the specified device,
+ * and create the sysfs files to manage it
+ */
+
+int kgsl_device_snapshot_init(struct kgsl_device *device)
+{
+	int ret;
+
+	if (device->snapshot == NULL)
+		device->snapshot = vmalloc(KGSL_SNAPSHOT_MEMSIZE);
+
+	if (device->snapshot == NULL)
+		return -ENOMEM;
+
+	device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE;
+	device->snapshot_timestamp = 0;
+
+	ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
+		&device->dev->kobj, "snapshot");
+	if (ret)
+		goto done;
+
+	ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr);
+	if (ret)
+		goto done;
+
+	ret  = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr);
+	if (ret)
+		goto done;
+
+	ret  = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_init);
+
+/* kgsl_device_snapshot_close - Take down snapshot memory for a device
+ * @device - Pointer to the kgsl_device
+ *
+ * Remove the sysfs files and free the memory allocated for the GPU
+ * snapshot
+ */
+
+void kgsl_device_snapshot_close(struct kgsl_device *device)
+{
+	sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
+	sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr);
+	sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr);
+
+	kobject_put(&device->snapshot_kobj);
+
+	vfree(device->snapshot);
+
+	device->snapshot = NULL;
+	device->snapshot_maxsize = 0;
+	device->snapshot_timestamp = 0;
+}
+EXPORT_SYMBOL(kgsl_device_snapshot_close);
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
new file mode 100644
index 0000000..4fdc8a1
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -0,0 +1,259 @@
+/* Copyright (c) 2012, 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 _KGSL_SNAPSHOT_H_
+#define _KGSL_SNAPSHOT_H_
+
+#include <linux/types.h>
+
+/* Snapshot header */
+
+#define SNAPSHOT_MAGIC 0x504D0001
+
+/* GPU ID scheme:
+ * [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
+ * [00:16] - GPU specific identifier
+ */
+
+struct kgsl_snapshot_header {
+	__u32 magic; /* Magic identifier */
+	__u32 gpuid; /* GPU ID - see above */
+} __packed;
+
+/* Section header */
+#define SNAPSHOT_SECTION_MAGIC 0xABCD
+
+struct kgsl_snapshot_section_header {
+	__u16 magic; /* Magic identifier */
+	__u16 id;    /* Type of section */
+	__u32 size;  /* Size of the section including this header */
+} __packed;
+
+/* Section identifiers */
+#define KGSL_SNAPSHOT_SECTION_OS           0x0101
+#define KGSL_SNAPSHOT_SECTION_REGS         0x0201
+#define KGSL_SNAPSHOT_SECTION_RB           0x0301
+#define KGSL_SNAPSHOT_SECTION_IB           0x0401
+#define KGSL_SNAPSHOT_SECTION_INDEXED_REGS 0x0501
+#define KGSL_SNAPSHOT_SECTION_ISTORE       0x0801
+#define KGSL_SNAPSHOT_SECTION_DEBUG        0x0901
+#define KGSL_SNAPSHOT_SECTION_END          0xFFFF
+
+/* OS sub-section header */
+#define KGSL_SNAPSHOT_OS_LINUX             0x0001
+
+/* Linux OS specific information */
+
+#define SNAPSHOT_STATE_HUNG 0
+#define SNAPSHOT_STATE_RUNNING 1
+
+struct kgsl_snapshot_linux {
+	int osid;                   /* subsection OS identifier */
+	int state;		    /* 1 if the thread is running, 0 for hung */
+	__u32 seconds;		    /* Unix timestamp for the snapshot */
+	__u32 power_flags;            /* Current power flags */
+	__u32 power_level;            /* Current power level */
+	__u32 power_interval_timeout; /* Power interval timeout */
+	__u32 grpclk;                 /* Current GP clock value */
+	__u32 busclk;		    /* Current busclk value */
+	__u32 ptbase;		    /* Current ptbase */
+	__u32 pid;		    /* PID of the process that owns the PT */
+	__u32 current_context;	    /* ID of the current context */
+	__u32 ctxtcount;	    /* Number of contexts appended to section */
+	unsigned char release[32];  /* kernel release */
+	unsigned char version[32];  /* kernel version */
+	unsigned char comm[16];	    /* Name of the process that owns the PT */
+} __packed;
+
+/*
+ * This structure contains a record of an active context.
+ * These are appended one after another in the OS section below
+ * the header above
+ */
+
+struct kgsl_snapshot_linux_context {
+	__u32 id;			/* The context ID */
+	__u32 timestamp_queued;		/* The last queued timestamp */
+	__u32 timestamp_retired;	/* The last timestamp retired by HW */
+};
+
+/* Ringbuffer sub-section header */
+struct kgsl_snapshot_rb {
+	int start;  /* dword at the start of the dump */
+	int end;    /* dword at the end of the dump */
+	int rbsize; /* Size (in dwords) of the ringbuffer */
+	int wptr;   /* Current index of the CPU write pointer */
+	int rptr;   /* Current index of the GPU read pointer */
+	int count;  /* Number of dwords in the dump */
+} __packed;
+
+/* Indirect buffer sub-section header */
+struct kgsl_snapshot_ib {
+	__u32 gpuaddr; /* GPU address of the the IB */
+	__u32 ptbase;  /* Base for the pagetable the GPU address is valid in */
+	int size;    /* Size of the IB */
+} __packed;
+
+/* Register sub-section header */
+struct kgsl_snapshot_regs {
+	__u32 count; /* Number of register pairs in the section */
+} __packed;
+
+/* Indexed register sub-section header */
+struct kgsl_snapshot_indexed_regs {
+	__u32 index_reg; /* Offset of the index register for this section */
+	__u32 data_reg;  /* Offset of the data register for this section */
+	int start;     /* Starting index */
+	int count;     /* Number of dwords in the data */
+} __packed;
+
+/* Istore sub-section header */
+struct kgsl_snapshot_istore {
+	int count;   /* Number of instructions in the istore */
+} __packed;
+
+/* Debug data sub-section header */
+
+#define SNAPSHOT_DEBUG_SX         1
+#define SNAPSHOT_DEBUG_CP         2
+#define SNAPSHOT_DEBUG_SQ         3
+#define SNAPSHOT_DEBUG_SQTHREAD   4
+#define SNAPSHOT_DEBUG_MIU        5
+
+struct kgsl_snapshot_debug {
+	int type;    /* Type identifier for the attached tata */
+	int size;   /* Size of the section in bytes */
+} __packed;
+
+#ifdef __KERNEL__
+
+/* Allocate 512K for each device snapshot */
+#define KGSL_SNAPSHOT_MEMSIZE (512 * 1024)
+
+struct kgsl_device;
+/*
+ * A helper macro to print out "not enough memory functions" - this
+ * makes it easy to standardize the messages as well as cut down on
+ * the number of strings in the binary
+ */
+
+#define SNAPSHOT_ERR_NOMEM(_d, _s) \
+	KGSL_DRV_ERR((_d), \
+	"snapshot: not enough snapshot memory for section %s\n", (_s))
+
+/*
+ * kgsl_snapshot_add_section - Add a new section to the GPU snapshot
+ * @device - the KGSL device being snapshotted
+ * @id - the section id
+ * @snapshot - pointer to the memory for the snapshot
+ * @remain - pointer to the number of bytes left in the snapshot region
+ * @func - Function pointer to fill the section
+ * @priv - Priv pointer to pass to the function
+ *
+ * Set up a KGSL snapshot header by filling the memory with the callback
+ * function and adding the standard section header
+ */
+
+static inline void *kgsl_snapshot_add_section(struct kgsl_device *device,
+	u16 id, void *snapshot, int *remain,
+	int (*func)(struct kgsl_device *, void *, int, void *), void *priv)
+{
+	struct kgsl_snapshot_section_header *header = snapshot;
+	void *data = snapshot + sizeof(*header);
+	int ret = 0;
+
+	/*
+	 * Sanity check to make sure there is enough for the header.  The
+	 * callback will check to make sure there is enough for the rest
+	 * of the data.  If there isn't enough room then don't advance the
+	 * pointer.
+	 */
+
+	if (*remain < sizeof(*header))
+		return snapshot;
+
+	/* It is legal to have no function (i.e. - make an empty section) */
+
+	if (func) {
+		ret = func(device, data, *remain, priv);
+
+		/*
+		 * If there wasn't enough room for the data then don't bother
+		 * setting up the header.
+		 */
+
+		if (ret == 0)
+			return snapshot;
+	}
+
+	header->magic = SNAPSHOT_SECTION_MAGIC;
+	header->id = id;
+	header->size = ret + sizeof(*header);
+
+	/* Decrement the room left in the snapshot region */
+	*remain -= header->size;
+	/* Advance the pointer to the end of the next function */
+	return snapshot + header->size;
+}
+
+/* A common helper function to dump a range of registers.  This will be used in
+ * the GPU specific devices like this:
+ *
+ * struct kgsl_snapshot_registers priv;
+ * priv.regs = registers_array;;
+ * priv.count = num_registers;
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
+ *	remain, kgsl_snapshot_dump_regs, &priv).
+ *
+ * Pass in an array of register range pairs in the form of:
+ * start reg, stop reg
+ * All the registers between start and stop inclusive will be dumped
+ */
+
+struct kgsl_snapshot_registers {
+	unsigned int *regs;  /* Pointer to the array of register ranges */
+	int count;	     /* Number of entries in the array */
+};
+
+int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv);
+
+/*
+ * A common helper function to dump a set of indexed registers. Use it
+ * like this:
+ *
+ * struct kgsl_snapshot_indexed_registers priv;
+ * priv.index = REG_INDEX;
+ * priv.data = REG_DATA;
+ * priv.count = num_registers
+ *
+ * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_INDEXED_REGS,
+ *	snapshot, remain, kgsl_snapshot_dump_indexed_regs, &priv).
+ *
+ * The callback function will write an index from 0 to priv.count to
+ * the index register and read the data from the data register.
+ */
+
+struct kgsl_snapshot_indexed_registers {
+	unsigned int index; /* Offset of the index register */
+	unsigned int data;  /* Offset of the data register */
+	unsigned int start;	/* Index to start with */
+	unsigned int count; /* Number of values to read from the pair */
+};
+
+int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device,
+	void *snapshot, int remain, void *priv);
+
+#endif
+#endif
diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c
index 2b3732c..f1d70aa 100644
--- a/drivers/media/video/msm/msm_vfe31.c
+++ b/drivers/media/video/msm/msm_vfe31.c
@@ -3892,6 +3892,7 @@
 	spin_lock_init(&vfe31_ctrl->io_lock);
 	spin_lock_init(&vfe31_ctrl->update_ack_lock);
 	spin_lock_init(&vfe31_ctrl->tasklet_lock);
+	spin_lock_init(&vfe31_ctrl->xbar_lock);
 
 	INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q);
 	vfe31_init_free_buf_queue();
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index fa30ef2..2c99a7f 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -42,6 +42,7 @@
 #define PM8921_VERSION_MASK	0xFFF0
 #define PM8921_VERSION_VALUE	0x06F0
 #define PM8922_VERSION_VALUE	0x0AF0
+#define PM8917_VERSION_VALUE	0x12F0
 #define PM8921_REVISION_MASK	0x000F
 
 #define REG_PM8921_PON_CNTRL_3	0x01D
@@ -116,6 +117,9 @@
 	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
 			== PM8922_VERSION_VALUE)
 		version = PM8XXX_VERSION_8922;
+	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+			== PM8917_VERSION_VALUE)
+		version = PM8XXX_VERSION_8917;
 
 	return version;
 }
@@ -138,7 +142,7 @@
 	.pmic_get_revision	= pm8921_get_revision,
 };
 
-static const struct resource gpio_cell_resources[] __devinitconst = {
+static struct resource gpio_cell_resources[] = {
 	[0] = {
 		.start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
 		.end   = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
@@ -167,7 +171,7 @@
 	.num_resources	= ARRAY_SIZE(adc_cell_resources),
 };
 
-static const struct resource mpp_cell_resources[] __devinitconst = {
+static struct resource mpp_cell_resources[] = {
 	{
 		.start	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
 		.end	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
@@ -545,7 +549,14 @@
 	}
 
 	if (pdata->gpio_pdata) {
-		pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+		if (version == PM8XXX_VERSION_8917) {
+			gpio_cell_resources[0].end = gpio_cell_resources[0].end
+							+ PM8917_NR_GPIOS
+							- PM8921_NR_GPIOS;
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
+		} else {
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+		}
 		gpio_cell.platform_data = pdata->gpio_pdata;
 		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
 		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
@@ -557,7 +568,14 @@
 	}
 
 	if (pdata->mpp_pdata) {
-		pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+		if (version == PM8XXX_VERSION_8917) {
+			mpp_cell_resources[0].end = mpp_cell_resources[0].end
+							+ PM8917_NR_MPPS
+							- PM8921_NR_MPPS;
+			pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
+		} else {
+			pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+		}
 		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
 		mpp_cell.platform_data = pdata->mpp_pdata;
 		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
@@ -665,12 +683,6 @@
 		goto bail;
 	}
 
-	ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
-	if (ret) {
-		pr_err("Failed to add pwm subdevice ret=%d\n", ret);
-		goto bail;
-	}
-
 	if (pdata->misc_pdata) {
 		misc_cell.platform_data = pdata->misc_pdata;
 		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
@@ -682,16 +694,6 @@
 		}
 	}
 
-	if (pdata->leds_pdata) {
-		leds_cell.platform_data = pdata->leds_pdata;
-		leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
-		ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
-		if (ret) {
-			pr_err("Failed to add leds subdevice ret=%d\n", ret);
-			goto bail;
-		}
-	}
-
 	ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
 				irq_base);
 	if (ret) {
@@ -708,16 +710,37 @@
 		goto bail;
 	}
 
-	if (pdata->vibrator_pdata) {
-		vibrator_cell.platform_data = pdata->vibrator_pdata;
-		vibrator_cell.pdata_size =
-				sizeof(struct pm8xxx_vibrator_platform_data);
-		ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL, 0);
+	if (version != PM8XXX_VERSION_8917) {
+		ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
 		if (ret) {
-			pr_err("Failed to add vibrator subdevice ret=%d\n",
-									ret);
+			pr_err("Failed to add pwm subdevice ret=%d\n", ret);
 			goto bail;
 		}
+
+		if (pdata->leds_pdata) {
+			leds_cell.platform_data = pdata->leds_pdata;
+			leds_cell.pdata_size =
+				sizeof(struct pm8xxx_led_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &leds_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add leds subdevice ret=%d\n",
+						ret);
+				goto bail;
+			}
+		}
+
+		if (pdata->vibrator_pdata) {
+			vibrator_cell.platform_data = pdata->vibrator_pdata;
+			vibrator_cell.pdata_size =
+				sizeof(struct pm8xxx_vibrator_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add vibrator ret=%d\n", ret);
+				goto bail;
+			}
+		}
 	}
 
 	if (pdata->ccadc_pdata) {
@@ -768,6 +791,11 @@
 	[PM8XXX_REVISION_8922_2p0]	= "2.0",
 };
 
+static const char * const pm8917_rev_names[] = {
+	[PM8XXX_REVISION_8917_TEST]	= "test",
+	[PM8XXX_REVISION_8917_1p0]	= "1.0",
+};
+
 static int __devinit pm8921_probe(struct platform_device *pdev)
 {
 	const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
@@ -823,9 +851,14 @@
 		if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
 			revision_name = pm8922_rev_names[revision];
 		pr_info("PMIC version: PM8922 rev %s\n", revision_name);
+	} else if (version == PM8XXX_VERSION_8917) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
+			revision_name = pm8917_rev_names[revision];
+		pr_info("PMIC version: PM8917 rev %s\n", revision_name);
 	} else {
 		WARN_ON(version != PM8XXX_VERSION_8921
-			&& version != PM8XXX_VERSION_8922);
+			&& version != PM8XXX_VERSION_8922
+			&& version != PM8XXX_VERSION_8917);
 	}
 
 	/* Log human readable restart reason */
diff --git a/drivers/net/wireless/wcnss/wcnss_riva.c b/drivers/net/wireless/wcnss/wcnss_riva.c
index fe6abf2..f6997ac 100644
--- a/drivers/net/wireless/wcnss/wcnss_riva.c
+++ b/drivers/net/wireless/wcnss/wcnss_riva.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -22,6 +22,7 @@
 #include <linux/semaphore.h>
 #include <linux/list.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
 #include <mach/msm_xo.h>
 #include <mach/msm_iomap.h>
 
@@ -79,10 +80,15 @@
 };
 
 
-static int configure_iris_xo(bool use_48mhz_xo, int on)
+static int configure_iris_xo(struct device *dev, bool use_48mhz_xo, int on)
 {
 	u32 reg = 0;
 	int rc = 0;
+	struct clk *cxo = clk_get(dev, "cxo");
+	if (IS_ERR(cxo)) {
+		pr_err("Couldn't get cxo clock\n");
+		return PTR_ERR(cxo);
+	}
 
 	if (on) {
 		msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256);
@@ -92,6 +98,11 @@
 		}
 
 		/* Enable IRIS XO */
+		rc = clk_prepare_enable(cxo);
+		if (rc) {
+			pr_err("cxo enable failed\n");
+			goto fail;
+		}
 		writel_relaxed(0, RIVA_PMU_CFG);
 		reg = readl_relaxed(RIVA_PMU_CFG);
 		reg |= RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
@@ -119,6 +130,7 @@
 		reg &= ~(RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
 				RIVA_PMU_CFG_IRIS_XO_CFG);
 		writel_relaxed(reg, RIVA_PMU_CFG);
+		clk_disable_unprepare(cxo);
 
 		if (!use_48mhz_xo) {
 			wlan_clock = msm_xo_get(MSM_XO_TCXO_A2, id);
@@ -148,12 +160,14 @@
 	/* Add some delay for XO to settle */
 	msleep(20);
 
+	clk_put(cxo);
 	return rc;
 
 msm_xo_vote_fail:
 	msm_xo_put(wlan_clock);
 
 fail:
+	clk_put(cxo);
 	return rc;
 }
 
@@ -302,13 +316,15 @@
 			goto fail_iris_on;
 
 		/* Configure IRIS XO */
-		rc = configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_ON);
+		rc = configure_iris_xo(dev, cfg->use_48mhz_xo,
+				WCNSS_WLAN_SWITCH_ON);
 		if (rc)
 			goto fail_iris_xo;
 		up(&riva_power_on_lock);
 
 	} else {
-		configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_OFF);
+		configure_iris_xo(dev, cfg->use_48mhz_xo,
+				WCNSS_WLAN_SWITCH_OFF);
 		wcnss_iris_vregs_off();
 		wcnss_riva_vregs_off();
 	}
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 177176e..86f8a25 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -426,6 +426,23 @@
 	kfree(f->name);
 }
 
+static void frmnet_suspend(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned port_num;
+
+	if (!atomic_read(&dev->online))
+		return;
+	/* This is a workaround for a bug in Windows 7/XP hosts in which
+	 * the DTR bit is not set low when going into suspend. Hence force it
+	 * low here when this function driver is suspended.
+	 */
+	if (dev->port.notify_modem) {
+		port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+		dev->port.notify_modem(&dev->port, port_num, ~ACM_CTRL_DTR);
+	}
+}
+
 static void frmnet_disable(struct usb_function *f)
 {
 	struct f_rmnet *dev = func_to_rmnet(f);
@@ -937,6 +954,7 @@
 	f->disable = frmnet_disable;
 	f->set_alt = frmnet_set_alt;
 	f->setup = frmnet_setup;
+	f->suspend = frmnet_suspend;
 	dev->port.send_cpkt_response = frmnet_send_cpkt_response;
 	dev->port.disconnect = frmnet_disconnect;
 	dev->port.connect = frmnet_connect;
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 787514c..3d739ae 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -2450,7 +2450,7 @@
 		goto free_regs;
 	}
 
-	motg->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+	motg->xo_handle = msm_xo_get(MSM_XO_CXO, "usb");
 	if (IS_ERR(motg->xo_handle)) {
 		dev_err(&pdev->dev, "%s not able to get the handle "
 			"to vote for TCXO D0 buffer\n", __func__);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index f27bd49..1449c8e 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -338,6 +338,8 @@
 {
 	unsigned long flag;
 	int ret = 0;
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
 	mutex_lock(&mdp_hist_mutex);
 	if (!mdp_is_hist_start) {
 		printk(KERN_ERR "%s histogram already stopped\n", __func__);
@@ -349,6 +351,14 @@
 	mdp_is_hist_start = FALSE;
 	spin_unlock_irqrestore(&mdp_spin_lock, flag);
 
+	if (!mfd->panel_power_on) {
+
+		mdp_is_hist_data = FALSE;
+		complete(&mdp_hist_comp);
+		ret = -EINVAL;
+		goto mdp_hist_stop_err;
+	}
+
 	ret = _mdp_histogram_ctrl(FALSE);
 
 mdp_hist_stop_err:
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index bdfadd5..42fa2ba 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3148,6 +3148,9 @@
 		break;
 
 	case MSMFB_HISTOGRAM_START:
+		if (!mfd->panel_power_on)
+			return -EPERM;
+
 		if (!mfd->do_histogram)
 			return -ENODEV;
 		ret = mdp_start_histogram(info);
diff --git a/drivers/video/msm/vidc/common/init/vidc_init.c b/drivers/video/msm/vidc/common/init/vidc_init.c
index f65f835..d24d43c 100644
--- a/drivers/video/msm/vidc/common/init/vidc_init.c
+++ b/drivers/video/msm/vidc/common/init/vidc_init.c
@@ -506,7 +506,7 @@
 			if (IS_ERR_OR_NULL(buff_ion_handle)) {
 				ERR("%s(): get_ION_handle failed\n",
 				 __func__);
-				goto ion_error;
+				goto bail_out_add;
 			}
 			if (ion_handle_get_flags(client_ctx->user_ion_client,
 						buff_ion_handle,
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index bdbaffd..d1c7733 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2012, 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
@@ -40,6 +40,7 @@
 #define APQ8060_MACHINE_ID	86
 #define AO8960_MACHINE_ID	87
 #define MSM8660_MACHINE_ID	71
+#define MDM9615_MACHINE_ID	104
 #define APQ8064_MACHINE_ID	109
 #define MSM8930_MACHINE_ID	116
 #define MSM8630_MACHINE_ID	117
@@ -47,10 +48,13 @@
 #define APQ8030_MACHINE_ID	119
 #define MSM8627_MACHINE_ID	120
 #define MSM8227_MACHINE_ID	121
+#define MSM8260A_MACHINE_ID	123
+#define MSM8974_MACHINE_ID	126
 #define APQ8060_TOOLS_ID	4062
 #define AO8960_TOOLS_ID		4064
 #define APQ8064_TOOLS_ID	4072
 #define MSM8930_TOOLS_ID	4072
+#define MSM8974_TOOLS_ID	4072
 
 #define MSG_MASK_0			(0x00000001)
 #define MSG_MASK_1			(0x00000002)
diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h
index 5ed615b..e7bf820 100644
--- a/include/linux/mfd/pm8xxx/core.h
+++ b/include/linux/mfd/pm8xxx/core.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -28,6 +28,7 @@
 	PM8XXX_VERSION_8018,
 	PM8XXX_VERSION_8922,
 	PM8XXX_VERSION_8038,
+	PM8XXX_VERSION_8917,
 };
 
 /* PMIC version specific silicon revisions */
@@ -68,6 +69,9 @@
 #define PM8XXX_REVISION_8038_2p0	2
 #define PM8XXX_REVISION_8038_2p1	3
 
+#define PM8XXX_REVISION_8917_TEST	0
+#define PM8XXX_REVISION_8917_1p0	1
+
 struct pm8xxx_drvdata {
 	int			(*pmic_readb) (const struct device *dev,
 						u16 addr, u8 *val);
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
index 35792d5..ee1216d 100644
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ b/include/linux/mfd/pm8xxx/pm8921.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -40,8 +40,10 @@
 #define PM8921_NR_IRQS		256
 
 #define PM8921_NR_GPIOS		44
+#define PM8917_NR_GPIOS		38
 
 #define PM8921_NR_MPPS		12
+#define PM8917_NR_MPPS		10
 
 #define PM8921_GPIO_BLOCK_START	24
 #define PM8921_MPP_BLOCK_START	16
diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h
index 423264d..f746780 100644
--- a/include/sound/compress_offload.h
+++ b/include/sound/compress_offload.h
@@ -56,6 +56,7 @@
 	size_t decoded;
 	size_t rendered;
 	__u32 sampling_rate;
+	uint64_t timestamp;
 };
 
 /**
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 6dd815d..8c07206 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1530,8 +1530,6 @@
 	if (PCM_RUNTIME_CHECK(substream))
 		return -ENXIO;
 	runtime = substream->runtime;
-	if (runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		return -EBADFD;
 	pr_err("%s called with cmd = %d\n", __func__, cmd);
 	err = substream->ops->ioctl(substream, cmd, arg);
 	return err;
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 8de9dec..2bdb917 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -118,7 +118,6 @@
 	u16 v_brh;
 	u16 v_brl;
 	u16 v_no_mic;
-	u8 nready;
 	u8 npoll;
 	u8 nbounce_wait;
 };
@@ -2705,9 +2704,21 @@
 	tabla->clock_active = false;
 }
 
+static int tabla_codec_mclk_index(const struct tabla_priv *tabla)
+{
+	if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ)
+		return 0;
+	else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ)
+		return 1;
+	else {
+		BUG_ON(1);
+		return -EINVAL;
+	}
+}
+
 static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
 {
-	u8 *n_cic;
+	u8 *n_ready, *n_cic;
 	struct tabla_mbhc_btn_detect_cfg *btn_det;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
@@ -2738,15 +2749,17 @@
 	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B12_CTL,
 		      (tabla->mbhc_data.v_brl >> 8) & 0xFF);
 
+	n_ready = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_READY);
 	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL,
-		      tabla->mbhc_data.nready);
+		      n_ready[tabla_codec_mclk_index(tabla)]);
 	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL,
 		      tabla->mbhc_data.npoll);
 	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL,
 		      tabla->mbhc_data.nbounce_wait);
 
 	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, n_cic[0]);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL,
+		      n_cic[tabla_codec_mclk_index(tabla)]);
 }
 
 static int tabla_startup(struct snd_pcm_substream *substream,
@@ -3388,7 +3401,7 @@
 	 */
 	btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
 	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
-	ncic = n_cic[0];
+	ncic = n_cic[tabla_codec_mclk_index(tabla)];
 	nmeas = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration)->n_meas;
 	navg = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration)->mbhc_navg;
 	mclk_rate = tabla->mclk_freq;
@@ -3475,7 +3488,7 @@
 		ret += sizeof(btn_det->_n_cic);
 	case TABLA_BTN_DET_N_CIC:
 		ret += sizeof(btn_det->_n_ready);
-	case TABLA_BTN_DET_V_N_READY:
+	case TABLA_BTN_DET_N_READY:
 		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
 	case TABLA_BTN_DET_V_BTN_HIGH:
 		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
@@ -3496,25 +3509,25 @@
 	struct tabla_mbhc_btn_detect_cfg *btn_det;
 	struct tabla_mbhc_plug_type_cfg *plug_type;
 	u16 *btn_high;
+	u8 *n_ready;
 	int i;
 
 	tabla = snd_soc_codec_get_drvdata(codec);
 	btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
 	plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
 
+	n_ready = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_READY);
 	if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ) {
-		tabla->mbhc_data.nready = 3;
 		tabla->mbhc_data.npoll = 9;
 		tabla->mbhc_data.nbounce_wait = 30;
 	} else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ) {
-		tabla->mbhc_data.nready = 2;
 		tabla->mbhc_data.npoll = 7;
 		tabla->mbhc_data.nbounce_wait = 23;
-	} else
-		WARN(1, "Unsupported mclk freq %d\n", tabla->mclk_freq);
+	}
 
 	tabla->mbhc_data.t_sta_dce = ((1000 * 256) / (tabla->mclk_freq / 1000) *
-				      tabla->mbhc_data.nready) + 10;
+				      n_ready[tabla_codec_mclk_index(tabla)]) +
+				     10;
 	tabla->mbhc_data.v_ins_hu =
 	    tabla_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
 	tabla->mbhc_data.v_ins_h =
@@ -3573,10 +3586,11 @@
 
 	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
-			    n_cic[0]);
+			    n_cic[tabla_codec_mclk_index(tabla)]);
 
 	gain = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_GAIN);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78, gain[0] << 3);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78,
+			    gain[tabla_codec_mclk_index(tabla)] << 3);
 
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
 			    generic->mbhc_nsa << 4);
@@ -3695,6 +3709,16 @@
 		pr_err("Error: no codec or calibration\n");
 		return -EINVAL;
 	}
+
+	if (mclk_rate != TABLA_MCLK_RATE_12288KHZ) {
+		if (mclk_rate == TABLA_MCLK_RATE_9600KHZ)
+			pr_err("Error: clock rate %dHz is not yet supported\n",
+			       mclk_rate);
+		else
+			pr_err("Error: unsupported clock rate %d\n", mclk_rate);
+		return -EINVAL;
+	}
+
 	tabla = snd_soc_codec_get_drvdata(codec);
 	tabla->headset_jack = headset_jack;
 	tabla->button_jack = button_jack;
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 77bfc73..7f70010 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -66,7 +66,7 @@
 enum tabla_mbhc_btn_det_mem {
 	TABLA_BTN_DET_V_BTN_LOW,
 	TABLA_BTN_DET_V_BTN_HIGH,
-	TABLA_BTN_DET_V_N_READY,
+	TABLA_BTN_DET_N_READY,
 	TABLA_BTN_DET_N_CIC,
 	TABLA_BTN_DET_GAIN
 };
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index d0497b1..f0f17a9 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -450,8 +450,37 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct compr_audio *compr = runtime->private_data;
 	struct msm_audio *prtd = &compr->prtd;
+	uint64_t timestamp;
+	uint64_t temp;
 
 	switch (cmd) {
+	case SNDRV_COMPRESS_TSTAMP: {
+		struct snd_compr_tstamp tstamp;
+		pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+		memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+		timestamp = q6asm_get_session_time(prtd->audio_client);
+		if (timestamp < 0) {
+			pr_err("%s: Get Session Time return value =%lld\n",
+				__func__, timestamp);
+			return -EAGAIN;
+		}
+		temp = (timestamp * 2 * runtime->channels);
+		temp = temp * (runtime->rate/1000);
+		temp = div_u64(temp, 1000);
+		tstamp.sampling_rate = runtime->rate;
+		tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+		tstamp.decoded  = (size_t)((temp >> 32) & 0xFFFFFFFF);
+		tstamp.timestamp = timestamp;
+		pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+			"timestamp = %lld,\n",
+			 __func__, tstamp.rendered, tstamp.decoded,
+			tstamp.timestamp);
+		if (copy_to_user((void *) arg, &tstamp,
+			sizeof(struct snd_compr_tstamp)))
+				return -EFAULT;
+		return 0;
+	}
 	case SNDRV_COMPRESS_GET_CAPS:
 		pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
 		if (copy_to_user((void *) arg, &compr->info.compr_cap,
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 0dc3b57..cb73ab6 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -571,7 +571,7 @@
 	void *tabla_cal;
 	struct tabla_mbhc_btn_detect_cfg *btn_cfg;
 	u16 *btn_low, *btn_high;
-	u8 *n_cic, *gain;
+	u8 *n_ready, *n_cic, *gain;
 
 	tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
 						TABLA_MBHC_DEF_RLOADS),
@@ -624,6 +624,9 @@
 	btn_high[1] = 140;
 	btn_low[2] = 160;
 	btn_high[2] = 240;
+	n_ready = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_READY);
+	n_ready[0] = 48;
+	n_ready[1] = 38;
 	n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
 	n_cic[0] = 120;
 	n_cic[1] = 94;