S3C2440上LCD驱动(FrameBuffer)实例开发讲解(三)

S3C2440上LCD驱动(FrameBuffer)实例开发讲解(三) 


转载地址:http://hi.baidu.com/fenhuashu/item/b3b05a93217808d71f4271ae

开发环境

主  机:VMWare--Fedora 9

开发板:Mini2440--64MB Nand, Kernel:2.6.30.4

编译器:arm-linux-gcc-4.3.2

上接:S3C2440上LCD驱动(FrameBuffer)实例开发详解(二)

四、帧缓冲(FrameBuffer)设备驱动实例代码:


③、帧缓冲设备驱动对底层硬件操作的函数接口实现(即: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,structfb_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显示开关(空白)的硬件接口函数等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值