linux 自带LCD驱动框架(三)

一、驱动框架图

二、内核kernel层(Framebuffer)

2.1、入口出口函数(init exit)

/*linux/drivers/video/fbmem.c*/

主设备号: 29,

提供了读写相关接口函数

#define FB_MAJOR        29   /* /dev/fb* framebuffers */

static const struct file_operations fb_fops = {
、、、、、、、、、
    .read =        fb_read,
    .write =    fb_write,
    .unlocked_ioctl = fb_ioctl,
    .mmap =        fb_mmap,
    .open =        fb_open,
    .release =    fb_release,
、、、、、、、
};

static int __init fbmem_init(void)
{
    
、、、、、、、

    ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
    
、、、、、、、
}

2.2、fb_open

/*linux/drivers/video/fbmem.c*/

static struct fb_info *get_fb_info(unsigned int idx)
{
    struct fb_info *fb_info;
、、、、、、、、

    fb_info = registered_fb[idx]; 
    
、、、、、、、
    return fb_info;
}

fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{
    int fbidx = iminor(inode);
    
、、、、、、、
    info = get_fb_info(fbidx);  //根据从fb_info结构体数组中取出一个成员
、、、、、、、、    
    
}

2.3、fb_read

/*linux/drivers/video/fbmem.c*/

又是操作  struct fb_info *registered_fb[FB_MAX]; 数组

static struct fb_info *file_fb_info(struct file *file)
{
    、、、、、、
    struct fb_info *info = registered_fb[fbidx];

    、、、、
}

static ssize_t  fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    unsigned long p = *ppos;
    struct fb_info *info = file_fb_info(file);  //从registered_fb数组中取出一个成员
    u8 *buffer, *dst;
    u8 __iomem *src;
    int c, cnt = 0, err = 0;
    unsigned long total_size;

    
    if (info->fbops->fb_read)
        return info->fbops->fb_read(info, buf, count, ppos);
    

、、、、、、

    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
             GFP_KERNEL);
、、、、

    src = (u8 __iomem *) (info->screen_base + p);
    
、、、、、、
        while (count) {
        c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
        dst = buffer;
        fb_memcpy_fromfb(dst, src, c);

        dst += c;
        src += c;

        if (copy_to_user(buf, buffer, c)) {
            err = -EFAULT;
            break;
        }
        *ppos += c;
        buf += c;
        cnt += c;
        count -= c;
    }

    、、、、、、、、、

    return (err) ? err : cnt;
}

2.4、registered_fb[FB_MAX] 初始化(设置)register_framebuffer

int  register_framebuffer(struct fb_info *fb_info)
{
    int ret;

    mutex_lock(&registration_lock);
    ret = do_register_framebuffer(fb_info);
    mutex_unlock(&registration_lock);

    return ret;
}

2.5、struct fb_info结构体解析

struct fb_info {
    atomic_t count;
    int node;
    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;    /*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 */

、、、、、、、
 

    struct fb_ops *fbops;     /*底层操作函数,大部分用内核提供的,不需要实现*/
    struct device *device;        /* This is the parent */
    struct device *dev;        /* This is this fb device */
    int class_flag;                    /* private sysfs flags */
、、、、、、
    union {
        char __iomem *screen_base;    /* Virtual address LCD显存虚拟地址*/
        char *screen_buffer;
    };
    unsigned long screen_size;    /* Amount of ioremapped VRAM or 0 */ 
    void *pseudo_palette;        /* Fake palette of 16 colors */ 
、、、、、、、
    /* From here on everything is device dependent */
    void *par;  //私有数据,可以存放自己的私有数据
  、、、、、、、、

    bool skip_vt_switch; /* no VT switch on suspend/resume required */
};
 

2.6、struct fb_var_screeninfo var;  结构体

struct fb_var_screeninfo { ///显示屏信息
    __u32 xres;            /* visible resolution*//可视区域,一行有多少个像素点
    __u32 yres;            ///可视区域,一列有多少个像素点
    __u32 xres_virtual;  /* virtual resolution*//虚拟区域,一行有多少个像素点,简单的意思就是内存中定义的区间是比较大的
    __u32 yres_virtual;////虚拟区域,一列有多少个像素点
    __u32 xoffset;            //虚拟到可见屏幕之间的行偏移
    __u32 yoffset;            /* resolution *//虚拟到可见屏幕之间的列偏移

