s3c2440液晶屏驱动 (非内核自带) linux-4.1.24

Posted 宁次

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了s3c2440液晶屏驱动 (非内核自带) linux-4.1.24相关的知识,希望对你有一定的参考价值。

对于,不想逐一检查内核自带驱动,想自己编写驱动。

1,make menuconfig 去掉 编译到内核,改为 M 编译为 模块(因为要用到里面的3个.ko 驱动)

Device Drivers --->
    Graphics support --->
        Support for frame buffer devices --->
            <M> S3C2410 LCD framebuffer support

 

2,make uImage && make modules 生成新内核  和 模块文件

烧写新内核或使用 nfs bootm 使用编译为 M 模块的内核启动。

复制 3个 ko 文件到 文件系统,这里用的是 NFS 网络文件系统。

cp drivers/video/fbdev/core/cfb*.ko /nfs_root/new_fs  (新的 4.1 内核是在这里,以前没有 这个 core 目录)

 

3, 使用原来的 2.6.22 的内核下的驱动程序,修改头文件后,编译为 .ko 文件放到 NFS 文件系统里,启动,如图所示

图忘拍了,有时是白屏,有时是竖的细彩线。

比较内核自带驱动 s3c2410fb.c 发现有个 usleep_range(1000, 1100); 

而原来的 2.6.22 的驱动里面,是没有启用 clk 的。添加上这部分后,重新编译,试机成功。

最后是完整的驱动源码:

  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 #include <asm/mach/map.h>
 28 #include <mach/regs-lcd.h>
 29 #include <mach/regs-gpio.h>
 30 #include <mach/fb.h>
 31 
 32 
 33 //参数定义
 34 #define LCD_WIDTH 480
 35 #define LCD_HEIGHT 272
 36 #define LCD_BIT 16
 37 
 38 #define LCD_CLKVAL 4
 39 #define LCD_TFT    3
 40 #define LCD_24BBP  0xd
 41 #define LCD_16BBP  0xc
 42 #define LCD_EN_OFF 0
 43 #define LCD_EN_ON  1
 44 #define LCD_VBPD   1
 45 #define LCD_LINEVAL  (LCD_HEIGHT - 1)
 46 #define LCD_VFPD   1
 47 #define LCD_VSPW   9
 48 #define LCD_HBPD   1
 49 #define LCD_HOZVAL (LCD_WIDTH - 1)
 50 #define LCD_HFPD   1
 51 #define LCD_HSPW   40
 52 #define LCD_INVVLINE 1
 53 #define LCD_INVVFRAME 1
 54 //调色板 u32 = unsigned int
 55 static u32 pseudo_palette[16];
 56 
 57 /* from pxafb.c */
 58 static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
 59 {
 60     //只取16bit 数据
 61     chan &= 0xffff;
 62     //如 r = 16-5 = 11 : 右移去掉不要的位
 63     chan >>= (16 - bf->length);
 64     //在移到高位去 就是右边补0
 65     return chan << bf->offset;
 66 }
 67 
 68 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
 69                  unsigned int green, unsigned int blue,
 70                  unsigned int transp, struct fb_info *info)
 71 {
 72     
 73     if(16 > regno)
 74     {
 75         unsigned int val;
 76         val  = chan_to_field(red, &info->var.red);
 77         val |= chan_to_field(green, &info->var.green);
 78         val |= chan_to_field(blue, &info->var.blue);
 79         pseudo_palette[regno] = val;
 80         return 0;
 81     }
 82     return 1;
 83 }
 84 
 85 
 86 static struct fb_ops s3c_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 //定义GPIO
 95 static volatile unsigned long *gpb_con;
 96 static volatile unsigned long *gpc_con;
 97 static volatile unsigned long *gpd_con;
 98 static volatile unsigned long *gpg_con;
 99 
