查看原文
其他

Linux驱动分析之LCD驱动架构

一口Linux 2021-11-06

The following article is from 嵌入式软件开发交流 Author young

点击上方“蓝字”关注我们




Framebuffer简介


    在Linux设备中,LCD显示采用了帧缓冲(framebuffer)技术,所以LCD驱动也叫Framebuffer驱动,所以LCD驱动框架就是围绕帧缓冲展开工作。帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象出来,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备, 主设备号为29,对应于/dev/fbn



Framebuffer框架结构



    核心层代码fbmem.c向上提供了完整的字符设备操作接口,也就是实现注册字符设备,提供通用的open,read,write,ioctl,mmap等接口;向下给硬件设备驱动层提供标准的驱动编程接口。

    在linux系统中,一个硬件控制器(显卡)抽象为一个fb_info结构体,要实现一个LCD驱动就是要实现这个结构,并且使用核心层提供的注册函数注册。



重要结构体


内核版本:4.20.12


  • fb_info

// include/linux/fb.hstruct fb_info { atomic_t count; int node; int flags;
int fbcon_rotate_hint;  struct mutex lock;    /* open/release/ioctl中使用*/ struct mutex mm_lock; /*fb_mmap和smem_*数据域中使用 */ struct fb_var_screeninfo var; /* LCD屏可变参数 */ struct fb_fix_screeninfo fix; /* LCD屏固定参数 */ struct fb_monspecs monspecs; /* Current Monitor specs */ struct work_struct queue; /* Framebuffer event queue */ struct fb_pixmap pixmap; /* Image hardware mapper */ struct fb_pixmap sprite; /* Cursor hardware mapper */ struct fb_cmap cmap; /* Current cmap */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT struct backlight_device *bl_dev; //背光设备
struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; //背光水平曲线#endif#ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio;#endif
struct fb_ops *fbops; /*真正操作硬件寄存器的方法集合*/ struct device *device;   struct device *dev;     /* fb设备*/ int class_flag; /* private sysfs flags */#ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */#endif union { char __iomem *screen_base; /* LCD虚拟显存地址 */ char *screen_buffer; }; unsigned long screen_size; /* LCD虚拟显存大小 */ void *pseudo_palette; /* 指向16种颜色的调试板,其实就是一块内存*/ #define FBINFO_STATE_RUNNING 0#define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ void *fbcon_par;
void *par; //私有数据,用来存放自己的数据的结构地址 struct apertures_struct { unsigned int count; struct aperture { resource_size_t base; resource_size_t size; } ranges[0]; } *apertures;
bool skip_vt_switch; /* no VT switch on suspend/resume required */};

使用标准的LCD框架编写,var, fix, fbops, screen_base这四个成员是一定要实现的。


  • fb_var_screeninfo

struct fb_var_screeninfo { __u32 xres; /* 可见屏幕一行有多少像素点 */ __u32 yres; /*可见屏幕一屏有多少行,也就是列*/ __u32 xres_virtual; /* 虚拟屏幕一行有多少像素点*/ __u32 yres_virtual; /*虚拟屏幕一屏有多少行*/ //显存大小并不一定等于实际屏幕显示对应的区域 __u32 xoffset; /* 虚拟屏到实际屏的水平偏移量 */ __u32 yoffset; /* 虚拟屏到实际屏的垂直偏移量*/
__u32 bits_per_pixel; /* 每个像素的位数即BPP,比如:RGB565则填入16*/ __u32 grayscale; /* 0 = 彩色, 1 = 灰度屏*/ /* >1 = FOURCC */ struct fb_bitfield red; /* 红色的长度和偏移信息 */ struct fb_bitfield green; /* 绿色的长度和偏移信息 */ struct fb_bitfield blue; /* 蓝色的长度和偏移信息 */ struct fb_bitfield transp; /* 透明色的长度和偏移信息*/
__u32 nonstd; /* 不等于0则为非标准像素格式 */ //定义修改参数生效时刻,一般马上生效,对应值是0,宏名是 FB_ACTIVATE_NOW __u32 activate; /* see FB_ACTIVATE_* */ //存放物理屏的物理尺寸,是外观尺寸,单位是mm,可选择填充的项 非重点 __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */   //下面是LCD屏的工作时序,参数从datasheet来 __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ __u32 colorspace; /* colorspace for FOURCC-based modes */ __u32 reserved[4]; /* Reserved for future compatibility */};

上面结构体的参数大部分都是要从LCD屏的datasheet中获取。


