本文主要阐述S55pvpv210处理器的HDMI插口在Linux3.0.8内核下的驱动框架。

如今三星的主流处理器基本都支持HDMI,使用HDMI也有段时间了,却始终不晓得它是如何工作的,只晓得linux和android下就会有一个HDMI-service的用户服务程序。之后底层会有HDMI驱动。晓得HDMI和framebuffer有点关系,却不晓得二者是怎样联系在一起的。从晓得HDMI以来就认为它神秘,出于好奇,决定揭露它的面纱一探真容。根据我的思路从下边四个方面并根据源码简单分析一下SamsungS55pvpv210处理器HDMI在linux3.0.8下的驱动框架。

•1.1何为HDMI,HDMI总线合同发展概况

•1.2HDMI驱动框架

•1.3从源码linux v4l2驱动框架,看HDMI设备驱动框架

•1.4从“疑问???”猜答案。(透过HDMI-service分析HDMI与Framebuffer的关系。)

1.1何为HDMI,HDMI总线合同发展概况

高清晰度多媒体插口(英语:HighDefinitionMultimediaInterface,HDMI)是一种数字化视频/音频插口技术,是适宜影像传输的专用型数字化插口,其可同时传送音频和影音讯号,最高数据传输速率为5Gbps。下边给出的是HDMItypeA型的公母头,另外HDMI还有typeBtypeC两种类型的插座。

linux驱动开发框架_框架驱动下_linux v4l2驱动框架

HDMI除了可以满足1080P的帧率,能够支持DVDAudio等数字音频格式,支持八声道96kHz或立体声192kHz数码音频传送,可以传送无压缩的音频讯号及视频讯号。HDMI的设备具有“即插即用”的特性。从下边的图片我们看一下个版本HDMI的差异

框架驱动下_linux驱动开发框架_linux v4l2驱动框架

在s55pvpv210处理器上的HDMI模块是1.3版,Exyson4412上用的是1.4版。4412在hdmi上的优势从前面的图就一目了然了。从数据指南上可以得到s55pvpv210处理器HDMI的基本参数:

linux驱动开发框架_linux v4l2驱动框架_框架驱动下

1.2HDMI驱动框架

在介绍HDMI的驱动框架之前,首先须要了解一下HDCP,HPD,DDC这几个新名词指的是哪些,否则直接深入源码只会搞得晕头转向,之后才会认为HDMI这玩意儿深奥的不得了。虽然它不如何深。。。^_^

•HDCP:HDCP的全称是High-bandwidthDigitalContentProtection,也就是“高带宽数字内容保护”。

•DDC:HDCP数据密钥在CPU和显示设备间的交换以及EDID(EDID中包含有关显示器及其性能的参数)要通过hdmi插口的两个DDC(IIC总线)引脚实现.(实质是实现一个IIC设备驱动)

•HDP:HotPlugDetection,在HDMI的一挽联接中,为热拔插的实现而设计的。简单地说,当发送端接入接受端时,接受端会回应HPD讯号给发送端,从而发送端会启动DDC通道linux操作系统原理,而读取接受端EDID的信息,之后进行HDCP的交互,若果双方认证成功,则视频、音频正常工作,否则连接失败,不同系统会有不同的处理。

HDCP,HPD,DDC这几个新名词清楚了,那就得想想它们作为HDMI的一个组件(这个词不清楚用的是否恰当)之间有哪些样的联系呢?虽然前面说的早已差不多了,我再简单总结下:

linux v4l2驱动框架_框架驱动下_linux驱动开发框架

HDCP起到一个数字内容保护的作用,它的密钥交换须要用到IIC总线,也就是HDMI的DDC通道。另外HDMI还须要从显示设备获得显示相关参数(EDID),例如码率等显示信息。这个信息也是通过IIC总线交互的,走的也是DDC通道。HPD就不用多说了,HDMI热拔插后的“工作”全靠它了。Linux驱动须要分别实现这几个组件的驱动,这几个组件的驱动互相配合共支撑实现了HDMI驱动。下边就先把HDMI框架画下来。

框架驱动下_linux v4l2驱动框架_linux驱动开发框架

这儿还须要介绍一下CEC:可以简单理解当您有好多HDMI设备通过HDMI线,切换器或则分配器连在一起的时侯,假如所有的HDMI产品都支持CEC功能,这么可以借助其中一台的遥控器可以去控制其他的设备.这就是CEC功能。210也提供了HDMIcec的驱动源码,而且生成了供给用户空间操作的设备文件(它是作为混杂设备被注册进内核),从里面的框图也可以见到,CEC驱动的实现不同于HDCP、HPD、DDC等组件的驱动向内核空间提供了函数插口。CEC驱动中仅实现了file_operation操作方式,供给用户对设备文件操作时调用。并没有对内核空间曝露函数插口。

