基于S3C2440的Linux-3.6.6移植——LCD的应用

 

要想使LCD工作,需要修改arch/arm/mach-s3c24xx/Mach-zhaocj2440.c文件中的相关内容。

zhaocj2440_lcd_cfg结构数组定义了各种LCD的时序及配置,我们需要在这个数组的末端添加针对自己开发板上的LCD的相关配置。

static struct s3c2410fb_display zhaocj2440_lcd_cfg[] __initdata = {

……

       /*zhaocj2440 + 3.5" TFT (WXCAT35-TG3) + touchscreen*/

       [4] = {

              _LCD_DECLARE(

                     /* clock */

                     7,

                     /* xres, margin_right, margin_left, hsync */

                     320, 20, 38, 30,

                     /* yres, margin_top, margin_bottom, vsync */

                     240, 12, 15, 3,

                     /* refresh rate */

                     64),

              .bpp = 32,

              .type =(S3C2410_LCDCON1_TFT24BPP| S3C2410_LCDCON1_TFT),

              .lcdcon5 = (S3C2410_LCDCON5_INVVFRAME |

                               S3C2410_LCDCON5_INVVLINE|

                               S3C2410_LCDCON5_PWREN),

       },

};

 

下面再来修改zhaocj2440_features_str数组,该数组定义了LCD、背光、触摸屏和摄像信息,数字代表的是应用zhaocj2440_lcd_cfg数组中定义的第几个LCD,b代表背光,t代表触摸屏,c代表摄像。在这里把这个数组赋值为4t,即:

static char zhaocj2440_features_str[12]__initdata = "4t";

 

通过上面两个步骤的修改,就完成了LCD的添加,编译后,烧写进开发板即可。因为系统是默认配置LCD,并且默认配置了开机显示logo,因此启动系统后,会在LCD的左上角显示企鹅的logo,而且在这个logo的左下角会有一个光标在不停的闪。如果我们执行下列命令,则起到了清屏作用,其中/dev/fb0就是帧缓冲设备,也就是我们开发板上的LCD设备:

[root@zhaocj /]# dd if=/dev/zero of=/dev/fb0

 

下面就具体介绍一下LCD的驱动内容。

下面的宏定义是为了简化LCD的时序配置而定义的,zhaocj2440_lcd_cfg数组就用到了该宏,因为通过该宏定义,可以很容易的为s3c2410fb_display结构中的成员赋值,尤其是可以自动计算pixclock成员的值。不同的LCD,其时序是完全不同的,具体的参数要参考LCD的数据手册。

#define_LCD_DECLARE(_clock,_xres,margin_left,margin_right,hsync, \

                     _yres,margin_top,margin_bottom,vsync,refresh) \

       .width= _xres, \                   //水平分辨率,也就是LCD的宽

       .xres= _xres, \

       .height= _yres, \                  //垂直分辨率,也就是LCD的高

       .yres= _yres, \

       .left_margin    = margin_left, \                    //左侧行切换的回扫时间

       .right_margin  = margin_right,      \             //右侧行切换的回扫时间

       .upper_margin       = margin_top, \                    //上侧帧切换的回扫时间

       .lower_margin       = margin_bottom,  \             //下侧帧切换的回扫时间

       .hsync_len      = hsync,  \                           //水平同步的长度

       .vsync_len      = vsync,  \                           //垂直同步的长度

       .pixclock = ((_clock*100000000000LL) /    \     

                        ((refresh) * \                   //场频,即刷新率,

                        (hsync + margin_left + _xres + margin_right)* \         //水平周期

                        (vsync + margin_top + _yres +margin_bottom))), \     //垂直周期

       .bpp        = 16,\                    //每个像素的位数

       .type              = (S3C2410_LCDCON1_TFT16BPP |\

                        S3C2410_LCDCON1_TFT)                         //LCD的类型

 

在上面的LCD移植过程中,我们改变了宏定义中的bpp和type的默认值,原宏定义的颜色值为16BPP,而我们移植的颜色值为24BPP。

 

接着往下看,zhaocj2440_fb_info结构定义了LCD的配置,包括前面介绍的LCD的时序配置,以及s3c2440相关LCD引脚的配置。

 

