linux2.6.30内核中lcd驱动程序学习笔记3

这几天学了LCD驱动,先跟着韦东山的视频做了一次,后来又看到内核中自带的驱动和韦东山老师的不一样,所以看了看,大体上也懂了点,,在此记录一下,希望能给像我一样初学的人一些启发,也给自己做个笔记,方便以后时间长了忘了。

首先从mach-smdk2440.c文件中看LCD的板级配置

/* LCD driver info */

static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {

	.lcdcon5	= S3C2410_LCDCON5_FRM565 |
			  S3C2410_LCDCON5_INVVLINE |
			  S3C2410_LCDCON5_INVVFRAME |
			  S3C2410_LCDCON5_PWREN |
			  S3C2410_LCDCON5_HWSWP,

	.type		= S3C2410_LCDCON1_TFT,

#if 	defined(CONFIG_FB_S3C24X0_W320240)
	.width		= 320,
	.height		= 240,

	.pixclock		= 80000, /* HCLK 100 MHz, divisor 3 */
	.setclkval		= 0x3,
	.xres		= 320,
	.yres		= 240,
	.bpp		= 16,
	.left_margin	= 28,	/* for HFPD*/
	.right_margin	= 24,	/* for HBPD*/
	.hsync_len	= 42,	/* for HSPW*/
	.upper_margin	= 6,	/* for VFPD*/
	.lower_margin	= 2,	/* for VBPD*/
	.vsync_len	= 12,	/* for VSPW*/
#endif
};

主要是关于LCD帧缓冲区格式的设置,长,宽,每个像素用多少位表示,以及LCD的一些显示参数,LCD时钟的设置,LCD时钟设置与2440lcd控制器中寄存器里的lcdcon1有关,后面会看到,其实有几位就是设置时钟分频的,要根据lcd的数据手册设置分频系数,

这个是LCD的时序图,从图里可以看到有很多时间参数,这些参数要对这2440手册中的LCD部分进行设置,

这个是上面时序图的时间参数,

这.个是2440中的lcd控制器中的一个tft图片的时序格式,,可以把这个图对应上面的那个lcd的时序图,对LCD的参数进行设置;

.right_margin = 24, /* for HBPD*/
.hsync_len = 42, /* for HSPW*/
.upper_margin = 6, /* for VFPD*/
.lower_margin = 2, /* for VBPD*/
.vsync_len = 12, /* for VSPW*/

这几个参数就是根据上面的图设置的。

上面说到了s3c2410fb_display,这个结构体的原型是

/* LCD description */
struct s3c2410fb_display {
	/* LCD type */
	unsigned type;

	/* Screen size */
	unsigned short width;
	unsigned short height;

	/* Screen info */
	unsigned short xres;
	unsigned short yres;
	unsigned short bpp;

	unsigned pixclock;		/* pixclock in picoseconds */
	unsigned setclkval;           /*clkval*/	
	unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
	unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
	unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
	unsigned short upper_margin;	/* value in lines (TFT) or 0 (STN) */
	unsigned short lower_margin;	/* value in lines (TFT) or 0 (STN) */
	unsigned short vsync_len;	/* value in lines (TFT) or 0 (STN) */

	/* lcd configuration registers */
	unsigned long	lcdcon5;
};

这个结构体定义在fb.h文件中

然后接着上面的板级配置文件mach-smdk2440.c

static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
	.displays	= &smdk2440_lcd_cfg,
	.num_displays	= 1,
	.default_display = 0,

#if 0
	/* currently setup by downloader */
	.gpccon		= 0xaa940659,
	.gpccon_mask	= 0xffffffff,
	.gpcup		= 0x0000ffff,
	.gpcup_mask	= 0xffffffff,
	.gpdcon		= 0xaa84aaa0,
	.gpdcon_mask	= 0xffffffff,
	.gpdup		= 0x0000faff,
	.gpdup_mask	= 0xffffffff,
#endif

//	.lpcsel		= ((0xCE6) & ~7) | 1<<4,
};

这个结构体中又包含了上面的displays结构,

