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平台设备驱动