前面在移植的过程中,介绍了zhaocj2440_features_str数组,以及各个字符的含义。而函数zhaocj2440_parse_features的作用就是根据上面的数组来配置zhaocj2440_features_t结构,并逐一给出各个平台设备。在zhaocj2440_parse_features函数内,通过下列语句给出了LCD平台设备:

features->optional[features->count++]= &s3c_device_lcd;

LCD平台设备是在Devs.c文件(arch/arm/plat-samsung目录下)内定义的:

struct platform_device s3c_device_lcd = {

       .name             = "s3c2410-lcd",

       .id          = -1,

       .num_resources      = ARRAY_SIZE(s3c_lcd_resource),

       .resource = s3c_lcd_resource,

       .dev        = {

              .dma_mask            = &samsung_device_dma_mask,

              .coherent_dma_mask     = DMA_BIT_MASK(32),

       }

};

 

zhaocj2440_init函数的作用是对开发板上的资源进行初始化,当然也包括了对LCD的初始化,而且通过下列语句把LCD这个平台设备添加到了平台设备总线上,并进行了注册:

platform_add_devices(features.optional,features.count);

 

上面主要介绍了LCD的平台设备,下面介绍LCD的平台驱动。在S3c2410fb.c文件(drivers/video目录下)内定义的。

我们先来给出三个很重要的结构:

struct fb_info {

       atomic_tcount;

       intnode;

       intflags;

       structmutex lock;          /* Lock foropen/release/ioctl funcs */

       structmutex mm_lock;         /* Lock for fb_mmapand smem_* fields */

       structfb_var_screeninfo var; /* Current var */           //可变参数

       structfb_fix_screeninfo fix;  /* Current fix */            //固定参数

       structfb_monspecs monspecs;       /* CurrentMonitor specs */          //显示器标准

       structwork_struct queue;      /* Framebufferevent queue */             //帧缓冲事件队列

       structfb_pixmap pixmap;      /* Image hardwaremapper */              //图像硬件mapper

       structfb_pixmap sprite; /* Cursor hardwaremapper */                     //光标硬件mapper

       structfb_cmap cmap;            /* Current cmap*/               //颜色表

       structlist_head modelist;      /* mode list */

       struct fb_videomode *mode;  /* current mode */                //视频模式

 

#ifdef CONFIG_FB_BACKLIGHT

       /*assigned backlight device */

       /*set before framebuffer registration,

          remove after unregister */

       structbacklight_device *bl_dev;                 //背光设备

 

       /*Backlight level curve */            //背光调整

       structmutex bl_curve_mutex;      

       u8bl_curve[FB_BACKLIGHT_LEVELS];

#endif

#ifdef CONFIG_FB_DEFERRED_IO

       structdelayed_work deferred_work;

       structfb_deferred_io *fbdefio;

#endif

 

       structfb_ops *fbops;            //帧缓冲操作

       structdevice *device;            /* This is theparent */

       structdevice *dev;         /* This is this fbdevice */

       int class_flag;                    /* private sysfs flags */

#ifdef CONFIG_FB_TILEBLITTING

       structfb_tile_ops *tileops;    /* Tile Blitting*/        //图块Blitting

#endif

       char__iomem *screen_base;  /* Virtual address*/              //虚拟基地址

       unsignedlong screen_size;     /* Amount ofioremapped VRAM or 0 */    //内存大小

       void*pseudo_palette;           /* Fake paletteof 16 colors */      //伪16色颜色表

#define FBINFO_STATE_RUNNING   0

#define FBINFO_STATE_SUSPENDED     1

       u32state;               /* Hardware state i.esuspend */

       void*fbcon_par;                /* fbconuse-only private area */

       /*From here on everything is device dependent */

       void*par;

       /*we need the PCI or similar aperture base/size not

          smem_start/size as smem_start may just be anobject

          allocated inside the aperture so may notactually overlap */

       structapertures_struct {

              unsignedint count;

              structaperture {

                     resource_size_tbase;

                     resource_size_tsize;

              }ranges[0];

       }*apertures;

};

 

struct fb_var_screeninfo {

       __u32xres;                   /* visibleresolution              */           //可见分辨率

