Merge "soc: qcom: smem: Update max processors in soc"
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index ffd4345..a00984f 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -46,6 +46,17 @@
 		    the first cell will be used to define gpio number and the
 		    second denotes the flags for this gpio
 
+- qcom,gpios-disallowed:
+	Usage: optional
+	Value type: <prop-encoded-array>
+	Definition: Array of the GPIO hardware numbers corresponding to GPIOs
+		    which the APSS processor is not allowed to configure.
+		    The hardware numbers are indexed from 1.
+		    The interrupt resources for these GPIOs must not be defined
+		    in "interrupts" and "interrupt-names" properties.
+		    GPIOs defined in this array won't be registered as pins
+		    in the pinctrl device or gpios in the gpio chip.
+
 Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
 a general description of GPIO and interrupt bindings.
 
@@ -234,6 +245,7 @@
 
 		gpio-controller;
 		#gpio-cells = <2>;
+		qcom,gpios-disallowed = <1 20>;
 
 		pm8921_gpio_keys: gpio-keys {
 			volume-keys {
diff --git a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
index 290ec06..7307aed 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
+++ b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
@@ -15,6 +15,21 @@
 - io-channels:     Should contain IIO channel specifier for the ADC channel,
                    which report chip die temperature.
 - io-channel-names: Should contain "thermal".
+- qcom,temperature-threshold-set: Defines the temperature threshold set to
+		   configure.  Supported values are 0 to 3.  Each set defines
+		   the over-temperature stage 1, 2, and 3 temperature
+		   thresholds. If this property is not specified, then set 0
+		   will be used by default.
+		   Threshold set mapping (TEMP_GEN1, TEMP_GEN2 rev 0):
+			0 = {105 C, 125 C, 145 C}
+			1 = {110 C, 130 C, 150 C}
+			2 = {115 C, 135 C, 155 C}
+			3 = {120 C, 140 C, 160 C}
+		   Threshold set mapping (TEMP_GEN2 rev 1 and above):
+			0 = { 90 C, 110 C, 140 C}
+			1 = { 95 C, 115 C, 145 C}
+			2 = {100 C, 120 C, 150 C}
+			3 = {105 C, 125 C, 155 C}
 
 Example:
 
diff --git a/arch/arm64/boot/dts/qcom/kona.dtsi b/arch/arm64/boot/dts/qcom/kona.dtsi
index bf23646..0e35a4d 100644
--- a/arch/arm64/boot/dts/qcom/kona.dtsi
+++ b/arch/arm64/boot/dts/qcom/kona.dtsi
@@ -1361,6 +1361,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1001 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1368,6 +1370,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1002 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1375,6 +1379,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1003 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1382,6 +1388,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1004 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1389,6 +1397,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1005 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1396,6 +1406,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1006 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1403,6 +1415,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1007 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1410,6 +1424,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "cdsprpc-smd";
 			iommus = <&apps_smmu 0x1008 0x0460>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1418,6 +1434,9 @@
 			label = "cdsprpc-smd";
 			qcom,secure-context-bank;
 			iommus = <&apps_smmu 0x1009 0x0460>;
+			dma-ranges = <0x60000000 0x60000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
+			qcom,iommu-vmid = <0xA>;	/* VMID_CP_PIXEL */
 			dma-coherent;
 		};
 
@@ -1425,6 +1444,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "adsprpc-smd";
 			iommus = <&apps_smmu 0x1803 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1432,6 +1453,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "adsprpc-smd";
 			iommus = <&apps_smmu 0x1804 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1439,6 +1462,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "adsprpc-smd";
 			iommus = <&apps_smmu 0x1805 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1446,6 +1471,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "sdsprpc-smd";
 			iommus = <&apps_smmu 0x0541 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1453,6 +1480,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "sdsprpc-smd";
 			iommus = <&apps_smmu 0x0542 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			dma-coherent;
 		};
 
@@ -1460,6 +1489,8 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "sdsprpc-smd";
 			iommus = <&apps_smmu 0x0543 0x0>;
+			dma-ranges = <0x80000000 0x80000000 0x78000000>;
+			qcom,iommu-faults = "stall-disable";
 			shared-cb = <4>;
 			dma-coherent;
 		};
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 215ae14..71f40c0 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -226,7 +226,6 @@ struct fastrpc_ctx_lst {
 
 struct fastrpc_smmu {
 	struct device *dev;
-	struct dma_iommu_mapping *mapping;
 	const char *dev_name;
 	int cb;
 	int enabled;
@@ -505,7 +504,7 @@ static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache)
 		int destVM[1] = {VMID_HLOS};
 		int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
-		if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID)
+		if (fl->sctx->smmu.cb)
 			buf->phys &= ~((uint64_t)fl->sctx->smmu.cb << 32);
 		vmid = fl->apps->channel[fl->cid].vmid;
 		if (vmid) {
@@ -872,8 +871,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
 		map->phys = sg_dma_address(map->table->sgl);
 
 		if (sess->smmu.cb) {
-			if (fl->cid != SDSP_DOMAIN_ID)
-				map->phys += ((uint64_t)sess->smmu.cb << 32);
+			map->phys += ((uint64_t)sess->smmu.cb << 32);
 			for_each_sg(map->table->sgl, sgl, map->table->nents,
 				sgl_index)
 				map->size += sg_dma_len(sgl);
@@ -975,7 +973,7 @@ static int fastrpc_buf_alloc(struct fastrpc_file *fl, size_t size,
 			current->comm, __func__, size, (int)buf->virt);
 		goto bail;
 	}
-	if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID)
+	if (fl->sctx->smmu.cb)
 		buf->phys += ((uint64_t)fl->sctx->smmu.cb << 32);
 	vmid = fl->apps->channel[fl->cid].vmid;
 	if (vmid) {
@@ -3509,10 +3507,8 @@ static int fastrpc_cb_probe(struct device *dev)
 	struct fastrpc_session_ctx *sess;
 	struct of_phandle_args iommuspec;
 	const char *name;
-	dma_addr_t start = 0x80000000;
 	int err = 0;
 	unsigned int sharedcb_count = 0, cid, i, j;
-	int secure_vmid = VMID_CP_PIXEL, cache_flush = 1;
 
 	VERIFY(err, NULL != (name = of_get_property(dev->of_node,
 					 "label", NULL)));
@@ -3547,60 +3543,15 @@ static int fastrpc_cb_probe(struct device *dev)
 						"dma-coherent");
 	sess->smmu.secure = of_property_read_bool(dev->of_node,
 						"qcom,secure-context-bank");
-
-	/* Software workaround for SMMU interconnect HW bug */
-	if (cid == SDSP_DOMAIN_ID) {
-		sess->smmu.cb = iommuspec.args[0] & 0x3;
-		VERIFY(err, sess->smmu.cb);
-		if (err)
-			goto bail;
-		start += ((uint64_t)sess->smmu.cb << 32);
-		dma_set_mask(dev, DMA_BIT_MASK(34));
-	} else {
-		sess->smmu.cb = iommuspec.args[0] & 0xf;
-	}
-
-	if (sess->smmu.secure)
-		start = 0x60000000;
-	VERIFY(err, !IS_ERR_OR_NULL(sess->smmu.mapping =
-				arm_iommu_create_mapping(&platform_bus_type,
-						start, 0x78000000)));
-	if (err) {
-		pr_err("adsprpc: %s: creating iommu mapping failed for %s, ret %pK\n",
-				__func__, dev_name(dev), sess->smmu.mapping);
-		goto bail;
-	}
-
-	err = iommu_domain_set_attr(sess->smmu.mapping->domain,
-			DOMAIN_ATTR_CB_STALL_DISABLE, &cache_flush);
-	if (err) {
-		pr_err("adsprpc: %s: setting CB stall iommu attribute failed for %s with err %d\n",
-			__func__, dev_name(dev), err);
-		goto bail;
-	}
-	if (sess->smmu.secure) {
-		err = iommu_domain_set_attr(sess->smmu.mapping->domain,
-				DOMAIN_ATTR_SECURE_VMID, &secure_vmid);
-		if (err) {
-			pr_err("adsprpc: %s: setting secure iommu attribute failed for %s with err %d\n",
-				__func__, dev_name(dev), err);
-			goto bail;
-		}
-	}
-
-	err = arm_iommu_attach_device(dev, sess->smmu.mapping);
-	if (err) {
-		pr_err("adsprpc: %s: attaching iommu device failed for %s with err %d\n",
-			__func__, dev_name(dev), err);
-		goto bail;
-	}
-
+	sess->smmu.cb = iommuspec.args[0] & 0xf;
 	sess->smmu.dev = dev;
 	sess->smmu.dev_name = dev_name(dev);
 	sess->smmu.enabled = 1;
+
 	if (!sess->smmu.dev->dma_parms)
 		sess->smmu.dev->dma_parms = devm_kzalloc(sess->smmu.dev,
 			sizeof(*sess->smmu.dev->dma_parms), GFP_KERNEL);
+
 	dma_set_max_seg_size(sess->smmu.dev, DMA_BIT_MASK(32));
 	dma_set_seg_boundary(sess->smmu.dev, (unsigned long)DMA_BIT_MASK(64));
 
