一,前言
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 .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
上一篇:《Linux驱动:s3c2440 lcd 驱动分析--终结篇》
下一篇:《Linux驱动:s3c2410/s3c2440 ts驱动分析--终结篇》
推荐阅读最新更新时间:2024-11-02 08:10
设计资源 培训 开发板 精华推荐
- EC2630QI 12V DC-DC 中压总线转换器的典型应用,27W 和 4.5A
- 【新疆工程学院】ne555制作呼吸灯
- AL5811EV2,使用 AL5811、75mA、60V 低侧可调线性 LED 驱动器的评估板
- LTC1687、52 Mbps 精密延迟 RS485 故障安全收发器的典型应用电路
- 移动底盘|全向移动智能垃圾桶
- LTC3535EDD 演示板,双通道 550mA 1MHz 同步升压型 DC/DC 转换器
- LTC2945IMS 轨到轨双向电流和功率监视器的典型应用
- 无桥PFC电源基础仿真电路
- 使用 Analog Devices 的 LTC2945CUD-1 的参考设计
- TCR6DA1829、200mA、1.8V 和 2.9V 输出电压双路输出 CMOS 低压降稳压器的典型应用