您的位置:首页 > 产品设计 > UI/UE

Exynos4412 Android4.4 HDMI Porting Guid 第二篇

2015-07-10 18:27 645 查看

1.2 硬件相关部分

下图为iTop4412开发板底板HDMI接口引脚定义:



下图为iTop4412开发板核心板HDMI相关引脚原理图:



原理图结合HDMI接口定义标准我们可以获知:

1 TMDS_D0- TMDS_D0+, TMDS_D1- TMDS_D1+, TMDS_D2- TMDS_D2+

三对数据线用于传输视频和音频及控制信号;

2 TMDS_CLK+ TMDS_CLK- 为HDMI传输提供时钟源;

3 SCL,SDA为I2C控制信号,用于EDID协议传输,主要用于板卡与HDMI显示设备之间进行协商,比如查询HDMI显示设备支持的最大分辨率,板卡设置HDMI输出分辨率等等均通过I2C总线传输,iTop4412开发平台采用I2C0传输EDID。

I2C作为HDMI的DDC通道,用于设备之间的沟通。

4 HDMI_HPD

引脚用于产生热插拔中断信号,CPU端通过此信号可以知道有HDMI设备插入或者拔出,驱动程序会处理中断,告知到Android层。

5 CEC

引脚用于HDMI的高级客户定制功能,用于传输厂商自定义的命令,属于HDMI的拓展功能,比如HDMI发送端设备可以通过CEC引脚告知HDMI显示设备随同发送设备开机,关机等操作。

1.3 Kernel

1.3.1 概述

iTO-P4412开发板采用的内核是Linux 3.0.15

版本,我们这边没有三星官方的关于HDMI的PortingGuid,

只能是自己根据Exynos4412的Datasheet结合三星提供的内核代码进行分析。

我们先看一下Exynos4412 Datasheet中关于HDMI功能的属性支持:

The features of HDMI are:

 Complies with HDMI 1.4 (3D feature), HDCP 1.1, and DVI 1.0

 The video formats that HDMI supports are:

 480p 59.94 Hz/60 Hz, 576p @ 50 Hz

 720p @ 50 Hz/59.94 Hz/60 Hz

 1080i @ 50 Hz/59.94 Hz/60 Hz

 1080p @ 50 Hz/59.94 Hz/60 Hz

 Supports other various formats up to 148.5 MHz Pixel Clock

 Supports Color Format: 4:4:4 RGB/YCbCr

 Supports 8-bit precision per color only

 Supports CEC function

* Contains an Integrated HDCP Encryption Engine for video/ audio

content protection*

* Does not include DDC. There is a dedicated Inter-Integrated Circuit

(I2C) for DDC in Exynos 4412 SCP*

可以知道HDMI控制器支持HDMI1.4,HDCP1.1,DVI1.0规范,另外支持最高1080P

60HZ的显示,当然也支持最常见的 480P ,720P,1080i输出,另外HDMI

输出的颜色格式可以是RGB的也可以是YUV的,这个可以通过软件界面进行输出设置。

DDC全文为Display Data

Channel,用于HDMI设备之间的协议沟通,Exynos4412内部没有专用的DDC控制器,而是采用I2C总线完成这部分工作,驱动部分使用I2C传输控制命令到显示终端设备。

系统框图:



HDMI的视频数据是通过 MIXER输入到 HDMI

CORE核心,然后通过PHY发送出去,MIXER是视频混合器,用于图层的混合。音频数据源有两路,一路是SPDIF总线输入,另外一路是I2S音频总线输入,我们的开发板采用的是I2S的音频源,故音频输入是通过I2S传输到HDMI

CORE的。

另外需要注意一下HDMI PHY,Exynos4412集成了HDMI PHY,PHY用于产生

pixel时钟和TMDS时钟,我们不再需要额外的PHY芯片,这样可以省去PCB布线,当然还有cost,软件通过CPU内部的专用的I2C总线配置Phy寄存器,针对Phy进行控制,比如开启,关闭PHY电源等等。

HDMI功能在Exynos4412平台中属于TVOUT子系统的一部分,图形图像数据可以输出到TV显示设备,也可以输出到HDMI显示设备



