linux framebuffer1

 

先看看一些比较重要的结构体: 
struct fb_info {   
    int node;    
    int flags;   
    struct mutex lock;        
    struct fb_var_screeninfo var;     
    struct fb_fix_screeninfo fix;     
    struct fb_monspecs monspecs;      
    struct work_struct queue;     
    struct fb_pixmap pixmap;      
    struct fb_pixmap sprite;      
    struct fb_cmap cmap;          
    struct list_head modelist;         
    struct fb_videomode *mode;   
 
#ifdef CONFIG_FB_BACKLIGHT   
    struct backlight_device *bl_dev;   
    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;        
    struct device *dev;       
    int class_flag;                  
#ifdef CONFIG_FB_TILEBLITTING   
    struct fb_tile_ops *tileops;     
#endif   
    char __iomem *screen_base;    
    unsigned long screen_size;      
    void *pseudo_palette;          
#define FBINFO_STATE_RUNNING    
#define FBINFO_STATE_SUSPENDED   
    u32 state;            
    void *fbcon_par;                   
    void *par;     
};
struct fb_fix_screeninfo {   
    char id[16];              
    unsigned long smem_start;     
    __u32 smem_len;           
    __u32 type;           
    __u32 type_aux;           
    __u32 visual;               
    __u16 xpanstep;           
    __u16 ypanstep;            
    __u16 ywrapstep;           
    __u32 line_length;        
    unsigned long mmio_start;     
    __u32 mmio_len;           
    __u32 accel;               
    __u16 reserved[3];        
};
struct fb_var_screeninfo {   
      
    __u32 xres;            
    __u32 yres;   
      
    __u32 xres_virtual;        
    __u32 yres_virtual;   
      
    __u32 xoffset;             
    __u32 yoffset;             
    __u32 bits_per_pixel;         
    __u32 grayscale;          
      
    struct fb_bitfield red;        
    struct fb_bitfield green;      
    struct fb_bitfield blue;   
    struct fb_bitfield transp;      
    __u32 nonstd;             
    __u32 activate;            
    __u32 height;             
    __u32 width;              
    __u32 accel_flags;         
      
    __u32 pixclock;           
    __u32 left_margin;        
    __u32 right_margin;       
    __u32 upper_margin;       
    __u32 lower_margin;       
    __u32 hsync_len;          
    __u32 vsync_len;          
    __u32 sync;            
    __u32 vmode;               
    __u32 rotate;             
    __u32 reserved[5];        
};

struct fb_cmap {   
    __u32 start;              
    __u32 len;            
      
    __u16 *red;            
    __u16 *green;   
    __u16 *blue;   
    __u16 *transp;             
}; 


 
struct fb_bitfield {   
    __u32 offset;         
    __u32 length;         
    __u32 msb_right;         
}; 


 
struct fb_ops {   
    struct module *owner;   
      
    int (*fb_open)(struct fb_info *info, int user);   
    int (*fb_release)(struct fb_info *info, int user);   
      
    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);   
      
    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);   
      
    int (*fb_set_par)(struct fb_info *info);   
      
    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,   
                unsigned blue, unsigned transp, struct fb_info *info);   
      
    int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);   
      
    int (*fb_blank)(int blank, struct fb_info *info);   
      
    int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);   
      
    void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);   
      
    void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);   
      
    void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);   
      
    int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);   
      
    void (*fb_rotate)(struct fb_info *info, int angle);   
      
    int (*fb_sync)(struct fb_info *info);   
      
    int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,   
            unsigned long arg);   
      
    int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,   
            unsigned long arg);   
      
    int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);   
      
    void (*fb_save_state)(struct fb_info *info);   
      
    void (*fb_restore_state)(struct fb_info *info);   
      
    void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,   
                struct fb_var_screeninfo *var);   
}; 

下面详细分析一下framebuffer的驱动源码,framebuffer作为一个平台驱动注册进内核:

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,   
    },   
};   
  
int __init s3c2410fb_init(void)   
  
    int ret = platform_driver_register(&s3c2410fb_driver);   
  
    if (ret == 0)   
        ret = platform_driver_register(&s3c2412fb_driver);;   
  
    return ret;   
  
  
