blob: f1b7b591e847848f8cf95426b3929b88b4a9240b [file] [log] [blame]
/*
* Copyright (c) 2014 Nest, Inc.
*
* Author: Andrew LeCain <alecain@nestlabs.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*
* Description:
* This file is the LCD panel driver for the Tianma TM025ZDZ01
* 320 x 320 TFT LCD display panel using the Renesas r61529a1
* interface driver.
*/
#include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/r61529a1.h>
#include <linux/time.h>
#include <plat/display.h>
/*
* Driver Strings
*/
#define TM025ZDZ01_DRIVER_NAME "Renesas r61529a1 LCD Driver"
#define TM025ZDZ01_DRIVER_VERSION "2014-4-23"
#define FRAME_DURATION (1000/60)
#define WAKE_RETRY_COUNT 5
/*
*user commands
*/
#define NOP 0x00
#define SOFT_RESET 0x01
#define READ_DDB_START 0x04
#define GET_POWER_MODE 0x0A
#define GET_ADDRESS_MODE 0x0B
#define GET_PIXEL_FORMAT 0x0C
#define GET_DISPLAY_MODE 0x0D
#define GET_SIGNAL_MODE 0x0E
#define GET_DIAG_RESULT 0x0F
#define ENTER_SLEEP_MODE 0x10
#define EXIT_SLEEP_MODE 0x11
#define ENTER_PARTIAL_MODE 0x12
#define ENTER_NORMAL_MODE 0x13
#define EXIT_DEEP_SLEEP 0xFF
#define EXIT_INVERT_MODE 0x20
#define ENTER_INVERT_MODE 0x21
#define SET_DISPLAY_OFF 0x28
#define SET_DISPLAY_ON 0x29
#define SET_COLUMN_ADDRESS 0x2A
#define SET_PAGE_ADDRESS 0x2B
#define WRITE_MEMORY_START 0x2C
#define READ_MEMORY_START 0x2E
#define SET_PARTIAL_AREA 0x30
#define SET_TEAR_OFF 0x34
#define SET_TEAR_ON 0x35
#define SET_ADDRESS_MODE 0x36
#define EXIT_IDLE_MODE 0x38
#define ENTER_IDLE_MODE 0x39
#define SET_PIXEL_FORMAT 0x3A
#define WRITE_MEM_CONTINUE 0x3C
#define READ_MEMORY_CONTIUE 0x3E
#define SET_TEAR_SCANLINE 0x44
#define GET_SCANLINE 0x45
/*
*Manufacturer commands
*/
#define MCAP 0xB0
#define LOW_POWER_CONTROL 0xB1
#define FRAME_MEM_ACCESS 0xB3
#define DISPLAY_MODE 0xB4
#define READ_CHECKSUM 0xB5
#define DSI_CONTROL 0xB6
#define MDDI_CONTROL 0xB7
#define BACKLIGHT_CONTROL_1 0xB8
#define BACKLIGHT_CONTROL_2 0xB9
#define BACKLIGHT_CONTROL_3 0xBA
#define DEVICE_CODE_READ 0xBF
#define DEVICE_CODE_1 0x01
#define DEVICE_CODE_2 0x22
#define DEVICE_CODE_3 0x15
#define DEVICE_CODE_4 0x29
#define PANEL_DRIVE 0xC0
#define V_TIMING 0xC1
#define TEST_MODE 0xC3
#define DRIVE_CONTROL 0xC4
#define DPI_POLARITY 0xC6
#define TEST_MODE_2 0xC7
#define GAMMA_SETTING_A 0xC8
#define GAMMA_SETTING_B 0xC9
#define GAMMA_SETTING_C 0xCA
#define CHARGE_PUMP 0xD0
#define VCOM 0xD1
#define TEST_MODE_4 0xD6
#define TEST_MODE_5 0xD7
#define TEST_MODE_6 0xD8
#define TEST_MODE_7 0xD9
#define TEST_MODE_8 0xDA
static struct regulator *reg;
const char *supply;
/*lines to delay after vsync*/
static u16 line=0;
static struct timespec last_frame;
static int32_t fps;
static ssize_t r61529a1_test_pixel_read(struct device *unused, struct device_attribute *attr, char *buf);
static ssize_t r61529a1_display_id_read(struct device *unused, struct device_attribute *attr, char *buf);
static ssize_t r61529a1_get_fps(struct device *unused, struct device_attribute *attr, char *buf);
static DEVICE_ATTR(test_pixel, S_IRUGO, r61529a1_test_pixel_read, NULL);
static DEVICE_ATTR(display_id, S_IRUGO, r61529a1_display_id_read, NULL);
static DEVICE_ATTR(fps, S_IRUGO, r61529a1_get_fps, NULL);
static int renesas_r61529a1_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h);
static struct attribute *r61529a1_attributes[] = {
&dev_attr_test_pixel.attr,
&dev_attr_display_id.attr,
&dev_attr_fps.attr,
NULL
};
static const struct attribute_group r61529a1_attr_group = {
.attrs = r61529a1_attributes,
};
void WriteCommand(uint16_t cmd){
omapdss_rfbi_write_command(&cmd, 2);
}
void WriteData(uint16_t dat){
omapdss_rfbi_write_data(&dat, 2);
}
int init_display(struct omap_dss_device *dssdev) //for void R61529A_initial_code(void)
{
char temp[5];
struct r61529a1_platform_data *pdata = (struct r61529a1_platform_data *)dssdev->data;
gpio_set_value(pdata->reset.gpio, pdata->reset.inverted);
mdelay(10);
gpio_set_value(pdata->reset.gpio, !pdata->reset.inverted);
mdelay(10);
//************************Start initial sequence****************************
WriteCommand(MCAP); //send enable manufacture commands
WriteData(0x0004);
//dev_printk(KERN_INFO, &dssdev->dev, "wrote mfg cmd\n");
//confirm display is awake
omapdss_rfbi_read_data(DEVICE_CODE_READ, temp, 5);
dev_printk(KERN_INFO, &dssdev->dev, "display ID: %02x %02x %02x %2x\n",
temp[1], temp[2], temp[3], temp[4]);
if ((temp[1] != DEVICE_CODE_1) || (temp[2] !=DEVICE_CODE_2 )
|| (temp[3] != DEVICE_CODE_3) || (temp[4] != DEVICE_CODE_4))
{
return -1;
}
WriteCommand(FRAME_MEM_ACCESS); //frame meory
WriteData(0x0002); //WEMODE=1 continuous write
WriteData(0x0000); //TE used, Every frame
WriteData(0x0000); //unused
WriteData(0x0000);////DFM disabled (2 pixels / 3 cycles)
WriteCommand(DISPLAY_MODE); // display mode
WriteData(0x0000); // internal oscillation clock
WriteCommand(SET_TEAR_ON); //enable TE
WriteData(0x0001); //hsync | vsync
WriteCommand(SET_TEAR_SCANLINE); //set tearing line.
WriteData(0x0000);
WriteData(0x0000); //assert TE at line 0
WriteCommand(SET_ADDRESS_MODE); //address mode
WriteData(0 << 7 | 0 << 6 | 0 << 5); //no mirroring
WriteCommand(PANEL_DRIVE); //panel driving setting
WriteData(0x0013); // REV | BGR| SS
WriteData(0x00DF); //NL[7..1] <<1 | 1 NL = 0xEF means 480 lines. (wat?)
WriteData(0x0040); //NL[8] << 6 | SCN[5..0] SCN = 0
WriteData(0x0010); // 0x10 (NOP)
WriteData(0x0000); //
WriteData(0x0001); //isc = 0x01 every 3 frames
WriteData(0x0000); //BLS=0 retrace voltage V255
WriteData(0x0055); //PCDIV = 0x55 5 clock div on high and low
WriteCommand(V_TIMING);//Frequency
WriteData(0x0007); //DIV = 0x7 fosc/12 (default)
WriteData(0x0029); //RTN = 0x29 41clk per line
WriteData(dssdev->panel.timings.vbp); //BP = 0x8 8 lines Back porch
WriteData(dssdev->panel.timings.vfp); //FP = 0x8 8 lines front porch
WriteData(0x0000); //LINEIN=0 PSNSET = 0
//mdelay(50);
WriteCommand(DRIVE_CONTROL);//panel driver
WriteData(0x0057); //NOWB = x5 NOW=x7 falling 4 clocks NOW = 8 clocks
WriteData(0x0000); //unused
WriteData(0x0005); //SEQGND = x5 gnd precharge = 5 clkcs
WriteData(0x0003); //SEQVCIL = x3 vcil precharge = 3 clks
//mdelay(50);
WriteCommand(DPI_POLARITY);//DPI signal polarity
WriteData(0x0004);
/*
*These values are correlated to the tianma panel we are driving.
*/
WriteCommand(GAMMA_SETTING_A);//gamma
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteCommand(GAMMA_SETTING_B);//gamma
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteCommand(GAMMA_SETTING_C);//gamma
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteData(0x0005);
WriteData(0x000d);
WriteData(0x001a);
WriteData(0x0028);
WriteData(0x003b);
WriteData(0x0054);//53
WriteData(0x003A);//39
WriteData(0x002F);//2E
WriteData(0x0029);//28
WriteData(0x0024);//23
WriteData(0x0020);
WriteData(0x000A);
WriteCommand(CHARGE_PUMP);//power setting
WriteData(0x0099);
WriteData(0x000A);//06
WriteData(0x0008);
WriteData(0x0020);
WriteData(0x000a);//29
WriteData(0x0004);
WriteData(0x0001);
WriteData(0x0000);
WriteData(0x0008);
WriteData(0x0001);
WriteData(0x0000);
WriteData(0x0006);
WriteData(0x0001);
WriteData(0x0000);
WriteData(0x0000);
WriteData(0x0020);
//mdelay(50);
WriteCommand(VCOM);//power setting
WriteData(0x0002);//00
WriteData(0x001C);//20
WriteData(0x001C);//20
WriteData(0x0030);//15VCOMDC=-1.0
//mdelay(50);
WriteCommand(SET_PIXEL_FORMAT);//pixel format seting
WriteData(0x0077); //24 bpp
WriteCommand(SET_COLUMN_ADDRESS);//resolution setting (col)
WriteData(0x0000);
WriteData(0x0000); //Start Column = 0
WriteData(((dssdev->panel.timings.y_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.y_res - 1) & 0xFF); //end column = y_res-1
WriteCommand(SET_PAGE_ADDRESS); //(row)
WriteData(0x0000);
WriteData(0); //Start row = 0
WriteData(((dssdev->panel.timings.x_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.x_res - 1) & 0xFF); //end row = x_res-1
WriteCommand(EXIT_SLEEP_MODE); //Exit Sleep
mdelay(6*FRAME_DURATION);
WriteCommand(SET_DISPLAY_ON); //Display On
mdelay(FRAME_DURATION);
WriteCommand(WRITE_MEMORY_START);
return 0;
}
static ssize_t r61529a1_test_pixel_read(struct device *dss, struct device_attribute *attr, char *buf)
{
char temp[4];
struct omap_dss_device *dssdev = (struct omap_dss_device*) dss;
/*set start address to where the FB starts writing
actually the bottom right of the image because it is rotated in sw */
//set_page_address
WriteCommand(SET_PAGE_ADDRESS);
WriteData(((dssdev->panel.timings.x_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.x_res - 1) & 0xFF); //start row = x_res-1
WriteData(((dssdev->panel.timings.x_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.x_res - 1) & 0xFF); //end row = x_res-1
//set_column_address
WriteCommand(SET_COLUMN_ADDRESS);
WriteData(((dssdev->panel.timings.y_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.y_res - 1) & 0xFF); //start column = y_res-1
WriteData(((dssdev->panel.timings.y_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.y_res - 1) & 0xFF); //end column = y_res-1
//send pixel read -- read back 3 bytes+ one dummy
omapdss_rfbi_read_data(READ_MEMORY_START, temp, 4);
//fix display addressing
WriteCommand(SET_COLUMN_ADDRESS);//resolution setting (col)
WriteData(0);
WriteData(0); //SC = 0
WriteData(((dssdev->panel.timings.y_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.y_res - 1) & 0xFF); //end column = y_res-1
WriteCommand(SET_PAGE_ADDRESS); //(row)
WriteData(0);
WriteData(0); //SR
WriteData(((dssdev->panel.timings.x_res - 1) & 0x100 ) >> 8);
WriteData((dssdev->panel.timings.x_res - 1) & 0xFF); //end row = x_res-1
return sprintf(buf, "%06x\n",(*((u32*)temp) & 0xFFFFFF00) >> 8);
}
static ssize_t r61529a1_display_id_read(struct device *unused, struct device_attribute *attr, char *buf)
{
char temp[5];
omapdss_rfbi_read_data(DEVICE_CODE_READ, temp, 5);
return sprintf(buf, "%08x\n",*((u32*)(temp+1)));
}
static ssize_t r61529a1_get_fps(struct device *unused, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", fps);
}
static int renesas_r61529a1_power_on(struct omap_dss_device *dssdev)
{
int r = 0;
dev_printk(KERN_INFO, &dssdev->dev, "Power on\n");
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (reg){
r = regulator_enable(reg);
}
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err0;
}
r = omapdss_rfbi_display_enable(dssdev);
if (r)
goto err1;
/* Setup external VSYNC triggering */
omap_rfbi_setup_te( OMAP_DSS_RFBI_TE_MODE_1,
20000000, /* HSYNC pulse 30uS */
300000000, /* VSYNC pulse 500uS */
0, 0, /* TE active high*/
0);
getrawmonotonic(&last_frame);
fps = -1;
r = init_display(dssdev);
return r;
err1:
omapdss_rfbi_display_disable(dssdev);
err0:
return r;
}
static void renesas_r61529a1_power_off(struct omap_dss_device *dssdev)
{
dev_printk(KERN_INFO, &dssdev->dev, "power off \n");
omapdss_rfbi_display_disable(dssdev);
WriteCommand(SET_DISPLAY_OFF); //Display off
mdelay(50);
WriteCommand(ENTER_SLEEP_MODE); //enter Sleep
mdelay(100);
WriteCommand(MCAP); //send enable manufacture commands
WriteData(0x0000);
WriteCommand(LOW_POWER_CONTROL); //enter deep standby
WriteData(0x0001);
mdelay(100);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (reg){
regulator_disable(reg);
}
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
}
static int renesas_r61529a1_probe(struct omap_dss_device *dssdev)
{
int status;
struct r61529a1_platform_data *pdata = (struct r61529a1_platform_data *)dssdev->data;
supply = pdata->regulator.vcc;
if (supply)
{
reg = regulator_get(&dssdev->dev, supply);
}
dssdev->panel.config = (OMAP_DSS_LCD_TFT |
OMAP_DSS_LCD_IPC |
OMAP_DSS_LCD_IEO |
OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS);
status = sysfs_create_group(&dssdev->dev.kobj, &r61529a1_attr_group);
if (status) goto err0;
status = gpio_request(pdata->reset.gpio, "r61529a1 reset");
if (status) goto err1;
status = gpio_direction_output(pdata->reset.gpio, !pdata->reset.inverted);
if (status) goto err2;
return 0;
err2:
gpio_free(pdata->reset.gpio);
err1:
sysfs_remove_group(&dssdev->dev.kobj, &r61529a1_attr_group);
err0:
return status;
}
static void renesas_r61529a1_remove(struct omap_dss_device *dssdev)
{
struct r61529a1_platform_data *pdata = (struct r61529a1_platform_data *)dssdev->data;
if (reg){
regulator_put(reg);
}
sysfs_remove_group(&dssdev->dev.kobj, &r61529a1_attr_group);
gpio_free(pdata->reset.gpio);
}
static int renesas_r61529a1_enable(struct omap_dss_device *dssdev)
{
int r = 0;
//dev_printk(KERN_INFO, &dssdev->dev, "r61529a1 enable\n");
r = renesas_r61529a1_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void renesas_r61529a1_disable(struct omap_dss_device *dssdev)
{
dev_printk(KERN_INFO, &dssdev->dev, "r61529a1 disable\n");
renesas_r61529a1_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static enum omap_dss_update_mode generic_get_update_mode(
struct omap_dss_device *dssdev)
{
return OMAP_DSS_UPDATE_AUTO;
}
static void update_done(void* data){
u16 w,h;
struct omap_dss_device *dssdev = (struct omap_dss_device *) data;
//queue up another frame
if(dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
{
dssdev->driver->get_resolution(dssdev, &w, &h);
renesas_r61529a1_update(dssdev, 0 , 0 , w, h);
}
//printk("r61529a1 update_done\n");
}
static int renesas_r61529a1_enable_te(struct omap_dss_device * dssdev, bool en)
{
/* Setup external VSYNC triggering */
omap_rfbi_setup_te( OMAP_DSS_RFBI_TE_MODE_1,
20000000, /* HSYNC pulse 30uS */
300000000, /* VSYNC pulse 500uS */
0, 0, /* TE active high*/
0);
omap_rfbi_enable_te(en, line); //480/2
return 0;
}
static int renesas_r61529a1_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -1;
#ifdef DISPLAY_PERFORMANCE
{
struct timespec t;
uint32_t deltaNs;
getrawmonotonic(&t);
if (t.tv_nsec < last_frame.tv_nsec)
{
deltaNs = 1000000000 + t.tv_nsec - last_frame.tv_nsec;
}
else //no overflow
{
deltaNs = t.tv_nsec - last_frame.tv_nsec;
}
fps = 1000 / (deltaNs / 1000000);
last_frame.tv_sec = t.tv_sec;
last_frame.tv_nsec = t.tv_nsec;
}
#endif
//dev_printk(KERN_INFO, &dssdev->dev, "update\n");
WriteCommand(WRITE_MEMORY_START);
renesas_r61529a1_enable_te(dssdev, 1);
omap_rfbi_update(dssdev, x, y, w, h, update_done, dssdev);
return 0;
}
static int renesas_r61529a1_suspend(struct omap_dss_device *dssdev)
{
dev_printk(KERN_INFO, &dssdev->dev, "r61529a1 suspend\n");
renesas_r61529a1_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int renesas_r61529a1_resume(struct omap_dss_device *dssdev)
{
int i,r = -1;
u16 w, h;
dev_printk(KERN_INFO, &dssdev->dev, "r61529a1 resume\n");
for(i = 0; i < WAKE_RETRY_COUNT && r != 0; i++)
{
r = renesas_r61529a1_power_on(dssdev);
}
if (--i > 0)
{
dev_printk(KERN_WARNING, &dssdev->dev,"resume_retries=%d\n",i);
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
//update the panel
dssdev->driver->get_resolution(dssdev, &w, &h);
renesas_r61529a1_update(dssdev, 0 , 0 , w, h);
return 0;
}
static struct omap_dss_driver generic_driver = {
.probe = renesas_r61529a1_probe,
.remove = renesas_r61529a1_remove,
.enable = renesas_r61529a1_enable,
.disable = renesas_r61529a1_disable,
.suspend = renesas_r61529a1_suspend,
.resume = renesas_r61529a1_resume,
.enable_te = renesas_r61529a1_enable_te,
.get_update_mode = generic_get_update_mode,
.update = renesas_r61529a1_update,
.driver = {
.name = "renesas_r61529a1",
.owner = THIS_MODULE,
},
};
static int __init renesas_r61529a1_drv_init(void)
{
return omap_dss_register_driver(&generic_driver);
}
static void __exit renesas_r61529a1_drv_exit(void)
{
omap_dss_unregister_driver(&generic_driver);
}
module_init(renesas_r61529a1_drv_init);
module_exit(renesas_r61529a1_drv_exit);
MODULE_AUTHOR("Nest, Inc.");
MODULE_DESCRIPTION(R61529A1_DRIVER_NAME);
MODULE_LICENSE("GPLv2");