[日期:2011-10-04] | 來源:Linux社區 作者:yaozhenguo2006 |
1.s3c2410fb.c是內核的一個模塊,在模塊初始化函數中只是簡單的調用了platform_driver_register把自己註冊成爲platform驅動。初始化函數如下:
- int __init s3c2410fb_init(void)
- {
- int ret = platform_driver_register(&s3c2410fb_driver);
- if (ret == 0)
- ret = platform_driver_register(&s3c2412fb_driver);
- return ret;
- }
- 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,
- },
- };
三. 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驅動最關鍵的函數。
- static int __init s3c24xxfb_probe(struct platform_device *pdev,
- enum s3c_drv_type drv_type)
- {
- struct s3c2410fb_info *info;
- struct s3c2410fb_display *display;
- struct fb_info *fbinfo;
- struct s3c2410fb_mach_info *mach_info;
- struct resource *res;
- int ret;
- int irq;
- int i;
- int size;
- u32 lcdcon1;
- mach_info = pdev->dev.platform_data;
- //在/arch/arm/mach-s3c2440/mach-mini2440.c的mini2440_machine_init函數中,調用s3c24xx_fb_set_platdata(&mini2440_fb_info)
- //將mini2440_fb_info賦值給pdev->dev.paltform_data,所以這裏得到的是mini2440_fb_info
- if (mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- return -EINVAL;
- }
- if (mach_info->default_display >= mach_info->num_displays) {
- dev_err(&pdev->dev, "default is %d but only %d displays\n",
- mach_info->default_display, mach_info->num_displays);
- return -EINVAL;
- }
- display = mach_info->displays + mach_info->default_display;
- //mach_info->displays = 0,mach_info->default_display = mini2440_lcd_cfg
- //所以display = mini2440_lcd_cfg
- irq = platform_get_irq(pdev, 0);
- //pdev是platfoem_device結構,這個函數是從platform_device佔用的資源裏取出irq號
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- //framebuffer_alloc所做的事就是分配一個fb_info結構體,因爲這個結構體最後有個通用指針*par,這個是設備自定義結構,在這裏是s3c24fb_info
- //所以分配內存的時候在fb_info結構的大小基礎上必須加上s3c2410fb_info結構的大小,這樣纔是這裏的fb_info真正的大小
- if (!fbinfo)
- return -ENOMEM;
- platform_set_drvdata(pdev, fbinfo);
- info = fbinfo->par; //將info(s3c2410fb_info結構)指向新分配的fbinfo的par位置
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- //得到lcd控制器io內存的物理地址
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory registers\n");
- ret = -ENXIO;
- goto dealloc_fb;
- }
- size = (res->end - res->start) + 1;
- info->mem = request_mem_region(res->start, size, pdev->name);
- //向內核請求所用的io內存,這裏主要防止其他模塊競爭,如果其他模塊佔用這塊內存,函數就會返回NULL
- if (info->mem == NULL) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto dealloc_fb;
- }
- info->io = ioremap(res->start, size);
- //將物理內存映射成虛擬地址,以供內核使用
- if (info->io == NULL) {
- dev_err(&pdev->dev, "ioremap() of registers failed\n");
- ret = -ENXIO;
- goto release_mem;
- }
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
- // irq_base是lcd中斷控制器寄存器對應的虛擬地址
- dprintk("devinit\n");
- strcpy(fbinfo->fix.id, driver_name);
- /* Stop the video */
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- //以上初始化fb_fix_screeninfo結構
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
- //以上初始化fb_var_screeninfo結構
- fbinfo->fbops = &s3c2410fb_ops;
- // 這裏將我們實現的函數與frambuffer核心的操作聯繫上
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &info->pseudo_pal;
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
- //註冊中斷處理函數,一般的lcd操作基本不需要中斷
- if (ret) {
- dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
- ret = -EBUSY;
- goto release_regs;
- }
- info->clk = clk_get(NULL, "lcd");
- if (!info->clk || IS_ERR(info->clk)) {
- printk(KERN_ERR "failed to get lcd clock source\n");
- ret = -ENOENT;
- goto release_irq;
- }
- clk_enable(info->clk);
- //以上操作使能lcd時鐘
- dprintk("got and enabled clock\n");
- msleep(1);
- info->clk_rate = clk_get_rate(info->clk);
- /* find maximum required memory size for display */
- for (i = 0; i < mach_info->num_displays; i++) {
- unsigned long smem_len = mach_info->displays[i].xres; // = 240
- smem_len *= mach_info->displays[i].yres; // = 320
- smem_len *= mach_info->displays[i].bpp; // = 16
- smem_len >>= 3; //將位的個數轉換成字節個數
- if (fbinfo->fix.smem_len < smem_len)
- fbinfo->fix.smem_len = smem_len;
- }
- /* Initialize video memory */
- ret = s3c2410fb_map_video_memory(fbinfo);
- //這個函數主要功能就是分配一塊內存,大小爲上面計算的smem_len,並且將分配的內存的物理地址賦值給fbinfo->fix.smem_start
- //將虛擬地址賦值給fbinfo->screen_base
- if (ret) {
- printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
- ret = -ENOMEM;
- goto release_clock;
- }
- dprintk("got video memory\n");
- fbinfo->var.xres = display->xres;
- fbinfo->var.yres = display->yres;
- fbinfo->var.bits_per_pixel = display->bpp;
- //這三個初始化很重要,對於下面的s3c2410fb_check_var尤其重要
- s3c2410fb_init_registers(fbinfo);
- //初始化lcd控制器的GPIO接口控制寄存器
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
- //這個函數根據fbinfo->var的xres,yres和bits_per_pixel選擇相應的s3c2410fb_display結構,並將這個結構的各個域的值賦值給
- //fbinfo->var的相應成員。因爲mini2440lcd驅動只有一個s3c2410fb_display結構就是mini2440_lcd_cfg,所以賦值的就是mini2440_lcd_cfg
- ret = s3c2410fb_cpufreq_register(info);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register cpufreq\n");
- goto free_video_memory;
- }
- ret = register_framebuffer(fbinfo);
- //將fbinfo結構註冊到frambuffer核心
- if (ret < 0) {
- printk(KERN_ERR "Failed to register framebuffer device: %d\n",
- ret);
- goto free_cpufreq;
- }
- /* create device files */
- ret = device_create_file(&pdev->dev, &dev_attr_debug);
- if (ret) {
- printk(KERN_ERR "failed to add debug attribute\n");
- }
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- fbinfo->node, fbinfo->fix.id);
- return 0;
- free_cpufreq:
- s3c2410fb_cpufreq_deregister(info);
- free_video_memory:
- s3c2410fb_unmap_video_memory(fbinfo);
- release_clock:
- clk_disable(info->clk);
- clk_put(info->clk);
- release_irq:
- free_irq(irq, info);
- release_regs:
- iounmap(info->io);
- release_mem:
- release_resource(info->mem);
- kfree(info->mem);
- dealloc_fb:
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(fbinfo);
- return ret;
- }
(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核心