LCD driver for Linux

摘自:http://code.google.com/p/androidteam/wiki/LCDanylsis

(1)LCDanalysis

基于2.6.25内核的LCD驱动程序结构体platfrom_device和platfrom_driver分析.

ntroduction

platform_device 和 platform_driver

Details

从2.6版本开始引入了platform这个概念, platform可以理解为一种设备类型,就像字符设备、块设备、网络设备一样,为了向内核添加一个platform设备,应该填写两个数据结构 一个为platform_device 另一个为platform_driver

  • 先来介绍platform_device
platform_device 定义在文件 kernel\include\linux\platform_device.h 中
  struct platform_device {
        const char      * name;
        int             id;
        struct device   dev;
        u32             num_resources;
        struct resource * resource;
};

该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,定义如下:
 struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
};

下面我们以s3c2410的lcd为例来具体讲解下 在 arch/arm/plat-s3c24xx/devs.c中 可以看到填写 lcd的代码 如下:
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;

struct platform_device s3c_device_lcd = {
        .name             = "s3c2410-lcd",
        .id               = -1,
        .num_resources    = ARRAY_SIZE(s3c_lcd_resource),
        .resource         = s3c_lcd_resource,
        .dev              = {
                .dma_mask               = &s3c_device_lcd_dmamask,
                .coherent_dma_mask      = 0xffffffffUL
        }
};

/* LCD Controller */

static struct resource s3c_lcd_resource[] = {
        [0] = {
                .start = S3C24XX_PA_LCD,
                .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = IRQ_LCD,
                .end   = IRQ_LCD,
                .flags = IORESOURCE_IRQ,
        }

};

struct resource结构实际上描述了该设备占用的硬件资源(如地址空间,中断号等s),s3c_lcd_resource描述了内存空间和中断分配情况

从上面可以看出我们通过填写platform_device描述了s3c2410中lcd的情况

  • 再看 platform_driver
platform_deiver 定义在文件 kernel\include\linux\platform_device.h 中 定义如下: 
struct platform_driver {
        int (*probe)(struct platform_device *);
        int (*remove)(struct platform_device *);
        void (*shutdown)(struct platform_device *);
        int (*suspend)(struct platform_device *, pm_message_t state);
        int (*suspend_late)(struct platform_device *, pm_message_t state);
        int (*resume_early)(struct platform_device *);
        int (*resume)(struct platform_device *);
        struct device_driver driver;
};

s3c2410中lcd的platfrom_driver 在那里填充的呢? 在/drivers/vedio/s3c2410fb.c中实现 代码如下: 
static struct platform_driver s3c2410fb_driver = {
        .probe          = s3c2410fb_probe,
        .remove         = s3c2410fb_remove,
        .suspend        = s3c2410fb_suspend,
        .resume         = s3c2410fb_resume,
        .driver         = {
                .name   = "s3c2410-lcd",
                .owner  = THIS_MODULE,
        },
};

可以看到该platform设备的驱动函数有s3c2410fb_probe、s3c2410fb_remove等等 这样一个platfrom设备的两部分数据结构都填充完成了

 要特别注意一点 s3c_device_lcd 结构体中的 name  s3c2410fb_driver driver成员变量
 中的name域的名字必须一致 ,这样 platfrom_device 才能和 platfrom_driver关联起来 只有这
 样将来才能注册成功 才能回调 函数 s3c2410fb_probe  哈哈

(2)LCDanalysis2

LCD驱动流程实现分析. 

Details

我们已经分析了一个platfrom设备的表示, 即就是要填充连个结构体 platfrom_device和platfrom_driver 下面我们着重看s3c2410的lcd驱动的流程

LCD流程分析

驱动入口 /drivers/vedio/s3c2410fb.c中 有这样的代码

module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup)

根据驱动模型 我们知道 加载时调用 s3c2410fb_init 函数 注销时调用 s3c2410fb_cleanup 函数 我们主要分析 加载时的情况 s3c2410fb_init函数实现如下:


int __init s3c2410fb_init(void)
{
        int ret = platform_driver_register(&s3c2410fb_driver);

        if (ret == 0)
                ret = platform_driver_register(&s3c2412fb_driver);;

        return ret;
}

在上面的代码中 我们可以看到主要调用了platform_driver_register(&s3c2410fb_driver)函数 简单的 说就是调用platform_driver_register要将向内核注册一个platform设备的驱动 毫无疑问 这个设备是s3c2410中lcd,通过platform_driver_register函数注册该设备的过程中,它会回调s3c2410fb_driver中的s3c2410fb_probe函数 

