From 84134f737fc4643cad5eb50fef3bae0df657f054 Mon Sep 17 00:00:00 2001 From: Andrew Ryabinin Date: Tue, 25 Sep 2012 11:41:12 +0400 Subject: 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 --- firmware/export/rk27xx.h | 20 +- firmware/target/arm/rk27xx/hm60x/lcd-target.h | 27 +++ firmware/target/arm/rk27xx/hm801/lcd-target.h | 27 +++ firmware/target/arm/rk27xx/lcd-hifiman.c | 89 +++++--- firmware/target/arm/rk27xx/lcdif-rk27xx.c | 252 +++++++++++++++------ firmware/target/arm/rk27xx/lcdif-rk27xx.h | 7 +- .../arm/rk27xx/rk27generic/lcd-rk27generic.c | 41 ++-- .../target/arm/rk27xx/rk27generic/lcd-target.h | 26 +++ 8 files changed, 352 insertions(+), 137 deletions(-) create mode 100644 firmware/target/arm/rk27xx/hm60x/lcd-target.h create mode 100644 firmware/target/arm/rk27xx/hm801/lcd-target.h create mode 100644 firmware/target/arm/rk27xx/rk27generic/lcd-target.h diff --git a/firmware/export/rk27xx.h b/firmware/export/rk27xx.h index d1b9758f65..c75004c5f7 100644 --- a/firmware/export/rk27xx.h +++ b/firmware/export/rk27xx.h @@ -846,10 +846,10 @@ #define VERT_PERIOD (*(volatile unsigned long *)(AHB1_LCDC + 0x0C)) #define HOR_PW (*(volatile unsigned long *)(AHB1_LCDC + 0x10)) #define VERT_PW (*(volatile unsigned long *)(AHB1_LCDC + 0x14)) -#define HOR_ACT (*(volatile unsigned long *)(AHB1_LCDC + 0x18)) -#define VERT_ACT (*(volatile unsigned long *)(AHB1_LCDC + 0x1C)) -#define HOR_BP (*(volatile unsigned long *)(AHB1_LCDC + 0x20)) -#define VERT_BP (*(volatile unsigned long *)(AHB1_LCDC + 0x24)) +#define HOR_BP (*(volatile unsigned long *)(AHB1_LCDC + 0x18)) +#define VERT_BP (*(volatile unsigned long *)(AHB1_LCDC + 0x1C)) +#define HOR_ACT (*(volatile unsigned long *)(AHB1_LCDC + 0x20)) +#define VERT_ACT (*(volatile unsigned long *)(AHB1_LCDC + 0x24)) #define LINE0_YADDR (*(volatile unsigned long *)(AHB1_LCDC + 0x28)) #define LINE_ALPHA_EN (1<<14) #define LINE_SCALE_EN (1<<13) @@ -877,13 +877,23 @@ #define INTR_MASK_VERT (1<<1) #define INTR_MASK_HOR (1<<0) +#define ALPHA_ALX (*(volatile unsigned long *)(AHB1_LCDC + 0x5C)) +#define ALPHA_ATY (*(volatile unsigned long *)(AHB1_LCDC + 0x60)) +#define ALPHA_ARX (*(volatile unsigned long *)(AHB1_LCDC + 0x64)) +#define ALPHA_ABY (*(volatile unsigned long *)(AHB1_LCDC + 0x68)) + +#define ALPHA_BLX (*(volatile unsigned long *)(AHB1_LCDC + 0x6C)) +#define ALPHA_BTY (*(volatile unsigned long *)(AHB1_LCDC + 0x70)) +#define ALPHA_BRX (*(volatile unsigned long *)(AHB1_LCDC + 0x74)) +#define ALPHA_BBY (*(volatile unsigned long *)(AHB1_LCDC + 0x78)) + #define LCDC_STA (*(volatile unsigned long *)(AHB1_LCDC + 0x7C)) #define LCDC_MCU_IDLE (1<<12) #define LCD_COMMAND (*(volatile unsigned long *)(AHB1_LCDC + 0x1000)) #define LCD_DATA (*(volatile unsigned long *)(AHB1_LCDC + 0x1004)) -#define LCD_BUFF (*(volatile unsigned long *)(AHB1_LCDC + 0x2000)) +#define LCD_BUFF ((volatile void *)(AHB1_LCDC + 0x2000)) /* High speed ADC interface */ #define AHB1_HS_ADC 0x186EC000 #define HSADC_DATA (*(volatile unsigned long *)(AHB1_HS_ADC + 0x00)) diff --git a/firmware/target/arm/rk27xx/hm60x/lcd-target.h b/firmware/target/arm/rk27xx/hm60x/lcd-target.h new file mode 100644 index 0000000000..7c2f194e4c --- /dev/null +++ b/firmware/target/arm/rk27xx/hm60x/lcd-target.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef LCD_TARGET_H +#define LCD_TARGET_H + +#define LCD_DATABUS_WIDTH LCDIF_16BIT + +#endif diff --git a/firmware/target/arm/rk27xx/hm801/lcd-target.h b/firmware/target/arm/rk27xx/hm801/lcd-target.h new file mode 100644 index 0000000000..7c2f194e4c --- /dev/null +++ b/firmware/target/arm/rk27xx/hm801/lcd-target.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef LCD_TARGET_H +#define LCD_TARGET_H + +#define LCD_DATABUS_WIDTH LCDIF_16BIT + +#endif diff --git a/firmware/target/arm/rk27xx/lcd-hifiman.c b/firmware/target/arm/rk27xx/lcd-hifiman.c index aaf181690e..4ab04f7653 100644 --- a/firmware/target/arm/rk27xx/lcd-hifiman.c +++ b/firmware/target/arm/rk27xx/lcd-hifiman.c @@ -20,6 +20,7 @@ * ****************************************************************************/ + #include "config.h" #include "kernel.h" #include "lcd.h" @@ -29,7 +30,7 @@ static bool display_on = false; -static void lcd_v1_display_init(void) +void lcd_v1_display_init(void) { unsigned int x, y; @@ -84,7 +85,7 @@ static void lcd_v1_display_init(void) udelay(40); /* Memmory access setting */ - lcd_write_reg(0x16, 0x48); + lcd_write_reg(0x16, 0x68); /* Setup 16bit mode */ lcd_write_reg(0x17, 0x05); @@ -92,11 +93,11 @@ static void lcd_v1_display_init(void) lcd_write_reg(0x02, 0x00); lcd_write_reg(0x03, 0x00); lcd_write_reg(0x04, 0x00); - lcd_write_reg(0x05, LCD_HEIGHT - 1); + lcd_write_reg(0x05, LCD_WIDTH - 1); lcd_write_reg(0x06, 0x00); lcd_write_reg(0x07, 0x00); lcd_write_reg(0x08, 0x00); - lcd_write_reg(0x09, LCD_WIDTH - 1); + lcd_write_reg(0x09, LCD_HEIGHT - 1); /* Start GRAM write */ lcd_cmd(0x22); @@ -110,6 +111,9 @@ static void lcd_v1_display_init(void) static void lcd_v1_enable (bool on) { + lcdctrl_bypass(1); + LCDC_CTRL |= RGB24B; + if (on) { lcd_write_reg(0x18, 0x44); @@ -139,23 +143,33 @@ static void lcd_v1_enable (bool on) lcd_write_reg(0x21, 0x00); } display_on = on; + + LCDC_CTRL &= ~RGB24B; +} +static void lcd_v1_set_gram_area(int x, int y, int width, int height) +{ + lcdctrl_bypass(1); + LCDC_CTRL |= RGB24B; + + lcd_write_reg(0x03, x); + lcd_write_reg(0x05, width-1); + lcd_write_reg(0x07, y); + lcd_write_reg(0x09, height-1); + + lcd_cmd(0x22); + LCDC_CTRL &= ~RGB24B; } static void lcd_v1_update_rect(int x, int y, int width, int height) { - int px = x, py = y; - int pxmax = x + width, pymax = y + height; - - lcd_write_reg(0x03, y); - lcd_write_reg(0x05, pymax-1); - lcd_write_reg(0x07, x); - lcd_write_reg(0x09, pxmax-1); - - lcd_cmd(0x22); - - for (px=x; px ) \___| < | \_\ ( <_> > < < * 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 +/* 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< ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id$ * * Copyright (C) 2011 Marcin Bukat * @@ -37,17 +36,6 @@ static inline void delay_nop(int cycles) } -/* converts RGB565 pixel into internal lcd bus format */ -static unsigned int lcd_pixel_transform(unsigned short rgb565) -{ - unsigned int r, g, b; - b = rgb565 & 0x1f; - g = (rgb565 >> 5) & 0x3f; - r = (rgb565 >> 11) & 0x1f; - - return r<<19 | g<<10 | b<<3; -} - /* not tested */ static void lcd_sleep(bool sleep) { @@ -71,7 +59,7 @@ static void lcd_sleep(bool sleep) lcd_cmd(GRAM_WRITE); } -static void lcd_display_init(void) +void lcd_display_init(void) { unsigned int x, y; @@ -173,31 +161,34 @@ static void lcd_display_init(void) lcd_sleep(false); } -void lcd_init_device(void) +void lcd_set_gram_area(int x, int y, int width, int height) { - lcdif_init(LCDIF_18BIT); - lcd_display_init(); -} - -void lcd_update_rect(int x, int y, int width, int height) -{ - int px = x, py = y; - int pxmax = x + width, pymax = y + height; + lcdctrl_bypass(1); + LCDC_CTRL |= RGB24B; /* addresses setup */ lcd_write_reg(WINDOW_H_START, y); - lcd_write_reg(WINDOW_H_END, pymax-1); + lcd_write_reg(WINDOW_H_END, height-1); lcd_write_reg(WINDOW_V_START, x); - lcd_write_reg(WINDOW_V_END, pxmax-1); + lcd_write_reg(WINDOW_V_END, width-1); lcd_write_reg(GRAM_H_ADDR, y); lcd_write_reg(GRAM_V_ADDR, x); lcd_cmd(GRAM_WRITE); + LCDC_CTRL &= ~RGB24B; +} + +void lcd_update_rect(int x, int y, int width, int height) +{ + int px = x, py = y; + int pxmax = x + width, pymax = y + height; + + lcd_set_gram_area(x, y, pxmax, pymax); for (py=y; py ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef LCD_TARGET_H +#define LCD_TARGET_H + +#define LCD_DATABUS_WIDTH LCDIF_18BIT + +#endif -- cgit v1.2.3