linux驱动 ----LCD驱动框架


前言

本文基于S3C2440开发板。

一、lcd驱动框架分析

在这里插入图片描述

所谓的lcd驱动框架,其实就是一个文件fbmen.c,这个文件里面封装了常用的lcd驱动程序框架,如注册驱动程序和与lcd相关的较为稳定的代码,这个框架,会创建设备类,但不会在该类下创建设备,这个需要有真正的设备内容,才能创建,这个真正的设备内容就是需要我们去编写的lcd驱动,想要在这个框架下写自己的驱动代码,也就是说想要用"fbmem.c"这一套代码时,就要按上图的框架来写代码,要自已定义底层的硬件驱动程序。

①定义一个fb_info结构体。
②实例化该结构体的内容,主要是一些硬件资源的描述和配置。
2.1 分配一个fb_info ,用这个函数 framebuffer_alloc(0, NULL);
2.2 实例化该结构体,设置固定参数和可变参数,设置操作函数,进行一些其他操作设置。
③把上面实例化的结构体传入函数register_framebuffer()进行注册,当该注册函数被调用时,才会真正在在上面所创建的设备类中创建设备。
Int register_framebuffer(struct fb_info *fb_info) -->if (!registered_fb[i]) //先找出一个空项。 -->fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), "fb%d", i);
在“fd_class”类下面创建设备。只有真正有硬件设备时才有必要在这个类下去创建设备。这样 mdev 或 udev 才能去自动创建设备节点。Fbmem.c 只是抽象出来的 LCD 驱动框架程序,并不能支持具体的驱动。它需要依赖底层的某个驱 动程序给它注册一个“fb_info”结构体。找到registered_fb数组中的空项,把结构体传进去,同时记录下这个数组的下标,创建为次设备号,用于上层app,打开设备是读取次设备号从而找到这个结构体的位置。

  • 具体的结构体和函数的原型
struct fb_info {
	int node;
	int flags;
	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;	
};
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
{
#define BYTES_PER_LONG (BITS_PER_LONG/8)
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
	int fb_info_size = sizeof(struct fb_info);
	struct fb_info *info;
	char *p;

	if (size)
		fb_info_size += PADDING;

	p = kzalloc(fb_info_size + size, GFP_KERNEL);

	if (!p)
		return NULL;

	info = (struct fb_info *) p;

	if (size)
		info->par = p + fb_info_size;

	info->device = dev;

#ifdef CONFIG_FB_BACKLIGHT
	mutex_init(&info->bl_curve_mutex);
#endif

	return info;
#undef PADDING
#undef BYTES_PER_LONG
}
int register_framebuffer(struct fb_info *fb_info)
{
	int i;
	struct fb_event event;
	struct fb_videomode mode;

	if (num_registered_fb == FB_MAX)
		return -ENXIO;
	num_registered_fb++;
	for (i = 0 ; i < FB_MAX; i++)
		if (!registered_fb[i])
			break;
	fb_info->node = i;

	fb_info->dev = device_create(fb_class, fb_info->device,
				     MKDEV(FB_MAJOR, i), "fb%d", i);
	if (IS_ERR(fb_info->dev)) {
		/* Not fatal */
		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
		fb_info->dev = NULL;
	} else
		fb_init_device(fb_info);

	if (fb_info->pixmap.addr == NULL) {
		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
		if (fb_info->pixmap.addr) {
			fb_info->pixmap.size = FBPIXMAPSIZE;
			fb_info->pixmap.buf_align = 1;
			fb_info->pixmap.scan_align = 1;
			fb_info->pixmap.access_align = 32;
			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
		}
	}	
	fb_info->pixmap.offset = 0;

	if (!fb_info->pixmap.blit_x)
		fb_info->pixmap.blit_x = ~(u32)0;

	if (!fb_info->pixmap.blit_y)
		fb_info->pixmap.blit_y = ~(u32)0;

	if (!fb_info->modelist.prev || !fb_info->modelist.next)
		INIT_LIST_HEAD(&fb_info->modelist);

	fb_var_to_videomode(&mode, &fb_info->var);
	fb_add_videomode(&mode, &fb_info->modelist);
	registered_fb[i] = fb_info;

	event.info = fb_info;
	fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
	return 0;
}

二、源码实例分析


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

