From 8da3ebd6ff5f9b9373bb4006f3e10bf1d6a8bd5c Mon Sep 17 00:00:00 2001 From: hhb Date: Tue, 20 Nov 2012 18:01:01 +0800 Subject: [PATCH] add RGB to MIPI tc358768 driver and reference lcd driver --- drivers/video/display/screen/Kconfig | 3 + drivers/video/display/screen/Makefile | 1 + .../video/display/screen/lcd_tl5001_mipi.c | 406 +++++++++++ drivers/video/display/transmitter/Kconfig | 5 + drivers/video/display/transmitter/Makefile | 1 + drivers/video/display/transmitter/tc358768.c | 689 ++++++++++++++++++ drivers/video/display/transmitter/tc358768.h | 99 +++ 7 files changed, 1204 insertions(+) create mode 100644 drivers/video/display/screen/lcd_tl5001_mipi.c create mode 100644 drivers/video/display/transmitter/tc358768.c create mode 100644 drivers/video/display/transmitter/tc358768.h diff --git a/drivers/video/display/screen/Kconfig b/drivers/video/display/screen/Kconfig index 0e3b0b6f8566..b31bd70ff39b 100755 --- a/drivers/video/display/screen/Kconfig +++ b/drivers/video/display/screen/Kconfig @@ -114,6 +114,9 @@ config LCD_HSD07PFW1 config LCD_I30_800X480 bool "lcd I30" +config LCD_TL5001_MIPI + bool "TL5001 720X1280" + endchoice diff --git a/drivers/video/display/screen/Makefile b/drivers/video/display/screen/Makefile index 5c938df3c816..bf64b2c23fd0 100755 --- a/drivers/video/display/screen/Makefile +++ b/drivers/video/display/screen/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_LCD_HJ080NA) += lcd_hj080na.o obj-$(CONFIG_LCD_HJ101NA) += lcd_hj101na.o obj-$(CONFIG_LCD_AUTO) += lcd_auto.o obj-$(CONFIG_LCD_I30_800X480) += lcd_I30_800x480.o +obj-$(CONFIG_LCD_TL5001_MIPI) += lcd_tl5001_mipi.o diff --git a/drivers/video/display/screen/lcd_tl5001_mipi.c b/drivers/video/display/screen/lcd_tl5001_mipi.c new file mode 100644 index 000000000000..b9b4135d1a46 --- /dev/null +++ b/drivers/video/display/screen/lcd_tl5001_mipi.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2012 ROCKCHIP, Inc. + * + * author: hhb@rock-chips.com + * creat date: 2012-04-19 + * route:drivers/video/display/screen/lcd_hj050na_06a.c + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../transmitter/tc358768.h" + +/* Base */ +#define OUT_TYPE SCREEN_RGB +#define OUT_FACE OUT_P888 +#define BYTE_PP 3 //bytes per pixel + + +#define OUT_CLK 65000000 // in fact it is 61384615 +#define LCDC_ACLK 300000000 + +/* Timing */ +#define H_PW 10 +#define H_BP 20 +#define H_VD 720 +#define H_FP 82 + +#define V_PW 8 +#define V_BP 6 +#define V_VD 1280 +#define V_FP 4 + + +#define LCD_WIDTH 62 //uint mm the lenth of lcd active area +#define LCD_HEIGHT 111 +/* Other */ +#define VSYNC_POL 0 +#define HSYNC_POL VSYNC_POL +#define DCLK_POL 1 +#define DEN_POL 0 //positive +#define SWAP_RB 0 + + +#define LCD_TEST 0 +#define CONFIG_DEEP_STANDBY_MODE 0 +#define CONFIG_TC358768_INIT_MODE 0 //1:ARRAY 0:FUNCTION + +#define dsi_init(data) mipi_dsi.dsi_init(data, ARRAY_SIZE(data)) +#define dsi_send_dcs_packet(data) mipi_dsi.dsi_send_dcs_packet(data, ARRAY_SIZE(data)) +#define dsi_hs_start(data) mipi_dsi.dsi_hs_start(data, ARRAY_SIZE(data)) + +#define lap_define ktime_t k0,k1; +#define lap_start k0 = ktime_get(); +#define lap_end { k1 = ktime_get(); k1 = ktime_sub(k1, k0); } + +static struct rk29lcd_info *gLcd_info = NULL; +struct mipi_dsi_t mipi_dsi; +struct tc358768_t *lcd_tc358768 = NULL; + +int lcd_init(void); +int lcd_standby(u8 enable); + + +#if CONFIG_TC358768_INIT_MODE +struct spi_cmd_data32 { + unsigned int delay; + unsigned int value; +}; + +struct spi_cmd_data32 TC358768XBG_INIT[] = { + + {0xffffffff, 0xffffffff} +}; + +#else + +//high speed mode +static unsigned int re_initialize[] = { + + +}; + +static unsigned int initialize[] = { +// ************************************************** +// Initizlize -> Display On after power-on +// ************************************************** +// ************************************************** +// Power on TC358768XBG according to recommended power-on sequence +// Relase reset (RESX="H") +// Start input REFCK and PCLK +// ************************************************** +// ************************************************** +// TC358768XBG Software Reset +// ************************************************** + 0x00020001, //SYSctl, S/W Reset + 10, + 0x00020000, //SYSctl, S/W Reset release + +// ************************************************** +// TC358768XBG PLL,Clock Setting +// ************************************************** + 0x00161063, //PLL Control Register 0 (PLL_PRD,PLL_FBD) + 0x00180603, //PLL_FRS,PLL_LBWS, PLL oscillation enable + 1000, + 0x00180613, //PLL_FRS,PLL_LBWS, PLL clock out enable + +// ************************************************** +// TC358768XBG DPI Input Control +// ************************************************** + 0x00060032, //FIFO Control Register + +// ************************************************** +// TC358768XBG D-PHY Setting +// ************************************************** + 0x01400000, //D-PHY Clock lane enable + 0x01420000, // + 0x01440000, //D-PHY Data lane0 enable + 0x01460000, // + 0x01480000, //D-PHY Data lane1 enable + 0x014A0000, // + 0x014C0000, //D-PHY Data lane2 enable + 0x014E0000, // + 0x01500000, //D-PHY Data lane3 enable + 0x01520000, // + +// ************************************************** +// TC358768XBG DSI-TX PPI Control +// ************************************************** + 0x021009C4, //LINEINITCNT + 0x02120000, // + 0x02140002, //LPTXTIMECNT + 0x02160000, // + 0x02200002, //THS_HEADERCNT + 0x02220000, // + 0x02244268, //TWAKEUPCNT + 0x02260000, // + 0x022C0001, //THS_TRAILCNT + 0x022E0000, // + 0x02300005, //HSTXVREGCNT + 0x02320000, // + 0x0234001F, //HSTXVREGEN enable + 0x02360000, // + 0x02380001, //DSI clock Enable/Disable during LP + 0x023A0000, // + 0x023C0001, //BTACNTRL1 + 0x023E0002, // + 0x02040001, //STARTCNTRL + 0x02060000, // + +// ************************************************** +// TC358768XBG DSI-TX Timing Control +// ************************************************** + 0x06200001, //Sync Pulse/Sync Event mode setting + 0x0622000E, //V Control Register1 + 0x06240006, //V Control Register2 + 0x06260500, //V Control Register3 + 0x0628005E, //H Control Register1 + 0x062A003F, //H Control Register2 + 0x062C0870, //H Control Register3 + + 0x05180001, //DSI Start + 0x051A0000, // + +}; + + + +static unsigned int start_dsi_hs_mode[] = { + +// ************************************************** +// Set to HS mode +// ************************************************** + 0x05000087, //DSI lane setting, DSI mode=HS + 0x0502A300, //bit set + 0x05008000, //Switch to DSI mode + 0x0502C300, // + +// ************************************************** +// Host: RGB(DPI) input start +// ************************************************** + + 0x00080037, //DSI-TX Format setting + 0x0050003E, //DSI-TX Pixel stream packet Data Type setting + 0x00040044 //Configuration Control Register + + +}; + +#endif + +static unsigned char boe_set_extension_command[] = {0xB9, 0xFF, 0x83, 0x94}; +static unsigned char boe_set_MIPI_ctrl[] = {0xBA, 0x13}; +static unsigned char boe_set_power[] = {0xB1, 0x7C, 0x00, 0x34, 0x09, 0x01, 0x11, 0x11, 0x36, 0x3E, 0x26, 0x26, 0x57, 0x12, 0x01, 0xE6}; +static unsigned char boe_setcyc[] = {0xB4, 0x00, 0x00, 0x00, 0x05, 0x06, 0x41, 0x42, 0x02, 0x41, 0x42, 0x43, 0x47, 0x19, 0x58, + 0x60, 0x08, 0x85, 0x10}; +static unsigned char boe_config05[] = {0xC7, 0x00, 0x20}; +static unsigned char boe_set_gip[] = {0xD5,0x4C,0x01,0x00,0x01,0xCD,0x23,0xEF,0x45,0x67,0x89,0xAB,0x11,0x00,0xDC,0x10,0xFE,0x32, + 0xBA,0x98,0x76,0x54,0x00,0x11,0x40}; + +//static unsigned char boe_set_panel[] = {0xCC, 0x01}; +//static unsigned char boe_set_vcom[] = {0xB6, 0x2a}; +static unsigned char boe_set_panel[] = {0xCC, 0x05}; +static unsigned char boe_set_vcom[] = {0xB6, 0x31}; + +static unsigned char boe_set_gamma[] = {0xE0,0x24,0x33,0x36,0x3F,0x3f,0x3f,0x3c,0x56,0x05,0x0C,0x0e,0x11,0x13,0x12,0x14,0x12,0x1e, + 0x24,0x33,0x36,0x3F,0x3f,0x3F,0x3c,0x56,0x05,0x0c, 0x0e,0x11,0x13,0x12,0x14,0x12, 0x1e}; // +static unsigned char boe_set_addr_mode[] = {0x36, 0x00}; +static unsigned char boe_set_pixel[] = {0x3a, 0x60}; + +static unsigned char boe_enter_sleep_mode[] = {0x10}; +static unsigned char boe_exit_sleep_mode[] = {0x11}; +static unsigned char boe_set_diaplay_on[] = {0x29}; +static unsigned char boe_set_diaplay_off[] = {0x28}; +static unsigned char boe_enter_invert_mode[] = {0x21}; +static unsigned char boe_all_pixel_on[] = {0x23}; +static unsigned char boe_set_id[] = {0xc3, 0xaa, 0x55, 0xee}; + + +void lcd_power_on(void) { + + +} + +void lcd_power_off(void) { + + +} +#if LCD_TEST +void lcd_test(void) { + u8 buf[8]; + printk("**mipi lcd test\n"); + buf[0] = 0x0c; + mipi_dsi.dsi_read_dcs_packet(buf, 1); + printk("**Get_pixel_format 0x0c:%02x\n", buf[0]); + buf[0] = 0x0a; + mipi_dsi.dsi_read_dcs_packet(buf, 1); + printk("**Get_power_mode 0x0a:%02x\n", buf[0]); + buf[0] = 0x0f; + mipi_dsi.dsi_read_dcs_packet(buf, 1); + printk("**Get_diagnostic_result 0x0f:%02x\n", buf[0]); + buf[0] = 0x09; + mipi_dsi.dsi_read_dcs_packet(buf, 4); + printk("**Read Display Status 0x09:%02x,%02x,%02x,%02x\n", buf[0],buf[1],buf[2],buf[3]); +} +#endif + + + +static unsigned char boe_set_wrdisbv[] = {0x51, 0xff}; +static unsigned char boe_set_wrctrld[] = {0x53, 0x24}; +static unsigned char boe_set_wrcabc[] = {0x55, 0x02}; +static unsigned char boe_set_wrcabcmb[] = {0x5e, 0x0}; +static unsigned char boe_set_cabc[] = {0xc9, 0x0d, 0x01, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0}; +static unsigned char boe_set_cabc_gain[] = {0xca, 0x32, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20}; + + +void lcd_cabc(u8 brightness) { + +} + + + +int lcd_init(void) +{ + + int i = 0; + lap_define + //power on + lcd_tc358768->power_up(NULL); + + if(gLcd_info) + gLcd_info->io_init(); + + i = 0; + lap_start + //Re-Initialize +#if CONFIG_TC358768_INIT_MODE + i = 0; + while (1) { + if(TC358768XBG_INIT[i].delay == 0xffffffff) + break; + tc358768_wr_reg_32bits_delay(TC358768XBG_INIT[i].delay, TC358768XBG_INIT[i].value); + i++; + } +#else + dsi_init(initialize); + + + //lcd init + dsi_send_dcs_packet(boe_exit_sleep_mode); + msleep(150); + dsi_send_dcs_packet(boe_set_extension_command); + msleep(1); + dsi_send_dcs_packet(boe_set_MIPI_ctrl); + msleep(1); + dsi_send_dcs_packet(boe_set_power); + msleep(1); + dsi_send_dcs_packet(boe_setcyc); + msleep(1); + dsi_send_dcs_packet(boe_set_vcom); + msleep(1); + dsi_send_dcs_packet(boe_set_panel); + msleep(1); + dsi_send_dcs_packet(boe_set_gip); + msleep(1); + dsi_send_dcs_packet(boe_set_gamma); + msleep(1); + dsi_send_dcs_packet(boe_set_addr_mode); + msleep(1); + dsi_send_dcs_packet(boe_set_diaplay_on); +#if LCD_TEST + lcd_test(); +#endif + dsi_hs_start(start_dsi_hs_mode); + + msleep(10); +#endif + lap_end + printk(">>time:%lld\n", k1.tv64); + return 0; + +} + +int lcd_standby(u8 enable) +{ + //int ret = 0; + if(enable) { + + printk("suspend lcd\n"); + //power down + if(gLcd_info) + gLcd_info->io_deinit(); + + lcd_tc358768->power_down(NULL); + + } else { + lcd_init(); + } + + return 0; +} + +void set_lcd_info(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info ) +{ + /* screen type & face */ + screen->type = OUT_TYPE; + screen->face = OUT_FACE; + + /* Screen size */ + screen->x_res = H_VD; + screen->y_res = V_VD; + + screen->width = LCD_WIDTH; + screen->height = LCD_HEIGHT; + + /* Timing */ + screen->lcdc_aclk = LCDC_ACLK; + screen->pixclock = OUT_CLK; + screen->left_margin = H_BP; + screen->right_margin = H_FP; + screen->hsync_len = H_PW; + screen->upper_margin = V_BP; + screen->lower_margin = V_FP; + screen->vsync_len = V_PW; + + /* Pin polarity */ + screen->pin_hsync = HSYNC_POL; + screen->pin_vsync = VSYNC_POL; + screen->pin_den = DEN_POL; + screen->pin_dclk = DCLK_POL; + + /* Swap rule */ + screen->swap_rb = SWAP_RB; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = lcd_init; + screen->standby = lcd_standby; + + if(lcd_info) + gLcd_info = lcd_info; + + if(tc358768_init(&mipi_dsi) == 0) + lcd_tc358768 = (struct tc358768_t *)mipi_dsi.chip; + else + printk("%s: %s:%d",__FILE__, __func__, __LINE__); + +} diff --git a/drivers/video/display/transmitter/Kconfig b/drivers/video/display/transmitter/Kconfig index 5565f72d0cf8..bd4421f2ef76 100644 --- a/drivers/video/display/transmitter/Kconfig +++ b/drivers/video/display/transmitter/Kconfig @@ -4,3 +4,8 @@ config RK610_LVDS default y if MFD_RK610 help Support Jetta(RK610) to output LCD1 and LVDS. + +config TC358768_RGB2MIPI + bool "toshiba RGB to MIPI DSI" + help + "a chip that change RGB interface parallel signal into DSI serial signal" diff --git a/drivers/video/display/transmitter/Makefile b/drivers/video/display/transmitter/Makefile index 75adab5b4151..65454d87f7ee 100644 --- a/drivers/video/display/transmitter/Makefile +++ b/drivers/video/display/transmitter/Makefile @@ -2,3 +2,4 @@ # Makefile for the jetta tv control. # obj-$(CONFIG_RK610_LVDS) += rk610_lcd.o +obj-$(CONFIG_TC358768_RGB2MIPI) += tc358768.o diff --git a/drivers/video/display/transmitter/tc358768.c b/drivers/video/display/transmitter/tc358768.c new file mode 100644 index 000000000000..7beb9e4ae8ab --- /dev/null +++ b/drivers/video/display/transmitter/tc358768.c @@ -0,0 +1,689 @@ +/* + * Copyright (C) 2012 ROCKCHIP, Inc. + * + * author: hhb@rock-chips.com + * create date: 2012-10-26 + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include "tc358768.h" + +#if 0 +#define dsi_debug printk +#else +#define dsi_debug(fmt...) do { } while (0) +#endif + +#ifdef CONFIG_TC358768_I2C +struct tc358768_t *tc358768 = NULL; +struct i2c_client *tc358768_client = NULL; +struct mipi_dsi_t *dsi; + + +u32 i2c_write_32bits(u32 value) +{ + struct i2c_msg msgs; + int ret = -1; + char buf[4]; + buf[0] = value>>24; + buf[1] = value>>16; + buf[2] = value>>8; + buf[3] = value; + + msgs.addr = tc358768_client->addr; + msgs.flags = tc358768_client->flags; + msgs.len = 4; + msgs.buf = buf; + msgs.scl_rate = CONFIG_TC358768_I2C_CLK; + msgs.udelay = tc358768_client->udelay; + + ret = i2c_transfer(tc358768_client->adapter, &msgs, 1); + if(ret < 0) + printk("%s:i2c_transfer fail =%d\n",__func__, ret); + return ret; +} + +u32 i2c_read_32bits(u32 value) +{ + struct i2c_msg msgs[2]; + int ret = -1; + char buf[4]; + buf[0] = value>>8; + buf[1] = value; + + msgs[0].addr = tc358768_client->addr; + msgs[0].flags = tc358768_client->flags; + msgs[0].len = 2; + msgs[0].buf = buf; + msgs[0].scl_rate = CONFIG_TC358768_I2C_CLK; + msgs[0].udelay = tc358768_client->udelay; + + msgs[1].addr = tc358768_client->addr; + msgs[1].flags = tc358768_client->flags | I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = buf; + msgs[1].scl_rate = CONFIG_TC358768_I2C_CLK; + msgs[1].udelay = tc358768_client->udelay; + + ret = i2c_transfer(tc358768_client->adapter, msgs, 2); + if(ret < 0) + printk("%s:i2c_transfer fail =%d\n",__func__, ret); + else + ret = (buf[0]<<8) | buf[1]; + + return ret; +} + + +int tc358768_gpio_init(void *data) { + int ret = 0; + struct reset_t *reset = &tc358768->reset; + struct power_t *vdd = &tc358768->vddc; + if(reset->reset_pin > INVALID_GPIO) { + ret = gpio_request(reset->reset_pin, "tc358768_reset"); + if (ret != 0) { + gpio_free(reset->reset_pin); + printk("%s: request TC358768_RST_PIN error\n", __func__); + } else { + if(reset->mux_name) + rk30_mux_api_set(reset->mux_name, reset->mux_mode); + gpio_direction_output(reset->reset_pin, reset->effect_value); + } + } + + if(vdd->enable_pin > INVALID_GPIO) { + ret = gpio_request(vdd->enable_pin, "tc358768_vddc"); + if (ret != 0) { + gpio_free(vdd->enable_pin); + printk("%s: request TC358768_vddc_PIN error\n", __func__); + } else { + if(vdd->mux_name) + rk30_mux_api_set(vdd->mux_name, vdd->mux_mode); + gpio_direction_output(vdd->enable_pin, !vdd->effect_value); + } + } + + vdd = &tc358768->vddio; + if(vdd->enable_pin > INVALID_GPIO) { + ret = gpio_request(vdd->enable_pin, "tc358768_vddio"); + if (ret != 0) { + gpio_free(vdd->enable_pin); + printk("%s: request TC358768_vddio_PIN error\n", __func__); + } else { + if(vdd->mux_name) + rk30_mux_api_set(vdd->mux_name, vdd->mux_mode); + gpio_direction_output(vdd->enable_pin, !vdd->effect_value); + } + } + + vdd = &tc358768->vdd_mipi; + if(vdd->enable_pin > INVALID_GPIO) { + ret = gpio_request(vdd->enable_pin, "tc358768_vdd_mipi"); + if (ret != 0) { + gpio_free(vdd->enable_pin); + printk("%s: request TC358768_vdd_mipi_PIN error\n", __func__); + } else { + if(vdd->mux_name) + rk30_mux_api_set(vdd->mux_name, vdd->mux_mode); + gpio_direction_output(vdd->enable_pin, !vdd->effect_value); + } + } + return 0; + +} + +int tc358768_gpio_deinit(void *data) { + struct reset_t *reset = &tc358768->reset; + struct power_t *vdd = &tc358768->vddc; + gpio_free(reset->reset_pin); + + vdd = &tc358768->vddio; + gpio_free(vdd->enable_pin); + + vdd = &tc358768->vdd_mipi; + gpio_free(vdd->enable_pin); + return 0; +} + +int tc358768_reset(void *data) { + int ret = 0; + struct reset_t *reset = &tc358768->reset; + if(reset->reset_pin <= INVALID_GPIO) + return -1; + gpio_set_value(reset->reset_pin, reset->effect_value); + if(reset->time_before_reset <= 0) + msleep(1); + else + msleep(reset->time_before_reset); + + gpio_set_value(reset->reset_pin, !reset->effect_value); + if(reset->time_after_reset <= 0) + msleep(5); + else + msleep(reset->time_after_reset); + return ret; +} + +int tc358768_vdd_enable(void *data) { + int ret = 0; + struct power_t *vdd = (struct power_t *)data; + if(vdd->enable_pin > INVALID_GPIO) { + gpio_set_value(vdd->enable_pin, vdd->effect_value); + } else { + //for other control + } + return ret; +} + +int tc358768_vdd_disable(void *data) { + int ret = 0; + struct power_t *vdd = (struct power_t *)data; + + if(vdd->enable_pin > INVALID_GPIO) { + gpio_set_value(vdd->enable_pin, !vdd->effect_value); + } else { + //for other control + } + return ret; +} + + +int tc358768_power_up(void *data) { + int ret = 0; + struct tc358768_t *tc = (struct tc358768_t *)tc358768; + + tc->vddc.enable(&tc->vddc); + tc->vdd_mipi.enable(&tc->vdd_mipi); + tc->vddio.enable(&tc->vddio); + tc->reset.do_reset(&tc->reset); + + return ret; +} + +int tc358768_power_down(void *data) { + int ret = 0; + struct tc358768_t *tc = (struct tc358768_t *)tc358768; + + tc->vddio.disable(&tc->vddio); + tc->vdd_mipi.disable(&tc->vdd_mipi); + tc->vddc.disable(&tc->vddc); + + return ret; +} + +static int tc358768_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + int ret = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_I2C\n"); + return -EIO; + } + tc358768 = (struct tc358768_t *)client->dev.platform_data; + if(!tc358768) { + ret = -1; + printk("%s:%d tc358768 is null\n", __func__, __LINE__); + return ret; + } + + tc358768_client = client; + if(!tc358768_client) { + ret = -1; + printk("%s:%d tc358768_client is null\n", __func__, __LINE__); + return ret; + } + + if(!tc358768->gpio_init) + tc358768->gpio_init = tc358768_gpio_init; + + if(!tc358768->gpio_deinit) + tc358768->gpio_deinit = tc358768_gpio_deinit; + + if(!tc358768->power_up) + tc358768->power_up = tc358768_power_up; + if(!tc358768->power_down) + tc358768->power_down = tc358768_power_down; + + if(!tc358768->reset.do_reset) + tc358768->reset.do_reset = tc358768_reset; + + if(!tc358768->vddc.enable) + tc358768->vddc.enable = tc358768_vdd_enable; + if(!tc358768->vddc.disable) + tc358768->vddc.disable = tc358768_vdd_disable; + + if(!tc358768->vddio.enable) + tc358768->vddio.enable = tc358768_vdd_enable; + if(!tc358768->vddio.disable) + tc358768->vddio.disable = tc358768_vdd_disable; + + if(!tc358768->vdd_mipi.enable) + tc358768->vdd_mipi.enable = tc358768_vdd_enable; + if(!tc358768->vdd_mipi.disable) + tc358768->vdd_mipi.disable = tc358768_vdd_disable; + + dsi->chip = tc358768; + + return ret; +} +static int tc358768_remove(struct i2c_client *client) +{ + tc358768_client = NULL; + tc358768 = NULL; + return 0; +} + +static const struct i2c_device_id tc358768_id[] = { + {"tc358768", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc358768_id); + +static struct i2c_driver tc358768_driver = { + .probe = tc358768_probe, + .remove = tc358768_remove, + .id_table = tc358768_id, + .driver = { + .name = "tc358768", + }, +}; +#else + +u32 spi_read_32bits(u32 addr) +{ + unsigned int i = 32; + //a frame starts + CS_CLR(); + CLK_SET(); + + addr <<= 16; + addr &= 0xfffe0000; + addr |= 0x00010000; + + udelay(2); + while(i--) { + CLK_CLR(); + if(addr & 0x80000000) + TXD_SET(); + else + TXD_CLR(); + addr <<= 1; + udelay(2); + CLK_SET(); + udelay(2); + } + //a frame ends + CS_SET(); + + + udelay(2); + CS_CLR(); + addr = 0xfffe0000; + i = 16; + while(i--) { + CLK_CLR(); + if(addr & 0x80000000) + TXD_SET(); + else + TXD_CLR(); + addr <<= 1; + udelay(2); + CLK_SET(); + udelay(2); + } + + TXD_SET(); + + addr = 0; + i = 16; + while(i--) { + CLK_CLR(); + udelay(1); + CLK_SET(); + udelay(1); + if (gpio_get_value(gLcd_info->rxd_pin) == 1) + addr |= 1 << i; + udelay(1); + } + CS_SET(); + + return addr; +} + + +//32 bits per frame +u32 spi_write_32bits(u32 value) +{ + int i = 32; + + //a frame starts + CS_CLR(); + CLK_SET(); + + while(i--) { + CLK_CLR(); + if(value & 0x80000000) + TXD_SET(); + else + TXD_CLR(); + value <<= 1; + CLK_SET(); + } + //a frame ends + CS_SET(); + + return 0; +} + +#endif + +u32 tc358768_wr_reg_32bits(u32 data) { +#ifdef CONFIG_TC358768_I2C + i2c_write_32bits(data); +#else + spi_write_32bits(data); +#endif + return 0; +} + + +u32 tc358768_wr_reg_32bits_delay(u32 delay, u32 data) { + //wait a minute according to the source format + if(delay < 20000) + udelay(delay); + else { + mdelay(delay/1000); + } + +#ifdef CONFIG_TC358768_I2C + i2c_write_32bits(data); +#else + spi_write_32bits(data); +#endif + return 0; +} + + + +u32 tc358768_rd_reg_32bits(u32 addr) { +#ifdef CONFIG_TC358768_I2C + return i2c_read_32bits(addr); +#else + return spi_read_32bits(addr); +#endif +} + + + +void tc_print(u32 addr) { + dsi_debug("+++++++++++addr->%04x: %04x\n", addr, tc358768_rd_reg_32bits(addr)); +} + +#define tc358768_wr_regs_32bits(reg_array) _tc358768_wr_regs_32bits(reg_array, ARRAY_SIZE(reg_array)) +int _tc358768_wr_regs_32bits(unsigned int reg_array[], int n) { + + int i = 0; + dsi_debug("%s:%d\n", __func__, n); + for(i = 0; i < n; i++) { + if(reg_array[i] < 0x00020000) { + if(reg_array[i] < 20000) + udelay(reg_array[i]); + else { + mdelay(reg_array[i]/1000); + } + } else { + tc358768_wr_reg_32bits(reg_array[i]); + } + } + return 0; +} + +int tc358768_command_tx_less8bytes(unsigned char type, unsigned char *regs, int n) { + int i = 0; + unsigned int command[] = { + 0x06020000, + 0x06040000, + 0x06100000, + 0x06120000, + 0x06140000, + 0x06160000, + }; + + if(n <= 2) + command[0] |= 0x1000; //short packet + else { + command[0] |= 0x4000; //long packet + command[1] |= n; //word count byte + } + command[0] |= type; //data type + + //dsi_debug("*cmd:\n"); + //dsi_debug("0x%08x\n", command[0]); + //dsi_debug("0x%08x\n", command[1]); + + for(i = 0; i < (n + 1)/2; i++) { + command[i+2] |= regs[i*2]; + if((i*2 + 1) < n) + command[i+2] |= regs[i*2 + 1] << 8; + dsi_debug("0x%08x\n", command[i+2]); + } + + _tc358768_wr_regs_32bits(command, (n + 1)/2 + 2); + tc358768_wr_reg_32bits(0x06000001); //Packet Transfer + //wait until packet is out + i = 100; + while(tc358768_rd_reg_32bits(0x0600) & 0x01) { + if(i-- == 0) + break; + tc_print(0x0600); + } + //udelay(50); + return 0; +} + +int tc358768_command_tx_more8bytes_hs(unsigned char type, unsigned char regs[], int n) { + + int i = 0; + unsigned int dbg_data = 0x00E80000, temp = 0; + unsigned int command[] = { + 0x05000080, //HS data 4 lane, EOT is added + 0x0502A300, + 0x00080001, + 0x00500000, //Data ID setting + 0x00220000, //Transmission byte count= byte + 0x00E08000, //Enable I2C/SPI write to VB + 0x00E20048, //Total word count = 0x48 (max 0xFFF). This value should be adjusted considering trade off between transmission time and transmission start/stop time delay + 0x00E4007F, //Vertical blank line = 0x7F + }; + + + command[3] |= type; //data type + command[4] |= n & 0xffff; //Transmission byte count + + tc358768_wr_regs_32bits(command); + + for(i = 0; i < (n + 1)/2; i++) { + temp = dbg_data | regs[i*2]; + if((i*2 + 1) < n) + temp |= (regs[i*2 + 1] << 8); + //dsi_debug("0x%08x\n", temp); + tc358768_wr_reg_32bits(temp); + } + if((n % 4 == 1) || (n % 4 == 2)) //4 bytes align + tc358768_wr_reg_32bits(dbg_data); + + tc358768_wr_reg_32bits(0x00E0C000); //Start command transmisison + tc358768_wr_reg_32bits(0x00E00000); //Stop command transmission. This setting should be done just after above setting to prevent multiple output + udelay(200); + //Re-Initialize + //tc358768_wr_regs_32bits(re_initialize); + return 0; +} + +//low power mode only for tc358768a +int tc358768_command_tx_more8bytes_lp(unsigned char type, unsigned char regs[], int n) { + + int i = 0; + unsigned int dbg_data = 0x00E80000, temp = 0; + unsigned int command[] = { + 0x00080001, + 0x00500000, //Data ID setting + 0x00220000, //Transmission byte count= byte + 0x00E08000, //Enable I2C/SPI write to VB + }; + + command[1] |= type; //data type + command[2] |= n & 0xffff; //Transmission byte count + + tc358768_wr_regs_32bits(command); + + for(i = 0; i < (n + 1)/2; i++) { + temp = dbg_data | regs[i*2]; + if((i*2 + 1) < n) + temp |= (regs[i*2 + 1] << 8); + //dsi_debug("0x%08x\n", temp); + tc358768_wr_reg_32bits(temp); + + } + if((n % 4 == 1) || (n % 4 == 2)) //4 bytes align + tc358768_wr_reg_32bits(dbg_data); + + tc358768_wr_reg_32bits(0x00E0E000); //Start command transmisison + udelay(1000); + tc358768_wr_reg_32bits(0x00E02000); //Keep Mask High to prevent short packets send out + tc358768_wr_reg_32bits(0x00E00000); //Stop command transmission. This setting should be done just after above setting to prevent multiple output + udelay(10); + return 0; +} + +int _tc358768_send_packet(unsigned char type, unsigned char regs[], int n) { + + if(n <= 8) { + tc358768_command_tx_less8bytes(type, regs, n); + } else { + //tc358768_command_tx_more8bytes_hs(type, regs, n); + tc358768_command_tx_more8bytes_lp(type, regs, n); + } + return 0; +} + +int tc358768_send_packet(unsigned char type, unsigned char regs[], int n) { + return _tc358768_send_packet(type, regs, n); +} + + +/* +The DCS is separated into two functional areas: the User Command Set and the Manufacturer Command +Set. Each command is an eight-bit code with 00h to AFh assigned to the User Command Set and all other +codes assigned to the Manufacturer Command Set. +*/ +int _mipi_dsi_send_dcs_packet(unsigned char regs[], int n) { + + unsigned char type = 0; + if(n == 1) { + type = DTYPE_DCS_SWRITE_0P; + } else if (n == 2) { + type = DTYPE_DCS_SWRITE_1P; + } else if (n > 2) { + type = DTYPE_DCS_LWRITE; + } + _tc358768_send_packet(type, regs, n); + return 0; +} + +int mipi_dsi_send_dcs_packet(unsigned char regs[], int n) { + return _mipi_dsi_send_dcs_packet(regs, n); +} + + +int _tc358768_rd_lcd_regs(unsigned char type, char comd, int size, unsigned char* buf) { + + unsigned char regs[8]; + u32 count = 0, data30, data32; + regs[0] = size; + regs[1] = 0; + tc358768_command_tx_less8bytes(0x37, regs, 2); + tc358768_wr_reg_32bits(0x05040010); + tc358768_wr_reg_32bits(0x05060000); + regs[0] = comd; + tc358768_command_tx_less8bytes(type, regs, 1); + + while (!(tc358768_rd_reg_32bits(0x0410) & 0x20)){ + printk("error 0x0410:%04x\n", tc358768_rd_reg_32bits(0x0410)); + msleep(1); + if(count++ > 10) { + break; + } + } + + data30 = tc358768_rd_reg_32bits(0x0430); //data id , word count[0:7] + //printk("0x0430:%04x\n", data30); + data32 = tc358768_rd_reg_32bits(0x0432); //word count[8:15] ECC + //printk("0x0432:%04x\n", data32); + + while(size > 0) { + data30 = tc358768_rd_reg_32bits(0x0430); + //printk("0x0430:%04x\n", data30); + data32 = tc358768_rd_reg_32bits(0x0432); + //printk("0x0432:%04x\n", data32); + + if(size-- > 0) + *buf++ = (u8)data30; + else + break; + if(size-- > 0) + *buf++ = (u8)(data30 >> 8); + else + break; + if(size-- > 0) { + *buf++ = (u8)data32; + if(size-- > 0) + *buf++ = (u8)(data32 >> 8); + } + } + + data30 = tc358768_rd_reg_32bits(0x0430); + //printk("0x0430:%04x\n", data30); + data32 = tc358768_rd_reg_32bits(0x0432); + //printk("0x0432:%04x\n", data32); + return 0; +} + +int mipi_dsi_read_dcs_packet(unsigned char *data, int n) { + //DCS READ + _tc358768_rd_lcd_regs(0x06, *data, n, data); + return 0; +} + + +int tc358768_init(struct mipi_dsi_t *pram) { + int ret = 0; + dsi = pram; + if(!dsi) + return -1; + dsi->id = 0x4401; + dsi->dsi_init = _tc358768_wr_regs_32bits; + dsi->dsi_hs_start = _tc358768_wr_regs_32bits; + dsi->dsi_send_dcs_packet = mipi_dsi_send_dcs_packet; + dsi->dsi_read_dcs_packet = mipi_dsi_read_dcs_packet; +#ifdef CONFIG_TC358768_I2C + i2c_add_driver(&tc358768_driver); +#endif + tc358768_gpio_init(NULL); + return 0; +exit_init: + //tc358768_power_down(NULL); + tc358768_gpio_deinit(NULL); + return -1; +} diff --git a/drivers/video/display/transmitter/tc358768.h b/drivers/video/display/transmitter/tc358768.h new file mode 100644 index 000000000000..ca3744cca7e9 --- /dev/null +++ b/drivers/video/display/transmitter/tc358768.h @@ -0,0 +1,99 @@ + +#ifndef TC358768_H_ +#define TC358768_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + + +//DSI DATA TYPE +#define DTYPE_DCS_SWRITE_0P 0X05 +#define DTYPE_DCS_SWRITE_1P 0X15 +#define DTYPE_DCS_LWRITE 0X39 +#define DTYPE_GEN_LWRITE 0X29 +#define DTYPE_GEN_SWRITE_2P 0X23 +#define DTYPE_GEN_SWRITE_1P 0X13 +#define DTYPE_GEN_SWRITE_0P 0X03 + +#define CONFIG_TC358768_I2C 1 +#define CONFIG_TC358768_I2C_CLK 400*1000 + +#if !CONFIG_TC358768_I2C +/* define spi write command and data interface function */ +#define TXD_PORT gLcd_info->txd_pin +#define CLK_PORT gLcd_info->clk_pin +#define CS_PORT gLcd_info->cs_pin +#define LCD_RST_PORT gLcd_info->reset_pin + +#define CS_OUT() gpio_direction_output(CS_PORT, 0) +#define CS_SET() gpio_set_value(CS_PORT, GPIO_HIGH) +#define CS_CLR() gpio_set_value(CS_PORT, GPIO_LOW) +#define CLK_OUT() gpio_direction_output(CLK_PORT, 0) +#define CLK_SET() gpio_set_value(CLK_PORT, GPIO_HIGH) +#define CLK_CLR() gpio_set_value(CLK_PORT, GPIO_LOW) +#define TXD_OUT() gpio_direction_output(TXD_PORT, 0) +#define TXD_SET() gpio_set_value(TXD_PORT, GPIO_HIGH) +#define TXD_CLR() gpio_set_value(TXD_PORT, GPIO_LOW) +#define LCD_RST_OUT(i) gpio_direction_output(LCD_RST_PORT, i) +#define LCD_RST(i) gpio_set_value(LCD_RST_PORT, i) +#endif + + +struct mipi_dsi_t { + u32 id; + int (*dsi_init)(void *, int n); + int (*dsi_hs_start)(void *, int n); + int (*dsi_send_dcs_packet)(unsigned char *, int n); + int (*dsi_read_dcs_packet)(unsigned char *, int n); + int (*dsi_send_packet)(void *, int n); + + void *chip; +}; + +struct power_t { + int enable_pin; //gpio that control power + char* mux_name; + u32 mux_mode; + u32 effect_value; + + u32 min_voltage; + u32 max_voltage; + int (*enable)(void *); + int (*disable)(void *); +}; + +struct reset_t { + int reset_pin; //gpio that control reset + char* mux_name; + u32 mux_mode; + u32 effect_value; + + u32 time_before_reset; //ms + u32 time_after_reset; + + int (*do_reset)(void *); +}; + +struct tc358768_t { + struct reset_t reset; + struct power_t vddc; + struct power_t vddio; + struct power_t vdd_mipi; + struct i2c_client *client; + int (*gpio_init)(void *); + int (*gpio_deinit)(void *); + int (*power_up)(void *); + int (*power_down)(void *); +}; + + +int tc358768_init(struct mipi_dsi_t *pram); +u32 tc358768_wr_reg_32bits_delay(u32 delay, u32 data); + +#endif /* end of TC358768_H_ */ -- 2.34.1