LCD驅動學習總結
到了神祕的LCD驅動了,信息還真有點膽怯,但是還是不得不走下去。對剛剛學習的linux驅動坐一下總結,畢竟是Linux內核當中的東東,而且是那麼的繁瑣。做一總結,等用筆記把學過東西幾下來,這樣就不會忘了。哈哈!
那就開始!!!
在編寫裸機LCD程序的時候,首先就是硬件初始化操作。有一個寄存器當中存放了幀緩衝的起始地址。這個參數是非常重要的。當配置好硬件後,幀緩衝中的數據能夠脫離CPU不停地將真緩衝當中的數據寫入到LCD屏。如果我們要現實一個圖片的話只需要將圖片數據放到幀緩衝當中,這樣就非常的方便了。
在linux當中,把整個LCD驅動分爲兩層:LCD幀緩衝區層和LCD硬件驅動層。LCD幀緩衝區層其實就是將內核中的一部分空間當作一個字符型設備,通過操作字符型設備的接口函數就可以操作這段幀緩衝區。而LCD硬件驅動層是對LCD硬件的初始化,LCD控制器在硬件驅動層被看作一個平臺設備。
LCD幀緩衝區層對應的文件是fbmem.c ,LCD硬件驅動層對應的文件是S32440fb.c 。
從最底層開始說吧,那當然是LCD硬件驅動層了。
LCD硬件驅動層(S32440fb.c):
前面提到了LCD控制器在LCD硬件驅動層被當看作是一個平臺設備,沒有定義字符設備所以不可能通過應用層來訪問硬件。
既然是平臺設備,那麼我們就按照平臺設備的執行流程來分析。
首先來看一下,在S32440fb.c文件中定義的平臺設備驅動變量。如下:
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2410-lcd",
.owner = THIS_MODULE,
},
};
後面兩個是功耗操作函數,而第一函數纔是最重要的呀!因爲平臺設備驅動在向平臺總線註冊時如果與掛接在該總線上的設備之一與該平臺設備驅動的匹配(文件名或者ID,通常時文件名),那麼就會指向探測函數s3c2410fb_probe(爲什麼會執行,在平臺設備總結文章中),探測函數只有一個語句即調用s3c24xxfb_probe函數,這兩個函數的參數是一樣的
該參數是對應的與之匹配的平臺設備結構體,此設備在devs.c中定義,定義代碼如下
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-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
}
};
s3c_lcd_resource是LCD驅動所使用到的資源,源的定義如下:
static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD,
.end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};
第一個元素爲配置LCD寄存器時所用的寄存器的空間,第二個參數是在LCD硬件驅動中用到了LCD中斷,雖然定義了當時沒有真正的用到。
進入s3c24xxfb_prob函數,裏面有一句代碼mach_info = pdev->dev.platform_data;platform_data雖然在定義的時候沒有賦值,但是在進入smdk2440_machine_init函數時,第一個語句調用了s3c24xx_fb_set_platdata函數,此函數的作用就是將smdk2440_fb_info變量賦值給s3c_device_lcd.dev.platform_data(具體請看源碼),smdk2440_fb_info的定義如下:
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
.displays = &smdk2440_lcd_cfg,
.num_displays = 1,
.default_display = 0,
.gpccon = 0xaaaaaaaa,
.gpccon_mask = 0xffffffff,
.gpcup = 0x0000ffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0xffffffff,
.gpdup = 0x0000ffff,
.gpdup_mask = 0xffffffff,
.lpcsel = 0x00,
};
接下來應該注意一下 fbinfo->fbops = &s3c2410fb_ops;此語句代碼。Fbinfo的fbops指向了一個操作在LCD硬件驅動層的一些硬件操作函數,然後再看 ret = register_framebuffer(fbinfo); 這中間很多的語句就是對fbinfo中的成賦值,在此不具體分析。register_framebuffer函數不是在該文件中定義的,而是在LCD幀緩衝區層的fbmem.c當中定義,是由有LCD硬件驅動層使用的。它是LCD硬件驅動層和LCD幀緩衝區層的信息交流的通道,這個函數將fbops指針變量傳給了LCD幀緩衝區,那麼LCD幀緩衝區就可以通過此指針來訪問LCD硬件驅動層中的操作函數了。稍後我們在LCD幀緩衝區層的實現代碼fbmem.c中詳細講解。
LCD幀緩衝區層(fbmem.c):
LCD幀緩衝區實際上就是把內存當中的一段幀緩衝區抽象成了以字符設備。既然是字符設備,那麼在模塊初始化函數當中就執行註冊字符設備函數,代碼如下:
static int __init
fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops);
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics");
if (IS_ERR(fb_class)) {
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
fb_class = NULL;
}
return 0;
}
設備的名字爲"fb",主設備號是固定的,在/linux/Major被宏定義爲29 ,系統啓動後就已經固定了。後面的代碼是用於創建結點的,在此不對其進行分析。
註冊設備就好了,當然在字符設備當中還有一個文件結構體定義如下:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
};
在應用程序中若相對幀緩衝操作,那麼必須先打開對應的結點,則對應要執行的函數當然是fb_open了。那就從fb_open開始分析吧!
進入fb_open函數,我們就就幾個核心代碼吧!
info = registered_fb[fbidx];這個語句是獲得代表幀緩衝區的信息(信息存放在結構體當中)只要有幀緩衝區註冊那麼就會依次放到registered_fb這個數組(具體怎麼被賦值到數組,稍後再註冊函數中分析),這樣就獲得了一個幀緩衝區的信息了。
然後再看這段代碼
file->private_data = info;
if (info->fbops->fb_open) {
res = info->fbops->fb_open(info,1);
if (res)
module_put(info->fbops->owner);
}
看到了其中的res = info->fbops->fb_open(info,1);語句了嗎?就是通過fbinfo指針的文件操作函數來訪問LCD硬件驅動層的打開函數的吧!LCD幀緩衝驅動層的程序通過fbinfo中fops指針的變量來訪問LCD硬件驅動層的東東。到此打開函數就執行完了。分析此函數的目的不僅是操作幀緩衝區必須先打開文件,還是爲了顯示出LCD幀緩衝區層怎樣來訪問LCD硬件驅動層的。
好了,接下來分析LCD幀緩衝區層和LCD硬件驅動層溝通的橋樑函數
現在進入register_framebuffer函數吧!
registered_fb[i] = fb_info;
看到這句代碼就知道了上面提到registered_fb爲什麼會用到了吧!在registered_fb數組中存放着每一個註冊代表幀緩衝區信息的結構體指針(struct fb_info *)。也許你會有疑問那i的值是多少吶?那怎麼就網上看,就看到了如下代碼:
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
對registered_fb指針數組遍歷,發現爲空後就退出,然後註冊的幀緩衝區指針就存放到這裏,那麼就可以實現當註冊多個時依次在registered_fb數組中存放了。
如果你要移植到一個開發板上,要改的東西也很少,linux的LCD驅動設計的時候就是可以方便移植的。若硬件差別不大,你只需要更改幾個結構體初始化數據,很方便的。
對於LCD的驅動就先講到這裏吧!以爲後來用。