/*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); } |