imx7 lcd驱动分析 1

 在imx7的开发板上使用LCD液晶屏,现在跟踪一下lcd的驱动。

1.注册framebuffer设备,这是一个字符设备。

内核驱动对应的文件为kernel-source/drivers/video/fbdev/core/fbmem.c

module_init(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;
}

//The name of this device has nothing to do with the name of the device in /dev.2

register_chrdev(FB_MAJOR, "fb", &fb_fops);   

把这个设备注册到系统中,加入到全局变量chrdevs[CHRDEV_MAJOR_HASH_SIZE],这个fb名字跟dev目录下面的名字没有关系。

注册成功后可以在/proc/devices中看到

# cat /proc/devices 
Character devices:
29 fb

fb_fops这个是操作函数,当应用操作这类设备的时候会调用这个函数。(后面分析应用调用的时候会用到) 。在linux设备驱动中,所有的显示缓存设备均由framebuffer子系统内部管理,即linux设备驱动框架只认识一个主设备号为29的framebuffer设备。应用层所有针对显示缓存(最多32个)的访问均会推送给fb_fops进行进一步分发操作。

static const struct file_operations fb_fops = {
    .owner =    THIS_MODULE,
    .read =        fb_read,
    .write =    fb_write,
    .unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = fb_compat_ioctl,
#endif
    .mmap =        fb_mmap,
    .open =        fb_open,

    .release =    fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
    .get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    .fsync =    fb_deferred_io_fsync,
#endif
    .llseek =    default_llseek,
};

新建一个全局的fb_class,用于生成设备文件,后面生成/dev/fb0的时候会用到。

fb_class = class_create(THIS_MODULE, "graphics"); 
 

2.lcd驱动分析

在dts文件中查找lcdif的compatible 为"fsl,imx7d-lcdif", "fsl,imx28-lcdif";

内核驱动对应的文件为kernel-source/drivers/video/fbdev/mxsfb.c

lcdif: lcdif@30730000 {
	compatible = "fsl,imx7d-lcdif", "fsl,imx28-lcdif";
	reg = <0x30730000 0x10000>;
	display = <&display0>;	
	status = "okay";
	display0: display {
		bits-per-pixel = <32>;
		bus-width = <24>;
		display-timings {
			native-mode = <&timing0>;
			timing0: ETM0700G0DH6 {
				clock-frequency = <33000000>;
				hactive = <800>;
				vactive = <480>;
				hfront-porch = <40>;
				hback-porch = <216>;
				hsync-len = <128>;
				vback-porch = <35>;
				vfront-porch = <10>;
				vsync-len = <2>;
				hsync-active = <0>;
				vsync-active = <0>;
				de-active = <1>;
				pixelclk-active = <0>;
			};
		};
	};
};
mxsfb_probe()
{
	struct mxsfb_info *host;
	struct fb_info *fb_info;
    //申请一块内存存放struct mxsfb_info
	host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);
	if (!host) {
		dev_err(&pdev->dev, "Failed to allocate IO resource\n");
		return -ENOMEM;
	}

	
    //申请一块内存存放struct fb_info fb_info->device = &pdev->dev;
    //0是fb_info中private data数据的大小
	fb_info = framebuffer_alloc(0, &pdev->dev);
	if (!fb_info) {
		dev_err(&pdev->dev, "Failed to allocate fbdev\n");
		devm_kfree(&pdev->dev, host);
		return -ENOMEM;
	}
	
	host->fb_info = fb_info; //here
	fb_info->par = host;

	//ioremap映射寄存器地址 lcdif@30730000
    //reg = <0x30730000 0x10000>;
	host->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(host->base)) {
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = PTR_ERR(host->base);
		goto fb_release;
	}
    
    //设备树文件里面读取数据用于初始化
	ret = mxsfb_init_fbinfo(host);
    
    //注册fb设备
    ret = register_framebuffer(fb_info);


}

主要是两个结构体   struct mxsfb_info, struct fb_info。mxsfb_info包含fb_info,fb_info的par指针指向mxsfb_info。

