/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2004 by Linus Nielsen Feltzing * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "cpu.h" #include "lcd.h" #include "kernel.h" #include "thread.h" #include #include #include "file.h" #include "debug.h" #include "system.h" #include "font.h" #include "bidi.h" static bool display_on = false; /* is the display turned on? */ static bool display_flipped = false; static int xoffset = 0; /* needed for flip */ /* register defines */ #define R_START_OSC 0x00 #define R_DRV_OUTPUT_CONTROL 0x01 #define R_DRV_WAVEFORM_CONTROL 0x02 #define R_ENTRY_MODE 0x03 #define R_COMPARE_REG1 0x04 #define R_COMPARE_REG2 0x05 #define R_DISP_CONTROL1 0x07 #define R_DISP_CONTROL2 0x08 #define R_DISP_CONTROL3 0x09 #define R_FRAME_CYCLE_CONTROL 0x0b #define R_EXT_DISP_IF_CONTROL 0x0c #define R_POWER_CONTROL1 0x10 #define R_POWER_CONTROL2 0x11 #define R_POWER_CONTROL3 0x12 #define R_POWER_CONTROL4 0x13 #define R_RAM_ADDR_SET 0x21 #define R_WRITE_DATA_2_GRAM 0x22 #define R_GAMMA_FINE_ADJ_POS1 0x30 #define R_GAMMA_FINE_ADJ_POS2 0x31 #define R_GAMMA_FINE_ADJ_POS3 0x32 #define R_GAMMA_GRAD_ADJ_POS 0x33 #define R_GAMMA_FINE_ADJ_NEG1 0x34 #define R_GAMMA_FINE_ADJ_NEG2 0x35 #define R_GAMMA_FINE_ADJ_NEG3 0x36 #define R_GAMMA_GRAD_ADJ_NEG 0x37 #define R_GAMMA_AMP_ADJ_RES_POS 0x38 #define R_GAMMA_AMP_AVG_ADJ_RES_NEG 0x39 #define R_GATE_SCAN_POS 0x40 #define R_VERT_SCROLL_CONTROL 0x41 #define R_1ST_SCR_DRV_POS 0x42 #define R_2ND_SCR_DRV_POS 0x43 #define R_HORIZ_RAM_ADDR_POS 0x44 #define R_VERT_RAM_ADDR_POS 0x45 #define LCD_CMD (*(volatile unsigned short *)0xf0000000) #define LCD_DATA (*(volatile unsigned short *)0xf0000002) /* called very frequently - inline! */ static inline void lcd_write_reg(int reg, int val) { LCD_CMD = reg; LCD_DATA = val; } /* called very frequently - inline! */ static inline void lcd_begin_write_gram(void) { LCD_CMD = R_WRITE_DATA_2_GRAM; } /*** hardware configuration ***/ void lcd_set_contrast(int val) { (void)val; } void lcd_set_invert_display(bool yesno) { (void)yesno; } static void flip_lcd(bool yesno) { if (yesno) { lcd_write_reg(R_DRV_OUTPUT_CONTROL, 0x031b); /* 224 lines, GS=SS=1 */ lcd_write_reg(R_GATE_SCAN_POS, 0x0002); /* 16 lines offset */ lcd_write_reg(R_1ST_SCR_DRV_POS, 0xdf04); /* 4..223 */ } else { lcd_write_reg(R_DRV_OUTPUT_CONTROL, 0x001b); /* 224 lines, GS=SS=0 */ lcd_write_reg(R_GATE_SCAN_POS, 0x0000); lcd_write_reg(R_1ST_SCR_DRV_POS, 0xdb00); /* 0..219 */ } } /* turn the display upside down (call lcd_update() afterwards) */ void lcd_set_flip(bool yesno) { display_flipped = yesno; xoffset = yesno ? 4 : 0; if (display_on) flip_lcd(yesno); } static void _display_on(void) { /** Sequence according to datasheet, p. 132 **/ lcd_write_reg(R_START_OSC, 0x0001); /* Start Oscilation */ sleep(1); /* zero everything*/ lcd_write_reg(R_POWER_CONTROL1, 0x0000); /* STB = 0, SLP = 0 */ lcd_write_reg(R_DISP_CONTROL1, 0x0000); /* GON = 0, DTE = 0, D1-0 = 00b */ lcd_write_reg(R_POWER_CONTROL3, 0x0000); /* PON = 0 */ lcd_write_reg(R_POWER_CONTROL4, 0x0000); /* VCOMG = 0 */ sleep(1); /* initialise power supply */ /* DC12-10 = 000b: Step-up1 = clock/8, * DC02-00 = 000b: Step-up2 = clock/16, * VC2-0 = 010b: VciOUT = 0.87 * VciLVL */ lcd_write_reg(R_POWER_CONTROL2, 0x0002); /* VRH3-0 = 1000b: Vreg1OUT = REGP * 1.90 */ lcd_write_reg(R_POWER_CONTROL3, 0x0008); /* VDV4-0 = 00110b: VcomA = Vreg1OUT * 0.76, * VCM4-0 = 10000b: VcomH = Vreg1OUT * 0.70*/ lcd_write_reg(R_POWER_CONTROL4, 0x0610); lcd_write_reg(R_POWER_CONTROL1, 0x0044); /* AP2-0 = 100b, DK = 1 */ lcd_write_reg(R_POWER_CONTROL3, 0x0018); /* PON = 1 */ sleep(4); /* Step-up circuit stabilising time */ /* start power supply */ lcd_write_reg(R_POWER_CONTROL1, 0x0540); /* BT2-0 = 101b, DK = 0 */ lcd_write_reg(R_POWER_CONTROL4, 0x2610); /* VCOMG = 1 */ /* other settings */ /* B/C = 1: n-line inversion form * EOR = 1: polarity inversion occurs by applying an EOR to odd/even * frame select signal and an n-line inversion signal. * FLD = 01b: 1 field interlaced scan, external display iface */ lcd_write_reg(R_DRV_WAVEFORM_CONTROL, 0x0700); /* Address counter updated in vertical direction; left to right; * vertical increment horizontal increment. * data format for 8bit transfer or spi = 65k (5,6,5) * Reverse order of RGB to BGR for 18bit data written to GRAM * Replace data on writing to GRAM */ lcd_write_reg(R_ENTRY_MODE, 0x7038); flip_lcd(display_flipped); lcd_write_reg(R_2ND_SCR_DRV_POS, 0x0000); lcd_write_reg(R_VERT_SCROLL_CONTROL, 0x0000); /* 19 clocks,no equalization */ lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x0002); /* Transfer mode for RGB interface disabled * internal clock operation; * System interface/VSYNC interface */ lcd_write_reg(R_EXT_DISP_IF_CONTROL, 0x0003); /* Front porch lines: 8; Back porch lines: 8; */ lcd_write_reg(R_DISP_CONTROL2, 0x0808); /* Scan mode by the gate driver in the non-display area: disabled; * Cycle of scan by the gate driver - set to 31frames(518ms), * disabled by above setting */ lcd_write_reg(R_DISP_CONTROL3, 0x003f); lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x0003); lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0707); lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0007); lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x0705); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0007); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 0x0000); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0407); lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 0x0507); lcd_write_reg(R_GAMMA_AMP_ADJ_RES_POS, 0x1d09); lcd_write_reg(R_GAMMA_AMP_AVG_ADJ_RES_NEG, 0x0303); display_on=true; /* must be done before calling lcd_update() */ lcd_update(); sleep(4); /* op-amp stabilising time */ /** Sequence according to datasheet, p. 130 **/ lcd_write_reg(R_POWER_CONTROL1, 0x4540); /* SAP2-0=100, BT2-0=101, AP2-0=100 */ lcd_write_reg(R_DISP_CONTROL1, 0x0005); /* GON=0, DTE=0, REV=1, D1-0=01 */ sleep(2); lcd_write_reg(R_DISP_CONTROL1, 0x0025); /* GON=1, DTE=0, REV=1, D1-0=01 */ lcd_write_reg(R_DISP_CONTROL1, 0x0027); /* GON=1, DTE=0, REV=1, D1-0=11 */ sleep(2); lcd_write_reg(R_DISP_CONTROL1, 0x0037); /* GON=1, DTE=1, REV=1, D1-0=11 */ } /* LCD init */ void lcd_init_device(void) { /* GPO46 is LCD RESET */ or_l(0x00004000, &GPIO1_OUT); or_l(0x00004000, &GPIO1_ENABLE); or_l(0x00004000, &GPIO1_FUNCTION); /* Reset LCD */ and_l(~0x00004000, &GPIO1_OUT); sleep(1); or_l(0x00004000, &GPIO1_OUT); sleep(1); _display_on(); } void lcd_enable(bool on) { if(display_on!=on) { if(on) { _display_on(); } else { /** Off sequence according to datasheet, p. 130 **/ lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x0002); /* EQ=0, 18 clks/line */ lcd_write_reg(R_DISP_CONTROL1, 0x0036); /* GON=1, DTE=1, REV=1, D1-0=10 */ sleep(2); lcd_write_reg(R_DISP_CONTROL1, 0x0026); /* GON=1, DTE=0, REV=1, D1-0=10 */ sleep(2); lcd_write_reg(R_DISP_CONTROL1, 0x0000); /* GON=0, DTE=0, D1-0=00 */ lcd_write_reg(R_POWER_CONTROL1, 0x0000); /* SAP2-0=000, AP2-0=000 */ lcd_write_reg(R_POWER_CONTROL3, 0x0000); /* PON=0 */ lcd_write_reg(R_POWER_CONTROL4, 0x0000); /* VCOMG=0 */ /* datasheet p. 131 */ lcd_write_reg(R_POWER_CONTROL1, 0x0001); /* STB=1: standby mode */ display_on=false; } } } /*** update functions ***/ /* Performance function that works with an external buffer note that by and bheight are in 8-pixel units! */ void lcd_blit(const fb_data* data, int x, int by, int width, int bheight, int stride) { /* TODO: Implement lcd_blit() */ (void)data; (void)x; (void)by; (void)width; (void)bheight; (void)stride; /*if(display_on)*/ } #define CSUB_X 2 #define CSUB_Y 2 #define RYFAC (31*257) #define GYFAC (63*257) #define BYFAC (31*257) #define RVFAC 11170 /* 31 * 257 * 1.402 */ #define GVFAC (-11563) /* 63 * 257 * -0.714136 */ #define GUFAC (-5572) /* 63 * 257 * -0.344136 */ #define BUFAC 14118 /* 31 * 257 * 1.772 */ #define ROUNDOFFS (127*257) /* Performance function to blit a YUV bitmap directly to the LCD */ void lcd_yuv_blit(unsigned char * const src[3], int src_x, int src_y, int stride, int x, int y, int width, int height) { if (display_on) { int ymax; width = (width + 1) & ~1; height = (height + 1) & ~1; ymax = y + height - 1; /* set update window */ /* horiz ram addr */ lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (ymax << 8) | y); /* vert ram addr */ lcd_write_reg(R_VERT_RAM_ADDR_POS,((x+xoffset+width-1) << 8) | (x+xoffset)); lcd_write_reg(R_RAM_ADDR_SET, ((x+xoffset) << 8) | y); lcd_begin_write_gram(); for (; y <= ymax; y++) { /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */ const unsigned char *ysrc = src[0] + stride * src_y + src_x; const unsigned char *usrc = src[1] + (stride/CSUB_X) * (src_y/CSUB_Y) + (src_x/CSUB_X); const unsigned char *vsrc = src[2] + (stride/CSUB_X) * (src_y/CSUB_Y) + (src_x/CSUB_X); const unsigned char *row_end = ysrc + width; int y, u, v; int rc, gc, bc; int red, green, blue; unsigned rbits, gbits, bbits; do { u = *usrc++ - 128; v = *vsrc++ - 128; rc = RVFAC * v + ROUNDOFFS; gc = GVFAC * v + GUFAC * u + ROUNDOFFS; bc = BUFAC * u + ROUNDOFFS; y = *ysrc++; red = RYFAC * y + rc; green = GYFAC * y + gc; blue = BYFAC * y + bc; if ((unsigned)red > (RYFAC*255+ROUNDOFFS)) { if (red < 0) red = 0; else red = (RYFAC*255+ROUNDOFFS); } if ((unsigned)green > (GYFAC*255+ROUNDOFFS)) { if (green < 0) green = 0; else green = (GYFAC*255+ROUNDOFFS); } if ((unsigned)blue > (BYFAC*255+ROUNDOFFS)) { if (blue < 0) blue = 0; else blue = (BYFAC*255+ROUNDOFFS); } rbits = ((unsigned)red) >> 16 ; gbits = ((unsigned)green) >> 16 ; bbits = ((unsigned)blue) >> 16 ; LCD_DATA = (rbits << 11) | (gbits << 5) | bbits; y = *ysrc++; red = RYFAC * y + rc; green = GYFAC * y + gc; blue = BYFAC * y + bc; if ((unsigned)red > (RYFAC*255+ROUNDOFFS)) { if (red < 0) red = 0; else red = (RYFAC*255+ROUNDOFFS); } if ((unsigned)green > (GYFAC*255+ROUNDOFFS)) { if (green < 0) green = 0; else green = (GYFAC*255+ROUNDOFFS); } if ((unsigned)blue > (BYFAC*255+ROUNDOFFS)) { if (blue < 0) blue = 0; else blue = (BYFAC*255+ROUNDOFFS); } rbits = ((unsigned)red) >> 16 ; gbits = ((unsigned)green) >> 16 ; bbits = ((unsigned)blue) >> 16 ; LCD_DATA = (rbits << 11) | (gbits << 5) | bbits; } while (ysrc < row_end); src_y++; } } } /* Update the display. This must be called after all other LCD functions that change the display. */ void lcd_update(void) ICODE_ATTR; void lcd_update(void) { if(display_on){ /* reset update window */ /* horiz ram addr: 0 - 175 */ lcd_write_reg(R_HORIZ_RAM_ADDR_POS, 0xaf00); /* vert ram addr: 0 - 219 */ lcd_write_reg(R_VERT_RAM_ADDR_POS,((xoffset+219)<<8) | xoffset); /* Copy display bitmap to hardware */ lcd_write_reg(R_RAM_ADDR_SET, xoffset << 8); lcd_begin_write_gram(); lcd_write_data((unsigned short *)lcd_framebuffer, LCD_WIDTH*LCD_HEIGHT); } } /* Update a fraction of the display. */ void lcd_update_rect(int, int, int, int) ICODE_ATTR; void lcd_update_rect(int x, int y, int width, int height) { if(display_on) { int ymax = y + height - 1; if(x + width > LCD_WIDTH) width = LCD_WIDTH - x; if (width <= 0) return; /* nothing left to do, 0 is harmful to lcd_write_data() */ if(ymax >= LCD_HEIGHT) ymax = LCD_HEIGHT-1; /* set update window */ /* horiz ram addr */ lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (ymax << 8) | y); /* vert ram addr */ lcd_write_reg(R_VERT_RAM_ADDR_POS,((x+xoffset+width-1) << 8) | (x+xoffset)); lcd_write_reg(R_RAM_ADDR_SET, ((x+xoffset) << 8) | y); lcd_begin_write_gram(); /* Copy specified rectangle bitmap to hardware */ for (; y <= ymax; y++) { lcd_write_data ((unsigned short *)&lcd_framebuffer[y][x], width); } } }