这个结构体的原型为

struct s3c2410fb_mach_info {

	struct s3c2410fb_display *displays;	/* attached diplays info */
	unsigned num_displays;			/* number of defined displays */
	unsigned default_display;

	/* GPIOs */

	unsigned long	gpcup;
	unsigned long	gpcup_mask;
	unsigned long	gpccon;
	unsigned long	gpccon_mask;
	unsigned long	gpdup;
	unsigned long	gpdup_mask;
	unsigned long	gpdcon;
	unsigned long	gpdcon_mask;

	/* lpc3600 control register */
	unsigned long	lpcsel;
};

定义在Fb.h文件中,含义是帧缓冲板级信息结构

除了上面两个结构体是关于lcd驱动的之外,还有

static struct platform_device *smdk2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c0,
	&s3c_device_iis,
        &s3c_device_dm9000,
};

发现这里面有lcd的平台设备,展开为

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
	}
};


tatic 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,
	}

};

static u64 s3c_device_lcd_dmamask = 0xffffffffUL;


这个结构定义在linux/arch/arm/plat-s3c24xx/devs.c下,属于平台驱动中的内容,这里不做解释,s3c_lcd_resource[]中定义了两个资源,一个是关于它的IO寄存器的资源

另一个是关于lcd中断的资源,这些会在以后的lcd驱动中被调出来用

回来继续看我们的mach-smdk2440.c文件

板级资源要注册到系统中通过这个函数

static void __init smdk2440_machine_init(void)
{
	s3c24xx_fb_set_platdata(&smdk2440_fb_info);
	s3c_i2c0_set_platdata(NULL);

	platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
	smdk_machine_init();
}


发现里面有platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));

即把平台设备加入到系统中

此外还有一个函数是关于fb的s3c24xx_fb_set_platdata(&smdk2440_fb_info);

看看里面都做了什么

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
	struct s3c2410fb_mach_info *npd;

	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
	if (npd) {
		memcpy(npd, pd, sizeof(*npd));
		s3c_device_lcd.dev.platform_data = npd;
	} else {
		printk(KERN_ERR "no memory for LCD platform data\n");
	}
}

通过看这个函数知道这里把smdk2440_fb_info这个结构体加入到了s3c_device_lcd.dev.platform_data中,这样的话后面就可以通过平台设备访问到前面我们定义的s3c2410fb_display 结构中的信息了,这就是板级配置文件中关于lcd信息的内容

下面我们再来看系统带的lcd平台驱动linux/drivers/video/s3c2410fb.c

首先从模块加载卸载函数看起

int __init s3c2410fb_init(void)
{
	int ret = platform_driver_register(&s3c2410fb_driver);

	if (ret == 0)
		ret = platform_driver_register(&s3c2412fb_driver);;

	return ret;
}

static void __exit s3c2410fb_cleanup(void)
{
	platform_driver_unregister(&s3c2410fb_driver);
	platform_driver_unregister(&s3c2412fb_driver);
}

module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup);

加载的时候注册了一个平台设备驱动

驱动的结构为

static struct platform_driver s3c2412fb_driver = {
	.probe		= s3c2412fb_probe,
	.remove		= s3c2410fb_remove,
	.suspend	= s3c2410fb_suspend,
	.resume		= s3c2410fb_resume,
	.driver		= {
		.name	= "s3c2412-lcd",
		.owner	= THIS_MODULE,
	},
};


结构定义了属于平台驱动机制的结构函数,有probe函数,resume函数,suspend函数等,驱动的名字要与前面定义的平台设备的名字相同,这样,在注册驱动时,驱动和设备才能正确的匹配,platform机制才能工作,probe函数才能执行,能对帧缓冲格式进行设置

所以probe函数是很关键的

static int __init s3c2410fb_probe(struct platform_device *pdev)
{
	return s3c24xxfb_probe(pdev, DRV_S3C2410);
}
此驱动支持s3c2410和s3c2412所以probe函数本来只有一个形参,进过这么嵌套后定义了用户想有的形参。

