一.应用程序
在fb_fix_screeninfo中有
__u32 smem_len 是这个/dev/fb0的大小,也就是内存大小。
__u32 line_length 是屏幕上一行的点在内存中占有的空间,不是一行上的点数。
在fb_var_screeninfo 中有
__u32 xres ,__u32 yres 是x和y方向的分辨率,就是两个方向上的点数。
__u32 bits_per_pixel 是每一点占有的内存空间。
显存中存有两页屏幕数据,这是方便快速的改变屏幕内容实现动画之类比较高的要求。
一切东西都是文件。我们对屏幕的读写就可以转换成对/dev/fb0的读写。那么就把/dev/fb0
用open打开,再用lseek定位要读写的位置,最后调用read或者write来操作。通过这么一大
段的操作我们才完成了对一个点的读或者写。这种方法开销太大了。还有一种方法,我们把
/dev/fb0映射到程序进程的内存空间中来,然后得到一个指向这段存储空间的指针,这样就
可以方便的读写了。但是我们要知道能映射多少和该映射多少,这能很方便的从上面一个程
序得出的参数来决定。
例子程序:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
int main () {
int fp=0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long screensize=0;
char *fbp = 0;
int x = 0, y = 0;
long location = 0;
fp = open ("/dev/fb0",O_RDWR);
ioctl(fp,FBIOGET_FSCREENINFO,&finfo);
ioctl(fp,FBIOGET_VSCREENINFO,&vinfo);
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
/*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/
fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
/*这是你想画的点的位置坐标,(0,0)点在屏幕左上角*/
x = 100;
y = 100;
location = x * (vinfo.bits_per_pixel / 8) + y * finfo.line_length;
*(fbp + location) = 100; /* 蓝色的色深 */ /*直接赋值来改变屏幕上某点的颜色*/
*(fbp + location + 1) = 15; /* 绿色的色深*/
*(fbp + location + 2) = 200; /* 红色的色深*/
*(fbp + location + 3) = 0; /* 是否透明*/
munmap (fbp, screensize); /*解除映射*/
close (fp); /*关闭文件*/
return 0;
}
二.硬件原理
1.我们根据数据手册来描述一下这个集成在S3C2440内部的LCD控制器:
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;
b:REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的;
c:LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,
视频数据在不需要CPU的干预的情况下显示在LCD屏上;
d:VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,
然后通过数据端口VD[23:0]传送视频数据到LCD驱动器;
e:TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,
而这些控制信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能
产生这些信号的不同形态,从而支持不同的LCD驱动器(即不同的STN/TFT屏)
2.作为帧同步信号的vsync,每发出一个脉冲,都意味着新的一副图像数据开始传送。而作为行同步信号的hsync,
每发出一个脉冲,都表明新的一行图像资料开始发送。在帧同步和行同步的头尾都必须有回扫时间,这个原因
始于CRT的电子枪是需要回扫时间的,后来延续下来成工业标准,LCD的显示参数描述如下:
(1)定时参数,诸如行场起始,有效值,行场同步宽度等,均可参考LCD规格书获得。
(2)像素时钟,最简单的算法是:行像素之和 X 列像素之和 X 场频。反之,如果我们知道某LCD的时钟,如
28.37516MHZ,那么取其倒数,即得画一个像素点需要多少秒。
(3)颜色位域,这个是由显示缓冲区跟显示像素点的对应关系决定的。比如RGB565模式,RED占5位,偏移11位;
GREEN占6位,偏移5位;BLUE占5位,偏移0位。那么就把这些值赋给结构体fb_info->var->red.offset,
fb_info->var->red.length等。
(4)固定参数,比如根据LCD的显示参数计算帧缓冲设备分配的缓冲区大小,为:行最大显示解析度*列最大解析度*
每像素最大字节数。
三.裸机驱动
端口初始化----------
static void Lcd_Port_Init( void )
{
rGPCUP=0xffffffff; // Disable Pull-up register
rGPCCON=0xaaaa02a8; //1010 1010 1010 1010 0000 0010 1010 1000Initialize VD[7:0],VM,VFRAME,VLINE,VCLK
rGPDUP=0xffffffff; // Disable Pull-up register
rGPDCON=0xaaaaaaaa; //Initialize VD[15:8]
}
显示模式初始化----------------------------------------------
CLKVAL---
确定VCLK频率的参数,单位为HZ。
MMODE--
确定VM的改变速度。在此选择MMODE=0,为每帧变化模式
VM----通过VM信号改变控制像素点得显示或熄灭。
PNRMODE---
确定扫描方式。选择PNRMODE=0x3,为TFT LCD面板扫描模式。
BPPMODE--
确定BPP(每像素位数)模式。在此选择BPPMODE=0xc,为TFT16位模式。
ENVID----
数据输出和逻辑信号使能控制位。选择ENVID=1,为允许数据输出和逻辑控制
LCD_PIXCLOCK=4;
/*bit[17:8](4:CLKVAL);bit[6:5](11:TFT LCD panel);bit[4:1](1100:16 bpp for TFT)*/
rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 << 5) | (12 << 1);
0100 0000 0000
0000 0110 0000
0000 0001 1000
0100 0111 1000
static void Lcd_EnvidOnOff(int onoff)
{
if(onoff==1)
rLCDCON1|=1; // ENVID=ON
else
rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off
}
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_HEIGHT 320
#define LCD_HSYNC_LEN 5
/*bit[31:24](1:VBPD);bit[23:14](320-1:行数);bit[13:6](5:VFPD);bit[5:0](1:VSPW)*/
rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);
VBPD----表示一帧图像开始时,帧同步信号以后的无效的行数,对应驱动中的upper_margin
VFBD-----表示在一帧图像结束后,帧同步信号以前的无效的行数,对应驱动中的lower_margin
VSPW-----表示垂直同步脉冲的宽带,用行数计算,对应驱动中的vsync_len
LINEVAL----确定显示的垂直方向的尺寸
LINEVAL=YSIZE-1=479
若是480行,LINEVAL=479;
HBPD------表示从水平同步信号开始到一行有效数据开始之间的VCLK的个数,对应驱动中的left_margin
HFPD------表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK个数,对应
HOZVAL----确定显示的水平方向尺寸
公式HOZVAL=XSIZE-1
就是决定显示器有多少列。
/*bit[25:19](36:HBPD);bit[18:8](240-1:列数);bit[7:0](19:HFPD)*/
rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH - 1) << 8) | (LCD_LEFT_MARGIN << 0);
HSPW-----表示水平同步信号的宽带,用VCLK计算,对应驱动中的hsync_len
/*bit[15:8](13:MVAL,只有当LCDCON1 bit[7]MMODE=1才有效);bit[7:0](5:HSPW)*/
rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0);
/*bit[11](5:6:5 Format);bit[9](VLINE/HSYNC polarity inverted);bit[8](VFRAME/VSYNC inverted)
bit[3](Enalbe PWERN signal),bit[1](half-word swap control bit)*/
rLCDCON5 = (1<<11) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 0);
FRM565---------三基色模式选择
帧缓冲初始化----告诉lcd控制器,帧缓冲--显示缓存的位置。
记录了帧缓冲的起始地址。
/*帧缓冲地址初始化*/
/*
LCDBANK: 视频帧缓冲区内存地址30-22位
LCDBASEU: 视频帧缓冲区的开始地址21-1位
LCDBASEL: 视频帧缓冲区的结束地址21-1位
*/
/*bit[29:21]:LCDBANK,bit[20:0]:LCDBASEU*/
rLCDSADDR1 = ((LCD_ADDR >> 22) << 21) | ((M5D(LCD_ADDR >> 1)) << 0);
/*bit[20:0]:LCDBASEL*/
rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);
/*PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行字节数
OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0
*/
/*bit[21:11]:OFFSIZE; bit[10:0]:PAGEWIDTH*/
rLCDSADDR3 = LCD_WIDTH;
#define LCD_N35 //NEC 256K色240*320/3.5英寸TFT真彩液晶屏,每一条水平线上包含240个像素点,共有320条这样的线
#if defined(LCD_N35)
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 4
#define LCD_RIGHT_MARGIN 39
#define LCD_LEFT_MARGIN 16
#define LCD_HSYNC_LEN 5
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1
#endif
void TFT_LCD_Test(void);
#define LCD_XSIZE LCD_WIDTH
#define LCD_YSIZE LCD_HEIGHT
#define SCR_XSIZE LCD_WIDTH
#define SCR_YSIZE LCD_HEIGHT
volatile static unsigned short LCD_BUFFER[SCR_YSIZE][SCR_XSIZE]; //定义320行,240列的数组,用于存放显示数据
extern unsigned char sunflower_240x320[];
#define M5D(n) ((n)&0x1fffff)
#define LCD_ADDR ((U32)LCD_BUFFER)
#define ADC_FREQ 2500000
volatile U32 preScaler;
static void cal_cpu_bus_clk(void);
void Set_Clk(void);
/*演示函数*/
void delay(int times)
{
int i,j;
for(i=0;i<times;i++)
for(j=0;j<400;j++);
}
/*在屏幕上画图*/
static void Pait_Bmp(int x0,int y0,int h,int l,const unsigned char *bmp)
{
int x,y;
U32 c;
int p = 0;
for( y = 0 ; y < l ; y++ )
{
for( x = 0 ; x < h ; x++ )
{
c = bmp[p+1] | (bmp[p]<<8) ;
if ( ( (x0+x) < SCR_XSIZE) && ( (y0+y) < SCR_YSIZE) )
LCD_BUFFER[y0+y][x0+x] = c ;
p = p + 2 ;
}
}
}
/*填充全屏为某一颜色*/
static void Lcd_ClearScr( U16 c)
{
unsigned int x,y ;
for( y = 0 ; y < SCR_YSIZE ; y++ )
{
for( x = 0 ; x < SCR_XSIZE ; x++ )
{
LCD_BUFFER[y][x] = c ;
}
}
}
/*LCD开关*/
static void Lcd_EnvidOnOff(int onoff)
{
if(onoff==1)
rLCDCON1|=1; // ENVID=ON
else
rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off
}
/*端口初始化*/
static void Lcd_Port_Init( void )
{
rGPCUP=0xffffffff; // Disable Pull-up register
rGPCCON=0xaaaa02a8; //Initialize VD[7:0],VM,VFRAME,VLINE,VCLK
rGPDUP=0xffffffff; // Disable Pull-up register
rGPDCON=0xaaaaaaaa; //Initialize VD[15:8]
}
/*LCD初始化*/
static void LCD_Init(void)
{
Lcd_Port_Init();
/*显示模式初始化*/
/*bit[17:8](4:CLKVAL);bit[6:5](11:TFT LCD panel);bit[4:1](1100:16 bpp for TFT)*/
rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 << 5) | (12 << 1);
/*bit[31:24](1:VBPD);bit[23:14](320-1:行数);bit[13:6](5:VFPD);bit[5:0](1:VSPW)*/
rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT - 1) << 14) | (LCD_LOWER_MARGIN << 6) | (LCD_VSYNC_LEN << 0);
/*bit[25:19](36:HBPD);bit[18:8](240-1:列数);bit[7:0](19:HFPD)*/
rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH - 1) << 8) | (LCD_LEFT_MARGIN << 0);
/*bit[15:8](13:MVAL,只有当LCDCON1 bit[7]MMODE=1才有效);bit[7:0](5:HSPW)*/
rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0);
/*bit[11](5:6:5 Format);bit[9](VLINE/HSYNC polarity inverted);bit[8](VFRAME/VSYNC inverted)
bit[3](Enalbe PWERN signal),bit[1](half-word swap control bit)*/
rLCDCON5 = (1<<11) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 0);
/*帧缓冲地址初始化*/
/*
LCDBANK: 视频帧缓冲区内存地址30-22位
LCDBASEU: 视频帧缓冲区的开始地址21-1位
LCDBASEL: 视频帧缓冲区的结束地址21-1位
*/
/*bit[29:21]:LCDBANK,bit[20:0]:LCDBASEU*/
rLCDSADDR1 = ((LCD_ADDR >> 22) << 21) | ((M5D(LCD_ADDR >> 1)) << 0);
/*bit[20:0]:LCDBASEL*/
rLCDSADDR2 = M5D((LCD_ADDR + LCD_WIDTH * LCD_HEIGHT * 2) >> 1);
/*PAGEWIDTH:虚拟屏幕一行的字节数,如果不使用虚拟屏幕,设置为实际屏幕的行字节数
OFFSIZE:虚拟屏幕左侧偏移的字节数,如果不使用虚拟屏幕,设置为0
*/
/*bit[21:11]:OFFSIZE; bit[10:0]:PAGEWIDTH*/
rLCDSADDR3 = LCD_WIDTH;
/*屏蔽中断*/
rLCDINTMSK |= 3;
rTCONSEL &= (~7);
/*disable调色板*/
rTPAL = 0x0;
/*禁止LPC3600/LCC3600模式*/
rTCONSEL &= ~((1<<4) | 1);
/*打开LCD*/
Lcd_EnvidOnOff(1);
}
void TFT_LCD_Show(void)
{
/*红(255:0:0);绿(0:255:0);蓝(0:0:255);黑(0:0:0);白(255,255,255)*/
/*在屏幕上显示三基色*/
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x00) ) ; //clear screen black
delay(10000);
Lcd_ClearScr((0x1f<<11) | (0x00<<5) | (0x00)); //red
delay(10000);
Lcd_ClearScr((0x00<<11) | (0x3f<<5) | (0x00)); //green
delay(10000);
Lcd_ClearScr((0x00<<11) | (0x00<<5) | (0x1f)); //blue
delay(10000);
Lcd_ClearScr( (0x1f<<11) | (0x3f<<5) | (0x1f) ) ; //clear screen white
delay(10000);
/*显示一副图片在屏幕上*/
Pait_Bmp(0, 0, 240, 320, sunflower_240x320);
}
/* 设置CPU的时钟频率*/
void Set_Clk(void)
{
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ; //don't use 100M!
//boot_params.cpu_clk.val = 3;
switch ( i ) {
case 0: //200
key = 12;
mpll_val = (92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val = (67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
//init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //set the register--rMPLLCON
ChangeClockDivider(key, 12); //the result of rCLKDIVN [0:1:0:1] 3-0 bit
cal_cpu_bus_clk(); //HCLK=100M PCLK=50M
}
/*设置PCLK\HCLK\FCLK的频率*/
static void cal_cpu_bus_clk(void)
{
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = rMPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100; //FCLK=400M FIN=12000000
val = rCLKDIVN;
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
Uart_Printf("\nFCLK:%dMHz,HCLK:%dMHz,PCLK=%dMHZ\n",FCLK/1000/1000,HCLK/1000/1000,PCLK/1000/1000);
}
/*主函数*/
int Main(void)
{
Set_Clk();
LCD_Init();
TFT_LCD_Show();
return 0;
}
四.linux驱动
驱动主要跟fb_info结构体有关,该结构体记录了帧缓冲设备的全部信息,包括设备的设置参数、状态以及对底层
硬件操作的函数指针。
struct fb_info{
int node;
int flags;
struct fb_var_screeninfo var;/*LCD可变参数结构体*/
struct fb_fix_screeninfo fix;/*LCD固定参数结构体*/
struct fb_ops *fbops; /*对底层硬件操作的函数指针*/
......
};
fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数,比如屏幕的分辨率和每个像素的比特数等
struct fb_var_screeninfo{
__u32 xres; /*可见屏幕一行有多少个像素点*/
__u32 yres; /*可见屏幕一列有多少个像素点*/
__u32 xres_virtual; /*虚拟屏幕一行有多少个像素点*/
__u32 yres_virtual; /*虚拟屏幕一列有多少个像素点*/
__u32 xoffset; /*虚拟到可见屏幕之间的行偏移*/
__u32 yoffset; /*虚拟到可见屏幕之间的列偏移*/
__u32 bits_per_pixel; /*每个像素的位数即BPP*/
struct fb_bitfield red; /*fb缓存的R位域*/
struct fb_bitfield green; /*fb缓存的G位域*/
struct fb_bitfield blue; /*fb缓存的B位域*/
struct fb_bitfield transp; /*透明度*/
/*定时:除了pixclock本身外,其他的都以像素时钟为单位*/
__u32 pixclock; /*像素时钟(皮秒)*/
__u32 left_margin; /*行切换,从同步到绘图之间的延迟*/
__u32 right_margin; /*行切换,从绘图到同步之间的延迟*/
__u32 upper_margin; /*帧切换,从同步到绘图之间的延迟*/
__u32 lower_margin; /*帧切换,从绘图到同步之间的延迟*/
};
fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数,比如屏幕缓冲区的物理地址和长度等
struct fb_fix_screeninfo{
char id[16]; /*字符串形式的标示符 */
unsigned long smem_start; /*fb缓存的开始位置 */
__u32 smem_len; /*fb缓存的长度 */
__u32 type; /*看FB_TYPE_* */
__u32 type_aux; /*分界*/
__u32 visual; /*看FB_VISUAL_* */
__u16 xpanstep; /*如果没有硬件panning就赋值为0 */
__u16 ypanstep; /*如果没有硬件panning就赋值为0 */
__u16 ywrapstep; /*如果没有硬件ywrap就赋值为0 */
__u32 line_length; /*一行的字节数 */
unsigned long mmio_start; /*内存映射IO的开始位置*/
__u32 mmio_len; /*内存映射IO的长度*/
};
fb_ops结构体是对底层硬件操作的函数指针,该结构体中定义了对硬件的操作有:
struct fb_ops{
struct module *owner;
//检查可变参数并进行设置
int (*fb_check_var)(struct fb_var_screeninfo*var,struct fb_info *info);
//根据设置的值进行更新,使之有效
int (*fb_set_par)(struct fb_info*info);
//设置颜色寄存器
int (*fb_setcolreg)(unsigned regno,unsigned red,unsigned green,
unsigned blue,unsigned transp,struct fb_info*info);
//显示空白
int (*fb_blank)(int blank,struct fb_info *info);
//矩形填充
void (*fb_fillrect)(struct fb_info*info,const struct fb_fillrect*rect);
//复制数据
void (*fb_copyarea)(struct fb_info*info,const struct fb_copyarea*region);
//图形填充
void (*fb_imageblit)(struct fb_info*info,const struct fb_image*image);
};
2.platform
(1)平台参数
Linux把它看做是一个平台设备,故在内核代码/arch/arm/plat-s3c24xx/devs.c中定义有LCD相关的平台设备及资源:
/* LCD Controller */
//LCD控制器的资源信息
static struct resource s3c_lcd_resource[]={
[0] = {
.start = S3C24XX_PA_LCD,//控制器IO端口开始地址
.end= S3C24XX_PA_LCD+ S3C24XX_SZ_LCD- 1,//控制器IO端口结束地址
.flags= IORESOURCE_MEM,//标识为LCD控制器IO端口,在驱动中引用这个就表示引用IO端口
},
[1] = {
.start = IRQ_LCD,//LCD中断
.end= IRQ_LCD,
.flags = IORESOURCE_IRQ,//标识为LCD中断
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",//作为平台设备的LCD设备名
.id =-1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),//资源数量
.resource = s3c_lcd_resource,//引用上面定义的资源
.dev = {
.dma_mask =&s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_lcd);//导出定义的LCD平台设备,好在mach-smdk2440.c的smdk2440_devices[]中添加到平台设备列表中
Linux还在/arch/arm/mach-s3c2410/include/mach/fb.h中为LCD平台设备定义了一个s3c2410fb_mach_info结构体,该结构体主要是
记录LCD的硬件参数信息(比如该结构体的s3c2410fb_display成员结构中就用于记录LCD的屏幕尺寸、屏幕信息、可变的屏幕参数、
LCD配置寄存器等),这样在写驱动的时候就直接使用这个结构体。下面,我们来看一下内核是如果使用这个结构体的。在/arch/arm
/mach-s3c2440/mach-smdk2440.c中定义有:
Linux在arch/$(ARCH)/kernel/vmlinux.lds中定义了.init段。__init和__initdata属性的数据都在这个段中,当内核启动完毕后,
这个段中的内存会被释放掉供其他使用.
/* LCD driver info */
//LCD硬件的配置信息,注意这里我使用的LCD是NEC 3.5寸TFT屏,这些参数要根据具体的LCD屏进行设置
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata={
//这个地方的设置是配置LCD寄存器5,这些宏定义在regs-lcd.h中,计算后二进制为:111111111111,然后对照数据手册上LCDCON5的各位来看,注意是从右边开始
.lcdcon5 = S3C2410_LCDCON5_FRM565|
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,//TFT类型
/* NEC 3.5'' */
.width = 240,//屏幕宽度
.height = 320,//屏幕高度
//以下一些参数在上面的时序图分析中讲到过,各参数的值请跟据具体的LCD屏数据手册结合上面时序分析来设定
.pixclock = 100000,//像素时钟
.xres = 240,//水平可见的有效像素
.yres = 320,//垂直可见的有效像素
.bpp = 16,//色位模式
.left_margin = 19,//行切换,从同步到绘图之间的延迟
.right_margin = 36,//行切换,从绘图到同步之间的延迟
.hsync_len = 5,//水平同步的长度
.upper_margin = 1,//帧切换,从同步到绘图之间的延迟
.lower_margin = 5,//帧切换,从绘图到同步之间的延迟
.vsync_len = 1,//垂直同步的长度
};
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata={
.displays = &smdk2440_lcd_cfg,//应用上面定义的配置信息
.num_displays = 1,
.default_display = 0,
.gpccon = 0xaaaa555a,//将GPC0、GPC1配置成LEND和VCLK,将GPC8-15配置成VD0-7,其他配置成普通输出IO口
.gpccon_mask = 0xffffffff,
.gpcup = 0x0000ffff,//禁止GPIOC的上拉功能
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaaaaaaaa,//将GPD0-15配置成VD8-23
.gpdcon_mask = 0xffffffff,
.gpdup = 0x0000ffff,//禁止GPIOD的上拉功能
.gpdup_mask = 0xffffffff,
.lpcsel = 0x0,//这个是三星TFT屏的参数,这里不用
};
(2)平台初始化
//S3C2440初始化函数
static void __init smdk2440_machine_init(void)
{
//调用该函数将上面定义的LCD硬件信息保存到平台数据中
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info*pd)
{
struct s3c2410fb_mach_info *npd;
npd = kmalloc(sizeof(*npd), GFP_KERNEL);
if (npd){
memcpy(npd, pd,sizeof(*npd));
//这里就是将内核中定义的s3c2410fb_mach_info结构体数据保存到LCD平台数据中,所以在写驱动的时候就可以直接在平台数据中获取s3c2410fb_mach_info结构体的数据(即LCD各种参数信息)进行操作
s3c_device_lcd.dev.platform_data= npd;
} else {
printk(KERN_ERR "no memory for LCD platform data\n");
}
}