Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1635283
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: LINUX

2010-08-19 11:43:32

③、 帧缓冲设备驱动对底层硬件操作的函数接口实现(即:my2440fb_ops的实现):

/*Framebuffer底层硬件操 作各接口函数*/
static struct fb_ops my2440fb_ops =
{
    . owner          = THIS_MODULE,
    . fb_check_var   = my2440fb_check_var, /*第②步中已实现*/
    . fb_set_par     = my2440fb_set_par, /*设置fb_info中的参数,主要是LCD的显示模式*/
    . fb_blank       = my2440fb_blank, /*显示空白(即:LCD开关控制)*/
    . fb_setcolreg   = my2440fb_setcolreg, /*设置颜色表*/
    /*以 下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
    . fb_fillrect    = cfb_fillrect, /*定义在 drivers/video/cfbfillrect.c中*/
    . fb_copyarea    = cfb_copyarea, /*定义在drivers/video/cfbcopyarea.c 中*/
    . fb_imageblit   = cfb_imageblit, /*定义在 drivers/video/cfbimgblt.c中*/
};

/*设置 fb_info中的参数,这里根据用户设置的可变参数var调整固定参数fix*/
static int my2440fb_set_par( struct fb_info * fbinfo)
{
    /*获得fb_info中的可变参数*/
    struct fb_var_screeninfo * var = & fbinfo- > var;

    /*判断可变参数中的色位模式,根据色位模式来设置色彩模式* /
    switch ( var- > bits_per_pixel)
    {
        case 32:
        case 16:
        case 12: /*12BPP时,设置为真彩色(分成红、绿、蓝三基色)*/
            fbinfo- > fix. visual = FB_VISUAL_TRUECOLOR;
            break ;
        case 1: /*1BPP时,设置为黑白色(分黑、白两种 色,FB_VISUAL_MONO01代表 黑,FB_VISUAL_MONO10代表白)*/
            fbinfo- > fix. visual = FB_VISUAL_MONO01;
            break ;
        default : /*默认设置为伪彩色,采用索引颜色显示*/
            fbinfo- > fix. visual = FB_VISUAL_PSEUDOCOLOR;
            break ;
    }

    /*设置fb_info中固定参数中一行的字节数,公式:1行 字节数=(1行像素 个数*每像素位数BPP)/8 */
    fbinfo- > fix. line_length = ( var- > xres_virtual * var- > bits_per_pixel) / 8;

    /*修改以上参数后,重新激活fb_info中的参数配置 (即:使修改后的参数在硬件上生效)*/
    my2440fb_activate_var( fbinfo) ;

    return 0;
}

/*重新激活fb_info中的参数配置*/
static void my2440fb_activate_var( struct fb_info * fbinfo)
{
    /*获得结构体变 量*/
    struct my2440fb_var * fbvar = fbinfo- > par;
    void __iomem * regs = fbvar- > lcd_base;

    /*获得fb_info可变参数*/
    struct fb_var_screeninfo * var = & fbinfo- > var;

    /*计算LCD控制寄存器1中的CLKVAL值, 根据数据手册中该寄存器的描述,计算公式如下:
    * STN屏:VCLK = HCLK / (CLKVAL * 2), CLKVAL要求>= 2
    * TFT屏:VCLK = HCLK / [(CLKVAL + 1) * 2], CLKVAL要求>= 0*/

    int clkdiv = my2440fb_calc_pixclk( fbvar, var- > pixclock) / 2;

    /*获得屏幕的类型*/
    int type = fbvar- > regs. lcdcon1 & S3C2410_LCDCON1_TFT;

    if ( type = = S3C2410_LCDCON1_TFT)
    {
        /*根据数据手册按照TFT屏的要求配置LCD控制寄 存器1-5*/
        my2440fb_config_tft_lcd_regs( fbinfo, & fbvar- > regs) ;

        - - clkdiv;

        if ( clkdiv < 0)
        {
            clkdiv = 0;
        }
    }
    else
    {
        /*根据数据手册按照STN屏的要求配置LCD控制寄 存器1-5*/
        my2440fb_config_stn_lcd_regs( fbinfo, & fbvar- > regs) ;

        if ( clkdiv < 2)
        {
            clkdiv = 2;
        }
    }

    /*设置计算的LCD控制寄存器1中的CLKVAL值*/
    fbvar- > regs. lcdcon1 | = S3C2410_LCDCON1_CLKVAL( clkdiv) ;

    /*将各参数 值写入LCD控制寄存器1-5中*/
    writel( fbvar- > regs. lcdcon1 & ~ S3C2410_LCDCON1_ENVID, regs + S3C2410_LCDCON1) ;
    writel( fbvar- > regs. lcdcon2, regs + S3C2410_LCDCON2) ;
    writel( fbvar- > regs. lcdcon3, regs + S3C2410_LCDCON3) ;
    writel( fbvar- > regs. lcdcon4, regs + S3C2410_LCDCON4) ;
    writel( fbvar- > regs. lcdcon5, regs + S3C2410_LCDCON5) ;

    /*配置帧缓冲起始地址寄存器1-3*/
    my2440fb_set_lcdaddr( fbinfo) ;

    fbvar- > regs. lcdcon1 | = S3C2410_LCDCON1_ENVID,
    writel( fbvar- > regs. lcdcon1, regs + S3C2410_LCDCON1) ;
}

