// This file was downloaded from adafruit.com and was modified to run on the
// ARM processor on the ZYBO board with a set of installed peripherals to
// provide the underlying connections. It provides an API for the
// LCD controller.

// IMPORTANT: LIBRARY MUST BE SPECIFICALLY CONFIGURED FOR EITHER TFT SHIELD
// OR BREAKOUT BOARD USAGE.  SEE RELEVANT COMMENTS IN Adafruit_TFTLCD.h

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

//#if defined(__SAM3X8E__)
//	#include <include/pio.h>
    #define PROGMEM
    #define pgm_read_byte(addr) (*(const unsigned char *)(addr))
    #define pgm_read_word(addr) (*(const unsigned short *)(addr))
//#endif
//#ifdef __AVR__
//	#include <avr/pgmspace.h>
//#endif
//#include "pins_arduino.h"
//#include "wiring_private.h"
#include "Adafruit_TFTLCD.h"
//#include "pin_magic.h"

#define TFTWIDTH   240
#define TFTHEIGHT  320

// LCD controller chip identifiers
#define ID_932X    0
#define ID_7575    1
#define ID_9341    2
#define ID_UNKNOWN 0xFF

#include "registers.h"
#include "lcd.h"

// Constructor for breakout board (configurable LCD control lines).
// Can still use this w/shield, but parameters are ignored.
//Adafruit_TFTLCD::Adafruit_TFTLCD(
//  uint8_t cs, uint8_t cd, uint8_t wr, uint8_t rd, uint8_t reset) :
//  Adafruit_GFX(TFTWIDTH, TFTHEIGHT) {
//
//#ifndef USE_ADAFRUIT_SHIELD_PINOUT
//  // Convert pin numbers to registers and bitmasks
//  _reset     = reset;
//  #ifdef __AVR__
//    csPort     = portOutputRegister(digitalPinToPort(cs));
//    cdPort     = portOutputRegister(digitalPinToPort(cd));
//    wrPort     = portOutputRegister(digitalPinToPort(wr));
//    rdPort     = portOutputRegister(digitalPinToPort(rd));
//  #endif
//  #if defined(__SAM3X8E__)
//    csPort     = digitalPinToPort(cs);
//    cdPort     = digitalPinToPort(cd);
//    wrPort     = digitalPinToPort(wr);
//    rdPort     = digitalPinToPort(rd);
//  #endif
//  csPinSet   = digitalPinToBitMask(cs);
//  cdPinSet   = digitalPinToBitMask(cd);
//  wrPinSet   = digitalPinToBitMask(wr);
//  rdPinSet   = digitalPinToBitMask(rd);
//  csPinUnset = ~csPinSet;
//  cdPinUnset = ~cdPinSet;
//  wrPinUnset = ~wrPinSet;
//  rdPinUnset = ~rdPinSet;
//  #ifdef __AVR__
//    *csPort   |=  csPinSet; // Set all control bits to HIGH (idle)
//    *cdPort   |=  cdPinSet; // Signals are ACTIVE LOW
//    *wrPort   |=  wrPinSet;
//    *rdPort   |=  rdPinSet;
//  #endif
//  #if defined(__SAM3X8E__)
//    csPort->PIO_SODR  |=  csPinSet; // Set all control bits to HIGH (idle)
//    cdPort->PIO_SODR  |=  cdPinSet; // Signals are ACTIVE LOW
//    wrPort->PIO_SODR  |=  wrPinSet;
//    rdPort->PIO_SODR  |=  rdPinSet;
//  #endif
//  pinMode(cs, OUTPUT);    // Enable outputs
//  pinMode(cd, OUTPUT);
//  pinMode(wr, OUTPUT);
//  pinMode(rd, OUTPUT);
//  if(reset) {
//    digitalWrite(reset, HIGH);
//    pinMode(reset, OUTPUT);
//  }
//#endif
//
//  init();
//}

// Constructor for shield (fixed LCD control lines)
Adafruit_TFTLCD::Adafruit_TFTLCD(void) : Adafruit_GFX(TFTWIDTH, TFTHEIGHT) {
  LCD_init();
  init();
}

