Introduced LCD_FBHEIGHT in addition to the already existing LCD_FBWIDTH to ease frame...
[Rockbox.git] / firmware / drivers / lcd-ipodvideo.c
blob237162b66414b45a2a17d2f71c57874b9bd1683a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * LCD driver for iPod Video
12 * Based on code from the ipodlinux project - http://ipodlinux.org/
13 * Adapted for Rockbox in December 2005
15 * Original file: linux/arch/armnommu/mach-ipod/fb.c
17 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
19 * All files in this archive are subject to the GNU General Public License.
20 * See the file COPYING in the source tree root for full license agreement.
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
25 ****************************************************************************/
26 #include "config.h"
27 #include "cpu.h"
28 #include "lcd.h"
29 #include "kernel.h"
30 #include "system.h"
32 /*** hardware configuration ***/
34 void lcd_set_contrast(int val)
36 /* TODO: Implement lcd_set_contrast() */
37 (void)val;
40 void lcd_set_invert_display(bool yesno)
42 /* TODO: Implement lcd_set_invert_display() */
43 (void)yesno;
46 /* turn the display upside down (call lcd_update() afterwards) */
47 void lcd_set_flip(bool yesno)
49 /* TODO: Implement lcd_set_flip() */
50 (void)yesno;
53 /* LCD init */
54 void lcd_init_device(void)
56 /* iPodLinux doesn't appear have any LCD init code for the Video */
59 /*** update functions ***/
61 /* Performance function that works with an external buffer
62 note that by and bheight are in 4-pixel units! */
63 void lcd_blit(const fb_data* data, int x, int by, int width,
64 int bheight, int stride)
66 /* TODO: Implement lcd_blit() */
67 (void)data;
68 (void)x;
69 (void)by;
70 (void)width;
71 (void)bheight;
72 (void)stride;
75 static inline void lcd_bcm_write32(unsigned address, unsigned value)
77 /* write out destination address as two 16bit values */
78 outw(address, 0x30010000);
79 outw((address >> 16), 0x30010000);
81 /* wait for it to be write ready */
82 while ((inw(0x30030000) & 0x2) == 0);
84 /* write out the value low 16, high 16 */
85 outw(value, 0x30000000);
86 outw((value >> 16), 0x30000000);
89 static void lcd_bcm_setup_rect(unsigned cmd,
90 unsigned start_horiz,
91 unsigned start_vert,
92 unsigned max_horiz,
93 unsigned max_vert,
94 unsigned count)
96 lcd_bcm_write32(0x1F8, 0xFFFA0005);
97 lcd_bcm_write32(0xE0000, cmd);
98 lcd_bcm_write32(0xE0004, start_horiz);
99 lcd_bcm_write32(0xE0008, start_vert);
100 lcd_bcm_write32(0xE000C, max_horiz);
101 lcd_bcm_write32(0xE0010, max_vert);
102 lcd_bcm_write32(0xE0014, count);
103 lcd_bcm_write32(0xE0018, count);
104 lcd_bcm_write32(0xE001C, 0);
107 static unsigned lcd_bcm_read32(unsigned address) {
108 while ((inw(0x30020000) & 1) == 0);
110 /* write out destination address as two 16bit values */
111 outw(address, 0x30020000);
112 outw((address >> 16), 0x30020000);
114 /* wait for it to be read ready */
115 while ((inw(0x30030000) & 0x10) == 0);
117 /* read the value */
118 return inw(0x30000000) | inw(0x30000000) << 16;
121 static int finishup_needed = 0;
123 /* Update a fraction of the display. */
124 void lcd_update_rect(int x, int y, int width, int height) ICODE_ATTR;
125 void lcd_update_rect(int x, int y, int width, int height)
128 int endy = x + width;
129 /* Ensure x and width are both even - so we can read 32-bit aligned
130 data from lcd_framebuffer */
131 x &= ~1;
132 width &= ~1;
133 if (x + width < endy) {
134 width += 2;
138 if (finishup_needed) {
139 unsigned int data;
140 /* Bottom-half of original lcd_bcm_finishup() function */
141 do {
142 /* This function takes about 14ms to execute - so we yield() */
143 yield();
144 data = lcd_bcm_read32(0x1F8);
145 } while (data == 0xFFFA0005 || data == 0xFFFF);
148 lcd_bcm_read32(0x1FC);
151 int rect1, rect2, rect3, rect4;
152 int count = (width * height) << 1;
153 /* calculate the drawing region */
154 rect1 = x; /* start horiz */
155 rect2 = y; /* start vert */
156 rect3 = (x + width) - 1; /* max horiz */
157 rect4 = (y + height) - 1; /* max vert */
159 /* setup the drawing region */
160 lcd_bcm_setup_rect(0x34, rect1, rect2, rect3, rect4, count);
163 /* write out destination address as two 16bit values */
164 outw((0xE0020 & 0xffff), 0x30010000);
165 outw((0xE0020 >> 16), 0x30010000);
167 /* wait for it to be write ready */
168 while ((inw(0x30030000) & 0x2) == 0);
171 unsigned short *src = (unsigned short*)&lcd_framebuffer[y][x];
172 unsigned short *end = &src[LCD_WIDTH * height];
173 int line_rem = (LCD_WIDTH - width);
174 while (src < end) {
175 /* Duff's Device to unroll loop */
176 register int count = width ;
177 register int n=( count + 7 ) / 8;
178 switch( count % 8 ) {
179 case 0: do{ outw(*(src++), 0x30000000);
180 case 7: outw(*(src++), 0x30000000);
181 case 6: outw(*(src++), 0x30000000);
182 case 5: outw(*(src++), 0x30000000);
183 case 4: outw(*(src++), 0x30000000);
184 case 3: outw(*(src++), 0x30000000);
185 case 2: outw(*(src++), 0x30000000);
186 case 1: outw(*(src++), 0x30000000);
187 } while(--n>0);
189 src += line_rem;
193 /* Top-half of original lcd_bcm_finishup() function */
194 outw(0x31, 0x30030000);
196 lcd_bcm_read32(0x1FC);
198 finishup_needed = 1;
201 /* Update the display.
202 This must be called after all other LCD functions that change the display. */
203 void lcd_update(void)
205 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
208 #define CSUB_X 2
209 #define CSUB_Y 2
211 #define RYFAC (31*257)
212 #define GYFAC (31*257)
213 #define BYFAC (31*257)
214 #define RVFAC 11170 /* 31 * 257 * 1.402 */
215 #define GVFAC (-5690) /* 31 * 257 * -0.714136 */
216 #define GUFAC (-2742) /* 31 * 257 * -0.344136 */
217 #define BUFAC 14118 /* 31 * 257 * 1.772 */
219 #define ROUNDOFFS (127*257)
220 #define ROUNDOFFSG (63*257)
222 /* Performance function to blit a YUV bitmap directly to the LCD */
223 void lcd_yuv_blit(unsigned char * const src[3],
224 int src_x, int src_y, int stride,
225 int x, int y, int width, int height)
227 int ymax;
229 width = (width + 1) & ~1;
231 if (finishup_needed) {
232 unsigned int data;
233 /* Bottom-half of original lcd_bcm_finishup() function */
234 data = lcd_bcm_read32(0x1F8);
235 while (data == 0xFFFA0005 || data == 0xFFFF) {
236 /* This loop can wait for up to 14ms - so we yield() */
237 yield();
238 data = lcd_bcm_read32(0x1F8);
242 lcd_bcm_read32(0x1FC);
245 int rect1, rect2, rect3, rect4;
246 int count = (width * height) << 1;
247 /* calculate the drawing region */
248 rect1 = x; /* start horiz */
249 rect2 = y; /* start vert */
250 rect3 = (x + width) - 1; /* max horiz */
251 rect4 = (y + height) - 1; /* max vert */
253 /* setup the drawing region */
254 lcd_bcm_setup_rect(0x34, rect1, rect2, rect3, rect4, count);
257 /* write out destination address as two 16bit values */
258 outw((0xE0020 & 0xffff), 0x30010000);
259 outw((0xE0020 >> 16), 0x30010000);
261 /* wait for it to be write ready */
262 while ((inw(0x30030000) & 0x2) == 0);
264 ymax = y + height - 1 ;
266 const int stride_div_csub_x = stride/CSUB_X;
268 for (; y <= ymax ; y++)
270 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
271 const unsigned char *ysrc = src[0] + stride * src_y + src_x;
273 const int uvoffset = stride_div_csub_x * (src_y/CSUB_Y) +
274 (src_x/CSUB_X);
276 const unsigned char *usrc = src[1] + uvoffset;
277 const unsigned char *vsrc = src[2] + uvoffset;
278 const unsigned char *row_end = ysrc + width;
280 int y, u, v;
281 int red1, green1, blue1;
282 int red2, green2, blue2;
283 unsigned rbits, gbits, bbits;
285 int rc, gc, bc;
289 u = *usrc++ - 128;
290 v = *vsrc++ - 128;
291 rc = RVFAC * v + ROUNDOFFS;
292 gc = GVFAC * v + GUFAC * u + ROUNDOFFSG;
293 bc = BUFAC * u + ROUNDOFFS;
295 /* Pixel 1 */
296 y = *ysrc++;
298 red1 = RYFAC * y + rc;
299 green1 = GYFAC * y + gc;
300 blue1 = BYFAC * y + bc;
302 /* Pixel 2 */
303 y = *ysrc++;
304 red2 = RYFAC * y + rc;
305 green2 = GYFAC * y + gc;
306 blue2 = BYFAC * y + bc;
308 /* Since out of bounds errors are relatively rare, we check two
309 pixels at once to see if any components are out of bounds, and
310 then fix whichever is broken. This works due to high values and
311 negative values both becoming larger than the cutoff when
312 casted to unsigned. And ORing them together checks all of them
313 simultaneously. */
314 if (((unsigned)(red1 | green1 | blue1 |
315 red2 | green2 | blue2)) > (RYFAC*255+ROUNDOFFS)) {
316 if (((unsigned)(red1 | green1 | blue1)) >
317 (RYFAC*255+ROUNDOFFS)) {
318 if ((unsigned)red1 > (RYFAC*255+ROUNDOFFS))
320 if (red1 < 0)
321 red1 = 0;
322 else
323 red1 = (RYFAC*255+ROUNDOFFS);
325 if ((unsigned)green1 > (GYFAC*255+ROUNDOFFSG))
327 if (green1 < 0)
328 green1 = 0;
329 else
330 green1 = (GYFAC*255+ROUNDOFFSG);
332 if ((unsigned)blue1 > (BYFAC*255+ROUNDOFFS))
334 if (blue1 < 0)
335 blue1 = 0;
336 else
337 blue1 = (BYFAC*255+ROUNDOFFS);
341 if (((unsigned)(red2 | green2 | blue2)) >
342 (RYFAC*255+ROUNDOFFS)) {
343 if ((unsigned)red2 > (RYFAC*255+ROUNDOFFS))
345 if (red2 < 0)
346 red2 = 0;
347 else
348 red2 = (RYFAC*255+ROUNDOFFS);
350 if ((unsigned)green2 > (GYFAC*255+ROUNDOFFSG))
352 if (green2 < 0)
353 green2 = 0;
354 else
355 green2 = (GYFAC*255+ROUNDOFFSG);
357 if ((unsigned)blue2 > (BYFAC*255+ROUNDOFFS))
359 if (blue2 < 0)
360 blue2 = 0;
361 else
362 blue2 = (BYFAC*255+ROUNDOFFS);
367 rbits = red1 >> 16 ;
368 gbits = green1 >> 15 ;
369 bbits = blue1 >> 16 ;
371 outw((rbits << 11) | (gbits << 5) | bbits, 0x30000000);
373 rbits = red2 >> 16 ;
374 gbits = green2 >> 15 ;
375 bbits = blue2 >> 16 ;
376 outw((rbits << 11) | (gbits << 5) | bbits, 0x30000000);
378 while (ysrc < row_end);
380 src_y++;
383 /* Top-half of original lcd_bcm_finishup() function */
384 outw(0x31, 0x30030000);
386 lcd_bcm_read32(0x1FC);
388 finishup_needed = 1;