Linux驱动修炼之道-framebuffer(中)

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/29/6452689.aspx

下面详细分析一下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的平台设备:

/* LCD Controller */ 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驱动的时候需要根据我们屏的参数重新设置。

/* LCD driver info */ 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; /*这就获得了刚才保存的s3c2410fb_mach_info*/ 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; } /*分配一个fb_info结构体*/ fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); if (!fbinfo) return -ENOMEM; /*设置pdev->dev->driver_data保存fbinfo的地址*/ platform_set_drvdata(pdev, fbinfo); info = fbinfo->par; info->dev = &pdev->dev; info->drv_type = drv_type; 这4句构建的关系图如下: /*获得IO内存*/ 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; /*申请IO内存*/ 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; } /*映射IO内存*/ info->io = ioremap(res->start, size); if (info->io == NULL) { dev_err(&pdev->dev, "ioremap() of registers failed\n"); ret = -ENXIO; goto release_mem; } /*获得LCD中断挂起寄存器的基地址*/ info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); dprintk("devinit\n"); strcpy(fbinfo->fix.id, driver_name); /*暂时关闭LCD控制器*/ 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; } /*获得LCD时钟*/ 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; } /*使能LCD时钟*/ clk_enable(info->clk); dprintk("got and enabled clock\n"); /*初始化LCD控制器之前要延时一段时间*/ msleep(1); /*计算缓冲区需要的最大内存,就是缓冲区一共占多少字节,xres*yres*bpp/8 */ 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); /*检查fb_info中的可变参数*/ 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 */ 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; }

总结一下探测函数完成的任务:
1)申请fb_info结构体的内存空间,初始化fb_info结构中固定和可变的内存参数,即填充fb_info中的fb_var_screeninfo var和struct fb_fix_screeninfo fix成员。
2)申请帧缓冲设备的显示缓冲区空间
3)注册帧缓冲设备

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; /*这里开辟的堆空间用来存储struct fb_info结构体和struct s3c2410fb_info结构体*/ p = kzalloc(fb_info_size + size, GFP_KERNEL); if (!p) return NULL; info = (struct fb_info *) p; /*在这里将par成员赋值,以后用于存储struct s3c2410fb_info结构*/ 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; /*LCD中断挂起寄存器基地址*/ void __iomem *irq_base = fbi->irq_base; /*读取LCD中断挂起寄存器值*/ unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); /*如果framebuffer发出了中断请求*/ 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)); /* it seems the only way to know exactly * if the palette wrote ok, is to check * to see if the value verifies ok */ if (readw(regs + S3C2410_TFTPAL(i)) == ent) fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR; else fbi->palette_ready = 1; /* retry */ } }

申请帧缓冲设备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) { /* prevent initial garbage on screen */ dprintk("map_video_memory: clear %p:%08x\n", info->screen_base, map_size); /*初始化为0*/ memset(info->screen_base, 0x00, map_size); /*设置物理地址*/ info->fix.smem_start = map_dma; dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\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; /*获得LCD寄存器基地址,这个在probe中获得*/ void __iomem *regs = fbi->io; void __iomem *tpal; void __iomem *lpcsel; if (is_s3c2412(fbi)) { tpal = regs + S3C2412_TPAL; lpcsel = regs + S3C2412_TCONSEL; } else { /*获得LCD调色板寄存器基地址,注意对于lpcsel这是一个针对三星TFT屏的一个专用寄存器,如果用的不是三星的屏就不用管它*/ tpal = regs + S3C2410_TPAL; lpcsel = regs + S3C2410_LPCSEL; } /* Initialise LCD with values from haret */ /*关中断*/ local_irq_save(flags); /* modify the gpio(s) with interrupts set (bjd) */ /*把IO端口C和D设置成LCD模式*/ 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 = 0x%08lx\n", mach_info->lpcsel); writel(mach_info->lpcsel, lpcsel); dprintk("replacing TPAL %08x\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; /*LCD的类型,S3C2410_LCDCON1_TFT*/ int type = default_display->type; unsigned i; dprintk("check_var(var=%p, info=%p)\n", var, info); /*获取与LCD屏有关的参数,封装在s3c2410fb_display中*/ 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; /*配置LCD控制寄存器1中5-6位(配置成TFT类型),配置寄存器5*/ fbi->regs.lcdcon5 = display->lcdcon5; /* set display type */ fbi->regs.lcdcon1 = display->type; /*设置透明度*/ var->transp.offset = 0; var->transp.length = 0; /*根据色位模式设置(BPP)来设置可变参数中R,G,B的颜色位域,显示缓冲区与显示点对应如下图:*/

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) { /* 8 bpp 332 */ 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: /* 12 bpp 444 */ 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) { /* 16 bpp, 565 format */ 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 { /* 16 bpp, 5551 format */ 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: /* 24 bpp 888 and 8 dummy */ 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; /* *每一个注册的fb_info,都会分配一个下标"i",对应的就是registered_fb[i] *最多能注册的fb_info个数为FB_MAX,若新注册FB则num_registered_fb++ */ num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; /*找到空闲的i,赋值给fb_info->node,这个node相当于次设备号了,以后通过这个i找到fb_info*/ 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)) { /* 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的属性文件*/ 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; } /*设置fb_info中固定参数一行的字节数*/ 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; /*获得CLKVAL*/ 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) { /*就是根据可变参数结构设置lcdcon1~5*/ 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); /* write new registers */ dprintk("new register set:\n"); dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1); dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2); dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3); dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4); dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5); /*设置寄存器前先把LCD使能关闭,然后将刚才设置的值写入真正的寄存器*/ 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); /*设置LCDSADDR1~3*/ s3c2410fb_set_lcdaddr(info); /*使能LCD*/ 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; /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */ switch (info->fix.visual) { /*真彩色*/ case FB_VISUAL_TRUECOLOR: /* true-colour, use pseudo-palette */ 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) { /* currently assume RGB 5-6-5 mode */ 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; /* unknown type */ } 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; /*LCD中断挂起寄存器基地址*/ void __iomem *irq_base = fbi->irq_base; /*屏蔽中断,将中断状态保存在flags中*/ 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); }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值