// Initialization common to both shield & breakout configs
void Adafruit_TFTLCD::init(void) {
//
//#ifdef USE_ADAFRUIT_SHIELD_PINOUT
//  CS_IDLE; // Set all control bits to idle state
//  WR_IDLE;
//  RD_IDLE;
//  CD_DATA;
//  digitalWrite(5, HIGH); // Reset line
//  pinMode(A3, OUTPUT);   // Enable outputs
//  pinMode(A2, OUTPUT);
//  pinMode(A1, OUTPUT);
//  pinMode(A0, OUTPUT);
//  pinMode( 5, OUTPUT);
//#endif
//
//  setWriteDir(); // Set up LCD data port(s) for WRITE operations
  LCD_negateWr();
  LCD_negateRd();
  LCD_setDataMode();
  LCD_setWriteDataDirection();
  rotation  = 0;
  cursor_y  = cursor_x = 0;
  textsize  = 1;
  textcolor = 0xFFFF;
  _width    = TFTWIDTH;
  _height   = TFTHEIGHT;
}

// Initialization command tables for different LCD controllers
#define TFTLCD_DELAY 0xFF
static const uint8_t HX8347G_regValues[] PROGMEM = {
  0x2E           , 0x89,
  0x29           , 0x8F,
  0x2B           , 0x02,
  0xE2           , 0x00,
  0xE4           , 0x01,
  0xE5           , 0x10,
  0xE6           , 0x01,
  0xE7           , 0x10,
  0xE8           , 0x70,
  0xF2           , 0x00,
  0xEA           , 0x00,
  0xEB           , 0x20,
  0xEC           , 0x3C,
  0xED           , 0xC8,
  0xE9           , 0x38,
  0xF1           , 0x01,

  // skip gamma, do later

  0x1B           , 0x1A,
  0x1A           , 0x02,
  0x24           , 0x61,
  0x25           , 0x5C,

  0x18           , 0x36,
  0x19           , 0x01,
  0x1F           , 0x88,
  TFTLCD_DELAY   , 5   , // delay 5 ms
  0x1F           , 0x80,
  TFTLCD_DELAY   , 5   ,
  0x1F           , 0x90,
  TFTLCD_DELAY   , 5   ,
  0x1F           , 0xD4,
  TFTLCD_DELAY   , 5   ,
  0x17           , 0x05,

  0x36           , 0x09,
  0x28           , 0x38,
  TFTLCD_DELAY   , 40  ,
  0x28           , 0x3C,

  0x02           , 0x00,
  0x03           , 0x00,
  0x04           , 0x00,
  0x05           , 0xEF,
  0x06           , 0x00,
  0x07           , 0x00,
  0x08           , 0x01,
  0x09           , 0x3F
};

static const uint16_t ILI932x_regValues[] PROGMEM = {
  ILI932X_START_OSC        , 0x0001, // Start oscillator
  TFTLCD_DELAY             , 50,     // 50 millisecond delay
  ILI932X_DRIV_OUT_CTRL    , 0x0100,
  ILI932X_DRIV_WAV_CTRL    , 0x0700,
  ILI932X_ENTRY_MOD        , 0x1030,
  ILI932X_RESIZE_CTRL      , 0x0000,
  ILI932X_DISP_CTRL2       , 0x0202,
  ILI932X_DISP_CTRL3       , 0x0000,
  ILI932X_DISP_CTRL4       , 0x0000,
  ILI932X_RGB_DISP_IF_CTRL1, 0x0,
  ILI932X_FRM_MARKER_POS   , 0x0,
  ILI932X_RGB_DISP_IF_CTRL2, 0x0,
  ILI932X_POW_CTRL1        , 0x0000,
  ILI932X_POW_CTRL2        , 0x0007,
  ILI932X_POW_CTRL3        , 0x0000,
  ILI932X_POW_CTRL4        , 0x0000,
  TFTLCD_DELAY             , 200,
  ILI932X_POW_CTRL1        , 0x1690,
  ILI932X_POW_CTRL2        , 0x0227,
  TFTLCD_DELAY             , 50,
  ILI932X_POW_CTRL3        , 0x001A,
  TFTLCD_DELAY             , 50,
  ILI932X_POW_CTRL4        , 0x1800,
  ILI932X_POW_CTRL7        , 0x002A,
  TFTLCD_DELAY             , 50,
  ILI932X_GAMMA_CTRL1      , 0x0000,
  ILI932X_GAMMA_CTRL2      , 0x0000,
  ILI932X_GAMMA_CTRL3      , 0x0000,
  ILI932X_GAMMA_CTRL4      , 0x0206,
  ILI932X_GAMMA_CTRL5      , 0x0808,
  ILI932X_GAMMA_CTRL6      , 0x0007,
  ILI932X_GAMMA_CTRL7      , 0x0201,
  ILI932X_GAMMA_CTRL8      , 0x0000,
  ILI932X_GAMMA_CTRL9      , 0x0000,
  ILI932X_GAMMA_CTRL10     , 0x0000,
  ILI932X_GRAM_HOR_AD      , 0x0000,
  ILI932X_GRAM_VER_AD      , 0x0000,
  ILI932X_HOR_START_AD     , 0x0000,
  ILI932X_HOR_END_AD       , 0x00EF,
  ILI932X_VER_START_AD     , 0X0000,
  ILI932X_VER_END_AD       , 0x013F,
  ILI932X_GATE_SCAN_CTRL1  , 0xA700, // Driver Output Control (R60h)
  ILI932X_GATE_SCAN_CTRL2  , 0x0003, // Driver Output Control (R61h)
  ILI932X_GATE_SCAN_CTRL3  , 0x0000, // Driver Output Control (R62h)
  ILI932X_PANEL_IF_CTRL1   , 0X0010, // Panel Interface Control 1 (R90h)
  ILI932X_PANEL_IF_CTRL2   , 0X0000,
  ILI932X_PANEL_IF_CTRL3   , 0X0003,
  ILI932X_PANEL_IF_CTRL4   , 0X1100,
  ILI932X_PANEL_IF_CTRL5   , 0X0000,
  ILI932X_PANEL_IF_CTRL6   , 0X0000,
  ILI932X_DISP_CTRL1       , 0x0133, // Main screen turn on
};