100 static volatile unsigned long *gpb_dat;
101 static volatile unsigned long *gpc_dat;
102 static volatile unsigned long *gpd_dat;
103 static volatile unsigned long *gpg_dat;
104 
105 struct lcd_regs {
106     unsigned long    lcdcon1;    
107     unsigned long    lcdcon2;    
108     unsigned long    lcdcon3;    
109     unsigned long    lcdcon4;    
110     unsigned long    lcdcon5;    
111     unsigned long    lcdsaddr1;    
112     unsigned long    lcdsaddr2;    
113     unsigned long    lcdsaddr3;    
114     unsigned long    redlut;    
115     unsigned long    greenlut;    
116     unsigned long    bluelut;    
117     unsigned long    reserved[9];    
118     unsigned long    dithmode;    
119     unsigned long    tpal;    
120     unsigned long    lcdintpnd;    
121     unsigned long    lcdsrcpnd;    
122     unsigned long    lcdintmsk;    
123     unsigned long    lpcsel;
124 };
125 
126 static volatile struct lcd_regs * lcd_reg;
127 
128 
129 //定义fb_info
130 static struct fb_info * s3c_lcd;
131 static int lcd_init(void)
132 {
133     //1,分配一个fb_info
134     s3c_lcd = framebuffer_alloc(0, NULL);
135 
136     struct clk *clk;    
137     clk = clk_get(NULL, "lcd");
138     if (IS_ERR(clk)) {
139         printk("failed to get lcd clock source\n");
140     }
141 
142     clk_prepare_enable(clk);
143     printk("got and enabled clock\n");
144 
145     usleep_range(1000, 1100);
146     
147     //2,设置
148     //2,1 设置固定的参数
149     strcpy(s3c_lcd->fix.id, "mylcd");
150     s3c_lcd->fix.smem_len  = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8;
151     s3c_lcd->fix.type        = FB_TYPE_PACKED_PIXELS;
152     s3c_lcd->fix.visual      = FB_VISUAL_TRUECOLOR;
153     s3c_lcd->fix.line_length = LCD_WIDTH * LCD_BIT / 8; //单位 bytes    
154     //物理地址由 dma_alloc_writecombine 函数分配
155     //s3c_lcd->fix.smem_start  = LCD_FRAMEBUFFER;
156     //s3c_lcd->fix.mmio_len    = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8;
157     
158     //2,2 设置可变的参数
159     s3c_lcd->var.width  = LCD_WIDTH;
160     s3c_lcd->var.height = LCD_HEIGHT;
161     s3c_lcd->var.xres         = LCD_WIDTH;
162     s3c_lcd->var.yres         = LCD_HEIGHT;
163     s3c_lcd->var.xres_virtual = LCD_WIDTH;
164     s3c_lcd->var.yres_virtual = LCD_HEIGHT;
165     s3c_lcd->var.bits_per_pixel = LCD_BIT;
166     //RGB 5 6 5
167     s3c_lcd->var.red.length   = 5;
168     s3c_lcd->var.red.offset   = 11;
169     s3c_lcd->var.green.length = 6;
170     s3c_lcd->var.green.offset = 5;
171     s3c_lcd->var.blue.length  = 5;
172     s3c_lcd->var.blue.offset  = 0;
173     s3c_lcd->var.activate     = FB_ACTIVATE_NOW;
174 
175     s3c_lcd->var.pixclock = 100000;
176 
177     //2,3 设置操作函数
178     s3c_lcd->fbops = &s3c_ops;
179 
180     //2.4 其它的设置
181     s3c_lcd->screen_size = LCD_WIDTH * LCD_HEIGHT * LCD_BIT / 8;
182     s3c_lcd->pseudo_palette = pseudo_palette;
183     
184     //3,硬件相关的操作
185     //3,1 配置GPIO
186     gpb_con = ioremap(0x56000010, 4);
187     gpb_dat = gpb_con + 1;
188 
189     gpc_con = ioremap(0x56000020, 4);
190     gpc_dat = gpc_con + 1;
191 
192     gpd_con = ioremap(0x56000030, 4);
193     gpc_dat = gpd_con + 1;
194 
195     gpg_con = ioremap(0x56000060, 4);
196     gpg_dat = gpg_con + 1;
197 
198     *gpc_con = 0xaaaaaaaa;   // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND 
199     *gpd_con = 0xaaaaaaaa;   // GPIO管脚用于VD[23:8]
200 
201     *gpb_con &= ~(3);        //配置背光引脚为输出
202     *gpb_con |= 1;
203     *gpb_dat &= 0;           //默认输出低电平
204 
205     *gpg_con &= ~(3<<2*4);   //GPGCON 4 配置为 LCD_POWER 
206     *gpg_con |= (3<<2*4);
207     
208     //3,2 配置lcdcon1 ~ lcdcon5
209     lcd_reg          = ioremap(0x4d000000, sizeof(struct lcd_regs));
210     lcd_reg->lcdcon1 = LCD_CLKVAL<<8 | LCD_TFT<<5 | LCD_16BBP<<1 | LCD_EN_OFF;
211     lcd_reg->lcdcon2 = LCD_VBPD<<24 | LCD_LINEVAL<<14 | LCD_VFPD<<6 | LCD_VSPW;
212     lcd_reg->lcdcon3 = LCD_HBPD<<19 | LCD_HOZVAL<<8 | LCD_HFPD;
213     lcd_reg->lcdcon4 = LCD_HSPW;
214     lcd_reg->lcdcon5 = 1<<11 | LCD_INVVLINE<<9 | LCD_INVVFRAME<<8 | 1;
215     //屏幕宽度 * 16bit 颜色值 / words : 1 words = 2 byte
216     lcd_reg->lcdsaddr3 = LCD_WIDTH * 16 / 16;
217     
218     //3,3 设置显存 bramebuffer, 并把地址告诉lcd 控制器
219     s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
220     
221     lcd_reg->lcdsaddr1 = (s3c_lcd->fix.smem_start>>22)<<21 | ((s3c_lcd->fix.smem_start>>1) & 0x1fffff);
222     lcd_reg->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1) & 0x1fffff;
223     
224     unsigned long saddr1,saddr2,saddr3;
225     saddr1  = s3c_lcd->fix.smem_start >> 1;
226     saddr2  = s3c_lcd->fix.smem_start;
227     saddr2 += s3c_lcd->fix.line_length * s3c_lcd->var.yres;
228     saddr2 >>= 1;
229     saddr3 = S3C2410_OFFSIZE(0) |
230      S3C2410_PAGEWIDTH((s3c_lcd->fix.line_length / 2) & 0x3ff);
231 
232     lcd_reg->lcdsaddr1 = saddr1;
233     lcd_reg->lcdsaddr2 = saddr2;
234     lcd_reg->lcdsaddr3 = saddr3;
235 
236     printk("fb %x \n", LCD_CLKVAL<<8 | LCD_TFT<<5 | LCD_16BBP<<1 | LCD_EN_OFF);
237     printk("fb %x \n", LCD_VBPD<<24 | LCD_LINEVAL<<14 | LCD_VFPD<<6 | LCD_VSPW);
238     printk("fb %x \n", LCD_HBPD<<19 | LCD_HOZVAL<<8 | LCD_HFPD);
239     printk("fb %x \n", LCD_HSPW);
240     printk("fb %x \n", 1<<11 | LCD_INVVLINE<<9 | LCD_INVVFRAME<<8 | 1);
241 
242     
243     printk("fb %x \n", saddr1);
244     printk("fb %x \n", saddr2);
245     printk("fb %x \n", saddr3);
246 
247     //打开 LCD_POWER 背光
248     lcd_reg->lcdcon1 |= LCD_EN_ON;
249     *gpb_dat |= 1;
250     lcd_reg->lcdcon5 &= ~(1<<3);
251     lcd_reg->lcdcon5 |=  (1<<3);
252     
253     //4,注册
254     register_framebuffer(s3c_lcd);
255     return 0;
256 }
257 
258 static void lcd_exit(void)
259 {
260     //关背光
261     *gpb_dat &= 0;
262     lcd_reg->lcdcon5 &= ~(1<<3);
263     lcd_reg->lcdcon1 &= LCD_EN_OFF;
264 
265     //注销显存
266     dma_free_writecombine(NULL,s3c_lcd->fix.smem_len,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);
267 
268     //注销
269     framebuffer_release(s3c_lcd);
270 
271     //注销framebuf
272     unregister_framebuffer(s3c_lcd);
273     
274     //取消map
275     iounmap(gpb_con);
276     iounmap(gpc_con);
277     iounmap(gpd_con);
278     iounmap(gpg_con);
279     iounmap(lcd_reg);
280 }
281 
282 
283 module_init(lcd_init);
284 module_exit(lcd_exit);
285 MODULE_LICENSE("GPL");

 

试验的时候要先加载那3个 .ko

insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko  编译出来的 lcd 驱动

以上是关于s3c2440液晶屏驱动 (非内核自带) linux-4.1.24的主要内容,如果未能解决你的问题,请参考以下文章

LCD液晶屏驱动详解

LCD驱动详解

Linux 内核自带的 LED 灯驱动

基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读

S3C2440 LCD驱动(FrameBuffer)实例开发<二>(转)

LCD段码屏可以不用驱动芯片,直接单片机驱动吗