基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.2(LCD驱动源码分析及移植之platform driver)

来源:互联网 发布:mac怎么给ipod导入歌曲 编辑:程序博客网 时间:2024/06/10 05:50
基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.2(LCD驱动源码分析及移植之platform driver) 2012-05-15 21:10:37

分类: LINUX

5.
LCD驱动模块的注册与注销:

点击(此处)折叠或打开

  1. int __init s3c2410fb_init(void)
  2. {
  3.     int ret = platform_driver_register(&s3c2410fb_driver);

  4.     if (ret == 0)
  5.         ret = platform_driver_register(&s3c2412fb_driver);

  6.     return ret;
  7. }

  8. static void __exit s3c2410fb_cleanup(void)
  9. {
  10.     platform_driver_unregister(&s3c2410fb_driver);
  11.     platform_driver_unregister(&s3c2412fb_driver);
  12. }

  13. module_init(s3c2410fb_init);
  14. module_exit(s3c2410fb_cleanup);
注册与注销模块由module_init宏与module_exit宏指定。

6.
LCD平台设备驱动s3c2410fb_driver

点击(此处)折叠或打开

  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. };

  11. static struct platform_driver s3c2412fb_driver = {
  12.     .probe        = s3c2412fb_probe,
  13.     .remove        = s3c2410fb_remove,
  14.     .suspend    = s3c2410fb_suspend,
  15.     .resume        = s3c2410fb_resume,
  16.     .driver        = {
  17.         .name    = "s3c2412-lcd",
  18.         .owner    = THIS_MODULE,
  19.     },
  20. };
该结构定义了LCD驱动程序的名字: "s3c2410-lcd",以及探测函数probe,移除函数remove,电源挂起函数suspend和电源恢复函数resume。

7.
探测函数
s3c2410fb_probe分析。

点击(此处)折叠或打开

  1. static int __init s3c2410fb_probe(struct platform_device *pdev)
  2. {
  3.     return s3c24xxfb_probe(pdev, DRV_S3C2410);
  4. }

  5. static int __init s3c2412fb_probe(struct platform_device *pdev)
  6. {
  7.     return s3c24xxfb_probe(pdev, DRV_S3C2412);
  8. }
由此可见,真正的探测函数为 s3c24xxfb_probe