       __u32yres;

       __u32xres_virtual;        /* virtual resolution              */           //虚拟分辨率

       __u32yres_virtual;

       __u32xoffset;               /* offset fromvirtual to visible */ //虚拟到可见之间的偏移

       __u32yoffset;               /* resolution                 */

 

       __u32bits_per_pixel;           /* guess what                */    //每个像素的位数,BPP

       __u32grayscale;           /* 0 = color, 1 =grayscale,    */    //非0为灰度

                                   /*>1 = FOURCC                 */

       structfb_bitfield red;            /* bitfield infb mem if true color, */

       structfb_bitfield green;  /* else only length issignificant */

       structfb_bitfield blue;

       structfb_bitfield transp; /* transparency                     */                  //透明度

 

       __u32nonstd;               /* != 0 Non standardpixel format */    //非0为非标准像素格式

 

       __u32activate;                     /* seeFB_ACTIVATE_*              */

 

       __u32height;                /* height ofpicture in mm    */         //高

       __u32width;                /* width of picturein mm     */        //宽

 

       __u32accel_flags;         /* (OBSOLETE) seefb_info.flags */

 

       /*Timing: All values in pixclocks, except pixclock (of course) */

       __u32pixclock;                   /* pixel clockin ps (pico seconds) */

       __u32left_margin;        /* time from sync topicture  */

       __u32right_margin;             /* time frompicture to sync  */

       __u32upper_margin;            /* time from syncto picture  */

       __u32lower_margin;

       __u32hsync_len;          /* length ofhorizontal sync   */

       __u32vsync_len;          /* length of verticalsync       */

       __u32sync;                  /* see FB_SYNC_*              */

       __u32vmode;               /* see FB_VMODE_*           */

       __u32rotate;                /* angle we rotatecounter clockwise */

       __u32colorspace;          /* colorspace forFOURCC-based modes */         //顺时针旋转的角度

       __u32reserved[4];        /* Reserved for futurecompatibility */

};

 

struct fb_fix_screeninfo {

       charid[16];                  /* identificationstring eg "TT Builtin" */     //字符串形式的标识符

       unsignedlong smem_start;    /* Start of framebuffer mem */    //帧缓冲的开始位置

                                   /*(physical address) */

       __u32smem_len;                 /* Length offrame buffer mem */        //帧缓冲长度

       __u32type;                  /* see FB_TYPE_*        */

       __u32type_aux;                   /* Interleavefor interleaved Planes */

       __u32visual;                /* see FB_VISUAL_*           */

       __u16xpanstep;                   /* zero if nohardware panning  */

       __u16ypanstep;                   /* zero if nohardware panning  */

       __u16ywrapstep;          /* zero if no hardwareywrap    */

       __u32line_length;         /* length of a linein bytes    */         //1行的字节数

       unsignedlong mmio_start;    /* Start of MemoryMapped I/O   */

                                   /*(physical address) */

       __u32mmio_len;                 /* Length ofMemory Mapped I/O  */

       __u32accel;                 /* Indicate todriver which    */

                                   /*  specific chip/card we have      */

       __u16capabilities;         /* see FB_CAP_*                 */

       __u16reserved[2];        /* Reserved for futurecompatibility */

};

 

再回过头来介绍S3c2410fb.c文件的内容。在s3c2410fb_init函数内通过platform_driver_register函数注册平台驱动s3c2410fb_driver,该平台驱动就是与上面介绍的LCD平台设备相匹配的平台驱动。当设备和驱动匹配上后,系统会调用s3c24xxfb_probe函数:

static int __devinit s3c24xxfb_probe(struct platform_device *pdev,

                              enum s3c_drv_type drv_type)

{

       structs3c2410fb_info *info;

       structs3c2410fb_display *display;

       structfb_info *fbinfo;

       structs3c2410fb_mach_info *mach_info;

       structresource *res;

       intret;

       intirq;

       inti;

       intsize;

       u32lcdcon1;

 

       //得到zhaocj2440_fb_info结构,即配置LCD的所有数据

       mach_info= pdev->dev.platform_data;

       if(mach_info == NULL) {

              dev_err(&pdev->dev,

                     "noplatform data for lcd, cannot attach\n");

              return-EINVAL;

       }

 

