Linux设备驱动--LCD平台设备与驱动(tiny4412)

1 环境与简介

    Host:Ubuntu14.04(64bit)

    Target:Tiny4412

    Kernel:linux-3.5.0

2 平台设备

2.1 声明

extern struct platform_device s5p_device_fimd0;

头文件:arch/arm/plat-samsung/include/plat/devs.h

对比分析:与《Linux设备驱动--LCD平台设备与驱动(smdk2440)》不同的是,这里不是通过EXPORT_SYMBOL(s5p_device_fimd0)使其在别的源文件可见,而是在头文件中声明该变量。

2.2 定义

    先定义一个通用版的s5p_device_fimd0,其名字为s5p-fb,这个名字在后面中还会根据实际使用的配置进行设置。

struct platform_device s5p_device_fimd0 = {
    .name       = "s5p-fb",
    .id     = 0,
    .num_resources  = ARRAY_SIZE(s5p_fimd0_resource),
    .resource   = s5p_fimd0_resource,
    .dev        = {
        .dma_mask       = &samsung_device_dma_mask,
        .coherent_dma_mask  = DMA_BIT_MASK(32),
    },
}; 

源文件:arch/arm/plat-samsung/devs.c

对比分析:与《Linux设备驱动--LCD平台设备与驱动(smdk2440)》和《Linux设备驱动--LCD平台设备与驱动(smdk6410)》不同的是,这里平台设备并不命名为xxx_device_lcd或者xxx_device_fb,而是命名为xxx_device_fimd。前面两个都命名都容易让人想到是与显示相关的,但是后面的命名却让不熟悉的人感到困惑。其实,fimdfully Interactive mobile display的缩写,意为完全交互式移动显示设备[2]。知道这层之后,就不会对xxx_device_fimd感到困惑了。

2.3 引用

    定义一个平台设备数组smdk4x12_devices,该数组包含了Tiny4412开发板的所有平台设备,其中当然包括上述s5p_device_fimd0,如下第35行所示:

static struct platform_device *smdk4x12_devices[] __initdata = {
#ifdef CONFIG_EXYNOS4_DEV_DWMCI
	&exynos_device_dwmci,
#endif
	&s3c_device_hsmmc2,
	&s3c_device_hsmmc3,
	&wm8994_fixed_voltage0,
	&wm8994_fixed_voltage1,
	&wm8994_fixed_voltage2,
	&s3c_device_i2c0,
	&s3c_device_i2c1,
	&s3c_device_i2c2,
	&s3c_device_i2c3,
#ifdef CONFIG_VIDEO_M5MOLS
	&s3c_device_i2c4,
#endif
	&s3c_device_i2c7,
	&s3c_device_adc,
	&s3c_device_rtc,
	&s3c_device_wdt,
#ifdef CONFIG_TINY4412_BUZZER
	&s3c_device_timer[0],
#endif
#ifdef CONFIG_VIDEO_EXYNOS_FIMC_LITE
	&exynos_device_flite0,
	&exynos_device_flite1,
#endif
	&s5p_device_mipi_csis0,
	&s5p_device_mipi_csis1,
	&s5p_device_fimc0,
	&s5p_device_fimc1,
	&s5p_device_fimc2,
	&s5p_device_fimc3,
	&s5p_device_fimc_md,
	&s5p_device_fimd0,
	&mali_gpu_device,
	&s5p_device_mfc,
	&s5p_device_mfc_l,
	&s5p_device_mfc_r,
	&s5p_device_jpeg,
#ifdef CONFIG_SAMSUNG_DEV_KEYPAD
	&samsung_device_keypad,
#endif
	&tiny4412_device_1wire,
	&tiny4412_device_adc,
#ifdef CONFIG_INPUT_GPIO
	&tiny4412_input_device,
#endif
#ifdef CONFIG_IR_GPIO_CIR
	&tiny4412_device_gpiorc,
#endif
#ifdef CONFIG_VIDEO_EXYNOS_FIMC_IS
	&exynos4_device_fimc_is,
#endif
#ifdef CONFIG_LCD_LMS501KF03
	&s3c_device_spi_gpio,
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI0
	&s3c64xx_device_spi0,
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI1
	&s3c64xx_device_spi1,
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI2
	&s3c64xx_device_spi2,
#endif
#ifdef CONFIG_ION_EXYNOS
	&exynos_device_ion,
#endif
	&s5p_device_i2c_hdmiphy,
	&s5p_device_hdmi,
	&s5p_device_mixer,
	&exynos4_bus_devfreq,
	&samsung_asoc_dma,
	&samsung_asoc_idma,
#ifdef CONFIG_SND_SAMSUNG_I2S
	&exynos4_device_i2s0,
#endif
#ifdef CONFIG_SND_SAMSUNG_PCM
	&exynos4_device_pcm0,
#endif
#ifdef CONFIG_SND_SAMSUNG_SPDIF
	&exynos4_device_spdif,
#endif
	&tiny4412_audio,
#ifdef CONFIG_VIDEO_EXYNOS_FIMG2D
	&s5p_device_fimg2d,
#endif
#ifdef CONFIG_EXYNOS_THERMAL
	&exynos_device_tmu,
#endif
	&s5p_device_ehci,
	&exynos4_device_ohci,
	&s5p_device_usbswitch,
#if defined CONFIG_SND_SAMSUNG_ALP
	&exynos_device_srp,
#endif
#ifdef CONFIG_BUSFREQ_OPP
	&exynos4_busfreq,
#endif
#ifdef CONFIG_BATTERY_SAMSUNG
	&samsung_device_battery,
#endif
};