这样我们这个流程就清楚了 过程如下:


  module_init() --->
              s3c2410fb_init----->
                       platfrom_driver_register()---->
                                      s3c2410fb_driver---->
                                                          s3c2410fb_probe

在s3c2410fb_probe 里面LCD驱动主要做的工作是填充结构体 struct fb_info 并使用register_framebuffer()函数在系统中进行注册

s3c2410fb_probe 函数代码 


static int __init s3c24xxfb_probe(struct platform_device *pdev,
                                  enum s3c_drv_type drv_type)
{
        struct s3c2410fb_info *info;
        struct s3c2410fb_display *display;
        struct fb_info *fbinfo;
        struct s3c2410fb_mach_info *mach_info;
        struct resource *res;
        int ret;
        int irq;
        int i;
        int size;
        u32 lcdcon1;

        mach_info = pdev->dev.platform_data;
        if (mach_info == NULL) {
                dev_err(&pdev->dev,
                        "no platform data for lcd, cannot attach\n");
                return -EINVAL;
        }

        if (mach_info->default_display >= mach_info->num_displays) {
                dev_err(&pdev->dev, "default is %d but only %d displays\n",
                        mach_info->default_display, mach_info->num_displays);
                return -EINVAL;
        }

        display = mach_info->displays + mach_info->default_display;

        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "no irq for device\n");
                return -ENOENT;
        }

        fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
        if (!fbinfo)
                return -ENOMEM;

        platform_set_drvdata(pdev, fbinfo);

        info = fbinfo->par;
        info->dev = &pdev->dev;
        info->drv_type = drv_type;

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "failed to get memory registers\n");
                ret = -ENXIO;
                goto dealloc_fb;
        }

        size = (res->end - res->start) + 1;
        info->mem = request_mem_region(res->start, size, pdev->name);
        if (info->mem == NULL) {
                dev_err(&pdev->dev, "failed to get memory region\n");
                ret = -ENOENT;
                goto dealloc_fb;
        }

        info->io = ioremap(res->start, size);
        if (info->io == NULL) {
                dev_err(&pdev->dev, "ioremap() of registers failed\n");
                ret = -ENXIO;
                goto release_mem;
        }

        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

        dprintk("devinit\n");

        strcpy(fbinfo->fix.id, driver_name);

        /* Stop the video */
        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

        fbinfo->fix.type            = FB_TYPE_PACKED_PIXELS;
        fbinfo->fix.type_aux        = 0;
        fbinfo->fix.xpanstep        = 0;
        fbinfo->fix.ypanstep        = 0;
        fbinfo->fix.ywrapstep       = 0;
        fbinfo->fix.accel           = FB_ACCEL_NONE;

        fbinfo->var.nonstd          = 0;
        fbinfo->var.activate        = FB_ACTIVATE_NOW;
        fbinfo->var.accel_flags     = 0;
        fbinfo->var.vmode           = FB_VMODE_NONINTERLACED;

        fbinfo->fbops               = &s3c2410fb_ops;
        fbinfo->flags               = FBINFO_FLAG_DEFAULT;
        fbinfo->pseudo_palette      = &info->pseudo_pal;

        for (i = 0; i < 256; i++)
                info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

        ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
        if (ret) {
                dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
                ret = -EBUSY;
                goto release_regs;
        }

        info->clk = clk_get(NULL, "lcd");
        if (!info->clk || IS_ERR(info->clk)) {
                printk(KERN_ERR "failed to get lcd clock source\n");
                ret = -ENOENT;
                goto release_irq;
        }

        clk_enable(info->clk);
        dprintk("got and enabled clock\n");

        msleep(1);

        /* find maximum required memory size for display */
        for (i = 0; i < mach_info->num_displays; i++) {
                unsigned long smem_len = mach_info->displays[i].xres;

                smem_len *= mach_info->displays[i].yres;
                smem_len *= mach_info->displays[i].bpp;
                smem_len >>= 3;
                if (fbinfo->fix.smem_len < smem_len)
                        fbinfo->fix.smem_len = smem_len;
        }

        /* Initialize video memory */
        ret = s3c2410fb_map_video_memory(fbinfo);
        if (ret) {
                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
                ret = -ENOMEM;
                goto release_clock;
        }

        dprintk("got video memory\n");

        fbinfo->var.xres = display->xres;
        fbinfo->var.yres = display->yres;
        fbinfo->var.bits_per_pixel = display->bpp;

        s3c2410fb_init_registers(fbinfo);

        s3c2410fb_check_var(&fbinfo->var, fbinfo);

        ret = register_framebuffer(fbinfo);
        if (ret < 0) {
                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
                        ret);
                goto free_video_memory;
        }

        /* create device files */
        device_create_file(&pdev->dev, &dev_attr_debug);

        printk(KERN_INFO "fb%d: %s frame buffer device\n",
                fbinfo->node, fbinfo->fix.id);

        return 0;

