#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/workqueue.h> #include <linux/wait.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/div64.h> #include <asm/mach/map.h> #include <mach/regs-lcd.h> #include <mach/regs-gpio.h> #include <mach/fb.h> 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; unsigned long lcdcon2; unsigned long lcdcon3; unsigned long lcdcon4; unsigned long lcdcon5; unsigned long lcdsaddr1; unsigned long lcdsaddr2; unsigned long lcdsaddr3; unsigned long redlut; unsigned long greenlut; unsigned long bluelut; unsigned long reserved[9]; unsigned long dithmode; unsigned long tpal; unsigned long lcdintpnd; unsigned long lcdsrcpnd; unsigned long lcdintmsk; unsigned long 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 *gpccon; static volatile unsigned long *gpdcon; static volatile unsigned long *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) { unsigned int val; if (regno > 16) return 1; 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 lcd_init( void ) { s3c_lcd = framebuffer_alloc(0, NULL); strcpy(s3c_lcd->fix.id, "tq2440_480_272_lcd" ); s3c_lcd->fix.smem_len = 480*272*16/8; s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS; s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; s3c_lcd->fix.line_length = 480*2; s3c_lcd->var.xres = 480; s3c_lcd->var.yres = 272; s3c_lcd->var.xres_virtual = 480; s3c_lcd->var.yres_virtual = 272; s3c_lcd->var.bits_per_pixel = 16; s3c_lcd->var.red.offset = 11; s3c_lcd->var.red.length = 5; s3c_lcd->var.green.offset = 5; s3c_lcd->var.green.length = 6; s3c_lcd->var.blue.offset = 0; s3c_lcd->var.blue.length = 5; s3c_lcd->var.activate = FB_ACTIVATE_NOW; s3c_lcd->fbops = &s3c_lcdfb_ops; s3c_lcd->pseudo_palette = pseudo_palette; s3c_lcd->screen_size = 480*272*16/8; gpccon = ioremap(0x56000020, 4); gpdcon = ioremap(0x56000030, 4); gpgcon = ioremap(0x56000060, 4); *gpccon = 0xaaaaaaaa; *gpdcon = 0xaaaaaaaa; *gpgcon |= (3<<8); lcd_regs = ioremap(0x4D000000, sizeof ( struct lcd_regs)); lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1); lcd_regs->lcdcon2 = (2<<24) | (271<<14) | (2<<6) | (10<<0); lcd_regs->lcdcon3 = (2<<19) | (479<<8) | (2); lcd_regs->lcdcon4 = (41<<0); lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0); 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 = (480*16/16); lcd_regs->lcdcon1 |= (1<<0); lcd_regs->lcdcon5 |= (1<<3); register_framebuffer(s3c_lcd); return 0; } static void lcd_exit( void ) { unregister_framebuffer(s3c_lcd); lcd_regs->lcdcon1 &= ~(1<<0); dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start); iounmap(lcd_regs); iounmap(gpccon); iounmap(gpdcon); iounmap(gpgcon); framebuffer_release(s3c_lcd); } module_init(lcd_init); module_exit(lcd_exit); MODULE_LICENSE("GPL" );
3.使用
MAKEFILE: 驱动文件名为sk_lcd.c
KERN_DIR = /home/stevenking/workspace/code/linux-2.6.39 //修改为自己的内核路径 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += sk_lcd.o 1.首先先从原先内核中make menuconfig去掉原来的驱动程序,不然会有冲突
-> Device Drivers -> Graphics support <M> S3C2410 LCD framebuffer support 2.编译内核,下载新的内核 make zImage make modules 3.重建根文件系统 将linux文件夹下/driver/video/下面的
cfbfillrect.ko cfbimgblt.ko cfbcopyarea.ko 以及新编译的
sk_lcd.ko 放入/root/lib/文件夹下 重新 mkyaffs2image root root.bin,下载新的文件系统 4.启动之后:
cd /lib insmod cfbcopyarea.ko insmod cfbfillrect.ko insmod cfbimgblt.ko insmod sk_lcd.ko echo hello > /dev/tty1 cat /proc/devices > /dev/tty1 就能在LCD上看到输出的文字了
补充:添加到内核
1.driver/video/kconfig
config FB_TQ2440_LCD_SK
tristate "TQ2440 LCD framebuffer support by Steven King"
depends on FB && ARCH_S3C2410
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
---help---
LCD driver by steven king 8/18/2013.
2.makefile
obj-$(CONFIG_FB_TQ2440_LCD_SK)
+= tq2440_lcd_sk.o
3.文件复制过去,加上选项,编译,即可。