Minor speed up (+3%) of iPod Video screen updates.
[kugel-rb.git] / firmware / target / arm / ipod / video / lcd-as-video.S
blob21a04a652961b9eb4d0b4bffb0b3be00885538a1
1 /***************************************************************************
2  *             __________               __   ___.
3  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
4  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
5  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
6  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
7  *                     \/            \/     \/    \/            \/
8  * $Id$
9  *
10  * Copyright (C) 2007 by Andree Buschmann
11  *
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.
16  *
17  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18  * KIND, either express or implied.
19  *
20  ****************************************************************************/
22     .section .icode, "ax", %progbits
24 /****************************************************************************
25  * void lcd_write_data(const fb_data *addr, 
26  *                     int pixelcount);
27  * 
28  * Writes pixelcount pixels from src-pointer (lcd_framebuffer) to BCM dataport.
29  * Use the sequence ld 2, wr 2, ld 2, wr 2 with alternating registers for best
30  * performance.
31  */
32     .align  2
33     .global lcd_write_data
34     .type   lcd_write_data, %function
35                                       /* r0 = addr, must be aligned */
36 lcd_write_data:                       /* r1 = pixel count, must be even */
37     stmfd   sp!, {r4, lr}
38     mov     lr, #0x30000000           /* LCD data port */
40     subs    r1, r1, #16
41 .loop16:
42     ldmgeia r0!, {r2-r3}
43     stmgeia lr,  {r2-r3}
44     ldmgeia r0!, {r4, r12}
45     stmgeia lr,  {r4, r12}
46     ldmgeia r0!, {r2-r3}
47     stmgeia lr,  {r2-r3}
48     ldmgeia r0!, {r4, r12}
49     stmgeia lr,  {r4, r12}
50     subges  r1, r1, #16
51     bge     .loop16
53     /* no need to correct the count, we're just checking bits from now */
54     tst     r1, #8
55     ldmneia r0!, {r2-r4, r12}
56     stmneia lr,  {r2-r4, r12}
57     tst     r1, #4
58     ldmneia r0!, {r2-r3}
59     stmneia lr,  {r2-r3}
60     tst     r1, #2
61     ldrne   r3, [r0], #4
62     strne   r3, [lr]
64     ldmfd   sp!, {r4, pc}
66 /****************************************************************************
67  * extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
68  *                                    unsigned bcmaddr
69  *                                    int width,
70  *                                    int stride);
71  *
72  *   Conversion from Motion JPEG and MPEG Y'PbPr to RGB is:
73  *   |R|   |1.164  0.000  1.596| |Y' -  16|
74  *   |G| = |1.164 -0.391 -0.813| |Pb - 128|
75  *   |B|   |1.164  2.018  0.000| |Pr - 128|
76  *
77  *   Scaled, normalized, rounded and tweaked to yield RGB 565:
78  *   |R|   |74   0 101| |Y' -  16| >> 9
79  *   |G| = |74 -24 -51| |Cb - 128| >> 8
80  *   |B|   |74 128   0| |Cr - 128| >> 9
81  *
82  * Converts two lines from YUV to RGB565 and writes to BCM at once. First loop
83  * loads Cb/Cr, calculates the chroma offset and saves them to buffer. Within
84  * the second loop these chroma offset are reloaded from buffer.
85  * Within each loop two pixels are calculated and written to BCM. Before each
86  * loop the desired destination address is transmitted to BCM.
87  */
88     .align      2
89     .global     lcd_write_yuv420_lines
90     .type       lcd_write_yuv420_lines, %function
91 lcd_write_yuv420_lines:
92                                       /* r0 = src = yuv_src */
93                                       /* r1 = dst = bcmaddr */
94                                       /* r2 = width */
95                                       /* r3 = stride */                
96     stmfd       sp!, { r4-r10, lr }   /* save non-scratch */
97     ldmia       r0, { r9, r10, r12 }  /* r9 = yuv_src[0] = Y'_p */
98                                       /* r10 = yuv_src[1] = Cb_p */
99                                       /* r12 = yuv_src[2] = Cr_p */
100     add         r3, r9, r3            /* r3 = &ysrc[stride] */
101     add         r4, r2, r2, asr #1    /* chroma buffer lenght = width/2 *3 */
102     mov         r4, r4, asl #2        /*   use words for str/ldm possibility */
103     add         r4, r4, #19           /*   plus room for 4 additional words, */
104     bic         r4, r4, #3            /*   rounded up to multiples of 4 byte */
105     sub         sp, sp, r4            /*   and allocate on stack */
106     stmia       sp, {r1-r4}           /* bcmaddr, width, &ysrc[stride], stack_alloc */
108     mov         r7, r2                /* r7 = loop count */
109     add         r8, sp, #16           /* chroma buffer */
110     mov         lr, #0x30000000       /* LCD data port */
111     
112     /* The following writes dest address to BCM and waits for write ready */
113     orr         r2, lr, #0x00010000   /* r2 = BCM_WR_ADDR32 */
114     orr         r6, lr, #0x00030000   /* r6 = BCM_CONTROL */
115     str         r1, [r2]              /* BCM_WR_ADDR32 = bcmaddr */
116 .busy_1:
117     ldrh        r1, [r6]              /* while (!(BCM_CONTROL & 0x2)) */
118     tst         r1, #0x2
119     beq         .busy_1      
121     /* 1st loop start */
122 10:                                   /* loop start */
124     ldrb        r0, [r10], #1         /* r0 = *usrc++ = *Cb_p++ */
125     ldrb        r1, [r12], #1         /* r1 = *vsrc++ = *Cr_p++ */
127     sub         r0, r0, #128          /* r0 = Cb-128 */
128     sub         r1, r1, #128          /* r1 = Cr-128 */
130     add         r2, r1, r1, asl #1    /* r2 = Cr*51 + Cb*24 */
131     add         r2, r2, r2, asl #4   
132     add         r2, r2, r0, asl #3   
133     add         r2, r2, r0, asl #4   
135     add         r4, r1, r1, asl #2    /* r1 = Cr*101 */
136     add         r4, r4, r1, asl #5
137     add         r1, r4, r1, asl #6
139     add         r1, r1, #256          /* r1 = rv = (r1 + 256) >> 9 */
140     mov         r1, r1, asr #9
141     rsb         r2, r2, #128          /* r2 = guv = (-r2 + 128) >> 8 */
142     mov         r2, r2, asr #8       
143     add         r0, r0, #2            /* r0 = bu = (Cb*128 + 256) >> 9 */
144     mov         r0, r0, asr #2       
145     stmia       r8!, {r0-r2}          /* store r0, r1 and r2 to chroma buffer */
146     
147     /* 1st loop, first pixel */
148     ldrb        r5, [r9], #1          /* r5 = *ysrc++ = *Y'_p++ */
149     sub         r5, r5, #16           /* r5 = (Y'-16) * 74 */
150     add         r3, r5, r5, asl #2
151     add         r5, r3, r5, asl #5
152     
153     add         r6, r1, r5, asr #8    /* r6 = r = (Y >> 9) + rv */
154     add         r3, r2, r5, asr #7    /* r3 = g = (Y >> 8) + guv */
155     add         r4, r0, r5, asr #8    /* r4 = b = (Y >> 9) + bu */
157     orr         r5, r6, r4            /* check if clamping is needed... */
158     orr         r5, r5, r3, asr #1    /* ...at all */
159     cmp         r5, #31                 
160     bls         15f                   /* -> no clamp */
161     cmp         r6, #31               /* clamp r */
162     mvnhi       r6, r6, asr #31         
163     andhi       r6, r6, #31             
164     cmp         r3, #63               /* clamp g */
165     mvnhi       r3, r3, asr #31
166     andhi       r3, r3, #63
167     cmp         r4, #31               /* clamp b */
168     mvnhi       r4, r4, asr #31         
169     andhi       r4, r4, #31          
170 15:                                   /* no clamp */
172     /* calculate pixel_1 and save to r5 for later pixel packing */
173     orr         r4, r4, r3, lsl #5    /* pixel_1 = r<<11 | g<<5 | b */
174     orr         r5, r4, r6, lsl #11   /* r5 = pixel_1 */
176     /* 1st loop, second pixel */
177     ldrb        r4, [r9], #1          /* r4 = *ysrc++ = *Y'_p++ */
178     sub         r4, r4, #16           /* r4 = (Y'-16) * 74 */
179     add         r3, r4, r4, asl #2
180     add         r4, r3, r4, asl #5
181     
182     add         r6, r1, r4, asr #8    /* r6 = r = (Y >> 9) + rv */
183     add         r3, r2, r4, asr #7    /* r3 = g = (Y >> 8) + guv */
184     add         r4, r0, r4, asr #8    /* r4 = b = (Y >> 9) + bu */   
185     
186     orr         r0, r6, r4            /* check if clamping is needed... */
187     orr         r0, r0, r3, asr #1    /* ...at all */
188     cmp         r0, #31                 
189     bls         15f                   /* -> no clamp */
190     cmp         r6, #31               /* clamp r */
191     mvnhi       r6, r6, asr #31         
192     andhi       r6, r6, #31             
193     cmp         r3, #63               /* clamp g */
194     mvnhi       r3, r3, asr #31
195     andhi       r3, r3, #63
196     cmp         r4, #31               /* clamp b */
197     mvnhi       r4, r4, asr #31         
198     andhi       r4, r4, #31          
199 15:                                   /* no clamp */
201     /* calculate pixel_2 and pack with pixel_1 before writing */
202     orr         r4, r4, r3, lsl #5    /* pixel_2 = r<<11 | g<<5 | b */
203     orr         r4, r4, r6, lsl #11   /* r4 = pixel_2 */
204     orr         r4, r5, r4, lsl #16   /* r4 = pixel_2<<16 | pixel_1 */
205     str         r4, [lr]              /* write packed pixels */
206   
207     subs        r7, r7, #2            /* check for loop end */
208     bgt         10b                   /* back to beginning  */
209     /* 1st loop end */
210     
211     /* Reload several registers for pointer rewinding for next loop */
212     add         r8, sp, #16           /* chroma buffer */
213     ldmia       sp, { r1, r7, r9}     /* r1  = bcmaddr */
214                                       /* r7  = loop count */
215                                       /* r9 = &ysrc[stride] */
216     
217     /* The following writes dest address to BCM and waits for write ready */
218     orr         r2, lr, #0x00010000   /* r2 = BCM_WR_ADDR32 */
219     orr         r6, lr, #0x00030000   /* r6 = BCM_CONTROL */
220     add         r1, r1, #640          /* dst += (LCD_WIDTH*2) */
221     str         r1, [r2]              /* BCM_WR_ADDR32 = dst */
222 .busy_2:
223     ldrh        r1, [r6]              /* while (!(BCM_CONTROL & 0x2)) */
224     tst         r1, #0x2
225     beq         .busy_2      
226                                       
228     /* 2nd loop start */
229 20:                                   /* loop start */
230     /* restore r0 (bu), r1 (rv) and r2 (guv) from chroma buffer */
231     ldmia       r8!, {r0-r2}
232   
233     /* 2nd loop, first pixel */
234     ldrb        r5, [r9], #1          /* r5 = *ysrc++ = *Y'_p++ */
235     sub         r5, r5, #16           /* r5 = (Y'-16) * 74 */
236     add         r3, r5, r5, asl #2
237     add         r5, r3, r5, asl #5
238     
239     add         r6, r1, r5, asr #8    /* r6 = r = (Y >> 9) + rv */
240     add         r3, r2, r5, asr #7    /* r3 = g = (Y >> 8) + guv */
241     add         r4, r0, r5, asr #8    /* r4 = b = (Y >> 9) + bu */
242    
243     orr         r5, r6, r4            /* check if clamping is needed... */
244     orr         r5, r5, r3, asr #1    /* ...at all */
245     cmp         r5, #31                 
246     bls         15f                   /* -> no clamp */
247     cmp         r6, #31               /* clamp r */
248     mvnhi       r6, r6, asr #31         
249     andhi       r6, r6, #31             
250     cmp         r3, #63               /* clamp g */
251     mvnhi       r3, r3, asr #31
252     andhi       r3, r3, #63
253     cmp         r4, #31               /* clamp b */
254     mvnhi       r4, r4, asr #31         
255     andhi       r4, r4, #31          
256 15:                                   /* no clamp */
257     /* calculate pixel_1 and save to r5 for later pixel packing */
258     orr         r4, r4, r3, lsl #5    /* pixel_1 = r<<11 | g<<5 | b */
259     orr         r5, r4, r6, lsl #11   /* r5 = pixel_1 */
260     
261     /* 2nd loop, second pixel */
262     ldrb        r4, [r9], #1          /* r4 = *ysrc++ = *Y'_p++ */
263     sub         r4, r4, #16           /* r4 = (Y'-16) * 74 */
264     add         r3, r4, r4, asl #2
265     add         r4, r3, r4, asl #5
266     
267     add         r6, r1, r4, asr #8    /* r6 = r = (Y >> 9) + rv */
268     add         r3, r2, r4, asr #7    /* r3 = g = (Y >> 8) + guv */
269     add         r4, r0, r4, asr #8    /* r4 = b = (Y >> 9) + bu */
270   
271     orr         r0, r6, r4            /* check if clamping is needed... */
272     orr         r0, r0, r3, asr #1    /* ...at all */
273     cmp         r0, #31                 
274     bls         15f                   /* -> no clamp */
275     cmp         r6, #31               /* clamp r */
276     mvnhi       r6, r6, asr #31         
277     andhi       r6, r6, #31             
278     cmp         r3, #63               /* clamp g */
279     mvnhi       r3, r3, asr #31
280     andhi       r3, r3, #63
281     cmp         r4, #31               /* clamp b */
282     mvnhi       r4, r4, asr #31         
283     andhi       r4, r4, #31          
284 15:                                   /* no clamp */
285    
286     /* calculate pixel_2 and pack with pixel_1 before writing */
287     orr         r4, r4, r3, lsl #5    /* pixel_2 = r<<11 | g<<5 | b */
288     orr         r4, r4, r6, lsl #11   /* r4 = pixel_2 */
289     orr         r4, r5, r4, lsl #16   /* r4 = pixel_2<<16 | pixel_1 */
290     str         r4, [lr]              /* write packed pixels */
291     
292     subs        r7, r7, #2            /* check for loop end */
293     bgt         20b                   /* back to beginning  */
294     /* 2nd loop end */
296     ldr         r3, [sp, #12]
297     add         sp, sp, r3            /* deallocate buffer */
298     ldmfd       sp!, { r4-r10, pc }   /* restore registers */
300     .ltorg
301     .size   lcd_write_yuv420_lines, .-lcd_write_yuv420_lines