參考s3c2410fb.c總結出框架 1.代碼分析 1.1 入口函數 註冊一個platform_driver結構體,如果存在同名的設備dev時,將調用probe函數。 搜索s3c2410-lcd可得下麵的s3c_device_lcd結構體 1.2 probe函數(只列出關鍵性代碼) 由此可知,其主要 ...
參考s3c2410fb.c總結出框架
1.代碼分析
1.1 入口函數
1 int __devinit s3c2410fb_init(void) 2 { 3 return platform_driver_register(&s3c2410fb_driver); 4 }
註冊一個platform_driver結構體,如果存在同名的設備dev時,將調用probe函數。
1 static struct platform_driver s3c2410fb_driver = { 2 .probe = s3c2410fb_probe, 3 .remove = s3c2410fb_remove, 4 .suspend = s3c2410fb_suspend, 5 .resume = s3c2410fb_resume, 6 .driver = { 7 .name = "s3c2410-lcd", //如果存在有同名"s3c2410-lcd"的平臺設備,就會調用s3c2410fb_driver的s3c2410fb_probe函數 8 .owner = THIS_MODULE, 9 }, 10 }; //這是s3c2410fb_driver這個結構體的具體成員
搜索s3c2410-lcd可得下麵的s3c_device_lcd結構體
1 struct platform_device s3c_device_lcd = { 2 .name = "s3c2410-lcd", 3 .id = -1, 4 .num_resources = ARRAY_SIZE(s3c_lcd_resource), 5 .resource = s3c_lcd_resource, //最重要的部分 6 .dev = { 7 .dma_mask = &s3c_device_lcd_dmamask, 8 .coherent_dma_mask = 0xffffffffUL 9 } 10 };
1.2 probe函數(只列出關鍵性代碼)
1 static int __init s3c2410fb_probe(struct platform_device *pdev) 2 { 3 struct s3c2410fb_info *info; 4 struct fb_info *fbinfo; 5 ...... 6 7 fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); 8 if (!fbinfo) { 9 return -ENOMEM; 10 } 11 ...... 12 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 13 fbinfo->fix.type_aux = 0; 14 fbinfo->fix.xpanstep = 0; 15 fbinfo->fix.ypanstep = 0; 16 fbinfo->fix.ywrapstep = 0; 17 fbinfo->fix.accel = FB_ACCEL_NONE; 18 19 fbinfo->var.nonstd = 0; 20 fbinfo->var.activate = FB_ACTIVATE_NOW; 21 fbinfo->var.height = mach_info->height; 22 fbinfo->var.width = mach_info->width; 23 fbinfo->var.accel_flags = 0; 24 fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 25 ....... 26 ret = register_framebuffer(fbinfo); 27 return 0; 28 29 }
由此可知,其主要框架是
(1)分配一個fb_info結構體
(2)設置其參數
(3)註冊這個結構體
(4)硬體相關的操作
1.3 fb_info結構體成員的瞭解
lcd為標準的幀緩衝設備,其主設備號為29,對應的設備為/dev/fb*
1 struct fb_info { 2 int node; //用作次設備號索引 3 int flags; 4 struct mutex lock; //用於open/release/ioctl函數的鎖 5 struct fb_var_screeninfo var; //可變參數,重點 6 struct fb_fix_screeninfo fix; //固定參數,重點 7 struct fb_monspecs monspecs; //顯示器標準 8 struct work_struct queue; //幀緩衝區隊列 9 struct fb_pixmap pixmap; //圖像硬體映射 10 struct fb_pixmap sprite; //游標硬體映射 11 struct fb_cmap cmap; //當前顏色表 12 struct list_head modelist; //模式鏈表 13 struct fb_videomode *mode; //當前video模式 14 15 char __iomem *screen_base; //顯存基地址 16 unsigned long screen_size; //顯存大小 17 void *pseudo_palette; //16色調色板 18 #define FBINFO_STATE_RUNNING 0 19 #define FBINFO_STATE_SUSPENDED 1 20 u32 state; //硬體狀態,如掛起 21 void *fbcon_par; //用作私有數據區 22 void *par; //info->par指向了額外多申請記憶體空間的首地址 23 };
另外,fb_fix_screeninfo和fb_var_screeninfo也是兩個比較重要的結構體,在設置fb_info結構體時會大量用到。
1 struct fb_fix_screeninfo { 2 char id[16]; /* identification string eg "TT Builtin" */ 3 unsigned long smem_start; /* Start of frame buffer mem */ 4 /* (physical address) */ 5 __u32 smem_len; /* Length of frame buffer mem */ 6 __u32 type; /* see FB_TYPE_* */ 7 __u32 type_aux; /* Interleave for interleaved Planes */ 8 __u32 visual; /* see FB_VISUAL_* */ 9 __u16 xpanstep; /* zero if no hardware panning */ 10 __u16 ypanstep; /* zero if no hardware panning */ 11 __u16 ywrapstep; /* zero if no hardware ywrap */ 12 __u32 line_length; /* length of a line in bytes */ 13 unsigned long mmio_start; /* Start of Memory Mapped I/O */ 14 /* (physical address) */ 15 __u32 mmio_len; /* Length of Memory Mapped I/O */ 16 __u32 accel; /* Indicate to driver which */ 17 /* specific chip/card we have */ 18 __u16 reserved[3]; /* Reserved for future compatibility */ 19 };
struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* != 0 Graylevels instead of colors */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ __u32 reserved[5]; /* Reserved for future compatibility */ };
1.4 fb_open函數
app: open("/dev/fb0"...) 主設備號:29 次設備號:0
-----------------------------------------------------------
kernel:
fb_open
int fbidx = iminor(inode)//獲取次設備號
struct fb_info *info = registered_fb[fbidx]
1.5 fb_read函數
app: read()
------------------------------------------------------------
kernel:
fb_read
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
src = (u32 __iomem *) (info->screen_base + p);
*dst++ = fb_readl(src++);
copy_to_user(buf, buffer, c)
1.6 registered_fb數組由誰來定義?
register_framebuffer
registered_fb[i] = fb_info
2 寫代碼
由1.2我們可以得知,代碼的總體框架為:
(1)分配一個fb_info結構體
(2)設置其參數
(3)註冊這個結構體
(4)硬體相關的操作
其實難點就在於第(2)步,主要是設置fb_info結構體的固定參數 fb_fix_screeninfo結構體和可變參數fb_var_screeninfo結構體,還有就是硬體相關
的設置,比如lcd時序參數的設置,也就是要設置lcdcon1~lcdcon5,lcdaddr1~lcdaddr3個寄存器。
2.2 源代碼
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 struct lcd_regs{ 28 unsigned long lcdcon1; 29 unsigned long lcdcon2; 30 unsigned long lcdcon3; 31 unsigned long lcdcon4; 32 unsigned long lcdcon5; 33 unsigned long lcdsaddr1; 34 unsigned long lcdsaddr2; 35 unsigned long lcdsaddr3; 36 unsigned long redlut; 37 unsigned long greenlut; 38 unsigned long bluelut; 39 unsigned long reserved[9]; 40 unsigned long dithmode; 41 unsigned long tpal; 42 unsigned long lcdintpnd; 43 unsigned long lcdsrcpnd; 44 unsigned long lcdintmsk; 45 unsigned long tconsel; 46 }; 47 48 static struct fb_info *s3c_lcd; 49 static volatile unsigned long *gpbcon; 50 static volatile unsigned long *gpbdat; 51 static volatile unsigned long *gpccon; 52 static volatile unsigned long *gpdcon; 53 static volatile unsigned long *gpgcon; 54 static u32 pseudo_palette[16]; 55 static volatile struct lcd_regs *lcd_regs; 56 57 58 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) 59 { 60 chan &= 0xffff; 61 chan >>= 16 - bf->length; 62 return chan << bf->offset; 63 } 64 65 static int s3c_lcdfb_setcolreg(unsigned regno, 66 unsigned red, unsigned green, unsigned blue, 67 unsigned transp, struct fb_info *info) 68 { 69 unsigned int val; 70 71 if (regno < 16) 72 { 73 // u32 *pal = fbi->fb->pseudo_palette; 74 val = chan_to_field(red, &info->var.red); 75 val |= chan_to_field(green, &info->var.green); 76 val |= chan_to_field(blue, &info->var.blue); 77 pseudo_palette[regno] = val; 78 } 79 else 80 return 1; 81 82 return 0; 83 84 } 85 86 static struct fb_ops s3clcdfb_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 static int lcd_init(void) 95 { 96 int ret; 97 /*1.分配一個fb_info結構體*/ 98 s3c_lcd = framebuffer_alloc(0, NULL); 99 if (!s3c_lcd) { 100 return -ENOMEM; 101 } 102 103 104 /*2.設置 */ 105 strcpy(s3c_lcd->fix.id, "mylcd"); 106 107 /*2.1設置固定數據fix*/ 108 s3c_lcd->fix.smem_len = 480*272*16/8; 109 s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS; 110 s3c_lcd->fix.type_aux = 0; 111 s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;//TFT真彩色 112 s3c_lcd->fix.line_length = 480*16/8; 113 // s3c_lcd->fix.smem_start 114 115 /*2.2設置可變參數var*/ 116 s3c_lcd->var.xres = 480; 117 s3c_lcd->var.yres = 272; 118 s3c_lcd->var.xres_virtual = 480; 119 s3c_lcd->var.yres_virtual = 272; 120 s3c_lcd->var.bits_per_pixel = 16; 121 122 /*RGB:565*/ 123 s3c_lcd->var.red.offset= 11; 124 s3c_lcd->var.red.length = 5; 125 s3c_lcd->var.green.offset = 5; 126 s3c_lcd->var.green.length = 6; 127 s3c_lcd->var.blue.offset = 0; 128 s3c_lcd->var.blue.length = 5; 129 130 s3c_lcd->var.activate = FB_ACTIVATE_NOW; 131 132 s3c_lcd->fbops = &s3clcdfb_ops; 133 s3c_lcd->pseudo_palette = pseudo_palette; 134 s3c_lcd->screen_size = 480*272*16/8; 135 136 /*3.硬體相關的操作*/ 137 /*3.1GPIO的初始化*/ 138 gpbcon = ioremap(0x56000010, 8); 139 gpbdat = gpbcon+1; 140 gpccon = ioremap(0x56000020, 4); 141 gpdcon = ioremap(0x56000030, 4); 142 gpgcon = ioremap(0x56000060, 4); 143 144 *gpccon = 0xaaaaaaaa; 145 *gpdcon = 0xaaaaaaaa; 146 147 *gpbcon &= ~(3); 148 *gpbcon |= 1; 149 *gpbdat &= ~1;//背光使能 150 151 *gpgcon |= (3<<8); //LCD 電源使能 152 153 /*3.2 設置LCD controller*/ 154 lcd_regs = ioremap(0x4D000000,sizeof(struct lcd_regs)); 155 156 157 lcd_regs->lcdcon1 = (4<<8) |(3<<5) |(0x0c<<1); 158 //lcdcon1[0] Enable the video output and the LCD control signal 159 160 lcd_regs->lcdcon2 = (1<<24) |(271<<14) |(1<<6) |(9); 161 162 lcd_regs->lcdcon3 = (1<<19) |(479<<8) |(1); 163 164 lcd_regs->lcdcon4 = 40; 165 166 lcd_regs->lcdcon5 = (1<<11) |(1<<9) |(1<<8) |(1<<0); 167 168 /*3.3 設置顯存的地址*/ 169 s3c_lcd->screen_base = dma_alloc_writecombine(NULL,s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start,GFP_KERNEL); 170 171 lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30); 172 lcd_regs->lcdsaddr2 = (s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) & 0x1fffff; 173 lcd_regs->lcdsaddr3 = (480*16/16);//行的長度 174 175 176 /*啟動lcd*/ 177 178 lcd_regs->lcdcon1 |= (1);//使能LCD控制器 179 lcd_regs->lcdcon5 |= (1<<3);//使能LCD電源 180 *gpbdat |=1; //使能背光 181 182 183 /*4.註冊該結構體*/ 184 ret = register_framebuffer(s3c_lcd); 185 if (ret < 0) { 186 printk("Failed to register framebuffer device: %d\n", ret); 187 } 188 189 return 0; 190 } 191 192 static int lcd_exit(void) 193 { 194 unregister_framebuffer(s3c_lcd); 195 lcd_regs->lcdcon1 &= ~1; 196 lcd_regs->lcdcon5 &= ~(1<<3); 197 *gpbdat &= ~1; 198 199 dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start); 200 iounmap(lcd_regs); 201 iounmap(gpbcon); 202 iounmap(gpccon); 203 iounmap(gpdcon); 204 iounmap(gpgcon); 205 206 framebuffer_release(s3c_lcd); 207 return 0; 208 } 209 210 module_init(lcd_init); 211 module_exit(lcd_exit); 212 213 MODULE_AUTHOR("lwd20170110"); 214 215 MODULE_LICENSE("GPL");lcd驅動程式
測試: 1. make menuconfig去掉原來的驅動程式 -> Device Drivers -> Graphics support <M> S3C2410 LCD framebuffer support 2. make uImage make modules 3. 使用新的uImage啟動開發板: 4. insmod cfbcopyarea.ko insmod cfbfillrect.ko insmod cfbimgblt.ko insmod lcd.ko echo hello > /dev/tty1 // 可以在LCD上看見hello cat lcd.ko > /dev/fb0 // 花屏 5. 修改 /etc/inittab tty1::askfirst:-/bin/sh 用新內核重啟開發板 insmod cfbcopyarea.ko insmod cfbfillrect.ko insmod cfbimgblt.ko insmod lcd.ko insmod buttons.ko
2.3 待解決:1.出現段錯誤。2.用新內核啟動後無法掛載nfs。
2017-01-10 16:12:27