Linux驱动之LCD驱动

显示设备例如LCD,在Linux中用Framebuffer来表征,在/dev目录下显示设备一般表示成这样:/dev/fbX,应用程序通过访问这个设备来访问LCD,实际上应用程序通过操作显存来操作显示设备,显存由驱动程序设置。

Linux中使用fb_info结构体来表示Framebuffer设备,其内容如下:

struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* 互斥锁 */
struct mutex mm_lock; /* 互斥锁,用于 fb_mmap 和 smem_*域*/
struct fb_var_screeninfo var; /* 当前可变参数 */
struct fb_fix_screeninfo fix; /* 当前固定参数 */
struct fb_monspecs monspecs; /* 当前显示器特性 */
struct work_struct queue; /* 帧缓冲事件队列 */
struct fb_pixmap pixmap; /* 图像硬件映射 */
struct fb_pixmap sprite; /* 光标硬件映射 */
struct fb_cmap cmap; /* 当前调色板 */
struct list_head modelist; /* 当前模式列表 */
struct fb_videomode *mode; /* 当前视频模式 */
#ifdef CONFIG_FB_BACKLIGHT /* 如果 LCD 支持背光的话 */
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;  /* 背光设备 */
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
......
struct fb_ops *fbops; /* 帧缓冲操作函数集 */
struct device *device; /* 父设备 */
struct device *dev; /* 当前 fb 设备 */
int class_flag; /* 私有 sysfs 标志 */
......
char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */
unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */
void *pseudo_palette; /* 伪 16 位调色板 */
......
};

其中最重要成员有:var,fix,fbops(LCD设备的操作集合),screen_base(显存基地址),screen_size(显存大小),pseudo_palette。

var结构体变量指的是显示设备的可变参数:

struct fb_var_screeninfo { 
__u32 xres;/* visible resolution*//*定义屏幕一行有多少个像素点 */
__u32 yres;                       /*定义屏幕一列由多少个像素点 */

__u32 bits_per_pixel;             /*每像素位数(多少BPP),单位为字节 */


__u32 pixclock; /* pixel clock in ps (pico seconds) */ /*像素时钟(皮秒) */
 
	/* 行切换,从同步到绘图之间的延迟即HFPD(有效数据之后无效的像素的个数) ,对应于LCD数据手册的Hsyn的front-porch*/
__u32 left_margin; /* time from sync to picture */  


	/*行切换,从绘图到同步之间的延迟即HBPD(Hsyn脉冲下降沿之后的无效像素的个数) ,对应于LCD数据手册的Hsyn的back-porch*/					   
__u32 right_margin; /* time from picture to sync */  


	/*帧切换,从同步到绘图之间的延迟即VFPD(有效数据之后还要经历的无效行数(之后是下一帧数据)) ,对应于LCD数据手册的Vsyn的front-porch*/					  
__u32 upper_margin; /* time from sync to picture */ 
 
 
	/*帧切换,从绘图到同步之间的延迟即VBPD(Vsyn脉冲下降沿之后还要经历的无效行数) ,对应于LCD数据手册的Vsyn的back-porch */
__u32 lower_margin;  


	/*水平同步的长度即HSPW(Hsyn信号的脉冲宽度),对应于LCD数据手册的Hsyn的pulse Width */ 					 
__u32 hsync_len;  /* length of horizontal sync*/


	/*垂直同步的长度即VSPW(Vsyn信号的脉冲宽度),对应于LCD数据手册的Vsyn的pulse Width */
__u32 vsync_len;  /* length of vertical sync*/    
 

__u32 sync;   /* see FB_SYNC_**/            /* 查看宏FB_SYNC_*/

__u32 vmode;  /* see FB_VMODE_**/         /*  查看宏FB_VMODE_ */

....
}

        这些参数都是可以在设备树中进行修改的,具体的参数是多少要根据LCD的数据手册来进行修改,LCD的驱动程序会从设备树中读取这些参数。 

fix结构体变量指的是不变参数,用户不可进行修改的参数,比如屏幕缓冲区的地址。

