1
0
Fork 0
forked from len0rd/rockbox

iPod Video: Further optimised LCD data transfer (5..6% speedup, but increase in FPS measured with test_fps is less), making use of the fact that the low address bits aren't decoded by the BCM. Major cleanup of the driver, and introduced register names.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15341 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jens Arnold 2007-10-28 12:09:46 +00:00
parent 868d3ce39c
commit b05066de12
2 changed files with 118 additions and 105 deletions

View file

@ -17,6 +17,47 @@
*
****************************************************************************/
.section .icode, "ax", %progbits
.align 2
.global lcd_write_data
.type lcd_write_data, %function
/* r0 = addr */
lcd_write_data: /* r1 = pixel count */
stmfd sp!, {r4-r6}
mov r2, #0x30000000 /* LCD data port */
tst r0, #2 /* first pixel unaligned? */
ldrneh r3, [r0], #2
strneh r3, [r2]
subne r1, r1, #1
subs r1, r1, #16
.loop16:
ldmgeia r0!, {r3-r6}
stmgeia r2, {r3-r6}
ldmgeia r0!, {r3-r6}
stmgeia r2, {r3-r6}
subges r1, r1, #16
bge .loop16
/* no need to correct the count, we're just checking bits from now */
tst r1, #8
ldmneia r0!, {r3-r6}
stmneia r2, {r3-r6}
tst r1, #4
ldmneia r0!, {r3-r4}
stmneia r2, {r3-r4}
tst r1, #2
ldrne r3, [r0], #4
strne r3, [r2]
tst r1, #1
ldrneh r3, [r0]
strneh r3, [r2]
ldmfd sp!, {r4-r6}
bx lr
/****************************************************************************
* void lcd_write_yuv_420_lines(unsigned char const * const src[3],
* int width,
@ -30,7 +71,6 @@
* |G| = |74 -24 -51| |Cb - 128| >> 8
* |B| |74 128 0| |Cr - 128| >> 9
*/
.section .icode, "ax", %progbits
.align 2
.global lcd_write_yuv420_lines
.type lcd_write_yuv420_lines, %function

View file

