1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
28 #include "pmu-target.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
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
63 int lcd_type
; /* also needed in debug-s5l8700.c */
64 static int xoffset
; /* needed for flip */
65 static bool lcd_ispowered
;
73 unsigned short lcd_init_sequence_0
[] = {
74 CMD16
, 0x00a4, DATA16
, 0x0001,
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,
134 CMD16
, 0x0007, DATA16
, 0x0021,
135 CMD16
, 0x0012, DATA16
, 0x0137,
137 CMD16
, 0x0007, DATA16
, 0x0021,
138 CMD16
, 0x0012, DATA16
, 0x1137,
140 CMD16
, 0x0007, DATA16
, 0x0233,
143 unsigned short lcd_init_sequence_1
[] = {
144 CMD16
, 0x0011, DATA16
, 0x0000,
145 CMD16
, 0x0029, DATA16
, 0x0000,
151 #endif /* HAVE_LCD_SLEEP */
153 static inline void s5l_lcd_write_cmd_data(int cmd
, int data
)
155 while (LCD_STATUS
& 0x10);
158 while (LCD_STATUS
& 0x10);
162 static inline void s5l_lcd_write_cmd(unsigned short cmd
)
164 while (LCD_STATUS
& 0x10);
168 static inline void s5l_lcd_write_data(unsigned short data
)
170 while (LCD_STATUS
& 0x10);
174 /*** hardware configuration ***/
176 int lcd_default_contrast(void)
181 void lcd_set_contrast(int val
)
186 void lcd_set_invert_display(bool 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 */
199 xoffset
= 132 - LCD_WIDTH
; /* 132 colums minus the 128 we have */
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
;
220 PCON13
&= ~0xf; /* Set pin 0 to input */
221 PCON14
&= ~0xf0; /* Set pin 1 to input */
227 /* reset the lcd chip */
229 LCD_RST_TIME
= 0x7FFF;
235 lcd_init_sequence
= lcd_init_sequence_0
;
236 lcd_init_sequence_length
= (sizeof(lcd_init_sequence_0
) - 1)/sizeof(unsigned short);
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
])
249 s5l_lcd_write_cmd(lcd_init_sequence
[i
+1]);
252 s5l_lcd_write_data(lcd_init_sequence
[i
+1]);
255 sleep(lcd_init_sequence
[i
+1]);
261 lcd_ispowered
= true;
262 send_event(LCD_EVENT_ACTIVATION
, NULL
);
267 if(!lcd_active()) lcd_wakeup();
271 void lcd_shutdown(void)
273 pmu_write(0x2b, 0); /* Kill the backlight, instantly. */
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);
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);
300 lcd_ispowered
= false;
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 LCD_CON
|= 0x180; /* use 16 bit bus width, big endian */
320 lcd_type
= 1; /* Similar to LDS176 - aka "type 7" */
321 LCD_CON
|= 0x100; /* use 16 bit bus width, little endian */
324 LCD_PHTIME
= 0x22; /* Set Phase Time reg (relevant for LCD IF speed) */
326 lcd_ispowered
= true;
329 /*** Update functions ***/
331 static inline void lcd_write_pixel(fb_data pixel
)
336 /* Update the display.
337 This must be called after all other LCD functions that change the display. */
338 void lcd_update(void) ICODE_ATTR
;
339 void lcd_update(void)
341 lcd_update_rect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
344 /* Line write helper function. */
345 extern void lcd_write_line(const fb_data
*addr
,
347 const unsigned int lcd_base_addr
);
349 /* Update a fraction of the display. */
350 void lcd_update_rect(int, int, int, int) ICODE_ATTR
;
351 void lcd_update_rect(int x
, int y
, int width
, int height
)
356 /* Both x and width need to be preprocessed due to asm optimizations */
357 x
= x
& ~1; /* ensure x is even */
358 width
= (width
+ 3) & ~3; /* ensure width is a multiple of 4 */
360 x0
= x
; /* start horiz */
361 y0
= y
; /* start vert */
362 x1
= (x
+ width
) - 1; /* max horiz */
363 y1
= (y
+ height
) - 1; /* max vert */
366 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS
, x0
);
367 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS
, x1
);
368 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS
, y0
);
369 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS
, y1
);
371 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET
, (x1
<< 8) | x0
);
372 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET
, (y1
<< 8) | y0
);
374 s5l_lcd_write_cmd(0);
375 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM
);
377 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET
);
378 s5l_lcd_write_data(x0
); /* Start column */
379 s5l_lcd_write_data(x1
); /* End column */
381 s5l_lcd_write_cmd(R_ROW_ADDR_SET
);
382 s5l_lcd_write_data(y0
); /* Start row */
383 s5l_lcd_write_data(y1
); /* End row */
385 s5l_lcd_write_cmd(R_MEMORY_WRITE
);
388 /* Copy display bitmap to hardware */
389 p
= &lcd_framebuffer
[y0
][x0
];
390 if (LCD_WIDTH
== width
) {
391 /* Write all lines at once */
392 lcd_write_line(p
, height
*LCD_WIDTH
, LCD_BASE
);
396 /* Write a single line */
397 lcd_write_line(p
, width
, LCD_BASE
);
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 const unsigned int lcd_baseadress
,
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 */
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
);
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
);
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]);
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);