源文件:arch/arm/mach-exynos/mach-tiny4412.c

2.4 注册

(1)注册函数

    在smdk4x12_machine_init()函数中调用platform_add_devices()函数将上述smdk4x12_devices注册到系统,即可完成平台设备的注册,如下第121行所示:

static void __init smdk4x12_machine_init(void)
{
#ifdef CONFIG_TOUCHSCREEN_FT5X0X
	struct s3cfb_lcd *lcd = tiny4412_get_lcd();
	ft5x0x_pdata.screen_max_x = lcd->width;
	ft5x0x_pdata.screen_max_y = lcd->height;
#endif

	exynos_bootdev_init();
	tiny4412_hwrev_init();

#ifdef CONFIG_S3C64XX_DEV_SPI0
	spi_register_board_info(spi0_board_info, ARRAY_SIZE(spi0_board_info));
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI1
	spi_register_board_info(spi1_board_info, ARRAY_SIZE(spi1_board_info));
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI2
	spi_register_board_info(spi2_board_info, ARRAY_SIZE(spi2_board_info));
#endif

	if (samsung_pack() != EXYNOS4412_PACK_SCP) {
#ifdef CONFIG_REGULATOR_MAX77686
		max77686_populate_pdata();
#endif
	}

	s3c_adc_set_platdata(NULL);
	s3c_adc_setname("samsung-adc-v4");

	s3c_i2c0_set_platdata(&tiny4412_i2c0_data);
	i2c_register_board_info(0, smdk4x12_i2c_devs0,
			ARRAY_SIZE(smdk4x12_i2c_devs0));

	s3c_i2c1_set_platdata(&tiny4412_i2c1_data);
	i2c_register_board_info(1, smdk4x12_i2c_devs1,
			ARRAY_SIZE(smdk4x12_i2c_devs1));

	s3c_i2c2_set_platdata(NULL);
	i2c_register_board_info(2, smdk4x12_i2c_devs2,
			ARRAY_SIZE(smdk4x12_i2c_devs2));

	s3c_i2c3_set_platdata(&tiny4412_i2c3_data);
	i2c_register_board_info(3, smdk4x12_i2c_devs3,
			ARRAY_SIZE(smdk4x12_i2c_devs3));

	s3c_i2c4_set_platdata(NULL);

	smdk4x12_rtc_wake_init();
	smdk4x12_pmu_wdt_init();
	smdk4x12_touch_init();

	if (is_board_rev_B()) {
#ifdef CONFIG_INPUT_GPIO
		tiny4412_key_info_fixup();
#endif
	} else {
		tiny4412_wifi_init();
	}

	s3c_i2c7_set_platdata(&tiny4412_i2c7_data);
	i2c_register_board_info(7, smdk4x12_i2c_devs7,
			ARRAY_SIZE(smdk4x12_i2c_devs7));

	s3c_hsotg_set_platdata(&smdk4x12_hsotg_pdata);
#ifdef CONFIG_USB_EXYNOS_SWITCH
	smdk4x12_usbswitch_init();
#endif
	samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);

	tiny4412_fb_init_pdata(&smdk4x12_lcd0_pdata);
	s5p_fimd0_set_platdata(&smdk4x12_lcd0_pdata);
#ifdef CONFIG_LCD_LMS501KF03
	spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
#endif

#ifdef CONFIG_SAMSUNG_DEV_KEYPAD
	samsung_keypad_set_platdata(&smdk4x12_keypad_data);
#endif

#ifdef CONFIG_EXYNOS4_DEV_DWMCI
	exynos_dwmci_set_platdata(&exynos_dwmci_pdata);
#endif

	s3c_sdhci2_set_platdata(&smdk4x12_hsmmc2_pdata);
	s3c_sdhci3_set_platdata(&smdk4x12_hsmmc3_pdata);

#ifdef CONFIG_ION_EXYNOS
	exynos_ion_set_platdata();