struct fb_fix_screeninfo { 

unsigned long smem_start; /* Start of frame buffer mem physical address) */

__u32 smem_len; /*fb缓冲的长度,等于max_xres*max_yres*max_bpp/8  */

unsigned long mmio_start; /* Start of Memory Mapped I/O*/  /* 内存映射的I/O的开始位置 */


__u32 mmio_len; /* 内存映射的I/O的长度 */

....
}

        var变量是驱动程序从LCD设备的设备树节点中取得,fix变量中的显存基址和显存大小由驱动程序设置。

        LCD设备在Linux内核中一般被组织成一个标准的platform设备,其驱动也是一个平台设备驱动,当总线将设备和驱动匹配起来以后,probe函数会被调用,平台设备驱动的重点就是probe函数。

LCD驱动的probe函数需要完成几件事:

一、申请一个fb_info结构体,设置好其成员变量。

二、初始化SoC的LCD控制器,其实就是配置控制器的各寄存器。

三、调用register_framebuffer函数向Linux内核注册初始化好的fb_info。

各SoC厂商在编写Framebuffer设备驱动的时候,往往会对Linux内核中的fb_info结构体进行二次封装,将SoC的LCD控制器的寄存器地址信息,LCD的时钟信息,fb_info封装在一起。NXP将封装后的结构体称之为:mxsfb_info

下面来看看LCD驱动中的一些重点内容:

static int mxsfb_probe(struct platform_device* pdev)
{
    struct resource *res;
    struct mxsfb_info *host;
    struct fb_info *fb_info;

    /*从设备树中提取LCD控制器寄存器地址信息*/
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    /*为二次封装的Framebuffer结构体分配内存*/
    host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info),GFP_KERNEL);

    /*给fb_info分配内存空间*/
    fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);

    /*物理地址映射成虚拟地址,此后可以通过base的值访问到LCD控制器的所有寄存器*/
    host->base = devm_ioremap_resource(&pdev->dev, res);

    /*通过LCD设备的设备树节点中的内容去填充fb_info结构体,主要就是设置上述的重点参数*/
    ret = mxsfb_init_fbinfo(host);

    /*配置LCD控制器*/
    mxsfb_set_par(fb_info);
    mxsfb_enable_controller(fb_info);
    
    /*向Linux内核注册*/
    ret = register_framebuffer(fb_info);

    .....
    
}

在mxsfb_init_fbinfo函数对fb_info进行初始化的时候,其中LCD的操作函数集如下:

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_ioctl = mxsfb_ioctl,
.fb_blank = mxsfb_blank,
.fb_pan_display = mxsfb_pan_display,
.fb_mmap = mxsfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};

        另外,mxsfb_init_fbinfo 函数通过调用 mxsfb_init_fbinfo_dt 函数从设备树中获取到 fb_info需要的各参数信息然后对fb_info进行填充mxsfb_init_fbinfo函数会调用 mxsfb_map_videomem 函数申请 LCD 的帧缓冲内存(也就是显存)。这些函数都在mxsfb.c文件中。

注意:

        SoC厂商提供的Linux内核一般都默认开启了LCD驱动,没开启的自己配置内核来开启,也就是说LCD驱动一般都是写好的,我们要做的就是改设备树信息。

下面来看看LCD设备树信息,来明确要修改哪些信息。

首先是LCD的引脚信息:

  

一般自己设计的板子,LCD引脚信息会和原厂评估版一样,几乎不用修改。

屏幕参数信息,这个需要按照自己的LCD屏幕重点修改。

 

 其中bits-per-pixel属性根据具体LCD来设置,RGB565那么一个像素16位,RGB888则为24位。

LCD像素时钟需要自己计算,下面贴出一个计算公式:

        注意这里计算的是显示一帧的时间,60帧还需要乘以60。计算像素时钟需要的这些参数,也就是设备树LCD参数信息,查询设备手册可以获得。 

参考资料:《正点原子Linux设备驱动开发手册》

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值