       if(mach_info->default_display >= mach_info->num_displays) {

              dev_err(&pdev->dev,"default is %d but only %d displays\n",

                     mach_info->default_display,mach_info->num_displays);

              return-EINVAL;

       }

 

       //得到zhaocj2440_lcd_cfg,即配置LCD时序等的数据

       display= mach_info->displays + mach_info->default_display;

 

       //获取中断号

       irq= platform_get_irq(pdev, 0);

       if(irq < 0) {

              dev_err(&pdev->dev,"no irq for device\n");

              return-ENOENT;

       }

 

       //为帧缓冲分配空间,得到fb_info结构

       fbinfo= framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);

       if(!fbinfo)

              return-ENOMEM;

 

       //把fb_info作为平台设备的私有数据

       platform_set_drvdata(pdev,fbinfo);

 

       info= fbinfo->par;        //获得fb_info的私有数据

       info->dev= &pdev->dev;            //获得平台设备

       info->drv_type= drv_type;          //该LCD驱动为兼容S3C2410的

 

       //得到该设备的内存资源

       res= platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if(res == NULL) {

              dev_err(&pdev->dev,"failed to get memory registers\n");

              ret= -ENXIO;

              gotodealloc_fb;

       }

 

       //得到内存大小

       size= resource_size(res);

       //申请内存区

       info->mem= request_mem_region(res->start, size, pdev->name);

       if(info->mem == NULL) {

              dev_err(&pdev->dev,"failed to get memory region\n");

              ret= -ENOENT;

              gotodealloc_fb;

       }

 

       //把内存物理地址转换为虚拟地址

       info->io= ioremap(res->start, size);

       if(info->io == NULL) {

              dev_err(&pdev->dev,"ioremap() of registers failed\n");

              ret= -ENXIO;

              gotorelease_mem;

       }

 

       //基址

       if(drv_type == DRV_S3C2412)

              info->irq_base = info->io + S3C2412_LCDINTBASE;

       else

              info->irq_base = info->io +S3C2410_LCDINTBASE;

 

       dprintk("devinit\n");

 

       //复制驱动名——s3c2410fb

       strcpy(fbinfo->fix.id,driver_name);

 

       /*Stop the video */

       //禁止LCD显示

       lcdcon1= readl(info->io + S3C2410_LCDCON1);

       writel(lcdcon1& ~S3C2410_LCDCON1_ENVID,info->io + S3C2410_LCDCON1);

 

       //为LCD的固定参数赋值

       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;

 

       //为LCD的可变参数赋值

       fbinfo->var.nonstd      = 0;

       fbinfo->var.activate          = FB_ACTIVATE_NOW;

       fbinfo->var.accel_flags     = 0;

       fbinfo->var.vmode     =FB_VMODE_NONINTERLACED;

 

       fbinfo->fbops           = &s3c2410fb_ops;              //LCD的操作函数

       fbinfo->flags            = FBINFO_FLAG_DEFAULT;

       fbinfo->pseudo_palette      = &info->pseudo_pal;

 

       //初始化调色板,即清空

       for (i = 0; i < 256; i++)

              info->palette_buffer[i] =PALETTE_BUFF_CLEAR;

 

       //申请中断,中断函数为s3c2410fb_irq

       ret =request_irq(irq, s3c2410fb_irq,0, pdev->name, info);

       if(ret) {

              dev_err(&pdev->dev,"cannot get irq %d - err %d\n", irq, ret);

              ret= -EBUSY;

              gotorelease_regs;

       }

 

       //获得时钟

       info->clk= clk_get(NULL, "lcd");

       if(IS_ERR(info->clk)) {

              printk(KERN_ERR"failed to get lcd clock source\n");

              ret = PTR_ERR(info->clk);

              gotorelease_irq;

       }

      

       //打开时钟

       clk_enable(info->clk);

       dprintk("gotand enabled clock\n");

 

       usleep_range(1000,1000);

 

       //得到时钟速率

       info->clk_rate= clk_get_rate(info->clk);

 

       /*find maximum required memory size for display */

       //得到所需要的最大的显存大小

       for(i = 0; i < mach_info->num_displays; i++) {

              unsignedlong smem_len = mach_info->displays[i].xres;

 

              smem_len*= mach_info->displays[i].yres;

              smem_len*= mach_info->displays[i].bpp;

              smem_len>>= 3;          //除以8,即单位为字节

              if(fbinfo->fix.smem_len < smem_len)

                     fbinfo->fix.smem_len= smem_len;

       }

 

       /*Initialize video memory */

       //为帧缓冲分配内存空间

       ret= s3c2410fb_map_video_memory(fbinfo);

       if(ret) {

              printk(KERN_ERR"Failed to allocate video RAM: %d\n", ret);

              ret= -ENOMEM;

              gotorelease_clock;

       }

 

       dprintk("gotvideo memory\n");

 

       fbinfo->var.xres= display->xres;         //水平分辨率

       fbinfo->var.yres= display->yres;         //垂直分辨率

       fbinfo->var.bits_per_pixel= display->bpp;         //用几位来表示一个像素

 

       //初始化所有与LCD有关的寄存器

       s3c2410fb_init_registers(fbinfo);

 

       //检查LCD的可变参数

       s3c2410fb_check_var(&fbinfo->var, fbinfo);

 

       ret = s3c2410fb_cpufreq_register(info);

       if(ret < 0) {

              dev_err(&pdev->dev,"Failed to register cpufreq\n");

              gotofree_video_memory;

       }

 

       //注册帧缓冲设备

       ret= register_framebuffer(fbinfo);

       if(ret < 0) {

              printk(KERN_ERR"Failed to register framebuffer device: %d\n",

                     ret);

              gotofree_cpufreq;

       }

 

       /*create device files */

       ret= device_create_file(&pdev->dev, &dev_attr_debug);

       if(ret)

              printk(KERN_ERR"failed to add debug attribute\n");

 

       printk(KERN_INFO"fb%d: %s frame buffer device\n",

              fbinfo->node,fbinfo->fix.id);

 

       return0;

 

 free_cpufreq:

       s3c2410fb_cpufreq_deregister(info);