free_video_memory:
        s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
        clk_disable(info->clk);
        clk_put(info->clk);
release_irq:
        free_irq(irq, info);
release_regs:
        iounmap(info->io);
release_mem:
        release_resource(info->mem);
        kfree(info->mem);
dealloc_fb:
        platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
        return ret;
}

我们在介绍了framebuffer相关知识后 会在重新分析该代码的实现

(3)LCDanalysis3

framebuffer相关知识介绍.


Introduction

framebuffer相关知识.

Details

  • framebuffer简介
    帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。 Linux为了开发FrameBuffer程序的方便,使用了分层结构。fbmem.c处于Framebuffer设备驱动技术的中心位置。它为上层应用程序提供系统调用,也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己 其中文件s3c2410fb.c就是特定硬件驱动(针对s3c2410芯片的),fbmem.c就是沟通应用层跟s3c2410fb.c的桥梁


  • framebuffer中主要结构体介绍
    结构体fb_info 位于 kernel/include/linux/fb.h ,fb_info中纪录了帧缓冲设备的全部信息,包括设备的设置参数,状态以及操作函数指针。每一个帧缓冲设备都必须对应一个fb_info结构 还记得在 s3c2410fb_probe 这个函数吗 在这个函数中我们所作的主要工作就是填充结构体 fb_info 结构体
struct fb_info {
        int node;
       //其中node成员域标示了特定的FrameBuffer,实际上也就是一个FrameBuffer设备的次设备号
        int flags;
        struct fb_var_screeninfo var;   /* Current var */
        struct fb_fix_screeninfo fix;   /* Current fix */
        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;
       //fbops为指向底层操作的函数的指针
        struct device *device;          /* This is the parent */
        struct device *dev;             /* This is this fb 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 */
        unsigned long screen_size;      /* Amount of ioremapped VRAM or 0 */ 
        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;      
};

  • fb_var_screeninfo 结构体
    fb_var_screeninfo结构体成员记录用户可修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数 fb_var_screeninfo中的xres定义屏幕一行有多少个点, yres定义屏幕一列有多少个点,bits_per_pixel定义每个点用多少个字节表示 定义如下:
struct fb_var_screeninfo {
        __u32 xres;                     /* visible resolution           */
        __u32 yres;
        __u32 xres_virtual;             /* virtual resolution           */
        __u32 yres_virtual;
        __u32 xoffset;                  /* offset from virtual to visible */
        __u32 yoffset;                  /* resolution                   */

        __u32 bits_per_pixel;           /* guess what                   */
        __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;                   /* height of picture in mm    */
        __u32 width;                    /* width of picture in 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) */
        __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 */
};

  • fb_fix_screeninfo 结构体 kernel/include/linux/fb.h
    fb_fix_screeninfo中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的 物理地址,长度。当对帧缓冲设备进行映射操作的时候,就是从fb_fix_screeninfo中取得缓冲区物理地址的 结构体如下:
struct fb_fix_screeninfo {
        char id[16];                    /* identification string eg "TT Builtin" */
        unsigned long smem_start;       /* Start of frame buffer mem */
                                        /* (physical address) */
        __u32 smem_len;                 /* Length of frame buffer mem */
        __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    */
        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 */
};

s3c2410fb_info 结构体 drivers/vedio/s3c2410fb.h 结构体如下:


struct s3c2410fb_info {
        struct device           *dev;
        struct clk              *clk;

        struct resource         *mem;
        void __iomem            *io;
        void __iomem            *irq_base;

        enum s3c_drv_type       drv_type;
        struct s3c2410fb_hw     regs;

        unsigned int            palette_ready;

        /* keep these registers in case we need to re-write palette */
        u32                     palette_buffer[256];
        u32                     pseudo_pal[16];
};

s3c2410fb_mach_info 结构体 kernel/include/asm-arm/arch-s3c2410/fb.h 

从字面上我们不难看出 和具体的硬件有关 结构体定义如下:
struct s3c2410fb_mach_info {

        struct s3c2410fb_display *displays;     /* attached diplays info */
        unsigned num_displays;                  /* number of defined displays */
        unsigned default_display;

