LCD turns on and draws.

Some basic progress on the LCD, such that the interface can
turn-on and draw.

The implementation uses a slow sequential-write method to update
the display; the vestiges of an auto-copy system are also in
place but not yet debugged.

The system seems to support two methods for issuing commands and
data to the LCD. One is a simple write-to-commit method. The
other is a more sophisticated command list that's set up, and
then dispatched. The write-to-commit kind of works now, although
it's left to be seen if we aren't running too far ahead of
commands or missing some wait states.

To get the demo to work, run the following commands in fernly:

bl 5  (turns on backlight)
lcd su   (sets up GPIOs and timing)
lcd init (initializes the LCD-side controller)
lcd tp1  (blasts a small test pattern to the top of display)

You'll note that the VCOM setting looks to be off (alternate
light/dark lines).

Next steps include:

1) verify that commands are being issued correctly to the LCD
(no missing commands due to writes happening faster than
the interface can keep up)

2) cross-check with LCD datasheet for the initialization
of the controller -- there are some undocumented commands
I left out in this implementation, maybe they need to go in
there; I also left out the gamma table.

3) get the autocopy method to work. I think, from what I can
tell, the autocopy will take a region of interest from memory,
composite it with up to four other regions with alpha blending,
and then auto-copy this to the LCD. You can insert commands
into the auto-copy stream as necessary, but I believe this,
combined with the TE (tearing effect) sync bit, will allow
you to just write to a region of memory as a frame buffer
and have it show up on the display without any CPU intervention.

Could be wrong tho....
This commit is contained in:
bunnie 2014-09-17 02:44:34 +08:00
parent 0893cc4584
commit 8f13d99dd4
3 changed files with 299 additions and 22 deletions

254
cmd-lcd.c
View file

