LCD驱动框架

框架入口源文件: lcd.c 

(可根据入口源文件,再按着框架到内核走一遍)

 内核版本:linux_2.6.22.6    硬件平台:JZ2440

 驱动框架:

以下是驱动代码:

LCD.c

#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>


//定义一个 fd_info 结构体
static struct fb_info *s3c_lcd ;

//IO寄存器
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpgcon;

//LCD控制寄存器
static struct lcd_con 
{
     unsigned long LCDCON1; 
     unsigned long LCDCON2; 
     unsigned long LCDCON3; 
     unsigned long LCDCON4; 
     unsigned long LCDCON5; 
     unsigned long LCDSADDR1; 
     unsigned long LCDSADDR2; 
     unsigned long LCDSADDR3; 
     unsigned long REDLUT; 
     unsigned long GREENLUT; 
     unsigned long BLUELUT; 
     unsigned long RESERVE[9];   //guess why
     unsigned long DITHMODE; 
     unsigned long TPAL; 
     unsigned long LCDINTPND; 
     unsigned long LCDSRCPND; 
     unsigned long LCDINTMSK; 
     unsigned long LPCSEL; 
};

static volatile struct lcd_con *lcd_con;

static int lcd_pseudo_palette(unsigned int regno,  unsigned int red,
                               unsigned int green,  unsigned int blue,
                               unsigned int transp, struct fb_info *info);


//定义一个 fd_ops 结构体
static struct fb_ops lcd_fb_fops =
{
    .owner        = THIS_MODULE,
    .fb_setcolreg    = lcd_pseudo_palette,
    .fb_fillrect    = cfb_fillrect,
    .fb_copyarea    = cfb_copyarea,
    .fb_imageblit    = cfb_imageblit,

};

