1 //*****************************************************************************
2 //
3 // SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4 // SPDX-License-Identifier: MIT
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23 //
24 // File: nvt_ovt.c
25 //
26 // Purpose: calculate Optimized Video Timing (OVT) timing
27 //
28 //*****************************************************************************
29
30
31 #include "nvBinSegment.h"
32 #include "nvmisc.h"
33
34 #include "nvtiming_pvt.h"
35
36 PUSH_SEGMENTS
37
38 CONS_SEGMENT(PAGE_CONS)
39
40 const NvU32 NVT_OVT_PIXEL_CLOCK_GRANULARITY = 1000; // Resulting Pixel Clock will be a multiple of this
41 const NvU32 NVT_OVT_MIN_H_TOTAL_GRANULARITY = 8; // Resulting Htotal value will be a multiple of this
42 const NvU32 NVT_OVT_MIN_V_BLANK_MICROSEC = 460; // Minimum duration of Vblank (us)
43 const NvU32 NVT_OVT_MIN_V_SYNC_LEADING_EDGE = 400; // Minimum duration of Vsync + Vback (us)
44 const NvU32 NVT_OVT_MIN_CLOCK_RATE_420 = 590000000; // interface-specific minimum pixel rate for transport of 4:2:0 sample
45 const NvU32 NVT_OVT_PIXEL_FACTOR_420 = 2; // Worst case of two pixels per link character for pixel rates of MinClockRate420 or more
46 const NvU32 NVT_OVT_MIN_H_BLANK_444 = 80; // Minimum Hblank width for pixel rates below MinClockRate420
47 const NvU32 NVT_OVT_MIN_H_BLANK_420 = 128; // Minimum Hblank width for pixel rates of MinClockRate420 or more
48 const NvU32 NVT_OVT_MAX_CHUNK_RATE = 650000000; // Maximum rate of chunks of pixels with a power-of-two size
49 const NvU32 NVT_OVT_AUDIO_PACKET_RATE = 195000; // 192k sample packets + 3k auxiliary data packets
50 const NvU32 NVT_OVT_AUDIO_PACKET_SIZE = 32; // each packet carries 8 audio sample
51 const NvU32 NVT_OVT_LINE_OVERHEAD = 32; // interface-specific overhead: 32 pixels/line
52
53 const NvU32 NVT_OVT_H_SYNC_PIXELS = 32;
54 const NvU32 NVT_OVT_H_BACK_WIDTH = 32;
55 const NvU32 NVT_OVT_V_SYNC_WIDTH = 8;
56
CODE_SEGMENT(PAGE_DD_CODE)57 CODE_SEGMENT(PAGE_DD_CODE)
58 static NvU32 nvFloorPow2_U32(NvU32 x)
59 {
60 return x & ~(x - 1);
61 }
62
CODE_SEGMENT(PAGE_DD_CODE)63 CODE_SEGMENT(PAGE_DD_CODE)
64 static NvU32 computeGCD(NvU32 a, NvU32 b)
65 {
66 NvU32 temp;
67 while (b != 0)
68 {
69 temp = a % b;
70 if (temp == 0) return b;
71 a = b;
72 b = temp;
73 }
74 return a;
75 }
76
CODE_SEGMENT(PAGE_DD_CODE)77 CODE_SEGMENT(PAGE_DD_CODE)
78 static NvU32 calculate_aspect_ratio(NVT_TIMING *pT)
79 {
80 NvU32 gcd = computeGCD(pT->HVisible, pT->VVisible);
81
82 if (gcd == 0)
83 {
84 pT->etc.aspect = (NvU32)0;
85 return 0;
86 }
87
88 return (pT->HVisible / gcd) << 16 | (pT->VVisible / gcd);
89 }
90
91 /**
92 * OVT Algorithm Calculations Formula
93 *
94 * @brief Sinks can indicate supported video formats with VFD in a VFDB that are not represented by a CTA VIC.
95 * The timing parameters of those Video Formats are determined by the Optimized Video Timing(OVT) algorithm
96 *
97 * @param width : resolution width from RID
98 * @param height : resolution height from RID
99 * @param refreshRate : refresh rate x fraction rate
100 * @param pT : output all the parameters in NVT_TIMING
101 *
102 * @return NVT_STATUS_SUCCESS
103 *
104 */
CODE_SEGMENT(PAGE_DD_CODE)105 CODE_SEGMENT(PAGE_DD_CODE)
106 NVT_STATUS NvTiming_CalcOVT(NvU32 width, NvU32 height, NvU32 refreshRate, NVT_TIMING *pT)
107 {
108 NvU32 hTotal = 0;
109 NvU32 vTotal = 0;
110 NvU32 maxVRate = refreshRate;
111 NvU32 vTotalGranularity = 1;
112 NvU32 resolutionGranularity = 0;
113 NvU32 minVBlank, minVTotal, minLineRate, minHBlank, minHTotal, vBlank, vSyncPosition;
114 NvU32 hTotalGranularityChunk, hTotalGranularity, maxAudioPacketsPerLine;
115
116 NvU64 minPixelClockRate = 0LL;
117 NvU64 pixelClockRate = 0LL;
118 NvU64 maxActiveTime = 0LL;
119 NvU64 minLineTime = 0LL;
120 NvU64 minResolution = 0LL;
121 NvU32 V = 0;
122 NvU32 H = 0;
123 NvU64 R = 0;
124
125 // parameter sanity check
126 if (width % 8 != 0)
127 return NVT_STATUS_ERR;
128
129 // ** Preparation **
130 // 1. Determine maximum Vrate of frame rate group (see Table 13) and V-Total granularity:
131 switch (refreshRate)
132 {
133 case 24: case 25: case 30:
134 maxVRate = 30;
135 vTotalGranularity = 20;
136 break;
137 case 48: case 50: case 60:
138 maxVRate = 60;
139 vTotalGranularity = 20;
140 break;
141 case 100: case 120:
142 maxVRate = 120;
143 vTotalGranularity = 5;
144 break;
145 case 144:
146 maxVRate = 144;
147 vTotalGranularity = 1;
148 break;
149 case 200: case 240:
150 maxVRate = 240;
151 vTotalGranularity = 5;
152 break;
153 case 300: case 360:
154 maxVRate = 360;
155 vTotalGranularity = 5;
156 break;
157 case 400: case 480:
158 maxVRate = 480;
159 vTotalGranularity = 5;
160 break;
161 default:
162 vTotalGranularity = 1;
163 maxVRate = refreshRate;
164 nvt_assert (0 && "invalid input refresh rate!");
165 }
166
167 // 2. Minimum Vtotal is found from highest frame rate of Vrate group, Vactive and the minimum Vblank time of 460 μSec:
168 // 2.1 the minimum number of determine the maximum active time. For the sake of precision, it is multiplied by 1,000,000.
169 maxActiveTime = ((NvU64)1000000000000 / (NvU64)maxVRate) - (NvU64)NVT_OVT_MIN_V_BLANK_MICROSEC * 1000000;
170 // 2.2 get the minimum line time
171 minLineTime = maxActiveTime / (NvU64)height;
172 // 2.3 get the minimum number of VBlank lines. The multiplicand 1000000 is for accuracy, because we multiply it at 2.1
173 minVBlank = (NvU32)(NV_UNSIGNED_DIV_CEIL((NvU64)NVT_OVT_MIN_V_BLANK_MICROSEC * (NvU64)1000000, (NvU64)minLineTime));
174 // 2.4 get the minimum total number of lines
175 minVTotal = height + minVBlank;
176 if (minVTotal % vTotalGranularity !=0)
177 minVTotal += (vTotalGranularity - (minVTotal % vTotalGranularity));
178
179 // 3. Find the audio packet rate and use it to determine the required audio packets per line:
180 // 3.1 determine a minimum line rate
181 minLineRate = maxVRate * minVTotal; // Hz
182 // 3.2 The maximum number of audio packets
183 maxAudioPacketsPerLine = NV_UNSIGNED_DIV_CEIL(NVT_OVT_AUDIO_PACKET_RATE, minLineRate);
184
185 // 4. Find initial minimum horizontal total size, based on audio requirements (1 pixel = 1 character):
186 minHBlank = NVT_OVT_LINE_OVERHEAD + NVT_OVT_AUDIO_PACKET_SIZE * maxAudioPacketsPerLine;
187 // 4.1 determine a minimum Horizontal Total pixel (MinHtotal)
188 minHTotal = width + NV_MAX(NVT_OVT_MIN_H_BLANK_444, minHBlank);
189
190 // 5. Find hTotal and vTotal so that the pixelClockRate is divisible by the pixelClockGranularity, and
191 // hTotal is divisible by an appropriate processing chunk size:
192 minPixelClockRate = (NvU64)maxVRate * (NvU64)minHTotal * (NvU64)minVTotal; // Hz
193 // 5.1 determinate new granularity and minHtotal based on the new granularity
194 hTotalGranularityChunk = nvNextPow2_U32((NvU32)NV_UNSIGNED_DIV_CEIL(minPixelClockRate, (NvU64)NVT_OVT_MAX_CHUNK_RATE));
195 // 5.2 If this value is greater than the 8, it becomes the new horizontal granularity
196 hTotalGranularity = NV_MAX((NvU64)NVT_OVT_MIN_H_TOTAL_GRANULARITY, hTotalGranularityChunk);
197 if (minHTotal % hTotalGranularity != 0)
198 {
199 minHTotal += (hTotalGranularity - (minHTotal % hTotalGranularity));
200 }
201 // 5.3 optimized by iterating on resolution totals without multiplying by the max refresh rate.
202 resolutionGranularity = NVT_OVT_PIXEL_CLOCK_GRANULARITY / computeGCD(NVT_OVT_PIXEL_CLOCK_GRANULARITY, maxVRate);
203
204 // ** OVT Timing Search **
205 // 5.4 it will repeat until the found pixel clock is greater than the divisible pixel clock of the search at hte previous vTotal value,
206 // the hTotal and vTotal values of that preceding search are chosen for the video timing
207 for(;;)
208 {
209 minResolution = 0;
210 V = minVTotal;
211
212 for(;;)
213 {
214 H = minHTotal;
215 R = (NvU64)H * (NvU64)V;
216
217 if (minResolution && R > minResolution)
218 break;
219
220 while (R % resolutionGranularity || maxVRate * R / nvFloorPow2_U32(H) > NVT_OVT_MAX_CHUNK_RATE)
221 {
222 H += hTotalGranularity;
223 R = (NvU64)H * (NvU64)V;
224 }
225
226 if (minResolution == 0 || R < minResolution)
227 {
228 hTotal = H;
229 vTotal = V;
230 minResolution = R;
231 }
232 V += vTotalGranularity;
233 }
234
235 pixelClockRate = maxVRate * minResolution;
236
237 // 6. Check if timing requires adjustments for 4:2:0:
238 // 6.a Re-calculate minHTotal, in pixels, adjusted for 4:2:0 requirements. (2 pixels = 1 character):
239 minHTotal = width + NV_MAX(NVT_OVT_MIN_H_BLANK_420, NVT_OVT_PIXEL_FACTOR_420 * minHBlank);
240 // 6.b If the resulting PixelClockRate allows for 4:2:0, assure that the new Hblank requirement is met, or repeat calculation with new MinHtotal:
241 if (pixelClockRate >= NVT_OVT_MIN_CLOCK_RATE_420 && hTotal < minHTotal)
242 {
243 continue;
244 }
245 break;
246 }
247
248 // ** post-processing **
249 // 7. Adjust Vtotal, in lines, to achieve (integer) target Vrate:
250 vTotal = vTotal * maxVRate / refreshRate;
251
252 // 8. Find Vsync leading edge:
253 vBlank = vTotal - height;
254 vSyncPosition = (NvU32)NV_UNSIGNED_DIV_CEIL(((NvU64)NVT_OVT_MIN_V_SYNC_LEADING_EDGE * (NvU64)pixelClockRate), ((NvU64)1000000 * (NvU64)hTotal));
255
256 // 10. fill in the essential timing info for output
257 pT->HVisible = (NvU16)width;
258 pT->HTotal = (NvU16)hTotal;
259 pT->HFrontPorch = (NvU16)(hTotal - width - NVT_OVT_H_SYNC_PIXELS - NVT_OVT_H_BACK_WIDTH);
260 pT->HSyncWidth = (NvU16)NVT_OVT_H_SYNC_PIXELS;
261 pT->VVisible = (NvU16)height;
262 pT->VTotal = (NvU16)vTotal;
263 pT->VSyncWidth = (NvU16)NVT_OVT_V_SYNC_WIDTH;
264 pT->VFrontPorch = (NvU16)(vBlank - vSyncPosition);
265 pT->pclk = (NvU32)(pixelClockRate /*Hz*/ / 1000 + 5) / 10; //convert to 10Khz
266 pT->HSyncPol = NVT_H_SYNC_POSITIVE;
267 pT->VSyncPol = NVT_V_SYNC_POSITIVE;
268 pT->HBorder = pT->VBorder = 0; // not supported
269 pT->interlaced = 0; // not supported
270 // fill in the extra timing info
271 pT->etc.flag = 0;
272 pT->etc.rr = (NvU16)refreshRate;
273 pT->etc.rrx1k = (NvU32)axb_div_c_64((NvU64)pT->pclk, (NvU64)10000 * (NvU64)1000, (NvU64)pT->HTotal*(NvU64)pT->VTotal);
274 pT->etc.aspect = calculate_aspect_ratio(pT);
275 pT->etc.rep = 0x1;
276 NVT_SNPRINTF((char *)pT->etc.name, 40, "CTA861-OVT:%dx%dx%dHz", width, height, refreshRate);
277 pT->etc.name[39] = '\0';
278
279 return NVT_STATUS_SUCCESS;
280 }
281
CODE_SEGMENT(PAGE_DD_CODE)282 CODE_SEGMENT(PAGE_DD_CODE)
283 NvBool NvTiming_IsTimingOVT(const NVT_TIMING *pTiming)
284 {
285 // Check from the Timing Type
286 if (pTiming->etc.flag & NVT_FLAG_CTA_OVT_TIMING)
287 {
288 return NV_TRUE;
289 }
290 return NV_FALSE;
291 }
292
293 POP_SEGMENTS
294