基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.2(LCD驱动源码分析及移植之platform driver)
2012-05-15 21:10:37
分类: LINUX
5.
LCD驱动模块的注册与注销:
注册与注销模块由module_init宏与module_exit
宏指定。
6.
LCD平台设备驱动 s3c2410fb_driver。
该结构定义了LCD驱动程序的名字:
"s3c2410-lcd",以及探测函数probe,移除函数remove,电源挂起函数suspend和电源恢复函数resume。
7.
探测函数s3c2410fb_probe 分析。
由此可见,真正的探测函数为 s3c24xxfb_probe
。
7.1
第一行用于获取platform device中定义的平台数据(已经在本博客的上一篇文章中提及)。其中的
mach_info定义如下:
主要是记录LCD的一些属性,包括与显示参数相关的“ struct s3c2410fb_display
*displays
; ”及使用的GPIO引脚。而
”if
(mach_info
-
>default_display
>
=mach_info
-
>num_displays
)
{...}“用来查看显示屏幕的具体规格。
7.2
获取设备中断号(定义于mach-smdk2440.c中)。
7.3
在内存中申请sizeof(struct s3c2410fb_info)+sizeof(struct fb_info)
大小的空间,然后将获得的
fbinfo设置为平台设备的驱动数据。其中
framebuffer_alloc源码如下:
其中 platform_set_drvdata宏如下:
7.4
设置驱动数据。
7.5
映射内核资源(IO口,中断号,硬件地址转虚拟地址)。
7.6
关闭显示屏幕
7.7
对fbinfo进行赋值。
7.8
映射中断响应函数。
7.9
打开LCD时钟并使能。
7.10
计算显示内存所需要的容量。
7.11
初始化所有的LCD控制寄存器,注册fbinfo,创建sysfs属性。其中
s3c2410fb_init_registers的源码如下:
8.
s3c2410fb_remove函数分析。
首先从平台设备中获得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
LCD驱动模块的注册与注销:
点击(此处)折叠或打开
- int __init s3c2410fb_init(void)
- {
- int ret = platform_driver_register(&s3c2410fb_driver);
-
- if (ret == 0)
- ret = platform_driver_register(&s3c2412fb_driver);
-
- return ret;
- }
-
- static void __exit s3c2410fb_cleanup(void)
- {
- platform_driver_unregister(&s3c2410fb_driver);
- platform_driver_unregister(&s3c2412fb_driver);
- }
-
- module_init(s3c2410fb_init);
- module_exit(s3c2410fb_cleanup);
6.
LCD平台设备驱动 s3c2410fb_driver。
点击(此处)折叠或打开
- 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,
- },
- };
-
- static struct platform_driver s3c2412fb_driver = {
- .probe = s3c2412fb_probe,
- .remove = s3c2410fb_remove,
- .suspend = s3c2410fb_suspend,
- .resume = s3c2410fb_resume,
- .driver = {
- .name = "s3c2412-lcd",
- .owner = THIS_MODULE,
- },
- };
7.
探测函数s3c2410fb_probe 分析。
点击(此处)折叠或打开
- static int __init s3c2410fb_probe(struct platform_device *pdev)
- {
- return s3c24xxfb_probe(pdev, DRV_S3C2410);
- }
-
- static int __init s3c2412fb_probe(struct platform_device *pdev)
- {
- return s3c24xxfb_probe(pdev, DRV_S3C2412);
- }
点击(此处)折叠或打开
- 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;
- 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;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
-
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, fbinfo);
-
- info = fbinfo->par;
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 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);
- 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);
-
- 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;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->fbops = &s3c2410fb_ops;
- 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);
- 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);
- 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;
-
- smem_len *= mach_info->displays[i].yres;
- smem_len *= mach_info->displays[i].bpp;
- 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);
- 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_init_registers(fbinfo);
-
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
-
- 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);
- 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;
- }
7.1
点击(此处)折叠或打开
- mach_info = pdev->dev.platform_data;
- 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;
点击(此处)折叠或打开
- struct s3c2410fb_mach_info *mach_info;
点击(此处)折叠或打开
- struct s3c2410fb_mach_info {
-
- struct s3c2410fb_display *displays; /* attached diplays info */
- unsigned num_displays; /* number of defined displays */
- unsigned default_display;
-
- /* GPIOs */
-
- unsigned long gpcup;
- unsigned long gpcup_mask;
- unsigned long gpccon;
- unsigned long gpccon_mask;
- unsigned long gpdup;
- unsigned long gpdup_mask;
- unsigned long gpdcon;
- unsigned long gpdcon_mask;
-
- /* lpc3600 control register */
- unsigned long lpcsel;
- };
7.2
点击(此处)折叠或打开
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
7.3
点击(此处)折叠或打开
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, fbinfo);
点击(此处)折叠或打开
- /**
- * framebuffer_alloc - creates a new frame buffer info structure
- *
- * @size: size of driver private data, can be zero
- * @dev: pointer to the device for this fb, this can be NULL
- *
- * Creates a new frame buffer info structure. Also reserves @size bytes
- * for driver private data (info->par). info->par (if any) will be
- * aligned to sizeof(long).
- *
- * Returns the new structure, or NULL if an error occured.
- *
- */
- struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
- {
- #define BYTES_PER_LONG (BITS_PER_LONG/8)
- #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
- int fb_info_size = sizeof(struct fb_info);
- struct fb_info *info;
- char *p;
-
- if (size)
- fb_info_size += PADDING;
-
- p = kzalloc(fb_info_size + size, GFP_KERNEL);
-
- if (!p)
- return NULL;
-
- info = (struct fb_info *) p;
-
- if (size)
- info->par = p + fb_info_size;
-
- info->device = dev;
-
- #ifdef CONFIG_FB_BACKLIGHT
- mutex_init(&info->bl_curve_mutex);
- #endif
-
- return info;
- #undef PADDING
- #undef BYTES_PER_LONG
- }
- EXPORT_SYMBOL(framebuffer_alloc);
点击(此处)折叠或打开
- #define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data))
7.4
点击(此处)折叠或打开
- info = fbinfo->par;
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
7.5
点击(此处)折叠或打开
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 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);
- 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;
- }
7.6
点击(此处)折叠或打开
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
7.7
点击(此处)折叠或打开
- 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;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &info->pseudo_pal;
-
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
7.8
点击(此处)折叠或打开
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
- if (ret) {
- dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
- ret = -EBUSY;
- goto release_regs;
- }
7.9
点击(此处)折叠或打开
- 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);
- dprintk("got and enabled clock\n");
-
- msleep(1);
-
- info->clk_rate = clk_get_rate(info->clk);
7.10
点击(此处)折叠或打开
- /* 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;
-
- smem_len *= mach_info->displays[i].yres;
- smem_len *= mach_info->displays[i].bpp;
- 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);
- if (ret) {
- printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
- ret = -ENOMEM;
- goto release_clock;
- }
-
- dprintk("got video memory\n");
7.11
点击(此处)折叠或打开
- s3c2410fb_init_registers(fbinfo);
-
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
-
- 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);
- 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;
点击(此处)折叠或打开
- /*
- * s3c2410fb_init_registers - Initialise all LCD-related registers
- */
- static int s3c2410fb_init_registers(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
- unsigned long flags;
- void __iomem *regs = fbi->io;
- void __iomem *tpal;
- void __iomem *lpcsel;
-
- if (is_s3c2412(fbi)) {
- tpal = regs + S3C2412_TPAL;
- lpcsel = regs + S3C2412_TCONSEL;
- } else {
- tpal = regs + S3C2410_TPAL;
- lpcsel = regs + S3C2410_LPCSEL;
- }
-
- /* Initialise LCD with values from haret */
-
- local_irq_save(flags);
-
- /* modify the gpio(s) with interrupts set (bjd) */
-
- modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
- modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
- modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
- modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
-
- local_irq_restore(flags);
-
- dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
- writel(mach_info->lpcsel, lpcsel);
-
- dprintk("replacing TPAL %08x\n", readl(tpal));
-
- /* ensure temporary palette disabled */
- writel(0x00, tpal);
-
- return 0;
- }
8.
s3c2410fb_remove函数分析。
点击(此处)折叠或打开
- static int s3c2410fb_remove(struct platform_device *pdev)
- {
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct s3c2410fb_info *info = fbinfo->par;
- int irq;
-
- unregister_framebuffer(fbinfo);
- s3c2410fb_cpufreq_deregister(info);
-
- s3c2410fb_lcd_enable(info, 0);
- msleep(1);
-
- s3c2410fb_unmap_video_memory(fbinfo);
-
- if (info->clk) {
- clk_disable(info->clk);
- clk_put(info->clk);
- info->clk = NULL;
- }
-
- irq = platform_get_irq(pdev, 0);
- free_irq(irq, info);
-
- iounmap(info->io);
-
- release_resource(info->mem);
- kfree(info->mem);
-
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(fbinfo);
-
- return 0;
- }
注:
a.本文旨在将与友善之臂开发板配套的的linux-2.6.32.2内核移植到天嵌科技的TQ2440开发板上。
b. 硬件平台:天嵌科技TQ2440开发板(标配)
c. 软件平台:linux-2.6.32.2内核源码
To be continued