LCD 设备驱动本身属于字符设备驱动的范畴,但其硬件操作复杂
LCD 设备驱动编写流程
- 分配一个 fb_info 结构体
- 设置 fb_info 结构体成员
-
- 设置固定参数==》设置fb_fix_screeninfo结构体成员
-
- 设置可变参数==》设置fb_var_screeninfo结构体成员
-
- 设置操作函数==》定义fbops结构体成员,并实现里面的函数
-
- 其他设置
-
-
- 设置显存的大小
-
-
-
- 设置调色板
-
-
-
- 设置显存的虚拟起始地址
-
- 硬件相关的操作
-
- 获取lcd时钟,使能时钟
-
- 配置GPIO用于LCD
-
- 映射LCD控制器对应寄存器
- 注册
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/platform_device.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/fb.h>
#include <asm/div64.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#define DriverName "LCD"
#define VSPW 9
#define VBPD 13
#define LINEVAL 479
#define VFPD 21
#define HSPW 19
#define HBPD 25
#define HOZVAL 799
#define HFPD 209
#define LeftTopX 0
#define LeftTopY 0
#define RightBotX 99
#define RightBotY 479
static u32 pseudo_palette[16] = {0};
static struct fb_info *fbinfo = NULL;
//static long unsigned long *gpbcon;
static long unsigned long *gpf0con;
static long unsigned long *gpf1con;
static long unsigned long *gpf2con;
static long unsigned long *gpf3con;
static long unsigned long *gpd0con;
static long unsigned long *gpd0dat;
static long unsigned long *display_control;
/*lcd registers*/
static long unsigned long *vidcon0;
static long unsigned long *vidcon1;
static long unsigned long *vidtcon2;
static long unsigned long *vidtcon3;
static long unsigned long *wincon0;
static long unsigned long *wincon2;
static long unsigned long *shadowcon;
static long unsigned long *vidosd0a;
static long unsigned long *vidosd0b;
static long unsigned long *vidosd0c;
static long unsigned long *vidw00add0b0;
static long unsigned long *vidw00add1b0;
static long unsigned long *vidw00add2;
static long unsigned long *vidtcon0;
static long unsigned long *vidtcon1;
struct clk *lcd_clk;
void lcd_hw_init(void);
void lcd_hw_deinit(void);
static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int lcd_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned 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);
pseudo_palette[regno] = val;
return 0;
}
static struct fb_ops lcd_fbops =
{
.owner = THIS_MODULE,
.fb_setcolreg = lcd_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
void lcd_hw_init()
{
//获取lcd时钟,使能时钟
lcd_clk = clk_get(NULL, "lcd");
if (!lcd_clk || IS_ERR(lcd_clk))
{
printk(KERN_INFO "failed to get lcd clock source\n");
}
clk_enable(lcd_clk);
//配置GPIO用于LCD
gpf0con = ioremap(0xE0200120, 4);
gpf1con = ioremap(0xE0200140, 4);
gpf2con = ioremap(0xE0200160, 4);
gpf3con = ioremap(0xE0200180, 4);
gpd0con = ioremap(0xE02000A0, 4);
gpd0dat = ioremap(0xE02000A4, 4);
display_control = ioremap(0xe0107008, 4);
//设置相关GPIO引脚用于LCD
*gpf0con = 0x22222222;
*gpf1con = 0x22222222;
*gpf2con = 0x22222222;
*gpf3con = 0x22222222;
//使能LCD
*gpd0con |= 1<<4;
*gpd0dat |= 1<<1;
//显示路径的选择,0b10: RGB=FIMD I80=FIMD ITU=FIMD
*display_control = 2<<0;
//映射LCD控制器对应寄存器
vidcon0 = ioremap(0xF8000000, 4);
vidcon1 = ioremap(0xF8000004, 4);
vidtcon2 = ioremap(0xF8000018, 4);
vidtcon3 = ioremap(0xF800001c, 4);
wincon0 = ioremap(0xF8000020, 4);
wincon2 = ioremap(0xF8000028, 4);
shadowcon = ioremap(0xF8000034, 4);
vidosd0a = ioremap(0xF8000040, 4);
vidosd0b = ioremap(0xF8000044, 4);
vidosd0c = ioremap(0xF8000048, 4);
vidw00add0b0 = ioremap(0xF80000A0, 4);
vidw00add1b0 = ioremap(0xF80000D0, 4);
vidw00add2 = ioremap(0xF8000100, 4);
vidtcon0 = ioremap(0xF8000010, 4);
vidtcon1 = ioremap(0xF8000014, 4);
// RGB I/F, RGB Parallel format
*vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6) | (1<<2));
*vidcon0 |= ((5<<6) | (1<<4) );
// 在vclk的下降沿获取数据
*vidcon1 &= ~(1<<7);
//HSYNC极性反转, VSYNC极性反转
*vidcon1 |= ((1<<6) | (1<<5));
*vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
*vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);
*vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
*wincon0 &= ~(0xf << 2);
*wincon0 |= (0xB<<2) | (1<<15);
*vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
*vidosd0b = (RightBotX<<11) | (RightBotY << 0);
*vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);
//frame buffer
*vidw00add0b0 = fbinfo->fix.smem_start;
*vidw00add1b0 = fbinfo->fix.smem_start + fbinfo->fix.smem_len;
*shadowcon = 0x1; /* 使能通道0 */
*vidcon0 |= 0x3; /* 开启总控制器 */
*wincon0 |= 1; /* 开启窗口0 */
}
static int __init LCD_init(void)
{
/* 分配一个 fb_info 结构体
* 第一个参数为0,表示只需要分配结构体本身大小,无需分配额外空间
* 第二个参数为NULL,表示无device结构
*/
fbinfo = framebuffer_alloc(0, NULL);
//设置固定参数
strcpy(fbinfo->fix.id, DriverName);
fbinfo->fix.smem_len = 800 * 480 * 32/8;
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
fbinfo->fix.line_length = 800 * 32/8;
//设置可变参数
fbinfo->var.xres = 800;
fbinfo->var.yres = 480;
fbinfo->var.xres_virtual = 800;
fbinfo->var.yres_virtual = 480;
fbinfo->var.bits_per_pixel = 32;
//设置RGB:888
fbinfo->var.red.offset = 16;
fbinfo->var.red.length = 8;
fbinfo->var.green.offset = 8;
fbinfo->var.green.length = 8;
fbinfo->var.blue.offset = 0;
fbinfo->var.blue.length = 8;
fbinfo->var.activate = FB_ACTIVATE_NOW;
//设置操作函数
fbinfo->fbops = &lcd_fbops;
//设置显存的大小
fbinfo->screen_size = 800 * 480 * 32/8;
//设置调色板
fbinfo->pseudo_palette = pseudo_palette;
/* 设置显存的虚拟起始地址
* 第一个参数:device结构体,设置为NULL,表示无device结构
* 第二个参数:需配置帧缓冲大小
* 第三个参数:返回的帧缓冲的物理起始地址,便是DMA可用
* 第四个参数:分配标志,如GFP_KERNEL
*/
fbinfo->screen_base = dma_alloc_writecombine(NULL, fbinfo->fix.smem_len,
(u32*)&(fbinfo->fix.smem_start), GFP_KERNEL);
//硬件相关的操作
lcd_hw_init();
//注册
register_framebuffer(fbinfo);
return 0;
}
void lcd_hw_deinit()
{
iounmap(gpf0con);
iounmap(gpf1con);
iounmap(gpf2con);
iounmap(gpf3con);
iounmap(gpd0con);
iounmap(gpd0dat);
iounmap(display_control);
iounmap(vidcon0);
iounmap(vidcon1);
iounmap(vidtcon2);
iounmap(vidtcon3);
iounmap(wincon0);
iounmap(wincon2);
iounmap(shadowcon);
iounmap(vidosd0a);
iounmap(vidosd0b);
iounmap(vidosd0c);
iounmap(vidw00add0b0);
iounmap(vidw00add1b0);
iounmap(vidw00add2);
iounmap(vidtcon0);
iounmap(vidtcon1);
}
static void __exit LCD_exit(void)
{
unregister_framebuffer(fbinfo);
dma_free_writecombine(NULL, fbinfo->fix.smem_len, (u32*)&(fbinfo->fix.smem_start), GFP_KERNEL);
lcd_hw_deinit();
framebuffer_release(fbinfo);
}
module_init(LCD_init);
module_exit(LCD_exit);
MODULE_LICENSE("GPL");