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核心

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值