static void __exit s3c2410fb_cleanup(void)   
  
    platform_driver_unregister(&s3c2410fb_driver);   
    platform_driver_unregister(&s3c2412fb_driver);   
  
  
module_init(s3c2410fb_init);   
module_exit(s3c2410fb_cleanup); 

在arch/arm/plat-s3c24xx/devs.c中定义了framebuffer的平台设备:

  
static struct resource s3c_lcd_resource[] = {   
    [0] = {   
        .start = S3C24XX_PA_LCD,                              //IO内存的物理起始地址   
        .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,         //IO内存的物理结束地址    
        .flags = IORESOURCE_MEM,                   
    },   
    [1] = {   
        .start = IRQ_LCD,                                     //LCD的中断号   
        .end   = IRQ_LCD,   
        .flags = IORESOURCE_IRQ,   
     
  
};   
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   
     
};   
  
EXPORT_SYMBOL(s3c_device_lcd); 

devs.c中的这个函数把s3c2410fb_mach_info存放到s3c_device_lcd.dev.platform_data,probe函数中会用到的。

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)   
  
    struct s3c2410fb_mach_info *npd;   
  
    npd = kmalloc(sizeof(*npd), GFP_KERNEL);   
    if (npd) {   
        memcpy(npd, pd, sizeof(*npd));   
        s3c_device_lcd.dev.platform_data = npd;   
    } else {   
        printk(KERN_ERR "no memory for LCD platform data\n");   
     

这个函数是在arch/arm/mach-s3c2440/mach-smdk2440.c中的smdk2440_machine_init中调用的,所以在系统启动后会自动调用。

static void __init smdk2440_machine_init(void)   
  
    s3c24xx_fb_set_platdata(&smdk2440_fb_info);   
    s3c_i2c0_set_platdata(NULL);   
    platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));   
    smdk_machine_init();   

s3c2410fb_display表示屏的显示参数,这个结构体在我们移植LCD驱动的时候需要根据我们屏的参数重新设置。

  
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {   
  
    .lcdcon5    = S3C2410_LCDCON5_FRM565 |   
              S3C2410_LCDCON5_INVVLINE |   
              S3C2410_LCDCON5_INVVFRAME |   
              S3C2410_LCDCON5_PWREN |   
              S3C2410_LCDCON5_HWSWP,   
  
    .type       = S3C2410_LCDCON1_TFT,   
  
    .width      = 240,   
    .height     = 320,   
  
    .pixclock   = 270000,    
    .xres       = 320,   
    .yres       = 240,   
    .bpp        = 16,   
    .left_margin    = 8,   
    .right_margin   = 5,   
    .hsync_len  = 63,   
    .upper_margin   = 15,   
    .lower_margin   = 3,   
    .vsync_len  = 5,   
}; 

将s3c2410fb_display结构体存于s3c2410fb_mach_info的displays域。

static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {   
    .displays   = &smdk2440_lcd_cfg,   
    .num_displays   = 1,   
    .default_display = 0,   
    .lpcsel     = 0,   
}; 

下面来看看当lcd驱动和设备匹配成功后会调用的探测函数:

static int __init s3c2410fb_probe(struct platform_device *pdev)   
  
    return s3c24xxfb_probe(pdev, DRV_S3C2410);   

这里调用了s3c24xxfb_probe(pdev, DRV_S3C2410),进行了一层封装,因为这样这部分代码可以与s3c2412进行复用。

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;   
    这4句构建的关系图如下:   
  
         
    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);   
  
      
    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);   
  
      
    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;   
     
      
    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;   
     
  
      
    ret = device_create_file(&pdev->dev, &dev_attr_debug);  //创建设备文件   
    if (ret) {   
        printk(KERN_ERR "failed to add debug attribute\n");   
     
  
    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;   

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)   
 
#define BYTES_PER_LONG (BITS_PER_LONG/8)  
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))   
    int fb_info_size = sizeof(struct fb_info);   
    struct fb_info *info;   
    char *p;   
    if (size)   
        fb_info_size += PADDING;   
      
    p = kzalloc(fb_info_size + size, GFP_KERNEL);   
    if (!p)   
        return NULL;   
    info = (struct fb_info *) p;   
      
    if (size)   
        info->par = p + fb_info_size;   
    info->device = dev;  
 