#endif
	s5p_tv_setup();
	s5p_i2c_hdmiphy_set_platdata(NULL);
	s5p_hdmi_set_platdata(smdk4x12_i2c_hdmiphy, NULL, 0);

#ifdef CONFIG_VIDEO_EXYNOS_FIMG2D
	s5p_fimg2d_set_platdata(&fimg2d_data);
#endif
#if defined(CONFIG_VIDEO_M5MOLS) || defined(CONFIG_VIDEO_S5K6A3)
	smdk4x12_camera_init();
#endif
#ifdef CONFIG_VIDEO_EXYNOS_FIMC_LITE
	smdk4x12_set_camera_flite_platdata();
	s3c_set_platdata(&exynos_flite0_default_data,
			sizeof(exynos_flite0_default_data), &exynos_device_flite0);
	s3c_set_platdata(&exynos_flite1_default_data,
			sizeof(exynos_flite1_default_data), &exynos_device_flite1);
#endif
	smdk4x12_ehci_init();

#ifdef CONFIG_S3C64XX_DEV_SPI0
	s3c64xx_spi0_set_platdata(NULL, 0, 1);
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI1
	s3c64xx_spi1_set_platdata(NULL, 0, 1);
#endif
#ifdef CONFIG_S3C64XX_DEV_SPI2
	s3c64xx_spi2_set_platdata(NULL, 0, 1);
#endif

	smdk4x12_ohci_init();
	platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices));
	if (!uhost0)
		platform_device_register(&s3c_device_usb_hsotg);

#ifdef CONFIG_VIDEO_EXYNOS_FIMC_IS
	exynos4_fimc_is_set_platdata(NULL);
#endif

	if (soc_is_exynos4412()) {
		if ((samsung_rev() >= EXYNOS4412_REV_2_0))
			initialize_prime_clocks();
		else
			initialize_non_prime_clocks();

#ifdef CONFIG_S3C64XX_DEV_SPI0
		exynos4_setup_clock(&s3c64xx_device_spi0.dev, "mdout_spi",
				"mout_mpll_user", 50 * MHZ);
#endif
	}
#ifdef CONFIG_BUSFREQ_OPP
	dev_add(&busfreq, &exynos4_busfreq.dev);
	ppmu_init(&exynos_ppmu[PPMU_DMC0], &exynos4_busfreq.dev);
	ppmu_init(&exynos_ppmu[PPMU_DMC1], &exynos4_busfreq.dev);
	ppmu_init(&exynos_ppmu[PPMU_CPU], &exynos4_busfreq.dev);
#endif
	set_tmu_platdata();
}

源文件:arch/arm/mach-exynos/mach-tiny4412.c

(2)注册时机

    上述smdk4x12_machine_init()函数是何时被调用的呢?答案是在内核初始化设备的时候,如下第9行所示:

MACHINE_START(TINY4412, "TINY4412")
	/* Maintainer: FriendlyARM (www.arm9.net) */
	/* Maintainer: Kukjin Kim  */
	/* Maintainer: Changhwan Youn  */
	.atag_offset	= 0x100,
	.init_irq	= exynos4_init_irq,
	.map_io		= smdk4x12_map_io,
	.handle_irq	= gic_handle_irq,
	.init_machine	= smdk4x12_machine_init,
	.init_late	= exynos_init_late,
	.timer		= &exynos4_timer,
	.restart	= exynos4_restart,
	.reserve	= &smdk4x12_reserve,
MACHINE_END

源文件:arch/arm/mach-exynos/mach-tiny4412.c

3 平台驱动

3.1 声明

extern struct platform_driver fimd_driver;
头文件:drivers/gpu/drm/exynos/exynos_drm_drv.h

3.2 定义

struct platform_driver fimd_driver = {
	.probe      = fimd_probe,
	.remove     = __devexit_p(fimd_remove),
	.driver     = {  
	  .name   = "exynos4-fb",
	  .owner  = THIS_MODULE,
	  .pm = &fimd_pm_ops,
	},
}; 
源文件:drivers/gpu/drm/exynos/exynos_drm_fimd.c

3.3 注册

    在exynos_drm_init()函数中调用platform_driver_register()将上述fimd_driver注册到系统中,例如下面第8行所示。

