Merge "msm: kgsl: Use the proper device when freeing IRQs" into msm-4.9
diff --git a/Documentation/devicetree/bindings/arm/msm/board-id.txt b/Documentation/devicetree/bindings/arm/msm/board-id.txt
new file mode 100644
index 0000000..e07a1c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/board-id.txt
@@ -0,0 +1,64 @@
+* BOARD-ID
+
+The qcom,board-id entry specifies the MSM platform and subtype revision.
+It can optionally be an array of these to indicate multiple hardware that use
+the same device tree.  It is expected that the bootloader will use this
+information at boot-up to decide which device tree to use when given multiple
+device trees, some of which may not be compatible with the actual hardware.  It
+is the bootloader's responsibility to pass the correct device tree to the kernel.
+
+Legacy format:
+
+It is expected that the qcom,board-id entry be at the top level of the device
+tree structure.  The format of the entry is:
+
+   qcom,board-id = <platform_id, subtype_id> [, <p2, s2> ...]
+
+where platform_id and subtype_id are the numeric values for the platform and
+subtype of the current hardware.
+
+The "subtype_id" cell is a 32-bit integer whose bit values are defined as follows:
+    bits 31-20 = Reserved bits
+    bits 19-16 = Boot Device Type.
+		 MSM:
+		     0: default (eMMC)
+		     2: EMMC_SDC1
+		     4: BOOT_UFS
+		 MDM:
+		     0: default (NAND)
+		     3: EMMC_SDC1
+    bits 15-8  = DDR Size. For devices with DDR Size as 512MB the value is 0x1, default value as 0x0
+    bits 7-0   = Platform Subtype
+
+In the event that a given device tree is applicable to all hardware versions
+matching a given Platform Type / Subtype ID, the major/minior platform version
+fields in the board_id property shall both be specified as 0xff.
+
+Modern format:
+The cell layout of the qcom,board-id property is as follows:
+
+   qcom,board-id = <board_id, reserved>
+
+where board_id is a 32-bit integer whose bit values are defined as follows:
+    bits 31-24 = Platform Subtype ID
+    bits 23-16 = Platform Version (Major)
+    bits 15-8  = Platform Version (Minor)
+    bits  7-0  = Platform Type ID
+
+and the 'reserved' cell is a 32-bit integer whose bit values are defined as follows:
+    bits 31-13 = Reserved Bits
+    bits 12-11 = Panel Detection. 00 - limit to HD, 01 - limit to 720p,
+		 10 - limit to qHD, 11 - limit to FWVGA
+    bits 10-8  = DDR Size. For devices with DDR Size as 512MB the value is 0x1,
+		 default value as 0x0
+    bits 7-0   = Platform Subtype
+
+In the event that a given device tree is applicable to all hardware versions
+matching a given Platform Type / Subtype ID, the major/minior platform version
+fields in the board_id property shall both be specified as 0xff.
+
+Example:
+   qcom,board-id = <15 0>;
+   qcom,board-id = <0x01040708, 0>;
+   qcom,board-id = <0x01ffff08, 0>;
+   qcom,board-id = <8, 0x100>;
diff --git a/Documentation/devicetree/bindings/arm/msm/msm-id.txt b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
new file mode 100644
index 0000000..c243154
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
@@ -0,0 +1,33 @@
+* MSM-ID
+
+The qcom,msm-id entry specifies the MSM chipset, platform, hardware revision
+and optional manufactured foundry.  It can optionally be an array of these to
+indicate multiple hardware that use the same device tree.  It is expected that
+the bootloader will use this information at boot-up to decide which device tree
+to use when given multiple device trees, some of which may not be compatible
+with the actual hardware.  It is the bootloader's responsibility to pass the
+correct device tree to the kernel.
+
+Format:
+
+It is expected that the qcom,msm-id entry be at the top level of the device
+tree structure.  The format can take one of the two forms below:
+
+   qcom,msm-id = <chipset_foundry_id, platform_id, rev_id> [, <c2, p2, r2> ...]
+   qcom,msm-id = <chipset_foundry_id, rev_id> [, <c2, r2> ...]
+
+If the second format is used one must also define the board-id.
+
+The "chipset_foundry_id" consists of three fields as below:
+
+   bits 0-15  = The unique MSM chipset id.
+   bits 16-23 = The optional foundry id. If bootloader doesn't find a device
+		tree which has exact matching foundry-id with hardware it
+		chooses the device tree with foundry-id = 0.
+   bits 24-31 = Reserved.
+
+Example:
+   qcom,msm-id = <0x1007e 15 0>;
+
+   qcom,board-id= <15 2>;
+   qcom,msm-id = <0x1007e 0>;
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dp.txt b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
new file mode 100644
index 0000000..790da12
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
@@ -0,0 +1,146 @@
+Qualcomm Technologies, Inc.
+sde-dp is the master Display Port device which supports DP host controllers that are compatible with VESA Display Port interface specification.
+DP Controller: Required properties:
+- compatible:           Should be "qcom,dp-display".
+- reg:                  Base address and length of DP hardware's memory mapped regions.
+- reg-names:            A list of strings that name the list of regs. "dp_ctrl" - DP controller memory region.
+			"dp_phy" - DP PHY memory region.
+			"dp_ln_tx0" - USB3 DP PHY combo TX-0 lane memory region.
+			"dp_ln_tx1" - USB3 DP PHY combo TX-1 lane memory region.
+			"dp_mmss_cc" - Display Clock Control memory region.
+			"qfprom_physical" - QFPROM Phys memory region.
+			"dp_pll" - USB3 DP combo PLL memory region.
+			"hdcp_physical" - DP HDCP memory region.
+- cell-index:           Specifies the controller instance.
+- clocks:               Clocks required for Display Port operation.
+- clock-names:          Names of the clocks corresponding to handles. Following clocks are required:
+			"core_aux_clk", "core_usb_ref_clk_src","core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk",
+			"ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent".
+- gdsc-supply:		phandle to gdsc regulator node.
+- vdda-1p2-supply:		phandle to vdda 1.2V regulator node.
+- vdda-0p9-supply:		phandle to vdda 0.9V regulator node.
+- interrupt-parent	phandle to the interrupt parent device node.
+- interrupts:		The interrupt signal from the DSI block.
+- qcom,aux-en-gpio:			Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio:		Specifies the aux-channel select gpio.
+- qcom,usbplug-cc-gpio:		Specifies the usbplug orientation gpio.
+- qcom,aux-cfg-settings:	An array that specifies the DP AUX configuration settings.
+- qcom,max-pclk-frequency-khz:	An integer specifying the max. pixel clock in KHz supported by Display Port.
+- qcom,dp-usbpd-detection:	Phandle for the PMI regulator node for USB PHY PD detection.
+- qcom,<type>-supply-entries:		A node that lists the elements of the supply used by the a particular "type" of DSI module. The module "types"
+					can be "core", "ctrl", and "phy". Within the same type,
+					there can be more than one instance of this binding,
+					in which case the entry would be appended with the
+					supply entry index.
+					e.g. qcom,ctrl-supply-entry@0
+					-- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+					-- qcom,supply-min-voltage: minimum voltage level (uV)
+					-- qcom,supply-max-voltage: maximum voltage level (uV)
+					-- qcom,supply-enable-load: load drawn (uA) from enabled supply
+					-- qcom,supply-disable-load: load drawn (uA) from disabled supply
+					-- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+					-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+					-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+					-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+- pinctrl-names:	List of names to assign mdss pin states defined in pinctrl device node
+					Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>:	Lists phandles each pointing to the pin configuration node within a pin
+					controller. These pin configurations are installed in the pinctrl
+					device node. Refer to pinctrl-bindings.txt
+
+Example:
+	sde_dp: qcom,dp_display@0{
+		cell-index = <0>;
+		compatible = "qcom,dp-display";
+
+		gdsc-supply = <&mdss_core_gdsc>;
+		vdda-1p2-supply = <&pm8998_l26>;
+		vdda-0p9-supply = <&pm8998_l1>;
+
+		reg =	<0xae90000 0xa84>,
+			<0x88eaa00 0x200>,
+			<0x88ea200 0x200>,
+			<0x88ea600 0x200>,
+			<0xaf02000 0x1a0>,
+			<0x780000 0x621c>,
+			<0x88ea030 0x10>,
+			<0x88e8000 0x621c>,
+			<0x0aee1000 0x034>;
+		reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+			"dp_mmss_cc", "qfprom_physical", "dp_pll",
+			"usb3_dp_com", "hdcp_physical";
+
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <12 0>;
+
+		clocks =  <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+			 <&clock_rpmh RPMH_CXO_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			 <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
+			 <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+		clock-names = "core_aux_clk", "core_usb_ref_clk_src",
+			"core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk",
+			"ctrl_link_iface_clk", "ctrl_crypto_clk",
+			"ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent";
+
+		qcom,dp-usbpd-detection = <&pmi8998_pdphy>;
+
+		qcom,aux-cfg-settings = [00 13 04 00 0a 26 0a 03 bb 03];
+		qcom,max-pclk-frequency-khz = <593470>;
+		pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+		pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
+		pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
+		qcom,aux-en-gpio = <&tlmm 43 0>;
+		qcom,aux-sel-gpio = <&tlmm 51 0>;
+		qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+		qcom,core-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,core-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "gdsc";
+				qcom,supply-min-voltage = <0>;
+				qcom,supply-max-voltage = <0>;
+				qcom,supply-enable-load = <0>;
+				qcom,supply-disable-load = <0>;
+			};
+		};
+
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/qbt1000/qbt1000.txt b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt
new file mode 100644
index 0000000..c9861e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt
@@ -0,0 +1,54 @@
+Qualcomm Technologies, Inc. QBT1000 Specific Bindings
+
+QBT is a fingerprint sensor ASIC capable of performing fingerprint image scans
+and detecting finger presence on the sensor using programmable firmware.
+
+=======================
+Required Node Structure
+=======================
+
+- compatible
+  Usage:      required
+  Value type: <string>
+  Definition: "qcom,qbt1000".
+
+- clock-names
+  Usage:      required
+  Value type: <stringlist>
+  Definition: List of clock names that need to be voted on/off.
+
+- clocks
+  Usage:      required
+  Value type: <prop_encoded-array>
+  Definition: Property pair that represents the clock controller and the clock
+		id. This in combination with the clock-name is used to obtain
+		the handle for the clock that needs to be voted on/off.
+
+- clock-frequency
+  Usage:      required
+  Value type: <u32>
+  Definition: Frequency of clock in Hz.
+
+- qcom,ipc-gpio
+  Usage:      required
+  Value type: <phandle>
+  Definition: phandle for GPIO to be used for IPC.
+
+- qcom,finger-detect-gpio
+  Usage:      required
+  Value type: <phandle>
+  Definition: phandle for GPIO to be used for finger detect.
+
+=======
+Example
+=======
+
+qcom,qbt1000 {
+	compatible = "qcom,qbt1000";
+	clock-names = "core", "iface";
+	clocks = <&clock_gcc clk_gcc_blsp2_qup6_spi_apps_clk>,
+		<&clock_gcc clk_gcc_blsp2_ahb_clk>;
+	clock-frequency = <15000000>;
+	qcom,ipc-gpio = <&tlmm 121 0>;
+	qcom,finger-detect-gpio = <&pmcobalt_gpios 2 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 79ac3b1..7ae63af 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -425,10 +425,44 @@
 					 2003 1675>;
 
 				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					<100000 100000 100000>;
+					/* Speed bin 0 */
+					<100000 100000 100000>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					/* Speed bin 1 */
+					<100000 100000 100000>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>;
 
 				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					<100000 100000 100000>;
+					/* Speed bin 0 */
+					<100000 100000 100000>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					/* Speed bin 1 */
+					<100000 100000 100000>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>,
+					<     0      0      0>;
 
 				qcom,allow-voltage-interpolation;
 				qcom,allow-quotient-interpolation;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 6038b6e..6818615 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2486,6 +2486,14 @@
 		qcom,pipe-attr-ee;
 	};
 
