linux framebuffer

Posted R1chie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\\n", 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\\n", 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\\n", 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\\n");
		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\\n");
		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\\n");

	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参数的函数。

以上是关于linux framebuffer的主要内容,如果未能解决你的问题,请参考以下文章

如何直接写入linux framebuffer?

直接访问linux framebuffer - copyarea

Linux framebuffer驱动和自定义文件操作

Linux下读取framebuffer(/dev/fb0)出来的数据全部是0,为啥会这样

如何通过linux framebuffer在屏幕上显示一些东西?

linux驱动之framebuffer