framebuffer学习


一.应用程序
在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");
    }
}

你可能感兴趣的:(framebuffer学习)