【Linux 驱动】IMX6ULL eLCDIF驱动

1. eLCDIF设备树

lcdif: lcdif@021c8000 {
				compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";    //属性
				reg = <0x021c8000 0x4000>;                             //起始地址 地址大小
				interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;          //中断
				clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,                 //时钟子系统
					 <&clks IMX6UL_CLK_LCDIF_APB>,
					 <&clks IMX6UL_CLK_DUMMY>;
				clock-names = "pix", "axi", "disp_axi";                //系统名称
				status = "disabled";                                   //状态
			};

* Freescale MXS LCD Interface (LCDIF)

Required properties:
- compatible: Should be "fsl,<chip>-lcdif".  Supported chips include
  imx23 and imx28.
- reg: Address and length of the register set for lcdif
- interrupts: Should contain lcdif interrupts
- display : phandle to display node (see below for details)

* display node

Required properties:
- bits-per-pixel : <16> for RGB565, <32> for RGB888/666.
- bus-width : number of data lines.  Could be <8>, <16>, <18> or <24>.

Required sub-node:
- display-timings : Refer to binding doc display-timing.txt for details.

  +----------+-------------------------------------+----------+-------+
  |          |        ↑                            |          |       |
  |          |        |vback_porch                 |          |       |
  |          |        ↓                            |          |       |
  +----------#######################################----------+-------+
  |          #        ↑                            #          |       |
  |          #        |                            #          |       |
  |  hback   #        |                            #  hfront  | hsync |
  |   porch  #        |       hactive              #  porch   |  len  |
  |<-------->#<-------+--------------------------->#<-------->|<----->|
  |          #        |                            #          |       |
  |          #        |vactive                     #          |       |
  |          #        |                            #          |       |
  |          #        ↓                            #          |       |
  +----------#######################################----------+-------+
  |          |        ↑                            |          |       |
  |          |        |vfront_porch                |          |       |
  |          |        ↓                            |          |       |
  +----------+-------------------------------------+----------+-------+
  |          |        ↑                            |          |       |
  |          |        |vsync_len                   |          |       |
  |          |        ↓                            |          |       |
  +----------+-------------------------------------+----------+-------+

Examples:

&lcdif@80030000 {
	pinctrl-names = "default";                    //pinctrl 名字
	pinctrl-0 = <&pinctrl_lcdif_dat               //pinctrl 子系统
		     &pinctrl_lcdif_ctrl
		     &pinctrl_lcdif_reset>;
	display = <&display0>;                        
	status = "okay";                               //状态

	display: display {                             //子节点
		bits-per-pixel = <32>;                     //每个像素点大小
		bus-width = <24>;                          //总线宽度

		display-timings {                          //display-timing子节点
			native-mode = <&timing0>;
			timing0: timing0 {
				clock-frequency = <33500000>;      //时钟频率
				hactive = <800>;                   //水平显示分别率
				vactive = <480>;                   //垂直显示分别率
				hfront-porch = <164>;              //水平方向前肩
				hback-porch = <89>;                //水平方向后肩
				hsync-len = <10>;                  //水平脉冲宽度
				vback-porch = <23>;                //垂直方向后肩
				vfront-porch = <10>;               //垂直方向前肩 
				vsync-len = <10>;                  //垂直脉冲宽度
				hsync-active = <0>;                //水平方向有效脉冲极性
				vsync-active = <0>;                //垂直方向有效脉冲极性
				de-active = <1>;                   //使能有效极性
				pixelclk-active = <0>;             //像素时钟边沿采样有效性
			};
		};
	};
};

2. 相关数据结构

(1)struct fb_info

Linux内核中使用fb_info结构体变量来描述一个framebuffer,在调用register_framebuffer接口注册framebuffer之前,必须要初始化其中的重要数据成员。

struct fb_info中成员众多,我们需要着重关注以下成员:

fb_var_screeninfo:代表可修改的LCD显示参数,如分辨率和像素比特数等;

fb_fix_screeninfo:代表不可修改的LCD属性参数,如显示内存的物理地址和长度等;

fb_ops:LCD底层硬件操作接口集。

