| /*************************************************************************** |
| * __________ __ ___. |
| * 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 <string.h> |
| #include <stdlib.h> |
| #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); |
| } |
| } |
| } |