void Adafruit_TFTLCD::begin(uint16_t id) {
  uint8_t i = 0;

  reset();

  if((id == 0x9325) || (id == 0x9328)) {

    uint16_t a, d;
    driver = ID_932X;
//    CS_ACTIVE;  // BLH: CS is always asserted.
    while(i < sizeof(ILI932x_regValues) / sizeof(uint16_t)) {
      a = pgm_read_word(&ILI932x_regValues[i++]);
      d = pgm_read_word(&ILI932x_regValues[i++]);
      if(a == TFTLCD_DELAY) LCD_delay(d);
      else                  writeRegister16(a, d);
    }
    setRotation(rotation);
    setAddrWindow(0, 0, TFTWIDTH-1, TFTHEIGHT-1);

  } else if (id == 0x9341) {

    uint16_t a, d;
    driver = ID_9341;
//     CS_ACTIVE;  // BLH: CS is always asserted.
    writeRegister8(ILI9341_SOFTRESET, 0);
    LCD_delay(50);
    writeRegister8(ILI9341_DISPLAYOFF, 0);

    writeRegister8(ILI9341_POWERCONTROL1, 0x23);
    writeRegister8(ILI9341_POWERCONTROL2, 0x10);
    writeRegister16(ILI9341_VCOMCONTROL1, 0x2B2B);
    writeRegister8(ILI9341_VCOMCONTROL2, 0xC0);
    writeRegister8(ILI9341_MEMCONTROL, ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR);
    writeRegister8(ILI9341_PIXELFORMAT, 0x55);
    writeRegister16(ILI9341_FRAMECONTROL, 0x001B);

    writeRegister8(ILI9341_ENTRYMODE, 0x07);
    /* writeRegister32(ILI9341_DISPLAYFUNC, 0x0A822700);*/

    writeRegister8(ILI9341_SLEEPOUT, 0);
    LCD_delay(150);
    writeRegister8(ILI9341_DISPLAYON, 0);
    LCD_delay(500);
    setAddrWindow(0, 0, TFTWIDTH-1, TFTHEIGHT-1);

  } else if(id == 0x7575) {

    uint8_t a, d;
    driver = ID_7575;
    // CS_ACTIVE;  // BLH: CS is always asserted.
    while(i < sizeof(HX8347G_regValues)) {
      a = pgm_read_byte(&HX8347G_regValues[i++]);
      d = pgm_read_byte(&HX8347G_regValues[i++]);
      if(a == TFTLCD_DELAY) LCD_delay(d);
      else                  writeRegister8(a, d);
    }
    setRotation(rotation);
    setLR(); // Lower-right corner of address window

  } else {
    driver = ID_UNKNOWN;
    return;
  }
}

