1. 程式人生 > 實用技巧 >9.LCD驅動架構

9.LCD驅動架構

1.小結

1.分配一個fb_info
2.設定

  • 2.1固定的引數
  • 2.2設定可變的引數
  • 2.3設定具體的檔案操作指標fb_info->fbops
  • 2.4其他設定

3.硬體相關操作

  • 3.1配置GPIO用於LCD
  • 3.2根據LCD手冊設定LCD控制器,如VCLK的頻率等
  • 3.3分配視訊記憶體framebuffer並把地址告訴LCD控制器

4.註冊

  • 4.1分配視訊記憶體fb_info->screnn_base,使用dma_alloc_writecombine()
     注:函式返回值是虛擬地址,有引數*handler返回實際實際實體地址,這個實體地址需要設定到LCD控制器
  • 4.2註冊fb_info
    結構體,register_framebuffer

5.出口

  • 5.1解除安裝核心中的fb_info
  • 5.2控制LCDCON1關閉PWREN訊號,關背光,iounmap登出地址
  • 5.3釋放DMA緩衝地址dma_free_write()
  • 5.4釋放fb_info

2.關鍵函式和資料結構

  • dma_alloc_writecombine
    分配記憶體空間

    void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
    解析:
          返回值:申請到的DMA緩衝區虛擬地址,若為NULL則表示分配失敗,此時需要使用dma_free_writecombine釋放記憶體
          *dev:   這裡填0,表示這個申請的緩衝區裡沒有內容
          size:   分配的地址大小(位元組單位)
          *handle: 申請到的物理起始地址
          gfp:     分配出來的記憶體引數,標誌定義在<linux/gfp.h>常用如下:
                    GFP_ATOMIC:用來從中斷處理和程序上下文之外的其他程式碼中分配記憶體,從不睡眠
                    GFP_KERNEL:核心記憶體的正常分配,可能會睡眠
                    GFP_USER:  用來為使用者空間也分配,可能會睡眠 
    

framebuffer_alloc
—建立一個新的幀緩衝區資訊結構

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)  

fb_info

struct fb_info {
	atomic_t count;
	int node;
	int flags;
	struct mutex lock;		/* Lock for open/release/ioctl funcs */
	struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
	struct fb_var_screeninfo var;	/* Current var */
	struct fb_fix_screeninfo fix;	/* Current fix */
	struct fb_monspecs monspecs;	/* Current Monitor specs */
	struct work_struct queue;	/* Framebuffer event queue */
	struct fb_pixmap pixmap;	/* Image hardware mapper */
	struct fb_pixmap sprite;	/* Cursor hardware mapper */
	struct fb_cmap cmap;		/* Current cmap */
	struct list_head modelist;      /* mode list */
	struct fb_videomode *mode;	/* current mode */
  #ifdef CONFIG_FB_BACKLIGHT
	/* assigned backlight device */
	/* set before framebuffer registration, 
	   remove after unregister */
	struct backlight_device *bl_dev;

	/* Backlight level curve */
	struct mutex bl_curve_mutex;	
	u8 bl_curve[FB_BACKLIGHT_LEVELS];
  #endif
  #ifdef CONFIG_FB_DEFERRED_IO
	struct delayed_work deferred_work;
	struct fb_deferred_io *fbdefio;
  #endif

	struct fb_ops *fbops;
	struct device *device;		/* This is the parent */
	struct device *dev;		/* This is this fb device */
	int class_flag;                    /* private sysfs flags */
  #ifdef CONFIG_FB_TILEBLITTING
	struct fb_tile_ops *tileops;    /* Tile Blitting */
  #endif
	char __iomem *screen_base;	/* Virtual address */
	unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 */ 
	void *pseudo_palette;		/* Fake palette of 16 colors */ 
  #define FBINFO_STATE_RUNNING	0
  #define FBINFO_STATE_SUSPENDED	1
	u32 state;			/* Hardware state i.e suspend */
	void *fbcon_par;                /* fbcon use-only private area */
	/* From here on everything is device dependent */
	void *par;
	/* we need the PCI or similar aperture base/size not
	   smem_start/size as smem_start may just be an object
	   allocated inside the aperture so may not actually overlap */
	struct apertures_struct {
		unsigned int count;
		struct aperture {
			resource_size_t base;
			resource_size_t size;
		} ranges[0];
	} *apertures;
};
  • register_framebuffer
    註冊一個幀緩衝裝置

    int register_framebuffer(struct fb_info *fb_info)
    

3.原始碼解析

lcd.c

  #define LCD_xres	480
  #define LCD_yres	270

static u32 pseudo_palette[16];

static struct fb_info *mylcd_info;

static volatile unsigned long *GPBcon;
static volatile unsigned long *GPCcon;
static volatile unsigned long *GPDcon;
static volatile unsigned long *GPGcon;		//GPG4:控制LCD訊號
static volatile unsigned long *GPBdat;		//GPB0: 控制背光

/*	lcd  control	*/
static struct lcd_reg {
	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 reserved[9];  
	unsigned long dithmode;     
	unsigned long tpal ;            
	unsigned long lcdintpnd;
	unsigned long lcdsrcpnd;
	unsigned long lcdintmsk;
	unsigned long tconsel;  

} *lcd_reg;

