msm: sde: Add mdss 3.0.0 support to sde v4l2 rotator driver

Add mdss 3.0.0 regdma support to sde v4l2 rotator driver.
Regdma enables hardware queueing of multiple concurrent rotation
requests. This reduces software overhead between submission
and improves overall performance.

CRs-Fixed: 972831

Change-Id: I5bb308e552d7ed6edd314a4574181e1ee3498960
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
diff --git a/drivers/media/platform/msm/sde/rotator/Makefile b/drivers/media/platform/msm/sde/rotator/Makefile
index 4c3ec132..dd496e0 100644
--- a/drivers/media/platform/msm/sde/rotator/Makefile
+++ b/drivers/media/platform/msm/sde/rotator/Makefile
@@ -15,9 +15,13 @@
 		sde_rotator_r1_ctl.o \
 		sde_rotator_r1.o
 
+obj-y += \
+		sde_rotator_r3.o
+
 obj-$(CONFIG_SYNC) += \
 		sde_rotator_sync.o
 
 obj-$(CONFIG_DEBUG_FS) += \
 		sde_rotator_debug.o \
-		sde_rotator_r1_debug.o
\ No newline at end of file
+		sde_rotator_r1_debug.o \
+		sde_rotator_r3_debug.o
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index f52e145..ea06636 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -33,6 +33,7 @@
 #include "sde_rotator_io_util.h"
 #include "sde_rotator_smmu.h"
 #include "sde_rotator_r1.h"
+#include "sde_rotator_r3.h"
 #include "sde_rotator_trace.h"
 
 /* waiting for hw time out, 3 vsync for 30fps*/
