msm: rotator: Enable support for 2-pass fast YUV mode

Add support for 2-pass rotator operation for downscaling
and 90 degree rotation when rotator operates in fast YUV
mode. 2-pass is supported for 420 H2V2 formats.

Change-Id: Ib8ce802a5a8aabe10c88422e3b8d82708c5b05d4
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index d6a871d..305033b 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -155,6 +155,8 @@
 	struct msm_rotator_img_info img_info;
 	struct msm_rotator_fd_info fd_info;
 	int fast_yuv_enable;
+	int enable_2pass;
+	u32 mem_hid;
 };
 
 struct msm_rotator_commit_info {
@@ -179,6 +181,7 @@
 	int session_index;
 	struct sync_fence *acq_fen;
 	int fast_yuv_en;
+	int enable_2pass;
 };
 
 struct msm_rotator_dev {
@@ -222,6 +225,9 @@
 	atomic_t commit_q_r;
 	atomic_t commit_q_w;
 	atomic_t commit_q_cnt;
+	struct rot_buf_type *y_rot_buf;
+	struct rot_buf_type *chroma_rot_buf;
+	struct rot_buf_type *chroma2_rot_buf;
 };
 
 #define COMPONENT_5BITS 1
@@ -248,6 +254,129 @@
 	{"smmu_iface_clk"}
 };
 
+u32 rotator_allocate_2pass_buf(struct rot_buf_type *rot_buf, int s_ndx)
+{
+	ion_phys_addr_t	addr, read_addr = 0;
+	size_t buffer_size;
+	unsigned long len;
+
+	if (!rot_buf) {
+		pr_err("Rot_buf NULL pointer %s %i", __func__, __LINE__);
+		return 0;
+	}
+
+	if (rot_buf->write_addr || !IS_ERR_OR_NULL(rot_buf->ihdl))
+		return 0;
+
+	buffer_size = roundup(1920 * 1088, SZ_4K);
+
+	if (!IS_ERR_OR_NULL(mrd->client)) {
+		pr_info("%s:%d ion based allocation\n",
+			__func__, __LINE__);
+		rot_buf->ihdl = ion_alloc(mrd->client, buffer_size, SZ_4K,
+			mrd->rot_session[s_ndx]->mem_hid, 0);
+		if (!IS_ERR_OR_NULL(rot_buf->ihdl)) {
+			if (rot_iommu_split_domain) {
+				if (ion_map_iommu(mrd->client, rot_buf->ihdl,
+					ROTATOR_SRC_DOMAIN, GEN_POOL, SZ_4K,
+					0, &read_addr, &len, 0, 0)) {
+					pr_err("ion_map_iommu() read failed\n");
+					return -ENOMEM;
+				}
+				if (mrd->rot_session[s_ndx]->mem_hid &
+								ION_SECURE) {
+					if (ion_phys(mrd->client, rot_buf->ihdl,
+						&addr, (size_t *)&len)) {
+						pr_err(
+						"%s:%d: ion_phys map failed\n",
+							 __func__, __LINE__);
+						return -ENOMEM;
+					}
+				} else {
+					if (ion_map_iommu(mrd->client,
+					    rot_buf->ihdl, ROTATOR_DST_DOMAIN,
+					    GEN_POOL, SZ_4K, 0, &addr, &len,
+					    0, 0)) {
+						pr_err("ion_map_iommu() failed\n");
+						return -ENOMEM;
+					}
+				}
+			} else {
+				if (ion_map_iommu(mrd->client, rot_buf->ihdl,
+					ROTATOR_SRC_DOMAIN, GEN_POOL, SZ_4K,
+					0, &addr, &len, 0, 0)) {
+					pr_err("ion_map_iommu() write failed\n");
+					return -ENOMEM;
+				}
+			}
+		} else {
+			pr_err("%s:%d: ion_alloc failed\n", __func__,
+				__LINE__);
+			return -ENOMEM;
+		}
+	} else {
+		addr = allocate_contiguous_memory_nomap(buffer_size,
+			mrd->rot_session[s_ndx]->mem_hid, 4);
+	}
+	if (addr) {
+		pr_info("allocating %d bytes at write=%x, read=%x for 2-pass\n",
+			buffer_size, (u32) addr, (u32) read_addr);
+		rot_buf->write_addr = addr;
+
+		if (read_addr)
+			rot_buf->read_addr = read_addr;
+		else
+			rot_buf->read_addr = rot_buf->write_addr;
+
+		return 0;
+	} else {
+		pr_err("%s cannot allocate memory for rotator 2-pass!\n",
+			 __func__);
+		return -ENOMEM;
+	}
+}
+
+void rotator_free_2pass_buf(struct rot_buf_type *rot_buf, int s_ndx)
+{
+
+	if (!rot_buf) {
+		pr_err("Rot_buf NULL pointer %s %i", __func__, __LINE__);
+		return;
+	}
+
+	if (!rot_buf->write_addr)
+		return;
+
+	if (!IS_ERR_OR_NULL(mrd->client)) {
+		if (!IS_ERR_OR_NULL(rot_buf->ihdl)) {
+			if (rot_iommu_split_domain) {
+				if (!(mrd->rot_session[s_ndx]->mem_hid &
+								ION_SECURE))
+					ion_unmap_iommu(mrd->client,
+					rot_buf->ihdl, ROTATOR_DST_DOMAIN,
+								GEN_POOL);
+				ion_unmap_iommu(mrd->client, rot_buf->ihdl,
+					ROTATOR_SRC_DOMAIN, GEN_POOL);
+			} else {
+				ion_unmap_iommu(mrd->client, rot_buf->ihdl,
+					ROTATOR_SRC_DOMAIN, GEN_POOL);
+			}
+			ion_free(mrd->client, rot_buf->ihdl);
+			rot_buf->ihdl = NULL;
+			pr_info("%s:%d Free rotator 2pass memory",
+					__func__, __LINE__);
+		}
+	} else {
+		if (rot_buf->write_addr) {
+			free_contiguous_memory_by_paddr(rot_buf->write_addr);
+			pr_debug("%s:%d Free rotator 2pass pmem\n", __func__,
+				__LINE__);
+		}
+	}
+	rot_buf->write_addr = 0;
+	rot_buf->read_addr = 0;
+}
+
 int msm_rotator_iommu_map_buf(int mem_id, int domain,
 	unsigned long *start, unsigned long *len,
 	struct ion_handle **pihdl, unsigned int secure)
