Index: firmware/target/arm/ipod/video/lcd-video.c =================================================================== --- firmware/target/arm/ipod/video/lcd-video.c (revision 20248) +++ firmware/target/arm/ipod/video/lcd-video.c (working copy) @@ -54,6 +54,31 @@ #define BCM_ALT_RD_ADDR32 (*(volatile unsigned long *)(0x30060000)) #define BCM_ALT_CONTROL (*(volatile unsigned short*)(0x30070000)) +/* BCM SRAM: 1.25M at 0-0x13FFFF, reading wraps at 0x200000 */ +#define BCMA_SRAM_BASE 0 +#define BCMA_COMMAND 0x1F8 +#define BCMA_STATUS 0x1FC + +/* BCM SDRAM: 4M at 0xC0000000-0xC0400000 + Writing wraps at 0xC0800000, until end of address space. + */ +#define BCMA_SDRAM_BASE 0xC0000000 +#define BCMA_TV_FB_BASE 0xC0000000 +#define BCMA_TV_CMD_BASE 0xC0200000 + +/* BCM commands. Write value to BCMA_COMMAND, execute by writing + 0x31 to BCM_CONTROL. Note how numbers are encoded. + */ +#define BCM_CMD(x) ((~(x) << 16) | (x)) +#define BCMCMD_LCD_UPDATE BCM_CMD(0) +#define BCMCMD_SELFTEST BCM_CMD(1) /* Send no data, wait 40 seconds */ +#define BCMCMD_TV_PALBMP BCM_CMD(2) +#define BCMCMD_TV_NTSCBMP BCM_CMD(3) +#define BCMCMD_LCD_UPDATERECT BCM_CMD(5) +#define BCMCMD_LCD_SLEEP BCM_CMD(8) +/* BCM_CMD(12) involved in shutdown */ +#define BCMCMD_TV_MVOFF BCM_CMD(14) /* Turn off Macrovision */ + /* Time until the BCM is considered stalled and will be re-kicked. * Must be guaranteed to be >~ 20ms. */ #define BCM_UPDATE_TIMEOUT (HZ/20) @@ -153,6 +178,8 @@ } #endif /* HAVE_LCD_SLEEP */ +bool tvout_enabled = true; + static inline void bcm_write_addr(unsigned address) { BCM_WR_ADDR32 = address; /* write destination address */ @@ -303,6 +330,9 @@ (void)yesno; } +static void tvout_init_320x240(void); +static void bcm_simplecmd(unsigned cmd); + /* LCD init */ void lcd_init_device(void) { @@ -337,17 +367,24 @@ } mutex_init(&lcdstate_lock); #endif - tick_add_task(&lcd_tick); + tvout_init_320x240(); + bcm_simplecmd(BCMCMD_TV_MVOFF); + bcm_simplecmd(BCMCMD_LCD_SLEEP); + //tick_add_task(&lcd_tick); #endif /* !BOOTLOADER */ } /*** update functions ***/ +static void tvout_write_rgb565(const fb_data* p_bytes, int count); +static void bcm_write_zero_words(int count); + /* Update a fraction of the display. */ -void lcd_update_rect(int x, int y, int width, int height) +void lcd_update_rect(int x, int y, int width, int inheight) { const fb_data *addr; unsigned bcmaddr; + int height; #ifdef HAVE_LCD_SLEEP if (!lcd_state.display_on) @@ -356,12 +393,13 @@ if (x + width >= LCD_WIDTH) width = LCD_WIDTH - x; - if (y + height >= LCD_HEIGHT) - height = LCD_HEIGHT - y; + if (y + inheight >= LCD_HEIGHT) + inheight = LCD_HEIGHT - y; - if ((width <= 0) || (height <= 0)) + if ((width <= 0) || (inheight <= 0)) return; /* Nothing left to do. */ +#if 0 /* Ensure x and width are both even. The BCM doesn't like small unaligned * writes and would just ignore them. */ width = (width + (x & 1) + 1) & ~1; @@ -376,10 +414,11 @@ if (width == LCD_WIDTH) { bcm_write_addr(bcmaddr); - lcd_write_data(addr, width * height); + lcd_write_data(addr, width * inheight); } else { + height = inheight; do { bcm_write_addr(bcmaddr); @@ -390,6 +429,47 @@ while (--height > 0); } lcd_unblock_and_update(); +#endif /* LCD enabled */ + + /* This must come second because it changes variables */ + if (tvout_enabled && width != 0) { + /* 4 pixels make 3 words in a repeating pattern. + Ensure x and width are divisible by 4. + Divide width by 4 for tvout_write_rgb565. + */ + width = (width + (x & 3) + 3) >> 2; + x &= ~3; + + addr = &lcd_framebuffer[y][x]; + bcmaddr = BCMA_TV_FB_BASE + (LCD_WIDTH*3) * y; + + if (width == LCD_WIDTH/4) + { + bcm_write_addr(bcmaddr); + tvout_write_rgb565(addr, width * inheight); + } + else + { + bcmaddr += x + (x << 1); + height = inheight; + do + { + bcm_write_addr(bcmaddr); + bcmaddr += (LCD_WIDTH*3); + tvout_write_rgb565(addr, width); + addr += LCD_WIDTH; + } + while (--height > 0); + } + + /* Write some more data after the end of the framebuffer to + flush written data from BCM cache. + TODO: Find a better method to flush the cache or at least + find how much data needs to be written exactly. + */ + bcm_write_addr(BCMA_TV_FB_BASE + LCD_WIDTH*LCD_HEIGHT*3); + bcm_write_zero_words(LCD_WIDTH*3*20/4); + } /* tvout_enabled */ } /* Update the display. @@ -607,3 +687,112 @@ } #endif /* HAVE_LCD_SHUTDOWN */ #endif /* HAVE_LCD_SLEEP */ + +void tvout_enable(bool enable) +{ + unsigned temp; + + /* Enable TV out? */ + temp = bcm_read32(0x10002804); + temp |= 0x40; + temp &= ~0x80; + bcm_write32(0x10002804, temp); + + if (enable) + bcm_write32(0x10002810, 0x80000); + else + bcm_write32(0x10002818, 0x80000); +} + +static void bcm_simplecmd(unsigned cmd) { + unsigned status; + + bcm_write32(BCMA_COMMAND, cmd); + + BCM_CONTROL = 0x31; + + do { + status = bcm_read32(BCMA_COMMAND); + } while (status == cmd || status == 0xFFFF); +} + +/* Lookup table from 6 bit 0-63 to 8 bit 16-235 */ +static const unsigned char leveltab_6[64] ICONST_ATTR = { + 16, 19, 23, 26, 30, 33, 37, 40, 44, 47, 51, 54, 58, 61, 65, 68, 72, 75, + 79, 82, 86, 89, 92, 96, 99, 103, 106, 110, 113, 117, 120, 124, 127, 131, + 134, 138, 141, 145, 148, 152, 155, 159, 162, 165, 169, 172, 176, 179, 183, + 186, 190, 193, 197, 200, 204, 207, 211, 214, 218, 221, 225, 228, 232, 235 +}; + +/* Lookup table from 5 bit 0-31 to 8 bit 16-235 */ +static const unsigned char leveltab_5[32] ICONST_ATTR = { + 16, 23, 30, 37, 44, 51, 58, 65, 73, 80, 87, 94, 101, 108, 115, 122, 129, + 136, 143, 150, 157, 164, 171, 178, 186, 193, 200, 207, 214, 221, 228, 235 +}; + +/* Write lcd_framebuffer data to TV out frame buffer, converting it to + 24 bit. In one loop, 4 pixels are written to 3 words. Count is + in units of 4 pixels + */ +static void tvout_write_rgb565(const fb_data* p_bytes, int count) +{ + register const unsigned long *inptr = (const unsigned long *)p_bytes; + do { + register unsigned long pix12, pix34; + pix12 = *(inptr++); + pix34 = *(inptr++); + BCM_DATA32 = + leveltab_5[pix12 & 0x1f] | + (leveltab_6[(pix12 >> 5) & 0x3f] << 8) | + (leveltab_5[(pix12 >> 11) & 0x1f] << 16) | + (leveltab_5[(pix12 >> 16) & 0x1f] << 24); + BCM_DATA32 = + leveltab_6[(pix12 >> 21) & 0x3f] | + (leveltab_5[pix12 >> 27] << 8) | + (leveltab_5[pix34 & 0x1f] << 16) | + (leveltab_6[(pix34 >> 5) & 0x3f] << 24); + BCM_DATA32 = + leveltab_5[(pix34 >> 11) & 0x1f] | + (leveltab_5[(pix34 >> 16) & 0x1f] << 8) | + (leveltab_6[(pix34 >> 21) & 0x3f] << 16) | + (leveltab_5[pix34 >> 27] << 24); + } while (--count); +} + +static void bcm_write_zero_words(int count) +{ + do { + BCM_DATA32 = 0; + } while (--count); +} + +/* BMP file header used for TV out BCM commands */ +static const fb_data bmhdr_320x240[] = { + 0x4D42, /* magic: BM */ + 0x8438, 3, /* file size */ + 0, 0, /* reserved */ + 0x36, 0, /* bitmap data offset */ + 40, 0, /* sizeof(BITMAPINFOHEADER) == 40 */ + 320, 0, /* width in pixels */ + 240, 0, /* height in pixels */ + 1, /* colour planes */ + 24, /* 24 bpp */ + 0, 0, /* uncomprsessed */ + 0x8402, 3, /* size of bitmap data */ + 0x0B12, 0, /* horizontal DPI */ + 0x0B12, 0, /* vertical DPI */ + 0 + /* Number of colours and number of important colours go here. + That's 6 more zero bytes. + */ +}; + +/* Set BCM to 320x240 TV output */ +static void tvout_init_320x240(void) { + /* Write bitmap data */ + bcm_write_addr(BCMA_TV_CMD_BASE); + lcd_write_data(bmhdr_320x240, 24); + bcm_write_zero_words(320*240*3/4+2); + + bcm_simplecmd(BCMCMD_TV_NTSCBMP); +}