//设置调色板
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 lcd_pseudo_palette(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 int lcd_init(void)
{
//申请一个     fd_info 结构体
   s3c_lcd = framebuffer_alloc(0,NULL);

//初始化 fd_info 结构体
   /*设置固定参数*/
   strcpy(s3c_lcd->fix.id ," mylcd");
   s3c_lcd->fix.smem_len = 480*272*16/8;
   s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
   s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;//lcd 类型
   s3c_lcd->fix.line_length = 480*2;

   /*设置可变参数*/
   s3c_lcd->var.xres =480;
   s3c_lcd->var.yres =272;
   s3c_lcd->var.xres_virtual =480;
   s3c_lcd->var.yres_virtual =272;
   s3c_lcd->var.bits_per_pixel =16;
   
   s3c_lcd->var.green.length =6;
   s3c_lcd->var.green.offset =5;

   s3c_lcd->var.red.length =5;
   s3c_lcd->var.red.offset =11;

   s3c_lcd->var.blue.length =5;
   s3c_lcd->var.blue.offset =0;

   s3c_lcd->var.activate = FB_ACTIVATE_NOW;

   /*设置操作函数*/
   s3c_lcd->fbops =&lcd_fb_fops;

   /*调色盘*/
   s3c_lcd->pseudo_palette = lcd_pseudo_palette;
   s3c_lcd->screen_size =480*272*2;
  
   //设置 LCD控制器寄存器          IO寄存器
   
     // 1.VD IO
     gpccon = ioremap(0x56000020,4); 
     gpdcon = ioremap(0x56000030,4);  
     *gpccon = 0xaaaaaaaa;
     *gpdcon = 0xaaaaaaaa;
     
     // 2. KEYBOARD LCD_PWENB
     gpbcon = ioremap(0x56000010,4);
     gpbdat = gpbcon +1;
     gpgcon = ioremap(0x56000060,4);
     *gpbcon &= ~3;   // gpb0 KEYBOARD 背光
     *gpbcon |= 1;
     *gpbdat &= ~1;

     *gpgcon |= (3<<8); //gpg4 LCD_PWENB 电源
     // 3. 控制寄存器
     lcd_con = ioremap(0x4d000000, sizeof(struct lcd_con));
     
         /* lcdcon1  
         * bit[17:8] VCLK = HCLK / [(CLKVAL + 1) × 2] VCLK=10000KHZ  HCLK=100000KHZ CLKVAL=4
         * bit[6:5]  0b11 tft_lcd 面板
         * bit[4:1]  0b1100 16bpp
         * bit[0]    0 禁止vd 1 使能vd
         */
         lcd_con->LCDCON1 = (4<<8)|(3<<5)|(12<<1)|(0<<0);
         
         /* lcdcon2  垂直方向时间参数
         * bit[31:24] VBPD 垂直同步周期后的的无效行数 lcd芯片手册p15                   T0-T2-T1= 327 - 322 - 1 = 4   3
         * bit[23:14] LINEVAL LCD 面板的垂直尺寸 320-1 = 319
         * bit[13:6]  VFPD 垂直同步周期前的的无效行数 t2 -t5 = 322 - 320 - 1 = 1   
         * bit[5:0]   VSPW  VSYNC 脉冲的高电平宽度 T1 -1 = 0
         */
         lcd_con->LCDCON2 =  (1<<24) | (271<<14) | (1<<6) | (9);

         /* lcdcon3  水平方向时间参数
         * bit[25:19]  tft_lcd  HSYNC 的下降沿与有效数据的开始之间的 VCLK 周期数  T6 -T7 -T8 = 17  16
         * bit[18:8]    LCD 面板的水平尺寸   240-1=239
         * bit[7:0]    有效数据的结束与 HSYNC 的上升沿之间的 VCLK 周期数 11 - 1 = 10
         */
         lcd_con->LCDCON3 =  (1<<19) | (479<<8) | (1);

         /* lcdcon4 水平方向同步信号
         * bit[15:8]  
         * bit[7:0]   算 VCLK 的数水平同步脉冲宽度决定 HSYNC 脉冲的高电平宽度  5-1=4
         */
         lcd_con->LCDCON4 = 40;

         /* lcdcon5
         * bit[11]  1  16bpp 5:6:5 格式
         * bit[10]  0 = VCLK 下降沿取视频数据
         * bit[9]   1 = HSYNC信号要反转,即低电平有效 
         * bit[8]   1 = VSYNC信号要反转,即低电平有效
         * bit[6]   0 = VDEN不用反转
         * bit[3]   0 = PWREN输出0
         
* bit[1]  0 = BSWP
         * bit[0]   1 = HWSWP 2440手册P413
         */
         lcd_con->LCDCON5 = (1<<11)|(0<<10)|(1<<9)|(1<<8)|(0<<6)|(0<<3)|(0<<1)|(1<<0);

     // 4. 分配显存
     s3c_lcd->screen_base =dma_alloc_writecombine(NULL,s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start,GFP_KERNEL);
         
         
         /*
         * bit[29:21] 帧内存起始地址
         * bit[20:0]  视口缓冲区起始地址 
         */
         lcd_con->LCDSADDR1 = (s3c_lcd->fix.smem_start>>1)& ~(3<<30);

         /*
         * bit[20:0] 视口缓冲区结束地址 
         */
         lcd_con->LCDSADDR2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1 ) & 0x1fffff;

         /*
         *  bit[21:11] 一行最后一个数据与下一行最开始数据的 差值的一半
         *  bit[10:0]  视口的宽度
         */
         lcd_con->LCDSADDR3 =480*1;
         
//启动LCD
   lcd_con->LCDCON1 |= (1<<0);  /* 使能LCD控制器 */
   lcd_con->LCDCON5 |= (1<<3);  /* 使能LCD本身 */
   *gpbdat |= 1;                /* 输出高电平, 使能背光 */        


//注册 fd_info 结构体
  register_framebuffer(s3c_lcd);

    return 0;
}

static void lcd_exit(void)
{
   //去掉注册的 fd_info 结构体
   unregister_framebuffer(s3c_lcd);

   //释放 fd_info 结构体空间
   framebuffer_release(s3c_lcd);

   //取消映射
   iounmap(lcd_con);
   iounmap(gpbcon);
   iounmap(gpccon);
   iounmap(gpdcon);   
   iounmap(gpgcon); 
   iounmap(gpbdat);

   //释放帧内存
   dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);

   //关闭LCD
   lcd_con->LCDCON1 &= ~(1<<0); /* 关闭LCD本身 */
   *gpbdat &= ~1;     /* 关闭背光 */
   
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");

以下是编译驱动的Makefile:

KERN_DIR = /work/systems/kernel/linux-2/linux-2.6.22.6

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += lcd.o
原文地址:https://www.cnblogs.com/zsy12138/p/10391924.html