+	qcom,qbt1000 {
+		compatible = "qcom,qbt1000";
+		clock-names = "core", "iface";
+		clock-frequency = <25000000>;
+		qcom,ipc-gpio = <&tlmm 121 0>;
+		qcom,finger-detect-gpio = <&pm8998_gpios 5 0>;
+	};
+
 	qcom_seecom: qseecom@86d00000 {
 		compatible = "qcom,qseecom";
 		reg = <0x86d00000 0x2200000>;
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 10b44f8..19a6db8 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -491,6 +491,7 @@
 CONFIG_MSM_AVTIMER=y
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
+CONFIG_MSM_QBT1000=y
 CONFIG_APSS_CORE_EA=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 737f47f..04a0d3e 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -513,6 +513,7 @@
 CONFIG_MSM_AVTIMER=y
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
+CONFIG_MSM_QBT1000=y
 CONFIG_APSS_CORE_EA=y
 CONFIG_QCOM_DCC_V2=y
 CONFIG_QTI_RPM_STATS_LOG=y
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index da845fd..fe8f94a 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -277,12 +277,10 @@
 	}
 	if (!user_mode(regs))
 		show_extra_register_data(regs, 64);
-	printk("\n");
 }
 
 void show_regs(struct pt_regs * regs)
 {
-	printk("\n");
 	__show_regs(regs);
 }
 
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
new file mode 100644
index 0000000..722c436
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. 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.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/of_gpio.h>
+
+#include "dp_parser.h"
+
+static void dp_parser_unmap_io_resources(struct dp_parser *parser)
+{
+	struct dp_io *io = &parser->io;
+
+	if (&io->ctrl_io)
+		msm_dss_iounmap(&io->ctrl_io);
+	if (&io->phy_io)
+		msm_dss_iounmap(&io->phy_io);
+	if (&io->ln_tx0_io)
+		msm_dss_iounmap(&io->ln_tx0_io);
+	if (&io->ln_tx1_io)
+		msm_dss_iounmap(&io->ln_tx0_io);
+	if (&io->dp_pll_io)
+		msm_dss_iounmap(&io->dp_pll_io);
+	if (&io->dp_cc_io)
+		msm_dss_iounmap(&io->dp_cc_io);
+	if (&io->qfprom_io)
+		msm_dss_iounmap(&io->qfprom_io);
+	if (&io->hdcp_io)
+		msm_dss_iounmap(&io->hdcp_io);
+}
+
+static int dp_parser_ctrl_res(struct dp_parser *parser)
+{
+	int rc = 0;
+	u32 index;
+	struct platform_device *pdev = parser->pdev;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	struct dp_io *io = &parser->io;
+
+	rc = of_property_read_u32(of_node, "cell-index", &index);
+	if (rc) {
+		pr_err("cell-index not specified, rc=%d\n", rc);
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->ctrl_io, "dp_ctrl");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->phy_io, "dp_phy");
+	if (rc) {
+		pr_err("unable to remap dp PHY resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->ln_tx0_io, "dp_ln_tx0");
+	if (rc) {
+		pr_err("unable to remap dp TX0 resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->ln_tx1_io, "dp_ln_tx1");
+	if (rc) {
+		pr_err("unable to remap dp TX1 resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_pll_io, "dp_pll");
+	if (rc) {
+		pr_err("unable to remap DP PLL resources\n");
+		goto err;
+	}
+
+	if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
+		pr_err("unable to remap dp MMSS_CC resources\n");
+		goto err;
+	}
+
+	if (msm_dss_ioremap_byname(pdev, &io->qfprom_io, "qfprom_physical"))
+		pr_warn("unable to remap dp qfprom resources\n");
+
+	if (msm_dss_ioremap_byname(pdev, &io->hdcp_io, "hdcp_physical"))
+		pr_warn("unable to remap dp hdcp resources\n");
+
+	return 0;
+err:
+	dp_parser_unmap_io_resources(parser);
+	return rc;
+}
+
+static int dp_parser_aux(struct dp_parser *parser)
+{
+	int len = 0, i = 0, rc = 0;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	const char *data;
+
+	data = of_get_property(of_node, "qcom,aux-cfg-settings", &len);
+	if (!data || (len != AUX_CFG_LEN)) {
+		pr_err("Unable to read DP AUX CFG settings\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	for (i = 0; i < len; i++)
+		parser->aux_cfg[i] = data[i];
+end:
+	return rc;
+}
+
+static int dp_parser_misc(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+
+	rc = of_property_read_u32(of_node,
+		"qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
+	if (rc)
+		parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
+
+	return 0;
+}
+
+static int dp_parser_pinctrl(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_pinctrl *pinctrl = &parser->pinctrl;
+
+	pinctrl->pin = devm_pinctrl_get(&parser->pdev->dev);
+
+	if (IS_ERR_OR_NULL(pinctrl->pin)) {
+		rc = PTR_ERR(pinctrl->pin);
+		pr_err("failed to get pinctrl, rc=%d\n", rc);
+		goto error;
+	}
+
+	pinctrl->state_active = pinctrl_lookup_state(pinctrl->pin,
+					"mdss_dp_active");
+	if (IS_ERR_OR_NULL(pinctrl->state_active)) {
+		rc = PTR_ERR(pinctrl->state_active);
+		pr_err("failed to get pinctrl active state, rc=%d\n", rc);
+		goto error;
+	}
+
+	pinctrl->state_suspend = pinctrl_lookup_state(pinctrl->pin,
+					"mdss_dp_sleep");
+	if (IS_ERR_OR_NULL(pinctrl->state_suspend)) {
+		rc = PTR_ERR(pinctrl->state_suspend);
+		pr_err("failed to get pinctrl suspend state, rc=%d\n", rc);
+		goto error;
+	}
+error:
+	return rc;
+}
+
+static int dp_parser_gpio(struct dp_parser *parser)
+{
+	int i = 0;
+	struct device *dev = &parser->pdev->dev;
+	struct device_node *of_node = dev->of_node;
+	struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
+	static const char * const dp_gpios[] = {
+		"qcom,aux-en-gpio",
+		"qcom,aux-sel-gpio",
+		"qcom,usbplug-cc-gpio",
+	};
+
+	mp->gpio_config = devm_kzalloc(dev,
+		sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
+	mp->num_gpio = ARRAY_SIZE(dp_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
+		mp->gpio_config[i].gpio = of_get_named_gpio(of_node,
+			dp_gpios[i], 0);
+
+		if (!gpio_is_valid(mp->gpio_config[i].gpio)) {
+			pr_err("%s gpio not specified\n", dp_gpios[i]);
+			return -EINVAL;
+		}
+
+		strlcpy(mp->gpio_config[i].gpio_name, dp_gpios[i],
+			sizeof(mp->gpio_config[i].gpio_name));
+
+		mp->gpio_config[i].value = 0;
+	}
+
+	return 0;
+}
+
+static const char *dp_parser_supply_node_name(enum dp_pm_type module)
+{
+	switch (module) {
+	case DP_CORE_PM:	return "qcom,core-supply-entries";
+	case DP_CTRL_PM:	return "qcom,ctrl-supply-entries";
+	case DP_PHY_PM:		return "qcom,phy-supply-entries";
+	default:		return "???";
+	}
+}
+
+static int dp_parser_get_vreg(struct dp_parser *parser,
+		enum dp_pm_type module)
+{
+	int i = 0, rc = 0;
+	u32 tmp = 0;
+	const char *pm_supply_name = NULL;
+	struct device_node *supply_node = NULL;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	struct device_node *supply_root_node = NULL;
+	struct dss_module_power *mp = &parser->mp[module];
+
+	mp->num_vreg = 0;
+	pm_supply_name = dp_parser_supply_node_name(module);
+	supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
+	if (!supply_root_node) {
+		pr_err("no supply entry present: %s\n", pm_supply_name);
+		goto novreg;
+	}
+
+	mp->num_vreg = of_get_available_child_count(supply_root_node);
+
+	if (mp->num_vreg == 0) {
+		pr_debug("no vreg\n");
+		goto novreg;
+	} else {
+		pr_debug("vreg found. count=%d\n", mp->num_vreg);
+	}
+
+	mp->vreg_config = devm_kzalloc(&parser->pdev->dev,
+		sizeof(struct dss_vreg) * mp->num_vreg, GFP_KERNEL);
+	if (!mp->vreg_config) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	for_each_child_of_node(supply_root_node, supply_node) {
+		const char *st = NULL;
+		/* vreg-name */
+		rc = of_property_read_string(supply_node,
+			"qcom,supply-name", &st);
+		if (rc) {
+			pr_err("error reading name. rc=%d\n",
+				 rc);
+			goto error;
+		}
+		snprintf(mp->vreg_config[i].vreg_name,
+			ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
+		/* vreg-min-voltage */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-min-voltage", &tmp);
+		if (rc) {
+			pr_err("error reading min volt. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].min_voltage = tmp;
+
+		/* vreg-max-voltage */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-max-voltage", &tmp);
+		if (rc) {
+			pr_err("error reading max volt. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].max_voltage = tmp;
+
+		/* enable-load */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-enable-load", &tmp);
+		if (rc) {
+			pr_err("error reading enable load. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].enable_load = tmp;
+
+		/* disable-load */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-disable-load", &tmp);
+		if (rc) {
+			pr_err("error reading disable load. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].disable_load = tmp;
+
+		pr_debug("%s min=%d, max=%d, enable=%d, disable=%d\n",
+			mp->vreg_config[i].vreg_name,
+			mp->vreg_config[i].min_voltage,
+			mp->vreg_config[i].max_voltage,
+			mp->vreg_config[i].enable_load,
+			mp->vreg_config[i].disable_load
+			);
+		++i;
+	}
+
+	return rc;
+
+error:
+	if (mp->vreg_config) {
+		devm_kfree(&parser->pdev->dev, mp->vreg_config);
+		mp->vreg_config = NULL;
+	}
+novreg:
+	mp->num_vreg = 0;
+
+	return rc;
+}
+
+static void dp_parser_put_vreg_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("invalid input\n");
+		return;
+	}
+
+	if (mp->vreg_config) {
+		devm_kfree(dev, mp->vreg_config);
+		mp->vreg_config = NULL;
+	}
+	mp->num_vreg = 0;
+}
+
+static int dp_parser_regulator(struct dp_parser *parser)
+{
+	int i, rc = 0;
+	struct platform_device *pdev = parser->pdev;
+
+	/* Parse the regulator information */
+	for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+		rc = dp_parser_get_vreg(parser, i);
+		if (rc) {
+			pr_err("get_dt_vreg_data failed for %s. rc=%d\n",
+				dp_parser_pm_name(i), rc);
+			i--;
+			for (; i >= DP_CORE_PM; i--)
+				dp_parser_put_vreg_data(&pdev->dev,
+					&parser->mp[i]);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name)
+{
+	return !!strnstr(clk_name, clk_prefix, strlen(clk_name));
+}
+
+static void dp_parser_put_clk_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return;
+	}
+
+	if (mp->clk_config) {
+		devm_kfree(dev, mp->clk_config);
+		mp->clk_config = NULL;
+	}
+
+	mp->num_clk = 0;
+}
+
+static int dp_parser_init_clk_data(struct dp_parser *parser)
+{
+	int num_clk = 0, i = 0, rc = 0;
+	int core_clk_count = 0, ctrl_clk_count = 0;
+	const char *core_clk = "core";
+	const char *ctrl_clk = "ctrl";
+	const char *clk_name;
+	struct device *dev = &parser->pdev->dev;
+	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	num_clk = of_property_count_strings(dev->of_node, "clock-names");
+	if (num_clk <= 0) {
+		pr_err("no clocks are defined\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	for (i = 0; i < num_clk; i++) {
+		of_property_read_string_index(dev->of_node,
+				"clock-names", i, &clk_name);
+
+		if (dp_parser_check_prefix(core_clk, clk_name))
+			core_clk_count++;
+
+		if (dp_parser_check_prefix(ctrl_clk, clk_name))
+			ctrl_clk_count++;
+	}
+
+	/* Initialize the CORE power module */
+	if (core_clk_count <= 0) {
+		pr_err("no core clocks are defined\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	core_power->num_clk = core_clk_count;
+	core_power->clk_config = devm_kzalloc(dev,
+			sizeof(struct dss_clk) * core_power->num_clk,
+			GFP_KERNEL);
+	if (!core_power->clk_config) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Initialize the CTRL power module */
+	if (ctrl_clk_count <= 0) {
+		pr_err("no ctrl clocks are defined\n");
+		rc = -EINVAL;
+		goto ctrl_clock_error;
+	}
+
+	ctrl_power->num_clk = ctrl_clk_count;
+	ctrl_power->clk_config = devm_kzalloc(dev,
+			sizeof(struct dss_clk) * ctrl_power->num_clk,
+			GFP_KERNEL);
+	if (!ctrl_power->clk_config) {
+		ctrl_power->num_clk = 0;
+		rc = -EINVAL;
+		goto ctrl_clock_error;
+	}
+
+	return rc;
+
+ctrl_clock_error:
+	dp_parser_put_clk_data(dev, core_power);
+exit:
+	return rc;
+}
+
+static int dp_parser_clock(struct dp_parser *parser)
+{
+	int rc = 0, i = 0;
+	int num_clk = 0;
+	int core_clk_index = 0, ctrl_clk_index = 0;
+	int core_clk_count = 0, ctrl_clk_count = 0;
+	const char *clk_name;
+	const char *core_clk = "core";
+	const char *ctrl_clk = "ctrl";
+	struct device *dev = &parser->pdev->dev;
+	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	core_power = &parser->mp[DP_CORE_PM];
+	ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	rc =  dp_parser_init_clk_data(parser);
+	if (rc) {
+		pr_err("failed to initialize power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	core_clk_count = core_power->num_clk;
+	ctrl_clk_count = ctrl_power->num_clk;
+
+	num_clk = core_clk_count + ctrl_clk_count;
+
+	for (i = 0; i < num_clk; i++) {
+		of_property_read_string_index(dev->of_node, "clock-names",
+				i, &clk_name);
+
+		if (dp_parser_check_prefix(core_clk, clk_name) &&
+				core_clk_index < core_clk_count) {
+			struct dss_clk *clk =
+				&core_power->clk_config[core_clk_index];
+			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			clk->type = DSS_CLK_AHB;
+			core_clk_index++;
+		} else if (dp_parser_check_prefix(ctrl_clk, clk_name) &&
+			   ctrl_clk_index < ctrl_clk_count) {
+			struct dss_clk *clk =
+				&ctrl_power->clk_config[ctrl_clk_index];
+			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			ctrl_clk_index++;
+
+			if (!strcmp(clk_name, "ctrl_link_clk") ||
+			    !strcmp(clk_name, "ctrl_pixel_clk") ||
+			    !strcmp(clk_name, "ctrl_crypto_clk"))
+				clk->type = DSS_CLK_PCLK;
+			else
+				clk->type = DSS_CLK_AHB;
+		}
+	}
+
+	pr_debug("clock parsing successful\n");
+
+exit:
+	return rc;
+}
+
+static int dp_parser_parse(struct dp_parser *parser)
+{
+	int rc = 0;
+
+	if (!parser) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = dp_parser_ctrl_res(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_aux(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_misc(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_clock(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_regulator(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_gpio(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_pinctrl(parser);
+err:
+	return rc;
+}
+
+struct dp_parser *dp_parser_get(struct platform_device *pdev)
+{
+	struct dp_parser *parser;
+
+	parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
+	if (!parser)
+		return ERR_PTR(-ENOMEM);
+
+	parser->parse = dp_parser_parse;
+	parser->pdev = pdev;
+
+	return parser;
+}
+
+void dp_parser_put(struct dp_parser *parser)
+{
+	int i = 0;
+	struct dss_module_power *power = NULL;
+
+	if (!parser) {
+		pr_err("invalid parser module\n");
+		return;
+	}
+
+	power = parser->mp;
+
+	for (i = 0; i < DP_MAX_PM; i++) {
+		struct dss_module_power *mp = &power[i];
+
+		devm_kfree(&parser->pdev->dev, mp->clk_config);
+		devm_kfree(&parser->pdev->dev, mp->vreg_config);
+		devm_kfree(&parser->pdev->dev, mp->gpio_config);
+	}
+
+	devm_kfree(&parser->pdev->dev, parser);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
new file mode 100644
index 0000000..fdcdd3a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. 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 _DP_PARSER_H_
+#define _DP_PARSER_H_
+
+#include <linux/sde_io_util.h>
+
+#define DP_LABEL "MDSS DP DISPLAY"
+#define AUX_CFG_LEN	10
+#define DP_MAX_PIXEL_CLK_KHZ	675000
+
+enum dp_pm_type {
+	DP_CORE_PM,
+	DP_CTRL_PM,
+	DP_PHY_PM,
+	DP_MAX_PM
+};
+
+static inline const char *dp_parser_pm_name(enum dp_pm_type module)
+{
+	switch (module) {
+	case DP_CORE_PM:	return "DP_CORE_PM";
+	case DP_CTRL_PM:	return "DP_CTRL_PM";
+	case DP_PHY_PM:		return "DP_PHY_PM";
+	default:		return "???";
+	}
+}
+
+/**
+ * struct dp_display_data  - display related device tree data.
+ *
+ * @ctrl_node: referece to controller device
+ * @phy_node:  reference to phy device
+ * @is_active: is the controller currently active
+ * @name: name of the display
+ * @display_type: type of the display
+ */
+struct dp_display_data {
+	struct device_node *ctrl_node;
+	struct device_node *phy_node;
+	bool is_active;
+	const char *name;
+	const char *display_type;
+};
+
+/**
+ * struct dp_ctrl_resource - controller's IO related data
+ *
+ * @ctrl_io: controller's mapped memory address
+ * @phy_io: phy's mapped memory address
+ * @ln_tx0_io: USB-DP lane TX0's mapped memory address
+ * @ln_tx1_io: USB-DP lane TX1's mapped memory address
+ * @dp_cc_io: DP cc's mapped memory address
+ * @qfprom_io: qfprom's mapped memory address
+ * @dp_pll_io: DP PLL mapped memory address
+ * @hdcp_io: hdcp's mapped memory address
+ */
+struct dp_io {
+	struct dss_io_data ctrl_io;
+	struct dss_io_data phy_io;
+	struct dss_io_data ln_tx0_io;
+	struct dss_io_data ln_tx1_io;
+	struct dss_io_data dp_cc_io;
+	struct dss_io_data qfprom_io;
+	struct dss_io_data dp_pll_io;
+	struct dss_io_data hdcp_io;
+};
+
+/**
+ * struct dp_pinctrl - DP's pin control
+ *
+ * @pin: pin-controller's instance
+ * @state_active: active state pin control
+ * @state_hpd_active: hpd active state pin control
+ * @state_suspend: suspend state pin control
+ */
+struct dp_pinctrl {
+	struct pinctrl *pin;
+	struct pinctrl_state *state_active;
+	struct pinctrl_state *state_hpd_active;
+	struct pinctrl_state *state_suspend;
+};
+
+/**
+ * struct dp_parser - DP parser's data exposed to clients
+ *
+ * @pdev: platform data of the client
+ * @mp: gpio, regulator and clock related data
+ * @pinctrl: pin-control related data
+ * @ctrl_resouce: controller's register address realated data
+ * @disp_data: controller's display related data
+ * @parse: function to be called by client to parse device tree.
+ */
+struct dp_parser {
+	struct platform_device *pdev;
+	struct dss_module_power mp[DP_MAX_PM];
+	struct dp_pinctrl pinctrl;
+	struct dp_io io;
+	struct dp_display_data disp_data;
+
+	u8 l_map[4];
+	u32 aux_cfg[AUX_CFG_LEN];
+	u32 max_pclk_khz;
+
+	int (*parse)(struct dp_parser *parser);
+};
+
+/**
+ * dp_parser_get() - get the DP's device tree parser module
+ *
+ * @pdev: platform data of the client
+ * return: pointer to dp_parser structure.
+ *
+ * This function provides client capability to parse the
+ * device tree and populate the data structures. The data
+ * related to clock, regulators, pin-control and other
+ * can be parsed using this module.
+ */
+struct dp_parser *dp_parser_get(struct platform_device *pdev);
+
+/**
+ * dp_parser_put() - cleans the dp_parser module
+ *
+ * @parser: pointer to the parser's data.
+ */
+void dp_parser_put(struct dp_parser *parser);
+#endif
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
new file mode 100644
index 0000000..54d4a10
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. 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.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/clk.h>
+#include "dp_power.h"
+
+struct dp_power_private {
+	struct dp_parser *parser;
+	struct platform_device *pdev;
+	struct clk *pixel_clk_rcg;
+	struct clk *pixel_parent;
+
+	struct dp_power dp_power;
+
+	bool core_clks_on;
+	bool link_clks_on;
+};
+
+static int dp_power_regulator_init(struct dp_power_private *power)
+{
+	int rc = 0, i = 0, j = 0;
+	struct platform_device *pdev;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+	pdev = power->pdev;
+
+	for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
+		rc = msm_dss_config_vreg(&pdev->dev,
+			parser->mp[i].vreg_config,
+			parser->mp[i].num_vreg, 1);
+		if (rc) {
+			pr_err("failed to init vregs for %s\n",
+				dp_parser_pm_name(i));
+			for (j = i - 1; j >= DP_CORE_PM; j--) {
+				msm_dss_config_vreg(&pdev->dev,
+				parser->mp[j].vreg_config,
+				parser->mp[j].num_vreg, 0);
+			}
+
+			goto error;
+		}
+	}
+error:
+	return rc;
+}
+
+static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
+{
+	int rc = 0, i = 0, j = 0;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+
+	for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+		rc = msm_dss_enable_vreg(
+			parser->mp[i].vreg_config,
+			parser->mp[i].num_vreg, enable);
+		if (rc) {
+			pr_err("failed to '%s' vregs for %s\n",
+					enable ? "enable" : "disable",
+					dp_parser_pm_name(i));
+			if (enable) {
+				for (j = i-1; j >= DP_CORE_PM; j--) {
+					msm_dss_enable_vreg(
+					parser->mp[j].vreg_config,
+					parser->mp[j].num_vreg, 0);
+				}
+			}
+			goto error;
+		}
+	}
+error:
+	return rc;
+}
+
+static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
+{
+	int rc = -EFAULT;
+	struct pinctrl_state *pin_state;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+
+	if (IS_ERR_OR_NULL(parser->pinctrl.pin))
+		return PTR_ERR(parser->pinctrl.pin);
+
+	pin_state = active ? parser->pinctrl.state_active
+				: parser->pinctrl.state_suspend;
+	if (!IS_ERR_OR_NULL(pin_state)) {
+		rc = pinctrl_select_state(parser->pinctrl.pin,
+				pin_state);
+		if (rc)
+			pr_err("can not set %s pins\n",
+			       active ? "dp_active"
+			       : "dp_sleep");
+	} else {
+		pr_err("invalid '%s' pinstate\n",
+		       active ? "dp_active"
+		       : "dp_sleep");
+	}
+
+	return rc;
+}
+
+static int dp_power_clk_init(struct dp_power_private *power, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *core, *ctrl;
+	struct device *dev;
+
+	core = &power->parser->mp[DP_CORE_PM];
+	ctrl = &power->parser->mp[DP_CTRL_PM];
+
+	dev = &power->pdev->dev;
+
+	if (!core || !ctrl) {
+		pr_err("invalid power_data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (enable) {
+		rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
+		if (rc) {
+			pr_err("failed to get %s clk. err=%d\n",
+				dp_parser_pm_name(DP_CORE_PM), rc);
+			goto exit;
+		}
+
+		rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk);
+		if (rc) {
+			pr_err("failed to get %s clk. err=%d\n",
+				dp_parser_pm_name(DP_CTRL_PM), rc);
+			goto ctrl_get_error;
+		}
+
+		power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg");
+		if (IS_ERR(power->pixel_clk_rcg)) {
+			pr_debug("Unable to get DP pixel clk RCG\n");
+			power->pixel_clk_rcg = NULL;
+		}
+
+		power->pixel_parent = devm_clk_get(dev, "pixel_parent");
+		if (IS_ERR(power->pixel_parent)) {
+			pr_debug("Unable to get DP pixel RCG parent\n");
+			power->pixel_parent = NULL;
+		}
+	} else {
+		if (power->pixel_parent)
+			devm_clk_put(dev, power->pixel_parent);
+
+		if (power->pixel_clk_rcg)
+			devm_clk_put(dev, power->pixel_clk_rcg);
+
+		msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk);
+		msm_dss_put_clk(core->clk_config, core->num_clk);
+	}
+
+	return rc;
+
+ctrl_get_error:
+	msm_dss_put_clk(core->clk_config, core->num_clk);
+exit:
+	return rc;
+}
+
+static int dp_power_clk_set_rate(struct dp_power_private *power,
+		enum dp_pm_type module, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *mp;
+
+	if (!power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mp = &power->parser->mp[module];
+
+	if (enable) {
+		rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+		if (rc) {
+			pr_err("failed to set clks rate.\n");
+			goto exit;
+		}
+
+		rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1);
+		if (rc) {
+			pr_err("failed to enable clks\n");
+			goto exit;
+		}
+	} else {
+		rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0);
+		if (rc) {
+			pr_err("failed to disable clks\n");
+				goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+
+static int dp_power_clk_enable(struct dp_power *dp_power,
+		enum dp_pm_type pm_type, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *mp;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	mp = &power->parser->mp[pm_type];
+
+	if ((pm_type != DP_CORE_PM) && (pm_type != DP_CTRL_PM)) {
+		pr_err("unsupported power module: %s\n",
+				dp_parser_pm_name(pm_type));
+		return -EINVAL;
+	}
+
+	if (enable) {
+		if ((pm_type == DP_CORE_PM)
+			&& (power->core_clks_on)) {
+			pr_debug("core clks already enabled\n");
+			return 0;
+		}
+
+		if ((pm_type == DP_CTRL_PM)
+			&& (power->link_clks_on)) {
+			pr_debug("links clks already enabled\n");
+			return 0;
+		}
+
+		if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) {
+			pr_debug("Need to enable core clks before link clks\n");
+
+			rc = dp_power_clk_set_rate(power, pm_type, enable);
+			if (rc) {
+				pr_err("failed to enable clks: %s. err=%d\n",
+					dp_parser_pm_name(DP_CORE_PM), rc);
+				goto error;
+			} else {
+				power->core_clks_on = true;
+			}
+		}
+	}
+
+	rc = dp_power_clk_set_rate(power, pm_type, enable);
+	if (rc) {
+		pr_err("failed to '%s' clks for: %s. err=%d\n",
+			enable ? "enable" : "disable",
+			dp_parser_pm_name(pm_type), rc);
+			goto error;
+	}
+
+	if (pm_type == DP_CORE_PM)
+		power->core_clks_on = enable;
+	else
+		power->link_clks_on = enable;
+
+	pr_debug("%s clocks for %s\n",
+			enable ? "enable" : "disable",
+			dp_parser_pm_name(pm_type));
+	pr_debug("link_clks:%s core_clks:%s\n",
+		power->link_clks_on ? "on" : "off",
+		power->core_clks_on ? "on" : "off");
+error:
+	return rc;
+}
+
+static int dp_power_request_gpios(struct dp_power_private *power)
+{
+	int rc = 0, i;
+	struct device *dev;
+	struct dss_module_power *mp;
+	static const char * const gpio_names[] = {
+		"aux_enable", "aux_sel", "usbplug_cc",
+	};
+
+	if (!power) {
+		pr_err("invalid power data\n");
+		return -EINVAL;
+	}
+
+	dev = &power->pdev->dev;
+	mp = &power->parser->mp[DP_CORE_PM];
+
+	for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+		unsigned int gpio = mp->gpio_config[i].gpio;
+
+		if (gpio_is_valid(gpio)) {
+			rc = devm_gpio_request(dev, gpio, gpio_names[i]);
+			if (rc) {
+				pr_err("request %s gpio failed, rc=%d\n",
+					       gpio_names[i], rc);
+				goto error;
+			}
+		}
+	}
+	return 0;
+error:
+	for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+		unsigned int gpio = mp->gpio_config[i].gpio;
+
+		if (gpio_is_valid(gpio))
+			gpio_free(gpio);
+	}
+	return rc;
+}
+
+static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
+{
+	return !!strnstr(gpio1, gpio2, strlen(gpio1));
+}
+
+static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
+{
+	int i;
+	struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
+	struct dss_gpio *config = mp->gpio_config;
+
+	for (i = 0; i < mp->num_gpio; i++) {
+		if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
+			config->value = flip;
+
+		if (gpio_is_valid(config->gpio)) {
+			pr_debug("gpio %s, value %d\n", config->gpio_name,
+				config->value);
+
+			if (dp_power_find_gpio(config->gpio_name, "aux-en") ||
+			    dp_power_find_gpio(config->gpio_name, "aux-sel"))
+				gpio_direction_output(config->gpio,
+					config->value);
+			else
+				gpio_set_value(config->gpio, config->value);
+
+		}
+		config++;
+	}
+}
+
+static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
+					bool enable)
+{
+	int rc = 0, i;
+	struct dss_module_power *mp;
+	struct dss_gpio *config;
+
+	mp = &power->parser->mp[DP_CORE_PM];
+	config = mp->gpio_config;
+
+	if (enable) {
+		rc = dp_power_request_gpios(power);
+		if (rc) {
+			pr_err("gpio request failed\n");
+			return rc;
+		}
+
+		dp_power_set_gpio(power, flip);
+	} else {
+		for (i = 0; i < mp->num_gpio; i++) {
+			gpio_set_value(config[i].gpio, 0);
+			gpio_free(config[i].gpio);
+		}
+	}
+
+	return 0;
+}
+
+static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	if (power->pixel_clk_rcg && power->pixel_parent)
+		clk_set_parent(power->pixel_clk_rcg, power->pixel_parent);
+exit:
+	return rc;
+}
+
+static int dp_power_init(struct dp_power *dp_power, bool flip)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	rc = dp_power_regulator_ctrl(power, true);
+	if (rc) {
+		pr_err("failed to enable regulators\n");
+		goto exit;
+	}
+
+	rc = dp_power_pinctrl_set(power, true);
+	if (rc) {
+		pr_err("failed to set pinctrl state\n");
+		goto err_pinctrl;
+	}
+
+	rc = dp_power_config_gpios(power, flip, true);
+	if (rc) {
+		pr_err("failed to enable gpios\n");
+		goto err_gpio;
+	}
+
+	rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
+	if (rc) {
+		pr_err("failed to enable DP core clocks\n");
+		goto err_clk;
+	}
+
+	return 0;
+
+err_clk:
+	dp_power_config_gpios(power, flip, false);
+err_gpio:
+	dp_power_pinctrl_set(power, false);
+err_pinctrl:
+	dp_power_regulator_ctrl(power, false);
+exit:
+	return rc;
+}
+
+static int dp_power_deinit(struct dp_power *dp_power)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+	dp_power_config_gpios(power, false, false);
+	dp_power_pinctrl_set(power, false);
+	dp_power_regulator_ctrl(power, false);
+exit:
+	return rc;
+}
+
+struct dp_power *dp_power_get(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+	struct dp_power *dp_power;
+
+	if (!parser) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
+	if (!power) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	power->parser = parser;
+	power->pdev = parser->pdev;
+
+	dp_power = &power->dp_power;
+
+	dp_power->init = dp_power_init;
+	dp_power->deinit = dp_power_deinit;
+	dp_power->clk_enable = dp_power_clk_enable;
+	dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
+
+	rc = dp_power_regulator_init(power);
+	if (rc) {
+		pr_err("failed to init regulators\n");
+		goto error;
+	}
+
+	rc = dp_power_clk_init(power, true);
+	if (rc) {
+		pr_err("failed to init clocks\n");
+		goto error;
+	}
+
+	return dp_power;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_power_put(struct dp_power *dp_power)
+{
+	struct dp_power_private *power = container_of(dp_power,
+			struct dp_power_private, dp_power);
+
+	(void)dp_power_clk_init(power, false);
+	devm_kfree(&power->pdev->dev, power);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h
new file mode 100644
index 0000000..3d71695
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. 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 _DP_POWER_H_
+#define _DP_POWER_H_
+
+#include "dp_parser.h"
+
+/**
+ * sruct dp_power - DisplayPort's power related data
+ *
+ * @init: initializes the regulators/core clocks/GPIOs/pinctrl
+ * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
+ * @clk_enable: enable/disable the DP clocks
+ * @set_pixel_clk_parent: set the parent of DP pixel clock
+ */
+struct dp_power {
+	int (*init)(struct dp_power *power, bool flip);
+	int (*deinit)(struct dp_power *power);
+	int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
+				bool enable);
+	int (*set_pixel_clk_parent)(struct dp_power *power);
+};
+
+/**
+ * dp_power_get() - configure and get the DisplayPort power module data
+ *
+ * @parser: instance of parser module
+ * return: pointer to allocated power module data
+ *
+ * This API will configure the DisplayPort's power module and provides
+ * methods to be called by the client to configure the power related
+ * modueles.
+ */
+struct dp_power *dp_power_get(struct dp_parser *parser);
+
+/**
+ * dp_power_put() - release the power related resources
+ *
+ * @power: pointer to the power module's data
+ */
+void dp_power_put(struct dp_power *power);
+#endif /* _DP_POWER_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index dcf3c08..a3a3d28 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3054,6 +3054,11 @@
 			state->crtc_h);
 		seq_printf(s, "\tmultirect: mode: %d index: %d\n",
 			pstate->multirect_mode, pstate->multirect_index);
+
+		seq_printf(s, "\texcl_rect: x:%4d y:%4d w:%4d h:%4d\n",
+			pstate->excl_rect.x, pstate->excl_rect.y,
+			pstate->excl_rect.w, pstate->excl_rect.h);
+
 		seq_puts(s, "\n");
 	}
 
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3a6de75..ad207d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2447,7 +2447,7 @@
 		   pstate->excl_rect.h != old_pstate->excl_rect.h ||
 		   pstate->excl_rect.x != old_pstate->excl_rect.x ||
 		   pstate->excl_rect.y != old_pstate->excl_rect.y) {
-		SDE_DEBUG_PLANE(psde, "excl rect updated\n");
+		SDE_DEBUG_PLANE(psde, "excl_rect updated\n");
 		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
 	}
 
@@ -2660,6 +2660,9 @@
 				(char *)&fmt->base.pixel_format);
 			ret = -EINVAL;
 		}
+		SDE_DEBUG_PLANE(psde, "excl_rect: {%d,%d,%d,%d}\n",
+				pstate->excl_rect.x, pstate->excl_rect.y,
+				pstate->excl_rect.w, pstate->excl_rect.h);
 	}
 
 modeset_update:
@@ -3480,20 +3483,24 @@
 	}
 
 	if (!usr_ptr) {
-		SDE_DEBUG_PLANE(psde, "excl rect data removed\n");
+		SDE_DEBUG_PLANE(psde, "invalid  excl_rect user data\n");
 		return;
 	}
 
 	if (copy_from_user(&excl_rect_v1, usr_ptr, sizeof(excl_rect_v1))) {
-		SDE_ERROR_PLANE(psde, "failed to copy excl rect data\n");
+		SDE_ERROR_PLANE(psde, "failed to copy excl_rect data\n");
 		return;
 	}
 
 	/* populate from user space */
 	pstate->excl_rect.x = excl_rect_v1.x1;
 	pstate->excl_rect.y = excl_rect_v1.y1;
-	pstate->excl_rect.w = excl_rect_v1.x2 - excl_rect_v1.x1 + 1;
-	pstate->excl_rect.h = excl_rect_v1.y2 - excl_rect_v1.y1 + 1;
+	pstate->excl_rect.w = excl_rect_v1.x2 - excl_rect_v1.x1;
+	pstate->excl_rect.h = excl_rect_v1.y2 - excl_rect_v1.y1;
+
+	SDE_DEBUG_PLANE(psde, "excl_rect: {%d,%d,%d,%d}\n",
+			pstate->excl_rect.x, pstate->excl_rect.y,
+			pstate->excl_rect.w, pstate->excl_rect.h);
 }
 
 static int sde_plane_atomic_set_property(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index da68139..38bf21f 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -16,9 +16,9 @@
 
 #define MAX_CLIENT_NAME_LEN 128
 
-#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA	2000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA	6000000000
 #define SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA	0
-#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA	2000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA	6000000000
 #define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA	0
 
 #include <linux/sde_io_util.h>
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index 50710cd..cab7e0f 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -442,7 +442,7 @@
 	return rc;
 }
 
-static bool sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc)
+static int sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc)
 {
 	struct sde_rsc_client *client;
 	int rc = STATE_UPDATE_NOT_ALLOWED;
@@ -467,7 +467,7 @@
 	return rc;
 }
 
-static bool sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
+static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
 	struct sde_rsc_cmd_config *config,
 	struct sde_rsc_client *caller_client)
 {
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index d04ddb0..e157e7b 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -813,12 +813,12 @@
 
 	t = jiffies + msecs_to_jiffies(timeout);
 
-	while (!time_after(jiffies, t)) {
+	do {
 		kgsl_gmu_regread(device, offset, &value);
 		if ((value & mask) == expected_ret)
 			return 0;
 		cpu_relax();
-	}
+	} while (!time_after(jiffies, t));
 
 	return -EINVAL;
 }
@@ -952,7 +952,7 @@
 	int ret = 0;
 
 	if (!kgsl_gmu_isenabled(device))
-		return -ENODEV;
+		return 0;
 
 	kgsl_gmu_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, set_mask);
 
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index 63dbde0..17ee6e6 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -640,6 +640,9 @@
 	unsigned int *data = (unsigned int *)(buf + sizeof(*header));
 	int i, j;
 
+	if (!device->snapshot_legacy)
+		return 0;
+
 	if (remain < sizeof(*header)) {
 		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
 		return 0;
@@ -748,6 +751,9 @@
 	unsigned int read_sel;
 	int i, j;
 
+	if (!device->snapshot_legacy)
+		return 0;
+
 	/* Figure out how many registers we are going to dump */
 	for (i = 0; i < regs->num_sets; i++) {
 		int start = regs->regs[i * 2];
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 876b668..be379e3 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -291,6 +291,8 @@
 
 	/* Use CP Crash dumper to get GPU snapshot*/
 	bool snapshot_crashdumper;
+	/* Use HOST side register reads to get GPU snapshot*/
+	bool snapshot_legacy;
 
 	struct kobject snapshot_kobj;
 
diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c
index 30e1d7c..b05e18d 100644
--- a/drivers/gpu/msm/kgsl_hfi.c
+++ b/drivers/gpu/msm/kgsl_hfi.c
@@ -573,44 +573,41 @@
 	if (result)
 		return result;
 
-	if (boot_state == GMU_COLD_BOOT) {
-		major = adreno_dev->gpucore->gpmu_major;
-		minor = adreno_dev->gpucore->gpmu_minor;
+	major = adreno_dev->gpucore->gpmu_major;
+	minor = adreno_dev->gpucore->gpmu_minor;
+	result = hfi_get_fw_version(gmu,
+			FW_VERSION(major, minor), &ver);
+	if (result)
+		dev_err(dev, "Failed to get FW version via HFI\n");
 
-		result = hfi_get_fw_version(gmu,
-				FW_VERSION(major, minor), &ver);
-		if (result)
-			dev_err(dev, "Failed to get FW version via HFI\n");
+	gmu->ver = ver;
+	if (major != FW_VER_MAJOR(ver))
+		dev_err(dev, "FW version major %d error (expect %d)\n",
+				FW_VER_MAJOR(ver),
+				adreno_dev->gpucore->gpmu_major);
 
-		gmu->ver = ver;
-		if (major != FW_VER_MAJOR(ver))
-			dev_err(dev, "FW version major %d error (expect %d)\n",
-					FW_VER_MAJOR(ver),
-					adreno_dev->gpucore->gpmu_major);
+	if (minor > FW_VER_MINOR(ver))
+		dev_err(dev, "FW version minor %d error (expect %d)\n",
+				FW_VER_MINOR(ver),
+				adreno_dev->gpucore->gpmu_minor);
 
-		if (minor > FW_VER_MINOR(ver))
-			dev_err(dev, "FW version minor %d error (expect %d)\n",
-					FW_VER_MINOR(ver),
-					adreno_dev->gpucore->gpmu_minor);
+	result = hfi_send_perftbl(gmu);
+	if (result)
+		return result;
 
-		result = hfi_send_perftbl(gmu);
-		if (result)
-			return result;
+	result = hfi_send_bwtbl(gmu);
+	if (result)
+		return result;
 
-		result = hfi_send_bwtbl(gmu);
-		if (result)
-			return result;
-
-		/*
-		 * FW is not ready for LM configuration
-		 * without powering on GPU.
-		 */
-		/*
-		 * result = hfi_send_lmconfig(gmu);
-		 * if (result)
-		 * return result;
-		 */
-	}
+	/*
+	 * FW is not ready for LM configuration
+	 * without powering on GPU.
+	 */
+	/*
+	 * result = hfi_send_lmconfig(gmu);
+	 * if (result)
+	 * return result;
+	 */
 
 	set_bit(GMU_HFI_ON, &gmu->flags);
 	return 0;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 40d239c..7cbda72 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -875,6 +875,25 @@
 	return snprintf(buf, PAGE_SIZE, "%lu\n", timestamp);
 }
 
+static ssize_t snapshot_legacy_show(struct kgsl_device *device, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", device->snapshot_legacy);
+}
+
+static ssize_t snapshot_legacy_store(struct kgsl_device *device,
+	const char *buf, size_t count)
+{
+	unsigned int val = 0;
+	int ret;
+
+	ret = kgsl_sysfs_store(buf, &val);
+
+	if (!ret && device)
+		device->snapshot_legacy = (bool)val;
+
+	return (ssize_t) ret < 0 ? ret : count;
+}
+
 static struct bin_attribute snapshot_attr = {
 	.attr.name = "dump",
 	.attr.mode = 0444,
@@ -894,6 +913,8 @@
 static SNAPSHOT_ATTR(force_panic, 0644, force_panic_show, force_panic_store);
 static SNAPSHOT_ATTR(snapshot_crashdumper, 0644, snapshot_crashdumper_show,
 	snapshot_crashdumper_store);
+static SNAPSHOT_ATTR(snapshot_legacy, 0644, snapshot_legacy_show,
+	snapshot_legacy_store);
 
 static ssize_t snapshot_sysfs_show(struct kobject *kobj,
 	struct attribute *attr, char *buf)
@@ -975,6 +996,7 @@
 	device->snapshot_faultcount = 0;
 	device->force_panic = 0;
 	device->snapshot_crashdumper = 1;
+	device->snapshot_legacy = 0;
 
 	ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
 		&device->dev->kobj, "snapshot");
@@ -1000,6 +1022,12 @@
 
 	ret  = sysfs_create_file(&device->snapshot_kobj,
 			&attr_snapshot_crashdumper.attr);
+	if (ret)
+		goto done;
+
+	ret  = sysfs_create_file(&device->snapshot_kobj,
+			&attr_snapshot_legacy.attr);
+
 done:
 	return ret;
 }
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 0bdcc99..a50e901 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -616,6 +616,15 @@
 	  This enables bare minimum support of power management at platform level.
 	  i.e WFI
 
+config MSM_QBT1000
+	bool "QBT1000 Ultrasonic Fingerprint Sensor"
+	help
+	  This driver provides services for configuring the fingerprint
+	  sensor hardware and for communicating with the trusted app which
+	  uses it. It enables clocks and provides commands for loading
+	  trusted apps, unloading them and marshalling buffers to the
+	  trusted fingerprint app.
+
 config APSS_CORE_EA
 	depends on CPU_FREQ && PM_OPP
 	bool "Qualcomm Technology Inc specific power aware driver"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 9d175cd..9a7262e 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -65,6 +65,7 @@
        obj-y += ramdump.o
 endif
 obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
+obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
 obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
 obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
 obj-$(CONFIG_MSM_IDLE_STATS)	+= lpm-stats.o
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
new file mode 100644
index 0000000..67a5e05
--- /dev/null
+++ b/drivers/soc/qcom/qbt1000.c
@@ -0,0 +1,1207 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. 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.
+ */
+#define DEBUG
+#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <uapi/linux/qbt1000.h>
+#include <soc/qcom/scm.h>
+#include "../../misc/qseecom_kernel.h"
+
+#define QBT1000_DEV "qbt1000"
+#define QBT1000_IN_DEV_NAME "qbt1000_key_input"
+#define QBT1000_IN_DEV_VERSION 0x0100
+#define MAX_FW_EVENTS 128
+#define FP_APP_CMD_RX_IPC 132
+#define FW_MAX_IPC_MSG_DATA_SIZE 0x500
+#define IPC_MSG_ID_CBGE_REQUIRED 29
+
+/*
+ * shared buffer size - init with max value,
+ * user space will provide new value upon tz app load
+ */
+static uint32_t g_app_buf_size = SZ_256K;
+static char const *const FP_APP_NAME = "fingerpr";
+
+struct finger_detect_gpio {
+	int gpio;
+	int active_low;
+	int irq;
+	struct work_struct work;
+	unsigned int key_code;
+	int power_key_enabled;
+	int last_gpio_state;
+	int event_reported;
+};
+
+struct fw_event_desc {
+	enum qbt1000_fw_event ev;
+};
+
+struct fw_ipc_info {
+	int gpio;
+	int irq;
+};
+
+struct qbt1000_drvdata {
+	struct class	*qbt1000_class;
+	struct cdev	qbt1000_cdev;
+	struct device	*dev;
+	char		*qbt1000_node;
+	struct clk	**clocks;
+	unsigned int	clock_count;
+	uint8_t		clock_state;
+	unsigned int	root_clk_idx;
+	unsigned int	frequency;
+	atomic_t	available;
+	struct mutex	mutex;
+	struct mutex	fw_events_mutex;
+	struct input_dev	*in_dev;
+	struct fw_ipc_info	fw_ipc;
+	struct finger_detect_gpio	fd_gpio;
+	DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS);
+	wait_queue_head_t read_wait_queue;
+	struct qseecom_handle *app_handle;
+	struct qseecom_handle *fp_app_handle;
+};
+
+/*
+ * struct fw_ipc_cmd -
+ *      used to store IPC commands to/from firmware
+ * @status - indicates whether sending/getting the IPC message was successful
+ * @msg_type - the type of IPC message
+ * @msg_len - the length of the message data
+ * @resp_needed - whether a response is needed for this message
+ * @msg_data - any extra data associated with the message
+ */
+struct fw_ipc_cmd {
+	uint32_t status;
+	uint32_t numMsgs;
+	uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE];
+};
+
+struct fw_ipc_header {
+	uint32_t msg_type;
+	uint32_t msg_len;
+	uint32_t resp_needed;
+};
+
+/*
+ * struct ipc_msg_type_to_fw_event -
+ *      entry in mapping between an IPC message type to a firmware event
+ * @msg_type - IPC message type, as reported by firmware
+ * @fw_event - corresponding firmware event code to report to driver client
+ */
+struct ipc_msg_type_to_fw_event {
+	uint32_t msg_type;
+	enum qbt1000_fw_event fw_event;
+};
+
+/* mapping between firmware IPC message types to HLOS firmware events */
+struct ipc_msg_type_to_fw_event g_msg_to_event[] = {
+		{IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED}
+};
+
+/**
+ * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
+ *                         aligns buffer lengths
+ * @hdl:	index of qseecom_handle
+ * @cmd:	req buffer - set to qseecom_handle.sbuf
+ * @cmd_len:	ptr to req buffer len
+ * @rsp:	rsp buffer - set to qseecom_handle.sbuf + offset
+ * @rsp_len:	ptr to rsp buffer len
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int get_cmd_rsp_buffers(struct qseecom_handle *hdl,
+	void **cmd,
+	uint32_t *cmd_len,
+	void **rsp,
+	uint32_t *rsp_len)
+{
+	/* 64 bytes alignment for QSEECOM */
+	*cmd_len = ALIGN(*cmd_len, 64);
+	*rsp_len = ALIGN(*rsp_len, 64);
+
+	if (((uint64_t)*rsp_len + (uint64_t)*cmd_len)
+			> (uint64_t)g_app_buf_size) {
+		pr_err("buffer too small to hold cmd=%d and rsp=%d\n",
+			*cmd_len, *rsp_len);
+		return -ENOMEM;
+	}
+
+	*cmd = hdl->sbuf;
+	*rsp = hdl->sbuf + *cmd_len;
+	return 0;
+}
+
+/**
+ * send_tz_cmd() - Function sends a command to TZ
+ *
+ * @drvdata: pointer to driver data
+ * @app_handle: handle to tz app
+ * @is_user_space: 1 if the cmd buffer is in user space, 0
+ *          otherwise
+ * @cmd: command buffer to send
+ * @cmd_len: length of the command buffer
+ * @rsp: output, will be set to location of response buffer
+ * @rsp_len: max size of response
+ *
+ * Return: 0 on success.
+ */
+static int send_tz_cmd(struct qbt1000_drvdata *drvdata,
+	struct qseecom_handle *app_handle,
+	int is_user_space,
+	void *cmd, uint32_t cmd_len,
+	void **rsp, uint32_t rsp_len)
+{
+	int rc = 0;
+	void *aligned_cmd;
+	void *aligned_rsp;
+	uint32_t aligned_cmd_len;
+	uint32_t aligned_rsp_len;
+
+	/* init command and response buffers and align lengths */
+	aligned_cmd_len = cmd_len;
+	aligned_rsp_len = rsp_len;
+
+	rc = get_cmd_rsp_buffers(app_handle,
+		(void **)&aligned_cmd,
+		&aligned_cmd_len,
+		(void **)&aligned_rsp,
+		&aligned_rsp_len);
+
+	if (rc != 0)
+		goto end;
+
+	if (!aligned_cmd) {
+		dev_err(drvdata->dev, "%s: Null command buffer\n",
+			__func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (aligned_cmd - cmd + cmd_len > g_app_buf_size) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	if (is_user_space) {
+		rc = copy_from_user(aligned_cmd, (void __user *)cmd,
+				cmd_len);
+		if (rc != 0) {
+			pr_err("failure to copy user space buf %d\n", rc);
+			rc = -EFAULT;
+			goto end;
+		}
+	} else
+		memcpy(aligned_cmd, cmd, cmd_len);
+
+	/* send cmd to TZ */
+	rc = qseecom_send_command(app_handle,
+		aligned_cmd,
+		aligned_cmd_len,
+		aligned_rsp,
+		aligned_rsp_len);
+
+	if (rc != 0) {
+		pr_err("failure to send tz cmd %d\n", rc);
+		goto end;
+	}
+
+	*rsp = aligned_rsp;
+
+end:
+	return rc;
+}
+
+/**
+ * qbt1000_open() - Function called when user space opens device.
+ * Successful if driver not currently open.
+ * @inode:	ptr to inode object
+ * @file:	ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_open(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+
+	struct qbt1000_drvdata *drvdata = container_of(inode->i_cdev,
+						   struct qbt1000_drvdata,
+						   qbt1000_cdev);
+	file->private_data = drvdata;
+
+	pr_debug("qbt1000_open begin\n");
+	/* disallowing concurrent opens */
+	if (!atomic_dec_and_test(&drvdata->available)) {
+		atomic_inc(&drvdata->available);
+		rc = -EBUSY;
+	}
+
+	pr_debug("qbt1000_open end : %d\n", rc);
+	return rc;
+}
+
+/**
+ * qbt1000_release() - Function called when user space closes device.
+
+ * @inode:	ptr to inode object
+ * @file:	ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_release(struct inode *inode, struct file *file)
+{
+	struct qbt1000_drvdata *drvdata;
+
+	if (!file || !file->private_data) {
+		pr_err("qbt1000_release: NULL pointer passed");
+		return -EINVAL;
+	}
+	drvdata = file->private_data;
+	atomic_inc(&drvdata->available);
+	return 0;
+}
+
+/**
+ * qbt1000_ioctl() - Function called when user space calls ioctl.
+ * @file:	struct file - not used
+ * @cmd:	cmd identifier:QBT1000_LOAD_APP,QBT1000_UNLOAD_APP,
+ *              QBT1000_SEND_TZCMD
+ * @arg:	ptr to relevant structe: either qbt1000_app or
+ *              qbt1000_send_tz_cmd depending on which cmd is passed
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static long qbt1000_ioctl(
+		struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+	void __user *priv_arg = (void __user *)arg;
+	struct qbt1000_drvdata *drvdata;
+
+	if (!file || !file->private_data) {
+		pr_err("qbt1000_ioctl: NULL pointer passed");
+		return -EINVAL;
+	}
+
+	drvdata = file->private_data;
+
+	mutex_lock(&drvdata->mutex);
+
+	pr_debug("qbt1000_ioctl %d\n", cmd);
+
+	switch (cmd) {
+	case QBT1000_LOAD_APP:
+	{
+		struct qbt1000_app app;
+		struct qseecom_handle *app_handle;
+
+		if (copy_from_user(&app, priv_arg,
+			sizeof(app)) != 0) {
+			rc = -EFAULT;
+			pr_err("failed copy from user space-LOAD\n");
+			goto end;
+		}
+
+		if (!app.app_handle) {
+			dev_err(drvdata->dev, "%s: LOAD app_handle is null\n",
+				__func__);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		if (drvdata->app_handle) {
+			dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n",
+				__func__);
+			drvdata->fp_app_handle = 0;
+			rc = qseecom_shutdown_app(&drvdata->app_handle);
+			if (rc != 0) {
+				dev_err(drvdata->dev, "%s: LOAD current app failed to shutdown\n",
+					  __func__);
+				goto end;
+			}
+		}
+
+		pr_debug("app %s load before\n", app.name);
+
+		/* start the TZ app */
+		rc = qseecom_start_app(
+				&drvdata->app_handle, app.name, app.size);
+		if (rc == 0) {
+			g_app_buf_size = app.size;
+			rc = qseecom_set_bandwidth(drvdata->app_handle,
+				app.high_band_width == 1 ? true : false);
+			if (rc != 0) {
+				/* log error, allow to continue */
+				pr_err("App %s failed to set bw\n", app.name);
+			}
+		} else {
+			pr_err("app %s failed to load\n", app.name);
+			goto end;
+		}
+
+		/* copy a fake app handle to user */
+		app_handle = drvdata->app_handle ?
+				(struct qseecom_handle *)123456 : 0;
+		rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+			sizeof(*app.app_handle));
+
+		if (rc != 0) {
+			dev_err(drvdata->dev,
+				"%s: Failed copy 2us LOAD rc:%d\n",
+				 __func__, rc);
+			rc = -ENOMEM;
+			goto end;
+		}
+
+		pr_debug("app %s load after\n", app.name);
+
+		if (!strcmp(app.name, FP_APP_NAME))
+			drvdata->fp_app_handle = drvdata->app_handle;
+
+		break;
+	}
+	case QBT1000_UNLOAD_APP:
+	{
+		struct qbt1000_app app;
+		struct qseecom_handle *app_handle = 0;
+
+		if (copy_from_user(&app, priv_arg,
+			sizeof(app)) != 0) {
+			rc = -ENOMEM;
+			pr_err("failed copy from user space-UNLOAD\n");
+			goto end;
+		}
+
+		if (!app.app_handle) {
+			dev_err(drvdata->dev, "%s: UNLOAD app_handle is null\n",
+				__func__);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		rc = copy_from_user(&app_handle, app.app_handle,
+			sizeof(app_handle));
+
+		if (rc != 0) {
+			dev_err(drvdata->dev,
+				"%s: Failed copy from user space-UNLOAD handle rc:%d\n",
+				 __func__, rc);
+			rc = -ENOMEM;
+			goto end;
+		}
+
+		/* if the app hasn't been loaded already, return err */
+		if (!drvdata->app_handle) {
+			pr_err("app not loaded\n");
+			rc = -EINVAL;
+			goto end;
+		}
+
+		if (drvdata->fp_app_handle == drvdata->app_handle)
+			drvdata->fp_app_handle = 0;
+
+		/* set bw & shutdown the TZ app */
+		qseecom_set_bandwidth(drvdata->app_handle,
+			app.high_band_width == 1 ? true : false);
+		rc = qseecom_shutdown_app(&drvdata->app_handle);
+		if (rc != 0) {
+			pr_err("app failed to shutdown\n");
+			goto end;
+		}
+
+		/* copy the app handle (should be null) to user */
+		rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+			sizeof(*app.app_handle));
+
+		if (rc != 0) {
+			dev_err(drvdata->dev,
+				"%s: Failed copy 2us UNLOAD rc:%d\n",
+				 __func__, rc);
+			rc = -ENOMEM;
+			goto end;
+		}
+
+		break;
+	}
+	case QBT1000_SEND_TZCMD:
+	{
+		struct qbt1000_send_tz_cmd tzcmd;
+		void *rsp_buf;
+
+		if (copy_from_user(&tzcmd, priv_arg,
+			sizeof(tzcmd))
+				!= 0) {
+			rc = -EFAULT;
+			pr_err("failed copy from user space %d\n", rc);
+			goto end;
+		}
+
+		if (tzcmd.req_buf_len > g_app_buf_size ||
+			tzcmd.rsp_buf_len > g_app_buf_size) {
+			rc = -ENOMEM;
+			pr_err("invalid cmd buf len, req=%d, rsp=%d\n",
+				tzcmd.req_buf_len, tzcmd.rsp_buf_len);
+			goto end;
+		}
+
+		/* if the app hasn't been loaded already, return err */
+		if (!drvdata->app_handle) {
+			pr_err("app not loaded\n");
+			rc = -EINVAL;
+			goto end;
+		}
+
+		rc = send_tz_cmd(drvdata,
+			drvdata->app_handle, 1,
+			tzcmd.req_buf, tzcmd.req_buf_len,
+			&rsp_buf, tzcmd.rsp_buf_len);
+
+		if (rc < 0) {
+			pr_err("failure sending command to tz\n");
+			goto end;
+		}
+
+		/* copy rsp buf back to user space buffer */
+		rc = copy_to_user((void __user *)tzcmd.rsp_buf,
+			 rsp_buf, tzcmd.rsp_buf_len);
+		if (rc != 0) {
+			pr_err("failed copy 2us rc:%d bytes %d:\n",
+				rc, tzcmd.rsp_buf_len);
+			rc = -EFAULT;
+			goto end;
+		}
+
+		break;
+	}
+	case QBT1000_SET_FINGER_DETECT_KEY:
+	{
+		struct qbt1000_set_finger_detect_key set_fd_key;
+
+		if (copy_from_user(&set_fd_key, priv_arg,
+			sizeof(set_fd_key))
+				!= 0) {
+			rc = -EFAULT;
+			pr_err("failed copy from user space %d\n", rc);
+			goto end;
+		}
+
+		drvdata->fd_gpio.key_code = set_fd_key.key_code;
+
+		break;
+	}
+	case QBT1000_CONFIGURE_POWER_KEY:
+	{
+		struct qbt1000_configure_power_key power_key;
+
+		if (copy_from_user(&power_key, priv_arg,
+			sizeof(power_key))
+				!= 0) {
+			rc = -EFAULT;
+			pr_err("failed copy from user space %d\n", rc);
+			goto end;
+		}
+
+		drvdata->fd_gpio.power_key_enabled = power_key.enable;
+
+		break;
+	}
+	default:
+		pr_err("invalid cmd %d\n", cmd);
+		rc = -ENOIOCTLCMD;
+		goto end;
+	}
+
+end:
+	mutex_unlock(&drvdata->mutex);
+	return rc;
+}
+
+static int get_events_fifo_len_locked(struct qbt1000_drvdata *drvdata)
+{
+	int len;
+
+	mutex_lock(&drvdata->fw_events_mutex);
+	len = kfifo_len(&drvdata->fw_events);
+	mutex_unlock(&drvdata->fw_events_mutex);
+
+	return len;
+}
+
+static ssize_t qbt1000_read(struct file *filp, char __user *ubuf,
+		size_t cnt, loff_t *ppos)
+{
+	struct fw_event_desc fw_event;
+	struct qbt1000_drvdata *drvdata = filp->private_data;
+
+	if (cnt < sizeof(fw_event.ev))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->fw_events_mutex);
+
+	while (kfifo_len(&drvdata->fw_events) == 0) {
+		mutex_unlock(&drvdata->fw_events_mutex);
+
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		pr_debug("fw_events fifo: empty, waiting\n");
+
+		if (wait_event_interruptible(drvdata->read_wait_queue,
+			  (get_events_fifo_len_locked(drvdata) > 0)))
+			return -ERESTARTSYS;
+
+		mutex_lock(&drvdata->fw_events_mutex);
+	}
+
+	if (!kfifo_get(&drvdata->fw_events, &fw_event)) {
+		pr_debug("fw_events fifo: unexpectedly empty\n");
+
+		mutex_unlock(&drvdata->fw_events_mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&drvdata->fw_events_mutex);
+
+	pr_debug("fw_event: %d\n", (int)fw_event.ev);
+	return copy_to_user(ubuf, &fw_event.ev, sizeof(fw_event.ev));
+}
+
+static unsigned int qbt1000_poll(struct file *filp,
+	struct poll_table_struct *wait)
+{
+	struct qbt1000_drvdata *drvdata = filp->private_data;
+	unsigned int mask = 0;
+
+	poll_wait(filp, &drvdata->read_wait_queue, wait);
+
+	if (kfifo_len(&drvdata->fw_events) > 0)
+		mask |= (POLLIN | POLLRDNORM);
+
+	return mask;
+}
+
+static const struct file_operations qbt1000_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = qbt1000_ioctl,
+	.open = qbt1000_open,
+	.release = qbt1000_release,
+	.read = qbt1000_read,
+	.poll = qbt1000_poll
+};
+
+static int qbt1000_dev_register(struct qbt1000_drvdata *drvdata)
+{
+	dev_t dev_no;
+	int ret = 0;
+	size_t node_size;
+	char *node_name = QBT1000_DEV;
+	struct device *dev = drvdata->dev;
+	struct device *device;
+
+	node_size = strlen(node_name) + 1;
+
+	drvdata->qbt1000_node = devm_kzalloc(dev, node_size, GFP_KERNEL);
+	if (!drvdata->qbt1000_node) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	strlcpy(drvdata->qbt1000_node, node_name, node_size);
+
+	ret = alloc_chrdev_region(&dev_no, 0, 1, drvdata->qbt1000_node);
+	if (ret) {
+		pr_err("alloc_chrdev_region failed %d\n", ret);
+		goto err_alloc;
+	}
+
+	cdev_init(&drvdata->qbt1000_cdev, &qbt1000_fops);
+
+	drvdata->qbt1000_cdev.owner = THIS_MODULE;
+	ret = cdev_add(&drvdata->qbt1000_cdev, dev_no, 1);
+	if (ret) {
+		pr_err("cdev_add failed %d\n", ret);
+		goto err_cdev_add;
+	}
+
+	drvdata->qbt1000_class = class_create(THIS_MODULE,
+					   drvdata->qbt1000_node);
+	if (IS_ERR(drvdata->qbt1000_class)) {
+		ret = PTR_ERR(drvdata->qbt1000_class);
+		pr_err("class_create failed %d\n", ret);
+		goto err_class_create;
+	}
+
+	device = device_create(drvdata->qbt1000_class, NULL,
+			       drvdata->qbt1000_cdev.dev, drvdata,
+			       drvdata->qbt1000_node);
+	if (IS_ERR(device)) {
+		ret = PTR_ERR(device);
+		pr_err("device_create failed %d\n", ret);
+		goto err_dev_create;
+	}
+
+	return 0;
+err_dev_create:
+	class_destroy(drvdata->qbt1000_class);
+err_class_create:
+	cdev_del(&drvdata->qbt1000_cdev);
+err_cdev_add:
+	unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+err_alloc:
+	return ret;
+}
+
+/**
+ * qbt1000_create_input_device() - Function allocates an input
+ * device, configures it for key events and registers it
+ *
+ * @drvdata:	ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata)
+{
+	int rc = 0;
+
+	drvdata->in_dev = input_allocate_device();
+	if (drvdata->in_dev == NULL) {
+		dev_err(drvdata->dev, "%s: input_allocate_device() failed\n",
+			__func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	drvdata->in_dev->name = QBT1000_IN_DEV_NAME;
+	drvdata->in_dev->phys = NULL;
+	drvdata->in_dev->id.bustype = BUS_HOST;
+	drvdata->in_dev->id.vendor  = 0x0001;
+	drvdata->in_dev->id.product = 0x0001;
+	drvdata->in_dev->id.version = QBT1000_IN_DEV_VERSION;
+
+	drvdata->in_dev->evbit[0] = BIT_MASK(EV_KEY) |  BIT_MASK(EV_ABS);
+	drvdata->in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	drvdata->in_dev->keybit[BIT_WORD(KEY_HOMEPAGE)] |=
+		BIT_MASK(KEY_HOMEPAGE);
+	drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |=
+		BIT_MASK(KEY_CAMERA);
+	drvdata->in_dev->keybit[BIT_WORD(KEY_VOLUMEDOWN)] |=
+		BIT_MASK(KEY_VOLUMEDOWN);
+	drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |=
+		BIT_MASK(KEY_POWER);
+
+	input_set_abs_params(drvdata->in_dev, ABS_X,
+			     0,
+			     1000,
+			     0, 0);
+	input_set_abs_params(drvdata->in_dev, ABS_Y,
+			     0,
+			     1000,
+			     0, 0);
+
+	rc = input_register_device(drvdata->in_dev);
+	if (rc) {
+		dev_err(drvdata->dev, "%s: input_reg_dev() failed %d\n",
+			__func__, rc);
+		goto end;
+	}
+
+end:
+	if (rc)
+		input_free_device(drvdata->in_dev);
+	return rc;
+}
+
+static void purge_finger_events(struct qbt1000_drvdata *drvdata)
+{
+	int i, fifo_len;
+	struct fw_event_desc fw_event;
+
+	fifo_len = kfifo_len(&drvdata->fw_events);
+
+	for (i = 0; i < fifo_len; i++) {
+		if (!kfifo_get(&drvdata->fw_events, &fw_event))
+			pr_err("fw events fifo: could not remove oldest item\n");
+		else if (fw_event.ev != FW_EVENT_FINGER_DOWN
+					&& fw_event.ev != FW_EVENT_FINGER_UP)
+			kfifo_put(&drvdata->fw_events, fw_event);
+	}
+}
+
+static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
+{
+	int state;
+	struct fw_event_desc fw_event;
+
+	state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0)
+		^ drvdata->fd_gpio.active_low;
+
+	if (drvdata->fd_gpio.event_reported
+		  && state == drvdata->fd_gpio.last_gpio_state)
+		return;
+
+	pr_debug("gpio %d: report state %d\n", drvdata->fd_gpio.gpio, state);
+
+	drvdata->fd_gpio.event_reported = 1;
+	drvdata->fd_gpio.last_gpio_state = state;
+
+	if (drvdata->fd_gpio.key_code) {
+		input_event(drvdata->in_dev, EV_KEY,
+			drvdata->fd_gpio.key_code, !!state);
+		input_sync(drvdata->in_dev);
+	}
+
+	if (state && drvdata->fd_gpio.power_key_enabled) {
+		input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1);
+		input_sync(drvdata->in_dev);
+		input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0);
+		input_sync(drvdata->in_dev);
+	}
+
+	fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP);
+
+	mutex_lock(&drvdata->fw_events_mutex);
+
+	if (kfifo_is_full(&drvdata->fw_events)) {
+		struct fw_event_desc dummy_fw_event;
+
+		pr_warn("fw events fifo: full, dropping oldest item\n");
+		if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event))
+			pr_err("fw events fifo: could not remove oldest item\n");
+	}
+
+	purge_finger_events(drvdata);
+
+	if (!kfifo_put(&drvdata->fw_events, fw_event))
+		pr_err("fw events fifo: error adding item\n");
+
+	mutex_unlock(&drvdata->fw_events_mutex);
+	wake_up_interruptible(&drvdata->read_wait_queue);
+}
+
+static void qbt1000_gpio_work_func(struct work_struct *work)
+{
+	struct qbt1000_drvdata *drvdata =
+		container_of(work, struct qbt1000_drvdata, fd_gpio.work);
+
+	qbt1000_gpio_report_event(drvdata);
+
+	pm_relax(drvdata->dev);
+}
+
+static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id)
+{
+	struct qbt1000_drvdata *drvdata = dev_id;
+
+	if (irq != drvdata->fd_gpio.irq) {
+		pr_warn("invalid irq %d (expected %d)\n",
+			irq, drvdata->fd_gpio.irq);
+		return IRQ_HANDLED;
+	}
+
+	pm_stay_awake(drvdata->dev);
+	schedule_work(&drvdata->fd_gpio.work);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * qbt1000_ipc_irq_handler() - function processes IPC
+ * interrupts on its own thread
+ * @irq:	the interrupt that occurred
+ * @dev_id: pointer to the qbt1000_drvdata
+ *
+ * Return: IRQ_HANDLED when complete
+ */
+static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
+{
+	uint8_t *msg_buffer;
+	struct fw_ipc_cmd *rx_cmd;
+	struct fw_ipc_header *header;
+	int i, j;
+	uint32_t rxipc = FP_APP_CMD_RX_IPC;
+	struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id;
+	int rc = 0;
+	uint32_t retry_count = 10;
+
+	pm_stay_awake(drvdata->dev);
+
+	mutex_lock(&drvdata->mutex);
+
+	if (irq != drvdata->fw_ipc.irq) {
+		pr_warn("invalid irq %d (expected %d)\n",
+			irq, drvdata->fw_ipc.irq);
+		goto end;
+	}
+
+	pr_debug("firmware interrupt received (irq %d)\n", irq);
+
+	if (!drvdata->fp_app_handle)
+		goto end;
+
+	while (retry_count > 0) {
+		/*
+		 * send the TZ command to fetch the message from firmware
+		 * TZ will process the message if it can
+		 */
+		rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0,
+				&rxipc, sizeof(rxipc),
+				(void *)&rx_cmd, sizeof(*rx_cmd));
+		if (rc < 0) {
+			msleep(50); // sleep for 50ms before retry
+			retry_count -= 1;
+			continue;
+		} else {
+			pr_err("retry_count %d\n", retry_count);
+			break;
+		}
+	}
+
+	if (rc < 0) {
+		pr_err("failure sending tz cmd %d\n", rxipc);
+		goto end;
+	}
+
+	if (rx_cmd->status != 0) {
+		pr_err("tz command failed to complete\n");
+		goto end;
+	}
+
+	msg_buffer = rx_cmd->msg_data;
+
+	for (j = 0; j < rx_cmd->numMsgs; j++) {
+		header = (struct fw_ipc_header *) msg_buffer;
+		/*
+		 * given the IPC message type, search for a corresponding
+		 * event for the driver client. If found, add to the events
+		 * FIFO
+		 */
+		for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
+			if (g_msg_to_event[i].msg_type == header->msg_type) {
+				enum qbt1000_fw_event ev =
+						g_msg_to_event[i].fw_event;
+				struct fw_event_desc fw_ev_desc;
+
+				mutex_lock(&drvdata->fw_events_mutex);
+				pr_debug("fw events: add %d\n", (int) ev);
+				fw_ev_desc.ev = ev;
+
+				if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
+					pr_err("fw events: fifo full, drop event %d\n",
+						(int) ev);
+
+				mutex_unlock(&drvdata->fw_events_mutex);
+				break;
+			}
+		}
+		msg_buffer += sizeof(*header) + header->msg_len;
+	}
+	wake_up_interruptible(&drvdata->read_wait_queue);
+end:
+	mutex_unlock(&drvdata->mutex);
+	pm_relax(drvdata->dev);
+	return IRQ_HANDLED;
+}
+
+static int setup_fd_gpio_irq(struct platform_device *pdev,
+	struct qbt1000_drvdata *drvdata)
+{
+	int rc = 0;
+	int irq;
+	const char *desc = "qbt_finger_detect";
+
+	rc = devm_gpio_request_one(&pdev->dev, drvdata->fd_gpio.gpio,
+		GPIOF_IN, desc);
+
+	if (rc < 0) {
+		pr_err("failed to request gpio %d, error %d\n",
+			drvdata->fd_gpio.gpio, rc);
+		goto end;
+	}
+
+	irq = gpio_to_irq(drvdata->fd_gpio.gpio);
+	if (irq < 0) {
+		rc = irq;
+		pr_err("unable to get irq number for gpio %d, error %d\n",
+			drvdata->fd_gpio.gpio, rc);
+		goto end;
+	}
+
+	drvdata->fd_gpio.irq = irq;
+	INIT_WORK(&drvdata->fd_gpio.work, qbt1000_gpio_work_func);
+
+	rc = devm_request_any_context_irq(&pdev->dev, drvdata->fd_gpio.irq,
+		qbt1000_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+		desc, drvdata);
+
+	if (rc < 0) {
+		pr_err("unable to claim irq %d; error %d\n",
+			drvdata->fd_gpio.irq, rc);
+		goto end;
+	}
+
+end:
+	return rc;
+}
+
+static int setup_ipc_irq(struct platform_device *pdev,
+	struct qbt1000_drvdata *drvdata)
+{
+	int rc = 0;
+	const char *desc = "qbt_ipc";
+
+	drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio);
+	pr_debug("\nirq %d gpio %d\n",
+			drvdata->fw_ipc.irq, drvdata->fw_ipc.gpio);
+	if (drvdata->fw_ipc.irq < 0) {
+		rc = drvdata->fw_ipc.irq;
+		pr_err("no irq for gpio %d, error=%d\n",
+		  drvdata->fw_ipc.gpio, rc);
+		goto end;
+	}
+
+	rc = devm_gpio_request_one(&pdev->dev, drvdata->fw_ipc.gpio,
+			GPIOF_IN, desc);
+
+	if (rc < 0) {
+		pr_err("failed to request gpio %d, error %d\n",
+			drvdata->fw_ipc.gpio, rc);
+		goto end;
+	}
+
+	rc = devm_request_threaded_irq(&pdev->dev,
+		drvdata->fw_ipc.irq,
+		NULL,
+		qbt1000_ipc_irq_handler,
+		IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+		desc,
+		drvdata);
+
+	if (rc < 0) {
+		pr_err("failed to register for ipc irq %d, rc = %d\n",
+			drvdata->fw_ipc.irq, rc);
+		goto end;
+	}
+
+end:
+	return rc;
+}
+
+/**
+ * qbt1000_read_device_tree() - Function reads device tree
+ * properties into driver data
+ * @pdev:	ptr to platform device object
+ * @drvdata:	ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_read_device_tree(struct platform_device *pdev,
+	struct qbt1000_drvdata *drvdata)
+{
+	int rc = 0;
+	uint32_t rate;
+	int gpio;
+	enum of_gpio_flags flags;
+
+	/* read clock frequency */
+	if (of_property_read_u32(pdev->dev.of_node,
+		"clock-frequency", &rate) == 0) {
+		pr_debug("clk frequency %d\n", rate);
+		drvdata->frequency = rate;
+	}
+
+	/* read IPC gpio */
+	drvdata->fw_ipc.gpio = of_get_named_gpio(pdev->dev.of_node,
+		"qcom,ipc-gpio", 0);
+	if (drvdata->fw_ipc.gpio < 0) {
+		rc = drvdata->fw_ipc.gpio;
+		pr_err("ipc gpio not found, error=%d\n", rc);
+		goto end;
+	}
+
+	/**
+	 * TODO: Need to revisit after adding GPIO in DTSI- read
+	 * finger detect GPIO configuration
+	 */
+
+	gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+				"qcom,finger-detect-gpio", 0, &flags);
+	if (gpio < 0) {
+		pr_err("failed to get gpio flags\n");
+		rc = gpio;
+		goto end;
+	}
+
+	drvdata->fd_gpio.gpio = gpio;
+	drvdata->fd_gpio.active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+end:
+	return rc;
+}
+
+/**
+ * qbt1000_probe() - Function loads hardware config from device tree
+ * @pdev:	ptr to platform device object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qbt1000_drvdata *drvdata;
+	int rc = 0;
+
+	pr_debug("qbt1000_probe begin\n");
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	drvdata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, drvdata);
+
+	rc = qbt1000_read_device_tree(pdev, drvdata);
+	if (rc < 0)
+		goto end;
+
+	atomic_set(&drvdata->available, 1);
+
+	mutex_init(&drvdata->mutex);
+	mutex_init(&drvdata->fw_events_mutex);
+
+	rc = qbt1000_dev_register(drvdata);
+	if (rc < 0)
+		goto end;
+
+	INIT_KFIFO(drvdata->fw_events);
+	init_waitqueue_head(&drvdata->read_wait_queue);
+
+	rc = qbt1000_create_input_device(drvdata);
+	if (rc < 0)
+		goto end;
+
+	rc = setup_fd_gpio_irq(pdev, drvdata);
+	if (rc < 0)
+		goto end;
+
+	rc = setup_ipc_irq(pdev, drvdata);
+	if (rc < 0)
+		goto end;
+
+	rc = device_init_wakeup(&pdev->dev, 1);
+	if (rc < 0)
+		goto end;
+
+end:
+	pr_debug("qbt1000_probe end : %d\n", rc);
+	return rc;
+}
+
+static int qbt1000_remove(struct platform_device *pdev)
+{
+	struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	input_unregister_device(drvdata->in_dev);
+
+	mutex_destroy(&drvdata->mutex);
+	mutex_destroy(&drvdata->fw_events_mutex);
+
+	device_destroy(drvdata->qbt1000_class, drvdata->qbt1000_cdev.dev);
+	class_destroy(drvdata->qbt1000_class);
+	cdev_del(&drvdata->qbt1000_cdev);
+	unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	return 0;
+}
+
+static int qbt1000_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int rc = 0;
+	struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	/*
+	 * Returning an error code if driver currently making a TZ call.
+	 * Note: The purpose of this driver is to ensure that the clocks are on
+	 * while making a TZ call. Hence the clock check to determine if the
+	 * driver will allow suspend to occur.
+	 */
+	if (!mutex_trylock(&drvdata->mutex))
+		return -EBUSY;
+
+	if (drvdata->clock_state)
+		rc = -EBUSY;
+	else {
+		enable_irq_wake(drvdata->fd_gpio.irq);
+		enable_irq_wake(drvdata->fw_ipc.irq);
+	}
+
+	mutex_unlock(&drvdata->mutex);
+
+	return rc;
+}
+
+static int qbt1000_resume(struct platform_device *pdev)
+{
+	struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	disable_irq_wake(drvdata->fd_gpio.irq);
+	disable_irq_wake(drvdata->fw_ipc.irq);
+
+	return 0;
+}
+
+static const struct of_device_id qbt1000_match[] = {
+	{ .compatible = "qcom,qbt1000" },
+	{}
+};
+
+static struct platform_driver qbt1000_plat_driver = {
+	.probe = qbt1000_probe,
+	.remove = qbt1000_remove,
+	.suspend = qbt1000_suspend,
+	.resume = qbt1000_resume,
+	.driver = {
+		.name = "qbt1000",
+		.owner = THIS_MODULE,
+		.of_match_table = qbt1000_match,
+	},
+};
+
+module_platform_driver(qbt1000_plat_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QBT1000 driver");
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 82718c8..57f38d3 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -266,7 +266,6 @@
 			pd->total_domains = resp->total_domains;
 			if (!resp->total_domains) {
 				pr_err("No matching domains found\n");
-				rc = -EIO;
 				goto out;
 			}
 
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 7c1899e..7cf7779 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -382,6 +382,7 @@
 header-y += psci.h
 header-y += ptp_clock.h
 header-y += ptrace.h
+header-y += qbt1000.h
 header-y += qcedev.h
 header-y += qcota.h
 header-y += qnx4_fs.h
diff --git a/include/uapi/linux/qbt1000.h b/include/uapi/linux/qbt1000.h
new file mode 100644
index 0000000..a4f0dca
--- /dev/null
+++ b/include/uapi/linux/qbt1000.h
@@ -0,0 +1,99 @@
+#ifndef _UAPI_QBT1000_H_
+#define _UAPI_QBT1000_H_
+
+#define MAX_NAME_SIZE					 32
+
+/*
+ * enum qbt1000_commands -
+ *      enumeration of command options
+ * @QBT1000_LOAD_APP - cmd loads TZ app
+ * @QBT1000_UNLOAD_APP - cmd unloads TZ app
+ * @QBT1000_SEND_TZCMD - sends cmd to TZ app
+ * @QBT1000_SET_FINGER_DETECT_KEY - sets the input key to send on finger detect
+ * @QBT1000_CONFIGURE_POWER_KEY - enables/disables sending the power key on
+	finger down events
+*/
+enum qbt1000_commands {
+	QBT1000_LOAD_APP = 100,
+	QBT1000_UNLOAD_APP = 101,
+	QBT1000_SEND_TZCMD = 102,
+	QBT1000_SET_FINGER_DETECT_KEY = 103,
+	QBT1000_CONFIGURE_POWER_KEY = 104
+};
+
+/*
+ * enum qbt1000_fw_event -
+ *      enumeration of firmware events
+ * @FW_EVENT_FINGER_DOWN - finger down detected
+ * @FW_EVENT_FINGER_UP - finger up detected
+ * @FW_EVENT_INDICATION - an indication IPC from the firmware is pending
+ */
+enum qbt1000_fw_event {
+	FW_EVENT_FINGER_DOWN = 1,
+	FW_EVENT_FINGER_UP = 2,
+	FW_EVENT_CBGE_REQUIRED = 3,
+};
+
+/*
+ * struct qbt1000_app -
+ *      used to load and unload apps in TZ
+ * @app_handle - qseecom handle for clients
+ * @name - Name of secure app to load
+ * @size - Size of requested buffer of secure app
+ * @high_band_width - 1 - for high bandwidth usage
+ *                    0 - for normal bandwidth usage
+ */
+struct qbt1000_app {
+	struct qseecom_handle **app_handle;
+	char name[MAX_NAME_SIZE];
+	uint32_t size;
+	uint8_t high_band_width;
+};
+
+/*
+ * struct qbt1000_send_tz_cmd -
+ *      used to cmds to TZ App
+ * @app_handle - qseecom handle for clients
+ * @req_buf - Buffer containing request for secure app
+ * @req_buf_len - Length of request buffer
+ * @rsp_buf - Buffer containing response from secure app
+ * @rsp_buf_len - Length of response buffer
+ */
+struct qbt1000_send_tz_cmd {
+	struct qseecom_handle *app_handle;
+	uint8_t *req_buf;
+	uint32_t req_buf_len;
+	uint8_t *rsp_buf;
+	uint32_t rsp_buf_len;
+};
+
+/*
+ * struct qbt1000_erie_event -
+ *      used to receive events from Erie
+ * @buf - Buffer containing event from Erie
+ * @buf_len - Length of buffer
+ */
+struct qbt1000_erie_event {
+	uint8_t *buf;
+	uint32_t buf_len;
+};
+
+/*
+ * struct qbt1000_set_finger_detect_key -
+ *      used to configure the input key which is sent on finger down/up event
+ * @key_code - Key code to send on finger down/up. 0 disables sending key events
+ */
+struct qbt1000_set_finger_detect_key {
+	unsigned int key_code;
+};
+
+/*
+ * struct qbt1000_configure_power_key -
+ *      used to configure whether the power key is sent on finger down
+ * @enable - if non-zero, power key is sent on finger down
+ */
+struct qbt1000_configure_power_key {
+	unsigned int enable;
+};
+
+#endif /* _UAPI_QBT1000_H_ */
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
index 37dd31f..421769e 100644
--- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -1165,28 +1165,27 @@
 		break;
 
 	case SNDRV_LSM_SET_FWK_MODE_CONFIG: {
-		u32 *mode = NULL;
+		u32 mode;
 
-		if (!arg) {
-			dev_err(rtd->dev,
-				"%s: Invalid param arg for ioctl %s session %d\n",
-				__func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG",
-				prtd->lsm_client->session);
-			rc = -EINVAL;
-			break;
+		if (copy_from_user(&mode, arg, sizeof(mode))) {
+			dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n",
+				__func__, "LSM_SET_FWK_MODE_CONFIG");
+			return -EFAULT;
 		}
-		mode = (u32 *)arg;
-		if (prtd->lsm_client->event_mode == *mode) {
+
+		dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n",
+			__func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG", mode);
+		if (prtd->lsm_client->event_mode == mode) {
 			dev_dbg(rtd->dev,
 				"%s: mode for %d already set to %d\n",
-				__func__, prtd->lsm_client->session, *mode);
+				__func__, prtd->lsm_client->session, mode);
 			rc = 0;
 		} else {
 			dev_dbg(rtd->dev, "%s: Event mode = %d\n",
-				 __func__, *mode);
-			rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, *mode);
+				 __func__, mode);
+			rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, mode);
 			if (!rc)
-				prtd->lsm_client->event_mode = *mode;
+				prtd->lsm_client->event_mode = mode;
 			else
 				dev_err(rtd->dev,
 					"%s: set event mode failed %d\n",