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;		//长度(像素为16bit[2字节],所以乘以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
posted @ 2020-08-09 15:32  人民广场的二道贩子  阅读(175)  评论(0编辑  收藏  举报