OpenHarmony平臺驅動使用(五)
HDMI
概述
功能簡介
HDMI(High Definition Multimedia Interface),即高清多媒體接口,主要用于DVD、機頂盒等音視頻Source到TV、顯示器等Sink設備的傳輸。
HDMI以主從方式工作,通常有一個Source端和一個Sink端。
HDMI接口定義了完成HDMI傳輸的通用方法集合,包括:
-
HDMI控制器管理:打開或關閉HDMI控制器。
-
HDMI啟動/停止傳輸:啟動或停止HDMI傳輸。
-
HDMI控制器設置:設置音頻、視頻及HDR屬性,設置色彩深度、聲音圖像消隱等。
-
HDMI讀取EDID:讀取Sink端原始的EDID數據。
-
HDMI熱插拔:注冊/注銷熱插拔回調函數。
基本概念
HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同發布的一款音視頻傳輸協議。傳輸過程遵循TMDS(Transition Minimized Differential Signaling)協議。
-
TMDS(Transition Minimized Differential signal):過渡調制差分信號,也被稱為最小化傳輸差分信號,用于發送音頻、視頻及各種輔助數據。
-
DDC(Display Data Channel):顯示數據通道,發送端與接收端可利用DDC通道得知彼此的發送與接收能力,但HDMI僅需單向獲知接收端(顯示器)的能力。
-
CEC(Consumer Electronics Control):消費電子控制,該功能應該能夠在連接HDMI的發送設備與接收設備之間實現交互操作。
-
FRL(Fixed Rate Link):TMDS 的架構進行訊號傳輸時,最高帶寬可達 18Gbps,而FRL模式的帶寬則提升到48 Gbps。
-
HDCP(High-bandwidth Digital Content Protection):即高帶寬數字內容保護技術,當用戶對高清晰信號進行非法復制時,該技術會進行干擾,降低復制出來的影像的質量,從而對內容進行保護。
-
EDID(Extended Display Identification Data):擴展顯示標識數據,通常存儲在顯示器的固件中,標識供應商信息、EDID版本信息、最大圖像大小、顏色設置、廠商預設置、頻率范圍的限制以及顯示器名和序列號的字符串。
運作機制
在HDF框架中,HDMI模塊接口適配模式擬采用獨立服務模式,如圖1所示。在這種模式下,每一個設備對象會獨立發布一個設備服務來處理外部訪問,設備管理器收到API的訪問請求之后,通過提取該請求的參數,達到調用實際設備對象的相應內部方法的目的。獨立服務模式可以直接借助HDF設備管理器的服務管理能力,但需要為每個設備單獨配置設備節點,增加內存占用。
獨立服務模式下,核心層不會統一發布一個服務供上層使用,因此這種模式下驅動要為每個控制器發布一個服務,具體表現為:
-
驅動適配者需要實現HdfDriverEntry的Bind鉤子函數以綁定服務。
-
device_info.hcs文件中deviceNode的policy字段為1或2,不能為0。
圖 1 HDMI獨立服務模式結構圖
HDMI模塊各分層作用:
-
接口層提供打開HDMI設備、啟動HDMI傳輸、停止HDMI傳輸、聲音圖像消隱設置、設置色彩深度、獲取色彩深度、設置視頻屬性、獲取視頻屬性、設置HDR屬性、讀取Sink端原始EDID數據、注冊HDMI熱插拔檢測回調函數、注銷HDMI熱插拔檢測回調函數、關閉HDMI設備的接口。
-
核心層主要提供HDMI控制器的打開、關閉及管理的能力,通過鉤子函數與適配層交互。
-
適配層主要是將鉤子函數的功能實例化,實現具體的功能。
HDMI的Source端提供+5V和GND,用于DDC和CEC通信。通過DDC通道,Source端可以讀取Sink端的各項參數,如接受能力等;CEC為可選通道,用于同步Source端與Sink端的控制信號,改善用戶體驗。TMDS通道有四組差分信號,TMDS Clock Channel為TMDS提供時鐘信號,其余三組傳輸音視頻數據及各種輔助數據;HDP為熱插拔檢測端口,當有Sink端接入時,Source端會通過中斷服務程序進行響應。
HDMI物理連接如圖2所示:
圖 2 HDMI物理連線示意圖
約束與限制
HDMI模塊當前僅支持輕量和小型系統內核(LiteOS),暫無實際適配驅動 。
使用指導
場景介紹
HDMI具有體積小,傳輸速率高,傳輸帶寬寬,兼容性好,能同時傳輸無壓縮音視頻信號等優點。與傳統的全模擬接口相比,HDMI不但增加了設備間接線的便捷性,還提供了一些HDMI特有的智能化功能,可用于小體積設備進行高質量音視頻傳輸的場景。
接口說明
HDMI模塊提供的主要接口如下所示,具體API詳見。
表 1 HDMI驅動API接口功能介紹
接口名 | 描述 |
---|---|
HdmiOpen | 打開HDMI控制器 |
HdmiClose | 關閉HDMI控制器 |
HdmiStart | 啟動HDMI傳輸 |
HdmiStop | 停止HDMI傳輸 |
HdmiAvmuteSet | 聲音圖像消隱設置 |
HdmiDeepColorSet | 設置色彩深度 |
HdmiDeepColorGet | 獲取色彩深度 |
HdmiSetVideoAttribute | 設置視頻屬性 |
HdmiSetAudioAttribute | 設置音頻屬性 |
HdmiSetHdrAttribute | 設置HDR屬性 |
HdmiReadSinkEdid | 讀取Sink端原始EDID數據 |
HdmiRegisterHpdCallbackFunc | 注冊HDMI熱插拔檢測回調函數 |
HdmiUnregisterHpdCallbackFunc | 注銷HDMI熱插拔檢測回調函數 |
開發步驟
使用HDMI設備的一般流程如圖3所示。
圖 3 HDMI設備使用流程圖
打開HDMI控制器
在進行HDMI通信前,首先要調用HdmiOpen打開HDMI控制器。
DevHandle HdmiOpen(int16_t number);
表 2 HdmiOpen參數和返回值描述
參數 | 參數描述 |
---|---|
number | int16_t類型,HDMI控制器號 |
返回值 | 返回值描述 |
NULL | 打開HDMI控制器失敗 |
控制器句柄 | 打開的HDMI控制器句柄 |
假設系統中存在2個HDMI控制器,編號從0到1,以下代碼示例為獲取0號控制器:
DevHandle hdmiHandle = NULL; // HDMI控制器句柄// 打開HDMI控制器
hdmiHandle = HdmiOpen(0);
if (hdmiHandle == NULL) {HDF_LOGE("HdmiOpen: hdmi open fail!\n");return NULL;
}
注冊熱插拔檢測回調函數
int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo *callback);
表 3 HdmiRegisterHpdCallbackFunc參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
callback | 結構體指針,熱插拔回調函數信息 |
返回值 | 返回值描述 |
HDF_SUCCESS | 注冊成功 |
負數 | 注冊失敗 |
注冊熱插拔檢測回調函數示例:
// 熱插拔檢測回調函數定義
static void HdmiHpdHandle(void *data, bool hpd)
{if (data == NULL) {HDF_LOGE("priv data is NULL");return;}if (hpd == true) {HDF_LOGD("HdmiHpdHandle: hot plug");// 調用者添加相關處理} else {HDF_LOGD("HdmiHpdHandle: hot unplug");// 調用者添加相關處理}
}// 熱插拔檢測回調函數注冊示例
struct HdmiHpdCallbackInfo info = {0};
info.data = handle;
info.callbackFunc = HdmiHpdHandle;
ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register hpd callback func fail, ret:%d", ret);return ret;
}
讀取EDID
int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len);
表 4 HdmiReadSinkEdid參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
buffer | uint8_t類型指針,數據緩沖區 |
len | uint32_t類型,數據長度 |
返回值 | 返回值描述 |
正整數 | 成功讀取的原始EDID數據 |
負數或0 | 讀取失敗 |
讀取Sink端的原始EDID數據示例:
int32_t len;
uint8_t edid[HDMI_EDID_MAX_LEN] = {0};len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN);
if (len <= 0) {HDF_LOGE("HdmiReadSinkEdid: hdmi read sink edid fail, len = %d.", len);return HDF_FAILURE;
}
設置音頻屬性
int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr);
表 5 HdmiSetAudioAttribute參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
attr | 結構體指針,音頻屬性 |
返回值 | 返回值描述 |
HDF_SUCCESS | 設置成功 |
負數 | 設置失敗 |
設置音頻屬性示例:
struct HdmiAudioAttr audioAttr = {0};
int32_t ret;audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;
audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;
audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;
audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;
audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;
ret = HdmiSetAudioAttribute(handle, &audioAttr);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiSetAudioAttribute: hdmi set audio attribute fail!, ret:%d", ret);return ret;
}
設置視頻屬性
int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr);
表 6 HdmiSetVideoAttribute參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
attr | 結構體指針,視頻屬性 |
返回值 | 返回值描述 |
HDF_SUCCESS | 設置成功 |
負數 | 設置失敗 |
設置視頻屬性示例:
struct HdmiVideoAttr videoAttr = {0};
int32_t ret;videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;
videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;
videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;
videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;
ret = HdmiSetVideoAttribute(handle, &videoAttr);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiSetVideoAttribute: hdmi set video attribute fail, ret:%d.", ret);return ret;
}
設置HDR屬性
int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr);
表 7 HdmiSetHdrAttribute參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
attr | 結構體指針,HDR屬性 |
返回值 | 返回值描述 |
HDF_SUCCESS | 設置成功 |
負數 | 設置失敗 |
設置HDR屬性示例:
struct HdmiHdrAttr hdrAttr = {0};
int32_t ret;hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;
hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;
hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;
hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;
hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;
ret = HdmiSetHdrAttribute(handle, &hdrAttr);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiSetHdrAttribute: hdmi set hdr attribute fail, ret:%d", ret);return ret;
}
設置HDMI聲音圖像消隱
int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
表 8 HdmiAvmuteSet參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
enable | 布爾值,使能/去使能avmute |
返回值 | 返回值描述 |
HDF_SUCCESS | 設置成功 |
負數 | 設置失敗 |
設置聲音圖像消隱示例:
int32_t ret;ret = HdmiAvmuteSet(hdmiHandle, true);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiAvmuteSet: hdmi avmute set fail, ret:%d", ret);return ret;
}
設置色彩深度
int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
表 9 HdmiDeepColorSet參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
color | 枚舉類型,色彩深度 |
返回值 | 返回值描述 |
HDF_SUCCESS | 設置成功 |
負數 | 設置失敗 |
設置色彩深度示例:
int32_t ret;ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiDeepColorSet: hdmi deep color set fail, ret:%d.", ret);return ret;
}
獲取色彩深度
int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color);
表 10 HdmiDeepColorGet參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
color | 枚舉類型指針,色彩深度 |
返回值 | 返回值描述 |
HDF_SUCCESS | 獲取成功 |
負數 | 獲取失敗 |
獲取色彩深度示例:
enum HdmiDeepColor color;
int32_t ret;ret = HdmiDeepColorGet(handle, &color);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiDeepColorGet: hdmi deep color get fail, ret:%d", ret);return ret;
}
啟動HDMI傳輸
int32_t HdmiStart(DevHandle handle);
表 11 HdmiStart參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
返回值 | 返回值描述 |
HDF_SUCCESS | 啟動成功 |
負數 | 啟動失敗 |
啟動HDMI傳輸示例:
int32_t ret;ret = HdmiStart(hdmiHandle);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiStart: start transmission fail, ret:%d", ret);return ret;
}
停止HDMI傳輸
int32_t HdmiStop(DevHandle handle);
表 12 HdmiStop參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
返回值 | 返回值描述 |
HDF_SUCCESS | 停止成功 |
負數 | 停止失敗 |
停止HDMI傳輸示例:
int32_t ret;ret = HdmiStop(hdmiHandle);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiStop: stop transmission fail, ret:%d.", ret);return ret;
}
注銷熱插拔檢測回調函數
int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
表 13 HdmiUnregisterHpdCallbackFunc參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
返回值 | 返回值描述 |
HDF_SUCCESS | 注銷成功 |
負數 | 注銷失敗 |
注銷熱插拔檢測回調函數示例:
int32_t ret;ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle);
if (ret != HDF_SUCCESS) {HDF_LOGE("HdmiUnregisterHpdCallbackFunc:unregister fail, ret:%d.", ret);return ret;
}
關閉HDMI控制器
void HdmiClose(DevHandle handle);
表 14 HdmiClose參數和返回值描述
參數 | 參數描述 |
---|---|
handle | DevHandle類型,HDMI控制器句柄 |
關閉HDMI控制器示例:
HdmiClose(hdmiHandle);
使用實例
本例程以操作開發板上的HDMI設備為例,詳細展示HDMI接口的完整使用流程。
本例擬在Hi3516DV300開發板上對虛擬驅動進行簡單的傳輸操作:
-
SOC:hi3516dv300。
-
HDMI控制器:使用0號HDMI控制器。
示例如下:
#include "hdmi_if.h" /* HDMI標準接口頭文件 */
#include "hdf_log.h" /* 標準日志打印頭文件 */
#include "osal_time.h" /* 標準延遲&睡眠接口頭文件 *//* 熱插拔回調函數 */
static void HdmiHpdHandle(void *data, bool hpd)
{if (data == NULL) {HDF_LOGE("priv data is NULL");return;}if (hpd == true) {HDF_LOGD("HdmiHpdHandle: hot plug");/* 調用者添加相關處理 */} else {HDF_LOGD("HdmiHpdHandle: hot unplug");/* 調用者添加相關處理 */}
}/* 設置HDMI相關屬性 */
static int32_t TestHdmiSetAttr(DevHandle handle)
{enum HdmiDeepColor color;struct HdmiVideoAttr videoAttr = {0};struct HdmiAudioAttr audioAttr = {0};struct HdmiHdrAttr hdrAttr = {0};int32_t ret;ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);if (ret != 0) {HDF_LOGE("HdmiDeepColorSet failed.");return ret;}ret = HdmiDeepColorGet(handle, &color);if (ret != 0) {HDF_LOGE("HdmiDeepColorGet failed.");return ret;}HDF_LOGE("HdmiDeepColorGet successful, color = %d.", color);videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED;videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM;videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL;ret = HdmiSetVideoAttribute(handle, &videoAttr);if (ret != 0) {HDF_LOGE("HdmiSetVideoAttribute failed.");return ret;}audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16;audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K;audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3;ret = HdmiSetAudioAttribute(handle, &audioAttr);if (ret != 0) {HDF_LOGE("HdmiSetAudioAttribute failed.");return ret;}hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048;hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1;hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709;ret = HdmiSetHdrAttribute(handle, &hdrAttr);if (ret != 0) {HDF_LOGE("HdmiSetHdrAttribute failed.");return ret;}return 0;
}/* HDMI例程總入口 */
static int32_t TestCaseHdmi(void)
{DevHandle handle = NULL;int32_t ret;struct HdmiHpdCallbackInfo info = {0};uint8_t data[128] = {0};HDF_LOGD("HdmiAdapterInit: successful.");handle = HdmiOpen(0);if (handle == NULL) {HDF_LOGE("HdmiOpen failed.");return ret;}info.data = handle;info.callbackFunc = HdmiHpdHandle;ret = HdmiRegisterHpdCallbackFunc(handle, &info);if (ret != 0) {HDF_LOGE("HdmiRegisterHpdCallbackFunc failed.");return ret;}ret = HdmiReadSinkEdid(handle, data, 128);if (ret <= 0) {HDF_LOGE("HdmiReadSinkEdid failed.");return ret;}HDF_LOGE("HdmiReadSinkEdid successful, data[6] = %d, data[8] = %d.", data[6], data[8]);ret = TestHdmiSetAttr(handle);if (ret != 0) {HDF_LOGE("TestHdmiSetAttr failed.");return ret;}ret = HdmiStart(handle);if (ret != 0) {HDF_LOGE("HdmiStart failed.");return ret;}OsalMSleep(1000);ret = HdmiStop(handle);if (ret != 0) {HDF_LOGE("HdmiStop failed.");return ret;}ret = HdmiUnregisterHpdCallbackFunc(handle);if (ret != 0) {HDF_LOGE("HdmiUnregisterHpdCallbackFunc failed.");return ret;}HdmiClose(handle);return 0;
}