Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631436
  • 博文数量: 75
  • 博客积分: 988
  • 博客等级: 准尉
  • 技术积分: 1269
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-10 15:44
文章分类

全部博文(75)

文章存档

2022年(1)

2019年(1)

2018年(1)

2016年(9)

2015年(7)

2013年(6)

2012年(40)

2011年(10)

分类: LINUX

2012-03-20 16:34:05

字符设备驱动的主要由以下几部分构成:
主设备号
file_operations
register_chrdev
入口
出口

今天要讨论的LCD驱动,首先分析fbmem.c LCD核心层,通过分析代码可知,在核心层里已经有 主设备号、file_operations、register_chrdev.然而这些只是抽象出的核心层,可以参考Atmel_fb.c来写出硬件相关的东西。在LCD驱动硬件相关的东西里面,有很大部分是寄存器相关的一些设置。



LCD驱动程序:
假设打开的文件主设备号为:29,次设备号为:0

应用程序: open("/dev/fb0" ...) 主设备号: 29, 次设备号: 0
------------------------------------------------------------
kernel:
fb_open
int fbidx = iminor(inode); // 得到设备节点的次设备号。
//struct fb_info *info == registered_fb[fbidx];
struct fb_info *info == registered_fb[0]; // 假设次设备号为0
应用程序: read()
-------------------------------------------------------------
kernel:
fb_read
//int fbidx = iminor(inode);
int 0 = iminor(inode);
struct fb_info *info = registered_fb[fbidx];  
//src = (u32 __iomem *) (info->screen_base + p);   // screen_base显存基地址
if (info->fbops->fb_read)                   // 如果有读函数
return info->fbops->fb_read(info, buf, count, ppos);
src = (u32 __iomem *) (info->screen_base + p); // 如果没有读函数
dst = buffer;
*dst++ = fb_readl(src++);
copy_to_user(buf, buffer, c)

---------------------------------------------------------------

综上,read和open都依赖于fb_info结构体,而fb_info结构体又依赖于regiseted_fb以次设备号为下标得到。

 registered_fb[fbidx]数组在哪里被设置?
 register_framebuffer (向核心层注册硬件设备的信息如,s3c2410fb.c、Atmel_fb.c等)

