vuprintf does not belong in stdio.h, causes problems with other versions of stdio.h
[kugel-rb.git] / firmware / target / arm / ipod / lcd-color_nano.c
blob7d004cb0f224e456024ba9072392a9cc55f1cf4f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Rockbox driver for iPod LCDs
12 * Based on code from the ipodlinux project - http://ipodlinux.org/
13 * Adapted for Rockbox in November 2005
15 * Original file: linux/arch/armnommu/mach-ipod/fb.c
17 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
25 * KIND, either express or implied.
27 ****************************************************************************/
28 #include "config.h"
29 #include "cpu.h"
30 #include "lcd.h"
31 #include "kernel.h"
32 #include "system.h"
33 #include "hwcompat.h"
35 /* LCD command codes for HD66789R */
36 #define LCD_CNTL_RAM_ADDR_SET 0x21
37 #define LCD_CNTL_WRITE_TO_GRAM 0x22
38 #define LCD_CNTL_HORIZ_RAM_ADDR_POS 0x44
39 #define LCD_CNTL_VERT_RAM_ADDR_POS 0x45
41 /*** globals ***/
42 int lcd_type = 1; /* 0 = "old" Color/Photo, 1 = "new" Color & Nano */
44 static inline void lcd_wait_write(void)
46 while (LCD2_PORT & LCD2_BUSY_MASK);
49 static void lcd_cmd_data(unsigned cmd, unsigned data)
51 if (lcd_type == 0) { /* 16 bit transfers */
52 lcd_wait_write();
53 LCD2_PORT = LCD2_CMD_MASK | cmd;
54 lcd_wait_write();
55 LCD2_PORT = LCD2_CMD_MASK | data;
56 } else {
57 lcd_wait_write();
58 LCD2_PORT = LCD2_CMD_MASK;
59 LCD2_PORT = LCD2_CMD_MASK | cmd;
60 lcd_wait_write();
61 LCD2_PORT = LCD2_DATA_MASK | (data >> 8);
62 LCD2_PORT = LCD2_DATA_MASK | (data & 0xff);
66 /*** hardware configuration ***/
68 void lcd_set_contrast(int val)
70 /* TODO: Implement lcd_set_contrast() */
71 (void)val;
74 void lcd_set_invert_display(bool yesno)
76 /* TODO: Implement lcd_set_invert_display() */
77 (void)yesno;
80 /* turn the display upside down (call lcd_update() afterwards) */
81 void lcd_set_flip(bool yesno)
83 /* TODO: Implement lcd_set_flip() */
84 (void)yesno;
87 /* LCD init */
88 void lcd_init_device(void)
90 #if CONFIG_LCD == LCD_IPODCOLOR
91 if (IPOD_HW_REVISION == 0x60000) {
92 lcd_type = 0;
93 } else {
94 int gpio_a01, gpio_a04;
96 /* A01 */
97 gpio_a01 = (GPIOA_INPUT_VAL & 0x2) >> 1;
98 /* A04 */
99 gpio_a04 = (GPIOA_INPUT_VAL & 0x10) >> 4;
101 if (((gpio_a01 << 1) | gpio_a04) == 0 || ((gpio_a01 << 1) | gpio_a04) == 2) {
102 lcd_type = 0;
103 } else {
104 lcd_type = 1;
107 if (lcd_type == 0) {
108 lcd_cmd_data(0xef, 0x0);
109 lcd_cmd_data(0x1, 0x0);
110 lcd_cmd_data(0x80, 0x1);
111 lcd_cmd_data(0x10, 0xc);
112 lcd_cmd_data(0x18, 0x6);
113 lcd_cmd_data(0x7e, 0x4);
114 lcd_cmd_data(0x7e, 0x5);
115 lcd_cmd_data(0x7f, 0x1);
118 #elif CONFIG_LCD == LCD_IPODNANO
119 /* iPodLinux doesn't appear have any LCD init code for the Nano */
120 #endif
123 /*** update functions ***/
125 #define CSUB_X 2
126 #define CSUB_Y 2
128 /* YUV- > RGB565 conversion
129 * |R| |1.000000 -0.000001 1.402000| |Y'|
130 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
131 * |B| |1.000000 1.772000 0.000000| |Pr|
132 * Scaled, normalized, rounded and tweaked to yield RGB 565:
133 * |R| |74 0 101| |Y' - 16| >> 9
134 * |G| = |74 -24 -51| |Cb - 128| >> 8
135 * |B| |74 128 0| |Cr - 128| >> 9
138 #define RGBYFAC 74 /* 1.0 */
139 #define RVFAC 101 /* 1.402 */
140 #define GVFAC (-51) /* -0.714136 */
141 #define GUFAC (-24) /* -0.334136 */
142 #define BUFAC 128 /* 1.772 */
144 /* ROUNDOFFS contain constant for correct round-offs as well as
145 constant parts of the conversion matrix (e.g. (Y'-16)*RGBYFAC
146 -> constant part = -16*RGBYFAC). Through extraction of these
147 constant parts we save at leat 4 substractions in the conversion
148 loop */
149 #define ROUNDOFFSR (256 - 16*RGBYFAC - 128*RVFAC)
150 #define ROUNDOFFSG (128 - 16*RGBYFAC - 128*GVFAC - 128*GUFAC)
151 #define ROUNDOFFSB (256 - 16*RGBYFAC - 128*BUFAC)
153 #define MAX_5BIT 0x1f
154 #define MAX_6BIT 0x3f
156 /* Performance function to blit a YUV bitmap directly to the LCD */
157 void lcd_blit_yuv(unsigned char * const src[3],
158 int src_x, int src_y, int stride,
159 int x, int y, int width, int height)
161 int h;
162 int y0, x0, y1, x1;
164 width = (width + 1) & ~1;
166 /* calculate the drawing region */
167 #if CONFIG_LCD == LCD_IPODNANO
168 y0 = x; /* start horiz */
169 x0 = y; /* start vert */
170 y1 = (x + width) - 1; /* max horiz */
171 x1 = (y + height) - 1; /* max vert */
172 #elif CONFIG_LCD == LCD_IPODCOLOR
173 y0 = y; /* start vert */
174 x0 = (LCD_WIDTH - 1) - x; /* start horiz */
175 y1 = (y + height) - 1; /* end vert */
176 x1 = (x0 - width) + 1; /* end horiz */
177 #endif
179 /* setup the drawing region */
180 if (lcd_type == 0) {
181 lcd_cmd_data(0x12, y0); /* start vert */
182 lcd_cmd_data(0x13, x0); /* start horiz */
183 lcd_cmd_data(0x15, y1); /* end vert */
184 lcd_cmd_data(0x16, x1); /* end horiz */
185 } else {
186 /* swap max horiz < start horiz */
187 if (y1 < y0) {
188 int t;
189 t = y0;
190 y0 = y1;
191 y1 = t;
194 /* swap max vert < start vert */
195 if (x1 < x0) {
196 int t;
197 t = x0;
198 x0 = x1;
199 x1 = t;
202 /* max horiz << 8 | start horiz */
203 lcd_cmd_data(LCD_CNTL_HORIZ_RAM_ADDR_POS, (y1 << 8) | y0);
204 /* max vert << 8 | start vert */
205 lcd_cmd_data(LCD_CNTL_VERT_RAM_ADDR_POS, (x1 << 8) | x0);
207 /* start vert = max vert */
208 #if CONFIG_LCD == LCD_IPODCOLOR
209 x0 = x1;
210 #endif
212 /* position cursor (set AD0-AD15) */
213 /* start vert << 8 | start horiz */
214 lcd_cmd_data(LCD_CNTL_RAM_ADDR_SET, ((x0 << 8) | y0));
216 /* start drawing */
217 lcd_wait_write();
218 LCD2_PORT = LCD2_CMD_MASK;
219 LCD2_PORT = (LCD2_CMD_MASK|LCD_CNTL_WRITE_TO_GRAM);
222 const int stride_div_csub_x = stride/CSUB_X;
224 h=0;
225 while (1) {
226 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
227 const unsigned char *ysrc = src[0] + stride * src_y + src_x;
229 const int uvoffset = stride_div_csub_x * (src_y/CSUB_Y) +
230 (src_x/CSUB_X);
232 const unsigned char *usrc = src[1] + uvoffset;
233 const unsigned char *vsrc = src[2] + uvoffset;
234 const unsigned char *row_end = ysrc + width;
236 int yp, up, vp;
237 int red1, green1, blue1;
238 int red2, green2, blue2;
240 int rc, gc, bc;
241 int pixels_to_write;
242 fb_data pixel1,pixel2;
244 if (h==0) {
245 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
246 LCD2_BLOCK_CONFIG = 0;
248 if (height == 0) break;
250 pixels_to_write = (width * height) * 2;
251 h = height;
253 /* calculate how much we can do in one go */
254 if (pixels_to_write > 0x10000) {
255 h = (0x10000/2) / width;
256 pixels_to_write = (width * h) * 2;
259 height -= h;
260 LCD2_BLOCK_CTRL = 0x10000080;
261 LCD2_BLOCK_CONFIG = 0xc0010000 | (pixels_to_write - 1);
262 LCD2_BLOCK_CTRL = 0x34000000;
267 up = *usrc++;
268 vp = *vsrc++;
269 rc = RVFAC * vp + ROUNDOFFSR;
270 gc = GVFAC * vp + GUFAC * up + ROUNDOFFSG;
271 bc = BUFAC * up + ROUNDOFFSB;
273 /* Pixel 1 -> RGB565 */
274 yp = *ysrc++ * RGBYFAC;
275 red1 = (yp + rc) >> 9;
276 green1 = (yp + gc) >> 8;
277 blue1 = (yp + bc) >> 9;
279 /* Pixel 2 -> RGB565 */
280 yp = *ysrc++ * RGBYFAC;
281 red2 = (yp + rc) >> 9;
282 green2 = (yp + gc) >> 8;
283 blue2 = (yp + bc) >> 9;
285 /* Since out of bounds errors are relatively rare, we check two
286 pixels at once to see if any components are out of bounds, and
287 then fix whichever is broken. This works due to high values and
288 negative values both being !=0 when bitmasking them.
289 We first check for red and blue components (5bit range). */
290 if ((red1 | blue1 | red2 | blue2) & ~MAX_5BIT)
292 if (red1 & ~MAX_5BIT)
293 red1 = (red1 >> 31) ? 0 : MAX_5BIT;
294 if (blue1 & ~MAX_5BIT)
295 blue1 = (blue1 >> 31) ? 0 : MAX_5BIT;
296 if (red2 & ~MAX_5BIT)
297 red2 = (red2 >> 31) ? 0 : MAX_5BIT;
298 if (blue2 & ~MAX_5BIT)
299 blue2 = (blue2 >> 31) ? 0 : MAX_5BIT;
301 /* We second check for green component (6bit range) */
302 if ((green1 | green2) & ~MAX_6BIT)
304 if (green1 & ~MAX_6BIT)
305 green1 = (green1 >> 31) ? 0 : MAX_6BIT;
306 if (green2 & ~MAX_6BIT)
307 green2 = (green2 >> 31) ? 0 : MAX_6BIT;
310 pixel1 = swap16((red1 << 11) | (green1 << 5) | blue1);
312 pixel2 = swap16((red2 << 11) | (green2 << 5) | blue2);
314 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_TXOK));
316 /* output 2 pixels */
317 LCD2_BLOCK_DATA = (pixel2 << 16) | pixel1;
319 while (ysrc < row_end);
321 src_y++;
322 h--;
325 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
326 LCD2_BLOCK_CONFIG = 0;
330 /* Update a fraction of the display. */
331 void lcd_update_rect(int x, int y, int width, int height)
333 int y0, x0, y1, x1;
334 int newx,newwidth;
335 unsigned long *addr;
337 /* Ensure x and width are both even - so we can read 32-bit aligned
338 data from lcd_framebuffer */
339 newx=x&~1;
340 newwidth=width&~1;
341 if (newx+newwidth < x+width) { newwidth+=2; }
342 x=newx; width=newwidth;
344 /* calculate the drawing region */
345 #if CONFIG_LCD == LCD_IPODNANO
346 y0 = x; /* start horiz */
347 x0 = y; /* start vert */
348 y1 = (x + width) - 1; /* max horiz */
349 x1 = (y + height) - 1; /* max vert */
350 #elif CONFIG_LCD == LCD_IPODCOLOR
351 y0 = y; /* start vert */
352 x0 = (LCD_WIDTH - 1) - x; /* start horiz */
353 y1 = (y + height) - 1; /* end vert */
354 x1 = (x0 - width) + 1; /* end horiz */
355 #endif
356 /* setup the drawing region */
357 if (lcd_type == 0) {
358 lcd_cmd_data(0x12, y0); /* start vert */
359 lcd_cmd_data(0x13, x0); /* start horiz */
360 lcd_cmd_data(0x15, y1); /* end vert */
361 lcd_cmd_data(0x16, x1); /* end horiz */
362 } else {
363 /* swap max horiz < start horiz */
364 if (y1 < y0) {
365 int t;
366 t = y0;
367 y0 = y1;
368 y1 = t;
371 /* swap max vert < start vert */
372 if (x1 < x0) {
373 int t;
374 t = x0;
375 x0 = x1;
376 x1 = t;
379 /* max horiz << 8 | start horiz */
380 lcd_cmd_data(LCD_CNTL_HORIZ_RAM_ADDR_POS, (y1 << 8) | y0);
381 /* max vert << 8 | start vert */
382 lcd_cmd_data(LCD_CNTL_VERT_RAM_ADDR_POS, (x1 << 8) | x0);
384 /* start vert = max vert */
385 #if CONFIG_LCD == LCD_IPODCOLOR
386 x0 = x1;
387 #endif
389 /* position cursor (set AD0-AD15) */
390 /* start vert << 8 | start horiz */
391 lcd_cmd_data(LCD_CNTL_RAM_ADDR_SET, ((x0 << 8) | y0));
393 /* start drawing */
394 lcd_wait_write();
395 LCD2_PORT = LCD2_CMD_MASK;
396 LCD2_PORT = (LCD2_CMD_MASK|LCD_CNTL_WRITE_TO_GRAM);
399 addr = (unsigned long*)&lcd_framebuffer[y][x];
401 while (height > 0) {
402 int c, r;
403 int h, pixels_to_write;
405 pixels_to_write = (width * height) * 2;
406 h = height;
408 /* calculate how much we can do in one go */
409 if (pixels_to_write > 0x10000) {
410 h = (0x10000/2) / width;
411 pixels_to_write = (width * h) * 2;
414 LCD2_BLOCK_CTRL = 0x10000080;
415 LCD2_BLOCK_CONFIG = 0xc0010000 | (pixels_to_write - 1);
416 LCD2_BLOCK_CTRL = 0x34000000;
418 /* for each row */
419 for (r = 0; r < h; r++) {
420 /* for each column */
421 for (c = 0; c < width; c += 2) {
422 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_TXOK));
424 /* output 2 pixels */
425 LCD2_BLOCK_DATA = *addr++;
427 addr += (LCD_WIDTH - width)/2;
430 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
431 LCD2_BLOCK_CONFIG = 0;
433 height -= h;
437 /* Update the display.
438 This must be called after all other LCD functions that change the display. */
439 void lcd_update(void)
441 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);