        /* GPIOs */

        unsigned long   gpcup;
        unsigned long   gpcup_mask;
        unsigned long   gpccon;
        unsigned long   gpccon_mask;
        unsigned long   gpdup;
        unsigned long   gpdup_mask;
        unsigned long   gpdcon;
        unsigned long   gpdcon_mask;

        /* lpc3600 control register */
        unsigned long   lpcsel;
};
/* LCD description */
struct s3c2410fb_display {
        /* LCD type */
        unsigned type;

        /* Screen size */
        unsigned short width;
        unsigned short height;

        /* Screen info */
        unsigned short xres;
        unsigned short yres;
        unsigned short bpp;

        unsigned pixclock;              /* pixclock in picoseconds */
        unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
        unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
        unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
        unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */
        unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
        unsigned short vsync_len;       /* value in lines (TFT) or 0 (STN) */

        /* lcd configuration registers */
        unsigned long   lcdcon5;
};
看到这个结构体应该不陌生 我们在lcd驱动移植步骤的第二步 就是填充了这个结构体 这个结构体和具体的硬件有关

(4)LCDanalysis4

Introduction

我们接着介绍framebuffer 在下面着重介绍struct file_operations 和 struct fb_ops .

Details

  • struct file_operations
    对于struct file_operation 我们在熟悉不过了 file_operations是提供给上层系统调用的接口,可以直接调用. 定义在 kernel/include/linux/fs.h 中 定义如下:
struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*dir_notify)(struct file *filp, unsigned long arg);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **);
};
对于LCD来说 该结构体的填充 位于kernel/drivers/vedio/fbmem.c中 代码如下:
static const struct file_operations fb_fops = {
        .owner =        THIS_MODULE,
        .read =         fb_read,
        .write =        fb_write,
        .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
};

struct fb_ops 在 kernel/include/linux/fb.h中
struct fb_ops 结构包含在fb_info结构中,指向驱动设备工作所需的函数集。fb_ops结构中包含了很多涵数指针 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);

        /* Rotates the display */
        void (*fb_rotate)(struct fb_info *info, int angle);

        /* 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);

        /* save current hardware state */
        void (*fb_save_state)(struct fb_info *info);

        /* restore saved state */
        void (*fb_restore_state)(struct fb_info *info);

        /* get capability given var */
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
                            struct fb_var_screeninfo *var);
};

对于LCD来说 该结构体的填充位于kernel/drivers/vedio/s3c2410fb.c 中 代码如下:
static struct fb_ops s3c2410fb_ops = {
        .owner          = THIS_MODULE,
        .fb_check_var   = s3c2410fb_check_var,
        .fb_set_par     = s3c2410fb_set_par,
        .fb_blank       = s3c2410fb_blank,
        .fb_setcolreg   = s3c2410fb_setcolreg,
        .fb_fillrect    = cfb_fillrect,
        .fb_copyarea    = cfb_copyarea,
        .fb_imageblit   = cfb_imageblit,
};
两个结构体之间的关系
 用户应用程序通过ioctl()系统调用操作硬件,fb_ops 中的函数就用于支持这些操作。
   ioctl()系统调用在文件fbmem.c中实现,通过观察可以发现ioctl()命令与fb_opss 中函数的关系:
   FBIOGET_VSCREENINFO fb_get_var
   FBIOPUT_VSCREENINFO fb_set_var
   FBIOGET_FSCREENINFO fb_get_fix
   FBIOPUTCMAP fb_set_cmap
   FBIOGETCMAP fb_get_cmap
   FBIOPAN_DISPLAY fb_pan_display
   如果我们定义了fb_XXX_XXX 方法,用户程序就可以使用FBIOXXXX宏的ioctl()操作来操作硬件。

怎么将一个设备文件 也就是dev目录下的文件和file_operation关联起来呢 ? 从上图中 我们可以知道是通过register_chrdev()函数注册实现的 位于kernel/drivers/vedio/fbmem.c中 实现如下
fbmem_init(void)
{
        create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);

        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
                printk("unable to get major %d for fb devs\n", FB_MAJOR);

        fb_class = class_create(THIS_MODULE, "graphics");
        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;
}

#ifdef MODULE
module_init(fbmem_init);
static void __exit
fbmem_exit(void)
{
        remove_proc_entry("fb", NULL);
        class_destroy(fb_class);
        unregister_chrdev(FB_MAJOR, "fb");
}

module_exit(fbmem_exit);

(5)LCDanalysis5