// Reset pin is not connected so only the software writes occur. BLH
void Adafruit_TFTLCD::reset(void) {

//  CS_IDLE;
////  CD_DATA;
//  WR_IDLE;
//  RD_IDLE;
	LCD_setDataMode();
	LCD_negateWr();
	LCD_negateRd();
//
//#ifdef USE_ADAFRUIT_SHIELD_PINOUT
//  digitalWrite(5, LOW);
//  delay(2);
//  digitalWrite(5, HIGH);
//#else
//  if(_reset) {
//    digitalWrite(_reset, LOW);
//    delay(2);
//    digitalWrite(_reset, HIGH);
//  }
//#endif
//
//  // Data transfer sync
//  CS_ACTIVE;
//  CD_COMMAND;
  LCD_setCommandMode();
  LCD_write8(0x00);
  for(uint8_t i=0; i<3; i++)
	  //WR_STROBE; // Three extra 0x00s
	  LCD_strobeWriteLine();
//  CS_IDLE;
}

// Sets the LCD address window (and address counter, on 932X).
// Relevant to rect/screen fills and H/V lines.  Input coordinates are
// assumed pre-sorted (e.g. x2 >= x1).
void Adafruit_TFTLCD::setAddrWindow(int x1, int y1, int x2, int y2) {

//  CS_ACTIVE;  // BLH: CS is always asserted.
  if(driver == ID_932X) {

    // Values passed are in current (possibly rotated) coordinate
    // system.  932X requires hardware-native coords regardless of
    // MADCTL, so rotate inputs as needed.  The address counter is
    // set to the top-left corner -- although fill operations can be
    // done in any direction, the current screen rotation is applied
    // because some users find it disconcerting when a fill does not
    // occur top-to-bottom.
    int x, y, t;
    switch(rotation) {
     default:
      x  = x1;
      y  = y1;
      break;
     case 1:
      t  = y1;
      y1 = x1;
      x1 = TFTWIDTH  - 1 - y2;
      y2 = x2;
      x2 = TFTWIDTH  - 1 - t;
      x  = x2;
      y  = y1;
      break;
     case 2:
      t  = x1;
      x1 = TFTWIDTH  - 1 - x2;
      x2 = TFTWIDTH  - 1 - t;
      t  = y1;
      y1 = TFTHEIGHT - 1 - y2;
      y2 = TFTHEIGHT - 1 - t;
      x  = x2;
      y  = y2;
      break;
     case 3:
      t  = x1;
      x1 = y1;
      y1 = TFTHEIGHT - 1 - x2;
      x2 = y2;
      y2 = TFTHEIGHT - 1 - t;
      x  = x1;
      y  = y2;
      break;
    }
    writeRegister16(0x0050, x1); // Set address window
    writeRegister16(0x0051, x2);
    writeRegister16(0x0052, y1);
    writeRegister16(0x0053, y2);
    writeRegister16(0x0020, x ); // Set address counter to top left
    writeRegister16(0x0021, y );

  } else if(driver == ID_7575) {

    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x1);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y1);
    writeRegisterPair(HX8347G_COLADDREND_HI  , HX8347G_COLADDREND_LO  , x2);
    writeRegisterPair(HX8347G_ROWADDREND_HI  , HX8347G_ROWADDREND_LO  , y2);

  } else if (driver == ID_9341) {
    uint32_t t;

    t = x1;
    t <<= 16;
    t |= x2;
    writeRegister32(ILI9341_COLADDRSET, t);
    t = y1;
    t <<= 16;
    t |= y2;
    writeRegister32(ILI9341_PAGEADDRSET, t);

  }
//  CS_IDLE;  // BLH: CS is always asserted.
}

// Unlike the 932X drivers that set the address window to the full screen
// by default (using the address counter for drawPixel operations), the
// 7575 needs the address window set on all graphics operations.  In order
// to save a few register writes on each pixel drawn, the lower-right
// corner of the address window is reset after most fill operations, so
// that drawPixel only needs to change the upper left each time.
void Adafruit_TFTLCD::setLR(void) {
//  CS_ACTIVE;
  writeRegisterPair(HX8347G_COLADDREND_HI, HX8347G_COLADDREND_LO, _width  - 1);
  writeRegisterPair(HX8347G_ROWADDREND_HI, HX8347G_ROWADDREND_LO, _height - 1);
//  CS_IDLE;
}

