From 066295144e5618047ef0be8ef93cfde9e5cb824b Mon Sep 17 00:00:00 2001 From: daeinki Date: Fri, 31 Jul 2009 21:58:42 +0900 Subject: [PATCH] s5pc110: fb: Added framebuffer driver for Dual LCD. Signed-off-by: daeinki --- drivers/video/s5p-dualfb.c | 243 +++++++++++++++++++++++++++++++++ drivers/video/s5p-dualfimd.c | 317 +++++++++++++++++++++++++++++++++++++++++++ drivers/video/tl2796_dual.c | 252 ++++++++++++++++++++++++++++++++++ 3 files changed, 812 insertions(+) create mode 100644 drivers/video/s5p-dualfb.c create mode 100644 drivers/video/s5p-dualfimd.c create mode 100644 drivers/video/tl2796_dual.c diff --git a/drivers/video/s5p-dualfb.c b/drivers/video/s5p-dualfb.c new file mode 100644 index 0000000..71fa249 --- /dev/null +++ b/drivers/video/s5p-dualfb.c @@ -0,0 +1,243 @@ +/* + * S5PC100 and S5PC110 LCD Controller driver. + * + * Author: InKi Dae + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s5p-fb.h" +#include "opening_wvga_32.h" + +#define PANEL_WIDTH 480 +#define PANEL_HEIGHT 800 +#define S5P_LCD_BPP 32 + +/* for DUAL LCD */ +#define SCREEN_WIDTH (480 * 2) +#define SCREEN_HEIGHT 800 + +extern void tl2796_panel_power_on(void); +extern void tl2796_panel_enable(void); +extern void tl2796_panel_init(void); + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +int dual_lcd = 1; + +void *lcd_base; +void *lcd_console_address; + +short console_col; +short console_row; + +static unsigned short makepixel565(char r, char g, char b) +{ + return (unsigned short)(((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)); +} + +static unsigned int makepixel8888(char a, char r, char g, char b) +{ + return (unsigned int)((a << 24) | (r << 16) | (g << 8) | b); +} + +static void read_image16(char* pImg, int x1pos, int y1pos, int x2pos, + int y2pos, unsigned short pixel) +{ + unsigned short *pDst = (unsigned short *)pImg; + unsigned int offset_s; + int i, j; + + for(i = y1pos; i < y2pos; i++) { + for(j = x1pos; j < x2pos; j++) { + offset_s = i * SCREEN_WIDTH + j; + *(pDst + offset_s) = pixel; + } + } +} + +static void read_image32(char* pImg, int x1pos, int y1pos, int x2pos, + int y2pos, unsigned int pixel) +{ + unsigned int *pDst = (unsigned int *)pImg; + unsigned int offset_s; + int i, j; + + for(i = y1pos; i < y2pos; i++) { + for(j = x1pos; j < x2pos; j++) { + offset_s = i * SCREEN_WIDTH + j; + *(pDst+offset_s) = pixel; + } + } +} + +/* LCD Panel data */ +vidinfo_t panel_info = { + .vl_col = SCREEN_WIDTH, + .vl_row = SCREEN_HEIGHT, + .vl_width = PANEL_WIDTH, + .vl_height = PANEL_HEIGHT, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_LOW, + .vl_vsp = CONFIG_SYS_LOW, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = S5P_LCD_BPP, + .vl_lbw = 0, + .vl_splt = 0, + .vl_clor = 1, + .vl_tft = 1, + + .vl_hpw = 4, + .vl_blw = 8, + .vl_elw = 8, + + .vl_vpw = 4, + .vl_bfw = 8, + .vl_efw = 8, +}; + +static void s5pc_lcd_init_mem(void *lcdbase, vidinfo_t *vid) +{ + unsigned long palette_size, palette_mem_size; + unsigned int fb_size; + + fb_size = vid->vl_row * vid->vl_col * (vid->vl_bpix / 8); + + lcd_base = lcdbase; + + palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16; + palette_mem_size = palette_size * sizeof(u32); + + s5pc_fimd_lcd_init_mem((unsigned long)lcd_base, (unsigned long)fb_size, palette_size); + + udebug("fb_size=%d, screen_base=%x, palette_size=%d, palettle_mem_size=%d\n", + fb_size, (unsigned int)lcd_base, (int)palette_size, (int)palette_mem_size); +} + +static void s5pc_gpio_setup(void) +{ + s5pc_c110_gpio_setup(); +} + +static void s5pc_lcd_init(vidinfo_t *vid) +{ + s5pc_fimd_lcd_init(vid); +} + +static void lcd_test(void) +{ + /* red */ + read_image32((char *)lcd_base, 0, 0, 960, 200, + makepixel8888(0, 255, 0, 0)); + /* green */ + read_image32((char *)lcd_base, 0, 200, 960, 400, + makepixel8888(0, 0, 255, 0)); + /* blue */ + read_image32((char *)lcd_base, 0, 400, 960, 600, + makepixel8888(0, 0, 0, 255)); + /* write */ + read_image32((char *)lcd_base, 0, 600, 960, 800, + makepixel8888(0, 255, 255, 255)); +} + +void draw_bitmap(void *lcdbase, int x, int y, int w, int h, unsigned long *bmp) +{ + int i, j, k = 0; + unsigned long *fb = (unsigned long*)lcdbase; + + for (j = y; j < (y + h); j++) { + for (i = x; i < (w + x); i++) { + *(fb + (j * SCREEN_WIDTH) + i) = + *(unsigned long *)(bmp + k); + k++; + } + } +} + +static void draw_samsung_logo(void* lcdbase) +{ + int x, y; + + x = (SCREEN_WIDTH - 138) / 2; + y = (SCREEN_HEIGHT - 28) / 2 - 5; + + draw_bitmap(lcdbase, x, y, 138, 28, (unsigned long *)opening_32); +} + +static void lcd_panel_on(void) +{ + tl2796_c110_panel_init(); + tl2796_c110_panel_power_on(); + + tl2796_panel_enable(); +} + +void lcd_ctrl_init(void *lcdbase) +{ + char *option; + + s5pc_lcd_init_mem(lcdbase, &panel_info); + + option = getenv("lcd"); + + if (strcmp(option, "test") == 0) { + memset(lcdbase, 0, panel_info.vl_col * + panel_info.vl_row * S5P_LCD_BPP >> 3); + + lcd_test(); + } else { + memset(lcdbase, 0, panel_info.vl_col * + panel_info.vl_row * S5P_LCD_BPP >> 3); + + draw_samsung_logo(lcdbase); + } + + s5pc_gpio_setup(); + + s5pc_lcd_init(&panel_info); +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blud) +{ + return; +} + +void lcd_enable(void) +{ + lcd_panel_on(); +} + +ulong calc_fbsize(void) +{ + return s5pc_fimd_calc_fbsize(); +} + +void s5pc1xxfb_test(void *lcdbase) +{ + lcd_ctrl_init(lcdbase); + lcd_enable(); +} diff --git a/drivers/video/s5p-dualfimd.c b/drivers/video/s5p-dualfimd.c new file mode 100644 index 0000000..ff9d36f --- /dev/null +++ b/drivers/video/s5p-dualfimd.c @@ -0,0 +1,317 @@ +/* + * S5PC100 and S5PC110 LCD Controller Specific driver. + * + * Author: InKi Dae + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "s5p-fb.h" + +#define MPLL 1 + +#define S5P_VFRAME_FREQ 60 + +static unsigned int ctrl_base; +static unsigned long *lcd_base_addr; +static vidinfo_t *pvid = NULL; + +extern int dual_lcd; +extern unsigned long get_pll_clk(int pllreg); + +void s5pc_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, u_long palette_size) +{ + lcd_base_addr = (unsigned long *)screen_base; + + udebug("lcd_base_addr(framebuffer memory) = %x\n", lcd_base_addr); + + return; +} + +void s5pc_c110_gpio_setup(void) +{ + /* set GPF0[0:7] for RGB Interface and Data lines */ + writel(0x22222222, 0xE0200120); + /* pull-up/down disable */ + writel(0x0, 0xE0200128); + /* drive strength to max */ + writel(0xffffffff, 0xE020012C); + + /* set Data lines */ + writel(0x22222222, 0xE0200140); + writel(0x22222222, 0xE0200160); + writel(readl(0xE0200180) & 0xFF0000, 0xE0200180); + writel(readl(0xE0200180) | 0x002222, 0xE0200180); + + if (dual_lcd) { + /* SUB_DISPLAY_PCLK (GPF3[4]) */ + writel(readl(0xE0200180) & 0xfff0ffff, 0xE0200180); + writel(readl(0xE0200180) | 0x00020000, 0xE0200180); + /* drive stength to max */ + writel(readl(0xE020018C) & 0xFCFF, 0xE020018C); + writel(readl(0xE020018C) | 0x0300, 0xE020018C); + /* set gpio configuration pin for SUBLCD_RST(MP0_2[1]) and SUBLCD_ON(MP0_2[0] */ + writel(readl(0xE0200300) & 0xffffff00, 0xE0200300); + writel(readl(0xE0200300) | 0x00000011, 0xE0200300); + /* set gpio confituration pin for SUB_DISPLAY_CS(MP0_1[2]) */ + writel(readl(0xE02002E0) & 0xfffff0ff, 0xE02002E0); + writel(readl(0xE02002E0) | 0x00000100, 0xE02002E0); + + /* + * DUALRGB INTERFACE SETTING REGISTER + * Line Split mode, using VCLK, Display bypass Dual mode + */ + writel(0x0C801e01, 0xF800027C); + } + + /* drive strength to max */ + writel(0xffffffff, 0xE020014C); + writel(0xffffffff, 0xE020016C); + writel(readl(0xE020018C) & 0xFF00, 0xE020018C); + writel(readl(0xE020018C) | 0x00FF, 0xE020018C); + + /* pull-up/down disable */ + writel(0x0, 0xE0200148); + writel(0x0, 0xE0200168); + writel(0x0, 0xE0200188); + + /* display output path selection */ + writel(0x3, 0xE0107008); + + /* set gpio configuration pin for MLCD_RST */ + writel(readl(0xE0200C20) & 0x0fffffff, 0xE0200C20); + writel(readl(0xE0200C20) | 0x10000000, 0xE0200C20); + + /* set gpio configuration pin for MLCD_ON */ + writel(readl(0xE0200260) & 0xFFFF0FFF, 0xE0200260); + writel(readl(0xE0200260) | 0xFFFF1000, 0xE0200260); + writel(readl(0xE0200264) & 0xf7, 0xE0200264); + + /* set gpio configuration pin for DISPLAY_CS, DISPLAY_CLK, DISPLSY_SI and LCD_ID */ + writel(readl(0xE02002E0) & 0xFFFFFF0F, 0xE02002E0); + writel(readl(0xE02002E0) | 0x00000010, 0xE02002E0); + writel(readl(0xE0200340) & 0xFFFF000F, 0xE0200340); + writel(readl(0xE0200340) | 0x00001110, 0xE0200340); + + return; +} + +static void s5pc_fimd_set_par(unsigned int win_id) +{ + unsigned int cfg = 0; + + /* set window control */ + cfg = readl(ctrl_base + S5P_WINCON(win_id)); + + cfg &= ~(S5P_WINCON_BITSWP_ENABLE | S5P_WINCON_BYTESWP_ENABLE | \ + S5P_WINCON_HAWSWP_ENABLE | S5P_WINCON_WSWP_ENABLE | \ + S5P_WINCON_BURSTLEN_MASK | S5P_WINCON_BPPMODE_MASK | \ + S5P_WINCON_INRGB_MASK | S5P_WINCON_DATAPATH_MASK); + + /* DATAPATH is DMA */ + cfg |= S5P_WINCON_DATAPATH_DMA; + + /* bpp is 32 */ + cfg |= S5P_WINCON_WSWP_ENABLE; + + /* dma burst is 16 */ + cfg |= S5P_WINCON_BURSTLEN_16WORD; + + /* pixel format is unpacked RGB888 */ + cfg |= S5P_WINCON_BPPMODE_24BPP_888; + + writel(cfg, ctrl_base + S5P_WINCON(win_id)); + udebug("wincon%d = %x\n", win_id, cfg); + + /* set window position to x=0, y=0*/ + cfg = S5P_VIDOSD_LEFT_X(0) | S5P_VIDOSD_TOP_Y(0); + writel(cfg, ctrl_base + S5P_VIDOSD_A(win_id)); + udebug("window postion left,top = %x\n", cfg); + + cfg = S5P_VIDOSD_RIGHT_X(pvid->vl_col - 1) | + S5P_VIDOSD_BOTTOM_Y(pvid->vl_row - 1); + writel(cfg, ctrl_base + S5P_VIDOSD_B(win_id)); + udebug("window postion right,bottom= %x\n", cfg); + + /* set window size for window0*/ + cfg = S5P_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); + writel(cfg, ctrl_base + S5P_VIDOSD_C(win_id)); + udebug("vidosd_c%d= %x\n", win_id, cfg); + + return; +} + +static void s5pc_fimd_set_buffer_address(unsigned int win_id) +{ + unsigned long start_addr, end_addr; + + start_addr = (unsigned long)lcd_base_addr; + end_addr = start_addr + ((pvid->vl_col * (pvid->vl_bpix / 8)) + * pvid->vl_row); + + writel(start_addr, ctrl_base + S5P_VIDADDR_START0(win_id)); + writel(end_addr, ctrl_base + S5P_VIDADDR_END0(win_id)); + + udebug("start addr = %x, end addr = %x\n", start_addr, end_addr); + + return; +} + +static void s5pc_fimd_set_clock(void) +{ + unsigned int cfg = 0, div = 0, mpll_ratio = 0; + unsigned long pixel_clock, src_clock, max_clock; + + max_clock = 66 * 1000000; + + pixel_clock = S5P_VFRAME_FREQ * (pvid->vl_hpw + pvid->vl_blw + + pvid->vl_elw + pvid->vl_width) * (pvid->vl_vpw + + pvid->vl_bfw + pvid->vl_efw + pvid->vl_height); + + src_clock = get_pll_clk(MPLL); + + cfg = readl(ctrl_base + S5P_VIDCON0); + cfg &= ~(S5P_VIDCON0_CLKSEL_MASK | S5P_VIDCON0_CLKVALUP_MASK | \ + S5P_VIDCON0_VCLKEN_MASK | S5P_VIDCON0_CLKDIR_MASK); + cfg |= (S5P_VIDCON0_CLKSEL_HCLK | S5P_VIDCON0_CLKVALUP_ALWAYS | \ + S5P_VIDCON0_VCLKEN_NORMAL | S5P_VIDCON0_CLKDIR_DIVIDED); + + if (pixel_clock > max_clock) + pixel_clock = max_clock; + + /* get mpll ratio */ + mpll_ratio = (readl(0xE0100300) & 0xf0000) >> 16; + + /* + * It can get sorce clock speed as (mpll / mpll_ratio) + * because lcd controller uses hclk_dsys. + * mpll is a parent of hclk_dsys. + */ + div = (unsigned int)((src_clock / (mpll_ratio + 1)) / pixel_clock); + cfg |= S5P_VIDCON0_CLKVAL_F(div - 1); + writel(cfg, ctrl_base + S5P_VIDCON0); + + udebug("mpll_ratio = %d, src_clock = %d, pixel_clock = %d, div = %d\n", + mpll_ratio, src_clock, pixel_clock, div); + + return; +} + +void s5pc_fimd_lcd_init(vidinfo_t *vid) +{ + unsigned int cfg = 0, rgb_mode, win_id = 0; + + /* store panel info to global variable */ + pvid = vid; + + /* select register base according to cpu type */ + ctrl_base = 0xF8000000; + + /* set output to BGR for 3.1inch TL2796 LCD Panel */ + rgb_mode = MODE_BGR_P; + + /* 3.5inch TL2796 LCD Panel */ + //rgb_mode = MODE_RGB_P; + + cfg = readl(ctrl_base + S5P_VIDCON0); + cfg &= ~S5P_VIDCON0_VIDOUT_MASK; + + /* clock source is HCLK */ + cfg |= 0 << 2; + + cfg |= S5P_VIDCON0_VIDOUT_RGB; + writel(cfg, ctrl_base + S5P_VIDCON0); + + /* set display mode */ + cfg = readl(ctrl_base + S5P_VIDCON0); + cfg &= ~S5P_VIDCON0_PNRMODE_MASK; + cfg |= (rgb_mode << S5P_VIDCON0_PNRMODE_SHIFT); + writel(cfg, ctrl_base + S5P_VIDCON0); + + /* set polarity */ + cfg = 0; + cfg |= S5P_VIDCON1_IVDEN_INVERT | S5P_VIDCON1_IVCLK_RISING_EDGE; + writel(cfg, ctrl_base + S5P_VIDCON1); + + /* set timing */ + cfg = 0; + cfg |= S5P_VIDTCON0_VBPD(pvid->vl_bfw - 1); + cfg |= S5P_VIDTCON0_VFPD(pvid->vl_efw - 1); + cfg |= S5P_VIDTCON0_VSPW(pvid->vl_vpw - 1); + writel(cfg, ctrl_base + S5P_VIDTCON0); + udebug("vidtcon0 = %x\n", cfg); + + cfg = 0; + cfg |= S5P_VIDTCON1_HBPD(pvid->vl_blw - 1); + cfg |= S5P_VIDTCON1_HFPD(pvid->vl_elw - 1); + cfg |= S5P_VIDTCON1_HSPW(pvid->vl_hpw - 1); + + writel(cfg, ctrl_base + S5P_VIDTCON1); + udebug("vidtcon1 = %x\n", cfg); + + /* set lcd size */ + cfg = 0; + cfg |= S5P_VIDTCON2_HOZVAL(pvid->vl_col - 1); + cfg |= S5P_VIDTCON2_LINEVAL(pvid->vl_row - 1); + + writel(cfg, ctrl_base + S5P_VIDTCON2); + udebug("vidtcon2 = %x\n", cfg); + + /* set par */ + s5pc_fimd_set_par(win_id); + + /* set memory address */ + s5pc_fimd_set_buffer_address(win_id); + + /* set buffer size */ + cfg = S5P_VIDADDR_PAGEWIDTH(pvid->vl_col * pvid->vl_bpix / 8); + writel(cfg, ctrl_base + S5P_VIDADDR_SIZE(win_id)); + udebug("vidaddr_pagewidth = %d\n", cfg); + + /* set clock */ + s5pc_fimd_set_clock(); + + /* display on */ + cfg = readl(ctrl_base + S5P_VIDCON0); + cfg |= (S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE); + writel(cfg, ctrl_base + S5P_VIDCON0); + udebug("vidcon0 = %x\n", cfg); + + /* enable window */ + cfg = readl(ctrl_base + S5P_WINCON(win_id)); + cfg |= S5P_WINCON_ENWIN_ENABLE; + writel(cfg, ctrl_base + S5P_WINCON(win_id)); + udebug("wincon%d=%x\n", win_id, cfg); + + udebug("lcd controller init completed.\n"); + + return; +} + +ulong s5pc_fimd_calc_fbsize(void) +{ + return (pvid->vl_col * pvid->vl_row * (pvid->vl_bpix / 8)); +} diff --git a/drivers/video/tl2796_dual.c b/drivers/video/tl2796_dual.c new file mode 100644 index 0000000..38dfeea --- /dev/null +++ b/drivers/video/tl2796_dual.c @@ -0,0 +1,252 @@ +/* + * LCD panel driver for Board based on S5PC100 and S5PC110. + * + * Author: InKi Dae + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#define SLEEPMSEC 0x1000 +#define ENDDEF 0x2000 +#define DEFMASK 0xFF00 +#define COMMAND_ONLY 0xFE +#define DATA_ONLY 0xFF + +#define S5PCFB_CS_LOW writel(readl(0xE02002E4) & 0xfd, 0xE02002E4) +#define S5PCFB_CS_HIGH writel(readl(0xE02002E4) | 0x02, 0xE02002E4) +#define S5PCFB_CLK_LOW writel(readl(0xE0200344) & 0xfd, 0xE0200344) +#define S5PCFB_CLK_HIGH writel(readl(0xE0200344) | 0x02, 0xE0200344) +#define S5PCFB_SDA_LOW writel(readl(0xE0200344) & 0xf7, 0xE0200344) +#define S5PCFB_SDA_HIGH writel(readl(0xE0200344) | 0x08, 0xE0200344) + +/* for DUAL LCD */ +#define S5PCFB_SUB_CS_LOW writel(readl(0xE02002E4) & 0xfb, 0xE02002E4) +#define S5PCFB_SUB_CS_HIGH writel(readl(0xE02002E4) | 0x04, 0xE02002E4) + +const unsigned short SEQ_DISPLAY_ON[] = { + 0x14, 0x03, + + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_DISPLAY_OFF[] = { + 0x14, 0x00, + + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_STANDBY_ON[] = { + 0x1D, 0xA1, + SLEEPMSEC, 200, + + ENDDEF, 0x0000 +}; + +const unsigned short SEQ_STANDBY_OFF[] = { + 0x1D, 0xA0, + SLEEPMSEC, 250, + + ENDDEF, 0x0000 +}; + + +const unsigned short SEQ_SETTING[] = { + 0x31, 0x08, /* panel setting */ + 0x32, 0x14, + 0x30, 0x02, + 0x27, 0x01, + 0x12, 0x08, + 0x13, 0x08, + 0x15, 0x1C, + 0x16, 0x00, /* 24bit line and 16M color */ + + 0xEF, 0xD0, /* pentile key setting */ + DATA_ONLY, 0xE8, + + 0x39, 0x44, /* gamma setting : 300cd */ + 0x40, 0x00, + 0x41, 0x00, + 0x42, 0x00, + 0x43, 0x24, + 0x44, 0x24, + 0x45, 0x1E, + 0x46, 0x3F, + + 0x50, 0x00, + 0x51, 0x00, + 0x52, 0x00, + 0x53, 0x23, + 0x54, 0x24, + 0x55, 0x1E, + 0x56, 0x3D, + + 0x60, 0x00, + 0x61, 0x00, + 0x62, 0x00, + 0x63, 0x22, + 0x64, 0x22, + 0x65, 0x1A, + 0x66, 0x56, + + 0x17, 0x22, /* power setting */ + 0x18, 0x33, + 0x19, 0x03, + 0x1A, 0x01, + 0x22, 0xA3, /* VXX x 0.60 */ + 0x23, 0x00, + 0x26, 0xA0, + + ENDDEF, 0x0000 +}; + +extern int dual_lcd; + +static void tl2796_spi_write_byte(unsigned char address, unsigned char command) +{ + int j; + unsigned char DELAY=1; + unsigned short data; + + data = (address << 8) + command; + + if (dual_lcd) + S5PCFB_SUB_CS_HIGH; + S5PCFB_CS_HIGH; + S5PCFB_SDA_HIGH; + S5PCFB_CLK_HIGH; + udelay(DELAY); + + if (dual_lcd) + S5PCFB_SUB_CS_LOW; + S5PCFB_CS_LOW; + udelay(DELAY); + + for (j = 15; j >= 0; j--) + { + S5PCFB_CLK_LOW; + + /* data high or low */ + if ((data >> j) & 0x0001) + S5PCFB_SDA_HIGH; + else + S5PCFB_SDA_LOW; + + udelay(DELAY); + + S5PCFB_CLK_HIGH; + udelay(DELAY); + } + + if (dual_lcd) + S5PCFB_SUB_CS_HIGH; + S5PCFB_CS_HIGH; + udelay(DELAY); +} + + +static void tl2796_spi_write(unsigned char address, unsigned char command) +{ + if(address != DATA_ONLY) + tl2796_spi_write_byte(0x70, address); + + tl2796_spi_write_byte(0x72, command); +} + +static void tl2796_panel_send_sequence(const unsigned short *wbuf) +{ + int i = 0; + + while ((wbuf[i] & DEFMASK) != ENDDEF) { + if ((wbuf[i] & DEFMASK) != SLEEPMSEC) + tl2796_spi_write(wbuf[i], wbuf[i+1]); + else + udelay(wbuf[i+1]*1000); + i += 2; + } +} + +void tl2796_c110_panel_power_on(void) +{ + /* set gpio data for MLCD_RST to HIGH */ + writel(readl(0xE0200C24) | 0x80, 0xE0200C24); + /* set gpio data for MLCD_ON to HIGH */ + writel(readl(0xE0200264) | 0x8, 0xE0200264); + udelay(25000); + + /* set gpio data for MLCD_RST to LOW */ + writel(readl(0xE0200C24) & 0x7f, 0xE0200C24); + udelay(20); + /* set gpio data for MLCD_RST to HIGH */ + writel(readl(0xE0200C24) | 0x80, 0xE0200C24); + + if (dual_lcd) { + /* set gpio data for SUBLCD_RST to HIGH */ + writel(readl(0xE0200304) | 0x02, 0xE0200304); + /* set gpio data for SUBLCD_ON to HIGH */ + writel(readl(0xE0200304) | 0x01, 0xE0200304); + udelay(25000); + + /* set gpio data for SUBLCD_RST to LOW */ + writel(readl(0xE0200304) & 0xfd, 0xE0200304); + udelay(20); + /* set gpio data for SUBLCD_RST to HIGH */ + writel(readl(0xE0200304) | 0x02, 0xE0200304); + } + udelay(20000); + + tl2796_panel_send_sequence(SEQ_SETTING); + tl2796_panel_send_sequence(SEQ_STANDBY_OFF); +} + +static inline void tl2796_c110_panel_hw_reset(void) +{ + /* set gpio pin for MLCD_RST to LOW */ + writel(readl(0xE0200C24) & 0x7f, 0xE0200C24); + udelay(1); /* Shorter than 5 usec */ + /* set gpio pin for MLCD_RST to HIGH */ + writel(readl(0xE0200C24) | 0x80, 0xE0200C24); + udelay(10000); +} + +void tl2796_panel_enable(void) +{ + tl2796_panel_send_sequence(SEQ_DISPLAY_ON); +} + +static void tl2796_panel_disable(void) +{ + tl2796_panel_send_sequence(SEQ_DISPLAY_OFF); +} + +void tl2796_c110_panel_init(void) +{ + /* set gpio pin for DISPLAY_CS to HIGH */ + writel(readl(0xE02002E4) | 0x02, 0xE02002E4); + /* set gpio pin for DISPLAY_CLK to HIGH */ + writel(readl(0xE0200344) | 0x02, 0xE0200344); + /* set gpio pin for DISPLAY_SI to HIGH */ + writel(readl(0xE0200344) | 0x08, 0xE0200344); + + if (dual_lcd) { + /* set gpio pin for SUB_DISPLAY_CS to HIGH */ + writel(readl(0xE02002E4) | 0x04, 0xE03002E4); + } +} -- 2.7.4