diff options
author | Andrew Ryabinin <ryabinin.a.a@gmail.com> | 2012-09-25 11:41:12 +0400 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2012-09-27 09:42:44 +0200 |
commit | 84134f737fc4643cad5eb50fef3bae0df657f054 (patch) | |
tree | b2b0b28a4723a2eef9a340f6bc7a18e796ab1b5b /firmware/target/arm/rk27xx/lcdif-rk27xx.c | |
parent | f636aa07dfb541b5828d44e8168e7b36e5ad8898 (diff) |
rk27xx lcd code rework
Use DMA engine for fullscreen updates and bypass mode for partial
updates. This gives major boost on rk27generic:
default ARM:AHB:APB 200:100:50
HEAD 1/1: 26.3fps 1/4: 105.0fps
patched 1/1: 116.5fps 1/4: 249.5fps
with freq scalling NORMAL mode ARM:AHB:APB 50:50:50
HEAD 1/1: 13.1fps 1/4: 52.5fps
patched 1/1: 54.5fps 1/4: 119.0fps
Tested on rk27generic noname DAP and on Hifimans.
Change-Id: Id9dd4d2d61542c7ea6b5c6336b170d6357cefde9
Diffstat (limited to 'firmware/target/arm/rk27xx/lcdif-rk27xx.c')
-rw-r--r-- | firmware/target/arm/rk27xx/lcdif-rk27xx.c | 252 |
1 files changed, 178 insertions, 74 deletions
diff --git a/firmware/target/arm/rk27xx/lcdif-rk27xx.c b/firmware/target/arm/rk27xx/lcdif-rk27xx.c index affc49b213..01b19603fd 100644 --- a/firmware/target/arm/rk27xx/lcdif-rk27xx.c +++ b/firmware/target/arm/rk27xx/lcdif-rk27xx.c @@ -5,9 +5,9 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id$ * * Copyright (C) 2011 Marcin Bukat + * Copyright (C) 2012 Andrew Ryabinin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,9 +25,28 @@ #include "system.h" #include "cpu.h" #include "lcdif-rk27xx.h" +#include "lcd-target.h" +#include <string.h> +/* dwdma linked list struct - hw defined + * we don't use __attribute__((packed)) just because + * all fields are 32bits and so are aligned + */ +struct llp_t { + uint32_t sar; + uint32_t dar; + struct llp_t *llp; + uint32_t ctl_l; + uint32_t ctl_h; + uint32_t dstat; +}; + +/* array of structs which describes full screen update + * each struct describes dma transfer for single line + */ +static struct llp_t scr_llp[LCD_HEIGHT]; -unsigned int lcd_data_transform(unsigned int data) +static uint32_t lcd_data_transform(uint32_t data) { unsigned int r, g, b; @@ -49,38 +68,10 @@ unsigned int lcd_data_transform(unsigned int data) return (r | g | b); } -void lcd_cmd(unsigned int cmd) -{ - LCD_COMMAND = lcd_data_transform(cmd); -} - -void lcd_data(unsigned int data) -{ - LCD_DATA = lcd_data_transform(data); -} - -void lcd_write_reg(unsigned int reg, unsigned int val) -{ - lcd_cmd(reg); - lcd_data(val); -} - -static void lcdctrl_bypass(unsigned int on_off) -{ - while (!(LCDC_STA & LCDC_MCU_IDLE)); - - if (on_off) - MCU_CTRL |= MCU_CTRL_BYPASS; - else - MCU_CTRL &= ~MCU_CTRL_BYPASS; -} - -/* This part is unclear. I am unable to use buffered/FIFO based writes - * to lcd. Depending on settings of IF I get various patterns on display - * but not what I want to display apparently. - */ static void lcdctrl_init(void) { + int i; + /* alpha b111 * stop at current frame complete * MCU mode @@ -89,37 +80,43 @@ static void lcdctrl_init(void) LCDC_CTRL = ALPHA(7) | LCDC_STOP | LCDC_MCU | RGB24B; MCU_CTRL = ALPHA_BASE(0x3f) | MCU_CTRL_BYPASS; - HOR_ACT = LCD_WIDTH + 3; /* define horizonatal active region */ - VERT_ACT = LCD_HEIGHT; /* define vertical active region */ - VERT_PERIOD = 0xfff; /* CSn/WEn/RDn signal timings */ - - LINE0_YADDR = LINE_ALPHA_EN | 0x7fe; - LINE1_YADDR = LINE_ALPHA_EN | ((1 * LCD_WIDTH) - 2); - LINE2_YADDR = LINE_ALPHA_EN | ((2 * LCD_WIDTH) - 2); - LINE3_YADDR = LINE_ALPHA_EN | ((3 * LCD_WIDTH) - 2); + /* Warning: datasheet addresses and OF addresses + * don't match for HOR_ACT and VERT_ACT + */ + HOR_ACT = LCD_WIDTH + 3; /* define horizonatal active region */ + VERT_ACT = LCD_HEIGHT; /* define vertical active region */ + VERT_PERIOD = (1<<7)|(1<<5)|1; /* CSn/WEn/RDn signal timings */ - LINE0_UVADDR = 0x7fe + 1; - LINE1_UVADDR = ((1 * LCD_WIDTH) - 2 + 1); - LINE2_UVADDR = ((2 * LCD_WIDTH) - 2 + 1); - LINE3_UVADDR = ((3 * LCD_WIDTH) - 2 + 1); + lcd_display_init(); + lcdctrl_bypass(0); -#if 0 + /* This lines define layout of data in lcdif internal buffer + * LINEx_UVADDR = LINEx_YADDR + 1 + * buffer is organized as 2048 x 32bit + * we use RGB565 (16 bits per pixel) so we pack 2 pixels + * in every lcdbuffer mem cell + */ LINE0_YADDR = 0; - LINE1_YADDR = (1 * LCD_WIDTH); - LINE2_YADDR = (2 * LCD_WIDTH); - LINE3_YADDR = (3 * LCD_WIDTH); + LINE1_YADDR = 1 * LCD_WIDTH/2; + LINE2_YADDR = 2 * LCD_WIDTH/2; + LINE3_YADDR = 3 * LCD_WIDTH/2; LINE0_UVADDR = 1; - LINE1_UVADDR = (1 * LCD_WIDTH) + 1; - LINE2_UVADDR = (2 * LCD_WIDTH) + 1; - LINE3_UVADDR = (3 * LCD_WIDTH) + 1; - - START_X = 0; - START_Y = 0; - DELTA_X = 0x200; /* no scaling */ - DELTA_Y = 0x200; /* no scaling */ -#endif - LCDC_INTR_MASK = INTR_MASK_LINE; /* INTR_MASK_EVENLINE; */ + LINE1_UVADDR = (1 * LCD_WIDTH/2) + 1; + LINE2_UVADDR = (2 * LCD_WIDTH/2) + 1; + LINE3_UVADDR = (3 * LCD_WIDTH/2) + 1; + + LCDC_INTR_MASK = INTR_MASK_EVENLINE; + + /* This loop seems to fix strange glitch where + * whole lcd content was shifted ~4 lines verticaly + * on second lcd_update call + */ + for (i=0; i<2048; i++) + *((uint32_t *)LCD_BUFF + i) = 0; + + /* Setup buffered writes to lcd controler */ + MCU_CTRL = MCU_CTRL_RS_HIGH|MCU_CTRL_BUFF_WRITE|MCU_CTRL_BUFF_START; } /* configure pins to drive lcd in 18bit mode (16bit mode for HiFiMAN's) */ @@ -138,26 +135,133 @@ static void iomux_lcd(enum lcdif_mode_t mode) SCU_IOMUXB_CON |= IOMUX_LCD_D815; } -void lcdif_init(enum lcdif_mode_t mode) +static void dwdma_init(void) { - iomux_lcd(mode); /* setup pins for lcd interface */ - lcdctrl_init(); /* basic lcdc module configuration */ - lcdctrl_bypass(1); /* run in bypass mode - all writes goes directly to lcd controller */ + DWDMA_DMA_CHEN = 0xf00; + DWDMA_CLEAR_BLOCK = 0x0f; + DWDMA_DMA_CFG = 1; /* global enable */ } -/* This is ugly hack. We drive lcd in bypass mode - * where all writes goes directly to lcd controller. - * This is suboptimal at best. IF module povides - * FIFO, internal sram buffer, hardware scaller, - * DMA signals, hardware alpha blending and more. - * BUT the fact is that I have no idea how to use - * this modes. Datasheet floating around is very - * unclean in this regard and OF uses ackward - * lcd update routines which are hard to understand. - * Moreover OF sets some bits in IF module registers - * which are referred as reseved in datasheet. +static void llp_setup(void *src, void *dst, struct llp_t *llp, uint32_t size) +{ + llp->sar = (uint32_t)src; + llp->dar = (uint32_t)dst; + llp->llp = llp + 1; + llp->ctl_h = size; + llp->ctl_l = (1<<20) | + (1<<23) | + (1<<17) | + (2<<1) | + (2<<4) | + (3<<11) | + (3<<14) | + (1<<27) | + (1<<28) ; +} + +static void llp_end(struct llp_t *llp) +{ + llp->ctl_l &= ~((1<<27)|(1<<28)); +} + + +static void dwdma_start(uint8_t ch, struct llp_t *llp, uint8_t handshake) +{ + DWDMA_SAR(ch) = 0; + DWDMA_DAR(ch) = 0; + DWDMA_LLP(ch) = (uint32_t)llp; + DWDMA_CTL_L(ch) = (1<<20) | + (1<<23) | + (1<<17) | + (2<<1) | + (2<<4) | + (3<<11) | + (3<<14) | + (1<<27) | + (1<<28) ; + + DWDMA_CTL_H(ch) = 1; + DWDMA_CFG_L(ch) = (7<<5); + DWDMA_CFG_H(ch) = (handshake<<11)|(1<<2); + DWDMA_SGR(ch) = (13<<20); + DWDMA_DMA_CHEN = (0x101<<ch); +} + +static void create_llp(void) +{ + int i; + + /* build LLPs */ + for (i=0; i<LCD_HEIGHT; i++) + llp_setup((void *)FBADDR(0,i), + (void*)(LCD_BUFF+((i%4)*4*LCD_WIDTH/2)), + &(scr_llp[i]), + LCD_WIDTH/2); + + llp_end(&scr_llp[LCD_HEIGHT-1]); +} + +/* Public functions */ + +/* Switch between lcdif bypass mode and buffered mode + * used in private implementations of lcd_set_gram_area() + */ +void lcdctrl_bypass(unsigned int on_off) +{ + while (!(LCDC_STA & LCDC_MCU_IDLE)); + + if (on_off) + MCU_CTRL |= MCU_CTRL_BYPASS; + else + MCU_CTRL &= ~MCU_CTRL_BYPASS; +} + +/* Helper functions used to write commands + * to lcd controler in bypass mode + * used in controler specific implementations of: + * lcd_sleep() + * lcd_display_init() */ +void lcd_cmd(unsigned int cmd) +{ + LCD_COMMAND = lcd_data_transform(cmd); +} + +void lcd_data(unsigned int data) +{ + LCD_DATA = lcd_data_transform(data); +} + +void lcd_write_reg(unsigned int reg, unsigned int val) +{ + lcd_cmd(reg); + lcd_data(val); +} + +/* rockbox API functions */ +void lcd_init_device(void) +{ + iomux_lcd(LCD_DATABUS_WIDTH); /* setup pins for lcd interface */ + dwdma_init(); /* init dwdma module */ + create_llp(); /* build LLPs for screen update dma */ + lcdctrl_init(); /* basic lcdc module configuration */ +} + void lcd_update() { - lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); + lcd_set_gram_area(0, 0, LCD_WIDTH, LCD_HEIGHT); + lcdctrl_bypass(0); + + commit_discard_dcache_range(FBADDR(0,0), 2*LCD_WIDTH*LCD_HEIGHT); + + while (!(LCDC_STA & LCDC_MCU_IDLE)); + + dwdma_start(0, scr_llp, 6); + udelay(10); + + /* Setup buffered writes to lcd controler */ + MCU_CTRL = MCU_CTRL_RS_HIGH|MCU_CTRL_BUFF_WRITE|MCU_CTRL_BUFF_START; + + /* Wait for DMA transfer to finish */ + while (DWDMA_CTL_L(0) & (1<<27)); } |