    __u32 bits_per_pixel; /* guess what*/ 每个像素的 bit 数,这个参数不需要自己配置,而是通过上层在调用 checkvar 函数传递 bpp 的时候赋值的
    __u32 grayscale;        /* 0 = color, 1 = grayscale,*////等于零就成黑白 (灰度)
                    /* >1 = FOURCC            */
    // 通过 pixel per bpp 来设定 red green 和 blue 的位置; pixel per bpp 可以通过 ioctl 设定
    struct fb_bitfield red;        //fb缓存的R位域
    struct fb_bitfield green;    /* else only length is significant *//fb缓存的G位域
    struct fb_bitfield blue;                                        //fb缓存的B位域
    struct fb_bitfield transp;    /* transparency *//透明度

    __u32 nonstd;            /* != 0 Non standard pixel format *///如果nonstd 不等于0,非标准的像素格式

    __u32 activate;            /* see FB_ACTIVATE_*     */

    __u32 height;         //内存中的图像高度
    __u32 width;        //内存中的图像宽度

    __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;         //行切换HBPD,从同步到绘图之间的延迟 左边框,驱动程序一般不设置
    __u32 right_margin;        /* (HFPD)    *///行切换,从绘图到同步之间的延迟边框,驱动程序一般不设置
    __u32 upper_margin;        /* VBPD    *///帧切换,从同步到绘图之间的延迟  边框,驱动程序一般不设置
    __u32 lower_margin;          //(VFPD)    //帧切换,从绘图到同步之间的延迟    边框,驱动程序一般不设置
    __u32 hsync_len;        /* length of horizontal sync    */ //水平同步的长度
    __u32 vsync_len;        /* length of vertical sync    */ //垂直同步的长度

    __u32 sync;            /* see FB_SYNC_*      *////---->看 FB_SYNC_*
    __u32 vmode;            /* see FB_VMODE_*      *////---->看 FB_VMODE_*
    __u32 rotate;            /* angle we rotate counter clockwise */
    __u32 colorspace;        /* colorspace for FOURCC-based modes */
    __u32 reserved[4];        /* Reserved for future compatibility */
};

2.7、struct fb_fix_screeninfo

struct fb_fix_screeninfo {
    char id[16];            //字符串形式的标识符
    unsigned long smem_start;   //显存物理起始地址,注意,是物理址,不是虚拟地址
    __u32 smem_len;            /* Length of frame buffer mem *///fb 显存的长度
    __u32 type;            /* see FB_TYPE_*        *////看FB_TYPE_* -->
    __u32 type_aux;            /* Interleave for interleaved Planes *///分界
    __u32 visual;             ///看FB_VISUAL_* -->
    __u16 xpanstep;         //如果没有硬件panning就赋值为0
    __u16 ypanstep;            //如果没有硬件panning就赋值为0
    __u16 ywrapstep;        //如果没有硬件ywrap就赋值为0
    __u32 line_length;        //一行的字节数
    unsigned long mmio_start;   //内存映射 IO的开始位置
                    /* (physical address) */
    __u32 mmio_len;            //内存映射 IO的长度
    __u32 accel;            /* Indicate to driver which    */
                    /*  specific chip/card we have    */
    __u16 capabilities;        /* see FB_CAP_*            *///功能 ---FB_CAP_FOURCC--- Device supports FOURCC-based formats
    __u16 reserved[2];       //为以后的兼容性保留
};

三、平台设备驱动层

3.1、 s3c_fb_driver

static const struct platform_device_id s3c_fb_driver_ids[] = {
    {
        .name        = "s3c-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_64xx,
    }, {
        .name        = "s5pv210-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s5pv210,
    }, {
        .name        = "exynos4-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_exynos4,
    }, {
        .name        = "exynos5-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_exynos5,
    }, {
        .name        = "s3c2443-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s3c2443,
    },
    {},
};

