S3C2440,Linux,LCD驅(qū)動
到了神秘的LCD驅(qū)動了,信息還真有點膽怯,但是還是不得不走下去。對剛剛學(xué)習(xí)的linux驅(qū)動坐一下總結(jié),畢竟是Linux內(nèi)核當(dāng)中的東東,而且是那么的繁瑣。做一總結(jié),等用筆記把學(xué)過東西幾下來,這樣就不會忘了。哈哈!
那就開始?。?!
在編寫裸機LCD程序的時候,首先就是硬件初始化操作。有一個寄存器當(dāng)中存放了幀緩沖的起始地址。這個參數(shù)是非常重要的。當(dāng)配置好硬件后,幀緩沖中的數(shù)據(jù)能夠脫離CPU不停地將真緩沖當(dāng)中的數(shù)據(jù)寫入到LCD屏。如果我們要現(xiàn)實一個圖片的話只需要將圖片數(shù)據(jù)放到幀緩沖當(dāng)中,這樣就非常的方便了。
在linux當(dāng)中,把整個LCD驅(qū)動分為兩層:LCD幀緩沖區(qū)層和LCD硬件驅(qū)動層。LCD幀緩沖區(qū)層其實就是將內(nèi)核中的一部分空間當(dāng)作一個字符型設(shè)備,通過操作字符型設(shè)備的接口函數(shù)就可以操作這段幀緩沖區(qū)。而LCD硬件驅(qū)動層是對LCD硬件的初始化,LCD控制器在硬件驅(qū)動層被看作一個平臺設(shè)備。
LCD幀緩沖區(qū)層對應(yīng)的文件是fbmem.c,LCD硬件驅(qū)動層對應(yīng)的文件是S32440fb.c。
從最底層開始說吧,那當(dāng)然是LCD硬件驅(qū)動層了。
LCD硬件驅(qū)動層(S32440fb.c):
前面提到了LCD控制器在LCD硬件驅(qū)動層被當(dāng)看作是一個平臺設(shè)備,沒有定義字符設(shè)備所以不可能通過應(yīng)用層來訪問硬件。
既然是平臺設(shè)備,那么我們就按照平臺設(shè)備的執(zhí)行流程來分析。
首先來看一下,在S32440fb.c文件中定義的平臺設(shè)備驅(qū)動變量。如下:
staticstructplatform_drivers3c2410fb_driver={
.probe =s3c2410fb_probe,
.remove =s3c2410fb_remove,
.suspend =s3c2410fb_suspend,
.resume =s3c2410fb_resume,
.driver ={
.name ="s3c2410-lcd",
.owner =THIS_MODULE,
},
};
后面兩個是功耗操作函數(shù),而第一函數(shù)才是最重要的呀!因為平臺設(shè)備驅(qū)動在向平臺總線注冊時如果與掛接在該總線上的設(shè)備之一與該平臺設(shè)備驅(qū)動的匹配(文件名或者ID,通常時文件名),那么就會指向探測函數(shù)s3c2410fb_probe(為什么會執(zhí)行,在平臺設(shè)備總結(jié)文章中),探測函數(shù)只有一個語句即調(diào)用s3c24xxfb_probe函數(shù),這兩個函數(shù)的參數(shù)是一樣的
該參數(shù)是對應(yīng)的與之匹配的平臺設(shè)備結(jié)構(gòu)體,此設(shè)備在devs.c中定義,定義代碼如下
staticu64s3c_device_lcd_dmamask=0xffffffffUL;
structplatform_devices3c_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驅(qū)動所使用到的資源,源的定義如下:
staticstructresources3c_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寄存器時所用的寄存器的空間,第二個參數(shù)是在LCD硬件驅(qū)動中用到了LCD中斷,雖然定義了當(dāng)時沒有真正的用到。
進入s3c24xxfb_prob函數(shù),里面有一句代碼mach_info=pdev->dev.platform_data;platform_data雖然在定義的時候沒有賦值,但是在進入smdk2440_machine_init函數(shù)時,第一個語句調(diào)用了s3c24xx_fb_set_platdata函數(shù),此函數(shù)的作用就是將smdk2440_fb_info變量賦值給s3c_device_lcd.dev.platform_data(具體請看源碼),smdk2440_fb_info的定義如下:
staticstructs3c2410fb_mach_infosmdk2440_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,
};
接下來應(yīng)該注意一下 fbinfo->fbops =&s3c2410fb_ops;此語句代碼。Fbinfo的fbops指向了一個操作在LCD硬件驅(qū)動層的一些硬件操作函數(shù),然后再看ret=register_framebuffer(fbinfo); 這中間很多的語句就是對fbinfo中的成賦值,在此不具體分析。register_framebuffer函數(shù)不是在該文件中定義的,而是在LCD幀緩沖區(qū)層的fbmem.c當(dāng)中定義,是由有LCD硬件驅(qū)動層使用的。它是LCD硬件驅(qū)動層和LCD幀緩沖區(qū)層的信息交流的通道,這個函數(shù)將fbops指針變量傳給了LCD幀緩沖區(qū),那么LCD幀緩沖區(qū)就可以通過此指針來訪問LCD硬件驅(qū)動層中的操作函數(shù)了。稍后我們在LCD幀緩沖區(qū)層的實現(xiàn)代碼fbmem.c中詳細(xì)講解。
LCD幀緩沖區(qū)層(fbmem.c):
LCD幀緩沖區(qū)實際上就是把內(nèi)存當(dāng)中的一段幀緩沖區(qū)抽象成了以字符設(shè)備。既然是字符設(shè)備,那么在模塊初始化函數(shù)當(dāng)中就執(zhí)行注冊字符設(shè)備函數(shù),代碼如下:
staticint__init
fbmem_init(void)
{
proc_create("fb",0,NULL,&fb_proc_fops);
if(register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unabletogetmajor%dforfbdevsn",FB_MAJOR);
fb_class=class_create(THIS_MODULE,"graphics");
if(IS_ERR(fb_class)){
printk(KERN_WARNING"Unabletocreatefbclass;errno=%ldn",PTR_ERR(fb_class));
fb_class=NULL;
}
return0;
}
設(shè)備的名字為"fb",主設(shè)備號是固定的,在/linux/Major被宏定義為29,系統(tǒng)啟動后就已經(jīng)固定了。后面的代碼是用于創(chuàng)建結(jié)點的,在此不對其進行分析。
注冊設(shè)備就好了,當(dāng)然在字符設(shè)備當(dāng)中還有一個文件結(jié)構(gòu)體定義如下:
staticconststructfile_operationsfb_fops={
.owner= THIS_MODULE,
.read= fb_read,
.write= fb_write,
.unlocked_ioctl=fb_ioctl,
#ifdefCONFIG_COMPAT
.compat_ioctl=fb_compat_ioctl,
#endif
.mmap= fb_mmap,
.open= fb_open,
.release= fb_release,
#ifdefHAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area=get_fb_unmapped_area,
#endif
#ifdefCONFIG_FB_DEFERRED_IO
.fsync= fb_deferred_io_fsync,
#endif
};
在應(yīng)用程序中若相對幀緩沖操作,那么必須先打開對應(yīng)的結(jié)點,則對應(yīng)要執(zhí)行的函數(shù)當(dāng)然是fb_open了。那就從fb_open開始分析吧!
進入fb_open函數(shù),我們就就幾個核心代碼吧!
info=registered_fb[fbidx];這個語句是獲得代表幀緩沖區(qū)的信息(信息存放在結(jié)構(gòu)體當(dāng)中)只要有幀緩沖區(qū)注冊那么就會依次放到registered_fb這個數(shù)組(具體怎么被賦值到數(shù)組,稍后再注冊函數(shù)中分析),這樣就獲得了一個幀緩沖區(qū)的信息了。
然后再看這段代碼
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指針的文件操作函數(shù)來訪問LCD硬件驅(qū)動層的打開函數(shù)的吧!LCD幀緩沖驅(qū)動層的程序通過fbinfo中fops指針的變量來訪問LCD硬件驅(qū)動層的東東。到此打開函數(shù)就執(zhí)行完了。分析此函數(shù)的目的不僅是操作幀緩沖區(qū)必須先打開文件,還是為了顯示出LCD幀緩沖區(qū)層怎樣來訪問LCD硬件驅(qū)動層的。
好了,接下來分析LCD幀緩沖區(qū)層和LCD硬件驅(qū)動層溝通的橋梁函數(shù)
現(xiàn)在進入register_framebuffer函數(shù)吧!
registered_fb[i]=fb_info;
看到這句代碼就知道了上面提到registered_fb為什么會用到了吧!在registered_fb數(shù)組中存放著每一個注冊代表幀緩沖區(qū)信息的結(jié)構(gòu)體指針(structfb_info*)。也許你會有疑問那i的值是多少吶?那怎么就網(wǎng)上看,就看到了如下代碼:
for(i=0;i if(!registered_fb[i]) break; 對registered_fb指針數(shù)組遍歷,發(fā)現(xiàn)為空后就退出,然后注冊的幀緩沖區(qū)指針就存放到這里,那么就可以實現(xiàn)當(dāng)注冊多個時依次在registered_fb數(shù)組中存放了。 如果你要移植到一個開發(fā)板上,要改的東西也很少,linux的LCD驅(qū)動設(shè)計的時候就是可以方便移植的。若硬件差別不大,你只需要更改幾個結(jié)構(gòu)體初始化數(shù)據(jù),很方便的。 對于LCD的驅(qū)動就先講到這里吧!以為后來用。