1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
32 /*** hardware configuration ***/
34 void lcd_set_contrast(int val
)
36 /* TODO: Implement lcd_set_contrast() */
40 void lcd_set_invert_display(bool yesno
)
42 /* TODO: Implement lcd_set_invert_display() */
46 /* turn the display upside down (call lcd_update() afterwards) */
47 void lcd_set_flip(bool yesno
)
49 /* TODO: Implement lcd_set_flip() */
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() */
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
,
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);
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 */
133 if (x
+ width
< endy
) {
138 if (finishup_needed
) {
140 /* Bottom-half of original lcd_bcm_finishup() function */
142 /* This function takes about 14ms to execute - so we 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
);
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);
193 /* Top-half of original lcd_bcm_finishup() function */
194 outw(0x31, 0x30030000);
196 lcd_bcm_read32(0x1FC);
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
);
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
)
229 width
= (width
+ 1) & ~1;
231 if (finishup_needed
) {
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() */
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
) +
276 const unsigned char *usrc
= src
[1] + uvoffset
;
277 const unsigned char *vsrc
= src
[2] + uvoffset
;
278 const unsigned char *row_end
= ysrc
+ width
;
281 int red1
, green1
, blue1
;
282 int red2
, green2
, blue2
;
283 unsigned rbits
, gbits
, bbits
;
291 rc
= RVFAC
* v
+ ROUNDOFFS
;
292 gc
= GVFAC
* v
+ GUFAC
* u
+ ROUNDOFFSG
;
293 bc
= BUFAC
* u
+ ROUNDOFFS
;
298 red1
= RYFAC
* y
+ rc
;
299 green1
= GYFAC
* y
+ gc
;
300 blue1
= BYFAC
* y
+ bc
;
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
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
))
323 red1
= (RYFAC
*255+ROUNDOFFS
);
325 if ((unsigned)green1
> (GYFAC
*255+ROUNDOFFSG
))
330 green1
= (GYFAC
*255+ROUNDOFFSG
);
332 if ((unsigned)blue1
> (BYFAC
*255+ROUNDOFFS
))
337 blue1
= (BYFAC
*255+ROUNDOFFS
);
341 if (((unsigned)(red2
| green2
| blue2
)) >
342 (RYFAC
*255+ROUNDOFFS
)) {
343 if ((unsigned)red2
> (RYFAC
*255+ROUNDOFFS
))
348 red2
= (RYFAC
*255+ROUNDOFFS
);
350 if ((unsigned)green2
> (GYFAC
*255+ROUNDOFFSG
))
355 green2
= (GYFAC
*255+ROUNDOFFSG
);
357 if ((unsigned)blue2
> (BYFAC
*255+ROUNDOFFS
))
362 blue2
= (BYFAC
*255+ROUNDOFFS
);
368 gbits
= green1
>> 15 ;
369 bbits
= blue1
>> 16 ;
371 outw((rbits
<< 11) | (gbits
<< 5) | bbits
, 0x30000000);
374 gbits
= green2
>> 15 ;
375 bbits
= blue2
>> 16 ;
376 outw((rbits
<< 11) | (gbits
<< 5) | bbits
, 0x30000000);
378 while (ysrc
< row_end
);
383 /* Top-half of original lcd_bcm_finishup() function */
384 outw(0x31, 0x30030000);
386 lcd_bcm_read32(0x1FC);