@ -4,6 +4,8 @@
#include "printf.h"
#include "serial.h"
#include "fernvale-lcd.h"
#include "fernvale-gpio.h"
#include "fernvale-clockgate.h"
// local implementation of strncmp() because we don't have it as a lib in fernly
static int my_strncmp(const char* s1, const char* s2, size_t n)
@ -17,10 +19,115 @@ static int my_strncmp(const char* s1, const char* s2, size_t n)
return 0;
}
static void usleep(uint32_t usecs) {
uint32_t i, j;
for (i = 0; i < usecs; i++) {
for (j = 0; j < 73; j++) {
asm("nop");
}
}
}
static void msleep(uint32_t msecs) {
uint32_t i, j;
for (i = 0; i < msecs; i++) {
for (j = 0; j < 73000; j++) {
asm("nop");
}
}
}
#define USE_DMA 0
#if USE_DMA // needs debuggin'
void flush_dma( uint8_t q ) {
while(*((volatile uint16_t *) LCD_STATUS_REG) & LCD_STATUS_BUSY_BIT)
;
*((volatile uint16_t *) LCD_INT_ENA_REG) &= ~LCD_INT_ENA_TRIG_BIT;
*((volatile uint32_t *) LCD_MEMMAP_CTRL_REG) &= 0x0FFFFFFF; // disable all layers
*((volatile uint32_t *) LCD_MEMMAP_CTRL_REG) &= 0xFFFFE0FF; // clear command count
*((volatile uint32_t *) LCD_MEMMAP_CTRL_REG) |= (q << 8); // enter command count
*((volatile uint32_t *) LCD_MEMMAP_CTRL_REG) |= 0x8000; // enable commands to be sent first
*((volatile uint32_t *)LCD_MEMMAP_SIZE_REG) = 0x0;
*((volatile uint16_t *)LCD_RUN_REG) = 0;
*((volatile uint16_t *)LCD_RUN_REG) = LCD_RUN_BIT;
usleep(1);
while(*((volatile uint16_t *) LCD_STATUS_REG) & LCD_STATUS_BUSY_BIT)
;
*((volatile uint32_t *)LCD_MEMMAP_SIZE_REG) = 0x014000F0;
}
void lcd_cmd( uint16_t cmd ) {
*((volatile uint32_t *)LCD_CMD_LIST_ADDR) = 0x800000 | cmd;
flush_dma(1);
}
void lcd_dat( uint16_t dat ) {
*((volatile uint32_t *)LCD_CMD_LIST_ADDR) = dat;
flush_dma(1);
}
// note these don't flush the DMA, you have to do this yourself explicitly later on
void lcd_cmd_slot( uint16_t cmd, uint8_t slot ) { // slot is the order to execute
*((volatile uint32_t *) (LCD_CMD_LIST_ADDR + (slot << 2))) = 0x800000 | cmd;
}
void lcd_dat_slot( uint16_t dat, uint8_t slot ) { // slot is the order to execute
*((volatile uint32_t *) (LCD_CMD_LIST_ADDR + (slot << 2))) = dat;
}
#else
#define lcd_cmd(_cmd_) *((volatile uint16_t *)LCD_PAR0_CMD_PORT_REG) = (uint16_t) _cmd_
#define lcd_dat(_dat_) *((volatile uint16_t *)LCD_PAR0_DAT_PORT_REG) = (uint16_t) _dat_
#endif
void setup_lcd_gpio() {
// LPCE0, LPTE0, LPRSTB
*((volatile uint32_t *) GPIO_CTRL_MODE5) &= ~( GPIO_CTRL_MODE5_IO40_MASK |
GPIO_CTRL_MODE5_IO46_MASK |
GPIO_CTRL_MODE5_IO45_MASK );
*((volatile uint32_t *) GPIO_CTRL_MODE5) |= ( GPIO_CTRL_MODE5_IO40_LPCE0B |
GPIO_CTRL_MODE5_IO45_LPTE0 |
GPIO_CTRL_MODE5_IO46_LPRSTB );
// NLD0-4, LWRB, LRDB, LPA0
*((volatile uint32_t *) GPIO_CTRL_MODE4) &= ~( GPIO_CTRL_MODE4_IO32_MASK |
GPIO_CTRL_MODE4_IO33_MASK |
GPIO_CTRL_MODE4_IO34_MASK |
GPIO_CTRL_MODE4_IO35_MASK |
GPIO_CTRL_MODE4_IO36_MASK |
GPIO_CTRL_MODE4_IO37_MASK |
GPIO_CTRL_MODE4_IO38_MASK |
GPIO_CTRL_MODE4_IO39_MASK );
*((volatile uint32_t *) GPIO_CTRL_MODE4) |= ( GPIO_CTRL_MODE4_IO32_NLD4 |
GPIO_CTRL_MODE4_IO33_NLD3 |
GPIO_CTRL_MODE4_IO34_NLD2 |
GPIO_CTRL_MODE4_IO35_NLD1 |
GPIO_CTRL_MODE4_IO36_NLD0 |
GPIO_CTRL_MODE4_IO37_LWRB |
GPIO_CTRL_MODE4_IO38_LRDB |
GPIO_CTRL_MODE4_IO39_LPA0 );
// NLD5-8
*((volatile uint32_t *) GPIO_CTRL_MODE3) &= ~( GPIO_CTRL_MODE3_IO28_MASK |
GPIO_CTRL_MODE3_IO29_MASK |
GPIO_CTRL_MODE3_IO30_MASK |
GPIO_CTRL_MODE3_IO31_MASK );
*((volatile uint32_t *) GPIO_CTRL_MODE3) |= ( GPIO_CTRL_MODE3_IO28_NLD8 |
GPIO_CTRL_MODE3_IO29_NLD7 |
GPIO_CTRL_MODE3_IO30_NLD6 |
GPIO_CTRL_MODE3_IO31_NLD5 );
}
int cmd_lcd(int argc, char **argv)
{
uint32_t genarg = 0;
char *subcmd;
int i;
if (argc < 1) {
printf("Usage: lcd [subcmd] [arg0]\n");
@ -35,25 +142,150 @@ int cmd_lcd(int argc, char **argv)
printf( "command got: %s\n", subcmd );
if( my_strncmp( subcmd, "su", 8 ) == 0 ) {
setup_lcd_gpio();
*((volatile uint32_t *) CLKGATE_SYS_CTL0_CLR) = CLKGATE_CTL0_LCD; // power up the LCD block
msleep(1);
// execute setup command
printf( "nothing for now.\n" );
// we're on CS0
// our internal bus period is 166 MHz, or 6.25ns
// write cycle = 66ns = 11 cycles - 1 = 10
// write c22write su (tcs) = 15ns = 3 cycles
// write ce2write hold (tdht) = 10ns = 2 cycles - 1 = 1
// read latency = 450 ns = 72 cycles, crop at 63 cycles
// read ce2read su (trdl - trcs) = 45-45 = 0 ns = 0 cycles
// read th = 90ns = 15 cycles (not quite clear, but best guess)
*((volatile uint32_t *)LCD_PAR0_CFG_REG) =
(10 << LCD_PAR_CFG_WR_WAIT_CYC_BIT) |
(3 << LCD_PAR_CFG_WR_TSU_BIT) |
(1 << LCD_PAR_CFG_WR_TH_BIT) |
(63 << LCD_PAR_CFG_RD_LATENCY_CYC_BIT ) | // this might need to be shorter??
(0 << LCD_PAR_CFG_RD_TSU_BIT) |
(15 << LCD_PAR_CFG_RD_TH_BIT);
// 9 bit width, tchw is 0 for this chipset (back2back writes allowed)
*((volatile uint32_t *)LCD_PAR_DATA_WIDTH_REG) =
(0 << LCD_PAR_W2W_WAIT0_BIT) |
(LCD_PAR_BUS_WIDTH_9BIT << LCD_PAR_BUS_WIDTH0_BIT);
*((volatile uint32_t *)LCD_AUTOCOPY_CTRL_REG) = 0; // AUTOCOPY off initially
} else if( my_strncmp( subcmd, "auto", 8 ) == 0 ) {
*((volatile uint32_t *)LCD_LAYER0_CTRL_REG) = 0;
*((volatile uint32_t *)LCD_LAYER0_OFFSET_REG) = 0;
*((volatile uint32_t *)LCD_LAYER0_BUFF_ADDR_REG) = 0;
*((volatile uint32_t *)LCD_LAYER0_SIZE_REG) = 0x014000f0;
*((volatile uint32_t *)LCD_LAYER0_MEM_OFFSET_REG) = 0x0;
*((volatile uint32_t *)LCD_LAYER0_MEM_PITCH_REG) = 0x1e0;
*((volatile uint32_t *)LCD_AUTOCOPY_OFFSET_REG) = 0;
*((volatile uint16_t *)LCD_AUTOCOPY_CMD_ADDR_REG) = 0x4000;
*((volatile uint16_t *)LCD_AUTOCOPY_DATA_ADDR_REG) = 0x4100;
*((volatile uint32_t *)LCD_AUTOCOPY_SIZE_REG) = 0x014000F0;
*((volatile uint32_t *)LCD_AUTOCOPY_BG_COLOR_REG) = 0x80008000;
*((volatile uint32_t *)LCD_AUTOCOPY_CTRL_REG) = 0x85020094;
} else if( my_strncmp( subcmd, "dump", 8 ) == 0 ) {
// dump registers
printf( "LCD_PAR0_CMD_PORT_REG: %08x\n", *((volatile uint32_t *) LCD_PAR0_CMD_PORT_REG) );
printf( "LCD_PAR0_DAT_PORT_REG: %08x\n", *((volatile uint32_t *) LCD_PAR0_DAT_PORT_REG) );
printf( "LCD_PAR1_CMD_PORT_REG: %08x\n", *((volatile uint32_t *) LCD_PAR1_CMD_PORT_REG) );
printf( "LCD_PAR1_DAT_PORT_REG: %08x\n", *((volatile uint32_t *) LCD_PAR1_DAT_PORT_REG) );
printf( "LCD_PAR0_CMD_PORT_REG: %04x\n", *((volatile uint16_t *) LCD_PAR0_CMD_PORT_REG) );
printf( "LCD_PAR0_DAT_PORT_REG: %04x\n", *((volatile uint16_t *) LCD_PAR0_DAT_PORT_REG) );
printf( "LCD_PAR1_CMD_PORT_REG: %04x\n", *((volatile uint16_t *) LCD_PAR1_CMD_PORT_REG) );
printf( "LCD_PAR1_DAT_PORT_REG: %04x\n", *((volatile uint16_t *) LCD_PAR1_DAT_PORT_REG) );
printf( "LCD_PAR0_CFG_REG: %08x\n", *((volatile uint32_t *) LCD_PAR0_CFG_REG) );
printf( "LCD_PAR1_CFG_REG: %08x\n", *((volatile uint32_t *) LCD_PAR1_CFG_REG) );
printf( "LCD_STATUS_REG: %08x\n", *((volatile uint32_t *) LCD_STATUS_REG) );
printf( "LCD_INT_ENA_REG: %08x\n", *((volatile uint32_t *) LCD_INT_ENA_REG) );
printf( "LCD_INT_STAT_REG: %08x\n", *((volatile uint32_t *) LCD_INT_STAT_REG) );
printf( "LCD_RUN_REG: %08x\n", *((volatile uint32_t *) LCD_RUN_REG) );
printf( "LCD_RESET_REG: %08x\n", *((volatile uint32_t *) LCD_RESET_REG) );
printf( "LCD_STATUS_REG: %04x\n", *((volatile uint16_t *) LCD_STATUS_REG) );
printf( "LCD_INT_ENA_REG: %04x\n", *((volatile uint16_t *) LCD_INT_ENA_REG) );
printf( "LCD_INT_STAT_REG: %04x\n", *((volatile uint16_t *) LCD_INT_STAT_REG) );
printf( "LCD_RUN_REG: %04x\n", *((volatile uint16_t *) LCD_RUN_REG) );
printf( "LCD_RESET_REG: %04x\n", *((volatile uint16_t *) LCD_RESET_REG) );
printf( "LCD_PAR_DATA_WIDTH_REG: %08x\n", *((volatile uint32_t *) LCD_PAR_DATA_WIDTH_REG) );
printf( "LCD_TEARING_CON_REG: %08x\n", *((volatile uint32_t *) LCD_TEARING_CON_REG) );
printf( "LCD_AUTOCOPY_CTRL_REG: %08x\n", *((volatile uint32_t *) LCD_AUTOCOPY_CTRL_REG) );
} else if( my_strncmp( subcmd, "run", 8 ) == 0 ) {
// cause the interface to run
*((volatile uint16_t *)LCD_RUN_REG) = 0;
*((volatile uint16_t *)LCD_RUN_REG) = LCD_RUN_BIT;
} else if( my_strncmp( subcmd, "stop", 8 ) == 0 ) {
// cause the interface to stop
*((volatile uint16_t *)LCD_RUN_REG) = 0;
} else if( my_strncmp( subcmd, "init", 8 ) == 0 ) {
*((volatile uint16_t *)LCD_RESET_REG) = 1;
usleep(20000);
*((volatile uint16_t *)LCD_RESET_REG) = 0; // turn on reset
msleep(20);
*((volatile uint16_t *)LCD_RESET_REG) = 1; // turn off reset
msleep(150);
lcd_cmd(0x11); //Exit Sleep
msleep(50); // Delay 50ms
lcd_cmd(0xC0); //Power control
lcd_dat(0x26);
lcd_cmd(0xC1); //Power control
lcd_dat(0x11); //SAP[2:0];BT[3:0]
lcd_cmd(0xC5); //VCM control
lcd_dat(0x35);
lcd_dat(0x3E);
lcd_cmd(0xc7);
lcd_dat(0xbe);
lcd_cmd(0x36); // Memory Access Control
lcd_dat(0x48);
lcd_cmd(0x3a); // pixel format set
lcd_dat(0x55); // 16bpp
lcd_cmd(0xB1); // Frame Rate Control
lcd_dat(0x00);
lcd_dat(0x1b);
//--------------ddram ---------------------
lcd_cmd(0x2a); // column set
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0xEF);
lcd_cmd(0x2b); // page address set
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0x01);
lcd_dat(0x3F);
lcd_cmd(0x34); // tearing effect off
//lcd_cmd(0x35); // tearing effect on
//lcd_cmd(0xb4); // display inversion
//lcd_dat(0x00,0x00);
lcd_cmd(0xb7); //entry mode set
lcd_dat(0x07);
//-----------------display---------------------
lcd_cmd(0xb6); // display function control
lcd_dat(0x0a);
lcd_dat(0x82);
lcd_dat(0x27);
lcd_dat(0x00);
lcd_cmd(0x11); //sleep out
msleep(100);
lcd_cmd(0x29); // display on
msleep(100);
lcd_cmd(0x2c); //memory write
} else if( my_strncmp( subcmd, "tp1", 8 ) == 0 ) {
lcd_cmd(0x2a); // column set
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0xEF);
lcd_cmd(0x2b); // page address set
lcd_dat(0x00);
lcd_dat(0x00);
lcd_dat(0x01);
lcd_dat(0x3F);
lcd_cmd(0x2c); //memory write
for( i = 0; i < 16384; i++ ) {
lcd_dat((uint16_t) i);
}
} else {
printf( "lcd commands: su (setup timing), dump (dump registers)\n" );
printf( "lcd commands: su (setup timing), dump (dump registers), run, stop, init, tp1\n" );
}
return 0;

