Linux LCD 驱动编写<二>

环境:linux-2.6.32.2,天嵌2440,东华3.5屏

  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 #include <asm/mach/map.h>
 21 
 22 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
 23                  unsigned int green, unsigned int blue,
 24                  unsigned int transp, struct fb_info *info);
 25 
 26 struct lcd_regs {
 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 
 47 static struct fb_ops s3c_lcdfb_ops = {
 48     .owner        = THIS_MODULE,
 49     .fb_setcolreg    = s3c_lcdfb_setcolreg,
 50     .fb_fillrect    = cfb_fillrect,
 51     .fb_copyarea    = cfb_copyarea,
 52     .fb_imageblit    = cfb_imageblit,
 53 };
 54 
 55 
 56 static struct fb_info *s3c_lcd;
 57 static volatile unsigned long *gpbcon;
 58 static volatile unsigned long *gpbdat;
 59 static volatile unsigned long *gpccon;
 60 static volatile unsigned long *gpdcon;
 61 static volatile unsigned long *gpgcon;
 62 static volatile struct lcd_regs* lcd_regs;
 63 static u32 pseudo_palette[16];
 64 
 65 
 66 /* from pxafb.c */
 67 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
 68 {
 69     chan &= 0xffff;
 70     chan >>= 16 - bf->length;
 71     return chan << bf->offset;
 72 }
 73 
 74 
 75 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
 76                  unsigned int green, unsigned int blue,
 77                  unsigned int transp, struct fb_info *info)
 78 {
 79     unsigned int val;
 80     
 81     if (regno > 16)
 82         return 1;
 83 
 84     /* 用red,green,blue三原色构造出val */
 85     val  = chan_to_field(red,    &info->var.red);
 86     val |= chan_to_field(green, &info->var.green);
 87     val |= chan_to_field(blue,    &info->var.blue);
 88     
 89     pseudo_palette[regno] = val;
 90     return 0;
 91 }
 92 
 93 static int lcd_init(void)
 94 {
 95     /* 1. 分配一个fb_info */
 96     s3c_lcd = framebuffer_alloc(0, NULL);
 97 
 98     /* 2. 设置 */
 99     /* 2.1 设置固定的参数 */
100     strcpy(s3c_lcd->fix.id, "mylcd");
101     s3c_lcd->fix.smem_len = 320*240*16/8;
102     s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
103     s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR; /* TFT */
104     s3c_lcd->fix.line_length = 320*2;
105     
106     /* 2.2 设置可变的参数 */
107     s3c_lcd->var.xres           = 320;
108     s3c_lcd->var.yres           = 240;
109     s3c_lcd->var.xres_virtual   = 320;
110     s3c_lcd->var.yres_virtual   = 240;
111     s3c_lcd->var.bits_per_pixel = 16;
112 
113     /* RGB:565 */
114     s3c_lcd->var.red.offset     = 11;
115     s3c_lcd->var.red.length     = 5;
116     
117     s3c_lcd->var.green.offset   = 5;
118     s3c_lcd->var.green.length   = 6;
119 
120     s3c_lcd->var.blue.offset    = 0;
121     s3c_lcd->var.blue.length    = 5;
122 
123     s3c_lcd->var.activate       = FB_ACTIVATE_NOW;
124     
125     
126     /* 2.3 设置操作函数 */
127     s3c_lcd->fbops              = &s3c_lcdfb_ops;
128     
129     /* 2.4 其他的设置 */
130     s3c_lcd->pseudo_palette = pseudo_palette;
131     s3c_lcd->screen_size   = 320*240*16/8;
132 
133     /* 3. 硬件相关的操作 */
134     /* 3.1 配置GPIO用于LCD */
135     gpbcon = ioremap(0x56000010, 8);
136     gpbdat = gpbcon+1;
137     gpccon = ioremap(0x56000020, 4);
138     gpdcon = ioremap(0x56000030, 4);
139     gpgcon = ioremap(0x56000060, 4);
140 
141   *gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
142     *gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */
143     *gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */
144     
145     /* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
146     lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
147 
148     /* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2]
149      * bit[6:5]: 0b11, TFT LCD
150      * bit[4:1]: 0b1100, 16 bpp for TFT
151      * bit[0]  : 0 = Disable the video output and the LCD control signal.
152      */
153     lcd_regs->lcdcon1  = (9<<8) | (3<<5) | (0x0c<<1); 
154 
155     /* 垂直方向的时间参数
156      * bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
157      *             
158      * bit[23:14]: 多少行, 
159      * bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
160      *             
161      * bit[5:0]  : VSPW, VSYNC信号的脉冲宽度, 
162      */
163     lcd_regs->lcdcon2  = (15<<24) | (240<<14) | (12<<6) | (3<<0);
164 
165 
166     /* 水平方向的时间参数
167      * bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
168      *             
169      *             
170      * bit[18:8]: 多少列,
171      * bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
172      *             
173      */
174     lcd_regs->lcdcon3 = (38<<19) | (320<<8) | (20<<0);
175 
176     /* 水平方向的同步信号
177      * bit[7:0]    : HSPW, HSYNC信号的脉冲宽度
178      */    
179     lcd_regs->lcdcon4 = 30;
180 
181     /* 信号的极性 
182      * bit[11]: 1=565 format
183      * bit[10]: 0 = The video data is fetched at VCLK falling edge
184      * bit[9] : 1 = HSYNC信号要反转,即低电平有效 
185      * bit[8] : 1 = VSYNC信号要反转,即低电平有效 
186      * bit[6] : 0 = VDEN不用反转
187      * bit[3] : 0 = PWREN输出0
188      * bit[1] : 0 = BSWP
189      * bit[0] : 1 = HWSWP 2440手册P413
190      */
191     lcd_regs->lcdcon5 = (1<<11) | (1<<10) | (1<<9) | (1<<8) | (1<<0);
192     
193     /* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器:screen_base是虚拟地址,smem_start是物理地址 */
194     s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
195     
196     lcd_regs->lcdsaddr1  = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
197     lcd_regs->lcdsaddr2  = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
198     lcd_regs->lcdsaddr3  = (320*16/16);  /* 一行的长度(单位: 2字节) */    
199     
200     /* 启动LCD */
201     lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */
202     lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 */    
203 
204     /* 4. 注册 */
205     register_framebuffer(s3c_lcd);
206     
207     return 0;
208 }
209 
210 static void lcd_exit(void)
211 {
212     unregister_framebuffer(s3c_lcd);
213     lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本身 */
214     dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
215     iounmap(lcd_regs);
216     iounmap(gpbcon);
217     iounmap(gpccon);
218     iounmap(gpdcon);
219     iounmap(gpgcon);
220     framebuffer_release(s3c_lcd);
221 }
222 
223 module_init(lcd_init);
224 module_exit(lcd_exit);
225 
226 MODULE_LICENSE("GPL");

装载驱动前要把内核自带的lcd驱动去掉,测试时发现这样写还有点问题,可能是上面的硬件设置还有点问题,不过可以使用了,但是当rmmod时不能卸载驱动,显示如下信息:

rmmod: cannot unload 'lcd': Resource temporarily unavailable

如有路过的大侠知道原因麻烦告诉下

 

posted @ 2013-04-08 01:07  linux_rookie  阅读(587)  评论(0编辑  收藏  举报