ipod nano 2g: make functions/variables static where possible, add missing #includes
[kugel-rb.git] / firmware / target / arm / s5l8700 / ipodnano2g / lcd-nano2g.c
blob0600c77b622a9db67c0dade33fb4987a95cbfd8e
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"
31 /* The Nano 2G has two different LCD types. What we call "type 0"
32 appears to be similar to the ILI9320 and "type 1" is similar to the
33 LDS176.
36 /* LCD type 0 register defines */
38 #define R_ENTRY_MODE 0x03
39 #define R_DISPLAY_CONTROL_1 0x07
40 #define R_POWER_CONTROL_1 0x10
41 #define R_POWER_CONTROL_2 0x12
42 #define R_POWER_CONTROL_3 0x13
43 #define R_HORIZ_GRAM_ADDR_SET 0x20
44 #define R_VERT_GRAM_ADDR_SET 0x21
45 #define R_WRITE_DATA_TO_GRAM 0x22
46 #define R_HORIZ_ADDR_START_POS 0x50
47 #define R_HORIZ_ADDR_END_POS 0x51
48 #define R_VERT_ADDR_START_POS 0x52
49 #define R_VERT_ADDR_END_POS 0x53
52 /* LCD type 1 register defines */
54 #define R_SLEEP_IN 0x10
55 #define R_DISPLAY_OFF 0x28
56 #define R_COLUMN_ADDR_SET 0x2a
57 #define R_ROW_ADDR_SET 0x2b
58 #define R_MEMORY_WRITE 0x2c
61 /** globals **/
63 int lcd_type; /* also needed in debug-s5l8700.c */
64 static int xoffset; /* needed for flip */
66 /** hardware access functions */
68 static inline void s5l_lcd_write_cmd_data(int cmd, int data)
70 while (LCD_STATUS & 0x10);
71 LCD_WCMD = cmd >> 8;
72 while (LCD_STATUS & 0x10);
73 LCD_WCMD = cmd & 0xff;
75 while (LCD_STATUS & 0x10);
76 LCD_WDATA = data >> 8;
77 while (LCD_STATUS & 0x10);
78 LCD_WDATA = data & 0xff;
81 static inline void s5l_lcd_write_cmd(unsigned short cmd)
83 while (LCD_STATUS & 0x10);
84 LCD_WCMD = cmd;
87 static inline void s5l_lcd_write_data(int data)
89 while (LCD_STATUS & 0x10);
90 LCD_WDATA = data >> 8;
91 while (LCD_STATUS & 0x10);
92 LCD_WDATA = data & 0xff;
95 /*** hardware configuration ***/
97 int lcd_default_contrast(void)
99 return 0x1f;
102 void lcd_set_contrast(int val)
104 (void)val;
107 void lcd_set_invert_display(bool yesno)
109 (void)yesno;
112 /* turn the display upside down (call lcd_update() afterwards) */
113 void lcd_set_flip(bool yesno)
115 /* TODO: flip mode isn't working. The commands in the else part of
116 this function are how the original firmware inits the LCD */
118 if (yesno)
120 xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 128 we have */
122 else
124 xoffset = 0;
128 void lcd_shutdown(void)
130 pmu_write(0x2b, 0); /* Kill the backlight, instantly. */
131 pmu_write(0x29, 0);
133 if (lcd_type == 0)
135 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x232);
136 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3, 0x1137);
137 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x201);
138 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3, 0x137);
139 s5l_lcd_write_cmd_data(R_DISPLAY_CONTROL_1, 0x200);
140 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1, 0x680);
141 s5l_lcd_write_cmd_data(R_POWER_CONTROL_2, 0x160);
142 s5l_lcd_write_cmd_data(R_POWER_CONTROL_3, 0x127);
143 s5l_lcd_write_cmd_data(R_POWER_CONTROL_1, 0x600);
145 else
147 s5l_lcd_write_cmd(R_DISPLAY_OFF);
148 s5l_lcd_write_data(0);
149 s5l_lcd_write_data(0);
150 s5l_lcd_write_cmd(R_SLEEP_IN);
151 s5l_lcd_write_data(0);
152 s5l_lcd_write_data(0);
156 /* LCD init */
157 void lcd_init_device(void)
159 /* Detect lcd type */
161 PCON13 &= ~0xf; /* Set pin 0 to input */
162 PCON14 &= ~0xf0; /* Set pin 1 to input */
164 if (((PDAT13 & 1) == 0) && ((PDAT14 & 2) == 2))
165 lcd_type = 0; /* Similar to ILI9320 - aka "type 2" */
166 else
167 lcd_type = 1; /* Similar to LDS176 - aka "type 7" */
169 /* Now init according to lcd type */
170 if (lcd_type == 0) {
171 /* TODO */
173 /* Entry Mode: AM=0, I/D1=1, I/D0=1, ORG=0, HWM=1, BGR=1 */
174 s5l_lcd_write_cmd_data(R_ENTRY_MODE, 0x1230);
175 } else {
176 /* TODO */
181 /*** Update functions ***/
183 static inline void lcd_write_pixel(fb_data pixel)
185 while (LCD_STATUS & 0x10);
186 LCD_WDATA = (pixel & 0xff00) >> 8;
187 while (LCD_STATUS & 0x10);
188 LCD_WDATA = pixel & 0xff;
191 /* Update the display.
192 This must be called after all other LCD functions that change the display. */
193 void lcd_update(void) ICODE_ATTR;
194 void lcd_update(void)
196 int x,y;
197 fb_data* p = &lcd_framebuffer[0][0];
199 if (lcd_type==0) {
200 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, 0);
201 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, LCD_WIDTH-1);
202 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, 0);
203 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, LCD_HEIGHT-1);
205 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, 0);
206 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, 0);
208 s5l_lcd_write_cmd(0);
209 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
210 } else {
211 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
212 s5l_lcd_write_data(0); /* Start column */
213 s5l_lcd_write_data(LCD_WIDTH-1); /* End column */
215 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
216 s5l_lcd_write_data(0); /* Start row */
217 s5l_lcd_write_data(LCD_HEIGHT-1); /* End row */
219 s5l_lcd_write_cmd(R_MEMORY_WRITE);
223 /* Copy display bitmap to hardware */
224 for (y = 0; y < LCD_HEIGHT; y++) {
225 for (x = 0; x < LCD_WIDTH; x++) {
226 lcd_write_pixel(*(p++));
231 /* Update a fraction of the display. */
232 void lcd_update_rect(int, int, int, int) ICODE_ATTR;
233 void lcd_update_rect(int x, int y, int width, int height)
235 int xx,yy;
236 int y0, x0, y1, x1;
237 fb_data* p;
239 x0 = x; /* start horiz */
240 y0 = y; /* start vert */
241 x1 = (x + width) - 1; /* max horiz */
242 y1 = (y + height) - 1; /* max vert */
244 if (lcd_type==0) {
245 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
246 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
247 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
248 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
250 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
251 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
253 s5l_lcd_write_cmd(0);
254 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
255 } else {
256 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
257 s5l_lcd_write_data(x0); /* Start column */
258 s5l_lcd_write_data(x1); /* End column */
260 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
261 s5l_lcd_write_data(y0); /* Start row */
262 s5l_lcd_write_data(y1); /* End row */
264 s5l_lcd_write_cmd(R_MEMORY_WRITE);
268 /* Copy display bitmap to hardware */
269 p = &lcd_framebuffer[y0][x0];
270 yy = height;
271 for (yy = y0; yy <= y1; yy++) {
272 for (xx = x0; xx <= x1; xx++) {
273 lcd_write_pixel(*(p++));
275 p += LCD_WIDTH - width;
279 /*** update functions ***/
281 #define CSUB_X 2
282 #define CSUB_Y 2
284 /* YUV- > RGB565 conversion
285 * |R| |1.000000 -0.000001 1.402000| |Y'|
286 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
287 * |B| |1.000000 1.772000 0.000000| |Pr|
288 * Scaled, normalized, rounded and tweaked to yield RGB 565:
289 * |R| |74 0 101| |Y' - 16| >> 9
290 * |G| = |74 -24 -51| |Cb - 128| >> 8
291 * |B| |74 128 0| |Cr - 128| >> 9
294 #define RGBYFAC 74 /* 1.0 */
295 #define RVFAC 101 /* 1.402 */
296 #define GVFAC (-51) /* -0.714136 */
297 #define GUFAC (-24) /* -0.334136 */
298 #define BUFAC 128 /* 1.772 */
300 /* ROUNDOFFS contain constant for correct round-offs as well as
301 constant parts of the conversion matrix (e.g. (Y'-16)*RGBYFAC
302 -> constant part = -16*RGBYFAC). Through extraction of these
303 constant parts we save at leat 4 substractions in the conversion
304 loop */
305 #define ROUNDOFFSR (256 - 16*RGBYFAC - 128*RVFAC)
306 #define ROUNDOFFSG (128 - 16*RGBYFAC - 128*GVFAC - 128*GUFAC)
307 #define ROUNDOFFSB (256 - 16*RGBYFAC - 128*BUFAC)
309 #define MAX_5BIT 0x1f
310 #define MAX_6BIT 0x3f
312 /* Performance function to blit a YUV bitmap directly to the LCD */
313 void lcd_blit_yuv(unsigned char * const src[3],
314 int src_x, int src_y, int stride,
315 int x, int y, int width, int height)
317 int h;
318 int y0, x0, y1, x1;
320 width = (width + 1) & ~1;
322 x0 = x; /* start horiz */
323 y0 = y; /* start vert */
324 x1 = (x + width) - 1; /* max horiz */
325 y1 = (y + height) - 1; /* max vert */
327 if (lcd_type==0) {
328 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_START_POS, x0);
329 s5l_lcd_write_cmd_data(R_HORIZ_ADDR_END_POS, x1);
330 s5l_lcd_write_cmd_data(R_VERT_ADDR_START_POS, y0);
331 s5l_lcd_write_cmd_data(R_VERT_ADDR_END_POS, y1);
333 s5l_lcd_write_cmd_data(R_HORIZ_GRAM_ADDR_SET, (x1 << 8) | x0);
334 s5l_lcd_write_cmd_data(R_VERT_GRAM_ADDR_SET, (y1 << 8) | y0);
336 s5l_lcd_write_cmd(0);
337 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
338 } else {
339 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
340 s5l_lcd_write_data(x0); /* Start column */
341 s5l_lcd_write_data(x1); /* End column */
343 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
344 s5l_lcd_write_data(y0); /* Start row */
345 s5l_lcd_write_data(y1); /* End row */
347 s5l_lcd_write_cmd(R_MEMORY_WRITE);
350 const int stride_div_csub_x = stride/CSUB_X;
352 h = height;
353 while (h > 0) {
354 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
355 const unsigned char *ysrc = src[0] + stride * src_y + src_x;
357 const int uvoffset = stride_div_csub_x * (src_y/CSUB_Y) +
358 (src_x/CSUB_X);
360 const unsigned char *usrc = src[1] + uvoffset;
361 const unsigned char *vsrc = src[2] + uvoffset;
362 const unsigned char *row_end = ysrc + width;
364 int yp, up, vp;
365 int red1, green1, blue1;
366 int red2, green2, blue2;
368 int rc, gc, bc;
372 up = *usrc++;
373 vp = *vsrc++;
374 rc = RVFAC * vp + ROUNDOFFSR;
375 gc = GVFAC * vp + GUFAC * up + ROUNDOFFSG;
376 bc = BUFAC * up + ROUNDOFFSB;
378 /* Pixel 1 -> RGB565 */
379 yp = *ysrc++ * RGBYFAC;
380 red1 = (yp + rc) >> 9;
381 green1 = (yp + gc) >> 8;
382 blue1 = (yp + bc) >> 9;
384 /* Pixel 2 -> RGB565 */
385 yp = *ysrc++ * RGBYFAC;
386 red2 = (yp + rc) >> 9;
387 green2 = (yp + gc) >> 8;
388 blue2 = (yp + bc) >> 9;
390 /* Since out of bounds errors are relatively rare, we check two
391 pixels at once to see if any components are out of bounds, and
392 then fix whichever is broken. This works due to high values and
393 negative values both being !=0 when bitmasking them.
394 We first check for red and blue components (5bit range). */
395 if ((red1 | blue1 | red2 | blue2) & ~MAX_5BIT)
397 if (red1 & ~MAX_5BIT)
398 red1 = (red1 >> 31) ? 0 : MAX_5BIT;
399 if (blue1 & ~MAX_5BIT)
400 blue1 = (blue1 >> 31) ? 0 : MAX_5BIT;
401 if (red2 & ~MAX_5BIT)
402 red2 = (red2 >> 31) ? 0 : MAX_5BIT;
403 if (blue2 & ~MAX_5BIT)
404 blue2 = (blue2 >> 31) ? 0 : MAX_5BIT;
406 /* We second check for green component (6bit range) */
407 if ((green1 | green2) & ~MAX_6BIT)
409 if (green1 & ~MAX_6BIT)
410 green1 = (green1 >> 31) ? 0 : MAX_6BIT;
411 if (green2 & ~MAX_6BIT)
412 green2 = (green2 >> 31) ? 0 : MAX_6BIT;
415 /* output 2 pixels */
416 lcd_write_pixel((red1 << 11) | (green1 << 5) | blue1);
417 lcd_write_pixel((red2 << 11) | (green2 << 5) | blue2);
419 while (ysrc < row_end);
421 src_y++;
422 h--;