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