cpp 复制代码
struct fb_info 
{
    int node;                       //用来表示该fb设备的次设备号
    int flags;                      //一个标志位
    struct mutex lock;              /* Lock for open/release/ioctl funcs */
    struct mutex mm_lock;           /* Lock for fb_mmap and smem_* fields */
    struct fb_var_screeninfo var;   /* Current var */        //  fb的可变参数
    struct fb_fix_screeninfo fix;   /* Current fix */        //  fb的不可变参数
    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
    /* 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
#ifdef CONFIG_FB_DEFERRED_IO
    struct delayed_work deferred_work;
    struct fb_deferred_io *fbdefio;
#endif

    struct fb_ops *fbops;          //  该设备对应的操作方法
    struct device *device;         /* This is the parent */     //fb设备的父设备
    struct device *dev;            /* This is this fb device */ //本设备的device
    int class_flag;                /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
    struct fb_tile_ops *tileops;   /* Tile Blitting */
#endif
    char __iomem *screen_base;     /* Virtual address */        //LCD的显存地址(虚拟地址) 
    unsigned long screen_size;     /* Amount of ioremapped VRAM or 0 *///  LCD显存的字节大小
    void *pseudo_palette;          /* Fake palette of 16 colors */ 
#define FBINFO_STATE_RUNNING    0
#define FBINFO_STATE_SUSPENDED    1
    u32 state;                     /* Hardware state i.e suspend */
    void *fbcon_par;               /* fbcon use-only private area */
    /* From here on everything is device dependent */
    void *par;
    /* we need the PCI or similiar aperture base/size not
       smem_start/size as smem_start may just be an object
       allocated inside the aperture so may not actually overlap */
    struct apertures_struct {
        unsigned int count;
        struct aperture {
            resource_size_t base;
            resource_size_t size;
        } ranges[0];
    } *apertures;
};

struct fb_info

(2)struct fb_var_screeninfo

Linux内核中使用struct fb_var_screeninfo来描述可修改的LCD显示参数,如分辨率和像素比特数等。

cpp 复制代码
struct fb_var_screeninfo 
{
    __u32 xres;                  //   水平分辨率 
    __u32 yres;                  //   垂直分辨率
    __u32 xres_virtual;          //   虚拟水平分辨率
    __u32 yres_virtual;          //   虚拟垂直分辨率
    __u32 xoffset;               //  当前显存水平偏移量
    __u32 yoffset;               //  当前显存垂直偏移量

    __u32 bits_per_pixel;        //  像素深度
    __u32 grayscale;             /* != 0 Graylevels instead of colors */

    struct fb_bitfield red;      /* bitfield in fb mem if true color, */
    struct fb_bitfield green;    /* else only length is significant */
    struct fb_bitfield blue;
    struct fb_bitfield transp;   /* transparency*/    

    __u32 nonstd;           /* != 0 Non standard pixel format */
    __u32 activate;         /* see FB_ACTIVATE_* */
    __u32 height;           //  LCD的物理高度mm
    __u32 width;            // LCD的物理宽度mm

    __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */

    /* Timing: All values in pixclocks, except pixclock (of course) */
    __u32 pixclock;            /* pixel clock in ps (pico seconds)  */  //  像素时钟

    //下面这六个就是LCD的时序参数
    __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 reserved[5];        /* Reserved for future compatibility */
};

struct fb_var_screeninfo

(3)struct fb_fix_screeninfo

Linux内核中使用struct fb_fix_screeninfo来描述不可修改的LCD属性参数,如显示内存的物理地址和长度等。

cpp 复制代码
struct fb_fix_screeninfo 
{
    char id[16];                /* identification string eg "TT Builtin" */
    unsigned long smem_start;   // LCD显存的起始地址(物理地址)
                    /* (physical address) */
    __u32 smem_len;            /* Length of frame buffer mem *///  LCD显存的字节大小
    __u32 type;               /* see FB_TYPE_**/
    __u32 type_aux;           /* Interleave for interleaved Planes */
    __u32 visual;             /* see FB_VISUAL_*              */ 
    __u16 xpanstep;           /* zero if no hardware panning  */
    __u16 ypanstep;           /* zero if no hardware panning  */
    __u16 ywrapstep;          /* zero if no hardware ywrap    */
    __u32 line_length;        /* length of a line in bytes    *///  LCD一行的长度 (以字节为单位)
    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 reserved[3];        /* Reserved for future compatibility */
};

struct fb_fix_screeninfo

3. fbmem.c

3.1 入口函数

FrameBuffer驱动是以模块的形式注册到系统中,在模块初始化时,创建FrameBuffer对应的设备文件及proc文件,并注册FrameBuffer设备操作接口函数fb_fops。

cpp 复制代码
static int __init
fbmem_init(void)
{
	proc_create("fb", 0, NULL, &fb_proc_fops);    //向 proc 文件系统报告驱动状态和参数

	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))    //注册字符设备驱动,主设备号是29 名称为fb
		printk("unable to get major %d for fb devs\n", FB_MAJOR);

	fb_class = class_create(THIS_MODULE, "graphics");    //创建 /sys/class/graphics 设备类,配合 mdev生成设备文件
	if (IS_ERR(fb_class)) {
		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
		fb_class = NULL;
	}
	return 0;
}

3.2 fb_fops

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

cpp 复制代码
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,
};

3.2.1 fb_open

3.2.2 fb_mmap

3.2.3 fb_ioctl

