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 #include <asm/io.h> 17 #include <asm/uaccess.h> 18 #include <asm/div64.h> 19 #include <asm/mach/map.h> 20 #include <asm/types.h> 21 22 #define LCD_BASE 0x11400000 23 #define LCD_F0 (LCD_BASE+0x0180) 24 #define LCD_F1 (LCD_BASE+0x01A0) 25 #define LCD_F2 (LCD_BASE+0x01C0) 26 #define LCD_F3 (LCD_BASE+0x01E0) 27 #define LCD_PWM (LCD_BASE+0x00A0) 28 #define LCD_PWM_BASE 0x139D0000 29 #define LCD_PWM_TCFG0 (LCD_PWM_BASE+0x0000) 30 #define LCD_PWM_TCFG1 (LCD_PWM_BASE+0x0004) 31 #define LCD_PWM_TCON (LCD_PWM_BASE+0x0008) 32 #define LCD_PWM_TCNTB1 (LCD_PWM_BASE+0x0018) 33 #define LCD_PWM_TCMPB1 (LCD_PWM_BASE+0x001C) 34 35 #define BLK_FLAG 1 36 37 //模块信息 38 39 MODULE_LICENSE("Dual BSD/GPL"); 40 41 42 43 static struct fb_info * my_fbInfo = NULL; 44 static int myLcdBpp = 32;//位深 45 static short myPaletteSize = 256; //调色板大小 46 static unsigned int myXvSize = 1024; //缓冲列大小 47 static unsigned int myYvSize = 768; //缓冲行大小 48 static unsigned int pseudo_palette [16];//调色板数组,被fb_info->pseudo_palette调用 49 50 //IO管脚寄存器 51 static unsigned int * __iomem gpf0con; 52 static unsigned int * __iomem gpf1con; 53 static unsigned int * __iomem gpf2con; 54 static unsigned int * __iomem gpf3con; 55 static unsigned int * __iomem gpd0con;//pwm控制 56 static unsigned int * __iomem gpd0dat; 57 58 //pwm寄存器 59 static unsigned int * __iomem tcfg0; 60 static unsigned int * __iomem tcfg1; 61 static unsigned int * __iomem tcon; 62 static unsigned int * __iomem tcntb1; 63 static unsigned int * __iomem tcmpb1; 64 65 //时钟相关寄存器 66 static unsigned int * __iomem clk_div_lcd;//lcd时钟分频 67 static unsigned int * __iomem clk_src_lcd0;//lcd时钟源 68 static unsigned int * __iomem lcdblk_cfg;//显示控制寄存器 69 static unsigned int * __iomem lcdblk_cfg2;//显示控制寄存器 70 71 //lcd寄存器 72 static unsigned int * __iomem vidcon0;//Configures video output format and displays enable/disable. 73 static unsigned int * __iomem vidcon1;//Specifies RGB I/F control signal 74 static unsigned int * __iomem vidcon2;//Specifies output data format control. 75 static unsigned int * __iomem vidtcon0;//Configures video output timing and determines the size of display 76 static unsigned int * __iomem vidtcon1;//Configures video output timing and determines the size of display 77 static unsigned int * __iomem vidtcon2;//Configures video output timing and determines the size of display 78 static unsigned int * __iomem wincon0;//Specifies window0 feature setting 79 static unsigned int * __iomem vidoso0a;//Specifies window position setting 80 static unsigned int * __iomem vidoso0b;//Specifies window position setting 81 static unsigned int * __iomem vidoso0c;//Specifies On Screen Display (OSD) size setting. 82 static unsigned int * __iomem shaadowcon;//Specifies shadow control register 83 static unsigned int * __iomem winchmap2;//Specifies window color control 84 static unsigned int * __iomem vidw00add0b0;//Specifies source image address setting 85 static unsigned int * __iomem vidw00add1b0;//Specifies source image address setting 86 static unsigned int * __iomem win0map;//Specifies window color control 87 static unsigned int * __iomem vidw00add2;//Specifies source image address setting 88 89 ////static T_LCD_REG * __iomem lcdBase; 90 /* from pxafb.c */ 91 /* 92 struct fb_bitfield { //fb缓存的RGB位域,该结构描述每一个像素显示缓冲区的组织方式, 93 //假如为RGB565模式,R占5位=bit[11:15]G占6位=bit[10:5] B占5位=bit[4:0] 94 __u32 offset; // beginning of bitfield 位域偏移:red=11 ,green=5 blue=0 95 __u32 length; // length of bitfield 位域长度 red=5 green=6 blue=5 96 __u32 msb_right;//!= 0 : Most significant bit is */ /*msb_right!=0=>MSB在右边 97 }; 98 99 */ 100 101 static inline unsigned int chan_to_field(unsigned int chan,struct fb_bitfield *bf) 102 { 103 /*内核中的单色都是16位,默认从左到右排列,比如G颜色[0x1f],那么chan就等于0XF800*/ 104 chan&= 0xffff; 105 chan >>= 16 - bf->length;//右移,将数据靠到位0上 106 return chan << bf->offset;//左移一定偏移值,放入16色数据中对应的位置 107 } 108 109 ////设置调色板函数,供内核调用 110 static int my_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info) 111 { 112 unsigned int val = 0; 113 //用red,green,blue三个颜色值构造出16色数据val 114 switch(info->fix.visual){ 115 case FB_VISUAL_TRUECOLOR: //true-color,use pseudo_palette 116 if(regno < 16){ 117 u32 *pal = info-> pseudo_palette; 118 val = chan_to_field(red,&info->var.red); 119 val |= chan_to_field(green,&info->var.green); 120 val |= chan_to_field(blue,&info->var.blue); 121 pal[regno] = val; 122 } 123 break; 124 default : 125 return 1; // 未知类型 126 } 127 128 return 0; 129 130 } 131 132 static struct fb_ops my_fb_ops = { 133 .owner = THIS_MODULE, 134 .fb_setcolreg = my_fb_setcolreg,//调用my_lcdfb_setcolreg()函数,来设置调色板fb_info-> pseudo_palette 135 .fb_fillrect = cfb_fillrect,//填充矩形 136 .fb_copyarea = cfb_copyarea,//复制数据 137 .fb_imageblit = cfb_imageblit,//绘画图形 138 }; 139 140 //设置可变参数 141 static int fb_fill_var(struct fb_info *myfb) 142 { 143 myfb->var.activate = FB_ACTIVATE_NOW; 144 myfb->var.vmode = FB_VMODE_NONINTERLACED; 145 myfb->var.bits_per_pixel = myLcdBpp;/*每个像素的位数即BPP,比如:RGB565则填入16*/ 146 myfb->var.xres_virtual = myXvSize;/*虚拟屏幕一行有多少个像素点 */ 147 myfb->var.yres_virtual = myYvSize;/*虚拟屏幕一列有多少个像素点*/ 148 myfb->var.xres = myXvSize;/*可见屏幕一行有多少个像素点*/ 149 myfb->var.yres = myYvSize;/*可见屏幕一列有多少个像素点*/ 150 myfb->var.xoffset = 0;/*虚拟到可见屏幕之间的行偏移,若可见和虚拟的分辨率一样,就直接设为0*/ 151 myfb->var.yoffset = 0;/*虚拟到可见屏幕之间的列偏移*/ 152 153 //保持透明度参数为0 154 myfb->var.transp.offset = 0; 155 myfb->var.transp.length = 0; 156 switch(myfb->var.bits_per_pixel){ 157 case 1: 158 case 2: 159 case 4: 160 case 8: 161 /* non palletised, A:1,R:2,G:3,B:2 mode */ 162 myfb->var.red.offset = 5; 163 myfb->var.green.offset = 2; 164 myfb->var.blue.offset = 0; 165 myfb->var.red.length = 2; 166 myfb->var.green.length = 3; 167 myfb->var.blue.length = 2; 168 myfb->var.transp.offset = 7; 169 myfb->var.transp.length = 1; 170 break; 171 case 19: 172 /* 666 with one bit alpha/transparency */ 173 myfb->var.transp.offset = 18; 174 myfb->var.transp.length = 1; 175 /* drop through */ 176 case 18: 177 myfb->var.bits_per_pixel = 32; 178 /* 666 format */ 179 myfb->var.red.offset = 12; 180 myfb->var.green.offset = 6; 181 myfb->var.blue.offset = 0; 182 myfb->var.red.length = 6; 183 myfb->var.green.length = 6; 184 myfb->var.blue.length = 6; 185 break; 186 case 16: 187 /* 16 bpp, 565 format */ 188 myfb->var.red.offset = 11; 189 myfb->var.green.offset = 5; 190 myfb->var.blue.offset = 0; 191 myfb->var.red.length = 5; 192 myfb->var.green.length = 6; 193 myfb->var.blue.length = 5; 194 break; 195 case 32: 196 case 28: 197 case 25: 198 myfb->var.transp.length = myfb-> var.bits_per_pixel - 24; 199 myfb->var.transp.offset = 24; 200 /* drop through */ 201 case 24: 202 /* our 24bpp is unpacked, so 32bpp */ 203 myfb->var.bits_per_pixel = 32; 204 myfb->var.red.offset = 16; 205 myfb->var.red.length = 8; 206 myfb->var.green.offset = 8; 207 myfb->var.green.length = 8; 208 myfb->var.blue.offset = 0; 209 myfb->var.blue.length = 8; 210 break; 211 default: 212 printk("invalid bpp "); 213 return -EINVAL; 214 } 215 return 0; 216 } 217 218 //设置固定参数 219 static int fb_fill_fix(struct fb_info *myfb) 220 { 221 unsigned int real_size,virt_size,size; 222 dma_addr_t map_dma; 223 strcpy(myfb->fix.id,"mylcd"); 224 myfb->fix.type = FB_TYPE_PACKED_PIXELS; //在该方式下,像素值与内存直接对应 225 myfb->fix.accel = FB_ACCEL_NONE; 226 myfb->fix.line_length =(myfb->var.xres_virtual * myfb->var.bits_per_pixel)/8;//一行的大小(字节数) 227 myfb->fix.xpanstep = myfb->var.xres_virtual> myfb->var.xres?1:0;/*如果没有硬件panning就赋值为0 */ 228 myfb->fix.ypanstep = myfb->var.yres_virtual> myfb->var.yres?1:0;/*如果没有硬件panning就赋值为0 */ 229 //fix.visual画面设置,常用参数如下 230 // FB_VISUAL_MONO01 0 单色,0:白色,1:黑色 231 // FB_VISUAL_MONO10 1 单色,1:白色,0:黑色 232 // FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩) 233 // FB_VISUAL_PSEUDOCOLOR 3 伪彩 234 // FB_VISUAL_DIRECTCOLOR 4 直彩 235 switch(myfb->var.bits_per_pixel){ 236 case 32: 237 case 24: 238 case 16: 239 case 12: 240 myfb->fix.visual = FB_VISUAL_TRUECOLOR; 241 break; 242 case 8: 243 if(myPaletteSize >= 256) 244 myfb->fix.visual = FB_VISUAL_PSEUDOCOLOR; 245 else 246 myfb->fix.visual = FB_VISUAL_TRUECOLOR; 247 break; 248 case 1: 249 myfb->fix.visual = FB_VISUAL_MONO01; 250 break; 251 default: 252 myfb->fix.visual = FB_VISUAL_TRUECOLOR; 253 break; 254 } 255 256 /*计算整体缓冲大小*/ 257 real_size = myfb->var.xres * myfb->var.yres; 258 virt_size = myfb->var.xres_virtual * myfb->var.yres_virtual; 259 size =(real_size > virt_size)?real_size:virt_size; 260 size *=(myfb->var.bits_per_pixel > 16)?32:myLcdBpp; 261 262 size /= 8; 263 myfb->fix.smem_len = size;//framebuffer长度,字节为单位 264 size = PAGE_ALIGN(size); 265 //显存虚拟起始地址,把地址告诉LCD控制器和fb_info 266 //void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配DMA缓存区给显存 267 //返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,则需要使用dma_free_writecombine()释放内存,避免内存泄漏 268 //参数如下: 269 //*dev:指针,这里填0,表示这个申请的缓冲区里没有内容 270 //size:分配的地址大小(字节单位) 271 //*handle:申请到的物理起始地址 272 //gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下: 273 //GFP_ATOMIC 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠. 274 //GFP_KERNEL 内核内存的正常分配. 可能睡眠. 275 //GFP_USER 用来为用户空间页来分配内存; 它可能睡眠. 276 myfb->screen_base = dma_alloc_writecombine(myfb->dev, size,&map_dma, GFP_KERNEL); 277 if(!myfb->screen_base){ 278 return -ENOMEM; 279 } 280 memset(myfb->screen_base,0x0,size); 281 myfb->fix.smem_start = map_dma; 282 return 0; 283 } 284 285 static void myfb_Init(struct fb_info *myfb) 286 { 287 /*varinit*/ 288 fb_fill_var(myfb); 289 /*fix init*/ 290 fb_fill_fix(myfb); 291 /*ops init*/ 292 myfb->fbops =&my_fb_ops; 293 /*ops init*/ 294 myfb->flags = FBINFO_FLAG_DEFAULT; 295 myfb->pseudo_palette = pseudo_palette;//保存调色板数组 296 } 297 298 static void mygpio_Init(void) 299 { 300 gpf0con = ioremap(LCD_F0,4); 301 gpf1con = ioremap(LCD_F1,4); 302 gpf2con = ioremap(LCD_F2,4); 303 gpf3con = ioremap(LCD_F3,4); 304 writel(0x22222222,gpf0con); 305 writel(0x22222222,gpf1con); 306 writel(0x22222222,gpf2con); 307 *gpf3con &=~(0xffff); 308 writel(0x2222,gpf3con); 309 310 //背光 311 if(BLK_FLAG)//pwm方式 312 { 313 gpd0con = ioremap(LCD_PWM,4); 314 writel((readl(gpd0con) & (~(0xf<<4))) | (0x2<<4) , gpd0con); 315 } 316 else //直接点亮方式 317 { 318 int val; 319 // unsigned int * __iomem gpd0con; 320 // unsigned int * __iomem gpd0dat; 321 322 gpd0con = ioremap(0x114000A0,4); 323 gpd0dat = ioremap(0x114000A4,4); 324 325 val = readl(gpd0con); 326 val &=~(0xf<<4); 327 val |=(0x1<<4); 328 writel(val,gpd0con); 329 val = readl(gpd0dat); 330 val|=(0x1<<1); 331 writel(val,gpd0dat); 332 } 333 } 334 335 static void myclk_Init(void) 336 { 337 clk_div_lcd = ioremap(0x1003c534,4); 338 clk_src_lcd0 = ioremap(0x1003c234,4); 339 lcdblk_cfg = ioremap(0x10010210,4); 340 lcdblk_cfg2 = ioremap(0x10010214,4); 341 342 /*时钟源选择MPLL具体参考<Exyons 4412 datasheet pg526> 343 *CLK_DIV_LCD: 344 *[3:0]:FIMD0_RATIO 0 345 *SCLK_FIMD0 = MOUTFIMD0/(FIMD0_RATIO + 1)=800M/1 = 800M 346 347 *<Exyons 4412 datasheet pg501> 348 *CLK_SRC_LCD0: 349 *[3:0]:FIMD0_SEL 0110 ===>0x6 SCLKmpll_user_t 选择时钟源为SCLKmpll_user_t 350 */ 351 writel(readl(clk_div_lcd)&~0xf,clk_div_lcd); 352 writel((readl(clk_src_lcd0)&~0xf)|0x6,clk_src_lcd0); 353 354 /*<Exyons 4412 datasheet pg1767> 355 *Using the display controller data, you can select one of the above data paths by 356 *setting LCDBLK_CFG Register (0x1001_0210). For more information, refer to the "System Others" manual. 357 *While using RGB interface, the VT_LBLKx bit fields in LCDBLKC_CFG (0x1001_0210) register should be set to 358 *RGB Interface out (2'b00), even though you use DSI Video Mode.*/ 359 360 /* 361 *<Exyons 4412 datasheet pg884> 362 *LCDBLK_CFG: 363 * [1] : FIMD of LBLK0 Bypass Selection 1 : FIMD Bypass 使用FIMD接口 364 *LCDBLK_CFG2: 365 * [0]:MIE0_DISPON 1 : PWM outpupt enable 366 */ 367 writel((readl(lcdblk_cfg)&(~(0x3<<10)))|(1<<1),lcdblk_cfg);//set FIMD 368 //writel(readl(lcdblk_cfg2)|(1<<0),lcdblk_cfg2); //set pwm 369 370 } 371 372 static void mypwm_Init(void) 373 { 374 tcfg0 = ioremap(LCD_PWM_TCFG0,4); 375 tcfg1 = ioremap(LCD_PWM_TCFG1,4); 376 tcon = ioremap(LCD_PWM_TCON,4); 377 tcntb1 = ioremap(LCD_PWM_TCNTB1,4); 378 tcmpb1 = ioremap(LCD_PWM_TCMPB1,4); 379 380 /*占空比控制背光亮度*/ 381 writel((readl(tcfg0) &(~0xff))|0xff,tcfg0); 382 writel((readl(tcfg1) &(~(0xf<<4)))|0x4<<4,tcfg1); 383 writel(300,tcntb1); 384 writel(150,tcmpb1); 385 386 writel((readl(tcon)&(~(0xf<<8)))|(0x1<<9),tcon); 387 388 //使能 389 writel((readl(tcon)&(~(0x1<<9)))|(0x1<<8),tcon); 390 391 } 392 393 static int lcd_Init(struct fb_info *myfb) 394 { 395 unsigned int val; 396 //lcd设置 397 vidcon0 =ioremap(0x11c00000,0x4); 398 vidcon1 =ioremap(0x11c00004,0x4); 399 vidcon2 =ioremap(0x11c00008,0x4); 400 vidtcon0 = ioremap(0x11c00010,0x4); 401 vidtcon1 = ioremap(0x11c00014,0x4); 402 vidtcon2 = ioremap(0x11c00018,0x4); 403 win0map = ioremap(0x11C00180, 0x4); 404 wincon0 = ioremap(0x11c00020, 0x4); 405 vidoso0a = ioremap(0x11c00040,0x4); 406 vidoso0b = ioremap(0x11c00044,0x4); 407 vidoso0c = ioremap(0x11c00048,0x4); 408 shaadowcon = ioremap(0x11c00034,0x4); 409 winchmap2 =ioremap(0x11c0003c,0x4); 410 vidw00add0b0 = ioremap(0x11c000a0,0x4); 411 vidw00add1b0 = ioremap(0x11c000d0,0x4); 412 vidw00add2 = ioremap(0x11C00104,0x4); 413 414 /*HV mode fclk :44.9~63 推荐51.2Mhz 参考 415 *vidcon0 [13-6] VCLK = FIMD*SCLK/(CLKVAL+1) 416 *50M = 800M/(CLK_VAL+1) CLK_VAL= 15 这里为了取整,取50M 417 *CLK_VAL为vidcon0 [13-6]这7位的十进制值,15为0001111,左移6位到[13-6], 418 *注意并不是将13位转化为10进制,而是单独7位 419 */ 420 //writel(15<<6, vidcon0); 421 writel((15<<6|3), vidcon0); 422 /* 423 *:VSYNC,HSYNC高电平触发信号,VCLK上升沿开始传输数据 424 *:VSD,HSD低电平触发信号,CLKV下降沿开始传输数据 425 * 426 * VIDCON1: 427 * [5]:IVSYNC ===> 1 : Inverted(反转) 428 * [6]:IHSYNC ===> 1 : Inverted(反转) 429 * [7]:IVCLK ===> 1 : Fetches video data at VCLK rising edge (下降沿触发,反转?) 430 * [10:9]:FIXVCLK ====> 01 : VCLK running� 431 * */ 432 writel((1<<9)|(1<<7)|(1<<5)|(1<<6),vidcon1); 433 //Reserved: This bit should be set to 1. 434 writel(1<<14, vidcon2); 435 436 /*参考 HV mode 437 * thbp: 160 (DCLK) 438 * thfp: 160 (DCLK) 439 * thpw: 1-140 (DCLK) 440 * tvpw: 1-20 (Th) 441 * ttvbp: 23 (Th) 442 * tvfp: 12 (Th) 443 */ 444 /* 445 *<Exyons 4412 datasheet pg1874 pg1848(时序)> <参考具体LCD屏参数(时序)> 446 *VIDTCON0: 447 * [23:16]: VBPD + 1 <------> tvpw (1 - 20) 13 448 * [15:8]: VFPD + 1 <------> tvfp 22 449 * [7:0]: VSPW + 1 <------> tvb - tvpw = 23 - 13 = 10 450 **/ 451 /*VIDTCONx用来设置时序和长宽等参数,这里就主要设置VBPD(vertical back porch)、 452 VFBD(vertical frontporch)、VSPW(vertical sync pulse width)、 453 HBPD(horizontal backporch)、 HFPD(horizontal sync pul se width)等参数 454 */ 455 /*<Exyons 4412 datasheet pg1874 pg1848(时序)> <参考具体LCD屏参数(时序)> 456 *VIDTCON1: 457 * [23:16]: HBPD + 1 <------> thpw (1 - 40) 36 458 * [15:8]: HFPD + 1 <------> thfp 210 459 * [7:0]: HSPW + 1 <------> thb - thpw = 46 - 36 = 10 460 */ 461 462 #define HSPW 31 //行同步 463 #define HBPD 79 464 #define HFPD 47 465 #define VSPW 4 //帧同步 466 #define VBPD 13 467 #define VFPD 2 468 writel(((VBPD <<16)|(VFPD <<8)|(VSPW <<0)),vidtcon0); 469 writel(((HBPD <<16)|(HFPD <<8)|(HSPW <<0)),vidtcon1); 470 471 /*设置大小,参考 Display Format Graphic 1024RGB*600 Dot-matrix 472 * HOZVAL = (Horizontal display size) – 1 and LINEVAL = (Vertical display size) – 1. 473 */ 474 writel(((1023<<0) | (599<<11)), vidtcon2); 475 476 /*绑定win,这里直接使用了win0 477 * TODO:0xd=32位,这里强行写的,具体需要参考实际情况写16,24或32 478 */ 479 //writel((1<<6)|(0xD<<2)|1,wincon0); 480 writel((1<<6)|(5<<2)|1,wincon0); 481 482 /*LCD左上角坐标*/ 483 writel(0<<11|0, vidoso0a); 484 485 /* LCD右下角坐标 参考 486 * 24 BPP mode should have X position by 1 pixel. (For example, X = 0, 1, 2, 3….) 487 * 16 BPP mode should have X position by 2 pixel. (For example, X = 0, 2, 4, 6….) 488 * 8 BPP mode should have X position by 4 pixel. (For example, X = 0, 4, 8, 12….) 489 */ 490 if(myLcdBpp == 16) 491 { 492 val = (((1023*2) <<11) | ((599*2) <<0)); 493 } 494 else if((myLcdBpp == 24) || (myLcdBpp == 32)) 495 { 496 val = (((1023) <<11) | ((599) <<0)); 497 } 498 writel(val, vidoso0b); 499 500 /*总大小*/ 501 writel(1024*600, vidoso0c); 502 503 /*帧缓冲起始地址*/ 504 writel(myfb->fix.smem_start, vidw00add0b0); 505 506 /*帧缓冲结束地址*/ 507 writel(myfb->fix.smem_start+myfb->fix.smem_len, vidw00add1b0); 508 509 /*设置偏移和宽度*/ 510 writel((0<<13) | ((1024 * myLcdBpp / 8)<<0), vidw00add2); 511 512 /*绑定通道和窗口*/ 513 writel(1, shaadowcon); 514 writel((readl(winchmap2)&(~(0x7<<16))&(~0x7))|(1<<16)|(1<<0), winchmap2); 515 516 /*Enables the video output and video control signal*/ 517 writel(readl(vidcon0)|0x3, vidcon0); 518 return 0; 519 } 520 521 static int fs4412_lcd_init(void) 522 { 523 int ret = 0; 524 /************************kernel相关*******************************/ 525 /**这部分参考s3c-fb.c**/ 526 /*申请fb_info*/ 527 my_fbInfo = framebuffer_alloc(0,NULL); //这里没有私有数据,所以是0;dev没有,平台驱动需要添加 528 529 /*初始化fb_info*/ 530 myfb_Init(my_fbInfo); 531 /************************硬件相关*******************************/ 532 /*管脚设置*/ 533 mygpio_Init(); 534 /*时钟设置*/ 535 myclk_Init(); 536 /*控制芯片设置*/ 537 mypwm_Init(); 538 lcd_Init(my_fbInfo); 539 540 541 /*注册*/ 542 ret = register_framebuffer(my_fbInfo); 543 if(0> ret){ 544 printk(KERN_ERR "failed to register framebuffer "); 545 //dma_free_coherent(fs_info->dev, fs_info->fix.smem_len,fs_info->screen_base, fs_info->fix.smem_start); 546 return ret; 547 } 548 printk("Lcd init ok "); 549 return 0; 550 } 551 552 553 static void fs4412_lcd_exit(void) 554 { 555 printk("Lcd exit "); 556 unregister_framebuffer(my_fbInfo); 557 iounmap(gpf0con); 558 iounmap(gpf1con); 559 iounmap(gpf2con); 560 iounmap(gpf3con); 561 //myfb->dev, size,&map_dma, GFP_KERNEL 562 563 /*释放DMA缓存地址dma_free_writecombine()*/ 564 dma_free_writecombine(my_fbInfo->dev,my_fbInfo->screen_size,my_fbInfo->screen_base,my_fbInfo->fix.smem_start); 565 framebuffer_release(my_fbInfo); 566 } 567 568 module_init(fs4412_lcd_init); 569 module_exit(fs4412_lcd_exit);
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <fcntl.h> 4 #include <linux/fb.h> 5 #include <sys/mman.h> 6 #include <errno.h> 7 #include <stdlib.h> 8 //#include "logo1.h" 9 char *framebuffer_ptr; 10 unsigned int xsize = 1024; 11 unsigned int ysize = 600; 12 unsigned int bpp = 16; 13 void PutPixel(unsigned int x, unsigned int y, unsigned int color) 14 { 15 unsigned char red,green,blue; 16 switch (bpp){ 17 case 16: 18 { 19 unsigned short *addr = (unsigned short *)framebuffer_ptr + (y * xsize + x); 20 red = (color >> 19) & 0x1f; 21 green = (color >> 10) & 0x3f; 22 blue = (color >> 3) & 0x1f; 23 color = (red << 11) | (green << 5) | blue; // 格式5:6:5 24 *addr = (unsigned short) color; 25 break; 26 } 27 28 case 8: 29 { 30 unsigned char *addr = (unsigned char *)framebuffer_ptr + (y * xsize + x); 31 *addr = (unsigned char) color; 32 break; 33 } 34 35 default: 36 break; 37 } 38 } 39 /* 40 * 绘制同心圆 41 */ 42 void Mire(void) 43 { 44 unsigned long x,y; 45 unsigned long color; 46 unsigned char red,green,blue,alpha; 47 48 for (y = 0; y < ysize; y++) 49 for (x = 0; x < xsize; x++){ 50 color = ((x-xsize/2)*(x-xsize/2) + (y-ysize/2)*(y-ysize/2))/64; 51 red = (color/8) % 256; 52 green = (color/4) % 256; 53 blue = (color/2) % 256; 54 alpha = (color*2) % 256; 55 56 color |= ((unsigned long)alpha << 24); 57 color |= ((unsigned long)red << 16); 58 color |= ((unsigned long)green << 8 ); 59 color |= ((unsigned long)blue ); 60 61 PutPixel(x,y,color); 62 } 63 } 64 /* 65 * 将屏幕清成单色 66 * 输入参数: 67 * color: 颜色值 68 * 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度), 69 * 需要转换为5:6:5格式 70 * 对于8BPP: color为调色板中的索引值, 71 * 其颜色取决于调色板中的数值 72 */ 73 void ClearScr(unsigned int color) 74 { 75 unsigned long x,y; 76 77 for (y = 0; y < ysize; y++) 78 for (x = 0; x < xsize; x++) 79 PutPixel(x, y, color); 80 } 81 int bmp2fb16_rgb565(unsigned char *bmpdata,unsigned short *fb16) 82 { 83 int x, y; 84 unsigned short r, g, b; 85 unsigned short pixel16; 86 unsigned short * fb16_buff; 87 88 fb16_buff = fb16; 89 for(y = ysize-1; y >= 0 ; y--) 90 { 91 for (x = 0 ; x < xsize; x++) /*copy one line to frame buffer*/ 92 { 93 /*copy one pixel to frame buffer*/ 94 b = *bmpdata; 95 bmpdata++; 96 b >>= 3; 97 g = *bmpdata; 98 bmpdata++; 99 g >>= 2; 100 r = *bmpdata; 101 bmpdata++; 102 r >>= 3; 103 pixel16 = (unsigned short)((r << 11) | (g << 5) | b); 104 *(fb16_buff + (y* xsize + x)) = pixel16; 105 } 106 } 107 return 0; 108 } 109 110 int bmp_read_file(const char *bmpfilename,unsigned char **bmpdata) 111 { 112 int nread; 113 int raw_size = 0; 114 *bmpdata = bmpfilename +(unsigned char)54; 115 return 0; 116 } 117 118 void draw_bmp(char *bmpfilename, unsigned short *fb) 119 { 120 unsigned char * bmpdata; 121 int ret; 122 123 ret = bmp_read_file(bmpfilename, &bmpdata); 124 if (ret) 125 { 126 printf("read bmpfile error. "); 127 } 128 printf("bmpread0000000!!!! "); 129 bmp2fb16_rgb565(bmpdata, fb); 130 printf("bm2fb1600000000000!!!! "); 131 } 132 //./lcd_test xxx.jpg 133 int main(int argc,char **argv) 134 { 135 int fp = 0; 136 struct fb_var_screeninfo vinfo; 137 struct fb_fix_screeninfo finfo; 138 139 int screensize = 0; 140 //char *fbp = 0; 141 //int x = 0, y = 0; 142 //int location = 0; 143 int bytes_per_pixel; 144 int pic_fd; 145 unsigned long len; 146 unsigned char *buffer; 147 int key; 148 int i; 149 150 /*1.打开一副图片*/ 151 pic_fd =open(argv[1],O_RDWR); 152 printf("pic_fd=%d ",pic_fd); 153 154 /*2.获取图片大小*/ 155 len =lseek(pic_fd, 0, SEEK_END); 156 printf("len =%ld ",len); 157 158 buffer =(unsigned char *)malloc(len); 159 160 lseek(pic_fd, 0, SEEK_SET); 161 /*3.读取图片数据*/ 162 read(pic_fd,buffer,len); 163 164 165 fp = open("/dev/fb0", O_RDWR); 166 if(fp < 0) { 167 printf("open failed "); 168 } 169 170 if(ioctl(fp, FBIOGET_FSCREENINFO, &finfo)) { 171 perror("ioctl"); 172 } 173 174 if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)) { 175 perror("ioctl"); 176 } 177 178 bytes_per_pixel = vinfo.bits_per_pixel/8; 179 screensize = vinfo.xres * vinfo.yres * bytes_per_pixel; 180 printf("x = %d y = %d bytes_per_pixel = %d ", vinfo.xres, vinfo.yres, bytes_per_pixel); 181 printf("screensize = %d ", screensize); 182 183 framebuffer_ptr = (char*) mmap(0, screensize, PROT_READ |PROT_WRITE, MAP_SHARED, fp, 0); 184 if(framebuffer_ptr < 0) { 185 perror("mmap"); 186 } 187 printf("line_length = %d ",finfo.line_length); 188 #if 0 189 for(x = 100; x<200; x++) { 190 for(y=100; y<200; y++) { 191 location = x * bytes_per_pixel + y * finfo.line_length; 192 *(framebuffer_ptr + location) = 0; 193 *(framebuffer_ptr +location + 1) = 255; 194 *(framebuffer_ptr + location + 2) = 0; 195 *(framebuffer_ptr + location + 3) = 0; 196 } 197 } 198 #endif 199 200 while(1) 201 { 202 /*2.图像处理*/ 203 scanf("%d",&key); 204 switch(key) 205 { 206 case 1: 207 /*2.7 显示图片*/ 208 printf("test picture! "); 209 210 printf("loadjpeg00000!!!! "); 211 draw_bmp(buffer,(unsigned short *) framebuffer_ptr); 212 break; 213 case 2: 214 ClearScr(0x00ff0000); 215 break; 216 case 3: 217 Mire(); 218 break; 219 default: 220 break; 221 } 222 } 223 munmap(framebuffer_ptr, screensize); 224 close(fp); 225 return 0; 226 }
1 ROOTFS_DIR = /source/rootfs 2 3 MODULE_NAME = lcd_drv 4 APP_NAME = lcd_test 5 6 CROSS_COMPILE = /home/kevin/Linux_4412/toolchain/gcc-4.6.4/bin/arm-none-linux-gnueabi- 7 CC = $(CROSS_COMPILE)gcc 8 9 ifeq ($(KERNELRELEASE), ) 10 11 KERNEL_DIR = /home/kevin/Linux_4412/linux-3.14-fs4412 12 CUR_DIR = $(shell pwd) 13 14 all : 15 make -C $(KERNEL_DIR) M=$(CUR_DIR) modules 16 $(CC) $(APP_NAME).c -o $(APP_NAME) 17 18 clean : 19 make -C $(KERNEL_DIR) M=$(CUR_DIR) clean 20 rm -rf $(APP_NAME) 21 22 install: 23 cp -raf *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module 24 25 26 else 27 28 obj-m += $(MODULE_NAME).o 29 30 31 endif
gt811的编程思路:
1, 为gt811需要一个i2c client提供从设备信息
arch/arm/mach-s5pv210/mach-smdkv210.c
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
/* To Be Updated */
{ I2C_BOARD_INFO("gt811_i2c_ts", 0x5d), },
};
make zImage -j2
更新内核:
cp -raf arch/arm/boot/zImage /tftpboot/
2, 构建i2c_driver, 注册到总线
实现probe()
|
1, 构建一个input device
2, 初始化 input device
3, 注册input device
4, 硬件初始化:
a, 上电初始化
1, 设置中断引脚为悬浮输入态,RESET设置成GPIO(内部上拉)
2, RESET引脚设置成输出低(开始复位), 延时1ms, 转成输入态
3, 延迟至少20ms,通过i2c寻址gt811 (确认复位是否成功)
4,如果有响应,分一次或者多次初始化寄存器
5,如果没有响应,重复2步骤
b, 申请中断--request_irq(), 还要用中断下半部(tasklet, 工作队列(选择这个))
|
1,通过i2c_tranfser(会导致休眠)读取到坐标
2, 分析坐标
3, 上报坐标
------------------------------------------------------------
linux驱动中:对于多点触摸屏, 实际需要一个多点上报协议:
Documentation/input$ ls multi-touch-protocol.txt
Here is what a minimal event sequence for a two-contact touch would look
like for a type A device:
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT //第0点上报完毕
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT //第1点上报完毕
SYN_REPORT //所有点上报完毕
在代码中实现:
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_X, x);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_Y, y);
input_mt_sync(gt811_dev->inputdev);
|
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_X, x);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_Y, y);
input_mt_sync(gt811_dev->inputdev);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_X, x);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_Y, y);
input_mt_sync(gt811_dev->inputdev);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_X, x);
input_event(gt811_dev->inputdev, EV_ABS, ABS_MT_POSITION_Y, y);
input_mt_sync(gt811_dev->inputdev);
input_sync(gt811_dev->inputdev); //所有点上报完毕