首页 > 操作系统

驱动05.lcd设备驱动程序

2017-01-11 00:01:35

参考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_start114 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 signal159 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 support2. make uImage  make modules 3. 使用新的uImage启动开发板:4. insmod cfbcopyarea.ko insmod cfbfillrect.ko insmod cfbimgblt.ko insmod lcd.koecho hello > /dev/tty1 // 可以在LCD上看见hellocat lcd.ko > /dev/fb0  // 花屏5. 修改 /etc/inittabtty1::askfirst:-/bin/sh用新内核重启开发板insmod cfbcopyarea.ko insmod cfbfillrect.ko insmod cfbimgblt.ko insmod lcd.koinsmod buttons.ko

2.3 待解决:1.出现段错误。2.用新内核启动后无法挂载nfs。

2017-01-10 16:12:27