#ifdef CONFIG_FB_BACKLIGHT   
    mutex_init(&info->bl_curve_mutex);  
#endif   
  
    return info;  
#undef PADDING  
#undef BYTES_PER_LONG   

中断处理函数:

static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)   
  
    struct s3c2410fb_info *fbi = dev_id;   
      
    void __iomem *irq_base = fbi->irq_base;   
      
    unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);   
      
    if (lcdirq & S3C2410_LCDINT_FRSYNC) {   
          
        if (fbi->palette_ready)   
            s3c2410fb_write_palette(fbi);   
          
        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);   
        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);   
     
  
    return IRQ_HANDLED;   

填充调色板:

static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)   
  
    unsigned int i;   
    void __iomem *regs = fbi->io;   
  
    fbi->palette_ready = 0;   
  
    for (i = 0; i < 256; i++) {   
        unsigned long ent = fbi->palette_buffer[i];   
        if (ent == PALETTE_BUFF_CLEAR)   
            continue;   
  
        writel(ent, regs + S3C2410_TFTPAL(i));   
  
          
  
        if (readw(regs + S3C2410_TFTPAL(i)) == ent)   
            fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;   
        else  
            fbi->palette_ready = 1;     
     

申请帧缓冲设备fb_info的缓冲区空间:

static int __init s3c2410fb_map_video_memory(struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    dma_addr_t map_dma;   
      
    unsigned map_size = PAGE_ALIGN(info->fix.smem_len);   
  
    dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);   
      
    info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,   
                           &map_dma, GFP_KERNEL);   
    if (info->screen_base) {   
          
        dprintk("map_video_memory: clear %p:x\n",   
            info->screen_base, map_size);   
          
        memset(info->screen_base, 0x00, map_size);    
          
        info->fix.smem_start = map_dma;   
        dprintk("map_video_memory: dma=lx cpu=%p size=x\n",   
            info->fix.smem_start, info->screen_base, map_size);   
     
      
    return info->screen_base ? 0 : -ENOMEM;   

初始化相关寄存器:

static int s3c2410fb_init_registers(struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;   
    unsigned long flags;   
      
    void __iomem *regs = fbi->io;   
    void __iomem *tpal;   
    void __iomem *lpcsel;   
  
    if (is_s3c2412(fbi)) {   
        tpal = regs + S3C2412_TPAL;   
        lpcsel = regs + S3C2412_TCONSEL;   
    } else {   
          
        tpal = regs + S3C2410_TPAL;   
        lpcsel = regs + S3C2410_LPCSEL;   
     
  
      
      
    local_irq_save(flags);   
  
      
      
    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);   
    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);   
    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);   
    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);   
      
    local_irq_restore(flags);   
  
    dprintk("LPCSEL    = 0xlx\n", mach_info->lpcsel);   
    writel(mach_info->lpcsel, lpcsel);   
  
    dprintk("replacing TPAL x\n", readl(tpal));   
  
      
    writel(0x00, tpal);   
  
    return 0;   

设置fb_info中的可变参数:

static int s3c2410fb_check_var(struct fb_var_screeninfo *var,   
                   struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;   
    struct s3c2410fb_display *display = NULL;   
    struct s3c2410fb_display *default_display = mach_info->displays +   
                            mach_info->default_display;   
      
    int type = default_display->type;   
    unsigned i;   
  
    dprintk("check_var(var=%p, info=%p)\n", var, info);   
  
      
    if (var->yres == default_display->yres &&   
        var->xres == default_display->xres &&   
        var->bits_per_pixel == default_display->bpp)   
        display = default_display;   
    else  
        for (i = 0; i < mach_info->num_displays; i++)   
            if (type == mach_info->displays[i].type &&   
                var->yres == mach_info->displays[i].yres &&   
                var->xres == mach_info->displays[i].xres &&   
                var->bits_per_pixel == mach_info->displays[i].bpp) {   
                display = mach_info->displays + i;   
                break;   
             
  
    if (!display) {   
        dprintk("wrong resolution or depth %dx%d at %d bpp\n",   
            var->xres, var->yres, var->bits_per_pixel);   
        return -EINVAL;   
     
  
      
    var->xres_virtual = display->xres;   
    var->yres_virtual = display->yres;   
    var->height = display->height;   
    var->width = display->width;   
  
      
    var->pixclock = display->pixclock;   
    var->left_margin = display->left_margin;   
    var->right_margin = display->right_margin;   
    var->upper_margin = display->upper_margin;   
    var->lower_margin = display->lower_margin;   
    var->vsync_len = display->vsync_len;   
    var->hsync_len = display->hsync_len;   
  
      
    fbi->regs.lcdcon5 = display->lcdcon5;   
      
    fbi->regs.lcdcon1 = display->type;   
  
      
    var->transp.offset = 0;   
    var->transp.length = 0;    
 switch (var->bits_per_pixel) {   
    case 1:   
    case 2:   
    case 4:   
        var->red.offset  = 0;   
        var->red.length  = var->bits_per_pixel;   
        var->green   = var->red;   
        var->blue    = var->red;   
        break;   
    case 8:   
        if (display->type != S3C2410_LCDCON1_TFT) {   
              
            var->red.length      = 3;   
            var->red.offset      = 5;   
            var->green.length    = 3;   
            var->green.offset    = 2;   
            var->blue.length = 2;   
            var->blue.offset = 0;   
        } else {   
            var->red.offset      = 0;   
            var->red.length      = 8;   
            var->green       = var->red;   
            var->blue        = var->red;   
         
        break;   
    case 12:   
          
        var->red.length      = 4;   
        var->red.offset      = 8;   
        var->green.length    = 4;   
        var->green.offset    = 4;   
        var->blue.length = 4;   
        var->blue.offset = 0;   
        break;   
  
    default:   
    case 16:   
        if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {   
              
            var->red.offset      = 11;   
            var->green.offset    = 5;   
            var->blue.offset = 0;   
            var->red.length      = 5;   
            var->green.length    = 6;   
            var->blue.length = 5;   
        } else {   
              
            var->red.offset      = 11;   
            var->green.offset    = 6;   
            var->blue.offset = 1;   
            var->red.length      = 5;   
            var->green.length    = 5;   
            var->blue.length = 5;   
         
        break;   
    case 32:   
          
        var->red.length      = 8;   
        var->red.offset      = 16;   
        var->green.length    = 8;   
        var->green.offset    = 8;   
        var->blue.length = 8;   
        var->blue.offset = 0;   
        break;   
     
    return 0;   

注册帧缓冲设备:

int  
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;   
  
    if (fb_check_foreignness(fb_info))   
        return -ENOSYS;   
      
    num_registered_fb++;   
    for (i = 0 ; i < FB_MAX; i++)   
        if (!registered_fb[i])   
            break;   
      
    fb_info->node = i;   
    mutex_init(&fb_info->lock);   
      
    fb_info->dev = device_create(fb_class, fb_info->device,   
                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);   
    if (IS_ERR(fb_info->dev)) {   
          
        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);   
    。。。。。。。。。。。。。。   
    return 0;   
  
  
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,   
}; 

设置参数,根据可变参数设置固定参数:

static int s3c2410fb_set_par(struct fb_info *info)   
  
    struct fb_var_screeninfo *var = &info->var;   
      
    switch (var->bits_per_pixel) {   
    case 32:   
    case 16:   
    case 12://设置成真彩,分红,绿,蓝三基色   
        info->fix.visual = FB_VISUAL_TRUECOLOR;   
        break;   
    case 1://设置为黑白,FB_VISUAL_MONO01代表黑,FB_VISUAL_MONO10代表白   
        info->fix.visual = FB_VISUAL_MONO01;   
        break;   
    default://默认设置为伪彩色,采用索引颜色显示   
        info->fix.visual = FB_VISUAL_PSEUDOCOLOR;   
        break;   
     
      
    info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;   
  
      
    s3c2410fb_activate_var(info);   
    return 0;   

激活设置:

static void s3c2410fb_activate_var(struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    void __iomem *regs = fbi->io;   
      
    int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;   
    struct fb_var_screeninfo *var = &info->var;   
      
    int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;   
  
    dprintk("%s: var->xres  = %d\n", __func__, var->xres);   
    dprintk("%s: var->yres  = %d\n", __func__, var->yres);   
    dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);   
  
    if (type == S3C2410_LCDCON1_TFT) {   
          
        s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);   
        --clkdiv;   
        if (clkdiv < 0)   
            clkdiv = 0;   
    } else {   
        s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);   
        if (clkdiv < 2)   
            clkdiv = 2;   
     
      
    fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);   
  
      
  
    dprintk("new register set:\n");   
    dprintk("lcdcon[1] = 0xlx\n", fbi->regs.lcdcon1);   
    dprintk("lcdcon[2] = 0xlx\n", fbi->regs.lcdcon2);   
    dprintk("lcdcon[3] = 0xlx\n", fbi->regs.lcdcon3);   
    dprintk("lcdcon[4] = 0xlx\n", fbi->regs.lcdcon4);   
    dprintk("lcdcon[5] = 0xlx\n", fbi->regs.lcdcon5);   
      
    writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,   
        regs + S3C2410_LCDCON1);   
    writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);   
    writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);   
    writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);   
    writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);   
  
      
    s3c2410fb_set_lcdaddr(info);   
      
    fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,   
    writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);   

