2.填寫framebuffer資料,可以先填寫fix的資料,var的資料我們可以單獨宣告一組structure與其對應,其中比較重要的欄位如fbops,就是我們前面提到的callback函式,fb_set_par和fb_fillrect, fb_copyarea, fb_imageblit一定要有相對應的方法處理,而pseudo_palette也十分重要,16 bit TFT需要使用這塊pseudo_palette儲存pixel color(如果fbinfo->fix.visual選擇的是FB_VISUAL_TRUECOLOR,那你一定得用pseudo_palette,否則開機就當了), fbinfo->par可儲存driver private data
3.fbinfo->var的部份最重要的地方在於
.pixclock = 1000000,
.left_margin = 30,//HFPD 30
.right_margin = 6,//HBPD 6
.upper_margin = 2,//VFPD 2
.lower_margin = 1,//VBPD 1
.vsync_len = 1,//VSPW 1
.hsync_len = 3,//HSPW 3
這些值怎麼來的?先看看它們各自的意義
pixclock - pixel clock in pico seconds
left_margin - time fron sync to picture對應HFPD
right_margin - time from picture to sync對應HBPD
upper_margin - time from sync to picture對應VFPD
lower_margin - time from picture to sync對應VBPD
hsync_len - length of horizontal sync對應HSPW
vsync_len - length of vertical sync對應VSPW
在Samsung 2410 user manual LCD section內有一幅重要的圖如下,它詳細說明了如何算出這些sync值

我翻閱了NEC LCD的相關說明,我還是無法將其Timing chart與上面這張對應,不過後來查閱Samsung Portrait LCD發現其Timing chart 100% match 2410 user manual的圖型,請參考下面圖示

從這張我們可以算出240*320的LCD參數大概可以設為
VSPW+1=2
VBPD+1=2
VFPD+1=3
而透過Honzital Timing chart可算出HSPW,HPBD,HFPD,請參考下圖

所以我把這些值直接拿來用,好像是可以動的,而至於pixclock的算法是
MHZ/DCF(CLKVAL),因為我的CLKVAL divisor為1,所以pixclock為1000000
4.S3C2410 LCD controller的部份直接照說明書填就可以了,比較特殊的是LCD controller5的部份,要小心byteswap的設定,而LCD address只要把一開始allocate的dma memory位址指派給LCD address register即可(一共三個,照manual填),底下是我的範例code
[qt2410_fb.c]
- #include "qt2410fb.h"
-
- static u_int pseudo_pal[16];
-
- struct lcdregs
- {
- void __iomem *lcdcon1;
- void __iomem *lcdcon2;
- void __iomem *lcdcon3;
- void __iomem *lcdcon4;
- void __iomem *lcdcon5;
- void __iomem *lcdsaddr1;
- void __iomem *lcdsaddr2;
- void __iomem *lcdsaddr3;
- void __iomem *gpioccon;
- void __iomem *gpiodcon;
- void __iomem *gpiocup;
- void __iomem *gpiodup;
- void __iomem *lpcsel;
- void __iomem *tmppal;
- void __iomem *pal;
- };
-
- typedef struct _driver_private_data
- {
- struct device *dev;
- struct lcdregs regs;
- struct clk *clk;//2410 specified structure clock
-
- }driver_private_data;
-
- static struct fb_var_screeninfo qt2410fb_var __initdata = {
- .xres = 240,
- .yres = 320,
- .xres_virtual =240,
- .yres_virtual = 320,
- .bits_per_pixel = 16,
- .red = {11, 5, 0},
- .green = {5, 6, 0},
- .blue = {0, 5, 0},
- .activate = FB_ACTIVATE_NOW,
- .height = 320,
- .width = 240,
- .vmode = FB_VMODE_NONINTERLACED,
-
-
- .pixclock = 1000000,
- .left_margin = 30,//HFPD 30
- .right_margin = 6,//HBPD 6
- .upper_margin = 2,//VFPD 2
- .lower_margin = 1,//VBPD 1
- .vsync_len = 1,//VSPW 1
- .hsync_len = 3,//HSPW 3
- };
-
-
- static inline unsigned int chan_to_field(unsigned int chan,
- struct fb_bitfield *bf)
- {
- chan &= 0xffff;
- chan >>= 16 - bf->length;
- return chan << bf->offset;
- }
-
- int Phy2VM(unsigned long phyaddr,char *name,void __iomem **vaddr)
- {
- if (!request_mem_region(phyaddr, 4, name))
- return 0;
- *vaddr= ioremap(phyaddr, 4);
- if (*vaddr==0) return 0;
- else printk("[qt2410fb]%s virtual address:0x%p\n",name,*vaddr);
-
- return 1;
- }
-
- void reqREGmem(struct lcdregs *regs)
- {
- Phy2VM(LCDCON1,"LCDCON1",®s->lcdcon1);
- Phy2VM(LCDCON2,"LCDCON2",®s->lcdcon2);
- Phy2VM(LCDCON3,"LCDCON3",®s->lcdcon3);
- Phy2VM(LCDCON4,"LCDCON4",®s->lcdcon4);
- Phy2VM(LCDCON5,"LCDCON5",®s->lcdcon5);
- Phy2VM(GPCCON,"GPCCON",®s->gpioccon);
- Phy2VM(GPDCON,"GPDCON",®s->gpiodcon);
- Phy2VM(GPCUP,"GPCUP",®s->gpiocup);
- Phy2VM(GPDUP,"GPDUP",®s->gpiodup);
- Phy2VM(LPCSEL,"LPCSEL",®s->lpcsel);
- Phy2VM(TPAL,"TPAL",®s->tmppal);
- Phy2VM(LCDSADDR1,"LCDSADDR1",®s->lcdsaddr1);
- Phy2VM(LCDSADDR2,"LCDSADDR2",®s->lcdsaddr2);
- Phy2VM(LCDSADDR3,"LCDSADDR3",®s->lcdsaddr3);
- Phy2VM(PALETTE,"PALETTE",®s->pal);
- }
-
- void initRegs(struct lcdregs *regs)
- {
- DEBUG_TRACE();
- (*(volatile unsigned *)regs->gpioccon)|=0xaaaaaaaa;
- (*(volatile unsigned *)regs->gpiodcon)|=0xaaaaaaaa;
- (*(volatile unsigned *)regs->gpiocup)|=0xffffffff;
- (*(volatile unsigned *)regs->gpiodup)|=0xffffffff;
- (*(volatile unsigned *)regs->lpcsel)&=(~7);
- (*(volatile unsigned *)regs->tmppal)&=0;
- }
-
-
- static int qt2410fb_blank(int blank, struct fb_info *info)
- {
- DEBUG_TRACE();
- return 0;
- }
-
-
- static int qt2410fb_check_var(struct fb_var_screeninfo *var,struct fb_info *info)
- {
- DEBUG_TRACE();
- return 0;
- }
- static int qt2410fb_set_par(struct fb_info *fbinfo)
- {
- driver_private_data *dpd=fbinfo->par;
- struct lcdregs *regs=&dpd->regs;
- unsigned long saddr1, saddr2, saddr3;
- u_int* palette;
- int i;
-
- DEBUG_TRACE();
- fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
- (*(volatile unsigned *)regs->lcdcon1)=(4<<8)|(0<<7)|(3<<5)|(12<<1)|0;
- (*(volatile unsigned *)regs->lcdcon2)=(VBPD<<24)|(319<<14)|(VFPD<<6)|(VSPW);
- (*(volatile unsigned *)regs->lcdcon3)=(HBPD<<19)|(239<<8)|(HFPD);
- (*(volatile unsigned *)regs->lcdcon4)=(0<<8)|(HSPW);
- (*(volatile unsigned *)regs->lcdcon5)=(1<<11)|(1<<9)|(1<<8)|1<<0; //FRM5:6:5,HSYNC and VSYNC are inverted,turn on BSWP
- (*(volatile unsigned *)regs->lcdcon5)&= ~S3C2410_LCDCON5_BSWP;
- fbinfo->fix.line_length = (fbinfo->var.xres_virtual * fbinfo->var.bits_per_pixel) / 8;
- 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);
- (*(volatile unsigned *)regs->lcdsaddr1)=saddr1;
- (*(volatile unsigned *)regs->lcdsaddr2)=saddr2;
- (*(volatile unsigned *)regs->lcdsaddr3)=saddr3;
- (*(volatile unsigned *)regs->lcdcon1)|=1;
- return 0;
- }
-
- static int qt2410fb_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- unsigned int val;
-
- printk("regno %d, fix.visual:%d\n",regno,info->fix.visual);
- if (info->fix.visual==FB_VISUAL_TRUECOLOR)
- {
- if (regno < 16)
- {
- u32 *pal = info->pseudo_palette;
-
- val = chan_to_field(red, &info->var.red);
- val |= chan_to_field(green, &info->var.green);
- val |= chan_to_field(blue, &info->var.blue);
-
- pal[regno] = val;
- }
- }
- return 0;
- }
-
- static struct fb_ops qt2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = qt2410fb_check_var,
- .fb_set_par = qt2410fb_set_par,
- .fb_setcolreg = qt2410fb_setcolreg,
- .fb_blank = qt2410fb_blank,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
-
- static int __init qt2410fb_map_video_memory(struct fb_info *info)
- {
- dma_addr_t map_dma;
- unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
- driver_private_data *dpd;
- DEBUG_TRACE();
-
- dpd=info->par;
- info->screen_base = dma_alloc_writecombine(dpd->dev, map_size,&map_dma, GFP_KERNEL);
- if (info->screen_base)
- {
- info->fix.smem_start = map_dma;
- DEBUG_TRACE();
- return 0;
- }
- else return -ENOMEM;
-
- }
- static inline void qt2410fb_unmap_video_memory(struct fb_info *info)
- {
- driver_private_data *dpd;
-
- dpd=info->par;
- dma_free_writecombine(dpd->dev, PAGE_ALIGN(info->fix.smem_len),info->screen_base, info->fix.smem_start);
- }
- static int __init qt2410fb_probe(struct platform_device *pdev)
- {
-
- int ret;
- struct fb_info *fbinfo;
- driver_private_data *dpd;
-
- DEBUG_TRACE();
- fbinfo = framebuffer_alloc(sizeof(driver_private_data), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
- platform_set_drvdata(pdev, fbinfo);
-
- strcpy(fbinfo->fix.id,"qt2410");
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->fbops = &qt2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->var=qt2410fb_var;//set variable
- fbinfo->fix.smem_len = ((fbinfo->var.xres)*(fbinfo->var.yres)*(fbinfo->var.bits_per_pixel))/8;
- fbinfo->pseudo_palette=&pseudo_pal;
- dpd=fbinfo->par;
- dpd->dev=&pdev->dev;
- qt2410fb_map_video_memory(fbinfo);
-
- //from here we initail LCD hardware stuff
- reqREGmem(&dpd->regs);
- dpd->clk = clk_get(NULL, "lcd");//2410 special stuff
- if (!dpd->clk || IS_ERR(dpd->clk))
- {
- printk(KERN_ERR "failed to get lcd clock source\n");
- return -ENOENT;
- }
- clk_enable(dpd->clk);
- initRegs(&dpd->regs);
- DEBUG_TRACE();
- ret = register_framebuffer(fbinfo);
- if (ret <0)
- {
- qt2410fb_unmap_video_memory(fbinfo);
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(fbinfo);
- }
- printk("fb%d: %s frame buffer device\n",fbinfo->node, fbinfo->fix.id);
- return ret;
- }
-
- static int qt2410fb_remove(struct platform_device *pdev)
- {
- DEBUG_TRACE();
- return 0;
- }
-
- static struct platform_driver qt2410fb_driver =
- {
- .probe = qt2410fb_probe,
- .remove = qt2410fb_remove,
- .driver = {
- .name = "s3c2410-lcd",
- .owner = THIS_MODULE,
- },
- };
-
- int __init qt2410fb_init(void)
- {
- int ret;
- DEBUG_TRACE();
- ret = platform_driver_register(&qt2410fb_driver);
- return ret;
- }
-
- static void __exit qt2410fb_cleanup(void)
- {
- DEBUG_TRACE();
-
- platform_driver_unregister(&qt2410fb_driver);
-
- }
-
- module_init(qt2410fb_init);
- module_exit(qt2410fb_cleanup);
[qt2410fb.h]
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/delay.h>
- #include <linux/fb.h>
- #include <linux/init.h>
- #include <linux/dma-mapping.h>
- #include <linux/interrupt.h>
- #include <linux/platform_device.h>
- #include <linux/clk.h>
-
- #include <asm/io.h>
- #include <asm/div64.h>
-
- #include <asm/mach/map.h>
- #include <asm/arch/regs-lcd.h>
- #include <asm/arch/regs-gpio.h>
- #include <asm/arch/fb.h>
- #include <asm/uaccess.h>
- #include <linux/ioport.h>
- #include <asm/io.h>
-
- #define PALETTE_BUFF_CLEAR (0x80000000)
- #define DEBUG_TRACE() printk("[%s]:%d =>\n",__FUNCTION__,__LINE__)
-
- // LCD CONTROLLER
- #define LCDCON1 0x4d000000 //LCD control 1
- #define LCDCON2 0x4d000004 //LCD control 2
- #define LCDCON3 0x4d000008 //LCD control 3
- #define LCDCON4 0x4d00000c //LCD control 4
- #define LCDCON5 0x4d000010 //LCD control 5
- #define LCDSADDR1 0x4d000014 //STN/TFT Frame buffer start address 1
- #define LCDSADDR2 0x4d000018 //STN/TFT Frame buffer start address 2
- #define LCDSADDR3 0x4d00001c //STN/TFT Virtual screen address set
- #define TPAL 0x4d000050 //TFT Temporary palette
- #define LCDINTPND 0x4d000054 //LCD Interrupt pending
- #define LCDSRCPND 0x4d000058 //LCD Interrupt source
- #define LCDINTMSK 0x4d00005c //LCD Interrupt mask
- #define LPCSEL 0x4d000060 //LPC3600 Control
- #define PALETTE 0x4d000400 //Palette start address
-
- #define GPCCON 0x56000020 //Port C control
- #define GPCUP 0x56000028 //Pull-up control C
- #define GPDCON 0x56000030 //Port D control
- #define GPDUP 0x56000038 //Pull-up control D
-
- // SYNC settings
-
- #define HFPD 30
- #define HBPD 6
- #define VFPD 2
- #define VBPD 1
- #define VSPW 1
- #define HSPW 3
另外一個qt2410 LCD framebuffer的懶人移植法就是直接參考mach-qt2410.c的source並把mach-smdk2410.c修改成如下
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/interrupt.h>
- #include <linux/list.h>
- #include <linux/timer.h>
- #include <linux/init.h>
- #include <linux/serial_core.h>
- #include <linux/platform_device.h>
-
- #include <asm/mach/arch.h>
- #include <asm/mach/map.h>
- #include <asm/mach/irq.h>
-
- #include <asm/hardware.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <asm/mach-types.h>
-
- #include <asm/plat-s3c/regs-serial.h>
-
- #include <asm/plat-s3c24xx/devs.h>
- #include <asm/plat-s3c24xx/cpu.h>
-
- #include <asm/plat-s3c24xx/common-smdk.h>
- #include <asm/arch-s3c2410/fb.h>
-
- static struct map_desc smdk2410_iodesc[] __initdata = {
-
- };
-
- #define UCON S3C2410_UCON_DEFAULT
- #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
- #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
-
- static struct s3c2410_uartcfg smdk2410_uartcfgs[] __initdata = {
- [0] = {
- .hwport = 0,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [1] = {
- .hwport = 1,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- },
- [2] = {
- .hwport = 2,
- .flags = 0,
- .ucon = UCON,
- .ulcon = ULCON,
- .ufcon = UFCON,
- }
- };
-
-
-
- static struct resource s3c_cs89x0_resources[] = {
- [0] = {
- .start = 0x19000300,
- .end = 0x19000300 + 16,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_EINT9,
- .end = IRQ_EINT9,
- .flags = IORESOURCE_IRQ,
- },
- };
-
- static struct platform_device s3c_cs89x0 = {
- .name = "cirrus-cs89x0",
- .num_resources = ARRAY_SIZE(s3c_cs89x0_resources),
- .resource = s3c_cs89x0_resources,
- };
-
- static struct resource s3c_led_resources[] = {
- [0] = {
- .start = 0x56000000,
- .end = 0x56000000 + 24,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_EINT23,
- .end = IRQ_EINT23,
- .flags = IORESOURCE_IRQ,
- },
- };
-
-
- static struct platform_device s3c_led = {
- .name = "JoeyLED",
- .num_resources = ARRAY_SIZE(s3c_led_resources),
- .resource = s3c_led_resources,
- };
-
-
-
-
- static struct s3c2410fb_display qt2410_lcd_cfg[] __initdata = {
- {
-
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
-
- .type = S3C2410_LCDCON1_TFT,
- .width = 640,
- .height = 480,
-
- .pixclock = 40000,
- .xres = 640,
- .yres = 480,
- .bpp = 16,
- .left_margin = 44,
- .right_margin = 116,
- .hsync_len = 96,
- .upper_margin = 19,
- .lower_margin = 11,
- .vsync_len = 15,
- },
- {
-
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
-
- .type = S3C2410_LCDCON1_TFT,
- .width = 480,
- .height = 640,
- .pixclock = 40000,
- .xres = 480,
- .yres = 640,
- .bpp = 16,
- .left_margin = 8,
- .right_margin = 24,
- .hsync_len = 8,
- .upper_margin = 2,
- .lower_margin = 4,
- .vsync_len = 2,
- },
- {
-
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
-
- .type = S3C2410_LCDCON1_TFT,
- .width = 240,
- .height = 320,
- .pixclock = 100000,
- .xres = 240,
- .yres = 320,
- .bpp = 16,
- .left_margin = 13,
- .right_margin = 8,
- .hsync_len = 4,
- .upper_margin = 2,
- .lower_margin = 7,
- .vsync_len = 4,
- },
- };
-
-
- static struct s3c2410fb_mach_info qt2410_fb_info __initdata = {
- .displays = qt2410_lcd_cfg,
- .num_displays = ARRAY_SIZE(qt2410_lcd_cfg),
- .default_display = 0,
-
- .lpcsel = ((0xCE6) & ~7) | 1<<4,
- };
-
-
-
- static char tft_type = 's';
-
- static int __init qt2410_tft_setup(char *str)
- {
- tft_type = 's';
- return 1;
- }
-
-
-
- static struct platform_device *smdk2410_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c,
- &s3c_device_iis,
- &s3c_cs89x0,
- &s3c_led,
- };
-
- static void __init smdk2410_map_io(void)
- {
- s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));
- s3c24xx_init_clocks(0);
- s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));
- }
-
- static void __init smdk2410_init(void)
- {
- switch (tft_type) {
- case 'p':
- qt2410_fb_info.default_display = 1;
- break;
- case 'b':
- qt2410_fb_info.default_display = 0;
- break;
- case 's':
- default:
- qt2410_fb_info.default_display = 2;
- break;
- }
- s3c24xx_fb_set_platdata(&qt2410_fb_info);
-
- platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
- smdk_machine_init();
- }
-
- MACHINE_START(SMDK2410, "SMDK2410")
-
-
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100,
- .map_io = smdk2410_map_io,
- .init_irq = s3c24xx_init_irq,
- .init_machine = smdk2410_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
小弟會找時間以官方driver為基礎繼續講解embedded GUI的應用
fw_printenv-control u-boot enviroment variables under linux (2009-05-12 16:35)
rpcapd與netcat-測試嵌入式系統的好工具 (2009-04-09 17:06)
udev-強大的device node管理系統 (2009-02-02 20:35)
UIO-Linux user space I/O driver (2008-12-15 14:50)
GDB-刺入bug心臟的寶劍 (2008-10-15 16:11)
Telnet daemon-RS232以外的選擇 (2008-10-03 18:57)
在嵌入式系統切換file system-以squashfs和jffs2為例 (2008-10-01 19:44)
Linux kernel module-進入系統核心的鑰匙 (2008-09-12 14:03)
SQLite-麻雀雖小五臟俱全 (2008-09-05 14:46)