static int __init s3c24xxfb_probe(struct platform_device *pdev,
				  enum s3c_drv_type drv_type)
{
	struct s3c2410fb_info *info;
	struct s3c2410fb_display *display;
	struct fb_info *fbinfo;
	struct s3c2410fb_mach_info *mach_info;
	struct resource *res;
	int ret;
	int irq;
	int i;
	int size;
	u32 lcdcon1;

	mach_info = pdev->dev.platform_data;
	if (mach_info == NULL) {
		dev_err(&pdev->dev,
			"no platform data for lcd, cannot attach\n");
		return -EINVAL;
	}

	if (mach_info->default_display >= mach_info->num_displays) {
		dev_err(&pdev->dev, "default is %d but only %d displays\n",
			mach_info->default_display, mach_info->num_displays);
		return -EINVAL;
	}

	display = mach_info->displays + mach_info->default_display;

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "no irq for device\n");
		return -ENOENT;
	}

	fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
	if (!fbinfo)
		return -ENOMEM;

	platform_set_drvdata(pdev, fbinfo);

	info = fbinfo->par;
	info->dev = &pdev->dev;
	info->drv_type = drv_type;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "failed to get memory registers\n");
		ret = -ENXIO;
		goto dealloc_fb;
	}

	size = (res->end - res->start) + 1;
	info->mem = request_mem_region(res->start, size, pdev->name);
	if (info->mem == NULL) {
		dev_err(&pdev->dev, "failed to get memory region\n");
		ret = -ENOENT;
		goto dealloc_fb;
	}

	info->io = ioremap(res->start, size);
	if (info->io == NULL) {
		dev_err(&pdev->dev, "ioremap() of registers failed\n");
		ret = -ENXIO;
		goto release_mem;
	}

	info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

	dprintk("devinit\n");

	strcpy(fbinfo->fix.id, driver_name);

	/* Stop the video */
	lcdcon1 = readl(info->io + S3C2410_LCDCON1);
	writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

	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;

	fbinfo->var.nonstd	    = 0;
	fbinfo->var.activate	    = FB_ACTIVATE_NOW;
	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;

	for (i = 0; i < 256; i++)
		info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

	ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
	if (ret) {
		dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
		ret = -EBUSY;
		goto release_regs;
	}

	info->clk = clk_get(NULL, "lcd");
	if (!info->clk || IS_ERR(info->clk)) {
		printk(KERN_ERR "failed to get lcd clock source\n");
		ret = -ENOENT;
		goto release_irq;
	}

	clk_enable(info->clk);
	dprintk("got and enabled clock\n");

	msleep(1);

	/* find maximum required memory size for display */
	for (i = 0; i < mach_info->num_displays; i++) {
		unsigned long smem_len = mach_info->displays[i].xres;

		smem_len *= mach_info->displays[i].yres;
		smem_len *= mach_info->displays[i].bpp;
		smem_len >>= 3;
		if (fbinfo->fix.smem_len < smem_len)
			fbinfo->fix.smem_len = smem_len;
	}

	/* Initialize video memory */
	ret = s3c2410fb_map_video_memory(fbinfo);
	if (ret) {
		printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
		ret = -ENOMEM;
		goto release_clock;
	}

	dprintk("got video memory\n");

	fbinfo->var.xres = display->xres;
	fbinfo->var.yres = display->yres;
	fbinfo->var.bits_per_pixel = display->bpp;

	s3c2410fb_init_registers(fbinfo);

	s3c2410fb_check_var(&fbinfo->var, fbinfo);

	ret = register_framebuffer(fbinfo);
	if (ret < 0) {
		printk(KERN_ERR "Failed to register framebuffer device: %d\n",
			ret);
		goto free_video_memory;
	}

	/* create device files */
	ret = device_create_file(&pdev->dev, &dev_attr_debug);
	if (ret) {
		printk(KERN_ERR "failed to add debug attribute\n");
	}

	printk(KERN_INFO "fb%d: %s frame buffer device\n",
		fbinfo->node, fbinfo->fix.id);

	return 0;

