美国克利夫兰危险吗:s3c2410_lcd & frame buffer 驱动分析
来源:百度文库 编辑:偶看新闻 时间:2024/04/28 12:38:36
int __init s3c2410fb_probe(struct device *dev)
{
struct s3c2410fb_info *info;
struct fb_info *fbinfo;
struct platform_device *pdev = to_platform_device(dev);
struct s3c2410fb_hw *mregs;
int ret;
int irq;
int i;
mach_info = dev->platform_data;//获取lcd相关寄存器配置信息
if (mach_info == NULL) {
dev_err(dev,"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
mregs = &mach_info->regs;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq for device\n");
return -ENOENT;
}
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), dev);//分配struct fb_info和struct s3c2410fb_info结构
if (!fbinfo) {
return -ENOMEM;
}
info = fbinfo->par;//调用framebuffer_alloc分配内存的时候,其实顺带分配了struct s3c2410fb_info结构,紧跟struct fb_info结构的后面,fbinfo->par指针处。
info->fb = fbinfo;
dev_set_drvdata(dev, fbinfo);
s3c2410fb_init_registers(info);//lcd相关寄存器配置
dprintk("devinit\n");
strcpy(fbinfo->fix.id, driver_name);
/* 配置信息存储起来,以后用到 */
memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));
info->mach_info = dev->platform_data;
/* 以下配置,详细请参考网上资料 */
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.height = mach_info->height;
fbinfo->var.width = mach_info->width;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;//重要的fop结构(fb设备本质上是字符设备)
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
fbinfo->var.xres = mach_info->xres.defval;
fbinfo->var.xres_virtual = mach_info->xres.defval;
fbinfo->var.yres = mach_info->yres.defval;
fbinfo->var.yres_virtual = mach_info->yres.defval;
fbinfo->var.bits_per_pixel = mach_info->bpp.defval;
fbinfo->var.upper_margin = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) +1;
fbinfo->var.lower_margin = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) +1;
fbinfo->var.vsync_len = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;
fbinfo->var.left_margin = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
fbinfo->var.right_margin = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
fbinfo->var.hsync_len = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;
/* 配色板设置(采用5、6、5模式) */
fbinfo->var.red.offset = 11;
fbinfo->var.green.offset = 5;
fbinfo->var.blue.offset = 0;
fbinfo->var.transp.offset = 0;
fbinfo->var.red.length = 5;
fbinfo->var.green.length = 6;
fbinfo->var.blue.length = 5;
fbinfo->var.transp.length = 0;
/* 计算需要申请的framer buffer大小(以字节为单位) */
fbinfo->fix.smem_len = mach_info->xres.max *
mach_info->yres.max *
mach_info->bpp.max / 8;
for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {
ret = -EBUSY;
goto dealloc_fb;
}
dprintk("got LCD region\n");
ret = request_irq(irq, s3c2410fb_irq, SA_INTERRUPT, pdev->name, info);
if (ret) {
dev_err(dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_mem;
}
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_use(info->clk);
clk_enable(info->clk);
dprintk("got and enabled clock\n");
msleep(1);
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(info);
if (ret) {
printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
dprintk("got video memory\n");
ret = s3c2410fb_init_registers(info);//怎么再次初始化lcd相关寄存器了?
ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);//注册frame buffer前的一个例行检查
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(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(info);
release_clock:
clk_disable(info->clk);
clk_unuse(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq,info);
release_mem:
release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}
/*
* s3c2410fb_map_video_memory():
* Allocates the DRAM memory for the frame buffer. This buffer is
* remapped into a non-cached, non-buffered, memory region to
* allow palette and pixel writes to occur without flushing the
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)
{
dprintk("map_video_memory(fbi=%p)\n", fbi);
fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);//页对齐
fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
&fbi->map_dma, GFP_KERNEL);//目前我只知道用于分配可供dma使用的内存,其中返回地址fbi->map_cpu为虚拟地址,fbi->map_dma为物理地址。
fbi->map_size = fbi->fb->fix.smem_len;//恢复真实的大小
if (fbi->map_cpu) {
/* prevent initial garbage on screen */
dprintk("map_video_memory: clear %p:%08x\n",
fbi->map_cpu, fbi->map_size);
memset(fbi->map_cpu, 0xf0, fbi->map_size);
fbi->screen_dma = fbi->map_dma;//物理地址
fbi->fb->screen_base = fbi->map_cpu;//虚拟地址
fbi->fb->fix.smem_start = fbi->screen_dma;//物理地址
dprintk("map_video_memory: dma=%08x cpu=%p size=%08x\n",
fbi->map_dma, fbi->map_cpu, fbi->fb->fix.smem_len);
}
return fbi->map_cpu ? 0 : -ENOMEM;
}
static struct s3c2410fb_mach_info sbc2410_lcdcfg __initdata = {
.fixed_syncs= 0,
.regs={
.lcdcon1= S3C2410_LCDCON1_TFT16BPP | \
S3C2410_LCDCON1_TFT | \
S3C2410_LCDCON1_CLKVAL(6),
.lcdcon2= S3C2410_LCDCON2_VBPD(2) | \
S3C2410_LCDCON2_LINEVAL(319) | \
S3C2410_LCDCON2_VFPD(0) | \
S3C2410_LCDCON2_VSPW(4),
.lcdcon3= S3C2410_LCDCON3_HBPD(47) | \
S3C2410_LCDCON3_HOZVAL(239) | \
S3C2410_LCDCON3_HFPD(15),
.lcdcon4= S3C2410_LCDCON4_MVAL(1) | \
S3C2410_LCDCON4_HSPW(31),
.lcdcon5= S3C2410_LCDCON5_FRM565 | \
S3C2410_LCDCON5_INVVLINE | \
S3C2410_LCDCON5_HWSWP,
},
.lpcsel= 0x0,
.gpccon= 0xaaaaaaaa,
.gpccon_mask= 0xffffffff,
.gpcup= 0xffffffff,
.gpcup_mask= 0xffffffff,
.gpdcon= 0xaaaaaaaa,
.gpdcon_mask= 0x0,
.gpdup= 0xffffffff,
.gpdup_mask= 0xffffffff,
.width= 240,
.height= 320,
.xres= {240,240,240},
.yres= {320,320,320},
.bpp= {16,16,16},
};
s3c2410fb.c文件中的其它部分单独来分析的话比较难理解,现结合fbmem.c来分析:
fbmem.c文件中:
#ifdef MODULE
module_init(fbmem_init);
static void __exit
fbmem_exit(void)
{
class_destroy(fb_class);
unregister_chrdev(FB_MAJOR, "fb");
}
module_exit(fbmem_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Framebuffer base");
#else
subsys_initcall(fbmem_init);
#endif
驱动入口为fbmem_init:
/**
* fbmem_init - init frame buffer subsystem
*
* Initialize the frame buffer subsystem.
*
* NOTE: This function is _only_ to be called by drivers/char/mem.c.
*
*/
static int __init
fbmem_init(void)
{
create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);
devfs_mk_dir("fb");
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;
}
可见,frame buffer驱动本质上是字符驱动,同样使用register_chrdev函数来注册自己。
下面再来看看frame buffer所支持的fb_fops方法:
static 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
};
可以看到,包含了常用的open、release、read、write、ioctl方法,细心的你可能会发现多了mmap方法,这是提供给用户映射frame buffer的接口函数(可是在底层函数s3c2410fb.c中确没有这个接口的实现代码,此乃后话)。
其中read、write接口很普通的字符驱动没什么不同,只是读写的对象变为frame buffer,纯粹的内存操作,在此我就不详细介绍了。
下面我们来研究一下fb_ioctl接口函数:
static int
fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
int i;
if (!fb)
return -ENODEV;
switch (cmd) {
case FBIOGET_VSCREENINFO://获取屏的可变参数
return copy_to_user(argp, &info->var,
sizeof(var)) ? -EFAULT : 0;
case FBIOPUT_VSCREENINFO://设置屏的可变参数
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
i = fb_set_var(info, &var);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
if (i) return i;
if (copy_to_user(argp, &var, sizeof(var)))
return -EFAULT;
return 0;
case FBIOGET_FSCREENINFO://获取屏的固定参数
return copy_to_user(argp, &info->fix,
sizeof(fix)) ? -EFAULT : 0;
case FBIOPUTCMAP://设置调色板
if (copy_from_user(&cmap, argp, sizeof(cmap)))
return -EFAULT;
return (fb_set_user_cmap(&cmap, info));
case FBIOGETCMAP://获取调色板信息
if (copy_from_user(&cmap, argp, sizeof(cmap)))
return -EFAULT;
return fb_cmap_to_user(&info->cmap, &cmap);
case FBIOPAN_DISPLAY:
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
acquire_console_sem();
i = fb_pan_display(info, &var);
release_console_sem();
if (i)
return i;
if (copy_to_user(argp, &var, sizeof(var)))
return -EFAULT;
return 0;
case FBIO_CURSOR:
return -EINVAL;
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
con2fb.framebuffer = -1;
event.info = info;
event.data = &con2fb;
notifier_call_chain(&fb_notifier_list,
FB_EVENT_GET_CONSOLE_MAP, &event);
return copy_to_user(argp, &con2fb,
sizeof(con2fb)) ? -EFAULT : 0;
case FBIOPUT_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return - EFAULT;
if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
return -EINVAL;
#ifdef CONFIG_KMOD
if (!registered_fb[con2fb.framebuffer])
try_to_load(con2fb.framebuffer);
#endif /* CONFIG_KMOD */
if (!registered_fb[con2fb.framebuffer])
return -EINVAL;
event.info = info;
event.data = &con2fb;
return notifier_call_chain(&fb_notifier_list,
FB_EVENT_SET_CONSOLE_MAP,
&event);
case FBIOBLANK:
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
i = fb_blank(info, arg);//开关显示用
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
return i;
default:
if (fb->fb_ioctl == NULL)
return -EINVAL;
return fb->fb_ioctl(inode, file, cmd, arg, info);
}
}
注:
s3c2410fb.c中var->activate设置为FB_ACTIVATE_NOW,所以这里只考虑条件2。
int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
{
int err, flags = info->flags;
1:
if (var->activate & FB_ACTIVATE_INV_MODE) {
struct fb_videomode mode1, mode2;
int ret = 0;
fb_var_to_videomode(&mode1, var);
fb_var_to_videomode(&mode2, &info->var);
/* make sure we don't delete the videomode of current var */
ret = fb_mode_is_equal(&mode1, &mode2);
if (!ret) {
struct fb_event event;
event.info = info;
event.data = &mode1;
ret = notifier_call_chain(&fb_notifier_list,
FB_EVENT_MODE_DELETE, &event);
}
if (!ret)
fb_delete_videomode(&mode1, &info->modelist);
return ret;
}
2:
if ((var->activate & FB_ACTIVATE_FORCE) ||
memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
if (!info->fbops->fb_check_var) {
*var = info->var;
return 0;
}
if ((err = info->fbops->fb_check_var(var, info)))//先检查一下设置是否符合要求
return err;
if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {//条件符合
struct fb_videomode mode;
int err = 0;
info->var = *var;
if (info->fbops->fb_set_par)
info->fbops->fb_set_par(info);//设置可变参数
fb_pan_display(info, &info->var);//s3c2410不支持硬件虚拟显示,在s3c2410fb.c上没有实现该接口。
fb_set_cmap(&info->cmap, info);//调色板设置
fb_var_to_videomode(&mode, &info->var);
if (info->modelist.prev && info->modelist.next &&
!list_empty(&info->modelist))
err = fb_add_videomode(&mode, &info->modelist);
if (!err && (flags & FBINFO_MISC_USEREVENT)) {
struct fb_event event;
int evnt = (var->activate & FB_ACTIVATE_ALL) ?
FB_EVENT_MODE_CHANGE_ALL :
FB_EVENT_MODE_CHANGE;
info->flags &= ~FBINFO_MISC_USEREVENT;
event.info = info;
notifier_call_chain(&fb_notifier_list, evnt,
&event);
}
}
}
return 0;
}
/*
* s3c2410fb_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*
*/
static int s3c2410fb_set_par(struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
struct fb_var_screeninfo *var = &info->var;
if (var->bits_per_pixel == 16)
fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR;
else
fbi->fb->fix.visual = FB_VISUAL_PSEUDOCOLOR;
fbi->fb->fix.line_length = (var->width*var->bits_per_pixel)/8;
/* activate this new configuration */
s3c2410fb_activate_var(fbi, var);
return 0;
}
/* s3c2410fb_activate_var
*
* activate (set) the controller from the given framebuffer
* information
*/
static void s3c2410fb_activate_var(struct s3c2410fb_info *fbi,
struct fb_var_screeninfo *var)
{
fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_MODEMASK;
dprintk("%s: var->xres = %d\n", __FUNCTION__, var->xres);
dprintk("%s: var->yres = %d\n", __FUNCTION__, var->yres);
dprintk("%s: var->bpp = %d\n", __FUNCTION__, var->bits_per_pixel);
switch (var->bits_per_pixel) {
case 1:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
break;
case 2:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
break;
case 4:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
break;
case 8:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
break;
case 16:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
break;
}
/* check to see if we need to update sync/borders */
if (!fbi->mach_info->fixed_syncs) {
dprintk("setting vert: up=%d, low=%d, sync=%d\n",
var->upper_margin, var->lower_margin,
var->vsync_len);
dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
var->left_margin, var->right_margin,
var->hsync_len);
fbi->regs.lcdcon2 =
S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
fbi->regs.lcdcon3 =
S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
S3C2410_LCDCON3_HFPD(var->left_margin - 1);
fbi->regs.lcdcon4 &= ~S3C2410_LCDCON4_HSPW(0xff);
fbi->regs.lcdcon4 |= S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
}
/* update X/Y info */
fbi->regs.lcdcon2 &= ~S3C2410_LCDCON2_LINEVAL(0x3ff);
fbi->regs.lcdcon2 |= S3C2410_LCDCON2_LINEVAL(var->yres - 1);
fbi->regs.lcdcon3 &= ~S3C2410_LCDCON3_HOZVAL(0x7ff);
fbi->regs.lcdcon3 |= S3C2410_LCDCON3_HOZVAL(var->xres - 1);
if (var->pixclock > 0) {
int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock);
clkdiv = (clkdiv / 2) -1;
if (clkdiv < 0)
clkdiv = 0;
fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_CLKVAL(0x3ff);
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);
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
/* set lcd address pointers */
s3c2410fb_set_lcdaddr(fbi);
writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
}
/**
* fb_set_cmap - set the colormap
* @cmap: frame buffer colormap structure
* @info: frame buffer info structure
*
* Sets the colormap @cmap for a screen of device @info.
*
* Returns negative errno on error, or zero on success.
*
*/
int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
{
int i, start, rc = 0;
u16 *red, *green, *blue, *transp;
u_int hred, hgreen, hblue, htransp = 0xffff;
red = cmap->red;
green = cmap->green;
blue = cmap->blue;
transp = cmap->transp;
start = cmap->start;
if (start < 0 || (!info->fbops->fb_setcolreg &&
!info->fbops->fb_setcmap))
return -EINVAL;
if (info->fbops->fb_setcmap) {
rc = info->fbops->fb_setcmap(cmap, info);
} else {
for (i = 0; i < cmap->len; i++) {
hred = *red++;
hgreen = *green++;
hblue = *blue++;
if (transp)
htransp = *transp++;
if (info->fbops->fb_setcolreg(start++,
hred, hgreen, hblue,
htransp, info))
break;
}
}
if (rc == 0)
fb_copy_cmap(cmap, &info->cmap);
return rc;
}
static int s3c2410fb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
unsigned int val;
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */
switch (fbi->fb->fix.visual) {
case FB_VISUAL_TRUECOLOR:
/* true-colour, use pseuo-palette */
if (regno < 16) {
u32 *pal = fbi->fb->pseudo_palette;
val = chan_to_field(red, &fbi->fb->var.red);
val |= chan_to_field(green, &fbi->fb->var.green);
val |= chan_to_field(blue, &fbi->fb->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, S3C2410_TFTPAL(regno));
schedule_palette_update(fbi, regno, val);
}
break;
default:
return 1; /* unknown type */
}
return 0;
}
int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
{
int rc, size = cmap->len * sizeof(u16);
struct fb_cmap umap;
if (cmap->start < 0 || (!info->fbops->fb_setcolreg &&
!info->fbops->fb_setcmap))
return -EINVAL;
memset(&umap, 0, sizeof(struct fb_cmap));
rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL);
if (rc)
return rc;
if (copy_from_user(umap.red, cmap->red, size) ||
copy_from_user(umap.green, cmap->green, size) ||
copy_from_user(umap.blue, cmap->blue, size) ||
(cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
fb_dealloc_cmap(&umap);
return -EFAULT;
}
umap.start = cmap->start;
rc = fb_set_cmap(&umap, info);
fb_dealloc_cmap(&umap);
return rc;
}
int fb_cmap_to_user(struct fb_cmap *from, struct fb_cmap_user *to)
{
int tooff = 0, fromoff = 0;
int size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
size = to->len - tooff;
if (size > (int) (from->len - fromoff))
size = from->len - fromoff;
if (size <= 0)
return -EINVAL;
size *= sizeof(u16);
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
if (copy_to_user(to->green+tooff, from->green+fromoff, size))
return -EFAULT;
if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
return -EFAULT;
if (from->transp && to->transp)
if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
return -EFAULT;
return 0;
}
int
fb_blank(struct fb_info *info, int blank)
{
int ret = -EINVAL;
if (blank > FB_BLANK_POWERDOWN)
blank = FB_BLANK_POWERDOWN;
if (info->fbops->fb_blank)
ret = info->fbops->fb_blank(blank, info);
if (!ret) {
struct fb_event event;
event.info = info;
event.data = ␣
notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event);
}
return ret;
}
/**
* s3c2410fb_blank
* @blank_mode: the blank mode we want.
* @info: frame buffer structure that represents a single frame buffer
*
* Blank the screen if blank_mode != 0, else unblank. Return 0 if
* blanking succeeded, != 0 if un-/blanking failed due to e.g. a
* video mode which doesn't support it. Implements VESA suspend
* and powerdown modes on hardware that supports disabling hsync/vsync:
* blank_mode == 2: suspend vsync
* blank_mode == 3: suspend hsync
* blank_mode == 4: powerdown
*
* Returns negative errno on error, or zero on success.
*
*/
static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
{
dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
if (mach_info == NULL)
return -EINVAL;
if (blank_mode == FB_BLANK_UNBLANK)
writel(0x0, S3C2410_TPAL);
else {
dprintk("setting TPAL to output 0x000000\n");
writel(S3C2410_TPAL_EN, S3C2410_TPAL);
}
return 0;
}
static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
int fbidx = iminor(file->f_dentry->d_inode);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
unsigned long off;
#if !defined(__sparc__) || defined(__sparc_v9__)
unsigned long start;
u32 len;
#endif
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
if (!fb)
return -ENODEV;
if (fb->fb_mmap) {
int res;
lock_kernel();
res = fb->fb_mmap(info, file, vma);
unlock_kernel();
return res;
}
/* 所有的驱动应该拥有自己的mmap实现,可惜的是s3c2410fb.c中没有实现mmap接口,以下内容我也不想去研究了。 */
#if defined(__sparc__) && !defined(__sparc_v9__)
/* Should never get here, all fb drivers should have their own
mmap routines */
return -EINVAL;
#else
/* !sparc32... */
lock_kernel();
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
if (off >= len) {
/* memory mapped io */
off -= len;
if (info->var.accel_flags) {
unlock_kernel();
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
}
unlock_kernel();
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
#if defined(__sparc_v9__)
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
#else
#if defined(__mc68000__)
#if defined(CONFIG_SUN3)
pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
#elif defined(CONFIG_MMU)
if (CPU_IS_020_OR_030)
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
if (CPU_IS_040_OR_060) {
pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
/* Use no-cache mode, serialized */
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
}
#endif
#elif defined(__powerpc__)
vma->vm_page_prot = phys_mem_access_prot(file, off,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
#elif defined(__alpha__)
/* Caching is off in the I/O space quadrant by design. */
#elif defined(__i386__) || defined(__x86_64__)
if (boot_cpu_data.x86 > 3)
pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
#elif defined(__mips__)
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#elif defined(__hppa__)
pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
#elif defined(__arm__) || defined(__sh__) || defined(__m32r__)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
#elif defined(__ia64__)
if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start))
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
else
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#else
#warning What do we have to do here??
#endif
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
#endif /* !__sparc_v9__ */
return 0;
#endif /* !sparc32 */
}
以下以内核自带的logo程序,介绍一下frame buffer的具体用法:
static struct logo_data {
int depth;
int needs_directpalette;
int needs_truepalette;
int needs_cmapreset;
const struct linux_logo *logo;
} fb_logo;
注:
logo图像数据结构.
/* 填充好logo显示用的数据 */
int fb_prepare_logo(struct fb_info *info)
{
int depth = fb_get_color_depth(&info->var, &info->fix);//16
memset(&fb_logo, 0, sizeof(struct logo_data));
if (info->flags & FBINFO_MISC_TILEBLITTING)
return 0;
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
depth = info->var.blue.length;
if (info->var.red.length < depth)
depth = info->var.red.length;
if (info->var.green.length < depth)
depth = info->var.green.length;
}
if (depth >= 8) {
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
fb_logo.needs_truepalette = 1;//使用tft屏的话就选择这个吧.
break;
case FB_VISUAL_DIRECTCOLOR:
fb_logo.needs_directpalette = 1;
fb_logo.needs_cmapreset = 1;
break;
case FB_VISUAL_PSEUDOCOLOR:
fb_logo.needs_cmapreset = 1;
break;
}
}
/* Return if no suitable logo was found */
fb_logo.logo = fb_find_logo(depth);//根据内核的配置,选择合适的logo.
if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) {
fb_logo.logo = NULL;
return 0;
}
/* What depth we asked for might be different from what we get */
if (fb_logo.logo->type == LINUX_LOGO_CLUT224)//在我的内核中选的就是这个.
fb_logo.depth = 8;
else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
fb_logo.depth = 4;
else
fb_logo.depth = 1;
return fb_logo.logo->height;
}
好了,准备好logo显示用的数据后,在适当的时候即可调用以下函数来显示:
int fb_show_logo(struct fb_info *info)
{
u32 *palette = NULL, *saved_pseudo_palette = NULL;
unsigned char *logo_new = NULL;
struct fb_image image;
int x;
/* Return if the frame buffer is not mapped or suspended */
if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING)
return 0;
image.depth = 8;
image.data = fb_logo.logo->data;
if (fb_logo.needs_cmapreset)
fb_set_logocmap(info, fb_logo.logo);
if (fb_logo.needs_truepalette ||
fb_logo.needs_directpalette) {
palette = kmalloc(256 * 4, GFP_KERNEL);//分配调色板所需的空间.
if (palette == NULL)
return 0;
if (fb_logo.needs_truepalette)
fb_set_logo_truepalette(info, fb_logo.logo, palette);//根据我们所找到的logo图像填充调色板
else
fb_set_logo_directpalette(info, fb_logo.logo, palette);
saved_pseudo_palette = info->pseudo_palette;
info->pseudo_palette = palette;
}
if (fb_logo.depth <= 4) {
logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height,
GFP_KERNEL);
if (logo_new == NULL) {
kfree(palette);
if (saved_pseudo_palette)
info->pseudo_palette = saved_pseudo_palette;
return 0;
}
image.data = logo_new;
fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth);
}
image.width = fb_logo.logo->width;
image.height = fb_logo.logo->height;
image.dy = 0;
/* 以下循环用于查询调色板,把logo信息填写到相应的frame buffer中 */
for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) &&
x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) {
image.dx = x;
info->fbops->fb_imageblit(info, &image);
}
kfree(palette);
if (saved_pseudo_palette != NULL)
info->pseudo_palette = saved_pseudo_palette;
kfree(logo_new);
return fb_logo.logo->height;
}
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,
.fb_cursor = soft_cursor,
};
void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
{
u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
u32 width = image->width, height = image->height;
u32 dx = image->dx, dy = image->dy;
int x2, y2, vxres, vyres;
u8 __iomem *dst1;
if (p->state != FBINFO_STATE_RUNNING)
return;
vxres = p->var.xres_virtual;
vyres = p->var.yres_virtual;
/*
* We could use hardware clipping but on many cards you get around
* hardware clipping by writing to framebuffer directly like we are
* doing here.
*/
if (image->dx > vxres || image->dy > vyres)
return;
x2 = image->dx + image->width;
y2 = image->dy + image->height;
dx = image->dx > 0 ? image->dx : 0;
dy = image->dy > 0 ? image->dy : 0;
x2 = x2 < vxres ? x2 : vxres;
y2 = y2 < vyres ? y2 : vyres;
width = x2 - dx;
height = y2 - dy;
bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);//算出要绘图的起始点位置(以bit为单位)
start_index = bitstart & (32 - 1);//非字对齐地址
pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
bitstart /= 8;
bitstart &= ~(bpl - 1);
dst1 = p->screen_base + bitstart;//图像在frame buffer上的地址(目标地址)
if (p->fbops->fb_sync)
p->fbops->fb_sync(p);
if (image->depth == 1) {
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
} else {
fgcolor = image->fg_color;
bgcolor = image->bg_color;
}
if (32 % bpp == 0 && !start_index && !pitch_index &&
((width & (32/bpp-1)) == 0) &&
bpp >= 8 && bpp <= 32)
fast_imageblit(image, p, dst1, fgcolor, bgcolor);
else
slow_imageblit(image, p, dst1, fgcolor, bgcolor,
start_index, pitch_index);
} else
color_imageblit(image, p, dst1, start_index, pitch_index);
}
static inline void color_imageblit(const struct fb_image *image,
struct fb_info *p, u8 __iomem *dst1,
u32 start_index,
u32 pitch_index)
{
/* Draw the penguin */
u32 __iomem *dst, *dst2;
u32 color = 0, val, shift;
int i, n, bpp = p->var.bits_per_pixel;
u32 null_bits = 32 - bpp;
u32 *palette = (u32 *) p->pseudo_palette;
const u8 *src = image->data;//image->data里的数据,实际上记录着的是调色板上对应颜色的序号.
dst2 = (u32 __iomem *) dst1;
for (i = image->height; i--; ) {//行
n = image->width;
dst = (u32 __iomem *) dst1;
shift = 0;
val = 0;
if (start_index) {
u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
val = FB_READL(dst) & start_mask;
shift = start_index;
}
while (n--) {//列
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR )
color = palette[*src];//调色板在这个时候终于派上用场了
else
color = *src;
color <<= LEFT_POS(bpp);.
val |= SHIFT_HIGH(color, shift);
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
val = (shift == null_bits) ? 0 :
SHIFT_LOW(color, 32 - shift);
}
shift += bpp;
shift &= (32 - 1);
src++;
}
if (shift) {
u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
}
dst1 += p->fix.line_length;
if (pitch_index) {
dst2 += p->fix.line_length;
dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
start_index += pitch_index;
start_index &= 32 - 1;
}
}
}
后记:
自问这个文档写得不是很好。由于到目前为止接手的项目都不带tft的屏,暂时没什么动力深入去研究,等以后有机会再继续完善这个文档好了^_^
{
struct s3c2410fb_info *info;
struct fb_info *fbinfo;
struct platform_device *pdev = to_platform_device(dev);
struct s3c2410fb_hw *mregs;
int ret;
int irq;
int i;
mach_info = dev->platform_data;//获取lcd相关寄存器配置信息
if (mach_info == NULL) {
dev_err(dev,"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
mregs = &mach_info->regs;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq for device\n");
return -ENOENT;
}
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), dev);//分配struct fb_info和struct s3c2410fb_info结构
if (!fbinfo) {
return -ENOMEM;
}
info = fbinfo->par;//调用framebuffer_alloc分配内存的时候,其实顺带分配了struct s3c2410fb_info结构,紧跟struct fb_info结构的后面,fbinfo->par指针处。
info->fb = fbinfo;
dev_set_drvdata(dev, fbinfo);
s3c2410fb_init_registers(info);//lcd相关寄存器配置
dprintk("devinit\n");
strcpy(fbinfo->fix.id, driver_name);
/* 配置信息存储起来,以后用到 */
memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));
info->mach_info = dev->platform_data;
/* 以下配置,详细请参考网上资料 */
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.height = mach_info->height;
fbinfo->var.width = mach_info->width;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;//重要的fop结构(fb设备本质上是字符设备)
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
fbinfo->var.xres = mach_info->xres.defval;
fbinfo->var.xres_virtual = mach_info->xres.defval;
fbinfo->var.yres = mach_info->yres.defval;
fbinfo->var.yres_virtual = mach_info->yres.defval;
fbinfo->var.bits_per_pixel = mach_info->bpp.defval;
fbinfo->var.upper_margin = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) +1;
fbinfo->var.lower_margin = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) +1;
fbinfo->var.vsync_len = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;
fbinfo->var.left_margin = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
fbinfo->var.right_margin = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
fbinfo->var.hsync_len = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;
/* 配色板设置(采用5、6、5模式) */
fbinfo->var.red.offset = 11;
fbinfo->var.green.offset = 5;
fbinfo->var.blue.offset = 0;
fbinfo->var.transp.offset = 0;
fbinfo->var.red.length = 5;
fbinfo->var.green.length = 6;
fbinfo->var.blue.length = 5;
fbinfo->var.transp.length = 0;
/* 计算需要申请的framer buffer大小(以字节为单位) */
fbinfo->fix.smem_len = mach_info->xres.max *
mach_info->yres.max *
mach_info->bpp.max / 8;
for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {
ret = -EBUSY;
goto dealloc_fb;
}
dprintk("got LCD region\n");
ret = request_irq(irq, s3c2410fb_irq, SA_INTERRUPT, pdev->name, info);
if (ret) {
dev_err(dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_mem;
}
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_use(info->clk);
clk_enable(info->clk);
dprintk("got and enabled clock\n");
msleep(1);
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(info);
if (ret) {
printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
dprintk("got video memory\n");
ret = s3c2410fb_init_registers(info);//怎么再次初始化lcd相关寄存器了?
ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);//注册frame buffer前的一个例行检查
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(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(info);
release_clock:
clk_disable(info->clk);
clk_unuse(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq,info);
release_mem:
release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}
/*
* s3c2410fb_map_video_memory():
* Allocates the DRAM memory for the frame buffer. This buffer is
* remapped into a non-cached, non-buffered, memory region to
* allow palette and pixel writes to occur without flushing the
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)
{
dprintk("map_video_memory(fbi=%p)\n", fbi);
fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);//页对齐
fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
&fbi->map_dma, GFP_KERNEL);//目前我只知道用于分配可供dma使用的内存,其中返回地址fbi->map_cpu为虚拟地址,fbi->map_dma为物理地址。
fbi->map_size = fbi->fb->fix.smem_len;//恢复真实的大小
if (fbi->map_cpu) {
/* prevent initial garbage on screen */
dprintk("map_video_memory: clear %p:%08x\n",
fbi->map_cpu, fbi->map_size);
memset(fbi->map_cpu, 0xf0, fbi->map_size);
fbi->screen_dma = fbi->map_dma;//物理地址
fbi->fb->screen_base = fbi->map_cpu;//虚拟地址
fbi->fb->fix.smem_start = fbi->screen_dma;//物理地址
dprintk("map_video_memory: dma=%08x cpu=%p size=%08x\n",
fbi->map_dma, fbi->map_cpu, fbi->fb->fix.smem_len);
}
return fbi->map_cpu ? 0 : -ENOMEM;
}
static struct s3c2410fb_mach_info sbc2410_lcdcfg __initdata = {
.fixed_syncs= 0,
.regs={
.lcdcon1= S3C2410_LCDCON1_TFT16BPP | \
S3C2410_LCDCON1_TFT | \
S3C2410_LCDCON1_CLKVAL(6),
.lcdcon2= S3C2410_LCDCON2_VBPD(2) | \
S3C2410_LCDCON2_LINEVAL(319) | \
S3C2410_LCDCON2_VFPD(0) | \
S3C2410_LCDCON2_VSPW(4),
.lcdcon3= S3C2410_LCDCON3_HBPD(47) | \
S3C2410_LCDCON3_HOZVAL(239) | \
S3C2410_LCDCON3_HFPD(15),
.lcdcon4= S3C2410_LCDCON4_MVAL(1) | \
S3C2410_LCDCON4_HSPW(31),
.lcdcon5= S3C2410_LCDCON5_FRM565 | \
S3C2410_LCDCON5_INVVLINE | \
S3C2410_LCDCON5_HWSWP,
},
.lpcsel= 0x0,
.gpccon= 0xaaaaaaaa,
.gpccon_mask= 0xffffffff,
.gpcup= 0xffffffff,
.gpcup_mask= 0xffffffff,
.gpdcon= 0xaaaaaaaa,
.gpdcon_mask= 0x0,
.gpdup= 0xffffffff,
.gpdup_mask= 0xffffffff,
.width= 240,
.height= 320,
.xres= {240,240,240},
.yres= {320,320,320},
.bpp= {16,16,16},
};
s3c2410fb.c文件中的其它部分单独来分析的话比较难理解,现结合fbmem.c来分析:
fbmem.c文件中:
#ifdef MODULE
module_init(fbmem_init);
static void __exit
fbmem_exit(void)
{
class_destroy(fb_class);
unregister_chrdev(FB_MAJOR, "fb");
}
module_exit(fbmem_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Framebuffer base");
#else
subsys_initcall(fbmem_init);
#endif
驱动入口为fbmem_init:
/**
* fbmem_init - init frame buffer subsystem
*
* Initialize the frame buffer subsystem.
*
* NOTE: This function is _only_ to be called by drivers/char/mem.c.
*
*/
static int __init
fbmem_init(void)
{
create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);
devfs_mk_dir("fb");
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;
}
可见,frame buffer驱动本质上是字符驱动,同样使用register_chrdev函数来注册自己。
下面再来看看frame buffer所支持的fb_fops方法:
static 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
};
可以看到,包含了常用的open、release、read、write、ioctl方法,细心的你可能会发现多了mmap方法,这是提供给用户映射frame buffer的接口函数(可是在底层函数s3c2410fb.c中确没有这个接口的实现代码,此乃后话)。
其中read、write接口很普通的字符驱动没什么不同,只是读写的对象变为frame buffer,纯粹的内存操作,在此我就不详细介绍了。
下面我们来研究一下fb_ioctl接口函数:
static int
fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
int i;
if (!fb)
return -ENODEV;
switch (cmd) {
case FBIOGET_VSCREENINFO://获取屏的可变参数
return copy_to_user(argp, &info->var,
sizeof(var)) ? -EFAULT : 0;
case FBIOPUT_VSCREENINFO://设置屏的可变参数
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
i = fb_set_var(info, &var);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
if (i) return i;
if (copy_to_user(argp, &var, sizeof(var)))
return -EFAULT;
return 0;
case FBIOGET_FSCREENINFO://获取屏的固定参数
return copy_to_user(argp, &info->fix,
sizeof(fix)) ? -EFAULT : 0;
case FBIOPUTCMAP://设置调色板
if (copy_from_user(&cmap, argp, sizeof(cmap)))
return -EFAULT;
return (fb_set_user_cmap(&cmap, info));
case FBIOGETCMAP://获取调色板信息
if (copy_from_user(&cmap, argp, sizeof(cmap)))
return -EFAULT;
return fb_cmap_to_user(&info->cmap, &cmap);
case FBIOPAN_DISPLAY:
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
acquire_console_sem();
i = fb_pan_display(info, &var);
release_console_sem();
if (i)
return i;
if (copy_to_user(argp, &var, sizeof(var)))
return -EFAULT;
return 0;
case FBIO_CURSOR:
return -EINVAL;
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
con2fb.framebuffer = -1;
event.info = info;
event.data = &con2fb;
notifier_call_chain(&fb_notifier_list,
FB_EVENT_GET_CONSOLE_MAP, &event);
return copy_to_user(argp, &con2fb,
sizeof(con2fb)) ? -EFAULT : 0;
case FBIOPUT_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return - EFAULT;
if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
return -EINVAL;
#ifdef CONFIG_KMOD
if (!registered_fb[con2fb.framebuffer])
try_to_load(con2fb.framebuffer);
#endif /* CONFIG_KMOD */
if (!registered_fb[con2fb.framebuffer])
return -EINVAL;
event.info = info;
event.data = &con2fb;
return notifier_call_chain(&fb_notifier_list,
FB_EVENT_SET_CONSOLE_MAP,
&event);
case FBIOBLANK:
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
i = fb_blank(info, arg);//开关显示用
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
return i;
default:
if (fb->fb_ioctl == NULL)
return -EINVAL;
return fb->fb_ioctl(inode, file, cmd, arg, info);
}
}
注:
s3c2410fb.c中var->activate设置为FB_ACTIVATE_NOW,所以这里只考虑条件2。
int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
{
int err, flags = info->flags;
1:
if (var->activate & FB_ACTIVATE_INV_MODE) {
struct fb_videomode mode1, mode2;
int ret = 0;
fb_var_to_videomode(&mode1, var);
fb_var_to_videomode(&mode2, &info->var);
/* make sure we don't delete the videomode of current var */
ret = fb_mode_is_equal(&mode1, &mode2);
if (!ret) {
struct fb_event event;
event.info = info;
event.data = &mode1;
ret = notifier_call_chain(&fb_notifier_list,
FB_EVENT_MODE_DELETE, &event);
}
if (!ret)
fb_delete_videomode(&mode1, &info->modelist);
return ret;
}
2:
if ((var->activate & FB_ACTIVATE_FORCE) ||
memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
if (!info->fbops->fb_check_var) {
*var = info->var;
return 0;
}
if ((err = info->fbops->fb_check_var(var, info)))//先检查一下设置是否符合要求
return err;
if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {//条件符合
struct fb_videomode mode;
int err = 0;
info->var = *var;
if (info->fbops->fb_set_par)
info->fbops->fb_set_par(info);//设置可变参数
fb_pan_display(info, &info->var);//s3c2410不支持硬件虚拟显示,在s3c2410fb.c上没有实现该接口。
fb_set_cmap(&info->cmap, info);//调色板设置
fb_var_to_videomode(&mode, &info->var);
if (info->modelist.prev && info->modelist.next &&
!list_empty(&info->modelist))
err = fb_add_videomode(&mode, &info->modelist);
if (!err && (flags & FBINFO_MISC_USEREVENT)) {
struct fb_event event;
int evnt = (var->activate & FB_ACTIVATE_ALL) ?
FB_EVENT_MODE_CHANGE_ALL :
FB_EVENT_MODE_CHANGE;
info->flags &= ~FBINFO_MISC_USEREVENT;
event.info = info;
notifier_call_chain(&fb_notifier_list, evnt,
&event);
}
}
}
return 0;
}
/*
* s3c2410fb_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*
*/
static int s3c2410fb_set_par(struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
struct fb_var_screeninfo *var = &info->var;
if (var->bits_per_pixel == 16)
fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR;
else
fbi->fb->fix.visual = FB_VISUAL_PSEUDOCOLOR;
fbi->fb->fix.line_length = (var->width*var->bits_per_pixel)/8;
/* activate this new configuration */
s3c2410fb_activate_var(fbi, var);
return 0;
}
/* s3c2410fb_activate_var
*
* activate (set) the controller from the given framebuffer
* information
*/
static void s3c2410fb_activate_var(struct s3c2410fb_info *fbi,
struct fb_var_screeninfo *var)
{
fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_MODEMASK;
dprintk("%s: var->xres = %d\n", __FUNCTION__, var->xres);
dprintk("%s: var->yres = %d\n", __FUNCTION__, var->yres);
dprintk("%s: var->bpp = %d\n", __FUNCTION__, var->bits_per_pixel);
switch (var->bits_per_pixel) {
case 1:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
break;
case 2:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
break;
case 4:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
break;
case 8:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
break;
case 16:
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
break;
}
/* check to see if we need to update sync/borders */
if (!fbi->mach_info->fixed_syncs) {
dprintk("setting vert: up=%d, low=%d, sync=%d\n",
var->upper_margin, var->lower_margin,
var->vsync_len);
dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
var->left_margin, var->right_margin,
var->hsync_len);
fbi->regs.lcdcon2 =
S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
fbi->regs.lcdcon3 =
S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
S3C2410_LCDCON3_HFPD(var->left_margin - 1);
fbi->regs.lcdcon4 &= ~S3C2410_LCDCON4_HSPW(0xff);
fbi->regs.lcdcon4 |= S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
}
/* update X/Y info */
fbi->regs.lcdcon2 &= ~S3C2410_LCDCON2_LINEVAL(0x3ff);
fbi->regs.lcdcon2 |= S3C2410_LCDCON2_LINEVAL(var->yres - 1);
fbi->regs.lcdcon3 &= ~S3C2410_LCDCON3_HOZVAL(0x7ff);
fbi->regs.lcdcon3 |= S3C2410_LCDCON3_HOZVAL(var->xres - 1);
if (var->pixclock > 0) {
int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock);
clkdiv = (clkdiv / 2) -1;
if (clkdiv < 0)
clkdiv = 0;
fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_CLKVAL(0x3ff);
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);
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
/* set lcd address pointers */
s3c2410fb_set_lcdaddr(fbi);
writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
}
/**
* fb_set_cmap - set the colormap
* @cmap: frame buffer colormap structure
* @info: frame buffer info structure
*
* Sets the colormap @cmap for a screen of device @info.
*
* Returns negative errno on error, or zero on success.
*
*/
int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
{
int i, start, rc = 0;
u16 *red, *green, *blue, *transp;
u_int hred, hgreen, hblue, htransp = 0xffff;
red = cmap->red;
green = cmap->green;
blue = cmap->blue;
transp = cmap->transp;
start = cmap->start;
if (start < 0 || (!info->fbops->fb_setcolreg &&
!info->fbops->fb_setcmap))
return -EINVAL;
if (info->fbops->fb_setcmap) {
rc = info->fbops->fb_setcmap(cmap, info);
} else {
for (i = 0; i < cmap->len; i++) {
hred = *red++;
hgreen = *green++;
hblue = *blue++;
if (transp)
htransp = *transp++;
if (info->fbops->fb_setcolreg(start++,
hred, hgreen, hblue,
htransp, info))
break;
}
}
if (rc == 0)
fb_copy_cmap(cmap, &info->cmap);
return rc;
}
static int s3c2410fb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
unsigned int val;
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */
switch (fbi->fb->fix.visual) {
case FB_VISUAL_TRUECOLOR:
/* true-colour, use pseuo-palette */
if (regno < 16) {
u32 *pal = fbi->fb->pseudo_palette;
val = chan_to_field(red, &fbi->fb->var.red);
val |= chan_to_field(green, &fbi->fb->var.green);
val |= chan_to_field(blue, &fbi->fb->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, S3C2410_TFTPAL(regno));
schedule_palette_update(fbi, regno, val);
}
break;
default:
return 1; /* unknown type */
}
return 0;
}
int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
{
int rc, size = cmap->len * sizeof(u16);
struct fb_cmap umap;
if (cmap->start < 0 || (!info->fbops->fb_setcolreg &&
!info->fbops->fb_setcmap))
return -EINVAL;
memset(&umap, 0, sizeof(struct fb_cmap));
rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL);
if (rc)
return rc;
if (copy_from_user(umap.red, cmap->red, size) ||
copy_from_user(umap.green, cmap->green, size) ||
copy_from_user(umap.blue, cmap->blue, size) ||
(cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
fb_dealloc_cmap(&umap);
return -EFAULT;
}
umap.start = cmap->start;
rc = fb_set_cmap(&umap, info);
fb_dealloc_cmap(&umap);
return rc;
}
int fb_cmap_to_user(struct fb_cmap *from, struct fb_cmap_user *to)
{
int tooff = 0, fromoff = 0;
int size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
size = to->len - tooff;
if (size > (int) (from->len - fromoff))
size = from->len - fromoff;
if (size <= 0)
return -EINVAL;
size *= sizeof(u16);
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
if (copy_to_user(to->green+tooff, from->green+fromoff, size))
return -EFAULT;
if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
return -EFAULT;
if (from->transp && to->transp)
if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
return -EFAULT;
return 0;
}
int
fb_blank(struct fb_info *info, int blank)
{
int ret = -EINVAL;
if (blank > FB_BLANK_POWERDOWN)
blank = FB_BLANK_POWERDOWN;
if (info->fbops->fb_blank)
ret = info->fbops->fb_blank(blank, info);
if (!ret) {
struct fb_event event;
event.info = info;
event.data = ␣
notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event);
}
return ret;
}
/**
* s3c2410fb_blank
* @blank_mode: the blank mode we want.
* @info: frame buffer structure that represents a single frame buffer
*
* Blank the screen if blank_mode != 0, else unblank. Return 0 if
* blanking succeeded, != 0 if un-/blanking failed due to e.g. a
* video mode which doesn't support it. Implements VESA suspend
* and powerdown modes on hardware that supports disabling hsync/vsync:
* blank_mode == 2: suspend vsync
* blank_mode == 3: suspend hsync
* blank_mode == 4: powerdown
*
* Returns negative errno on error, or zero on success.
*
*/
static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
{
dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
if (mach_info == NULL)
return -EINVAL;
if (blank_mode == FB_BLANK_UNBLANK)
writel(0x0, S3C2410_TPAL);
else {
dprintk("setting TPAL to output 0x000000\n");
writel(S3C2410_TPAL_EN, S3C2410_TPAL);
}
return 0;
}
static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
int fbidx = iminor(file->f_dentry->d_inode);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
unsigned long off;
#if !defined(__sparc__) || defined(__sparc_v9__)
unsigned long start;
u32 len;
#endif
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
if (!fb)
return -ENODEV;
if (fb->fb_mmap) {
int res;
lock_kernel();
res = fb->fb_mmap(info, file, vma);
unlock_kernel();
return res;
}
/* 所有的驱动应该拥有自己的mmap实现,可惜的是s3c2410fb.c中没有实现mmap接口,以下内容我也不想去研究了。 */
#if defined(__sparc__) && !defined(__sparc_v9__)
/* Should never get here, all fb drivers should have their own
mmap routines */
return -EINVAL;
#else
/* !sparc32... */
lock_kernel();
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
if (off >= len) {
/* memory mapped io */
off -= len;
if (info->var.accel_flags) {
unlock_kernel();
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
}
unlock_kernel();
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL;
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
#if defined(__sparc_v9__)
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
#else
#if defined(__mc68000__)
#if defined(CONFIG_SUN3)
pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
#elif defined(CONFIG_MMU)
if (CPU_IS_020_OR_030)
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
if (CPU_IS_040_OR_060) {
pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
/* Use no-cache mode, serialized */
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
}
#endif
#elif defined(__powerpc__)
vma->vm_page_prot = phys_mem_access_prot(file, off,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
#elif defined(__alpha__)
/* Caching is off in the I/O space quadrant by design. */
#elif defined(__i386__) || defined(__x86_64__)
if (boot_cpu_data.x86 > 3)
pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
#elif defined(__mips__)
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#elif defined(__hppa__)
pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
#elif defined(__arm__) || defined(__sh__) || defined(__m32r__)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
#elif defined(__ia64__)
if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start))
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
else
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#else
#warning What do we have to do here??
#endif
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
#endif /* !__sparc_v9__ */
return 0;
#endif /* !sparc32 */
}
以下以内核自带的logo程序,介绍一下frame buffer的具体用法:
static struct logo_data {
int depth;
int needs_directpalette;
int needs_truepalette;
int needs_cmapreset;
const struct linux_logo *logo;
} fb_logo;
注:
logo图像数据结构.
/* 填充好logo显示用的数据 */
int fb_prepare_logo(struct fb_info *info)
{
int depth = fb_get_color_depth(&info->var, &info->fix);//16
memset(&fb_logo, 0, sizeof(struct logo_data));
if (info->flags & FBINFO_MISC_TILEBLITTING)
return 0;
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
depth = info->var.blue.length;
if (info->var.red.length < depth)
depth = info->var.red.length;
if (info->var.green.length < depth)
depth = info->var.green.length;
}
if (depth >= 8) {
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
fb_logo.needs_truepalette = 1;//使用tft屏的话就选择这个吧.
break;
case FB_VISUAL_DIRECTCOLOR:
fb_logo.needs_directpalette = 1;
fb_logo.needs_cmapreset = 1;
break;
case FB_VISUAL_PSEUDOCOLOR:
fb_logo.needs_cmapreset = 1;
break;
}
}
/* Return if no suitable logo was found */
fb_logo.logo = fb_find_logo(depth);//根据内核的配置,选择合适的logo.
if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) {
fb_logo.logo = NULL;
return 0;
}
/* What depth we asked for might be different from what we get */
if (fb_logo.logo->type == LINUX_LOGO_CLUT224)//在我的内核中选的就是这个.
fb_logo.depth = 8;
else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
fb_logo.depth = 4;
else
fb_logo.depth = 1;
return fb_logo.logo->height;
}
好了,准备好logo显示用的数据后,在适当的时候即可调用以下函数来显示:
int fb_show_logo(struct fb_info *info)
{
u32 *palette = NULL, *saved_pseudo_palette = NULL;
unsigned char *logo_new = NULL;
struct fb_image image;
int x;
/* Return if the frame buffer is not mapped or suspended */
if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING)
return 0;
image.depth = 8;
image.data = fb_logo.logo->data;
if (fb_logo.needs_cmapreset)
fb_set_logocmap(info, fb_logo.logo);
if (fb_logo.needs_truepalette ||
fb_logo.needs_directpalette) {
palette = kmalloc(256 * 4, GFP_KERNEL);//分配调色板所需的空间.
if (palette == NULL)
return 0;
if (fb_logo.needs_truepalette)
fb_set_logo_truepalette(info, fb_logo.logo, palette);//根据我们所找到的logo图像填充调色板
else
fb_set_logo_directpalette(info, fb_logo.logo, palette);
saved_pseudo_palette = info->pseudo_palette;
info->pseudo_palette = palette;
}
if (fb_logo.depth <= 4) {
logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height,
GFP_KERNEL);
if (logo_new == NULL) {
kfree(palette);
if (saved_pseudo_palette)
info->pseudo_palette = saved_pseudo_palette;
return 0;
}
image.data = logo_new;
fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth);
}
image.width = fb_logo.logo->width;
image.height = fb_logo.logo->height;
image.dy = 0;
/* 以下循环用于查询调色板,把logo信息填写到相应的frame buffer中 */
for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) &&
x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) {
image.dx = x;
info->fbops->fb_imageblit(info, &image);
}
kfree(palette);
if (saved_pseudo_palette != NULL)
info->pseudo_palette = saved_pseudo_palette;
kfree(logo_new);
return fb_logo.logo->height;
}
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,
.fb_cursor = soft_cursor,
};
void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
{
u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
u32 width = image->width, height = image->height;
u32 dx = image->dx, dy = image->dy;
int x2, y2, vxres, vyres;
u8 __iomem *dst1;
if (p->state != FBINFO_STATE_RUNNING)
return;
vxres = p->var.xres_virtual;
vyres = p->var.yres_virtual;
/*
* We could use hardware clipping but on many cards you get around
* hardware clipping by writing to framebuffer directly like we are
* doing here.
*/
if (image->dx > vxres || image->dy > vyres)
return;
x2 = image->dx + image->width;
y2 = image->dy + image->height;
dx = image->dx > 0 ? image->dx : 0;
dy = image->dy > 0 ? image->dy : 0;
x2 = x2 < vxres ? x2 : vxres;
y2 = y2 < vyres ? y2 : vyres;
width = x2 - dx;
height = y2 - dy;
bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);//算出要绘图的起始点位置(以bit为单位)
start_index = bitstart & (32 - 1);//非字对齐地址
pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
bitstart /= 8;
bitstart &= ~(bpl - 1);
dst1 = p->screen_base + bitstart;//图像在frame buffer上的地址(目标地址)
if (p->fbops->fb_sync)
p->fbops->fb_sync(p);
if (image->depth == 1) {
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
} else {
fgcolor = image->fg_color;
bgcolor = image->bg_color;
}
if (32 % bpp == 0 && !start_index && !pitch_index &&
((width & (32/bpp-1)) == 0) &&
bpp >= 8 && bpp <= 32)
fast_imageblit(image, p, dst1, fgcolor, bgcolor);
else
slow_imageblit(image, p, dst1, fgcolor, bgcolor,
start_index, pitch_index);
} else
color_imageblit(image, p, dst1, start_index, pitch_index);
}
static inline void color_imageblit(const struct fb_image *image,
struct fb_info *p, u8 __iomem *dst1,
u32 start_index,
u32 pitch_index)
{
/* Draw the penguin */
u32 __iomem *dst, *dst2;
u32 color = 0, val, shift;
int i, n, bpp = p->var.bits_per_pixel;
u32 null_bits = 32 - bpp;
u32 *palette = (u32 *) p->pseudo_palette;
const u8 *src = image->data;//image->data里的数据,实际上记录着的是调色板上对应颜色的序号.
dst2 = (u32 __iomem *) dst1;
for (i = image->height; i--; ) {//行
n = image->width;
dst = (u32 __iomem *) dst1;
shift = 0;
val = 0;
if (start_index) {
u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
val = FB_READL(dst) & start_mask;
shift = start_index;
}
while (n--) {//列
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR )
color = palette[*src];//调色板在这个时候终于派上用场了
else
color = *src;
color <<= LEFT_POS(bpp);.
val |= SHIFT_HIGH(color, shift);
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
val = (shift == null_bits) ? 0 :
SHIFT_LOW(color, 32 - shift);
}
shift += bpp;
shift &= (32 - 1);
src++;
}
if (shift) {
u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
}
dst1 += p->fix.line_length;
if (pitch_index) {
dst2 += p->fix.line_length;
dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
start_index += pitch_index;
start_index &= 32 - 1;
}
}
}
后记:
自问这个文档写得不是很好。由于到目前为止接手的项目都不带tft的屏,暂时没什么动力深入去研究,等以后有机会再继续完善这个文档好了^_^
frame.exe 是什么?
Net Frame Work是什么意思
frame.dll是什吗
Frame Relay是什么意思
请问什么是frame pointer
frame buffer设置求助
sink base frame 底框
frame缓冲器的作用
java中的frame问题.
如何防被人Frame
什么是Open frame
VB隐藏Frame
frame maker 软件
AXWIN Frame window 是什么?
FRAME之间对象如何引用?
frame和framework的区别?
on-chip frame buffer size
防止被人frame的代码
frame面板是指什么啊
delphi 中frame内存释放的问题
frame同页面转页问题
如何在操作系统中安装.Net Frame
如何在操作系统中安装.Net Frame
如何解决“Frame.exe----应用程序错误”