mini2440驅動分析之LCD



[日期:2011-10-04] 來源:Linux社區  作者:yaozhenguo2006  

二. 模塊初始化
  1.s3c2410fb.c是內核的一個模塊,在模塊初始化函數中只是簡單的調用了platform_driver_register把自己註冊成爲platform驅動。初始化函數如下:
  1. int __init s3c2410fb_init(void)  
  2. {  
  3.     int ret = platform_driver_register(&s3c2410fb_driver);  
  4.   
  5.     if (ret == 0)  
  6.         ret = platform_driver_register(&s3c2412fb_driver);  
  7.   
  8.     return ret;  
  9. }  
platform_driver_register是platform類型驅動的註冊函數,他傳入一個platform_driver結構體。mini2440lcd驅動初始化了這樣一個結構體。如下:
  1. static struct platform_driver s3c2410fb_driver = {  
  2.     .probe      = s3c2410fb_probe,  
  3.     .remove     = s3c2410fb_remove,  
  4.     .suspend    = s3c2410fb_suspend,  
  5.     .resume     = s3c2410fb_resume,  
  6.     .driver     = {  
  7.         .name   = "s3c2410-lcd",  
  8.         .owner  = THIS_MODULE,  
  9.     },  
  10. };  
  可以看出這裏初始化了相應的函數,以及設備名稱和擁有模塊。其中 name="s3c2410-lcd"這個很重要,他是platform核心尋找相應platform設備的的依據。初始化的各個函數都需要lcd驅動程序編寫。
三. linux設備模型相關函數
  1. 對應上面的platform_driver初始化用的函數:
s3c2410fb_probe      
s3c2410fb_remove
s3c2410fb_suspend,
s3c2410fb_resume,
    其中s3c2410fb_probe函數是調用platform_driver_register時,由platform_bus的match函數找到合適的lcd設備成功後調用的函數,完成初始化工作。下面重點分析這個函數。
  2. s3c2410fb_probe 函數分析
