1# HDMI
2
3## 概述
4
5### 功能简介
6
7HDMI(High Definition Multimedia Interface),即高清多媒体接口,主要用于DVD、机顶盒等音视频Source到TV、显示器等Sink设备的传输。
8
9HDMI以主从方式工作,通常有一个Source端和一个Sink端。
10
11HDMI接口定义了完成HDMI传输的通用方法集合,包括:
12
13- HDMI控制器管理:打开或关闭HDMI控制器
14
15- HDMI启动/停止传输:启动或停止HDMI传输
16
17- HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等
18
19- HDMI读取EDID:读取Sink端原始的EDID数据
20
21- HDMI热插拔:注册/注销热插拔回调函数
22
23### 基本概念
24
25HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同发布的一款音视频传输协议。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。
26
27- TMDS(Transition Minimized Differential signal):过渡调制差分信号,也被称为最小化传输差分信号,用于发送音频、视频及各种辅助数据。
28
29- DDC(Display Data Channel):显示数据通道,发送端与接收端可利用DDC通道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力。
30
31- CEC(Consumer Electronics Control):消费电子控制,该功能应该能够在连接HDMI的发送设备与接收设备之间实现交互操作。
32
33- FRL(Fixed Rate Link):TMDS 的架构进行讯号传输时,最高带宽可达 18Gbps,而FRL模式的带宽则提升到48 Gbps。
34
35- HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。
36
37- EDID(Extended Display Identification Data):扩展显示标识数据,通常存储在显示器的固件中,标识供应商信息、EDID版本信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串。
38
39### 运作机制
40
41在HDF框架中,HDMI模块接口适配模式拟采用独立服务模式,如图1所示。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
42
43独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
44
45- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
46
47- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
48
49**图 1** HDMI独立服务模式结构图
50
51![HDMI独立服务模式结构图](figures/独立服务模式结构图.png)
52
53HDMI模块各分层作用:
54
55- 接口层提供打开HDMI设备、启动HDMI传输、停止HDMI传输、声音图像消隐设置、设置色彩深度、获取色彩深度、设置视频属性、获取视频属性、设置HDR属性、读取Sink端原始EDID数据、注册HDMI热插拔检测回调函数、注销HDMI热插拔检测回调函数、关闭HDMI设备的接口。
56
57- 核心层主要提供HDMI控制器的打开、关闭及管理的能力,通过钩子函数与适配层交互。
58
59- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
60
61HDMI的Source端提供+5V和GND,用于DDC和CEC通信。通过DDC通道,Source端可以读取Sink端的各项参数,如接受能力等;CEC为可选通道,用于同步Source端与Sink端的控制信号,改善用户体验。TMDS通道有四组差分信号,TMDS Clock Channel为TMDS提供时钟信号,其余三组传输音视频数据及各种辅助数据;HDP为热插拔检测端口,当有Sink端接入时,Source端会通过中断服务程序进行响应。
62
63HDMI物理连接如图2所示:
64
65**图 2** HDMI物理连线示意图
66
67![HDMI物理连线示意图](figures/HDMI物理连线示意图.png)
68
69### 约束与限制
70
71HDMI模块当前仅支持轻量和小型系统内核(LiteOS),暂无实际适配驱动 。
72
73## 使用指导
74
75### 场景介绍
76
77HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同时传输无压缩音视频信号等优点。与传统的全模拟接口相比,HDMI不但增加了设备间接线的便捷性,还提供了一些HDMI特有的智能化功能,可用于小体积设备进行高质量音视频传输的场景。
78
79### 接口说明
80
81HDMI模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/framework/include/platform/hdmi_if.h82
83**表 1** HDMI驱动API接口功能介绍
84
85| 接口名 | 描述 |
86| ----------------------------- | -------------------------- |
87| HdmiOpen | 打开HDMI控制器 |
88| HdmiClose | 关闭HDMI控制器 |
89| HdmiStart | 启动HDMI传输 |
90| HdmiStop | 停止HDMI传输 |
91| HdmiAvmuteSet | 声音图像消隐设置 |
92| HdmiDeepColorSet | 设置色彩深度 |
93| HdmiDeepColorGet | 获取色彩深度 |
94| HdmiSetVideoAttribute | 设置视频属性 |
95| HdmiSetAudioAttribute | 设置音频属性 |
96| HdmiSetHdrAttribute | 设置HDR属性 |
97| HdmiReadSinkEdid | 读取Sink端原始EDID数据 |
98| HdmiRegisterHpdCallbackFunc | 注册HDMI热插拔检测回调函数 |
99| HdmiUnregisterHpdCallbackFunc | 注销HDMI热插拔检测回调函数 |
100
101### 开发步骤
102
103使用HDMI设备的一般流程如图3所示。
104
105**图 3** HDMI设备使用流程图
106
107![HDMI设备使用流程图](figures/HDMI使用流程图.png)
108
109#### 打开HDMI控制器
110
111在进行HDMI通信前,首先要调用HdmiOpen打开HDMI控制器。
112
113```c
114DevHandle HdmiOpen(int16_t number);
115```
116
117**表 2** HdmiOpen参数和返回值描述
118
119| 参数 | 参数描述 |
120| ---------- | -------------------- |
121| number | int16_t类型,HDMI控制器号 |
122| **返回值** | **返回值描述** |
123| NULL | 打开HDMI控制器失败 |
124| 控制器句柄 | 打开的HDMI控制器句柄 |
125
126假设系统中存在2个HDMI控制器,编号从0到1,以下代码示例为获取0号控制器:
127
128```c
129DevHandle hdmiHandle = NULL;  // HDMI控制器句柄
130
131// 打开HDMI控制器
132hdmiHandle = HdmiOpen(0);
133if (hdmiHandle == NULL) {
134    HDF_LOGE("HdmiOpen: hdmi open fail!\n");
135    return NULL;
136}
137```
138
139#### 注册热插拔检测回调函数
140
141```c
142int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo *callback);
143```
144
145**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述
146
147| 参数 | 参数描述 |
148| ---------- | ------------------ |
149| handle | DevHandle类型,HDMI控制器句柄 |
150| callback | 结构体指针,热插拔回调函数信息 |
151| **返回值** | **返回值描述** |
152| HDF_SUCCESS | 注册成功 |
153| 负数 | 注册失败 |
154
155注册热插拔检测回调函数示例:
156
157```c
158// 热插拔检测回调函数定义
159static void HdmiHpdHandle(void *data, bool hpd)
160{
161    if (data == NULL) {
162        HDF_LOGE("priv data is NULL");
163        return;
164    }
165    if (hpd == true) {
166        HDF_LOGD("HdmiHpdHandle: hot plug");
167        // 调用者添加相关处理
168    } else {
169        HDF_LOGD("HdmiHpdHandle: hot unplug");
170        // 调用者添加相关处理
171    }
172}
173
174// 热插拔检测回调函数注册示例
175struct HdmiHpdCallbackInfo info = {0};
176info.data = handle;
177info.callbackFunc = HdmiHpdHandle;
178ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info);
179if (ret != HDF_SUCCESS) {
180    HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register hpd callback func fail, ret:%d", ret);
181    return ret;
182}
183```
184
185#### 读取EDID
186
187```c
188int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len);
189```
190
191**表 4** HdmiReadSinkEdid参数和返回值描述
192
193| 参数 | 参数描述 |
194| ---------- | ---------------------- |
195| handle | DevHandle类型,HDMI控制器句柄 |
196| buffer | uint8_t类型指针,数据缓冲区 |
197| len | uint32_t类型,数据长度 |
198| **返回值** | **返回值描述** |
199| 正整数 | 成功读取的原始EDID数据 |
200| 负数或0 | 读取失败 |
201
202读取Sink端的原始EDID数据示例:
203
204```c
205int32_t len;
206uint8_t edid[HDMI_EDID_MAX_LEN] = {0};
207
208len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN);
209if (len <= 0) {
210    HDF_LOGE("HdmiReadSinkEdid: hdmi read sink edid fail, len = %d.", len);
211	return HDF_FAILURE;
212}
213```
214
215#### 设置音频属性
216
217```c
218int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr);
219```
220
221**表 5** HdmiSetAudioAttribute参数和返回值描述
222
223| 参数 | 参数描述 |
224| ------ | -------------- |
225| handle | DevHandle类型,HDMI控制器句柄 |
226| attr | 结构体指针,音频属性 |
227| 返回值 | 返回值描述 |
228| HDF_SUCCESS | 设置成功 |
229| 负数 | 设置失败 |
230
231设置音频属性示例:
232
233```c
234struct HdmiAudioAttr audioAttr = {0};
235int32_t ret;
236
237audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;
238audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;
239audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;
240audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;
241audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;
242ret = HdmiSetAudioAttribute(handle, &audioAttr);
243if (ret != HDF_SUCCESS) {
244    HDF_LOGE("HdmiSetAudioAttribute: hdmi set audio attribute fail!, ret:%d", ret);
245    return ret;
246}
247```
248
249#### 设置视频属性
250
251```c
252int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr);
253```
254
255**表 6** HdmiSetVideoAttribute参数和返回值描述
256
257| 参数| 参数描述|
258| ---------- | -------------- |
259| handle | DevHandle类型,HDMI控制器句柄 |
260| attr | 结构体指针,视频属性 |
261| **返回值** | **返回值描述** |
262| HDF_SUCCESS | 设置成功 |
263| 负数 | 设置失败 |
264
265设置视频属性示例:
266
267```c
268struct HdmiVideoAttr videoAttr = {0};
269int32_t ret;
270
271videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;
272videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;
273videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
274videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;
275ret = HdmiSetVideoAttribute(handle, &videoAttr);
276if (ret != HDF_SUCCESS) {
277    HDF_LOGE("HdmiSetVideoAttribute: hdmi set video attribute fail, ret:%d.", ret);
278    return ret;
279}
280```
281
282#### 设置HDR属性
283
284```c
285int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr);
286```
287
288**表 7** HdmiSetHdrAttribute参数和返回值描述
289
290| 参数 | 参数描述 |
291| ---------- | -------------- |
292| handle | DevHandle类型,HDMI控制器句柄 |
293| attr | 结构体指针,HDR属性 |
294| **返回值** | **返回值描述** |
295| HDF_SUCCESS | 设置成功 |
296| 负数 | 设置失败 |
297
298设置HDR属性示例:
299
300```c
301struct HdmiHdrAttr hdrAttr = {0};
302int32_t ret;
303
304hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;
305hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;
306hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;
307hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;
308hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;
309ret = HdmiSetHdrAttribute(handle, &hdrAttr);
310if (ret != HDF_SUCCESS) {
311    HDF_LOGE("HdmiSetHdrAttribute: hdmi set hdr attribute fail, ret:%d", ret);
312    return ret;
313}
314```
315
316#### 设置HDMI声音图像消隐
317
318```c
319int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
320```
321
322**表 8** HdmiAvmuteSet参数和返回值描述
323
324| 参数 | 参数描述 |
325| ---------- | ----------------- |
326| handle | DevHandle类型,HDMI控制器句柄 |
327| enable | 布尔值,使能/去使能avmute |
328| **返回值** | **返回值描述** |
329| HDF_SUCCESS | 设置成功 |
330| 负数 | 设置失败 |
331
332设置声音图像消隐示例:
333
334```c
335int32_t ret;
336
337ret = HdmiAvmuteSet(hdmiHandle, true);
338if (ret != HDF_SUCCESS) {
339    HDF_LOGE("HdmiAvmuteSet: hdmi avmute set fail, ret:%d", ret);
340    return ret;
341}
342```
343
344#### 设置色彩深度
345
346```c
347int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
348```
349
350**表 9** HdmiDeepColorSet参数和返回值描述
351
352| 参数 | 参数描述 |
353| ---------- | -------------- |
354| handle | DevHandle类型,HDMI控制器句柄 |
355| color | 枚举类型,色彩深度 |
356| **返回值** | **返回值描述** |
357| HDF_SUCCESS | 设置成功 |
358| 负数 | 设置失败 |
359
360设置色彩深度示例:
361
362```c
363int32_t ret;
364
365ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
366if (ret != HDF_SUCCESS) {
367    HDF_LOGE("HdmiDeepColorSet: hdmi deep color set fail, ret:%d.", ret);
368    return ret;
369}
370```
371
372#### 获取色彩深度
373
374```c
375int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color);
376```
377
378**表 10** HdmiDeepColorGet参数和返回值描述
379
380| 参数 | 参数描述 |
381| ---------- | -------------- |
382| handle | DevHandle类型,HDMI控制器句柄 |
383| color | 枚举类型指针,色彩深度 |
384| **返回值** | **返回值描述** |
385| HDF_SUCCESS | 获取成功 |
386| 负数 | 获取失败 |
387
388获取色彩深度示例:
389
390```c
391enum HdmiDeepColor color;
392int32_t ret;
393
394ret = HdmiDeepColorGet(handle, &color);
395if (ret != HDF_SUCCESS) {
396    HDF_LOGE("HdmiDeepColorGet: hdmi deep color get fail, ret:%d", ret);
397    return ret;
398}
399```
400
401#### 启动HDMI传输
402
403```c
404int32_t HdmiStart(DevHandle handle);
405```
406
407**表 11** HdmiStart参数和返回值描述
408
409| 参数 | 参数描述 |
410| ---------- | -------------- |
411| handle | DevHandle类型,HDMI控制器句柄 |
412| **返回值** | **返回值描述** |
413| HDF_SUCCESS | 启动成功 |
414| 负数 | 启动失败 |
415
416启动HDMI传输示例:
417
418```c
419int32_t ret;
420
421ret = HdmiStart(hdmiHandle);
422if (ret != HDF_SUCCESS) {
423    HDF_LOGE("HdmiStart: start transmission fail, ret:%d", ret);
424    return ret;
425}
426```
427
428#### 停止HDMI传输<a name="section11"></a>
429
430```c
431int32_t HdmiStop(DevHandle handle);
432```
433
434**表 12** HdmiStop参数和返回值描述
435
436| 参数 | 参数描述 |
437| ---------- | -------------- |
438| handle | DevHandle类型,HDMI控制器句柄 |
439| **返回值** | **返回值描述** |
440| HDF_SUCCESS | 停止成功 |
441| 负数 | 停止失败 |
442
443停止HDMI传输示例:
444
445```c
446int32_t ret;
447
448ret = HdmiStop(hdmiHandle);
449if (ret != HDF_SUCCESS) {
450    HDF_LOGE("HdmiStop: stop transmission fail, ret:%d.", ret);
451    return ret;
452}
453```
454
455#### 注销热插拔检测回调函数
456
457```c
458int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
459```
460
461**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述
462
463| 参数 | 参数描述 |
464| ---------- | -------------- |
465| handle | DevHandle类型,HDMI控制器句柄 |
466| **返回值** | **返回值描述** |
467| HDF_SUCCESS | 注销成功 |
468| 负数 | 注销失败 |
469
470注销热插拔检测回调函数示例:
471
472```c
473int32_t ret;
474
475ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle);
476if (ret != HDF_SUCCESS) {
477    HDF_LOGE("HdmiUnregisterHpdCallbackFunc:unregister fail, ret:%d.", ret);
478    return ret;
479}
480```
481
482#### 关闭HDMI控制器
483
484```c
485void HdmiClose(DevHandle handle);
486```
487
488**表 14**  HdmiClose参数和返回值描述
489
490| 参数 | 参数描述 |
491| ---------- | -------------- |
492| handle | DevHandle类型,HDMI控制器句柄 |
493
494关闭HDMI控制器示例:
495
496```c
497HdmiClose(hdmiHandle);
498```
499
500### 使用实例
501
502本例程以操作开发板上的HDMI设备为例,详细展示HDMI接口的完整使用流程。
503
504本例拟在Hi3516DV300开发板上对虚拟驱动进行简单的传输操作:
505
506- SOC:hi3516dv300。
507
508- HDMI控制器:使用0号HDMI控制器。
509
510
511示例如下:
512
513```c
514#include "hdmi_if.h"          /* HDMI标准接口头文件 */
515#include "hdf_log.h"         /* 标准日志打印头文件 */
516#include "osal_time.h"       /* 标准延迟&睡眠接口头文件 */
517
518/* 热插拔回调函数 */
519static void HdmiHpdHandle(void *data, bool hpd)
520{
521    if (data == NULL) {
522    HDF_LOGE("priv data is NULL");
523    return;
524    }
525
526    if (hpd == true) {
527        HDF_LOGD("HdmiHpdHandle: hot plug");
528        /* 调用者添加相关处理 */
529    } else {
530        HDF_LOGD("HdmiHpdHandle: hot unplug");
531        /* 调用者添加相关处理 */
532    }
533}
534
535/* 设置HDMI相关属性 */
536static int32_t TestHdmiSetAttr(DevHandle handle)
537{
538    enum HdmiDeepColor color;
539    struct HdmiVideoAttr videoAttr = {0};
540    struct HdmiAudioAttr audioAttr = {0};
541    struct HdmiHdrAttr hdrAttr = {0};
542    int32_t ret;
543
544    ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
545
546    if (ret != 0) {
547        HDF_LOGE("HdmiDeepColorSet failed.");
548        return ret;
549    }
550    ret = HdmiDeepColorGet(handle, &color);
551    if (ret != 0) {
552        HDF_LOGE("HdmiDeepColorGet failed.");
553        return ret;
554    }
555    HDF_LOGE("HdmiDeepColorGet successful, color = %d.", color);
556    videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;
557    videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;
558    videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
559    videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;
560    ret = HdmiSetVideoAttribute(handle, &videoAttr);
561    if (ret != 0) {
562        HDF_LOGE("HdmiSetVideoAttribute failed.");
563        return ret;
564    }
565    audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;
566    audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;
567    audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;
568    audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;
569    audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;
570    ret = HdmiSetAudioAttribute(handle, &audioAttr);
571    if (ret != 0) {
572        HDF_LOGE("HdmiSetAudioAttribute failed.");
573        return ret;
574    }
575    hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;
576    hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;
577    hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;
578    hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;
579    hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;
580    ret = HdmiSetHdrAttribute(handle, &hdrAttr);
581    if (ret != 0) {
582        HDF_LOGE("HdmiSetHdrAttribute failed.");
583        return ret;
584    }
585
586    return 0;
587}
588
589/* HDMI例程总入口 */
590static int32_t TestCaseHdmi(void)
591{
592    DevHandle handle = NULL;
593    int32_t ret;
594
595    struct HdmiHpdCallbackInfo info = {0};
596    uint8_t data[128] = {0};
597
598    HDF_LOGD("HdmiAdapterInit: successful.");
599    handle = HdmiOpen(0);
600    if (handle == NULL) {
601        HDF_LOGE("HdmiOpen failed.");
602        return ret;
603    }
604    info.data = handle;
605    info.callbackFunc = HdmiHpdHandle;
606    ret = HdmiRegisterHpdCallbackFunc(handle, &info);
607    if (ret != 0) {
608        HDF_LOGE("HdmiRegisterHpdCallbackFunc failed.");
609        return ret;
610    }
611
612    ret = HdmiReadSinkEdid(handle, data, 128);
613    if (ret <= 0) {
614        HDF_LOGE("HdmiReadSinkEdid failed.");
615        return ret;
616    }
617    HDF_LOGE("HdmiReadSinkEdid successful, data[6] = %d, data[8] = %d.", data[6], data[8]);
618
619    ret = TestHdmiSetAttr(handle);
620    if (ret != 0) {
621        HDF_LOGE("TestHdmiSetAttr failed.");
622        return ret;
623    }
624
625    ret = HdmiStart(handle);
626    if (ret != 0) {
627        HDF_LOGE("HdmiStart failed.");
628        return ret;
629    }
630
631    OsalMSleep(1000);
632
633    ret = HdmiStop(handle);
634    if (ret != 0) {
635        HDF_LOGE("HdmiStop failed.");
636        return ret;
637    }
638
639    ret = HdmiUnregisterHpdCallbackFunc(handle);
640    if (ret != 0) {
641        HDF_LOGE("HdmiUnregisterHpdCallbackFunc failed.");
642        return ret;
643    }
644    HdmiClose(handle);
645    return 0;
646}
647
648```
649