linux lcd设备驱动剖析二
上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18188259
上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
plain?
- 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; /* 包含s3c2410fb_display */
- struct resource *res;
- int ret;
- int irq;
- int i;
- int size;
- u32 lcdcon1;
- /* s3c24xx_fb_set_platdata()里会设置platform_data
- * tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
- * 所以这里传入来的platform_data就是tq2440_fb_info结构体实例
- */
- mach_info = pdev->dev.platform_data;
- /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL */
- if (mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- return -EINVAL; /* 表示无效的参数 */
- }
- /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
- 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指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
- display = mach_info->displays + mach_info->default_display;
- /* 通过平台设备platform_device获得IRQ
- * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)
- */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
- /* 分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间
- * 用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
- * 比如:clk,resource,io,irq_base,drv_type等额外信息
- */
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM; /* 返回NULL表示失败 */
- /* 相当于pdev->dev->driver_data = fbinfo */
- platform_set_drvdata(pdev, fbinfo);
- /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
- info = fbinfo->par; /* 将私有数据赋给info指针变量 */
- info->dev = &pdev->dev; /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
- info->drv_type = drv_type; /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
- /* 通过平台设备platform_device获得资源(IO) */
- 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; /* 资源的大小 */
- /* 申请以res->start地址开始大小为size的I/O内存 */
- 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;
- }
- /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
- 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 + 0x54,刚好是LCDINTPND寄存器的地址 */
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
- dprintk("devinit\n");
- /* 驱动名,fbinfo->fix.id = s3c2410fb */
- strcpy(fbinfo->fix.id, driver_name);
- /* Stop the video */
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- /* 禁止Video output */
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
- /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
- 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_info结构体通用的可变参数fb_var_screeninfo结构体 */
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
- /* 设置fb_ops结构体 */
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- /* 设置假调色板 */
- fbinfo->pseudo_palette = &info->pseudo_pal;
- /* palette_buffer[i] = 0x80000000,清空调色板 */
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- /* 申请中断,s3c2410fb_irq是中断处理函数 */
- 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;
- }
- /* 获取lcd时钟 */
- 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;
- }
- /* 使能lcd时钟 */
- clk_enable(info->clk);
- dprintk("got and enabled clock\n");
- msleep(1);
- /* find maximum required memory size for display */
- /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
- * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
- * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
- */
- for (i = 0; i < mach_info->num_displays; i++) { /* 这里mach_info->num_displays = 1 */
- unsigned long smem_len = mach_info->displays[i].xres; /* x方向分辨率 */
- smem_len *= mach_info->displays[i].yres; /* y方向分辨率 */
- smem_len *= mach_info->displays[i].bpp; /* bpp */
- smem_len >>= 3; /* smem_len除以8 */
- 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");
- /* display指向tq2440_lcd_cfg */
- fbinfo->var.xres = display->xres; /* 设置x方向的分辨率 */
- fbinfo->var.yres = display->yres; /* 设置y方向的分辨率 */
- fbinfo->var.bits_per_pixel = display->bpp; /* 设置bpp位数 */
- /* 初始化LCD相关的寄存器 */
- s3c2410fb_init_registers(fbinfo);
- /* 检查可变参数 */
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
- /* 注册fb_info结构体 */
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register framebuffer device: %d\n",
- ret);
- goto free_video_memory;
- }
- /* create device files */
- ret = device_create_file(&pdev->dev, &dev_attr_debug);
- if (ret) {
- printk(KERN_ERR "failed to add debug attribute\n");
- }
- /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device */
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- fbinfo->node, fbinfo->fix.id);
- return 0;
- free_video_memory:
- s3c2410fb_unmap_video_memory(fbinfo);
- release_clock:
- clk_disable(info->clk); /* 禁止lcd时钟 */
- clk_put(info->clk); /* 删除lcd时钟 */
- release_irq:
- free_irq(irq, info); /* 释放IRQ */
- release_regs:
- iounmap(info->io); /* 解除映射 */
- release_mem:
- release_resource(info->mem); /* 释放资源 */
- kfree(info->mem); /* 释放刚申请的内存 */
- dealloc_fb:
- platform_set_drvdata(pdev, NULL); /* 相当于pdev->dev->driver_data = NULL */
- framebuffer_release(fbinfo); /* 释放fb_info结构体 */
- return ret;
- }
拆分详解:
一、获得平台数据
plain?
- mach_info = pdev->dev.platform_data;
- /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL */
- if (mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- return -EINVAL; /* 表示无效的参数 */
- }
s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
plain?
- static void __init tq2440_machine_init(void)
- {
- /* 初始化tq2440_fb_info实体结构体 */
- s3c24xx_fb_set_platdata(&tq2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
- /* 添加tq2440_devices到内核,它是platform_device类的设备 */
- platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
- EmbedSky_machine_init();
- s3c2410_gpio_setpin(S3C2410_GPG12, 0);
- s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);
- s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);
- }
s3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
plain?
- void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
- {
- struct s3c2410fb_mach_info *npd;
- /* 申请分配s3c2410fb_mach_info大小的内存 */
- npd = kmalloc(sizeof(*npd), GFP_KERNEL);
- if (npd) {
- /* 拷贝s3c2410fb_mach_info型实体给npd */
- memcpy(npd, pd, sizeof(*npd));
- /* 最后将s3c2410fb_mach_info型实体赋给platform_data */
- s3c_device_lcd.dev.platform_data = npd;
- } else {
- printk(KERN_ERR "no memory for LCD platform data\n");
- }
- }
二、设置s3c2410fb_display指向tq2440_lcd_cfg
plain?
- /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
- 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指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
- display = mach_info->displays + mach_info->default_display;
三、获得IRQ资源
plain?
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
四、分配fb_info内存
plain?
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM; /* 返回NULL表示失败 */
framebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。
五、设置s3c2410fb_info结构体
plain?
- /* 相当于pdev->dev->driver_data = fbinfo */
- platform_set_drvdata(pdev, fbinfo);
- /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
- info = fbinfo->par; /* 将私有数据赋给info指针变量 */
- info->dev = &pdev->dev; /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
- info->drv_type = drv_type; /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
六、获取IO资源,映射IO
plain?
- /* 通过平台设备platform_device获得资源(IO) */
- 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; /* 资源的大小 */
- /* 申请以res->start地址开始大小为size的I/O内存 */
- 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;
- }
- /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
- 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 + 0x54,刚好是LCDINTPND寄存器的地址 */
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
七、读写LCDCON1,禁止视频数据输出
plain?
- /* Stop the video */
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- /* 禁止Video output */
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
plain?
- /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
- 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_info结构体通用的可变参数fb_var_screeninfo结构体 */
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
- /* 设置fb_ops结构体 */
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- /* 设置假调色板 */
- fbinfo->pseudo_palette = &info->pseudo_pal;
- /* palette_buffer[i] = 0x80000000,清空调色板 */
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
九、申请中断、获取LCD时钟,使能LCD时钟
plain?
- /* 申请中断,s3c2410fb_irq是中断处理函数 */
- 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;
- }
- /* 获取lcd时钟 */
- 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;
- }
- /* 使能lcd时钟 */
- clk_enable(info->clk);
- dprintk("got and enabled clock\n");
十、计算显存大小、分配显存内存
plain?
- /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
- * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
- * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
- */
- for (i = 0; i < mach_info->num_displays; i++) { /* 这里mach_info->num_displays = 1 */
- unsigned long smem_len = mach_info->displays[i].xres; /* x方向分辨率 */
- smem_len *= mach_info->displays[i].yres; /* y方向分辨率 */
- smem_len *= mach_info->displays[i].bpp; /* bpp */
- smem_len >>= 3; /* smem_len除以8 */
- 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;
- }
十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
plain?
- /* display指向tq2440_lcd_cfg */
- fbinfo->var.xres = display->xres; /* 设置x方向的分辨率 */
- fbinfo->var.yres = display->yres; /* 设置y方向的分辨率 */
- fbinfo->var.bits_per_pixel = display->bpp; /* 设置bpp位数 */
十二、LCD相关寄存器的设置和fb_info的可变参数的检测
plain?
- /* 初始化LCD相关的寄存器 */
- s3c2410fb_init_registers(fbinfo);
- /* 检查可变参数 */
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
十三、注册fb_info结构体
plain?
- /* 注册fb_info结构体 */
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register framebuffer device: %d\n",
- ret);
- goto free_video_memory;
- }
linux lcd设备驱动剖析二的更多相关文章
- linux lcd设备驱动剖析四
在"linux lcd设备驱动剖析二"文章中,我们详细分析了s3c24xxfb_probe函数. 文章链接:http://blog.csdn.net/lwj103862095/ar ...
- linux lcd设备驱动剖析一
s3c2440 lcd驱动源码文件是:drivers/video/s3c2410fb.c 看驱动源码首先当然是先看入口函数,这里是s3c2410fb_init函数 [cpp] view plain? ...
- linux lcd设备驱动剖析三
上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体 [cpp] view plain? static struct fb_ops s3 ...
- 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(二)
我们在 浅谈Linux PCI设备驱动(一)中(以下简称 浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设 ...
- Linux 块设备驱动 (二)
linux下Ramdisk驱动 1 什么是Ramdisk Ramdisk是一种模拟磁盘,其数据实际上是存储在RAM中,它使用一部分内存空间来模拟出一个磁盘设备,并以块设备的方式来组织和访问这片内存.对 ...
- linux 块设备驱动(二)——块设备数据结构
本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块设备相关的数据结构以及接口: 块设备接口则相对复 ...
- 深入理解Linux字符设备驱动
文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...
- Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】
本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...
- Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门
Linux FC/iSCSI存储设备管理系列(一):Linux系统设备驱动入门 转载请在文首保留原文出处:EMC中文支持论坛 - https://community.emc.com/go/chines ...
随机推荐
- 负载均衡之DNS域名解析
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38017027 在上一篇文章(http://blog.csdn.net/cywosp/arti ...
- python脚本4_求1到5阶乘之和
#求1到5阶乘之和 # a = 1 sum = 0 for i in range(1,6): a = i*a sum = sum+a print(sum)
- jquery二维码生成插件jquery.qrcode.js
插件描述:jquery.qrcode.js 是一个能够在客户端生成矩阵二维码QRCode 的jquery插件 ,使用它可以很方便的在页面上生成二维条码. 转载于:http://www.jq22.com ...
- JSON字符串与JSON对象的互相转换
比如工作中遇到的app强制退出功能的参数问题 首先定义一个JSON字符串 objectStr : String value=UUID.randomUUID().toString();SimpleDat ...
- opencv:摄像头和视频的读取
示例代码: #include <opencv.hpp> using namespace cv; int main() { VideoCapture Capture(); //打开默认摄像头 ...
- 【2018年全国多校算法寒假训练营练习比赛(第四场)- E】通知小弟(强连通缩点)
题目链接:https://www.nowcoder.com/acm/contest/76/E 题目描述 在战争时期,A国派出了许多间谍到其他国家去收集情报.因为间谍需要隐秘自己的身份, ...
- Android界面View及ViewGroup学习 《转载》
View及ViewGroup类关系 Android View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的. View ...
- 剑指offer--33.丑数
本来用数组做标志位,但是测试数据有第1500个,859963392,惹不起哦 ------------------------------------------------------------- ...
- 【tensorflow:Google】三、tensorflow入门
[一]计算图模型 节点是计算,边是数据流, a = tf.constant( [1., 2.] )定义的是节点,节点有属性 a.graph 取得默认计算图 g1 = tf.get_default_gr ...
- [置顶]
Android 关于ToolBar分分钟玩死自己?
场景一: 今天早上十点高高兴兴的跟平时早上一样买一杯粥然后一边喝着一边去上班,步行了15分钟到了公司,然后打指纹开门,然后就愉快的写代码了,我擦,好想电脑没开机,我晕好像没带眼镜,发现最近记性不是很好 ...