static int __init exynos_drm_init(void)
{
	int ret;

	DRM_DEBUG_DRIVER("%s\n", __FILE__);

#ifdef CONFIG_DRM_EXYNOS_FIMD
	ret = platform_driver_register(&fimd_driver);
	if (ret < 0)
		goto out_fimd;
#endif

#ifdef CONFIG_DRM_EXYNOS_HDMI
	ret = platform_driver_register(&hdmi_driver);
	if (ret < 0)
		goto out_hdmi;
	ret = platform_driver_register(&mixer_driver);
	if (ret < 0)
		goto out_mixer;
	ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
	if (ret < 0)
		goto out_common_hdmi;
#endif

#ifdef CONFIG_DRM_EXYNOS_VIDI
	ret = platform_driver_register(&vidi_driver);
	if (ret < 0)
		goto out_vidi;
#endif

#ifdef CONFIG_DRM_EXYNOS_G2D
	ret = platform_driver_register(&g2d_driver);
	if (ret < 0)
		goto out_g2d;
#endif

	ret = platform_driver_register(&exynos_drm_platform_driver);
	if (ret < 0)
		goto out;

	return 0;

out:
#ifdef CONFIG_DRM_EXYNOS_G2D
	platform_driver_unregister(&g2d_driver);
out_g2d:
#endif

#ifdef CONFIG_DRM_EXYNOS_VIDI
out_vidi:
	platform_driver_unregister(&vidi_driver);
#endif

#ifdef CONFIG_DRM_EXYNOS_HDMI
	platform_driver_unregister(&exynos_drm_common_hdmi_driver);
out_common_hdmi:
	platform_driver_unregister(&mixer_driver);
out_mixer:
	platform_driver_unregister(&hdmi_driver);
out_hdmi:
#endif

#ifdef CONFIG_DRM_EXYNOS_FIMD
	platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
	return ret;
}

源文件:drivers/gpu/drm/exynos/exynos_drm_drv.c

4 设备与驱动匹配

    平台设备和驱动必须同名才能匹配,然而上述平台设备的name为s5p-fb,而平台驱动的name为exynos4-fb,根本就不一致!这是不是搞错了呢?答案是否定的,这其实是一个用于支持多种显示设备的技巧:在特定的函数中对上述设备名进行更改,使其和驱动名匹配。下面分析该过程。

4.1 更改设备名字

(1)exynos4_map_io()

    该函数调用s5p_fb_setname()函数将上述s5p_device_fimd0的名字由原来的s5p-fb改为exynos4-fb,见38行。

static void __init exynos4_map_io(void)
{
    iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));

    if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_0)
        iotable_init(exynos4_iodesc0, ARRAY_SIZE(exynos4_iodesc0));
    else
        iotable_init(exynos4_iodesc1, ARRAY_SIZE(exynos4_iodesc1));

    if (soc_is_exynos4412())
        iotable_init(exynos4412_iodesc, ARRAY_SIZE(exynos4412_iodesc));
    else
        iotable_init(exynos4xxx_iodesc, ARRAY_SIZE(exynos4xxx_iodesc));

    /* initialize device information early */
    exynos4_default_sdhci0();
    exynos4_default_sdhci1();
    exynos4_default_sdhci2();
    exynos4_default_sdhci3();

    s3c_adc_setname("samsung-adc-v3");

    s3c_fimc_setname(0, "exynos4-fimc");
    s3c_fimc_setname(1, "exynos4-fimc");
    s3c_fimc_setname(2, "exynos4-fimc");
    s3c_fimc_setname(3, "exynos4-fimc");

    s3c_sdhci_setname(0, "exynos4-sdhci");
    s3c_sdhci_setname(1, "exynos4-sdhci");
    s3c_sdhci_setname(2, "exynos4-sdhci");
    s3c_sdhci_setname(3, "exynos4-sdhci");

    /* The I2C bus controllers are directly compatible with s3c2440 */
    s3c_i2c0_setname("s3c2440-i2c");
    s3c_i2c1_setname("s3c2440-i2c");
    s3c_i2c2_setname("s3c2440-i2c");

    s5p_fb_setname(0, "exynos4-fb");
    s5p_hdmi_setname("exynos4-hdmi");
}

源文件:arch/arm/mach-exynos/common.c

(2)s5p_fb_setname()

    该函数主要就是用于给s5p_device_fimd0这个全局变量改名,详见下面第5行:

static inline void s5p_fb_setname(int id, char *name)
{
    switch (id) {
#ifdef CONFIG_S5P_DEV_FIMD0
    case 0:
        s5p_device_fimd0.name = name;                                                                                                                                                                        
    break;
#endif
    default:
        printk(KERN_ERR "%s: invalid device id(%d)\n", __func__, id);
    break;
    }
} 

源文件:arch/arm/plat-samsung/include/plat/fb-core.h 

4.2 匹配过程

    同《Linux设备驱动--WDT平台设备与驱动》第4节。

5 总结

    与《Linux设备驱动--LCD平台设备与驱动(smdk2440)》和《Linux设备驱动--LCD平台设备与驱动(smdk6410)》对比可知,主要流程基本一致,然而Tiny4412为了兼容更多的显示设备,采取了更加灵活的方法。

参考资料

[1]Tiny 4412 lcd 驱动分析

[2]FIMD架构分析

[3]tiny4412LCD驱动加字符显示

你可能感兴趣的:(Kernel,Display)