# HDMI - [概述](#1) - [开发步骤](#2) - [开发实例](#3) ## 概述 HDMI(High-Definition Multiface Interface)是Hitachi、Panasonic、Philips、SiliconImage、Sony、Thomson、Toshiba共同发布的一款音视频传输协议,主要用于DVD、机顶盒等音视频source到TV、显示器等sink设备的传输。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。 在HDF框架中,HDMI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。 **图 1** HDMI统一服务模式 ![image1](figures/独立服务模式结构图.png) ## 开发步骤 HDMI模块适配的三个环节是配置属性文件,实例化驱动入口以及实例化HDMI控制器对象。 1. **实例化驱动入口:** - 实例化HdfDriverEntry结构体成员。 - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. **配置属性文件:** - 在device_info.hcs文件中添加deviceNode描述。 - 【可选】添加hdmi_config.hcs器件属性文件。 3. **实例化HDMI控制器对象:** - 初始化HdmiCntlr成员。 - 实例化HdmiCntlr成员HdmiCntlrOps方法集合,其定义和成员函数说明见下文。 HdmiCntlrOps定义: ```c struct HdmiCntlrOps { void (*hardWareInit)(struct HdmiCntlr *cntlr); void (*hardWareStatusGet)(struct HdmiCntlr *cntlr, struct HdmiHardwareStatus *status); void (*controllerReset)(struct HdmiCntlr *cntlr); bool (*hotPlugStateGet)(struct HdmiCntlr *cntlr); bool (*hotPlugInterruptStateGet)(struct HdmiCntlr *cntlr); void (*lowPowerSet)(struct HdmiCntlr *cntlr, bool enable); void (*tmdsModeSet)(struct HdmiCntlr *cntlr, enum HdmiTmdsModeType mode); int32_t (*tmdsConfigSet)(struct HdmiCntlr *cntlr, struct HdmiTmdsConfig mode); void (*infoFrameEnable)(struct HdmiCntlr *cntlr, enum HdmiPacketType infoFrameType, bool enable); int32_t (*infoFrameSend)(struct HdmiCntlr *cntlr, enum HdmiPacketType infoFrameType, uint8_t *data, uint32_t len); int32_t (*infoFrameDataSet)(struct HdmiCntlr *cntlr, uint32_t type, uint8_t *data, uint32_t len); int32_t (*cecMsgSend)(struct HdmiCntlr *cntlr, struct HdmiCecMsg *msg); void (*audioPathEnable)(struct HdmiCntlr *cntlr, bool enable); void (*audioPathSet)(struct HdmiCntlr *cntlr, struct HdmiAudioConfigInfo *config); void (*phyOutputEnable)(struct HdmiCntlr *cntlr, bool enable); void (*phyOutputSet)(struct HdmiCntlr *cntlr, struct HdmiPhyCfg *cfg); void (*blackDataSet)(struct HdmiCntlr *cntlr, bool enable); void (*videoMuteEnable)(struct HdmiCntlr *cntlr, bool enable); void (*videoPathSet)(struct HdmiCntlr *cntlr, struct HdmiVideoAttr *attr); void (*audioMuteEnable)(struct HdmiCntlr *cntlr, bool enable); void (*avmuteSet)(struct HdmiCntlr *cntlr, bool enable); int32_t (*ddcTransfer)(struct HdmiCntlr *cntlr, struct HdmiDdcCfg *ddcCfg); bool (*scdcSourceScrambleGet)(struct HdmiCntlr *cntlr); int32_t (*scdcSourceScrambleSet)(struct HdmiCntlr *cntlr, bool enable); void (*frlSet)(struct HdmiCntlr *cntlr); int32_t (*frlEnable)(struct HdmiCntlr *cntlr, bool enable); int32_t (*audioNctsSet)(struct HdmiCntlr *cntlr, struct HdmiFrlAudioNctsConfig *cfg); void (*frlTrainingConfigSet)(struct HdmiCntlr *cntlr, struct HdmiFrlTrainConfig *cfg); void (*frlTrainingStart)(struct HdmiCntlr *cntlr); void (*frlGetTriningRslt)(struct HdmiCntlr *cntlr, struct HdmiFrlTrainRslt *rslt); void (*hdcpRegInit)(struct HdmiCntlr *cntlr); int32_t (*hdcpGenerateAksvAndAn)(struct HdmiCntlr *cntlr); int32_t (*hdcpOptReg)(struct HdmiCntlr *cntlr, enum HdmiHdcpRegOptType type, uint8_t *data, uint32_t len); void (*hdrTimerSet)(struct HdmiCntlr *cntlr, struct HdmiHdrTimerConfig *config); }; ``` 表1 HdmiCntlrOps结构体成员的回调函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- | | hardWareInit | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | 无 | HDMI硬件初始化 | | hardWareStatusGet | **cntlr**: 结构体指针,核心层HDMI控制器;
| **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 | | controllerReset | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | 无 | HDMI控制器复位 | | hotPlugStateGet | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | bool: HDMI热插拔状态 | 获取HDMI热插拔状态 | | hotPlugInterruptStateGet | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | bool: HDMI热插拔中断状态 | 获取HDMI热插拔中断状态 | | lowPowerSet | **cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 | | tmdsModeSet | **cntlr**: 结构体指针,核心层HDMI控制器;
**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 | |tmdsConfigSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**mode**: TMDS参数|无|HDF_STATUS相关状态|配置TMDS参数| |infoFrameEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**infoFrameType**: packet类型
**enable**: bool,使能/去使能|无|无|使能/去使能infoFrame| |infoFrameSend|**cntlr**: 结构体指针,核心层HDMI控制器;
**infoFrameType**: packet类型
**data**: infoFrame数据
**len**:数据长度|无|HDF_STATUS相关状态|发送inforFrame| |cecMsgSend|**cntlr**: 结构体指针,核心层HDMI控制器;
**msg**: CEC消息|无|HDF_STATUS相关状态|发送CEC消息| |audioPathEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|使能/去使能audio通路| |audioPathSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**config**: 配置信息|无|无|设置audio通路配置信息| |phyOutputEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|使能/去使能物理层输出状态| |phyOutputSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**cfg**: 配置信息|无|无|设置物理层配置信息| |blackDataSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|黑屏设置| |videoMuteEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|使能/去使能video静音| |videoPathSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**attr**: 配置信息|无|无|设置viedo通路配置信息| |audioMuteEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|使能/去使能audio静音| |avmuteSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|无|使能/去使能声音图像消隐| |ddcTransfer|**cntlr**: 结构体指针,核心层HDMI控制器;
**ddcCfg**:DDC配置参数|**ddcCfg**:DDC配置参数|HDF_STATUS相关状态|DDC读写数据| |scdcSourceScrambleGet|**cntlr**: 结构体指针,核心层HDMI控制器;|无|bool,加扰状态|获取source端的加扰状态| |scdcSourceScrambleSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|HDF_STATUS相关状态|使能/去使能source端的加扰| |frlEnable|**cntlr**: 结构体指针,核心层HDMI控制器;
**enable**: bool,使能/去使能|无|HDF_STATUS相关状态|使能/去使能FRL| |audioNctsSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**cfg**:N/CTS配 置参数|无|HDF_STATUS相关状态|设置audio的N/CTS信息| |frlTrainingConfigSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**cfg**:FRL Traning配置参数|无|无|设置FRL Traning配置信息| |frlTrainingStart|**cntlr**: 结构体指针,核心层HDMI控制器;|无|无|开始FRL Traning流程| |frlGetTriningRslt|**cntlr**: 结构体指针,核心层HDMI控制器;|**rslt**:FRL Traning结果|无|获取FRL Traning结果| |hdcpRegInit|**cntlr**: 结构体指针,核心层HDMI控制器;|无|无|初始化HDCP流程相关的寄存器| |hdcpGenerateAksvAndAn|**cntlr**: 结构体指针,核心层HDMI控制器;|无|HDF_STATUS相关状态|HDCP流程中生成aksv和an| |hdcpOptReg|**cntlr**: 结构体指针,核心层HDMI控制器;
**type**: 操作类型
**data**: 寄存器数据
**len**: 数据长度|**data**: 寄存器数据|HDF_STATUS相关状态|HDCP流程中的相关寄存器读写操作| |hdrTimerSet|**cntlr**: 结构体指针,核心层HDMI控制器;
**config**: timer配置信息|无|无|设置HDR相关的timer配置信息| ## 开发实例 1. 驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 HDMI驱动入口参考: ```c struct HdfDriverEntry g_hdmiDriverEntry = { .moduleVersion = 1, .Bind = HdmiAdapterBind, .Init = HdmiAdapterInit, .Release = HdmiAdapterRelease, .moduleName = "adapter_hdmi_driver",//【必要】与HCS里面的名字匹配 }; HDF_INIT(g_hdmiDriverEntry); //调用HDF_INIT将驱动入口注册到HDF框架中 ``` 2. 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。 从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。 - device_info.hcs 配置参考 ```c root { platform :: host { device_hdmi :: device { device0 :: deviceNode { policy = 2; // 等于2,需要发布服务 priority = 20; // 驱动启动优先级 permission = 0644; // 驱动创建设备节点权限 serviceName = "HDF_PLATFORM_HDMI_0"; //【必要】驱动对外发布服务的名称,必须唯一 moduleName = "hdmi_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致; deviceMatchAttr = "adapter_hdmi_driver"; //【必要】用于配置控制器私有数据,要与hdmi_config.hcs中对应控制器保持一致 } // 具体的控制器信息在 hdmi_config.hcs 中 } } } ``` - hdmi_config.hcs 配置参考 ```c root { platform { hdmi_config { template hdmi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省 match_attr = ""; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致 index = 0; //【必要】hdmi控制器编号 regBasePhy = 0x10100000; //【必要】寄存器物理基地址 regSize = 0xd1; //【必要】寄存器位宽 irqNum = 100; //【必要】中断号 maxTmdsClock = 300; videoTiming = 10; quantization = 1; colorSpace = 0; colorimetry = 0; audioIfType = 0; audioBitDepth = 1; audioSampleRate = 2; audioChannels = 1; hdrColorimetry = 4; hdrUserMode = 1; cap = 0xd001e045; } controller_0x10100000 :: hdmi_controller { match_attr = "adapter_hdmi_driver"; index = 0; regBasePhy = 0x10100000; irqNum = 100; maxTmdsClock = 400; defTmdsClock = 300; maxFrlRate = 600; videoTiming = 10; quantization = 1; colorSpace = 0; colorimetry = 0; audioIfType = 0; audioSampleRate = 2; audioChannels = 1; hdrColorimetry = 4; hdrUserMode = 1; cap = 0xd001e045; } } } } ``` 3. 最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 - 自定义结构体参考 > ![](../public_sys-resources/icon-note.gif) **说明:** > 从驱动角度看,自定义结构体是参数和数据的载体。HDF会读取hdmi_config.hcs文件中的数值并通过DeviceResourceIface来初始化结构体成员,且其中一些重要数值(例如设备号、总线号等)也会被传递给核心层HdmiCntlr对象。 ```c struct HdmiAdapterHost { struct HdmiCntlr *cntlr; //【必要】是核心层控制对象,具体描述如下 volatile unsigned char *regBase;//【必要】寄存器基地址 uint32_t regBasePhy; //【必要】寄存器物理基地址 uint32_t regSize; //【必要】寄存器位宽 uint32_t irqNum; //【必要】中断号 }; /* HdmiCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值 */ struct HdmiCntlr { struct IDeviceIoService service; struct HdfDeviceObject *hdfDevObj; struct PlatformDevice device; struct OsalMutex mutex; struct PlatformQueue *msgQueue; struct HdmiCntlrCap cap; struct HdmiAttr attr; struct HdmiCntlrOps *ops; uint32_t deviceIndex; uint32_t state; // 控制器状态 enum HdmiTmdsModeType tmdsMode; struct HdmiDevice *hdmi; struct HdmiInfoframe infoframe; struct HdmiScdc *scdc; struct HdmiDdc ddc; struct HdmiFrl *frl; struct HdmiHdcp *hdcp; struct HdmiCec *cec; struct HdmiEvent event; struct HdmiHdr *hdr; void *priv; }; ``` - **【重要】** HdmiCntlr成员回调函数结构体HdmiCntlrOps的实例化 ```c static struct HdmiCntlrOps g_hdmiAdapterHostOps = { .hardWareInit = HdmiAdapterHardWareInit, .hardWareStatusGet = HdmiAdapterHardWareStatusGet, .controllerReset = HdmiAdapterControllerReset, .hotPlugStateGet = HdmiAdapterHotPlugStateGet, .hotPlugInterruptStateGet = HdmiAdapterHotPlugInterruptStateGet, .lowPowerSet = HdmiAdapterLowPowerSet, .tmdsModeSet = HdmiAdapterTmdsModeSet, .tmdsConfigSet = HdmiAdapterTmdsConfigSet, .infoframeEnable = HdmiAdapterInfoframeEnable, .infoframeSend = HdmiAdapterInfoframeSend, .infoframeDataSet = HdmiAdapterInfoframeDataSet, .cecMsgSend = HdmiAdapterCecMsgSend, .audioPathEnable = HdmiAdapterAudioPathEnable, .audioPathSet = HdmiAdapterAudioPathSet, .phyOutputEnable = HdmiAdapterPhyOutputEnable, .phyOutputSet = HdmiAdapterPhyOutputSet, .blackDataSet = HdmiAdapterBlackDataSet, .videoMuteEnable = HdmiAdapterVideoMuteEnable, .videoPathSet = HdmiAdapterVideoPathSet, .audioMuteEnable = HdmiAdapterAudioMuteEnable, .avmuteSet = HdmiAdapterAvmuteSet, .ddcTransfer = HdmiAdapterDdcTransfer, .scdcSourceScrambleGet = HdmiAdapterScdcSourceScrambleGet, .scdcSourceScrambleSet = HdmiAdapterScdcSourceScrambleSet, .frlSet = HdmiAdapterFrlSet, .frlEnable = HdmiAdapterFrlEnable, .audioNctsSet = HdmiAdapterAudioNctsSet, .frlTrainingConfigSet = HdmiAdapterFrlTrainingConfigSet, .frlTrainingStart = HdmiAdapterFrlTrainingStart, .frlGetTriningRslt = HdmiAdapterFrlGetTriningRslt, .hdcpRegInit = HdmiAdapterHdcpRegInit, .hdcpGenerateAksvAndAn = HdmiAdapterHdcpGenerateAksvAndAn, .hdcpOptReg = HdmiAdapterHdcpOptReg, .hdrTimerSet = HdmiAdapterHdrTimerSet, }; ``` - **Bind函数参考** > **入参:** > HdfDeviceObject 是整个驱动对外呈现的接口参数,具备 HCS 配置文件的信息 > > **返回值:** > HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义) |状态(值)|状态描述| |:-|:-| |HDF_ERR_INVALID_OBJECT|控制器对象非法| |HDF_ERR_INVALID_PARAM |参数非法| |HDF_ERR_MALLOC_FAIL |内存分配失败| |HDF_ERR_IO |I/O 错误| |HDF_SUCCESS |传输成功| |HDF_FAILURE |传输失败| > **函数说明:** > 初始化自定义结构体对象HdmiAdapterHost,初始化HdmiCntlr成员,调用核心层HdmiCntlrAdd函数。 > > HdmiCntlr,HdmiAdapterHost,HdfDeviceObject之间互相赋值,方便其他函数可以相互转化。 ```c static int32_t HdmiAdapterBind(struct HdfDeviceObject *obj) { struct HdmiCntlr *cntlr = NULL; struct HimciAdapterHost *host = NULL; int32_t ret; cntlr = (struct HdmiCntlr *)OsalMemCalloc(sizeof(struct HdmiCntlr)); if (cntlr == NULL) { HDF_LOGE("%s: malloc cntlr failed!", __func__); return HDF_ERR_MALLOC_FAIL; } host = (struct HimciAdapterHost *)OsalMemCalloc(sizeof(struct HimciAdapterHost)); if (host == NULL) { HDF_LOGE("%s: malloc host failed!", __func__); return HDF_ERR_MALLOC_FAIL; } cntlr->priv = (void *)host; //【必要】将host存放至cntlr的私有数据 cntlr->ops = &g_hdmiHostOps; //【必要】HdmiCntlrOps的实例化对象的挂载 cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 obj->service = &cntlr->service; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 ret = HdmiAdapterCntlrParse(cntlr, obj); //【必要】 初始化 cntlr. 失败则 goto __ERR; ... ret = HdmiAdapterHostParse(host, obj); //【必要】 初始化 host对象的相关属性,失败则 goto __ERR; ... ret = HdmiAdapterHostInit(host, cntlr); //厂商自定义的初始化,失败则 goto __ERR; ... ret = HdmiCntlrAdd(cntlr); //调用核心层函数 失败则 goto __ERR; ... HDF_LOGD("HdmiAdapterBind: success."); return HDF_SUCCESS; __ERR: HdmiAdapterDeleteHost(host); HDF_LOGD("HdmiAdapterBind: fail, err = %d.", ret); return ret; } ``` - **init函数参考** >**入参:** >HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息 > >**返回值:** >HDF_STATUS相关状态 > >函数说明: > >实现HdmiAdapterInit函数。 ```c static int32_t HdmiAdapterInit(struct HdfDeviceObject *obj) { return HDF_SUCCESS; } ``` - **Release 函数参考** > **入参:** > HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息 > > **返回值:** > 无 > > **函数说明:** > 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。 ```c static void HdmiAdapterRelease(struct HdfDeviceObject *obj) { struct HdmiCntlr *cntlr = NULL; ... cntlr = (struct HdmiCntlr *)obj->service;//这里有HdfDeviceObject到HdmiCntlr的强制转化,通过service成员,赋值见Bind函数 ... HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);//厂商自定义的内存释放函数,这里有HdmiCntlr到HimciAdapterHost的强制转化 } ``` > 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。