free_video_memory:

       s3c2410fb_unmap_video_memory(fbinfo);

release_clock:

       clk_disable(info->clk);

       clk_put(info->clk);

release_irq:

       free_irq(irq,info);

release_regs:

       iounmap(info->io);

release_mem:

       release_mem_region(res->start,size);

dealloc_fb:

       platform_set_drvdata(pdev,NULL);

       framebuffer_release(fbinfo);

       returnret;

}

 

在上面的函数中,指定了LCD的操作集s3c2410fb_ops,即:

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,                          //绘制图形

};

 

对于上面的LCD操作函数中,我们只介绍最重要的两个函数——s3c2410fb_check_var和s3c2410fb_set_par。

static int s3c2410fb_check_var(struct fb_var_screeninfo *var,

                            struct fb_info *info)

{

       structs3c2410fb_info *fbi =info->par;

       structs3c2410fb_mach_info*mach_info = fbi->dev->platform_data;

       structs3c2410fb_display *display= NULL;

       structs3c2410fb_display*default_display = mach_info->displays +

                                              mach_info->default_display;

       inttype = default_display->type;

       unsigned i;

 

       dprintk("check_var(var=%p,info=%p)\n", var, info);

 

       /* validate x/yresolution */

       /*choose default mode if possible */

       //检验水平、垂直分辨率,以及bpp

       //先检验缺省的LCD,不行再检验其他

       if(var->yres == default_display->yres &&

           var->xres == default_display->xres &&

           var->bits_per_pixel== default_display->bpp)

              display= default_display;

       else

              for(i = 0; i < mach_info->num_displays; i++)

                     if(type == mach_info->displays[i].type &&

                         var->yres ==mach_info->displays[i].yres &&

                         var->xres == mach_info->displays[i].xres&&

                         var->bits_per_pixel ==mach_info->displays[i].bpp) {

                            display= mach_info->displays + i;

                            break;

                     }

 

       if(!display) {

              dprintk("wrongresolution or depth %dx%d at %d bpp\n",

                     var->xres, var->yres,var->bits_per_pixel);

              return-EINVAL;

       }

 