free_video_memory:
	s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
	clk_disable(info->clk);
	clk_put(info->clk);
release_irq:
	free_irq(irq, info);
release_regs:
	iounmap(info->io);
release_mem:
	release_resource(info->mem);
	kfree(info->mem);
dealloc_fb:
	platform_set_drvdata(pdev, NULL);
	framebuffer_release(fbinfo);
	return ret;
}

probe函数是学习本驱动的关键,下面对这个函数中做的进行简要分析

函数中关键的一些步骤按顺序取了出来

struct s3c2410fb_mach_info *mach_info;
mach_info = pdev->dev.platform_data;
说明s3c2410fb_mach_info *mach_info结构被放到了
platform_device *pdev->dev.platform_data;中,在前面的void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)函数中实现了,这里再通过pdev再取出来,因为平台驱动机制的关键就是传递用户定义的板级参数,实现驱动程序隔离硬件区别,

irq = platform_get_irq(pdev, 0);得到平台设备中的中断资源,中断号
struct fb_info *fbinfo;
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
分配一个新的帧缓存结构

platform_set_drvdata(pdev, fbinfo);设置pdev->driver_data = fbinfo;

struct s3c2410fb_info *info;
info = fbinfo->par;赋给info 地址,以后操作info就是操作fbinfo->par
info->dev = &pdev->dev;
info->drv_type = drv_type;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);得到平台设备的内存资源
返回资源结构体,这里得到的是s3c2440中lcd控制器寄存器的物理地址

info->mem = request_mem_region(res->start, size, pdev->name);

展开为

#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
分配一块IO内存

info->io = ioremap(res->start, size);物理地址映射为虚拟地址

strcpy(fbinfo->fix.id, driver_name);拷贝driver_name到fbinfo->fix.id

/* Stop the video */
 lcdcon1 = readl(info->io + S3C2410_LCDCON1);
 writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
先把lcdcon1寄存器中的值读出来,然后再写心值

开始对lcd控制器进行设置了,先把lcd使能关了,最后设好后才开

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;

 fbinfo->var.nonstd     = 0;
 fbinfo->var.activate     = FB_ACTIVATE_NOW;
 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;
 设置一些参数

info->clk = clk_get(NULL, "lcd");
clk_enable(info->clk);
得到lcd的时钟,这地方我也不懂,,希望有人懂的指点
ret = s3c2410fb_map_video_memory(fbinfo);
分配显存,展开这个函数为

struct s3c2410fb_info *fbi = info->par;//与前面我们提到的info = fbinfo->par联系
传入一些信息
info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
         &map_dma, GFP_KERNEL);
分配显存

*
 * s3c2410fb_map_video_memory():
 *	Allocates the DRAM memory for the frame buffer.  This buffer is
 *	remapped into a non-cached, non-buffered, memory region to
 *	allow palette and pixel writes to occur without flushing the
 *	cache.  Once this area is remapped, all virtual memory
 *	access to the video memory should occur at the new region.
 */
static int __init s3c2410fb_map_video_memory(struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	dma_addr_t map_dma;
	unsigned map_size = PAGE_ALIGN(info->fix.smem_len);

	dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);

	info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
						   &map_dma, GFP_KERNEL);

	if (info->screen_base) {
		/* prevent initial garbage on screen */
		dprintk("map_video_memory: clear %p:%08x\n",
			info->screen_base, map_size);
		memset(info->screen_base, 0x00, map_size);

		info->fix.smem_start = map_dma;得到显存的物理地址

		dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
			info->fix.smem_start, info->screen_base, map_size);
	}

	return info->screen_base ? 0 : -ENO
}


然后
fbinfo->var.xres = display->xres;行的长度
 fbinfo->var.yres = display->yres;列的长度
 fbinfo->var.bits_per_pixel = display->bpp;
把板级配置中的信息传到fbinfo中
出现了这个函数,看看里面做了一些什么
s3c2410fb_init_registers(fbinfo);

/*
 * s3c2410fb_init_registers - Initialise all LCD-related registers
 */