@@ -2278,6 +2279,8 @@
 
 	if ((mdata->mdss_version & 0xFFFF0000) == 0x10070000) {
 		mgr->ops_hw_init = sde_rotator_r1_init;
+	} else if ((mdata->mdss_version & 0xFFFF0000) == 0x30000000) {
+		mgr->ops_hw_init = sde_rotator_r3_init;
 	} else {
 		SDEROT_ERR("unsupported sde version %x\n",
 				mdata->mdss_version);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
new file mode 100644
index 0000000..a054736
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -0,0 +1,1873 @@
+/* Copyright (c) 2015-2016, 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)	"%s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/sync.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/msm_ion.h>
+
+#include "sde_rotator_core.h"
+#include "sde_rotator_util.h"
+#include "sde_rotator_smmu.h"
+#include "sde_rotator_r3.h"
+#include "sde_rotator_r3_internal.h"
+#include "sde_rotator_r3_hwio.h"
+#include "sde_rotator_r3_debug.h"
+#include "sde_rotator_trace.h"
+
+/* XIN mapping */
+#define XIN_SSPP		0
+#define XIN_WRITEBACK		1
+
+/* wait for at most 2 vsync for lowest refresh rate (24hz) */
+#define KOFF_TIMEOUT msecs_to_jiffies(42 * 32)
+
+/* Macro for constructing the REGDMA command */
+#define SDE_REGDMA_WRITE(p, off, data) \
+	do { \
+		*p++ = REGDMA_OP_REGWRITE | \
+			((off) & REGDMA_ADDR_OFFSET_MASK); \
+		*p++ = (data); \
+	} while (0)
+
+#define SDE_REGDMA_MODIFY(p, off, mask, data) \
+	do { \
+		*p++ = REGDMA_OP_REGMODIFY | \
+			((off) & REGDMA_ADDR_OFFSET_MASK); \
+		*p++ = (mask); \
+		*p++ = (data); \
+	} while (0)
+
+#define SDE_REGDMA_BLKWRITE_INC(p, off, len) \
+	do { \
+		*p++ = REGDMA_OP_BLKWRITE_INC | \
+			((off) & REGDMA_ADDR_OFFSET_MASK); \
+		*p++ = (len); \
+	} while (0)
+
+#define SDE_REGDMA_BLKWRITE_DATA(p, data) \
+	do { \
+		*(p) = (data); \
+		(p)++; \
+	} while (0)
+
+/* Macro for directly accessing mapped registers */
+#define SDE_ROTREG_WRITE(base, off, data) \
+	writel_relaxed(data, (base + (off)))
+
+#define SDE_ROTREG_READ(base, off) \
+	readl_relaxed(base + (off))
+
+/**
+ * sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
+ * on provided session_id. Each rotator has a different session_id.
+ */
+static struct sde_hw_rotator_context *sde_hw_rotator_get_ctx(
+		struct sde_hw_rotator *rot, u32 session_id,
+		enum sde_rot_queue_prio q_id)
+{
+	int i;
+	struct sde_hw_rotator_context  *ctx = NULL;
+
+	for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++) {
+		ctx = rot->rotCtx[q_id][i];
+
+		if (ctx && (ctx->session_id == session_id)) {
+			SDEROT_DBG(
+				"rotCtx sloti[%d][%d] ==> ctx:%p | session-id:%d\n",
+				q_id, i, ctx, ctx->session_id);
+			return ctx;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * sde_hw_rotator_map_vaddr - map the debug buffer to kernel space
+ * @dbgbuf: Pointer to debug buffer
+ * @buf: Pointer to layer buffer structure
+ * @data: Pointer to h/w mapped buffer structure
+ */
+static void sde_hw_rotator_map_vaddr(struct sde_dbg_buf *dbgbuf,
+		struct sde_layer_buffer *buf, struct sde_mdp_data *data)
+{
+	dbgbuf->dmabuf = data->p[0].srcp_dma_buf;
+	dbgbuf->buflen = data->p[0].srcp_dma_buf->size;
+
+	dbgbuf->vaddr  = NULL;
+	dbgbuf->width  = buf->width;
+	dbgbuf->height = buf->height;
+
+	if (dbgbuf->dmabuf && (dbgbuf->buflen > 0)) {
+		dma_buf_begin_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
+				DMA_FROM_DEVICE);
+		dbgbuf->vaddr = dma_buf_kmap(dbgbuf->dmabuf, 0);
+		SDEROT_DBG("vaddr mapping: 0x%p/%ld w:%d/h:%d\n",
+				dbgbuf->vaddr, dbgbuf->buflen,
+				dbgbuf->width, dbgbuf->height);
+	}
+}
+
+/*
+ * sde_hw_rotator_unmap_vaddr - unmap the debug buffer from kernel space
+ * @dbgbuf: Pointer to debug buffer
+ */
+static void sde_hw_rotator_unmap_vaddr(struct sde_dbg_buf *dbgbuf)
+{
+	if (dbgbuf->vaddr) {
+		dma_buf_kunmap(dbgbuf->dmabuf, 0, dbgbuf->vaddr);
+		dma_buf_end_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
+				DMA_FROM_DEVICE);
+	}
+
+	dbgbuf->vaddr  = NULL;
+	dbgbuf->dmabuf = NULL;
+	dbgbuf->buflen = 0;
+	dbgbuf->width  = 0;
+	dbgbuf->height = 0;
+}
+
+/*
+ * sde_hw_rotator_setup_timestamp_packet - setup timestamp writeback command
+ * @ctx: Pointer to rotator context
+ * @mask: Bit mask location of the timestamp
+ * @swts: Software timestamp
+ */
+static void sde_hw_rotator_setup_timestamp_packet(
+		struct sde_hw_rotator_context *ctx, u32 mask, u32 swts)
+{
+	u32 *wrptr;
+
+	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+	/*
+	 * Create a dummy packet write out to 1 location for timestamp
+	 * generation.
+	 */
+	SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 6);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
+	SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_YSTRIDE0, 4);
+	SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_FORMAT, 4);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x004037FF);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x80000000);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->timestamp);
+	SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 4);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x000037FF);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_YSTRIDE0, 4);
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE, 0x00010001);
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE, 0x00010001);
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY, 0);
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC, 0);
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 1);
+	SDE_REGDMA_MODIFY(wrptr, REGDMA_TIMESTAMP_REG, mask, swts);
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_fetchengine - setup fetch engine
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @cfg: Fetch configuration
+ * @danger_lut: real-time QoS LUT for danger setting (not used)
+ * @safe_lut: real-time QoS LUT for safe setting (not used)
+ * @flags: Control flag
+ */
+static void sde_hw_rotator_setup_fetchengine(struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id,
+		struct sde_hw_rot_sspp_cfg *cfg, u32 danger_lut, u32 safe_lut,
+		u32 flags)
+{
+	struct sde_hw_rotator *rot = ctx->rot;
+	struct sde_mdp_format_params *fmt;
+	struct sde_mdp_data *data;
+	u32 *wrptr;
+	u32 opmode = 0;
+	u32 chroma_samp = 0;
+	u32 src_format = 0;
+	u32 unpack = 0;
+	u32 width = cfg->img_width;
+	u32 height = cfg->img_height;
+	u32 fetch_blocksize = 0;
+	int i;
+
+	if (ctx->rot->mode == ROT_REGDMA_ON) {
+		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_EN,
+				REGDMA_INT_MASK);
+		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_OP_MODE,
+				REGDMA_EN);
+	}
+
+	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+	/* source image setup */
+	if ((flags & SDE_ROT_FLAG_DEINTERLACE)
+			&& !(flags & SDE_ROT_FLAG_SOURCE_ROTATED_90)) {
+		for (i = 0; i < cfg->src_plane.num_planes; i++)
+			cfg->src_plane.ystride[i] *= 2;
+		width *= 2;
+		height /= 2;
+	}
+
+	/*
+	 * REGDMA BLK write from SRC_SIZE to OP_MODE, total 15 registers
+	 */
+	SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 15);
+
+	/* SRC_SIZE, SRC_IMG_SIZE, SRC_XY, OUT_SIZE, OUT_XY */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr,
+			cfg->src_rect->w | (cfg->src_rect->h << 16));
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0); /* SRC_IMG_SIZE unused */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr,
+			cfg->src_rect->x | (cfg->src_rect->y << 16));
+	SDE_REGDMA_BLKWRITE_DATA(wrptr,
+			cfg->src_rect->w | (cfg->src_rect->h << 16));
+	SDE_REGDMA_BLKWRITE_DATA(wrptr,
+			cfg->src_rect->x | (cfg->src_rect->y << 16));
+
+	/* SRC_ADDR [0-3], SRC_YSTRIDE [0-1] */
+	data = cfg->data;
+	for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
+		SDE_REGDMA_BLKWRITE_DATA(wrptr, data->p[i].addr);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[0] |
+			(cfg->src_plane.ystride[1] << 16));
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[2] |
+			(cfg->src_plane.ystride[3] << 16));
+
+	/* UNUSED, write 0 */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+
+	/* setup source format */
+	fmt = cfg->fmt;
+
+	chroma_samp = fmt->chroma_sample;
+	if (flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) {
+		if (chroma_samp == SDE_MDP_CHROMA_H2V1)
+			chroma_samp = SDE_MDP_CHROMA_H1V2;
+		else if (chroma_samp == SDE_MDP_CHROMA_H1V2)
+			chroma_samp = SDE_MDP_CHROMA_H2V1;
+	}
+
+	src_format = (chroma_samp << 23)   |
+		(fmt->fetch_planes << 19)  |
+		(fmt->bits[C3_ALPHA] << 6) |
+		(fmt->bits[C2_R_Cr] << 4)  |
+		(fmt->bits[C1_B_Cb] << 2)  |
+		(fmt->bits[C0_G_Y] << 0);
+
+	if (fmt->alpha_enable &&
+			(fmt->fetch_planes == SDE_MDP_PLANE_INTERLEAVED))
+		src_format |= BIT(8); /* SRCC3_EN */
+
+	src_format |= ((fmt->unpack_count - 1) << 12) |
+			(fmt->unpack_tight << 17)       |
+			(fmt->unpack_align_msb << 18)   |
+			((fmt->bpp - 1) << 9)           |
+			((fmt->frame_format & 3) << 30);
+
+	if (flags & SDE_ROT_FLAG_ROT_90)
+		src_format |= BIT(11);	/* ROT90 */
+
+	if (sde_mdp_is_ubwc_format(fmt))
+		opmode |= BIT(0); /* BWC_DEC_EN */
+
+	/* if this is YUV pixel format, enable CSC */
+	if (sde_mdp_is_yuv_format(fmt))
+		src_format |= BIT(15); /* SRC_COLOR_SPACE */
+
+	if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
+		src_format |= BIT(14); /* UNPACK_DX_FORMAT */
+
+	/* SRC_FORMAT */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, src_format);
+
+	/* setup source unpack pattern */
+	unpack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
+		 (fmt->element[1] << 8)  | (fmt->element[0] << 0);
+
+	/* SRC_UNPACK_PATTERN */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, unpack);
+
+	/* setup source op mode */
+	if (flags & SDE_ROT_FLAG_FLIP_LR)
+		opmode |= BIT(13); /* FLIP_MODE L/R horizontal flip */
+	if (flags & SDE_ROT_FLAG_FLIP_UD)
+		opmode |= BIT(14); /* FLIP_MODE U/D vertical flip */
+	opmode |= BIT(31); /* MDSS_MDP_OP_PE_OVERRIDE */
+
+	/* SRC_OP_MODE */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, opmode);
+
+	/* setup source fetch config, TP10 uses different block size */
+	if (sde_mdp_is_tp10_format(fmt))
+		fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_96;
+	else
+		fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_128;
+	SDE_REGDMA_WRITE(wrptr, ROT_SSPP_FETCH_CONFIG,
+			fetch_blocksize |
+			SDE_ROT_SSPP_FETCH_CONFIG_RESET_VALUE |
+			((rot->highest_bank & 0x3) << 18));
+
+	/* setup source buffer plane security status */
+	if (flags & SDE_ROT_FLAG_SECURE_OVERLAY_SESSION) {
+		SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0xF);
+		ctx->is_secure = true;
+	}
+
+	/* Update command queue write ptr */
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_wbengine - setup writeback engine
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @cfg: Writeback configuration
+ * @flags: Control flag
+ */
+static void sde_hw_rotator_setup_wbengine(struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id,
+		struct sde_hw_rot_wb_cfg *cfg,
+		u32 flags)
+{
+	struct sde_mdp_format_params *fmt;
+	u32 *wrptr;
+	u32 pack = 0;
+	u32 dst_format = 0;
+	int i;
+
+	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+	fmt = cfg->fmt;
+
+	/* setup WB DST format */
+	dst_format |= (fmt->chroma_sample << 23) |
+			(fmt->fetch_planes << 19)  |
+			(fmt->bits[C3_ALPHA] << 6) |
+			(fmt->bits[C2_R_Cr] << 4)  |
+			(fmt->bits[C1_B_Cb] << 2)  |
+			(fmt->bits[C0_G_Y] << 0);
+
+	/* alpha control */
+	if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
+		dst_format |= BIT(8);
+		if (!fmt->alpha_enable) {
+			dst_format |= BIT(14);
+			SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ALPHA_X_VALUE, 0);
+		}
+	}
+
+	dst_format |= ((fmt->unpack_count - 1) << 12)	|
+			(fmt->unpack_tight << 17)	|
+			(fmt->unpack_align_msb << 18)	|
+			((fmt->bpp - 1) << 9)		|
+			((fmt->frame_format & 3) << 30);
+
+	if (sde_mdp_is_yuv_format(fmt))
+		dst_format |= BIT(15);
+
+	if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
+		dst_format |= BIT(21); /* PACK_DX_FORMAT */
+
+	/*
+	 * REGDMA BLK write, from DST_FORMAT to DST_YSTRIDE 1, total 9 regs
+	 */
+	SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 9);
+
+	/* DST_FORMAT */
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, dst_format);
+
+	/* DST_OP_MODE */
+	if (sde_mdp_is_ubwc_format(fmt))
+		SDE_REGDMA_BLKWRITE_DATA(wrptr, BIT(0));
+	else
+		SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
+
+	/* DST_PACK_PATTERN */
+	pack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
+		(fmt->element[1] << 8) | (fmt->element[0] << 0);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, pack);
+
+	/* DST_ADDR [0-3], DST_YSTRIDE [0-1] */
+	for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
+		SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->data->p[i].addr);
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[0] |
+			(cfg->dst_plane.ystride[1] << 16));
+	SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[2] |
+			(cfg->dst_plane.ystride[3] << 16));
+
+	/* setup WB out image size and ROI */
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE,
+			cfg->img_width | (cfg->img_height << 16));
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE,
+			cfg->dst_rect->w | (cfg->dst_rect->h << 16));
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY,
+			cfg->dst_rect->x | (cfg->dst_rect->y << 16));
+
+	/*
+	 * setup Downscale factor
+	 */
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC,
+			cfg->v_downscale_factor |
+			(cfg->h_downscale_factor << 16));
+
+	/* write config setup for bank configration */
+	SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_WRITE_CONFIG,
+			(ctx->rot->highest_bank & 0x3) << 8);
+
+	if (flags & SDE_ROT_FLAG_ROT_90)
+		SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x3);
+	else
+		SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x1);
+
+	/* Update command queue write ptr */
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_start_no_regdma - start non-regdma operation
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ */
+static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id)
+{
+	struct sde_hw_rotator *rot = ctx->rot;
+	u32 *wrptr;
+	u32 *rdptr;
+	u8 *addr;
+	u32 mask;
+	u32 blksize;
+
+	rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
+	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+	if (rot->irq_num >= 0) {
+		SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
+		SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
+		reinit_completion(&ctx->rot_comp);
+		enable_irq(rot->irq_num);
+	}
+
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+
+	/* Update command queue write ptr */
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+	SDEROT_DBG("BEGIN %d\n", ctx->timestamp);
+	/* Write all command stream to Rotator blocks */
+	/* Rotator will start right away after command stream finish writing */
+	while (rdptr < wrptr) {
+		u32 op = REGDMA_OP_MASK & *rdptr;
+
+		switch (op) {
+		case REGDMA_OP_NOP:
+			SDEROT_DBG("NOP\n");
+			rdptr++;
+			break;
+		case REGDMA_OP_REGWRITE:
+			SDEROT_DBG("REGW %6.6x %8.8x\n",
+					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+					rdptr[1]);
+			addr =  rot->mdss_base +
+				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+			writel_relaxed(*rdptr++, addr);
+			break;
+		case REGDMA_OP_REGMODIFY:
+			SDEROT_DBG("REGM %6.6x %8.8x %8.8x\n",
+					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+					rdptr[1], rdptr[2]);
+			addr =  rot->mdss_base +
+				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+			mask = *rdptr++;
+			writel_relaxed((readl_relaxed(addr) & mask) | *rdptr++,
+					addr);
+			break;
+		case REGDMA_OP_BLKWRITE_SINGLE:
+			SDEROT_DBG("BLKWS %6.6x %6.6x\n",
+					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+					rdptr[1]);
+			addr =  rot->mdss_base +
+				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+			blksize = *rdptr++;
+			while (blksize--) {
+				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
+				writel_relaxed(*rdptr++, addr);
+			}
+			break;
+		case REGDMA_OP_BLKWRITE_INC:
+			SDEROT_DBG("BLKWI %6.6x %6.6x\n",
+					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
+					rdptr[1]);
+			addr =  rot->mdss_base +
+				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
+			blksize = *rdptr++;
+			while (blksize--) {
+				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
+				writel_relaxed(*rdptr++, addr);
+				addr += 4;
+			}
+			break;
+		default:
+			/* Other not supported OP mode
+			 * Skip data for now for unregonized OP mode
+			 */
+			SDEROT_DBG("UNDEFINED\n");
+			rdptr++;
+			break;
+		}
+	}
+	SDEROT_DBG("END %d\n", ctx->timestamp);
+
+	return ctx->timestamp;
+}
+
+/*
+ * sde_hw_rotator_start_regdma - start regdma operation
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ */
+static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id)
+{
+	struct sde_hw_rotator *rot = ctx->rot;
+	u32 *wrptr;
+	u32  regdmaSlot;
+	u32  offset;
+	long length;
+	long ts_length;
+	u32  enableInt;
+	u32  swts = 0;
+	u32  mask = 0;
+
+	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+	if (rot->irq_num >= 0)
+		reinit_completion(&ctx->regdma_comp);
+
+	/* enable IRQ for first regdma submission from idle */
+	if (atomic_read(&rot->regdma_submit_count) ==
+				atomic_read(&rot->regdma_done_count)) {
+		SDEROT_DBG("Enable IRQ! regdma submitcnt==donecnt -> %d\n",
+				atomic_read(&rot->regdma_submit_count));
+		enable_irq(rot->irq_num);
+	}
+
+	/*
+	 * Last ROT command must be ROT_START before REGDMA start
+	 */
+	SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+	/*
+	 * Start REGDMA with command offset and size
+	 */
+	regdmaSlot = sde_hw_rotator_get_regdma_ctxidx(ctx);
+	length = ((long)wrptr - (long)ctx->regdma_base) / 4;
+	offset = (u32)(ctx->regdma_base - (u32 *)(rot->mdss_base +
+				REGDMA_RAM_REGDMA_CMD_RAM));
+	enableInt = ((ctx->timestamp & 1) + 1) << 30;
+
+	SDEROT_DBG(
+		"regdma(%d)[%d] <== INT:0x%X|length:%ld|offset:0x%X, ts:%X\n",
+		queue_id, regdmaSlot, enableInt, length, offset,
+		ctx->timestamp);
+
+	/* ensure the command packet is issued before the submit command */
+	wmb();
+
+	/* REGDMA submission for current context */
+	if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
+		SDE_ROTREG_WRITE(rot->mdss_base,
+				REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
+				(length << 14) | offset);
+		swts = ctx->timestamp;
+		mask = ~SDE_REGDMA_SWTS_MASK;
+	} else {
+		SDE_ROTREG_WRITE(rot->mdss_base,
+				REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
+				(length << 14) | offset);
+		swts = ctx->timestamp << SDE_REGDMA_SWTS_SHIFT;
+		mask = ~(SDE_REGDMA_SWTS_MASK << SDE_REGDMA_SWTS_SHIFT);
+	}
+
+	/* Write timestamp after previous rotator job finished */
+	sde_hw_rotator_setup_timestamp_packet(ctx, mask, swts);
+	offset += length;
+	ts_length = sde_hw_rotator_get_regdma_segment(ctx) - wrptr;
+	WARN_ON((length + ts_length) > SDE_HW_ROT_REGDMA_SEG_SIZE);
+
+	/* ensure command packet is issue before the submit command */
+	wmb();
+
+	if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
+		SDE_ROTREG_WRITE(rot->mdss_base,
+				REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
+				enableInt | (ts_length << 14) | offset);
+	} else {
+		SDE_ROTREG_WRITE(rot->mdss_base,
+				REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
+				enableInt | (ts_length << 14) | offset);
+	}
+
+	/* Update REGDMA submit count */
+	atomic_inc(&rot->regdma_submit_count);
+
+	/* Update command queue write ptr */
+	sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+	return ctx->timestamp;
+}
+
+/*
+ * sde_hw_rotator_wait_done_no_regdma - wait for non-regdma completion
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @flags: Option flag
+ */
+static u32 sde_hw_rotator_wait_done_no_regdma(
+		struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id, u32 flag)
+{
+	struct sde_hw_rotator *rot = ctx->rot;
+	int rc = 0;
+	u32 sts = 0;
+	u32 status;
+	unsigned long flags;
+
+	if (rot->irq_num >= 0) {
+		SDEROT_DBG("Wait for Rotator completion\n");
+		rc = wait_for_completion_timeout(&ctx->rot_comp,
+					KOFF_TIMEOUT);
+
+		spin_lock_irqsave(&rot->rotisr_lock, flags);
+		status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+		if (rc == 0) {
+			/*
+			 * Timeout, there might be error,
+			 * or rotator still busy
+			 */
+			if (status & ROT_BUSY_BIT)
+				SDEROT_ERR(
+					"Timeout waiting for rotator done\n");
+			else if (status & ROT_ERROR_BIT)
+				SDEROT_ERR(
+					"Rotator report error status\n");
+			else
+				SDEROT_WARN(
+					"Timeout waiting, but rotator job is done!!\n");
+
+			disable_irq_nosync(rot->irq_num);
+		}
+		spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+	} else {
+		int cnt = 200;
+
+		do {
+			udelay(500);
+			status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+			cnt--;
+		} while ((cnt > 0) && (status & ROT_BUSY_BIT)
+				&& ((status & ROT_ERROR_BIT) == 0));
+
+		if (status & ROT_ERROR_BIT)
+			SDEROT_ERR("Rotator error\n");
+		else if (status & ROT_BUSY_BIT)
+			SDEROT_ERR("Rotator busy\n");
+
+		SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+				ROT_DONE_CLEAR);
+	}
+
+	sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+
+	return sts;
+}
+
+/*
+ * sde_hw_rotator_wait_done_regdma - wait for regdma completion
+ * @ctx: Pointer to rotator context
+ * @queue_id: Priority queue identifier
+ * @flags: Option flag
+ */
+static u32 sde_hw_rotator_wait_done_regdma(
+		struct sde_hw_rotator_context *ctx,
+		enum sde_rot_queue_prio queue_id, u32 flag)
+{
+	struct sde_hw_rotator *rot = ctx->rot;
+	int rc = 0;
+	u32 status;
+	u32 last_isr;
+	u32 last_ts;
+	u32 int_id;
+	u32 sts = 0;
+	u32 d_count;
+	unsigned long flags;
+
+	if (rot->irq_num >= 0) {
+		SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
+				ctx, ctx->timestamp);
+		rc = wait_for_completion_timeout(&ctx->regdma_comp,
+				KOFF_TIMEOUT);
+
+		spin_lock_irqsave(&rot->rotisr_lock, flags);
+
+		last_isr = ctx->last_regdma_isr_status;
+		last_ts  = ctx->last_regdma_timestamp;
+		status   = last_isr & REGDMA_INT_MASK;
+		int_id   = last_ts & 1;
+		SDEROT_DBG("INT status:0x%X, INT id:%d, timestamp:0x%X\n",
+				status, int_id, last_ts);
+
+		if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+			SDEROT_ERR(
+				"Timeout wait for regdma interrupt status, ts:%X\n",
+				ctx->timestamp);
+
+			if (status & REGDMA_WATCHDOG_INT)
+				SDEROT_ERR("REGDMA watchdog interrupt\n");
+			else if (status & REGDMA_INVALID_DESCRIPTOR)
+				SDEROT_ERR("REGDMA invalid descriptor\n");
+			else if (status & REGDMA_INCOMPLETE_CMD)
+				SDEROT_ERR("REGDMA incomplete command\n");
+			else if (status & REGDMA_INVALID_CMD)
+				SDEROT_ERR("REGDMA invalid command\n");
+
+			status = ROT_ERROR_BIT;
+		} else if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
+			/* Got to match exactly with interrupt ID */
+			int_id = REGDMA_QUEUE0_INT0 << int_id;
+
+			SDE_ROTREG_WRITE(rot->mdss_base,
+					REGDMA_CSR_REGDMA_INT_CLEAR,
+					int_id);
+
+			status = 0;
+		} else if (queue_id == ROT_QUEUE_LOW_PRIORITY) {
+			/* Matching interrupt ID */
+			int_id = REGDMA_QUEUE1_INT0 << int_id;
+
+			SDE_ROTREG_WRITE(rot->mdss_base,
+					REGDMA_CSR_REGDMA_INT_CLEAR,
+					int_id);
+
+			status = 0;
+		}
+
+		/* regardless success or timeout, update done count */
+		d_count = atomic_inc_return(&rot->regdma_done_count);
+
+		/* disable IRQ if no more regdma submission in queue */
+		if (d_count == atomic_read(&rot->regdma_submit_count)) {
+			SDEROT_DBG(
+				"Disable IRQ!! regdma donecnt==submitcnt -> %d\n",
+				d_count);
+			disable_irq_nosync(rot->irq_num);
+		}
+
+		spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+	} else {
+		int cnt = 200;
+
+		do {
+			udelay(500);
+			status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+			cnt--;
+		} while ((cnt > 0) && (status & ROT_BUSY_BIT)
+				&& ((status & ROT_ERROR_BIT) == 0));
+
+		if (status & ROT_ERROR_BIT)
+			SDEROT_ERR("Rotator error\n");
+		else if (status & ROT_BUSY_BIT)
+			SDEROT_ERR("Rotator busy\n");
+
+		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
+				0xFFFF);
+	}
+
+	sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+
+	return sts;
+}
+
+/*
+ * setup_rotator_ops - setup callback functions for the low-level HAL
+ * @ops: Pointer to low-level ops callback
+ * @mode: Operation mode (non-regdma or regdma)
+ */
+static void setup_rotator_ops(struct sde_hw_rotator_ops *ops,
+		enum sde_rotator_regdma_mode mode)
+{
+	ops->setup_rotator_fetchengine = sde_hw_rotator_setup_fetchengine;
+	ops->setup_rotator_wbengine = sde_hw_rotator_setup_wbengine;
+	if (mode == ROT_REGDMA_ON) {
+		ops->start_rotator = sde_hw_rotator_start_regdma;
+		ops->wait_rotator_done = sde_hw_rotator_wait_done_regdma;
+	} else {
+		ops->start_rotator = sde_hw_rotator_start_no_regdma;
+		ops->wait_rotator_done = sde_hw_rotator_wait_done_no_regdma;
+	}
+}
+
+/*
+ * sde_hw_rotator_swts_create - create software timestamp buffer
+ * @rot: Pointer to rotator hw
+ *
+ * This buffer is used by regdma to keep track of last completed command.
+ */
+static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot)
+{
+	int rc = 0;
+	struct ion_handle *handle;
+	struct sde_mdp_img_data *data;
+	u32 bufsize = sizeof(int) * SDE_HW_ROT_REGDMA_TOTAL_CTX * 2;
+
+	rot->iclient = msm_ion_client_create(rot->pdev->name);
+	if (IS_ERR_OR_NULL(rot->iclient)) {
+		SDEROT_ERR("msm_ion_client_create() return error (%p)\n",
+				rot->iclient);
+		return -EINVAL;
+	}
+
+	handle = ion_alloc(rot->iclient, bufsize, SZ_4K,
+			ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(handle)) {
+		SDEROT_ERR("ion memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	data = &rot->swts_buf;
+	data->len = bufsize;
+	data->srcp_dma_buf = ion_share_dma_buf(rot->iclient, handle);
+	if (IS_ERR(data->srcp_dma_buf)) {
+		SDEROT_ERR("ion_dma_buf setup failed\n");
+		rc = -ENOMEM;
+		goto imap_err;
+	}
+
+	sde_smmu_ctrl(1);
+
+	data->srcp_attachment = sde_smmu_dma_buf_attach(data->srcp_dma_buf,
+			&rot->pdev->dev, SDE_IOMMU_DOMAIN_ROT_UNSECURE);
+	if (IS_ERR_OR_NULL(data->srcp_attachment)) {
+		SDEROT_ERR("sde_smmu_dma_buf_attach error\n");
+		rc = -ENOMEM;
+		goto err_put;
+	}
+
+	data->srcp_table = dma_buf_map_attachment(data->srcp_attachment,
+			DMA_BIDIRECTIONAL);
+	if (IS_ERR_OR_NULL(data->srcp_table)) {
+		SDEROT_ERR("dma_buf_map_attachment error\n");
+		rc = -ENOMEM;
+		goto err_detach;
+	}
+
+	rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table,
+			SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr,
+			&data->len, DMA_BIDIRECTIONAL);
+	if (IS_ERR_VALUE(rc)) {
+		SDEROT_ERR("smmu_map_dma_buf failed: (%d)\n", rc);
+		goto err_unmap;
+	}
+
+	dma_buf_begin_cpu_access(data->srcp_dma_buf, 0, data->len,
+			DMA_FROM_DEVICE);
+	rot->swts_buffer = dma_buf_kmap(data->srcp_dma_buf, 0);
+	if (IS_ERR_OR_NULL(rot->swts_buffer)) {
+		SDEROT_ERR("ion kernel memory mapping failed\n");
+		rc = IS_ERR(rot->swts_buffer);
+		goto kmap_err;
+	}
+
+	data->mapped = true;
+	SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr,
+			data->len, rot->swts_buffer);
+
+	ion_free(rot->iclient, handle);
+
+	sde_smmu_ctrl(0);
+
+	return rc;
+kmap_err:
+	sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
+			DMA_FROM_DEVICE, data->srcp_dma_buf);
+err_unmap:
+	dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
+			DMA_FROM_DEVICE);
+err_detach:
+	dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+err_put:
+	dma_buf_put(data->srcp_dma_buf);
+	data->srcp_dma_buf = NULL;
+imap_err:
+	ion_free(rot->iclient, handle);
+
+	return rc;
+}
+
+/*
+ * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer
+ * @rot: Pointer to rotator hw
+ */
+static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
+{
+	struct sde_mdp_img_data *data;
+
+	data = &rot->swts_buf;
+
+	dma_buf_end_cpu_access(data->srcp_dma_buf, 0, data->len,
+			DMA_FROM_DEVICE);
+	dma_buf_kunmap(data->srcp_dma_buf, 0, rot->swts_buffer);
+
+	sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
+			DMA_FROM_DEVICE, data->srcp_dma_buf);
+	dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
+			DMA_FROM_DEVICE);
+	dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+	dma_buf_put(data->srcp_dma_buf);
+	data->srcp_dma_buf = NULL;
+}
+
+/*
+ * sde_hw_rotator_destroy - Destroy hw rotator and free allocated resources
+ * @mgr: Pointer to rotator manager
+ */
+static void sde_hw_rotator_destroy(struct sde_rot_mgr *mgr)
+{
+	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	struct sde_hw_rotator *rot;
+
+	if (!mgr || !mgr->pdev || !mgr->hw_data) {
+		SDEROT_ERR("null parameters\n");
+		return;
+	}
+
+	rot = mgr->hw_data;
+	if (rot->irq_num >= 0)
+		devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
+
+	if (rot->mode == ROT_REGDMA_ON)
+		sde_hw_rotator_swtc_destroy(rot);
+
+	devm_kfree(&mgr->pdev->dev, mgr->hw_data);
+	mgr->hw_data = NULL;
+}
+
+/*
+ * sde_hw_rotator_alloc_ext - allocate rotator resource from rotator hw
+ * @mgr: Pointer to rotator manager
+ * @pipe_id: pipe identifier (not used)
+ * @wb_id: writeback identifier/priority queue identifier
+ *
+ * This function allocates a new hw rotator resource for the given priority.
+ */
+static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
+		struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id)
+{
+	struct sde_hw_rotator_resource_info *resinfo;
+
+	if (!mgr || !mgr->hw_data) {
+		SDEROT_ERR("null parameters\n");
+		return NULL;
+	}
+
+	/*
+	 * Allocate rotator resource info. Each allocation is per
+	 * HW priority queue
+	 */
+	resinfo = devm_kzalloc(&mgr->pdev->dev, sizeof(*resinfo), GFP_KERNEL);
+	if (!resinfo) {
+		SDEROT_ERR("Failed allocation HW rotator resource info\n");
+		return NULL;
+	}
+
+	resinfo->rot = mgr->hw_data;
+	resinfo->hw.wb_id = wb_id;
+	atomic_set(&resinfo->hw.num_active, 0);
+	init_waitqueue_head(&resinfo->hw.wait_queue);
+
+	/* For non-regdma, only support one active session */
+	if (resinfo->rot->mode == ROT_REGDMA_OFF)
+		resinfo->hw.max_active = 1;
+	else {
+		resinfo->hw.max_active = SDE_HW_ROT_REGDMA_TOTAL_CTX - 1;
+
+		if (resinfo->rot->iclient == NULL)
+			sde_hw_rotator_swts_create(resinfo->rot);
+	}
+
+	SDEROT_DBG("New rotator resource:%p, priority:%d\n",
+			resinfo, wb_id);
+
+	return &resinfo->hw;
+}
+
+/*
+ * sde_hw_rotator_free_ext - free the given rotator resource
+ * @mgr: Pointer to rotator manager
+ * @hw: Pointer to rotator resource
+ */
+static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
+		struct sde_rot_hw_resource *hw)
+{
+	struct sde_hw_rotator_resource_info *resinfo;
+
+	if (!mgr || !mgr->hw_data)
+		return;
+
+	resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+
+	SDEROT_DBG(
+		"Free rotator resource:%p, priority:%d, active:%d, pending:%d\n",
+		resinfo, hw->wb_id, atomic_read(&hw->num_active),
+		hw->pending_count);
+
+	devm_kfree(&mgr->pdev->dev, resinfo);
+}
+
+/*
+ * sde_hw_rotator_alloc_rotctx - allocate rotator context
+ * @rot: Pointer to rotator hw
+ * @hw: Pointer to rotator resource
+ * @session_id: Session identifier of this context
+ *
+ * This function allocates a new rotator context for the given session id.
+ */
+static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
+		struct sde_hw_rotator *rot,
+		struct sde_rot_hw_resource *hw,
+		u32    session_id)
+{
+	struct sde_hw_rotator_context *ctx;
+
+	/* Allocate rotator context */
+	ctx = devm_kzalloc(&rot->pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		SDEROT_ERR("Failed allocation HW rotator context\n");
+		return NULL;
+	}
+
+	ctx->rot        = rot;
+	ctx->q_id       = hw->wb_id;
+	ctx->session_id = session_id;
+	ctx->hwres      = hw;
+	ctx->timestamp  = atomic_add_return(1, &rot->timestamp[ctx->q_id]);
+	ctx->timestamp &= SDE_REGDMA_SWTS_MASK;
+	ctx->is_secure  = false;
+
+	ctx->regdma_base  = rot->cmd_wr_ptr[ctx->q_id]
+		[sde_hw_rotator_get_regdma_ctxidx(ctx)];
+	ctx->regdma_wrptr = ctx->regdma_base;
+	ctx->ts_addr      = (dma_addr_t)((u32 *)rot->swts_buf.addr +
+		ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
+		sde_hw_rotator_get_regdma_ctxidx(ctx));
+
+	init_completion(&ctx->rot_comp);
+	init_completion(&ctx->regdma_comp);
+
+	/* Store rotator context for lookup purpose */
+	sde_hw_rotator_put_ctx(ctx);
+
+	SDEROT_DBG(
+		"New rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
+		ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
+		ctx->q_id, ctx->timestamp,
+		atomic_read(&ctx->hwres->num_active));
+
+	return ctx;
+}
+
+/*
+ * sde_hw_rotator_free_rotctx - free the given rotator context
+ * @rot: Pointer to rotator hw
+ * @ctx: Pointer to rotator context
+ */
+static void sde_hw_rotator_free_rotctx(struct sde_hw_rotator *rot,
+		struct sde_hw_rotator_context *ctx)
+{
+	if (!rot || !ctx)
+		return;
+
+	SDEROT_DBG(
+		"Free rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
+		ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
+		ctx->q_id, ctx->timestamp,
+		atomic_read(&ctx->hwres->num_active));
+
+	rot->rotCtx[ctx->q_id][sde_hw_rotator_get_regdma_ctxidx(ctx)] = NULL;
+
+	devm_kfree(&rot->pdev->dev, ctx);
+}
+
+/*
+ * sde_hw_rotator_config - configure hw for the given rotation entry
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ *
+ * This function setup the fetch/writeback/rotator blocks, as well as VBIF
+ * based on the given rotation entry.
+ */
+static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
+		struct sde_rot_entry *entry)
+{
+	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	struct sde_hw_rotator *rot;
+	struct sde_hw_rotator_resource_info *resinfo;
+	struct sde_hw_rotator_context *ctx;
+	struct sde_hw_rot_sspp_cfg sspp_cfg;
+	struct sde_hw_rot_wb_cfg wb_cfg;
+	u32 danger_lut = 0;	/* applicable for realtime client only */
+	u32 safe_lut = 0;	/* applicable for realtime client only */
+	u32 flags = 0;
+	struct sde_rotation_item *item;
+
+	if (!hw || !entry) {
+		SDEROT_ERR("null hw resource/entry\n");
+		return -EINVAL;
+	}
+
+	resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+	rot = resinfo->rot;
+	item = &entry->item;
+
+	ctx = sde_hw_rotator_alloc_rotctx(rot, hw, item->session_id);
+	if (!ctx) {
+		SDEROT_ERR("Failed allocating rotator context!!\n");
+		return -EINVAL;
+	}
+
+	flags = (item->flags & SDE_ROTATION_FLIP_LR) ?
+			SDE_ROT_FLAG_FLIP_LR : 0;
+	flags |= (item->flags & SDE_ROTATION_FLIP_UD) ?
+			SDE_ROT_FLAG_FLIP_UD : 0;
+	flags |= (item->flags & SDE_ROTATION_90) ?
+			SDE_ROT_FLAG_ROT_90 : 0;
+	flags |= (item->flags & SDE_ROTATION_DEINTERLACE) ?
+			SDE_ROT_FLAG_DEINTERLACE : 0;
+	flags |= (item->flags & SDE_ROTATION_SECURE) ?
+			SDE_ROT_FLAG_SECURE_OVERLAY_SESSION : 0;
+
+	sspp_cfg.img_width = item->input.width;
+	sspp_cfg.img_height = item->input.height;
+	sspp_cfg.fmt = sde_get_format_params(item->input.format);
+	if (!sspp_cfg.fmt) {
+		SDEROT_ERR("null format\n");
+		return -EINVAL;
+	}
+	sspp_cfg.src_rect = &item->src_rect;
+	sspp_cfg.data = &entry->src_buf;
+	sde_mdp_get_plane_sizes(sspp_cfg.fmt, item->input.width,
+			item->input.height, &sspp_cfg.src_plane,
+			0, /* No bwc_mode */
+			(flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) ?
+					true : false);
+
+	rot->ops.setup_rotator_fetchengine(ctx, ctx->q_id,
+			&sspp_cfg, danger_lut, safe_lut, flags);
+
+	wb_cfg.img_width = item->output.width;
+	wb_cfg.img_height = item->output.height;
+	wb_cfg.fmt = sde_get_format_params(item->output.format);
+	wb_cfg.dst_rect = &item->dst_rect;
+	wb_cfg.data = &entry->dst_buf;
+	sde_mdp_get_plane_sizes(wb_cfg.fmt, item->output.width,
+			item->output.height, &wb_cfg.dst_plane,
+			0, /* No bwc_mode */
+			(flags & SDE_ROT_FLAG_ROT_90) ? true : false);
+
+	wb_cfg.v_downscale_factor = entry->dnsc_factor_h;
+	wb_cfg.h_downscale_factor = entry->dnsc_factor_w;
+
+	rot->ops.setup_rotator_wbengine(ctx, ctx->q_id, &wb_cfg, flags);
+
+	/* setup VA mapping for debugfs */
+	if (rot->dbgmem) {
+		sde_hw_rotator_map_vaddr(&ctx->src_dbgbuf,
+				&item->input,
+				&entry->src_buf);
+
+		sde_hw_rotator_map_vaddr(&ctx->dst_dbgbuf,
+				&item->output,
+				&entry->dst_buf);
+	}
+
+	if (mdata->default_ot_rd_limit) {
+		struct sde_mdp_set_ot_params ot_params;
+
+		memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
+		ot_params.xin_id = XIN_SSPP;
+		ot_params.num = 0; /* not used */
+		ot_params.width = sspp_cfg.img_width;
+		ot_params.height = sspp_cfg.img_height;
+		ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_RD_LIM_CONF;
+		ot_params.reg_off_mdp_clk_ctrl =
+				MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+		ot_params.bit_off_mdp_clk_ctrl =
+				MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN0;
+		ot_params.is_rot = true;
+		ot_params.is_wb = true;
+		ot_params.is_yuv = sde_mdp_is_yuv_format(sspp_cfg.fmt);
+		sde_mdp_set_ot_limit(&ot_params);
+	}
+
+	if (mdata->default_ot_wr_limit) {
+		struct sde_mdp_set_ot_params ot_params;
+
+		memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
+		ot_params.xin_id = XIN_WRITEBACK;
+		ot_params.num = 0; /* not used */
+		ot_params.width = wb_cfg.img_width;
+		ot_params.height = wb_cfg.img_height;
+		ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_WR_LIM_CONF;
+		ot_params.reg_off_mdp_clk_ctrl =
+				MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+		ot_params.bit_off_mdp_clk_ctrl =
+				MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1;
+		ot_params.is_rot = true;
+		ot_params.is_wb = true;
+		ot_params.is_yuv = sde_mdp_is_yuv_format(wb_cfg.fmt);
+		sde_mdp_set_ot_limit(&ot_params);
+	}
+
+	if (test_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map))	{
+		u32 qos_lut = 0; /* low priority for nrt read client */
+
+		trace_rot_perf_set_qos_luts(XIN_SSPP, sspp_cfg.fmt->format,
+			qos_lut, sde_mdp_is_linear_format(sspp_cfg.fmt));
+
+		SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CREQ_LUT, qos_lut);
+	}
+
+	if (mdata->npriority_lvl > 0) {
+		u32 mask, reg_val, i, vbif_qos;
+
+		for (i = 0; i < mdata->npriority_lvl; i++) {
+			reg_val = SDE_VBIF_READ(mdata,
+					MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4);
+			mask = 0x3 << (XIN_SSPP * 2);
+			reg_val &= ~(mask);
+			vbif_qos = mdata->vbif_nrt_qos[i];
+			reg_val |= vbif_qos << (XIN_SSPP * 2);
+			/* ensure write is issued after the read operation */
+			mb();
+			SDE_VBIF_WRITE(mdata,
+					MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4,
+					reg_val);
+		}
+	}
+
+	/* Enable write gather for writeback to remove write gaps, which
+	 * may hang AXI/BIMC/SDE.
+	 */
+	SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
+			BIT(XIN_WRITEBACK));
+
+	return 0;
+}
+
+/*
+ * sde_hw_rotator_kickoff - kickoff processing on the given entry
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ */
+static int sde_hw_rotator_kickoff(struct sde_rot_hw_resource *hw,
+		struct sde_rot_entry *entry)
+{
+	struct sde_hw_rotator *rot;
+	struct sde_hw_rotator_resource_info *resinfo;
+	struct sde_hw_rotator_context *ctx;
+	int ret = 0;
+
+	if (!hw || !entry) {
+		SDEROT_ERR("null hw resource/entry\n");
+		return -EINVAL;
+	}
+
+	resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+	rot = resinfo->rot;
+
+	/* Lookup rotator context from session-id */
+	ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
+	if (!ctx) {
+		SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+				entry->item.session_id);
+	}
+	WARN_ON(ctx == NULL);
+
+	ret = sde_smmu_ctrl(1);
+	if (IS_ERR_VALUE(ret)) {
+		SDEROT_ERR("IOMMU attach failed\n");
+		return ret;
+	}
+
+	rot->ops.start_rotator(ctx, ctx->q_id);
+
+	return 0;
+}
+
+/*
+ * sde_hw_rotator_wait4done - wait for completion notification
+ * @hw: Pointer to rotator resource
+ * @entry: Pointer to rotation entry
+ *
+ * This function blocks until the given entry is complete, error
+ * is detected, or timeout.
+ */
+static int sde_hw_rotator_wait4done(struct sde_rot_hw_resource *hw,
+		struct sde_rot_entry *entry)
+{
+	struct sde_hw_rotator *rot;
+	struct sde_hw_rotator_resource_info *resinfo;
+	struct sde_hw_rotator_context *ctx;
+	int ret;
+
+	if (!hw || !entry) {
+		SDEROT_ERR("null hw resource/entry\n");
+		return -EINVAL;
+	}
+
+	resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+	rot = resinfo->rot;
+
+	/* Lookup rotator context from session-id */
+	ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
+	if (!ctx) {
+		SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+				entry->item.session_id);
+	}
+	WARN_ON(ctx == NULL);
+
+	ret = rot->ops.wait_rotator_done(ctx, ctx->q_id, 0);
+
+	sde_smmu_ctrl(0);
+
+	if (rot->dbgmem) {
+		sde_hw_rotator_unmap_vaddr(&ctx->src_dbgbuf);
+		sde_hw_rotator_unmap_vaddr(&ctx->dst_dbgbuf);
+	}
+
+	/* Current rotator context job is finished, time to free up*/
+	sde_hw_rotator_free_rotctx(rot, ctx);
+
+	return ret;
+}
+
+/*
+ * sde_rotator_hw_rev_init - setup feature and/or capability bitmask
+ * @rot: Pointer to hw rotator
+ *
+ * This function initializes feature and/or capability bitmask based on
+ * h/w version read from the device.
+ */
+static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
+{
+	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	u32 hw_version;
+
+	if (!mdata) {
+		SDEROT_ERR("null rotator data\n");
+		return -EINVAL;
+	}
+
+	hw_version = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_HW_VERSION);
+	SDEROT_DBG("hw version %8.8x\n", hw_version);
+
+	clear_bit(SDE_QOS_PER_PIPE_IB, mdata->sde_qos_map);
+	set_bit(SDE_QOS_OVERHEAD_FACTOR, mdata->sde_qos_map);
+	clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
+	set_bit(SDE_QOS_OTLIM, mdata->sde_qos_map);
+	set_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map);
+	clear_bit(SDE_QOS_SIMPLIFIED_PREFILL, mdata->sde_qos_map);
+
+	set_bit(SDE_CAPS_R3_WB, mdata->sde_caps_map);
+
+	return 0;
+}
+
+/*
+ * sde_hw_rotator_rotirq_handler - non-regdma interrupt handler
+ * @irq: Interrupt number
+ * @ptr: Pointer to private handle provided during registration
+ *
+ * This function services rotator interrupt and wakes up waiting client
+ * with pending rotation requests already submitted to h/w.
+ */
+static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
+{
+	struct sde_hw_rotator *rot = ptr;
+	struct sde_hw_rotator_context *ctx;
+	irqreturn_t ret = IRQ_NONE;
+	u32 isr;
+
+	isr = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_INTR_STATUS);
+
+	SDEROT_DBG("intr_status = %8.8x\n", isr);
+
+	if (isr & ROT_DONE_MASK) {
+		if (rot->irq_num >= 0)
+			disable_irq_nosync(rot->irq_num);
+		SDEROT_DBG("Notify rotator complete\n");
+
+		/* Normal rotator only 1 session, no need to lookup */
+		ctx = rot->rotCtx[0][0];
+		WARN_ON(ctx == NULL);
+		complete_all(&ctx->rot_comp);
+
+		spin_lock(&rot->rotisr_lock);
+		SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+				ROT_DONE_CLEAR);
+		spin_unlock(&rot->rotisr_lock);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+/*
+ * sde_hw_rotator_regdmairq_handler - regdma interrupt handler
+ * @irq: Interrupt number
+ * @ptr: Pointer to private handle provided during registration
+ *
+ * This function services rotator interrupt, decoding the source of
+ * events (high/low priority queue), and wakes up all waiting clients
+ * with pending rotation requests already submitted to h/w.
+ */
+static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
+{
+	struct sde_hw_rotator *rot = ptr;
+	struct sde_hw_rotator_context *ctx;
+	irqreturn_t ret = IRQ_NONE;
+	u32 isr;
+	u32 ts;
+	u32 q_id;
+
+	isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
+	ts  = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+
+	SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
+
+	/* Any REGDMA status, including error and watchdog timer, should
+	 * trigger and wake up waiting thread
+	 */
+	if (isr & (REGDMA_INT_HIGH_MASK | REGDMA_INT_LOW_MASK)) {
+		spin_lock(&rot->rotisr_lock);
+
+		/*
+		 * Obtain rotator context based on timestamp from regdma
+		 * and low/high interrupt status
+		 */
+		if (isr & REGDMA_INT_HIGH_MASK) {
+			q_id = ROT_QUEUE_HIGH_PRIORITY;
+			ts   = ts & SDE_REGDMA_SWTS_MASK;
+		} else if (isr & REGDMA_INT_LOW_MASK) {
+			q_id = ROT_QUEUE_LOW_PRIORITY;
+			ts   = (ts >> SDE_REGDMA_SWTS_SHIFT) &
+				SDE_REGDMA_SWTS_MASK;
+		}
+
+		ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
+		WARN_ON(ctx == NULL);
+
+		/*
+		 * Wake up all waiting context from the current and previous
+		 * SW Timestamp.
+		 */
+		do {
+			ctx->last_regdma_isr_status = isr;
+			ctx->last_regdma_timestamp  = ts;
+			SDEROT_DBG(
+				"regdma complete: ctx:%p, ts:%X, dcount:%X\n",
+				ctx, ts, atomic_read(&rot->regdma_done_count));
+			complete_all(&ctx->regdma_comp);
+
+			ts  = (ts - 1) & SDE_REGDMA_SWTS_MASK;
+			ctx = rot->rotCtx[q_id]
+				[ts & SDE_HW_ROT_REGDMA_SEG_MASK];
+		} while (ctx && (ctx->last_regdma_timestamp == 0));
+
+		/*
+		 * Clear corresponding regdma interrupt because it is a level
+		 * interrupt
+		 */
+		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
+				isr);
+
+		spin_unlock(&rot->rotisr_lock);
+		ret = IRQ_HANDLED;
+	} else if (isr & REGDMA_INT_ERR_MASK) {
+		/*
+		 * For REGDMA Err, we save the isr info and wake up
+		 * all waiting contexts
+		 */
+		int i, j;
+
+		SDEROT_ERR(
+			"regdma err isr:%X, wake up all waiting contexts\n",
+			isr);
+
+		spin_lock(&rot->rotisr_lock);
+
+		for (i = 0; i < ROT_QUEUE_MAX; i++) {
+			for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+				ctx = rot->rotCtx[i][j];
+				if (ctx && ctx->last_regdma_isr_status == 0) {
+					ctx->last_regdma_isr_status = isr;
+					ctx->last_regdma_timestamp  = ts;
+					complete_all(&ctx->regdma_comp);
+					SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
+							i, j, ctx);
+				}
+			}
+		}
+
+		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
+				isr);
+
+		spin_unlock(&rot->rotisr_lock);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+/*
+ * sde_hw_rotator_validate_entry - validate rotation entry
+ * @mgr: Pointer to rotator manager
+ * @entry: Pointer to rotation entry
+ *
+ * This function validates the given rotation entry and provides possible
+ * fixup (future improvement) if available.  This function returns 0 if
+ * the entry is valid, and returns error code otherwise.
+ */
+static int sde_hw_rotator_validate_entry(struct sde_rot_mgr *mgr,
+		struct sde_rot_entry *entry)
+{
+	int ret = 0;
+	u16 src_w, src_h, dst_w, dst_h;
+	struct sde_rotation_item *item = &entry->item;
+	struct sde_mdp_format_params *fmt;
+
+	src_w = item->src_rect.w;
+	src_h = item->src_rect.h;
+
+	if (item->flags & SDE_ROTATION_90) {
+		dst_w = item->dst_rect.h;
+		dst_h = item->dst_rect.w;
+	} else {
+		dst_w = item->dst_rect.w;
+		dst_h = item->dst_rect.h;
+	}
+
+	entry->dnsc_factor_w = 0;
+	entry->dnsc_factor_h = 0;
+
+	if ((src_w != dst_w) || (src_h != dst_h)) {
+		if ((src_w % dst_w) || (src_h % dst_h)) {
+			SDEROT_DBG("non integral scale not support\n");
+			ret = -EINVAL;
+			goto dnsc_err;
+		}
+		entry->dnsc_factor_w = src_w / dst_w;
+		if ((entry->dnsc_factor_w & (entry->dnsc_factor_w - 1)) ||
+				(entry->dnsc_factor_w > 64)) {
+			SDEROT_DBG("non power-of-2 w_scale not support\n");
+			ret = -EINVAL;
+			goto dnsc_err;
+		}
+		entry->dnsc_factor_h = src_h / dst_h;
+		if ((entry->dnsc_factor_h & (entry->dnsc_factor_h - 1)) ||
+				(entry->dnsc_factor_h > 64)) {
+			SDEROT_DBG("non power-of-2 h_scale not support\n");
+			ret = -EINVAL;
+			goto dnsc_err;
+		}
+	}
+
+	fmt = sde_get_format_params(item->output.format);
+	/* Tiled format downscale support not applied to AYUV tiled */
+	if (sde_mdp_is_tilea5x_format(fmt) && (entry->dnsc_factor_h > 4)) {
+		SDEROT_DBG("max downscale for tiled format is 4\n");
+		ret = -EINVAL;
+		goto dnsc_err;
+	}
+	if (sde_mdp_is_ubwc_format(fmt)	&& (entry->dnsc_factor_h > 2)) {
+		SDEROT_DBG("downscale with ubwc cannot be more than 2\n");
+		ret = -EINVAL;
+	}
+
+dnsc_err:
+	/* Downscaler does not support asymmetrical dnsc */
+	if (entry->dnsc_factor_w != entry->dnsc_factor_h) {
+		SDEROT_DBG("asymmetric downscale not support\n");
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		entry->dnsc_factor_w = 0;
+		entry->dnsc_factor_h = 0;
+	}
+	return ret;
+}
+
+/*
+ * sde_hw_rotator_show_caps - output capability info to sysfs 'caps' file
+ * @mgr: Pointer to rotator manager
+ * @attr: Pointer to device attribute interface
+ * @buf: Pointer to output buffer
+ * @len: Length of output buffer
+ */
+static ssize_t sde_hw_rotator_show_caps(struct sde_rot_mgr *mgr,
+		struct device_attribute *attr, char *buf, ssize_t len)
+{
+	struct sde_hw_rotator *hw_data;
+	int cnt = 0;
+
+	if (!mgr || !buf)
+		return 0;
+
+	hw_data = mgr->hw_data;
+
+#define SPRINT(fmt, ...) \
+		(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
+
+	/* insert capabilities here */
+
+#undef SPRINT
+	return cnt;
+}
+
+/*
+ * sde_hw_rotator_show_state - output state info to sysfs 'state' file
+ * @mgr: Pointer to rotator manager
+ * @attr: Pointer to device attribute interface
+ * @buf: Pointer to output buffer
+ * @len: Length of output buffer
+ */
+static ssize_t sde_hw_rotator_show_state(struct sde_rot_mgr *mgr,
+		struct device_attribute *attr, char *buf, ssize_t len)
+{
+	struct sde_hw_rotator *rot;
+	struct sde_hw_rotator_context *ctx;
+	int cnt = 0;
+	int num_active = 0;
+	int i, j;
+
+	if (!mgr || !buf) {
+		SDEROT_ERR("null parameters\n");
+		return 0;
+	}
+
+	rot = mgr->hw_data;
+
+#define SPRINT(fmt, ...) \
+		(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
+
+	if (rot) {
+		SPRINT("rot_mode=%d\n", rot->mode);
+		SPRINT("irq_num=%d\n", rot->irq_num);
+
+		if (rot->mode == ROT_REGDMA_OFF) {
+			SPRINT("max_active=1\n");
+			SPRINT("num_active=%d\n", rot->rotCtx[0][0] ? 1 : 0);
+		} else {
+			for (i = 0; i < ROT_QUEUE_MAX; i++) {
+				for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX;
+						j++) {
+					ctx = rot->rotCtx[i][j];
+
+					if (ctx) {
+						SPRINT(
+							"rotCtx[%d][%d]:%p\n",
+							i, j, ctx);
+						++num_active;
+					}
+				}
+			}
+
+			SPRINT("max_active=%d\n", SDE_HW_ROT_REGDMA_TOTAL_CTX);
+			SPRINT("num_active=%d\n", num_active);
+		}
+	}
+
+#undef SPRINT
+	return cnt;
+}
+
+/*
+ * sde_hw_rotator_parse_dt - parse r3 specific device tree settings
+ * @hw_data: Pointer to rotator hw
+ * @dev: Pointer to platform device
+ */
+static int sde_hw_rotator_parse_dt(struct sde_hw_rotator *hw_data,
+		struct platform_device *dev)
+{
+	int ret = 0;
+	u32 data;
+
+	if (!hw_data || !dev)
+		return -EINVAL;
+
+	ret = of_property_read_u32(dev->dev.of_node, "qcom,mdss-rot-mode",
+			&data);
+	if (ret) {
+		SDEROT_DBG("default to regdma off\n");
+		ret = 0;
+		hw_data->mode = ROT_REGDMA_OFF;
+	} else if (data < ROT_REGDMA_MAX) {
+		SDEROT_DBG("set to regdma mode %d\n", data);
+		hw_data->mode = data;
+	} else {
+		SDEROT_ERR("regdma mode out of range. default to regdma off\n");
+		hw_data->mode = ROT_REGDMA_OFF;
+	}
+
+	ret = of_property_read_u32(dev->dev.of_node,
+			"qcom,mdss-highest-bank-bit", &data);
+	if (ret) {
+		SDEROT_DBG("default to A5X bank\n");
+		ret = 0;
+		hw_data->highest_bank = 2;
+	} else {
+		SDEROT_DBG("set highest bank bit to %d\n", data);
+		hw_data->highest_bank = data;
+	}
+
+	return ret;
+}
+
+/*
+ * sde_rotator_r3_init - initialize the r3 module
+ * @mgr: Pointer to rotator manager
+ *
+ * This function setup r3 callback functions, parses r3 specific
+ * device tree settings, installs r3 specific interrupt handler,
+ * as well as initializes r3 internal data structure.
+ */
+int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
+{
+	struct sde_hw_rotator *rot;
+	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	int i;
+	int ret;
+
+	rot = devm_kzalloc(&mgr->pdev->dev, sizeof(*rot), GFP_KERNEL);
+	if (!rot)
+		return -ENOMEM;
+
+	mgr->hw_data = rot;
+	mgr->queue_count = ROT_QUEUE_MAX;
+
+	rot->mdss_base = mdata->sde_io.base;
+	rot->pdev      = mgr->pdev;
+
+	/* Assign ops */
+	mgr->ops_hw_destroy = sde_hw_rotator_destroy;
+	mgr->ops_hw_alloc = sde_hw_rotator_alloc_ext;
+	mgr->ops_hw_free = sde_hw_rotator_free_ext;
+	mgr->ops_config_hw = sde_hw_rotator_config;
+	mgr->ops_kickoff_entry = sde_hw_rotator_kickoff;
+	mgr->ops_wait_for_entry = sde_hw_rotator_wait4done;
+	mgr->ops_hw_validate_entry = sde_hw_rotator_validate_entry;
+	mgr->ops_hw_show_caps = sde_hw_rotator_show_caps;
+	mgr->ops_hw_show_state = sde_hw_rotator_show_state;
+	mgr->ops_hw_create_debugfs = sde_rotator_r3_create_debugfs;
+
+	ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
+	if (ret)
+		goto error_parse_dt;
+
+	rot->irq_num = platform_get_irq(mgr->pdev, 0);
+	if (rot->irq_num < 0) {
+		SDEROT_ERR("fail to get rotator irq\n");
+	} else {
+		if (rot->mode == ROT_REGDMA_OFF)
+			ret = devm_request_threaded_irq(&mgr->pdev->dev,
+					rot->irq_num,
+					sde_hw_rotator_rotirq_handler,
+					NULL, 0, "sde_rotator_r3", rot);
+		else
+			ret = devm_request_threaded_irq(&mgr->pdev->dev,
+					rot->irq_num,
+					sde_hw_rotator_regdmairq_handler,
+					NULL, 0, "sde_rotator_r3", rot);
+		if (ret) {
+			SDEROT_ERR("fail to request irq r:%d\n", ret);
+			rot->irq_num = -1;
+		} else {
+			disable_irq(rot->irq_num);
+		}
+	}
+
+	setup_rotator_ops(&rot->ops, rot->mode);
+
+	spin_lock_init(&rot->rotctx_lock);
+	spin_lock_init(&rot->rotisr_lock);
+
+	/* REGDMA initialization */
+	if (rot->mode == ROT_REGDMA_OFF) {
+		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+			rot->cmd_wr_ptr[0][i] = &rot->cmd_queue[
+				SDE_HW_ROT_REGDMA_SEG_SIZE * i];
+	} else {
+		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+			rot->cmd_wr_ptr[ROT_QUEUE_HIGH_PRIORITY][i] =
+				(u32 *)(rot->mdss_base +
+					REGDMA_RAM_REGDMA_CMD_RAM +
+					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i);
+
+		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
+			rot->cmd_wr_ptr[ROT_QUEUE_LOW_PRIORITY][i] =
+				(u32 *)(rot->mdss_base +
+					REGDMA_RAM_REGDMA_CMD_RAM +
+					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 *
+					(i + SDE_HW_ROT_REGDMA_TOTAL_CTX));
+	}
+
+	atomic_set(&rot->timestamp[0], 0);
+	atomic_set(&rot->timestamp[1], 0);
+	atomic_set(&rot->regdma_submit_count, 0);
+	atomic_set(&rot->regdma_done_count, 0);
+
+	ret = sde_rotator_hw_rev_init(rot);
+	if (ret)
+		goto error_hw_rev_init;
+
+	return 0;
+error_hw_rev_init:
+	if (rot->irq_num >= 0)
+		devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
+	devm_kfree(&mgr->pdev->dev, mgr->hw_data);
+error_parse_dt:
+	return ret;
+}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h
new file mode 100644
index 0000000..e6158a9
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2015-2016, 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 __SDE_ROTATOR_R3_H__
+#define __SDE_ROTATOR_R3_H__
+
+#include "sde_rotator_core.h"
+
+int sde_rotator_r3_init(struct sde_rot_mgr *mgr);
+
+#endif /* __SDE_ROTATOR_R3_H__ */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c
new file mode 100644
index 0000000..987e61c
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2015-2016, 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)	"%s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+
+#include "sde_rotator_r3_debug.h"
+#include "sde_rotator_core.h"
+#include "sde_rotator_r3.h"
+#include "sde_rotator_r3_internal.h"
+
+/*
+ * sde_rotator_r3_create_debugfs - Setup rotator r3 debugfs directory structure.
+ * @rot_dev: Pointer to rotator device
+ */
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+		struct dentry *debugfs_root)
+{
+	struct sde_hw_rotator *hw_data;
+
+	if (!mgr || !debugfs_root || !mgr->hw_data)
+		return -EINVAL;
+
+	hw_data = mgr->hw_data;
+
+	if (!debugfs_create_bool("dbgmem", 0644,
+			debugfs_root, &hw_data->dbgmem)) {
+		SDEROT_ERR("fail create dbgmem\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h
new file mode 100644
index 0000000..25cd637
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_debug.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2015-2016, 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 __SDE_ROTATOR_R3_DEBUG_H__
+#define __SDE_ROTATOR_R3_DEBUG_H__
+
+#include <linux/types.h>
+#include <linux/dcache.h>
+
+struct sde_rot_mgr;
+
+#if defined(CONFIG_DEBUG_FS)
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+		struct dentry *debugfs_root);
+#else
+static inline
+int sde_rotator_r3_create_debugfs(struct sde_rot_mgr *mgr,
+		struct dentry *debugfs_root)
+{
+	return 0;
+}
+#endif
+#endif /* __SDE_ROTATOR_R3_DEBUG_H__ */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
new file mode 100644
index 0000000..d0c15f4
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -0,0 +1,274 @@
+/* Copyright (c) 2015-2016, 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 _SDE_ROTATOR_R3_HWIO_H
+#define _SDE_ROTATOR_R3_HWIO_H
+
+#include <linux/bitops.h>
+
+/* MMSS_MDSS:
+ * OFFSET=0x000000
+ */
+#define MMSS_MDSS_HW_INTR_STATUS		0x10
+#define MMSS_MDSS_HW_INTR_STATUS_ROT		BIT(2)
+
+/* SDE_ROT_ROTTOP:
+ * OFFSET=0x0A8800
+ */
+#define SDE_ROT_ROTTOP_OFFSET                   0xA8800
+#define ROTTOP_HW_VERSION                       (SDE_ROT_ROTTOP_OFFSET+0x00)
+#define ROTTOP_CLK_CTRL                         (SDE_ROT_ROTTOP_OFFSET+0x10)
+#define ROTTOP_CLK_STATUS                       (SDE_ROT_ROTTOP_OFFSET+0x14)
+#define ROTTOP_ROT_NEWROI_PRIOR_TO_START        (SDE_ROT_ROTTOP_OFFSET+0x18)
+#define ROTTOP_SW_RESET                         (SDE_ROT_ROTTOP_OFFSET+0x20)
+#define ROTTOP_SW_RESET_CTRL                    (SDE_ROT_ROTTOP_OFFSET+0x24)
+#define ROTTOP_SW_RESET_OVERRIDE                (SDE_ROT_ROTTOP_OFFSET+0x28)
+#define ROTTOP_INTR_EN                          (SDE_ROT_ROTTOP_OFFSET+0x30)
+#define ROTTOP_INTR_STATUS                      (SDE_ROT_ROTTOP_OFFSET+0x34)
+#define ROTTOP_INTR_CLEAR                       (SDE_ROT_ROTTOP_OFFSET+0x38)
+#define ROTTOP_START_CTRL                       (SDE_ROT_ROTTOP_OFFSET+0x40)
+#define ROTTOP_STATUS                           (SDE_ROT_ROTTOP_OFFSET+0x44)
+#define ROTTOP_OP_MODE                          (SDE_ROT_ROTTOP_OFFSET+0x48)
+#define ROTTOP_DNSC                             (SDE_ROT_ROTTOP_OFFSET+0x4C)
+#define ROTTOP_DEBUGBUS_CTRL                    (SDE_ROT_ROTTOP_OFFSET+0x50)
+#define ROTTOP_DEBUGBUS_STATUS                  (SDE_ROT_ROTTOP_OFFSET+0x54)
+#define ROTTOP_ROT_UBWC_DEC_VERSION             (SDE_ROT_ROTTOP_OFFSET+0x58)
+#define ROTTOP_ROT_UBWC_ENC_VERSION             (SDE_ROT_ROTTOP_OFFSET+0x5C)
+
+/* SDE_ROT_SSPP:
+ * OFFSET=0x0A8900
+ */
+#define SDE_ROT_SSPP_OFFSET                     0xA8900
+#define ROT_SSPP_SRC_SIZE                       (SDE_ROT_SSPP_OFFSET+0x00)
+#define ROT_SSPP_SRC_IMG_SIZE                   (SDE_ROT_SSPP_OFFSET+0x04)
+#define ROT_SSPP_SRC_XY                         (SDE_ROT_SSPP_OFFSET+0x08)
+#define ROT_SSPP_OUT_SIZE                       (SDE_ROT_SSPP_OFFSET+0x0C)
+#define ROT_SSPP_OUT_XY                         (SDE_ROT_SSPP_OFFSET+0x10)
+#define ROT_SSPP_SRC0_ADDR                      (SDE_ROT_SSPP_OFFSET+0x14)
+#define ROT_SSPP_SRC1_ADDR                      (SDE_ROT_SSPP_OFFSET+0x18)
+#define ROT_SSPP_SRC2_ADDR                      (SDE_ROT_SSPP_OFFSET+0x1C)
+#define ROT_SSPP_SRC3_ADDR                      (SDE_ROT_SSPP_OFFSET+0x20)
+#define ROT_SSPP_SRC_YSTRIDE0                   (SDE_ROT_SSPP_OFFSET+0x24)
+#define ROT_SSPP_SRC_YSTRIDE1                   (SDE_ROT_SSPP_OFFSET+0x28)
+#define ROT_SSPP_TILE_FRAME_SIZE                (SDE_ROT_SSPP_OFFSET+0x2C)
+#define ROT_SSPP_SRC_FORMAT                     (SDE_ROT_SSPP_OFFSET+0x30)
+#define ROT_SSPP_SRC_UNPACK_PATTERN             (SDE_ROT_SSPP_OFFSET+0x34)
+#define ROT_SSPP_SRC_OP_MODE                    (SDE_ROT_SSPP_OFFSET+0x38)
+#define ROT_SSPP_SRC_CONSTANT_COLOR             (SDE_ROT_SSPP_OFFSET+0x3C)
+#define ROT_SSPP_FETCH_CONFIG                   (SDE_ROT_SSPP_OFFSET+0x48)
+#define ROT_SSPP_VC1_RANGE                      (SDE_ROT_SSPP_OFFSET+0x4C)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_0   (SDE_ROT_SSPP_OFFSET+0x50)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_1   (SDE_ROT_SSPP_OFFSET+0x54)
+#define ROT_SSPP_REQPRIORITY_FIFO_WATERMARK_2   (SDE_ROT_SSPP_OFFSET+0x58)
+#define ROT_SSPP_DANGER_LUT                     (SDE_ROT_SSPP_OFFSET+0x60)
+#define ROT_SSPP_SAFE_LUT                       (SDE_ROT_SSPP_OFFSET+0x64)
+#define ROT_SSPP_CREQ_LUT                       (SDE_ROT_SSPP_OFFSET+0x68)
+#define ROT_SSPP_QOS_CTRL                       (SDE_ROT_SSPP_OFFSET+0x6C)
+#define ROT_SSPP_SRC_ADDR_SW_STATUS             (SDE_ROT_SSPP_OFFSET+0x70)
+#define ROT_SSPP_CURRENT_SRC0_ADDR              (SDE_ROT_SSPP_OFFSET+0xA4)
+#define ROT_SSPP_CURRENT_SRC1_ADDR              (SDE_ROT_SSPP_OFFSET+0xA8)
+#define ROT_SSPP_CURRENT_SRC2_ADDR              (SDE_ROT_SSPP_OFFSET+0xAC)
+#define ROT_SSPP_CURRENT_SRC3_ADDR              (SDE_ROT_SSPP_OFFSET+0xB0)
+#define ROT_SSPP_DECIMATION_CONFIG              (SDE_ROT_SSPP_OFFSET+0xB4)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE0            (SDE_ROT_SSPP_OFFSET+0xD0)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE1            (SDE_ROT_SSPP_OFFSET+0xD4)
+#define ROT_SSPP_FETCH_SMP_WR_PLANE2            (SDE_ROT_SSPP_OFFSET+0xD8)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE0           (SDE_ROT_SSPP_OFFSET+0xE0)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE1           (SDE_ROT_SSPP_OFFSET+0xE4)
+#define ROT_SSPP_SMP_UNPACK_RD_PLANE2           (SDE_ROT_SSPP_OFFSET+0xE8)
+#define ROT_SSPP_FILL_LEVELS                    (SDE_ROT_SSPP_OFFSET+0xF0)
+#define ROT_SSPP_STATUS                         (SDE_ROT_SSPP_OFFSET+0xF4)
+#define ROT_SSPP_UNPACK_LINE_COUNT              (SDE_ROT_SSPP_OFFSET+0xF8)
+#define ROT_SSPP_UNPACK_BLK_COUNT               (SDE_ROT_SSPP_OFFSET+0xFC)
+#define ROT_SSPP_SW_PIX_EXT_C0_LR               (SDE_ROT_SSPP_OFFSET+0x100)
+#define ROT_SSPP_SW_PIX_EXT_C0_TB               (SDE_ROT_SSPP_OFFSET+0x104)
+#define ROT_SSPP_SW_PIX_EXT_C0_REQ_PIXELS       (SDE_ROT_SSPP_OFFSET+0x108)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_LR             (SDE_ROT_SSPP_OFFSET+0x110)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_TB             (SDE_ROT_SSPP_OFFSET+0x114)
+#define ROT_SSPP_SW_PIX_EXT_C1C2_REQ_PIXELS     (SDE_ROT_SSPP_OFFSET+0x118)
+#define ROT_SSPP_SW_PIX_EXT_C3_LR               (SDE_ROT_SSPP_OFFSET+0x120)
+#define ROT_SSPP_SW_PIX_EXT_C3_TB               (SDE_ROT_SSPP_OFFSET+0x124)
+#define ROT_SSPP_SW_PIX_EXT_C3_REQ_PIXELS       (SDE_ROT_SSPP_OFFSET+0x128)
+#define ROT_SSPP_TRAFFIC_SHAPER                 (SDE_ROT_SSPP_OFFSET+0x130)
+#define ROT_SSPP_CDP_CNTL                       (SDE_ROT_SSPP_OFFSET+0x134)
+#define ROT_SSPP_UBWC_ERROR_STATUS              (SDE_ROT_SSPP_OFFSET+0x138)
+#define ROT_SSPP_SW_CROP_W_C0C3                 (SDE_ROT_SSPP_OFFSET+0x140)
+#define ROT_SSPP_SW_CROP_W_C1C2                 (SDE_ROT_SSPP_OFFSET+0x144)
+#define ROT_SSPP_SW_CROP_H_C0C3                 (SDE_ROT_SSPP_OFFSET+0x148)
+#define ROT_SSPP_SW_CROP_H_C1C2                 (SDE_ROT_SSPP_OFFSET+0x14C)
+#define ROT_SSPP_TRAFFIC_SHAPER_PREFILL         (SDE_ROT_SSPP_OFFSET+0x150)
+#define ROT_SSPP_TRAFFIC_SHAPER_REC1_PREFILL    (SDE_ROT_SSPP_OFFSET+0x154)
+#define ROT_SSPP_OUT_SIZE_REC1                  (SDE_ROT_SSPP_OFFSET+0x160)
+#define ROT_SSPP_OUT_XY_REC1                    (SDE_ROT_SSPP_OFFSET+0x164)
+#define ROT_SSPP_SRC_XY_REC1                    (SDE_ROT_SSPP_OFFSET+0x168)
+#define ROT_SSPP_SRC_SIZE_REC1                  (SDE_ROT_SSPP_OFFSET+0x16C)
+#define ROT_SSPP_MULTI_REC_OP_MODE              (SDE_ROT_SSPP_OFFSET+0x170)
+#define ROT_SSPP_SRC_FORMAT_REC1                (SDE_ROT_SSPP_OFFSET+0x174)
+#define ROT_SSPP_SRC_UNPACK_PATTERN_REC1        (SDE_ROT_SSPP_OFFSET+0x178)
+#define ROT_SSPP_SRC_OP_MODE_REC1               (SDE_ROT_SSPP_OFFSET+0x17C)
+#define ROT_SSPP_SRC_CONSTANT_COLOR_REC1        (SDE_ROT_SSPP_OFFSET+0x180)
+#define ROT_SSPP_TPG_CONTROL                    (SDE_ROT_SSPP_OFFSET+0x190)
+#define ROT_SSPP_TPG_CONFIG                     (SDE_ROT_SSPP_OFFSET+0x194)
+#define ROT_SSPP_TPG_COMPONENT_LIMITS           (SDE_ROT_SSPP_OFFSET+0x198)
+#define ROT_SSPP_TPG_RECTANGLE                  (SDE_ROT_SSPP_OFFSET+0x19C)
+#define ROT_SSPP_TPG_BLACK_WHITE_PATTERN_FRAMES (SDE_ROT_SSPP_OFFSET+0x1A0)
+#define ROT_SSPP_TPG_RGB_MAPPING                (SDE_ROT_SSPP_OFFSET+0x1A4)
+#define ROT_SSPP_TPG_PATTERN_GEN_INIT_VAL       (SDE_ROT_SSPP_OFFSET+0x1A8)
+
+#define SDE_ROT_SSPP_FETCH_CONFIG_RESET_VALUE   0x00087
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_128        (0 << 16)
+#define SDE_ROT_SSPP_FETCH_BLOCKSIZE_96         (2 << 16)
+
+
+/* SDE_ROT_WB:
+ * OFFSET=0x0A8B00
+ */
+#define SDE_ROT_WB_OFFSET                       0xA8B00
+#define ROT_WB_DST_FORMAT                       (SDE_ROT_WB_OFFSET+0x000)
+#define ROT_WB_DST_OP_MODE                      (SDE_ROT_WB_OFFSET+0x004)
+#define ROT_WB_DST_PACK_PATTERN                 (SDE_ROT_WB_OFFSET+0x008)
+#define ROT_WB_DST0_ADDR                        (SDE_ROT_WB_OFFSET+0x00C)
+#define ROT_WB_DST1_ADDR                        (SDE_ROT_WB_OFFSET+0x010)
+#define ROT_WB_DST2_ADDR                        (SDE_ROT_WB_OFFSET+0x014)
+#define ROT_WB_DST3_ADDR                        (SDE_ROT_WB_OFFSET+0x018)
+#define ROT_WB_DST_YSTRIDE0                     (SDE_ROT_WB_OFFSET+0x01C)
+#define ROT_WB_DST_YSTRIDE1                     (SDE_ROT_WB_OFFSET+0x020)
+#define ROT_WB_DST_DITHER_BITDEPTH              (SDE_ROT_WB_OFFSET+0x024)
+#define ROT_WB_DITHER_MATRIX_ROW0               (SDE_ROT_WB_OFFSET+0x030)
+#define ROT_WB_DITHER_MATRIX_ROW1               (SDE_ROT_WB_OFFSET+0x034)
+#define ROT_WB_DITHER_MATRIX_ROW2               (SDE_ROT_WB_OFFSET+0x038)
+#define ROT_WB_DITHER_MATRIX_ROW3               (SDE_ROT_WB_OFFSET+0x03C)
+#define ROT_WB_TRAFFIC_SHAPER_WR_CLIENT         (SDE_ROT_WB_OFFSET+0x040)
+#define ROT_WB_DST_WRITE_CONFIG                 (SDE_ROT_WB_OFFSET+0x048)
+#define ROT_WB_ROTATOR_PIPE_DOWNSCALER          (SDE_ROT_WB_OFFSET+0x054)
+#define ROT_WB_OUT_SIZE                         (SDE_ROT_WB_OFFSET+0x074)
+#define ROT_WB_DST_ALPHA_X_VALUE                (SDE_ROT_WB_OFFSET+0x078)
+#define ROT_WB_HW_VERSION                       (SDE_ROT_WB_OFFSET+0x080)
+#define ROT_WB_DANGER_LUT                       (SDE_ROT_WB_OFFSET+0x084)
+#define ROT_WB_SAFE_LUT                         (SDE_ROT_WB_OFFSET+0x088)
+#define ROT_WB_CREQ_LUT                         (SDE_ROT_WB_OFFSET+0x08C)
+#define ROT_WB_QOS_CTRL                         (SDE_ROT_WB_OFFSET+0x090)
+#define ROT_WB_CSC_MATRIX_COEFF_0               (SDE_ROT_WB_OFFSET+0x260)
+#define ROT_WB_CSC_MATRIX_COEFF_1               (SDE_ROT_WB_OFFSET+0x264)
+#define ROT_WB_CSC_MATRIX_COEFF_2               (SDE_ROT_WB_OFFSET+0x268)
+#define ROT_WB_CSC_MATRIX_COEFF_3               (SDE_ROT_WB_OFFSET+0x26C)
+#define ROT_WB_CSC_MATRIX_COEFF_4               (SDE_ROT_WB_OFFSET+0x270)
+#define ROT_WB_CSC_COMP0_PRECLAMP               (SDE_ROT_WB_OFFSET+0x274)
+#define ROT_WB_CSC_COMP1_PRECLAMP               (SDE_ROT_WB_OFFSET+0x278)
+#define ROT_WB_CSC_COMP2_PRECLAMP               (SDE_ROT_WB_OFFSET+0x27C)
+#define ROT_WB_CSC_COMP0_POSTCLAMP              (SDE_ROT_WB_OFFSET+0x280)
+#define ROT_WB_CSC_COMP1_POSTCLAMP              (SDE_ROT_WB_OFFSET+0x284)
+#define ROT_WB_CSC_COMP2_POSTCLAMP              (SDE_ROT_WB_OFFSET+0x288)
+#define ROT_WB_CSC_COMP0_PREBIAS                (SDE_ROT_WB_OFFSET+0x28C)
+#define ROT_WB_CSC_COMP1_PREBIAS                (SDE_ROT_WB_OFFSET+0x290)
+#define ROT_WB_CSC_COMP2_PREBIAS                (SDE_ROT_WB_OFFSET+0x294)
+#define ROT_WB_CSC_COMP0_POSTBIAS               (SDE_ROT_WB_OFFSET+0x298)
+#define ROT_WB_CSC_COMP1_POSTBIAS               (SDE_ROT_WB_OFFSET+0x29C)
+#define ROT_WB_CSC_COMP2_POSTBIAS               (SDE_ROT_WB_OFFSET+0x2A0)
+#define ROT_WB_DST_ADDR_SW_STATUS               (SDE_ROT_WB_OFFSET+0x2B0)
+#define ROT_WB_CDP_CNTL                         (SDE_ROT_WB_OFFSET+0x2B4)
+#define ROT_WB_STATUS                           (SDE_ROT_WB_OFFSET+0x2B8)
+#define ROT_WB_UBWC_ERROR_STATUS                (SDE_ROT_WB_OFFSET+0x2BC)
+#define ROT_WB_OUT_IMG_SIZE                     (SDE_ROT_WB_OFFSET+0x2C0)
+#define ROT_WB_OUT_XY                           (SDE_ROT_WB_OFFSET+0x2C4)
+
+
+/* SDE_ROT_REGDMA_RAM:
+ * OFFSET=0x0A8E00
+ */
+#define SDE_ROT_REGDMA_RAM_OFFSET              0xA8E00
+#define REGDMA_RAM_REGDMA_CMD_RAM              (SDE_ROT_REGDMA_RAM_OFFSET+0x00)
+
+
+/* SDE_ROT_REGDMA_CSR:
+ * OFFSET=0x0AAE00
+ */
+#define SDE_ROT_REGDMA_OFFSET                    0xAAE00
+#define REGDMA_CSR_REGDMA_VERSION                (SDE_ROT_REGDMA_OFFSET+0x00)
+#define REGDMA_CSR_REGDMA_OP_MODE                (SDE_ROT_REGDMA_OFFSET+0x04)
+#define REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT         (SDE_ROT_REGDMA_OFFSET+0x10)
+#define REGDMA_CSR_REGDMA_QUEUE_0_STATUS         (SDE_ROT_REGDMA_OFFSET+0x14)
+#define REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT         (SDE_ROT_REGDMA_OFFSET+0x18)
+#define REGDMA_CSR_REGDMA_QUEUE_1_STATUS         (SDE_ROT_REGDMA_OFFSET+0x1C)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_0             (SDE_ROT_REGDMA_OFFSET+0x20)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_0             (SDE_ROT_REGDMA_OFFSET+0x24)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_1             (SDE_ROT_REGDMA_OFFSET+0x28)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_1             (SDE_ROT_REGDMA_OFFSET+0x2C)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_2             (SDE_ROT_REGDMA_OFFSET+0x30)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_2             (SDE_ROT_REGDMA_OFFSET+0x34)
+#define REGDMA_CSR_REGDMA_BLOCK_LO_3             (SDE_ROT_REGDMA_OFFSET+0x38)
+#define REGDMA_CSR_REGDMA_BLOCK_HI_3             (SDE_ROT_REGDMA_OFFSET+0x3C)
+#define REGDMA_CSR_REGDMA_WD_TIMER_CTL           (SDE_ROT_REGDMA_OFFSET+0x40)
+#define REGDMA_CSR_REGDMA_WD_TIMER_CTL2          (SDE_ROT_REGDMA_OFFSET+0x44)
+#define REGDMA_CSR_REGDMA_WD_TIMER_LOAD_VALUE    (SDE_ROT_REGDMA_OFFSET+0x48)
+#define REGDMA_CSR_REGDMA_WD_TIMER_STATUS_VALUE  (SDE_ROT_REGDMA_OFFSET+0x4C)
+#define REGDMA_CSR_REGDMA_INT_STATUS             (SDE_ROT_REGDMA_OFFSET+0x50)
+#define REGDMA_CSR_REGDMA_INT_EN                 (SDE_ROT_REGDMA_OFFSET+0x54)
+#define REGDMA_CSR_REGDMA_INT_CLEAR              (SDE_ROT_REGDMA_OFFSET+0x58)
+#define REGDMA_CSR_REGDMA_BLOCK_STATUS           (SDE_ROT_REGDMA_OFFSET+0x5C)
+#define REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET (SDE_ROT_REGDMA_OFFSET+0x60)
+#define REGDMA_CSR_REGDMA_FSM_STATE              (SDE_ROT_REGDMA_OFFSET+0x64)
+#define REGDMA_CSR_REGDMA_DEBUG_SEL              (SDE_ROT_REGDMA_OFFSET+0x68)
+
+
+/* SDE_ROT_QDSS:
+ * OFFSET=0x0AAF00
+ */
+#define ROT_QDSS_CONFIG                          0x00
+#define ROT_QDSS_ATB_DATA_ENABLE0                0x04
+#define ROT_QDSS_ATB_DATA_ENABLE1                0x08
+#define ROT_QDSS_ATB_DATA_ENABLE2                0x0C
+#define ROT_QDSS_ATB_DATA_ENABLE3                0x10
+#define ROT_QDSS_CLK_CTRL                        0x14
+#define ROT_QDSS_CLK_STATUS                      0x18
+#define ROT_QDSS_PULSE_TRIGGER                   0x20
+
+
+/* REGDMA OP Code */
+#define REGDMA_OP_NOP                   (0 << 28)
+#define REGDMA_OP_REGWRITE              (1 << 28)
+#define REGDMA_OP_REGMODIFY             (2 << 28)
+#define REGDMA_OP_BLKWRITE_SINGLE       (3 << 28)
+#define REGDMA_OP_BLKWRITE_INC          (4 << 28)
+#define REGDMA_OP_MASK                  0xF0000000
+
+/* REGDMA ADDR offset Mask */
+#define REGDMA_ADDR_OFFSET_MASK         0xFFFFF
+
+/* General defines */
+#define ROT_DONE_MASK                   0x1
+#define ROT_DONE_CLEAR                  0x1
+#define ROT_BUSY_BIT                    BIT(1)
+#define ROT_ERROR_BIT                   BIT(8)
+#define ROT_STATUS_MASK                 (ROT_BUSY_BIT | ROT_ERROR_BIT)
+#define REGDMA_EN                       0x1
+#define REGDMA_SECURE_EN                BIT(8)
+#define REGDMA_HALT                     BIT(16)
+
+#define REGDMA_WATCHDOG_INT             BIT(19)
+#define REGDMA_INVALID_DESCRIPTOR       BIT(18)
+#define REGDMA_INCOMPLETE_CMD           BIT(17)
+#define REGDMA_INVALID_CMD              BIT(16)
+#define REGDMA_QUEUE1_INT2              BIT(10)
+#define REGDMA_QUEUE1_INT1              BIT(9)
+#define REGDMA_QUEUE1_INT0              BIT(8)
+#define REGDMA_QUEUE0_INT2              BIT(2)
+#define REGDMA_QUEUE0_INT1              BIT(1)
+#define REGDMA_QUEUE0_INT0              BIT(0)
+#define REGDMA_INT_MASK                 0x000F0707
+#define REGDMA_INT_HIGH_MASK            0x00000007
+#define REGDMA_INT_LOW_MASK             0x00000700
+#define REGDMA_INT_ERR_MASK             0x000F0000
+#define REGDMA_TIMESTAMP_REG            ROT_SSPP_TPG_PATTERN_GEN_INIT_VAL
+
+#endif /*_SDE_ROTATOR_R3_HWIO_H */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
new file mode 100644
index 0000000..5196b13
--- /dev/null
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -0,0 +1,352 @@
+/* Copyright (c) 2015-2016, 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 _SDE_ROTATOR_R3_INTERNAL_H
+#define _SDE_ROTATOR_R3_INTERNAL_H
+
+#include "sde_rotator_core.h"
+
+struct sde_hw_rotator;
+struct sde_hw_rotator_context;
+
+/**
+ * Flags
+ */
+#define SDE_ROT_FLAG_SECURE_OVERLAY_SESSION 0x1
+#define SDE_ROT_FLAG_FLIP_LR                0x2
+#define SDE_ROT_FLAG_FLIP_UD                0x4
+#define SDE_ROT_FLAG_SOURCE_ROTATED_90      0x8
+#define SDE_ROT_FLAG_ROT_90                 0x10
+#define SDE_ROT_FLAG_DEINTERLACE            0x20
+
+/**
+ * General defines
+ */
+#define SDE_HW_ROT_REGDMA_RAM_SIZE      1024
+#define SDE_HW_ROT_REGDMA_TOTAL_CTX     8
+#define SDE_HW_ROT_REGDMA_SEG_MASK      (SDE_HW_ROT_REGDMA_TOTAL_CTX - 1)
+#define SDE_HW_ROT_REGDMA_SEG_SIZE \
+	(SDE_HW_ROT_REGDMA_RAM_SIZE / SDE_HW_ROT_REGDMA_TOTAL_CTX)
+#define SDE_REGDMA_SWTS_MASK            0x00000FFF
+#define SDE_REGDMA_SWTS_SHIFT           12
+
+enum sde_rot_queue_prio {
+	ROT_QUEUE_HIGH_PRIORITY,
+	ROT_QUEUE_LOW_PRIORITY,
+	ROT_QUEUE_MAX
+};
+
+enum sde_rot_angle {
+	ROT_ANGLE_0,
+	ROT_ANGLE_90,
+	ROT_ANGEL_MAX
+};
+
+enum sde_rotator_regdma_mode {
+	ROT_REGDMA_OFF,
+	ROT_REGDMA_ON,
+	ROT_REGDMA_MAX
+};
+
+/**
+ * struct sde_hw_rot_sspp_cfg: Rotator SSPP Configration description
+ * @src:       source surface information
+ * @src_rect:  src ROI, caller takes into account the different operations
+ *             such as decimation, flip etc to program this field
+ * @addr:      source surface address
+ */
+struct sde_hw_rot_sspp_cfg {
+	struct sde_mdp_format_params *fmt;
+	struct sde_mdp_plane_sizes    src_plane;
+	struct sde_rect              *src_rect;
+	struct sde_mdp_data          *data;
+	u32                           img_width;
+	u32                           img_height;
+};
+
+
+
+/**
+ *  struct sde_hw_rot_wb_cfg: Rotator WB Configration description
+ *  @dest:      destination surface information
+ *  @dest_rect: dest ROI, caller takes into account the different operations
+ *              such as decimation, flip etc to program this field
+ *  @addr:      destination surface address
+ */
+struct sde_hw_rot_wb_cfg {
+	struct sde_mdp_format_params   *fmt;
+	struct sde_mdp_plane_sizes      dst_plane;
+	struct sde_rect                *dst_rect;
+	struct sde_mdp_data            *data;
+	u32                             img_width;
+	u32                             img_height;
+	u32                             v_downscale_factor;
+	u32                             h_downscale_factor;
+};
+
+
+
+/**
+ *
+ * struct sde_hw_rotator_ops: Interface to the Rotator Hw driver functions
+ *
+ * Pre-requsises:
+ *  - Caller must call the init function to get the rotator context
+ *  - These functions will be called after clocks are enabled
+ */
+struct sde_hw_rotator_ops {
+	/**
+	 * setup_rotator_fetchengine():
+	 *    Setup Source format
+	 *    Setup Source dimension/cropping rectangle (ROI)
+	 *    Setup Source surface base address and stride
+	 *    Setup fetch engine op mode (linear/tiled/compression/...)
+	 * @ctx:        Rotator context created in sde_hw_rotator_config
+	 * @queue_id:   Select either low / high priority queue
+	 * @cfg:        Rotator Fetch engine configuration parameters
+	 * @danger_lut: Danger LUT setting
+	 * @safe_lut:   Safe LUT setting
+	 * @flags:      Specific config flag, see SDE_ROT_FLAG_ for details
+	 */
+	void (*setup_rotator_fetchengine)(
+			struct sde_hw_rotator_context  *ctx,
+			enum   sde_rot_queue_prio       queue_id,
+			struct sde_hw_rot_sspp_cfg     *cfg,
+			u32                             danger_lut,
+			u32                             safe_lut,
+			u32                             flags);
+
+	/**
+	 * setup_rotator_wbengine():
+	 *     Setup destination formats
+	 *     Setup destination dimension/cropping rectangle (ROI)
+	 *     Setup destination surface base address and strides
+	 *     Setup writeback engine op mode (linear/tiled/compression)
+	 * @ctx:        Rotator context created in sde_hw_rotator_config
+	 * @queue_id:   Select either low / high priority queue
+	 * @cfg:        Rotator WriteBack engine configuration parameters
+	 * @flags:      Specific config flag, see SDE_ROT_FLAG_ for details
+	 */
+	void (*setup_rotator_wbengine)(
+			struct sde_hw_rotator_context *ctx,
+			enum   sde_rot_queue_prio      queue_id,
+			struct sde_hw_rot_wb_cfg      *cfg,
+			u32                            flags);
+
+	/**
+	 * start_rotator():
+	 *     Kick start rotator operation based on cached setup parameters
+	 *     REGDMA commands will get generated at this points
+	 * @ctx:      Rotator context
+	 * @queue_id: Select either low / high priority queue
+	 * Returns:   unique job timestamp per submit. Used for tracking
+	 *            rotator finished job.
+	 */
+	u32 (*start_rotator)(
+			struct sde_hw_rotator_context  *ctx,
+			enum   sde_rot_queue_prio       queue_id);
+
+	/**
+	 * wait_rotator_done():
+	 *     Notify Rotator HAL layer previously submitted job finished.
+	 *     A job timestamp will return to caller.
+	 * @ctx:    Rotator context
+	 * @flags:  Reserved
+	 * Returns: job timestamp for tracking purpose
+	 *
+	 */
+	u32 (*wait_rotator_done)(
+			struct sde_hw_rotator_context  *ctx,
+			enum   sde_rot_queue_prio       queue_id,
+			u32                             flags);
+};
+
+/**
+ * struct sde_dbg_buf : Debug buffer used by debugfs
+ * @vaddr:        VA address mapped from dma buffer
+ * @dmabuf:       DMA buffer
+ * @buflen:       Length of DMA buffer
+ * @width:        pixel width of buffer
+ * @height:       pixel height of buffer
+ */
+struct sde_dbg_buf {
+	void *vaddr;
+	struct dma_buf *dmabuf;
+	unsigned long buflen;
+	u32 width;
+	u32 height;
+};
+
+/**
+ * struct sde_hw_rotator_context : Each rotator context ties to each priority
+ * queue. Max number of concurrent contexts in regdma is limited to regdma
+ * ram segment size allocation. Each rotator context can be any priority. A
+ * incrementatl timestamp is used to identify and assigne to each context.
+ */
+struct sde_hw_rotator_context {
+	struct sde_hw_rotator *rot;
+	struct sde_rot_hw_resource *hwres;
+	enum   sde_rot_queue_prio q_id;
+	u32    session_id;
+	u32    *regdma_base;
+	u32    *regdma_wrptr;
+	u32    timestamp;
+	struct completion rot_comp;
+	struct completion regdma_comp;
+	struct sde_dbg_buf src_dbgbuf;
+	struct sde_dbg_buf dst_dbgbuf;
+	u32    last_regdma_isr_status;
+	u32    last_regdma_timestamp;
+	dma_addr_t ts_addr;
+	bool   is_secure;
+};
+
+/**
+ * struct sde_hw_rotator_resource_info : Each rotator resource ties to each
+ * priority queue
+ */
+struct sde_hw_rotator_resource_info {
+	struct sde_hw_rotator      *rot;
+	struct sde_rot_hw_resource  hw;
+};
+
+/**
+ * struct sde_hw_rotator : Rotator description
+ * @hw:           mdp register mapped offset
+ * @ops:          pointer to operations possible for the rotator HW
+ */
+struct sde_hw_rotator {
+	/* base */
+	char __iomem *mdss_base;
+
+	/* Platform device from upper manager */
+	struct platform_device *pdev;
+
+	/* Ops */
+	struct sde_hw_rotator_ops ops;
+
+	/* Cmd Queue */
+	u32    cmd_queue[SDE_HW_ROT_REGDMA_RAM_SIZE];
+
+	/* Cmd Queue Write Ptr */
+	u32   *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+
+	/* Rotator Context */
+	struct sde_hw_rotator_context
+		*rotCtx[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+
+	/* Cmd timestamp sequence for different priority*/
+	atomic_t timestamp[ROT_QUEUE_MAX];
+
+	/* regdma mode */
+	enum   sde_rotator_regdma_mode mode;
+
+	/* logical interrupt number */
+	int    irq_num;
+
+	/* internal ION memory for SW timestamp */
+	struct ion_client *iclient;
+	struct sde_mdp_img_data swts_buf;
+	void *swts_buffer;
+
+	u32    highest_bank;
+	struct completion rot_comp;
+	struct completion regdma_comp;
+
+	atomic_t regdma_submit_count;
+	atomic_t regdma_done_count;
+
+	spinlock_t rotctx_lock;
+	spinlock_t rotisr_lock;
+
+	bool    dbgmem;
+};
+
+/**
+ * sde_hw_rotator_get_regdma_ctxidx(): regdma segment index is based on
+ * timestamp. For non-regdma, just return 0 (i.e. first index)
+ * @ctx: Rotator Context
+ * return: regdma segment index
+ */
+static inline u32 sde_hw_rotator_get_regdma_ctxidx(
+		struct sde_hw_rotator_context *ctx)
+{
+	if (ctx->rot->mode == ROT_REGDMA_OFF)
+		return 0;
+	else
+		return ctx->timestamp & SDE_HW_ROT_REGDMA_SEG_MASK;
+}
+
+/**
+ * sde_hw_rotator_get_regdma_segment_base: return the base pointe of current
+ * regdma command buffer
+ * @ctx: Rotator Context
+ * return: base segment address
+ */
+static inline u32 *sde_hw_rotator_get_regdma_segment_base(
+		struct sde_hw_rotator_context *ctx)
+{
+	SDEROT_DBG("regdma base @slot[%d]: %p\n",
+			sde_hw_rotator_get_regdma_ctxidx(ctx),
+			ctx->regdma_base);
+
+	return ctx->regdma_base;
+}
+
+/**
+ * sde_hw_rotator_get_regdma_segment(): return current regdma command buffer
+ * pointer for current regdma segment.
+ * @ctx: Rotator Context
+ * return: segment address
+ */
+static inline u32 *sde_hw_rotator_get_regdma_segment(
+		struct sde_hw_rotator_context *ctx)
+{
+	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+	u32 *addr = ctx->regdma_wrptr;
+
+	SDEROT_DBG("regdma slot[%d] ==> %p\n", idx, addr);
+	return addr;
+}
+
+/**
+ * sde_hw_rotator_put_regdma_segment(): update current regdma command buffer
+ * pointer for current regdma segment
+ * @ctx: Rotator Context
+ * @wrptr: current regdma segment location
+ */
+static inline void sde_hw_rotator_put_regdma_segment(
+		struct sde_hw_rotator_context *ctx,
+		u32 *wrptr)
+{
+	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+
+	ctx->regdma_wrptr = wrptr;
+	SDEROT_DBG("regdma slot[%d] <== %p\n", idx, wrptr);
+}
+
+/**
+ * sde_hw_rotator_put_ctx(): Storing rotator context according to its
+ * timestamp.
+ */
+static inline void sde_hw_rotator_put_ctx(struct sde_hw_rotator_context *ctx)
+{
+	 struct sde_hw_rotator *rot = ctx->rot;
+	 u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
+
+	 rot->rotCtx[ctx->q_id][idx] = ctx;
+	 SDEROT_DBG("rotCtx[%d][%d] <== ctx:%p | session-id:%d\n",
+			 ctx->q_id, idx, ctx, ctx->session_id);
+}
+
+#endif /*_SDE_ROTATOR_R3_INTERNAL_H */