       /*it is always the size as the display */

       //为水平、垂直分辨率,和高、宽赋值

       var->xres_virtual= display->xres;

       var->yres_virtual= display->yres;

       var->height= display->height;

       var->width= display->width;

 

       /*copy lcd settings */

       //为LCD的其他时序赋值

       var->pixclock= display->pixclock;

       var->left_margin= display->left_margin;

       var->right_margin= display->right_margin;

       var->upper_margin =display->upper_margin;

       var->lower_margin= display->lower_margin;

       var->vsync_len =display->vsync_len;

       var->hsync_len =display->hsync_len;

 

       fbi->regs.lcdcon5 =display->lcdcon5; 

       /* set displaytype */

       fbi->regs.lcdcon1= display->type;

 

       var->transp.offset = 0;

       var->transp.length = 0;

       /* set r/g/bpositions */

       //设置红、绿、蓝三色的位置

       switch(var->bits_per_pixel) {

       case1:

       case2:

       case4:

              var->red.offset       = 0;

              var->red.length      = var->bits_per_pixel;

              var->green      = var->red;

              var->blue       = var->red;

              break;

       case8:

              if(display->type != S3C2410_LCDCON1_TFT){

                     /*8 bpp 332 */

                     var->red.length             = 3;

                     var->red.offset              = 5;

                     var->green.length   = 3;

                     var->green.offset    = 2;

                     var->blue.length     = 2;

                     var->blue.offset     = 0;

              } else {

                     var->red.offset              = 0;

                     var->red.length             = 8;

                     var->green             = var->red;

                     var->blue              = var->red;

              }

              break;

       case 12:

              /* 12 bpp 444 */

              var->red.length             = 4;

              var->red.offset              = 8;

              var->green.length   = 4;

              var->green.offset    = 4;

              var->blue.length     = 4;

              var->blue.offset     = 0;

              break;

 

       default:

       case 16:

              if(display->lcdcon5 & S3C2410_LCDCON5_FRM565){

                     /* 16 bpp, 565 format */

                     var->red.offset              = 11;              //偏移量

                     var->green.offset    = 5;

                     var->blue.offset     = 0;

                     var->red.length             = 5;        //长度

                     var->green.length   = 6;

                     var->blue.length     = 5;

              } else {

                     /* 16 bpp, 5551 format */

                     var->red.offset              = 11;

                     var->green.offset    = 6;

                     var->blue.offset     = 1;

                     var->red.length             = 5;

                     var->green.length   = 5;

                     var->blue.length     = 5;

              }

              break;

       case32:

              /*24 bpp 888 and 8 dummy */

              var->red.length             = 8;

              var->red.offset              = 16;

              var->green.length   = 8;

              var->green.offset    = 8;

              var->blue.length     = 8;

              var->blue.offset     = 0;

              break;

       }

       return0;

}

 

在s3c2410fb_set_par函数中,最主要的作用是调用了s3c2410fb_activate_var函数:

static void s3c2410fb_activate_var(struct fb_info *info)

{

       structs3c2410fb_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

       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);

 

       //依据是TFT还是STN,来设置LCD控制寄存器1~4

       if (type == S3C2410_LCDCON1_TFT) {

              s3c2410fb_calculate_tft_lcd_regs(info,&fbi->regs);

              --clkdiv;

              if(clkdiv < 0)

                     clkdiv= 0;

       }else {

              s3c2410fb_calculate_stn_lcd_regs(info,&fbi->regs);

              if(clkdiv < 2)

                     clkdiv= 2;

       }

 

       //设置CLKVAL的值

       fbi->regs.lcdcon1|=  S3C2410_LCDCON1_CLKVAL(clkdiv);

 

       /*write new registers */

 

       dprintk("newregister set:\n");

       dprintk("lcdcon[1]= 0x%08lx\n", fbi->regs.lcdcon1);

       dprintk("lcdcon[2]= 0x%08lx\n", fbi->regs.lcdcon2);

       dprintk("lcdcon[3]= 0x%08lx\n", fbi->regs.lcdcon3);

       dprintk("lcdcon[4]= 0x%08lx\n", fbi->regs.lcdcon4);

       dprintk("lcdcon[5]= 0x%08lx\n", fbi->regs.lcdcon5);

 

       //向LCD控制寄存器中写新值

       writel(fbi->regs.lcdcon1& ~S3C2410_LCDCON1_ENVID,

              regs+ S3C2410_LCDCON1);

       writel(fbi->regs.lcdcon2,regs + S3C2410_LCDCON2);

       writel(fbi->regs.lcdcon3,regs + S3C2410_LCDCON3);

       writel(fbi->regs.lcdcon4,regs + S3C2410_LCDCON4);

       writel(fbi->regs.lcdcon5,regs + S3C2410_LCDCON5);

 

       /*set lcd address pointers */

       //设置帧缓冲地址寄存器

       s3c2410fb_set_lcdaddr(info);

 

       //使能LCD

       fbi->regs.lcdcon1|= S3C2410_LCDCON1_ENVID,

       writel(fbi->regs.lcdcon1,regs + S3C2410_LCDCON1);

}

 

