s3c2440液晶屏驱动 (非内核自带) linux-4.1.24
对于,不想逐一检查内核自带驱动,想自己编写驱动。
1,make menuconfig 去掉 编译到内核,改为 M 编译为 模块(因为要用到里面的3个.ko 驱动)
Device Drivers --->
Graphics support --->
Support for frame buffer devices --->
<M> S3C2410 LCD framebuffer support
2,make uImage && make modules 生成新内核 和 模块文件
烧写新内核或使用 nfs bootm 使用编译为 M 模块的内核启动。
复制 3个 ko 文件到 文件系统,这里用的是 NFS 网络文件系统。
cp drivers/video/fbdev/core/cfb*.ko /nfs_root/new_fs (新的 4.1 内核是在这里,以前没有 这个 core 目录)
3, 使用原来的 2.6.22 的内核下的驱动程序,修改头文件后,编译为 .ko 文件放到 NFS 文件系统里,启动,如图所示
图忘拍了,有时是白屏,有时是竖的细彩线。
比较内核自带驱动 s3c2410fb.c 发现有个 usleep_range(1000, 1100);
而原来的 2.6.22 的驱动里面,是没有启用 clk 的。添加上这部分后,重新编译,试机成功。
最后是完整的驱动源码:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/errno.h> 4 #include <linux/string.h> 5 #include <linux/mm.h> 6 #include <linux/slab.h> 7 #include <linux/delay.h> 8 #include <linux/fb.h> 9 #include <linux/init.h> 10 #include <linux/dma-mapping.h> 11 #include <linux/interrupt.h> 12 #include <linux/workqueue.h> 13 #include <linux/wait.h> 14 #include <linux/platform_device.h> 15 #include <linux/clk.h> 16 17 #include <asm/io.h> 18 #include <asm/uaccess.h> 19 #include <asm/div64.h> 20 21 //#include <asm/mach/map.h> 22 //#include <asm/arch/regs-lcd.h> 23 //#include <asm/arch/regs-gpio.h> 24 //#include <asm/arch/fb.h> 25 26 27 #include <asm/mach/map.h> 28 #include <mach/regs-lcd.h> 29 #include <mach/regs-gpio.h> 30 #include <mach/fb.h> 31 32 33 //参数定义 34 #define LCD_WIDTH 480 35 #define LCD_HEIGHT 272 36 #define LCD_BIT 16 37 38 #define LCD_CLKVAL 4 39 #define LCD_TFT 3 40 #define LCD_24BBP 0xd 41 #define LCD_16BBP 0xc 42 #define LCD_EN_OFF 0 43 #define LCD_EN_ON 1 44 #define LCD_VBPD 1 45 #define LCD_LINEVAL (LCD_HEIGHT - 1) 46 #define LCD_VFPD 1 47 #define LCD_VSPW 9 48 #define LCD_HBPD 1 49 #define LCD_HOZVAL (LCD_WIDTH - 1) 50 #define LCD_HFPD 1 51 #define LCD_HSPW 40 52 #define LCD_INVVLINE 1 53 #define LCD_INVVFRAME 1 54 //调色板 u32 = unsigned int 55 static u32 pseudo_palette[16]; 56 57 /* from pxafb.c */ 58 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) 59 { 60 //只取16bit 数据 61 chan &= 0xffff; 62 //如 r = 16-5 = 11 : 右移去掉不要的位 63 chan >>= (16 - bf->length); 64 //在移到高位去 就是右边补0 65 return chan << bf->offset; 66 } 67 68 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, 69 unsigned int green, unsigned int blue, 70 unsigned int transp, struct fb_info *info) 71 { 72 73 if(16 > regno) 74 { 75 unsigned int val; 76 val = chan_to_field(red, &info->var.red); 77 val |= chan_to_field(green, &info->var.green); 78 val |= chan_to_field(blue, &info->var.blue); 79 pseudo_palette[regno] = val; 80 return 0; 81 } 82 return 1; 83 } 84 85 86 static struct fb_ops s3c_ops = { 87 .owner = THIS_MODULE, 88 .fb_setcolreg = s3c_lcdfb_setcolreg, 89 .fb_fillrect = cfb_fillrect, 90 .fb_copyarea = cfb_copyarea, 91 .fb_imageblit = cfb_imageblit, 92 }; 93 94 //定义GPIO 95 static volatile unsigned long *gpb_con; 96 static volatile unsigned long *gpc_con; 97 static volatile unsigned long *gpd_con; 98 static volatile unsigned long *gpg_con; 99 100 static volatile unsigned long *gpb_dat; 101 static volatile unsigned long *gpc_dat; 102 static volatile unsigned long *gpd_dat; 103 static volatile unsigned long *gpg_dat; 104 105 struct lcd_regs { 106 unsigned long lcdcon1; 107 unsigned long lcdcon2; 108 unsigned long lcdcon3; 109 unsigned long lcdcon4; 110 unsigned long lcdcon5; 111 unsigned long lcdsaddr1; 112 unsigned long lcdsaddr2; 113 unsigned long lcdsaddr3; 114 unsigned long redlut; 115 unsigned long greenlut; 116 unsigned long bluelut; 117 unsigned long reserved[9]; 118 unsigned long dithmode; 119 unsigned long tpal; 120 unsigned long lcdintpnd; 121 unsigned long lcdsrcpnd; 122 unsigned long lcdintmsk; 123 unsigned long lpcsel; 124 }; 125 126 static volatile struct lcd_regs * lcd_reg; 127 128 129 //定义fb_info 130 static struct fb_info * s3c_lcd; 131 static int lcd_init(void) 132 { 133 //1,分配一个fb_info 134 s3c_lcd = framebuffer_alloc(0, NULL); 135 136 struct clk *clk; 137 clk = clk_get(NULL, "lcd"); 138 if (IS_ERR(clk)) { 139 printk("failed to get lcd clock source\n"); 140 } 141 142 clk_prepare_enable(clk); 143 printk("got and enabled clock\n"); 144 145 usleep_range(1000, 1100); 146 147 //2,设置 148 //2,1 设置固定的参数 149 strcpy(s3c_lcd->fix.id, "mylcd"); 150 s3c_lcd->fix.smem_len = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8; 151 s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS; 152 s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; 153 s3c_lcd->fix.line_length = LCD_WIDTH * LCD_BIT / 8; //单位 bytes 154 //物理地址由 dma_alloc_writecombine 函数分配 155 //s3c_lcd->fix.smem_start = LCD_FRAMEBUFFER; 156 //s3c_lcd->fix.mmio_len = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8; 157 158 //2,2 设置可变的参数 159 s3c_lcd->var.width = LCD_WIDTH; 160 s3c_lcd->var.height = LCD_HEIGHT; 161 s3c_lcd->var.xres = LCD_WIDTH; 162 s3c_lcd->var.yres = LCD_HEIGHT; 163 s3c_lcd->var.xres_virtual = LCD_WIDTH; 164 s3c_lcd->var.yres_virtual = LCD_HEIGHT; 165 s3c_lcd->var.bits_per_pixel = LCD_BIT; 166 //RGB 5 6 5 167 s3c_lcd->var.red.length = 5; 168 s3c_lcd->var.red.offset = 11; 169 s3c_lcd->var.green.length = 6; 170 s3c_lcd->var.green.offset = 5; 171 s3c_lcd->var.blue.length = 5; 172 s3c_lcd->var.blue.offset = 0; 173 s3c_lcd->var.activate = FB_ACTIVATE_NOW; 174 175 s3c_lcd->var.pixclock = 100000; 176 177 //2,3 设置操作函数 178 s3c_lcd->fbops = &s3c_ops; 179 180 //2.4 其它的设置 181 s3c_lcd->screen_size = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8; 182 s3c_lcd->pseudo_palette = pseudo_palette; 183 184 //3,硬件相关的操作 185 //3,1 配置GPIO 186 gpb_con = ioremap(0x56000010, 4); 187 gpb_dat = gpb_con + 1; 188 189 gpc_con = ioremap(0x56000020, 4); 190 gpc_dat = gpc_con + 1; 191 192 gpd_con = ioremap(0x56000030, 4); 193 gpc_dat = gpd_con + 1; 194 195 gpg_con = ioremap(0x56000060, 4); 196 gpg_dat = gpg_con + 1; 197 198 *gpc_con = 0xaaaaaaaa; // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND 199 *gpd_con = 0xaaaaaaaa; // GPIO管脚用于VD[23:8] 200 201 *gpb_con &= ~(3); //配置背光引脚为输出 202 *gpb_con |= 1; 203 *gpb_dat &= 0; //默认输出低电平 204 205 *gpg_con &= ~(3<<2*4); //GPGCON 4 配置为 LCD_POWER 206 *gpg_con |= (3<<2*4); 207 208 //3,2 配置lcdcon1 ~ lcdcon5 209 lcd_reg = ioremap(0x4d000000, sizeof(struct lcd_regs)); 210 lcd_reg->lcdcon1 = LCD_CLKVAL<<8 | LCD_TFT<<5 | LCD_16BBP<<1 | LCD_EN_OFF; 211 lcd_reg->lcdcon2 = LCD_VBPD<<24 | LCD_LINEVAL<<14 | LCD_VFPD<<6 | LCD_VSPW; 212 lcd_reg->lcdcon3 = LCD_HBPD<<19 | LCD_HOZVAL<<8 | LCD_HFPD; 213 lcd_reg->lcdcon4 = LCD_HSPW; 214 lcd_reg->lcdcon5 = 1<<11 | LCD_INVVLINE<<9 | LCD_INVVFRAME<<8 | 1; 215 //屏幕宽度 * 16bit 颜色值 / words : 1 words = 2 byte 216 lcd_reg->lcdsaddr3 = LCD_WIDTH * 16 / 16; 217 218 //3,3 设置显存 bramebuffer, 并把地址告诉lcd 控制器 219 s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL); 220 221 lcd_reg->lcdsaddr1 = (s3c_lcd->fix.smem_start>>22)<<21 | ((s3c_lcd->fix.smem_start>>1) & 0x1fffff); 222 lcd_reg->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1) & 0x1fffff; 223 224 unsigned long saddr1,saddr2,saddr3; 225 saddr1 = s3c_lcd->fix.smem_start >> 1; 226 saddr2 = s3c_lcd->fix.smem_start; 227 saddr2 += s3c_lcd->fix.line_length * s3c_lcd->var.yres; 228 saddr2 >>= 1; 229 saddr3 = S3C2410_OFFSIZE(0) | 230 S3C2410_PAGEWIDTH((s3c_lcd->fix.line_length / 2) & 0x3ff); 231 232 lcd_reg->lcdsaddr1 = saddr1; 233 lcd_reg->lcdsaddr2 = saddr2; 234 lcd_reg->lcdsaddr3 = saddr3; 235 236 printk("fb %x \n", LCD_CLKVAL<<8 | LCD_TFT<<5 | LCD_16BBP<<1 | LCD_EN_OFF); 237 printk("fb %x \n", LCD_VBPD<<24 | LCD_LINEVAL<<14 | LCD_VFPD<<6 | LCD_VSPW); 238 printk("fb %x \n", LCD_HBPD<<19 | LCD_HOZVAL<<8 | LCD_HFPD); 239 printk("fb %x \n", LCD_HSPW); 240 printk("fb %x \n", 1<<11 | LCD_INVVLINE<<9 | LCD_INVVFRAME<<8 | 1); 241 242 243 printk("fb %x \n", saddr1); 244 printk("fb %x \n", saddr2); 245 printk("fb %x \n", saddr3); 246 247 //打开 LCD_POWER 背光 248 lcd_reg->lcdcon1 |= LCD_EN_ON; 249 *gpb_dat |= 1; 250 lcd_reg->lcdcon5 &= ~(1<<3); 251 lcd_reg->lcdcon5 |= (1<<3); 252 253 //4,注册 254 register_framebuffer(s3c_lcd); 255 return 0; 256 } 257 258 static void lcd_exit(void) 259 { 260 //关背光 261 *gpb_dat &= 0; 262 lcd_reg->lcdcon5 &= ~(1<<3); 263 lcd_reg->lcdcon1 &= LCD_EN_OFF; 264 265 //注销显存 266 dma_free_writecombine(NULL,s3c_lcd->fix.smem_len,s3c_lcd->screen_base,s3c_lcd->fix.smem_start); 267 268 //注销 269 framebuffer_release(s3c_lcd); 270 271 //注销framebuf 272 unregister_framebuffer(s3c_lcd); 273 274 //取消map 275 iounmap(gpb_con); 276 iounmap(gpc_con); 277 iounmap(gpd_con); 278 iounmap(gpg_con); 279 iounmap(lcd_reg); 280 } 281 282 283 module_init(lcd_init); 284 module_exit(lcd_exit); 285 MODULE_LICENSE("GPL");
试验的时候要先加载那3个 .ko
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko 编译出来的 lcd 驱动