驱动程序之_1_字符设备_11_LCD驱动_2_编写
上一篇文章粗浅地分析了内核的一个LCD驱动代码,由此知道编写LCD驱动的一个方法:编写一个设备,将其融入内核的fbmem即可。
本文仿照上一篇文章所分析的例子,去掉platform框架,编写一个符合fbmem机制的简单LCD驱动程序
在入口函数
1、分配fb_info结构体
static struct fb_info *s3c_lcd;
s3c_lcd = framebuffer_alloc(0,NULL);
2、设置fb_info结构体
①、根据硬件要求设置bpp、xres、yres、line_length等基本参数
②、设置调色板
③、设置操作函数结构体
这里只列出操作函数结构体的设置,其中cfb_fillrect、cfb_copyarea、cfb_imageblit这三个操作函数是使用内核自带的程序,使用这个驱动程序的时候需要将对这三个函数所在的文件作相应处理(编进内核或者编译为模块手动加载,在文末测试的时候会采用编译为模块的方法)
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = s3c_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
s3c_lcd->fbops = &s3c_lcdfb_ops;
3、注册fb_info结构体
register_framebuffer(s3c_lcd);
4、硬件初始化
根据硬件要求,IO映射、设置IO引脚、LCD控制器、分配物理、虚拟显存等等
2、在出口函数完成与入口函数相反的操作
①、注销fb_info结构体
unregister_framebuffer(s3c_lcd);
②、关闭LCD控制器和相关电源,释放显存、取消IO映射
*gpbdat &= ~1;
lcd_regs->lcdcon1 &= ~1;
lcd_regs->lcdcon5 &= ~(1<<3);
dma_free_writecombine(NULL,s3c_lcd->screen_size,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
iounmap(lcd_regs);
③、销毁fb_info结构体
framebuffer_release(s3c_lcd);
测试方法:
1、进入内核目录,make menuconfig,反选内核的LCD驱动并退出保存,make uImage,编译内核,在arch/arm/boot/中找到内核将其下载到开发板
2、进入内核目录,make modules,编译模块,在drivers/video/中找到cfbimgblt.ko、cfbfillrect.ko、cfbcopyarea.ko,将它们拷贝到开发板上
3、编译LCD驱动,拷贝到开发板上
4、用新的内核启动开发板,加载cfbimgblt.ko、cfbfillrect.ko、cfbcopyarea.ko,再加载LCD驱动
5、输入 echo hello > /dev/tty1 ,将hello输出到lcd上
6、输入 cat 文件名 > /dev/tty1 ,将一个文件的内容输出到lcd上
附上完整代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>
struct s3c_lcd_regs{
volatile unsigned long lcdcon1; //0X4D000000
volatile unsigned long lcdcon2;
volatile unsigned long lcdcon3;
volatile unsigned long lcdcon4;
volatile unsigned long lcdcon5;
volatile unsigned long lcdsaddr1;
volatile unsigned long lcdsaddr2;
volatile unsigned long lcdsaddr3;
volatile unsigned long redlut;
volatile unsigned long greenlut;
volatile unsigned long bluelut;
volatile unsigned long reserved[9];
volatile unsigned long dithmode; //0X4D00004C
volatile unsigned long tpal; //0X4D000050
volatile unsigned long lcdintpnd;
volatile unsigned long lcdsrcpnd;
volatile unsigned long lcdintmsk;
volatile unsigned long tconsel;
};
static u32 pseudo_palette[16];
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsigned int val;
if (regno > 16)
return 1;
/* 用red,green,blue三原色构造出val */
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
//((u32 *)(info->pseudo_palette))[regno] = val;
pseudo_palette[regno] = val;
return 0;
}
static struct fb_info *s3c_lcd;
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = s3c_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static struct s3c_lcd_regs *lcd_regs;
static volatile unsigned long *gpbcon,*gpbdat,*gpccon,*gpdcon,*gpgcon;
static int lcd_init(void)
{
//硬件初始化
//分配设置注册结构体
s3c_lcd = framebuffer_alloc(0,NULL);
strcmp(s3c_lcd->fix.id,"s3c_lcd");
s3c_lcd->fix.smem_len = 480 * 272 * 2;
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s3c_lcd->fix.line_length = 480 * 2;
s3c_lcd->var.xres = 480;
s3c_lcd->var.xres_virtual = 480;
s3c_lcd->var.yres = 272;
s3c_lcd->var.yres_virtual = 272;
s3c_lcd->var.bits_per_pixel = 16;
s3c_lcd->var.red.offset = 11;
s3c_lcd->var.red.length = 5;
s3c_lcd->var.green.offset = 5;
s3c_lcd->var.green.length = 6;
s3c_lcd->var.blue.offset = 0;
s3c_lcd->var.blue.length = 5;
s3c_lcd->var.activate = FB_ACTIVATE_NOW;
s3c_lcd->fbops = &s3c_lcdfb_ops;
s3c_lcd->pseudo_palette = pseudo_palette;
s3c_lcd->screen_size = 480 * 272 * 2;
gpbcon = ioremap(0x56000010,8);
gpbdat = gpbcon + 1;
gpccon = ioremap(0x56000020,4);
gpdcon = ioremap(0x56000030,4);
gpgcon = ioremap(0x56000060,4);
*gpccon = 0xaaaaaaaa;
*gpdcon = 0xaaaaaaaa;
*gpbcon &= ~(3); //GPB0 KEYBOARD //LED电源
*gpbcon |= (1); //GPB0 KEYBOARD //LED电源
*gpbdat &= ~1;
*gpgcon |= (3<<8); //GPG4 LCD_PWREN
lcd_regs = ioremap(0X4D000000,sizeof(struct s3c_lcd_regs));
/*
lcdcon1
27:18 (read only)
17:8 TFT: VCLK = HCLK / [(CLKVAL+1) x 2] ( CLKVAL ≥ 0 )
7 Determine the toggle rate of the VM: 0 = Each Frame 1 = The rate defined by the MVAL
6:5 11 = TFT LCD panel
4:1 1011 = 8 bpp for TFT 1100 = 16 bpp for TFT 1101 = 24 bpp for TFT
0 1 = Enable
*/
lcd_regs->lcdcon1 = (4<<8) |
(3<<5) |
(0x0c<<1);
/*
lcdcon2
31:24 TFT: Vertical back porch
23:14 vertical size of LCD panel.
13:6 Vertical front porch is the number of inactive lines at the end of a frame, before vertical synchronization period.
5:0 Vertical sync pulse width determines the VSYNC pulse's high level width by counting the number of inactive lines.
*/
//th = VBPD+1
//tv = VSPW+1
//272 = LINEVAL+1
//tvf = VFPD+1
//thp = HSPW+1
//thb = HBPD+1
//480 = HOZVAL+1
//thf = HFPD+1
/*
Note 1: t hd =480CLK, t hf = 2CLK, t hp = 41CLK, t hb = 2CLK
525CLK=480CLK + 2CLK + 41CLK + 2CLK
Note 2: t hf + t hp + t hb ﹥44 CLK
*/
lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9);
/*
lcdcon3
25:19 Horizontal back porch is the number of VCLK periods between the falling edge of HSYNC and the start of active data
18:8 These bits determine the horizontal size of LCD panel.
7:0 Horizontal front porch is the number of VCLK periods between the end of active data and the rising edge of HSYNC
*/
lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1);
/*
lcdcon4
7:0 TFT: Horizontal sync pulse width determines the HSYNC pulse's high level width by counting the number of the VCLK.
*/
lcd_regs->lcdcon4 = 40;
/*
lcdcon5
12 BPP24BL TFT: This bit determines the order of 24 bpp video memory. 0 = LSB valid 1 = MSB Valid
11 FRM565 0 = 5:5:5:1 Format 1 = 5:6:5 Format
10 INVVCLK 0 = The video data is fetched at VCLK falling edge
9 INVVLINE This bit indicates the VLINE/HSYNC pulse polarity. 0 = Normal 1 = Inverted
8 INVVFRAME This bit indicates the VFRAME/VSYNC pulse polarity.0 = Normal 1 = Inverted
7 INVVD This bit indicates the VD (video data) pulse polarity.0 = Normal 1 = VD is inverted.
6 INVVDEN TFT: This bit indicates the VDEN signal polarity.0 = normal 1 = inverted
5 INVPWREN This bit indicates the PWREN signal polarity.0 = normal 1 = inverted
4 INVLEND This bit indicates the LEND signal polarity.0 = normal 1 = inverted
3 PWREN LCD_PWREN output signal enable/disable.0 = Disable PWREN signal 1 = Enable PWREN signal
2 ENLEND LEND output signal enable/disable. 0 = Disable LEND signal 1 = Enable LEND signal
1 BSWP
0 HWSWP
*/
lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start+s3c_lcd->fix.smem_len)>>1) & (0x1fffff);
lcd_regs->lcdsaddr3 = 480 * 16 / 16;
lcd_regs->lcdcon1 |= 1;
lcd_regs->lcdcon5 |= 1<<3;
*gpbdat |= 1;
register_framebuffer(s3c_lcd);
return 0;
}
static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
*gpbdat &= ~1;
lcd_regs->lcdcon1 &= ~1;
lcd_regs->lcdcon5 &= ~(1<<3);
dma_free_writecombine(NULL,s3c_lcd->screen_size,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
iounmap(lcd_regs);
framebuffer_release(s3c_lcd);
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
本文深入解析LCD驱动程序的编写过程,从初始化硬件、配置fb_info结构体到注册和注销驱动,详细介绍了如何创建一个简单的LCD驱动,适用于内核fbmem机制。通过具体代码示例,展示了操作函数设置、调色板配置及硬件初始化步骤。
2433

被折叠的 条评论
为什么被折叠?



