Files
retrobsd/sys/pic32/hx8357.c
2015-09-26 16:23:42 -07:00

831 lines
20 KiB
C

/*
* HX8357 TFT driver for PIC32.
*
* Copyright (C) 2014 Majenko Technologies <matt@majenko.co.uk>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/adc.h>
#include <sys/debug.h>
#include <sys/kconfig.h>
#include <sys/hx8357.h>
#include <sys/fonts/default.h>
char frame[40][80];
typedef struct {
unsigned char linesPerCharacter;
unsigned char bytesPerLine;
unsigned char startGlyph;
unsigned char endGlyph;
unsigned char bitsPerPixel;
} FontHeader;
struct tty hx8357_ttys[1];
typedef struct {
union {
unsigned short value;
struct {
unsigned r:5;
unsigned g:6;
unsigned b:5;
} __attribute__((packed));
} __attribute__((packed));
} __attribute__((packed)) Color565;
#define HX8357_EXIT_SLEEP_MODE 0x11
#define HX8357_SET_DISPLAY_OFF 0x28
#define HX8357_SET_DISPLAY_ON 0x29
#define HX8357_SET_COLUMN_ADDRESS 0x2A
#define HX8357_SET_PAGE_ADDRESS 0x2B
#define HX8357_WRITE_MEMORY_START 0x2C
#define HX8357_READ_MEMORY_START 0x2E
#define HX8357_SET_TEAR_ON 0x35
#define HX8357_SET_ADDRESS_MODE 0x36
#define HX8357_SET_PIXEL_FORMAT 0x3A
#define HX8357_WRITE_MEMORY_CONTINUE 0x3C
#define HX8357_READ_MEMORY_CONTINUE 0x3E
#define HX8357_SET_INTERNAL_OSCILLATOR 0xB0
#define HX8357_SET_POWER_CONTROL 0xB1
#define HX8357_SET_DISPLAY_MODE 0xB4
#define HX8357_SET_VCOM_VOLTAGE 0xB6
#define HX8357_ENABLE_EXTENSION_COMMAND 0xB9
#define HX8357_SET_PANEL_DRIVING 0xC0 // not documented!
#define HX8357_SET_PANEL_CHARACTERISTIC 0xCC
#define HX8357_SET_GAMMA_CURVE 0xE0
static int _width = 320;
static int _height = 480;
static int cursor_x = 0;
static int cursor_y = 0;
static int rotation = 0;
static unsigned short textcolor = 0x7BEF;
static unsigned short texthicolor = 0xFFFF;
static unsigned short textbgcolor = 0x0000;
const unsigned char *font = Default;
const unsigned char _font_width = 6;
const unsigned char _font_height = 8;
const struct devspec hx8357devs[] = {
{ 0, "tft0" },
{ 1, "tftin0" },
{ 0, 0 }
};
void inline static writeCommand(unsigned short c)
{
while (PMMODE & PIC32_PMMODE_BUSY);
PMADDR = 0x0000;
PMDIN = c;
}
void inline static writeData(unsigned short c)
{
while (PMMODE & PIC32_PMMODE_BUSY);
PMADDR = 0x0001;
PMDIN = c;
}
void initDisplay()
{
PMCONCLR = PIC32_PMCON_ON;
asm volatile("nop");
PMCONSET = PIC32_PMCON_PTWREN | PIC32_PMCON_PTRDEN;
PMCONCLR = PIC32_PMCON_CSF;
PMAEN = 0x0001; // Enable PMA0 pin for RS pin and CS1 as CS
PMMODE = PIC32_PMMODE_MODE16 | PIC32_PMMODE_MODE_MAST2;
PMADDR = 0;
PMCONSET = PIC32_PMCON_ON;
writeCommand(HX8357_EXIT_SLEEP_MODE); //Sleep Out
udelay(150000);
writeCommand(HX8357_ENABLE_EXTENSION_COMMAND);
writeData(0xFF);
writeData(0x83);
writeData(0x57);
udelay(1000);
writeCommand(HX8357_SET_POWER_CONTROL);
writeData(0x00);
writeData(0x12);
writeData(0x12);
writeData(0x12);
writeData(0xC3);
writeData(0x44);
udelay(1000);
writeCommand(HX8357_SET_DISPLAY_MODE);
writeData(0x02);
writeData(0x40);
writeData(0x00);
writeData(0x2A);
writeData(0x2A);
writeData(0x20);
writeData(0x91);
udelay(1000);
writeCommand(HX8357_SET_VCOM_VOLTAGE);
writeData(0x38);
udelay(1000);
writeCommand(HX8357_SET_INTERNAL_OSCILLATOR);
writeData(0x68);
writeCommand(0xE3); //Unknown Command
writeData(0x2F);
writeData(0x1F);
writeCommand(0xB5); //Set BGP
writeData(0x01);
writeData(0x01);
writeData(0x67);
writeCommand(HX8357_SET_PANEL_DRIVING);
writeData(0x70);
writeData(0x70);
writeData(0x01);
writeData(0x3C);
writeData(0xC8);
writeData(0x08);
udelay(1000);
writeCommand(0xC2); // Set Gate EQ
writeData(0x00);
writeData(0x08);
writeData(0x04);
udelay(1000);
writeCommand(HX8357_SET_PANEL_CHARACTERISTIC);
writeData(0x09);
udelay(1000);
writeCommand(HX8357_SET_GAMMA_CURVE);
writeData(0x01);
writeData(0x02);
writeData(0x03);
writeData(0x05);
writeData(0x0E);
writeData(0x22);
writeData(0x32);
writeData(0x3B);
writeData(0x5C);
writeData(0x54);
writeData(0x4C);
writeData(0x41);
writeData(0x3D);
writeData(0x37);
writeData(0x31);
writeData(0x21);
writeData(0x01);
writeData(0x02);
writeData(0x03);
writeData(0x05);
writeData(0x0E);
writeData(0x22);
writeData(0x32);
writeData(0x3B);
writeData(0x5C);
writeData(0x54);
writeData(0x4C);
writeData(0x41);
writeData(0x3D);
writeData(0x37);
writeData(0x31);
writeData(0x21);
writeData(0x00);
writeData(0x01);
udelay(1000);
writeCommand(HX8357_SET_PIXEL_FORMAT); //COLMOD RGB888
writeData(0x55);
writeCommand(HX8357_SET_ADDRESS_MODE);
writeData(0x00);
writeCommand(HX8357_SET_TEAR_ON); //TE ON
writeData(0x00);
udelay(10000);
writeCommand(HX8357_SET_DISPLAY_ON); //Display On
udelay(10000);
writeCommand(HX8357_WRITE_MEMORY_START); //Write SRAM Data
int l, c;
for (l = 0; l < 39; l++) {
for (c = 0; c < 80; c++) {
frame[l][c]=' ';
}
}
}
void inline static setAddrWindow(unsigned short x0, unsigned short y0, unsigned short x1, unsigned short y1)
{
writeCommand(HX8357_SET_COLUMN_ADDRESS); // Column addr set
writeData((x0) >> 8);
writeData(x0); // XSTART
writeData((x1) >> 8);
writeData(x1); // XEND
writeCommand(HX8357_SET_PAGE_ADDRESS); // Row addr set
writeData((y0) >> 8);
writeData(y0); // YSTART
writeData((y1) >> 8);
writeData(y1); // YEND
writeCommand(HX8357_WRITE_MEMORY_START); //Write SRAM Data
}
void inline static setPixel(int x, int y, unsigned short color)
{
if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height))
return;
setAddrWindow(x,y,x+1,y+1);
while (PMMODE & PIC32_PMMODE_BUSY);
PMADDR = 0x0001;
PMDIN = color;
}
void inline static fillRectangle(int x, int y, int w, int h, unsigned short color)
{
setAddrWindow(x, y, x+w-1, y+h-1);
while (PMMODE & PIC32_PMMODE_BUSY);
PMADDR = 0x0001;
for(y=h; y>0; y--) {
for(x=w; x>0; x--) {
while (PMMODE & PIC32_PMMODE_BUSY);
PMDIN = color;
}
}
}
void inline static setRotation(unsigned char m)
{
writeCommand(HX8357_SET_ADDRESS_MODE);
rotation = m % 4; // can't be higher than 3
switch (rotation) {
case 0:
//PORTRAIT
writeData(0x0000);
_width = 320;
_height = 480;
break;
case 1:
//LANDSCAPE
writeData(0x0060);
_width = 480;
_height = 320;
break;
case 2:
//UPSIDE DOWN PORTRAIT
writeData(0x00C0);
_width = 320;
_height = 480;
break;
case 3:
//UPSIDE DOWN LANDSCAPE
writeData(0x00A0);
_width = 480;
_height = 320;
break;
}
}
unsigned short inline static mix(unsigned short a, unsigned short b, unsigned char pct)
{
Color565 col_a;
Color565 col_b;
Color565 col_out;
col_a.value = a;
col_b.value = b;
unsigned int temp;
temp = (((int)col_a.r * (255-pct)) / 255) + (((unsigned int)col_b.r * pct) / 255);
col_out.r = temp;
temp = (((int)col_a.g * (255-pct)) / 255) + (((unsigned int)col_b.g * pct) / 255);
col_out.g = temp;
temp = (((int)col_a.b * (255-pct)) / 255) + (((unsigned int)col_b.b * pct) / 255);
col_out.b = temp;
return col_out.value;
}
unsigned char drawChar(int x, int y, unsigned char c, unsigned short color, unsigned short bg)
{
if (font == NULL) {
return 0;
}
FontHeader *header = (FontHeader *)font;
if (c < header->startGlyph || c > header->endGlyph) {
return 0;
}
c = c - header->startGlyph;
// Start of this character's data is the character number multiplied by the
// number of lines in a character (plus one for the character width) multiplied
// by the number of bytes in a line, and then offset by 4 for the header.
unsigned int charstart = (c * ((header->linesPerCharacter * header->bytesPerLine) + 1)) + sizeof(FontHeader); // Start of character data
unsigned char charwidth = font[charstart++]; // The first byte of a block is the width of the character
unsigned int bitmask = (1 << header->bitsPerPixel) - 1;
setAddrWindow(x, y, x + charwidth - 1, y + header->linesPerCharacter - 1);
char lineNumber = 0;
for (lineNumber = 0; lineNumber < header->linesPerCharacter; lineNumber++ ) {
unsigned char lineData = 0;
char bitsLeft = -1;
unsigned char byteNumber = 0;
char pixelNumber = 0;
for (pixelNumber = 0; pixelNumber < charwidth; pixelNumber++) {
if (bitsLeft <= 0) {
bitsLeft = 8;
lineData = font[charstart + (lineNumber * header->bytesPerLine) + (header->bytesPerLine - byteNumber - 1)];
byteNumber++;
}
unsigned int pixelValue = lineData & bitmask;
if (pixelValue > 0) {
writeData(color);
} else {
writeData(bg);
}
lineData >>= header->bitsPerPixel;
bitsLeft -= header->bitsPerPixel;
}
}
return charwidth;
}
void updateLine(int l)
{
if (l < 0 || l > 39)
return;
int c;
for (c = 0; c < 80; c++) {
drawChar(c * _font_width, l * _font_height, frame[l][c] & 0x7F,
frame[l][c] & 0x80 ? texthicolor : textcolor, textbgcolor);
}
}
void scrollUp()
{
int l, c;
for (l = 0; l < 39; l++) {
for (c = 0; c < 80; c++) {
frame[l][c] = frame[l+1][c];
}
updateLine(l);
}
for (c = 0; c < 80; c++) {
frame[39][c] = ' ';
}
updateLine(39);
}
int inEscape = 0;
int edValPos = 0;
int edVal[4];
int bold = 0;
void printChar(unsigned char c)
{
switch (c) {
case '\n':
cursor_y ++;
if (cursor_y > 39) {
scrollUp();
cursor_y = 39;
}
break;
case '\r':
cursor_x = 0;
break;
case 8:
cursor_x--;
if (cursor_x < 0) {
cursor_x = 79;
cursor_y--;
if (cursor_y < 0) {
cursor_y = 0;
}
}
break;
default:
frame[cursor_y][cursor_x] = c | (bold ? 0x80 : 0x00);
cursor_x++;
if (cursor_x == 80) {
cursor_x = 0;
cursor_y++;
if (cursor_y == 40) {
cursor_y = 39;
scrollUp();
}
}
updateLine(cursor_y);
}
}
int extended = 0;
void writeChar(unsigned char c)
{
int i, j;
updateLine(cursor_y);
if (inEscape == 1) {
switch (c) {
case 27:
inEscape = 0;
printChar('^');
printChar('[');
break;
case '[':
extended = 1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
edVal[edValPos%4] *= 10;
edVal[edValPos%4] += c - '0';
break;
case ';':
edValPos++;
edVal[edValPos%4] = 0;
break;
case 'J':
if (extended == 1) {
if (edVal[0] == 0) {
for (i = cursor_y; i < 40; i++) {
for (j = 0; j < 80; j++) {
frame[i][j] = ' ';
}
updateLine(i);
}
} else if (edVal[0] == 1) {
for (i = 0; i < cursor_y; i++) {
for (j = 0; j < 80; j++) {
frame[i][j] = ' ';
}
updateLine(i);
}
} else if (edVal[0] == 2) {
for (i = 0; i < 40; i++) {
for (j = 0; j < 80; j++) {
frame[i][j] = ' ';
}
updateLine(i);
}
}
}
inEscape = 0;
break;
case 'H':
if (extended == 1) {
if (edVal[0] == 0) {
edVal[0] = 1;
}
if (edVal[1] == 0) {
edVal[1] = 1;
}
cursor_x = (edVal[1] - 1) % 80;
cursor_y = (edVal[0] - 1) % 40;
}
inEscape = 0;
break;
case 'm':
if (extended == 1) {
if (edVal[0] == 0) {
bold = 0;
} else if (edVal[0] == 1) {
bold = 1;
}
}
inEscape = 0;
break;
case 'A':
if (extended == 0) {
cursor_y--;
if (cursor_y > 0) {
cursor_y = 39;
}
}
inEscape = 0;
break;
case 'B':
if (extended == 0) {
cursor_y++;
if (cursor_y == 40) {
cursor_y = 0;
}
}
inEscape = 0;
break;
case 'C':
if (extended == 0) {
cursor_x++;
if (cursor_x == 80) {
cursor_x = 0;
cursor_y++;
if (cursor_y == 40) {
cursor_y = 0;
}
}
}
inEscape = 0;
break;
case 'D':
if (extended == 0) {
cursor_x--;
if (cursor_x < 0) {
cursor_x = 79;
cursor_y--;
if (cursor_y < 0) {
cursor_y = 39;
}
}
}
inEscape = 0;
break;
case 'K':
if (extended == 1) {
if (edVal[0] == 0) {
for (i = cursor_x; i < 80; i++) {
frame[cursor_y][i] = ' ';
}
} else if (edVal[1] == 1) {
for (i = 0; i < cursor_x; i++) {
frame[cursor_y][i] = ' ';
}
} else {
for (i = 0; i < 80; i++) {
frame[cursor_y][i] = ' ';
}
}
updateLine(cursor_y);
}
inEscape = 0;
break;
default:
if (extended) {
printf("Unhandled extended escape code %c\n", c);
} else {
printf("Unhandled escape code %c\n", c);
}
inEscape = 0;
break;
}
} else {
if (c == 27) {
inEscape = 1;
extended = 0;
edVal[0] = 0;
edValPos = 0;
} else {
printChar(c);
}
}
drawChar(cursor_x * _font_width, cursor_y * _font_height,
frame[cursor_y][cursor_x] & 0x7F, 0x0000,
bold ? texthicolor : textcolor);
#if 0
for (i = 0; i < _font_width; i++) {
setPixel(cursor_x * _font_width + i,
(cursor_y + 1) * _font_height - 1, 0xFFFF);
}
#endif
}
void hx8357_start(struct tty *tp)
{
register int s;
s = spltty();
ttyowake(tp);
// tp->t_state &= TS_BUSY;
if (tp->t_outq.c_cc > 0) {
led_control (LED_TTY, 1);
while (tp->t_outq.c_cc != 0) {
int c = getc (&tp->t_outq);
writeChar(c);
}
led_control (LED_TTY, 0);
}
// tp->t_state |= TS_BUSY;
splx (s);
}
void hx8357_init()
{
initDisplay();
setRotation(1);
int i,j;
for (i = 0; i < 40; i++) {
for (j = 0; j < 80; j++) {
frame[i][j] = ' ';
}
updateLine(i);
}
}
int ttyIsOpen = 0;
int hx8357_open(dev_t dev, int flag, int mode)
{
int unit = minor(dev);
if (unit == 0) {
ttyIsOpen = 1;
struct tty *tp = &hx8357_ttys[0];
tp->t_oproc = hx8357_start;
tp->t_state = TS_ISOPEN | TS_CARR_ON;
tp->t_flags = ECHO | XTABS | CRMOD | CRTBS | CRTERA | CTLECH | CRTKIL;
return ttyopen(dev, tp);
}
if (unit == 1) {
if (!ttyIsOpen) {
return EIO;
}
return 0;
}
return ENODEV;
}
int hx8357_close(dev_t dev, int flag, int mode)
{
int unit = minor(dev);
if (unit == 0) {
ttyIsOpen = 0;
struct tty *tp = &hx8357_ttys[0];
ttyclose(tp);
return 0;
}
if (unit == 1) {
return 0;
}
return ENODEV;
}
int hx8357_read(dev_t dev, struct uio *uio, int flag)
{
int unit = minor(dev);
if (unit == 0) {
struct tty *tp = &hx8357_ttys[unit];
return ttread(tp, uio, flag);
}
if (unit == 1) {
return EIO;
}
return ENODEV;
}
int hx8357_write(dev_t dev, struct uio *uio, int flag)
{
int unit = minor(dev);
if (unit == 0) {
struct tty *tp = &hx8357_ttys[0];
return ttwrite(tp, uio, flag);
}
if (unit == 1) {
if (!ttyIsOpen) {
return EIO;
}
struct tty *tp = &hx8357_ttys[0];
struct iovec *iov = uio->uio_iov;
while (iov->iov_len > 0) {
ttyinput(*(iov->iov_base), tp);
iov->iov_base ++;
iov->iov_len --;
uio->uio_resid --;
uio->uio_offset ++;
}
return 0;
}
return ENODEV;
}
int hx8357_ioctl(dev_t dev, register u_int cmd, caddr_t addr, int flag)
{
int unit = minor(dev);
if (unit == 0) {
#if 0
struct tty *tp = &hx8357_ttys[unit];
int error;
error = ttioctl(tp, cmd, addr, flag);
if (error < 0)
error = ENOTTY;
return (error);
#else
switch (cmd) {
case 0xC0C0: {
unsigned short* wndcoo = (unsigned short*)addr;
setAddrWindow(wndcoo[0], wndcoo[1], wndcoo[2], wndcoo[3]);
break;
}
case 0xDADA: {
unsigned short* data = (unsigned short*)addr;
unsigned short cnt = *data++;
while (PMMODE & PIC32_PMMODE_BUSY);
PMADDR = 0x0001;
while (cnt--) {
while (PMMODE & PIC32_PMMODE_BUSY);
PMDIN = *data++;
}
break;
}
}
return 0;
#endif
}
if (unit == 1) {
return EIO;
}
return ENODEV;
}
void hx8357_putc(dev_t dev, char c)
{
writeChar(c);
}
char hx8357_getc(dev_t dev)
{
return 0;
}
int hx8357_select (dev_t dev, int rw)
{
int unit = minor(dev);
if (unit == 0) {
struct tty *tp = &hx8357_ttys[unit];
return (ttyselect (tp, rw));
}
if (unit == 1) {
return EIO;
}
return ENODEV;
}
/*
* Test to see if device is present.
* Return true if found and initialized ok.
*/
static int
hxtftprobe(config)
struct conf_device *config;
{
int flags = config->dev_flags;
printf("hxtft0: flags %#x\n", flags);
return 1;
}
struct driver hxtftdriver = {
"hxtft", hxtftprobe,
};