点击上方 蓝字关注我们!
前言
前面我们了解了LCD的基本架构《Linux驱动分析之LCD驱动架构》,接下来我们拿个具体的实例来分析分析。这样可以了解其大概是如何使用和工作的。
FrameBuffer驱动分析
内核版本:4.20
芯片平台:s3c2410
依然是使用之前的方式进行分析,大部分内容在注释。
(1)装载和卸载函数
static struct platform_driver s3c2410fb_driver = {.probe = s3c2410fb_probe,.remove = s3c2410fb_remove,.suspend = s3c2410fb_suspend,.resume = s3c2410fb_resume,.driver = {.name = "s3c2410-lcd",},};int __init s3c2410fb_init(void){int ret = platform_driver_register(&s3c2410fb_driver);return ret;}static void __exit s3c2410fb_cleanup(void){platform_driver_unregister(&s3c2410fb_driver);}module_init(s3c2410fb_init);module_exit(s3c2410fb_cleanup);
上面比较简单,就是注册一个platform_driver, 控制器一般都是使用platform总线。
(2)probe()
static int s3c2410fb_probe(struct platform_device *pdev){return s3c24xxfb_probe(pdev, DRV_S3C2410);}static int 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 = dev_get_platdata(&pdev->dev);//省略......//display包含了屏幕的分辨率信息等display = mach_info->displays + mach_info->default_display;//获取中断号irq = platform_get_irq(pdev, 0);//分配一个fb_infofbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);//保存fbinfo到driver dataplatform_set_drvdata(pdev, fbinfo);info = fbinfo->par;info->dev = &pdev->dev;info->drv_type = drv_type;//获取IO资源并申请res = platform_get_resource(pdev, IORESOURCE_MEM, 0);size = resource_size(res);info->mem = request_mem_region(res->start, size, pdev->name);//映射为虚拟地址info->io = ioremap(res->start, size);//获取中断基地址info->irq_base = info->io + S3C2410_LCDINTBASE;strcpy(fbinfo->fix.id, driver_name);//操作寄存器,停止LCDC输出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, 0, pdev->name, info);//获取时钟并使能info->clk = clk_get(NULL, "lcd");clk_prepare_enable(info->clk);usleep_range(1000, 1100);info->clk_rate = clk_get_rate(info->clk);/* 计算显示所需分配的内存大小 */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;}/* 初始化显存,这里面设置了DMA */ret = s3c2410fb_map_video_memory(fbinfo);//根据屏幕信息初始化fbinfo中的可变参数fbinfo->var.xres = display->xres;fbinfo->var.yres = display->yres;fbinfo->var.bits_per_pixel = display->bpp;//初始化LCDC相关寄存器s3c2410fb_init_registers(fbinfo);//注册framebufferret = register_framebuffer(fbinfo);return 0;}
上面省略了一些错误判断。
上面总结下来就两个部分:
1. 根据屏幕信息填充fb_info, 然后调用register_framebuffer进行注册
2. LCDC相对应的寄存器的配置(硬件平台相关的)
(3)各种操作屏幕的函数
static struct fb_ops s3c2410fb_ops = {.owner = THIS_MODULE,.fb_check_var = s3c2410fb_check_var, //检查可变参数合法性.fb_set_par = s3c2410fb_set_par, //设置可变参数.fb_blank = s3c2410fb_blank, //设置开关屏.fb_setcolreg = s3c2410fb_setcolreg, //设置颜色寄存器//下面三个都是使用内核自带的函数.fb_fillrect = cfb_fillrect, //绘制矩形.fb_copyarea = cfb_copyarea, //区域拷贝.fb_imageblit = cfb_imageblit,//绘制位图};
简单看几个函数,其实就是对寄存器的操作。
s3c2410fb_set_par
static int s3c2410fb_set_par(struct fb_info *info){//设置参数信息struct fb_var_screeninfo *var = &info->var;switch (var->bits_per_pixel) {case 32:case 16:case 12:info->fix.visual = FB_VISUAL_TRUECOLOR;break;case 1:info->fix.visual = FB_VISUAL_MONO01;break;default:info->fix.visual = FB_VISUAL_PSEUDOCOLOR;break;}info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;//设置寄存器,该函数中都是寄存器的操作s3c2410fb_activate_var(info);return 0;}
s3c2410fb_blank
static int s3c2410fb_blank(int blank_mode, struct fb_info *info){struct s3c2410fb_info *fbi = info->par;void __iomem *tpal_reg = fbi->io;dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;//根据开关设置寄存器if (blank_mode == FB_BLANK_POWERDOWN)s3c2410fb_lcd_enable(fbi, 0);elses3c2410fb_lcd_enable(fbi, 1);if (blank_mode == FB_BLANK_UNBLANK)writel(0x0, tpal_reg);else {dprintk("setting TPAL to output 0x000000\n");writel(S3C2410_TPAL_EN, tpal_reg);}return 0;}
这些操作函数和裸机程序基本差不多,就是一些对寄存器的操作。
总结
上面其实没有多少内容,我们并不需要过多关注细节。比如寄存器配置的含义之类的,因为每个平台都不一样,即使你把这款芯片的所有细节理解了,换个平台,依然要重新来。所以我们应该理解的是框架。
随着技术的发展,特别是GPU的出现,单纯使用Framebuffer来显示越来越少,它已经渐渐成为DRM的一部分了。特别是现在的Android设备, 对显示要求越来越高。后期会带来一些DRM相关的文章!
end
往期推荐
长按识别图中二维码关注
文章转载自嵌入式软件开发交流,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