cpp 复制代码
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
			unsigned long arg)
{
	struct fb_ops *fb;
	struct fb_var_screeninfo var;
	struct fb_fix_screeninfo fix;
	struct fb_con2fbmap con2fb;
	struct fb_cmap cmap_from;
	struct fb_cmap_user cmap;
	struct fb_event event;
	void __user *argp = (void __user *)arg;
	long ret = 0;

	switch (cmd) {
	case FBIOGET_VSCREENINFO:        // 获取可变屏幕参数
		if (!lock_fb_info(info))
			return -ENODEV;
		var = info->var;
		unlock_fb_info(info);

		ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
		break;
	case FBIOPUT_VSCREENINFO:        // 设置可变屏幕参数
		if (copy_from_user(&var, argp, sizeof(var)))
			return -EFAULT;
		console_lock();
		if (!lock_fb_info(info)) {
			console_unlock();
			return -ENODEV;
		}
		info->flags |= FBINFO_MISC_USEREVENT;
		ret = fb_set_var(info, &var);
		info->flags &= ~FBINFO_MISC_USEREVENT;
		unlock_fb_info(info);
		console_unlock();
		if (!ret && copy_to_user(argp, &var, sizeof(var)))
			ret = -EFAULT;
		break;
	case FBIOGET_FSCREENINFO:        //获得固定的屏幕参数设置
		if (!lock_fb_info(info))
			return -ENODEV;
		fix = info->fix;
		unlock_fb_info(info);

		ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
		break;
	case FBIOPUTCMAP:                // 获得固定的屏幕参数设置
		if (copy_from_user(&cmap, argp, sizeof(cmap)))
			return -EFAULT;
		ret = fb_set_user_cmap(&cmap, info);
		break;
	case FBIOGETCMAP:                // 获得颜色表
		if (copy_from_user(&cmap, argp, sizeof(cmap)))
			return -EFAULT;
		if (!lock_fb_info(info))
			return -ENODEV;
		cmap_from = info->cmap;
		unlock_fb_info(info);
		ret = fb_cmap_to_user(&cmap_from, &cmap);
		break;
	case FBIOPAN_DISPLAY:            // 平移显示
		if (copy_from_user(&var, argp, sizeof(var)))
			return -EFAULT;
		console_lock();
		if (!lock_fb_info(info)) {
			console_unlock();
			return -ENODEV;
		}
		ret = fb_pan_display(info, &var);
		unlock_fb_info(info);
		console_unlock();
		if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
			return -EFAULT;
		break;
	case FBIO_CURSOR:
		ret = -EINVAL;
		break;
	case FBIOGET_CON2FBMAP:
		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
			return -EFAULT;
		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
			return -EINVAL;
		con2fb.framebuffer = -1;
		event.data = &con2fb;
		if (!lock_fb_info(info))
			return -ENODEV;
		event.info = info;
		fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
		unlock_fb_info(info);
		ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
		break;
	case FBIOPUT_CON2FBMAP:
		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
			return -EFAULT;
		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
			return -EINVAL;
		if (con2fb.framebuffer >= FB_MAX)
			return -EINVAL;
		if (!registered_fb[con2fb.framebuffer])
			request_module("fb%d", con2fb.framebuffer);
		if (!registered_fb[con2fb.framebuffer]) {
			ret = -EINVAL;
			break;
		}
		event.data = &con2fb;
		console_lock();
		if (!lock_fb_info(info)) {
			console_unlock();
			return -ENODEV;
		}
		event.info = info;
		ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
		unlock_fb_info(info);
		console_unlock();
		break;
	case FBIOBLANK:
		console_lock();
		if (!lock_fb_info(info)) {
			console_unlock();
			return -ENODEV;
		}
		info->flags |= FBINFO_MISC_USEREVENT;
		ret = fb_blank(info, arg);
		info->flags &= ~FBINFO_MISC_USEREVENT;
		unlock_fb_info(info);
		console_unlock();
		break;
	default:
		if (!lock_fb_info(info))
			return -ENODEV;
		fb = info->fbops;
		if (fb->fb_ioctl)
			ret = fb->fb_ioctl(info, cmd, arg);
		else
			ret = -ENOTTY;
		unlock_fb_info(info);
	}
	return ret;
}

4. mxsfb.c

一个标准的platform平台设备驱动

4.1 mxsfb_probe

相关推荐
朝九晚五ฺ3 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream4 小时前
Linux的桌面
linux
xiaozhiwise4 小时前
Makefile 之 自动化变量
linux
意疏6 小时前
【Linux 篇】Docker 的容器之海与镜像之岛:于 Linux 系统内探索容器化的奇妙航行
linux·docker
BLEACH-heiqiyihu6 小时前
RedHat7—Linux中kickstart自动安装脚本制作
linux·运维·服务器
一只爱撸猫的程序猿6 小时前
一个简单的Linux 服务器性能优化案例
linux·mysql·nginx
我的K84098 小时前
Flink整合Hudi及使用
linux·服务器·flink
1900438 小时前
linux6:常见命令介绍
linux·运维·服务器
Camellia-Echo8 小时前
【Linux从青铜到王者】Linux进程间通信(一)——待完善
linux·运维·服务器