s3c2410 LCD驱动的结构分析

1、设备与驱动分离分层platform_device与platform_driver

1.1、s3c2410fb.c 与设备分离分层概念相似

s3c2410fb.c
	--> platform_driver
	static struct platform_driver s3c2410fb_driver = {
		.probe		= s3c2410fb_probe,	//探针,在匹配到相应的设备资源模块的时候得到执行
		.remove		= s3c2410fb_remove,	//移除,负责资源的释放等等
		.suspend	= s3c2410fb_suspend,//挂起
		.resume		= s3c2410fb_resume,	//恢复,与挂起相对
		.driver		= {					//名字匹配
			.name	= "s3c2410-lcd",
			.owner	= THIS_MODULE,
		},
	};
	//此函数在加载模块的时候会被执行到
	static int __init s3c2410fb_probe(struct platform_device *pdev)
		-->	struct s3c2410fb_info *info;	//S3C2410的framebuffer信息
				-->	u32			pseudo_pal[16];
					/* raw memory addresses */
					dma_addr_t		map_dma;	/* physical */
					u_char *		map_cpu;	/* virtual */
					u_int			map_size;

					struct s3c2410fb_hw	regs;

					/* addresses of pieces placed in raw buffer */
					u_char *		screen_cpu;	/* virtual address of buffer */
					dma_addr_t		screen_dma;	/* physical address of buffer */
			struct fb_info	   *fbinfo;	//framebuffer结构体
			struct s3c2410fb_hw *mregs;	//S3C2410LCD控制器结构体
			//mach_info里面有LCD的相关信息以及IO口定义
			mach_info = pdev->dev.platform_data;	//寻找核对S3C2410平台设备结构体有没有平台信息
			if (mach_info == NULL) {
				dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");
				return -EINVAL;
			}

			mregs = &mach_info->regs;	//得到LCD控制器寄存器信息

			irq = platform_get_irq(pdev, 0);	//里面是platform_get_resource(dev, IORESOURCE_IRQ, 0); 得到IRQ信息

			fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);	//分配一个fb_info结构体

			strcpy(fbinfo->fix.id, driver_name);	//拷贝驱动名信息 driver_name = “s3c2410fb”
			/*设定fb_info的固定参数*/
			fbinfo->fix.type	    = FB_TYPE_PACKED_PIXELS;
			fbinfo->fix.type_aux	    = 0;
			fbinfo->fix.xpanstep	    = 0;
			fbinfo->fix.ypanstep	    = 0;
			fbinfo->fix.ywrapstep	    = 0;
			fbinfo->fix.accel	    = FB_ACCEL_NONE;
			/*设定fb_info的可变参数*/
			fbinfo->var.nonstd	    = 0;
			fbinfo->var.activate	    = FB_ACTIVATE_NOW;
			fbinfo->var.height	    = mach_info->height;
			fbinfo->var.width	    = mach_info->width;
			fbinfo->var.accel_flags     = 0;
			fbinfo->var.vmode	    = FB_VMODE_NONINTERLACED;

			fbinfo->fbops		    = &s3c2410fb_ops;
			fbinfo->flags		    = FBINFO_FLAG_DEFAULT;
			fbinfo->pseudo_palette      = &info->pseudo_pal;	//调色板信息,调色板需要自己去构建

			fbinfo->var.xres	    = mach_info->xres.defval;
			fbinfo->var.xres_virtual    = mach_info->xres.defval;
			fbinfo->var.yres	    = mach_info->yres.defval;
			fbinfo->var.yres_virtual    = mach_info->yres.defval;
			fbinfo->var.bits_per_pixel  = mach_info->bpp.defval;

			fbinfo->var.upper_margin    = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1;
			fbinfo->var.lower_margin    = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1;
			fbinfo->var.vsync_len	    = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;

			fbinfo->var.left_margin	    = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
			fbinfo->var.right_margin    = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
			fbinfo->var.hsync_len	    = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;

			fbinfo->var.red.offset      = 11;
			fbinfo->var.green.offset    = 5;
			fbinfo->var.blue.offset     = 0;
			fbinfo->var.transp.offset   = 0;
			fbinfo->var.red.length      = 5;
			fbinfo->var.green.length    = 6;
			fbinfo->var.blue.length     = 5;
			fbinfo->var.transp.length   = 0;
			//整屏幕所需的空间,也就是framebuffer大小,单位字节
			fbinfo->fix.smem_len        =	mach_info->xres.max *
							mach_info->yres.max *
							mach_info->bpp.max / 8;
			/*清空调色板缓冲区,0x80000000*/
			for (i = 0; i < 256; i++)
				info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

			info->clk = clk_get(NULL, "lcd");	//得到LCD控制器时钟频率

			/* Initialize video memory */
			ret = s3c2410fb_map_video_memory(info);
				/*在内核里面分配一个屏幕framebuffer大小的空间*/
				-->	fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,&fbi->map_dma, GFP_KERNEL);
					fbinfo->screen_base	= fbi->map_cpu;	//返回分配之后的虚拟地址
					fbinfo->fix.smem_start  = fbi->map_dma;//物理内存起始地址
					
			ret = s3c2410fb_init_registers(info);	//设置IO口,以及LCD控制器寄存器
				-->	modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);
					modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
					modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);
					modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);

					writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
					writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
					writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
					writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
					writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
			ret = register_framebuffer(fbinfo);	//注册fb_info结构体
	//移除设备的时候调用到
	static int s3c2410fb_remove(struct platform_device *pdev)
		--> s3c2410fb_stop_lcd(info);	//LCD使能寄存器位关闭
			s3c2410fb_unmap_video_memory(info);
			irq = platform_get_irq(pdev, 0);
			free_irq(irq,info);
			release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
			unregister_framebuffer(fbinfo);