@@ -3824,14 +3775,8 @@ static void fastrpc_deinit(void)
 		for (j = 0; j < NUM_SESSIONS; j++) {
 			struct fastrpc_session_ctx *sess = &chan->session[j];
 
-			if (sess->smmu.dev) {
-				arm_iommu_detach_device(sess->smmu.dev);
+			if (sess->smmu.dev)
 				sess->smmu.dev = NULL;
-			}
-			if (sess->smmu.mapping) {
-				arm_iommu_release_mapping(sess->smmu.mapping);
-				sess->smmu.mapping = NULL;
-			}
 		}
 		kfree(chan->rhvm.vmid);
 		kfree(chan->rhvm.vmperm);
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index a30fab2..d138aa5 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -12,6 +12,8 @@
 #include <linux/clk.h>
 #include <linux/bitmap.h>
 #include <linux/sde_rsc.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/llcc-qcom.h>
 
 #include "msm_prop.h"
 
@@ -297,6 +299,107 @@ static inline enum sde_crtc_client_type _get_sde_client_type(
 		return RT_CLIENT;
 }
 
+/**
+ * @_sde_core_perf_activate_llcc() - Activates/deactivates the system llcc
+ * @kms - pointer to the kms
+ * @uid - ID for which the llcc would be activated
+ * @activate - boolean to indicate if activate/deactivate the LLCC
+ *
+ * Function assumes that caller has already acquired the "sde_core_perf_lock",
+ * which would protect from any race condition between CRTC's
+ */
+static int _sde_core_perf_activate_llcc(struct sde_kms *kms,
+	u32 uid, bool activate)
+{
+	struct llcc_slice_desc *slice;
+	struct drm_device *drm_dev;
+	struct device *dev;
+	struct platform_device *pdev;
+	int rc = 0;
+
+	if (!kms || !kms->dev || !kms->dev->dev) {
+		SDE_ERROR("wrong params won't activate llcc\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	drm_dev = kms->dev;
+	dev = drm_dev->dev;
+	pdev = to_platform_device(dev);
+
+	/* If LLCC is already in the requested state, skip */
+	SDE_EVT32(activate, kms->perf.llcc_active);
+	if ((activate && kms->perf.llcc_active) ||
+		(!activate && !kms->perf.llcc_active)) {
+		SDE_DEBUG("skip llcc request:%d state:%d\n",
+			activate, kms->perf.llcc_active);
+		goto exit;
+	}
+
+	SDE_DEBUG("activate/deactivate the llcc request:%d state:%d\n",
+		activate, kms->perf.llcc_active);
+
+	slice = llcc_slice_getd(uid);
+	if (IS_ERR_OR_NULL(slice))  {
+		SDE_ERROR("failed to get llcc slice for uid:%d\n", uid);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (activate) {
+		llcc_slice_activate(slice);
+		kms->perf.llcc_active = true;
+	} else {
+		llcc_slice_deactivate(slice);
+		kms->perf.llcc_active = false;
+	}
+
+exit:
+	if (rc)
+		SDE_ERROR("error activating llcc:%d rc:%d\n",
+			activate, rc);
+	return rc;
+
+}
+
+static void _sde_core_perf_crtc_update_llcc(struct sde_kms *kms,
+		struct drm_crtc *crtc)
+{
+	struct drm_crtc *tmp_crtc;
+	struct sde_crtc *sde_crtc;
+	enum sde_crtc_client_type curr_client_type
+					= sde_crtc_get_client_type(crtc);
+	u32 total_llcc_active = 0;
+
+	if (!kms->perf.catalog->sc_cfg.has_sys_cache) {
+		SDE_DEBUG("System Cache is not enabled!. Won't use\n");
+		return;
+	}
+
+	drm_for_each_crtc(tmp_crtc, crtc->dev) {
+		if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
+			_is_crtc_client_type_matches(tmp_crtc, curr_client_type,
+								&kms->perf)) {
+
+			/* use current perf, which are the values voted */
+			sde_crtc = to_sde_crtc(tmp_crtc);
+			total_llcc_active |=
+			  sde_crtc->cur_perf.llcc_active;
+
+			SDE_DEBUG("crtc=%d llcc:%llu active:0x%x\n",
+				tmp_crtc->base.id,
+				sde_crtc->cur_perf.llcc_active,
+				total_llcc_active);
+
+			if (total_llcc_active)
+				break;
+		}
+	}
+
+	_sde_core_perf_activate_llcc(kms, LLCC_ROTATOR,
+			total_llcc_active ? true : false);
+}
+
 static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
 		struct drm_crtc *crtc, u32 bus_id)
 {
@@ -494,7 +597,7 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
 		int params_changed, bool stop_req)
 {
 	struct sde_core_perf_params *new, *old;
-	int update_bus = 0, update_clk = 0;
+	int update_bus = 0, update_clk = 0, update_llcc = 0;
 	u64 clk_rate = 0;
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *sde_cstate;
@@ -534,6 +637,28 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
 	new = &sde_crtc->new_perf;
 
 	if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
+
+		/*
+		 * cases for the llcc update.
+		 * 1. llcc is transitioning: 'inactive->active' during kickoff,
+		 *	for current request.
+		 * 2. llcc is transitioning: 'active->inactive'at the end of the
+		 *	commit or during stop
+		 */
+
+		if ((params_changed &&
+			 new->llcc_active && !old->llcc_active) ||
+		    (!params_changed &&
+			!new->llcc_active && old->llcc_active)) {
+
+			SDE_DEBUG("crtc=%d p=%d new_llcc=%d, old_llcc=%d\n",
+				crtc->base.id, params_changed,
+				new->llcc_active, old->llcc_active);
+
+			old->llcc_active = new->llcc_active;
+			update_llcc = 1;
+		}
+
 		for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
 			/*
 			 * cases for bus bandwidth update.
@@ -604,11 +729,12 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
 			update_clk = 1;
 		}
 	} else {
-		SDE_DEBUG("crtc=%d disable\n", crtc->base.id);
+		SDE_ERROR("crtc=%d disable\n", crtc->base.id);
 		memset(old, 0, sizeof(*old));
 		memset(new, 0, sizeof(*new));
 		update_bus = ~0;
 		update_clk = 1;
+		update_llcc = 1;
 	}
 	trace_sde_perf_crtc_update(crtc->base.id,
 		new->bw_ctl[SDE_POWER_HANDLE_DBUS_ID_MNOC],
@@ -620,6 +746,9 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
 		new->core_clk_rate, stop_req,
 		update_bus, update_clk, params_changed);
 
+	if (update_llcc)
+		_sde_core_perf_crtc_update_llcc(kms, crtc);
+
 	for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
 		if (update_bus & BIT(i))
 			_sde_core_perf_crtc_update_bus(kms, crtc, i);
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index 45c4bc6..2d0de09 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -21,11 +21,13 @@
  * @max_per_pipe_ib: maximum instantaneous bandwidth request
  * @bw_ctl: arbitrated bandwidth request
  * @core_clk_rate: core clock rate request
+ * @llcc_active: request to activate/deactivate the llcc
  */
 struct sde_core_perf_params {
 	u64 max_per_pipe_ib[SDE_POWER_HANDLE_DBUS_ID_MAX];
 	u64 bw_ctl[SDE_POWER_HANDLE_DBUS_ID_MAX];
 	u64 core_clk_rate;
+	bool llcc_active;
 };
 
 /**
@@ -59,6 +61,7 @@ struct sde_core_perf_tune {
  * @bw_vote_mode: apps rsc vs display rsc bandwidth vote mode
  * @sde_rsc_available: is display rsc available
  * @bw_vote_mode_updated: bandwidth vote mode update
+ * @llcc_active: status of the llcc, true if active.
  */
 struct sde_core_perf {
 	struct drm_device *dev;
@@ -78,6 +81,7 @@ struct sde_core_perf {
 	u32 bw_vote_mode;
 	bool sde_rsc_available;
 	bool bw_vote_mode_updated;
+	bool llcc_active;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 2bcadbe..d7d0974 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1234,7 +1234,8 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
 				state->src_x >> 16, state->src_y >> 16,
 				state->src_w >> 16, state->src_h >> 16,
 				state->crtc_x, state->crtc_y,
-				state->crtc_w, state->crtc_h);
+				state->crtc_w, state->crtc_h,
+				pstate->rotation);
 
 		stage_idx = zpos_cnt[pstate->stage]++;
 		stage_cfg->stage[pstate->stage][stage_idx] =
@@ -2965,10 +2966,18 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
 	 * required. However, if those planes were power collapsed since
 	 * last commit cycle, driver has to restore the hardware state
 	 * of those planes explicitly here prior to plane flush.
+	 * Also use this iteration to see if any plane requires cache,
+	 * so during the perf update driver can activate/deactivate
+	 * the cache accordingly.
 	 */
-	drm_atomic_crtc_for_each_plane(plane, crtc)
+	sde_crtc->new_perf.llcc_active = false;
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
 		sde_plane_restore(plane);
 
+		if (sde_plane_is_cache_required(plane))
+			sde_crtc->new_perf.llcc_active = true;
+	}
+
 	/* wait for acquire fences before anything else is done */
 	_sde_crtc_wait_for_fences(crtc);
 
@@ -5737,9 +5746,17 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 	sde_cp_crtc_init(crtc);
 	sde_cp_crtc_install_properties(crtc);
 
+	sde_crtc->cur_perf.llcc_active = false;
+	sde_crtc->new_perf.llcc_active = false;
+
 	kthread_init_delayed_work(&sde_crtc->idle_notify_work,
 					__sde_crtc_idle_notify_work);
 
+	SDE_DEBUG("crtc=%d new_llcc=%d, old_llcc=%d\n",
+		crtc->base.id,
+		sde_crtc->new_perf.llcc_active,
+		sde_crtc->cur_perf.llcc_active);
+
 	SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name);
 	return crtc;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 1f9a4d0..497cc40 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -642,6 +642,16 @@ static const struct sde_format sde_format_map_tp10_ubwc[] = {
 		SDE_FETCH_UBWC, 4, SDE_TILE_HEIGHT_NV12),
 };
 
