From ce5f3b3d701e7f6ac40fe5e06c5ac32562ba36c7 Mon Sep 17 00:00:00 2001
From: Andrew Hannam <andrewh@inmarket.com.au>
Date: Fri, 8 Mar 2013 00:53:34 +1000
Subject: Nokia6610GE8 driver updates

Fix screen size to ignore hidden pixels.
Add orientation support.
Fix bit blitting boundary conditions.
Add power modes support.
This driver is now fully implemented.
---
 drivers/gdisp/Nokia6610GE8/gdisp_lld.c | 317 +++++++++++++++++++++------------
 1 file changed, 201 insertions(+), 116 deletions(-)

(limited to 'drivers/gdisp/Nokia6610GE8')

diff --git a/drivers/gdisp/Nokia6610GE8/gdisp_lld.c b/drivers/gdisp/Nokia6610GE8/gdisp_lld.c
index ed6861e8..65431c9b 100644
--- a/drivers/gdisp/Nokia6610GE8/gdisp_lld.c
+++ b/drivers/gdisp/Nokia6610GE8/gdisp_lld.c
@@ -41,7 +41,7 @@
 
 #include "GE8.h"
 
-/* This controller is only ever used with a 132 x 132 display */
+/* This controller is only ever used with a 130 x 130 display */
 #if defined(GDISP_SCREEN_HEIGHT)
 	#warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
 	#undef GDISP_SCREEN_HEIGHT
@@ -50,8 +50,14 @@
 	#warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
 	#undef GDISP_SCREEN_WIDTH
 #endif
-#define GDISP_SCREEN_HEIGHT		132
-#define GDISP_SCREEN_WIDTH		132
+#define GDISP_SCREEN_HEIGHT		130
+#define GDISP_SCREEN_WIDTH		130
+
+#define GDISP_SCAN_LINES		132		/* 130 lines + 2 invisible lines */
+#define GDISP_RAM_X_OFFSET		0		/* Offset in RAM of visible area */
+#define GDISP_RAM_Y_OFFSET		2		/* Offset in RAM of visible area */
+#define GDISP_SLEEP_SIZE		32		/* Sleep mode window lines */
+#define GDISP_SLEEP_POS			((GDISP_SCAN_LINES-GDISP_SLEEP_SIZE)/2)
 
 #define GDISP_INITIAL_CONTRAST	38
 #define GDISP_INITIAL_BACKLIGHT	100
@@ -79,18 +85,36 @@
 #endif
 
 // Some macros just to make reading the code easier
-#define delayms(ms)					chThdSleepMilliseconds(ms)
-#define write_data2(d1, d2)			{ write_data(d1); write_data(d2); }
-#define write_data3(d1, d2, d3)		{ write_data(d1); write_data(d2); write_data(d3); }
-#define write_cmd1(cmd, d1)			{ write_cmd(cmd); write_data(d1); }
-#define write_cmd2(cmd, d1, d2)		{ write_cmd(cmd); write_data2(d1, d2); }
-#define write_cmd3(cmd, d1, d2, d3)	{ write_cmd(cmd); write_data3(d1, d2, d3); }
-
-// A very common thing to do.
+#define delayms(ms)						chThdSleepMilliseconds(ms)
+#define write_data2(d1, d2)				{ write_data(d1); write_data(d2); }
+#define write_data3(d1, d2, d3)			{ write_data(d1); write_data(d2); write_data(d3); }
+#define write_data4(d1, d2, d3, d4)		{ write_data(d1); write_data(d2); write_data(d3); write_data(d4); }
+#define write_cmd1(cmd, d1)				{ write_cmd(cmd); write_data(d1); }
+#define write_cmd2(cmd, d1, d2)			{ write_cmd(cmd); write_data2(d1, d2); }
+#define write_cmd3(cmd, d1, d2, d3)		{ write_cmd(cmd); write_data3(d1, d2, d3); }
+#define write_cmd4(cmd, d1, d2, d3, d4)	{ write_cmd(cmd); write_data4(d1, d2, d3, d4); }
+
+// Set the drawing window on the controller.
 // An inline function has been used here incase the parameters have side effects with the internal calculations.
