《Linux驱动:s3c2440 lcd 驱动分析》

一,前言

s3c2440 lcd 驱动分析,涉及到的内容有,LCD图像显示原理、s3c2440的LCD控制器的操作、LCD驱动使用平台总线-设备-驱动模型的实例、LCD相关参数的设置、fb字符设备驱动实例、framebuffer的注册和管理、以及一次LCD显示的完整过程分析。

二,LCD原理和硬件分析

2.1 LCD原理解析

SDRAM:在SDRAM中申请了一块连续的内存作为LCD显示数据的存储,叫做显存(framebuffer)。
LCD控制器:LCD控制器通过硬件电路和LCD屏连接。
LCD屏:作为一个外设通过硬件电路和MCU(引脚配置为LCD引脚)连接。
在这里插入图片描述

图像在LCD屏上显示,可以看成是LCD控制器先从显存中取出一帧图像数据,然后输入到LCD屏上。480*272的屏,所显示的一帧有480*272个像素点、272行、480列。对于每一行的像素点,LCD控制器有一个VCLK信号控制,每来一个VCLK,显示的像素点就向右移动一个,当移动到这一行中的最后一个像素点时,LCD控制器有一个HSYNC信号,控制像素点跳到下一行的第一个像素显示。对于一帧图像(也叫一场),即当像素点移动到最后一行的最后一个位置显示完后,LCD控制器有一个VSYNC信号,控制像素点重新移动到第一行的第一个像素显示下一帧图像。

2.2 硬件电路

2.2.1 LCD背光电路

开启LCD显示,需要使能KEYBOARD(一般EN表示高电平有效,EN上面画一横表示低电平有效)开启背光。
在这里插入图片描述

2.2.2 LCD屏

VLINE:HSYNC信号输出引脚(由LCD控制器操作)
VFRAME:VSYNC信号输出引脚(由LCD控制器操作)
VCLK:VCLK信号输出引脚(由LCD控制器操作)
VD3~VD7:RGB(565)中B数据输出引脚
VD10~VD15:RGB(565)中G数据输出引脚
VD19~VD23:RGB(565)中R数据输出引脚
TS*:供ts触摸屏连接
在这里插入图片描述

2.2.3 S3c2440主控

涉及 GPG、GPD、GPC引脚。
VM:LCD控制器使能引脚(由LCD控制器的寄存器配置),开启LCD显示需要配置相关寄存器的相应位使能。
LCD_PWREN:LCD电源使能引脚(由LCD控制器的寄存器配置),开启LCD显示需要配置相关寄存器的相应位使能。
在这里插入图片描述

三,LCD应用平台总线-设备-驱动模型

3.1 lcd 设备的加载和注册

MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io	= S3C2410_PA_UART,
.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params	= S3C2410_SDRAM_PA + 0x100,

.init_irq	= s3c24xx_init_irq,
.map_io		= smdk2440_map_io,
.init_machine	= smdk2440_machine_init,
.timer		= &s3c24xx_timer,
MACHINE_END

将上面的宏展开


static const struct machine_desc __mach_desc_SMDK2440
 __attribute_used__
 __attribute__((__section__(".arch.info.init"))) = {
 .nr = MACH_TYPE_SMDK2410, /* architecture number */
 .name = "SMDK2440", /* architecture name */
 /* Maintainer: Jonas Dietsche */
 .phys_io = S3C2410_PA_UART, /* start of physical io */
 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
 .boot_params = S3C2410_SDRAM_PA + 0x100, /* tagged list */
 .map_io = smdk2440_map_io, /* IO mapping function */
 .init_irq = s3c24xx_init_irq,
 .init_machine = smdk2440_machine_init,
 .timer = &s3c24xx_timer,
}

MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(“.arch.info.init”),是初始化数据,Kernel 起来之后将被丢弃。
各个成员函数在不同时期被调用:

  1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
  2. init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被调用
  3. map_io 在 setup_arch() -> paging_init() -> devicemaps_init()被调用
    其他主要都在 setup_arch() 中用到。

系统初始化时,会调用smdk2440_machine_init

static void __init smdk2440_machine_init(void)
{
    // 这里设置LCD的参数,和驱动分离。这样要修改LCD时,驱动层程序可以不需要改动,只需修改设备层参数就行了,方便移植。
	s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);
    
    // 将smdk2440_devices数组中的设备注册到平台总线
	platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
	smdk_machine_init();
}

