FS#11807 - Major speedup of iPod nano 2G. Part 4: Introduce asm for yuv blitting...
[kugel-rb.git] / firmware / target / arm / s5l8700 / ipodnano2g / lcd-nano2g.c
blobd1c41d652f03c64cd43c4d26ff0509dae29de905
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" */
320 LCD_CON |= 0x100; /* use 16 bit bus width, little endian */
322 lcd_ispowered = true;
325 /*** Update functions ***/
327 static inline void lcd_write_pixel(fb_data pixel)
329 LCD_WDATA = pixel;
332 /* Update the display.
333 This must be called after all other LCD functions that change the display. */
334 void lcd_update(void) ICODE_ATTR;
335 void lcd_update(void)
337 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
340 /* Update a fraction of the display. */
341 void lcd_update_rect(int, int, int, int) ICODE_ATTR;
342 void lcd_update_rect(int x, int y, int width, int height)
344 int y0, x0, y1, x1;
345 fb_data* p;
347 width = (width + 1) & ~1; /* ensure width is even */
349 x0 = x; /* start horiz */
350 y0 = y; /* start vert */
351 x1 = (x + width) - 1; /* max horiz */
352 y1 = (y + height) - 1; /* max vert */
354 if (lcd_type==0) {
355 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
356 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
357 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
358 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
360 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
361 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
363 s5l_lcd_write_cmd(0);
364 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
365 } else {
366 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
367 s5l_lcd_write_data(x0); /* Start column */
368 s5l_lcd_write_data(x1); /* End column */
370 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
371 s5l_lcd_write_data(y0); /* Start row */
372 s5l_lcd_write_data(y1); /* End row */
374 s5l_lcd_write_cmd(R_MEMORY_WRITE);
377 /* Copy display bitmap to hardware */
378 p = &lcd_framebuffer[y0][x0];
379 if (LCD_WIDTH == width)
381 x1 = height*LCD_WIDTH/4;
382 do {
383 while (LCD_STATUS & 0x08); /* wait while FIFO is half full */
384 lcd_write_pixel(*(p++));
385 lcd_write_pixel(*(p++));
386 lcd_write_pixel(*(p++));
387 lcd_write_pixel(*(p++));
388 } while (--x1 > 0);
389 } else {
390 y1 = height;
391 do {
392 x1 = width/2; /* width is forced to even to allow speed up */
393 do {
394 while (LCD_STATUS & 0x08); /* wait while FIFO is half full */
395 lcd_write_pixel(*(p++));
396 lcd_write_pixel(*(p++));
397 } while (--x1 > 0 );
398 p += LCD_WIDTH - width;
399 } while (--y1 > 0 );
403 /* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
404 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
405 unsigned lcd_baseadress,
406 int width,
407 int stride);
409 /* Blit a YUV bitmap directly to the LCD */
410 void lcd_blit_yuv(unsigned char * const src[3],
411 int src_x, int src_y, int stride,
412 int x, int y, int width, int height)
414 unsigned int z, y0, x0, y1, x1;;
415 unsigned char const * yuv_src[3];
417 width = (width + 1) & ~1; /* ensure width is even */
419 x0 = x; /* start horiz */
420 y0 = y; /* start vert */
421 x1 = (x + width) - 1; /* max horiz */
422 y1 = (y + height) - 1; /* max vert */
424 if (lcd_type==0) {
425 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
426 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
427 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
428 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
430 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
431 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
433 s5l_lcd_write_cmd(0);
434 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
435 } else {
436 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
437 s5l_lcd_write_data(x0); /* Start column */
438 s5l_lcd_write_data(x1); /* End column */
440 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
441 s5l_lcd_write_data(y0); /* Start row */
442 s5l_lcd_write_data(y1); /* End row */
444 s5l_lcd_write_cmd(R_MEMORY_WRITE);
447 z = stride * src_y;
448 yuv_src[0] = src[0] + z + src_x;
449 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
450 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
452 height >>= 1;
454 do {
455 lcd_write_yuv420_lines(yuv_src, LCD_BASE, width, stride);
456 yuv_src[0] += stride << 1;
457 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
458 yuv_src[2] += stride >> 1;
459 } while (--height > 0);