// Fast block fill operation for fillScreen, fillRect, H/V line, etc.
// Requires setAddrWindow() has previously been called to set the fill
// bounds.  'len' is inclusive, MUST be >= 1.
void Adafruit_TFTLCD::flood(uint16_t color, uint32_t len) {
  uint16_t blocks;
  uint8_t  i, hi = color >> 8,
              lo = color;

//  CS_ACTIVE;
//  CD_COMMAND;
  LCD_setCommandMode();  // BLH
  if (driver == ID_9341) {
    write8(0x2C);
  } else if (driver == ID_932X) {
    write8(0x00); // High byte of GRAM register...
    write8(0x22); // Write data to GRAM
  } else {
    write8(0x22); // Write data to GRAM
  }

  // Write first pixel normally, decrement counter by 1
//  CD_DATA;
  LCD_setDataMode();  // BLH
  write8(hi);
  write8(lo);
  len--;

  blocks = (uint16_t)(len / 64); // 64 pixels/block
  if(hi == lo) {
    // High and low bytes are identical.  Leave prior data
    // on the port(s) and just toggle the write strobe.
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
//        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // 2 bytes/pixel
//        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // x 4 pixels
    	  LCD_strobeWriteLine(); LCD_strobeWriteLine(); LCD_strobeWriteLine(); LCD_strobeWriteLine();
    	  LCD_strobeWriteLine(); LCD_strobeWriteLine(); LCD_strobeWriteLine(); LCD_strobeWriteLine();
      } while(--i);
    }
    // Fill any remaining pixels (1 to 64)
    for(i = (uint8_t)len & 63; i--; ) {
//      WR_STROBE;
//      WR_STROBE;
    	LCD_strobeWriteLine(); LCD_strobeWriteLine();
    }
  } else {
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        write8(hi); write8(lo); write8(hi); write8(lo);
        write8(hi); write8(lo); write8(hi); write8(lo);
      } while(--i);
    }
    for(i = (uint8_t)len & 63; i--; ) {
      write8(hi);
      write8(lo);
    }
  }
//  CS_IDLE;
}

void Adafruit_TFTLCD::drawFastHLine(int16_t x, int16_t y, int16_t length,
  uint16_t color)
{
  int16_t x2;

  // Initial off-screen clipping
  if((length <= 0     ) ||
     (y      <  0     ) || ( y                  >= _height) ||
     (x      >= _width) || ((x2 = (x+length-1)) <  0      )) return;

  if(x < 0) {        // Clip left
    length += x;
    x       = 0;
  }
  if(x2 >= _width) { // Clip right
    x2      = _width - 1;
    length  = x2 - x + 1;
  }

  setAddrWindow(x, y, x2, y);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  setLR();
}

void Adafruit_TFTLCD::drawFastVLine(int16_t x, int16_t y, int16_t length,
  uint16_t color)
{
  int16_t y2;

  // Initial off-screen clipping
  if((length <= 0      ) ||
     (x      <  0      ) || ( x                  >= _width) ||
     (y      >= _height) || ((y2 = (y+length-1)) <  0     )) return;
  if(y < 0) {         // Clip top
    length += y;
    y       = 0;
  }
  if(y2 >= _height) { // Clip bottom
    y2      = _height - 1;
    length  = y2 - y + 1;
  }

  setAddrWindow(x, y, x, y2);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  setLR();
}

void Adafruit_TFTLCD::fillRect(int16_t x1, int16_t y1, int16_t w, int16_t h,
  uint16_t fillcolor) {
  int16_t  x2, y2;

  // Initial off-screen clipping
  if( (w            <= 0     ) ||  (h             <= 0      ) ||
      (x1           >= _width) ||  (y1            >= _height) ||
     ((x2 = x1+w-1) <  0     ) || ((y2  = y1+h-1) <  0      )) return;
  if(x1 < 0) { // Clip left
    w += x1;
    x1 = 0;
  }
  if(y1 < 0) { // Clip top
    h += y1;
    y1 = 0;
  }
  if(x2 >= _width) { // Clip right
    x2 = _width - 1;
    w  = x2 - x1 + 1;
  }
  if(y2 >= _height) { // Clip bottom
    y2 = _height - 1;
    h  = y2 - y1 + 1;
  }

  setAddrWindow(x1, y1, x2, y2);
  flood(fillcolor, (uint32_t)w * (uint32_t)h);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  setLR();
}

