欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux驱动分析之LCD驱动架构

程序员文章站 2022-07-14 09:38:32
...

Framebuffer简介

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

 

Framebuffer框架结构

Linux驱动分析之LCD驱动架构

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

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

 

重要结构体

内核版本:4.20.12

  • fb_info

// include/linux/fb.h
struct 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驱动分析之LCD驱动架构
更多好文,关注公众号

 

相关标签: Linux驱动