显示空白:blank_mode有5中模式,是一个枚举,定义在include/linux/fb.h中:

static int s3c2410fb_blank(int blank_mode, struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    void __iomem *tpal_reg = fbi->io;   
  
    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);   
  
    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;   
           
    if (blank_mode == FB_BLANK_POWERDOWN) {   //如果是空白模式,则关闭LCD   
        s3c2410fb_lcd_enable(fbi, 0);   
    } else {   
        s3c2410fb_lcd_enable(fbi, 1);   
     
    if (blank_mode == FB_BLANK_UNBLANK)   
          
        writel(0x0, tpal_reg);   
    else {   
          
        dprintk("setting TPAL to output 0x000000\n");   
        writel(S3C2410_TPAL_EN, tpal_reg);   
     
    return 0;   

设置颜色表:

static int s3c2410fb_setcolreg(unsigned regno,   
                   unsigned red, unsigned green, unsigned blue,   
                   unsigned transp, struct fb_info *info)   
  
    struct s3c2410fb_info *fbi = info->par;   
    void __iomem *regs = fbi->io;   
    unsigned int val;   
  
      
  
    switch (info->fix.visual) {   
          
    case FB_VISUAL_TRUECOLOR:   
          
  
        if (regno < 16) {   
            u32 *pal = info->pseudo_palette;   
  
            val  = chan_to_field(red,   &info->var.red);   
            val |= chan_to_field(green, &info->var.green);   
            val |= chan_to_field(blue,  &info->var.blue);   
  
            pal[regno] = val;   
         
        break;   
          
    case FB_VISUAL_PSEUDOCOLOR:   
        if (regno < 256) {   
              
  
            val  = (red   >>  0) & 0xf800;   
            val |= (green >>  5) & 0x07e0;   
            val |= (blue  >> 11) & 0x001f;   
  
            writel(val, regs + S3C2410_TFTPAL(regno));   
              
            schedule_palette_update(fbi, regno, val);   
         
        break;   
    default:   
        return 1;     
     
    return 0;   
  
static inline unsigned int chan_to_field(unsigned int chan,   
                     struct fb_bitfield *bf)   
  
    chan &= 0xffff;   
    chan >>= 16 - bf->length;   
    return chan << bf->offset;   

修改调色板:

static void schedule_palette_update(struct s3c2410fb_info *fbi,   
                    unsigned int regno, unsigned int val)   
  
    unsigned long flags;   
    unsigned long irqen;   
      
    void __iomem *irq_base = fbi->irq_base;   
  
      
    local_irq_save(flags);   
  
    fbi->palette_buffer[regno] = val;   
      
    if (!fbi->palette_ready) {   
        fbi->palette_ready = 1;   
          
        irqen = readl(irq_base + S3C24XX_LCDINTMSK);   
        irqen &= ~S3C2410_LCDINT_FRSYNC;   
        writel(irqen, irq_base + S3C24XX_LCDINTMSK);   
     
      
    local_irq_restore(flags);   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值