void Adafruit_TFTLCD::fillScreen(uint16_t color) {

  if(driver == ID_932X) {

    // For the 932X, a full-screen address window is already the default
    // state, just need to set the address pointer to the top-left corner.
    // Although we could fill in any direction, the code uses the current
    // screen rotation because some users find it disconcerting when a
    // fill does not occur top-to-bottom.
    uint16_t x, y;
    switch(rotation) {
      default: x = 0            ; y = 0            ; break;
      case 1 : x = TFTWIDTH  - 1; y = 0            ; break;
      case 2 : x = TFTWIDTH  - 1; y = TFTHEIGHT - 1; break;
      case 3 : x = 0            ; y = TFTHEIGHT - 1; break;
    }
//    CS_ACTIVE;
    writeRegister16(0x0020, x);
    writeRegister16(0x0021, y);

  } else if (driver == ID_9341) {
    setAddrWindow(0, 0, _width - 1, _height - 1);
  } else if(driver == ID_7575) {

    // For the 7575, there is no settable address pointer, instead the
    // address window must be set for each drawing operation.  However,
    // this display takes rotation into account for the parameters, no
    // need to do extra rotation math here.
    setAddrWindow(0, 0, _width - 1, _height - 1);

  }
  flood(color, (long)TFTWIDTH * (long)TFTHEIGHT);
}

void Adafruit_TFTLCD::drawPixel(int16_t x, int16_t y, uint16_t color) {

  // Clip
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return;

//  CS_ACTIVE;
  if(driver == ID_932X) {
    int16_t t;
    switch(rotation) {
     case 1:
      t = x;
      x = TFTWIDTH  - 1 - y;
      y = t;
      break;
     case 2:
      x = TFTWIDTH  - 1 - x;
      y = TFTHEIGHT - 1 - y;
      break;
     case 3:
      t = x;
      x = y;
      y = TFTHEIGHT - 1 - t;
      break;
    }
    writeRegister16(0x0020, x);
    writeRegister16(0x0021, y);
    writeRegister16(0x0022, color);

  } else if(driver == ID_7575) {

    uint8_t hi, lo;
    switch(rotation) {
     default: lo = 0   ; break;
     case 1 : lo = 0x60; break;
     case 2 : lo = 0xc0; break;
     case 3 : lo = 0xa0; break;
    }
    writeRegister8(   HX8347G_MEMACCESS      , lo);
    // Only upper-left is set -- bottom-right is full screen default
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
    hi = color >> 8; lo = color;
//    CD_COMMAND;
    LCD_setCommandMode();
    write8(0x22);
//    CD_DATA;
    LCD_setCommandMode();
    write8(hi); write8(lo);

  } else if (driver == ID_9341) {
  	// This is bug fix suggested by a student that seems to work.
  	// The bus because the original code did not handle rotation
  	// for the 9341 controller. This patch fixes that.
  	//    setAddrWindow(x, y, 239, 319);  // original code.
  	// This bug-fix was tested against all of the provided test routines and looks
  	// to generate the correct output on the LCD.
  	// Start of bug-fix.
  	switch (rotation) {               // New code. BLH
  	case 0:                           // New code. BLH
  	case 2:                           // New code. BLH
  		// Portrait mode (0: origin lower-left corner, 2: upper right-hand corner.
   		setAddrWindow(x, y, 239, 319);  // New code. BLH
  		break;                          // New code. BLH
  	case 1:                           // New code. BLH
  	case 3:                           // New code. BLH
  		// Landscape mode (1: origin in upper-left corner of 330 board, 3: origin lower right corner.
  		setAddrWindow(x, y, 319, 239);  // New code. BLH
  		break;                          // New code. BLH
  	}                                 // New code. BLH
    // end of bug-fix.
//    CS_ACTIVE;
//    CD_COMMAND;
    LCD_setCommandMode();
    write8(0x2C);
//    CD_DATA;
    LCD_setDataMode();
    write8(color >> 8); write8(color);
  }

//  CS_IDLE;
}

// Issues 'raw' an array of 16-bit color values to the LCD; used
// externally by BMP examples.  Assumes that setWindowAddr() has
// previously been set to define the bounds.  Max 255 pixels at
// a time (BMP examples read in small chunks due to limited RAM).
void Adafruit_TFTLCD::pushColors(uint16_t *data, uint8_t len, bool first) {
  uint16_t color;
  uint8_t  hi, lo;
//  CS_ACTIVE;
  if(first == true) { // Issue GRAM write command only on first call
//    CD_COMMAND;
	  LCD_setCommandMode();
    if(driver == ID_932X) write8(0x00);
     if (driver == ID_9341) {
       write8(0x2C);
     }  else {
       write8(0x22);
     }
  }
//  CD_DATA;
  LCD_setDataMode();
  while(len--) {
    color = *data++;
    hi    = color >> 8; // Don't simplify or merge these
    lo    = color;      // lines, there's macro shenanigans
    write8(hi);         // going on.
    write8(lo);
  }
//  CS_IDLE;
}

