【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 小时前
Function Calling 参数总出错?只修参数就行
linux·windows·python
Android系统攻城狮3 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_prepare_read调用流程与实战(七十四)
linux·运维·服务器·gstreamer音视频·音视频进阶
我科绝伦(Huanhuan Zhou)4 小时前
Linux 通用压力测试脚本合集:磁盘 + 网络
linux·网络·压力测试
Soneswy6 小时前
Linux系统篇--重定向与文件操作命令
linux
有毒的教程6 小时前
Linux底层存储路由配
linux·运维·网络
Linux系统运维工程师_郭主任7 小时前
运维人还在被拒offer?现在能考的,只有 RHCE 10.0。
linux
自养Agent7 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
Coisinier8 小时前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
A心有千千结9 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
书生执笔画浮沉9 小时前
Linux 内核模块为什么不能自己解决兼容性问题?
linux·c·kernel·update·灵感