http://blog.chinaunix.net/u1/49924/showart_506417.html 三、解剖s3c2410fb_driver變量s3c2410fb_driver變量有什么作用呢?在前面的2.2節(jié)提到了它的定義,從它的原型可以看出s3c2410fb_driver是個platform_driver類型的變量,前面的幾個小節(jié)提到了從platform_driver的名字可以看出它應該是platform_device的驅(qū)動類型。為了方便閱讀,這里再貼一次s3c2410fb_driver的定義: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, },};從定義可以看出,該platform_device的驅(qū)動函數(shù)有s3c2410fb_probe,s3c2410fb_remove,s3c2410fb_suspend和s3c2410fb_suspend。.resource成員前面的章節(jié)有說明,.driver成員的值相信不用再說明了吧,再明白不過了。前面的章節(jié),s3c2410fb_probe被比較詳細的介紹,這節(jié)中的主要任務就是解釋其他的幾個函數(shù)。在解釋他們之前,s3c2410fb_probe里面在該函數(shù)結(jié)尾的時候調(diào)用了幾個函數(shù)沒有說到,所以在這里補上。 3.1 s3c2410fb_probe余黨 在s3c2410fb_probe中最好調(diào)用了s3c2410fb_init_registers和s3c2410fb_check_var函數(shù),這里應該將他們交代清楚。很顯然,s3c2410fb_init_registers是初始化相關寄存器。那么后者呢?這里先把s3c2410fb_init_registers搞定再說。s3c2410fb_init_registers的定義與實現(xiàn)如下,先根據(jù)它的指向流程,一步一步解釋: static int s3c2410fb_init_registers(struct s3c2410fb_info *fbi){ unsigned long flags; /* Initialise LCD with values from haret */
local_irq_save(flags); /* 關閉中斷,在關閉中斷前,中斷的當前狀態(tài)被保存在flags中,對于關閉中斷的函數(shù),linux內(nèi)核有很多種,可以查閱相關的資料。*/ /* modify the gpio(s) with interrupts set (bjd) */
/*下面的modify_gpio函數(shù)是修改處理器GPIO的工作模式,它的實現(xiàn)很簡單,將第二個參數(shù)的值與第三個參數(shù)的反碼按位與操作后,在寫到第一個參數(shù)。這里的第一個參數(shù)實際就是硬件的GPIO控制器。*/ modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
local_irq_restore(flags); //使能中斷,并恢復以前的狀態(tài) /*下面的幾個writel函數(shù)開始初始化LCD控制寄存器,它的值就是我們在smdk2410_lcd_platdata(arch/arm/mach-s3c2410/mach-smdk2410.c)中regs域的值。*/ writel(fbi->regs.lcdcon1, S3C2410_LCDCON1); writel(fbi->regs.lcdcon2, S3C2410_LCDCON2); writel(fbi->regs.lcdcon3, S3C2410_LCDCON3); writel(fbi->regs.lcdcon4, S3C2410_LCDCON4); writel(fbi->regs.lcdcon5, S3C2410_LCDCON5); s3c2410fb_set_lcdaddr(fbi); /*該函數(shù)的主要作用是讓處理器的LCD控制器的三個地址寄存器指向正確的位置,這個位置就是LCD的緩沖區(qū),詳細的情況可以參見s3c2410的用戶手冊。*/…… /* Enable video by setting the ENVID bit to 1 這里打開video,在s3c2410fb_probe中被關閉了,這里打開*/
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID; writel(fbi->regs.lcdcon1, S3C2410_LCDCON1); return 0;}OK,s3c2410fb_init_registers就簡單介紹到這里。下面看看s3c2410fb_check_var函數(shù)要干些什么事,要說到這個函數(shù),還得提到fb_var_screeninfo結(jié)構類型,與它對應的是fb_fix_screeninfo結(jié)構類型。這兩個類型分別代表了顯示屏的屬性信息,這些信息可以分為可變屬性信息(如:顏色深度,分辨率等)和不可變的信息(如幀緩沖的其實地址)。既然fb_var_screeninfo表示了可變的屬下信息,那么這些可變信息就應該有一定范圍,否則顯示就會出問題,所以s3c2410fb_check_var函數(shù)的功能就是要在LCD的幀緩沖驅(qū)動開始運行之前將這些值初始到合法的范圍內(nèi)。知道了s3c2410fb_check_var要做什么,再去閱讀s3c2410fb_check_var函數(shù)的代碼就沒什么問題了。 3.2 s3c2410fb_remove 從這里開始將解釋s3c2410fb_driver中的其他幾個函數(shù)。那么就從s3c2410fb_remove開刀吧!顧名思義該函數(shù)就該知道,它要將這個platform設備從系統(tǒng)中移除,可以推測它應該釋放掉所有的資源,包括內(nèi)存空間,中斷線等等。還是按照慣例,在它的實現(xiàn)代碼中一步步的解釋。static int s3c2410fb_remove(struct platform_device *pdev){ struct fb_info *fbinfo = platform_get_drvdata(pdev); /*該函數(shù)從platform_device中,到fb_info信息*/
struct s3c2410fb_info *info = fbinfo->par; //得到私有數(shù)據(jù) int irq; s3c2410fb_stop_lcd(info); //該函數(shù)停止LCD控制器,實現(xiàn)可以在s3c2410fb.c中找到 msleep(1); //休息以下,等待LCD停止 s3c2410fb_unmap_video_memory(info); //該函數(shù)釋放緩沖區(qū) if (info->clk) { //停止時鐘
clk_disable(info->clk); clk_put(info->clk); info->clk = NULL;
} irq = platform_get_irq(pdev, 0); //得到中斷線,以便釋放 free_irq(irq,info); //釋放該中斷 release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD); /* 釋放內(nèi)存空間 */ unregister_framebuffer(fbinfo); //向內(nèi)核注銷該幀緩沖 return 0;} 3.3 s3c2410fb_suspend與s3c2410fb_resume 在實際的設備,常?梢钥吹絃CD在不需要的時候進入休眠狀態(tài),當需要使用的時候又開始工作,比如手機,在不需要的時候LCD就熄滅,當需要使用的時候LCD又被點亮。從實際中可以看出這對函數(shù)非常重要。雖然他們很重要,但不一定很復雜,下面看看它們是怎么樣實現(xiàn)的。 static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)
{ struct fb_info *fbinfo = platform_get_drvdata(dev); //這兩條語句好面熟^_^
struct s3c2410fb_info *info = fbinfo->par; s3c2410fb_stop_lcd(info); //停止LCD /* sleep before disabling the clock, we need to ensure
* the LCD DMA engine is not going to get back on the bus
* before the clock goes off again (bjd) */
msleep(1); //等待一下,因為LCD停止需要一點時間 clk_disable(info->clk); //關閉LCD的時鐘 return 0;}^_^,下面的代碼就不用解釋了吧!static int s3c2410fb_resume(struct platform_device *dev){ struct fb_info *fbinfo = platform_get_drvdata(dev);
struct s3c2410fb_info *info = fbinfo->par; clk_enable(info->clk); msleep(1); s3c2410fb_init_registers(info); return 0;} OK,到現(xiàn)在為止,對于platform device的相關驅(qū)動就over了。不過精彩的還在后頭哦!To be continued……------ anmnmnly
------ 2008.03.24