void Adafruit_TFTLCD::setRotation(uint8_t x) {

  // Call parent rotation func first -- sets up rotation flags, etc.
  Adafruit_GFX::setRotation(x);
  // Then perform hardware-specific rotation operations...

//  CS_ACTIVE;
  if(driver == ID_932X) {

    uint16_t t;
    switch(rotation) {
     default: t = 0x1030; break;
     case 1 : t = 0x1028; break;
     case 2 : t = 0x1000; break;
     case 3 : t = 0x1018; break;
    }
    writeRegister16(0x0003, t ); // MADCTL
    // For 932X, init default full-screen address window:
    setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here

  }
 if(driver == ID_7575) {

    uint8_t t;
    switch(rotation) {
     default: t = 0   ; break;
     case 1 : t = 0x60; break;
     case 2 : t = 0xc0; break;
     case 3 : t = 0xa0; break;
    }
    writeRegister8(HX8347G_MEMACCESS, t);
    // 7575 has to set the address window on most drawing operations.
    // drawPixel() cheats by setting only the top left...by default,
    // the lower right is always reset to the corner.
    setLR(); // CS_IDLE happens here
  }

 if(driver == ID_9341) {
   uint16_t t;

   switch (rotation) {
   case 2:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR;
     break;
   case 3:
     t = ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
     break;
  case 0:
    t = ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR;
    break;
   case 1:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
    break;
  }
   writeRegister8(ILI9341_MADCTL, t ); // MADCTL
   // For 9341, init default full-screen address window:
   setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here
  }
}

#ifdef read8isFunctionalized
  #define read8(x) x=read8fn()
#endif

// Because this function is used infrequently, it configures the ports for
// the read operation, reads the data, then restores the ports to the write
// configuration.  Write operations happen a LOT, so it's advantageous to
// leave the ports in that state as a default.
uint16_t Adafruit_TFTLCD::readPixel(int16_t x, int16_t y) {

  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0;

//  CS_ACTIVE;
  if(driver == ID_932X) {

    uint8_t hi, lo;
    int16_t t;
    switch(rotation) {
     case 1:
      t = x;
      x = TFTWIDTH  - 1 - y;
      y = t;
      break;
     case 2:
      x = TFTWIDTH  - 1 - x;
      y = TFTHEIGHT - 1 - y;
      break;
     case 3:
      t = x;
      x = y;
      y = TFTHEIGHT - 1 - t;
      break;
    }
    writeRegister16(0x0020, x);
    writeRegister16(0x0021, y);
    // Inexplicable thing: sometimes pixel read has high/low bytes
    // reversed.  A second read fixes this.  Unsure of reason.  Have
    // tried adjusting timing in read8() etc. to no avail.
    for(uint8_t pass=0; pass<2; pass++) {
//      CD_COMMAND;
      LCD_setCommandMode();
      write8(0x00); write8(0x22); // Read data from GRAM
 //     CD_DATA;
      LCD_setDataMode();
      setReadDir();  // Set up LCD data port(s) for READ operations
      read8(hi);     // First 2 bytes back are a dummy read
      read8(hi);
      read8(hi);     // Bytes 3, 4 are actual pixel value
      read8(lo);
      setWriteDir(); // Restore LCD data port(s) to WRITE configuration
    }
//    CS_IDLE;
    return ((uint16_t)hi << 8) | lo;

  } else if(driver == ID_7575) {

    uint8_t r, g, b;
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
//    CD_COMMAND;
    LCD_setCommandMode();
    write8(0x22); // Read data from GRAM
    setReadDir();  // Set up LCD data port(s) for READ operations
//    CD_DATA;
    LCD_setDataMode();
    read8(r);      // First byte back is a dummy read
    read8(r);
    read8(g);
    read8(b);
    setWriteDir(); // Restore LCD data port(s) to WRITE configuration
//    CS_IDLE;
    return (((uint16_t)r & /*B11111000*/ 0xF8) << 8) |
           (((uint16_t)g & /*B11111100*/ 0xFC) << 3) |
           (           b              >> 3);
  } else return 0;
}