View file

@ -6,8 +6,8 @@
// control set 0
#define CLKGATE_SYS_CTL0 (0xA0010000 + 0x0300)
#define CLKGATE_SYS_CTL0_SET (0xA0010000 + 0x0310)
#define CLKGATE_SYS_CTL0_CLR (0xA0010000 + 0x0320)
#define CLKGATE_SYS_CTL0_SET (0xA0010000 + 0x0310) // turns the block off
#define CLKGATE_SYS_CTL0_CLR (0xA0010000 + 0x0320) // turns the block on
#define CLKGATE_CTL0_LCD 0x0001
#define CLKGATE_CTL0_RESIZER 0x0002

View file

@ -26,7 +26,7 @@
#define LCD_PAR_CFG_RD_TH_MASK (0xF0000000)
#define LCD_PAR_CFG_RD_TH_BIT (28)
#define LCD_STATUS_REG (LCD_CTRL_ADDR + 0x0000)
#define LCD_STATUS_REG (LCD_CTRL_ADDR + 0x0000) // 16-bit
#define LCD_STATUS_RUN_BIT (0x1)
#define LCD_STATUS_WAIT_CMDQ_BIT (0x2)
#define LCD_STATUS_WAIT_HTT_BIT (0x8)
@ -34,7 +34,7 @@
#define LCD_STATUS_BUSY_BIT (0x20)
#define LCD_STATUS_GMC_REQ_BIT (0x40)
#define LCD_INT_ENA_REG (LCD_CTRL_ADDR + 0x0004)
#define LCD_INT_ENA_REG (LCD_CTRL_ADDR + 0x0004) // 16-bit
#define LCD_INT_ENA_TRIG_BIT (0x1)
#define LCD_INT_ENA_REG_TRIG_BIT (0x2)
#define LCD_INT_ENA_CMD_TRIG_BIT (0x4)
@ -42,13 +42,13 @@
#define LCD_INT_ENA_HSYNC_TRIG_BIT (0x20)
#define LCD_INT_ENA_VSYNC_TRIG_BIT (0x20)
#define LCD_INT_STAT_REG (LCD_CTRL_ADDR + 0x0008)
#define LCD_INT_STAT_REG (LCD_CTRL_ADDR + 0x0008) // 16-bit
#define LCD_INT_STAT_DONE_BIT (0x1)
#define LCD_RUN_REG (LCD_CTRL_ADDR + 0x000C)
#define LCD_RUN_REG (LCD_CTRL_ADDR + 0x000C) // 16-bit
#define LCD_RUN_BIT (0x8000)
#define LCD_RESET_REG (LCD_CTRL_ADDR + 0x0010)
#define LCD_RESET_REG (LCD_CTRL_ADDR + 0x0010) // 16-bit
#define LCD_RESET_BIT (0x1) // check polarity
#define LCD_PAR_DATA_WIDTH_REG (LCD_CTRL_ADDR + 0x003C)
@ -56,10 +56,10 @@
#define LCD_PAR_BUS_WIDTH0_BIT (0)
#define LCD_PAR_BUS_WIDTH1_MASK (0x70)
#define LCD_PAR_BUS_WIDTH1_BIT (4)
#define LCD_PAR_WAIT0_MASK (0xF0000)
#define LCD_PAR_WAIT0_BIT (16)
#define LCD_PAR_WAIT1_MASK (0xF00000)
#define LCD_PAR_WAIT1_BIT (20)
#define LCD_PAR_W2W_WAIT0_MASK (0xF0000) // write 2 write delay
#define LCD_PAR_W2W_WAIT0_BIT (16)
#define LCD_PAR_W2W_WAIT1_MASK (0xF00000)
#define LCD_PAR_W2W_WAIT1_BIT (20)
#define LCD_PAR_BUS_WIDTH_8BIT (0) // valid values for LCD_PAR_BUS_WIDTHn
#define LCD_PAR_BUS_WIDTH_9BIT (1)
#define LCD_PAR_BUS_WIDTH_16BIT (2)
@ -71,4 +71,49 @@
#define LCD_TEARING_SYNCEDGE_BIT (0x2)
#define LCD_TEARING_SW_FORCE_BIT (0x8000)
#define LCD_AUTOCOPY_CTRL_REG (LCD_CTRL_ADDR+0x0080)
// should be:
// 0 - BGR
// 0 - significance (?)
// 1 - padding on MSBs
// 010 - RGB565 (100 for RGB888, 011 for RGB66)
// 10 - 9-bit interface
// 000000 - command (no commands now)
// 0 -- disable W2M
// 0 -- enable commands
// 00000010 -- 2 cycle waiting period (may be ok to set 0 but for now set to 2)
// 1 -- send residue mode, residue per frame
// 0
// 1 -- enable frame update counter. why not?
// 0
// 1000 -- enable layer 0 only
// 1000 0101 0000 0010 1000 0000 1001 0100 = 0x85028094
#define LCD_AUTOCOPY_CTRL_SEND_RES_MOD_MASK 0x01000000
#define LCD_AUTOCOPY_CTRL_PERIOD_MASK 0x00FF0000
#define LCD_AUTOCOPY_CTRL_LAYER_MASK 0xF0000000
#define LCD_AUTOCOPY_CTRL_CMD_ENABLE_BIT 0x00008000
#define LCD_AUTOCOPY_CTRL_DATA_FORMAT_MASK 0x020000FF ///24 bit interface support
#define LCD_AUTOCOPY_CTRL_CMDQ_SEL_BIT 0
#define LCD_AUTOCOPY_CTRL_CMD_NUMBER_MASK 0x00003F00
#define LCD_AUTOCOPY_OFFSET_REG (LCD_CTRL_ADDR+0x0084) // set to 0, uint 32
#define LCD_AUTOCOPY_CMD_ADDR_REG (LCD_CTRL_ADDR+0x0088) // set to 0x4000, uint 16
#define LCD_AUTOCOPY_DATA_ADDR_REG (LCD_CTRL_ADDR+0x008C) // set to 0x4100, uint 16
#define LCD_AUTOCOPY_SIZE_REG (LCD_CTRL_ADDR+0x0090) // set to 0x014000F0, uint32 (320x240)
#define LCD_AUTOCOPY_BG_COLOR_REG (LCD_CTRL_ADDR+0x009C) // set to 0x80008000 (slight greenish)
#define LCD_LAYER0_CTRL_REG (LCD_CTRL_ADDR+0x00B0) // set to 0x0
#define LCD_LAYER0_SRC_KEY_REG (LCD_CTRL_ADDR+0x00B4)
#define LCD_LAYER0_OFFSET_REG (LCD_CTRL_ADDR+0x00B8) // set to 0x0
#define LCD_LAYER0_BUFF_ADDR_REG (LCD_CTRL_ADDR+0x00BC) // set to 0x0
#define LCD_LAYER0_SIZE_REG (LCD_CTRL_ADDR+0x00C0) // set to 0x014000f0
#define LCD_LAYER0_MEM_OFFSET_REG (LCD_CTRL_ADDR+0x00C8) // set to 0x0
#define LCD_LAYER0_MEM_PITCH_REG (LCD_CTRL_ADDR+0x00CC) // set to 0x1e0
// there's 3 more layers, but I'm happy to just get one working for now...
#define LCD_CMD_LIST_ADDR (LCD_CTRL_ADDR + 0x0C00)
#endif /* __FV_LCD_H__ */