再简单说明下框架:在linux3.0.8中HDMI作为TV_OUT的一部份,TV_OUT驱动注册时,HDMI作为TV_OUT的一个子系统被初始化。【当TV_OUT的探针函数(staticint__devinits5p_tv_probe(structplatform_device*pdev))被执行时,会调用HDMI相关初始化函数s5p_hdmi_probe(pdev,3,4);】。而且同时会注册一个符合V4L2标准的设备,因而用户空间对HDMI设备(video14)的基本操作如设置区分等操作符合V4L2标准操作(这个在旁边都会按照源码深入剖析)。而CECHDCPDDCHPD作为HDMI的组件,她们的驱动实现即为HDMI驱动的实现提供函数插口(HDCPDDCHPD这三个组件的驱动会相互曝露函数调用),也为用户空间提供了操作这个组件的方式(HPDCEC这两个组件的驱动向用户空间提供了操作方式)。

通过前面的框图可以看见,我们在用户空间可以看见的跟HDMI相关的设备文件只有三个,分别是CECHPDvideo14这三个设备文件,依照驱动中实现的方式应用程序可以通过这三个设备文件分别是实现HDMICEC功能操作、读取热拔插HDMI设备状态、对HDMI设备操作。这三个设备文件是怎样生成的,她们提供了什么操作方式呢?看下边的剖析———-

1.3透过源码看HDMI设备驱动框架的实现

HDMI的这几个组件都有自己的驱动源文件,下边就分开瞧瞧各组件驱动的实现及其在HDMI主体驱动中的作用

HDMIDDC驱动:

先来瞧瞧HDMIDDC驱动,DDC驱动实际上实现了一个标准IIC设备的驱动。

DDC设备注册:

DDC设备结构,在板级初始化时被注册进内核mach-XXX210.c(我用的是tiny210mach-mini210.c)

这是DDC设备结构体,其中包含了DDC设备IIC总线上的地址信息。在mini210_machine_init(void)函数中会调用:

i2c_register_board_info(1,mini210_i2c_devs1,ARRAY_SIZE(mini210_i2c_devs1));函数将这个结构注册进内核。如右图所示:

框架驱动下_linux驱动开发框架_linux v4l2驱动框架

再看一下DDC驱动注册:

这是DDC驱动的结构体。在内核启动后start_kernel-->rest_init()-->kernel_init()-->do_basic_setup()-->do_initcalls()执行到do_initcalls函数时所有带有__init标识的函数被次序执行这儿ddc_init函数就被执行了,将里面的驱动结构注册进内核如右图:

若IIC总线上设备、驱动匹配,则ddc_probe函数被执行。ddc_probe函数内容如下:

框架驱动下_linux驱动开发框架_linux v4l2驱动框架

linux v4l2驱动框架_框架驱动下_linux驱动开发框架

ddc_probe函数被执行后会获得IIC设备资源,主要是IIC设备地址信息。

DDC驱动中实现了两个方式,这两个方式不曝露给用户,姑且称为“内核API”(这些说法不太确切,为了便捷前面姑且如此叫).驱动将这两个方式,提供给HDMI的HDCP驱动调用(HDCP密钥的交换须要用到DDC组件)。这两个方式的实现如下:

DDC驱动内容大致就那么多

HDMIHDCP驱动:

下边了解一下HDCP驱动:

HDCP驱动完全没有给用户空间提供操作插口,对用户空间来说觉得不到它的存在,而且驱动提供了“内核API”供HDMI驱动及其它组件调用。对外曝露的函数列表如下:

staticints5p_hdcp_is_reset(void)//函数功能:判定HDCP的载流子锁是否早已被获取倘若被获取返回1,否则返回0.函数未被调用。

staticbools5p_set_hpd_detection(booldetection,boolhdcp_enabled,structi2c_client*client)//函数功能:检查HPD引脚状态,函数未被调用。

ints5p_hdcp_init(void)//函数功能:初始化HDCP

bools5p_start_hdcp(void)//函数功能:开启HDCP功能,并从显示设备获得密钥。

bools5p_stop_hdcp(void)//函数功能:关掉HDCP功能

ints5p_hdcp_encrypt_stop(boolon)//函数功能:当HDMI插口拔下时,会执行这个函数,停止HDCP加密

ints5p_hdmi_set_dvi(boolen)//函数功能:设置开启/关掉DVI标志

voids5p_hdmi_set_audio(boolen)//函数功能:设置开启/关掉PCM流无损音频输出标志

ints5p_hdmi_audio_enable(boolen)//函数功能:开启音频输出

ints5p_hdmi_set_mute(boolen)//函数功能:设置声音图象消隐开启/关掉标志

ints5p_hdmi_get_mute(void)//函数功能:获得声音图象消隐开启/关掉标志状态

voids5p_hdmi_mute_en(boolen)//函数功能:开启声音图象消隐

顺便提一下哪些是声音图象消隐:简单来说就是显示设备的声音至于静音状态、图像至于死机状态。例如HDMI输出设备在切换码率、ColorSpace、开关机等操作时,显示设备可能会见到一些过度死机的现象,所以,HDMI输出设备在进行相应操作之前,发送给显示设备一个AVMute讯号linux v4l2驱动框架,让显示设备至于死机静音状态,等待HDMI输出设备切换好后linux驱动下载,再发送一个ClearAVMute讯号,让显示设备再开机,这样,切换过程中的死机现象都会被屏蔽掉。

