您的位置:首页 > Web前端

s3c2410_lcd & frame buffer 驱动分析

2011-08-08 17:49 323 查看
s3c2410_lcd & frame buffer 驱动分析
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;

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: