


open/read/write/ioctl/mmap 等設備操作介面。fb_ops 結構體),由廠商/開發者編寫。
struct fb_info *s3c_lcd = framebuffer_alloc(0, NULL);
// 固定參數
strcpy(s3c_lcd->fix.id, "mylcd");
s3c_lcd->fix.smem_len = 320*240*32/8; // 顯存大小
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s3c_lcd->fix.line_length = 240*4; // 一行位元組數
// 可變參數
s3c_lcd->var.xres = 240; s3c_lcd->var.yres = 320;
s3c_lcd->var.xres_virtual = 240; s3c_lcd->var.yres_virtual = 320;
s3c_lcd->var.bits_per_pixel = 32; // 實際 24bpp,但 2440 分配 32bit
s3c_lcd->var.red.offset = 16; s3c_lcd->var.red.length = 8;
s3c_lcd->var.green.offset = 8; s3c_lcd->var.green.length = 8;
s3c_lcd->var.blue.offset = 0; s3c_lcd->var.blue.length = 8;
s3c_lcd->var.activate = FB_ACTIVATE_NOW;
// 操作函數
s3c_lcd->fbops = &s3c_lcdfb_ops;
// 調色板
s3c_lcd->pseudo_palette = pseudo_palette;

gpbcon = ioremap(0x56000010, 8); gpbdat = gpbcon+1;
gpccon = ioremap(0x56000020, 4);
gpdcon = ioremap(0x56000030, 4);
gpgcon = ioremap(0x56000060, 4);
*gpccon = 0xaaaaaaaa; // VD[7:0], LCDVF[2:0], VM, VFRAME, VLINE, VCLK, LEND
*gpdcon = 0xaaaaaaaa; // VD[23:8]
*gpgcon |= (3<<8); // GPG4 = LCD_PWREN
0xaaaaaaaa = 二進位 1010...,將對應腳位設為 Function 2 (LCD 專用)。
建立寄存器結構體映射:
struct lcd_regs {
unsigned long lcdcon1, lcdcon2, lcdcon3, lcdcon4, lcdcon5;
unsigned long lcdsaddr1, lcdsaddr2, lcdsaddr3;
unsigned long redlut, greenlut, bluelut;
unsigned long reserved[9];
unsigned long dithmode, tpal;
unsigned long lcdintpnd, lcdsrcpnd, lcdintmsk, lpcsel;
};
static volatile struct lcd_regs* lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
// VCLK = HCLK / [(CLKVAL+1)×2] = 100MHz / [(7+1)×2] = 6.25MHz ≈ 6.39MHz (面板需求)
// MMODE=0, PNRMODE=11(TFT), BPPMODE=1101(24bpp), ENVID=0(稍後啟用)
lcd_regs->lcdcon1 = (7<<8) | (3<<5) | (0x0D<<1);
// VBPD=2, LINEVAL=319(320行), VFPD=2, VSPW=0(脈寬1)
lcd_regs->lcdcon2 = (2<<24) | (319<<14) | (2<<6) | (0<<0);
// HBPD=20, HOZVAL=239(240列), HFPD=10
lcd_regs->lcdcon3 = (20<<19) | (239<<8) | (10<<0);
// HSPW=10
lcd_regs->lcdcon4 = 10;
// HSYNC/VDEN 低電平有效,PWREN=0,LSB valid(高位字節不用)
lcd_regs->lcdcon5 = (0<<10) | (1<<9) | (1<<8) | (0<<12) | (0<<1) | (0<<0);
// 分配 DMA 可寫合併記憶體
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len,
&s3c_lcd->fix.smem_start, GFP_KERNEL);
// 設定 LCD DMA 起始/結束/行長度位址(單位:半字=2bytes)
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30); // 清高兩位
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
lcd_regs->lcdsaddr3 = (240*32/16); // 一行長度(半字)
lcd_regs->lcdcon1 |= (1<<0); // ENVID=1 啟用控制器
lcd_regs->lcdcon5 |= (1<<3); // PWREN=1 啟用面板電源
register_framebuffer(s3c_lcd);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
unsigned int blue, unsigned int transp, struct fb_info *info);
struct lcd_regs {
unsigned long lcdcon1, lcdcon2, lcdcon3, lcdcon4, lcdcon5;
unsigned long lcdsaddr1, lcdsaddr2, lcdsaddr3;
unsigned long redlut, greenlut, bluelut;
unsigned long reserved[9];
unsigned long dithmode, tpal;
unsigned long lcdintpnd, lcdsrcpnd, lcdintmsk, lpcsel;
};
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = s3c_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static struct fb_info *s3c_lcd;
static volatile unsigned long *gpbcon, *gpbdat, *gpccon, *gpdcon, *gpgcon;
static volatile struct lcd_regs* lcd_regs;
static u32 pseudo_palette[16];
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 int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
unsigned int blue, unsigned int transp, struct fb_info *info) {
if (regno > 16) return 1;
unsigned int val = 0;
val |= chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pseudo_palette[regno] = val;
return 0;
}
static int __init lcd_init(void) {
s3c_lcd = framebuffer_alloc(0, NULL);
// ... 設置 fix, var, fbops, pseudo_palette ...
// GPIO 映射與配置
gpbcon = ioremap(0x56000010, 8); gpbdat = gpbcon+1;
gpccon = ioremap(0x56000020, 4);
gpdcon = ioremap(0x56000030, 4);
gpgcon = ioremap(0x56000060, 4);
*gpccon = 0xaaaaaaaa; *gpdcon = 0xaaaaaaaa; *gpgcon |= (3<<8);
// LCD 控制器映射與配置
lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
lcd_regs->lcdcon1 = (7<<8) | (3<<5) | (0x0D<<1);
lcd_regs->lcdcon2 = (2<<24) | (319<<14) | (2<<6) | 0;
lcd_regs->lcdcon3 = (20<<19) | (239<<8) | 10;
lcd_regs->lcdcon4 = 10;
lcd_regs->lcdcon5 = (1<<9) | (1<<8);
// 分配顯存
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len,
&s3c_lcd->fix.smem_start, GFP_KERNEL);
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
lcd_regs->lcdsaddr3 = (240*32/16);
// 啟動
lcd_regs->lcdcon1 |= 1; lcd_regs->lcdcon5 |= (1<<3);
register_framebuffer(s3c_lcd);
return 0;
}
static void __exit lcd_exit(void) {
unregister_framebuffer(s3c_lcd);
lcd_regs->lcdcon1 &= ~1; lcd_regs->lcdcon5 &= ~(1<<3);
dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
iounmap(lcd_regs); iounmap(gpbcon); iounmap(gpccon); iounmap(gpdcon); iounmap(gpgcon);
framebuffer_release(s3c_lcd);
}
module_init(lcd_init); module_exit(lcd_exit);
MODULE_LICENSE("GPL");
LCDCON5 的 BPP24BL、BSWP、HWSWP 決定高位字節位置。LCDSADDR1/2/3 以半字(2bytes)為單位,物理位址右移 1 位。