FS#11807 - Major speedup of iPod nano 2G. Part 5: Introduce asm for RGB screen update...
[kugel-rb.git] / firmware / target / arm / s5l8700 / ipodnano2g / lcd-nano2g.c
blob7b177f4fc411a3927dc9300dd98243000f752139
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2009 by Dave Chapman
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
23 #include "hwcompat.h"
24 #include "kernel.h"
25 #include "lcd.h"
26 #include "system.h"
27 #include "cpu.h"
28 #include "pmu-target.h"
29 #include "power.h"
32 /* The Nano 2G has two different LCD types. What we call "type 0"
33 appears to be similar to the ILI9320 and "type 1" is similar to the
34 LDS176.
37 /* LCD type 0 register defines */
39 #define R_ENTRY_MODE 0x03
40 #define R_DISPLAY_CONTROL_1 0x07
41 #define R_POWER_CONTROL_1 0x10
42 #define R_POWER_CONTROL_2 0x12
43 #define R_POWER_CONTROL_3 0x13
44 #define R_HORIZ_GRAM_ADDR_SET 0x20
45 #define R_VERT_GRAM_ADDR_SET 0x21
46 #define R_WRITE_DATA_TO_GRAM 0x22
47 #define R_HORIZ_ADDR_START_POS 0x50
48 #define R_HORIZ_ADDR_END_POS 0x51
49 #define R_VERT_ADDR_START_POS 0x52
50 #define R_VERT_ADDR_END_POS 0x53
53 /* LCD type 1 register defines */
55 #define R_SLEEP_IN 0x10
56 #define R_DISPLAY_OFF 0x28
57 #define R_COLUMN_ADDR_SET 0x2a
58 #define R_ROW_ADDR_SET 0x2b
59 #define R_MEMORY_WRITE 0x2c
61 /** globals **/
63 int lcd_type; /* also needed in debug-s5l8700.c */
64 static int xoffset; /* needed for flip */
65 static bool lcd_ispowered;
67 #ifdef HAVE_LCD_SLEEP
69 #define SLEEP 0
70 #define CMD16 1
71 #define DATA16 2
73 unsigned short lcd_init_sequence_0[] = {
74 CMD16, 0x00a4, DATA16, 0x0001,
75 SLEEP, 0x0000,
76 CMD16, 0x0001, DATA16, 0x0100,
77 CMD16, 0x0002, DATA16, 0x0300,
78 CMD16, 0x0003, DATA16, 0x1230,
79 CMD16, 0x0008, DATA16, 0x0404,
80 CMD16, 0x0008, DATA16, 0x0404,
81 CMD16, 0x000e, DATA16, 0x0010,
82 CMD16, 0x0070, DATA16, 0x1000,
83 CMD16, 0x0071, DATA16, 0x0001,
84 CMD16, 0x0030, DATA16, 0x0002,
85 CMD16, 0x0031, DATA16, 0x0400,
86 CMD16, 0x0032, DATA16, 0x0007,
87 CMD16, 0x0033, DATA16, 0x0500,
88 CMD16, 0x0034, DATA16, 0x0007,
89 CMD16, 0x0035, DATA16, 0x0703,
90 CMD16, 0x0036, DATA16, 0x0507,
91 CMD16, 0x0037, DATA16, 0x0005,
92 CMD16, 0x0038, DATA16, 0x0407,
93 CMD16, 0x0039, DATA16, 0x000e,
94 CMD16, 0x0040, DATA16, 0x0202,
95 CMD16, 0x0041, DATA16, 0x0003,
96 CMD16, 0x0042, DATA16, 0x0000,
97 CMD16, 0x0043, DATA16, 0x0200,
98 CMD16, 0x0044, DATA16, 0x0707,
99 CMD16, 0x0045, DATA16, 0x0407,
100 CMD16, 0x0046, DATA16, 0x0505,
101 CMD16, 0x0047, DATA16, 0x0002,
102 CMD16, 0x0048, DATA16, 0x0004,
103 CMD16, 0x0049, DATA16, 0x0004,
104 CMD16, 0x0060, DATA16, 0x0202,
105 CMD16, 0x0061, DATA16, 0x0003,
106 CMD16, 0x0062, DATA16, 0x0000,
107 CMD16, 0x0063, DATA16, 0x0200,
108 CMD16, 0x0064, DATA16, 0x0707,
109 CMD16, 0x0065, DATA16, 0x0407,
110 CMD16, 0x0066, DATA16, 0x0505,
111 CMD16, 0x0068, DATA16, 0x0004,
112 CMD16, 0x0069, DATA16, 0x0004,
113 CMD16, 0x0007, DATA16, 0x0001,
114 CMD16, 0x0018, DATA16, 0x0001,
115 CMD16, 0x0010, DATA16, 0x1690,
116 CMD16, 0x0011, DATA16, 0x0100,
117 CMD16, 0x0012, DATA16, 0x0117,
118 CMD16, 0x0013, DATA16, 0x0f80,
119 CMD16, 0x0012, DATA16, 0x0137,
120 CMD16, 0x0020, DATA16, 0x0000,
121 CMD16, 0x0021, DATA16, 0x0000,
122 CMD16, 0x0050, DATA16, 0x0000,
123 CMD16, 0x0051, DATA16, 0x00af,
124 CMD16, 0x0052, DATA16, 0x0000,
125 CMD16, 0x0053, DATA16, 0x0083,
126 CMD16, 0x0090, DATA16, 0x0003,
127 CMD16, 0x0091, DATA16, 0x0000,
128 CMD16, 0x0092, DATA16, 0x0101,
129 CMD16, 0x0098, DATA16, 0x0400,
130 CMD16, 0x0099, DATA16, 0x1302,
131 CMD16, 0x009a, DATA16, 0x0202,
132 CMD16, 0x009b, DATA16, 0x0200,
133 SLEEP, 0x0000,
134 CMD16, 0x0007, DATA16, 0x0021,
135 CMD16, 0x0012, DATA16, 0x0137,
136 SLEEP, 0x0000,
137 CMD16, 0x0007, DATA16, 0x0021,
138 CMD16, 0x0012, DATA16, 0x1137,
139 SLEEP, 0x0000,
140 CMD16, 0x0007, DATA16, 0x0233,
143 unsigned short lcd_init_sequence_1[] = {
144 CMD16, 0x0011, DATA16, 0x0000,
145 CMD16, 0x0029, DATA16, 0x0000,
150 #endif /* HAVE_LCD_SLEEP */
152 static inline void s5l_lcd_write_cmd_data(int cmd, int data)
154 while (LCD_STATUS & 0x10);
155 LCD_WCMD = cmd;
157 while (LCD_STATUS & 0x10);
158 LCD_WDATA = data;
161 static inline void s5l_lcd_write_cmd(unsigned short cmd)
163 while (LCD_STATUS & 0x10);
164 LCD_WCMD = cmd;
167 static inline void s5l_lcd_write_data(unsigned short data)
169 while (LCD_STATUS & 0x10);
170 LCD_WDATA = data;
173 /*** hardware configuration ***/
175 int lcd_default_contrast(void)
177 return 0x1f;
180 void lcd_set_contrast(int val)
182 (void)val;
185 void lcd_set_invert_display(bool yesno)
187 (void)yesno;
190 /* turn the display upside down (call lcd_update() afterwards) */
191 void lcd_set_flip(bool yesno)
193 /* TODO: flip mode isn't working. The commands in the else part of
194 this function are how the original firmware inits the LCD */
196 if (yesno)
198 xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 128 we have */
200 else
202 xoffset = 0;
206 bool lcd_active(void)
208 return lcd_ispowered;
211 #ifdef HAVE_LCD_SLEEP
213 void lcd_wakeup(void)
215 unsigned short *lcd_init_sequence;
216 unsigned int lcd_init_sequence_length;
218 PWRCONEXT &= ~0x80;
219 PCON13 &= ~0xf; /* Set pin 0 to input */
220 PCON14 &= ~0xf0; /* Set pin 1 to input */
222 pmu_write(0x2b, 1);
224 if (lcd_type == 0)
226 /* reset the lcd chip */
228 LCD_RST_TIME = 0x7FFF;
229 LCD_DRV_RST = 0;
230 sleep(0);
231 LCD_DRV_RST = 1;
232 sleep(HZ / 100);
234 lcd_init_sequence = lcd_init_sequence_0;
235 lcd_init_sequence_length = (sizeof(lcd_init_sequence_0) - 1)/sizeof(unsigned short);
237 else
239 lcd_init_sequence = lcd_init_sequence_1;
240 lcd_init_sequence_length = (sizeof(lcd_init_sequence_1) - 1)/sizeof(unsigned short);
243 for(unsigned int i=0;i<lcd_init_sequence_length;i+=2)
245 switch(lcd_init_sequence[i])
247 case CMD16:
248 s5l_lcd_write_cmd(lcd_init_sequence[i+1]);
249 break;
250 case DATA16:
251 s5l_lcd_write_data(lcd_init_sequence[i+1]);
252 break;
253 case SLEEP:
254 sleep(lcd_init_sequence[i+1]);
255 break;
256 default:
257 break;
260 lcd_ispowered = true;
261 send_event(LCD_EVENT_ACTIVATION, NULL);
264 void lcd_awake(void)
266 if(!lcd_active()) lcd_wakeup();
268 #endif
270 void lcd_shutdown(void)
272 pmu_write(0x2b, 0); /* Kill the backlight, instantly. */
273 pmu_write(0x29, 0);
275 if (lcd_type == 0)
277 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0232);
278 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x1137);
279 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0201);
280 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x0137);
281 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0200);
282 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1 , 0x0680);
283 s5l_lcd_write_cmd_data(R_POWER_CONTROL_2 , 0x0160);
284 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x0127);
285 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1 , 0x0600);
287 else
289 s5l_lcd_write_cmd(R_DISPLAY_OFF);
290 s5l_lcd_write_data(0);
291 s5l_lcd_write_data(0);
292 s5l_lcd_write_cmd(R_SLEEP_IN);
293 s5l_lcd_write_data(0);
294 s5l_lcd_write_data(0);
297 PWRCONEXT |= 0x80;
299 lcd_ispowered = false;
302 void lcd_sleep(void)
304 lcd_shutdown();
307 /* LCD init */
308 void lcd_init_device(void)
310 /* Detect lcd type */
312 PCON13 &= ~0xf; /* Set pin 0 to input */
313 PCON14 &= ~0xf0; /* Set pin 1 to input */
315 if (((PDAT13 & 1) == 0) && ((PDAT14 & 2) == 2)) {
316 lcd_type = 0; /* Similar to ILI9320 - aka "type 2" */
317 } else {
318 lcd_type = 1; /* Similar to LDS176 - aka "type 7" */
319 LCD_PHTIME = 0x22; /* Set Phase Time reg (relevant for LCD IF speed) */
322 LCD_CON |= 0x100; /* use 16 bit bus width, little endian */
324 lcd_ispowered = true;
327 /*** Update functions ***/
329 static inline void lcd_write_pixel(fb_data pixel)
331 LCD_WDATA = pixel;
334 /* Update the display.
335 This must be called after all other LCD functions that change the display. */
336 void lcd_update(void) ICODE_ATTR;
337 void lcd_update(void)
339 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
342 /* Line write helper function. */
343 extern void lcd_write_line(const fb_data *addr,
344 int pixelcount,
345 const unsigned int lcd_base_addr);
347 /* Update a fraction of the display. */
348 void lcd_update_rect(int, int, int, int) ICODE_ATTR;
349 void lcd_update_rect(int x, int y, int width, int height)
351 int y0, x0, y1, x1;
352 fb_data* p;
354 /* Both x and width need to be preprocessed due to asm optimizations */
355 x = x & ~1; /* ensure x is even */
356 width = (width + 3) & ~3; /* ensure width is a multiple of 4 */
358 x0 = x; /* start horiz */
359 y0 = y; /* start vert */
360 x1 = (x + width) - 1; /* max horiz */
361 y1 = (y + height) - 1; /* max vert */
363 if (lcd_type==0) {
364 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
365 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
366 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
367 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
369 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
370 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
372 s5l_lcd_write_cmd(0);
373 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
374 } else {
375 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
376 s5l_lcd_write_data(x0); /* Start column */
377 s5l_lcd_write_data(x1); /* End column */
379 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
380 s5l_lcd_write_data(y0); /* Start row */
381 s5l_lcd_write_data(y1); /* End row */
383 s5l_lcd_write_cmd(R_MEMORY_WRITE);
386 /* Copy display bitmap to hardware */
387 p = &lcd_framebuffer[y0][x0];
388 if (LCD_WIDTH == width) {
389 /* Write all lines at once */
390 lcd_write_line(p, height*LCD_WIDTH, LCD_BASE);
391 } else {
392 y1 = height;
393 do {
394 /* Write a single line */
395 lcd_write_line(p, width, LCD_BASE);
396 p += LCD_WIDTH;
397 } while (--y1 > 0 );
401 /* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
402 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
403 const unsigned int lcd_baseadress,
404 int width,
405 int stride);
407 /* Blit a YUV bitmap directly to the LCD */
408 void lcd_blit_yuv(unsigned char * const src[3],
409 int src_x, int src_y, int stride,
410 int x, int y, int width, int height)
412 unsigned int z, y0, x0, y1, x1;;
413 unsigned char const * yuv_src[3];
415 width = (width + 1) & ~1; /* ensure width is even */
417 x0 = x; /* start horiz */
418 y0 = y; /* start vert */
419 x1 = (x + width) - 1; /* max horiz */
420 y1 = (y + height) - 1; /* max vert */
422 if (lcd_type==0) {
423 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
424 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
425 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
426 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
428 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
429 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
431 s5l_lcd_write_cmd(0);
432 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
433 } else {
434 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
435 s5l_lcd_write_data(x0); /* Start column */
436 s5l_lcd_write_data(x1); /* End column */
438 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
439 s5l_lcd_write_data(y0); /* Start row */
440 s5l_lcd_write_data(y1); /* End row */
442 s5l_lcd_write_cmd(R_MEMORY_WRITE);
445 z = stride * src_y;
446 yuv_src[0] = src[0] + z + src_x;
447 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
448 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
450 height >>= 1;
452 do {
453 lcd_write_yuv420_lines(yuv_src, LCD_BASE, width, stride);
454 yuv_src[0] += stride << 1;
455 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
456 yuv_src[2] += stride >> 1;
457 } while (--height > 0);