static struct fb_info *s3c2440_lcd;
static volatile unsigned long  *gpbcon;
static volatile unsigned long  *gpbdat;
static volatile unsigned long  *gpccon;
static volatile unsigned long  *gpdcon;
static volatile unsigned long  *gpgcon;
static volatile struct lcd_regs * lcd_regs;
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 struct lcd_regs {
	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	lpcsel;
};
static int s3c_lcdfb_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;

	
	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 struct fb_ops s3c_lcdfb_ops = {
	.owner		= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,
	.fb_fillrect	        = cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};
static int lcd_init(void)
{
	s3c2440_lcd = framebuffer_alloc(0, NULL);
	
	strcpy(s3c2440_lcd->fix.id, "mylcd");
	s3c2440_lcd->fix.smem_len = 480*272*16/8;
	s3c2440_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
	s3c2440_lcd->fix.visual   = FB_VISUAL_TRUECOLOR; /* TFT */
	s3c2440_lcd->fix.line_length = 480*2;

	s3c2440_lcd->var.xres           = 480;
	s3c2440_lcd->var.yres           = 272;
	s3c2440_lcd->var.xres_virtual   = 480;
	s3c2440_lcd->var.yres_virtual   = 272;
	s3c2440_lcd->var.bits_per_pixel = 16;

	s3c2440_lcd->var.red.offset     = 11;
	s3c2440_lcd->var.red.length     = 5;
	
	s3c2440_lcd->var.green.offset   = 5;
	s3c2440_lcd->var.green.length   = 6;

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

	s3c2440_lcd->var.activate       = FB_ACTIVATE_NOW;


	s3c2440_lcd->fbops              = &s3c_lcdfb_ops;

	s3c2440_lcd->pseudo_palette = pseudo_palette;
	s3c2440_lcd->screen_size   = 480*272*16/8;

	gpbcon = ioremap(0x56000010, 8);
	gpbdat = gpbcon+1;
	gpccon = ioremap(0x56000020, 4);
	gpdcon = ioremap(0x56000030, 4);
	gpgcon = ioremap(0x56000060, 4);

	*gpccon  = 0xaaaaaaaa;   
	*gpdcon  = 0xaaaaaaaa;

	*gpbcon &= ~(3);  
	*gpbcon |= 1;
	*gpbdat &= ~1;    

	*gpgcon |= (3<<8); 

	lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));

	lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);

	lcd_regs->lcdcon2  = (1<<24) | (271<<14) | (1<<6) | (9<<0);

	lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);

	lcd_regs->lcdcon4 = (40<<0);

	lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);

	s3c2440_lcd->screen_base = dma_alloc_writecombine(NULL, s3c2440_lcd->fix.smem_len, &s3c2440_lcd->fix.smem_start, GFP_KERNEL);


	lcd_regs->lcdsaddr1  = (s3c2440_lcd->fix.smem_start >> 1) & ~(3<<30);
	lcd_regs->lcdsaddr2  = ((s3c2440_lcd->fix.smem_start + s3c2440_lcd->fix.smem_len) >> 1) & 0x1fffff;
	lcd_regs->lcdsaddr3  = (480*16/16);  
	
	lcd_regs->lcdcon1 |= (1<<0); 
	lcd_regs->lcdcon5 |= (1<<3); 
	*gpbdat |= 1;    
	
	register_framebuffer(s3c2440_lcd);

	
	return 0;
}
static void lcd_exit(void)
{
	unregister_framebuffer(s3c2440_lcd);
	lcd_regs->lcdcon1 &= ~(1<<0); 
	*gpbdat &= ~1;     
	dma_free_writecombine(NULL, s3c2440_lcd->fix.smem_len, s3c2440_lcd->screen_base, s3c2440_lcd->fix.smem_start);
	iounmap(lcd_regs);
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);
	framebuffer_release(s3c2440_lcd);
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");





三、实验结果

在这里插入图片描述

  • echo hello > /dev/tty1 // 可以在 LCD 上看见 hello 。 /dev/tty1
    是用的如下驱动程序:linux/drivers/video/fbcon.c – Low level frame buffer based
    console driver这个驱动程序最终也会用到“framebuffer”.
  • cat lcd.ko > /dev/fb0 // 花屏.是直接把 lcd.ko 中的内容直接扔到 LCD 上,根本不
    知道里面是什么格式的内容,肯定是花屏。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值