如图,VideoProcessor硬件模块从内存获取到YUV420格式的图像数据进行裁剪,及空间色彩转换,然后把数据传输到MIXER硬件模块,Dataheet是这样解释VideoProcessor的功能定义:



*Video Processor (VP) is responsible for video scaling, de-interlacing,

and video post processing of TV-out data path. VP reads reconstructed

YCbCr 4:2:0 video sequences from DRAM. It then processes the sequence,

and sends it to on-the- fly Mixer。*

MIXER

模块主要是对VideoProcessor输入的图形,视频,背景进行混合叠加,形成完成的窗口显示,然后把数据传输到TVENC进行编码,数模转换,输出到TV设备,或者把数据传输到HDMI模块,由HDMI的PHY把数据传输到HDMI接收显示设备。

*Mixer overlaps or blends the input data such as graphic, video,

background and sends the resulting data to the TVOUT module. The TVOUT

module generates all the video control signals*

如果您对MIXER的功能作用不是很清楚,可以看一下Datasheet中对MIXER混合器描述的图例:



通过上面的解释,我们知道内存数据是如何显示到HDMI设备上的,这样对HDMI就有了一个框架性的认识和理解,方便我们分析

Linux HDMI驱动结构,及驱动模块在HDMI使用中扮演怎么样的角色。

1.3.2 内核代码

这里我们把HDMI驱动相关划分为两部分,一部分是驱动文件,驱动文件实现了HDMI的驱动架构。另外一部分是板级支持文件,与iTop4412开发板相关的文件。

1.3.2.1 驱动文件

首先我们看一下HDMI驱动相关文件夹,因为HDMI属于TVOUT子系统的一部分,那么HDMI驱动是离不开TV输出系统的:

路径:iTop4412_Kernel_3.0/drivers/media/video/Samsung/tvout



TVOUT文件夹为TVOUT子系统的驱动,HDMI驱动就位于其中:



这里我们看到了s5p_tvout_hpd.c

关于HDMI热插拔事件相关驱动,该驱动文件会产生/dev/HPD设备节点,用户态软件会打开设备节点,用于监控HDMI设备的插入和拔出。

s5p_mixer_ctrl.c 视频混合器,s5p_vp_ctrl.c

图形裁剪驱动,这些文件是TV驱动调用的接口文件,实际的实现位于hw_if文件夹下面,hw_if文件夹下面的这些文件会操作最底层的寄存器配置:



比如 hdmi.c文件包含对HDMI

控制寄存器,状态寄存器,及其他功能寄存器的配置,当然也包含了对HDMI PHY配置:

void s5p_hdmi_reg_enable(bool en)

{

u8 reg;

reg = readb(hdmi_base + S5P_HDMI_CON_0);

if (en)

reg |= S5P_HDMI_EN;

else

reg &= \~(S5P_HDMI_EN | S5P_HDMI_ASP_EN);

writeb(reg, hdmi_base + S5P_HDMI_CON_0);

if (!en) {

do {

reg = readb(hdmi_base + S5P_HDMI_CON_0);

} while (reg & S5P_HDMI_EN);

}

}

*s32 s5p_hdmi_phy_config( enum phy_freq freq, enum

s5p_hdmi_color_depth cd)*

{

s32 index;

s32 size;

u8 buffer[32] = {0, };

u8 reg;

int loop =0;

switch (cd) {

case HDMI_CD_24:

index = 0;

break;

case HDMI_CD_30:

index = 1;

break;

case HDMI_CD_36:

index = 2;

break;

default:

return -1;

}

buffer[0] = PHY_REG_MODE_SET_DONE;

buffer[1] = 0x00;

if (s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS, 2, buffer) != 0) {

tvout_err(“s5p_hdmi_i2c_phy_write failed.\n”);

return -1;

}

writeb(0x5, i2c_hdmi_phy_base + HDMI_I2C_LC);

size = sizeof(phy_config[freq][index])

/ sizeof(phy_config[freq][index][0]);

memcpy(buffer, phy_config[freq][index], sizeof(buffer));

if (s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS, size, buffer) != 0)

return -1;

#ifdef CONFIG_HDMI_PHY_32N

buffer[0] = PHY_REG_MODE_SET_DONE;

buffer[1] = 0x80;