//定义平台设备驱动,与平台设备对应
static struct platform_driver s3c2410fb_driver = {
	.probe		= s3c2410fb_probe,
	.remove		= s3c2410fb_remove,
	.suspend	= s3c2410fb_suspend,
	.resume		= s3c2410fb_resume,
	.driver		= {
		.name	= "s3c2410-lcd",
		.owner	= THIS_MODULE,
	},
};

1.2、devs.c 这个跟前面记录的设备与驱动分离分层概念一致

// S3C2410的资源,也就是LCD控制寄存器块
devs.c      --> platform_device
static struct resource s3c_lcd_resource[] = {
	[0] = {
		.start = S3C24XX_PA_LCD,
		.end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_LCD,
		.end   = IRQ_LCD,
		.flags = IORESOURCE_IRQ,
	}
};
//定义平台设备,与平台设备驱动对应
struct platform_device s3c_device_lcd = {
	.name		  = "s3c2410-lcd",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_lcd_resource),
	.resource	  = s3c_lcd_resource,
	.dev              = {
		.dma_mask		= &s3c_device_lcd_dmamask,
		.coherent_dma_mask	= 0xffffffffUL
	}
};

2、fb_mem.c framebuffer核心函数提供

提供函数例如:
int register_framebuffer(struct fb_info *fb_info)

int unregister_framebuffer(struct fb_info *fb_info)

fb_mem.c:

类似于input.c一样的抽象层,应用程序读写设备的时候都会通过fb_mem.c来进行读写,fb_mem.c里面也会提供file_operations结构体进行读写函数的构建

3、初始化步骤

3.1、platform_driver

分配一个 fb_info结构体 framebuffer_alloc
设置它
硬件相关的操作
注册此结构体, framebuffer_register

3.2、platform_device

设定平台资源
static struct resource s3c_lcd_resource[]
设定平台设备
struct platform_device s3c_device_lcd

4、framebuffer驱动运行之猜测

4.1、消息传递

如果在操作系统界面执行 echo hellow > /dev/fb0
bash会负责把此消息传递给 fb0设备,当设备接收到消息之后有相应的驱动进行读取,读取到信息之后再由驱动进行写入 framebuffer的缓冲区

最后由LCD液晶进行显示

如果在操作系统界面执行 echo hellow > /dev/tty1
若执行此命令的话会由tty1调用到我们设置的fb_info结构体,这个通过在register_framebuffer函数里面构建的registered_fb[]数组得到,然后
tty1获取字符之后在framebuffer里面进行描述,然后LCD读取缓冲区里面的内容并显示到LCD屏幕上面

4.2、设备驱动的构建

/*进行fb字符设备驱动的注册,并创建类,绑定file_operations结构体*/
static int __init fbmem_init(void)
{
	create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);


	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
		printk("unable to get major %d for fb devs\n", FB_MAJOR);


	fb_class = class_create(THIS_MODULE, "graphics");
	if (IS_ERR(fb_class)) {
		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
		fb_class = NULL;
	}
	return 0;
}

/*在lcd驱动里面进行fb_info的注册之后调用到此函数,然后在已经创建的类上面创建一个设备,设备名由fb设备的数量决定*/
int register_framebuffer(struct fb_info *fb_info)
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);
				 
//之后执行上面的linux命令之后最后会有下面的file_operations结构体里面相应的处理函数进行相应的处理
static const struct file_operations fb_fops = {
	.owner =	THIS_MODULE,
	.read =		fb_read,
	.write =	fb_write,
	.ioctl =	fb_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = fb_compat_ioctl,
#endif
	.mmap =		fb_mmap,
	.open =		fb_open,
	.release =	fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
	.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	.fsync =	fb_deferred_io_fsync,
#endif
};


你可能感兴趣的:(ARM,framebuffer,linux驱动,fb_info)