/*计算LCD 控制寄存器1中的CLKVAL值*/
static unsigned int my2440fb_calc_pixclk( struct my2440fb_var * fbvar, unsigned long pixclk)
{
    /*获得LCD的时钟*/
    unsigned long clk = clk_get_rate( fbvar- > lcd_clock) ;

    /* 像素时钟单位是皮秒,而时钟的单位是赫兹,所以计算公式为:
     * Hz -> picoseconds is / 10^-12
     */

    unsigned long long div = ( unsigned long long ) clk * pixclk;

    div > > = 12;             /* div / 2^12 */
    do_div( div , 625 * 625UL * 625) ; /* div / 5^12, do_div宏定义在asm/div64.h中*/

    return div ;
}

/*根据数据手册按照TFT屏的要求配置LCD控制寄存器1-5*/
static void my2440fb_config_tft_lcd_regs( const struct fb_info * fbinfo, struct s3c2410fb_hw * regs)
{
    const struct my2440fb_var * fbvar = fbinfo- > par;
    const struct fb_var_screeninfo * var = & fbinfo- > var;

    /*根据色位模式设置LCD控制寄存器1和5,参考数据手册* /
    switch ( var- > bits_per_pixel)
    {
        case 1: /*1BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT1BPP;
            break ;
        case 2: /*2BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT2BPP;
            break ;
        case 4: /*4BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT4BPP;
            break ;
        case 8: /*8BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT8BPP;
            regs- > lcdcon5 | = S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_FRM565;
            regs- > lcdcon5 & = ~ S3C2410_LCDCON5_HWSWP;
            break ;
        case 16: /*16BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT16BPP;
            regs- > lcdcon5 & = ~ S3C2410_LCDCON5_BSWP;
            regs- > lcdcon5 | = S3C2410_LCDCON5_HWSWP;
            break ;
        case 32: /*32BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_TFT24BPP;
            regs- > lcdcon5 & = ~ ( S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_HWSWP | S3C2410_LCDCON5_BPP24BL) ;
            break ;
        default : /*无效的BPP*/
            dev_err( fbvar- > dev, "invalid bpp %d\n" , var- > bits_per_pixel) ;
    }

    /*设置LCD配置寄存器2、3、4*/
    regs- > lcdcon2 = S3C2410_LCDCON2_LINEVAL( var- > yres - 1) |
            S3C2410_LCDCON2_VBPD( var- > upper_margin - 1) |
            S3C2410_LCDCON2_VFPD( var- > lower_margin - 1) |
            S3C2410_LCDCON2_VSPW( var- > vsync_len - 1) ;

    regs- > lcdcon3 = S3C2410_LCDCON3_HBPD( var- > right_margin - 1) |
            S3C2410_LCDCON3_HFPD( var- > left_margin - 1) |
            S3C2410_LCDCON3_HOZVAL( var- > xres - 1) ;

    regs- > lcdcon4 = S3C2410_LCDCON4_HSPW( var- > hsync_len - 1) ;
}