// smdk2440_devices数组
static struct platform_device *smdk2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c,
	&s3c_device_iis,
    &s3c2440_device_sdi,
};

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

// 设置lcd设备参数 smdk2440_lcd_cfg。各参数含义后面在probe分析时解析
/* 480x272 */ 
static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {
    .regs   = {
        .lcdcon1 =  S3C2410_LCDCON1_TFT16BPP | \
                S3C2410_LCDCON1_TFT | \
				  S3C2410_LCDCON1_CLKVAL(0x04),

        .lcdcon2 =  S3C2410_LCDCON2_VBPD(1) | \
                S3C2410_LCDCON2_LINEVAL(271) | \
                S3C2410_LCDCON2_VFPD(1) | \
                S3C2410_LCDCON2_VSPW(9),

        .lcdcon3 =  S3C2410_LCDCON3_HBPD(1) | \
                S3C2410_LCDCON3_HOZVAL(479) | \
                S3C2410_LCDCON3_HFPD(1),

        .lcdcon4 =  S3C2410_LCDCON4_HSPW(40),

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

    .gpccon      =  0xaaaaaaaa,
	.gpccon_mask	= 0xffffffff,
    .gpcup       =  0xffffffff,
	.gpcup_mask	= 0xffffffff,

    .gpdcon      =  0xaaaaaaaa,
	.gpdcon_mask	= 0xffffffff,
    .gpdup       =  0xffffffff,
	.gpdup_mask	= 0xffffffff,

    .fixed_syncs =  1,
    .type        =  S3C2410_LCDCON1_TFT, 
	.width		= 480,
	.height		= 272,

	.xres		= {
		.min	= 480,
		.max	= 480,
		.defval	= 480,
	},

	.yres		= {
        .max    =   272,
        .min    =   272,
        .defval =   272,
    },

    .bpp    = {
        .min    =   16,
        .max    =   16,
        .defval =   16,
    },
};

调用platform_device_register注册平台设备

int platform_add_devices(struct platform_device **devs, int num)
{
	int i, ret = 0;

	for (i = 0; i < num; i++) {
		ret = platform_device_register(devs[i]);
		if (ret) {
			while (--i >= 0)
				platform_device_unregister(devs[i]);
			break;
		}
	}

	return ret;
}

3.2 lcd 驱动的加载和注册

3.2.1 编译进内核,加载驱动

编译内核设置,make menuconfig

-> Device Drivers
  -> Graphics support
    <*> Support for frame buffer devices

linux-2.6.22.6/.config
CONFIG_FB_S3C2410=y
linux-2.6.22.6/drivers/video/Makefile
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o

然后make uImage,将s3c2410fb驱动编译进内核,系统启动便会加载,即调用驱动的s3c2410fb_init函数。

int __devinit s3c2410fb_init(void)
{
    // 注册到平台总线驱动
	return platform_driver_register(&s3c2410fb_driver);
}

3.3 lcd 设备和驱动的匹配

platform_driver_register->
	driver_register->
		bus_add_driver->
			driver_attach->
				bus_for_each_dev->
					__driver_attach

__driver_attach(kernel 2.6.22)

static int __driver_attach(struct device * dev, void * data)
{
	struct device_driver * drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */

	if (dev->parent)	/* Needed for USB */
		down(&dev->parent->sem);
	down(&dev->sem);
	if (!dev->driver)
		driver_probe_device(drv, dev); // 调用驱动层程序的 probe函数
	up(&dev->sem);
	if (dev->parent)
		up(&dev->parent->sem);

	return 0;
}

__driver_attach(kernel 3.4)

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
    
    // 匹配驱动和设备
	if (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

driver_probe_device 调用驱动层的probe

driver_probe_device->
    really_probe->
        drv->probe(dev)

四,probe函数分析(s3c2410fb_probe)

4.1 LCD 参数设置

4.1.1 固定参数设置

4.1.2 可变参数设置

4.1.3 其他参数设置

4.1.4 硬件相关设置(相关寄存器和引脚)

4.2 注册framebuffer

五,fbmem字符设备驱动

5.1 驱动加载并初始化

5.2 register_framebuffer

六,一次LCD显示的过程

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程界的小学生、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值