[日期:2011-10-04] | 来源:Linux社区 作者:yaozhenguo2006 |
由s3c2410fb_ops结构可以看出,mini2440lcd驱动主要实现了下面几个函数:
s3c2410fb_check_var
s3c2410fb_set_par
s3c2410fb_blank
s3c2410fb_setcolreg
cfb_fillrect
cfb_copyarea
cfb_imageblit
其中最重要的是s3c2410fb_set_par,这个函数根据fbinfo的值初始化了底层的lcd控制器,重点分析这个函数。他由fb_set_var调用,对应则frambufer核心ioctl中的FBIOPUT_VSCREENINFO命令。其他的函数也是为了完成lcd的相关功能而编写的,与具体实现的功能有关。s3c2410fb_set_par函数定义如下:
- 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:
- info->fix.visual = FB_VISUAL_MONO01;
- break;
- default:
- info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
- break;
- }
- info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
- /* activate this new configuration */
- s3c2410fb_activate_var(info);
- return 0;
- }
- static void s3c2410fb_activate_var(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
- struct fb_var_screeninfo *var = &info->var;
- int clkdiv;
- clkdiv = DIV_ROUND_UP(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) {
- //判断lcd型号,我们的lcd是TFT型lcd,所以下面语句执行
- s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
- //这个函数主要的功能就是将info中的lcd配置相关的值赋值给s3c2410fb_info结构的regs成员
- //这个regs是一个s3c2410fb_hw结构,这个结构就是定义了5个lcd控制寄存器lcdcon1~5
- --clkdiv;
- if (clkdiv < 0)
- clkdiv = 0;
- } else {
- //如果是STN型的lcd,那么执行下面的函数。因为s3c2440的lcd控制器由有几个专门用于控制STN型lcd的寄存器,所以要单独设置
- 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);
- 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);
- //将配置值写入五个寄存器
- /* set lcd address pointers */
- s3c2410fb_set_lcdaddr(info);
- //设置显存地址寄存器,设置为我们分配的那块内存,设置之后,lcd控制器就会在这块内存取数据送往lcd显示
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- //打开视频显示,这样lcd就可以正确显示了
- writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
- }
mini2440lcd驱动分别涉及到了platform和frambuffer核心,利用这两个核心实现其功能。刚开始分析这个驱动的时候,总是感觉很乱没有重点。我想这个主要是和自己对frambuffer和lcd显示还不熟悉的原因。看了几天过后才逐渐有点眉目。最让人迷惑的就是如何设置lcd控制器的寄存器问题。我以前认为这个应该在probe函数中设置,一般来说这个函数检测设备状态,初始化设备,然后设备就绪,应用程序就可以操作了。但是在s3c2410fb_probe中只是设置了相关的GPIO接口寄存器,根本没有设置lcd控制寄存器。后来发现是在s3c2410fb_set_par函数中设置的。这个函数对应用户空间ioctl的FBIOPUT_VSCREENINFO。也就是说应用程序必须调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)才能设置正确的lcd状态,但是这个命令有一个参数是fb_var_screeninfo结构,也就意味这应用程序必须填充这样一个结构,才可以调用ioctl。这样一来内核初始化的默认配置信息就没用了。唯一的办法是先调用ioctl(fd,FBIOGET_VSCREENINFO,struct fb_var_screeninfo *var)获得这个结构,然后修改之后在调用ioctl(fd,FBIOPUT_VSCREENINFO,struct fb_var_screeninfo *var)将修改的值写入。我在MiniGUI的源码中验证了这个推论,在MiniGUI的fbcon图形引擎中的FB_SetVideoMode函数中,有如下的调用
- if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
- GAL_SetError("NEWGAL>FBCON: Couldn't get console pixel format\n");
- FB_VideoQuit(this);
- return(-1);
- }
- if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
- vinfo.yres_virtual = height;
- if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
- GAL_SetError("NEWGAL>FBCON: Couldn't set console screen info");
- return(NULL);
- }
- }