/*根据数据手册按照STN屏的要求配置LCD控制寄存器1-5*/
static void my2440fb_config_stn_lcd_regs( const struct fb_info * fbinfo, struct s3c2410fb_hw * regs)
{
    const struct my2440fb_var    * fbvar = fbinfo- > par;
    const struct fb_var_screeninfo * var = & fbinfo- > var;

    int type = regs- > lcdcon1 & ~ S3C2410_LCDCON1_TFT;
    int hs = var- > xres > > 2;
    unsigned wdly = ( var- > left_margin > > 4) - 1;
    unsigned wlh = ( var- > hsync_len > > 4) - 1;

    if ( type ! = S3C2410_LCDCON1_STN4)
    {
        hs > > = 1;
    }

    /*根据色位模式设置LCD控制寄存器1,参考数据手册*/
    switch ( var- > bits_per_pixel)
    {
        case 1: /*1BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_STN1BPP;
            break ;
        case 2: /*2BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_STN2GREY;
            break ;
        case 4: /*4BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_STN4GREY;
            break ;
        case 8: /*8BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_STN8BPP;
            hs * = 3;
            break ;
        case 12: /*12BPP*/
            regs- > lcdcon1 | = S3C2410_LCDCON1_STN12BPP;
            hs * = 3;
            break ;
        default : /*无效的BPP*/
            dev_err( fbvar- > dev, "invalid bpp %d\n" , var- > bits_per_pixel) ;
    }
    
    /*设置LCD配置寄存器2、3、4, 参考数据手册*/
    if ( wdly > 3) wdly = 3;
    if ( wlh > 3) wlh = 3;
    regs- > lcdcon2 = S3C2410_LCDCON2_LINEVAL( var- > yres - 1) ;

    regs- > lcdcon3 =  S3C2410_LCDCON3_WDLY( wdly) |
            S3C2410_LCDCON3_LINEBLANK( var- > right_margin / 8) |
            S3C2410_LCDCON3_HOZVAL( hs - 1) ;

    regs- > lcdcon4 = S3C2410_LCDCON4_WLH( wlh) ;
}

/*配置帧缓冲起始地址寄存器1-3,参考数据手册*/
static void my2440fb_set_lcdaddr( struct fb_info * fbinfo)
{
    unsigned long saddr1, saddr2, saddr3;
    struct my2440fb_var * fbvar = fbinfo- > par;
    void __iomem * regs = fbvar- > lcd_base;

    saddr1 = fbinfo- > fix. smem_start > > 1;
    saddr2 = fbinfo- > fix. smem_start;
    saddr2 + = fbinfo- > fix. line_length * fbinfo- > var. yres;
    saddr2 > > = 1;
    saddr3 = S3C2410_OFFSIZE( 0) | S3C2410_PAGEWIDTH( ( fbinfo- > fix. line_length / 2) & 0x3ff) ;

    writel( saddr1, regs + S3C2410_LCDSADDR1) ;
    writel( saddr2, regs + S3C2410_LCDSADDR2) ;
    writel( saddr3, regs + S3C2410_LCDSADDR3) ;
}

/*显示空白,blank mode有5种模式,定义在fb.h中,是一个枚举*/
static int my2440fb_blank( int blank_mode, struct fb_info * fbinfo)
{
    struct my2440fb_var * fbvar = fbinfo- > par;
    void __iomem * regs = fbvar- > lcd_base;

    /*根据显示空白的模式来设置LCD是开启还是停止*/
    if ( blank_mode = = FB_BLANK_POWERDOWN)
    {
        my2440fb_lcd_enable( fbvar, 0) ; /*在第②步中定义*/
    }
    else
    {
        my2440fb_lcd_enable( fbvar, 1) ; /*在第②步中定义*/
    }

    /*根据显示空白的模式来控制临时调色板寄存器*/
    if ( blank_mode = = FB_BLANK_UNBLANK)
    {
        /*临时调色板寄存器无效*/
        writel( 0x0, regs + S3C2410_TPAL) ;
    }
    else
    {
        /*临时调色 板寄存器有效*/
        writel( S3C2410_TPAL_EN, regs + S3C2410_TPAL) ;
    }

    return 0;
}

