关闭

Exynos4412 Android4.4 HDMI Porting Guid 第二篇

2467人阅读 评论(0) 收藏 举报
分类:

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输出界面出现卡顿的现象。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:546585次
    • 积分:5701
    • 等级:
    • 排名:第4545名
    • 原创:54篇
    • 转载:67篇
    • 译文:3篇
    • 评论:123条
    文章分类
    最新评论