From f4b9f0bcfeb5a71cf5b46073cc30f6e496c23dc2 Mon Sep 17 00:00:00 2001
From: inmarket <andrewh@inmarket.com.au>
Date: Mon, 21 Oct 2013 17:12:48 +1000
Subject: Convert SSD1306 driver to new driver format. This driver requires the
 new flush operation.

---
 drivers/gdisp/SSD1306/board_SSD1306_i2c.h          | 121 ++++
 drivers/gdisp/SSD1306/board_SSD1306_spi.h          | 122 ++++
 drivers/gdisp/SSD1306/board_SSD1306_template.h     | 116 ++++
 drivers/gdisp/SSD1306/gdisp_lld.c                  | 729 +++++----------------
 drivers/gdisp/SSD1306/gdisp_lld.mk                 |   5 +-
 .../gdisp/SSD1306/gdisp_lld_board_example_i2c.h    | 137 ----
 .../gdisp/SSD1306/gdisp_lld_board_example_spi.h    | 139 ----
 drivers/gdisp/SSD1306/gdisp_lld_board_template.h   |  74 ---
 drivers/gdisp/SSD1306/gdisp_lld_config.h           |  14 +-
 9 files changed, 520 insertions(+), 937 deletions(-)
 create mode 100644 drivers/gdisp/SSD1306/board_SSD1306_i2c.h
 create mode 100644 drivers/gdisp/SSD1306/board_SSD1306_spi.h
 create mode 100644 drivers/gdisp/SSD1306/board_SSD1306_template.h
 delete mode 100644 drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h
 delete mode 100644 drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h
 delete mode 100644 drivers/gdisp/SSD1306/gdisp_lld_board_template.h