這個函數只有一條語句就是調用s3c24xxfb_probe,下面是s3c24xxfb_probe函數,這個是lcd驅動最關鍵的函數。
  1. static int __init s3c24xxfb_probe(struct platform_device *pdev,  
  2.                   enum s3c_drv_type drv_type)  
  3. {  
  4.     struct s3c2410fb_info *info;  
  5.     struct s3c2410fb_display *display;  
  6.     struct fb_info *fbinfo;  
  7.     struct s3c2410fb_mach_info *mach_info;  
  8.     struct resource *res;  
  9.     int ret;  
  10.     int irq;  
  11.     int i;  
  12.     int size;  
  13.     u32 lcdcon1;  
  14.   
  15.     mach_info = pdev->dev.platform_data;  
  16.         //在/arch/arm/mach-s3c2440/mach-mini2440.c的mini2440_machine_init函數中,調用s3c24xx_fb_set_platdata(&mini2440_fb_info)   
  17.         //將mini2440_fb_info賦值給pdev->dev.paltform_data,所以這裏得到的是mini2440_fb_info   
  18.     if (mach_info == NULL) {  
  19.         dev_err(&pdev->dev,  
  20.             "no platform data for lcd, cannot attach\n");  
  21.         return -EINVAL;  
  22.     }  
  23.   
  24.     if (mach_info->default_display >= mach_info->num_displays) {  
  25.         dev_err(&pdev->dev, "default is %d but only %d displays\n",  
  26.             mach_info->default_display, mach_info->num_displays);  
  27.         return -EINVAL;  
  28.     }  
  29.   
  30.     display = mach_info->displays + mach_info->default_display;  
  31.         //mach_info->displays = 0,mach_info->default_display = mini2440_lcd_cfg   
  32.         //所以display = mini2440_lcd_cfg   
  33.     irq = platform_get_irq(pdev, 0);  
  34.         //pdev是platfoem_device結構,這個函數是從platform_device佔用的資源裏取出irq號   
  35.     if (irq < 0) {  
  36.         dev_err(&pdev->dev, "no irq for device\n");  
  37.         return -ENOENT;  
  38.     }  
  39.   
  40.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  
  41.         //framebuffer_alloc所做的事就是分配一個fb_info結構體,因爲這個結構體最後有個通用指針*par,這個是設備自定義結構,在這裏是s3c24fb_info   
  42.         //所以分配內存的時候在fb_info結構的大小基礎上必須加上s3c2410fb_info結構的大小,這樣纔是這裏的fb_info真正的大小   
  43.     if (!fbinfo)  
  44.         return -ENOMEM;  
  45.   
  46.     platform_set_drvdata(pdev, fbinfo);  
  47.   
  48.     info = fbinfo->par; //將info(s3c2410fb_info結構)指向新分配的fbinfo的par位置   
  49.     info->dev = &pdev->dev;  
  50.     info->drv_type = drv_type;  
  51.   
  52.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  53.         //得到lcd控制器io內存的物理地址   
  54.     if (res == NULL) {  
  55.         dev_err(&pdev->dev, "failed to get memory registers\n");  
  56.         ret = -ENXIO;  
  57.         goto dealloc_fb;  
  58.     }  
  59.   
  60.     size = (res->end - res->start) + 1;  
  61.     info->mem = request_mem_region(res->start, size, pdev->name);  
  62.         //向內核請求所用的io內存,這裏主要防止其他模塊競爭,如果其他模塊佔用這塊內存,函數就會返回NULL   
  63.     if (info->mem == NULL) {  
  64.         dev_err(&pdev->dev, "failed to get memory region\n");  
  65.         ret = -ENOENT;  
  66.         goto dealloc_fb;  
  67.     }  
  68.   
  69.     info->io = ioremap(res->start, size);  
  70.         //將物理內存映射成虛擬地址,以供內核使用   
  71.     if (info->io == NULL) {  
  72.         dev_err(&pdev->dev, "ioremap() of registers failed\n");  
  73.         ret = -ENXIO;  
  74.         goto release_mem;  
  75.     }  
  76.   
  77.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  
  78.         // irq_base是lcd中斷控制器寄存器對應的虛擬地址   
  79.     dprintk("devinit\n");  
  80.   
  81.     strcpy(fbinfo->fix.id, driver_name);  
  82.   
  83.     /* Stop the video */  
  84.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);  
  85.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);  
  86.   
  87.     fbinfo->fix.type     = FB_TYPE_PACKED_PIXELS;  
  88.     fbinfo->fix.type_aux     = 0;  
  89.     fbinfo->fix.xpanstep     = 0;  
  90.     fbinfo->fix.ypanstep     = 0;  
  91.     fbinfo->fix.ywrapstep        = 0;  
  92.     fbinfo->fix.accel        = FB_ACCEL_NONE;  
  93.         //以上初始化fb_fix_screeninfo結構   
  94.     fbinfo->var.nonstd       = 0;  
  95.     fbinfo->var.activate     = FB_ACTIVATE_NOW;  
  96.     fbinfo->var.accel_flags     = 0;  
  97.     fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;  
  98.         //以上初始化fb_var_screeninfo結構   
  99.     fbinfo->fbops            = &s3c2410fb_ops;  
  100.         // 這裏將我們實現的函數與frambuffer核心的操作聯繫上   
  101.     fbinfo->flags            = FBINFO_FLAG_DEFAULT;  
  102.     fbinfo->pseudo_palette      = &info->pseudo_pal;  
  103.   
  104.     for (i = 0; i < 256; i++)  
  105.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
  106.   
  107.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
  108.         //註冊中斷處理函數,一般的lcd操作基本不需要中斷   
  109.     if (ret) {  
  110.         dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);  
  111.         ret = -EBUSY;  
  112.         goto release_regs;  
  113.     }  
  114.   
  115.     info->clk = clk_get(NULL, "lcd");  
  116.     if (!info->clk || IS_ERR(info->clk)) {  
  117.         printk(KERN_ERR "failed to get lcd clock source\n");  
  118.         ret = -ENOENT;  
  119.         goto release_irq;  
  120.     }  
  121.   
  122.     clk_enable(info->clk);  
  123.         //以上操作使能lcd時鐘   
  124.     dprintk("got and enabled clock\n");  
  125.   
  126.     msleep(1);  
  127.   
  128.     info->clk_rate = clk_get_rate(info->clk);  
  129.     /* find maximum required memory size for display */  
  130.     for (i = 0; i < mach_info->num_displays; i++) {  
  131.         unsigned long smem_len = mach_info->displays[i].xres; // = 240   
  132.   
  133.         smem_len *= mach_info->displays[i].yres; // = 320   
  134.         smem_len *= mach_info->displays[i].bpp;  // = 16   
  135.         smem_len >>= 3;                          //將位的個數轉換成字節個數   
  136.         if (fbinfo->fix.smem_len < smem_len)  
  137.             fbinfo->fix.smem_len = smem_len;  
  138.     }  
  139.   
  140.     /* Initialize video memory */  
  141.     ret = s3c2410fb_map_video_memory(fbinfo);  
  142.         //這個函數主要功能就是分配一塊內存,大小爲上面計算的smem_len,並且將分配的內存的物理地址賦值給fbinfo->fix.smem_start   
  143.         //將虛擬地址賦值給fbinfo->screen_base   
  144.     if (ret) {  
  145.         printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);  
  146.         ret = -ENOMEM;  
  147.         goto release_clock;  
  148.     }  
  149.   
  150.     dprintk("got video memory\n");  
  151.   
  152.     fbinfo->var.xres = display->xres;  
  153.     fbinfo->var.yres = display->yres;  
  154.     fbinfo->var.bits_per_pixel = display->bpp;  
  155.         //這三個初始化很重要,對於下面的s3c2410fb_check_var尤其重要   
  156.     s3c2410fb_init_registers(fbinfo);  
  157.         //初始化lcd控制器的GPIO接口控制寄存器   
  158.   
  159.     s3c2410fb_check_var(&fbinfo->var, fbinfo);  
  160.         //這個函數根據fbinfo->var的xres,yres和bits_per_pixel選擇相應的s3c2410fb_display結構,並將這個結構的各個域的值賦值給   
  161.         //fbinfo->var的相應成員。因爲mini2440lcd驅動只有一個s3c2410fb_display結構就是mini2440_lcd_cfg,所以賦值的就是mini2440_lcd_cfg   
  162.     ret = s3c2410fb_cpufreq_register(info);  
  163.     if (ret < 0) {  
  164.         dev_err(&pdev->dev, "Failed to register cpufreq\n");  
  165.         goto free_video_memory;  
  166.     }  
  167.   
  168.     ret = register_framebuffer(fbinfo);  
  169.         //將fbinfo結構註冊到frambuffer核心   
  170.     if (ret < 0) {  
  171.         printk(KERN_ERR "Failed to register framebuffer device: %d\n",  
  172.             ret);  
  173.         goto free_cpufreq;  
  174.     }  
  175.   
  176.     /* create device files */  
  177.     ret = device_create_file(&pdev->dev, &dev_attr_debug);  
  178.     if (ret) {  
  179.         printk(KERN_ERR "failed to add debug attribute\n");  
  180.     }  
  181.   
  182.     printk(KERN_INFO "fb%d: %s frame buffer device\n",  
  183.         fbinfo->node, fbinfo->fix.id);  
  184.   
  185.     return 0;  
  186.   
  187. free_cpufreq:  
  188.     s3c2410fb_cpufreq_deregister(info);  
  189. free_video_memory:  
  190.     s3c2410fb_unmap_video_memory(fbinfo);  
  191. release_clock:  
  192.     clk_disable(info->clk);  
  193.     clk_put(info->clk);  
  194. release_irq:  
  195.     free_irq(irq, info);  
  196. release_regs:  
  197.     iounmap(info->io);  
  198. release_mem:  
  199.     release_resource(info->mem);  
  200.     kfree(info->mem);  
  201. dealloc_fb:  
  202.     platform_set_drvdata(pdev, NULL);  
  203.     framebuffer_release(fbinfo);  
  204.     return ret;  
  205. }   
從上面分析可以看出,這個函數主要做了下面幾件事:
  (1) 從platform_device中獲得s3c2410fb_mach_info結構體賦值給mach_info。這就得到了lcd控制器的所有初始配置。
  (2) 從mach_info中獲得s3c2410fb_display結構體賦值給display。這樣就得到了顯示相關的初始配置。
  (3) 分配一個fb_info結構體fbinfo和一個s3c2410fb_info結構體info,並且將info指向fbinfo->par
  (4) 由pdev中所用的資源初始化info結構,主要初始化io內存,並映射虛擬地址。
  (5) 關閉lcd顯示
  (6) 初始化fbinfo->fix,fbinfo->var 的部分域(不依賴配置信息的部分)
  (7) 初始化fbinfo->fbops爲s3c2410fb_ops
  (8) 註冊中斷處理程序s3c2410fb_irq
  (9) 使能lcd時鐘
  (10)爲lcd設備分配顯存,顯存開始地址賦值給fbinfo->screen_base
  (11)初始化lcd控制器的io接口控制寄存器
  (12)用display中的值初始化fbinfo->var中相應的值(與顯示配置相關的部分)
  (13)將fbinfo結構註冊到frambuffer核心
發佈了2 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章