@ -29,6 +29,25 @@
#include "kernel.h"
#include "system.h"
/* The BCM bus width is 16 bits. But since the low address bits aren't decoded
* by the chip (the 3 BCM address bits are mapped to address bits 16..18 of the
* PP5022), writing 32 bits (and even more, using 'stmia') at once works. */
#define BCM_DATA (*(volatile unsigned short*)(0x30000000))
#define BCM_DATA32 (*(volatile unsigned long *)(0x30000000))
#define BCM_WR_ADDR (*(volatile unsigned short*)(0x30010000))
#define BCM_WR_ADDR32 (*(volatile unsigned long *)(0x30010000))
#define BCM_RD_ADDR (*(volatile unsigned short*)(0x30020000))
#define BCM_RD_ADDR32 (*(volatile unsigned long *)(0x30020000))
#define BCM_CONTROL (*(volatile unsigned short*)(0x30030000))
#define BCM_ALT_DATA (*(volatile unsigned short*)(0x30040000))
#define BCM_ALT_DATA32 (*(volatile unsigned long *)(0x30040000))
#define BCM_ALT_WR_ADDR (*(volatile unsigned short*)(0x30050000))
#define BCM_ALT_WR_ADDR32 (*(volatile unsigned long *)(0x30050000))
#define BCM_ALT_RD_ADDR (*(volatile unsigned short*)(0x30060000))
#define BCM_ALT_RD_ADDR32 (*(volatile unsigned long *)(0x30060000))
#define BCM_ALT_CONTROL (*(volatile unsigned short*)(0x30070000))
/*** hardware configuration ***/
void lcd_set_contrast(int val)
@ -74,130 +93,97 @@ void lcd_blit(const fb_data* data, int x, int by, int width,
static inline void lcd_bcm_write32(unsigned address, unsigned value)
{
/* write out destination address as two 16bit values */
outw(address, 0x30010000);
outw((address >> 16), 0x30010000);
/* write out destination address */
BCM_WR_ADDR32 = address;
/* wait for it to be write ready */
while ((inw(0x30030000) & 0x2) == 0);
while (!(BCM_CONTROL & 0x2));
/* write out the value low 16, high 16 */
outw(value, 0x30000000);
outw((value >> 16), 0x30000000);
/* write out the value */
BCM_DATA32 = value;
}
static void lcd_bcm_setup_rect(unsigned cmd,
unsigned start_horiz,
unsigned start_vert,
unsigned max_horiz,
unsigned max_vert,
unsigned count)
unsigned x,
unsigned y,
unsigned width,
unsigned height)
{
lcd_bcm_write32(0x1F8, 0xFFFA0005);
lcd_bcm_write32(0xE0000, cmd);
lcd_bcm_write32(0xE0004, start_horiz);
lcd_bcm_write32(0xE0008, start_vert);
lcd_bcm_write32(0xE000C, max_horiz);
lcd_bcm_write32(0xE0010, max_vert);
lcd_bcm_write32(0xE0014, count);
lcd_bcm_write32(0xE0018, count);
lcd_bcm_write32(0xE0004, x);
lcd_bcm_write32(0xE0008, y);
lcd_bcm_write32(0xE000C, x + width - 1);
lcd_bcm_write32(0xE0010, y + height - 1);
lcd_bcm_write32(0xE0014, (width * height) << 1);
lcd_bcm_write32(0xE0018, (width * height) << 1);
lcd_bcm_write32(0xE001C, 0);
}
static inline unsigned lcd_bcm_read32(unsigned address) {
while ((inw(0x30020000) & 1) == 0);
static inline unsigned lcd_bcm_read32(unsigned address)
{
while (!(BCM_RD_ADDR & 1));
/* write out destination address as two 16bit values */
outw(address, 0x30020000);
outw((address >> 16), 0x30020000);
/* write out destination address */
BCM_RD_ADDR32 = address;
/* wait for it to be read ready */
while ((inw(0x30030000) & 0x10) == 0);
while (!(BCM_CONTROL & 0x10));
/* read the value */
return inw(0x30000000) | inw(0x30000000) << 16;
return BCM_DATA32;
}
static int finishup_needed = 0;
static bool finishup_needed = false;
/* Update a fraction of the display. */
void lcd_update_rect(int x, int y, int width, int height) ICODE_ATTR;
void lcd_update_rect(int x, int y, int width, int height)
{
{
int endy = x + width;
/* Ensure x and width are both even - so we can read 32-bit aligned
data from lcd_framebuffer */
x &= ~1;
width &= ~1;
if (x + width < endy) {
width += 2;
}
}
const fb_data *addr;
if (finishup_needed)
if (x + width >= LCD_WIDTH)
width = LCD_WIDTH - x;
if (y + height >= LCD_HEIGHT)
height = LCD_HEIGHT - y;
if ((width <= 0) || (height <= 0))
return; /* Nothing left to do - 0 is harmful to lcd_write_data(). */
addr = &lcd_framebuffer[y][x];
if (finishup_needed)
{
/* Bottom-half of original lcd_bcm_finishup() function */
unsigned int data = lcd_bcm_read32(0x1F8);
while (data == 0xFFFA0005 || data == 0xFFFF)
while (data == 0xFFFA0005 || data == 0xFFFF)
{
/* This loop can wait for up to 14ms - so we yield() */
yield();
data = lcd_bcm_read32(0x1F8);
}
}
lcd_bcm_read32(0x1FC);
lcd_bcm_setup_rect(0x34, x, y, width, height);
/* write out destination address */
BCM_WR_ADDR32 = 0xE0020;
while (!(BCM_CONTROL & 0x2)); /* wait for it to be write ready */
do
{
int rect1, rect2, rect3, rect4;
int count = (width * height) << 1;
/* calculate the drawing region */
rect1 = x; /* start horiz */
rect2 = y; /* start vert */
rect3 = (x + width) - 1; /* max horiz */
rect4 = (y + height) - 1; /* max vert */
/* setup the drawing region */
lcd_bcm_setup_rect(0x34, rect1, rect2, rect3, rect4, count);
}
/* write out destination address as two 16bit values */
outw((0xE0020 & 0xffff), 0x30010000);
outw((0xE0020 >> 16), 0x30010000);
/* wait for it to be write ready */
while ((inw(0x30030000) & 0x2) == 0);
{
unsigned short *src = (unsigned short*)&lcd_framebuffer[y][x];
unsigned short *end = &src[LCD_WIDTH * height];
int line_rem = (LCD_WIDTH - width);
while (src < end) {
/* Duff's Device to unroll loop */
register int count = width ;
register int n=( count + 7 ) / 8;
switch( count % 8 ) {
case 0: do{ outw(*(src++), 0x30000000);
case 7: outw(*(src++), 0x30000000);
case 6: outw(*(src++), 0x30000000);
case 5: outw(*(src++), 0x30000000);
case 4: outw(*(src++), 0x30000000);
case 3: outw(*(src++), 0x30000000);
case 2: outw(*(src++), 0x30000000);
case 1: outw(*(src++), 0x30000000);
} while(--n>0);
}
src += line_rem;
}
lcd_write_data(addr, width);
addr += LCD_WIDTH;
}
while (--height > 0);
/* Top-half of original lcd_bcm_finishup() function */
outw(0x31, 0x30030000);
BCM_CONTROL = 0x31;
lcd_bcm_read32(0x1FC);
finishup_needed = 1;
finishup_needed = true;
}
/* Update the display.
@ -228,11 +214,11 @@ void lcd_yuv_blit(unsigned char * const src[3],
yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
if (finishup_needed)
if (finishup_needed)
{
/* Bottom-half of original lcd_bcm_finishup() function */
unsigned int data = lcd_bcm_read32(0x1F8);
while (data == 0xFFFA0005 || data == 0xFFFF)
while (data == 0xFFFA0005 || data == 0xFFFF)
{
/* This loop can wait for up to 14ms - so we yield() */
yield();
@ -241,26 +227,13 @@ void lcd_yuv_blit(unsigned char * const src[3],
}
lcd_bcm_read32(0x1FC);
lcd_bcm_setup_rect(0x34, x, y, width, height);
{
int rect1, rect2, rect3, rect4;
int count = (width * height) << 1;
/* calculate the drawing region */
rect1 = x; /* start horiz */
rect2 = y; /* start vert */
rect3 = (x + width) - 1; /* max horiz */
rect4 = (y + height) - 1; /* max vert */
/* write out destination address */
BCM_WR_ADDR32 = 0xE0020;
/* setup the drawing region */
lcd_bcm_setup_rect(0x34, rect1, rect2, rect3, rect4, count);
}
/* write out destination address as two 16bit values */
outw((0xE0020 & 0xffff), 0x30010000);
outw((0xE0020 >> 16), 0x30010000);
/* wait for it to be write ready */
while ((inw(0x30030000) & 0x2) == 0);
while (!(BCM_CONTROL & 0x2)); /* wait for it to be write ready */
height >>= 1;
do
@ -274,9 +247,9 @@ void lcd_yuv_blit(unsigned char * const src[3],
while (--height > 0);
/* Top-half of original lcd_bcm_finishup() function */
outw(0x31, 0x30030000);
BCM_CONTROL = 0x31;
lcd_bcm_read32(0x1FC);
finishup_needed = 1;
finishup_needed = true;
}