+inline bool sde_format_is_tp10_ubwc(const struct sde_format *fmt)
+{
+	if (SDE_FORMAT_IS_YUV(fmt) && SDE_FORMAT_IS_DX(fmt) &&
+			SDE_FORMAT_IS_UBWC(fmt) &&
+			(fmt->num_planes == 4) && fmt->unpack_tight)
+		return true;
+	else
+		return false;
+}
+
 /* _sde_get_v_h_subsample_rate - Get subsample rates for all formats we support
  *   Note: Not using the drm_format_*_subsampling since we have formats
  */
@@ -1334,3 +1344,38 @@ uint32_t sde_populate_formats(
 
 	return i;
 }
+
+int sde_format_validate_fmt(struct msm_kms *kms,
+	const struct msm_format *msm_fmt,
+	const struct sde_format_extended *fmt_list)
+{
+	const struct msm_format *fmt_tmp;
+	bool valid_format = false;
+	int ret = 0;
+
+	if (!msm_fmt || !fmt_list) {
+		SDE_ERROR("invalid fmt:%d list:%d\n",
+			!msm_fmt, !fmt_list);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	while (fmt_list->fourcc_format) {
+		fmt_tmp = sde_get_msm_format(kms,
+				fmt_list->fourcc_format,
+				fmt_list->modifier);
+		if (fmt_tmp &&
+			(fmt_tmp->pixel_format == msm_fmt->pixel_format)) {
+			valid_format = true;
+			break;
+		}
+		++fmt_list;
+	}
+
+	if (!valid_format) {
+		SDE_ERROR("fmt:%d not found within the list!\n", *msm_fmt);
+		ret = -EINVAL;
+	}
+exit:
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.h b/drivers/gpu/drm/msm/sde/sde_formats.h
index 0e55df4..32c676c 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.h
+++ b/drivers/gpu/drm/msm/sde/sde_formats.h
@@ -142,4 +142,25 @@ uint32_t sde_format_get_framebuffer_size(
 		const uint32_t *pitches,
 		const uint64_t modifier);
 
+/**
+ * sde_format_is_tp10_ubwc - check if the format is tp10 ubwc
+ * @format:            DRM pixel format
+ *
+ * Return: returns true if the format is tp10 ubwc, otherwise false.
+ */
+inline bool sde_format_is_tp10_ubwc(const struct sde_format *fmt);
+
+/**
+ * sde_format_validate_fmt - validates if the format "msm_fmt" is within
+ *	the list "fmt_list"
+ * @kms: pointer to the kms object
+ * @msm_fmt: pointer to the format to look within the list
+ * @fmt_list: list where driver will loop to look for the 'msm_fmt' format.
+ * @result: returns 0 if the format is found, otherwise will return an
+ *	error code.
+ */
+int sde_format_validate_fmt(struct msm_kms *kms,
+	const struct msm_format *msm_fmt,
+	const struct sde_format_extended *fmt_list);
+
 #endif /*_SDE_FORMATS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index d5600f5..f345710 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -64,6 +64,10 @@
 #define MAX_DOWNSCALE_RATIO		4
 #define SSPP_UNITY_SCALE		1
 
+#define MAX_DOWNSCALE_RATIO_INLINE_ROT_RT_DEFAULT	2
+#define MAX_DOWNSCALE_RATIO_INLINE_ROT_NRT_DEFAULT	4
+#define MAX_PRE_ROT_HEIGHT_INLINE_ROT_DEFAULT	1088
+
 #define MAX_HORZ_DECIMATION		4
 #define MAX_VERT_DECIMATION		4
 
@@ -1098,6 +1102,23 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
 
 	sblk->format_list = sde_cfg->vig_formats;
 	sblk->virt_format_list = sde_cfg->virt_vig_formats;
+	if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev)) {
+		set_bit(SDE_SSPP_TRUE_INLINE_ROT_V1, &sspp->features);
+		sblk->in_rot_format_list = sde_cfg->inline_rot_formats;
+		sblk->in_rot_maxdwnscale_rt =
+			sde_cfg->true_inline_dwnscale_rt;
+		sblk->in_rot_maxdwnscale_nrt =
+			sde_cfg->true_inline_dwnscale_nrt;
+		sblk->in_rot_maxheight =
+			MAX_PRE_ROT_HEIGHT_INLINE_ROT_DEFAULT;
+	}
+
+	if (sde_cfg->sc_cfg.has_sys_cache) {
+		set_bit(SDE_PERF_SSPP_SYS_CACHE, &sspp->perf_features);
+		sblk->llcc_scid = sde_cfg->sc_cfg.llcc_scid;
+		sblk->llcc_slice_size =
+			sde_cfg->sc_cfg.llcc_slice_size;
+	}
 }
 
 static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
@@ -2018,6 +2039,63 @@ static void _sde_dspp_setup_blocks(struct sde_mdss_cfg *sde_cfg,
 	}
 }
 
+static int sde_rot_parse_dt(struct device_node *np,
+	struct sde_mdss_cfg *sde_cfg)
+{
+	struct platform_device *pdev;
+	struct of_phandle_args phargs;
+	struct llcc_slice_desc *slice;
+	int rc = 0;
+
+	rc = of_parse_phandle_with_args(np,
+		"qcom,sde-inline-rotator", "#list-cells",
+		0, &phargs);
+
+	if (rc) {
+		/*
+		 * This is not a fatal error, system cache can be disabled
+		 * in device tree, anyways recommendation is to have it
+		 * enabled, so print an error but don't fail
+		 */
+		SDE_DEBUG("sys cache will be disabled rc:%d\n", rc);
+		rc = 0;
+		goto exit;
+	}
+
+	if (!phargs.np || !phargs.args_count) {
+		SDE_ERROR("wrong phandle args %d %d\n",
+			!phargs.np, !phargs.args_count);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	pdev = of_find_device_by_node(phargs.np);
+	if (!pdev) {
+		SDE_ERROR("invalid sde rotator node\n");
+		goto exit;
+	}
+
+	slice = llcc_slice_getd(LLCC_ROTATOR);
+	if (IS_ERR_OR_NULL(slice))  {
+		SDE_ERROR("failed to get rotator slice!\n");
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	sde_cfg->sc_cfg.llcc_scid = llcc_get_slice_id(slice);
+	sde_cfg->sc_cfg.llcc_slice_size = llcc_get_slice_size(slice);
+	llcc_slice_putd(slice);
+
+	sde_cfg->sc_cfg.has_sys_cache = true;
+
+	SDE_DEBUG("rotator llcc scid:%d slice_size:%zukb\n",
+		sde_cfg->sc_cfg.llcc_scid, sde_cfg->sc_cfg.llcc_slice_size);
+cleanup:
+	of_node_put(phargs.np);
+exit:
+	return rc;
+}
+
 static int sde_dspp_top_parse_dt(struct device_node *np,
 		struct sde_mdss_cfg *sde_cfg)
 {
@@ -3321,7 +3399,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 {
 	int rc = 0;
 	uint32_t dma_list_size, vig_list_size, wb2_list_size;
-	uint32_t virt_vig_list_size;
+	uint32_t virt_vig_list_size, in_rot_list_size = 0;
 	uint32_t cursor_list_size = 0;
 	uint32_t index = 0;
 
@@ -3346,6 +3424,8 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 	virt_vig_list_size = ARRAY_SIZE(plane_formats);
 	wb2_list_size = ARRAY_SIZE(wb2_formats);
 
+	if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev))
+		in_rot_list_size = ARRAY_SIZE(true_inline_rot_v1_fmts);
 
 	sde_cfg->dma_formats = kcalloc(dma_list_size,
 		sizeof(struct sde_format_extended), GFP_KERNEL);
@@ -3376,6 +3456,16 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 		goto end;
 	}
 
+	if (in_rot_list_size) {
+		sde_cfg->inline_rot_formats = kcalloc(in_rot_list_size,
+			sizeof(struct sde_format_extended), GFP_KERNEL);
+		if (!sde_cfg->inline_rot_formats) {
+			SDE_ERROR("failed to alloc inline rot format list\n");
+			rc = -ENOMEM;
+			goto end;
+		}
+	}
+
 	index = sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
 		0, plane_formats, ARRAY_SIZE(plane_formats));
 
@@ -3391,7 +3481,10 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 
 	index = sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
 		0, wb2_formats, ARRAY_SIZE(wb2_formats));
-
+	if (in_rot_list_size)
+		index = sde_copy_formats(sde_cfg->inline_rot_formats,
+			in_rot_list_size, 0, true_inline_rot_v1_fmts,
+			ARRAY_SIZE(true_inline_rot_v1_fmts));
 end:
 	return rc;
 }
@@ -3507,6 +3600,11 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 		clear_bit(MDSS_INTR_AD4_1_INTR, sde_cfg->mdss_irqs);
 		sde_cfg->has_hdr = true;
 		sde_cfg->has_vig_p010 = true;