点击(此处)折叠或打开

  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.     mach_info = pdev->dev.platform_data;
  15.     if (mach_info == NULL) {
  16.         dev_err(&pdev->dev,
  17.             "no platform data for lcd, cannot attach\n");
  18.         return -EINVAL;
  19.     }

  20.     if (mach_info->default_display >= mach_info->num_displays) {
  21.         dev_err(&pdev->dev, "default is %d but only %d displays\n",
  22.             mach_info->default_display, mach_info->num_displays);
  23.         return -EINVAL;
  24.     }

  25.     display = mach_info->displays + mach_info->default_display;

  26.     irq = platform_get_irq(pdev, 0);
  27.     if (irq < 0) {
  28.         dev_err(&pdev->dev, "no irq for device\n");
  29.         return -ENOENT;
  30.     }

  31.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
  32.     if (!fbinfo)
  33.         return -ENOMEM;

  34.     platform_set_drvdata(pdev, fbinfo);

  35.     info = fbinfo->par;
  36.     info->dev = &pdev->dev;
  37.     info->drv_type = drv_type;

  38.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  39.     if (res == NULL) {
  40.         dev_err(&pdev->dev, "failed to get memory registers\n");
  41.         ret = -ENXIO;
  42.         goto dealloc_fb;
  43.     }

  44.     size = (res->end - res->start) + 1;
  45.     info->mem = request_mem_region(res->start, size, pdev->name);
  46.     if (info->mem == NULL) {
  47.         dev_err(&pdev->dev, "failed to get memory region\n");
  48.         ret = -ENOENT;
  49.         goto dealloc_fb;
  50.     }

  51.     info->io = ioremap(res->start, size);
  52.     if (info->io == NULL) {
  53.         dev_err(&pdev->dev, "ioremap() of registers failed\n");
  54.         ret = -ENXIO;
  55.         goto release_mem;
  56.     }

  57.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

  58.     dprintk("devinit\n");

  59.     strcpy(fbinfo->fix.id, driver_name);

  60.     /* Stop the video */
  61.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  62.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

  63.     fbinfo->fix.type     = FB_TYPE_PACKED_PIXELS;
  64.     fbinfo->fix.type_aux     = 0;
  65.     fbinfo->fix.xpanstep     = 0;
  66.     fbinfo->fix.ypanstep     = 0;
  67.     fbinfo->fix.ywrapstep     = 0;
  68.     fbinfo->fix.accel     = FB_ACCEL_NONE;

  69.     fbinfo->var.nonstd     = 0;
  70.     fbinfo->var.activate     = FB_ACTIVATE_NOW;
  71.     fbinfo->var.accel_flags = 0;
  72.     fbinfo->var.vmode     = FB_VMODE_NONINTERLACED;

  73.     fbinfo->fbops         = &s3c2410fb_ops;
  74.     fbinfo->flags         = FBINFO_FLAG_DEFAULT;
  75.     fbinfo->pseudo_palette = &info->pseudo_pal;

  76.     for (= 0; i < 256; i++)
  77.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

  78.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
  79.     if (ret) {
  80.         dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  81.         ret = -EBUSY;
  82.         goto release_regs;
  83.     }

  84.     info->clk = clk_get(NULL, "lcd");
  85.     if (!info->clk || IS_ERR(info->clk)) {
  86.         printk(KERN_ERR "failed to get lcd clock source\n");
  87.         ret = -ENOENT;
  88.         goto release_irq;
  89.     }

  90.     clk_enable(info->clk);
  91.     dprintk("got and enabled clock\n");

  92.     msleep(1);

  93.     info->clk_rate = clk_get_rate(info->clk);

  94.     /* find maximum required memory size for display */
  95.     for (= 0; i < mach_info->num_displays; i++) {
  96.         unsigned long smem_len = mach_info->displays[i].xres;

  97.         smem_len *= mach_info->displays[i].yres;
  98.         smem_len *= mach_info->displays[i].bpp;
  99.         smem_len >>= 3;
  100.         if (fbinfo->fix.smem_len < smem_len)
  101.             fbinfo->fix.smem_len = smem_len;
  102.     }

  103.     /* Initialize video memory */
  104.     ret = s3c2410fb_map_video_memory(fbinfo);
  105.     if (ret) {
  106.         printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
  107.         ret = -ENOMEM;
  108.         goto release_clock;
  109.     }

  110.     dprintk("got video memory\n");

  111.     fbinfo->var.xres = display->xres;
  112.     fbinfo->var.yres = display->yres;
  113.     fbinfo->var.bits_per_pixel = display->bpp;

  114.     s3c2410fb_init_registers(fbinfo);

  115.     s3c2410fb_check_var(&fbinfo->var, fbinfo);

  116.     ret = s3c2410fb_cpufreq_register(info);
  117.     if (ret < 0) {
  118.         dev_err(&pdev->dev, "Failed to register cpufreq\n");
  119.         goto free_video_memory;
  120.     }

  121.     ret = register_framebuffer(fbinfo);
  122.     if (ret < 0) {
  123.         printk(KERN_ERR "Failed to register framebuffer device: %d\n",
  124.             ret);
  125.         goto free_cpufreq;
  126.     }

  127.     /* create device files */
  128.     ret = device_create_file(&pdev->dev, &dev_attr_debug);
  129.     if (ret) {
  130.         printk(KERN_ERR "failed to add debug attribute\n");
  131.     }

  132.     printk(KERN_INFO "fb%d: %s frame buffer device\n",
  133.         fbinfo->node, fbinfo->fix.id);

  134.     return 0;

  135.  free_cpufreq:
  136.     s3c2410fb_cpufreq_deregister(info);
  137. free_video_memory:
  138.     s3c2410fb_unmap_video_memory(fbinfo);
  139. release_clock:
  140.     clk_disable(info->clk);
  141.     clk_put(info->clk);
  142. release_irq:
  143.     free_irq(irq, info);
  144. release_regs:
  145.     iounmap(info->io);
  146. release_mem:
  147.     release_resource(info->mem);
  148.     kfree(info->mem);
  149. dealloc_fb:
  150.     platform_set_drvdata(pdev, NULL);
  151.     framebuffer_release(fbinfo);
  152.     return ret;
  153. }