if (s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS, 2, buffer) != 0) {

tvout_err(“s5p_hdmi_i2c_phy_write failed.\n”);

return -1;

}

#else

buffer[0] = 0x01;

if (s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS, 1, buffer) != 0) {

tvout_err(“s5p_hdmi_i2c_phy_write failed.\n”);

return -1;

}

#endif

s5p_hdmi_print_phy_config();

#ifndef CONFIG_HDMI_PHY_32N

s5p_hdmi_reg_core_reset();

#endif

#ifdef CONFIG_HDMI_PHY_32N

do {

reg = readb(hdmi_base + S5P_HDMI_PHY_STATUS0);

} while (!(reg & S5P_HDMI_PHY_STATUS_READY));

#else

do {

reg = readb(hdmi_base + S5P_HDMI_PHY_STATUS);

mdelay(5);

loop++;

if(loop==100) return -1; //added yqf, for robust

} while (!(reg & S5P_HDMI_PHY_STATUS_READY));

#endif

writeb(I2C_CLK_PEND_INT, i2c_hdmi_phy_base + HDMI_I2C_CON);

writeb(I2C_IDLE, i2c_hdmi_phy_base + HDMI_I2C_STAT);

return 0;

}

Mixer.c文件包含了对视频混合器的底层配置:

void s5p_mixer_start(void)

{

*writel((readl(mixer_base + S5P_MXR_STATUS) |

S5P_MXR_STATUS_RUN),*

mixer_base + S5P_MXR_STATUS);

}

void s5p_mixer_stop(void)

{

u32 reg = readl(mixer_base + S5P_MXR_STATUS);

reg &= \~S5P_MXR_STATUS_RUN;

writel(reg, mixer_base + S5P_MXR_STATUS);

do {

reg = readl(mixer_base + S5P_MXR_STATUS);

} while (!(reg & S5P_MXR_STATUS_IDLE_MODE));

}

这些驱动文件中当然有一个主文件,作为驱动的入口文件,他就是

s5p_tvout.c文件,

该文件提供了驱动注册函数,另外构建了VideoProcessor

对象,MIXER视频混淆器对象,还有非常重要的V4L2用户态调用接口,用户态程序是通过V4L2接口控制HDMI的输出的。

*static int __devinit s5p_tvout_probe(struct platform_device

*pdev)*

{

s5p_tvout_pm_runtime_enable(&pdev->dev);

#if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_VCM)

if (s5p_tvout_vcm_create_unified() < 0)

goto err;

if (s5p_tvout_vcm_init() < 0)

goto err;

#elif defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_S5P_VMEM)

s5p_sysmmu_enable(&pdev->dev);

printk(“sysmmu on\n”);

*s5p_sysmmu_set_tablebase_pgd(&pdev->dev,

__pa(swapper_pg_dir));*

#endif

#ifndef CONFIG_TC4_EVT //yqf

tv_regulator_vdd18 = regulator_get(NULL, “vdd18_mipi”);

if (IS_ERR(tv_regulator_vdd18)) {

printk(“%s: failed to get %s\n”, __func__, “vdd18_mipi”);

goto err_regulator;

}

//regulator_enable(tv_regulator_vdd18);

tv_regulator_vdd10 = regulator_get(NULL, “vdd10_mipi”);

if (IS_ERR(tv_regulator_vdd10)) {

printk(“%s: failed to get %s\n”, __func__, “vdd10_mipi”);

goto err_regulator;

}

//regulator_enable(tv_regulator_vdd10);

#endif

if (s5p_tvout_clk_get(pdev, &s5ptv_status) < 0)

goto err;

******if (s5p_vp_ctrl_constructor(pdev) < 0)

goto err;

/* s5p_mixer_ctrl_constructor must be called

before s5p_tvif_ctrl_constructor */

if (s5p_mixer_ctrl_constructor(pdev) < 0)

goto err;

if (s5p_tvif_ctrl_constructor(pdev) < 0)

goto err;

if (s5p_tvout_v4l2_constructor(pdev) < 0)

goto err;

#ifdef CONFIG_HAS_EARLYSUSPEND

spin_lock_init(&s5ptv_status.tvout_lock);

s5ptv_early_suspend.suspend = s5p_tvout_early_suspend;

s5ptv_early_suspend.resume = s5p_tvout_late_resume;

*s5ptv_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;

//added yqf, suspend before LCD*

register_early_suspend(&s5ptv_early_suspend);

suspend_status = 0;

#endif

#ifdef CONFIG_TV_FB

#ifndef CONFIG_USER_ALLOC_TVOUT

clk_enable(s5ptv_status.i2c_phy_clk); //added yqf

s5p_hdmi_phy_power(true);

*if (s5p_tvif_ctrl_start(TVOUT_720P_60, TVOUT_HDMI) < 0) //yqf,

back later*

goto err;

#endif

/* prepare memory */

if (s5p_tvout_fb_alloc_framebuffer(&pdev->dev))

goto err;

if (s5p_tvout_fb_register_framebuffer(&pdev->dev))

goto err;

#endif

on_stop_process = false;

on_start_process = false;

return 0;

**

#ifndef CONFIG_TC4_EVT

err_regulator:

regulator_put(tv_regulator_vdd18);

regulator_put(tv_regulator_vdd10);

#endif

err:

return -ENODEV;

}