+		sde_cfg->true_inline_rot_rev = SDE_INLINE_ROT_VERSION_1_0_0;
+		sde_cfg->true_inline_dwnscale_rt =
+			MAX_DOWNSCALE_RATIO_INLINE_ROT_RT_DEFAULT;
+		sde_cfg->true_inline_dwnscale_nrt =
+			MAX_DOWNSCALE_RATIO_INLINE_ROT_NRT_DEFAULT;
 	} else {
 		SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
 		sde_cfg->perf.min_prefill_lines = 0xffff;
@@ -3618,6 +3716,7 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg)
 	kfree(sde_cfg->vig_formats);
 	kfree(sde_cfg->wb_formats);
 	kfree(sde_cfg->virt_vig_formats);
+	kfree(sde_cfg->inline_rot_formats);
 
 	kfree(sde_cfg);
 }
@@ -3649,6 +3748,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
 	if (rc)
 		goto end;
 
+	rc = sde_rot_parse_dt(np, sde_cfg);
+	if (rc)
+		goto end;
+
 	rc = sde_ctl_parse_dt(np, sde_cfg);
 	if (rc)
 		goto end;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index aaae43d..185cbee 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -83,6 +83,13 @@
 #define IS_SDE_CTL_REV_100(rev) \
 	((rev) == SDE_CTL_CFG_VERSION_1_0_0)
 
+/**
+ * True inline rotation supported versions
+ */
+#define SDE_INLINE_ROT_VERSION_1_0_0	0x100
+#define IS_SDE_INLINE_ROT_REV_100(rev) \
+	((rev) == SDE_INLINE_ROT_VERSION_1_0_0)
+
 #define SDE_HW_UBWC_VER(rev) \
 	SDE_HW_VER((((rev) >> 8) & 0xF), (((rev) >> 4) & 0xF), ((rev) & 0xF))
 