7.1

点击(此处)折叠或打开

  1. mach_info = pdev->dev.platform_data;
  2.     if (mach_info == NULL) {
  3.         dev_err(&pdev->dev,
  4.             "no platform data for lcd, cannot attach\n");
  5.         return -EINVAL;
  6.     }

  7.     if (mach_info->default_display >= mach_info->num_displays) {
  8.         dev_err(&pdev->dev, "default is %d but only %d displays\n",
  9.             mach_info->default_display, mach_info->num_displays);
  10.         return -EINVAL;
  11.     }

  12.     display = mach_info->displays + mach_info->default_display;
第一行用于获取platform device中定义的平台数据(已经在本博客的上一篇文章中提及)。其中的mach_info定义如下:

点击(此处)折叠或打开

  1. struct s3c2410fb_mach_info *mach_info;

点击(此处)折叠或打开

  1. struct s3c2410fb_mach_info {

  2.     struct s3c2410fb_display *displays;    /* attached diplays info */
  3.     unsigned num_displays;            /* number of defined displays */
  4.     unsigned default_display;

  5.     /* GPIOs */

  6.     unsigned long    gpcup;
  7.     unsigned long    gpcup_mask;
  8.     unsigned long    gpccon;
  9.     unsigned long    gpccon_mask;
  10.     unsigned long    gpdup;
  11.     unsigned long    gpdup_mask;
  12.     unsigned long    gpdcon;
  13.     unsigned long    gpdcon_mask;

  14.     /* lpc3600 control register */
  15.     unsigned long    lpcsel;
  16. };
主要是记录LCD的一些属性,包括与显示参数相关的“ struct s3c2410fb_display *displays; ”及使用的GPIO引脚。而 ”if (mach_info->default_display >=mach_info->num_displays) {...}“用来查看显示屏幕的具体规格。

7.2

点击(此处)折叠或打开

  1. irq = platform_get_irq(pdev, 0);
  2.     if (irq < 0) {
  3.         dev_err(&pdev->dev, "no irq for device\n");
  4.         return -ENOENT;
  5.     }
获取设备中断号(定义于mach-smdk2440.c中)。

7.3

点击(此处)折叠或打开

  1. fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
  2.     if (!fbinfo)
  3.         return -ENOMEM;

  4.     platform_set_drvdata(pdev, fbinfo);
在内存中申请sizeof(struct s3c2410fb_info)+sizeof(struct fb_info)大小的空间,然后将获得的fbinfo设置为平台设备的驱动数据。其中framebuffer_alloc源码如下:

点击(此处)折叠或打开

  1. /**
  2.  * framebuffer_alloc - creates a new frame buffer info structure
  3.  *
  4.  * @size: size of driver private data, can be zero
  5.  * @dev: pointer to the device for this fb, this can be NULL
  6.  *
  7.  * Creates a new frame buffer info structure. Also reserves @size bytes
  8.  * for driver private data (info->par). info->par (if any) will be
  9.  * aligned to sizeof(long).
  10.  *
  11.  * Returns the new structure, or NULL if an error occured.
  12.  *
  13.  */
  14. struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
  15. {
  16. #define BYTES_PER_LONG (BITS_PER_LONG/8)
  17. #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
  18.     int fb_info_size = sizeof(struct fb_info);
  19.     struct fb_info *info;
  20.     char *p;

  21.     if (size)
  22.         fb_info_size += PADDING;

  23.     p = kzalloc(fb_info_size + size, GFP_KERNEL);

  24.     if (!p)
  25.         return NULL;

  26.     info = (struct fb_info *) p;

  27.     if (size)
  28.         info->par = p + fb_info_size;

  29.     info->device = dev;

  30. #ifdef CONFIG_FB_BACKLIGHT
  31.     mutex_init(&info->bl_curve_mutex);
  32. #endif

  33.     return info;
  34. #undef PADDING
  35. #undef BYTES_PER_LONG
  36. }
  37. EXPORT_SYMBOL(framebuffer_alloc);
