zoukankan      html  css  js  c++  java
  • linux framebuffer

    1.从fbmem.c入手

    • 内核版本:linux 4.9

    1.1 fbmem_init

    static int __init
    fbmem_init(void)
    {
    	int ret;
    
    	if (!proc_create("fb", 0, NULL, &fb_proc_fops))
    		return -ENOMEM;
    
    	ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
    	if (ret) {
    		printk("unable to get major %d for fb devs
    ", FB_MAJOR);
    		goto err_chrdev;
    	}
    
    	fb_class = class_create(THIS_MODULE, "graphics");
    	if (IS_ERR(fb_class)) {
    		ret = PTR_ERR(fb_class);
    		pr_warn("Unable to create fb class; errno = %d
    ", ret);
    		fb_class = NULL;
    		goto err_class;
    	}
    	return 0;
    
    err_class:
    	unregister_chrdev(FB_MAJOR, "fb");
    err_chrdev:
    	remove_proc_entry("fb", NULL);
    	return ret;
    }
    

    ​ 首先在/proc下面创建一个名字为fb的节点

    register_chrdev(FB_MAJOR, "fb", &fb_fops);向内核一个名字为fb,主设备号为29的设备节点。

    fb_class = class_create(THIS_MODULE, "graphics");创建一个名字为graphics的类。可以在/sys/class中看到graphics这个类。

    ​ 还有一个比较重要的地方,就是关于fb_fops结构体,这里面有对应的fb_open、fb_ioctl、fb_read等,如果你进去看这几个地方,就会发现,它会去遍历 registered_fb这个数组,并且使用对应fb的fb_ops操作,那么其实这里的fb_fops是一个中转的操作。

    1.2 register_framebuffer

    static int do_register_framebuffer(struct fb_info *fb_info)
    {
    	int i, ret;
    	struct fb_event event;
    	struct fb_videomode mode;
    
    	if (fb_check_foreignness(fb_info))
    		return -ENOSYS;
    
    	ret = do_remove_conflicting_framebuffers(fb_info->apertures,
    						 fb_info->fix.id,
    						 fb_is_primary_device(fb_info));
    	if (ret)
    		return ret;
    
    	if (num_registered_fb == FB_MAX)
    		return -ENXIO;
    
    	num_registered_fb++;
    	for (i = 0 ; i < FB_MAX; i++)
    		if (!registered_fb[i])
    			break;
    	fb_info->node = i;
    	atomic_set(&fb_info->count, 1);
    	mutex_init(&fb_info->lock);
    	mutex_init(&fb_info->mm_lock);
    
    	fb_info->dev = device_create(fb_class, fb_info->device,
    				     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    	if (IS_ERR(fb_info->dev)) {
    		/* Not fatal */
    		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld
    ", i, PTR_ERR(fb_info->dev));
    		fb_info->dev = NULL;
    	} else
    		fb_init_device(fb_info);
    
    	if (fb_info->pixmap.addr == NULL) {
    		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
    		if (fb_info->pixmap.addr) {
    			fb_info->pixmap.size = FBPIXMAPSIZE;
    			fb_info->pixmap.buf_align = 1;
    			fb_info->pixmap.scan_align = 1;
    			fb_info->pixmap.access_align = 32;
    			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
    		}
    	}	
    	fb_info->pixmap.offset = 0;
    
    	if (!fb_info->pixmap.blit_x)
    		fb_info->pixmap.blit_x = ~(u32)0;
    
    	if (!fb_info->pixmap.blit_y)
    		fb_info->pixmap.blit_y = ~(u32)0;
    
    	if (!fb_info->modelist.prev || !fb_info->modelist.next)
    		INIT_LIST_HEAD(&fb_info->modelist);
    
    	if (fb_info->skip_vt_switch)
    		pm_vt_switch_required(fb_info->dev, false);
    	else
    		pm_vt_switch_required(fb_info->dev, true);
    
    	fb_var_to_videomode(&mode, &fb_info->var);
    	fb_add_videomode(&mode, &fb_info->modelist);
    	registered_fb[i] = fb_info;
    
    	event.info = fb_info;
    	if (!lockless_register_fb)
    		console_lock();
    	if (!lock_fb_info(fb_info)) {
    		if (!lockless_register_fb)
    			console_unlock();
    		return -ENODEV;
    	}
    
    	fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    	unlock_fb_info(fb_info);
    	if (!lockless_register_fb)
    		console_unlock();
    	return 0;
    }
    

    ​ 有一个重要的地方 registered_fb[i] = fb_info;,往这个数组填充fb_info, registered_fb,是由内核管理的,在应用程序中对某个fb节点的操作,内核就会到这个数组找到对应的fb,并且使用这个fb对应的fops函数。

    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);是一个通知函数,最终会调用fbcon.c中的回调函数fbcon_event_notify,然后调用fbcon_fb_registered进行fb的注册。

    ​ 通过分析基本上就能知道,需要填充fb_info这个结构体。

    2.分析mxsfb.c

    ​ 分析一个驱动的入口,要么就是module_init(xxx_init),要么就是xxx_probe。为什么会出现这两种入口?原因是后者引入了设备树,其实设备节点会被转换成一个platform_device。而前者就是自己手动写一个platform_device出来。

    2.1 mxsfb_probe

    ​ 那么mxsfb显然使用了设备树的。那么就从mxsfb_probe开始分析:

    static int mxsfb_probe(struct platform_device *pdev)
    {
    	const struct of_device_id *of_id =
    			of_match_device(mxsfb_dt_ids, &pdev->dev);
    	struct resource *res;
    	struct mxsfb_info *host;
    	struct fb_info *fb_info;
    	struct fb_videomode *mode;
    	int ret;
    
    	if (of_id)
    		pdev->id_entry = of_id->data;
    
    	fb_info = framebuffer_alloc(sizeof(struct mxsfb_info), &pdev->dev);
    	if (!fb_info) {
    		dev_err(&pdev->dev, "Failed to allocate fbdev
    ");
    		return -ENOMEM;
    	}
    
    	mode = devm_kzalloc(&pdev->dev, sizeof(struct fb_videomode),
    			GFP_KERNEL);
    	if (mode == NULL)
    		return -ENOMEM;
    
    	host = to_imxfb_host(fb_info);
    
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    	host->base = devm_ioremap_resource(&pdev->dev, res);
    	if (IS_ERR(host->base)) {
    		ret = PTR_ERR(host->base);
    		goto fb_release;
    	}
    
    	host->pdev = pdev;
    	platform_set_drvdata(pdev, host);
    
    	host->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
    
    	host->clk = devm_clk_get(&host->pdev->dev, NULL);
    	if (IS_ERR(host->clk)) {
    		ret = PTR_ERR(host->clk);
    		goto fb_release;
    	}
    
    	host->clk_axi = devm_clk_get(&host->pdev->dev, "axi");
    	if (IS_ERR(host->clk_axi))
    		host->clk_axi = NULL;
    
    	host->clk_disp_axi = devm_clk_get(&host->pdev->dev, "disp_axi");
    	if (IS_ERR(host->clk_disp_axi))
    		host->clk_disp_axi = NULL;
    
    	host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
    	if (IS_ERR(host->reg_lcd))
    		host->reg_lcd = NULL;
    
    	fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,
    					       GFP_KERNEL);
    	if (!fb_info->pseudo_palette) {
    		ret = -ENOMEM;
    		goto fb_release;
    	}
    
    	ret = mxsfb_init_fbinfo(host, mode);
    	if (ret != 0)
    		goto fb_release;
    
    	fb_videomode_to_var(&fb_info->var, mode);
    
    	/* init the color fields */
    	mxsfb_check_var(&fb_info->var, fb_info);
    
    	platform_set_drvdata(pdev, fb_info);
    
    	ret = register_framebuffer(fb_info);
    	if (ret != 0) {
    		dev_err(&pdev->dev,"Failed to register framebuffer
    ");
    		goto fb_destroy;
    	}
    
    	if (!host->enabled) {
    		mxsfb_enable_axi_clk(host);
    		writel(0, host->base + LCDC_CTRL);
    		mxsfb_disable_axi_clk(host);
    		mxsfb_set_par(fb_info);
    		mxsfb_enable_controller(fb_info);
    	}
    
    	dev_info(&pdev->dev, "initialized
    ");
    
    	return 0;
    
    fb_destroy:
    	if (host->enabled)
    		clk_disable_unprepare(host->clk);
    fb_release:
    	framebuffer_release(fb_info);
    
    	return ret;
    }
    

    ​ 从一开始就有一个值得注意的地方,就是framebuffer_alloc的使用,传进大小是struct mxsfb_info结构体的大小,一开始我还挺懵的,难道不应该是传fb_info的大小吗?然后往下再看就明白了。原来虽然是申请的大小不是fb_info,但是下面的to_imxfb_host函数实际上是container_of的宏定义,并且值得注意的是,fb_info是mxsfb_info的第一个成员,所以才这么使用。

    ​ 申请完了内存,就该完善里面的参数了。里面使用了很多of类函数来获取设备树中的lcd寄存器地址,并且使用ioremap进行映射。mxsfb_init_fbinfo这里就是填充fb_info了,并且使用DMA。

    ​ 最后就是调用register_framebuffer注册fb_info了。

    顺便看看mxs_ops:

    static struct fb_ops mxsfb_ops = {
    	.owner = THIS_MODULE,
    	.fb_check_var = mxsfb_check_var,
    	.fb_set_par = mxsfb_set_par,
    	.fb_setcolreg = mxsfb_setcolreg,
    	.fb_blank = mxsfb_blank,
    	.fb_pan_display = mxsfb_pan_display,
    	.fb_fillrect = cfb_fillrect,
    	.fb_copyarea = cfb_copyarea,
    	.fb_imageblit = cfb_imageblit,
    };
    

    这是一些关于屏幕的操作,例如填充、划线、清屏等。还有设置lcd参数的函数。

  • 相关阅读:
    2019 SDN上机第5次作业
    hdu 2553 N皇后问题(递归)
    百练oj 2766 最大子矩阵和
    POJ 1664 放苹果
    POJ 3617 Best Cow Line(贪心)
    HDU 2013 ACM/ICPC Asia Regional Hangzhou Online ------ Zhuge Liang's Mines
    HDU 4712 Hamming Distance (随机算法)
    HDU 1171 Big Event in HDU
    HDU 1085 Holding Bin-Laden Captive!
    HDU 1028 母函数
  • 原文地址:https://www.cnblogs.com/r1chie/p/14792378.html
Copyright © 2011-2022 走看看