From 826edf9b2b9af7b9bc32f4363724af174911175c Mon Sep 17 00:00:00 2001
From: Tim Yamin <plasm@roo.me.uk>
Date: Tue, 7 Apr 2009 11:52:35 -0700
Subject: [PATCH] DSS2: Add YUV VRFB rotation support

This is a rebased version of Hardik Shah's patch with a few additional
tweaks from me.

Signed-off-by: Tim Yamin <plasm@roo.me.uk>
---
 arch/arm/plat-omap/include/mach/display.h |    6 ++
 arch/arm/plat-omap/vrfb.c                 |    4 +-
 drivers/video/omap2/dss/dispc.c           |   87 ++++++++++++++++++++++++++--
 drivers/video/omap2/dss/dss.h             |    1 +
 drivers/video/omap2/dss/manager.c         |    1 +
 drivers/video/omap2/omapfb/omapfb-main.c  |   66 +++++++++++-----------
 drivers/video/omap2/omapfb/omapfb.h       |    7 +--
 7 files changed, 126 insertions(+), 46 deletions(-)

diff --git a/arch/arm/plat-omap/include/mach/display.h b/arch/arm/plat-omap/include/mach/display.h
index 6b702c7..b0a6272 100644
--- a/arch/arm/plat-omap/include/mach/display.h
+++ b/arch/arm/plat-omap/include/mach/display.h
@@ -341,6 +341,11 @@ enum omap_dss_overlay_managers {
 
 struct omap_overlay_manager;
 
+enum omap_dss_rotation_type {
+	OMAP_DSS_ROT_DMA = 0,
+	OMAP_DSS_ROT_VRFB = 1,
+};
+
 struct omap_overlay_info {
 	bool enabled;
 
@@ -351,6 +356,7 @@ struct omap_overlay_info {
 	u16 height;
 	enum omap_color_mode color_mode;
 	u8 rotation;
+	enum omap_dss_rotation_type rotation_type;
 	bool mirror;
 
 	u16 pos_x;
diff --git a/arch/arm/plat-omap/vrfb.c b/arch/arm/plat-omap/vrfb.c
index 7e0f8fc..77aa9a7 100644
--- a/arch/arm/plat-omap/vrfb.c
+++ b/arch/arm/plat-omap/vrfb.c
@@ -61,8 +61,10 @@ void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
 			width, height, bytespp);
 
 	if (bytespp == 4)
+	{
 		pixel_size_exp = 2;
-	else if (bytespp == 2)
+		width >>= 1;
+	} else if (bytespp == 2)
 		pixel_size_exp = 1;
 	else
 		BUG();
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index ae7be3d..b1dbb79 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -1106,7 +1106,7 @@ static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
 			case 0: vidrot = 0; break;
 			case 1: vidrot = 1; break;
 			case 2: vidrot = 2; break;
-			case 3: vidrot = 1; break;
+			case 3: vidrot = 3; break;
 			}
 		}
 
@@ -1134,7 +1134,70 @@ static s32 pixinc(int pixels, u8 ps)
 		BUG();
 }
 