static int s3c2410fb_init_registers(struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
	unsigned long flags;
	void __iomem *regs = fbi->io;
	void __iomem *tpal;
	void __iomem *lpcsel;

	if (is_s3c2412(fbi)) {
		tpal = regs + S3C2412_TPAL;
		lpcsel = regs + S3C2412_TCONSEL;
	} else {
		tpal = regs + S3C2410_TPAL;
		lpcsel = regs + S3C2410_LPCSEL;
	}

	/* Initialise LCD with values from haret */

	local_irq_save(flags);

	/* modify the gpio(s) with interrupts set (bjd) */

	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);

	local_irq_restore(flags);

	dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);
	writel(mach_info->lpcsel, lpcsel);

	dprintk("replacing TPAL %08x\n", readl(tpal));

	/* ensure temporary palette disabled */
	writel(0x00, tpal);

	return 0;
}


函数里主要设置了与lcd控制器寄存器相关的IO口寄存器的配置
s3c2410fb_check_var(&fbinfo->var, fbinfo);
检查、设置fbinfo->var中设置的参数,

ret = register_framebuffer(fbinfo);

到这,probe函数的主要函数就完了,它主要实现的就是初始化了与lcd接口相关的Io口设置、把板级配置中设的关于lcd的显示参数传入到struct fb_info结构体中,然后用注册函数,把这个结构体注册到系统中,但是我们发现关于lcd控制的寄存器lcdcon1,lcdcon2,lcdcon3等的寄存器没有配置啊,没有配置那些寄存器lcd肯定是不能工作的,

于是我找了半天,,终于找到了

/* s3c2410fb_activate_var
 *
 * activate (set) the controller from the given framebuffer
 * information
 */
static void s3c2410fb_activate_var(struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	void __iomem *regs = fbi->io;
	int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
	struct fb_var_screeninfo *var = &info->var;
	struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
	struct s3c2410fb_display *default_display = mach_info->displays +
						    mach_info->default_display;
	int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;

	dprintk("%s: var->xres  = %d\n", __func__, var->xres);
	dprintk("%s: var->yres  = %d\n", __func__, var->yres);
	dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);

	if (type == S3C2410_LCDCON1_TFT) {
		s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
		--clkdiv;
		if (clkdiv < 0)
			clkdiv = 0;
	} else {
		s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
		if (clkdiv < 2)
			clkdiv = 2;
	}

//	fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
	fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(default_display->setclkval);

	/* write new registers */

	dprintk("new register set:\n");
	dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
	dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
	dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
	dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
	dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);

	writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
		regs + S3C2410_LCDCON1);
	writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
	writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
	writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
	writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);

	/* set lcd address pointers */
	s3c2410fb_set_lcdaddr(info);

	fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
	writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
}


具体的解释不用多说了,这函数实现了设置lcd控制寄存器的任务,但是它在哪被调用呢

/*
 *      s3c2410fb_set_par - Alters the hardware state.
 *      @info: frame buffer structure that represents a single frame buffer
 *
 */
static int s3c2410fb_set_par(struct fb_info *info)
{
	struct fb_var_screeninfo *var = &info->var;

	switch (var->bits_per_pixel) {
	case 32:
	case 16:
	case 12:
		info->fix.visual = FB_VISUAL_TRUECOLOR;
		break;
	case 1:
		info->fix.visual = FB_VISUAL_MONO01;
		break;
	default:
		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
		break;
	}

	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

	/* activate this new configuration */

	s3c2410fb_activate_var(info);
	return 0;
}


static struct fb_ops s3c2410fb_ops = {
	.owner		= THIS_MODULE,
	.fb_check_var	= s3c2410fb_check_var,
	.fb_set_par	= s3c2410fb_set_par,
	.fb_blank	= s3c2410fb_blank,
	.fb_setcolreg	= s3c2410fb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};


这个是S3C2410的帧缓冲操作函数,操作函数里定义了.fb_set_par = s3c2410fb_set_par,

在LCD驱动加载时会执行到它









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值