我们再来看LCD的文件层程序fbmem.c(在drivers/video目录下),系统把帧缓冲设备看做一个字符设备,其操作函数集为:

static const struct file_operations fb_fops= {

       .owner= THIS_MODULE,

       .read=           fb_read,                //读

       .write=   fb_write,                      //写

       .unlocked_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

#ifdef CONFIG_FB_DEFERRED_IO

       .fsync=   fb_deferred_io_fsync,

#endif

       .llseek=  default_llseek,

};

 

在这里,我们只介绍帧缓冲控制函数fb_ioctl,在该函数中,主要调用了do_fb_ioctl函数,了解了该函数对写应用程序很有帮助:

static long do_fb_ioctl(struct fb_info*info, unsigned int cmd,

                     unsignedlong arg)

{

       structfb_ops *fb;

       structfb_var_screeninfo var;

       structfb_fix_screeninfo fix;

       structfb_con2fbmap con2fb;

       structfb_cmap cmap_from;

       structfb_cmap_user cmap;

       structfb_event event;

       void__user *argp = (void __user *)arg;

       longret = 0;

 

       switch(cmd) {

       caseFBIOGET_VSCREENINFO:                      //得到可变参数数据

              if(!lock_fb_info(info))

                     return -ENODEV;

              var = info->var;

              unlock_fb_info(info);

 

              ret= copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;

              break;

       caseFBIOPUT_VSCREENINFO:               //设置可变参数数据

              if(copy_from_user(&var, argp, sizeof(var)))

                     return-EFAULT;

              if(!lock_fb_info(info))

                     return-ENODEV;

              console_lock();

              info->flags|= FBINFO_MISC_USEREVENT;

              //下面函数调用了s3c2410fb_check_var函数和s3c2410fb_set_par函数

              ret = fb_set_var(info, &var);

              info->flags &=~FBINFO_MISC_USEREVENT;

              console_unlock();

              unlock_fb_info(info);

              if(!ret && copy_to_user(argp, &var, sizeof(var)))

                     ret= -EFAULT;

              break;

       caseFBIOGET_FSCREENINFO:               //得到固定参数数据

              if(!lock_fb_info(info))

                     return-ENODEV;

              fix= info->fix;

              unlock_fb_info(info);

 

              ret= copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;

              break;

       caseFBIOPUTCMAP:                               //设置颜色表

              if(copy_from_user(&cmap, argp, sizeof(cmap)))

                     return-EFAULT;

              //下面函数调用了s3c2410fb_setcolreg函数

              ret= fb_set_user_cmap(&cmap, info);

              break;

       caseFBIOGETCMAP:                               //得到颜色表

              if(copy_from_user(&cmap, argp, sizeof(cmap)))

                     return-EFAULT;

              if(!lock_fb_info(info))

                     return-ENODEV;

              cmap_from= info->cmap;

              unlock_fb_info(info);

              ret= fb_cmap_to_user(&cmap_from, &cmap);

              break;

       caseFBIOPAN_DISPLAY:                         //PAN显示

              if(copy_from_user(&var, argp, sizeof(var)))

                     return-EFAULT;

              if(!lock_fb_info(info))

                     return-ENODEV;

              console_lock();

              ret= fb_pan_display(info, &var);

              console_unlock();

              unlock_fb_info(info);

              if(ret == 0 && copy_to_user(argp, &var, sizeof(var)))

                     return-EFAULT;

              break;

       caseFBIO_CURSOR:                         //光标

              ret= -EINVAL;

              break;

       caseFBIOGET_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.data = &con2fb;

              if(!lock_fb_info(info))

                     return-ENODEV;

              event.info= info;

              fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP,&event);

              unlock_fb_info(info);

              ret= copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;

              break;

       caseFBIOPUT_CON2FBMAP:

              if(copy_from_user(&con2fb, argp, sizeof(con2fb)))

                     return -EFAULT;

              if (con2fb.console < 1 ||con2fb.console > MAX_NR_CONSOLES)

                     return -EINVAL;

              if (con2fb.framebuffer < 0 ||con2fb.framebuffer >= FB_MAX)

                     return-EINVAL;

              if(!registered_fb[con2fb.framebuffer])

                     request_module("fb%d",con2fb.framebuffer);

              if(!registered_fb[con2fb.framebuffer]) {

                     ret= -EINVAL;

                     break;

              }

              event.data= &con2fb;

              if(!lock_fb_info(info))

                     return-ENODEV;

              event.info= info;

              ret= fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);

              unlock_fb_info(info);

              break;

       caseFBIOBLANK:                             //显示空白

              if(!lock_fb_info(info))

                     return-ENODEV;

              console_lock();

              info->flags|= FBINFO_MISC_USEREVENT;

              //下面的函数调用了s3c2410fb_blank函数

              ret = fb_blank(info, arg);

              info->flags &=~FBINFO_MISC_USEREVENT;

              console_unlock();

              unlock_fb_info(info);

              break;

       default:

              if(!lock_fb_info(info))

                     return-ENODEV;

              fb= info->fbops;

              if(fb->fb_ioctl)

                     ret= fb->fb_ioctl(info, cmd, arg);

              else

                     ret= -ENOTTY;

              unlock_fb_info(info);

       }

       returnret;

}

 

