linux驅動入門之LCD驅動
硬體環境:tq2440 4.3 LCD
軟體環境:Fedora17 arm-linux-gcc-4.3.2
核心版本:2.6.39
以韋東山老師視訊為基礎,加入自己的實踐
1.基礎知識(轉載,原作者不詳,感謝先):
1. LCD工作的硬體需求: 要使一塊LCD正常的顯示文字或影象,不僅需要LCD驅動器,而且還需要相應的LCD控制器。在通常情況下,生產廠商把LCD驅動器會以COF/COG的形式與LCD玻璃基板製作在一起,而LCD控制器則是由外部的電路來實現,現在很多的MCU內部都集成了LCD控制器,如S3C2410/2440等。通過LCD控制器就可以產生LCD驅動器所需要的控制訊號來控制STN/TFT屏了。 2. S3C2440內部LCD控制器結構圖:
|
|
|
|
三、幀緩衝(FrameBuffer)裝置驅動結構: 幀緩衝裝置為標準的字元型裝置,在Linux中主裝置號29,定義在/include/linux/major.h中的FB_MAJOR,次裝置號定義幀緩衝的個數,最大允許有32個FrameBuffer,定義在/include/linux/fb.h中的FB_MAX,對應於檔案系統下/dev/fb%d裝置檔案。
1. 幀緩衝裝置驅動在Linux子系統中的結構如下:
我們從上面這幅圖看,幀緩衝裝置在Linux中也可以看做是一個完整的子系統,大體由fbmem.c和xxxfb.c組成。向上給應用程式提供完善的裝置檔案操作介面(即對FrameBuffer裝置進行read、write、ioctl等操作),介面在Linux提供的fbmem.c檔案中實現;向下提供了硬體操作的介面,只是這些介面Linux並沒有提供實現,因為這要根據具體的LCD控制器硬體進行設定,所以這就是我們要做的事情了(即xxxfb.c部分的實現)。
2. 幀緩衝相關的重要資料結構:
從幀緩衝裝置驅動程式結構看,該驅動主要跟fb_info結構體有關,該結構體記錄了幀緩衝裝置的全部資訊,包括裝置的設定引數、狀態以及對底層硬體操作的函式指標。在Linux中,每一個幀緩衝裝置都必須對應一個fb_info,fb_info在/linux/fb.h中的定義如下:(只列出重要的一些)
|
其中,比較重要的成員有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和structfb_ops *fbops,他們也都是結構體。下面我們一個一個的來看。
fb_var_screeninfo結構體主要記錄使用者可以修改的控制器的引數,比如螢幕的解析度和每個畫素的位元數等,該結構體定義如下:
|
而fb_fix_screeninfo結構體又主要記錄使用者不可以修改的控制器的引數,比如螢幕緩衝區的實體地址和長度等,該結構體的定義如下:
|
fb_ops結構體是對底層硬體操作的函式指標,該結構體中定義了對硬體的操作有:(這裡只列出了常用的操作)
|
3. 幀緩衝裝置作為平臺裝置(本文程式碼中沒有用到):
在S3C2440中,LCD控制器被整合在晶片的內部作為一個相對獨立的單元,所以Linux把它看做是一個平臺裝置,故在核心程式碼/arch/arm/plat-s3c24xx/devs.c中定義有LCD相關的平臺裝置及資源,程式碼如下:
|
除此之外,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 LCD驅動時,這些引數是怎麼設定的了
注意:可能有很多朋友不知道上面紅色部分的引數是做什麼的,其值又是怎麼設定的?其實它是跟你的開發板LCD控制器密切相關的,看了下面兩幅圖相信就大概知道他們是幹什麼用的:
上面第一幅圖是開發板原理圖的LCD控制器部分,第二幅圖是S3c2440資料手冊中IO埠C和IO埠D控制器部分。原理圖中使用了GPC8-15和GPD0-15來用做LCD控制器VD0-VD23的資料埠,又分別使用GPC0、GPC1埠用做LCD控制器的LEND和VCLK訊號,對於GPC2-7則是用做STN屏或者三星專業TFT屏的相關訊號。然而,S3C2440的各個IO口並不是單一的功能,都是複用埠,要使用他們首先要對他們進行配置。所以上面紅色部分的引數就是把GPC和GPD的部分埠配置成LCD控制功能模式。
從以上講述的內容來看,要使LCD控制器支援其他的LCD屏,重要的是根據LCD的資料手冊修改以上這些引數的值。下面,我們再看一下在驅動中是如果引用到s3c2410fb_mach_info結構體的(注意上面講的是在核心中如何使用的)。在mach-smdk2440.c中有:
|
s3c24xx_fb_set_platdata定義在plat-s3c24xx/devs.c中:
|
這裡再講一個小知識:不知大家有沒有留意,在平臺裝置驅動中,platform_data可以儲存各自平臺裝置例項的資料,但這些資料的型別都是不同的,為什麼都可以儲存?這就要看看platform_data的定義,定義在/linux/device.h中,void *platform_data是一個void型別的指標,在Linux中void可儲存任何資料型別。
2.程式碼:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>
static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info);
struct lcd_regs {
unsigned long lcdcon1;
unsigned long lcdcon2;
unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;
unsigned long lcdsaddr1;
unsigned long lcdsaddr2;
unsigned long lcdsaddr3;
unsigned long redlut;
unsigned long greenlut;
unsigned long bluelut;
unsigned long reserved[9];
unsigned long dithmode;
unsigned long tpal;
unsigned long lcdintpnd;
unsigned long lcdsrcpnd;
unsigned long lcdintmsk;
unsigned long lpcsel;
};
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = s3c_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,/* linux核心自帶的驅動,需要載入,後面使用的時候會講到 */
};
static struct fb_info *s3c_lcd;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static