#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/types.h>
#include "fbtft.h"
#define DRVNAME "ls020fb"
#define WIDTH 176
#define HEIGHT 132
/* Module Parameter: debug (also available through sysfs) */
MODULE_PARM_DEBUG;
u8 buffer[(WIDTH*HEIGHT*2)];
int ls020fb_writeData(struct fbtft_par *par, void *buf, size_t s) {
int ret;
gpio_set_value(par->gpio.dc, 0);
ret = par->fbtftops.write(par, buf, s);
return ret;
}
void ls020fb_writeCmd(struct fbtft_par *par, uint8_t a, uint8_t b) {
uint8_t tx[2];
tx[0] = a;
tx[1] = b;
gpio_set_value(par->gpio.dc, 1);
par->fbtftops.write(par, tx, 2);
}
void ls020fb_reset(struct fbtft_par *par)
{
fbtft_dev_dbg(DEBUG_RESET, par->info->device, "[IN] %s()\n", __func__);
if (par->gpio.reset == -1)
return;
gpio_set_value(par->gpio.reset, 0);
msleep(50);
gpio_set_value(par->gpio.reset, 1);
msleep(50);
fbtft_dev_dbg(DEBUG_RESET, par->info->device, "[OUT] %s()\n", __func__);
}
void ls020fb_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_fbtft_dev_dbg(DEBUG_SET_ADDR_WIN, par, par->info->device, "[IN] %s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x08, ys); //set y0
ls020fb_writeCmd(par, 0x09, ye); //set y1
ls020fb_writeCmd(par, 0x0A, (WIDTH-1)-xs); //set x0
ls020fb_writeCmd(par, 0x0B, (WIDTH-1)-xe); //set x1
ls020fb_writeCmd(par, 0x06, ys); //set y cursor pos
ls020fb_writeCmd(par, 0x07, (WIDTH-1)-xs); //set x cursor pos
fbtft_fbtft_dev_dbg(DEBUG_SET_ADDR_WIN, par, par->info->device, "[OUT] %s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
}
static int ls020fb_init_display(struct fbtft_par *par)
{
fbtft_dev_dbg(DEBUG_INIT_DISPLAY, par->info->device, "[IN] %s()\n", __func__);
// Disable DC alias RS
gpio_set_value(par->gpio.dc, 1);
// Reset the device
par->fbtftops.reset(par);
// Display off
ls020fb_writeCmd(par, 0xFD, 0xFD);
ls020fb_writeCmd(par, 0xFD, 0xFD);
msleep(50);
//init 1
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0xEE, 0x04);
ls020fb_writeCmd(par, 0x1B, 0x04);
ls020fb_writeCmd(par, 0xFE, 0xFE);
ls020fb_writeCmd(par, 0xFE, 0xFE);
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x4A, 0x04);
ls020fb_writeCmd(par, 0x7F, 0x3F);
ls020fb_writeCmd(par, 0xEE, 0x04);
ls020fb_writeCmd(par, 0x43, 0x06);
msleep(7); //important 7ms
//init 2
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x09, 0x83);
ls020fb_writeCmd(par, 0x08, 0x00);
ls020fb_writeCmd(par, 0x0B, 0xAF);
ls020fb_writeCmd(par, 0x0A, 0x00);
ls020fb_writeCmd(par, 0x05, 0x00);
ls020fb_writeCmd(par, 0x06, 0x00);
ls020fb_writeCmd(par, 0x07, 0x00);
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0xEE, 0x0C);
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x00, 0x80);
ls020fb_writeCmd(par, 0xEF, 0xB0);
ls020fb_writeCmd(par, 0x49, 0x02);
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0x7F, 0x01);
ls020fb_writeCmd(par, 0xE1, 0x81);
ls020fb_writeCmd(par, 0xE2, 0x02);
ls020fb_writeCmd(par, 0xE2, 0x76);
ls020fb_writeCmd(par, 0xE1, 0x83);
msleep(50);
//display on
ls020fb_writeCmd(par, 0x80, 0x01);
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x00, 0x00);
//display options
ls020fb_writeCmd(par, 0xEF, 0x90);
ls020fb_writeCmd(par, 0x01, 0x40); //x1->x0, y0->y1
ls020fb_writeCmd(par, 0x05, 0x04); //0x04=rotate, 0x00=normal
ls020fb_set_addr_win(par, 0, 0, (WIDTH-1), (HEIGHT-1));
fbtft_dev_dbg(DEBUG_INIT_DISPLAY, par->info->device, "[OUT] %s()\n", __func__);
return 0;
}
int ls020fb_write_vmem(struct fbtft_par *par)
{
u8 *vmem8;
size_t remain;
int i;
int ret = 0;
size_t offset, len;
offset = par->dirty_lines_start * par->info->fix.line_length;
len = (par->dirty_lines_end - par->dirty_lines_start + 1) * par->info->fix.line_length;
remain = len;
vmem8 = par->info->screen_base + offset;
fbtft_fbtft_dev_dbg(DEBUG_WRITE_VMEM, par, par->info->device, "[IN] %s: offset=%d, len=%d\n", __func__, offset, len);
// Shit on this
for (i=0;i<len;i+=2) {
buffer[i] = vmem8[i+1];
buffer[i+1] = vmem8[i];
}
ret = ls020fb_writeData(par, buffer, len);
fbtft_fbtft_dev_dbg(DEBUG_WRITE_VMEM, par, par->info->device, "[OUT] %s: offset=%d, len=%d, ret=%d\n", __func__, offset, len, ret);
return ret;
}
struct fbtft_display ls020fb_display = {
.width = WIDTH,
.height = HEIGHT,
};
static int ls020fb_probe(struct spi_device *spi)
{
struct fb_info *info;
struct fbtft_par *par;
int ret;
fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "[IN] %s()\n", __func__);
info = fbtft_framebuffer_alloc(&ls020fb_display, &spi->dev);
if (!info)
return -ENOMEM;
par = info->par;
par->spi = spi;
fbtft_debug_init(par);
par->fbtftops.init_display = ls020fb_init_display;
par->fbtftops.reset = ls020fb_reset;
par->fbtftops.set_addr_win = ls020fb_set_addr_win;
par->fbtftops.write_vmem = ls020fb_write_vmem;
ret = fbtft_register_framebuffer(info);
if (ret < 0)
goto out_release;
fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "[OUT] %s()\n", __func__);
return 0;
out_release:
fbtft_framebuffer_release(info);
fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "[OUT] %s()\n", __func__);
return ret;
}
static int ls020fb_remove(struct spi_device *spi)
{
struct fb_info *info = spi_get_drvdata(spi);
fbtft_dev_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, &spi->dev, "%s()\n", __func__);
if (info) {
struct fbtft_par *par = info->par;
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0x7E, 0x04);
msleep(62);
ls020fb_writeCmd(par, 0xEF, 0xB0);
ls020fb_writeCmd(par, 0x5A, 0x48);
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0x7F, 0x01);
msleep(62);
ls020fb_writeCmd(par, 0xEF, 0xB0);
ls020fb_writeCmd(par, 0x64, 0xFF);
ls020fb_writeCmd(par, 0x65, 0x00);
ls020fb_writeCmd(par, 0xEF, 0x00);
ls020fb_writeCmd(par, 0x7F, 0x01);
msleep(62);
ls020fb_writeCmd(par, 0xe2, 0x62);
msleep(94);
ls020fb_writeCmd(par, 0xe2, 0x02);
msleep(62);
ls020fb_writeCmd(par, 0xef, 0xb0);
ls020fb_writeCmd(par, 0xbc, 0x02);
ls020fb_writeCmd(par, 0xef, 0x00);
ls020fb_writeCmd(par, 0x7f, 0x01);
msleep(22);
ls020fb_writeCmd(par, 0xe2, 0x00);
msleep(22);
ls020fb_writeCmd(par, 0x80, 0x00);
ls020fb_writeCmd(par, 0xe2, 0x04);
msleep(22);
ls020fb_writeCmd(par, 0xe2, 0x00);
msleep(22);
ls020fb_writeCmd(par, 0xe1, 0x00);
msleep(22);
ls020fb_writeCmd(par, 0xef, 0xb0);
ls020fb_writeCmd(par, 0xbc, 0x00);
ls020fb_writeCmd(par, 0xef, 0x00);
ls020fb_writeCmd(par, 0x7f, 0x01);
msleep(62);
ls020fb_reset(par);
fbtft_unregister_framebuffer(info);
fbtft_framebuffer_release(info);
}
return 0;
}
static struct spi_driver ls020fb_driver = {
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
.probe = ls020fb_probe,
.remove = ls020fb_remove,
};
static int __init ls020fb_init(void)
{
fbtft_pr_debug("\n\n"DRVNAME": %s()\n", __func__);
return spi_register_driver(&ls020fb_driver);
}
static void __exit ls020fb_exit(void)
{
fbtft_pr_debug(DRVNAME": %s()\n", __func__);
spi_unregister_driver(&ls020fb_driver);
}
/* ------------------------------------------------------------------------- */
module_init(ls020fb_init);
module_exit(ls020fb_exit);
MODULE_DESCRIPTION("ls020 S65 LCD Driver");
MODULE_AUTHOR("Mirko Kohns");
MODULE_LICENSE("GPL");