各个结构体之间的关系 和 register_framebuffer()函数的实现.

Introduction

platfrom_device fb_info s3c2410fb_info s3c2410fb_mach_info 之间是怎么关联的. register_framebuffer()是如何实现注册的.

Details

  • platform_device fb_info s3c2410fb_info s3c2410fb_mach_info 之间的关系
       platform_device  ---------------- s3c2410fb_mach_info 
    
        mach_info= pdev->dev.platform_data  

       其中 mach_info 为s3c2410fb_info类型的指针   
        其实mach_info中存放的是LCD的初始化时所用的值
        pdev 为结构体platform_device类 型的指针
        还记得我们在驱动移植步骤中的第二步就是填充结构体 mach_info 
       然后 调用  s3c24xx_fb_set_platdata(&qt2410_biglcd_cfg); 就是将 platform_device同mach_info联系起来


        fb_info---------------------s3c2410fb 
           
            info = fbinfo->par 
            info->fb = fbinfo
       其中 info 为 结构体s3c2410fb_info类型的指针
             fbinfo为结构体fb_info类型的指针

          platform_device -------------- fb_info
          
         pdev->dev.driver_data = fbinfo  
       其中 pdev为 结构体platform_device类型的指针
             fbinfo为结构体 fb_info 类型的指针


      platform_device  ------------ platform_driver
        这个在前面介绍着两个结构体的时候都说过了 是通过 name属性相互关联起来的


这样所有的主要结构体都串起来了
  • 总结一下 如下图 :
     结构体platform_driver

              |               
              | 
              | 通过name 相互关联 
              | 
              | 
              | 
   结构体 platform_device {}                  结构体 s3c2410fb_mach_info     |                                                        |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |       上面两个通过dev. platform_data关联  |
              |--------------- 结构体device---------------|        
 dev.data将上下|                                          |
 两个结构体关联 |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |                                          |
              |       你中有我  我中有你                      |
    结构体 fb_info---------------------------- 结构体 s3c2410fb


分析 register_framebuffer()函数的实现

fbmem.c实现了Linux FrameBuffer的中间层,任何一个FrameBuffer驱动,在系统初始化时,必须向fbmem.c注册,即需要调用register_framebuffer()函数,在这个过程中,设备驱动的信息将会存放入名称为registered_fb数组中 这个数组定义为struct fb_info*registered_fbFB_MAX、int num_registered_fb;它是类型为fb_info的数组,另外num_register_fb则存放了注册过的设备数量
代码如下:
register_framebuffer(struct fb_info *fb_info)
{
        int i;
        struct fb_event event;
        struct fb_videomode mode;

        if (num_registered_fb == FB_MAX)
                return -ENXIO;
        //看数组时否已经满了  是否超过最大注册数量
        num_registered_fb++;
        for (i = 0 ; i < FB_MAX; i++)
                if (!registered_fb[i])   //找到空余的空间
                        break;
        fb_info->node = i;
                fb_info->dev = device_create(fb_class, fb_info->device,
                                     MKDEV(FB_MAJOR, i), "fb%d", i);
        ////建立的设备节点的次设备号就是该驱动信息在registered_fb存放的位置,即数组下标i 
        if (IS_ERR(fb_info->dev)) {
                /* Not fatal */
                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
                fb_info->dev = NULL;
        } else
                fb_init_device(fb_info);
             //初始化设备
        if (fb_info->pixmap.addr == NULL) {
                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
                if (fb_info->pixmap.addr) {
                        fb_info->pixmap.size = FBPIXMAPSIZE;
                        fb_info->pixmap.buf_align = 1;
                        fb_info->pixmap.scan_align = 1;
                        fb_info->pixmap.access_align = 32;
                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
                }
        }       
        fb_info->pixmap.offset = 0;

        if (!fb_info->pixmap.blit_x)
                fb_info->pixmap.blit_x = ~(u32)0;

        if (!fb_info->pixmap.blit_y)
                fb_info->pixmap.blit_y = ~(u32)0;

        if (!fb_info->modelist.prev || !fb_info->modelist.next)
                INIT_LIST_HEAD(&fb_info->modelist);

        fb_var_to_videomode(&mode, &fb_info->var);
        fb_add_videomode(&mode, &fb_info->modelist);
        registered_fb[i] = fb_info;

        event.info = fb_info;
        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
        return 0;
}

当FrameBuffer驱动进行注册的时候,它将驱动的fb_info结构体记录到全局数组registered_fb中,并动态建立设备节点,进行设备的初始化。



阅读更多
个人分类: Linux
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