最后,我们给出一段完整的对LCD操作的应用程序:

#include<unistd.h>

#include<stdio.h>

#include<fcntl.h>

#include<linux/fb.h>

#include<sys/mman.h>

int  main(void)

{

   int fd_fb = 0;

   struct fb_var_screeninfo vinfo;

   struct fb_fix_screeninfo finfo;

   long int screen_size = 0;

   unsigned long  *fbp32 = NULL;

 

   int x = 0, y = 0;

 

   fd_fb = open("/dev/fb0", O_RDWR);

   if (!fd_fb)

   {

       printf("Error: cannot open framebuffer device.\n");

       return(1);

   }

 

   // Get fixed screen info

   if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &finfo))

   {

       printf("Error reading fixed information.\n");

       return(2);

}

 

// Get variable screen info

   if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &vinfo))

   {

       printf("Error 2 reading variable information.\n");

       return(3);

   }

 

   // the size of the screen in bytes

   screen_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

 

   printf("%dx%d, %dbpp, screen_size = %d\n", vinfo.xres, vinfo.yres,vinfo.bits_per_pixel, screen_size );

 

   // map framebuffer to user memory

   fbp32 = (unsigned long *)mmap(0, screen_size, PROT_READ | PROT_WRITE,MAP_SHARED, fd_fb, 0);

 

   if ((int)fbp32 == -1)

   {

       printf("Error: failed to map framebuffer device tomemory.\n");

       return(4);

   }

 

   if(vinfo.bits_per_pixel == 32)

   {

       printf("24 bpp framebuffer\n");

 

       // Red Screen

       printf("Red Screen\n");

       for(y = 0; y < vinfo.yres;  y++)

        {

            for(x = 0; x < vinfo.xres; x++)

            {

                *(fbp32 + y *vinfo.xres + x) = 0xff0000;

            }

        }

    }    

    else

    {

        printf("warnning: bpp is not24\n");

   }

 

   munmap(fbp32, screen_size);

   close(fd_fb);

   return 0;

}

 

上面的程序使LCD全为红色。需要说明的是该程序只针对BPP为24的情况。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值