嵌入式linux mono,嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解

static struct fb_ops my2440fb_ops =

{

.owner = THIS_MODULE,

.fb_check_var = my2440fb_check_var,

.fb_set_par = my2440fb_set_par,

.fb_blank = my2440fb_blank,

.fb_setcolreg = my2440fb_setcolreg,

.fb_fillrect = cfb_fillrect,

.fb_copyarea = cfb_copyarea,

.fb_imageblit = cfb_imageblit,

};

static int my2440fb_set_par(struct fb_info *fbinfo)

{

struct fb_var_screeninfo *var = &fbinfo->var;

switch (var->bits_per_pixel)

{

case 32:

case 16:

case 12:

fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;

break;

case 1:

fbinfo->fix.visual = FB_VISUAL_MONO01;

break;

default:

fbinfo->fix.visual = FB_VISUAL_PSEUDOCOLOR;

break;

}

fbinfo->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

my2440fb_activate_var(fbinfo);

return 0;

}

static void my2440fb_activate_var(struct fb_info *fbinfo)

{

struct my2440fb_var *fbvar = fbinfo->par;

void __iomem *regs = fbvar->lcd_base;

struct fb_var_screeninfo *var = &fbinfo->var;

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

int type = fbvar->regs.lcdcon1 & S3C2410_LCDCON1_TFT;

if (type == S3C2410_LCDCON1_TFT)

{

my2440fb_config_tft_lcd_regs(fbinfo, &fbvar->regs);

--clkdiv;

if (clkdiv < 0)

{

clkdiv = 0;

}

}

else

{

my2440fb_config_stn_lcd_regs(fbinfo, &fbvar->regs);

if (clkdiv < 2)

{

clkdiv = 2;

}

}

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

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

my2440fb_set_lcdaddr(fbinfo);

fbvar->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,

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

}

static unsigned int my2440fb_calc_pixclk(struct my2440fb_var *fbvar, unsigned longpixclk)

{

unsigned long clk = clk_get_rate(fbvar->lcd_clock);

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

div >>= 12;

do_div(div, 625 * 625UL * 625);

return div;

}

static void my2440fb_config_tft_lcd_regs(const struct fb_info *fbinfo, structs3c2410fb_hw *regs)

{

const struct my2440fb_var *fbvar = fbinfo->par;

const struct fb_var_screeninfo *var = &fbinfo->var;

switch (var->bits_per_pixel)

{

case 1:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;

break;

case 2:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;

break;

case 4:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;

break;

case 8:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;

regs->lcdcon5 |= S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_FRM565;

regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;

break;

case 16:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;

regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;

regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;

break;

case 32:

regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;

regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_HWSWP |S3C2410_LCDCON5_BPP24BL);

break;

default:

dev_err(fbvar->dev, "invalid bpp %d\n", var->bits_per_pixel);

}

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

}

static void my2440fb_config_stn_lcd_regs(const struct fb_info *fbinfo, structs3c2410fb_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;

}

switch (var->bits_per_pixel)

{

case 1:

regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;

break;

case 2:

regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;

break;

case 4:

regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;

break;

case 8:

regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;

hs *= 3;

break;

case 12:

regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;

hs *= 3;

break;

default:

dev_err(fbvar->dev, "invalid bpp %d\n", var->bits_per_pixel);

}

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

}

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

}

static int my2440fb_blank(int blank_mode, struct fb_info *fbinfo)

{

struct my2440fb_var *fbvar = fbinfo->par;

void __iomem *regs = fbvar->lcd_base;

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,unsignedblue,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;

void __iomem *lcd_irq_base = fbvar->lcd_base + S3C2410_LCDINTBASE;

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

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值