-static void calc_rotation_offset(u8 rotation, bool mirror,
+static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
+		u16 screen_width,
+		u16 width, u16 height,
+		enum omap_color_mode color_mode, bool fieldmode,
+		unsigned *offset0, unsigned *offset1,
+		s32 *row_inc, s32 *pix_inc)
+{
+	u8 ps;
+
+	switch (color_mode) {
+	case OMAP_DSS_COLOR_RGB16:
+	case OMAP_DSS_COLOR_ARGB16:
+		ps = 2;
+		break;
+
+	case OMAP_DSS_COLOR_RGB24P:
+		ps = 3;
+		break;
+
+	case OMAP_DSS_COLOR_RGB24U:
+	case OMAP_DSS_COLOR_ARGB32:
+	case OMAP_DSS_COLOR_RGBA32:
+	case OMAP_DSS_COLOR_RGBX32:
+	case OMAP_DSS_COLOR_YUV2:
+	case OMAP_DSS_COLOR_UYVY:
+		ps = 4;
+		break;
+
+	default:
+		BUG();
+		return;
+	}
+
+	DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
+			width, height);
+	switch (rotation + mirror * 4) {
+	case 0:
+	case 2:
+		/*
+		 * If the pixel format is YUV or UYVY divide the width
+		 * of the image by 2.
+		 */
+		if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+			color_mode == OMAP_DSS_COLOR_UYVY)
+			width = width >> 1;
+	case 1:
+	case 3:
+		*offset0 = 0;
+		if (fieldmode)
+			*offset1 = screen_width * ps;
+		else
+			*offset1 = 0;
+
+		*row_inc = pixinc(1 + (screen_width - width) +
+				(fieldmode ? screen_width : 0),
+				ps);
+		*pix_inc = pixinc(1, ps);
+		break;
+	default:
+		BUG();
+	}
+}
+
+static void calc_dma_rotation_offset(u8 rotation, bool mirror,
 		u16 screen_width,
 		u16 width, u16 height,
 		enum omap_color_mode color_mode, bool fieldmode,
@@ -1357,6 +1420,7 @@ static int _dispc_setup_plane(enum omap_plane plane,
 		u16 out_width, u16 out_height,
 		enum omap_color_mode color_mode,
 		bool ilace,
+		enum omap_dss_rotation_type rotation_type,
 		u8 rotation, int mirror)
 {
 	const int maxdownscale = cpu_is_omap34xx() ? 4 : 2;
@@ -1463,10 +1527,16 @@ static int _dispc_setup_plane(enum omap_plane plane,
 			return -EINVAL;
 	}
 
-	calc_rotation_offset(rotation, mirror,
-			screen_width, width, frame_height, color_mode,
-			fieldmode,
-			&offset0, &offset1, &row_inc, &pix_inc);
+	if (rotation_type == OMAP_DSS_ROT_DMA)
+		calc_dma_rotation_offset(rotation, mirror,
+				screen_width, width, height, color_mode,
+				fieldmode,
+				&offset0, &offset1, &row_inc, &pix_inc);
+	else
+		calc_vrfb_rotation_offset(rotation, mirror,
+				screen_width, width, height, color_mode,
+				fieldmode,
+				&offset0, &offset1, &row_inc, &pix_inc);
 
 	DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
 			offset0, offset1, row_inc, pix_inc);
@@ -2879,6 +2949,7 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
 		       u16 out_width, u16 out_height,
 		       enum omap_color_mode color_mode,
 		       bool ilace,
+		       enum omap_dss_rotation_type rotation_type,
 		       u8 rotation, bool mirror)
 {
 	int r = 0;
@@ -2899,6 +2970,7 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
 			   width, height,
 			   out_width, out_height,
 			   color_mode, ilace,
+			   rotation_type,
 			   rotation, mirror);
 
 	enable_clocks(0);
@@ -3112,7 +3184,8 @@ void dispc_setup_partial_planes(struct omap_display *display,
 				pw, ph,
 				pow, poh,
 				pi->color_mode, 0,
-				pi->rotation, // XXX rotation probably wrong
+				pi->rotation_type,
+				pi->rotation,
 				pi->mirror);
 
 		dispc_enable_plane(ovl->id, 1);
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 0be42b6..c722eee 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -269,6 +269,7 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_channel channel_out,
 		      u16 out_width, u16 out_height,
 		      enum omap_color_mode color_mode,
 		      bool ilace,
+		      enum omap_dss_rotation_type rotation_type,
 		      u8 rotation, bool mirror);
 
 void dispc_go(enum omap_channel channel);
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index b0fee80..8ca0bbb 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -395,6 +395,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
 				outh,
 				ovl->info.color_mode,
 				ilace,
+				ovl->info.rotation_type,
 				ovl->info.rotation,
 				ovl->info.mirror);
 
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 12ce0c3..8327681 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -176,15 +176,9 @@ static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot)
 
 static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi)
 {
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB) {
-		unsigned offset;
-		int rot;
-
-		rot = ofbi->rotation;
-
-		offset = omapfb_get_vrfb_offset(ofbi, rot);
-
-		return ofbi->region.vrfb.paddr[rot] + offset;
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+		return ofbi->region.vrfb.paddr[ofbi->rotation]
+			+ omapfb_get_vrfb_offset(ofbi, ofbi->rotation);
 	} else {
 		return ofbi->region.paddr;
 	}