怎么写LCD驱动程序?
1,分配一个fb_info结构体    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev)
2,设置
3,注册  register_framebuffer
4,硬件相关的操作  ioctrl  FBIOGET_VSCREENINFO(可变的屏幕信息)其中的V即为Var
if (!fb)
return -ENODEV;
switch (cmd) {
case FBIOGET_VSCREENINFO:
return copy_to_user(argp, &info->var,
   sizeof(var)) ? -EFAULT : 0;
struct fb_info {
int node;
int flags;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist;      /* mode list */
struct fb_videomode *mode; /* current mode */
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_fix_screeninfo {              /* 固定参数 */
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */ 
__u16 xpanstep; /* zero if no hardware panning  */
__u16 ypanstep; /* zero if no hardware panning  */
__u16 ywrapstep; /* zero if no hardware ywrap    */
__u32 line_length; /* length of a line in bytes    */
unsigned long mmio_start; /* Start of Memory Mapped I/O   */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O  */
__u32 accel; /* Indicate to driver which */
/*  specific chip/card we have */
__u16 reserved[3]; /* Reserved for future compatibility */
};


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,make menuconfig去掉原来的驱动程序,以避免对新的驱动程序有影响,去掉的是 S3C2410 LCD framebuffer support编译成模块,因为还要调用里面的cfbcopyarea.ko,cfbfillrect.ko,cfbimgblt.ko。
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
按回车,然后在LCD屏上就可以通过之前的那个buttons.ko按键程序来操作了。

s3c_lcd.c

点击(此处)折叠或打开

  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/arch/regs-lcd.h>
  21. #include <asm/arch/regs-gpio.h>
  22. #include <asm/arch/fb.h>

  23. static int s3c_lcdfb_setcolreg(unsigned int regno,unsigned int red,
  24.                                  unsigned int green,unsigned int blue,
  25.                                  unsigned int transp,struct fb_info * info);


  26. struct lcd_regs {            /* 根据2440手册将要配置的寄存器放到一个结构体中,以简化配置 */
  27.     unsigned long lcdcon1;
  28.     unsigned long lcdcon2;
  29.     unsigned long lcdcon3;
  30.     unsigned long lcdcon4;
  31.     unsigned long lcdcon5;
  32.     unsigned long lcdsaddr1;
  33.     unsigned long lcdsaddr2;
  34.     unsigned long lcdsaddr3;
  35.     unsigned long redlut;
  36.     unsigned long greenlut;
  37.     unsigned long bluelut;
  38.     unsigned long reserved[9];    /* 保留位 */
  39.     unsigned long dithmode;
  40.     unsigned long tpal;
  41.     unsigned long lcdintpnd;
  42.     unsigned long lcdsrcpnd;
  43.     unsigned long lcdintmsk;
  44.     unsigned long lpcsel;
  45. };
  46. static struct fb_ops s3c_lcdfb_ops = {
  47.     .owner        = THIS_MODULE,
  48.     .fb_setcolreg    = s3c_lcdfb_setcolreg,   /* 设置颜色寄存器 */
  49.     .fb_fillrect    = cfb_fillrect,
  50.     .fb_copyarea    = cfb_copyarea,
  51.     .fb_imageblit    = cfb_imageblit,
  52. };

  53. static struct fb_info *s3c_lcd;
  54. static volatile unsigned long *gpbcon;
  55. static volatile unsigned long *gpbdat;
  56. static volatile unsigned long *gpccon;
  57. static volatile unsigned long *gpdcon;
  58. static volatile unsigned long *gpgcon;
  59. static volatile struct lcd_regs *lcd_regs;
  60. static u32 pseudo_palette[16];

  61. static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf/* from pxafb.c */
  62. {
  63.     chan &= 0xffff;            /* 保留高16位 */
  64.     chan >>= 16 - bf->length; /* 当 bf->length 长度为5时,保留高5位,为6时保留高6位 */
  65.     return chan << bf->offset;  /* 保留相应的低位 */  
  66. }

  67. static int s3c_lcdfb_setcolreg(unsigned int regno,unsigned int red,
  68.                                  unsigned int green,unsigned int blue,
  69.                                  unsigned int transp,struct fb_info * info)
  70. {
  71.     unsigned int val;
  72.     if (regno > 16)   /* regno 颜色寄存器编号 */
  73.         return 1;
  74.     /* 用红、绿、蓝三原色构造出val值 */
  75.     val = chan_to_field(red, &info->var.red);
  76.     val|= chan_to_field(green, &info->var.green);
  77.     val|= chan_to_field(blue, &info->var.blue);
  78.     //((u32 *)(info->pseudo_palette))[regno] = val;
  79.     pseudo_palette[regno] = val;
  80.     return 0;
  81. }

  82. static int lcd_init(void)       /* 入口函数 */
  83. {
  84.     /* 1.分配一个fb_info结构体 */
  85.     s3c_lcd = framebuffer_alloc(0, NULL); // 应该对返回值进行判断,因为有时候可能内存不够。

  86.                                            // 为了减少代码,这里不做判断(不合理)
  87.     /* 2.设置 */
  88.     /* 2.1 设置固定的参数 */
  89.     strcpy(s3c_lcd->fix.id, "mylcd");
  90.     s3c_lcd->fix.smem_len = 240*320*16/8; // RGB 5 6 5 , 物理显存
  91.     s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
  92.     s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; // TFT屏是真彩色
  93.     s3c_lcd->fix.line_length = 240*2; // 一行为240个相素点,一个点为16位色,即为2字节。
  94.     /* 2.2 设置可变的参数 */
  95.     s3c_lcd->var.xres = 240;                // 物理分辨率
  96.     s3c_lcd->var.yres = 320;
  97.     s3c_lcd->var.xres_virtual = 240;        // 虚拟分辨率
  98.     s3c_lcd->var.yres_virtual = 320;
  99.     s3c_lcd->var.bits_per_pixel = 16;       // 每个象素点占的位
  100.     /* RGB 565 R begin form bit11 */
  101.     s3c_lcd->var.red.offset = 11;     /* 16位,11到16位为RED */
  102.     s3c_lcd->var.red.length = 5;
  103.     
  104.     s3c_lcd->var.green.offset = 5; /* G begin form bit5  5 到10 */
  105.     s3c_lcd->var.green.length = 6;
  106.     
  107.     s3c_lcd->var.blue.offset = 0; /* B begin form bit0 0到4*/
  108.     s3c_lcd->var.blue.length = 5;

  109.     s3c_lcd->var.activate = FB_ACTIVATE_NOW;


  110.     /* 2.3 设置操作函数 */
  111.     s3c_lcd->fbops = &s3c_lcdfb_ops;
  112.     /* 2.4 其他设置 */
  113.     s3c_lcd->pseudo_palette = pseudo_palette; /* 调色板 */
  114.     //s3c_lcd->screen_base = /* 显存的虚拟地址 */
  115.     s3c_lcd->screen_size = 240*320*16/8;

  116.     /* 3.硬件相关的操作 */
  117.     /* 3.1 配置GPIO用于LCD */
  118.     gpbcon = ioremap(0x56000010,8);
  119.     gpbdat = gpbcon + 1;
  120.     gpccon = ioremap(0x56000020,4);
  121.     gpdcon = ioremap(0x56000030,4);
  122.     gpgcon = ioremap(0x56000060,4);

  123.     *gpccon = 0xaaaaaaaa; /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
  124.     *gpdcon = 0xaaaaaaaa; /* GPIO管脚用于VD[23:8] */

  125.     *gpbcon &= ~(3); /* GPB0设置为输出引脚 */
  126.     *gpbcon |= 1;
  127.     *gpbdat &= ~1; /* 输出低电平,不让背光开启 */
  128.     *gpgcon |= (3<<8) /* GPG4用作LCD电源使能引脚LCD_PWEN */
  129.    
  130.     /* 3.2 根据LCD手册设置LCD控制器 */
  131.     lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
  132.     
  133.     /*bit[17:8]:VCLK = HCLK / [(CLKVAL+1) x 2]
  134.      * VCLK = 100MHz / [(CLKVAL+1) x 2] // 100MHz 可以通过dmesg查看。
  135.      * 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2] LCD手册第14页。
  136.      * ==>CLKVAL = 4;
  137.      *bit[6:5]: 0b11 TFT LCD
  138.      *bit[4:1]: 0b1100 ,16bpt for TFT 0b1100=0xc0h
  139.      *bit[0] : 0 = Disable the video output and the LCD control signal.
  140.      */
  141.     lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0xc<<1);
  142.     
  143.     /* 垂直方向的时间参数
  144.      *bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第一行数据
  145.      * LCD手册 T0-T2-T1 = 4
  146.      * VBPD = 3;
  147.      *bit[23:14]: 多少行,320行,所以LINEVAL = 320 - 1 = 319; 根据2440数据手册。
  148.      *bit[13:6] : VFPD发出最后一行数据之后再过多长时间才发出VSYNC信号
  149.      * LCD手册 T2-T0 = 322 - 320 = 2; 所以VFPD = 2-1 = 1;
  150.      *bit[5:0] : VSPW(脉冲宽度) VSYNC信号的脉冲宽度。LCD手册,T1=1,所以VSPW=1-1=0;
  151.      */
  152.     lcd_regs->lcdcon2 = (3<<24) | (319<<14) | (1<<6) | (0<<0);
  153.     
  154.     /* 水平方向的时间参数
  155.      *bit[25:19]: HBPD,HSYNC之后过多长时间才能发出第一个象素的数据
  156.      * LCD手册,T6-T7-T8=17;
  157.      * HBPD = 16;
  158.      *bit[18:8] : 多少列,240列,所以HOZVAL = 240 - 1 = 239;
  159.      *bit[7:0] : HFPD,发出一行里面最后一个象素数据之后,再过多长时间才发出HSYNC信号,
  160.      * LCD手册,T8-T11=251-240=11,所以HFPD=11-1=10;
  161.      */
  162.     lcd_regs->lcdcon3 = (16<<19) | (239<<8) | (10<<0);
  163.     
  164.     /* 水平方向的同步信号
  165.      *bit[7:0] : HSPW,HSYNC信号脉冲宽度,LCD手册T7=5,所以HSPW=5-1=4;
  166.      */
  167.     lcd_regs->lcdcon4 = (4<<0); // = 4

  168.     /* 信号的极性,只读的不用管
  169.      * bit[11] : 1= 5 6 5 format
  170.      * bit[10] : 0=反转VCLK,0 = The video data is fetched at VCLK falling edge
  171.      * bit[9] : 1=水平方向的同步信号要反转,即低电平有效
  172.      * bit[8] : 1=帧同步信号要反转,即低电平有效
  173.      * bit[7] : 0=数据不需要反转
  174.      * bit[6] : 0=VDEN不需要反转
  175.      * bit[5] : 0=电源使能信号,不需要反转
  176.      * bit[3] : 0=PWEN输出0
  177.      * bit[1] : 0=BSWP
  178.      * bit[0] : 1=HWSWP 看2440手册P413
  179.      */
  180.     lcd_regs->lcdcon5 = (1<<11) | (1<<9) | (1<<8) | (1<<0);
  181.     
  182.     /* 3.3 分配显存(framebuffer),并把地址告诉LCD控制器 */
  183.     s3c_lcd->screen_base = dma_alloc_writecombine(NULL,s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start,GFP_KERNEL);
  184.     /*
  185.      *
  186.      */
  187.     lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);
  188.     lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1) & 0x1fffff;
  189.     lcd_regs->lcdsaddr3 = (240/16/16); /* 一行的长度(单位: 半字(2字节)) */
  190.     //s3c_lcd->fix.smem_start =  /* 显存的物理地址 */
  191.     /* 启动LCD */
  192.     lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */
  193.     lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 这里如果不使能,LCD将不会有显示 */
  194.     *gpbdat |= 1;                /* 输出高电平,让背光开启 */
  195.     /* 4.注册 */

  196.     register_framebuffer(s3c_lcd);
  197.     
  198.     return 0;
  199. }

  200. static void lcd_exit(void)               /* 出口函数 */
  201. {
  202.     unregister_framebuffer(s3c_lcd);
  203.     lcd_regs->lcdcon1 &= ~(1<<0);        /* 关闭LCD控制器 */
  204.     *gpbdat &= ~1;                       /* 输出低电平,不让背光开启 */
  205.     dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
  206.     iounmap(lcd_regs);
  207.     iounmap(gpbcon);
  208.     iounmap(gpccon);
  209.     iounmap(gpdcon);
  210.     iounmap(gpgcon);
  211.     framebuffer_release(s3c_lcd);
  212. }

  213. module_init(lcd_init);
  214. module_exit(lcd_exit);

  215. MODULE_DESCRIPTION("Framebuffer driver for the s3c2440");
  216. MODULE_LICENSE("GPL");
写LCD驱动在参考别人的驱动程序的同时,还需要严格对照LCD数据手册,芯片的数据手册。
阅读(8277) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~