static struct platform_driver s3c_fb_driver = {
    .probe        = s3c_fb_probe,   //重点分析
    .remove        = s3c_fb_remove,
    .id_table    = s3c_fb_driver_ids,
    .driver        = {
        .name    = "s3c-fb",
        .pm    = &s3cfb_pm_ops,
    },
};

3.2、s3c_fb_probe分析

static int s3c_fb_probe(struct platform_device *pdev)

{
    const struct platform_device_id *platid;
    struct s3c_fb_driverdata *fbdrv;
    struct device *dev = &pdev->dev;
    struct s3c_fb_platdata *pd;
    struct s3c_fb *sfb;
    struct resource *res;
    int win;
    int ret = 0;
    u32 reg;

    platid = platform_get_device_id(pdev);
    fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;

    if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
        dev_err(dev, "too many windows, cannot attach\n");
        return -EINVAL;
    }

    pd = dev_get_platdata(&pdev->dev);
    if (!pd) {
        dev_err(dev, "no platform data specified\n");
        return -EINVAL;
    }

    sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL);
    if (!sfb) {
        dev_err(dev, "no memory for framebuffers\n");
        return -ENOMEM;
    }

    dev_dbg(dev, "allocate new framebuffer %p\n", sfb);

    sfb->dev = dev;
    sfb->pdata = pd;
    sfb->variant = fbdrv->variant;

    spin_lock_init(&sfb->slock);

    sfb->bus_clk = devm_clk_get(dev, "lcd");
    if (IS_ERR(sfb->bus_clk)) {
        dev_err(dev, "failed to get bus clock\n");
        return PTR_ERR(sfb->bus_clk);
    }

    clk_prepare_enable(sfb->bus_clk);

    if (!sfb->variant.has_clksel) {
        sfb->lcd_clk = devm_clk_get(dev, "sclk_fimd");
        if (IS_ERR(sfb->lcd_clk)) {
            dev_err(dev, "failed to get lcd clock\n");
            ret = PTR_ERR(sfb->lcd_clk);
            goto err_bus_clk;
        }

        clk_prepare_enable(sfb->lcd_clk);
    }

    pm_runtime_enable(sfb->dev);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    sfb->regs = devm_ioremap_resource(dev, res);
    if (IS_ERR(sfb->regs)) {
        ret = PTR_ERR(sfb->regs);
        goto err_lcd_clk;
    }

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (!res) {
        dev_err(dev, "failed to acquire irq resource\n");
        ret = -ENOENT;
        goto err_lcd_clk;
    }
    sfb->irq_no = res->start;
    ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq,
              0, "s3c_fb", sfb);
    if (ret) {
        dev_err(dev, "irq request failed\n");
        goto err_lcd_clk;
    }

    dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);

    platform_set_drvdata(pdev, sfb);
    pm_runtime_get_sync(sfb->dev);

    /* setup gpio and output polarity controls */

    pd->setup_gpio();

    writel(pd->vidcon1, sfb->regs + VIDCON1);

    /* set video clock running at under-run */
    if (sfb->variant.has_fixvclk) {
        reg = readl(sfb->regs + VIDCON1);
        reg &= ~VIDCON1_VCLK_MASK;
        reg |= VIDCON1_VCLK_RUN;
        writel(reg, sfb->regs + VIDCON1);
    }

    /* zero all windows before we do anything */

    for (win = 0; win < fbdrv->variant.nr_windows; win++)
        s3c_fb_clear_win(sfb, win);

    /* initialise colour key controls */
    for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
        void __iomem *regs = sfb->regs + sfb->variant.keycon;

        regs += (win * 8);
        writel(0xffffff, regs + WKEYCON0);
        writel(0xffffff, regs + WKEYCON1);
    }

    s3c_fb_set_rgb_timing(sfb);

    /* we have the register setup, start allocating framebuffers */

    for (win = 0; win < fbdrv->variant.nr_windows; win++) {
        if (!pd->win[win])
            continue;

        ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],&sfb->windows[win]);
        if (ret < 0) {
            dev_err(dev, "failed to create window %d\n", win);
            for (; win >= 0; win--)
                s3c_fb_release_win(sfb, sfb->windows[win]);
            goto err_pm_runtime;
        }
    }

    platform_set_drvdata(pdev, sfb);
    pm_runtime_put_sync(sfb->dev);

    return 0;