diff --git a/drivers/gdisp/SSD1306/board_SSD1306_i2c.h b/drivers/gdisp/SSD1306/board_SSD1306_i2c.h
new file mode 100644
index 00000000..c89562e0
--- /dev/null
+++ b/drivers/gdisp/SSD1306/board_SSD1306_i2c.h
@@ -0,0 +1,121 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ *              http://ugfx.org/license.html
+ */
+
+/**
+ * @file    drivers/gdisp/SSD1306/board_SSD1306_i2c.h
+ * @brief   GDISP Graphic Driver subsystem board interface for the SSD1306 display.
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+#define GDISP_BUS_MAX_TRANSFER_SIZE		64
+
+// For a multiple display configuration we would put all this in a structure and then
+//	set g->board to that structure.
+#define SSD1306_RESET_PORT		GPIOB
+#define SSD1306_RESET_PIN		5
+
+/**
+ * The default slave address is 0x3D, (talking about
+ * only the real address part here) and the slave
+ * address can be changed to 0x3C by soldering the
+ * SA0 pads on the bottom side of the module.
+ *
+ * b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0
+ * --------------------------------------
+ * 0  | 1  | 1  | 1  | 1  | 0  |SA0 | R/W
+ */
+#define SSD1306_I2C_ADDRESS   	0x3D
+#define SSD1306_SDA_PORT		GPIOB
+#define SSD1306_SDA_PIN			7
+#define SSD1306_SCL_PORT		GPIOB
+#define SSD1306_SCL_PIN			6
+#define SET_RST					palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+#define CLR_RST					palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+
+// I2C configuration structure.
+static I2CConfig i2cconfig;
+
+static inline void init_board(GDisplay *g) {
+
+	// As we are not using multiple displays we set g->board to NULL as we don't use it.
+	g->board = 0;
+
+	switch(g->controllerdisplay) {
+	case 0:											// Set up for Display 0
+		// RESET pin.
+		palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+
+
+		/*
+		 * Initializes the I2C driver 1. The I2C1 signals are routed as follows:
+		 * PB6 - SCL.
+		 * PB7 - SDA.
+		 * Timing value comes from ST I2C config tool (xls):
+		 * 0x00901D2B;		// 100kHz Standard Mode
+		 * 0x00300444;		// 100kHz Fast Mode
+		 * 0x0030020A;		// 400kHz Fast Mode
+		 * 0x00100002;		// 800kHz Fast Mode +
+		 */
+		i2cconfig.timingr = 0x00100002;		// 800kHz Fast Mode+
+		i2cInit();
+		palSetPadMode(SSD1306_SCL_PORT, SSD1306_SCL_PIN, PAL_MODE_ALTERNATE(1));
+		palSetPadMode(SSD1306_SDA_PORT, SSD1306_SDA_PIN, PAL_MODE_ALTERNATE(1));
+		break;
+	}
+}
+
+static inline void post_init_board(GDisplay *g) {
+	(void) g;
+}
+
+static inline void setpin_reset(GDisplay *g, bool_t state) {
+	(void) g;
+	if(state)
+		CLR_RST
+	else
+		SET_RST
+}
+
+static inline void acquire_bus(GDisplay *g) {
+	(void) g;
+	i2cAcquireBus(&I2CD1);
+}
+
+static inline void release_bus(GDisplay *g) {
+	(void) g;
+	i2cReleaseBus(&I2CD1);
+}
+
+static inline void write_cmd(GDisplay *g, uint8_t cmd) {
+	uint8_t command[2];
+	(void) g;
+
+	command[0] = 0x00;		// Co = 0, D/C = 0
+	command[1] = cmd;
+
+	i2cStart(&I2CD1, &i2cconfig);
+	i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, 2, NULL, 0, MS2ST(10));
+	i2cStop(&I2CD1);
+}
+
+static inline void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
+	uint8_t command[1];
+	(void) g;
+
+	command[0] = 0x40; 		// Co = 0, D/C = 1
+
+	i2cStart(&I2CD1, &i2cconfig);
+	i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, 1, NULL, 0, MS2ST(10));
+	i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, data, NULL, length, MS2ST(10));
+	i2cStop(&I2CD1);
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
+
+
diff --git a/drivers/gdisp/SSD1306/board_SSD1306_spi.h b/drivers/gdisp/SSD1306/board_SSD1306_spi.h
new file mode 100644
index 00000000..e206a517
--- /dev/null
+++ b/drivers/gdisp/SSD1306/board_SSD1306_spi.h
@@ -0,0 +1,122 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ *              http://ugfx.org/license.html
+ */
+
+/**
+ * @file    drivers/gdisp/SSD1306/board_SSD1306_spi.h
+ * @brief   GDISP Graphic Driver subsystem board interface for the SSD1306 display.
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+#define GDISP_BUS_MAX_TRANSFER_SIZE		64
+
+// For a multiple display configuration we would put all this in a structure and then
+//	set g->board to that structure.
+#define SSD1306_RESET_PORT		GPIOB
+#define SSD1306_RESET_PIN		5
+#define SSD1306_MISO_PORT		GPIOB
+#define SSD1306_MISO_PIN		8
+#define SSD1306_MOSI_PORT		GPIOB
+#define SSD1306_MOSI_PIN		7
+#define SSD1306_SCK_PORT		GPIOB
+#define SSD1306_SCK_PIN			6
+#define SSD1306_CS_PORT			GPIOB
+#define SSD1306_CS_PIN			5
+#define SET_RST					palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+#define CLR_RST					palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+
+/*
+ * SPI1 configuration structure.
+ * Speed 42MHz, CPHA=0, CPOL=0, 8bits frames, MSb transmitted first.
+ * The slave select line is the pin 4 on the port GPIOA.
+ */
+static const SPIConfig spi1config = {
+	NULL,
+	/* HW dependent part.*/
+	SSD1306_MISO_PORT,
+	SSD1306_MISO_PIN,
+	0
+	//SPI_CR1_BR_0
+};
+
+static inline void init_board(GDisplay *g) {
+
+	// As we are not using multiple displays we set g->board to NULL as we don't use it.
+	g->board = 0;
+
+	switch(g->controllerdisplay) {
+	case 0:											// Set up for Display 0
+		// RESET pin.
+		palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+
+		spiInit();
+		palSetPadMode(SSD1306_MISO_PORT, SSD1306_MISO_PIN, 	PAL_MODE_ALTERNATE(1)|
+															PAL_STM32_OSPEED_HIGHEST);
+		palSetPadMode(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN, 	PAL_MODE_ALTERNATE(1)|
+															PAL_STM32_OSPEED_HIGHEST);
+		palSetPadMode(SSD1306_SCK_PORT,  SSD1306_SCK_PIN,  	PAL_MODE_ALTERNATE(1)|
+															PAL_STM32_OSPEED_HIGHEST);
+		palSetPad(SSD1306_CS_PORT, SSD1306_CS_PIN);
+		palSetPadMode(SSD1306_CS_PORT,   SSD1306_CS_PIN,   	PAL_MODE_ALTERNATE(1)|
+															PAL_STM32_OSPEED_HIGHEST);
+		break;
+	}
+}
+
+static inline void post_init_board(GDisplay *g) {
+	(void) g;
+}
+
+static inline void setpin_reset(GDisplay *g, bool_t state) {
+	(void) g;
+	if(state)
+		CLR_RST
+	else
+		SET_RST
+}
+
+static inline void acquire_bus(GDisplay *g) {
+	(void) g;
+	spiAcquireBus(&SPID1);
+}
+
+static inline void release_bus(GDisplay *g) {
+	(void) g;
+	spiReleaseBus(&SPID1);
+}
+
+static inline void write_cmd(GDisplay *g, uint8_t cmd) {
+	uint8_t command[2];
+
+	command[0] = 0x00;		// Co = 0, D/C = 0
+	command[1] = cmd;
+
+	spiStart(&SPID1, &spi1config);
+	spiSelect(&SPID1);
+	spiStartSend(&SPID1, 2, command);
+	spiUnselect(&SPID1);
+	spiStop(&SPID1);
+}
+
+static inline void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
+	uint8_t command[1];
+	(void) g;
+
+	command[0] = 0x40; 		// Co = 0, D/C = 1
+
+	spiStart(&SPID1, &spi1config);
+	spiSelect(&SPID1);
+	spiStartSend(&SPID1, 1, command);
+	spiStartSend(&SPID1, length, data);
+	spiUnselect(&SPID1);
+	spiStop(&SPID1);
+}
+
+
+#endif /* _GDISP_LLD_BOARD_H */
+
diff --git a/drivers/gdisp/SSD1306/board_SSD1306_template.h b/drivers/gdisp/SSD1306/board_SSD1306_template.h
new file mode 100644
index 00000000..ec7f44f5
--- /dev/null
+++ b/drivers/gdisp/SSD1306/board_SSD1306_template.h
@@ -0,0 +1,116 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ *              http://ugfx.org/license.html
+ */
+
+/**
+ * @file    drivers/gdisp/SSD1306/board_SSD1306_template.h
+ * @brief   GDISP Graphic Driver subsystem board interface for the SSD1306 display.
+ *
+ * @addtogroup GDISP
+ * @{
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+/**
+ * @brief   How many bytes to write in one operation when updating the display.
+ * @note	The screen size (in bytes) must evenly divide by this number.
+ *
+ * @notapi
+ */
+#define GDISP_BUS_MAX_TRANSFER_SIZE		64
+
+/**
+ * @brief   Initialise the board for the display.
+ *
+ * @param[in] g			The GDisplay structure
+ *
+ * @note	Set the g->board member to whatever is appropriate. For multiple
+ * 			displays this might be a pointer to the appropriate register set.
+ *
+ * @notapi
+ */
+static inline void init_board(GDisplay *g) {
+	(void) g;
+}
+
+/**
+ * @brief   After the initialisation.
+ *
+ * @param[in] g			The GDisplay structure
+ *
+ * @notapi
+ */
+static inline void post_init_board(GDisplay *g) {
+	(void) g;
+}
+
+/**
+ * @brief   Set or clear the lcd reset pin.
+ *
+ * @param[in] g			The GDisplay structure
+ * @param[in] state		TRUE = lcd in reset, FALSE = normal operation
+ *
+ * @notapi
+ */
+static inline void setpin_reset(GDisplay *g, bool_t state) {
+	(void) g;
+	(void) state;
+}
+
+/**
+ * @brief   Take exclusive control of the bus
+ *
+ * @param[in] g				The GDisplay structure
+ *
+ * @notapi
+ */
+static inline void acquire_bus(GDisplay *g) {
+	(void) g;
+}
+
+/**
+ * @brief   Release exclusive control of the bus
+ *
+ * @param[in] g				The GDisplay structure
+ *
+ * @notapi
+ */
+static inline void release_bus(GDisplay *g) {
+	(void) g;
+}
+
+/**
+ * @brief   Send a command to the controller.
+ *
+ * @param[in] g				The GDisplay structure
+ * @param[in] cmd			The command to send *
+ *
+ * @notapi
+ */
+static inline void write_cmd(GDisplay *g, uint8_t cmd) {
+	(void) g;
+	(void) cmd;
+}
+
+/**
+ * @brief   Send data to the lcd.
+ *
+ * @param[in] g				The GDisplay structure
+ * @param[in] data			The data to send
+ *
+ * @notapi
+ */
+static inline void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
+	(void) g;
+	(void) data;
+	(void) length;
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld.c b/drivers/gdisp/SSD1306/gdisp_lld.c
index 5f337b54..36ba9686 100644
--- a/drivers/gdisp/SSD1306/gdisp_lld.c
+++ b/drivers/gdisp/SSD1306/gdisp_lld.c
@@ -5,642 +5,219 @@
  *              http://ugfx.org/license.html
  */
 
-#include "gfx.h"
+/**
+ * @file    drivers/gdisp/SSD1306/gdisp_lld.c
+ * @brief   GDISP Graphics Driver subsystem low level driver source for the SSD1306 display.
+ */
 
-#include "SSD1306.h"
+#include "gfx.h"
 
 #if GFX_USE_GDISP || defined(__DOXYGEN__)
 
-/* Include the emulation code for things we don't support */
-#include "gdisp/lld/emulation.c"
+#define GDISP_DRIVER_VMT			GDISPVMT_SSD1306
+#include "../drivers/gdisp/SSD1306/gdisp_lld_config.h"
+#include "gdisp/lld/gdisp_lld.h"
+
+#include "board_SSD1306.h"
 
 /*===========================================================================*/
 /* Driver local definitions.                                                 */
 /*===========================================================================*/
 
 #ifndef GDISP_SCREEN_HEIGHT
-	#define GDISP_SCREEN_HEIGHT		64
+	#define GDISP_SCREEN_HEIGHT		64		// This controller should support 32 (untested) or 64
 #endif
 #ifndef GDISP_SCREEN_WIDTH
 	#define GDISP_SCREEN_WIDTH		128
 #endif
