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");