#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/vmalloc.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/clk.h> #include <linux/dma-mapping.h> static struct fb_info *s3c_lcd_info; static u32 s3c_pseudo_palette[16]; static volatile unsigned long *gpbcon; static volatile unsigned long *gpbdat; static volatile unsigned long *gpfcon; static volatile unsigned long *gpfdat; static volatile unsigned long *gpicon; static volatile unsigned long *gpjcon; static volatile unsigned long *mifpcon; static volatile unsigned long *spcon; 一个一个ioremap 太麻烦,统一放一结构里面,从6410的芯片手册上得到以下的寄存器P455 struct s3c6410_lcd_regs { unsigned long vidcon0; //视频时钟源 unsigned long vidcon1; unsigned long vidcon2; unsigned long reserver1; unsigned long vidtcon0; unsigned long vidtcon1; unsigned long vidtcon2; unsigned long reserver2; unsigned long wincon0; unsigned long wincon1; unsigned long wincon2; unsigned long wincon3; unsigned long wincon4; unsigned long reserver3[3]; unsigned long vidosd0a; unsigned long vidosd0b; unsigned long vidosd0c; unsigned long reserver4; unsigned long vidosd1a; unsigned long vidosd1b; unsigned long vidosd1c; unsigned long vidosd1d; unsigned long vidosd2a; unsigned long vidosd2b; unsigned long vidosd2c; unsigned long vidosd2d; unsigned long vidosd3a; unsigned long vidosd3b; unsigned long vidosd3c; unsigned long reserver5; unsigned long vidosd4a; unsigned long vidosd4b; unsigned long vidosd4c; unsigned long reserver6[5]; unsigned long vidw00add0b0; unsigned long vidw00add0b1; unsigned long vidw01add0b0; unsigned long vidw01add0b1; unsigned long vidw02add0; unsigned long reserver7; unsigned long vidw03add0; unsigned long reserver8; unsigned long vidw04add0; unsigned long reserver9[3]; unsigned long vidw00add1b0; unsigned long vidw00add1b1; }; static int s3c_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { u32 color = 0; u32 *p; /* 0xRRGGBB */ /* red = 0xAABB * green = 0xCCDD * blue = 0xEEFF * color = 0xAACCEE */ red = (red >> (16 - info->var.red.length)) << info->var.red.offset; // 0xAA0000 green = (green >> (16 - info->var.green.length)) << info->var.green.offset; // 0xCC00 blue = (blue >> (16 - info->var.blue.length)) << info->var.blue.offset; // 0xEE color = red | green | blue; // 0xAACCEE p = info->pseudo_palette; p[regno] = color; return 0; } static struct fb_ops s3c_fb_ops = { .fb_setcolreg = s3c_fb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, }; static struct s3c6410_lcd_regs * lcd_regs; static int s3c_lcd_init(void) { struct clk *clk; int hclk; int clkval; /* 1. 分配fb_info */ s3c_lcd_info = framebuffer_alloc(0, NULL); /* 2. 设置 */ /* 2.1 设置固定的信息,在fb_info 结构体里的fix*/ strcpy(s3c_lcd_info->fix.id, "uplooking_lcd"); // s3c_lcd_info->fix.smem_start /* 显存物理地址 */ s3c_lcd_info->fix.smem_len = 480*272*32/8;//看lcd手册,4位 s3c_lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;//在fb.h里的FB_TYPE里第一个 s3c_lcd_info->fix.visual = FB_VISUAL_TRUECOLOR; //TFT屏是真彩色 s3c_lcd_info->fix.line_length = 480*32/8; //480乘以272,一个像素4位 /* 2.2 设置可变的信息,fb_info结构体的var */ s3c_lcd_info->var.xres = 480; s3c_lcd_info->var.yres = 272; s3c_lcd_info->var.xres_virtual = 480; s3c_lcd_info->var.yres_virtual = 272; s3c_lcd_info->var.bits_per_pixel = 32; //每个像素用多少位 // 0xRRGGBB s3c_lcd_info->var.red.length = 8; s3c_lcd_info->var.red.offset = 16; //从16位开始,长度为8 s3c_lcd_info->var.green.length = 8; s3c_lcd_info->var.green.offset = 8; s3c_lcd_info->var.blue.length = 8; s3c_lcd_info->var.blue.offset = 0; s3c_lcd_info->var.activate = FB_ACTIVATE_NOW;// 看不懂就用默认值为0 的 /* 2.3 设置操作函数 */ s3c_lcd_info->fbops = &s3c_fb_ops; /* 2.4 设置其他信息 */ //s3c_lcd_info->screen_base = ;/* 显存的虚拟地址 */ s3c_lcd_info->screen_size = 480*272*4; s3c_lcd_info->pseudo_palette = &s3c_pseudo_palette;//调色板, /* 4. 硬件相关的操作 */ /* 4.1 配置GPIO用于LCD */ gpicon = ioremap(0x7F008100, 4); *gpicon = 0xAAAAAAAA; gpjcon = ioremap(0x7F008120, 4); *gpjcon = 0xAAAAAAAA; gpbcon = ioremap(0x7F008020, 4); gpbdat = gpbcon + 1; *gpbcon &= ~(0xf<<24); *gpbcon |= (0x1<<24); /* backlight enable pin */ gpfcon = ioremap(0x7F0080A0, 4); gpfdat = gpfcon + 1; *gpfcon &= ~(3<<28); *gpfcon |= (1<<28); /* GPF14 use as PWREN */ /* 4.2 根据LCD的性能设置LCD控制器 */ /* MIFPCON:.调制解调器接口端口控制寄存器,6410手册要求就要这么设置 */ *mifpcon &= ~(1<<3); /*spcon:特殊管脚功能寄存器,下面选择01=RGB*/ spcon = ioremap(0x7F0081A0, 4); *spcon &= ~(3); *spcon |= 1; lcd_regs = ioremap(0x77100000, sizeof(struct s3c6410_lcd_regs)); clk = clk_get(NULL, "lcd"); /*struct clk *clk_get(struct device *dev, const char *id)*/ clk_enable(clk); /* HCLK_GATE[3]设为1 */ /* HCLK_GATE = ioremap(0x7E00F030, 4); * HCLK_GATE |= (1<<3); */ /* bit[13:6], CLKVAL_F,VCLK = Video Clock Source / (CLKVAL+1)=133/15=9MHZ, VCLK = 9000000 CLKVAL_F = hclk/9000000 - 1=14 * bit[3:2], CLKSEL_F, 00 = HCLK * bit[1], ENVID-使能LCD控制器的输出, 先设为0, 最后全部设置好了再设为1 */ clk = clk_get(NULL, "hclk"); hclk = clk_get_rate(clk); clkval = hclk/9000000 - 1; printk("hclk = %d, clkval = %d\n", hclk, clkval); lcd_regs->vidcon0 = (0<<29)|(0<<26) | (0<<17) | (clkval << 6) | (1<<4); /* bit[7], 0-在VLCK下降沿读数据,通过lcd手册的时序图看是上升还是下降 * bit[6], HSYNC低脉冲有效 * bit[5], VSYNC低脉冲有效 * bit[4], VDEN高电平有效 */ lcd_regs->vidcon1 = (0<<7) | (1<<6) | (1<<5) | (0<<4); /* LCD手册P11 * tvp = 10, tvb = 2, tvf=2 */ lcd_regs->vidtcon0 = (1<<16) | (1<<8) | (9<<0); /* LCD手册P11 * thp = 41, thb = 2, thf=2 */ lcd_regs->vidtcon1 = (1<<16) | (1<<8) | (40<<0); lcd_regs->vidtcon2 = (271<<11) | (479<<0); /* 4.3 分配显存, 并告诉LCD控制器,专业的函数来分配显存!不用kmalloc! */ s3c_lcd_info->screen_base = dma_alloc_writecombine(NULL, s3c_lcd_info->screen_size, &s3c_lcd_info->fix.smem_start, GFP_KERNEL); //void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) /* bit[17],BSWP = 0, bit[16],HWSWP = 0 * bit[5:2],BPPMODE_F, 0b1011, 24bpp * bit[0], ENWIN_F, 0-先不使能 */ lcd_regs->wincon0 &= ~(0xf << 2); lcd_regs->wincon0 |= (0xb<<2); /* unpacked 24 BPP (non-palletized R:8-G:8-B:8 ) */ #define LeftTopX 0 #define LeftTopY 0 #define RightBotX 479 #define RightBotY 271 lcd_regs->vidosd0a = (LeftTopX<<11) | (LeftTopY << 0); //6410手册 P473 lcd_regs->vidosd0b = (RightBotX<<11) | (RightBotY << 0); lcd_regs->vidosd0c = 480*272*4 / 4; lcd_regs->vidw00add0b0 = s3c_lcd_info->fix.smem_start; //窗口 0 的缓冲区开始地址控制寄存器,缓冲区 0 lcd_regs->vidw00add1b0 = (s3c_lcd_info->fix.smem_start + s3c_lcd_info->fix.smem_len) & 0xffffff; //窗口 0 的缓冲区结束地址控制寄存器,缓冲区 0 /* 4.4 使能 */ *gpbdat |= (1<<6); /* 使能背光 */ *gpfdat |= (1<<14); /* PWREN,提供电源 */ lcd_regs->vidcon0 |= (3); /* 使能LCD控制器输出信号 */ lcd_regs->wincon0 |= (1<<0); /* 使能窗口0 */ /* 3. 注册 */ register_framebuffer(s3c_lcd_info); return 0; } static void s3c_lcd_exit(void) { struct clk *clk; unregister_framebuffer(s3c_lcd_info); *gpbdat &= ~(1<<6); /* 禁止背光 */ *gpfdat &= ~(1<<14); /* 禁止PWREN */ lcd_regs->vidcon0 &= ~(1<<1); /* 禁止LCD控制器输出信号 */ lcd_regs->wincon0 &= ~(1<<0); /* 禁止窗口0 */ dma_free_writecombine(NULL, s3c_lcd_info->screen_size, s3c_lcd_info->screen_base, s3c_lcd_info->fix.smem_start); clk = clk_get(NULL, "lcd"); clk_disable(clk); /* HCLK_GATE[3]设为0 */ iounmap(lcd_regs); iounmap(gpbcon); iounmap(gpfcon); iounmap(gpicon); iounmap(gpjcon); iounmap(mifpcon); iounmap(spcon); framebuffer_release(s3c_lcd_info); } module_init(s3c_lcd_init); module_exit(s3c_lcd_exit); MODULE_LICENSE("GPL");