static const struct dev_pm_ops s5p_tvout_pm_ops = {

.suspend = s5p_tvout_suspend,

.resume = s5p_tvout_resume,

.runtime_suspend = s5p_tvout_runtime_suspend,

.runtime_resume = s5p_tvout_runtime_resume

};

static struct platform_driver s5p_tvout_driver = {

.probe = s5p_tvout_probe,

.remove = s5p_tvout_remove,

.driver = {

.name = “s5p-tvout”,

.owner = THIS_MODULE,

.pm = &s5p_tvout_pm_ops

},

};

static char banner[] __initdata =

KERN_INFO “S5P TVOUT Driver v3.0 (c) 2010 Samsung Electronics\n”;

static int __init s5p_tvout_init(void)

{

int ret;

printk(banner);

ret = platform_driver_register(&s5p_tvout_driver);

if (ret) {

printk(KERN_ERR “Platform Device Register Failed %d\n”, ret);

return -1;

}

#ifdef CONFIG_PM

tvout_resume_wq = create_freezable_workqueue(“tvout resume work”);

if (!tvout_resume_wq) {

printk(KERN_ERR “Platform Device Register Failed %d\n”, ret);

platform_driver_unregister(&s5p_tvout_driver);

return -1;

}

*INIT_WORK(&tvout_resume_work, (work_func_t)

s5p_tvout_resume_work);*

#endif

return 0;

}

static void __exit s5p_tvout_exit(void)

{

#ifdef CONFIG_HAS_EARLYSUSPEND

mutex_destroy(&s5p_tvout_mutex);

#endif

platform_driver_unregister(&s5p_tvout_driver);

}

late_initcall(s5p_tvout_init);

module_exit(s5p_tvout_exit);

1.3.2.2 板级相关文件

文件列表:

iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c

iTop4412_Kernel_3.0/arch/arm/plat-s5p/dev-tvout.c

iTop4412_Kernel_3.0/arch/arm/mach-exynos/setup-tvout.c



mach-itop4412.c文件为iTop4412开发板的入口文件,该文件定义了iTop4412开发板的所有板载资源,当然也包括HDMI相关的设备:

static struct platform_device *smdk4x12_devices[] __initdata = {

……

#ifdef CONFIG_VIDEO_TVOUT

&s5p_device_tvout,

&s5p_device_cec,

&s5p_device_hpd,

#endif

……..

}

static void __init smdk4x12_machine_init(void)

{

……

#if defined(CONFIG_VIDEO_TVOUT)

s5p_hdmi_hpd_set_platdata(&hdmi_hpd_data);

s5p_hdmi_cec_set_platdata(&hdmi_cec_data);

#ifdef CONFIG_EXYNOS_DEV_PD

s5p_device_tvout.dev.parent = &exynos4_device_pd[PD_TV].dev;

*exynos4_device_pd[PD_TV].dev.parent=

&exynos4_device_pd[PD_LCD0].dev;*

#endif

…….

}

■ dev-tvout.c

文件定义了HDMI系统相关的设备,如VideoProcess,Mixer视频混淆器占用的系统资源,如寄存器地址,中断:

/* TVOUT interface */

