Commit FS#7692 - LCD garbled top two lines by Jesse Lockwood and Jonathan Backer...
[kugel-rb.git] / firmware / target / arm / sandisk / sansa-e200 / lcd-e200.c
blob31df3f9f195571054391ffd4563f15cd8f8ffcdf
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Rockbox driver for Sansa e200 LCDs
12 * Based on reverse engineering done my MrH
14 * Copyright (c) 2006 Daniel Ankers
16 * All files in this archive are subject to the GNU General Public License.
17 * See the file COPYING in the source tree root for full license agreement.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "lcd.h"
24 #include "system.h"
25 #include <string.h>
26 #include "backlight-target.h"
27 #include "pp5024.h"
29 /* Power and display status */
30 static bool power_on = false; /* Is the power turned on? */
31 static bool display_on NOCACHEBSS_ATTR = false; /* Is the display turned on? */
33 /* Reverse Flag */
34 #define R_DISP_CONTROL_NORMAL 0x0004
35 #define R_DISP_CONTROL_REV 0x0000
36 static unsigned short r_disp_control_rev = R_DISP_CONTROL_NORMAL;
38 /* Flipping */
39 #define R_DRV_OUTPUT_CONTROL_NORMAL 0x101b
40 #define R_DRV_OUTPUT_CONTROL_FLIPPED 0x131b
41 static unsigned short r_drv_output_control = R_DRV_OUTPUT_CONTROL_NORMAL;
43 #define LCD_DATA_IN_GPIO GPIOB_INPUT_VAL
44 #define LCD_DATA_IN_PIN 6
46 #define LCD_DATA_OUT_GPIO GPIOB_OUTPUT_VAL
47 #define LCD_DATA_OUT_PIN 7
49 #define LCD_CLOCK_GPIO GPIOB_OUTPUT_VAL
50 #define LCD_CLOCK_PIN 5
52 #define LCD_CS_GPIO GPIOD_OUTPUT_VAL
53 #define LCD_CS_PIN 6
55 #define LCD_REG_0 (*(volatile unsigned long *)(0xc2000000))
56 #define LCD_REG_1 (*(volatile unsigned long *)(0xc2000004))
57 #define LCD_REG_2 (*(volatile unsigned long *)(0xc2000008))
58 #define LCD_REG_3 (*(volatile unsigned long *)(0xc200000c))
59 #define LCD_REG_4 (*(volatile unsigned long *)(0xc2000010))
60 #define LCD_REG_5 (*(volatile unsigned long *)(0xc2000014))
61 #define LCD_REG_6 (*(volatile unsigned long *)(0xc2000018))
62 #define LCD_REG_7 (*(volatile unsigned long *)(0xc200001c))
63 #define LCD_REG_8 (*(volatile unsigned long *)(0xc2000020))
64 #define LCD_REG_9 (*(volatile unsigned long *)(0xc2000024))
65 #define LCD_FB_BASE_REG (*(volatile unsigned long *)(0xc2000028))
67 /* Taken from HD66789 datasheet and seems similar enough.
68 Definitely a Renesas chip though with a perfect register index
69 match but at least one bit seems to be set that that datasheet
70 doesn't show. It says T.B.D. on the regmap anyway. */
71 #define R_START_OSC 0x00
72 #define R_DRV_OUTPUT_CONTROL 0x01
73 #define R_DRV_WAVEFORM_CONTROL 0x02
74 #define R_ENTRY_MODE 0x03
75 #define R_COMPARE_REG1 0x04
76 #define R_COMPARE_REG2 0x05
77 #define R_DISP_CONTROL1 0x07
78 #define R_DISP_CONTROL2 0x08
79 #define R_DISP_CONTROL3 0x09
80 #define R_FRAME_CYCLE_CONTROL 0x0b
81 #define R_EXT_DISP_INTF_CONTROL 0x0c
82 #define R_POWER_CONTROL1 0x10
83 #define R_POWER_CONTROL2 0x11
84 #define R_POWER_CONTROL3 0x12
85 #define R_POWER_CONTROL4 0x13
86 #define R_RAM_ADDR_SET 0x21
87 #define R_RAM_READ_DATA 0x21
88 #define R_RAM_WRITE_DATA 0x22
89 #define R_RAM_WRITE_DATA_MASK1 0x23
90 #define R_RAM_WRITE_DATA_MASK2 0x24
91 #define R_GAMMA_FINE_ADJ_POS1 0x30
92 #define R_GAMMA_FINE_ADJ_POS2 0x31
93 #define R_GAMMA_FINE_ADJ_POS3 0x32
94 #define R_GAMMA_GRAD_ADJ_POS 0x33
95 #define R_GAMMA_FINE_ADJ_NEG1 0x34
96 #define R_GAMMA_FINE_ADJ_NEG2 0x35
97 #define R_GAMMA_FINE_ADJ_NEG3 0x36
98 #define R_GAMMA_GRAD_ADJ_NEG 0x37
99 #define R_GAMMA_AMP_ADJ_POS 0x38
100 #define R_GAMMA_AMP_ADJ_NEG 0x39
101 #define R_GATE_SCAN_START_POS 0x40
102 #define R_VERT_SCROLL_CONTROL 0x41
103 #define R_1ST_SCR_DRIVE_POS 0x42
104 #define R_2ND_SCR_DRIVE_POS 0x43
105 #define R_HORIZ_RAM_ADDR_POS 0x44
106 #define R_VERT_RAM_ADDR_POS 0x45
108 /* We don't know how to receive a DMA finished signal from the LCD controller
109 * To avoid problems with flickering, we double-buffer the framebuffer and turn
110 * off DMA while updates are taking place */
111 static fb_data lcd_driver_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH]
112 __attribute__((aligned(16))); /* Same alignment as in lcd-16bit.c */
114 #ifdef BOOTLOADER
115 static void lcd_init_gpio(void)
117 GPIOB_ENABLE |= (1<<7);
118 GPIOB_ENABLE |= (1<<5);
119 GPIOB_OUTPUT_EN |= (1<<7);
120 GPIOB_OUTPUT_EN |= (1<<5);
121 GPIOD_ENABLE |= (1<<6);
122 GPIOD_OUTPUT_EN |= (1<<6);
124 #endif
126 static void lcd_bus_idle(void)
128 LCD_CLOCK_GPIO |= (1 << LCD_CLOCK_PIN);
129 LCD_DATA_OUT_GPIO |= (1 << LCD_DATA_OUT_PIN);
132 static void lcd_send_byte(unsigned char byte)
135 int i;
137 for (i = 7; i >=0 ; i--)
139 LCD_CLOCK_GPIO &= ~(1 << LCD_CLOCK_PIN);
140 if ((byte >> i) & 1)
142 LCD_DATA_OUT_GPIO |= (1 << LCD_DATA_OUT_PIN);
143 } else {
144 LCD_DATA_OUT_GPIO &= ~(1 << LCD_DATA_OUT_PIN);
146 udelay(1);
147 LCD_CLOCK_GPIO |= (1 << LCD_CLOCK_PIN);
148 udelay(1);
149 lcd_bus_idle();
150 udelay(3);
154 static void lcd_send_msg(unsigned char cmd, unsigned int data)
156 lcd_bus_idle();
157 udelay(1);
158 LCD_CS_GPIO &= ~(1 << LCD_CS_PIN);
159 udelay(10);
160 lcd_send_byte(cmd);
161 lcd_send_byte((unsigned char)(data >> 8));
162 lcd_send_byte((unsigned char)(data & 0xff));
163 LCD_CS_GPIO |= (1 << LCD_CS_PIN);
164 udelay(1);
165 lcd_bus_idle();
168 static void lcd_write_reg(unsigned int reg, unsigned int data)
170 lcd_send_msg(0x70, reg);
171 lcd_send_msg(0x72, data);
174 /* The LCD controller gets passed the address of the framebuffer, but can only
175 use the physical, not the remapped, address. This is a quick and dirty way
176 of correcting it */
177 static unsigned long phys_fb_address(unsigned long address)
179 if(address < 0x10000000)
181 return address + 0x10000000;
182 } else {
183 return address;
187 /* Run the powerup sequence for the driver IC */
188 static void lcd_power_on(void)
190 /* Clear standby bit */
191 lcd_write_reg(R_POWER_CONTROL1, 0x0000);
193 /** Power ON Sequence **/
194 lcd_write_reg(R_START_OSC, 0x0001);
195 /* 10ms or more for oscillation circuit to stabilize */
196 sleep(HZ/50);
198 /* SAP2-0=100, BT2-0=100, AP2-0=100, DK=1, SLP=0, STB=0 */
199 lcd_write_reg(R_POWER_CONTROL1, 0x4444);
200 /* DC12-10=000, DC2-0=000, VC2-0=001 */
201 lcd_write_reg(R_POWER_CONTROL2, 0x0001);
202 /* PON=0, VRH3-0=0011 */
203 lcd_write_reg(R_POWER_CONTROL3, 0x0003);
204 /* VCOMG=0, VDV4-0=10001, VCM3-0=11001 */
205 lcd_write_reg(R_POWER_CONTROL4, 0x1119);
206 /* PON=1, VRH3-0=0011 */
207 lcd_write_reg(R_POWER_CONTROL3, 0x0013);
208 sleep(HZ/25);
210 /* SAP2-0=100, BT2-0=100, AP2-0=100, DK=0, SLP=0, STB=0 */
211 lcd_write_reg(R_POWER_CONTROL1, 0x4440);
212 /* VCOMG=1, VDV4-0=10001, VCM3-0=11001 */
213 lcd_write_reg(R_POWER_CONTROL4, 0x3119);
214 sleep(HZ/6);
216 /* VSPL=0, HSPL=0, DPL=1, EPL=0, SM=0, GS=x, SS=x, NL4-0=11011 */
217 lcd_write_reg(R_DRV_OUTPUT_CONTROL, r_drv_output_control);
218 /* FLD=0, FLD0=1, B/C=1, EOR=1, NW5-0=000000 */
219 lcd_write_reg(R_DRV_WAVEFORM_CONTROL, 0x0700);
220 /* TRI=0, DFM1-0=11, BGR=0, HWM=1, ID1-0=10, AM=0, LG2-0=000
221 * AM: horizontal update direction
222 * ID1-0: H decrement, V increment
224 lcd_write_reg(R_ENTRY_MODE, 0x6020);
225 lcd_write_reg(R_COMPARE_REG1, 0x0000);
226 lcd_write_reg(R_COMPARE_REG2, 0x0000);
227 /* FP3-0=0010, BP3-0=0010 */
228 lcd_write_reg(R_DISP_CONTROL2, 0x0202);
229 /* PTG1-0=00 (normal scan), ISC3-0=0000 (ignored) */
230 lcd_write_reg(R_DISP_CONTROL3, 0x0000);
231 /* NO2-0=01, SDT1-0=00, EQ1-0=01, DIV1-0=00, RTN3-0=0000 */
232 lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4400);
233 /* RM=1, DM1-0=01, RIM1-0=00 */
234 lcd_write_reg(R_EXT_DISP_INTF_CONTROL, 0x0110);
235 /* SCN4-0=00000 - G1 if GS=0, G240 if GS=1 */
236 lcd_write_reg(R_GATE_SCAN_START_POS, 0x0000);
237 /* VL7-0=00000000 (0 lines) */
238 lcd_write_reg(R_VERT_SCROLL_CONTROL, 0x0000);
239 /* SE17-10=219, SS17-10=0 - 220 gates */
240 lcd_write_reg(R_1ST_SCR_DRIVE_POS, (219 << 8));
241 /* SE27-10=0, SS27-10=0 - no second screen */
242 lcd_write_reg(R_2ND_SCR_DRIVE_POS, 0x0000);
243 /* HEA=175, HSA=0 = H window from 0-175 */
244 lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (175 << 8));
245 /* VEA=219, VSA=0 = V window from 0-219 */
246 lcd_write_reg(R_VERT_RAM_ADDR_POS, (219 << 8));
247 /* PKP12-10=000, PKP02-00=000 */
248 lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x0000);
249 /* PKP32-30=111, PKP22-20=100 */
250 lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0704);
251 /* PKP52-50=001, PKP42-40=111 */
252 lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0107);
253 /* PRP12-10=111, PRP02-00=100 */
254 lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x0704);
255 /* PKN12-10=001, PKN02-00=111 */
256 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0107);
257 /* PKN32-30=000, PKN22-20=010 */
258 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 0x0002);
259 /* PKN52-50=111, PKN42-40=111 */
260 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0707);
261 /* PRN12-10=101, PRN02-00=011 */
262 lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 0x0503);
263 /* VRP14-10=00000, VRP03-00=0000 */
264 lcd_write_reg(R_GAMMA_AMP_ADJ_POS, 0x0000);
265 /* WRN14-10=00000, VRN03-00=0000 */
266 lcd_write_reg(R_GAMMA_AMP_ADJ_NEG, 0x0000);
267 /* AD15-0=175 (upper right corner) */
268 lcd_write_reg(R_RAM_ADDR_SET, 175);
269 /* RM=1, DM1-0=01, RIM1-0=00 */
270 lcd_write_reg(R_EXT_DISP_INTF_CONTROL, 0x0110);
272 power_on = true;
275 /* Run the display on sequence for the driver IC */
276 static void lcd_display_on(void)
278 if (!power_on)
280 /* Power has been turned off so full reinit is needed */
281 lcd_power_on();
283 else
285 /* Restore what we fiddled with when turning display off */
286 /* PON=1, VRH3-0=0011 */
287 lcd_write_reg(R_POWER_CONTROL3, 0x0013);
288 /* NO2-0=01, SDT1-0=00, EQ1-0=01, DIV1-0=00, RTN3-0=0000 */
289 lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4400);
290 /* VCOMG=1, VDV4-0=10001, VCM3-0=11001 */
291 lcd_write_reg(R_POWER_CONTROL4, 0x3119);
294 /* SAP2-0=100, BT2-0=111, AP2-0=100, DK=1, SLP=0, STB=0 */
295 lcd_write_reg(R_POWER_CONTROL1, 0x4740);
297 sleep(HZ/25);
299 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=0, DTE=0, CL=0,
300 REV=x, D1-0=01 */
301 lcd_write_reg(R_DISP_CONTROL1, 0x0041 | r_disp_control_rev);
303 udelay(HZ/20);
305 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
306 REV=x, D1-0=01 */
307 lcd_write_reg(R_DISP_CONTROL1, 0x0061 | r_disp_control_rev);
308 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
309 REV=x, D1-0=11 */
310 lcd_write_reg(R_DISP_CONTROL1, 0x0063 | r_disp_control_rev);
312 udelay(HZ/20);
314 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=1, CL=0,
315 REV=x, D1-0=11 */
316 lcd_write_reg(R_DISP_CONTROL1, 0x0073 | r_disp_control_rev);
318 /* Go into write data mode */
319 lcd_send_msg(0x70, R_RAM_WRITE_DATA);
321 /* tell that we're on now */
322 display_on = true;
325 /* Turn off visible display operations */
326 static void lcd_display_off(void)
328 /* block drawing operations and changing of first */
329 display_on = false;
331 /* NO2-0=01, SDT1-0=00, EQ1-0=00, DIV1-0=00, RTN3-0=0000 */
332 lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x4000);
334 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=1, CL=0,
335 REV=x, D1-0=10 */
336 lcd_write_reg(R_DISP_CONTROL1, 0x0072 | r_disp_control_rev);
338 sleep(HZ/25);
340 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, DTE=0, CL=0,
341 REV=x, D1-0=10 */
342 lcd_write_reg(R_DISP_CONTROL1, 0x0062 | r_disp_control_rev);
344 sleep(HZ/25);
346 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=0, GON=0, DTE=0, CL=0,
347 REV=0, D1-0=00 */
348 lcd_write_reg(R_DISP_CONTROL1, 0x0000);
349 /* SAP2-0=000, BT2-0=000, AP2-0=000, DK=0, SLP=0, STBY=0 */
350 lcd_write_reg(R_POWER_CONTROL1, 0x0000);
351 /* PON=0, VRH3-0=0011 */
352 lcd_write_reg(R_POWER_CONTROL3, 0x0003);
353 /* VCOMG=0, VDV4-0=10001, VCM4-0=11001 */
354 lcd_write_reg(R_POWER_CONTROL4, 0x1119);
357 void lcd_init_device(void)
359 /* All this is magic worked out by MrH */
361 /* Stop any DMA which is in progress */
362 LCD_REG_6 &= ~1;
363 udelay(100000);
365 #ifdef BOOTLOADER /* Bother at all to do this again? */
366 /* Init GPIO ports */
367 lcd_init_gpio();
368 /* Controller init */
369 outl((inl(0x70000084) | (1 << 28)), 0x70000084);
370 outl((inl(0x70000080) & ~(1 << 28)), 0x70000080);
371 outl(((inl(0x70000010) & (0x03ffffff)) | (0x15 << 26)), 0x70000010);
372 outl(((inl(0x70000014) & (0x0fffffff)) | (0x5 << 28)), 0x70000014);
373 outl((inl(0x70000020) & ~(0x3 << 10)), 0x70000020);
374 DEV_EN |= DEV_LCD; /* Enable controller */
375 outl(0x6, 0x600060d0);
376 DEV_RS |= DEV_LCD; /* Reset controller */
377 outl((inl(0x70000020) & ~(1 << 14)), 0x70000020);
378 lcd_bus_idle();
379 DEV_RS &=~DEV_LCD; /* Clear reset */
380 udelay(1000);
382 LCD_REG_0 = (LCD_REG_0 & (0x00ffffff)) | (0x22 << 24);
383 LCD_REG_0 = (LCD_REG_0 & (0xff00ffff)) | (0x14 << 16);
384 LCD_REG_0 = (LCD_REG_0 & (0xffffc0ff)) | (0x3 << 8);
385 LCD_REG_0 = (LCD_REG_0 & (0xffffffc0)) | (0xa);
387 LCD_REG_1 &= 0x00ffffff;
388 LCD_REG_1 &= 0xff00ffff;
389 LCD_REG_1 = (LCD_REG_1 & 0xffff03ff) | (0x2 << 10);
390 LCD_REG_1 = (LCD_REG_1 & 0xfffffc00) | (0xdd);
392 LCD_REG_2 |= (1 << 5);
393 LCD_REG_2 |= (1 << 6);
394 LCD_REG_2 = (LCD_REG_2 & 0xfffffcff) | (0x2 << 8);
396 LCD_REG_7 &= (0xf800ffff);
397 LCD_REG_7 &= (0xfffff800);
399 LCD_REG_8 = (LCD_REG_8 & (0xf800ffff)) | (0xb0 << 16);
400 LCD_REG_8 = (LCD_REG_8 & (0xfffff800)) | (0xdc); /* X-Y Geometry? */
402 LCD_REG_5 |= 0xc;
403 LCD_REG_5 = (LCD_REG_5 & ~(0x70)) | (0x3 << 4);
404 LCD_REG_5 |= 2;
406 LCD_REG_6 &= ~(1 << 15);
407 LCD_REG_6 |= (0xe00);
408 LCD_REG_6 = (LCD_REG_6 & (0xffffff1f)) | (0x4 << 5);
409 LCD_REG_6 |= (1 << 4);
411 LCD_REG_5 &= ~(1 << 7);
412 LCD_FB_BASE_REG = phys_fb_address((unsigned long)lcd_driver_framebuffer);
414 udelay(100000);
416 /* LCD init */
417 /* Pull RESET low, then high to reset driver IC */
418 outl((inl(0x70000080) & ~(1 << 28)), 0x70000080);
419 udelay(10000);
420 outl((inl(0x70000080) | (1 << 28)), 0x70000080);
421 udelay(10000);
423 lcd_display_on();
424 #else
425 /* Power and display already ON - switch framebuffer address and reset
426 settings */
427 LCD_FB_BASE_REG = phys_fb_address((unsigned long)lcd_driver_framebuffer);
429 power_on = true;
430 display_on = true;
432 lcd_set_invert_display(false);
433 lcd_set_flip(false);
434 #endif
436 LCD_REG_6 |= 1; /* Start DMA */
439 void lcd_enable(bool on)
441 if (on == display_on)
442 return;
444 if (on)
446 DEV_EN |= DEV_LCD; /* Enable LCD controller */
447 lcd_display_on(); /* Turn on display */
448 lcd_update(); /* Resync display */
449 LCD_REG_6 |= 1; /* Restart DMA */
450 sleep(HZ/25); /* Wait for a frame to be written by
451 DMA or a white flash will happen */
453 else
455 LCD_REG_6 &= ~1; /* Disable DMA */
456 sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
457 lcd_display_off(); /* Turn off display */
458 DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
462 bool lcd_enabled(void)
464 return display_on;
467 void lcd_sleep(void)
469 LCD_REG_6 &= ~1;
470 sleep(HZ/50);
472 if (power_on)
474 /* Turn off display */
475 if (display_on)
476 lcd_display_off();
478 power_on = false;
481 /* Set standby mode */
482 /* SAP2-0=000, BT2-0=000, AP2-0=000, DK=0, SLP=0, STB=1 */
483 lcd_write_reg(R_POWER_CONTROL1, 0x0001);
486 /* Copies a rectangle from one framebuffer to another. Can be used in
487 single transfer mode with width = num pixels, and height = 1 which
488 allows a full-width rectangle to be copied more efficiently. */
489 extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src,
490 int width, int height);
491 void lcd_update_rect(int x, int y, int width, int height)
493 fb_data *dst, *src;
495 if (!display_on)
496 return;
498 if (x + width > LCD_WIDTH)
499 width = LCD_WIDTH - x; /* Clip right */
500 if (x < 0)
501 width += x, x = 0; /* Clip left */
502 if (width <= 0)
503 return; /* nothing left to do */
505 if (y + height > LCD_HEIGHT)
506 height = LCD_HEIGHT - y; /* Clip bottom */
507 if (y < 0)
508 height += y, y = 0; /* Clip top */
509 if (height <= 0)
510 return; /* nothing left to do */
512 /* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer
513 * and lcd_framebuffer */
514 dst = &lcd_driver_framebuffer[y][x];
515 src = &lcd_framebuffer[y][x];
517 /* Copy part of the Rockbox framebuffer to the second framebuffer */
518 if (width < LCD_WIDTH)
520 /* Not full width - do line-by-line */
521 lcd_copy_buffer_rect(dst, src, width, height);
523 else
525 /* Full width - copy as one line */
526 lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1);
529 flush_icache();
532 void lcd_update(void)
534 if (!display_on)
535 return;
537 /* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer
538 * and lcd_framebuffer */
539 /* Copy the Rockbox framebuffer to the second framebuffer */
540 lcd_copy_buffer_rect(&lcd_driver_framebuffer[0][0],
541 &lcd_framebuffer[0][0], LCD_WIDTH*LCD_HEIGHT, 1);
543 flush_icache();
547 /*** hardware configuration ***/
549 void lcd_set_contrast(int val)
551 /* TODO: Implement lcd_set_contrast() */
552 (void)val;
555 void lcd_set_invert_display(bool yesno)
557 bool dma_on = LCD_REG_6 & 1;
559 if (dma_on)
561 LCD_REG_6 &= ~1; /* Disable DMA */
562 sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
563 DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
566 r_disp_control_rev = yesno ? R_DISP_CONTROL_REV :
567 R_DISP_CONTROL_NORMAL;
569 if (display_on)
571 /* PT1-0=00, VLE2-1=00, SPT=0, IB6(??)=1, GON=1, CL=0,
572 DTE=1, REV=x, D1-0=11 */
573 lcd_write_reg(R_DISP_CONTROL1, 0x0073 | r_disp_control_rev);
576 if (dma_on)
578 DEV_EN |= DEV_LCD; /* Enable LCD controller */
579 lcd_send_msg(0x70, R_RAM_WRITE_DATA); /* Set to RAM write mode */
580 LCD_REG_6 |= 1; /* Restart DMA */
584 /* turn the display upside down (call lcd_update() afterwards) */
585 void lcd_set_flip(bool yesno)
587 bool dma_on = LCD_REG_6 & 1;
589 if (dma_on)
591 LCD_REG_6 &= ~1; /* Disable DMA */
592 sleep(HZ/50); /* Wait for dma end (assuming 50Hz) */
593 DEV_EN &= ~DEV_LCD; /* Disable LCD controller */
596 r_drv_output_control = yesno ? R_DRV_OUTPUT_CONTROL_FLIPPED :
597 R_DRV_OUTPUT_CONTROL_NORMAL;
599 if (power_on)
601 /* VSPL=0, HSPL=0, DPL=1, EPL=0, SM=0, GS=x, SS=x,
602 NL4-0=11011 (G1-G224) */
603 lcd_write_reg(R_DRV_OUTPUT_CONTROL, r_drv_output_control);
606 if (dma_on)
608 DEV_EN |= DEV_LCD; /* Enable LCD controller */
609 lcd_send_msg(0x70, R_RAM_WRITE_DATA); /* Set to RAM write mode */
610 LCD_REG_6 |= 1; /* Restart DMA */
614 /* Blitting functions */
616 void lcd_blit(const fb_data* data, int x, int by, int width,
617 int bheight, int stride)
619 /* TODO: Implement lcd_blit() */
620 (void)data;
621 (void)x;
622 (void)by;
623 (void)width;
624 (void)bheight;
625 (void)stride;
628 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
629 extern void lcd_write_yuv420_lines(fb_data *dst,
630 unsigned char chroma_buf[LCD_HEIGHT/2*3],
631 unsigned char const * const src[3],
632 int width,
633 int stride);
634 /* Performance function to blit a YUV bitmap directly to the LCD */
635 /* For the e200 - show it rotated */
636 /* So the LCD_WIDTH is now the height */
637 void lcd_yuv_blit(unsigned char * const src[3],
638 int src_x, int src_y, int stride,
639 int x, int y, int width, int height)
641 /* Caches for chroma data so it only need be recaculated every other
642 line */
643 static unsigned char chroma_buf[LCD_HEIGHT/2*3]; /* 330 bytes */
644 unsigned char const * yuv_src[3];
645 off_t z;
647 if (!display_on)
648 return;
650 /* Sorry, but width and height must be >= 2 or else */
651 width &= ~1;
652 height >>= 1;
654 fb_data *dst = (fb_data*)lcd_driver_framebuffer +
655 x * LCD_WIDTH + (LCD_WIDTH - y) - 1;
657 z = stride*src_y;
658 yuv_src[0] = src[0] + z + src_x;
659 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
660 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
664 lcd_write_yuv420_lines(dst, chroma_buf, yuv_src, width,
665 stride);
666 yuv_src[0] += stride << 1; /* Skip down two luma lines */
667 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
668 yuv_src[2] += stride >> 1;
669 dst -= 2;
671 while (--height > 0);