text: /*
   * Created by Oleg Gerasimov <ogerasimov@gmail.com>
   * 10.08.2016
   */
   
  #include "gfx.h"
  
  #if GFX_USE_GDISP
  
  #if defined(GDISP_SCREEN_HEIGHT)
  	#warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
  	#undef GISP_SCREEN_HEIGHT
  #endif
  #if defined(GDISP_SCREEN_WIDTH)
  	#warning "GDISP: This low level driver does not support setting a screen size. It is being ignored."
  	#undef GDISP_SCREEN_WIDTH
  #endif
  
  #define GDISP_DRIVER_VMT			GDISPVMT_ILI9225
  #include "gdisp_lld_config.h"
  #include "src/gdisp/gdisp_driver.h"
  
  #include "board_ILI9225.h"
  
  /*===========================================================================*/
  /* Driver local definitions.                                                 */
  /*===========================================================================*/
  
  #ifndef GDISP_SCREEN_HEIGHT
  	#define GDISP_SCREEN_HEIGHT		220
  #endif
  #ifndef GDISP_SCREEN_WIDTH
      #define GDISP_SCREEN_WIDTH		176
  #endif
  #ifndef GDISP_INITIAL_CONTRAST
  	#define GDISP_INITIAL_CONTRAST	100
  #endif
  #ifndef GDISP_INITIAL_BACKLIGHT
  	#define GDISP_INITIAL_BACKLIGHT	100
  #endif
  
  #include "drivers/gdisp/ILI9225/ili9225.h"
  
  // Some common routines and macros
  #define dummy_read(g)				{ volatile uint16_t dummy; dummy = read_data(g); (void) dummy; }
  #define write_reg(g, reg, data)		{ write_cmd(g, reg); write_data(g, data); }
  
  // Serial write data for fast fill.
  #ifndef write_data_repeat
  #define write_data_repeat(g, data, count) { int i; for (i = 0; i < count; ++i) write_data (g, data) }
  #endif
  
  LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
  	// No private area for this controller
  	g->priv = 0;
  
  	// Initialise the board interface
  	init_board(g);
  
  	// Hardware reset
  	setpin_reset(g, TRUE);
  	gfxSleepMilliseconds(20);
  	setpin_reset(g, FALSE);
  	gfxSleepMilliseconds(20);
  
  	// Get the bus for the following initialisation commands
  	acquire_bus(g);
  
  	write_reg(g, ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB
  	write_reg(g, ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC
  	write_reg(g, ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3
  	write_reg(g, ILI9225_POWER_CTRL4, 0x0000); // Set GVDD
  	write_reg(g, ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage
  	gfxSleepMilliseconds(40);
  	// Power-on sequence
  	write_reg(g, ILI9225_POWER_CTRL2, 0x0018); // Set APON,PON,AON,VCI1EN,VC
  	write_reg(g, ILI9225_POWER_CTRL3, 0x6121); // Set BT,DC1,DC2,DC3
  	write_reg(g, ILI9225_POWER_CTRL4, 0x006F); // Set GVDD   /*007F 0088 */
  	write_reg(g, ILI9225_POWER_CTRL5, 0x495F); // Set VCOMH/VCOML voltage
  	write_reg(g, ILI9225_POWER_CTRL1, 0x0F00); // Set SAP,DSTB,STB
  	gfxSleepMilliseconds(10);
  	write_reg(g, ILI9225_POWER_CTRL2, 0x103B); // Set APON,PON,AON,VCI1EN,VC
  	gfxSleepMilliseconds(50);
  	write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x011C); // set the display line number and display direction
  	write_reg(g, ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion
  	write_reg(g, ILI9225_ENTRY_MODE, 0x1030); // set GRAM write direction and BGR=1.
  	write_reg(g, ILI9225_DISP_CTRL1, 0x0000); // Display off
  	write_reg(g, ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch
  	write_reg(g, ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line
  	write_reg(g, ILI9225_INTERFACE_CTRL, 0x0000); // CPU interface
  	write_reg(g, ILI9225_OSC_CTRL, 0x0D01); // Set Osc  /*0e01*/
  	write_reg(g, ILI9225_VCI_RECYCLING, 0x0020); // Set VCI recycling
  	write_reg(g, ILI9225_RAM_ADDR_SET1, 0x0000); // RAM Address
  	write_reg(g, ILI9225_RAM_ADDR_SET2, 0x0000); // RAM Address
  	// Set GRAM area
  	write_reg(g, ILI9225_GATE_SCAN_CTRL, 0x0000);
  	write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL1, 0x00DB);
  	write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL2, 0x0000);
  	write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL3, 0x0000);
  	write_reg(g, ILI9225_PARTIAL_DRIVING_POS1, 0x00DB);
  	write_reg(g, ILI9225_PARTIAL_DRIVING_POS2, 0x0000);
  	write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF);
  	write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000);
  	write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB);
  	write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000);
  
  	// Set GAMMA curve
  	write_reg(g, ILI9225_GAMMA_CTRL1, 0x0000);
  	write_reg(g, ILI9225_GAMMA_CTRL2, 0x0808);
  	write_reg(g, ILI9225_GAMMA_CTRL3, 0x080A);
  	write_reg(g, ILI9225_GAMMA_CTRL4, 0x000A);
  	write_reg(g, ILI9225_GAMMA_CTRL5, 0x0A08);
  	write_reg(g, ILI9225_GAMMA_CTRL6, 0x0808);
  	write_reg(g, ILI9225_GAMMA_CTRL7, 0x0000);
  	write_reg(g, ILI9225_GAMMA_CTRL8, 0x0A00);
  	write_reg(g, ILI9225_GAMMA_CTRL9, 0x0710);
  	write_reg(g, ILI9225_GAMMA_CTRL10, 0x0710);
  
  	write_reg(g, ILI9225_DISP_CTRL1, 0x0012);
  	gfxSleepMilliseconds(50);
  	write_reg(g, ILI9225_DISP_CTRL1, 0x1017);
  
  	release_bus(g);
  
  	// Finish Init
  	post_init_board(g);
  
  	/* Turn on the back-light */
  	set_backlight(g, GDISP_INITIAL_BACKLIGHT);
  
  	/* Initialise the GDISP structure */
  	g->g.Width = GDISP_SCREEN_WIDTH;
  	g->g.Height = GDISP_SCREEN_HEIGHT;
  	g->g.Orientation = GDISP_ROTATE_0;
  	g->g.Powermode = powerOn;
  	g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
  	g->g.Contrast = GDISP_INITIAL_CONTRAST;
  	return TRUE;
  }
  
  static void set_cursor(GDisplay *g) {
  	switch(g->g.Orientation) {
  		default:
  		case GDISP_ROTATE_0:
  		case GDISP_ROTATE_180:
  			write_reg(g, ILI9225_RAM_ADDR_SET1, g->p.x);
  			write_reg(g, ILI9225_RAM_ADDR_SET2, g->p.y);
  			break;
  
  		case GDISP_ROTATE_90:
  		case GDISP_ROTATE_270:
  			write_reg(g, ILI9225_RAM_ADDR_SET1, g->p.y);
  			write_reg(g, ILI9225_RAM_ADDR_SET2, g->p.x);
  			break;
  	}
  	write_cmd (g, ILI9225_GRAM_DATA_REG);
  }
  
  static void set_viewport(GDisplay *g) {
  	switch(g->g.Orientation) {
  		default:
  		case GDISP_ROTATE_0:
  		case GDISP_ROTATE_180:
  			write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR2, g->p.x);
  			write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR1, g->p.x + g->p.cx - 1);
  			write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR2, g->p.y);
  			write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR1, g->p.y + g->p.cy - 1);
  			break;
  
  		case GDISP_ROTATE_90:
  		case GDISP_ROTATE_270:
  			write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR2, g->p.y);
  			write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR1, g->p.y + g->p.cy - 1);
  			write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR2, g->p.x);
  			write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR1, g->p.x + g->p.cx - 1);
  		break;
  	}
  }
  
  
  // temporary variables for saving viewport
  static int svx,svy,svcx,svcy,ws;
  
  #if GDISP_HARDWARE_STREAM_WRITE
  LLDSPEC	void gdisp_lld_write_start(GDisplay *g) {
  	acquire_bus(g);
  	set_viewport(g);
  
  	// save viewport
  	switch(g->g.Orientation) {
  		default:
  		case GDISP_ROTATE_0:
  		case GDISP_ROTATE_180:
  			svx = g->p.x; svy = g->p.y; svcx = g->p.cx; svcy = g->p.cy;
  			break;
  		case GDISP_ROTATE_90:
  		case GDISP_ROTATE_270:
  			svx = g->p.y; svy = g->p.x; svcx = g->p.cy; svcy = g->p.cx;
  			break;
  	}
  	ws++;
  }
  
  LLDSPEC	void gdisp_lld_write_color(GDisplay *g) {
  	LLDCOLOR_TYPE	c;
  	c = gdispColor2Native(g->p.color);
  	write_data(g, c );
  }
  
  LLDSPEC	void gdisp_lld_write_stop(GDisplay *g) {
  	release_bus(g);
  	if (ws)
  		ws--;
  }
  
  LLDSPEC void gdisp_lld_write_pos(GDisplay *g) {
  	set_cursor(g);
  }
  #endif
  
  LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) {
  	set_cursor(g);
  	gdisp_lld_write_color (g);
  
  }
  
  #if GDISP_HARDWARE_FILLS
  LLDSPEC void gdisp_lld_fill_area(GDisplay *g) {
  	LLDCOLOR_TYPE c = gdispColor2Native(g->p.color);
  
  	acquire_bus(g);
  
  	// Set view port if drawing more than 1 line, or write not started
  	if (g->p.cy != 1 || !ws)
  		set_viewport(g);
  
  	set_cursor(g);
  	write_data_repeat (g,c,g->p.cx*g->p.cy);
  
  	// Restore view port if write started and drawed more than 1 line
  	if (g->p.cy != 1 && ws)
  	{
  		write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR2, svx);
  		write_reg(g, ILI9225_HORIZONTAL_WINDOW_ADDR1, svx + svcx - 1);
  		write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR2, svy);
  		write_reg(g, ILI9225_VERTICAL_WINDOW_ADDR1, svy + svcy - 1);
  	}
  	release_bus(g);
  }
  #endif // GDISP_HARDWARE_FILLS
  
  #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
  LLDSPEC void gdisp_lld_control(GDisplay *g) {
  	switch(g->p.x) {
  	case GDISP_CONTROL_POWER:
  		if (g->g.Powermode == (powermode_t)g->p.ptr)
  			return;
  		switch((powermode_t)g->p.ptr) {
  			case powerOff:
  				acquire_bus(g);
  				write_reg(g, ILI9225_DISP_CTRL1, 0x0000); // Display off
  				write_reg(g, ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB
  				write_reg(g, ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC
  				write_reg(g, ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3
  				write_reg(g, ILI9225_POWER_CTRL4, 0x0000); // Set GVDD
  				write_reg(g, ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage
  				release_bus();
  				set_backlight(g, 0);
  				break;
  			case powerSleep:
  			case powerDeepSleep:
  				acquire_bus(g);
  				write_reg(g, ILI9225_DISP_CTRL1, 0x0000); // Display off
  				write_reg(g, ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB
  				write_reg(g, ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC
  				write_reg(g, ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3
  				write_reg(g, ILI9225_POWER_CTRL4, 0x0000); // Set GVDD
  				gfxSleepMilliseconds(40);
  				write_reg(g, ILI9225_POWER_CTRL1, 0x0004); // Set SAP,DSTB,STB
  				release_bus(g);
  				set_backlight(g, 0);
  				break;
  			case powerOn:
  				acquire_bus(g);
  				write_reg(g, ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB
  				write_reg(g, ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC
  				write_reg(g, ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3
  				write_reg(g, ILI9225_POWER_CTRL4, 0x0000); // Set GVDD
  				write_reg(g, ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage
  				gfxSleepMilliseconds(40);
  				// Power-on sequence
  				write_reg(g, ILI9225_POWER_CTRL2, 0x0018); // Set APON,PON,AON,VCI1EN,VC
  				write_reg(g, ILI9225_POWER_CTRL3, 0x6121); // Set BT,DC1,DC2,DC3
  				write_reg(g, ILI9225_POWER_CTRL4, 0x006F); // Set GVDD   /*007F 0088 */
  				write_reg(g, ILI9225_POWER_CTRL5, 0x495F); // Set VCOMH/VCOML voltage
  				write_reg(g, ILI9225_POWER_CTRL1, 0x0F00); // Set SAP,DSTB,STB
  				gfxSleepMilliseconds(10);
  				write_reg(g, ILI9225_POWER_CTRL2, 0x103B); // Set APON,PON,AON,VCI1EN,VC
  				gfxSleepMilliseconds(50);
  				write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x011C); // set the display line number and display direction
  				write_reg(g, ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion
  				write_reg(g, ILI9225_ENTRY_MODE, 0x1030); // set GRAM write direction and BGR=1.
  				write_reg(g, ILI9225_DISP_CTRL1, 0x0000); // Display off
  				write_reg(g, ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch
  				write_reg(g, ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line
  				write_reg(g, ILI9225_INTERFACE_CTRL, 0x0000); // CPU interface
  				write_reg(g, ILI9225_OSC_CTRL, 0x0D01); // Set Osc  /*0e01*/
  				write_reg(g, ILI9225_VCI_RECYCLING, 0x0020); // Set VCI recycling
  				// Set GRAM area
  				write_reg(g, ILI9225_GATE_SCAN_CTRL, 0x0000);
  				write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL1, 0x00DB);
  				write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL2, 0x0000);
  				write_reg(g, ILI9225_VERTICAL_SCROLL_CTRL3, 0x0000);
  				write_reg(g, ILI9225_PARTIAL_DRIVING_POS1, 0x00DB);
  				write_reg(g, ILI9225_PARTIAL_DRIVING_POS2, 0x0000);
  				write_reg(g, ILI9225_DISP_CTRL1, 0x0012);
  				gfxSleepMilliseconds(50);
  				write_reg(g, ILI9225_DISP_CTRL1, 0x1017);
  				release_bus(g);
  				set_backlight(g, g->g.Backlight);
  				break;
  			default:
  				return;
  		}
  		g->g.Powermode = (powermode_t)g->p.ptr;
  		return;
  
  	case GDISP_CONTROL_ORIENTATION:
  		if (g->g.Orientation == (orientation_t)g->p.ptr)
  			return;
  		switch((orientation_t)g->p.ptr) {
  			case GDISP_ROTATE_0: // correct
  				acquire_bus(g);
  				write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x011C);
  				write_reg(g, ILI9225_ENTRY_MODE, 0x1030);
  				g->g.Height = GDISP_SCREEN_HEIGHT;
  				g->g.Width = GDISP_SCREEN_WIDTH;
  				release_bus(g);
  				break;
  			case GDISP_ROTATE_90:
  				acquire_bus(g);
  				write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x031C);
  				write_reg(g, ILI9225_ENTRY_MODE, 0x1038);
  				g->g.Height = GDISP_SCREEN_WIDTH;
  				g->g.Width = GDISP_SCREEN_HEIGHT;
  				release_bus(g);
  				break;
  			case GDISP_ROTATE_180:
  				acquire_bus(g);
  				write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x021C);
  				write_reg(g, ILI9225_ENTRY_MODE, 0x1030);
  				g->g.Height = GDISP_SCREEN_HEIGHT;
  				g->g.Width = GDISP_SCREEN_WIDTH;
  				release_bus(g);
  				break;
  			case GDISP_ROTATE_270:
  				acquire_bus(g);
  				write_reg(g, ILI9225_DRIVER_OUTPUT_CTRL, 0x001C);
  				write_reg(g, ILI9225_ENTRY_MODE, 0x1038);
  				g->g.Height = GDISP_SCREEN_WIDTH;
  				g->g.Width = GDISP_SCREEN_HEIGHT;
  				release_bus(g);
  				break;
  			default:
  				return;
  		}
  		g->g.Orientation = (orientation_t)g->p.ptr;
  		return;
  	case GDISP_CONTROL_BACKLIGHT:
  		if ((unsigned)g->p.ptr > 100)
  			g->p.ptr = (void *)100;
  		set_backlight(g, (unsigned)g->p.ptr);
  		g->g.Backlight = (unsigned)g->p.ptr;
  		return;
  	default:
  		return;
  	}
  }
  #endif
  
  #endif /* GFX_USE_GDISP */
  