06.Linux LCD設備驅動

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章