err_pm_runtime:
    pm_runtime_put_sync(sfb->dev);

err_lcd_clk:
    pm_runtime_disable(sfb->dev);

    if (!sfb->variant.has_clksel)
        clk_disable_unprepare(sfb->lcd_clk);

err_bus_clk:
    clk_disable_unprepare(sfb->bus_clk);

    return ret;
}

static int s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
                struct s3c_fb_win_variant *variant,
                struct s3c_fb_win **res)
{
    struct fb_var_screeninfo *var;
    struct fb_videomode initmode;
    struct s3c_fb_pd_win *windata;
    struct s3c_fb_win *win;
    struct fb_info *fbinfo;
    int palette_size;
    int ret;

    dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);

    init_waitqueue_head(&sfb->vsync_info.wait);

    palette_size = variant->palette_sz * 4;

    fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
                   palette_size * sizeof(u32), sfb->dev);

    if (!fbinfo) {
        dev_err(sfb->dev, "failed to allocate framebuffer\n");
        return -ENOENT;
    }

    windata = sfb->pdata->win[win_no];
    initmode = *sfb->pdata->vtiming;

    WARN_ON(windata->max_bpp == 0);
    WARN_ON(windata->xres == 0);
    WARN_ON(windata->yres == 0);

    win = fbinfo->par;
    *res = win;
    var = &fbinfo->var;
    win->variant = *variant;
    win->fbinfo = fbinfo;
    win->parent = sfb;
    win->windata = windata;
    win->index = win_no;
    win->palette_buffer = (u32 *)(win + 1);

    ret = s3c_fb_alloc_memory(sfb, win);
    if (ret) {
        dev_err(sfb->dev, "failed to allocate display memory\n");
        return ret;
    }

    /* setup the r/b/g positions for the window's palette */
    if (win->variant.palette_16bpp) {
        /* Set RGB 5:6:5 as default */
        win->palette.r.offset = 11;
        win->palette.r.length = 5;
        win->palette.g.offset = 5;
        win->palette.g.length = 6;
        win->palette.b.offset = 0;
        win->palette.b.length = 5;

    } else {
        /* Set 8bpp or 8bpp and 1bit alpha */
        win->palette.r.offset = 16;
        win->palette.r.length = 8;
        win->palette.g.offset = 8;
        win->palette.g.length = 8;
        win->palette.b.offset = 0;
        win->palette.b.length = 8;
    }

    /* setup the initial video mode from the window */
    initmode.xres = windata->xres;
    initmode.yres = windata->yres;
    fb_videomode_to_var(&fbinfo->var, &initmode);

    fbinfo->fix.type    = FB_TYPE_PACKED_PIXELS;
    fbinfo->fix.accel    = FB_ACCEL_NONE;
    fbinfo->var.activate    = FB_ACTIVATE_NOW;
    fbinfo->var.vmode    = FB_VMODE_NONINTERLACED;
    fbinfo->var.bits_per_pixel = windata->default_bpp;
    fbinfo->fbops        = &s3c_fb_ops;
    fbinfo->flags        = FBINFO_FLAG_DEFAULT;
    fbinfo->pseudo_palette  = &win->pseudo_palette;

    /* prepare to actually start the framebuffer */

    ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
    if (ret < 0) {
        dev_err(sfb->dev, "check_var failed on initial video params\n");
        return ret;
    }

    /* create initial colour map */

    ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
    if (ret == 0)
        fb_set_cmap(&fbinfo->cmap, fbinfo);
    else
        dev_err(sfb->dev, "failed to allocate fb cmap\n");

    s3c_fb_set_par(fbinfo);

    dev_dbg(sfb->dev, "about to register framebuffer\n");

    /* run the check_var and set_par on our configuration. */

    ret = register_framebuffer(fbinfo);
    if (ret < 0) {
        dev_err(sfb->dev, "failed to register framebuffer\n");
        return ret;
    }

    dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);

    return 0;
}

发布了137 篇原创文章 · 获赞 106 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/103718137