zoukankan      html  css  js  c++  java
  • linux lcd设备驱动剖析二

    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。

    上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18188259

    上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:

    [cpp] view plain?
    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;  /* 包含s3c2410fb_display */  
    8.     struct resource *res;  
    9.     int ret;  
    10.     int irq;  
    11.     int i;  
    12.     int size;  
    13.     u32 lcdcon1;  
    14.   
    15.     /*  s3c24xx_fb_set_platdata()里会设置platform_data 
    16.      *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info); 
    17.      *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例 
    18.      */  
    19.     mach_info = pdev->dev.platform_data;  
    20.   
    21.     /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */  
    22.     if (mach_info == NULL) {  
    23.         dev_err(&pdev->dev,  
    24.             "no platform data for lcd, cannot attach ");  
    25.         return -EINVAL;     /* 表示无效的参数 */  
    26.     }  
    27.   
    28.     /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */  
    29.     if (mach_info->default_display >= mach_info->num_displays) {  
    30.         dev_err(&pdev->dev, "default is %d but only %d displays ",  
    31.             mach_info->default_display, mach_info->num_displays);  
    32.         return -EINVAL;  
    33.     }  
    34.   
    35.     /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */  
    36.     display = mach_info->displays + mach_info->default_display;  
    37.   
    38.     /* 通过平台设备platform_device获得IRQ 
    39.      * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num) 
    40.      */  
    41.     irq = platform_get_irq(pdev, 0);  
    42.     if (irq < 0) {  
    43.         dev_err(&pdev->dev, "no irq for device ");  
    44.         return -ENOENT;  
    45.     }  
    46.   
    47.     /*  分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间 
    48.      *  用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据 
    49.      *  比如:clk,resource,io,irq_base,drv_type等额外信息 
    50.      */  
    51.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  
    52.     if (!fbinfo)  
    53.         return -ENOMEM;     /* 返回NULL表示失败 */  
    54.   
    55.     /* 相当于pdev->dev->driver_data = fbinfo */  
    56.     platform_set_drvdata(pdev, fbinfo);  
    57.   
    58.     /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */  
    59.     info = fbinfo->par;          /* 将私有数据赋给info指针变量 */  
    60.     info->dev = &pdev->dev;       /* 指定struct s3c2410fb_info中dev为平台设备中的dev */  
    61.     info->drv_type = drv_type;   /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */  
    62.   
    63.     /*  通过平台设备platform_device获得资源(IO) */  
    64.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
    65.     if (res == NULL) {  
    66.         dev_err(&pdev->dev, "failed to get memory registers ");  
    67.         ret = -ENXIO;  
    68.         goto dealloc_fb;  
    69.     }  
    70.   
    71.     size = (res->end - res->start) + 1;       /* 资源的大小 */  
    72.   
    73.     /* 申请以res->start地址开始大小为size的I/O内存 */  
    74.     info->mem = request_mem_region(res->start, size, pdev->name);  
    75.     if (info->mem == NULL) {  
    76.         dev_err(&pdev->dev, "failed to get memory region ");  
    77.         ret = -ENOENT;  
    78.         goto dealloc_fb;  
    79.     }  
    80.   
    81.     /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */  
    82.     info->io = ioremap(res->start, size);  
    83.     if (info->io == NULL) {  
    84.         dev_err(&pdev->dev, "ioremap() of registers failed ");  
    85.         ret = -ENXIO;  
    86.         goto release_mem;  
    87.     }  
    88.   
    89.     /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */  
    90.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  
    91.   
    92.     dprintk("devinit ");  
    93.   
    94.     /* 驱动名,fbinfo->fix.id = s3c2410fb */  
    95.     strcpy(fbinfo->fix.id, driver_name);   
    96.   
    97.     /* Stop the video */  
    98.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);  
    99.     /* 禁止Video output */  
    100.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);  
    101.   
    102.     /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */  
    103.     fbinfo->fix.type         = FB_TYPE_PACKED_PIXELS;  
    104.     fbinfo->fix.type_aux     = 0;  
    105.     fbinfo->fix.xpanstep     = 0;  
    106.     fbinfo->fix.ypanstep     = 0;  
    107.     fbinfo->fix.ywrapstep        = 0;  
    108.     fbinfo->fix.accel            = FB_ACCEL_NONE;    /* 无硬件加速 */  
    109.   
    110.     /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */  
    111.     fbinfo->var.nonstd           = 0;  
    112.     fbinfo->var.activate     = FB_ACTIVATE_NOW;  
    113.     fbinfo->var.accel_flags     = 0;  
    114.     fbinfo->var.vmode            = FB_VMODE_NONINTERLACED;  
    115.   
    116.     /* 设置fb_ops结构体 */  
    117.     fbinfo->fbops                = &s3c2410fb_ops;  
    118.       
    119.     fbinfo->flags                = FBINFO_FLAG_DEFAULT;  
    120.   
    121.     /* 设置假调色板 */  
    122.     fbinfo->pseudo_palette      = &info->pseudo_pal;  
    123.   
    124.     /* palette_buffer[i] = 0x80000000,清空调色板 */  
    125.     for (i = 0; i < 256; i++)  
    126.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
    127.   
    128.     /* 申请中断,s3c2410fb_irq是中断处理函数 */  
    129.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
    130.     if (ret) {  
    131.         dev_err(&pdev->dev, "cannot get irq %d - err %d ", irq, ret);  
    132.         ret = -EBUSY;  
    133.         goto release_regs;  
    134.     }  
    135.   
    136.     /* 获取lcd时钟 */  
    137.     info->clk = clk_get(NULL, "lcd");  
    138.     if (!info->clk || IS_ERR(info->clk)) {  
    139.         printk(KERN_ERR "failed to get lcd clock source ");  
    140.         ret = -ENOENT;  
    141.         goto release_irq;  
    142.     }  
    143.   
    144.     /* 使能lcd时钟 */  
    145.     clk_enable(info->clk);         
    146.     dprintk("got and enabled clock ");  
    147.   
    148.     msleep(1);  
    149.   
    150.     /* find maximum required memory size for display */  
    151.   
    152.     /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位, 
    153.      * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。 
    154.      * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。 
    155.      */  
    156.     for (i = 0; i < mach_info->num_displays; i++) {           /* 这里mach_info->num_displays = 1 */  
    157.         unsigned long smem_len = mach_info->displays[i].xres;    /* x方向分辨率 */  
    158.   
    159.         smem_len *= mach_info->displays[i].yres;             /* y方向分辨率 */  
    160.         smem_len *= mach_info->displays[i].bpp;                  /* bpp */  
    161.         smem_len >>= 3;                                           /* smem_len除以8 */  
    162.         if (fbinfo->fix.smem_len < smem_len)  
    163.             fbinfo->fix.smem_len = smem_len;  
    164.     }  
    165.   
    166.     /* Initialize video memory */  
    167.     ret = s3c2410fb_map_video_memory(fbinfo);   /* 分配显存 */  
    168.     if (ret) {  
    169.         printk(KERN_ERR "Failed to allocate video RAM: %d ", ret);  
    170.         ret = -ENOMEM;  
    171.         goto release_clock;  
    172.     }  
    173.   
    174.     dprintk("got video memory ");  
    175.   
    176.     /* display指向tq2440_lcd_cfg */  
    177.     fbinfo->var.xres = display->xres;         /* 设置x方向的分辨率 */  
    178.     fbinfo->var.yres = display->yres;         /* 设置y方向的分辨率 */  
    179.     fbinfo->var.bits_per_pixel = display->bpp;    /* 设置bpp位数 */  
    180.   
    181.     /* 初始化LCD相关的寄存器 */  
    182.     s3c2410fb_init_registers(fbinfo);  
    183.   
    184.     /* 检查可变参数 */  
    185.     s3c2410fb_check_var(&fbinfo->var, fbinfo);  
    186.   
    187.     /* 注册fb_info结构体 */  
    188.     ret = register_framebuffer(fbinfo);  
    189.     if (ret < 0) {  
    190.         printk(KERN_ERR "Failed to register framebuffer device: %d ",  
    191.             ret);  
    192.         goto free_video_memory;  
    193.     }  
    194.   
    195.     /* create device files */  
    196.     ret = device_create_file(&pdev->dev, &dev_attr_debug);  
    197.     if (ret) {  
    198.         printk(KERN_ERR "failed to add debug attribute ");  
    199.     }  
    200.   
    201.   
    202.     /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */  
    203.     printk(KERN_INFO "fb%d: %s frame buffer device ",  
    204.         fbinfo->node, fbinfo->fix.id);  
    205.   
    206.     return 0;  
    207.   
    208. free_video_memory:  
    209.     s3c2410fb_unmap_video_memory(fbinfo);  
    210. release_clock:  
    211.     clk_disable(info->clk);              /* 禁止lcd时钟 */  
    212.     clk_put(info->clk);                  /* 删除lcd时钟 */  
    213. release_irq:  
    214.     free_irq(irq, info);                /* 释放IRQ */  
    215. release_regs:  
    216.     iounmap(info->io);                   /* 解除映射 */  
    217. release_mem:  
    218.     release_resource(info->mem);     /* 释放资源 */  
    219.     kfree(info->mem);                    /* 释放刚申请的内存 */  
    220. dealloc_fb:  
    221.     platform_set_drvdata(pdev, NULL);   /* 相当于pdev->dev->driver_data = NULL */  
    222.     framebuffer_release(fbinfo);        /* 释放fb_info结构体 */  
    223.     return ret;  
    224. }  
    拆分详解:

    一、获得平台数据

    [cpp] view plain?
    1. mach_info = pdev->dev.platform_data;  
    2.   
    3. /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */  
    4. if (mach_info == NULL) {  
    5.     dev_err(&pdev->dev,  
    6.         "no platform data for lcd, cannot attach ");  
    7.     return -EINVAL;     /* 表示无效的参数 */  
    8. }  
    s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    所以这里传入来的platform_data就是tq2440_fb_info结构体实例。

    [cpp] view plain?
    1. static void __init tq2440_machine_init(void)  
    2. {  
    3.     /* 初始化tq2440_fb_info实体结构体 */  
    4.     s3c24xx_fb_set_platdata(&tq2440_fb_info);  
    5.     s3c_i2c0_set_platdata(NULL);  
    6.   
    7.     /* 添加tq2440_devices到内核,它是platform_device类的设备 */  
    8.     platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));  
    9.     EmbedSky_machine_init();  
    10.     s3c2410_gpio_setpin(S3C2410_GPG12, 0);  
    11.     s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);  
    12.     s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);  
    13. }  
    s3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info

    [cpp] view plain?
    1. void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)  
    2. {  
    3.     struct s3c2410fb_mach_info *npd;  
    4.   
    5.     /* 申请分配s3c2410fb_mach_info大小的内存 */  
    6.     npd = kmalloc(sizeof(*npd), GFP_KERNEL);  
    7.     if (npd) {  
    8.         /* 拷贝s3c2410fb_mach_info型实体给npd */  
    9.         memcpy(npd, pd, sizeof(*npd));  
    10.   
    11.         /* 最后将s3c2410fb_mach_info型实体赋给platform_data */  
    12.         s3c_device_lcd.dev.platform_data = npd;  
    13.     } else {  
    14.         printk(KERN_ERR "no memory for LCD platform data ");  
    15.     }  
    16. }  
    二、设置s3c2410fb_display指向tq2440_lcd_cfg

    [cpp] view plain?
    1. /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */  
    2. if (mach_info->default_display >= mach_info->num_displays) {  
    3.     dev_err(&pdev->dev, "default is %d but only %d displays ",  
    4.         mach_info->default_display, mach_info->num_displays);  
    5.     return -EINVAL;  
    6. }  
    7.   
    8. /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */  
    9. display = mach_info->displays + mach_info->default_display;  
    三、获得IRQ资源

    [cpp] view plain?
    1. irq = platform_get_irq(pdev, 0);  
    2. if (irq < 0) {  
    3.     dev_err(&pdev->dev, "no irq for device ");  
    4.     return -ENOENT;  
    5. }  
    四、分配fb_info内存
    [cpp] view plain?
    1. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  
    2.     if (!fbinfo)  
    3.         return -ENOMEM;     /* 返回NULL表示失败 */  
    framebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。
    五、设置s3c2410fb_info结构体

    [cpp] view plain?
    1. /* 相当于pdev->dev->driver_data = fbinfo */  
    2.     platform_set_drvdata(pdev, fbinfo);  
    3.   
    4.     /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */  
    5.     info = fbinfo->par;          /* 将私有数据赋给info指针变量 */  
    6.     info->dev = &pdev->dev;       /* 指定struct s3c2410fb_info中dev为平台设备中的dev */  
    7.     info->drv_type = drv_type;   /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */  
    六、获取IO资源,映射IO

    [cpp] view plain?
    1. /*  通过平台设备platform_device获得资源(IO) */  
    2.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
    3.     if (res == NULL) {  
    4.         dev_err(&pdev->dev, "failed to get memory registers ");  
    5.         ret = -ENXIO;  
    6.         goto dealloc_fb;  
    7.     }  
    8.   
    9.     size = (res->end - res->start) + 1;       /* 资源的大小 */  
    10.   
    11.     /* 申请以res->start地址开始大小为size的I/O内存 */  
    12.     info->mem = request_mem_region(res->start, size, pdev->name);  
    13.     if (info->mem == NULL) {  
    14.         dev_err(&pdev->dev, "failed to get memory region ");  
    15.         ret = -ENOENT;  
    16.         goto dealloc_fb;  
    17.     }  
    18.   
    19.     /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */  
    20.     info->io = ioremap(res->start, size);  
    21.     if (info->io == NULL) {  
    22.         dev_err(&pdev->dev, "ioremap() of registers failed ");  
    23.         ret = -ENXIO;  
    24.         goto release_mem;  
    25.     }  
    26.   
    27.     /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */  
    28.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  
    七、读写LCDCON1,禁止视频数据输出

    [cpp] view plain?
    1. /* Stop the video */  
    2. lcdcon1 = readl(info->io + S3C2410_LCDCON1);  
    3. /* 禁止Video output */  
    4. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);  
    八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等

    [cpp] view plain?
    1. /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */  
    2.     fbinfo->fix.type         = FB_TYPE_PACKED_PIXELS;  
    3.     fbinfo->fix.type_aux     = 0;  
    4.     fbinfo->fix.xpanstep     = 0;  
    5.     fbinfo->fix.ypanstep     = 0;  
    6.     fbinfo->fix.ywrapstep        = 0;  
    7.     fbinfo->fix.accel            = FB_ACCEL_NONE;    /* 无硬件加速 */  
    8.   
    9.     /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */  
    10.     fbinfo->var.nonstd           = 0;  
    11.     fbinfo->var.activate     = FB_ACTIVATE_NOW;  
    12.     fbinfo->var.accel_flags     = 0;  
    13.     fbinfo->var.vmode            = FB_VMODE_NONINTERLACED;  
    14.   
    15.     /* 设置fb_ops结构体 */  
    16.     fbinfo->fbops                = &s3c2410fb_ops;  
    17.       
    18.     fbinfo->flags                = FBINFO_FLAG_DEFAULT;  
    19.   
    20.     /* 设置假调色板 */  
    21.     fbinfo->pseudo_palette      = &info->pseudo_pal;  
    22.   
    23.     /* palette_buffer[i] = 0x80000000,清空调色板 */  
    24.     for (i = 0; i < 256; i++)  
    25.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
    九、申请中断、获取LCD时钟,使能LCD时钟
    [cpp] view plain?
    1. /* 申请中断,s3c2410fb_irq是中断处理函数 */  
    2. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
    3. if (ret) {  
    4.     dev_err(&pdev->dev, "cannot get irq %d - err %d ", irq, ret);  
    5.     ret = -EBUSY;  
    6.     goto release_regs;  
    7. }  
    8.   
    9. /* 获取lcd时钟 */  
    10. info->clk = clk_get(NULL, "lcd");  
    11. if (!info->clk || IS_ERR(info->clk)) {  
    12.     printk(KERN_ERR "failed to get lcd clock source ");  
    13.     ret = -ENOENT;  
    14.     goto release_irq;  
    15. }  
    16.   
    17. /* 使能lcd时钟 */  
    18. clk_enable(info->clk);         
    19. dprintk("got and enabled clock ");  
    十、计算显存大小、分配显存内存

    [cpp] view plain?
    1. /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位, 
    2.     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。 
    3.     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。 
    4.  */  
    5. for (i = 0; i < mach_info->num_displays; i++) {           /* 这里mach_info->num_displays = 1 */  
    6.     unsigned long smem_len = mach_info->displays[i].xres;    /* x方向分辨率 */  
    7.   
    8.     smem_len *= mach_info->displays[i].yres;             /* y方向分辨率 */  
    9.     smem_len *= mach_info->displays[i].bpp;                  /* bpp */  
    10.     smem_len >>= 3;                                           /* smem_len除以8 */  
    11.     if (fbinfo->fix.smem_len < smem_len)  
    12.         fbinfo->fix.smem_len = smem_len;  
    13. }  
    14.   
    15. /* Initialize video memory */  
    16. ret = s3c2410fb_map_video_memory(fbinfo);   /* 分配显存 */  
    17. if (ret) {  
    18.     printk(KERN_ERR "Failed to allocate video RAM: %d ", ret);  
    19.     ret = -ENOMEM;  
    20.     goto release_clock;  
    21. }  
    十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP

    [cpp] view plain?
    1. /* display指向tq2440_lcd_cfg */  
    2. fbinfo->var.xres = display->xres;         /* 设置x方向的分辨率 */  
    3. fbinfo->var.yres = display->yres;         /* 设置y方向的分辨率 */  
    4. fbinfo->var.bits_per_pixel = display->bpp;    /* 设置bpp位数 */  
    十二、LCD相关寄存器的设置和fb_info的可变参数的检测

    [cpp] view plain?
    1. /* 初始化LCD相关的寄存器 */  
    2.     s3c2410fb_init_registers(fbinfo);  
    3.   
    4.     /* 检查可变参数 */  
    5.     s3c2410fb_check_var(&fbinfo->var, fbinfo);  
    十三、注册fb_info结构体

    [cpp] view plain?
    1. /* 注册fb_info结构体 */  
    2. ret = register_framebuffer(fbinfo);  
    3. if (ret < 0) {  
    4.     printk(KERN_ERR "Failed to register framebuffer device: %d ",  
    5.         ret);  
    6.     goto free_video_memory;  
    7. }  

  • 相关阅读:
    算法-在字符串中删除特定的字符或字符串
    Linux 下的7种文件类型
    TCP/IP协议、三次握手、四次挥手
    8、VUE自定义组件
    7、VUE事件
    6、VUE指令
    Redis高级功能-1、高并发基本概述
    5、插值
    4、VUE生命周期
    3、Vue实例的属性
  • 原文地址:https://www.cnblogs.com/alan666/p/8312423.html
Copyright © 2011-2022 走看看