static struct resource s5p_tvout_resources[] = {

[0] = {

.start = S5P_PA_TVENC,

.end = S5P_PA_TVENC + S5P_SZ_TVENC - 1,

.flags = IORESOURCE_MEM,

.name = “s5p-sdo”

},

[1] = {

.start = S5P_PA_VP,

.end = S5P_PA_VP + S5P_SZ_VP - 1,

.flags = IORESOURCE_MEM,

.name = “s5p-vp”

},

[2] = {

.start = S5P_PA_MIXER,

.end = S5P_PA_MIXER + S5P_SZ_MIXER - 1,

.flags = IORESOURCE_MEM,

.name = “s5p-mixer”

},

[3] = {

.start = S5P_PA_HDMI,

.end = S5P_PA_HDMI + S5P_SZ_HDMI - 1,

.flags = IORESOURCE_MEM,

.name = “s5p-hdmi”

},

[4] = {

.start = S5P_I2C_HDMI_PHY,

.end = S5P_I2C_HDMI_PHY + S5P_I2C_HDMI_SZ_PHY - 1,

.flags = IORESOURCE_MEM,

.name = “s5p-i2c-hdmi-phy”

},

[5] = {

.start = IRQ_MIXER,

.end = IRQ_MIXER,

.flags = IORESOURCE_IRQ,

.name = “s5p-mixer”

},

[6] = {

.start = IRQ_HDMI,

.end = IRQ_HDMI,

.flags = IORESOURCE_IRQ,

.name = “s5p-hdmi”

},

[7] = {

.start = IRQ_TVENC,

.end = IRQ_TVENC,

.flags = IORESOURCE_IRQ,

.name = “s5p-sdo”

},

};

struct platform_device s5p_device_tvout = {

.name = “s5p-tvout”,

.id = -1,

.num_resources = ARRAY_SIZE(s5p_tvout_resources),

.resource = s5p_tvout_resources,

};

EXPORT_SYMBOL(s5p_device_tvout);

当然也包括设备节点 /dev/HPD所使用的资源:

/* HPD */

static struct resource s5p_hpd_resources[] = {

[0] = {

.start = IRQ_TVOUT_HPD,

.end = IRQ_TVOUT_HPD,

.flags = IORESOURCE_IRQ,

},

};

struct platform_device s5p_device_hpd = {

.name = “s5p-tvout-hpd”,

.id = -1,

.num_resources = ARRAY_SIZE(s5p_hpd_resources),

.resource = s5p_hpd_resources,

};

EXPORT_SYMBOL(s5p_device_hpd);

■ setup-tvout.c 文件主要提供了GPIO引脚功能配置:

void s5p_int_src_hdmi_hpd(struct platform_device *pdev)

{

s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3));

s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_DOWN);

}

void s5p_int_src_ext_hpd(struct platform_device *pdev)

{

s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0xf));

s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_DOWN);

}

int s5p_hpd_read_gpio(struct platform_device *pdev)

{

return gpio_get_value(EXYNOS4_GPX3(7));

}

void s5p_cec_cfg_gpio(struct platform_device *pdev)

{

s3c_gpio_cfgpin(EXYNOS4_GPX3(6), S3C_GPIO_SFN(0x3));

s3c_gpio_setpull(EXYNOS4_GPX3(6), S3C_GPIO_PULL_NONE);

}

setup-tvout.c 文件提供的功能函数会被其他文件的函数体调用。

1.3.2.3 内核配置

我们以POP核心的内核配置为例来说明:

cp config_for_android_pop .config

make menuconfig->Device Drivers->Multimedia support->video capture

adapters



Samsung TVOUT Driver 使能该驱动选项,然后同样打开 HDMI CEC ,HDMI

HPD,HDMI 14A Driver, HDMI PHY, TVOUT frame buffer driver support,

Support pre allocate frame buffer memory.

如果需要获得更多的调试信息,需要使能 TVOUT driver debug message,

这样内核会输出HDMI的调试信息到调试串口,为我们学习HDMI驱动提供有力的帮助,HDMI功能调试完成后需要关闭该选项,因为频繁的打印调试信息会影响到系统的性能,严重的情况下会导致HDMI输出界面出现卡顿的现象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: