Add short sleep after wakeup of LDS176 type LCD to avoid occasional shift of first...
[maemo-rb.git] / firmware / target / arm / s5l8700 / ipodnano2g / lcd-nano2g.c
blobe4193d1efa8d1ed0497067baf06d0c4d9f2b2006
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,
146 SLEEP, 0x0000,
151 #endif /* HAVE_LCD_SLEEP */
153 static inline void s5l_lcd_write_cmd_data(int cmd, int data)
155 while (LCD_STATUS & 0x10);
156 LCD_WCMD = cmd;
158 while (LCD_STATUS & 0x10);
159 LCD_WDATA = data;
162 static inline void s5l_lcd_write_cmd(unsigned short cmd)
164 while (LCD_STATUS & 0x10);
165 LCD_WCMD = cmd;
168 static inline void s5l_lcd_write_data(unsigned short data)
170 while (LCD_STATUS & 0x10);
171 LCD_WDATA = data;
174 /*** hardware configuration ***/
176 int lcd_default_contrast(void)
178 return 0x1f;
181 void lcd_set_contrast(int val)
183 (void)val;
186 void lcd_set_invert_display(bool yesno)
188 (void)yesno;
191 /* turn the display upside down (call lcd_update() afterwards) */
192 void lcd_set_flip(bool yesno)
194 /* TODO: flip mode isn't working. The commands in the else part of
195 this function are how the original firmware inits the LCD */
197 if (yesno)
199 xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 128 we have */
201 else
203 xoffset = 0;
207 bool lcd_active(void)
209 return lcd_ispowered;
212 #ifdef HAVE_LCD_SLEEP
214 void lcd_wakeup(void)
216 unsigned short *lcd_init_sequence;
217 unsigned int lcd_init_sequence_length;
219 PWRCONEXT &= ~0x80;
220 PCON13 &= ~0xf; /* Set pin 0 to input */
221 PCON14 &= ~0xf0; /* Set pin 1 to input */
223 pmu_write(0x2b, 1);
225 if (lcd_type == 0)
227 /* reset the lcd chip */
229 LCD_RST_TIME = 0x7FFF;
230 LCD_DRV_RST = 0;
231 sleep(0);
232 LCD_DRV_RST = 1;
233 sleep(HZ / 100);
235 lcd_init_sequence = lcd_init_sequence_0;
236 lcd_init_sequence_length = (sizeof(lcd_init_sequence_0) - 1)/sizeof(unsigned short);
238 else
240 lcd_init_sequence = lcd_init_sequence_1;
241 lcd_init_sequence_length = (sizeof(lcd_init_sequence_1) - 1)/sizeof(unsigned short);
244 for(unsigned int i=0;i<lcd_init_sequence_length;i+=2)
246 switch(lcd_init_sequence[i])
248 case CMD16:
249 s5l_lcd_write_cmd(lcd_init_sequence[i+1]);
250 break;
251 case DATA16:
252 s5l_lcd_write_data(lcd_init_sequence[i+1]);
253 break;
254 case SLEEP:
255 sleep(lcd_init_sequence[i+1]);
256 break;
257 default:
258 break;
261 lcd_ispowered = true;
262 send_event(LCD_EVENT_ACTIVATION, NULL);
265 void lcd_awake(void)
267 if(!lcd_active()) lcd_wakeup();
269 #endif
271 void lcd_shutdown(void)
273 pmu_write(0x2b, 0); /* Kill the backlight, instantly. */
274 pmu_write(0x29, 0);
276 if (lcd_type == 0)
278 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0232);
279 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x1137);
280 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0201);
281 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x0137);
282 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x0200);
283 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1 , 0x0680);
284 s5l_lcd_write_cmd_data(R_POWER_CONTROL_2 , 0x0160);
285 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3 , 0x0127);
286 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1 , 0x0600);
288 else
290 s5l_lcd_write_cmd(R_DISPLAY_OFF);
291 s5l_lcd_write_data(0);
292 s5l_lcd_write_data(0);
293 s5l_lcd_write_cmd(R_SLEEP_IN);
294 s5l_lcd_write_data(0);
295 s5l_lcd_write_data(0);
298 PWRCONEXT |= 0x80;
300 lcd_ispowered = false;
303 void lcd_sleep(void)
305 lcd_shutdown();
308 /* LCD init */
309 void lcd_init_device(void)
311 /* Detect lcd type */
313 PCON13 &= ~0xf; /* Set pin 0 to input */
314 PCON14 &= ~0xf0; /* Set pin 1 to input */
316 if (((PDAT13 & 1) == 0) && ((PDAT14 & 2) == 2)) {
317 lcd_type = 0; /* Similar to ILI9320 - aka "type 2" */
318 } else {
319 lcd_type = 1; /* Similar to LDS176 - aka "type 7" */
320 LCD_PHTIME = 0x22; /* Set Phase Time reg (relevant for LCD IF speed) */
323 LCD_CON |= 0x100; /* use 16 bit bus width, little endian */
325 lcd_ispowered = true;
328 /*** Update functions ***/
330 static inline void lcd_write_pixel(fb_data pixel)
332 LCD_WDATA = pixel;
335 /* Update the display.
336 This must be called after all other LCD functions that change the display. */
337 void lcd_update(void) ICODE_ATTR;
338 void lcd_update(void)
340 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
343 /* Line write helper function. */
344 extern void lcd_write_line(const fb_data *addr,
345 int pixelcount,
346 const unsigned int lcd_base_addr);
348 /* Update a fraction of the display. */
349 void lcd_update_rect(int, int, int, int) ICODE_ATTR;
350 void lcd_update_rect(int x, int y, int width, int height)
352 int y0, x0, y1, x1;
353 fb_data* p;
355 /* Both x and width need to be preprocessed due to asm optimizations */
356 x = x & ~1; /* ensure x is even */
357 width = (width + 3) & ~3; /* ensure width is a multiple of 4 */
359 x0 = x; /* start horiz */
360 y0 = y; /* start vert */
361 x1 = (x + width) - 1; /* max horiz */
362 y1 = (y + height) - 1; /* max vert */
364 if (lcd_type==0) {
365 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
366 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
367 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
368 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
370 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
371 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
373 s5l_lcd_write_cmd(0);
374 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
375 } else {
376 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
377 s5l_lcd_write_data(x0); /* Start column */
378 s5l_lcd_write_data(x1); /* End column */
380 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
381 s5l_lcd_write_data(y0); /* Start row */
382 s5l_lcd_write_data(y1); /* End row */
384 s5l_lcd_write_cmd(R_MEMORY_WRITE);
387 /* Copy display bitmap to hardware */
388 p = &lcd_framebuffer[y0][x0];
389 if (LCD_WIDTH == width) {
390 /* Write all lines at once */
391 lcd_write_line(p, height*LCD_WIDTH, LCD_BASE);
392 } else {
393 y1 = height;
394 do {
395 /* Write a single line */
396 lcd_write_line(p, width, LCD_BASE);
397 p += LCD_WIDTH;
398 } while (--y1 > 0 );
402 /* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
403 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
404 const unsigned int lcd_baseadress,
405 int width,
406 int stride);
408 /* Blit a YUV bitmap directly to the LCD */
409 void lcd_blit_yuv(unsigned char * const src[3],
410 int src_x, int src_y, int stride,
411 int x, int y, int width, int height)
413 unsigned int z, y0, x0, y1, x1;;
414 unsigned char const * yuv_src[3];
416 width = (width + 1) & ~1; /* ensure width is even */
418 x0 = x; /* start horiz */
419 y0 = y; /* start vert */
420 x1 = (x + width) - 1; /* max horiz */
421 y1 = (y + height) - 1; /* max vert */
423 if (lcd_type==0) {
424 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
425 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
426 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
427 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
429 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
430 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
432 s5l_lcd_write_cmd(0);
433 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
434 } else {
435 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
436 s5l_lcd_write_data(x0); /* Start column */
437 s5l_lcd_write_data(x1); /* End column */
439 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
440 s5l_lcd_write_data(y0); /* Start row */
441 s5l_lcd_write_data(y1); /* End row */
443 s5l_lcd_write_cmd(R_MEMORY_WRITE);
446 z = stride * src_y;
447 yuv_src[0] = src[0] + z + src_x;
448 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
449 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
451 height >>= 1;
453 do {
454 lcd_write_yuv420_lines(yuv_src, LCD_BASE, width, stride);
455 yuv_src[0] += stride << 1;
456 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
457 yuv_src[2] += stride >> 1;
458 } while (--height > 0);