首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

LCD驱动实例开发(3)

LCD驱动实例开发(3)




/*重新激活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显示开关(空白)的硬件接口函数等。



            void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
    struct s3c2410fb_mach_info *npd;

    npd = kmalloc(sizeof(*npd), GFP_KERNEL);
    if (npd) {
        memcpy(npd, pd, sizeof(*npd));

                    //这里就是将内核中定义的s3c2410fb_mach_info结构体数据保存到LCD平台数据中,所以在写驱动的时候就可以直接在平台数据中获取s3c2410fb_mach_info结构体的数据(即LCD各种参数信息)进行操作
        s3c_device_lcd.dev.platform_data = npd;
    } else {
        printk(KERN_ERR "no memory for LCD platform data/n");
    }
}

            

            //S3C2440初始化函数
static void __init smdk2440_machine_init(void)
{

                //调用该函数将上面定义的LCD硬件信息保存到平台数据中
    s3c24xx_fb_set_platdata(&smdk2440_fb_info);
   
    s3c_i2c0_set_platdata(NULL);

    platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
    smdk_machine_init();
}

            

            /* LCD Controller */
            //LCD控制器的资源信息
static struct resource s3c_lcd_resource[] = {
    [0] = {
        .start = S3C24XX_PA_LCD
, //控制器IO端口开始地址
        .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,//控制器IO端口结束地址
        .flags = IORESOURCE_MEM,//标识为LCD控制器IO端口,在驱动中引用这个就表示引用IO端口
    },
    [1] = {
        .start = IRQ_LCD
,//LCD中断
        .end = IRQ_LCD,
        .flags = IORESOURCE_IRQ
,//标识为LCD中断
    }
};

static u64 s3c_device_lcd_dmamask = 0xffffffffUL;

struct platform_device s3c_device_lcd = {
    .name         = "s3c2410-lcd"
,//作为平台设备的LCD设备名
    .id         = -1,
    .num_resources = ARRAY_SIZE(s3c_lcd_resource)
,//资源数量
    .resource     = s3c_lcd_resource,//引用上面定义的资源
    .dev = {
        .dma_mask = &s3c_device_lcd_dmamask,
        .coherent_dma_mask = 0xffffffffUL
    }
};

EXPORT_SYMBOL(s3c_device_lcd)
;//导出定义的LCD平台设备,好在mach-smdk2440.c的smdk2440_devices[]中添加到平台设备列表中

            

            struct fb_info {
    int node;
    int flags;
    struct fb_var_screeninfo var;/*LCD可变参数结构体*/
    struct fb_fix_screeninfo fix;/*LCD固定参数结构体*/
    struct fb_monspecs monspecs; /*LCD显示器标准*/
    struct work_struct queue;    /*帧缓冲事件队列*/
    struct fb_pixmap pixmap;     /*图像硬件mapper*/
    struct fb_pixmap sprite;     /*光标硬件mapper*/
    struct fb_cmap cmap;         /*当前的颜色表*/
    struct fb_videomode *mode;   /*当前的显示模式*/

#ifdef CONFIG_FB_BACKLIGHT
   
struct backlight_device *bl_dev;/*对应的背光设备*/
    struct mutex bl_curve_mutex;
    u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    struct delayed_work deferred_work;
    struct fb_deferred_io *fbdefio;
#endif

    struct fb_ops *fbops; /*对底层硬件操作的函数指针*/
    struct device *device;
    struct device *dev;   /*fb设备*/
    int class_flag;   
#ifdef CONFIG_FB_TILEBLITTING
    struct fb_tile_ops *tileops; /*图块Blitting*/
#endif
    char __iomem *screen_base;   /*虚拟基地址*/
    unsigned long screen_size;   /*LCD IO映射的虚拟内存大小*/
    void *pseudo_palette;        /*伪16色颜色表*/
#define FBINFO_STATE_RUNNING    0
#define FBINFO_STATE_SUSPENDED  1
    u32 state;  /*LCD的挂起或恢复状态*/
    void *fbcon_par;
    void *par;   
};

            
其中,比较重要的成员有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和struct fb_ops *fbops,他们也都是结构体。下面我们一个一个的来看。
fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数,比如屏幕的分辨率和每个像素的比特数等,该结构体定义如下:

而fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数,比如屏幕缓冲区的物理地址和长度等,该结构体的定义如下:


            struct fb_fix_screeninfo {
    char id[16];                /*字符串形式的标示符 */
    unsigned long smem_start;   /*fb缓存的开始位置 */
    __u32 smem_len;             /*fb缓存的长度 */
    __u32 type;                 /*看FB_TYPE_* */
    __u32 type_aux;             /*分界*/
    __u32 visual;               /*看FB_VISUAL_* */
    __u16 xpanstep;             /*如果没有硬件panning就赋值为0 */
    __u16 ypanstep;             /*如果没有硬件panning就赋值为0 */
    __u16 ywrapstep;            /*如果没有硬件ywrap就赋值为0 */
    __u32 line_length;          /*一行的字节数 */
    unsigned long mmio_start;   /*内存映射IO的开始位置*/
    __u32 mmio_len;             /*内存映射IO的长度*/
    __u32 accel;
    __u16 reserved[3];          /*保留*/
};

            

            struct fb_var_screeninfo {
    __u32 xres;                /*可见屏幕一行有多少个像素点*/
    __u32 yres;                /*可见屏幕一列有多少个像素点*/
    __u32 xres_virtual;        /*虚拟屏幕一行有多少个像素点*/        
    __u32 yres_virtual;        /*虚拟屏幕一列有多少个像素点*/
    __u32 xoffset;             /*虚拟到可见屏幕之间的行偏移*/
    __u32 yoffset;             /*虚拟到可见屏幕之间的列偏移*/
    __u32 bits_per_pixel;      /*每个像素的位数即BPP*/
    __u32 grayscale;           /*非0时,指的是灰度*/

    struct fb_bitfield red;    /*fb缓存的R位域*/
    struct fb_bitfield green;  /*fb缓存的G位域*/
    struct fb_bitfield blue;   /*fb缓存的B位域*/
    struct fb_bitfield transp; /*透明度*/   

    __u32 nonstd;              /* != 0 非标准像素格式*/
    __u32 activate;               
    __u32 height;              /*高度*/
    __u32 width;               /*宽度*/
    __u32 accel_flags;   

    /*定时:除了pixclock本身外,其他的都以像素时钟为单位*/
    __u32 pixclock;            /*像素时钟(皮秒)*/
    __u32 left_margin;         /*行切换,从同步到绘图之间的延迟*/
    __u32 right_margin;        /*行切换,从绘图到同步之间的延迟*/
    __u32 upper_margin;        /*帧切换,从同步到绘图之间的延迟*/
    __u32 lower_margin;        /*帧切换,从绘图到同步之间的延迟*/
    __u32 hsync_len;           /*水平同步的长度*/
    __u32 vsync_len;           /*垂直同步的长度*/
    __u32 sync;
    __u32 vmode;
    __u32 rotate;
    __u32 reserved[5];         /*保留*/
};

            
返回列表