• 《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  */
    .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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    将上面的宏展开

    
    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,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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,
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    调用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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.2 lcd 驱动的加载和注册

    3.2.1 编译进内核,加载驱动

    编译内核设置,make menuconfig

    -> Device Drivers
      -> Graphics support
        <*> Support for frame buffer devices
    
    • 1
    • 2
    • 3

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.3 lcd 设备和驱动的匹配

    platform_driver_register->
    	driver_register->
    		bus_add_driver->
    			driver_attach->
    				bus_for_each_dev->
    					__driver_attach
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    __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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    __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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    driver_probe_device 调用驱动层的probe

    driver_probe_device->
        really_probe->
            drv->probe(dev)
    
    • 1
    • 2
    • 3

    四,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显示的过程

  • 相关阅读:
    亚信安全入选中国数据安全市场图谱
    [附源码]计算机毕业设计JAVAjsp美容院业务管理系统
    (十五)51单片机——呼吸灯与直流电机调速(PWM)
    企业怎么管理自己固定资产的
    Hadoop
    FPGA之旅设计99例之第十例-----串口上位机模拟OLED屏
    [从0开始机器学习]1.一元一次函数线性回归
    分布式计算模型Mapreduce实践与原理剖析(一)
    【Java】绘图入门和机制,绘图方法演示(绘制坦克)
    【微信小程序】用painter插件生成海报分享朋友圈简单教程
  • 原文地址:https://blog.csdn.net/qq_40709487/article/details/126337768