// Ditto with the read/write port directions, as above.
uint16_t Adafruit_TFTLCD::readID(void) {
  uint8_t hi, lo;

  uint16_t id = readReg(0xD3);
  if (id == 0x9341) {
    return id;
  }

//  CS_ACTIVE;
//  CD_COMMAND;
  LCD_setCommandMode();
  write8(0x00);
//  WR_STROBE;     // Repeat prior byte (0x00)
  LCD_strobeWriteLine();
  setReadDir();  // Set up LCD data port(s) for READ operations
//  CD_DATA;
  LCD_setDataMode();
  read8(hi);
  read8(lo);
//  setWriteDir();  // Restore LCD data port(s) to WRITE configuration
  LCD_assertWr();
//  CS_IDLE;

  id = hi; id <<= 8; id |= lo;
  return id;
}

uint32_t Adafruit_TFTLCD::readReg(uint8_t r) {
  uint32_t id;
  uint8_t x;

  // try reading register #4
//  CS_ACTIVE;
//  CD_COMMAND;
  LCD_setCommandMode();
  write8(r);
  setReadDir();  // Set up LCD data port(s) for READ operations
//  CD_DATA;
  LCD_setDataMode();
//  LCD_delayMicroseconds(50);
  read8(x);
  id = x;          // Do not merge or otherwise simplify
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
//  CS_IDLE;
  setWriteDir();  // Restore LCD data port(s) to WRITE configuration

  //Serial.print("Read $"); Serial.print(r, HEX);
  //Serial.print(":\t0x"); Serial.println(id, HEX);
  return id;
}

// Pass 8-bit (each) R,G,B, get back 16-bit packed color
uint16_t Adafruit_TFTLCD::color565(uint8_t r, uint8_t g, uint8_t b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

// For I/O macros that were left undefined, declare function
// versions that reference the inline macros just once:

#ifndef write8
void Adafruit_TFTLCD::write8(uint8_t value) {
//  write8inline(value);
  LCD_write8(value);
}
#endif

#ifdef read8isFunctionalized
uint8_t Adafruit_TFTLCD::read8fn(void) {
  uint8_t result;
//  read8inline(result);
  result = LCD_read8();
  return result;
}
#endif

#ifndef setWriteDir
void Adafruit_TFTLCD::setWriteDir(void) {
//  setWriteDirInline();
	LCD_setWriteDataDirection();
	LCD_assertWr();
	LCD_negateRd();
}
#endif

#ifndef setReadDir
void Adafruit_TFTLCD::setReadDir(void) {
//  setReadDirInline();
	LCD_setReadDataDirection();
	LCD_negateWr();
	LCD_assertRd();
}
#endif

#ifndef writeRegister8
void Adafruit_TFTLCD::writeRegister8(uint8_t a, uint8_t d) {
//  writeRegister8inline(a, d);
	// This functionality was copied from pin_magic.h which is not included or otherwise used here.
	LCD_setCommandMode();
	write8(a);
	LCD_setDataMode();
	write8(d);
}
#endif

#ifndef writeRegister16
void Adafruit_TFTLCD::writeRegister16(uint16_t a, uint16_t d) {
//  writeRegister16inline(a, d);
	// From pin_magic.h
	uint8_t hi, lo; \
	hi = (a) >> 8; lo = (a); /*CD_COMMAND;*/ LCD_setCommandMode(); write8(hi); write8(lo);
	hi = (d) >> 8; lo = (d); /*CD_DATA*/ LCD_setDataMode();   ; write8(hi); write8(lo);
}
#endif

#ifndef writeRegisterPair
void Adafruit_TFTLCD::writeRegisterPair(uint8_t aH, uint8_t aL, uint16_t d) {
//  writeRegisterPairInline(aH, aL, d);
  // from pin_magic.h
  uint8_t hi = (d) >> 8, lo = (d); \
  /*CD_COMMAND;*/ LCD_setCommandMode(); write8(aH); /*CD_DATA;*/ LCD_setDataMode(); write8(hi);
  /*CD_COMMAND;*/ LCD_setCommandMode(); write8(aL); /*CD_DATA;*/ LCD_setDataMode(); write8(lo);
}
#endif


void Adafruit_TFTLCD::writeRegister32(uint8_t r, uint32_t d) {
//  CS_ACTIVE;
//  CD_COMMAND;
  LCD_setCommandMode();
  write8(r);
//  CD_DATA;
  LCD_setDataMode();
//  LCD_delayMicroseconds(10);
  write8(d >> 24);
//  LCD_delayMicroseconds(10);
  write8(d >> 16);
//  LCD_delayMicroseconds(10);
  write8(d >> 8);
//  LCD_delayMicroseconds(10);
  write8(d);
//  CS_IDLE;
}