@@ -1007,7 +1136,7 @@
 				  MSM_ROTATOR_OUT_PACK_PATTERN1);
 		}
 
-		iowrite32((3  << 18) | 		/* chroma sampling 3=4:2:0 */
+		iowrite32((3  << 18) |		/* chroma sampling 3=4:2:0 */
 			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
 			  1 << 8 |			/* ROT_EN */
 			  fast_yuv_en << 4 |		/*fast YUV*/
@@ -1018,6 +1147,307 @@
 		iowrite32((is_tile ? 2 : 0) << 29 |  /* frame format */
 			  (use_imem ? 0 : 1) << 22 | /* tile size */
 			  (in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
+			  0 << 18 |		/* unpack align */
+			  1 << 17 |		/* unpack tight */
+			  1 << 13 |		/* unpack count 0=1 component */
+			  0 << 9  |		/* src Bpp 0=1 byte ... */
+			  0 << 8  |		/* has alpha */
+			  0 << 6  |		/* alpha bits 3=8bits */
+			  3 << 4  |		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  |		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+	return 0;
+}
+
+static int msm_rotator_ycxcx_h2v2_2pass(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned int in_chroma_paddr,
+				  unsigned int out_chroma_paddr,
+				  unsigned int in_chroma2_paddr,
+				  unsigned int out_chroma2_paddr,
+				  int fast_yuv_en,
+				  int enable_2pass,
+				  int session_index)
+{
+	uint32_t pass2_src_format, pass1_dst_format, dst_format;
+	int is_tile = 0, post_pass1_buf_is_planar = 0;
+	unsigned int status;
+	int post_pass1_ystride = info->src_rect.w >> info->downscale_ratio;
+	int post_pass1_height = info->src_rect.h >> info->downscale_ratio;
+
+	/* DST format = SRC format for non-tiled SRC formats
+	 * when fast YUV is enabled. For TILED formats,
+	 * DST format of MDP_Y_CRCB_H2V2_TILE = MDP_Y_CRCB_H2V2
+	 * DST format of MDP_Y_CBCR_H2V2_TILE = MDP_Y_CBCR_H2V2
+	 */
+	switch (info->src.format) {
+	case MDP_Y_CRCB_H2V2_TILE:
+		is_tile = 1;
+		dst_format = MDP_Y_CRCB_H2V2;
+		pass1_dst_format = MDP_Y_CRCB_H2V2;
+		pass2_src_format = pass1_dst_format;
+		break;
+	case MDP_Y_CR_CB_H2V2:
+	case MDP_Y_CR_CB_GH2V2:
+	case MDP_Y_CB_CR_H2V2:
+		post_pass1_buf_is_planar = 1;
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V2:
+		dst_format = info->src.format;
+		pass1_dst_format = info->src.format;
+		pass2_src_format = pass1_dst_format;
+		break;
+	case MDP_Y_CBCR_H2V2_TILE:
+		is_tile = 1;
+		dst_format = MDP_Y_CBCR_H2V2;
+		pass1_dst_format = info->src.format;
+		pass2_src_format = pass1_dst_format;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (info->dst.format  != dst_format)
+		return -EINVAL;
+
+	/* Beginning of Pass-1 */
+	/* rotator expects YCbCr for planar input format */
+	if ((info->src.format == MDP_Y_CR_CB_H2V2 ||
+	    info->src.format == MDP_Y_CR_CB_GH2V2) &&
+	    rotator_hw_revision < ROTATOR_REVISION_V2)
+		swap(in_chroma_paddr, in_chroma2_paddr);
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(in_chroma2_paddr, MSM_ROTATOR_SRCP2_ADDR);
+
+	if (new_session) {
+		if (in_chroma2_paddr) {
+			if (info->src.format == MDP_Y_CR_CB_GH2V2) {
+				iowrite32(ALIGN(info->src.width, 16) |
+					ALIGN((info->src.width / 2), 16) << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+				iowrite32(ALIGN((info->src.width / 2), 16),
+					MSM_ROTATOR_SRC_YSTRIDE2);
+			} else {
+				iowrite32(info->src.width |
+					(info->src.width / 2) << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+				iowrite32((info->src.width / 2),
+					MSM_ROTATOR_SRC_YSTRIDE2);
+			}
+		} else {
+			iowrite32(info->src.width |
+					info->src.width << 16,
+					MSM_ROTATOR_SRC_YSTRIDE1);
+		}
+	}
+
+	pr_debug("src_rect.w=%i src_rect.h=%i src_rect.x=%i src_rect.y=%i",
+		info->src_rect.w, info->src_rect.h, info->src_rect.x,
+		info->src_rect.y);
+	pr_debug("src.width=%i src.height=%i src_format=%i",
+		info->src.width, info->src.height, info->src.format);
+	pr_debug("dst_width=%i dst_height=%i dst.x=%i dst.y=%i",
+		info->dst.width, info->dst.height, info->dst_x, info->dst_y);
+	pr_debug("post_pass1_ystride=%i post_pass1_height=%i downscale=%i",
+		post_pass1_ystride, post_pass1_height, info->downscale_ratio);
+
+	rotator_allocate_2pass_buf(mrd->y_rot_buf, session_index);
+	rotator_allocate_2pass_buf(mrd->chroma_rot_buf, session_index);
+	if (post_pass1_buf_is_planar)
+		rotator_allocate_2pass_buf(mrd->chroma2_rot_buf, session_index);
+
+	iowrite32(mrd->y_rot_buf->write_addr, MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(mrd->chroma_rot_buf->write_addr, MSM_ROTATOR_OUTP1_ADDR);
+	if (post_pass1_buf_is_planar)
+		iowrite32(mrd->chroma2_rot_buf->write_addr,
+				MSM_ROTATOR_OUTP2_ADDR);
+
+	if (post_pass1_buf_is_planar) {
+		if (pass1_dst_format == MDP_Y_CR_CB_GH2V2) {
+			iowrite32(ALIGN(post_pass1_ystride, 16) |
+				ALIGN((post_pass1_ystride / 2), 16) << 16,
+				MSM_ROTATOR_OUT_YSTRIDE1);
+			iowrite32(ALIGN((post_pass1_ystride / 2), 16),
+				MSM_ROTATOR_OUT_YSTRIDE2);
+		} else {
+			iowrite32(post_pass1_ystride |
+					post_pass1_ystride / 2 << 16,
+					MSM_ROTATOR_OUT_YSTRIDE1);
+			iowrite32(post_pass1_ystride / 2,
+					MSM_ROTATOR_OUT_YSTRIDE2);
+		}
+	} else {
+		iowrite32(post_pass1_ystride |
+				post_pass1_ystride << 16,
+				MSM_ROTATOR_OUT_YSTRIDE1);
+	}
+
+	if (pass1_dst_format == MDP_Y_CBCR_H2V2 ||
+		pass1_dst_format == MDP_Y_CB_CR_H2V2) {
+		iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+			  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+		iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+			  MSM_ROTATOR_OUT_PACK_PATTERN1);
+	} else {
+		iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+			  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+		iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+			  MSM_ROTATOR_OUT_PACK_PATTERN1);
+	}
+
+	iowrite32((3  << 18) |		/* chroma sampling 3=4:2:0 */
+		   0 << 9 |		/* Pass-1 No Rotation */
+		   1 << 8 |			/* ROT_EN */
+		   fast_yuv_en << 4 |		/*fast YUV*/
+		   info->downscale_ratio << 2 |	/* downscale v ratio */
+		   info->downscale_ratio,	/* downscale h ratio */
+		   MSM_ROTATOR_SUB_BLOCK_CFG);
+
+	iowrite32((is_tile ? 2 : 0) << 29 |  /* frame format */
+		  (use_imem ? 0 : 1) << 22 | /* tile size */
+		  (in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
+		  0 << 18 |		/* unpack align */
+		  1 << 17 |		/* unpack tight */
+		  1 << 13 |		/* unpack count 0=1 component */
+		  0 << 9  |		/* src Bpp 0=1 byte ... */
+		  0 << 8  |		/* has alpha */
+		  0 << 6  |		/* alpha bits 3=8bits */
+		  3 << 4  |		/* R/Cr bits 1=5 2=6 3=8 */
+		  3 << 2  |		/* B/Cb bits 1=5 2=6 3=8 */
+		  3 << 0,		/* G/Y  bits 1=5 2=6 3=8 */
+		  MSM_ROTATOR_SRC_FORMAT);
+
+	iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
+
+	msm_rotator_dev->processing = 1;
+	iowrite32(0x1, MSM_ROTATOR_START);
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	/* End of Pass-1 */
+	wait_event(msm_rotator_dev->wq,
+		   (msm_rotator_dev->processing == 0));
+	/* Beginning of Pass-2 */
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
+	if ((status & 0x03) != 0x01) {
+		pr_err("%s(): AXI Bus Error, issuing SW_RESET\n",
+							__func__);
+		iowrite32(0x1, MSM_ROTATOR_SW_RESET);
+	}
+	iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
+	iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
+
+	if (use_imem)
+		iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
+
+	iowrite32(((post_pass1_height & 0x1fff)
+				<< 16) |
+		  (post_pass1_ystride & 0x1fff),
+		  MSM_ROTATOR_SRC_SIZE);
+	iowrite32(0 << 16 | 0,
+		  MSM_ROTATOR_SRC_XY);
+	iowrite32(((post_pass1_height & 0x1fff)
+				<< 16) |
+		  (post_pass1_ystride & 0x1fff),
+		  MSM_ROTATOR_SRC_IMAGE_SIZE);
+
+	/* rotator expects YCbCr for planar input format */
+	if ((pass2_src_format == MDP_Y_CR_CB_H2V2 ||
+	    pass2_src_format == MDP_Y_CR_CB_GH2V2) &&
+	    rotator_hw_revision < ROTATOR_REVISION_V2)
+		swap(mrd->chroma_rot_buf->read_addr,
+				mrd->chroma2_rot_buf->read_addr);
+
+	iowrite32(mrd->y_rot_buf->read_addr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(mrd->chroma_rot_buf->read_addr,
+						MSM_ROTATOR_SRCP1_ADDR);
+	if (mrd->chroma2_rot_buf->read_addr)
+		iowrite32(mrd->chroma2_rot_buf->read_addr,
+						MSM_ROTATOR_SRCP2_ADDR);
+
+	if (post_pass1_buf_is_planar) {
+		if (pass2_src_format == MDP_Y_CR_CB_GH2V2) {
+			iowrite32(ALIGN(post_pass1_ystride, 16) |
+				ALIGN((post_pass1_ystride / 2), 16) << 16,
+				MSM_ROTATOR_SRC_YSTRIDE1);
+			iowrite32(ALIGN((post_pass1_ystride / 2), 16),
+				MSM_ROTATOR_SRC_YSTRIDE2);
+		} else {
+			iowrite32(post_pass1_ystride |
+				(post_pass1_ystride / 2) << 16,
+				MSM_ROTATOR_SRC_YSTRIDE1);
+			iowrite32((post_pass1_ystride / 2),
+				MSM_ROTATOR_SRC_YSTRIDE2);
+		}
+	} else {
+		iowrite32(post_pass1_ystride |
+				post_pass1_ystride << 16,
+				MSM_ROTATOR_SRC_YSTRIDE1);
+	}
+
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chroma_paddr +
+			(((info->dst_y * info->dst.width)/2) + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+	if (out_chroma2_paddr)
+		iowrite32(out_chroma2_paddr +
+			(((info->dst_y * info->dst.width)/2) + info->dst_x),
+			  MSM_ROTATOR_OUTP2_ADDR);
+
+
+	if (new_session) {
+		if (out_chroma2_paddr) {
+			if (info->dst.format == MDP_Y_CR_CB_GH2V2) {
+				iowrite32(ALIGN(info->dst.width, 16) |
+					ALIGN((info->dst.width / 2), 16) << 16,
+					MSM_ROTATOR_OUT_YSTRIDE1);
+				iowrite32(ALIGN((info->dst.width / 2), 16),
+					MSM_ROTATOR_OUT_YSTRIDE2);
+			} else {
+				iowrite32(info->dst.width |
+						info->dst.width/2 << 16,
+						MSM_ROTATOR_OUT_YSTRIDE1);
+				iowrite32(info->dst.width/2,
+						MSM_ROTATOR_OUT_YSTRIDE2);
+			}
+		} else {
+			iowrite32(info->dst.width |
+					info->dst.width << 16,
+					MSM_ROTATOR_OUT_YSTRIDE1);
+		}
+
+		if (dst_format == MDP_Y_CBCR_H2V2 ||
+			dst_format == MDP_Y_CB_CR_H2V2) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+
+		iowrite32((3  << 18) |	/* chroma sampling 3=4:2:0 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8 |			/* ROT_EN */
+			  fast_yuv_en << 4 |		/*fast YUV*/
+			  0 << 2 |	/* No downscale in Pass-2 */
+			  0,	/* No downscale in Pass-2 */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+
+		iowrite32(0 << 29 |
+				/* Output of Pass-1 will always be non-tiled */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  (in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
 			  0 << 18 | 		/* unpack align */
 			  1 << 17 | 		/* unpack tight */
 			  1 << 13 | 		/* unpack count 0=1 component */
@@ -1497,6 +1927,7 @@
 	commit_info->session_index = s;
 	commit_info->acq_fen = msm_rotator_dev->sync_info[s].acq_fen;
 	commit_info->fast_yuv_en = mrd->rot_session[s]->fast_yuv_enable;
+	commit_info->enable_2pass = mrd->rot_session[s]->enable_2pass;
 	mutex_unlock(&msm_rotator_dev->rotator_lock);
 	return 0;
 
@@ -1616,7 +2047,8 @@
 	case MDP_Y_CR_CB_GH2V2:
 	case MDP_Y_CRCB_H2V2_TILE:
 	case MDP_Y_CBCR_H2V2_TILE:
-		rc = msm_rotator_ycxcx_h2v2(img_info,
+		if (!commit_info->enable_2pass)
+			rc = msm_rotator_ycxcx_h2v2(img_info,
 					    in_paddr, out_paddr, use_imem,
 					    msm_rotator_dev->last_session_idx
 								!= s,
@@ -1625,6 +2057,18 @@
 					    in_chroma2_paddr,
 					    out_chroma2_paddr,
 					    commit_info->fast_yuv_en);
+		else
+			rc = msm_rotator_ycxcx_h2v2_2pass(img_info,
+					    in_paddr, out_paddr, use_imem,
+					    msm_rotator_dev->last_session_idx
+								!= s,
+					    in_chroma_paddr,
+					    out_chroma_paddr,
+					    in_chroma2_paddr,
+					    out_chroma2_paddr,
+					    commit_info->fast_yuv_en,
+					    commit_info->enable_2pass,
+					    s);
 		break;
 	case MDP_Y_CBCR_H2V1:
 	case MDP_Y_CRCB_H2V1:
@@ -1913,7 +2357,7 @@
 	int first_free_idx = INVALID_SESSION;
 	unsigned int dst_w, dst_h;
 	unsigned int is_planar420 = 0;
-	int fast_yuv_en = 0;
+	int fast_yuv_en = 0, enable_2pass = 0;
 	struct rot_sync_info *sync_info;
 
 	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
@@ -1954,9 +2398,7 @@
 	case MDP_Y_CRCB_H2V2:
 	case MDP_Y_CRCB_H2V2_TILE:
 	case MDP_Y_CBCR_H2V2_TILE:
-		if (rotator_hw_revision >= ROTATOR_REVISION_V2 &&
-			!(info.downscale_ratio &&
-			(info.rotations & MDP_ROT_90)))
+		if (rotator_hw_revision >= ROTATOR_REVISION_V2) {
 			fast_yuv_en = !fast_yuv_invalid_size_checker(
 						info.rotations,
 						info.src.width,
@@ -1965,6 +2407,10 @@
 						dst_h,
 						dst_w,
 						is_planar420);
+			if (fast_yuv_en && info.downscale_ratio &&
+				(info.rotations & MDP_ROT_90))
+					enable_2pass = 1;
+		}
 	break;
 	default:
 		fast_yuv_en = 0;
@@ -2030,6 +2476,7 @@
 			rot_session->img_info =	info;
 			rot_session->fd_info =	*fd_info;
 			rot_session->fast_yuv_enable = fast_yuv_en;
+			rot_session->enable_2pass = enable_2pass;
 
 			if (msm_rotator_dev->last_session_idx == s)
 				msm_rotator_dev->last_session_idx =
@@ -2060,6 +2507,18 @@
 		rot_session->img_info =	info;
 		rot_session->fd_info =	*fd_info;
 		rot_session->fast_yuv_enable = fast_yuv_en;
+		rot_session->enable_2pass = enable_2pass;
+
+		if (!IS_ERR_OR_NULL(mrd->client)) {
+			if (rot_session->img_info.secure) {
+				rot_session->mem_hid &= ~BIT(ION_IOMMU_HEAP_ID);
+				rot_session->mem_hid |= ION_SECURE;
+			} else {
+				rot_session->mem_hid |= BIT(ION_IOMMU_HEAP_ID);
+				rot_session->mem_hid &= ~ION_SECURE;
+			}
+		}
+
 		s = first_free_idx;
 	} else if (s == MAX_SESSIONS) {
 		dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
@@ -2115,6 +2574,11 @@
 					INVALID_SESSION;
 			msm_rotator_signal_timeline(s);
 			msm_rotator_release_acq_fence(s);
+			if (msm_rotator_dev->rot_session[s]->enable_2pass) {
+				rotator_free_2pass_buf(mrd->y_rot_buf, s);
+				rotator_free_2pass_buf(mrd->chroma_rot_buf, s);
+				rotator_free_2pass_buf(mrd->chroma2_rot_buf, s);
+			}
 			kfree(msm_rotator_dev->rot_session[s]);
 			msm_rotator_dev->rot_session[s] = NULL;
 			break;
@@ -2283,6 +2747,14 @@
 	msm_rotator_dev->core_clk = NULL;
 	msm_rotator_dev->pclk = NULL;
 
+	mrd->y_rot_buf = kmalloc(sizeof(struct rot_buf_type), GFP_KERNEL);
+	mrd->chroma_rot_buf = kmalloc(sizeof(struct rot_buf_type), GFP_KERNEL);
+	mrd->chroma2_rot_buf = kmalloc(sizeof(struct rot_buf_type), GFP_KERNEL);
+
+	memset((void *)mrd->y_rot_buf, 0, sizeof(struct rot_buf_type));
+	memset((void *)mrd->chroma_rot_buf, 0, sizeof(struct rot_buf_type));
+	memset((void *)mrd->chroma2_rot_buf, 0, sizeof(struct rot_buf_type));
+
 #ifdef CONFIG_MSM_BUS_SCALING
 	if (!msm_rotator_dev->bus_client_handle && pdata &&
 		pdata->bus_scale_table) {
diff --git a/include/linux/msm_rotator.h b/include/linux/msm_rotator.h
index 6a8d286..bb4aec0 100644
--- a/include/linux/msm_rotator.h
+++ b/include/linux/msm_rotator.h
@@ -30,6 +30,12 @@
 	int rel_fen_fd;
 };
 
+struct rot_buf_type {
+	struct ion_handle *ihdl;
+	uint32_t write_addr;
+	uint32_t read_addr;
+};
+
 struct msm_rotator_img_info {
 	unsigned int session_id;
 	struct msmfb_img  src;