  • fb_fix_screeninfo

struct fb_fix_screeninfo { char id[16]; /* LCD标识名 填写一个16字符以内字符串即可 */ unsigned long smem_start; /* 显存的物理起始地址,不是虚拟地址*/ /* (physical address) */ __u32 smem_len; /* 显存的内存长度 */ __u32 type; /* 表示像素类型 see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* 表示颜色类型 see FB_VISUAL_* */ __u16 xpanstep; /* 如果没有硬件panning就赋值为0 */ __u16 ypanstep; /* 如果没有硬件panning就赋值为0 */ __u16 ywrapstep; /* 如果没有硬件panning就赋值为0 */ //一行的字节数 ,例:(RGB565)240*320,那么这里就等于240*16/8 __u32 line_length; /* length of a line in bytes */ unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ //独立显卡相关的,基本不用 __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ __u16 capabilities; /* see FB_CAP_* */ __u16 reserved[2]; /* Reserved for future compatibility */};
  • fb_ops

struct fb_ops { /* open/release and usage marking */ struct module *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user);
/* For framebuffers with strange non linear layouts or that do not * work with normal memory mapped access */ ssize_t (*fb_read)(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos); ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos);
/* checks var and eventually tweaks it to something supported, * DO NOT MODIFY PAR */ int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
/* set the video mode according to info->var */ int (*fb_set_par)(struct fb_info *info);
/* set color register */ int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */ int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* blank display */ int (*fb_blank)(int blank, struct fb_info *info);
/* pan display */ int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */ void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); /* Copy data from area to another */ void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); /* Draws a image to the display */ void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */ int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* wait for blit idle, optional */ int (*fb_sync)(struct fb_info *info);
/* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg);
/* Handle 32bit compat ioctl (optional) */ int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg);
/* perform fb specific mmap */ int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */ void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */ void (*fb_destroy)(struct fb_info *info);
/* called at KDB enter and leave time to prepare the console */ int (*fb_debug_enter)(struct fb_info *info); int (*fb_debug_leave)(struct fb_info *info);};

常用的重要成员:

  • fb_open:当你的lcd不需要做什么特殊的初始化操作,这个方法可以不实现

  • fb_release:当你的应用程序不使用lcd设备的时候,需要做的事情在这里实现

  • fb_read:当你的lcd控制器使用的内存是独立显存的时候才需要使用,直接使用核心层read

  • fb_write:当你的lcd控制器使用的内存是独立显存的时候才需要使用,直接使用核心层write

  • fb_check_var:检测应用程序传递下来的可变参数是否合法。当你不提供给应用程序通过ioctl命令动态修改LCD可变参数时不需要实现。

  • fb_set_par:实现的功能是把可变参数设置到硬件寄存器中去

  • fb_blank:实现屏幕的黑屏白屏模式(开屏,关屏)

  • fb_fillrect:实现功能是填充矩形

  • fb_copyarea:实现功能是区域复制

  • fb_imageblit:实现功能是绘制位图

  • fb_mmap:实现的功能是把内核空间的分配的显存映射到用户空间中对应的mmap系统调用,当你的控制器是独显的时候才需要




API函数


/*函数功能:注册fb_info*/int register_framebuffer(struct fb_info *fb_info)
/*函数功能:注销fb_info*/int unregister_framebuffer(struct fb_info *fb_info)
/*函数功能:动态分配DMA内存,同时可以得到分配内存的虚拟地址和物理地址 参数说明: dev:设备指针如果没有赋值NULL size:内存大小 dma_handle:作为输出参数使用,存放分配到的内存对应的物理地址 flag:内存分配方式 返回值:分配到的内存的首地址*/void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)
/*功能:释放由dma_alloc_writecombine分配的dma内存 参数: dev:设备指针如果没有赋值NULL size:内存大小 cpu_addr:dma_alloc_writecombine得到的虚拟地址首地址 dma_handle:作为输出参数使用,存放分配到的内存对应的物理地址 */ void dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle)

lcd每屏显示的数据量很大,所以lcd一般是利用DMA来搬运数据,而DMA模块只会使用物理地址,所以LCD驱动中需要记录物理地址。



总结


    上面的结构体中有很多参数,对于一些普通的LCD屏,需要用到的参数并不多。主要就是从数据手册中获取屏幕参数信息,填充fb_info,然后注册。另一部分就LCDC的寄存器配置。目前很多原厂会将这两部分进行分离,这样就更方便客户进行定制。




一口君个人微信


添加一口君个人微信即送Linux、嵌入式等独家入门视频


→ 精选技术资料共享

→ 高手如云交流社群





本公众号全部原创干货已整理成一个目录,请在公众号里回复「m」获取!或者关注进入后台点击左下角「干货」!

推荐阅读



【1】手把手教Linux驱动1-模块化编程
【2】手把手教Linux驱动2-模块参数param和符号导出export用法
【3】手把手教Linux驱动3-之字符设备架构详解,有这篇就够了
【4】手把手教Linux驱动4-进程、文件描述符、file、inode关系详解
【5】玩转内核链表list_head,如何管理不同类型节点的实现(万字文)可以收藏
【6】
I2C干货-基于Cortex-A9(重新整理)【7】一文搞懂ADC裸机和基于Linux驱动编写方法
【8】22张图详解浏览器请求数据包如何到达web服务器(搞懂网络可以毕业了)
【9】手把手教Linux驱动5-自旋锁、信号量、互斥体概述
【10】Linux命令cURL详解+定时上传文件到FTP服务器(送源码)


5T技术资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,单片机,树莓派,等等。在公众号内回复「1024」,即可免费获取!!




一键四连


: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存