-static inline void setviewport(coord_t x, coord_t y, coord_t cx, coord_t cy) {
-	write_cmd2(CASET, x, x+cx-1);			// Column address set
-	write_cmd2(PASET, y, y+cy-1);			// Page address set
+static __inline void setviewport(coord_t x, coord_t y, coord_t cx, coord_t cy) {
+	switch(GDISP.Orientation) {
+		case GDISP_ROTATE_0:
+			write_cmd2(CASET, GDISP_RAM_X_OFFSET+x, GDISP_RAM_X_OFFSET+x+cx-1);			// Column address set
+			write_cmd2(PASET, GDISP_RAM_Y_OFFSET+y, GDISP_RAM_Y_OFFSET+y+cy-1);			// Page address set
+			break;
+		case GDISP_ROTATE_90:
+			write_cmd2(CASET, GDISP_RAM_X_OFFSET+GDISP.Height-y-cy, GDISP_RAM_X_OFFSET+GDISP.Height-y-1);
+			write_cmd2(PASET, GDISP_RAM_Y_OFFSET+x, GDISP_RAM_Y_OFFSET+x+cx-1);
+			break;
+		case GDISP_ROTATE_180:
+			write_cmd2(CASET, GDISP_RAM_X_OFFSET+GDISP.Width-x-cx, GDISP_RAM_X_OFFSET+GDISP.Width-x-1);
+			write_cmd2(PASET, GDISP_RAM_Y_OFFSET+GDISP.Height-y-cy, GDISP_RAM_Y_OFFSET+GDISP.Height-y-1);
+			break;
+		case GDISP_ROTATE_270:
+			write_cmd2(CASET, GDISP_RAM_X_OFFSET+y, GDISP_RAM_X_OFFSET+y+cy-1);
+			write_cmd2(PASET, GDISP_RAM_Y_OFFSET+GDISP.Width-x-cx, GDISP_RAM_Y_OFFSET+GDISP.Width-x-1);
+			break;
+	}
 }
 
 /*===========================================================================*/
@@ -101,14 +125,8 @@ static inline void setviewport(coord_t x, coord_t y, coord_t cx, coord_t cy) {
 /* Driver exported functions.                                                */
 /*===========================================================================*/
 
-/* ---- Required Routines ---- */
-/*
-	The following 2 routines are required.
-	All other routines are optional.
-*/
-
 /**
- * @brief   Low level GDISP driver initialization.
+ * @brief   Low level GDISP driver initialisation.
  *
  * @notapi
  */
@@ -125,22 +143,24 @@ bool_t gdisp_lld_init(void) {
 	// Get the bus for the following initialisation commands
 	acquire_bus();
 	
-	write_cmd3(DISCTL, 0x00, 0x20, 0x00);				// Display control
+	write_cmd4(DISCTL, 0x00, GDISP_SCAN_LINES/4-1, 0x0A, 0x00);	// Display control - How the controller drives the LCD
 															// P1: 0x00 = 2 divisions, switching period=8 (default)
 															// P2: 0x20 = nlines/4 - 1 = 132/4 - 1 = 32)
-															// P3: 0x00 = no inversely highlighted lines
-	write_cmd1(COMSCN, 0x01);							// COM scan		P1: 0x01 = Scan 1->80, 160<-81
-	write_cmd(OSCON);									// Internal oscilator ON
+															// P3: 0x0A = standard inverse highlight, inversion every frame
+															// P4: 0x00 = dispersion on
+	write_cmd1(COMSCN, 0x01);							// COM scan - How the LCD is connected to the controller
+															// P1: 0x01 = Scan 1->80, 160<-81
+	write_cmd(OSCON);									// Internal oscillator ON
 	write_cmd(SLPOUT);									// Sleep out
 	write_cmd1(PWRCTR, 0x0F);							// Power control - reference voltage regulator on, circuit voltage follower on, BOOST ON
-	write_cmd3(DATCTL, 0x48, 0x00, 0x02);				// Data control
-															// P1: 0x01 = page address inverted, column address normal, address scan in column direction
+	write_cmd3(DATCTL, 0x00, 0x00, 0x02);				// Data control
+															// P1: 0x00 = page address normal, column address normal, address scan in column direction
 															// P2: 0x00 = RGB sequence (default value)
-															// P3: 0x02 = Grayscale -> 16 (selects 12-bit color, type A)
+															// P3: 0x02 = 4 bits per colour (Type A)
 	write_cmd2(VOLCTR, GDISP_INITIAL_CONTRAST, 0x03);	// Voltage control (contrast setting)
 															// P1 = Contrast
 															// P2 = 3 resistance ratio (only value that works)
-	delayms(100);						// allow power supply to stabilize
+	delayms(100);										// Allow power supply to stabilise
 	write_cmd(DISON);									// Turn on the display
 
 	// Release the bus
@@ -197,7 +217,7 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 	 * @notapi
 	 */
 	void gdisp_lld_fill_area(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) {
-		unsigned i, tuples;
+		unsigned tuples;
 
 		#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
 			if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; }
@@ -207,13 +227,13 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 			if (y+cy > GDISP.clipy1)	cy = GDISP.clipy1 - y;
 		#endif
 
-		tuples = (cx*cy+1)/2;				// With an odd sized area we over-print by one pixel.
-											// This extra pixel is ignored by the controller.
+		tuples = (cx*cy+1)>>1;				// With an odd sized area we over-print by one pixel.
+											// This extra pixel overwrites the first pixel (harmless as it is the same colour)
 
 		acquire_bus();
 		setviewport(x, y, cx, cy);
 		write_cmd(RAMWR);
-		for(i=0; i < tuples; i++)
+		while(tuples--)
 			write_data3(((color >> 4) & 0xFF), (((color << 4) & 0xF0)|((color >> 8) & 0x0F)), (color & 0xFF));
 		release_bus();
 	}
@@ -223,20 +243,23 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 	/**
 	 * @brief   Fill an area with a bitmap.
 	 *
-	 * @param[in] x, y     The start filled area
-	 * @param[in] cx, cy   The width and height to be filled
-	 * @param[in] srcx, srcy   The bitmap position to start the fill from
-	 * @param[in] srccx    The width of a line in the bitmap.
-	 * @param[in] buffer   The pixels to use to fill the area.
+	 * @param[in] x, y			The start filled area
+	 * @param[in] cx, cy		The width and height to be filled
+	 * @param[in] srcx, srcy	The bitmap position to start the fill from
+	 * @param[in] srccx			The width of a line in the bitmap.
+	 * @param[in] buffer		The pixels to use to fill the area.
 	 *
 	 * @notapi
 	 */
 	void gdisp_lld_blit_area_ex(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) {
-		coord_t endx, endy, lg;
-		color_t	c1, c2;
+		coord_t		lg;
+		color_t		c1, c2;
+		unsigned	tuples;
 		#if GDISP_PACKED_PIXELS
-			coord_t pos;
-			const uint8_t *p;
+			unsigned		pnum, pstart;
+			const uint8_t	*p;
+		#else
+			const pixel_t	*p;
 		#endif
 
 		#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
@@ -248,86 +271,123 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 			if (y+cy > GDISP.clipy1)	cy = GDISP.clipy1 - y;
 		#endif
 
-		/* What are our end points */
-		endx = srcx + cx;
-		endy = y + cy;
-
+		/* Set up the data window to transfer */
+		tuples = (cx * cy + 1)>>1;
 		acquire_bus();
 		setviewport(x, y, cx, cy);
 		write_cmd(RAMWR);
 
+		/*
+		 * Due to the way the Nokia6610 handles a decrementing column or page,
+		 * we have to make adjustments as to where it is actually drawing from in the bitmap.
+		 * For example, for 90 degree rotation the column is decremented on each
+		 * memory write. The controller always starts with column 0 and then decrements
+		 * to column cx-1, cx-2 etc. We therefore have to write-out the last bitmap line first.
+		 */
+		switch(GDISP.Orientation) {
+		case GDISP_ROTATE_0:		x = 0;		y = 0;		break;
+		case GDISP_ROTATE_90:		x = 0;		y = cy-1;	break;
+		case GDISP_ROTATE_180:		x = cx-1;	y = cy-1;	break;
+		case GDISP_ROTATE_270:		x = cx-1;	y = 0;		break;
+		}
+
 		#if !GDISP_PACKED_PIXELS
 			// Although this controller uses packed pixels we support unpacked pixel
 			//  formats in this blit by packing the data as we feed it to the controller.
-			lg = srccx - cx;
-			buffer += srcy * srccx + srcx;
-			x = srcx;
-			while (1) {
+
+			lg = srccx - cx;						// The buffer gap between lines
+			buffer += srcy * srccx + srcx;			// The buffer start position
+			p = buffer + srccx*y + x;				// Adjustment for controller craziness
+
+			while(tuples--) {
 				/* Get a pixel */
-				c1 = *buffer++;
-				if (++x >= endx) {
-					if (++y >= endy) {
-						/* Odd pixel at end */
-						write_data3(0, ((c1 >> 8) & 0x0F), (c1 & 0xFF));
-						break;
+				c1 = *p++;
+
+				/* Check for line or buffer wrapping */
+				if (++x >= cx) {
+					x = 0;
+					p += lg;
+					if (++y >= cy) {
+						y = 0;
+						p = buffer;
 					}
-					x = srcx;
-					buffer += lg;
 				}
+
 				/* Get the next pixel */
-				c2 = *buffer++;
-				write_data3(((c1 >> 4) & 0xFF), (((c1 << 4) & 0xF0)|((c2 >> 8) & 0x0F)), (c2 & 0xFF));
-				if (++x >= endx) {
-					if (++y >= endy)
-						break;
-					x = srcx;
-					buffer += lg;
+				c2 = *p++;
+
+				/* Check for line or buffer wrapping */
+				if (++x >= cx) {
+					x = 0;
+					p += lg;
+					if (++y >= cy) {
+						y = 0;
+						p = buffer;
+					}
 				}
+
+				/* Write the pair of pixels to the display */
+				write_data3(((c1 >> 4) & 0xFF), (((c1 << 4) & 0xF0)|((c2 >> 8) & 0x0F)), (c2 & 0xFF));
 			}
 
 		#else
 
 			// Although this controller uses packed pixels, we may have to feed it into
 			//  the controller with different packing to the source bitmap
+			// There are 2 pixels per 3 bytes
+
 			#if !GDISP_PACKED_LINES
 				srccx = (srccx + 1) & ~1;
 			#endif
-			pos = srcy*srccx;
-			lg = (srccx - cx)/2*3;
-			p = ((const uint8_t *)buffer) + ((pos+srcx)/2 * 3);
+			pstart = srcy * srccx + srcx;												// The starting pixel number
+			buffer = (const pixel_t)(((const uint8_t *)buffer) + ((pstart>>1) * 3));	// The buffer start position
+			lg = ((srccx-cx)>>1)*3;														// The buffer gap between lines
+			pnum = pstart + srccx*y + x;												// Adjustment for controller craziness
+			p = ((const uint8_t *)buffer) + (((srccx*y + x)>>1)*3);						// Adjustment for controller craziness
 
-			x = srcx;
-			while (1) {
+			while (tuples--) {
 				/* Get a pixel */
-				switch((pos+x)&1) {
-				case 0:		c1 = (((color_t)p[0]) << 4)|(((color_t)p[1])>>4);	break;
-				case 1:		c1 = (((color_t)p[1]&0x0F) << 8)|((color_t)p[1]);	break;
+				switch(pnum++ & 1) {
+				case 0:		c1 = (((color_t)p[0]) << 4)|(((color_t)p[1])>>4);				break;
+				case 1:		c1 = (((color_t)p[1]&0x0F) << 8)|((color_t)p[1]);	p += 3;		break;
 				}
-				if (++x >= endx) {
-					if (++y >= endy) {
-						/* Odd pixel at end */
-						write_data3(0, ((c1 >> 8) & 0x0F), (c1 & 0xFF));
-						break;
-					}
-					x = srcx;
+
+				/* Check for line or buffer wrapping */
+				if (++x >= cx) {
+					x = 0;
 					p += lg;
-					pos += srccx;
+					pnum += srccx - cx;
+					if (++y >= cy) {
+						y = 0;
+						p = (const uint8_t *)buffer;
+						pnum = pstart;
+					}
 				}
+
 				/* Get the next pixel */
-				switch((pos+x)&1) {
-				case 0:		c2 = (((color_t)p[0]) << 4)|(((color_t)p[1])>>4);	break;
-				case 1:		c2 = (((color_t)p[1]&0x0F) << 8)|((color_t)p[1]);	break;
+				switch(pnum++ & 1) {
+				case 0:		c1 = (((color_t)p[0]) << 4)|(((color_t)p[1])>>4);				break;
+				case 1:		c1 = (((color_t)p[1]&0x0F) << 8)|((color_t)p[1]);	p += 3;		break;
 				}
-				write_data3(((c1 >> 4) & 0xFF), (((c1 << 4) & 0xF0)|((c2 >> 8) & 0x0F)), (c2 & 0xFF));
-				if (++x >= endx) {
-					if (++y >= endy)
-						break;
-					x = srcx;
+
+				/* Check for line or buffer wrapping */
+				if (++x >= cx) {
+					x = 0;
 					p += lg;
-					pos += srccx;
+					pnum += srccx - cx;
+					if (++y >= cy) {
+						y = 0;
+						p = (const uint8_t *)buffer;
+						pnum = pstart;
+					}
 				}
+
+				/* Write the pair of pixels to the display */
+				write_data3(((c1 >> 4) & 0xFF), (((c1 << 4) & 0xF0)|((c2 >> 8) & 0x0F)), (c2 & 0xFF));
 			}
 		#endif
+
+		/* All done */
 		release_bus();
 	}
 #endif
@@ -343,8 +403,8 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 	 */
 	color_t gdisp_lld_get_pixel_color(coord_t x, coord_t y) {
 		/* NOT IMPLEMENTED */
-		/* Some board hardware might support this in the future.
-		 * The Olimex board doesn't.
+		/* This controller does not support reading back over the SPI interface.
+		 * Additionally, the Olimex board doesn't even connect the pin.
 		 */
 	}
 #endif
@@ -364,8 +424,11 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 	 */
 	void gdisp_lld_vertical_scroll(coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) {
 		/* NOT IMPLEMENTED */
-		/* The hardware seems capable of doing this.
-		 * It is just really complex so we leave it out for now.
+		/**
+		 * The hardware is capable of doing full width vertical scrolls aligned
+		 * on a 4 line boundary however that is not sufficient to support this routine.
+		 *
+		 * We also can't manually do read/modify scrolling because we can't read in SPI mode.
 		 */
 	}
 #endif
@@ -392,68 +455,91 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 	 */
 	void gdisp_lld_control(unsigned what, void *value) {
 		/* The hardware is capable of supporting...
-		 * 	GDISP_CONTROL_POWER				- not implemented yet
-		 * 	GDISP_CONTROL_ORIENTATION		- not implemented yet
-		 * 	GDISP_CONTROL_BACKLIGHT			- supported (the OlimexSAM7EX256 board.h currently only implements off/on although PWM is supported by the hardware)
+		 * 	GDISP_CONTROL_POWER				- supported
+		 * 	GDISP_CONTROL_ORIENTATION		- supported
+		 * 	GDISP_CONTROL_BACKLIGHT			- supported
 		 * 	GDISP_CONTROL_CONTRAST			- supported
 		 */
 		switch(what) {
-#if 0
-		// NOT IMPLEMENTED YET
 		case GDISP_CONTROL_POWER:
 			if (GDISP.Powermode == (gdisp_powermode_t)value)
 				return;
+			acquire_bus();
 			switch((gdisp_powermode_t)value) {
 				case powerOff:
-					// 	Code here
+					set_backlight(0);									// Turn off the backlight
+					write_cmd(DISOFF);									// Turn off the display
+					write_cmd1(PWRCTR, 0x00);							// Power control - all off
+					write_cmd(SLPIN);									// Sleep in
+					write_cmd(OSCOFF);									// Internal oscillator off
 					break;
 				case powerOn:
-					// 	Code here
-					/* You may need this ---
-					 *	if (GDISP.Powermode != powerSleep)
-					 *		gdisp_lld_init();
-					 */
+					write_cmd(OSCON);									// Internal oscillator on
+					write_cmd(SLPOUT);									// Sleep out
+					write_cmd1(PWRCTR, 0x0F);							// Power control - reference voltage regulator on, circuit voltage follower on, BOOST ON
+					write_cmd2(VOLCTR, GDISP.Contrast, 0x03);			// Voltage control (contrast setting)
+					delayms(100);										// Allow power supply to stabilise
+					write_cmd(DISON);									// Turn on the display
+					write_cmd(PTLOUT);									// Remove sleep window
+					set_backlight(GDISP.Backlight);						// Turn on the backlight
 					break;
 				case powerSleep:
-					/* 	Code here */
+					write_cmd(OSCON);									// Internal oscillator on
+					write_cmd(SLPOUT);									// Sleep out
+					write_cmd1(PWRCTR, 0x0F);							// Power control - reference voltage regulator on, circuit voltage follower on, BOOST ON
+					write_cmd2(VOLCTR, GDISP.Contrast, 0x03);			// Voltage control (contrast setting)
+					delayms(100);										// Allow power supply to stabilise
+					write_cmd(DISON);									// Turn on the display
+					write_cmd2(PTLIN, GDISP_SLEEP_POS/4, (GDISP_SLEEP_POS+GDISP_SLEEP_SIZE)/4);	// Sleep Window
+					set_backlight(GDISP.Backlight);						// Turn on the backlight
+					break;
+				case powerDeepSleep:
+					write_cmd(OSCON);									// Internal oscillator on
+					write_cmd(SLPOUT);									// Sleep out
+					write_cmd1(PWRCTR, 0x0F);							// Power control - reference voltage regulator on, circuit voltage follower on, BOOST ON
+					write_cmd2(VOLCTR, GDISP.Contrast, 0x03);			// Voltage control (contrast setting)
+					delayms(100);										// Allow power supply to stabilise
+					write_cmd(DISON);									// Turn on the display
+					write_cmd2(PTLIN, GDISP_SLEEP_POS/4, (GDISP_SLEEP_POS+GDISP_SLEEP_SIZE)/4);	// Sleep Window
+					set_backlight(0);									// Turn off the backlight
 					break;
 				default:
+					release_bus();
 					return;
 			}
+			release_bus();
 			GDISP.Powermode = (gdisp_powermode_t)value;
 			return;
-#endif
-#if 0
-		// NOT IMPLEMENTED YET
 		case GDISP_CONTROL_ORIENTATION:
 			if (GDISP.Orientation == (gdisp_orientation_t)value)
 				return;
-	//		WriteSpiData(0x48); // no mirror Y (temporary to satisfy Olimex bmptoarray utility)
-	//		WriteSpiData(0xC8); // restore to (mirror x and y, reverse rgb)
+			acquire_bus();
 			switch((gdisp_orientation_t)value) {
 				case GDISP_ROTATE_0:
-					// 	Code here
+					write_cmd3(DATCTL, 0x00, 0x00, 0x02);	// P1: page normal, column normal, scan in column direction
 					GDISP.Height = GDISP_SCREEN_HEIGHT;
 					GDISP.Width = GDISP_SCREEN_WIDTH;
 					break;
 				case GDISP_ROTATE_90:
-					// 	Code here
+					write_cmd3(DATCTL, 0x06, 0x00, 0x02);	// P1: page normal, column reverse, scan in page direction
 					GDISP.Height = GDISP_SCREEN_WIDTH;
 					GDISP.Width = GDISP_SCREEN_HEIGHT;
 					break;
 				case GDISP_ROTATE_180:
-					// 	Code here
+					write_cmd3(DATCTL, 0x03, 0x00, 0x02);	// P1: page reverse, column reverse, scan in column direction
 					GDISP.Height = GDISP_SCREEN_HEIGHT;
 					GDISP.Width = GDISP_SCREEN_WIDTH;
 					break;
 				case GDISP_ROTATE_270:
-					// 	Code here
+					write_cmd3(DATCTL, 0x05, 0x00, 0x02);	// P1: page reverse, column normal, scan in page direction
 					GDISP.Height = GDISP_SCREEN_WIDTH;
 					GDISP.Width = GDISP_SCREEN_HEIGHT;
 					break;
 				default:
+					release_bus();
 					return;
 			}
+			release_bus();
 			#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
 				GDISP.clipx0 = 0;
 				GDISP.clipy0 = 0;
@@ -462,7 +548,6 @@ void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
 			#endif
 			GDISP.Orientation = (gdisp_orientation_t)value;
 			return;
-#endif
 		case GDISP_CONTROL_BACKLIGHT:
 			if ((unsigned)value > 100) value = (void *)100;
 			set_backlight((unsigned)value);
-- 
cgit v1.2.3