其中 platform_set_drvdata宏如下:

点击(此处)折叠或打开

  1. #define platform_set_drvdata(_dev,data)    dev_set_drvdata(&(_dev)->dev, (data))

7.4

点击(此处)折叠或打开

  1. info = fbinfo->par;
  2.     info->dev = &pdev->dev;
  3.     info->drv_type = drv_type;
设置驱动数据。

7.5

点击(此处)折叠或打开

  1. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  2.     if (res == NULL) {
  3.         dev_err(&pdev->dev, "failed to get memory registers\n");
  4.         ret = -ENXIO;
  5.         goto dealloc_fb;
  6.     }

  7.     size = (res->end - res->start) + 1;
  8.     info->mem = request_mem_region(res->start, size, pdev->name);
  9.     if (info->mem == NULL) {
  10.         dev_err(&pdev->dev, "failed to get memory region\n");
  11.         ret = -ENOENT;
  12.         goto dealloc_fb;
  13.     }

  14.     info->io = ioremap(res->start, size);
  15.     if (info->io == NULL) {
  16.         dev_err(&pdev->dev, "ioremap() of registers failed\n");
  17.         ret = -ENXIO;
  18.         goto release_mem;
  19.     }
映射内核资源(IO口,中断号,硬件地址转虚拟地址)。

7.6

点击(此处)折叠或打开

  1. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  2.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
关闭显示屏幕

7.7

点击(此处)折叠或打开

  1. fbinfo->fix.type     = FB_TYPE_PACKED_PIXELS;
  2.     fbinfo->fix.type_aux     = 0;
  3.     fbinfo->fix.xpanstep     = 0;
  4.     fbinfo->fix.ypanstep     = 0;
  5.     fbinfo->fix.ywrapstep     = 0;
  6.     fbinfo->fix.accel     = FB_ACCEL_NONE;

  7.     fbinfo->var.nonstd     = 0;
  8.     fbinfo->var.activate     = FB_ACTIVATE_NOW;
  9.     fbinfo->var.accel_flags = 0;
  10.     fbinfo->var.vmode     = FB_VMODE_NONINTERLACED;

  11.     fbinfo->fbops         = &s3c2410fb_ops;
  12.     fbinfo->flags         = FBINFO_FLAG_DEFAULT;
  13.     fbinfo->pseudo_palette = &info->pseudo_pal;

  14.     for (= 0; i < 256; i++)
  15.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
对fbinfo进行赋值。

7.8

点击(此处)折叠或打开

  1. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
  2.     if (ret) {
  3.         dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  4.         ret = -EBUSY;
  5.         goto release_regs;
  6.     }
映射中断响应函数。

7.9

点击(此处)折叠或打开

  1. info->clk = clk_get(NULL, "lcd");
  2.     if (!info->clk || IS_ERR(info->clk)) {
  3.         printk(KERN_ERR "failed to get lcd clock source\n");
  4.         ret = -ENOENT;
  5.         goto release_irq;
  6.     }

  7.     clk_enable(info->clk);
  8.     dprintk("got and enabled clock\n");

  9.     msleep(1);

  10.     info->clk_rate = clk_get_rate(info->clk);
打开LCD时钟并使能。

7.10

点击(此处)折叠或打开

  1. /* find maximum required memory size for display */
  2.     for (= 0; i < mach_info->num_displays; i++) {
  3.         unsigned long smem_len = mach_info->displays[i].xres;

  4.         smem_len *= mach_info->displays[i].yres;
  5.         smem_len *= mach_info->displays[i].bpp;
  6.         smem_len >>= 3;
  7.         if (fbinfo->fix.smem_len < smem_len)
  8.             fbinfo->fix.smem_len = smem_len;
  9.     }

  10.     /* Initialize video memory */
  11.     ret = s3c2410fb_map_video_memory(fbinfo);
  12.     if (ret) {
  13.         printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
  14.         ret = -ENOMEM;
  15.         goto release_clock;
  16.     }

  17.     dprintk("got video memory\n");
计算显示内存所需要的容量。

7.11

