1 /*
2  * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3  *
4  * HDF is dual licensed: you can use it either under the terms of
5  * the GPL, or the BSD license, at your option.
6  * See the LICENSE file in the root of this repository for complete details.
7  */
8 
9 #include "hdmi_dfm.h"
10 #include "hdf_log.h"
11 #include "hdmi_common.h"
12 
13 #define HDF_LOG_TAG hdmi_dfm_c
14 
15 #define HDMI_DFM_THOUSAND 1000
16 #define HDMI_DFM_INVALID_VAL (-1)
17 
HdmiDfmGetPixelFormat(enum HdmiColorSpace colorSpace)18 static uint32_t HdmiDfmGetPixelFormat(enum HdmiColorSpace colorSpace)
19 {
20     uint32_t pixelFormat;
21 
22     switch (colorSpace) {
23         case HDMI_COLOR_SPACE_RGB:
24             pixelFormat = HDMI_DFM_PIXEL_FORMAT_MODE_0;
25             break;
26         case HDMI_COLOR_SPACE_YCBCR420:
27             pixelFormat = HDMI_DFM_PIXEL_FORMAT_MODE_1;
28             break;
29         case HDMI_COLOR_SPACE_YCBCR422:
30             pixelFormat = HDMI_DFM_PIXEL_FORMAT_MODE_2;
31             break;
32         case HDMI_COLOR_SPACE_YCBCR444:
33             pixelFormat = HDMI_DFM_PIXEL_FORMAT_MODE_3;
34             break;
35         default:
36             pixelFormat = HDMI_DFM_PIXEL_FORMAT_MODE_0;
37             break;
38     }
39     return pixelFormat;
40 }
41 
HdmiDfmFillParam(struct HdmiDfmParam * param,const struct HdmiVideoDefInfo * videoInfo,const struct HdmiAudioAttr * audioAttr,enum HdmiColorSpace colorSpace,enum HdmiDeepColor deepColor)42 void HdmiDfmFillParam(struct HdmiDfmParam *param, const struct HdmiVideoDefInfo *videoInfo,
43     const struct HdmiAudioAttr *audioAttr, enum HdmiColorSpace colorSpace, enum HdmiDeepColor deepColor)
44 {
45     if (param == NULL || videoInfo == NULL || audioAttr == NULL) {
46         return;
47     }
48     param->hactive = videoInfo->hactive;
49     param->vactive = videoInfo->vactive;
50     param->hblank = videoInfo->hblank;
51     param->vblank = videoInfo->vblank;
52     param->hsync = videoInfo->hsync;
53     param->hback = videoInfo->hback;
54     param->hfront = videoInfo->hfront;
55     param->vsync = videoInfo->vsync;
56     param->vback = videoInfo->vback;
57     param->vfront = videoInfo->vfront;
58     param->vfreq = videoInfo->rate;
59     param->colorDepth = HdmiCommonDeepClolorConvertToColorDepth(deepColor);
60     param->pixelFormat = HdmiDfmGetPixelFormat(colorSpace);
61     param->audioRate = (uint32_t)audioAttr->sampleRate;
62     param->layout = (audioAttr->channels > HDMI_AUDIO_FORMAT_CHANNEL_2) ? true : false;
63     /* ACAT packet_type */
64     param->acat = HDMI_AUDIO_CHANNEL_ALLOC_TYPE3;
65     param->packetType = HDMI_AUDIO_SAMPLE_PACKET;
66 }
67 
HdmiDfmBaseInfoInit(struct HdmiDfmInfo * info,const struct HdmiDfmParam * param)68 static void HdmiDfmBaseInfoInit(struct HdmiDfmInfo *info, const struct HdmiDfmParam *param)
69 {
70     info->htotal = param->hactive + param->hblank;
71     info->vtotal = param->vactive + param->vblank;
72     info->pixelClk = (uint64_t)(info->htotal) * info->vtotal * param->vfreq / HDMI_DFM_THOUSAND;
73 
74     /* 1. Determine the maximum legal pixel rate. */
75     info->maxPixelClk = info->pixelClk * (HDMI_DFM_THOUSAND + HDMI_DFM_FRL_PIXELCLK_TOLERANCE) / HDMI_DFM_THOUSAND;
76     info->minPixelClk = info->pixelClk * (HDMI_DFM_THOUSAND - HDMI_DFM_FRL_PIXELCLK_TOLERANCE) / HDMI_DFM_THOUSAND;
77     if (info->maxPixelClk == 0 || info->minPixelClk == 0) {
78         HDF_LOGE("max or min pixel clock is 0!");
79         return;
80     }
81 
82     /* 2. Determine the minimum Video Line period. the coefficient for converting from Mbps to bps.1000000000000*/
83     info->lineMinTime = (uint64_t)info->htotal * 1000000000000 / info->maxPixelClk;
84     info->lineMaxTime = (uint64_t)info->htotal * 1000000000000 / info->minPixelClk;
85 
86     /* 3. Determine the Worst Case Slow Bit Rate. x10000 */
87     info->minBitRate = (uint64_t)param->bitRate * 1000000000 * (10000 - HDMI_DFM_FRL_BITRATE_TOLERANCE) / 10000;
88     info->maxBitRate = (uint64_t)param->bitRate * 1000000000 * (10000 + HDMI_DFM_FRL_BITRATE_TOLERANCE) / 10000;
89 
90     /* 4. Determine the FRL Character Rate */
91     info->minFrlCharRate = info->minBitRate / 18;
92     info->maxFrlCharRate = info->maxBitRate / 18;
93 
94     /* 5. Determine the Total FRL Characters per line Period. the coefficient for converting from Mbps to bps.
95     1000000000000*/
96     info->minFrlCharsPerLine = (uint32_t)(info->lineMinTime * info->minFrlCharRate * param->laneNum / 1000000000000);
97     info->maxFrlCharsPerLine = (uint32_t)(info->lineMaxTime * info->maxFrlCharRate * param->laneNum / 1000000000000);
98 
99     info->cFrlSb = HDMI_DFM_FRL_SB_LEN(param->laneNum);
100     info->overheadSb = param->laneNum * 100000 / info->cFrlSb;
101     info->overheadRs = HDMI_DFM_RS_NUM_PER_CB * HDMI_DFM_FRL_CB_NUM_PER_SB * 100000 / info->cFrlSb;
102     info->overheadMap = 25 * 10000 / info->cFrlSb; /* FRL map chars per blk. */
103     info->overheadMin = info->overheadSb + info->overheadRs + info->overheadMap;
104     info->overheadMax = info->overheadMin + HDMI_DFM_OVERHEAD_SIZE;
105 }
106 
HdmiDfmGetAudioPackets(const struct HdmiDfmParam * param)107 static uint32_t HdmiDfmGetAudioPackets(const struct HdmiDfmParam *param)
108 {
109     uint32_t ap = 0;
110 
111     switch (param->packetType) {
112         case HDMI_AUDIO_SAMPLE_PACKET:
113         case HDMI_ONE_BIT_AUDIO_SAMPLE_PACKET:
114             ap = HDMI_AUDIO_AP_SIZE_100;
115             if (param->layout == false) {
116                 ap = HDMI_AUDIO_AP_SIZE_25;
117             }
118             break;
119         case HDMI_DTS_AUDIO_PACKET:
120         case HDMI_HBR_AUDIO_PACKET:
121         case HDMI_MULTI_STREAM_AUDIO_SAMPLE_PACKET:
122         case HDMI_ONE_BIT_MULTI_STREAM_AUDIO_SAMPLE_PACKET:
123             ap = HDMI_AUDIO_AP_SIZE_100;
124             break;
125         case HDMI_AUDIO_3D_SAMPLE_PACKET:
126         case HDMI_ONE_BIT_AUDIO_3D_SAMPLE_PACKET:
127             if (param->acat == HDMI_AUDIO_CHANNEL_ALLOC_TYPE1) {
128                 ap = HDMI_AUDIO_AP_SIZE_200;
129             } else if (param->acat == HDMI_AUDIO_CHANNEL_ALLOC_TYPE2) {
130                 ap = HDMI_AUDIO_AP_SIZE_300;
131             } else if (param->acat == HDMI_AUDIO_CHANNEL_ALLOC_TYPE3) {
132                 ap = HDMI_AUDIO_AP_SIZE_400;
133             }
134             break;
135         default:
136             HDF_LOGE("audio packet type 0x%x, is not support.", param->packetType);
137             break;
138     }
139 
140     return ap;
141 }
142 
HdmiDfmCaculateAudioInfo(struct HdmiDfmInfo * info,const struct HdmiDfmParam * param)143 static void HdmiDfmCaculateAudioInfo(struct HdmiDfmInfo *info, const struct HdmiDfmParam *param)
144 {
145     /* 1. Determine the number of audio packets required to carry each sample. */
146     info->audioAp = HdmiDfmGetAudioPackets(param);
147 
148     /* 2. Determine Average Audio Related Packet Rate. */
149     info->audioRap = (uint32_t)((uint64_t)param->audioRate * (1000 + HDMI_DFM_FRL_AUDIOCLK_TOLERANCE) *
150         info->audioAp / 100000);
151 
152     /* 3. Determine Average Required Packets per line. */
153     info->avgAudioPacketsPerLine = (uint32_t)((uint64_t)info->audioRap * info->lineMinTime / 1000000000);
154 
155     /* 4. Determine the Packets per Hblank that must be supportable. */
156     info->audioPacketsLine = (info->avgAudioPacketsPerLine + HDMI_DFM_THOUSAND - 1) / HDMI_DFM_THOUSAND;
157 
158     /*
159      * 5. Determine The c_frl_blank_min(64(guard bands, two 12-character control periods, c_frl_active_extra)
160      * and 32 audio_packets).
161      */
162     info->hblankAudioMin = 64 + 32 * info->audioPacketsLine;
163 }
164 
HdmiDfmCaculateVideoBorrowInfo(struct HdmiDfmInfo * info)165 static void HdmiDfmCaculateVideoBorrowInfo(struct HdmiDfmInfo *info)
166 {
167     if (info->activeTimeRef >= info->activeTimeMin && info->blankTimeRef >= info->blankTimeMin) {
168         info->tBorrow = 0;
169     } else if (info->activeTimeRef < info->activeTimeMin && info->blankTimeRef >= info->blankTimeMin) {
170         info->tBorrow = (int32_t)(info->activeTimeMin - info->activeTimeRef);
171     } else {
172         info->tBorrow = HDMI_DFM_INVALID_VAL;
173     }
174     if (info->tBorrow == HDMI_DFM_INVALID_VAL) {
175         info->tbBorrow = HDMI_DFM_INVALID_VAL;
176     } else {
177         info->tbBorrow = ((int32_t)(info->tBorrow * info->avgTbRate / 100000000000) + 10 - 1) / 10;
178     }
179 }
180 
HdmiDfmCaculateVideoInfo(struct HdmiDfmInfo * info,const struct HdmiDfmParam * param)181 static void HdmiDfmCaculateVideoInfo(struct HdmiDfmInfo *info, const struct HdmiDfmParam *param)
182 {
183     uint32_t kcd;
184     uint32_t k420;
185 
186     /*
187      * 1. if 4:2:2 pixels, kcd is 1. Otherwise, kcd is CD / 8.
188      * if 4:2:0 pixels, k420 is 2. Otherwise, k420 is 1.
189      */
190     kcd = (param->pixelFormat == 2) ? HDMI_DFM_MAGNIFICATION_8 : param->colorDepth;
191     k420 = (param->pixelFormat == 1) ? 2 : 1;
192 
193     /* 2. Determine Bits per Pixel */
194     info->bpp = ((24 * kcd) / k420) / HDMI_DFM_MAGNIFICATION_8;
195 
196     /* 3. Determine Video Bytes per line. */
197     info->activeBytesPerLine = info->bpp * param->hactive / HDMI_DFM_MAGNIFICATION_8;
198 
199     /*
200      * 4. Determine Required Characters to carry Active Video per line.
201      * 3 is means active_bytes_per_line need 3 characters.
202      */
203     info->activeTbPerLine = (info->activeBytesPerLine + 3 - 1) / 3;
204 
205     /* 5. Determine Required Characters to carry H-Blank Video per line. */
206     info->hblankTbPerLine = (param->hblank * kcd / k420 + HDMI_DFM_MAGNIFICATION_8 - 1) / HDMI_DFM_MAGNIFICATION_8;
207 
208     /* 6. 32 is FRL characters each packet, 7 is guard bands island(4 FRL char) + video(3 FRL char) */
209     if (((param->hblank * kcd) / k420) / HDMI_DFM_MAGNIFICATION_8 > 32 * (1 + info->audioPacketsLine) + 7) {
210         info->cFrlFree = ((param->hblank * kcd) / k420) / HDMI_DFM_MAGNIFICATION_8 -
211             32 * (1 + info->audioPacketsLine) - 7;
212     }
213 
214     /* 7. add 1 character each for RC break caused by 4 */
215     info->cFrlRcMargin = 4;
216     /* 8. RC compression transmit control characters reduce 7/8th total characters. */
217     if (7 * info->cFrlFree / HDMI_DFM_MAGNIFICATION_8 > info->cFrlRcMargin) {
218         info->cFrlRcSavings = 7 * info->cFrlFree / HDMI_DFM_MAGNIFICATION_8 - info->cFrlRcMargin;
219     }
220 
221     if (info->htotal == 0) {
222         HDF_LOGE("htotal is 0!");
223         return;
224     }
225     info->avgTbRate = info->maxPixelClk * (info->activeTbPerLine + info->hblankTbPerLine) / info->htotal;
226     info->activeTimeRef = (uint64_t)info->lineMinTime * param->hactive / info->htotal;
227     info->blankTimeRef = (uint64_t)info->lineMinTime * param->hblank / info->htotal;
228 
229     /* 9. (3 / 2) is active_tb coefficient in protocol */
230     info->activeTimeMin = (uint64_t)info->activeTbPerLine * 3 * 100000000000000 /
231         (2 * param->laneNum * info->minFrlCharRate * (HDMI_DFM_RATE_MAGNIFICATION - info->overheadMax) /
232         HDMI_DFM_THOUSAND);
233     info->blankTimeMin = (uint64_t)info->hblankTbPerLine * 100000000000000 /
234         (param->laneNum * info->minFrlCharRate * (HDMI_DFM_RATE_MAGNIFICATION - info->overheadMax) /
235         HDMI_DFM_THOUSAND);
236 
237     /* 10. Determine Borrow Info */
238     HdmiDfmCaculateVideoBorrowInfo(info);
239 
240     /* 11. (3 / 2) is active_tb coefficient in protocol */
241     info->cFrlActualPayload =
242         (3 * info->activeTbPerLine + 2 - 1) / 2 + info->hblankTbPerLine - info->cFrlRcSavings;
243 
244     if (info->minFrlCharsPerLine == 0) {
245         HDF_LOGE("min FRL Characters per line is 0!");
246         return;
247     }
248     /* 100000 is used to calculate utilization and margin */
249     info->utilization = (uint64_t)info->cFrlActualPayload * 100000 / (uint64_t)info->minFrlCharsPerLine;
250     info->margin = (int32_t)(100000 - (info->utilization + info->overheadMax));
251 }
252 
HdmiDfmFillInfo(struct HdmiDfmInfo * info,struct HdmiDfmParam * param)253 static void HdmiDfmFillInfo(struct HdmiDfmInfo *info, struct HdmiDfmParam *param)
254 {
255     HdmiDfmBaseInfoInit(info, param);
256     HdmiDfmCaculateAudioInfo(info, param);
257     HdmiDfmCaculateVideoInfo(info, param);
258 
259     /* Check Audio Cap. */
260     info->audioSupport = (info->hblankTbPerLine >= info->hblankAudioMin) ? true : false;
261     /* Check Video Cap. */
262     if (info->tbBorrow == HDMI_DFM_INVALID_VAL) {
263         info->videoSupport = false;
264     } else {
265         info->videoSupport = (info->tbBorrow <= HDMI_DFM_FRL_MAX_TB_BORROW) ? true : false;
266     }
267 
268     info->uncompressSupport = (info->margin >= 0) ? true : false;
269     info->canbeTrans = (info->audioSupport && info->videoSupport && info->uncompressSupport) ? true : false;
270     info->isExtraMode = (info->canbeTrans && (info->tbBorrow > 0)) ? true : false;
271 }
272 
HdmiDfmFormatSupport(struct HdmiDfmParam * param)273 bool HdmiDfmFormatSupport(struct HdmiDfmParam *param)
274 {
275     struct HdmiDfmInfo info = {0};
276 
277     HdmiDfmFillInfo(&info, param);
278     if (info.audioSupport == true && info.videoSupport == true && info.uncompressSupport == true) {
279         return true;
280     }
281     return false;
282 }
283