/*	把單色提出來,放到rgb中對應的位置	*/
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 mylcd_fb_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;		//調色盤陣列不能大於15

	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 mylcd_fb_ops = {
	.owner = THIS_MODULE,
	.fb_setcolreg = mylcd_fb_setcolreg,
	.fb_fillrect = cfb_fillrect,            //填充矩形
	.fb_copyarea = cfb_copyarea,            //複製資料
	.fb_imageblit = cfb_imageblit,          //繪畫影象
};

static int lcd_init(void)
{
	printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*	1.申請fb_info結構體	*/
	mylcd_info = framebuffer_alloc(0, 0);
	
	/*	2.設定	*/
	/*	2.1設定固定引數	*/
	strcpy(mylcd_info->fix.id, "mylcd");					//名字
	mylcd_info->fix.smem_len = LCD_xres * LCD_yres * 2;		//長度
	mylcd_info->fix.type     = FB_TYPE_PACKED_PIXELS;		//支援的型別
	mylcd_info->fix.visual	 = FB_VISUAL_TRUECOLOR;			//真彩色
	mylcd_info->fix.line_length = LCD_xres * 2;
	
	printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*	2.2設定可變引數	*/
	mylcd_info->var.xres = 480;				//x
	mylcd_info->var.yres = 270;				//y
	mylcd_info->var.xres_virtual = 480;		//虛擬x
	mylcd_info->var.yres_virtual = 270;		//虛擬y
	mylcd_info->var.xoffset = 0;			//x偏移
	mylcd_info->var.yoffset = 0;			//y偏移

	mylcd_info->var.bits_per_pixel = 16;	//畫素
	mylcd_info->var.grayscale = 0;			//灰度
	
	
	printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*	rgb:565	*/
	mylcd_info->var.red.offset = 11;		//偏移位置
	mylcd_info->var.red.length = 5;			//長度
	mylcd_info->var.green.offset = 5;
	mylcd_info->var.green.length = 6;
	mylcd_info->var.blue.offset	= 0;
	mylcd_info->var.blue.length = 5;
		 
	/*	2.3設定操作函式	*/
	mylcd_info->fbops = &mylcd_fb_ops;

	printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*	2.4設定其他	*/
	mylcd_info->pseudo_palette = pseudo_palette;		//儲存調色盤陣列
	mylcd_info->screen_size = LCD_xres * LCD_yres * 2;	//虛擬地址長
	
	/*	3.硬體操作		*/
	/*	3.1設定gpio為lcd	*/
	GPBcon = ioremap(0x56000010, 8);
	GPBdat = GPBcon + 1;
	GPCcon = ioremap(0x56000020, 4);
	GPDcon = ioremap(0x56000030, 4);
	GPGcon = ioremap(0x56000060, 4);

	*GPBcon &= ~(0X3 << 0);
	*GPBcon |= (0X1 << 0);		//輸出
	*GPBdat &= ~(0X1 << 0);		//關背光

	*GPCcon = 0Xaaaaaaaa;
	*GPDcon = 0Xaaaaaaaa;
	*GPGcon |= (0x3 << (4 * 2));	//lcd_power
	
	/*	3.2設定lcd控制器	*/
	lcd_reg->lcdcon1 = (4<<8) | (3<<5) | (0xc<<1);
	lcd_reg->lcdcon2 = (3<<24) | (271<<14) | (1<<6) |(0<<0);
	lcd_reg->lcdcon3 = ((16)<<19) | (479<<8) | ((10));
	lcd_reg->lcdcon4 = (4);
	lcd_reg->lcdcon5 = (1<<11) | (1<<9) | (1<<8)|(1<<0);	  

	lcd_reg->lcdcon1 &= ~(1<<0);	//關閉PWREN訊號輸出
	lcd_reg->lcdcon5 &= ~(1<<3);	 //禁止PWREN訊號

	/*	3.3分配視訊記憶體	*/
	mylcd_info->screen_base = dma_alloc_writecombine(0, mylcd_info->fix.smem_len, mylcd_info->fix.smem_start, GFP_KERNEL);

	lcd_reg->lcdsaddr1 = (mylcd_info->fix.smem_start >> 1) & (0x3fffffff);
	lcd_reg->lcdsaddr2 = ((mylcd_info->fix.smem_start + mylcd_info->screen_size) >> 1) & 0x1fffff;
	lcd_reg->lcdsaddr3 = LCD_xres & 0x3ff;

	/*	4.1開啟lcd	*/
	lcd_reg->lcdcon1 |= (1 << 0);		//輸出power訊號
	lcd_reg->lcdcon2 |= (1 << 3);		//允許power訊號
	*GPBdat |= (1 << 0);				//開背光
	
	/*	4.2註冊fb_info	*/
	register_framebuffer(mylcd_info);

	return 0;
}

static int lcd_exit(void)
{
	printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	/*	1.解除安裝fb_info	*/
	unregister_framebuffer(mylcd_info);
	
	/*	2.控制lcd關閉,釋放io	*/
	lcd_reg->lcdcon1 &= ~(1 << 0);
	lcd_reg->lcdcon5 &= ~(1 << 3);
	*GPBdat &= ~(1 << 0);

	iounmap(GPBcon);
	iounmap(GPCcon);
	iounmap(GPDcon);
	iounmap(GPGcon);
	
	/*	3.釋放視訊記憶體	*/
	dma_free_writecombine(0, mylcd_info->screen_size, mylcd_info->screen_base, mylcd_info->fix.smem_start);
	
	/*	4.釋放fb_info	*/
	framebuffer_release(mylcd_info);

	return 0;
}

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

Makefile

KERN_DIR = /home/book/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