![]() |
|
LCD Configuration of uClinux 2.4.x for 44B0X
by Tong Lai Yu, Felix Lo, John Jiang, and Ken Liu August 2005
LCD pin mapping ( for the Brightek 44b0x board )
| LCD & Controller Wintek VM-X1216VE-6CL LCD and Novatek NT7523 Controller | map to 44b0 ( IDE ) | IDE pin # | 44bb0 board pin | |||
|---|---|---|---|---|---|---|
| 1 | S/P | L | ||||
| 2 | S8/16 | L | ||||
| 3 | DB0 | DATA0 | 17 | |||
| 4 | DB1 | DATA1 | 15 | |||
| 5 | DB2 | DATA2 | 13 | |||
| 6 | DB3 | DATA3 | 11 | |||
| 7 | DB4 | DATA4 | 9 | |||
| 8 | DB5 | DATA5 | 7 | |||
| 9 | DB6 | DATA6 | 5 | |||
| 10 | DB7 | DATA7 | 3 | |||
| 11 | DB8 | DATA8 | 4 | |||
| 12 | DB9 | DATA9 | 6 | |||
| 13 | DB10 | DATA10 | 8 | |||
| 14 | DB11 | DATA11 | 10 | |||
| 15 | DB12 | DATA12 | 12 | |||
| 16 | DB13 | DATA13 | 13 | |||
| 17 | DB14 | DATA14 | 16 | |||
| 18 | DB15 | DATA15 | 18 | |||
| 19 | VDD | VDD | 39 | |||
| 20 | VSS | GND | 40 | |||
| 21 | CSB | nDATA_CS | -- | 19 | ||
| 22 | RESET | nRESET | 1 | |||
| 23 | A0 | ADDR0 | -- | 4 | ||
| 24 | E/WRB(SCL) | nWE | 21 | |||
| 25 | R/W(RDB) | nOE | 27 | |||
| 26 | NC | NC | ||||
| 27 | LED_A | NC | ||||
| 28 | LED_B | NC | ||||
| 29 | C86 | H | ||||
| 30 | NC | NC | ||||
Later, the connection was modified: A0 of LCD ( pin 23 ) was connected to Addr2 of 44B0X. Also, LED_A and LED_B ( pin 27, 28 ) of LCD have to be connected to 12.3V and GND respectively in order that the panel will be lit up.
We are using the 16-bit parallel mode of the LCD Controller. We must also set the 44B0X to 16-bit mode. The 44B0X schematics shows that we should use address bank 0x040000000 - 0x06000000 ( nGS2 ). The following piece of ASM code will accomplish these:
AREA asm1, CODE, READONLY ENTRY START LDR R0, =0x01c80000 ;44B0X port to set mode LDR R1, =0x11111290 ;44B0X 16-bit mode STR R1, [R0] ;set to 16-bit mode LDR R2, =0x04000004 ;44B0 port nGS2 write ( Adr2 to A0 of LCDC ) LDR R0, =0x04000000 ;corresponding read port ...... |
We then need to do the initialization of the LCDC ( NT7532, p.52, p.68 and Wintek p.10 ) as follows:
..... LDR R1, =0x0002 ;start of initialization, Wintek p.10 STR R1, [R0] LDR R1, =0x0400 STR R1, [R2] LDR R1, =0x0000 STR R1, [R0] LDR R1, =0X7523 STR R1, [R2] LDR R1, =0x0001 STR R1, [R0] LDR R1, =0X0213 STR R1, [R2] LDR R1, =0x0005 STR R1, [R0] LDR R1, =0X0010 STR R1, [R2] LDR R1, =0x000B STR R1, [R0] LDR R1, =0X0000 STR R1, [R2] LDR R1, =0x0016 STR R1, [R0] LDR R1, =0X7F00 STR R1, [R2] LDR R1, =0x0017 STR R1, [R0] LDR R1, =0X9F00 STR R1, [R2] LDR R1, =0x0020 STR R1, [R0] LDR R1, =0X0000 STR R1, [R2] LDR R1, =0x0003 STR R1, [R0] LDR R1, =0X8C08 STR R1, [R2] LDR R1, =0x000c STR R1, [R0] LDR R1, =0X0000 STR R1, [R2] LDR R1, =0x0004 STR R1, [R0] LDR R1, =0X014B STR R1, [R2] LDR R1, =0x0007 STR R1, [R0] LDR R1, =0X000B STR R1, [R2] LDR R1, =0X0000 STR R1, [R2] LDR R1, =0X000B STR R1, [R2] |
We can now read-write to the LCD ( Novatek p.68 )
LDR r1, =0x0000 ;set IR ( index register, 7 bits ) to 0 STR r1, [r0] ;write to LCD port ldr r3, [r2] ;read from LCD port, we should get 0x7532 ldr r3, [r2] ldr r3, [r2] ldr r3, [r2] ;write data to LCD LDR r1, =0x0021 str r1, [r0] ;points IR to 0x21, i.e. use R21 ldr r1, =0x0100 str r1, [r0] BL write_data_to_lcd LDR r1, =0x0021 str r1, [r0] ldr r1, =0x0200 str r1, [r0] BL write_data_to_lcd LDR r1, =0x0021 str r1, [r0] ldr r1, =0x0300 str r1, [r0] BL write_data_to_lcd LDR r1, =0x0021 str r1, [r0] ldr r1, =0x0400 str r1, [r0] BL write_data_to_lcd b . write_data_to_lcd LDR r1, =0x0022 str r1, [r0] mov r3, #0 lcd_loop ldr r1, =0x3f3f str r1, [r2] ADD r3, r3, #1 CMP r3, #0x83 blt lcd_loop MOV pc, lr END |
A crucial trick in bringing this to work to to first read the LCD status. Make sure that you get the value 0x7523 before you proceed further. If you get something like 0x2323, most likely your 44B0X mode has not been set to 16-bit.
The corresponding C-code is :
//lcd.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#define ADDR_INDEX 0x04000000
#define ADDR_DATA 0x04000004
#define ADDR_ARM_BOARD_DATA_BITS 0x01c80000
#define VALUE_ARM_BOARD_DATA_BITS_32 0x11110292
#define VALUE_ARM_BOARD_DATA_BITS_16 0x11110092
#define OUTL(addr,data) *(volatile unsigned long int *)(addr)=(data)
#define OUTW(addr,data) *(volatile unsigned short int*)(addr)=(data)
#define OUT_INDEX(data) OUTW(ADDR_INDEX,data)
#define OUT_DATA(data) OUTW(ADDR_DATA,data)
#define INW(addr) *(volatile unsigned short int *)(addr)
#define INL(addr) *(volatile unsigned long int *)(addr)
//define s3c44b0 bank num
#define BANK0 0
#define BANK1 1
#define BANK2 2
#define BANK3 3
#define BANK4 4
#define BANK5 5
#define BANK6 6
#define BANK7 7
//define s3c44b0 memory data bus width
#define BIT8 8
#define BIT16 16
#define BIT32 32
const int major = 29;
const char *name = "fb0";
unsigned long int s3c44b0_mem_ctrl;
int lcd_open(struct inode* inode,struct file* file);
int lcd_close(struct inode* inode,struct file* file);
int lcd_read(struct inode * inode,struct file* file,char* buf,int count);
int lcd_write(struct inode* inode,struct file* file,const char* buf,int count);
char buf[240][320];
struct file_operations lcd_fb =
{
.read = lcd_read,
.write = lcd_write,
.open = lcd_open,
.release = lcd_close
};
inline void get_s3c44b0_mem_ctrl()
{
s3c44b0_mem_ctrl = INL(0x01C80000);
}
int set_s3c44b0_bus_width(unsigned char bank,unsigned char bits)
{
unsigned long int new_mem_ctrl;
switch(bank)
{
case BANK1:
break;
case BANK2:
new_mem_ctrl = s3c44b0_mem_ctrl & (~(0x1 << 8));
new_mem_ctrl = new_mem_ctrl & (~(0x1 << 9));
if(bits == BIT8)
break;
else if(bits == BIT16)
new_mem_ctrl |= (0x01 << 8);
else if(bits == BIT32)
new_mem_ctrl |= (0x01 << 9);
else
return -1;
break;
case BANK3:
case BANK4:
case BANK5:
case BANK6:
case BANK7:
break;
default:
printk("<1>john:s3c44b0 bank error\n");
return -1;
}
printk("<1>s3c44b0 new memory control value is:%x\n",new_mem_ctrl);
OUTL(ADDR_ARM_BOARD_DATA_BITS,new_mem_ctrl);
return 0;
};
inline void set_s3c44b0_bus_width_to_raw()
{
OUTL(ADDR_ARM_BOARD_DATA_BITS,s3c44b0_mem_ctrl);
}
void lcd_init()
{
DECLARE_WAIT_QUEUE_HEAD(wait_on_interrupt);
get_s3c44b0_mem_ctrl();
printk("<1>john:old s3c44bo mem ctrl is:%x\n",s3c44b0_mem_ctrl);
OUT_INDEX(0x02);
OUT_DATA(0x0400);
OUT_INDEX(0x00);
OUT_DATA(0x7523);
interruptible_sleep_on_timeout(&wait_on_interrupt,1);
OUT_INDEX(0x00);
printk("<1>john:read start oscillation:%x\n",INW(0x04000004));
OUT_INDEX(0x01);
OUT_DATA(0x0213);
OUT_INDEX(0x05);
OUT_DATA(0x0010);
OUT_INDEX(0x0B);
OUT_DATA(0x0000);
OUT_INDEX(0x16);
OUT_DATA(0x8300);
OUT_INDEX(0x17);
OUT_DATA(0xAF00);
OUT_INDEX(0x20);
OUT_DATA(0x0000);
OUT_INDEX(0x03);
OUT_DATA(0x8C08);
OUT_INDEX(0x0C);
OUT_DATA(0x0000);
OUT_INDEX(0x04);
OUT_DATA(0x014B);
OUT_INDEX(0x07);
OUT_DATA(0x0003);
}
static int __init my_init(void)
{
lcd_init();
register_chrdev(major,name,&lcd_fb);
return 0;
}
static void __exit my_cleanup(void)
{
if(!MOD_IN_USE)
unregister_chrdev(major,name);
}
int lcd_open(struct inode* inode,struct file* file)
{
MOD_INC_USE_COUNT;
return 0;
}
int lcd_close(struct inode* inode,struct file* file)
{
MOD_DEC_USE_COUNT;
return 0;
}
int lcd_read(struct inode * inode,struct file* file,char* buf,int count)
{
return 0;
}
int lcd_write(struct inode* inode,struct file* file,const char* buf,int count)
{
int row_cur,col_cur;
unsigned short tmp;
int i = 0;
row_cur = 0x4F;
while(row_cur<0x7F)
{
col_cur = 0x10;
OUTL(0x01c80000,0x11110292); //set s3c44b0 to 32 bits
OUTW(0x04000000,0x21);
OUTW(0x04000002,(col_cur<<8)|row_cur);
OUTW(0x04000000,0x22);
while(col_cur<0x48F)
{
OUTW(0x04000002,0x0000);
col_cur++;
}
row_cur++;
}
row_cur = 0x4F;
while(row_cur<0x7F)
{
col_cur = 0x10;
OUTL(0x01c80000,0x11110292); //set s3c44b0 to 32 bits
OUTW(0x04000000,0x21);
OUTW(0x04000002,(col_cur<<8)|row_cur);
OUTW(0x04000000,0x22);
while(col_cur<0x48F)
{
OUTW(0x04000002,0x0000);
col_cur++;
}
row_cur++;
}
row_cur = 0x4F;
col_cur = 0x10;
while(row_cur < 0x7F)
{
col_cur = 0x6F;
while(col_cur < 0x8F)
{
i = (col_cur<<8 | row_cur);
OUTW(0x04000000,0x21);
OUTW(0x04000002,i);
OUTW(0x04000000,0x22);
if((tmp = INW(0x04000002)) != 0 && tmp != 0x4b4b)
{
printk("<1>row=%x col=%x pixel=%x\n",row_cur,col_cur,tmp);
}
col_cur++;
}
row_cur++;
}
return 0;
}
module_init(my_init);
module_exit(my_cleanup);
EXPORT_NO_SYMBOLS;
|
Notes:
software flow:
NT7523 LCD Framebuffer Driver for S3C44B0X
Hints and NotesIt is very important that the driver should set the memory address bus of the S3C44B0X to 32bits. Otherwise, data access will have error.
The screen size of the LCD is 176 x 132. The LCD's DRAM address (0,0) has mapped with pixel address (0,0), so it is more easy to understand.
The framebuffer driver should reserve a (176x132x16 / 8) bytes memory for framebuffer. The interface of the LCD controller doesn't support DMA, so we need to reserve memory for buffer from the SDRAM. Moreover, the LCD controller doesn't support memory map so we need to update display by a special call.
The framebuffer has a method call nt7523_redraw. It is used to copy the content from board's SDRAM to LCD's DRAM. The programmer can use the framebuffer's ioctl call.
Eg.
//nt7523.h
#define FBIO_SCREEN_UPDATE 0x8000
int fbfd = 0;
char * param;
fbfd = open("/dev/fb0", O_RDWR);
if (!fbfd) {
printf("Error: cannot open framebuffer device.\n");
} else {
ioctl(fbfd, FBIO_SCREEN_UPDATE, ¶m);
}
close(fbfd);
|
Because of this special call, the framebuffer program doesn't know that it needs to call this frame update method. Therefore, I have written a simple program which loop infinitely to update the screen. You can run it as a background proccess by
If the programmer has written a high level GUI program, he can call this method without running the background process when the program has finished painting.
UtilityI have written a tool to control the register inside the LCD's control. The usage is as following
The tool has another function that it can test the color of the LCD display. Please terminate the looping process because it will overwrite the color at the LCD's DRAM.
.
.
extern int s3c44b0xfb_init(void);
extern int s3c44b0xfb_setup(char*);
extern int nt7523fb_init(void); //Add this
extern int nt7523fb_setup(char*); //Add this
.
.
#ifdef CONFIG_FB_S3C44B0X
{ "s3c44b0xfb", s3c44b0xfb_init, s3c44b0xfb_setup },
#endif
//Add following lines
#ifdef CONFIG_FB_NT7523
{ "nt7523fb", nt7523fb_init, nt7523fb_setup },
#endif
.
.
|
. . #ifndef NT7523FB_H #define NT7523FB_H . . #define CMAP_SIZE 30 #define SCR_XSIZE 176 #define SCR_YSIZE 132 #define LCD_XSIZE 176 #define LCD_YSIZE 132 #define LCD_DEPTH 16 #define LCD_BUF_SIZE ((SCR_XSIZE*SCR_YSIZE*2)) /* 2bytes per pixel*/ #define FBIO_SCREEN_UPDATE 0x8000 #define ADDR_INDEX 0x04000000 #define ADDR_DATA 0x04000004 #define OUTL(addr,data) *(volatile unsigned long int *)(addr)=(data) #define OUTW(addr,data) *(volatile unsigned short int*)(addr)=(data) #define OUT_INDEX(data) OUTW(ADDR_INDEX,data) #define OUT_DATA(data) OUTW(ADDR_DATA,data) #define IN_DATA() *(volatile unsigned short int *)(ADDR_DATA) #define INW(addr) *(volatile unsigned short int *)(addr) #define INL(addr) *(volatile unsigned long int *)(addr) |
. . #ifdef MODULE module_init(nt7523fb_init); module_exit(nt7523fb_exit); #endif . . |
inline void setup_s3c44b0x_mem_ctrl()
{
unsigned int data = inl(S3C44B0X_BWSCON);
outl( (data&0xFFFFFCFF)|S3C44B0X_BWSCON_DW2_32, S3C44B0X_BWSCON);
}
|
static int
s3c44b0fb_set_var(struct fb_var_screeninfo *var, int con,
struct fb_info *info)
{
.
.
var->transp.msb_right = 0;
var->transp.offset = 0;
var->transp.length = 0;
var->red.msb_right = 0;
var->red.offset = 11;
var->red.length = 5;
var->green.msb_right = 0;
var->green.offset = 5;
var->green.length = 6;
var->blue.msb_right = 0;
var->blue.offset = 0;
var->blue.length = 5;
switch (var->bits_per_pixel) {
#ifdef FBCON_HAS_CFB16
case 16:
cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;//FB_VISUAL_TRUECOLOR;
display->dispsw = &fbcon_cfb16;
display->dispsw_data = NULL;
break;
#endif
default:
return -EINVAL;
}
|
static int nt7523_redraw() {
//Full screen redraw
int count=0;
int SIZE = LCD_XSIZE * LCD_YSIZE;
unsigned short int *buf = (unsigned short int *)(cfb->fb.screen_base);
OUT_INDEX(0x21);
OUT_DATA(0x0);
OUT_INDEX(0x22);
while(count |
static int nt7523fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
u_long arg, int con, struct fb_info *info)
{
.
.
switch (cmd){
case FBIO_SCREEN_UPDATE:
nt7523_redraw();
break;
.
.
}
|
static struct fb_ops s3c44b0fb_ops = {
owner: THIS_MODULE,
fb_set_var: s3c44b0fb_set_var,
fb_set_cmap: s3c44b0fb_set_cmap,
fb_get_fix: gen_get_fix,
fb_get_var: gen_get_var,
fb_get_cmap: gen_get_cmap,
fb_ioctl: nt7523fb_ioctl,
};
|
int __init nt7523fb_init(void)
{
.
.
.
cfb->fb.disp = (struct display *)(cfb + 1);
fb_alloc_cmap(&cfb->fb.cmap, CMAP_SIZE, 0);
/*
* Power up the LCD
*/
setup_s3c44b0x_mem_ctrl();
//Start Oscilation
OUT_INDEX(0x0000);
OUT_DATA(0x7523);
//interruptible_sleep_on_timeout(&wait_on_interrupt,1);
mdelay(15); //10ms
//LCD Driving Waveform control
OUT_INDEX(0x0002);
OUT_DATA(0x0400);
mdelay(1);
OUT_INDEX(0x00);
printk("<1>john:read start oscillation:%x\n",INW(ADDR_DATA));
//Driver Output Control
OUT_INDEX(0x0001);
OUT_DATA(0x0215);
//Entry Mode
OUT_INDEX(0x0005);
OUT_DATA(0x0028);
//Frame Cycle Control
OUT_INDEX(0x000B);
OUT_DATA(0x0000);
//Horizontal RAM Address Position
OUT_INDEX(0x0016);
OUT_DATA(0x8300);
//Vertical RAM Address Position
OUT_INDEX(0x0017);
OUT_DATA(0xAF00); //Don't change it
//RAM Write Data Mask
OUT_INDEX(0x0020);
OUT_DATA(0x0000);
//Power Control 1
OUT_INDEX(0x0003);
OUT_DATA(0x8c08);
//Power Control 2
OUT_INDEX(0x000c);
OUT_DATA(0x0000);
//Contrast Control
OUT_INDEX(0x0004);
OUT_DATA(0x0168);
//Display Control
OUT_INDEX(0x0007);
OUT_DATA(0x0003);
|