在分配内存保存struct mxsfb_info, struct fb_info后,ioremap映射寄存器,进入mxsfb_init_fbinfo()对fb_info进行初始化并设置lcd寄存器。

static int mxsfb_init_fbinfo(struct mxsfb_info *host)
{
	int ret;
	struct fb_info *fb_info = host->fb_info;
	struct fb_var_screeninfo *var = &fb_info->var;
	struct fb_modelist *modelist;

    //操作fb0 通过系统调用,调用fb_fops后最终会执行到这里
	fb_info->fbops = &mxsfb_ops; //fb0的操作函数
	fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
	fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
	fb_info->fix.ypanstep = 1;
	fb_info->fix.ywrapstep = 1;
	fb_info->fix.visual = FB_VISUAL_TRUECOLOR,
	fb_info->fix.accel = FB_ACCEL_NONE;

    //struct videomode -->struct fb_videomode 
    //mxsfb_init_fbinfo_dt()->fb_videomode_from_videomode(&vm, &fb_vm);计算刷新频率
	ret = mxsfb_init_fbinfo_dt(host);//解析device-tree,计算刷新频率


	if (host->id < 0)
		sprintf(fb_info->fix.id, "mxs-lcdif");
	else
		sprintf(fb_info->fix.id, "mxs-lcdif%d", host->id);

    //注意这个list,fb_info->modelist可以找到fb_videomode
	if (!list_empty(&fb_info->modelist)) {
		/* first video mode in the modelist as default video mode  */
		modelist = list_first_entry(&fb_info->modelist,
				struct fb_modelist, list);
		fb_videomode_to_var(var, &modelist->mode); //初始化struct fb_var_screeninfo
	}
	/* save the sync value getting from dtb */
	host->sync = fb_info->var.sync;
	
	printk("host->id is %d,host->sync is %d\n",host->id,host->sync); //-19
	var->nonstd = 0;
	var->activate = FB_ACTIVATE_NOW;
	var->accel_flags = 0;
	var->vmode = FB_VMODE_NONINTERLACED;

	/* init the color fields */
	mxsfb_check_var(var, fb_info);

	fb_info->fix.line_length =
		fb_info->var.xres * (fb_info->var.bits_per_pixel >> 3);

    //framebuffer缓冲大小
	fb_info->fix.smem_len = SZ_32M;

    //为framebuffer分配内存空间地址,存放到fbi->fix.smem_start
    //fbi->screen_size = fbi->fix.smem_len;
    //这个地址会在mmap中用到,应用程序和驱动操作同一块内存
	/* Memory allocation for framebuffer */
	if (mxsfb_map_videomem(fb_info) < 0)
		return -ENOMEM;
    
    //设置LCD控制器信息 设置寄存器
	if (mxsfb_restore_mode(host))
		memset((char *)fb_info->screen_base, 0, fb_info->fix.smem_len);

	return 0;
}

进行完上面的操作后,register_framebuffer(fb_info) 注册这个fb,在dev/目录下生成fb0。

应用程序 可以操作这个文件节点。

register_framebuffer(struct fb_info *fb_info)
{
   do_register_framebuffer(fb_info);
}

static int do_register_framebuffer(struct fb_info *fb_info)
{
    if (num_registered_fb == FB_MAX)//num_registered_fb 全局变量,最多32个framebuffer
	    return -ENXIO;

	num_registered_fb++;
	for (i = 0 ; i < FB_MAX; i++)
		if (!registered_fb[i])
			break;

    fb_info->node = i; //这个是次设备号

    //fb_class在前面注册fb设备的时候创建
    //device_create这里会通过mdev最终在/dev/下面生成fb0
    fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), 
                   NULL,"fb%d",i); //fb次设备号 -->FB0

    fb_init_device(fb_info)
    {
        //在sys中创建相关的节点
	    for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
		    error = device_create_file(fb_info->dev, &device_attrs[i]);}

    }


    fb_var_to_videomode(&mode, &fb_info->var);
	fb_add_videomode(&mode, &fb_info->modelist);
	registered_fb[i] = fb_info; //最终把这个fb_info加入到全局的registered_fb中
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值