/*设置颜色表*/
static int my2440fb_setcolreg( unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info * fbinfo)
{
    unsigned int val;
    struct my2440fb_var * fbvar = fbinfo- > par;
    void __iomem * regs = fbvar- > lcd_base;

    switch ( fbinfo- > fix. visual)
    {
        case FB_VISUAL_TRUECOLOR:
            /* 真彩色*/
            if ( regno < 16)
            {
                u32 * pal = fbinfo- > pseudo_palette;

                val = chan_to_field( red, & fbinfo- > var. red) ;
                val | = chan_to_field( green, & fbinfo- > var. green) ;
                val | = chan_to_field( blue, & fbinfo- > var. blue) ;

                pal[ regno] = val;
            }
            break ;
        case FB_VISUAL_PSEUDOCOLOR:
            /*伪彩色*/
            if ( regno < 256)
            {
                val = ( red > > 0) & 0xf800;
                val | = ( green > > 5) & 0x07e0;
                val | = ( blue > > 11) & 0x001f;

                writel( val, regs + S3C2410_TFTPAL( regno) ) ;

                /*修改调色板*/
                schedule_palette_update( fbvar, regno, val) ;
            }
            break ;
        default :
            return 1;
    }

    return 0;
}

static inline unsigned int chan_to_field( unsigned int chan, struct fb_bitfield * bf)
{
    chan & = 0xffff;
    chan > > = 16 - bf- > length;
    return chan < < bf- > offset;
}

/*修改调色 板*/
static void schedule_palette_update( struct my2440fb_var    * fbvar, unsigned int regno, unsigned int val)
{
    unsigned long flags;
    unsigned long irqen;

    /*LCD中断挂起寄存器基地址*/
    void __iomem * lcd_irq_base = fbvar- > lcd_base + S3C2410_LCDINTBASE;

    /*在修改中断寄存器值之前先屏蔽中断,将中断状态保存到 flags中*/
    local_irq_save( flags) ;

    fbvar- > palette_buffer[ regno] = val;

    /*判断调色板是否准备就像*/
    if ( ! fbvar- > palette_ready)
    {
        fbvar- > palette_ready = 1;

        /*使能中断屏蔽寄存器*/
        irqen = readl( lcd_irq_base + S3C24XX_LCDINTMSK) ;
        irqen & = ~ S3C2410_LCDINT_FRSYNC;
        writel( irqen, lcd_irq_base + S3C24XX_LCDINTMSK) ;
    }

    /*恢复被屏蔽的中断*/
    local_irq_restore( flags) ;
}

 

五、从整体上再描述一 下FrameBuffer设备驱动实例代码的结构:
 
1、在第①部分代码中主要做的事情有:
   a.将LCD设备注册到系统平台设备中;
   b.定义LCD平台设备结构体lcd_fb_driver。
 
2、在第②部分代码中主要做的事情有:
   a.获取和设置LCD平台设备的各种资源;
   b.分配fb_info结构体空间;
   c.初始化fb_info结构体中的各参数;
   d.初始化LCD控制器;
   e.检查fb_info中可变参数;
   f.申请帧缓冲设备的显示缓冲区空间;
   g.注册fb_info。
 
3、在第 部 分代码中主要做的事情有:
   a.实现对fb_info相关参数进行检查的硬件接口函数;
   b.实现对LCD显示模式进行设定的硬件接口函数;
   c.实现对LCD显示开关(空白)的硬件接口函数等。
阅读(789) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~