@@ -101,6 +108,15 @@ enum {
 		IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_UBWC_VER_30)
 
 /**
+ * Supported SSPP system cache settings
+ */
+#define SSPP_SYS_CACHE_EN_FLAG	BIT(0)
+#define SSPP_SYS_CACHE_SCID		BIT(1)
+#define SSPP_SYS_CACHE_OP_MODE	BIT(2)
+#define SSPP_SYS_CACHE_OP_TYPE	BIT(3)
+#define SSPP_SYS_CACHE_NO_ALLOC	BIT(4)
+
+/**
  * SDE INTERRUPTS - maintains the possible hw irq's allowed by HW
  * The order in this enum must match the order of the irqs defined
  * by 'sde_irq_map'
@@ -171,6 +187,7 @@ enum {
  * @SDE_SSPP_SEC_UI_ALLOWED   Allows secure-ui layers
  * @SDE_SSPP_BLOCK_SEC_UI    Blocks secure-ui layers
  * @SDE_SSPP_SCALER_QSEED3LITE Qseed3lite algorithm support
+ * @SDE_SSPP_TRUE_INLINE_ROT_V1, Support of SSPP true inline rotation v1
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -198,6 +215,7 @@ enum {
 	SDE_SSPP_SEC_UI_ALLOWED,
 	SDE_SSPP_BLOCK_SEC_UI,
 	SDE_SSPP_SCALER_QSEED3LITE,
+	SDE_SSPP_TRUE_INLINE_ROT_V1,
 	SDE_SSPP_MAX
 };
 
@@ -209,6 +227,7 @@ enum {
  * @SDE_PERF_SSPP_TS_PREFILL_REC1 Supports prefill with traffic shaper multirec
  * @SDE_PERF_SSPP_CDP             Supports client driven prefetch
  * @SDE_PERF_SSPP_QOS_FL_NOCALC   Avoid fill level calc for QoS/danger/safe
+ * @SDE_PERF_SSPP_SYS_CACHE,      SSPP supports system cache
  * @SDE_PERF_SSPP_MAX             Maximum value
  */
 enum {
@@ -218,6 +237,7 @@ enum {
 	SDE_PERF_SSPP_TS_PREFILL_REC1,
 	SDE_PERF_SSPP_CDP,
 	SDE_PERF_SSPP_QOS_FL_NOCALC,
+	SDE_PERF_SSPP_SYS_CACHE,
 	SDE_PERF_SSPP_MAX
 };
 
@@ -535,6 +555,12 @@ struct sde_qos_lut_tbl {
  * @dgm_csc_blk: DGM CSC blocks
  * @format_list: Pointer to list of supported formats
  * @virt_format_list: Pointer to list of supported formats for virtual planes
+ * @in_rot_format_list: Pointer to list of supported formats for inline rotation
+ * @in_rot_maxdwnscale_rt: max downscale ratio for inline rotation rt clients
+ * @in_rot_maxdwnscale_nrt: max downscale ratio for inline rotation nrt clients
+ * @in_rot_maxheight: max pre rotated height for inline rotation
+ * @llcc_scid: scid for the system cache
+ * @llcc_slice size: slice size of the system cache
  */
 struct sde_sspp_sub_blks {
 	u32 maxlinewidth;
@@ -563,6 +589,12 @@ struct sde_sspp_sub_blks {
 
 	const struct sde_format_extended *format_list;
 	const struct sde_format_extended *virt_format_list;
+	const struct sde_format_extended *in_rot_format_list;
+	u32 in_rot_maxdwnscale_rt;
+	u32 in_rot_maxdwnscale_nrt;
+	u32 in_rot_maxheight;
+	int llcc_scid;
+	size_t llcc_slice_size;
 };
 
 /**
@@ -946,6 +978,18 @@ struct sde_perf_cdp_cfg {
 };
 
 /**
+ * struct sde_sc_cfg - define system cache configuration
+ * @has_sys_cache: true if system cache is enabled
+ * @llcc_scid: scid for the system cache
+ * @llcc_slice_size: slice size of the system cache
+ */
+struct sde_sc_cfg {
+	bool has_sys_cache;
+	int llcc_scid;
+	size_t llcc_slice_size;
+};
+
+/**
  * struct sde_perf_cfg - performance control settings
  * @max_bw_low         low threshold of maximum bandwidth (kbps)
  * @max_bw_high        high threshold of maximum bandwidth (kbps)
@@ -1035,12 +1079,16 @@ struct sde_perf_cfg {
  * @virt_vig_formats   Supported formats for virtual vig pipe
  * @vbif_qos_nlvl      number of vbif QoS priority level
  * @ts_prefill_rev     prefill traffic shaper feature revision
+ * @true_inline_rot_rev	inline rotator feature revision
+ * @true_inline_dwnscale_rt    true inline rotator downscale ratio for rt
+ * @true_inline_dwnscale_nrt    true inline rotator downscale ratio for nrt
  * @macrotile_mode     UBWC parameter for macro tile channel distribution
  * @pipe_order_type    indicate if it is required to specify pipe order
  * @delay_prg_fetch_start indicates if throttling the fetch start is required
  * @has_qsync	       Supports qsync feature
  * @has_3d_merge_reset Supports 3D merge reset
  * @has_decimation     Supports decimation
+ * @sc_cfg: system cache configuration
  * @sui_misr_supported  indicate if secure-ui-misr is supported
  * @sui_block_xin_mask  mask of all the xin-clients to be blocked during
  *                         secure-ui when secure-ui-misr feature is supported
@@ -1052,6 +1100,7 @@ struct sde_perf_cfg {
  * @sui_supported_blendstage  secure-ui supported blendstage
  * @has_cursor    indicates if hardware cursor is supported
  * @has_vig_p010  indicates if vig pipe supports p010 format
+ * @inline_rot_formats	formats supported by the inline rotator feature
  * @mdss_irqs	  bitmap with the irqs supported by the target
  */
 struct sde_mdss_cfg {
@@ -1082,6 +1131,9 @@ struct sde_mdss_cfg {
 	bool has_idle_pc;
 	u32 vbif_qos_nlvl;
 	u32 ts_prefill_rev;
+	u32 true_inline_rot_rev;
+	u32 true_inline_dwnscale_rt;
+	u32 true_inline_dwnscale_nrt;
 	u32 macrotile_mode;
 	u32 pipe_order_type;
 	bool delay_prg_fetch_start;
@@ -1089,6 +1141,8 @@ struct sde_mdss_cfg {
 	bool has_3d_merge_reset;
 	bool has_decimation;
 
+	struct sde_sc_cfg sc_cfg;
+
 	bool sui_misr_supported;
 	u32 sui_block_xin_mask;
 
@@ -1158,6 +1212,7 @@ struct sde_mdss_cfg {
 	struct sde_format_extended *vig_formats;
 	struct sde_format_extended *wb_formats;
 	struct sde_format_extended *virt_vig_formats;
+	struct sde_format_extended *inline_rot_formats;
 
 	DECLARE_BITMAP(mdss_irqs, MDSS_INTR_MAX);
 };
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
index 2b4a4de..2dba8e1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
@@ -149,3 +149,10 @@ static const struct sde_format_extended p010_ubwc_formats[] = {
 	{DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_DX |
 		DRM_FORMAT_MOD_QCOM_COMPRESSED},
 };
+
+static const struct sde_format_extended true_inline_rot_v1_fmts[] = {
+	{DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+	{DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED |
+		DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_TIGHT}, /* tp10 */
+	{0, 0},
+};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 5c1f0ff..717fc0b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -919,6 +919,37 @@ static void sde_hw_sspp_setup_cdp(struct sde_hw_pipe *ctx,
 	SDE_REG_WRITE(&ctx->hw, cdp_cntl_offset, cdp_cntl);
 }
 
+static void sde_hw_sspp_setup_sys_cache(struct sde_hw_pipe *ctx,
+		struct sde_hw_pipe_sc_cfg *cfg)
+{
+	u32 idx, val;
+
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+		return;
+
+	if (!cfg)
+		return;
+
+	val = SDE_REG_READ(&ctx->hw, SSPP_SYS_CACHE_MODE + idx);
+
+	if (cfg->flags & SSPP_SYS_CACHE_EN_FLAG)
+		val = (val & ~BIT(15)) | ((cfg->rd_en & 0x1) << 15);
+
+	if (cfg->flags & SSPP_SYS_CACHE_SCID)
+		val = (val & ~0x1F00) | ((cfg->rd_scid & 0x1f) << 8);
+
+	if (cfg->flags & SSPP_SYS_CACHE_OP_MODE)
+		val = (val & ~0xC0000) | ((cfg->op_mode & 0x3) << 18);
+
+	if (cfg->flags & SSPP_SYS_CACHE_OP_TYPE)
+		val = (val & ~0xF) | ((cfg->rd_op_type & 0xf) << 0);
+
+	if (cfg->flags & SSPP_SYS_CACHE_NO_ALLOC)
+		val = (val & ~0x10) | ((cfg->rd_noallocate & 0x1) << 4);
+
+	SDE_REG_WRITE(&ctx->hw, SSPP_SYS_CACHE_MODE + idx, val);
+}
+
 static void _setup_layer_ops_colorproc(struct sde_hw_pipe *c,
 		unsigned long features)
 {
@@ -1112,6 +1143,9 @@ static void _setup_layer_ops(struct sde_hw_pipe *c,
 			c->ops.setup_scaler = reg_dmav1_setup_vig_qseed3;
 	}
 
+	if (test_bit(SDE_PERF_SSPP_SYS_CACHE, &perf_features))
+		c->ops.setup_sys_cache = sde_hw_sspp_setup_sys_cache;
+
 	if (test_bit(SDE_PERF_SSPP_CDP, &perf_features))
 		c->ops.setup_cdp = sde_hw_sspp_setup_cdp;
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index fcfd1d9..7f6ca34 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -248,6 +248,7 @@ enum {
  * @rd_scid: system cache read block id
  * @rd_noallocate: system cache read no allocate attribute
  * @rd_op_type: system cache read operation type
+ * @flags: dirty flags to change the configuration
  */
 struct sde_hw_pipe_sc_cfg {
 	u32 op_mode;
@@ -255,6 +256,7 @@ struct sde_hw_pipe_sc_cfg {
 	u32 rd_scid;
 	bool rd_noallocate;
 	u32 rd_op_type;
+	u32 flags;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3f5e8fb..b94e552 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -204,27 +204,6 @@ static struct sde_hw_ctl *_sde_plane_get_hw_ctl(const struct drm_plane *plane)
 	return ctl;
 }
 
-/**
- * _sde_plane_get_crtc_state - obtain crtc state attached to given plane state
- * @pstate: Pointer to drm plane state
- * return: Pointer to crtc state if success; pointer error, otherwise
- */
-static struct drm_crtc_state *_sde_plane_get_crtc_state(
-		struct drm_plane_state *pstate)
-{
-	struct drm_crtc_state *cstate;
-
-	if (!pstate || !pstate->crtc)
-		return NULL;
-
-	if (pstate->state)
-		cstate = drm_atomic_get_crtc_state(pstate->state, pstate->crtc);
-	else
-		cstate = pstate->crtc->state;
-
-	return cstate;
-}
-
 static bool sde_plane_enabled(const struct drm_plane_state *state)
 {
 	return state && state->fb && state->crtc;
@@ -1013,6 +992,15 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
 	memset(scale_cfg, 0, sizeof(*scale_cfg));
 	memset(&pstate->pixel_ext, 0, sizeof(struct sde_hw_pixel_ext));
 
+	/*
+	 * For inline rotation cases, scaler config is post-rotation,
+	 * so swap the dimensions here. However, pixel extension will
+	 * need pre-rotation settings, this will be corrected below
+	 * when calculating pixel extension settings.
+	 */
+	if (pstate->rotation & DRM_MODE_ROTATE_90)
+		swap(src_w, src_h);
+
 	decimated = DECIMATED_DIMENSION(src_w,
 			psde->pipe_cfg.horz_decimation);
 	scale_cfg->phase_step_x[SDE_SSPP_COMP_0] =
@@ -1049,10 +1037,19 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
 		}
 		scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H;
 		scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V;
-		pstate->pixel_ext.num_ext_pxls_top[i] =
-			scale_cfg->src_height[i];
-		pstate->pixel_ext.num_ext_pxls_left[i] =
-			scale_cfg->src_width[i];
+
+		/* For pixel extension we need the pre-rotated orientation */
+		if (pstate->rotation & DRM_MODE_ROTATE_90) {
+			pstate->pixel_ext.num_ext_pxls_top[i] =
+				scale_cfg->src_width[i];
+			pstate->pixel_ext.num_ext_pxls_left[i] =
+				scale_cfg->src_height[i];
+		} else {
+			pstate->pixel_ext.num_ext_pxls_top[i] =
+				scale_cfg->src_height[i];
+			pstate->pixel_ext.num_ext_pxls_left[i] =
+				scale_cfg->src_width[i];
+		}
 	}
 
 	if ((!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
@@ -1581,18 +1578,20 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
 }
 
 /**
- * sde_plane_rot_atomic_check - verify rotator update of the given state
- * @plane: Pointer to drm plane
- * @state: Pointer to drm plane state to be validated
- * return: 0 if success; error code otherwise
- */
+* sde_plane_rot_atomic_check - verify rotator update of the given state
+* @plane: Pointer to drm plane
+* @state: Pointer to drm plane state to be validated
+* return: 0 if success; error code otherwise
+*/
 static int sde_plane_rot_atomic_check(struct drm_plane *plane,
-		struct drm_plane_state *state)
+	struct drm_plane_state *state)
 {
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate, *old_pstate;
-	struct drm_crtc_state *cstate;
 	int ret = 0;
+	const struct msm_format *msm_fmt;
+	const struct sde_format *fmt;
+	u32 height;
 
 	if (!plane || !state) {
 		SDE_ERROR("invalid plane/state\n");
@@ -1603,19 +1602,65 @@ static int sde_plane_rot_atomic_check(struct drm_plane *plane,
 	pstate = to_sde_plane_state(state);
 	old_pstate = to_sde_plane_state(plane->state);
 
-	/* cstate will be null if crtc is disconnected from plane */
-	cstate = _sde_plane_get_crtc_state(state);
-	if (IS_ERR(cstate)) {
-		ret = PTR_ERR(cstate);
-		SDE_ERROR("invalid crtc state %d\n", ret);
-		return ret;
-	}
-
+	/* check inline rotation and simplify the transform */
 	pstate->rotation = drm_rotation_simplify(
 			state->rotation,
 			DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
 			DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
 
+	if ((pstate->rotation & DRM_MODE_ROTATE_180) ||
+		(pstate->rotation & DRM_MODE_ROTATE_270)) {
+		SDE_ERROR_PLANE(psde,
+			"invalid rotation transform must be simplified 0x%x\n",
+			pstate->rotation);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	msm_fmt = msm_framebuffer_format(state->fb);
+	fmt = to_sde_format(msm_fmt);
+	height = state->fb ? state->fb->height : 0x0;
+
+	if ((pstate->rotation & DRM_MODE_ROTATE_90)) {
+		struct msm_drm_private *priv = plane->dev->dev_private;
+		struct sde_kms *sde_kms;
+
+		if (!psde->pipe_sblk->in_rot_maxdwnscale_rt ||
+			!psde->pipe_sblk->in_rot_maxdwnscale_nrt ||
+			!psde->pipe_sblk->in_rot_maxheight ||
+			!psde->pipe_sblk->in_rot_format_list ||
+			!(psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1))) {
+			SDE_ERROR_PLANE(psde,
+				"wrong config rt:%d nrt:%d fmt:%d h:%d 0x%x\n",
+				!psde->pipe_sblk->in_rot_maxdwnscale_rt,
+				!psde->pipe_sblk->in_rot_maxdwnscale_nrt,
+				!psde->pipe_sblk->in_rot_format_list,
+				!psde->pipe_sblk->in_rot_maxheight,
+				psde->features);
+			ret = -EINVAL;
+			goto exit;
+		}
+
+		/* check for valid height */
+		if (height > psde->pipe_sblk->in_rot_maxheight) {
+			SDE_ERROR_PLANE(psde,
+				"invalid height for inline rot:%d max:%d\n",
+				height, psde->pipe_sblk->in_rot_maxheight);
+			ret = -EINVAL;
+			goto exit;
+		}
+
+		if (!sde_plane_enabled(state))
+			goto exit;
+
+		/* check for valid formats supported by inline rot */
+		sde_kms = to_sde_kms(priv->kms);
+		ret = sde_format_validate_fmt(&sde_kms->base, msm_fmt,
+			psde->pipe_sblk->in_rot_format_list);
+
+	}
+
+exit:
 	return ret;
 }
 
@@ -1716,6 +1761,10 @@ static void sde_plane_rot_install_properties(struct drm_plane *plane,
 		return;
 	}
 
+	if (psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1))
+		supported_rotations |= DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
+			DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270;
+
 	ret = drm_plane_create_rotation_property(plane,
 			DRM_MODE_ROTATE_0, supported_rotations);
 	if (ret) {
@@ -2140,6 +2189,10 @@ static void _sde_plane_sspp_atomic_check_mode_changed(struct sde_plane *psde,
 		   pstate->excl_rect.y != old_pstate->excl_rect.y) {
 		SDE_DEBUG_PLANE(psde, "excl_rect updated\n");
 		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+	} else if (pstate->rotation != old_pstate->rotation) {
+		SDE_DEBUG_PLANE(psde, "rotation updated 0x%x->0x%x\n",
+			pstate->rotation, old_pstate->rotation);
+		pstate->dirty |= SDE_PLANE_DIRTY_FORMAT;
 	}
 
 	fb = state->fb;
@@ -2333,14 +2386,18 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
 	int ret = 0;
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
+	const struct msm_format *msm_fmt;
 	const struct sde_format *fmt;
 	struct sde_rect src, dst;
 	uint32_t deci_w, deci_h, src_deci_w, src_deci_h;
+	uint32_t scaler_src_w, scaler_src_h;
 	uint32_t max_upscale, max_downscale, min_src_size, max_linewidth;
 	bool q16_data = true;
 	struct drm_framebuffer *fb;
 	u32 width;
 	u32 height;
+	bool inline_rotation, rt_client;
+	struct drm_crtc *crtc;
 
 	if (!plane || !state) {
 		SDE_ERROR("invalid arg(s), plane %d state %d\n",
@@ -2370,10 +2427,35 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
 	src_deci_w = DECIMATED_DIMENSION(src.w, deci_w);
 	src_deci_h = DECIMATED_DIMENSION(src.h, deci_h);
 
+	/* with inline rotator, the source of the scaler is post-rotated */
+	inline_rotation = pstate->rotation & DRM_MODE_ROTATE_90 ? true : false;
+	if (inline_rotation) {
+		scaler_src_w = src_deci_h;
+		scaler_src_h = src_deci_w;
+	} else {
+		scaler_src_w = src_deci_w;
+		scaler_src_h = src_deci_h;
+	}
+
 	max_upscale = psde->pipe_sblk->maxupscale;
-	max_downscale = psde->pipe_sblk->maxdwnscale;
 	max_linewidth = psde->pipe_sblk->maxlinewidth;
 
+	crtc = state->crtc;
+	if (crtc)
+		rt_client = (sde_crtc_get_client_type(crtc) != NRT_CLIENT);
+	else
+		rt_client = true;
+
+	/* inline rotation RT clients have a different max downscaling limit */
+	if (inline_rotation) {
+		if (rt_client)
+			max_downscale = psde->pipe_sblk->in_rot_maxdwnscale_rt;
+		else
+			max_downscale = psde->pipe_sblk->in_rot_maxdwnscale_nrt;
+	} else {
+		max_downscale = psde->pipe_sblk->maxdwnscale;
+	}
+
 	SDE_DEBUG_PLANE(psde, "check %d -> %d\n",
 		sde_plane_enabled(plane->state), sde_plane_enabled(state));
 
@@ -2390,13 +2472,14 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
 			width, height,
 			fb ? (char *) &state->fb->format->format : 0x0,
 			fb ? state->fb->modifier : 0x0);
-	SDE_DEBUG("src:%dx%d %d,%d crtc:%dx%d+%d+%d\n"
+	SDE_DEBUG("src:%dx%d %d,%d crtc:%dx%d+%d+%d\n",
 			state->src_w >> 16, state->src_h >> 16,
 			state->src_x >> 16, state->src_y >> 16,
 			state->crtc_w, state->crtc_h,
 			state->crtc_x, state->crtc_y);
 
-	fmt = to_sde_format(msm_framebuffer_format(state->fb));
+	msm_fmt = msm_framebuffer_format(fb);
+	fmt = to_sde_format(msm_fmt);
 
 	min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1;
 
@@ -2458,13 +2541,14 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
 		ret = -E2BIG;
 
 	/* check max scaler capability */
-	} else if (((src_deci_w * max_upscale) < dst.w) ||
-		((src_deci_h * max_upscale) < dst.h) ||
-		((dst.w * max_downscale) < src_deci_w) ||
-		((dst.h * max_downscale) < src_deci_h)) {
+	} else if (((scaler_src_w * max_upscale) < dst.w) ||
+		((scaler_src_h * max_upscale) < dst.h) ||
+		((dst.w * max_downscale) < scaler_src_w) ||
+		((dst.h * max_downscale) < scaler_src_h)) {
 		SDE_ERROR_PLANE(psde,
-			"too much scaling requested %ux%u->%ux%u\n",
-			src_deci_w, src_deci_h, dst.w, dst.h);
+			"too much scaling requested %ux%u->%ux%u rot:%d\n",
+			scaler_src_w, scaler_src_h, dst.w, dst.h,
+			inline_rotation);
 		ret = -E2BIG;
 	} else if (_sde_plane_validate_scaler_v2(psde, pstate, fmt,
 				width, height,
@@ -2585,6 +2669,34 @@ void sde_plane_set_error(struct drm_plane *plane, bool error)
 	psde->is_error = error;
 }
 
+static void _sde_plane_sspp_setup_sys_cache(struct sde_plane *psde,
+	struct sde_plane_state *pstate, const struct sde_format *fmt)
+{
+	if (psde->pipe_hw->ops.setup_sys_cache &&
+		(psde->perf_features & BIT(SDE_PERF_SSPP_SYS_CACHE))) {
+
+		SDE_DEBUG("features:0x%x rotation:0x%x\n",
+			__func__, psde->features, pstate->rotation);
+
+		if ((pstate->rotation & DRM_MODE_ROTATE_90) &&
+				sde_format_is_tp10_ubwc(fmt)) {
+			pstate->sc_cfg.rd_en = true;
+			pstate->sc_cfg.rd_scid =
+				psde->pipe_sblk->llcc_scid;
+			pstate->sc_cfg.flags = SSPP_SYS_CACHE_EN_FLAG |
+				SSPP_SYS_CACHE_SCID;
+		} else {
+			pstate->sc_cfg.rd_en = false;
+			pstate->sc_cfg.rd_scid = 0x0;
+			pstate->sc_cfg.flags = SSPP_SYS_CACHE_EN_FLAG |
+				SSPP_SYS_CACHE_SCID;
+		}
+
+		psde->pipe_hw->ops.setup_sys_cache(
+			psde->pipe_hw, &pstate->sc_cfg);
+	}
+}
+
 static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
 				struct drm_plane_state *old_state)
 {
@@ -2835,12 +2947,17 @@ static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
 	if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT ||
 			pstate->dirty & SDE_PLANE_DIRTY_RECTS) &&
 			psde->pipe_hw->ops.setup_format) {
+
 		src_flags = pstate->pipe_order_flags;
-		SDE_DEBUG_PLANE(psde, "rotation 0x%X\n", pstate->rotation);
+
+		SDE_DEBUG_PLANE(psde, "rotation 0x%x features:0x%x\n",
+			pstate->rotation, psde->features);
 		if (pstate->rotation & DRM_MODE_REFLECT_X)
 			src_flags |= SDE_SSPP_FLIP_LR;
 		if (pstate->rotation & DRM_MODE_REFLECT_Y)
 			src_flags |= SDE_SSPP_FLIP_UD;
+		if (pstate->rotation & DRM_MODE_ROTATE_90)
+			src_flags |= SDE_SSPP_ROT_90;
 
 		/* update format */
 		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt,
@@ -2865,6 +2982,8 @@ static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
 					pstate->multirect_index);
 		}
 
+		_sde_plane_sspp_setup_sys_cache(psde, pstate, fmt);
+
 		/* update csc */
 		if (SDE_FORMAT_IS_YUV(fmt))
 			_sde_plane_setup_csc(psde);
@@ -3007,6 +3126,24 @@ void sde_plane_restore(struct drm_plane *plane)
 	sde_plane_atomic_update(plane, plane->state);
 }
 
+bool sde_plane_is_cache_required(struct drm_plane *plane)
+{
+	struct sde_plane_state *pstate;
+
+	if (!plane || !plane->state) {
+		SDE_ERROR("invalid plane\n");
+		return false;
+	}
+
+	pstate = to_sde_plane_state(plane->state);
+
+	/* check if llcc is required for the plane */
+	if (pstate->sc_cfg.rd_en)
+		return true;
+	else
+		return false;
+}
+
 /* helper to install properties which are common to planes and crtcs */
 static void _sde_plane_install_properties(struct drm_plane *plane,
 	struct sde_mdss_cfg *catalog, u32 master_plane_id)
@@ -3241,6 +3378,32 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
 	if (psde->features & BIT(SDE_SSPP_BLOCK_SEC_UI))
 		sde_kms_info_add_keyint(info, "block_sec_ui", 1);
 
+	if (psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1)) {
+		const struct sde_format_extended *inline_rot_fmt_list;
+
+		sde_kms_info_add_keyint(info, "true_inline_rot_rev", 1);
+		sde_kms_info_add_keyint(info, "true_inline_dwnscale_rt",
+			psde->pipe_sblk->in_rot_maxdwnscale_rt);
+		sde_kms_info_add_keyint(info, "true_inline_dwnscale_nrt",
+			psde->pipe_sblk->in_rot_maxdwnscale_nrt);
+		sde_kms_info_add_keyint(info, "true_inline_max_height",
+			psde->pipe_sblk->in_rot_maxheight);
+
+		inline_rot_fmt_list = psde->pipe_sblk->in_rot_format_list;
+
+		if (inline_rot_fmt_list) {
+			sde_kms_info_start(info, "inline_rot_pixel_formats");
+			while (inline_rot_fmt_list->fourcc_format) {
+				sde_kms_info_append_format(info,
+					inline_rot_fmt_list->fourcc_format,
+					inline_rot_fmt_list->modifier);
+				++inline_rot_fmt_list;
+			}
+			sde_kms_info_stop(info);
+		}
+
+	}
+
 	msm_property_set_blob(&psde->property_info, &psde->blob_info,
 			info->data, SDE_KMS_INFO_DATALEN(info),
 			PLANE_PROP_INFO);
@@ -3943,6 +4106,21 @@ static int _sde_plane_init_debugfs(struct drm_plane *plane)
 				psde->debugfs_root,
 				&psde->debugfs_default_scale);
 
+	if (cfg->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1)) {
+		debugfs_create_u32("in_rot_max_downscale_rt",
+			0600,
+			psde->debugfs_root,
+			(u32 *) &psde->pipe_sblk->in_rot_maxdwnscale_rt);
+		debugfs_create_u32("in_rot_max_downscale_nrt",
+			0600,
+			psde->debugfs_root,
+			(u32 *) &psde->pipe_sblk->in_rot_maxdwnscale_nrt);
+		debugfs_create_u32("in_rot_max_height",
+			0600,
+			psde->debugfs_root,
+			(u32 *) &psde->pipe_sblk->in_rot_maxheight);
+	}
+
 	debugfs_create_u32("xin_id",
 			0400,
 			psde->debugfs_root,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index ca5c4a6..2be78d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -285,4 +285,11 @@ u32 sde_plane_get_ubwc_error(struct drm_plane *plane);
  */
 void sde_plane_clear_ubwc_error(struct drm_plane *plane);
 
+/* sde_plane_is_cache_required - indicates if the system cache is
+ *	required for the plane.
+ * @plane: Pointer to DRM plane object
+ * Returns: true if sys cache is required, otherwise false.
+ */
+bool sde_plane_is_cache_required(struct drm_plane *plane);
+
 #endif /* _SDE_PLANE_H_ */
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index cf82db7..8bf68b3 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2012-2014, 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.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
  */
 
 #include <linux/gpio.h>
@@ -137,6 +129,7 @@ enum pmic_gpio_func_index {
  * struct pmic_gpio_pad - keep current GPIO settings
  * @base: Address base in SPMI device.
  * @irq: IRQ number which this GPIO generate.
+ * @gpio_idx: The index in GPIO's hardware number space (1-based)
  * @is_enabled: Set to false when GPIO should be put in high Z state.
  * @out_value: Cached pin output value
  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
@@ -157,6 +150,7 @@ enum pmic_gpio_func_index {
 struct pmic_gpio_pad {
 	u16		base;
 	int		irq;
+	int		gpio_idx;
 	bool		is_enabled;
 	bool		out_value;
 	bool		have_buffer;
@@ -179,6 +173,7 @@ struct pmic_gpio_state {
 	struct regmap	*map;
 	struct pinctrl_dev *ctrl;
 	struct gpio_chip chip;
+	const char **gpio_groups;
 };
 
 static const struct pinconf_generic_params pmic_gpio_bindings[] = {
@@ -292,7 +287,9 @@ static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
 					 const char *const **groups,
 					 unsigned *const num_qgroups)
 {
-	*groups = pmic_gpio_groups;
+	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = state->gpio_groups;
 	*num_qgroups = pctldev->desc->npins;
 	return 0;
 }
@@ -432,6 +429,9 @@ static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
 			return -EINVAL;
 		arg = 1;
 		break;
+	case PIN_CONFIG_OUTPUT_ENABLE:
+		arg = pad->output_enabled;
+		break;
 	case PIN_CONFIG_OUTPUT:
 		arg = pad->out_value;
 		break;
@@ -511,6 +511,9 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
 		case PIN_CONFIG_INPUT_ENABLE:
 			pad->input_enabled = arg ? true : false;
 			break;
+		case PIN_CONFIG_OUTPUT_ENABLE:
+			pad->output_enabled = arg ? true : false;
+			break;
 		case PIN_CONFIG_OUTPUT:
 			pad->output_enabled = true;
 			pad->out_value = arg;
@@ -644,7 +647,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
 
 	pad = pctldev->desc->pins[pin].drv_data;
 
-	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
+	seq_printf(s, " gpio%-2d:", pad->gpio_idx);
 
 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
 
@@ -749,13 +752,29 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip,
 			      const struct of_phandle_args *gpio_desc,
 			      u32 *flags)
 {
+	int i;
+	struct pmic_gpio_state *state = gpiochip_get_data(chip);
+	struct pinctrl_desc *desc = state->ctrl->desc;
+	struct pmic_gpio_pad *pad;
+
 	if (chip->of_gpio_n_cells < 2)
 		return -EINVAL;
 
 	if (flags)
 		*flags = gpio_desc->args[1];
 
-	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
+	for (i = 0; i < chip->ngpio; i++) {
+		pad = desc->pins[i].drv_data;
+		if (pad->gpio_idx == gpio_desc->args[0]) {
+			dev_dbg(state->dev, "gpio%-2d xlate to pin%-2d\n",
+						gpio_desc->args[0], i);
+			return i;
+		}
+	}
+
+	dev_err(state->dev, "Couldn't find pin for gpio %d\n",
+				gpio_desc->args[0]);
+	return -ENODEV;
 }
 
 static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
@@ -942,43 +961,124 @@ static int pmic_gpio_probe(struct platform_device *pdev)
 	struct pinctrl_desc *pctrldesc;
 	struct pmic_gpio_pad *pad, *pads;
 	struct pmic_gpio_state *state;
-	int ret, npins, i;
-	u32 reg;
+	int ret, npins, ngpios, i, j, pin_idx;
+	int disallowed_count = 0;
+	u32 reg[2], start, size;
+	u32 *disallowed = NULL;
 
-	ret = of_property_read_u32(dev->of_node, "reg", &reg);
+	ret = of_property_read_u32_array(dev->of_node, "reg", reg, 2);
 	if (ret < 0) {
-		dev_err(dev, "missing base address");
+		dev_err(dev, "reg property reading failed\n");
 		return ret;
 	}
+	start = reg[0];
+	size = reg[1];
 
-	npins = platform_irq_count(pdev);
-	if (!npins)
+	ngpios = size / PMIC_GPIO_ADDRESS_RANGE;
+	if (ngpios == 0) {
+		dev_err(dev, "no gpios assigned\n");
+		return -ENODEV;
+	}
+
+	if (ngpios > ARRAY_SIZE(pmic_gpio_groups)) {
+		dev_err(dev, "reg property defines %d gpios, but only %d are allowed\n",
+				ngpios, (int)ARRAY_SIZE(pmic_gpio_groups));
 		return -EINVAL;
-	if (npins < 0)
-		return npins;
+	}
 
-	BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
+	if (of_find_property(dev->of_node, "qcom,gpios-disallowed",
+					&disallowed_count)) {
+		disallowed_count /= sizeof(u32);
+		if (disallowed_count == 0) {
+			dev_err(dev, "No data in gpios-disallowed\n");
+			return -EINVAL;
+		}
+
+		disallowed = kcalloc(disallowed_count, sizeof(u32), GFP_KERNEL);
+		if (disallowed == NULL)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(dev->of_node,
+				"qcom,gpios-disallowed",
+				disallowed, disallowed_count);
+		if (ret < 0) {
+			dev_err(dev, "qcom,gpios-disallowed property reading failed, ret=%d\n",
+								ret);
+			goto err_free;
+		}
+
+		for (i = 0; i < disallowed_count; i++) {
+			if (disallowed[i] >= ngpios + PMIC_GPIO_PHYSICAL_OFFSET
+				|| disallowed[i] < PMIC_GPIO_PHYSICAL_OFFSET) {
+				dev_err(dev, "invalid gpio = %d specified in qcom,gpios-disallowed, supported values: %d to %d\n",
+					disallowed[i],
+					PMIC_GPIO_PHYSICAL_OFFSET,
+					ngpios - 1 + PMIC_GPIO_PHYSICAL_OFFSET);
+				ret = -EINVAL;
+				goto err_free;
+			}
+			for (j = 0; j < i; j++) {
+				if (disallowed[i] == disallowed[j]) {
+					dev_err(dev, "duplicate gpio = %d listed in qcom,gpios-disallowed\n",
+							disallowed[i]);
+					ret = -EINVAL;
+					goto err_free;
+				}
+			}
+			dev_dbg(dev, "gpio %d NOT supported\n", disallowed[i]);
+		}
+	} else {
+		disallowed_count = 0;
+	}
+
+	npins = ngpios - disallowed_count;
+	if (npins <= 0) {
+		dev_err(dev, "No pins assigned\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+	if (platform_irq_count(pdev) != npins) {
+		dev_err(dev, "%d IRQs defined but %d expected\n",
+				platform_irq_count(pdev), npins);
+		ret = -EINVAL;
+		goto err_free;
+	}
 
 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
-	if (!state)
-		return -ENOMEM;
+	if (!state) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
 
 	platform_set_drvdata(pdev, state);
 
 	state->dev = &pdev->dev;
 	state->map = dev_get_regmap(dev->parent, NULL);
 
+	state->gpio_groups = devm_kcalloc(dev, sizeof(*state->gpio_groups),
+						npins, GFP_KERNEL);
+	if (!state->gpio_groups) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
-	if (!pindesc)
-		return -ENOMEM;
+	if (!pindesc) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
 
 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
-	if (!pads)
-		return -ENOMEM;
+	if (!pads) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
 
 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
-	if (!pctrldesc)
-		return -ENOMEM;
+	if (!pctrldesc) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
 
 	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
 	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
@@ -992,22 +1092,42 @@ static int pmic_gpio_probe(struct platform_device *pdev)
 #ifdef CONFIG_DEBUG_FS
 	pctrldesc->custom_conf_items = pmic_conf_items;
 #endif
+	for (pin_idx = 0, i = 0; i < ngpios; i++) {
+		for (j = 0; j < disallowed_count; j++) {
+			if (i + PMIC_GPIO_PHYSICAL_OFFSET == disallowed[j])
+				break;
+		}
+		if (j != disallowed_count)
+			continue;
 
-	for (i = 0; i < npins; i++, pindesc++) {
-		pad = &pads[i];
+		pad = &pads[pin_idx];
 		pindesc->drv_data = pad;
-		pindesc->number = i;
+		pindesc->number = pin_idx;
 		pindesc->name = pmic_gpio_groups[i];
 
-		pad->irq = platform_get_irq(pdev, i);
-		if (pad->irq < 0)
-			return pad->irq;
+		pad->gpio_idx = i + PMIC_GPIO_PHYSICAL_OFFSET;
+		pad->irq = platform_get_irq(pdev, pin_idx);
+		if (pad->irq < 0) {
+			dev_err(state->dev,
+				"failed to get irq for gpio %d (pin %d), ret=%d\n",
+					pad->gpio_idx, pin_idx, pad->irq);
+			ret = pad->irq;
+			goto err_free;
+		}
+		/* Every pin is a group */
+		state->gpio_groups[pin_idx] = pmic_gpio_groups[i];
 
-		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
+		pad->base = start + i * PMIC_GPIO_ADDRESS_RANGE;
 
 		ret = pmic_gpio_populate(state, pad);
-		if (ret < 0)
-			return ret;
+		if (ret < 0) {
+			dev_err(state->dev,
+				"failed to populate gpio %d, ret=%d\n",
+							i, ret);
+			goto err_free;
+		}
+		pindesc++;
+		pin_idx++;
 	}
 
 	state->chip = pmic_gpio_gpio_template;
@@ -1019,25 +1139,29 @@ static int pmic_gpio_probe(struct platform_device *pdev)
 	state->chip.can_sleep = false;
 
 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
-	if (IS_ERR(state->ctrl))
-		return PTR_ERR(state->ctrl);
+	if (IS_ERR(state->ctrl)) {
+		ret = PTR_ERR(state->ctrl);
+		dev_err(state->dev, "failed to register pinctrl device, ret=%d\n",
+							ret);
+		goto err_free;
+	}
 
 	ret = gpiochip_add_data(&state->chip, state);
 	if (ret) {
-		dev_err(state->dev, "can't add gpio chip\n");
-		return ret;
+		dev_err(state->dev, "can't add gpio chip, ret=%d\n", ret);
+		goto err_free;
 	}
 
 	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
 	if (ret) {
-		dev_err(dev, "failed to add pin range\n");
-		goto err_range;
+		dev_err(dev, "failed to add pin range\n, ret=%d\n", ret);
+		gpiochip_remove(&state->chip);
+		goto err_free;
 	}
 
-	return 0;
+err_free:
+	kfree(disallowed);
 
-err_range:
-	gpiochip_remove(&state->chip);
 	return ret;
 }
 
diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c
index ad4f3a8..bda5af5 100644
--- a/drivers/thermal/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom-spmi-temp-alarm.c
@@ -23,6 +23,7 @@
 #include <linux/regmap.h>
 #include <linux/thermal.h>
 
+#define QPNP_TM_REG_DIG_MAJOR		0x01
 #define QPNP_TM_REG_TYPE		0x04
 #define QPNP_TM_REG_SUBTYPE		0x05
 #define QPNP_TM_REG_STATUS		0x08
@@ -42,20 +43,28 @@
 
 #define ALARM_CTRL_FORCE_ENABLE		BIT(7)
 
-/*
- * Trip point values based on threshold control
- * 0 = {105 C, 125 C, 145 C}
- * 1 = {110 C, 130 C, 150 C}
- * 2 = {115 C, 135 C, 155 C}
- * 3 = {120 C, 140 C, 160 C}
-*/
-#define TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
+#define THRESH_COUNT			4
+#define STAGE_COUNT			3
+
+/* Over-temperature trip point values in mC */
+static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
+	{105000, 125000, 145000},
+	{110000, 130000, 150000},
+	{115000, 135000, 155000},
+	{120000, 140000, 160000},
+};
+
+static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
+	{ 90000, 110000, 140000},
+	{ 95000, 115000, 145000},
+	{100000, 120000, 150000},
+	{105000, 125000, 155000},
+};
+
 #define TEMP_STAGE_HYSTERESIS		2000
 
-#define TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
-#define TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
-
 #define THRESH_MIN			0
+#define THRESH_MAX			3
 
 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
 #define DEFAULT_TEMP			37000
@@ -69,7 +78,9 @@ struct qpnp_tm_chip {
 	unsigned int			stage;
 	unsigned int			prev_stage;
 	unsigned int			base;
+	u32				init_thresh;
 	struct iio_channel		*adc;
+	const long			(*temp_map)[THRESH_COUNT][STAGE_COUNT];
 };
 
 /* This array maps from GEN2 alarm state to GEN1 alarm stage */
@@ -94,6 +105,23 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
 }
 
 /**
+ * qpnp_tm_decode_temp() - return temperature in mC corresponding to the
+ *		specified over-temperature stage
+ * @chip:		Pointer to the qpnp_tm chip
+ * @stage:		Over-temperature stage
+ *
+ * Return: temperature in mC
+ */
+static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
+{
+	if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0
+	    || stage > STAGE_COUNT)
+		return 0;
+
+	return (*chip->temp_map)[chip->thresh][stage - 1];
+}
+
+/**
  * qpnp_tm_get_temp_stage() - return over-temperature stage
  * @chip:		Pointer to the qpnp_tm chip
  *
@@ -140,14 +168,12 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
 
 	if (stage_new > stage_old) {
 		/* increasing stage, use lower bound */
-		chip->temp = (stage_new - 1) * TEMP_STAGE_STEP +
-			     chip->thresh * TEMP_THRESH_STEP +
-			     TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+		chip->temp = qpnp_tm_decode_temp(chip, stage_new)
+				+ TEMP_STAGE_HYSTERESIS;
 	} else if (stage_new < stage_old) {
 		/* decreasing stage, use upper bound */
-		chip->temp = stage_new * TEMP_STAGE_STEP +
-			     chip->thresh * TEMP_THRESH_STEP -
-			     TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+		chip->temp = qpnp_tm_decode_temp(chip, stage_new + 1)
+				- TEMP_STAGE_HYSTERESIS;
 	}
 
 	chip->stage = stage;
@@ -220,15 +246,13 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
 		? chip->stage : alarm_state_map[chip->stage];
 
 	if (stage)
-		chip->temp = chip->thresh * TEMP_THRESH_STEP +
-			     (stage - 1) * TEMP_STAGE_STEP +
-			     TEMP_THRESH_MIN;
+		chip->temp = qpnp_tm_decode_temp(chip, stage);
 
 	/*
 	 * Set threshold and disable software override of stage 2 and 3
 	 * shutdowns.
 	 */
-	chip->thresh = THRESH_MIN;
+	chip->thresh = chip->init_thresh;
 	reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK);
 	reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
 	ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
@@ -246,7 +270,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 {
 	struct qpnp_tm_chip *chip;
 	struct device_node *node;
-	u8 type, subtype;
+	u8 type, subtype, dig_major;
 	u32 res;
 	int ret, irq;
 
@@ -266,6 +290,16 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 	if (ret < 0)
 		return ret;
 
+	chip->init_thresh = THRESH_MIN;
+	if (of_property_read_u32(node, "qcom,temperature-threshold-set",
+				 &chip->init_thresh) == 0) {
+		if (chip->init_thresh > THRESH_MAX) {
+			dev_err(&pdev->dev, "Invalid qcom,temperature-threshold-set=%u\n",
+				chip->init_thresh);
+			return -EINVAL;
+		}
+	}
+
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
@@ -293,6 +327,12 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MAJOR, &dig_major);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not read dig_major\n");
+		return ret;
+	}
+
 	if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
 				     && subtype != QPNP_TM_SUBTYPE_GEN2)) {
 		dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
@@ -302,6 +342,11 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 
 	chip->subtype = subtype;
 
+	if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
+		chip->temp_map = &temp_map_gen2_v1;
+	else
+		chip->temp_map = &temp_map_gen1;
+
 	ret = qpnp_tm_init(chip);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "init failed\n");