@@ -192,7 +186,7 @@ static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi)
 
 u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
 {
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 		return ofbi->region.vrfb.paddr[0];
 	else
 		return ofbi->region.paddr;
@@ -200,7 +194,7 @@ u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
 
 void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi)
 {
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 		return ofbi->region.vrfb.vaddr[0];
 	else
 		return ofbi->region.vaddr;
@@ -383,7 +377,7 @@ void set_fb_fix(struct fb_info *fbi)
 	fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi);
 
 	/* used by mmap in fbmem.c */
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 		fix->line_length =
 			(OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;
 	else
@@ -419,11 +413,21 @@ void set_fb_fix(struct fb_info *fbi)
 	fix->xpanstep = 1;
 	fix->ypanstep = 1;
 
-	if (rg->size) {
-		if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
-			omap_vrfb_setup(&rg->vrfb, rg->paddr,
-					var->xres_virtual, var->yres_virtual,
-					var->bits_per_pixel >> 3);
+	if (rg->size && ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+		switch(var->nonstd)
+		{
+			case OMAPFB_COLOR_YUV422:
+			case OMAPFB_COLOR_YUV420:
+			case OMAPFB_COLOR_YUY422:
+				omap_vrfb_setup(&rg->vrfb, rg->paddr,
+						var->xres_virtual, var->yres_virtual,
+						var->bits_per_pixel >> 2);
+				break;
+			default:
+				omap_vrfb_setup(&rg->vrfb, rg->paddr,
+						var->xres_virtual, var->yres_virtual,
+						var->bits_per_pixel >> 3);
+		}
 	}
 }
 
@@ -512,7 +516,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
 	if (var->yres > var->yres_virtual)
 		var->yres = var->yres_virtual;
 
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 		line_size = OMAP_VRFB_LINE_LEN * bytespp;
 	else
 		line_size = var->xres_virtual * bytespp;
@@ -534,7 +538,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
 
 	if (line_size * var->yres_virtual > max_frame_size) {
 		DBG("can't fit FB into memory, reducing x\n");
-		if (ofbi->rotation_type == OMAPFB_ROT_VRFB)
+		if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 			return -EINVAL;
 
 		var->xres_virtual = max_frame_size / var->yres_virtual /
@@ -657,7 +661,7 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
 	struct omap_overlay_info info;
 	int xres, yres;
 	int screen_width;
-	int rot, mirror;
+	int mirror;
 
 	DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id,
 			posx, posy, outw, outh);
@@ -673,7 +677,7 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
 	offset = ((var->yoffset * var->xres_virtual +
 				var->xoffset) * var->bits_per_pixel) >> 3;
 
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB) {
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
 		data_start_p = omapfb_get_region_rot_paddr(ofbi);
 		data_start_v = NULL;
 	} else {
@@ -696,13 +700,10 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
 
 	ovl->get_overlay_info(ovl, &info);
 
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB) {
-		rot = 0;
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
 		mirror = 0;
-	} else {
-		rot = ofbi->rotation;
+	else
 		mirror = ofbi->mirror;
-	}
 
 	info.paddr = data_start_p;
 	info.vaddr = data_start_v;
@@ -710,7 +711,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
 	info.width = xres;
 	info.height = yres;
 	info.color_mode = mode;
-	info.rotation = rot;
+	info.rotation_type = ofbi->rotation_type;
+	info.rotation = ofbi->rotation;
 	info.mirror = mirror;
 
 	info.pos_x = posx;
@@ -1106,7 +1108,7 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
 	if (rg->vaddr)
 		iounmap(rg->vaddr);
 
-	if (ofbi->rotation_type == OMAPFB_ROT_VRFB) {
+	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
 		/* unmap the 0 angle rotation */
 		if (rg->vrfb.vaddr[0]) {
 			iounmap(rg->vrfb.vaddr[0]);
@@ -1166,7 +1168,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
 		return -ENOMEM;
 	}
 
-	if (ofbi->rotation_type != OMAPFB_ROT_VRFB) {
+	if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) {
 		vaddr = ioremap_wc(paddr, size);
 
 		if (!vaddr) {
@@ -1245,7 +1247,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
 
 		display->get_resolution(display, &w, &h);
 
-		if (ofbi->rotation_type == OMAPFB_ROT_VRFB) {
+		if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
 #ifdef DEBUG
 			int oldw = w, oldh = h;
 #endif
@@ -1602,8 +1604,8 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
 		ofbi->id = i;
 
 		/* assign these early, so that fb alloc can use them */
-		ofbi->rotation_type = def_vrfb ? OMAPFB_ROT_VRFB :
-			OMAPFB_ROT_DMA;
+		ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :
+			OMAP_DSS_ROT_DMA;
 		ofbi->rotation = def_rotate;
 		ofbi->mirror = def_mirror;
 
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
index 65e9e6e..88e7675 100644
--- a/drivers/video/omap2/omapfb/omapfb.h
+++ b/drivers/video/omap2/omapfb/omapfb.h
@@ -51,11 +51,6 @@ struct omapfb2_mem_region {
 	bool		map;		/* kernel mapped by the driver */
 };
 
-enum omapfb_rotation_type {
-	OMAPFB_ROT_DMA = 0,
-	OMAPFB_ROT_VRFB = 1,
-};
-
 /* appended to fb_info */
 struct omapfb_info {
 	int id;
@@ -64,7 +59,7 @@ struct omapfb_info {
 	int num_overlays;
 	struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
 	struct omapfb2_device *fbdev;
-	enum omapfb_rotation_type rotation_type;
+	enum omap_dss_rotation_type rotation_type;
 	u8 rotation;
 	bool mirror;
 };
-- 
1.5.6.3