点击(此处)折叠或打开

  1. s3c2410fb_init_registers(fbinfo);

  2.     s3c2410fb_check_var(&fbinfo->var, fbinfo);

  3.     ret = s3c2410fb_cpufreq_register(info);
  4.     if (ret < 0) {
  5.         dev_err(&pdev->dev, "Failed to register cpufreq\n");
  6.         goto free_video_memory;
  7.     }

  8.     ret = register_framebuffer(fbinfo);
  9.     if (ret < 0) {
  10.         printk(KERN_ERR "Failed to register framebuffer device: %d\n",
  11.             ret);
  12.         goto free_cpufreq;
  13.     }

  14.     /* create device files */
  15.     ret = device_create_file(&pdev->dev, &dev_attr_debug);
  16.     if (ret) {
  17.         printk(KERN_ERR "failed to add debug attribute\n");
  18.     }

  19.     printk(KERN_INFO "fb%d: %s frame buffer device\n",
  20.         fbinfo->node, fbinfo->fix.id);

  21.     return 0;
初始化所有的LCD控制寄存器,注册fbinfo,创建sysfs属性。其中s3c2410fb_init_registers的源码如下:

点击(此处)折叠或打开

  1. /*
  2.  * s3c2410fb_init_registers - Initialise all LCD-related registers
  3.  */
  4. static int s3c2410fb_init_registers(struct fb_info *info)
  5. {
  6.     struct s3c2410fb_info *fbi = info->par;
  7.     struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
  8.     unsigned long flags;
  9.     void __iomem *regs = fbi->io;
  10.     void __iomem *tpal;
  11.     void __iomem *lpcsel;

  12.     if (is_s3c2412(fbi)) {
  13.         tpal = regs + S3C2412_TPAL;
  14.         lpcsel = regs + S3C2412_TCONSEL;
  15.     } else {
  16.         tpal = regs + S3C2410_TPAL;
  17.         lpcsel = regs + S3C2410_LPCSEL;
  18.     }

  19.     /* Initialise LCD with values from haret */

  20.     local_irq_save(flags);

  21.     /* modify the gpio(s) with interrupts set (bjd) */

  22.     modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
  23.     modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
  24.     modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
  25.     modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);

  26.     local_irq_restore(flags);

  27.     dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
  28.     writel(mach_info->lpcsel, lpcsel);

  29.     dprintk("replacing TPAL %08x\n", readl(tpal));

  30.     /* ensure temporary palette disabled */
  31.     writel(0x00, tpal);

  32.     return 0;
  33. }

8.
s3c2410fb_remove函数分析。

点击(此处)折叠或打开

  1. static int s3c2410fb_remove(struct platform_device *pdev)
  2. {
  3.     struct fb_info *fbinfo = platform_get_drvdata(pdev);
  4.     struct s3c2410fb_info *info = fbinfo->par;
  5.     int irq;

  6.     unregister_framebuffer(fbinfo);
  7.     s3c2410fb_cpufreq_deregister(info);

  8.     s3c2410fb_lcd_enable(info, 0);
  9.     msleep(1);

  10.     s3c2410fb_unmap_video_memory(fbinfo);

  11.     if (info->clk) {
  12.         clk_disable(info->clk);
  13.         clk_put(info->clk);
  14.         info->clk = NULL;
  15.     }

  16.     irq = platform_get_irq(pdev, 0);
  17.     free_irq(irq, info);

  18.     iounmap(info->io);

  19.     release_resource(info->mem);
  20.     kfree(info->mem);

  21.     platform_set_drvdata(pdev, NULL);
  22.     framebuffer_release(fbinfo);

  23.     return 0;
  24. }
首先从平台设备中获得fb_info结构信息,然后停止LCD控制器,msleep(1)等待LCD停止,释放缓冲区,停止时钟,释放中断,释放内核空间,在内核中注销帧缓冲。该函数完成了与s3c2410_probe函数相反的工作。

注:
a.本文旨在将与友善之臂开发板配套的的linux-2.6.32.2内核移植到天嵌科技的TQ2440开发板上。
b. 硬件平台:天嵌科技TQ2440开发板(标配)
c. 软件平台:linux-2.6.32.2内核源码

                                              To be continued