+#ifndef GDISP_INITIAL_CONTRAST
+	#define GDISP_INITIAL_CONTRAST	100
+#endif
+#ifndef GDISP_INITIAL_BACKLIGHT
+	#define GDISP_INITIAL_BACKLIGHT	100
+#endif
 
-#define GDISP_INITIAL_CONTRAST		0xFF
+#define GDISP_FLG_NEEDFLUSH			(GDISP_FLG_DRIVER<<0)
+
+#include "SSD1306.h"
 
 /*===========================================================================*/
 /* Driver local functions.                                                   */
 /*===========================================================================*/
 
-// Include wiring specific header
-#include "gdisp_lld_board_example_i2c.h"
+// Some common routines and macros
+#define RAM(g)							((uint8_t *)g->priv)
+#define write_cmd2(g, cmd1, cmd2)		{ write_cmd(g, cmd1); write_cmd(g, cmd2); }
+#define write_cmd3(g, cmd1, cmd2, cmd3)	{ write_cmd(g, cmd1); write_cmd(g, cmd2); write_cmd(g, cmd3); }
 
 // Some common routines and macros
 #define delay(us)					gfxSleepMicroseconds(us)
 #define delayms(ms)					gfxSleepMilliseconds(ms)
 
-// The memory buffer for the display
-static uint8_t gdisp_buffer[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH / 8];
-
-/** Set the display to normal or inverse.
- *  @param[in] value 0 for normal mode, or 1 for inverse mode.
- *  @notapi
- */
-static void invert_display(uint8_t i) {
-	write_cmd(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
-}
-
-/** Turn the whole display off.
- *	Sends the display to sleep, but leaves RAM intact.
- *	@notapi
- */
-static void display_off(){
-	write_cmd(SSD1306_DISPLAYOFF);
-}
-
-/** Turn the whole display on.
- * 	Wakes up this display following a sleep() call.
- *	@notapi
- */
-static void display_on()	{
-	write_cmd(SSD1306_DISPLAYON);
-}
-
-/** Set the vertical shift by COM.
- * 	@param[in] value The number of rows to shift, from 0 - 63.
- *	@notapi
-*/
-static void set_display_offset(unsigned char value) {
-	write_cmd(SSD1306_SETDISPLAYOFFSET);
-	write_cmd(value & 0x3F);
-}
-
-/** Set the display contrast.
- *  @param[in] value The contrast, from 1 to 256.
- *	@notapi
- */
-static void set_contrast(unsigned char value) {
-	write_cmd(SSD1306_SETCONTRAST);
-	write_cmd(value);
-}
-
-/** Set the display start line.  This is the line at which the display will start rendering.
- *  @param[in] value A value from 0 to 63 denoting the line to start at.
- *	@notapi
- */
-static void set_display_start_line(unsigned char value) {
-	write_cmd(SSD1306_SETSTARTLINE | value);
-}
-
-/** Set the segment remap state.  This allows the module to be addressed as if flipped horizontally.
- * NOTE: Changing this setting has no effect on data already in the module's GDDRAM.
- * @param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped).
- *	@notapi
- */
-static void set_segment_remap(unsigned char value) {
-	write_cmd(value ? SSD1306_SEGREMAP+1 : SSD1306_SEGREMAP);
-}
-
-/** Set the multiplex ratio.
- *  @param[in] value MUX will be set to (value+1). Valid values range from 15 to 63 - MUX 16 to 64.
- *	@notapi
- */
-static void set_multiplex_ratio(unsigned char value) {
-	write_cmd(SSD1306_SETMULTIPLEX);
-	write_cmd(value & 0x3F);
-}
-
-/** Set COM output scan direction.  If the display is active, this will immediately vertically
- * flip the display.
- * @param[in] value 0 = Scan from COM0 (default), 1 = reversed (scan from COM[N-1]).
- *	@notapi
- */
-static void set_com_output_scan_direction(unsigned char value) {
-	write_cmd(value ? SSD1306_COMSCANDEC : SSD1306_COMSCANINC);
-}
-
-static void set_com_pins_hardware_configuration(unsigned char sequential, unsigned char lr_remap)	{
-	write_cmd(SSD1306_SETCOMPINS);
-	write_cmd(0x02 | ((sequential & 1) << 4) | ((lr_remap & 1) << 5));
-}
-
-/** Flip display content horizontally.
- * 	NOTE: This only flips display content, but doesn't turn the char writing around.
- * 		  You have to unmirror everything manually.
- * 	@param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped).
- *	@notapi
- */
-static void flip_display(unsigned char enable) {
-	if( enable && GDISP.Orientation == GDISP_ROTATE_0) {
-		set_com_output_scan_direction(0);
-		set_segment_remap(0);
-		GDISP.Orientation = GDISP_ROTATE_0;
-	}
-	if( !enable && GDISP.Orientation == GDISP_ROTATE_180) {
-		set_com_output_scan_direction(1);
-		set_segment_remap(1);
-		GDISP.Orientation = GDISP_ROTATE_180;
-	}
-	else
-		return;
-}
-
-/** Perform a "no operation".
- *	@notapi
- */
-static void nop() {
-	write_cmd(0xE3);
-}
-
-/** Page Addressing Mode: Set the column start address register for
- * 	page addressing mode.
- *	@param[in] address The address (full byte).
- *	@notapi
- */
-static void set_start_address_pam(unsigned char address)
-{
-	// "Set Lower Column Start Address for Page Addressing Mode"
-	write_cmd(address & 0x0F);
-
-	// "Set Higher Column Start Address for Page Addressing Mode"
-	write_cmd((address << 4) & 0x0F);
-}
-
-/** Set memory addressing mode to the given value.
- *	@param[in] mode 0 for Horizontal addressing mode,\n 1 for Vertical addressing mode,\n or 2 for Page addressing mode (PAM).  2 is the default.
- *	@notapi
- */
-static void set_memory_addressing_mode(unsigned char mode)
-{
-	write_cmd(SSD1306_MEMORYMODE);
-	write_cmd(mode & 0x3);
-}
-
-/** Set column address range for horizontal/vertical addressing mode.
- *	 @param[in] start Column start address, 0 - 127.
- *	 @param[in] end Column end address, 0 - 127.
- *	@notapi
- */
-static void set_column_address_hvam(unsigned char start, unsigned char end)
-{
-	write_cmd(SSD1306_HV_COLUMN_ADDRESS);
-	write_cmd(start & 0x7F);
-	write_cmd(end & 0x7F);
-}
-
-/** Set page start and end address for horizontal/vertical addressing mode.
- * 	@param[in] start The start page, 0 - 7.
- *	@param[in] end The end page, 0 - 7.
- *	@notapi
- */
-static void set_page_address_hvam(unsigned char start, unsigned char end)
-{
-	write_cmd(SSD1306_HV_PAGE_ADDRESS);
-	write_cmd(start & 0x07);
-	write_cmd(end & 0x07);
-}
-
-/** Set the GDDRAM page start address for page addressing mode.
- *	@param[in] address The start page, 0 - 7.
- *	@notapi
- */
-static void set_page_start_pam(unsigned char address)
-{
-	write_cmd(SSD1306_PAM_PAGE_START | (address & 0x07));
-}
-
-/** Set the display clock divide ratio and the oscillator frequency.
- * 	@param[in] ratio The divide ratio, default is 0.
- *	@param[in] frequency The oscillator frequency, 0 - 127. Default is 8.
- *	@notapi
- */
-static void set_display_clock_ratio_and_frequency(unsigned char ratio, unsigned char frequency)
-{
-	write_cmd(SSD1306_SETDISPLAYCLOCKDIV);
-	write_cmd((ratio & 0x0F) | ((frequency & 0x0F) << 4));
-}
-
-/** Set the precharge period.
- * 	@param[in] phase1 Phase 1 period in DCLK clocks.  1 - 15, default is 2.
- *	@param[in] phase2 Phase 2 period in DCLK clocks.  1 - 15, default is 2.
- *	@notapi
- */
-static void set_precharge_period(unsigned char phase1, unsigned char phase2)
-{
-	write_cmd(SSD1306_SETPRECHARGE);
-	write_cmd((phase1 & 0x0F) | ((phase2 & 0x0F ) << 4));
-}
-
-/** Set the Vcomh deselect level.
- *	@param[in] level @p 0 = 0.65 x Vcc, @p 1 = 0.77 x Vcc (default), @p 2 = 0.83 x Vcc.
- *	@notapi
- */
-static void set_vcomh_deselect_level(unsigned char level)
-{
-	write_cmd(SSD1306_SETVCOMDETECT);
-	write_cmd((level & 0x03) << 4);
-}
-
-/** Enable/disable charge pump.
- *	@param[in] enable 0 to disable, 1 to enable the internal charge pump.
- *	@notapi
- */
-static void set_charge_pump(unsigned char enable)
-{
-	write_cmd(SSD1306_ENABLE_CHARGE_PUMP);
-	write_cmd(enable ? 0x14 : 0x10);
-}
-
-/*===========================================================================*/
-/* Driver interrupt handlers.                                                */
-/*===========================================================================*/
-
 /*===========================================================================*/
 /* Driver exported functions.                                                */
 /*===========================================================================*/
 
-/* ---- Required Routines ---- */
-/*
-	The following 2 routines are required.
-	All other routines are optional.
-*/
-
 /**
- * @brief   Low level GDISP driver initialization.
- *
- * @notapi
- */
-bool_t gdisp_lld_init(void) {
-	// Initialize your display
-	init_board();
-
-	// Hardware reset.
-	setpin_reset(TRUE);
-	delayms(1);
-	setpin_reset(FALSE);
-	delayms(10);
-	setpin_reset(TRUE);
-
-	// Get the bus for the following initialization commands.
-	acquire_bus();
-
-	display_off();
-	set_display_clock_ratio_and_frequency(0, 8);
-	#if GDISP_SCREEN_HEIGHT == 64
-		set_multiplex_ratio(0x3F);			// 1/64 duty
-	#endif
-	#if GDISP_SCREEN_HEIGHT == 32
-		set_multiplex_ratio(0x1F); 			// 1/32 duty
-	#endif
-	set_precharge_period(0xF, 0x01);		//
-	set_display_offset(0);					//
-	set_display_start_line(0);					//
-	set_charge_pump(1);						// Enable internal charge pump.
-	set_memory_addressing_mode(0); 			// horizontal addressing mode; across then down //act like ks0108 (horizontal addressing mode)
-	set_segment_remap(1);					//
-	set_com_output_scan_direction(1);		//
+ * As this controller can't update on a pixel boundary we need to maintain the
+ * the entire display surface in memory so that we can do the necessary bit
+ * operations. Fortunately it is a small display in monochrome.
+ * 64 * 128 / 8 = 1024 bytes.
+ */
+
+LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
+	// The private area is the display surface.
+	g->priv = gfxAlloc(GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH / 8);
+
+	// Initialise the board interface
+	init_board(g);
+
+	// Hardware reset
+	setpin_reset(g, TRUE);
+	gfxSleepMilliseconds(20);
+	setpin_reset(g, FALSE);
+	gfxSleepMilliseconds(20);
+
+	acquire_bus(g);
+
+	write_cmd(g, SSD1306_DISPLAYOFF);
+	write_cmd2(g, SSD1306_SETDISPLAYCLOCKDIV, 0x80);
+	write_cmd2(g, SSD1306_SETMULTIPLEX, GDISP_SCREEN_HEIGHT-1);
+	write_cmd2(g, SSD1306_SETPRECHARGE, 0x1F);
+	write_cmd2(g, SSD1306_SETDISPLAYOFFSET, 0);
+	write_cmd(g, SSD1306_SETSTARTLINE | 0);
+	write_cmd2(g, SSD1306_ENABLE_CHARGE_PUMP, 0x14);
+	write_cmd2(g, SSD1306_MEMORYMODE, 0);
+	write_cmd(g, SSD1306_SEGREMAP+1);
+	write_cmd(g, SSD1306_COMSCANDEC);
 	#if GDISP_SCREEN_HEIGHT == 64
-		set_com_pins_hardware_configuration(1, 0);
-	#endif
-	#if GDISP_SCREEN_HEIGHT == 32
-		set_com_pins_hardware_configuration(0, 1);
-	#endif
-	set_contrast(GDISP_INITIAL_CONTRAST);	// Set initial contrast.
-	set_vcomh_deselect_level(1);			//
-	display_on();							// Turn on OLED panel.
-	invert_display(0);						// Disable Inversion of display.
-	set_column_address_hvam(0, 127);		//
-	set_page_address_hvam(0, 7);			//
-
-	release_bus();
-
-	gdisp_lld_display();
-
-    // Initialize the GDISP structure
-	GDISP.Width = GDISP_SCREEN_WIDTH;
-	GDISP.Height = GDISP_SCREEN_HEIGHT;
-	GDISP.Orientation = GDISP_ROTATE_0;
-	GDISP.Powermode = powerOn;
-	GDISP.Contrast = GDISP_INITIAL_CONTRAST;
-	#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
-		GDISP.clipx0 = 0;
-		GDISP.clipy0 = 0;
-		GDISP.clipx1 = GDISP.Width;
-		GDISP.clipy1 = GDISP.Height;
+		write_cmd2(g, SSD1306_SETCOMPINS, 0x12);
+	#else
+		write_cmd2(g, SSD1306_SETCOMPINS, 0x22);
 	#endif
+	write_cmd2(g, SSD1306_SETCONTRAST, (uint8_t)(GDISP_INITIAL_CONTRAST*256/101));	// Set initial contrast.
+	write_cmd2(g, SSD1306_SETVCOMDETECT, 0x10);
+	write_cmd(g, SSD1306_DISPLAYON);
+	write_cmd(g, SSD1306_NORMALDISPLAY);
+	write_cmd3(g, SSD1306_HV_COLUMN_ADDRESS, 0, GDISP_SCREEN_WIDTH-1);
+	write_cmd3(g, SSD1306_HV_PAGE_ADDRESS, 0, GDISP_SCREEN_HEIGHT/8-1);
+
+    // Finish Init
+    post_init_board(g);
+
+ 	// Release the bus
+	release_bus(g);
+
+	/* 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;
 }
 
-/**
- * @brief   Draws a pixel on the display.
- *
- * @param[in] x        X location of the pixel
- * @param[in] y        Y location of the pixel
- * @param[in] color    The color of the pixel
- *
- * @notapi
- */
-void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
-	#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
-		if (x < GDISP.clipx0 || y < GDISP.clipy0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
-	#endif
-
-	if (color == SSD1306_WHITE)
-		gdisp_buffer[x+ (y/8)*GDISP_SCREEN_WIDTH] |=  (1<<y%8);
-	else
-		gdisp_buffer[x+ (y/8)*GDISP_SCREEN_WIDTH] &= ~(1<<y%8);
-}
-
-void gdisp_lld_display() {
-	set_display_start_line(0);
+#if GDISP_HARDWARE_FLUSH
+	LLDSPEC void gdisp_lld_flush(GDisplay *g) {
+		unsigned	i;
 
-	/* We're sending half a line in one X-mission.*/
-	uint8_t command[GDISP_SCREEN_WIDTH/2],
-			cmdLength = sizeof(command)/sizeof(command[0]),
-			parts = GDISP_SCREEN_WIDTH/cmdLength;
-
-	for(int i=0; i<GDISP_SCREEN_HEIGHT/8; i++){
-		for(int j = 0; j<parts; j++){
-			memmove(command, &gdisp_buffer[i*GDISP_SCREEN_WIDTH + j*cmdLength], cmdLength);
-			write_data(command, cmdLength);
-		}
-	}
-}
-
-/* ---- Optional Routines ---- */
-/*
-	All the below routines are optional.
-	Defining them will increase speed but everything
-	will work if they are not defined.
-	If you are not using a routine - turn it off using
-	the appropriate GDISP_HARDWARE_XXXX macro.
-	Don't bother coding for obvious similar routines if
-	there is no performance penalty as the emulation software
-	makes a good job of using similar routines.
-		eg. If gfillarea() is defined there is little
-			point in defining clear() unless the
-			performance bonus is significant.
-	For good performance it is suggested to implement
-		fillarea() and blitarea().
-*/
-
-#if (GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL) || defined(__DOXYGEN__)
-	/**
-	 * @brief   Scroll vertically a section of the screen.
-	 * @note    Optional.
-	 * @note    If x,y + cx,cy is off the screen, the result is undefined.
-	 * @note    If lines is >= cy, it is equivalent to a area fill with bgcolor.
-	 *
-	 * @param[in] x, y     The start of the area to be scrolled
-	 * @param[in] cx, cy   The size of the area to be scrolled
-	 * @param[in] lines    The number of lines to scroll (Can be positive or negative)
-	 * @param[in] bgcolor  The color to fill the newly exposed area.
-	 *
-	 * @notapi
-	 */
-	void gdisp_lld_vertical_scroll(coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) {
-		#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
-			if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; }
-			if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; }
-			if (!lines || cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
-			if (x+cx > GDISP.clipx1)	cx = GDISP.clipx1 - x;
-			if (y+cy > GDISP.clipy1)	cy = GDISP.clipy1 - y;
-		#endif
-
-		/* See datasheet table T10-1 for this*/
-		uint8_t fHeight = (uint8_t)gdispGetFontMetric(gwinGetDefaultFont(), fontLineSpacing);
-		set_multiplex_ratio(GDISP_SCREEN_HEIGHT - fHeight+1);
-		set_display_offset(fHeight-2);
-
-		/* Scrolling animation.*/
-		for(int i=0; i<fHeight; i++){
-			set_display_start_line(i);
-			gfxSleepMilliseconds(10);
-		}
-
-		/* Shift buffer up a font line.*/
-		for (int i = 0; i < GDISP_SCREEN_WIDTH*(GDISP_SCREEN_HEIGHT/8-1); i++) {
-			gdisp_buffer[i]  = gdisp_buffer[i+GDISP_SCREEN_WIDTH*(fHeight/8)] >> fHeight % 8;
-			gdisp_buffer[i] |= gdisp_buffer[i+GDISP_SCREEN_WIDTH*(fHeight/8 + 1)] << (8 - fHeight%8);
-		}
+		// Don't flush if we don't need it.
+		if (!(g->flags & GDISP_FLG_NEEDFLUSH))
+			return;
 
-		/* Clear last page.*/
-		memset( &gdisp_buffer[GDISP_SCREEN_HEIGHT*GDISP_SCREEN_WIDTH/8 - GDISP_SCREEN_WIDTH*2], SSD1306_BLACK, GDISP_SCREEN_WIDTH*2);
+		write_cmd(g, SSD1306_SETSTARTLINE | 0);
 
-		/* Update display.*/
-		gdisp_lld_display();
+		for(i=0; i < GDISP_SCREEN_WIDTH * GDISP_SCREEN_HEIGHT/8; i+=GDISP_BUS_MAX_TRANSFER_SIZE)
+			write_data(g, RAM(g)+i, GDISP_BUS_MAX_TRANSFER_SIZE);
 	}
+#endif
 
-	/**
-	 * @warning Implementation only fully supports left and right...some command issues here.
-	 * Activate a scroll for rows start through stop.
-	 * Hint, the display is 16 rows tall. To scroll the whole display, run:
-	 * @code
-	 * display.scrollright(0x00, 0x0F)
-	 * @endcode
-	 * @param[in] start The start of the area to be scrolled
-	 * @param[in] stop	The size of the area to be scrolled
-	 * @param[in] dir	direction of scrolling
-	 * 					[left, right, up, down, up_right, up_left, down_left, down_right]
-	 * @note    Optional. *
-	 *
-	 * @notapi
-	 */
-	void gdisp_lld_start_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval){
-//		if(dir == GDISP_SCROLL_RIGHT || GDISP_SCROLL_LEFT || GDISP_SCROLL_UP) {
-//			switch (dir) {
-//				case GDISP_SCROLL_RIGHT:
-//					write_cmd(SSD1306_SCROLL_HORIZONTAL_RIGHT);
-//					break;
-//				case GDISP_SCROLL_LEFT:
-//					write_cmd(SSD1306_SCROLL_HORIZONTAL_LEFT);
-//					break;
-//			}
-//			write_cmd(0X00);			// Dummy byte.
-//			write_cmd(start & 0x07);	// Define start page address.
-//			switch (interval) {			// Set time interval between each scroll step (5 frames)
-//			        case   2: write_cmd(0x07); break; // 111b
-//			        case   3: write_cmd(0x04); break; // 100b
-//			        case   4: write_cmd(0x05); break; // 101b
-//			        case   5: write_cmd(0x00); break; // 000b
-//			        case  25: write_cmd(0x06); break; // 110b
-//			        case  64: write_cmd(0x01); break; // 001b
-//			        case 128: write_cmd(0x02); break; // 010b
-//			        case 256: write_cmd(0x03); break; // 011b
-//			        default:
-//			            // default to 2 frame interval
-//			            write_cmd(0x07); break;
-//			    }
-//			write_cmd(stop & 0x07);		// Define stop page address
-//			write_cmd(0X01);			// Set vertical scrolling offset as no row.
-//			write_cmd(0XFF);			// Undocumented but needed.
-//			write_cmd(SSD1306_SCROLL_ACTIVATE);
-//		}
-//		else if(dir == GDISP_SCROLL_UP || GDISP_SCROLL_DOWN) {
-//			switch (dir) {
-//				case GDISP_SCROLL_UP:
-//					gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT);
-//					write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT);
-//					break;
-//
-//				case GDISP_SCROLL_DOWN:
-//					gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT);
-//					write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_LEFT);
-//					break;
-//			}
-//			write_cmd(0X00);		// Dummy byte.
-//			write_cmd(start);		// Define start page address.
-//			write_cmd(0X00);		// Set time interval between each scroll step (5 frames)
-//			write_cmd(stop);		// Define stop page address
-//			write_cmd(0X01);		// Set vertical scrolling offset as no row.
-//			write_cmd(SSD1306_SCROLL_ACTIVATE);
-//			gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT-10);
-//			write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT);
-//			write_cmd(0X00);		// Dummy byte.
-//			write_cmd(start);		// Define start page address.
-//			write_cmd(0X00);		// Set time interval between each scroll step (5 frames)
-//			write_cmd(stop);		// Define stop page address
-//			write_cmd(0X03);		// Set vertical scrolling offset as no row.
-//			write_cmd(SSD1306_SCROLL_ACTIVATE);
-//		}
+#if GDISP_HARDWARE_DRAWPIXEL
+	LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) {
+		if (g->p.color != Black)
+			RAM(g)[g->p.x + (g->p.y>>3)*GDISP_SCREEN_WIDTH] |=  (1<<(g->p.y&7));
+		else
+			RAM(g)[g->p.x + (g->p.y>>3)*GDISP_SCREEN_WIDTH] &=  ~(1<<(g->p.y&7));
+		g->flags |= GDISP_FLG_NEEDFLUSH;
 	}
+#endif
 
-	/**
-	 * Sets vertical scroll area of display.
-	 * @param[in] start The start of the area to be scrolled [y coordinate]
-	 * @param[in] stop	The size of the area to be scrolled [y coordinate]
-	 * @note    Optional. *
-	 *
-	 * @notapi
-	 */
-	void gdisp_lld_set_vertical_scroll_area(uint8_t start, uint8_t stop){
-		write_cmd(SSD1306_SCROLL_SET_VERTICAL_SCROLL_AREA);
-		write_cmd(start);
-		write_cmd(stop);
+#if GDISP_HARDWARE_PIXELREAD
+	LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g) {
+		return (RAM(g)[g->p.x + (g->p.y>>3)*GDISP_SCREEN_WIDTH] & (1<<(g->p.y&7))) ? White : Black;
 	}
+#endif
 
-	/** Deactivate the continuous scroll set up with start_horizontal_scroll() or
-	 *  start_vertical_and_horizontal_scroll().
-	 *	@see set_horizontal_scroll, set_vertical_and_horizontal_scroll
-	 * 	@notapi
-	 */
-	void gdisp_lld_stop_scroll(void){
-		write_cmd(SSD1306_SCROLL_DEACTIVATE);
-	}
-#endif	// GDISP_NEED_SCROLL
-
-#if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__)
-	void gdisp_lld_fill_area(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) {
-	    #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
-	        if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; }
-	        if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; }
-	        if (cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
-	        if (x+cx > GDISP.clipx1)	cx = GDISP.clipx1 - x;
-	        if (y+cy > GDISP.clipy1)	cy = GDISP.clipy1 - y;
-	    #endif
-
-		for(int i=x; i<x+cx; i++) {
-			for(int j=y; j<y+cy; j++) {
-				gdisp_lld_draw_pixel(i,j,color);
-			}
-		}
-	}
-#endif 	// GDISP_HARDWARE_FILLS
-
-#if (GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL) || defined(__DOXYGEN__)
-	/**
-	 * @brief   Driver Control
-	 * @details Unsupported control codes are ignored.
-	 * @note    The value parameter should always be typecast to (void *).
-	 * @note    There are some predefined and some specific to the low level driver.
-	 * @note    GDISP_CONTROL_POWER         - Takes a gdisp_powermode_t
-	 *          GDISP_CONTROL_ORIENTATION   - Takes a gdisp_orientation_t
-	 *          GDISP_CONTROL_BACKLIGHT 	- Takes an int from 0 to 100. For a driver
-	 *                                        that only supports off/on anything other
-	 *                                        than zero is on.
-	 *          GDISP_CONTROL_CONTRAST      - Takes an int from 0 to 100.
-	 *          GDISP_CONTROL_LLD           - Low level driver control constants start at
-	 *                                        this value.
-	 *
-	 * @param[in] what		What to do.
-	 * @param[in] value 	The value to use (always cast to a void *).
-	 *
-	 * @notapi
-	 */
-	void gdisp_lld_control(unsigned what, void *value) {
-		switch(what) {
+#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
+	LLDSPEC void gdisp_lld_control(GDisplay *g) {
+		switch(g->p.x) {
 		case GDISP_CONTROL_POWER:
-			if (GDISP.Powermode == (gdisp_powermode_t)value)
+			if (g->g.Powermode == (powermode_t)g->p.ptr)
 				return;
-			switch((gdisp_powermode_t)value) {
+			switch((powermode_t)g->p.ptr) {
 			case powerOff:
-				display_off();
 			case powerSleep:
-				display_off();
 			case powerDeepSleep:
-				display_off();
+				acquire_bus(g);
+				write_cmd(g, SSD1306_DISPLAYOFF);
+				release_bus(g);
+				break;
 			case powerOn:
-				display_on();
+				acquire_bus(g);
+				write_cmd(g, SSD1306_DISPLAYON);
+				release_bus(g);
 			default:
 				return;
 			}
-			GDISP.Powermode = (gdisp_powermode_t)value;
+			g->g.Powermode = (powermode_t)g->p.ptr;
 			return;
+
 		case GDISP_CONTROL_ORIENTATION:
-				if (GDISP.Orientation == (gdisp_orientation_t)value)
-					return;
-				switch((gdisp_orientation_t)value) {
-				case GDISP_ROTATE_0:
-					flip_display(0);
-					GDISP.Height = GDISP_SCREEN_HEIGHT;
-					GDISP.Width = GDISP_SCREEN_WIDTH;
-					break;
-				case GDISP_ROTATE_180:
-					flip_display(1);
-					GDISP.Height = GDISP_SCREEN_HEIGHT;
-					GDISP.Width = GDISP_SCREEN_WIDTH;
-					break;
-				default:
-					return;
-				}
-				#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
-					GDISP.clipx0 = 0;
-					GDISP.clipy0 = 0;
-					GDISP.clipx1 = GDISP.Width;
-					GDISP.clipy1 = GDISP.Height;
-				#endif
-				GDISP.Orientation = (gdisp_orientation_t)value;
+			if (g->g.Orientation == (orientation_t)g->p.ptr)
 				return;
-		case GDISP_CONTROL_CONTRAST:
-				if ((unsigned)value > 100)
-					value = (void *)100;
-				if (GDISP.Contrast == (uint8_t)((float)((uint8_t)value) * 256.0/100.0) )
-					return;
-				set_contrast((uint8_t)((float)((uint8_t)value) * 256.0/100.0) );
-				GDISP.Contrast = (unsigned)value;
+			switch((orientation_t)g->p.ptr) {
+			case GDISP_ROTATE_0:
+				acquire_bus(g);
+				write_cmd(g, SSD1306_COMSCANDEC);
+				write_cmd(g, SSD1306_SEGREMAP+1);
+				GDISP.Height = GDISP_SCREEN_HEIGHT;
+				GDISP.Width = GDISP_SCREEN_WIDTH;
+				release_bus(g);
+				break;
+			case GDISP_ROTATE_180:
+				acquire_bus(g);
+				write_cmd(g, SSD1306_COMSCANINC);
+				write_cmd(g, SSD1306_SEGREMAP);
+				GDISP.Height = GDISP_SCREEN_HEIGHT;
+				GDISP.Width = GDISP_SCREEN_WIDTH;
+				release_bus(g);
+				break;
+			default:
 				return;
+			}
+			g->g.Orientation = (orientation_t)value;
+			return;
+
+		case GDISP_CONTROL_CONTRAST:
+            if ((unsigned)g->p.ptr > 100)
+            	g->p.ptr = (void *)100;
+			acquire_bus(g);
+			write_cmd2(g, SSD1306_SETCONTRAST, (((uint16_t)value)<<8)/101);
+			release_bus(g);
+            g->g.Contrast = (unsigned)g->p.ptr;
+			return;
+
+		// Our own special controller code to inverse the display
+		// 0 = normal, 1 = inverse
+		case GDISP_CONTROL_INVERT:
+			acquire_bus(g);
+			write_cmd(g, g->p.ptr ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
+			release_bus(g);
+			return;
 		}
 	}
 #endif // GDISP_NEED_CONTROL
 
-/**
- * Let the display blink several times by means of invert and invert back.
- * @param	num		number of blink cycles to do
- * @param	speed	milliseconds to wait between toggling inversion
- * @param	wait	milliseconds to wait before start of all blink cycles and after finishing blink cycles
- * @notapi
- */
- void gdisp_lld_display_blink(uint8_t num, uint16_t speed, uint16_t wait){
-	uint8_t	 inv = 0;
-
-	gfxSleepMilliseconds(wait);
-	for(int i=0; i<2*num; i++) {
-		inv ^= 1;
-		invert_display(inv);
-		gfxSleepMilliseconds(speed);
-	}
-	gfxSleepMilliseconds(wait);
-}
-
 #endif // GFX_USE_GDISP
-/** @} */
 
diff --git a/drivers/gdisp/SSD1306/gdisp_lld.mk b/drivers/gdisp/SSD1306/gdisp_lld.mk
index 2a2e1364..ad320292 100644
--- a/drivers/gdisp/SSD1306/gdisp_lld.mk
+++ b/drivers/gdisp/SSD1306/gdisp_lld.mk
@@ -1,5 +1,2 @@
-# List the required driver.
-GFXSRC += $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.c
-
-# Required include directories
 GFXINC += $(GFXLIB)/drivers/gdisp/SSD1306
+GFXSRC += $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.c
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h
deleted file mode 100644
index 0e7e153a..00000000
--- a/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * This file is subject to the terms of the GFX License. If a copy of
- * the license was not distributed with this file, you can obtain one at:
- *
- *              http://ugfx.org/license.html
- */
-
-#ifndef _GDISP_LLD_BOARD_H
-#define _GDISP_LLD_BOARD_H
-
-#define SSD1306_RESET_PORT		GPIOB
-#define SSD1306_RESET_PIN		5
-
-/**
- * The default slave address is 0x3D, (talking about
- * only the real address part here) and the slave
- * address can be changed to 0x3C by soldering the
- * SA0 pads on the bottom side of the module.
- *
- * b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0
- * --------------------------------------
- * 0  | 1  | 1  | 1  | 1  | 0  |SA0 | R/W
- */
-#define SSD1306_I2C_ADDRESS   	0x3D
-#define SSD1306_SDA_PORT		GPIOB
-#define SSD1306_SDA_PIN			7
-#define SSD1306_SCL_PORT		GPIOB
-#define SSD1306_SCL_PIN			6
-#define SET_RST					palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
-#define CLR_RST					palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
-int8_t vccstate;
-int32_t row_offset = 0;
-
-// I2C configuration structure.
-static I2CConfig i2cconfig;
-
-
-/**
- * @brief   Initialize the board for the display.
- * @notes	This board definition uses GPIO and assumes exclusive access to these GPIO pins
- * @notapi
- */
-static inline void init_board(void) {
-
-	// RESET pin.
-	palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
-
-
-	/*
-	 * Initializes the I2C driver 1. The I2C1 signals are routed as follows:
-	 * PB6 - SCL.
-	 * PB7 - SDA.
-	 * Timing value comes from ST I2C config tool (xls):
-	 * 0x00901D2B;		// 100kHz Standard Mode
-	 * 0x00300444;		// 100kHz Fast Mode
-	 * 0x0030020A;		// 400kHz Fast Mode
-	 * 0x00100002;		// 800kHz Fast Mode +
-	 */
-	i2cconfig.timingr = 0x00100002;		// 800kHz Fast Mode+
-	i2cInit();
-	palSetPadMode(SSD1306_SCL_PORT, SSD1306_SCL_PIN, PAL_MODE_ALTERNATE(1));
-	palSetPadMode(SSD1306_SDA_PORT, SSD1306_SDA_PIN, PAL_MODE_ALTERNATE(1));
-	vccstate = SSD1306_SWITCHCAPVCC;
-}
-
-/**
- * @brief   Set or clear the lcd reset pin.
- * @param[in] state		TRUE = lcd in reset, FALSE = normal operation
- * @notapi
- */
-static inline void setpin_reset(bool_t state) {
-	if(state)
-		SET_RST
-	else
-		CLR_RST
-}
-
-/**
- * @brief   Set the lcd back-light level.
- * @param[in] percent		0 to 100%
- * @notapi
- */
-static inline void set_backlight(uint8_t percent) {
-	// Since we are on OLED no backlight needed
-}
-
-/**
- * @brief   Take exclusive control of the bus
- * @notapi
- */
-static inline void acquire_bus(void) {
-		i2cAcquireBus(&I2CD1);
-}
-
-/**
- * @brief   Release exclusive control of the bus
- * @notapi
- */
-static inline void release_bus(void) {
-		i2cReleaseBus(&I2CD1);
-}
-
-/**
- * @brief   Send command to the display.
- * @param[in] cmd	The command to send *
- * @notapi
- */
-static inline void write_cmd(uint8_t cmd) {
-	uint8_t command[] = { 0x00, 		// Co = 0, D/C = 0
-						  cmd 		},
-						  txLength = sizeof(command)/sizeof(command[0]),
-						  rxLength = 0;
-	i2cStart(&I2CD1, &i2cconfig);
-	i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10));
-	i2cStop(&I2CD1);
-}
-
-/**
- * @brief   Send data to the display.
- * @param[in] data	The data to send
- * @notapi
- */
-static inline void write_data(uint8_t* data, uint16_t length) {
-	uint8_t command[length+1],
-			txLength = length+1,
-			rxLength = 0;
-	command[0] = 0x40; 		// Co = 0, D/C = 1
-	memmove(&command[1], data, length);
-
-	i2cStart(&I2CD1, &i2cconfig);
-	i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10));
-	i2cStop(&I2CD1);
-}
-
-#endif /* _GDISP_LLD_BOARD_H */
-/** @} */
-
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h
deleted file mode 100644
index 232cd58c..00000000
--- a/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * This file is subject to the terms of the GFX License. If a copy of
- * the license was not distributed with this file, you can obtain one at:
- *
- *              http://ugfx.org/license.html
- */
-
-#ifndef _GDISP_LLD_BOARD_H
-#define _GDISP_LLD_BOARD_H
-
-#define SSD1306_RESET_PORT		GPIOB
-#define SSD1306_RESET_PIN		5
-#define SSD1306_MISO_PORT		GPIOB
-#define SSD1306_MISO_PIN		8
-#define SSD1306_MOSI_PORT		GPIOB
-#define SSD1306_MOSI_PIN		7
-#define SSD1306_SCK_PORT		GPIOB
-#define SSD1306_SCK_PIN			6
-#define SSD1306_CS_PORT			GPIOB
-#define SSD1306_CS_PIN			5
-#define SET_RST					palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
-#define CLR_RST					palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
-int8_t vccstate;
-int32_t row_offset = 0;
-
-/*
- * SPI1 configuration structure.
- * Speed 42MHz, CPHA=0, CPOL=0, 8bits frames, MSb transmitted first.
- * The slave select line is the pin 4 on the port GPIOA.
- */
-static const SPIConfig spi1config = {
-	NULL,
-	/* HW dependent part.*/
-	SSD1306_MISO_PORT,
-	SSD1306_MISO_PIN,
-	0
-	//SPI_CR1_BR_0
-};
-
-/**
- * @brief   Initialize the board for the display.
- * @notes	This board definition uses GPIO and assumes exclusive access to these GPIO pins
- * @notapi
- */
-static inline void init_board(void) {
-
-	// RESET pin.
-	palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
-
-	spiInit();
-	palSetPadMode(SSD1306_MISO_PORT, SSD1306_MISO_PIN, 	PAL_MODE_ALTERNATE(1)|
-														PAL_STM32_OSPEED_HIGHEST);
-	palSetPadMode(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN, 	PAL_MODE_ALTERNATE(1)|
-														PAL_STM32_OSPEED_HIGHEST);
-	palSetPadMode(SSD1306_SCK_PORT,  SSD1306_SCK_PIN,  	PAL_MODE_ALTERNATE(1)|
-														PAL_STM32_OSPEED_HIGHEST);
-	palSetPad(SSD1306_CS_PORT, SSD1306_CS_PIN);
-	palSetPadMode(SSD1306_CS_PORT,   SSD1306_CS_PIN,   	PAL_MODE_ALTERNATE(1)|
-														PAL_STM32_OSPEED_HIGHEST);
-	vccstate = SSD1306_SWITCHCAPVCC;
-}
-
-/**
- * @brief   Set or clear the lcd reset pin.
- * @param[in] state		TRUE = lcd in reset, FALSE = normal operation
- * @notapi
- */
-static inline void setpin_reset(bool_t state) {
-	if(state)
-		SET_RST
-	else
-		CLR_RST
-}
-
-/**
- * @brief   Set the lcd back-light level.
- * @param[in] percent		0 to 100%
- * @notapi
- */
-static inline void set_backlight(uint8_t percent) {
-	// Since we are on OLED no backlight needed
-}
-
-/**
- * @brief   Take exclusive control of the bus
- * @notapi
- */
-static inline void acquire_bus(void) {
-	spiAcquireBus(&SPID1);
-}
-
-/**
- * @brief   Release exclusive control of the bus
- * @notapi
- */
-static inline void release_bus(void) {
-	spiReleaseBus(&SPID1);
-}
-
-/**
- * @brief   Send command to the display.
- * @param[in] cmd	The command to send *
- * @notapi
- */
-static inline void write_cmd(uint8_t cmd) {
-	uint8_t command[] = { 0x00, 		// Co = 0, D/C = 0
-			cmd 		},
-			txLength = sizeof(command)/sizeof(command[0]),
-			rxLength = 0;
-
-	spiStart(&SPID1, &spi1config);
-	spiSelect(&SPID1);
-	spiStartSend(&SPID1, txLength, command);
-	spiUnselect(&SPID1);
-	spiStop(&SPID1);
-}
-
-/**
- * @brief   Send data to the display.
- * @param[in] data	The data to send
- * @notapi
- */
-static inline void write_data(uint8_t* data, uint16_t length) {
-	uint8_t command[length+1],
-			txLength = length+1;
-	command[0] = 0x40; 		// Co = 0, D/C = 1
-	memmove(&command[1], data, length);
-
-	spiStart(&SPID1, &spi1config);
-	spiSelect(&SPID1);
-	spiStartSend(&SPID1, txLength, command);
-	spiUnselect(&SPID1);
-	spiStop(&SPID1);
-}
-
-
-#endif /* _GDISP_LLD_BOARD_H */
-/** @} */
-
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_template.h b/drivers/gdisp/SSD1306/gdisp_lld_board_template.h
deleted file mode 100644
index 48028e83..00000000
--- a/drivers/gdisp/SSD1306/gdisp_lld_board_template.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * This file is subject to the terms of the GFX License. If a copy of
- * the license was not distributed with this file, you can obtain one at:
- *
- *              http://ugfx.org/license.html
- */
-
-#ifndef _GDISP_LLD_BOARD_H
-#define _GDISP_LLD_BOARD_H
-
-/**
- * @brief   Initialize the board for the display.
- * @notes	This board definition uses GPIO and assumes exclusive access to these GPIO pins
- * @notapi
- */
-static inline void init_board(void) {
-
-}
-
-/**
- * @brief   Set or clear the lcd reset pin.
- * @param[in] state		TRUE = lcd in reset, FALSE = normal operation
- * @notapi
- */
-static inline void setpin_reset(bool_t state) {
-
-}
-
-/**
- * @brief   Set the lcd back-light level.
- * @param[in] percent		0 to 100%
- * @notapi
- */
-static inline void set_backlight(uint8_t percent) {
-	// Since we are on OLED no backlight needed
-}
-
-/**
- * @brief   Take exclusive control of the bus
- * @notapi
- */
-static inline void acquire_bus(void) {
-
-}
-
-/**
- * @brief   Release exclusive control of the bus
- * @notapi
- */
-static inline void release_bus(void) {
-
-}
-
-/**
- * @brief   Send command to the display.
- * @param[in] cmd	The command to send *
- * @notapi
- */
-static inline void write_cmd(uint8_t cmd) {
-
-}
-
-/**
- * @brief   Send data to the display.
- * @param[in] data	The data to send
- * @notapi
- */
-static inline void write_data(uint8_t* data, uint16_t length) {
-
-}
-
-#endif /* _GDISP_LLD_BOARD_H */
-/** @} */
-
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_config.h b/drivers/gdisp/SSD1306/gdisp_lld_config.h
index f0efc18e..8580f933 100644
--- a/drivers/gdisp/SSD1306/gdisp_lld_config.h
+++ b/drivers/gdisp/SSD1306/gdisp_lld_config.h
@@ -14,17 +14,17 @@
 /* Driver hardware support.                                                  */
 /*===========================================================================*/
 
-#define GDISP_DRIVER_NAME				"SSD1306"
-
-#define GDISP_HARDWARE_CLEARS			FALSE
-#define GDISP_HARDWARE_FILLS			TRUE
-#define GDISP_HARDWARE_BITFILLS			FALSE
-#define GDISP_HARDWARE_SCROLL			TRUE
-#define GDISP_HARDWARE_PIXELREAD		FALSE
+#define GDISP_HARDWARE_FLUSH			TRUE		// This controller requires flushing
+#define GDISP_HARDWARE_DRAWPIXEL		TRUE
+#define GDISP_HARDWARE_PIXELREAD		TRUE
 #define GDISP_HARDWARE_CONTROL			TRUE
 
 #define GDISP_PIXELFORMAT				GDISP_PIXELFORMAT_MONO
 
+// This controller supports a special gdispControl() to inverse the display.
+// Pass a parameter of 1 for inverse and 0 for normal.
+#define GDISP_CONTROL_INVERSE			(GDISP_CONTROL_LLD+0)
+
 #endif	/* GFX_USE_GDISP */
 
 #endif	/* _GDISP_LLD_CONFIG_H */
-- 
cgit v1.2.3