HDCP驱动大致就提供了这种插口函数,供HDMI驱动的其它组件调用。

HDMIHPD驱动:

linux v4l2驱动框架_框架驱动下_linux驱动开发框架

之后再介绍一下HPD驱动:

HPD驱动使用了Platform&misc框架。设备结构注册到内核,驱动结构体注册到内核,设备驱动匹配后,HPD的探针函数中(probe)获得设备资源,并注册一个混杂设备,这时会在根文件系统生成设备文件HPD,用户可以通过驱动所支持的方式,操作设备文件,因而获得HDMIHPD引脚线的状态。这段文字用源码的方式诠释如下:

Mach-mini210.c文件中,HPD设备结构的定义如下

作为板级平台设备,里面的结构包含在mini210_devices链表中

系统平台设备初始化(mini210_machine_init(void)—-àplatform_add_devices)时,将设备结构s5p_device_hpd注册进内核。

下边的代码在函数s5p_hpd_init上将驱动结构s5p_hpd_driver注册进内核

当platform的设备和驱动匹配后执行probe探针函数

探针函数s5p_hpd_probe中注册将HPD注册为一个混杂设备,设备结构体如下

可以看见这个驱动中的file_operations结构实现了s5p_hpd_open、s5p_hpd_release、s5p_hpd_read、s5p_hpd_poll这几个方式。用户可以通过read系统调用实现读取HPD引脚的状态。

另外HPD驱动中还有三个函数(方式):

ints5p_hpd_get_state(void)

ints5p_hpd_set_hdmiint(void)

ints5p_hpd_set_eint(void)

这三个操作方式,作为KernelAPI供给HDMI驱动核心调用,分别用于获取当前HDMI插口状态、上电初始化HDMIHPD、及上电初始化HPD引脚中断。

HDMI主设备驱动

linux驱动开发框架_框架驱动下_linux v4l2驱动框架

下边就要步入HDMI驱动最精彩的部份了,HDMI设备的注册:HDMI作为TV_OUT的一部份,设备资源包含在TV_OUT设备资源结构中如右图链表中下标为3、4的元素:

下边是TV_OUT的设备结构体:

它作为板级平台设备,被下边的链表包含

在mini210_machine_init函数中的platform函数被调用时注册进内核

下边是TV_OUT的驱动结构体

linux v4l2驱动框架_linux驱动开发框架_框架驱动下

在s5p_tv_init函数中s5p_tv_driver被注册进内核

当platform的设备和驱动匹配时执行探针函数(probe)s5p_tv_probe

框架驱动下_linux v4l2驱动框架_linux驱动开发框架

这个probe函数中与HDMI相关的s5p_hdmi_probe函数被调用,完成HDMI设备的初始化。

紧接着,在probe函数中,下边红框内调用video_device_register函数,将HDMI设备注册,

函数会获得s5p_tvout这个结构,

并会被注册为遵守V4L2框架的video设备,对HDMI的操作函数嵌入到v4l2框架。,之后会在根文件生成名为video14的设备文件(.minor=TVOUT_MINOR_TVOUT,这个元素中TVOUT_MINOR_TVOUT宏展开为14)。

而对video14(HDMI)设备的操作函数最终通过V4L2框架封装曝露给用户空间,实现的操作方式如下:

符合V4L2框架的ioctl操作

到此为止我们早已可以通过/dev/video14设备文件,根据V4L2规范对HDMI进行操作了。

框架驱动下_linux v4l2驱动框架_linux驱动开发框架

HDMICEC驱动暂略前面会补上

1.4疑惑???

到现今为止,我们应当还有一个疑惑,用户空间是怎样使用HDMI设备的,由此可以引出四个问题:

•在用户空间怎么配置HDMI?

•HDMI显示数据从哪来?

•HDMI显示和Framebuffer有何关联?

从android的HDMI-service我们可以看见,在Service中打开HDMI设备:(/device/samsung/proprietary/libhdmi/SecHdmi.cpp)

linux v4l2驱动框架_linux驱动开发框架_框架驱动下

但是会在Overlay的初始化过程将HDMI显示与Framebuffer关联上去。在overlay.cpp中会在系统启动时对HDMI进行初始化,将帧缓冲显存映射到HDMI显示缓冲区中。源码中的buffer表针指向帧缓冲区

经过上述方式,实现了帧缓冲区向HDMI插口的映射。我们在应用程序中,只须要向帧缓冲区中写入图象数据都会通过HDMI输出到显示设备上(HDMI-service正常工作做的前提下,HDMI-service在系统启动后被启动,拿来初始化HDMI基本参数,如码率等)。

映射完成后framebuffer与HDMI的关系如右图:

至此HDMI驱动框架介绍完毕。因为网上基本搜不到210HDMI的linux驱动相关信息,以上内容都是通过读源码剖析总结的,可能会有一些认识错误,还请你们在阅读后及时见谅,感谢。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复