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");
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章